//carphysics.cpp //simulation of all forces and tourques moving the cars around #include #include #include "vectors.h" #include "entities.h" #include "carphysics.h" #include "gamemem.h" #include "gameframe.h" #include "roads.h" #include "text.h" #include "particles.h" #include "tracks.h" #include "collision.h" #include "config.h" #include "environment.h" #include "gamesound.h" #include "renderframe.h" #include "networkphysics.h" #define kMaxSmoke 120.0 #define kIndicatorTime 0.5 #define kMaxTwistAngle (PI/12) #define kSpinRotations 10.0 #define kTrackPrecision 5 //each nTH frame a skid mark is generated void InstallCarAddOns(tCarPhysics *phys) { tCarDefinition *car=&(phys->car); for(int i=0;inumAddOns;i++) //is this add-on installed? if((phys->addOns>>i)&1) if(!(phys->addOns&car->addOns[i].conflicts)) { car->mass+=car->addOns[i].mass; car->massCenter=car->massCenter+car->addOns[i].massCenter; car->rearLift+=car->addOns[i].rearLift; car->frontLift+=car->addOns[i].frontLift; car->frontAirResistance+=car->addOns[i].frontAirResistance; if(car->addOns[i].powerPercent>0) { car->power*=car->addOns[i].powerPercent; car->powerRPM*=car->addOns[i].powerPercent; } if(car->addOns[i].frontSwayBar>0) car->frontSwayBar=car->addOns[i].frontSwayBar; if(car->addOns[i].exhaustFire>0) car->exhaustFire=car->addOns[i].exhaustFire; if(car->addOns[i].rearSwayBar>0) car->rearSwayBar=car->addOns[i].rearSwayBar; if(car->addOns[i].damperStrength>0) car->damperStrength=car->addOns[i].damperStrength; if(car->addOns[i].engineInertia>0) car->engineInertia=car->addOns[i].engineInertia; if(car->addOns[i].topGearRatio>0) car->gearRatios[car->numGears-1]=car->addOns[i].topGearRatio; if(car->addOns[i].maxRPM>0) car->maxRPM=car->addOns[i].maxRPM; if(car->addOns[i].track>0) { car->wheels[0].pos.x-=car->addOns[i].track; car->wheels[1].pos.x+=car->addOns[i].track; car->wheels[2].pos.x-=car->addOns[i].track; car->wheels[3].pos.x+=car->addOns[i].track; } if(car->addOns[i].frontWheelWidth>0) { car->wheels[0].width=car->addOns[i].frontWheelWidth; car->wheels[1].width=car->addOns[i].frontWheelWidth; } if(car->addOns[i].rearWheelWidth>0) { car->wheels[2].width=car->addOns[i].rearWheelWidth; car->wheels[3].width=car->addOns[i].rearWheelWidth; } if(car->addOns[i].frontMaxSuspension>0) { car->wheels[0].maxSuspension=car->addOns[i].frontMaxSuspension; car->wheels[1].maxSuspension=car->addOns[i].frontMaxSuspension; } if(car->addOns[i].rearMaxSuspension>0) { car->wheels[2].maxSuspension=car->addOns[i].rearMaxSuspension; car->wheels[3].maxSuspension=car->addOns[i].rearMaxSuspension; } if(car->addOns[i].torquePercent>0) car->torque*=car->addOns[i].torquePercent; if(car->addOns[i].differentialLockCoefficient>0) car->differentialLockCoefficient=car->addOns[i].differentialLockCoefficient; if(car->addOns[i].finalDriveRatio>0) car->finalDriveRatio=car->addOns[i].finalDriveRatio; if(car->addOns[i].gearSwitchTime>0) car->gearSwitchTime=car->addOns[i].gearSwitchTime; } } float WheelGroundDistance(tGameEntity *carEntity,tVector3 wheelPos,tVector3 yDir,tWheelDefinition *wheel,tVector3 *groundNormal,float oldHeight,int *surfaceType,float *bump) //returns the distance between the wheel at wheelPos and the ground. //gives the normal of the ground under the wheel in groundNormal //and the surface type of the ground in surfaceType { tVector3 wheelGroundPoint=wheelPos-yDir*(wheel->radius+oldHeight); return GetGroundOffsetAndBump(wheelGroundPoint,&carEntity->lastRoadIndex,groundNormal,surfaceType,bump)+oldHeight; } void CalcWheelPositions(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) //Calculates the positions and directions/angles of all wheels of the car passed in carEntity. { for(int i=0;inumWheels;i++) { wheels[i].pos=car->wheels[i].pos*carEntity->dir; wheels[i].velo=((wheels[i].pos*carEntity->rVelo)-wheels[i].pos)*kFPS+carEntity->velo; wheels[i].oldAngle=phys->wheels[i].angle; if(phys->wheels[i].rotation>=2*PI) phys->wheels[i].rotation-=2*PI; else if(phys->wheels[i].rotation<0) phys->wheels[i].rotation+=2*PI; phys->wheels[i].angle=car->wheels[i].maxAngle*phys->steering;//*(0.5+0.5*sqr(phys->steering)); } } void CalcWheelGroundOffsets(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) //Calculates the spring supression for all wheels of the car passed in carEntity. { for(int i=0;inumWheels;i++) { float dist=WheelGroundDistance(carEntity,wheels[i].pos+carEntity->pos,*MatrixGetYVector(carEntity->dir),&car->wheels[i],&wheels[i].groundNormal,phys->wheels[i].suspension,&phys->wheels[i].surfaceType,&(wheels[i].bump)); if(wheels[i].onGround=distwheels[i].maxSuspension) { phys->wheels[i].suspension=dist; if(phys->wheels[i].suspension<0) phys->wheels[i].suspension=0; } else phys->wheels[i].suspension=car->wheels[i].maxSuspension; } } void CalcSwayBars(tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) //Calculates the Forces generated by the cars Anti-Sway-Bars. { float force; int arcade=(gGameInfo->arcade==kGameModeArcade||gGameInfo->arcade==kGameModeTurbo); force=(phys->wheels[1].suspension-phys->wheels[0].suspension)*(car->frontSwayBar+(arcade?car->mass*10:0)); wheels[0].suspensionForce+=force; wheels[1].suspensionForce-=force; force=(phys->wheels[3].suspension-phys->wheels[2].suspension)*(car->rearSwayBar+(arcade?car->mass*10:0)); wheels[2].suspensionForce+=force; wheels[3].suspensionForce-=force; } void CalcWheelSupension(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) //Calculates spring and damper forces { //Calculate the distance of each wheel from car's center of gravity float totalInvMassDistance=0; float invMassDistance[kMaxWheels]; for(int i=0;inumWheels;i++) { invMassDistance[i]=1/~(car->massCenter-car->wheels[i].pos); totalInvMassDistance+=invMassDistance[i]; } //factor used to calculate the spring forces float totalSpringForceFactor=car->mass*2*gEnvironment->gravity; tVector3 zDir=*MatrixGetZVector(carEntity->dir); float airRes=~carEntity->velo*0.5*fabs(zDir*carEntity->velo)*gEnvironment->airDensity; for(int i=0;inumWheels;i++) { //from the distance to CG, determine the fraction of the cars mass this wheel has to carry. //not entierly correct, but should suffice for most cases float massFraction=invMassDistance[i]/totalInvMassDistance; float calcSuspension=(phys->wheels[i].suspension/car->wheels[i].maxSuspension)*2-1; calcSuspension=calcSuspension*calcSuspension*calcSuspension; calcSuspension=(calcSuspension*0.5+0.5)*car->wheels[i].maxSuspension; calcSuspension=phys->wheels[i].suspension; //calculate spring force, based on totalSpringForceFactor, massFraction, and the ratio of spring compression //wheels[i].suspensionForce=(car->wheels[i].maxSuspension-phys->wheels[i].suspension)*totalSpringForceFactor*massFraction/car->wheels[i].maxSuspension; wheels[i].suspensionForce=(car->wheels[i].maxSuspension-calcSuspension)*totalSpringForceFactor*massFraction/car->wheels[i].maxSuspension; //if(gGameInfo->arcade&&gFrameCount*kFrameTime<=1)wheels[i].suspensionForce*=gFrameCount*kFrameTime; //if the springs are fully compressed, we need to add another force, applied by the ground, //which keeps the wheel from falling into the ground if(!phys->wheels[i].suspension) { float aerodynamicForce=airRes*(i<2?car->frontLift:car->rearLift); float fGroundForce=-wheels[i].velo*wheels[i].groundNormal*kFPS*car->mass/car->numWheels-aerodynamicForce/2; if(fGroundForce>car->mass/car->numWheels*gEnvironment->gravity-aerodynamicForce/2) fGroundForce=car->mass/car->numWheels*gEnvironment->gravity-aerodynamicForce/2; if(fGroundForce>0) wheels[i].suspensionForce+=fGroundForce; } //the spring and ground force make up the normal force to the wheel, used to calculate tire traction wheels[i].normalForce=wheels[i].suspensionForce; //and now, calculate damper forces if(wheels[i].onGround){ //wheels speed WRT the ground float wheelYVelo=wheels[i].velo*wheels[i].groundNormal; //friction caused by the suspension (constant, not dependant on the speed of wheel Y movement) float suspensionResistance=sign(wheelYVelo)*car->supsensionFriction*massFraction; //force caused by dampers (a linear function of wheel Y velocity) //dampers stronger in first second to avoid arcade mode shake bug. suspensionResistance+=wheelYVelo*car->damperStrength*massFraction*((gGameInfo->arcade&&gFrameCount*kFrameTime<=1)?5:1); //do not apply forces which are higher than necessary to damp all motion if(fabs(suspensionResistance)>fabs(wheelYVelo*kFPS*car->mass/car->numWheels)) suspensionResistance=wheelYVelo*kFPS*car->mass/car->numWheels; //to prevent unexpected behavior when loosing ground contact (ie. jumping with the car) if(suspensionResistance>2*car->mass) suspensionResistance=2*car->mass; //apply damper resistance wheels[i].suspensionForce-=suspensionResistance; } } CalcSwayBars(car,phys,wheels); int wall=true; for(int i=0;inumWheels;i++) if(wheels[i].groundNormal.y!=0) wall=false; if(wall) if(wheels[1].pos.y>wheels[0].pos.y) { wheels[1].suspensionForce+=100000; wheels[3].suspensionForce+=100000; } else { wheels[0].suspensionForce+=100000; wheels[2].suspensionForce+=100000; } } tVector3 PacejkaCalcWheelRoadForces(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,int i,float engineTorque,float frictionTorque); float CalcTorque(float rpm,tCarDefinition *car) //Calculate the maximal torque the car can currently produce, based on engine revs. //this is a function based on the car's power and torque ratings, and does not //necessaryly correspond to the actual car's torque curve, but it is usually close enough. //the advantage of this is that we do not need to know a car's actual torque chart to simulate a car. { float torqueResult; if(rpmtorqueRPM) torqueResult=car->torque*(-sqr(rpm/car->torqueRPM-1)+1); else { float maxPowerTorque=car->power/(car->powerRPM*2*PI/60); float aproxFactor=(car->torque-maxPowerTorque)/(2*car->torqueRPM*car->powerRPM-sqr(car->powerRPM)-sqr(car->torqueRPM)); float torque=aproxFactor*sqr(rpm-car->torqueRPM)+car->torque; torqueResult=torque>0?torque:0; } if(rpm>car->maxRPM) { torqueResult*=1-((rpm-car->maxRPM)*0.006); if(torqueResult<0) torqueResult=0; } if(rpm<0) torqueResult=0; return torqueResult; } #include "random.h" void CalcWheelRoadForceNoClutch(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) //Calculater the force the wheels apply onto road when the clutch is fully engaged { //Calculate the drivetrain ratio float ratio=car->gearRatios[phys->gear+1]*car->finalDriveRatio; if(gGameInfo->arcade==kGameModeTurbo) ratio*=0.8; //Calculate the engine inertia at the final drive float inertia=car->engineInertia*sqr(ratio); //Calculate the frictional braking torque of the engine (in N*m) float engineFrictionTorque=25+phys->rpm*0.02; if(car->engineBaseFriction||car->engineRPMFriction) engineFrictionTorque=car->engineBaseFriction+car->idleRPM*car->engineRPMFriction; //Calculate the current engine torque (in N*m) float engineTorque=(CalcTorque(phys->rpm,car)+engineFrictionTorque)*phys->throttle; if(phys->damage>kFatalDamage) engineTorque=0; if(phys->damage>kEngineDamage) engineTorque=RandomProb(0.6)?engineTorque:0; //Calculate average radius of powered wheels float wheelRadius=0; for(int i=0;inumWheels;i++) wheelRadius+=car->wheels[i].radius*car->wheels[i].powered; //Calculate average angular velocity of powered wheels float averageAngularVelo=0; for(int i=0;inumWheels;i++) averageAngularVelo+=car->wheels[i].powered*phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius); float rpm=0; for(int i=0;inumWheels;i++) { //Calculate the inertia at the wheel wheels[i].inertia=car->wheels[i].inertia+car->wheels[i].powered*inertia; //Calculate the amount of braking applied to the wheel float braked=phys->brake+phys->handbrake*car->wheels[i].handbraked; if(braked>1)braked=1; float glow=braked*fabs(phys->wheels[i].angularVelo)*0.02; if(glow>1)glow=1; phys->wheels[i].glow+=(glow-0.2)*0.3*kFrameTime; if(phys->wheels[i].glow>1)phys->wheels[i].glow=1; else if(phys->wheels[i].glow<0)phys->wheels[i].glow=0; //Calculate the torque applied by the brakes float brakeFriction=braked*car->wheels[i].braked+fabs(phys->wheels[i].angularVelo)*wheels[i].normalForce*gSurfaceTypes->types[phys->wheels[i].surfaceType].brakeFactor; //Calculate the torque applied by differential lock float lockingTorque=(averageAngularVelo-phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius))*car->wheels[i].powered*car->differentialLockCoefficient; //Calculate the total braking torque (brakes, friction) applied on the wheel float wheelFrictionTorque=engineFrictionTorque*car->wheels[i].powered*fabs(ratio)+car->wheels[i].friction+brakeFriction; //Calculate the engine torque applied on the wheels float wheelEngineTorque=engineTorque*car->wheels[i].powered*ratio+lockingTorque; if(wheels[i].onGround) //Calculate the force the wheel applies onto the road wheels[i].roadForce=PacejkaCalcWheelRoadForces(carEntity,car,phys,wheels,i,wheelEngineTorque,wheelFrictionTorque); else{ //wheel applies no force onto the road wheels[i].roadForce=Vector(0,0,0); phys->wheels[i].slipVelo=0; //Calculate wheel's new angular velocity float angularAcceleration=((wheelEngineTorque-wheelFrictionTorque*sign(phys->wheels[i].angularVelo))/wheels[i].inertia); if(-angularAcceleration*kFrameTime>phys->wheels[i].angularVelo) phys->wheels[i].angularVelo=0; else phys->wheels[i].angularVelo+=angularAcceleration*kFrameTime; } //rotate the wheel according to it's angular velocity phys->wheels[i].rotation+=phys->wheels[i].angularVelo*kFrameTime; //Calulate average wheels angular velocities. rpm+=phys->wheels[i].angularVelo*car->wheels[i].powered; } //Calculate new engine RPM phys->engineAngularVelo=rpm*ratio; phys->rpm=phys->engineAngularVelo*60.0/(2*PI); } //Calculates how much throttle has to be given to maintain the car's idle RPM float CalcIdleThrottle(tCarDefinition *car) { float engineFrictionTorque=25+car->idleRPM*0.02; if(car->engineBaseFriction||car->engineRPMFriction) engineFrictionTorque=car->engineBaseFriction+car->idleRPM*car->engineRPMFriction; float engineTorque=CalcTorque(car->idleRPM,car)+engineFrictionTorque; return engineFrictionTorque/engineTorque; } float CalcEngine(tCarDefinition *car,tCarPhysics *phys) { if(phys->idleThrottle==0) phys->idleThrottle=CalcIdleThrottle(car); float rawEngineTorque=CalcTorque(phys->rpm,car); float engineFrictionTorque=25+phys->rpm*0.02; if(car->engineBaseFriction||car->engineRPMFriction) engineFrictionTorque=car->engineBaseFriction+phys->rpm*car->engineRPMFriction; float engineTorque=(rawEngineTorque+fabs(engineFrictionTorque))*phys->throttle; if(phys->rpm>5*car->maxRPM)//safety against engine physics getting out of hand. { phys->rpm=0; phys->gear=0; } if(phys->damage>kFatalDamage) engineTorque=0; if(phys->damage>kEngineDamage) engineTorque=RandomProb(0.6)?engineTorque:0; float engineAngularAcceleration=(engineTorque-engineFrictionTorque)/car->engineInertia; phys->rpm=phys->engineAngularVelo*(60.0/(2*PI)); if(phys->rpmidleRPM) { phys->rpm=car->idleRPM; phys->clutch=0; } phys->engineAngularVelo+=engineAngularAcceleration*kFrameTime; return engineAngularAcceleration; } float CalcWheelTorques(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,float ratio) { float oldDrivetrainAngularVelo=phys->drivetrainAngularVelo; phys->drivetrainAngularVelo=0; //Calculate average radius of powered wheels float wheelRadius=0; for(int i=0;inumWheels;i++) wheelRadius+=car->wheels[i].radius*car->wheels[i].powered; //Calculate average angular velocity of powered wheels float averageAngularVelo=0; for(int i=0;inumWheels;i++) averageAngularVelo+=car->wheels[i].powered*phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius); for(int i=0;inumWheels;i++) { wheels[i].inertia=car->wheels[i].inertia; //Calculate the amount of braking applied to the wheel float braked=phys->brake+phys->handbrake*car->wheels[i].handbraked; if(braked>1)braked=1; if(gFrameCount*kFrameTimewheels[i].angularVelo)*0.02; if(glow>1)glow=1; phys->wheels[i].glow+=(glow-0.2)*0.3*kFrameTime; if(phys->wheels[i].glow>1)phys->wheels[i].glow=1; else if(phys->wheels[i].glow<0)phys->wheels[i].glow=0; //Calculate the torque applied by the brakes float brakeFriction=braked*car->wheels[i].braked+fabs(phys->wheels[i].angularVelo)*wheels[i].normalForce*gSurfaceTypes->types[phys->wheels[i].surfaceType].brakeFactor; //Calculate the torque applied by differential lock float lockingTorque=(averageAngularVelo-phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius))*car->wheels[i].powered*car->differentialLockCoefficient; //Calculate the total braking torque (brakes, friction) applied on the wheel float wheelFrictionTorque=car->wheels[i].friction+brakeFriction; float wheelEngineTorque=phys->clutchTorque*car->wheels[i].powered*ratio+lockingTorque; if(wheels[i].onGround) wheels[i].roadForce=PacejkaCalcWheelRoadForces(carEntity,car,phys,wheels,i,wheelEngineTorque,wheelFrictionTorque); else{ float angularAcceleration=((wheelEngineTorque-wheelFrictionTorque*sign(phys->wheels[i].angularVelo))/wheels[i].inertia); if(-angularAcceleration*kFrameTime>phys->wheels[i].angularVelo) phys->wheels[i].angularVelo=0; else phys->wheels[i].angularVelo+=angularAcceleration*kFrameTime; wheels[i].roadForce=Vector(0,0,0); phys->wheels[i].slipVelo=0; } phys->wheels[i].rotation+=phys->wheels[i].angularVelo*kFrameTime; phys->drivetrainAngularVelo+=phys->wheels[i].angularVelo*car->wheels[i].powered; } phys->drivetrainAngularVelo*=ratio; return (phys->drivetrainAngularVelo-oldDrivetrainAngularVelo)/kFrameTime; } void CalcClutchTorqueTransfer(tCarDefinition *car,tCarPhysics *phys,float ratio,float engineAngularAcceleration,float drivetrainAngularAcceleration) { if(ratio==0||phys->clutch==0) { phys->clutchTorque=0; return; } float angularAcceleration=(phys->engineAngularVelo+engineAngularAcceleration*kFrameTime-phys->drivetrainAngularVelo+drivetrainAngularAcceleration*kFrameTime)/kFrameTime; float i1=car->engineInertia; float i2=0; for(int i=0;inumWheels;i++) if(car->wheels[i].powered) i2+=car->wheels[i].inertia/sqr(ratio); float inertia=(i1*i2)/(i1+i2); float optimalTorque=inertia*angularAcceleration; float maxTorque=car->maxClutchTorqueTransfer*phys->clutch*2; phys->clutchTorque=optimalTorqueengineAngularVelo-=phys->clutchTorque/i1*kFrameTime; } void CalcWheelRoadForceClutch(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) { float ratio=car->gearRatios[phys->gear+1]*car->finalDriveRatio; if(gGameInfo->arcade==kGameModeTurbo) ratio*=0.8; float engineAngularAcceleration=CalcEngine(car,phys); float drivetrainAngularAcceleration=CalcWheelTorques(carEntity,car,phys,wheels,ratio); CalcClutchTorqueTransfer(car,phys,ratio,engineAngularAcceleration,drivetrainAngularAcceleration); } void CalcWheelRoadForce(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) { if(phys->clutch==1.0&&phys->gear!=0) CalcWheelRoadForceNoClutch(carEntity,car,phys,wheels); else CalcWheelRoadForceClutch(carEntity,car,phys,wheels); } void CalcTotalWheelForce(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,tVector3 *totalWheelForce,tVector3 *totalWheelTorque) { *totalWheelForce=Vector(0,0,0); *totalWheelTorque=Vector(0,0,0); for(int i=0;inumWheels;i++){ tVector3 wheelForce=wheels[i].roadForce+wheels[i].suspensionForce*wheels[i].groundNormal; tVector3 wheelGroundPoint=wheels[i].pos-*MatrixGetYVector(carEntity->dir)*(car->wheels[i].radius+phys->wheels[i].suspension); tVector3 wheelCarTorque=(wheelGroundPoint-car->massCenter*carEntity->dir)%wheelForce; *totalWheelTorque=*totalWheelTorque+wheelCarTorque; *totalWheelForce=*totalWheelForce+wheelForce; } if(phys->rpmjerkRPM) phys->rpm=car->jerkRPM; } #define kMaxDraftDistance 60.0 #define kMaxDraftAngle (PI/15.0) #define kMaxDraftVeloAngle (PI/10.0) float CalcDraftFactor(tGameEntity *car1) { float draftFactor=0; for(int i=0;inumPlayers;i++) if(gCarEntities[i]!=car1) { tGameEntity *car2=gCarEntities[i]; tVector3 v=car2->pos-car1->pos; float dir,veloDir,dist; dir=1-acos((!car1->velo)*!v)/kMaxDraftAngle; if(isnan(dir))dir=1; if(dir<0)dir=0; veloDir=1-acos((!car1->velo)*(!car2->velo))/kMaxDraftVeloAngle; if(isnan(veloDir))veloDir=1; if(veloDir<0)veloDir=0; dist=1-(~v)/kMaxDraftDistance; if(dist<0)dist=0; draftFactor+=dir*veloDir*dist; } if(draftFactor>1) draftFactor=1; return draftFactor; } //#define __NO_AERODYNAMICS void CalcAerodynamics(tGameEntity *carEntity,tCarDefinition *car,tVector3 *aerodynamicForce,tVector3 *aerodynamicTorque) { #ifndef __NO_AERODYNAMICS tVector3 xDir=*MatrixGetXVector(carEntity->dir); tVector3 yDir=*MatrixGetYVector(carEntity->dir); tVector3 zDir=*MatrixGetZVector(carEntity->dir); float fVelo=~carEntity->velo; float draftFactor=CalcDraftFactor(carEntity); float airRes=fVelo*0.5*fabs(zDir*carEntity->velo)*gEnvironment->airDensity; tVector3 frontSpoilPoint=Vector(0,car->massCenter.y,car->wheels[0].pos.z)*carEntity->dir; tVector3 rearSpoilPoint=Vector(0,car->massCenter.y,car->wheels[2].pos.z)*carEntity->dir; *aerodynamicForce=yDir*airRes*(car->frontLift+car->rearLift); *aerodynamicTorque=(frontSpoilPoint-car->massCenter*carEntity->dir)%(yDir*airRes*(car->frontLift))+(rearSpoilPoint-car->massCenter*carEntity->dir)%(yDir*airRes*(car->rearLift)); tVector3 dragCenter=Vector(0,car->massCenter.y,0); float dragFactor=fabs(zDir*carEntity->velo*car->frontAirResistance)+fabs(yDir*carEntity->velo*car->topAirResistance)+fabs(xDir*carEntity->velo*car->sideAirResistance); *aerodynamicForce=*aerodynamicForce-carEntity->velo*0.5*dragFactor*gEnvironment->airDensity*(1-0.9*draftFactor); *aerodynamicTorque=*aerodynamicTorque+((dragCenter-car->massCenter)*carEntity->dir)%*aerodynamicForce; #else *aerodynamicForce=Vector(0,0,0); *aerodynamicTorque=Vector(0,0,0); #endif } void CarLights(tCarPhysics *phys) { phys->lightFlags&=~(kLightFlagRevLight+kLightFlagBrakeLight); if((int)(gFrameCount*kFrameTime/kIndicatorTime)&1) phys->lightFlags&=~(kLightFlagLIndLight+kLightFlagRIndLight); if(phys->brake>0) phys->lightFlags|=kLightFlagBrakeLight; if(phys->gear==-1) phys->lightFlags|=kLightFlagRevLight; if(gEnvironment->spotLightEnable&&gFrameCount==0) phys->lightFlags|=kLightFlagDriveLight; } //Create Tracks and Particle Effects for the car void CarCreateEffects(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) { float totalSlip=0; for(int i=0;inumWheels;i++) { tSurfaceType *surf=gSurfaceTypes->types+phys->wheels[i].surfaceType; totalSlip+=fabs(phys->wheels[i].slipVelo)/car->numWheels; //does this surface smoke? if(wheels[i].onGround) if(surf->smokeEnable&&!((gCameraMode==kCameraCockpit||gCameraMode==kCameraCockpitCarHidden)&&carEntity==gViewedEntity)) { //is the wheel slipping enough to smoke? float smoke=(fabs(phys->wheels[i].slipVelo)-surf->minSmokeSlideVelo)/(surf->maxSmokeSlideVelo-surf->minSmokeSlideVelo); if(smoke>0) { //create particles tParticlesDef def; ParticlesInitDef(&def); if(smoke>1)smoke=1; smoke*=kMaxSmoke; def.sprite=surf->smokeTexture; def.maxSpread=surf->smokeSpread; def.maxVelo=surf->smokeMaxVelo; def.gravity=surf->smokeGravity; def.maxLife=surf->smokeMaxLife; def.brightness=0.5+(gEnvironment->ambient.x+gEnvironment->ambient.y+gEnvironment->ambient.z)*0.166; def.screenTexture=surf->smokeStickEnable?surf->smokeTexture:-1; def.xSize=surf->smokeSize;def.ySize=surf->smokeSize; tVector3 baseVelo=Vector(0,0.03,-0.1*phys->wheels[i].slip)*carEntity->dir*surf->smokeSpeed+carEntity->velo*0.3; ParticlesCreate(smoke*kFrameTime,wheels[i].pos+carEntity->pos,baseVelo,&def); } } //does this surface leave tracks? if(surf->trackEnable&&!((gCameraMode==kCameraCockpit||gCameraMode==kCameraCockpitCarHidden)&&carEntity==gViewedEntity)) //we only generate tracks every kTrackPrecision-th frame for performance reasons if(!(gFrameCount%kTrackPrecision)) { //Calculate track intensity float tracks=(fabs(phys->wheels[i].slipVelo)-surf->minTrackSlideVelo)/(surf->maxTrackSlideVelo-surf->minTrackSlideVelo); if(tracks>0&&wheels[i].onGround) { //create tracks. if(tracks>1)tracks=1; tVector3 pos1=wheels[i].pos+carEntity->pos-wheels[i].groundNormal*(phys->wheels[i].suspension+car->wheels[i].radius); pos1.y-=wheels[i].bump; tVector3 pos2=pos1+wheels[i].velo*kFrameTime; phys->wheels[i].lastTrack=TracksAdd(pos1,pos2,wheels[i].groundNormal,car->wheels[i].width,gSurfaceTypes->types[phys->wheels[i].surfaceType].trackTexture,tracks,phys->wheels[i].lastTrack); } else phys->wheels[i].lastTrack=0; } } //make the car dirtier phys->dirt+=totalSlip*kFrameTime*0.1; if(phys->dirt>100) phys->dirt=100; //exhaust fire if(car->exhaustFire+(phys->damage>kEngineDamage?100000:0)) { float exhaustFire=(((phys->oldThrottle-phys->throttle)*kFPS*phys->rpm)-(car->exhaustFire+(phys->damage>kEngineDamage?100000:0)))/((car->exhaustFire+(phys->damage>kEngineDamage?100000:0))); if(exhaustFire>0) { if(exhaustFire>1)exhaustFire=1; CarPlayCrashNoise(carEntity,FileGetReference("exhaust.wav"),exhaustFire); if(exhaustFire>0.5) { tParticlesDef def; ParticlesInitDef(&def); def.sprite=FileGetReference("exhaustfire.pct"); def.maxSpread=0.1; def.maxVelo=0.2; def.gravity=-0.2; def.maxLife=0.3; def.screenTexture=-1; def.xSize=0.6;def.ySize=0.6; def.grow=true; tVector3 baseVelo=Vector(0,0,0); //two exhaust pipes. Both may be at the same space to make it look like one. ParticlesCreate(exhaustFire*200*kFrameTime,car->exhaust1Pos*carEntity->dir+carEntity->pos,baseVelo,&def); ParticlesCreate(exhaustFire*200*kFrameTime,car->exhaust2Pos*carEntity->dir+carEntity->pos,baseVelo,&def); } } } phys->oldThrottle=phys->throttle; //exhaust smoke. float v=~carEntity->velo; if(v<10&&phys->damagethrottle; def.sprite=FileGetReference("exhaustsmoke.pct"); def.maxSpread=0.1; def.maxVelo=0.2; def.gravity=-0.8; def.maxLife=2; def.a=1-(v*0.1); def.screenTexture=-1; def.xSize=0.4;def.ySize=0.4; def.grow=true; tVector3 baseVelo=-*MatrixGetZVector(carEntity->dir)*smoke*2; //two exhaust pipes. Both may be at the same space to make it look like one. ParticlesCreate(smoke*40*kFrameTime,car->exhaust1Pos*carEntity->dir+carEntity->pos,baseVelo,&def); ParticlesCreate(smoke*40*kFrameTime,car->exhaust2Pos*carEntity->dir+carEntity->pos,baseVelo,&def); } if(phys->damage>kEngineDamage) { tParticlesDef def; ParticlesInitDef(&def); float smoke=0.2+0.8*phys->throttle; def.sprite=FileGetReference("exhaustsmoke.pct"); def.maxSpread=0.2; def.maxVelo=0.3; def.gravity=-0.7; def.maxLife=2; def.a=1; def.screenTexture=-1; def.xSize=1.5;def.ySize=1.5; def.grow=true; tVector3 baseVelo=Vector(0,1,0); //two exhaust pipes. Both may be at the same space to make it look like one. ParticlesCreate(smoke*60*kFrameTime,Vector(0,1,2.3)*carEntity->dir+carEntity->pos,baseVelo,&def); } } void CarPhysicsEntitySimulation(tGameEntity *carEntity) { tCarPhysics *phys=(tCarPhysics*)carEntity->physics; tCarDefinition *car=&(phys->car); tWheelCalcData wheels[kMaxWheels]; //Wheels position and velocity CalcWheelPositions(carEntity,car,phys,wheels); //wheels position relative to the ground CalcWheelGroundOffsets(carEntity,car,phys,wheels); //wheels springs & suspension CalcWheelSupension(carEntity,car,phys,wheels); if(carEntity->physicsMachine==kPhysicsRemote||gReplay) { for(int i=0;imass*gEnvironment->gravity,0); //Apply Forces tVector3 totalForce=totalWheelForce+gravityForce; tVector3 acceleration=totalForce*(1/car->mass); carEntity->velo=carEntity->velo+acceleration*kFrameTime; } else { //Force the wheel projects onto the road CalcWheelRoadForce(carEntity,car,phys,wheels); //Sum of wheel forces tVector3 totalWheelForce,totalWheelTorque; CalcTotalWheelForce(carEntity,car,phys,wheels,&totalWheelForce,&totalWheelTorque); //Aerodynamics tVector3 aerodynamicForce,aerodynamicTorque; CalcAerodynamics(carEntity,car,&aerodynamicForce,&aerodynamicTorque); //Gravity tVector3 gravityForce=Vector(0,-car->mass*gEnvironment->gravity,0); //Apply Forces tVector3 totalForce=totalWheelForce+aerodynamicForce+gravityForce; tVector3 acceleration=totalForce*(1/car->mass); carEntity->accel=acceleration; carEntity->velo=carEntity->velo+acceleration*kFrameTime; //Apply Torques tVector3 totalTorque=totalWheelTorque+aerodynamicTorque; tMatrix3 invMatrix; MatrixTranspose(carEntity->dir,invMatrix); totalTorque=totalTorque*invMatrix; float zTorq=1; if(gFrameCountinertia.x,totalTorque.y/car->inertia.y,zTorq*totalTorque.z/car->inertia.z)*carEntity->dir; tMatrix3 accelerationMatrix; RotationVectorToMatrix(angularAcceleration*kFrameTime*kFrameTime,accelerationMatrix); MatrixMult(carEntity->rVelo,accelerationMatrix,carEntity->rVelo); //if(carEntity->physicsMachine==kPhysicsLocal&&!gReplay) //{ //Lights CarLights(phys); int wheelOnGround=0; for(int i=0;inumWheels;i++) { if(wheels[i].onGround) wheelOnGround++; phys->wheels[i].onGround=wheels[i].onGround; } // if(wheelOnGround>2)//no y network Accel when on ground to dampen spring motion on bumpy surfaces // carEntity->accel.y=0; //Crash Recovery if(((wheelOnGroundnumWheels&&wheelOnGround<4)||phys->collision>gFrameCount-10)&&phys->damagecrashTime>kFPS*5) if(sqr(carEntity->velo)collision>gFrameCount-10&&sqr(carEntity->velo)crashTime=gFrameCount; phys->stuckTime=gFrameCount; } } else phys->crashTime=gFrameCount; carEntity->lastActivity=gFrameCount; int wheelsOnGround=0; for(int i=0;inumWheels;i++) if(wheels[i].onGround) wheelsOnGround++; phys->onGround=wheelsOnGround>=2; } //Smoke and Tracks! CarCreateEffects(carEntity,car,phys,wheels); } #define kMaxCornerAccel 15 #define kMaxEngineAccel 6 #define kMaxBrakeAccel 8 #define kEngineFactor 0.8 void CarPhysicsEntitySimple(tGameEntity *carEntity) { tCarPhysics *phys=(tCarPhysics*)carEntity->physics; tVector3 velo=carEntity->velo; tVector3 rVelo=MatrixToEulerAngles(carEntity->rVelo); rVelo.y=0; EulerAnglesToMatrix(rVelo,carEntity->rVelo); CarPhysicsEntitySimulation(carEntity); carEntity->velo.x=(5*velo.x+carEntity->velo.x)/6.0; carEntity->velo.z=(5*velo.z+carEntity->velo.z)/6.0; tCarDefinition *car=&(phys->car); float groundFriction=0; for(int i=0;inumWheels;i++) groundFriction+=gSurfaceTypes->types[phys->wheels[i].surfaceType].grip/(float)car->numWheels; //Calculate the drivetrain ratio float ratio=car->gearRatios[phys->gear+1]*car->finalDriveRatio; //Calculate the engine inertia at the final drive float inertia=car->engineInertia*sqr(ratio); //Calculate the frictional braking torque of the engine (in N*m) float engineFrictionTorque=25+phys->rpm*0.02; if(car->engineBaseFriction||car->engineRPMFriction) engineFrictionTorque=car->engineBaseFriction+phys->rpm*car->engineRPMFriction; //Calculate the current engine torque (in N*m) float engineTorque=(CalcTorque(phys->rpm,car)+engineFrictionTorque)*phys->throttle-engineFrictionTorque; if(engineTorque<0&&carEntity->velo**MatrixGetZVector(carEntity->dir)*ratio<0) engineTorque*=-1; tVector3 flatDir=!Vector(MatrixGetZVector(carEntity->dir)->x,0,MatrixGetZVector(carEntity->dir)->z); tVector3 flatVelo=Vector(velo.x,0,velo.z); float fEngineForce=(car->aiSpeedIndex!=0?car->aiSpeedIndex:1)*kEngineFactor*engineTorque*ratio/car->wheels[2].radius; if(fEngineForce>car->mass*kMaxEngineAccel*groundFriction) fEngineForce=car->mass*kMaxEngineAccel*groundFriction; tVector3 engineForce=flatDir*fEngineForce; float arcadeDraftBoost=CalcDraftFactor(carEntity)*(~carEntity->velo)/70.0; if(phys->arcadeDraftBoostarcadeDraftBoost+=2*kFrameTime; if(phys->arcadeDraftBoost>arcadeDraftBoost) phys->arcadeDraftBoost-=2*kFrameTime; if(phys->arcadeDraftBoost>1)phys->arcadeDraftBoost=1; if(phys->arcadeDraftBoost<0)phys->arcadeDraftBoost=0; if(carEntity->controlType==kControlTypeAIInput) phys->arcadeDraftBoost=0; //engineForce=engineForce+!engineForce*14*car->mass*phys->arcadeDraftBoost; tVector3 airRes=-carEntity->velo*~carEntity->velo*0.5*car->frontAirResistance*gEnvironment->airDensity; float surfaceBrake=0; for(int i=0;inumWheels;i++) surfaceBrake+=gSurfaceTypes->types[phys->wheels[i].surfaceType].brakeFactor*fabs(phys->wheels[i].angularVelo)/(float)car->numWheels; float brake=phys->arcadeBrake+phys->handbrake*0.5; if(brake>1)brake=1; brake+=surfaceBrake; tVector3 brakeForce=-!flatVelo*brake*car->mass*kMaxBrakeAccel; if(sqr(brakeForce/car->mass)*kFrameTime>sqr(flatVelo)) brakeForce=-!flatVelo*car->mass*kFrameTime; tVector3 totalForce; if(phys->onGround) totalForce=engineForce+airRes+brakeForce; else totalForce=airRes; tVector3 totalAccel=totalForce/car->mass; if(gGameInfo->arcade==kGameModeTurbo) totalAccel=totalAccel*2.3; carEntity->velo=carEntity->velo+(totalAccel*kFrameTime); tMatrix3 m; MatrixIdentity(m); MatrixRotY(m,phys->steering*0.1); flatDir=flatDir*~flatVelo; flatDir=flatDir*m; float rev=sign(flatDir*flatVelo); flatDir=flatDir*rev; float diff=~(flatVelo-flatDir); float cornerAccel=kFrameTime*kMaxCornerAccel*groundFriction; if(gGameInfo->arcade==kGameModeTurbo) cornerAccel*=1.9; if(cornerAccel>diff)cornerAccel=diff; tVector3 vCornerAccel=-!(flatVelo-flatDir)*cornerAccel; carEntity->velo=carEntity->velo+vCornerAccel; carEntity->accel=totalAccel+vCornerAccel*kFPS; float handBrakeFactor=1+phys->handbrake*2; if(rev<0) handBrakeFactor=1; float veloSteer=((15/(~velo+1))+1)*handBrakeFactor; float maxRotSteer=10*kFrameTime*handBrakeFactor; float acardeSteerVeloInput=rev*phys->steering*~velo*kFrameTime*0.1*handBrakeFactor; if(!phys->onGround) acardeSteerVeloInput=0; if(acardeSteerVeloInput>phys->arcadeSteerVelo){ phys->arcadeSteerVelo+=0.015*veloSteer*kFrameTime; if(acardeSteerVeloInputarcadeSteerVelo) phys->arcadeSteerVelo=acardeSteerVeloInput; } else{ phys->arcadeSteerVelo-=0.015*veloSteer*kFrameTime; if(acardeSteerVeloInput>phys->arcadeSteerVelo) phys->arcadeSteerVelo=acardeSteerVeloInput; } if(fabs(phys->arcadeSteerVelo)>maxRotSteer) phys->arcadeSteerVelo=sign(phys->arcadeSteerVelo)*maxRotSteer; MatrixRotY(carEntity->rVelo,phys->arcadeSteerVelo); } void CarPhysicsEntityMagic(tGameEntity *carEntity) { tCarPhysics *phys=(tCarPhysics*)carEntity->physics; MatrixIdentity(carEntity->rVelo); tVector3 x[4],a[4]; x[0]=Vector(1.2,0,2); x[1]=Vector(-1.2,0,2); x[2]=Vector(1.2,0,-2); x[3]=Vector(-1.2,0,-2); tVector3 howerAccel=Vector(0,0,0); tVector3 howerRotAccel=Vector(0,0,0); for(int i=0;i<4;i++) { x[i]=x[i]*carEntity->dir; tVector3 normal; float offs=GetGroundOffset(x[i]+carEntity->pos,&carEntity->lastRoadIndex,&normal,NULL); if(offs<0.2)offs=0.2; a[i]=normal*(1.25/offs)*(1+0.5*phys->steering*(i&1?1:-1))*(1+0.1*phys->brake*(i&2?1:-1))*(1-0.1*phys->throttle*(i&2?1:-1)); howerAccel=howerAccel+a[i]; howerRotAccel=howerRotAccel+x[i]%a[i]; } howerAccel=howerAccel*(1+sin(gFrameCount*kFrameTime*2)*0.12); tMatrix3 invMatrix; MatrixTranspose(carEntity->dir,invMatrix); howerRotAccel=Vector(howerRotAccel.x*5,0,howerRotAccel.z*30); tMatrix3 accelerationMatrix; RotationVectorToMatrix(howerRotAccel*kFrameTime*kFrameTime,accelerationMatrix); MatrixMult(carEntity->rVelo,accelerationMatrix,carEntity->rVelo); tVector3 velo=carEntity->velo; tVector3 gravityAccel=Vector(0,-gEnvironment->gravity,0); tVector3 engineAccel=phys->throttle**MatrixGetZVector(carEntity->dir)*(20); tVector3 brakeAccel=phys->brake*!(*MatrixGetZVector(carEntity->dir)+((sqr(carEntity->velo)>0.001)?carEntity->velo*0.1:Vector(0,0,0)))*-(22); if(gFrameCount*kFrameTime<=kStartGameDelaySeconds) { engineAccel=Vector(0,0,0); brakeAccel=Vector(0,0,0); } if(phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1) brakeAccel=phys->brake*(((sqr(carEntity->velo)>0.001)?!carEntity->velo:Vector(0,0,0)))*-(22); brakeAccel.y=0; tVector3 dampAccel=Vector(0,0,0); tVector3 airResAccel=Vector(0,0,0); if(sqr(carEntity->velo)>0.001) { dampAccel=-!carEntity->velo*5; airResAccel=-!carEntity->velo*sqr(carEntity->velo)*0.003; if(sqr(dampAccel*kFrameTime)>sqr(carEntity->velo)) dampAccel=-carEntity->velo*kFPS; dampAccel.x*=0.1; dampAccel.z*=0.1; } tVector3 totalAccel=gravityAccel+howerAccel+engineAccel+brakeAccel+dampAccel+airResAccel; carEntity->velo=carEntity->velo+totalAccel*kFrameTime; carEntity->lastActivity=gFrameCount; tVector3 flatDir=!Vector(MatrixGetZVector(carEntity->dir)->x,0,MatrixGetZVector(carEntity->dir)->z); tVector3 flatVelo=Vector(velo.x,0,velo.z); tMatrix3 m; MatrixIdentity(m); MatrixRotY(m,phys->steering*0.1); flatDir=flatDir*~flatVelo; flatDir=flatDir*m; float rev=sign(flatDir*flatVelo); flatDir=flatDir*rev; float diff=~(flatVelo-flatDir); float cornerAccel=kFrameTime*kMaxCornerAccel; if(gGameInfo->arcade==kGameModeTurbo) cornerAccel*=1.9; if(cornerAccel>diff)cornerAccel=diff; tVector3 vCornerAccel=-!(flatVelo-flatDir)*cornerAccel; carEntity->velo=carEntity->velo+vCornerAccel; carEntity->accel=totalAccel+vCornerAccel*kFPS; float acardeSteerVeloInput=phys->steering*kFrameTime*(3+~velo*0.04); if(acardeSteerVeloInput>phys->arcadeSteerVelo){ phys->arcadeSteerVelo+=0.15*kFrameTime; if(acardeSteerVeloInputarcadeSteerVelo) phys->arcadeSteerVelo=acardeSteerVeloInput; } else{ phys->arcadeSteerVelo-=0.15*kFrameTime; if(acardeSteerVeloInput>phys->arcadeSteerVelo) phys->arcadeSteerVelo=acardeSteerVeloInput; } if(gFrameCount*kFrameTime>=kStartGameDelaySeconds) MatrixRotY(carEntity->rVelo,phys->arcadeSteerVelo); if(gEnvironment->spotLightEnable&&gFrameCount==0) phys->lightFlags|=kLightFlagDriveLight; if(phys->collision>gFrameCount-10) { if(gFrameCount-phys->crashTime>kFPS*5) if(sqr(carEntity->velo)crashTime=gFrameCount; phys->stuckTime=gFrameCount; } }else phys->crashTime=gFrameCount; *MatrixGetYVector(carEntity->dir)=!(*MatrixGetYVector(carEntity->dir)+(Vector(0,1,0)-*MatrixGetYVector(carEntity->dir))*kFrameTime*0.5); *MatrixGetXVector(carEntity->dir)=!(*MatrixGetYVector(carEntity->dir)%*MatrixGetZVector(carEntity->dir)); *MatrixGetZVector(carEntity->dir)=!(*MatrixGetXVector(carEntity->dir)%*MatrixGetYVector(carEntity->dir)); } void CarPhysicsEntity(tGameEntity *carEntity) { tCarPhysics *phys=(tCarPhysics*)carEntity->physics; tCarDefinition *car=&(phys->car); if(car->magic) CarPhysicsEntityMagic(carEntity); else if((gGameInfo->arcade==kGameModeTurbo||gGameInfo->arcade==kGameModeArcade||carEntity->controlType==kControlTypeAIInput)&&carEntity->physicsMachine==kPhysicsLocal) CarPhysicsEntitySimple(carEntity); else CarPhysicsEntitySimulation(carEntity); } #define kPredictTime 0.15 //Move the car around void MatrixReAdjust2(tMatrix3 m) { *(tVector3*)m[1]=!*(tVector3*)m[1]; *(tVector3*)m[0]=!(*(tVector3*)m[1]%*(tVector3*)m[2]); *(tVector3*)m[2]=!(*(tVector3*)m[0]%*(tVector3*)m[1]); } void FullyResetCar(tGameEntity *carEntity) { tCarPhysics *phys=(tCarPhysics*)carEntity->physics; carEntity->pos=gMapInfo->startPos+Vector(0,10,0); carEntity->oldPos=carEntity->pos; carEntity->netPos=carEntity->pos; carEntity->velo=Vector(0,0,0); carEntity->netVelo=Vector(0,0,0); carEntity->collVelo=Vector(0,0,0); carEntity->accel=Vector(0,0,0); MatrixIdentity(carEntity->dir); MatrixIdentity(carEntity->rVelo); MatrixIdentity(carEntity->oldDir); MatrixIdentity(carEntity->netDir); MatrixIdentity(carEntity->netRVelo); for(int i=0;ilastRVelos[i]); carEntity->lastVelos[i]=Vector(0,0,0); carEntity->lastAccel[i]=Vector(0,0,0); } carEntity->remoteCameraPos=Vector(0,0,0); carEntity->zDist=0; phys->maxSpeed=0; phys->averageSpeed=0; phys->accel100=0; phys->accel200=0; phys->accelQuarter=0; phys->accelKM=0; phys->odo; phys->position=0; phys->lead=0; phys->rpm=0; phys->engineAngularVelo=0; phys->drivetrainAngularVelo=0; phys->clutchTorque=0; for(int i=0;iwheels[i].angle=0; phys->wheels[i].suspension=0; phys->wheels[i].slipVelo=0; phys->wheels[i].slip=0; phys->wheels[i].slipAngle=0; phys->wheels[i].rotation=0; phys->wheels[i].angularVelo=0; phys->wheels[i].glow=0; } } void CarMotionEntity(tGameEntity *carEntity) { tCarPhysics *phys=(tCarPhysics*)carEntity->physics; tCarDefinition *car=&(phys->car); for(int i=kNumAvgRVelos-2;i>=0;i--) { MatrixCopy(carEntity->lastRVelos[i],carEntity->lastRVelos[i+1]); carEntity->lastVelos[i+1]=carEntity->lastVelos[i]; carEntity->lastAccel[i+1]=carEntity->lastAccel[i]; } MatrixCopy(carEntity->rVelo,carEntity->lastRVelos[0]); carEntity->lastVelos[0]=carEntity->velo; carEntity->lastAccel[0]=carEntity->accel; carEntity->lastActivity=gFrameCount; //move the car if(carEntity->physicsMachine==kPhysicsRemote) { if(!gReplay) { float smoothness=0; for(int i=0;ilastCollisions[i].frameCount)*kFrameTime<0.5) smoothness+=(8.0f/kNumLastCollisions); if(smoothness>1) smoothness=1; float predictTime=kPredictTime*(1+smoothness*1.5); carEntity->netVelo=carEntity->netVelo+carEntity->accel*kFrameTime; carEntity->netPos=carEntity->netPos+carEntity->netVelo*kFrameTime; carEntity->velo=carEntity->velo+((carEntity->netPos+carEntity->netVelo*predictTime+carEntity->accel*predictTime*predictTime*0.5)-(carEntity->pos+carEntity->velo*predictTime))/predictTime; MatrixMult(carEntity->netDir,carEntity->rVelo,carEntity->netDir); tVector3 netx=*MatrixGetXVector(carEntity->netDir); tVector3 nety=*MatrixGetYVector(carEntity->netDir); tVector3 netz=*MatrixGetZVector(carEntity->netDir); tVector3 curx=*MatrixGetXVector(carEntity->dir); tVector3 curz=*MatrixGetZVector(carEntity->dir); *MatrixGetXVector(carEntity->dir)=curx+(netx-curx)*0.3; *MatrixGetYVector(carEntity->dir)=nety; *MatrixGetZVector(carEntity->dir)=curz+(netz-curz)*0.3; MatrixReAdjust2(carEntity->dir); if(sqr(carEntity->pos-carEntity->netPos)>sqr(5+smoothness*10)) { carEntity->pos=carEntity->netPos; carEntity->velo=carEntity->netVelo; MatrixCopy(carEntity->netDir,carEntity->dir); } } for(int i=0;inumWheels;i++) phys->wheels[i].rotation+=phys->wheels[i].angularVelo*kFrameTime; } if(gReplay) { carEntity->velo=carEntity->velo+carEntity->accel*kFrameTime; MatrixMult(carEntity->netDir,carEntity->rVelo,carEntity->netDir); tVector3 netx=*MatrixGetXVector(carEntity->netDir); tVector3 nety=*MatrixGetYVector(carEntity->netDir); tVector3 netz=*MatrixGetZVector(carEntity->netDir); tVector3 curx=*MatrixGetXVector(carEntity->dir); tVector3 curz=*MatrixGetZVector(carEntity->dir); *MatrixGetXVector(carEntity->dir)=curx+(netx-curx)*0.3; *MatrixGetYVector(carEntity->dir)=nety; *MatrixGetZVector(carEntity->dir)=curz+(netz-curz)*0.3; MatrixReAdjust2(carEntity->dir); } carEntity->pos=carEntity->pos+carEntity->velo*kFrameTime; //rotate the car around it's center of gravity tVector3 massCenter=car->massCenter*carEntity->dir; tVector3 massMotion=((massCenter*carEntity->rVelo)-massCenter); MatrixMult(carEntity->dir,carEntity->rVelo,carEntity->dir); carEntity->pos=carEntity->pos-massMotion; //update road position index float oldPosition=phys->position; phys->position=RoadGetPosition(carEntity->pos,carEntity->lastRoadIndex,NULL); if(carEntity==gViewedEntity) for(int i=0;ireverse){ if(phys->positiongCornerSigns[i].pos) { gAccelSignDisplayIntensity=1.5; gAccelSignDisplayCorner=-gCornerSigns[i].accel; } }else{ if(phys->position>gCornerSigns[i].pos&&oldPositionreverse) { if(oldPosition>phys->position+50) phys->lap--; else if(oldPositionposition-50) phys->lap++; } else { if(oldPosition>phys->position+50) phys->lap++; else if(oldPositionposition-50) phys->lap--; } //test if we are stuck (used for ai recovery) int stuck=gFrameCount*kFrameTime>5&&sqr(carEntity->velo)lapCount<=gGameInfo->numLaps; if(stuck) phys->stuckTime++; else phys->stuckTime=0; //are we moving backwards? (used to display "wrong direction" message). if((gGameInfo->reverse?oldPosition<=phys->position:oldPosition>=phys->position)&&sqr(carEntity->velo)>sqr(5)) phys->wrongDriectionFrames++; else phys->wrongDriectionFrames-=10; if(phys->wrongDriectionFrames<0) phys->wrongDriectionFrames=0; else if(phys->wrongDriectionFrames>kFPS*5) { phys->wrongDriectionFrames=kFPS*5; if(gGameInfo->network&kGameInfoNetworkCantGoBackwards) { if(carEntity==gViewedEntity) TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,0.9,1.8,"Wrong Way!"); RoadCenterCar(carEntity); } } float velo=~carEntity->velo; //Check if we have beaten our speed record for this game if(velo>phys->maxSpeed) phys->maxSpeed=velo; //Update our average speed if(phys->lapCount&&(phys->lapCount<=gGameInfo->numLaps||gGameInfo->numLaps==-1)&&!gReplay) { phys->averageSpeed=((phys->averageSpeed*(phys->averageSpeedFrames))+velo)/(phys->averageSpeedFrames+1); phys->averageSpeedFrames++; } if(velo>=100/3.6&&phys->accel100==0) phys->accel100=gFrameCount*kFrameTime-kStartGameDelaySeconds; if(velo>=200/3.6&&phys->accel200==0) phys->accel200=gFrameCount*kFrameTime-kStartGameDelaySeconds; phys->odo+=velo*kFrameTime; if(phys->odo>=402.32454&&phys->accelQuarter==0) phys->accelQuarter=gFrameCount*kFrameTime-kStartGameDelaySeconds; if(phys->odo>=1000&&phys->accelKM==0) phys->accelKM=gFrameCount*kFrameTime-kStartGameDelaySeconds; /*if(Button()&&phys->accel200!=0&&!gReplay) { printf("0-100km/h: %.1f seconds\n0-200km/h: %.1f seconds\n100-200km/h: %.1f seconds\nQuarter Mile: %.1f seconds\n1km: %.1f seconds\nTop Speed:%.1f km/h\n",phys->accel100,phys->accel200,phys->accel200-phys->accel100,phys->accelQuarter,phys->accelKM,phys->maxSpeed*3.6); phys->accel200=0; }*/ if(isnan(carEntity->pos.x)||isnan(carEntity->pos.y)||isnan(carEntity->pos.z)) FullyResetCar(carEntity); }