Skip to main content

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}