diff --git a/src/libs/racescreens/carselect.cpp b/src/libs/racescreens/carselect.cpp index 6525bf8dc..d0623c4bc 100644 --- a/src/libs/racescreens/carselect.cpp +++ b/src/libs/racescreens/carselect.cpp @@ -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(); diff --git a/src/libs/tgfdata/cars.cpp b/src/libs/tgfdata/cars.cpp index a10eec26d..07c682c87 100644 --- a/src/libs/tgfdata/cars.cpp +++ b/src/libs/tgfdata/cars.cpp @@ -17,6 +17,7 @@ * * ***************************************************************************/ +#include #include #include #include @@ -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; +// } diff --git a/src/libs/tgfdata/cars.h b/src/libs/tgfdata/cars.h index 34ada0116..115ad008d 100644 --- a/src/libs/tgfdata/cars.h +++ b/src/libs/tgfdata/cars.h @@ -23,6 +23,8 @@ #include #include +#include + #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