Code/HumanoidRetargeter/Core/Formats/Mmd/Bullet/BulletDynamics/DiscreteDynamicsWorld.cs

Physics world implementation (DiscreteDynamicsWorld) ported from Bullet. Manages rigid bodies, collisions, constraints, islands and simulation stepping including gravity, integration, CCD, constraint solving and motion state synchronization.

Native Interop
/*
 * 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;
using System.Collections.Generic;
using System.Diagnostics;
using HumanoidRetargeter.Core.Formats.Mmd.Bullet.BulletCollision;
using HumanoidRetargeter.Core.Formats.Mmd.Bullet.LinearMath;

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

internal class DiscreteDynamicsWorld : DynamicsWorld
{
	public DiscreteDynamicsWorld(IDispatcher dispatcher, IBroadphaseInterface pairCache, IConstraintSolver constraintSolver, ICollisionConfiguration collisionConfiguration) : base(dispatcher, pairCache, collisionConfiguration)
	{
		m_ownsIslandManager = true;
		m_constraints = new ObjectArray<TypedConstraint>();
		m_sortedConstraints = new ObjectArray<TypedConstraint>();
		m_islandSortPredicate = new SortConstraintOnIslandPredicate();
		m_islandQuickSortPredicate = new QuickSortConstraintOnIslandPredicate();
		m_actions = new List<IActionInterface>();
		m_nonStaticRigidBodies = new ObjectArray<RigidBody>();
		m_islandManager = new SimulationIslandManager();
		m_constraintSolver = constraintSolver;
		IndexedVector3 gravity = new IndexedVector3(0, -10, 0);
		SetGravity(ref gravity);
		m_localTime = 0f;
		m_profileTimings = 0;
		m_synchronizeAllMotionStates = false;
		if (m_constraintSolver == null)
		{
			m_constraintSolver = new SequentialImpulseConstraintSolver();
			m_ownsConstraintSolver = true;
		}
		else
		{
			m_ownsConstraintSolver = false;
		}
	}

	public override void Cleanup()
	{
		base.Cleanup();
		//only delete it when we created it
		if (m_ownsIslandManager)
		{
			m_islandManager.Cleanup();
			m_islandManager = null;
			m_ownsIslandManager = false;
		}

		if (m_ownsConstraintSolver)
		{
			m_constraintSolver.Cleanup();
			m_constraintSolver = null;
			m_ownsConstraintSolver = false;
		}
	}

	public override int StepSimulation(float timeStep, int maxSubSteps)
	{
		return StepSimulation(timeStep, maxSubSteps, s_fixedTimeStep);
	}

	public override int StepSimulation(float timeStep, int maxSubSteps, float fixedTimeStep)
	{
		StartProfiling(timeStep);
		BulletGlobals.StartProfile("stepSimulation");
		int numSimulationSubSteps = 0;
		if (maxSubSteps != 0)
		{
			//fixed timestep with interpolation
			m_localTime += timeStep;
			if (m_localTime >= fixedTimeStep)
			{
				numSimulationSubSteps = (int)(m_localTime / fixedTimeStep);
				m_localTime -= numSimulationSubSteps * fixedTimeStep;
			}
		}
		else
		{
			//variable timestep
			fixedTimeStep = timeStep;
			m_localTime = timeStep;
			if (MathUtil.FuzzyZero(timeStep))
			{
				numSimulationSubSteps = 0;
				maxSubSteps = 0;
			}
			else
			{
				numSimulationSubSteps = 1;
				maxSubSteps = 1;
			}
		}

		//process some debugging flags
		if (GetDebugDrawer() != null)
		{
			IDebugDraw debugDrawer = GetDebugDrawer();
			BulletGlobals.gDisableDeactivation = ((debugDrawer.GetDebugMode() & DebugDrawModes.DBG_NoDeactivation) != 0);
		}

		if (numSimulationSubSteps != 0)
		{
			//clamp the number of substeps, to prevent simulation grinding spiralling down to a halt
			int clampedSimulationSteps = (numSimulationSubSteps > maxSubSteps) ? maxSubSteps : numSimulationSubSteps;
			SaveKinematicState(fixedTimeStep * clampedSimulationSteps);
			ApplyGravity();
			for (int i = 0; i < clampedSimulationSteps; i++)
			{
				InternalSingleStepSimulation(fixedTimeStep);
				SynchronizeMotionStates();
			}
		}
		else
		{
			SynchronizeMotionStates();
		}

		ClearForces();
		if (m_profileManager != null)
		{
			m_profileManager.Increment_Frame_Counter();
		}

		return numSimulationSubSteps;
	}

	public override void SynchronizeMotionStates()
	{
		if (m_synchronizeAllMotionStates)
		{
			//iterate  over all collision objects
			int length = m_collisionObjects.Count;
			for (int i = 0; i < length; ++i)
			{
				RigidBody body = RigidBody.Upcast(m_collisionObjects[i]);
				if (body != null)
				{
					SynchronizeSingleMotionState(body);
				}
			}
		}
		else
		{
			//iterate over all active rigid bodies
			int length = m_nonStaticRigidBodies.Count;
			for (int i = 0; i < length; ++i)
			{
				RigidBody body = m_nonStaticRigidBodies[i];
				if (body.IsActive())
				{
					SynchronizeSingleMotionState(body);
				}
			}
		}
	}

	public void SynchronizeSingleMotionState(RigidBody body)
	{
		{
		}

		if (body.GetMotionState() != null && !body.IsStaticOrKinematicObject())
		{
			//we need to call the update at least once, even for sleeping objects
			//otherwise the 'graphics' transform never updates properly
			//if (body.getActivationState() != ISLAND_SLEEPING)
			{
				IndexedMatrix interpolatedTransform;
				TransformUtil.IntegrateTransform(body.GetInterpolationWorldTransform(), body.SetInterpolationLinearVelocity(), body.GetInterpolationAngularVelocity(), m_localTime * body.GetHitFraction(), out interpolatedTransform);
				body.GetMotionState().SetWorldTransform(ref interpolatedTransform);
			}
		}
	}

	public override void AddConstraint(TypedConstraint constraint, bool disableCollisionsBetweenLinkedBodies)
	{
		//if (constraint is ConeTwistConstraint)
		//if (constraint is HingeConstraint)
		//{
		//    return;
		//}
		m_constraints.Add(constraint);
		if (disableCollisionsBetweenLinkedBodies)
		{
			constraint.GetRigidBodyA().AddConstraintRef(constraint);
			constraint.GetRigidBodyB().AddConstraintRef(constraint);
		}
	}

	public override void RemoveConstraint(TypedConstraint constraint)
	{
		m_constraints.Remove(constraint);
		constraint.GetRigidBodyA().RemoveConstraintRef(constraint);
		constraint.GetRigidBodyB().RemoveConstraintRef(constraint);
	}

	public override void AddAction(IActionInterface action)
	{
		m_actions.Add(action);
	}

	public override void RemoveAction(IActionInterface action)
	{
		m_actions.Remove(action);
	}

	public SimulationIslandManager GetSimulationIslandManager()
	{
		return m_islandManager;
	}

	public CollisionWorld GetCollisionWorld()
	{
		return this;
	}

	public override void SetGravity(IndexedVector3 gravity)
	{
		SetGravity(ref gravity);
	}

	public override void SetGravity(ref IndexedVector3 gravity)
	{
		m_gravity = gravity;
		int length = m_nonStaticRigidBodies.Count;
		for (int i = 0; i < length; ++i)
		{
			RigidBody body = m_nonStaticRigidBodies[i];
			if (body.IsActive() && ((body.GetFlags() & RigidBodyFlags.BT_DISABLE_WORLD_GRAVITY) == 0))
			{
				body.SetGravity(ref gravity);
			}
		}
	}

	public override IndexedVector3 GetGravity()
	{
		return m_gravity;
	}

	public override void AddCollisionObject(CollisionObject collisionObject)
	{
		AddCollisionObject(collisionObject, CollisionFilterGroups.StaticFilter, CollisionFilterGroups.AllFilter ^ CollisionFilterGroups.StaticFilter);
	}

	public override void AddCollisionObject(CollisionObject collisionObject, CollisionFilterGroups collisionFilterGroup, CollisionFilterGroups collisionFilterMask)
	{
		base.AddCollisionObject(collisionObject, collisionFilterGroup, collisionFilterMask);
	}

	public override void RemoveCollisionObject(CollisionObject collisionObject)
	{
		RigidBody body = RigidBody.Upcast(collisionObject);
		if (body != null)
		{
			RemoveRigidBody(body);
		}
		else
		{
			base.RemoveCollisionObject(collisionObject);
		}
	}

	public void SetSynchronizeAllMotionStates(bool synchronizeAll)
	{
		m_synchronizeAllMotionStates = synchronizeAll;
	}

	public bool GetSynchronizeAllMotionStates()
	{
		return m_synchronizeAllMotionStates;
	}

	public override void AddRigidBody(RigidBody body)
	{
		if (!body.IsStaticOrKinematicObject() && 0 == (body.GetFlags() & RigidBodyFlags.BT_DISABLE_WORLD_GRAVITY))
		{
			body.SetGravity(ref m_gravity);
		}

		if (body.GetCollisionShape() != null)
		{
			if (!body.IsStaticObject())
			{
				m_nonStaticRigidBodies.Add(body);
			}
			else
			{
				body.SetActivationState(ActivationState.ISLAND_SLEEPING);
			}

			bool isDynamic = !(body.IsStaticObject() || body.IsKinematicObject());
			CollisionFilterGroups collisionFilterGroup = isDynamic ? CollisionFilterGroups.DefaultFilter : CollisionFilterGroups.StaticFilter;
			CollisionFilterGroups collisionFilterMask = isDynamic ? CollisionFilterGroups.AllFilter : (CollisionFilterGroups.AllFilter ^ CollisionFilterGroups.StaticFilter);
			AddCollisionObject(body, collisionFilterGroup, collisionFilterMask);
		}
	}

	public override void AddRigidBody(RigidBody body, CollisionFilterGroups group, CollisionFilterGroups mask)
	{
		if (!body.IsStaticOrKinematicObject() && 0 == (body.GetFlags() & RigidBodyFlags.BT_DISABLE_WORLD_GRAVITY))
		{
			body.SetGravity(ref m_gravity);
		}

		if (body.GetCollisionShape() != null)
		{
			if (!body.IsStaticObject())
			{
				if (!m_nonStaticRigidBodies.Contains(body))
				{
					m_nonStaticRigidBodies.Add(body);
				}
			}
			else
			{
				body.SetActivationState(ActivationState.ISLAND_SLEEPING);
			}

			AddCollisionObject(body, group, mask);
		}
	}

	public override void RemoveRigidBody(RigidBody body)
	{
		m_nonStaticRigidBodies.Remove(body);
		base.RemoveCollisionObject(body);
	}

	public override void DebugDrawWorld()
	{
		BulletGlobals.StartProfile("debugDrawWorld");
		base.DebugDrawWorld();
		if (GetDebugDrawer() != null)
		{
			DebugDrawModes mode = GetDebugDrawer().GetDebugMode();
			if ((mode & (DebugDrawModes.DBG_DrawConstraints | DebugDrawModes.DBG_DrawConstraintLimits)) != 0)
			{
				for (int i = GetNumConstraints() - 1; i >= 0; i--)
				{
					TypedConstraint constraint = GetConstraint(i);
					DrawHelper.DebugDrawConstraint(constraint, GetDebugDrawer());
				}
			}

			if (mode != 0)
			{
				int actionsCount = m_actions.Count;
				for (int i = 0; i < actionsCount; ++i)
				{
					m_actions[i].DebugDraw(m_debugDrawer);
				}
			}
		}

		BulletGlobals.StopProfile();
	}

	public override void SetConstraintSolver(IConstraintSolver solver)
	{
		//if (m_ownsConstraintSolver)
		//{
		//    btAlignedFree( m_constraintSolver);
		//}
		m_ownsConstraintSolver = false;
		m_constraintSolver = solver;
	}

	public override IConstraintSolver GetConstraintSolver()
	{
		return m_constraintSolver;
	}

	public override int GetNumConstraints()
	{
		return m_constraints.Count;
	}

	public override TypedConstraint GetConstraint(int index)
	{
		return m_constraints[index];
	}

	public override DynamicsWorldType GetWorldType()
	{
		return DynamicsWorldType.BT_DISCRETE_DYNAMICS_WORLD;
	}

	public override void ClearForces()
	{
		int length = m_nonStaticRigidBodies.Count;
		for (int i = 0; i < length; ++i)
		{
			m_nonStaticRigidBodies[i].ClearForces();
		}
	}

	public virtual void ApplyGravity()
	{
		int length = m_nonStaticRigidBodies.Count;
		for (int i = 0; i < length; ++i)
		{
			RigidBody body = m_nonStaticRigidBodies[i];
			if (body != null && body.IsActive())
			{
				body.ApplyGravity();
			}
		}
	}

	public virtual void SetNumTasks(int numTasks)
	{
	//(void) numTasks;
	}

	public virtual void UpdateVehicles(float timeStep)
	{
		UpdateActions(timeStep);
	}

	public override void AddVehicle(IActionInterface vehicle)
	{
		AddAction(vehicle);
	}

	public override void RemoveVehicle(IActionInterface vehicle)
	{
		RemoveAction(vehicle);
	}

	public override void AddCharacter(IActionInterface character)
	{
		AddAction(character);
	}

	public override void RemoveCharacter(IActionInterface character)
	{
		RemoveAction(character);
	}

	protected virtual void PredictUnconstraintMotion(float timeStep)
	{
		BulletGlobals.StartProfile("predictUnconstraintMotion");
		int length = m_nonStaticRigidBodies.Count;
		//for (int i = 0; i < length;i++)
		for (int i = 0; i < m_nonStaticRigidBodies.Count; i++)
		{
			RigidBody body = m_nonStaticRigidBodies[i];
			if (!body.IsStaticOrKinematicObject())
			{
				body.IntegrateVelocities(timeStep);
				//dampingF
				body.ApplyDamping(timeStep);
				IndexedMatrix temp;
				body.PredictIntegratedTransform(timeStep, out temp);
				body.SetInterpolationWorldTransform(ref temp);
			}
		}

		BulletGlobals.StopProfile();
	}

	protected virtual void IntegrateTransforms(float timeStep)
	{
		BulletGlobals.StartProfile("integrateTransforms");
		IndexedMatrix predictedTrans;
		int length = m_nonStaticRigidBodies.Count;
		for (int i = 0; i < length; ++i)
		{
			RigidBody body = m_nonStaticRigidBodies[i];
			if (body != null)
			{
				body.SetHitFraction(1f);
				if (body.IsActive() && (!body.IsStaticOrKinematicObject()))
				{
					body.PredictIntegratedTransform(timeStep, out predictedTrans);
					float squareMotion = (predictedTrans._origin - body.GetWorldTransform()._origin).LengthSquared();
					//if (body.GetCcdSquareMotionThreshold() != 0 && body.GetCcdSquareMotionThreshold() < squareMotion)
					if (GetDispatchInfo().m_useContinuous && body.GetCcdSquareMotionThreshold() != 0.0f && body.GetCcdSquareMotionThreshold() < squareMotion)
					{
						BulletGlobals.StartProfile("CCD motion clamping");
						if (body.GetCollisionShape().IsConvex())
						{
							gNumClampedCcdMotions++;
							using (ClosestNotMeConvexResultCallback sweepResults = BulletGlobals.ClosestNotMeConvexResultCallbackPool.Get())
							{
								sweepResults.Initialize(body, body.GetWorldTransform()._origin, predictedTrans._origin, GetBroadphase().GetOverlappingPairCache(), GetDispatcher());
								//btConvexShape* convexShape = static_cast<btConvexShape*>(body.GetCollisionShape());
								SphereShape tmpSphere = BulletGlobals.SphereShapePool.Get();
								tmpSphere.Initialize(body.GetCcdSweptSphereRadius()); //btConvexShape* convexShape = static_cast<btConvexShape*>(body.GetCollisionShape());
								sweepResults.m_allowedPenetration = GetDispatchInfo().GetAllowedCcdPenetration();
								sweepResults.m_collisionFilterGroup = body.GetBroadphaseProxy().m_collisionFilterGroup;
								sweepResults.m_collisionFilterMask = body.GetBroadphaseProxy().m_collisionFilterMask;
								IndexedMatrix modifiedPredictedTrans = predictedTrans;
								modifiedPredictedTrans._basis = body.GetWorldTransform()._basis;
								modifiedPredictedTrans._origin = predictedTrans._origin;
								ConvexSweepTest(tmpSphere, body.GetWorldTransform(), modifiedPredictedTrans, sweepResults, 0f);
								if (sweepResults.HasHit() && (sweepResults.m_closestHitFraction < 1.0f))
								{
									//printf("clamped integration to hit fraction = %f\n",fraction);
									body.SetHitFraction(sweepResults.m_closestHitFraction);
									body.PredictIntegratedTransform(timeStep * body.GetHitFraction(), out predictedTrans);
									body.SetHitFraction(0.0f);
									body.ProceedToTransform(ref predictedTrans);
									//response  between two dynamic objects without friction, assuming 0 penetration depth
									float appliedImpulse = 0.0f;
									float depth = 0.0f;
									appliedImpulse = ContactConstraint.ResolveSingleCollision(body, sweepResults.m_hitCollisionObject, ref sweepResults.m_hitPointWorld, ref sweepResults.m_hitNormalWorld, GetSolverInfo(), depth);
									continue;
								}

								BulletGlobals.SphereShapePool.Free(tmpSphere);
							}
						}

						BulletGlobals.StopProfile();
					}

					body.ProceedToTransform(ref predictedTrans);
				}
			}
		}
	}

	protected virtual void CalculateSimulationIslands()
	{
		BulletGlobals.StartProfile("calculateSimulationIslands");
		GetSimulationIslandManager().UpdateActivationState(GetCollisionWorld(), GetCollisionWorld().GetDispatcher());
		{
			int length = m_constraints.Count;
			for (int i = 0; i < length; ++i)
			{
				TypedConstraint constraint = m_constraints[i];
				RigidBody colObj0 = constraint.GetRigidBodyA();
				RigidBody colObj1 = constraint.GetRigidBodyB();
				if (((colObj0 != null) && (!colObj0.IsStaticOrKinematicObject())) && ((colObj1 != null) && (!colObj1.IsStaticOrKinematicObject())))
				{
					if (colObj0.IsActive() || colObj1.IsActive())
					{
						GetSimulationIslandManager().GetUnionFind().Unite((colObj0).GetIslandTag(), (colObj1).GetIslandTag());
					}
				}
			}
		}

		//Store the island id in each body
		GetSimulationIslandManager().StoreIslandActivationState(GetCollisionWorld());
		BulletGlobals.StopProfile();
	}

	protected virtual void SolveConstraints(ContactSolverInfo solverInfo)
	{
		//sorted version of all btTypedConstraint, based on islandId
		m_sortedConstraints.Resize(m_constraints.Count);
		int numConstraints = GetNumConstraints();
		for (int i = 0; i < numConstraints; i++)
		{
			m_sortedConstraints[i] = m_constraints[i];
		}

		if (numConstraints > 1)
		{
			//sortedConstraints.quickSort(btSortConstraintOnIslandPredicate());
			// If this sort is removed then the constraint gets twitchy...
			//m_sortedConstraints.Sort(m_islandSortPredicate);
			m_sortedConstraints.QuickSort(m_islandQuickSortPredicate);
		}

		//	btAssert(0);
		if (m_solverIslandCallback == null)
		{
			m_solverIslandCallback = new InplaceSolverIslandCallback(solverInfo, m_constraintSolver, m_sortedConstraints, GetNumConstraints(), m_debugDrawer, m_dispatcher1);
		}
		else
		{
			m_solverIslandCallback.Setup(solverInfo, m_sortedConstraints, numConstraints, m_debugDrawer);
		}

		m_constraintSolver.PrepareSolve(GetCollisionWorld().GetNumCollisionObjects(), GetCollisionWorld().GetDispatcher().GetNumManifolds());
		m_islandManager.BuildAndProcessIslands(GetCollisionWorld().GetDispatcher(), GetCollisionWorld(), m_solverIslandCallback);
		m_solverIslandCallback.ProcessConstraints();
		m_constraintSolver.AllSolved(solverInfo, m_debugDrawer);
	}

	protected void UpdateActivationState(float timeStep)
	{
		BulletGlobals.StartProfile("updateActivationState");
		int length = m_nonStaticRigidBodies.Count;
		for (int i = 0; i < length; ++i)
		{
			RigidBody body = m_nonStaticRigidBodies[i];
			if (body != null)
			{
				body.UpdateDeactivation(timeStep);
				if (body.WantsSleeping())
				{
					if (body.IsStaticOrKinematicObject())
					{
						body.SetActivationState(ActivationState.ISLAND_SLEEPING);
					}
					else
					{
						if (body.GetActivationState() == ActivationState.ACTIVE_TAG)
						{
							body.SetActivationState(ActivationState.WANTS_DEACTIVATION);
						}

						if (body.GetActivationState() == ActivationState.ISLAND_SLEEPING)
						{
							IndexedVector3 zero = IndexedVector3.Zero;
							body.SetAngularVelocity(ref zero);
							body.SetLinearVelocity(ref zero);
						}
					}
				}
				else
				{
					if (body.GetActivationState() != ActivationState.DISABLE_DEACTIVATION)
						body.SetActivationState(ActivationState.ACTIVE_TAG);
				}
			}
		}

		BulletGlobals.StopProfile();
	}

	protected void UpdateActions(float timeStep)
	{
		BulletGlobals.StartProfile("updateActions");
		int length = m_actions.Count;
		for (int i = 0; i < length; ++i)
		{
			m_actions[i].UpdateAction(this, timeStep);
		}

		BulletGlobals.StopProfile();
	}

	protected void StartProfiling(float timeStep)
	{
		BulletGlobals.ResetProfile();
	}

	protected virtual void InternalSingleStepSimulation(float timeStep)
	{
		BulletGlobals.StartProfile("internalSingleStepSimulation");
		if (null != m_internalPreTickCallback)
		{
			m_internalPreTickCallback.InternalTickCallback(this, timeStep);
		}

		PredictUnconstraintMotion(timeStep);
		DispatcherInfo dispatchInfo = GetDispatchInfo();
		dispatchInfo.SetTimeStep(timeStep);
		dispatchInfo.SetStepCount(0);
		dispatchInfo.SetDebugDraw(GetDebugDrawer());
		PerformDiscreteCollisionDetection();
		CalculateSimulationIslands();
		GetSolverInfo().m_timeStep = timeStep;
		SolveConstraints(GetSolverInfo());
		IntegrateTransforms(timeStep);
		UpdateActions(timeStep);
		UpdateActivationState(timeStep);
		if (m_internalTickCallback != null)
		{
			m_internalTickCallback.InternalTickCallback(this, timeStep);
		}

		BulletGlobals.StopProfile();
	}

	protected virtual void SaveKinematicState(float timeStep)
	{
		int length = m_collisionObjects.Count;
		for (int i = 0; i < length; ++i)
		{
			RigidBody body = RigidBody.Upcast(m_collisionObjects[i]);
			if (body != null && body.GetActivationState() != ActivationState.ISLAND_SLEEPING)
			{
				if (body.IsKinematicObject())
				{
					//to calculate velocities next frame
					body.SaveKinematicState(timeStep);
				}
			}
		}
	}

	public static int GetConstraintIslandId(TypedConstraint lhs)
	{
		int islandId;
		CollisionObject rcolObj0 = lhs.GetRigidBodyA();
		CollisionObject rcolObj1 = lhs.GetRigidBodyB();
		islandId = rcolObj0.GetIslandTag() >= 0 ? rcolObj0.GetIslandTag() : rcolObj1.GetIslandTag();
		return islandId;
	}

	protected IConstraintSolver m_constraintSolver;
	protected InplaceSolverIslandCallback m_solverIslandCallback;
	protected SimulationIslandManager m_islandManager;
	protected ObjectArray<TypedConstraint> m_constraints;
	protected ObjectArray<TypedConstraint> m_sortedConstraints;
	protected SortConstraintOnIslandPredicate m_islandSortPredicate;
	protected QuickSortConstraintOnIslandPredicate m_islandQuickSortPredicate;
	protected ObjectArray<RigidBody> m_nonStaticRigidBodies;
	protected IndexedVector3 m_gravity;
	//for variable timesteps
	protected float m_localTime;
	//for variable timesteps
	protected bool m_ownsIslandManager;
	protected bool m_ownsConstraintSolver;
	protected bool m_synchronizeAllMotionStates;
	protected IList<IActionInterface> m_actions;
	protected int m_profileTimings;
	protected int gNumClampedCcdMotions;
	protected const float s_fixedTimeStep = 1f / 60f;
//public const float s_fixedTimeStep = 0.016666668f;
//protected float s_fixedTimeStep2 = 1 / 60;
//protected float s_fixedTimeStep3 = (float)(1.0 / 60.0);
//protected float s_fixedTimeStep4 = 0.016666668f;
}