veloren/voxygen/anim/src/lib.rs
Ben Wallis b499cf2c58 Added egui debug UI - a 100% rust UI framework (similar to imgui) allowing for rapid development of debug interfaces to aid development. This is feature-gated behind the egui-ui feature which is enabled by default but removed for airshipper builds.
Included in the initial implementation is an entity browser which lists all entities in the client ECS, an entity component viewer which shows select components belonging to the selected entity including character state information, and a simple frame time graph.

This MR also includes an extraction of the animation hot reloading code which has been reused for egui to allow for hot-reloading of the egui interface to allow rapid development of the UI with realtime feedback upon save as is the case with aninmations. This is feature-gated behind the `hot-egui` feature which is not enabled by default due to the extra startup time that it adds.
2021-07-04 09:47:18 +00:00

213 lines
6.2 KiB
Rust

#![feature(const_generics)]
#![feature(generic_associated_types)]
#![allow(incomplete_features)]
#[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<Factor> for &'a $Skeleton
where
Factor: Copy,
$crate::Bone: Lerp<Factor, Output=$crate::Bone>
{
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 biped_large;
pub mod biped_small;
pub mod bird_large;
pub mod bird_medium;
pub mod character;
pub mod dragon;
pub mod fish_medium;
pub mod fish_small;
pub mod fixture;
pub mod golem;
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};
#[cfg(feature = "use-dyn-lib")]
use {
lazy_static::lazy_static, std::ffi::CStr, std::sync::Arc, std::sync::Mutex,
voxygen_dynlib::LoadedLib,
};
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;
fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
let normal = mat.map_cols(Vec4::normalized);
FigureBoneData(mat.into_col_arrays(), normal.into_col_arrays())
}
pub type Bone = Transform<f32, f32, f32>;
#[cfg(feature = "use-dyn-lib")]
lazy_static! {
static ref LIB: Arc<Mutex<Option<LoadedLib>>> =
voxygen_dynlib::init("veloren-voxygen-anim", "veloren-voxygen-anim-dyn", "anim");
}
#[cfg(feature = "use-dyn-lib")]
pub fn init() { lazy_static::initialize(&LIB); }
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_inner(
&self,
base_mat: Mat4<f32>,
buf: &mut [FigureBoneData; MAX_BONE_COUNT],
) -> Vec3<f32>;
}
pub fn compute_matrices<S: Skeleton>(
skeleton: &S,
base_mat: Mat4<f32>,
buf: &mut [FigureBoneData; MAX_BONE_COUNT],
) -> Vec3<f32> {
#[cfg(not(feature = "use-dyn-lib"))]
{
S::compute_matrices_inner(skeleton, base_mat, buf)
}
#[cfg(feature = "use-dyn-lib")]
{
let lock = LIB.lock().unwrap();
let lib = &lock.as_ref().unwrap().lib;
#[allow(clippy::type_complexity)]
let compute_fn: voxygen_dynlib::Symbol<
fn(&S, Mat4<f32>, &mut [FigureBoneData; MAX_BONE_COUNT]) -> Vec3<f32>,
> = unsafe { lib.get(S::COMPUTE_FN) }.unwrap_or_else(|e| {
panic!(
"Trying to use: {} but had error: {:?}",
CStr::from_bytes_with_nul(S::COMPUTE_FN)
.map(CStr::to_str)
.unwrap()
.unwrap(),
e
)
});
compute_fn(skeleton, base_mat, buf)
}
}
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<'a>(
_skeleton: &Self::Skeleton,
_dependency: Self::Dependency<'a>,
_anim_time: f32,
_rate: &mut f32,
_skeleton_attr: &<<Self as Animation>::Skeleton as Skeleton>::Attr,
) -> Self::Skeleton;
/// Calls `update_skeleton_inner` either directly or via `libloading` to
/// generate the new skeleton.
fn update_skeleton<'a>(
skeleton: &Self::Skeleton,
dependency: Self::Dependency<'a>,
anim_time: f32,
rate: &mut f32,
skeleton_attr: &<<Self as Animation>::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;
#[allow(clippy::type_complexity)]
let update_fn: voxygen_dynlib::Symbol<
fn(
&Self::Skeleton,
Self::Dependency<'a>,
f32,
&mut f32,
&<Self::Skeleton as Skeleton>::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)
}
}
}