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

MultiSapBroadphase class from a Bullet physics port. Implements a multi-sap broadphase interface that manages MultiSapProxy objects, delegates to child broadphases (including a SimpleBroadphase), maintains an overlapping pair cache, builds a quantized BVH, and computes overlapping pairs and AABB/ray tests.

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

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

internal class MultiSapBroadphase : IBroadphaseInterface
{
	public MultiSapBroadphase() : this(16384, null)
	{
	}

	public MultiSapBroadphase(int maxProxies, IOverlappingPairCache pairCache)
	{
		m_overlappingPairs = pairCache;
		m_optimizedAabbTree = null;
		m_ownsPairCache = false;
		m_invalidPair = 0;
		if (m_overlappingPairs == null)
		{
			m_ownsPairCache = true;
			m_overlappingPairs = new SortedOverlappingPairCache();
		}

		m_filterCallback = new MultiSapOverlapFilterCallback();
		m_overlappingPairs.SetOverlapFilterCallback(m_filterCallback);
		//	mem = btAlignedAlloc(sizeof(btSimpleBroadphase),16);
		//	m_simpleBroadphase = new (mem) btSimpleBroadphase(maxProxies,m_overlappingPairs);
		m_simpleBroadphase = new SimpleBroadphase(maxProxies, m_overlappingPairs);
	}

	public IList<IBroadphaseInterface> GetBroadphaseArray()
	{
		return m_sapBroadphases;
	}

	public virtual void Cleanup()
	{
		if (m_ownsPairCache)
		{
			m_overlappingPairs.Cleanup();
			m_overlappingPairs = null;
			m_ownsPairCache = false;
		}
	}

	public virtual BroadphaseProxy CreateProxy(IndexedVector3 aabbMin, IndexedVector3 aabbMax, BroadphaseNativeTypes shapeType, Object userPtr, CollisionFilterGroups collisionFilterGroup, CollisionFilterGroups collisionFilterMask, IDispatcher dispatcher, Object multiSapProxy)
	{
		return CreateProxy(ref aabbMin, ref aabbMax, shapeType, userPtr, collisionFilterGroup, collisionFilterMask, dispatcher, multiSapProxy);
	}

	public virtual BroadphaseProxy CreateProxy(ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax, BroadphaseNativeTypes shapeType, Object userPtr, CollisionFilterGroups collisionFilterGroup, CollisionFilterGroups collisionFilterMask, IDispatcher dispatcher, Object multiSapProxy)
	{
		//void* ignoreMe -> we could think of recursive multi-sap, if someone is interested
		MultiSapProxy proxy = new MultiSapProxy(ref aabbMin, ref aabbMax, shapeType, userPtr, collisionFilterGroup, collisionFilterMask);
		m_multiSapProxies.Add(proxy);
		SetAabb(proxy, ref aabbMin, ref aabbMax, dispatcher);
		return proxy;
	}

	public virtual void DestroyProxy(BroadphaseProxy proxy, IDispatcher dispatcher)
	{
		{
		}
	}

