// ---------------------------------------------------------------------------- // update_reduced_damp_timestamp() // void update_reduced_damp_timestamp( physics_info *pi, float impulse ) { // Compute duration of reduced damp from present // Compare with current value and increase if greater, otherwise ignore int reduced_damp_decay_time; reduced_damp_decay_time = (int) (REDUCED_DAMP_TIME * impulse / (REDUCED_DAMP_VEL * pi->mass)); if ( reduced_damp_decay_time > REDUCED_DAMP_TIME ) reduced_damp_decay_time = REDUCED_DAMP_TIME; // Reset timestamp if larger than current (if any) if ( timestamp_valid( pi->reduced_damp_decay ) ) { int time_left = timestamp_until( pi->reduced_damp_decay ); if ( time_left > 0 ) { // increment old time, but apply cap int new_time = reduced_damp_decay_time + time_left; if ( new_time < REDUCED_DAMP_TIME ) { pi->reduced_damp_decay = timestamp( new_time ); } } else { pi->reduced_damp_decay = timestamp( reduced_damp_decay_time ); } } else { // set if not valid pi->reduced_damp_decay = timestamp( reduced_damp_decay_time ); } }
void crw_check_weapon( int weapon_num, int collide_next_check ) { float next_check_time; weapon *wp; wp = &Weapons[weapon_num]; // if this weapons life left > time before next collision, then we cannot remove it crw_status[WEAPON_INDEX(wp)] = CRW_IN_PAIR; next_check_time = ((float)(timestamp_until(collide_next_check)) / 1000.0f); if ( wp->lifeleft < next_check_time ) crw_status[WEAPON_INDEX(wp)] = CRW_CAN_DELETE; }
void physics_apply_whack(vec3d *impulse, vec3d *pos, physics_info *pi, matrix *orient, float mass) { vec3d local_torque, torque; // vec3d npos; // Detect null vector. if ((fl_abs(impulse->xyz.x) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.y) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.z) <= WHACK_LIMIT)) return; // first do the rotational velocity // calculate the torque on the body based on the point on the // object that was hit and the momentum being applied to the object vm_vec_crossprod(&torque, pos, impulse); vm_vec_rotate ( &local_torque, &torque, orient ); vec3d delta_rotvel; vm_vec_rotate( &delta_rotvel, &local_torque, &pi->I_body_inv ); vm_vec_scale ( &delta_rotvel, (float) ROTVEL_WHACK_CONST ); vm_vec_add2( &pi->rotvel, &delta_rotvel ); //mprintf(("Whack: %7.3f %7.3f %7.3f\n", pi->rotvel.xyz.x, pi->rotvel.xyz.y, pi->rotvel.xyz.z)); // instant whack on the velocity // reduce damping on all axes pi->flags |= PF_REDUCED_DAMP; update_reduced_damp_timestamp( pi, vm_vec_mag(impulse) ); // find time for shake from weapon to end int dtime = timestamp_until(pi->afterburner_decay); if (dtime < WEAPON_SHAKE_TIME) { pi->afterburner_decay = timestamp( WEAPON_SHAKE_TIME ); } // Goober5000 - pi->mass should probably be just mass, as specified in the header vm_vec_scale_add2( &pi->vel, impulse, 1.0f / mass ); if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) { // Get DaveA nprintf(("Physics", "speed reset in physics_apply_whack [speed: %f]\n", vm_vec_mag(&pi->vel))); vm_vec_normalize(&pi->vel); vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED); } vm_vec_rotate( &pi->prev_ramp_vel, &pi->vel, orient ); // set so velocity will ramp starting from current speed // ramped velocity is now affected by collision }
void physics_collide_whack( vec3d *impulse, vec3d *world_delta_rotvel, physics_info *pi, matrix *orient, bool is_landing ) { vec3d body_delta_rotvel; // Detect null vector. if ((fl_abs(impulse->xyz.x) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.y) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.z) <= WHACK_LIMIT)) return; vm_vec_rotate( &body_delta_rotvel, world_delta_rotvel, orient ); // vm_vec_scale( &body_delta_rotvel, (float) ROTVEL_COLLIDE_WHACK_CONST ); vm_vec_add2( &pi->rotvel, &body_delta_rotvel ); update_reduced_damp_timestamp( pi, vm_vec_mag(impulse) ); // find time for shake from weapon to end if (!is_landing) { int dtime = timestamp_until(pi->afterburner_decay); if (dtime < WEAPON_SHAKE_TIME) { pi->afterburner_decay = timestamp( WEAPON_SHAKE_TIME ); } } pi->flags |= PF_REDUCED_DAMP; vm_vec_scale_add2( &pi->vel, impulse, 1.0f / pi->mass ); // check that collision does not give ship too much speed // reset if too high if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) { // Get DaveA nprintf(("Physics", "speed reset in physics_collide_whack [speed: %f]\n", vm_vec_mag(&pi->vel))); vm_vec_normalize(&pi->vel); vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED); } vm_vec_rotate( &pi->prev_ramp_vel, &pi->vel, orient ); // set so velocity will ramp starting from current speed // ramped velocity is now affected by collision // rotate previous ramp velocity (in model coord) to be same as vel (in world coords) }
// function looks at the flying controls and the current velocity to determine a goal velocity // function determines velocity in object's reference frame and goal velocity in object's reference frame void physics_read_flying_controls( matrix * orient, physics_info * pi, control_info * ci, float sim_time, vec3d *wash_rot) { vec3d goal_vel; // goal velocity in local coords, *not* accounting for ramping of velcity float ramp_time_const; // time constant for velocity ramping // apply throttle, unless reverse thrusters are held down if (ci->forward != -1.0f) ci->forward += (ci->forward_cruise_percent / 100.0f); // give control input to cause rotation in engine wash extern int Wash_on; if ( wash_rot && Wash_on ) { ci->pitch += wash_rot->xyz.x; ci->bank += wash_rot->xyz.z; ci->heading += wash_rot->xyz.y; } if (ci->pitch > 1.0f ) ci->pitch = 1.0f; else if (ci->pitch < -1.0f ) ci->pitch = -1.0f; if (ci->vertical > 1.0f ) ci->vertical = 1.0f; else if (ci->vertical < -1.0f ) ci->vertical = -1.0f; if (ci->heading > 1.0f ) ci->heading = 1.0f; else if (ci->heading < -1.0f ) ci->heading = -1.0f; if (ci->sideways > 1.0f ) ci->sideways = 1.0f; else if (ci->sideways < -1.0f ) ci->sideways = -1.0f; if (ci->bank > 1.0f ) ci->bank = 1.0f; else if (ci->bank < -1.0f ) ci->bank = -1.0f; if ( pi->flags & PF_AFTERBURNER_ON ){ //SparK: modifield to accept reverse burners if (!(pi->afterburner_max_reverse_vel > 0.0f)){ ci->forward = 1.0f; } } if (ci->forward > 1.0f ) ci->forward = 1.0f; else if (ci->forward < -1.0f ) ci->forward = -1.0f; if (!Flight_controls_follow_eyepoint_orientation || (Player_obj == NULL) || (Player_obj->type != OBJ_SHIP)) { // Default behavior; eyepoint orientation has no effect on controls pi->desired_rotvel.xyz.x = ci->pitch * pi->max_rotvel.xyz.x; pi->desired_rotvel.xyz.y = ci->heading * pi->max_rotvel.xyz.y; } else { // Optional behavior; pitch and yaw are always relative to the eyepoint // orientation (excluding slew) vec3d tmp_vec, new_rotvel; matrix tmp_mat, eyemat, rotvelmat; ship_get_eye(&tmp_vec, &eyemat, Player_obj, false); vm_copy_transpose_matrix(&tmp_mat, &Player_obj->orient); vm_matrix_x_matrix(&rotvelmat, &tmp_mat, &eyemat); vm_vec_rotate(&new_rotvel, &pi->max_rotvel, &rotvelmat); vm_vec_unrotate(&tmp_vec, &pi->max_rotvel, &rotvelmat); new_rotvel.xyz.x = tmp_vec.xyz.x; new_rotvel.xyz.x = ci->pitch * new_rotvel.xyz.x; new_rotvel.xyz.y = ci->heading * new_rotvel.xyz.y; vm_vec_unrotate(&tmp_vec, &new_rotvel, &rotvelmat); pi->desired_rotvel = tmp_vec; } float delta_bank; #ifdef BANK_WHEN_TURN // To change direction of bank, negate the whole expression. // To increase magnitude of banking, decrease denominator. // Adam: The following statement is all the math for banking while turning. delta_bank = - (ci->heading * pi->max_rotvel.xyz.y) * pi->delta_bank_const; #else delta_bank = 0.0f; #endif pi->desired_rotvel.xyz.z = ci->bank * pi->max_rotvel.xyz.z + delta_bank; pi->forward_thrust = ci->forward; pi->vert_thrust = ci->vertical; //added these two in order to get side and forward thrusters pi->side_thrust = ci->sideways; //to glow brighter when the ship is moving in the right direction -Bobboau if ( pi->flags & PF_AFTERBURNER_ON ) { goal_vel.xyz.x = ci->sideways*pi->afterburner_max_vel.xyz.x; goal_vel.xyz.y = ci->vertical*pi->afterburner_max_vel.xyz.y; if(ci->forward < 0.0f) goal_vel.xyz.z = ci->forward* pi->afterburner_max_reverse_vel; else goal_vel.xyz.z = ci->forward* pi->afterburner_max_vel.xyz.z; } else if ( pi->flags & PF_BOOSTER_ON ) { goal_vel.xyz.x = ci->sideways*pi->booster_max_vel.xyz.x; goal_vel.xyz.y = ci->vertical*pi->booster_max_vel.xyz.y; goal_vel.xyz.z = ci->forward* pi->booster_max_vel.xyz.z; } else { goal_vel.xyz.x = ci->sideways*pi->max_vel.xyz.x; goal_vel.xyz.y = ci->vertical*pi->max_vel.xyz.y; goal_vel.xyz.z = ci->forward* pi->max_vel.xyz.z; } if ( goal_vel.xyz.z < -pi->max_rear_vel && !(pi->flags & PF_AFTERBURNER_ON) ) goal_vel.xyz.z = -pi->max_rear_vel; if ( pi->flags & PF_ACCELERATES ) { // // Determine *resultant* DESIRED VELOCITY (desired_vel) accounting for RAMPING of velocity // Use LOCAL coordinates // if slide_enabled, ramp velocity for x and y, otherwise set goal (0) // always ramp velocity for z // // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast. // Scale according to reduced_damp_time_expansion. float reduced_damp_ramp_time_expansion; if ( pi->flags & PF_REDUCED_DAMP && !timestamp_elapsed(pi->reduced_damp_decay) ) { float reduced_damp_fraction_time_left = timestamp_until( pi->reduced_damp_decay ) / (float) REDUCED_DAMP_TIME; reduced_damp_ramp_time_expansion = 1.0f + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left; } else { reduced_damp_ramp_time_expansion = 1.0f; } if (pi->flags & PF_SLIDE_ENABLED) { // determine the local velocity // deterimine whether accelerating or decleration toward goal for x if ( goal_vel.xyz.x > 0.0f ) { if ( goal_vel.xyz.x >= pi->prev_ramp_vel.xyz.x ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else if ( goal_vel.xyz.x < 0.0f ) { if ( goal_vel.xyz.x <= pi->prev_ramp_vel.xyz.x ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else { ramp_time_const = pi->slide_decel_time_const; } // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast if ( pi->flags & PF_REDUCED_DAMP ) { ramp_time_const *= reduced_damp_ramp_time_expansion; } pi->prev_ramp_vel.xyz.x = velocity_ramp(pi->prev_ramp_vel.xyz.x, goal_vel.xyz.x, ramp_time_const, sim_time); // deterimine whether accelerating or decleration toward goal for y if ( goal_vel.xyz.y > 0.0f ) { if ( goal_vel.xyz.y >= pi->prev_ramp_vel.xyz.y ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else if ( goal_vel.xyz.y < 0.0f ) { if ( goal_vel.xyz.y <= pi->prev_ramp_vel.xyz.y ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else { ramp_time_const = pi->slide_decel_time_const; } // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast if ( pi->flags & PF_REDUCED_DAMP ) { ramp_time_const *= reduced_damp_ramp_time_expansion; } pi->prev_ramp_vel.xyz.y = velocity_ramp( pi->prev_ramp_vel.xyz.y, goal_vel.xyz.y, ramp_time_const, sim_time); } else { // slide not enabled pi->prev_ramp_vel.xyz.x = 0.0f; pi->prev_ramp_vel.xyz.y = 0.0f; } // deterimine whether accelerating or decleration toward goal for z if ( goal_vel.xyz.z > 0.0f ) { if ( goal_vel.xyz.z >= pi->prev_ramp_vel.xyz.z ) { if ( pi->flags & PF_AFTERBURNER_ON ) ramp_time_const = pi->afterburner_forward_accel_time_const; else if (pi->flags & PF_BOOSTER_ON) ramp_time_const = pi->booster_forward_accel_time_const; else ramp_time_const = pi->forward_accel_time_const; } else { ramp_time_const = pi->forward_decel_time_const; } } else if ( goal_vel.xyz.z < 0.0f ) { if ( pi->flags & PF_AFTERBURNER_ON ) ramp_time_const = pi->afterburner_reverse_accel; else ramp_time_const = pi->forward_decel_time_const; } else { ramp_time_const = pi->forward_decel_time_const; } // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast if ( pi->flags & PF_REDUCED_DAMP ) { ramp_time_const *= reduced_damp_ramp_time_expansion; } pi->prev_ramp_vel.xyz.z = velocity_ramp(pi->prev_ramp_vel.xyz.z, goal_vel.xyz.z, ramp_time_const, sim_time); //Deternine the current dynamic glide cap, and ramp to it //This is outside the normal "glide" block since we want the cap to adjust whether or not the ship is in glide mode float dynamic_glide_cap_goal = 0.0; if (pi->flags & PF_AFTERBURNER_ON) { dynamic_glide_cap_goal = ( goal_vel.xyz.z >= 0.0f ) ? pi->afterburner_max_vel.xyz.z : pi->afterburner_max_reverse_vel; } else { //Use the maximum value in X, Y, and Z (including overclocking) dynamic_glide_cap_goal = MAX(MAX(pi->max_vel.xyz.x,pi->max_vel.xyz.y), pi->max_vel.xyz.z); } pi->cur_glide_cap = velocity_ramp(pi->cur_glide_cap, dynamic_glide_cap_goal, ramp_time_const, sim_time); if ( (pi->flags & PF_GLIDING) || (pi->flags & PF_FORCE_GLIDE ) ) { pi->desired_vel = pi->vel; //SUSHI: A (hopefully better) approach to dealing with accelerations in glide mode //Get *actual* current velocities along each axis and use those instead of ramped velocities vec3d local_vel; vm_vec_rotate(&local_vel, &pi->vel, orient); //Having pi->glide_cap == 0 means we're using a dynamic glide cap float curGlideCap = 0.0f; if (pi->glide_cap == 0.0f) curGlideCap = pi->cur_glide_cap; else curGlideCap = pi->glide_cap; //If we're near the (positive) glide cap, decay velocity where we aren't thrusting //This is a hack, but makes the flight feel a lot smoother //Don't do this if we aren't applying any thrust, we have no glide cap, or the accel multiplier is 0 (no thrust while gliding) float cap_decay_threshold = 0.95f; float cap_decay_amount = 0.2f; if (curGlideCap >= 0.0f && vm_vec_mag(&pi->desired_vel) >= cap_decay_threshold * curGlideCap && vm_vec_mag(&goal_vel) > 0.0f && pi->glide_accel_mult != 0.0f) { if (goal_vel.xyz.x == 0.0f) vm_vec_scale_add2(&pi->desired_vel, &orient->vec.rvec, -cap_decay_amount * local_vel.xyz.x); if (goal_vel.xyz.y == 0.0f) vm_vec_scale_add2(&pi->desired_vel, &orient->vec.uvec, -cap_decay_amount * local_vel.xyz.y); if (goal_vel.xyz.z == 0.0f) vm_vec_scale_add2(&pi->desired_vel, &orient->vec.fvec, -cap_decay_amount * local_vel.xyz.z); } //The glide_ramp function uses (basically) the same math as the velocity ramp so that thruster power is consistent //Only ramp if the glide cap is positive float xVal = glide_ramp(local_vel.xyz.x, goal_vel.xyz.x, pi->slide_accel_time_const, pi->glide_accel_mult, sim_time); float yVal = glide_ramp(local_vel.xyz.y, goal_vel.xyz.y, pi->slide_accel_time_const, pi->glide_accel_mult, sim_time); float zVal = 0.0; if (pi->flags & PF_AFTERBURNER_ON) zVal = glide_ramp(local_vel.xyz.z, goal_vel.xyz.z, pi->afterburner_forward_accel_time_const, pi->glide_accel_mult, sim_time); else { if (goal_vel.xyz.z >= 0.0f) zVal = glide_ramp(local_vel.xyz.z, goal_vel.xyz.z, pi->forward_accel_time_const, pi->glide_accel_mult, sim_time); else zVal = glide_ramp(local_vel.xyz.z, goal_vel.xyz.z, pi->forward_decel_time_const, pi->glide_accel_mult, sim_time); } //Compensate for effect of dampening: normal flight cheats here, so /we make up for it this way so glide acts the same way xVal *= pi->side_slip_time_const / sim_time; yVal *= pi->side_slip_time_const / sim_time; if (pi->use_newtonian_damp) zVal *= pi->side_slip_time_const / sim_time; vm_vec_scale_add2(&pi->desired_vel, &orient->vec.fvec, zVal); vm_vec_scale_add2(&pi->desired_vel, &orient->vec.rvec, xVal); vm_vec_scale_add2(&pi->desired_vel, &orient->vec.uvec, yVal); // Only do the glide cap if we have one and are actively thrusting in some direction. if ( curGlideCap >= 0.0f && (ci->forward != 0.0f || ci->sideways != 0.0f || ci->vertical != 0.0f) ) { float currentmag = vm_vec_mag(&pi->desired_vel); if ( currentmag > curGlideCap ) { vm_vec_scale( &pi->desired_vel, curGlideCap / currentmag ); } } } else { // this translates local desired velocities to world velocities vm_vec_zero(&pi->desired_vel); vm_vec_scale_add2( &pi->desired_vel, &orient->vec.rvec, pi->prev_ramp_vel.xyz.x ); vm_vec_scale_add2( &pi->desired_vel, &orient->vec.uvec, pi->prev_ramp_vel.xyz.y ); vm_vec_scale_add2( &pi->desired_vel, &orient->vec.fvec, pi->prev_ramp_vel.xyz.z ); } } else // object does not accelerate (PF_ACCELERATES not set) pi->desired_vel = pi->vel; }
// Adds velocity to position // finds velocity and displacement in local coords void physics_sim_vel(vec3d * position, physics_info * pi, float sim_time, matrix *orient) { vec3d local_disp; // displacement in this frame vec3d local_v_in; // velocity in local coords at the start of this frame vec3d local_desired_vel; // desired velocity in local coords vec3d local_v_out; // velocity in local coords following this frame vec3d damp; // Maybe clear the reduced_damp flag. // This fixes the problem of the player getting near-instantaneous acceleration under unknown circumstances. // The larger problem is probably that PF_USE_VEL is getting stuck set. if ((pi->flags & PF_REDUCED_DAMP) && (timestamp_elapsed(pi->reduced_damp_decay))) { pi->flags &= ~PF_REDUCED_DAMP; } // Set up damping constants based on special conditions // ie. shockwave, collision, weapon, dead if (pi->flags & PF_DEAD_DAMP) { // side_slip_time_const is already quite large and now needs to be applied in all directions vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, pi->side_slip_time_const ); } else if (pi->flags & PF_REDUCED_DAMP) { // case of shock, weapon, collide, etc. if ( timestamp_elapsed(pi->reduced_damp_decay) ) { vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, 0.0f ); } else { // damp is multiplied by fraction and not fraction^2, gives better collision separation float reduced_damp_fraction_time_left = timestamp_until( pi->reduced_damp_decay ) / (float) REDUCED_DAMP_TIME; damp.xyz.x = pi->side_slip_time_const * ( 1 + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left ); damp.xyz.y = pi->side_slip_time_const * ( 1 + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left ); damp.xyz.z = pi->side_slip_time_const * reduced_damp_fraction_time_left * REDUCED_DAMP_FACTOR; } } else { // regular damping if (pi->use_newtonian_damp) { vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, pi->side_slip_time_const ); } else { vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, 0.0f ); } } // Note: CANNOT maintain a *local velocity* since a rotation can occur in this frame. // thus the local velocity of in the next frame can be different (this would require rotate to change local vel // and this is not desired // get local components of current velocity vm_vec_rotate (&local_v_in, &pi->vel, orient); // get local components of desired velocity vm_vec_rotate (&local_desired_vel, &pi->desired_vel, orient); // find updated LOCAL velocity and position in the local x direction apply_physics (damp.xyz.x, local_desired_vel.xyz.x, local_v_in.xyz.x, sim_time, &local_v_out.xyz.x, &local_disp.xyz.x); // find updated LOCAL velocity and position in the local y direction apply_physics (damp.xyz.y, local_desired_vel.xyz.y, local_v_in.xyz.y, sim_time, &local_v_out.xyz.y, &local_disp.xyz.y); // find updated LOCAL velocity and position in the local z direction // for player ship, damp should normally be zero, but may be altered in a shockwave // in death, shockwave,etc. we want damping time const large for all 3 axes // warp in test - make excessive speed drop exponentially from max allowed // become (0.01x in 3 sec) int special_warp_in = FALSE; float excess = local_v_in.xyz.z - pi->max_vel.xyz.z; if (excess > 5 && (pi->flags & PF_SPECIAL_WARP_IN)) { special_warp_in = TRUE; float exp_factor = float(exp(-sim_time / SPECIAL_WARP_T_CONST)); local_v_out.xyz.z = pi->max_vel.xyz.z + excess * exp_factor; local_disp.xyz.z = (pi->max_vel.xyz.z * sim_time) + excess * (float(SPECIAL_WARP_T_CONST) * (1.0f - exp_factor)); } else if (pi->flags & PF_SPECIAL_WARP_OUT) { float exp_factor = float(exp(-sim_time / SPECIAL_WARP_T_CONST)); vec3d temp; vm_vec_rotate(&temp, &pi->prev_ramp_vel, orient); float deficeit = temp.xyz.z - local_v_in.xyz.z; local_v_out.xyz.z = local_v_in.xyz.z + deficeit * (1.0f - exp_factor); local_disp.xyz.z = (local_v_in.xyz.z * sim_time) + deficeit * (sim_time - (float(SPECIAL_WARP_T_CONST) * (1.0f - exp_factor))); } else { apply_physics (damp.xyz.z, local_desired_vel.xyz.z, local_v_in.xyz.z, sim_time, &local_v_out.xyz.z, &local_disp.xyz.z); } // maybe turn off special warp in flag if ((pi->flags & PF_SPECIAL_WARP_IN) && (excess < 5)) { pi->flags &= ~(PF_SPECIAL_WARP_IN); } // update world position from local to world coords using orient vec3d world_disp; vm_vec_unrotate (&world_disp, &local_disp, orient); vm_vec_add2 (position, &world_disp); // update world velocity vm_vec_unrotate(&pi->vel, &local_v_out, orient); if (special_warp_in) { vm_vec_rotate(&pi->prev_ramp_vel, &pi->vel, orient); } }
void physics_sim_rot(matrix * orient, physics_info * pi, float sim_time ) { angles tangles; vec3d new_vel; matrix tmp; float shock_amplitude; float rotdamp; float shock_fraction_time_left; Assert(is_valid_matrix(orient)); Assert(is_valid_vec(&pi->rotvel)); Assert(is_valid_vec(&pi->desired_rotvel)); // Handle special case of shockwave shock_amplitude = 0.0f; if ( pi->flags & PF_IN_SHOCKWAVE ) { if ( timestamp_elapsed(pi->shockwave_decay) ) { pi->flags &= ~PF_IN_SHOCKWAVE; rotdamp = pi->rotdamp; } else { shock_fraction_time_left = timestamp_until( pi->shockwave_decay ) / (float) SW_BLAST_DURATION; rotdamp = pi->rotdamp + pi->rotdamp * (SW_ROT_FACTOR - 1) * shock_fraction_time_left; shock_amplitude = pi->shockwave_shake_amp * shock_fraction_time_left; } } else { rotdamp = pi->rotdamp; } // Do rotational physics with given damping apply_physics( rotdamp, pi->desired_rotvel.xyz.x, pi->rotvel.xyz.x, sim_time, &new_vel.xyz.x, NULL ); apply_physics( rotdamp, pi->desired_rotvel.xyz.y, pi->rotvel.xyz.y, sim_time, &new_vel.xyz.y, NULL ); apply_physics( rotdamp, pi->desired_rotvel.xyz.z, pi->rotvel.xyz.z, sim_time, &new_vel.xyz.z, NULL ); Assert(is_valid_vec(&new_vel)); pi->rotvel = new_vel; tangles.p = pi->rotvel.xyz.x*sim_time; tangles.h = pi->rotvel.xyz.y*sim_time; tangles.b = pi->rotvel.xyz.z*sim_time; /* // Make ship shake due to afterburner. if (pi->flags & PF_AFTERBURNER_ON || !timestamp_elapsed(pi->afterburner_decay) ) { float max_speed; max_speed = vm_vec_mag_quick(&pi->max_vel); tangles.p += (float) (rand()-RAND_MAX_2) * RAND_MAX_1f * pi->speed/max_speed/64.0f; tangles.h += (float) (rand()-RAND_MAX_2) * RAND_MAX_1f * pi->speed/max_speed/64.0f; if ( pi->flags & PF_AFTERBURNER_ON ) { pi->afterburner_decay = timestamp(ABURN_DECAY_TIME); } } */ // Make ship shake due to shockwave, decreasing in amplitude at the end of the shockwave if ( pi->flags & PF_IN_SHOCKWAVE ) { tangles.p += (float) (myrand()-RAND_MAX_2) * RAND_MAX_1f * shock_amplitude; tangles.h += (float) (myrand()-RAND_MAX_2) * RAND_MAX_1f * shock_amplitude; } vm_angles_2_matrix(&pi->last_rotmat, &tangles ); vm_matrix_x_matrix( &tmp, orient, &pi->last_rotmat ); *orient = tmp; vm_orthogonalize_matrix(orient); }
void obj_check_all_collisions() { obj_pair *parent, *tmp; #ifdef PAIR_STATS // debug info float avg_time_to_next_check = 0.0f; #endif if (Cmdline_dis_collisions) return; if ( !(Game_detail_flags & DETAIL_FLAG_COLLISION) ) return; parent = &pair_used_list; tmp = parent->next; Num_pairs_checked = 0; while (tmp != NULL) { int removed = 0; if ( !timestamp_elapsed(tmp->next_check_time) ) goto NextPair; if ( (tmp->a) && (tmp->b) ) { Num_pairs_checked++; if ( (*tmp->check_collision)(tmp) ) { // We never need to check this pair again. #if 0 //def DONT_REMOVE_PAIRS // Never check it again, but keep the pair around // (useful for debugging) tmp->next_check_time = timestamp(-1); #else // Never check it again, so remove the pair removed = 1; tmp->a->num_pairs--; Assert( tmp->a->num_pairs > -1 ); tmp->b->num_pairs--; Assert( tmp->b->num_pairs > -1 ); Num_pairs--; // Assert(Num_pairs >= 0); parent->next = tmp->next; tmp->a = tmp->b = NULL; tmp->next = pair_free_list.next; pair_free_list.next = tmp; tmp = parent->next; #endif } } NextPair: if ( !removed ) { parent = tmp; tmp = tmp->next; #ifdef PAIR_STATS // debug info if (tmp) { int add_time = timestamp_until( tmp->next_check_time ); if (add_time > 0) avg_time_to_next_check += (float) add_time; } #endif } } MONITOR_INC(NumPairs, Num_pairs); MONITOR_INC(NumPairsChecked, Num_pairs_checked); #ifdef PAIR_STATS avg_time_to_next_check = avg_time_to_next_check / Num_pairs; extern int Num_hull_pieces; extern int Weapons_created; // mprintf(( "[pairs checked: %d, start_pairs: %d, num obj: %d, avg next time: %f]\n", n, org_pairs, Num_objects, avg_time_to_next_check )); // mprintf(( "[Num_hull_pieces: %3d, Num_weapons_created: %3d, pairs_not_created: %3d, pairs_created: %3d, percent new saved: %9.5f]\n", Num_hull_pieces, Weapons_created, pairs_not_created, Pairs_created, 100.0f*(float)pairs_not_created/(float)(pairs_not_created + Pairs_created) )); mprintf(( "[pairs_created: %3d, pairs_not_created: %3d, percent saved %6.3f]\n", Pairs_created, pairs_not_created, 100.0f*pairs_not_created/(Pairs_created+pairs_not_created) )); pairs_not_created = 0; Weapons_created = 0; Pairs_created = 0; #endif // What percent of the pairs did we check? // FYI: (n*(n-1))/2 is the total number of checks required for comparing n objects. // if ( org_pairs > 1 ) { // Object_checked_percentage = (i2fl(n)*100.0f) / i2fl(org_pairs); // } else { // Object_checked_percentage = 0.0f; // } }
// ---------------------------------------------------------------------------- // afterburners_update() // // Update the state of the afterburner fuel remaining for an object using the // afterburner. // // for the player ship, key_up_time() is called for the afterburner key to // detect when afterburners disengage. // // input: *objp => pointer to the object starting afterburners // fl_frametime => time in seconds of the last frame // void afterburners_update(object* objp, float fl_frametime) { Assert(objp != NULL); Assert(objp->type == OBJ_SHIP); Assert(objp->instance >= 0 && objp->instance < MAX_SHIPS); ship_info* sip; ship* shipp; static int volume_chg_timer = 1; shipp = &Ships[objp->instance]; Assert(shipp->ship_info_index >= 0 && shipp->ship_info_index < Num_ship_classes); sip = &Ship_info[shipp->ship_info_index]; if ((objp->flags & OF_PLAYER_SHIP) && (Game_mode & GM_DEAD)) { return; } if (!(sip->flags & SIF_AFTERBURNER)) { return; // nothing to update, afterburners are not even on the ship } //shut the afterburners off if we're using the booster tertiary //shut the afterburners off if we're in glide mode. if ((objp->phys_info.flags & PF_AFTERBURNER_ON) && ((objp->phys_info.flags & PF_BOOSTER_ON) || (objp->phys_info.flags & PF_GLIDING))) { if (objp == Player_obj) afterburner_stop_sounds(); afterburners_stop(objp); return; } if (objp == Player_obj) { if (!timestamp_elapsed(Player_disengage_timer)) { float remaining; remaining = timestamp_until(Player_disengage_timer) / i2fl(DISENGAGE_TIME); if (remaining <= 0) { afterburner_stop_sounds(); } else { if (remaining > 1.0f) { remaining = 1.0f; } snd_set_volume(Player_afterburner_loop_id, remaining * Player_afterburner_vol); } } else { if (Player_disengage_timer != 1) { afterburner_stop_sounds(); } } } // single player, multiplayer servers, and clients for their own ships if (!(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER || (objp == Player_obj)) { if (!(objp->phys_info.flags & PF_AFTERBURNER_ON)) { // Recover afterburner fuel if (shipp->afterburner_fuel < sip->afterburner_fuel_capacity) { float recharge_scale; recharge_scale = Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile-> afterburner_recharge_scale[Game_skill_level]; shipp->afterburner_fuel += (sip->afterburner_recover_rate * fl_frametime * recharge_scale); if (shipp->afterburner_fuel > sip->afterburner_fuel_capacity) { shipp->afterburner_fuel = sip->afterburner_fuel_capacity; } } return; } else { // Check if there is enough afterburner fuel if (shipp->afterburner_fuel <= 0) { shipp->afterburner_fuel = 0.0f; afterburners_stop(objp); return; } } // afterburners are firing at this point // Reduce the afterburner fuel shipp->afterburner_fuel -= (sip->afterburner_burn_rate * fl_frametime); if (shipp->afterburner_fuel < 0.0f) { shipp->afterburner_fuel = 0.0f; } } if (objp == Player_obj) { if ((Viewer_mode & VM_NOT_COCKPIT)) { // stop afterburner sound if it is playing if (Player_afterburner_loop_id != -1) { snd_stop(Player_afterburner_loop_id); Player_afterburner_loop_id = -1; } return; } if (timestamp_elapsed(Player_afterburner_loop_delay)) { Player_afterburner_vol = Snds[SND_ABURN_LOOP].default_volume; Player_afterburner_loop_delay = timestamp(50); if (Player_afterburner_loop_id == -1) { Player_afterburner_loop_id = snd_play_looping(&Snds[SND_ABURN_LOOP], 0.0f, -1, -1, Player_afterburner_vol); //snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol); // nprintf(("Alan","PLAY LOOPING SOUND\n")); } } // Reduce the volume of the afterburner sound if near the end if (timestamp_elapsed(volume_chg_timer)) { float percent_afterburner_left; percent_afterburner_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity; volume_chg_timer = timestamp(AFTERBURNER_VOLUME_UPDATE); if (percent_afterburner_left < AFTERBURNER_PERCENT_VOL_ATTENUATE) { Player_afterburner_vol = percent_afterburner_left * (1 / AFTERBURNER_PERCENT_VOL_ATTENUATE) * Snds[SND_ABURN_LOOP].default_volume; snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol); } } // end if (timestamp_elapsed(volume_chg_timer)) } }
/** * Update the state of the afterburner fuel remaining for an object using the afterburner. * * For the player ship, key_up_time() is called for the afterburner key to * detect when afterburners disengage. * * @param *objp pointer to the object starting afterburners * @param fl_frametime time in seconds of the last frame */ void afterburners_update(object *objp, float fl_frametime) { Assert( objp != NULL ); Assert( objp->type == OBJ_SHIP ); Assert( objp->instance >= 0 && objp->instance < MAX_SHIPS ); ship_info *sip; ship *shipp; static int volume_chg_timer = 1; shipp = &Ships[objp->instance]; Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < static_cast<int>(Ship_info.size()) ); sip = &Ship_info[shipp->ship_info_index]; if ( (objp->flags[Object::Object_Flags::Player_ship] ) && (Game_mode & GM_DEAD) ) { return; } if ( !(sip->flags[Ship::Info_Flags::Afterburner]) ) { return; // nothing to update, afterburners are not even on the ship } //shut the afterburners off if we're using the booster tertiary if ( objp->phys_info.flags & PF_BOOSTER_ON) { if (objp==Player_obj) afterburner_stop_sounds(); afterburners_stop(objp); return; } if ( objp == Player_obj ) { if ( !timestamp_elapsed(Player_disengage_timer) ) { float remaining; remaining = timestamp_until(Player_disengage_timer) / i2fl(DISENGAGE_TIME); if ( remaining <= 0 ) { afterburner_stop_sounds(); } else { snd_set_volume( Player_afterburner_loop_id, remaining*Player_afterburner_vol); } } else { if ( Player_disengage_timer != 1 ) { afterburner_stop_sounds(); } } } // single player, multiplayer servers, and clients for their own ships if(!(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER || (objp == Player_obj)) { if ( !(objp->phys_info.flags & PF_AFTERBURNER_ON) ) { // Recover afterburner fuel if ( shipp->afterburner_fuel < sip->afterburner_fuel_capacity ) { float recharge_scale; recharge_scale = Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level]; shipp->afterburner_fuel += (sip->afterburner_recover_rate * fl_frametime * recharge_scale); if ( shipp->afterburner_fuel > sip->afterburner_fuel_capacity){ shipp->afterburner_fuel = sip->afterburner_fuel_capacity; } } return; } else { // Check if there is enough afterburner fuel if ( shipp->afterburner_fuel <= 0 ) { shipp->afterburner_fuel = 0.0f; afterburners_stop(objp); return; } } // afterburners are firing at this point // Reduce the afterburner fuel shipp->afterburner_fuel -= (sip->afterburner_burn_rate * fl_frametime); if ( shipp->afterburner_fuel < 0.0f ) { shipp->afterburner_fuel = 0.0f; } } if ( objp == Player_obj ) { if ( timestamp_elapsed(Player_afterburner_loop_delay) ) { Player_afterburner_vol = AFTERBURNER_DEFAULT_VOL; Player_afterburner_loop_delay = 0; if ( Player_afterburner_loop_id == -1 ) { Player_afterburner_loop_id = snd_play_looping( gamesnd_get_game_sound(ship_get_sound(objp, GameSounds::ABURN_LOOP)), 0.0f , -1, -1); snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol); } } // Reduce the volume of the afterburner sound if near the end if ( timestamp_elapsed(volume_chg_timer) ) { float percent_afterburner_left; percent_afterburner_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity; volume_chg_timer = timestamp(AFTERBURNER_VOLUME_UPDATE); if ( percent_afterburner_left < AFTERBURNER_PERCENT_VOL_ATTENUATE ) { Player_afterburner_vol = percent_afterburner_left*(1/AFTERBURNER_PERCENT_VOL_ATTENUATE)*AFTERBURNER_DEFAULT_VOL; snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol); } } // end if (timestamp_elapsed(volume_chg_timer)) } }