//====================================================================== // Vehicle Physics/Simulation #include "g_local.h" #include "veh_defs.h" /* Veh_ProcessFrame Handles everything from input acceleration/braking, through world physics and dynamics. */ void Veh_ProcessFrame( edict_t *ent, usercmd_t *ucmd, pmove_t *pm ) { vehicle_t *vehicle; // find the vehicle structure if (ent->vehicle_index) { vehicle = &global_vehicles[ent->vehicle_index - 1]; } else // need to assign them one { int i; for (i=0; i< MAX_VEHICLES; i++) { if (!global_vehicles[i].driver) { vehicle = &global_vehicles[i]; ent->vehicle_index = i + 1; vehicle->driver = ent; // this will eventually be setup inside the map, but for now just do it here vehicle->def = &vehicle_defines[0]; Veh_InitVehicle( vehicle ); } } } VectorCopy( vehicle->origin, vehicle->oldorigin ); VectorCopy( vehicle->angles, vehicle->oldangles ); // Process the controls Veh_ProcessControls( vehicle, ucmd ); // Set steering avelocity Veh_SetSteeringAngleVelocity( vehicle ); // Do Angular Velocities Veh_PerformAngleVelocity( vehicle, 0.001 * ((float)ucmd->msec), pm ); // Move the vehicle according to it's velocity Veh_PerformMove( vehicle, pm ); // Examine movement, adjusting velocity accordingly // Set the position of the owner to the middle of the front and rear positions, with angles: // PITCH: according to front/rear positions // ROLL: use interpolated roll angles from downward traces at each wheel(??) // Show the vehicle Veh_PositionModels( vehicle ); } /* ================== Veh_InitVehicle Initializes a vehicle ready for placing into the game ================== */ void Veh_InitVehicle ( vehicle_t *vehicle ) { vehicle->front_traction = 1.0; vehicle->rear_traction = 1.0; vehicle->front_weight_ratio = 0.5; vehicle->front_onground = true; vehicle->rear_onground = true; VectorClear( vehicle->velocity ); VectorClear( vehicle->avelocity ); VectorCopy( vehicle->driver->s.origin, vehicle->origin ); VectorCopy( vehicle->driver->s.angles, vehicle->angles ); vehicle->gear = 1; // neutral vehicle->rpm = 0; vehicle->throttle = 0; vehicle->steer_yaw = 0; vehicle->fl = G_Spawn(); vehicle->fl->s.modelindex = gi.modelindex( vehicle->def->wheel_model ); vehicle->fr = G_Spawn(); vehicle->fr->s.modelindex = gi.modelindex( vehicle->def->wheel_model ); vehicle->rl = G_Spawn(); vehicle->rl->s.modelindex = gi.modelindex( vehicle->def->wheel_model ); vehicle->rr = G_Spawn(); vehicle->rr->s.modelindex = gi.modelindex( vehicle->def->wheel_model ); } /* ===================== Veh_ProcessControls Process all inputs and adjust velocities and traction levels ===================== */ void Veh_ProcessControls ( vehicle_t *vehicle, usercmd_t *ucmd ) { #define THROTTLE_ACCELERATION 20000.0 // increase this amount per second #define THROTTLE_DECCELERATION 20000.0 #define STEERING_ACCELERATION 75.0 float throttle_add, throttle_ideal, throttle_max, throttle_rate; float steering_ideal, steering_add, steering_max, steer_rate; vec3_t forward, unit_vel; float frametime; float current_speed; float new_rpm; float wheel_torque, max_wheel_torque; float ideal_speed, rear_speed; float accel_force = 0; // force applied by ground float accel_speed = 0; // transferred velocity increase to vehicle AngleVectors( vehicle->angles, forward, NULL, NULL ); if (vehicle->velocity[0] || vehicle->velocity[1] || vehicle->velocity[2]) { current_speed = VectorNormalize2( vehicle->velocity, unit_vel ); } else { VectorCopy( forward, unit_vel ); current_speed = 0; } frametime = (0.001 * ucmd->msec); // adjust throttle throttle_ideal = ((float)(ucmd->forwardmove) / 400.0) * (THROTTLE_MAX - IDLE_THROTTLE) + IDLE_THROTTLE; if (throttle_ideal < 0) throttle_ideal = 0; // TODO: this would be brakes throttle_add = throttle_ideal - vehicle->throttle; if ((throttle_add < 0) && (ucmd->forwardmove >= 0) && (vehicle->gear == 1)) throttle_rate = THROTTLE_DECCELERATION; else throttle_rate = THROTTLE_ACCELERATION; if (fabs( throttle_add ) > (throttle_max = (throttle_rate * frametime) ) ) { if (throttle_add < 0) throttle_add = -throttle_max; else throttle_add = throttle_max; } vehicle->throttle = vehicle->throttle + throttle_add; Veh_Debug( "Thr=%4i ", (int)vehicle->throttle ); // adjust steering steering_ideal = -((float)(ucmd->sidemove) / 400.0) * vehicle->def->max_steer * (0.5 + 0.5 * ((VEH_MAX_SPEED - current_speed) / VEH_MAX_SPEED)); // turn faster if changing directions steer_rate = STEERING_ACCELERATION; if (steering_ideal == 0) steer_rate *= 2; if (steering_ideal < 0) { if (vehicle->steer_yaw > 0) steer_rate *= 2; } else { if (vehicle->steer_yaw < 0) steer_rate *= 2; } steering_add = steering_ideal - vehicle->steer_yaw; if (fabs( steering_add ) > (steering_max = (steer_rate * frametime) ) ) { if (steering_add < 0) steering_add = -steering_max; else steering_add = steering_max; } vehicle->steer_yaw += steering_add; Veh_Debug( "Str=%2i ", (int)vehicle->steer_yaw ); Veh_Debug( "G=%s ", vehicle->def->gearbox->gears[vehicle->gear].name ); // Do engine acceleration // if (vehicle->throttle > vehicle->rpm) { // Get the new engine RPM, which is effected by the current throttle, and drivetrain resistance if (vehicle->def->gearbox->gears[vehicle->gear].ratio) // current gear isn't neutral { // Get the "force" applied by the acceleration on the drivetrain // We use this to determine whether or not the rear wheels will hold traction // get the amount of torque applied at the rear wheels wheel_torque = Veh_WheelTorqueAtRPM( vehicle, vehicle->throttle - vehicle->rpm ); Veh_Debug( "Tw=%i ", (int)wheel_torque ); // get the maximum amount of torque the rear wheels will take max_wheel_torque = (1.0 - vehicle->front_weight_ratio) * (vehicle->def->weight * KG_TO_LBS) * ((vehicle->rear_traction + 1.0) / 2.0); Veh_Debug( "TwM=%i ", (int)max_wheel_torque ); // get the speed we should be doing at the new rpm ideal_speed = Veh_SpeedAtRPM( vehicle, vehicle->throttle ); Veh_Debug( "iSpd=%i ", (int)ideal_speed ); rear_speed = current_speed; if (rear_speed > 0) // rear_speed *= ((fabs(DotProduct( forward, unit_vel )) + 1.0) / 2.0); rear_speed *= DotProduct( forward, unit_vel ); Veh_Debug( "cSpd=%i ", (int)rear_speed ); if ( ((ideal_speed > 0) && (ideal_speed > rear_speed)) || ((ideal_speed < 0) && (ideal_speed < rear_speed))) { if (wheel_torque > max_wheel_torque) { vehicle->rear_traction -= (wheel_torque / max_wheel_torque) * frametime; } else if (vehicle->rear_traction < 1.0) { vehicle->rear_traction += ((max_wheel_torque - wheel_torque) / max_wheel_torque) * frametime * 7.0; if (vehicle->rear_traction > 1.0) vehicle->rear_traction = 1.0; } // cap min traction (TODO: use current surface as factor of minimum) if (vehicle->rear_traction < 0.1) // HACK vehicle->rear_traction = 0.1; // get the amount of force applied to the vehicle at the new throttle accel_force = (wheel_torque) * LBS_TO_KG * FT_TO_CM * CM_TO_UNITS * vehicle->rear_traction * 2.0 // HACK, fudge factor / frametime; // convert to force per second if (ideal_speed < 0) accel_force *= -1; } else // slowing down { // if we're going faster than the maximum revs of the engine will take us if ( Veh_SpeedAtRPM( vehicle, THROTTLE_MAX ) < rear_speed ) { // lose traction, but still grip vehicle->rear_traction -= 2 * frametime; // ??? // cap min traction (TODO: use current surface as factor of minimum) if (vehicle->rear_traction < 0.5) // HACK vehicle->rear_traction = 0.5; } else if (vehicle->rear_traction < 1.0) { // regain traction vehicle->rear_traction += 5 * frametime; // ??? if (vehicle->rear_traction > 1.0) vehicle->rear_traction = 1.0; } // Decompression braking! wheel_torque = Veh_WheelTorqueAtRPM( vehicle, vehicle->rpm ); accel_force = (wheel_torque) * LBS_TO_KG * -0.5 * FT_TO_CM * CM_TO_UNITS * vehicle->rear_traction / frametime; // convert to force per second } Veh_Debug( "Tr=%1.2f ", vehicle->rear_traction ); Veh_Debug( "Af=%i ", (int)accel_force ); accel_speed = accel_force / vehicle->def->weight; // factor in rolling resistance accel_speed -= current_speed * 0.0007656; // factor in drag resistance accel_speed -= (0.5 * 0.3 * 20 * 0.00025 * ((current_speed*UNITS_TO_CM*CM_TO_FT) * (current_speed*UNITS_TO_CM*CM_TO_FT))) * FT_TO_CM * CM_TO_UNITS * 0.05; // Fudge factor Veh_Debug( "As=%i ", (int)accel_speed ); } } // Calculate the transferred velocity to the vehicle VectorMA( vehicle->velocity, accel_speed * frametime, forward, vehicle->velocity ); current_speed = VectorLength( vehicle->velocity ); Veh_Debug( "Spd=%i ", (int)current_speed ); // get the new engine RPM if (vehicle->gear != 1) { rear_speed = current_speed; if (rear_speed > 0) rear_speed *= ((fabs(DotProduct( forward, unit_vel )) + 1.0) / 2.0); new_rpm = Veh_RPMatSpeed( vehicle, rear_speed ); if (new_rpm > THROTTLE_MAX) new_rpm = THROTTLE_MAX; if (vehicle->rear_traction >= 1) { vehicle->rpm = new_rpm; if (vehicle->rear_traction > 1.0) vehicle->rear_traction = 1.0; } else // use new_rpm, but adjust for throttle { vehicle->rpm = new_rpm + (vehicle->throttle - new_rpm) * (1.0 - vehicle->rear_traction); } } else { vehicle->rpm = vehicle->throttle; } Veh_Debug( "Erpm=%4.0f ", vehicle->rpm ); Veh_Debug( "\n" ); } /* ================= Veh_Debug ================= */ void Veh_Debug( char *fmt, ...) { int len; va_list argptr; char bigbuffer[0x10000]; va_start (argptr,fmt); len = vsprintf (bigbuffer,fmt,argptr); va_end (argptr); gi.dprintf( bigbuffer ); } /* ================= Veh_SpeedAtRPM ================= */ float Veh_SpeedAtRPM( vehicle_t *veh, float rpm ) { float speed; speed = (rpm) * (1.0 / veh->def->gearbox->gears[veh->gear].ratio) * (1.0 / veh->def->diff_ratio) * (M_PI * veh->def->wheels->radius * 2 * CM_TO_UNITS) * MIN_PER_SEC; return speed; } /* ================= Veh_RPMatSpeed ================= */ float Veh_RPMatSpeed( vehicle_t *veh, float speed ) { float rpm; rpm = (speed) * (veh->def->gearbox->gears[veh->gear].ratio) * (veh->def->diff_ratio) * (1.0 / (M_PI * veh->def->wheels->radius * 2 * CM_TO_UNITS)) * SEC_PER_MIN; if (rpm < 0) // reversing rpm = -rpm; return rpm; } /* ================= Veh_SpeedAtRPM ================= */ float Veh_WheelTorqueAtRPM( vehicle_t *veh, float rpm ) { float torque; torque = (veh->def->engine_power * 1.5) // translate roughly to foot-pounds of engine torque * (rpm / THROTTLE_MAX) * (veh->def->gearbox->gears[veh->gear].ratio) * (veh->def->gearbox->gears[veh->gear].ratio) // Fudge factor * (veh->def->diff_ratio) * (1.0 / (veh->def->wheels->radius * CM_TO_FT)); return torque; } /* ================= Veh_PerformMove ================= */ void Veh_PerformMove( vehicle_t *vehicle, pmove_t *pm ) { int i; gclient_t *client; edict_t *ent; ent = vehicle->driver; client = vehicle->driver->client; for (i=0 ; i<3 ; i++) { pm->s.origin[i] = vehicle->origin[i]*8; pm->s.velocity[i] = vehicle->velocity[i]*8; } // perform a pmove gi.Pmove (pm); // save results of pmove client->ps.pmove = pm->s; client->old_pmove = pm->s; for (i=0 ; i<3 ; i++) { vehicle->origin[i] = pm->s.origin[i]*0.125; vehicle->velocity[i] = pm->s.velocity[i]*0.125; } // VectorCopy (pm->mins, ent->mins); // VectorCopy (pm->maxs, ent->maxs); // client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); // client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); // client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); VectorCopy( vehicle->angles, client->ps.viewangles ); VectorCopy( vehicle->angles, client->v_angle ); client->ps.pmove.pm_type = PM_FREEZE; ent->viewheight = pm->viewheight; ent->waterlevel = pm->waterlevel; ent->watertype = pm->watertype; ent->groundentity = pm->groundentity; if (pm->groundentity) ent->groundentity_linkcount = pm->groundentity->linkcount; VectorCopy( vehicle->origin, ent->s.origin ); } void Veh_PerformAngleVelocity( vehicle_t *vehicle, float frametime, pmove_t *pm ) { float speed; vec3_t unitvel, forward, up; VectorMA( vehicle->angles, frametime, vehicle->avelocity, vehicle->angles ); // adjust velocity if (vehicle->velocity[0] || vehicle->velocity[1] || vehicle->velocity[2]) { AngleVectors( vehicle->angles, forward, NULL, up ); speed = VectorNormalize2( vehicle->velocity, unitvel ); VectorScale( forward, speed * (vehicle->rear_traction + vehicle->front_traction) / 2.0, vehicle->velocity ); VectorMA( vehicle->velocity, speed * (1.0 - ((vehicle->rear_traction + vehicle->front_traction) / 2.0)), unitvel, vehicle->velocity ); } // According to traction levels, we should move the CG (center of gravity), such that: // // * Under FULL traction of the REAR wheels, rotate about the rear axle center // * Under ZERO traction of the REAR wheels, rotate about the front axle center // Just calculate this as a movement of the center of the vehicle / second, and add to velocity if (vehicle->avelocity[YAW]) { vec3_t dst, pnt, rear_pnt, move; vec3_t velback; VectorMA( vehicle->origin, (-vehicle->def->wheelspan_length*0.5 + (vehicle->def->wheelspan_length * (1.0 - vehicle->rear_traction) * (vehicle->front_traction*0.5 + 0.5))), forward, rear_pnt ); VectorSubtract( vehicle->origin, rear_pnt, pnt ); RotatePointAroundVector( dst, up, pnt, vehicle->avelocity[YAW] * frametime ); VectorSubtract( dst, pnt, move ); // convert to per second, since it'll get converted back in Pmove(); VectorScale( move, 0.3 / frametime, move ); // 0.3 is a HACK, should be 1.0 // save the velocity VectorCopy( vehicle->velocity, velback ); VectorCopy( move, vehicle->velocity ); // move the vehicle to account for the change in CG Veh_PerformMove( vehicle, pm ); // set the velocity back VectorCopy( velback, vehicle->velocity ); Veh_Debug( "YawAdjust=%3.0f ", VectorLength(move) ); } } void Veh_SetSteeringAngleVelocity( vehicle_t *vehicle ) { float speed, dot, yaw_change; vec3_t forward, unitvel; AngleVectors( vehicle->angles, forward, NULL, NULL ); if (vehicle->velocity[0] || vehicle->velocity[1] || vehicle->velocity[2]) { speed = VectorNormalize2( vehicle->velocity, unitvel ); yaw_change = ( (vehicle->steer_yaw) * (speed / (vehicle->def->wheelspan_length * CM_TO_UNITS)) * (vehicle->front_traction)) - vehicle->avelocity[YAW]; dot = DotProduct( forward, unitvel ); yaw_change *= dot; // this works for reverse, since yaw is just the opposite } else { speed = 0; VectorClear( unitvel ); yaw_change = -vehicle->avelocity[YAW] * vehicle->front_traction; } vehicle->avelocity[YAW] += yaw_change; Veh_Debug( "YawVel=%3.0f\n", vehicle->avelocity[YAW] ); } void Veh_HoldRearWheels( vehicle_t *vehicle ) { vec3_t old_rear, new_rear; vec3_t old_fwd, new_fwd; vec3_t ideal_rear, ideal_vec, end_rear; vec3_t new_vec, old_vec; AngleVectors( vehicle->oldangles, old_fwd, NULL, NULL ); VectorMA( vehicle->oldorigin, -vehicle->def->wheelspan_length * 0.5, old_fwd, old_rear ); AngleVectors( vehicle->angles, new_fwd, NULL, NULL ); VectorMA( vehicle->origin, -vehicle->def->wheelspan_length * 0.5, new_fwd, new_rear ); VectorSubtract( old_rear, vehicle->origin, old_vec ); VectorNormalize( old_vec ); VectorMA( vehicle->origin, vehicle->def->wheelspan_length * 0.5, old_vec, ideal_rear ); // now move to it depending on traction VectorSubtract( ideal_rear, new_rear, ideal_vec ); VectorMA( new_rear, vehicle->rear_traction, ideal_vec, end_rear ); VectorSubtract( vehicle->origin, end_rear, new_vec ); VectorNormalize( new_vec ); vehicle->angles[YAW] = vectoyaw( new_vec ); } // this is hacked together just for debugging void Veh_PositionModels( vehicle_t *vehicle ) { vec3_t fwd, right, up; AngleVectors( vehicle->angles, fwd, right, up ); VectorMA( vehicle->origin, 0.5 * CM_TO_UNITS * vehicle->def->wheelspan_length, fwd, vehicle->fl->s.origin ); VectorMA( vehicle->fl->s.origin, -0.5 * CM_TO_UNITS * vehicle->def->wheelspan_width, right, vehicle->fl->s.origin ); VectorMA( vehicle->fl->s.origin, -24 + CM_TO_UNITS * vehicle->def->wheels->radius, up, vehicle->fl->s.origin ); VectorCopy( vehicle->angles, vehicle->fl->s.angles ); vehicle->fl->s.angles[YAW] += vehicle->steer_yaw; gi.linkentity( vehicle->fl ); VectorMA( vehicle->origin, 0.5 * CM_TO_UNITS * vehicle->def->wheelspan_length, fwd, vehicle->fr->s.origin ); VectorMA( vehicle->fr->s.origin, 0.5 * CM_TO_UNITS * vehicle->def->wheelspan_width, right, vehicle->fr->s.origin ); VectorMA( vehicle->fr->s.origin, -24 + CM_TO_UNITS * vehicle->def->wheels->radius, up, vehicle->fr->s.origin ); VectorCopy( vehicle->angles, vehicle->fr->s.angles ); vehicle->fr->s.angles[YAW] += vehicle->steer_yaw; gi.linkentity( vehicle->fr ); VectorMA( vehicle->origin, -0.5 * CM_TO_UNITS * vehicle->def->wheelspan_length, fwd, vehicle->rl->s.origin ); VectorMA( vehicle->rl->s.origin, -0.5 * CM_TO_UNITS * vehicle->def->wheelspan_width, right, vehicle->rl->s.origin ); VectorMA( vehicle->rl->s.origin, -24 + CM_TO_UNITS * vehicle->def->wheels->radius, up, vehicle->rl->s.origin ); VectorCopy( vehicle->angles, vehicle->rl->s.angles ); gi.linkentity( vehicle->rl ); VectorMA( vehicle->origin, -0.5 * CM_TO_UNITS * vehicle->def->wheelspan_length, fwd, vehicle->rr->s.origin ); VectorMA( vehicle->rr->s.origin, 0.5 * CM_TO_UNITS * vehicle->def->wheelspan_width, right, vehicle->rr->s.origin ); VectorMA( vehicle->rr->s.origin, -24 + CM_TO_UNITS * vehicle->def->wheels->radius, up, vehicle->rr->s.origin ); VectorCopy( vehicle->angles, vehicle->rr->s.angles ); gi.linkentity( vehicle->rr ); }