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