ref: 6d241d506fbce2d0b6a69f19f83b34bdda0ac6af
dir: /src/wipeout/ship.c/
#include "../mem.h" #include "../utils.h" #include "../system.h" #include "object.h" #include "scene.h" #include "track.h" #include "weapon.h" #include "camera.h" #include "image.h" #include "object.h" #include "ship.h" #include "ship_ai.h" #include "ship_player.h" #include "game.h" #include "race.h" #include "sfx.h" void ships_load() { texture_list_t ship_textures = image_get_compressed_textures("wipeout/common/allsh.cmp"); Object *ship_models = objects_load("wipeout/common/allsh.prm", ship_textures); texture_list_t collision_textures = image_get_compressed_textures("wipeout/common/alcol.cmp"); Object *collision_models = objects_load("wipeout/common/alcol.prm", collision_textures); int object_index; Object *ship_model = ship_models; Object *collision_model = collision_models; for (object_index = 0; object_index < len(g.ships) && ship_model && collision_model; object_index++) { int ship_index = def.ship_model_to_pilot[object_index]; g.ships[ship_index].model = ship_model; g.ships[ship_index].collision_model = collision_model; ship_model = ship_model->next; collision_model = collision_model->next; ship_init_exhaust_plume(&g.ships[ship_index]); } error_if(object_index != len(g.ships), "Expected %ld ship models, got %d", len(g.ships), object_index); uint16_t shadow_textures_start = render_textures_len(); image_get_texture_semi_trans("wipeout/textures/shad1.tim"); image_get_texture_semi_trans("wipeout/textures/shad2.tim"); image_get_texture_semi_trans("wipeout/textures/shad3.tim"); image_get_texture_semi_trans("wipeout/textures/shad4.tim"); for (int i = 0; i < len(g.ships); i++) { g.ships[i].shadow_texture = shadow_textures_start + (i >> 1); } } void ships_init(section_t *section) { section_t *start_sections[len(g.ships)]; int ranks_to_pilots[NUM_PILOTS]; // Initialize ranks with all pilots in order for (int i = 0; i < len(g.ships); i++) { ranks_to_pilots[i] = i; } // Randomize order for single race or new championship if (g.race_type != RACE_TYPE_CHAMPIONSHIP || g.circut == CIRCUT_ALTIMA_VII) { shuffle(ranks_to_pilots, len(ranks_to_pilots)); } // Randomize some tiers in an ongoing championship else if (g.race_type == RACE_TYPE_CHAMPIONSHIP) { // Initialize with current championship order for (int i = 0; i < len(g.ships); i++) { ranks_to_pilots[i] = g.championship_ranks[i].pilot; } shuffle(ranks_to_pilots, 2); // shuffle 0..1 shuffle(ranks_to_pilots + 4, len(ranks_to_pilots)-5); // shuffle 4..len-1 } // player is always last for (int i = 0; i < len(ranks_to_pilots)-1; i++) { if (ranks_to_pilots[i] == g.pilot) { swap(ranks_to_pilots[i], ranks_to_pilots[i+1]); } } int start_line_pos = def.circuts[g.circut].settings[g.race_class].start_line_pos; for (int i = 0; i < start_line_pos - 15; i++) { section = section->next; } for (int i = 0; i < len(g.ships); i++) { start_sections[i] = section; section = section->next; if ((i % 2) == 0) { section = section->next; } } for (int i = 0; i < len(ranks_to_pilots); i++) { int rank_inv = (len(g.ships)-1) - i; int pilot = ranks_to_pilots[i]; ship_init(&g.ships[pilot], start_sections[rank_inv], pilot, rank_inv); } } static inline bool sort_rank_compare(pilot_points_t *pa, pilot_points_t *pb) { ship_t *a = &g.ships[pa->pilot]; ship_t *b = &g.ships[pb->pilot]; if (a->total_section_num == b->total_section_num) { vec3_t c0 = a->section->center; vec3_t c1 = a->section->next->center; vec3_t dir = vec3_sub(c1, c0); float pos_a = vec3_dot(vec3_sub(a->position, c0), dir); float pos_b = vec3_dot(vec3_sub(b->position, c0), dir); return (pos_a < pos_b); } else { return a->total_section_num < b->total_section_num; } } void ships_update() { if (g.race_type == RACE_TYPE_TIME_TRIAL) { ship_update(&g.ships[g.pilot]); } else { for (int i = 0; i < len(g.ships); i++) { ship_update(&g.ships[i]); } for (int j = 0; j < (len(g.ships) - 1); j++) { for (int i = j + 1; i < len(g.ships); i++) { ship_collide_with_ship(&g.ships[i], &g.ships[j]); } } if (flags_is(g.ships[g.pilot].flags, SHIP_RACING)) { sort(g.race_ranks, len(g.race_ranks), sort_rank_compare); for (int32_t i = 0; i < len(g.ships); i++) { g.ships[g.race_ranks[i].pilot].position_rank = i + 1; } } } } void ships_draw() { // Ship models for (int i = 0; i < len(g.ships); i++) { if ( flags_is(g.ships[i].flags, SHIP_VIEW_INTERNAL) || (g.race_type == RACE_TYPE_TIME_TRIAL && i != g.pilot) ) { continue; } ship_draw(&g.ships[i]); } // Shadows render_set_model_mat(&mat4_identity()); render_set_depth_write(false); render_set_depth_offset(-32.0); for (int i = 0; i < len(g.ships); i++) { if ( (g.race_type == RACE_TYPE_TIME_TRIAL && i != g.pilot) || flags_not(g.ships[i].flags, SHIP_VISIBLE) || flags_is(g.ships[i].flags, SHIP_FLYING) ) { continue; } ship_draw_shadow(&g.ships[i]); } render_set_depth_offset(0.0); render_set_depth_write(true); } void ship_init(ship_t *self, section_t *section, int pilot, int inv_start_rank) { self->pilot = pilot; self->velocity = vec3(0, 0, 0); self->acceleration = vec3(0, 0, 0); self->angle = vec3(0, 0, 0); self->angular_velocity = vec3(0, 0, 0); self->turn_rate = 0; self->thrust_mag = 0; self->current_thrust_max = 0; self->turn_rate_from_hit = 0; self->brake_right = 0; self->brake_left = 0; self->flags = SHIP_RACING | SHIP_VISIBLE | SHIP_DIRECTION_FORWARD; self->weapon_type = WEAPON_TYPE_NONE; self->lap = -1; self->max_lap = -1; self->speed = 0; self->ebolt_timer = 0; self->revcon_timer = 0; self->special_timer = 0; self->mat = mat4_identity(); self->update_timer = 0; self->last_impact_time = 0; int team = def.pilots[pilot].team; self->mass = def.teams[team].attributes[g.race_class].mass; self->thrust_max = def.teams[team].attributes[g.race_class].thrust_max; self->skid = def.teams[team].attributes[g.race_class].skid; self->turn_rate = def.teams[team].attributes[g.race_class].turn_rate; self->turn_rate_max = def.teams[team].attributes[g.race_class].turn_rate_max; self->resistance = def.teams[team].attributes[g.race_class].resistance; self->lap_time = 0; self->update_timer = UPDATE_TIME_INITIAL; self->position_rank = NUM_PILOTS - inv_start_rank; if (pilot == g.pilot) { self->update_func = ship_player_update_intro; self->remote_thrust_max = 2900; self->remote_thrust_mag = 46; self->fight_back = 0; } else { self->update_func = ship_ai_update_intro; self->remote_thrust_max = def.ai_settings[g.race_class][inv_start_rank-1].thrust_max; self->remote_thrust_mag = def.ai_settings[g.race_class][inv_start_rank-1].thrust_magnitude; self->fight_back = def.ai_settings[g.race_class][inv_start_rank-1].fight_back; } self->section = section; self->prev_section = section; float spread_base = def.circuts[g.circut].settings[g.race_class].spread_base; float spread_factor = def.circuts[g.circut].settings[g.race_class].spread_factor; int p = inv_start_rank - 1; self->start_accelerate_timer = p * (spread_base + (p * spread_factor)) * (1.0/30.0); track_face_t *face = g.track.faces + section->face_start; face++; if ((inv_start_rank % 2) != 0) { face++; } vec3_t face_point = vec3_mulf(vec3_add(face->tris[0].vertices[0].pos, face->tris[0].vertices[2].pos), 0.5); self->position = vec3_add(face_point, vec3_mulf(face->normal, 200)); self->section_num = section->num; self->prev_section_num = section->num; self->total_section_num = section->num; section_t *next = section->next; vec3_t direction = vec3_sub(next->center, section->center); self->angle.y = -atan2(direction.x, direction.z); } void ship_init_exhaust_plume(ship_t *self) { int16_t indices[64]; int16_t indices_len = 0; Prm prm = {.primitive = self->model->primitives}; for (int i = 0; i < self->model->primitives_len; i++) { switch (prm.f3->type) { case PRM_TYPE_F3 : if (flags_is(prm.f3->flag, PRM_SHIP_ENGINE)) { die("F3 ::SE marked polys should be ft3's"); } prm.f3 += 1; break; case PRM_TYPE_F4 : if (flags_is(prm.f4->flag, PRM_SHIP_ENGINE)) { die("F4 ::SE marked polys should be ft3's"); } prm.f4 += 1; break; case PRM_TYPE_FT3 : if (flags_is(prm.ft3->flag, PRM_SHIP_ENGINE)) { indices[indices_len++] = prm.ft3->coords[0]; indices[indices_len++] = prm.ft3->coords[1]; indices[indices_len++] = prm.ft3->coords[2]; flags_add(prm.ft3->flag, PRM_TRANSLUCENT); prm.ft3->colour.as_rgba.r = 180; prm.ft3->colour.as_rgba.g = 97 ; prm.ft3->colour.as_rgba.b = 120; prm.ft3->colour.as_rgba.a = 140; } prm.ft3 += 1; break; case PRM_TYPE_FT4 : if (flags_is(prm.ft4->flag, PRM_SHIP_ENGINE)) { die("FT4 ::SE marked polys should be ft3's"); } prm.ft4 += 1; break; case PRM_TYPE_G3 : if (flags_is(prm.g3->flag, PRM_SHIP_ENGINE)) { die("G3 ::SE marked polys should be ft3's"); } prm.g3 += 1; break; case PRM_TYPE_G4 : if (flags_is(prm.g4->flag, PRM_SHIP_ENGINE)) { die("G4 ::SE marked polys should be ft3's"); } prm.g4 += 1; break; case PRM_TYPE_GT3 : if (flags_is(prm.gt3->flag, PRM_SHIP_ENGINE)) { indices[indices_len++] = prm.gt3->coords[0]; indices[indices_len++] = prm.gt3->coords[1]; indices[indices_len++] = prm.gt3->coords[2]; flags_add(prm.gt3->flag, PRM_TRANSLUCENT); for (int j = 0; j < 3; j++) { prm.gt3->colour[j].as_rgba.r = 180; prm.gt3->colour[j].as_rgba.g = 97 ; prm.gt3->colour[j].as_rgba.b = 120; prm.gt3->colour[j].as_rgba.a = 140; } } prm.gt3 += 1; break; case PRM_TYPE_GT4 : if (flags_is(prm.gt4->flag, PRM_SHIP_ENGINE)) { die("GT4 ::SE marked polys should be ft3's"); } prm.gt4 += 1; break; default : die("cone.c::InitCone:Bad primitive type %x\n", prm.f3->type); break; } } // get out the center vertex self->exhaust_plume[0].v = NULL; self->exhaust_plume[1].v = NULL; self->exhaust_plume[2].v = NULL; int shared[3] = {-1, -1, -1}; int booster = 0; for (int i = 0; (i < indices_len) && (booster < 3); i++) { int similar = 0; for (int j = 0; j < indices_len; j++) { if (indices[i] == indices[j]) { similar++; if (similar > 3) { int found = 0; for (int k = 0; k < 3; k++) { if (shared[k] == indices[i]) { found = 1; } } if (!found) { shared[booster] = indices[i]; booster++; } } } } } for (int j = 0; j < 3; j++) { if (shared[j] != -1) { self->exhaust_plume[j].v = &self->model->vertices[shared[j]]; self->exhaust_plume[j].initial = self->model->vertices[shared[j]]; } } } void ship_draw(ship_t *self) { object_draw(self->model, &self->mat); } void ship_draw_shadow(ship_t *self) { track_face_t *face = track_section_get_base_face(self->section); vec3_t face_point = face->tris[0].vertices[0].pos; vec3_t nose = vec3_add(self->position, vec3_mulf(self->dir_forward, 384)); vec3_t wngl = vec3_sub(vec3_sub(self->position, vec3_mulf(self->dir_right, 256)), vec3_mulf(self->dir_forward, 384)); vec3_t wngr = vec3_sub(vec3_add(self->position, vec3_mulf(self->dir_right, 256)), vec3_mulf(self->dir_forward, 384)); nose = vec3_sub(nose, vec3_mulf(face->normal, vec3_distance_to_plane(nose, face_point, face->normal))); wngl = vec3_sub(wngl, vec3_mulf(face->normal, vec3_distance_to_plane(wngl, face_point, face->normal))); wngr = vec3_sub(wngr, vec3_mulf(face->normal, vec3_distance_to_plane(wngr, face_point, face->normal))); rgba_t color = rgba(0 , 0 , 0, 128); render_push_tris((tris_t) { .vertices = { { .pos = {wngl.x, wngl.y, wngl.z}, .uv = {0, 256}, .color = color, }, { .pos = {wngr.x, wngr.y, wngr.z}, .uv = {128, 256}, .color = color }, { .pos = {nose.x, nose.y, nose.z}, .uv = {64, 0}, .color = color }, } }, self->shadow_texture); } void ship_update(ship_t *self) { // Set Unit vectors of this ship float sx = sin(self->angle.x); float cx = cos(self->angle.x); float sy = sin(self->angle.y); float cy = cos(self->angle.y); float sz = sin(self->angle.z); float cz = cos(self->angle.z); self->dir_forward.x = -(sy * cx); self->dir_forward.y = - sx; self->dir_forward.z = (cy * cx); self->dir_right.x = (cy * cz) + (sy * sz * sx); self->dir_right.y = -(sz * cx); self->dir_right.z = (sy * cz) - (cy * sx * sz); self->dir_up.x = (cy * sz) - (sy * sx * cz); self->dir_up.y = -(cx * cz); self->dir_up.z = (sy * sz) + (cy * sx * cz); self->prev_section = self->section; float distance; self->section = track_nearest_section(self->position, self->section, &distance); if (distance > 3700) { flags_add(self->flags, SHIP_FLYING); } else { flags_rm(self->flags, SHIP_FLYING); } self->prev_section_num = self->prev_section->num; self->section_num = self->section->num; // Figure out which side of the track the ship is on track_face_t *face = track_section_get_base_face(self->section); vec3_t to_face_vector = vec3_sub( face->tris[0].vertices[0].pos, face->tris[0].vertices[1].pos ); vec3_t direction = vec3_sub(self->section->center, self->position); if (vec3_dot(direction, to_face_vector) > 0) { flags_add(self->flags, SHIP_LEFT_SIDE); } else { flags_rm(self->flags, SHIP_LEFT_SIDE); face++; } // Collect powerup if ( flags_is(face->flags, FACE_PICKUP_ACTIVE) && flags_not(self->flags, SHIP_SPECIALED) && self->weapon_type == WEAPON_TYPE_NONE && track_collect_pickups(face) ) { if (self->pilot == g.pilot) { sfx_play(SFX_POWERUP); if (flags_is(self->flags, SHIP_SHIELDED)) { self->weapon_type = weapon_get_random_type(WEAPON_CLASS_PROJECTILE); } else { self->weapon_type = weapon_get_random_type(WEAPON_CLASS_ANY); } } else { self->weapon_type = 1; } } self->last_impact_time += system_tick(); // Call the active player/ai update function (self->update_func)(self); // Animate the exhaust plume int exhaust_len; if (self->pilot == g.pilot) { // get the z exhaust_len related to speed or thrust exhaust_len = self->thrust_mag * 0.0625; exhaust_len += self->speed * 0.00390625; } else { // for remote ships the z exhaust_len is a constant exhaust_len = 150; } for (int i = 0; i < 3; i++) { if (self->exhaust_plume[i].v != NULL) { self->exhaust_plume[i].v->z = self->exhaust_plume[i].initial.z - exhaust_len + (rand_int(-16383, 16383) >> 9); self->exhaust_plume[i].v->x = self->exhaust_plume[i].initial.x + (rand_int(-16383, 16383) >> 11); self->exhaust_plume[i].v->y = self->exhaust_plume[i].initial.y + (rand_int(-16383, 16383) >> 11); } } mat4_set_translation(&self->mat, self->position); mat4_set_yaw_pitch_roll(&self->mat, self->angle); // Race position and lap times self->lap_time += system_tick(); int start_line_pos = def.circuts[g.circut].settings[g.race_class].start_line_pos; // Crossed line backwards if (self->prev_section_num == start_line_pos + 1 && self->section_num <= start_line_pos) { self->lap--; } // Crossed line forwards else if (self->prev_section_num == start_line_pos && self->section_num > start_line_pos) { self->lap++; // Is it the first time we're crossing the line for this lap? if (self->lap > self->max_lap) { self->max_lap = self->lap; if (self->lap > 0 && self->lap <= NUM_LAPS) { g.lap_times[self->pilot][self->lap-1] = self->lap_time; } self->lap_time = 0; if (g.race_type == RACE_TYPE_TIME_TRIAL) { self->weapon_type = WEAPON_TYPE_TURBO; } if (self->lap == NUM_LAPS && self->pilot == g.pilot) { race_end(); } } } int section_num_from_line = self->section_num - (start_line_pos + 1); if (section_num_from_line < 0) { section_num_from_line += g.track.section_count; } self->total_section_num = self->lap * g.track.section_count + section_num_from_line; } vec3_t ship_cockpit(ship_t *self) { return vec3_add(self->position, vec3_mulf(self->dir_up, 128)); } vec3_t ship_nose(ship_t *self) { return vec3_add(self->position, vec3_mulf(self->dir_forward, 512)); } vec3_t ship_wing_left(ship_t *self) { return vec3_sub(vec3_sub(self->position, vec3_mulf(self->dir_right, 256)), vec3_mulf(self->dir_forward, 256)); } vec3_t ship_wing_right(ship_t *self) { return vec3_sub(vec3_add(self->position, vec3_mulf(self->dir_right, 256)), vec3_mulf(self->dir_forward, 256)); } static bool vec3_is_on_face(vec3_t pos, track_face_t *face, float alpha) { vec3_t plane_point = vec3_sub(pos, vec3_mulf(face->normal, alpha)); vec3_t vec0 = vec3_sub(plane_point, face->tris[0].vertices[1].pos); vec3_t vec1 = vec3_sub(plane_point, face->tris[0].vertices[2].pos); vec3_t vec2 = vec3_sub(plane_point, face->tris[0].vertices[0].pos); vec3_t vec3 = vec3_sub(plane_point, face->tris[1].vertices[0].pos); float angle = vec3_angle(vec0, vec2) + vec3_angle(vec2, vec3) + vec3_angle(vec3, vec1) + vec3_angle(vec1, vec0); return (angle > M_PI * 2 - 0.01); } void ship_resolve_wing_collision(ship_t *self, track_face_t *face, float direction) { vec3_t collision_vector = vec3_sub(self->section->center, face->tris[0].vertices[2].pos); float angle = vec3_angle(collision_vector, self->dir_forward); self->velocity = vec3_reflect(self->velocity, face->normal, 2); self->position = vec3_sub(self->position, vec3_mulf(self->velocity, 0.015625)); // system_tick? self->velocity = vec3_sub(self->velocity, vec3_mulf(self->velocity, 0.5)); self->velocity = vec3_add(self->velocity, vec3_mulf(face->normal, 4096)); // div by 4096? float magnitude = (fabsf(angle) * self->speed) * M_PI / (4096 * 16.0); // (6 velocity shift, 12 angle shift?) vec3_t wing_pos; if (direction > 0) { self->angular_velocity.z += magnitude; wing_pos = vec3_add(self->position, vec3_mulf(vec3_sub(self->dir_right, self->dir_forward), 256)); // >> 4?? } else { self->angular_velocity.z -= magnitude; wing_pos = vec3_sub(self->position, vec3_mulf(vec3_sub(self->dir_right, self->dir_forward), 256)); // >> 4?? } if (self->last_impact_time > 0.2) { self->last_impact_time = 0; sfx_play_at(SFX_IMPACT, wing_pos, vec3(0, 0, 0), 1); } } void ship_resolve_nose_collision(ship_t *self, track_face_t *face, float direction) { vec3_t collision_vector = vec3_sub(self->section->center, face->tris[0].vertices[2].pos); float angle = vec3_angle(collision_vector, self->dir_forward); self->velocity = vec3_reflect(self->velocity, face->normal, 2); self->position = vec3_sub(self->position, vec3_mulf(self->velocity, 0.015625)); // system_tick? self->velocity = vec3_sub(self->velocity, vec3_mulf(self->velocity, 0.5)); self->velocity = vec3_add(self->velocity, vec3_mulf(face->normal, 4096)); // div by 4096? float magnitude = ((self->speed * 0.0625) + 400) * M_PI / (4096.0 * 64.0); if (direction > 0) { self->angular_velocity.y += magnitude; } else { self->angular_velocity.y -= magnitude; } if (self->last_impact_time > 0.2) { self->last_impact_time = 0; sfx_play_at(SFX_IMPACT, ship_nose(self), vec3(0, 0, 0), 1); } } void ship_collide_with_track(ship_t *self, track_face_t *face) { float alpha; section_t *trackPtr; bool collide; track_face_t *face2; trackPtr = self->section->next; vec3_t direction = vec3_sub(trackPtr->center, self->section->center); float down_track = vec3_dot(direction, self->dir_forward); if (down_track < 0) { flags_rm(self->flags, SHIP_DIRECTION_FORWARD); } else { flags_add(self->flags, SHIP_DIRECTION_FORWARD); } vec3_t to_face_vector = vec3_sub(face->tris[0].vertices[0].pos, face->tris[0].vertices[1].pos); direction = vec3_sub(self->section->center, self->position); float to_face = vec3_dot(direction, to_face_vector); face--; // Check against left hand side of track // FIXME: the collision checks in junctions are very flakey and often select // the wrong face to test for a collision. // Instead of this whole mess here, there should just be a function // `track_get_nearest_face(section, pos)` that we call with the nose and // wing positions and then just resolve against this face. if (to_face > 0) { flags_add(self->flags, SHIP_LEFT_SIDE); vec3_t face_point = face->tris[0].vertices[0].pos; alpha = vec3_distance_to_plane(ship_nose(self), face_point, face->normal); if (alpha <= 0) { if (flags_is(self->section->flags, SECTION_JUNCTION_START)) { collide = vec3_is_on_face(ship_nose(self), face, alpha); if (collide) { ship_resolve_nose_collision(self, face, -down_track); } else { face2 = g.track.faces + self->section->next->face_start; collide = vec3_is_on_face(ship_nose(self), face2, alpha); if (collide) { ship_resolve_nose_collision(self, face, -down_track); } } } else if (flags_is(self->section->flags, SECTION_JUNCTION_END)) { collide = vec3_is_on_face(ship_nose(self), face, alpha); if (collide) { ship_resolve_nose_collision(self, face, -down_track); } else { face2 = g.track.faces + self->section->prev->face_start; collide = vec3_is_on_face(ship_nose(self), face2, alpha); if (collide) { ship_resolve_nose_collision(self, face, -down_track); } } } else { ship_resolve_nose_collision(self, face, -down_track); } return; } alpha = vec3_distance_to_plane(ship_wing_left(self), face_point, face->normal); if (alpha <= 0) { if ( flags_is(self->section->flags, SECTION_JUNCTION_START) || flags_is(self->section->flags, SECTION_JUNCTION_END) ) { collide = vec3_is_on_face(ship_wing_left(self), face, alpha); if (collide) { ship_resolve_nose_collision(self, face, -down_track); } } else { ship_resolve_wing_collision(self, face, -down_track); } return; } alpha = vec3_distance_to_plane(ship_wing_right(self), face_point, face->normal); if (alpha <= 0) { if ( flags_is(self->section->flags, SECTION_JUNCTION_START) || flags_is(self->section->flags, SECTION_JUNCTION_END) ) { collide = vec3_is_on_face(ship_wing_right(self), face, alpha); if (collide) { ship_resolve_nose_collision(self, face, -down_track); } } else { ship_resolve_wing_collision(self, face, -down_track); } return; } } // Collision check against 2nd wall else { flags_rm(self->flags, SHIP_LEFT_SIDE); face++; while (face->flags & FACE_TRACK_BASE) { face++; } vec3_t face_point = face->tris[0].vertices[0].pos; alpha = vec3_distance_to_plane(ship_nose(self), face_point, face->normal); if (alpha <= 0) { if (flags_is(self->section->flags, SECTION_JUNCTION_START)) { collide = vec3_is_on_face(ship_nose(self), face, alpha); if (collide) { ship_resolve_nose_collision(self, face, down_track); } else { face2 = g.track.faces + self->section->next->face_start; face2 += 3; collide = vec3_is_on_face(ship_nose(self), face2, alpha); if (collide) { ship_resolve_nose_collision(self, face, -down_track); } } } else if (flags_is(self->section->flags, SECTION_JUNCTION_END)) { collide = vec3_is_on_face(ship_nose(self), face, alpha); if (collide) { ship_resolve_nose_collision(self, face, -down_track); } else { face2 = g.track.faces + self->section->prev->face_start; face2 += 3; collide = vec3_is_on_face(ship_nose(self), face2, alpha); if (collide) { ship_resolve_nose_collision(self, face2, -down_track); } } } else { ship_resolve_nose_collision(self, face, down_track); } return; } alpha = vec3_distance_to_plane(ship_wing_left(self), face_point, face->normal); if (alpha <= 0) { if ( flags_is(self->section->flags, SECTION_JUNCTION_START) || flags_is(self->section->flags, SECTION_JUNCTION_END) ) { collide = vec3_is_on_face(ship_wing_left(self), face, alpha); if (collide) { ship_resolve_nose_collision(self, face, down_track); } } else { ship_resolve_wing_collision(self, face, down_track); } return; } alpha = vec3_distance_to_plane(ship_wing_right(self), face_point, face->normal); if (alpha <= 0) { if ( flags_is(self->section->flags, SECTION_JUNCTION_START) || flags_is(self->section->flags, SECTION_JUNCTION_END) ) { collide = vec3_is_on_face(ship_wing_right(self), face, alpha); if (collide) { ship_resolve_nose_collision(self, face, down_track); } } else { ship_resolve_wing_collision(self, face, down_track); } return; } } } bool ship_intersects_ship(ship_t *self, ship_t *other) { // Get 4 points of collision model relative to the // camera vec3_t a = vec3_transform(other->collision_model->vertices[0], &other->mat); vec3_t b = vec3_transform(other->collision_model->vertices[1], &other->mat); vec3_t c = vec3_transform(other->collision_model->vertices[2], &other->mat); vec3_t d = vec3_transform(other->collision_model->vertices[3], &other->mat); vec3_t other_points[6] = {b, a, d, a, a, b}; vec3_t other_lines[6] = { vec3_sub(c, b), vec3_sub(c, a), vec3_sub(c, d), vec3_sub(b, a), vec3_sub(d, a), vec3_sub(d, b) }; Prm poly = {.primitive = other->collision_model->primitives}; int primitives_len = other->collision_model->primitives_len; vec3_t p1, p2, p3; // for all 4 planes of the enemy ship for (int pi = 0; pi < primitives_len; pi++) { int16_t *indices; switch (poly.primitive->type) { case PRM_TYPE_F3: indices = poly.f3->coords; p1 = vec3_transform(self->collision_model->vertices[indices[0]], &self->mat); p2 = vec3_transform(self->collision_model->vertices[indices[1]], &self->mat); p3 = vec3_transform(self->collision_model->vertices[indices[2]], &self->mat); poly.f3++; break; case PRM_TYPE_G3: indices = poly.g3->coords; p1 = vec3_transform(self->collision_model->vertices[indices[0]], &self->mat); p2 = vec3_transform(self->collision_model->vertices[indices[1]], &self->mat); p3 = vec3_transform(self->collision_model->vertices[indices[2]], &self->mat); poly.g3++; break; case PRM_TYPE_FT3: indices = poly.ft3->coords; p1 = vec3_transform(self->collision_model->vertices[indices[0]], &self->mat); p2 = vec3_transform(self->collision_model->vertices[indices[1]], &self->mat); p3 = vec3_transform(self->collision_model->vertices[indices[2]], &self->mat); poly.ft3++; break; case PRM_TYPE_GT3: indices = poly.gt3->coords; p1 = vec3_transform(self->collision_model->vertices[indices[0]], &self->mat); p2 = vec3_transform(self->collision_model->vertices[indices[1]], &self->mat); p3 = vec3_transform(self->collision_model->vertices[indices[2]], &self->mat); poly.gt3++; break; default: break; } // Find polyGon line vectors vec3_t p1p2 = vec3_sub(p2, p1); vec3_t p1p3 = vec3_sub(p3, p1); // Find plane equations vec3_t plane1 = vec3_cross(p1p2, p1p3); for (int vi = 0; vi < 6; vi++) { float dp1 = vec3_dot(vec3_sub(p1, other_points[vi]), plane1); float dp2 = vec3_dot(other_lines[vi], plane1); if (dp2 != 0) { float norm = dp1 / dp2; if ((norm >= 0) && (norm <= 1)) { vec3_t term = vec3_mulf(other_lines[vi], norm); vec3_t res = vec3_add(term, other_points[vi]); vec3_t v0 = vec3_sub(p1, res); vec3_t v1 = vec3_sub(p2, res); vec3_t v2 = vec3_sub(p3, res); float angle = vec3_angle(v0, v1) + vec3_angle(v1, v2) + vec3_angle(v2, v0); if ((angle >= M_PI * 2 - M_PI * 0.1)) { return true; } } } } } return false; } void ship_collide_with_ship(ship_t *self, ship_t *other) { float distance = vec3_len(vec3_sub(self->position, other->position)); // Do a quick distance check; if ships are far apart, remove the collision flag // and early out. if (distance > 960) { flags_rm(self->flags, SHIP_COLL); flags_rm(other->flags, SHIP_COLL); return; } // Ships are close, do a real collision test if (!ship_intersects_ship(self, other)) { return; } // Ships did collide, resolve vec3_t vc = vec3_divf( vec3_add( vec3_mulf(self->velocity, self->mass), vec3_mulf(other->velocity, other->mass) ), self->mass + other->mass ); vec3_t ship_react = vec3_mulf(vec3_sub(vc, self->velocity), 0.25); vec3_t other_react = vec3_mulf(vec3_sub(vc, other->velocity), 0.25); self->position = vec3_sub(self->position, vec3_mulf(self->velocity, 0.015625)); // >> 6 other->position = vec3_sub(other->position, vec3_mulf(other->velocity, 0.015625)); // >> 6 self->velocity = vec3_add(vc, ship_react); other->velocity = vec3_add(vc, other_react); vec3_t res = vec3_sub(self->position, other->position); self->velocity = vec3_add(self->velocity, vec3_mulf(res, 2)); // << 2 self->position = vec3_add(self->position, vec3_mulf(self->velocity, 0.015625)); // >> 6 other->velocity = vec3_sub(other->velocity, vec3_mulf(res, 2)); // << 2 other->position = vec3_add(other->position, vec3_mulf(other->velocity, 0.015625)); // >> 6 if ( flags_not(self->flags, SHIP_COLL) && flags_not(other->flags, SHIP_COLL) && self->last_impact_time > 0.2 ) { self->last_impact_time = 0; vec3_t sound_pos = vec3_mulf(vec3_add(self->position, other->position), 0.5); sfx_play_at(SFX_CRUNCH, sound_pos, vec3(0, 0, 0), 1); } flags_add(self->flags, SHIP_COLL); flags_add(other->flags, SHIP_COLL); }