aboutsummaryrefslogtreecommitdiff
path: root/src/dynamics/solver/joint_constraint/joint_constraint_helper.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/dynamics/solver/joint_constraint/joint_constraint_helper.rs')
-rw-r--r--src/dynamics/solver/joint_constraint/joint_constraint_helper.rs141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/dynamics/solver/joint_constraint/joint_constraint_helper.rs b/src/dynamics/solver/joint_constraint/joint_constraint_helper.rs
new file mode 100644
index 0000000..5b2b311
--- /dev/null
+++ b/src/dynamics/solver/joint_constraint/joint_constraint_helper.rs
@@ -0,0 +1,141 @@
+pub(self) mod math {
+ pub use crate::math::*;
+ pub type RealConst = Real;
+
+ pub const LANES: usize = 1;
+}
+
+#[path = "./joint_constraint_helper_template.rs"]
+mod joint_constraint_helper_template;
+
+use crate::utils::{SimdBasis, SimdDot};
+pub use joint_constraint_helper_template::JointConstraintHelperTemplate as JointConstraintHelper;
+
+use crate::dynamics::solver::joint_constraint::joint_velocity_constraint::{
+ JointFixedSolverBody, JointOneBodyConstraint, JointTwoBodyConstraint, WritebackId,
+};
+use crate::dynamics::solver::joint_constraint::JointSolverBody;
+use crate::dynamics::{IntegrationParameters, JointIndex};
+use crate::math::*;
+
+impl JointConstraintHelper {
+ // TODO: this method is almost identical to the one_body version, except for the
+ // return type. Could they share their implementation somehow?
+ #[cfg(feature = "dim3")]
+ pub fn limit_angular_coupled(
+ &self,
+ params: &IntegrationParameters,
+ joint_id: [JointIndex; 1],
+ body1: &JointSolverBody<Real, 1>,
+ body2: &JointSolverBody<Real, 1>,
+ coupled_axes: u8,
+ limits: [Real; 2],
+ writeback_id: WritebackId,
+ ) -> JointTwoBodyConstraint<Real, 1> {
+ // NOTE: right now, this only supports exactly 2 coupled axes.
+ let ang_coupled_axes = coupled_axes >> DIM;
+ assert_eq!(ang_coupled_axes.count_ones(), 2);
+ let not_coupled_index = ang_coupled_axes.trailing_ones() as usize;
+ let axis1 = self.basis.column(not_coupled_index).into_owned();
+ let axis2 = self.basis2.column(not_coupled_index).into_owned();
+
+ let rot = Rotation::rotation_between(&axis1, &axis2).unwrap_or_else(Rotation::identity);
+ let (ang_jac, angle) = rot
+ .axis_angle()
+ .map(|(axis, angle)| (axis.into_inner(), angle))
+ .unwrap_or_else(|| (axis1.orthonormal_basis()[0], 0.0));
+
+ let min_enabled = angle <= limits[0];
+ let max_enabled = limits[1] <= angle;
+
+ let impulse_bounds = [
+ if min_enabled { -Real::INFINITY } else { 0.0 },
+ if max_enabled { Real::INFINITY } else { 0.0 },
+ ];
+
+ let rhs_wo_bias = 0.0;
+
+ let erp_inv_dt = params.joint_erp_inv_dt();
+ let cfm_coeff = params.joint_cfm_coeff();
+ let rhs_bias = ((angle - limits[1]).max(0.0) - (limits[0] - angle).max(0.0)) * erp_inv_dt;
+
+ let ang_jac1 = body1.sqrt_ii * ang_jac;
+ let ang_jac2 = body2.sqrt_ii * ang_jac;
+
+ JointTwoBodyConstraint {
+ joint_id,
+ solver_vel1: body1.solver_vel,
+ solver_vel2: body2.solver_vel,
+ im1: body1.im,
+ im2: body2.im,
+ impulse: 0.0,
+ impulse_bounds,
+ lin_jac: Default::default(),
+ ang_jac1,
+ ang_jac2,
+ inv_lhs: 0.0, // Will be set during ortogonalization.
+ cfm_coeff,
+ cfm_gain: 0.0,
+ rhs: rhs_wo_bias + rhs_bias,
+ rhs_wo_bias,
+ writeback_id,
+ }
+ }
+
+ #[cfg(feature = "dim3")]
+ pub fn limit_angular_coupled_one_body(
+ &self,
+ params: &IntegrationParameters,
+ joint_id: [JointIndex; 1],
+ body1: &JointFixedSolverBody<Real>,
+ body2: &JointSolverBody<Real, 1>,
+ coupled_axes: u8,
+ limits: [Real; 2],
+ writeback_id: WritebackId,
+ ) -> JointOneBodyConstraint<Real, 1> {
+ // NOTE: right now, this only supports exactly 2 coupled axes.
+ let ang_coupled_axes = coupled_axes >> DIM;
+ assert_eq!(ang_coupled_axes.count_ones(), 2);
+ let not_coupled_index = ang_coupled_axes.trailing_ones() as usize;
+ let axis1 = self.basis.column(not_coupled_index).into_owned();
+ let axis2 = self.basis2.column(not_coupled_index).into_owned();
+
+ let rot = Rotation::rotation_between(&axis1, &axis2).unwrap_or_else(Rotation::identity);
+ let (ang_jac, angle) = rot
+ .axis_angle()
+ .map(|(axis, angle)| (axis.into_inner(), angle))
+ .unwrap_or_else(|| (axis1.orthonormal_basis()[0], 0.0));
+
+ let min_enabled = angle <= limits[0];
+ let max_enabled = limits[1] <= angle;
+
+ let impulse_bounds = [
+ if min_enabled { -Real::INFINITY } else { 0.0 },
+ if max_enabled { Real::INFINITY } else { 0.0 },
+ ];
+
+ let rhs_wo_bias = -ang_jac.gdot(body1.angvel);
+
+ let erp_inv_dt = params.joint_erp_inv_dt();
+ let cfm_coeff = params.joint_cfm_coeff();
+ let rhs_bias = ((angle - limits[1]).max(0.0) - (limits[0] - angle).max(0.0)) * erp_inv_dt;
+
+ let ang_jac2 = body2.sqrt_ii * ang_jac;
+
+ JointOneBodyConstraint {
+ joint_id,
+ solver_vel2: body2.solver_vel,
+ im2: body2.im,
+ impulse: 0.0,
+ impulse_bounds,
+ lin_jac: Default::default(),
+ ang_jac2,
+ inv_lhs: 0.0, // Will be set during ortogonalization.
+ cfm_coeff,
+ cfm_gain: 0.0,
+ rhs: rhs_wo_bias + rhs_bias,
+ rhs_wo_bias,
+ writeback_id,
+ }
+ }
+}