RayTest fails

Post Reply
paokakis
Posts: 29
Joined: Mon Jan 04, 2021 10:31 am

RayTest fails

Post by paokakis »

Hello to the group,

I am using bullet in an OpenGL application creating a 3D space shooter. At some point I want to ray cast from the middle of the screen to a rigid body on the screen. This works fine for rigid bodies with group 1 and mask 1. But I have some objects that don't collide with others set a group of 1 and mask of 0. On those objects the the ray misses. Here is the ray that I am casting :

Code: Select all

bool BphysicsManager::rayHasHit(glm::vec3& rayFromWorld, glm::vec3& rayToWorld, btVector3* hitPointWorld, btRigidBody** body)
{
	mMutex->lock();
	bool ret;
	rayFrom.setX(rayFromWorld.x);
	rayFrom.setY(rayFromWorld.y);
	rayFrom.setZ(rayFromWorld.z);

	rayTo.setX(rayToWorld.x);
	rayTo.setY(rayToWorld.y);
	rayTo.setZ(rayToWorld.z);

	btCollisionWorld::ClosestRayResultCallback rayCallback(rayFrom, rayTo);
	mpDynamicsWorld->rayTest(rayFrom, rayTo, rayCallback);

	if (rayCallback.m_collisionObject)
	{
		*body = (btRigidBody*)btRigidBody::upcast(rayCallback.m_collisionObject);
		*hitPointWorld = rayCallback.m_hitPointWorld;

		auto colShape = (*body)->getCollisionShape();
		if (dynamic_cast<btConcaveShape*>(colShape) != nullptr)
		{
			*body = nullptr;
		}
	}

	ret = rayCallback.hasHit();
	mMutex->unlock();

	return ret;
}
and here is the rigid body creation :

Code: Select all

btRigidBody* BphysicsManager::loadBody(btVector3& pLocation, float pSizeX, float pSizeY, float pSizeZ, btVector3& scale , float pDensity, void* userPtr, shape_type_t type, int group, int mask)
{
	mMutex->lock();

	btCollisionShape* shape;

	switch (type) {
	case SHAPE_BOX:
		shape = new btBoxShape(btVector3(pSizeX, pSizeY, pSizeZ));
		break;
	case SHAPE_SPHERE:
		shape = new btSphereShape(pSizeX / 2);
		break;
	case SHAPE_CAPSULE:
		shape = new btCapsuleShape(pSizeY, pSizeY / 4);
		break;
	case SHAPE_CYLINDER:
		shape = new btCylinderShape(btVector3(pSizeX / 2, pSizeY / 2, pSizeZ / 2));
		break;
	default:
		shape = new btSphereShape(pSizeX / 2);
		break;
	}

	shape->setMargin(0.001f);
	shape->setLocalScaling(scale);
	
	btTransform startTransform = btTransform();
	startTransform.setIdentity();
	startTransform.setOrigin(pLocation);	//put it to x,y,z coordinates	

	btAssert((!shape || shape->getShapeType() != INVALID_SHAPE_PROXYTYPE));

	//rigidbody is dynamic if and only if mass is non zero, otherwise static
	bool isDynamic = (pDensity != 0.f);

	btVector3 localInertia(0, 0, 0);
	if (isDynamic)
		shape->calculateLocalInertia(pDensity, localInertia);

	//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
#define USE_MOTIONSTATE 1
#ifdef USE_MOTIONSTATE
	btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
	vMotionStates.push_back(myMotionState);

	btRigidBody::btRigidBodyConstructionInfo cInfo(pDensity, myMotionState, shape, localInertia);

	btRigidBody* body = new btRigidBody(cInfo);
	body->setWorldTransform(startTransform);

#else
	btRigidBody* body = new btRigidBody(mass, 0, shape, localInertia);
	body->setWorldTransform(startTransform);
#endif//

	if (userPtr) body->setUserPointer(userPtr);

	vCollisionShapes.push_back(shape);
	vRigidBodies.push_back(body);

	body->setAngularFactor(1.f);
	body->setLinearVelocity(btVector3(0, 0, 0));
	body->setAngularVelocity(btVector3(0, 0, 0));
	body->setFriction(1.0);
	body->activate(true);
	body->setSleepingThresholds(0.f, 0.f);
	if (userPtr) body->setUserPointer(userPtr);

	mpDynamicsWorld->addRigidBody(body, group, mask);

	mMutex->unlock();

	return body;
}
Can someone identify some problem?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: RayTest fails

Post by drleviathan »

The collision groups and masks are used at the BroadPhase to submit overlap pairs to the NarrowPhase.

AABB's overlaps in the BroadPhase are submitted to the NarrowPhase iff each object collides with the other's group. In other words when: ObjectA.group & ObjectB.mask && ObjectB.group & ObjectA.mask.

If you don't submit a custom mask when adding an object to the world it receives a default value of btBroadphaseProxy::AllFilter whose value is -1 in unsigned context: e.g. all bits set.

When you use a zero mask then it collides with nothing, including ray traces...

... because btCollisionWorld::ClosestRayResultCallback derives from btCollisonWorld::RayResultCallback which has m_collisionFilterGroup and m_collisionFilterMask data members which default to btBroadphaseProxy::AllFilter and are used when checking for overlaps in the BroadPhase data structure.

You need to set your callback's group to some single bit and then be sure set that same bit in the mask of all objects which should be pickable by rays.
paokakis
Posts: 29
Joined: Mon Jan 04, 2021 10:31 am

Re: RayTest fails

Post by paokakis »

I used also other groups but no success... I tried for example group 4 with mask 4 but again I get no raytest results. Only when I use group 1 with mask 1 it works, ah also the default works (no groups and no mask specified).
You need to set your callback's group to some single bit and then be sure set that same bit in the mask of all objects which should be pickable by rays.
How can I set my callback's group to something different?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: RayTest fails

Post by drleviathan »

Check out the ctor implementation of RayResultCallback, from which ClosestRayHitCallback derives:

Code: Select all

                RayResultCallback()
                        : m_closestHitFraction(btScalar(1.)),
                          m_collisionObject(0),
                          m_collisionFilterGroup(btBroadphaseProxy::DefaultFilter),
                          m_collisionFilterMask(btBroadphaseProxy::AllFilter),
                          //@BP Mod
                          m_flags(0)
                {         
                }
You can see the default group and mask being set. If you track down where btBroadphaseProxy::DefaultFilter and btBroadphaseProxy::AllFilter are defined you'll learn their values are 1 and -1 respectively (of course, interpreted as unsigned). You can set those data members to whatever you want after instantiation: they are public because RayResultCallback is a C++ struct, which is just a class but with all methods and members defaulting to public.
paokakis
Posts: 29
Joined: Mon Jan 04, 2021 10:31 am

Re: RayTest fails

Post by paokakis »

Thank you very much for your support. I was able to solve my problem this way :

Code: Select all

btCollisionWorld::ClosestRayResultCallback rayCallback(rayFrom, rayTo);
rayCallback.m_collisionFilterGroup = 0b1111111;
rayCallback.m_collisionFilterMask = 0b1111111;
mpDynamicsWorld->rayTest(rayFrom, rayTo, rayCallback);
In case it helps someone...
Post Reply