bulletphysics / bullet3

Bullet Physics SDK: real-time collision detection and multi-physics simulation for VR, games, visual effects, robotics, machine learning etc.

Home Page:http://bulletphysics.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

btCollisionWorld::rayTest incredibly wrong/inaccurate

darksylinc opened this issue · comments

I was using btCollisionWorld::rayTest to test gun bullet (bullet as in projectiles, not the library) collisions against obstacles; when I noticed bullets were going through obstacles too often and from blatant places (i.e. "There's now way Bullet is missing those raycast collisions").

So I started logging data to reproduce the problem locally.

I have a static btBoxShape: center = (0, 2.024, -3.0) halfSize = 2.024000, 2.024000, 2.024000.

const btVector3 rayFrom( -2.027535, 1.323740, -4.894639 );
const btVector3 rayTo( 2.506727, 1.292858, -2.102663 );

btCollisionWorld::ClosestRayResultCallback closestResults( rayFrom, rayTo );
closestResults.m_collisionFilterGroup = 0xFFFFFFFF;
closestResults.m_collisionFilterMask = 0xFFFFFFFF;

m_world->rayTest( rayFromWorld, rayToWorld, resultCallback );

This fails despite being blatantly going through the box (Box is the obstacle, the spheres are to and from):

Test

I've defined USE_BRUTEFORCE_RAYBROADPHASE to debug this problem but the problem remains.

What's most amazing is that if I swap from and to, it reports a collision!

I googled around and found a forum post from 2010 mentioning this exact problem.

I can't post there because registering is disabled and my findings matches theirs.

Bottom line is; I'm seeing that rayTest is so wrong and misses so many cases it is downright unusable.

This needs at the very least to be documented, the function maybe even disabled; and at best fixed.

I'm sorry for being this harsh, but it is really broken. I will have to roll down my own versions.

OK I just looked at which version I'm using and it looks like I'm using a really old version of the library.

I will update it first (commit 688d750) today and if the problem is fixed I will close this issue.

I upgraded to Bullet 3.25 (that was surprisingly painless) commit 2c204c4 and I'm afraid the problems remain.

The following patch applied on top of that commit can repro the problem:

diff --git a/examples/BasicDemo/BasicExample.cpp b/examples/BasicDemo/BasicExample.cpp
index 67f670d07..21a7f3a3f 100644
--- a/examples/BasicDemo/BasicExample.cpp
+++ b/examples/BasicDemo/BasicExample.cpp
@@ -55,6 +55,7 @@ void BasicExample::initPhysics()
 	if (m_dynamicsWorld->getDebugDrawer())
 		m_dynamicsWorld->getDebugDrawer()->setDebugMode(btIDebugDraw::DBG_DrawWireframe + btIDebugDraw::DBG_DrawContactPoints);
 
+#if 0
 	///create a few basic rigid bodies
 	btBoxShape* groundShape = createBoxShape(btVector3(btScalar(50.), btScalar(50.), btScalar(50.)));
 
@@ -110,6 +111,41 @@ void BasicExample::initPhysics()
 			}
 		}
 	}
+#endif
+
+	btBoxShape* boxShape = createBoxShape(btVector3(btScalar(2.024000), btScalar(2.024000), btScalar(2.024000)));
+
+	btTransform startTransform;
+	startTransform.setIdentity();
+	startTransform.setOrigin(btVector3(0, 2.024, -3.0));
+
+	createRigidBody(0.0f, startTransform, boxShape);
+
+	{
+		const btVector3 rayFrom( 2.506727+3.0, 1.292858, -2.102663 );
+		const btVector3 rayTo( -2.027535, 1.323740, -4.894639 );
+
+		btCollisionWorld::ClosestRayResultCallback cb( rayFrom, rayTo );
+		cb.m_collisionFilterGroup = static_cast<int>( 0xFFFFFFFF );
+		cb.m_collisionFilterMask = static_cast<int>( 0xFFFFFFFF );
+
+		m_dynamicsWorld->rayTest( rayFrom, rayTo, cb );
+
+		assert( cb.hasHit() );
+	}
+
+	{
+		const btVector3 rayFrom( -2.027535, 1.323740, -4.894639 );
+		const btVector3 rayTo( 2.506727+3.0, 1.292858, -2.102663 );
+
+		btCollisionWorld::ClosestRayResultCallback cb( rayFrom, rayTo );
+		cb.m_collisionFilterGroup = static_cast<int>( 0xFFFFFFFF );
+		cb.m_collisionFilterMask = static_cast<int>( 0xFFFFFFFF );
+
+		m_dynamicsWorld->rayTest( rayFrom, rayTo, cb );
+
+		assert( cb.hasHit() );
+	}
 
 	m_guiHelper->autogenerateGraphicsObjects(m_dynamicsWorld);
 }

