Code/HumanoidRetargeter/Core/Formats/Mmd/Bullet/BulletCollision/CollisionWorld.cs

CollisionWorld, a core part of the Bullet collision system port. Manages collision objects, broadphase pair cache and dispatcher, updates AABBs, performs raycasts, convex sweeps, debug drawing and discrete collision detection.

Native InteropFile Access
/*
 * C# / XNA  port of Bullet (c) 2011 Mark Neale <[email protected]>
 *
 * Bullet Continuous Collision Detection and Physics Library
 * Copyright (c) 2003-2008 Erwin Coumans  http://www.bulletphysics.com/
 *
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the authors be held liable for any damages arising from
 * the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */
// Adapted from BulletXNA 73b2e1b9: namespace isolation, no XNA rendering, one type per file.
#nullable disable
#pragma warning disable CS1591, CS0168, CS0219, CS0414, CS0660, CS0661, CS0659, CS0693, CS8981, CS0108, CS0114
using System.Diagnostics;
using HumanoidRetargeter.Core.Formats.Mmd.Bullet.LinearMath;
using HumanoidRetargeter.Core.Formats.Mmd.Bullet.BulletDynamics;
using System;

namespace HumanoidRetargeter.Core.Formats.Mmd.Bullet.BulletCollision;

internal class CollisionWorld
{
	//this constructor doesn't own the dispatcher and paircache/broadphase
	public CollisionWorld(IDispatcher dispatcher, IBroadphaseInterface broadphasePairCache, ICollisionConfiguration collisionConfiguration)
	{
		m_dispatcher1 = dispatcher;
		m_broadphasePairCache = broadphasePairCache;
		m_collisionObjects = new ObjectArray<CollisionObject>();
		m_dispatchInfo = new DispatcherInfo();
		m_forceUpdateAllAabbs = true;
	}

	public virtual void Cleanup()
	{
		foreach (CollisionObject collisionObject in m_collisionObjects)
		{
			BroadphaseProxy bp = collisionObject.GetBroadphaseHandle();
			if (bp != null)
			{
				//
				// only clear the cached algorithms
				//
				if (GetBroadphase().GetOverlappingPairCache() != null)
				{
					GetBroadphase().GetOverlappingPairCache().CleanProxyFromPairs(bp, m_dispatcher1);
				}

				GetBroadphase().DestroyProxy(bp, m_dispatcher1);
				collisionObject.SetBroadphaseHandle(null);
			}
		}
	}

	public void SetBroadphase(IBroadphaseInterface pairCache)
	{
		m_broadphasePairCache = pairCache;
	}

	public IBroadphaseInterface GetBroadphase()
	{
		return m_broadphasePairCache;
	}

	public IOverlappingPairCache GetPairCache()
	{
		return m_broadphasePairCache.GetOverlappingPairCache();
	}

	public IDispatcher GetDispatcher()
	{
		return m_dispatcher1;
	}

	public void UpdateSingleAabb(CollisionObject colObj)
	{
		IndexedVector3 minAabb;
		IndexedVector3 maxAabb;
		IndexedMatrix wt = colObj.GetWorldTransform();
		colObj.GetCollisionShape().GetAabb(ref wt, out minAabb, out maxAabb);
		//need to increase the aabb for contact thresholds
		IndexedVector3 contactThreshold = new IndexedVector3(BulletGlobals.gContactBreakingThreshold);
		minAabb -= contactThreshold;
		maxAabb += contactThreshold;
		if (GetDispatchInfo().m_useContinuous && colObj.GetInternalType() == CollisionObjectTypes.CO_RIGID_BODY && !colObj.IsStaticOrKinematicObject())
		{
			IndexedVector3 minAabb2, maxAabb2;
			colObj.GetCollisionShape().GetAabb(colObj.GetInterpolationWorldTransform(), out minAabb2, out maxAabb2);
			minAabb2 -= contactThreshold;
			maxAabb2 += contactThreshold;
			MathUtil.VectorMin(ref minAabb2, ref minAabb);
			MathUtil.VectorMax(ref maxAabb2, ref maxAabb);
		}

		IBroadphaseInterface bp = m_broadphasePairCache as IBroadphaseInterface;
		//moving objects should be moderately sized, probably something wrong if not
		if (colObj.IsStaticObject() || ((maxAabb - minAabb).LengthSquared() < 1e12f))
		{
			bp.SetAabb(colObj.GetBroadphaseHandle(), ref minAabb, ref maxAabb, m_dispatcher1);
		}
		else
		{
			//something went wrong, investigate
			//this assert is unwanted in 3D modelers (danger of loosing work)
			colObj.SetActivationState(ActivationState.DISABLE_SIMULATION);
			//static bool reportMe = true;
			bool reportMe = true;
			if (reportMe && m_debugDrawer != null)
			{
				reportMe = false;
				m_debugDrawer.ReportErrorWarning("Overflow in AABB, object removed from simulation");
				m_debugDrawer.ReportErrorWarning("If you can reproduce this, please email [email protected]\n");
				m_debugDrawer.ReportErrorWarning("Please include above information, your Platform, version of OS.\n");
				m_debugDrawer.ReportErrorWarning("Thanks.\n");
			}
		}
	}

