/* rotates a body by a certain angle around one of its axes */ void body_rotate_around_axis (dBodyID b, t_real angle, unsigned int axis_id) { t_vector u, v, t, rotation_axis; t_vector new_u, new_v, new_t; /* get old axes */ body_axis (u, b, 0); body_axis (v, b, 1); body_axis (t, b, 2); if (axis_id==0) dCopyVector3 (rotation_axis, u); else if (axis_id==1) dCopyVector3 (rotation_axis, v); else if (axis_id==2) dCopyVector3 (rotation_axis, t); else { err_message ("Invalid axis ID\n"); exit (EXIT_FAILURE); } /* rotate axes */ vector_rotate_around_axis (new_u, u, angle, rotation_axis); vector_rotate_around_axis (new_v, v, angle, rotation_axis); vector_rotate_around_axis (new_t, t, angle, rotation_axis); /* set the new axes to the body */ body_set_axes (b, new_u, new_v, new_t); }
dxPlanarJoint::dxPlanarJoint(dxWorld *world) : dxJoint(world) { dSetZero(anchor, 3); dCopyVector3(anchor1, anchor); dCopyVector3(anchor2, anchor); // default to plane z=0 dSetZero(planeNormal, 3); planeNormal[2] = 1; dCopyVector3(axis1, planeNormal); dCopyVector3(axis2, planeNormal); }
void dxJointDBall::updateTargetDistance() { dVector3 p1, p2; if (node[0].body) dBodyGetRelPointPos(node[0].body, anchor1[0], anchor1[1], anchor1[2], p1); else dCopyVector3(p1, anchor1); if (node[1].body) dBodyGetRelPointPos(node[1].body, anchor2[0], anchor2[1], anchor2[2], p2); else dCopyVector3(p2, anchor2); targetDistance = dCalcPointsDistance3(p1, p2); }
void dJointGetTransmissionContactPoint2( dJointID j, dVector3 result ) { dxJointTransmission* joint = static_cast<dxJointTransmission*>(j); dUASSERT( joint, "bad joint argument" ); dUASSERT( result, "bad result argument" ); dCopyVector3(result, joint->contacts[1]); }
void dJointGetDBallAnchor2( dJointID j, dVector3 result ) { dxJointDBall* joint = dynamic_cast<dxJointDBall*>(j); dUASSERT( joint, "bad joint argument" ); dUASSERT( result, "bad result argument" ); if ( joint->flags & dJOINT_REVERSE ) { if (joint->node[0].body) dBodyGetRelPointPos(joint->node[0].body, joint->anchor1[0], joint->anchor1[1], joint->anchor1[2], result); else dCopyVector3(result, joint->anchor1); } else { if (joint->node[1].body) dBodyGetRelPointPos(joint->node[1].body, joint->anchor2[0], joint->anchor2[1], joint->anchor2[2], result); else dCopyVector3(result, joint->anchor2); } }
void dJointPlanarSetAnchor(dJointID joint, dVector3 anchor) { dUASSERT( joint, "bad joint argument" ); checktype( joint, Plane2D ); dxPlanarJoint* planarJoint = (dxPlanarJoint*) joint; dCopyVector3(planarJoint->anchor, anchor); planarJoint->updatePlane(); }
dReal dxJointHinge2::measureAngle2() const { // bring axis 1 into second body's reference frame dVector3 p, q; if (node[0].body) dMultiply0_331( p, node[0].body->posr.R, axis1 ); else dCopyVector3(p, axis1); if (node[1].body) dMultiply1_331( q, node[1].body->posr.R, p ); else dCopyVector3(q, p); dReal x = dCalcVectorDot3( w1, q ); dReal y = dCalcVectorDot3( w2, q ); return -dAtan2( y, x ); }
void dJointPlanarSetPlaneNormal(dJointID joint, dVector3 planeNormal) { dUASSERT( joint, "bad joint argument" ); checktype( joint, Plane2D ); dxPlanarJoint* planarJoint = (dxPlanarJoint*) joint; dUASSERT(dLENGTHSQUARED(planeNormal) > 0.000001, "plane normal cannot have zero length"); dCopyVector3(planarJoint->planeNormal, planeNormal); dNormalize3(planarJoint->planeNormal); planarJoint->updatePlane(); }
void dJointGetPUAnchor( dJointID j, dVector3 result ) { dxJointPU* joint = ( dxJointPU* ) j; dUASSERT( joint, "bad joint argument" ); dUASSERT( result, "bad result argument" ); checktype( joint, PU ); if ( joint->node[1].body ) getAnchor2( joint, result, joint->anchor2 ); else { // result[i] = joint->anchor2[i]; dCopyVector3( result, joint->anchor2 ); } }
void dxPlanarJoint::updatePlane() { dBodyID body1 = this->node[0].body; if (body1) { // compute plane normal and anchor coordinates in the local coordinates of the first body dBodyVectorFromWorld(body1, planeNormal[0], planeNormal[1], planeNormal[2], axis1); dBodyGetPosRelPoint(body1, anchor[0], anchor[1], anchor[2], anchor1); dBodyID body2 = this->node[1].body; if (body2) // second body given, attach the plane to it { // compute plane normal and anchor coordinates in the local coordinates of the second body dBodyVectorFromWorld(body2, planeNormal[0], planeNormal[1], planeNormal[2], axis2); dBodyGetPosRelPoint(body2, anchor[0], anchor[1], anchor[2], anchor2); } else // second body not given, attach the plane to the world { // plane normal and anchor coordinates in the world world frame are the same as global coordinates dCopyVector3(axis2, planeNormal); dCopyVector3(anchor2, anchor); } } }
/* returns the force acting on the body */ void body_force (t_real *res, dBodyID b) { dCopyVector3 (res, dBodyGetForce (b)); }
/* returns the vector containing the position of the body */ void body_position (t_real *res, dBodyID b) { dCopyVector3 (res, dBodyGetPosition (b)); }
/* returns the value of the torque applied on a body */ void body_torque (t_real *res, dBodyID b) { dCopyVector3 (res, dBodyGetTorque (b)); }
/* returns the angular velocity of body b in the lab frame reference */ void body_angular_velocity (t_real *res, dBodyID b) { dCopyVector3 (res, dBodyGetAngularVel (b)); }
/* returns the velocity of the body b in the vector vec */ void body_velocity (t_real *res, dBodyID b) { dCopyVector3 (res, dBodyGetLinearVel (b)); }
void dxJointDBall::getInfo2( dxJoint::Info2 *info ) { info->erp = erp; info->cfm[0] = cfm; dVector3 globalA1, globalA2; dBodyGetRelPointPos(node[0].body, anchor1[0], anchor1[1], anchor1[2], globalA1); if (node[1].body) dBodyGetRelPointPos(node[1].body, anchor2[0], anchor2[1], anchor2[2], globalA2); else dCopyVector3(globalA2, anchor2); dVector3 q; dSubtractVectors3(q, globalA1, globalA2); #ifdef dSINGLE const dReal MIN_LENGTH = REAL(1e-7); #else const dReal MIN_LENGTH = REAL(1e-12); #endif if (dCalcVectorLength3(q) < MIN_LENGTH) { // too small, let's choose an arbitrary direction // heuristic: difference in velocities at anchors dVector3 v1, v2; dBodyGetPointVel(node[0].body, globalA1[0], globalA1[1], globalA1[2], v1); if (node[1].body) dBodyGetPointVel(node[1].body, globalA2[0], globalA2[1], globalA2[2], v2); else dSetZero(v2, 3); dSubtractVectors3(q, v1, v2); if (dCalcVectorLength3(q) < MIN_LENGTH) { // this direction is as good as any q[0] = 1; q[1] = 0; q[2] = 0; } } dNormalize3(q); info->J1l[0] = q[0]; info->J1l[1] = q[1]; info->J1l[2] = q[2]; dVector3 relA1; dBodyVectorToWorld(node[0].body, anchor1[0], anchor1[1], anchor1[2], relA1); dMatrix3 a1m; dSetZero(a1m, 12); dSetCrossMatrixMinus(a1m, relA1, 4); dMultiply1_331(info->J1a, a1m, q); if (node[1].body) { info->J2l[0] = -q[0]; info->J2l[1] = -q[1]; info->J2l[2] = -q[2]; dVector3 relA2; dBodyVectorToWorld(node[1].body, anchor2[0], anchor2[1], anchor2[2], relA2); dMatrix3 a2m; dSetZero(a2m, 12); dSetCrossMatrixPlus(a2m, relA2, 4); dMultiply1_331(info->J2a, a2m, q); } const dReal k = info->fps * info->erp; info->c[0] = k * (targetDistance - dCalcPointsDistance3(globalA1, globalA2)); }
void SimpleTrackedVehicleEnvironment::nearCallbackGrouserTerrain(dGeomID o1, dGeomID o2) { dBodyID b1 = dGeomGetBody(o1); dBodyID b2 = dGeomGetBody(o2); if(b1 && b2 && dAreConnectedExcluding(b1, b2, dJointTypeContact)) return; // body of the whole vehicle dBodyID vehicleBody = ((SimpleTrackedVehicle*)this->v)->vehicleBody; unsigned long geom1Categories = dGeomGetCategoryBits(o1); // speeds of the belts const dReal leftBeltSpeed = ((SimpleTrackedVehicle*)this->v)->leftTrack->getVelocity(); const dReal rightBeltSpeed = ((SimpleTrackedVehicle*)this->v)->rightTrack->getVelocity(); dReal beltSpeed = 0; // speed of the belt which is in collision and examined right now if (geom1Categories & Category::LEFT) { beltSpeed = leftBeltSpeed; } else { beltSpeed = rightBeltSpeed; } // the desired linear and angular speeds (set by desired track velocities) const dReal linearSpeed = (leftBeltSpeed + rightBeltSpeed) / 2; const dReal angularSpeed = (leftBeltSpeed - rightBeltSpeed) * steeringEfficiency / tracksDistance; // radius of the turn the robot is doing const dReal desiredRotationRadiusSigned = (fabs(angularSpeed) < 0.1) ? dInfinity : // is driving straight ((fabs(linearSpeed) < 0.1) ? 0 : // is rotating about a single point linearSpeed / angularSpeed // general movement ); dVector3 yAxisGlobal; // vector pointing from vehicle body center in the direction of +y axis dVector3 centerOfRotation; // at infinity if driving straight, so we need to distinguish the case { // compute the center of rotation dBodyVectorToWorld(vehicleBody, 0, 1, 0, yAxisGlobal); dCopyVector3(centerOfRotation, yAxisGlobal); // make the unit vector as long as we need (and change orientation if needed; the radius is a signed number) dScaleVector3(centerOfRotation, desiredRotationRadiusSigned); const dReal *vehicleBodyPos = dBodyGetPosition(vehicleBody); dAddVectors3(centerOfRotation, centerOfRotation, vehicleBodyPos); } int maxContacts = 20; dContact contact[maxContacts]; int numContacts = dCollide(o1, o2, maxContacts, &contact[0].geom, sizeof(dContact)); for(size_t i = 0; i < numContacts; i++) { dVector3 contactInVehiclePos; // position of the contact point relative to vehicle body dBodyGetPosRelPoint(vehicleBody, contact[i].geom.pos[0], contact[i].geom.pos[1], contact[i].geom.pos[2], contactInVehiclePos); dVector3 beltDirection; // vector tangent to the belt pointing in the belt's movement direction dCalcVectorCross3(beltDirection, contact[i].geom.normal, yAxisGlobal); if (beltSpeed > 0) { dNegateVector3(beltDirection); } if (desiredRotationRadiusSigned != dInfinity) { // non-straight drive dVector3 COR2Contact; // vector pointing from the center of rotation to the contact point dSubtractVectors3(COR2Contact, contact[i].geom.pos, centerOfRotation); // the friction force should be perpendicular to COR2Contact dCalcVectorCross3(contact[i].fdir1, contact[i].geom.normal, COR2Contact); const dReal linearSpeedSignum = (fabs(linearSpeed) > 0.1) ? sgn(linearSpeed) : 1; // contactInVehiclePos[0] > 0 means the contact is in the front part of the track if (sgn(angularSpeed) * sgn(dCalcVectorDot3(yAxisGlobal, contact[i].fdir1)) != sgn(contactInVehiclePos[0]) * linearSpeedSignum) { dNegateVector3(contact[i].fdir1); } } else { // straight drive dCalcVectorCross3(contact[i].fdir1, contact[i].geom.normal, yAxisGlobal); if (dCalcVectorDot3(contact[i].fdir1, beltDirection) < 0) { dNegateVector3(contact[i].fdir1); } } // use friction direction and motion1 to simulate the track movement contact[i].surface.mode = dContactFDir1 | dContactMotion1 | dContactMu2; contact[i].surface.mu = 0.5; contact[i].surface.mu2 = 10; // the dot product <beltDirection,fdir1> is the cosine of the angle they form (because both are unit vectors) contact[i].surface.motion1 = -dCalcVectorDot3(beltDirection, contact[i].fdir1) * fabs(beltSpeed) * 0.07; // friction force visualization dMatrix3 forceRotation; dVector3 vec; dBodyVectorToWorld(vehicleBody, 1, 0, 0, vec); dRFrom2Axes(forceRotation, contact[i].fdir1[0], contact[i].fdir1[1], contact[i].fdir1[2], vec[0], vec[1], vec[2]); posr data; dCopyVector3(data.pos, contact[i].geom.pos); dCopyMatrix4x3(data.R, forceRotation); forces.push_back(data); dJointID c = dJointCreateContact(this->world, this->contactGroup, &contact[i]); dJointAttach(c, b1, b2); if(!isValidCollision(o1, o2, contact[i])) this->badCollision = true; if(config.contact_grouser_terrain.debug) this->contacts.push_back(contact[i].geom); } }
void dxPlanarJoint::getInfo2(dReal worldFPS, dReal worldERP, const Info2Descr* info) { /* ==================================================================== * This joint restricts 2 rotational and 1 translational DOF. * * The 2 rotational constraints are essentially the same as in dxJointPiston, so the implementation here is mostly * just a copy of them. * * The translational 1DOF constraint has not yet been implemented for any joint in ODE, so we'll talk more about it. * It is similar to the constraints in a slider joint, but in this case we only want body2 to move the plane, and * body1 anchor has no use in this case, too. * * We basically want to express the plane in body2 local coordinates (because it should move along with body2), and * check, if body1 origin lies in the plane. * * Let's say n' is the plane normal in body2 coordinates and a' is the anchor point in body2 coordinates * (with n, a, being the corresponding global variables). * Further, let's call the global position of body1 p1, and similarly p2 for body2. * Last, let's call body2 rotation matrix R2 (so that n = R2*n' and a = R2*a' + p2). * * The plane can then be expressed in global coordinates as: * (n^ . x) - (n^ . a) = 0, * where x is in global coordinates and ^ denotes vector/matrix transpose. * * Rewriting the equation using body2 local coordinates and setting x=p1, we get the constraint: * (R2*n')^ * p1 - (R2*n')^ * (R2*a' + p2) = 0 * ... * (n'^ * R2^ * p1) - (n'^ * R2^ * p2) - (n'^ * a') = 0 * (n^ * p1) - (n^ * p2) - (n'^ * a') = 0 * * The part left to "=" will be the "c" on the RHS of the Jacobian equation (because it expresses * the distance of p1 from the plane). * * Next, we need to take time derivative of the constraint to get the J1 and J2 parts. * v1, v2, w1 and w2 denote the linear and angular velocities od body1 and body2. * [a]x denotes the skew-symmetric cross-product matrix of vector a. * * d/dt [(n'^ * R2^ * p1) - (n'^ * R2^ * p2) - (n'^ * a') = 0] (n', a' are constant in time) * n'^ * ((d/dt[R2^] * p1) + (R2^ * v1)) - n'^ * ((d/dt[R2^] * p2) + (R2^ * v2)) = 0 * n'^ * ((([w2]x R2)^ * p1) + (R2^ * v1)) - n'^ * ((([w2]x R2)^ * p2) + (R2^ * v2)) = 0 * ... * n^*v1 - n^*v2 + [n]x (p1 - p2) . w2 = 0 * -n^*v1 + n^*v2 + [n]x (p2 - p1) . w2 = 0 * * Thus we see that * J1l = -n * J2l = n * J1a = 0 * J2a = [n]x (p2 - p1) * c = eps * ( (n^ * p1) - (n^ * a) ) * */ const int row0Index = 0; const int row1Index = info->rowskip; const int row2Index = 2 * row1Index; const dReal eps = worldFPS * worldERP; dVector3 globalAxis1; dBodyVectorToWorld(node[0].body, axis1[0], axis1[1], axis1[2], globalAxis1); dVector3 globalAxis2; dVector3 globalAnchor; if (node[1].body) { dBodyVectorToWorld(node[1].body, axis2[0], axis2[1], axis2[2], globalAxis2); dBodyGetRelPointPos(node[1].body, anchor2[0], anchor2[1], anchor2[2], globalAnchor); } else { dCopyVector3(globalAxis2, axis2); dCopyVector3(globalAnchor, anchor2); } /////////////////////////////////////////////////////// // Angular velocity constraints (2 rows) // // Refer to the piston joint source code for details. /////////////////////////////////////////////////////// // Find the 2 direction vectors of the plane. dVector3 p, q; dPlaneSpace ( globalAxis2, p, q ); // LHS 0 dCopyNegatedVector3 (info->J1a + row0Index, p ); dCopyNegatedVector3 (info->J1a + row1Index, q ); // LHS 1 dCopyVector3 (info->J2a + row0Index, p ); dCopyVector3 (info->J2a + row1Index, q ); // RHS 0 & 1 (absolute errors of the axis direction computed by dot product of the desired and actual axis) dVector3 b; dCalcVectorCross3( b, globalAxis2, globalAxis1 ); info->c[0] = eps * dCalcVectorDot3 ( p, b ); info->c[1] = eps * dCalcVectorDot3 ( q, b ); ////////////////////////////////////////////////////////////////////////////////////////// // Linear velocity constraint (1 row, removes 1 translational DOF along the plane normal) // // Only body1 should be restricted by the plane, which is moved along with body2. ////////////////////////////////////////////////////////////////////////////////////////// dVector3 body1Center; dCopyVector3(body1Center, node[0].body->posr.pos); dVector3 body2Center = {0, 0, 0}; if (node[1].body) { dCopyVector3(body2Center, node[1].body->posr.pos); } // LHS 2 dCopyNegatedVector3(info->J1l + row2Index, globalAxis2); dCopyVector3( info->J2l + row2Index, globalAxis2); dVector3 body2_body1; dSubtractVectors3(body2_body1, body2Center, body1Center); dCalcVectorCross3(info->J2a + row2Index, globalAxis2, body2_body1); // RHS 2 (distance of body1 center from the plane) info->c[2] = eps * (dCalcVectorDot3(globalAxis2, body1Center) - dCalcVectorDot3(globalAxis2, globalAnchor)); }
void dxJointPR::getInfo2( dReal worldFPS, dReal worldERP, int rowskip, dReal *J1, dReal *J2, int pairskip, dReal *pairRhsCfm, dReal *pairLoHi, int *findex ) { dReal k = worldFPS * worldERP; dVector3 q; // plane space of axP and after that axR // pull out pos and R for both bodies. also get the `connection' // vector pos2-pos1. dReal *pos2 = NULL, *R2 = NULL; dReal *pos1 = node[0].body->posr.pos; dReal *R1 = node[0].body->posr.R; dxBody *body1 = node[1].body; if ( body1 ) { pos2 = body1->posr.pos; R2 = body1->posr.R; } dVector3 axP; // Axis of the prismatic joint in global frame dMultiply0_331( axP, R1, axisP1 ); // distance between the body1 and the anchor2 in global frame // Calculated in the same way as the offset dVector3 wanchor2 = {0, 0, 0}, dist; if ( body1 ) { // Calculate anchor2 in world coordinate dMultiply0_331( wanchor2, R2, anchor2 ); dist[0] = wanchor2[0] + pos2[0] - pos1[0]; dist[1] = wanchor2[1] + pos2[1] - pos1[1]; dist[2] = wanchor2[2] + pos2[2] - pos1[2]; } else { if ( (flags & dJOINT_REVERSE) != 0 ) { dSubtractVectors3(dist, pos1, anchor2); // Invert the value } else { dSubtractVectors3(dist, anchor2, pos1); // Invert the value } } // ====================================================================== // Work on the Rotoide part (i.e. row 0, 1 and maybe 4 if rotoide powered // Set the two rotoide rows. The rotoide axis should be the only unconstrained // rotational axis, the angular velocity of the two bodies perpendicular to // the rotoide axis should be equal. Thus the constraint equations are // p*w1 - p*w2 = 0 // q*w1 - q*w2 = 0 // where p and q are unit vectors normal to the rotoide axis, and w1 and w2 // are the angular velocity vectors of the two bodies. dVector3 ax2; dVector3 ax1; dMultiply0_331( ax1, R1, axisR1 ); dCalcVectorCross3( q , ax1, axP ); dCopyVector3(J1 + GI2__JA_MIN, axP); if ( body1 ) { dCopyNegatedVector3(J2 + GI2__JA_MIN, axP); } dCopyVector3(J1 + rowskip + GI2__JA_MIN, q); if ( body1 ) { dCopyNegatedVector3(J2 + rowskip + GI2__JA_MIN, q); } // Compute the right hand side of the constraint equation set. Relative // body velocities along p and q to bring the rotoide back into alignment. // ax1,ax2 are the unit length rotoide axes of body1 and body2 in world frame. // We need to rotate both bodies along the axis u = (ax1 x ax2). // if `theta' is the angle between ax1 and ax2, we need an angular velocity // along u to cover angle erp*theta in one step : // |angular_velocity| = angle/time = erp*theta / stepsize // = (erp*fps) * theta // angular_velocity = |angular_velocity| * (ax1 x ax2) / |ax1 x ax2| // = (erp*fps) * theta * (ax1 x ax2) / sin(theta) // ...as ax1 and ax2 are unit length. if theta is smallish, // theta ~= sin(theta), so // angular_velocity = (erp*fps) * (ax1 x ax2) // ax1 x ax2 is in the plane space of ax1, so we project the angular // velocity to p and q to find the right hand side. if ( body1 ) { dMultiply0_331( ax2, R2, axisR2 ); } else { dCopyVector3(ax2, axisR2); } dVector3 b; dCalcVectorCross3( b, ax1, ax2 ); pairRhsCfm[GI2_RHS] = k * dCalcVectorDot3( b, axP ); pairRhsCfm[pairskip + GI2_RHS] = k * dCalcVectorDot3( b, q ); // ========================== // Work on the Prismatic part (i.e row 2,3 and 4 if only the prismatic is powered // or 5 if rotoide and prismatic powered // two rows. we want: vel2 = vel1 + w1 x c ... but this would // result in three equations, so we project along the planespace vectors // so that sliding along the prismatic axis is disregarded. for symmetry we // also substitute (w1+w2)/2 for w1, as w1 is supposed to equal w2. // p1 + R1 dist' = p2 + R2 anchor2' ## OLD ## p1 + R1 anchor1' = p2 + R2 dist' // 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 // v_p is speed of prismatic joint (i.e. elongation rate) // Since the constraints are perpendicular to v_p we have: // p dot v_p = 0 and q dot v_p = 0 // ax1 dot ( v1 + w1 x dist = v2 + w2 x anchor2 ) // q dot ( v1 + w1 x dist = v2 + w2 x anchor2 ) // == // 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 // since a . (b x c) = - b . (a x c) = - (a x c) . b // and a x b = - b x a // ax1 . v1 - ax1 x dist . w1 - ax1 . v2 - (- ax1 x anchor2 . w2) = 0 // ax1 . v1 + dist x ax1 . w1 - ax1 . v2 - anchor2 x ax1 . w2 = 0 // Coeff for 1er line of: J1l => ax1, J2l => -ax1 // Coeff for 2er line of: J1l => q, J2l => -q // Coeff for 1er line of: J1a => dist x ax1, J2a => - anchor2 x ax1 // Coeff for 2er line of: J1a => dist x q, J2a => - anchor2 x q int currRowSkip = 2 * rowskip; { dCopyVector3( J1 + currRowSkip + GI2__JL_MIN, ax1 ); dCalcVectorCross3( J1 + currRowSkip + GI2__JA_MIN, dist, ax1 ); if ( body1 ) { dCopyNegatedVector3( J2 + currRowSkip + GI2__JL_MIN, ax1 ); // ax2 x anchor2 instead of anchor2 x ax2 since we want the negative value dCalcVectorCross3( J2 + currRowSkip + GI2__JA_MIN, ax2, wanchor2 ); // since ax1 == ax2 } } currRowSkip += rowskip; { dCopyVector3( J1 + currRowSkip + GI2__JL_MIN, q ); dCalcVectorCross3(J1 + currRowSkip + GI2__JA_MIN, dist, q ); if ( body1 ) { dCopyNegatedVector3( J2 + currRowSkip + GI2__JL_MIN, q); // The cross product is in reverse order since we want the negative value dCalcVectorCross3( J2 + currRowSkip + GI2__JA_MIN, q, wanchor2 ); } } // We want to make correction for motion not in the line of the axisP // We calculate the displacement w.r.t. the anchor pt. // // compute the elements 2 and 3 of right hand side. // we want to align the offset point (in body 2's frame) with the center of body 1. // The position should be the same when we are not along the prismatic axis dVector3 err; dMultiply0_331( err, R1, offset ); dSubtractVectors3(err, dist, err); int currPairSkip = 2 * pairskip; { pairRhsCfm[currPairSkip + GI2_RHS] = k * dCalcVectorDot3( ax1, err ); } currPairSkip += pairskip; { pairRhsCfm[currPairSkip + GI2_RHS] = k * dCalcVectorDot3( q, err ); } currRowSkip += rowskip; currPairSkip += pairskip; if ( body1 || (flags & dJOINT_REVERSE) == 0 ) { if (limotP.addLimot ( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, axP, 0 )) { currRowSkip += rowskip; currPairSkip += pairskip; } } else { dVector3 rAxP; dCopyNegatedVector3(rAxP, axP); if (limotP.addLimot ( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, rAxP, 0 )) { currRowSkip += rowskip; currPairSkip += pairskip; } } limotR.addLimot ( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, ax1, 1 ); }
void dxJointPU::getInfo2( dReal worldFPS, dReal worldERP, const Info2Descr *info ) { const int s1 = info->rowskip; const int s2 = 2 * s1; const dReal k = worldFPS * worldERP; // ====================================================================== // The angular constraint // dVector3 ax1, ax2; // Global axes of rotation getAxis(this, ax1, axis1); getAxis2(this,ax2, axis2); dVector3 uniPerp; // Axis perpendicular to axes of rotation dCalcVectorCross3(uniPerp,ax1,ax2); dNormalize3( uniPerp ); dCopyVector3( info->J1a , uniPerp ); if ( node[1].body ) { dCopyNegatedVector3( info->J2a , uniPerp ); } // Corrective velocity attempting to keep uni axes perpendicular dReal val = dCalcVectorDot3( ax1, ax2 ); // Small angle approximation : // theta = asin(val) // theta is approximately val when val is near zero. info->c[0] = -k * val; // ========================================================================== // Handle axes orthogonal to the prismatic dVector3 an1, an2; // Global anchor positions dVector3 axP, sep; // Prismatic axis and separation vector getAnchor(this,an1,anchor1); getAnchor2(this,an2,anchor2); if (flags & dJOINT_REVERSE) { getAxis2(this, axP, axisP1); } else { getAxis(this, axP, axisP1); } dSubtractVectors3(sep,an2,an1); dVector3 p,q; dPlaneSpace(axP,p,q); dCopyVector3(( info->J1l ) + s1, p ); dCopyVector3(( info->J1l ) + s2, q ); // Make the anchors be body local // Aliasing isn't a problem here. dSubtractVectors3(an1,an1,node[0].body->posr.pos); dCalcVectorCross3(( info->J1a ) + s1, an1, p ); dCalcVectorCross3(( info->J1a ) + s2, an1, q ); if (node[1].body) { dCopyNegatedVector3(( info->J2l ) + s1, p ); dCopyNegatedVector3(( info->J2l ) + s2, q ); dSubtractVectors3(an2,an2,node[1].body->posr.pos); dCalcVectorCross3(( info->J2a ) + s1, p, an2 ); dCalcVectorCross3(( info->J2a ) + s2, q, an2 ); } info->c[1] = k * dCalcVectorDot3( p, sep ); info->c[2] = k * dCalcVectorDot3( q, sep ); // ========================================================================== // Handle the limits/motors int row = 3 + limot1.addLimot( this, worldFPS, info, 3, ax1, 1 ); row += limot2.addLimot( this, worldFPS, info, row, ax2, 1 ); if ( node[1].body || !(flags & dJOINT_REVERSE) ) limotP.addTwoPointLimot( this, worldFPS, info, row, axP, an1, an2 ); else { axP[0] = -axP[0]; axP[1] = -axP[1]; axP[2] = -axP[2]; limotP.addTwoPointLimot ( this, worldFPS, info, row, axP, an1, an2 ); } }
void dxJointPU::getInfo2( dxJoint::Info2 *info ) { const int s0 = 0; const int s1 = info->rowskip; const int s2 = 2 * s1; const dReal k = info->fps * info->erp; // pull out pos and R for both bodies. also get the `connection' // vector pos2-pos1. dReal *pos1, *pos2 = 0, *R1, *R2 = 0; pos1 = node[0].body->posr.pos; R1 = node[0].body->posr.R; if ( node[1].body ) { pos2 = node[1].body->posr.pos; R2 = node[1].body->posr.R; } dVector3 axP; // Axis of the prismatic joint in global frame dMultiply0_331( axP, R1, axisP1 ); // distance between the body1 and the anchor2 in global frame // Calculated in the same way as the offset dVector3 dist; dVector3 wanchor2 = {0,0,0}; if ( node[1].body ) { dMultiply0_331( wanchor2, R2, anchor2 ); dist[0] = wanchor2[0] + pos2[0] - pos1[0]; dist[1] = wanchor2[1] + pos2[1] - pos1[1]; dist[2] = wanchor2[2] + pos2[2] - pos1[2]; } else { if (flags & dJOINT_REVERSE ) { // Invert the sign of dist dist[0] = pos1[0] - anchor2[0]; dist[1] = pos1[1] - anchor2[1]; dist[2] = pos1[2] - anchor2[2]; } else { dist[0] = anchor2[0] - pos1[0]; dist[1] = anchor2[1] - pos1[1]; dist[2] = anchor2[2] - pos1[2]; } } dVector3 q; // Temporary axis vector // Will be used at 2 places with 2 different meaning // ====================================================================== // Work on the angular part (i.e. row 0) // // The axis perpendicular to both axis1 and axis2 should be the only unconstrained // rotational axis, the angular velocity of the two bodies perpendicular to // the rotoide axes should be equal. Thus the constraint equations are // p*w1 - p*w2 = 0 // where p is a unit vector perpendicular to both axis1 and axis2 // and w1 and w2 are the angular velocity vectors of the two bodies. dVector3 ax1, ax2; getAxes( ax1, ax2 ); dReal val = dCalcVectorDot3( ax1, ax2 ); q[0] = ax2[0] - val * ax1[0]; q[1] = ax2[1] - val * ax1[1]; q[2] = ax2[2] - val * ax1[2]; dVector3 p; dCalcVectorCross3( p, ax1, q ); dNormalize3( p ); // info->J1a[s0+i] = p[i]; dCopyVector3(( info->J1a ) + s0, p ); if ( node[1].body ) { // info->J2a[s0+i] = -p[i]; dCopyNegatedVector3(( info->J2a ) + s0, p ); } // compute the right hand side of the constraint equation. Set relative // body velocities along p to bring the axes back to perpendicular. // If ax1, ax2 are unit length joint axes as computed from body1 and // body2, we need to rotate both bodies along the axis p. If theta // is the angle between ax1 and ax2, we need an angular velocity // along p to cover the angle erp * (theta - Pi/2) in one step: // // |angular_velocity| = angle/time = erp*(theta - Pi/2) / stepsize // = (erp*fps) * (theta - Pi/2) // // if theta is close to Pi/2, // theta - Pi/2 ~= cos(theta), so // |angular_velocity| ~= (erp*fps) * (ax1 dot ax2) info->c[0] = k * - val; // ========================================================================== // Work on the linear part (i.e rows 1 and 2) // // We want: vel2 = vel1 + w1 x c ... but this would // result in three equations, so we project along the planespace vectors // so that sliding along the axisP is disregarded. // // p1 + R1 dist' = p2 + R2 anchor2' // v1 + w1 x R1 dist' + v_p = v2 + w2 x R2 anchor2' // v_p is speed of prismatic joint (i.e. elongation rate) // Since the constraints are perpendicular to v_p we have: // e1 dot v_p = 0 and e2 dot v_p = 0 // e1 dot ( v1 + w1 x dist = v2 + w2 x anchor2 ) // e2 dot ( v1 + w1 x dist = v2 + w2 x anchor2 ) // == // e1 . v1 + e1 . w1 x dist = e1 . v2 + e1 . w2 x anchor2 // since a . (b x c) = - b . (a x c) = - (a x c) . b // and a x b = - b x a // e1 . v1 - e1 x dist . w1 - e1 . v2 - (- e1 x anchor2 . w2) = 0 // e1 . v1 + dist x e1 . w1 - e1 . v2 - anchor2 x e1 . w2 = 0 // Coeff for 1er line of: J1l => e1, J2l => -e1 // Coeff for 2er line of: J1l => e2, J2l => -ax2 // Coeff for 1er line of: J1a => dist x e1, J2a => - anchor2 x e1 // Coeff for 2er line of: J1a => dist x e2, J2a => - anchor2 x e2 // e1 and e2 are perpendicular to axP // so e1 = ax1 and e2 = ax1 x axP // N.B. ax2 is not always perpendicular to axP since it is attached to body 2 dCalcVectorCross3( q , ax1, axP ); dMultiply0_331( axP, R1, axisP1 ); dCalcVectorCross3(( info->J1a ) + s1, dist, ax1 ); dCalcVectorCross3(( info->J1a ) + s2, dist, q ); // info->J1l[s1+i] = ax[i]; dCopyVector3(( info->J1l ) + s1, ax1 ); // info->J1l[s2+i] = q[i]; dCopyVector3(( info->J1l ) + s2, q ); if ( node[1].body ) { // Calculate anchor2 in world coordinate // q x anchor2 instead of anchor2 x q since we want the negative value dCalcVectorCross3(( info->J2a ) + s1, ax1, wanchor2 ); // The cross product is in reverse order since we want the negative value dCalcVectorCross3(( info->J2a ) + s2, q, wanchor2 ); // info->J2l[s1+i] = -ax1[i]; dCopyNegatedVector3(( info->J2l ) + s1, ax1 ); // info->J2l[s2+i] = -ax1[i]; dCopyNegatedVector3(( info->J2l ) + s2, q ); } // We want to make correction for motion not in the line of the axisP // We calculate the displacement w.r.t. the anchor pt. // // compute the elements 1 and 2 of right hand side. // We want to align the offset point (in body 2's frame) with the center of body 1. // The position should be the same when we are not along the prismatic axis dVector3 err; dMultiply0_331( err, R1, anchor1 ); // err[i] = dist[i] - err[i]; dSubtractVectors3( err, dist, err ); info->c[1] = k * dCalcVectorDot3( ax1, err ); info->c[2] = k * dCalcVectorDot3( q, err ); int row = 3 + limot1.addLimot( this, info, 3, ax1, 1 ); row += limot2.addLimot( this, info, row, ax2, 1 ); if ( node[1].body || !(flags & dJOINT_REVERSE) ) limotP.addLimot( this, info, row, axP, 0 ); else { axP[0] = -axP[0]; axP[1] = -axP[1]; axP[2] = -axP[2]; limotP.addLimot ( this, info, row, axP, 0 ); } }
void dxJointUniversal::getInfo2( dReal worldFPS, dReal worldERP, int rowskip, dReal *J1, dReal *J2, int pairskip, dReal *pairRhsCfm, dReal *pairLoHi, int *findex ) { // set the three ball-and-socket rows setBall( this, worldFPS, worldERP, rowskip, J1, J2, pairskip, pairRhsCfm, anchor1, anchor2 ); // set the universal joint row. the angular velocity about an axis // perpendicular to both joint axes should be equal. thus the constraint // equation is // p*w1 - p*w2 = 0 // where p is a vector normal to both joint axes, and w1 and w2 // are the angular velocity vectors of the two bodies. // length 1 joint axis in global coordinates, from each body dVector3 ax1, ax2; // length 1 vector perpendicular to ax1 and ax2. Neither body can rotate // about this. dVector3 p; // Since axis1 and axis2 may not be perpendicular // we find a axis2_tmp which is really perpendicular to axis1 // and in the plane of axis1 and axis2 getAxes( ax1, ax2 ); dReal k = dCalcVectorDot3( ax1, ax2 ); dVector3 ax2_temp; dAddVectorScaledVector3(ax2_temp, ax2, ax1, -k); dCalcVectorCross3( p, ax1, ax2_temp ); dNormalize3( p ); int currRowSkip = 3 * rowskip; { dCopyVector3( J1 + currRowSkip + GI2__JA_MIN, p); if ( node[1].body ) { dCopyNegatedVector3( J2 + currRowSkip + GI2__JA_MIN, p); } } // compute the right hand side of the constraint equation. set relative // body velocities along p to bring the axes back to perpendicular. // If ax1, ax2 are unit length joint axes as computed from body1 and // body2, we need to rotate both bodies along the axis p. If theta // is the angle between ax1 and ax2, we need an angular velocity // along p to cover the angle erp * (theta - Pi/2) in one step: // // |angular_velocity| = angle/time = erp*(theta - Pi/2) / stepsize // = (erp*fps) * (theta - Pi/2) // // if theta is close to Pi/2, // theta - Pi/2 ~= cos(theta), so // |angular_velocity| ~= (erp*fps) * (ax1 dot ax2) int currPairSkip = 3 * pairskip; { pairRhsCfm[currPairSkip + GI2_RHS] = worldFPS * worldERP * (-k); } currRowSkip += rowskip; currPairSkip += pairskip; // if the first angle is powered, or has joint limits, add in the stuff if (limot1.addLimot( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, ax1, 1 )) { currRowSkip += rowskip; currPairSkip += pairskip; } // if the second angle is powered, or has joint limits, add in more stuff limot2.addLimot( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, ax2, 1 ); }
void dxJointPU::getInfo2( dReal worldFPS, dReal worldERP, int rowskip, dReal *J1, dReal *J2, int pairskip, dReal *pairRhsCfm, dReal *pairLoHi, int *findex ) { const dReal k = worldFPS * worldERP; // ====================================================================== // The angular constraint // dVector3 ax1, ax2; // Global axes of rotation getAxis(this, ax1, axis1); getAxis2(this,ax2, axis2); dVector3 uniPerp; // Axis perpendicular to axes of rotation dCalcVectorCross3(uniPerp,ax1,ax2); dNormalize3( uniPerp ); dCopyVector3( J1 + GI2__JA_MIN, uniPerp ); dxBody *body1 = node[1].body; if ( body1 ) { dCopyNegatedVector3( J2 + GI2__JA_MIN , uniPerp ); } // Corrective velocity attempting to keep uni axes perpendicular dReal val = dCalcVectorDot3( ax1, ax2 ); // Small angle approximation : // theta = asin(val) // theta is approximately val when val is near zero. pairRhsCfm[GI2_RHS] = -k * val; // ========================================================================== // Handle axes orthogonal to the prismatic dVector3 an1, an2; // Global anchor positions dVector3 axP, sep; // Prismatic axis and separation vector getAnchor(this, an1, anchor1); getAnchor2(this, an2, anchor2); if (flags & dJOINT_REVERSE) { getAxis2(this, axP, axisP1); } else { getAxis(this, axP, axisP1); } dSubtractVectors3(sep, an2, an1); dVector3 p, q; dPlaneSpace(axP, p, q); dCopyVector3( J1 + rowskip + GI2__JL_MIN, p ); dCopyVector3( J1 + 2 * rowskip + GI2__JL_MIN, q ); // Make the anchors be body local // Aliasing isn't a problem here. dSubtractVectors3(an1, an1, node[0].body->posr.pos); dCalcVectorCross3( J1 + rowskip + GI2__JA_MIN, an1, p ); dCalcVectorCross3( J1 + 2 * rowskip + GI2__JA_MIN, an1, q ); if (body1) { dCopyNegatedVector3( J2 + rowskip + GI2__JL_MIN, p ); dCopyNegatedVector3( J2 + 2 * rowskip + GI2__JL_MIN, q ); dSubtractVectors3(an2, an2, body1->posr.pos); dCalcVectorCross3( J2 + rowskip + GI2__JA_MIN, p, an2 ); dCalcVectorCross3( J2 + 2 * rowskip + GI2__JA_MIN, q, an2 ); } pairRhsCfm[pairskip + GI2_RHS] = k * dCalcVectorDot3( p, sep ); pairRhsCfm[2 * pairskip + GI2_RHS] = k * dCalcVectorDot3( q, sep ); // ========================================================================== // Handle the limits/motors int currRowSkip = 3 * rowskip, currPairSkip = 3 * pairskip; if (limot1.addLimot( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, ax1, 1 )) { currRowSkip += rowskip; currPairSkip += pairskip; } if (limot2.addLimot( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, ax2, 1 )) { currRowSkip += rowskip; currPairSkip += pairskip; } if ( body1 || (flags & dJOINT_REVERSE) == 0 ) { limotP.addTwoPointLimot( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, axP, an1, an2 ); } else { dNegateVector3(axP); limotP.addTwoPointLimot ( this, worldFPS, J1 + currRowSkip, J2 + currRowSkip, pairRhsCfm + currPairSkip, pairLoHi + currPairSkip, axP, an1, an2 ); } }
void start() { world = dWorldCreate(); dWorldSetGravity (world,0,0,-9.8); contact_group = dJointGroupCreate(0); space = dSimpleSpaceCreate (0); // first, the ground plane // it has to coincide with the plane we have in drawstuff ground = dCreatePlane(space, 0, 0, 1, 0); // now a ball dMass m; dMassSetSphere(&m, 0.1, ball_radius); ball1_geom = dCreateSphere(space, ball_radius); ball1_body = dBodyCreate(world); dGeomSetBody(ball1_geom, ball1_body); dBodySetMass(ball1_body, &m); ball2_geom = dCreateSphere(space, ball_radius); ball2_body = dBodyCreate(world); dGeomSetBody(ball2_geom, ball2_body); dBodySetMass(ball2_body, &m); // tracks made out of boxes dGeomID trk; dMatrix3 r1, r2, r3; dVector3 ro = {0, -(0.5*track_gauge + 0.5*track_width), track_elevation}; dMatrix3 s1, s2, s3; dVector3 so = {0, 0.5*track_gauge + 0.5*track_width, track_elevation}; dRFromAxisAndAngle(r1, 1, 0, 0, track_angle); dRFromAxisAndAngle(r2, 0, 1, 0, -track_incl); dMultiply0_333(r3, r2, r1); dRFromAxisAndAngle(s1, 1, 0, 0, -track_angle); dRFromAxisAndAngle(s2, 0, 1, 0, -track_incl); dMultiply0_333(s3, s2, s1); trk = dCreateBox(space, track_len, track_width, track_height); dGeomSetPosition(trk, ro[0], ro[1] + balls_sep, ro[2]); dGeomSetRotation(trk, r3); trk = dCreateBox(space, track_len, track_width, track_height); dGeomSetPosition(trk, so[0], so[1] + balls_sep, so[2]); dGeomSetRotation(trk, s3); // tracks made out of trimesh for (unsigned i=0; i<n_box_verts; ++i) { dVector3 p; dMultiply0_331(p, s3, box_verts[i]); dAddVectors3(p, p, so); dCopyVector3(track_verts[i], p); } // trimesh tracks 2, transform all vertices by s3 for (unsigned i=0; i<n_box_verts; ++i) { dVector3 p; dMultiply0_331(p, r3, box_verts[i]); dAddVectors3(p, p, ro); dCopyVector3(track_verts[n_box_verts + i], p); } // copy face indices for (unsigned i=0; i<n_box_faces; ++i) for (unsigned j=0; j<3; ++j) // each face index track_faces[3*i+j] = box_faces[3*i+j]; for (unsigned i=0; i<n_box_faces; ++i) for (unsigned j=0; j<3; ++j) // each face index track_faces[3*(i + n_box_faces)+j] = box_faces[3*i+j] + n_box_verts; mesh_data = dGeomTriMeshDataCreate(); dGeomTriMeshDataBuildSimple(mesh_data, track_verts[0], n_track_verts, track_faces, 3*n_track_faces); mesh_geom = dCreateTriMesh(space, mesh_data, 0, 0, 0); resetSim(); // initial camera position static float xyz[3] = {-5.9414,-0.4804,2.9800}; static float hpr[3] = {32.5000,-10.0000,0.0000}; dsSetViewpoint (xyz,hpr); dsSetSphereQuality(3); }
int dCollideSTL(dxGeom* g1, dxGeom* SphereGeom, int Flags, dContactGeom* Contacts, int Stride){ dIASSERT (Stride >= (int)sizeof(dContactGeom)); dIASSERT (g1->type == dTriMeshClass); dIASSERT (SphereGeom->type == dSphereClass); dIASSERT ((Flags & NUMC_MASK) >= 1); dxTriMesh* TriMesh = (dxTriMesh*)g1; // Init const dVector3& TLPosition = *(const dVector3*)dGeomGetPosition(TriMesh); const dMatrix3& TLRotation = *(const dMatrix3*)dGeomGetRotation(TriMesh); const unsigned uiTLSKind = TriMesh->getParentSpaceTLSKind(); dIASSERT(uiTLSKind == SphereGeom->getParentSpaceTLSKind()); // The colliding spaces must use matching cleanup method TrimeshCollidersCache *pccColliderCache = GetTrimeshCollidersCache(uiTLSKind); SphereCollider& Collider = pccColliderCache->_SphereCollider; const dVector3& Position = *(const dVector3*)dGeomGetPosition(SphereGeom); dReal Radius = dGeomSphereGetRadius(SphereGeom); // Sphere Sphere Sphere; dCopyVector3(Sphere.mCenter, Position); Sphere.mRadius = Radius; Matrix4x4 amatrix; // TC results if (TriMesh->doSphereTC) { dxTriMesh::SphereTC* sphereTC = 0; for (int i = 0; i < TriMesh->SphereTCCache.size(); i++){ if (TriMesh->SphereTCCache[i].Geom == SphereGeom){ sphereTC = &TriMesh->SphereTCCache[i]; break; } } if (!sphereTC){ TriMesh->SphereTCCache.push(dxTriMesh::SphereTC()); sphereTC = &TriMesh->SphereTCCache[TriMesh->SphereTCCache.size() - 1]; sphereTC->Geom = SphereGeom; } // Intersect Collider.SetTemporalCoherence(true); Collider.Collide(*sphereTC, Sphere, TriMesh->Data->BVTree, null, &MakeMatrix(TLPosition, TLRotation, amatrix)); } else { Collider.SetTemporalCoherence(false); Collider.Collide(pccColliderCache->defaultSphereCache, Sphere, TriMesh->Data->BVTree, null, &MakeMatrix(TLPosition, TLRotation, amatrix)); } if (! Collider.GetContactStatus()) { // no collision occurred return 0; } // get results int TriCount = Collider.GetNbTouchedPrimitives(); const int* Triangles = (const int*)Collider.GetTouchedPrimitives(); if (TriCount != 0){ if (TriMesh->ArrayCallback != null){ TriMesh->ArrayCallback(TriMesh, SphereGeom, Triangles, TriCount); } int OutTriCount = 0; for (int i = 0; i < TriCount; i++){ if (OutTriCount == (Flags & NUMC_MASK)){ break; } const int TriIndex = Triangles[i]; dVector3 dv[3]; if (!Callback(TriMesh, SphereGeom, TriIndex)) continue; FetchTriangle(TriMesh, TriIndex, TLPosition, TLRotation, dv); dVector3& v0 = dv[0]; dVector3& v1 = dv[1]; dVector3& v2 = dv[2]; dVector3 vu; dSubtractVectors3r4(vu, v1, v0); vu[3] = REAL(0.0); dVector3 vv; dSubtractVectors3r4(vv, v2, v0); vv[3] = REAL(0.0); // Get plane coefficients dVector4 Plane; dCalcVectorCross3(Plane, vu, vv); // Even though all triangles might be initially valid, // a triangle may degenerate into a segment after applying // space transformation. if (!dSafeNormalize3(Plane)) { continue; } /* If the center of the sphere is within the positive halfspace of the * triangle's plane, allow a contact to be generated. * If the center of the sphere made it into the positive halfspace of a * back-facing triangle, then the physics update and/or velocity needs * to be adjusted (penetration has occured anyway). */ dReal side = dCalcVectorDot3(Plane, Position) - dCalcVectorDot3(Plane, v0); if(side < REAL(0.0)) { continue; } dReal Depth; dReal u, v; if (!GetContactData(Position, Radius, v0, vu, vv, Depth, u, v)){ continue; // Sphere doesn't hit triangle } if (Depth < REAL(0.0)){ continue; // Negative depth does not produce a contact } dVector3 ContactPos; dReal w = REAL(1.0) - u - v; dAddScaledVectors3r4(ContactPos, v1, v2, u, v); dAddScaledVector3r4(ContactPos, v0, w); // Depth returned from GetContactData is depth along // contact point - sphere center direction // we'll project it to contact normal dVector3 dir; dSubtractVectors3r4(dir, Position, ContactPos); dReal dirProj = dCalcVectorDot3(dir, Plane) / dCalcVectorLength3(dir); // Since Depth already had a requirement to be non-negative, // negative direction projections should not be allowed as well, // as otherwise the multiplication will result in negative contact depth. if (dirProj < REAL(0.0)) continue; // Zero contact depth could be ignored dContactGeom* Contact = SAFECONTACT(Flags, Contacts, OutTriCount, Stride); dCopyVector3r4(Contact->pos, ContactPos); // Using normal as plane (reversed) dCopyNegatedVector3r4(Contact->normal, Plane); Contact->depth = Depth * dirProj; //Contact->depth = Radius - side; // (mg) penetration depth is distance along normal not shortest distance // We need to set these unconditionally, as the merging may fail! - Bram Contact->g1 = TriMesh; Contact->g2 = SphereGeom; Contact->side2 = -1; Contact->side1 = TriIndex; OutTriCount++; } if (OutTriCount > 0) { if (TriMesh->SphereContactsMergeOption == MERGE_CONTACTS_FULLY) { dContactGeom* Contact = SAFECONTACT(Flags, Contacts, 0, Stride); Contact->g1 = TriMesh; Contact->g2 = SphereGeom; Contact->side2 = -1; if (OutTriCount > 1 && !(Flags & CONTACTS_UNIMPORTANT)) { dVector3 pos; dCopyVector3r4(pos, Contact->pos); dVector3 normal; dCopyScaledVector3r4(normal, Contact->normal, Contact->depth); int TriIndex = Contact->side1; for (int i = 1; i < OutTriCount; i++) { dContactGeom* TempContact = SAFECONTACT(Flags, Contacts, i, Stride); dAddVector3r4(pos, TempContact->pos); dAddScaledVector3r4(normal, TempContact->normal, TempContact->depth); TriIndex = (TriMesh->TriMergeCallback) ? TriMesh->TriMergeCallback(TriMesh, TriIndex, TempContact->side1) : -1; } Contact->side1 = TriIndex; dReal invOutTriCount = dRecip(OutTriCount); dCopyScaledVector3r4(Contact->pos, pos, invOutTriCount); if ( !dSafeNormalize3(normal) ) return OutTriCount; // Cannot merge in this pathological case // Using a merged normal, means that for each intersection, this new normal will be less effective in solving the intersection. // That is why we need to correct this by increasing the depth for each intersection. // The maximum of the adjusted depths is our newly merged depth value - Bram. dReal mergedDepth = REAL(0.0); dReal minEffectiveness = REAL(0.5); for ( int i = 0; i < OutTriCount; ++i ) { dContactGeom* TempContact = SAFECONTACT(Flags, Contacts, i, Stride); dReal effectiveness = dCalcVectorDot3(normal, TempContact->normal); if ( effectiveness < dEpsilon ) return OutTriCount; // Cannot merge this pathological case // Cap our adjustment for the new normal to a factor 2, meaning a 60 deg change in normal. effectiveness = ( effectiveness < minEffectiveness ) ? minEffectiveness : effectiveness; dReal adjusted = TempContact->depth / effectiveness; mergedDepth = ( mergedDepth < adjusted ) ? adjusted : mergedDepth; } Contact->depth = mergedDepth; dCopyVector3r4(Contact->normal, normal); } return 1; } else if (TriMesh->SphereContactsMergeOption == MERGE_CONTACT_NORMALS) { if (OutTriCount != 1 && !(Flags & CONTACTS_UNIMPORTANT)) { dVector3 Normal; dContactGeom* FirstContact = SAFECONTACT(Flags, Contacts, 0, Stride); dCopyScaledVector3r4(Normal, FirstContact->normal, FirstContact->depth); for (int i = 1; i < OutTriCount; i++) { dContactGeom* Contact = SAFECONTACT(Flags, Contacts, i, Stride); dAddScaledVector3r4(Normal, Contact->normal, Contact->depth); } dNormalize3(Normal); for (int i = 0; i < OutTriCount; i++) { dContactGeom* Contact = SAFECONTACT(Flags, Contacts, i, Stride); dCopyVector3r4(Contact->normal, Normal); } } return OutTriCount; } else { dIASSERT(TriMesh->SphereContactsMergeOption == DONT_MERGE_CONTACTS); return OutTriCount; } } else return 0; } else return 0; }
void dxJointTransmission::getInfo2( dReal worldFPS, dReal /*worldERP*/, const Info2Descr* info ) { dVector3 a[2], n[2], l[2], r[2], c[2], s, t, O, d, z, u, v; dReal theta, delta, nn, na_0, na_1, cosphi, sinphi, m; const dReal *p[2], *omega[2]; int i; // Transform all needed quantities to the global frame. for (i = 0 ; i < 2 ; i += 1) { dBodyGetRelPointPos(node[i].body, anchors[i][0], anchors[i][1], anchors[i][2], a[i]); dBodyVectorToWorld(node[i].body, axes[i][0], axes[i][1], axes[i][2], n[i]); p[i] = dBodyGetPosition(node[i].body); omega[i] = dBodyGetAngularVel(node[i].body); } if (update) { // Make sure both gear reference frames end up with the same // handedness. if (dCalcVectorDot3(n[0], n[1]) < 0) { dNegateVector3(axes[0]); dNegateVector3(n[0]); } } // Calculate the mesh geometry based on the current mode. switch (mode) { case dTransmissionParallelAxes: // Simply calculate the contact point as the point on the // baseline that will yield the correct ratio. dIASSERT (ratio > 0); dSubtractVectors3(d, a[1], a[0]); dAddScaledVectors3(c[0], a[0], d, 1, ratio / (1 + ratio)); dCopyVector3(c[1], c[0]); dNormalize3(d); for (i = 0 ; i < 2 ; i += 1) { dCalcVectorCross3(l[i], d, n[i]); } break; case dTransmissionIntersectingAxes: // Calculate the line of intersection between the planes of the // gears. dCalcVectorCross3(l[0], n[0], n[1]); dCopyVector3(l[1], l[0]); nn = dCalcVectorDot3(n[0], n[1]); dIASSERT(fabs(nn) != 1); na_0 = dCalcVectorDot3(n[0], a[0]); na_1 = dCalcVectorDot3(n[1], a[1]); dAddScaledVectors3(O, n[0], n[1], (na_0 - na_1 * nn) / (1 - nn * nn), (na_1 - na_0 * nn) / (1 - nn * nn)); // Find the contact point as: // // c = ((r_a - O) . l) l + O // // where r_a the anchor point of either gear and l, O the tangent // line direction and origin. for (i = 0 ; i < 2 ; i += 1) { dSubtractVectors3(d, a[i], O); m = dCalcVectorDot3(d, l[i]); dAddScaledVectors3(c[i], O, l[i], 1, m); } break; case dTransmissionChainDrive: dSubtractVectors3(d, a[0], a[1]); m = dCalcVectorLength3(d); dIASSERT(m > 0); // Caclulate the angle of the contact point relative to the // baseline. cosphi = clamp((radii[1] - radii[0]) / m, REAL(-1.0), REAL(1.0)); // Force into range to fix possible computation errors sinphi = dSqrt (REAL(1.0) - cosphi * cosphi); dNormalize3(d); for (i = 0 ; i < 2 ; i += 1) { // Calculate the contact radius in the local reference // frame of the chain. This has axis x pointing along the // baseline, axis y pointing along the sprocket axis and // the remaining axis normal to both. u[0] = radii[i] * cosphi; u[1] = 0; u[2] = radii[i] * sinphi; // Transform the contact radius into the global frame. dCalcVectorCross3(z, d, n[i]); v[0] = dCalcVectorDot3(d, u); v[1] = dCalcVectorDot3(n[i], u); v[2] = dCalcVectorDot3(z, u); // Finally calculate contact points and l. dAddVectors3(c[i], a[i], v); dCalcVectorCross3(l[i], v, n[i]); dNormalize3(l[i]); // printf ("%d: %f, %f, %f\n", // i, l[i][0], l[i][1], l[i][2]); } break; } if (update) { // We need to calculate an initial reference frame for each // wheel which we can measure the current phase against. This // frame will have the initial contact radius as the x axis, // the wheel axis as the z axis and their cross product as the // y axis. for (i = 0 ; i < 2 ; i += 1) { dSubtractVectors3 (r[i], c[i], a[i]); radii[i] = dCalcVectorLength3(r[i]); dIASSERT(radii[i] > 0); dBodyVectorFromWorld(node[i].body, r[i][0], r[i][1], r[i][2], reference[i]); dNormalize3(reference[i]); dCopyVector3(reference[i] + 8, axes[i]); dCalcVectorCross3(reference[i] + 4, reference[i] + 8, reference[i]); // printf ("%f\n", dDOT(r[i], n[i])); // printf ("(%f, %f, %f,\n %f, %f, %f,\n %f, %f, %f)\n", // reference[i][0],reference[i][1],reference[i][2], // reference[i][4],reference[i][5],reference[i][6], // reference[i][8],reference[i][9],reference[i][10]); radii[i] = radii[i]; phase[i] = 0; } ratio = radii[0] / radii[1]; update = 0; } for (i = 0 ; i < 2 ; i += 1) { dReal phase_hat; dSubtractVectors3 (r[i], c[i], a[i]); // Transform the (global) contact radius into the gear's // reference frame. dBodyVectorFromWorld (node[i].body, r[i][0], r[i][1], r[i][2], s); dMultiply0_331(t, reference[i], s); // Now simply calculate its angle on the plane relative to the // x-axis which is the initial contact radius. This will be // an angle between -pi and pi that is coterminal with the // actual phase of the wheel. To find the real phase we // estimate it by adding omega * dt to the old phase and then // find the closest angle to that, that is coterminal to // theta. theta = atan2(t[1], t[0]); phase_hat = phase[i] + dCalcVectorDot3(omega[i], n[i]) / worldFPS; if (phase_hat > M_PI_2) { if (theta < 0) { theta += (dReal)(2 * M_PI); } theta += (dReal)(floor(phase_hat / (2 * M_PI)) * (2 * M_PI)); } else if (phase_hat < -M_PI_2) { if (theta > 0) { theta -= (dReal)(2 * M_PI); } theta += (dReal)(ceil(phase_hat / (2 * M_PI)) * (2 * M_PI)); } if (phase_hat - theta > M_PI) { phase[i] = theta + (dReal)(2 * M_PI); } else if (phase_hat - theta < -M_PI) { phase[i] = theta - (dReal)(2 * M_PI); } else { phase[i] = theta; } dIASSERT(fabs(phase_hat - phase[i]) < M_PI); } // Calculate the phase error. Depending on the mode the condition // is that the distances traveled by each contact point must be // either equal (chain and sprockets) or opposite (gears). if (mode == dTransmissionChainDrive) { delta = (dCalcVectorLength3(r[0]) * phase[0] - dCalcVectorLength3(r[1]) * phase[1]); } else { delta = (dCalcVectorLength3(r[0]) * phase[0] + dCalcVectorLength3(r[1]) * phase[1]); } // When in chain mode a torque reversal, signified by the change // in sign of the wheel phase difference, has the added effect of // switching the active chain branch. We must therefore reflect // the contact points and tangents across the baseline. if (mode == dTransmissionChainDrive && delta < 0) { dVector3 d; dSubtractVectors3(d, a[0], a[1]); for (i = 0 ; i < 2 ; i += 1) { dVector3 nn; dReal a; dCalcVectorCross3(nn, n[i], d); a = dCalcVectorDot3(nn, nn); dIASSERT(a > 0); dAddScaledVectors3(c[i], c[i], nn, 1, -2 * dCalcVectorDot3(c[i], nn) / a); dAddScaledVectors3(l[i], l[i], nn, -1, 2 * dCalcVectorDot3(l[i], nn) / a); } } // Do not add the constraint if there's backlash and we're in the // backlash gap. if (backlash == 0 || fabs(delta) > backlash) { // The constraint is satisfied iff the absolute velocity of the // contact point projected onto the tangent of the wheels is equal // for both gears. This velocity can be calculated as: // // u = v + omega x r_c // // The constraint therefore becomes: // (v_1 + omega_1 x r_c1) . l = (v_2 + omega_2 x r_c2) . l <=> // (v_1 . l + (r_c1 x l) . omega_1 = v_2 . l + (r_c2 x l) . omega_2 for (i = 0 ; i < 2 ; i += 1) { dSubtractVectors3 (r[i], c[i], p[i]); } dCalcVectorCross3(info->J1a, r[0], l[0]); dCalcVectorCross3(info->J2a, l[1], r[1]); dCopyVector3(info->J1l, l[0]); dCopyNegatedVector3(info->J2l, l[1]); if (delta > 0) { if (backlash > 0) { info->lo[0] = -dInfinity; info->hi[0] = 0; } info->c[0] = -worldFPS * erp * (delta - backlash); } else { if (backlash > 0) { info->lo[0] = 0; info->hi[0] = dInfinity; } info->c[0] = -worldFPS * erp * (delta + backlash); } } info->cfm[0] = cfm; // printf ("%f, %f, %f, %f, %f\n", delta, phase[0], phase[1], -phase[1] / phase[0], ratio); // Cache the contact point (in world coordinates) to avoid // recalculation if requested by the user. dCopyVector3(contacts[0], c[0]); dCopyVector3(contacts[1], c[1]); }