1 /*
2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2006 Erwin Coumans  https://bulletphysics.org
4 
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
10 
11 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.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
14 */
15 
16 #include "btSphereBoxCollisionAlgorithm.h"
17 #include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
18 #include "BulletCollision/CollisionShapes/btSphereShape.h"
19 #include "BulletCollision/CollisionShapes/btBoxShape.h"
20 #include "BulletCollision/CollisionDispatch/btCollisionObject.h"
21 #include "BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h"
22 //#include <stdio.h>
23 
btSphereBoxCollisionAlgorithm(btPersistentManifold * mf,const btCollisionAlgorithmConstructionInfo & ci,const btCollisionObjectWrapper * col0Wrap,const btCollisionObjectWrapper * col1Wrap,bool isSwapped)24 btSphereBoxCollisionAlgorithm::btSphereBoxCollisionAlgorithm(btPersistentManifold* mf, const btCollisionAlgorithmConstructionInfo& ci, const btCollisionObjectWrapper* col0Wrap, const btCollisionObjectWrapper* col1Wrap, bool isSwapped)
25 	: btActivatingCollisionAlgorithm(ci, col0Wrap, col1Wrap),
26 	  m_ownManifold(false),
27 	  m_manifoldPtr(mf),
28 	  m_isSwapped(isSwapped)
29 {
30 	const btCollisionObjectWrapper* sphereObjWrap = m_isSwapped ? col1Wrap : col0Wrap;
31 	const btCollisionObjectWrapper* boxObjWrap = m_isSwapped ? col0Wrap : col1Wrap;
32 
33 	if (!m_manifoldPtr && m_dispatcher->needsCollision(sphereObjWrap->getCollisionObject(), boxObjWrap->getCollisionObject()))
34 	{
35 		m_manifoldPtr = m_dispatcher->getNewManifold(sphereObjWrap->getCollisionObject(), boxObjWrap->getCollisionObject());
36 		m_ownManifold = true;
37 	}
38 }
39 
~btSphereBoxCollisionAlgorithm()40 btSphereBoxCollisionAlgorithm::~btSphereBoxCollisionAlgorithm()
41 {
42 	if (m_ownManifold)
43 	{
44 		if (m_manifoldPtr)
45 			m_dispatcher->releaseManifold(m_manifoldPtr);
46 	}
47 }
48 
processCollision(const btCollisionObjectWrapper * body0Wrap,const btCollisionObjectWrapper * body1Wrap,const btDispatcherInfo & dispatchInfo,btManifoldResult * resultOut)49 void btSphereBoxCollisionAlgorithm::processCollision(const btCollisionObjectWrapper* body0Wrap, const btCollisionObjectWrapper* body1Wrap, const btDispatcherInfo& dispatchInfo, btManifoldResult* resultOut)
50 {
51 	(void)dispatchInfo;
52 	(void)resultOut;
53 	if (!m_manifoldPtr)
54 		return;
55 
56 	const btCollisionObjectWrapper* sphereObjWrap = m_isSwapped ? body1Wrap : body0Wrap;
57 	const btCollisionObjectWrapper* boxObjWrap = m_isSwapped ? body0Wrap : body1Wrap;
58 
59 	btVector3 pOnBox;
60 
61 	btVector3 normalOnSurfaceB;
62 	btScalar penetrationDepth;
63 	btVector3 sphereCenter = sphereObjWrap->getWorldTransform().getOrigin();
64 	const btSphereShape* sphere0 = (const btSphereShape*)sphereObjWrap->getCollisionShape();
65 	btScalar radius = sphere0->getRadius();
66 	btScalar maxContactDistance = m_manifoldPtr->getContactBreakingThreshold();
67 
68 	resultOut->setPersistentManifold(m_manifoldPtr);
69 
70 	if (getSphereDistance(boxObjWrap, pOnBox, normalOnSurfaceB, penetrationDepth, sphereCenter, radius, maxContactDistance))
71 	{
72 		/// report a contact. internally this will be kept persistent, and contact reduction is done
73 		resultOut->addContactPoint(normalOnSurfaceB, pOnBox, penetrationDepth);
74 	}
75 
76 	if (m_ownManifold)
77 	{
78 		if (m_manifoldPtr->getNumContacts())
79 		{
80 			resultOut->refreshContactPoints();
81 		}
82 	}
83 }
84 
calculateTimeOfImpact(btCollisionObject * col0,btCollisionObject * col1,const btDispatcherInfo & dispatchInfo,btManifoldResult * resultOut)85 btScalar btSphereBoxCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0, btCollisionObject* col1, const btDispatcherInfo& dispatchInfo, btManifoldResult* resultOut)
86 {
87 	(void)resultOut;
88 	(void)dispatchInfo;
89 	(void)col0;
90 	(void)col1;
91 
92 	//not yet
93 	return btScalar(1.);
94 }
95 
getSphereDistance(const btCollisionObjectWrapper * boxObjWrap,btVector3 & pointOnBox,btVector3 & normal,btScalar & penetrationDepth,const btVector3 & sphereCenter,btScalar fRadius,btScalar maxContactDistance)96 bool btSphereBoxCollisionAlgorithm::getSphereDistance(const btCollisionObjectWrapper* boxObjWrap, btVector3& pointOnBox, btVector3& normal, btScalar& penetrationDepth, const btVector3& sphereCenter, btScalar fRadius, btScalar maxContactDistance)
97 {
98 	const btBoxShape* boxShape = (const btBoxShape*)boxObjWrap->getCollisionShape();
99 	btVector3 const& boxHalfExtent = boxShape->getHalfExtentsWithoutMargin();
100 	btScalar boxMargin = boxShape->getMargin();
101 	penetrationDepth = 1.0f;
102 
103 	// convert the sphere position to the box's local space
104 	btTransform const& m44T = boxObjWrap->getWorldTransform();
105 	btVector3 sphereRelPos = m44T.invXform(sphereCenter);
106 
107 	// Determine the closest point to the sphere center in the box
108 	btVector3 closestPoint = sphereRelPos;
109 	closestPoint.setX(btMin(boxHalfExtent.getX(), closestPoint.getX()));
110 	closestPoint.setX(btMax(-boxHalfExtent.getX(), closestPoint.getX()));
111 	closestPoint.setY(btMin(boxHalfExtent.getY(), closestPoint.getY()));
112 	closestPoint.setY(btMax(-boxHalfExtent.getY(), closestPoint.getY()));
113 	closestPoint.setZ(btMin(boxHalfExtent.getZ(), closestPoint.getZ()));
114 	closestPoint.setZ(btMax(-boxHalfExtent.getZ(), closestPoint.getZ()));
115 
116 	btScalar intersectionDist = fRadius + boxMargin;
117 	btScalar contactDist = intersectionDist + maxContactDistance;
118 	normal = sphereRelPos - closestPoint;
119 
120 	//if there is no penetration, we are done
121 	btScalar dist2 = normal.length2();
122 	if (dist2 > contactDist * contactDist)
123 	{
124 		return false;
125 	}
126 
127 	btScalar distance;
128 
129 	//special case if the sphere center is inside the box
130 	if (dist2 <= SIMD_EPSILON)
131 	{
132 		distance = -getSpherePenetration(boxHalfExtent, sphereRelPos, closestPoint, normal);
133 	}
134 	else  //compute the penetration details
135 	{
136 		distance = normal.length();
137 		normal /= distance;
138 	}
139 
140 	pointOnBox = closestPoint + normal * boxMargin;
141 	//	v3PointOnSphere = sphereRelPos - (normal * fRadius);
142 	penetrationDepth = distance - intersectionDist;
143 
144 	// transform back in world space
145 	btVector3 tmp = m44T(pointOnBox);
146 	pointOnBox = tmp;
147 	//	tmp = m44T(v3PointOnSphere);
148 	//	v3PointOnSphere = tmp;
149 	tmp = m44T.getBasis() * normal;
150 	normal = tmp;
151 
152 	return true;
153 }
154 
getSpherePenetration(btVector3 const & boxHalfExtent,btVector3 const & sphereRelPos,btVector3 & closestPoint,btVector3 & normal)155 btScalar btSphereBoxCollisionAlgorithm::getSpherePenetration(btVector3 const& boxHalfExtent, btVector3 const& sphereRelPos, btVector3& closestPoint, btVector3& normal)
156 {
157 	//project the center of the sphere on the closest face of the box
158 	btScalar faceDist = boxHalfExtent.getX() - sphereRelPos.getX();
159 	btScalar minDist = faceDist;
160 	closestPoint.setX(boxHalfExtent.getX());
161 	normal.setValue(btScalar(1.0f), btScalar(0.0f), btScalar(0.0f));
162 
163 	faceDist = boxHalfExtent.getX() + sphereRelPos.getX();
164 	if (faceDist < minDist)
165 	{
166 		minDist = faceDist;
167 		closestPoint = sphereRelPos;
168 		closestPoint.setX(-boxHalfExtent.getX());
169 		normal.setValue(btScalar(-1.0f), btScalar(0.0f), btScalar(0.0f));
170 	}
171 
172 	faceDist = boxHalfExtent.getY() - sphereRelPos.getY();
173 	if (faceDist < minDist)
174 	{
175 		minDist = faceDist;
176 		closestPoint = sphereRelPos;
177 		closestPoint.setY(boxHalfExtent.getY());
178 		normal.setValue(btScalar(0.0f), btScalar(1.0f), btScalar(0.0f));
179 	}
180 
181 	faceDist = boxHalfExtent.getY() + sphereRelPos.getY();
182 	if (faceDist < minDist)
183 	{
184 		minDist = faceDist;
185 		closestPoint = sphereRelPos;
186 		closestPoint.setY(-boxHalfExtent.getY());
187 		normal.setValue(btScalar(0.0f), btScalar(-1.0f), btScalar(0.0f));
188 	}
189 
190 	faceDist = boxHalfExtent.getZ() - sphereRelPos.getZ();
191 	if (faceDist < minDist)
192 	{
193 		minDist = faceDist;
194 		closestPoint = sphereRelPos;
195 		closestPoint.setZ(boxHalfExtent.getZ());
196 		normal.setValue(btScalar(0.0f), btScalar(0.0f), btScalar(1.0f));
197 	}
198 
199 	faceDist = boxHalfExtent.getZ() + sphereRelPos.getZ();
200 	if (faceDist < minDist)
201 	{
202 		minDist = faceDist;
203 		closestPoint = sphereRelPos;
204 		closestPoint.setZ(-boxHalfExtent.getZ());
205 		normal.setValue(btScalar(0.0f), btScalar(0.0f), btScalar(-1.0f));
206 	}
207 
208 	return minDist;
209 }
210