diff --git a/OVP/D3D7Client/VVessel.cpp b/OVP/D3D7Client/VVessel.cpp index a39428973..84ae613be 100644 --- a/OVP/D3D7Client/VVessel.cpp +++ b/OVP/D3D7Client/VVessel.cpp @@ -164,6 +164,11 @@ void vVessel::UpdateRenderVectors() if (logscale) len = log(len + shift) - lshift; else len *= scale; AddVector(unit(F) * (len * pscale), _V(0, 0, 0), scale2, std::string(cbuf), _V(1, 0, 0), alpha, D3DRGB(1, 0.5, 0.5)); } + if ((flag & BFV_SIDEFORCE) && vessel->GetSideForceVector(F)) { + sprintf(cbuf, "SF = %fN", len = length(F)); + if (logscale) len = log(len + shift) - lshift; else len *= scale; + AddVector(unit(F) * (len * pscale), _V(0, 0, 0), scale2, std::string(cbuf), _V(0.0392, 0.6235, 0.4941), alpha, D3DRGB(0.0392, 0.6235, 0.4941)); + } if ((flag & BFV_TOTAL) && vessel->GetForceVector(F)) { sprintf(cbuf, "F = %fN", len = length(F)); if (logscale) len = log(len + shift) - lshift; else len *= scale; diff --git a/OVP/D3D9Client/VVessel.cpp b/OVP/D3D9Client/VVessel.cpp index 86dd4c4d4..e4315e328 100644 --- a/OVP/D3D9Client/VVessel.cpp +++ b/OVP/D3D9Client/VVessel.cpp @@ -860,6 +860,7 @@ void vVessel::RenderVectors (LPDIRECT3DDEVICE9 dev, D3D9Pad *pSkp) if (bfvmode & BFV_LIFT) { vessel->GetLiftVector(vector); if (length(vector)>len) len = length(vector); } if (bfvmode & BFV_TOTAL) { vessel->GetForceVector(vector); if (length(vector)>len) len = length(vector); } if (bfvmode & BFV_TORQUE) { vessel->GetTorqueVector(vector); if (length(vector)>len) len = length(vector); } + if (bfvmode & BFV_SIDEFORCE) { vessel->GetSideForceVector(vector); if (length(vector) > len) len = length(vector); } lscale = float(size * sclset / len); } @@ -924,6 +925,15 @@ void vVessel::RenderVectors (LPDIRECT3DDEVICE9 dev, D3D9Pad *pSkp) RenderAxisLabel(pSkp, ptr(D3DXCOLOR(1,0,1,alpha)), vector, lscale, scale, label, bLog); } } + + if (bfvmode & BFV_SIDEFORCE) { + vessel->GetSideForceVector(vector); + if (length(vector) > threshold) { + RenderAxisVector(pSkp, ptr(D3DXCOLOR(0.0392, 0.6235, 0.4941, alpha)), vector, lscale, scale, bLog); + sprintf_s(label, 64, "SF = %sN", value_string(length(vector))); + RenderAxisLabel(pSkp, ptr(D3DXCOLOR(0.0392, 0.6235, 0.4941, alpha)), vector, lscale, scale, label, bLog); + } + } } } diff --git a/Orbitersdk/include/GraphicsAPI.h b/Orbitersdk/include/GraphicsAPI.h index b67fb3443..35c9fa4ee 100644 --- a/Orbitersdk/include/GraphicsAPI.h +++ b/Orbitersdk/include/GraphicsAPI.h @@ -319,6 +319,7 @@ typedef void *HDC; #define BFV_DRAG 0x0020 ///< Show drag vector #define BFV_TOTAL 0x0040 ///< Show total force vector #define BFV_TORQUE 0x0080 ///< Show torque vector +#define BFV_SIDEFORCE 0x0100 ///< Show side-force vector /// @} /// \defgroup favflag Bit flags for frame axis vector render options diff --git a/Orbitersdk/include/OrbiterAPI.h b/Orbitersdk/include/OrbiterAPI.h index 72e69f5c2..13761b512 100644 --- a/Orbitersdk/include/OrbiterAPI.h +++ b/Orbitersdk/include/OrbiterAPI.h @@ -1627,6 +1627,17 @@ typedef void (*AirfoilCoeffFuncEx)( // Contains additional parameters (calling vessel and pointer to // user-defined data) +typedef void (*AirfoilCoeffFuncEx2)( + VESSEL* v, + VECTOR3 WindDir, + double alpha, double beta, double gamma, + double M, double Re, void* context, + double* CA, double* CN, double* CY, + double* Cl, double* Cm, double* Cn); +// Further extended version of aerodynamic coefficients callback function +// Contains additional parameters (calling vessel and pointer to +// user-defined data for all force and moment coefficients) + // =========================================================================== /// \ingroup defines @@ -1638,11 +1649,12 @@ typedef void (*AirfoilCoeffFuncEx)( * * Defines the orientation of an airfoil by the direction of the lift vector * generated (vertical or horizontal). - * \sa VESSEL::CreateAirfoil, VESSEL::CreateAirfoil2, VESSEL::CreateAirfoil3 + * \sa VESSEL::CreateAirfoil, VESSEL::CreateAirfoil2, VESSEL::CreateAirfoil3, VESSEL::CreateAirfoil4 */ typedef enum { LIFT_VERTICAL, ///< lift direction is vertical (e.g. elevator) - LIFT_HORIZONTAL ///< lift direction is horizontal (e.g. rudder) + LIFT_HORIZONTAL, ///< lift direction is horizontal (e.g. rudder) + FORCE_AND_MOMENT ///< complex model of forces and moments along and about all axes } AIRFOIL_ORIENTATION; //@} diff --git a/Orbitersdk/include/VesselAPI.h b/Orbitersdk/include/VesselAPI.h index 549c18590..3f9e891f7 100644 --- a/Orbitersdk/include/VesselAPI.h +++ b/Orbitersdk/include/VesselAPI.h @@ -1488,6 +1488,40 @@ class OAPIFUNC VESSEL { */ AIRFOILHANDLE CreateAirfoil3 (AIRFOIL_ORIENTATION align, const VECTOR3 &ref, AirfoilCoeffFuncEx cf, void *context, double c, double S, double A) const; + /** + * \brief Creates a new airfoil and defines its aerodynamic properties. + * \param ref centre of pressure in vessel coordinates [m], nominally this shoud be at the CoM. + * \param cf pointer to coefficient callback function (see notes) + * \param context pointer to data block passed to cf callback function + * \param c airfoil chord length [m] + * \param S wing area [m2] + * \param A wing aspect ratio + * \return Handle for the new airfoil. + * \note This method is an extension to \ref CreateAirfoil3, using a + * more versatile coefficient callback function. + * \note AirfoilCoeffFuncEx has the following interface: + * \code + * void (*AirfoilCoeffFuncEx2)( + * VESSEL* v, + * double alpha, double beta, double gamma, + * double M, double Re, void* context, + * double* CA, double* CN, double* CY, + * double* Cl, double* Cm, double* Cn); + * \endcode + * where \e v is a pointer to the calling vessel instance, and + * \a context is the pointer passed to CreateAirfoil4. It can be + * used to make available to the callback function any additional + * parameters required to calculate the lift and drag coefficients. + * The coefficients: CA, CN, and CY are force coefficients along the + * vessel's Z, Y, and X body axes respectively. + * The coefficients: Cl, Cm, and Cn are moment coefficients about the + * vessel's Z, X, and Y body axes respectively. + * All other parameters are identical to AirfoilCoeffFunc (see + * \ref CreateAirfoil). + * \sa CreateAirfoil, CreateAirfoil2, CreateAirfoil3, EditAirfoil, DelAirfoil + */ + AIRFOILHANDLE CreateAirfoil4(const VECTOR3& ref, AirfoilCoeffFuncEx2 cf, void* context, double c, double S, double A) const; + /** * \brief Returns the parameters of an existing airfoil. * \param [in] hAirfoil airfoil handle @@ -1982,6 +2016,14 @@ class OAPIFUNC VESSEL { */ double GetDrag () const; + /** + * \brief Returns magnitude of aerodynamic side-force vector. + * \return Magnitude of drag force vector [N]. + * \note Return value is the sum of drag components from all airfoils. + * \sa GetDragVector, GetLift + */ + double GetSideForce() const; + /** * \brief Returns gravitational force vector in local vessel coordinates. * \param[out] G gravitational force vector [N] @@ -2037,6 +2079,20 @@ class OAPIFUNC VESSEL { */ bool GetDragVector (VECTOR3 &D) const; + /** + * \brief Returns aerodynamic side-force force vector in local vessel + * coordinates. + * \param[out] SF drag vector [N] + * \return false indicates zero side-force. In that case, the returned vector + * is (0,0,0). + * \note On return, SD contains the sum of Side-force components from all + * airfoils. + * \note The side-force vector is mutually orthogonal to the Lift and Drag vectors + * \sa GetDrag, GetWeightVector, GetThrustVector, GetLiftVector, + * GetForceVector + */ + bool GetSideForceVector (VECTOR3 &SF) const; + /** * \brief Returns total force vector acting on the vessel in local * vessel coordinates. diff --git a/Src/Orbiter/OptionsPages.cpp b/Src/Orbiter/OptionsPages.cpp index 0b3ac1ffd..1ebdad07b 100644 --- a/Src/Orbiter/OptionsPages.cpp +++ b/Src/Orbiter/OptionsPages.cpp @@ -2177,8 +2177,8 @@ const HELPCONTEXT* OptionsPage_Forces::HelpContext() const void OptionsPage_Forces::UpdateControls(HWND hPage) { - std::array residForces = { - IDC_OPT_VEC_WEIGHT, IDC_OPT_VEC_THRUST, IDC_OPT_VEC_LIFT, IDC_OPT_VEC_DRAG, IDC_OPT_VEC_TOTAL, + std::array residForces = { + IDC_OPT_VEC_WEIGHT, IDC_OPT_VEC_THRUST, IDC_OPT_VEC_LIFT, IDC_OPT_VEC_DRAG, IDC_OPT_VEC_SIDEFORCE, IDC_OPT_VEC_TOTAL, IDC_OPT_VEC_TORQUE, IDC_OPT_VEC_LINSCL, IDC_OPT_VEC_LOGSCL, IDC_OPT_VEC_SCALE, IDC_OPT_VEC_OPACITY, IDC_STATIC1, IDC_STATIC2, IDC_STATIC3, IDC_STATIC4, IDC_STATIC5 }; @@ -2193,6 +2193,7 @@ void OptionsPage_Forces::UpdateControls(HWND hPage) SendDlgItemMessage(hPage, IDC_OPT_VEC_THRUST, BM_SETCHECK, vecFlag & BFV_THRUST ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hPage, IDC_OPT_VEC_LIFT, BM_SETCHECK, vecFlag & BFV_LIFT ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hPage, IDC_OPT_VEC_DRAG, BM_SETCHECK, vecFlag & BFV_DRAG ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(hPage, IDC_OPT_VEC_SIDEFORCE, BM_SETCHECK, vecFlag & BFV_SIDEFORCE ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hPage, IDC_OPT_VEC_TOTAL, BM_SETCHECK, vecFlag & BFV_TOTAL ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hPage, IDC_OPT_VEC_TORQUE, BM_SETCHECK, vecFlag & BFV_TORQUE ? BST_CHECKED : BST_UNCHECKED, 0); SendDlgItemMessage(hPage, IDC_OPT_VEC_LINSCL, BM_SETCHECK, vecFlag & BFV_LOGSCALE ? BST_UNCHECKED : BST_CHECKED, 0); @@ -2227,6 +2228,7 @@ BOOL OptionsPage_Forces::OnCommand(HWND hPage, WORD ctrlId, WORD notification, H case IDC_OPT_VEC_THRUST: case IDC_OPT_VEC_LIFT: case IDC_OPT_VEC_DRAG: + case IDC_OPT_VEC_SIDEFORCE: case IDC_OPT_VEC_TOTAL: case IDC_OPT_VEC_TORQUE: case IDC_OPT_VEC_LINSCL: @@ -2247,14 +2249,15 @@ void OptionsPage_Forces::OnItemClicked(HWND hPage, WORD ctrlId) bool check = (SendDlgItemMessage(hPage, ctrlId, BM_GETCHECK, 0, 0) == TRUE); DWORD flag; switch (ctrlId) { - case IDC_OPT_VEC: flag = BFV_ENABLE; break; - case IDC_OPT_VEC_WEIGHT: flag = BFV_WEIGHT; break; - case IDC_OPT_VEC_THRUST: flag = BFV_THRUST; break; - case IDC_OPT_VEC_LIFT: flag = BFV_LIFT; break; - case IDC_OPT_VEC_DRAG: flag = BFV_DRAG; break; - case IDC_OPT_VEC_TOTAL: flag = BFV_TOTAL; break; - case IDC_OPT_VEC_TORQUE: flag = BFV_TORQUE; break; - case IDC_OPT_VEC_LINSCL: flag = BFV_LOGSCALE; check = false; break; + case IDC_OPT_VEC: flag = BFV_ENABLE; break; + case IDC_OPT_VEC_WEIGHT: flag = BFV_WEIGHT; break; + case IDC_OPT_VEC_THRUST: flag = BFV_THRUST; break; + case IDC_OPT_VEC_LIFT: flag = BFV_LIFT; break; + case IDC_OPT_VEC_DRAG: flag = BFV_DRAG; break; + case IDC_OPT_VEC_SIDEFORCE: flag = BFV_SIDEFORCE; break; + case IDC_OPT_VEC_TOTAL: flag = BFV_TOTAL; break; + case IDC_OPT_VEC_TORQUE: flag = BFV_TORQUE; break; + case IDC_OPT_VEC_LINSCL: flag = BFV_LOGSCALE; check = false; break; case IDC_OPT_VEC_LOGSCL: flag = BFV_LOGSCALE; check = true; break; default: flag = 0; break; } diff --git a/Src/Orbiter/Orbiter.rc b/Src/Orbiter/Orbiter.rc index c5424189f..3771b5264 100644 --- a/Src/Orbiter/Orbiter.rc +++ b/Src/Orbiter/Orbiter.rc @@ -764,7 +764,8 @@ BEGIN CONTROL "Thrust",IDC_OPT_VEC_THRUST,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,40,44,10 CONTROL "Lift",IDC_OPT_VEC_LIFT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,56,28,44,10 CONTROL "Drag",IDC_OPT_VEC_DRAG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,56,40,44,10 - CONTROL "Total",IDC_OPT_VEC_TOTAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,56,80,10 + CONTROL "Side", IDC_OPT_VEC_SIDEFORCE, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, 56, 52, 44, 10 + CONTROL "Total",IDC_OPT_VEC_TOTAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,68,80,10 GROUPBOX "Angular moments",IDC_STATIC2,6,80,100,26 CONTROL "Torque",IDC_OPT_VEC_TORQUE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,92,80,10 GROUPBOX "Display",IDC_STATIC3,114,16,100,90 diff --git a/Src/Orbiter/Vessel.cpp b/Src/Orbiter/Vessel.cpp index 6b3122d0e..08b9b9146 100644 --- a/Src/Orbiter/Vessel.cpp +++ b/Src/Orbiter/Vessel.cpp @@ -315,7 +315,7 @@ void Vessel::SetDefaultState () m_bThrustEngaged = false; bForceActive = false; rpressure = g_pOrbiter->Cfg()->CfgPhysicsPrm.bRadiationPressure; - Lift = Drag = 0.0; + Lift = Drag = SideForce = 0.0; attach_status.pname = 0; hudskp = NULL; next_hullvtx = 0; @@ -1281,6 +1281,21 @@ bool Vessel::GetDragVector (Vector &D) const // ============================================================== +bool Vessel::GetSideForceVector (Vector &SF) const +{ + if (!SideForce) { + SF.Set (0, 0, 0); + return false; + } else { + double v0 = std::hypot(sp.airvel_ship.x, sp.airvel_ship.z); + double scale = (v0 ? SideForce / v0 : 0.0); + SF.Set(sp.airvel_ship.z * scale, 0, - sp.airvel_ship.x * scale); + return true; + } +} + +// ============================================================== + bool Vessel::GetForceVector (Vector &F) const { // Obtain total force vector from acceleration vector @@ -1767,6 +1782,29 @@ AirfoilSpec *Vessel::CreateAirfoil (AIRFOIL_ORIENTATION align, const Vector &ref // ============================================================== +AirfoilSpec* Vessel::CreateAirfoil(AIRFOIL_ORIENTATION align, const Vector& ref, AirfoilCoeffFuncEx2 cf, void* context, double c, double S, double A) +{ + AirfoilSpec* af, ** tmp = new AirfoilSpec * [nairfoil + 1]; TRACENEW + if (nairfoil) { + memcpy(tmp, airfoil, nairfoil * sizeof(AirfoilSpec*)); + delete[]airfoil; + } + airfoil = tmp; + + af = airfoil[nairfoil++] = new AirfoilSpec; TRACENEW + af->version = 2; + af->align = align; + af->ref.Set(ref); + af->cf = (AirfoilCoeffFunc)cf; + af->context = context; + af->c = c; + af->S = S; + af->A = A; + return af; +} + +// ============================================================== + bool Vessel::GetAirfoilParam (AirfoilSpec *af, VECTOR3 *ref, AirfoilCoeffFunc *cf, void **context, double *c, double *S, double *A) { for (DWORD i = 0; i < nairfoil; i++) { @@ -3740,7 +3778,7 @@ double Vessel::GetGravityGradientDamping () const void Vessel::UpdateBodyForces () { - Lift = Drag = 0.0; + Lift = Drag = SideForce = 0.0; if (m_thruster.size()) UpdateThrustForces (); if (CtrlSurfSyncMode) ApplyControlSurfaceLevels (); @@ -4000,6 +4038,7 @@ void Vessel::UpdateAerodynamicForces () double aoa = atan2 (-sp.airvel_ship.y,sp.airvel_ship.z); // angle of attack double beta = atan2 (-sp.airvel_ship.x,sp.airvel_ship.z); // lateral angle of attack (slip) + double gamma = (!sp.airvel_ship.y) ? atan2(-sp.airvel_ship.x, -sp.airvel_ship.y) : 0; const double mu = 1.7894e-5; // viscosity coefficient dummy - MAKE VARIABLE! double Re0 = sp.atmrho * sp.airspd / mu; // template for Reynolds coefficient (to be multiplied by chord length) @@ -4031,32 +4070,51 @@ void Vessel::UpdateAerodynamicForces () Vector ddir (-sp.airvel_ship.unit()); // freestream airflow direction (= drag direction) Vector ldir (0, sp.airvel_ship.z, -sp.airvel_ship.y); ldir.unify(); // lift direction (vertical on drag and transversal ship axis) Vector sdir (sp.airvel_ship.z, 0, -sp.airvel_ship.x); sdir.unify(); // sidelift direction (vertical on drag and vertical ship axis) - double lift, drag, S; - double Cl, Cm, Cd; // lift, moment, drag coeffs + double lift, drag, side, S; + double CL, CD; // lift, drag coeffs (used by AirfoilCoeffFunc and AirfoilCoeffFuncEx) + double CA, CN, CY; //force coefficients along Orbiter Z, Y, X axes (AirfoilCoeffFuncEx2) + double Cl, Cm, Cn; //moment coefficients about Orbiter Z, X, Y axes (AirfoilCoeffFuncEx2 uses Cl and Cn) // airfoil lift+drag components for (i = 0; i < nairfoil; i++) { AirfoilSpec *af = airfoil[i]; if (af->align == LIFT_VERTICAL) { if (af->version == 0) - af->cf (aoa, sp.atmM, Re0*af->c, &Cl, &Cm, &Cd); + af->cf (aoa, sp.atmM, Re0*af->c, &CL, &Cm, &CD); else - ((AirfoilCoeffFuncEx)af->cf)((VESSEL*)modIntf.v, aoa, sp.atmM, Re0*af->c, af->context, &Cl, &Cm, &Cd); + ((AirfoilCoeffFuncEx)af->cf)((VESSEL*)modIntf.v, aoa, sp.atmM, Re0*af->c, af->context, &CL, &Cm, &CD); if (af->S) S = af->S; else S = fabs(ddir.z)*cs.z + fabs(ddir.y)*cs.y; // use projected vessel CS as reference area - AddForce (ldir*(lift=(Cl*sp.dynp*S)) + ddir*(drag=(Cd*sp.dynp*S)), af->ref); + AddForce (ldir*(lift=(CL*sp.dynp*S)) + ddir*(drag=(CD*sp.dynp*S)), af->ref); if (Cm) Amom_add.x += Cm*sp.dynp*af->S*af->c; Lift += lift, Drag += drag; - } else { // horizontal lift component + } else if (af->align == LIFT_HORIZONTAL) { // horizontal lift component if (af->version == 0) - af->cf (beta, sp.atmM, Re0*af->c, &Cl, &Cm, &Cd); + af->cf (beta, sp.atmM, Re0*af->c, &CL, &Cm, &CD); else - ((AirfoilCoeffFuncEx)af->cf)((VESSEL*)modIntf.v, beta, sp.atmM, Re0*af->c, af->context, &Cl, &Cm, &Cd); + ((AirfoilCoeffFuncEx)af->cf)((VESSEL*)modIntf.v, beta, sp.atmM, Re0*af->c, af->context, &CL, &Cm, &CD); if (af->S) S = af->S; else S = fabs(ddir.z)*cs.z + fabs(ddir.x)*cs.z; // use projected vessel CS as reference area - AddForce (sdir*(Cl*sp.dynp*S) + ddir*(drag=(Cd*sp.dynp*S)), af->ref); + AddForce (sdir*(side = (CL*sp.dynp*S)) + ddir*(drag=(CD*sp.dynp*S)), af->ref); if (Cm) Amom_add.y += Cm*sp.dynp*af->S*af->c; Drag += drag; + SideForce += side; + } else if (af->align == FORCE_AND_MOMENT) { + ((AirfoilCoeffFuncEx2)af->cf)((VESSEL*)modIntf.v, -_V(sp.airvel_ship.unit().x, sp.airvel_ship.unit().y, sp.airvel_ship.unit().z), aoa, beta, gamma, sp.atmM, Re0 * af->c, af->context, &CA, &CN, &CY, &Cl, &Cm, &Cn); + if (af->S) S = af->S; + else S = fabs(ddir.z) * cs.z + fabs(ddir.x) * cs.z; // use projected vessel CS as reference area + + Vector AeroForce((CY * S) * sp.dynp, (CN * S) * sp.dynp, -(CA * S) * sp.dynp); + + AddForce(AeroForce, af->ref); + Lift = dotp(AeroForce, ldir); + Drag = dotp(AeroForce, ddir); + SideForce = dotp(AeroForce, sdir); + + Amom_add.x += Cm * sp.dynp * af->S * af->c; + Amom_add.y -= Cn * sp.dynp * af->S * af->c; + Amom_add.z -= Cl * sp.dynp * af->S * af->c; + } } @@ -4089,8 +4147,8 @@ void Vessel::UpdateAerodynamicForces () for (i = 0; i < ndragel; i++) { double lvl = *dragel[i]->drag; if (lvl) { - Cd = lvl*dragel[i]->factor; - drag = Cd*sp.dynp; + CD = lvl*dragel[i]->factor; + drag = CD*sp.dynp; AddForce (ddir*drag, dragel[i]->ref); Drag += drag; } @@ -6669,6 +6727,11 @@ double VESSEL::GetDrag (void) const return vessel->Drag; } +double VESSEL::GetSideForce() const +{ + return vessel->SideForce; +} + bool VESSEL::GetWeightVector (VECTOR3 &G) const { static Vector F; @@ -6701,6 +6764,14 @@ bool VESSEL::GetDragVector (VECTOR3 &D) const return bDrag; } +bool VESSEL::GetSideForceVector(VECTOR3 &SF) const +{ + static Vector F; + bool bSideForce = vessel->GetSideForceVector(F); + CopyVector(F, SF); + return bSideForce; +} + bool VESSEL::GetForceVector (VECTOR3 &F) const { static Vector FF; @@ -8374,6 +8445,12 @@ AIRFOILHANDLE VESSEL::CreateAirfoil3 (AIRFOIL_ORIENTATION align, const VECTOR3 & return (AIRFOILHANDLE)vessel->CreateAirfoil (align, r, cf, context, c, S, A); } +AIRFOILHANDLE VESSEL::CreateAirfoil4 (const VECTOR3& ref, AirfoilCoeffFuncEx2 cf, void* context, double c, double S, double A) const +{ + Vector r(MakeVector(ref)); + return (AIRFOILHANDLE)vessel->CreateAirfoil(FORCE_AND_MOMENT, r, cf, context, c, S, A); +} + bool VESSEL::GetAirfoilParam (AIRFOILHANDLE hAirfoil, VECTOR3 *ref, AirfoilCoeffFunc *cf, void **context, double *c, double *S, double *A) const { return vessel->GetAirfoilParam ((AirfoilSpec*)hAirfoil, ref, cf, context, c, S, A); diff --git a/Src/Orbiter/Vessel.h b/Src/Orbiter/Vessel.h index 83ebfc09f..eac06a4e5 100644 --- a/Src/Orbiter/Vessel.h +++ b/Src/Orbiter/Vessel.h @@ -105,7 +105,7 @@ typedef struct { // obsolete exhaust render definition } OldExhaustSpec; typedef struct { // airfoil definition - int version; // 0: uses AirfoilCoeffFunc, 1: uses AirfoilCoeffFuncEx + int version; // 0: uses AirfoilCoeffFunc, 1: uses AirfoilCoeffFuncEx, 3: uses AirfoilCoeffFuncEx2 AIRFOIL_ORIENTATION align; // vertical or horizontal Vector ref; // lift,drag attack reference point AirfoilCoeffFunc cf; // pointer to coefficients callback function @@ -377,6 +377,10 @@ class Vessel: public VesselBase { // Returns drag vector in D (in local vessel frame). // Return value indicates if drag is present + bool GetSideForceVector(Vector& SF) const; + // Returns side-force vector in SF (in local vessel frame). + // Return value indicates if side-force is present + bool GetForceVector (Vector &F) const; // Returns total linear force vector acting on the vessel // Return value is always true @@ -805,6 +809,9 @@ class Vessel: public VesselBase { AirfoilSpec *CreateAirfoil (AIRFOIL_ORIENTATION align, const Vector &ref, AirfoilCoeffFuncEx cf, void *context, double c, double S, double A); // Create a new airfoil; extended version + AirfoilSpec* CreateAirfoil(AIRFOIL_ORIENTATION align, const Vector& ref, AirfoilCoeffFuncEx2 cf, void* context, double c, double S, double A); + // Create a new airfoil; extended force and moment version + bool GetAirfoilParam (AirfoilSpec *af, VECTOR3 *ref, AirfoilCoeffFunc *cf, void **context, double *c, double *S, double *A); // Return airfoil parameters @@ -1663,7 +1670,7 @@ class Vessel: public VesselBase { Vector Thrust; // linear thrust vector (sum of all thruster contributions) mutable Vector Weight; // weight vector (due to gravitational acceleration) mutable bool weight_valid; // flag for 'Weight' up to date - double Lift, Drag; // current lift and drag magnitudes + double Lift, Drag, SideForce; // current lift and drag magnitudes DWORD navmode; // bitflags for currently active navmodes struct HoverHoldAlt { // Hover hold altitude data diff --git a/Src/Orbiter/Vvessel.cpp b/Src/Orbiter/Vvessel.cpp index e1c5cbf62..ed81b1dcf 100644 --- a/Src/Orbiter/Vvessel.cpp +++ b/Src/Orbiter/Vvessel.cpp @@ -830,6 +830,11 @@ void VVessel::UpdateRenderVectors() if (logscale) len = log(len+shift) - lshift; else len *= scale; AddVector (F.unit()*(len*pscale), Vector(0,0,0), scale2, std::string(cbuf), Vector (1,1,1), alpha, D3DRGB (1,1,1)); } + if ((flag & BFV_SIDEFORCE) && vessel->GetSideForceVector(F)) { + sprintf(cbuf, "SF =%sN", FloatStr(len = F.length(), 4)); + if (logscale) len = log(len + shift) - lshift; else len *= scale; + AddVector(F.unit() * (len * pscale), Vector(0, 0, 0), scale2, std::string(cbuf), Vector(0.0392, 0.6235, 0.4941), alpha, D3DRGB(0.0392, 0.6235, 0.4941)); + } if (1) { for (int i = 0; i < vessel->nforcevec; i++) { F = vessel->forcevec[i]; diff --git a/Src/Orbiter/resource.h b/Src/Orbiter/resource.h index 8a75c0d0e..5e0ba24d9 100644 --- a/Src/Orbiter/resource.h +++ b/Src/Orbiter/resource.h @@ -275,6 +275,7 @@ #define IDC_OPT_JOY_STATIC2 1103 #define IDC_OPT_JOY_STATIC3 1104 #define IDC_OPT_JOY_STATIC4 1105 +#define IDC_OPT_VEC_SIDEFORCE 1106 // Unsorted #define IDC_SCN_LIST 1090