1049 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1049 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| Bullet Continuous Collision Detection and Physics Library
 | |
| Copyright (c) 2003-2008 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.
 | |
| */
 | |
| 
 | |
| /*
 | |
| GJK-EPA collision solver by Nathanael Presson, 2008
 | |
| */
 | |
| #include "BulletCollision/CollisionShapes/btConvexInternalShape.h"
 | |
| #include "BulletCollision/CollisionShapes/btSphereShape.h"
 | |
| #include "btGjkEpa2.h"
 | |
| 
 | |
| #if defined(DEBUG) || defined (_DEBUG)
 | |
| #include <stdio.h> //for debug printf
 | |
| #ifdef __SPU__
 | |
| #include <spu_printf.h>
 | |
| #define printf spu_printf
 | |
| #endif //__SPU__
 | |
| #endif
 | |
| 
 | |
| namespace gjkepa2_impl
 | |
| {
 | |
| 
 | |
| 	// Config
 | |
| 
 | |
| 	/* GJK	*/ 
 | |
| #define GJK_MAX_ITERATIONS	128
 | |
| 
 | |
| #ifdef BT_USE_DOUBLE_PRECISION
 | |
| 	#define GJK_ACCURACY		((btScalar)1e-12)
 | |
| 	#define GJK_MIN_DISTANCE	((btScalar)1e-12)
 | |
| 	#define GJK_DUPLICATED_EPS	((btScalar)1e-12)
 | |
| #else
 | |
| 	#define GJK_ACCURACY		((btScalar)0.0001)
 | |
| 	#define GJK_MIN_DISTANCE	((btScalar)0.0001)
 | |
| 	#define GJK_DUPLICATED_EPS	((btScalar)0.0001)
 | |
| #endif //BT_USE_DOUBLE_PRECISION
 | |
| 
 | |
| 
 | |
| #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	128
 | |
| #define EPA_MAX_ITERATIONS	255
 | |
| 
 | |
| #ifdef BT_USE_DOUBLE_PRECISION
 | |
| 	#define EPA_ACCURACY		((btScalar)1e-12)
 | |
| 	#define EPA_PLANE_EPS		((btScalar)1e-14)
 | |
| 	#define EPA_INSIDE_EPS		((btScalar)1e-9)
 | |
| #else
 | |
| 	#define EPA_ACCURACY		((btScalar)0.0001)
 | |
| 	#define EPA_PLANE_EPS		((btScalar)0.00001)
 | |
| 	#define EPA_INSIDE_EPS		((btScalar)0.01)
 | |
| #endif
 | |
| 
 | |
| #define EPA_FALLBACK            (10*EPA_ACCURACY)
 | |
| #define EPA_MAX_FACES           (EPA_MAX_VERTICES*2)
 | |
| 
 | |
| 
 | |
| 	// Shorthands
 | |
| 	typedef unsigned int	U;
 | |
| 	typedef unsigned char	U1;
 | |
| 
 | |
| 	// MinkowskiDiff
 | |
| 	struct	MinkowskiDiff
 | |
| 	{
 | |
| 		const btConvexShape*	m_shapes[2];
 | |
| 		btMatrix3x3				m_toshape1;
 | |
| 		btTransform				m_toshape0;
 | |
| #ifdef __SPU__
 | |
| 		bool					m_enableMargin;
 | |
| #else
 | |
| 		btVector3				(btConvexShape::*Ls)(const btVector3&) const;
 | |
| #endif//__SPU__
 | |
| 		
 | |
| 
 | |
| 		MinkowskiDiff()
 | |
| 		{
 | |
| 
 | |
| 		}
 | |
| #ifdef __SPU__
 | |
| 			void					EnableMargin(bool enable)
 | |
| 		{
 | |
| 			m_enableMargin = enable;
 | |
| 		}	
 | |
| 		inline btVector3		Support0(const btVector3& d) const
 | |
| 		{
 | |
| 			if (m_enableMargin)
 | |
| 			{
 | |
| 				return m_shapes[0]->localGetSupportVertexNonVirtual(d);
 | |
| 			} else
 | |
| 			{
 | |
| 				return m_shapes[0]->localGetSupportVertexWithoutMarginNonVirtual(d);
 | |
| 			}
 | |
| 		}
 | |
| 		inline btVector3		Support1(const btVector3& d) const
 | |
| 		{
 | |
| 			if (m_enableMargin)
 | |
| 			{
 | |
| 				return m_toshape0*(m_shapes[1]->localGetSupportVertexNonVirtual(m_toshape1*d));
 | |
| 			} else
 | |
| 			{
 | |
| 				return m_toshape0*(m_shapes[1]->localGetSupportVertexWithoutMarginNonVirtual(m_toshape1*d));
 | |
| 			}
 | |
| 		}
 | |
| #else
 | |
| 		void					EnableMargin(bool enable)
 | |
| 		{
 | |
| 			if(enable)
 | |
| 				Ls=&btConvexShape::localGetSupportVertexNonVirtual;
 | |
| 			else
 | |
| 				Ls=&btConvexShape::localGetSupportVertexWithoutMarginNonVirtual;
 | |
| 		}	
 | |
| 		inline btVector3		Support0(const btVector3& d) const
 | |
| 		{
 | |
| 			return(((m_shapes[0])->*(Ls))(d));
 | |
| 		}
 | |
| 		inline btVector3		Support1(const btVector3& d) const
 | |
| 		{
 | |
| 			return(m_toshape0*((m_shapes[1])->*(Ls))(m_toshape1*d));
 | |
| 		}
 | |
| #endif //__SPU__
 | |
| 
 | |
| 		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));
 | |
