Fixed: Orphaned declaration of dJointAddPUTorque() was replaced with dJointAddPUTorques()
[ode.git] / ode / src / joints / universal.cpp
blob8c604491c652485cc9e5d4c927c4e8661e144fca
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 *************************************************************************/
24 #include <ode/odeconfig.h>
25 #include "config.h"
26 #include "universal.h"
27 #include "joint_internal.h"
31 //****************************************************************************
32 // universal
34 // I just realized that the universal joint is equivalent to a hinge 2 joint with
35 // perfectly stiff suspension. By comparing the hinge 2 implementation to
36 // the universal implementation, you may be able to improve this
37 // implementation (or, less likely, the hinge2 implementation).
39 dxJointUniversal::dxJointUniversal( dxWorld *w ) :
40 dxJoint( w )
42 dSetZero( anchor1, 4 );
43 dSetZero( anchor2, 4 );
44 dSetZero( axis1, 4 );
45 axis1[0] = 1;
46 dSetZero( axis2, 4 );
47 axis2[1] = 1;
48 dSetZero( qrel1, 4 );
49 dSetZero( qrel2, 4 );
50 limot1.init( world );
51 limot2.init( world );
55 void
56 dxJointUniversal::getAxes( dVector3 ax1, dVector3 ax2 )
58 // This says "ax1 = joint->node[0].body->posr.R * joint->axis1"
59 dMultiply0_331( ax1, node[0].body->posr.R, axis1 );
61 if ( node[1].body )
63 dMultiply0_331( ax2, node[1].body->posr.R, axis2 );
65 else
67 ax2[0] = axis2[0];
68 ax2[1] = axis2[1];
69 ax2[2] = axis2[2];
73 void
74 dxJointUniversal::getAngles( dReal *angle1, dReal *angle2 )
76 if ( node[0].body )
78 // length 1 joint axis in global coordinates, from each body
79 dVector3 ax1, ax2;
80 dMatrix3 R;
81 dQuaternion qcross, qq, qrel;
83 getAxes( ax1, ax2 );
85 // It should be possible to get both angles without explicitly
86 // constructing the rotation matrix of the cross. Basically,
87 // orientation of the cross about axis1 comes from body 2,
88 // about axis 2 comes from body 1, and the perpendicular
89 // axis can come from the two bodies somehow. (We don't really
90 // want to assume it's 90 degrees, because in general the
91 // constraints won't be perfectly satisfied, or even very well
92 // satisfied.)
94 // However, we'd need a version of getHingeAngleFromRElativeQuat()
95 // that CAN handle when its relative quat is rotated along a direction
96 // other than the given axis. What I have here works,
97 // although it's probably much slower than need be.
99 dRFrom2Axes( R, ax1[0], ax1[1], ax1[2], ax2[0], ax2[1], ax2[2] );
101 dRtoQ( R, qcross );
104 // This code is essentialy the same as getHingeAngle(), see the comments
105 // there for details.
107 // get qrel = relative rotation between node[0] and the cross
108 dQMultiply1( qq, node[0].body->q, qcross );
109 dQMultiply2( qrel, qq, qrel1 );
111 *angle1 = getHingeAngleFromRelativeQuat( qrel, axis1 );
113 // This is equivalent to
114 // dRFrom2Axes(R, ax2[0], ax2[1], ax2[2], ax1[0], ax1[1], ax1[2]);
115 // You see that the R is constructed from the same 2 axis as for angle1
116 // but the first and second axis are swapped.
117 // So we can take the first R and rapply a rotation to it.
118 // The rotation is around the axis between the 2 axes (ax1 and ax2).
119 // We do a rotation of 180deg.
121 dQuaternion qcross2;
122 // Find the vector between ax1 and ax2 (i.e. in the middle)
123 // We need to turn around this vector by 180deg
125 // The 2 axes should be normalize so to find the vector between the 2.
126 // Add and devide by 2 then normalize or simply normalize
127 // ax2
128 // ^
129 // |
130 // |
131 /// *------------> ax1
132 // We want the vector a 45deg
134 // N.B. We don't need to normalize the ax1 and ax2 since there are
135 // normalized when we set them.
137 // We set the quaternion q = [cos(theta), dir*sin(theta)] = [w, x, y, Z]
138 qrel[0] = 0; // equivalent to cos(Pi/2)
139 qrel[1] = ax1[0] + ax2[0]; // equivalent to x*sin(Pi/2); since sin(Pi/2) = 1
140 qrel[2] = ax1[1] + ax2[1];
141 qrel[3] = ax1[2] + ax2[2];
143 dReal l = dRecip( sqrt( qrel[1] * qrel[1] + qrel[2] * qrel[2] + qrel[3] * qrel[3] ) );
144 qrel[1] *= l;
145 qrel[2] *= l;
146 qrel[3] *= l;
148 dQMultiply0( qcross2, qrel, qcross );
150 if ( node[1].body )
152 dQMultiply1( qq, node[1].body->q, qcross2 );
153 dQMultiply2( qrel, qq, qrel2 );
155 else
157 // pretend joint->node[1].body->q is the identity
158 dQMultiply2( qrel, qcross2, qrel2 );
161 *angle2 = - getHingeAngleFromRelativeQuat( qrel, axis2 );
163 else
165 *angle1 = 0;
166 *angle2 = 0;
170 dReal
171 dxJointUniversal::getAngle1()
173 if ( node[0].body )
175 // length 1 joint axis in global coordinates, from each body
176 dVector3 ax1, ax2;
177 dMatrix3 R;
178 dQuaternion qcross, qq, qrel;
180 getAxes( ax1, ax2 );
182 // It should be possible to get both angles without explicitly
183 // constructing the rotation matrix of the cross. Basically,
184 // orientation of the cross about axis1 comes from body 2,
185 // about axis 2 comes from body 1, and the perpendicular
186 // axis can come from the two bodies somehow. (We don't really
187 // want to assume it's 90 degrees, because in general the
188 // constraints won't be perfectly satisfied, or even very well
189 // satisfied.)
191 // However, we'd need a version of getHingeAngleFromRElativeQuat()
192 // that CAN handle when its relative quat is rotated along a direction
193 // other than the given axis. What I have here works,
194 // although it's probably much slower than need be.
196 dRFrom2Axes( R, ax1[0], ax1[1], ax1[2], ax2[0], ax2[1], ax2[2] );
197 dRtoQ( R, qcross );
199 // This code is essential the same as getHingeAngle(), see the comments
200 // there for details.
202 // get qrel = relative rotation between node[0] and the cross
203 dQMultiply1( qq, node[0].body->q, qcross );
204 dQMultiply2( qrel, qq, qrel1 );
206 return getHingeAngleFromRelativeQuat( qrel, axis1 );
208 return 0;
212 dReal
213 dxJointUniversal::getAngle2()
215 if ( node[0].body )
217 // length 1 joint axis in global coordinates, from each body
218 dVector3 ax1, ax2;
219 dMatrix3 R;
220 dQuaternion qcross, qq, qrel;
222 getAxes( ax1, ax2 );
224 // It should be possible to get both angles without explicitly
225 // constructing the rotation matrix of the cross. Basically,
226 // orientation of the cross about axis1 comes from body 2,
227 // about axis 2 comes from body 1, and the perpendicular
228 // axis can come from the two bodies somehow. (We don't really
229 // want to assume it's 90 degrees, because in general the
230 // constraints won't be perfectly satisfied, or even very well
231 // satisfied.)
233 // However, we'd need a version of getHingeAngleFromRElativeQuat()
234 // that CAN handle when its relative quat is rotated along a direction
235 // other than the given axis. What I have here works,
236 // although it's probably much slower than need be.
238 dRFrom2Axes( R, ax2[0], ax2[1], ax2[2], ax1[0], ax1[1], ax1[2] );
239 dRtoQ( R, qcross );
241 if ( node[1].body )
243 dQMultiply1( qq, node[1].body->q, qcross );
244 dQMultiply2( qrel, qq, qrel2 );
246 else
248 // pretend joint->node[1].body->q is the identity
249 dQMultiply2( qrel, qcross, qrel2 );
252 return - getHingeAngleFromRelativeQuat( qrel, axis2 );
254 return 0;
258 void
259 dxJointUniversal::getSureMaxInfo( SureMaxInfo* info )
261 info->max_m = 6;
265 void
266 dxJointUniversal::getInfo1( dxJoint::Info1 *info )
268 info->nub = 4;
269 info->m = 4;
271 bool limiting1 = ( limot1.lostop >= -M_PI || limot1.histop <= M_PI ) &&
272 limot1.lostop <= limot1.histop;
273 bool limiting2 = ( limot2.lostop >= -M_PI || limot2.histop <= M_PI ) &&
274 limot2.lostop <= limot2.histop;
276 // We need to call testRotationLimit() even if we're motored, since it
277 // records the result.
278 limot1.limit = 0;
279 limot2.limit = 0;
281 if ( limiting1 || limiting2 )
283 dReal angle1, angle2;
284 getAngles( &angle1, &angle2 );
285 if ( limiting1 )
286 limot1.testRotationalLimit( angle1 );
287 if ( limiting2 )
288 limot2.testRotationalLimit( angle2 );
291 if ( limot1.limit || limot1.fmax > 0 ) info->m++;
292 if ( limot2.limit || limot2.fmax > 0 ) info->m++;
296 void
297 dxJointUniversal::getInfo2( dReal worldFPS, dReal worldERP,
298 int rowskip, dReal *J1, dReal *J2,
299 int pairskip, dReal *pairRhsCfm, dReal *pairLoHi,
300 int *findex )
302 // set the three ball-and-socket rows
303 setBall( this, worldFPS, worldERP, rowskip, J1, J2, pairskip, pairRhsCfm, anchor1, anchor2 );
305 // set the universal joint row. the angular velocity about an axis
306 // perpendicular to both joint axes should be equal. thus the constraint
307 // equation is
308 // p*w1 - p*w2 = 0
309 // where p is a vector normal to both joint axes, and w1 and w2
310 // are the angular velocity vectors of the two bodies.
312 // length 1 joint axis in global coordinates, from each body
313 dVector3 ax1, ax2;
314 // length 1 vector perpendicular to ax1 and ax2. Neither body can rotate
315 // about this.
316 dVector3 p;
318 // Since axis1 and axis2 may not be perpendicular
319 // we find a axis2_tmp which is really perpendicular to axis1
320 // and in the plane of axis1 and axis2
321 getAxes( ax1, ax2 );
323 dReal k = dCalcVectorDot3( ax1, ax2 );
325 dVector3 ax2_temp;
326 dAddVectorScaledVector3(ax2_temp, ax2, ax1, -k);
327 dCalcVectorCross3( p, ax1, ax2_temp );
328 dNormalize3( p );
330 int currRowSkip = 3 * rowskip;
332 dCopyVector3( J1 + currRowSkip + GI2__JA_MIN, p);
334 if ( node[1].body )
336 dCopyNegatedVector3( J2 + currRowSkip + GI2__JA_MIN, p);
340 // compute the right hand side of the constraint equation. set relative
341 // body velocities along p to bring the axes back to perpendicular.
342 // If ax1, ax2 are unit length joint axes as computed from body1 and
343 // body2, we need to rotate both bodies along the axis p. If theta
344 // is the angle between ax1 and ax2, we need an angular velocity
345 // along p to cover the angle erp * (theta - Pi/2) in one step:
347 // |angular_velocity| = angle/time = erp*(theta - Pi/2) / stepsize
348 // = (erp*fps) * (theta - Pi/2)
350 // if theta is close to Pi/2,
351 // theta - Pi/2 ~= cos(theta), so
352 // |angular_velocity| ~= (erp*fps) * (ax1 dot ax2)
354 int currPairSkip = 3 * pairskip;
356 pairRhsCfm[currPairSkip + GI2_RHS] = worldFPS * worldERP * (-k);
359 currRowSkip += rowskip; currPairSkip += pairskip;
361 // if the first angle is powered, or has joint limits, add in the stuff
362 if (limot1.addLimot( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, ax1, 1 ))
364 currRowSkip += rowskip; currPairSkip += pairskip;
367 // if the second angle is powered, or has joint limits, add in more stuff
368 limot2.addLimot( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, ax2, 1 );
372 void
373 dxJointUniversal::computeInitialRelativeRotations()
375 if ( node[0].body )
377 dVector3 ax1, ax2;
378 dMatrix3 R;
379 dQuaternion qcross;
381 getAxes( ax1, ax2 );
383 // Axis 1.
384 dRFrom2Axes( R, ax1[0], ax1[1], ax1[2], ax2[0], ax2[1], ax2[2] );
385 dRtoQ( R, qcross );
386 dQMultiply1( qrel1, node[0].body->q, qcross );
388 // Axis 2.
389 dRFrom2Axes( R, ax2[0], ax2[1], ax2[2], ax1[0], ax1[1], ax1[2] );
390 dRtoQ( R, qcross );
391 if ( node[1].body )
393 dQMultiply1( qrel2, node[1].body->q, qcross );
395 else
397 // set joint->qrel to qcross
398 for ( int i = 0; i < 4; i++ ) qrel2[i] = qcross[i];
403 void
404 dxJointUniversal::buildFirstBodyTorqueVector(dVector3 torqueVector, dReal torque1, dReal torque2)
406 if (this->flags & dJOINT_REVERSE)
408 dReal temp = torque1;
409 torque1 = -torque2;
410 torque2 = -temp;
413 dVector3 axis1, axis2;
414 getAxis(this, axis1, this->axis1);
415 getAxis2(this, axis2, this->axis2);
416 torqueVector[0] = axis1[0] * torque1 + axis2[0] * torque2;
417 torqueVector[1] = axis1[1] * torque1 + axis2[1] * torque2;
418 torqueVector[2] = axis1[2] * torque1 + axis2[2] * torque2;
421 void dJointSetUniversalAnchor( dJointID j, dReal x, dReal y, dReal z )
423 dxJointUniversal* joint = ( dxJointUniversal* )j;
424 dUASSERT( joint, "bad joint argument" );
425 checktype( joint, Universal );
426 setAnchors( joint, x, y, z, joint->anchor1, joint->anchor2 );
427 joint->computeInitialRelativeRotations();
431 void dJointSetUniversalAxis1( dJointID j, dReal x, dReal y, dReal z )
433 dxJointUniversal* joint = ( dxJointUniversal* )j;
434 dUASSERT( joint, "bad joint argument" );
435 checktype( joint, Universal );
436 if ( joint->flags & dJOINT_REVERSE )
437 setAxes( joint, x, y, z, NULL, joint->axis2 );
438 else
439 setAxes( joint, x, y, z, joint->axis1, NULL );
440 joint->computeInitialRelativeRotations();
443 void dJointSetUniversalAxis1Offset( dJointID j, dReal x, dReal y, dReal z,
444 dReal offset1, dReal offset2 )
446 dxJointUniversal* joint = ( dxJointUniversal* )j;
447 dUASSERT( joint, "bad joint argument" );
448 checktype( joint, Universal );
449 if ( joint->flags & dJOINT_REVERSE )
451 setAxes( joint, x, y, z, NULL, joint->axis2 );
452 offset1 = -offset1;
453 offset2 = -offset2;
455 else
456 setAxes( joint, x, y, z, joint->axis1, NULL );
458 joint->computeInitialRelativeRotations();
461 dVector3 ax2;
462 getAxis2( joint, ax2, joint->axis2 );
465 dVector3 ax1;
466 joint->getAxes(ax1, ax2);
471 dQuaternion qAngle;
472 dQFromAxisAndAngle(qAngle, x, y, z, offset1);
474 dMatrix3 R;
475 dRFrom2Axes( R, x, y, z, ax2[0], ax2[1], ax2[2] );
477 dQuaternion qcross;
478 dRtoQ( R, qcross );
480 dQuaternion qOffset;
481 dQMultiply0(qOffset, qAngle, qcross);
483 dQMultiply1( joint->qrel1, joint->node[0].body->q, qOffset );
485 // Calculating the second offset
486 dQFromAxisAndAngle(qAngle, ax2[0], ax2[1], ax2[2], offset2);
488 dRFrom2Axes( R, ax2[0], ax2[1], ax2[2], x, y, z );
489 dRtoQ( R, qcross );
491 dQMultiply1(qOffset, qAngle, qcross);
492 if ( joint->node[1].body )
494 dQMultiply1( joint->qrel2, joint->node[1].body->q, qOffset );
496 else
498 joint->qrel2[0] = qcross[0];
499 joint->qrel2[1] = qcross[1];
500 joint->qrel2[2] = qcross[2];
501 joint->qrel2[3] = qcross[3];
506 void dJointSetUniversalAxis2( dJointID j, dReal x, dReal y, dReal z )
508 dxJointUniversal* joint = ( dxJointUniversal* )j;
509 dUASSERT( joint, "bad joint argument" );
510 checktype( joint, Universal );
511 if ( joint->flags & dJOINT_REVERSE )
512 setAxes( joint, x, y, z, joint->axis1, NULL );
513 else
514 setAxes( joint, x, y, z, NULL, joint->axis2 );
515 joint->computeInitialRelativeRotations();
518 void dJointSetUniversalAxis2Offset( dJointID j, dReal x, dReal y, dReal z,
519 dReal offset1, dReal offset2 )
521 dxJointUniversal* joint = ( dxJointUniversal* )j;
522 dUASSERT( joint, "bad joint argument" );
523 checktype( joint, Universal );
525 if ( joint->flags & dJOINT_REVERSE )
527 setAxes( joint, x, y, z, joint->axis1, NULL );
528 offset1 = -offset2;
529 offset2 = -offset1;
531 else
532 setAxes( joint, x, y, z, NULL, joint->axis2 );
535 joint->computeInitialRelativeRotations();
537 // It is easier to retreive the 2 axes here since
538 // when there is only one body B2 (the axes switch position)
539 // Doing this way eliminate the need to write the code differently
540 // for both case.
541 dVector3 ax1, ax2;
542 joint->getAxes(ax1, ax2 );
546 dQuaternion qAngle;
547 dQFromAxisAndAngle(qAngle, ax1[0], ax1[1], ax1[2], offset1);
549 dMatrix3 R;
550 dRFrom2Axes( R, ax1[0], ax1[1], ax1[2], ax2[0], ax2[1], ax2[2]);
552 dQuaternion qcross;
553 dRtoQ( R, qcross );
555 dQuaternion qOffset;
556 dQMultiply0(qOffset, qAngle, qcross);
560 dQMultiply1( joint->qrel1, joint->node[0].body->q, qOffset );
563 // Calculating the second offset
564 dQFromAxisAndAngle(qAngle, ax2[0], ax2[1], ax2[2], offset2);
566 dRFrom2Axes( R, ax2[0], ax2[1], ax2[2], ax1[0], ax1[1], ax1[2]);
567 dRtoQ( R, qcross );
569 dQMultiply1(qOffset, qAngle, qcross);
570 if ( joint->node[1].body )
572 dQMultiply1( joint->qrel2, joint->node[1].body->q, qOffset );
574 else
576 joint->qrel2[0] = qcross[0];
577 joint->qrel2[1] = qcross[1];
578 joint->qrel2[2] = qcross[2];
579 joint->qrel2[3] = qcross[3];
584 void dJointGetUniversalAnchor( dJointID j, dVector3 result )
586 dxJointUniversal* joint = ( dxJointUniversal* )j;
587 dUASSERT( joint, "bad joint argument" );
588 dUASSERT( result, "bad result argument" );
589 checktype( joint, Universal );
590 if ( joint->flags & dJOINT_REVERSE )
591 getAnchor2( joint, result, joint->anchor2 );
592 else
593 getAnchor( joint, result, joint->anchor1 );
597 void dJointGetUniversalAnchor2( dJointID j, dVector3 result )
599 dxJointUniversal* joint = ( dxJointUniversal* )j;
600 dUASSERT( joint, "bad joint argument" );
601 dUASSERT( result, "bad result argument" );
602 checktype( joint, Universal );
603 if ( joint->flags & dJOINT_REVERSE )
604 getAnchor( joint, result, joint->anchor1 );
605 else
606 getAnchor2( joint, result, joint->anchor2 );
610 void dJointGetUniversalAxis1( dJointID j, dVector3 result )
612 dxJointUniversal* joint = ( dxJointUniversal* )j;
613 dUASSERT( joint, "bad joint argument" );
614 dUASSERT( result, "bad result argument" );
615 checktype( joint, Universal );
616 if ( joint->flags & dJOINT_REVERSE )
617 getAxis2( joint, result, joint->axis2 );
618 else
619 getAxis( joint, result, joint->axis1 );
623 void dJointGetUniversalAxis2( dJointID j, dVector3 result )
625 dxJointUniversal* joint = ( dxJointUniversal* )j;
626 dUASSERT( joint, "bad joint argument" );
627 dUASSERT( result, "bad result argument" );
628 checktype( joint, Universal );
629 if ( joint->flags & dJOINT_REVERSE )
630 getAxis( joint, result, joint->axis1 );
631 else
632 getAxis2( joint, result, joint->axis2 );
636 void dJointSetUniversalParam( dJointID j, int parameter, dReal value )
638 dxJointUniversal* joint = ( dxJointUniversal* )j;
639 dUASSERT( joint, "bad joint argument" );
640 checktype( joint, Universal );
641 if (( parameter & 0xff00 ) == 0x100 )
643 joint->limot2.set( parameter & 0xff, value );
645 else
647 joint->limot1.set( parameter, value );
652 dReal dJointGetUniversalParam( dJointID j, int parameter )
654 dxJointUniversal* joint = ( dxJointUniversal* )j;
655 dUASSERT( joint, "bad joint argument" );
656 checktype( joint, Universal );
657 if (( parameter & 0xff00 ) == 0x100 )
659 return joint->limot2.get( parameter & 0xff );
661 else
663 return joint->limot1.get( parameter );
667 void dJointGetUniversalAngles( dJointID j, dReal *angle1, dReal *angle2 )
669 dxJointUniversal* joint = ( dxJointUniversal* )j;
670 dUASSERT( joint, "bad joint argument" );
671 checktype( joint, Universal );
672 if ( joint->flags & dJOINT_REVERSE )
674 joint->getAngles( angle2, angle1 );
675 *angle2 = -(*angle2);
676 return;
678 else
679 return joint->getAngles( angle1, angle2 );
683 dReal dJointGetUniversalAngle1( dJointID j )
685 dxJointUniversal* joint = ( dxJointUniversal* )j;
686 dUASSERT( joint, "bad joint argument" );
687 checktype( joint, Universal );
688 if ( joint->flags & dJOINT_REVERSE )
689 return joint->getAngle2();
690 else
691 return joint->getAngle1();
695 dReal dJointGetUniversalAngle2( dJointID j )
697 dxJointUniversal* joint = ( dxJointUniversal* )j;
698 dUASSERT( joint, "bad joint argument" );
699 checktype( joint, Universal );
700 if ( joint->flags & dJOINT_REVERSE )
701 return -joint->getAngle1();
702 else
703 return joint->getAngle2();
707 dReal dJointGetUniversalAngle1Rate( dJointID j )
709 dxJointUniversal* joint = ( dxJointUniversal* )j;
710 dUASSERT( joint, "bad joint argument" );
711 checktype( joint, Universal );
713 if ( joint->node[0].body )
715 dVector3 axis;
717 if ( joint->flags & dJOINT_REVERSE )
718 getAxis2( joint, axis, joint->axis2 );
719 else
720 getAxis( joint, axis, joint->axis1 );
722 dReal rate = dCalcVectorDot3( axis, joint->node[0].body->avel );
723 if ( joint->node[1].body )
724 rate -= dCalcVectorDot3( axis, joint->node[1].body->avel );
725 return rate;
727 return 0;
731 dReal dJointGetUniversalAngle2Rate( dJointID j )
733 dxJointUniversal* joint = ( dxJointUniversal* )j;
734 dUASSERT( joint, "bad joint argument" );
735 checktype( joint, Universal );
737 if ( joint->node[0].body )
739 dVector3 axis;
741 if ( joint->flags & dJOINT_REVERSE )
742 getAxis( joint, axis, joint->axis1 );
743 else
744 getAxis2( joint, axis, joint->axis2 );
746 dReal rate = dCalcVectorDot3( axis, joint->node[0].body->avel );
747 if ( joint->node[1].body ) rate -= dCalcVectorDot3( axis, joint->node[1].body->avel );
748 return rate;
750 return 0;
754 void dJointAddUniversalTorques( dJointID j, dReal torque1, dReal torque2 )
756 dxJointUniversal* joint = ( dxJointUniversal* )j;
757 dAASSERT( joint );
758 checktype( joint, Universal );
760 dVector3 axis;
761 joint->buildFirstBodyTorqueVector(axis, torque1, torque2);
763 if ( joint->node[0].body != 0 )
764 dBodyAddTorque( joint->node[0].body, axis[0], axis[1], axis[2] );
765 if ( joint->node[1].body != 0 )
766 dBodyAddTorque( joint->node[1].body, -axis[0], -axis[1], -axis[2] );
770 dJointType
771 dxJointUniversal::type() const
773 return dJointTypeUniversal;
777 sizeint
778 dxJointUniversal::size() const
780 return sizeof( *this );
785 void
786 dxJointUniversal::setRelativeValues()
788 dVector3 anchor;
789 dJointGetUniversalAnchor(this, anchor);
790 setAnchors( this, anchor[0], anchor[1], anchor[2], anchor1, anchor2 );
792 dVector3 ax1,ax2;
793 dJointGetUniversalAxis1(this, ax1);
794 dJointGetUniversalAxis2(this, ax2);
796 if ( flags & dJOINT_REVERSE )
798 setAxes( this, ax1[0],ax1[1],ax1[2], NULL, axis2 );
799 setAxes( this, ax2[0],ax2[1],ax2[2], axis1, NULL );
801 else
803 setAxes( this, ax1[0],ax1[1],ax1[2], axis1, NULL );
804 setAxes( this, ax2[0],ax2[1],ax2[2], NULL, axis2 );
807 computeInitialRelativeRotations();