[ODE] Having some trouble with rays attached to geom transforms

Megan Fox shalinor at circustent.us
Fri Jul 30 15:44:54 MST 2004


I'm having a bit of trouble with my "sensors" - that is, rays attached to
physical bodies via gem transforms, so that they can have an orientation
independent of the physical body.  (fake car suspension, hovering player
bodies, etc)

The problem is that it's as if the ray is originating from a position quite
a bit left of where the body actually is.  When using a sensor to create a
chasecam (works perfectly, no complaints, except this one bug ;) ), it
collides when the box in question is still a good ways to the left of the
entity, and stops colliding when the entity is still behind the box.


Some code and screenshots:

Here's the creation code for the ray -

    data->rayGeomID = nOpende::CreateRay(0, maxLen);
    dGeomID transGeomID =
nOpende::CreateGeomTransform(this->refPhysServer->physColSpaceID);
    nOpende::GeomTransformSetGeom(transGeomID, data->rayGeomID);
    nOpende::GeomRaySet(data->rayGeomID, pos, dir);
    nOpende::GeomSetData(data->rayGeomID, data);
    nOpende::GeomSetData(transGeomID, data);
    nOpende::GeomSetBody(transGeomID, this->tempBodyID);

Here's the orientation update, called once per sensor right before the phys
steps (localDir is the vector direction in object space - (0,0,1) in this
case):

    quaternion quat = ownerEntity->GetRot();
    vector3 headingVec = quat.rotate(sensorList->localDir);
    nOpende::GeomRaySet(sensorList->rayGeomID, sensorList->localPos,
headingVec);

Here's the chasecam positioning code (hasCollided and collisionDepth are set
in the collision callback, collisionDepth being set to the shallowest
collision, if multiples exist.  tempPos, ray start point, is (0,0,0) in this
case):

    vector3 tempPos, tempDir;
    nOpende::GeomRayGet(data->rayGeomID, tempPos, tempDir);

    tempPos += curLookatEntity->GetPosition();
    tempDir.norm();

    if (data->hasCollided)
    {
        tempDir *= data->collisionDepth;
        tempPos += tempDir;
    }
    else
    {
        tempDir *= nOpende::GeomRayGetLength(data->rayGeomID);
        tempPos += tempDir;
    }

    ownerEntity->SetPosition(tempPos);


Here's the entirety of my collision callback, just to be complete:


void eODEServer::PhysCollisionCallback(void *data, dGeomID o1, dGeomID o2)
{
    eODEServer *server = (eODEServer*)data;

    // Determine the true geom type - unwrap the geom from the transform, if
necessary
    int geom1Type, geom2Type;
    dGeomID geom1Wrapped = NULL;
    dGeomID geom2Wrapped = NULL;
    if (nOpende::GeomGetClass(o1) == dGeomTransformClass)
    {
        geom1Wrapped = nOpende::GeomTransformGetGeom(o1);
        geom1Type = nOpende::GeomGetClass(geom1Wrapped);
    }
    else
        geom1Type = nOpende::GeomGetClass(o1);

    if (nOpende::GeomGetClass(o2) == dGeomTransformClass)
    {
        geom2Wrapped = nOpende::GeomTransformGetGeom(o2);
        geom2Type = nOpende::GeomGetClass(geom2Wrapped);
    }
    else
        geom2Type = nOpende::GeomGetClass(o2);

    // Check if either geom is a ray - if it is, ASSUME that the data ptr
for the ray points to
    // a valid eODESensorData structure.
    if (geom1Type == dRayClass)
    {
        // Do the collision
        if (nOpende::Collide(o1, o2, 1, &server->physContactArray[0].geom,
sizeof(dContact)))
        {
            // Store the appropriate data
            eODESensorData *data =
(eODESensorData*)nOpende::GeomGetData(o1);

            // If there was no prior collision, simply store this one
            if (!data->hasCollided)
            {
                data->hasCollided = true;
                data->collisionDepth =
server->physContactArray[0].geom.depth;

nOpendeMarshal::dVector3_2_vector3(server->physContactArray[0].geom.normal,
data->reflectionNormal);
            }
            // If there was, store the closest contact (shortest collision
depth)
            else if (data->collisionDepth >
server->physContactArray[0].geom.depth)
            {
                data->hasCollided = true;
                data->collisionDepth =
server->physContactArray[0].geom.depth;

nOpendeMarshal::dVector3_2_vector3(server->physContactArray[0].geom.normal,
data->reflectionNormal);
            }
        }

        // FIXME: does not properly handle trigger objects or what have you,
assumes everything is solid
    }
    else if (geom2Type == dRayClass)
    {
        // Do the collision
        if (nOpende::Collide(o2, o1, 1, &server->physContactArray[0].geom,
sizeof(dContact)))
        {
            // Store the appropriate data
            eODESensorData *data =
(eODESensorData*)nOpende::GeomGetData(o2);

            // If there was no prior collision, simply store this one
            if (!data->hasCollided)
            {
                data->hasCollided = true;
                data->collisionDepth =
server->physContactArray[0].geom.depth;

nOpendeMarshal::dVector3_2_vector3(server->physContactArray[0].geom.normal,
data->reflectionNormal);
            }
            // If there was, store the closest contact (shortest collision
depth)
            else if (data->collisionDepth >
server->physContactArray[0].geom.depth)
            {
                data->hasCollided = true;
                data->collisionDepth =
server->physContactArray[0].geom.depth;

nOpendeMarshal::dVector3_2_vector3(server->physContactArray[0].geom.normal,
data->reflectionNormal);
            }
        }

        // FIXME: does not properly handle trigger objects or what have you,
assumes everything is solid
    }
    // If none of the geoms are of a special type, continue on as usual
    else
    {
        int numContacts = nOpende::Collide(o1, o2, PHYS_MAX_CONTACTS,
&server->physContactArray[0].geom, sizeof(dContact));

        for (int index = 0; index < numContacts; index++)
        {
            dJointID jointID =
nOpende::JointCreateContact(server->physWorldID,
server->physContactJointGroupID, &server->physContactArray[index]);
            nOpende::JointAttach(jointID, nOpende::GeomGetBody(o1),
nOpende::GeomGetBody(o2));
        }
    }
}




And here's some screenshots:

Here is a starting point.  Just about the closest I can get to the box
before it causes the bug: http://shalinor.circustent.us/images/chasecam1.jpg

Here is what happens when I move a hair to the right (chasecam adjusts to
avoid collision): http://shalinor.circustent.us/images/chasecam2.jpg

Here is where I am when the chasecam decides it is no longer colliding (I'm
that little black smudge above the cube - that's the penguin's skull):
http://shalinor.circustent.us/images/chasecam3.jpg


Throughout the sequence, the camera is adjusting in a way that suggests it's
colliding perfectly with the cube - the distance shortens and then lengthens
to account for the shape of the cube from that angle - it just does it as if
the chasecam sensor is physically sitting about 15m too far to the right.


I've done a fair bit of debugging on the cameraai functions, and the vectors
and startpos it's reporting are square on the money (then again, of course,
they are, the camera remains exactly behind the penguin, right where I
expect it).  Given that the camera is being positioned based on data spat
out from the physics side of things, I'm confused how the collision would be
this far off?

Am I encountering some sort of bug when combing rays with geom transforms?
Is that not allowed?

Thanks,

-Megan Fox / "Shalinor"
Lead Developer, Elium Project (http://www.elium.tk)



More information about the ODE mailing list