	public virtual void UpdateAabbs()
	{
		BulletGlobals.StartProfile("updateAabbs");
		//IndexedMatrix predictedTrans = new IndexedMatrix();
		int count = m_collisionObjects.Count;
		for (int i = 0; i < count; i++)
		{
			CollisionObject colObj = m_collisionObjects[i];
			//only update aabb of active objects
			if (m_forceUpdateAllAabbs || colObj.IsActive())
			{
				UpdateSingleAabb(colObj);
			}
		}

		BulletGlobals.StopProfile();
	}

	public virtual void SetDebugDrawer(IDebugDraw debugDrawer)
	{
		m_debugDrawer = debugDrawer;
		BulletGlobals.gDebugDraw = debugDrawer;
	}

	public virtual IDebugDraw GetDebugDrawer()
	{
		return m_debugDrawer;
	}

	public virtual void DebugDrawWorld()
	{
		if (GetDebugDrawer() != null && ((GetDebugDrawer().GetDebugMode() & DebugDrawModes.DBG_DrawContactPoints) != 0))
		{
			int numManifolds = GetDispatcher().GetNumManifolds();
			IndexedVector3 color = new IndexedVector3(1, 1, 0.5f);
			for (int i = 0; i < numManifolds; i++)
			{
				PersistentManifold contactManifold = GetDispatcher().GetManifoldByIndexInternal(i);
				//btCollisionObject* obA = static_cast<btCollisionObject*>(contactManifold.getBody0());
				//btCollisionObject* obB = static_cast<btCollisionObject*>(contactManifold.getBody1());
				int numContacts = contactManifold.GetNumContacts();
				for (int j = 0; j < numContacts; j++)
				{
					ManifoldPoint cp = contactManifold.GetContactPoint(j);
					GetDebugDrawer().DrawContactPoint(cp.GetPositionWorldOnB(), cp.GetNormalWorldOnB(), cp.GetDistance(), cp.GetLifeTime(), color);
				}
			}
		}

		if (GetDebugDrawer() != null)
		{
			DebugDrawModes debugMode = GetDebugDrawer().GetDebugMode();
			bool wireFrame = (debugMode & DebugDrawModes.DBG_DrawWireframe) != 0;
			bool aabb = (debugMode & DebugDrawModes.DBG_DrawAabb) != 0;
			if (wireFrame || aabb)
			{
				int length = m_collisionObjects.Count;
				for (int i = 0; i < length; ++i)
				{
					CollisionObject colObj = m_collisionObjects[i];
					if (wireFrame)
					{
						IndexedVector3 color = new IndexedVector3(255, 255, 255);
						switch (colObj.GetActivationState())
						{
							case ActivationState.ACTIVE_TAG:
							{
								color = new IndexedVector3(255, 255, 255);
								break;
							}

							case ActivationState.ISLAND_SLEEPING:
							{
								color = new IndexedVector3(0, 255, 0);
								break;
							}

							case ActivationState.WANTS_DEACTIVATION:
							{
								color = new IndexedVector3(0, 255, 255);
								break;
							}

							case ActivationState.DISABLE_DEACTIVATION:
							{
								color = new IndexedVector3(255, 0, 0);
								break;
							}

							case ActivationState.DISABLE_SIMULATION:
							{
								color = new IndexedVector3(255, 255, 0);
								break;
							}

							default:
							{
								color = new IndexedVector3(255, 0, 0);
								break;
							}
						};
						IndexedMatrix transform = colObj.GetWorldTransform();
						DebugDrawObject(ref transform, colObj.GetCollisionShape(), ref color);
					}

					if (aabb)
					{
						IndexedVector3 minAabb;
						IndexedVector3 maxAabb;
						IndexedVector3 colorvec = new IndexedVector3(1, 0, 0);
						colObj.GetCollisionShape().GetAabb(colObj.GetWorldTransform(), out minAabb, out maxAabb);
						IndexedVector3 contactThreshold = new IndexedVector3(BulletGlobals.gContactBreakingThreshold);
						minAabb -= contactThreshold;
						maxAabb += contactThreshold;
						IndexedVector3 minAabb2, maxAabb2;
						if (GetDispatchInfo().m_useContinuous && colObj.GetInternalType() == CollisionObjectTypes.CO_RIGID_BODY && !colObj.IsStaticOrKinematicObject())
						{
							IndexedMatrix m = colObj.GetInterpolationWorldTransform();
							//(colObj as RigidBody).GetMotionState().GetWorldTransform(out m);
							colObj.GetCollisionShape().GetAabb(m, out minAabb2, out maxAabb2);
							minAabb2 -= contactThreshold;
							maxAabb2 += contactThreshold;
							MathUtil.VectorMin(ref minAabb2, ref minAabb);
							MathUtil.VectorMax(ref maxAabb2, ref maxAabb);
						}

						m_debugDrawer.DrawAabb(ref minAabb, ref maxAabb, ref colorvec);
					}
				}
			}
		}
	}

