Re #139 (D30 / Car select menu) Improved data sheet + work-in-progress overall rating sliders (Acceleration and Cornering)

git-svn-id: https://svn.code.sf.net/p/speed-dreams/code/trunk@3232 30fe4595-0a0c-4342-8851-515496e4dcbd

Former-commit-id: d4e05ad87ac176c3dfc3367a66b5ea7d7215484b
Former-commit-id: da0abd70e92ca3e777f9526d0bc694fa1e90f939
This commit is contained in:
pouillot 2011-01-02 21:44:57 +00:00
parent 72887c9c3a
commit 8c819db3d9
3 changed files with 334 additions and 83 deletions

View file

@ -43,7 +43,7 @@ void RmCarSelectMenu::onActivateCB(void *pCarSelectMenu)
const GfCar* pCurCar = pDriver->getCar();
// Initialize the GUI contents.
GfuiLabelSetText(pMenu->GetMenuHandle(), pMenu->GetDynamicControlId("drivernamelabel"),
GfuiLabelSetText(pMenu->GetMenuHandle(), pMenu->GetDynamicControlId("DriverNameLabel"),
pDriver->getName().c_str());
pMenu->resetCarCategoryComboBox(pCurCar->getCategoryName());
pMenu->resetCarModelComboBox(pCurCar->getCategoryName(), pCurCar->getName());
@ -55,7 +55,7 @@ void RmCarSelectMenu::onActivateCB(void *pCarSelectMenu)
const GfCar* RmCarSelectMenu::getSelectedCarModel() const
{
const char* pszSelCarName =
GfuiComboboxGetText(GetMenuHandle(), GetDynamicControlId("modelcombo"));
GfuiComboboxGetText(GetMenuHandle(), GetDynamicControlId("ModelCombo"));
if (pszSelCarName)
return GfCars::self()->getCarWithName(pszSelCarName);
@ -151,7 +151,7 @@ RmCarSelectMenu::RmCarSelectMenu()
void RmCarSelectMenu::resetCarCategoryComboBox(const std::string& strSelCatName)
{
const int nCatComboId = GetDynamicControlId("categorycombo");
const int nCatComboId = GetDynamicControlId("CategoryCombo");
// Disable the combo-box for non human drivers (robot drivers can't change their car).
GfuiEnable(GetMenuHandle(), nCatComboId, getDriver()->isHuman() ? GFUI_ENABLE : GFUI_DISABLE);
@ -179,7 +179,7 @@ void RmCarSelectMenu::resetCarCategoryComboBox(const std::string& strSelCatName)
void RmCarSelectMenu::resetCarModelComboBox(const std::string& strCatName,
const std::string& strSelCarName)
{
const int nModelComboId = GetDynamicControlId("modelcombo");
const int nModelComboId = GetDynamicControlId("ModelCombo");
// Disable the combo-box for non human drivers (robot drivers can't change their car).
GfuiEnable(GetMenuHandle(), nModelComboId, getDriver()->isHuman() ? GFUI_ENABLE : GFUI_DISABLE);
@ -209,62 +209,70 @@ void RmCarSelectMenu::resetCarModelComboBox(const std::string& strCatName,
void RmCarSelectMenu::resetCarDataSheet(const std::string& strSelCarId)
{
// TODO : Merge params with category / user settings ?
// Open new car params.
static const char* pszDriveWheels[] = { "Rear", "Front", "4" };
// Retrieve selected car.
const GfCar* pSelCar = GfCars::self()->getCar(strSelCarId);
void* hparmSelCar = GfParmReadFile(pSelCar->getDescriptorFileName().c_str(), GFPARM_RMODE_STD);
// Update GUI.
std::ostringstream ossSpecValue;
ossSpecValue << (long)GfParmGetNum(hparmSelCar, SECT_CAR, PRM_MASS, 0 /* SI */, 0) << " kg";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("masslabel"),
ossSpecValue << (long)pSelCar->getMass() << " kg ";
const long nFRMassPercent = (long)(pSelCar->getFrontRearMassRatio() * 100);
if (nFRMassPercent > 50)
ossSpecValue << "(" << nFRMassPercent << "% front)";
else
ossSpecValue << "(" << 100 - nFRMassPercent << "% rear)";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("MassLabel"),
ossSpecValue.str().c_str());
const tdble dMaxRev = GfParmGetNum(hparmSelCar, SECT_ENGINE, PRM_REVSLIM, 0, 0);
tdble dMaxTorque = 0;
tdble dMaxTorqueRev = 0;
tdble dMaxPower = 0;
tdble dMaxPowerRev = 0;
std::ostringstream ossSpecPath;
ossSpecPath << SECT_ENGINE << '/' << ARR_DATAPTS;
const int nEngineTqCurvePts = GfParmGetEltNb(hparmSelCar, ossSpecPath.str().c_str());
for (int nPtInd = 2; nPtInd <= nEngineTqCurvePts; nPtInd++)
{
ossSpecPath.str("");
ossSpecPath << SECT_ENGINE << '/' << ARR_DATAPTS << '/' << nPtInd;
const tdble dRev = GfParmGetNum(hparmSelCar, ossSpecPath.str().c_str(), PRM_RPM, 0, 0);
if (dRev > dMaxRev)
break;
const tdble dTorque = GfParmGetNum(hparmSelCar, ossSpecPath.str().c_str(), PRM_TQ, 0, 0);
if (dTorque > dMaxTorque)
{
dMaxTorque = dTorque;
dMaxTorqueRev = dRev;
}
const tdble dPower = (tdble)(dTorque * dRev / (75 * G));
if (dPower > dMaxPower)
{
dMaxPower = dPower;
dMaxPowerRev = dRev;
}
}
ossSpecValue.str("");
ossSpecValue << (long)dMaxPower << " bhp (" << dMaxPowerRev * 30.0 / PI << " rpm)";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("maxpowerlabel"),
ossSpecValue << pszDriveWheels[pSelCar->getDriveTrain()] << " WD, "
<< pSelCar->getGearsCount() << " gears";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("DriveTrainLabel"),
ossSpecValue.str().c_str());
ossSpecValue.str("");
ossSpecValue << (long)pSelCar->getMaxPower() << " bhp ("
<< (long)(pSelCar->getMaxPowerSpeed() * 30.0 / PI) << " rpm)";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("MaxPowerLabel"),
ossSpecValue.str().c_str());
ossSpecValue.str("");
ossSpecValue << (long)dMaxTorque << " N.m (" << dMaxTorqueRev * 30.0 / PI << " rpm)";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("maxtorquelabel"),
ossSpecValue << (long)pSelCar->getMaxTorque() << " N.m ("
<< (long)(pSelCar->getMaxTorqueSpeed() * 30.0 / PI) << " rpm)";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("MaxTorqueLabel"),
ossSpecValue.str().c_str());
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("Engine1Label"),
"? cyl., ???? cm3");
ossSpecValue.str("");
ossSpecValue << (pSelCar->isTurboCharged() ? "Turbo-charged" : "Naturally-aspirated");
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("Engine2Label"),
ossSpecValue.str().c_str());
GfuiProgressbarSetValue(GetMenuHandle(), GetDynamicControlId("TopSpeedProgress"),
pSelCar->getTopSpeed() * 3.6f);
GfuiProgressbarSetValue(GetMenuHandle(), GetDynamicControlId("PowerMassRatioProgress"),
pSelCar->getMaxPower() / pSelCar->getMass());
GfuiProgressbarSetValue(GetMenuHandle(), GetDynamicControlId("HighSpeedGripProgress"),
pSelCar->getLowSpeedGrip());
GfuiProgressbarSetValue(GetMenuHandle(), GetDynamicControlId("LowSpeedGripProgress"),
pSelCar->getHighSpeedGrip());
GfuiProgressbarSetValue(GetMenuHandle(), GetDynamicControlId("CorneringProgress"),
pSelCar->getInvertedZAxisInertia());
GfLogDebug("%s : ts=%f, mpr=%f, lsg=%f, hsg=%f, izi=%f\n", strSelCarId.c_str(),
pSelCar->getTopSpeed(), pSelCar->getMaxPower() / pSelCar->getMass(),
pSelCar->getLowSpeedGrip(), pSelCar->getHighSpeedGrip(),
pSelCar->getInvertedZAxisInertia());
}
void RmCarSelectMenu::resetSkinComboBox(const std::string& strCarName,
const GfDriverSkin* pSelSkin)
{
const int nSkinComboId = GetDynamicControlId("skincombo");
const int nSkinComboId = GetDynamicControlId("SkinCombo");
// Get really available skins and previews for this car and current driver.
const std::string strCarId =
@ -294,7 +302,7 @@ void RmCarSelectMenu::resetSkinComboBox(const std::string& strCarName,
void RmCarSelectMenu::resetCarPreviewImage(const GfDriverSkin& selSkin)
{
const int nCarImageId = GetDynamicControlId("previewimage");
const int nCarImageId = GetDynamicControlId("PreviewImage");
// Load the preview image.
if (GfFileExists(selSkin.getCarPreviewFileName().c_str()))
@ -326,22 +334,30 @@ bool RmCarSelectMenu::Initialize()
CreateStaticControls();
CreateLabelControl("drivernamelabel");
CreateComboboxControl("categorycombo", this, onChangeCategory);
CreateComboboxControl("modelcombo", this, onChangeModel);
CreateComboboxControl("skincombo", this, onChangeSkin);
CreateStaticImageControl("previewimage");
CreateLabelControl("masslabel");
CreateLabelControl("maxpowerlabel");
CreateLabelControl("maxtorquelabel");
CreateProgressbarControl("topspeedprogress");
CreateProgressbarControl("accelerationprogress");
CreateProgressbarControl("handlingprogress");
CreateProgressbarControl("brakingprogress");
CreateLabelControl("DriverNameLabel");
CreateComboboxControl("CategoryCombo", this, onChangeCategory);
CreateComboboxControl("ModelCombo", this, onChangeModel);
CreateComboboxControl("SkinCombo", this, onChangeSkin);
CreateStaticImageControl("PreviewImage");
CreateLabelControl("DriveTrainLabel");
CreateLabelControl("MaxPowerLabel");
CreateLabelControl("MaxTorqueLabel");
CreateLabelControl("MassLabel");
CreateLabelControl("Engine1Label");
CreateLabelControl("Engine2Label");
CreateProgressbarControl("TopSpeedProgress");
CreateProgressbarControl("PowerMassRatioProgress");
CreateProgressbarControl("HighSpeedGripProgress");
CreateProgressbarControl("LowSpeedGripProgress");
CreateProgressbarControl("CorneringProgress");
CreateButtonControl("garagebutton", this, onGarageCB);
CreateButtonControl("acceptbutton", this, onAcceptCB);
CreateButtonControl("cancelbutton", this, onCancelCB);
CreateButtonControl("GarageButton", this, onGarageCB);
CreateButtonControl("AcceptButton", this, onAcceptCB);
CreateButtonControl("CancelButton", this, onCancelCB);
CloseXMLDescriptor();

View file

@ -17,6 +17,7 @@
* *
***************************************************************************/
#include <cmath>
#include <map>
#include <sstream>
#include <algorithm>
@ -121,13 +122,8 @@ GfCars::GfCars()
GfParmReleaseHandle(hparmCat);
}
// Store it in the GfCar structure.
GfCar* pCar = new GfCar;
pCar->setId(pszCarId);
pCar->setCategoryId(strCatId);
pCar->setName(GfParmGetName(hparmCar));
pCar->setDescriptorFileName(ossCarFileName.str());
pCar->setCategoryName(strCatName);
// Store it in a new GfCar instance.
GfCar* pCar = new GfCar(pszCarId, strCatId, strCatName, hparmCar);
// Update the GfCars singleton.
_pPrivate->vecCars.push_back(pCar);
@ -247,6 +243,145 @@ void GfCars::print() const
// GfCar class ------------------------------------------------------------------
GfCar::GfCar(const std::string& strId, const std::string& strCatId,
const std::string& strCatName, void* hparmCar)
: _strId(strId), _strCatId(strCatId), _strCatName(strCatName)
{
load(hparmCar);
}
void GfCar::load(void* hparmCar)
{
static const char *pszGearName[MAX_GEARS] = {"r", "n", "1", "2", "3", "4", "5", "6", "7", "8"};
// Name.
_strName = GfParmGetName(hparmCar);
// Descriptor file name (default setup).
_strDescFile = GfParmGetFileName(hparmCar);
// Mass and front/rear repartition.
_fMass = GfParmGetNum(hparmCar, SECT_CAR, PRM_MASS, (char*)NULL, 1500);
_fFrontRearMassRatio = GfParmGetNum(hparmCar, SECT_CAR, PRM_FRWEIGHTREP, (char*)NULL, .5);
// Drive train.
const std::string strDriveTrain =
GfParmGetStr(hparmCar, SECT_DRIVETRAIN, PRM_TYPE, VAL_TRANS_RWD);
if (strDriveTrain == VAL_TRANS_RWD)
_eDriveTrain = eRWD;
else if (strDriveTrain == VAL_TRANS_FWD)
_eDriveTrain = eFWD;
else if (strDriveTrain == VAL_TRANS_4WD)
_eDriveTrain = e4WD;
// Number of gears.
std::ostringstream ossSpecPath;
for (int nGearInd = MAX_GEARS - 1; nGearInd >= 0; nGearInd--)
{
ossSpecPath.str("");
ossSpecPath << SECT_GEARBOX << '/' << ARR_GEARS << '/' << pszGearName[nGearInd];
if (GfParmGetNum(hparmCar, ossSpecPath.str().c_str(), PRM_RATIO, (char*)NULL, 0.0f))
{
_nGears = nGearInd - 1;
break;
}
}
// Turbo.
_bTurboCharged = strcmp(GfParmGetStr(hparmCar, SECT_ENGINE, PRM_TURBO, "false"), "true") == 0;
// Max power and torque + associated engine speeds.
_fMaxTorque = 0;
_fMaxTorqueSpeed = 0;
_fMaxPower = 0;
_fMaxPowerSpeed = 0;
const tdble fMaxSpeed = GfParmGetNum(hparmCar, SECT_ENGINE, PRM_REVSLIM, 0, 0);
ossSpecPath.str("");
ossSpecPath << SECT_ENGINE << '/' << ARR_DATAPTS;
const int nEngineTqCurvePts = GfParmGetEltNb(hparmCar, ossSpecPath.str().c_str());
for (int nPtInd = 2; nPtInd <= nEngineTqCurvePts; nPtInd++)
{
ossSpecPath.str("");
ossSpecPath << SECT_ENGINE << '/' << ARR_DATAPTS << '/' << nPtInd;
const tdble fSpeed = GfParmGetNum(hparmCar, ossSpecPath.str().c_str(), PRM_RPM, 0, 0);
if (fSpeed > fMaxSpeed)
break;
const tdble fTorque = GfParmGetNum(hparmCar, ossSpecPath.str().c_str(), PRM_TQ, 0, 0);
if (fTorque > _fMaxTorque)
{
_fMaxTorque = fTorque;
_fMaxTorqueSpeed = fSpeed;
}
const tdble fPower = (tdble)(fTorque * fSpeed / (75 * G));
if (fPower > _fMaxPower)
{
_fMaxPower = fPower;
_fMaxPowerSpeed = fSpeed;
}
}
// "Mechanical = Low speed" grip (~mu*g, but with front/rear mass repartition).
const tdble fMuFront =
(GfParmGetNum(hparmCar, SECT_FRNTRGTWHEEL, PRM_MU, (char*)NULL, 1.0)
+ GfParmGetNum(hparmCar, SECT_FRNTLFTWHEEL, PRM_MU, (char*)NULL, 1.0)) / 2.0f;
const tdble fMuRear =
(GfParmGetNum(hparmCar, SECT_REARRGTWHEEL, PRM_MU, (char*)NULL, 1.0)
+ GfParmGetNum(hparmCar, SECT_REARLFTWHEEL, PRM_MU, (char*)NULL, 1.0)) / 2.0f;
// Work in progress.
_fLowSpeedGrip = 0.0f;
// (_fFrontRearMassRatio * fMuFront + (1.0f - _fFrontRearMassRatio) * fMuRear) * G;
// "Aerodynamic = High speed" grip (same + with aero down-force).
// TODO: Check formula (F/R Clift repartition "guessed" from Kristof's)
const tdble fRefCarSpeed = 200 * 1000 / 3600.0f;
const tdble fFrontWingArea =
GfParmGetNum(hparmCar, SECT_FRNTWING, PRM_WINGAREA, (char*)NULL, 0.0);
const tdble fRearWingArea =
GfParmGetNum(hparmCar, SECT_REARWING, PRM_WINGAREA, (char*)NULL, 0.0);
const tdble fFrontWingAngle =
GfParmGetNum(hparmCar, SECT_FRNTWING, PRM_WINGANGLE, (char*)NULL, 0.0);
const tdble fRearWingAngle =
GfParmGetNum(hparmCar, SECT_REARWING, PRM_WINGANGLE, (char*)NULL, 0.0);
const tdble fFrontClift =
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_FCL, (char*)NULL, 0.0);
const tdble fRearClift =
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_RCL, (char*)NULL, 0.0);
const double fTotalFrontClift = 2 * fFrontClift + 4.92 * fFrontWingArea * sin(fFrontWingAngle);
const double fTotalRearClift = 2 * fRearClift + 4.92 * fRearWingArea * sin(fRearWingAngle);
_fHighSpeedGrip = _fLowSpeedGrip;
// Work in progress.
// _fHighSpeedGrip +=
// fRefCarSpeed * fRefCarSpeed
// * (tdble)(_fFrontRearMassRatio * fTotalFrontClift
// + (1.0 - _fFrontRearMassRatio) * fTotalRearClift) * G;
// Inverse of the inertia around the Z axis.
const tdble fMassRepCoef = GfParmGetNum(hparmCar, SECT_CAR, PRM_CENTR, (char*)NULL, 1.0);
const tdble fCarLength = GfParmGetNum(hparmCar, SECT_CAR, PRM_LEN, (char*)NULL, 4.7f);
const tdble fCarWidth = GfParmGetNum(hparmCar, SECT_CAR, PRM_WIDTH, (char*)NULL, 1.9f);
_fInvertedZAxisInertia = // Stolen from Simu V2.1, car.cpp, SimCarConfig()
12.0f / (_fMass * fMassRepCoef * fMassRepCoef * (fCarWidth * fCarWidth + fCarLength * fCarLength));
// Theorical top speed on a flat road, assuming the gears are tuned accordingly.
const tdble fFrontArea =
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_FRNTAREA, (char*)NULL, 2.0f);
const tdble fCx =
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_CX, (char*)NULL, 0.35f);
const tdble muRollRes = 0.015f; // Average track.
const tdble eff = 0.95f * 0.95f; // Average gear * differential efficiencies
const tdble Cd =
0.645f * fCx * fFrontArea
+ 1.23f * (fFrontWingArea * sin(fFrontWingAngle) + fRearWingArea * sin(fRearWingAngle));
const double pp =
muRollRes * _fMass * G / (Cd + muRollRes * (tdble)(fTotalFrontClift + fTotalRearClift));
const double q = eff * _fMaxPower / (Cd + muRollRes * (tdble)(fTotalFrontClift + fTotalRearClift));
// Work in progress.
_fTopSpeed = 0.0f;
// (tdble)pow(q/2+sqrt(q*q/4+(pp*pp*pp)/27), 1.0/3)
// - (tdble)pow(-q/2+sqrt(q*q/4+(pp*pp*pp)/27), 1.0/3);
}
const std::string& GfCar::getId() const
{
return _strId;
@ -272,28 +407,92 @@ const std::string& GfCar::getDescriptorFileName() const
return _strDescFile;
}
void GfCar::setId(const std::string& strId)
GfCar::EDriveTrain GfCar::getDriveTrain() const
{
_strId = strId;
return _eDriveTrain;
}
void GfCar::setName(const std::string& strName)
unsigned GfCar::getGearsCount() const
{
_strName = strName;
return _nGears;
}
void GfCar::setCategoryId(const std::string& strCatId)
bool GfCar::isTurboCharged() const
{
_strCatId = strCatId;
return _bTurboCharged;
}
void GfCar::setCategoryName(const std::string& strCatName)
tdble GfCar::getMaxPower() const
{
_strCatName = strCatName;
return _fMaxPower;
}
void GfCar::setDescriptorFileName(const std::string& strDescFile)
tdble GfCar::getMaxPowerSpeed() const
{
_strDescFile = strDescFile;
return _fMaxPowerSpeed;
}
tdble GfCar::getMaxTorque() const
{
return _fMaxTorque;
}
tdble GfCar::getMaxTorqueSpeed() const
{
return _fMaxTorqueSpeed;
}
tdble GfCar::getMass() const
{
return _fMass;
}
tdble GfCar::getFrontRearMassRatio() const
{
return _fFrontRearMassRatio;
}
tdble GfCar::getTopSpeed() const
{
return _fTopSpeed;
}
tdble GfCar::getLowSpeedGrip() const
{
return _fLowSpeedGrip;
}
tdble GfCar::getHighSpeedGrip() const
{
return _fHighSpeedGrip;
}
tdble GfCar::getInvertedZAxisInertia() const
{
return _fInvertedZAxisInertia;
}
// void GfCar::setId(const std::string& strId)
// {
// _strId = strId;
// }
// void GfCar::setName(const std::string& strName)
// {
// _strName = strName;
// }
// void GfCar::setCategoryId(const std::string& strCatId)
// {
// _strCatId = strCatId;
// }
// void GfCar::setCategoryName(const std::string& strCatName)
// {
// _strCatName = strCatName;
// }
// void GfCar::setDescriptorFileName(const std::string& strDescFile)
// {
// _strDescFile = strDescFile;
// }

