1use std::ptr;
2
3use crate::asset::Asset;
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::mesh::Mesh;
8use crate::mesh_buffer::MeshBufferAllocator;
9use crate::object::Object;
10use crate::types::{BoundingBox, VoxelArrayInfo, VoxelIndexExtent};
11use crate::util::{parse_json, required_handle};
12
13#[derive(Debug, Clone)]
14pub struct VoxelArray {
16 handle: ObjectHandle,
17}
18
19impl VoxelArray {
20 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
22 Self { handle }
23 }
24
25 pub fn new(
27 voxel_indices: &[[i32; 4]],
28 bounding_box: BoundingBox,
29 voxel_extent: f32,
30 ) -> Result<Self> {
31 let flattened = voxel_indices
32 .iter()
33 .flat_map(|index| index.iter().copied())
34 .collect::<Vec<_>>();
35 let mut out_voxel_array = ptr::null_mut();
36 let mut out_error = ptr::null_mut();
37 let status = unsafe {
39 ffi::mdl_voxel_array_new_with_indices(
40 flattened.as_ptr(),
41 voxel_indices.len() as u64,
42 bounding_box.min[0],
43 bounding_box.min[1],
44 bounding_box.min[2],
45 bounding_box.max[0],
46 bounding_box.max[1],
47 bounding_box.max[2],
48 voxel_extent,
49 &mut out_voxel_array,
50 &mut out_error,
51 )
52 };
53 crate::util::status_result(status, out_error)?;
54 Ok(Self::from_handle(required_handle(
55 out_voxel_array,
56 "MDLVoxelArray",
57 )?))
58 }
59
60 pub fn from_asset(asset: &Asset, divisions: i32, patch_radius: f32) -> Result<Self> {
62 let mut out_voxel_array = ptr::null_mut();
63 let mut out_error = ptr::null_mut();
64 let status = unsafe {
66 ffi::mdl_voxel_array_new_with_asset(
67 asset.as_ptr(),
68 divisions,
69 patch_radius,
70 &mut out_voxel_array,
71 &mut out_error,
72 )
73 };
74 crate::util::status_result(status, out_error)?;
75 Ok(Self::from_handle(required_handle(
76 out_voxel_array,
77 "MDLVoxelArray",
78 )?))
79 }
80
81 pub fn info(&self) -> Result<VoxelArrayInfo> {
83 parse_json(
84 unsafe { ffi::mdl_voxel_array_info_json(self.handle.as_ptr()) },
86 "MDLVoxelArray",
87 )
88 }
89
90 #[must_use]
91 pub fn count(&self) -> usize {
93 unsafe { ffi::mdl_voxel_array_count(self.handle.as_ptr()) as usize }
95 }
96
97 pub fn set_voxel(&self, index: [i32; 4]) {
99 unsafe {
101 ffi::mdl_voxel_array_set_voxel(
102 self.handle.as_ptr(),
103 index[0],
104 index[1],
105 index[2],
106 index[3],
107 );
108 };
109 }
110
111 pub fn set_voxels_for_mesh(&self, mesh: &Mesh, divisions: i32, patch_radius: f32) {
113 unsafe {
115 ffi::mdl_voxel_array_set_voxels_for_mesh(
116 self.handle.as_ptr(),
117 mesh.as_ptr(),
118 divisions,
119 patch_radius,
120 );
121 };
122 }
123
124 #[must_use]
125 #[allow(clippy::fn_params_excessive_bools)]
126 pub fn voxel_exists(
128 &self,
129 index: [i32; 4],
130 allow_any_x: bool,
131 allow_any_y: bool,
132 allow_any_z: bool,
133 allow_any_shell: bool,
134 ) -> bool {
135 unsafe {
137 ffi::mdl_voxel_array_voxel_exists(
138 self.handle.as_ptr(),
139 index[0],
140 index[1],
141 index[2],
142 index[3],
143 i32::from(allow_any_x),
144 i32::from(allow_any_y),
145 i32::from(allow_any_z),
146 i32::from(allow_any_shell),
147 ) != 0
148 }
149 }
150
151 #[must_use]
152 pub fn voxel_indices(&self) -> Vec<[i32; 4]> {
154 let count = self.count();
155 if count == 0 {
156 return Vec::new();
157 }
158 let mut flattened = vec![0_i32; count * 4];
159 let written = unsafe {
161 ffi::mdl_voxel_array_copy_indices(
162 self.handle.as_ptr(),
163 flattened.as_mut_ptr(),
164 count as u64,
165 )
166 } as usize;
167 flattened.truncate(written * 4);
168 flattened
169 .chunks_exact(4)
170 .map(|chunk| [chunk[0], chunk[1], chunk[2], chunk[3]])
171 .collect()
172 }
173
174 #[must_use]
175 pub fn voxels_within_extent(&self, extent: &VoxelIndexExtent) -> Vec<[i32; 4]> {
177 let capacity = self.count();
178 if capacity == 0 {
179 return Vec::new();
180 }
181 let mut flattened = vec![0_i32; capacity * 4];
182 let written = unsafe {
184 ffi::mdl_voxel_array_copy_voxels_within_extent(
185 self.handle.as_ptr(),
186 extent.minimum_extent[0],
187 extent.minimum_extent[1],
188 extent.minimum_extent[2],
189 extent.minimum_extent[3],
190 extent.maximum_extent[0],
191 extent.maximum_extent[1],
192 extent.maximum_extent[2],
193 extent.maximum_extent[3],
194 flattened.as_mut_ptr(),
195 capacity as u64,
196 )
197 } as usize;
198 flattened.truncate(written * 4);
199 flattened
200 .chunks_exact(4)
201 .map(|chunk| [chunk[0], chunk[1], chunk[2], chunk[3]])
202 .collect()
203 }
204
205 pub fn union_with(&self, other: &Self) {
207 unsafe { ffi::mdl_voxel_array_union(self.handle.as_ptr(), other.handle.as_ptr()) };
209 }
210
211 pub fn intersect_with(&self, other: &Self) {
213 unsafe { ffi::mdl_voxel_array_intersect(self.handle.as_ptr(), other.handle.as_ptr()) };
215 }
216
217 pub fn difference_with(&self, other: &Self) {
219 unsafe { ffi::mdl_voxel_array_difference(self.handle.as_ptr(), other.handle.as_ptr()) };
221 }
222
223 #[must_use]
224 pub fn index_of_spatial_location(&self, location: [f32; 3]) -> [i32; 4] {
226 let mut values = [0_i32; 4];
227 unsafe {
229 ffi::mdl_voxel_array_index_of_spatial_location(
230 self.handle.as_ptr(),
231 location[0],
232 location[1],
233 location[2],
234 values.as_mut_ptr(),
235 );
236 };
237 values
238 }
239
240 #[must_use]
241 pub fn spatial_location_of_index(&self, index: [i32; 4]) -> [f32; 3] {
243 let mut values = [0.0_f32; 3];
244 unsafe {
246 ffi::mdl_voxel_array_spatial_location_of_index(
247 self.handle.as_ptr(),
248 index[0],
249 index[1],
250 index[2],
251 index[3],
252 &mut values[0],
253 &mut values[1],
254 &mut values[2],
255 );
256 };
257 values
258 }
259
260 #[must_use]
261 pub fn voxel_bounding_box_at_index(&self, index: [i32; 4]) -> BoundingBox {
263 let mut min = [0.0_f32; 3];
264 let mut max = [0.0_f32; 3];
265 unsafe {
267 ffi::mdl_voxel_array_voxel_bounding_box_at_index(
268 self.handle.as_ptr(),
269 index[0],
270 index[1],
271 index[2],
272 index[3],
273 &mut min[0],
274 &mut min[1],
275 &mut min[2],
276 &mut max[0],
277 &mut max[1],
278 &mut max[2],
279 );
280 };
281 BoundingBox { min, max }
282 }
283
284 pub fn convert_to_signed_shell_field(&self) {
286 unsafe { ffi::mdl_voxel_array_convert_to_signed_shell_field(self.handle.as_ptr()) };
288 }
289
290 pub fn set_shell_field_interior_thickness(&self, value: f32) {
292 unsafe {
294 ffi::mdl_voxel_array_set_shell_field_interior_thickness(self.handle.as_ptr(), value);
295 };
296 }
297
298 pub fn set_shell_field_exterior_thickness(&self, value: f32) {
300 unsafe {
302 ffi::mdl_voxel_array_set_shell_field_exterior_thickness(self.handle.as_ptr(), value);
303 };
304 }
305
306 #[must_use]
307 pub fn coarse_mesh(&self) -> Option<Mesh> {
309 self.coarse_mesh_with_allocator(None)
310 }
311
312 #[must_use]
313 pub fn coarse_mesh_with_allocator(
315 &self,
316 allocator: Option<&MeshBufferAllocator>,
317 ) -> Option<Mesh> {
318 let ptr = unsafe {
320 ffi::mdl_voxel_array_coarse_mesh_with_allocator(
321 self.handle.as_ptr(),
322 allocator.map_or(ptr::null_mut(), MeshBufferAllocator::as_ptr),
323 )
324 };
325 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
327 }
328
329 #[must_use]
330 pub fn mesh(&self) -> Option<Mesh> {
332 self.mesh_with_allocator(None)
333 }
334
335 #[must_use]
336 pub fn mesh_with_allocator(&self, allocator: Option<&MeshBufferAllocator>) -> Option<Mesh> {
338 let ptr = unsafe {
340 ffi::mdl_voxel_array_mesh_with_allocator(
341 self.handle.as_ptr(),
342 allocator.map_or(ptr::null_mut(), MeshBufferAllocator::as_ptr),
343 )
344 };
345 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
347 }
348
349 #[must_use]
350 pub fn as_object(&self) -> Object {
352 Object::from_handle(self.handle.clone())
353 }
354}