A C# port of Bullet physics CompoundShape class. Manages child collision shapes, local AABB calculation, an optional dynamic AABB tree (Dbvt), transforms, scaling, inertia and removal/update of children.
/* * 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 CompoundShape : CollisionShape { public CompoundShape() : this(true) { } public CompoundShape(bool enableDynamicAabbTree) { m_children = new ObjectArray<CompoundShapeChild>(); m_localAabbMax = new IndexedVector3(float.MinValue); m_localAabbMin = new IndexedVector3(float.MaxValue); m_collisionMargin = 0f; m_localScaling = new IndexedVector3(1f); m_dynamicAabbTree = null; m_updateRevision = 1; m_shapeType = BroadphaseNativeTypes.COMPOUND_SHAPE_PROXYTYPE; if (enableDynamicAabbTree) { m_dynamicAabbTree = new Dbvt(); } } public override void Cleanup() { base.Cleanup(); if (m_dynamicAabbTree != null) { m_dynamicAabbTree.Cleanup(); m_dynamicAabbTree = null; } } public void AddChildShape(ref IndexedMatrix localTransform, CollisionShape shape) { m_updateRevision++; //m_childTransforms.push_back(localTransform); //m_childShapes.push_back(shape); CompoundShapeChild child = new CompoundShapeChild(); child.m_transform = localTransform; child.m_childShape = shape; child.m_childShapeType = shape.GetShapeType(); child.m_childMargin = shape.GetMargin(); //extend the local aabbMin/aabbMax IndexedVector3 localAabbMin; IndexedVector3 localAabbMax; shape.GetAabb(ref localTransform, out localAabbMin, out localAabbMax); MathUtil.VectorMin(ref localAabbMin, ref m_localAabbMin); MathUtil.VectorMax(ref localAabbMax, ref m_localAabbMax); if (m_dynamicAabbTree != null) { DbvtAabbMm bounds = DbvtAabbMm.FromMM(ref localAabbMin, ref localAabbMax); int index = m_children.Count; child.m_treeNode = m_dynamicAabbTree.Insert(ref bounds, (object)index); } m_children.Add(child); } public virtual void RemoveChildShape(CollisionShape shape) { m_updateRevision++; // Find the children containing the shape specified, and remove those children. //note: there might be multiple children using the same shape! for (int i = m_children.Count - 1; i >= 0; i--) { if (m_children[i].m_childShape == shape) { RemoveChildShapeByIndex(i); } } RecalculateLocalAabb(); } public void RemoveChildShapeByIndex(int childShapeIndex) { m_updateRevision++; { } if (m_dynamicAabbTree != null) { m_dynamicAabbTree.Remove(m_children[childShapeIndex].m_treeNode); } m_children.RemoveAtQuick(childShapeIndex); if (m_dynamicAabbTree != null && m_children.Count > childShapeIndex) { m_children[childShapeIndex].m_treeNode.dataAsInt = childShapeIndex; } //m_children[childShapeIndex] = m_children[m_children.Count - 1]; //m_children.RemoveAt(m_children.Count - 1); //m_children.pop_back(); } public int GetNumChildShapes() { return m_children.Count; } public CollisionShape GetChildShape(int index) { return m_children[index].m_childShape; } public IndexedMatrix GetChildTransform(int index) { return m_children[index].m_transform; } public void UpdateChildTransform(int childIndex, ref IndexedMatrix newChildTransform) { UpdateChildTransform(childIndex, ref newChildTransform, true); } public void UpdateChildTransform(int childIndex, ref IndexedMatrix newChildTransform, bool shouldRecalculateLocalAabb) { m_children[childIndex].m_transform = newChildTransform; if (m_dynamicAabbTree != null) { IndexedVector3 localAabbMin; IndexedVector3 localAabbMax; m_children[childIndex].m_childShape.GetAabb(ref newChildTransform, out localAabbMin, out localAabbMax); DbvtAabbMm bounds = DbvtAabbMm.FromMM(ref localAabbMin, ref localAabbMax); //int index = m_children.Count - 1; m_dynamicAabbTree.Update(m_children[childIndex].m_treeNode, ref bounds); } if (shouldRecalculateLocalAabb) { RecalculateLocalAabb(); } } public override void SetLocalScaling(ref IndexedVector3 scaling) { for (int i = 0; i < m_children.Count; i++) { IndexedMatrix childTrans = GetChildTransform(i); IndexedVector3 childScale = m_children[i].m_childShape.GetLocalScaling(); // childScale = childScale * (childTrans.getBasis() * scaling); childScale = childScale * scaling / m_localScaling; m_children[i].m_childShape.SetLocalScaling(ref childScale); childTrans._origin = ((childTrans._origin) * scaling); UpdateChildTransform(i, ref childTrans, false); } m_localScaling = scaling; RecalculateLocalAabb(); } public void CreateAabbTreeFromChildren() { if (m_dynamicAabbTree == null) { m_dynamicAabbTree = new Dbvt(); for (int index = 0; index < m_children.Count; index++) { CompoundShapeChild child = m_children[index]; //extend the local aabbMin/aabbMax IndexedVector3 localAabbMin, localAabbMax; child.m_childShape.GetAabb(ref child.m_transform, out localAabbMin, out localAabbMax); DbvtAabbMm bounds = DbvtAabbMm.FromMM(ref localAabbMin, ref localAabbMax); child.m_treeNode = m_dynamicAabbTree.Insert(ref bounds, (object)index); } } } public IList<CompoundShapeChild> GetChildList() { return m_children; } public override void GetAabb(ref IndexedMatrix trans, out IndexedVector3 aabbMin, out IndexedVector3 aabbMax) { IndexedVector3 localHalfExtents = .5f * (m_localAabbMax - m_localAabbMin); IndexedVector3 localCenter = .5f * (m_localAabbMax + m_localAabbMin); //avoid an illegal AABB when there are no children if (m_children.Count == 0) { localHalfExtents = IndexedVector3.Zero; localCenter = IndexedVector3.Zero; } float margin = GetMargin(); localHalfExtents += new IndexedVector3(margin); 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)); aabbMin = center - extent; aabbMax = center + extent; } public virtual void RecalculateLocalAabb() { // Recalculate the local aabb // Brute force, it iterates over all the shapes left. m_localAabbMin = new IndexedVector3(float.MaxValue); m_localAabbMax = new IndexedVector3(float.MinValue); //extend the local aabbMin/aabbMax IndexedVector3 localAabbMin; IndexedVector3 localAabbMax; for (int j = 0; j < m_children.Count; j++) { IndexedMatrix foo = m_children[j].m_transform; m_children[j].m_childShape.GetAabb(ref foo, out localAabbMin, out localAabbMax); MathUtil.VectorMin(ref localAabbMin, ref m_localAabbMin); MathUtil.VectorMax(ref localAabbMax, ref m_localAabbMax); } } public override IndexedVector3 GetLocalScaling() { return m_localScaling; } public override void CalculateLocalInertia(float mass, out IndexedVector3 inertia) { //approximation: take the inertia from the aabb for now IndexedMatrix ident = IndexedMatrix.Identity; IndexedVector3 aabbMin; IndexedVector3 aabbMax; GetAabb(ref ident, out aabbMin, out aabbMax); IndexedVector3 halfExtents = (aabbMax - aabbMin) * .5f; float lx = 2f * (halfExtents.X); float ly = 2f * (halfExtents.Y); float lz = 2f * (halfExtents.Z); inertia = new IndexedVector3(mass / (12.0f) * (ly * ly + lz * lz), mass / (12.0f) * (lx * lx + lz * lz), mass / (12.0f) * (lx * lx + ly * ly)); } public override void SetMargin(float margin) { m_collisionMargin = margin; } public override float GetMargin() { return m_collisionMargin; } public override String GetName() { return "Compound"; } //this is optional, but should make collision queries faster, by culling non-overlapping nodes //public void createAabbTreeFromChildren(); public Dbvt GetDynamicAabbTree() { return m_dynamicAabbTree; } public void CalculatePrincipalAxisTransform(IList<float> masses, ref IndexedMatrix principal, out IndexedVector3 inertia) { int n = m_children.Count; float totalMass = 0; IndexedVector3 center = IndexedVector3.Zero; for (int k = 0; k < n; k++) { { } center += m_children[k].m_transform._origin * masses[k]; totalMass += masses[k]; } { } center /= totalMass; principal._origin = center; IndexedBasisMatrix tensor = new IndexedBasisMatrix(); for (int k = 0; k < n; k++) { IndexedVector3 i; m_children[k].m_childShape.CalculateLocalInertia(masses[k], out i); IndexedMatrix t = m_children[k].m_transform; IndexedVector3 o = t._origin - center; //compute inertia tensor in coordinate system of compound shape IndexedBasisMatrix j = t._basis.Transpose(); j._el0 *= i.X; j._el1 *= i.Y; j._el2 *= i.Z; j = t._basis * j; //add inertia tensor tensor._el0 += j._el0; tensor._el1 += j._el1; tensor._el2 += j._el2; //tensor += j; //compute inertia tensor of pointmass at o float o2 = o.LengthSquared(); j._el0 = new IndexedVector3(o2, 0, 0); j._el1 = new IndexedVector3(0, o2, 0); j._el2 = new IndexedVector3(0, 0, o2); j._el0 += o * -o.X; j._el1 += o * -o.Y; j._el2 += o * -o.Z; //add inertia tensor of pointmass tensor._el0 += masses[k] * j._el0; tensor._el1 += masses[k] * j._el1; tensor._el2 += masses[k] * j._el2; } tensor.Diagonalize(out principal, 0.00001f, 20); inertia = new IndexedVector3(tensor._el0.X, tensor._el1.Y, tensor._el2.Z); } public int GetUpdateRevision() { return m_updateRevision; } private ObjectArray<CompoundShapeChild> m_children; private IndexedVector3 m_localAabbMin; private IndexedVector3 m_localAabbMax; private Dbvt m_dynamicAabbTree; private int m_updateRevision; private float m_collisionMargin; protected IndexedVector3 m_localScaling; }