1use super::dispatch::{compute_shape_mass, make_shape_proxy};
8use super::lifecycle::get_shape;
9use super::query::ray_cast_shape;
10use super::shape_flags;
11use crate::body::{get_body_transform, get_body_transform_quick, make_body_id};
12use crate::constants::{NULL_NAME, SHAPE_NAME_LENGTH};
13use crate::contact::contact_flags;
14use crate::core::NULL_INDEX;
15use crate::distance::{shape_distance, DistanceInput, ShapeProxy, SimplexCache};
16use crate::events::ContactData;
17use crate::geometry::{MassData, RayCastInput, ShapeType};
18use crate::id::{BodyId, ContactId, ShapeId, WorldId};
19use crate::math_functions::{
20 inv_mul_transforms, is_valid_float, is_valid_position, is_valid_vec3, offset_pos,
21 to_relative_transform, transform_point, Aabb, Pos, Vec3, POS_ZERO, TRANSFORM_IDENTITY,
22 VEC3_ZERO,
23};
24use crate::types::WorldCastOutput;
25use crate::world::World;
26
27fn truncate_shape_name(name: &str) -> &str {
28 if name.len() > SHAPE_NAME_LENGTH {
29 let mut end = SHAPE_NAME_LENGTH;
30 while end > 0 && !name.is_char_boundary(end) {
31 end -= 1;
32 }
33 &name[..end]
34 } else {
35 name
36 }
37}
38
39fn set_shape_flag(world: &mut World, shape_id: ShapeId, bit: u8, flag: bool) {
40 debug_assert!(!world.locked);
41 if world.locked {
42 return;
43 }
44 let index = get_shape(world, shape_id);
45 let shape = &mut world.shapes[index as usize];
46 if flag {
47 shape.flags |= bit;
48 } else {
49 shape.flags &= !bit;
50 }
51}
52
53fn shape_has_flag(world: &World, shape_id: ShapeId, bit: u8) -> bool {
54 let index = get_shape(world, shape_id);
55 (world.shapes[index as usize].flags & bit) != 0
56}
57
58pub fn shape_enable_sensor_events(world: &mut World, shape_id: ShapeId, flag: bool) {
60 crate::recording::with_recording(world, |rec| {
61 rec.write_shape_enable_sensor_events(shape_id, flag);
62 });
63 set_shape_flag(world, shape_id, shape_flags::ENABLE_SENSOR_EVENTS, flag);
64}
65
66pub fn shape_are_sensor_events_enabled(world: &World, shape_id: ShapeId) -> bool {
68 shape_has_flag(world, shape_id, shape_flags::ENABLE_SENSOR_EVENTS)
69}
70
71pub fn shape_enable_contact_events(world: &mut World, shape_id: ShapeId, flag: bool) {
73 crate::recording::with_recording(world, |rec| {
74 rec.write_shape_enable_contact_events(shape_id, flag);
75 });
76 set_shape_flag(world, shape_id, shape_flags::ENABLE_CONTACT_EVENTS, flag);
77}
78
79pub fn shape_are_contact_events_enabled(world: &World, shape_id: ShapeId) -> bool {
81 shape_has_flag(world, shape_id, shape_flags::ENABLE_CONTACT_EVENTS)
82}
83
84pub fn shape_enable_pre_solve_events(world: &mut World, shape_id: ShapeId, flag: bool) {
86 crate::recording::with_recording(world, |rec| {
87 rec.write_shape_enable_pre_solve_events(shape_id, flag);
88 });
89 set_shape_flag(world, shape_id, shape_flags::ENABLE_PRE_SOLVE_EVENTS, flag);
90}
91
92pub fn shape_are_pre_solve_events_enabled(world: &World, shape_id: ShapeId) -> bool {
94 shape_has_flag(world, shape_id, shape_flags::ENABLE_PRE_SOLVE_EVENTS)
95}
96
97pub fn shape_enable_hit_events(world: &mut World, shape_id: ShapeId, flag: bool) {
99 crate::recording::with_recording(world, |rec| {
100 rec.write_shape_enable_hit_events(shape_id, flag);
101 });
102 set_shape_flag(world, shape_id, shape_flags::ENABLE_HIT_EVENTS, flag);
103}
104
105pub fn shape_are_hit_events_enabled(world: &World, shape_id: ShapeId) -> bool {
107 shape_has_flag(world, shape_id, shape_flags::ENABLE_HIT_EVENTS)
108}
109
110pub fn shape_set_user_data(world: &mut World, shape_id: ShapeId, user_data: u64) {
112 let index = get_shape(world, shape_id);
113 world.shapes[index as usize].user_data = user_data;
114}
115
116pub fn shape_get_user_data(world: &World, shape_id: ShapeId) -> u64 {
118 let index = get_shape(world, shape_id);
119 world.shapes[index as usize].user_data
120}
121
122pub fn shape_set_name(world: &mut World, shape_id: ShapeId, name: &str) {
125 crate::recording::with_recording(world, |rec| {
126 rec.write_shape_set_name(shape_id, name);
127 });
128 let truncated = truncate_shape_name(name);
129 let name_id = world.names.add_name(truncated);
130 let index = get_shape(world, shape_id);
131 world.shapes[index as usize].name_id = name_id;
132}
133
134pub fn shape_get_name(world: &World, shape_id: ShapeId) -> &str {
136 let index = get_shape(world, shape_id);
137 let name_id = world.shapes[index as usize].name_id;
138 if name_id == NULL_NAME {
139 return "";
140 }
141 world.names.find_name(name_id).unwrap_or("")
142}
143
144pub fn shape_is_sensor(world: &World, shape_id: ShapeId) -> bool {
146 let index = get_shape(world, shape_id);
147 world.shapes[index as usize].sensor_index != NULL_INDEX
148}
149
150pub fn shape_get_type(world: &World, shape_id: ShapeId) -> ShapeType {
152 let index = get_shape(world, shape_id);
153 world.shapes[index as usize].shape_type()
154}
155
156pub fn shape_get_body(world: &World, shape_id: ShapeId) -> BodyId {
158 let index = get_shape(world, shape_id);
159 let body_index = world.shapes[index as usize].body_id;
160 make_body_id(world, body_index)
161}
162
163pub fn shape_get_world(world: &World, shape_id: ShapeId) -> WorldId {
165 WorldId {
166 index1: shape_id.world0.wrapping_add(1),
167 generation: world.generation,
168 }
169}
170
171pub fn shape_set_density(
173 world: &mut World,
174 shape_id: ShapeId,
175 density: f32,
176 update_body_mass: bool,
177) {
178 crate::recording::with_recording(world, |rec| {
179 rec.write_shape_set_density(shape_id, density, update_body_mass);
180 });
181 debug_assert!(is_valid_float(density) && density >= 0.0);
182 debug_assert!(!world.locked);
183 if world.locked {
184 return;
185 }
186
187 let index = get_shape(world, shape_id);
188 if density == world.shapes[index as usize].density {
189 return;
190 }
191
192 world.shapes[index as usize].density = density;
193
194 if update_body_mass {
195 let body_index = world.shapes[index as usize].body_id;
196 crate::body::update_body_mass_data(world, body_index);
197 }
198}
199
200pub fn shape_get_density(world: &World, shape_id: ShapeId) -> f32 {
202 let index = get_shape(world, shape_id);
203 world.shapes[index as usize].density
204}
205
206pub fn shape_get_aabb(world: &World, shape_id: ShapeId) -> Aabb {
208 let index = get_shape(world, shape_id);
209 world.shapes[index as usize].aabb
210}
211
212pub fn shape_ray_cast(
217 world: &World,
218 shape_id: ShapeId,
219 origin: Pos,
220 translation: Vec3,
221) -> WorldCastOutput {
222 debug_assert!(is_valid_position(origin));
223 debug_assert!(is_valid_vec3(translation));
224
225 let index = get_shape(world, shape_id);
226 let body_id = world.shapes[index as usize].body_id;
227 let transform = to_relative_transform(get_body_transform(world, body_id), origin);
228
229 let input = RayCastInput {
230 origin: VEC3_ZERO,
231 translation,
232 max_fraction: 1.0,
233 };
234
235 let local = ray_cast_shape(&world.shapes[index as usize], transform, &input);
236 WorldCastOutput {
237 normal: local.normal,
238 point: offset_pos(origin, local.point),
239 fraction: local.fraction,
240 iterations: local.iterations,
241 triangle_index: local.triangle_index,
242 child_index: local.child_index,
243 material_index: local.material_index,
244 hit: local.hit,
245 }
246}
247
248pub fn shape_get_closest_point(world: &World, shape_id: ShapeId, target: Vec3) -> Vec3 {
252 let index = get_shape(world, shape_id);
253 let body_id = world.shapes[index as usize].body_id;
254 let body = &world.bodies[body_id as usize];
255 let transform = to_relative_transform(get_body_transform_quick(world, body), POS_ZERO);
256
257 let mut proxy_b = ShapeProxy::default();
258 proxy_b.points[0] = target;
259 proxy_b.count = 1;
260 proxy_b.radius = 0.0;
261
262 let input = DistanceInput {
263 proxy_a: make_shape_proxy(&world.shapes[index as usize]),
264 proxy_b,
265 transform: inv_mul_transforms(transform, TRANSFORM_IDENTITY),
266 use_radii: true,
267 };
268
269 let mut cache = SimplexCache::default();
270 let output = shape_distance(&input, &mut cache, None);
271
272 transform_point(transform, output.point_a)
274}
275
276pub fn shape_get_contact_capacity(world: &World, shape_id: ShapeId) -> i32 {
278 debug_assert!(!world.locked);
279 if world.locked {
280 return 0;
281 }
282
283 let shape_index = get_shape(world, shape_id);
284 let shape = &world.shapes[shape_index as usize];
285 if shape.sensor_index != NULL_INDEX {
286 return 0;
287 }
288
289 world.bodies[shape.body_id as usize].contact_count
291}
292
293pub fn shape_get_contact_data(
295 world: &World,
296 shape_id: ShapeId,
297 capacity: usize,
298) -> Vec<ContactData> {
299 debug_assert!(!world.locked);
300 if world.locked {
301 return Vec::new();
302 }
303
304 let shape_index = get_shape(world, shape_id);
305 let shape = &world.shapes[shape_index as usize];
306 if shape.sensor_index != NULL_INDEX {
307 return Vec::new();
308 }
309
310 let mut out = Vec::new();
311 let mut contact_key = world.bodies[shape.body_id as usize].head_contact_key;
312 while contact_key != NULL_INDEX && out.len() < capacity {
313 let contact_id = contact_key >> 1;
314 let edge_index = contact_key & 1;
315
316 let contact = &world.contacts[contact_id as usize];
317 contact_key = contact.edges[edge_index as usize].next_key;
318
319 if (contact.shape_id_a == shape_index || contact.shape_id_b == shape_index)
321 && (contact.flags & contact_flags::TOUCHING) != 0
322 {
323 let shape_a = &world.shapes[contact.shape_id_a as usize];
324 let shape_b = &world.shapes[contact.shape_id_b as usize];
325
326 out.push(ContactData {
327 contact_id: ContactId {
328 index1: contact.contact_id + 1,
329 world0: shape_id.world0,
330 padding: 0,
331 generation: contact.generation,
332 },
333 shape_id_a: ShapeId {
334 index1: shape_a.id + 1,
335 world0: shape_id.world0,
336 generation: shape_a.generation,
337 },
338 shape_id_b: ShapeId {
339 index1: shape_b.id + 1,
340 world0: shape_id.world0,
341 generation: shape_b.generation,
342 },
343 manifolds: contact.manifolds.clone(),
344 });
345 }
346 }
347
348 debug_assert!(out.len() <= capacity);
349 out
350}
351
352pub fn shape_get_sensor_capacity(world: &World, shape_id: ShapeId) -> i32 {
355 debug_assert!(!world.locked);
356 if world.locked {
357 return 0;
358 }
359
360 let shape_index = get_shape(world, shape_id);
361 let shape = &world.shapes[shape_index as usize];
362 if shape.sensor_index == NULL_INDEX {
363 return 0;
364 }
365
366 world.sensors[shape.sensor_index as usize].overlaps2.len() as i32
367}
368
369pub fn shape_get_sensor_data(world: &World, shape_id: ShapeId, capacity: usize) -> Vec<ShapeId> {
373 debug_assert!(!world.locked);
374 if world.locked {
375 return Vec::new();
376 }
377
378 let shape_index = get_shape(world, shape_id);
379 let shape = &world.shapes[shape_index as usize];
380 if shape.sensor_index == NULL_INDEX {
381 return Vec::new();
382 }
383
384 let sensor = &world.sensors[shape.sensor_index as usize];
385 let count = sensor.overlaps2.len().min(capacity);
386 sensor.overlaps2[..count]
387 .iter()
388 .map(|visitor| ShapeId {
389 index1: visitor.shape_id + 1,
390 world0: shape_id.world0,
391 generation: visitor.generation,
392 })
393 .collect()
394}
395
396pub fn shape_get_sensor_overlaps(
399 world: &World,
400 shape_id: ShapeId,
401 capacity: usize,
402) -> Vec<ShapeId> {
403 shape_get_sensor_data(world, shape_id, capacity)
404}
405
406pub fn shape_compute_mass_data(world: &World, shape_id: ShapeId) -> MassData {
408 let shape_index = get_shape(world, shape_id);
409 compute_shape_mass(&world.shapes[shape_index as usize])
410}