#![allow(incomplete_features)] #![allow(clippy::single_match)] #[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))] compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once"); macro_rules! skeleton_impls { { struct $Skeleton:ident { $( $(+)? $bone:ident ),* $(,)? $(:: $($field:ident : $field_ty:ty),* $(,)? )? } } => { #[derive(Clone, Default)] pub struct $Skeleton { $( $bone: $crate::Bone, )* $($( $field : $field_ty, )*)? } impl<'a, Factor> $crate::vek::Lerp for &'a $Skeleton where Factor: Copy, $crate::Bone: Lerp { type Output = $Skeleton; fn lerp_unclamped_precise(from: Self, to: Self, factor: Factor) -> Self::Output { Self::Output { $( $bone: Lerp::lerp_unclamped_precise(from.$bone, to.$bone, factor), )* $($( $field : to.$field.clone(), )*)? } } fn lerp_unclamped(from: Self, to: Self, factor: Factor) -> Self::Output { Self::Output { $( $bone: Lerp::lerp_unclamped(from.$bone, to.$bone, factor), )* $($( $field : to.$field.clone(), )*)? } } } } } pub mod arthropod; pub mod biped_large; pub mod biped_small; pub mod bird_large; pub mod bird_medium; pub mod character; pub mod crustacean; pub mod dragon; pub mod fish_medium; pub mod fish_small; pub mod fixture; pub mod golem; pub mod item_drop; pub mod object; pub mod quadruped_low; pub mod quadruped_medium; pub mod quadruped_small; pub mod ship; pub mod theropod; pub mod vek; use self::vek::*; use bytemuck::{Pod, Zeroable}; use common::comp::tool::ToolKind; #[cfg(feature = "use-dyn-lib")] use { common_dynlib::LoadedLib, lazy_static::lazy_static, std::ffi::CStr, std::sync::Arc, std::sync::Mutex, }; type MatRaw = [[f32; 4]; 4]; #[repr(C)] #[derive(Debug, Clone, Copy, Pod, Zeroable, Default)] pub struct FigureBoneData(pub MatRaw, pub MatRaw); pub const MAX_BONE_COUNT: usize = 16; pub fn make_bone(mat: Mat4) -> FigureBoneData { let normal = mat.map_cols(Vec4::normalized); FigureBoneData(mat.into_col_arrays(), normal.into_col_arrays()) } pub type Bone = Transform; #[cfg(feature = "use-dyn-lib")] lazy_static! { static ref LIB: Arc>> = common_dynlib::init("veloren-voxygen-anim", "anim"); } #[cfg(feature = "use-dyn-lib")] pub fn init() { lazy_static::initialize(&LIB); } // Offsets that will be returned after computing the skeleton matrices pub struct Offsets { pub lantern: Option>, pub viewpoint: Option>, pub mount_bone: Transform, pub primary_trail_mat: Option<(Mat4, TrailSource)>, pub secondary_trail_mat: Option<(Mat4, TrailSource)>, } #[derive(Clone, Copy)] pub enum TrailSource { Weapon, GliderLeft, GliderRight, Propeller(f32), } impl TrailSource { pub fn relative_offsets(&self, tool: Option) -> (Vec4, Vec4) { // Offsets const GLIDER_VERT: f32 = 5.0; const GLIDER_HORIZ: f32 = 15.0; // Trail width const GLIDER_WIDTH: f32 = 1.0; match self { Self::Weapon => { let lengths = match tool { Some(ToolKind::Sword) => (0.0, 29.25), Some(ToolKind::Axe) => (10.0, 19.25), Some(ToolKind::Hammer) => (10.0, 19.25), Some(ToolKind::Staff) => (10.0, 19.25), Some(ToolKind::Sceptre) => (10.0, 19.25), _ => (0.0, 0.0), }; ( Vec4::new(0.0, 0.0, lengths.0, 1.0), Vec4::new(0.0, 0.0, lengths.1, 1.0), ) }, Self::GliderLeft => ( Vec4::new(GLIDER_HORIZ, 0.0, GLIDER_VERT, 1.0), Vec4::new(GLIDER_HORIZ + GLIDER_WIDTH, 0.0, GLIDER_VERT, 1.0), ), Self::GliderRight => ( Vec4::new(-GLIDER_HORIZ, 0.0, GLIDER_VERT, 1.0), Vec4::new(-(GLIDER_HORIZ + GLIDER_WIDTH), 0.0, GLIDER_VERT, 1.0), ), Self::Propeller(length) => ( Vec4::new(0.0, 0.0, *length * 0.5, 1.0), Vec4::new(0.0, 0.0, *length, 1.0), ), } } } pub trait Skeleton: Send + Sync + 'static { type Attr; type Body; const BONE_COUNT: usize; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8]; fn compute_matrices( &self, base_mat: Mat4, buf: &mut [FigureBoneData; MAX_BONE_COUNT], body: Self::Body, ) -> Offsets { #[cfg(not(feature = "use-dyn-lib"))] { self.compute_matrices_inner(base_mat, buf, body) } #[cfg(feature = "use-dyn-lib")] { let lock = LIB.lock().unwrap(); let lib = &lock.as_ref().unwrap().lib; let compute_fn: common_dynlib::Symbol< fn(&Self, Mat4, &mut [FigureBoneData; MAX_BONE_COUNT], Self::Body) -> Offsets, > = unsafe { lib.get(Self::COMPUTE_FN) }.unwrap_or_else(|e| { panic!( "Trying to use: {} but had error: {:?}", CStr::from_bytes_with_nul(Self::COMPUTE_FN) .map(CStr::to_str) .unwrap() .unwrap(), e ) }); compute_fn(self, base_mat, buf, body) } } fn compute_matrices_inner( &self, base_mat: Mat4, buf: &mut [FigureBoneData; MAX_BONE_COUNT], body: Self::Body, ) -> Offsets; } pub fn compute_matrices( skeleton: &S, base_mat: Mat4, buf: &mut [FigureBoneData; MAX_BONE_COUNT], body: S::Body, ) -> Offsets { S::compute_matrices(skeleton, base_mat, buf, body) } pub trait Animation { type Skeleton: Skeleton; type Dependency<'a>; #[cfg(feature = "use-dyn-lib")] const UPDATE_FN: &'static [u8]; /// Returns a new skeleton that is generated by the animation. fn update_skeleton_inner( _skeleton: &Self::Skeleton, _dependency: Self::Dependency<'_>, _anim_time: f32, _rate: &mut f32, _skeleton_attr: &<::Skeleton as Skeleton>::Attr, ) -> Self::Skeleton; /// Calls `update_skeleton_inner` either directly or via `libloading` to /// generate the new skeleton. fn update_skeleton( skeleton: &Self::Skeleton, dependency: Self::Dependency<'_>, anim_time: f32, rate: &mut f32, skeleton_attr: &<::Skeleton as Skeleton>::Attr, ) -> Self::Skeleton { #[cfg(not(feature = "use-dyn-lib"))] { Self::update_skeleton_inner(skeleton, dependency, anim_time, rate, skeleton_attr) } #[cfg(feature = "use-dyn-lib")] { let lock = LIB.lock().unwrap(); let lib = &lock.as_ref().unwrap().lib; let update_fn: common_dynlib::Symbol< fn( &Self::Skeleton, Self::Dependency<'_>, f32, &mut f32, &::Attr, ) -> Self::Skeleton, > = unsafe { //let start = std::time::Instant::now(); // Overhead of 0.5-5 us (could use hashmap to mitigate if this is an issue) lib.get(Self::UPDATE_FN) //println!("{}", start.elapsed().as_nanos()); } .unwrap_or_else(|e| { panic!( "Trying to use: {} but had error: {:?}", CStr::from_bytes_with_nul(Self::UPDATE_FN) .map(CStr::to_str) .unwrap() .unwrap(), e ) }); update_fn(skeleton, dependency, anim_time, rate, skeleton_attr) } } }