The first assert won't hit but the second one will despite being the exact same ray, just swapped.

You can make it even more extreme:

const btVector3 rayFrom( 5.506727, 1.292858, -2.102663 );
const btVector3 rayTo( -2.027535, 1.323740, -4.894639 );

vs 

const btVector3 rayFrom( -2.027535, 1.323740, -4.894639 );
const btVector3 rayTo( 5.506727, 1.292858, -2.102663 );

And the problem remains. This doesn't look like a simple floating point error. This is a huge error:

Screenshot_2023-05-07_17-44-24

We can see the ray goes a very long distance through the box yet one version returns true and the other false!

I tried boxShape->setMargin( 0.001 ); but the problem persists.

This may be a duplicate of #4286 except in this case the origin is very close yet not inside to the box (i.e. locally the point is at x = -2.027535 and would be considered inside the box if x >= -2.024 and x <= 2.024)

If anyone finds it useful, I derived from btDiscreteDynamicsWorld as CustomBulletWorld via CustomBulletWorld::rayTest2 to implement a new ray test that performs a more accurate Ray vs Box test and fallbacks to regular code for other tests.

I didn't want to change Bullet's code because I was unsure if it wouldn't break anything; because the current rayTest may be undesirable for end users; but depending on the use-case it has the desirable property that rays that start inside other objects can "escape" out of the object.

If someone needs a ray test that works even if the ray is spawned inside the shape, here's a solution.

I don't know if it compiles with BT_USE_SSE/BT_USE_NEON enabled.

CustomBulletWorld.h

#pragma once

#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h"

class CustomBulletWorld final : public btDiscreteDynamicsWorld
{
public:
	CustomBulletWorld( btDispatcher *dispatcher, btBroadphaseInterface *pairCache,
					  btConstraintSolver       *constraintSolver,
					  btCollisionConfiguration *collisionConfiguration );

	/** Replaces rayTest with a custom implementation that deals with some ray to shape (e.g. Box)
		intersectaions with more accuracy than the original.

		See https://github.com/bulletphysics/bullet3/issues/4464
	@remarks
		It can deal with compount shapes to RayResultCallback,
		but we won't report which child shape got hit.

		When a particular shape is not implemented, we fallback to the same code
		as btDiscreteDynamicsWorld rayTest().

		This only affects world ray tests, not ray tests made by ghosts.
	@param rayFromWorld
	@param rayToWorld
	@param resultCallback
	*/
	void rayTest2( const btVector3 &rayFromWorld, const btVector3 &rayToWorld,
				   RayResultCallback &resultCallback ) const;

	static void rayTestSingle2( const btTransform &rayFromTrans, const btTransform &rayToTrans,
								const btCollisionObject *collisionObject,
								const btCollisionShape  *collisionShape,
								const btTransform       &colObjWorldTransform,
								RayResultCallback       &resultCallback );
};

CustomBulletWorld.cpp

#include "CustomBulletWorld.h"

#include "BulletCollision/BroadphaseCollision/btDbvt.h"
#include "BulletCollision/CollisionShapes/btBoxShape.h"
#include "BulletCollision/CollisionShapes/btCompoundShape.h"
#include "BulletCollision/CollisionShapes/btConvexShape.h"

#include <algorithm>
#include <limits>

struct btSingleRayCallback2 : public btBroadphaseRayCallback
{
	btVector3 m_rayFromWorld;
	btVector3 m_rayToWorld;
	btTransform m_rayFromTrans;
	btTransform m_rayToTrans;
	btVector3 m_hitNormal;

