1use super::lifecycle::get_shape;
7use super::ShapeGeometry;
8use crate::body::wake_body_with_lock;
9use crate::core::get_length_units_per_meter;
10use crate::geometry::ShapeType;
11use crate::hull::{get_hull_edges, get_hull_faces, get_hull_planes, get_hull_points};
12use crate::id::ShapeId;
13use crate::math_functions::{
14 add, cross, dot, get_length_and_normalize, length, make_matrix_from_quat, min_float, mul_add,
15 mul_mv, mul_sub, mul_sv, normalize, rotate_vector, sub, to_relative_transform, Vec3, PI,
16 POS_ZERO, VEC3_ZERO,
17};
18use crate::solver_set::{AWAKE_SET, DISABLED_SET, FIRST_SLEEPING_SET};
19use crate::types::BodyType;
20use crate::world::World;
21
22pub fn shape_apply_wind(
29 world: &mut World,
30 shape_id: ShapeId,
31 wind: Vec3,
32 drag: f32,
33 lift: f32,
34 max_speed: f32,
35 wake: bool,
36) {
37 crate::recording::with_recording(world, |rec| {
38 rec.write_shape_apply_wind(shape_id, wind, drag, lift, max_speed, wake);
39 });
40 let shape_index = get_shape(world, shape_id);
41
42 let shape_type = world.shapes[shape_index as usize].shape_type();
43 if shape_type != ShapeType::Sphere
44 && shape_type != ShapeType::Capsule
45 && shape_type != ShapeType::Hull
46 {
47 return;
48 }
49
50 let body_id = world.shapes[shape_index as usize].body_id;
51
52 if world.bodies[body_id as usize].type_ != BodyType::Dynamic {
53 return;
54 }
55
56 if world.bodies[body_id as usize].set_index == DISABLED_SET {
57 return;
58 }
59
60 if world.bodies[body_id as usize].set_index >= FIRST_SLEEPING_SET && !wake {
61 return;
62 }
63
64 if world.bodies[body_id as usize].set_index != AWAKE_SET {
65 wake_body_with_lock(world, body_id);
67 }
68
69 debug_assert!(world.bodies[body_id as usize].set_index == AWAKE_SET);
70
71 let local_index = world.bodies[body_id as usize].local_index;
72 let (transform, local_center_of_mass) = {
73 let sim = &world.solver_sets[AWAKE_SET as usize].body_sims[local_index as usize];
74 (
76 to_relative_transform(sim.transform, POS_ZERO),
77 sim.local_center,
78 )
79 };
80 let (linear_velocity, angular_velocity) = {
81 let state = &world.solver_sets[AWAKE_SET as usize].body_states[local_index as usize];
82 (state.linear_velocity, state.angular_velocity)
83 };
84
85 let length_units = get_length_units_per_meter();
86 let volume_units = length_units * length_units * length_units;
87 let air_density = 1.2250 / volume_units;
88
89 let mut force = VEC3_ZERO;
90 let mut torque = VEC3_ZERO;
91
92 let shape = &world.shapes[shape_index as usize];
93 match &shape.geometry {
94 ShapeGeometry::Sphere(sphere) => {
95 let radius = sphere.radius;
96 let centroid = shape.local_centroid;
97 let lever = rotate_vector(transform.q, sub(centroid, local_center_of_mass));
98 let shape_velocity = add(linear_velocity, cross(angular_velocity, lever));
99 let relative_velocity = mul_sub(wind, drag, shape_velocity);
100 let mut speed = 0.0;
101 let direction = get_length_and_normalize(&mut speed, relative_velocity);
102 let speed = min_float(speed, max_speed);
103 let projected_area = PI * radius * radius;
104 force = mul_sv(
105 0.5 * air_density * projected_area * speed * speed,
106 direction,
107 );
108 torque = cross(lever, force);
109 }
110
111 ShapeGeometry::Capsule(capsule) => {
112 let centroid = shape.local_centroid;
113 let lever = rotate_vector(transform.q, sub(centroid, local_center_of_mass));
114 let shape_velocity = add(linear_velocity, cross(angular_velocity, lever));
115 let relative_velocity = mul_sub(wind, drag, shape_velocity);
116 let mut speed = 0.0;
117 let direction = get_length_and_normalize(&mut speed, relative_velocity);
118 let speed = min_float(speed, max_speed);
119
120 let mut d = sub(capsule.center2, capsule.center1);
121 d = rotate_vector(transform.q, d);
122
123 let radius = capsule.radius;
124 let projected_area = PI * radius * radius + 2.0 * radius * length(cross(d, direction));
125
126 let e = normalize(d);
128 let normal = sub(mul_sv(dot(direction, e), e), direction);
129
130 let lift_direction = cross(cross(normal, direction), direction);
132
133 let force_magnitude = 0.5 * air_density * projected_area * speed * speed;
134 force = mul_sv(force_magnitude, mul_add(direction, lift, lift_direction));
135
136 let edge_lever = mul_add(lever, radius, normal);
137 torque = cross(edge_lever, force);
138 }
139
140 ShapeGeometry::Hull(hull) => {
141 let matrix = make_matrix_from_quat(transform.q);
142
143 let face_count = hull.face_count;
144 let points = get_hull_points(hull);
145 let faces = get_hull_faces(hull);
146 let edges = get_hull_edges(hull);
147 let planes = get_hull_planes(hull);
148
149 for i in 0..face_count {
150 let face = &faces[i as usize];
151 let edge1_index = face.edge;
152 let edge2_index = edges[edge1_index as usize].next;
153 let mut edge3_index = edges[edge2_index as usize].next;
154
155 debug_assert!(edge1_index != edge3_index);
156 debug_assert!((edges[edge1_index as usize].origin as i32) < hull.vertex_count);
157 debug_assert!((edges[edge2_index as usize].origin as i32) < hull.vertex_count);
158
159 let local_point1 = points[edges[edge1_index as usize].origin as usize];
160 let mut local_point2 = points[edges[edge2_index as usize].origin as usize];
161 let v1 = mul_mv(matrix, local_point1);
162 let mut v2 = mul_mv(matrix, local_point2);
163 let normal = mul_mv(matrix, planes[i as usize].normal);
164
165 loop {
166 debug_assert!((edges[edge3_index as usize].origin as i32) < hull.vertex_count);
167 let local_point3 = points[edges[edge3_index as usize].origin as usize];
168 let v3 = mul_mv(matrix, local_point3);
169
170 let triangle_local_center =
172 mul_sv(0.333333, add(local_point1, add(local_point2, local_point3)));
173
174 let lever = mul_mv(matrix, sub(triangle_local_center, local_center_of_mass));
176
177 let center_velocity = add(linear_velocity, cross(angular_velocity, lever));
179
180 let relative_velocity = mul_sub(wind, drag, center_velocity);
181 let mut speed = 0.0;
182 let direction = get_length_and_normalize(&mut speed, relative_velocity);
183
184 if dot(normal, direction) < -f32::EPSILON {
186 let projected_area = -0.5 * dot(cross(sub(v2, v1), sub(v3, v1)), direction);
187 debug_assert!(projected_area >= -f32::EPSILON);
188
189 let lift_direction = cross(cross(normal, direction), direction);
190
191 let speed = min_float(speed, max_speed);
192
193 let force_magnitude = 0.5 * air_density * projected_area * speed * speed;
194 let delta_force =
195 mul_sv(force_magnitude, mul_add(direction, lift, lift_direction));
196 let delta_torque = cross(lever, delta_force);
197
198 force = add(force, delta_force);
199 torque = add(torque, delta_torque);
200 }
201
202 edge3_index = edges[edge3_index as usize].next;
204 v2 = v3;
205 local_point2 = local_point3;
206
207 if edge1_index == edge3_index {
208 break;
209 }
210 }
211 }
212 }
213
214 _ => {}
215 }
216
217 let sim = &mut world.solver_sets[AWAKE_SET as usize].body_sims[local_index as usize];
218 sim.force = add(sim.force, force);
219 sim.torque = add(sim.torque, torque);
220}