View file

@ -23,6 +23,8 @@
#include <string>
#include <vector>
#include <tgf.h>
#include "tgfdata.h"
@ -35,17 +37,38 @@ class TGFDATA_API GfCar
{
public:
GfCar(const std::string& strId, const std::string& strCatId,
const std::string& strCatName, void* hparmCar);
const std::string& getId() const;
const std::string& getName() const;
const std::string& getCategoryId() const;
const std::string& getCategoryName() const;
const std::string& getDescriptorFileName() const;
enum EDriveTrain { eRWD, eFWD, e4WD };
EDriveTrain getDriveTrain() const;
unsigned getGearsCount() const;
bool isTurboCharged() const;
tdble getMaxPower() const;
tdble getMaxPowerSpeed() const;
tdble getMaxTorque() const;
tdble getMaxTorqueSpeed() const;
tdble getMass() const;
tdble getFrontRearMassRatio() const;
tdble getTopSpeed() const;
tdble getLowSpeedGrip() const;
tdble getHighSpeedGrip() const;
tdble getInvertedZAxisInertia() const;
void setId(const std::string& strId);
void setName(const std::string& strName);
void setCategoryId(const std::string& strCatId);
void setCategoryName(const std::string& strCatName);
void setDescriptorFileName(const std::string& strDescFile);
// void setId(const std::string& strId);
// void setName(const std::string& strName);
// void setCategoryId(const std::string& strCatId);
// void setCategoryName(const std::string& strCatName);
// void setDescriptorFileName(const std::string& strDescFile);
void load(void* hparmCar);
protected:
@ -54,6 +77,19 @@ protected:
std::string _strCatId; // Category XML file / folder name (ex: "LS-GT1").
std::string _strCatName; // User friendly category name (ex: "Long day Series GT1").
std::string _strDescFile; // Path-name of the XML descriptor file.
EDriveTrain _eDriveTrain;
unsigned _nGears; // Number of gears.
bool _bTurboCharged;
tdble _fMaxPower, _fMaxPowerSpeed; // Engine max power (bhp) and associated engine speed.
tdble _fMaxTorque, _fMaxTorqueSpeed; // Engine max torque (Nm) and associated engine speed.
tdble _fMass; // Total mass (kg).
tdble _fFrontRearMassRatio; // Front to rear mass ratio (no unit, inside ]0,1[).
tdble _fTopSpeed; // Theorical top speed (m/s)
tdble _fLowSpeedGrip; // Mechanical grip (~mu*g, but with front/rear mass repartition)
tdble _fHighSpeedGrip; // Aerodynamic grip (same + with aero down-force)
tdble _fInvertedZAxisInertia;
};
class TGFDATA_API GfCars