| 		}
 | |
| 	};
 | |
| 
 | |
| 	typedef	MinkowskiDiff	tShape;
 | |
| 
 | |
| 
 | |
| 	// GJK
 | |
| 	struct	GJK
 | |
| 	{
 | |
| 		/* Types		*/ 
 | |
| 		struct	sSV
 | |
| 		{
 | |
| 			btVector3	d,w;
 | |
| 		};
 | |
| 		struct	sSimplex
 | |
| 		{
 | |
| 			sSV*		c[4];
 | |
| 			btScalar	p[4];
 | |
| 			U			rank;
 | |
| 		};
 | |
| 		struct	eStatus	{ enum _ {
 | |
| 			Valid,
 | |
| 			Inside,
 | |
| 			Failed		};};
 | |
| 			/* Fields		*/ 
 | |
| 			tShape			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;
 | |
| 			eStatus::_		m_status;
 | |
| 			/* Methods		*/ 
 | |
| 			GJK()
 | |
| 			{
 | |
| 				Initialize();
 | |
| 			}
 | |
| 			void				Initialize()
 | |
| 			{
 | |
| 				m_ray		=	btVector3(0,0,0);
 | |
| 				m_nfree		=	0;
 | |
| 				m_status	=	eStatus::Failed;
 | |
| 				m_current	=	0;
 | |
| 				m_distance	=	0;
 | |
| 			}
 | |
| 			eStatus::_			Evaluate(const tShape& 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			=	eStatus::Valid;
 | |
| 				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=eStatus::Inside;
 | |
| 						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_ACCURACY*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=eStatus::Inside;
 | |
| 					}
 | |
| 					else
 | |
| 					{/* Return old simplex				*/ 
 | |
| 						removevertice(m_simplices[m_current]);
 | |
| 						break;
 | |
| 					}
 | |
| 					m_status=((++iterations)<GJK_MAX_ITERATIONS)?m_status:eStatus::Failed;
 | |
| 				} while(m_status==eStatus::Valid);
 | |
| 				m_simplex=&m_simplices[m_current];
 | |
| 				switch(m_status)
 | |
| 				{
 | |
| 				case	eStatus::Valid:		m_distance=m_ray.length();break;
 | |
| 				case	eStatus::Inside:	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);
 | |
| 			}
 | |
| 	};
 | |
| 
 | |
| 	// EPA
 | |
| 	struct	EPA
 | |
| 	{
 | |
| 		/* Types		*/ 
 | |
| 		typedef	GJK::sSV	sSV;
 | |
| 		struct	sFace
 | |
| 		{
 | |
| 			btVector3	n;
 | |
| 			btScalar	d;
 | |
| 			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)	{}
 | |
| 		};
 | |
| 		struct	eStatus { enum _ {
 | |
| 			Valid,
 | |
| 			Touching,
 | |
| 			Degenerated,
 | |
| 			NonConvex,
 | |
| 			InvalidHull,		
 | |
| 			OutOfFaces,
 | |
| 			OutOfVertices,
 | |
| 			AccuraryReached,
 | |
| 			FallBack,
 | |
| 			Failed		};};
 | |
| 			/* Fields		*/ 
 | |
| 			eStatus::_		m_status;
 | |
| 			GJK::sSimplex	m_result;
 | |
| 			btVector3		m_normal;
 | |
| 			btScalar		m_depth;
 | |
