veloren_voxygen_anim/
lib.rs

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