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

A static port of Bullet Physics box-vs-box collision detection. Computes closest points or contact manifold between two BoxShape instances given transforms, using SAT and face/edge intersection logic, and reports contacts via IDiscreteCollisionDetectorInterfaceResult.

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

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

internal static class BoxBoxDetector
{
	//public BoxBoxDetector(BoxShape box1, BoxShape box2)
	//{
	//    m_box1 = box1;
	//    m_box2 = box2;
	//}
	// Work in progress to copy redo the box detector to remove un-necessary allocations
	public static void GetClosestPoints(BoxShape box1, BoxShape box2, ref ClosestPointInput input, ManifoldResult output, IDebugDraw debugDraw, bool swapResults)
	{
		IndexedMatrix transformA = input.m_transformA;
		IndexedMatrix transformB = input.m_transformB;
		int skip = 0;
		Object contact = null;
		IndexedVector3 normal = new IndexedVector3();
		float depth = 0f;
		int return_code = -1;
		int maxc = 4;
		IndexedVector3 translationA = new IndexedVector3(transformA._origin);
		IndexedVector3 translationB = new IndexedVector3(transformB._origin);
		//IndexedVector3 debugExtents = new IndexedVector3(2f, 2f, 2f);
		IndexedVector3 box1Margin = new IndexedVector3(2f * box1.GetHalfExtentsWithMargin());
		IndexedVector3 box2Margin = new IndexedVector3(2f * box2.GetHalfExtentsWithMargin());
		//IndexedVector3 box1Margin = 2f * debugExtents;
		//IndexedVector3 box2Margin = 2f * debugExtents;
		IndexedBasisMatrix rotateA = transformA._basis.Transpose();
		IndexedBasisMatrix rotateB = transformB._basis.Transpose();
		for (int j = 0; j < 3; j++)
		{
			s_temp1[0 + 4 * j] = transformA._basis[j].X;
			s_temp2[0 + 4 * j] = transformB._basis[j].X;
			s_temp1[1 + 4 * j] = transformA._basis[j].Y;
			s_temp2[1 + 4 * j] = transformB._basis[j].Y;
			s_temp1[2 + 4 * j] = transformA._basis[j].Z;
			s_temp2[2 + 4 * j] = transformB._basis[j].Z;
		}

		//s_temp1[0] = rotateA._Row0.X;
		//s_temp1[1] = rotateA._Row0.Y;
		//s_temp1[2] = rotateA._Row0.Z;
		//s_temp1[4] = rotateA._Row1.X;
		//s_temp1[5] = rotateA._Row1.Y;
		//s_temp1[6] = rotateA._Row1.Z;
		//s_temp1[8] = rotateA._Row2.X;
		//s_temp1[9] = rotateA._Row2.X;
		//s_temp1[10] = rotateA._Row2.X;
		//s_temp2[0] = rotateB._Row0.X;
		//s_temp2[1] = rotateB._Row0.Y;
		//s_temp2[2] = rotateB._Row0.Z;
		//s_temp2[4] = rotateB._Row1.X;
		//s_temp2[5] = rotateB._Row1.Y;
		//s_temp2[6] = rotateB._Row1.Z;
		//s_temp2[8] = rotateB._Row2.X;
		//s_temp2[9] = rotateB._Row2.Y;
		//s_temp2[10] = rotateB._Row2.Z;
		DBoxBox2(ref translationA, s_temp1, ref box1Margin, ref translationB, s_temp2, ref box2Margin, ref normal, ref depth, ref return_code, maxc, contact, skip, output);
	}

