1use crate::*;
2use boxcars;
3use std::sync::Arc;
4
5fn or_zero_boxcars_3f() -> boxcars::Vector3f {
6 boxcars::Vector3f {
7 x: 0.0,
8 y: 0.0,
9 z: 0.0,
10 }
11}
12
13type RigidBodyArrayResult<F> = SubtrActorResult<[F; 12]>;
14type RigidBodyQuaternionArrayResult<F> = SubtrActorResult<[F; 13]>;
15type RigidBodyBasisArrayResult<F> = SubtrActorResult<[F; 15]>;
16
17pub fn get_rigid_body_properties<F: TryFrom<f32>>(
19 rigid_body: &boxcars::RigidBody,
20) -> RigidBodyArrayResult<F>
21where
22 <F as TryFrom<f32>>::Error: std::fmt::Debug,
23{
24 let linear_velocity = rigid_body
25 .linear_velocity
26 .unwrap_or_else(or_zero_boxcars_3f);
27 let angular_velocity = rigid_body
28 .angular_velocity
29 .unwrap_or_else(or_zero_boxcars_3f);
30 let rotation = rigid_body.rotation;
31 let location = rigid_body.location;
32 let (rx, ry, rz) =
33 glam::quat(rotation.x, rotation.y, rotation.z, rotation.w).to_euler(glam::EulerRot::XYZ);
34 convert_all_floats!(
35 location.x,
36 location.y,
37 location.z,
38 rx,
39 ry,
40 rz,
41 linear_velocity.x,
42 linear_velocity.y,
43 linear_velocity.z,
44 angular_velocity.x,
45 angular_velocity.y,
46 angular_velocity.z,
47 )
48}
49
50pub fn get_rigid_body_properties_quaternion<F: TryFrom<f32>>(
52 rigid_body: &boxcars::RigidBody,
53) -> RigidBodyQuaternionArrayResult<F>
54where
55 <F as TryFrom<f32>>::Error: std::fmt::Debug,
56{
57 let linear_velocity = rigid_body
58 .linear_velocity
59 .unwrap_or_else(or_zero_boxcars_3f);
60 let angular_velocity = rigid_body
61 .angular_velocity
62 .unwrap_or_else(or_zero_boxcars_3f);
63 let rotation = rigid_body.rotation;
64 let location = rigid_body.location;
65 convert_all_floats!(
66 location.x,
67 location.y,
68 location.z,
69 rotation.x,
70 rotation.y,
71 rotation.z,
72 rotation.w,
73 linear_velocity.x,
74 linear_velocity.y,
75 linear_velocity.z,
76 angular_velocity.x,
77 angular_velocity.y,
78 angular_velocity.z,
79 )
80}
81
82pub fn get_rigid_body_properties_basis<F: TryFrom<f32>>(
84 rigid_body: &boxcars::RigidBody,
85) -> RigidBodyBasisArrayResult<F>
86where
87 <F as TryFrom<f32>>::Error: std::fmt::Debug,
88{
89 let linear_velocity = rigid_body
90 .linear_velocity
91 .unwrap_or_else(or_zero_boxcars_3f);
92 let angular_velocity = rigid_body
93 .angular_velocity
94 .unwrap_or_else(or_zero_boxcars_3f);
95 let rotation = rigid_body.rotation;
96 let location = rigid_body.location;
97 let quat = glam::quat(rotation.x, rotation.y, rotation.z, rotation.w);
98 let forward = quat.mul_vec3(glam::Vec3::X);
99 let up = quat.mul_vec3(glam::Vec3::Z);
100 convert_all_floats!(
101 location.x,
102 location.y,
103 location.z,
104 forward.x,
105 forward.y,
106 forward.z,
107 up.x,
108 up.y,
109 up.z,
110 linear_velocity.x,
111 linear_velocity.y,
112 linear_velocity.z,
113 angular_velocity.x,
114 angular_velocity.y,
115 angular_velocity.z,
116 )
117}
118
119pub fn get_rigid_body_properties_no_velocities<F: TryFrom<f32>>(
121 rigid_body: &boxcars::RigidBody,
122) -> SubtrActorResult<[F; 7]>
123where
124 <F as TryFrom<f32>>::Error: std::fmt::Debug,
125{
126 let rotation = rigid_body.rotation;
127 let location = rigid_body.location;
128 convert_all_floats!(
129 location.x, location.y, location.z, rotation.x, rotation.y, rotation.z, rotation.w
130 )
131}
132
133fn default_rb_state<F: TryFrom<f32>>() -> RigidBodyArrayResult<F>
134where
135 <F as TryFrom<f32>>::Error: std::fmt::Debug,
136{
137 convert_all!(
138 convert_float_conversion_error,
139 0.0,
140 0.0,
141 0.0,
142 0.0,
143 0.0,
144 0.0,
145 0.0,
146 0.0,
147 0.0,
148 0.0,
149 0.0,
150 0.0,
151 )
152}
153
154fn default_rb_state_quaternion<F: TryFrom<f32>>() -> RigidBodyQuaternionArrayResult<F>
155where
156 <F as TryFrom<f32>>::Error: std::fmt::Debug,
157{
158 convert_all_floats!(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,)
159}
160
161fn default_rb_state_basis<F: TryFrom<f32>>() -> RigidBodyBasisArrayResult<F>
162where
163 <F as TryFrom<f32>>::Error: std::fmt::Debug,
164{
165 convert_all_floats!(0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,)
166}
167
168fn default_rb_state_no_velocities<F: TryFrom<f32>>() -> SubtrActorResult<[F; 7]>
169where
170 <F as TryFrom<f32>>::Error: std::fmt::Debug,
171{
172 convert_all_floats!(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,)
173}
174
175build_global_feature_adder!(
176 SecondsRemaining,
177 |_, processor: &ReplayProcessor, _frame, _index, _current_time| {
178 convert_all_floats!(processor.get_seconds_remaining().unwrap_or(0) as f32)
179 },
180 "seconds remaining"
181);
182
183build_global_feature_adder!(
184 CurrentTime,
185 |_, _processor, _frame, _index, current_time: f32| { convert_all_floats!(current_time) },
186 "current time"
187);
188
189build_global_feature_adder!(
190 ReplicatedStateName,
191 |_, processor: &ReplayProcessor, _frame, _index, _current_time| {
192 convert_all_floats!(processor.get_replicated_state_name().unwrap_or(0) as f32)
193 },
194 "game state"
195);
196
197build_global_feature_adder!(
198 ReplicatedGameStateTimeRemaining,
199 |_, processor: &ReplayProcessor, _frame, _index, _current_time| {
200 convert_all_floats!(processor
201 .get_replicated_game_state_time_remaining()
202 .unwrap_or(0) as f32)
203 },
204 "kickoff countdown"
205);
206
207build_global_feature_adder!(
208 BallHasBeenHit,
209 |_, processor: &ReplayProcessor, _frame, _index, _current_time| {
210 convert_all_floats!(if processor.get_ball_has_been_hit().unwrap_or(false) {
211 1.0
212 } else {
213 0.0
214 })
215 },
216 "ball has been hit"
217);
218
219build_global_feature_adder!(
220 FrameTime,
221 |_, _processor, frame: &boxcars::Frame, _index, _current_time| {
222 convert_all_floats!(frame.time)
223 },
224 "frame time"
225);
226
227build_global_feature_adder!(
228 BallRigidBody,
229 |_, processor: &ReplayProcessor, _frame, _index, _current_time| {
230 processor
231 .get_normalized_ball_rigid_body()
232 .and_then(|rb| get_rigid_body_properties(&rb))
233 .or_else(|_| default_rb_state())
234 },
235 "Ball - position x",
236 "Ball - position y",
237 "Ball - position z",
238 "Ball - rotation x",
239 "Ball - rotation y",
240 "Ball - rotation z",
241 "Ball - linear velocity x",
242 "Ball - linear velocity y",
243 "Ball - linear velocity z",
244 "Ball - angular velocity x",
245 "Ball - angular velocity y",
246 "Ball - angular velocity z",
247);
248
249build_global_feature_adder!(
250 BallRigidBodyNoVelocities,
251 |_, processor: &ReplayProcessor, _frame, _index, _current_time| {
252 processor
253 .get_normalized_ball_rigid_body()
254 .and_then(|rb| get_rigid_body_properties_no_velocities(&rb))
255 .or_else(|_| default_rb_state_no_velocities())
256 },
257 "Ball - position x",
258 "Ball - position y",
259 "Ball - position z",
260 "Ball - rotation x",
261 "Ball - rotation y",
262 "Ball - rotation z",
263 "Ball - rotation w",
264);
265
266build_global_feature_adder!(
267 VelocityAddedBallRigidBodyNoVelocities,
268 |_, processor: &ReplayProcessor, _frame, _index, current_time: f32| {
269 processor
270 .get_velocity_applied_ball_rigid_body(current_time)
271 .and_then(|rb| get_rigid_body_properties_no_velocities(&rb))
272 .or_else(|_| default_rb_state_no_velocities())
273 },
274 "Ball - position x",
275 "Ball - position y",
276 "Ball - position z",
277 "Ball - rotation x",
278 "Ball - rotation y",
279 "Ball - rotation z",
280 "Ball - rotation w",
281);
282
283#[derive(derive_new::new)]
285pub struct InterpolatedBallRigidBodyNoVelocities<F> {
286 close_enough_to_frame_time: f32,
287 _zero: std::marker::PhantomData<F>,
288}
289
290impl<F> InterpolatedBallRigidBodyNoVelocities<F> {
291 pub fn arc_new(close_enough_to_frame_time: f32) -> Arc<Self> {
293 Arc::new(Self::new(close_enough_to_frame_time))
294 }
295}
296
297global_feature_adder!(
298 InterpolatedBallRigidBodyNoVelocities,
299 |s: &InterpolatedBallRigidBodyNoVelocities<F>,
300 processor: &ReplayProcessor,
301 _frame: &boxcars::Frame,
302 _index,
303 current_time: f32| {
304 processor
305 .get_interpolated_ball_rigid_body(current_time, s.close_enough_to_frame_time)
306 .map(|v| get_rigid_body_properties_no_velocities(&v))
307 .unwrap_or_else(|_| default_rb_state_no_velocities())
308 },
309 "Ball - position x",
310 "Ball - position y",
311 "Ball - position z",
312 "Ball - rotation x",
313 "Ball - rotation y",
314 "Ball - rotation z",
315 "Ball - rotation w",
316);
317
318build_player_feature_adder!(
319 PlayerRigidBody,
320 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
321 if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
322 get_rigid_body_properties(&rb)
323 } else {
324 default_rb_state()
325 }
326 },
327 "position x",
328 "position y",
329 "position z",
330 "rotation x",
331 "rotation y",
332 "rotation z",
333 "linear velocity x",
334 "linear velocity y",
335 "linear velocity z",
336 "angular velocity x",
337 "angular velocity y",
338 "angular velocity z",
339);
340
341build_player_feature_adder!(
342 PlayerRigidBodyNoVelocities,
343 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
344 if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
345 get_rigid_body_properties_no_velocities(&rb)
346 } else {
347 default_rb_state_no_velocities()
348 }
349 },
350 "position x",
351 "position y",
352 "position z",
353 "rotation x",
354 "rotation y",
355 "rotation z",
356 "rotation w"
357);
358
359build_player_feature_adder!(
360 VelocityAddedPlayerRigidBodyNoVelocities,
361 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, current_time: f32| {
362 if let Ok(rb) = processor.get_velocity_applied_player_rigid_body(player_id, current_time) {
363 get_rigid_body_properties_no_velocities(&rb)
364 } else {
365 default_rb_state_no_velocities()
366 }
367 },
368 "position x",
369 "position y",
370 "position z",
371 "rotation x",
372 "rotation y",
373 "rotation z",
374 "rotation w"
375);
376
377#[derive(derive_new::new)]
379pub struct InterpolatedPlayerRigidBodyNoVelocities<F> {
380 close_enough_to_frame_time: f32,
381 _zero: std::marker::PhantomData<F>,
382}
383
384impl<F> InterpolatedPlayerRigidBodyNoVelocities<F> {
385 pub fn arc_new(close_enough_to_frame_time: f32) -> Arc<Self> {
387 Arc::new(Self::new(close_enough_to_frame_time))
388 }
389}
390
391player_feature_adder!(
392 InterpolatedPlayerRigidBodyNoVelocities,
393 |s: &InterpolatedPlayerRigidBodyNoVelocities<F>,
394 player_id: &PlayerId,
395 processor: &ReplayProcessor,
396 _frame: &boxcars::Frame,
397 _index,
398 current_time: f32| {
399 processor
400 .get_interpolated_player_rigid_body(
401 player_id,
402 current_time,
403 s.close_enough_to_frame_time,
404 )
405 .map(|v| get_rigid_body_properties_no_velocities(&v))
406 .unwrap_or_else(|_| default_rb_state_no_velocities())
407 },
408 "i position x",
409 "i position y",
410 "i position z",
411 "i rotation x",
412 "i rotation y",
413 "i rotation z",
414 "i rotation w"
415);
416
417build_player_feature_adder!(
418 PlayerRelativeBallPosition,
419 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
420 let relative_position = processor
421 .get_normalized_player_rigid_body(player_id)
422 .ok()
423 .zip(processor.get_normalized_ball_rigid_body().ok())
424 .map(|(player_rigid_body, ball_rigid_body)| {
425 vec_to_glam(&ball_rigid_body.location) - vec_to_glam(&player_rigid_body.location)
426 })
427 .unwrap_or(glam::f32::Vec3::ZERO);
428 convert_all_floats!(
429 relative_position.x,
430 relative_position.y,
431 relative_position.z
432 )
433 },
434 "relative ball position x",
435 "relative ball position y",
436 "relative ball position z"
437);
438
439build_player_feature_adder!(
440 PlayerRelativeBallVelocity,
441 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
442 let relative_velocity = processor
443 .get_normalized_player_rigid_body(player_id)
444 .ok()
445 .zip(processor.get_normalized_ball_rigid_body().ok())
446 .map(|(player_rigid_body, ball_rigid_body)| {
447 vec_to_glam(
448 &ball_rigid_body
449 .linear_velocity
450 .unwrap_or_else(or_zero_boxcars_3f),
451 ) - vec_to_glam(
452 &player_rigid_body
453 .linear_velocity
454 .unwrap_or_else(or_zero_boxcars_3f),
455 )
456 })
457 .unwrap_or(glam::f32::Vec3::ZERO);
458 convert_all_floats!(
459 relative_velocity.x,
460 relative_velocity.y,
461 relative_velocity.z
462 )
463 },
464 "relative ball velocity x",
465 "relative ball velocity y",
466 "relative ball velocity z"
467);
468
469build_player_feature_adder!(
470 PlayerLocalRelativeBallPosition,
471 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
472 let local_relative_position = processor
473 .get_normalized_player_rigid_body(player_id)
474 .ok()
475 .zip(processor.get_normalized_ball_rigid_body().ok())
476 .map(|(player_rigid_body, ball_rigid_body)| {
477 let player_rotation = player_rigid_body.rotation;
478 let player_quat = glam::quat(
479 player_rotation.x,
480 player_rotation.y,
481 player_rotation.z,
482 player_rotation.w,
483 );
484 let world_relative_position = vec_to_glam(&ball_rigid_body.location)
485 - vec_to_glam(&player_rigid_body.location);
486 player_quat.inverse().mul_vec3(world_relative_position)
487 })
488 .unwrap_or(glam::f32::Vec3::ZERO);
489 convert_all_floats!(
490 local_relative_position.x,
491 local_relative_position.y,
492 local_relative_position.z
493 )
494 },
495 "local relative ball position x",
496 "local relative ball position y",
497 "local relative ball position z"
498);
499
500build_player_feature_adder!(
501 PlayerLocalRelativeBallVelocity,
502 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
503 let local_relative_velocity = processor
504 .get_normalized_player_rigid_body(player_id)
505 .ok()
506 .zip(processor.get_normalized_ball_rigid_body().ok())
507 .map(|(player_rigid_body, ball_rigid_body)| {
508 let player_rotation = player_rigid_body.rotation;
509 let player_quat = glam::quat(
510 player_rotation.x,
511 player_rotation.y,
512 player_rotation.z,
513 player_rotation.w,
514 );
515 let world_relative_velocity = vec_to_glam(
516 &ball_rigid_body
517 .linear_velocity
518 .unwrap_or_else(or_zero_boxcars_3f),
519 ) - vec_to_glam(
520 &player_rigid_body
521 .linear_velocity
522 .unwrap_or_else(or_zero_boxcars_3f),
523 );
524 player_quat.inverse().mul_vec3(world_relative_velocity)
525 })
526 .unwrap_or(glam::f32::Vec3::ZERO);
527 convert_all_floats!(
528 local_relative_velocity.x,
529 local_relative_velocity.y,
530 local_relative_velocity.z
531 )
532 },
533 "local relative ball velocity x",
534 "local relative ball velocity y",
535 "local relative ball velocity z"
536);
537
538build_player_feature_adder!(
539 PlayerBallDistance,
540 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
541 let distance = processor
542 .get_normalized_player_rigid_body(player_id)
543 .ok()
544 .zip(processor.get_normalized_ball_rigid_body().ok())
545 .map(|(player_rigid_body, ball_rigid_body)| {
546 (vec_to_glam(&player_rigid_body.location) - vec_to_glam(&ball_rigid_body.location))
547 .length()
548 })
549 .unwrap_or(0.0);
550 convert_all_floats!(distance)
551 },
552 "distance to ball"
553);
554
555build_player_feature_adder!(
556 PlayerBoost,
557 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
558 convert_all_floats!(processor.get_player_boost_level(player_id).unwrap_or(0.0))
559 },
560 "boost level (raw replay units)"
561);
562
563fn u8_get_f32(v: u8) -> SubtrActorResult<f32> {
564 Ok(v.into())
565}
566
567build_player_feature_adder!(
568 PlayerJump,
569 |_,
570 player_id: &PlayerId,
571 processor: &ReplayProcessor,
572 _frame,
573 _frame_number,
574 _current_time: f32| {
575 convert_all_floats!(
576 processor
577 .get_dodge_active(player_id)
578 .and_then(u8_get_f32)
579 .unwrap_or(0.0),
580 processor
581 .get_jump_active(player_id)
582 .and_then(u8_get_f32)
583 .unwrap_or(0.0),
584 processor
585 .get_double_jump_active(player_id)
586 .and_then(u8_get_f32)
587 .unwrap_or(0.0),
588 )
589 },
590 "dodge active",
591 "jump active",
592 "double jump active"
593);
594
595build_player_feature_adder!(
596 PlayerAnyJump,
597 |_,
598 player_id: &PlayerId,
599 processor: &ReplayProcessor,
600 _frame,
601 _frame_number,
602 _current_time: f32| {
603 let dodge_is_active = processor.get_dodge_active(player_id).unwrap_or(0) % 2;
604 let jump_is_active = processor.get_jump_active(player_id).unwrap_or(0) % 2;
605 let double_jump_is_active = processor.get_double_jump_active(player_id).unwrap_or(0) % 2;
606 let value: f32 = [dodge_is_active, jump_is_active, double_jump_is_active]
607 .into_iter()
608 .enumerate()
609 .map(|(index, is_active)| (1 << index) * is_active)
610 .sum::<u8>() as f32;
611 convert_all_floats!(value)
612 },
613 "any_jump_active"
614);
615
616build_player_feature_adder!(
617 PlayerDodgeRefreshed,
618 |_,
619 player_id: &PlayerId,
620 processor: &ReplayProcessor,
621 _frame,
622 _frame_number,
623 _current_time: f32| {
624 let dodge_refresh_count = processor
625 .current_frame_dodge_refreshed_events()
626 .iter()
627 .filter(|event| &event.player == player_id)
628 .count() as f32;
629 convert_all_floats!(dodge_refresh_count)
630 },
631 "dodge refresh count"
632);
633
634const DEMOLISH_APPEARANCE_FRAME_COUNT: usize = 30;
635
636build_player_feature_adder!(
637 PlayerDemolishedBy,
638 |_,
639 player_id: &PlayerId,
640 processor: &ReplayProcessor,
641 _frame,
642 frame_number,
643 _current_time: f32| {
644 let demolisher_index = processor
645 .demolishes
646 .iter()
647 .find(|demolish_info| {
648 &demolish_info.victim == player_id
649 && frame_number - demolish_info.frame < DEMOLISH_APPEARANCE_FRAME_COUNT
650 })
651 .map(|demolish_info| {
652 processor
653 .iter_player_ids_in_order()
654 .position(|player_id| player_id == &demolish_info.attacker)
655 .unwrap_or_else(|| processor.iter_player_ids_in_order().count())
656 })
657 .and_then(|v| i32::try_from(v).ok())
658 .unwrap_or(-1);
659 convert_all_floats!(demolisher_index as f32)
660 },
661 "player demolished by"
662);
663
664build_player_feature_adder!(
665 PlayerRigidBodyQuaternions,
666 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
667 if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
668 let rotation = rb.rotation;
669 let location = rb.location;
670 convert_all_floats!(
671 location.x, location.y, location.z, rotation.x, rotation.y, rotation.z, rotation.w
672 )
673 } else {
674 convert_all_floats!(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)
675 }
676 },
677 "position x",
678 "position y",
679 "position z",
680 "quaternion x",
681 "quaternion y",
682 "quaternion z",
683 "quaternion w"
684);
685
686build_player_feature_adder!(
687 PlayerRigidBodyQuaternionVelocities,
688 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
689 if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
690 get_rigid_body_properties_quaternion(&rb)
691 } else {
692 default_rb_state_quaternion()
693 }
694 },
695 "position x",
696 "position y",
697 "position z",
698 "quaternion x",
699 "quaternion y",
700 "quaternion z",
701 "quaternion w",
702 "linear velocity x",
703 "linear velocity y",
704 "linear velocity z",
705 "angular velocity x",
706 "angular velocity y",
707 "angular velocity z",
708);
709
710build_player_feature_adder!(
711 PlayerRigidBodyBasis,
712 |_, player_id: &PlayerId, processor: &ReplayProcessor, _frame, _index, _current_time: f32| {
713 if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
714 get_rigid_body_properties_basis(&rb)
715 } else {
716 default_rb_state_basis()
717 }
718 },
719 "position x",
720 "position y",
721 "position z",
722 "forward x",
723 "forward y",
724 "forward z",
725 "up x",
726 "up y",
727 "up z",
728 "linear velocity x",
729 "linear velocity y",
730 "linear velocity z",
731 "angular velocity x",
732 "angular velocity y",
733 "angular velocity z",
734);
735
736build_global_feature_adder!(
737 BallRigidBodyQuaternions,
738 |_, processor: &ReplayProcessor, _frame, _index, _current_time| {
739 match processor.get_normalized_ball_rigid_body() {
740 Ok(rb) => {
741 let rotation = rb.rotation;
742 let location = rb.location;
743 convert_all_floats!(
744 location.x, location.y, location.z, rotation.x, rotation.y, rotation.z,
745 rotation.w
746 )
747 }
748 Err(_) => convert_all_floats!(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0),
749 }
750 },
751 "Ball - position x",
752 "Ball - position y",
753 "Ball - position z",
754 "Ball - quaternion x",
755 "Ball - quaternion y",
756 "Ball - quaternion z",
757 "Ball - quaternion w"
758);
759
760build_global_feature_adder!(
761 BallRigidBodyQuaternionVelocities,
762 |_, processor: &ReplayProcessor, _frame, _index, _current_time| {
763 processor
764 .get_normalized_ball_rigid_body()
765 .and_then(|rb| get_rigid_body_properties_quaternion(&rb))
766 .or_else(|_| default_rb_state_quaternion())
767 },
768 "Ball - position x",
769 "Ball - position y",
770 "Ball - position z",
771 "Ball - quaternion x",
772 "Ball - quaternion y",
773 "Ball - quaternion z",
774 "Ball - quaternion w",
775 "Ball - linear velocity x",
776 "Ball - linear velocity y",
777 "Ball - linear velocity z",
778 "Ball - angular velocity x",
779 "Ball - angular velocity y",
780 "Ball - angular velocity z",
781);
782
783build_global_feature_adder!(
784 BallRigidBodyBasis,
785 |_, processor: &ReplayProcessor, _frame, _index, _current_time| {
786 processor
787 .get_normalized_ball_rigid_body()
788 .and_then(|rb| get_rigid_body_properties_basis(&rb))
789 .or_else(|_| default_rb_state_basis())
790 },
791 "Ball - position x",
792 "Ball - position y",
793 "Ball - position z",
794 "Ball - forward x",
795 "Ball - forward y",
796 "Ball - forward z",
797 "Ball - up x",
798 "Ball - up y",
799 "Ball - up z",
800 "Ball - linear velocity x",
801 "Ball - linear velocity y",
802 "Ball - linear velocity z",
803 "Ball - angular velocity x",
804 "Ball - angular velocity y",
805 "Ball - angular velocity z",
806);