Skip to main content

modelio/
asset.rs

1use std::path::Path;
2use std::ptr;
3
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::mesh::Mesh;
8use crate::object::Object;
9use crate::types::{AssetInfo, BoundingBox};
10use crate::util::{c_string, parse_json, path_to_c_string, required_handle, take_string};
11
12#[derive(Debug, Clone)]
13pub struct Asset {
14    handle: ObjectHandle,
15}
16
17impl Asset {
18    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
19        Self { handle }
20    }
21
22    pub fn new() -> Result<Self> {
23        let mut out_asset = ptr::null_mut();
24        let mut out_error = ptr::null_mut();
25        let status = unsafe { ffi::mdl_asset_new_empty(&mut out_asset, &mut out_error) };
26        crate::util::status_result(status, out_error)?;
27        Ok(Self::from_handle(required_handle(out_asset, "MDLAsset")?))
28    }
29
30    pub fn from_url(path: impl AsRef<Path>) -> Result<Self> {
31        let path = path_to_c_string(path.as_ref())?;
32        let mut out_asset = ptr::null_mut();
33        let mut out_error = ptr::null_mut();
34        let status =
35            unsafe { ffi::mdl_asset_new_with_url(path.as_ptr(), &mut out_asset, &mut out_error) };
36        crate::util::status_result(status, out_error)?;
37        Ok(Self::from_handle(required_handle(out_asset, "MDLAsset")?))
38    }
39
40    #[must_use]
41    pub fn can_import_file_extension(path_extension: &str) -> bool {
42        let trimmed = path_extension.trim_start_matches('.');
43        crate::util::c_string(trimmed)
44            .is_ok_and(|ext| unsafe { ffi::mdl_asset_can_import_file_extension(ext.as_ptr()) != 0 })
45    }
46
47    #[must_use]
48    pub fn can_export_file_extension(path_extension: &str) -> bool {
49        let trimmed = path_extension.trim_start_matches('.');
50        crate::util::c_string(trimmed)
51            .is_ok_and(|ext| unsafe { ffi::mdl_asset_can_export_file_extension(ext.as_ptr()) != 0 })
52    }
53
54    pub fn info(&self) -> Result<AssetInfo> {
55        parse_json(
56            unsafe { ffi::mdl_asset_info_json(self.handle.as_ptr()) },
57            "MDLAsset",
58        )
59    }
60
61    pub fn export_to_url(&self, path: impl AsRef<Path>) -> Result<()> {
62        let path = path_to_c_string(path.as_ref())?;
63        let mut out_error = ptr::null_mut();
64        let status = unsafe {
65            ffi::mdl_asset_export_to_url(self.handle.as_ptr(), path.as_ptr(), &mut out_error)
66        };
67        crate::util::status_result(status, out_error)
68    }
69
70    #[must_use]
71    pub fn count(&self) -> usize {
72        unsafe { ffi::mdl_asset_count(self.handle.as_ptr()) as usize }
73    }
74
75    #[must_use]
76    pub fn bounding_box(&self) -> BoundingBox {
77        let mut min = [0.0_f32; 3];
78        let mut max = [0.0_f32; 3];
79        unsafe {
80            ffi::mdl_asset_bounding_box(
81                self.handle.as_ptr(),
82                &mut min[0],
83                &mut min[1],
84                &mut min[2],
85                &mut max[0],
86                &mut max[1],
87                &mut max[2],
88            );
89        }
90        BoundingBox { min, max }
91    }
92
93    #[must_use]
94    pub fn bounding_box_at_time(&self, time: f64) -> BoundingBox {
95        let mut min = [0.0_f32; 3];
96        let mut max = [0.0_f32; 3];
97        unsafe {
98            ffi::mdl_asset_bounding_box_at_time(
99                self.handle.as_ptr(),
100                time,
101                &mut min[0],
102                &mut min[1],
103                &mut min[2],
104                &mut max[0],
105                &mut max[1],
106                &mut max[2],
107            );
108        }
109        BoundingBox { min, max }
110    }
111
112    #[must_use]
113    pub fn url(&self) -> Option<String> {
114        take_string(unsafe { ffi::mdl_asset_url_string(self.handle.as_ptr()) })
115    }
116
117    #[must_use]
118    pub fn frame_interval(&self) -> f64 {
119        unsafe { ffi::mdl_asset_frame_interval(self.handle.as_ptr()) }
120    }
121
122    pub fn set_frame_interval(&self, value: f64) {
123        unsafe { ffi::mdl_asset_set_frame_interval(self.handle.as_ptr(), value) };
124    }
125
126    #[must_use]
127    pub fn start_time(&self) -> f64 {
128        unsafe { ffi::mdl_asset_start_time(self.handle.as_ptr()) }
129    }
130
131    pub fn set_start_time(&self, value: f64) {
132        unsafe { ffi::mdl_asset_set_start_time(self.handle.as_ptr(), value) };
133    }
134
135    #[must_use]
136    pub fn end_time(&self) -> f64 {
137        unsafe { ffi::mdl_asset_end_time(self.handle.as_ptr()) }
138    }
139
140    pub fn set_end_time(&self, value: f64) {
141        unsafe { ffi::mdl_asset_set_end_time(self.handle.as_ptr(), value) };
142    }
143
144    #[must_use]
145    pub fn up_axis(&self) -> [f32; 3] {
146        let mut axis = [0.0_f32; 3];
147        unsafe {
148            ffi::mdl_asset_up_axis(
149                self.handle.as_ptr(),
150                &mut axis[0],
151                &mut axis[1],
152                &mut axis[2],
153            );
154        };
155        axis
156    }
157
158    pub fn set_up_axis(&self, axis: [f32; 3]) {
159        unsafe { ffi::mdl_asset_set_up_axis(self.handle.as_ptr(), axis[0], axis[1], axis[2]) };
160    }
161
162    #[must_use]
163    pub fn object_at(&self, index: usize) -> Option<Object> {
164        let ptr = unsafe { ffi::mdl_asset_object_at_index(self.handle.as_ptr(), index as u64) };
165        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Object::from_handle)
166    }
167
168    pub fn object_at_path(&self, path: &str) -> Result<Option<Object>> {
169        let path = c_string(path)?;
170        let ptr = unsafe { ffi::mdl_asset_object_at_path(self.handle.as_ptr(), path.as_ptr()) };
171        Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Object::from_handle))
172    }
173
174    pub fn add_object(&self, object: &Object) {
175        unsafe { ffi::mdl_asset_add_object(self.handle.as_ptr(), object.as_ptr()) };
176    }
177
178    pub fn remove_object(&self, object: &Object) {
179        unsafe { ffi::mdl_asset_remove_object(self.handle.as_ptr(), object.as_ptr()) };
180    }
181
182    #[must_use]
183    pub fn mesh_at(&self, index: usize) -> Option<Mesh> {
184        let ptr = unsafe { ffi::mdl_asset_mesh_at_index(self.handle.as_ptr(), index as u64) };
185        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
186    }
187
188    #[must_use]
189    pub fn meshes(&self) -> Vec<Mesh> {
190        (0..self.count())
191            .filter_map(|index| self.mesh_at(index))
192            .collect()
193    }
194
195    pub fn load_textures(&self) {
196        unsafe { ffi::mdl_asset_load_textures(self.handle.as_ptr()) };
197    }
198}