Skip to main content

modelio/
object.rs

1use std::ptr;
2
3use crate::camera::Camera;
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::light::Light;
8use crate::mesh::Mesh;
9use crate::physically_plausible_light::PhysicallyPlausibleLight;
10use crate::skeleton::Skeleton;
11use crate::types::{BoundingBox, ObjectInfo, ObjectKind};
12use crate::util::{c_string, parse_json, required_handle, take_string};
13use crate::voxel_array::VoxelArray;
14use crate::PackedJointAnimation;
15
16#[derive(Debug, Clone)]
17pub struct Object {
18    handle: ObjectHandle,
19}
20
21impl Object {
22    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
23        Self { handle }
24    }
25
26    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
27        self.handle.as_ptr()
28    }
29
30    pub fn new() -> Result<Self> {
31        let mut out_object = ptr::null_mut();
32        let mut out_error = ptr::null_mut();
33        let status = unsafe { ffi::mdl_object_new(&mut out_object, &mut out_error) };
34        crate::util::status_result(status, out_error)?;
35        Ok(Self::from_handle(required_handle(out_object, "MDLObject")?))
36    }
37
38    pub fn info(&self) -> Result<ObjectInfo> {
39        parse_json(
40            unsafe { ffi::mdl_object_info_json(self.handle.as_ptr()) },
41            "MDLObject",
42        )
43    }
44
45    #[must_use]
46    pub fn kind(&self) -> ObjectKind {
47        ObjectKind::from_raw(unsafe { ffi::mdl_object_kind(self.handle.as_ptr()) })
48            .unwrap_or(ObjectKind::Unknown)
49    }
50
51    #[must_use]
52    pub fn name(&self) -> Option<String> {
53        take_string(unsafe { ffi::mdl_object_name_string(self.handle.as_ptr()) })
54    }
55
56    pub fn set_name(&self, name: &str) -> Result<()> {
57        let name = c_string(name)?;
58        unsafe { ffi::mdl_object_set_name(self.handle.as_ptr(), name.as_ptr()) };
59        Ok(())
60    }
61
62    #[must_use]
63    pub fn path(&self) -> Option<String> {
64        take_string(unsafe { ffi::mdl_object_path_string(self.handle.as_ptr()) })
65    }
66
67    #[must_use]
68    pub fn hidden(&self) -> bool {
69        unsafe { ffi::mdl_object_hidden(self.handle.as_ptr()) != 0 }
70    }
71
72    pub fn set_hidden(&self, hidden: bool) {
73        unsafe { ffi::mdl_object_set_hidden(self.handle.as_ptr(), i32::from(hidden)) };
74    }
75
76    pub fn add_child(&self, child: &Self) {
77        unsafe { ffi::mdl_object_add_child(self.handle.as_ptr(), child.handle.as_ptr()) };
78    }
79
80    #[must_use]
81    pub fn child_count(&self) -> usize {
82        unsafe { ffi::mdl_object_child_count(self.handle.as_ptr()) as usize }
83    }
84
85    #[must_use]
86    pub fn child_at(&self, index: usize) -> Option<Self> {
87        let ptr = unsafe { ffi::mdl_object_child_at(self.handle.as_ptr(), index as u64) };
88        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Self::from_handle)
89    }
90
91    #[must_use]
92    pub fn children(&self) -> Vec<Self> {
93        (0..self.child_count())
94            .filter_map(|index| self.child_at(index))
95            .collect()
96    }
97
98    pub fn at_path(&self, path: &str) -> Result<Option<Self>> {
99        let path = c_string(path)?;
100        let ptr = unsafe { ffi::mdl_object_at_path(self.handle.as_ptr(), path.as_ptr()) };
101        Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Self::from_handle))
102    }
103
104    #[must_use]
105    pub fn bounding_box_at_time(&self, time: f64) -> BoundingBox {
106        let mut min = [0.0_f32; 3];
107        let mut max = [0.0_f32; 3];
108        unsafe {
109            ffi::mdl_object_bounding_box_at_time(
110                self.handle.as_ptr(),
111                time,
112                &mut min[0],
113                &mut min[1],
114                &mut min[2],
115                &mut max[0],
116                &mut max[1],
117                &mut max[2],
118            );
119        }
120        BoundingBox { min, max }
121    }
122
123    #[must_use]
124    pub fn as_mesh(&self) -> Option<Mesh> {
125        (self.kind() == ObjectKind::Mesh).then(|| Mesh::from_handle(self.handle.clone()))
126    }
127
128    #[must_use]
129    pub fn as_light(&self) -> Option<Light> {
130        matches!(
131            self.kind(),
132            ObjectKind::Light | ObjectKind::PhysicallyPlausibleLight
133        )
134        .then(|| Light::from_handle(self.handle.clone()))
135    }
136
137    #[must_use]
138    pub fn as_physically_plausible_light(&self) -> Option<PhysicallyPlausibleLight> {
139        (self.kind() == ObjectKind::PhysicallyPlausibleLight)
140            .then(|| PhysicallyPlausibleLight::from_handle(self.handle.clone()))
141    }
142
143    #[must_use]
144    pub fn as_camera(&self) -> Option<Camera> {
145        (self.kind() == ObjectKind::Camera).then(|| Camera::from_handle(self.handle.clone()))
146    }
147
148    #[must_use]
149    pub fn as_voxel_array(&self) -> Option<VoxelArray> {
150        (self.kind() == ObjectKind::VoxelArray)
151            .then(|| VoxelArray::from_handle(self.handle.clone()))
152    }
153
154    #[must_use]
155    pub fn as_skeleton(&self) -> Option<Skeleton> {
156        (self.kind() == ObjectKind::Skeleton).then(|| Skeleton::from_handle(self.handle.clone()))
157    }
158
159    #[must_use]
160    pub fn as_packed_joint_animation(&self) -> Option<PackedJointAnimation> {
161        (self.kind() == ObjectKind::PackedJointAnimation)
162            .then(|| PackedJointAnimation::from_handle(self.handle.clone()))
163    }
164}