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) {