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}