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