1036 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			1036 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /*
 | ||
|  | Bullet Continuous Collision Detection and Physics Library | ||
|  | Copyright (c) 2003-2014 Erwin Coumans  http://continuousphysics.com/Bullet/
 | ||
|  | 
 | ||
|  | 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. | ||
|  | */ | ||
|  | 
 | ||
|  | /*
 | ||
|  | Initial GJK-EPA collision solver by Nathanael Presson, 2008 | ||
|  | Improvements and refactoring by Erwin Coumans, 2008-2014 | ||
|  | */ | ||
|  | #ifndef BT_GJK_EPA3_H
 | ||
|  | #define BT_GJK_EPA3_H
 | ||
|  | 
 | ||
|  | #include "LinearMath/btTransform.h"
 | ||
|  | #include "btGjkCollisionDescription.h"
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | struct	btGjkEpaSolver3 | ||
|  | { | ||
|  | struct	sResults | ||
|  | 	{ | ||
|  | 	enum eStatus | ||
|  | 		{ | ||
|  | 		Separated,		/* Shapes doesnt penetrate												*/  | ||
|  | 		Penetrating,	/* Shapes are penetrating												*/  | ||
|  | 		GJK_Failed,		/* GJK phase fail, no big issue, shapes are probably just 'touching'	*/  | ||
|  | 		EPA_Failed		/* EPA phase fail, bigger problem, need to save parameters, and debug	*/  | ||
|  | 		}		status; | ||
|  | 	btVector3	witnesses[2]; | ||
|  | 	btVector3	normal; | ||
|  | 	btScalar	distance; | ||
|  | 	}; | ||
|  | 
 | ||
|  | 
 | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #if defined(DEBUG) || defined (_DEBUG)
 | ||
|  | #include <stdio.h> //for debug printf
 | ||
|  | #ifdef __SPU__
 | ||
|  | #include <spu_printf.h>
 | ||
|  | #define printf spu_printf
 | ||
|  | #endif //__SPU__
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 
 | ||
|  |      | ||
|  |     // Config
 | ||
|  |      | ||
|  |     /* GJK	*/ | ||
|  | #define GJK_MAX_ITERATIONS	128
 | ||
|  | #define GJK_ACCURARY		((btScalar)0.0001)
 | ||
|  | #define GJK_MIN_DISTANCE	((btScalar)0.0001)
 | ||
|  | #define GJK_DUPLICATED_EPS	((btScalar)0.0001)
 | ||
|  | #define GJK_SIMPLEX2_EPS	((btScalar)0.0)
 | ||
|  | #define GJK_SIMPLEX3_EPS	((btScalar)0.0)
 | ||
|  | #define GJK_SIMPLEX4_EPS	((btScalar)0.0)
 | ||
|  |      | ||
|  |     /* EPA	*/ | ||
|  | #define EPA_MAX_VERTICES	64
 | ||
|  | #define EPA_MAX_FACES		(EPA_MAX_VERTICES*2)
 | ||
|  | #define EPA_MAX_ITERATIONS	255
 | ||
|  | #define EPA_ACCURACY		((btScalar)0.0001)
 | ||
|  | #define EPA_FALLBACK		(10*EPA_ACCURACY)
 | ||
|  | #define EPA_PLANE_EPS		((btScalar)0.00001)
 | ||
|  | #define EPA_INSIDE_EPS		((btScalar)0.01)
 | ||
|  |      | ||
|  |      | ||
|  |     // Shorthands
 | ||
|  |     typedef unsigned int	U; | ||
|  |     typedef unsigned char	U1; | ||
|  |      | ||
|  |     // MinkowskiDiff
 | ||
|  |     template <typename btConvexTemplate> | ||
|  |     struct	MinkowskiDiff | ||
|  |     { | ||
|  |         const btConvexTemplate* m_convexAPtr; | ||
|  |         const btConvexTemplate* m_convexBPtr; | ||
|  |          | ||
|  |         btMatrix3x3				m_toshape1; | ||
|  |         btTransform				m_toshape0; | ||
|  |          | ||
|  |         bool					m_enableMargin; | ||
|  |          | ||
|  |          | ||
|  |         MinkowskiDiff(const btConvexTemplate& a, const btConvexTemplate& b) | ||
|  |         :m_convexAPtr(&a), | ||
|  |         m_convexBPtr(&b) | ||
|  |         { | ||
|  |         } | ||
|  |          | ||
|  |         void					EnableMargin(bool enable) | ||
|  |         { | ||
|  |             m_enableMargin = enable; | ||
|  |         } | ||
|  |         inline btVector3		Support0(const btVector3& d) const | ||
|  |         { | ||
|  |             return m_convexAPtr->getLocalSupportWithMargin(d); | ||
|  |         } | ||
|  |         inline btVector3		Support1(const btVector3& d) const | ||
|  |         { | ||
|  |             return m_toshape0*m_convexBPtr->getLocalSupportWithMargin(m_toshape1*d); | ||
|  |         } | ||
|  |          | ||
|  |          | ||
|  |         inline btVector3		Support(const btVector3& d) const | ||
|  |         { | ||
|  |             return(Support0(d)-Support1(-d)); | ||
|  |         } | ||
|  |         btVector3				Support(const btVector3& d,U index) const | ||
|  |         { | ||
|  |             if(index) | ||
|  |                 return(Support1(d)); | ||
|  |             else | ||
|  |                 return(Support0(d)); | ||
|  |         } | ||
|  |     }; | ||
|  |      | ||
|  | enum	eGjkStatus | ||
|  | { | ||
|  |     eGjkValid, | ||
|  |     eGjkInside, | ||
|  |     eGjkFailed | ||
|  | }; | ||
|  | 
 | ||
|  |     // GJK
 | ||
|  |     template <typename btConvexTemplate> | ||
|  |     struct	GJK | ||
|  |     { | ||
|  |         /* Types		*/ | ||
|  |         struct	sSV | ||
|  |         { | ||
|  |             btVector3	d,w; | ||
|  |         }; | ||
|  |         struct	sSimplex | ||
|  |         { | ||
|  |             sSV*		c[4]; | ||
|  |             btScalar	p[4]; | ||
|  |             U			rank; | ||
|  |         }; | ||
|  |          | ||
|  |         /* Fields		*/ | ||
|  |          | ||
|  |         MinkowskiDiff<btConvexTemplate>			m_shape; | ||
|  |         btVector3		m_ray; | ||
|  |         btScalar		m_distance; | ||
|  |         sSimplex		m_simplices[2]; | ||
|  |         sSV				m_store[4]; | ||
|  |         sSV*			m_free[4]; | ||
|  |         U				m_nfree; | ||
|  |         U				m_current; | ||
|  |         sSimplex*		m_simplex; | ||
|  |         eGjkStatus      m_status; | ||
|  |         /* Methods		*/ | ||
|  |          | ||
|  |         GJK(const btConvexTemplate& a, const btConvexTemplate& b) | ||
|  |         :m_shape(a,b) | ||
|  |         { | ||
|  |             Initialize(); | ||
|  |         } | ||
|  |         void				Initialize() | ||
|  |         { | ||
|  |             m_ray		=	btVector3(0,0,0); | ||
|  |             m_nfree		=	0; | ||
|  |             m_status	=	eGjkFailed; | ||
|  |             m_current	=	0; | ||
|  |             m_distance	=	0; | ||
|  |         } | ||
|  |         eGjkStatus			Evaluate(const MinkowskiDiff<btConvexTemplate>& shapearg,const btVector3& guess) | ||
|  |         { | ||
|  |             U			iterations=0; | ||
|  |             btScalar	sqdist=0; | ||
|  |             btScalar	alpha=0; | ||
|  |             btVector3	lastw[4]; | ||
|  |             U			clastw=0; | ||
|  |             /* Initialize solver		*/ | ||
|  |             m_free[0]			=	&m_store[0]; | ||
|  |             m_free[1]			=	&m_store[1]; | ||
|  |             m_free[2]			=	&m_store[2]; | ||
|  |             m_free[3]			=	&m_store[3]; | ||
|  |             m_nfree				=	4; | ||
|  |             m_current			=	0; | ||
|  |             m_status			=	eGjkValid; | ||
|  |             m_shape				=	shapearg; | ||
|  |             m_distance			=	0; | ||
|  |             /* Initialize simplex		*/ | ||
|  |             m_simplices[0].rank	=	0; | ||
|  |             m_ray				=	guess; | ||
|  |             const btScalar	sqrl=	m_ray.length2(); | ||
|  |             appendvertice(m_simplices[0],sqrl>0?-m_ray:btVector3(1,0,0)); | ||
|  |             m_simplices[0].p[0]	=	1; | ||
|  |             m_ray				=	m_simplices[0].c[0]->w; | ||
|  |             sqdist				=	sqrl; | ||
|  |             lastw[0]			= | ||
|  |             lastw[1]			= | ||
|  |             lastw[2]			= | ||
|  |             lastw[3]			=	m_ray; | ||
|  |             /* Loop						*/ | ||
|  |             do	{ | ||
|  |                 const U		next=1-m_current; | ||
|  |                 sSimplex&	cs=m_simplices[m_current]; | ||
|  |                 sSimplex&	ns=m_simplices[next]; | ||
|  |                 /* Check zero							*/ | ||
|  |                 const btScalar	rl=m_ray.length(); | ||
|  |                 if(rl<GJK_MIN_DISTANCE) | ||
|  |                 {/* Touching or inside				*/ | ||
|  |                     m_status=eGjkInside; | ||
|  |                     break; | ||
|  |                 } | ||
|  |                 /* Append new vertice in -'v' direction	*/ | ||
|  |                 appendvertice(cs,-m_ray); | ||
|  |                 const btVector3&	w=cs.c[cs.rank-1]->w; | ||
|  |                 bool				found=false; | ||
|  |                 for(U i=0;i<4;++i) | ||
|  |                 { | ||
|  |                     if((w-lastw[i]).length2()<GJK_DUPLICATED_EPS) | ||
|  |                     { found=true;break; } | ||
|  |                 } | ||
|  |                 if(found) | ||
|  |                 {/* Return old simplex				*/ | ||
|  |                     removevertice(m_simplices[m_current]); | ||
|  |                     break; | ||
|  |                 } | ||
|  |                 else | ||
|  |                 {/* Update lastw					*/ | ||
|  |                     lastw[clastw=(clastw+1)&3]=w; | ||
|  |                 } | ||
|  |                 /* Check for termination				*/ | ||
|  |                 const btScalar	omega=btDot(m_ray,w)/rl; | ||
|  |                 alpha=btMax(omega,alpha); | ||
|  |                 if(((rl-alpha)-(GJK_ACCURARY*rl))<=0) | ||
|  |                 {/* Return old simplex				*/ | ||
|  |                     removevertice(m_simplices[m_current]); | ||
|  |                     break; | ||
|  |                 } | ||
|  |                 /* Reduce simplex						*/ | ||
|  |                 btScalar	weights[4]; | ||
|  |                 U			mask=0; | ||
|  |                 switch(cs.rank) | ||
|  |                 { | ||
|  |                     case	2:	sqdist=projectorigin(	cs.c[0]->w, | ||
|  |                                                      cs.c[1]->w, | ||
|  |                                                      weights,mask);break; | ||
|  |                     case	3:	sqdist=projectorigin(	cs.c[0]->w, | ||
|  |                                                      cs.c[1]->w, | ||
|  |                                                      cs.c[2]->w, | ||
|  |                                                      weights,mask);break; | ||
|  |                     case	4:	sqdist=projectorigin(	cs.c[0]->w, | ||
|  |                                                      cs.c[1]->w, | ||
|  |                                                      cs.c[2]->w, | ||
|  |                                                      cs.c[3]->w, | ||
|  |                                                      weights,mask);break; | ||
|  |                 } | ||
|  |                 if(sqdist>=0) | ||
|  |                 {/* Valid	*/ | ||
|  |                     ns.rank		=	0; | ||
|  |                     m_ray		=	btVector3(0,0,0); | ||
|  |                     m_current	=	next; | ||
|  |                     for(U i=0,ni=cs.rank;i<ni;++i) | ||
|  |                     { | ||
|  |                         if(mask&(1<<i)) | ||
|  |                         { | ||
|  |                             ns.c[ns.rank]		=	cs.c[i]; | ||
|  |                             ns.p[ns.rank++]		=	weights[i]; | ||
|  |                             m_ray				+=	cs.c[i]->w*weights[i]; | ||
|  |                         } | ||
|  |                         else | ||
|  |                         { | ||
|  |                             m_free[m_nfree++]	=	cs.c[i]; | ||
|  |                         } | ||
|  |                     } | ||
|  |                     if(mask==15) m_status=eGjkInside; | ||
|  |                 } | ||
|  |                 else | ||
|  |                 {/* Return old simplex				*/ | ||
|  |                     removevertice(m_simplices[m_current]); | ||
|  |                     break; | ||
|  |                 } | ||
|  |                 m_status=((++iterations)<GJK_MAX_ITERATIONS)?m_status:eGjkFailed; | ||
|  |             } while(m_status==eGjkValid); | ||
|  |             m_simplex=&m_simplices[m_current]; | ||
|  |             switch(m_status) | ||
|  |             { | ||
|  |                 case	eGjkValid:		m_distance=m_ray.length();break; | ||
|  |                 case	eGjkInside:	m_distance=0;break; | ||
|  |                 default: | ||
|  |                 { | ||
|  |                 } | ||
|  |             } | ||
|  |             return(m_status); | ||
|  |         } | ||
|  |         bool					EncloseOrigin() | ||
|  |         { | ||
|  |             switch(m_simplex->rank) | ||
|  |             { | ||
|  |                 case	1: | ||
|  |                 { | ||
|  |                     for(U i=0;i<3;++i) | ||
|  |                     { | ||
|  |                         btVector3		axis=btVector3(0,0,0); | ||
|  |                         axis[i]=1; | ||
|  |                         appendvertice(*m_simplex, axis); | ||
|  |                         if(EncloseOrigin())	return(true); | ||
|  |                         removevertice(*m_simplex); | ||
|  |                         appendvertice(*m_simplex,-axis); | ||
|  |                         if(EncloseOrigin())	return(true); | ||
|  |                         removevertice(*m_simplex); | ||
|  |                     } | ||
|  |                 } | ||
|  |                     break; | ||
|  |                 case	2: | ||
|  |                 { | ||
|  |                     const btVector3	d=m_simplex->c[1]->w-m_simplex->c[0]->w; | ||
|  |                     for(U i=0;i<3;++i) | ||
|  |                     { | ||
|  |                         btVector3		axis=btVector3(0,0,0); | ||
|  |                         axis[i]=1; | ||
|  |                         const btVector3	p=btCross(d,axis); | ||
|  |                         if(p.length2()>0) | ||
|  |                         { | ||
|  |                             appendvertice(*m_simplex, p); | ||
|  |                             if(EncloseOrigin())	return(true); | ||
|  |                             removevertice(*m_simplex); | ||
|  |                             appendvertice(*m_simplex,-p); | ||
|  |                             if(EncloseOrigin())	return(true); | ||
|  |                             removevertice(*m_simplex); | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  |                     break; | ||
|  |                 case	3: | ||
|  |                 { | ||
|  |                     const btVector3	n=btCross(m_simplex->c[1]->w-m_simplex->c[0]->w, | ||
|  |                                               m_simplex->c[2]->w-m_simplex->c[0]->w); | ||
|  |                     if(n.length2()>0) | ||
|  |                     { | ||
|  |                         appendvertice(*m_simplex,n); | ||
|  |                         if(EncloseOrigin())	return(true); | ||
|  |                         removevertice(*m_simplex); | ||
|  |                         appendvertice(*m_simplex,-n); | ||
|  |                         if(EncloseOrigin())	return(true); | ||
|  |                         removevertice(*m_simplex); | ||
|  |                     } | ||
|  |                 } | ||
|  |                     break; | ||
|  |                 case	4: | ||
|  |                 { | ||
|  |                     if(btFabs(det(	m_simplex->c[0]->w-m_simplex->c[3]->w, | ||
|  |                                   m_simplex->c[1]->w-m_simplex->c[3]->w, | ||
|  |                                   m_simplex->c[2]->w-m_simplex->c[3]->w))>0) | ||
|  |                         return(true); | ||
|  |                 } | ||
|  |                     break; | ||
|  |             } | ||
|  |             return(false); | ||
|  |         } | ||
|  |         /* Internals	*/ | ||
|  |         void				getsupport(const btVector3& d,sSV& sv) const | ||
|  |         { | ||
|  |             sv.d	=	d/d.length(); | ||
|  |             sv.w	=	m_shape.Support(sv.d); | ||
|  |         } | ||
|  |         void				removevertice(sSimplex& simplex) | ||
|  |         { | ||
|  |             m_free[m_nfree++]=simplex.c[--simplex.rank]; | ||
|  |         } | ||
|  |         void				appendvertice(sSimplex& simplex,const btVector3& v) | ||
|  |         { | ||
|  |             simplex.p[simplex.rank]=0; | ||
|  |             simplex.c[simplex.rank]=m_free[--m_nfree]; | ||
|  |             getsupport(v,*simplex.c[simplex.rank++]); | ||
|  |         } | ||
|  |         static btScalar		det(const btVector3& a,const btVector3& b,const btVector3& c) | ||
|  |         { | ||
|  |             return(	a.y()*b.z()*c.x()+a.z()*b.x()*c.y()- | ||
|  |                    a.x()*b.z()*c.y()-a.y()*b.x()*c.z()+ | ||
|  |                    a.x()*b.y()*c.z()-a.z()*b.y()*c.x()); | ||
|  |         } | ||
|  |         static btScalar		projectorigin(	const btVector3& a, | ||
|  |                                           const btVector3& b, | ||
|  |                                           btScalar* w,U& m) | ||
|  |         { | ||
|  |             const btVector3	d=b-a; | ||
|  |             const btScalar	l=d.length2(); | ||
|  |             if(l>GJK_SIMPLEX2_EPS) | ||
|  |             { | ||
|  |                 const btScalar	t(l>0?-btDot(a,d)/l:0); | ||
|  |                 if(t>=1)		{ w[0]=0;w[1]=1;m=2;return(b.length2()); } | ||
|  |                 else if(t<=0)	{ w[0]=1;w[1]=0;m=1;return(a.length2()); } | ||
|  |                 else			{ w[0]=1-(w[1]=t);m=3;return((a+d*t).length2()); } | ||
|  |             } | ||
|  |             return(-1); | ||
|  |         } | ||
|  |         static btScalar		projectorigin(	const btVector3& a, | ||
|  |                                           const btVector3& b, | ||
|  |                                           const btVector3& c, | ||
|  |                                           btScalar* w,U& m) | ||
|  |         { | ||
|  |             static const U		imd3[]={1,2,0}; | ||
|  |             const btVector3*	vt[]={&a,&b,&c}; | ||
|  |             const btVector3		dl[]={a-b,b-c,c-a}; | ||
|  |             const btVector3		n=btCross(dl[0],dl[1]); | ||
|  |             const btScalar		l=n.length2(); | ||
|  |             if(l>GJK_SIMPLEX3_EPS) | ||
|  |             { | ||
|  |                 btScalar	mindist=-1; | ||
|  |                 btScalar	subw[2]={0.f,0.f}; | ||
|  |                 U			subm(0); | ||
|  |                 for(U i=0;i<3;++i) | ||
|  |                 { | ||
|  |                     if(btDot(*vt[i],btCross(dl[i],n))>0) | ||
|  |                     { | ||
|  |                         const U			j=imd3[i]; | ||
|  |                         const btScalar	subd(projectorigin(*vt[i],*vt[j],subw,subm)); | ||
|  |                         if((mindist<0)||(subd<mindist)) | ||
|  |                         { | ||
|  |                             mindist		=	subd; | ||
|  |                             m			=	static_cast<U>(((subm&1)?1<<i:0)+((subm&2)?1<<j:0)); | ||
|  |                             w[i]		=	subw[0]; | ||
|  |                             w[j]		=	subw[1]; | ||
|  |                             w[imd3[j]]	=	0; | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  |                 if(mindist<0) | ||
|  |                 { | ||
|  |                     const btScalar	d=btDot(a,n); | ||
|  |                     const btScalar	s=btSqrt(l); | ||
|  |                     const btVector3	p=n*(d/l); | ||
|  |                     mindist	=	p.length2(); | ||
|  |                     m		=	7; | ||
|  |                     w[0]	=	(btCross(dl[1],b-p)).length()/s; | ||
|  |                     w[1]	=	(btCross(dl[2],c-p)).length()/s; | ||
|  |                     w[2]	=	1-(w[0]+w[1]); | ||
|  |                 } | ||
|  |                 return(mindist); | ||
|  |             } | ||
|  |             return(-1); | ||
|  |         } | ||
|  |         static btScalar		projectorigin(	const btVector3& a, | ||
|  |                                           const btVector3& b, | ||
|  |                                           const btVector3& c, | ||
|  |                                           const btVector3& d, | ||
|  |                                           btScalar* w,U& m) | ||
|  |         { | ||
|  |             static const U		imd3[]={1,2,0}; | ||
|  |             const btVector3*	vt[]={&a,&b,&c,&d}; | ||
|  |             const btVector3		dl[]={a-d,b-d,c-d}; | ||
|  |             const btScalar		vl=det(dl[0],dl[1],dl[2]); | ||
|  |             const bool			ng=(vl*btDot(a,btCross(b-c,a-b)))<=0; | ||
|  |             if(ng&&(btFabs(vl)>GJK_SIMPLEX4_EPS)) | ||
|  |             { | ||
|  |                 btScalar	mindist=-1; | ||
|  |                 btScalar	subw[3]={0.f,0.f,0.f}; | ||
|  |                 U			subm(0); | ||
|  |                 for(U i=0;i<3;++i) | ||
|  |                 { | ||
|  |                     const U			j=imd3[i]; | ||
|  |                     const btScalar	s=vl*btDot(d,btCross(dl[i],dl[j])); | ||
|  |                     if(s>0) | ||
|  |                     { | ||
|  |                         const btScalar	subd=projectorigin(*vt[i],*vt[j],d,subw,subm); | ||
|  |                         if((mindist<0)||(subd<mindist)) | ||
|  |                         { | ||
|  |                             mindist		=	subd; | ||
|  |                             m			=	static_cast<U>((subm&1?1<<i:0)+ | ||
|  |                                                            (subm&2?1<<j:0)+ | ||
|  |                                                            (subm&4?8:0)); | ||
|  |                             w[i]		=	subw[0]; | ||
|  |                             w[j]		=	subw[1]; | ||
|  |                             w[imd3[j]]	=	0; | ||
|  |                             w[3]		=	subw[2]; | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  |                 if(mindist<0) | ||
|  |                 { | ||
|  |                     mindist	=	0; | ||
|  |                     m		=	15; | ||
|  |                     w[0]	=	det(c,b,d)/vl; | ||
|  |                     w[1]	=	det(a,c,d)/vl; | ||
|  |                     w[2]	=	det(b,a,d)/vl; | ||
|  |                     w[3]	=	1-(w[0]+w[1]+w[2]); | ||
|  |                 } | ||
|  |                 return(mindist); | ||
|  |             } | ||
|  |             return(-1); | ||
|  |         } | ||
|  |     }; | ||
|  | 
 | ||
|  | 
 | ||
|  | enum	eEpaStatus | ||
|  | { | ||
|  |     eEpaValid, | ||
|  |     eEpaTouching, | ||
|  |     eEpaDegenerated, | ||
|  |     eEpaNonConvex, | ||
|  |     eEpaInvalidHull, | ||
|  |     eEpaOutOfFaces, | ||
|  |     eEpaOutOfVertices, | ||
|  |     eEpaAccuraryReached, | ||
|  |     eEpaFallBack, | ||
|  |     eEpaFailed | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  |     // EPA
 | ||
|  | template <typename btConvexTemplate> | ||
|  |     struct	EPA | ||
|  |     { | ||
|  |         /* Types		*/ | ||
|  |         | ||
|  |         struct	sFace | ||
|  |         { | ||
|  |             btVector3	n; | ||
|  |             btScalar	d; | ||
|  |             typename GJK<btConvexTemplate>::sSV*		c[3]; | ||
|  |             sFace*		f[3]; | ||
|  |             sFace*		l[2]; | ||
|  |             U1			e[3]; | ||
|  |             U1			pass; | ||
|  |         }; | ||
|  |         struct	sList | ||
|  |         { | ||
|  |             sFace*		root; | ||
|  |             U			count; | ||
|  |             sList() : root(0),count(0)	{} | ||
|  |         }; | ||
|  |         struct	sHorizon | ||
|  |         { | ||
|  |             sFace*		cf; | ||
|  |             sFace*		ff; | ||
|  |             U			nf; | ||
|  |             sHorizon() : cf(0),ff(0),nf(0)	{} | ||
|  |         }; | ||
|  |         | ||
|  |         /* Fields		*/ | ||
|  |         eEpaStatus		m_status; | ||
|  |         typename GJK<btConvexTemplate>::sSimplex	m_result; | ||
|  |         btVector3		m_normal; | ||
|  |         btScalar		m_depth; | ||
|  |         typename GJK<btConvexTemplate>::sSV				m_sv_store[EPA_MAX_VERTICES]; | ||
|  |         sFace			m_fc_store[EPA_MAX_FACES]; | ||
|  |         U				m_nextsv; | ||
|  |         sList			m_hull; | ||
|  |         sList			m_stock; | ||
|  |         /* Methods		*/ | ||
|  |         EPA() | ||
|  |         { | ||
|  |             Initialize(); | ||
|  |         } | ||
|  |          | ||
|  |          | ||
|  |         static inline void		bind(sFace* fa,U ea,sFace* fb,U eb) | ||
|  |         { | ||
|  |             fa->e[ea]=(U1)eb;fa->f[ea]=fb; | ||
|  |             fb->e[eb]=(U1)ea;fb->f[eb]=fa; | ||
|  |         } | ||
|  |         static inline void		append(sList& list,sFace* face) | ||
|  |         { | ||
|  |             face->l[0]	=	0; | ||
|  |             face->l[1]	=	list.root; | ||
|  |             if(list.root) list.root->l[0]=face; | ||
|  |             list.root	=	face; | ||
|  |             ++list.count; | ||
|  |         } | ||
|  |         static inline void		remove(sList& list,sFace* face) | ||
|  |         { | ||
|  |             if(face->l[1]) face->l[1]->l[0]=face->l[0]; | ||
|  |             if(face->l[0]) face->l[0]->l[1]=face->l[1]; | ||
|  |             if(face==list.root) list.root=face->l[1]; | ||
|  |             --list.count; | ||
|  |         } | ||
|  |          | ||
|  |          | ||
|  |         void				Initialize() | ||
|  |         { | ||
|  |             m_status	=	eEpaFailed; | ||
|  |             m_normal	=	btVector3(0,0,0); | ||
|  |             m_depth		=	0; | ||
|  |             m_nextsv	=	0; | ||
|  |             for(U i=0;i<EPA_MAX_FACES;++i) | ||
|  |             { | ||
|  |                 append(m_stock,&m_fc_store[EPA_MAX_FACES-i-1]); | ||
|  |             } | ||
|  |         } | ||
|  |         eEpaStatus			Evaluate(GJK<btConvexTemplate>& gjk,const btVector3& guess) | ||
|  |         { | ||
|  |             typename GJK<btConvexTemplate>::sSimplex&	simplex=*gjk.m_simplex; | ||
|  |             if((simplex.rank>1)&&gjk.EncloseOrigin()) | ||
|  |             { | ||
|  |                  | ||
|  |                 /* Clean up				*/ | ||
|  |                 while(m_hull.root) | ||
|  |                 { | ||
|  |                     sFace*	f = m_hull.root; | ||
|  |                     remove(m_hull,f); | ||
|  |                     append(m_stock,f); | ||
|  |                 } | ||
|  |                 m_status	=	eEpaValid; | ||
|  |                 m_nextsv	=	0; | ||
|  |                 /* Orient simplex		*/ | ||
|  |                 if(gjk.det(	simplex.c[0]->w-simplex.c[3]->w, | ||
|  |                            simplex.c[1]->w-simplex.c[3]->w, | ||
|  |                            simplex.c[2]->w-simplex.c[3]->w)<0) | ||
|  |                 { | ||
|  |                     btSwap(simplex.c[0],simplex.c[1]); | ||
|  |                     btSwap(simplex.p[0],simplex.p[1]); | ||
|  |                 } | ||
|  |                 /* Build initial hull	*/ | ||
|  |                 sFace*	tetra[]={newface(simplex.c[0],simplex.c[1],simplex.c[2],true), | ||
|  |                     newface(simplex.c[1],simplex.c[0],simplex.c[3],true), | ||
|  |                     newface(simplex.c[2],simplex.c[1],simplex.c[3],true), | ||
|  |                     newface(simplex.c[0],simplex.c[2],simplex.c[3],true)}; | ||
|  |                 if(m_hull.count==4) | ||
|  |                 { | ||
|  |                     sFace*		best=findbest(); | ||
|  |                     sFace		outer=*best; | ||
|  |                     U			pass=0; | ||
|  |                     U			iterations=0; | ||
|  |                     bind(tetra[0],0,tetra[1],0); | ||
|  |                     bind(tetra[0],1,tetra[2],0); | ||
|  |                     bind(tetra[0],2,tetra[3],0); | ||
|  |                     bind(tetra[1],1,tetra[3],2); | ||
|  |                     bind(tetra[1],2,tetra[2],1); | ||
|  |                     bind(tetra[2],2,tetra[3],1); | ||
|  |                     m_status=eEpaValid; | ||
|  |                     for(;iterations<EPA_MAX_ITERATIONS;++iterations) | ||
|  |                     { | ||
|  |                         if(m_nextsv<EPA_MAX_VERTICES) | ||
|  |                         { | ||
|  |                             sHorizon		horizon; | ||
|  |                             typename GJK<btConvexTemplate>::sSV*			w=&m_sv_store[m_nextsv++]; | ||
|  |                             bool			valid=true; | ||
|  |                             best->pass	=	(U1)(++pass); | ||
|  |                             gjk.getsupport(best->n,*w); | ||
|  |                             const btScalar	wdist=btDot(best->n,w->w)-best->d; | ||
|  |                             if(wdist>EPA_ACCURACY) | ||
|  |                             { | ||
|  |                                 for(U j=0;(j<3)&&valid;++j) | ||
|  |                                 { | ||
|  |                                     valid&=expand(	pass,w, | ||
|  |                                                   best->f[j],best->e[j], | ||
|  |                                                   horizon); | ||
|  |                                 } | ||
|  |                                 if(valid&&(horizon.nf>=3)) | ||
|  |                                 { | ||
|  |                                     bind(horizon.cf,1,horizon.ff,2); | ||
|  |                                     remove(m_hull,best); | ||
|  |                                     append(m_stock,best); | ||
|  |                                     best=findbest(); | ||
|  |                                     outer=*best; | ||
|  |                                 } else { m_status=eEpaInvalidHull;break; } | ||
|  |                             } else { m_status=eEpaAccuraryReached;break; } | ||
|  |                         } else { m_status=eEpaOutOfVertices;break; } | ||
|  |                     } | ||
|  |                     const btVector3	projection=outer.n*outer.d; | ||
|  |                     m_normal	=	outer.n; | ||
|  |                     m_depth		=	outer.d; | ||
|  |                     m_result.rank	=	3; | ||
|  |                     m_result.c[0]	=	outer.c[0]; | ||
|  |                     m_result.c[1]	=	outer.c[1]; | ||
|  |                     m_result.c[2]	=	outer.c[2]; | ||
|  |                     m_result.p[0]	=	btCross(	outer.c[1]->w-projection, | ||
|  |                                                 outer.c[2]->w-projection).length(); | ||
|  |                     m_result.p[1]	=	btCross(	outer.c[2]->w-projection, | ||
|  |                                                 outer.c[0]->w-projection).length(); | ||
|  |                     m_result.p[2]	=	btCross(	outer.c[0]->w-projection, | ||
|  |                                                 outer.c[1]->w-projection).length(); | ||
|  |                     const btScalar	sum=m_result.p[0]+m_result.p[1]+m_result.p[2]; | ||
|  |                     m_result.p[0]	/=	sum; | ||
|  |                     m_result.p[1]	/=	sum; | ||
|  |                     m_result.p[2]	/=	sum; | ||
|  |                     return(m_status); | ||
|  |                 } | ||
|  |             } | ||
|  |             /* Fallback		*/ | ||
|  |             m_status	=	eEpaFallBack; | ||
|  |             m_normal	=	-guess; | ||
|  |             const btScalar	nl=m_normal.length(); | ||
|  |             if(nl>0) | ||
|  |                 m_normal	=	m_normal/nl; | ||
|  |             else | ||
|  |                 m_normal	=	btVector3(1,0,0); | ||
|  |             m_depth	=	0; | ||
|  |             m_result.rank=1; | ||
|  |             m_result.c[0]=simplex.c[0]; | ||
|  |             m_result.p[0]=1; | ||
|  |             return(m_status); | ||
|  |         } | ||
|  |         bool getedgedist(sFace* face, typename GJK<btConvexTemplate>::sSV* a, typename GJK<btConvexTemplate>::sSV* b, btScalar& dist) | ||
|  |         { | ||
|  |             const btVector3 ba = b->w - a->w; | ||
|  |             const btVector3 n_ab = btCross(ba, face->n); // Outward facing edge normal direction, on triangle plane
 | ||
|  |             const btScalar a_dot_nab = btDot(a->w, n_ab); // Only care about the sign to determine inside/outside, so not normalization required
 | ||
|  |              | ||
|  |             if(a_dot_nab < 0) | ||
|  |             { | ||
|  |                 // Outside of edge a->b
 | ||
|  |                  | ||
|  |                 const btScalar ba_l2 = ba.length2(); | ||
|  |                 const btScalar a_dot_ba = btDot(a->w, ba); | ||
|  |                 const btScalar b_dot_ba = btDot(b->w, ba); | ||
|  |                  | ||
|  |                 if(a_dot_ba > 0) | ||
|  |                 { | ||
|  |                     // Pick distance vertex a
 | ||
|  |                     dist = a->w.length(); | ||
|  |                 } | ||
|  |                 else if(b_dot_ba < 0) | ||
|  |                 { | ||
|  |                     // Pick distance vertex b
 | ||
|  |                     dist = b->w.length(); | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     // Pick distance to edge a->b
 | ||
|  |                     const btScalar a_dot_b = btDot(a->w, b->w); | ||
|  |                     dist = btSqrt(btMax((a->w.length2() * b->w.length2() - a_dot_b * a_dot_b) / ba_l2, (btScalar)0)); | ||
|  |                 } | ||
|  |                  | ||
|  |                 return true; | ||
|  |             } | ||
|  |              | ||
|  |             return false; | ||
|  |         } | ||
|  |         sFace*				newface(typename GJK<btConvexTemplate>::sSV* a,typename GJK<btConvexTemplate>::sSV* b,typename GJK<btConvexTemplate>::sSV* c,bool forced) | ||
|  |         { | ||
|  |             if(m_stock.root) | ||
|  |             { | ||
|  |                 sFace*	face=m_stock.root; | ||
|  |                 remove(m_stock,face); | ||
|  |                 append(m_hull,face); | ||
|  |                 face->pass	=	0; | ||
|  |                 face->c[0]	=	a; | ||
|  |                 face->c[1]	=	b; | ||
|  |                 face->c[2]	=	c; | ||
|  |                 face->n		=	btCross(b->w-a->w,c->w-a->w); | ||
|  |                 const btScalar	l=face->n.length(); | ||
|  |                 const bool		v=l>EPA_ACCURACY; | ||
|  |                  | ||
|  |                 if(v) | ||
|  |                 { | ||
|  |                     if(!(getedgedist(face, a, b, face->d) || | ||
|  |                          getedgedist(face, b, c, face->d) || | ||
|  |                          getedgedist(face, c, a, face->d))) | ||
|  |                     { | ||
|  |                         // Origin projects to the interior of the triangle
 | ||
|  |                         // Use distance to triangle plane
 | ||
|  |                         face->d = btDot(a->w, face->n) / l; | ||
|  |                     } | ||
|  |                      | ||
|  |                     face->n /= l; | ||
|  |                     if(forced || (face->d >= -EPA_PLANE_EPS)) | ||
|  |                     { | ||
|  |                         return face; | ||
|  |                     } | ||
|  |                     else | ||
|  |                         m_status=eEpaNonConvex; | ||
|  |                 } | ||
|  |                 else | ||
|  |                     m_status=eEpaDegenerated; | ||
|  |                  | ||
|  |                 remove(m_hull, face); | ||
|  |                 append(m_stock, face); | ||
|  |                 return 0; | ||
|  |                  | ||
|  |             } | ||
|  |             m_status = m_stock.root ? eEpaOutOfVertices : eEpaOutOfFaces; | ||
|  |             return 0; | ||
|  |         } | ||
|  |         sFace*				findbest() | ||
|  |         { | ||
|  |             sFace*		minf=m_hull.root; | ||
|  |             btScalar	mind=minf->d*minf->d; | ||
|  |             for(sFace* f=minf->l[1];f;f=f->l[1]) | ||
|  |             { | ||
|  |                 const btScalar	sqd=f->d*f->d; | ||
|  |                 if(sqd<mind) | ||
|  |                 { | ||
|  |                     minf=f; | ||
|  |                     mind=sqd; | ||
|  |                 } | ||
|  |             } | ||
|  |             return(minf); | ||
|  |         } | ||
|  |         bool				expand(U pass,typename GJK<btConvexTemplate>::sSV* w,sFace* f,U e,sHorizon& horizon) | ||
|  |         { | ||
|  |             static const U	i1m3[]={1,2,0}; | ||
|  |             static const U	i2m3[]={2,0,1}; | ||
|  |             if(f->pass!=pass) | ||
|  |             { | ||
|  |                 const U	e1=i1m3[e]; | ||
|  |                 if((btDot(f->n,w->w)-f->d)<-EPA_PLANE_EPS) | ||
|  |                 { | ||
|  |                     sFace*	nf=newface(f->c[e1],f->c[e],w,false); | ||
|  |                     if(nf) | ||
|  |                     { | ||
|  |                         bind(nf,0,f,e); | ||
|  |                         if(horizon.cf) bind(horizon.cf,1,nf,2); else horizon.ff=nf; | ||
|  |                         horizon.cf=nf; | ||
|  |                         ++horizon.nf; | ||
|  |                         return(true); | ||
|  |                     } | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     const U	e2=i2m3[e]; | ||
|  |                     f->pass		=	(U1)pass; | ||
|  |                     if(	expand(pass,w,f->f[e1],f->e[e1],horizon)&& | ||
|  |                        expand(pass,w,f->f[e2],f->e[e2],horizon)) | ||
|  |                     { | ||
|  |                         remove(m_hull,f); | ||
|  |                         append(m_stock,f); | ||
|  |                         return(true); | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  |             return(false); | ||
|  |         } | ||
|  |          | ||
|  |     }; | ||
|  |      | ||
|  |     template <typename btConvexTemplate> | ||
|  |     static void	Initialize(	const btConvexTemplate& a, const btConvexTemplate& b, | ||
|  |                            btGjkEpaSolver3::sResults& results, | ||
|  |                            MinkowskiDiff<btConvexTemplate>& shape) | ||
|  |     { | ||
|  |         /* Results		*/  | ||
|  |         results.witnesses[0]	= | ||
|  |         results.witnesses[1]	=	btVector3(0,0,0); | ||
|  |         results.status			=	btGjkEpaSolver3::sResults::Separated; | ||
|  |         /* Shape		*/  | ||
|  |         | ||
|  |         shape.m_toshape1		=	b.getWorldTransform().getBasis().transposeTimes(a.getWorldTransform().getBasis()); | ||
|  |         shape.m_toshape0		=	a.getWorldTransform().inverseTimes(b.getWorldTransform()); | ||
|  |          | ||
|  |     } | ||
|  |      | ||
|  | 
 | ||
|  | //
 | ||
|  | // Api
 | ||
|  | //
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | //
 | ||
|  | template <typename btConvexTemplate> | ||
|  | bool		btGjkEpaSolver3_Distance(const btConvexTemplate& a, const btConvexTemplate& b, | ||
|  |                                       const btVector3& guess, | ||
|  |                                       btGjkEpaSolver3::sResults& results) | ||
|  | { | ||
|  |     MinkowskiDiff<btConvexTemplate>			shape(a,b); | ||
|  |     Initialize(a,b,results,shape); | ||
|  |     GJK<btConvexTemplate>				gjk(a,b); | ||
|  |     eGjkStatus	gjk_status=gjk.Evaluate(shape,guess); | ||
|  |     if(gjk_status==eGjkValid) | ||
|  |     { | ||
|  |         btVector3	w0=btVector3(0,0,0); | ||
|  |         btVector3	w1=btVector3(0,0,0); | ||
|  |         for(U i=0;i<gjk.m_simplex->rank;++i) | ||
|  |         { | ||
|  |             const btScalar	p=gjk.m_simplex->p[i]; | ||
|  |             w0+=shape.Support( gjk.m_simplex->c[i]->d,0)*p; | ||
|  |             w1+=shape.Support(-gjk.m_simplex->c[i]->d,1)*p; | ||
|  |         } | ||
|  |         results.witnesses[0]	=	a.getWorldTransform()*w0; | ||
|  |         results.witnesses[1]	=	a.getWorldTransform()*w1; | ||
|  |         results.normal			=	w0-w1; | ||
|  |         results.distance		=	results.normal.length(); | ||
|  |         results.normal			/=	results.distance>GJK_MIN_DISTANCE?results.distance:1; | ||
|  |         return(true); | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         results.status	=	gjk_status==eGjkInside? | ||
|  |         btGjkEpaSolver3::sResults::Penetrating	: | ||
|  |         btGjkEpaSolver3::sResults::GJK_Failed	; | ||
|  |         return(false); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | template <typename btConvexTemplate> | ||
|  | bool	btGjkEpaSolver3_Penetration(const btConvexTemplate& a, | ||
|  |                                      const btConvexTemplate& b, | ||
|  |                                      const btVector3& guess, | ||
|  |                                      btGjkEpaSolver3::sResults& results) | ||
|  | { | ||
|  |     MinkowskiDiff<btConvexTemplate>			shape(a,b); | ||
|  |     Initialize(a,b,results,shape); | ||
|  |     GJK<btConvexTemplate>				gjk(a,b); | ||
|  |     eGjkStatus	gjk_status=gjk.Evaluate(shape,-guess); | ||
|  |     switch(gjk_status) | ||
|  |     { | ||
|  |         case	eGjkInside: | ||
|  |         { | ||
|  |             EPA<btConvexTemplate>				epa; | ||
|  |             eEpaStatus	epa_status=epa.Evaluate(gjk,-guess); | ||
|  |             if(epa_status!=eEpaFailed) | ||
|  |             { | ||
|  |                 btVector3	w0=btVector3(0,0,0); | ||
|  |                 for(U i=0;i<epa.m_result.rank;++i) | ||
|  |                 { | ||
|  |                     w0+=shape.Support(epa.m_result.c[i]->d,0)*epa.m_result.p[i]; | ||
|  |                 } | ||
|  |                 results.status			=	btGjkEpaSolver3::sResults::Penetrating; | ||
|  |                 results.witnesses[0]	=	a.getWorldTransform()*w0; | ||
|  |                 results.witnesses[1]	=	a.getWorldTransform()*(w0-epa.m_normal*epa.m_depth); | ||
|  |                 results.normal			=	-epa.m_normal; | ||
|  |                 results.distance		=	-epa.m_depth; | ||
|  |                 return(true); | ||
|  |             } else results.status=btGjkEpaSolver3::sResults::EPA_Failed; | ||
|  |         } | ||
|  |             break; | ||
|  |         case	eGjkFailed: | ||
|  |             results.status=btGjkEpaSolver3::sResults::GJK_Failed; | ||
|  |             break; | ||
|  |         default: | ||
|  |         { | ||
|  |         } | ||
|  |     } | ||
|  |     return(false); | ||
|  | } | ||
|  | 
 | ||
|  | #if 0
 | ||
|  | int	btComputeGjkEpaPenetration2(const btCollisionDescription& colDesc, btDistanceInfo* distInfo) | ||
|  | { | ||
|  |     btGjkEpaSolver3::sResults results; | ||
|  |     btVector3 guess = colDesc.m_firstDir; | ||
|  |      | ||
|  |     bool res = btGjkEpaSolver3::Penetration(colDesc.m_objA,colDesc.m_objB, | ||
|  |                                             colDesc.m_transformA,colDesc.m_transformB, | ||
|  |                                             colDesc.m_localSupportFuncA,colDesc.m_localSupportFuncB, | ||
|  |                                             guess, | ||
|  |                                             results); | ||
|  |     if (res) | ||
|  |     { | ||
|  |         if ((results.status==btGjkEpaSolver3::sResults::Penetrating) || results.status==GJK::eStatus::Inside) | ||
|  |         { | ||
|  |             //normal could be 'swapped'
 | ||
|  |              | ||
|  |             distInfo->m_distance = results.distance; | ||
|  |             distInfo->m_normalBtoA = results.normal; | ||
|  |             btVector3 tmpNormalInB = results.witnesses[1]-results.witnesses[0]; | ||
|  |             btScalar lenSqr = tmpNormalInB.length2(); | ||
|  |             if (lenSqr <= (SIMD_EPSILON*SIMD_EPSILON)) | ||
|  |             { | ||
|  |                 tmpNormalInB = results.normal; | ||
|  |                 lenSqr = results.normal.length2(); | ||
|  |             } | ||
|  |              | ||
|  |             if (lenSqr > (SIMD_EPSILON*SIMD_EPSILON)) | ||
|  |             { | ||
|  |                 tmpNormalInB /= btSqrt(lenSqr); | ||
|  |                 btScalar distance2 = -(results.witnesses[0]-results.witnesses[1]).length(); | ||
|  |                 //only replace valid penetrations when the result is deeper (check)
 | ||
|  |                 //if ((distance2 < results.distance))
 | ||
|  |                 { | ||
|  |                     distInfo->m_distance = distance2; | ||
|  |                     distInfo->m_pointOnA= results.witnesses[0]; | ||
|  |                     distInfo->m_pointOnB= results.witnesses[1]; | ||
|  |                     distInfo->m_normalBtoA= tmpNormalInB; | ||
|  |                     return 0; | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |          | ||
|  |     } | ||
|  |      | ||
|  |     return -1; | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | template <typename btConvexTemplate, typename btDistanceInfoTemplate> | ||
|  | int	btComputeGjkDistance(const btConvexTemplate& a, const btConvexTemplate& b, | ||
|  |                          const btGjkCollisionDescription& colDesc, btDistanceInfoTemplate* distInfo) | ||
|  | { | ||
|  |     btGjkEpaSolver3::sResults results; | ||
|  |     btVector3 guess = colDesc.m_firstDir; | ||
|  |      | ||
|  |     bool isSeparated = btGjkEpaSolver3_Distance(	a,b, | ||
|  |                                                  guess, | ||
|  |                                                  results); | ||
|  |     if (isSeparated) | ||
|  |     { | ||
|  |         distInfo->m_distance = results.distance; | ||
|  |         distInfo->m_pointOnA= results.witnesses[0]; | ||
|  |         distInfo->m_pointOnB= results.witnesses[1]; | ||
|  |         distInfo->m_normalBtoA= results.normal; | ||
|  |         return 0; | ||
|  |     } | ||
|  |      | ||
|  |     return -1; | ||
|  | } | ||
|  | 
 | ||
|  | /* Symbols cleanup		*/  | ||
|  | 
 | ||
|  | #undef GJK_MAX_ITERATIONS
 | ||
|  | #undef GJK_ACCURARY
 | ||
|  | #undef GJK_MIN_DISTANCE
 | ||
|  | #undef GJK_DUPLICATED_EPS
 | ||
|  | #undef GJK_SIMPLEX2_EPS
 | ||
|  | #undef GJK_SIMPLEX3_EPS
 | ||
|  | #undef GJK_SIMPLEX4_EPS
 | ||
|  | 
 | ||
|  | #undef EPA_MAX_VERTICES
 | ||
|  | #undef EPA_MAX_FACES
 | ||
|  | #undef EPA_MAX_ITERATIONS
 | ||
|  | #undef EPA_ACCURACY
 | ||
|  | #undef EPA_FALLBACK
 | ||
|  | #undef EPA_PLANE_EPS
 | ||
|  | #undef EPA_INSIDE_EPS
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #endif //BT_GJK_EPA3_H
 | ||
|  | 
 |