collision.c (2062B)
1 struct WallCollision 2 { 3 bool collision_occurred; 4 f32 distance_scale_factor; 5 }; 6 7 internal struct WallCollision get_wall_collision(v2 entity_p_initial, v2 entity_p_final, segment wall, v2 wall_normal) 8 9 { 10 struct WallCollision result = {0}; 11 result.collision_occurred = false; 12 13 assert(math_v2_le(wall.min, wall.max)); 14 15 bool wall_is_horizontal = wall.min.y == wall.max.y; 16 bool wall_is_vertical = wall.min.x == wall.max.x; 17 assert(wall_is_horizontal || wall_is_vertical); 18 19 enum VectorAxis wall_axis = AXIS_X; 20 enum VectorAxis wall_normal_axis = AXIS_Y; 21 if (wall_is_vertical) 22 { 23 wall_axis = AXIS_Y; 24 wall_normal_axis = AXIS_X; 25 } 26 27 v2 player_delta = math_v2_s(entity_p_final, entity_p_initial); 28 29 // NOTE(amin): We needn't normalize player_delta here because its magnitude 30 // becomes irrelevant as the value of cos(theta) nears zero, and we are 31 // concerned only about the behavior near pi/2 and 3pi/2, where cos(theta) 32 // is zero. Therefore we simply treat player_delta as if it _were_ 33 // normalized. 34 f32 cos_theta = math_v2_dot(player_delta, wall_normal); 35 36 bool theta_is_obtuse = cos_theta < 0.0f; 37 if (theta_is_obtuse) 38 { 39 f32 wall_position = wall.min.E[wall_normal_axis]; 40 41 // Not to be confused with the anime about street racing 42 f32 initial_d = wall_position - entity_p_initial.E[wall_normal_axis]; 43 44 f32 segment_scale_factor = initial_d / player_delta.E[wall_normal_axis]; 45 v2 collision_point = math_v2_a(entity_p_initial, math_v2f_m(player_delta, segment_scale_factor)); 46 47 if (math_in_interval_open(segment_scale_factor, 0.0f, 1.0f)) 48 { 49 if (math_in_interval_open( 50 collision_point.E[wall_axis], 51 wall.min.E[wall_axis], 52 wall.max.E[wall_axis])) 53 { 54 result.collision_occurred = true; 55 result.distance_scale_factor = math_max(0.0f, segment_scale_factor); 56 } 57 } 58 } 59 60 return result; 61 }