2 Physics Effects Copyright(C) 2010 Sony Computer Entertainment Inc.
\r
5 Physics Effects is open software; you can redistribute it and/or
\r
6 modify it under the terms of the BSD License.
\r
8 Physics Effects is distributed in the hope that it will be useful,
\r
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
\r
11 See the BSD License for more details.
\r
13 A copy of the BSD License is distributed with
\r
14 Physics Effects under the filename: physics_effects_license.txt
\r
17 #include "pfx_gjk_solver.h"
\r
18 #include "pfx_intersect_common.h"
\r
22 namespace PhysicsEffects {
\r
25 template <class T, int maxData = 20>
\r
26 struct PfxGjkStack {
\r
30 PfxGjkStack() : cur(0) {}
\r
32 void push(const T &d)
\r
34 SCE_PFX_ASSERT(cur<maxData-1);
\r
49 ///////////////////////////////////////////////////////////////////////////////
\r
52 PfxGjkSolver::PfxGjkSolver()
\r
58 facetsHead = g_facetsHead;
\r
62 PfxGjkSolver::~PfxGjkSolver()
\r
66 ///////////////////////////////////////////////////////////////////////////////
\r
69 void PfxGjkSolver::setup(void *sA,void *sB,PfxGetSupportVertexFunc fA,PfxGetSupportVertexFunc fB)
\r
73 getSupportVertexShapeA = fA;
\r
74 getSupportVertexShapeB = fB;
\r
77 ///////////////////////////////////////////////////////////////////////////////
\r
78 // Construct Silhouette
\r
80 void PfxGjkSolver::silhouette(PfxGjkFacet *facet,int i,PfxVector3 &w)
\r
82 PfxGjkStack<PfxGjkEdge> gs;
\r
84 gs.push(PfxGjkEdge(facet,i));
\r
87 PfxGjkEdge stk = gs.pop();
\r
88 PfxGjkFacet *ft = stk.f;
\r
90 if(ft->obsolete==0) {
\r
92 if(dot(ft->normal,w-ft->closest) < 0.0f) {
\r
94 SCE_PFX_ASSERT(numEdges<=(int)MAX_FACETS);
\r
95 edges[numEdges] = stk;
\r
100 gs.push(PfxGjkEdge(ft->adj[(stk.i+2)%3],ft->j[(stk.i+2)%3]));
\r
101 gs.push(PfxGjkEdge(ft->adj[(stk.i+1)%3],ft->j[(stk.i+1)%3]));
\r
104 } while(SCE_PFX_UNLIKELY(!gs.isEmpty()));
\r
107 ///////////////////////////////////////////////////////////////////////////////
\r
108 // Detect Penetration Depth (EPA)
\r
110 PfxFloat PfxGjkSolver::detectPenetrationDepth(
\r
111 const PfxTransform3 &transformA,const PfxTransform3 &transformB,
\r
112 PfxVector3 &pA,PfxVector3 &pB,PfxVector3 &normal)
\r
114 PfxMatrix3 invRotA = transpose(transformA.getUpper3x3());
\r
115 PfxMatrix3 invRotB = transpose(transformB.getUpper3x3());
\r
117 int epaIterationCount = 0;
\r
118 PfxFloat distance = SCE_PFX_FLT_MAX;
\r
124 if(m_simplex.numVertices <= 1) {
\r
127 else if(m_simplex.numVertices == 2) {
\r
128 PfxVector3 v0 = m_simplex.W[0];
\r
129 PfxVector3 v1 = m_simplex.W[1];
\r
130 PfxVector3 dir = normalize(v1-v0);
\r
131 PfxMatrix3 rot = PfxMatrix3::rotation(2.0943951023932f,dir);//120 deg
\r
133 if(dir[0] < dir[1]) {
\r
134 if(dir[0] < dir[2]) {
\r
142 if(dir[1] < dir[2]) {
\r
149 PfxVector3 vec(0.0f);
\r
153 aux[0] = cross(dir,vec);
\r
154 aux[1] = rot * aux[0];
\r
155 aux[2] = rot * aux[1];
\r
157 PfxVector3 p[3],q[3],w[3];
\r
159 for(int i=0;i<3;i++) {
\r
160 PfxVector3 pInA,qInB;
\r
161 getSupportVertexShapeA(shapeA,invRotA * aux[i],pInA);
\r
162 getSupportVertexShapeB(shapeB,invRotB * (-aux[i]),qInB);
\r
163 p[i] = transformA.getTranslation() + transformA.getUpper3x3() * pInA;
\r
164 q[i] = transformB.getTranslation() + transformB.getUpper3x3() * qInB;
\r
165 w[i] = p[i] - q[i];
\r
171 if(originInTetrahedron(w[0],w[1],w[2],v0)) {
\r
172 vertsP[3] = m_simplex.P[0];
\r
173 vertsQ[3] = m_simplex.Q[0];
\r
174 vertsW[3] = m_simplex.W[0];
\r
177 else if(originInTetrahedron(w[0],w[1],w[2],v1)){
\r
178 vertsP[3] = m_simplex.P[1];
\r
179 vertsQ[3] = m_simplex.Q[1];
\r
180 vertsW[3] = m_simplex.W[1];
\r
187 else if(m_simplex.numVertices == 3) {
\r
189 for(int i=0;i<numVerts;i++) {
\r
190 vertsP[i] = m_simplex.P[i];
\r
191 vertsQ[i] = m_simplex.Q[i];
\r
192 vertsW[i] = m_simplex.W[i];
\r
195 PfxVector3 p[2],q[2],w[2];
\r
198 PfxVector3 v = cross(vertsW[2]-vertsW[0],vertsW[1]-vertsW[0]);
\r
199 PfxVector3 pInA,qInB;
\r
200 getSupportVertexShapeA(shapeA,invRotA * v,pInA);
\r
201 getSupportVertexShapeB(shapeB,invRotB * (-v),qInB);
\r
202 p[0] = transformA.getTranslation() + transformA.getUpper3x3() * pInA;
\r
203 q[0] = transformB.getTranslation() + transformB.getUpper3x3() * qInB;
\r
204 w[0] = p[0] - q[0];
\r
205 getSupportVertexShapeA(shapeA,invRotA * (-v),pInA);
\r
206 getSupportVertexShapeB(shapeB,invRotB * v,qInB);
\r
207 p[1] = transformA.getTranslation() + transformA.getUpper3x3() * pInA;
\r
208 q[1] = transformB.getTranslation() + transformB.getUpper3x3() * qInB;
\r
209 w[1] = p[1] - q[1];
\r
212 if(originInTetrahedron(vertsW[0],vertsW[1],vertsW[2],w[0])) {
\r
218 else if(originInTetrahedron(vertsW[0],vertsW[1],vertsW[2],w[1])){
\r
230 for(int i=0;i<numVerts;i++) {
\r
231 vertsP[i] = m_simplex.P[i];
\r
232 vertsQ[i] = m_simplex.Q[i];
\r
233 vertsW[i] = m_simplex.W[i];
\r
237 SCE_PFX_ASSERT(numVerts == 4);
\r
239 // 原点が単体の内部にあるかどうかを判定
\r
240 if(SCE_PFX_UNLIKELY(!originInTetrahedron(vertsW[0],vertsW[1],vertsW[2],vertsW[3]))) {
\r
245 if(dot(-vertsW[0],cross(vertsW[2]-vertsW[0],vertsW[1]-vertsW[0])) > 0.0f) {
\r
246 PfxVector3 vertsP1,vertsQ1,vertsW1;
\r
247 PfxVector3 vertsP3,vertsQ3,vertsW3;
\r
248 vertsQ1=vertsQ[1];vertsW1=vertsW[1];vertsP1=vertsP[1];
\r
249 vertsQ3=vertsQ[3];vertsW3=vertsW[3];vertsP3=vertsP[3];
\r
250 vertsQ[1]=vertsQ3;vertsW[1]=vertsW3;vertsP[1]=vertsP3;
\r
251 vertsQ[3]=vertsQ1;vertsW[3]=vertsW1;vertsP[3]=vertsP1;
\r
255 PfxGjkFacet *f0 = addFacet(0,1,2);
\r
256 PfxGjkFacet *f1 = addFacet(0,3,1);
\r
257 PfxGjkFacet *f2 = addFacet(0,2,3);
\r
258 PfxGjkFacet *f3 = addFacet(1,3,2);
\r
260 if(SCE_PFX_UNLIKELY(!f0 || !f1 || !f2 || !f3)) return distance;
\r
262 linkFacets(f0,0,f1,2);
\r
263 linkFacets(f0,1,f3,2);
\r
264 linkFacets(f0,2,f2,0);
\r
265 linkFacets(f1,0,f2,2);
\r
266 linkFacets(f1,1,f3,0);
\r
267 linkFacets(f2,1,f3,1);
\r
271 PfxGjkFacet *facetMin = NULL;
\r
274 // 原点から一番近い点を算出し、そのベクトルと支点を返す
\r
275 int minFacetIdx = 0;
\r
277 PfxFloat minDistSqr = SCE_PFX_FLT_MAX;
\r
278 for(int i=0;i<numFacetsHead;i++) {
\r
279 if(facetsHead[i]->obsolete==0 && facetsHead[i]->distSqr < minDistSqr) {
\r
280 minDistSqr = facetsHead[i]->distSqr;
\r
281 facetMin = facetsHead[i];
\r
288 facetsHead[minFacetIdx] = facetsHead[--numFacetsHead];
\r
290 PfxVector3 pInA(0.0f),qInB(0.0f);
\r
291 getSupportVertexShapeA(shapeA,invRotA * facetMin->normal,pInA);
\r
292 getSupportVertexShapeB(shapeB,invRotB * (-facetMin->normal),qInB);
\r
293 PfxVector3 p = transformA.getTranslation() + transformA.getUpper3x3() * pInA;
\r
294 PfxVector3 q = transformB.getTranslation() + transformB.getUpper3x3() * qInB;
\r
295 PfxVector3 w = p - q;
\r
296 PfxVector3 v = facetMin->closest;
\r
299 PfxFloat l0 = length(v);
\r
300 PfxFloat l1 = dot(facetMin->normal,w);
\r
302 if((l1 - l0) < SCE_PFX_GJK_EPSILON) {
\r
308 SCE_PFX_ASSERT(numVerts<(int)MAX_VERTS);
\r
309 int vId = numVerts++;
\r
314 facetMin->obsolete = 1;
\r
318 silhouette(facetMin->adj[0],facetMin->j[0],w);
\r
319 silhouette(facetMin->adj[1],facetMin->j[1],w);
\r
320 silhouette(facetMin->adj[2],facetMin->j[2],w);
\r
322 if(SCE_PFX_UNLIKELY(numEdges == 0)) break;
\r
324 bool edgeCheck = true;
\r
325 PfxGjkFacet *firstFacet,*lastFacet;
\r
327 PfxGjkEdge &edge = edges[0];
\r
328 int v0 = edge.f->v[(edge.i+1)%3];
\r
329 int v1 = edge.f->v[edge.i];
\r
330 firstFacet = addFacet(v0,v1,vId);
\r
331 if(SCE_PFX_UNLIKELY(!firstFacet)) {
\r
335 linkFacets(edge.f,edge.i,firstFacet,0);
\r
336 lastFacet = firstFacet;
\r
339 if(SCE_PFX_UNLIKELY(!edgeCheck)) break;
\r
341 for(int e=1;e<numEdges;e++) {
\r
342 PfxGjkEdge &edge = edges[e];
\r
343 int v0 = edge.f->v[(edge.i+1)%3];
\r
344 int v1 = edge.f->v[edge.i];
\r
345 PfxGjkFacet *f = addFacet(v0,v1,vId);
\r
346 if(SCE_PFX_UNLIKELY(!f)) {edgeCheck=false;break;}
\r
347 linkFacets(edge.f,edge.i,f,0);
\r
348 linkFacets(f,2,lastFacet,1);
\r
351 if(SCE_PFX_UNLIKELY(!edgeCheck)) break;
\r
353 linkFacets(lastFacet,1,firstFacet,2);
\r
356 epaIterationCount++;
\r
357 if(SCE_PFX_UNLIKELY(epaIterationCount > SCE_PFX_EPA_ITERATION_MAX || numFacetsHead == 0)) {
\r
363 int v1 = facetMin->v[0];
\r
364 int v2 = facetMin->v[1];
\r
365 int v3 = facetMin->v[2];
\r
367 PfxVector3 p0 = vertsW[v2]-vertsW[v1];
\r
368 PfxVector3 p1 = vertsW[v3]-vertsW[v1];
\r
369 PfxVector3 p2 = facetMin->closest-vertsW[v1];
\r
371 PfxVector3 v = cross( p0, p1 );
\r
372 PfxVector3 crS = cross( v, p0 );
\r
373 PfxVector3 crT = cross( v, p1 );
\r
374 PfxFloat d0 = dot( crT, p0 );
\r
375 PfxFloat d1 = dot( crS, p1 );
\r
377 if(fabsf(d0) < SCE_PFX_GJK_EPSILON || fabsf(d1) < SCE_PFX_GJK_EPSILON) return distance;
\r
379 PfxFloat lamda1 = dot( crT, p2 ) / d0;
\r
380 PfxFloat lamda2 = dot( crS, p2 ) / d1;
\r
382 pA = vertsP[v1] + lamda1*(vertsP[v2]-vertsP[v1]) + lamda2*(vertsP[v3]-vertsP[v1]);
\r
383 pB = vertsQ[v1] + lamda1*(vertsQ[v2]-vertsQ[v1]) + lamda2*(vertsQ[v3]-vertsQ[v1]);
\r
385 PfxFloat lenSqr = lengthSqr(pB-pA);
\r
386 normal = normalize(pB-pA);
\r
388 return -sqrtf(lenSqr);
\r
391 ///////////////////////////////////////////////////////////////////////////////
\r
394 PfxFloat PfxGjkSolver::collide( PfxVector3& normal, PfxPoint3 &pointA, PfxPoint3 &pointB,
\r
395 const PfxTransform3 & transformA,
\r
396 const PfxTransform3 & transformB,
\r
397 PfxFloat distanceThreshold)
\r
399 (void) distanceThreshold;
\r
401 int gjkIterationCount = 0;
\r
405 PfxTransform3 cTransformA = transformA;
\r
406 PfxTransform3 cTransformB = transformB;
\r
407 PfxMatrix3 invRotA = transpose(cTransformA.getUpper3x3());
\r
408 PfxMatrix3 invRotB = transpose(cTransformB.getUpper3x3());
\r
410 PfxVector3 offset = (cTransformA.getTranslation() + cTransformB.getTranslation())*0.5f;
\r
411 cTransformA.setTranslation(cTransformA.getTranslation()-offset);
\r
412 cTransformB.setTranslation(cTransformB.getTranslation()-offset);
\r
414 PfxVector3 separatingAxis(-cTransformA.getTranslation());
\r
415 if(lengthSqr(separatingAxis) < 0.000001f) separatingAxis = PfxVector3(1.0,0.0,0.0);
\r
416 PfxFloat squaredDistance = SCE_PFX_FLT_MAX;
\r
417 PfxFloat delta = 0.0f;
\r
418 PfxFloat distance = SCE_PFX_FLT_MAX;
\r
422 PfxVector3 pInA,qInB;
\r
424 getSupportVertexShapeA(shapeA,invRotA * (-separatingAxis),pInA);
\r
425 getSupportVertexShapeB(shapeB,invRotB * separatingAxis,qInB);
\r
427 PfxVector3 p = cTransformA.getTranslation() + cTransformA.getUpper3x3() * pInA;
\r
428 PfxVector3 q = cTransformB.getTranslation() + cTransformB.getUpper3x3() * qInB;
\r
429 PfxVector3 w = p - q;
\r
431 delta = dot(separatingAxis,w);
\r
434 if(SCE_PFX_UNLIKELY(delta > 0.0f)) {
\r
435 normal = separatingAxis;
\r
439 if(SCE_PFX_UNLIKELY(m_simplex.inSimplex(w))) {
\r
443 PfxFloat f0 = squaredDistance - delta;
\r
444 PfxFloat f1 = squaredDistance * SCE_PFX_GJK_EPSILON;
\r
446 if (SCE_PFX_UNLIKELY(f0 <= f1)) {
\r
451 m_simplex.addVertex(w,p,q);
\r
453 // 原点と単体の最近接点を求め、分離軸を返す
\r
454 if(SCE_PFX_UNLIKELY(!m_simplex.closest(separatingAxis))) {
\r
455 normal = separatingAxis;
\r
459 squaredDistance = lengthSqr(separatingAxis);
\r
461 if(SCE_PFX_UNLIKELY(gjkIterationCount >= SCE_PFX_GJK_ITERATION_MAX || m_simplex.fullSimplex())) {
\r
465 gjkIterationCount++;
\r
468 PfxVector3 pA(0.0f),pB(0.0f);
\r
470 // 2つのConvexは交差しているので、接触点を探索する
\r
471 PfxFloat dist = detectPenetrationDepth(cTransformA,cTransformB,pA,pB,normal);
\r
475 pA += normal * SCE_PFX_GJK_MARGIN;
\r
476 pB -= normal * SCE_PFX_GJK_MARGIN;
\r
477 dist = dot(normal,pA-pB);
\r
478 pointA = orthoInverse(transformA)*PfxPoint3(pA+offset);
\r
479 pointB = orthoInverse(transformB)*PfxPoint3(pB+offset);
\r
484 } //namespace PhysicsEffects
\r