Skip to main content

modelio/
skeleton.rs

1use std::ffi::CString;
2use std::ptr;
3
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::object::Object;
8use crate::types::SkeletonInfo;
9use crate::util::{c_string, parse_json, required_handle};
10use crate::value_types::Matrix4x4Array;
11
12fn c_string_vec(values: &[&str]) -> Result<(Vec<CString>, Vec<*const i8>)> {
13    let c_strings = values
14        .iter()
15        .map(|value| c_string(value))
16        .collect::<Result<Vec<_>>>()?;
17    let raw = c_strings.iter().map(|value| value.as_ptr()).collect();
18    Ok((c_strings, raw))
19}
20
21fn copy_matrices(
22    handle: &ObjectHandle,
23    count: usize,
24    copier: unsafe extern "C" fn(*mut std::ffi::c_void, *mut f32, u64) -> u64,
25) -> Vec<[f32; 16]> {
26    if count == 0 {
27        return Vec::new();
28    }
29    let mut flattened = vec![0.0_f32; count * 16];
30    // SAFETY: The unsafe operation is valid in this context.
31    let written = unsafe { copier(handle.as_ptr(), flattened.as_mut_ptr(), count as u64) } as usize;
32    flattened.truncate(written * 16);
33    flattened
34        .chunks_exact(16)
35        .map(|chunk| {
36            let mut matrix = [0.0_f32; 16];
37            matrix.copy_from_slice(chunk);
38            matrix
39        })
40        .collect()
41}
42
43#[derive(Debug, Clone)]
44/// Wraps the corresponding Model I/O skeleton counterpart.
45pub struct Skeleton {
46    /// Stores the corresponding Model I/O handle value for this wrapper.
47    pub(crate) handle: ObjectHandle,
48}
49
50impl Skeleton {
51    /// Builds this wrapper from the retained handle of the wrapped Model I/O skeleton counterpart.
52    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
53        Self { handle }
54    }
55
56    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O skeleton counterpart.
57    pub fn new(name: &str, joint_paths: &[&str]) -> Result<Self> {
58        let name = c_string(name)?;
59        let (_joint_paths, raw_joint_paths) = c_string_vec(joint_paths)?;
60        let mut out_skeleton = ptr::null_mut();
61        let mut out_error = ptr::null_mut();
62        // SAFETY: The unsafe operation is valid in this context.
63        let status = unsafe {
64            ffi::mdl_skeleton_new(
65                name.as_ptr(),
66                raw_joint_paths.as_ptr(),
67                raw_joint_paths.len() as u64,
68                &mut out_skeleton,
69                &mut out_error,
70            )
71        };
72        crate::util::status_result(status, out_error)?;
73        Ok(Self::from_handle(required_handle(
74            out_skeleton,
75            "MDLSkeleton",
76        )?))
77    }
78
79    /// Calls the corresponding Model I/O method on the wrapped Model I/O skeleton counterpart.
80    pub fn info(&self) -> Result<SkeletonInfo> {
81        parse_json(
82            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
83            unsafe { ffi::mdl_skeleton_info_json(self.handle.as_ptr()) },
84            "MDLSkeleton",
85        )
86    }
87
88    /// Calls the corresponding Model I/O method on the wrapped Model I/O skeleton counterpart.
89    pub fn joint_bind_transform_array(&self) -> Result<Matrix4x4Array> {
90        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
91        let ptr = unsafe { ffi::mdl_skeleton_joint_bind_transform_array(self.handle.as_ptr()) };
92        Ok(Matrix4x4Array::from_handle(required_handle(
93            ptr,
94            "MDLSkeleton jointBindTransforms",
95        )?))
96    }
97
98    /// Calls the corresponding Model I/O method on the wrapped Model I/O skeleton counterpart.
99    pub fn joint_rest_transform_array(&self) -> Result<Matrix4x4Array> {
100        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
101        let ptr = unsafe { ffi::mdl_skeleton_joint_rest_transform_array(self.handle.as_ptr()) };
102        Ok(Matrix4x4Array::from_handle(required_handle(
103            ptr,
104            "MDLSkeleton jointRestTransforms",
105        )?))
106    }
107
108    /// Calls the corresponding Model I/O method on the wrapped Model I/O skeleton counterpart.
109    pub fn joint_bind_transforms(&self) -> Result<Vec<[f32; 16]>> {
110        let count = self.info()?.joint_bind_transform_count;
111        Ok(copy_matrices(
112            &self.handle,
113            count,
114            ffi::mdl_skeleton_copy_joint_bind_transforms,
115        ))
116    }
117
118    /// Calls the corresponding Model I/O method on the wrapped Model I/O skeleton counterpart.
119    pub fn joint_rest_transforms(&self) -> Result<Vec<[f32; 16]>> {
120        let count = self.info()?.joint_rest_transform_count;
121        Ok(copy_matrices(
122            &self.handle,
123            count,
124            ffi::mdl_skeleton_copy_joint_rest_transforms,
125        ))
126    }
127
128    #[must_use]
129    /// Calls the corresponding Model I/O method on the wrapped Model I/O skeleton counterpart.
130    pub fn as_object(&self) -> Object {
131        Object::from_handle(self.handle.clone())
132    }
133}