	public virtual void DebugDrawObject(ref IndexedMatrix worldTransform, CollisionShape shape, ref IndexedVector3 color)
	{
		// Draw a small simplex at the center of the object
		GetDebugDrawer().DrawTransform(ref worldTransform, 1.0f);
		switch (shape.GetShapeType())
		{
			case BroadphaseNativeTypes.COMPOUND_SHAPE_PROXYTYPE:
			{
				CompoundShape compoundShape = (CompoundShape)shape;
				for (int i = compoundShape.GetNumChildShapes() - 1; i >= 0; i--)
				{
					IndexedMatrix childTrans = compoundShape.GetChildTransform(i);
					CollisionShape colShape = compoundShape.GetChildShape(i);
					IndexedMatrix temp = worldTransform * childTrans;
					DebugDrawObject(ref temp, colShape, ref color);
				}

				break;
			}

			case (BroadphaseNativeTypes.BOX_SHAPE_PROXYTYPE):
			{
				BoxShape boxShape = shape as BoxShape;
				IndexedVector3 halfExtents = boxShape.GetHalfExtentsWithMargin();
				IndexedVector3 negHalfExtents = -halfExtents;
				GetDebugDrawer().DrawBox(ref negHalfExtents, ref halfExtents, ref worldTransform, ref color);
				break;
			}

			case BroadphaseNativeTypes.SPHERE_SHAPE_PROXYTYPE:
			{
				SphereShape sphereShape = shape as SphereShape;
				float radius = sphereShape.GetMargin(); //radius doesn't include the margin, so draw with margin
				GetDebugDrawer().DrawSphere(radius, ref worldTransform, ref color);
				break;
			}

			case BroadphaseNativeTypes.MULTI_SPHERE_SHAPE_PROXYTYPE:
			{
				MultiSphereShape multiSphereShape = (MultiSphereShape)shape;
				for (int i = multiSphereShape.GetSphereCount() - 1; i >= 0; i--)
				{
					IndexedMatrix childTransform = worldTransform;
					childTransform._origin += multiSphereShape.GetSpherePosition(i);
					GetDebugDrawer().DrawSphere(multiSphereShape.GetSphereRadius(i), ref childTransform, ref color);
				}

				break;
			}

			case BroadphaseNativeTypes.CAPSULE_SHAPE_PROXYTYPE:
			{
				CapsuleShape capsuleShape = shape as CapsuleShape;
				float radius = capsuleShape.GetRadius();
				float halfHeight = capsuleShape.GetHalfHeight();
				int upAxis = capsuleShape.GetUpAxis();
				GetDebugDrawer().DrawCapsule(radius, halfHeight, upAxis, ref worldTransform, ref color);
				break;
			}

			case BroadphaseNativeTypes.CONE_SHAPE_PROXYTYPE:
			{
				ConeShape coneShape = (ConeShape)shape;
				float radius = coneShape.GetRadius(); //+coneShape->getMargin();
				float height = coneShape.GetHeight(); //+coneShape->getMargin();
				int upAxis = coneShape.GetConeUpIndex();
				GetDebugDrawer().DrawCone(radius, height, upAxis, ref worldTransform, ref color);
				break;
			}

			case BroadphaseNativeTypes.CYLINDER_SHAPE_PROXYTYPE:
			{
				CylinderShape cylinder = (CylinderShape)shape;
				int upAxis = cylinder.GetUpAxis();
				float radius = cylinder.GetRadius();
				float halfHeight = cylinder.GetHalfExtentsWithMargin()[upAxis];
				GetDebugDrawer().DrawCylinder(radius, halfHeight, upAxis, ref worldTransform, ref color);
				break;
			}

			case BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE:
			{
				StaticPlaneShape staticPlaneShape = shape as StaticPlaneShape;
				float planeConst = staticPlaneShape.GetPlaneConstant();
				IndexedVector3 planeNormal = staticPlaneShape.GetPlaneNormal();
				GetDebugDrawer().DrawPlane(ref planeNormal, planeConst, ref worldTransform, ref color);
				break;
			}

			default:
			{
				if (shape.IsPolyhedral())
				{
					PolyhedralConvexShape polyshape = shape as PolyhedralConvexShape;
					ConvexPolyhedron poly = polyshape.GetConvexPolyhedron();
					if (poly != null)
					{
						for (int i = 0; i < poly.m_faces.Count; i++)
						{
							IndexedVector3 centroid = IndexedVector3.Zero;
							int numVerts = poly.m_faces[i].m_indices.Count;
							if (numVerts != 0)
							{
								int lastV = poly.m_faces[i].m_indices[numVerts - 1];
								for (int v = 0; v < poly.m_faces[i].m_indices.Count; v++)
								{
									int curVert = poly.m_faces[i].m_indices[v];
									centroid += poly.m_vertices[curVert];
									GetDebugDrawer().DrawLine(worldTransform * poly.m_vertices[lastV], worldTransform * poly.m_vertices[curVert], color);
									lastV = curVert;
								}
							}

							centroid *= 1.0f / (float)(numVerts);
							IndexedVector3 normalColor = new IndexedVector3(1, 1, 0);
							IndexedVector3 faceNormal = new IndexedVector3(poly.m_faces[i].m_plane[0], poly.m_faces[i].m_plane[1], poly.m_faces[i].m_plane[2]);
							GetDebugDrawer().DrawLine(worldTransform * centroid, worldTransform * (centroid + faceNormal), normalColor);
						}
					}
					else
					{
						for (int i = 0; i < polyshape.GetNumEdges(); i++)
						{
							IndexedVector3 a, b;
							polyshape.GetEdge(i, out a, out b);
							IndexedVector3 wa = worldTransform * a;
							IndexedVector3 wb = worldTransform * b;
							GetDebugDrawer().DrawLine(ref wa, ref wb, ref color);
						}
					}
				}

				if (shape.IsConcave())
				{
					ConcaveShape concaveMesh = (ConcaveShape)shape;
					IndexedVector3 aabbMax = MathUtil.MAX_VECTOR;
					IndexedVector3 aabbMin = MathUtil.MIN_VECTOR;
					using (HumanoidRetargeter.Core.Formats.Mmd.Bullet.DebugDrawcallback drawCallback = BulletGlobals.DebugDrawcallbackPool.Get())
					{
						drawCallback.Initialise(GetDebugDrawer(), ref worldTransform, ref color);
						concaveMesh.ProcessAllTriangles(drawCallback, ref aabbMin, ref aabbMax);
					}
				}
				else if (shape.GetShapeType() == BroadphaseNativeTypes.CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE)
				{
					ConvexTriangleMeshShape convexMesh = (ConvexTriangleMeshShape)shape;
					//todo: pass camera for some culling
					IndexedVector3 aabbMax = MathUtil.MAX_VECTOR;
					IndexedVector3 aabbMin = MathUtil.MIN_VECTOR;
				//DebugDrawcallback drawCallback;
				//DebugDrawcallback drawCallback = new DebugDrawcallback(debugDraw, ref worldTransform, ref color);
				//convexMesh.GetMeshInterface().InternalProcessAllTriangles(drawCallback, ref aabbMin, ref aabbMax);
				//drawCallback.Cleanup();
				}

				break;
			}
		}
	}

