Code/HumanoidRetargeter/Core/Formats/Mmd/Bullet/AabbUtil2.cs

Utility class from a Bullet physics port that provides axis-aligned bounding box (AABB) helpers. It includes expansions, overlap tests (point, AABB, triangle), ray-vs-AABB tests, quantized AABB tests, and AABB transforms using matrices.

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

namespace HumanoidRetargeter.Core.Formats.Mmd.Bullet;

internal static class AabbUtil2
{
	public static void AabbExpand(ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax, ref IndexedVector3 expansionMin, ref IndexedVector3 expansionMax)
	{
		aabbMin = aabbMin + expansionMin;
		aabbMax = aabbMax + expansionMax;
	}

	public static bool TestPointAgainstAabb2(ref IndexedVector3 aabbMin1, ref IndexedVector3 aabbMax1, ref IndexedVector3 point)
	{
		bool overlap = true;
		overlap = (aabbMin1.X > point.X || aabbMax1.X < point.X) ? false : overlap;
		overlap = (aabbMin1.Z > point.Z || aabbMax1.Z < point.Z) ? false : overlap;
		overlap = (aabbMin1.Y > point.Y || aabbMax1.Y < point.Y) ? false : overlap;
		return overlap;
	}

	public static bool TestAabbAgainstAabb2(ref IndexedVector3 aabbMin1, ref IndexedVector3 aabbMax1, ref IndexedVector3 aabbMin2, ref IndexedVector3 aabbMax2)
	{
		bool overlap = true;
		overlap = (aabbMin1.X > aabbMax2.X || aabbMax1.X < aabbMin2.X) ? false : overlap;
		overlap = (aabbMin1.Z > aabbMax2.Z || aabbMax1.Z < aabbMin2.Z) ? false : overlap;
		overlap = (aabbMin1.Y > aabbMax2.Y || aabbMax1.Y < aabbMin2.Y) ? false : overlap;
		return overlap;
	}

	public static bool TestTriangleAgainstAabb2(IndexedVector3[] vertices, ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax)
	{
		if (Math.Min(Math.Min(vertices[0].X, vertices[1].X), vertices[2].X) > aabbMax.X)
			return false;
		if (Math.Max(Math.Max(vertices[0].X, vertices[1].X), vertices[2].X) < aabbMin.X)
			return false;
		if (Math.Min(Math.Min(vertices[0].Z, vertices[1].Z), vertices[2].Z) > aabbMax.Z)
			return false;
		if (Math.Max(Math.Max(vertices[0].Z, vertices[1].Z), vertices[2].Z) < aabbMin.Z)
			return false;
		if (Math.Min(Math.Min(vertices[0].Y, vertices[1].Y), vertices[2].Y) > aabbMax.Y)
			return false;
		if (Math.Max(Math.Max(vertices[0].Y, vertices[1].Y), vertices[2].Y) < aabbMin.Y)
			return false;
		return true;
	}

	//public static bool TestTriangleAgainstAabb2(IList<IndexedVector3> vertices,
	//                            ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax)
	//{
	//    if (Math.Min(Math.Min(vertices[0].X, vertices[1].X), vertices[2].X) > aabbMax.X) return false;
	//    if (Math.Max(Math.Max(vertices[0].X, vertices[1].X), vertices[2].X) < aabbMin.X) return false;
	//    if (Math.Min(Math.Min(vertices[0].Z, vertices[1].Z), vertices[2].Z) > aabbMax.Z) return false;
	//    if (Math.Max(Math.Max(vertices[0].Z, vertices[1].Z), vertices[2].Z) < aabbMin.Z) return false;
	//    if (Math.Min(Math.Min(vertices[0].Y, vertices[1].Y), vertices[2].Y) > aabbMax.Y) return false;
	//    if (Math.Max(Math.Max(vertices[0].Y, vertices[1].Y), vertices[2].Y) < aabbMin.Y) return false;
	//    return true;
	//}
	public static int Outcode(ref IndexedVector3 p, ref IndexedVector3 halfExtent)
	{
		return (p.X < -halfExtent.X ? 0x01 : 0x0) | (p.X > halfExtent.X ? 0x08 : 0x0) | (p.Y < -halfExtent.Y ? 0x02 : 0x0) | (p.Y > halfExtent.Y ? 0x10 : 0x0) | (p.Z < -halfExtent.Z ? 0x4 : 0x0) | (p.Z > halfExtent.Z ? 0x20 : 0x0);
	}

