1use super::{
7 create_shape_proxy, destroy_shape_proxy, get_shape_centroid, shape_flags, Shape, ShapeGeometry,
8};
9use crate::compound::{get_compound_materials, CompoundData};
10use crate::constants::{linear_slop, max_aabb_margin, AABB_MARGIN_FRACTION, MAX_SHAPES};
11use crate::core::{NULL_INDEX, SECRET_COOKIE};
12use crate::geometry::{Capsule, ShapeType, Sphere};
13use crate::height_field::HeightFieldData;
14use crate::hull::{get_hull_points, HullData};
15use crate::id::{ShapeId, NULL_SHAPE_ID};
16use crate::math_functions::{
17 distance, distance_squared, is_valid_float, lerp, min_float, safe_scale, Aabb, Vec3,
18 WorldTransform, VEC3_ZERO,
19};
20use crate::mesh::{is_valid_mesh, MeshData};
21use crate::solver_set::DISABLED_SET;
22use crate::types::{BodyType, ShapeDef};
23use crate::world::World;
24
25pub fn compute_shape_margin(shape: &Shape) -> f32 {
28 let margin = match &shape.geometry {
29 ShapeGeometry::Sphere(sphere) => sphere.radius,
30 ShapeGeometry::Capsule(capsule) => {
31 0.5 * distance(capsule.center2, capsule.center1) + capsule.radius
32 }
33 ShapeGeometry::Hull(hull) => {
34 let points = get_hull_points(hull);
35 let mut max_extent_sqr = 0.0f32;
36 for point in points {
37 let dist_sqr = distance_squared(*point, hull.center);
38 max_extent_sqr = crate::math_functions::max_float(max_extent_sqr, dist_sqr);
39 }
40 max_extent_sqr.sqrt()
41 }
42 ShapeGeometry::Mesh { .. } | ShapeGeometry::HeightField(_) | ShapeGeometry::Compound(_) => {
43 return max_aabb_margin();
45 }
46 };
47
48 min_float(max_aabb_margin(), AABB_MARGIN_FRACTION * margin)
49}
50
51pub(crate) fn create_shape_internal(
54 world: &mut World,
55 body_index: i32,
56 body_transform: WorldTransform,
57 def: &ShapeDef,
58 geometry: ShapeGeometry,
59) -> i32 {
60 let shape_type = geometry.shape_type();
61
62 let shape_id = world.shape_id_pool.alloc_id();
63
64 if shape_id == world.shapes.len() as i32 {
65 world.shapes.push(Shape::default());
66 } else {
67 debug_assert!(world.shapes[shape_id as usize].id == NULL_INDEX);
68 }
69
70 let (body_raw_id, body_set_index, body_type, head_shape_id) = {
71 let body = &world.bodies[body_index as usize];
72 (body.id, body.set_index, body.type_, body.head_shape_id)
73 };
74
75 let truncated = if def.name.len() > crate::constants::SHAPE_NAME_LENGTH {
77 let mut end = crate::constants::SHAPE_NAME_LENGTH;
78 while end > 0 && !def.name.is_char_boundary(end) {
79 end -= 1;
80 }
81 &def.name[..end]
82 } else {
83 def.name.as_str()
84 };
85 let name_id = world.names.add_name(truncated);
86
87 {
88 let shape = &mut world.shapes[shape_id as usize];
89 shape.geometry = geometry;
90
91 shape.id = shape_id;
92 shape.body_id = body_raw_id;
93 shape.density = def.density;
94 shape.explosion_scale = def.explosion_scale;
95 shape.filter = def.filter;
96 shape.user_data = def.user_data;
97 shape.user_shape = 0;
98 shape.flags = 0;
99 if def.enable_sensor_events {
100 shape.flags |= shape_flags::ENABLE_SENSOR_EVENTS;
101 }
102 if def.enable_contact_events {
103 shape.flags |= shape_flags::ENABLE_CONTACT_EVENTS;
104 }
105 if def.enable_custom_filtering {
106 shape.flags |= shape_flags::ENABLE_CUSTOM_FILTERING;
107 }
108 if def.enable_hit_events {
109 shape.flags |= shape_flags::ENABLE_HIT_EVENTS;
110 }
111 if def.enable_pre_solve_events {
112 shape.flags |= shape_flags::ENABLE_PRE_SOLVE_EVENTS;
113 }
114 shape.proxy_key = NULL_INDEX;
115 shape.local_centroid = get_shape_centroid(shape);
116 shape.aabb_margin = compute_shape_margin(shape);
117 shape.aabb = Aabb {
118 lower_bound: VEC3_ZERO,
119 upper_bound: VEC3_ZERO,
120 };
121 shape.fat_aabb = Aabb {
122 lower_bound: VEC3_ZERO,
123 upper_bound: VEC3_ZERO,
124 };
125 shape.name_id = name_id;
126 shape.generation = shape.generation.wrapping_add(1);
127
128 if shape_type == ShapeType::Compound {
129 let mats = match &shape.geometry {
133 ShapeGeometry::Compound(compound) => get_compound_materials(compound).to_vec(),
134 _ => unreachable!(),
135 };
136 if mats.is_empty() {
137 shape.material = def.base_material;
138 shape.materials.clear();
139 } else if mats.len() == 1 {
140 shape.material = mats[0];
141 shape.materials.clear();
142 } else {
143 shape.material = def.base_material;
144 shape.materials = mats;
145 }
146 } else if def.materials.len() > 1 {
147 shape.materials = def.materials.clone();
148 shape.material = def.base_material;
149 } else if def.materials.len() == 1 {
150 shape.material = def.materials[0];
151 shape.materials.clear();
152 } else {
153 shape.material = def.base_material;
154 shape.materials.clear();
155 }
156 }
157
158 if body_set_index != DISABLED_SET {
159 let force_pair_creation = def.invoke_contact_creation && shape_type != ShapeType::Compound;
160 let (shapes, broad_phase) = (&mut world.shapes, &mut world.broad_phase);
161 create_shape_proxy(
162 &mut shapes[shape_id as usize],
163 broad_phase,
164 body_type,
165 body_transform,
166 force_pair_creation,
167 );
168 }
169
170 if head_shape_id != NULL_INDEX {
172 world.shapes[head_shape_id as usize].prev_shape_id = shape_id;
173 }
174
175 world.shapes[shape_id as usize].prev_shape_id = NULL_INDEX;
176 world.shapes[shape_id as usize].next_shape_id = head_shape_id;
177 world.bodies[body_index as usize].head_shape_id = shape_id;
178 world.bodies[body_index as usize].shape_count += 1;
179
180 if def.is_sensor {
181 world.shapes[shape_id as usize].sensor_index = world.sensors.len() as i32;
182 world.sensors.push(crate::sensor::Sensor::new(shape_id));
183 } else {
184 world.shapes[shape_id as usize].sensor_index = NULL_INDEX;
185 }
186
187 world.validate_solver_sets();
188
189 shape_id
190}
191
192fn create_shape(
194 world: &mut World,
195 body_id: crate::id::BodyId,
196 def: &ShapeDef,
197 geometry: ShapeGeometry,
198) -> ShapeId {
199 use crate::body::{
200 body_flags, get_body_full_id, get_body_transform_quick, sync_body_flags,
201 update_body_mass_data,
202 };
203
204 debug_assert!(def.internal_value == SECRET_COOKIE);
205 debug_assert!(is_valid_float(def.density) && def.density >= 0.0);
206 debug_assert!(is_valid_float(def.base_material.friction) && def.base_material.friction >= 0.0);
207 debug_assert!(
208 is_valid_float(def.base_material.restitution) && def.base_material.restitution >= 0.0
209 );
210
211 debug_assert!(!world.locked);
212 if world.locked {
213 return NULL_SHAPE_ID;
214 }
215
216 if world.shapes.len() as i32 == MAX_SHAPES && world.shape_id_pool.free_array.is_empty() {
217 debug_assert!(false);
218 return NULL_SHAPE_ID;
219 }
220
221 let body_index = get_body_full_id(world, body_id);
222 let shape_type = geometry.shape_type();
223
224 if world.bodies[body_index as usize].type_ != BodyType::Static
225 && (shape_type == ShapeType::Compound || shape_type == ShapeType::Height)
226 {
227 return NULL_SHAPE_ID;
229 }
230
231 world.locked = true;
232
233 let body_transform = get_body_transform_quick(world, &world.bodies[body_index as usize]);
234
235 let shape_id = create_shape_internal(world, body_index, body_transform, def, geometry);
236
237 if shape_id == NULL_INDEX {
238 world.locked = false;
239 return NULL_SHAPE_ID;
240 }
241
242 if def.update_body_mass {
243 update_body_mass_data(world, body_index);
244 } else if world.bodies[body_index as usize].flags & body_flags::DIRTY_MASS == 0 {
245 world.bodies[body_index as usize].flags |= body_flags::DIRTY_MASS;
246 sync_body_flags(world, body_index);
247 }
248
249 world.validate_solver_sets();
250
251 let id = ShapeId {
252 index1: shape_id + 1,
253 world0: body_id.world0,
254 generation: world.shapes[shape_id as usize].generation,
255 };
256
257 world.locked = false;
258
259 id
260}
261
262pub fn create_sphere_shape(
264 world: &mut World,
265 body_id: crate::id::BodyId,
266 def: &ShapeDef,
267 sphere: &Sphere,
268) -> ShapeId {
269 let shape_id = create_shape(world, body_id, def, ShapeGeometry::Sphere(*sphere));
270 if shape_id.index1 != 0 {
271 crate::recording::with_recording(world, |rec| {
272 rec.write_create_sphere_shape(body_id, def, *sphere, shape_id);
273 });
274 }
275 shape_id
276}
277
278pub fn create_capsule_shape(
280 world: &mut World,
281 body_id: crate::id::BodyId,
282 def: &ShapeDef,
283 capsule: &Capsule,
284) -> ShapeId {
285 let length_sqr = distance_squared(capsule.center1, capsule.center2);
286 let shape_id = if length_sqr <= linear_slop() * linear_slop() {
287 let sphere = Sphere {
288 center: lerp(capsule.center1, capsule.center2, 0.5),
289 radius: capsule.radius,
290 };
291 create_shape(world, body_id, def, ShapeGeometry::Sphere(sphere))
292 } else {
293 create_shape(world, body_id, def, ShapeGeometry::Capsule(*capsule))
294 };
295 if shape_id.index1 != 0 {
296 crate::recording::with_recording(world, |rec| {
297 rec.write_create_capsule_shape(body_id, def, *capsule, shape_id);
298 });
299 }
300 shape_id
301}
302
303pub fn create_hull_shape(
305 world: &mut World,
306 body_id: crate::id::BodyId,
307 def: &ShapeDef,
308 hull: &HullData,
309) -> ShapeId {
310 debug_assert!(crate::hull::is_valid_hull(hull));
311 debug_assert!(hull.hash != 0);
312 let shared = world.hull_database.add(hull);
313 let shape_id = create_shape(world, body_id, def, ShapeGeometry::Hull(shared));
314 if shape_id.index1 != 0 {
315 crate::recording::with_recording(world, |rec| {
316 let geometry_id = rec.registry.intern_hull(hull);
317 rec.write_create_hull_shape(body_id, def, geometry_id, shape_id);
318 });
319 }
320 shape_id
321}
322
323pub fn create_mesh_shape(
328 world: &mut World,
329 body_id: crate::id::BodyId,
330 def: &ShapeDef,
331 mesh: &MeshData,
332 scale: Vec3,
333) -> ShapeId {
334 debug_assert!(is_valid_mesh(Some(mesh)));
335 debug_assert!(mesh.hash != 0);
336 let shape_id = create_shape(
337 world,
338 body_id,
339 def,
340 ShapeGeometry::Mesh {
341 data: mesh.clone(),
342 scale: safe_scale(scale),
343 },
344 );
345 if shape_id.index1 != 0 {
346 crate::recording::with_recording(world, |rec| {
347 let geometry_id = rec.registry.intern_mesh(mesh);
348 rec.write_create_mesh_shape(body_id, def, geometry_id, safe_scale(scale), shape_id);
349 });
350 }
351 shape_id
352}
353
354pub fn create_height_field_shape(
358 world: &mut World,
359 body_id: crate::id::BodyId,
360 def: &ShapeDef,
361 height_field: &HeightFieldData,
362) -> ShapeId {
363 debug_assert!(height_field.hash != 0);
364 let shape_id = create_shape(
365 world,
366 body_id,
367 def,
368 ShapeGeometry::HeightField(height_field.clone()),
369 );
370 if shape_id.index1 != 0 {
371 crate::recording::with_recording(world, |rec| {
372 let geometry_id = rec.registry.intern_height_field(height_field);
373 rec.write_create_height_field_shape(body_id, def, geometry_id, shape_id);
374 });
375 }
376 shape_id
377}
378
379pub fn create_compound_shape(
384 world: &mut World,
385 body_id: crate::id::BodyId,
386 def: &ShapeDef,
387 compound: &CompoundData,
388) -> ShapeId {
389 debug_assert!(!def.is_sensor);
390 let shape_id = create_shape(
391 world,
392 body_id,
393 def,
394 ShapeGeometry::Compound(compound.clone()),
395 );
396 if shape_id.index1 != 0 {
397 crate::recording::with_recording(world, |rec| {
398 let geometry_id = rec.registry.intern_compound(compound);
399 rec.write_create_compound_shape(body_id, def, geometry_id, shape_id);
400 });
401 }
402 shape_id
403}
404
405pub fn get_shape(world: &World, shape_id: ShapeId) -> i32 {
407 let id = shape_id.index1 - 1;
408 let shape = &world.shapes[id as usize];
409 debug_assert!(shape.id == id && shape.generation == shape_id.generation);
410 id
411}
412
413pub fn shape_is_valid(world: &World, id: ShapeId) -> bool {
415 if id.index1 < 1 || (world.shapes.len() as i32) < id.index1 {
416 return false;
417 }
418 let shape = &world.shapes[(id.index1 - 1) as usize];
419 if shape.id == NULL_INDEX {
420 return false;
421 }
422 shape.generation == id.generation
423}
424
425pub fn shape_get_hull(world: &World, shape_id: ShapeId) -> Option<&HullData> {
427 let index = get_shape(world, shape_id);
428 match &world.shapes[index as usize].geometry {
429 ShapeGeometry::Hull(hull) => Some(hull.as_ref()),
430 _ => {
431 debug_assert!(false, "shape is not a hull");
432 None
433 }
434 }
435}
436
437pub(crate) fn destroy_shape_allocation_for_shape_change(world: &mut World, shape_index: i32) {
440 if matches!(
441 world.shapes[shape_index as usize].geometry,
442 ShapeGeometry::Hull(_)
443 ) {
444 let geometry = std::mem::replace(
445 &mut world.shapes[shape_index as usize].geometry,
446 ShapeGeometry::default(),
447 );
448 if let ShapeGeometry::Hull(rc) = &geometry {
449 world.hull_database.release(rc);
450 }
451 }
452
453 let user_shape = world.shapes[shape_index as usize].user_shape;
454 if user_shape != 0 {
455 if let Some(destroy) = world.destroy_debug_shape {
456 destroy(user_shape, world.user_debug_shape_context);
457 }
458 world.shapes[shape_index as usize].user_shape = 0;
459 }
460}
461
462fn destroy_shape_allocations(world: &mut World, shape_index: i32) {
465 destroy_shape_allocation_for_shape_change(world, shape_index);
466 world.shapes[shape_index as usize].materials.clear();
467}
468
469pub(crate) fn destroy_shape_internal(
471 world: &mut World,
472 shape_index: i32,
473 body_index: i32,
474 wake_bodies: bool,
475) {
476 let _ = wake_bodies; let (prev_shape_id, next_shape_id, sensor_index) = {
479 let shape = &world.shapes[shape_index as usize];
480 (shape.prev_shape_id, shape.next_shape_id, shape.sensor_index)
481 };
482
483 if prev_shape_id != NULL_INDEX {
485 world.shapes[prev_shape_id as usize].next_shape_id = next_shape_id;
486 }
487 if next_shape_id != NULL_INDEX {
488 world.shapes[next_shape_id as usize].prev_shape_id = prev_shape_id;
489 }
490 if shape_index == world.bodies[body_index as usize].head_shape_id {
491 world.bodies[body_index as usize].head_shape_id = next_shape_id;
492 }
493 world.bodies[body_index as usize].shape_count -= 1;
494
495 {
497 let (shapes, broad_phase) = (&mut world.shapes, &mut world.broad_phase);
498 destroy_shape_proxy(&mut shapes[shape_index as usize], broad_phase);
499 }
500
501 let mut contact_key = world.bodies[body_index as usize].head_contact_key;
503 while contact_key != NULL_INDEX {
504 let contact_id = contact_key >> 1;
505 let edge_index = contact_key & 1;
506 let next_key = world.contacts[contact_id as usize].edges[edge_index as usize].next_key;
507 let shape_id_a = world.contacts[contact_id as usize].shape_id_a;
508 let shape_id_b = world.contacts[contact_id as usize].shape_id_b;
509 contact_key = next_key;
510
511 if shape_id_a == shape_index || shape_id_b == shape_index {
512 crate::contact::destroy_contact(world, contact_id, wake_bodies);
513 }
514 }
515
516 if sensor_index != NULL_INDEX {
517 crate::sensor::destroy_sensor(world, shape_index);
518 }
519
520 destroy_shape_allocations(world, shape_index);
521
522 world.shape_id_pool.free_id(shape_index);
523 world.shapes[shape_index as usize].id = NULL_INDEX;
524
525 world.validate_solver_sets();
526}
527
528pub fn destroy_shape(world: &mut World, shape_id: ShapeId, update_body_mass: bool) {
530 debug_assert!(!world.locked);
531 if world.locked {
532 return;
533 }
534
535 crate::recording::with_recording(world, |rec| {
536 rec.write_destroy_shape(shape_id, update_body_mass);
537 });
538
539 world.locked = true;
540
541 let shape_index = get_shape(world, shape_id);
542 let body_index = world.shapes[shape_index as usize].body_id;
543
544 destroy_shape_internal(world, shape_index, body_index, true);
545
546 if update_body_mass {
547 crate::body::update_body_mass_data(world, body_index);
548 }
549
550 world.locked = false;
551}