diff --git a/src/plugins/simulator/fsxcommon/simconnectobject.h b/src/plugins/simulator/fsxcommon/simconnectobject.h index fe41fdecc..d83effc03 100644 --- a/src/plugins/simulator/fsxcommon/simconnectobject.h +++ b/src/plugins/simulator/fsxcommon/simconnectobject.h @@ -182,12 +182,28 @@ namespace BlackSimPlugin //! Camera rotation; const SIMCONNECT_DATA_PBH &cameraRotation() const { return m_cameraRotation; } + //! Camera position/rotation + void setCameraPositionAndRotation(const SIMCONNECT_DATA_XYZ &position, const SIMCONNECT_DATA_PBH &rotation) + { + m_cameraPosition = position; + m_cameraRotation = rotation; + } + //! Camera GUID GUID getCameraGUID() const { return m_cameraGuid; } //! Set camera GUID void setCameraGUID(GUID guid) { m_cameraGuid = guid; m_camera = true; } + //! No camera anymore + void removeCamera() { m_camera = false; } + + //! Set observer + void setObserverName(const QString &observer) { m_observerName = observer; } + + //! Observer name + const QString &getObserverName() const { return m_observerName; } + //! Reset the state (like it was a new onject) without affecting interpolator and aircraft void resetState(); @@ -288,6 +304,7 @@ namespace BlackSimPlugin SIMCONNECT_DATA_XYZ m_cameraPosition; SIMCONNECT_DATA_PBH m_cameraRotation; QByteArray m_callsignByteArray; + QString m_observerName; BlackMisc::Aviation::CAircraftLights m_currentLightsInSim { nullptr }; //!< current lights to know state for toggling BlackMisc::Aviation::CAircraftLights m_lightsAsSent { nullptr }; //!< lights as sent to simulator SIMCONNECT_PERIOD m_requestSimDataPeriod = SIMCONNECT_PERIOD_NEVER; //!< how often do we query ground elevation diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp index 724dcee5e..3d463db42 100644 --- a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp +++ b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp @@ -539,6 +539,18 @@ namespace BlackSimPlugin return m_simConnectObjects.removeByOtherSimObject(trace.simObject); } + void CSimulatorFsxCommon::removeCamera(CSimConnectObject &simObject) + { + // not in FSX + Q_UNUSED(simObject); + } + + void CSimulatorFsxCommon::removeObserver(CSimConnectObject &simObject) + { + // not in FSX + Q_UNUSED(simObject); + } + bool CSimulatorFsxCommon::triggerAutoTraceSendId(qint64 traceTimeMs) { if (m_traceSendId) { return false; } // no need @@ -1554,6 +1566,8 @@ namespace BlackSimPlugin // call in SIM const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectRemove); + this->removeCamera(simObject); + this->removeObserver(simObject); const HRESULT result = SimConnect_AIRemoveObject(m_hSimConnect, static_cast(simObject.getObjectId()), requestId); if (isOk(result)) { @@ -1968,6 +1982,16 @@ namespace BlackSimPlugin return position; } + SIMCONNECT_DATA_PBH CSimulatorFsxCommon::aircraftSituationToFsxPBH(const CAircraftSituation &situation) + { + // MSFS has inverted pitch and bank angles + SIMCONNECT_DATA_PBH pbh; + pbh.Pitch = -situation.getPitch().value(CAngleUnit::deg()); + pbh.Bank = -situation.getBank().value(CAngleUnit::deg()); + pbh.Heading = situation.getHeading().value(CAngleUnit::deg()); + return pbh; + } + SIMCONNECT_DATA_INITPOSITION CSimulatorFsxCommon::coordinateToFsxPosition(const ICoordinateGeodetic &coordinate) { SIMCONNECT_DATA_INITPOSITION position; @@ -1982,6 +2006,15 @@ namespace BlackSimPlugin return position; } + SIMCONNECT_DATA_LATLONALT CSimulatorFsxCommon::coordinateToFsxLatLonAlt(const ICoordinateGeodetic &coordinate) + { + SIMCONNECT_DATA_LATLONALT lla; + lla.Latitude = coordinate.latitude().value(CAngleUnit::deg()); + lla.Longitude = coordinate.longitude().value(CAngleUnit::deg()); + lla.Altitude = coordinate.geodeticHeight().value(CLengthUnit::ft()); // already corrected in interpolator if there is an underflow + return lla; + } + void CSimulatorFsxCommon::synchronizeTime(const CTime &zuluTimeSim, const CTime &localTimeSim) { if (!m_simTimeSynced) { return; } diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h index 1f294e800..2112e2caa 100644 --- a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h +++ b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h @@ -261,6 +261,12 @@ namespace BlackSimPlugin //! Remove the CSimConnectObject linked in the trace bool removeSimObjectForTrace(const TraceFsxSendId &trace); + //! Remove camera if any + virtual void removeCamera(CSimConnectObject &simObject); + + //! Remove observer if any + virtual void removeObserver(CSimConnectObject &simObject); + //! Trace if required, log errors HRESULT logAndTraceSendId(HRESULT hr, const QString &warningMsg, const QString &functionName, const QString &functionDetails = {}); @@ -281,6 +287,20 @@ namespace BlackSimPlugin static bool is64bit() { return (BlackConfig::CBuildConfig::buildWordSize() == 64); } //! @} + //! Format conversion + //! \note must be valid situation + static SIMCONNECT_DATA_INITPOSITION aircraftSituationToFsxPosition(const BlackMisc::Aviation::CAircraftSituation &situation, bool sendGnd = true); + + //! Format conversion + //! \note must be valid situation + static SIMCONNECT_DATA_PBH aircraftSituationToFsxPBH(const BlackMisc::Aviation::CAircraftSituation &situation); + + //! Format conversion + static SIMCONNECT_DATA_INITPOSITION coordinateToFsxPosition(const BlackMisc::Geo::ICoordinateGeodetic &coordinate); + + //! Format conversion + static SIMCONNECT_DATA_LATLONALT coordinateToFsxLatLonAlt(const BlackMisc::Geo::ICoordinateGeodetic &coordinate); + static constexpr qint64 AutoTraceOffsetMs = 10 * 1000; //!< how long do we trace? HANDLE m_hSimConnect = nullptr; //!< handle to SimConnect object DispatchProc m_dispatchProc = &CSimulatorFsxCommon::SimConnectProc; //!< called function for dispatch, can be overriden by specialized P3D function @@ -454,13 +474,6 @@ namespace BlackSimPlugin //! The SimConnect object for idxs CSimConnectObject getSimObjectForObjectId(DWORD objectId) const; - //! Format conversion - //! \note must be valid situation - SIMCONNECT_DATA_INITPOSITION aircraftSituationToFsxPosition(const BlackMisc::Aviation::CAircraftSituation &situation, bool sendGnd = true); - - //! Format conversion - SIMCONNECT_DATA_INITPOSITION coordinateToFsxPosition(const BlackMisc::Geo::ICoordinateGeodetic &coordinate); - //! Sync time with user's computer void synchronizeTime(const BlackMisc::PhysicalQuantities::CTime &zuluTimeSim, const BlackMisc::PhysicalQuantities::CTime &localTimeSim); diff --git a/src/plugins/simulator/p3d/simulatorp3d.cpp b/src/plugins/simulator/p3d/simulatorp3d.cpp index 764106a7b..e5c20da03 100644 --- a/src/plugins/simulator/p3d/simulatorp3d.cpp +++ b/src/plugins/simulator/p3d/simulatorp3d.cpp @@ -102,7 +102,7 @@ namespace BlackSimPlugin const double latDeg = reference.latitude().value(CAngleUnit::deg()); const double lngDeg = reference.longitude().value(CAngleUnit::deg()); const double maxAltFt = hasHeight ? reference.geodeticHeight().value(CLengthUnit::ft()) : 50000; - const DWORD dwGridWidth = 1.0; + const DWORD dwGridWidth = 1.0; const DWORD dwGridHeight = 1.0; const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimObjTerrainProbe(); // P3D we use new request id each time (no simobject) @@ -136,55 +136,93 @@ namespace BlackSimPlugin bool CSimulatorP3D::followAircraft(const CCallsign &callsign) { if (this->isShuttingDownOrDisconnected()) { return false; } - if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return false; } - // Experimental code, suffering from bugs and also requiring - // P3D v4.2 (bugs in V4.1 CSimConnectObject &simObject = m_simConnectObjects[callsign]; - - const CAircraftModel model = simObject.getAircraft().getModel(); - const QString viewName = "Commercial Jet-" + callsign.asString(); - const char *view = viewName.toLatin1().constData(); - CLogMessage(this).warning("Modelview %1") << viewName; - Q_UNUSED(model); - - HRESULT hr = SimConnect_ChangeView(m_hSimConnect, view); - return isOk(hr); - - /** if (!simObject.hasValidRequestAndObjectId()) { return false; } if (simObject.getCallsignByteArray().isEmpty()) { return false; } - const char *cs = simObject.getCallsignByteArray().constData(); + HRESULT hr = s_false(); - HRESULT hr = S_FALSE; + // + // Experimental code + // + + /** version a + const char *cameraName = simObject.getCallsignByteArray().constData(); if (!simObject.hasCamera()) { + SIMCONNECT_DATA_XYZ position; position.x = 25; position.y = 25; position.z = 0; + SIMCONNECT_DATA_PBH rotation; rotation.Pitch = 0; rotation.Bank = 0; rotation.Heading = -90; + simObject.setCameraPositionAndRotation(position, rotation); + GUID guid; CoCreateGuid(&guid); - const SIMCONNECT_CAMERA_TYPE cameraType = SIMCONNECT_CAMERA_TYPE_OBJECT_CENTER; - hr = SimConnect_CreateCameraDefinition(m_hSimConnect, guid, cameraType, cs, simObject.cameraPosition(), simObject.cameraRotation()); - if (hr == S_OK) + // SIMCONNECT_CAMERA_TYPE_OBJECT_AI_VIRTUAL needs a P3D configuration + // SIMCONNECT_CAMERA_TYPE_LATLONALT_ORTHOGONAL is a top down view + // SIMCONNECT_CAMERA_TYPE_FIXED position at one place + const SIMCONNECT_CAMERA_TYPE cameraType = SIMCONNECT_CAMERA_TYPE_OBJECT_AI_CENTER; + hr = SimConnect_CreateCameraDefinition(m_hSimConnect, guid, cameraType, cameraName, simObject.cameraPosition(), simObject.cameraRotation()); + if (isOk(hr)) { const SIMCONNECT_OBJECT_ID objectId = static_cast(simObject.getObjectId()); - const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimData(); - hr = SimConnect_CreateCameraInstance(m_hSimConnect, guid, cs, objectId, requestId); - if (hr == S_OK) + const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimObjAircraft(); + hr = SimConnect_CreateCameraInstance(m_hSimConnect, guid, cameraName, objectId, requestId); + if (isOk(hr)) { simObject.setCameraGUID(guid); } } + + } + version a **/ + + /** version b + const CAircraftModel m = simObject.getAircraftModel(); + const QString viewName = "Regional Jet-" + callsign.asString(); + const QByteArray viewNameBA = viewName.toLatin1(); + hr = SimConnect_ChangeView(m_hSimConnect, viewNameBA.constData()); + **/ + + // Observer is P3D only, not FSX + const CAircraftSituation situation = m_lastSentSituations[callsign]; + if (situation.isNull()) { return false; } + SIMCONNECT_DATA_OBSERVER obs; + SIMCONNECT_DATA_PBH pbh; pbh.Pitch = pbh.Bank = pbh.Heading = 0; + obs.Rotation = pbh; + obs.Position = coordinateToFsxLatLonAlt(situation); + obs.Regime = SIMCONNECT_OBSERVER_REGIME_GHOST; + obs.RotateOnTarget = TRUE; + obs.FocusFixed = TRUE; + obs.FieldOfViewH = 30; // deg. + obs.FieldOfViewV = 30; // deg. + obs.LinearStep = 20; // meters + obs.AngularStep = 10; // deg. + + const char *observerName = simObject.getCallsignByteArray().constData(); + hr = SimConnect_CreateObserver(m_hSimConnect, observerName, obs); + if (isOk(hr)) + { + SIMCONNECT_DATA_XYZ offset; + offset.x = offset.y = 30; + offset.z = 0; + hr = SimConnect_ObserverAttachToEntityOn(m_hSimConnect, observerName, simObject.getObjectId(), offset); + if (isOk(hr)) + { + SimConnect_SetObserverLookAt(m_hSimConnect, observerName, obs.Position); + + // const QByteArray viewName = QStringLiteral("Observer %1").arg(callsign.asString()).toLatin1(); + hr = SimConnect_OpenView(m_hSimConnect, observerName); + + simObject.setObserverName(callsign.asString()); + } } - if (!simObject.hasCamera()) { return false; } - hr = SimConnect_OpenView(m_hSimConnect, cs); - return hr == S_OK; - **/ + return isOk(hr); } HRESULT CSimulatorP3D::initEventsP3D() { - HRESULT hr = S_OK; - if (hr != S_OK) + HRESULT hr = s_ok(); + if (isFailure(hr)) { CLogMessage(this).error("P3D plugin error: %1") << "initEventsP3D failed"; return hr; @@ -192,6 +230,23 @@ namespace BlackSimPlugin return hr; } + void CSimulatorP3D::removeCamera(FsxCommon::CSimConnectObject &simObject) + { + if (!simObject.hasCamera()) { return; } + simObject.removeCamera(); + // const char *cameraName = simObject.getCallsignByteArray().constData(); + // SimConnect_DeleteCameraInstance(m_hSimConnect, simObject.getCameraGUID(), 0); + // SimConnect_CloseView(m_hSimConnect, cameraName); + } + + void CSimulatorP3D::removeObserver(CSimConnectObject &simObject) + { + if (simObject.getObserverName().isEmpty()) { return; } + + QByteArray viewName = simObject.getObserverName().toLatin1(); + SimConnect_CloseView(m_hSimConnect, viewName.constData()); + } + bool CSimulatorP3D::releaseAIControl(const CSimConnectObject &simObject, SIMCONNECT_DATA_REQUEST_ID requestId) { // completely remove AI control diff --git a/src/plugins/simulator/p3d/simulatorp3d.h b/src/plugins/simulator/p3d/simulatorp3d.h index 4818a2fba..518b5e1b7 100644 --- a/src/plugins/simulator/p3d/simulatorp3d.h +++ b/src/plugins/simulator/p3d/simulatorp3d.h @@ -55,6 +55,12 @@ namespace BlackSimPlugin virtual HRESULT initEventsP3D() override; #ifdef Q_OS_WIN64 + //! \copydoc FsxCommon::CSimulatorFsxCommon::removeCamera + virtual void removeCamera(FsxCommon::CSimConnectObject &simObject) override; + + //! \copydoc FsxCommon::CSimulatorFsxCommon::removeObserver + virtual void removeObserver(FsxCommon::CSimConnectObject &simObject) override; + //! \copydoc FsxCommon::CSimulatorFsxCommon::releaseAIControl //! \remark P3D API release of control virtual bool releaseAIControl(const FsxCommon::CSimConnectObject &simObject, SIMCONNECT_DATA_REQUEST_ID requestId) override;