1use super::api::reset_proxy;
9use super::{compute_shape_margin, get_shape_index};
10use crate::body::wake_body;
11use crate::collision::{Capsule, ChainSegment, Circle, Polygon, Segment, ShapeGeometry, ShapeType};
12use crate::core::NULL_INDEX;
13use crate::id::{ChainId, ShapeId};
14use crate::math_functions::{
15 abs_float, add, cross, cross_sv, distance_squared, dot, get_length_and_normalize, left_perp,
16 lerp, mul_add, mul_sub, mul_sv, neg, normalize, right_perp, rotate_vector, sub, Vec2,
17 VEC2_ZERO,
18};
19use crate::solver_set::{AWAKE_SET, FIRST_SLEEPING_SET};
20use crate::types::BodyType;
21use crate::world::World;
22
23pub fn shape_get_circle(world: &World, shape_id: ShapeId) -> Circle {
26 let shape_index = get_shape_index(world, shape_id);
27 match &world.shapes[shape_index as usize].geometry {
28 ShapeGeometry::Circle(circle) => *circle,
29 _ => unreachable!("shape is not a circle"),
30 }
31}
32
33pub fn shape_get_segment(world: &World, shape_id: ShapeId) -> Segment {
36 let shape_index = get_shape_index(world, shape_id);
37 match &world.shapes[shape_index as usize].geometry {
38 ShapeGeometry::Segment(segment) => *segment,
39 _ => unreachable!("shape is not a segment"),
40 }
41}
42
43pub fn shape_get_chain_segment(world: &World, shape_id: ShapeId) -> ChainSegment {
46 let shape_index = get_shape_index(world, shape_id);
47 match &world.shapes[shape_index as usize].geometry {
48 ShapeGeometry::ChainSegment(chain_segment) => *chain_segment,
49 _ => unreachable!("shape is not a chain segment"),
50 }
51}
52
53pub fn shape_get_capsule(world: &World, shape_id: ShapeId) -> Capsule {
56 let shape_index = get_shape_index(world, shape_id);
57 match &world.shapes[shape_index as usize].geometry {
58 ShapeGeometry::Capsule(capsule) => *capsule,
59 _ => unreachable!("shape is not a capsule"),
60 }
61}
62
63pub fn shape_get_polygon(world: &World, shape_id: ShapeId) -> Polygon {
66 let shape_index = get_shape_index(world, shape_id);
67 match &world.shapes[shape_index as usize].geometry {
68 ShapeGeometry::Polygon(polygon) => *polygon,
69 _ => unreachable!("shape is not a polygon"),
70 }
71}
72
73fn set_geometry(world: &mut World, shape_index: i32, geometry: ShapeGeometry) {
77 {
78 let shape = &mut world.shapes[shape_index as usize];
79 shape.geometry = geometry;
80 shape.aabb_margin = compute_shape_margin(shape);
81 }
82
83 let wake_bodies = true;
85 let destroy_proxy = true;
86 reset_proxy(world, shape_index, wake_bodies, destroy_proxy);
87}
88
89pub fn shape_set_circle(world: &mut World, shape_id: ShapeId, circle: &Circle) {
92 crate::recording::record_op(world, |rec, _| {
93 crate::recording::write_shape_geometry(
94 rec,
95 crate::recording::OP_SHAPE_SET_CIRCLE,
96 shape_id,
97 |buf| crate::recording::rec_w_circle(buf, *circle),
98 )
99 });
100 debug_assert!(!world.locked);
101 if world.locked {
102 return;
103 }
104
105 let shape_index = get_shape_index(world, shape_id);
106 set_geometry(world, shape_index, ShapeGeometry::Circle(*circle));
107}
108
109pub fn shape_set_capsule(world: &mut World, shape_id: ShapeId, capsule: &Capsule) {
112 crate::recording::record_op(world, |rec, _| {
113 crate::recording::write_shape_geometry(
114 rec,
115 crate::recording::OP_SHAPE_SET_CAPSULE,
116 shape_id,
117 |buf| crate::recording::rec_w_capsule(buf, *capsule),
118 )
119 });
120 debug_assert!(!world.locked);
121 if world.locked {
122 return;
123 }
124
125 let length_sqr = distance_squared(capsule.center1, capsule.center2);
126 if length_sqr <= crate::constants::linear_slop() * crate::constants::linear_slop() {
127 return;
128 }
129
130 let shape_index = get_shape_index(world, shape_id);
131 set_geometry(world, shape_index, ShapeGeometry::Capsule(*capsule));
132}
133
134pub fn shape_set_segment(world: &mut World, shape_id: ShapeId, segment: &Segment) {
137 crate::recording::record_op(world, |rec, _| {
138 crate::recording::write_shape_geometry(
139 rec,
140 crate::recording::OP_SHAPE_SET_SEGMENT,
141 shape_id,
142 |buf| crate::recording::rec_w_segment(buf, *segment),
143 )
144 });
145 debug_assert!(!world.locked);
146 if world.locked {
147 return;
148 }
149
150 let shape_index = get_shape_index(world, shape_id);
151 set_geometry(world, shape_index, ShapeGeometry::Segment(*segment));
152}
153
154pub fn shape_set_polygon(world: &mut World, shape_id: ShapeId, polygon: &Polygon) {
157 crate::recording::record_op(world, |rec, _| {
158 crate::recording::write_shape_geometry(
159 rec,
160 crate::recording::OP_SHAPE_SET_POLYGON,
161 shape_id,
162 |buf| crate::recording::rec_w_polygon(buf, polygon),
163 )
164 });
165 debug_assert!(!world.locked);
166 if world.locked {
167 return;
168 }
169
170 let shape_index = get_shape_index(world, shape_id);
171 set_geometry(world, shape_index, ShapeGeometry::Polygon(*polygon));
172}
173
174pub fn shape_set_chain_segment(world: &mut World, shape_id: ShapeId, chain_segment: &ChainSegment) {
178 crate::recording::record_op(world, |rec, _| {
179 crate::recording::write_shape_geometry(
180 rec,
181 crate::recording::OP_SHAPE_SET_CHAIN_SEGMENT,
182 shape_id,
183 |buf| crate::recording::rec_w_chainseg(buf, *chain_segment),
184 )
185 });
186 debug_assert!(!world.locked);
187 if world.locked {
188 return;
189 }
190
191 let shape_index = get_shape_index(world, shape_id);
192
193 if let ShapeGeometry::ChainSegment(existing) = &world.shapes[shape_index as usize].geometry {
195 if existing.chain_id != NULL_INDEX {
196 debug_assert!(false, "cannot modify a chain segment owned by a chain");
197 return;
198 }
199 }
200
201 let length_sqr = distance_squared(chain_segment.segment.point1, chain_segment.segment.point2);
202 if length_sqr <= crate::constants::linear_slop() * crate::constants::linear_slop() {
203 return;
204 }
205
206 let mut local = *chain_segment;
207 local.chain_id = NULL_INDEX;
208 set_geometry(world, shape_index, ShapeGeometry::ChainSegment(local));
209}
210
211pub fn shape_get_parent_chain(world: &World, shape_id: ShapeId) -> ChainId {
214 let shape_index = get_shape_index(world, shape_id);
215 if let ShapeGeometry::ChainSegment(chain_segment) = &world.shapes[shape_index as usize].geometry
216 {
217 let chain_id = chain_segment.chain_id;
218 if chain_id != NULL_INDEX {
219 let chain = &world.chain_shapes[chain_id as usize];
220 return ChainId {
221 index1: chain_id + 1,
222 world0: world.world_id,
223 generation: chain.generation,
224 };
225 }
226 }
227
228 ChainId::default()
229}
230
231pub fn shape_apply_wind(
238 world: &mut World,
239 shape_id: ShapeId,
240 wind: Vec2,
241 drag: f32,
242 lift: f32,
243 wake: bool,
244) {
245 crate::recording::record_op(world, |rec, _| {
246 crate::recording::write_shape_apply_wind(rec, shape_id, wind, drag, lift, wake)
247 });
248 let shape_index = get_shape_index(world, shape_id);
249
250 let shape_type = world.shapes[shape_index as usize].shape_type();
251 if shape_type != ShapeType::Circle
252 && shape_type != ShapeType::Capsule
253 && shape_type != ShapeType::Polygon
254 {
255 return;
256 }
257
258 let body_id = world.shapes[shape_index as usize].body_id;
259
260 if world.bodies[body_id as usize].type_ != BodyType::Dynamic {
261 return;
262 }
263
264 if world.bodies[body_id as usize].set_index >= FIRST_SLEEPING_SET && !wake {
265 return;
266 }
267
268 if world.bodies[body_id as usize].set_index != AWAKE_SET {
269 wake_body(world, body_id);
271 }
272
273 debug_assert!(world.bodies[body_id as usize].set_index == AWAKE_SET);
274
275 let local_index = world.bodies[body_id as usize].local_index;
276 let (transform, local_center) = {
277 let sim = &world.solver_sets[AWAKE_SET as usize].body_sims[local_index as usize];
278 (sim.transform, sim.local_center)
279 };
280 let (linear_velocity, angular_velocity) = {
281 let state = &world.solver_sets[AWAKE_SET as usize].body_states[local_index as usize];
282 (state.linear_velocity, state.angular_velocity)
283 };
284 let shape = &world.shapes[shape_index as usize];
285
286 let length_units = crate::core::get_length_units_per_meter();
287 let volume_units = length_units * length_units * length_units;
288
289 let air_density = 1.2250 / volume_units;
291
292 let mut force = VEC2_ZERO;
293 let mut torque = 0.0f32;
294
295 match &shape.geometry {
296 ShapeGeometry::Circle(circle) => {
297 let radius = circle.radius;
298 let centroid = shape.local_centroid;
299 let lever = rotate_vector(transform.q, sub(centroid, local_center));
300 let shape_velocity = add(linear_velocity, cross_sv(angular_velocity, lever));
301 let relative_velocity = mul_sub(wind, drag, shape_velocity);
302 let mut speed = 0.0;
303 let direction = get_length_and_normalize(&mut speed, relative_velocity);
304 let projected_area = 2.0 * radius;
305 force = mul_sv(
306 0.5 * air_density * projected_area * speed * speed,
307 direction,
308 );
309 torque = cross(lever, force);
310 }
311
312 ShapeGeometry::Capsule(capsule) => {
313 let centroid = shape.local_centroid;
314 let lever = rotate_vector(transform.q, sub(centroid, local_center));
315 let shape_velocity = add(linear_velocity, cross_sv(angular_velocity, lever));
316 let relative_velocity = mul_sub(wind, drag, shape_velocity);
317 let mut speed = 0.0;
318 let direction = get_length_and_normalize(&mut speed, relative_velocity);
319
320 let mut d = sub(capsule.center2, capsule.center1);
321 d = rotate_vector(transform.q, d);
322
323 let radius = capsule.radius;
324 let projected_area = 2.0 * radius + abs_float(cross(d, direction));
325
326 let mut normal = left_perp(normalize(d));
328 if dot(normal, direction) > 0.0 {
329 normal = neg(normal);
330 }
331
332 let lift_direction = cross_sv(cross(normal, direction), direction);
334
335 let force_magnitude = 0.5 * air_density * projected_area * speed * speed;
336 force = mul_sv(force_magnitude, mul_add(direction, lift, lift_direction));
337
338 let edge_lever = mul_add(lever, radius, normal);
339 torque = cross(edge_lever, force);
340 }
341
342 ShapeGeometry::Polygon(polygon) => {
343 let centroid = shape.local_centroid;
344 let lever = rotate_vector(transform.q, sub(centroid, local_center));
345 let shape_velocity = add(linear_velocity, cross_sv(angular_velocity, lever));
346 let relative_velocity = mul_sub(wind, drag, shape_velocity);
347 let mut speed = 0.0;
348 let direction = get_length_and_normalize(&mut speed, relative_velocity);
349
350 let count = polygon.count as usize;
352 let vertices = &polygon.vertices;
353
354 let mut v1 = vertices[count - 1];
355 for vertex in vertices[..count].iter() {
356 let v2 = *vertex;
357 let mut d = sub(v2, v1);
358 let edge_center = lerp(v1, v2, 0.5);
359 v1 = v2;
360
361 d = rotate_vector(transform.q, d);
362
363 let projected_area = cross(d, direction);
364 if projected_area <= 0.0 {
365 continue;
367 }
368
369 let normal = right_perp(normalize(d));
370
371 let lift_direction = cross_sv(cross(normal, direction), direction);
373
374 let force_magnitude = 0.5 * air_density * projected_area * speed * speed;
375 let f = mul_sv(force_magnitude, mul_add(direction, lift, lift_direction));
376
377 let edge_lever = rotate_vector(transform.q, sub(edge_center, local_center));
378
379 force = add(force, f);
380 torque += cross(edge_lever, f);
381 }
382 }
383
384 _ => {}
385 }
386
387 let sim = &mut world.solver_sets[AWAKE_SET as usize].body_sims[local_index as usize];
388 sim.force = add(sim.force, force);
389 sim.torque += torque;
390}