	public int GetNumCollisionObjects()
	{
		return m_collisionObjects.Count;
	}

	public virtual void RayTest(ref IndexedVector3 rayFromWorld, ref IndexedVector3 rayToWorld, RayResultCallback resultCallback)
	{
		BulletGlobals.StartProfile("rayTest");
		using (SingleRayCallback rayCB = BulletGlobals.SingleRayCallbackPool.Get())
		{
			rayCB.Initialize(ref rayFromWorld, ref rayToWorld, this, resultCallback);
			m_broadphasePairCache.RayTest(ref rayFromWorld, ref rayToWorld, rayCB);
		}

		BulletGlobals.StopProfile();
	}

	//fixed me == checked Debug draw world
	public void ContactTest(CollisionObject colObj, ContactResultCallback resultCallback)
	{
	}

	public void ContactPairTest(CollisionObject colObjA, CollisionObject colObjB, ContactResultCallback resultCallback)
	{
	}

	// convexTest performs a swept convex cast on all objects in the btCollisionWorld, and calls the resultCallback
	// This allows for several queries: first hit, all hits, any hit, dependent on the value return by the callback.
	public virtual void ConvexSweepTest(ConvexShape castShape, IndexedMatrix convexFromWorld, IndexedMatrix convexToWorld, ConvexResultCallback resultCallback, float allowedCcdPenetration)
	{
		ConvexSweepTest(castShape, ref convexFromWorld, ref convexToWorld, resultCallback, allowedCcdPenetration);
	}

	public virtual void ConvexSweepTest(ConvexShape castShape, ref IndexedMatrix convexFromWorld, ref IndexedMatrix convexToWorld, ConvexResultCallback resultCallback, float allowedCcdPenetration)
	{
		BulletGlobals.StartProfile("convexSweepTest");
		IndexedMatrix convexFromTrans;
		IndexedMatrix convexToTrans;
		convexFromTrans = convexFromWorld;
		convexToTrans = convexToWorld;
		IndexedVector3 castShapeAabbMin;
		IndexedVector3 castShapeAabbMax;
		/* Compute AABB that encompasses angular movement */
		{
			IndexedVector3 linVel;
			IndexedVector3 angVel;
			TransformUtil.CalculateVelocity(ref convexFromTrans, ref convexToTrans, 1.0f, out linVel, out angVel);
			IndexedVector3 zeroLinVel = new IndexedVector3();
			IndexedMatrix R = IndexedMatrix.Identity;
			R.SetRotation(convexFromTrans.GetRotation());
			castShape.CalculateTemporalAabb(ref R, ref zeroLinVel, ref angVel, 1.0f, out castShapeAabbMin, out castShapeAabbMax);
		}

		SingleSweepCallback convexCB = BulletGlobals.SingleSweepCallbackPool.Get();
		convexCB.Initialize(castShape, ref convexFromWorld, ref convexToWorld, this, resultCallback, allowedCcdPenetration);
		IndexedVector3 tempFrom = convexFromTrans._origin;
		IndexedVector3 tempTo = convexToTrans._origin;
		m_broadphasePairCache.RayTest(ref tempFrom, ref tempTo, convexCB, ref castShapeAabbMin, ref castShapeAabbMax);
		convexCB.Cleanup();
		BulletGlobals.SingleSweepCallbackPool.Free(convexCB);
		BulletGlobals.StopProfile();
	}

