Page 1 of 1

how to avoid Ghost Objects with rayTest()

Posted: Fri Jul 13, 2018 2:38 am
by PcChip

I've been reading through documentation and forum posts, and am having trouble figuring out how to do this:

some of my units (turrets), have a cylinderShape ghost object around them that I'm using as a trigger to start targeting enemies inside of it

This works perfectly, however now when I do a rayTest() for the user right-clicking on the terrain, instead of the terrain I get the ghost object as the result

Is there a way to just ignore ghost objects in rayTest() ?

FYI I've tried setting the ghost object's collision filter group to "sensorTrigger" but then it started detecting everything (even mass 0) as a hit!

This is how I'm doing it:

Code: Select all

	btCollisionWorld::ClosestRayResultCallback RayCallback(
		btVector3(out_origin.x, out_origin.y, out_origin.z),
		btVector3(out_end.x, out_end.y, out_end.z)

		btVector3(out_origin.x, out_origin.y, out_origin.z),
		btVector3(out_end.x, out_end.y, out_end.z),

	if (RayCallback.hasHit()) {
		//std::cout << "mesh " << (int)RayCallback.m_collisionObject->getUserPointer();
		mouseRaycastHasHit = true;
		btVector3 hitPointWorld = RayCallback.m_hitPointWorld;
		mouseHitPointWorld = glm::vec3(hitPointWorld.getX(), hitPointWorld.getY(), hitPointWorld.getZ());
		mouseHitObjectUserIndex1 = (int)RayCallback.m_collisionObject->getUserIndex();
		mouseHitObjectUserIndex2 = (int)RayCallback.m_collisionObject->getUserIndex2();

	else {
		mouseRaycastHasHit = false;
thanks for any help you can provide :)

Re: how to avoid Ghost Objects with rayTest()

Posted: Sun Jul 15, 2018 12:36 pm
by drleviathan
You need to also set the collision group and mask correctly on the btCollisionWorld::ClosestRayResultCallback. The default ctor for that struct has these lines in it:

Code: Select all

Those are public data members so you can set them directly. Before submitting the callback to the rayTest() something like this should prevent it from colliding with groupA and groupB:

Code: Select all

            RayCallback.m_collsionFilterMask &= ~(groupA & groupB);

Re: how to avoid Ghost Objects with rayTest()

Posted: Mon Jul 16, 2018 2:05 am
by PcChip
Thanks for the help - this makes sense

-old post removed-

Edit: with your help I was able to get rayTest to avoid the trigger ghost object, but now the ghost object is detecting the terrain in its collision manifold! (wasting CPU cycles)

any tips on how I can avoid this?

this is how I'm doing it now when spawning a turret's ghost trigger volume:

Code: Select all

dynamicsWorld->addCollisionObject(myGhostObject, btBroadphaseProxy::SensorTrigger);
when doing the rayTest and trying to avoid ghost objects:

Code: Select all

	RayCallback.m_collisionFilterMask &= ~btBroadphaseProxy::SensorTrigger;

however the manifold is full of hits with the terrain =(

Re: how to avoid Ghost Objects with rayTest()

Posted: Mon Jul 16, 2018 4:16 pm
by drleviathan
To make the ghost not detect the terrain you need to set the collision group and mask correctly. The simplest case would be: terrain is in a single group (GroupA) and the ghost is also in a single group (GroupB), and you would clear the GroupA bit on the ghost's mask. As long as one of the objects' masks is missing the bit of the other then the broadphase won't track overlaps between the two objects.

You don't have to use the default groups defined in Bullet:

Code: Select all

            DefaultFilter = 1,
            StaticFilter = 2,
            KinematicFilter = 4,
            DebrisFilter = 8,
            SensorTrigger = 16,
            CharacterFilter = 32,
You can ignore these and define your own.

Re: how to avoid Ghost Objects with rayTest()

Posted: Tue Jul 17, 2018 12:08 am
by PcChip
okay thanks, I'll try to figure this out

two questions:

- does this mean I now have to set collision groups on everything? never used them before and was hoping for an easy way to do what I want without them. Why is this only happening now that I set the ghost object to be a sensortrigger?

- is there some guide that explains the difference between groups, filters, masks, and flags? and a tutorial on how to use all of them?

thanks again for your help

Re: how to avoid Ghost Objects with rayTest()

Posted: Tue Jul 17, 2018 12:54 am
by drleviathan
You only need collision groups if you're doing fancy stuff. Using ghosts for radar sensors and ray-picking for object selection would count as "fancy stuff".

However, there is another reason to use groups: collision optimizations. The groups+masks are checked on each new overlap in the broadphase. If the two AABB proxies should not collide according to their groups+masks then the broadphase never reports the overlapping pair to the narrowphase. In short: an early cheap check can avoid more expensive later checks.

I don't know why this is happening now. I have insufficient info to answer that question.

I don't know of a good tutorial but I'm sure there is one somewhere in these forums. Understanding of groups and masks didn't click for me right away, but now it seems simple enough. I would summarize it with this fact: two overlapping broadphase proxies are filtered out (don't collide) if: !(maskA & groupB) || !(maskB & groupA). That is, if either (group & mask) combination evaluates to zero, then the broadphase proxies don't "touch".

One way to go for your game be to define the following groups and masks:

Code: Select all

    terrainGroup = 0x01
    unitGroup = 0x02
    rayGroup = 0x04
    ghostGroup = 0x08

    terrainMask = unitGroup
    unitMask = unitGroup | terrainGroup
    clickableUnitMask = unitGroup | terrainGroup | rayGroup
    rayMask = rayGroup
    ghostMask = unitGroup
Terrain would have terrainGroup + terrainMask. If you want the terrain to be clickable then add the rayGroup bit to its mask.

Units would have unitGroup and either unitMask or clickableUnitMask, depending on if you should be able to click on it or not.

Rays would have rayGroup and rayMask. They only collide against things in their own group (e.g. any units set as "clickable").

Ghosts would have ghostGroup and ghostMask.

Re: how to avoid Ghost Objects with rayTest()

Posted: Tue Jul 17, 2018 1:00 am
by PcChip
thanks for the mini-tutorial, it's hard to find good understandable info on this

I think I've managed to sort it out for now but I will certainly add your example into the comments of my code for later use (I know I'll be back to it soon) - as a point of reference this is my simple little game:

It appears to be working as expected now when I do the following:

Terrain Rigid Body:

Code: Select all

dynamicsWorld->addRigidBody(myRigidBody, btBroadphaseProxy::StaticFilter, ~btBroadphaseProxy::SensorTrigger);

Turret Ghost Object (radar/sensor)

Code: Select all

dynamicsWorld->addCollisionObject(myGhostObject, btBroadphaseProxy::SensorTrigger, ~(btBroadphaseProxy::SensorTrigger | btBroadphaseProxy::StaticFilter));

it also appears to be running smoother as well, less CPU wasted on narrowphase I'm hoping

Thanks again!
(and hopefully someone down the line can use this example when trying to learn groups)

Re: how to avoid Ghost Objects with rayTest()

Posted: Thu Jul 19, 2018 2:34 am
by PcChip

above wasn't working so I gave it another shot using your suggestion + the tutorial on

this is what I'm doing:

Code: Select all

enum collisionGroups //
	COL_NOTHING   =     0,  //<Collide with nothing
	COL_GROUND    = BIT(0), //<Collide with ground
	COL_BUILDINGS = BIT(1), //<Collide with buildings
	COL_GHOSTS    = BIT(2), //<Collide with ghost objects
	COL_UNITS     = BIT(3), //<Collide with units
	COL_RAYCASTS  = BIT(4)  //<Collide with Raycasts

enum collisionMasks
	terrainCollidesWith   = COL_UNITS | COL_RAYCASTS,
	buildingsCollidesWith = COL_UNITS | COL_RAYCASTS,
	ghostsCollidesWith    = COL_UNITS,
	rayCastCollidesWith   = COL_GROUND | COL_UNITS  | COL_BUILDINGS
now strangely everything is working perfectly except the ghost objects - they're not registering the units in their manifold, and they ARE registering other ghost objects... hmmm

Edit: Back answering my own question for future people

The problem was that when coming up with the "detection" code using ghost objects, I found that the ghost object was always in body0, and the unit detected was always in body1, so I built the detection code around that idea. The reality is that objects added to the simulation world after the ghost object were in body1, however objects added into the simulation *before* the ghost were coming up in body0

this is my detection code that my turrets use now:

Code: Select all

bool managerBase::findAnyTarget(btPairCachingGhostObject* myGhostObject, int &index1, int &index2)
	btManifoldArray manifoldArray;
	btBroadphasePairArray& pairArray = myGhostObject->getOverlappingPairCache()->getOverlappingPairArray();
	int numPairs = pairArray.size();

	for (int k = 0; k < numPairs; ++k)
		const btBroadphasePair& pair = pairArray[k];
		btBroadphasePair* collisionPair = world->physicsModule->dynamicsWorld->getPairCache()->findPair(pair.m_pProxy0, pair.m_pProxy1);
		if (!collisionPair) continue;
		if (collisionPair->m_algorithm)

		for (int j = 0; j < manifoldArray.size(); j++)
			btPersistentManifold* manifold = manifoldArray[j];
			for (int p = 0; p < manifold->getNumContacts(); ++p)
				const btManifoldPoint& pt = manifold->getContactPoint(p);

				if (pt.getDistance()<0.f)
					// Object is inside ghost's shape at this manifold point
					// process shape overlap here
					int test1 = manifold->getBody1()->getUserIndex();
					int test2 = manifold->getBody1()->getUserIndex2();

					int test3 = manifold->getBody0()->getUserIndex();
					int test4 = manifold->getBody0()->getUserIndex2();
					if (test1 >= 0 && test2 >= 0) //found something that's not ground or ghost
						index1 = test1;
						index2 = test2;
						//std::cout << "Target found: " << test1 << ":" << test2 << "\n";
						return true;
					else if (test3 >= 0 && test4 >= 0)//found something that's not ground or ghost
						index1 = test3;
						index2 = test4;
						//std::cout << "Target found: " << test3 << ":" << test4 << "\n";
						return true;

						//std::cout << "invalid Target found: myGhostObject: " << myGhostObject << " body0: " << test3 << ":" << test4 << " body1: " << test1 << ":" << test2 << "\n";

					// Object is in ghost's aabb but not inside its shape at this manifold point
					// process aabb overlap here

	return false; //didn't find anything