1 /*************************************************************************
3 * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. *
4 * All rights reserved. Email: russ@q12.org Web: www.q12.org *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of EITHER: *
8 * (1) The GNU Lesser General Public License as published by the Free *
9 * Software Foundation; either version 2.1 of the License, or (at *
10 * your option) any later version. The text of the GNU Lesser *
11 * General Public License is included with this library in the *
13 * (2) The BSD-style license that is included with this library in *
14 * the file LICENSE-BSD.TXT. *
16 * This library is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
19 * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
21 *************************************************************************/
25 design note: the general principle for giving a joint the option of connecting
26 to the static environment (i.e. the absolute frame) is to check the second
27 body (joint->node[1].body), and if it is zero then behave as if its body
28 transform is the identity.
33 #include <ode/odemath.h>
34 #include <ode/rotation.h>
35 #include <ode/matrix.h>
38 //****************************************************************************
41 // extern "C" void dBodyAddTorque (dBodyID, dReal fx, dReal fy, dReal fz);
42 // extern "C" void dBodyAddForce (dBodyID, dReal fx, dReal fy, dReal fz);
44 //****************************************************************************
47 // set three "ball-and-socket" rows in the constraint equation, and the
48 // corresponding right hand side.
50 static inline void setBall (dxJoint
*joint
, dxJoint::Info2
*info
,
51 dVector3 anchor1
, dVector3 anchor2
)
53 // anchor points in global coordinates with respect to body PORs.
56 int s
= info
->rowskip
;
62 dMULTIPLY0_331 (a1
,joint
->node
[0].body
->posr
.R
,anchor1
);
63 dCROSSMAT (info
->J1a
,a1
,s
,-,+);
64 if (joint
->node
[1].body
) {
67 info
->J2l
[2*s
+2] = -1;
68 dMULTIPLY0_331 (a2
,joint
->node
[1].body
->posr
.R
,anchor2
);
69 dCROSSMAT (info
->J2a
,a2
,s
,+,-);
72 // set right hand side
73 dReal k
= info
->fps
* info
->erp
;
74 if (joint
->node
[1].body
) {
75 for (int j
=0; j
<3; j
++) {
76 info
->c
[j
] = k
* (a2
[j
] + joint
->node
[1].body
->posr
.pos
[j
] -
77 a1
[j
] - joint
->node
[0].body
->posr
.pos
[j
]);
81 for (int j
=0; j
<3; j
++) {
82 info
->c
[j
] = k
* (anchor2
[j
] - a1
[j
] -
83 joint
->node
[0].body
->posr
.pos
[j
]);
89 // this is like setBall(), except that `axis' is a unit length vector
90 // (in global coordinates) that should be used for the first jacobian
91 // position row (the other two row vectors will be derived from this).
92 // `erp1' is the erp value to use along the axis.
94 static inline void setBall2 (dxJoint
*joint
, dxJoint::Info2
*info
,
95 dVector3 anchor1
, dVector3 anchor2
,
96 dVector3 axis
, dReal erp1
)
98 // anchor points in global coordinates with respect to body PORs.
101 int i
,s
= info
->rowskip
;
103 // get vectors normal to the axis. in setBall() axis,q1,q2 is [1 0 0],
104 // [0 1 0] and [0 0 1], which makes everything much easier.
106 dPlaneSpace (axis
,q1
,q2
);
109 for (i
=0; i
<3; i
++) info
->J1l
[i
] = axis
[i
];
110 for (i
=0; i
<3; i
++) info
->J1l
[s
+i
] = q1
[i
];
111 for (i
=0; i
<3; i
++) info
->J1l
[2*s
+i
] = q2
[i
];
112 dMULTIPLY0_331 (a1
,joint
->node
[0].body
->posr
.R
,anchor1
);
113 dCROSS (info
->J1a
,=,a1
,axis
);
114 dCROSS (info
->J1a
+s
,=,a1
,q1
);
115 dCROSS (info
->J1a
+2*s
,=,a1
,q2
);
116 if (joint
->node
[1].body
) {
117 for (i
=0; i
<3; i
++) info
->J2l
[i
] = -axis
[i
];
118 for (i
=0; i
<3; i
++) info
->J2l
[s
+i
] = -q1
[i
];
119 for (i
=0; i
<3; i
++) info
->J2l
[2*s
+i
] = -q2
[i
];
120 dMULTIPLY0_331 (a2
,joint
->node
[1].body
->posr
.R
,anchor2
);
121 dCROSS (info
->J2a
,= -,a2
,axis
);
122 dCROSS (info
->J2a
+s
,= -,a2
,q1
);
123 dCROSS (info
->J2a
+2*s
,= -,a2
,q2
);
126 // set right hand side - measure error along (axis,q1,q2)
127 dReal k1
= info
->fps
* erp1
;
128 dReal k
= info
->fps
* info
->erp
;
130 for (i
=0; i
<3; i
++) a1
[i
] += joint
->node
[0].body
->posr
.pos
[i
];
131 if (joint
->node
[1].body
) {
132 for (i
=0; i
<3; i
++) a2
[i
] += joint
->node
[1].body
->posr
.pos
[i
];
133 info
->c
[0] = k1
* (dDOT(axis
,a2
) - dDOT(axis
,a1
));
134 info
->c
[1] = k
* (dDOT(q1
,a2
) - dDOT(q1
,a1
));
135 info
->c
[2] = k
* (dDOT(q2
,a2
) - dDOT(q2
,a1
));
138 info
->c
[0] = k1
* (dDOT(axis
,anchor2
) - dDOT(axis
,a1
));
139 info
->c
[1] = k
* (dDOT(q1
,anchor2
) - dDOT(q1
,a1
));
140 info
->c
[2] = k
* (dDOT(q2
,anchor2
) - dDOT(q2
,a1
));
145 // set three orientation rows in the constraint equation, and the
146 // corresponding right hand side.
148 static void setFixedOrientation(dxJoint
*joint
, dxJoint::Info2
*info
, dQuaternion qrel
, int start_row
)
150 int s
= info
->rowskip
;
151 int start_index
= start_row
* s
;
153 // 3 rows to make body rotations equal
154 info
->J1a
[start_index
] = 1;
155 info
->J1a
[start_index
+ s
+ 1] = 1;
156 info
->J1a
[start_index
+ s
*2+2] = 1;
157 if (joint
->node
[1].body
) {
158 info
->J2a
[start_index
] = -1;
159 info
->J2a
[start_index
+ s
+1] = -1;
160 info
->J2a
[start_index
+ s
*2+2] = -1;
163 // compute the right hand side. the first three elements will result in
164 // relative angular velocity of the two bodies - this is set to bring them
165 // back into alignment. the correcting angular velocity is
166 // |angular_velocity| = angle/time = erp*theta / stepsize
167 // = (erp*fps) * theta
168 // angular_velocity = |angular_velocity| * u
169 // = (erp*fps) * theta * u
170 // where rotation along unit length axis u by theta brings body 2's frame
171 // to qrel with respect to body 1's frame. using a small angle approximation
172 // for sin(), this gives
173 // angular_velocity = (erp*fps) * 2 * v
174 // where the quaternion of the relative rotation between the two bodies is
175 // q = [cos(theta/2) sin(theta/2)*u] = [s v]
177 // get qerr = relative rotation (rotation error) between two bodies
179 if (joint
->node
[1].body
) {
181 dQMultiply1 (qq
,joint
->node
[0].body
->q
,joint
->node
[1].body
->q
);
182 dQMultiply2 (qerr
,qq
,qrel
);
185 dQMultiply3 (qerr
,joint
->node
[0].body
->q
,qrel
);
188 qerr
[1] = -qerr
[1]; // adjust sign of qerr to make theta small
192 dMULTIPLY0_331 (e
,joint
->node
[0].body
->posr
.R
,qerr
+1); // @@@ bad SIMD padding!
193 dReal k
= info
->fps
* info
->erp
;
194 info
->c
[start_row
] = 2*k
* e
[0];
195 info
->c
[start_row
+1] = 2*k
* e
[1];
196 info
->c
[start_row
+2] = 2*k
* e
[2];
200 // compute anchor points relative to bodies
202 static void setAnchors (dxJoint
*j
, dReal x
, dReal y
, dReal z
,
203 dVector3 anchor1
, dVector3 anchor2
)
205 if (j
->node
[0].body
) {
207 q
[0] = x
- j
->node
[0].body
->posr
.pos
[0];
208 q
[1] = y
- j
->node
[0].body
->posr
.pos
[1];
209 q
[2] = z
- j
->node
[0].body
->posr
.pos
[2];
211 dMULTIPLY1_331 (anchor1
,j
->node
[0].body
->posr
.R
,q
);
212 if (j
->node
[1].body
) {
213 q
[0] = x
- j
->node
[1].body
->posr
.pos
[0];
214 q
[1] = y
- j
->node
[1].body
->posr
.pos
[1];
215 q
[2] = z
- j
->node
[1].body
->posr
.pos
[2];
217 dMULTIPLY1_331 (anchor2
,j
->node
[1].body
->posr
.R
,q
);
230 // compute axes relative to bodies. either axis1 or axis2 can be 0.
232 static void setAxes (dxJoint
*j
, dReal x
, dReal y
, dReal z
,
233 dVector3 axis1
, dVector3 axis2
)
235 if (j
->node
[0].body
) {
243 dMULTIPLY1_331 (axis1
,j
->node
[0].body
->posr
.R
,q
);
247 if (j
->node
[1].body
) {
248 dMULTIPLY1_331 (axis2
,j
->node
[1].body
->posr
.R
,q
);
261 static void getAnchor (dxJoint
*j
, dVector3 result
, dVector3 anchor1
)
263 if (j
->node
[0].body
) {
264 dMULTIPLY0_331 (result
,j
->node
[0].body
->posr
.R
,anchor1
);
265 result
[0] += j
->node
[0].body
->posr
.pos
[0];
266 result
[1] += j
->node
[0].body
->posr
.pos
[1];
267 result
[2] += j
->node
[0].body
->posr
.pos
[2];
272 static void getAnchor2 (dxJoint
*j
, dVector3 result
, dVector3 anchor2
)
274 if (j
->node
[1].body
) {
275 dMULTIPLY0_331 (result
,j
->node
[1].body
->posr
.R
,anchor2
);
276 result
[0] += j
->node
[1].body
->posr
.pos
[0];
277 result
[1] += j
->node
[1].body
->posr
.pos
[1];
278 result
[2] += j
->node
[1].body
->posr
.pos
[2];
281 result
[0] = anchor2
[0];
282 result
[1] = anchor2
[1];
283 result
[2] = anchor2
[2];
288 static void getAxis (dxJoint
*j
, dVector3 result
, dVector3 axis1
)
290 if (j
->node
[0].body
) {
291 dMULTIPLY0_331 (result
,j
->node
[0].body
->posr
.R
,axis1
);
296 static void getAxis2 (dxJoint
*j
, dVector3 result
, dVector3 axis2
)
298 if (j
->node
[1].body
) {
299 dMULTIPLY0_331 (result
,j
->node
[1].body
->posr
.R
,axis2
);
302 result
[0] = axis2
[0];
303 result
[1] = axis2
[1];
304 result
[2] = axis2
[2];
309 static dReal
getHingeAngleFromRelativeQuat (dQuaternion qrel
, dVector3 axis
)
311 // the angle between the two bodies is extracted from the quaternion that
312 // represents the relative rotation between them. recall that a quaternion
314 // [s,v] = [ cos(theta/2) , sin(theta/2) * u ]
315 // where s is a scalar and v is a 3-vector. u is a unit length axis and
316 // theta is a rotation along that axis. we can get theta/2 by:
317 // theta/2 = atan2 ( sin(theta/2) , cos(theta/2) )
318 // but we can't get sin(theta/2) directly, only its absolute value, i.e.:
319 // |v| = |sin(theta/2)| * |u|
321 // using this value will have a strange effect. recall that there are two
322 // quaternion representations of a given rotation, q and -q. typically as
323 // a body rotates along the axis it will go through a complete cycle using
324 // one representation and then the next cycle will use the other
325 // representation. this corresponds to u pointing in the direction of the
326 // hinge axis and then in the opposite direction. the result is that theta
327 // will appear to go "backwards" every other cycle. here is a fix: if u
328 // points "away" from the direction of the hinge (motor) axis (i.e. more
329 // than 90 degrees) then use -q instead of q. this represents the same
330 // rotation, but results in the cos(theta/2) value being sign inverted.
332 // extract the angle from the quaternion. cost2 = cos(theta/2),
333 // sint2 = |sin(theta/2)|
334 dReal cost2
= qrel
[0];
335 dReal sint2
= dSqrt (qrel
[1]*qrel
[1]+qrel
[2]*qrel
[2]+qrel
[3]*qrel
[3]);
336 dReal theta
= (dDOT(qrel
+1,axis
) >= 0) ? // @@@ padding assumptions
337 (2 * dAtan2(sint2
,cost2
)) : // if u points in direction of axis
338 (2 * dAtan2(sint2
,-cost2
)); // if u points in opposite direction
340 // the angle we get will be between 0..2*pi, but we want to return angles
342 if (theta
> M_PI
) theta
-= (dReal
) (2*M_PI
);
344 // the angle we've just extracted has the wrong sign
351 // given two bodies (body1,body2), the hinge axis that they are connected by
352 // w.r.t. body1 (axis), and the initial relative orientation between them
353 // (q_initial), return the relative rotation angle. the initial relative
354 // orientation corresponds to an angle of zero. if body2 is 0 then measure the
355 // angle between body1 and the static frame.
357 // this will not return the correct angle if the bodies rotate along any axis
358 // other than the given hinge axis.
360 static dReal
getHingeAngle (dxBody
*body1
, dxBody
*body2
, dVector3 axis
,
361 dQuaternion q_initial
)
363 // get qrel = relative rotation between the two bodies
367 dQMultiply1 (qq
,body1
->q
,body2
->q
);
368 dQMultiply2 (qrel
,qq
,q_initial
);
371 // pretend body2->q is the identity
372 dQMultiply3 (qrel
,body1
->q
,q_initial
);
375 return getHingeAngleFromRelativeQuat (qrel
,axis
);
378 //****************************************************************************
381 void dxJointLimitMotor::init (dxWorld
*world
)
388 normal_cfm
= world
->global_cfm
;
389 stop_erp
= world
->global_erp
;
390 stop_cfm
= world
->global_cfm
;
397 void dxJointLimitMotor::set (int num
, dReal value
)
410 if (value
>= 0) fmax
= value
;
412 case dParamFudgeFactor
:
413 if (value
>= 0 && value
<= 1) fudge_factor
= value
;
431 dReal
dxJointLimitMotor::get (int num
)
434 case dParamLoStop
: return lostop
;
435 case dParamHiStop
: return histop
;
436 case dParamVel
: return vel
;
437 case dParamFMax
: return fmax
;
438 case dParamFudgeFactor
: return fudge_factor
;
439 case dParamBounce
: return bounce
;
440 case dParamCFM
: return normal_cfm
;
441 case dParamStopERP
: return stop_erp
;
442 case dParamStopCFM
: return stop_cfm
;
448 int dxJointLimitMotor::testRotationalLimit (dReal angle
)
450 if (angle
<= lostop
) {
452 limit_err
= angle
- lostop
;
455 else if (angle
>= histop
) {
457 limit_err
= angle
- histop
;
467 int dxJointLimitMotor::addLimot (dxJoint
*joint
,
468 dxJoint::Info2
*info
, int row
,
469 const dVector3 ax1
, int rotational
)
471 int srow
= row
* info
->rowskip
;
473 // if the joint is powered, or has joint limits, add in the extra row
474 int powered
= fmax
> 0;
475 if (powered
|| limit
) {
476 dReal
*J1
= rotational
? info
->J1a
: info
->J1l
;
477 dReal
*J2
= rotational
? info
->J2a
: info
->J2l
;
482 if (joint
->node
[1].body
) {
483 J2
[srow
+0] = -ax1
[0];
484 J2
[srow
+1] = -ax1
[1];
485 J2
[srow
+2] = -ax1
[2];
488 // linear limot torque decoupling step:
490 // if this is a linear limot (e.g. from a slider), we have to be careful
491 // that the linear constraint forces (+/- ax1) applied to the two bodies
492 // do not create a torque couple. in other words, the points that the
493 // constraint force is applied at must lie along the same ax1 axis.
494 // a torque couple will result in powered or limited slider-jointed free
495 // bodies from gaining angular momentum.
496 // the solution used here is to apply the constraint forces at the point
497 // halfway between the body centers. there is no penalty (other than an
498 // extra tiny bit of computation) in doing this adjustment. note that we
499 // only need to do this if the constraint connects two bodies.
501 dVector3 ltd
; // Linear Torque Decoupling vector (a torque)
502 if (!rotational
&& joint
->node
[1].body
) {
504 c
[0]=REAL(0.5)*(joint
->node
[1].body
->posr
.pos
[0]-joint
->node
[0].body
->posr
.pos
[0]);
505 c
[1]=REAL(0.5)*(joint
->node
[1].body
->posr
.pos
[1]-joint
->node
[0].body
->posr
.pos
[1]);
506 c
[2]=REAL(0.5)*(joint
->node
[1].body
->posr
.pos
[2]-joint
->node
[0].body
->posr
.pos
[2]);
507 dCROSS (ltd
,=,c
,ax1
);
508 info
->J1a
[srow
+0] = ltd
[0];
509 info
->J1a
[srow
+1] = ltd
[1];
510 info
->J1a
[srow
+2] = ltd
[2];
511 info
->J2a
[srow
+0] = ltd
[0];
512 info
->J2a
[srow
+1] = ltd
[1];
513 info
->J2a
[srow
+2] = ltd
[2];
516 // if we're limited low and high simultaneously, the joint motor is
518 if (limit
&& (lostop
== histop
)) powered
= 0;
521 info
->cfm
[row
] = normal_cfm
;
524 info
->lo
[row
] = -fmax
;
525 info
->hi
[row
] = fmax
;
528 // the joint is at a limit, AND is being powered. if the joint is
529 // being powered into the limit then we apply the maximum motor force
530 // in that direction, because the motor is working against the
531 // immovable limit. if the joint is being powered away from the limit
532 // then we have problems because actually we need *two* lcp
533 // constraints to handle this case. so we fake it and apply some
534 // fraction of the maximum force. the fraction to use can be set as
538 if ((vel
> 0) || (vel
==0 && limit
==2)) fm
= -fm
;
540 // if we're powering away from the limit, apply the fudge factor
541 if ((limit
==1 && vel
> 0) || (limit
==2 && vel
< 0)) fm
*= fudge_factor
;
544 dBodyAddTorque (joint
->node
[0].body
,-fm
*ax1
[0],-fm
*ax1
[1],
546 if (joint
->node
[1].body
)
547 dBodyAddTorque (joint
->node
[1].body
,fm
*ax1
[0],fm
*ax1
[1],fm
*ax1
[2]);
550 dBodyAddForce (joint
->node
[0].body
,-fm
*ax1
[0],-fm
*ax1
[1],-fm
*ax1
[2]);
551 if (joint
->node
[1].body
) {
552 dBodyAddForce (joint
->node
[1].body
,fm
*ax1
[0],fm
*ax1
[1],fm
*ax1
[2]);
554 // linear limot torque decoupling step: refer to above discussion
555 dBodyAddTorque (joint
->node
[0].body
,-fm
*ltd
[0],-fm
*ltd
[1],
557 dBodyAddTorque (joint
->node
[1].body
,-fm
*ltd
[0],-fm
*ltd
[1],
565 dReal k
= info
->fps
* stop_erp
;
566 info
->c
[row
] = -k
* limit_err
;
567 info
->cfm
[row
] = stop_cfm
;
569 if (lostop
== histop
) {
570 // limited low and high simultaneously
571 info
->lo
[row
] = -dInfinity
;
572 info
->hi
[row
] = dInfinity
;
578 info
->hi
[row
] = dInfinity
;
582 info
->lo
[row
] = -dInfinity
;
588 // calculate joint velocity
591 vel
= dDOT(joint
->node
[0].body
->avel
,ax1
);
592 if (joint
->node
[1].body
)
593 vel
-= dDOT(joint
->node
[1].body
->avel
,ax1
);
596 vel
= dDOT(joint
->node
[0].body
->lvel
,ax1
);
597 if (joint
->node
[1].body
)
598 vel
-= dDOT(joint
->node
[1].body
->lvel
,ax1
);
601 // only apply bounce if the velocity is incoming, and if the
602 // resulting c[] exceeds what we already have.
606 dReal newc
= -bounce
* vel
;
607 if (newc
> info
->c
[row
]) info
->c
[row
] = newc
;
611 // high limit - all those computations are reversed
613 dReal newc
= -bounce
* vel
;
614 if (newc
< info
->c
[row
]) info
->c
[row
] = newc
;
625 //****************************************************************************
628 static void ballInit (dxJointBall
*j
)
630 dSetZero (j
->anchor1
,4);
631 dSetZero (j
->anchor2
,4);
632 j
->erp
= j
->world
->global_erp
;
633 j
->cfm
= j
->world
->global_cfm
;
637 static void ballGetInfo1 (dxJointBall
*j
, dxJoint::Info1
*info
)
644 static void ballGetInfo2 (dxJointBall
*joint
, dxJoint::Info2
*info
)
646 info
->erp
= joint
->erp
;
647 info
->cfm
[0] = joint
->cfm
;
648 info
->cfm
[1] = joint
->cfm
;
649 info
->cfm
[2] = joint
->cfm
;
650 setBall (joint
,info
,joint
->anchor1
,joint
->anchor2
);
654 void dJointSetBallAnchor (dJointID j
, dReal x
, dReal y
, dReal z
)
656 dxJointBall
* joint
= (dxJointBall
*)j
;
657 dUASSERT(joint
,"bad joint argument");
658 dUASSERT(joint
->vtable
== &__dball_vtable
,"joint is not a ball");
659 setAnchors (joint
,x
,y
,z
,joint
->anchor1
,joint
->anchor2
);
663 void dJointSetBallAnchor2 (dJointID j
, dReal x
, dReal y
, dReal z
)
665 dxJointBall
* joint
= (dxJointBall
*)j
;
666 dUASSERT(joint
,"bad joint argument");
667 dUASSERT(joint
->vtable
== &__dball_vtable
,"joint is not a ball");
668 joint
->anchor2
[0] = x
;
669 joint
->anchor2
[1] = y
;
670 joint
->anchor2
[2] = z
;
671 joint
->anchor2
[3] = 0;
675 void dJointGetBallAnchor (dJointID j
, dVector3 result
)
677 dxJointBall
* joint
= (dxJointBall
*)j
;
678 dUASSERT(joint
,"bad joint argument");
679 dUASSERT(result
,"bad result argument");
680 dUASSERT(joint
->vtable
== &__dball_vtable
,"joint is not a ball");
681 if (joint
->flags
& dJOINT_REVERSE
)
682 getAnchor2 (joint
,result
,joint
->anchor2
);
684 getAnchor (joint
,result
,joint
->anchor1
);
688 void dJointGetBallAnchor2 (dJointID j
, dVector3 result
)
690 dxJointBall
* joint
= (dxJointBall
*)j
;
691 dUASSERT(joint
,"bad joint argument");
692 dUASSERT(result
,"bad result argument");
693 dUASSERT(joint
->vtable
== &__dball_vtable
,"joint is not a ball");
694 if (joint
->flags
& dJOINT_REVERSE
)
695 getAnchor (joint
,result
,joint
->anchor1
);
697 getAnchor2 (joint
,result
,joint
->anchor2
);
701 void dxJointBall::set (int num
, dReal value
)
714 dReal
dxJointBall::get (int num
)
727 void dJointSetBallParam (dJointID j
, int parameter
, dReal value
)
729 dxJointBall
* joint
= (dxJointBall
*)j
;
730 dUASSERT(joint
,"bad joint argument");
731 dUASSERT(joint
->vtable
== &__dball_vtable
,"joint is not a ball joint");
732 joint
->set (parameter
,value
);
736 dReal
dJointGetBallParam (dJointID j
, int parameter
)
738 dxJointBall
* joint
= (dxJointBall
*)j
;
739 dUASSERT(joint
,"bad joint argument");
740 dUASSERT(joint
->vtable
== &__dball_vtable
,"joint is not a ball joint");
741 return joint
->get (parameter
);
745 dxJoint::Vtable __dball_vtable
= {
747 (dxJoint::init_fn
*) ballInit
,
748 (dxJoint::getInfo1_fn
*) ballGetInfo1
,
749 (dxJoint::getInfo2_fn
*) ballGetInfo2
,
752 //****************************************************************************
755 static void hingeInit (dxJointHinge
*j
)
757 dSetZero (j
->anchor1
,4);
758 dSetZero (j
->anchor2
,4);
759 dSetZero (j
->axis1
,4);
761 dSetZero (j
->axis2
,4);
763 dSetZero (j
->qrel
,4);
764 j
->limot
.init (j
->world
);
768 static void hingeGetInfo1 (dxJointHinge
*j
, dxJoint::Info1
*info
)
772 // see if joint is powered
773 if (j
->limot
.fmax
> 0)
774 info
->m
= 6; // powered hinge needs an extra constraint row
777 // see if we're at a joint limit.
778 if ((j
->limot
.lostop
>= -M_PI
|| j
->limot
.histop
<= M_PI
) &&
779 j
->limot
.lostop
<= j
->limot
.histop
) {
780 dReal angle
= getHingeAngle (j
->node
[0].body
,j
->node
[1].body
,j
->axis1
,
782 if (j
->limot
.testRotationalLimit (angle
)) info
->m
= 6;
787 static void hingeGetInfo2 (dxJointHinge
*joint
, dxJoint::Info2
*info
)
789 // set the three ball-and-socket rows
790 setBall (joint
,info
,joint
->anchor1
,joint
->anchor2
);
792 // set the two hinge rows. the hinge axis should be the only unconstrained
793 // rotational axis, the angular velocity of the two bodies perpendicular to
794 // the hinge axis should be equal. thus the constraint equations are
797 // where p and q are unit vectors normal to the hinge axis, and w1 and w2
798 // are the angular velocity vectors of the two bodies.
800 dVector3 ax1
; // length 1 joint axis in global coordinates, from 1st body
801 dVector3 p
,q
; // plane space vectors for ax1
802 dMULTIPLY0_331 (ax1
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
803 dPlaneSpace (ax1
,p
,q
);
805 int s3
=3*info
->rowskip
;
806 int s4
=4*info
->rowskip
;
808 info
->J1a
[s3
+0] = p
[0];
809 info
->J1a
[s3
+1] = p
[1];
810 info
->J1a
[s3
+2] = p
[2];
811 info
->J1a
[s4
+0] = q
[0];
812 info
->J1a
[s4
+1] = q
[1];
813 info
->J1a
[s4
+2] = q
[2];
815 if (joint
->node
[1].body
) {
816 info
->J2a
[s3
+0] = -p
[0];
817 info
->J2a
[s3
+1] = -p
[1];
818 info
->J2a
[s3
+2] = -p
[2];
819 info
->J2a
[s4
+0] = -q
[0];
820 info
->J2a
[s4
+1] = -q
[1];
821 info
->J2a
[s4
+2] = -q
[2];
824 // compute the right hand side of the constraint equation. set relative
825 // body velocities along p and q to bring the hinge back into alignment.
826 // if ax1,ax2 are the unit length hinge axes as computed from body1 and
827 // body2, we need to rotate both bodies along the axis u = (ax1 x ax2).
828 // if `theta' is the angle between ax1 and ax2, we need an angular velocity
829 // along u to cover angle erp*theta in one step :
830 // |angular_velocity| = angle/time = erp*theta / stepsize
831 // = (erp*fps) * theta
832 // angular_velocity = |angular_velocity| * (ax1 x ax2) / |ax1 x ax2|
833 // = (erp*fps) * theta * (ax1 x ax2) / sin(theta)
834 // ...as ax1 and ax2 are unit length. if theta is smallish,
835 // theta ~= sin(theta), so
836 // angular_velocity = (erp*fps) * (ax1 x ax2)
837 // ax1 x ax2 is in the plane space of ax1, so we project the angular
838 // velocity to p and q to find the right hand side.
841 if (joint
->node
[1].body
) {
842 dMULTIPLY0_331 (ax2
,joint
->node
[1].body
->posr
.R
,joint
->axis2
);
845 ax2
[0] = joint
->axis2
[0];
846 ax2
[1] = joint
->axis2
[1];
847 ax2
[2] = joint
->axis2
[2];
849 dCROSS (b
,=,ax1
,ax2
);
850 dReal k
= info
->fps
* info
->erp
;
851 info
->c
[3] = k
* dDOT(b
,p
);
852 info
->c
[4] = k
* dDOT(b
,q
);
854 // if the hinge is powered, or has joint limits, add in the stuff
855 joint
->limot
.addLimot (joint
,info
,5,ax1
,1);
859 // compute initial relative rotation body1 -> body2, or env -> body1
861 static void hingeComputeInitialRelativeRotation (dxJointHinge
*joint
)
863 if (joint
->node
[0].body
) {
864 if (joint
->node
[1].body
) {
865 dQMultiply1 (joint
->qrel
,joint
->node
[0].body
->q
,joint
->node
[1].body
->q
);
868 // set joint->qrel to the transpose of the first body q
869 joint
->qrel
[0] = joint
->node
[0].body
->q
[0];
870 for (int i
=1; i
<4; i
++) joint
->qrel
[i
] = -joint
->node
[0].body
->q
[i
];
876 void dJointSetHingeAnchor (dJointID j
, dReal x
, dReal y
, dReal z
)
878 dxJointHinge
* joint
= (dxJointHinge
*)j
;
879 dUASSERT(joint
,"bad joint argument");
880 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a hinge");
881 setAnchors (joint
,x
,y
,z
,joint
->anchor1
,joint
->anchor2
);
882 hingeComputeInitialRelativeRotation (joint
);
886 void dJointSetHingeAnchorDelta (dJointID j
, dReal x
, dReal y
, dReal z
, dReal dx
, dReal dy
, dReal dz
)
888 dxJointHinge
* joint
= (dxJointHinge
*)j
;
889 dUASSERT(joint
,"bad joint argument");
890 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a hinge");
892 if (joint
->node
[0].body
) {
894 q
[0] = x
- joint
->node
[0].body
->posr
.pos
[0];
895 q
[1] = y
- joint
->node
[0].body
->posr
.pos
[1];
896 q
[2] = z
- joint
->node
[0].body
->posr
.pos
[2];
898 dMULTIPLY1_331 (joint
->anchor1
,joint
->node
[0].body
->posr
.R
,q
);
900 if (joint
->node
[1].body
) {
901 q
[0] = x
- joint
->node
[1].body
->posr
.pos
[0];
902 q
[1] = y
- joint
->node
[1].body
->posr
.pos
[1];
903 q
[2] = z
- joint
->node
[1].body
->posr
.pos
[2];
905 dMULTIPLY1_331 (joint
->anchor2
,joint
->node
[1].body
->posr
.R
,q
);
908 // Move the relative displacement between the passive body and the
909 // anchor in the same direction as the passive body has just moved
910 joint
->anchor2
[0] = x
+ dx
;
911 joint
->anchor2
[1] = y
+ dy
;
912 joint
->anchor2
[2] = z
+ dz
;
915 joint
->anchor1
[3] = 0;
916 joint
->anchor2
[3] = 0;
918 hingeComputeInitialRelativeRotation (joint
);
923 void dJointSetHingeAxis (dJointID j
, dReal x
, dReal y
, dReal z
)
925 dxJointHinge
* joint
= (dxJointHinge
*)j
;
926 dUASSERT(joint
,"bad joint argument");
927 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a hinge");
928 setAxes (joint
,x
,y
,z
,joint
->axis1
,joint
->axis2
);
929 hingeComputeInitialRelativeRotation (joint
);
933 void dJointGetHingeAnchor (dJointID j
, dVector3 result
)
935 dxJointHinge
* joint
= (dxJointHinge
*)j
;
936 dUASSERT(joint
,"bad joint argument");
937 dUASSERT(result
,"bad result argument");
938 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a hinge");
939 if (joint
->flags
& dJOINT_REVERSE
)
940 getAnchor2 (joint
,result
,joint
->anchor2
);
942 getAnchor (joint
,result
,joint
->anchor1
);
946 void dJointGetHingeAnchor2 (dJointID j
, dVector3 result
)
948 dxJointHinge
* joint
= (dxJointHinge
*)j
;
949 dUASSERT(joint
,"bad joint argument");
950 dUASSERT(result
,"bad result argument");
951 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a hinge");
952 if (joint
->flags
& dJOINT_REVERSE
)
953 getAnchor (joint
,result
,joint
->anchor1
);
955 getAnchor2 (joint
,result
,joint
->anchor2
);
959 void dJointGetHingeAxis (dJointID j
, dVector3 result
)
961 dxJointHinge
* joint
= (dxJointHinge
*)j
;
962 dUASSERT(joint
,"bad joint argument");
963 dUASSERT(result
,"bad result argument");
964 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a hinge");
965 getAxis (joint
,result
,joint
->axis1
);
969 void dJointSetHingeParam (dJointID j
, int parameter
, dReal value
)
971 dxJointHinge
* joint
= (dxJointHinge
*)j
;
972 dUASSERT(joint
,"bad joint argument");
973 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a hinge");
974 joint
->limot
.set (parameter
,value
);
978 dReal
dJointGetHingeParam (dJointID j
, int parameter
)
980 dxJointHinge
* joint
= (dxJointHinge
*)j
;
981 dUASSERT(joint
,"bad joint argument");
982 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a hinge");
983 return joint
->limot
.get (parameter
);
987 dReal
dJointGetHingeAngle (dJointID j
)
989 dxJointHinge
* joint
= (dxJointHinge
*)j
;
991 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a hinge");
992 if (joint
->node
[0].body
) {
993 dReal ang
= getHingeAngle (joint
->node
[0].body
,joint
->node
[1].body
,joint
->axis1
,
995 if (joint
->flags
& dJOINT_REVERSE
)
1004 dReal
dJointGetHingeAngleRate (dJointID j
)
1006 dxJointHinge
* joint
= (dxJointHinge
*)j
;
1008 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a Hinge");
1009 if (joint
->node
[0].body
) {
1011 dMULTIPLY0_331 (axis
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
1012 dReal rate
= dDOT(axis
,joint
->node
[0].body
->avel
);
1013 if (joint
->node
[1].body
) rate
-= dDOT(axis
,joint
->node
[1].body
->avel
);
1014 if (joint
->flags
& dJOINT_REVERSE
) rate
= - rate
;
1021 void dJointAddHingeTorque (dJointID j
, dReal torque
)
1023 dxJointHinge
* joint
= (dxJointHinge
*)j
;
1026 dUASSERT(joint
->vtable
== &__dhinge_vtable
,"joint is not a Hinge");
1028 if (joint
->flags
& dJOINT_REVERSE
)
1031 getAxis (joint
,axis
,joint
->axis1
);
1036 if (joint
->node
[0].body
!= 0)
1037 dBodyAddTorque (joint
->node
[0].body
, axis
[0], axis
[1], axis
[2]);
1038 if (joint
->node
[1].body
!= 0)
1039 dBodyAddTorque(joint
->node
[1].body
, -axis
[0], -axis
[1], -axis
[2]);
1043 dxJoint::Vtable __dhinge_vtable
= {
1044 sizeof(dxJointHinge
),
1045 (dxJoint::init_fn
*) hingeInit
,
1046 (dxJoint::getInfo1_fn
*) hingeGetInfo1
,
1047 (dxJoint::getInfo2_fn
*) hingeGetInfo2
,
1050 //****************************************************************************
1053 static void sliderInit (dxJointSlider
*j
)
1055 dSetZero (j
->axis1
,4);
1057 dSetZero (j
->qrel
,4);
1058 dSetZero (j
->offset
,4);
1059 j
->limot
.init (j
->world
);
1063 dReal
dJointGetSliderPosition (dJointID j
)
1065 dxJointSlider
* joint
= (dxJointSlider
*)j
;
1066 dUASSERT(joint
,"bad joint argument");
1067 dUASSERT(joint
->vtable
== &__dslider_vtable
,"joint is not a slider");
1069 // get axis1 in global coordinates
1071 dMULTIPLY0_331 (ax1
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
1073 if (joint
->node
[1].body
) {
1074 // get body2 + offset point in global coordinates
1075 dMULTIPLY0_331 (q
,joint
->node
[1].body
->posr
.R
,joint
->offset
);
1076 for (int i
=0; i
<3; i
++) q
[i
] = joint
->node
[0].body
->posr
.pos
[i
] - q
[i
] -
1077 joint
->node
[1].body
->posr
.pos
[i
];
1080 for (int i
=0; i
<3; i
++) q
[i
] = joint
->node
[0].body
->posr
.pos
[i
] -
1088 dReal
dJointGetSliderPositionRate (dJointID j
)
1090 dxJointSlider
* joint
= (dxJointSlider
*)j
;
1091 dUASSERT(joint
,"bad joint argument");
1092 dUASSERT(joint
->vtable
== &__dslider_vtable
,"joint is not a slider");
1094 // get axis1 in global coordinates
1096 dMULTIPLY0_331 (ax1
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
1098 if (joint
->node
[1].body
) {
1099 return dDOT(ax1
,joint
->node
[0].body
->lvel
) -
1100 dDOT(ax1
,joint
->node
[1].body
->lvel
);
1103 return dDOT(ax1
,joint
->node
[0].body
->lvel
);
1108 static void sliderGetInfo1 (dxJointSlider
*j
, dxJoint::Info1
*info
)
1112 // see if joint is powered
1113 if (j
->limot
.fmax
> 0)
1114 info
->m
= 6; // powered slider needs an extra constraint row
1117 // see if we're at a joint limit.
1119 if ((j
->limot
.lostop
> -dInfinity
|| j
->limot
.histop
< dInfinity
) &&
1120 j
->limot
.lostop
<= j
->limot
.histop
) {
1121 // measure joint position
1122 dReal pos
= dJointGetSliderPosition (j
);
1123 if (pos
<= j
->limot
.lostop
) {
1125 j
->limot
.limit_err
= pos
- j
->limot
.lostop
;
1128 else if (pos
>= j
->limot
.histop
) {
1130 j
->limot
.limit_err
= pos
- j
->limot
.histop
;
1137 static void sliderGetInfo2 (dxJointSlider
*joint
, dxJoint::Info2
*info
)
1139 int i
,s
= info
->rowskip
;
1142 // pull out pos and R for both bodies. also get the `connection'
1143 // vector pos2-pos1.
1145 dReal
*pos1
,*pos2
,*R1
,*R2
;
1147 pos1
= joint
->node
[0].body
->posr
.pos
;
1148 R1
= joint
->node
[0].body
->posr
.R
;
1149 if (joint
->node
[1].body
) {
1150 pos2
= joint
->node
[1].body
->posr
.pos
;
1151 R2
= joint
->node
[1].body
->posr
.R
;
1152 for (i
=0; i
<3; i
++) c
[i
] = pos2
[i
] - pos1
[i
];
1159 // 3 rows to make body rotations equal
1160 setFixedOrientation(joint
, info
, joint
->qrel
, 0);
1162 // remaining two rows. we want: vel2 = vel1 + w1 x c ... but this would
1163 // result in three equations, so we project along the planespace vectors
1164 // so that sliding along the slider axis is disregarded. for symmetry we
1165 // also substitute (w1+w2)/2 for w1, as w1 is supposed to equal w2.
1167 dVector3 ax1
; // joint axis in global coordinates (unit length)
1168 dVector3 p
,q
; // plane space of ax1
1169 dMULTIPLY0_331 (ax1
,R1
,joint
->axis1
);
1170 dPlaneSpace (ax1
,p
,q
);
1171 if (joint
->node
[1].body
) {
1173 dCROSS (tmp
, = REAL(0.5) * ,c
,p
);
1174 for (i
=0; i
<3; i
++) info
->J1a
[s3
+i
] = tmp
[i
];
1175 for (i
=0; i
<3; i
++) info
->J2a
[s3
+i
] = tmp
[i
];
1176 dCROSS (tmp
, = REAL(0.5) * ,c
,q
);
1177 for (i
=0; i
<3; i
++) info
->J1a
[s4
+i
] = tmp
[i
];
1178 for (i
=0; i
<3; i
++) info
->J2a
[s4
+i
] = tmp
[i
];
1179 for (i
=0; i
<3; i
++) info
->J2l
[s3
+i
] = -p
[i
];
1180 for (i
=0; i
<3; i
++) info
->J2l
[s4
+i
] = -q
[i
];
1182 for (i
=0; i
<3; i
++) info
->J1l
[s3
+i
] = p
[i
];
1183 for (i
=0; i
<3; i
++) info
->J1l
[s4
+i
] = q
[i
];
1185 // compute last two elements of right hand side. we want to align the offset
1186 // point (in body 2's frame) with the center of body 1.
1187 dReal k
= info
->fps
* info
->erp
;
1188 if (joint
->node
[1].body
) {
1189 dVector3 ofs
; // offset point in global coordinates
1190 dMULTIPLY0_331 (ofs
,R2
,joint
->offset
);
1191 for (i
=0; i
<3; i
++) c
[i
] += ofs
[i
];
1192 info
->c
[3] = k
* dDOT(p
,c
);
1193 info
->c
[4] = k
* dDOT(q
,c
);
1196 dVector3 ofs
; // offset point in global coordinates
1197 for (i
=0; i
<3; i
++) ofs
[i
] = joint
->offset
[i
] - pos1
[i
];
1198 info
->c
[3] = k
* dDOT(p
,ofs
);
1199 info
->c
[4] = k
* dDOT(q
,ofs
);
1202 // if the slider is powered, or has joint limits, add in the extra row
1203 joint
->limot
.addLimot (joint
,info
,5,ax1
,0);
1207 void dJointSetSliderAxis (dJointID j
, dReal x
, dReal y
, dReal z
)
1209 dxJointSlider
* joint
= (dxJointSlider
*)j
;
1211 dUASSERT(joint
,"bad joint argument");
1212 dUASSERT(joint
->vtable
== &__dslider_vtable
,"joint is not a slider");
1213 setAxes (joint
,x
,y
,z
,joint
->axis1
,0);
1215 // compute initial relative rotation body1 -> body2, or env -> body1
1216 // also compute center of body1 w.r.t body 2
1217 if (joint
->node
[1].body
) {
1218 dQMultiply1 (joint
->qrel
,joint
->node
[0].body
->q
,joint
->node
[1].body
->q
);
1221 c
[i
] = joint
->node
[0].body
->posr
.pos
[i
] - joint
->node
[1].body
->posr
.pos
[i
];
1222 dMULTIPLY1_331 (joint
->offset
,joint
->node
[1].body
->posr
.R
,c
);
1225 // set joint->qrel to the transpose of the first body's q
1226 joint
->qrel
[0] = joint
->node
[0].body
->q
[0];
1227 for (i
=1; i
<4; i
++) joint
->qrel
[i
] = -joint
->node
[0].body
->q
[i
];
1228 for (i
=0; i
<3; i
++) joint
->offset
[i
] = joint
->node
[0].body
->posr
.pos
[i
];
1233 void dJointSetSliderAxisDelta (dJointID j
, dReal x
, dReal y
, dReal z
, dReal dx
, dReal dy
, dReal dz
)
1235 dxJointSlider
* joint
= (dxJointSlider
*)j
;
1237 dUASSERT(joint
,"bad joint argument");
1238 dUASSERT(joint
->vtable
== &__dslider_vtable
,"joint is not a slider");
1239 setAxes (joint
,x
,y
,z
,joint
->axis1
,0);
1241 // compute initial relative rotation body1 -> body2, or env -> body1
1242 // also compute center of body1 w.r.t body 2
1243 if (joint
->node
[1].body
) {
1244 dQMultiply1 (joint
->qrel
,joint
->node
[0].body
->q
,joint
->node
[1].body
->q
);
1247 c
[i
] = joint
->node
[0].body
->posr
.pos
[i
] - joint
->node
[1].body
->posr
.pos
[i
];
1248 dMULTIPLY1_331 (joint
->offset
,joint
->node
[1].body
->posr
.R
,c
);
1251 // set joint->qrel to the transpose of the first body's q
1252 joint
->qrel
[0] = joint
->node
[0].body
->q
[0];
1255 joint
->qrel
[i
] = -joint
->node
[0].body
->q
[i
];
1257 joint
->offset
[0] = joint
->node
[0].body
->posr
.pos
[0] + dx
;
1258 joint
->offset
[1] = joint
->node
[0].body
->posr
.pos
[1] + dy
;
1259 joint
->offset
[2] = joint
->node
[0].body
->posr
.pos
[2] + dz
;
1265 void dJointGetSliderAxis (dJointID j
, dVector3 result
)
1267 dxJointSlider
* joint
= (dxJointSlider
*)j
;
1268 dUASSERT(joint
,"bad joint argument");
1269 dUASSERT(result
,"bad result argument");
1270 dUASSERT(joint
->vtable
== &__dslider_vtable
,"joint is not a slider");
1271 getAxis (joint
,result
,joint
->axis1
);
1275 void dJointSetSliderParam (dJointID j
, int parameter
, dReal value
)
1277 dxJointSlider
* joint
= (dxJointSlider
*)j
;
1278 dUASSERT(joint
,"bad joint argument");
1279 dUASSERT(joint
->vtable
== &__dslider_vtable
,"joint is not a slider");
1280 joint
->limot
.set (parameter
,value
);
1284 dReal
dJointGetSliderParam (dJointID j
, int parameter
)
1286 dxJointSlider
* joint
= (dxJointSlider
*)j
;
1287 dUASSERT(joint
,"bad joint argument");
1288 dUASSERT(joint
->vtable
== &__dslider_vtable
,"joint is not a slider");
1289 return joint
->limot
.get (parameter
);
1293 void dJointAddSliderForce (dJointID j
, dReal force
)
1295 dxJointSlider
* joint
= (dxJointSlider
*)j
;
1297 dUASSERT(joint
,"bad joint argument");
1298 dUASSERT(joint
->vtable
== &__dslider_vtable
,"joint is not a slider");
1300 if (joint
->flags
& dJOINT_REVERSE
)
1303 getAxis (joint
,axis
,joint
->axis1
);
1308 if (joint
->node
[0].body
!= 0)
1309 dBodyAddForce (joint
->node
[0].body
,axis
[0],axis
[1],axis
[2]);
1310 if (joint
->node
[1].body
!= 0)
1311 dBodyAddForce(joint
->node
[1].body
, -axis
[0], -axis
[1], -axis
[2]);
1313 if (joint
->node
[0].body
!= 0 && joint
->node
[1].body
!= 0) {
1314 // linear torque decoupling:
1315 // we have to compensate the torque, that this slider force may generate
1316 // if body centers are not aligned along the slider axis
1318 dVector3 ltd
; // Linear Torque Decoupling vector (a torque)
1321 c
[0]=REAL(0.5)*(joint
->node
[1].body
->posr
.pos
[0]-joint
->node
[0].body
->posr
.pos
[0]);
1322 c
[1]=REAL(0.5)*(joint
->node
[1].body
->posr
.pos
[1]-joint
->node
[0].body
->posr
.pos
[1]);
1323 c
[2]=REAL(0.5)*(joint
->node
[1].body
->posr
.pos
[2]-joint
->node
[0].body
->posr
.pos
[2]);
1324 dCROSS (ltd
,=,c
,axis
);
1326 dBodyAddTorque (joint
->node
[0].body
,ltd
[0],ltd
[1], ltd
[2]);
1327 dBodyAddTorque (joint
->node
[1].body
,ltd
[0],ltd
[1], ltd
[2]);
1332 dxJoint::Vtable __dslider_vtable
= {
1333 sizeof(dxJointSlider
),
1334 (dxJoint::init_fn
*) sliderInit
,
1335 (dxJoint::getInfo1_fn
*) sliderGetInfo1
,
1336 (dxJoint::getInfo2_fn
*) sliderGetInfo2
,
1339 //****************************************************************************
1342 static void contactInit (dxJointContact
*j
)
1344 // default frictionless contact. hmmm, this info gets overwritten straight
1345 // away anyway, so why bother?
1346 #if 0 /* so don't bother ;) */
1347 j
->contact
.surface
.mode
= 0;
1348 j
->contact
.surface
.mu
= 0;
1349 dSetZero (j
->contact
.geom
.pos
,4);
1350 dSetZero (j
->contact
.geom
.normal
,4);
1351 j
->contact
.geom
.depth
= 0;
1356 static void contactGetInfo1 (dxJointContact
*j
, dxJoint::Info1
*info
)
1358 // make sure mu's >= 0, then calculate number of constraint rows and number
1359 // of unbounded rows.
1361 if (j
->contact
.surface
.mu
< 0) j
->contact
.surface
.mu
= 0;
1362 if (j
->contact
.surface
.mode
& dContactMu2
) {
1363 if (j
->contact
.surface
.mu
> 0) m
++;
1364 if (j
->contact
.surface
.mu2
< 0) j
->contact
.surface
.mu2
= 0;
1365 if (j
->contact
.surface
.mu2
> 0) m
++;
1366 if (j
->contact
.surface
.mu
== dInfinity
) nub
++;
1367 if (j
->contact
.surface
.mu2
== dInfinity
) nub
++;
1370 if (j
->contact
.surface
.mu
> 0) m
+= 2;
1371 if (j
->contact
.surface
.mu
== dInfinity
) nub
+= 2;
1380 static void contactGetInfo2 (dxJointContact
*j
, dxJoint::Info2
*info
)
1382 int s
= info
->rowskip
;
1385 // get normal, with sign adjusted for body1/body2 polarity
1387 if (j
->flags
& dJOINT_REVERSE
) {
1388 normal
[0] = - j
->contact
.geom
.normal
[0];
1389 normal
[1] = - j
->contact
.geom
.normal
[1];
1390 normal
[2] = - j
->contact
.geom
.normal
[2];
1393 normal
[0] = j
->contact
.geom
.normal
[0];
1394 normal
[1] = j
->contact
.geom
.normal
[1];
1395 normal
[2] = j
->contact
.geom
.normal
[2];
1397 normal
[3] = 0; // @@@ hmmm
1399 // c1,c2 = contact points with respect to body PORs
1401 c1
[0] = j
->contact
.geom
.pos
[0] - j
->node
[0].body
->posr
.pos
[0];
1402 c1
[1] = j
->contact
.geom
.pos
[1] - j
->node
[0].body
->posr
.pos
[1];
1403 c1
[2] = j
->contact
.geom
.pos
[2] - j
->node
[0].body
->posr
.pos
[2];
1405 // set jacobian for normal
1406 info
->J1l
[0] = normal
[0];
1407 info
->J1l
[1] = normal
[1];
1408 info
->J1l
[2] = normal
[2];
1409 dCROSS (info
->J1a
,=,c1
,normal
);
1410 if (j
->node
[1].body
) {
1411 c2
[0] = j
->contact
.geom
.pos
[0] - j
->node
[1].body
->posr
.pos
[0];
1412 c2
[1] = j
->contact
.geom
.pos
[1] - j
->node
[1].body
->posr
.pos
[1];
1413 c2
[2] = j
->contact
.geom
.pos
[2] - j
->node
[1].body
->posr
.pos
[2];
1414 info
->J2l
[0] = -normal
[0];
1415 info
->J2l
[1] = -normal
[1];
1416 info
->J2l
[2] = -normal
[2];
1417 dCROSS (info
->J2a
,= -,c2
,normal
);
1420 // set right hand side and cfm value for normal
1421 dReal erp
= info
->erp
;
1422 if (j
->contact
.surface
.mode
& dContactSoftERP
)
1423 erp
= j
->contact
.surface
.soft_erp
;
1424 dReal k
= info
->fps
* erp
;
1425 dReal depth
= j
->contact
.geom
.depth
- j
->world
->contactp
.min_depth
;
1426 if (depth
< 0) depth
= 0;
1428 const dReal maxvel
= j
->world
->contactp
.max_vel
;
1429 info
->c
[0] = k
*depth
;
1430 if (info
->c
[0] > maxvel
)
1431 info
->c
[0] = maxvel
;
1433 if (j
->contact
.surface
.mode
& dContactSoftCFM
)
1434 info
->cfm
[0] = j
->contact
.surface
.soft_cfm
;
1437 if (j
->contact
.surface
.mode
& dContactBounce
) {
1438 // calculate outgoing velocity (-ve for incoming contact)
1439 dReal outgoing
= dDOT(info
->J1l
,j
->node
[0].body
->lvel
) +
1440 dDOT(info
->J1a
,j
->node
[0].body
->avel
);
1441 if (j
->node
[1].body
) {
1442 outgoing
+= dDOT(info
->J2l
,j
->node
[1].body
->lvel
) +
1443 dDOT(info
->J2a
,j
->node
[1].body
->avel
);
1445 // only apply bounce if the outgoing velocity is greater than the
1446 // threshold, and if the resulting c[0] exceeds what we already have.
1447 if (j
->contact
.surface
.bounce_vel
>= 0 &&
1448 (-outgoing
) > j
->contact
.surface
.bounce_vel
) {
1449 dReal newc
= - j
->contact
.surface
.bounce
* outgoing
;
1450 if (newc
> info
->c
[0]) info
->c
[0] = newc
;
1454 // set LCP limits for normal
1456 info
->hi
[0] = dInfinity
;
1458 // now do jacobian for tangential forces
1459 dVector3 t1
,t2
; // two vectors tangential to normal
1461 // first friction direction
1462 if (j
->the_m
>= 2) {
1463 if (j
->contact
.surface
.mode
& dContactFDir1
) { // use fdir1 ?
1464 t1
[0] = j
->contact
.fdir1
[0];
1465 t1
[1] = j
->contact
.fdir1
[1];
1466 t1
[2] = j
->contact
.fdir1
[2];
1467 dCROSS (t2
,=,normal
,t1
);
1470 dPlaneSpace (normal
,t1
,t2
);
1472 info
->J1l
[s
+0] = t1
[0];
1473 info
->J1l
[s
+1] = t1
[1];
1474 info
->J1l
[s
+2] = t1
[2];
1475 dCROSS (info
->J1a
+s
,=,c1
,t1
);
1476 if (j
->node
[1].body
) {
1477 info
->J2l
[s
+0] = -t1
[0];
1478 info
->J2l
[s
+1] = -t1
[1];
1479 info
->J2l
[s
+2] = -t1
[2];
1480 dCROSS (info
->J2a
+s
,= -,c2
,t1
);
1482 // set right hand side
1483 if (j
->contact
.surface
.mode
& dContactMotion1
) {
1484 info
->c
[1] = j
->contact
.surface
.motion1
;
1486 // set LCP bounds and friction index. this depends on the approximation
1488 info
->lo
[1] = -j
->contact
.surface
.mu
;
1489 info
->hi
[1] = j
->contact
.surface
.mu
;
1490 if (j
->contact
.surface
.mode
& dContactApprox1_1
) info
->findex
[1] = 0;
1492 // set slip (constraint force mixing)
1493 if (j
->contact
.surface
.mode
& dContactSlip1
)
1494 info
->cfm
[1] = j
->contact
.surface
.slip1
;
1497 // second friction direction
1498 if (j
->the_m
>= 3) {
1499 info
->J1l
[s2
+0] = t2
[0];
1500 info
->J1l
[s2
+1] = t2
[1];
1501 info
->J1l
[s2
+2] = t2
[2];
1502 dCROSS (info
->J1a
+s2
,=,c1
,t2
);
1503 if (j
->node
[1].body
) {
1504 info
->J2l
[s2
+0] = -t2
[0];
1505 info
->J2l
[s2
+1] = -t2
[1];
1506 info
->J2l
[s2
+2] = -t2
[2];
1507 dCROSS (info
->J2a
+s2
,= -,c2
,t2
);
1509 // set right hand side
1510 if (j
->contact
.surface
.mode
& dContactMotion2
) {
1511 info
->c
[2] = j
->contact
.surface
.motion2
;
1513 // set LCP bounds and friction index. this depends on the approximation
1515 if (j
->contact
.surface
.mode
& dContactMu2
) {
1516 info
->lo
[2] = -j
->contact
.surface
.mu2
;
1517 info
->hi
[2] = j
->contact
.surface
.mu2
;
1520 info
->lo
[2] = -j
->contact
.surface
.mu
;
1521 info
->hi
[2] = j
->contact
.surface
.mu
;
1523 if (j
->contact
.surface
.mode
& dContactApprox1_2
) info
->findex
[2] = 0;
1525 // set slip (constraint force mixing)
1526 if (j
->contact
.surface
.mode
& dContactSlip2
)
1527 info
->cfm
[2] = j
->contact
.surface
.slip2
;
1532 dxJoint::Vtable __dcontact_vtable
= {
1533 sizeof(dxJointContact
),
1534 (dxJoint::init_fn
*) contactInit
,
1535 (dxJoint::getInfo1_fn
*) contactGetInfo1
,
1536 (dxJoint::getInfo2_fn
*) contactGetInfo2
,
1539 //****************************************************************************
1540 // hinge 2. note that this joint must be attached to two bodies for it to work
1542 static dReal
measureHinge2Angle (dxJointHinge2
*joint
)
1545 dMULTIPLY0_331 (a1
,joint
->node
[1].body
->posr
.R
,joint
->axis2
);
1546 dMULTIPLY1_331 (a2
,joint
->node
[0].body
->posr
.R
,a1
);
1547 dReal x
= dDOT(joint
->v1
,a2
);
1548 dReal y
= dDOT(joint
->v2
,a2
);
1549 return -dAtan2 (y
,x
);
1553 static void hinge2Init (dxJointHinge2
*j
)
1555 dSetZero (j
->anchor1
,4);
1556 dSetZero (j
->anchor2
,4);
1557 dSetZero (j
->axis1
,4);
1559 dSetZero (j
->axis2
,4);
1569 j
->limot1
.init (j
->world
);
1570 j
->limot2
.init (j
->world
);
1572 j
->susp_erp
= j
->world
->global_erp
;
1573 j
->susp_cfm
= j
->world
->global_cfm
;
1575 j
->flags
|= dJOINT_TWOBODIES
;
1579 static void hinge2GetInfo1 (dxJointHinge2
*j
, dxJoint::Info1
*info
)
1584 // see if we're powered or at a joint limit for axis 1
1585 j
->limot1
.limit
= 0;
1586 if ((j
->limot1
.lostop
>= -M_PI
|| j
->limot1
.histop
<= M_PI
) &&
1587 j
->limot1
.lostop
<= j
->limot1
.histop
) {
1588 dReal angle
= measureHinge2Angle (j
);
1589 j
->limot1
.testRotationalLimit (angle
);
1591 if (j
->limot1
.limit
|| j
->limot1
.fmax
> 0) info
->m
++;
1593 // see if we're powering axis 2 (we currently never limit this axis)
1594 j
->limot2
.limit
= 0;
1595 if (j
->limot2
.fmax
> 0) info
->m
++;
1599 // macro that computes ax1,ax2 = axis 1 and 2 in global coordinates (they are
1600 // relative to body 1 and 2 initially) and then computes the constrained
1601 // rotational axis as the cross product of ax1 and ax2.
1602 // the sin and cos of the angle between axis 1 and 2 is computed, this comes
1603 // from dot and cross product rules.
1605 #define HINGE2_GET_AXIS_INFO(axis,sin_angle,cos_angle) \
1607 dMULTIPLY0_331 (ax1,joint->node[0].body->posr.R,joint->axis1); \
1608 dMULTIPLY0_331 (ax2,joint->node[1].body->posr.R,joint->axis2); \
1609 dCROSS (axis,=,ax1,ax2); \
1610 sin_angle = dSqrt (axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]); \
1611 cos_angle = dDOT (ax1,ax2);
1614 static void hinge2GetInfo2 (dxJointHinge2
*joint
, dxJoint::Info2
*info
)
1616 // get information we need to set the hinge row
1619 HINGE2_GET_AXIS_INFO (q
,s
,c
);
1620 dNormalize3 (q
); // @@@ quicker: divide q by s ?
1622 // set the three ball-and-socket rows (aligned to the suspension axis ax1)
1623 setBall2 (joint
,info
,joint
->anchor1
,joint
->anchor2
,ax1
,joint
->susp_erp
);
1625 // set the hinge row
1626 int s3
=3*info
->rowskip
;
1627 info
->J1a
[s3
+0] = q
[0];
1628 info
->J1a
[s3
+1] = q
[1];
1629 info
->J1a
[s3
+2] = q
[2];
1630 if (joint
->node
[1].body
) {
1631 info
->J2a
[s3
+0] = -q
[0];
1632 info
->J2a
[s3
+1] = -q
[1];
1633 info
->J2a
[s3
+2] = -q
[2];
1636 // compute the right hand side for the constrained rotational DOF.
1637 // axis 1 and axis 2 are separated by an angle `theta'. the desired
1638 // separation angle is theta0. sin(theta0) and cos(theta0) are recorded
1639 // in the joint structure. the correcting angular velocity is:
1640 // |angular_velocity| = angle/time = erp*(theta0-theta) / stepsize
1641 // = (erp*fps) * (theta0-theta)
1642 // (theta0-theta) can be computed using the following small-angle-difference
1644 // theta0-theta ~= tan(theta0-theta)
1645 // = sin(theta0-theta)/cos(theta0-theta)
1646 // = (c*s0 - s*c0) / (c*c0 + s*s0)
1647 // = c*s0 - s*c0 assuming c*c0 + s*s0 ~= 1
1648 // where c = cos(theta), s = sin(theta)
1649 // c0 = cos(theta0), s0 = sin(theta0)
1651 dReal k
= info
->fps
* info
->erp
;
1652 info
->c
[3] = k
* (joint
->c0
* s
- joint
->s0
* c
);
1654 // if the axis1 hinge is powered, or has joint limits, add in more stuff
1655 int row
= 4 + joint
->limot1
.addLimot (joint
,info
,4,ax1
,1);
1657 // if the axis2 hinge is powered, add in more stuff
1658 joint
->limot2
.addLimot (joint
,info
,row
,ax2
,1);
1660 // set parameter for the suspension
1661 info
->cfm
[0] = joint
->susp_cfm
;
1665 // compute vectors v1 and v2 (embedded in body1), used to measure angle
1666 // between body 1 and body 2
1668 static void makeHinge2V1andV2 (dxJointHinge2
*joint
)
1670 if (joint
->node
[0].body
) {
1671 // get axis 1 and 2 in global coords
1673 dMULTIPLY0_331 (ax1
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
1674 dMULTIPLY0_331 (ax2
,joint
->node
[1].body
->posr
.R
,joint
->axis2
);
1676 // don't do anything if the axis1 or axis2 vectors are zero or the same
1677 if ((ax1
[0]==0 && ax1
[1]==0 && ax1
[2]==0) ||
1678 (ax2
[0]==0 && ax2
[1]==0 && ax2
[2]==0) ||
1679 (ax1
[0]==ax2
[0] && ax1
[1]==ax2
[1] && ax1
[2]==ax2
[2])) return;
1681 // modify axis 2 so it's perpendicular to axis 1
1682 dReal k
= dDOT(ax1
,ax2
);
1683 for (int i
=0; i
<3; i
++) ax2
[i
] -= k
*ax1
[i
];
1686 // make v1 = modified axis2, v2 = axis1 x (modified axis2)
1687 dCROSS (v
,=,ax1
,ax2
);
1688 dMULTIPLY1_331 (joint
->v1
,joint
->node
[0].body
->posr
.R
,ax2
);
1689 dMULTIPLY1_331 (joint
->v2
,joint
->node
[0].body
->posr
.R
,v
);
1694 void dJointSetHinge2Anchor (dJointID j
, dReal x
, dReal y
, dReal z
)
1696 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1697 dUASSERT(joint
,"bad joint argument");
1698 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1699 setAnchors (joint
,x
,y
,z
,joint
->anchor1
,joint
->anchor2
);
1700 makeHinge2V1andV2 (joint
);
1704 void dJointSetHinge2Axis1 (dJointID j
, dReal x
, dReal y
, dReal z
)
1706 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1707 dUASSERT(joint
,"bad joint argument");
1708 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1709 if (joint
->node
[0].body
) {
1716 dMULTIPLY1_331 (joint
->axis1
,joint
->node
[0].body
->posr
.R
,q
);
1717 joint
->axis1
[3] = 0;
1719 // compute the sin and cos of the angle between axis 1 and axis 2
1721 HINGE2_GET_AXIS_INFO(ax
,joint
->s0
,joint
->c0
);
1723 makeHinge2V1andV2 (joint
);
1727 void dJointSetHinge2Axis2 (dJointID j
, dReal x
, dReal y
, dReal z
)
1729 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1730 dUASSERT(joint
,"bad joint argument");
1731 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1732 if (joint
->node
[1].body
) {
1739 dMULTIPLY1_331 (joint
->axis2
,joint
->node
[1].body
->posr
.R
,q
);
1740 joint
->axis1
[3] = 0;
1742 // compute the sin and cos of the angle between axis 1 and axis 2
1744 HINGE2_GET_AXIS_INFO(ax
,joint
->s0
,joint
->c0
);
1746 makeHinge2V1andV2 (joint
);
1750 void dJointSetHinge2Param (dJointID j
, int parameter
, dReal value
)
1752 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1753 dUASSERT(joint
,"bad joint argument");
1754 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1755 if ((parameter
& 0xff00) == 0x100) {
1756 joint
->limot2
.set (parameter
& 0xff,value
);
1759 if (parameter
== dParamSuspensionERP
) joint
->susp_erp
= value
;
1760 else if (parameter
== dParamSuspensionCFM
) joint
->susp_cfm
= value
;
1761 else joint
->limot1
.set (parameter
,value
);
1766 void dJointGetHinge2Anchor (dJointID j
, dVector3 result
)
1768 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1769 dUASSERT(joint
,"bad joint argument");
1770 dUASSERT(result
,"bad result argument");
1771 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1772 if (joint
->flags
& dJOINT_REVERSE
)
1773 getAnchor2 (joint
,result
,joint
->anchor2
);
1775 getAnchor (joint
,result
,joint
->anchor1
);
1779 void dJointGetHinge2Anchor2 (dJointID j
, dVector3 result
)
1781 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1782 dUASSERT(joint
,"bad joint argument");
1783 dUASSERT(result
,"bad result argument");
1784 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1785 if (joint
->flags
& dJOINT_REVERSE
)
1786 getAnchor (joint
,result
,joint
->anchor1
);
1788 getAnchor2 (joint
,result
,joint
->anchor2
);
1792 void dJointGetHinge2Axis1 (dJointID j
, dVector3 result
)
1794 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1795 dUASSERT(joint
,"bad joint argument");
1796 dUASSERT(result
,"bad result argument");
1797 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1798 if (joint
->node
[0].body
) {
1799 dMULTIPLY0_331 (result
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
1804 void dJointGetHinge2Axis2 (dJointID j
, dVector3 result
)
1806 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1807 dUASSERT(joint
,"bad joint argument");
1808 dUASSERT(result
,"bad result argument");
1809 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1810 if (joint
->node
[1].body
) {
1811 dMULTIPLY0_331 (result
,joint
->node
[1].body
->posr
.R
,joint
->axis2
);
1816 dReal
dJointGetHinge2Param (dJointID j
, int parameter
)
1818 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1819 dUASSERT(joint
,"bad joint argument");
1820 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1821 if ((parameter
& 0xff00) == 0x100) {
1822 return joint
->limot2
.get (parameter
& 0xff);
1825 if (parameter
== dParamSuspensionERP
) return joint
->susp_erp
;
1826 else if (parameter
== dParamSuspensionCFM
) return joint
->susp_cfm
;
1827 else return joint
->limot1
.get (parameter
);
1832 dReal
dJointGetHinge2Angle1 (dJointID j
)
1834 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1835 dUASSERT(joint
,"bad joint argument");
1836 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1837 if (joint
->node
[0].body
) return measureHinge2Angle (joint
);
1842 dReal
dJointGetHinge2Angle1Rate (dJointID j
)
1844 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1845 dUASSERT(joint
,"bad joint argument");
1846 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1847 if (joint
->node
[0].body
) {
1849 dMULTIPLY0_331 (axis
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
1850 dReal rate
= dDOT(axis
,joint
->node
[0].body
->avel
);
1851 if (joint
->node
[1].body
) rate
-= dDOT(axis
,joint
->node
[1].body
->avel
);
1858 dReal
dJointGetHinge2Angle2Rate (dJointID j
)
1860 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1861 dUASSERT(joint
,"bad joint argument");
1862 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1863 if (joint
->node
[0].body
&& joint
->node
[1].body
) {
1865 dMULTIPLY0_331 (axis
,joint
->node
[1].body
->posr
.R
,joint
->axis2
);
1866 dReal rate
= dDOT(axis
,joint
->node
[0].body
->avel
);
1867 if (joint
->node
[1].body
) rate
-= dDOT(axis
,joint
->node
[1].body
->avel
);
1874 void dJointAddHinge2Torques (dJointID j
, dReal torque1
, dReal torque2
)
1876 dxJointHinge2
* joint
= (dxJointHinge2
*)j
;
1877 dVector3 axis1
, axis2
;
1878 dUASSERT(joint
,"bad joint argument");
1879 dUASSERT(joint
->vtable
== &__dhinge2_vtable
,"joint is not a hinge2");
1881 if (joint
->node
[0].body
&& joint
->node
[1].body
) {
1882 dMULTIPLY0_331 (axis1
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
1883 dMULTIPLY0_331 (axis2
,joint
->node
[1].body
->posr
.R
,joint
->axis2
);
1884 axis1
[0] = axis1
[0] * torque1
+ axis2
[0] * torque2
;
1885 axis1
[1] = axis1
[1] * torque1
+ axis2
[1] * torque2
;
1886 axis1
[2] = axis1
[2] * torque1
+ axis2
[2] * torque2
;
1887 dBodyAddTorque (joint
->node
[0].body
,axis1
[0],axis1
[1],axis1
[2]);
1888 dBodyAddTorque(joint
->node
[1].body
, -axis1
[0], -axis1
[1], -axis1
[2]);
1893 dxJoint::Vtable __dhinge2_vtable
= {
1894 sizeof(dxJointHinge2
),
1895 (dxJoint::init_fn
*) hinge2Init
,
1896 (dxJoint::getInfo1_fn
*) hinge2GetInfo1
,
1897 (dxJoint::getInfo2_fn
*) hinge2GetInfo2
,
1900 //****************************************************************************
1903 // I just realized that the universal joint is equivalent to a hinge 2 joint with
1904 // perfectly stiff suspension. By comparing the hinge 2 implementation to
1905 // the universal implementation, you may be able to improve this
1906 // implementation (or, less likely, the hinge2 implementation).
1908 static void universalInit (dxJointUniversal
*j
)
1910 dSetZero (j
->anchor1
,4);
1911 dSetZero (j
->anchor2
,4);
1912 dSetZero (j
->axis1
,4);
1914 dSetZero (j
->axis2
,4);
1916 dSetZero(j
->qrel1
,4);
1917 dSetZero(j
->qrel2
,4);
1918 j
->limot1
.init (j
->world
);
1919 j
->limot2
.init (j
->world
);
1923 static void getUniversalAxes(dxJointUniversal
*joint
, dVector3 ax1
, dVector3 ax2
)
1925 // This says "ax1 = joint->node[0].body->posr.R * joint->axis1"
1926 dMULTIPLY0_331 (ax1
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
1928 if (joint
->node
[1].body
) {
1929 dMULTIPLY0_331 (ax2
,joint
->node
[1].body
->posr
.R
,joint
->axis2
);
1932 ax2
[0] = joint
->axis2
[0];
1933 ax2
[1] = joint
->axis2
[1];
1934 ax2
[2] = joint
->axis2
[2];
1938 static void getUniversalAngles(dxJointUniversal
*joint
, dReal
*angle1
, dReal
*angle2
)
1940 if (joint
->node
[0].body
)
1942 // length 1 joint axis in global coordinates, from each body
1945 dQuaternion qcross
, qq
, qrel
;
1947 getUniversalAxes (joint
,ax1
,ax2
);
1949 // It should be possible to get both angles without explicitly
1950 // constructing the rotation matrix of the cross. Basically,
1951 // orientation of the cross about axis1 comes from body 2,
1952 // about axis 2 comes from body 1, and the perpendicular
1953 // axis can come from the two bodies somehow. (We don't really
1954 // want to assume it's 90 degrees, because in general the
1955 // constraints won't be perfectly satisfied, or even very well
1958 // However, we'd need a version of getHingeAngleFromRElativeQuat()
1959 // that CAN handle when its relative quat is rotated along a direction
1960 // other than the given axis. What I have here works,
1961 // although it's probably much slower than need be.
1963 dRFrom2Axes (R
, ax1
[0], ax1
[1], ax1
[2], ax2
[0], ax2
[1], ax2
[2]);
1968 // This code is essentialy the same as getHingeAngle(), see the comments
1969 // there for details.
1971 // get qrel = relative rotation between node[0] and the cross
1972 dQMultiply1 (qq
, joint
->node
[0].body
->q
, qcross
);
1973 dQMultiply2 (qrel
, qq
, joint
->qrel1
);
1975 *angle1
= getHingeAngleFromRelativeQuat(qrel
, joint
->axis1
);
1977 // This is equivalent to
1978 // dRFrom2Axes(R, ax2[0], ax2[1], ax2[2], ax1[0], ax1[1], ax1[2]);
1979 // You see that the R is constructed from the same 2 axis as for angle1
1980 // but the first and second axis are swapped.
1981 // So we can take the first R and rapply a rotation to it.
1982 // The rotation is around the axis between the 2 axes (ax1 and ax2).
1983 // We do a rotation of 180deg.
1985 dQuaternion qcross2
;
1986 // Find the vector between ax1 and ax2 (i.e. in the middle)
1987 // We need to turn around this vector by 180deg
1989 // The 2 axes should be normalize so to find the vector between the 2.
1990 // Add and devide by 2 then normalize or simply normalize
1995 /// *------------> ax1
1996 // We want the vector a 45deg
1998 // N.B. We don't need to normalize the ax1 and ax2 since there are
1999 // normalized when we set them.
2001 // We set the quaternion q = [cos(theta), dir*sin(theta)] = [w, x, y, Z]
2002 qrel
[0] = 0; // equivalent to cos(Pi/2)
2003 qrel
[1] = ax1
[0] + ax2
[0]; // equivalent to x*sin(Pi/2); since sin(Pi/2) = 1
2004 qrel
[2] = ax1
[1] + ax2
[1];
2005 qrel
[3] = ax1
[2] + ax2
[2];
2007 dReal l
= dRecip(sqrt(qrel
[1]*qrel
[1] + qrel
[2]*qrel
[2] + qrel
[3]*qrel
[3]));
2012 dQMultiply0 (qcross2
, qrel
, qcross
);
2014 if (joint
->node
[1].body
) {
2015 dQMultiply1 (qq
, joint
->node
[1].body
->q
, qcross2
);
2016 dQMultiply2 (qrel
, qq
, joint
->qrel2
);
2019 // pretend joint->node[1].body->q is the identity
2020 dQMultiply2 (qrel
, qcross2
, joint
->qrel2
);
2023 *angle2
= - getHingeAngleFromRelativeQuat(qrel
, joint
->axis2
);
2033 static dReal
getUniversalAngle1(dxJointUniversal
*joint
)
2035 if (joint
->node
[0].body
) {
2036 // length 1 joint axis in global coordinates, from each body
2039 dQuaternion qcross
, qq
, qrel
;
2041 getUniversalAxes (joint
,ax1
,ax2
);
2043 // It should be possible to get both angles without explicitly
2044 // constructing the rotation matrix of the cross. Basically,
2045 // orientation of the cross about axis1 comes from body 2,
2046 // about axis 2 comes from body 1, and the perpendicular
2047 // axis can come from the two bodies somehow. (We don't really
2048 // want to assume it's 90 degrees, because in general the
2049 // constraints won't be perfectly satisfied, or even very well
2052 // However, we'd need a version of getHingeAngleFromRElativeQuat()
2053 // that CAN handle when its relative quat is rotated along a direction
2054 // other than the given axis. What I have here works,
2055 // although it's probably much slower than need be.
2057 dRFrom2Axes(R
, ax1
[0], ax1
[1], ax1
[2], ax2
[0], ax2
[1], ax2
[2]);
2060 // This code is essential the same as getHingeAngle(), see the comments
2061 // there for details.
2063 // get qrel = relative rotation between node[0] and the cross
2064 dQMultiply1 (qq
,joint
->node
[0].body
->q
,qcross
);
2065 dQMultiply2 (qrel
,qq
,joint
->qrel1
);
2067 return getHingeAngleFromRelativeQuat(qrel
, joint
->axis1
);
2073 static dReal
getUniversalAngle2(dxJointUniversal
*joint
)
2075 if (joint
->node
[0].body
) {
2076 // length 1 joint axis in global coordinates, from each body
2079 dQuaternion qcross
, qq
, qrel
;
2081 getUniversalAxes (joint
,ax1
,ax2
);
2083 // It should be possible to get both angles without explicitly
2084 // constructing the rotation matrix of the cross. Basically,
2085 // orientation of the cross about axis1 comes from body 2,
2086 // about axis 2 comes from body 1, and the perpendicular
2087 // axis can come from the two bodies somehow. (We don't really
2088 // want to assume it's 90 degrees, because in general the
2089 // constraints won't be perfectly satisfied, or even very well
2092 // However, we'd need a version of getHingeAngleFromRElativeQuat()
2093 // that CAN handle when its relative quat is rotated along a direction
2094 // other than the given axis. What I have here works,
2095 // although it's probably much slower than need be.
2097 dRFrom2Axes(R
, ax2
[0], ax2
[1], ax2
[2], ax1
[0], ax1
[1], ax1
[2]);
2100 if (joint
->node
[1].body
) {
2101 dQMultiply1 (qq
, joint
->node
[1].body
->q
, qcross
);
2102 dQMultiply2 (qrel
,qq
,joint
->qrel2
);
2105 // pretend joint->node[1].body->q is the identity
2106 dQMultiply2 (qrel
,qcross
, joint
->qrel2
);
2109 return - getHingeAngleFromRelativeQuat(qrel
, joint
->axis2
);
2115 static void universalGetInfo1 (dxJointUniversal
*j
, dxJoint::Info1
*info
)
2120 bool limiting1
= (j
->limot1
.lostop
>= -M_PI
|| j
->limot1
.histop
<= M_PI
) &&
2121 j
->limot1
.lostop
<= j
->limot1
.histop
;
2122 bool limiting2
= (j
->limot2
.lostop
>= -M_PI
|| j
->limot2
.histop
<= M_PI
) &&
2123 j
->limot2
.lostop
<= j
->limot2
.histop
;
2125 // We need to call testRotationLimit() even if we're motored, since it
2126 // records the result.
2127 j
->limot1
.limit
= 0;
2128 j
->limot2
.limit
= 0;
2130 if (limiting1
|| limiting2
) {
2131 dReal angle1
, angle2
;
2132 getUniversalAngles (j
, &angle1
, &angle2
);
2134 j
->limot1
.testRotationalLimit (angle1
);
2136 j
->limot2
.testRotationalLimit (angle2
);
2139 if (j
->limot1
.limit
|| j
->limot1
.fmax
> 0) info
->m
++;
2140 if (j
->limot2
.limit
|| j
->limot2
.fmax
> 0) info
->m
++;
2144 static void universalGetInfo2 (dxJointUniversal
*joint
, dxJoint::Info2
*info
)
2146 // set the three ball-and-socket rows
2147 setBall (joint
,info
,joint
->anchor1
,joint
->anchor2
);
2149 // set the universal joint row. the angular velocity about an axis
2150 // perpendicular to both joint axes should be equal. thus the constraint
2153 // where p is a vector normal to both joint axes, and w1 and w2
2154 // are the angular velocity vectors of the two bodies.
2156 // length 1 joint axis in global coordinates, from each body
2159 // length 1 vector perpendicular to ax1 and ax2. Neither body can rotate
2164 // Since axis1 and axis2 may not be perpendicular
2165 // we find a axis2_tmp which is really perpendicular to axis1
2166 // and in the plane of axis1 and axis2
2167 getUniversalAxes(joint
, ax1
, ax2
);
2169 ax2_temp
[0] = ax2
[0] - k
*ax1
[0];
2170 ax2_temp
[1] = ax2
[1] - k
*ax1
[1];
2171 ax2_temp
[2] = ax2
[2] - k
*ax1
[2];
2172 dCROSS(p
, =, ax1
, ax2_temp
);
2175 int s3
=3*info
->rowskip
;
2177 info
->J1a
[s3
+0] = p
[0];
2178 info
->J1a
[s3
+1] = p
[1];
2179 info
->J1a
[s3
+2] = p
[2];
2181 if (joint
->node
[1].body
) {
2182 info
->J2a
[s3
+0] = -p
[0];
2183 info
->J2a
[s3
+1] = -p
[1];
2184 info
->J2a
[s3
+2] = -p
[2];
2187 // compute the right hand side of the constraint equation. set relative
2188 // body velocities along p to bring the axes back to perpendicular.
2189 // If ax1, ax2 are unit length joint axes as computed from body1 and
2190 // body2, we need to rotate both bodies along the axis p. If theta
2191 // is the angle between ax1 and ax2, we need an angular velocity
2192 // along p to cover the angle erp * (theta - Pi/2) in one step:
2194 // |angular_velocity| = angle/time = erp*(theta - Pi/2) / stepsize
2195 // = (erp*fps) * (theta - Pi/2)
2197 // if theta is close to Pi/2,
2198 // theta - Pi/2 ~= cos(theta), so
2199 // |angular_velocity| ~= (erp*fps) * (ax1 dot ax2)
2201 info
->c
[3] = info
->fps
* info
->erp
* - k
;
2203 // if the first angle is powered, or has joint limits, add in the stuff
2204 int row
= 4 + joint
->limot1
.addLimot (joint
,info
,4,ax1
,1);
2206 // if the second angle is powered, or has joint limits, add in more stuff
2207 joint
->limot2
.addLimot (joint
,info
,row
,ax2
,1);
2211 static void universalComputeInitialRelativeRotations (dxJointUniversal
*joint
)
2213 if (joint
->node
[0].body
) {
2218 getUniversalAxes(joint
, ax1
, ax2
);
2221 dRFrom2Axes(R
, ax1
[0], ax1
[1], ax1
[2], ax2
[0], ax2
[1], ax2
[2]);
2223 dQMultiply1 (joint
->qrel1
, joint
->node
[0].body
->q
, qcross
);
2226 dRFrom2Axes(R
, ax2
[0], ax2
[1], ax2
[2], ax1
[0], ax1
[1], ax1
[2]);
2228 if (joint
->node
[1].body
) {
2229 dQMultiply1 (joint
->qrel2
, joint
->node
[1].body
->q
, qcross
);
2232 // set joint->qrel to qcross
2233 for (int i
=0; i
<4; i
++) joint
->qrel2
[i
] = qcross
[i
];
2239 void dJointSetUniversalAnchor (dJointID j
, dReal x
, dReal y
, dReal z
)
2241 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2242 dUASSERT(joint
,"bad joint argument");
2243 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2244 setAnchors (joint
,x
,y
,z
,joint
->anchor1
,joint
->anchor2
);
2245 universalComputeInitialRelativeRotations(joint
);
2249 void dJointSetUniversalAxis1 (dJointID j
, dReal x
, dReal y
, dReal z
)
2251 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2252 dUASSERT(joint
,"bad joint argument");
2253 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2254 if (joint
->flags
& dJOINT_REVERSE
)
2255 setAxes (joint
,x
,y
,z
,NULL
,joint
->axis2
);
2257 setAxes (joint
,x
,y
,z
,joint
->axis1
,NULL
);
2258 universalComputeInitialRelativeRotations(joint
);
2262 void dJointSetUniversalAxis2 (dJointID j
, dReal x
, dReal y
, dReal z
)
2264 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2265 dUASSERT(joint
,"bad joint argument");
2266 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2267 if (joint
->flags
& dJOINT_REVERSE
)
2268 setAxes (joint
,x
,y
,z
,joint
->axis1
,NULL
);
2270 setAxes (joint
,x
,y
,z
,NULL
,joint
->axis2
);
2271 universalComputeInitialRelativeRotations(joint
);
2275 void dJointGetUniversalAnchor (dJointID j
, dVector3 result
)
2277 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2278 dUASSERT(joint
,"bad joint argument");
2279 dUASSERT(result
,"bad result argument");
2280 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2281 if (joint
->flags
& dJOINT_REVERSE
)
2282 getAnchor2 (joint
,result
,joint
->anchor2
);
2284 getAnchor (joint
,result
,joint
->anchor1
);
2288 void dJointGetUniversalAnchor2 (dJointID j
, dVector3 result
)
2290 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2291 dUASSERT(joint
,"bad joint argument");
2292 dUASSERT(result
,"bad result argument");
2293 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2294 if (joint
->flags
& dJOINT_REVERSE
)
2295 getAnchor (joint
,result
,joint
->anchor1
);
2297 getAnchor2 (joint
,result
,joint
->anchor2
);
2301 void dJointGetUniversalAxis1 (dJointID j
, dVector3 result
)
2303 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2304 dUASSERT(joint
,"bad joint argument");
2305 dUASSERT(result
,"bad result argument");
2306 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2307 if (joint
->flags
& dJOINT_REVERSE
)
2308 getAxis2 (joint
,result
,joint
->axis2
);
2310 getAxis (joint
,result
,joint
->axis1
);
2314 void dJointGetUniversalAxis2 (dJointID j
, dVector3 result
)
2316 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2317 dUASSERT(joint
,"bad joint argument");
2318 dUASSERT(result
,"bad result argument");
2319 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2320 if (joint
->flags
& dJOINT_REVERSE
)
2321 getAxis (joint
,result
,joint
->axis1
);
2323 getAxis2 (joint
,result
,joint
->axis2
);
2327 void dJointSetUniversalParam (dJointID j
, int parameter
, dReal value
)
2329 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2330 dUASSERT(joint
,"bad joint argument");
2331 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2332 if ((parameter
& 0xff00) == 0x100) {
2333 joint
->limot2
.set (parameter
& 0xff,value
);
2336 joint
->limot1
.set (parameter
,value
);
2341 dReal
dJointGetUniversalParam (dJointID j
, int parameter
)
2343 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2344 dUASSERT(joint
,"bad joint argument");
2345 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2346 if ((parameter
& 0xff00) == 0x100) {
2347 return joint
->limot2
.get (parameter
& 0xff);
2350 return joint
->limot1
.get (parameter
);
2354 void dJointGetUniversalAngles (dJointID j
, dReal
*angle1
, dReal
*angle2
)
2356 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2357 dUASSERT(joint
,"bad joint argument");
2358 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2359 if (joint
->flags
& dJOINT_REVERSE
)
2360 return getUniversalAngles (joint
, angle2
, angle1
);
2362 return getUniversalAngles (joint
, angle1
, angle2
);
2366 dReal
dJointGetUniversalAngle1 (dJointID j
)
2368 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2369 dUASSERT(joint
,"bad joint argument");
2370 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2371 if (joint
->flags
& dJOINT_REVERSE
)
2372 return getUniversalAngle2 (joint
);
2374 return getUniversalAngle1 (joint
);
2378 dReal
dJointGetUniversalAngle2 (dJointID j
)
2380 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2381 dUASSERT(joint
,"bad joint argument");
2382 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2383 if (joint
->flags
& dJOINT_REVERSE
)
2384 return getUniversalAngle1 (joint
);
2386 return getUniversalAngle2 (joint
);
2390 dReal
dJointGetUniversalAngle1Rate (dJointID j
)
2392 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2393 dUASSERT(joint
,"bad joint argument");
2394 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2396 if (joint
->node
[0].body
) {
2399 if (joint
->flags
& dJOINT_REVERSE
)
2400 getAxis2 (joint
,axis
,joint
->axis2
);
2402 getAxis (joint
,axis
,joint
->axis1
);
2404 dReal rate
= dDOT(axis
, joint
->node
[0].body
->avel
);
2405 if (joint
->node
[1].body
) rate
-= dDOT(axis
, joint
->node
[1].body
->avel
);
2412 dReal
dJointGetUniversalAngle2Rate (dJointID j
)
2414 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2415 dUASSERT(joint
,"bad joint argument");
2416 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2418 if (joint
->node
[0].body
) {
2421 if (joint
->flags
& dJOINT_REVERSE
)
2422 getAxis (joint
,axis
,joint
->axis1
);
2424 getAxis2 (joint
,axis
,joint
->axis2
);
2426 dReal rate
= dDOT(axis
, joint
->node
[0].body
->avel
);
2427 if (joint
->node
[1].body
) rate
-= dDOT(axis
, joint
->node
[1].body
->avel
);
2434 void dJointAddUniversalTorques (dJointID j
, dReal torque1
, dReal torque2
)
2436 dxJointUniversal
* joint
= (dxJointUniversal
*)j
;
2437 dVector3 axis1
, axis2
;
2439 dUASSERT(joint
->vtable
== &__duniversal_vtable
,"joint is not a universal");
2441 if (joint
->flags
& dJOINT_REVERSE
) {
2442 dReal temp
= torque1
;
2443 torque1
= - torque2
;
2447 getAxis (joint
,axis1
,joint
->axis1
);
2448 getAxis2 (joint
,axis2
,joint
->axis2
);
2449 axis1
[0] = axis1
[0] * torque1
+ axis2
[0] * torque2
;
2450 axis1
[1] = axis1
[1] * torque1
+ axis2
[1] * torque2
;
2451 axis1
[2] = axis1
[2] * torque1
+ axis2
[2] * torque2
;
2453 if (joint
->node
[0].body
!= 0)
2454 dBodyAddTorque (joint
->node
[0].body
,axis1
[0],axis1
[1],axis1
[2]);
2455 if (joint
->node
[1].body
!= 0)
2456 dBodyAddTorque(joint
->node
[1].body
, -axis1
[0], -axis1
[1], -axis1
[2]);
2463 dxJoint::Vtable __duniversal_vtable
= {
2464 sizeof(dxJointUniversal
),
2465 (dxJoint::init_fn
*) universalInit
,
2466 (dxJoint::getInfo1_fn
*) universalGetInfo1
,
2467 (dxJoint::getInfo2_fn
*) universalGetInfo2
,
2468 dJointTypeUniversal
};
2472 //****************************************************************************
2473 // Prismatic and Rotoide
2475 static void PRInit (dxJointPR
*j
)
2479 // | Body 1 P R Body2
2480 // |+---------+ _ _ +-----------+
2481 // || |----|----(_)--------+ |
2482 // |+---------+ - +-----------+
2484 // X.-----------------------------------------> Y
2485 // N.B. X is comming out of the page
2486 dSetZero (j
->anchor2
,4);
2488 dSetZero (j
->axisR1
,4);
2490 dSetZero (j
->axisR2
,4);
2493 dSetZero (j
->axisP1
,4);
2495 dSetZero (j
->qrel
,4);
2496 dSetZero (j
->offset
,4);
2498 j
->limotR
.init (j
->world
);
2499 j
->limotP
.init (j
->world
);
2503 dReal
dJointGetPRPosition (dJointID j
)
2505 dxJointPR
* joint
= (dxJointPR
*) j
;
2506 dUASSERT (joint
,"bad joint argument");
2507 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a Prismatic and Rotoide");
2510 // get the offset in global coordinates
2511 dMULTIPLY0_331 (q
,joint
->node
[0].body
->posr
.R
,joint
->offset
);
2513 if (joint
->node
[1].body
) {
2516 // get the anchor2 in global coordinates
2517 dMULTIPLY0_331 (anchor2
,joint
->node
[1].body
->posr
.R
,joint
->anchor2
);
2519 q
[0] = ( (joint
->node
[0].body
->posr
.pos
[0] + q
[0]) -
2520 (joint
->node
[1].body
->posr
.pos
[0] + anchor2
[0]) );
2521 q
[1] = ( (joint
->node
[0].body
->posr
.pos
[1] + q
[1]) -
2522 (joint
->node
[1].body
->posr
.pos
[1] + anchor2
[1]) );
2523 q
[2] = ( (joint
->node
[0].body
->posr
.pos
[2] + q
[2]) -
2524 (joint
->node
[1].body
->posr
.pos
[2] + anchor2
[2]) );
2528 //N.B. When there is no body 2 the joint->anchor2 is already in
2529 // global coordinates
2531 q
[0] = ( (joint
->node
[0].body
->posr
.pos
[0] + q
[0]) -
2532 (joint
->anchor2
[0]) );
2533 q
[1] = ( (joint
->node
[0].body
->posr
.pos
[1] + q
[1]) -
2534 (joint
->anchor2
[1]) );
2535 q
[2] = ( (joint
->node
[0].body
->posr
.pos
[2] + q
[2]) -
2536 (joint
->anchor2
[2]) );
2541 // get prismatic axis in global coordinates
2542 dMULTIPLY0_331 (axP
,joint
->node
[0].body
->posr
.R
,joint
->axisP1
);
2544 return dDOT (axP
, q
);
2547 dReal
dJointGetPRPositionRate (dJointID j
)
2549 dxJointPR
* joint
= (dxJointPR
*) j
;
2550 dUASSERT (joint
,"bad joint argument");
2551 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a PR");
2552 // get axis1 in global coordinates
2554 dMULTIPLY0_331 (ax1
,joint
->node
[0].body
->posr
.R
,joint
->axisP1
);
2556 if (joint
->node
[1].body
) {
2558 dBodyGetRelPointVel (joint
->node
[1].body
, joint
->anchor2
[0], joint
->anchor2
[1], joint
->anchor2
[2], lv2
);
2559 return dDOT (ax1
, joint
->node
[0].body
->lvel
) - dDOT (ax1
, lv2
);
2562 return dDOT (ax1
,joint
->node
[0].body
->lvel
);
2567 static void PRGetInfo1 (dxJointPR
*j
, dxJoint::Info1
*info
)
2573 // see if we're at a joint limit.
2574 j
->limotP
.limit
= 0;
2575 if ( (j
->limotP
.lostop
> -dInfinity
|| j
->limotP
.histop
< dInfinity
) &&
2576 j
->limotP
.lostop
<= j
->limotP
.histop
) {
2577 // measure joint position
2578 dReal pos
= dJointGetPRPosition (j
);
2579 j
->limotP
.testRotationalLimit (pos
); // N.B. The function is ill named
2582 // powered needs an extra constraint row
2583 if (j
->limotP
.limit
|| j
->limotP
.fmax
> 0) info
->m
++;
2588 static void PRGetInfo2 (dxJointPR
*joint
, dxJoint::Info2
*info
)
2590 int s
= info
->rowskip
;
2595 dReal k
= info
->fps
* info
->erp
;
2598 dVector3 q
; // plane space of axP and after that axR
2600 // pull out pos and R for both bodies. also get the `connection'
2601 // vector pos2-pos1.
2603 dReal
*pos1
,*pos2
,*R1
,*R2
;
2604 pos1
= joint
->node
[0].body
->posr
.pos
;
2605 R1
= joint
->node
[0].body
->posr
.R
;
2606 if (joint
->node
[1].body
) {
2607 pos2
= joint
->node
[1].body
->posr
.pos
;
2608 R2
= joint
->node
[1].body
->posr
.R
;
2611 // pos2 = 0; // N.B. We can do that to be safe but it is no necessary
2612 // R2 = 0; // N.B. We can do that to be safe but it is no necessary
2616 dVector3 axP
; // Axis of the prismatic joint in global frame
2617 dMULTIPLY0_331 (axP
, R1
, joint
->axisP1
);
2619 // distance between the body1 and the anchor2 in global frame
2620 // Calculated in the same way as the offset
2621 dVector3 anchor2
, dist
;
2623 if (joint
->node
[1].body
) {
2624 // Calculate anchor2 in world coordinate
2625 dMULTIPLY0_331 (anchor2
, R2
, joint
->anchor2
);
2626 dist
[0] = anchor2
[0] + pos2
[0] - pos1
[0];
2627 dist
[1] = anchor2
[1] + pos2
[1] - pos1
[1];
2628 dist
[2] = anchor2
[2] + pos2
[2] - pos1
[2];
2631 dist
[0] = joint
->anchor2
[0] - pos1
[0];
2632 dist
[1] = joint
->anchor2
[1] - pos1
[1];
2633 dist
[2] = joint
->anchor2
[2] - pos1
[2];
2637 // ======================================================================
2638 // Work on the Rotoide part (i.e. row 0, 1 and maybe 4 if rotoide powered
2640 // Set the two rotoide rows. The rotoide axis should be the only unconstrained
2641 // rotational axis, the angular velocity of the two bodies perpendicular to
2642 // the rotoide axis should be equal. Thus the constraint equations are
2645 // where p and q are unit vectors normal to the rotoide axis, and w1 and w2
2646 // are the angular velocity vectors of the two bodies.
2648 dMULTIPLY0_331 (ax1
, joint
->node
[0].body
->posr
.R
, joint
->axisR1
);
2649 dCROSS (q
, =, ax1
, axP
);
2651 info
->J1a
[0] = axP
[0];
2652 info
->J1a
[1] = axP
[1];
2653 info
->J1a
[2] = axP
[2];
2654 info
->J1a
[s
+0] = q
[0];
2655 info
->J1a
[s
+1] = q
[1];
2656 info
->J1a
[s
+2] = q
[2];
2658 if (joint
->node
[1].body
) {
2659 info
->J2a
[0] = -axP
[0];
2660 info
->J2a
[1] = -axP
[1];
2661 info
->J2a
[2] = -axP
[2];
2662 info
->J2a
[s
+0] = -q
[0];
2663 info
->J2a
[s
+1] = -q
[1];
2664 info
->J2a
[s
+2] = -q
[2];
2668 // Compute the right hand side of the constraint equation set. Relative
2669 // body velocities along p and q to bring the rotoide back into alignment.
2670 // ax1,ax2 are the unit length rotoide axes of body1 and body2 in world frame.
2671 // We need to rotate both bodies along the axis u = (ax1 x ax2).
2672 // if `theta' is the angle between ax1 and ax2, we need an angular velocity
2673 // along u to cover angle erp*theta in one step :
2674 // |angular_velocity| = angle/time = erp*theta / stepsize
2675 // = (erp*fps) * theta
2676 // angular_velocity = |angular_velocity| * (ax1 x ax2) / |ax1 x ax2|
2677 // = (erp*fps) * theta * (ax1 x ax2) / sin(theta)
2678 // ...as ax1 and ax2 are unit length. if theta is smallish,
2679 // theta ~= sin(theta), so
2680 // angular_velocity = (erp*fps) * (ax1 x ax2)
2681 // ax1 x ax2 is in the plane space of ax1, so we project the angular
2682 // velocity to p and q to find the right hand side.
2685 if (joint
->node
[1].body
) {
2686 dMULTIPLY0_331 (ax2
, R2
, joint
->axisR2
);
2689 ax2
[0] = joint
->axisR2
[0];
2690 ax2
[1] = joint
->axisR2
[1];
2691 ax2
[2] = joint
->axisR2
[2];
2695 dCROSS (b
,=,ax1
, ax2
);
2696 info
->c
[0] = k
* dDOT (b
, axP
);
2697 info
->c
[1] = k
* dDOT (b
, q
);
2701 // ==========================
2702 // Work on the Prismatic part (i.e row 2,3 and 4 if only the prismatic is powered
2703 // or 5 if rotoide and prismatic powered
2705 // two rows. we want: vel2 = vel1 + w1 x c ... but this would
2706 // result in three equations, so we project along the planespace vectors
2707 // so that sliding along the prismatic axis is disregarded. for symmetry we
2708 // also substitute (w1+w2)/2 for w1, as w1 is supposed to equal w2.
2710 // p1 + R1 dist' = p2 + R2 anchor2' ## OLD ## p1 + R1 anchor1' = p2 + R2 dist'
2711 // v1 + w1 x R1 dist' + v_p = v2 + w2 x R2 anchor2'## OLD v1 + w1 x R1 anchor1' = v2 + w2 x R2 dist' + v_p
2712 // v_p is speed of prismatic joint (i.e. elongation rate)
2713 // Since the constraints are perpendicular to v_p we have:
2714 // p dot v_p = 0 and q dot v_p = 0
2715 // ax1 dot ( v1 + w1 x dist = v2 + w2 x anchor2 )
2716 // q dot ( v1 + w1 x dist = v2 + w2 x anchor2 )
2718 // ax1 . v1 + ax1 . w1 x dist = ax1 . v2 + ax1 . w2 x anchor2 ## OLD ## ax1 . v1 + ax1 . w1 x anchor1 = ax1 . v2 + ax1 . w2 x dist
2719 // since a . (b x c) = - b . (a x c) = - (a x c) . b
2720 // and a x b = - b x a
2721 // ax1 . v1 - ax1 x dist . w1 - ax1 . v2 - (- ax1 x anchor2 . w2) = 0
2722 // ax1 . v1 + dist x ax1 . w1 - ax1 . v2 - anchor2 x ax1 . w2 = 0
2723 // Coeff for 1er line of: J1l => ax1, J2l => -ax1
2724 // Coeff for 2er line of: J1l => q, J2l => -q
2725 // Coeff for 1er line of: J1a => dist x ax1, J2a => - anchor2 x ax1
2726 // Coeff for 2er line of: J1a => dist x q, J2a => - anchor2 x q
2729 dCROSS ( (info
->J1a
) +s2
, = , dist
, ax1
);
2731 dCROSS ( (info
->J1a
) +s3
, = , dist
, q
);
2734 info
->J1l
[s2
+0] = ax1
[0];
2735 info
->J1l
[s2
+1] = ax1
[1];
2736 info
->J1l
[s2
+2] = ax1
[2];
2738 info
->J1l
[s3
+0] = q
[0];
2739 info
->J1l
[s3
+1] = q
[1];
2740 info
->J1l
[s3
+2] = q
[2];
2742 if (joint
->node
[1].body
) {
2743 // ax2 x anchor2 instead of anchor2 x ax2 since we want the negative value
2744 dCROSS ( (info
->J2a
) +s2
, = , ax2
, anchor2
); // since ax1 == ax2
2746 // The cross product is in reverse order since we want the negative value
2747 dCROSS ( (info
->J2a
) +s3
, = , q
, anchor2
);
2749 info
->J2l
[s2
+0] = -ax1
[0];
2750 info
->J2l
[s2
+1] = -ax1
[1];
2751 info
->J2l
[s2
+2] = -ax1
[2];
2753 info
->J2l
[s3
+0] = -q
[0];
2754 info
->J2l
[s3
+1] = -q
[1];
2755 info
->J2l
[s3
+2] = -q
[2];
2759 // We want to make correction for motion not in the line of the axisP
2760 // We calculate the displacement w.r.t. the anchor pt.
2762 // compute the elements 2 and 3 of right hand side.
2763 // we want to align the offset point (in body 2's frame) with the center of body 1.
2764 // The position should be the same when we are not along the prismatic axis
2766 dMULTIPLY0_331 (err
, R1
, joint
->offset
);
2767 err
[0] = dist
[0] - err
[0];
2768 err
[1] = dist
[1] - err
[1];
2769 err
[2] = dist
[2] - err
[2];
2770 info
->c
[2] = k
* dDOT (ax1
, err
);
2771 info
->c
[3] = k
* dDOT (q
, err
);
2773 joint
->limotP
.addLimot (joint
, info
, 4, axP
, 0);
2777 // compute initial relative rotation body1 -> body2, or env -> body1
2778 static void PRComputeInitialRelativeRotation (dxJointPR
*joint
)
2780 if (joint
->node
[0].body
) {
2781 if (joint
->node
[1].body
) {
2782 dQMultiply1 (joint
->qrel
,joint
->node
[0].body
->q
,joint
->node
[1].body
->q
);
2785 // set joint->qrel to the transpose of the first body q
2786 joint
->qrel
[0] = joint
->node
[0].body
->q
[0];
2787 for (int i
=1; i
<4; i
++) joint
->qrel
[i
] = -joint
->node
[0].body
->q
[i
];
2788 // WARNING do we need the - in -joint->node[0].body->q[i]; or not
2793 void dJointSetPRAnchor (dJointID j
, dReal x
, dReal y
, dReal z
)
2795 dxJointPR
* joint
= (dxJointPR
*) j
;
2796 dUASSERT (joint
,"bad joint argument");
2797 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a Prismatic and Rotoide");
2799 setAnchors (joint
,x
,y
,z
,joint
->offset
,joint
->anchor2
);
2803 void dJointSetPRAxis1 (dJointID j
, dReal x
, dReal y
, dReal z
)
2805 dxJointPR
* joint
= (dxJointPR
*) j
;
2806 dUASSERT (joint
,"bad joint argument");
2807 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a Prismatic and Rotoide");
2809 setAxes (joint
,x
,y
,z
,joint
->axisP1
, 0);
2811 PRComputeInitialRelativeRotation (joint
);
2815 void dJointSetPRAxis2 (dJointID j
, dReal x
, dReal y
, dReal z
)
2817 dxJointPR
* joint
= (dxJointPR
*) j
;
2818 dUASSERT (joint
,"bad joint argument");
2819 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a Prismatic and Rotoide");
2820 setAxes (joint
,x
,y
,z
,joint
->axisR1
,joint
->axisR2
);
2821 PRComputeInitialRelativeRotation (joint
);
2825 void dJointSetPRParam (dJointID j
, int parameter
, dReal value
)
2827 dxJointPR
* joint
= (dxJointPR
*) j
;
2828 dUASSERT (joint
,"bad joint argument");
2829 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a Prismatic and Rotoide");
2830 if ( (parameter
& 0xff00) == 0x100) {
2831 joint
->limotR
.set (parameter
& 0xff, value
); // Take only lower part of the
2834 joint
->limotP
.set (parameter
, value
);
2838 void dJointGetPRAnchor (dJointID j
, dVector3 result
)
2840 dxJointPR
* joint
= (dxJointPR
*) j
;
2841 dUASSERT (joint
,"bad joint argument");
2842 dUASSERT (result
,"bad result argument");
2843 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a Prismatic and Rotoide");
2845 if (joint
->node
[1].body
)
2846 getAnchor2 (joint
,result
,joint
->anchor2
);
2848 result
[0] = joint
->anchor2
[0];
2849 result
[1] = joint
->anchor2
[1];
2850 result
[2] = joint
->anchor2
[2];
2855 void dJointGetPRAxis1 (dJointID j
, dVector3 result
)
2857 dxJointPR
* joint
= (dxJointPR
*) j
;
2858 dUASSERT (joint
,"bad joint argument");
2859 dUASSERT (result
,"bad result argument");
2860 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a Prismatic and Rotoide");
2861 getAxis (joint
, result
, joint
->axisP1
);
2864 void dJointGetPRAxis2 (dJointID j
, dVector3 result
)
2866 dxJointPR
* joint
= (dxJointPR
*) j
;
2867 dUASSERT (joint
,"bad joint argument");
2868 dUASSERT (result
,"bad result argument");
2869 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a Prismatic and Rotoide");
2870 getAxis (joint
, result
, joint
->axisR1
);
2873 dReal
dJointGetPRParam (dJointID j
, int parameter
)
2875 dxJointPR
* joint
= (dxJointPR
*) j
;
2876 dUASSERT (joint
,"bad joint argument");
2877 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not Prismatic and Rotoide");
2878 if ( (parameter
& 0xff00) == 0x100) {
2879 return joint
->limotR
.get (parameter
& 0xff);
2882 return joint
->limotP
.get (parameter
);
2886 void dJointAddPRTorque (dJointID j
, dReal torque
)
2888 dxJointPR
* joint
= (dxJointPR
*) j
;
2891 dUASSERT (joint
->vtable
== &__dPR_vtable
,"joint is not a Prismatic and Rotoide");
2893 if (joint
->flags
& dJOINT_REVERSE
)
2896 getAxis (joint
,axis
,joint
->axisR1
);
2901 if (joint
->node
[0].body
!= 0)
2902 dBodyAddTorque (joint
->node
[0].body
, axis
[0], axis
[1], axis
[2]);
2903 if (joint
->node
[1].body
!= 0)
2904 dBodyAddTorque (joint
->node
[1].body
, -axis
[0], -axis
[1], -axis
[2]);
2908 dxJoint::Vtable __dPR_vtable
= {
2910 (dxJoint::init_fn
*) PRInit
,
2911 (dxJoint::getInfo1_fn
*) PRGetInfo1
,
2912 (dxJoint::getInfo2_fn
*) PRGetInfo2
,
2917 //****************************************************************************
2918 // Prismatic and Universal
2920 static void PUInit (dxJointPU
*j
)
2927 // | / Body 2 | / Body 1
2928 // | / +---------+ | / +-----------+
2929 // | / / /| | / / /|
2930 // | / / / + _/ - / / +
2931 // | / / /-/--------(_)----|--- /-----------/-------> AxisP
2932 // | / +---------+ / - +-----------+ /
2934 // | / +---------+ +-----------+
2936 // .-----------------------------------------> X
2937 // |----------------->
2938 // Anchor2 <--------------|
2942 // Setting member variables which are w.r.t body2
2943 dSetZero (j
->axis1
,4);
2946 // Setting member variables which are w.r.t body2
2947 dSetZero (j
->anchor2
,4);
2948 dSetZero (j
->axis2
,4);
2951 dSetZero (j
->axisP1
,4);
2954 dSetZero (j
->qrel1
,4);
2955 dSetZero (j
->qrel2
,4);
2958 j
->limotP
.init (j
->world
);
2959 j
->limot1
.init (j
->world
);
2960 j
->limot2
.init (j
->world
);
2964 dReal
dJointGetPUPosition (dJointID j
)
2966 dxJointPU
* joint
= (dxJointPU
*) j
;
2967 dUASSERT (joint
,"bad joint argument");
2968 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
2971 // get the offset in global coordinates
2972 dMULTIPLY0_331 (q
,joint
->node
[0].body
->posr
.R
,joint
->anchor1
);
2974 if (joint
->node
[1].body
) {
2977 // get the anchor2 in global coordinates
2978 dMULTIPLY0_331 (anchor2
,joint
->node
[1].body
->posr
.R
,joint
->anchor2
);
2980 q
[0] = ( (joint
->node
[0].body
->posr
.pos
[0] + q
[0]) -
2981 (joint
->node
[1].body
->posr
.pos
[0] + anchor2
[0]) );
2982 q
[1] = ( (joint
->node
[0].body
->posr
.pos
[1] + q
[1]) -
2983 (joint
->node
[1].body
->posr
.pos
[1] + anchor2
[1]) );
2984 q
[2] = ( (joint
->node
[0].body
->posr
.pos
[2] + q
[2]) -
2985 (joint
->node
[1].body
->posr
.pos
[2] + anchor2
[2]) );
2988 //N.B. When there is no body 2 the joint->anchor2 is already in
2989 // global coordinates
2991 q
[0] = ( (joint
->node
[0].body
->posr
.pos
[0] + q
[0]) -
2992 (joint
->anchor2
[0]) );
2993 q
[1] = ( (joint
->node
[0].body
->posr
.pos
[1] + q
[1]) -
2994 (joint
->anchor2
[1]) );
2995 q
[2] = ( (joint
->node
[0].body
->posr
.pos
[2] + q
[2]) -
2996 (joint
->anchor2
[2]) );
3001 // get prismatic axis in global coordinates
3002 dMULTIPLY0_331 (axP
,joint
->node
[0].body
->posr
.R
,joint
->axisP1
);
3004 return dDOT (axP
, q
);
3008 dReal
dJointGetPUPositionRate (dJointID j
)
3010 dxJointPU
* joint
= (dxJointPU
*) j
;
3011 dUASSERT (joint
,"bad joint argument");
3012 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3014 if (joint
->node
[0].body
) {
3015 // We want to find the rate of change of the prismatic part of the joint
3016 // We can find it by looking at the speed difference between body1 and the
3019 // r will be used to find the distance between body1 and the anchor point
3022 if (joint
->node
[1].body
) {
3023 // Find joint->anchor2 in global coordinates
3024 dMULTIPLY0_331 (anchor2
,joint
->node
[1].body
->posr
.R
,joint
->anchor2
);
3026 r
[0] = (joint
->node
[0].body
->posr
.pos
[0] -
3027 (anchor2
[0] + joint
->node
[1].body
->posr
.pos
[0]) );
3028 r
[1] = (joint
->node
[0].body
->posr
.pos
[1] -
3029 (anchor2
[1] + joint
->node
[1].body
->posr
.pos
[1]) );
3030 r
[2] = (joint
->node
[0].body
->posr
.pos
[2] -
3031 (anchor2
[2] + joint
->node
[1].body
->posr
.pos
[2]) );
3034 //N.B. When there is no body 2 the joint->anchor2 is already in
3035 // global coordinates
3036 // r = joint->node[0].body->posr.pos - joint->anchor2;
3037 dOP (r
, -, joint
->node
[0].body
->posr
.pos
, joint
->anchor2
);
3040 // The body1 can have velocity coming from the rotation of
3041 // the rotoide axis. We need to remove this.
3043 // N.B. We do vel = r X w instead of vel = w x r to have vel negative
3044 // since we want to remove it from the linear velocity of the body
3046 dCROSS (lvel1
, =, r
, joint
->node
[0].body
->avel
);
3048 // lvel1 += joint->node[0].body->lvel;
3049 dOPE (lvel1
, +=, joint
->node
[0].body
->lvel
);
3051 if (joint
->node
[1].body
) {
3052 // Find the contribution of the angular rotation to the linear speed
3053 // N.B. We do vel = r X w instead of vel = w x r to have vel negative
3054 // since we want to remove it from the linear velocity of the body
3056 dCROSS (lvel2
, =, anchor2
, joint
->node
[1].body
->avel
);
3058 // lvel1 -= lvel2 + joint->node[1].body->lvel;
3059 dOPE2 (lvel1
, -=, lvel2
, + , joint
->node
[1].body
->lvel
);
3063 // Since we want rate of change along the prismatic axis
3064 // get axisP1 in global coordinates and get the component
3065 // along this axis only
3067 dMULTIPLY0_331 (axP1
,joint
->node
[0].body
->posr
.R
,joint
->axisP1
);
3068 return dDOT (axP1
, lvel1
);
3076 static void PUGetInfo1 (dxJointPU
*j
, dxJoint::Info1
*info
)
3081 // powered needs an extra constraint row
3083 // see if we're at a joint limit.
3084 j
->limotP
.limit
= 0;
3085 if ( (j
->limotP
.lostop
> -dInfinity
|| j
->limotP
.histop
< dInfinity
) &&
3086 j
->limotP
.lostop
<= j
->limotP
.histop
) {
3087 // measure joint position
3088 dReal pos
= dJointGetPUPosition (j
);
3089 j
->limotP
.testRotationalLimit (pos
); // N.B. The function is ill named
3092 if (j
->limotP
.limit
|| j
->limotP
.fmax
> 0) info
->m
++;
3095 bool limiting1
= (j
->limot1
.lostop
>= -M_PI
|| j
->limot1
.histop
<= M_PI
) &&
3096 j
->limot1
.lostop
<= j
->limot1
.histop
;
3097 bool limiting2
= (j
->limot2
.lostop
>= -M_PI
|| j
->limot2
.histop
<= M_PI
) &&
3098 j
->limot2
.lostop
<= j
->limot2
.histop
;
3100 // We need to call testRotationLimit() even if we're motored, since it
3101 // records the result.
3102 j
->limot1
.limit
= 0;
3103 j
->limot1
.limit
= 0;
3104 if (limiting1
|| limiting2
) {
3105 dReal angle1
, angle2
;
3106 getUniversalAngles (j
, &angle1
, &angle2
);
3108 j
->limot1
.testRotationalLimit (angle1
);
3110 j
->limot2
.testRotationalLimit (angle2
);
3113 if (j
->limot1
.limit
|| j
->limot1
.fmax
> 0) info
->m
++;
3114 if (j
->limot2
.limit
|| j
->limot2
.fmax
> 0) info
->m
++;
3119 static void PUGetInfo2 (dxJointPU
*joint
, dxJoint::Info2
*info
)
3122 const int s1
= info
->rowskip
;
3127 const dReal k
= info
->fps
* info
->erp
;
3129 // pull out pos and R for both bodies. also get the `connection'
3130 // vector pos2-pos1.
3132 dReal
*pos1
,*pos2
,*R1
,*R2
;
3133 pos1
= joint
->node
[0].body
->posr
.pos
;
3134 R1
= joint
->node
[0].body
->posr
.R
;
3135 if (joint
->node
[1].body
) {
3136 pos2
= joint
->node
[1].body
->posr
.pos
;
3137 R2
= joint
->node
[1].body
->posr
.R
;
3140 // pos2 = 0; // N.B. We can do that to be safe but it is no necessary
3141 // R2 = 0; // N.B. We can do that to be safe but it is no necessary
3145 dVector3 axP
; // Axis of the prismatic joint in global frame
3146 dMULTIPLY0_331 (axP
, R1
, joint
->axisP1
);
3148 // distance between the body1 and the anchor2 in global frame
3149 // Calculated in the same way as the offset
3152 if (joint
->node
[1].body
) {
3153 dMULTIPLY0_331 (anchor2
, R2
, joint
->anchor2
);
3154 dist
[0] = anchor2
[0] + pos2
[0] - pos1
[0];
3155 dist
[1] = anchor2
[1] + pos2
[1] - pos1
[1];
3156 dist
[2] = anchor2
[2] + pos2
[2] - pos1
[2];
3159 // dist[i] = joint->anchor2[i] - pos1[i];
3160 dOPE2 (dist
, =, joint
->anchor2
, -, pos1
);
3163 dVector3 q
; // Temporary axis vector
3164 // Will be used at 2 places with 2 different meaning
3166 // ======================================================================
3167 // Work on the angular part (i.e. row 0)
3170 // The axis perpendicular to both axis1 and axis2 should be the only unconstrained
3171 // rotational axis, the angular velocity of the two bodies perpendicular to
3172 // the rotoide axes should be equal. Thus the constraint equations are
3174 // where p is a unit vector perpendicular to both axis1 and axis2
3175 // and w1 and w2 are the angular velocity vectors of the two bodies.
3177 getUniversalAxes (joint
, ax1
, ax2
);
3178 dReal val
= dDOT (ax1
, ax2
);
3179 q
[0] = ax2
[0] - val
*ax1
[0];
3180 q
[1] = ax2
[1] - val
*ax1
[1];
3181 q
[2] = ax2
[2] - val
*ax1
[2];
3184 dCROSS (p
, =, ax1
, q
);
3187 // info->J1a[s0+i] = p[i];
3188 dOPE ( (info
->J1a
) + s0
, =, p
);
3190 if (joint
->node
[1].body
) {
3191 // info->J2a[s0+i] = -p[i];
3192 dOPE ( (info
->J2a
) + s0
, = -, p
);
3195 // compute the right hand side of the constraint equation. Set relative
3196 // body velocities along p to bring the axes back to perpendicular.
3197 // If ax1, ax2 are unit length joint axes as computed from body1 and
3198 // body2, we need to rotate both bodies along the axis p. If theta
3199 // is the angle between ax1 and ax2, we need an angular velocity
3200 // along p to cover the angle erp * (theta - Pi/2) in one step:
3202 // |angular_velocity| = angle/time = erp*(theta - Pi/2) / stepsize
3203 // = (erp*fps) * (theta - Pi/2)
3205 // if theta is close to Pi/2,
3206 // theta - Pi/2 ~= cos(theta), so
3207 // |angular_velocity| ~= (erp*fps) * (ax1 dot ax2)
3209 info
->c
[0] = k
* - val
;
3213 // ==========================================================================
3214 // Work on the linear part (i.e rows 1 and 2)
3216 // We want: vel2 = vel1 + w1 x c ... but this would
3217 // result in three equations, so we project along the planespace vectors
3218 // so that sliding along the axisP is disregarded.
3220 // p1 + R1 dist' = p2 + R2 anchor2'
3221 // v1 + w1 x R1 dist' + v_p = v2 + w2 x R2 anchor2'
3222 // v_p is speed of prismatic joint (i.e. elongation rate)
3223 // Since the constraints are perpendicular to v_p we have:
3224 // e1 dot v_p = 0 and e2 dot v_p = 0
3225 // e1 dot ( v1 + w1 x dist = v2 + w2 x anchor2 )
3226 // e2 dot ( v1 + w1 x dist = v2 + w2 x anchor2 )
3228 // e1 . v1 + e1 . w1 x dist = e1 . v2 + e1 . w2 x anchor2
3229 // since a . (b x c) = - b . (a x c) = - (a x c) . b
3230 // and a x b = - b x a
3231 // e1 . v1 - e1 x dist . w1 - e1 . v2 - (- e1 x anchor2 . w2) = 0
3232 // e1 . v1 + dist x e1 . w1 - e1 . v2 - anchor2 x e1 . w2 = 0
3233 // Coeff for 1er line of: J1l => e1, J2l => -e1
3234 // Coeff for 2er line of: J1l => e2, J2l => -ax2
3235 // Coeff for 1er line of: J1a => dist x e1, J2a => - anchor2 x e1
3236 // Coeff for 2er line of: J1a => dist x e2, J2a => - anchor2 x e2
3237 // e1 and e2 are perpendicular to axP
3238 // so e1 = ax1 and e2 = ax1 x axP
3239 // N.B. ax2 is not always perpendicular to axP since it is attached to body 2
3240 dCROSS (q
, =, ax1
, axP
);
3242 dMULTIPLY0_331 (axP
, R1
, joint
->axisP1
);
3244 dCROSS ( (info
->J1a
) +s1
, = , dist
, ax1
);
3245 dCROSS ( (info
->J1a
) +s2
, = , dist
, q
);
3247 // info->J1l[s1+i] = ax[i];
3248 dOPE ( (info
->J1l
) + s1
, =, ax1
);
3250 // info->J1l[s2+i] = q[i];
3251 dOPE ( (info
->J1l
) + s2
, =, q
);
3253 if (joint
->node
[1].body
) {
3254 // Calculate anchor2 in world coordinate
3256 // q x anchor2 instead of anchor2 x q since we want the negative value
3257 dCROSS ( (info
->J2a
) +s1
, = , ax1
, anchor2
);
3258 // The cross product is in reverse order since we want the negative value
3259 dCROSS ( (info
->J2a
) +s2
, = , q
, anchor2
);
3262 // info->J2l[s1+i] = -ax1[i];
3263 dOPE ( (info
->J2l
) + s1
, = -, ax1
);
3264 // info->J2l[s2+i] = -ax1[i];
3265 dOPE ( (info
->J2l
) + s2
, = -, q
);
3270 // We want to make correction for motion not in the line of the axisP
3271 // We calculate the displacement w.r.t. the anchor pt.
3273 // compute the elements 1 and 2 of right hand side.
3274 // We want to align the offset point (in body 2's frame) with the center of body 1.
3275 // The position should be the same when we are not along the prismatic axis
3277 dMULTIPLY0_331 (err
, R1
, joint
->anchor1
);
3278 // err[i] = dist[i] - err[i];
3279 dOPE2 (err
, =, dist
, -, err
);
3280 info
->c
[1] = k
* dDOT (ax1
, err
);
3281 info
->c
[2] = k
* dDOT (q
, err
);
3283 int row
= 3 + joint
->limot1
.addLimot (joint
,info
,3,ax1
,1);
3284 row
+= joint
->limot2
.addLimot (joint
,info
,row
,ax2
,1);
3285 joint
->limotP
.addLimot (joint
,info
,row
,axP
,0);
3288 void dJointSetPUAnchor (dJointID j
, dReal x
, dReal y
, dReal z
)
3290 dxJointPU
* joint
= (dxJointPU
*) j
;
3291 dUASSERT (joint
,"bad joint argument");
3292 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3294 setAnchors (joint
,x
,y
,z
,joint
->anchor1
,joint
->anchor2
);
3295 universalComputeInitialRelativeRotations (joint
);
3299 * This function initialize the anchor and the relative position of each body
3300 * as if body2 was at its current position + [dx,dy,dy].
3305 * dJointGetPUAxis3(jId, dir);
3306 * dJointSetPUAnchor(jId, 0, 0, 0);
3307 * // If you request the position you will have: dJointGetPUPosition(jId) == 0
3308 * dJointSetPUAnchorDelta(jId, 0, 0, 0, dir[X]*offset, dir[Y]*offset, dir[Z]*offset);
3309 * // If you request the position you will have: dJointGetPUPosition(jId) == -offset
3312 * @param j The PU joint for which the anchor point will be set
3313 * @param x The X position of the anchor point in world frame
3314 * @param y The Y position of the anchor point in world frame
3315 * @param z The Z position of the anchor point in world frame
3316 * @param dx A delta to be added to the X position as if the anchor was set
3317 * when body1 was at current_position[X] + dx
3318 * @param dx A delta to be added to the Y position as if the anchor was set
3319 * when body1 was at current_position[Y] + dy
3320 * @param dx A delta to be added to the Z position as if the anchor was set
3321 * when body1 was at current_position[Z] + dz
3322 * @note Should have the same meaning as dJointSetSliderAxisDelta
3324 void dJointSetPUAnchorDelta (dJointID j
, dReal x
, dReal y
, dReal z
,
3325 dReal dx
, dReal dy
, dReal dz
)
3327 dxJointPU
* joint
= (dxJointPU
*) j
;
3328 dUASSERT (joint
,"bad joint argument");
3329 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3331 if (joint
->node
[0].body
) {
3332 joint
->node
[0].body
->posr
.pos
[0] += dx
;
3333 joint
->node
[0].body
->posr
.pos
[1] += dy
;
3334 joint
->node
[0].body
->posr
.pos
[2] += dz
;
3337 setAnchors (joint
,x
,y
,z
,joint
->anchor1
,joint
->anchor2
);
3339 if (joint
->node
[0].body
) {
3340 joint
->node
[0].body
->posr
.pos
[0] -= dx
;
3341 joint
->node
[0].body
->posr
.pos
[1] -= dy
;
3342 joint
->node
[0].body
->posr
.pos
[2] -= dz
;
3345 universalComputeInitialRelativeRotations (joint
);
3350 void dJointSetPUAxis1 (dJointID j
, dReal x
, dReal y
, dReal z
)
3352 dxJointPU
* joint
= (dxJointPU
*) j
;
3353 dUASSERT (joint
,"bad joint argument");
3354 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3355 if (joint
->flags
& dJOINT_REVERSE
)
3356 setAxes (joint
,x
,y
,z
,NULL
,joint
->axis2
);
3358 setAxes (joint
,x
,y
,z
,joint
->axis1
,NULL
);
3359 universalComputeInitialRelativeRotations (joint
);
3362 void dJointSetPUAxis2 (dJointID j
, dReal x
, dReal y
, dReal z
)
3364 dxJointPU
* joint
= (dxJointPU
*) j
;
3365 dUASSERT (joint
,"bad joint argument");
3366 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3367 if (joint
->flags
& dJOINT_REVERSE
)
3368 setAxes (joint
,x
,y
,z
,joint
->axis1
,NULL
);
3370 setAxes (joint
,x
,y
,z
,NULL
,joint
->axis2
);
3371 universalComputeInitialRelativeRotations (joint
);
3375 void dJointSetPUAxisP (dJointID id
, dReal x
, dReal y
, dReal z
)
3377 dJointSetPUAxis3 (id
, x
,y
, z
);
3382 void dJointSetPUAxis3 (dJointID j
, dReal x
, dReal y
, dReal z
)
3384 dxJointPU
* joint
= (dxJointPU
*) j
;
3385 dUASSERT (joint
,"bad joint argument");
3386 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3388 setAxes (joint
,x
,y
,z
,joint
->axisP1
, 0);
3390 universalComputeInitialRelativeRotations (joint
);
3396 void dJointGetPUAngles (dJointID j
, dReal
*angle1
, dReal
*angle2
)
3398 dxJointUniversal
* joint
= (dxJointUniversal
*) j
;
3399 dUASSERT (joint
,"bad joint argument");
3400 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3401 if (joint
->flags
& dJOINT_REVERSE
)
3402 return getUniversalAngles (joint
, angle2
, angle1
);
3404 return getUniversalAngles (joint
, angle1
, angle2
);
3408 dReal
dJointGetPUAngle1 (dJointID j
)
3410 dxJointUniversal
* joint
= (dxJointUniversal
*) j
;
3411 dUASSERT (joint
,"bad joint argument");
3412 dUASSERT (joint
->vtable
== &__dPU_vtable
,
3413 "joint is not a Prismatic and Universal");
3414 if (joint
->flags
& dJOINT_REVERSE
)
3415 return getUniversalAngle2 (joint
);
3417 return getUniversalAngle1 (joint
);
3421 dReal
dJointGetPUAngle2 (dJointID j
)
3423 dxJointUniversal
* joint
= (dxJointUniversal
*) j
;
3424 dUASSERT (joint
,"bad joint argument");
3425 dUASSERT (joint
->vtable
== &__dPU_vtable
,
3426 "joint is not a Prismatic and Universal");
3427 if (joint
->flags
& dJOINT_REVERSE
)
3428 return getUniversalAngle1 (joint
);
3430 return getUniversalAngle2 (joint
);
3434 dReal
dJointGetPUAngle1Rate (dJointID j
)
3436 dxJointPU
* joint
= (dxJointPU
*) j
;
3437 dUASSERT (joint
,"bad joint argument");
3438 dUASSERT (joint
->vtable
== &__dPU_vtable
,
3439 "joint is not a Prismatic and Universal");
3441 if (joint
->node
[0].body
) {
3444 if (joint
->flags
& dJOINT_REVERSE
)
3445 getAxis2 (joint
,axis
,joint
->axis2
);
3447 getAxis (joint
,axis
,joint
->axis1
);
3449 dReal rate
= dDOT (axis
, joint
->node
[0].body
->avel
);
3450 if (joint
->node
[1].body
) rate
-= dDOT (axis
, joint
->node
[1].body
->avel
);
3457 dReal
dJointGetPUAngle2Rate (dJointID j
)
3459 dxJointPU
* joint
= (dxJointPU
*) j
;
3460 dUASSERT (joint
,"bad joint argument");
3461 dUASSERT (joint
->vtable
== &__dPU_vtable
,
3462 "joint is not a Prismatic and Universal");
3464 if (joint
->node
[0].body
) {
3467 if (joint
->flags
& dJOINT_REVERSE
)
3468 getAxis (joint
,axis
,joint
->axis1
);
3470 getAxis2 (joint
,axis
,joint
->axis2
);
3472 dReal rate
= dDOT (axis
, joint
->node
[0].body
->avel
);
3473 if (joint
->node
[1].body
) rate
-= dDOT (axis
, joint
->node
[1].body
->avel
);
3480 void dJointSetPUParam (dJointID j
, int parameter
, dReal value
)
3482 dxJointPU
* joint
= (dxJointPU
*) j
;
3483 dUASSERT (joint
,"bad joint argument");
3484 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3486 switch ( parameter
& 0xff00 ) {
3488 joint
->limot1
.set (parameter
,value
);
3491 joint
->limot2
.set (parameter
& 0xff,value
);
3494 joint
->limotP
.set (parameter
& 0xff,value
);
3499 void dJointGetPUAnchor (dJointID j
, dVector3 result
)
3501 dxJointPU
* joint
= (dxJointPU
*) j
;
3502 dUASSERT (joint
,"bad joint argument");
3503 dUASSERT (result
,"bad result argument");
3504 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3506 if (joint
->node
[1].body
)
3507 getAnchor2 (joint
,result
,joint
->anchor2
);
3509 // result[i] = joint->anchor2[i];
3510 dOPE (result
, =, joint
->anchor2
);
3515 void dJointGetPUAxis1 (dJointID j
, dVector3 result
)
3517 dxJointPU
* joint
= (dxJointPU
*) j
;
3518 dUASSERT (joint
,"bad joint argument");
3519 dUASSERT (result
,"bad result argument");
3520 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3521 getAxis (joint
, result
, joint
->axis1
);
3524 void dJointGetPUAxis2 (dJointID j
, dVector3 result
)
3526 dxJointPU
* joint
= (dxJointPU
*) j
;
3527 dUASSERT (joint
,"bad joint argument");
3528 dUASSERT (result
,"bad result argument");
3529 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3530 getAxis (joint
, result
, joint
->axis2
);
3534 * @brief Get the prismatic axis
3537 * @note This function was added for convenience it is the same as
3540 void dJointGetPUAxisP (dJointID id
, dVector3 result
)
3542 dJointGetPUAxis3 (id
, result
);
3546 void dJointGetPUAxis3 (dJointID j
, dVector3 result
)
3548 dxJointPU
* joint
= (dxJointPU
*) j
;
3549 dUASSERT (joint
,"bad joint argument");
3550 dUASSERT (result
,"bad result argument");
3551 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not a Prismatic and Universal");
3552 getAxis (joint
, result
, joint
->axisP1
);
3555 dReal
dJointGetPUParam (dJointID j
, int parameter
)
3557 dxJointPU
* joint
= (dxJointPU
*) j
;
3558 dUASSERT (joint
,"bad joint argument");
3559 dUASSERT (joint
->vtable
== &__dPU_vtable
,"joint is not Prismatic and Universal");
3561 switch ( parameter
& 0xff00 ) {
3563 return joint
->limot1
.get (parameter
);
3566 return joint
->limot2
.get (parameter
& 0xff);
3569 return joint
->limotP
.get (parameter
& 0xff);
3576 dxJoint::Vtable __dPU_vtable
= {
3578 (dxJoint::init_fn
*) PUInit
,
3579 (dxJoint::getInfo1_fn
*) PUGetInfo1
,
3580 (dxJoint::getInfo2_fn
*) PUGetInfo2
,
3587 //****************************************************************************
3591 static void PistonInit (dxJointPiston
*j
)
3593 dSetZero (j
->axis1
,4);
3594 dSetZero (j
->axis2
,4);
3599 dSetZero (j
->qrel
,4);
3601 dSetZero (j
->anchor1
,4);
3602 dSetZero (j
->anchor2
,4);
3604 j
->limotP
.init (j
->world
);
3606 j
->limotR
.init (j
->world
);
3610 dReal
dJointGetPistonPosition (dJointID j
)
3612 dxJointPiston
* joint
= (dxJointPiston
*) j
;
3613 dUASSERT (joint
,"bad joint argument");
3614 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston");
3616 if (joint
->node
[0].body
) {
3618 // get the anchor (or offset) in global coordinates
3619 dMULTIPLY0_331 (q
,joint
->node
[0].body
->posr
.R
,joint
->anchor1
);
3621 if (joint
->node
[1].body
) {
3623 // get the anchor2 in global coordinates
3624 dMULTIPLY0_331 (anchor2
,joint
->node
[1].body
->posr
.R
,joint
->anchor2
);
3626 q
[0] = ( (joint
->node
[0].body
->posr
.pos
[0] + q
[0]) -
3627 (joint
->node
[1].body
->posr
.pos
[0] + anchor2
[0]) );
3628 q
[1] = ( (joint
->node
[0].body
->posr
.pos
[1] + q
[1]) -
3629 (joint
->node
[1].body
->posr
.pos
[1] + anchor2
[1]) );
3630 q
[2] = ( (joint
->node
[0].body
->posr
.pos
[2] + q
[2]) -
3631 (joint
->node
[1].body
->posr
.pos
[2] + anchor2
[2]) );
3634 // N.B. When there is no body 2 the joint->anchor2 is already in
3635 // global coordinates
3636 q
[0] = ( (joint
->node
[0].body
->posr
.pos
[0] + q
[0]) -
3637 (joint
->anchor2
[0]) );
3638 q
[1] = ( (joint
->node
[0].body
->posr
.pos
[1] + q
[1]) -
3639 (joint
->anchor2
[1]) );
3640 q
[2] = ( (joint
->node
[0].body
->posr
.pos
[2] + q
[2]) -
3641 (joint
->anchor2
[2]) );
3644 // get axis in global coordinates
3646 dMULTIPLY0_331 (ax
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
3651 dDEBUGMSG ("The function always return 0 since no body are attached");
3656 dReal
dJointGetPistonPositionRate (dJointID j
)
3658 dxJointPiston
* joint
= (dxJointPiston
*) j
;
3659 dUASSERT (joint
,"bad joint argument");
3660 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston");
3662 // get axis in global coordinates
3664 dMULTIPLY0_331 (ax
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
3666 // The linear velocity created by the rotation can be discarded since
3667 // the rotation is along the prismatic axis and this rotation don't create
3668 // linear velocity in the direction of the prismatic axis.
3669 if (joint
->node
[1].body
) {
3670 return ( dDOT (ax
,joint
->node
[0].body
->lvel
) -
3671 dDOT (ax
,joint
->node
[1].body
->lvel
) );
3674 return dDOT (ax
,joint
->node
[0].body
->lvel
);
3679 dReal
dJointGetPistonAngle (dJointID j
)
3681 dxJointPiston
* joint
= (dxJointPiston
*) j
;
3683 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston")
3685 if (joint
->node
[0].body
) {
3686 dReal ang
= getHingeAngle (joint
->node
[0].body
,joint
->node
[1].body
,joint
->axis1
,
3688 if (joint
->flags
& dJOINT_REVERSE
)
3697 dReal
dJointGetPistonAngleRate (dJointID j
)
3699 dxJointPiston
* joint
= (dxJointPiston
*) j
;
3701 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston");
3703 if (joint
->node
[0].body
) {
3705 dMULTIPLY0_331 (axis
,joint
->node
[0].body
->posr
.R
,joint
->axis1
);
3706 dReal rate
= dDOT (axis
,joint
->node
[0].body
->avel
);
3707 if (joint
->node
[1].body
) rate
-= dDOT (axis
,joint
->node
[1].body
->avel
);
3708 if (joint
->flags
& dJOINT_REVERSE
) rate
= - rate
;
3715 static void PistonGetInfo1 (dxJointPiston
*j
, dxJoint::Info1
*info
)
3717 info
->nub
= 4; // Number of unbound variables
3718 // The only bound variable is one linear displacement
3720 info
->m
= 4; // Default number of constraint row
3722 // see if we're at a joint limit.
3723 j
->limotP
.limit
= 0;
3724 if ( (j
->limotP
.lostop
> -dInfinity
|| j
->limotP
.histop
< dInfinity
) &&
3725 j
->limotP
.lostop
<= j
->limotP
.histop
) {
3726 // measure joint position
3727 dReal pos
= dJointGetPistonPosition (j
);
3728 j
->limotP
.testRotationalLimit (pos
); // N.B. The fucntion is ill named
3731 // powered Piston or at limits needs an extra constraint row
3732 if (j
->limotP
.limit
|| j
->limotP
.fmax
> 0) info
->m
++;
3736 // see if we're at a joint limit.
3737 j
->limotR
.limit
= 0;
3738 if ( (j
->limotR
.lostop
> -dInfinity
|| j
->limotR
.histop
< dInfinity
) &&
3739 j
->limotR
.lostop
<= j
->limotR
.histop
) {
3740 // measure joint position
3741 dReal angle
= getHingeAngle (j
->node
[0].body
, j
->node
[1].body
, j
->axis1
,
3743 j
->limotR
.testRotationalLimit (angle
);
3746 // powered Piston or at limits needs an extra constraint row
3747 if (j
->limotR
.limit
|| j
->limotR
.fmax
> 0) info
->m
++;
3752 static void PistonGetInfo2 (dxJointPiston
*joint
, dxJoint::Info2
*info
)
3755 const int s1
= info
->rowskip
;
3756 const int s2
=2*s1
, s3
=3*s1
/*, s4=4*s1*/;
3758 const dReal k
= info
->fps
* info
->erp
;
3761 // Pull out pos and R for both bodies. also get the `connection'
3762 // vector pos2-pos1.
3764 dReal
*pos1
,*pos2
,*R1
,*R2
;
3765 dVector3 dist
; // Current position of body_1 w.r.t "anchor"
3766 // 2 bodies anchor is center of body 2
3767 // 1 bodies anchor is origin
3770 pos1
= joint
->node
[0].body
->posr
.pos
;
3771 R1
= joint
->node
[0].body
->posr
.R
;
3773 if (joint
->node
[1].body
) {
3774 pos2
= joint
->node
[1].body
->posr
.pos
;
3775 R2
= joint
->node
[1].body
->posr
.R
;
3777 dMULTIPLY0_331 (anchor2
, R2
, joint
->anchor2
);
3778 dist
[0] = anchor2
[0] + pos2
[0] - pos1
[0];
3779 dist
[1] = anchor2
[1] + pos2
[1] - pos1
[1];
3780 dist
[2] = anchor2
[2] + pos2
[2] - pos1
[2];
3783 // pos2 = 0; // N.B. We can do that to be safe but it is no necessary
3784 // R2 = 0; // N.B. We can do that to be safe but it is no necessary
3785 // dist[i] = joint->anchor2[i] - pos1[ui];
3786 dOPE2 (dist
, =, joint
->anchor2
, -, pos1
);
3789 // ======================================================================
3790 // Work on the angular part (i.e. row 0, 1)
3791 // Set the two orientation rows. The rotoide axis should be the only
3792 // unconstrained rotational axis, the angular velocity of the two bodies
3793 // perpendicular to the rotoide axis should be equal.
3794 // Thus the constraint equations are:
3797 // where p and q are unit vectors normal to the rotoide axis, and w1 and w2
3798 // are the angular velocity vectors of the two bodies.
3799 // Since the rotoide axis is the same as the prismatic axis.
3802 // Also, compute the right hand side (RHS) of the rotation constraint equation set.
3803 // The first 2 element will result in the relative angular velocity of the two
3804 // bodies along axis p and q. This is set to bring the rotoide back into alignment.
3805 // if `theta' is the angle between ax1 and ax2, we need an angular velocity
3806 // along u to cover angle erp*theta in one step :
3807 // |angular_velocity| = angle/time = erp*theta / stepsize
3808 // = (erp*fps) * theta
3809 // angular_velocity = |angular_velocity| * u
3810 // = (erp*fps) * theta * u
3811 // where rotation along unit length axis u by theta brings body 2's frame
3813 // if theta is smallish, sin(theta) ~= theta and cos(theta) ~= 1
3814 // where the quaternion of the relative rotation between the two bodies is
3815 // quat = [cos(theta/2) sin(theta/2)*u]
3816 // quat = [1 theta/2*u]
3818 // 2 * q[1+i] = theta * u[i]
3820 // Since there is no constraint along the rotoide axis
3821 // only along p and q that we want the same angular velocity and need to reduce
3824 dMULTIPLY0_331 (ax1
, joint
->node
[0].body
->posr
.R
, joint
->axis1
);
3826 // Find the 2 axis perpendicular to the rotoide axis.
3827 dPlaneSpace (ax1
, p
, q
);
3830 dOPE ( (info
->J1a
) + s0
, =, p
);
3831 dOPE ( (info
->J1a
) + s1
, =, q
);
3834 if (joint
->node
[1].body
) {
3836 // info->J2a[s0+i] = -p[i]
3837 dOPE ( (info
->J2a
) + s0
, = -, p
);
3838 dOPE ( (info
->J2a
) + s1
, = -, q
);
3841 // Some math for the RHS
3843 dMULTIPLY0_331 (ax2
, R2
, joint
->axis2
);
3844 dCROSS (b
,=,ax1
, ax2
);
3847 // Some math for the RHS
3848 dCROSS (b
,=,ax1
,joint
->axis2
);
3852 info
->c
[0] = k
* dDOT (p
, b
);
3853 info
->c
[1] = k
* dDOT (q
, b
);
3855 // ======================================================================
3856 // Work on the linear part (i.e row 2,3)
3857 // p2 + R2 anchor2' = p1 + R1 dist'
3858 // v2 + w2 R2 anchor2' + R2 d(anchor2')/dt = v1 + w1 R1 dist' + R1 d(dist')/dt
3859 // v2 + w2 x anchor2 = v1 + w1 x dist + v_p
3860 // v_p is speed of prismatic joint (i.e. elongation rate)
3861 // Since the constraints are perpendicular to v_p we have:
3862 // p . v_p = 0 and q . v_p = 0
3863 // Along p and q we have (since sliding along the prismatic axis is disregarded):
3864 // u . ( v2 + w2 x anchor2 = v1 + w1 x dist + v_p) ( where u is p or q )
3866 // u . v2 + u. w2 x anchor2 = u . v1 + u . w1 x dist
3868 // u . v1 - u . v2 + u . w1 x dist - u2 . w2 x anchor2 = 0
3869 // using the fact that (a x b = - b x a)
3870 // u . v1 - u . v2 - u . dist x w1 + u . anchor2 x w2 = 0
3871 // With the help of the triple product:
3872 // i.e. a . b x c = b . c x a = c . a x b or a . b x c = a x b . c
3873 // Ref: http://mathworld.wolfram.com/ScalarTripleProduct.html
3874 // u . v1 - u . v2 - u x dist . w1 + u x anchor2 . w2 = 0
3875 // u . v1 - u . v2 + dist x u . w1 - u x anchor2 . w2 = 0
3877 // Coeff for 1er line of: J1l => p, J2l => -p
3878 // Coeff for 2er line of: J1l => q, J2l => -q
3879 // Coeff for 1er line of: J1a => dist x p, J2a => p x anchor2
3880 // Coeff for 2er line of: J1a => dist x q, J2a => q x anchor2
3882 dCROSS ( (info
->J1a
) +s2
, = , dist
, p
);
3884 dCROSS ( (info
->J1a
) +s3
, = , dist
, q
);
3886 dOPE ( (info
->J1l
) + s2
, =, p
);
3887 dOPE ( (info
->J1l
) + s3
, =, q
);
3889 if (joint
->node
[1].body
) {
3890 // q x anchor2 instead of anchor2 x q since we want the negative value
3891 dCROSS ( (info
->J2a
) +s2
, = , p
, anchor2
);
3893 // The cross product is in reverse order since we want the negative value
3894 dCROSS ( (info
->J2a
) +s3
, = , q
, anchor2
);
3896 // info->J2l[s2+i] = -p[i];
3897 dOPE ( (info
->J2l
) + s2
, = -, p
);
3898 dOPE ( (info
->J2l
) + s3
, = -, q
);
3902 // We want to make correction for motion not in the line of the axis
3903 // We calculate the displacement w.r.t. the "anchor" pt.
3904 // i.e. Find the difference between the current position and the initial
3905 // position along the constrained axies (i.e. axis p and q).
3906 // The bodies can move w.r.t each other only along the prismatic axis
3908 // Compute the RHS of rows 2 and 3
3910 dMULTIPLY0_331 (err
, R1
, joint
->anchor1
);
3911 dOPE2 (err
, =, dist
, -, err
);
3913 info
->c
[2] = k
* dDOT (p
, err
);
3914 info
->c
[3] = k
* dDOT (q
, err
);
3917 int row
= 4 + joint
->limotP
.addLimot (joint
, info
, 4, ax1
, 0);
3918 joint
->limotR
.addLimot (joint
,info
,row
,ax1
,1);
3921 static void PistonComputeInitialRelativeRotation (dxJointPiston
*joint
)
3923 if (joint
->node
[0].body
) {
3924 if (joint
->node
[1].body
) {
3925 dQMultiply1 (joint
->qrel
,joint
->node
[0].body
->q
,joint
->node
[1].body
->q
);
3928 // set joint->qrel to the transpose of the first body q
3929 joint
->qrel
[0] = joint
->node
[0].body
->q
[0];
3930 for (int i
=1; i
<4; i
++) joint
->qrel
[i
] = -joint
->node
[0].body
->q
[i
];
3931 // WARNING do we need the - in -joint->node[0].body->q[i]; or not
3936 void dJointSetPistonAnchor (dJointID j
, dReal x
, dReal y
, dReal z
)
3938 dxJointPiston
* joint
= (dxJointPiston
*) j
;
3939 dUASSERT (joint
,"bad joint argument");
3940 dUASSERT (joint
->vtable
== &__dPiston_vtable
,"joint is not a Piston");
3942 setAnchors (joint
,x
,y
,z
,joint
->anchor1
,joint
->anchor2
);
3943 PistonComputeInitialRelativeRotation (joint
);
3948 void dJointGetPistonAnchor (dJointID j
, dVector3 result
)
3950 dxJointPiston
* joint
= (dxJointPiston
*) j
;
3951 dUASSERT (joint
,"bad joint argument");
3952 dUASSERT (result
,"bad result argument");
3953 dUASSERT (joint
->vtable
== &__dPiston_vtable
,"joint is not a piston");
3954 if (joint
->flags
& dJOINT_REVERSE
)
3955 getAnchor2 (joint
,result
,joint
->anchor2
);
3957 getAnchor (joint
,result
,joint
->anchor1
);
3961 void dJointGetPistonAnchor2 (dJointID j
, dVector3 result
)
3963 dxJointPiston
* joint
= (dxJointPiston
*) j
;
3964 dUASSERT (joint
,"bad joint argument");
3965 dUASSERT (result
,"bad result argument");
3966 dUASSERT (joint
->vtable
== &__dPiston_vtable
,"joint is not a piston");
3967 if (joint
->flags
& dJOINT_REVERSE
)
3968 getAnchor (joint
,result
,joint
->anchor1
);
3970 getAnchor2 (joint
,result
,joint
->anchor2
);
3975 void dJointSetPistonAxis (dJointID j
, dReal x
, dReal y
, dReal z
)
3977 dxJointPiston
* joint
= (dxJointPiston
*) j
;
3978 dUASSERT (joint
,"bad joint argument");
3979 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston");
3981 setAxes (joint
,x
,y
,z
, joint
->axis1
, joint
->axis2
);
3983 PistonComputeInitialRelativeRotation (joint
);
3987 void dJointSetPistonAxisDelta (dJointID j
, dReal x
, dReal y
, dReal z
,
3988 dReal dx
, dReal dy
, dReal dz
)
3990 dxJointPiston
* joint
= (dxJointPiston
*) j
;
3991 dUASSERT (joint
,"bad joint argument");
3992 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston");
3993 setAxes (joint
,x
,y
,z
,joint
->axis1
, joint
->axis2
);
3995 PistonComputeInitialRelativeRotation (joint
);
3998 if (joint
->node
[1].body
) {
3999 c
[0] = ( joint
->node
[0].body
->posr
.pos
[0] -
4000 joint
->node
[1].body
->posr
.pos
[0] - dx
);
4001 c
[1] = ( joint
->node
[0].body
->posr
.pos
[1] -
4002 joint
->node
[1].body
->posr
.pos
[1] - dy
);
4003 c
[2] = ( joint
->node
[0].body
->posr
.pos
[2] -
4004 joint
->node
[1].body
->posr
.pos
[2] - dz
);
4006 else if (joint
->node
[0].body
) {
4007 c
[0] = joint
->node
[0].body
->posr
.pos
[0] - dx
;
4008 c
[1] = joint
->node
[0].body
->posr
.pos
[1] - dy
;
4009 c
[2] = joint
->node
[0].body
->posr
.pos
[2] - dz
;
4012 // Convert into frame of body 1
4013 dMULTIPLY1_331 (joint
->anchor1
,joint
->node
[0].body
->posr
.R
,c
);
4018 void dJointGetPistonAxis (dJointID j
, dVector3 result
)
4020 dxJointPiston
* joint
= (dxJointPiston
*) j
;
4021 dUASSERT (joint
,"bad joint argument");
4022 dUASSERT (result
,"bad result argument");
4023 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston");
4025 getAxis (joint
,result
,joint
->axis1
);
4028 void dJointSetPistonParam (dJointID j
, int parameter
, dReal value
)
4030 dxJointPiston
* joint
= (dxJointPiston
*) j
;
4031 dUASSERT (joint
,"bad joint argument");
4032 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston");
4034 if ( (parameter
& 0xff00) == 0x100) {
4035 joint
->limotR
.set (parameter
& 0xff, value
);
4038 joint
->limotP
.set (parameter
, value
);
4043 dReal
dJointGetPistonParam (dJointID j
, int parameter
)
4045 dxJointPiston
* joint
= (dxJointPiston
*) j
;
4046 dUASSERT (joint
,"bad joint argument");
4047 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston");
4049 if ( (parameter
& 0xff00) == 0x100) {
4050 return joint
->limotR
.get (parameter
& 0xff);
4053 return joint
->limotP
.get (parameter
);
4058 void dJointAddPistonForce (dJointID j
, dReal force
)
4060 dxJointPiston
* joint
= (dxJointPiston
*) j
;
4061 dUASSERT (joint
,"bad joint argument");
4062 dUASSERT (joint
->vtable
== &__dPiston_vtable
, "joint is not a Piston");
4064 if (joint
->flags
& dJOINT_REVERSE
)
4068 getAxis (joint
,axis
,joint
->axis1
);
4070 dOPEC (axis
, *=, force
);
4073 if (joint
->node
[0].body
!= 0)
4074 dBodyAddForce (joint
->node
[0].body
,axis
[0],axis
[1],axis
[2]);
4075 if (joint
->node
[1].body
!= 0)
4076 dBodyAddForce (joint
->node
[1].body
, -axis
[0], -axis
[1], -axis
[2]);
4078 if (joint
->node
[0].body
!= 0 && joint
->node
[1].body
!= 0) {
4079 // Case where we don't need ltd since center of mass of both bodies
4080 // pass by the anchor point '*' when travelling along the prismatic axis.
4084 // | |---------------*-------------| | ---> prismatic axis
4088 // Case where we need ltd
4094 // -----*----- ---> prismatic axis
4105 // In real life force apply at the '*' point
4106 // But in ODE the force are applied on the center of mass of Body_1 and Body_2
4107 // So we have to add torques on both bodies to compensate for that when there
4108 // is an offset between the anchor point and the center of mass of both bodies.
4110 // We need to add to each body T = r x F
4111 // Where r is the distance between the cm and '*'
4113 dVector3 ltd
; // Linear Torque Decoupling vector (a torque)
4114 dVector3 c
; // Distance of the body w.r.t the anchor
4115 // N.B. The distance along the prismatic axis might not
4116 // not be included in this variable since it won't add
4117 // anything to the ltd.
4119 // Calculate the distance of the body w.r.t the anchor
4121 // The anchor1 of body1 can be used since:
4122 // Real anchor = Position of body 1 + anchor + d* axis1 = anchor in world frame
4123 // d is the position of the prismatic joint (i.e. elongation)
4124 // Since axis1 x axis1 == 0
4125 // We can do the following.
4126 dMULTIPLY0_331 (c
, joint
->node
[0].body
->posr
.R
, joint
->anchor1
);
4127 dCROSS (ltd
,=, c
, axis
);
4128 dBodyAddTorque (joint
->node
[0].body
, ltd
[0],ltd
[1], ltd
[2]);
4131 dMULTIPLY0_331 (c
, joint
->node
[1].body
->posr
.R
, joint
->anchor2
);
4132 dCROSS (ltd
,=, c
, axis
);
4133 dBodyAddTorque (joint
->node
[1].body
, ltd
[0],ltd
[1], ltd
[2]);
4138 dxJoint::Vtable __dPiston_vtable
= {
4139 sizeof (dxJointPiston
),
4140 (dxJoint::init_fn
*) PistonInit
,
4141 (dxJoint::getInfo1_fn
*) PistonGetInfo1
,
4142 (dxJoint::getInfo2_fn
*) PistonGetInfo2
,
4145 //****************************************************************************
4148 static void amotorInit (dxJointAMotor
*j
)
4152 j
->mode
= dAMotorUser
;
4153 for (i
=0; i
<3; i
++) {
4155 dSetZero (j
->axis
[i
],4);
4156 j
->limot
[i
].init (j
->world
);
4159 dSetZero (j
->reference1
,4);
4160 dSetZero (j
->reference2
,4);
4164 // compute the 3 axes in global coordinates
4166 static void amotorComputeGlobalAxes (dxJointAMotor
*joint
, dVector3 ax
[3])
4168 if (joint
->mode
== dAMotorEuler
) {
4169 // special handling for euler mode
4170 dMULTIPLY0_331 (ax
[0],joint
->node
[0].body
->posr
.R
,joint
->axis
[0]);
4171 if (joint
->node
[1].body
) {
4172 dMULTIPLY0_331 (ax
[2],joint
->node
[1].body
->posr
.R
,joint
->axis
[2]);
4175 ax
[2][0] = joint
->axis
[2][0];
4176 ax
[2][1] = joint
->axis
[2][1];
4177 ax
[2][2] = joint
->axis
[2][2];
4179 dCROSS (ax
[1],=,ax
[2],ax
[0]);
4180 dNormalize3 (ax
[1]);
4183 for (int i
=0; i
< joint
->num
; i
++) {
4184 if (joint
->rel
[i
] == 1) {
4186 dMULTIPLY0_331 (ax
[i
],joint
->node
[0].body
->posr
.R
,joint
->axis
[i
]);
4188 else if (joint
->rel
[i
] == 2) {
4190 if (joint
->node
[1].body
) { // jds: don't assert, just ignore
4191 dMULTIPLY0_331 (ax
[i
],joint
->node
[1].body
->posr
.R
,joint
->axis
[i
]);
4195 // global - just copy it
4196 ax
[i
][0] = joint
->axis
[i
][0];
4197 ax
[i
][1] = joint
->axis
[i
][1];
4198 ax
[i
][2] = joint
->axis
[i
][2];
4205 static void amotorComputeEulerAngles (dxJointAMotor
*joint
, dVector3 ax
[3])
4208 // global axes already calculated --> ax
4209 // axis[0] is relative to body 1 --> global ax[0]
4210 // axis[2] is relative to body 2 --> global ax[2]
4211 // ax[1] = ax[2] x ax[0]
4212 // original ax[0] and ax[2] are perpendicular
4213 // reference1 is perpendicular to ax[0] (in body 1 frame)
4214 // reference2 is perpendicular to ax[2] (in body 2 frame)
4215 // all ax[] and reference vectors are unit length
4217 // calculate references in global frame
4219 dMULTIPLY0_331 (ref1
,joint
->node
[0].body
->posr
.R
,joint
->reference1
);
4220 if (joint
->node
[1].body
) {
4221 dMULTIPLY0_331 (ref2
,joint
->node
[1].body
->posr
.R
,joint
->reference2
);
4224 ref2
[0] = joint
->reference2
[0];
4225 ref2
[1] = joint
->reference2
[1];
4226 ref2
[2] = joint
->reference2
[2];
4229 // get q perpendicular to both ax[0] and ref1, get first euler angle
4231 dCROSS (q
,=,ax
[0],ref1
);
4232 joint
->angle
[0] = -dAtan2 (dDOT(ax
[2],q
),dDOT(ax
[2],ref1
));
4234 // get q perpendicular to both ax[0] and ax[1], get second euler angle
4235 dCROSS (q
,=,ax
[0],ax
[1]);
4236 joint
->angle
[1] = -dAtan2 (dDOT(ax
[2],ax
[0]),dDOT(ax
[2],q
));
4238 // get q perpendicular to both ax[1] and ax[2], get third euler angle
4239 dCROSS (q
,=,ax
[1],ax
[2]);
4240 joint
->angle
[2] = -dAtan2 (dDOT(ref2
,ax
[1]), dDOT(ref2
,q
));
4244 // set the reference vectors as follows:
4245 // * reference1 = current axis[2] relative to body 1
4246 // * reference2 = current axis[0] relative to body 2
4247 // this assumes that:
4248 // * axis[0] is relative to body 1
4249 // * axis[2] is relative to body 2
4251 static void amotorSetEulerReferenceVectors (dxJointAMotor
*j
)
4253 if (j
->node
[0].body
&& j
->node
[1].body
) {
4254 dVector3 r
; // axis[2] and axis[0] in global coordinates
4255 dMULTIPLY0_331 (r
,j
->node
[1].body
->posr
.R
,j
->axis
[2]);
4256 dMULTIPLY1_331 (j
->reference1
,j
->node
[0].body
->posr
.R
,r
);
4257 dMULTIPLY0_331 (r
,j
->node
[0].body
->posr
.R
,j
->axis
[0]);
4258 dMULTIPLY1_331 (j
->reference2
,j
->node
[1].body
->posr
.R
,r
);
4262 // else if (j->node[0].body) {
4263 // dMULTIPLY1_331 (j->reference1,j->node[0].body->posr.R,j->axis[2]);
4264 // dMULTIPLY0_331 (j->reference2,j->node[0].body->posr.R,j->axis[0]);
4266 // We want to handle angular motors attached to passive geoms
4267 dVector3 r
; // axis[2] and axis[0] in global coordinates
4268 r
[0] = j
->axis
[2][0]; r
[1] = j
->axis
[2][1]; r
[2] = j
->axis
[2][2]; r
[3] = j
->axis
[2][3];
4269 dMULTIPLY1_331 (j
->reference1
,j
->node
[0].body
->posr
.R
,r
);
4270 dMULTIPLY0_331 (r
,j
->node
[0].body
->posr
.R
,j
->axis
[0]);
4271 j
->reference2
[0] += r
[0]; j
->reference2
[1] += r
[1];
4272 j
->reference2
[2] += r
[2]; j
->reference2
[3] += r
[3];
4277 static void amotorGetInfo1 (dxJointAMotor
*j
, dxJoint::Info1
*info
)
4282 // compute the axes and angles, if in euler mode
4283 if (j
->mode
== dAMotorEuler
) {
4285 amotorComputeGlobalAxes (j
,ax
);
4286 amotorComputeEulerAngles (j
,ax
);
4289 // see if we're powered or at a joint limit for each axis
4290 for (int i
=0; i
< j
->num
; i
++) {
4291 if (j
->limot
[i
].testRotationalLimit (j
->angle
[i
]) ||
4292 j
->limot
[i
].fmax
> 0) {
4299 static void amotorGetInfo2 (dxJointAMotor
*joint
, dxJoint::Info2
*info
)
4303 // compute the axes (if not global)
4305 amotorComputeGlobalAxes (joint
,ax
);
4307 // in euler angle mode we do not actually constrain the angular velocity
4308 // along the axes axis[0] and axis[2] (although we do use axis[1]) :
4310 // to get constrain w2-w1 along ...not
4311 // ------ --------------------- ------
4312 // d(angle[0])/dt = 0 ax[1] x ax[2] ax[0]
4313 // d(angle[1])/dt = 0 ax[1]
4314 // d(angle[2])/dt = 0 ax[0] x ax[1] ax[2]
4316 // constraining w2-w1 along an axis 'a' means that a'*(w2-w1)=0.
4317 // to prove the result for angle[0], write the expression for angle[0] from
4318 // GetInfo1 then take the derivative. to prove this for angle[2] it is
4319 // easier to take the euler rate expression for d(angle[2])/dt with respect
4320 // to the components of w and set that to 0.
4327 dVector3 ax0_cross_ax1
;
4328 dVector3 ax1_cross_ax2
;
4329 if (joint
->mode
== dAMotorEuler
) {
4330 dCROSS (ax0_cross_ax1
,=,ax
[0],ax
[1]);
4331 axptr
[2] = &ax0_cross_ax1
;
4332 dCROSS (ax1_cross_ax2
,=,ax
[1],ax
[2]);
4333 axptr
[0] = &ax1_cross_ax2
;
4337 for (i
=0; i
< joint
->num
; i
++) {
4338 row
+= joint
->limot
[i
].addLimot (joint
,info
,row
,*(axptr
[i
]),1);
4343 void dJointSetAMotorNumAxes (dJointID j
, int num
)
4345 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4346 dAASSERT(joint
&& num
>= 0 && num
<= 3);
4347 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4348 if (joint
->mode
== dAMotorEuler
) {
4352 if (num
< 0) num
= 0;
4353 if (num
> 3) num
= 3;
4359 void dJointSetAMotorAxis (dJointID j
, int anum
, int rel
, dReal x
, dReal y
, dReal z
)
4361 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4362 dAASSERT(joint
&& anum
>= 0 && anum
<= 2 && rel
>= 0 && rel
<= 2);
4363 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4364 dUASSERT(!(!joint
->node
[1].body
&& (joint
->flags
& dJOINT_REVERSE
) && rel
== 1),"no first body, can't set axis rel=1");
4365 dUASSERT(!(!joint
->node
[1].body
&& !(joint
->flags
& dJOINT_REVERSE
) && rel
== 2),"no second body, can't set axis rel=2");
4366 if (anum
< 0) anum
= 0;
4367 if (anum
> 2) anum
= 2;
4369 // adjust rel to match the internal body order
4370 if (!joint
->node
[1].body
&& rel
==2) rel
= 1;
4372 joint
->rel
[anum
] = rel
;
4374 // x,y,z is always in global coordinates regardless of rel, so we may have
4375 // to convert it to be relative to a body
4383 dMULTIPLY1_331 (joint
->axis
[anum
],joint
->node
[0].body
->posr
.R
,r
);
4386 // don't assert; handle the case of attachment to a bodiless geom
4387 if (joint
->node
[1].body
) { // jds
4388 dMULTIPLY1_331 (joint
->axis
[anum
],joint
->node
[1].body
->posr
.R
,r
);
4391 joint
->axis
[anum
][0] = r
[0]; joint
->axis
[anum
][1] = r
[1];
4392 joint
->axis
[anum
][2] = r
[2]; joint
->axis
[anum
][3] = r
[3];
4397 joint
->axis
[anum
][0] = r
[0];
4398 joint
->axis
[anum
][1] = r
[1];
4399 joint
->axis
[anum
][2] = r
[2];
4401 dNormalize3 (joint
->axis
[anum
]);
4402 if (joint
->mode
== dAMotorEuler
) amotorSetEulerReferenceVectors (joint
);
4406 void dJointSetAMotorAngle (dJointID j
, int anum
, dReal angle
)
4408 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4409 dAASSERT(joint
&& anum
>= 0 && anum
< 3);
4410 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4411 if (joint
->mode
== dAMotorUser
) {
4412 if (anum
< 0) anum
= 0;
4413 if (anum
> 3) anum
= 3;
4414 joint
->angle
[anum
] = angle
;
4419 void dJointSetAMotorParam (dJointID j
, int parameter
, dReal value
)
4421 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4423 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4424 int anum
= parameter
>> 8;
4425 if (anum
< 0) anum
= 0;
4426 if (anum
> 2) anum
= 2;
4428 joint
->limot
[anum
].set (parameter
, value
);
4432 void dJointSetAMotorMode (dJointID j
, int mode
)
4434 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4436 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4438 if (joint
->mode
== dAMotorEuler
) {
4440 amotorSetEulerReferenceVectors (joint
);
4445 int dJointGetAMotorNumAxes (dJointID j
)
4447 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4449 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4454 void dJointGetAMotorAxis (dJointID j
, int anum
, dVector3 result
)
4456 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4457 dAASSERT(joint
&& anum
>= 0 && anum
< 3);
4458 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4459 if (anum
< 0) anum
= 0;
4460 if (anum
> 2) anum
= 2;
4461 if (joint
->rel
[anum
] > 0) {
4462 if (joint
->rel
[anum
]==1) {
4463 dMULTIPLY0_331 (result
,joint
->node
[0].body
->posr
.R
,joint
->axis
[anum
]);
4466 if (joint
->node
[1].body
) { // jds
4467 dMULTIPLY0_331 (result
,joint
->node
[1].body
->posr
.R
,joint
->axis
[anum
]);
4470 result
[0] = joint
->axis
[anum
][0]; result
[1] = joint
->axis
[anum
][1];
4471 result
[2] = joint
->axis
[anum
][2]; result
[3] = joint
->axis
[anum
][3];
4476 result
[0] = joint
->axis
[anum
][0];
4477 result
[1] = joint
->axis
[anum
][1];
4478 result
[2] = joint
->axis
[anum
][2];
4483 int dJointGetAMotorAxisRel (dJointID j
, int anum
)
4485 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4486 dAASSERT(joint
&& anum
>= 0 && anum
< 3);
4487 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4488 if (anum
< 0) anum
= 0;
4489 if (anum
> 2) anum
= 2;
4490 return joint
->rel
[anum
];
4494 dReal
dJointGetAMotorAngle (dJointID j
, int anum
)
4496 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4497 dAASSERT(joint
&& anum
>= 0 && anum
< 3);
4498 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4499 if (anum
< 0) anum
= 0;
4500 if (anum
> 3) anum
= 3;
4501 return joint
->angle
[anum
];
4505 dReal
dJointGetAMotorAngleRate (dJointID j
, int anum
)
4507 //dxJointAMotor* joint = (dxJointAMotor*)j;
4509 dDebug (0,"not yet implemented");
4514 dReal
dJointGetAMotorParam (dJointID j
, int parameter
)
4516 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4518 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4519 int anum
= parameter
>> 8;
4520 if (anum
< 0) anum
= 0;
4521 if (anum
> 2) anum
= 2;
4523 return joint
->limot
[anum
].get (parameter
);
4527 int dJointGetAMotorMode (dJointID j
)
4529 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4531 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4536 void dJointAddAMotorTorques (dJointID j
, dReal torque1
, dReal torque2
, dReal torque3
)
4538 dxJointAMotor
* joint
= (dxJointAMotor
*)j
;
4541 dUASSERT(joint
->vtable
== &__damotor_vtable
,"joint is not an amotor");
4543 if (joint
->num
== 0)
4545 dUASSERT((joint
->flags
& dJOINT_REVERSE
) == 0, "dJointAddAMotorTorques not yet implemented for reverse AMotor joints");
4547 amotorComputeGlobalAxes (joint
,axes
);
4548 axes
[0][0] *= torque1
;
4549 axes
[0][1] *= torque1
;
4550 axes
[0][2] *= torque1
;
4551 if (joint
->num
>= 2) {
4552 axes
[0][0] += axes
[1][0] * torque2
;
4553 axes
[0][1] += axes
[1][1] * torque2
;
4554 axes
[0][2] += axes
[1][2] * torque2
;
4555 if (joint
->num
>= 3) {
4556 axes
[0][0] += axes
[2][0] * torque3
;
4557 axes
[0][1] += axes
[2][1] * torque3
;
4558 axes
[0][2] += axes
[2][2] * torque3
;
4562 if (joint
->node
[0].body
!= 0)
4563 dBodyAddTorque (joint
->node
[0].body
,axes
[0][0],axes
[0][1],axes
[0][2]);
4564 if (joint
->node
[1].body
!= 0)
4565 dBodyAddTorque(joint
->node
[1].body
, -axes
[0][0], -axes
[0][1], -axes
[0][2]);
4569 dxJoint::Vtable __damotor_vtable
= {
4570 sizeof(dxJointAMotor
),
4571 (dxJoint::init_fn
*) amotorInit
,
4572 (dxJoint::getInfo1_fn
*) amotorGetInfo1
,
4573 (dxJoint::getInfo2_fn
*) amotorGetInfo2
,
4578 //****************************************************************************
4580 static void lmotorInit (dxJointLMotor
*j
)
4585 dSetZero(j
->axis
[i
],4);
4586 j
->limot
[i
].init(j
->world
);
4590 static void lmotorComputeGlobalAxes (dxJointLMotor
*joint
, dVector3 ax
[3])
4592 for (int i
=0; i
< joint
->num
; i
++) {
4593 if (joint
->rel
[i
] == 1) {
4594 dMULTIPLY0_331 (ax
[i
],joint
->node
[0].body
->posr
.R
,joint
->axis
[i
]);
4596 else if (joint
->rel
[i
] == 2) {
4597 if (joint
->node
[1].body
) { // jds: don't assert, just ignore
4598 dMULTIPLY0_331 (ax
[i
],joint
->node
[1].body
->posr
.R
,joint
->axis
[i
]);
4601 ax
[i
][0] = joint
->axis
[i
][0];
4602 ax
[i
][1] = joint
->axis
[i
][1];
4603 ax
[i
][2] = joint
->axis
[i
][2];
4608 static void lmotorGetInfo1 (dxJointLMotor
*j
, dxJoint::Info1
*info
)
4612 for (int i
=0; i
< j
->num
; i
++) {
4613 if (j
->limot
[i
].fmax
> 0) {
4619 static void lmotorGetInfo2 (dxJointLMotor
*joint
, dxJoint::Info2
*info
)
4623 lmotorComputeGlobalAxes(joint
, ax
);
4625 for (int i
=0;i
<joint
->num
;i
++) {
4626 row
+= joint
->limot
[i
].addLimot(joint
,info
,row
,ax
[i
], 0);
4630 void dJointSetLMotorAxis (dJointID j
, int anum
, int rel
, dReal x
, dReal y
, dReal z
)
4632 dxJointLMotor
* joint
= (dxJointLMotor
*)j
;
4633 //for now we are ignoring rel!
4634 dAASSERT(joint
&& anum
>= 0 && anum
<= 2 && rel
>= 0 && rel
<= 2);
4635 dUASSERT(joint
->vtable
== &__dlmotor_vtable
,"joint is not an lmotor");
4636 if (anum
< 0) anum
= 0;
4637 if (anum
> 2) anum
= 2;
4639 if (!joint
->node
[1].body
&& rel
==2) rel
= 1; //ref 1
4641 joint
->rel
[anum
] = rel
;
4650 dMULTIPLY1_331 (joint
->axis
[anum
],joint
->node
[0].body
->posr
.R
,r
);
4652 //second body has to exists thanks to ref 1 line
4653 dMULTIPLY1_331 (joint
->axis
[anum
],joint
->node
[1].body
->posr
.R
,r
);
4656 joint
->axis
[anum
][0] = r
[0];
4657 joint
->axis
[anum
][1] = r
[1];
4658 joint
->axis
[anum
][2] = r
[2];
4661 dNormalize3 (joint
->axis
[anum
]);
4664 void dJointSetLMotorNumAxes (dJointID j
, int num
)
4666 dxJointLMotor
* joint
= (dxJointLMotor
*)j
;
4667 dAASSERT(joint
&& num
>= 0 && num
<= 3);
4668 dUASSERT(joint
->vtable
== &__dlmotor_vtable
,"joint is not an lmotor");
4669 if (num
< 0) num
= 0;
4670 if (num
> 3) num
= 3;
4674 void dJointSetLMotorParam (dJointID j
, int parameter
, dReal value
)
4676 dxJointLMotor
* joint
= (dxJointLMotor
*)j
;
4678 dUASSERT(joint
->vtable
== &__dlmotor_vtable
,"joint is not an lmotor");
4679 int anum
= parameter
>> 8;
4680 if (anum
< 0) anum
= 0;
4681 if (anum
> 2) anum
= 2;
4683 joint
->limot
[anum
].set (parameter
, value
);
4686 int dJointGetLMotorNumAxes (dJointID j
)
4688 dxJointLMotor
* joint
= (dxJointLMotor
*)j
;
4690 dUASSERT(joint
->vtable
== &__dlmotor_vtable
,"joint is not an lmotor");
4695 void dJointGetLMotorAxis (dJointID j
, int anum
, dVector3 result
)
4697 dxJointLMotor
* joint
= (dxJointLMotor
*)j
;
4698 dAASSERT(joint
&& anum
>= 0 && anum
< 3);
4699 dUASSERT(joint
->vtable
== &__dlmotor_vtable
,"joint is not an lmotor");
4700 if (anum
< 0) anum
= 0;
4701 if (anum
> 2) anum
= 2;
4702 result
[0] = joint
->axis
[anum
][0];
4703 result
[1] = joint
->axis
[anum
][1];
4704 result
[2] = joint
->axis
[anum
][2];
4707 dReal
dJointGetLMotorParam (dJointID j
, int parameter
)
4709 dxJointLMotor
* joint
= (dxJointLMotor
*)j
;
4711 dUASSERT(joint
->vtable
== &__dlmotor_vtable
,"joint is not an lmotor");
4712 int anum
= parameter
>> 8;
4713 if (anum
< 0) anum
= 0;
4714 if (anum
> 2) anum
= 2;
4716 return joint
->limot
[anum
].get (parameter
);
4719 dxJoint::Vtable __dlmotor_vtable
= {
4720 sizeof(dxJointLMotor
),
4721 (dxJoint::init_fn
*) lmotorInit
,
4722 (dxJoint::getInfo1_fn
*) lmotorGetInfo1
,
4723 (dxJoint::getInfo2_fn
*) lmotorGetInfo2
,
4728 //****************************************************************************
4731 static void fixedInit (dxJointFixed
*j
)
4733 dSetZero (j
->offset
,4);
4734 dSetZero (j
->qrel
,4);
4735 j
->erp
= j
->world
->global_erp
;
4736 j
->cfm
= j
->world
->global_cfm
;
4740 static void fixedGetInfo1 (dxJointFixed
*j
, dxJoint::Info1
*info
)
4747 static void fixedGetInfo2 (dxJointFixed
*joint
, dxJoint::Info2
*info
)
4749 int s
= info
->rowskip
;
4751 // Three rows for orientation
4752 setFixedOrientation(joint
, info
, joint
->qrel
, 3);
4754 // Three rows for position.
4758 info
->J1l
[2*s
+2] = 1;
4760 info
->erp
= joint
->erp
;
4761 info
->cfm
[0] = joint
->cfm
;
4762 info
->cfm
[1] = joint
->cfm
;
4763 info
->cfm
[2] = joint
->cfm
;
4766 dMULTIPLY0_331 (ofs
,joint
->node
[0].body
->posr
.R
,joint
->offset
);
4767 if (joint
->node
[1].body
) {
4768 dCROSSMAT (info
->J1a
,ofs
,s
,+,-);
4770 info
->J2l
[s
+1] = -1;
4771 info
->J2l
[2*s
+2] = -1;
4774 // set right hand side for the first three rows (linear)
4775 dReal k
= info
->fps
* info
->erp
;
4776 if (joint
->node
[1].body
) {
4777 for (int j
=0; j
<3; j
++)
4778 info
->c
[j
] = k
* (joint
->node
[1].body
->posr
.pos
[j
] -
4779 joint
->node
[0].body
->posr
.pos
[j
] + ofs
[j
]);
4782 for (int j
=0; j
<3; j
++)
4783 info
->c
[j
] = k
* (joint
->offset
[j
] - joint
->node
[0].body
->posr
.pos
[j
]);
4788 void dJointSetFixed (dJointID j
)
4790 dxJointFixed
* joint
= (dxJointFixed
*)j
;
4791 dUASSERT(joint
,"bad joint argument");
4792 dUASSERT(joint
->vtable
== &__dfixed_vtable
,"joint is not fixed");
4795 // This code is taken from sJointSetSliderAxis(), we should really put the
4796 // common code in its own function.
4797 // compute the offset between the bodies
4798 if (joint
->node
[0].body
) {
4799 if (joint
->node
[1].body
) {
4800 dQMultiply1 (joint
->qrel
,joint
->node
[0].body
->q
,joint
->node
[1].body
->q
);
4802 for (i
=0; i
<4; i
++) ofs
[i
] = joint
->node
[0].body
->posr
.pos
[i
];
4803 for (i
=0; i
<4; i
++) ofs
[i
] -= joint
->node
[1].body
->posr
.pos
[i
];
4804 dMULTIPLY1_331 (joint
->offset
,joint
->node
[0].body
->posr
.R
,ofs
);
4807 // set joint->qrel to the transpose of the first body's q
4808 joint
->qrel
[0] = joint
->node
[0].body
->q
[0];
4809 for (i
=1; i
<4; i
++) joint
->qrel
[i
] = -joint
->node
[0].body
->q
[i
];
4810 for (i
=0; i
<4; i
++) joint
->offset
[i
] = joint
->node
[0].body
->posr
.pos
[i
];
4815 void dxJointFixed::set (int num
, dReal value
)
4828 dReal
dxJointFixed::get (int num
)
4841 void dJointSetFixedParam (dJointID j
, int parameter
, dReal value
)
4843 dxJointFixed
* joint
= (dxJointFixed
*)j
;
4844 dUASSERT(joint
,"bad joint argument");
4845 dUASSERT(joint
->vtable
== &__dfixed_vtable
,"joint is not a fixed joint");
4846 joint
->set (parameter
,value
);
4850 dReal
dJointGetFixedParam (dJointID j
, int parameter
)
4852 dxJointFixed
* joint
= (dxJointFixed
*)j
;
4853 dUASSERT(joint
,"bad joint argument");
4854 dUASSERT(joint
->vtable
== &__dfixed_vtable
,"joint is not a fixed joint");
4855 return joint
->get (parameter
);
4859 dxJoint::Vtable __dfixed_vtable
= {
4860 sizeof(dxJointFixed
),
4861 (dxJoint::init_fn
*) fixedInit
,
4862 (dxJoint::getInfo1_fn
*) fixedGetInfo1
,
4863 (dxJoint::getInfo2_fn
*) fixedGetInfo2
,
4866 //****************************************************************************
4869 static void nullGetInfo1 (dxJointNull
*j
, dxJoint::Info1
*info
)
4876 static void nullGetInfo2 (dxJointNull
*joint
, dxJoint::Info2
*info
)
4878 dDebug (0,"this should never get called");
4882 dxJoint::Vtable __dnull_vtable
= {
4883 sizeof(dxJointNull
),
4884 (dxJoint::init_fn
*) 0,
4885 (dxJoint::getInfo1_fn
*) nullGetInfo1
,
4886 (dxJoint::getInfo2_fn
*) nullGetInfo2
,
4893 This code is part of the Plane2D ODE joint
4895 Wed Apr 23 18:53:43 CEST 2003
4897 Add this code to the file: ode/src/joint.cpp
4901 # define VoXYZ(v1, o1, x, y, z) \
4908 static const dReal Midentity
[3][3] =
4917 static void plane2dInit (dxJointPlane2D
*j
)
4918 /*********************************************/
4920 /* MINFO ("plane2dInit ()"); */
4921 j
->motor_x
.init (j
->world
);
4922 j
->motor_y
.init (j
->world
);
4923 j
->motor_angle
.init (j
->world
);
4928 static void plane2dGetInfo1 (dxJointPlane2D
*j
, dxJoint::Info1
*info
)
4929 /***********************************************************************/
4931 /* MINFO ("plane2dGetInfo1 ()"); */
4936 if (j
->motor_x
.fmax
> 0)
4937 j
->row_motor_x
= info
->m
++;
4938 if (j
->motor_y
.fmax
> 0)
4939 j
->row_motor_y
= info
->m
++;
4940 if (j
->motor_angle
.fmax
> 0)
4941 j
->row_motor_angle
= info
->m
++;
4946 static void plane2dGetInfo2 (dxJointPlane2D
*joint
, dxJoint::Info2
*info
)
4947 /***************************************************************************/
4952 dReal eps
= info
->fps
* info
->erp
;
4954 /* MINFO ("plane2dGetInfo2 ()"); */
4958 (v2, omega2 not important (== static environment))
4960 constraint equations:
4965 <=> ( 0 0 1 ) (vx) ( 0 0 0 ) (wx) ( 0 )
4966 ( 0 0 0 ) (vy) + ( 1 0 0 ) (wy) = ( 0 )
4967 ( 0 0 0 ) (vz) ( 0 1 0 ) (wz) ( 0 )
4971 // fill in linear and angular coeff. for left hand side:
4973 VoXYZ (&info
->J1l
[r0
], =, 0, 0, 1);
4974 VoXYZ (&info
->J1l
[r1
], =, 0, 0, 0);
4975 VoXYZ (&info
->J1l
[r2
], =, 0, 0, 0);
4977 VoXYZ (&info
->J1a
[r0
], =, 0, 0, 0);
4978 VoXYZ (&info
->J1a
[r1
], =, 1, 0, 0);
4979 VoXYZ (&info
->J1a
[r2
], =, 0, 1, 0);
4981 // error correction (against drift):
4983 // a) linear vz, so that z (== pos[2]) == 0
4984 info
->c
[0] = eps
* -joint
->node
[0].body
->posr
.pos
[2];
4987 // b) angular correction? -> left to application !!!
4988 dReal
*body_z_axis
= &joint
->node
[0].body
->R
[8];
4989 info
->c
[1] = eps
* +atan2 (body_z_axis
[1], body_z_axis
[2]); // wx error
4990 info
->c
[2] = eps
* -atan2 (body_z_axis
[0], body_z_axis
[2]); // wy error
4993 // if the slider is powered, or has joint limits, add in the extra row:
4995 if (joint
->row_motor_x
> 0)
4996 joint
->motor_x
.addLimot (
4997 joint
, info
, joint
->row_motor_x
, Midentity
[0], 0);
4999 if (joint
->row_motor_y
> 0)
5000 joint
->motor_y
.addLimot (
5001 joint
, info
, joint
->row_motor_y
, Midentity
[1], 0);
5003 if (joint
->row_motor_angle
> 0)
5004 joint
->motor_angle
.addLimot (
5005 joint
, info
, joint
->row_motor_angle
, Midentity
[2], 1);
5010 dxJoint::Vtable __dplane2d_vtable
=
5012 sizeof (dxJointPlane2D
),
5013 (dxJoint::init_fn
*) plane2dInit
,
5014 (dxJoint::getInfo1_fn
*) plane2dGetInfo1
,
5015 (dxJoint::getInfo2_fn
*) plane2dGetInfo2
,
5020 void dJointSetPlane2DXParam (dxJoint
*joint
,
5021 int parameter
, dReal value
)
5023 dUASSERT (joint
, "bad joint argument");
5024 dUASSERT (joint
->vtable
== &__dplane2d_vtable
, "joint is not a plane2d");
5025 dxJointPlane2D
* joint2d
= (dxJointPlane2D
*)( joint
);
5026 joint2d
->motor_x
.set (parameter
, value
);
5030 void dJointSetPlane2DYParam (dxJoint
*joint
,
5031 int parameter
, dReal value
)
5033 dUASSERT (joint
, "bad joint argument");
5034 dUASSERT (joint
->vtable
== &__dplane2d_vtable
, "joint is not a plane2d");
5035 dxJointPlane2D
* joint2d
= (dxJointPlane2D
*)( joint
);
5036 joint2d
->motor_y
.set (parameter
, value
);
5041 void dJointSetPlane2DAngleParam (dxJoint
*joint
,
5042 int parameter
, dReal value
)
5044 dUASSERT (joint
, "bad joint argument");
5045 dUASSERT (joint
->vtable
== &__dplane2d_vtable
, "joint is not a plane2d");
5046 dxJointPlane2D
* joint2d
= (dxJointPlane2D
*)( joint
);
5047 joint2d
->motor_angle
.set (parameter
, value
);