	public virtual void AddCollisionObject(CollisionObject collisionObject)
	{
		AddCollisionObject(collisionObject, CollisionFilterGroups.DefaultFilter, CollisionFilterGroups.AllFilter);
	}

	public virtual void AddCollisionObject(CollisionObject collisionObject, CollisionFilterGroups collisionFilterGroup, CollisionFilterGroups collisionFilterMask)
	{
		{
		}

		//Debug.Assert(!m_collisionObjects.Contains(collisionObject));
		if (m_collisionObjects.Contains(collisionObject))
		{
			return;
		}

		m_collisionObjects.Add(collisionObject);
		//calculate new AABB
		IndexedMatrix trans = collisionObject.GetWorldTransform();
		IndexedVector3 minAabb;
		IndexedVector3 maxAabb;
		collisionObject.GetCollisionShape().GetAabb(ref trans, out minAabb, out maxAabb);
		BroadphaseNativeTypes type = collisionObject.GetCollisionShape().GetShapeType();
		collisionObject.SetBroadphaseHandle(GetBroadphase().CreateProxy(ref minAabb, ref maxAabb, type, collisionObject, collisionFilterGroup, collisionFilterMask, m_dispatcher1, 0));
	}

	public ObjectArray<CollisionObject> GetCollisionObjectArray()
	{
		return m_collisionObjects;
	}

	public DispatcherInfo GetDispatchInfo()
	{
		return m_dispatchInfo;
	}

	public virtual void PerformDiscreteCollisionDetection()
	{
		BulletGlobals.StartProfile("performDiscreteCollisionDetection");
		DispatcherInfo dispatchInfo = GetDispatchInfo();
		UpdateAabbs();
		{
			BulletGlobals.StartProfile("calculateOverlappingPairs");
			m_broadphasePairCache.CalculateOverlappingPairs(m_dispatcher1);
			BulletGlobals.StopProfile();
		}

		IDispatcher dispatcher = GetDispatcher();
		{
			BulletGlobals.StartProfile("dispatchAllCollisionPairs");
			if (dispatcher != null)
			{
				dispatcher.DispatchAllCollisionPairs(m_broadphasePairCache.GetOverlappingPairCache(), dispatchInfo, m_dispatcher1);
			}

			BulletGlobals.StopProfile();
		}

		BulletGlobals.StopProfile();
	}

	public virtual void RemoveCollisionObject(CollisionObject collisionObject)
	{
		//bool removeFromBroadphase = false;
		{
			BroadphaseProxy bp = collisionObject.GetBroadphaseHandle();
			if (bp != null)
			{
				//
				// only clear the cached algorithms
				//
				GetBroadphase().GetOverlappingPairCache().CleanProxyFromPairs(bp, m_dispatcher1);
				GetBroadphase().DestroyProxy(bp, m_dispatcher1);
				collisionObject.SetBroadphaseHandle(null);
			}
		}

		//swapremove
		m_collisionObjects.Remove(collisionObject);
	}

	public bool GetForceUpdateAllAabbs()
	{
		return m_forceUpdateAllAabbs;
	}

	public void SetForceUpdateAllAabbs(bool forceUpdateAllAabbs)
	{
		m_forceUpdateAllAabbs = forceUpdateAllAabbs;
	}