	public static bool RayAabb2(ref IndexedVector3 rayFrom, ref IndexedVector3 rayInvDirection, bool[] raySign, IndexedVector3[] bounds, out float tmin, float lambda_min, float lambda_max)
	{
		float tmax, tymin, tymax, tzmin, tzmax;
		tmin = (bounds[raySign[0] ? 1 : 0].X - rayFrom.X) * rayInvDirection.X;
		tmax = (bounds[1 - (raySign[0] ? 1 : 0)].X - rayFrom.X) * rayInvDirection.X;
		tymin = (bounds[raySign[1] ? 1 : 0].Y - rayFrom.Y) * rayInvDirection.Y;
		tymax = (bounds[1 - (raySign[1] ? 1 : 0)].Y - rayFrom.Y) * rayInvDirection.Y;
		if ((tmin > tymax) || (tymin > tmax))
			return false;
		if (tymin > tmin)
			tmin = tymin;
		if (tymax < tmax)
			tmax = tymax;
		tzmin = (bounds[(raySign[2] ? 1 : 0)].Z - rayFrom.Z) * rayInvDirection.Z;
		tzmax = (bounds[1 - (raySign[2] ? 1 : 0)].Z - rayFrom.Z) * rayInvDirection.Z;
		if ((tmin > tzmax) || (tzmin > tmax))
			return false;
		if (tzmin > tmin)
			tmin = tzmin;
		if (tzmax < tmax)
			tmax = tzmax;
		return ((tmin < lambda_max) && (tmax > lambda_min));
	}

	public static bool RayAabb2Alt(ref IndexedVector3 rayFrom, ref IndexedVector3 rayInvDirection, bool raySign0, bool raySign1, bool raySign2, ref IndexedVector3 minBounds, ref IndexedVector3 maxBounds, out float tmin, float lambda_min, float lambda_max)
	{
		float tmax, tymin, tymax, tzmin, tzmax;
		tmin = ((raySign0 ? maxBounds : minBounds).X - rayFrom.X) * rayInvDirection.X;
		tmax = ((raySign0 ? minBounds : maxBounds).X - rayFrom.X) * rayInvDirection.X;
		tymin = ((raySign1 ? maxBounds : minBounds).Y - rayFrom.Y) * rayInvDirection.Y;
		tymax = ((raySign1 ? minBounds : maxBounds).Y - rayFrom.Y) * rayInvDirection.Y;
		if ((tmin > tymax) || (tymin > tmax))
			return false;
		if (tymin > tmin)
			tmin = tymin;
		if (tymax < tmax)
			tmax = tymax;
		tzmin = ((raySign2 ? maxBounds : minBounds).Z - rayFrom.Z) * rayInvDirection.Z;
		tzmax = ((raySign2 ? minBounds : maxBounds).Z - rayFrom.Z) * rayInvDirection.Z;
		if ((tmin > tzmax) || (tzmin > tmax))
			return false;
		if (tzmin > tmin)
			tmin = tzmin;
		if (tzmax < tmax)
			tmax = tzmax;
		return ((tmin < lambda_max) && (tmax > lambda_min));
	}

	public static bool RayAabb(IndexedVector3 rayFrom, IndexedVector3 rayTo, ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax, ref float param, out IndexedVector3 normal)
	{
		return RayAabb(ref rayFrom, ref rayTo, ref aabbMin, ref aabbMax, ref param, out normal);
	}

	public static bool RayAabb(ref IndexedVector3 rayFrom, ref IndexedVector3 rayTo, ref IndexedVector3 aabbMin, ref IndexedVector3 aabbMax, ref float param, out IndexedVector3 normal)
	{
		IndexedVector3 aabbHalfExtent = (aabbMax - aabbMin) * 0.5f;
		IndexedVector3 aabbCenter = (aabbMax + aabbMin) * 0.5f;
		IndexedVector3 source = rayFrom - aabbCenter;
		IndexedVector3 target = rayTo - aabbCenter;
		int sourceOutcode = Outcode(ref source, ref aabbHalfExtent);
		int targetOutcode = Outcode(ref target, ref aabbHalfExtent);
		if ((sourceOutcode & targetOutcode) == 0x0)
		{
			float lambda_enter = 0f;
			float lambda_exit = param;
			IndexedVector3 r = target - source;
			int i;
			float normSign = 1;
			IndexedVector3 hitNormal = IndexedVector3.Zero;
			int bit = 1;
			for (int j = 0; j < 2; j++)
			{
				for (i = 0; i != 3; ++i)
				{
					if ((sourceOutcode & bit) != 0)
					{
						float lambda = (-source[i] - aabbHalfExtent[i] * normSign) / r[i];
						if (lambda_enter <= lambda)
						{
							lambda_enter = lambda;
							hitNormal = IndexedVector3.Zero;
							hitNormal[i] = normSign;
						}
					}
					else if ((targetOutcode & bit) != 0)
					{
						float lambda = (-source[i] - aabbHalfExtent[i] * normSign) / r[i];
						lambda_exit = Math.Min(lambda_exit, lambda);
					}

					bit <<= 1;
				}

				normSign = -1f;
			}

			if (lambda_enter <= lambda_exit)
			{
				param = lambda_enter;
				normal = hitNormal;
				return true;
			}
		}

		param = 0f;
		normal = IndexedVector3.Zero;
		return false;
	}

