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}