Skip to main content

box3d_rust/hull/
box_hull.rs

1//! Box hull constructors. (hull.c: b3Make*BoxHull, b3ScaleBox)
2
3use super::types::{
4    BoxHull, HullData, HullFace, HullHalfEdge, HullVertex, BOX_HULL_SIZE, HULL_VERSION,
5};
6use crate::constants::linear_slop;
7use crate::core::{hash, non_zero_hash, HASH_INIT};
8use crate::math_functions::{
9    aabb_transform, abs, box_inertia, inv_rotate_vector, make_matrix_from_quat,
10    make_plane_from_normal_and_point, make_quat_from_matrix, max, min, min_float, mul, mul_sv, neg,
11    normalize, rotate_inertia, rotate_vector, transform_plane, transform_point, Aabb, Transform,
12    Vec3, QUAT_IDENTITY, TRANSFORM_IDENTITY, VEC3_AXIS_X, VEC3_AXIS_Y, VEC3_AXIS_Z, VEC3_ZERO,
13};
14
15fn box_hull_template() -> BoxHull {
16    let edges: [HullHalfEdge; 24] = [
17        HullHalfEdge {
18            next: 2,
19            twin: 1,
20            origin: 2,
21            face: 0,
22        },
23        HullHalfEdge {
24            next: 17,
25            twin: 0,
26            origin: 1,
27            face: 5,
28        },
29        HullHalfEdge {
30            next: 4,
31            twin: 3,
32            origin: 1,
33            face: 0,
34        },
35        HullHalfEdge {
36            next: 20,
37            twin: 2,
38            origin: 5,
39            face: 3,
40        },
41        HullHalfEdge {
42            next: 6,
43            twin: 5,
44            origin: 5,
45            face: 0,
46        },
47        HullHalfEdge {
48            next: 23,
49            twin: 4,
50            origin: 6,
51            face: 4,
52        },
53        HullHalfEdge {
54            next: 0,
55            twin: 7,
56            origin: 6,
57            face: 0,
58        },
59        HullHalfEdge {
60            next: 18,
61            twin: 6,
62            origin: 2,
63            face: 2,
64        },
65        HullHalfEdge {
66            next: 10,
67            twin: 9,
68            origin: 0,
69            face: 1,
70        },
71        HullHalfEdge {
72            next: 21,
73            twin: 8,
74            origin: 3,
75            face: 5,
76        },
77        HullHalfEdge {
78            next: 12,
79            twin: 11,
80            origin: 3,
81            face: 1,
82        },
83        HullHalfEdge {
84            next: 16,
85            twin: 10,
86            origin: 7,
87            face: 2,
88        },
89        HullHalfEdge {
90            next: 14,
91            twin: 13,
92            origin: 7,
93            face: 1,
94        },
95        HullHalfEdge {
96            next: 19,
97            twin: 12,
98            origin: 4,
99            face: 4,
100        },
101        HullHalfEdge {
102            next: 8,
103            twin: 15,
104            origin: 4,
105            face: 1,
106        },
107        HullHalfEdge {
108            next: 22,
109            twin: 14,
110            origin: 0,
111            face: 3,
112        },
113        HullHalfEdge {
114            next: 7,
115            twin: 17,
116            origin: 3,
117            face: 2,
118        },
119        HullHalfEdge {
120            next: 9,
121            twin: 16,
122            origin: 2,
123            face: 5,
124        },
125        HullHalfEdge {
126            next: 11,
127            twin: 19,
128            origin: 6,
129            face: 2,
130        },
131        HullHalfEdge {
132            next: 5,
133            twin: 18,
134            origin: 7,
135            face: 4,
136        },
137        HullHalfEdge {
138            next: 15,
139            twin: 21,
140            origin: 1,
141            face: 3,
142        },
143        HullHalfEdge {
144            next: 1,
145            twin: 20,
146            origin: 0,
147            face: 5,
148        },
149        HullHalfEdge {
150            next: 3,
151            twin: 23,
152            origin: 4,
153            face: 3,
154        },
155        HullHalfEdge {
156            next: 13,
157            twin: 22,
158            origin: 5,
159            face: 4,
160        },
161    ];
162
163    // Offsets match offsetof in C b3BoxHull.
164    let vertex_offset = 136;
165    let point_offset = 144;
166    let edge_offset = 240;
167    let face_offset = 336;
168    let plane_offset = 344;
169
170    BoxHull {
171        base: HullData {
172            version: HULL_VERSION,
173            byte_count: BOX_HULL_SIZE as i32,
174            hash: 0,
175            aabb: Aabb::default(),
176            surface_area: 0.0,
177            volume: 0.0,
178            inner_radius: 0.0,
179            center: VEC3_ZERO,
180            central_inertia: crate::math_functions::MAT3_ZERO,
181            vertex_count: 8,
182            vertex_offset,
183            point_offset,
184            edge_count: 24,
185            edge_offset,
186            face_count: 6,
187            face_offset,
188            plane_offset,
189            padding: 0,
190            vertices: Vec::new(),
191            points: Vec::new(),
192            edges: Vec::new(),
193            faces: Vec::new(),
194            planes: Vec::new(),
195        },
196        box_vertices: [
197            HullVertex { edge: 8 },
198            HullVertex { edge: 1 },
199            HullVertex { edge: 0 },
200            HullVertex { edge: 9 },
201            HullVertex { edge: 13 },
202            HullVertex { edge: 3 },
203            HullVertex { edge: 5 },
204            HullVertex { edge: 11 },
205        ],
206        box_points: [VEC3_ZERO; 8],
207        box_edges: edges,
208        box_faces: [
209            HullFace { edge: 0 },
210            HullFace { edge: 8 },
211            HullFace { edge: 16 },
212            HullFace { edge: 20 },
213            HullFace { edge: 19 },
214            HullFace { edge: 21 },
215        ],
216        padding: [0, 0],
217        box_planes: [crate::math_functions::Plane {
218            normal: VEC3_ZERO,
219            offset: 0.0,
220        }; 6],
221    }
222}
223
224/// Make a transformed box as a hull. (b3MakeTransformedBoxHull)
225pub fn make_transformed_box_hull(hx: f32, hy: f32, hz: f32, transform: Transform) -> BoxHull {
226    let mut box_hull = box_hull_template();
227
228    let min_h = 0.2 * linear_slop();
229    let h = max(
230        Vec3 {
231            x: min_h,
232            y: min_h,
233            z: min_h,
234        },
235        Vec3 {
236            x: hx,
237            y: hy,
238            z: hz,
239        },
240    );
241
242    box_hull.base.aabb = aabb_transform(
243        transform,
244        Aabb {
245            lower_bound: neg(h),
246            upper_bound: h,
247        },
248    );
249    box_hull.base.surface_area = 8.0 * (h.x * h.y + h.x * h.z + h.y * h.z);
250    box_hull.base.volume = 8.0 * h.x * h.y * h.z;
251    box_hull.base.inner_radius = min_float(h.x, min_float(h.y, h.z));
252    box_hull.base.center = transform.p;
253
254    let box_inertia = box_inertia(box_hull.base.volume, neg(h), h);
255    box_hull.base.central_inertia = rotate_inertia(transform.q, box_inertia);
256
257    let lower = neg(h);
258    let upper = h;
259
260    box_hull.box_planes[0] = transform_plane(
261        transform,
262        make_plane_from_normal_and_point(neg(VEC3_AXIS_X), lower),
263    );
264    box_hull.box_planes[1] = transform_plane(
265        transform,
266        make_plane_from_normal_and_point(VEC3_AXIS_X, upper),
267    );
268    box_hull.box_planes[2] = transform_plane(
269        transform,
270        make_plane_from_normal_and_point(neg(VEC3_AXIS_Y), lower),
271    );
272    box_hull.box_planes[3] = transform_plane(
273        transform,
274        make_plane_from_normal_and_point(VEC3_AXIS_Y, upper),
275    );
276    box_hull.box_planes[4] = transform_plane(
277        transform,
278        make_plane_from_normal_and_point(neg(VEC3_AXIS_Z), lower),
279    );
280    box_hull.box_planes[5] = transform_plane(
281        transform,
282        make_plane_from_normal_and_point(VEC3_AXIS_Z, upper),
283    );
284
285    box_hull.box_points[0] = transform_point(
286        transform,
287        Vec3 {
288            x: h.x,
289            y: h.y,
290            z: h.z,
291        },
292    );
293    box_hull.box_points[1] = transform_point(
294        transform,
295        Vec3 {
296            x: -h.x,
297            y: h.y,
298            z: h.z,
299        },
300    );
301    box_hull.box_points[2] = transform_point(
302        transform,
303        Vec3 {
304            x: -h.x,
305            y: -h.y,
306            z: h.z,
307        },
308    );
309    box_hull.box_points[3] = transform_point(
310        transform,
311        Vec3 {
312            x: h.x,
313            y: -h.y,
314            z: h.z,
315        },
316    );
317    box_hull.box_points[4] = transform_point(
318        transform,
319        Vec3 {
320            x: h.x,
321            y: h.y,
322            z: -h.z,
323        },
324    );
325    box_hull.box_points[5] = transform_point(
326        transform,
327        Vec3 {
328            x: -h.x,
329            y: h.y,
330            z: -h.z,
331        },
332    );
333    box_hull.box_points[6] = transform_point(
334        transform,
335        Vec3 {
336            x: -h.x,
337            y: -h.y,
338            z: -h.z,
339        },
340    );
341    box_hull.box_points[7] = transform_point(
342        transform,
343        Vec3 {
344            x: h.x,
345            y: -h.y,
346            z: -h.z,
347        },
348    );
349
350    // Keep base Vec accessors in sync with the embedded arrays (C offsets into
351    // the same allocation). Hash still uses the contiguous byte layout.
352    box_hull.sync_base_arrays();
353
354    box_hull.base.hash = 0;
355    let bytes = box_hull.to_bytes_with_hash(0);
356    box_hull.base.hash = non_zero_hash(hash(HASH_INIT, &bytes));
357
358    box_hull
359}
360
361/// Make a cube as a hull. (b3MakeCubeHull)
362pub fn make_cube_hull(half_width: f32) -> BoxHull {
363    make_box_hull(half_width, half_width, half_width)
364}
365
366/// Make an offset box as a hull. (b3MakeOffsetBoxHull)
367pub fn make_offset_box_hull(hx: f32, hy: f32, hz: f32, offset: Vec3) -> BoxHull {
368    let transform = Transform {
369        p: offset,
370        q: QUAT_IDENTITY,
371    };
372    make_transformed_box_hull(hx, hy, hz, transform)
373}
374
375/// Make a box as a hull. (b3MakeBoxHull)
376pub fn make_box_hull(hx: f32, hy: f32, hz: f32) -> BoxHull {
377    make_transformed_box_hull(hx, hy, hz, TRANSFORM_IDENTITY)
378}
379
380/// Scale a box's half-widths and transform by a post-scale. (b3ScaleBox)
381pub fn scale_box(
382    half_widths: &mut Vec3,
383    transform: &mut Transform,
384    post_scale: Vec3,
385    min_half_width: f32,
386) {
387    debug_assert!(crate::math_functions::is_valid_float(min_half_width) && min_half_width > 0.0);
388
389    let mut q = transform.q;
390
391    if post_scale.x < 0.0 || post_scale.y < 0.0 || post_scale.z < 0.0 {
392        let mut m = make_matrix_from_quat(q);
393        m.cx.x *= post_scale.x;
394        m.cy.x *= post_scale.x;
395        m.cz.x *= post_scale.x;
396        m.cx.y *= post_scale.y;
397        m.cy.y *= post_scale.y;
398        m.cz.y *= post_scale.y;
399        m.cx.z *= post_scale.z;
400        m.cy.z *= post_scale.z;
401        m.cz.z *= post_scale.z;
402        m.cx = normalize(m.cx);
403        m.cy = normalize(m.cy);
404        m.cz = normalize(m.cz);
405        m.cx = if post_scale.x < 0.0 { neg(m.cx) } else { m.cx };
406        m.cy = if post_scale.y < 0.0 { neg(m.cy) } else { m.cy };
407        m.cz = if post_scale.z < 0.0 { neg(m.cz) } else { m.cz };
408        q = make_quat_from_matrix(&m);
409    }
410
411    let abs_scale = abs(post_scale);
412    let h = *half_widths;
413    let p1 = mul(abs_scale, rotate_vector(q, neg(h)));
414    let p2 = mul(abs_scale, rotate_vector(q, h));
415
416    let local_p1 = inv_rotate_vector(q, p1);
417    let local_p2 = inv_rotate_vector(q, p2);
418
419    let lower = min(local_p1, local_p2);
420    let upper = max(local_p1, local_p2);
421
422    let scaled_half_width = mul_sv(0.5, crate::math_functions::sub(upper, lower));
423    let m_limit = Vec3 {
424        x: min_half_width,
425        y: min_half_width,
426        z: min_half_width,
427    };
428    *half_widths = max(scaled_half_width, m_limit);
429    transform.p = mul(post_scale, transform.p);
430    transform.q = q;
431}
432
433/// Make a scaled box hull. (b3MakeScaledBoxHull)
434pub fn make_scaled_box_hull(half_widths: Vec3, transform: Transform, post_scale: Vec3) -> BoxHull {
435    let mut h = half_widths;
436    let mut xf = transform;
437    scale_box(&mut h, &mut xf, post_scale, 4.0 * linear_slop());
438    make_transformed_box_hull(h.x, h.y, h.z, xf)
439}