	public virtual void SetAabb(BroadphaseProxy proxy, ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax, IDispatcher dispatcher)
	{
		MultiSapProxy multiProxy = (MultiSapProxy)proxy;
		multiProxy.m_aabbMin = aabbMin;
		multiProxy.m_aabbMax = aabbMax;
		MyNodeOverlapCallback myNodeCallback = new MyNodeOverlapCallback(this, multiProxy, dispatcher);
		if (m_optimizedAabbTree != null)
		{
			m_optimizedAabbTree.ReportAabbOverlappingNodex(myNodeCallback, ref aabbMin, ref aabbMax);
		}

		for (int i = 0; i < multiProxy.m_bridgeProxies.Count; i++)
		{
			IndexedVector3 worldAabbMin;
			IndexedVector3 worldAabbMax;
			multiProxy.m_bridgeProxies[i].m_childBroadphase.GetBroadphaseAabb(out worldAabbMin, out worldAabbMax);
			bool overlapsBroadphase = AabbUtil2.TestAabbAgainstAabb2(ref worldAabbMin, ref worldAabbMax, ref multiProxy.m_aabbMin, ref multiProxy.m_aabbMax);
			if (!overlapsBroadphase)
			{
				//remove it now
				BridgeProxy bridgeProxy = multiProxy.m_bridgeProxies[i];
				BroadphaseProxy childProxy = bridgeProxy.m_childProxy;
				bridgeProxy.m_childBroadphase.DestroyProxy(childProxy, dispatcher);
				multiProxy.m_bridgeProxies.RemoveAtQuick(i);
			}
		}

		/*

            if (1)
            {

                //find broadphase that contain this multiProxy
                int numChildBroadphases = getBroadphaseArray().size();
                for (int i=0;i<numChildBroadphases;i++)
                {
                    btBroadphaseInterface* childBroadphase = getBroadphaseArray()[i];
                    btVector3 worldAabbMin,worldAabbMax;
                    childBroadphase->getBroadphaseAabb(worldAabbMin,worldAabbMax);
                    bool overlapsBroadphase = TestAabbAgainstAabb2(worldAabbMin,worldAabbMax,multiProxy->m_aabbMin,multiProxy->m_aabbMax);

                //	fullyContained = fullyContained || boxIsContainedWithinBox(worldAabbMin,worldAabbMax,multiProxy->m_aabbMin,multiProxy->m_aabbMax);
                    int containingBroadphaseIndex = -1;

                    //if already contains this

                    for (int i=0;i<multiProxy->m_bridgeProxies.size();i++)
                    {
                        if (multiProxy->m_bridgeProxies[i]->m_childBroadphase == childBroadphase)
                        {
                            containingBroadphaseIndex = i;
                        }
                        alreadyInSimple = alreadyInSimple || (multiProxy->m_bridgeProxies[i]->m_childBroadphase == m_simpleBroadphase);
                    }

                    if (overlapsBroadphase)
                    {
                        if (containingBroadphaseIndex<0)
                        {
                            btBroadphaseProxy* childProxy = childBroadphase->createProxy(aabbMin,aabbMax,multiProxy->m_shapeType,multiProxy->m_clientObject,multiProxy->m_collisionFilterGroup,multiProxy->m_collisionFilterMask, dispatcher);
                            childProxy->m_multiSapParentProxy = multiProxy;
                            addToChildBroadphase(multiProxy,childProxy,childBroadphase);
                        }
                    } else
                    {
                        if (containingBroadphaseIndex>=0)
                        {
                            //remove
                            btBridgeProxy* bridgeProxy = multiProxy->m_bridgeProxies[containingBroadphaseIndex];

                            btBroadphaseProxy* childProxy = bridgeProxy->m_childProxy;
                            bridgeProxy->m_childBroadphase->destroyProxy(childProxy,dispatcher);

                            multiProxy->m_bridgeProxies.swap( containingBroadphaseIndex,multiProxy->m_bridgeProxies.size()-1);
                            multiProxy->m_bridgeProxies.pop_back();
                        }
                    }
                }


                ///If we are in no other child broadphase, stick the proxy in the global 'simple' broadphase (brute force)
                ///hopefully we don't end up with many entries here (can assert/provide feedback on stats)
                if (0)//!multiProxy->m_bridgeProxies.size())
                {
                    ///we don't pass the userPtr but our multisap proxy. We need to patch this, before processing an actual collision
                    ///this is needed to be able to calculate the aabb overlap
                    btBroadphaseProxy* childProxy = m_simpleBroadphase->createProxy(aabbMin,aabbMax,multiProxy->m_shapeType,multiProxy->m_clientObject,multiProxy->m_collisionFilterGroup,multiProxy->m_collisionFilterMask, dispatcher);
                    childProxy->m_multiSapParentProxy = multiProxy;
                    addToChildBroadphase(multiProxy,childProxy,m_simpleBroadphase);
                }
            }

            if (!multiProxy->m_bridgeProxies.size())
            {
                ///we don't pass the userPtr but our multisap proxy. We need to patch this, before processing an actual collision
                ///this is needed to be able to calculate the aabb overlap
                btBroadphaseProxy* childProxy = m_simpleBroadphase->createProxy(aabbMin,aabbMax,multiProxy->m_shapeType,multiProxy->m_clientObject,multiProxy->m_collisionFilterGroup,multiProxy->m_collisionFilterMask, dispatcher);
                childProxy->m_multiSapParentProxy = multiProxy;
                addToChildBroadphase(multiProxy,childProxy,m_simpleBroadphase);
            }
        */
		//update
		for (int i = 0; i < multiProxy.m_bridgeProxies.Count; i++)
		{
			BridgeProxy bridgeProxyRef = multiProxy.m_bridgeProxies[i];
			bridgeProxyRef.m_childBroadphase.SetAabb(bridgeProxyRef.m_childProxy, ref aabbMin, ref aabbMax, dispatcher);
		}
	}

