joint.cpp | joint.cpp | |||
---|---|---|---|---|
skipping to change at line 36 | skipping to change at line 36 | |||
to the static environment (i.e. the absolute frame) is to check the second | to the static environment (i.e. the absolute frame) is to check the second | |||
body (joint->node[1].body), and if it is zero then behave as if its body | body (joint->node[1].body), and if it is zero then behave as if its body | |||
transform is the identity. | transform is the identity. | |||
*/ | */ | |||
#include <ode/ode.h> | #include <ode/ode.h> | |||
#include <ode/odemath.h> | #include <ode/odemath.h> | |||
#include <ode/rotation.h> | #include <ode/rotation.h> | |||
#include <ode/matrix.h> | #include <ode/matrix.h> | |||
#include "config.h" | ||||
#include "joint.h" | #include "joint.h" | |||
#include "joint_internal.h" | #include "joint_internal.h" | |||
extern void addObjectToList( dObject *obj, dObject **first ); | extern void addObjectToList( dObject *obj, dObject **first ); | |||
dxJoint::dxJoint( dxWorld *w ) : | dxJoint::dxJoint( dxWorld *w ) : | |||
dObject( w ) | dObject( w ) | |||
{ | { | |||
//printf("constructing %p\n", this); | //printf("constructing %p\n", this); | |||
dIASSERT( w ); | dIASSERT( w ); | |||
skipping to change at line 95 | skipping to change at line 96 | |||
{ | { | |||
// anchor points in global coordinates with respect to body PORs. | // anchor points in global coordinates with respect to body PORs. | |||
dVector3 a1, a2; | dVector3 a1, a2; | |||
int s = info->rowskip; | int s = info->rowskip; | |||
// set jacobian | // set jacobian | |||
info->J1l[0] = 1; | info->J1l[0] = 1; | |||
info->J1l[s+1] = 1; | info->J1l[s+1] = 1; | |||
info->J1l[2*s+2] = 1; | info->J1l[2*s+2] = 1; | |||
dMULTIPLY0_331( a1, joint->node[0].body->posr.R, anchor1 ); | dMultiply0_331( a1, joint->node[0].body->posr.R, anchor1 ); | |||
dCROSSMAT( info->J1a, a1, s, -, + ); | dSetCrossMatrixMinus( info->J1a, a1, s ); | |||
if ( joint->node[1].body ) | if ( joint->node[1].body ) | |||
{ | { | |||
info->J2l[0] = -1; | info->J2l[0] = -1; | |||
info->J2l[s+1] = -1; | info->J2l[s+1] = -1; | |||
info->J2l[2*s+2] = -1; | info->J2l[2*s+2] = -1; | |||
dMULTIPLY0_331( a2, joint->node[1].body->posr.R, anchor2 ); | dMultiply0_331( a2, joint->node[1].body->posr.R, anchor2 ); | |||
dCROSSMAT( info->J2a, a2, s, + , - ); | dSetCrossMatrixPlus( info->J2a, a2, s ); | |||
} | } | |||
// set right hand side | // set right hand side | |||
dReal k = info->fps * info->erp; | dReal k = info->fps * info->erp; | |||
if ( joint->node[1].body ) | if ( joint->node[1].body ) | |||
{ | { | |||
for ( int j = 0; j < 3; j++ ) | for ( int j = 0; j < 3; j++ ) | |||
{ | { | |||
info->c[j] = k * ( a2[j] + joint->node[1].body->posr.pos[j] - | info->c[j] = k * ( a2[j] + joint->node[1].body->posr.pos[j] - | |||
a1[j] - joint->node[0].body->posr.pos[j] ); | a1[j] - joint->node[0].body->posr.pos[j] ); | |||
skipping to change at line 149 | skipping to change at line 150 | |||
// get vectors normal to the axis. in setBall() axis,q1,q2 is [1 0 0], | // get vectors normal to the axis. in setBall() axis,q1,q2 is [1 0 0], | |||
// [0 1 0] and [0 0 1], which makes everything much easier. | // [0 1 0] and [0 0 1], which makes everything much easier. | |||
dVector3 q1, q2; | dVector3 q1, q2; | |||
dPlaneSpace( axis, q1, q2 ); | dPlaneSpace( axis, q1, q2 ); | |||
// set jacobian | // set jacobian | |||
for ( i = 0; i < 3; i++ ) info->J1l[i] = axis[i]; | for ( i = 0; i < 3; i++ ) info->J1l[i] = axis[i]; | |||
for ( i = 0; i < 3; i++ ) info->J1l[s+i] = q1[i]; | for ( i = 0; i < 3; i++ ) info->J1l[s+i] = q1[i]; | |||
for ( i = 0; i < 3; i++ ) info->J1l[2*s+i] = q2[i]; | for ( i = 0; i < 3; i++ ) info->J1l[2*s+i] = q2[i]; | |||
dMULTIPLY0_331( a1, joint->node[0].body->posr.R, anchor1 ); | dMultiply0_331( a1, joint->node[0].body->posr.R, anchor1 ); | |||
dCROSS( info->J1a, = , a1, axis ); | dCalcVectorCross3( info->J1a, a1, axis ); | |||
dCROSS( info->J1a + s, = , a1, q1 ); | dCalcVectorCross3( info->J1a + s, a1, q1 ); | |||
dCROSS( info->J1a + 2*s, = , a1, q2 ); | dCalcVectorCross3( info->J1a + 2*s, a1, q2 ); | |||
if ( joint->node[1].body ) | if ( joint->node[1].body ) | |||
{ | { | |||
for ( i = 0; i < 3; i++ ) info->J2l[i] = -axis[i]; | for ( i = 0; i < 3; i++ ) info->J2l[i] = -axis[i]; | |||
for ( i = 0; i < 3; i++ ) info->J2l[s+i] = -q1[i]; | for ( i = 0; i < 3; i++ ) info->J2l[s+i] = -q1[i]; | |||
for ( i = 0; i < 3; i++ ) info->J2l[2*s+i] = -q2[i]; | for ( i = 0; i < 3; i++ ) info->J2l[2*s+i] = -q2[i]; | |||
dMULTIPLY0_331( a2, joint->node[1].body->posr.R, anchor2 ); | dMultiply0_331( a2, joint->node[1].body->posr.R, anchor2 ); | |||
dCROSS( info->J2a, = -, a2, axis ); | dReal *J2a = info->J2a; | |||
dCROSS( info->J2a + s, = -, a2, q1 ); | dCalcVectorCross3( J2a, a2, axis ); | |||
dCROSS( info->J2a + 2*s, = -, a2, q2 ); | dNegateVector3( J2a ); | |||
dReal *J2a_plus_s = J2a + s; | ||||
dCalcVectorCross3( J2a_plus_s, a2, q1 ); | ||||
dNegateVector3( J2a_plus_s ); | ||||
dReal *J2a_plus_2s = J2a_plus_s + s; | ||||
dCalcVectorCross3( J2a_plus_2s, a2, q2 ); | ||||
dNegateVector3( J2a_plus_2s ); | ||||
} | } | |||
// set right hand side - measure error along (axis,q1,q2) | // set right hand side - measure error along (axis,q1,q2) | |||
dReal k1 = info->fps * erp1; | dReal k1 = info->fps * erp1; | |||
dReal k = info->fps * info->erp; | dReal k = info->fps * info->erp; | |||
for ( i = 0; i < 3; i++ ) a1[i] += joint->node[0].body->posr.pos[i]; | for ( i = 0; i < 3; i++ ) a1[i] += joint->node[0].body->posr.pos[i]; | |||
if ( joint->node[1].body ) | if ( joint->node[1].body ) | |||
{ | { | |||
for ( i = 0; i < 3; i++ ) a2[i] += joint->node[1].body->posr.pos[i] ; | for ( i = 0; i < 3; i++ ) a2[i] += joint->node[1].body->posr.pos[i] ; | |||
info->c[0] = k1 * ( dDOT( axis, a2 ) - dDOT( axis, a1 ) ); | ||||
info->c[1] = k * ( dDOT( q1, a2 ) - dDOT( q1, a1 ) ); | dVector3 a2_minus_a1; | |||
info->c[2] = k * ( dDOT( q2, a2 ) - dDOT( q2, a1 ) ); | dSubtractVectors3(a2_minus_a1, a2, a1); | |||
info->c[0] = k1 * dCalcVectorDot3( axis, a2_minus_a1 ); | ||||
info->c[1] = k * dCalcVectorDot3( q1, a2_minus_a1 ); | ||||
info->c[2] = k * dCalcVectorDot3( q2, a2_minus_a1 ); | ||||
} | } | |||
else | else | |||
{ | { | |||
info->c[0] = k1 * ( dDOT( axis, anchor2 ) - dDOT( axis, a1 ) ); | dVector3 anchor2_minus_a1; | |||
info->c[1] = k * ( dDOT( q1, anchor2 ) - dDOT( q1, a1 ) ); | dSubtractVectors3(anchor2_minus_a1, anchor2, a1); | |||
info->c[2] = k * ( dDOT( q2, anchor2 ) - dDOT( q2, a1 ) ); | info->c[0] = k1 * dCalcVectorDot3( axis, anchor2_minus_a1 ); | |||
info->c[1] = k * dCalcVectorDot3( q1, anchor2_minus_a1 ); | ||||
info->c[2] = k * dCalcVectorDot3( q2, anchor2_minus_a1 ); | ||||
} | } | |||
} | } | |||
// set three orientation rows in the constraint equation, and the | // set three orientation rows in the constraint equation, and the | |||
// corresponding right hand side. | // corresponding right hand side. | |||
void setFixedOrientation( dxJoint *joint, dxJoint::Info2 *info, dQuaternion qrel, int start_row ) | void setFixedOrientation( dxJoint *joint, dxJoint::Info2 *info, dQuaternion qrel, int start_row ) | |||
{ | { | |||
int s = info->rowskip; | int s = info->rowskip; | |||
int start_index = start_row * s; | int start_index = start_row * s; | |||
skipping to change at line 235 | skipping to change at line 247 | |||
else | else | |||
{ | { | |||
dQMultiply3( qerr, joint->node[0].body->q, qrel ); | dQMultiply3( qerr, joint->node[0].body->q, qrel ); | |||
} | } | |||
if ( qerr[0] < 0 ) | if ( qerr[0] < 0 ) | |||
{ | { | |||
qerr[1] = -qerr[1]; // adjust sign of qerr to make theta small | qerr[1] = -qerr[1]; // adjust sign of qerr to make theta small | |||
qerr[2] = -qerr[2]; | qerr[2] = -qerr[2]; | |||
qerr[3] = -qerr[3]; | qerr[3] = -qerr[3]; | |||
} | } | |||
dMULTIPLY0_331( e, joint->node[0].body->posr.R, qerr + 1 ); // @@@ bad SIMD padding! | dMultiply0_331( e, joint->node[0].body->posr.R, qerr + 1 ); // @@@ bad SIMD padding! | |||
dReal k = info->fps * info->erp; | dReal k = info->fps * info->erp; | |||
info->c[start_row] = 2 * k * e[0]; | info->c[start_row] = 2 * k * e[0]; | |||
info->c[start_row+1] = 2 * k * e[1]; | info->c[start_row+1] = 2 * k * e[1]; | |||
info->c[start_row+2] = 2 * k * e[2]; | info->c[start_row+2] = 2 * k * e[2]; | |||
} | } | |||
// compute anchor points relative to bodies | // compute anchor points relative to bodies | |||
void setAnchors( dxJoint *j, dReal x, dReal y, dReal z, | void setAnchors( dxJoint *j, dReal x, dReal y, dReal z, | |||
dVector3 anchor1, dVector3 anchor2 ) | dVector3 anchor1, dVector3 anchor2 ) | |||
{ | { | |||
if ( j->node[0].body ) | if ( j->node[0].body ) | |||
{ | { | |||
dReal q[4]; | dReal q[4]; | |||
q[0] = x - j->node[0].body->posr.pos[0]; | q[0] = x - j->node[0].body->posr.pos[0]; | |||
q[1] = y - j->node[0].body->posr.pos[1]; | q[1] = y - j->node[0].body->posr.pos[1]; | |||
q[2] = z - j->node[0].body->posr.pos[2]; | q[2] = z - j->node[0].body->posr.pos[2]; | |||
q[3] = 0; | q[3] = 0; | |||
dMULTIPLY1_331( anchor1, j->node[0].body->posr.R, q ); | dMultiply1_331( anchor1, j->node[0].body->posr.R, q ); | |||
if ( j->node[1].body ) | if ( j->node[1].body ) | |||
{ | { | |||
q[0] = x - j->node[1].body->posr.pos[0]; | q[0] = x - j->node[1].body->posr.pos[0]; | |||
q[1] = y - j->node[1].body->posr.pos[1]; | q[1] = y - j->node[1].body->posr.pos[1]; | |||
q[2] = z - j->node[1].body->posr.pos[2]; | q[2] = z - j->node[1].body->posr.pos[2]; | |||
q[3] = 0; | q[3] = 0; | |||
dMULTIPLY1_331( anchor2, j->node[1].body->posr.R, q ); | dMultiply1_331( anchor2, j->node[1].body->posr.R, q ); | |||
} | } | |||
else | else | |||
{ | { | |||
anchor2[0] = x; | anchor2[0] = x; | |||
anchor2[1] = y; | anchor2[1] = y; | |||
anchor2[2] = z; | anchor2[2] = z; | |||
} | } | |||
} | } | |||
anchor1[3] = 0; | anchor1[3] = 0; | |||
anchor2[3] = 0; | anchor2[3] = 0; | |||
skipping to change at line 289 | skipping to change at line 301 | |||
if ( j->node[0].body ) | if ( j->node[0].body ) | |||
{ | { | |||
dReal q[4]; | dReal q[4]; | |||
q[0] = x; | q[0] = x; | |||
q[1] = y; | q[1] = y; | |||
q[2] = z; | q[2] = z; | |||
q[3] = 0; | q[3] = 0; | |||
dNormalize3( q ); | dNormalize3( q ); | |||
if ( axis1 ) | if ( axis1 ) | |||
{ | { | |||
dMULTIPLY1_331( axis1, j->node[0].body->posr.R, q ); | dMultiply1_331( axis1, j->node[0].body->posr.R, q ); | |||
axis1[3] = 0; | axis1[3] = 0; | |||
} | } | |||
if ( axis2 ) | if ( axis2 ) | |||
{ | { | |||
if ( j->node[1].body ) | if ( j->node[1].body ) | |||
{ | { | |||
dMULTIPLY1_331( axis2, j->node[1].body->posr.R, q ); | dMultiply1_331( axis2, j->node[1].body->posr.R, q ); | |||
} | } | |||
else | else | |||
{ | { | |||
axis2[0] = x; | axis2[0] = x; | |||
axis2[1] = y; | axis2[1] = y; | |||
axis2[2] = z; | axis2[2] = z; | |||
} | } | |||
axis2[3] = 0; | axis2[3] = 0; | |||
} | } | |||
} | } | |||
} | } | |||
void getAnchor( dxJoint *j, dVector3 result, dVector3 anchor1 ) | void getAnchor( dxJoint *j, dVector3 result, dVector3 anchor1 ) | |||
{ | { | |||
if ( j->node[0].body ) | if ( j->node[0].body ) | |||
{ | { | |||
dMULTIPLY0_331( result, j->node[0].body->posr.R, anchor1 ); | dMultiply0_331( result, j->node[0].body->posr.R, anchor1 ); | |||
result[0] += j->node[0].body->posr.pos[0]; | result[0] += j->node[0].body->posr.pos[0]; | |||
result[1] += j->node[0].body->posr.pos[1]; | result[1] += j->node[0].body->posr.pos[1]; | |||
result[2] += j->node[0].body->posr.pos[2]; | result[2] += j->node[0].body->posr.pos[2]; | |||
} | } | |||
} | } | |||
void getAnchor2( dxJoint *j, dVector3 result, dVector3 anchor2 ) | void getAnchor2( dxJoint *j, dVector3 result, dVector3 anchor2 ) | |||
{ | { | |||
if ( j->node[1].body ) | if ( j->node[1].body ) | |||
{ | { | |||
dMULTIPLY0_331( result, j->node[1].body->posr.R, anchor2 ); | dMultiply0_331( result, j->node[1].body->posr.R, anchor2 ); | |||
result[0] += j->node[1].body->posr.pos[0]; | result[0] += j->node[1].body->posr.pos[0]; | |||
result[1] += j->node[1].body->posr.pos[1]; | result[1] += j->node[1].body->posr.pos[1]; | |||
result[2] += j->node[1].body->posr.pos[2]; | result[2] += j->node[1].body->posr.pos[2]; | |||
} | } | |||
else | else | |||
{ | { | |||
result[0] = anchor2[0]; | result[0] = anchor2[0]; | |||
result[1] = anchor2[1]; | result[1] = anchor2[1]; | |||
result[2] = anchor2[2]; | result[2] = anchor2[2]; | |||
} | } | |||
} | } | |||
void getAxis( dxJoint *j, dVector3 result, dVector3 axis1 ) | void getAxis( dxJoint *j, dVector3 result, dVector3 axis1 ) | |||
{ | { | |||
if ( j->node[0].body ) | if ( j->node[0].body ) | |||
{ | { | |||
dMULTIPLY0_331( result, j->node[0].body->posr.R, axis1 ); | dMultiply0_331( result, j->node[0].body->posr.R, axis1 ); | |||
} | } | |||
} | } | |||
void getAxis2( dxJoint *j, dVector3 result, dVector3 axis2 ) | void getAxis2( dxJoint *j, dVector3 result, dVector3 axis2 ) | |||
{ | { | |||
if ( j->node[1].body ) | if ( j->node[1].body ) | |||
{ | { | |||
dMULTIPLY0_331( result, j->node[1].body->posr.R, axis2 ); | dMultiply0_331( result, j->node[1].body->posr.R, axis2 ); | |||
} | } | |||
else | else | |||
{ | { | |||
result[0] = axis2[0]; | result[0] = axis2[0]; | |||
result[1] = axis2[1]; | result[1] = axis2[1]; | |||
result[2] = axis2[2]; | result[2] = axis2[2]; | |||
} | } | |||
} | } | |||
dReal getHingeAngleFromRelativeQuat( dQuaternion qrel, dVector3 axis ) | dReal getHingeAngleFromRelativeQuat( dQuaternion qrel, dVector3 axis ) | |||
skipping to change at line 386 | skipping to change at line 398 | |||
// hinge axis and then in the opposite direction. the result is that th eta | // hinge axis and then in the opposite direction. the result is that th eta | |||
// will appear to go "backwards" every other cycle. here is a fix: if u | // will appear to go "backwards" every other cycle. here is a fix: if u | |||
// points "away" from the direction of the hinge (motor) axis (i.e. mor e | // points "away" from the direction of the hinge (motor) axis (i.e. mor e | |||
// than 90 degrees) then use -q instead of q. this represents the same | // than 90 degrees) then use -q instead of q. this represents the same | |||
// rotation, but results in the cos(theta/2) value being sign inverted. | // rotation, but results in the cos(theta/2) value being sign inverted. | |||
// extract the angle from the quaternion. cost2 = cos(theta/2), | // extract the angle from the quaternion. cost2 = cos(theta/2), | |||
// sint2 = |sin(theta/2)| | // sint2 = |sin(theta/2)| | |||
dReal cost2 = qrel[0]; | dReal cost2 = qrel[0]; | |||
dReal sint2 = dSqrt( qrel[1] * qrel[1] + qrel[2] * qrel[2] + qrel[3] * qrel[3] ); | dReal sint2 = dSqrt( qrel[1] * qrel[1] + qrel[2] * qrel[2] + qrel[3] * qrel[3] ); | |||
dReal theta = ( dDOT( qrel + 1, axis ) >= 0 ) ? // @@@ padding assumpti ons | dReal theta = ( dCalcVectorDot3( qrel + 1, axis ) >= 0 ) ? // @@@ paddi ng assumptions | |||
( 2 * dAtan2( sint2, cost2 ) ) : // if u points in direc tion of axis | ( 2 * dAtan2( sint2, cost2 ) ) : // if u points in direc tion of axis | |||
( 2 * dAtan2( sint2, -cost2 ) ); // if u points in oppos ite direction | ( 2 * dAtan2( sint2, -cost2 ) ); // if u points in oppos ite direction | |||
// the angle we get will be between 0..2*pi, but we want to return angl es | // the angle we get will be between 0..2*pi, but we want to return angl es | |||
// between -pi..pi | // between -pi..pi | |||
if ( theta > M_PI ) theta -= ( dReal )( 2 * M_PI ); | if ( theta > M_PI ) theta -= ( dReal )( 2 * M_PI ); | |||
// the angle we've just extracted has the wrong sign | // the angle we've just extracted has the wrong sign | |||
theta = -theta; | theta = -theta; | |||
skipping to change at line 572 | skipping to change at line 584 | |||
// extra tiny bit of computation) in doing this adjustment. note th at we | // extra tiny bit of computation) in doing this adjustment. note th at we | |||
// only need to do this if the constraint connects two bodies. | // only need to do this if the constraint connects two bodies. | |||
dVector3 ltd = {0,0,0}; // Linear Torque Decoupling vector (a torqu e) | dVector3 ltd = {0,0,0}; // Linear Torque Decoupling vector (a torqu e) | |||
if ( !rotational && joint->node[1].body ) | if ( !rotational && joint->node[1].body ) | |||
{ | { | |||
dVector3 c; | dVector3 c; | |||
c[0] = REAL( 0.5 ) * ( joint->node[1].body->posr.pos[0] - joint ->node[0].body->posr.pos[0] ); | c[0] = REAL( 0.5 ) * ( joint->node[1].body->posr.pos[0] - joint ->node[0].body->posr.pos[0] ); | |||
c[1] = REAL( 0.5 ) * ( joint->node[1].body->posr.pos[1] - joint ->node[0].body->posr.pos[1] ); | c[1] = REAL( 0.5 ) * ( joint->node[1].body->posr.pos[1] - joint ->node[0].body->posr.pos[1] ); | |||
c[2] = REAL( 0.5 ) * ( joint->node[1].body->posr.pos[2] - joint ->node[0].body->posr.pos[2] ); | c[2] = REAL( 0.5 ) * ( joint->node[1].body->posr.pos[2] - joint ->node[0].body->posr.pos[2] ); | |||
dCROSS( ltd, = , c, ax1 ); | dCalcVectorCross3( ltd, c, ax1 ); | |||
info->J1a[srow+0] = ltd[0]; | info->J1a[srow+0] = ltd[0]; | |||
info->J1a[srow+1] = ltd[1]; | info->J1a[srow+1] = ltd[1]; | |||
info->J1a[srow+2] = ltd[2]; | info->J1a[srow+2] = ltd[2]; | |||
info->J2a[srow+0] = ltd[0]; | info->J2a[srow+0] = ltd[0]; | |||
info->J2a[srow+1] = ltd[1]; | info->J2a[srow+1] = ltd[1]; | |||
info->J2a[srow+2] = ltd[2]; | info->J2a[srow+2] = ltd[2]; | |||
} | } | |||
// if we're limited low and high simultaneously, the joint motor is | // if we're limited low and high simultaneously, the joint motor is | |||
// ineffective | // ineffective | |||
skipping to change at line 669 | skipping to change at line 681 | |||
info->hi[row] = 0; | info->hi[row] = 0; | |||
} | } | |||
// deal with bounce | // deal with bounce | |||
if ( bounce > 0 ) | if ( bounce > 0 ) | |||
{ | { | |||
// calculate joint velocity | // calculate joint velocity | |||
dReal vel; | dReal vel; | |||
if ( rotational ) | if ( rotational ) | |||
{ | { | |||
vel = dDOT( joint->node[0].body->avel, ax1 ); | vel = dCalcVectorDot3( joint->node[0].body->avel, a x1 ); | |||
if ( joint->node[1].body ) | if ( joint->node[1].body ) | |||
vel -= dDOT( joint->node[1].body->avel, ax1 ); | vel -= dCalcVectorDot3( joint->node[1].body->av el, ax1 ); | |||
} | } | |||
else | else | |||
{ | { | |||
vel = dDOT( joint->node[0].body->lvel, ax1 ); | vel = dCalcVectorDot3( joint->node[0].body->lvel, a x1 ); | |||
if ( joint->node[1].body ) | if ( joint->node[1].body ) | |||
vel -= dDOT( joint->node[1].body->lvel, ax1 ); | vel -= dCalcVectorDot3( joint->node[1].body->lv el, ax1 ); | |||
} | } | |||
// only apply bounce if the velocity is incoming, and i f the | // only apply bounce if the velocity is incoming, and i f the | |||
// resulting c[] exceeds what we already have. | // resulting c[] exceeds what we already have. | |||
if ( limit == 1 ) | if ( limit == 1 ) | |||
{ | { | |||
// low limit | // low limit | |||
if ( vel < 0 ) | if ( vel < 0 ) | |||
{ | { | |||
dReal newc = -bounce * vel; | dReal newc = -bounce * vel; | |||
End of changes. 22 change blocks. | ||||
33 lines changed or deleted | 45 lines changed or added | |||
This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |