veloren_voxygen_anim/
lib.rs

1#![expect(clippy::single_match)]
2#[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))]
3compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once");
4
5macro_rules! skeleton_impls {
6    { struct $Skeleton:ident { $( $(+ $bone_vis:vis)? $bone:ident ),* $(,)? $(:: $($field:ident : $field_ty:ty),* $(,)? )? } } => {
7        #[derive(Clone, Default)]
8        pub struct $Skeleton {
9            $(
10                $($bone_vis)? $bone: $crate::Bone,
11            )*
12            $($(
13                $field : $field_ty,
14            )*)?
15        }
16
17        impl<'a, Factor> $crate::vek::Lerp<Factor> for &'a $Skeleton
18            where
19                Factor: Copy,
20                $crate::Bone: Lerp<Factor, Output=$crate::Bone>
21        {
22            type Output = $Skeleton;
23
24            fn lerp_unclamped_precise(from: Self, to: Self, factor: Factor) -> Self::Output {
25                Self::Output {
26                    $(
27                        $bone: Lerp::lerp_unclamped_precise(from.$bone, to.$bone, factor),
28                    )*
29                    $($(
30                        $field : to.$field.clone(),
31                    )*)?
32                }
33            }
34
35            fn lerp_unclamped(from: Self, to: Self, factor: Factor) -> Self::Output {
36                Self::Output {
37                    $(
38                        $bone: Lerp::lerp_unclamped(from.$bone, to.$bone, factor),
39                    )*
40                    $($(
41                        $field : to.$field.clone(),
42                    )*)?
43                }
44            }
45        }
46    }
47}
48
49pub mod arthropod;
50pub mod biped_large;
51pub mod biped_small;
52pub mod bird_large;
53pub mod bird_medium;
54pub mod character;
55pub mod crustacean;
56pub mod dragon;
57pub mod fish_medium;
58pub mod fish_small;
59pub mod fixture;
60pub mod golem;
61pub mod item;
62pub mod object;
63#[cfg(feature = "plugins")] pub mod plugin;
64pub mod quadruped_low;
65pub mod quadruped_medium;
66pub mod quadruped_small;
67pub mod ship;
68pub mod theropod;
69pub mod util;
70pub mod vek;
71
72use self::vek::*;
73use bytemuck::{Pod, Zeroable};
74use common::comp::tool::ToolKind;
75#[cfg(feature = "use-dyn-lib")]
76use {
77    common_dynlib::LoadedLib, lazy_static::lazy_static, std::ffi::CStr, std::sync::Arc,
78    std::sync::Mutex,
79};
80
81type MatRaw = [[f32; 4]; 4];
82
83#[repr(C)]
84#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
85pub struct FigureBoneData(pub MatRaw, pub MatRaw);
86
87pub const MAX_BONE_COUNT: usize = 16;
88
89pub fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
90    let normal = mat.map_cols(Vec4::normalized);
91    FigureBoneData(mat.into_col_arrays(), normal.into_col_arrays())
92}
93
94pub type Bone = Transform<f32, f32, f32>;
95
96#[cfg(feature = "use-dyn-lib")]
97lazy_static! {
98    static ref LIB: Arc<Mutex<Option<LoadedLib>>> =
99        common_dynlib::init("veloren-voxygen-anim", "anim", &[
100            #[cfg(feature = "plugins")]
101            "plugins",
102        ]);
103}
104
105#[cfg(feature = "use-dyn-lib")]
106pub fn init() { lazy_static::initialize(&LIB); }
107
108// Offsets that will be returned after computing the skeleton matrices
109#[derive(Default)]
110pub struct Offsets {
111    pub lantern: Option<Vec3<f32>>,
112    pub viewpoint: Option<Vec3<f32>>,
113    pub mount_bone: Transform<f32, f32, f32>,
114    pub primary_trail_mat: Option<(Mat4<f32>, TrailSource)>,
115    pub secondary_trail_mat: Option<(Mat4<f32>, TrailSource)>,
116    pub heads: Vec<Vec3<f32>>,
117    pub tail: Option<(Vec3<f32>, Vec3<f32>)>,
118}
119
120#[derive(Clone, Copy)]
121pub enum TrailSource {
122    Weapon,
123    GliderLeft,
124    GliderRight,
125    Propeller(f32),
126}
127
128impl TrailSource {
129    pub fn relative_offsets(&self, tool: Option<ToolKind>) -> (Vec4<f32>, Vec4<f32>) {
130        // Offsets
131        const GLIDER_VERT: f32 = 5.0;
132        const GLIDER_HORIZ: f32 = 15.0;
133        // Trail width
134        const GLIDER_WIDTH: f32 = 1.0;
135
136        match self {
137            Self::Weapon => {
138                let lengths = match tool {
139                    Some(ToolKind::Sword) => (0.0, 29.25),
140                    Some(ToolKind::Axe) => (10.0, 19.25),
141                    Some(ToolKind::Hammer) => (10.0, 19.25),
142                    Some(ToolKind::Staff) => (10.0, 19.25),
143                    Some(ToolKind::Sceptre) => (10.0, 19.25),
144                    _ => (0.0, 0.0),
145                };
146                (
147                    Vec4::new(0.0, 0.0, lengths.0, 1.0),
148                    Vec4::new(0.0, 0.0, lengths.1, 1.0),
149                )
150            },
151            Self::GliderLeft => (
152                Vec4::new(GLIDER_HORIZ, 0.0, GLIDER_VERT, 1.0),
153                Vec4::new(GLIDER_HORIZ + GLIDER_WIDTH, 0.0, GLIDER_VERT, 1.0),
154            ),
155            Self::GliderRight => (
156                Vec4::new(-GLIDER_HORIZ, 0.0, GLIDER_VERT, 1.0),
157                Vec4::new(-(GLIDER_HORIZ + GLIDER_WIDTH), 0.0, GLIDER_VERT, 1.0),
158            ),
159            Self::Propeller(length) => (
160                Vec4::new(0.0, 0.0, *length * 0.5, 1.0),
161                Vec4::new(0.0, 0.0, *length, 1.0),
162            ),
163        }
164    }
165}
166
167pub trait Skeleton: Send + Sync + 'static {
168    type Attr;
169    type Body;
170
171    const BONE_COUNT: usize;
172
173    #[cfg(feature = "use-dyn-lib")]
174    const COMPUTE_FN: &'static [u8];
175
176    fn compute_matrices(
177        &self,
178        base_mat: Mat4<f32>,
179        buf: &mut [FigureBoneData; MAX_BONE_COUNT],
180        body: Self::Body,
181    ) -> Offsets {
182        #[cfg(not(feature = "use-dyn-lib"))]
183        {
184            self.compute_matrices_inner(base_mat, buf, body)
185        }
186        #[cfg(feature = "use-dyn-lib")]
187        {
188            let lock = LIB.lock().unwrap();
189            let lib = &lock.as_ref().unwrap().lib;
190
191            let compute_fn: common_dynlib::Symbol<
192                fn(&Self, Mat4<f32>, &mut [FigureBoneData; MAX_BONE_COUNT], Self::Body) -> Offsets,
193            > = unsafe { lib.get(Self::COMPUTE_FN) }.unwrap_or_else(|e| {
194                panic!(
195                    "Trying to use: {} but had error: {:?}",
196                    CStr::from_bytes_with_nul(Self::COMPUTE_FN)
197                        .map(CStr::to_str)
198                        .unwrap()
199                        .unwrap(),
200                    e
201                )
202            });
203
204            compute_fn(self, base_mat, buf, body)
205        }
206    }
207
208    fn compute_matrices_inner(
209        &self,
210        base_mat: Mat4<f32>,
211        buf: &mut [FigureBoneData; MAX_BONE_COUNT],
212        body: Self::Body,
213    ) -> Offsets;
214}
215
216pub fn compute_matrices<S: Skeleton>(
217    skeleton: &S,
218    base_mat: Mat4<f32>,
219    buf: &mut [FigureBoneData; MAX_BONE_COUNT],
220    body: S::Body,
221) -> Offsets {
222    S::compute_matrices(skeleton, base_mat, buf, body)
223}
224
225pub trait Animation {
226    type Skeleton: Skeleton;
227    type Dependency<'a>;
228
229    #[cfg(feature = "use-dyn-lib")]
230    const UPDATE_FN: &'static [u8];
231
232    /// Returns a new skeleton that is generated by the animation.
233    fn update_skeleton_inner(
234        _skeleton: &Self::Skeleton,
235        _dependency: Self::Dependency<'_>,
236        _anim_time: f32,
237        _rate: &mut f32,
238        _skeleton_attr: &<<Self as Animation>::Skeleton as Skeleton>::Attr,
239    ) -> Self::Skeleton;
240
241    /// Calls `update_skeleton_inner` either directly or via `libloading` to
242    /// generate the new skeleton.
243    fn update_skeleton(
244        skeleton: &Self::Skeleton,
245        dependency: Self::Dependency<'_>,
246        anim_time: f32,
247        rate: &mut f32,
248        skeleton_attr: &<<Self as Animation>::Skeleton as Skeleton>::Attr,
249    ) -> Self::Skeleton {
250        #[cfg(not(feature = "use-dyn-lib"))]
251        {
252            Self::update_skeleton_inner(skeleton, dependency, anim_time, rate, skeleton_attr)
253        }
254        #[cfg(feature = "use-dyn-lib")]
255        {
256            let lock = LIB.lock().unwrap();
257            let lib = &lock.as_ref().unwrap().lib;
258
259            let update_fn: common_dynlib::Symbol<
260                fn(
261                    &Self::Skeleton,
262                    Self::Dependency<'_>,
263                    f32,
264                    &mut f32,
265                    &<Self::Skeleton as Skeleton>::Attr,
266                ) -> Self::Skeleton,
267            > = unsafe {
268                //let start = std::time::Instant::now();
269                // Overhead of 0.5-5 us (could use hashmap to mitigate if this is an issue)
270                lib.get(Self::UPDATE_FN)
271                //println!("{}", start.elapsed().as_nanos());
272            }
273            .unwrap_or_else(|e| {
274                panic!(
275                    "Trying to use: {} but had error: {:?}",
276                    CStr::from_bytes_with_nul(Self::UPDATE_FN)
277                        .map(CStr::to_str)
278                        .unwrap()
279                        .unwrap(),
280                    e
281                )
282            });
283
284            update_fn(skeleton, dependency, anim_time, rate, skeleton_attr)
285        }
286    }
287}