	const CustomBulletWorld *m_world;
	btCollisionWorld::RayResultCallback &m_resultCallback;

	btSingleRayCallback2( const btVector3 &rayFromWorld, const btVector3 &rayToWorld,
						  const CustomBulletWorld *world,
						  btCollisionWorld::RayResultCallback &resultCallback ) :
		m_rayFromWorld( rayFromWorld ),
		m_rayToWorld( rayToWorld ),
		m_world( world ),
		m_resultCallback( resultCallback )
	{
		m_rayFromTrans.setIdentity();
		m_rayFromTrans.setOrigin( m_rayFromWorld );
		m_rayToTrans.setIdentity();
		m_rayToTrans.setOrigin( m_rayToWorld );

		btVector3 rayDir = ( rayToWorld - rayFromWorld );

		rayDir.normalize();
		/// what about division by zero? --> just set rayDirection[i] to INF/BT_LARGE_FLOAT
		m_rayDirectionInverse[0] =
			rayDir[0] == btScalar( 0.0 ) ? btScalar( BT_LARGE_FLOAT ) : btScalar( 1.0 ) / rayDir[0];
		m_rayDirectionInverse[1] =
			rayDir[1] == btScalar( 0.0 ) ? btScalar( BT_LARGE_FLOAT ) : btScalar( 1.0 ) / rayDir[1];
		m_rayDirectionInverse[2] =
			rayDir[2] == btScalar( 0.0 ) ? btScalar( BT_LARGE_FLOAT ) : btScalar( 1.0 ) / rayDir[2];
		m_signs[0] = m_rayDirectionInverse[0] < 0.0;
		m_signs[1] = m_rayDirectionInverse[1] < 0.0;
		m_signs[2] = m_rayDirectionInverse[2] < 0.0;

		m_lambda_max = rayDir.dot( m_rayToWorld - m_rayFromWorld );
	}

