rayTest problem

AlexSilverman
Posts: 141
Joined: Mon Jul 02, 2007 5:12 pm

rayTest problem

Post by AlexSilverman »

Hello,

I need support for a segment test that returns multiple collisions, so I added a MultipleRayResultCallback to Bullet\BulletCollision\CollisionDispatch\btCollisionWorld.h.

I am currently testing a segment (beginning at an arbitrary object and ending at my camera) against my scene, and turning any colliding objects transparent, so that the arbitrary object will always be visible. I've found that the btCollisionWorld::rayTest is not reporting some collisions in certain instances (verified both by the fact that those objects are not being turned transparent, and also by looking at the btCollisionObject * array in the custom results structure). I'm only seeing this happening in certain places, but I'm not sure why it's happening.

I've adjusted the CcdPhysicsDemo to show this. You'll have to take my word that the start and end points pass through the objects I say they do, but the rest is explained in the comments of my code. I've denoted any sections I've added/changed with the comment "ALEX_NOTE" so searching for that will highlight anything I've done. I've attached an archive containing only the files I've changed to acheive this (along with the files that contain the trimesh data needed, ), in the correct directory structure, so extracting it an existing installation of Bullet 2.64 will only add the MultipleRayResultCallback structure, and change the CcdPhysicsDemo to show my behavior. I'm going to keep looking through my scene to see about other instances of this happening, and if I can see any common threads. If anyone would like this demo altered to show some other aspect of what's happening, let me know and I'll do my best to oblige.

- Alex
You do not have the required permissions to view the files attached to this post.
venzon
Posts: 12
Joined: Mon Nov 26, 2007 2:42 am

Re: rayTest problem

Post by venzon »

It'd be great to have a multiple ray result callback as a standard feature. I ended up rolling my own, also (we even named it the same):

Code: Select all

struct	MultipleRayResultCallback : public btCollisionWorld::RayResultCallback
{
	MultipleRayResultCallback(const btVector3& rayFromWorld,const btVector3& rayToWorld) : m_rayFromWorld(rayFromWorld), m_rayToWorld(rayToWorld) {}
	btVector3	m_rayFromWorld;//used to calculate hitPointWorld from hitFraction
	btVector3	m_rayToWorld;
	
	list <btCollisionWorld::LocalRayResult> results;
	
	virtual	btScalar AddSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
	{
		m_closestHitFraction = rayResult.m_hitFraction;
		results.push_back(rayResult);
		
		if (!normalInWorldSpace)
		{
			///need to transform normal into worldspace
			results.back().m_hitNormalLocal = rayResult.m_collisionObject->getWorldTransform().getBasis()*rayResult.m_hitNormalLocal;
		}
		//m_hitPointWorld.setInterpolate3(m_rayFromWorld,m_rayToWorld,rayResult.m_hitFraction);
		return rayResult.m_hitFraction;
	}
};
I didn't have time to check through your code, but you might want to try my implementation just to make sure the problem is really in bullet.
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: rayTest problem

Post by Erwin Coumans »

I haven't checked the issue yet, but it should be possible to get the first hit, the closest hit or all hits using Bullet's ray test.

There will be a demo in Bullet 2.65 that shows the 3 different versions.

Thanks for the feedback,
Erwin
AlexSilverman
Posts: 141
Joined: Mon Jul 02, 2007 5:12 pm

Re: rayTest problem

Post by AlexSilverman »

@venzon
Thanks for the code. I replaced my MultipleRayResultsCallback structure with yours and the result is the same. The only real difference between ours is that I went with an array of ten pointers rather than a list to avoid as many runtime allocations and deallocations. Unfortunately it looks to be the same result, so the problem seems to be in Bullet somewhere.

@Erwin
I have in my code two raytests. The first does return two results, so the MultipleRayResultsCallback seems to be catching multiple results just fine. I've also found other situations in my game where I can catch as many as 8 results (because that's as many objects as I can manage to line up), so the problem doesn't seem to be there. The second raytest is where the problems are, as it appears as if the first collision somehow invalidates all other collisions. I'm trying to steal time away wherever I can to step through variations on the scene in my uploaded demo and find where the problems occur. I'll let you know what I find, if I'm able to find anything :)

Thanks for your help.
- Alex
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: rayTest problem

Post by Erwin Coumans »

From btCollisionWorld.h:

Code: Select all

	/// This allows for several queries: first hit, all hits, any hit, dependent on the value returned by the callback.
