#include "StdAfx.h" #include "vtPhysXAll.h" #include "pEngine.h" #include "pVehicleAll.h" #include // Local trace? //#define LTRACE #define DEF_SIZE .25 #define DEF_MAXRPM 5000 #define DEF_MAXPOWER 100 #define DEF_FRICTION 0 #define DEF_MAXTORQUE 340 // ~ F1 Jos Verstappen #define DEF_TORQUE 100 // In case no curve is present //#define LTRACE // If USE_HARD_REVLIMIT is used, any rpm above the max. RPM // returns a 0 engine torque. Although this works, it gives quite // hard rev limits (esp. when in 1st gear). Better is to supply // a curve which moves down to 0 torque when rpm gets above maxRPM, // so a more natural (smooth) balance is obtained (and turn // this define off) //#define USE_HARD_REVLIMIT pEngine::pEngine(pVehicle *_car) : pDriveLineComp() { SetName("engine"); ownerVehicle=_car; driveLine=ownerVehicle->getDriveLine(); initData(); } pEngine::~pEngine() { } void pEngine::initData() // initData all member variables { flags=0; size=DEF_SIZE; mass=0; torqueReaction=0; maxRPM=DEF_MAXRPM; idleRPM=900; stallRPM=350; startRPM=900; autoClutchRPM=idleRPM*1.5f; //autoClutch=0; starterTorque=300; idleThrottle=0.1f; throttleRange=1000.0-idleThrottle*1000.0; friction=0; brakingCoeff=0; position.Set(0,0,0); maxTorque=0; throttle=0; brakes=0; size = 0.25f; //QDELETE(crvTorque); clean(); } void pEngine::clean() // Reset engine before usage (after Shift-R for example) { torqueReaction=0; //torqueCurve.clear(); pDriveLineComp::Reset(); // No automatic clutch engaged //flags&=~AUTOCLUTCH_ACTIVE; //flags|=AUTOCLUTCH_ACTIVE; // Start engine (or not) and make it stable; AFTER RDLC:clean(), since that // sets the rotational velocity to 0. if(flags&START_STALLED) { // Start with the engine off EnableStall(); setRPM(0); } else { // Pre-start engine DisableStall(); setRPM(idleRPM); } } void pEngine::setRPM(float rpm) { // Convert to radians and seconds rotV=rpm*2*PI/60.0f; } /* float pEngine::GetGearRatio(int n) { pLinearInterpolation &ratios = ((pGearBox*)(driveLine->gearbox))->getGearRatios(); if ( n >=0 && n 1)throttle=1; else if(throttle<0)throttle=0; //qdbg("pEngine:updateUserControl(); ctlT=%d, throttle=%f\n",ctlThrottle,throttle); //qdbg(" idleThrottle=%f, idleRPM=%f\n",idleThrottle,idleRPM); //qdbg("pEngine:updateUserControl(ctlClutch=%d)\n",ctlClutch); //#define ND_AUTO_CLUTCH //#ifdef ND_AUTO_CLUTCH // Only update clutch value when the ownerVehicle is not auto-shifting #ifdef ND_AUTO_CLUTCH float ctlClutch = driveLine->GetClutchApplication(); bool autoShiftStart =true; if(!autoShiftStart) { clutch=((float)ctlClutch)/1000.0f; if(clutch>1)clutch=1; else if(clutch<0)clutch=0; } //#else // Pass clutch on to driveline //driveLine->SetClutchApplication(((float)ctlClutch)/1000.0f); // Directly set clutch //clutch=((float)ctlClutch)/1000.0f; //if(clutch>1)clutch=1; //else if(clutch<0)clutch=0; #endif } float pEngine::GetMaxTorque(float rpm) // Returns the maximum torque generated at 'rpm' { if ( getVehicle()->getProcessOptions() & pVPO_Engine_UseHardRevlimit) { // Clip hard at max rpm (return 0 torque if rpm gets too high) if(rpm>maxRPM) return 0; } if(!torqueCurve.getSize()) { // No curve available, use a less realistic constant torque return DEF_TORQUE; } else { float t; // Find normalized torque in RPM-to-torque curve t=(float)torqueCurve.getValue(rpm); t = 1 / maxTorque *t; //qdbg("pEngine: rpm=%.2f => T=%.2f, (curve)maxTorque=%.2f\n",rpm,t,maxTorque); return maxTorque*t; } } // Returns the minimum torque generated at 'rpm' (engine braking). float pEngine::GetMinTorque(float rpm) { // If 0% throttle, this is the minimal torque which is generated // by the engine return -brakingCoeff*rpm/60.0f; } void pEngine::CalcForces() { float minTorque,maxTorque; float rpm=getRPM(); static int starterDelay; // Temp crap to avoid multiple starter smps if(starterDelay>0) starterDelay--; // Check starter; this section assumes CalcForces() is called // only once per simulation step. Although this may not be // the case in the future, it should not harm that much. if(IsStalled()) { // There's only some engine braking // Note this skips calculating min/maxTorque tEngine=GetMinTorque(rpm); //qdbg("Stalled; check starter=%d\n",RMGR->controls->control[RControls::T_STARTER]->value); bool starter = true; if(starter) { // Use the starter tEngine+=starterTorque; // Raise the starter sample volume if(starterDelay==0) { starterDelay=1000/ownerVehicle->_lastDT; } //#ifdef LTRACE // qdbg(" starting; T_starter=%f, tEngine=%f\n",starterTorque,tEngine); //#endif } //#ifdef LTRACE // qdbg("Stalled engine torque: %.3f\n",tEngine); //#endif } else { // Engine is running // Calculate minimal torque (if 0% throttle) minTorque=GetMinTorque(rpm); // Calculate maximum torque (100% throttle situation) maxTorque=GetMaxTorque(rpm); // The throttle accounts for how much of the torque is actually // produced. // NOTE: The output power then is 2*PI*rps*outputTorque // (rps=rpm/60; rotations per second). Nothing but a statistic now. tEngine=(maxTorque-minTorque)*throttle+minTorque; #ifdef LTRACE qdbg("minTorque=%f, maxTorque=%.f, throttle=%f => torque=%f\n",minTorque,maxTorque,throttle,tEngine); #endif } } void pEngine::CalcAccelerations() { if(!driveLine->IsPrePostLocked()) { // Engine moves separately from the rest of the driveLine rotA=(tEngine-driveLine->GetClutchTorque())/GetInertia(); } // else rotA is calculated in the driveline (passed up // from the wheels on to the root - the engine) } void pEngine::Integrate() // Step the engine. // Also looks at the stall state (you can kickstart the engine when driving). { float rpm=getRPM(); // This is normally done by RDriveLineComp, but avoid the function call float timeStep= lastStepTimeSec; rotV+=rotA*timeStep; // Deduce state of engine (stalling) /*if(IsStalled()) { // Turning back on? if(rpm>=startRPM) { DisableStall(); } } else { // Stalling? if(rpmgetDriveLine()->EnableAutoClutch(); //flags|=AUTOCLUTCH_ACTIVE; if(autoClutch<0)autoClutch=0; else if(autoClutch>1)autoClutch=1; ownerVehicle->getDriveLine()->SetClutchApplication(autoClutch); } else { // Turn off auto-clutch ownerVehicle->getDriveLine()->DisableAutoClutch(); } } //qdbg(" post rotV=%.2f\n",rotV); } void pEngine::setToDefault() { torqueCurve.clear(); torqueCurve.insert(500.f, 193.f); torqueCurve.insert(2000.f, 134.f); torqueCurve.insert(4000.f, 175.f); torqueCurve.insert(5000.f, 175.f); torqueCurve.insert(6000.f, 166.f); torqueCurve.insert(7000.f, 166.f); /* torqueCurve.insert(466.0f, 0.46f); torqueCurve.insert(900.0f, 0.75f); torqueCurve.insert(1466.0f, 0.89f); torqueCurve.insert(2600.0f, 0.985f); torqueCurve.insert(3500.0f, 1.0f); torqueCurve.insert(4466.0f, 0.987f); torqueCurve.insert(5133.333f,0.97f); torqueCurve.insert(6933.0f, 0.631f); torqueCurve.insert(7500.0f, 0.0f); */ char buf[128],fname[128]; position.Set(0,0,0); size = 0.0f; maxRPM = 6200; idleRPM = 700; mass = 180.0; torqueReaction=1.0f; maxTorque=280.0f; forceFeedbackScale = 100.0f; SetInertia(0.3f); timeScale = 1.0f; rotationalEndFactor = 1.0f; friction=0.01f; brakingCoeff=0.1f; starterTorque=25; stallRPM=100.0f; startRPM=900; //autoClutchRPM=1250.0f; autoClutchRPM=1000.0f; //flags|=AUTOMATIC; flags|=AUTOCLUTCH_ACTIVE; //flags|=HAS_STARTER; preStep(); } void pEngine::setTorqueCurve(pLinearInterpolation val) { torqueCurve = val; preStep(); } void pEngine::setBrakingCoeff(float val) { brakingCoeff = val; preStep(); } void pEngine::setFriction(float val) { friction = val; preStep(); } void pEngine::setStartRPM(float val) { startRPM = val; preStep(); } void pEngine::setIdleRPM(float val) { idleRPM = val; preStep(); } void pEngine::setStarterTorque(float val) { starterTorque = val; preStep(); } float pEngine::getEndTorqueForWheel(CK3dEntity *wheelReference) { return 1; } /*void pEngine::SetInertia(float i) { inertia = i; preStep(); }*/ void pEngine::setFlags(int val) { } /* bool pEngine::Load(QInfo *info,cstring path) // 'path' may be 0, in which case the default "engine" is used { char buf[128],fname[128]; QInfo *infoCurve; if(!path)path="engine"; // Location sprintf(buf,"%s.x",path); position.x=info->GetFloat(buf); sprintf(buf,"%s.y",path); position.y=info->GetFloat(buf); sprintf(buf,"%s.z",path); position.z=info->GetFloat(buf); sprintf(buf,"%s.size",path); size=info->GetFloat(buf,DEF_SIZE); // Physical attribs #ifdef OBS sprintf(buf,"%s.rolling_friction_coeff",path); rollingFrictionCoeff=info->GetFloat(buf); #endif sprintf(buf,"%s.mass",path); mass=info->GetFloat(buf); // Power/torque sprintf(buf,"%s.max_rpm",path); maxRPM=info->GetFloat(buf,DEF_MAXRPM); sprintf(buf,"%s.idle_rpm",path); idleRPM=info->GetFloat(buf,1000); // Torque reaction sprintf(buf,"%s.torque_reaction",path); torqueReaction=info->GetFloat(buf); // Torque curve or constant (curve is preferred) //sprintf(buf,"%s.constant_torque",path); sprintf(buf,"%s.max_torque",path); maxTorque=info->GetFloat(buf,DEF_MAXTORQUE); crvTorque=new QCurve(); sprintf(buf,"%s.curve_torque",path); info->GetString(buf,fname); //qdbg("fname_torque='%s', maxTorque=%.2f\n",fname,maxTorque); if(fname[0]) { infoCurve=new QInfo(RFindFile(fname,ownerVehicle->GetDir())); crvTorque->Load(infoCurve,"curve"); QDELETE(infoCurve); } else { // No torque curve //QDELETE(crvTorque); } // Check torque curve in that it makes sense and is usable if(!crvTorque) { //qwarn("No torque curve (torque.crv?) present; you really should have one"); } else { // Make sure it ends at 0 (assuming no engine will rev above 100,000 rpm) if(fabs(crvTorque->GetValue(100000))>D3_EPSILON) { //qwarn("The torque curve needs to end at 0 torque (is now %.2f)", crvTorque->GetValue(100000)); } } sprintf(buf,"%s.inertia.engine",path); SetInertia(info->GetFloat(buf)); //inertiaEngine=info->GetFloat(buf); #ifdef OBS sprintf(buf,"%s.inertia.final_drive",path); inertiaDriveShaft=info->GetFloat(buf); #endif sprintf(buf,"%s.friction",path); friction=info->GetFloat(buf,DEF_FRICTION); sprintf(buf,"%s.braking_coeff",path); brakingCoeff=info->GetFloat(buf); // Starter engine sprintf(buf,"%s.starter",path); if(info->GetInt(buf,1)) flags|=HAS_STARTER; sprintf(buf,"%s.start_stalled",path); if(info->GetInt(buf,1)) flags|=START_STALLED; sprintf(buf,"%s.starter_torque",path); starterTorque=info->GetFloat(buf); sprintf(buf,"%s.stall_rpm",path); stallRPM=info->GetFloat(buf); sprintf(buf,"%s.start_rpm",path); startRPM=info->GetFloat(buf); sprintf(buf,"%s.autoclutch_rpm",path); autoClutchRPM=info->GetFloat(buf); #ifdef OBS // Shifting sprintf(buf,"%s.shifting.automatic",path); if(info->GetInt(buf)) flags|=AUTOMATIC; //qdbg("Autoshift: flags=%d, buf='%s'\n",flags,buf); sprintf(buf,"%s.shifting.shift_up_rpm",path); shiftUpRPM=info->GetFloat(buf,3500); sprintf(buf,"%s.shifting.shift_down_rpm",path); shiftDownRPM=info->GetFloat(buf,2000); sprintf(buf,"%s.shifting.time_to_declutch",path); timeToDeclutch=info->GetInt(buf,500); sprintf(buf,"%s.shifting.time_to_clutch",path); timeToClutch=info->GetInt(buf,500); // Gearbox path="gearbox"; // ! sprintf(buf,"%s.gears",path); gears=info->GetInt(buf,4); for(i=0;iGetFloat(buf,1.0); sprintf(buf,"%s.gear%d.inertia",path,i); gearInertia[i]=info->GetFloat(buf); } sprintf(buf,"%s.end_ratio",path); endRatio=info->GetFloat(buf,3.0); #endif // Calculate static facts preStep(); return TRUE; } */ /* void pEngine::OnGfxFrame() { } void pEngine::DbgPrint(cstring s) { qdbg("pEngine state (%s):\n",s); qdbg(" rpm=%.2f, stalled: %d, idleThrottle: %.2f, Treaction=%.2f\n", getRPM(),IsStalled(),idleThrottle,torqueReaction); } bool pEngine::LoadState(QFile *f) { RDriveLineComp::LoadState(f); return TRUE; } bool pEngine::SaveState(QFile *f) { RDriveLineComp::LoadState(f); return TRUE; } */