	bool process( const btBroadphaseProxy *proxy ) override;
};
//-----------------------------------------------------------------------------
bool btSingleRayCallback2::process( const btBroadphaseProxy *proxy )
{
	/// terminate further ray tests, once the closestHitFraction reached zero
	if( m_resultCallback.m_closestHitFraction <= btScalar( 0.f ) )
		return false;

	btCollisionObject *collisionObject = (btCollisionObject *)proxy->m_clientObject;

	// only perform raycast if filterMask matches
	if( m_resultCallback.needsCollision( collisionObject->getBroadphaseHandle() ) )
	{
		// RigidcollisionObject* collisionObject = ctrl->GetRigidcollisionObject();
		// btVector3 collisionObjectAabbMin,collisionObjectAabbMax;
#if 0
#	ifdef RECALCULATE_AABB
		btVector3 collisionObjectAabbMin,collisionObjectAabbMax;
		collisionObject->getCollisionShape()->getAabb(
				// collisionObject->getWorldTransform(),collisionObjectAabbMin,collisionObjectAabbMax);
#	else
		//getBroadphase()->getAabb(collisionObject->getBroadphaseHandle(),
		//  collisionObjectAabbMin,collisionObjectAabbMax);
		const btVector3& collisionObjectAabbMin = collisionObject->getBroadphaseHandle()->m_aabbMin;
		const btVector3& collisionObjectAabbMax = collisionObject->getBroadphaseHandle()->m_aabbMax;
#	endif
#endif
		// btScalar hitLambda = m_resultCallback.m_closestHitFraction;
		// culling already done by broadphase
		// if( btRayAabb( m_rayFromWorld, m_rayToWorld, collisionObjectAabbMin, collisionObjectAabbMax,
		//			   hitLambda, m_hitNormal ) )
		{
			m_world->rayTestSingle2( m_rayFromTrans, m_rayToTrans, collisionObject,
									 collisionObject->getCollisionShape(),
									 collisionObject->getWorldTransform(), m_resultCallback );
		}
	}
	return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CustomBulletWorld::CustomBulletWorld( btDispatcher *dispatcher, btBroadphaseInterface *pairCache,
									  btConstraintSolver *constraintSolver,
									  btCollisionConfiguration *collisionConfiguration ) :
	btDiscreteDynamicsWorld( dispatcher, pairCache, constraintSolver, collisionConfiguration )
{
}
//-----------------------------------------------------------------------------
void CustomBulletWorld::rayTest2( const btVector3 &rayFromWorld, const btVector3 &rayToWorld,
								  RayResultCallback &resultCallback ) const
{
	btSingleRayCallback2 rayCB( rayFromWorld, rayToWorld, this, resultCallback );

#ifndef USE_BRUTEFORCE_RAYBROADPHASE
	m_broadphasePairCache->rayTest( rayFromWorld, rayToWorld, rayCB );
#else
	for( int i = 0; i < this->getNumCollisionObjects(); i++ )
	{
		rayCB.process( m_collisionObjects[i]->getBroadphaseHandle() );
	}
#endif  // USE_BRUTEFORCE_RAYBROADPHASE
}
//-----------------------------------------------------------------------------
static inline bool testSegmentVsBox( const btTransform &rayFromTrans, const btTransform &rayToTrans,
									 const btBoxShape *box, const btTransform &colObjWorldTransform,
									 btVector3 &outNormal, btScalar &outFraction )
{
	const btVector3 localFrom = colObjWorldTransform.invXform( rayFromTrans.getOrigin() );
	const btVector3 localTo = colObjWorldTransform.invXform( rayToTrans.getOrigin() );

	const btVector3 rayDir = localTo - localFrom;

	const btVector3 halfSize = box->getHalfExtentsWithMargin();
	const btVector3 vMin = -halfSize;
	const btVector3 vMax = halfSize;

	btScalar minT = std::numeric_limits<btScalar>::max();
	btVector3 normal;

	const btVector3 tMin = ( vMin - localFrom ) / rayDir;
	const btVector3 tMax = ( vMax - localFrom ) / rayDir;

	// Check each face in turn Min + Max x, y & z
	for( size_t j = 0u; j < 3u; ++j )
	{
		if( tMin.m_floats[j] >= btScalar( 0.0 ) && tMin.m_floats[j] < minT )
		{
			// Check the point is actually inside the AABB
			btVector3 pt = ( localFrom + rayDir * tMin.m_floats[j] ).absolute();

			// Fix accuracy issues (this value will lay exactly in the extend,
			// but often is slightly beyond it, causing the test to fail)
			pt.m_floats[j] = 0.0f;

			if( pt.x() <= halfSize.x() && pt.y() <= halfSize.y() && pt.z() <= halfSize.z() )
			{
				minT = tMin.m_floats[j];
				normal.setZero();
				normal.m_floats[j] = btScalar( -1.0 );
			}
		}
		if( tMax.m_floats[j] >= btScalar( 0.0 ) && tMax.m_floats[j] < minT )
		{
			// Check the point is actually inside the AABB
			btVector3 pt = ( localFrom + rayDir * tMax.m_floats[j] ).absolute();

			// Fix accuracy issues (this value will lay exactly in the extend,
			// but often is slightly beyond it, causing the test to fail)
			pt.m_floats[j] = 0.0f;

			if( pt.x() <= halfSize.x() && pt.y() <= halfSize.y() && pt.z() <= halfSize.z() )
			{
				minT = tMax.m_floats[j];
				normal.setZero();
				normal.m_floats[j] = btScalar( -1.0 );
			}
		}
	}

	if( minT < btScalar( 0.0 ) || minT > btScalar( 1.0 ) )
		return false;

	outFraction = minT;
	outNormal = normal * colObjWorldTransform.getBasis();

	return true;
}
//-----------------------------------------------------------------------------
void CustomBulletWorld::rayTestSingle2( const btTransform &rayFromTrans, const btTransform &rayToTrans,
										const btCollisionObject *collisionObject,
										const btCollisionShape *collisionShape,
										const btTransform &colObjWorldTransform,
										RayResultCallback &resultCallback )
{
	if( collisionShape->isConvex() )
	{
		switch( collisionShape->getShapeType() )
		{
		case BOX_SHAPE_PROXYTYPE:
		{
			btVector3 normal;
			btScalar fraction;
			if( testSegmentVsBox( rayFromTrans, rayToTrans,
								  static_cast<const btBoxShape *>( collisionShape ),
								  colObjWorldTransform, normal, fraction ) )
			{
				if( fraction < resultCallback.m_closestHitFraction )
				{
					btCollisionWorld::LocalRayResult localRayResult( collisionObject, 0, normal,
																	 fraction );
					bool normalInWorldSpace = true;
					resultCallback.addSingleResult( localRayResult, normalInWorldSpace );
				}
			}
			break;
		}
		default:
		{
			// Fallback to Bullet's code
			btCollisionObjectWrapper collisionObjectWrap( 0, collisionShape, collisionObject,
														  colObjWorldTransform, -1, -1 );
			rayTestSingleInternal( rayFromTrans, rayToTrans, &collisionObjectWrap, resultCallback );
			break;
		}
		}
	}
	else
	{
		//	BT_PROFILE("rayTestCompound");
		if( collisionShape->isCompound() )
		{
			// Deal with compound objects because we may handle some of the children.
			struct RayTester final : btDbvt::ICollide
			{
				const btCollisionObject *m_collisionObject;
				const btCompoundShape *m_compoundShape;
				const btTransform &m_colObjWorldTransform;
				const btTransform &m_rayFromTrans;
				const btTransform &m_rayToTrans;
				RayResultCallback &m_resultCallback;

				RayTester( const btCollisionObject *collisionObject,
						   const btCompoundShape *compoundShape, const btTransform &colObjWorldTransform,
						   const btTransform &rayFromTrans, const btTransform &rayToTrans,
						   RayResultCallback &resultCallback ) :
					m_collisionObject( collisionObject ),
					m_compoundShape( compoundShape ),
					m_colObjWorldTransform( colObjWorldTransform ),
					m_rayFromTrans( rayFromTrans ),
					m_rayToTrans( rayToTrans ),
					m_resultCallback( resultCallback )
				{
				}

				void ProcessLeaf( int i )
				{
					const btCollisionShape *childCollisionShape = m_compoundShape->getChildShape( i );
					const btTransform &childTrans = m_compoundShape->getChildTransform( i );
					btTransform childWorldTrans = m_colObjWorldTransform * childTrans;
					rayTestSingle2( m_rayFromTrans, m_rayToTrans, m_collisionObject, childCollisionShape,
									childWorldTrans, m_resultCallback );
				}

				void Process( const btDbvtNode *leaf ) override { ProcessLeaf( leaf->dataAsInt ); }
			};

			const btCompoundShape *compoundShape =
				static_cast<const btCompoundShape *>( collisionShape );
			const btDbvt *dbvt = compoundShape->getDynamicAabbTree();

			RayTester rayCB( collisionObject, compoundShape, colObjWorldTransform, rayFromTrans,
							 rayToTrans, resultCallback );
#ifndef DISABLE_DBVT_COMPOUNDSHAPE_RAYCAST_ACCELERATION
			if( dbvt )
			{
				btVector3 localRayFrom = colObjWorldTransform.inverseTimes( rayFromTrans ).getOrigin();
				btVector3 localRayTo = colObjWorldTransform.inverseTimes( rayToTrans ).getOrigin();
				btDbvt::rayTest( dbvt->m_root, localRayFrom, localRayTo, rayCB );
			}
			else
#endif  // DISABLE_DBVT_COMPOUNDSHAPE_RAYCAST_ACCELERATION
			{
				for( int i = 0, n = compoundShape->getNumChildShapes(); i < n; ++i )
				{
					rayCB.ProcessLeaf( i );
				}
			}
		}
		else
		{
			// Fallback to Bullet's code
			btCollisionObjectWrapper collisionObjectWrap( 0, collisionShape, collisionObject,
														  colObjWorldTransform, -1, -1 );
			rayTestSingleInternal( rayFromTrans, rayToTrans, &collisionObjectWrap, resultCallback );
		}
	}
}

I tried your change to basicexample.cpp in bullet 2.89 (with double precision on) and can't reproduce your problem. I run the basic example from the example browser and both rays return true.
If I don't create the rigidbody I don't get a hit- as expected.

Then I tried the values from your first comment and it works too. I get the following hit point:
m_hitPointWorld.x(): -2.024
m_hitPointWorld.y(): 1.32372
m_hitPointWorld.z(): -4.89246

I can add that I've used the raytests extensively in the past and never had problems with them.

I'm using single precision, not double. No SIMD.

To be more specific:

/usr/bin/clang++-12 -DBT_USE_EGL -DUSE_GRAPHICAL_BENCHMARK -I/home/matias/Projects/bullet3/src -g -D_DEBUG -MD -MT src/BulletCollision/CMakeFiles/BulletCollision.dir/CollisionDispatch/btCollisionWorld.o -MF src/BulletCollision/CMakeFiles/BulletCollision.dir/CollisionDispatch/btCollisionWorld.o.d -o src/BulletCollision/CMakeFiles/BulletCollision.dir/CollisionDispatch/btCollisionWorld.o -c /home/matias/Projects/bullet3/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp

/usr/bin/clang++-12 -DB3_USE_STANDALONE_EXAMPLE -DBT_USE_EGL -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DUSE_GRAPHICAL_BENCHMARK -I/home/matias/Projects/bullet3/src -I/home/matias/Projects/bullet3/btgui -I/home/matias/Projects/bullet3/examples -I/home/matias/Projects/bullet3/examples/ThirdPartyLibs/glad -I/home/matias/Projects/bullet3/btgui/OpenGLWindow/GlewWindows -g -D_DEBUG -MD -MT examples/BasicDemo/CMakeFiles/AppBasicExampleGui.dir/BasicExample.o -MF examples/BasicDemo/CMakeFiles/AppBasicExampleGui.dir/BasicExample.o.d -o examples/BasicDemo/CMakeFiles/AppBasicExampleGui.dir/BasicExample.o -c /home/matias/Projects/bullet3/examples/BasicDemo/BasicExample.cpp

I can add that I've used the raytests extensively in the past and never had problems with them.

As I said before, this looks like a duplicate of #4286 where ray casts originating inside an shape are not reported as hit; and the difference between that ticket and mine is that although mine is originating outside the cube, it is so close to being inside that some floating point error could explain it (i.e. double vs single precision).

However turning double precision on is not a solution because rays originating inside the cube won't be reported and that's not acceptable for me.

I gave a lot of thinking to this issue, and the only solution is to modify the API to select the desired behavior:

  1. Ray casts originating inside the shape should be reported
    • This is useful when we need to know if the ray collides against something (e.g. in a game, whether a projectile hits any target)
    • It needs different code to handle this type of ray test (see my proposed solution for rays vs box)
  2. Ray casts originating inside the shape should not be reported
    • This is useful on certain simulations, because it allows objects to "escape" the inside of a shape.
    • Without this property, objects that rely on ray testing but for some reason originated inside a shape would never be able to escape the object they were encased in.
    • This is the current behavior because the ray tests were implemented as a sphere sweep test of with 0 radius

It's not a matter of fixing a bug, it's a matter of having very different goals.

For some applications, the current behavior is not a bug, it's a feature.

For double precision, you can try this patch instead:

diff --git a/examples/BasicDemo/BasicExample.cpp b/examples/BasicDemo/BasicExample.cpp
index 67f670d07..fb4cd3dc6 100644
--- a/examples/BasicDemo/BasicExample.cpp
+++ b/examples/BasicDemo/BasicExample.cpp
@@ -55,6 +55,7 @@ void BasicExample::initPhysics()
 	if (m_dynamicsWorld->getDebugDrawer())
 		m_dynamicsWorld->getDebugDrawer()->setDebugMode(btIDebugDraw::DBG_DrawWireframe + btIDebugDraw::DBG_DrawContactPoints);
 
+#if 0
 	///create a few basic rigid bodies
 	btBoxShape* groundShape = createBoxShape(btVector3(btScalar(50.), btScalar(50.), btScalar(50.)));
 
@@ -110,6 +111,43 @@ void BasicExample::initPhysics()
 			}
 		}
 	}