	private static int DBoxBox2(ref IndexedVector3 p1, float[] R1, ref IndexedVector3 side1, ref IndexedVector3 p2, float[] R2, ref IndexedVector3 side2, ref IndexedVector3 normal, ref float depth, ref int return_code, int maxc, Object contact, int skip, IDiscreteCollisionDetectorInterfaceResult output)
	{
		//IndexedVector3 centerDifference = IndexedVector3.Zero, ppv = IndexedVector3.Zero;
		float[] normalR = null;
		int normalROffsetResult = 0;
		IndexedVector3 A = side1 * 0.5f;
		IndexedVector3 B = side2 * 0.5f;
		int code;
		bool invert_normal;
		// get vector from centers of box 1 to box 2, relative to box 1
		IndexedVector3 p = p2 - p1;
		IndexedVector3 pp = new IndexedVector3();
		DMULTIPLY1_331(ref pp, R1, ref p); // get pp = p relative to body 1
		// for all 15 possible separating axes:
		//   * see if the axis separates the boxes. if so, return 0.
		//   * find the depth of the penetration along the separating axis (s2)
		//   * if this is the largest depth so far, record it.
		// the normal vector will be set to the separating axis with the smallest
		// depth. note: normalR is set to point to a column of R1 or R2 if that is
		// the smallest depth normal so far. otherwise normalR is 0 and normalC is
		// set to a vector relative to body 1. invert_normal is 1 if the sign of
		// the normal should be flipped.
		float R11 = DDOT44(R1, 0, R2, 0);
		float R12 = DDOT44(R1, 0, R2, 1);
		float R13 = DDOT44(R1, 0, R2, 2);
		float R21 = DDOT44(R1, 1, R2, 0);
		float R22 = DDOT44(R1, 1, R2, 1);
		float R23 = DDOT44(R1, 1, R2, 2);
		float R31 = DDOT44(R1, 2, R2, 0);
		float R32 = DDOT44(R1, 2, R2, 1);
		float R33 = DDOT44(R1, 2, R2, 2);
		float Q11 = Math.Abs(R11);
		float Q12 = Math.Abs(R12);
		float Q13 = Math.Abs(R13);
		float Q21 = Math.Abs(R21);
		float Q22 = Math.Abs(R22);
		float Q23 = Math.Abs(R23);
		float Q31 = Math.Abs(R31);
		float Q32 = Math.Abs(R32);
		float Q33 = Math.Abs(R33);
		float s = -float.MaxValue;
		invert_normal = false;
		code = 0;
		int normalROffset = 0;
		// separating axis = u1,u2,u3
		if (TST(pp.X, (A.X + B.X * Q11 + B.Y * Q12 + B.Z * Q13), R1, ref normalR, 0, ref normalROffset, 1, ref code, ref s, ref invert_normal))
			return 0;
		if (TST(pp.Y, (A.Y + B.X * Q21 + B.Y * Q22 + B.Z * Q23), R1, ref normalR, 1, ref normalROffset, 2, ref code, ref s, ref invert_normal))
			return 0;
		if (TST(pp.Z, (A.Z + B.X * Q31 + B.Y * Q32 + B.Z * Q33), R1, ref normalR, 2, ref normalROffset, 3, ref code, ref s, ref invert_normal))
			return 0;
		// separating axis = v1,v2,v3
		if (TST(DDOT41(R2, 0, ref p, 0), (A.X * Q11 + A.Y * Q21 + A.Z * Q31 + B.X), R2, ref normalR, 0, ref normalROffset, 4, ref code, ref s, ref invert_normal))
			return 0;
		if (TST(DDOT41(R2, 1, ref p, 0), (A.X * Q12 + A.Y * Q22 + A.Z * Q32 + B.Y), R2, ref normalR, 1, ref normalROffset, 5, ref code, ref s, ref invert_normal))
			return 0;
		if (TST(DDOT41(R2, 2, ref p, 0), (A.X * Q13 + A.Y * Q23 + A.Z * Q33 + B.Z), R2, ref normalR, 2, ref normalROffset, 6, ref code, ref s, ref invert_normal))
			return 0;
		// note: cross product axes need to be scaled when s is computed.
		// normal (n1,n2,n3) is relative to box 1.
		// separating axis = u1 x (v1,v2,v3)
		//private static bool TST2(float expr1,float expr2,ref IndexedVector3 normal, ref IndexedVector3 normalC,int cc,ref int code)
		IndexedVector3 normalC = new IndexedVector3();
		float fudge2 = 1.0e-5f;
		Q11 += fudge2;
		Q12 += fudge2;
		Q13 += fudge2;
		Q21 += fudge2;
		Q22 += fudge2;
		Q23 += fudge2;
		Q31 += fudge2;
		Q32 += fudge2;
		Q33 += fudge2;
		// separating axis = u1 x (v1,v2,v3)
		if (TST2(pp.Z * R21 - pp.Y * R31, (A.Y * Q31 + A.Z * Q21 + B.Y * Q13 + B.Z * Q12), 0, -R31, R21, ref normalC, ref normalR, 7, ref code, ref s, ref invert_normal))
			return 0;
		if (TST2(pp.Z * R22 - pp.Y * R32, (A.Y * Q32 + A.Z * Q22 + B.X * Q13 + B.Z * Q11), 0, -R32, R22, ref normalC, ref normalR, 8, ref code, ref s, ref invert_normal))
			return 0;
		if (TST2(pp.Z * R23 - pp.Y * R33, (A.Y * Q33 + A.Z * Q23 + B.X * Q12 + B.Y * Q11), 0, -R33, R23, ref normalC, ref normalR, 9, ref code, ref s, ref invert_normal))
			return 0;
		// separating axis = u2 x (v1,v2,v3)
		if (TST2(pp.X * R31 - pp.Z * R11, (A.X * Q31 + A.Z * Q11 + B.Y * Q23 + B.Z * Q22), R31, 0, -R11, ref normalC, ref normalR, 10, ref code, ref s, ref invert_normal))
			return 0;
		if (TST2(pp.X * R32 - pp.Z * R12, (A.X * Q32 + A.Z * Q12 + B.X * Q23 + B.Z * Q21), R32, 0, -R12, ref normalC, ref normalR, 11, ref code, ref s, ref invert_normal))
			return 0;
		if (TST2(pp.X * R33 - pp.Z * R13, (A.X * Q33 + A.Z * Q13 + B.X * Q22 + B.Y * Q21), R33, 0, -R13, ref normalC, ref normalR, 12, ref code, ref s, ref invert_normal))
			return 0;
		// separating axis = u3 x (v1,v2,v3)
		if (TST2(pp.Y * R11 - pp.X * R21, (A.X * Q21 + A.Y * Q11 + B.Y * Q33 + B.Z * Q32), -R21, R11, 0, ref normalC, ref normalR, 13, ref code, ref s, ref invert_normal))
			return 0;
		if (TST2(pp.Y * R12 - pp.X * R22, (A.X * Q22 + A.Y * Q12 + B.X * Q33 + B.Z * Q31), -R22, R12, 0, ref normalC, ref normalR, 14, ref code, ref s, ref invert_normal))
			return 0;
		if (TST2(pp.Y * R13 - pp.X * R23, (A.X * Q23 + A.Y * Q13 + B.X * Q32 + B.Y * Q31), -R23, R13, 0, ref normalC, ref normalR, 15, ref code, ref s, ref invert_normal))
			return 0;
		if (code == 0)
		{
			return 0;
		}

		// if we get to this point, the boxes interpenetrate. compute the normal
		// in global coordinates.
		if (normalR != null)
		{
			normal.X = normalR[0 + normalROffset];
			normal.Y = normalR[4 + normalROffset];
			normal.Z = normalR[8 + normalROffset];
		}
		else
		{
			DMULTIPLY0_331(ref normal, R1, ref normalC);
		}

		if (invert_normal)
		{
			normal = -normal;
		}

		depth = -s;
		// compute contact point(s)
		if (code > 6)
		{
			// an edge from box 1 touches an edge from box 2.
			// find a point pa on the intersecting edge of box 1
			IndexedVector3 pa1 = p1;
			for (int j = 0; j < 3; j++)
			{
				float sign = (DDOT14(ref normal, 0, R1, j) > 0) ? 1.0f : -1.0f;
				for (int i = 0; i < 3; i++)
				{
					pa1[i] += sign * A[j] * R1[i * 4 + j];
				}
			}

			// find a point pb on the intersecting edge of box 2
			IndexedVector3 pb1 = p2;
			//for (i = 0; i < 3; i++) pb[i] = p2[i];
			for (int j = 0; j < 3; j++)
			{
				float sign = (DDOT14(ref normal, 0, R2, j) > 0) ? -1.0f : 1.0f;
				for (int i = 0; i < 3; i++)
				{
					pb1[i] += sign * B[j] * R2[i * 4 + j];
				}
			}

			float alpha, beta;
			IndexedVector3 ua = new IndexedVector3();
			IndexedVector3 ub = new IndexedVector3();
			for (int i = 0; i < 3; i++)
			{
				ua[i] = R1[((code) - 7) / 3 + i * 4];
			}

			for (int i = 0; i < 3; i++)
			{
				ub[i] = R2[((code) - 7) % 3 + i * 4];
			}

			DLineClosestApproach(ref pa1, ref ua, ref pb1, ref ub, out alpha, out beta);
			for (int i = 0; i < 3; i++)
			{
				pa1[i] += ua[i] * alpha;
			}

			for (int i = 0; i < 3; i++)
			{
				pb1[i] += ub[i] * beta;
			}

			{
				//contact[0].pos[i] = float(0.5)*(pa[i]+pb[i]);
				//contact[0].depth = *depth;
				output.AddContactPoint(-normal, pb1, -depth);
				return_code = code;
			}

			return 1;
		}

		// okay, we have a face-something intersection (because the separating
		// axis is perpendicular to a face). define face 'a' to be the reference
		// face (i.e. the normal vector is perpendicular to this) and face 'b' to be
		// the incident face (the closest face of the other box).
		float[] Ra;
		float[] Rb;
		IndexedVector3 pa;
		IndexedVector3 pb;
		IndexedVector3 Sa;
		IndexedVector3 Sb;
		if (code <= 3)
		{
			Ra = R1;
			Rb = R2;
			pa = p1;
			pb = p2;
			Sa = A;
			Sb = B;
		}
		else
		{
			Ra = R2;
			Rb = R1;
			pa = p2;
			pb = p1;
			Sa = B;
			Sb = A;
		}

		// nr = normal vector of reference face dotted with axes of incident box.
		// anr = absolute values of nr.
		IndexedVector3 normal2;
		IndexedVector3 nr = new IndexedVector3();
		IndexedVector3 anr;
		if (code <= 3)
		{
			normal2 = normal;
		}
		else
		{
			normal2 = -normal;
		}

		DMULTIPLY1_331(ref nr, Rb, ref normal2);
		nr.Abs(out anr);
		// find the largest compontent of anr: this corresponds to the normal
		// for the indident face. the other axis numbers of the indicent face
		// are stored in a1,a2.
		int lanr, a1, a2;
		if (anr.Y > anr.X)
		{
			if (anr.Y > anr.Z)
			{
				a1 = 0;
				lanr = 1;
				a2 = 2;
			}
			else
			{
				a1 = 0;
				a2 = 1;
				lanr = 2;
			}
		}
		else
		{
			if (anr.X > anr.Z)
			{
				lanr = 0;
				a1 = 1;
				a2 = 2;
			}
			else
			{
				a1 = 0;
				a2 = 1;
				lanr = 2;
			}
		}

		// compute center point of incident face, in reference-face coordinates
		IndexedVector3 center;
		if (nr[lanr] < 0)
		{
			center = new IndexedVector3(pb.X - pa.X + Sb[lanr] * Rb[lanr], pb.Y - pa.Y + Sb[lanr] * Rb[lanr + 4], pb.Z - pa.Z + Sb[lanr] * Rb[lanr + 8]);
		}
		else
		{
			center = new IndexedVector3(pb.X - pa.X - Sb[lanr] * Rb[lanr], pb.Y - pa.Y - Sb[lanr] * Rb[lanr + 4], pb.Z - pa.Z - Sb[lanr] * Rb[lanr + 8]);
		}

		// find the normal and non-normal axis numbers of the reference box
		int codeN, code1, code2;
		if (code <= 3)
		{
			codeN = code - 1;
		}
		else
		{
			codeN = code - 4;
		}

		if (codeN == 0)
		{
			code1 = 1;
			code2 = 2;
		}
		else if (codeN == 1)
		{
			code1 = 0;
			code2 = 2;
		}
		else
		{
			code1 = 0;
			code2 = 1;
		}

		// find the four corners of the incident face, in reference-face coordinates
		float[] quad = s_quad; // 2D coordinate of incident face (x,y pairs)
		float c1, c2, m11, m12, m21, m22;
		c1 = DDOT14(ref center, 0, Ra, code1);
		c2 = DDOT14(ref center, 0, Ra, code2);
		// optimize this? - we have already computed this data above, but it is not
		// stored in an easy-to-index format. for now it's quicker just to recompute
		// the four dot products.
		m11 = DDOT44(Ra, code1, Rb, a1);
		m12 = DDOT44(Ra, code1, Rb, a2);
		m21 = DDOT44(Ra, code2, Rb, a1);
		m22 = DDOT44(Ra, code2, Rb, a2);
		{
			float k1 = m11 * Sb[a1];
			float k2 = m21 * Sb[a1];
			float k3 = m12 * Sb[a2];
			float k4 = m22 * Sb[a2];
			quad[0] = c1 - k1 - k3;
			quad[1] = c2 - k2 - k4;
			quad[2] = c1 - k1 + k3;
			quad[3] = c2 - k2 + k4;
			quad[4] = c1 + k1 + k3;
			quad[5] = c2 + k2 + k4;
			quad[6] = c1 + k1 - k3;
			quad[7] = c2 + k2 - k4;
		}

		// find the size of the reference face
		//float[] s_rectReferenceFace = new float[2];
		s_rectReferenceFace[0] = Sa[code1];
		s_rectReferenceFace[1] = Sa[code2];
		// intersect the incident and reference faces
		float[] ret = s_ret;
		int n = IntersectRectQuad2(s_rectReferenceFace, quad, ret);
		if (n < 1)
		{
			return 0; // this should never happen
		}

		// convert the intersection points into reference-face coordinates,
		// and compute the contact position and depth for each point. only keep
		// those points that have a positive (penetrating) depth. delete points in
		// the 'ret' array as necessary so that 'point' and 'ret' correspond.
		float[] point = s_point; // penetrating contact points
		float[] dep = s_dep; // depths for those points
		float det1 = 1f / (m11 * m22 - m12 * m21);
		m11 *= det1;
		m12 *= det1;
		m21 *= det1;
		m22 *= det1;
		int cnum = 0; // number of penetrating contact points found
		for (int j = 0; j < n; j++)
		{
			float k1 = m22 * (ret[j * 2] - c1) - m12 * (ret[j * 2 + 1] - c2);
			float k2 = -m21 * (ret[j * 2] - c1) + m11 * (ret[j * 2 + 1] - c2);
			for (int i = 0; i < 3; i++)
			{
				point[cnum * 3 + i] = center[i] + k1 * Rb[i * 4 + a1] + k2 * Rb[i * 4 + a2];
			}

			dep[cnum] = Sa[codeN] - DDOT(ref normal2, 0, point, cnum * 3);
			if (dep[cnum] >= 0)
			{
				ret[cnum * 2] = ret[j * 2];
				ret[cnum * 2 + 1] = ret[j * 2 + 1];
				cnum++;
			}
		}

		if (cnum < 1)
		{
			return 0; // this should never happen
		}

		// we can't generate more contacts than we actually have
		if (maxc > cnum)
		{
			maxc = cnum;
		}

		if (maxc < 1)
		{
			maxc = 1;
		}

		if (cnum <= maxc)
		{
			if (code < 4)
			{
				// we have less contacts than we need, so we use them all
				for (int j = 0; j < cnum; j++)
				{
					IndexedVector3 pointInWorldFA = new IndexedVector3();
					;
					for (int i = 0; i < 3; i++)
					{
						pointInWorldFA[i] = point[j * 3 + i] + pa[i];
					}

					output.AddContactPoint(-normal, pointInWorldFA, -dep[j]);
				}
			}
			else
			{
				// we have less contacts than we need, so we use them all
				for (int j = 0; j < cnum; j++)
				{
					IndexedVector3 pointInWorld = new IndexedVector3();
					;
					for (int i = 0; i < 3; i++)
					{
						pointInWorld[i] = point[j * 3 + i] + pa[i];
					}

					output.AddContactPoint(-normal, pointInWorld, -dep[j]);
				}
			}
		}
		else
		{
			// we have more contacts than are wanted, some of them must be culled.
			// find the deepest point, it is always the first contact.
			int i1 = 0;
			float maxdepth = dep[0];
			for (int i = 1; i < cnum; i++)
			{
				if (dep[i] > maxdepth)
				{
					maxdepth = dep[i];
					i1 = i;
				}
			}

			int[] iret = new int[8];
			CullPoints2(cnum, ret, maxc, i1, iret);
			for (int j = 0; j < maxc; j++)
			{
				//      dContactGeom *con = CONTACT(contact,skip*j);
				//    for (i=0; i<3; i++) con->pos[i] = point[iret[j]*3+i] + pa[i];
				//  con->depth = dep[iret[j]];
				IndexedVector3 posInWorldFA = new IndexedVector3();
				;
				for (int i = 0; i < 3; i++)
				{
					posInWorldFA[i] = point[iret[j] * 3 + i] + pa[i];
				}

				IndexedVector3 pointInWorld = posInWorldFA;
				output.AddContactPoint((-normal), pointInWorld, -dep[iret[j]]);
			}

			cnum = maxc;
		}

		return_code = code;
		return cnum;
	}

