ode/src/util.cpp
changeset 0 2f259fa3e83a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ode/src/util.cpp	Tue Feb 02 01:00:49 2010 +0200
@@ -0,0 +1,375 @@
+/*************************************************************************
+ *                                                                       *
+ * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith.       *
+ * All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of EITHER:                                  *
+ *   (1) The GNU Lesser General Public License as published by the Free  *
+ *       Software Foundation; either version 2.1 of the License, or (at  *
+ *       your option) any later version. The text of the GNU Lesser      *
+ *       General Public License is included with this library in the     *
+ *       file LICENSE.TXT.                                               *
+ *   (2) The BSD-style license that is included with this library in     *
+ *       the file LICENSE-BSD.TXT.                                       *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files    *
+ * LICENSE.TXT and LICENSE-BSD.TXT for more details.                     *
+ *                                                                       *
+ *************************************************************************/
+
+#include <ode/ode.h>
+#include "object.h"
+#include "joint.h"
+#include "util.h"
+#include <ode/lookup_tables.h>
+
+//#define ALLOCA dALLOCA16
+
+//****************************************************************************
+// Auto disabling
+
+void dInternalHandleAutoDisabling (dxWorld *world, dReal stepsize)
+{
+	dxBody *bb;
+	for ( bb=world->firstbody; bb; bb=(dxBody*)bb->next )
+	{
+		// don't freeze objects mid-air (patch 1586738)
+		if ( bb->firstjoint == NULL ) continue;
+
+		// nothing to do unless this body is currently enabled and has
+		// the auto-disable flag set
+		if ( (bb->flags & (dxBodyAutoDisable|dxBodyDisabled)) != dxBodyAutoDisable ) continue;
+
+		// if sampling / threshold testing is disabled, we can never sleep.
+		if ( bb->adis.average_samples == 0 ) continue;
+
+		//
+		// see if the body is idle
+		//
+		
+#ifndef dNODEBUG
+		// sanity check
+		if ( bb->average_counter >= bb->adis.average_samples )
+		{
+
+			// something is going wrong, reset the average-calculations
+			bb->average_ready = 0; // not ready for average calculation
+			bb->average_counter = 0; // reset the buffer index
+		}
+#endif // dNODEBUG
+
+		// sample the linear and angular velocity
+		bb->average_lvel_buffer[bb->average_counter][0] = bb->lvel[0];
+		bb->average_lvel_buffer[bb->average_counter][1] = bb->lvel[1];
+		bb->average_lvel_buffer[bb->average_counter][2] = bb->lvel[2];
+		bb->average_avel_buffer[bb->average_counter][0] = bb->avel[0];
+		bb->average_avel_buffer[bb->average_counter][1] = bb->avel[1];
+		bb->average_avel_buffer[bb->average_counter][2] = bb->avel[2];
+		bb->average_counter++;
+		
+		// buffer ready test
+		if ( bb->average_counter >= bb->adis.average_samples )
+		{
+			bb->average_counter = 0; // fill the buffer from the beginning
+			bb->average_ready = 1; // this body is ready now for average calculation
+		}
+
+		int idle = 0; // Assume it's in motion unless we have samples to disprove it.
+
+		// enough samples?
+		if ( bb->average_ready )
+		{
+			idle = 1; // Initial assumption: IDLE
+
+			// the sample buffers are filled and ready for calculation
+			dVector3 average_lvel, average_avel;
+
+			// Store first velocity samples
+			average_lvel[0] = bb->average_lvel_buffer[0][0];
+			average_avel[0] = bb->average_avel_buffer[0][0];
+			average_lvel[1] = bb->average_lvel_buffer[0][1];
+			average_avel[1] = bb->average_avel_buffer[0][1];
+			average_lvel[2] = bb->average_lvel_buffer[0][2];
+			average_avel[2] = bb->average_avel_buffer[0][2];
+			
+			// If we're not in "instantaneous mode"
+			if ( bb->adis.average_samples > 1 )
+			{
+				// add remaining velocities together
+				for ( unsigned int i = 1; i < bb->adis.average_samples; ++i )
+				{
+					average_lvel[0] += bb->average_lvel_buffer[i][0];
+					average_avel[0] += bb->average_avel_buffer[i][0];
+					average_lvel[1] += bb->average_lvel_buffer[i][1];
+					average_avel[1] += bb->average_avel_buffer[i][1];
+					average_lvel[2] += bb->average_lvel_buffer[i][2];
+					average_avel[2] += bb->average_avel_buffer[i][2];
+				}
+
+				// make average
+				dReal r1 = dDIV(REAL( 1.0 ),REAL( bb->adis.average_samples ));
+
+				average_lvel[0] = dMUL(average_lvel[0],r1);
+				average_avel[0] = dMUL(average_avel[0],r1);
+				average_lvel[1] = dMUL(average_lvel[1],r1);
+				average_avel[1] = dMUL(average_avel[1],r1);
+				average_lvel[2] = dMUL(average_lvel[2],r1);
+				average_avel[2] = dMUL(average_avel[2],r1);
+			}
+
+			// threshold test
+			dReal av_lspeed, av_aspeed;
+			av_lspeed = dDOT( average_lvel, average_lvel );
+			if ( av_lspeed > bb->adis.linear_average_threshold )
+			{
+				idle = 0; // average linear velocity is too high for idle
+			}
+			else
+			{
+				av_aspeed = dDOT( average_avel, average_avel );
+				if ( av_aspeed > bb->adis.angular_average_threshold )
+				{
+					idle = 0; // average angular velocity is too high for idle
+				}
+			}
+		}
+
+		// if it's idle, accumulate steps and time.
+		// these counters won't overflow because this code doesn't run for disabled bodies.
+		if (idle) {
+			bb->adis_stepsleft--;
+			bb->adis_timeleft -= stepsize;
+		}
+		else {
+			// Reset countdowns
+			bb->adis_stepsleft = bb->adis.idle_steps;
+			bb->adis_timeleft = bb->adis.idle_time;
+		}
+
+		// disable the body if it's idle for a long enough time
+		if ( bb->adis_stepsleft <= 0 && bb->adis_timeleft <= 0 )
+		{
+			bb->flags |= dxBodyDisabled; // set the disable flag
+
+			// disabling bodies should also include resetting the velocity
+			// should prevent jittering in big "islands"
+			bb->lvel[0] = 0;
+			bb->lvel[1] = 0;
+			bb->lvel[2] = 0;
+			bb->avel[0] = 0;
+			bb->avel[1] = 0;
+			bb->avel[2] = 0;
+		}
+	}
+}
+
+
+//****************************************************************************
+// body rotation
+
+// return sin(x)/x. this has a singularity at 0 so special handling is needed
+// for small arguments.
+
+static inline dReal sinc (dReal x)
+{
+  // if |x| < 1e-4 then use a taylor series expansion. this two term expansion
+  // is actually accurate to one LS bit within this range if double precision
+  // is being used - so don't worry!
+  if (dFabs(x) < REAL(1.0e-4)) return REAL(1.0) - dMUL(dMUL(x,x),REAL(0.166666666666666666667));
+  else return dDIV(dSin(x),x);
+}
+
+
+// given a body b, apply its linear and angular rotation over the time
+// interval h, thereby adjusting its position and orientation.
+
+void dxStepBody (dxBody *b, dReal h)
+{
+  int j;
+
+  // handle linear velocity
+  for (j=0; j<3; j++) b->posr.pos[j] += dMUL(h,b->lvel[j]);
+
+  if (b->flags & dxBodyFlagFiniteRotation) {
+    dVector3 irv;	// infitesimal rotation vector
+    dQuaternion q;	// quaternion for finite rotation
+
+    if (b->flags & dxBodyFlagFiniteRotationAxis) {
+      // split the angular velocity vector into a component along the finite
+      // rotation axis, and a component orthogonal to it.
+      dVector3 frv;		// finite rotation vector
+      dReal k = dDOT (b->finite_rot_axis,b->avel);
+      frv[0] = dMUL(b->finite_rot_axis[0],k);
+      frv[1] = dMUL(b->finite_rot_axis[1],k);
+      frv[2] = dMUL(b->finite_rot_axis[2],k);
+      irv[0] = b->avel[0] - frv[0];
+      irv[1] = b->avel[1] - frv[1];
+      irv[2] = b->avel[2] - frv[2];
+
+      // make a rotation quaternion q that corresponds to frv * h.
+      // compare this with the full-finite-rotation case below.
+      h = dMUL(h,REAL(0.5));
+      dReal theta = dMUL(k,h);
+      q[0] = dCos(theta);
+      dReal s = dMUL(sinc(theta),h);
+      q[1] = dMUL(frv[0],s);
+      q[2] = dMUL(frv[1],s);
+      q[3] = dMUL(frv[2],s);
+    }
+    else {
+      // make a rotation quaternion q that corresponds to w * h
+      dReal wlen = dSqrt (dMUL(b->avel[0],b->avel[0]) + dMUL(b->avel[1],b->avel[1]) +
+			  dMUL(b->avel[2],b->avel[2]));
+      h = dMUL(h,REAL(0.5));
+      dReal theta = dMUL(wlen,h);
+      q[0] = dCos(theta);
+      dReal s = dMUL(sinc(theta),h);
+      q[1] = dMUL(b->avel[0],s);
+      q[2] = dMUL(b->avel[1],s);
+      q[3] = dMUL(b->avel[2],s);
+    }
+
+    // do the finite rotation
+    dQuaternion q2;
+    dQMultiply0 (q2,q,b->q);
+    for (j=0; j<4; j++) b->q[j] = q2[j];
+
+    // do the infitesimal rotation if required
+    if (b->flags & dxBodyFlagFiniteRotationAxis) {
+      dReal dq[4];
+      dWtoDQ (irv,b->q,dq);
+      for (j=0; j<4; j++) b->q[j] += dMUL(h,dq[j]);
+    }
+  }
+  else {
+    // the normal way - do an infitesimal rotation
+    dReal dq[4];
+    dWtoDQ (b->avel,b->q,dq);
+    for (j=0; j<4; j++) b->q[j] += dMUL(h,dq[j]);
+  }
+
+  // normalize the quaternion and convert it to a rotation matrix
+  dNormalize4 (b->q);
+  dQtoR (b->q,b->posr.R);
+
+  // notify all attached geoms that this body has moved
+  for (dxGeom *geom = b->geom; geom; geom = dGeomGetBodyNext (geom))
+    dGeomMoved (geom);
+}
+
+//****************************************************************************
+// island processing
+
+// this groups all joints and bodies in a world into islands. all objects
+// in an island are reachable by going through connected bodies and joints.
+// each island can be simulated separately.
+// note that joints that are not attached to anything will not be included
+// in any island, an so they do not affect the simulation.
+//
+// this function starts new island from unvisited bodies. however, it will
+// never start a new islands from a disabled body. thus islands of disabled
+// bodies will not be included in the simulation. disabled bodies are
+// re-enabled if they are found to be part of an active island.
+
+void dxProcessIslands (dxWorld *world, dReal stepsize, dstepper_fn_t stepper)
+{
+  dxBody *b,*bb,**body;
+  dxJoint *j,**joint;
+
+  // nothing to do if no bodies
+  if (world->nb <= 0) return;
+
+  // handle auto-disabling of bodies
+  dInternalHandleAutoDisabling (world,stepsize);
+
+  // make arrays for body and joint lists (for a single island) to go into
+  body = (dxBody**) malloc (world->nb * sizeof(dxBody*));
+  if(body == NULL){
+  	return;
+  }
+  joint = (dxJoint**) malloc (world->nj * sizeof(dxJoint*));
+  if(joint == NULL){
+  	free(body);
+	return;
+  }
+  int bcount = 0;	// number of bodies in `body'
+  int jcount = 0;	// number of joints in `joint'
+
+  // set all body/joint tags to 0
+  for (b=world->firstbody; b; b=(dxBody*)b->next) b->tag = 0;
+  for (j=world->firstjoint; j; j=(dxJoint*)j->next) j->tag = 0;
+
+  // allocate a stack of unvisited bodies in the island. the maximum size of
+  // the stack can be the lesser of the number of bodies or joints, because
+  // new bodies are only ever added to the stack by going through untagged
+  // joints. all the bodies in the stack must be tagged!
+  int stackalloc = (world->nj < world->nb) ? world->nj : world->nb;
+  dxBody **stack = (dxBody**) malloc (stackalloc * sizeof(dxBody*));
+  if(stack == NULL){
+	free(body);
+	free(joint);
+	return;
+  }
+
+  for (bb=world->firstbody; bb; bb=(dxBody*)bb->next) {
+    // get bb = the next enabled, untagged body, and tag it
+    if (bb->tag || (bb->flags & dxBodyDisabled)) continue;
+    bb->tag = 1;
+
+    // tag all bodies and joints starting from bb.
+    int stacksize = 0;
+    int firsttime = 1;
+    
+    b = bb;
+    body[0] = bb;
+    bcount = 1;
+    jcount = 0;
+    
+    while (stacksize > 0 || firsttime)
+    {
+      if (!firsttime)
+          {
+          b = stack[--stacksize];	// pop body off stack
+          body[bcount++] = b;	// put body on body list
+          }
+      else
+          {
+          firsttime = 0;
+          }
+
+      // traverse and tag all body's joints, add untagged connected bodies
+      // to stack
+      for (dxJointNode *n=b->firstjoint; n; n=n->next) {
+	if (!n->joint->tag) {
+	  n->joint->tag = 1;
+	  joint[jcount++] = n->joint;
+	  if (n->body && !n->body->tag) {
+	    n->body->tag = 1;
+	    stack[stacksize++] = n->body;
+	  }
+	}
+      }
+    }
+
+    // now do something with body and joint lists
+    stepper (world,body,bcount,joint,jcount,stepsize);
+
+    // what we've just done may have altered the body/joint tag values.
+    // we must make sure that these tags are nonzero.
+    // also make sure all bodies are in the enabled state.
+    int i;
+    for (i=0; i<bcount; i++) {
+      body[i]->tag = 1;
+      body[i]->flags &= ~dxBodyDisabled;
+    }
+    for (i=0; i<jcount; i++) joint[i]->tag = 1;
+  }
+  
+  free(body);
+  free(joint);
+  free(stack);
+
+}