Skip to content

Commit

Permalink
refactor track
Browse files Browse the repository at this point in the history
  • Loading branch information
Nan committed Apr 27, 2024
1 parent 0ba0e19 commit f8c0f78
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 154 deletions.
2 changes: 1 addition & 1 deletion src/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ impl Animation {
return "ozz-animation";
}

#[inline]
/// `Animation` resource file version for `Archive`.
#[inline]
pub fn version() -> u32 {
return 6;
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,5 @@ pub use skinning_job::{SkinningJob, SkinningJobArc, SkinningJobRc, SkinningJobRe
pub use track::Track;
pub use track_sampling_job::{TrackSamplingJob, TrackSamplingJobArc, TrackSamplingJobRc, TrackSamplingJobRef};
pub use track_triggering_job::{
TrackTriggeringJob, TrackTriggeringJobArc, TrackTriggeringJobRc, TrackTriggeringJobRef,
Edge, TrackTriggeringJob, TrackTriggeringJobArc, TrackTriggeringJobRc, TrackTriggeringJobRef,
};
247 changes: 99 additions & 148 deletions src/track.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
use core::slice;
use glam::{Quat, Vec2, Vec3, Vec4};
use std::alloc::{self, Layout};
use std::fmt::Debug;
use std::io::Read;
use std::{mem, ptr};

use crate::archive::{Archive, ArchiveRead};
use crate::base::OzzError;

/// Value type that can be stored in a `Track`.
pub trait TrackValue
where
Self: Debug + Default + Copy + Clone + PartialEq + ArchiveRead<Self>,
{
/// Ozz file tag in '.ozz' file for `Archive`.
fn tag() -> &'static str;

/// Linear interpolation between two values.
fn lerp(a: Self, b: Self, t: f32) -> Self;

// Compare two values with a maximum difference.
fn abs_diff_eq(a: Self, b: Self, diff: f32) -> bool;
}

impl TrackValue for f32 {
#[inline]
fn tag() -> &'static str {
return "ozz-float_track";
}

#[inline]
fn lerp(a: f32, b: f32, t: f32) -> f32 {
return a + (b - a) * t;
Expand All @@ -29,6 +38,11 @@ impl TrackValue for f32 {
}

impl TrackValue for Vec2 {
#[inline]
fn tag() -> &'static str {
return "ozz-float2_track";
}

#[inline]
fn lerp(a: Vec2, b: Vec2, t: f32) -> Vec2 {
return Vec2::lerp(a, b, t);
Expand All @@ -41,6 +55,11 @@ impl TrackValue for Vec2 {
}

impl TrackValue for Vec3 {
#[inline]
fn tag() -> &'static str {
return "ozz-float3_track";
}

#[inline]
fn lerp(a: Vec3, b: Vec3, t: f32) -> Vec3 {
return Vec3::lerp(a, b, t);
Expand All @@ -53,6 +72,11 @@ impl TrackValue for Vec3 {
}

impl TrackValue for Vec4 {
#[inline]
fn tag() -> &'static str {
return "ozz-float4_track";
}

#[inline]
fn lerp(a: Vec4, b: Vec4, t: f32) -> Vec4 {
return Vec4::lerp(a, b, t);
Expand All @@ -65,6 +89,11 @@ impl TrackValue for Vec4 {
}

impl TrackValue for Quat {
#[inline]
fn tag() -> &'static str {
return "ozz-quat_track";
}

#[inline]
fn lerp(a: Quat, b: Quat, t: f32) -> Quat {
return Quat::lerp(a, b, t);
Expand All @@ -76,200 +105,122 @@ impl TrackValue for Quat {
}
}

#[derive(Debug)]
struct TrackInner<V: TrackValue> {
size: usize,
key_count: usize,
values_ptr: *mut V,
ratios_ptr: *mut f32,
steps_ptr: *mut u8,
name_len: usize,
name_ptr: *mut u8,
}

impl<V: TrackValue> Default for TrackInner<V> {
fn default() -> Self {
return TrackInner {
size: 0,
key_count: 0,
values_ptr: ptr::null_mut(),
ratios_ptr: ptr::null_mut(),
steps_ptr: ptr::null_mut(),
name_len: 0,
name_ptr: ptr::null_mut(),
};
}
}

#[derive(Debug)]
/// Runtime user-channel track data.
///
/// Keyframe ratios, values and interpolation mode are all store as separate buffers in order
/// to access the cache coherently. Ratios are usually accessed/read alone from the jobs that
/// all start by looking up the keyframes to interpolate indeed.
#[derive(Debug, Default)]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub struct Track<V: TrackValue> {
inner: *mut TrackInner<V>,
}

impl<V: TrackValue> Clone for Track<V> {
fn clone(&self) -> Self {
let inner = self.inner();
let mut track = Track::new(inner.key_count, inner.name_len);
track.values_mut().copy_from_slice(self.values());
track.ratios_mut().copy_from_slice(self.ratios());
track.steps_mut().copy_from_slice(self.steps());
if inner.name_len != 0 {
let name = unsafe { slice::from_raw_parts_mut(track.inner_mut().name_ptr, inner.name_len) };
name.copy_from_slice(self.name().as_bytes());
}
return track;
}
}

impl<V: TrackValue> Drop for Track<V> {
fn drop(&mut self) {
unsafe {
let align = usize::max(mem::align_of::<V>(), mem::align_of::<TrackInner<V>>());
let layout = Layout::from_size_align_unchecked(self.inner().size, align);
alloc::dealloc(self.inner as *mut u8, layout);
}
}
key_count: u32,
ratios: Vec<f32>,
values: Vec<V>,
steps: Vec<u8>,
name: String,
}

impl<V: TrackValue> Track<V> {
#[inline(always)]
fn inner(&self) -> &TrackInner<V> {
return unsafe { &*self.inner };
}

#[inline(always)]
fn inner_mut(&mut self) -> &mut TrackInner<V> {
return unsafe { &mut *self.inner };
/// `Track` resource file tag for `Archive`.
#[inline]
pub fn tag() -> &'static str {
return V::tag();
}

pub fn new(key_count: usize, name_len: usize) -> Track<V> {
let align = usize::max(mem::align_of::<V>(), mem::align_of::<TrackInner<V>>());
let inner_size = (mem::size_of::<TrackInner<V>>() + align - 1) & !(align - 1);
let size = inner_size
+ key_count * mem::size_of::<V>()
+ key_count * mem::size_of::<f32>()
+ (key_count + 7) / 8 * mem::size_of::<u8>()
+ name_len;

unsafe {
let layout = Layout::from_size_align_unchecked(size, align);
let mut ptr = alloc::alloc(layout);
let mut track = Track {
inner: ptr as *mut TrackInner<V>,
};
ptr = ptr.add(inner_size);

track.inner_mut().size = size;
track.inner_mut().key_count = key_count;

track.inner_mut().values_ptr = ptr as *mut V;
ptr = ptr.add(track.inner_mut().key_count * mem::size_of::<V>());
track.inner_mut().ratios_ptr = ptr as *mut f32;
ptr = ptr.add(track.inner_mut().key_count * mem::size_of::<f32>());
track.inner_mut().steps_ptr = ptr as *mut u8;
ptr = ptr.add(((track.inner_mut().key_count + 7) / 8) * mem::size_of::<u8>());
track.inner_mut().name_len = name_len;
track.inner_mut().name_ptr = ptr as *mut u8;
ptr = ptr.add(name_len);
assert_eq!(ptr, (track.inner as *mut u8).add(size));

return track;
}
/// `Track` resource file version for `Archive`.
#[inline]
pub fn version() -> u32 {
return 1;
}

#[cfg(test)]
pub(crate) fn from_raw(values: &[V], ratios: &[f32], steps: &[u8]) -> Result<Track<V>, OzzError> {
if values.len() != ratios.len() || (values.len() + 7) / 8 != steps.len() {
return Err(OzzError::Custom("Invalid arguments".into()));
}
let mut track = Track::new(values.len(), 0);
track.values_mut().copy_from_slice(values);
track.ratios_mut().copy_from_slice(ratios);
track.steps_mut().copy_from_slice(steps);
return Ok(track);
return Ok(Track {
key_count: values.len() as u32,
ratios: ratios.to_vec(),
values: values.to_vec(),
steps: steps.to_vec(),
name: String::new(),
});
}

/// Reads an `Track` from an `Archive`.
pub fn from_archive(archive: &mut Archive<impl Read>) -> Result<Track<V>, OzzError> {
// if (_version > 1) {
// log::Err() << "Unsupported Track version " << _version << "." << std::endl;
// return;
// }
if archive.tag() != Self::tag() {
return Err(OzzError::InvalidTag);
}
if archive.version() != Self::version() {
return Err(OzzError::InvalidVersion);
}

let key_count: u32 = archive.read()?;
let name_len: u32 = archive.read()?;
let mut track = Track::new(key_count as usize, name_len as usize);

let ratios: Vec<f32> = archive.read_vec(key_count as usize)?;
let values: Vec<V> = archive.read_vec(key_count as usize)?;
let steps: Vec<u8> = archive.read_vec((key_count + 7) as usize / 8)?;

for i in 0..key_count {
track.ratios_mut()[i as usize] = archive.read()?;
}
for i in 0..key_count {
track.values_mut()[i as usize] = archive.read()?;
}
for i in 0..(key_count + 7) / 8 {
track.steps_mut()[i as usize] = archive.read()?;
}
let mut name = String::new();
if name_len != 0 {
let name = unsafe { slice::from_raw_parts_mut(track.inner_mut().name_ptr, track.inner_mut().name_len) };
for i in 0..name_len {
name[i as usize] = archive.read()?;
}
std::str::from_utf8(name)?;
let buf = archive.read_vec(name_len as usize)?;
name = String::from_utf8(buf).map_err(|e| e.utf8_error())?;
}

return Ok(track);
return Ok(Track {
key_count,
ratios,
values,
steps,
name,
});
}

/// The memory size of the context in bytes.
#[inline]
pub fn size(&self) -> usize {
return self.inner().size;
/// Reads an `Track` from a file path.
#[cfg(not(feature = "wasm"))]
pub fn from_path<P: AsRef<std::path::Path>>(path: P) -> Result<Track<V>, OzzError> {
let mut archive = Archive::from_path(path)?;
return Track::from_archive(&mut archive);
}

// Only for wasm test in NodeJS environment.
#[cfg(all(feature = "wasm", feature = "nodejs"))]
pub fn from_path(path: &str) -> Result<Track<V>, OzzError> {
let mut archive = Archive::from_path(path)?;
return Track::from_archive(&mut archive);
}
}

impl<V: TrackValue> Track<V> {
/// The key count in the track.
#[inline]
pub fn key_count(&self) -> usize {
return self.inner().key_count;
return self.key_count as usize;
}

/// Keyframe values.
#[inline]
pub fn values(&self) -> &[V] {
return unsafe { std::slice::from_raw_parts(self.inner().values_ptr, self.inner().key_count) };
}

#[inline]
pub fn values_mut(&mut self) -> &mut [V] {
return unsafe { std::slice::from_raw_parts_mut(self.inner_mut().values_ptr, self.inner().key_count) };
return &self.values;
}

/// Keyframe ratios (0 is the beginning of the track, 1 is the end).
#[inline]
pub fn ratios(&self) -> &[f32] {
return unsafe { std::slice::from_raw_parts(self.inner().ratios_ptr, self.inner().key_count) };
}

#[inline]
pub fn ratios_mut(&mut self) -> &mut [f32] {
return unsafe { std::slice::from_raw_parts_mut(self.inner_mut().ratios_ptr, self.inner().key_count) };
return &self.ratios;
}

/// Keyframe modes (1 bit per key): 1 for step, 0 for linear.
#[inline]
pub fn steps(&self) -> &[u8] {
return unsafe { std::slice::from_raw_parts(self.inner().steps_ptr, (self.inner().key_count + 7) / 8) };
}

#[inline]
pub fn steps_mut(&mut self) -> &mut [u8] {
return unsafe { std::slice::from_raw_parts_mut(self.inner_mut().steps_ptr, (self.inner().key_count + 7) / 8) };
return &self.steps;
}

/// Track name.
#[inline]
pub fn name(&self) -> &str {
unsafe {
let name = slice::from_raw_parts(self.inner().name_ptr, self.inner().name_len);
return std::str::from_utf8_unchecked(name);
};
return &self.name;
}
}
2 changes: 1 addition & 1 deletion src/track_sampling_job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ mod track_sampling_tests {
assert!(job.run().unwrap_err().is_invalid_job());

let mut job: TrackSamplingJob<Vec3> = TrackSamplingJob::default();
job.set_track(Rc::new(Track::new(0, 0)));
job.set_track(Rc::new(Track::default()));
assert!(job.validate());
assert!(job.run().is_ok());
}
Expand Down
Loading

0 comments on commit f8c0f78

Please sign in to comment.