	public static void RayTestSingle(ref IndexedMatrix rayFromTrans, ref IndexedMatrix rayToTrans, CollisionObject collisionObject, CollisionShape collisionShape, ref IndexedMatrix colObjWorldTransform, RayResultCallback resultCallback)
	{
		SphereShape pointShape = BulletGlobals.SphereShapePool.Get();
		pointShape.Initialize(0.0f);
		pointShape.SetMargin(0f);
		ConvexShape castShape = pointShape;
		if (collisionShape.IsConvex())
		{
			BulletGlobals.StartProfile("rayTestConvex");
			CastResult castResult = BulletGlobals.CastResultPool.Get();
			castResult.m_fraction = resultCallback.m_closestHitFraction;
			ConvexShape convexShape = collisionShape as ConvexShape;
			VoronoiSimplexSolver simplexSolver = BulletGlobals.VoronoiSimplexSolverPool.Get();
			//#define USE_SUBSIMPLEX_CONVEX_CAST 1
			//#ifdef USE_SUBSIMPLEX_CONVEX_CAST
			// FIXME - MAN - convexcat here seems to make big difference to forklift.
			SubSimplexConvexCast convexCaster = BulletGlobals.SubSimplexConvexCastPool.Get();
			convexCaster.Initialize(castShape, convexShape, simplexSolver);
			//GjkConvexCast convexCaster = new GjkConvexCast(castShape, convexShape, simplexSolver);
			//#else
			//btGjkConvexCast	convexCaster(castShape,convexShape,&simplexSolver);
			//btContinuousConvexCollision convexCaster(castShape,convexShape,&simplexSolver,0);
			//#endif //#USE_SUBSIMPLEX_CONVEX_CAST
			if (convexCaster.CalcTimeOfImpact(ref rayFromTrans, ref rayToTrans, ref colObjWorldTransform, ref colObjWorldTransform, castResult))
			{
				//add hit
				if (castResult.m_normal.LengthSquared() > 0.0001f)
				{
					if (castResult.m_fraction < resultCallback.m_closestHitFraction)
					{
						//if (resultCallback.m_closestHitFraction != 1f)
						//{
						//    int ibreak = 0;
						//    convexCaster.calcTimeOfImpact(ref rayFromTrans, ref rayToTrans, ref colObjWorldTransform, ref colObjWorldTransform, castResult);
						//}
						//#ifdef USE_SUBSIMPLEX_CONVEX_CAST
						//rotate normal into worldspace
						castResult.m_normal = rayFromTrans._basis * castResult.m_normal;
						//#endif //USE_SUBSIMPLEX_CONVEX_CAST
						castResult.m_normal.Normalize();
						LocalRayResult localRayResult = new LocalRayResult(collisionObject, //null, // updated to allow different ctor on struct
						ref castResult.m_normal, castResult.m_fraction);
						bool normalInWorldSpace = true;
						resultCallback.AddSingleResult(ref localRayResult, normalInWorldSpace);
					}
				}
			}

			castResult.Cleanup();
			BulletGlobals.SubSimplexConvexCastPool.Free(convexCaster);
			BulletGlobals.VoronoiSimplexSolverPool.Free(simplexSolver);
			BulletGlobals.StopProfile();
		}
		else
		{
			if (collisionShape.IsConcave())
			{
				BulletGlobals.StartProfile("rayTestConcave");
				if (collisionShape.GetShapeType() == BroadphaseNativeTypes.TRIANGLE_MESH_SHAPE_PROXYTYPE && collisionShape is BvhTriangleMeshShape)
				{
					BvhTriangleMeshShape triangleMesh = (BvhTriangleMeshShape)collisionShape;
					IndexedMatrix worldTocollisionObject = colObjWorldTransform.Inverse();
					IndexedVector3 rayFromLocal = worldTocollisionObject * rayFromTrans._origin;
					IndexedVector3 rayToLocal = worldTocollisionObject * rayToTrans._origin;
					IndexedMatrix transform = IndexedMatrix.Identity;
					using (BridgeTriangleRaycastCallback rcb = BulletGlobals.BridgeTriangleRaycastCallbackPool.Get())
					{
						rcb.Initialize(ref rayFromLocal, ref rayToLocal, resultCallback, collisionObject, triangleMesh, ref transform);
						rcb.m_hitFraction = resultCallback.m_closestHitFraction;
						triangleMesh.PerformRaycast(rcb, ref rayFromLocal, ref rayToLocal);
					}
				}
				else if (collisionShape.GetShapeType() == BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE && collisionShape is HeightfieldTerrainShape)
				{
					HeightfieldTerrainShape heightField = (HeightfieldTerrainShape)collisionShape;
					IndexedMatrix worldTocollisionObject = colObjWorldTransform.Inverse();
					IndexedVector3 rayFromLocal = worldTocollisionObject * rayFromTrans._origin;
					IndexedVector3 rayToLocal = worldTocollisionObject * rayToTrans._origin;
					IndexedMatrix transform = IndexedMatrix.Identity;
					using (BridgeTriangleConcaveRaycastCallback rcb = BulletGlobals.BridgeTriangleConcaveRaycastCallbackPool.Get())
					{
						rcb.Initialize(ref rayFromLocal, ref rayToLocal, resultCallback, collisionObject, heightField, ref transform);
						rcb.m_hitFraction = resultCallback.m_closestHitFraction;
						heightField.PerformRaycast(rcb, ref rayFromLocal, ref rayToLocal);
					}
				}
				else
				{
					//generic (slower) case
					ConcaveShape concaveShape = (ConcaveShape)collisionShape;
					IndexedMatrix worldTocollisionObject = colObjWorldTransform.Inverse();
					IndexedVector3 rayFromLocal = worldTocollisionObject * rayFromTrans._origin;
					IndexedVector3 rayToLocal = worldTocollisionObject * rayToTrans._origin;
					//ConvexCast::CastResult
					IndexedMatrix transform = IndexedMatrix.Identity;
					using (BridgeTriangleConcaveRaycastCallback rcb = BulletGlobals.BridgeTriangleConcaveRaycastCallbackPool.Get())
					{
						rcb.Initialize(ref rayFromLocal, ref rayToLocal, resultCallback, collisionObject, concaveShape, ref transform);
						rcb.m_hitFraction = resultCallback.m_closestHitFraction;
						IndexedVector3 rayAabbMinLocal = rayFromLocal;
						MathUtil.VectorMin(ref rayToLocal, ref rayAabbMinLocal);
						IndexedVector3 rayAabbMaxLocal = rayFromLocal;
						MathUtil.VectorMax(ref rayToLocal, ref rayAabbMaxLocal);
						concaveShape.ProcessAllTriangles(rcb, ref rayAabbMinLocal, ref rayAabbMaxLocal);
					}
				}

				BulletGlobals.StopProfile();
			}
			else
			{
				BulletGlobals.StartProfile("rayTestCompound");
				if (collisionShape.IsCompound())
				{
					CompoundShape compoundShape = collisionShape as CompoundShape;
					Dbvt dbvt = compoundShape.GetDynamicAabbTree();
					RayTester rayCB = new RayTester(collisionObject, compoundShape, ref colObjWorldTransform, ref rayFromTrans, ref rayToTrans, resultCallback);
					if (dbvt != null)
					{
						IndexedVector3 localRayFrom = colObjWorldTransform.InverseTimes(ref rayFromTrans)._origin;
						IndexedVector3 localRayTo = colObjWorldTransform.InverseTimes(ref rayToTrans)._origin;
						Dbvt.RayTest(dbvt.m_root, ref localRayFrom, ref localRayTo, rayCB);
					}
					else
					{
						for (int i = 0, n = compoundShape.GetNumChildShapes(); i < n; ++i)
						{
							rayCB.Process(i);
						}
					}

					rayCB.Cleanup();
					BulletGlobals.StopProfile();
				}
			}
		}

		BulletGlobals.SphereShapePool.Free(pointShape);
	}

