[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