	public virtual void GetAabb(BroadphaseProxy proxy, out IndexedVector3 aabbMin, out IndexedVector3 aabbMax)
	{
		MultiSapProxy multiProxy = (MultiSapProxy)(proxy);
		aabbMin = multiProxy.m_aabbMin;
		aabbMax = multiProxy.m_aabbMax;
	}

	public virtual void RayTest(ref IndexedVector3 rayFrom, ref IndexedVector3 rayTo, BroadphaseRayCallback rayCallback)
	{
		IndexedVector3 min = MathUtil.MIN_VECTOR;
		IndexedVector3 max = MathUtil.MAX_VECTOR;
		RayTest(ref rayFrom, ref rayTo, rayCallback, ref min, ref max);
	}

	public virtual void RayTest(ref IndexedVector3 rayFrom, ref IndexedVector3 rayTo, BroadphaseRayCallback rayCallback, ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax)
	{
		for (int i = 0; i < m_multiSapProxies.Count; i++)
		{
			rayCallback.Process(m_multiSapProxies[i]);
		}
	}

	public virtual void AabbTest(ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax, IBroadphaseAabbCallback callback)
	{
	// not sure why this isn't implemented in the c++ version?
	}

	public void AddToChildBroadphase(MultiSapProxy parentMultiSapProxy, BroadphaseProxy childProxy, IBroadphaseInterface childBroadphase)
	{
		BridgeProxy bridgeProxyRef = new BridgeProxy();
		bridgeProxyRef.m_childProxy = childProxy;
		bridgeProxyRef.m_childBroadphase = childBroadphase;
		parentMultiSapProxy.m_bridgeProxies.Add(bridgeProxyRef);
	}

	public void CalculateOverlappingPairs(IDispatcher dispatcher)
	{
		if (!m_stopUpdating && GetOverlappingPairCache().HasDeferredRemoval())
		{
			IList<BroadphasePair> overlappingPairArray = GetOverlappingPairCache().GetOverlappingPairArray();
			((List<BroadphasePair>)overlappingPairArray).Sort();
			m_invalidPair = 0;
			int i;
			BroadphasePair previousPair = new BroadphasePair();
			previousPair.m_pProxy0 = null;
			previousPair.m_pProxy1 = null;
			previousPair.m_algorithm = null;
			for (i = 0; i < overlappingPairArray.Count; i++)
			{
				BroadphasePair pair = overlappingPairArray[i];
				MultiSapProxy aProxy0 = pair.m_pProxy0 != null ? (MultiSapProxy)pair.m_pProxy0.m_multiSapParentProxy : null;
				MultiSapProxy aProxy1 = pair.m_pProxy1 != null ? (MultiSapProxy)pair.m_pProxy1.m_multiSapParentProxy : null;
				MultiSapProxy bProxy0 = previousPair.m_pProxy0 != null ? (MultiSapProxy)previousPair.m_pProxy0.m_multiSapParentProxy : null;
				MultiSapProxy bProxy1 = previousPair.m_pProxy1 != null ? (MultiSapProxy)previousPair.m_pProxy1.m_multiSapParentProxy : null;
				bool isDuplicate = (aProxy0 == bProxy0) && (aProxy1 == bProxy1);
				previousPair = pair;
				bool needsRemoval = false;
				if (!isDuplicate)
				{
					bool hasOverlap = TestAabbOverlap(pair.m_pProxy0, pair.m_pProxy1);
					if (hasOverlap)
					{
						needsRemoval = false; //callback->processOverlap(pair);
					}
					else
					{
						needsRemoval = true;
					}
				}
				else
				{
					//remove duplicate
					needsRemoval = true;
					{
					}
				}

				if (needsRemoval)
				{
					GetOverlappingPairCache().CleanOverlappingPair(pair, dispatcher);
					//		m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1);
					//		m_overlappingPairArray.pop_back();
					pair.m_pProxy0 = null;
					pair.m_pProxy1 = null;
					m_invalidPair++;
					BulletGlobals.gOverlappingPairs--;
				}
			}
		//#define CLEAN_INVALID_PAIRS 1
		//#ifdef CLEAN_INVALID_PAIRS
		//    //perform a sort, to sort 'invalid' pairs to the end
		//    //overlappingPairArray.heapSort(btMultiSapBroadphasePairSortPredicate());
		//    overlappingPairArray.quickSort(btMultiSapBroadphasePairSortPredicate());
		//    overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair);
		//    m_invalidPair = 0;
		//#endif//CLEAN_INVALID_PAIRS
		//printf("overlappingPairArray.size()=%d\n",overlappingPairArray.size());
		}
	}

