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

2846 lines
90 KiB
C++
Executable File

#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 <dtEntity/entitymanager.h>
#include <dtEntity/commandmessages.h>
#include <dtEntity/core.h>
#include <dtEntity/mapcomponent.h>
#include <dtEntity/systemmessages.h>
#include <dtEntity/windowinterface.h>
#include <dtEntityOSG/cameracomponent.h>
#include <dtEntityOSG/layercomponent.h>
#include <dtEntityOSG/layerattachpointcomponent.h>
#include <dtEntityOSG/lightcomponent.h>
#include <dtEntityOSG/osginputinterface.h>
#include <dtEntityOSG/osgsysteminterface.h>
#include <dtEntityOSG/positionattitudetransformcomponent.h>
#include <dtEntityOSG/staticmeshcomponent.h>
#include <osg/BlendFunc>
#include <osg/Camera>
#include <osg/ComputeBoundsVisitor>
#include <osg/GraphicsContext>
#include <osg/io_utils>
#include <osg/LineWidth>
#include <osg/LineStipple>
#include <osg/Point>
#include <osg/Vec3>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgViewer/CompositeViewer>
#include <vector>
#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<const dtEntity::TickMessage &>(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<ShotDisplay>::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<Shot *>::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<ShotDisplay>::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) <<LOGFUNCTION <<"Found Geopde and remove it";
osg::Group *parent = shotdspl.mGeode->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<int, int> &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<std::string, std::string>& 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<dtEntity::EntityId> ids;
std::vector<float> heights;
std::vector<int> 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<osgViewer::CompositeViewer*>(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<osgViewer::View*>(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<unsigned int>(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 " <<QString::number(dtEntity::GetSystemInterface()->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<osg::Geode>();
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<osg::LineWidth> 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<osg::LineStipple> ls = new osg::LineStipple(stippleSize, 0xAAAA);
ss->setAttributeAndModes(ls, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
}
//osg::ref_ptr<osg::LineStipple> 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<Device *>::iterator j = building->mDevices.begin();
while (j != building->mDevices.end())
{
Device *device = *j;
if (device->mWorldPlayerId == iconmainid)
{
for (BuildingDisplay &bd : building->mDisplays)
{
std::vector<DeviceDisplay>::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 <class T>
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<int, int> 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<BuildingData> &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<dtEntity::EntityId> &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 (dis<distance)
{
distance = dis;
nearestVertex = vertex;
}
}
if (nearestVertex.isNull())
{
// something fails, use fallback
posX.x() = roomObb.mPoly.at(0).x();
posX.y() = roomObb.mPoly.at(0).y();
} else {
posX.x() = nearestVertex.x();
posX.y() = nearestVertex.y();
}
}
const osg::Vec3 pos = posX * roomObb.mMatrix;
// osg::Vec3 center3D = roomObb.mBB.center();
// center3D.z() = (roomObb.mBB.zMin() + center3D.z()) / 2.0;
// const osg::Vec3 pos = center3D * roomObb.mMatrix;
// create entity for display in world view
dtEntity::Entity* e;
if (!mEntityManager->CreateEntity(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);
}
}
}
}