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) }
void physics_apply_shock(vec3d *direction_vec, float pressure, physics_info *pi, matrix *orient, vec3d *min, vec3d *max, float radius) { vec3d normal; vec3d local_torque, temp_torque, torque; vec3d impact_vec; vec3d area; vec3d sin; if (radius > MAX_RADIUS) { return; } vm_vec_normalize_safe ( direction_vec ); area.xyz.x = (max->xyz.y - min->xyz.z) * (max->xyz.z - min->xyz.z); area.xyz.y = (max->xyz.x - min->xyz.x) * (max->xyz.z - min->xyz.z); area.xyz.z = (max->xyz.x - min->xyz.x) * (max->xyz.y - min->xyz.y); normal.xyz.x = vm_vec_dotprod( direction_vec, &orient->vec.rvec ); normal.xyz.y = vm_vec_dotprod( direction_vec, &orient->vec.uvec ); normal.xyz.z = vm_vec_dotprod( direction_vec, &orient->vec.fvec ); sin.xyz.x = fl_sqrt( fl_abs(1.0f - normal.xyz.x*normal.xyz.x) ); sin.xyz.y = fl_sqrt( fl_abs(1.0f - normal.xyz.y*normal.xyz.y) ); sin.xyz.z = fl_sqrt( fl_abs(1.0f - normal.xyz.z*normal.xyz.z) ); vm_vec_make( &torque, 0.0f, 0.0f, 0.0f ); // find the torque exerted due to the shockwave hitting each face // model the effect of the shockwave as if the shockwave were a plane of projectiles, // all moving in the direction direction_vec. then find the torque as the cross prod // of the force (pressure * area * normal * sin * scale * mass) // normal takes account the fraction of the surface exposed to the shockwave // the sin term is not technically needed but "feels" better // scale factors out the increase in area with larger objects // more massive objects get less rotation // find torque due to forces on the right/left face if ( normal.xyz.x < 0.0f ) // normal < 0, hits the right face vm_vec_copy_scale( &impact_vec, &orient->vec.rvec, max->xyz.x * pressure * area.xyz.x * normal.xyz.x * sin.xyz.x / pi->mass ); else // normal > 0, hits the left face vm_vec_copy_scale( &impact_vec, &orient->vec.rvec, min->xyz.x * pressure * area.xyz.x * -normal.xyz.x * sin.xyz.x / pi->mass ); vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec ); vm_vec_add2( &torque, &temp_torque ); // find torque due to forces on the up/down face if ( normal.xyz.y < 0.0f ) vm_vec_copy_scale( &impact_vec, &orient->vec.uvec, max->xyz.y * pressure * area.xyz.y * normal.xyz.y * sin.xyz.y / pi->mass ); else vm_vec_copy_scale( &impact_vec, &orient->vec.uvec, min->xyz.y * pressure * area.xyz.y * -normal.xyz.y * sin.xyz.y / pi->mass ); vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec ); vm_vec_add2( &torque, &temp_torque ); // find torque due to forces on the forward/backward face if ( normal.xyz.z < 0.0f ) vm_vec_copy_scale( &impact_vec, &orient->vec.fvec, max->xyz.z * pressure * area.xyz.z * normal.xyz.z * sin.xyz.z / pi->mass ); else vm_vec_copy_scale( &impact_vec, &orient->vec.fvec, min->xyz.z * pressure * area.xyz.z * -normal.xyz.z * sin.xyz.z / pi->mass ); vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec ); vm_vec_add2( &torque, &temp_torque ); // compute delta rotvel, scale according to blast and radius float scale; if (radius < MIN_RADIUS) { scale = 1.0f; } else { scale = (MAX_RADIUS - radius)/(MAX_RADIUS-MIN_RADIUS); } // set shockwave shake amplitude, duration, flag pi->shockwave_shake_amp = (float)(MAX_SHAKE*(pressure/STD_PRESSURE)*scale); pi->shockwave_decay = timestamp( SW_BLAST_DURATION ); pi->flags |= PF_IN_SHOCKWAVE; // safety dance if (!(IS_VEC_NULL_SQ_SAFE(&torque))) { vec3d delta_rotvel; vm_vec_rotate( &local_torque, &torque, orient ); vm_vec_copy_normalize(&delta_rotvel, &local_torque); vm_vec_scale(&delta_rotvel, (float)(MAX_ROTVEL*(pressure/STD_PRESSURE)*scale)); // nprintf(("Physics", "rotvel scale %f\n", (MAX_ROTVEL*(pressure/STD_PRESSURE)*scale))); vm_vec_add2(&pi->rotvel, &delta_rotvel); } // set reduced translational damping, set flags float velocity_scale = (float)MAX_VEL*scale; pi->flags |= PF_REDUCED_DAMP; update_reduced_damp_timestamp( pi, velocity_scale*pi->mass ); vm_vec_scale_add2( &pi->vel, direction_vec, velocity_scale ); vm_vec_rotate(&pi->prev_ramp_vel, &pi->vel, orient); // set so velocity will ramp starting from current speed // check that kick from shockwave is not too large 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_shock [speed: %f]\n", vm_vec_mag(&pi->vel))); vm_vec_normalize(&pi->vel); vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED); } }
// Returns TRUE if the weapon will never hit the other object. // If it can it predicts how long until these two objects need // to be checked and fills the time in in current_pair. int weapon_will_never_hit( object *obj_weapon, object *other, obj_pair * current_pair ) { Assert( obj_weapon->type == OBJ_WEAPON ); weapon *wp = &Weapons[obj_weapon->instance]; weapon_info *wip = &Weapon_info[wp->weapon_info_index]; // mprintf(( "Frame: %d, Weapon=%d, Other=%d, pair=$%08x\n", G3_frame_count, OBJ_INDEX(weapon), OBJ_INDEX(other), current_pair )); // Do some checks for weapons that don't turn if ( !(wip->wi_flags & WIF_TURNS) ) { // This first check is to see if a weapon is behind an object, and they // are heading in opposite directions. If so, we don't need to ever check // them again. This is only valid for weapons that don't turn. float vdot; if (wip->subtype == WP_LASER) { vec3d velocity_rel_weapon; vm_vec_sub(&velocity_rel_weapon, &obj_weapon->phys_info.vel, &other->phys_info.vel); vdot = -vm_vec_dot(&velocity_rel_weapon, &obj_weapon->orient.vec.fvec); } else { vdot = vm_vec_dot( &other->phys_info.vel, &obj_weapon->phys_info.vel); } if ( vdot <= 0.0f ) { // They're heading in opposite directions... // check their positions vec3d weapon2other; vm_vec_sub( &weapon2other, &other->pos, &obj_weapon->pos ); float pdot = vm_vec_dot( &obj_weapon->orient.vec.fvec, &weapon2other ); if ( pdot <= -other->radius ) { // The other object is behind the weapon by more than // its radius, so it will never hit... return 1; } } // FUTURE ENHANCEMENT IDEAS // Given a laser does it hit a slow or not moving object // in its life or the next n seconds? We'd actually need to check the // model for this. } // This check doesn't care about orient, only looks at the maximum speed // of the two objects, so it knows that in the next n seconds, they can't // go further than some distance, so don't bother checking collisions for // that time. This is very rough, but is so general that it works for // everything and immidiately gets rid of a lot of cases. if ( current_pair ) { // Find the time it will take before these get within each others distances. // tmp->next_check_time = timestamp(500); //vector max_vel; //maximum foward velocity in x,y,z float max_vel_weapon, max_vel_other; //SUSHI: Fix bug where additive weapon velocity screws up collisions //Assumes that weapons which don't home don't change speed, which is currently the case. if (!(wip->wi_flags & WIF_TURNS)) max_vel_weapon = obj_weapon->phys_info.speed; else if (wp->lssm_stage==5) max_vel_weapon = wip->lssm_stage5_vel; else max_vel_weapon = wp->weapon_max_vel; max_vel_other = other->phys_info.max_vel.xyz.z; if (max_vel_other < 10.0f) { if ( vm_vec_mag_squared( &other->phys_info.vel ) > 100 ) { // bump up velocity from collision max_vel_other = vm_vec_mag( &other->phys_info.vel ) + 10.0f; } else { max_vel_other = 10.0f; // object may move from collision } } // check weapon that does not turn against sphere expanding at ship maxvel // compare (weeapon) ray with expanding sphere (ship) to find earliest possible collision time // look for two time solutions to Xw = Xs, where Xw = Xw0 + Vwt*t Xs = Xs + Vs*(t+dt), where Vs*dt = radius of ship // Since direction of Vs is unknown, solve for (Vs*t) and find norm of both sides if ( !(wip->wi_flags & WIF_TURNS) ) { vec3d delta_x, laser_vel; float a,b,c, delta_x_dot_vl, delta_t; float root1, root2, root, earliest_time; if (max_vel_weapon == max_vel_other) { // this will give us NAN using the below formula, so check every frame current_pair->next_check_time = timestamp(0); return 0; } vm_vec_sub( &delta_x, &obj_weapon->pos, &other->pos ); laser_vel = obj_weapon->phys_info.vel; // vm_vec_copy_scale( &laser_vel, &weapon->orient.vec.fvec, max_vel_weapon ); delta_t = (other->radius + 10.0f) / max_vel_other; // time to get from center to radius of other obj delta_x_dot_vl = vm_vec_dotprod( &delta_x, &laser_vel ); a = max_vel_weapon*max_vel_weapon - max_vel_other*max_vel_other; b = 2.0f * (delta_x_dot_vl - max_vel_other*max_vel_other*delta_t); c = vm_vec_mag_squared( &delta_x ) - max_vel_other*max_vel_other*delta_t*delta_t; float discriminant = b*b - 4.0f*a*c; if ( discriminant < 0) { // never hit return 1; } else { root = fl_sqrt( discriminant ); root1 = (-b + root) / (2.0f * a) * 1000.0f; // get time in ms root2 = (-b - root) / (2.0f * a) * 1000.0f; // get time in ms } // standard algorithm if (max_vel_weapon > max_vel_other) { // find earliest positive time if ( root1 > root2 ) { float temp = root1; root1 = root2; root2 = temp; } if (root1 > 0) { earliest_time = root1; } else if (root2 > 0) { // root1 < 0 and root2 > 0, so we're inside sphere and next check should be next frame current_pair->next_check_time = timestamp(0); // check next time return 0; } else { // both times negative, so never collides return 1; } } // need to modify it for weapons that are slower than ships else { if (root2 > 0) { earliest_time = root2; } else { current_pair->next_check_time = timestamp(0); return 0; } } // check if possible collision occurs after weapon expires if ( earliest_time > 1000*wp->lifeleft ) return 1; // Allow one worst case frametime to elapse (~5 fps) earliest_time -= 200.0f; if (earliest_time > 100) { current_pair->next_check_time = timestamp( fl2i(earliest_time) ); return 0; } else { current_pair->next_check_time = timestamp(0); // check next time return 0; } } else { float dist, max_vel, time; max_vel = max_vel_weapon + max_vel_other; // suggest that fudge factor for other radius be changed to other_radius + const (~10) dist = vm_vec_dist( &other->pos, &obj_weapon->pos ) - (other->radius + 10.0f); if ( dist > 0.0f ) { time = (dist*1000.0f) / max_vel; int time_ms = fl2i(time); // check if possible collision occurs after weapon expires if ( time_ms > 1000*wp->lifeleft ) return 1; time_ms -= 200; // Allow at least one worst case frametime to elapse (~5 fps) if ( time_ms > 100 ) { // If it takes longer than 1/10th of a second, then delay it current_pair->next_check_time = timestamp(time_ms); //mprintf(( "Delaying %d ms\n", time_ms )); return 0; } } current_pair->next_check_time = timestamp(0); // check next time } } return 0; }
// Adds the pair to the pair list void obj_add_pair( object *A, object *B, int check_time, int add_to_end ) { uint ctype; int (*check_collision)( obj_pair *pair ); int swapped = 0; check_collision = NULL; if ( Num_pairs_allocated == 0 ) return; // don't have anything to add the pair too if ( A==B ) return; // Don't check collisions with yourself if ( !(A->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything if ( !(B->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything // Make sure you're not checking a parent with it's kid or vicy-versy // if ( A->parent_sig == B->signature && !(A->type == OBJ_SHIP && B->type == OBJ_DEBRIS) ) return; // if ( B->parent_sig == A->signature && !(A->type == OBJ_DEBRIS && B->type == OBJ_SHIP) ) return; if ( reject_obj_pair_on_parent(A,B) ) { return; } Assert( A->type < 127 ); Assert( B->type < 127 ); ctype = COLLISION_OF(A->type,B->type); switch( ctype ) { case COLLISION_OF(OBJ_WEAPON,OBJ_SHIP): swapped = 1; check_collision = collide_ship_weapon; break; case COLLISION_OF(OBJ_SHIP, OBJ_WEAPON): check_collision = collide_ship_weapon; break; case COLLISION_OF(OBJ_DEBRIS, OBJ_WEAPON): check_collision = collide_debris_weapon; break; case COLLISION_OF(OBJ_WEAPON, OBJ_DEBRIS): swapped = 1; check_collision = collide_debris_weapon; break; case COLLISION_OF(OBJ_DEBRIS, OBJ_SHIP): check_collision = collide_debris_ship; break; case COLLISION_OF(OBJ_SHIP, OBJ_DEBRIS): check_collision = collide_debris_ship; swapped = 1; break; case COLLISION_OF(OBJ_ASTEROID, OBJ_WEAPON): // Only check collision's with player weapons // if ( Objects[B->parent].flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_weapon; // } break; case COLLISION_OF(OBJ_WEAPON, OBJ_ASTEROID): swapped = 1; // Only check collision's with player weapons // if ( Objects[A->parent].flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_weapon; // } break; case COLLISION_OF(OBJ_ASTEROID, OBJ_SHIP): // Only check collisions with player ships // if ( B->flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_ship; // } break; case COLLISION_OF(OBJ_SHIP, OBJ_ASTEROID): // Only check collisions with player ships // if ( A->flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_ship; // } swapped = 1; break; case COLLISION_OF(OBJ_SHIP,OBJ_SHIP): check_collision = collide_ship_ship; break; case COLLISION_OF(OBJ_BEAM, OBJ_SHIP): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_ship; break; case COLLISION_OF(OBJ_BEAM, OBJ_ASTEROID): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_asteroid; break; case COLLISION_OF(OBJ_BEAM, OBJ_DEBRIS): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_debris; break; case COLLISION_OF(OBJ_BEAM, OBJ_WEAPON): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_missile; break; case COLLISION_OF(OBJ_WEAPON, OBJ_WEAPON): { weapon_info *awip, *bwip; awip = &Weapon_info[Weapons[A->instance].weapon_info_index]; bwip = &Weapon_info[Weapons[B->instance].weapon_info_index]; if ((awip->weapon_hitpoints > 0) || (bwip->weapon_hitpoints > 0)) { if (bwip->weapon_hitpoints == 0) { check_collision = collide_weapon_weapon; swapped=1; } else { check_collision = collide_weapon_weapon; } } /* if (awip->subtype != WP_LASER || bwip->subtype != WP_LASER) { if (awip->subtype == WP_LASER) { if ( bwip->wi_flags & WIF_BOMB ) { check_collision = collide_weapon_weapon; } } else if (bwip->subtype == WP_LASER) { if ( awip->wi_flags & WIF_BOMB ) { check_collision = collide_weapon_weapon; swapped=1; } } else { if ( (awip->wi_flags&WIF_BOMB) || (bwip->wi_flags&WIF_BOMB) ) { check_collision = collide_weapon_weapon; } } } */ /* int atype, btype; atype = Weapon_info[Weapons[A->instance].weapon_info_index].subtype; btype = Weapon_info[Weapons[B->instance].weapon_info_index].subtype; if ((atype == WP_LASER) && (btype == WP_MISSILE)) check_collision = collide_weapon_weapon; else if ((atype == WP_MISSILE) && (btype == WP_LASER)) { check_collision = collide_weapon_weapon; swapped = 1; } else if ((atype == WP_MISSILE) && (btype == WP_MISSILE)) check_collision = collide_weapon_weapon; */ break; } default: return; } // Swap them if needed if ( swapped ) { object *tmp = A; A = B; B = tmp; } // if there are any more obj_pair checks // we should then add function int maybe_not_add_obj_pair() // MWA -- 4/1/98 -- I'd do it, but I don't want to bust anything, so I'm doing my stuff here instead :-) //if ( MULTIPLAYER_CLIENT && !(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE)){ // multiplayer clients will only do ship/ship collisions, and their own ship to boot // if ( check_collision != collide_ship_ship ){ // return; // } // if ( (A != Player_obj) && (B != Player_obj) ){ // return; // } //} // only check debris:weapon collisions for player if (check_collision == collide_debris_weapon) { // weapon is B if ( !(Weapon_info[Weapons[B->instance].weapon_info_index].wi_flags & WIF_TURNS) ) { // check for dumbfire weapon // check if debris is behind laser float vdot; if (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) { vec3d velocity_rel_weapon; vm_vec_sub(&velocity_rel_weapon, &B->phys_info.vel, &A->phys_info.vel); vdot = -vm_vec_dot(&velocity_rel_weapon, &B->orient.vec.fvec); } else { vdot = vm_vec_dot( &A->phys_info.vel, &B->phys_info.vel); } if ( vdot <= 0.0f ) { // They're heading in opposite directions... // check their positions vec3d weapon2other; vm_vec_sub( &weapon2other, &A->pos, &B->pos ); float pdot = vm_vec_dot( &B->orient.vec.fvec, &weapon2other ); if ( pdot <= -A->radius ) { // The other object is behind the weapon by more than // its radius, so it will never hit... return; } } // check dist vs. dist moved during weapon lifetime vec3d delta_v; vm_vec_sub(&delta_v, &B->phys_info.vel, &A->phys_info.vel); if (vm_vec_dist_squared(&A->pos, &B->pos) > (vm_vec_mag_squared(&delta_v)*Weapons[B->instance].lifeleft*Weapons[B->instance].lifeleft)) { return; } // for nonplayer ships, only create collision pair if close enough if ( (B->parent >= 0) && !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (vm_vec_dist(&B->pos, &A->pos) < (4.0f*A->radius + 200.0f)) ) return; } } // don't check same team laser:ship collisions on small ships if not player if (check_collision == collide_ship_weapon) { // weapon is B if ( (B->parent >= 0) && !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (Ships[Objects[B->parent].instance].team == Ships[A->instance].team) && (Ship_info[Ships[A->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) ) { pairs_not_created++; return; } } if ( !check_collision ) return; Pairs_created++; // At this point, we have determined that collisions between // these two should be checked, so add the pair to the // collision pair list. if ( pair_free_list.next == NULL ) { nprintf(( "collision", "Out of object pairs!! Not all collisions will work!\n" )); return; } Num_pairs++; /* if (Num_pairs > Num_pairs_hwm) { Num_pairs_hwm = Num_pairs; //nprintf(("AI", "Num_pairs high water mark = %i\n", Num_pairs_hwm)); } */ if ( Num_pairs >= (Num_pairs_allocated - 20) ) { int i; Assert( Obj_pairs != NULL ); int old_pair_count = Num_pairs_allocated; obj_pair *old_pairs_ptr = Obj_pairs; // determine where we need to update the "previous" ptrs to int prev_free_mark = (pair_free_list.next - old_pairs_ptr); int prev_used_mark = (pair_used_list.next - old_pairs_ptr); Obj_pairs = (obj_pair*) vm_realloc_q( Obj_pairs, sizeof(obj_pair) * (Num_pairs_allocated + PAIRS_BUMP) ); // allow us to fail here and only if we don't do we setup the new pairs if (Obj_pairs == NULL) { // failed, just go back to the way we were and use only the pairs we have already Obj_pairs = old_pairs_ptr; } else { Num_pairs_allocated += PAIRS_BUMP; Assert( Obj_pairs != NULL ); // have to reset all of the "next" ptrs for the old set and handle the new set for (i = 0; i < Num_pairs_allocated; i++) { if (i >= old_pair_count) { memset( &Obj_pairs[i], 0, sizeof(obj_pair) ); Obj_pairs[i].next = &Obj_pairs[i+1]; } else { if (Obj_pairs[i].next != NULL) { // the "next" ptr will end up going backwards for used pairs so we have // to allow for that with this craziness... int next_mark = (Obj_pairs[i].next - old_pairs_ptr); Obj_pairs[i].next = &Obj_pairs[next_mark]; } // catch that last NULL from the previously allocated set if ( i == (old_pair_count-1) ) { Obj_pairs[i].next = &Obj_pairs[i+1]; } } } Obj_pairs[Num_pairs_allocated-1].next = NULL; // reset the "previous" ptrs pair_free_list.next = &Obj_pairs[prev_free_mark]; pair_used_list.next = &Obj_pairs[prev_used_mark]; } } // get a new obj_pair from the free list obj_pair * new_pair = pair_free_list.next; pair_free_list.next = new_pair->next; if ( add_to_end ) { obj_pair *last, *tmp; last = tmp = pair_used_list.next; while( tmp != NULL ) { if ( tmp->next == NULL ) last = tmp; tmp = tmp->next; } if ( last == NULL ) last = &pair_used_list; last->next = new_pair; Assert(new_pair != NULL); new_pair->next = NULL; } else { new_pair->next = pair_used_list.next; pair_used_list.next = new_pair; } A->num_pairs++; B->num_pairs++; new_pair->a = A; new_pair->b = B; new_pair->check_collision = check_collision; if ( check_time == -1 ){ new_pair->next_check_time = timestamp(0); // 0 means instantly time out } else { new_pair->next_check_time = check_time; } }
void obj_collide_pair(object *A, object *B) { uint ctype; int (*check_collision)( obj_pair *pair ); int swapped = 0; check_collision = NULL; if ( A==B ) return; // Don't check collisions with yourself if ( !(A->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything if ( !(B->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything // Make sure you're not checking a parent with it's kid or vicy-versy // if ( A->parent_sig == B->signature && !(A->type == OBJ_SHIP && B->type == OBJ_DEBRIS) ) return; // if ( B->parent_sig == A->signature && !(A->type == OBJ_DEBRIS && B->type == OBJ_SHIP) ) return; if ( reject_obj_pair_on_parent(A,B) ) { return; } Assert( A->type < 127 ); Assert( B->type < 127 ); ctype = COLLISION_OF(A->type,B->type); switch( ctype ) { case COLLISION_OF(OBJ_WEAPON,OBJ_SHIP): swapped = 1; check_collision = collide_ship_weapon; break; case COLLISION_OF(OBJ_SHIP, OBJ_WEAPON): check_collision = collide_ship_weapon; break; case COLLISION_OF(OBJ_DEBRIS, OBJ_WEAPON): check_collision = collide_debris_weapon; break; case COLLISION_OF(OBJ_WEAPON, OBJ_DEBRIS): swapped = 1; check_collision = collide_debris_weapon; break; case COLLISION_OF(OBJ_DEBRIS, OBJ_SHIP): check_collision = collide_debris_ship; break; case COLLISION_OF(OBJ_SHIP, OBJ_DEBRIS): check_collision = collide_debris_ship; swapped = 1; break; case COLLISION_OF(OBJ_ASTEROID, OBJ_WEAPON): // Only check collision's with player weapons // if ( Objects[B->parent].flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_weapon; // } break; case COLLISION_OF(OBJ_WEAPON, OBJ_ASTEROID): swapped = 1; // Only check collision's with player weapons // if ( Objects[A->parent].flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_weapon; // } break; case COLLISION_OF(OBJ_ASTEROID, OBJ_SHIP): // Only check collisions with player ships // if ( B->flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_ship; // } break; case COLLISION_OF(OBJ_SHIP, OBJ_ASTEROID): // Only check collisions with player ships // if ( A->flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_ship; // } swapped = 1; break; case COLLISION_OF(OBJ_SHIP,OBJ_SHIP): check_collision = collide_ship_ship; break; case COLLISION_OF(OBJ_SHIP, OBJ_BEAM): if(beam_collide_early_out(B, A)){ return; } swapped = 1; check_collision = beam_collide_ship; break; case COLLISION_OF(OBJ_BEAM, OBJ_SHIP): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_ship; break; case COLLISION_OF(OBJ_ASTEROID, OBJ_BEAM): if(beam_collide_early_out(B, A)) { return; } swapped = 1; check_collision = beam_collide_asteroid; break; case COLLISION_OF(OBJ_BEAM, OBJ_ASTEROID): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_asteroid; break; case COLLISION_OF(OBJ_DEBRIS, OBJ_BEAM): if(beam_collide_early_out(B, A)) { return; } swapped = 1; check_collision = beam_collide_debris; break; case COLLISION_OF(OBJ_BEAM, OBJ_DEBRIS): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_debris; break; case COLLISION_OF(OBJ_WEAPON, OBJ_BEAM): if(beam_collide_early_out(B, A)) { return; } swapped = 1; check_collision = beam_collide_missile; break; case COLLISION_OF(OBJ_BEAM, OBJ_WEAPON): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_missile; break; case COLLISION_OF(OBJ_WEAPON, OBJ_WEAPON): { weapon_info *awip, *bwip; awip = &Weapon_info[Weapons[A->instance].weapon_info_index]; bwip = &Weapon_info[Weapons[B->instance].weapon_info_index]; if ((awip->weapon_hitpoints > 0) || (bwip->weapon_hitpoints > 0)) { if (bwip->weapon_hitpoints == 0) { check_collision = collide_weapon_weapon; swapped=1; } else { check_collision = collide_weapon_weapon; } } break; } default: return; } if ( !check_collision ) return; // Swap them if needed if ( swapped ) { object *tmp = A; A = B; B = tmp; } collider_pair *collision_info = NULL; bool valid = false; uint key = (OBJ_INDEX(A) << 12) + OBJ_INDEX(B); collision_info = &Collision_cached_pairs[key]; if ( collision_info->initialized ) { // make sure we're referring to the correct objects in case the original pair was deleted if ( collision_info->signature_a == collision_info->a->signature && collision_info->signature_b == collision_info->b->signature ) { valid = true; } else { collision_info->a = A; collision_info->b = B; collision_info->signature_a = A->signature; collision_info->signature_b = B->signature; collision_info->next_check_time = timestamp(0); } } else { collision_info->a = A; collision_info->b = B; collision_info->signature_a = A->signature; collision_info->signature_b = B->signature; collision_info->initialized = true; collision_info->next_check_time = timestamp(0); } if ( valid && A->type != OBJ_BEAM ) { // if this signature is valid, make the necessary checks to see if we need to collide check if ( collision_info->next_check_time == -1 ) { return; } else { if ( !timestamp_elapsed(collision_info->next_check_time) ) { return; } } } else { //if ( A->type == OBJ_BEAM ) { //if(beam_collide_early_out(A, B)){ //collision_info->next_check_time = -1; //return; //} //} // only check debris:weapon collisions for player if (check_collision == collide_debris_weapon) { // weapon is B if ( !(Weapon_info[Weapons[B->instance].weapon_info_index].wi_flags & WIF_TURNS) ) { // check for dumbfire weapon // check if debris is behind laser float vdot; if (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) { vec3d velocity_rel_weapon; vm_vec_sub(&velocity_rel_weapon, &B->phys_info.vel, &A->phys_info.vel); vdot = -vm_vec_dot(&velocity_rel_weapon, &B->orient.vec.fvec); } else { vdot = vm_vec_dot( &A->phys_info.vel, &B->phys_info.vel); } if ( vdot <= 0.0f ) { // They're heading in opposite directions... // check their positions vec3d weapon2other; vm_vec_sub( &weapon2other, &A->pos, &B->pos ); float pdot = vm_vec_dot( &B->orient.vec.fvec, &weapon2other ); if ( pdot <= -A->radius ) { // The other object is behind the weapon by more than // its radius, so it will never hit... collision_info->next_check_time = -1; return; } } // check dist vs. dist moved during weapon lifetime vec3d delta_v; vm_vec_sub(&delta_v, &B->phys_info.vel, &A->phys_info.vel); if (vm_vec_dist_squared(&A->pos, &B->pos) > (vm_vec_mag_squared(&delta_v)*Weapons[B->instance].lifeleft*Weapons[B->instance].lifeleft)) { collision_info->next_check_time = -1; return; } // for nonplayer ships, only create collision pair if close enough if ( (B->parent >= 0) && !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (vm_vec_dist(&B->pos, &A->pos) < (4.0f*A->radius + 200.0f)) ) { collision_info->next_check_time = -1; return; } } } // don't check same team laser:ship collisions on small ships if not player if (check_collision == collide_ship_weapon) { // weapon is B if ( (B->parent >= 0) && !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (Ships[Objects[B->parent].instance].team == Ships[A->instance].team) && (Ship_info[Ships[A->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) ) { collision_info->next_check_time = -1; return; } } } obj_pair new_pair; new_pair.a = A; new_pair.b = B; new_pair.check_collision = check_collision; new_pair.next_check_time = collision_info->next_check_time; if ( check_collision(&new_pair) ) { // don't have to check ever again collision_info->next_check_time = -1; } else { collision_info->next_check_time = new_pair.next_check_time; } }