void SimWheelUpdateRotation(tCar *car) { int i; tWheel *wheel; tdble deltan; tdble cosaz, sinaz; for (i = 0; i < 4; i++) { wheel = &(car->wheel[i]); /*calculate gyroscopic forces*/ cosaz = cos(wheel->relPos.az); sinaz = sin(wheel->relPos.az); if( (i == 0) || (i == 1) ){ wheel->torques.y = wheel->torques.x * sinaz; wheel->torques.x = wheel->torques.x * cosaz; } else { wheel->torques.x = wheel->torques.y =0.0; } deltan = -(wheel->in.spinVel - wheel->prespinVel) * wheel->I / SimDeltaTime; wheel->torques.x -= deltan * wheel->cosax *sinaz; wheel->torques.y += deltan * wheel->cosax *cosaz; wheel->torques.z = deltan * wheel->sinax; /*update rotation*/ wheel->spinVel = wheel->in.spinVel; FLOAT_RELAXATION2(wheel->spinVel, wheel->prespinVel, 50.0f); wheel->relPos.ay += wheel->spinVel * SimDeltaTime; FLOAT_NORM_PI_PI(wheel->relPos.ay); car->carElt->_wheelSpinVel(i) = wheel->spinVel; } }
static void SimCarUpdatePos(tCar *car) { tdble vx, vy; vx = car->DynGCg.vel.x; vy = car->DynGCg.vel.y; car->DynGCg.pos.x += vx * SimDeltaTime; car->DynGCg.pos.y += vy * SimDeltaTime; car->DynGCg.pos.z += car->DynGCg.vel.z * SimDeltaTime; car->DynGCg.pos.ax += car->DynGCg.vel.ax * SimDeltaTime; car->DynGCg.pos.ay += car->DynGCg.vel.ay * SimDeltaTime; car->DynGCg.pos.az += car->DynGCg.vel.az * SimDeltaTime; FLOAT_NORM_PI_PI(car->DynGCg.pos.az); if (car->DynGCg.pos.ax > aMax) car->DynGCg.pos.ax = aMax; if (car->DynGCg.pos.ax < -aMax) car->DynGCg.pos.ax = -aMax; if (car->DynGCg.pos.ay > aMax) car->DynGCg.pos.ay = aMax; if (car->DynGCg.pos.ay < -aMax) car->DynGCg.pos.ay = -aMax; car->DynGC.pos.x = car->DynGCg.pos.x; car->DynGC.pos.y = car->DynGCg.pos.y; car->DynGC.pos.z = car->DynGCg.pos.z; car->DynGC.pos.ax = car->DynGCg.pos.ax; car->DynGC.pos.ay = car->DynGCg.pos.ay; car->DynGC.pos.az = car->DynGCg.pos.az; RtTrackGlobal2Local(car->trkPos.seg, car->DynGCg.pos.x, car->DynGCg.pos.y, &(car->trkPos), TR_LPOS_MAIN); }
// Compute steer value. float Driver::getSteer() { float targetAngle; vec2f target = getTargetPoint(); targetAngle = atan2(target.y - car->_pos_Y, target.x - car->_pos_X); targetAngle -= car->_yaw; FLOAT_NORM_PI_PI(targetAngle); return targetAngle / car->_steerLock; }
void SimWheelUpdateRotation(tCar *car) { int i; tWheel *wheel; for (i = 0; i < 4; i++) { wheel = &(car->wheel[i]); wheel->spinVel = wheel->in.spinVel; FLOAT_RELAXATION2(wheel->spinVel, wheel->prespinVel, 50.0f); wheel->relPos.ay += wheel->spinVel * SimDeltaTime; FLOAT_NORM_PI_PI(wheel->relPos.ay); car->carElt->_wheelSpinVel(i) = wheel->spinVel; } }
// Update my private data every timestep. void Driver::update(tSituation *s) { // Update global car data (shared by all instances) just once per timestep. if (currentsimtime != s->currentTime) { currentsimtime = s->currentTime; cardata->update(); } // Update the local data rest. speedangle = mycardata->getTrackangle() - atan2(car->_speed_Y, car->_speed_X); FLOAT_NORM_PI_PI(speedangle); mass = CARMASS + car->_fuel; currentspeedsqr = car->_speed_x*car->_speed_x; opponents->update(s, this); strategy->update(car, s); if (!pit->getPitstop()) { pit->setPitstop(strategy->needPitstop(car, s)); } pit->update(); alone = isAlone(); learn->update(s, track, car, alone, myoffset, car->_trkPos.seg->width/WIDTHDIV-BORDER_OVERTAKE_MARGIN, radius); }
void SimWheelUpdateForce(tCar *car, int index) { tWheel *wheel = &(car->wheel[index]); tdble axleFz = wheel->axleFz; tdble vt, v, v2, wrl; // wheel related velocity tdble Fn, Ft; tdble waz; tdble CosA, SinA; tdble s, sa, sx, sy; // slip vector tdble stmp, F, Bx; tdble mu; tdble reaction_force = 0.0f; wheel->state = 0; // VERTICAL STUFF CONSIDERING SMALL PITCH AND ROLL ANGLES // update suspension force SimSuspUpdate(&(wheel->susp)); // check suspension state wheel->state |= wheel->susp.state; if ((wheel->state & SIM_SUSP_EXT) == 0) { wheel->forces.z = axleFz + wheel->susp.force; reaction_force = wheel->forces.z; wheel->rel_vel -= SimDeltaTime * wheel->susp.force / wheel->mass; if (wheel->forces.z < 0.0f) { wheel->forces.z = 0.0f; } } else { if (wheel->rel_vel < 0.0) { wheel->rel_vel = 0.0; } wheel->rel_vel -= SimDeltaTime * wheel->susp.force / wheel->mass; wheel->forces.z = 0.0f; } // update wheel coord, center relative to GC wheel->relPos.z = - wheel->susp.x / wheel->susp.spring.bellcrank + wheel->radius; // HORIZONTAL FORCES waz = wheel->steer + wheel->staticPos.az; CosA = cos(waz); SinA = sin(waz); // tangent velocity. vt = wheel->bodyVel.x * CosA + wheel->bodyVel.y * SinA; v2 = wheel->bodyVel.x * wheel->bodyVel.x + wheel->bodyVel.y * wheel->bodyVel.y; v = sqrt(v2); // slip angle if (v < 0.000001f) { sa = 0.0f; } else { sa = atan2(wheel->bodyVel.y, wheel->bodyVel.x) - waz; } FLOAT_NORM_PI_PI(sa); wrl = wheel->spinVel * wheel->radius; if ((wheel->state & SIM_SUSP_EXT) != 0) { sx = sy = 0.0f; } else if (v < 0.000001f) { sx = wrl; sy = 0.0f; } else { sx = (vt - wrl) / fabs(vt); sy = sin(sa); } Ft = 0.0f; Fn = 0.0f; s = sqrt(sx*sx+sy*sy); { // calculate _skid and _reaction for sound. if (v < 2.0f) { car->carElt->_skid[index] = 0.0f; } else { car->carElt->_skid[index] = MIN(1.0f, (s*reaction_force*0.0002f)); } car->carElt->_reaction[index] = reaction_force; } stmp = MIN(s, 1.5f); // MAGIC FORMULA Bx = wheel->mfB * stmp; F = sin(wheel->mfC * atan(Bx * (1.0f - wheel->mfE) + wheel->mfE * atan(Bx))) * (1.0f + stmp * simSkidFactor[car->carElt->_skillLevel]); // load sensitivity mu = wheel->mu * (wheel->lfMin + (wheel->lfMax - wheel->lfMin) * exp(wheel->lfK * wheel->forces.z / wheel->opLoad)); F *= wheel->forces.z * mu * wheel->trkPos.seg->surface->kFriction * (1.0f + 0.05f * sin(-wheel->staticPos.ax * 18.0f)); /* coeff */ //For debug weather with some tracks #ifdef SD_DEBUG GfLogDebug("Simu v2 kFriction : %f ", wheel->trkPos.seg->surface->kFriction); #endif wheel->rollRes = wheel->forces.z * wheel->trkPos.seg->surface->kRollRes; car->carElt->priv.wheel[index].rollRes = wheel->rollRes; if (s > 0.000001f) { // wheel axis based Ft -= F * sx / s; Fn -= F * sy / s; } FLOAT_RELAXATION2(Fn, wheel->preFn, 50.0f); FLOAT_RELAXATION2(Ft, wheel->preFt, 50.0f); wheel->relPos.az = waz; wheel->forces.x = Ft * CosA - Fn * SinA; wheel->forces.y = Ft * SinA + Fn * CosA; wheel->spinTq = Ft * wheel->radius; wheel->sa = sa; wheel->sx = sx; wheel->feedBack.spinVel = wheel->spinVel; wheel->feedBack.Tq = wheel->spinTq; wheel->feedBack.brkTq = wheel->brake.Tq; car->carElt->_wheelSlipSide(index) = sy*v; car->carElt->_wheelSlipAccel(index) = sx*v; car->carElt->_reaction[index] = reaction_force; }
void SimAeroUpdate(tCar *car, tSituation *s) { tdble hm; int i; tCar *otherCar; tdble x, y; tdble yaw, otherYaw, airSpeed, tmpas, spdang, tmpsdpang, dyaw; tdble dragK = 1.0; x = car->DynGCg.pos.x; y = car->DynGCg.pos.y; yaw = car->DynGCg.pos.az; airSpeed = car->DynGC.vel.x; spdang = atan2(car->DynGCg.vel.y, car->DynGCg.vel.x); if (airSpeed > 10.0) { for (i = 0; i < s->_ncars; i++) { if (i == car->carElt->index) { continue; } otherCar = &(SimCarTable[i]); otherYaw = otherCar->DynGCg.pos.az; tmpsdpang = spdang - atan2(y - otherCar->DynGCg.pos.y, x - otherCar->DynGCg.pos.x); FLOAT_NORM_PI_PI(tmpsdpang); dyaw = yaw - otherYaw; FLOAT_NORM_PI_PI(dyaw); if ((otherCar->DynGC.vel.x > 10.0) && (fabs(dyaw) < 0.1396)) { if (fabs(tmpsdpang) > 2.9671) { /* 10 degrees */ /* behind another car */ tmpas = (tdble) (1.0 - exp(- 2.0 * DIST(x, y, otherCar->DynGCg.pos.x, otherCar->DynGCg.pos.y) / (otherCar->aero.Cd * otherCar->DynGC.vel.x))); if (tmpas < dragK) { dragK = tmpas; } } else if (fabs(tmpsdpang) < 0.1396) { /* 8 degrees */ /* before another car [not sure how much the drag should be reduced in this case. In no case it should be lowered more than 50% I think. - Christos] */ tmpas = (tdble) (1.0 - 0.5f * exp(- 8.0 * DIST(x, y, otherCar->DynGCg.pos.x, otherCar->DynGCg.pos.y) / (car->aero.Cd * car->DynGC.vel.x))); if (tmpas < dragK) { dragK = tmpas; } } } } } car->airSpeed2 = airSpeed * airSpeed; tdble v2 = car->airSpeed2; // simulate ground effect drop off caused by non-frontal airflow (diffusor stops working etc.) tdble speed = sqrt(car->DynGC.vel.x*car->DynGC.vel.x + car->DynGC.vel.y*car->DynGC.vel.y); tdble cosa = 1.0f; if (speed > 1.0f) { cosa = car->DynGC.vel.x/speed; } if (cosa < 0.0f) { cosa = 0.0f; } car->aero.drag = (tdble) (-SIGN(car->DynGC.vel.x) * car->aero.SCx2 * v2 * (1.0f + (tdble)car->dammage / 10000.0f) * dragK * dragK); hm = 1.5f * (car->wheel[0].rideHeight + car->wheel[1].rideHeight + car->wheel[2].rideHeight + car->wheel[3].rideHeight); hm = hm*hm; hm = hm*hm; hm = 2 * exp(-3.0f*hm); car->aero.lift[0] = - car->aero.Clift[0] * v2 * hm; car->aero.lift[1] = - car->aero.Clift[1] * v2 * hm; }
void RtTrackGlobal2Local(tTrackSeg *segment, tdble X, tdble Y, tTrkLocPos *p, int type) { int segnotfound = 1; tdble x, y; tTrackSeg *seg = segment; tTrackSeg *sseg; tdble theta, a2; int depl = 0; tdble curWidth; p->type = type; while (segnotfound) { switch(seg->type) { case TR_STR: /* rotation */ tdble ts; x = X - seg->vertex[TR_SR].x; y = Y - seg->vertex[TR_SR].y; ts = x * seg->cos + y * seg->sin; p->seg = seg; p->toStart = ts; p->toRight = y * seg->cos - x * seg->sin; if ((ts < 0) && (depl < 1)) { /* get back */ seg = seg->prev; depl = -1; } else if ((ts > seg->length) && (depl > -1)) { seg = seg->next; depl = 1; } else { segnotfound = 0; } break; case TR_LFT: /* rectangular to polar */ x = X - seg->center.x; y = Y - seg->center.y; a2 = seg->arc / 2.0f; theta = atan2(y, x) - (seg->angle[TR_CS] + a2); FLOAT_NORM_PI_PI(theta); p->seg = seg; p->toStart = theta + a2; p->toRight = seg->radiusr - sqrt(x*x + y*y); if ((theta < -a2) && (depl < 1)) { seg = seg->prev; depl = -1; } else if ((theta > a2) && (depl > -1)) { seg = seg->next; depl = 1; } else { segnotfound = 0; } break; case TR_RGT: /* rectangular to polar */ x = X - seg->center.x; y = Y - seg->center.y; a2 = seg->arc / 2.0f; theta = seg->angle[TR_CS] - a2 - atan2(y, x); FLOAT_NORM_PI_PI(theta); p->seg = seg; p->toStart = theta + a2; p->toRight = sqrt(x*x + y*y) - seg->radiusr; if ((theta < -a2) && (depl < 1)) { seg = seg->prev; depl = -1; } else if ((theta > a2) && (depl > -1)) { seg = seg->next; depl = 1; } else { segnotfound = 0; } break; } } /* The track is of constant width */ /* This is subject to change */ p->toMiddle = p->toRight - seg->width / 2.0f; p->toLeft = seg->width - p->toRight; /* Consider all the track with the sides */ /* Stay on main segment */ if (type == TR_LPOS_TRACK) { if (seg->rside != NULL) { sseg = seg->rside; p->toRight += RtTrackGetWidth(sseg, p->toStart); sseg = sseg->rside; if (sseg) { p->toRight += RtTrackGetWidth(sseg, p->toStart); } } if (seg->lside != NULL) { sseg = seg->lside; p->toLeft += RtTrackGetWidth(sseg, p->toStart); sseg = sseg->lside; if (sseg) { p->toLeft += RtTrackGetWidth(sseg, p->toStart); } } } /* Relative to a segment, change to the side segment if necessary */ if (type == TR_LPOS_SEGMENT) { if ((p->toRight < 0) && (seg->rside != NULL)) { sseg = seg->rside; p->seg = sseg; curWidth = RtTrackGetWidth(sseg, p->toStart); p->toRight += curWidth; p->toLeft -= seg->width; p->toMiddle += (seg->width + curWidth) / 2.0f; if ((p->toRight < 0) && (sseg->rside != NULL)) { p->toLeft -= curWidth; p->toMiddle += curWidth / 2.0f; seg = sseg; sseg = seg->rside; curWidth = RtTrackGetWidth(sseg, p->toStart); p->seg = sseg; p->toRight += curWidth; p->toMiddle += curWidth / 2.0f; } } else if ((p->toLeft < 0) && (seg->lside != NULL)) { sseg = seg->lside; p->seg = sseg; curWidth = RtTrackGetWidth(sseg, p->toStart); p->toRight += -seg->width; p->toMiddle -= (seg->width + curWidth) / 2.0f; p->toLeft += curWidth; if ((p->toLeft < 0) && (sseg->lside != NULL)) { p->toRight -= curWidth; p->toMiddle -= curWidth / 2.0f; seg = sseg; sseg = seg->lside; curWidth = RtTrackGetWidth(sseg, p->toStart); p->seg = sseg; p->toMiddle -= curWidth / 2.0f; p->toLeft += curWidth; } } } }
// Steer filter for collision avoidance. float Driver::filterSColl(float steer) { int i; float sidedist = 0.0f, fsidedist = 0.0f, minsidedist = FLT_MAX; Opponent *o = NULL; // Get the index of the nearest car (o). for (i = 0; i < opponents->getNOpponents(); i++) { if (opponent[i].getState() & OPP_SIDE) { sidedist = opponent[i].getSideDist(); fsidedist = fabs(sidedist); if (fsidedist < minsidedist) { minsidedist = fsidedist; o = &opponent[i]; } } } // If there is another car handle the situation. if (o != NULL) { float d = fsidedist - o->getWidth(); // Near, so we need to look at it. if (d < SIDECOLL_MARGIN) { /* compute angle between cars */ tCarElt *ocar = o->getCarPtr(); float diffangle = ocar->_yaw - car->_yaw; FLOAT_NORM_PI_PI(diffangle); // We are near and heading toward the car. if (diffangle*o->getSideDist() < 0.0f) { const float c = SIDECOLL_MARGIN/2.0f; d = d - c; if (d < 0.0f) { d = 0.0f; } // Steer delta required to drive parallel to the opponent. float psteer = diffangle/car->_steerLock; myoffset = car->_trkPos.toMiddle; // Limit myoffset to suitable limits. float w = o->getCarPtr()->_trkPos.seg->width/WIDTHDIV-BORDER_OVERTAKE_MARGIN; if (fabs(myoffset) > w) { myoffset = (myoffset > 0.0f) ? w : -w; } // On straights the car near to the middle can correct more, in turns the car inside // the turn does (because if you leave the track on the turn "inside" you will skid // back to the track. if (car->_trkPos.seg->type == TR_STR) { if (fabs(car->_trkPos.toMiddle) > fabs(ocar->_trkPos.toMiddle)) { // Its me, I do correct not that much. psteer = steer*(d/c) + 1.5f*psteer*(1.0f - d/c); } else { // Its the opponent, so I correct more. psteer = steer*(d/c) + 2.0f*psteer*(1.0f - d/c); } } else { // Who is outside, heavy corrections are less dangerous // if you drive near the middle of the track. float outside = car->_trkPos.toMiddle - ocar->_trkPos.toMiddle; float sign = (car->_trkPos.seg->type == TR_RGT) ? 1.0f : -1.0f; if (outside*sign > 0.0f) { psteer = steer*(d/c) + 1.5f*psteer*(1.0f - d/c); } else { psteer = steer*(d/c) + 2.0f*psteer*(1.0f - d/c); } } if (psteer*steer > 0.0f && fabs(steer) > fabs(psteer)) { return steer; } else { return psteer; } } } } return steer; }
static void RemoveCar(tCar *car, tSituation *s) { int i; tCarElt *carElt; tTrkLocPos trkPos; int trkFlag; tdble travelTime; tdble dang; static tdble PULL_Z_OFFSET = 3.0; static tdble PULL_SPD = 0.5; carElt = car->carElt; if (carElt->_state & RM_CAR_STATE_PULLUP) { carElt->_pos_Z += car->restPos.vel.z * SimDeltaTime; carElt->_yaw += car->restPos.vel.az * SimDeltaTime; carElt->_roll += car->restPos.vel.ax * SimDeltaTime; carElt->_pitch += car->restPos.vel.ay * SimDeltaTime; sgMakeCoordMat4(carElt->pub.posMat, carElt->_pos_X, carElt->_pos_Y, carElt->_pos_Z - carElt->_statGC_z, (float) RAD2DEG(carElt->_yaw), (float) RAD2DEG(carElt->_roll), (float) RAD2DEG(carElt->_pitch)); if (carElt->_pos_Z > (car->restPos.pos.z + PULL_Z_OFFSET)) { carElt->_state &= ~RM_CAR_STATE_PULLUP; carElt->_state |= RM_CAR_STATE_PULLSIDE; // Moved pullside velocity computation down due to floating point error accumulation. } return; } if (carElt->_state & RM_CAR_STATE_PULLSIDE) { // Recompute speed to avoid missing the parking point due to error accumulation (the pos might be // in the 0-10000 range, depending on the track and vel*dt is around 0-0.001, so basically all // but the most significant digits are lost under bad conditions, happens e.g on e-track-4). // Should not lead to a division by zero because the pullside process stops if the car is within // [0.5, 0.5]. Do not move it back. travelTime = DIST(car->restPos.pos.x, car->restPos.pos.y, carElt->_pos_X, carElt->_pos_Y) / PULL_SPD; car->restPos.vel.x = (car->restPos.pos.x - carElt->_pos_X) / travelTime; car->restPos.vel.y = (car->restPos.pos.y - carElt->_pos_Y) / travelTime; carElt->_pos_X += car->restPos.vel.x * SimDeltaTime; carElt->_pos_Y += car->restPos.vel.y * SimDeltaTime; sgMakeCoordMat4(carElt->pub.posMat, carElt->_pos_X, carElt->_pos_Y, carElt->_pos_Z - carElt->_statGC_z, (float) RAD2DEG(carElt->_yaw), (float) RAD2DEG(carElt->_roll), (float) RAD2DEG(carElt->_pitch)); if ((fabs(car->restPos.pos.x - carElt->_pos_X) < 0.5) && (fabs(car->restPos.pos.y - carElt->_pos_Y) < 0.5)) { carElt->_state &= ~RM_CAR_STATE_PULLSIDE; carElt->_state |= RM_CAR_STATE_PULLDN; } return; } if (carElt->_state & RM_CAR_STATE_PULLDN) { carElt->_pos_Z -= car->restPos.vel.z * SimDeltaTime; sgMakeCoordMat4(carElt->pub.posMat, carElt->_pos_X, carElt->_pos_Y, carElt->_pos_Z - carElt->_statGC_z, (float) RAD2DEG(carElt->_yaw), (float) RAD2DEG(carElt->_roll), (float) RAD2DEG(carElt->_pitch)); if (carElt->_pos_Z < car->restPos.pos.z) { carElt->_state &= ~RM_CAR_STATE_PULLDN; carElt->_state |= RM_CAR_STATE_OUT; } return; } if (carElt->_state & (RM_CAR_STATE_NO_SIMU & ~RM_CAR_STATE_PIT)) { return; } if (carElt->_state & RM_CAR_STATE_PIT) { if ((s->_maxDammage) && (car->dammage > s->_maxDammage)) { // Broken during pit stop. carElt->_state &= ~RM_CAR_STATE_PIT; carElt->_pit->pitCarIndex = TR_PIT_STATE_FREE; } else { return; } } if ((s->_maxDammage) && (car->dammage > s->_maxDammage)) { carElt->_state |= RM_CAR_STATE_BROKEN; } else { carElt->_state |= RM_CAR_STATE_OUTOFGAS; } carElt->_gear = car->transmission.gearbox.gear = 0; carElt->_enginerpm = car->engine.rads = 0; if (!(carElt->_state & RM_CAR_STATE_DNF)) { if (fabs(carElt->_speed_x) > 1.0) { return; } } carElt->_state |= RM_CAR_STATE_PULLUP; // RM_CAR_STATE_NO_SIMU evaluates to > 0 from here, so we remove the car from the // collision detection. SimCollideRemoveCar(car, s->_ncars); carElt->priv.collision = car->collision = 0; for(i = 0; i < 4; i++) { carElt->_skid[i] = 0; carElt->_wheelSpinVel(i) = 0; carElt->_brakeTemp(i) = 0; } carElt->pub.DynGC = car->DynGC; carElt->_speed_x = 0; // Compute the target zone for the wrecked car. trkPos = car->trkPos; if (trkPos.toRight > trkPos.seg->width / 2.0) { while (trkPos.seg->lside != 0) { trkPos.seg = trkPos.seg->lside; } trkPos.toLeft = -3.0; trkFlag = TR_TOLEFT; } else { while (trkPos.seg->rside != 0) { trkPos.seg = trkPos.seg->rside; } trkPos.toRight = -3.0; trkFlag = TR_TORIGHT; } trkPos.type = TR_LPOS_SEGMENT; RtTrackLocal2Global(&trkPos, &(car->restPos.pos.x), &(car->restPos.pos.y), trkFlag); car->restPos.pos.z = RtTrackHeightL(&trkPos) + carElt->_statGC_z; car->restPos.pos.az = RtTrackSideTgAngleL(&trkPos); car->restPos.pos.ax = 0; car->restPos.pos.ay = 0; car->restPos.vel.z = PULL_SPD; travelTime = (car->restPos.pos.z + PULL_Z_OFFSET - carElt->_pos_Z) / car->restPos.vel.z; dang = car->restPos.pos.az - carElt->_yaw; FLOAT_NORM_PI_PI(dang); car->restPos.vel.az = dang / travelTime; dang = car->restPos.pos.ax - carElt->_roll; FLOAT_NORM_PI_PI(dang); car->restPos.vel.ax = dang / travelTime; dang = car->restPos.pos.ay - carElt->_pitch; FLOAT_NORM_PI_PI(dang); car->restPos.vel.ay = dang / travelTime; }