1use crate::{SubtrActorError, SubtrActorErrorVariant, SubtrActorResult};
2
3pub fn vec_to_glam(v: &boxcars::Vector3f) -> glam::f32::Vec3 {
4 glam::f32::Vec3::new(v.x, v.y, v.z)
5}
6
7pub fn glam_to_vec(v: &glam::f32::Vec3) -> boxcars::Vector3f {
8 boxcars::Vector3f {
9 x: v.x,
10 y: v.y,
11 z: v.z,
12 }
13}
14
15pub fn quat_to_glam(q: &boxcars::Quaternion) -> glam::Quat {
16 glam::Quat::from_xyzw(q.x, q.y, q.z, q.w)
17}
18
19pub fn glam_to_quat(rotation: &glam::Quat) -> boxcars::Quaternion {
20 boxcars::Quaternion {
21 x: rotation.x,
22 y: rotation.y,
23 z: rotation.z,
24 w: rotation.w,
25 }
26}
27
28pub fn apply_velocities_to_rigid_body(
29 rigid_body: &boxcars::RigidBody,
30 time_delta: f32,
31) -> boxcars::RigidBody {
32 let mut interpolated = *rigid_body;
33 if time_delta == 0.0 {
34 return interpolated;
35 }
36 let linear_velocity = interpolated.linear_velocity.unwrap_or(boxcars::Vector3f {
37 x: 0.0,
38 y: 0.0,
39 z: 0.0,
40 });
41 let location = vec_to_glam(&rigid_body.location) + (time_delta * vec_to_glam(&linear_velocity));
42 interpolated.location = glam_to_vec(&location);
43 interpolated.rotation = apply_angular_velocity(rigid_body, time_delta);
44 interpolated
45}
46
47pub(crate) fn touch_candidate_rank(
54 ball_body: &boxcars::RigidBody,
55 player_body: &boxcars::RigidBody,
56) -> Option<(f32, f32)> {
57 const TOUCH_LOOKBACK_SECONDS: f32 = 0.12;
58 const TOUCH_LOOKAHEAD_SECONDS: f32 = 0.03;
59
60 let relative_position = vec_to_glam(&player_body.location) - vec_to_glam(&ball_body.location);
61 let current_distance = relative_position.length();
62 if !current_distance.is_finite() {
63 return None;
64 }
65
66 let relative_velocity =
67 vec_to_glam(&player_body.linear_velocity.unwrap_or(boxcars::Vector3f {
68 x: 0.0,
69 y: 0.0,
70 z: 0.0,
71 })) - vec_to_glam(&ball_body.linear_velocity.unwrap_or(boxcars::Vector3f {
72 x: 0.0,
73 y: 0.0,
74 z: 0.0,
75 }));
76 let relative_speed_squared = relative_velocity.length_squared();
77 let closest_time = if relative_speed_squared > f32::EPSILON {
78 (-relative_position.dot(relative_velocity) / relative_speed_squared)
79 .clamp(-TOUCH_LOOKBACK_SECONDS, TOUCH_LOOKAHEAD_SECONDS)
80 } else {
81 0.0
82 };
83 let closest_distance = (relative_position + relative_velocity * closest_time).length();
84 if !closest_distance.is_finite() {
85 return None;
86 }
87
88 Some((closest_distance, current_distance))
89}
90
91fn apply_angular_velocity(rigid_body: &boxcars::RigidBody, time_delta: f32) -> boxcars::Quaternion {
92 let rbav = rigid_body.angular_velocity.unwrap_or(boxcars::Vector3f {
95 x: 0.0,
96 y: 0.0,
97 z: 0.0,
98 });
99 let angular_velocity = glam::Vec3::new(rbav.x, rbav.y, rbav.z);
100 let magnitude = angular_velocity.length();
101 let angular_velocity_unit_vector = angular_velocity.normalize_or_zero();
102
103 let mut rotation = glam::Quat::from_xyzw(
104 rigid_body.rotation.x,
105 rigid_body.rotation.y,
106 rigid_body.rotation.z,
107 rigid_body.rotation.w,
108 );
109
110 if angular_velocity_unit_vector.length() != 0.0 {
111 let delta_rotation =
112 glam::Quat::from_axis_angle(angular_velocity_unit_vector, magnitude * time_delta);
113 rotation *= delta_rotation;
114 }
115
116 boxcars::Quaternion {
117 x: rotation.x,
118 y: rotation.y,
119 z: rotation.z,
120 w: rotation.w,
121 }
122}
123
124pub fn get_interpolated_rigid_body(
138 start_body: &boxcars::RigidBody,
139 start_time: f32,
140 end_body: &boxcars::RigidBody,
141 end_time: f32,
142 time: f32,
143) -> SubtrActorResult<boxcars::RigidBody> {
144 if !(start_time <= time && time <= end_time) {
145 return SubtrActorError::new_result(SubtrActorErrorVariant::InterpolationTimeOrderError {
146 start_time,
147 time,
148 end_time,
149 });
150 }
151
152 let duration = end_time - start_time;
153 let interpolation_amount = (time - start_time) / duration;
154 let start_position = vec_to_glam(&start_body.location);
155 let end_position = vec_to_glam(&end_body.location);
156 let interpolated_location = start_position.lerp(end_position, interpolation_amount);
157 let start_rotation = quat_to_glam(&start_body.rotation);
158 let end_rotation = quat_to_glam(&end_body.rotation);
159 let interpolated_rotation = start_rotation.slerp(end_rotation, interpolation_amount);
160
161 Ok(boxcars::RigidBody {
162 location: glam_to_vec(&interpolated_location),
163 rotation: glam_to_quat(&interpolated_rotation),
164 sleeping: start_body.sleeping,
165 linear_velocity: start_body.linear_velocity,
166 angular_velocity: start_body.angular_velocity,
167 })
168}