avian3d/dynamics/solver/xpbd/
angular_constraint.rs1use super::XpbdConstraint;
2use crate::prelude::*;
3
4pub trait AngularConstraint: XpbdConstraint<2> {
6 #[cfg(feature = "2d")]
11 fn apply_angular_lagrange_update(
12 &self,
13 body1: &mut RigidBodyQueryItem,
14 body2: &mut RigidBodyQueryItem,
15 delta_lagrange: Scalar,
16 ) -> Scalar {
17 if delta_lagrange.abs() <= Scalar::EPSILON {
18 return 0.0;
19 }
20
21 self.apply_angular_impulse(body1, body2, -delta_lagrange)
22 }
23
24 #[cfg(feature = "2d")]
29 fn apply_angular_impulse(
30 &self,
31 body1: &mut RigidBodyQueryItem,
32 body2: &mut RigidBodyQueryItem,
33 impulse: Scalar,
34 ) -> Scalar {
35 let inv_inertia1 = body1.effective_global_angular_inertia().inverse();
36 let inv_inertia2 = body2.effective_global_angular_inertia().inverse();
37
38 if body1.rb.is_dynamic() && body1.dominance() <= body2.dominance() {
40 let delta_angle = Self::get_delta_rot(inv_inertia1, impulse);
41 *body1.rotation = body1.rotation.add_angle(delta_angle);
42 }
43 if body2.rb.is_dynamic() && body2.dominance() <= body1.dominance() {
44 let delta_angle = Self::get_delta_rot(inv_inertia2, -impulse);
45 *body2.rotation = body2.rotation.add_angle(delta_angle);
46 }
47
48 impulse
49 }
50
51 #[cfg(feature = "3d")]
56 fn apply_angular_lagrange_update(
57 &self,
58 body1: &mut RigidBodyQueryItem,
59 body2: &mut RigidBodyQueryItem,
60 delta_lagrange: Scalar,
61 axis: Vector,
62 ) -> Vector {
63 if delta_lagrange.abs() <= Scalar::EPSILON {
64 return Vector::ZERO;
65 }
66
67 let impulse = -delta_lagrange * axis;
68
69 self.apply_angular_impulse(body1, body2, impulse)
70 }
71
72 #[cfg(feature = "3d")]
77 fn apply_angular_impulse(
78 &self,
79 body1: &mut RigidBodyQueryItem,
80 body2: &mut RigidBodyQueryItem,
81 impulse: Vector,
82 ) -> Vector {
83 let inv_inertia1 = body1.effective_global_angular_inertia().inverse();
84 let inv_inertia2 = body2.effective_global_angular_inertia().inverse();
85
86 if body1.rb.is_dynamic() {
88 let delta_quat = Self::get_delta_rot(inv_inertia1, impulse);
93 body1.rotation.0 = delta_quat * body1.rotation.0;
94 *body1.rotation = body1.rotation.fast_renormalize();
95 }
96 if body2.rb.is_dynamic() {
97 let delta_quat = Self::get_delta_rot(inv_inertia2, -impulse);
99 body2.rotation.0 = delta_quat * body2.rotation.0;
100 *body2.rotation = body2.rotation.fast_renormalize();
101 }
102
103 impulse
104 }
105
106 #[cfg(feature = "2d")]
110 fn align_orientation(
111 &self,
112 body1: &mut RigidBodyQueryItem,
113 body2: &mut RigidBodyQueryItem,
114 angle: Scalar,
115 lagrange: &mut Scalar,
116 compliance: Scalar,
117 dt: Scalar,
118 ) -> Torque {
119 if angle.abs() <= Scalar::EPSILON {
120 return Torque::ZERO;
121 }
122
123 let w1 = body1.effective_global_angular_inertia().inverse();
124 let w2 = body2.effective_global_angular_inertia().inverse();
125 let w = [w1, w2];
126
127 let delta_lagrange = self.compute_lagrange_update(*lagrange, angle, &w, compliance, dt);
129 *lagrange += delta_lagrange;
130
131 self.apply_angular_lagrange_update(body1, body2, delta_lagrange);
133
134 self.compute_torque(delta_lagrange, dt)
136 }
137
138 #[cfg(feature = "3d")]
142 fn align_orientation(
143 &self,
144 body1: &mut RigidBodyQueryItem,
145 body2: &mut RigidBodyQueryItem,
146 rotation_difference: Vector,
147 lagrange: &mut Scalar,
148 compliance: Scalar,
149 dt: Scalar,
150 ) -> Torque {
151 let angle = rotation_difference.length();
152
153 if angle <= Scalar::EPSILON {
154 return Torque::ZERO;
155 }
156
157 let axis = rotation_difference / angle;
158
159 let w1 = AngularConstraint::compute_generalized_inverse_mass(self, body1, axis);
161 let w2 = AngularConstraint::compute_generalized_inverse_mass(self, body2, axis);
162 let w = [w1, w2];
163
164 let delta_lagrange = self.compute_lagrange_update(*lagrange, angle, &w, compliance, dt);
166 *lagrange += delta_lagrange;
167
168 self.apply_angular_lagrange_update(body1, body2, delta_lagrange, axis);
170
171 self.compute_torque(delta_lagrange, axis, dt)
173 }
174
175 #[cfg(feature = "2d")]
181 fn apply_angular_correction(
182 &self,
183 body1: &mut RigidBodyQueryItem,
184 body2: &mut RigidBodyQueryItem,
185 delta_lagrange: Scalar,
186 axis: Vector3,
187 ) -> Scalar {
188 if delta_lagrange.abs() <= Scalar::EPSILON {
189 return 0.0;
190 }
191
192 let p = -delta_lagrange * axis.z;
195
196 let inv_inertia1 = body1.effective_global_angular_inertia().inverse();
197 let inv_inertia2 = body2.effective_global_angular_inertia().inverse();
198
199 if body1.rb.is_dynamic() && body1.dominance() <= body2.dominance() {
201 let delta_angle = Self::get_delta_rot(inv_inertia1, p);
202 *body1.rotation = body1.rotation.add_angle(delta_angle);
203 }
204 if body2.rb.is_dynamic() && body2.dominance() <= body1.dominance() {
205 let delta_angle = Self::get_delta_rot(inv_inertia2, -p);
206 *body2.rotation = body2.rotation.add_angle(delta_angle);
207 }
208
209 p
210 }
211
212 #[cfg(feature = "3d")]
216 fn apply_angular_correction(
217 &self,
218 body1: &mut RigidBodyQueryItem,
219 body2: &mut RigidBodyQueryItem,
220 delta_lagrange: Scalar,
221 axis: Vector,
222 ) -> Vector {
223 if delta_lagrange.abs() <= Scalar::EPSILON {
224 return Vector::ZERO;
225 }
226
227 let p = -delta_lagrange * axis;
229
230 let inv_inertia1 = body1.effective_global_angular_inertia().inverse();
231 let inv_inertia2 = body2.effective_global_angular_inertia().inverse();
232
233 if body1.rb.is_dynamic() {
235 let delta_quat = Self::get_delta_rot(inv_inertia1, p);
240 body1.rotation.0 = delta_quat * body1.rotation.0;
241 *body1.rotation = body1.rotation.fast_renormalize();
242 }
243 if body2.rb.is_dynamic() {
244 let delta_quat = Self::get_delta_rot(inv_inertia2, -p);
246 body2.rotation.0 = delta_quat * body2.rotation.0;
247 *body2.rotation = body2.rotation.fast_renormalize();
248 }
249
250 p
251 }
252
253 #[cfg(feature = "2d")]
259 fn compute_generalized_inverse_mass(&self, body: &RigidBodyQueryItem, axis: Vector3) -> Scalar {
260 if body.rb.is_dynamic() {
261 axis.dot(body.angular_inertia.inverse() * axis)
262 } else {
263 0.0
265 }
266 }
267
268 #[cfg(feature = "3d")]
271 fn compute_generalized_inverse_mass(&self, body: &RigidBodyQueryItem, axis: Vector) -> Scalar {
272 if body.rb.is_dynamic() {
273 axis.dot(body.effective_global_angular_inertia().inverse() * axis)
274 } else {
275 0.0
277 }
278 }
279
280 #[cfg(feature = "2d")]
282 fn get_delta_rot(inverse_inertia: Scalar, p: Scalar) -> Scalar {
283 inverse_inertia * p
285 }
286
287 #[cfg(feature = "3d")]
289 fn get_delta_rot(inverse_inertia: Matrix3, p: Vector) -> Quaternion {
290 Quaternion::from_scaled_axis(inverse_inertia * p)
292 }
293
294 #[cfg(feature = "2d")]
297 fn compute_torque(&self, lagrange: Scalar, dt: Scalar) -> Torque {
298 lagrange / dt.powi(2)
300 }
301
302 #[cfg(feature = "3d")]
304 fn compute_torque(&self, lagrange: Scalar, axis: Vector, dt: Scalar) -> Torque {
305 lagrange * axis / dt.powi(2)
307 }
308}