Added: dSpaceSetSublevel/dSpaceGetSublevel and possibility to collide a space as...
[ode.git] / ode / src / joint.cpp
blob7207f832712f95e14b9fa98804f53095a58ee5cc
1 /*************************************************************************
2 * *
3 * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. *
4 * All rights reserved. Email: russ@q12.org Web: www.q12.org *
5 * *
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 *
12 * file LICENSE.TXT. *
13 * (2) The BSD-style license that is included with this library in *
14 * the file LICENSE-BSD.TXT. *
15 * *
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. *
20 * *
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.
32 #include <ode/ode.h>
33 #include <ode/odemath.h>
34 #include <ode/rotation.h>
35 #include <ode/matrix.h>
36 #include "joint.h"
38 //****************************************************************************
39 // externs
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 //****************************************************************************
45 // utility
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.
54 dVector3 a1,a2;
56 int s = info->rowskip;
58 // set jacobian
59 info->J1l[0] = 1;
60 info->J1l[s+1] = 1;
61 info->J1l[2*s+2] = 1;
62 dMULTIPLY0_331 (a1,joint->node[0].body->posr.R,anchor1);
63 dCROSSMAT (info->J1a,a1,s,-,+);
64 if (joint->node[1].body) {
65 info->J2l[0] = -1;
66 info->J2l[s+1] = -1;
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]);
80 else {
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.
99 dVector3 a1,a2;
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.
105 dVector3 q1,q2;
106 dPlaneSpace (axis,q1,q2);
108 // set jacobian
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));
137 else {
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
178 dQuaternion qerr,e;
179 if (joint->node[1].body) {
180 dQuaternion qq;
181 dQMultiply1 (qq,joint->node[0].body->q,joint->node[1].body->q);
182 dQMultiply2 (qerr,qq,qrel);
184 else {
185 dQMultiply3 (qerr,joint->node[0].body->q,qrel);
187 if (qerr[0] < 0) {
188 qerr[1] = -qerr[1]; // adjust sign of qerr to make theta small
189 qerr[2] = -qerr[2];
190 qerr[3] = -qerr[3];
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) {
206 dReal q[4];
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];
210 q[3] = 0;
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];
216 q[3] = 0;
217 dMULTIPLY1_331 (anchor2,j->node[1].body->posr.R,q);
219 else {
220 anchor2[0] = x;
221 anchor2[1] = y;
222 anchor2[2] = z;
225 anchor1[3] = 0;
226 anchor2[3] = 0;
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) {
236 dReal q[4];
237 q[0] = x;
238 q[1] = y;
239 q[2] = z;
240 q[3] = 0;
241 dNormalize3 (q);
242 if (axis1) {
243 dMULTIPLY1_331 (axis1,j->node[0].body->posr.R,q);
244 axis1[3] = 0;
246 if (axis2) {
247 if (j->node[1].body) {
248 dMULTIPLY1_331 (axis2,j->node[1].body->posr.R,q);
250 else {
251 axis2[0] = x;
252 axis2[1] = y;
253 axis2[2] = z;
255 axis2[3] = 0;
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];
280 else {
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);
301 else {
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
313 // q is:
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|
320 // = |sin(theta/2)|
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
341 // between -pi..pi
342 if (theta > M_PI) theta -= (dReal) (2*M_PI);
344 // the angle we've just extracted has the wrong sign
345 theta = -theta;
347 return theta;
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
364 dQuaternion qrel;
365 if (body2) {
366 dQuaternion qq;
367 dQMultiply1 (qq,body1->q,body2->q);
368 dQMultiply2 (qrel,qq,q_initial);
370 else {
371 // pretend body2->q is the identity
372 dQMultiply3 (qrel,body1->q,q_initial);
375 return getHingeAngleFromRelativeQuat (qrel,axis);
378 //****************************************************************************
379 // dxJointLimitMotor
381 void dxJointLimitMotor::init (dxWorld *world)
383 vel = 0;
384 fmax = 0;
385 lostop = -dInfinity;
386 histop = dInfinity;
387 fudge_factor = 1;
388 normal_cfm = world->global_cfm;
389 stop_erp = world->global_erp;
390 stop_cfm = world->global_cfm;
391 bounce = 0;
392 limit = 0;
393 limit_err = 0;
397 void dxJointLimitMotor::set (int num, dReal value)
399 switch (num) {
400 case dParamLoStop:
401 lostop = value;
402 break;
403 case dParamHiStop:
404 histop = value;
405 break;
406 case dParamVel:
407 vel = value;
408 break;
409 case dParamFMax:
410 if (value >= 0) fmax = value;
411 break;
412 case dParamFudgeFactor:
413 if (value >= 0 && value <= 1) fudge_factor = value;
414 break;
415 case dParamBounce:
416 bounce = value;
417 break;
418 case dParamCFM:
419 normal_cfm = value;
420 break;
421 case dParamStopERP:
422 stop_erp = value;
423 break;
424 case dParamStopCFM:
425 stop_cfm = value;
426 break;
431 dReal dxJointLimitMotor::get (int num)
433 switch (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;
443 default: return 0;
448 int dxJointLimitMotor::testRotationalLimit (dReal angle)
450 if (angle <= lostop) {
451 limit = 1;
452 limit_err = angle - lostop;
453 return 1;
455 else if (angle >= histop) {
456 limit = 2;
457 limit_err = angle - histop;
458 return 1;
460 else {
461 limit = 0;
462 return 0;
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;
479 J1[srow+0] = ax1[0];
480 J1[srow+1] = ax1[1];
481 J1[srow+2] = ax1[2];
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) {
503 dVector3 c;
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
517 // ineffective
518 if (limit && (lostop == histop)) powered = 0;
520 if (powered) {
521 info->cfm[row] = normal_cfm;
522 if (! limit) {
523 info->c[row] = vel;
524 info->lo[row] = -fmax;
525 info->hi[row] = fmax;
527 else {
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
535 // a fudge factor.
537 dReal fm = fmax;
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;
543 if (rotational) {
544 dBodyAddTorque (joint->node[0].body,-fm*ax1[0],-fm*ax1[1],
545 -fm*ax1[2]);
546 if (joint->node[1].body)
547 dBodyAddTorque (joint->node[1].body,fm*ax1[0],fm*ax1[1],fm*ax1[2]);
549 else {
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],
556 -fm*ltd[2]);
557 dBodyAddTorque (joint->node[1].body,-fm*ltd[0],-fm*ltd[1],
558 -fm*ltd[2]);
564 if (limit) {
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;
574 else {
575 if (limit == 1) {
576 // low limit
577 info->lo[row] = 0;
578 info->hi[row] = dInfinity;
580 else {
581 // high limit
582 info->lo[row] = -dInfinity;
583 info->hi[row] = 0;
586 // deal with bounce
587 if (bounce > 0) {
588 // calculate joint velocity
589 dReal vel;
590 if (rotational) {
591 vel = dDOT(joint->node[0].body->avel,ax1);
592 if (joint->node[1].body)
593 vel -= dDOT(joint->node[1].body->avel,ax1);
595 else {
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.
603 if (limit == 1) {
604 // low limit
605 if (vel < 0) {
606 dReal newc = -bounce * vel;
607 if (newc > info->c[row]) info->c[row] = newc;
610 else {
611 // high limit - all those computations are reversed
612 if (vel > 0) {
613 dReal newc = -bounce * vel;
614 if (newc < info->c[row]) info->c[row] = newc;
620 return 1;
622 else return 0;
625 //****************************************************************************
626 // ball and socket
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)
639 info->m = 3;
640 info->nub = 3;
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);
683 else
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);
696 else
697 getAnchor2 (joint,result,joint->anchor2);
701 void dxJointBall::set (int num, dReal value)
703 switch (num) {
704 case dParamCFM:
705 cfm = value;
706 break;
707 case dParamERP:
708 erp = value;
709 break;
714 dReal dxJointBall::get (int num)
716 switch (num) {
717 case dParamCFM:
718 return cfm;
719 case dParamERP:
720 return erp;
721 default:
722 return 0;
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 = {
746 sizeof(dxJointBall),
747 (dxJoint::init_fn*) ballInit,
748 (dxJoint::getInfo1_fn*) ballGetInfo1,
749 (dxJoint::getInfo2_fn*) ballGetInfo2,
750 dJointTypeBall};
752 //****************************************************************************
753 // hinge
755 static void hingeInit (dxJointHinge *j)
757 dSetZero (j->anchor1,4);
758 dSetZero (j->anchor2,4);
759 dSetZero (j->axis1,4);
760 j->axis1[0] = 1;
761 dSetZero (j->axis2,4);
762 j->axis2[0] = 1;
763 dSetZero (j->qrel,4);
764 j->limot.init (j->world);
768 static void hingeGetInfo1 (dxJointHinge *j, dxJoint::Info1 *info)
770 info->nub = 5;
772 // see if joint is powered
773 if (j->limot.fmax > 0)
774 info->m = 6; // powered hinge needs an extra constraint row
775 else info->m = 5;
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,
781 j->qrel);
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
795 // p*w1 - p*w2 = 0
796 // q*w1 - q*w2 = 0
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.
840 dVector3 ax2,b;
841 if (joint->node[1].body) {
842 dMULTIPLY0_331 (ax2,joint->node[1].body->posr.R,joint->axis2);
844 else {
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);
867 else {
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) {
893 dReal q[4];
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];
897 q[3] = 0;
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];
904 q[3] = 0;
905 dMULTIPLY1_331 (joint->anchor2,joint->node[1].body->posr.R,q);
907 else {
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);
941 else
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);
954 else
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;
990 dAASSERT(joint);
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,
994 joint->qrel);
995 if (joint->flags & dJOINT_REVERSE)
996 return -ang;
997 else
998 return ang;
1000 else return 0;
1004 dReal dJointGetHingeAngleRate (dJointID j)
1006 dxJointHinge* joint = (dxJointHinge*)j;
1007 dAASSERT(joint);
1008 dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a Hinge");
1009 if (joint->node[0].body) {
1010 dVector3 axis;
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;
1015 return rate;
1017 else return 0;
1021 void dJointAddHingeTorque (dJointID j, dReal torque)
1023 dxJointHinge* joint = (dxJointHinge*)j;
1024 dVector3 axis;
1025 dAASSERT(joint);
1026 dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a Hinge");
1028 if (joint->flags & dJOINT_REVERSE)
1029 torque = -torque;
1031 getAxis (joint,axis,joint->axis1);
1032 axis[0] *= torque;
1033 axis[1] *= torque;
1034 axis[2] *= torque;
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,
1048 dJointTypeHinge};
1050 //****************************************************************************
1051 // slider
1053 static void sliderInit (dxJointSlider *j)
1055 dSetZero (j->axis1,4);
1056 j->axis1[0] = 1;
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
1070 dVector3 ax1,q;
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];
1079 else {
1080 for (int i=0; i<3; i++) q[i] = joint->node[0].body->posr.pos[i] -
1081 joint->offset[i];
1084 return dDOT(ax1,q);
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
1095 dVector3 ax1;
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);
1102 else {
1103 return dDOT(ax1,joint->node[0].body->lvel);
1108 static void sliderGetInfo1 (dxJointSlider *j, dxJoint::Info1 *info)
1110 info->nub = 5;
1112 // see if joint is powered
1113 if (j->limot.fmax > 0)
1114 info->m = 6; // powered slider needs an extra constraint row
1115 else info->m = 5;
1117 // see if we're at a joint limit.
1118 j->limot.limit = 0;
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) {
1124 j->limot.limit = 1;
1125 j->limot.limit_err = pos - j->limot.lostop;
1126 info->m = 6;
1128 else if (pos >= j->limot.histop) {
1129 j->limot.limit = 2;
1130 j->limot.limit_err = pos - j->limot.histop;
1131 info->m = 6;
1137 static void sliderGetInfo2 (dxJointSlider *joint, dxJoint::Info2 *info)
1139 int i,s = info->rowskip;
1140 int s3=3*s,s4=4*s;
1142 // pull out pos and R for both bodies. also get the `connection'
1143 // vector pos2-pos1.
1145 dReal *pos1,*pos2,*R1,*R2;
1146 dVector3 c;
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];
1154 else {
1155 pos2 = 0;
1156 R2 = 0;
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) {
1172 dVector3 tmp;
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);
1195 else {
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;
1210 int i;
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);
1219 dVector3 c;
1220 for (i=0; i<3; i++)
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);
1224 else {
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;
1236 int i;
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);
1245 dVector3 c;
1246 for (i=0; i<3; i++)
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);
1250 else {
1251 // set joint->qrel to the transpose of the first body's q
1252 joint->qrel[0] = joint->node[0].body->q[0];
1254 for (i=1; i<4; i++)
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;
1296 dVector3 axis;
1297 dUASSERT(joint,"bad joint argument");
1298 dUASSERT(joint->vtable == &__dslider_vtable,"joint is not a slider");
1300 if (joint->flags & dJOINT_REVERSE)
1301 force -= force;
1303 getAxis (joint,axis,joint->axis1);
1304 axis[0] *= force;
1305 axis[1] *= force;
1306 axis[2] *= force;
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)
1320 dVector3 c;
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,
1337 dJointTypeSlider};
1339 //****************************************************************************
1340 // contact
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;
1352 #endif
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.
1360 int m = 1, nub=0;
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 ++;
1369 else {
1370 if (j->contact.surface.mu > 0) m += 2;
1371 if (j->contact.surface.mu == dInfinity) nub += 2;
1374 j->the_m = m;
1375 info->m = m;
1376 info->nub = nub;
1380 static void contactGetInfo2 (dxJointContact *j, dxJoint::Info2 *info)
1382 int s = info->rowskip;
1383 int s2 = 2*s;
1385 // get normal, with sign adjusted for body1/body2 polarity
1386 dVector3 normal;
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];
1392 else {
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
1400 dVector3 c1,c2;
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;
1436 // deal with bounce
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
1455 info->lo[0] = 0;
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);
1469 else {
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
1487 // mode
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
1514 // mode
1515 if (j->contact.surface.mode & dContactMu2) {
1516 info->lo[2] = -j->contact.surface.mu2;
1517 info->hi[2] = j->contact.surface.mu2;
1519 else {
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,
1537 dJointTypeContact};
1539 //****************************************************************************
1540 // hinge 2. note that this joint must be attached to two bodies for it to work
1542 static dReal measureHinge2Angle (dxJointHinge2 *joint)
1544 dVector3 a1,a2;
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);
1558 j->axis1[0] = 1;
1559 dSetZero (j->axis2,4);
1560 j->axis2[1] = 1;
1561 j->c0 = 0;
1562 j->s0 = 0;
1564 dSetZero (j->v1,4);
1565 j->v1[0] = 1;
1566 dSetZero (j->v2,4);
1567 j->v2[1] = 1;
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)
1581 info->m = 4;
1582 info->nub = 4;
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) \
1606 dVector3 ax1,ax2; \
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
1617 dReal s,c;
1618 dVector3 q;
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
1643 // approximation:
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
1672 dVector3 ax1,ax2,v;
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];
1684 dNormalize3 (ax2);
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) {
1710 dReal q[4];
1711 q[0] = x;
1712 q[1] = y;
1713 q[2] = z;
1714 q[3] = 0;
1715 dNormalize3 (q);
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
1720 dVector3 ax;
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) {
1733 dReal q[4];
1734 q[0] = x;
1735 q[1] = y;
1736 q[2] = z;
1737 q[3] = 0;
1738 dNormalize3 (q);
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
1743 dVector3 ax;
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);
1758 else {
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);
1774 else
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);
1787 else
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);
1824 else {
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);
1838 else return 0;
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) {
1848 dVector3 axis;
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);
1852 return rate;
1854 else return 0;
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) {
1864 dVector3 axis;
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);
1868 return rate;
1870 else return 0;
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,
1898 dJointTypeHinge2};
1900 //****************************************************************************
1901 // universal
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);
1913 j->axis1[0] = 1;
1914 dSetZero (j->axis2,4);
1915 j->axis2[1] = 1;
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);
1931 else {
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
1943 dVector3 ax1, ax2;
1944 dMatrix3 R;
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
1956 // satisfied.)
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]);
1965 dRtoQ (R, qcross);
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
1991 // ax2
1992 // ^
1993 // |
1994 // |
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]));
2008 qrel[1] *= l;
2009 qrel[2] *= l;
2010 qrel[3] *= l;
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);
2018 else {
2019 // pretend joint->node[1].body->q is the identity
2020 dQMultiply2 (qrel, qcross2, joint->qrel2);
2023 *angle2 = - getHingeAngleFromRelativeQuat(qrel, joint->axis2);
2026 else
2028 *angle1 = 0;
2029 *angle2 = 0;
2033 static dReal getUniversalAngle1(dxJointUniversal *joint)
2035 if (joint->node[0].body) {
2036 // length 1 joint axis in global coordinates, from each body
2037 dVector3 ax1, ax2;
2038 dMatrix3 R;
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
2050 // satisfied.)
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]);
2058 dRtoQ (R,qcross);
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);
2069 return 0;
2073 static dReal getUniversalAngle2(dxJointUniversal *joint)
2075 if (joint->node[0].body) {
2076 // length 1 joint axis in global coordinates, from each body
2077 dVector3 ax1, ax2;
2078 dMatrix3 R;
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
2090 // satisfied.)
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]);
2098 dRtoQ(R, qcross);
2100 if (joint->node[1].body) {
2101 dQMultiply1 (qq, joint->node[1].body->q, qcross);
2102 dQMultiply2 (qrel,qq,joint->qrel2);
2104 else {
2105 // pretend joint->node[1].body->q is the identity
2106 dQMultiply2 (qrel,qcross, joint->qrel2);
2109 return - getHingeAngleFromRelativeQuat(qrel, joint->axis2);
2111 return 0;
2115 static void universalGetInfo1 (dxJointUniversal *j, dxJoint::Info1 *info)
2117 info->nub = 4;
2118 info->m = 4;
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);
2133 if (limiting1)
2134 j->limot1.testRotationalLimit (angle1);
2135 if (limiting2)
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
2151 // equation is
2152 // p*w1 - p*w2 = 0
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
2157 dVector3 ax1, ax2;
2158 dVector3 ax2_temp;
2159 // length 1 vector perpendicular to ax1 and ax2. Neither body can rotate
2160 // about this.
2161 dVector3 p;
2162 dReal k;
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);
2168 k = dDOT(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);
2173 dNormalize3(p);
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) {
2214 dVector3 ax1, ax2;
2215 dMatrix3 R;
2216 dQuaternion qcross;
2218 getUniversalAxes(joint, ax1, ax2);
2220 // Axis 1.
2221 dRFrom2Axes(R, ax1[0], ax1[1], ax1[2], ax2[0], ax2[1], ax2[2]);
2222 dRtoQ(R, qcross);
2223 dQMultiply1 (joint->qrel1, joint->node[0].body->q, qcross);
2225 // Axis 2.
2226 dRFrom2Axes(R, ax2[0], ax2[1], ax2[2], ax1[0], ax1[1], ax1[2]);
2227 dRtoQ(R, qcross);
2228 if (joint->node[1].body) {
2229 dQMultiply1 (joint->qrel2, joint->node[1].body->q, qcross);
2231 else {
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);
2256 else
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);
2269 else
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);
2283 else
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);
2296 else
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);
2309 else
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);
2322 else
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);
2335 else {
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);
2349 else {
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);
2361 else
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);
2373 else
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);
2385 else
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) {
2397 dVector3 axis;
2399 if (joint->flags & dJOINT_REVERSE)
2400 getAxis2 (joint,axis,joint->axis2);
2401 else
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);
2406 return rate;
2408 return 0;
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) {
2419 dVector3 axis;
2421 if (joint->flags & dJOINT_REVERSE)
2422 getAxis (joint,axis,joint->axis1);
2423 else
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);
2428 return rate;
2430 return 0;
2434 void dJointAddUniversalTorques (dJointID j, dReal torque1, dReal torque2)
2436 dxJointUniversal* joint = (dxJointUniversal*)j;
2437 dVector3 axis1, axis2;
2438 dAASSERT(joint);
2439 dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal");
2441 if (joint->flags & dJOINT_REVERSE) {
2442 dReal temp = torque1;
2443 torque1 = - torque2;
2444 torque2 = - temp;
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)
2477 // Default Position
2478 // Z^
2479 // | Body 1 P R Body2
2480 // |+---------+ _ _ +-----------+
2481 // || |----|----(_)--------+ |
2482 // |+---------+ - +-----------+
2483 // |
2484 // X.-----------------------------------------> Y
2485 // N.B. X is comming out of the page
2486 dSetZero (j->anchor2,4);
2488 dSetZero (j->axisR1,4);
2489 j->axisR1[0] = 1;
2490 dSetZero (j->axisR2,4);
2491 j->axisR2[0] = 1;
2493 dSetZero (j->axisP1,4);
2494 j->axisP1[1] = 1;
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");
2509 dVector3 q;
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) {
2514 dVector3 anchor2;
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]) );
2527 else {
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]) );
2540 dVector3 axP;
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
2553 dVector3 ax1;
2554 dMULTIPLY0_331 (ax1,joint->node[0].body->posr.R,joint->axisP1);
2556 if (joint->node[1].body) {
2557 dVector3 lv2;
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);
2561 else
2562 return dDOT (ax1,joint->node[0].body->lvel);
2567 static void PRGetInfo1 (dxJointPR *j, dxJoint::Info1 *info)
2569 info->nub = 4;
2570 info->m = 4;
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;
2591 int s2= 2*s;
2592 int s3= 3*s;
2593 //int s4= 4*s;
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;
2610 else {
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];
2630 else {
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
2643 // p*w1 - p*w2 = 0
2644 // q*w1 - q*w2 = 0
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.
2647 dVector3 ax1;
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.
2684 dVector3 ax2;
2685 if (joint->node[1].body) {
2686 dMULTIPLY0_331 (ax2, R2, joint->axisR2);
2688 else {
2689 ax2[0] = joint->axisR2[0];
2690 ax2[1] = joint->axisR2[1];
2691 ax2[2] = joint->axisR2[2];
2694 dVector3 b;
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 )
2717 // ==
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
2765 dVector3 err;
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);
2784 else {
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
2832 } // parameter alue
2833 else {
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);
2847 else {
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);
2881 else {
2882 return joint->limotP.get (parameter);
2886 void dJointAddPRTorque (dJointID j, dReal torque)
2888 dxJointPR* joint = (dxJointPR*) j;
2889 dVector3 axis;
2890 dAASSERT (joint);
2891 dUASSERT (joint->vtable == &__dPR_vtable,"joint is not a Prismatic and Rotoide");
2893 if (joint->flags & dJOINT_REVERSE)
2894 torque = -torque;
2896 getAxis (joint,axis,joint->axisR1);
2897 axis[0] *= torque;
2898 axis[1] *= torque;
2899 axis[2] *= torque;
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 = {
2909 sizeof(dxJointPR),
2910 (dxJoint::init_fn*) PRInit,
2911 (dxJoint::getInfo1_fn*) PRGetInfo1,
2912 (dxJoint::getInfo2_fn*) PRGetInfo2,
2913 dJointTypePR
2917 //****************************************************************************
2918 // Prismatic and Universal
2920 static void PUInit (dxJointPU *j)
2922 // Default Position
2923 // Y ^ Axis2
2924 // ^ |
2925 // / | ^ Axis1
2926 // Z^ / | /
2927 // | / Body 2 | / Body 1
2928 // | / +---------+ | / +-----------+
2929 // | / / /| | / / /|
2930 // | / / / + _/ - / / +
2931 // | / / /-/--------(_)----|--- /-----------/-------> AxisP
2932 // | / +---------+ / - +-----------+ /
2933 // | / | |/ | |/
2934 // | / +---------+ +-----------+
2935 // |/
2936 // .-----------------------------------------> X
2937 // |----------------->
2938 // Anchor2 <--------------|
2939 // Anchor1
2942 // Setting member variables which are w.r.t body2
2943 dSetZero (j->axis1,4);
2944 j->axis1[1] = 1;
2946 // Setting member variables which are w.r.t body2
2947 dSetZero (j->anchor2,4);
2948 dSetZero (j->axis2,4);
2949 j->axis2[2] = 1;
2951 dSetZero (j->axisP1,4);
2952 j->axisP1[0] = 1;
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");
2970 dVector3 q;
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) {
2975 dVector3 anchor2;
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]) );
2987 else {
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]) );
3000 dVector3 axP;
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
3017 // anchor point.
3019 // r will be used to find the distance between body1 and the anchor point
3020 dVector3 r;
3021 dVector3 anchor2;
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]) );
3033 else {
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
3045 dVector3 lvel1;
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
3055 dVector3 lvel2;
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
3066 dVector3 axP1;
3067 dMULTIPLY0_331 (axP1,joint->node[0].body->posr.R,joint->axisP1);
3068 return dDOT (axP1, lvel1);
3071 return 0.0;
3076 static void PUGetInfo1 (dxJointPU *j, dxJoint::Info1 *info)
3078 info->m = 3;
3079 info->nub = 3;
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);
3107 if (limiting1)
3108 j->limot1.testRotationalLimit (angle1);
3109 if (limiting2)
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)
3121 const int s0 = 0;
3122 const int s1 = info->rowskip;
3123 const int s2= 2*s1;
3124 const int s3= 3*s1;
3125 const int s4= 4*s1;
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;
3139 else {
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
3150 dVector3 dist;
3151 dVector3 anchor2;
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];
3158 else {
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
3173 // p*w1 - p*w2 = 0
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.
3176 dVector3 ax1, ax2;
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];
3183 dVector3 p;
3184 dCROSS (p, =, ax1, q);
3185 dNormalize3 (p);
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 )
3227 // ==
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
3276 dVector3 err;
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].
3301 * Ex:
3302 * <PRE>
3303 * dReal offset = 1;
3304 * dVector3 dir;
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
3310 * </PRE>
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);
3357 else
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);
3369 else
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);
3403 else
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);
3416 else
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);
3429 else
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) {
3442 dVector3 axis;
3444 if (joint->flags & dJOINT_REVERSE)
3445 getAxis2 (joint,axis,joint->axis2);
3446 else
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);
3451 return rate;
3453 return 0;
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) {
3465 dVector3 axis;
3467 if (joint->flags & dJOINT_REVERSE)
3468 getAxis (joint,axis,joint->axis1);
3469 else
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);
3474 return rate;
3476 return 0;
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 ) {
3487 case dParamGroup1:
3488 joint->limot1.set (parameter,value);
3489 break;
3490 case dParamGroup2:
3491 joint->limot2.set (parameter & 0xff,value);
3492 break;
3493 case dParamGroup3:
3494 joint->limotP.set (parameter & 0xff,value);
3495 break;
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);
3508 else {
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
3535 * @ingroup joints
3537 * @note This function was added for convenience it is the same as
3538 * dJointGetPUAxis3
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 ) {
3562 case dParamGroup1:
3563 return joint->limot1.get (parameter);
3564 break;
3565 case dParamGroup2:
3566 return joint->limot2.get (parameter & 0xff);
3567 break;
3568 case dParamGroup3:
3569 return joint->limotP.get (parameter & 0xff);
3570 break;
3573 return 0;
3576 dxJoint::Vtable __dPU_vtable = {
3577 sizeof (dxJointPU),
3578 (dxJoint::init_fn*) PUInit,
3579 (dxJoint::getInfo1_fn*) PUGetInfo1,
3580 (dxJoint::getInfo2_fn*) PUGetInfo2,
3581 dJointTypePU
3587 //****************************************************************************
3588 // Piston
3591 static void PistonInit (dxJointPiston *j)
3593 dSetZero (j->axis1,4);
3594 dSetZero (j->axis2,4);
3596 j->axis1[0] = 1;
3597 j->axis2[0] = 1;
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) {
3617 dVector3 q;
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) {
3622 dVector3 anchor2;
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]) );
3633 else {
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
3645 dVector3 ax;
3646 dMULTIPLY0_331 (ax,joint->node[0].body->posr.R,joint->axis1);
3648 return dDOT (ax,q);
3651 dDEBUGMSG ("The function always return 0 since no body are attached");
3652 return 0;
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
3663 dVector3 ax;
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) );
3673 else {
3674 return dDOT (ax,joint->node[0].body->lvel);
3679 dReal dJointGetPistonAngle (dJointID j)
3681 dxJointPiston* joint = (dxJointPiston *) j;
3682 dAASSERT (joint);
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,
3687 joint->qrel);
3688 if (joint->flags & dJOINT_REVERSE)
3689 return -ang;
3690 else
3691 return ang;
3693 else return 0;
3697 dReal dJointGetPistonAngleRate (dJointID j)
3699 dxJointPiston* joint = (dxJointPiston*) j;
3700 dAASSERT (joint);
3701 dUASSERT (joint->vtable == &__dPiston_vtable, "joint is not a Piston");
3703 if (joint->node[0].body) {
3704 dVector3 axis;
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;
3709 return rate;
3711 else return 0;
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,
3742 j->qrel);
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)
3754 const int s0 = 0;
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
3768 dVector3 anchor2;
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];
3782 else {
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:
3795 // p*w1 - p*w2 = 0
3796 // q*w1 - q*w2 = 0
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]
3817 // => q[0] ~= 1
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
3822 // the error
3823 dVector3 ax1, p, q;
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);
3829 // LHS
3830 dOPE ( (info->J1a) + s0, =, p );
3831 dOPE ( (info->J1a) + s1, =, q );
3833 dVector3 b;
3834 if (joint->node[1].body) {
3835 // LHS
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
3842 dVector3 ax2;
3843 dMULTIPLY0_331 (ax2, R2, joint->axis2);
3844 dCROSS (b,=,ax1, ax2);
3846 else {
3847 // Some math for the RHS
3848 dCROSS (b,=,ax1,joint->axis2);
3851 // RHS
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 )
3865 // Simplify
3866 // u . v2 + u. w2 x anchor2 = u . v1 + u . w1 x dist
3867 // or
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
3909 dVector3 err;
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);
3927 else {
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);
3956 else
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);
3969 else
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);
3997 dVector3 c;
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);
4037 else {
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);
4052 else {
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)
4065 force -= force;
4067 dVector3 axis;
4068 getAxis (joint,axis,joint->axis1);
4069 // axis[i] *= force
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.
4081 // Body_2
4082 // Body_1 -----
4083 // --- |-- | |
4084 // | |---------------*-------------| | ---> prismatic axis
4085 // --- |-- | |
4086 // -----
4087 // Body_2
4088 // Case where we need ltd
4089 // Body_1
4090 // ---
4091 // | |---------
4092 // --- |
4093 // | |--
4094 // -----*----- ---> prismatic axis
4095 // |-- |
4096 // |
4097 // |
4098 // | -----
4099 // | | |
4100 // -------| |
4101 // | |
4102 // -----
4103 // Body_2
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,
4143 dJointTypePiston};
4145 //****************************************************************************
4146 // angular motor
4148 static void amotorInit (dxJointAMotor *j)
4150 int i;
4151 j->num = 0;
4152 j->mode = dAMotorUser;
4153 for (i=0; i<3; i++) {
4154 j->rel[i] = 0;
4155 dSetZero (j->axis[i],4);
4156 j->limot[i].init (j->world);
4157 j->angle[i] = 0;
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]);
4174 else {
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]);
4182 else {
4183 for (int i=0; i < joint->num; i++) {
4184 if (joint->rel[i] == 1) {
4185 // relative to b1
4186 dMULTIPLY0_331 (ax[i],joint->node[0].body->posr.R,joint->axis[i]);
4188 else if (joint->rel[i] == 2) {
4189 // relative to b2
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]);
4194 else {
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])
4207 // assumptions:
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
4218 dVector3 ref1,ref2;
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);
4223 else {
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
4230 dVector3 q;
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);
4261 else { // jds
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)
4279 info->m = 0;
4280 info->nub = 0;
4282 // compute the axes and angles, if in euler mode
4283 if (j->mode == dAMotorEuler) {
4284 dVector3 ax[3];
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) {
4293 info->m++;
4299 static void amotorGetInfo2 (dxJointAMotor *joint, dxJoint::Info2 *info)
4301 int i;
4303 // compute the axes (if not global)
4304 dVector3 ax[3];
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.
4322 dVector3 *axptr[3];
4323 axptr[0] = &ax[0];
4324 axptr[1] = &ax[1];
4325 axptr[2] = &ax[2];
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;
4336 int row=0;
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) {
4349 joint->num = 3;
4351 else {
4352 if (num < 0) num = 0;
4353 if (num > 3) num = 3;
4354 joint->num = num;
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
4376 dVector3 r;
4377 r[0] = x;
4378 r[1] = y;
4379 r[2] = z;
4380 r[3] = 0;
4381 if (rel > 0) {
4382 if (rel==1) {
4383 dMULTIPLY1_331 (joint->axis[anum],joint->node[0].body->posr.R,r);
4385 else {
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);
4390 else {
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];
4396 else {
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;
4422 dAASSERT(joint);
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;
4427 parameter &= 0xff;
4428 joint->limot[anum].set (parameter, value);
4432 void dJointSetAMotorMode (dJointID j, int mode)
4434 dxJointAMotor* joint = (dxJointAMotor*)j;
4435 dAASSERT(joint);
4436 dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor");
4437 joint->mode = mode;
4438 if (joint->mode == dAMotorEuler) {
4439 joint->num = 3;
4440 amotorSetEulerReferenceVectors (joint);
4445 int dJointGetAMotorNumAxes (dJointID j)
4447 dxJointAMotor* joint = (dxJointAMotor*)j;
4448 dAASSERT(joint);
4449 dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor");
4450 return joint->num;
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]);
4465 else {
4466 if (joint->node[1].body) { // jds
4467 dMULTIPLY0_331 (result,joint->node[1].body->posr.R,joint->axis[anum]);
4469 else {
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];
4475 else {
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;
4508 // @@@
4509 dDebug (0,"not yet implemented");
4510 return 0;
4514 dReal dJointGetAMotorParam (dJointID j, int parameter)
4516 dxJointAMotor* joint = (dxJointAMotor*)j;
4517 dAASSERT(joint);
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;
4522 parameter &= 0xff;
4523 return joint->limot[anum].get (parameter);
4527 int dJointGetAMotorMode (dJointID j)
4529 dxJointAMotor* joint = (dxJointAMotor*)j;
4530 dAASSERT(joint);
4531 dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor");
4532 return joint->mode;
4536 void dJointAddAMotorTorques (dJointID j, dReal torque1, dReal torque2, dReal torque3)
4538 dxJointAMotor* joint = (dxJointAMotor*)j;
4539 dVector3 axes[3];
4540 dAASSERT(joint);
4541 dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor");
4543 if (joint->num == 0)
4544 return;
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,
4574 dJointTypeAMotor};
4578 //****************************************************************************
4579 // lmotor joint
4580 static void lmotorInit (dxJointLMotor *j)
4582 int i;
4583 j->num = 0;
4584 for (i=0;i<3;i++) {
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]);
4600 } else {
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)
4610 info->m = 0;
4611 info->nub = 0;
4612 for (int i=0; i < j->num; i++) {
4613 if (j->limot[i].fmax > 0) {
4614 info->m++;
4619 static void lmotorGetInfo2 (dxJointLMotor *joint, dxJoint::Info2 *info)
4621 int row=0;
4622 dVector3 ax[3];
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;
4643 dVector3 r;
4644 r[0] = x;
4645 r[1] = y;
4646 r[2] = z;
4647 r[3] = 0;
4648 if (rel > 0) {
4649 if (rel==1) {
4650 dMULTIPLY1_331 (joint->axis[anum],joint->node[0].body->posr.R,r);
4651 } else {
4652 //second body has to exists thanks to ref 1 line
4653 dMULTIPLY1_331 (joint->axis[anum],joint->node[1].body->posr.R,r);
4655 } else {
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;
4671 joint->num = num;
4674 void dJointSetLMotorParam (dJointID j, int parameter, dReal value)
4676 dxJointLMotor* joint = (dxJointLMotor*)j;
4677 dAASSERT(joint);
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;
4682 parameter &= 0xff;
4683 joint->limot[anum].set (parameter, value);
4686 int dJointGetLMotorNumAxes (dJointID j)
4688 dxJointLMotor* joint = (dxJointLMotor*)j;
4689 dAASSERT(joint);
4690 dUASSERT(joint->vtable == &__dlmotor_vtable,"joint is not an lmotor");
4691 return joint->num;
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;
4710 dAASSERT(joint);
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;
4715 parameter &= 0xff;
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,
4724 dJointTypeLMotor
4728 //****************************************************************************
4729 // fixed joint
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)
4742 info->m = 6;
4743 info->nub = 6;
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.
4755 // set jacobian
4756 info->J1l[0] = 1;
4757 info->J1l[s+1] = 1;
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;
4765 dVector3 ofs;
4766 dMULTIPLY0_331 (ofs,joint->node[0].body->posr.R,joint->offset);
4767 if (joint->node[1].body) {
4768 dCROSSMAT (info->J1a,ofs,s,+,-);
4769 info->J2l[0] = -1;
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]);
4781 else {
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");
4793 int i;
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);
4801 dReal ofs[4];
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);
4806 else {
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)
4817 switch (num) {
4818 case dParamCFM:
4819 cfm = value;
4820 break;
4821 case dParamERP:
4822 erp = value;
4823 break;
4828 dReal dxJointFixed::get (int num)
4830 switch (num) {
4831 case dParamCFM:
4832 return cfm;
4833 case dParamERP:
4834 return erp;
4835 default:
4836 return 0;
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,
4864 dJointTypeFixed};
4866 //****************************************************************************
4867 // null joint
4869 static void nullGetInfo1 (dxJointNull *j, dxJoint::Info1 *info)
4871 info->m = 0;
4872 info->nub = 0;
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,
4887 dJointTypeNull};
4893 This code is part of the Plane2D ODE joint
4894 by psero@gmx.de
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) \
4903 (v1)[0] o1 (x), \
4904 (v1)[1] o1 (y), \
4905 (v1)[2] o1 (z) \
4908 static const dReal Midentity[3][3] =
4910 { 1, 0, 0 },
4911 { 0, 1, 0 },
4912 { 0, 0, 1, }
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 ()"); */
4933 info->nub = 3;
4934 info->m = 3;
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 /***************************************************************************/
4949 int r0 = 0,
4950 r1 = info->rowskip,
4951 r2 = 2 * r1;
4952 dReal eps = info->fps * info->erp;
4954 /* MINFO ("plane2dGetInfo2 ()"); */
4957 v = v1, w = omega1
4958 (v2, omega2 not important (== static environment))
4960 constraint equations:
4961 xz = 0
4962 wx = 0
4963 wy = 0
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 )
4968 J1/J1l Omega1/J1a
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];
4986 # if 0
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
4991 # endif
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,
5016 dJointTypePlane2D
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);