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

Collision manifold result helper from a Bullet physics port. Manages contact points between two CollisionObject instances, transforms contact positions between world and local space, creates or updates ManifoldPoint entries, and computes combined friction/restitution.

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.Diagnostics;
using HumanoidRetargeter.Core.Formats.Mmd.Bullet.LinearMath;

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

internal class ManifoldResult : IDiscreteCollisionDetectorInterfaceResult
{
	public ManifoldResult()
	{
		int ibreak = 0;
	}

	public ManifoldResult(CollisionObject body0, CollisionObject body1)
	{
		Initialise(body0, body1);
	}

	public void Initialise(CollisionObject body0, CollisionObject body1)
	{
		m_body0 = body0;
		m_body1 = body1;
		m_rootTransA = body0.GetWorldTransform();
		m_rootTransB = body1.GetWorldTransform();
		m_manifoldPtr = null;
	}

	public void SetPersistentManifold(PersistentManifold manifoldPtr)
	{
		m_manifoldPtr = manifoldPtr;
	}

	public PersistentManifold GetPersistentManifold()
	{
		return m_manifoldPtr;
	}

	public virtual void SetShapeIdentifiersA(int partId0, int index0)
	{
		m_partId0 = partId0;
		m_index0 = index0;
	}

	public virtual void SetShapeIdentifiersB(int partId1, int index1)
	{
		m_partId1 = partId1;
		m_index1 = index1;
	}

	public CollisionObject GetBody0Internal()
	{
		return m_body0;
	}

	public CollisionObject GetBody1Internal()
	{
		return m_body1;
	}

	public virtual void AddContactPoint(IndexedVector3 normalOnBInWorld, IndexedVector3 pointInWorld, float depth)
	{
		AddContactPoint(ref normalOnBInWorld, ref pointInWorld, depth);
	}

	public virtual void AddContactPoint(ref IndexedVector3 normalOnBInWorld, ref IndexedVector3 pointInWorld, float depth)
	{
		{
		}

		//order in manifold needs to match
		//if (depth > m_manifoldPtr.GetContactProcessingThreshold())
		if (depth > m_manifoldPtr.GetContactBreakingThreshold())
		{
			return;
		}

		bool isSwapped = m_manifoldPtr.GetBody0() != m_body0;
		IndexedVector3 pointA = pointInWorld + normalOnBInWorld * depth;
		IndexedVector3 localA;
		IndexedVector3 localB;
		if (isSwapped)
		{
			MathUtil.InverseTransform(ref m_rootTransB, ref pointA, out localA);
			MathUtil.InverseTransform(ref m_rootTransA, ref pointInWorld, out localB);
		}
		else
		{
			MathUtil.InverseTransform(ref m_rootTransA, ref pointA, out localA);
			MathUtil.InverseTransform(ref m_rootTransB, ref pointInWorld, out localB);
		}

		ManifoldPoint newPt = BulletGlobals.ManifoldPointPool.Get();
		newPt.Initialise(ref localA, ref localB, ref normalOnBInWorld, depth);
		newPt.SetPositionWorldOnA(ref pointA);
		newPt.SetPositionWorldOnB(ref pointInWorld);
		int insertIndex = m_manifoldPtr.GetCacheEntry(newPt);
		newPt.SetCombinedFriction(CalculateCombinedFriction(m_body0, m_body1));
		newPt.SetCombinedRestitution(CalculateCombinedRestitution(m_body0, m_body1));
		//BP mod, store contact triangles.
		if (isSwapped)
		{
			newPt.m_partId0 = m_partId1;
			newPt.m_partId1 = m_partId0;
			newPt.m_index0 = m_index1;
			newPt.m_index1 = m_index0;
		}
		else
		{
			newPt.m_partId0 = m_partId0;
			newPt.m_partId1 = m_partId1;
			newPt.m_index0 = m_index0;
			newPt.m_index1 = m_index1;
		}

		//printf("depth=%f\n",depth);
		if (insertIndex >= 0)
		{
			//const btManifoldPoint& oldPoint = m_manifoldPtr->getContactPoint(insertIndex);
			m_manifoldPtr.ReplaceContactPoint(newPt, insertIndex);
		}
		else
		{
			insertIndex = m_manifoldPtr.AddManifoldPoint(newPt);
		}

		//User can override friction and/or restitution
		if (BulletGlobals.gContactAddedCallback != null && //and if either of the two bodies requires custom material
		((m_body0.GetCollisionFlags() & CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK) != 0 || (m_body1.GetCollisionFlags() & CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK) != 0))
		{
			//experimental feature info, for per-triangle material etc.
			CollisionObject obj0 = isSwapped ? m_body1 : m_body0;
			CollisionObject obj1 = isSwapped ? m_body0 : m_body1;
			//gContactAddedCallback.callback(m_manifoldPtr.getContactPoint(insertIndex),obj0,m_partId0,m_index0,obj1,m_partId1,m_index1);
			ManifoldPoint point = m_manifoldPtr.GetContactPoint(insertIndex);
			BulletGlobals.gContactAddedCallback.Callback(ref point, obj0, newPt.m_partId0, newPt.m_index0, obj1, newPt.m_partId1, newPt.m_index1);
		}
	}

	public void RefreshContactPoints()
	{
		{
		}

		if (m_manifoldPtr.GetNumContacts() == 0)
		{
			return;
		}

		bool isSwapped = m_manifoldPtr.GetBody0() != m_body0;
		if (isSwapped)
		{
			m_manifoldPtr.RefreshContactPoints(ref m_rootTransB, ref m_rootTransA);
		}
		else
		{
			m_manifoldPtr.RefreshContactPoints(ref m_rootTransA, ref m_rootTransB);
		}
	}

	private float CalculateCombinedFriction(CollisionObject body0, CollisionObject body1)
	{
		float friction = body0.GetFriction() * body1.GetFriction();
		const float MAX_FRICTION = 10f;
		if (friction < -MAX_FRICTION)
			friction = -MAX_FRICTION;
		if (friction > MAX_FRICTION)
			friction = MAX_FRICTION;
		return friction;
	}

	private float CalculateCombinedRestitution(CollisionObject body0, CollisionObject body1)
	{
		return body0.GetRestitution() * body1.GetRestitution();
	}

	protected PersistentManifold m_manifoldPtr;
	//we need this for compounds
	protected IndexedMatrix m_rootTransA;
	protected IndexedMatrix m_rootTransB;
	protected CollisionObject m_body0;
	protected CollisionObject m_body1;
	protected int m_partId0;
	protected int m_partId1;
	protected int m_index0;
	protected int m_index1;
}