The user needs to return 0 for first hit, hitFraction for closest hit, and 1 for all hits in the AddSingleResult callback.

It might be that the hitFraction isn't propagated properly at the moment, I will check this. The user-returned hitFraction is used for termination/early out.

I'll try to get this fixed today,
Thanks,
Erwin
AlexSilverman
Posts: 141
Joined: Mon Jul 02, 2007 5:12 pm

Re: rayTest problem

Post by AlexSilverman »

Erwin,

I believe this makes sense with what I've seen. My mostly blind stepping through the code has shown that the difference between the tests that work properly and the tests that don't is in line 61 of btRaycastCallback, where distance is compared to m_hitFraction. m_hitFraction is 1 when it works, and a different (smaller, but I don't remember the exact value) value in the tests that do not work.

I just changed this in the header being compiled by my game and I didn't see a change, but I haven't had a chance to test with this change in the Bullet Demos. I'll see what I can see.

Thanks for your help.
- Alex
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: rayTest problem

Post by Erwin Coumans »

Hi Alex,

I just had the chance to look into your demo, and made it work with Bullet 2.65 beta 2.

Main chance is to always return closestHitFraction in AddSingleResult, and to assign closestHitFraction according to:
//3 options:
//A) ALL HITS: don't update m_closestHitFraction
//B: CLOSEST HIT: assign m_closestHitFraction = rayResult.m_hitFraction;
//C: FIRST HIT: assign m_closestHitFraction = 0.f;


Can you try out attached demo with Bullet 2.65 beta 2 (you don't need to add MultipleRayResultCallback to btCollisionWorld, attached demo has it included as local class in CcdPhysicsdemo.

Bullet 2.65 should also has better ray test performance against meshes, especially for long rays.
Hope this helps,
Erwin


Code: Select all

struct	MultipleRayResultCallback : public btCollisionWorld::RayResultCallback
	{
		MultipleRayResultCallback(const btVector3&	rayFromWorld,const btVector3&	rayToWorld)
		:m_rayFromWorld(rayFromWorld),
		m_rayToWorld(rayToWorld)
		{
            iNumObjects = 0;
            iClosestObj = -1;
            fClosestDist = 999.f;
            for(int i = 0; i < 10; i++)
            {
                m_collisionObject[i] = NULL;
            }
		}

		btVector3	m_rayFromWorld;//used to calculate hitPointWorld from hitFraction
		btVector3	m_rayToWorld;

		btVector3	m_hitNormalWorld;
		btVector3	m_hitPointWorld;
		btCollisionObject * m_collisionObject[10];
        int iNumObjects;
        int iClosestObj;
        float fClosestDist;
		
		virtual	btScalar	AddSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
		{
            // Filter if we're at 10 objects already
			//assert(iNumObjects <= 10);
            if(iNumObjects == 10)
                return 0.f;
			
			m_collisionObject[iNumObjects] = rayResult.m_collisionObject;
            iNumObjects++;
			if (normalInWorldSpace)
			{
				m_hitNormalWorld = rayResult.m_hitNormalLocal;
			}
            else
			{
				///need to transform normal into worldspace
				m_hitNormalWorld = m_collisionObject[0]->getWorldTransform().getBasis()*rayResult.m_hitNormalLocal;
			}

			m_hitPointWorld.setInterpolate3(m_rayFromWorld,m_rayToWorld,rayResult.m_hitFraction);

            // Compare the distance to this point to the distance to the previous closest point 
            if(rayResult.m_hitFraction < fClosestDist)
            {
                iClosestObj = iNumObjects - 1;

				//3 options: 
				//A) ALL HITS: don't update m_closestHitFraction
				//B: CLOSEST HIT: assign m_closestHitFraction = rayResult.m_hitFraction;
				//C: FIRST HIT: assign m_closestHitFraction = 0.f;
    		//	m_closestHitFraction = rayResult.m_hitFraction;
    			//m_closestHitFraction = 0.f;
				
            }

            fClosestDist = rayResult.m_hitFraction;
			//always return m_closestHitFraction
			return m_closestHitFraction;
		}
    };
You do not have the required permissions to view the files attached to this post.
AlexSilverman
Posts: 141
Joined: Mon Jul 02, 2007 5:12 pm

Re: rayTest problem

Post by AlexSilverman »

Hi Erwin,

I just integrated Bullet 2.65 beta 2 into our game and it worked perfectly in every test situation I could cook up. Thanks a ton.

- Alex