avian3d/dynamics/solver/joints/
revolute.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 RevoluteJoint {
20 pub entity1: Entity,
22 pub entity2: Entity,
24 pub local_anchor1: Vector,
26 pub local_anchor2: Vector,
28 #[cfg(feature = "2d")]
32 pub(crate) aligned_axis: Vector3,
33 #[cfg(feature = "3d")]
35 pub aligned_axis: Vector,
36 pub angle_limit: Option<AngleLimit>,
38 pub damping_linear: Scalar,
40 pub damping_angular: Scalar,
42 pub position_lagrange: Scalar,
44 pub align_lagrange: Scalar,
46 pub angle_limit_lagrange: Scalar,
48 pub compliance: Scalar,
50 pub force: Vector,
52 pub align_torque: Torque,
54 pub angle_limit_torque: Torque,
56}
57
58impl XpbdConstraint<2> for RevoluteJoint {
59 fn entities(&self) -> [Entity; 2] {
60 [self.entity1, self.entity2]
61 }
62
63 fn clear_lagrange_multipliers(&mut self) {
64 self.position_lagrange = 0.0;
65 self.align_lagrange = 0.0;
66 self.angle_limit_lagrange = 0.0;
67 }
68
69 fn solve(&mut self, bodies: [&mut RigidBodyQueryItem; 2], dt: Scalar) {
70 let [body1, body2] = bodies;
71 let compliance = self.compliance;
72
73 #[cfg(feature = "3d")]
74 {
75 let difference = self.get_rotation_difference(&body1.rotation, &body2.rotation);
77 let mut lagrange = self.align_lagrange;
78 self.align_torque =
79 self.align_orientation(body1, body2, difference, &mut lagrange, compliance, dt);
80 self.align_lagrange = lagrange;
81 }
82
83 self.angle_limit_torque = self.apply_angle_limits(body1, body2, dt);
85
86 let mut lagrange = self.position_lagrange;
88 self.force = self.align_position(
89 body1,
90 body2,
91 self.local_anchor1,
92 self.local_anchor2,
93 &mut lagrange,
94 compliance,
95 dt,
96 );
97 self.position_lagrange = lagrange;
98 }
99}
100
101impl Joint for RevoluteJoint {
102 fn new(entity1: Entity, entity2: Entity) -> Self {
103 Self {
104 entity1,
105 entity2,
106 local_anchor1: Vector::ZERO,
107 local_anchor2: Vector::ZERO,
108 aligned_axis: Vector3::Z,
109 angle_limit: None,
110 damping_linear: 1.0,
111 damping_angular: 1.0,
112 position_lagrange: 0.0,
113 align_lagrange: 0.0,
114 angle_limit_lagrange: 0.0,
115 compliance: 0.0,
116 force: Vector::ZERO,
117 #[cfg(feature = "2d")]
118 align_torque: 0.0,
119 #[cfg(feature = "3d")]
120 align_torque: Vector::ZERO,
121 #[cfg(feature = "2d")]
122 angle_limit_torque: 0.0,
123 #[cfg(feature = "3d")]
124 angle_limit_torque: Vector::ZERO,
125 }
126 }
127
128 fn with_compliance(self, compliance: Scalar) -> Self {
129 Self { compliance, ..self }
130 }
131
132 fn with_local_anchor_1(self, anchor: Vector) -> Self {
133 Self {
134 local_anchor1: anchor,
135 ..self
136 }
137 }
138
139 fn with_local_anchor_2(self, anchor: Vector) -> Self {
140 Self {
141 local_anchor2: anchor,
142 ..self
143 }
144 }
145
146 fn with_linear_velocity_damping(self, damping: Scalar) -> Self {
147 Self {
148 damping_linear: damping,
149 ..self
150 }
151 }
152
153 fn with_angular_velocity_damping(self, damping: Scalar) -> Self {
154 Self {
155 damping_angular: damping,
156 ..self
157 }
158 }
159
160 fn local_anchor_1(&self) -> Vector {
161 self.local_anchor1
162 }
163
164 fn local_anchor_2(&self) -> Vector {
165 self.local_anchor2
166 }
167
168 fn damping_linear(&self) -> Scalar {
169 self.damping_linear
170 }
171
172 fn damping_angular(&self) -> Scalar {
173 self.damping_angular
174 }
175}
176
177impl RevoluteJoint {
178 #[cfg(feature = "3d")]
180 pub fn with_aligned_axis(self, axis: Vector) -> Self {
181 Self {
182 aligned_axis: axis,
183 ..self
184 }
185 }
186
187 pub fn with_angle_limits(self, min: Scalar, max: Scalar) -> Self {
189 Self {
190 angle_limit: Some(AngleLimit::new(min, max)),
191 ..self
192 }
193 }
194
195 #[cfg(feature = "3d")]
196 fn get_rotation_difference(&self, rot1: &Rotation, rot2: &Rotation) -> Vector3 {
197 let a1 = rot1 * self.aligned_axis;
198 let a2 = rot2 * self.aligned_axis;
199 a1.cross(a2)
200 }
201
202 #[allow(clippy::too_many_arguments)]
204 fn apply_angle_limits(
205 &mut self,
206 body1: &mut RigidBodyQueryItem,
207 body2: &mut RigidBodyQueryItem,
208 dt: Scalar,
209 ) -> Torque {
210 let Some(Some(correction)) = self.angle_limit.map(|angle_limit| {
211 #[cfg(feature = "2d")]
212 {
213 angle_limit.compute_correction(*body1.rotation, *body2.rotation, PI)
214 }
215 #[cfg(feature = "3d")]
216 {
217 let a1 = *body1.rotation * self.aligned_axis;
219 let b1 = *body1.rotation * self.aligned_axis.any_orthonormal_vector();
220 let b2 = *body2.rotation * self.aligned_axis.any_orthonormal_vector();
221 angle_limit.compute_correction(a1, b1, b2, PI)
222 }
223 }) else {
224 return Torque::ZERO;
225 };
226
227 let mut lagrange = self.angle_limit_lagrange;
228 let torque =
229 self.align_orientation(body1, body2, correction, &mut lagrange, self.compliance, dt);
230 self.angle_limit_lagrange = lagrange;
231 torque
232 }
233}
234
235impl PositionConstraint for RevoluteJoint {}
236
237impl AngularConstraint for RevoluteJoint {}
238
239impl MapEntities for RevoluteJoint {
240 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
241 self.entity1 = entity_mapper.get_mapped(self.entity1);
242 self.entity2 = entity_mapper.get_mapped(self.entity2);
243 }
244}