	public bool TestAabbOverlap(BroadphaseProxy childProxy0, BroadphaseProxy childProxy1)
	{
		MultiSapProxy multiSapProxy0 = (MultiSapProxy)childProxy0.m_multiSapParentProxy;
		MultiSapProxy multiSapProxy1 = (MultiSapProxy)childProxy1.m_multiSapParentProxy;
		return AabbUtil2.TestAabbAgainstAabb2(ref multiSapProxy0.m_aabbMin, ref multiSapProxy0.m_aabbMax, ref multiSapProxy1.m_aabbMin, ref multiSapProxy1.m_aabbMax);
	}

	public virtual IOverlappingPairCache GetOverlappingPairCache()
	{
		return m_overlappingPairs;
	}

	public virtual void GetBroadphaseAabb(out IndexedVector3 aabbMin, out IndexedVector3 aabbMax)
	{
		aabbMin = MathUtil.MIN_VECTOR;
		aabbMax = MathUtil.MAX_VECTOR;
	}

	public void BuildTree(ref IndexedVector3 bvhAabbMin, ref IndexedVector3 bvhAabbMax)
	{
		m_optimizedAabbTree = new QuantizedBvh();
		m_optimizedAabbTree.SetQuantizationValues(ref bvhAabbMin, ref bvhAabbMax);
		IList<QuantizedBvhNode> nodes = m_optimizedAabbTree.GetLeafNodeArray();
		for (int i = 0; i < m_sapBroadphases.Count; i++)
		{
			QuantizedBvhNode node = new QuantizedBvhNode();
			IndexedVector3 aabbMin;
			IndexedVector3 aabbMax;
			m_sapBroadphases[i].GetBroadphaseAabb(out aabbMin, out aabbMax);
			m_optimizedAabbTree.Quantize(out node.m_quantizedAabbMin, ref aabbMin, false);
			m_optimizedAabbTree.Quantize(out node.m_quantizedAabbMax, ref aabbMax, true);
			int partId = 0;
			node.m_escapeIndexOrTriangleIndex = (partId << (31 - QuantizedBvh.MAX_NUM_PARTS_IN_BITS)) | i;
			nodes.Add(node);
		}

		m_optimizedAabbTree.BuildInternal();
	}

	public virtual void PrintStats()
	{
	}

	//public void quicksort(IList<BroadphasePair> a, int lo, int hi);
	public virtual void ResetPool(IDispatcher dispatcher)
	{
	// not yet
	}

	private IList<IBroadphaseInterface> m_sapBroadphases = new List<IBroadphaseInterface>();
	private SimpleBroadphase m_simpleBroadphase;
	private IOverlappingPairCache m_overlappingPairs;
	private QuantizedBvh m_optimizedAabbTree;
	private bool m_ownsPairCache;
	private IOverlapFilterCallback m_filterCallback;
	private int m_invalidPair;
	protected IList<MultiSapProxy> m_multiSapProxies;
	protected bool m_stopUpdating = false;
	class MyNodeOverlapCallback : INodeOverlapCallback
	{
		MultiSapBroadphase m_multiSap;
		MultiSapProxy m_multiProxy;
		IDispatcher m_dispatcher;
		public MyNodeOverlapCallback(MultiSapBroadphase multiSap, MultiSapProxy multiProxy, IDispatcher dispatcher)
		{
			m_multiSap = multiSap;
			m_multiProxy = multiProxy;
			m_dispatcher = dispatcher;
		}

		public virtual void ProcessNode(int nodeSubPart, int broadphaseIndex)
		{
			IBroadphaseInterface childBroadphase = m_multiSap.GetBroadphaseArray()[broadphaseIndex];
			int containingBroadphaseIndex = -1;
			//already found?
			for (int i = 0; i < m_multiProxy.m_bridgeProxies.Count; i++)
			{
				if (m_multiProxy.m_bridgeProxies[i].m_childBroadphase == childBroadphase)
				{
					containingBroadphaseIndex = i;
					break;
				}
			}

			if (containingBroadphaseIndex < 0)
			{
				//add it
				BroadphaseProxy childProxy = childBroadphase.CreateProxy(ref m_multiProxy.m_aabbMin, ref m_multiProxy.m_aabbMax, m_multiProxy.m_shapeType, m_multiProxy.m_clientObject, m_multiProxy.m_collisionFilterGroup, m_multiProxy.m_collisionFilterMask, m_dispatcher, m_multiProxy);
				m_multiSap.AddToChildBroadphase(m_multiProxy, childProxy, childBroadphase);
			}
		}

		public virtual void Cleanup()
		{
		}
	}
}