lotus_shared/animation.rs
1//! Animations-Kinematik und Handles für scriptseitig zugreifbare Animationen.
2//!
3//! Animation kinematics and handles for script-accessible animations.
4
5#[cfg(feature = "bevy")]
6use bevy::prelude::Component;
7use glam::Vec3;
8#[cfg(feature = "ffi")]
9use lotus_script_sys::FfiObject;
10use serde::{Deserialize, Serialize};
11
12/// Fehler beim Auflösen oder Abfragen von Animationen.
13///
14/// Errors returned when resolving or querying animations.
15#[derive(Debug, thiserror::Error)]
16pub enum AnimationError {
17 /// Die angeforderte Animation wurde nicht gefunden.
18 ///
19 /// The requested animation was not found.
20 #[error("animation not found")]
21 AnimationNotFound = 65536,
22 /// Ein unbekannter Animationsfehler ist aufgetreten.
23 ///
24 /// An unknown animation error occurred.
25 #[error("unknown error")]
26 Unknown = 0,
27}
28
29impl From<u32> for AnimationError {
30 fn from(value: u32) -> Self {
31 match value {
32 65536 => AnimationError::AnimationNotFound,
33 _ => AnimationError::Unknown,
34 }
35 }
36}
37
38/// Lineare und Winkelgeschwindigkeit sowie -beschleunigung im globalen Simulationskoordinatensystem.
39///
40/// Linear and angular velocity and acceleration in the global simulation frame.
41#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
42#[cfg_attr(feature = "bevy", derive(Component))]
43pub struct AccelerationVelocity {
44 /// Lineare Geschwindigkeit in m/s.
45 ///
46 /// Linear velocity in m/s.
47 pub linear_velocity: Vec3,
48 /// Lineare Beschleunigung in m/s².
49 ///
50 /// Linear acceleration in m/s².
51 pub linear_acceleration: Vec3,
52 /// Winkelgeschwindigkeit in rad/s.
53 ///
54 /// Angular velocity in rad/s.
55 pub angular_velocity: Vec3,
56 /// Winkelbeschleunigung in rad/s².
57 ///
58 /// Angular acceleration in rad/s².
59 pub angular_acceleration: Vec3,
60}
61
62impl AccelerationVelocity {
63 /// Erstellt achslokale Kinematik in LOTUS-Koordinaten (X = quer, Y = längs, Z = vertikal)
64 /// und rotiert sie ins globale Simulationskoordinatensystem.
65 ///
66 /// `*_derivation`-Werte sind Ableitungen bzgl. der Längswegstrecke (gleicher Parameter wie
67 /// `longitudinal_velocity`): Gleiskrümmung über `inv_radius` plus optionale Unregelmäßigkeiten /
68 /// Höhenneigungen (`lateral_*`, `elevation_*`).
69 ///
70 /// Builds axle-local kinematics in LOTUS coordinates (X = lateral, Y = longitudinal, Z = vertical)
71 /// and rotates them into the global simulation frame.
72 ///
73 /// `*_derivation` values are derivatives w.r.t. longitudinal path distance (same parameter as
74 /// `longitudinal_velocity`): track curvature via `inv_radius`, plus optional irregularity /
75 /// elevation slopes (`lateral_*`, `elevation_*`).
76 #[allow(clippy::too_many_arguments)]
77 pub fn from_rail_axle_local(
78 longitudinal_velocity: f32,
79 longitudinal_acceleration: f32,
80 inv_radius: f32,
81 lateral_derivation: f32,
82 lateral_second_derivation: f32,
83 elevation_derivation: f32,
84 elevation_second_derivation: f32,
85 axle_rotation: glam::Quat,
86 ) -> Self {
87 let v = longitudinal_velocity;
88 let a_long = longitudinal_acceleration;
89 let lateral_acceleration =
90 v * v * (inv_radius + lateral_second_derivation) + lateral_derivation * a_long;
91 let vertical_acceleration =
92 elevation_second_derivation * v * v + elevation_derivation * a_long;
93
94 let local = Self {
95 linear_velocity: Vec3::new(lateral_derivation * v, v, elevation_derivation * v),
96 linear_acceleration: Vec3::new(lateral_acceleration, a_long, vertical_acceleration),
97 angular_velocity: Vec3::new(0.0, 0.0, v * inv_radius),
98 angular_acceleration: Vec3::new(0.0, 0.0, a_long * inv_radius),
99 };
100
101 local.transform_axes_to_global(axle_rotation)
102 }
103
104 /// Rotiert Geschwindigkeits- und Beschleunigungsvektoren ins globale Koordinatensystem.
105 ///
106 /// Rotates linear/angular velocity and acceleration vectors into the global frame.
107 pub fn transform_axes_to_global(self, rotation: glam::Quat) -> Self {
108 Self {
109 linear_velocity: rotation * self.linear_velocity,
110 linear_acceleration: rotation * self.linear_acceleration,
111 angular_velocity: rotation * self.angular_velocity,
112 angular_acceleration: rotation * self.angular_acceleration,
113 }
114 }
115
116 /// Rotiert Geschwindigkeits- und Beschleunigungsvektoren ins lokale Koordinatensystem.
117 ///
118 /// Rotates linear/angular velocity and acceleration vectors into a local frame.
119 pub fn transform_axes_to_local(self, rotation: glam::Quat) -> Self {
120 let inv = rotation.inverse();
121 Self {
122 linear_velocity: inv * self.linear_velocity,
123 linear_acceleration: inv * self.linear_acceleration,
124 angular_velocity: inv * self.angular_velocity,
125 angular_acceleration: inv * self.angular_acceleration,
126 }
127 }
128
129 /// Gibt den komponentenweisen Mittelwert zweier kinematischer Zustände zurück.
130 ///
131 /// Returns the component-wise average of two kinematic states.
132 pub fn average(self, other: Self) -> Self {
133 Self {
134 linear_velocity: 0.5 * (self.linear_velocity + other.linear_velocity),
135 linear_acceleration: 0.5 * (self.linear_acceleration + other.linear_acceleration),
136 angular_velocity: 0.5 * (self.angular_velocity + other.angular_velocity),
137 angular_acceleration: 0.5 * (self.angular_acceleration + other.angular_acceleration),
138 }
139 }
140
141 /// Kombiniert diesen lokalen kinematischen Zustand mit einem übergeordneten starren Körper.
142 ///
143 /// Combines this local kinematic state with a parent rigid body.
144 pub fn relative_to_parent(
145 self,
146 parent: &AccelerationVelocity,
147 position_relative_to_parent: Vec3,
148 ) -> Self {
149 Self {
150 angular_velocity: parent.angular_velocity + self.angular_velocity,
151 angular_acceleration: parent.angular_acceleration
152 + self.angular_acceleration
153 + parent.angular_velocity.cross(self.angular_velocity),
154 linear_velocity: parent.linear_velocity
155 + parent.angular_velocity.cross(position_relative_to_parent)
156 + self.linear_velocity,
157
158 linear_acceleration: parent.linear_acceleration
159 + parent
160 .angular_acceleration
161 .cross(position_relative_to_parent)
162 + parent
163 .angular_velocity
164 .cross(parent.angular_velocity.cross(position_relative_to_parent))
165 + (2.0 * parent.angular_velocity).cross(self.linear_velocity)
166 + self.linear_acceleration,
167 }
168 }
169
170 /// Lineare und Winkelbeschleunigung an einem festen Punkt `local_offset` (lokales Koordinatensystem der Animationseinheit),
171 /// ausgedrückt im lokalen Koordinatensystem der Animationseinheit.
172 ///
173 /// Enthält Euler- (`α × r`) und Zentripetal- (`ω × (ω × r)`) Anteile für starre Körperbewegung.
174 ///
175 /// Linear and angular acceleration at a fixed point `local_offset` (animation-unit local frame),
176 /// expressed in the animation-unit local frame.
177 ///
178 /// Includes Euler (`α × r`) and centripetal (`ω × (ω × r)`) contributions for rigid-body motion.
179 pub fn acceleration_at_local_point(self, local_offset: Vec3) -> LocalPointAcceleration {
180 LocalPointAcceleration {
181 linear_acceleration: self.linear_acceleration
182 + self.angular_acceleration.cross(local_offset)
183 + self
184 .angular_velocity
185 .cross(self.angular_velocity.cross(local_offset)),
186 angular_acceleration: self.angular_acceleration,
187 }
188 }
189}
190
191/// Lineare und Winkelbeschleunigung an einem Punkt einer Animationseinheit.
192///
193/// Linear and angular acceleration at a point on an animation unit.
194#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
195pub struct LocalPointAcceleration {
196 /// Lineare Beschleunigung am Punkt in m/s².
197 ///
198 /// Linear acceleration at the point in m/s².
199 pub linear_acceleration: Vec3,
200 /// Winkelbeschleunigung am Punkt in rad/s².
201 ///
202 /// Angular acceleration at the point in rad/s².
203 pub angular_acceleration: Vec3,
204}
205
206/// Handle auf eine benannte Animation am aktuellen Objekt.
207///
208/// Handle to a named animation on the current object.
209#[cfg(feature = "ffi")]
210#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
211pub struct Animation {
212 index: usize,
213}
214
215#[cfg(feature = "ffi")]
216impl Animation {
217 /// Gibt den internen Animationsindex zurück.
218 ///
219 /// Returns the internal animation index.
220 pub fn index(&self) -> usize {
221 self.index
222 }
223
224 /// Sucht eine Animation anhand ihres Namens am aktuellen Objekt.
225 ///
226 /// Looks up an animation by name on the current object.
227 pub fn get(name: &str) -> Result<Self, AnimationError> {
228 let name = FfiObject::new(&name);
229
230 match unsafe { lotus_script_sys::animation::get_animation_index(name.packed()) } {
231 65536 => Err(AnimationError::AnimationNotFound),
232 index => Ok(Self {
233 index: index as usize,
234 }),
235 }
236 }
237
238 /// Gibt den globalen kinematischen Zustand dieser Animationseinheit zurück.
239 ///
240 /// Returns the global kinematic state of this animation unit.
241 pub fn get_animation_global_acceleration_velocity(self) -> AccelerationVelocity {
242 let state = unsafe {
243 lotus_script_sys::animation::get_animation_global_acceleration_velocity(
244 self.index as i32,
245 )
246 };
247
248 FfiObject::from_packed(state).deserialize()
249 }
250}