simple ray-world collision detection

chrislu
Posts: 3
Joined: Fri Jun 22, 2007 6:39 am

simple ray-world collision detection

Post by chrislu »

hi,
i am completely new to collision detection and physics libraries, so please bear with me...

for the first step i want to do a simple ray world collision detection for selection purposes. i had a look at the collision inferface demo and i think i get the basics. now my question is how to simply use bullet collision detection to just test all world objects against a single ray and get the nearest collision object to the starting point of the ray (eye position)?

my second question would be if bullet is using the GIMPACT library implicitly for such collision detection or do i have to specify the use of GIMPACT directly for collision tests against concave objects?

thanks for the help...
-chris

p.s.
i saw that i have to specify the solver directly for physics simulation/constraint resolving. i couldn't find information about which solver i should use for which purpose in terms of efficiency?
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

When using Bullet, it is easiest to use the built-in raycast functionality. Mouse picking is using raycasting, see CcdPhysicsDemo, and Bullet/Demos/OpenGL/DemoApplication.cpp for implementation. Use the ClosestRayResultCallback to find the first hit. This is implemented in btCollisionWorld (and derived classes like btDiscreteDynamicsWorld).

Code: Select all

	/// rayTest performs a raycast on all objects in the btCollisionWorld, and calls the resultCallback
	/// This allows for several queries: first hit, all hits, any hit, dependent on the value returned by the callback.
	void	rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback);
Bullet (and Havok, Ageia PhysX) don't recommend using moving concave triangle meshes. It is better to use compound objects (convex decomposition). But if you really intend to use moving concave triangle meshes, you can use GIMPACT. See MovingConcaveDemo for an integration. The updated GIMPACT 0.2 is integrated in Bullet Subversion repository as Extras, so it will be available in next Bullet version 2.54.

It is best to stick with the default constraint solver. There is a option to choose ODE quickstep solver, but only contact constraints are hooked up (constraints/motors/limits are todo).

Hope this helps,
Erwin
chrislu
Posts: 3
Joined: Fri Jun 22, 2007 6:39 am

Post by chrislu »

Erwin Coumans wrote:Bullet (and Havok, Ageia PhysX) don't recommend using moving concave triangle meshes. It is better to use compound objects (convex decomposition). But if you really intend to use moving concave triangle meshes, you can use GIMPACT. See MovingConcaveDemo for an integration. The updated GIMPACT 0.2 is integrated in Bullet Subversion repository as Extras, so it will be available in next Bullet version 2.54.
ok, i will have a look at the svn version with GIMPACT 0.2.

i thought bullet uses GIMPACT implicitly for collision tests and i would not have to care about its integration. are there cases where it is better to use bullets own collision detection over GIMPACTs?
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

In all cases I recommend to just stick with Bullet's collision detection: Bullet doesn't use GIMPACT by default, it is just an optional extension.

Bullet handles collisions and raycasting against box, sphere, triangle, convex, cone, cylinder, static triangle mesh or compound shape natively without using GIMPACT.

