Issue #17 Restore aircraft labels with math and logic cribbed from LiveTraffic's XPMP2

This commit is contained in:
Mat Sutcliffe
2020-08-12 20:09:23 +01:00
parent c615b45e78
commit 19d88d1740
6 changed files with 134 additions and 16 deletions

View File

@@ -213,10 +213,10 @@ namespace BlackSimPlugin
//! \copydoc XSwiftBus::CTraffic::setDefaultIcao
void setDefaultIcao(const QString &defaultIcao);
//! deprecated
//! \copydoc XSwiftBus::CTraffic::setDrawingLabels
void setDrawingLabels(bool drawing);
//! deprecated
//! \copydoc XSwiftBus::CTraffic::isDrawingLabels
bool isDrawingLabels() const;
//! \copydoc XSwiftBus::CTraffic::setMaxPlanes

View File

@@ -13,7 +13,7 @@
#include <XPLM/XPLMDataAccess.h>
#include <XPLM/XPLMUtilities.h>
#include <vector>
#include <array>
#include <string>
#include <cassert>
@@ -65,10 +65,10 @@ namespace XSwiftBus
bool isValid() const { return m_ref; }
template <typename T>
void implSetAll(std::vector<T> const &);
void implSetAll(T* const);
template <typename T>
std::vector<T> implGetAll() const;
void implGetAll(T*) const;
template <typename T>
void implSetAt(int index, T);
@@ -136,11 +136,14 @@ namespace XSwiftBus
//! Dataref type
using DataRefType = typename DataRefTraits::type;
//! Array dataref size
static constexpr auto DataRefSize = DataRefTraits::size;
//! Set the value of the whole array (if it is writable)
void setAll(std::vector<DataRefType> const &a) { static_assert(DataRefTraits::writable, "read-only dataref"); ArrayDataRefImpl::implSetAll(a); }
void setAll(std::array<DataRefType, DataRefSize> const &a) { static_assert(DataRefTraits::writable, "read-only dataref"); ArrayDataRefImpl::implSetAll<DataRefType>(a.data()); }
//! Get the value of the whole array
std::vector<DataRefType> getAll() const { return ArrayDataRefImpl::implGetAll<DataRefType>(); }
std::array<DataRefType, DataRefSize> getAll() const { std::array<DataRefType, DataRefSize> result; ArrayDataRefImpl::implGetAll<DataRefType>(result.data()); return result; }
//! Set the value of a single element (if it is writable)
void setAt(int index, DataRefType d) { static_assert(DataRefTraits::writable, "read-only dataref"); ArrayDataRefImpl::implSetAt(index, d); }
@@ -218,13 +221,13 @@ namespace XSwiftBus
inline double DataRefImpl::implGet<double>() const { return XPLMGetDatad(m_ref); }
template <>
inline void ArrayDataRefImpl::implSetAll<int>(std::vector<int> const &v) { assert((int)v.size() <= m_size); XPLMSetDatavi(m_ref, const_cast<int *>(&v[0]), 0, (int)v.size()); }
inline void ArrayDataRefImpl::implSetAll(int const *v) { XPLMSetDatavi(m_ref, const_cast<int *>(v), 0, m_size); }
template <>
inline void ArrayDataRefImpl::implSetAll<float>(std::vector<float> const &v) { assert((int)v.size() <= m_size); XPLMSetDatavf(m_ref, const_cast<float *>(&v[0]), 0, (int)v.size()); }
inline void ArrayDataRefImpl::implSetAll(float const *v) { XPLMSetDatavf(m_ref, const_cast<float *>(v), 0, m_size); }
template <>
inline std::vector<int> ArrayDataRefImpl::implGetAll<int>() const { std::vector<int> v(m_size); XPLMGetDatavi(m_ref, &v[0], 0, m_size); return v; }
inline void ArrayDataRefImpl::implGetAll(int *v) const { XPLMGetDatavi(m_ref, &v[0], 0, m_size); }
template <>
inline std::vector<float> ArrayDataRefImpl::implGetAll<float>() const { std::vector<float> v(m_size); XPLMGetDatavf(m_ref, &v[0], 0, m_size); return v; }
inline void ArrayDataRefImpl::implGetAll(float *v) const { XPLMGetDatavf(m_ref, &v[0], 0, m_size); }
template <>
inline void ArrayDataRefImpl::implSetAt<int>(int i, int d) { assert(i <= m_size); XPLMSetDatavi(m_ref, &d, i, 1); }

