Skip to main content

modelio/
mesh.rs

1use std::ptr;
2
3use crate::error::Result;
4use crate::ffi;
5use crate::handle::ObjectHandle;
6use crate::object::Object;
7use crate::submesh::Submesh;
8use crate::types::{BoundingBox, GeometryType, MeshBufferInfo, VertexAttributeInfo};
9use crate::util::{c_string, parse_json, required_handle};
10use crate::vertex_attribute::VertexDescriptor;
11
12#[derive(Debug, Clone)]
13pub struct Mesh {
14    handle: ObjectHandle,
15}
16
17impl Mesh {
18    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
19        Self { handle }
20    }
21
22    pub fn new_box(
23        extent: [f32; 3],
24        segments: [u32; 3],
25        inward_normals: bool,
26        geometry_type: GeometryType,
27    ) -> Result<Self> {
28        let mut out_mesh = ptr::null_mut();
29        let mut out_error = ptr::null_mut();
30        let status = unsafe {
31            ffi::mdl_mesh_new_box(
32                extent[0],
33                extent[1],
34                extent[2],
35                segments[0],
36                segments[1],
37                segments[2],
38                i32::from(inward_normals),
39                geometry_type.as_raw(),
40                &mut out_mesh,
41                &mut out_error,
42            )
43        };
44        crate::util::status_result(status, out_error)?;
45        Ok(Self::from_handle(required_handle(out_mesh, "MDLMesh box")?))
46    }
47
48    pub fn new_ellipsoid(
49        extent: [f32; 3],
50        segments: [u32; 2],
51        inward_normals: bool,
52        hemisphere: bool,
53        geometry_type: GeometryType,
54    ) -> Result<Self> {
55        let mut out_mesh = ptr::null_mut();
56        let mut out_error = ptr::null_mut();
57        let status = unsafe {
58            ffi::mdl_mesh_new_ellipsoid(
59                extent[0],
60                extent[1],
61                extent[2],
62                segments[0],
63                segments[1],
64                i32::from(inward_normals),
65                i32::from(hemisphere),
66                geometry_type.as_raw(),
67                &mut out_mesh,
68                &mut out_error,
69            )
70        };
71        crate::util::status_result(status, out_error)?;
72        Ok(Self::from_handle(required_handle(
73            out_mesh,
74            "MDLMesh ellipsoid",
75        )?))
76    }
77
78    pub fn new_sphere(
79        radius: f32,
80        segments: [u32; 2],
81        inward_normals: bool,
82        geometry_type: GeometryType,
83    ) -> Result<Self> {
84        Self::new_ellipsoid(
85            [radius, radius, radius],
86            segments,
87            inward_normals,
88            false,
89            geometry_type,
90        )
91    }
92
93    pub fn new_cylinder(
94        extent: [f32; 3],
95        segments: [u32; 2],
96        inward_normals: bool,
97        top_cap: bool,
98        bottom_cap: bool,
99        geometry_type: GeometryType,
100    ) -> Result<Self> {
101        let mut out_mesh = ptr::null_mut();
102        let mut out_error = ptr::null_mut();
103        let status = unsafe {
104            ffi::mdl_mesh_new_cylinder(
105                extent[0],
106                extent[1],
107                extent[2],
108                segments[0],
109                segments[1],
110                i32::from(inward_normals),
111                i32::from(top_cap),
112                i32::from(bottom_cap),
113                geometry_type.as_raw(),
114                &mut out_mesh,
115                &mut out_error,
116            )
117        };
118        crate::util::status_result(status, out_error)?;
119        Ok(Self::from_handle(required_handle(
120            out_mesh,
121            "MDLMesh cylinder",
122        )?))
123    }
124
125    pub fn new_plane(
126        extent: [f32; 3],
127        segments: [u32; 2],
128        geometry_type: GeometryType,
129    ) -> Result<Self> {
130        let mut out_mesh = ptr::null_mut();
131        let mut out_error = ptr::null_mut();
132        let status = unsafe {
133            ffi::mdl_mesh_new_plane(
134                extent[0],
135                extent[1],
136                extent[2],
137                segments[0],
138                segments[1],
139                geometry_type.as_raw(),
140                &mut out_mesh,
141                &mut out_error,
142            )
143        };
144        crate::util::status_result(status, out_error)?;
145        Ok(Self::from_handle(required_handle(
146            out_mesh,
147            "MDLMesh plane",
148        )?))
149    }
150
151    pub fn new_icosahedron(
152        extent: [f32; 3],
153        inward_normals: bool,
154        geometry_type: GeometryType,
155    ) -> Result<Self> {
156        let mut out_mesh = ptr::null_mut();
157        let mut out_error = ptr::null_mut();
158        let status = unsafe {
159            ffi::mdl_mesh_new_icosahedron(
160                extent[0],
161                extent[1],
162                extent[2],
163                i32::from(inward_normals),
164                geometry_type.as_raw(),
165                &mut out_mesh,
166                &mut out_error,
167            )
168        };
169        crate::util::status_result(status, out_error)?;
170        Ok(Self::from_handle(required_handle(
171            out_mesh,
172            "MDLMesh icosahedron",
173        )?))
174    }
175
176    #[must_use]
177    pub fn vertex_count(&self) -> usize {
178        unsafe { ffi::mdl_mesh_vertex_count(self.handle.as_ptr()) as usize }
179    }
180
181    #[must_use]
182    pub fn vertex_buffer_count(&self) -> usize {
183        unsafe { ffi::mdl_mesh_vertex_buffer_count(self.handle.as_ptr()) as usize }
184    }
185
186    #[must_use]
187    pub fn vertex_buffer(&self, index: usize) -> Option<MeshBuffer> {
188        let ptr = unsafe { ffi::mdl_mesh_vertex_buffer_at(self.handle.as_ptr(), index as u64) };
189        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBuffer::from_handle)
190    }
191
192    #[must_use]
193    pub fn vertex_buffers(&self) -> Vec<MeshBuffer> {
194        (0..self.vertex_buffer_count())
195            .filter_map(|index| self.vertex_buffer(index))
196            .collect()
197    }
198
199    #[must_use]
200    pub fn submesh_count(&self) -> usize {
201        unsafe { ffi::mdl_mesh_submesh_count(self.handle.as_ptr()) as usize }
202    }
203
204    #[must_use]
205    pub fn submesh(&self, index: usize) -> Option<Submesh> {
206        let ptr = unsafe { ffi::mdl_mesh_submesh_at(self.handle.as_ptr(), index as u64) };
207        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Submesh::from_handle)
208    }
209
210    #[must_use]
211    pub fn submeshes(&self) -> Vec<Submesh> {
212        (0..self.submesh_count())
213            .filter_map(|index| self.submesh(index))
214            .collect()
215    }
216
217    #[must_use]
218    pub fn bounding_box(&self) -> BoundingBox {
219        let mut min = [0.0_f32; 3];
220        let mut max = [0.0_f32; 3];
221        unsafe {
222            ffi::mdl_mesh_bounding_box(
223                self.handle.as_ptr(),
224                &mut min[0],
225                &mut min[1],
226                &mut min[2],
227                &mut max[0],
228                &mut max[1],
229                &mut max[2],
230            );
231        }
232        BoundingBox { min, max }
233    }
234
235    #[must_use]
236    pub fn vertex_descriptor(&self) -> Option<VertexDescriptor> {
237        let ptr = unsafe { ffi::mdl_mesh_vertex_descriptor(self.handle.as_ptr()) };
238        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(VertexDescriptor::from_handle)
239    }
240
241    pub fn vertex_attribute_data_named(
242        &self,
243        attribute_name: &str,
244    ) -> Result<Option<VertexAttributeData>> {
245        let attribute_name = c_string(attribute_name)?;
246        let ptr = unsafe {
247            ffi::mdl_mesh_vertex_attribute_data(self.handle.as_ptr(), attribute_name.as_ptr())
248        };
249        Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(VertexAttributeData::from_handle))
250    }
251
252    #[must_use]
253    pub fn as_object(&self) -> Object {
254        Object::from_handle(self.handle.clone())
255    }
256}
257
258#[derive(Debug, Clone)]
259pub struct MeshBuffer {
260    handle: ObjectHandle,
261}
262
263impl MeshBuffer {
264    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
265        Self { handle }
266    }
267
268    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
269        self.handle.as_ptr()
270    }
271
272    pub fn info(&self) -> Result<MeshBufferInfo> {
273        parse_json(
274            unsafe { ffi::mdl_mesh_buffer_info_json(self.handle.as_ptr()) },
275            "MDLMeshBuffer",
276        )
277    }
278
279    pub fn bytes(&self) -> Result<Vec<u8>> {
280        let info = self.info()?;
281        let mut bytes = vec![0_u8; info.length];
282        if bytes.is_empty() {
283            return Ok(bytes);
284        }
285        let written = unsafe {
286            ffi::mdl_mesh_buffer_copy_bytes(
287                self.handle.as_ptr(),
288                bytes.as_mut_ptr(),
289                bytes.len() as u64,
290            )
291        } as usize;
292        bytes.truncate(written);
293        Ok(bytes)
294    }
295}
296
297#[derive(Debug, Clone)]
298pub struct VertexAttributeData {
299    handle: ObjectHandle,
300}
301
302impl VertexAttributeData {
303    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
304        Self { handle }
305    }
306
307    pub fn info(&self) -> Result<VertexAttributeInfo> {
308        parse_json(
309            unsafe { ffi::mdl_vertex_attribute_data_info_json(self.handle.as_ptr()) },
310            "MDLVertexAttributeData",
311        )
312    }
313
314    pub fn bytes(&self) -> Result<Vec<u8>> {
315        let info = self.info()?;
316        let mut bytes = vec![0_u8; info.buffer_size];
317        if bytes.is_empty() {
318            return Ok(bytes);
319        }
320        let written = unsafe {
321            ffi::mdl_vertex_attribute_data_copy_bytes(
322                self.handle.as_ptr(),
323                bytes.as_mut_ptr(),
324                bytes.len() as u64,
325            )
326        } as usize;
327        bytes.truncate(written);
328        Ok(bytes)
329    }
330}