+#endif
+
+	btBoxShape* boxShape = createBoxShape(btVector3(btScalar(2.024000), btScalar(2.024000), btScalar(2.024000)));
+
+	boxShape->setMargin( 0.001 );
+
+	btTransform startTransform;
+	startTransform.setIdentity();
+	startTransform.setOrigin(btVector3(0, 2.024, 0.0));
+
+	createRigidBody(0.0f, startTransform, boxShape);
+
+	{
+		const btVector3 rayFrom( 5.506727, 1.292858, 1.102663 );
+		const btVector3 rayTo( -2.007535, 1.323740, -1.894639 );
+
+		btCollisionWorld::ClosestRayResultCallback cb( rayFrom, rayTo );
+		cb.m_collisionFilterGroup = static_cast<int>( 0xFFFFFFFF );
+		cb.m_collisionFilterMask = static_cast<int>( 0xFFFFFFFF );
+
+		m_dynamicsWorld->rayTest( rayFrom, rayTo, cb );
+
+		assert( cb.hasHit() );
+	}
+
+	{
+		const btVector3 rayFrom( -2.007535, 1.323740, -1.894639 );
+		const btVector3 rayTo( 5.506727, 1.292858, 1.102663 );
+
+		btCollisionWorld::ClosestRayResultCallback cb( rayFrom, rayTo );
+		cb.m_collisionFilterGroup = static_cast<int>( 0xFFFFFFFF );
+		cb.m_collisionFilterMask = static_cast<int>( 0xFFFFFFFF );
+
+		m_dynamicsWorld->rayTest( rayFrom, rayTo, cb );
+
+		assert( cb.hasHit() );
+	}
 
 	m_guiHelper->autogenerateGraphicsObjects(m_dynamicsWorld);
 }