	public static void ObjectQuerySingle(ConvexShape castShape, ref IndexedMatrix convexFromTrans, ref IndexedMatrix convexToTrans, CollisionObject collisionObject, CollisionShape collisionShape, ref IndexedMatrix colObjWorldTransform, ConvexResultCallback resultCallback, float allowedPenetration)
	{
		if (collisionShape.IsConvex())
		{
			BulletGlobals.StartProfile("convexSweepConvex");
			CastResult castResult = BulletGlobals.CastResultPool.Get();
			castResult.m_allowedPenetration = allowedPenetration;
			castResult.m_fraction = resultCallback.m_closestHitFraction; //float(1.);//??
			ConvexShape convexShape = collisionShape as ConvexShape;
			VoronoiSimplexSolver simplexSolver = BulletGlobals.VoronoiSimplexSolverPool.Get();
			GjkEpaPenetrationDepthSolver gjkEpaPenetrationSolver = BulletGlobals.GjkEpaPenetrationDepthSolverPool.Get();
			ContinuousConvexCollision convexCaster1 = BulletGlobals.ContinuousConvexCollisionPool.Get();
			convexCaster1.Initialize(castShape, convexShape, simplexSolver, gjkEpaPenetrationSolver);
			//btGjkConvexCast convexCaster2(castShape,convexShape,&simplexSolver);
			//btSubsimplexConvexCast convexCaster3(castShape,convexShape,&simplexSolver);
			IConvexCast castPtr = convexCaster1;
			if (castPtr.CalcTimeOfImpact(ref convexFromTrans, ref convexToTrans, ref colObjWorldTransform, ref colObjWorldTransform, castResult))
			{
				//add hit
				if (castResult.m_normal.LengthSquared() > 0.0001f)
				{
					if (castResult.m_fraction < resultCallback.m_closestHitFraction)
					{
						castResult.m_normal.Normalize();
						LocalConvexResult localConvexResult = new LocalConvexResult(collisionObject, //null, // updated to allow different ctor on struct
						ref castResult.m_normal, ref castResult.m_hitPoint, castResult.m_fraction);
						bool normalInWorldSpace = true;
						resultCallback.AddSingleResult(ref localConvexResult, normalInWorldSpace);
					}
				}
			}

			BulletGlobals.ContinuousConvexCollisionPool.Free(convexCaster1);
			BulletGlobals.GjkEpaPenetrationDepthSolverPool.Free(gjkEpaPenetrationSolver);
			BulletGlobals.VoronoiSimplexSolverPool.Free(simplexSolver);
			castResult.Cleanup();
			BulletGlobals.StopProfile();
		}
		else
		{
			if (collisionShape.IsConcave())
			{
				if (collisionShape.GetShapeType() == BroadphaseNativeTypes.TRIANGLE_MESH_SHAPE_PROXYTYPE)
				{
					BulletGlobals.StartProfile("convexSweepbtBvhTriangleMesh");
					BvhTriangleMeshShape triangleMesh = (BvhTriangleMeshShape)collisionShape;
					IndexedMatrix worldTocollisionObject = colObjWorldTransform.Inverse();
					IndexedVector3 convexFromLocal = worldTocollisionObject * convexFromTrans._origin;
					IndexedVector3 convexToLocal = worldTocollisionObject * convexToTrans._origin;
					// rotation of box in local mesh space = MeshRotation^-1 * ConvexToRotation
					IndexedMatrix rotationXform = new IndexedMatrix(worldTocollisionObject._basis * convexToTrans._basis, new IndexedVector3(0));
					using (BridgeTriangleConvexcastCallback tccb = BulletGlobals.BridgeTriangleConvexcastCallbackPool.Get())
					{
						tccb.Initialize(castShape, ref convexFromTrans, ref convexToTrans, resultCallback, collisionObject, triangleMesh, ref colObjWorldTransform);
						tccb.m_hitFraction = resultCallback.m_closestHitFraction;
						tccb.m_allowedPenetration = allowedPenetration;
						IndexedVector3 boxMinLocal;
						IndexedVector3 boxMaxLocal;
						castShape.GetAabb(ref rotationXform, out boxMinLocal, out boxMaxLocal);
						triangleMesh.PerformConvexCast(tccb, ref convexFromLocal, ref convexToLocal, ref boxMinLocal, ref boxMaxLocal);
					}

					BulletGlobals.StopProfile();
				}
				else
				{
					if (collisionShape.GetShapeType() == BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE)
					{
						CastResult castResult = BulletGlobals.CastResultPool.Get();
						castResult.m_allowedPenetration = allowedPenetration;
						castResult.m_fraction = resultCallback.m_closestHitFraction;
						StaticPlaneShape planeShape = collisionShape as StaticPlaneShape;
						ContinuousConvexCollision convexCaster1 = new ContinuousConvexCollision(castShape, planeShape);
						if (convexCaster1.CalcTimeOfImpact(ref convexFromTrans, ref convexToTrans, ref colObjWorldTransform, ref colObjWorldTransform, castResult))
						{
							//add hit
							if (castResult.m_normal.LengthSquared() > 0.0001f)
							{
								if (castResult.m_fraction < resultCallback.m_closestHitFraction)
								{
									castResult.m_normal.Normalize();
									LocalConvexResult localConvexResult = new LocalConvexResult(collisionObject, //null, // updated to allow different ctor on struct
									ref castResult.m_normal, ref castResult.m_hitPoint, castResult.m_fraction);
									bool normalInWorldSpace = true;
									resultCallback.AddSingleResult(ref localConvexResult, normalInWorldSpace);
								}
							}
						}

						castResult.Cleanup();
					}
					else
					{
						BulletGlobals.StartProfile("convexSweepConcave");
						ConcaveShape concaveShape = (ConcaveShape)collisionShape;
						IndexedMatrix worldTocollisionObject = colObjWorldTransform.Inverse();
						IndexedVector3 convexFromLocal = worldTocollisionObject * convexFromTrans._origin;
						IndexedVector3 convexToLocal = worldTocollisionObject * convexToTrans._origin;
						// rotation of box in local mesh space = MeshRotation^-1 * ConvexToRotation
						IndexedMatrix rotationXform = new IndexedMatrix(worldTocollisionObject._basis * convexToTrans._basis, new IndexedVector3(0));
						using (BridgeTriangleConvexcastCallback tccb = BulletGlobals.BridgeTriangleConvexcastCallbackPool.Get())
						{
							tccb.Initialize(castShape, ref convexFromTrans, ref convexToTrans, resultCallback, collisionObject, concaveShape, ref colObjWorldTransform);
							tccb.m_hitFraction = resultCallback.m_closestHitFraction;
							tccb.m_allowedPenetration = allowedPenetration;
							IndexedVector3 boxMinLocal;
							IndexedVector3 boxMaxLocal;
							castShape.GetAabb(ref rotationXform, out boxMinLocal, out boxMaxLocal);
							IndexedVector3 rayAabbMinLocal = convexFromLocal;
							MathUtil.VectorMin(ref convexToLocal, ref rayAabbMinLocal);
							//rayAabbMinLocal.setMin(convexToLocal);
							IndexedVector3 rayAabbMaxLocal = convexFromLocal;
							//rayAabbMaxLocal.setMax(convexToLocal);
							MathUtil.VectorMax(ref convexToLocal, ref rayAabbMaxLocal);
							rayAabbMinLocal += boxMinLocal;
							rayAabbMaxLocal += boxMaxLocal;
							concaveShape.ProcessAllTriangles(tccb, ref rayAabbMinLocal, ref rayAabbMaxLocal);
							BulletGlobals.StopProfile();
						}
					}
				}
			}
			else
			{
				if (collisionShape.IsCompound())
				{
					BulletGlobals.StartProfile("convexSweepCompound");
					CompoundShape compoundShape = (CompoundShape)collisionShape;
					for (int i = 0; i < compoundShape.GetNumChildShapes(); i++)
					{
						IndexedMatrix childTrans = compoundShape.GetChildTransform(i);
						CollisionShape childCollisionShape = compoundShape.GetChildShape(i);
						IndexedMatrix childWorldTrans = colObjWorldTransform * childTrans;
						// replace collision shape so that callback can determine the triangle
						CollisionShape saveCollisionShape = collisionObject.GetCollisionShape();
						collisionObject.InternalSetTemporaryCollisionShape(childCollisionShape);
						LocalInfoAdder my_cb = new LocalInfoAdder(i, resultCallback);
						my_cb.m_closestHitFraction = resultCallback.m_closestHitFraction;
						ObjectQuerySingle(castShape, ref convexFromTrans, ref convexToTrans, collisionObject, childCollisionShape, ref childWorldTrans, my_cb, allowedPenetration);
						// restore
						collisionObject.InternalSetTemporaryCollisionShape(saveCollisionShape);
					}

					BulletGlobals.StopProfile();
				}
			}
		}
	}

	protected ObjectArray<CollisionObject> m_collisionObjects;
	protected IDispatcher m_dispatcher1;
	protected DispatcherInfo m_dispatchInfo;
	protected IBroadphaseInterface m_broadphasePairCache;
	protected IDebugDraw m_debugDrawer;
	protected bool m_forceUpdateAllAabbs;
	protected IProfileManager m_profileManager;
}