	//This block replaces the block below and uses no branches, and replaces the 8 bit return with a 32 bit return for improved performance (~3x on XBox 360)
	public static bool TestQuantizedAabbAgainstQuantizedAabb(ref UShortVector3 aabbMin1, ref UShortVector3 aabbMax1, ref UShortVector3 aabbMin2, ref UShortVector3 aabbMax2)
	{
		//return ((aabbMin1[0] <= aabbMax2[0]) && (aabbMax1[0] >= aabbMin2[0])
		//    & (aabbMin1[2] <= aabbMax2[2]) && (aabbMax1[2] >= aabbMin2[2])
		//    & (aabbMin1[1] <= aabbMax2[1]) && (aabbMax1[1] >= aabbMin2[1]));
		//return (MathUtil.select(val,1, 0));
		// MAN - Not sure why this version isn't just replaced by anding all of the above, it's still not conditional as theres a quick ref.
		bool overlap = true;
		overlap = (aabbMin1.X > aabbMax2.X || aabbMax1.X < aabbMin2.X) ? false : overlap;
		overlap = (aabbMin1.Z > aabbMax2.Z || aabbMax1.Z < aabbMin2.Z) ? false : overlap;
		overlap = (aabbMin1.Y > aabbMax2.Y || aabbMax1.Y < aabbMin2.Y) ? false : overlap;
		return overlap;
	}

	public static void TransformAabb(IndexedVector3 halfExtents, float margin, ref IndexedMatrix t, out IndexedVector3 aabbMinOut, out IndexedVector3 aabbMaxOut)
	{
		TransformAabb(ref halfExtents, margin, ref t, out aabbMinOut, out aabbMaxOut);
	}

	public static void TransformAabb(ref IndexedVector3 halfExtents, float margin, ref IndexedMatrix t, out IndexedVector3 aabbMinOut, out IndexedVector3 aabbMaxOut)
	{
		IndexedVector3 halfExtentsWithMargin = halfExtents + new IndexedVector3(margin);
		IndexedBasisMatrix abs_b = t._basis.Absolute();
		IndexedVector3 center = t._origin;
		IndexedVector3 extent = new IndexedVector3(abs_b._el0.Dot(ref halfExtentsWithMargin), abs_b._el1.Dot(ref halfExtentsWithMargin), abs_b._el2.Dot(ref halfExtentsWithMargin));
		aabbMinOut = center - extent;
		aabbMaxOut = center + extent;
	}

	public static void TransformAabb(IndexedVector3 localAabbMin, IndexedVector3 localAabbMax, float margin, ref IndexedMatrix trans, out IndexedVector3 aabbMinOut, out IndexedVector3 aabbMaxOut)
	{
		{
		}

		{
		}

		{
		}

		IndexedVector3 localHalfExtents = -.5f * (localAabbMax - localAabbMin);
		localHalfExtents += new IndexedVector3(margin);
		IndexedVector3 localCenter = 0.5f * (localAabbMax + localAabbMin);
		IndexedBasisMatrix abs_b = trans._basis.Absolute();
		IndexedVector3 center = trans * localCenter;
		IndexedVector3 extent = new IndexedVector3(abs_b._el0.Dot(ref localHalfExtents), abs_b._el1.Dot(ref localHalfExtents), abs_b._el2.Dot(ref localHalfExtents));
		aabbMinOut = center - extent;
		aabbMaxOut = center + extent;
	}

	public static void TransformAabb(ref IndexedVector3 localAabbMin, ref IndexedVector3 localAabbMax, float margin, ref IndexedMatrix trans, out IndexedVector3 aabbMinOut, out IndexedVector3 aabbMaxOut)
	{
		{
		}

		{
		}

		{
		}

		IndexedVector3 localHalfExtents = 0.5f * (localAabbMax - localAabbMin);
		localHalfExtents += new IndexedVector3(margin);
		IndexedVector3 localCenter = 0.5f * (localAabbMax + localAabbMin);
		IndexedBasisMatrix abs_b = trans._basis.Absolute();
		IndexedVector3 center = trans * localCenter;
		IndexedVector3 extent = new IndexedVector3(abs_b._el0.Dot(ref localHalfExtents), abs_b._el1.Dot(ref localHalfExtents), abs_b._el2.Dot(ref localHalfExtents));
		aabbMinOut = center - extent;
		aabbMaxOut = center + extent;
	}
}