	private static void DLineClosestApproach(ref IndexedVector3 pa, ref IndexedVector3 ua, ref IndexedVector3 pb, ref IndexedVector3 ub, out float alpha, out float beta)
	{
		IndexedVector3 p = pb - pa;
		float uaub = ua.Dot(ref ub);
		float q1 = ua.Dot(ref p);
		float q2 = -(ub.Dot(ref p));
		float d = 1 - uaub * uaub;
		if (d <= 0.0001f)
		{
			// @@@ this needs to be made more robust
			alpha = 0f;
			beta = 0f;
		}
		else
		{
			d = 1f / d;
			alpha = (q1 + uaub * q2) * d;
			beta = (uaub * q1 + q2) * d;
		}
	}

	// find all the intersection points between the 2D rectangle with vertices
	// at (+/-h[0],+/-h[1]) and the 2D quadrilateral with vertices (p[0],p[1]),
	// (p[2],p[3]),(p[4],p[5]),(p[6],p[7]).
	//
	// the intersection points are returned as x,y pairs in the 'ret' array.
	// the number of intersection points is returned by the function (this will
	// be in the range 0 to 8).
	static int IntersectRectQuad2(float[] h, float[] p, float[] ret)
	{
		// q (and r) contain nq (and nr) coordinate points for the current (and
		// chopped) polygons
		int nq = 4, nr = 0;
		float[] buffer = s_quadBuffer;
		float[] q = p;
		float[] r = ret;
		for (int dir = 0; dir <= 1; dir++)
		{
			// direction notation: xy[0] = x axis, xy[1] = y axis
			for (int sign = -1; sign <= 1; sign += 2)
			{
				// chop q along the line xy[dir] = sign*h[dir]
				float[] pq = q;
				float[] pr = r;
				int pqIndex = 0;
				int prIndex = 0;
				nr = 0;
				for (int i = nq; i > 0; i--)
				{
					// go through all points in q and all lines between adjacent points
					if (sign * pq[pqIndex + dir] < h[dir])
					{
						// this point is inside the chopping line
						pr[prIndex] = pq[pqIndex];
						pr[prIndex + 1] = pq[pqIndex + 1];
						prIndex += 2;
						nr++;
						if ((nr & 8) != 0)
						{
							q = r;
							goto done;
						}
					}

					float nextQDir = 0f;
					float nextQ1MinusDir = 0f;
					if (i > 1)
					{
						nextQDir = pq[pqIndex + 2 + dir];
						nextQ1MinusDir = pq[pqIndex + 2 + (1 - dir)];
					}
					else
					{
						nextQDir = q[dir];
						nextQ1MinusDir = q[1 - dir];
					}

					if ((sign * pq[pqIndex + dir] < h[dir]) ^ (sign * nextQDir < h[dir]))
					{
						// this line crosses the chopping line
						pr[prIndex + (1 - dir)] = pq[pqIndex + (1 - dir)] + (nextQ1MinusDir - pq[pqIndex + (1 - dir)]) / (nextQDir - pq[pqIndex + dir]) * (sign * h[dir] - pq[pqIndex + dir]);
						pr[prIndex + dir] = sign * h[dir];
						prIndex += 2;
						nr++;
						if ((nr & 8) != 0)
						{
							q = r;
							goto done;
						}
					}

					pqIndex += 2;
				}

				q = r;
				r = (q == ret) ? buffer : ret;
				nq = nr;
			}
		}

		done:
			// data already in ret
			if (q != ret)
			{
				for (int i = 0; i < nr * 2; ++i)
				{
					ret[i] = q[i];
				}
			}

		return nr;
	}