View File

@@ -28,15 +28,20 @@ namespace XSwiftBus
//! Destructor.
virtual ~CDrawable() { hide(); }
//! Is currently shown.
bool isVisible() const { return m_visible; }
//! Register the draw callback.
void show()
{
m_visible = true;
XPLMRegisterDrawCallback(callback, m_phase, m_before, static_cast<void*>(this));
}
//! Unregister the draw callback.
void hide()
{
m_visible = false;
XPLMUnregisterDrawCallback(callback, m_phase, m_before, static_cast<void*>(this));
}
@@ -51,8 +56,9 @@ namespace XSwiftBus
return 1;
}
const XPLMDrawingPhase m_phase;
const bool m_before;
XPLMDrawingPhase m_phase;
bool m_before = false;
bool m_visible = false;
};
}

View File

@@ -31,6 +31,10 @@ namespace XSwiftBus
{
// m_startServerMenuItem = m_menu.item("Start XSwiftBus", [this]{ startServer(CDBusConnection::SessionBus); });
m_showHideLabelsMenuItem = m_menu.item("Show/Hide Aircraft Labels", [this]
{
m_traffic->setDrawingLabels(!m_traffic->isDrawingLabels());
});
m_messageWindowSubMenu = m_menu.subMenu("Message Window");
m_toggleMessageWindowMenuItem = m_messageWindowSubMenu.item("Show/Hide", [this]
{

View File

@@ -65,6 +65,9 @@ namespace XSwiftBus
assert(!s_instance);
s_instance = this;
XPLMRegisterKeySniffer(followAircraftKeySniffer, 1, this);
// init labels
this->setDrawingLabels(this->getSettings().isDrawingLabels());
}
// *INDENT-ON*
@@ -257,6 +260,30 @@ namespace XSwiftBus
XPMPSetDefaultPlaneICAO(defaultIcao.c_str());
}
void CTraffic::setDrawingLabels(bool drawing)
{
CSettings s = this->getSettings();
if (s.isDrawingLabels() != drawing)
{
s.setDrawingLabels(drawing);
this->setSettings(s);
}
if (drawing)
{
m_labels.show();
}
else
{
m_labels.hide();
}
}
bool CTraffic::isDrawingLabels() const
{
return m_labels.isVisible();
}
void CTraffic::setMaxPlanes(int planes)
{
CSettings s = this->getSettings();
@@ -581,13 +608,19 @@ namespace XSwiftBus
else if (message.getMethodName() == "setDrawingLabels")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
// removed in xpmp2
bool drawing = true;
message.beginArgumentRead();
message.getArgument(drawing);
queueDBusCall([ = ]()
{
setDrawingLabels(drawing);
});
}
else if (message.getMethodName() == "isDrawingLabels")
{
queueDBusCall([ = ]()
{
sendDBusReply(sender, serial, false); // always false in xpmp2
sendDBusReply(sender, serial, isDrawingLabels());
});
}
else if (message.getMethodName() == "setMaxPlanes")
@@ -843,6 +876,7 @@ namespace XSwiftBus
void CTraffic::interpolatePosition(Plane *plane)
{
std::memcpy(&plane->positions[3], &plane->positions[2], sizeof(plane->positions[2]));
return;
const auto now = std::chrono::steady_clock::now();
const auto t1 = plane->positionTimes[2] - plane->positionTimes[0];
@@ -879,6 +913,53 @@ namespace XSwiftBus
plane->prevSurfacesLerpTime = now;
}
void CTraffic::Labels::draw()
{
static const double metersPerFt = 0.3048;
static float color[3]{ 1.0f, 0.75f, 0.0f };
std::array<float, 16> worldMat = m_worldMat.getAll();
std::array<float, 16> projMat = m_projMat.getAll();
double windowWidth = static_cast<double>(m_windowWidth.get());
double windowHeight = static_cast<double>(m_windowHeight.get());
XPLMCameraPosition_t camPos {};
XPLMReadCameraPosition(&camPos);
for (const auto pair : m_traffic->m_planesById)
{
char *text = const_cast<char *>(pair.second->label);
const XPMPPlanePosition_t &planePos = pair.second->positions[3];
double worldPos[4]{ 0, 0, 0, 1 };
double localPos[4]{};
double windowPos[4]{};
XPLMWorldToLocal(planePos.lat, planePos.lon, planePos.elevation * metersPerFt, &worldPos[0], &worldPos[1], &worldPos[2]);
matrixMultVec(localPos, worldMat.data(), worldPos);
matrixMultVec(windowPos, projMat.data(), localPos);
windowPos[3] = 1.0 / windowPos[3];
windowPos[0] *= windowPos[3];
windowPos[1] *= windowPos[3];
windowPos[2] *= windowPos[3];
if (windowPos[2] < 0.0 || windowPos[2] > 1.0)
{
continue; // plane is behind camera
}
XPLMDrawString(color,
static_cast<int>(std::lround(windowWidth * (windowPos[0] * 0.5 + 0.5))),
static_cast<int>(std::lround(windowHeight * (windowPos[1] * 0.5 + 0.5))),
text, nullptr, xplmFont_Basic);
}
}
void CTraffic::Labels::matrixMultVec(double out[4], const float m[16], const double v[4])
{
out[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + v[3] * m[12];
out[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + v[3] * m[13];
out[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + v[3] * m[14];
out[3] = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + v[3] * m[15];
}
int CTraffic::orbitPlaneFunc(XPLMCameraPosition_t *cameraPosition, int isLosingControl, void *refcon)
{
constexpr bool DEBUG = false;

View File

@@ -16,6 +16,7 @@
#include "command.h"
#include "datarefs.h"
#include "terrainprobe.h"
#include "drawable.h"
#include "menus.h"
#include "XPMPMultiplayer.h"
#include <XPLM/XPLMCamera.h>
@@ -74,6 +75,12 @@ namespace XSwiftBus
//! Set the ICAO code to use for aircraft without a model match
void setDefaultIcao(const std::string &defaultIcao);
//! Set whether the plugin draws type and callsign labels above aircraft
void setDrawingLabels(bool drawing);
//! Get whether the plugin draws type and callsign labels above aircraft
bool isDrawingLabels() const;
//! Set the maximum number of aircraft.
void setMaxPlanes(int planes);
@@ -182,6 +189,24 @@ namespace XSwiftBus
const std::string &livery_, const std::string &modelName_);
};
//! Label renderer
class Labels : public CDrawable
{
public:
Labels(CTraffic *traffic) : CDrawable(xplm_Phase_Window, false), m_traffic(traffic) {}
protected:
virtual void draw() override;
private:
static void matrixMultVec(double out[4], const float m[16], const double v[4]);
CTraffic *m_traffic = nullptr;
ArrayDataRef<xplane::data::sim::graphics::view::world_matrix> m_worldMat;
ArrayDataRef<xplane::data::sim::graphics::view::projection_matrix_3d> m_projMat;
DataRef<xplane::data::sim::graphics::view::window_width> m_windowWidth;
DataRef<xplane::data::sim::graphics::view::window_height> m_windowHeight;
DataRef<xplane::data::sim::graphics::view::visibility_effective_m> m_visibilityM;
};
Labels m_labels { this };
//! Check functions
//! @{
static bool isPlusMinus180(float v);
@@ -220,7 +245,6 @@ namespace XSwiftBus
CCommand m_followPlaneViewNextCommand;
CCommand m_followPlaneViewPreviousCommand;
DataRef<xplane::data::sim::graphics::view::world_render_type> m_worldRenderType;
DataRef<xplane::data::sim::flightmodel::position::local_x> m_ownAircraftPositionX;
DataRef<xplane::data::sim::flightmodel::position::local_y> m_ownAircraftPositionY;
DataRef<xplane::data::sim::flightmodel::position::local_z> m_ownAircraftPositionZ;