From 37e592759c458d8a1f1eddd065308f3994e9715d Mon Sep 17 00:00:00 2001 From: Roland Winklmeier Date: Tue, 27 Mar 2018 13:55:37 +0200 Subject: [PATCH] [xswiftbus] Plane View Menu FSX/P3D have a nice feature to follow a remote aircraft with the camera. X-Plane was missing this feature, since for X-Plane the remote aircrafts are not known. However, from a plugin point of view, we know them and where they are. So we can manually control the camera and provide a similar feature. This is still very simple. As soon as you move the mouse, the PoV angle will change. In future updates, it should be change only while the right mouse button is pressed. --- src/xswiftbus/plugin.cpp | 3 ++ src/xswiftbus/plugin.h | 1 + src/xswiftbus/traffic.cpp | 71 +++++++++++++++++++++++++++++++++++++++ src/xswiftbus/traffic.h | 11 ++++++ 4 files changed, 86 insertions(+) diff --git a/src/xswiftbus/plugin.cpp b/src/xswiftbus/plugin.cpp index 82ed23676..93e2ccc14 100644 --- a/src/xswiftbus/plugin.cpp +++ b/src/xswiftbus/plugin.cpp @@ -32,6 +32,7 @@ namespace XSwiftBus m_toggleMessageWindowMenuItem = m_menu.item("Toggle Message Window", [this] { if(m_service) { m_service->toggleMessageBoxVisibility(); } }); // m_startServerMenuItems.push_back(m_menu.item("Start server on system bus", [this]{ startServer(BlackMisc::CDBusServer::systemBusAddress()); })); // m_startServerMenuItems.push_back(m_menu.item("Start server on localhost P2P", [this]{ startServer(BlackMisc::CDBusServer::p2pAddress("localhost")); })); + m_planeViewSubMenu = m_menu.subMenu("Plane View"); m_dbusThread = std::thread([this]() { @@ -71,6 +72,8 @@ namespace XSwiftBus m_traffic = new CTraffic(m_dbusConnection.get()); m_weather = new CWeather(m_dbusConnection.get()); + m_traffic->setPlaneViewMenu(m_planeViewSubMenu); + INFO_LOG("XSwiftBus started."); } diff --git a/src/xswiftbus/plugin.h b/src/xswiftbus/plugin.h index 8df96f493..83bb86cd0 100644 --- a/src/xswiftbus/plugin.h +++ b/src/xswiftbus/plugin.h @@ -57,6 +57,7 @@ namespace XSwiftBus CMenu m_menu; CMenuItem m_startServerMenuItem; CMenuItem m_toggleMessageWindowMenuItem; + CMenu m_planeViewSubMenu; std::thread m_dbusThread; bool m_shouldStop = false; diff --git a/src/xswiftbus/traffic.cpp b/src/xswiftbus/traffic.cpp index 210aa8469..2bdc58e66 100644 --- a/src/xswiftbus/traffic.cpp +++ b/src/xswiftbus/traffic.cpp @@ -16,6 +16,7 @@ #include "utils.h" #include "XPMPMultiplayer.h" #include "XPMPPlaneRenderer.h" +#include "XPLMGraphics.h" #include #include #include @@ -120,6 +121,20 @@ namespace XSwiftBus sendDBusMessage(signalRemoteAircraftData); } + void CTraffic::orbitRemotePlane(const std::string &callsign) + { + m_planeViewCallsign = callsign; + + /* This is the hotkey callback. First we simulate a joystick press and + * release to put us in 'free view 1'. This guarantees that no panels + * are showing and we are an external view. */ + XPLMCommandButtonPress(xplm_joy_v_fr1); + XPLMCommandButtonRelease(xplm_joy_v_fr1); + + /* Now we control the camera until the view changes. */ + XPLMControlCamera(xplm_ControlCameraUntilViewChanges, orbitPlaneFunc, this); + } + int g_maxPlanes = 100; float g_drawDistance = 50.0f; @@ -206,11 +221,17 @@ namespace XSwiftBus Plane *plane = new Plane(id, callsign, aircraftIcao, airlineIcao, livery, modelName); m_planesByCallsign[callsign] = plane; m_planesById[id] = plane; + + // Create view menu item + CMenuItem planeViewMenuItem = m_planeViewSubMenu.item(callsign, [this, callsign] { orbitRemotePlane(callsign); }); + m_planeViewMenuItems[callsign] = planeViewMenuItem; } } void CTraffic::removePlane(const std::string &callsign) { + m_planeViewMenuItems.erase(callsign); + auto planeIt = m_planesByCallsign.find(callsign); if (planeIt == m_planesByCallsign.end()) { return; } @@ -232,6 +253,7 @@ namespace XSwiftBus } m_planesByCallsign.clear(); m_planesById.clear(); + m_planeViewMenuItems.clear(); } void CTraffic::setPlanePosition(const std::string &callsign, double latitude, double longitude, double altitude, double pitch, double roll, double heading) @@ -627,6 +649,55 @@ namespace XSwiftBus default: return xpmpData_Unavailable; } } + + int CTraffic::orbitPlaneFunc(XPLMCameraPosition_t *cameraPosition, int isLosingControl, void *refcon) + { + auto *traffic = static_cast(refcon); + + if (isLosingControl == 1) + { + // traffic->m_planeViewCallsign.clear(); + return 0; + } + + if (cameraPosition) + { + int w, h, x, y; + // First get the screen size and mouse location. We will use this to decide + // what part of the orbit we are in. The mouse will move us up-down and around. + // fixme: In a future update, change the orbit only while right mouse button is pressed. + XPLMGetScreenSize(&w, &h); + XPLMGetMouseLocation(&x, &y); + double heading = 360.0 * static_cast(x) / static_cast(w); + double pitch = 20.0 * ((static_cast(y) / static_cast(h)) * 2.0 - 1.0); + + // Now calculate where the camera should be positioned to be 200 + // meters from the plane and pointing at the plane at the pitch and + // heading we wanted above. + double dx = -50.0 * sin(heading * M_PI / 180.0); + double dz = 50.0 * cos(heading * M_PI / 180.0); + double dy = -50.0 * tan(pitch * M_PI / 180.0); + + auto planeIt = traffic->m_planesByCallsign.find(traffic->m_planeViewCallsign); + if (planeIt == traffic->m_planesByCallsign.end()) { return 0; } + Plane *plane = planeIt->second; + + double lx, ly, lz; + static const double kFtToMeters = 0.3048; + XPLMWorldToLocal(plane->position.lat, plane->position.lon, plane->position.elevation * kFtToMeters, &lx, &ly, &lz); + + // Fill out the camera position info. + cameraPosition->x = static_cast(lx + dx); + cameraPosition->y = static_cast(ly + dy); + cameraPosition->z = static_cast(lz + dz); + cameraPosition->pitch = static_cast(pitch); + cameraPosition->heading = static_cast(heading); + cameraPosition->roll = 0; + } + + // Return 1 to indicate we want to keep controlling the camera. + return 1; + } } //! \endcond diff --git a/src/xswiftbus/traffic.h b/src/xswiftbus/traffic.h index 156d52c43..62a5f85e4 100644 --- a/src/xswiftbus/traffic.h +++ b/src/xswiftbus/traffic.h @@ -15,7 +15,9 @@ #include "dbusobject.h" #include "datarefs.h" #include "terrainprobe.h" +#include "menus.h" #include "XPMPMultiplayer.h" +#include "XPLMCamera.h" #include #include #include @@ -53,6 +55,9 @@ namespace XSwiftBus return s; } + //! Set plane view submenu + void setPlaneViewMenu(const CMenu &planeViewSubMenu) { m_planeViewSubMenu = planeViewSubMenu; } + //! Called by XPluginStart static void initLegacyData(); @@ -113,9 +118,11 @@ namespace XSwiftBus void emitSimFrame(); void emitRemoteAircraftData(const std::string &callsign, double latitude, double longitude, double elevation, double modelVerticalOffset); + void orbitRemotePlane(const std::string &callsign); static int preferences(const char *section, const char *name, int def); static float preferences(const char *section, const char *name, float def); + static int orbitPlaneFunc(XPLMCameraPosition_t *cameraPosition, int isLosingControl, void *refcon); struct Plane { @@ -142,6 +149,10 @@ namespace XSwiftBus std::unordered_map m_planesById; std::chrono::system_clock::time_point m_timestampLastSimFrame = std::chrono::system_clock::now(); + CMenu m_planeViewSubMenu; + std::unordered_map m_planeViewMenuItems; + std::string m_planeViewCallsign; + int getPlaneData(void *id, int dataType, void *io_data); static int getPlaneData(void *id, int dataType, void *io_data, void *self) {