	// given n points in the plane (array p, of size 2*n), generate m points that
	// best represent the whole set. the definition of 'best' here is not
	// predetermined - the idea is to select points that give good box-box
	// collision detection behavior. the chosen point indexes are returned in the
	// array iret (of size m). 'i0' is always the first entry in the array.
	// n must be in the range [1..8]. m must be in the range [1..n]. i0 must be
	// in the range [0..n-1].
	private static void CullPoints2(int n, float[] p, int m, int i0, int[] iret)
	{
		// compute the centroid of the polygon in cx,cy
		int iretIndex = 0;
		int i, j;
		float a, cx, cy, q;
		if (n == 1)
		{
			cx = p[0];
			cy = p[1];
		}
		else if (n == 2)
		{
			cx = 0.5f * (p[0] + p[2]);
			cy = 0.5f * (p[1] + p[3]);
		}
		else
		{
			a = 0;
			cx = 0;
			cy = 0;
			for (i = 0; i < (n - 1); i++)
			{
				q = p[i * 2] * p[i * 2 + 3] - p[i * 2 + 2] * p[i * 2 + 1];
				a += q;
				cx += q * (p[i * 2] + p[i * 2 + 2]);
				cy += q * (p[i * 2 + 1] + p[i * 2 + 3]);
			}

			q = p[n * 2 - 2] * p[1] - p[0] * p[n * 2 - 1];
			if (Math.Abs(a + q) > MathUtil.SIMD_EPSILON)
			{
				a = 1f / (3.0f * (a + q));
			}
			else
			{
				a = 1e30f;
			}

			cx = a * (cx + q * (p[n * 2 - 2] + p[0]));
			cy = a * (cy + q * (p[n * 2 - 1] + p[1]));
		}

		// compute the angle of each point w.r.t. the centroid
		float[] A = s_A;
		for (i = 0; i < n; i++)
		{
			A[i] = (float)Math.Atan2(p[i * 2 + 1] - cy, p[i * 2] - cx);
		}

		// search for points that have angles closest to A[i0] + i*(2*pi/m).
		for (i = 0; i < n; i++)
		{
			s_availablePoints[i] = 1;
		}

		s_availablePoints[i0] = 0;
		iret[0] = i0;
		iretIndex++;
		for (j = 1; j < m; j++)
		{
			a = j * (MathUtil.SIMD_2_PI / m) + A[i0];
			if (a > MathUtil.SIMD_PI)
			{
				a -= MathUtil.SIMD_2_PI;
			}

			float maxdiff = float.MaxValue, diff;
			iret[iretIndex] = i0; // iret is not allowed to keep this value, but it sometimes does, when diff=#QNAN0
			for (i = 0; i < n; i++)
			{
				if (s_availablePoints[i] != 0)
				{
					diff = Math.Abs(A[i] - a);
					if (diff > MathUtil.SIMD_PI)
					{
						diff = MathUtil.SIMD_2_PI - diff;
					}

					if (diff < maxdiff)
					{
						maxdiff = diff;
						iret[iretIndex] = i;
					}
				}
			}

			//#if defined(DEBUG) || defined (_DEBUG)
			//    btAssert (*iret != i0);	// ensure iret got set
			//#endif
			s_availablePoints[iret[iretIndex]] = 0;
			iretIndex++;
		}
	}

