[ODE] Any BloodRayne 2 developers listening?
Mark Randel
mark.randel at gmail.com
Mon Nov 1 19:25:42 MST 2004
It will probably explain more to just post our code for this. Since
this is directly cut and paste from our library, you may not compile
directly. Basically, it puts an island to sleep if all bodies are
below a threshold. If any body in the island is above the threshold,
it resets the timer on all bodies in that island. All bodies in an
island get the same timer, the maximum of the timer in that island.
I am not sure how well this will integrate into the current version of
ODE. We started with ODE 0.39 and have only integrated bug fixes or
stuff that we needed. At some point, there was a new autodisable
routine integrated, but we don't have that in our code.
And we have hard coded thresholds for auto disabling. Note that we
use 1.0F = 1 foot, and 32.0F = gravity. They worked well enough so we
didn't have to have different constants per sim type.
Here it goes...sorry for any email formatting problems...and the lengthy post...
In object.h, declare the following:
const float kMaxAutoDisableTimer = 1.0F;
Inside of dxBody, declare the following member:
float autoDisableTimer;
In ode.cpp, inside of...
dBodySetPosition
dBodySetRotation
dBodySetQuaternion
dBodySetLinearVel
dBodySetAngularVel
dBodyAddForce (and all its dervitives)
...add the following line:
b->autoDisableTimer = kMaxAutoDisableTimer;
This will set the timer to maximum when you position a body, add a
force, etc. You should disable a body after setting it's inital
values, otherwise it will start out enabled.
Inside your nearCallback routine, make sure that you enable touching bodies...
if (b1 && b2) {
if (dBodyIsEnabled(b1) || dBodyIsEnabled(b2)) {
dBodyEnable(b1);
dBodyEnable(b2);
}
}
>From our island stepper (we changed stepfast.cpp to call dxQuickStep)...
static void
processIslandsFast (dxWorld * world, dReal stepsize, int maxiterations)
{
PROFILE_BLOCK(ode_processIslandsFast);
dxBody *b, *bb, **body;
dxJoint *j, **joint;
// nothing to do if no bodies
if (world->nb <= 0)
return;
// make arrays for body and joint lists (for a single island) to go into
body = (dxBody**)malloc(world->nb * sizeof(dxBody*));
if (body == 0) {
GTFO("processIslandsFast - Out of memory allocating %d bodies
(nj=%d)",world->nb,world->nj);
}
joint = 0;
if (world->nj > 0) {
joint = (dxJoint**)malloc(world->nj * sizeof(dxJoint*));
if (joint == 0) {
GTFO("processIslandsFast - Out of memory allocating %d joints
(nb=%d)",world->nj,world->nb);
}
}
int bcount = 0; // number of bodies in `body'
int jcount = 0; // number of joints in `joint'
int tbcount = 0;
int tjcount = 0;
// 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 = 0;
if (stackalloc > 0) {
stack = (dxBody**)malloc(stackalloc * sizeof(dxBody*));
if (stack == 0) {
GTFO("processIslandsFast - Out of memory allocating %d stackalloc
(nb=%d,nj=%d)",stackalloc,world->nb,world->nj);
}
}
int *autostack = 0;
if (stackalloc > 0) {
autostack = (int*)malloc(stackalloc * sizeof(int));
if (autostack == 0) {
GTFO("processIslandsFast - Out of memory allocating %d autostack
(nb=%d,nj=%d)",stackalloc,world->nb,world->nj);
}
}
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 autoDepth = autoEnableDepth;
b = bb;
body[0] = bb;
bcount = 1;
jcount = 0;
goto quickstart;
while (stacksize > 0)
{
b = stack[--stacksize]; // pop body off stack
autoDepth = autostack[stacksize];
body[bcount++] = b; // put body on body list
quickstart:
// 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)
{
int thisDepth = autoEnableDepth;
n->joint->tag = 1;
joint[jcount++] = n->joint;
if (n->body && !n->body->tag)
{
if (n->body->flags & dxBodyDisabled)
thisDepth = autoDepth - 1;
if (thisDepth < 0)
continue;
n->body->flags &= ~dxBodyDisabled;
n->body->tag = 1;
autostack[stacksize] = thisDepth;
stack[stacksize++] = n->body;
}
}
}
dIASSERT (stacksize <= world->nb);
dIASSERT (stacksize <= world->nj);
}
// // now do something with body and joint lists
// dInternalStepIslandFast (world, body, bcount, joint, jcount,
stepsize, maxiterations);
dxQuickStepper(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;
tbcount += bcount;
tjcount += jcount;
// Process autodisabling on the island of bodies. Find
// the maximum auto disable timer
float maxTimer = 0.0F;
for (i = 0; i < bcount; i++) {
if (body[i]->autoDisableTimer > maxTimer) {
maxTimer = body[i]->autoDisableTimer;
}
}
// Check to see if all the bodies are candidates for
// disabling
int disableCount = 0;
for (i = 0; i < bcount; i++) {
float moveSpeed = body[i]->lvel[0] * body[i]->lvel[0] +
body[i]->lvel[1] * body[i]->lvel[1] + body[i]->lvel[2] *
body[i]->lvel[2];
float angSpeed = body[i]->avel[0] * body[i]->avel[0] +
body[i]->avel[1] * body[i]->avel[1] + body[i]->avel[2] *
body[i]->avel[2];
if ((moveSpeed < 1.0F) && (angSpeed < 1.0F)) {
disableCount++;
}
}
if (disableCount == bcount) {
maxTimer -= stepsize;
if (maxTimer < 0.0F) {
maxTimer = 0.0F;
}
} else {
maxTimer = kMaxAutoDisableTimer;
}
for (i = 0; i < bcount; i++) {
body[i]->autoDisableTimer = maxTimer;
if (maxTimer == 0.0F) {
body[i]->flags |= dxBodyDisabled;
}
}
}
// if debugging, check that all objects (except for disabled bodies,
// unconnected joints, and joints that are connected to disabled bodies)
// were tagged.
# ifndef dNODEBUG
for (b = world->firstbody; b; b = (dxBody *) b->next)
{
if (b->flags & dxBodyDisabled)
{
if (b->tag)
dDebug (0, "disabled body tagged");
}
else
{
if (!b->tag)
dDebug (0, "enabled body not tagged");
}
}
for (j = world->firstjoint; j; j = (dxJoint *) j->next)
{
if ((j->node[0].body && (j->node[0].body->flags & dxBodyDisabled) ==
0) || (j->node[1].body && (j->node[1].body->flags & dxBodyDisabled) ==
0))
{
if (!j->tag)
dDebug (0, "attached enabled joint not tagged");
}
else
{
if (j->tag)
dDebug (0, "unattached or disabled joint tagged");
}
}
# endif
// Free allocated memory
free(body);
if (joint != 0) {
free(joint);
}
if (stack != 0) {
free(stack);
}
if (autostack != 0) {
free(autostack);
}
}
That should do it. You should now have a nice autodisable system of
islands rather than bodies.
>> We then wrote our own auto-disable that would disable an island of
>> bodies, rather than one individual body. This is subtle, but the
>> effect was amazing. Dead bodies, box stacks, etc. became totally
>> stable with this. In one test level, I had 20 boxes stacked one on
>> top of another. You could run into the stack, it would sway, then
>> stop. If you hit it hard enough, then it would fall over.
>
>Has anyone else tried this method - disabling the whole island instead
>of single bodies? How well does it work for others?
>
>Mark - do you have any more details on how you did this (e.g. how do
>you decide when to put the island asleep)?
>
>Tyler
More information about the ODE
mailing list