convexSweepTest problem

DragonGeo2
Posts: 16
Joined: Sat Dec 18, 2010 1:30 am

convexSweepTest problem

Post by DragonGeo2 »

My problem is that I want to convert my working trace-based character controller from using raycasts (and the character having zero size) to using convex sweeps (and the character being represented as an AABB). But I am running into trouble with this method as it is causing my character controller to jump and glitch out and clip through objects and I'm not sure why. The character controller code is more or less a modified version of the Quake3 PMOVE code. If you are exceptionally interested in my modified PMOVE source code, you can find a copy of its latest version here. Take a look at the differences between my old and new trace functions and see what I mean.

Both the old and new code are called from this tracewrapper, which sits as the code "between" the Bullet physics code and the PMOVE code:

Code: Select all

void tracewrapper(traceResults* const results, const Position3D& start, const Position3D& end, const Position3D& BBHalfExtents, const Rotation3D& rotation, const Object* const traceobj)
{
	NewPhysTraceResults out;

	// Both start and end are positions in worldspace that reference the center of the trace object
	const bool hasHit = NewPhysicsTrace(&out, start, end, BBHalfExtents, rotation, traceobj);

	if (out.fraction < 0.001f)
		results->startsolid = true;
	else
		results->startsolid = false;

	results->allsolid = false;

	if (!hasHit)
	{
		results->endpos = end;
		results->planenormal = Position3D(0.0f, 1.0f, 0.0f); // Just to set it to some known value, why not make it the up vector
		results->entityNum = ENTITYNUM_NONE;
		results->fraction = 1.0f;
	}
	else
	{
		results->fraction = out.fraction;
		results->planenormal = out.hitNormal;
		bprintf("Start: (%f, %f, %f) End: (%f, %f, %f) HitNormal: (%f, %f, %f) Fraction: %f Hitpos: (%f, %f, %f)\n", 
			start.xPos, start.yPos, start.zPos,
			end.xPos, end.yPos, end.zPos,
			results->planenormal.xPos, results->planenormal.yPos, results->planenormal.zPos, results->fraction,
			results->endpos.xPos, results->endpos.yPos, results->endpos.zPos);

		// Override the Bullet hit position with one computed from the fraction and the ray direction
		// because Bullet's hit position will not necessarily lie on the cast line (unless
		// using the old ray-based trace code):
		results->endpos = (out.endPos - start) * results->fraction;
		results->endpos += start;
		results->entityNum = ENTITYNUM_WORLD;
	}
}
Old (working) trace function:

Code: Select all

const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Position3D& start, const Position3D& end, 
	const Position3D& BBHalfExtents, const Rotation3D& rotation, const Object* const traceobj)
{
	// Note that the ray-based version doesn't do anything with the shape it constructs
	btBoxShape newshape(btVector3(BBHalfExtents.xPos, BBHalfExtents.yPos, BBHalfExtents.zPos) );
	btTransform from, to;
	from.setIdentity();
	from.setOrigin(btVector3(start.xPos, start.yPos, start.zPos) );
	from.setRotation(btQuaternion(rotation.yRot, rotation.xRot, rotation.zRot) );

	to.setIdentity();
	to.setOrigin(btVector3(end.xPos, end.yPos, end.zPos) );
	to.setRotation(btQuaternion(rotation.yRot, rotation.xRot, rotation.zRot) );

	btCollisionWorld::ClosestRayResultCallback newRayTraceCallback(btVector3(start.xPos, start.yPos, start.zPos), btVector3(end.xPos, end.yPos, end.zPos) );

	m_dynamicsWorld->rayTest(btVector3(start.xPos, start.yPos, start.zPos), btVector3(end.xPos, end.yPos, end.zPos), newRayTraceCallback);

	out->fraction = newRayTraceCallback.m_closestHitFraction;

	out->hitNormal.xPos = newRayTraceCallback.m_hitNormalWorld.x();
	out->hitNormal.yPos = newRayTraceCallback.m_hitNormalWorld.y();
	out->hitNormal.zPos = newRayTraceCallback.m_hitNormalWorld.z();

	out->endPos.xPos = newRayTraceCallback.m_hitPointWorld.x();
	out->endPos.yPos = newRayTraceCallback.m_hitPointWorld.y();
	out->endPos.zPos = newRayTraceCallback.m_hitPointWorld.z();

	return out->fraction < 1.0f; // This is the same as returning newRayTraceCallback.hasHit()
}
New (glitchy and almost never working) trace function:

Code: Select all

const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Position3D& start, const Position3D& end, 
	const Position3D& BBHalfExtents, const Rotation3D& rotation, const Object* const traceobj)
{
	btBoxShape newshape(btVector3(BBHalfExtents.xPos, BBHalfExtents.yPos, BBHalfExtents.zPos) );
	btTransform from, to;
	from.setIdentity();
	from.setOrigin(btVector3(start.xPos, start.yPos, start.zPos) );
	from.setRotation(btQuaternion(rotation.yRot, rotation.xRot, rotation.zRot) );

	to.setIdentity();
	to.setOrigin(btVector3(end.xPos, end.yPos, end.zPos) );
	to.setRotation(btQuaternion(rotation.yRot, rotation.xRot, rotation.zRot) );

	btCollisionWorld::ClosestConvexResultCallback
		newTraceCallback(btVector3(start.xPos, start.yPos, start.zPos), btVector3(end.xPos, end.yPos, end.zPos) );

	m_dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback);

	out->fraction = newTraceCallback.m_closestHitFraction;

	out->hitNormal.xPos = newTraceCallback.m_hitNormalWorld.x();
	out->hitNormal.yPos = newTraceCallback.m_hitNormalWorld.y();
	out->hitNormal.zPos = newTraceCallback.m_hitNormalWorld.z();

	out->endPos.xPos = newTraceCallback.m_hitPointWorld.x();
	out->endPos.yPos = newTraceCallback.m_hitPointWorld.y();
	out->endPos.zPos = newTraceCallback.m_hitPointWorld.z();

	return out->fraction < 1.0f; // This is the same as returning newTraceCallback.hasHit()
}
There's barely any difference there, does convexSweepTest return different units than rayTest does? Am I using the convexSweepTest function correctly? Does anybody have any suggestions for things I should try to debug?

If you want to see a video of the old (ray-based) character controller in action, you can see that here, but note that this is not a product advertisement, I'm looking for help on this problem!
DragonGeo2
Posts: 16
Joined: Sat Dec 18, 2010 1:30 am

Re: convexSweepTest problem

Post by DragonGeo2 »

I've solved my own problem :P

As it turns out, Bullet's hit positions are returned in "a different space" (but not really) than the input points. The input points (start and end points of the cast) are in worldspace and refer to the object's origin position at the beginning and end of the sweep. However, the hit points returned by the function (in the callbacks) are just in worldspace, they have nothing to do with the position of the swept object's origin. I'm not going to post the fixed code here, but the source code will be updated on my SVN on Sourceforge.