	private static bool TST(float expr1, float expr2, float[] norm, ref float[] normalR, int offset, ref int normalROffset, int cc, ref int code, ref float s, ref bool invert_normal)
	{
		float s2 = Math.Abs(expr1) - expr2;
		if (s2 > 0)
		{
			return true;
		}

		if (s2 > s)
		{
			s = s2;
			normalR = norm;
			normalROffset = offset;
			invert_normal = expr1 < 0;
			code = cc;
		}

		return false;
	}

	// note: cross product axes need to be scaled when s is computed.
	// normal (n1,n2,n3) is relative to box 1.
	private static bool TST2(float expr1, float expr2, float n1, float n2, float n3, ref IndexedVector3 normalC, ref float[] normalR, int cc, ref int code, ref float s, ref bool invert_normal)
	{
		float s2 = Math.Abs(expr1) - (expr2);
		if (s2 > MathUtil.SIMD_EPSILON)
		{
			return true;
		}

		float l = (float)Math.Sqrt((n1 * n1) + (n2 * n2) + (n3 * n3));
		if (l > MathUtil.SIMD_EPSILON)
		{
			s2 /= l;
			if (s2 * fudge_factor > s)
			{
				s = s2;
				normalR = null;
				normalC.X = (n1) / l;
				normalC.Y = (n2) / l;
				normalC.Z = (n3) / l;
				invert_normal = ((expr1) < 0);
				invert_normal = ((expr1) < 0);
				code = (cc);
			}
		}

		return false;
	}

