[ODE] Determining if a point is inside a geom

Shamyl Zakariya shamyl at zakariya.net
Tue May 8 07:28:18 MST 2007


I'm trying to come up with a quick way to test if a point is inside a  
geom, primarily so I can check if a lightsource goes inside a shadow- 
caster but I imagine it will be useful elsewhere. I've taken two  
approaches, but both only work for trimeshes ( significant! ) and not  
for primitive types such boxes, capsules, etc.

My first approach was to cast a ray from the point straight up, and  
to keep track of how many times it intersected each geom. The closest  
geom with an odd intersection count should be the geom the point is  
inside of. But, I was only getting one intersection point for  
primitives shapes.

My second approach was to cast the ray ( up, again ) and to dot the  
ray's direction against the closest collision's normal, and if  
greater than zero then the point's probably inside that geom. Again,  
this only works for trimeshes.

Looking at both of these and trying to come up with a common failure  
point, it seems that my problem here is that a ray cast from *inside*  
a primitive geom doesn't collide with that geom.

Since the problem is the same for both, I'm posting the simpler of  
the two code samples. Hopefully somebody can find a bug here and help  
me fix it...



EntityRef
Ray::pointCheck( const vec3 &p )
{
     vec3 up( 0.0f, 0.0f, 1.0f );
     Collision c = cast( p, up, dInfinity );
     if ( c.didCollide )
     {
         if ( dot( c.normal, up ) > 0 )
         {
             return c.collidedWith.lock();
         }
     }

     return EntityRef();
}

Ray::Collision
Ray::cast( const vec3 &origin,
            const vec3 &direction,
            float length )
{
     if ( !_space )
     {
         _space = dHashSpaceCreate( 0 );
         _ray = dCreateRay( _space, length );
     }

     dGeomRaySet( _ray, origin.x, origin.y, origin.z,
                  direction.x, direction.y, direction.z );

     Ray::Collision cActor, cStatic;
     cActor.ray = cStatic.ray = shared_from_this();

     WorldRef w( world() );

     /*
         Check against actor space, and static space
     */

     dSpaceCollide2( ( dGeomID ) _space, (dGeomID) w->actorSpace(),
                     (void*) &cActor, &raycastCallback);

     dSpaceCollide2( ( dGeomID ) _space, (dGeomID) w->staticSpace(),
                     (void*) &cStatic, &raycastCallback);


     /*
         Find closest collision
     */

     if ( cActor.distance < cStatic.distance )
     {
         _lastCollision = cActor;
         return cActor;
     }

     _lastCollision = cStatic;
     return cStatic;
}

void Ray::raycastCallback( void *data, dGeomID g1, dGeomID g2 )
{
     Collision *collision = (Collision *) data;

     // one of the two geoms is the ray, and one is the object it  
intersected

     dGeomID ray = NULL, other = NULL;
     if ( dGeomGetClass( g1 ) == dRayClass )
     {
         ray = g1;
         other = g2;
     }
     else if ( dGeomGetClass( g2 ) == dRayClass )
     {
         ray = g2;
         other = g1;
     }
     else
     {
         return;
     }

     const int nContacts = 32;
     dContactGeom contacts[ nContacts ];
     int generated = dCollide( ray, other, nContacts, contacts, sizeof 
(dContactGeom));

     /*
         Now we've got to iterate the contacts and find the closest  
( compared
         to the distance in collision
     */
     if ( generated > 0 )
     {
         int closest = 0;
         for ( int i = 1; i < generated; i++ )
         {
             if ( contacts[i].depth < contacts[closest].depth )  
closest = i;
         }

         /*
             See if the closest contact is closer than what's already in
             collision -- if it is ( or if collision is "empty" ) --
             set the fields.
         */
         if ( !collision->didCollide || contacts[closest].depth <  
collision->distance )
         {
             /*
                 If we can extract an entity from 'other' we have to  
ask if it allows
                 ray intersection with this ray, otherwise, we can  
just assume
                 an anonymous geom allows ray intersection.
             */

             ColliderRef c = Collider::colliderForGeom( other );
             EntityRef e = c ? c->entity() : EntityRef();
             RayRef r = collision->ray.lock();

             if ( !e || !r || ( e->allowRayIntersection( r )))
             {
                 collision->distance = contacts[closest].depth;
                 collision->didCollide = true;
                 collision->position = contacts[closest].pos;
                 collision->normal = contacts[closest].normal;
                 collision->collidedWith = e;
             }
         }
     }
}


shamyl at zakariya.net
	In the same episode, the scientist suggests that the
	debigulation can only be reversed by a rebigulator.
		-- wikipedia




More information about the ODE mailing list