1use super::{create_shape_proxy, get_shape_centroid, Shape};
6use crate::collision::ShapeGeometry;
7use crate::core::NULL_INDEX;
8use crate::math_functions::{max_float, min_float, Aabb, WorldTransform};
9
10pub(crate) fn compute_shape_margin(shape: &Shape) -> f32 {
13 use crate::constants::{max_aabb_margin, AABB_MARGIN_FRACTION};
14 use crate::math_functions::distance;
15
16 let margin = match &shape.geometry {
17 ShapeGeometry::Capsule(capsule) => {
18 0.5 * distance(capsule.center2, capsule.center1) + capsule.radius
19 }
20 ShapeGeometry::Circle(circle) => circle.radius,
21 ShapeGeometry::Polygon(poly) => {
22 let mut max_extent_sqr = 0.0f32;
23 for i in 0..poly.count as usize {
24 let distance_sqr =
25 crate::math_functions::distance_squared(poly.vertices[i], poly.centroid);
26 max_extent_sqr = max_float(max_extent_sqr, distance_sqr);
27 }
28 max_extent_sqr.sqrt()
29 }
30 ShapeGeometry::Segment(segment) => 0.5 * distance(segment.point1, segment.point2),
31 ShapeGeometry::ChainSegment(chain_segment) => {
32 0.5 * distance(chain_segment.segment.point1, chain_segment.segment.point2)
33 }
34 };
35
36 min_float(max_aabb_margin(), AABB_MARGIN_FRACTION * margin)
37}
38
39pub(crate) fn create_shape_internal(
43 world: &mut crate::world::World,
44 body_id: i32,
45 transform: WorldTransform,
46 def: &crate::types::ShapeDef,
47 geometry: ShapeGeometry,
48) -> i32 {
49 let shape_id = world.shape_id_pool.alloc_id();
50
51 if shape_id == world.shapes.len() as i32 {
52 world.shapes.push(Shape::default());
53 } else {
54 debug_assert!(world.shapes[shape_id as usize].id == NULL_INDEX);
55 }
56
57 let (body_raw_id, body_set_index, body_type, head_shape_id) = {
58 let body = &world.bodies[body_id as usize];
59 (body.id, body.set_index, body.type_, body.head_shape_id)
60 };
61
62 {
63 let shape = &mut world.shapes[shape_id as usize];
64 shape.geometry = geometry;
65
66 shape.id = shape_id;
67 shape.body_id = body_raw_id;
68 shape.density = def.density;
69 shape.material = def.material;
70 shape.filter = def.filter;
71 shape.user_data = def.user_data;
72 shape.enlarged_aabb = false;
73 shape.enable_sensor_events = def.enable_sensor_events;
74 shape.enable_contact_events = def.enable_contact_events;
75 shape.enable_custom_filtering = def.enable_custom_filtering;
76 shape.enable_hit_events = def.enable_hit_events;
77 shape.enable_pre_solve_events = def.enable_pre_solve_events;
78 shape.proxy_key = NULL_INDEX;
79 shape.local_centroid = get_shape_centroid(shape);
80 shape.aabb_margin = compute_shape_margin(shape);
81 shape.aabb = Aabb::default();
82 shape.fat_aabb = Aabb::default();
83 shape.generation += 1;
84 }
85
86 if body_set_index != crate::solver_set::DISABLED_SET {
87 let (shapes, broad_phase) = (&mut world.shapes, &mut world.broad_phase);
89 create_shape_proxy(
90 &mut shapes[shape_id as usize],
91 broad_phase,
92 body_type,
93 transform,
94 def.invoke_contact_creation || def.is_sensor,
95 );
96 }
97
98 if head_shape_id != NULL_INDEX {
100 world.shapes[head_shape_id as usize].prev_shape_id = shape_id;
101 }
102
103 world.shapes[shape_id as usize].prev_shape_id = NULL_INDEX;
104 world.shapes[shape_id as usize].next_shape_id = head_shape_id;
105 world.bodies[body_id as usize].head_shape_id = shape_id;
106 world.bodies[body_id as usize].shape_count += 1;
107
108 if def.is_sensor {
109 world.shapes[shape_id as usize].sensor_index = world.sensors.len() as i32;
110 world.sensors.push(crate::sensor::Sensor::new(shape_id));
111 } else {
112 world.shapes[shape_id as usize].sensor_index = NULL_INDEX;
113 }
114
115 world.validate_solver_sets();
116
117 shape_id
118}
119
120fn create_shape(
122 world: &mut crate::world::World,
123 body_id: crate::id::BodyId,
124 def: &crate::types::ShapeDef,
125 geometry: ShapeGeometry,
126) -> crate::id::ShapeId {
127 use crate::body::body_flags::DIRTY_MASS;
128 use crate::body::{
129 get_body_full_id, get_body_transform, sync_body_flags, update_body_mass_data,
130 };
131 use crate::math_functions::is_valid_float;
132
133 debug_assert!(def.internal_value == crate::core::SECRET_COOKIE);
134 debug_assert!(is_valid_float(def.density) && def.density >= 0.0);
135 debug_assert!(is_valid_float(def.material.friction) && def.material.friction >= 0.0);
136 debug_assert!(is_valid_float(def.material.restitution) && def.material.restitution >= 0.0);
137 debug_assert!(
138 is_valid_float(def.material.rolling_resistance) && def.material.rolling_resistance >= 0.0
139 );
140 debug_assert!(is_valid_float(def.material.tangent_speed));
141
142 let body_index = get_body_full_id(world, body_id);
143 let transform = get_body_transform(world, body_index);
144
145 let shape_id = create_shape_internal(world, body_index, transform, def, geometry);
146
147 if def.update_body_mass {
148 update_body_mass_data(world, body_index);
149 } else if world.bodies[body_index as usize].flags & DIRTY_MASS == 0 {
150 world.bodies[body_index as usize].flags |= DIRTY_MASS;
151 sync_body_flags(world, body_index);
152 }
153
154 world.validate_solver_sets();
155
156 crate::id::ShapeId {
157 index1: shape_id + 1,
158 world0: body_id.world0,
159 generation: world.shapes[shape_id as usize].generation,
160 }
161}
162
163pub fn create_circle_shape(
165 world: &mut crate::world::World,
166 body_id: crate::id::BodyId,
167 def: &crate::types::ShapeDef,
168 circle: &crate::collision::Circle,
169) -> crate::id::ShapeId {
170 let id = create_shape(world, body_id, def, ShapeGeometry::Circle(*circle));
171 crate::recording::record_op(world, |rec, _| {
172 crate::recording::write_create_shape(
173 rec,
174 crate::recording::OP_CREATE_CIRCLE_SHAPE,
175 body_id,
176 def,
177 |buf| crate::recording::rec_w_circle(buf, *circle),
178 id,
179 )
180 });
181 id
182}
183
184pub fn create_capsule_shape(
186 world: &mut crate::world::World,
187 body_id: crate::id::BodyId,
188 def: &crate::types::ShapeDef,
189 capsule: &crate::collision::Capsule,
190) -> crate::id::ShapeId {
191 use crate::constants::linear_slop;
192 use crate::math_functions::distance_squared;
193
194 let length_sqr = distance_squared(capsule.center1, capsule.center2);
195 if length_sqr <= linear_slop() * linear_slop() {
196 return crate::id::ShapeId::default();
197 }
198
199 let id = create_shape(world, body_id, def, ShapeGeometry::Capsule(*capsule));
200 crate::recording::record_op(world, |rec, _| {
201 crate::recording::write_create_shape(
202 rec,
203 crate::recording::OP_CREATE_CAPSULE_SHAPE,
204 body_id,
205 def,
206 |buf| crate::recording::rec_w_capsule(buf, *capsule),
207 id,
208 )
209 });
210 id
211}
212
213pub fn create_polygon_shape(
215 world: &mut crate::world::World,
216 body_id: crate::id::BodyId,
217 def: &crate::types::ShapeDef,
218 polygon: &crate::collision::Polygon,
219) -> crate::id::ShapeId {
220 debug_assert!(crate::math_functions::is_valid_float(polygon.radius) && polygon.radius >= 0.0);
221
222 let id = create_shape(world, body_id, def, ShapeGeometry::Polygon(*polygon));
223 crate::recording::record_op(world, |rec, _| {
224 crate::recording::write_create_shape(
225 rec,
226 crate::recording::OP_CREATE_POLYGON_SHAPE,
227 body_id,
228 def,
229 |buf| crate::recording::rec_w_polygon(buf, polygon),
230 id,
231 )
232 });
233 id
234}
235
236pub fn create_segment_shape(
238 world: &mut crate::world::World,
239 body_id: crate::id::BodyId,
240 def: &crate::types::ShapeDef,
241 segment: &crate::collision::Segment,
242) -> crate::id::ShapeId {
243 use crate::constants::linear_slop;
244 use crate::math_functions::distance_squared;
245
246 let length_sqr = distance_squared(segment.point1, segment.point2);
247 if length_sqr <= linear_slop() * linear_slop() {
248 debug_assert!(false);
249 return crate::id::ShapeId::default();
250 }
251
252 let id = create_shape(world, body_id, def, ShapeGeometry::Segment(*segment));
253 crate::recording::record_op(world, |rec, _| {
254 crate::recording::write_create_shape(
255 rec,
256 crate::recording::OP_CREATE_SEGMENT_SHAPE,
257 body_id,
258 def,
259 |buf| crate::recording::rec_w_segment(buf, *segment),
260 id,
261 )
262 });
263 id
264}
265
266pub fn create_chain_segment_shape(
268 world: &mut crate::world::World,
269 body_id: crate::id::BodyId,
270 def: &crate::types::ShapeDef,
271 chain_segment: &crate::collision::ChainSegment,
272) -> crate::id::ShapeId {
273 use crate::constants::linear_slop;
274 use crate::math_functions::distance_squared;
275
276 let length_sqr = distance_squared(chain_segment.segment.point1, chain_segment.segment.point2);
277 if length_sqr <= linear_slop() * linear_slop() {
278 debug_assert!(false);
279 return crate::id::ShapeId::default();
280 }
281
282 let mut local = *chain_segment;
284 local.chain_id = NULL_INDEX;
285
286 let id = create_shape(world, body_id, def, ShapeGeometry::ChainSegment(local));
287 crate::recording::record_op(world, |rec, _| {
289 crate::recording::write_create_shape(
290 rec,
291 crate::recording::OP_CREATE_CHAIN_SEGMENT_SHAPE,
292 body_id,
293 def,
294 |buf| crate::recording::rec_w_chainseg(buf, local),
295 id,
296 )
297 });
298 id
299}
300
301pub fn get_shape_index(world: &crate::world::World, shape_id: crate::id::ShapeId) -> i32 {
304 let index = shape_id.index1 - 1;
305 debug_assert!((index as usize) < world.shapes.len());
306 let shape = &world.shapes[index as usize];
307 debug_assert!(shape.id == index && shape.generation == shape_id.generation);
308 index
309}
310
311pub(crate) fn destroy_shape_internal(
314 world: &mut crate::world::World,
315 shape_id: i32,
316 body_id: i32,
317 wake_bodies: bool,
318) {
319 let (prev_shape_id, next_shape_id) = {
321 let shape = &world.shapes[shape_id as usize];
322 (shape.prev_shape_id, shape.next_shape_id)
323 };
324
325 if prev_shape_id != NULL_INDEX {
326 world.shapes[prev_shape_id as usize].next_shape_id = next_shape_id;
327 }
328
329 if next_shape_id != NULL_INDEX {
330 world.shapes[next_shape_id as usize].prev_shape_id = prev_shape_id;
331 }
332
333 {
334 let body = &mut world.bodies[body_id as usize];
335 if shape_id == body.head_shape_id {
336 body.head_shape_id = next_shape_id;
337 }
338 body.shape_count -= 1;
339 }
340
341 {
343 let (shapes, broad_phase) = (&mut world.shapes, &mut world.broad_phase);
344 super::destroy_shape_proxy(&mut shapes[shape_id as usize], broad_phase);
345 }
346
347 let mut contact_key = world.bodies[body_id as usize].head_contact_key;
349 while contact_key != NULL_INDEX {
350 let contact_id = contact_key >> 1;
351 let edge_index = contact_key & 1;
352
353 contact_key = world.contacts[contact_id as usize].edges[edge_index as usize].next_key;
354
355 let (contact_shape_a, contact_shape_b) = {
356 let contact = &world.contacts[contact_id as usize];
357 (contact.shape_id_a, contact.shape_id_b)
358 };
359 if contact_shape_a == shape_id || contact_shape_b == shape_id {
360 crate::contact::destroy_contact(world, contact_id, wake_bodies);
361 }
362 }
363
364 if world.shapes[shape_id as usize].sensor_index != NULL_INDEX {
365 crate::sensor::destroy_sensor(world, shape_id);
368 }
369
370 world.shape_id_pool.free_id(shape_id);
372 world.shapes[shape_id as usize].id = NULL_INDEX;
373
374 world.validate_solver_sets();
375}
376
377pub fn destroy_shape(
380 world: &mut crate::world::World,
381 shape_id: crate::id::ShapeId,
382 update_body_mass: bool,
383) {
384 crate::recording::record_op(world, |rec, _| {
385 crate::recording::write_destroy_shape(rec, shape_id, update_body_mass)
386 });
387 let shape_index = get_shape_index(world, shape_id);
388
389 if let ShapeGeometry::ChainSegment(chain_segment) = &world.shapes[shape_index as usize].geometry
391 {
392 if chain_segment.chain_id != NULL_INDEX {
393 debug_assert!(false, "cannot destroy a chain segment owned by a chain");
394 return;
395 }
396 }
397
398 let wake_bodies = true;
400 let body_id = world.shapes[shape_index as usize].body_id;
401 destroy_shape_internal(world, shape_index, body_id, wake_bodies);
402
403 if update_body_mass {
404 crate::body::update_body_mass_data(world, body_id);
405 }
406}