	//#define dDOTpq(a,b,p,q) ((a)[0]*(b)[0] + (a)[p]*(b)[q] + (a)[2*(p)]*(b)[2*(q)])
	private static float DDOT(ref IndexedVector3 a, int aOffset, ref IndexedVector3 b, int bOffset)
	{
		//return (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
		//return DDOTpq(ref a, ref b, aOffset, bOffset, 1, 1);
		return a[aOffset] * b[bOffset] + a[1 + aOffset] * b[1 + bOffset] + a[2 + aOffset] * b[2 + bOffset];
	}

	private static float DDOT(ref IndexedVector3 a, int aOffset, float[] b, int bOffset)
	{
		//return DDOTpq(ref a, b, aOffset, bOffset, 1, 1);
		return (a[aOffset] * b[bOffset] + a[1 + aOffset] * b[1 + bOffset] + a[2 + aOffset] * b[2 + bOffset]);
	}

	private static float DDOT(float[] a, int aOffset, ref IndexedVector3 b, int bOffset)
	{
		//return DDOTpq(a, ref b, aOffset, bOffset, 1, 1);
		return (a[aOffset] * b[bOffset] + a[1 + aOffset] * b[1 + bOffset] + a[2 + aOffset] * b[2 + bOffset]);
	}

	private static float DDOT44(float[] a, int aOffset, float[] b, int bOffset)
	{
		//return DDOTpq(a, b, aOffset, bOffset, 4, 4);
		return (a[aOffset] * b[bOffset] + a[4 + aOffset] * b[4 + bOffset] + a[8 + aOffset] * b[8 + bOffset]);
	}

