Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use bimap for skeleton names #73

Merged
merged 1 commit into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ wasm = []
nodejs = ["wasm", "dep:js-sys", "dep:wasm-bindgen"]

[dependencies]
bimap = { version = "0.6" }
bytecheck = { version = "0.6", optional = true, default-features = false }
glam = { version = "0.25", features = [ "core-simd", "libm" ] }
js-sys = { version = "0.3", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion src/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ impl ArchiveRead<QuaternionKey> for QuaternionKey {
/// coherency when sampling the animation, Keyframes in this array are sorted by
/// time, then by track number.
///
#[derive(Debug)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct Animation {
duration: f32,
Expand Down
2 changes: 1 addition & 1 deletion src/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl Archive<Cursor<Vec<u8>>> {
pub fn from_path(path: &str) -> Result<Archive<Cursor<Vec<u8>>>, OzzError> {
match crate::nodejs::read_file(path) {
Ok(buf) => return Archive::from_vec(buf),
Err(err) => return Err(OzzError::Custom(err.as_string().unwrap_or("".into()).into())),
Err(err) => return Err(OzzError::Custom(err.as_string().unwrap_or("".into()))),
};
}
}
Expand Down
7 changes: 3 additions & 4 deletions src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use std::cell::{Ref, RefCell, RefMut};
use std::collections::hash_map::DefaultHasher;
use std::error::Error;
use std::fmt::Debug;
use std::hash::BuildHasher;
use std::ops::{Deref, DerefMut};
Expand Down Expand Up @@ -40,9 +39,9 @@ pub enum OzzError {
InvalidVersion,

/// Custom errors.
/// Ozz-animation-rs does not generate this error, but you can use it in your own code.
/// Ozz-animation-rs does not generate this error (except test & nodejs), but you can use it in your own code.
#[error("Custom error: {0}")]
Custom(Box<dyn Error>),
Custom(String),
}

impl OzzError {
Expand Down Expand Up @@ -111,7 +110,7 @@ pub const SKELETON_MAX_SOA_JOINTS: i32 = (SKELETON_MAX_JOINTS + 3) / 4;
pub const SKELETON_NO_PARENT: i32 = -1;

/// A hasher builder that creates `DefaultHasher` with default keys.
#[derive(Default)]
#[derive(Debug, Default, Clone, Copy)]
pub struct DeterministicState;

impl DeterministicState {
Expand Down
14 changes: 7 additions & 7 deletions src/blending_job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,12 @@ where

#[cfg(test)]
mod blending_tests {
use std::collections::HashMap;
use std::mem;
use wasm_bindgen_test::*;

use super::*;
use crate::base::DeterministicState;
use crate::skeleton::JointHashMap;

const IDENTITY: SoaTransform = SoaTransform {
translation: SoaVec3::splat_col([0.0; 3]),
Expand Down Expand Up @@ -787,7 +787,7 @@ mod blending_tests {
let skeleton = Rc::new(Skeleton::from_raw(
joint_rest_poses,
vec![0; 8],
HashMap::with_hasher(DeterministicState::new()),
JointHashMap::with_hashers(DeterministicState::new(), DeterministicState::new()),
));

execute_test(
Expand Down Expand Up @@ -849,7 +849,7 @@ mod blending_tests {
let skeleton = Rc::new(Skeleton::from_raw(
rest_poses,
vec![0; 8],
HashMap::with_hasher(DeterministicState::new()),
JointHashMap::with_hashers(DeterministicState::new(), DeterministicState::new()),
));

{
Expand Down Expand Up @@ -949,7 +949,7 @@ mod blending_tests {
let skeleton = Rc::new(Skeleton::from_raw(
rest_poses,
vec![0; 8],
HashMap::with_hasher(DeterministicState::new()),
JointHashMap::with_hashers(DeterministicState::new(), DeterministicState::new()),
));

{
Expand Down Expand Up @@ -1012,7 +1012,7 @@ mod blending_tests {
return Rc::new(Skeleton::from_raw(
joint_rest_poses,
vec![0; 4],
HashMap::with_hasher(DeterministicState::new()),
JointHashMap::with_hashers(DeterministicState::new(), DeterministicState::new()),
));
}
#[test]
Expand Down Expand Up @@ -1232,7 +1232,7 @@ mod blending_tests {
let skeleton = Rc::new(Skeleton::from_raw(
vec![IDENTITY; 1],
vec![0; 4],
HashMap::with_hasher(DeterministicState::new()),
JointHashMap::with_hashers(DeterministicState::new(), DeterministicState::new()),
));

let mut input1 = vec![IDENTITY; 1];
Expand Down Expand Up @@ -1403,7 +1403,7 @@ mod blending_tests {
let skeleton = Rc::new(Skeleton::from_raw(
vec![IDENTITY; 1],
vec![0; 4],
HashMap::with_hasher(DeterministicState::new()),
JointHashMap::with_hashers(DeterministicState::new(), DeterministicState::new()),
));

let mut input1 = vec![IDENTITY; 1];
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub use local_to_model_job::{LocalToModelJob, LocalToModelJobArc, LocalToModelJo
pub use sampling_job::{
InterpSoaFloat3, InterpSoaQuaternion, SamplingContext, SamplingJob, SamplingJobArc, SamplingJobRc, SamplingJobRef,
};
pub use skeleton::Skeleton;
pub use skeleton::{JointHashMap, Skeleton};
pub use skinning_job::{SkinningJob, SkinningJobArc, SkinningJobRc, SkinningJobRef};
pub use track::Track;
pub use track_sampling_job::{TrackSamplingJob, TrackSamplingJobArc, TrackSamplingJobRc, TrackSamplingJobRef};
Expand Down
6 changes: 3 additions & 3 deletions src/local_to_model_job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,12 @@ where
#[cfg(test)]
mod local_to_model_tests {
use glam::Vec3;
use std::collections::HashMap;
use wasm_bindgen_test::*;

use super::*;
use crate::base::DeterministicState;
use crate::math::{SoaQuat, SoaVec3};
use crate::skeleton::JointHashMap;

#[test]
#[wasm_bindgen_test]
Expand Down Expand Up @@ -339,7 +339,7 @@ mod local_to_model_tests {
],
vec![-1, 0, 1, 0, 3, 3],
(|| {
let mut map = HashMap::with_hasher(DeterministicState::new());
let mut map = JointHashMap::with_hashers(DeterministicState::new(), DeterministicState::new());
map.insert("j0".into(), 0);
map.insert("j1".into(), 1);
map.insert("j2".into(), 2);
Expand Down Expand Up @@ -398,7 +398,7 @@ mod local_to_model_tests {
],
vec![-1, 0, 1, 0, 3, 4, 3, -1],
(|| {
let mut map = HashMap::with_hasher(DeterministicState::new());
let mut map = JointHashMap::with_hashers(DeterministicState::new(), DeterministicState::new());
map.insert("j0".into(), 0);
map.insert("j1".into(), 1);
map.insert("j2".into(), 2);
Expand Down
107 changes: 97 additions & 10 deletions src/skeleton.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,66 @@
use std::collections::HashMap;
use bimap::BiHashMap;
use std::io::Read;

use crate::archive::Archive;
use crate::base::{DeterministicState, OzzError, OzzIndex};
use crate::math::SoaTransform;

/// Rexported `BiHashMap` in bimap crate.
pub type JointHashMap = BiHashMap<String, i16, DeterministicState, DeterministicState>;

struct JointHashMapWrapper;

#[cfg(feature = "rkyv")]
const _: () = {
use rkyv::collections::util::Entry;
use rkyv::ser::{ScratchSpace, Serializer};
use rkyv::string::ArchivedString;
use rkyv::vec::{ArchivedVec, VecResolver};
use rkyv::with::{ArchiveWith, DeserializeWith, SerializeWith};
use rkyv::{Deserialize, Fallible};

impl ArchiveWith<JointHashMap> for JointHashMapWrapper {
type Archived = ArchivedVec<Entry<ArchivedString, i16>>;
type Resolver = VecResolver;

unsafe fn resolve_with(field: &JointHashMap, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) {
ArchivedVec::resolve_from_len(field.len(), pos, resolver, out);
}
}

impl<S> SerializeWith<JointHashMap, S> for JointHashMapWrapper
where
S: ScratchSpace + Serializer + ?Sized,
{
fn serialize_with(field: &JointHashMap, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
return ArchivedVec::serialize_from_iter(field.iter().map(|(key, value)| Entry { key, value }), serializer);
}
}

impl<D> DeserializeWith<ArchivedVec<Entry<ArchivedString, i16>>, JointHashMap, D> for JointHashMapWrapper
where
D: Fallible + ?Sized,
{
fn deserialize_with(
field: &ArchivedVec<Entry<ArchivedString, i16>>,
deserializer: &mut D,
) -> Result<JointHashMap, D::Error> {
let mut result = JointHashMap::with_capacity_and_hashers(
field.len() as usize,
DeterministicState::new(),
DeterministicState::new(),
);
for entry in field.iter() {
result.insert(
entry.key.deserialize(deserializer)?,
entry.value.deserialize(deserializer)?,
);
}
return Ok(result);
}
}
};

///
/// This runtime skeleton data structure provides a const-only access to joint
/// hierarchy, joint names and rest-pose.
Expand All @@ -16,12 +72,13 @@ use crate::math::SoaTransform;
/// order. This is enough to traverse the whole joint hierarchy. Use
/// iter_depth_first() to implement a depth-first traversal utility.
///
#[derive(Debug)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct Skeleton {
joint_rest_poses: Vec<SoaTransform>,
joint_parents: Vec<i16>,
joint_names: HashMap<String, i16, DeterministicState>,
#[cfg_attr(feature = "rkyv", with(JointHashMapWrapper))]
joint_names: JointHashMap,
}

impl Skeleton {
Expand All @@ -41,7 +98,7 @@ impl Skeleton {
pub(crate) fn from_raw(
joint_rest_poses: Vec<SoaTransform>,
joint_parents: Vec<i16>,
joint_names: HashMap<String, i16, DeterministicState>,
joint_names: JointHashMap,
) -> Skeleton {
return Skeleton {
joint_rest_poses,
Expand All @@ -64,12 +121,16 @@ impl Skeleton {
return Ok(Skeleton {
joint_rest_poses: Vec::new(),
joint_parents: Vec::new(),
joint_names: HashMap::with_hasher(DeterministicState::new()),
joint_names: BiHashMap::with_hashers(DeterministicState::new(), DeterministicState::new()),
});
}

let _char_count: i32 = archive.read()?;
let mut joint_names = HashMap::with_capacity_and_hasher(num_joints as usize, DeterministicState::new());
let mut joint_names = BiHashMap::with_capacity_and_hashers(
num_joints as usize,
DeterministicState::new(),
DeterministicState::new(),
);
for idx in 0..num_joints {
joint_names.insert(archive.read::<String>()?, idx as i16);
}
Expand Down Expand Up @@ -144,14 +205,20 @@ impl Skeleton {

/// Gets joint's name map.
#[inline]
pub fn joint_names(&self) -> &HashMap<String, i16, DeterministicState> {
pub fn joint_names(&self) -> &JointHashMap {
return &self.joint_names;
}

/// Gets joint's index by name.
#[inline]
pub fn joint_by_name(&self, name: &str) -> Option<i16> {
return self.joint_names.get(name).map(|idx| *idx);
return self.joint_names.get_by_left(name).map(|idx| *idx);
}

/// Gets joint's name by index.
#[inline]
pub fn name_by_joint(&self, index: i16) -> Option<&str> {
return self.joint_names.get_by_right(&index).map(|s| s.as_str());
}

/// Test if a joint is a leaf.
Expand Down Expand Up @@ -267,7 +334,27 @@ mod tests {
assert_eq!(skeleton.joint_parents()[66], 65);

assert_eq!(skeleton.joint_names().len(), 67);
assert_eq!(skeleton.joint_names()["Hips"], 0);
assert_eq!(skeleton.joint_names()["Bip01 R Toe0Nub"], 66);
assert_eq!(skeleton.joint_by_name("Hips"), Some(0));
assert_eq!(skeleton.joint_by_name("Bip01 R Toe0Nub"), Some(66));
}

#[cfg(feature = "rkyv")]
#[test]
#[wasm_bindgen_test]
fn test_rkyv_skeleton() {
use rkyv::ser::Serializer;
use rkyv::Deserialize;

let skeleton = Skeleton::from_path("./resource/playback/skeleton.ozz").unwrap();
let mut serializer = rkyv::ser::serializers::AllocSerializer::<30720>::default();
serializer.serialize_value(&skeleton).unwrap();
let buf = serializer.into_serializer().into_inner();
let archived = unsafe { rkyv::archived_root::<Skeleton>(&buf) };
let mut deserializer = rkyv::Infallible::default();
let skeleton2: Skeleton = archived.deserialize(&mut deserializer).unwrap();

assert_eq!(skeleton.joint_rest_poses(), skeleton2.joint_rest_poses());
assert_eq!(skeleton.joint_parents(), skeleton2.joint_parents());
assert_eq!(skeleton.joint_names(), skeleton2.joint_names());
}
}
Loading