| 			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	=	eStatus::Failed;
 | |
| 				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]);
 | |
| 				}
 | |
| 			}
 | |
| 			eStatus::_			Evaluate(GJK& gjk,const btVector3& guess)
 | |
| 			{
 | |
| 				GJK::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	=	eStatus::Valid;
 | |
| 					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=eStatus::Valid;
 | |
| 						for(;iterations<EPA_MAX_ITERATIONS;++iterations)
 | |
| 						{
 | |
| 							if(m_nextsv<EPA_MAX_VERTICES)
 | |
| 							{	
 | |
| 								sHorizon		horizon;
 | |
| 								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=eStatus::InvalidHull;break; }
 | |
| 								} else { m_status=eStatus::AccuraryReached;break; }
 | |
| 							} else { m_status=eStatus::OutOfVertices;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	=	eStatus::FallBack;
 | |
| 				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, sSV* a, 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(sSV* a,sSV* b,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=eStatus::NonConvex;
 | |
| 					}
 | |
| 					else
 | |
| 						m_status=eStatus::Degenerated;
 | |
| 
 | |
| 					remove(m_hull, face);
 | |
| 					append(m_stock, face);
 | |
| 					return 0;
 | |
| 
 | |
| 				}
 | |
| 				m_status = m_stock.root ? eStatus::OutOfVertices : eStatus::OutOfFaces;
 | |
| 				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,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);
 | |
| 			}
 | |
| 
 | |
| 	};
 | |
| 
 | |
| 	//
 | |
| 	static void	Initialize(	const btConvexShape* shape0,const btTransform& wtrs0,
 | |
| 		const btConvexShape* shape1,const btTransform& wtrs1,
 | |
| 		btGjkEpaSolver2::sResults& results,
 | |
| 		tShape& shape,
 | |
| 		bool withmargins)
 | |