	private static float DDOT41(float[] a, int aOffset, float[] b, int bOffset)
	{
		//return DDOTpq(a, b, aOffset, bOffset, 4, 1);
		return (a[aOffset] * b[bOffset] + a[4 + aOffset] * b[1 + bOffset] + a[8 + aOffset] * b[2 + bOffset]);
	}

	private static float DDOT41(float[] a, int aOffset, ref IndexedVector3 b, int bOffset)
	{
		//return DDOTpq(a, ref b, aOffset, bOffset, 4, 1);
		return (a[aOffset] * b[bOffset] + a[4 + aOffset] * b[1 + bOffset] + a[8 + aOffset] * b[2 + bOffset]);
	}

	private static float DDOT14(float[] a, int aOffset, float[] b, int bOffset)
	{
		//return DDOTpq(a, b, aOffset, bOffset, 1, 4);
		return (a[aOffset] * b[bOffset] + a[1 + aOffset] * b[4 + bOffset] + a[2 + aOffset] * b[8 + bOffset]);
	}

	private static float DDOT14(ref IndexedVector3 a, int aOffset, float[] b, int bOffset)
	{
		//return DDOTpq(ref a, b, aOffset, bOffset, 1, 4);
		return (a[aOffset] * b[bOffset] + a[1 + aOffset] * b[4 + bOffset] + a[2 + aOffset] * b[8 + bOffset]);
	}

