1220 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			1220 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | /*
 | ||
|  | Bullet Continuous Collision Detection and Physics Library | ||
|  | Copyright (c) 2003-2006 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. | ||
|  | */ | ||
|  | ///btSoftBodyHelpers.cpp by Nathanael Presson
 | ||
|  | 
 | ||
|  | #include "btSoftBodyInternals.h"
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include "btSoftBodyHelpers.h"
 | ||
|  | #include "LinearMath/btConvexHull.h"
 | ||
|  | #include "LinearMath/btConvexHullComputer.h"
 | ||
|  | 
 | ||
|  | 
 | ||
|  | //
 | ||
|  | static void				drawVertex(	btIDebugDraw* idraw, | ||
|  | 								   const btVector3& x,btScalar s,const btVector3& c) | ||
|  | { | ||
|  | 	idraw->drawLine(x-btVector3(s,0,0),x+btVector3(s,0,0),c); | ||
|  | 	idraw->drawLine(x-btVector3(0,s,0),x+btVector3(0,s,0),c); | ||
|  | 	idraw->drawLine(x-btVector3(0,0,s),x+btVector3(0,0,s),c); | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | static void				drawBox(	btIDebugDraw* idraw, | ||
|  | 								const btVector3& mins, | ||
|  | 								const btVector3& maxs, | ||
|  | 								const btVector3& color) | ||
|  | { | ||
|  | 	const btVector3	c[]={	btVector3(mins.x(),mins.y(),mins.z()), | ||
|  | 		btVector3(maxs.x(),mins.y(),mins.z()), | ||
|  | 		btVector3(maxs.x(),maxs.y(),mins.z()), | ||
|  | 		btVector3(mins.x(),maxs.y(),mins.z()), | ||
|  | 		btVector3(mins.x(),mins.y(),maxs.z()), | ||
|  | 		btVector3(maxs.x(),mins.y(),maxs.z()), | ||
|  | 		btVector3(maxs.x(),maxs.y(),maxs.z()), | ||
|  | 		btVector3(mins.x(),maxs.y(),maxs.z())}; | ||
|  | 	idraw->drawLine(c[0],c[1],color);idraw->drawLine(c[1],c[2],color); | ||
|  | 	idraw->drawLine(c[2],c[3],color);idraw->drawLine(c[3],c[0],color); | ||
|  | 	idraw->drawLine(c[4],c[5],color);idraw->drawLine(c[5],c[6],color); | ||
|  | 	idraw->drawLine(c[6],c[7],color);idraw->drawLine(c[7],c[4],color); | ||
|  | 	idraw->drawLine(c[0],c[4],color);idraw->drawLine(c[1],c[5],color); | ||
|  | 	idraw->drawLine(c[2],c[6],color);idraw->drawLine(c[3],c[7],color); | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | static void				drawTree(	btIDebugDraw* idraw, | ||
|  | 								 const btDbvtNode* node, | ||
|  | 								 int depth, | ||
|  | 								 const btVector3& ncolor, | ||
|  | 								 const btVector3& lcolor, | ||
|  | 								 int mindepth, | ||
|  | 								 int maxdepth) | ||
|  | { | ||
|  | 	if(node) | ||
|  | 	{ | ||
|  | 		if(node->isinternal()&&((depth<maxdepth)||(maxdepth<0))) | ||
|  | 		{ | ||
|  | 			drawTree(idraw,node->childs[0],depth+1,ncolor,lcolor,mindepth,maxdepth); | ||
|  | 			drawTree(idraw,node->childs[1],depth+1,ncolor,lcolor,mindepth,maxdepth); | ||
|  | 		} | ||
|  | 		if(depth>=mindepth) | ||
|  | 		{ | ||
|  | 			const btScalar	scl=(btScalar)(node->isinternal()?1:1); | ||
|  | 			const btVector3	mi=node->volume.Center()-node->volume.Extents()*scl; | ||
|  | 			const btVector3	mx=node->volume.Center()+node->volume.Extents()*scl; | ||
|  | 			drawBox(idraw,mi,mx,node->isleaf()?lcolor:ncolor); | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | template <typename T> | ||
|  | static inline T				sum(const btAlignedObjectArray<T>& items) | ||
|  | { | ||
|  | 	T	v; | ||
|  | 	if(items.size()) | ||
|  | 	{ | ||
|  | 		v=items[0]; | ||
|  | 		for(int i=1,ni=items.size();i<ni;++i) | ||
|  | 		{ | ||
|  | 			v+=items[i]; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return(v); | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | template <typename T,typename Q> | ||
|  | static inline void			add(btAlignedObjectArray<T>& items,const Q& value) | ||
|  | { | ||
|  | 	for(int i=0,ni=items.size();i<ni;++i) | ||
|  | 	{ | ||
|  | 		items[i]+=value; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | template <typename T,typename Q> | ||
|  | static inline void			mul(btAlignedObjectArray<T>& items,const Q& value) | ||
|  | { | ||
|  | 	for(int i=0,ni=items.size();i<ni;++i) | ||
|  | 	{ | ||
|  | 		items[i]*=value; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | template <typename T> | ||
|  | static inline T				average(const btAlignedObjectArray<T>& items) | ||
|  | { | ||
|  | 	const btScalar	n=(btScalar)(items.size()>0?items.size():1); | ||
|  | 	return(sum(items)/n); | ||
|  | } | ||
|  | 
 | ||
|  | #if 0
 | ||
|  | //
 | ||
|  |  inline static btScalar		tetravolume(const btVector3& x0, | ||
|  | 										const btVector3& x1, | ||
|  | 										const btVector3& x2, | ||
|  | 										const btVector3& x3) | ||
|  | { | ||
|  | 	const btVector3	a=x1-x0; | ||
|  | 	const btVector3	b=x2-x0; | ||
|  | 	const btVector3	c=x3-x0; | ||
|  | 	return(btDot(a,btCross(b,c))); | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | //
 | ||
|  | #if 0
 | ||
|  | static btVector3		stresscolor(btScalar stress) | ||
|  | { | ||
|  | 	static const btVector3	spectrum[]=	{	btVector3(1,0,1), | ||
|  | 		btVector3(0,0,1), | ||
|  | 		btVector3(0,1,1), | ||
|  | 		btVector3(0,1,0), | ||
|  | 		btVector3(1,1,0), | ||
|  | 		btVector3(1,0,0), | ||
|  | 		btVector3(1,0,0)}; | ||
|  | 	static const int		ncolors=sizeof(spectrum)/sizeof(spectrum[0])-1; | ||
|  | 	static const btScalar	one=1; | ||
|  | 	stress=btMax<btScalar>(0,btMin<btScalar>(1,stress))*ncolors; | ||
|  | 	const int				sel=(int)stress; | ||
|  | 	const btScalar			frc=stress-sel; | ||
|  | 	return(spectrum[sel]+(spectrum[sel+1]-spectrum[sel])*frc); | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | //
 | ||
|  | void			btSoftBodyHelpers::Draw(	btSoftBody* psb, | ||
|  | 										btIDebugDraw* idraw, | ||
|  | 										int drawflags) | ||
|  | { | ||
|  | 	const btScalar		scl=(btScalar)0.1; | ||
|  | 	const btScalar		nscl=scl*5; | ||
|  | 	const btVector3		lcolor=btVector3(0,0,0); | ||
|  | 	const btVector3		ncolor=btVector3(1,1,1); | ||
|  | 	const btVector3		ccolor=btVector3(1,0,0); | ||
|  | 	int i,j,nj; | ||
|  | 
 | ||
|  | 		/* Clusters	*/  | ||
|  | 	if(0!=(drawflags&fDrawFlags::Clusters)) | ||
|  | 	{ | ||
|  | 		srand(1806); | ||
|  | 		for(i=0;i<psb->m_clusters.size();++i) | ||
|  | 		{ | ||
|  | 			if(psb->m_clusters[i]->m_collide) | ||
|  | 			{ | ||
|  | 				btVector3						color(	rand()/(btScalar)RAND_MAX, | ||
|  | 					rand()/(btScalar)RAND_MAX, | ||
|  | 					rand()/(btScalar)RAND_MAX); | ||
|  | 				color=color.normalized()*0.75; | ||
|  | 				btAlignedObjectArray<btVector3>	vertices; | ||
|  | 				vertices.resize(psb->m_clusters[i]->m_nodes.size()); | ||
|  | 				for(j=0,nj=vertices.size();j<nj;++j) | ||
|  | 				{				 | ||
|  | 					vertices[j]=psb->m_clusters[i]->m_nodes[j]->m_x; | ||
|  | 				} | ||
|  | #define USE_NEW_CONVEX_HULL_COMPUTER
 | ||
|  | #ifdef USE_NEW_CONVEX_HULL_COMPUTER
 | ||
|  | 				btConvexHullComputer	computer; | ||
|  | 				int stride = sizeof(btVector3); | ||
|  | 				int count = vertices.size(); | ||
|  | 				btScalar shrink=0.f; | ||
|  | 				btScalar shrinkClamp=0.f; | ||
|  | 				computer.compute(&vertices[0].getX(),stride,count,shrink,shrinkClamp); | ||
|  | 				for (int i=0;i<computer.faces.size();i++) | ||
|  | 				{ | ||
|  | 
 | ||
|  | 					int face = computer.faces[i]; | ||
|  | 					//printf("face=%d\n",face);
 | ||
|  | 					const btConvexHullComputer::Edge*  firstEdge = &computer.edges[face]; | ||
|  | 					const btConvexHullComputer::Edge*  edge = firstEdge->getNextEdgeOfFace(); | ||
|  | 
 | ||
|  | 					int v0 = firstEdge->getSourceVertex(); | ||
|  | 					int v1 = firstEdge->getTargetVertex(); | ||
|  | 					while (edge!=firstEdge) | ||
|  | 					{ | ||
|  | 						int v2 = edge->getTargetVertex(); | ||
|  | 						idraw->drawTriangle(computer.vertices[v0],computer.vertices[v1],computer.vertices[v2],color,1); | ||
|  | 						edge = edge->getNextEdgeOfFace(); | ||
|  | 						v0=v1; | ||
|  | 						v1=v2; | ||
|  | 					}; | ||
|  | 				} | ||
|  | #else
 | ||
|  | 
 | ||
|  | 				HullDesc		hdsc(QF_TRIANGLES,vertices.size(),&vertices[0]); | ||
|  | 				HullResult		hres; | ||
|  | 				HullLibrary		hlib; | ||
|  | 				hdsc.mMaxVertices=vertices.size(); | ||
|  | 				hlib.CreateConvexHull(hdsc,hres); | ||
|  | 				const btVector3	center=average(hres.m_OutputVertices); | ||
|  | 				add(hres.m_OutputVertices,-center); | ||
|  | 				mul(hres.m_OutputVertices,(btScalar)1); | ||
|  | 				add(hres.m_OutputVertices,center); | ||
|  | 				for(j=0;j<(int)hres.mNumFaces;++j) | ||
|  | 				{ | ||
|  | 					const int idx[]={hres.m_Indices[j*3+0],hres.m_Indices[j*3+1],hres.m_Indices[j*3+2]}; | ||
|  | 					idraw->drawTriangle(hres.m_OutputVertices[idx[0]], | ||
|  | 						hres.m_OutputVertices[idx[1]], | ||
|  | 						hres.m_OutputVertices[idx[2]], | ||
|  | 						color,1); | ||
|  | 				} | ||
|  | 				hlib.ReleaseResult(hres); | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 			} | ||
|  | 			/* Velocities	*/  | ||
|  | #if 0
 | ||
|  | 			for(int j=0;j<psb->m_clusters[i].m_nodes.size();++j) | ||
|  | 			{ | ||
|  | 				const btSoftBody::Cluster&	c=psb->m_clusters[i]; | ||
|  | 				const btVector3				r=c.m_nodes[j]->m_x-c.m_com; | ||
|  | 				const btVector3				v=c.m_lv+btCross(c.m_av,r); | ||
|  | 				idraw->drawLine(c.m_nodes[j]->m_x,c.m_nodes[j]->m_x+v,btVector3(1,0,0)); | ||
|  | 			} | ||
|  | #endif
 | ||
|  | 			/* Frame		*/  | ||
|  | 	//		btSoftBody::Cluster& c=*psb->m_clusters[i];
 | ||
|  | 	//		idraw->drawLine(c.m_com,c.m_framexform*btVector3(10,0,0),btVector3(1,0,0));
 | ||
|  | 	//		idraw->drawLine(c.m_com,c.m_framexform*btVector3(0,10,0),btVector3(0,1,0));
 | ||
|  | 	//		idraw->drawLine(c.m_com,c.m_framexform*btVector3(0,0,10),btVector3(0,0,1));
 | ||
|  | 		} | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 		/* Nodes	*/  | ||
|  | 		if(0!=(drawflags&fDrawFlags::Nodes)) | ||
|  | 		{ | ||
|  | 			for(i=0;i<psb->m_nodes.size();++i) | ||
|  | 			{ | ||
|  | 				const btSoftBody::Node&	n=psb->m_nodes[i]; | ||
|  | 				if(0==(n.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; | ||
|  | 				idraw->drawLine(n.m_x-btVector3(scl,0,0),n.m_x+btVector3(scl,0,0),btVector3(1,0,0)); | ||
|  | 				idraw->drawLine(n.m_x-btVector3(0,scl,0),n.m_x+btVector3(0,scl,0),btVector3(0,1,0)); | ||
|  | 				idraw->drawLine(n.m_x-btVector3(0,0,scl),n.m_x+btVector3(0,0,scl),btVector3(0,0,1)); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		/* Links	*/  | ||
|  | 		if(0!=(drawflags&fDrawFlags::Links)) | ||
|  | 		{ | ||
|  | 			for(i=0;i<psb->m_links.size();++i) | ||
|  | 			{ | ||
|  | 				const btSoftBody::Link&	l=psb->m_links[i]; | ||
|  | 				if(0==(l.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; | ||
|  | 				idraw->drawLine(l.m_n[0]->m_x,l.m_n[1]->m_x,lcolor); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		/* Normals	*/  | ||
|  | 		if(0!=(drawflags&fDrawFlags::Normals)) | ||
|  | 		{ | ||
|  | 			for(i=0;i<psb->m_nodes.size();++i) | ||
|  | 			{ | ||
|  | 				const btSoftBody::Node&	n=psb->m_nodes[i]; | ||
|  | 				if(0==(n.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; | ||
|  | 				const btVector3			d=n.m_n*nscl; | ||
|  | 				idraw->drawLine(n.m_x,n.m_x+d,ncolor); | ||
|  | 				idraw->drawLine(n.m_x,n.m_x-d,ncolor*0.5); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		/* Contacts	*/  | ||
|  | 		if(0!=(drawflags&fDrawFlags::Contacts)) | ||
|  | 		{ | ||
|  | 			static const btVector3		axis[]={btVector3(1,0,0), | ||
|  | 				btVector3(0,1,0), | ||
|  | 				btVector3(0,0,1)}; | ||
|  | 			for(i=0;i<psb->m_rcontacts.size();++i) | ||
|  | 			{		 | ||
|  | 				const btSoftBody::RContact&	c=psb->m_rcontacts[i]; | ||
|  | 				const btVector3				o=	c.m_node->m_x-c.m_cti.m_normal* | ||
|  | 					(btDot(c.m_node->m_x,c.m_cti.m_normal)+c.m_cti.m_offset); | ||
|  | 				const btVector3				x=btCross(c.m_cti.m_normal,axis[c.m_cti.m_normal.minAxis()]).normalized(); | ||
|  | 				const btVector3				y=btCross(x,c.m_cti.m_normal).normalized(); | ||
|  | 				idraw->drawLine(o-x*nscl,o+x*nscl,ccolor); | ||
|  | 				idraw->drawLine(o-y*nscl,o+y*nscl,ccolor); | ||
|  | 				idraw->drawLine(o,o+c.m_cti.m_normal*nscl*3,btVector3(1,1,0)); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		/* Faces	*/  | ||
|  | 	if(0!=(drawflags&fDrawFlags::Faces)) | ||
|  | 	{ | ||
|  | 		const btScalar	scl=(btScalar)0.8; | ||
|  | 		const btScalar	alp=(btScalar)1; | ||
|  | 		const btVector3	col(0,(btScalar)0.7,0); | ||
|  | 		for(i=0;i<psb->m_faces.size();++i) | ||
|  | 		{ | ||
|  | 			const btSoftBody::Face&	f=psb->m_faces[i]; | ||
|  | 			if(0==(f.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; | ||
|  | 			const btVector3			x[]={f.m_n[0]->m_x,f.m_n[1]->m_x,f.m_n[2]->m_x}; | ||
|  | 			const btVector3			c=(x[0]+x[1]+x[2])/3; | ||
|  | 			idraw->drawTriangle((x[0]-c)*scl+c, | ||
|  | 				(x[1]-c)*scl+c, | ||
|  | 				(x[2]-c)*scl+c, | ||
|  | 				col,alp); | ||
|  | 		}	 | ||
|  | 	} | ||
|  | 	/* Tetras	*/  | ||
|  | 	if(0!=(drawflags&fDrawFlags::Tetras)) | ||
|  | 	{ | ||
|  | 		const btScalar	scl=(btScalar)0.8; | ||
|  | 		const btScalar	alp=(btScalar)1; | ||
|  | 		const btVector3	col((btScalar)0.3,(btScalar)0.3,(btScalar)0.7); | ||
|  | 		for(int i=0;i<psb->m_tetras.size();++i) | ||
|  | 		{ | ||
|  | 			const btSoftBody::Tetra&	t=psb->m_tetras[i]; | ||
|  | 			if(0==(t.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; | ||
|  | 			const btVector3				x[]={t.m_n[0]->m_x,t.m_n[1]->m_x,t.m_n[2]->m_x,t.m_n[3]->m_x}; | ||
|  | 			const btVector3				c=(x[0]+x[1]+x[2]+x[3])/4; | ||
|  | 			idraw->drawTriangle((x[0]-c)*scl+c,(x[1]-c)*scl+c,(x[2]-c)*scl+c,col,alp); | ||
|  | 			idraw->drawTriangle((x[0]-c)*scl+c,(x[1]-c)*scl+c,(x[3]-c)*scl+c,col,alp); | ||
|  | 			idraw->drawTriangle((x[1]-c)*scl+c,(x[2]-c)*scl+c,(x[3]-c)*scl+c,col,alp); | ||
|  | 			idraw->drawTriangle((x[2]-c)*scl+c,(x[0]-c)*scl+c,(x[3]-c)*scl+c,col,alp); | ||
|  | 		}	 | ||
|  | 	} | ||
|  | 	} | ||
|  | 	/* Anchors	*/  | ||
|  | 	if(0!=(drawflags&fDrawFlags::Anchors)) | ||
|  | 	{ | ||
|  | 		for(i=0;i<psb->m_anchors.size();++i) | ||
|  | 		{ | ||
|  | 			const btSoftBody::Anchor&	a=psb->m_anchors[i]; | ||
|  | 			const btVector3				q=a.m_body->getWorldTransform()*a.m_local; | ||
|  | 			drawVertex(idraw,a.m_node->m_x,0.25,btVector3(1,0,0)); | ||
|  | 			drawVertex(idraw,q,0.25,btVector3(0,1,0)); | ||
|  | 			idraw->drawLine(a.m_node->m_x,q,btVector3(1,1,1)); | ||
|  | 		} | ||
|  | 		for(i=0;i<psb->m_nodes.size();++i) | ||
|  | 		{ | ||
|  | 			const btSoftBody::Node&	n=psb->m_nodes[i];		 | ||
|  | 			if(0==(n.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; | ||
|  | 			if(n.m_im<=0) | ||
|  | 			{ | ||
|  | 				drawVertex(idraw,n.m_x,0.25,btVector3(1,0,0)); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	 | ||
|  | 
 | ||
|  | 	/* Notes	*/  | ||
|  | 	if(0!=(drawflags&fDrawFlags::Notes)) | ||
|  | 	{ | ||
|  | 		for(i=0;i<psb->m_notes.size();++i) | ||
|  | 		{ | ||
|  | 			const btSoftBody::Note&	n=psb->m_notes[i]; | ||
|  | 			btVector3				p=n.m_offset; | ||
|  | 			for(int j=0;j<n.m_rank;++j) | ||
|  | 			{ | ||
|  | 				p+=n.m_nodes[j]->m_x*n.m_coords[j]; | ||
|  | 			} | ||
|  | 			idraw->draw3dText(p,n.m_text); | ||
|  | 		} | ||
|  | 	} | ||
|  | 	/* Node tree	*/  | ||
|  | 	if(0!=(drawflags&fDrawFlags::NodeTree))		DrawNodeTree(psb,idraw); | ||
|  | 	/* Face tree	*/  | ||
|  | 	if(0!=(drawflags&fDrawFlags::FaceTree))		DrawFaceTree(psb,idraw); | ||
|  | 	/* Cluster tree	*/  | ||
|  | 	if(0!=(drawflags&fDrawFlags::ClusterTree))	DrawClusterTree(psb,idraw); | ||
|  | 	/* Joints		*/  | ||
|  | 	if(0!=(drawflags&fDrawFlags::Joints)) | ||
|  | 	{ | ||
|  | 		for(i=0;i<psb->m_joints.size();++i) | ||
|  | 		{ | ||
|  | 			const btSoftBody::Joint*	pj=psb->m_joints[i]; | ||
|  | 			switch(pj->Type()) | ||
|  | 			{ | ||
|  | 			case	btSoftBody::Joint::eType::Linear: | ||
|  | 				{ | ||
|  | 					const btSoftBody::LJoint*	pjl=(const btSoftBody::LJoint*)pj; | ||
|  | 					const btVector3	a0=pj->m_bodies[0].xform()*pjl->m_refs[0]; | ||
|  | 					const btVector3	a1=pj->m_bodies[1].xform()*pjl->m_refs[1]; | ||
|  | 					idraw->drawLine(pj->m_bodies[0].xform().getOrigin(),a0,btVector3(1,1,0)); | ||
|  | 					idraw->drawLine(pj->m_bodies[1].xform().getOrigin(),a1,btVector3(0,1,1)); | ||
|  | 					drawVertex(idraw,a0,0.25,btVector3(1,1,0)); | ||
|  | 					drawVertex(idraw,a1,0.25,btVector3(0,1,1)); | ||
|  | 				} | ||
|  | 				break; | ||
|  | 			case	btSoftBody::Joint::eType::Angular: | ||
|  | 				{ | ||
|  | 					//const btSoftBody::AJoint*	pja=(const btSoftBody::AJoint*)pj;
 | ||
|  | 					const btVector3	o0=pj->m_bodies[0].xform().getOrigin(); | ||
|  | 					const btVector3	o1=pj->m_bodies[1].xform().getOrigin(); | ||
|  | 					const btVector3	a0=pj->m_bodies[0].xform().getBasis()*pj->m_refs[0]; | ||
|  | 					const btVector3	a1=pj->m_bodies[1].xform().getBasis()*pj->m_refs[1]; | ||
|  | 					idraw->drawLine(o0,o0+a0*10,btVector3(1,1,0)); | ||
|  | 					idraw->drawLine(o0,o0+a1*10,btVector3(1,1,0)); | ||
|  | 					idraw->drawLine(o1,o1+a0*10,btVector3(0,1,1)); | ||
|  | 					idraw->drawLine(o1,o1+a1*10,btVector3(0,1,1)); | ||
|  | 					break; | ||
|  | 				} | ||
|  | 				default: | ||
|  | 				{ | ||
|  | 				} | ||
|  | 					 | ||
|  | 			}		 | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | void			btSoftBodyHelpers::DrawInfos(		btSoftBody* psb, | ||
|  | 											 btIDebugDraw* idraw, | ||
|  | 											 bool masses, | ||
|  | 											 bool areas, | ||
|  | 											 bool /*stress*/) | ||
|  | { | ||
|  | 	for(int i=0;i<psb->m_nodes.size();++i) | ||
|  | 	{ | ||
|  | 		const btSoftBody::Node&	n=psb->m_nodes[i]; | ||
|  | 		char					text[2048]={0}; | ||
|  | 		char					buff[1024]; | ||
|  | 		if(masses) | ||
|  | 		{ | ||
|  | 			sprintf(buff," M(%.2f)",1/n.m_im); | ||
|  | 			strcat(text,buff); | ||
|  | 		} | ||
|  | 		if(areas) | ||
|  | 		{ | ||
|  | 			sprintf(buff," A(%.2f)",n.m_area); | ||
|  | 			strcat(text,buff); | ||
|  | 		} | ||
|  | 		if(text[0]) idraw->draw3dText(n.m_x,text); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | void			btSoftBodyHelpers::DrawNodeTree(	btSoftBody* psb, | ||
|  | 												btIDebugDraw* idraw, | ||
|  | 												int mindepth, | ||
|  | 												int maxdepth) | ||
|  | { | ||
|  | 	drawTree(idraw,psb->m_ndbvt.m_root,0,btVector3(1,0,1),btVector3(1,1,1),mindepth,maxdepth); | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | void			btSoftBodyHelpers::DrawFaceTree(	btSoftBody* psb, | ||
|  | 												btIDebugDraw* idraw, | ||
|  | 												int mindepth, | ||
|  | 												int maxdepth) | ||
|  | { | ||
|  | 	drawTree(idraw,psb->m_fdbvt.m_root,0,btVector3(0,1,0),btVector3(1,0,0),mindepth,maxdepth); | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | void			btSoftBodyHelpers::DrawClusterTree(	btSoftBody* psb, | ||
|  | 												   btIDebugDraw* idraw, | ||
|  | 												   int mindepth, | ||
|  | 												   int maxdepth) | ||
|  | { | ||
|  | 	drawTree(idraw,psb->m_cdbvt.m_root,0,btVector3(0,1,1),btVector3(1,0,0),mindepth,maxdepth); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | //The btSoftBody object from the BulletSDK includes an array of Nodes and Links. These links appear
 | ||
|  | // to be first set up to connect a node to between 5 and 6 of its neighbors [480 links], 
 | ||
|  | //and then to the rest of the nodes after the execution of the Floyd-Warshall graph algorithm 
 | ||
|  | //[another 930 links]. 
 | ||
|  | //The way the links are stored by default, we have a number of cases where adjacent links share a node in common
 | ||
|  | // - this leads to the creation of a data dependency through memory. 
 | ||
|  | //The PSolve_Links() function reads and writes nodes as it iterates over each link. 
 | ||
|  | //So, we now have the possibility of a data dependency between iteration X 
 | ||
|  | //that processes link L with iteration X+1 that processes link L+1 
 | ||
|  | //because L and L+1 have one node in common, and iteration X updates the positions of that node, 
 | ||
|  | //and iteration X+1 reads in the position of that shared node.
 | ||
|  | //
 | ||
|  | //Such a memory dependency limits the ability of a modern CPU to speculate beyond 
 | ||
|  | //a certain point because it has to respect a possible dependency 
 | ||
|  | //- this prevents the CPU from making full use of its out-of-order resources. 
 | ||
|  | //If we re-order the links such that we minimize the cases where a link L and L+1 share a common node, 
 | ||
|  | //we create a temporal gap between when the node position is written, 
 | ||
|  | //and when it is subsequently read. This in turn allows the CPU to continue execution without 
 | ||
|  | //risking a dependency violation. Such a reordering would result in significant speedups on 
 | ||
|  | //modern CPUs with lots of execution resources. 
 | ||
|  | //In our testing, we see it have a tremendous impact not only on the A7, 
 | ||
|  | //but also on all x86 cores that ship with modern Macs. 
 | ||
|  | //The attached source file includes a single function (ReoptimizeLinkOrder) which can be called on a 
 | ||
|  | //btSoftBody object in the solveConstraints() function before the actual solver is invoked, 
 | ||
|  | //or right after generateBendingConstraints() once we have all 1410 links.
 | ||
|  | 
 | ||
|  | 
 | ||
|  | //===================================================================
 | ||
|  | //
 | ||
|  | //
 | ||
|  | // This function takes in a list of interdependent Links and tries 
 | ||
|  | // to maximize the distance between calculation
 | ||
|  | // of dependent links.  This increases the amount of parallelism that can
 | ||
|  | // be exploited by out-of-order instruction processors with large but
 | ||
|  | // (inevitably) finite instruction windows.
 | ||
|  | //
 | ||
|  | //===================================================================
 | ||
|  | 
 | ||
|  | // A small structure to track lists of dependent link calculations
 | ||
|  | class LinkDeps_t { | ||
|  | 	public: | ||
|  | 	int value;			// A link calculation that is dependent on this one
 | ||
|  | 		// Positive values = "input A" while negative values = "input B"
 | ||
|  | 	LinkDeps_t *next;	// Next dependence in the list
 | ||
|  | }; | ||
|  | typedef LinkDeps_t *LinkDepsPtr_t; | ||
|  | 
 | ||
|  | // Dependency list constants
 | ||
|  | #define REOP_NOT_DEPENDENT	-1
 | ||
|  | #define REOP_NODE_COMPLETE	-2	// Must be less than REOP_NOT_DEPENDENT
 | ||
|  | 
 | ||
|  | 
 | ||
|  | void btSoftBodyHelpers::ReoptimizeLinkOrder(btSoftBody *psb /* This can be replaced by a btSoftBody pointer */) | ||
|  | { | ||
|  | 	int i, nLinks=psb->m_links.size(), nNodes=psb->m_nodes.size(); | ||
|  | 	btSoftBody::Link *lr; | ||
|  | 	int ar, br; | ||
|  | 	btSoftBody::Node *node0 = &(psb->m_nodes[0]); | ||
|  | 	btSoftBody::Node *node1 = &(psb->m_nodes[1]); | ||
|  | 	LinkDepsPtr_t linkDep; | ||
|  | 	int readyListHead, readyListTail, linkNum, linkDepFrees, depLink; | ||
|  | 	 | ||
|  | 	// Allocate temporary buffers
 | ||
|  | 	int *nodeWrittenAt = new int[nNodes+1];	// What link calculation produced this node's current values?
 | ||
|  | 	int *linkDepA = new int[nLinks];			// Link calculation input is dependent upon prior calculation #N
 | ||
|  | 	int *linkDepB = new int[nLinks]; | ||
|  | 	int *readyList = new int[nLinks];		// List of ready-to-process link calculations (# of links, maximum)
 | ||
|  | 	LinkDeps_t *linkDepFreeList = new LinkDeps_t[2*nLinks];		// Dependent-on-me list elements (2x# of links, maximum)
 | ||
|  | 	LinkDepsPtr_t *linkDepListStarts = new LinkDepsPtr_t[nLinks];	// Start nodes of dependent-on-me lists, one for each link
 | ||
|  | 		 | ||
|  | 	// Copy the original, unsorted links to a side buffer
 | ||
|  | 	btSoftBody::Link *linkBuffer = new btSoftBody::Link[nLinks]; | ||
|  | 	memcpy(linkBuffer, &(psb->m_links[0]), sizeof(btSoftBody::Link)*nLinks); | ||
|  | 
 | ||
|  | 	// Clear out the node setup and ready list
 | ||
|  | 	for (i=0; i < nNodes+1; i++) { | ||
|  | 		nodeWrittenAt[i] = REOP_NOT_DEPENDENT; | ||
|  | 	} | ||
|  | 	for (i=0; i < nLinks; i++) { | ||
|  | 		linkDepListStarts[i] = NULL; | ||
|  | 	} | ||
|  | 	readyListHead = readyListTail = linkDepFrees = 0; | ||
|  | 
 | ||
|  | 	// Initial link analysis to set up data structures
 | ||
|  | 	for (i=0; i < nLinks; i++) { | ||
|  | 	 | ||
|  | 		// Note which prior link calculations we are dependent upon & build up dependence lists
 | ||
|  | 		lr = &(psb->m_links[i]); | ||
|  | 		ar = (lr->m_n[0] - node0)/(node1 - node0); | ||
|  | 		br = (lr->m_n[1] - node0)/(node1 - node0); | ||
|  | 		if (nodeWrittenAt[ar] > REOP_NOT_DEPENDENT) { | ||
|  | 			linkDepA[i] = nodeWrittenAt[ar]; | ||
|  | 			linkDep = &linkDepFreeList[linkDepFrees++]; | ||
|  | 			linkDep->value = i; | ||
|  | 			linkDep->next = linkDepListStarts[nodeWrittenAt[ar]]; | ||
|  | 			linkDepListStarts[nodeWrittenAt[ar]] = linkDep; | ||
|  | 		} else { | ||
|  | 			linkDepA[i] = REOP_NOT_DEPENDENT; | ||
|  | 		} | ||
|  | 		if (nodeWrittenAt[br] > REOP_NOT_DEPENDENT) { | ||
|  | 			linkDepB[i] = nodeWrittenAt[br]; | ||
|  | 			linkDep = &linkDepFreeList[linkDepFrees++]; | ||
|  | 			linkDep->value = -(i+1); | ||
|  | 			linkDep->next = linkDepListStarts[nodeWrittenAt[br]]; | ||
|  | 			linkDepListStarts[nodeWrittenAt[br]] = linkDep; | ||
|  | 		} else { | ||
|  | 			linkDepB[i] = REOP_NOT_DEPENDENT; | ||
|  | 		} | ||
|  | 		 | ||
|  | 		// Add this link to the initial ready list, if it is not dependent on any other links
 | ||
|  | 		if ((linkDepA[i] == REOP_NOT_DEPENDENT) && (linkDepB[i] == REOP_NOT_DEPENDENT)) { | ||
|  | 			readyList[readyListTail++] = i; | ||
|  | 			linkDepA[i] = linkDepB[i] = REOP_NODE_COMPLETE;	// Probably not needed now
 | ||
|  | 		} | ||
|  | 		 | ||
|  | 		// Update the nodes to mark which ones are calculated by this link
 | ||
|  | 		nodeWrittenAt[ar] = nodeWrittenAt[br] = i; | ||
|  | 	} | ||
|  | 	 | ||
|  | 	// Process the ready list and create the sorted list of links
 | ||
|  | 	// -- By treating the ready list as a queue, we maximize the distance between any
 | ||
|  | 	//    inter-dependent node calculations
 | ||
|  | 	// -- All other (non-related) nodes in the ready list will automatically be inserted
 | ||
|  | 	//    in between each set of inter-dependent link calculations by this loop
 | ||
|  | 	i = 0; | ||
|  | 	while (readyListHead != readyListTail) { | ||
|  | 		// Use ready list to select the next link to process
 | ||
|  | 		linkNum = readyList[readyListHead++]; | ||
|  | 		// Copy the next-to-calculate link back into the original link array
 | ||
|  | 		psb->m_links[i++] = linkBuffer[linkNum]; | ||
|  | 
 | ||
|  | 		// Free up any link inputs that are dependent on this one
 | ||
|  | 		linkDep = linkDepListStarts[linkNum]; | ||
|  | 		while (linkDep) { | ||
|  | 			depLink = linkDep->value; | ||
|  | 			if (depLink >= 0) { | ||
|  | 				linkDepA[depLink] = REOP_NOT_DEPENDENT; | ||
|  | 			} else { | ||
|  | 				depLink = -depLink - 1; | ||
|  | 				linkDepB[depLink] = REOP_NOT_DEPENDENT; | ||
|  | 			} | ||
|  | 			// Add this dependent link calculation to the ready list if *both* inputs are clear
 | ||
|  | 			if ((linkDepA[depLink] == REOP_NOT_DEPENDENT) && (linkDepB[depLink] == REOP_NOT_DEPENDENT)) { | ||
|  | 				readyList[readyListTail++] = depLink; | ||
|  | 				linkDepA[depLink] = linkDepB[depLink] = REOP_NODE_COMPLETE;	// Probably not needed now
 | ||
|  | 			} | ||
|  | 			linkDep = linkDep->next; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Delete the temporary buffers
 | ||
|  | 	delete [] nodeWrittenAt; | ||
|  | 	delete [] linkDepA; | ||
|  | 	delete [] linkDepB; | ||
|  | 	delete [] readyList; | ||
|  | 	delete [] linkDepFreeList; | ||
|  | 	delete [] linkDepListStarts; | ||
|  | 	delete [] linkBuffer; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | //
 | ||
|  | void			btSoftBodyHelpers::DrawFrame(		btSoftBody* psb, | ||
|  | 											 btIDebugDraw* idraw) | ||
|  | { | ||
|  | 	if(psb->m_pose.m_bframe) | ||
|  | 	{ | ||
|  | 		static const btScalar	ascl=10; | ||
|  | 		static const btScalar	nscl=(btScalar)0.1; | ||
|  | 		const btVector3			com=psb->m_pose.m_com; | ||
|  | 		const btMatrix3x3		trs=psb->m_pose.m_rot*psb->m_pose.m_scl; | ||
|  | 		const btVector3			Xaxis=(trs*btVector3(1,0,0)).normalized(); | ||
|  | 		const btVector3			Yaxis=(trs*btVector3(0,1,0)).normalized(); | ||
|  | 		const btVector3			Zaxis=(trs*btVector3(0,0,1)).normalized(); | ||
|  | 		idraw->drawLine(com,com+Xaxis*ascl,btVector3(1,0,0)); | ||
|  | 		idraw->drawLine(com,com+Yaxis*ascl,btVector3(0,1,0)); | ||
|  | 		idraw->drawLine(com,com+Zaxis*ascl,btVector3(0,0,1)); | ||
|  | 		for(int i=0;i<psb->m_pose.m_pos.size();++i) | ||
|  | 		{ | ||
|  | 			const btVector3	x=com+trs*psb->m_pose.m_pos[i]; | ||
|  | 			drawVertex(idraw,x,nscl,btVector3(1,0,1)); | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | btSoftBody*		btSoftBodyHelpers::CreateRope(	btSoftBodyWorldInfo& worldInfo, const btVector3& from, | ||
|  | 											  const btVector3& to, | ||
|  | 											  int res, | ||
|  | 											  int fixeds) | ||
|  | { | ||
|  | 	/* Create nodes	*/  | ||
|  | 	const int		r=res+2; | ||
|  | 	btVector3*		x=new btVector3[r]; | ||
|  | 	btScalar*		m=new btScalar[r]; | ||
|  | 	int i; | ||
|  | 
 | ||
|  | 	for(i=0;i<r;++i) | ||
|  | 	{ | ||
|  | 		const btScalar	t=i/(btScalar)(r-1); | ||
|  | 		x[i]=lerp(from,to,t); | ||
|  | 		m[i]=1; | ||
|  | 	} | ||
|  | 	btSoftBody*		psb= new btSoftBody(&worldInfo,r,x,m); | ||
|  | 	if(fixeds&1) psb->setMass(0,0); | ||
|  | 	if(fixeds&2) psb->setMass(r-1,0); | ||
|  | 	delete[] x; | ||
|  | 	delete[] m; | ||
|  | 	/* Create links	*/  | ||
|  | 	for(i=1;i<r;++i) | ||
|  | 	{ | ||
|  | 		psb->appendLink(i-1,i); | ||
|  | 	} | ||
|  | 	/* Finished		*/  | ||
|  | 	return(psb); | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | btSoftBody*		btSoftBodyHelpers::CreatePatch(btSoftBodyWorldInfo& worldInfo,const btVector3& corner00, | ||
|  | 											   const btVector3& corner10, | ||
|  | 											   const btVector3& corner01, | ||
|  | 											   const btVector3& corner11, | ||
|  | 											   int resx, | ||
|  | 											   int resy, | ||
|  | 											   int fixeds, | ||
|  | 											   bool gendiags) | ||
|  | { | ||
|  | #define IDX(_x_,_y_)	((_y_)*rx+(_x_))
 | ||
|  | 	/* Create nodes	*/  | ||
|  | 	if((resx<2)||(resy<2)) return(0); | ||
|  | 	const int	rx=resx; | ||
|  | 	const int	ry=resy; | ||
|  | 	const int	tot=rx*ry; | ||
|  | 	btVector3*	x=new btVector3[tot]; | ||
|  | 	btScalar*	m=new btScalar[tot]; | ||
|  | 	int iy; | ||
|  | 
 | ||
|  | 	for(iy=0;iy<ry;++iy) | ||
|  | 	{ | ||
|  | 		const btScalar	ty=iy/(btScalar)(ry-1); | ||
|  | 		const btVector3	py0=lerp(corner00,corner01,ty); | ||
|  | 		const btVector3	py1=lerp(corner10,corner11,ty); | ||
|  | 		for(int ix=0;ix<rx;++ix) | ||
|  | 		{ | ||
|  | 			const btScalar	tx=ix/(btScalar)(rx-1); | ||
|  | 			x[IDX(ix,iy)]=lerp(py0,py1,tx); | ||
|  | 			m[IDX(ix,iy)]=1; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	btSoftBody*		psb=new btSoftBody(&worldInfo,tot,x,m); | ||
|  | 	if(fixeds&1)	psb->setMass(IDX(0,0),0); | ||
|  | 	if(fixeds&2)	psb->setMass(IDX(rx-1,0),0); | ||
|  | 	if(fixeds&4)	psb->setMass(IDX(0,ry-1),0); | ||
|  | 	if(fixeds&8)	psb->setMass(IDX(rx-1,ry-1),0); | ||
|  | 	delete[] x; | ||
|  | 	delete[] m; | ||
|  | 	/* Create links	and faces */  | ||
|  | 	for(iy=0;iy<ry;++iy) | ||
|  | 	{ | ||
|  | 		for(int ix=0;ix<rx;++ix) | ||
|  | 		{ | ||
|  | 			const int	idx=IDX(ix,iy); | ||
|  | 			const bool	mdx=(ix+1)<rx; | ||
|  | 			const bool	mdy=(iy+1)<ry; | ||
|  | 			if(mdx) psb->appendLink(idx,IDX(ix+1,iy)); | ||
|  | 			if(mdy) psb->appendLink(idx,IDX(ix,iy+1)); | ||
|  | 			if(mdx&&mdy) | ||
|  | 			{ | ||
|  | 				if((ix+iy)&1) | ||
|  | 				{ | ||
|  | 					psb->appendFace(IDX(ix,iy),IDX(ix+1,iy),IDX(ix+1,iy+1)); | ||
|  | 					psb->appendFace(IDX(ix,iy),IDX(ix+1,iy+1),IDX(ix,iy+1)); | ||
|  | 					if(gendiags) | ||
|  | 					{ | ||
|  | 						psb->appendLink(IDX(ix,iy),IDX(ix+1,iy+1)); | ||
|  | 					} | ||
|  | 				} | ||
|  | 				else | ||
|  | 				{ | ||
|  | 					psb->appendFace(IDX(ix,iy+1),IDX(ix,iy),IDX(ix+1,iy)); | ||
|  | 					psb->appendFace(IDX(ix,iy+1),IDX(ix+1,iy),IDX(ix+1,iy+1)); | ||
|  | 					if(gendiags) | ||
|  | 					{ | ||
|  | 						psb->appendLink(IDX(ix+1,iy),IDX(ix,iy+1)); | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	/* Finished		*/  | ||
|  | #undef IDX
 | ||
|  | 	return(psb); | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | btSoftBody*		btSoftBodyHelpers::CreatePatchUV(btSoftBodyWorldInfo& worldInfo, | ||
|  | 												 const btVector3& corner00, | ||
|  | 												 const btVector3& corner10, | ||
|  | 												 const btVector3& corner01, | ||
|  | 												 const btVector3& corner11, | ||
|  | 												 int resx, | ||
|  | 												 int resy, | ||
|  | 												 int fixeds, | ||
|  | 												 bool gendiags, | ||
|  | 												 float* tex_coords) | ||
|  | { | ||
|  | 
 | ||
|  | 	/*
 | ||
|  | 	* | ||
|  | 	*  corners: | ||
|  | 	* | ||
|  | 	*  [0][0]     corner00 ------- corner01   [resx][0] | ||
|  | 	*                |                | | ||
|  | 	*                |                | | ||
|  | 	*  [0][resy]  corner10 -------- corner11  [resx][resy] | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	*   "fixedgs" map: | ||
|  | 	* | ||
|  | 	*  corner00     -->   +1 | ||
|  | 	*  corner01     -->   +2 | ||
|  | 	*  corner10     -->   +4 | ||
|  | 	*  corner11     -->   +8 | ||
|  | 	*  upper middle -->  +16 | ||
|  | 	*  left middle  -->  +32 | ||
|  | 	*  right middle -->  +64 | ||
|  | 	*  lower middle --> +128 | ||
|  | 	*  center       --> +256 | ||
|  | 	* | ||
|  | 	* | ||
|  | 	*   tex_coords size   (resx-1)*(resy-1)*12 | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	*     SINGLE QUAD INTERNALS | ||
|  | 	* | ||
|  | 	*  1) btSoftBody's nodes and links, | ||
|  | 	*     diagonal link is optional ("gendiags") | ||
|  | 	* | ||
|  | 	* | ||
|  | 	*    node00 ------ node01 | ||
|  | 	*      | .               | ||
|  | 	*      |   .             | ||
|  | 	*      |     .           | ||
|  | 	*      |       .         | ||
|  | 	*      |         .       | ||
|  | 	*    node10        node11 | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	*   2) Faces: | ||
|  | 	*      two triangles, | ||
|  | 	*      UV Coordinates (hier example for single quad) | ||
|  | 	*       | ||
|  | 	*     (0,1)          (0,1)  (1,1) | ||
|  | 	*     1 |\            3 \-----| 2 | ||
|  | 	*       | \              \    | | ||
|  | 	*       |  \              \   | | ||
|  | 	*       |   \              \  | | ||
|  | 	*       |    \              \ | | ||
|  | 	*     2 |-----\ 3            \| 1 | ||
|  | 	*     (0,0)    (1,0)       (1,0) | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	* | ||
|  | 	*/ | ||
|  | 
 | ||
|  | #define IDX(_x_,_y_)	((_y_)*rx+(_x_))
 | ||
|  | 	/* Create nodes		*/  | ||
|  | 	if((resx<2)||(resy<2)) return(0); | ||
|  | 	const int	rx=resx; | ||
|  | 	const int	ry=resy; | ||
|  | 	const int	tot=rx*ry; | ||
|  | 	btVector3*	x=new btVector3[tot]; | ||
|  | 	btScalar*	m=new btScalar[tot]; | ||
|  | 
 | ||
|  | 	int iy; | ||
|  | 
 | ||
|  | 	for(iy=0;iy<ry;++iy) | ||
|  | 	{ | ||
|  | 		const btScalar	ty=iy/(btScalar)(ry-1); | ||
|  | 		const btVector3	py0=lerp(corner00,corner01,ty); | ||
|  | 		const btVector3	py1=lerp(corner10,corner11,ty); | ||
|  | 		for(int ix=0;ix<rx;++ix) | ||
|  | 		{ | ||
|  | 			const btScalar	tx=ix/(btScalar)(rx-1); | ||
|  | 			x[IDX(ix,iy)]=lerp(py0,py1,tx); | ||
|  | 			m[IDX(ix,iy)]=1; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	btSoftBody*	psb=new btSoftBody(&worldInfo,tot,x,m); | ||
|  | 	if(fixeds&1)		psb->setMass(IDX(0,0),0); | ||
|  | 	if(fixeds&2)		psb->setMass(IDX(rx-1,0),0); | ||
|  | 	if(fixeds&4)		psb->setMass(IDX(0,ry-1),0); | ||
|  | 	if(fixeds&8)		psb->setMass(IDX(rx-1,ry-1),0); | ||
|  | 	if(fixeds&16)		psb->setMass(IDX((rx-1)/2,0),0); | ||
|  | 	if(fixeds&32)		psb->setMass(IDX(0,(ry-1)/2),0); | ||
|  | 	if(fixeds&64)		psb->setMass(IDX(rx-1,(ry-1)/2),0); | ||
|  | 	if(fixeds&128)		psb->setMass(IDX((rx-1)/2,ry-1),0); | ||
|  | 	if(fixeds&256)		psb->setMass(IDX((rx-1)/2,(ry-1)/2),0); | ||
|  | 	delete[] x; | ||
|  | 	delete[] m; | ||
|  | 
 | ||
|  | 
 | ||
|  | 	int z = 0; | ||
|  | 	/* Create links	and faces	*/  | ||
|  | 	for(iy=0;iy<ry;++iy) | ||
|  | 	{ | ||
|  | 		for(int ix=0;ix<rx;++ix) | ||
|  | 		{ | ||
|  | 			const bool	mdx=(ix+1)<rx; | ||
|  | 			const bool	mdy=(iy+1)<ry; | ||
|  | 
 | ||
|  | 			int node00=IDX(ix,iy); | ||
|  | 			int node01=IDX(ix+1,iy); | ||
|  | 			int node10=IDX(ix,iy+1); | ||
|  | 			int node11=IDX(ix+1,iy+1); | ||
|  | 
 | ||
|  | 			if(mdx) psb->appendLink(node00,node01); | ||
|  | 			if(mdy) psb->appendLink(node00,node10); | ||
|  | 			if(mdx&&mdy) | ||
|  | 			{ | ||
|  | 				psb->appendFace(node00,node10,node11); | ||
|  | 				if (tex_coords) { | ||
|  | 					tex_coords[z+0]=CalculateUV(resx,resy,ix,iy,0); | ||
|  | 					tex_coords[z+1]=CalculateUV(resx,resy,ix,iy,1); | ||
|  | 					tex_coords[z+2]=CalculateUV(resx,resy,ix,iy,0); | ||
|  | 					tex_coords[z+3]=CalculateUV(resx,resy,ix,iy,2); | ||
|  | 					tex_coords[z+4]=CalculateUV(resx,resy,ix,iy,3); | ||
|  | 					tex_coords[z+5]=CalculateUV(resx,resy,ix,iy,2); | ||
|  | 				} | ||
|  | 				psb->appendFace(node11,node01,node00); | ||
|  | 				if (tex_coords) { | ||
|  | 					tex_coords[z+6 ]=CalculateUV(resx,resy,ix,iy,3); | ||
|  | 					tex_coords[z+7 ]=CalculateUV(resx,resy,ix,iy,2); | ||
|  | 					tex_coords[z+8 ]=CalculateUV(resx,resy,ix,iy,3); | ||
|  | 					tex_coords[z+9 ]=CalculateUV(resx,resy,ix,iy,1); | ||
|  | 					tex_coords[z+10]=CalculateUV(resx,resy,ix,iy,0); | ||
|  | 					tex_coords[z+11]=CalculateUV(resx,resy,ix,iy,1); | ||
|  | 				} | ||
|  | 				if (gendiags) psb->appendLink(node00,node11); | ||
|  | 				z += 12; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	/* Finished	*/  | ||
|  | #undef IDX
 | ||
|  | 	return(psb); | ||
|  | } | ||
|  | 
 | ||
|  | float   btSoftBodyHelpers::CalculateUV(int resx,int resy,int ix,int iy,int id) | ||
|  | { | ||
|  | 
 | ||
|  | 	/*
 | ||
|  | 	* | ||
|  | 	* | ||
|  | 	*    node00 --- node01 | ||
|  | 	*      |          | | ||
|  | 	*    node10 --- node11 | ||
|  | 	* | ||
|  | 	* | ||
|  | 	*   ID map: | ||
|  | 	* | ||
|  | 	*   node00 s --> 0 | ||
|  | 	*   node00 t --> 1 | ||
|  | 	* | ||
|  | 	*   node01 s --> 3 | ||
|  | 	*   node01 t --> 1 | ||
|  | 	* | ||
|  | 	*   node10 s --> 0 | ||
|  | 	*   node10 t --> 2 | ||
|  | 	* | ||
|  | 	*   node11 s --> 3 | ||
|  | 	*   node11 t --> 2 | ||
|  | 	* | ||
|  | 	* | ||
|  | 	*/ | ||
|  | 
 | ||
|  | 	float tc=0.0f; | ||
|  | 	if (id == 0) { | ||
|  | 		tc = (1.0f/((resx-1))*ix); | ||
|  | 	} | ||
|  | 	else if (id==1) { | ||
|  | 		tc = (1.0f/((resy-1))*(resy-1-iy)); | ||
|  | 	} | ||
|  | 	else if (id==2) { | ||
|  | 		tc = (1.0f/((resy-1))*(resy-1-iy-1)); | ||
|  | 	} | ||
|  | 	else if (id==3) { | ||
|  | 		tc = (1.0f/((resx-1))*(ix+1)); | ||
|  | 	} | ||
|  | 	return tc; | ||
|  | } | ||
|  | //
 | ||
|  | btSoftBody*		btSoftBodyHelpers::CreateEllipsoid(btSoftBodyWorldInfo& worldInfo,const btVector3& center, | ||
|  | 												   const btVector3& radius, | ||
|  | 												   int res) | ||
|  | { | ||
|  | 	struct	Hammersley | ||
|  | 	{ | ||
|  | 		static void	Generate(btVector3* x,int n) | ||
|  | 		{ | ||
|  | 			for(int i=0;i<n;i++) | ||
|  | 			{ | ||
|  | 				btScalar	p=0.5,t=0; | ||
|  | 				for(int j=i;j;p*=0.5,j>>=1) if(j&1) t+=p; | ||
|  | 				btScalar	w=2*t-1; | ||
|  | 				btScalar	a=(SIMD_PI+2*i*SIMD_PI)/n; | ||
|  | 				btScalar	s=btSqrt(1-w*w); | ||
|  | 				*x++=btVector3(s*btCos(a),s*btSin(a),w); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	}; | ||
|  | 	btAlignedObjectArray<btVector3>	vtx; | ||
|  | 	vtx.resize(3+res); | ||
|  | 	Hammersley::Generate(&vtx[0],vtx.size()); | ||
|  | 	for(int i=0;i<vtx.size();++i) | ||
|  | 	{ | ||
|  | 		vtx[i]=vtx[i]*radius+center; | ||
|  | 	} | ||
|  | 	return(CreateFromConvexHull(worldInfo,&vtx[0],vtx.size())); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | //
 | ||
|  | btSoftBody*		btSoftBodyHelpers::CreateFromTriMesh(btSoftBodyWorldInfo& worldInfo,const btScalar*	vertices, | ||
|  | 													 const int* triangles, | ||
|  | 													 int ntriangles, bool randomizeConstraints) | ||
|  | { | ||
|  | 	int		maxidx=0; | ||
|  | 	int i,j,ni; | ||
|  | 
 | ||
|  | 	for(i=0,ni=ntriangles*3;i<ni;++i) | ||
|  | 	{ | ||
|  | 		maxidx=btMax(triangles[i],maxidx); | ||
|  | 	} | ||
|  | 	++maxidx; | ||
|  | 	btAlignedObjectArray<bool>		chks; | ||
|  | 	btAlignedObjectArray<btVector3>	vtx; | ||
|  | 	chks.resize(maxidx*maxidx,false); | ||
|  | 	vtx.resize(maxidx); | ||
|  | 	for(i=0,j=0,ni=maxidx*3;i<ni;++j,i+=3) | ||
|  | 	{ | ||
|  | 		vtx[j]=btVector3(vertices[i],vertices[i+1],vertices[i+2]); | ||
|  | 	} | ||
|  | 	btSoftBody*		psb=new btSoftBody(&worldInfo,vtx.size(),&vtx[0],0); | ||
|  | 	for( i=0,ni=ntriangles*3;i<ni;i+=3) | ||
|  | 	{ | ||
|  | 		const int idx[]={triangles[i],triangles[i+1],triangles[i+2]}; | ||
|  | #define IDX(_x_,_y_) ((_y_)*maxidx+(_x_))
 | ||
|  | 		for(int j=2,k=0;k<3;j=k++) | ||
|  | 		{ | ||
|  | 			if(!chks[IDX(idx[j],idx[k])]) | ||
|  | 			{ | ||
|  | 				chks[IDX(idx[j],idx[k])]=true; | ||
|  | 				chks[IDX(idx[k],idx[j])]=true; | ||
|  | 				psb->appendLink(idx[j],idx[k]); | ||
|  | 			} | ||
|  | 		} | ||
|  | #undef IDX
 | ||
|  | 		psb->appendFace(idx[0],idx[1],idx[2]); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (randomizeConstraints) | ||
|  | 	{ | ||
|  | 		psb->randomizeConstraints(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return(psb); | ||
|  | } | ||
|  | 
 | ||
|  | //
 | ||
|  | btSoftBody*		btSoftBodyHelpers::CreateFromConvexHull(btSoftBodyWorldInfo& worldInfo,	const btVector3* vertices, | ||
|  | 														int nvertices, bool randomizeConstraints) | ||
|  | { | ||
|  | 	HullDesc		hdsc(QF_TRIANGLES,nvertices,vertices); | ||
|  | 	HullResult		hres; | ||
|  | 	HullLibrary		hlib;/*??*/  | ||
|  | 	hdsc.mMaxVertices=nvertices; | ||
|  | 	hlib.CreateConvexHull(hdsc,hres); | ||
|  | 	btSoftBody*		psb=new btSoftBody(&worldInfo,(int)hres.mNumOutputVertices, | ||
|  | 		&hres.m_OutputVertices[0],0); | ||
|  | 	for(int i=0;i<(int)hres.mNumFaces;++i) | ||
|  | 	{ | ||
|  | 		const int idx[]={	static_cast<int>(hres.m_Indices[i*3+0]), | ||
|  | 							static_cast<int>(hres.m_Indices[i*3+1]), | ||
|  | 							static_cast<int>(hres.m_Indices[i*3+2])}; | ||
|  | 		if(idx[0]<idx[1]) psb->appendLink(	idx[0],idx[1]); | ||
|  | 		if(idx[1]<idx[2]) psb->appendLink(	idx[1],idx[2]); | ||
|  | 		if(idx[2]<idx[0]) psb->appendLink(	idx[2],idx[0]); | ||
|  | 		psb->appendFace(idx[0],idx[1],idx[2]); | ||
|  | 	} | ||
|  | 	hlib.ReleaseResult(hres); | ||
|  | 	if (randomizeConstraints) | ||
|  | 	{ | ||
|  | 		psb->randomizeConstraints(); | ||
|  | 	} | ||
|  | 	return(psb); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static int nextLine(const char* buffer) | ||
|  | { | ||
|  | 	int numBytesRead=0; | ||
|  | 
 | ||
|  | 	while (*buffer != '\n') | ||
|  | 	{ | ||
|  | 		buffer++; | ||
|  | 		numBytesRead++; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	 | ||
|  | 	if (buffer[0]==0x0a) | ||
|  | 	{ | ||
|  | 		buffer++; | ||
|  | 		numBytesRead++; | ||
|  | 	} | ||
|  | 	return numBytesRead; | ||
|  | } | ||
|  | 
 | ||
|  | /* Create from TetGen .ele, .face, .node data							*/  | ||
|  | btSoftBody*	btSoftBodyHelpers::CreateFromTetGenData(btSoftBodyWorldInfo& worldInfo, | ||
|  | 													const char* ele, | ||
|  | 													const char* face, | ||
|  | 													const char* node, | ||
|  | 													bool bfacelinks, | ||
|  | 													bool btetralinks, | ||
|  | 													bool bfacesfromtetras) | ||
|  | { | ||
|  | btAlignedObjectArray<btVector3>	pos; | ||
|  | int								nnode=0; | ||
|  | int								ndims=0; | ||
|  | int								nattrb=0; | ||
|  | int								hasbounds=0; | ||
|  | int result = sscanf(node,"%d %d %d %d",&nnode,&ndims,&nattrb,&hasbounds); | ||
|  | result = sscanf(node,"%d %d %d %d",&nnode,&ndims,&nattrb,&hasbounds); | ||
|  | node += nextLine(node); | ||
|  | 
 | ||
|  | pos.resize(nnode); | ||
|  | for(int i=0;i<pos.size();++i) | ||
|  | 	{ | ||
|  | 	int			index=0; | ||
|  | 	//int			bound=0;
 | ||
|  | 	float	x,y,z; | ||
|  | 	sscanf(node,"%d %f %f %f",&index,&x,&y,&z); | ||
|  | 
 | ||
|  | //	sn>>index;
 | ||
|  | //	sn>>x;sn>>y;sn>>z;
 | ||
|  | 	node += nextLine(node); | ||
|  | 
 | ||
|  | 	//for(int j=0;j<nattrb;++j) 
 | ||
|  | 	//	sn>>a;
 | ||
|  | 
 | ||
|  | 	//if(hasbounds) 
 | ||
|  | 	//	sn>>bound;
 | ||
|  | 
 | ||
|  | 	pos[index].setX(btScalar(x)); | ||
|  | 	pos[index].setY(btScalar(y)); | ||
|  | 	pos[index].setZ(btScalar(z)); | ||
|  | 	} | ||
|  | btSoftBody*						psb=new btSoftBody(&worldInfo,nnode,&pos[0],0); | ||
|  | #if 0
 | ||
|  | if(face&&face[0]) | ||
|  | 	{ | ||
|  | 	int								nface=0; | ||
|  | 	sf>>nface;sf>>hasbounds; | ||
|  | 	for(int i=0;i<nface;++i) | ||
|  | 		{ | ||
|  | 		int			index=0; | ||
|  | 		int			bound=0; | ||
|  | 		int			ni[3]; | ||
|  | 		sf>>index; | ||
|  | 		sf>>ni[0];sf>>ni[1];sf>>ni[2]; | ||
|  | 		sf>>bound; | ||
|  | 		psb->appendFace(ni[0],ni[1],ni[2]);	 | ||
|  | 		if(btetralinks) | ||
|  | 			{ | ||
|  | 			psb->appendLink(ni[0],ni[1],0,true); | ||
|  | 			psb->appendLink(ni[1],ni[2],0,true); | ||
|  | 			psb->appendLink(ni[2],ni[0],0,true); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | #endif
 | ||
|  | 
 | ||
|  | if(ele&&ele[0]) | ||
|  | 	{ | ||
|  | 	int								ntetra=0; | ||
|  | 	int								ncorner=0; | ||
|  | 	int								neattrb=0; | ||
|  | 	sscanf(ele,"%d %d %d",&ntetra,&ncorner,&neattrb); | ||
|  | 	ele += nextLine(ele); | ||
|  | 	 | ||
|  | 	//se>>ntetra;se>>ncorner;se>>neattrb;
 | ||
|  | 	for(int i=0;i<ntetra;++i) | ||
|  | 		{ | ||
|  | 		int			index=0; | ||
|  | 		int			ni[4]; | ||
|  | 
 | ||
|  | 		//se>>index;
 | ||
|  | 		//se>>ni[0];se>>ni[1];se>>ni[2];se>>ni[3];
 | ||
|  | 		sscanf(ele,"%d %d %d %d %d",&index,&ni[0],&ni[1],&ni[2],&ni[3]); | ||
|  | 		ele+=nextLine(ele); | ||
|  | 		//for(int j=0;j<neattrb;++j) 
 | ||
|  | 		//	se>>a;
 | ||
|  | 		psb->appendTetra(ni[0],ni[1],ni[2],ni[3]); | ||
|  | 		if(btetralinks) | ||
|  | 			{ | ||
|  | 			psb->appendLink(ni[0],ni[1],0,true); | ||
|  | 			psb->appendLink(ni[1],ni[2],0,true); | ||
|  | 			psb->appendLink(ni[2],ni[0],0,true); | ||
|  | 			psb->appendLink(ni[0],ni[3],0,true); | ||
|  | 			psb->appendLink(ni[1],ni[3],0,true); | ||
|  | 			psb->appendLink(ni[2],ni[3],0,true); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | printf("Nodes:  %u\r\n",psb->m_nodes.size()); | ||
|  | printf("Links:  %u\r\n",psb->m_links.size()); | ||
|  | printf("Faces:  %u\r\n",psb->m_faces.size()); | ||
|  | printf("Tetras: %u\r\n",psb->m_tetras.size()); | ||
|  | return(psb); | ||
|  | } | ||
|  | 
 |