There is one exception: when you need to have moving concave objects, it is recommended to decompose them (automatically or manually) into convex pieces and add them into a btCompoundShape. You can also use GIMPACT for moving concave triangle meshes (if you cannot or don't want to decompose your moving concave object for some reason), but a btCompoundShape is the preferred solution.

Thanks,
Erwin
Zimbarbo
Posts: 14
Joined: Thu Jun 28, 2007 2:26 pm

RayTest problems

Post by Zimbarbo »

Do not use RayTest if you are looking for an accurate way to get the exact point of contact.

I've been having problems with RayTest so I stepped into it. It would seem they are first testing against an aabb with RayAabb to see if it passes through the objects bounding box. This works fine. However, it is in RayTestSingle that looks like it checks against a sphere? In my case, I have a long box I am trying to get the coord my ray is hitting the aabb. However, it only detects it if the ray is pretty close to the center, and even then, the hit point is usually above the object. To their credit, there is a comment that says they should move RaytestSingle into a method on CollisionObject, and they are right.
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: RayTest problems

Post by Erwin Coumans »

Zimbarbo wrote:Do not use RayTest if you are looking for an accurate way to get the exact point of contact.
You should be able to use RayTest for finding the intersection of a ray against objects in the btCollisionWorld.

It sweeps a sphere of radius 0 (= point) againt all objects that overlap the aabb with the ray. It is using the collision margin, so that might explain why the intersection point is slightly above the box. Also, there is an accuracy threshold that causes another very small approximation. We could make this user-tunable. We could move the raycast to the btCollisionShape class, then the user can override the RayTest with a more accurate/custom version.

Hope this helps,
Erwin
Zimbarbo
Posts: 14
Joined: Thu Jun 28, 2007 2:26 pm

More detail

Post by Zimbarbo »

Ok, thanks, that does make more sense. However, I still see horrible results. I will try stepping into the SubsimbplexConvexCast's CalctimeOfImpact to see what is happening because in my example here is what I get:

(X,Y,Z)
I have a collision object with an Aabb (and actual coords since it is a box) of
Min - (69,73,-10)
Max - (261, 100, 20)

I shoot a ray:
From: (105, 24, 0)
To: (304, 224, 0)

I get a hit point of (217,136,0)

136(Y) is more than a little bit above 100(Y), which is pretty bad.

And when I shoot the ray;
From (51, 38, 0)
To (251, 238, 0)

It doesn't hit at all. The collision of dynamic objects against this box are spot on. I have another box I can control and fly around it, and it hits and interacts perfectly, so I know my rigidbody and graphical representation is fine. There is just either something wrong with what I am expecting from the ray test, or something wrong with the ray test.

Thanks for the reply and explanation, though!
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

Is this XNA C# or C++? If C++, can you create a reproducing case in one of the Bullet demos (for example Raytracer demo)

If it is XNA / C#, it might be a porting bug. I will create a Bullet XNA / C# forum, to avoid confusion. Can you make sure to report XNA/C# bugs here?
http://www.codeplex.com/xnadevru/WorkItem/List.aspx


Thanks,
Erwin
Zimbarbo
Posts: 14
Joined: Thu Jun 28, 2007 2:26 pm

C#

Post by Zimbarbo »

Yes, it is in the C#/XNA version.
Zimbarbo
Posts: 14
Joined: Thu Jun 28, 2007 2:26 pm

My Solution

Post by Zimbarbo »

because I am just dealing with Aabb, I made a simple function to test a ray against it and find the hitpoint. The intersection code came right from GraphicsGems, so I tried not to alter it, and I give it credit here.

Code: Select all

public bool HitBoundingBox( Vector3 vMinBox, Vector3 vMaxbox, Vector3 vFrom, Vector3 vDir, out Vector3 vHitPoint )
        {
        //char HitBoundingBox(minB,maxB, origin, dir,coord)
        //double minB[NUMDIM], maxB[NUMDIM];		/*box */
        //double origin[NUMDIM], dir[NUMDIM];		/*ray */
        //double coord[NUMDIM];				/* hit point */
        //{

            int NUMDIM = 3;
            int LEFT = 1;
            int RIGHT = 0;
            int MIDDLE = 2;

            float[] minB = { 0.0f, 0.0f, 0.0f };
            float[] maxB = { 0.0f, 0.0f, 0.0f };
            float[] origin = { 0.0f, 0.0f, 0.0f };
            float[] dir = { 0.0f, 0.0f, 0.0f };		/*ray */
            float[] coord = { 0.0f, 0.0f, 0.0f };

            vHitPoint = Vector3.Zero;

            minB[0] = vMinBox.X;  minB[1] = vMinBox.Y; minB[2] = vMinBox.Z;
            maxB[0] = vMaxbox.X;  maxB[1] = vMaxbox.Y; maxB[2] = vMaxbox.Z;
            origin[0] = vFrom.X;  origin[1] = vFrom.Y; origin[2] = vFrom.Z;
            dir[0] = vDir.X;  dir[1] = vDir.Y; dir[2] = vDir.Z;

            bool inside = true;
            float[] quadrant = { 0.0f, 0.0f, 0.0f };
	        int i;
	        int whichPlane;
            float[] maxT = { 0.0f, 0.0f, 0.0f };
            float[] candidatePlane = { 0.0f, 0.0f, 0.0f };

	        /* Find candidate planes; this loop can be avoided if
   	        rays cast all from the eye(assume perpsective view) */
	        for (i=0; i<NUMDIM; i++)
		        if(origin[i] < minB[i]) {
			        quadrant[i] = LEFT;
			        candidatePlane[i] = minB[i];
			        inside = false;
		        }else if (origin[i] > maxB[i]) {
			        quadrant[i] = RIGHT;
			        candidatePlane[i] = maxB[i];
			        inside = false;
		        }else	{
			        quadrant[i] = MIDDLE;
		        }

	        /* Ray origin inside bounding box */
	        if(inside)	{
		        coord = origin;
                vHitPoint.X = coord[0];
                vHitPoint.Y = coord[1];
                vHitPoint.Z = coord[2];

		        return (true);
	        }


	        /* Calculate T distances to candidate planes */
	        for (i = 0; i < NUMDIM; i++)
		        if (quadrant[i] != MIDDLE && dir[i] !=0.0f)
			        maxT[i] = (candidatePlane[i]-origin[i]) / dir[i];
		        else
			        maxT[i] = -1.0f;

	        /* Get largest of the maxT's for final choice of intersection */
	        whichPlane = 0;
	        for (i = 1; i < NUMDIM; i++)
		        if (maxT[whichPlane] < maxT[i])
			        whichPlane = i;

	        /* Check final candidate actually inside box */
	        if (maxT[whichPlane] < 0.0f) return (false);
	        for (i = 0; i < NUMDIM; i++)
		        if (whichPlane != i) {
			        coord[i] = origin[i] + maxT[whichPlane] *dir[i];
			        if (coord[i] < minB[i] || coord[i] > maxB[i])
				        return (false);
		        } else {
			        coord[i] = candidatePlane[i];
		        }

            vHitPoint.X = coord[0];
            vHitPoint.Y = coord[1];
            vHitPoint.Z = coord[2];
	        return (true);				/* ray hits box */
        }	
        
        public bool RayTest( RigidBody bodyBox, Vector3 from, Vector3 to, out Vector3 hitPoint )
        {
            Boolean foundHit = false;
            hitPoint = Vector3.Zero;

            DiscreteDynamicsWorld physics = (DiscreteDynamicsWorld)(game.Services.GetService(typeof(DiscreteDynamicsWorld)));

            Vector3 currentHitPoint = new Vector3();
            float minRayLength = float.MaxValue;
            float currentRayLength = float.MaxValue;
            Vector3 collisionObjectAabbMin, collisionObjectAabbMax;
            float hitLambda = 1f;
            Vector3 hitNormal = new Vector3();

            Vector3 vDir = to - from;
            vDir.Normalize();

            foreach (CollisionObject collisionObject in physics.CollisionObjects)
            {
                collisionObject.CollisionShape.GetAabb(collisionObject.WorldTransform, out collisionObjectAabbMin, out collisionObjectAabbMax);

                //  Quick function to cull out many objects. might now be needed since hitboundingbox does about the same thing.			        
                if (XnaDevRu.BulletX.MathHelper.RayAabb(from, to, collisionObjectAabbMin, collisionObjectAabbMax, hitLambda, hitNormal))
                {
                    RigidBody body = RigidBody.Upcast(collisionObject);
                    if (!ReferenceEquals(body, bodyBox))
                    {
                        bool bHit = HitBoundingBox(collisionObjectAabbMin, collisionObjectAabbMax, from, vDir, out currentHitPoint);

                        if (bHit)
                        {
                            Vector3 vLength = currentHitPoint - from;
                            currentRayLength = vLength.Length();

                            if( currentRayLength < minRayLength )
                            {
                                minRayLength = currentRayLength;
                                hitPoint = currentHitPoint;
                                foundHit = true;
                            }
                        }
                    }
                }
            }

            return foundHit;
        }
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

This forum is mainly for Bullet C++ version.

For questions/feedback/contribution to the C# version, please visit codeplex:

http://www.codeplex.com/xnadevru/Wiki/V ... Title=Home

Thanks,
Erwin
divzero
Posts: 2
Joined: Sun Nov 25, 2007 4:09 am

Re: simple ray-world collision detection

Post by divzero »

Note: apologies for bumping this old thread, but I do have a relevant reply (perhaps an admin could prepend "XNA" or something to the subject line to avoid confusion?)

Ok, thanks, that does make more sense. However, I still see horrible results. I will try stepping into the SubsimbplexConvexCast's CalctimeOfImpact to see what is happening because in my example here is what I get:
I have had the same experience with the XNA port of Bullet. I recently ported the Raycast Vehicle class which I needed, and along the way hit up against this problem.

Ray-casts against non-rotated boxes work perfectly. Ray-casts against rotated boxes (even a slight rotation of 1 degree) give very wild results.

I wish I had a solution for you, but I don't - this is just a confirmation of the bug (which was presumably a result of porting). I don't have time to look further at the bug, as I really want to move on with development of my hobby game, so for now it's flat collision only for me :) Suffice to say I'd be pretty grateful if someone did take a look at it.

Cheers,
Will