	public static void DMULTIPLY1_331(ref IndexedVector3 A, float[] B, ref IndexedVector3 C)
	{
		A.X = DDOT41(B, 0, ref C, 0);
		A.Y = DDOT41(B, 1, ref C, 0);
		A.Z = DDOT41(B, 2, ref C, 0);
	}

	public static void DMULTIPLY0_331(ref IndexedVector3 A, float[] B, ref IndexedVector3 C)
	{
		A.X = DDOT(B, 0, ref C, 0);
		A.Y = DDOT(B, 4, ref C, 0);
		A.Z = DDOT(B, 8, ref C, 0);
	}

	//public static void DMULTIPLY0_331(float[] A, float[] B, float[] C)
	//{
	//    A[0] = DDOT(B, 0, C, 0);
	//    A[1] = DDOT(B, 4, C, 0);
	//    A[2] = DDOT(B, 8, C, 0);
	//}
	//private BoxShape m_box1;
	//private BoxShape m_box2;
	private static float fudge_factor = 1.05f;
	private static float[] s_buffer = new float[12];
	private static float[] s_quadBuffer = new float[16];
	private static float[] s_temp1 = new float[12];
	private static float[] s_temp2 = new float[12];
	private static float[] s_quad = new float[8];
	private static float[] s_ret = new float[16];
	private static float[] s_point = new float[3 * 8]; // penetrating contact points
	private static float[] s_dep = new float[8]; // depths for those points
	private static float[] s_A = new float[8];
	private static float[] s_rectReferenceFace = new float[2];
	private static int[] s_availablePoints = new int[8];
}