| 	{
 | |
| 		/* Results		*/ 
 | |
| 		results.witnesses[0]	=
 | |
| 			results.witnesses[1]	=	btVector3(0,0,0);
 | |
| 		results.status			=	btGjkEpaSolver2::sResults::Separated;
 | |
| 		/* Shape		*/ 
 | |
| 		shape.m_shapes[0]		=	shape0;
 | |
| 		shape.m_shapes[1]		=	shape1;
 | |
| 		shape.m_toshape1		=	wtrs1.getBasis().transposeTimes(wtrs0.getBasis());
 | |
| 		shape.m_toshape0		=	wtrs0.inverseTimes(wtrs1);
 | |
| 		shape.EnableMargin(withmargins);
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| //
 | |
| // Api
 | |
| //
 | |
| 
 | |
| using namespace	gjkepa2_impl;
 | |
| 
 | |
| //
 | |
| int			btGjkEpaSolver2::StackSizeRequirement()
 | |
| {
 | |
| 	return(sizeof(GJK)+sizeof(EPA));
 | |
| }
 | |
| 
 | |
| //
 | |
| bool		btGjkEpaSolver2::Distance(	const btConvexShape*	shape0,
 | |
| 									  const btTransform&		wtrs0,
 | |
| 									  const btConvexShape*	shape1,
 | |
| 									  const btTransform&		wtrs1,
 | |
| 									  const btVector3&		guess,
 | |
| 									  sResults&				results)
 | |
| {
 | |
| 	tShape			shape;
 | |
| 	Initialize(shape0,wtrs0,shape1,wtrs1,results,shape,false);
 | |
| 	GJK				gjk;
 | |
| 	GJK::eStatus::_	gjk_status=gjk.Evaluate(shape,guess);
 | |
| 	if(gjk_status==GJK::eStatus::Valid)
 | |
| 	{
 | |
| 		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]	=	wtrs0*w0;
 | |
| 		results.witnesses[1]	=	wtrs0*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==GJK::eStatus::Inside?
 | |
| 			sResults::Penetrating	:
 | |
| 		sResults::GJK_Failed	;
 | |
| 		return(false);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| //
 | |
| bool	btGjkEpaSolver2::Penetration(	const btConvexShape*	shape0,
 | |
| 									 const btTransform&		wtrs0,
 | |
| 									 const btConvexShape*	shape1,
 | |
| 									 const btTransform&		wtrs1,
 | |
| 									 const btVector3&		guess,
 | |
| 									 sResults&				results,
 | |
| 									 bool					usemargins)
 | |
| {
 | |
| 	tShape			shape;
 | |
| 	Initialize(shape0,wtrs0,shape1,wtrs1,results,shape,usemargins);
 | |
| 	GJK				gjk;	
 | |
| 	GJK::eStatus::_	gjk_status=gjk.Evaluate(shape,-guess);
 | |
| 	switch(gjk_status)
 | |
| 	{
 | |
| 	case	GJK::eStatus::Inside:
 | |
| 		{
 | |
| 			EPA				epa;
 | |
| 			EPA::eStatus::_	epa_status=epa.Evaluate(gjk,-guess);
 | |
| 			if(epa_status!=EPA::eStatus::Failed)
 | |
| 			{
 | |
| 				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			=	sResults::Penetrating;
 | |
| 				results.witnesses[0]	=	wtrs0*w0;
 | |
| 				results.witnesses[1]	=	wtrs0*(w0-epa.m_normal*epa.m_depth);
 | |
| 				results.normal			=	-epa.m_normal;
 | |
| 				results.distance		=	-epa.m_depth;
 | |
| 				return(true);
 | |
| 			} else results.status=sResults::EPA_Failed;
 | |
| 		}
 | |
| 		break;
 | |
| 	case	GJK::eStatus::Failed:
 | |
| 		results.status=sResults::GJK_Failed;
 | |
| 		break;
 | |
| 		default:
 | |
| 					{
 | |
| 					}
 | |
| 	}
 | |
| 	return(false);
 | |
| }
 | |
| 
 | |
| #ifndef __SPU__
 | |
| //
 | |
| btScalar	btGjkEpaSolver2::SignedDistance(const btVector3& position,
 | |
| 											btScalar margin,
 | |
| 											const btConvexShape* shape0,
 | |
| 											const btTransform& wtrs0,
 | |
| 											sResults& results)
 | |
| {
 | |
| 	tShape			shape;
 | |
| 	btSphereShape	shape1(margin);
 | |
| 	btTransform		wtrs1(btQuaternion(0,0,0,1),position);
 | |
| 	Initialize(shape0,wtrs0,&shape1,wtrs1,results,shape,false);
 | |
| 	GJK				gjk;	
 | |
| 	GJK::eStatus::_	gjk_status=gjk.Evaluate(shape,btVector3(1,1,1));
 | |
| 	if(gjk_status==GJK::eStatus::Valid)
 | |
| 	{
 | |
| 		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]	=	wtrs0*w0;
 | |
| 		results.witnesses[1]	=	wtrs0*w1;
 | |
| 		const btVector3	delta=	results.witnesses[1]-
 | |
| 			results.witnesses[0];
 | |
| 		const btScalar	margin=	shape0->getMarginNonVirtual()+
 | |
| 			shape1.getMarginNonVirtual();
 | |
| 		const btScalar	length=	delta.length();	
 | |
| 		results.normal			=	delta/length;
 | |
| 		results.witnesses[0]	+=	results.normal*margin;
 | |
| 		return(length-margin);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if(gjk_status==GJK::eStatus::Inside)
 | |
| 		{
 | |
| 			if(Penetration(shape0,wtrs0,&shape1,wtrs1,gjk.m_ray,results))
 | |
| 			{
 | |
| 				const btVector3	delta=	results.witnesses[0]-
 | |
| 					results.witnesses[1];
 | |
| 				const btScalar	length=	delta.length();
 | |
| 				if (length >= SIMD_EPSILON)
 | |
| 					results.normal	=	delta/length;			
 | |
| 				return(-length);
 | |
| 			}
 | |
| 		}	
 | |
| 	}
 | |
| 	return(SIMD_INFINITY);
 | |
| }
 | |
| 
 | |
| //
 | |
| bool	btGjkEpaSolver2::SignedDistance(const btConvexShape*	shape0,
 | |
| 										const btTransform&		wtrs0,
 | |
| 										const btConvexShape*	shape1,
 | |
| 										const btTransform&		wtrs1,
 | |
| 										const btVector3&		guess,
 | |
| 										sResults&				results)
 | |
| {
 | |
| 	if(!Distance(shape0,wtrs0,shape1,wtrs1,guess,results))
 | |
| 		return(Penetration(shape0,wtrs0,shape1,wtrs1,guess,results,false));
 | |
| 	else
 | |
| 		return(true);
 | |
| }
 | |
| #endif //__SPU__
 | |
| 
 | |
| /* Symbols cleanup		*/ 
 | |
| 
 | |
| #undef GJK_MAX_ITERATIONS
 | |
| #undef GJK_ACCURACY
 | |
| #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
 |