avian3d/dynamics/solver/joints/
spherical.rs1use crate::{dynamics::solver::xpbd::*, prelude::*};
4use bevy::{
5 ecs::{
6 entity::{EntityMapper, MapEntities},
7 reflect::ReflectMapEntities,
8 },
9 prelude::*,
10};
11
12#[derive(Component, Clone, Copy, Debug, PartialEq, Reflect)]
16#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
18#[reflect(Debug, Component, MapEntities, PartialEq)]
19pub struct SphericalJoint {
20 pub entity1: Entity,
22 pub entity2: Entity,
24 pub local_anchor1: Vector,
26 pub local_anchor2: Vector,
28 pub swing_axis: Vector3,
30 pub twist_axis: Vector3,
32 pub swing_limit: Option<AngleLimit>,
34 pub twist_limit: Option<AngleLimit>,
36 pub damping_linear: Scalar,
38 pub damping_angular: Scalar,
40 pub position_lagrange: Scalar,
42 pub swing_lagrange: Scalar,
44 pub twist_lagrange: Scalar,
46 pub compliance: Scalar,
48 pub force: Vector,
50 pub swing_torque: Torque,
52 pub twist_torque: Torque,
54}
55
56impl XpbdConstraint<2> for SphericalJoint {
57 fn entities(&self) -> [Entity; 2] {
58 [self.entity1, self.entity2]
59 }
60
61 fn clear_lagrange_multipliers(&mut self) {
62 self.position_lagrange = 0.0;
63 self.swing_lagrange = 0.0;
64 self.twist_lagrange = 0.0;
65 }
66
67 fn solve(&mut self, bodies: [&mut RigidBodyQueryItem; 2], dt: Scalar) {
68 let [body1, body2] = bodies;
69 let compliance = self.compliance;
70
71 let mut lagrange = self.position_lagrange;
73 self.force = self.align_position(
74 body1,
75 body2,
76 self.local_anchor1,
77 self.local_anchor2,
78 &mut lagrange,
79 compliance,
80 dt,
81 );
82 self.position_lagrange = lagrange;
83
84 self.swing_torque = self.apply_swing_limits(body1, body2, dt);
86
87 self.twist_torque = self.apply_twist_limits(body1, body2, dt);
89 }
90}
91
92impl Joint for SphericalJoint {
93 fn new(entity1: Entity, entity2: Entity) -> Self {
94 Self {
95 entity1,
96 entity2,
97 local_anchor1: Vector::ZERO,
98 local_anchor2: Vector::ZERO,
99 swing_axis: Vector3::X,
100 twist_axis: Vector3::Y,
101 swing_limit: None,
102 twist_limit: None,
103 damping_linear: 1.0,
104 damping_angular: 1.0,
105 position_lagrange: 0.0,
106 swing_lagrange: 0.0,
107 twist_lagrange: 0.0,
108 compliance: 0.0,
109 force: Vector::ZERO,
110 #[cfg(feature = "2d")]
111 swing_torque: 0.0,
112 #[cfg(feature = "3d")]
113 swing_torque: Vector::ZERO,
114 #[cfg(feature = "2d")]
115 twist_torque: 0.0,
116 #[cfg(feature = "3d")]
117 twist_torque: Vector::ZERO,
118 }
119 }
120
121 fn with_compliance(self, compliance: Scalar) -> Self {
122 Self { compliance, ..self }
123 }
124
125 fn with_local_anchor_1(self, anchor: Vector) -> Self {
126 Self {
127 local_anchor1: anchor,
128 ..self
129 }
130 }
131
132 fn with_local_anchor_2(self, anchor: Vector) -> Self {
133 Self {
134 local_anchor2: anchor,
135 ..self
136 }
137 }
138
139 fn with_linear_velocity_damping(self, damping: Scalar) -> Self {
140 Self {
141 damping_linear: damping,
142 ..self
143 }
144 }
145
146 fn with_angular_velocity_damping(self, damping: Scalar) -> Self {
147 Self {
148 damping_angular: damping,
149 ..self
150 }
151 }
152
153 fn local_anchor_1(&self) -> Vector {
154 self.local_anchor1
155 }
156
157 fn local_anchor_2(&self) -> Vector {
158 self.local_anchor2
159 }
160
161 fn damping_linear(&self) -> Scalar {
162 self.damping_linear
163 }
164
165 fn damping_angular(&self) -> Scalar {
166 self.damping_angular
167 }
168}
169
170impl SphericalJoint {
171 pub fn with_swing_limits(self, min: Scalar, max: Scalar) -> Self {
173 Self {
174 swing_limit: Some(AngleLimit::new(min, max)),
175 ..self
176 }
177 }
178
179 #[cfg(feature = "3d")]
181 pub fn with_twist_limits(self, min: Scalar, max: Scalar) -> Self {
182 Self {
183 twist_limit: Some(AngleLimit::new(min, max)),
184 ..self
185 }
186 }
187
188 fn apply_swing_limits(
190 &mut self,
191 body1: &mut RigidBodyQueryItem,
192 body2: &mut RigidBodyQueryItem,
193 dt: Scalar,
194 ) -> Torque {
195 if let Some(joint_limit) = self.swing_limit {
196 let a1 = *body1.rotation * self.swing_axis;
197 let a2 = *body2.rotation * self.swing_axis;
198
199 let n = a1.cross(a2);
200 let n_magnitude = n.length();
201
202 if n_magnitude <= Scalar::EPSILON {
203 return Torque::ZERO;
204 }
205
206 let n = n / n_magnitude;
207
208 if let Some(correction) = joint_limit.compute_correction(n, a1, a2, PI) {
209 let mut lagrange = self.swing_lagrange;
210 let torque = self.align_orientation(
211 body1,
212 body2,
213 correction,
214 &mut lagrange,
215 self.compliance,
216 dt,
217 );
218 self.swing_lagrange = lagrange;
219 return torque;
220 }
221 }
222 Torque::ZERO
223 }
224
225 fn apply_twist_limits(
227 &mut self,
228 body1: &mut RigidBodyQueryItem,
229 body2: &mut RigidBodyQueryItem,
230 dt: Scalar,
231 ) -> Torque {
232 if let Some(joint_limit) = self.twist_limit {
233 let a1 = *body1.rotation * self.swing_axis;
234 let a2 = *body2.rotation * self.swing_axis;
235
236 let b1 = *body1.rotation * self.twist_axis;
237 let b2 = *body2.rotation * self.twist_axis;
238
239 let n = a1 + a2;
240 let n_magnitude = n.length();
241
242 if n_magnitude <= Scalar::EPSILON {
243 return Torque::ZERO;
244 }
245
246 let n = n / n_magnitude;
247
248 let n1 = b1 - n.dot(b1) * n;
249 let n2 = b2 - n.dot(b2) * n;
250 let n1_magnitude = n1.length();
251 let n2_magnitude = n2.length();
252
253 if n1_magnitude <= Scalar::EPSILON || n2_magnitude <= Scalar::EPSILON {
254 return Torque::ZERO;
255 }
256
257 let n1 = n1 / n1_magnitude;
258 let n2 = n2 / n2_magnitude;
259
260 let max_correction = if a1.dot(a2) > -0.5 { 2.0 * PI } else { dt };
261
262 if let Some(correction) = joint_limit.compute_correction(n, n1, n2, max_correction) {
263 let mut lagrange = self.twist_lagrange;
264 let torque = self.align_orientation(
265 body1,
266 body2,
267 correction,
268 &mut lagrange,
269 self.compliance,
270 dt,
271 );
272 self.twist_lagrange = lagrange;
273 return torque;
274 }
275 }
276 Torque::ZERO
277 }
278}
279
280impl PositionConstraint for SphericalJoint {}
281
282impl AngularConstraint for SphericalJoint {}
283
284impl MapEntities for SphericalJoint {
285 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
286 self.entity1 = entity_mapper.get_mapped(self.entity1);
287 self.entity2 = entity_mapper.get_mapped(self.entity2);
288 }
289}