You can see rayFrom & rayTo have identical values, but swapped. One hits, the other one misses.

I will repeat this again: in some scenarios this is a desirable property, but in other scenarios this is not.

With the new numbers I can confirm the issue in double precision. The first ray hits, the second doesn't.
To make it a bit more obvious I changed it to this:

`
btBoxShape* boxShape = createBoxShape(btVector3(btScalar(2.0), btScalar(2.0), btScalar(2.0)));
boxShape->setMargin(0.001);
btTransform startTransform;
startTransform.setIdentity();
startTransform.setOrigin(btVector3(0, 2.0, 0.0));
createRigidBody(0.0f, startTransform, boxShape);

const btVector3 Point1(200.0, 1, 1);
const btVector3 Point2(-2.00000001, 1, 1); //2nd ray doesn't hit
//const btVector3 Point2(-2.0000001, 1, 1); //this works

{
	btCollisionWorld::ClosestRayResultCallback cb(Point1, Point2);
	cb.m_collisionFilterGroup = static_cast<int>(0xFFFFFFFF);
	cb.m_collisionFilterMask = static_cast<int>(0xFFFFFFFF);
	m_dynamicsWorld->rayTest(Point1, Point2, cb);
	assert(cb.hasHit());
}

{
	btCollisionWorld::ClosestRayResultCallback cb(Point2, Point1);
	cb.m_collisionFilterGroup = static_cast<int>(0xFFFFFFFF);
	cb.m_collisionFilterMask = static_cast<int>(0xFFFFFFFF);
	m_dynamicsWorld->rayTest(Point2, Point1, cb);
	assert(cb.hasHit());
}`

With the ray only moving along the x axis the error is <0.0000001. It doesn't make use of the full double precision, so that could be improved.
I would say the issue title "rayTest incredibly wrong/inaccurate" is a bit over exaggerated.
Otherwise I agree there should be a ray that detects a hit if it starts inside an object. Either a separate ray or a setting that's off by default. In that case the floating point error would probably exist if the ray starts at the end of an object.

As a workaround you could use a ghostobject at the start of the ray to see if it collides with anything. Maybe a small sphere that you reuse for each gun that fires.

However turning double precision on is not a solution because rays originating inside the cube won't be reported and that's not acceptable for me. <

Regardless of this issue using double precision is recommended for bullet, unless you have a reason not to.

Bullet development is unfortunately slow compared to Ogre, I have 3 open PRs with trivial stuff, so I didn`t bother to contribute more. But give it a try, I would like to see the engine get better.