3710 lines
		
	
	
		
			92 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			3710 lines
		
	
	
		
			92 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. | |||
|  | */ | |||
|  | ///btSoftBody implementation by Nathanael Presson
 | |||
|  | 
 | |||
|  | #include "btSoftBodyInternals.h"
 | |||
|  | #include "BulletSoftBody/btSoftBodySolvers.h"
 | |||
|  | #include "btSoftBodyData.h"
 | |||
|  | #include "LinearMath/btSerializer.h"
 | |||
|  | #include "BulletDynamics/Featherstone/btMultiBodyLinkCollider.h"
 | |||
|  | #include "BulletDynamics/Featherstone/btMultiBodyConstraint.h"
 | |||
|  | 
 | |||
|  | 
 | |||
|  | //
 | |||
|  | btSoftBody::btSoftBody(btSoftBodyWorldInfo*	worldInfo,int node_count,  const btVector3* x,  const btScalar* m) | |||
|  | :m_softBodySolver(0),m_worldInfo(worldInfo) | |||
|  | {	 | |||
|  | 	/* Init		*/  | |||
|  | 	initDefaults(); | |||
|  | 
 | |||
|  | 	/* Default material	*/  | |||
|  | 	Material*	pm=appendMaterial(); | |||
|  | 	pm->m_kLST	=	1; | |||
|  | 	pm->m_kAST	=	1; | |||
|  | 	pm->m_kVST	=	1; | |||
|  | 	pm->m_flags	=	fMaterial::Default; | |||
|  | 
 | |||
|  | 	/* Nodes			*/  | |||
|  | 	const btScalar		margin=getCollisionShape()->getMargin(); | |||
|  | 	m_nodes.resize(node_count); | |||
|  | 	for(int i=0,ni=node_count;i<ni;++i) | |||
|  | 	{	 | |||
|  | 		Node&	n=m_nodes[i]; | |||
|  | 		ZeroInitialize(n); | |||
|  | 		n.m_x		=	x?*x++:btVector3(0,0,0); | |||
|  | 		n.m_q		=	n.m_x; | |||
|  | 		n.m_im		=	m?*m++:1; | |||
|  | 		n.m_im		=	n.m_im>0?1/n.m_im:0; | |||
|  | 		n.m_leaf	=	m_ndbvt.insert(btDbvtVolume::FromCR(n.m_x,margin),&n); | |||
|  | 		n.m_material=	pm; | |||
|  | 	} | |||
|  | 	updateBounds();	 | |||
|  | 
 | |||
|  | } | |||
|  | 
 | |||
|  | btSoftBody::btSoftBody(btSoftBodyWorldInfo*	worldInfo) | |||
|  | :m_worldInfo(worldInfo) | |||
|  | { | |||
|  | 	initDefaults(); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | void	btSoftBody::initDefaults() | |||
|  | { | |||
|  | 	m_internalType		=	CO_SOFT_BODY; | |||
|  | 	m_cfg.aeromodel		=	eAeroModel::V_Point; | |||
|  | 	m_cfg.kVCF			=	1; | |||
|  | 	m_cfg.kDG			=	0; | |||
|  | 	m_cfg.kLF			=	0; | |||
|  | 	m_cfg.kDP			=	0; | |||
|  | 	m_cfg.kPR			=	0; | |||
|  | 	m_cfg.kVC			=	0; | |||
|  | 	m_cfg.kDF			=	(btScalar)0.2; | |||
|  | 	m_cfg.kMT			=	0; | |||
|  | 	m_cfg.kCHR			=	(btScalar)1.0; | |||
|  | 	m_cfg.kKHR			=	(btScalar)0.1; | |||
|  | 	m_cfg.kSHR			=	(btScalar)1.0; | |||
|  | 	m_cfg.kAHR			=	(btScalar)0.7; | |||
|  | 	m_cfg.kSRHR_CL		=	(btScalar)0.1; | |||
|  | 	m_cfg.kSKHR_CL		=	(btScalar)1; | |||
|  | 	m_cfg.kSSHR_CL		=	(btScalar)0.5; | |||
|  | 	m_cfg.kSR_SPLT_CL	=	(btScalar)0.5; | |||
|  | 	m_cfg.kSK_SPLT_CL	=	(btScalar)0.5; | |||
|  | 	m_cfg.kSS_SPLT_CL	=	(btScalar)0.5; | |||
|  | 	m_cfg.maxvolume		=	(btScalar)1; | |||
|  | 	m_cfg.timescale		=	1; | |||
|  | 	m_cfg.viterations	=	0; | |||
|  | 	m_cfg.piterations	=	1;	 | |||
|  | 	m_cfg.diterations	=	0; | |||
|  | 	m_cfg.citerations	=	4; | |||
|  | 	m_cfg.collisions	=	fCollision::Default; | |||
|  | 	m_pose.m_bvolume	=	false; | |||
|  | 	m_pose.m_bframe		=	false; | |||
|  | 	m_pose.m_volume		=	0; | |||
|  | 	m_pose.m_com		=	btVector3(0,0,0); | |||
|  | 	m_pose.m_rot.setIdentity(); | |||
|  | 	m_pose.m_scl.setIdentity(); | |||
|  | 	m_tag				=	0; | |||
|  | 	m_timeacc			=	0; | |||
|  | 	m_bUpdateRtCst		=	true; | |||
|  | 	m_bounds[0]			=	btVector3(0,0,0); | |||
|  | 	m_bounds[1]			=	btVector3(0,0,0); | |||
|  | 	m_worldTransform.setIdentity(); | |||
|  | 	setSolver(eSolverPresets::Positions); | |||
|  | 	 | |||
|  | 	/* Collision shape	*/  | |||
|  | 	///for now, create a collision shape internally
 | |||
|  | 	m_collisionShape = new btSoftBodyCollisionShape(this); | |||
|  | 	m_collisionShape->setMargin(0.25f); | |||
|  | 	 | |||
|  | 	m_initialWorldTransform.setIdentity(); | |||
|  | 
 | |||
|  | 	m_windVelocity = btVector3(0,0,0); | |||
|  | 	m_restLengthScale = btScalar(1.0); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btSoftBody::~btSoftBody() | |||
|  | { | |||
|  | 	//for now, delete the internal shape
 | |||
|  | 	delete m_collisionShape;	 | |||
|  | 	int i; | |||
|  | 
 | |||
|  | 	releaseClusters(); | |||
|  | 	for(i=0;i<m_materials.size();++i)  | |||
|  | 		btAlignedFree(m_materials[i]); | |||
|  | 	for(i=0;i<m_joints.size();++i)  | |||
|  | 		btAlignedFree(m_joints[i]); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | bool			btSoftBody::checkLink(int node0,int node1) const | |||
|  | { | |||
|  | 	return(checkLink(&m_nodes[node0],&m_nodes[node1])); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | bool			btSoftBody::checkLink(const Node* node0,const Node* node1) const | |||
|  | { | |||
|  | 	const Node*	n[]={node0,node1}; | |||
|  | 	for(int i=0,ni=m_links.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		const Link&	l=m_links[i]; | |||
|  | 		if(	(l.m_n[0]==n[0]&&l.m_n[1]==n[1])|| | |||
|  | 			(l.m_n[0]==n[1]&&l.m_n[1]==n[0])) | |||
|  | 		{ | |||
|  | 			return(true); | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return(false); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | bool			btSoftBody::checkFace(int node0,int node1,int node2) const | |||
|  | { | |||
|  | 	const Node*	n[]={	&m_nodes[node0], | |||
|  | 		&m_nodes[node1], | |||
|  | 		&m_nodes[node2]}; | |||
|  | 	for(int i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		const Face&	f=m_faces[i]; | |||
|  | 		int			c=0; | |||
|  | 		for(int j=0;j<3;++j) | |||
|  | 		{ | |||
|  | 			if(	(f.m_n[j]==n[0])|| | |||
|  | 				(f.m_n[j]==n[1])|| | |||
|  | 				(f.m_n[j]==n[2])) c|=1<<j; else break; | |||
|  | 		} | |||
|  | 		if(c==7) return(true); | |||
|  | 	} | |||
|  | 	return(false); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btSoftBody::Material*		btSoftBody::appendMaterial() | |||
|  | { | |||
|  | 	Material*	pm=new(btAlignedAlloc(sizeof(Material),16)) Material(); | |||
|  | 	if(m_materials.size()>0) | |||
|  | 		*pm=*m_materials[0]; | |||
|  | 	else | |||
|  | 		ZeroInitialize(*pm); | |||
|  | 	m_materials.push_back(pm); | |||
|  | 	return(pm); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendNote(	const char* text, | |||
|  | 									   const btVector3& o, | |||
|  | 									   const btVector4& c, | |||
|  | 									   Node* n0, | |||
|  | 									   Node* n1, | |||
|  | 									   Node* n2, | |||
|  | 									   Node* n3) | |||
|  | { | |||
|  | 	Note	n; | |||
|  | 	ZeroInitialize(n); | |||
|  | 	n.m_rank		=	0; | |||
|  | 	n.m_text		=	text; | |||
|  | 	n.m_offset		=	o; | |||
|  | 	n.m_coords[0]	=	c.x(); | |||
|  | 	n.m_coords[1]	=	c.y(); | |||
|  | 	n.m_coords[2]	=	c.z(); | |||
|  | 	n.m_coords[3]	=	c.w(); | |||
|  | 	n.m_nodes[0]	=	n0;n.m_rank+=n0?1:0; | |||
|  | 	n.m_nodes[1]	=	n1;n.m_rank+=n1?1:0; | |||
|  | 	n.m_nodes[2]	=	n2;n.m_rank+=n2?1:0; | |||
|  | 	n.m_nodes[3]	=	n3;n.m_rank+=n3?1:0; | |||
|  | 	m_notes.push_back(n); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendNote(	const char* text, | |||
|  | 									   const btVector3& o, | |||
|  | 									   Node* feature) | |||
|  | { | |||
|  | 	appendNote(text,o,btVector4(1,0,0,0),feature); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendNote(	const char* text, | |||
|  | 									   const btVector3& o, | |||
|  | 									   Link* feature) | |||
|  | { | |||
|  | 	static const btScalar	w=1/(btScalar)2; | |||
|  | 	appendNote(text,o,btVector4(w,w,0,0),	feature->m_n[0], | |||
|  | 		feature->m_n[1]); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendNote(	const char* text, | |||
|  | 									   const btVector3& o, | |||
|  | 									   Face* feature) | |||
|  | { | |||
|  | 	static const btScalar	w=1/(btScalar)3; | |||
|  | 	appendNote(text,o,btVector4(w,w,w,0),	feature->m_n[0], | |||
|  | 		feature->m_n[1], | |||
|  | 		feature->m_n[2]); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendNode(	const btVector3& x,btScalar m) | |||
|  | { | |||
|  | 	if(m_nodes.capacity()==m_nodes.size()) | |||
|  | 	{ | |||
|  | 		pointersToIndices(); | |||
|  | 		m_nodes.reserve(m_nodes.size()*2+1); | |||
|  | 		indicesToPointers(); | |||
|  | 	} | |||
|  | 	const btScalar	margin=getCollisionShape()->getMargin(); | |||
|  | 	m_nodes.push_back(Node()); | |||
|  | 	Node&			n=m_nodes[m_nodes.size()-1]; | |||
|  | 	ZeroInitialize(n); | |||
|  | 	n.m_x			=	x; | |||
|  | 	n.m_q			=	n.m_x; | |||
|  | 	n.m_im			=	m>0?1/m:0; | |||
|  | 	n.m_material	=	m_materials[0]; | |||
|  | 	n.m_leaf		=	m_ndbvt.insert(btDbvtVolume::FromCR(n.m_x,margin),&n); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendLink(int model,Material* mat) | |||
|  | { | |||
|  | 	Link	l; | |||
|  | 	if(model>=0) | |||
|  | 		l=m_links[model]; | |||
|  | 	else | |||
|  | 	{ ZeroInitialize(l);l.m_material=mat?mat:m_materials[0]; } | |||
|  | 	m_links.push_back(l); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendLink(	int node0, | |||
|  | 									   int node1, | |||
|  | 									   Material* mat, | |||
|  | 									   bool bcheckexist) | |||
|  | { | |||
|  | 	appendLink(&m_nodes[node0],&m_nodes[node1],mat,bcheckexist); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendLink(	Node* node0, | |||
|  | 									   Node* node1, | |||
|  | 									   Material* mat, | |||
|  | 									   bool bcheckexist) | |||
|  | { | |||
|  | 	if((!bcheckexist)||(!checkLink(node0,node1))) | |||
|  | 	{ | |||
|  | 		appendLink(-1,mat); | |||
|  | 		Link&	l=m_links[m_links.size()-1]; | |||
|  | 		l.m_n[0]		=	node0; | |||
|  | 		l.m_n[1]		=	node1; | |||
|  | 		l.m_rl			=	(l.m_n[0]->m_x-l.m_n[1]->m_x).length(); | |||
|  | 		m_bUpdateRtCst=true; | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendFace(int model,Material* mat) | |||
|  | { | |||
|  | 	Face	f; | |||
|  | 	if(model>=0) | |||
|  | 	{ f=m_faces[model]; } | |||
|  | 	else | |||
|  | 	{ ZeroInitialize(f);f.m_material=mat?mat:m_materials[0]; } | |||
|  | 	m_faces.push_back(f); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendFace(int node0,int node1,int node2,Material* mat) | |||
|  | { | |||
|  | 	if (node0==node1) | |||
|  | 		return; | |||
|  | 	if (node1==node2) | |||
|  | 		return; | |||
|  | 	if (node2==node0) | |||
|  | 		return; | |||
|  | 
 | |||
|  | 	appendFace(-1,mat); | |||
|  | 	Face&	f=m_faces[m_faces.size()-1]; | |||
|  | 	btAssert(node0!=node1); | |||
|  | 	btAssert(node1!=node2); | |||
|  | 	btAssert(node2!=node0); | |||
|  | 	f.m_n[0]	=	&m_nodes[node0]; | |||
|  | 	f.m_n[1]	=	&m_nodes[node1]; | |||
|  | 	f.m_n[2]	=	&m_nodes[node2]; | |||
|  | 	f.m_ra		=	AreaOf(	f.m_n[0]->m_x, | |||
|  | 		f.m_n[1]->m_x, | |||
|  | 		f.m_n[2]->m_x);	 | |||
|  | 	m_bUpdateRtCst=true; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendTetra(int model,Material* mat) | |||
|  | { | |||
|  | Tetra	t; | |||
|  | if(model>=0) | |||
|  | 	t=m_tetras[model]; | |||
|  | 	else | |||
|  | 	{ ZeroInitialize(t);t.m_material=mat?mat:m_materials[0]; } | |||
|  | m_tetras.push_back(t); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendTetra(int node0, | |||
|  | 										int node1, | |||
|  | 										int node2, | |||
|  | 										int node3, | |||
|  | 										Material* mat) | |||
|  | { | |||
|  | 	appendTetra(-1,mat); | |||
|  | 	Tetra&	t=m_tetras[m_tetras.size()-1]; | |||
|  | 	t.m_n[0]	=	&m_nodes[node0]; | |||
|  | 	t.m_n[1]	=	&m_nodes[node1]; | |||
|  | 	t.m_n[2]	=	&m_nodes[node2]; | |||
|  | 	t.m_n[3]	=	&m_nodes[node3]; | |||
|  | 	t.m_rv		=	VolumeOf(t.m_n[0]->m_x,t.m_n[1]->m_x,t.m_n[2]->m_x,t.m_n[3]->m_x); | |||
|  | 	m_bUpdateRtCst=true; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | 
 | |||
|  | void			btSoftBody::appendAnchor(int node,btRigidBody* body, bool disableCollisionBetweenLinkedBodies,btScalar influence) | |||
|  | { | |||
|  | 	btVector3 local = body->getWorldTransform().inverse()*m_nodes[node].m_x; | |||
|  | 	appendAnchor(node,body,local,disableCollisionBetweenLinkedBodies,influence); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendAnchor(int node,btRigidBody* body, const btVector3& localPivot,bool disableCollisionBetweenLinkedBodies,btScalar influence) | |||
|  | { | |||
|  | 	if (disableCollisionBetweenLinkedBodies) | |||
|  | 	{ | |||
|  | 		if (m_collisionDisabledObjects.findLinearSearch(body)==m_collisionDisabledObjects.size()) | |||
|  | 		{ | |||
|  | 			m_collisionDisabledObjects.push_back(body); | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	Anchor	a; | |||
|  | 	a.m_node			=	&m_nodes[node]; | |||
|  | 	a.m_body			=	body; | |||
|  | 	a.m_local			=	localPivot; | |||
|  | 	a.m_node->m_battach	=	1; | |||
|  | 	a.m_influence = influence; | |||
|  | 	m_anchors.push_back(a); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendLinearJoint(const LJoint::Specs& specs,Cluster* body0,Body body1) | |||
|  | { | |||
|  | 	LJoint*		pj	=	new(btAlignedAlloc(sizeof(LJoint),16)) LJoint(); | |||
|  | 	pj->m_bodies[0]	=	body0; | |||
|  | 	pj->m_bodies[1]	=	body1; | |||
|  | 	pj->m_refs[0]	=	pj->m_bodies[0].xform().inverse()*specs.position; | |||
|  | 	pj->m_refs[1]	=	pj->m_bodies[1].xform().inverse()*specs.position; | |||
|  | 	pj->m_cfm		=	specs.cfm; | |||
|  | 	pj->m_erp		=	specs.erp; | |||
|  | 	pj->m_split		=	specs.split; | |||
|  | 	m_joints.push_back(pj); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendLinearJoint(const LJoint::Specs& specs,Body body) | |||
|  | { | |||
|  | 	appendLinearJoint(specs,m_clusters[0],body); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendLinearJoint(const LJoint::Specs& specs,btSoftBody* body) | |||
|  | { | |||
|  | 	appendLinearJoint(specs,m_clusters[0],body->m_clusters[0]); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendAngularJoint(const AJoint::Specs& specs,Cluster* body0,Body body1) | |||
|  | { | |||
|  | 	AJoint*		pj	=	new(btAlignedAlloc(sizeof(AJoint),16)) AJoint(); | |||
|  | 	pj->m_bodies[0]	=	body0; | |||
|  | 	pj->m_bodies[1]	=	body1; | |||
|  | 	pj->m_refs[0]	=	pj->m_bodies[0].xform().inverse().getBasis()*specs.axis; | |||
|  | 	pj->m_refs[1]	=	pj->m_bodies[1].xform().inverse().getBasis()*specs.axis; | |||
|  | 	pj->m_cfm		=	specs.cfm; | |||
|  | 	pj->m_erp		=	specs.erp; | |||
|  | 	pj->m_split		=	specs.split; | |||
|  | 	pj->m_icontrol	=	specs.icontrol; | |||
|  | 	m_joints.push_back(pj); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendAngularJoint(const AJoint::Specs& specs,Body body) | |||
|  | { | |||
|  | 	appendAngularJoint(specs,m_clusters[0],body); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::appendAngularJoint(const AJoint::Specs& specs,btSoftBody* body) | |||
|  | { | |||
|  | 	appendAngularJoint(specs,m_clusters[0],body->m_clusters[0]); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::addForce(const btVector3& force) | |||
|  | { | |||
|  | 	for(int i=0,ni=m_nodes.size();i<ni;++i) addForce(force,i); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::addForce(const btVector3& force,int node) | |||
|  | { | |||
|  | 	Node&	n=m_nodes[node]; | |||
|  | 	if(n.m_im>0) | |||
|  | 	{ | |||
|  | 		n.m_f	+=	force; | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | void			btSoftBody::addAeroForceToNode(const btVector3& windVelocity,int nodeIndex) | |||
|  | { | |||
|  | 	btAssert(nodeIndex >= 0 && nodeIndex < m_nodes.size()); | |||
|  | 
 | |||
|  | 	const btScalar dt = m_sst.sdt; | |||
|  | 	const btScalar kLF = m_cfg.kLF; | |||
|  | 	const btScalar kDG = m_cfg.kDG; | |||
|  | 	//const btScalar kPR = m_cfg.kPR;
 | |||
|  | 	//const btScalar kVC = m_cfg.kVC;
 | |||
|  | 	const bool as_lift = kLF>0; | |||
|  | 	const bool as_drag = kDG>0; | |||
|  | 	const bool as_aero = as_lift || as_drag; | |||
|  | 	const bool as_vaero = as_aero && (m_cfg.aeromodel < btSoftBody::eAeroModel::F_TwoSided); | |||
|  | 
 | |||
|  | 	Node& n = m_nodes[nodeIndex]; | |||
|  | 
 | |||
|  | 	if( n.m_im>0 ) | |||
|  | 	{ | |||
|  | 		btSoftBody::sMedium	medium; | |||
|  | 
 | |||
|  | 		EvaluateMedium(m_worldInfo, n.m_x, medium); | |||
|  | 		medium.m_velocity = windVelocity; | |||
|  | 		medium.m_density = m_worldInfo->air_density; | |||
|  | 
 | |||
|  | 		/* Aerodynamics			*/  | |||
|  | 		if(as_vaero) | |||
|  | 		{				 | |||
|  | 			const btVector3	rel_v = n.m_v - medium.m_velocity;					 | |||
|  | 			const btScalar rel_v_len = rel_v.length(); | |||
|  | 			const btScalar	rel_v2 = rel_v.length2(); | |||
|  | 
 | |||
|  | 			if(rel_v2>SIMD_EPSILON) | |||
|  | 			{ | |||
|  | 				const btVector3 rel_v_nrm = rel_v.normalized(); | |||
|  | 				btVector3	nrm = n.m_n;						 | |||
|  | 
 | |||
|  | 				if (m_cfg.aeromodel == btSoftBody::eAeroModel::V_TwoSidedLiftDrag) | |||
|  | 				{ | |||
|  | 					nrm *= (btScalar)( (btDot(nrm,rel_v) < 0) ? -1 : +1); | |||
|  | 					btVector3 fDrag(0, 0, 0); | |||
|  | 					btVector3 fLift(0, 0, 0); | |||
|  | 
 | |||
|  | 					btScalar n_dot_v = nrm.dot(rel_v_nrm); | |||
|  | 					btScalar tri_area = 0.5f * n.m_area; | |||
|  | 							 | |||
|  | 					fDrag = 0.5f * kDG * medium.m_density * rel_v2 * tri_area * n_dot_v * (-rel_v_nrm); | |||
|  | 							 | |||
|  | 					// Check angle of attack
 | |||
|  | 					// cos(10<31>) = 0.98480
 | |||
|  | 					if ( 0 < n_dot_v && n_dot_v < 0.98480f) | |||
|  | 						fLift = 0.5f * kLF * medium.m_density * rel_v_len * tri_area * btSqrt(1.0f-n_dot_v*n_dot_v) * (nrm.cross(rel_v_nrm).cross(rel_v_nrm)); | |||
|  | 
 | |||
|  | 					// Check if the velocity change resulted by aero drag force exceeds the current velocity of the node.
 | |||
|  | 					btVector3 del_v_by_fDrag = fDrag*n.m_im*m_sst.sdt;										 | |||
|  | 					btScalar del_v_by_fDrag_len2 = del_v_by_fDrag.length2(); | |||
|  | 					btScalar v_len2 = n.m_v.length2(); | |||
|  | 
 | |||
|  | 					if (del_v_by_fDrag_len2 >= v_len2 && del_v_by_fDrag_len2 > 0) | |||
|  | 					{ | |||
|  | 						btScalar del_v_by_fDrag_len = del_v_by_fDrag.length(); | |||
|  | 						btScalar v_len = n.m_v.length(); | |||
|  | 						fDrag *= btScalar(0.8)*(v_len / del_v_by_fDrag_len); | |||
|  | 					} | |||
|  | 
 | |||
|  | 					n.m_f += fDrag; | |||
|  | 					n.m_f += fLift; | |||
|  | 				} | |||
|  | 				else if (m_cfg.aeromodel == btSoftBody::eAeroModel::V_Point || m_cfg.aeromodel == btSoftBody::eAeroModel::V_OneSided || m_cfg.aeromodel == btSoftBody::eAeroModel::V_TwoSided) | |||
|  | 				{ | |||
|  | 					if (m_cfg.aeromodel == btSoftBody::eAeroModel::V_TwoSided) | |||
|  | 						nrm *= (btScalar)( (btDot(nrm,rel_v) < 0) ? -1 : +1); | |||
|  | 
 | |||
|  | 					const btScalar dvn = btDot(rel_v,nrm); | |||
|  | 					/* Compute forces	*/  | |||
|  | 					if(dvn>0) | |||
|  | 					{ | |||
|  | 						btVector3		force(0,0,0); | |||
|  | 						const btScalar	c0	=	n.m_area * dvn * rel_v2/2; | |||
|  | 						const btScalar	c1	=	c0 * medium.m_density; | |||
|  | 						force	+=	nrm*(-c1*kLF); | |||
|  | 						force	+=	rel_v.normalized() * (-c1 * kDG); | |||
|  | 						ApplyClampedForce(n, force, dt); | |||
|  | 					} | |||
|  | 				}	 | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | void			btSoftBody::addAeroForceToFace(const btVector3& windVelocity,int faceIndex) | |||
|  | { | |||
|  | 	const btScalar dt = m_sst.sdt; | |||
|  | 	const btScalar kLF = m_cfg.kLF; | |||
|  | 	const btScalar kDG = m_cfg.kDG; | |||
|  | //	const btScalar kPR = m_cfg.kPR;
 | |||
|  | //	const btScalar kVC = m_cfg.kVC;
 | |||
|  | 	const bool as_lift = kLF>0; | |||
|  | 	const bool as_drag = kDG>0; | |||
|  | 	const bool as_aero = as_lift || as_drag; | |||
|  | 	const bool as_faero = as_aero && (m_cfg.aeromodel >= btSoftBody::eAeroModel::F_TwoSided); | |||
|  | 
 | |||
|  | 	if(as_faero) | |||
|  | 	{ | |||
|  | 		btSoftBody::Face&	f=m_faces[faceIndex]; | |||
|  | 
 | |||
|  | 		btSoftBody::sMedium	medium; | |||
|  | 		 | |||
|  | 		const btVector3	v=(f.m_n[0]->m_v+f.m_n[1]->m_v+f.m_n[2]->m_v)/3; | |||
|  | 		const btVector3	x=(f.m_n[0]->m_x+f.m_n[1]->m_x+f.m_n[2]->m_x)/3; | |||
|  | 		EvaluateMedium(m_worldInfo,x,medium); | |||
|  | 		medium.m_velocity = windVelocity; | |||
|  | 		medium.m_density = m_worldInfo->air_density; | |||
|  | 		const btVector3	rel_v=v-medium.m_velocity; | |||
|  | 		const btScalar rel_v_len = rel_v.length(); | |||
|  | 		const btScalar	rel_v2=rel_v.length2(); | |||
|  | 
 | |||
|  | 		if(rel_v2>SIMD_EPSILON) | |||
|  | 		{ | |||
|  | 			const btVector3 rel_v_nrm = rel_v.normalized(); | |||
|  | 			btVector3	nrm = f.m_normal; | |||
|  | 
 | |||
|  | 			if (m_cfg.aeromodel == btSoftBody::eAeroModel::F_TwoSidedLiftDrag) | |||
|  | 			{ | |||
|  | 				nrm *= (btScalar)( (btDot(nrm,rel_v) < 0) ? -1 : +1); | |||
|  | 
 | |||
|  | 				btVector3 fDrag(0, 0, 0); | |||
|  | 				btVector3 fLift(0, 0, 0); | |||
|  | 
 | |||
|  | 				btScalar n_dot_v = nrm.dot(rel_v_nrm); | |||
|  | 				btScalar tri_area = 0.5f * f.m_ra; | |||
|  | 					 | |||
|  | 				fDrag = 0.5f * kDG * medium.m_density * rel_v2 * tri_area * n_dot_v * (-rel_v_nrm); | |||
|  | 
 | |||
|  | 				// Check angle of attack
 | |||
|  | 				// cos(10<31>) = 0.98480
 | |||
|  | 				if ( 0 < n_dot_v && n_dot_v < 0.98480f) | |||
|  | 					fLift = 0.5f * kLF * medium.m_density * rel_v_len * tri_area * btSqrt(1.0f-n_dot_v*n_dot_v) * (nrm.cross(rel_v_nrm).cross(rel_v_nrm)); | |||
|  | 
 | |||
|  | 				fDrag /= 3; | |||
|  | 				fLift /= 3; | |||
|  | 
 | |||
|  | 				for(int j=0;j<3;++j)  | |||
|  | 				{ | |||
|  | 					if (f.m_n[j]->m_im>0) | |||
|  | 					{ | |||
|  | 						// Check if the velocity change resulted by aero drag force exceeds the current velocity of the node.
 | |||
|  | 						btVector3 del_v_by_fDrag = fDrag*f.m_n[j]->m_im*m_sst.sdt;										 | |||
|  | 						btScalar del_v_by_fDrag_len2 = del_v_by_fDrag.length2(); | |||
|  | 						btScalar v_len2 = f.m_n[j]->m_v.length2(); | |||
|  | 
 | |||
|  | 						if (del_v_by_fDrag_len2 >= v_len2 && del_v_by_fDrag_len2 > 0) | |||
|  | 						{ | |||
|  | 							btScalar del_v_by_fDrag_len = del_v_by_fDrag.length(); | |||
|  | 							btScalar v_len = f.m_n[j]->m_v.length(); | |||
|  | 							fDrag *= btScalar(0.8)*(v_len / del_v_by_fDrag_len); | |||
|  | 						} | |||
|  | 
 | |||
|  | 						f.m_n[j]->m_f += fDrag;  | |||
|  | 						f.m_n[j]->m_f += fLift; | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 			else if (m_cfg.aeromodel == btSoftBody::eAeroModel::F_OneSided || m_cfg.aeromodel == btSoftBody::eAeroModel::F_TwoSided) | |||
|  | 			{ | |||
|  | 				if (m_cfg.aeromodel == btSoftBody::eAeroModel::F_TwoSided) | |||
|  | 					nrm *= (btScalar)( (btDot(nrm,rel_v) < 0) ? -1 : +1); | |||
|  | 
 | |||
|  | 				const btScalar	dvn=btDot(rel_v,nrm); | |||
|  | 				/* Compute forces	*/  | |||
|  | 				if(dvn>0) | |||
|  | 				{ | |||
|  | 					btVector3		force(0,0,0); | |||
|  | 					const btScalar	c0	=	f.m_ra*dvn*rel_v2; | |||
|  | 					const btScalar	c1	=	c0*medium.m_density; | |||
|  | 					force	+=	nrm*(-c1*kLF); | |||
|  | 					force	+=	rel_v.normalized()*(-c1*kDG); | |||
|  | 					force	/=	3; | |||
|  | 					for(int j=0;j<3;++j) ApplyClampedForce(*f.m_n[j],force,dt); | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::addVelocity(const btVector3& velocity) | |||
|  | { | |||
|  | 	for(int i=0,ni=m_nodes.size();i<ni;++i) addVelocity(velocity,i); | |||
|  | } | |||
|  | 
 | |||
|  | /* Set velocity for the entire body										*/  | |||
|  | void				btSoftBody::setVelocity(	const btVector3& velocity) | |||
|  | { | |||
|  | 	for(int i=0,ni=m_nodes.size();i<ni;++i)  | |||
|  | 	{ | |||
|  | 		Node&	n=m_nodes[i]; | |||
|  | 		if(n.m_im>0) | |||
|  | 		{ | |||
|  | 			n.m_v	=	velocity; | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::addVelocity(const btVector3& velocity,int node) | |||
|  | { | |||
|  | 	Node&	n=m_nodes[node]; | |||
|  | 	if(n.m_im>0) | |||
|  | 	{ | |||
|  | 		n.m_v	+=	velocity; | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::setMass(int node,btScalar mass) | |||
|  | { | |||
|  | 	m_nodes[node].m_im=mass>0?1/mass:0; | |||
|  | 	m_bUpdateRtCst=true; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btScalar		btSoftBody::getMass(int node) const | |||
|  | { | |||
|  | 	return(m_nodes[node].m_im>0?1/m_nodes[node].m_im:0); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btScalar		btSoftBody::getTotalMass() const | |||
|  | { | |||
|  | 	btScalar	mass=0; | |||
|  | 	for(int i=0;i<m_nodes.size();++i) | |||
|  | 	{ | |||
|  | 		mass+=getMass(i); | |||
|  | 	} | |||
|  | 	return(mass); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::setTotalMass(btScalar mass,bool fromfaces) | |||
|  | { | |||
|  | 	int i; | |||
|  | 
 | |||
|  | 	if(fromfaces) | |||
|  | 	{ | |||
|  | 
 | |||
|  | 		for(i=0;i<m_nodes.size();++i) | |||
|  | 		{ | |||
|  | 			m_nodes[i].m_im=0; | |||
|  | 		} | |||
|  | 		for(i=0;i<m_faces.size();++i) | |||
|  | 		{ | |||
|  | 			const Face&		f=m_faces[i]; | |||
|  | 			const btScalar	twicearea=AreaOf(	f.m_n[0]->m_x, | |||
|  | 				f.m_n[1]->m_x, | |||
|  | 				f.m_n[2]->m_x); | |||
|  | 			for(int j=0;j<3;++j) | |||
|  | 			{ | |||
|  | 				f.m_n[j]->m_im+=twicearea; | |||
|  | 			} | |||
|  | 		} | |||
|  | 		for( i=0;i<m_nodes.size();++i) | |||
|  | 		{ | |||
|  | 			m_nodes[i].m_im=1/m_nodes[i].m_im; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	const btScalar	tm=getTotalMass(); | |||
|  | 	const btScalar	itm=1/tm; | |||
|  | 	for( i=0;i<m_nodes.size();++i) | |||
|  | 	{ | |||
|  | 		m_nodes[i].m_im/=itm*mass; | |||
|  | 	} | |||
|  | 	m_bUpdateRtCst=true; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::setTotalDensity(btScalar density) | |||
|  | { | |||
|  | 	setTotalMass(getVolume()*density,true); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::setVolumeMass(btScalar mass) | |||
|  | { | |||
|  | btAlignedObjectArray<btScalar>	ranks; | |||
|  | ranks.resize(m_nodes.size(),0); | |||
|  | int i; | |||
|  | 
 | |||
|  | for(i=0;i<m_nodes.size();++i) | |||
|  | 	{ | |||
|  | 	m_nodes[i].m_im=0; | |||
|  | 	} | |||
|  | for(i=0;i<m_tetras.size();++i) | |||
|  | 	{ | |||
|  | 	const Tetra& t=m_tetras[i]; | |||
|  | 	for(int j=0;j<4;++j) | |||
|  | 		{ | |||
|  | 		t.m_n[j]->m_im+=btFabs(t.m_rv); | |||
|  | 		ranks[int(t.m_n[j]-&m_nodes[0])]+=1; | |||
|  | 		} | |||
|  | 	} | |||
|  | for( i=0;i<m_nodes.size();++i) | |||
|  | 	{ | |||
|  | 	if(m_nodes[i].m_im>0) | |||
|  | 		{ | |||
|  | 		m_nodes[i].m_im=ranks[i]/m_nodes[i].m_im; | |||
|  | 		} | |||
|  | 	} | |||
|  | setTotalMass(mass,false); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::setVolumeDensity(btScalar density) | |||
|  | { | |||
|  | btScalar	volume=0; | |||
|  | for(int i=0;i<m_tetras.size();++i) | |||
|  | 	{ | |||
|  | 	const Tetra& t=m_tetras[i]; | |||
|  | 	for(int j=0;j<4;++j) | |||
|  | 		{ | |||
|  | 		volume+=btFabs(t.m_rv); | |||
|  | 		} | |||
|  | 	} | |||
|  | setVolumeMass(volume*density/6); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::transform(const btTransform& trs) | |||
|  | { | |||
|  | 	const btScalar	margin=getCollisionShape()->getMargin(); | |||
|  | 	ATTRIBUTE_ALIGNED16(btDbvtVolume)	vol; | |||
|  | 	 | |||
|  | 	for(int i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Node&	n=m_nodes[i]; | |||
|  | 		n.m_x=trs*n.m_x; | |||
|  | 		n.m_q=trs*n.m_q; | |||
|  | 		n.m_n=trs.getBasis()*n.m_n; | |||
|  | 		vol = btDbvtVolume::FromCR(n.m_x,margin); | |||
|  | 		 | |||
|  | 		m_ndbvt.update(n.m_leaf,vol); | |||
|  | 	} | |||
|  | 	updateNormals(); | |||
|  | 	updateBounds(); | |||
|  | 	updateConstants(); | |||
|  | 	m_initialWorldTransform = trs; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::translate(const btVector3& trs) | |||
|  | { | |||
|  | 	btTransform	t; | |||
|  | 	t.setIdentity(); | |||
|  | 	t.setOrigin(trs); | |||
|  | 	transform(t); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::rotate(	const btQuaternion& rot) | |||
|  | { | |||
|  | 	btTransform	t; | |||
|  | 	t.setIdentity(); | |||
|  | 	t.setRotation(rot); | |||
|  | 	transform(t); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::scale(const btVector3& scl) | |||
|  | { | |||
|  | 
 | |||
|  | 	const btScalar	margin=getCollisionShape()->getMargin(); | |||
|  | 	ATTRIBUTE_ALIGNED16(btDbvtVolume)	vol; | |||
|  | 	 | |||
|  | 	for(int i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Node&	n=m_nodes[i]; | |||
|  | 		n.m_x*=scl; | |||
|  | 		n.m_q*=scl; | |||
|  | 		vol = btDbvtVolume::FromCR(n.m_x,margin); | |||
|  | 		m_ndbvt.update(n.m_leaf,vol); | |||
|  | 	} | |||
|  | 	updateNormals(); | |||
|  | 	updateBounds(); | |||
|  | 	updateConstants(); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btScalar btSoftBody::getRestLengthScale() | |||
|  | { | |||
|  | 	return m_restLengthScale; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void btSoftBody::setRestLengthScale(btScalar restLengthScale) | |||
|  | { | |||
|  | 	for(int i=0, ni=m_links.size(); i<ni; ++i) | |||
|  | 	{ | |||
|  | 		Link&		l=m_links[i]; | |||
|  | 		l.m_rl	=	l.m_rl / m_restLengthScale * restLengthScale; | |||
|  | 		l.m_c1	=	l.m_rl*l.m_rl; | |||
|  | 	} | |||
|  | 	m_restLengthScale = restLengthScale; | |||
|  | 	 | |||
|  | 	if (getActivationState() == ISLAND_SLEEPING) | |||
|  | 		activate(); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::setPose(bool bvolume,bool bframe) | |||
|  | { | |||
|  | 	m_pose.m_bvolume	=	bvolume; | |||
|  | 	m_pose.m_bframe		=	bframe; | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	/* Weights		*/  | |||
|  | 	const btScalar	omass=getTotalMass(); | |||
|  | 	const btScalar	kmass=omass*m_nodes.size()*1000; | |||
|  | 	btScalar		tmass=omass; | |||
|  | 	m_pose.m_wgh.resize(m_nodes.size()); | |||
|  | 	for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		if(m_nodes[i].m_im<=0) tmass+=kmass; | |||
|  | 	} | |||
|  | 	for( i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Node&	n=m_nodes[i]; | |||
|  | 		m_pose.m_wgh[i]=	n.m_im>0					? | |||
|  | 			1/(m_nodes[i].m_im*tmass)	: | |||
|  | 		kmass/tmass; | |||
|  | 	} | |||
|  | 	/* Pos		*/  | |||
|  | 	const btVector3	com=evaluateCom(); | |||
|  | 	m_pose.m_pos.resize(m_nodes.size()); | |||
|  | 	for( i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		m_pose.m_pos[i]=m_nodes[i].m_x-com; | |||
|  | 	} | |||
|  | 	m_pose.m_volume	=	bvolume?getVolume():0; | |||
|  | 	m_pose.m_com	=	com; | |||
|  | 	m_pose.m_rot.setIdentity(); | |||
|  | 	m_pose.m_scl.setIdentity(); | |||
|  | 	/* Aqq		*/  | |||
|  | 	m_pose.m_aqq[0]	= | |||
|  | 		m_pose.m_aqq[1]	= | |||
|  | 		m_pose.m_aqq[2]	=	btVector3(0,0,0); | |||
|  | 	for( i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		const btVector3&	q=m_pose.m_pos[i]; | |||
|  | 		const btVector3		mq=m_pose.m_wgh[i]*q; | |||
|  | 		m_pose.m_aqq[0]+=mq.x()*q; | |||
|  | 		m_pose.m_aqq[1]+=mq.y()*q; | |||
|  | 		m_pose.m_aqq[2]+=mq.z()*q; | |||
|  | 	} | |||
|  | 	m_pose.m_aqq=m_pose.m_aqq.inverse(); | |||
|  | 	 | |||
|  | 	updateConstants(); | |||
|  | } | |||
|  | 
 | |||
|  | void				btSoftBody::resetLinkRestLengths() | |||
|  | { | |||
|  | 	for(int i=0, ni=m_links.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Link& l =	m_links[i]; | |||
|  | 		l.m_rl	=	(l.m_n[0]->m_x-l.m_n[1]->m_x).length(); | |||
|  | 		l.m_c1	=	l.m_rl*l.m_rl; | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btScalar		btSoftBody::getVolume() const | |||
|  | { | |||
|  | 	btScalar	vol=0; | |||
|  | 	if(m_nodes.size()>0) | |||
|  | 	{ | |||
|  | 		int i,ni; | |||
|  | 
 | |||
|  | 		const btVector3	org=m_nodes[0].m_x; | |||
|  | 		for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			const Face&	f=m_faces[i]; | |||
|  | 			vol+=btDot(f.m_n[0]->m_x-org,btCross(f.m_n[1]->m_x-org,f.m_n[2]->m_x-org)); | |||
|  | 		} | |||
|  | 		vol/=(btScalar)6; | |||
|  | 	} | |||
|  | 	return(vol); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | int				btSoftBody::clusterCount() const | |||
|  | { | |||
|  | 	return(m_clusters.size()); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btVector3		btSoftBody::clusterCom(const Cluster* cluster) | |||
|  | { | |||
|  | 	btVector3		com(0,0,0); | |||
|  | 	for(int i=0,ni=cluster->m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		com+=cluster->m_nodes[i]->m_x*cluster->m_masses[i]; | |||
|  | 	} | |||
|  | 	return(com*cluster->m_imass); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btVector3		btSoftBody::clusterCom(int cluster) const | |||
|  | { | |||
|  | 	return(clusterCom(m_clusters[cluster])); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btVector3		btSoftBody::clusterVelocity(const Cluster* cluster,const btVector3& rpos) | |||
|  | { | |||
|  | 	return(cluster->m_lv+btCross(cluster->m_av,rpos)); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::clusterVImpulse(Cluster* cluster,const btVector3& rpos,const btVector3& impulse) | |||
|  | { | |||
|  | 	const btVector3	li=cluster->m_imass*impulse; | |||
|  | 	const btVector3	ai=cluster->m_invwi*btCross(rpos,impulse); | |||
|  | 	cluster->m_vimpulses[0]+=li;cluster->m_lv+=li; | |||
|  | 	cluster->m_vimpulses[1]+=ai;cluster->m_av+=ai; | |||
|  | 	cluster->m_nvimpulses++; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::clusterDImpulse(Cluster* cluster,const btVector3& rpos,const btVector3& impulse) | |||
|  | { | |||
|  | 	const btVector3	li=cluster->m_imass*impulse; | |||
|  | 	const btVector3	ai=cluster->m_invwi*btCross(rpos,impulse); | |||
|  | 	cluster->m_dimpulses[0]+=li; | |||
|  | 	cluster->m_dimpulses[1]+=ai; | |||
|  | 	cluster->m_ndimpulses++; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::clusterImpulse(Cluster* cluster,const btVector3& rpos,const Impulse& impulse) | |||
|  | { | |||
|  | 	if(impulse.m_asVelocity)	clusterVImpulse(cluster,rpos,impulse.m_velocity); | |||
|  | 	if(impulse.m_asDrift)		clusterDImpulse(cluster,rpos,impulse.m_drift); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::clusterVAImpulse(Cluster* cluster,const btVector3& impulse) | |||
|  | { | |||
|  | 	const btVector3	ai=cluster->m_invwi*impulse; | |||
|  | 	cluster->m_vimpulses[1]+=ai;cluster->m_av+=ai; | |||
|  | 	cluster->m_nvimpulses++; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::clusterDAImpulse(Cluster* cluster,const btVector3& impulse) | |||
|  | { | |||
|  | 	const btVector3	ai=cluster->m_invwi*impulse; | |||
|  | 	cluster->m_dimpulses[1]+=ai; | |||
|  | 	cluster->m_ndimpulses++; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::clusterAImpulse(Cluster* cluster,const Impulse& impulse) | |||
|  | { | |||
|  | 	if(impulse.m_asVelocity)	clusterVAImpulse(cluster,impulse.m_velocity); | |||
|  | 	if(impulse.m_asDrift)		clusterDAImpulse(cluster,impulse.m_drift); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::clusterDCImpulse(Cluster* cluster,const btVector3& impulse) | |||
|  | { | |||
|  | 	cluster->m_dimpulses[0]+=impulse*cluster->m_imass; | |||
|  | 	cluster->m_ndimpulses++; | |||
|  | } | |||
|  | 
 | |||
|  | struct NodeLinks | |||
|  | { | |||
|  |     btAlignedObjectArray<int> m_links; | |||
|  | }; | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | //
 | |||
|  | int				btSoftBody::generateBendingConstraints(int distance,Material* mat) | |||
|  | { | |||
|  | 	int i,j; | |||
|  | 
 | |||
|  | 	if(distance>1) | |||
|  | 	{ | |||
|  | 		/* Build graph	*/  | |||
|  | 		const int		n=m_nodes.size(); | |||
|  | 		const unsigned	inf=(~(unsigned)0)>>1; | |||
|  | 		unsigned*		adj=new unsigned[n*n]; | |||
|  | 		 | |||
|  | 
 | |||
|  | #define IDX(_x_,_y_)	((_y_)*n+(_x_))
 | |||
|  | 		for(j=0;j<n;++j) | |||
|  | 		{ | |||
|  | 			for(i=0;i<n;++i) | |||
|  | 			{ | |||
|  | 				if(i!=j) | |||
|  | 				{ | |||
|  | 					adj[IDX(i,j)]=adj[IDX(j,i)]=inf; | |||
|  | 				} | |||
|  | 				else | |||
|  | 				{ | |||
|  | 					adj[IDX(i,j)]=adj[IDX(j,i)]=0; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 		for( i=0;i<m_links.size();++i) | |||
|  | 		{ | |||
|  | 			const int	ia=(int)(m_links[i].m_n[0]-&m_nodes[0]); | |||
|  | 			const int	ib=(int)(m_links[i].m_n[1]-&m_nodes[0]); | |||
|  | 			adj[IDX(ia,ib)]=1; | |||
|  | 			adj[IDX(ib,ia)]=1; | |||
|  | 		} | |||
|  | 
 | |||
|  | 
 | |||
|  | 		//special optimized case for distance == 2
 | |||
|  | 		if (distance == 2) | |||
|  | 		{ | |||
|  | 
 | |||
|  | 			btAlignedObjectArray<NodeLinks> nodeLinks; | |||
|  | 
 | |||
|  | 
 | |||
|  | 			/* Build node links */ | |||
|  | 			nodeLinks.resize(m_nodes.size()); | |||
|  | 
 | |||
|  | 			for( i=0;i<m_links.size();++i) | |||
|  | 			{ | |||
|  | 				const int	ia=(int)(m_links[i].m_n[0]-&m_nodes[0]); | |||
|  | 				const int	ib=(int)(m_links[i].m_n[1]-&m_nodes[0]); | |||
|  | 				if (nodeLinks[ia].m_links.findLinearSearch(ib)==nodeLinks[ia].m_links.size()) | |||
|  | 					nodeLinks[ia].m_links.push_back(ib); | |||
|  | 
 | |||
|  | 				if (nodeLinks[ib].m_links.findLinearSearch(ia)==nodeLinks[ib].m_links.size()) | |||
|  | 					nodeLinks[ib].m_links.push_back(ia); | |||
|  | 			} | |||
|  | 			for (int ii=0;ii<nodeLinks.size();ii++) | |||
|  | 			{ | |||
|  | 				int i=ii; | |||
|  | 
 | |||
|  | 				for (int jj=0;jj<nodeLinks[ii].m_links.size();jj++) | |||
|  | 				{ | |||
|  | 					int k = nodeLinks[ii].m_links[jj]; | |||
|  | 					for (int kk=0;kk<nodeLinks[k].m_links.size();kk++) | |||
|  | 					{ | |||
|  | 						int j = nodeLinks[k].m_links[kk]; | |||
|  | 						if (i!=j) | |||
|  | 						{ | |||
|  | 							const unsigned	sum=adj[IDX(i,k)]+adj[IDX(k,j)]; | |||
|  | 							btAssert(sum==2); | |||
|  | 							if(adj[IDX(i,j)]>sum) | |||
|  | 							{ | |||
|  | 								adj[IDX(i,j)]=adj[IDX(j,i)]=sum; | |||
|  | 							} | |||
|  | 						} | |||
|  | 
 | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} else | |||
|  | 		{ | |||
|  | 			///generic Floyd's algorithm
 | |||
|  | 			for(int k=0;k<n;++k) | |||
|  | 			{ | |||
|  | 				for(j=0;j<n;++j) | |||
|  | 				{ | |||
|  | 					for(i=j+1;i<n;++i) | |||
|  | 					{ | |||
|  | 						const unsigned	sum=adj[IDX(i,k)]+adj[IDX(k,j)]; | |||
|  | 						if(adj[IDX(i,j)]>sum) | |||
|  | 						{ | |||
|  | 							adj[IDX(i,j)]=adj[IDX(j,i)]=sum; | |||
|  | 						} | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 
 | |||
|  | 
 | |||
|  | 		/* Build links	*/  | |||
|  | 		int	nlinks=0; | |||
|  | 		for(j=0;j<n;++j) | |||
|  | 		{ | |||
|  | 			for(i=j+1;i<n;++i) | |||
|  | 			{ | |||
|  | 				if(adj[IDX(i,j)]==(unsigned)distance) | |||
|  | 				{ | |||
|  | 					appendLink(i,j,mat); | |||
|  | 					m_links[m_links.size()-1].m_bbending=1; | |||
|  | 					++nlinks; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 		delete[] adj;		 | |||
|  | 		return(nlinks); | |||
|  | 	} | |||
|  | 	return(0); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::randomizeConstraints() | |||
|  | { | |||
|  | 	unsigned long	seed=243703; | |||
|  | #define NEXTRAND (seed=(1664525L*seed+1013904223L)&0xffffffff)
 | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	for(i=0,ni=m_links.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		btSwap(m_links[i],m_links[NEXTRAND%ni]); | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		btSwap(m_faces[i],m_faces[NEXTRAND%ni]); | |||
|  | 	} | |||
|  | #undef NEXTRAND
 | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::releaseCluster(int index) | |||
|  | { | |||
|  | 	Cluster*	c=m_clusters[index]; | |||
|  | 	if(c->m_leaf) m_cdbvt.remove(c->m_leaf); | |||
|  | 	c->~Cluster(); | |||
|  | 	btAlignedFree(c); | |||
|  | 	m_clusters.remove(c); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::releaseClusters() | |||
|  | { | |||
|  | 	while(m_clusters.size()>0) releaseCluster(0); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | int				btSoftBody::generateClusters(int k,int maxiterations) | |||
|  | { | |||
|  | 	int i; | |||
|  | 	releaseClusters(); | |||
|  | 	m_clusters.resize(btMin(k,m_nodes.size())); | |||
|  | 	for(i=0;i<m_clusters.size();++i) | |||
|  | 	{ | |||
|  | 		m_clusters[i]			=	new(btAlignedAlloc(sizeof(Cluster),16)) Cluster(); | |||
|  | 		m_clusters[i]->m_collide=	true; | |||
|  | 	} | |||
|  | 	k=m_clusters.size(); | |||
|  | 	if(k>0) | |||
|  | 	{ | |||
|  | 		/* Initialize		*/  | |||
|  | 		btAlignedObjectArray<btVector3>	centers; | |||
|  | 		btVector3						cog(0,0,0); | |||
|  | 		int								i; | |||
|  | 		for(i=0;i<m_nodes.size();++i) | |||
|  | 		{ | |||
|  | 			cog+=m_nodes[i].m_x; | |||
|  | 			m_clusters[(i*29873)%m_clusters.size()]->m_nodes.push_back(&m_nodes[i]); | |||
|  | 		} | |||
|  | 		cog/=(btScalar)m_nodes.size(); | |||
|  | 		centers.resize(k,cog); | |||
|  | 		/* Iterate			*/  | |||
|  | 		const btScalar	slope=16; | |||
|  | 		bool			changed; | |||
|  | 		int				iterations=0; | |||
|  | 		do	{ | |||
|  | 			const btScalar	w=2-btMin<btScalar>(1,iterations/slope); | |||
|  | 			changed=false; | |||
|  | 			iterations++;	 | |||
|  | 			int i; | |||
|  | 
 | |||
|  | 			for(i=0;i<k;++i) | |||
|  | 			{ | |||
|  | 				btVector3	c(0,0,0); | |||
|  | 				for(int j=0;j<m_clusters[i]->m_nodes.size();++j) | |||
|  | 				{ | |||
|  | 					c+=m_clusters[i]->m_nodes[j]->m_x; | |||
|  | 				} | |||
|  | 				if(m_clusters[i]->m_nodes.size()) | |||
|  | 				{ | |||
|  | 					c			/=	(btScalar)m_clusters[i]->m_nodes.size(); | |||
|  | 					c			=	centers[i]+(c-centers[i])*w; | |||
|  | 					changed		|=	((c-centers[i]).length2()>SIMD_EPSILON); | |||
|  | 					centers[i]	=	c; | |||
|  | 					m_clusters[i]->m_nodes.resize(0); | |||
|  | 				}			 | |||
|  | 			} | |||
|  | 			for(i=0;i<m_nodes.size();++i) | |||
|  | 			{ | |||
|  | 				const btVector3	nx=m_nodes[i].m_x; | |||
|  | 				int				kbest=0; | |||
|  | 				btScalar		kdist=ClusterMetric(centers[0],nx); | |||
|  | 				for(int j=1;j<k;++j) | |||
|  | 				{ | |||
|  | 					const btScalar	d=ClusterMetric(centers[j],nx); | |||
|  | 					if(d<kdist) | |||
|  | 					{ | |||
|  | 						kbest=j; | |||
|  | 						kdist=d; | |||
|  | 					} | |||
|  | 				} | |||
|  | 				m_clusters[kbest]->m_nodes.push_back(&m_nodes[i]); | |||
|  | 			}		 | |||
|  | 		} while(changed&&(iterations<maxiterations)); | |||
|  | 		/* Merge		*/  | |||
|  | 		btAlignedObjectArray<int>	cids; | |||
|  | 		cids.resize(m_nodes.size(),-1); | |||
|  | 		for(i=0;i<m_clusters.size();++i) | |||
|  | 		{ | |||
|  | 			for(int j=0;j<m_clusters[i]->m_nodes.size();++j) | |||
|  | 			{ | |||
|  | 				cids[int(m_clusters[i]->m_nodes[j]-&m_nodes[0])]=i; | |||
|  | 			} | |||
|  | 		} | |||
|  | 		for(i=0;i<m_faces.size();++i) | |||
|  | 		{ | |||
|  | 			const int idx[]={	int(m_faces[i].m_n[0]-&m_nodes[0]), | |||
|  | 				int(m_faces[i].m_n[1]-&m_nodes[0]), | |||
|  | 				int(m_faces[i].m_n[2]-&m_nodes[0])}; | |||
|  | 			for(int j=0;j<3;++j) | |||
|  | 			{ | |||
|  | 				const int cid=cids[idx[j]]; | |||
|  | 				for(int q=1;q<3;++q) | |||
|  | 				{ | |||
|  | 					const int kid=idx[(j+q)%3]; | |||
|  | 					if(cids[kid]!=cid) | |||
|  | 					{ | |||
|  | 						if(m_clusters[cid]->m_nodes.findLinearSearch(&m_nodes[kid])==m_clusters[cid]->m_nodes.size()) | |||
|  | 						{ | |||
|  | 							m_clusters[cid]->m_nodes.push_back(&m_nodes[kid]); | |||
|  | 						} | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 		/* Master		*/  | |||
|  | 		if(m_clusters.size()>1) | |||
|  | 		{ | |||
|  | 			Cluster*	pmaster=new(btAlignedAlloc(sizeof(Cluster),16)) Cluster(); | |||
|  | 			pmaster->m_collide	=	false; | |||
|  | 			pmaster->m_nodes.reserve(m_nodes.size()); | |||
|  | 			for(int i=0;i<m_nodes.size();++i) pmaster->m_nodes.push_back(&m_nodes[i]); | |||
|  | 			m_clusters.push_back(pmaster); | |||
|  | 			btSwap(m_clusters[0],m_clusters[m_clusters.size()-1]); | |||
|  | 		} | |||
|  | 		/* Terminate	*/  | |||
|  | 		for(i=0;i<m_clusters.size();++i) | |||
|  | 		{ | |||
|  | 			if(m_clusters[i]->m_nodes.size()==0) | |||
|  | 			{ | |||
|  | 				releaseCluster(i--); | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} else | |||
|  | 	{ | |||
|  | 		//create a cluster for each tetrahedron (if tetrahedra exist) or each face
 | |||
|  | 		if (m_tetras.size()) | |||
|  | 		{ | |||
|  | 			m_clusters.resize(m_tetras.size()); | |||
|  | 			for(i=0;i<m_clusters.size();++i) | |||
|  | 			{ | |||
|  | 				m_clusters[i]			=	new(btAlignedAlloc(sizeof(Cluster),16)) Cluster(); | |||
|  | 				m_clusters[i]->m_collide=	true; | |||
|  | 			} | |||
|  | 			for (i=0;i<m_tetras.size();i++) | |||
|  | 			{ | |||
|  | 				for (int j=0;j<4;j++) | |||
|  | 				{ | |||
|  | 					m_clusters[i]->m_nodes.push_back(m_tetras[i].m_n[j]); | |||
|  | 				} | |||
|  | 			} | |||
|  | 
 | |||
|  | 		} else | |||
|  | 		{ | |||
|  | 			m_clusters.resize(m_faces.size()); | |||
|  | 			for(i=0;i<m_clusters.size();++i) | |||
|  | 			{ | |||
|  | 				m_clusters[i]			=	new(btAlignedAlloc(sizeof(Cluster),16)) Cluster(); | |||
|  | 				m_clusters[i]->m_collide=	true; | |||
|  | 			} | |||
|  | 
 | |||
|  | 			for(i=0;i<m_faces.size();++i) | |||
|  | 			{ | |||
|  | 				for(int j=0;j<3;++j) | |||
|  | 				{ | |||
|  | 					m_clusters[i]->m_nodes.push_back(m_faces[i].m_n[j]); | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if (m_clusters.size()) | |||
|  | 	{ | |||
|  | 		initializeClusters(); | |||
|  | 		updateClusters(); | |||
|  | 
 | |||
|  | 
 | |||
|  | 		//for self-collision
 | |||
|  | 		m_clusterConnectivity.resize(m_clusters.size()*m_clusters.size()); | |||
|  | 		{ | |||
|  | 			for (int c0=0;c0<m_clusters.size();c0++) | |||
|  | 			{ | |||
|  | 				m_clusters[c0]->m_clusterIndex=c0; | |||
|  | 				for (int c1=0;c1<m_clusters.size();c1++) | |||
|  | 				{ | |||
|  | 					 | |||
|  | 					bool connected=false; | |||
|  | 					Cluster* cla = m_clusters[c0]; | |||
|  | 					Cluster* clb = m_clusters[c1]; | |||
|  | 					for (int i=0;!connected&&i<cla->m_nodes.size();i++) | |||
|  | 					{ | |||
|  | 						for (int j=0;j<clb->m_nodes.size();j++) | |||
|  | 						{ | |||
|  | 							if (cla->m_nodes[i] == clb->m_nodes[j]) | |||
|  | 							{ | |||
|  | 								connected=true; | |||
|  | 								break; | |||
|  | 							} | |||
|  | 						} | |||
|  | 					} | |||
|  | 					m_clusterConnectivity[c0+c1*m_clusters.size()]=connected; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return(m_clusters.size()); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::refine(ImplicitFn* ifn,btScalar accurary,bool cut) | |||
|  | { | |||
|  | 	const Node*			nbase = &m_nodes[0]; | |||
|  | 	int					ncount = m_nodes.size(); | |||
|  | 	btSymMatrix<int>	edges(ncount,-2); | |||
|  | 	int					newnodes=0; | |||
|  | 	int i,j,k,ni; | |||
|  | 
 | |||
|  | 	/* Filter out		*/  | |||
|  | 	for(i=0;i<m_links.size();++i) | |||
|  | 	{ | |||
|  | 		Link&	l=m_links[i]; | |||
|  | 		if(l.m_bbending) | |||
|  | 		{ | |||
|  | 			if(!SameSign(ifn->Eval(l.m_n[0]->m_x),ifn->Eval(l.m_n[1]->m_x))) | |||
|  | 			{ | |||
|  | 				btSwap(m_links[i],m_links[m_links.size()-1]); | |||
|  | 				m_links.pop_back();--i; | |||
|  | 			} | |||
|  | 		}	 | |||
|  | 	} | |||
|  | 	/* Fill edges		*/  | |||
|  | 	for(i=0;i<m_links.size();++i) | |||
|  | 	{ | |||
|  | 		Link&	l=m_links[i]; | |||
|  | 		edges(int(l.m_n[0]-nbase),int(l.m_n[1]-nbase))=-1; | |||
|  | 	} | |||
|  | 	for(i=0;i<m_faces.size();++i) | |||
|  | 	{	 | |||
|  | 		Face&	f=m_faces[i]; | |||
|  | 		edges(int(f.m_n[0]-nbase),int(f.m_n[1]-nbase))=-1; | |||
|  | 		edges(int(f.m_n[1]-nbase),int(f.m_n[2]-nbase))=-1; | |||
|  | 		edges(int(f.m_n[2]-nbase),int(f.m_n[0]-nbase))=-1; | |||
|  | 	} | |||
|  | 	/* Intersect		*/  | |||
|  | 	for(i=0;i<ncount;++i) | |||
|  | 	{ | |||
|  | 		for(j=i+1;j<ncount;++j) | |||
|  | 		{ | |||
|  | 			if(edges(i,j)==-1) | |||
|  | 			{ | |||
|  | 				Node&			a=m_nodes[i]; | |||
|  | 				Node&			b=m_nodes[j]; | |||
|  | 				const btScalar	t=ImplicitSolve(ifn,a.m_x,b.m_x,accurary); | |||
|  | 				if(t>0) | |||
|  | 				{ | |||
|  | 					const btVector3	x=Lerp(a.m_x,b.m_x,t); | |||
|  | 					const btVector3	v=Lerp(a.m_v,b.m_v,t); | |||
|  | 					btScalar		m=0; | |||
|  | 					if(a.m_im>0) | |||
|  | 					{ | |||
|  | 						if(b.m_im>0) | |||
|  | 						{ | |||
|  | 							const btScalar	ma=1/a.m_im; | |||
|  | 							const btScalar	mb=1/b.m_im; | |||
|  | 							const btScalar	mc=Lerp(ma,mb,t); | |||
|  | 							const btScalar	f=(ma+mb)/(ma+mb+mc); | |||
|  | 							a.m_im=1/(ma*f); | |||
|  | 							b.m_im=1/(mb*f); | |||
|  | 							m=mc*f; | |||
|  | 						} | |||
|  | 						else | |||
|  | 						{ a.m_im/=0.5f;m=1/a.m_im; } | |||
|  | 					} | |||
|  | 					else | |||
|  | 					{ | |||
|  | 						if(b.m_im>0) | |||
|  | 						{ b.m_im/=0.5f;m=1/b.m_im; } | |||
|  | 						else | |||
|  | 							m=0; | |||
|  | 					} | |||
|  | 					appendNode(x,m); | |||
|  | 					edges(i,j)=m_nodes.size()-1; | |||
|  | 					m_nodes[edges(i,j)].m_v=v; | |||
|  | 					++newnodes; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	nbase=&m_nodes[0]; | |||
|  | 	/* Refine links		*/  | |||
|  | 	for(i=0,ni=m_links.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Link&		feat=m_links[i]; | |||
|  | 		const int	idx[]={	int(feat.m_n[0]-nbase), | |||
|  | 			int(feat.m_n[1]-nbase)}; | |||
|  | 		if((idx[0]<ncount)&&(idx[1]<ncount)) | |||
|  | 		{ | |||
|  | 			const int ni=edges(idx[0],idx[1]); | |||
|  | 			if(ni>0) | |||
|  | 			{ | |||
|  | 				appendLink(i); | |||
|  | 				Link*		pft[]={	&m_links[i], | |||
|  | 					&m_links[m_links.size()-1]};			 | |||
|  | 				pft[0]->m_n[0]=&m_nodes[idx[0]]; | |||
|  | 				pft[0]->m_n[1]=&m_nodes[ni]; | |||
|  | 				pft[1]->m_n[0]=&m_nodes[ni]; | |||
|  | 				pft[1]->m_n[1]=&m_nodes[idx[1]]; | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	/* Refine faces		*/  | |||
|  | 	for(i=0;i<m_faces.size();++i) | |||
|  | 	{ | |||
|  | 		const Face&	feat=m_faces[i]; | |||
|  | 		const int	idx[]={	int(feat.m_n[0]-nbase), | |||
|  | 			int(feat.m_n[1]-nbase), | |||
|  | 			int(feat.m_n[2]-nbase)}; | |||
|  | 		for(j=2,k=0;k<3;j=k++) | |||
|  | 		{ | |||
|  | 			if((idx[j]<ncount)&&(idx[k]<ncount)) | |||
|  | 			{ | |||
|  | 				const int ni=edges(idx[j],idx[k]); | |||
|  | 				if(ni>0) | |||
|  | 				{ | |||
|  | 					appendFace(i); | |||
|  | 					const int	l=(k+1)%3; | |||
|  | 					Face*		pft[]={	&m_faces[i], | |||
|  | 						&m_faces[m_faces.size()-1]}; | |||
|  | 					pft[0]->m_n[0]=&m_nodes[idx[l]]; | |||
|  | 					pft[0]->m_n[1]=&m_nodes[idx[j]]; | |||
|  | 					pft[0]->m_n[2]=&m_nodes[ni]; | |||
|  | 					pft[1]->m_n[0]=&m_nodes[ni]; | |||
|  | 					pft[1]->m_n[1]=&m_nodes[idx[k]]; | |||
|  | 					pft[1]->m_n[2]=&m_nodes[idx[l]]; | |||
|  | 					appendLink(ni,idx[l],pft[0]->m_material); | |||
|  | 					--i;break; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	/* Cut				*/  | |||
|  | 	if(cut) | |||
|  | 	{	 | |||
|  | 		btAlignedObjectArray<int>	cnodes; | |||
|  | 		const int					pcount=ncount; | |||
|  | 		int							i; | |||
|  | 		ncount=m_nodes.size(); | |||
|  | 		cnodes.resize(ncount,0); | |||
|  | 		/* Nodes		*/  | |||
|  | 		for(i=0;i<ncount;++i) | |||
|  | 		{ | |||
|  | 			const btVector3	x=m_nodes[i].m_x; | |||
|  | 			if((i>=pcount)||(btFabs(ifn->Eval(x))<accurary)) | |||
|  | 			{ | |||
|  | 				const btVector3	v=m_nodes[i].m_v; | |||
|  | 				btScalar		m=getMass(i); | |||
|  | 				if(m>0) { m*=0.5f;m_nodes[i].m_im/=0.5f; } | |||
|  | 				appendNode(x,m); | |||
|  | 				cnodes[i]=m_nodes.size()-1; | |||
|  | 				m_nodes[cnodes[i]].m_v=v; | |||
|  | 			} | |||
|  | 		} | |||
|  | 		nbase=&m_nodes[0]; | |||
|  | 		/* Links		*/  | |||
|  | 		for(i=0,ni=m_links.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			const int		id[]={	int(m_links[i].m_n[0]-nbase), | |||
|  | 				int(m_links[i].m_n[1]-nbase)}; | |||
|  | 			int				todetach=0; | |||
|  | 			if(cnodes[id[0]]&&cnodes[id[1]]) | |||
|  | 			{ | |||
|  | 				appendLink(i); | |||
|  | 				todetach=m_links.size()-1; | |||
|  | 			} | |||
|  | 			else | |||
|  | 			{ | |||
|  | 				if((	(ifn->Eval(m_nodes[id[0]].m_x)<accurary)&& | |||
|  | 					(ifn->Eval(m_nodes[id[1]].m_x)<accurary))) | |||
|  | 					todetach=i; | |||
|  | 			} | |||
|  | 			if(todetach) | |||
|  | 			{ | |||
|  | 				Link&	l=m_links[todetach]; | |||
|  | 				for(int j=0;j<2;++j) | |||
|  | 				{ | |||
|  | 					int cn=cnodes[int(l.m_n[j]-nbase)]; | |||
|  | 					if(cn) l.m_n[j]=&m_nodes[cn]; | |||
|  | 				}			 | |||
|  | 			} | |||
|  | 		} | |||
|  | 		/* Faces		*/  | |||
|  | 		for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			Node**			n=	m_faces[i].m_n; | |||
|  | 			if(	(ifn->Eval(n[0]->m_x)<accurary)&& | |||
|  | 				(ifn->Eval(n[1]->m_x)<accurary)&& | |||
|  | 				(ifn->Eval(n[2]->m_x)<accurary)) | |||
|  | 			{ | |||
|  | 				for(int j=0;j<3;++j) | |||
|  | 				{ | |||
|  | 					int cn=cnodes[int(n[j]-nbase)]; | |||
|  | 					if(cn) n[j]=&m_nodes[cn]; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 		/* Clean orphans	*/  | |||
|  | 		int							nnodes=m_nodes.size(); | |||
|  | 		btAlignedObjectArray<int>	ranks; | |||
|  | 		btAlignedObjectArray<int>	todelete; | |||
|  | 		ranks.resize(nnodes,0); | |||
|  | 		for(i=0,ni=m_links.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			for(int j=0;j<2;++j) ranks[int(m_links[i].m_n[j]-nbase)]++; | |||
|  | 		} | |||
|  | 		for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			for(int j=0;j<3;++j) ranks[int(m_faces[i].m_n[j]-nbase)]++; | |||
|  | 		} | |||
|  | 		for(i=0;i<m_links.size();++i) | |||
|  | 		{ | |||
|  | 			const int	id[]={	int(m_links[i].m_n[0]-nbase), | |||
|  | 				int(m_links[i].m_n[1]-nbase)}; | |||
|  | 			const bool	sg[]={	ranks[id[0]]==1, | |||
|  | 				ranks[id[1]]==1}; | |||
|  | 			if(sg[0]||sg[1]) | |||
|  | 			{ | |||
|  | 				--ranks[id[0]]; | |||
|  | 				--ranks[id[1]]; | |||
|  | 				btSwap(m_links[i],m_links[m_links.size()-1]); | |||
|  | 				m_links.pop_back();--i; | |||
|  | 			} | |||
|  | 		} | |||
|  | #if 0	
 | |||
|  | 		for(i=nnodes-1;i>=0;--i) | |||
|  | 		{ | |||
|  | 			if(!ranks[i]) todelete.push_back(i); | |||
|  | 		}	 | |||
|  | 		if(todelete.size()) | |||
|  | 		{		 | |||
|  | 			btAlignedObjectArray<int>&	map=ranks; | |||
|  | 			for(int i=0;i<nnodes;++i) map[i]=i; | |||
|  | 			PointersToIndices(this); | |||
|  | 			for(int i=0,ni=todelete.size();i<ni;++i) | |||
|  | 			{ | |||
|  | 				int		j=todelete[i]; | |||
|  | 				int&	a=map[j]; | |||
|  | 				int&	b=map[--nnodes]; | |||
|  | 				m_ndbvt.remove(m_nodes[a].m_leaf);m_nodes[a].m_leaf=0; | |||
|  | 				btSwap(m_nodes[a],m_nodes[b]); | |||
|  | 				j=a;a=b;b=j;			 | |||
|  | 			} | |||
|  | 			IndicesToPointers(this,&map[0]); | |||
|  | 			m_nodes.resize(nnodes); | |||
|  | 		} | |||
|  | #endif
 | |||
|  | 	} | |||
|  | 	m_bUpdateRtCst=true; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | bool			btSoftBody::cutLink(const Node* node0,const Node* node1,btScalar position) | |||
|  | { | |||
|  | 	return(cutLink(int(node0-&m_nodes[0]),int(node1-&m_nodes[0]),position)); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | bool			btSoftBody::cutLink(int node0,int node1,btScalar position) | |||
|  | { | |||
|  | 	bool			done=false; | |||
|  | 	int i,ni; | |||
|  | //	const btVector3	d=m_nodes[node0].m_x-m_nodes[node1].m_x;
 | |||
|  | 	const btVector3	x=Lerp(m_nodes[node0].m_x,m_nodes[node1].m_x,position); | |||
|  | 	const btVector3	v=Lerp(m_nodes[node0].m_v,m_nodes[node1].m_v,position); | |||
|  | 	const btScalar	m=1; | |||
|  | 	appendNode(x,m); | |||
|  | 	appendNode(x,m); | |||
|  | 	Node*			pa=&m_nodes[node0]; | |||
|  | 	Node*			pb=&m_nodes[node1]; | |||
|  | 	Node*			pn[2]={	&m_nodes[m_nodes.size()-2], | |||
|  | 		&m_nodes[m_nodes.size()-1]}; | |||
|  | 	pn[0]->m_v=v; | |||
|  | 	pn[1]->m_v=v; | |||
|  | 	for(i=0,ni=m_links.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		const int mtch=MatchEdge(m_links[i].m_n[0],m_links[i].m_n[1],pa,pb); | |||
|  | 		if(mtch!=-1) | |||
|  | 		{ | |||
|  | 			appendLink(i); | |||
|  | 			Link*	pft[]={&m_links[i],&m_links[m_links.size()-1]}; | |||
|  | 			pft[0]->m_n[1]=pn[mtch]; | |||
|  | 			pft[1]->m_n[0]=pn[1-mtch]; | |||
|  | 			done=true; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		for(int k=2,l=0;l<3;k=l++) | |||
|  | 		{ | |||
|  | 			const int mtch=MatchEdge(m_faces[i].m_n[k],m_faces[i].m_n[l],pa,pb); | |||
|  | 			if(mtch!=-1) | |||
|  | 			{ | |||
|  | 				appendFace(i); | |||
|  | 				Face*	pft[]={&m_faces[i],&m_faces[m_faces.size()-1]}; | |||
|  | 				pft[0]->m_n[l]=pn[mtch]; | |||
|  | 				pft[1]->m_n[k]=pn[1-mtch]; | |||
|  | 				appendLink(pn[0],pft[0]->m_n[(l+1)%3],pft[0]->m_material,true); | |||
|  | 				appendLink(pn[1],pft[0]->m_n[(l+1)%3],pft[0]->m_material,true); | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	if(!done) | |||
|  | 	{ | |||
|  | 		m_ndbvt.remove(pn[0]->m_leaf); | |||
|  | 		m_ndbvt.remove(pn[1]->m_leaf); | |||
|  | 		m_nodes.pop_back(); | |||
|  | 		m_nodes.pop_back(); | |||
|  | 	} | |||
|  | 	return(done); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | bool			btSoftBody::rayTest(const btVector3& rayFrom, | |||
|  | 									const btVector3& rayTo, | |||
|  | 									sRayCast& results) | |||
|  | { | |||
|  | 	if(m_faces.size()&&m_fdbvt.empty())  | |||
|  | 		initializeFaceTree(); | |||
|  | 
 | |||
|  | 	results.body	=	this; | |||
|  | 	results.fraction = 1.f; | |||
|  | 	results.feature	=	eFeature::None; | |||
|  | 	results.index	=	-1; | |||
|  | 
 | |||
|  | 	return(rayTest(rayFrom,rayTo,results.fraction,results.feature,results.index,false)!=0); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::setSolver(eSolverPresets::_ preset) | |||
|  | { | |||
|  | 	m_cfg.m_vsequence.clear(); | |||
|  | 	m_cfg.m_psequence.clear(); | |||
|  | 	m_cfg.m_dsequence.clear(); | |||
|  | 	switch(preset) | |||
|  | 	{ | |||
|  | 	case	eSolverPresets::Positions: | |||
|  | 		m_cfg.m_psequence.push_back(ePSolver::Anchors); | |||
|  | 		m_cfg.m_psequence.push_back(ePSolver::RContacts); | |||
|  | 		m_cfg.m_psequence.push_back(ePSolver::SContacts); | |||
|  | 		m_cfg.m_psequence.push_back(ePSolver::Linear);	 | |||
|  | 		break;	 | |||
|  | 	case	eSolverPresets::Velocities: | |||
|  | 		m_cfg.m_vsequence.push_back(eVSolver::Linear); | |||
|  | 
 | |||
|  | 		m_cfg.m_psequence.push_back(ePSolver::Anchors); | |||
|  | 		m_cfg.m_psequence.push_back(ePSolver::RContacts); | |||
|  | 		m_cfg.m_psequence.push_back(ePSolver::SContacts); | |||
|  | 
 | |||
|  | 		m_cfg.m_dsequence.push_back(ePSolver::Linear); | |||
|  | 		break; | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::predictMotion(btScalar dt) | |||
|  | { | |||
|  | 
 | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	/* Update				*/  | |||
|  | 	if(m_bUpdateRtCst) | |||
|  | 	{ | |||
|  | 		m_bUpdateRtCst=false; | |||
|  | 		updateConstants(); | |||
|  | 		m_fdbvt.clear(); | |||
|  | 		if(m_cfg.collisions&fCollision::VF_SS) | |||
|  | 		{ | |||
|  | 			initializeFaceTree();			 | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/* Prepare				*/  | |||
|  | 	m_sst.sdt		=	dt*m_cfg.timescale; | |||
|  | 	m_sst.isdt		=	1/m_sst.sdt; | |||
|  | 	m_sst.velmrg	=	m_sst.sdt*3; | |||
|  | 	m_sst.radmrg	=	getCollisionShape()->getMargin(); | |||
|  | 	m_sst.updmrg	=	m_sst.radmrg*(btScalar)0.25; | |||
|  | 	/* Forces				*/  | |||
|  | 	addVelocity(m_worldInfo->m_gravity*m_sst.sdt); | |||
|  | 	applyForces(); | |||
|  | 	/* Integrate			*/  | |||
|  | 	for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Node&	n=m_nodes[i]; | |||
|  | 		n.m_q	=	n.m_x; | |||
|  | 		btVector3 deltaV = n.m_f*n.m_im*m_sst.sdt; | |||
|  | 		{ | |||
|  | 			btScalar maxDisplacement = m_worldInfo->m_maxDisplacement; | |||
|  | 			btScalar clampDeltaV = maxDisplacement/m_sst.sdt; | |||
|  | 			for (int c=0;c<3;c++) | |||
|  | 			{ | |||
|  | 				if (deltaV[c]>clampDeltaV) | |||
|  | 				{ | |||
|  | 					deltaV[c] = clampDeltaV; | |||
|  | 				} | |||
|  | 				if (deltaV[c]<-clampDeltaV) | |||
|  | 				{ | |||
|  | 					deltaV[c]=-clampDeltaV; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 		n.m_v	+=	deltaV; | |||
|  | 		n.m_x	+=	n.m_v*m_sst.sdt; | |||
|  | 		n.m_f	=	btVector3(0,0,0); | |||
|  | 	} | |||
|  | 	/* Clusters				*/  | |||
|  | 	updateClusters(); | |||
|  | 	/* Bounds				*/  | |||
|  | 	updateBounds();	 | |||
|  | 	/* Nodes				*/  | |||
|  | 	ATTRIBUTE_ALIGNED16(btDbvtVolume)	vol; | |||
|  | 	for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Node&	n=m_nodes[i]; | |||
|  | 		vol = btDbvtVolume::FromCR(n.m_x,m_sst.radmrg); | |||
|  | 		m_ndbvt.update(	n.m_leaf, | |||
|  | 			vol, | |||
|  | 			n.m_v*m_sst.velmrg, | |||
|  | 			m_sst.updmrg); | |||
|  | 	} | |||
|  | 	/* Faces				*/  | |||
|  | 	if(!m_fdbvt.empty()) | |||
|  | 	{ | |||
|  | 		for(int i=0;i<m_faces.size();++i) | |||
|  | 		{ | |||
|  | 			Face&			f=m_faces[i]; | |||
|  | 			const btVector3	v=(	f.m_n[0]->m_v+ | |||
|  | 				f.m_n[1]->m_v+ | |||
|  | 				f.m_n[2]->m_v)/3; | |||
|  | 			vol = VolumeOf(f,m_sst.radmrg); | |||
|  | 			m_fdbvt.update(	f.m_leaf, | |||
|  | 				vol, | |||
|  | 				v*m_sst.velmrg, | |||
|  | 				m_sst.updmrg); | |||
|  | 		} | |||
|  | 	} | |||
|  | 	/* Pose					*/  | |||
|  | 	updatePose(); | |||
|  | 	/* Match				*/  | |||
|  | 	if(m_pose.m_bframe&&(m_cfg.kMT>0)) | |||
|  | 	{ | |||
|  | 		const btMatrix3x3	posetrs=m_pose.m_rot; | |||
|  | 		for(int i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			Node&	n=m_nodes[i]; | |||
|  | 			if(n.m_im>0) | |||
|  | 			{ | |||
|  | 				const btVector3	x=posetrs*m_pose.m_pos[i]+m_pose.m_com; | |||
|  | 				n.m_x=Lerp(n.m_x,x,m_cfg.kMT); | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	/* Clear contacts		*/  | |||
|  | 	m_rcontacts.resize(0); | |||
|  | 	m_scontacts.resize(0); | |||
|  | 	/* Optimize dbvt's		*/  | |||
|  | 	m_ndbvt.optimizeIncremental(1); | |||
|  | 	m_fdbvt.optimizeIncremental(1); | |||
|  | 	m_cdbvt.optimizeIncremental(1); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::solveConstraints() | |||
|  | { | |||
|  | 
 | |||
|  | 	/* Apply clusters		*/  | |||
|  | 	applyClusters(false); | |||
|  | 	/* Prepare links		*/  | |||
|  | 
 | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	for(i=0,ni=m_links.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Link&	l=m_links[i]; | |||
|  | 		l.m_c3		=	l.m_n[1]->m_q-l.m_n[0]->m_q; | |||
|  | 		l.m_c2		=	1/(l.m_c3.length2()*l.m_c0); | |||
|  | 	} | |||
|  | 	/* Prepare anchors		*/  | |||
|  | 	for(i=0,ni=m_anchors.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Anchor&			a=m_anchors[i]; | |||
|  | 		const btVector3	ra=a.m_body->getWorldTransform().getBasis()*a.m_local; | |||
|  | 		a.m_c0	=	ImpulseMatrix(	m_sst.sdt, | |||
|  | 			a.m_node->m_im, | |||
|  | 			a.m_body->getInvMass(), | |||
|  | 			a.m_body->getInvInertiaTensorWorld(), | |||
|  | 			ra); | |||
|  | 		a.m_c1	=	ra; | |||
|  | 		a.m_c2	=	m_sst.sdt*a.m_node->m_im; | |||
|  | 		a.m_body->activate(); | |||
|  | 	} | |||
|  | 	/* Solve velocities		*/  | |||
|  | 	if(m_cfg.viterations>0) | |||
|  | 	{ | |||
|  | 		/* Solve			*/  | |||
|  | 		for(int isolve=0;isolve<m_cfg.viterations;++isolve) | |||
|  | 		{ | |||
|  | 			for(int iseq=0;iseq<m_cfg.m_vsequence.size();++iseq) | |||
|  | 			{ | |||
|  | 				getSolver(m_cfg.m_vsequence[iseq])(this,1); | |||
|  | 			} | |||
|  | 		} | |||
|  | 		/* Update			*/  | |||
|  | 		for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			Node&	n=m_nodes[i]; | |||
|  | 			n.m_x	=	n.m_q+n.m_v*m_sst.sdt; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	/* Solve positions		*/  | |||
|  | 	if(m_cfg.piterations>0) | |||
|  | 	{ | |||
|  | 		for(int isolve=0;isolve<m_cfg.piterations;++isolve) | |||
|  | 		{ | |||
|  | 			const btScalar ti=isolve/(btScalar)m_cfg.piterations; | |||
|  | 			for(int iseq=0;iseq<m_cfg.m_psequence.size();++iseq) | |||
|  | 			{ | |||
|  | 				getSolver(m_cfg.m_psequence[iseq])(this,1,ti); | |||
|  | 			} | |||
|  | 		} | |||
|  | 		const btScalar	vc=m_sst.isdt*(1-m_cfg.kDP); | |||
|  | 		for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			Node&	n=m_nodes[i]; | |||
|  | 			n.m_v	=	(n.m_x-n.m_q)*vc; | |||
|  | 			n.m_f	=	btVector3(0,0,0);		 | |||
|  | 		} | |||
|  | 	} | |||
|  | 	/* Solve drift			*/  | |||
|  | 	if(m_cfg.diterations>0) | |||
|  | 	{ | |||
|  | 		const btScalar	vcf=m_cfg.kVCF*m_sst.isdt; | |||
|  | 		for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			Node&	n=m_nodes[i]; | |||
|  | 			n.m_q	=	n.m_x; | |||
|  | 		} | |||
|  | 		for(int idrift=0;idrift<m_cfg.diterations;++idrift) | |||
|  | 		{ | |||
|  | 			for(int iseq=0;iseq<m_cfg.m_dsequence.size();++iseq) | |||
|  | 			{ | |||
|  | 				getSolver(m_cfg.m_dsequence[iseq])(this,1,0); | |||
|  | 			} | |||
|  | 		} | |||
|  | 		for(int i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			Node&	n=m_nodes[i]; | |||
|  | 			n.m_v	+=	(n.m_x-n.m_q)*vcf; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	/* Apply clusters		*/  | |||
|  | 	dampClusters(); | |||
|  | 	applyClusters(true); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::staticSolve(int iterations) | |||
|  | { | |||
|  | 	for(int isolve=0;isolve<iterations;++isolve) | |||
|  | 	{ | |||
|  | 		for(int iseq=0;iseq<m_cfg.m_psequence.size();++iseq) | |||
|  | 		{ | |||
|  | 			getSolver(m_cfg.m_psequence[iseq])(this,1,0); | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::solveCommonConstraints(btSoftBody** /*bodies*/,int /*count*/,int /*iterations*/) | |||
|  | { | |||
|  | 	/// placeholder
 | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::solveClusters(const btAlignedObjectArray<btSoftBody*>& bodies) | |||
|  | { | |||
|  | 	const int	nb=bodies.size(); | |||
|  | 	int			iterations=0; | |||
|  | 	int i; | |||
|  | 
 | |||
|  | 	for(i=0;i<nb;++i) | |||
|  | 	{ | |||
|  | 		iterations=btMax(iterations,bodies[i]->m_cfg.citerations); | |||
|  | 	} | |||
|  | 	for(i=0;i<nb;++i) | |||
|  | 	{ | |||
|  | 		bodies[i]->prepareClusters(iterations); | |||
|  | 	} | |||
|  | 	for(i=0;i<iterations;++i) | |||
|  | 	{ | |||
|  | 		const btScalar sor=1; | |||
|  | 		for(int j=0;j<nb;++j) | |||
|  | 		{ | |||
|  | 			bodies[j]->solveClusters(sor); | |||
|  | 		} | |||
|  | 	} | |||
|  | 	for(i=0;i<nb;++i) | |||
|  | 	{ | |||
|  | 		bodies[i]->cleanupClusters(); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::integrateMotion() | |||
|  | { | |||
|  | 	/* Update			*/  | |||
|  | 	updateNormals(); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btSoftBody::RayFromToCaster::RayFromToCaster(const btVector3& rayFrom,const btVector3& rayTo,btScalar mxt) | |||
|  | { | |||
|  | 	m_rayFrom = rayFrom; | |||
|  | 	m_rayNormalizedDirection = (rayTo-rayFrom); | |||
|  | 	m_rayTo = rayTo; | |||
|  | 	m_mint	=	mxt; | |||
|  | 	m_face	=	0; | |||
|  | 	m_tests	=	0; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::RayFromToCaster::Process(const btDbvtNode* leaf) | |||
|  | { | |||
|  | 	btSoftBody::Face&	f=*(btSoftBody::Face*)leaf->data; | |||
|  | 	const btScalar		t=rayFromToTriangle(	m_rayFrom,m_rayTo,m_rayNormalizedDirection, | |||
|  | 		f.m_n[0]->m_x, | |||
|  | 		f.m_n[1]->m_x, | |||
|  | 		f.m_n[2]->m_x, | |||
|  | 		m_mint); | |||
|  | 	if((t>0)&&(t<m_mint))  | |||
|  | 	{  | |||
|  | 		m_mint=t;m_face=&f;  | |||
|  | 	} | |||
|  | 	++m_tests; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btScalar			btSoftBody::RayFromToCaster::rayFromToTriangle(	const btVector3& rayFrom, | |||
|  | 																   const btVector3& rayTo, | |||
|  | 																   const btVector3& rayNormalizedDirection, | |||
|  | 																   const btVector3& a, | |||
|  | 																   const btVector3& b, | |||
|  | 																   const btVector3& c, | |||
|  | 																   btScalar maxt) | |||
|  | { | |||
|  | 	static const btScalar	ceps=-SIMD_EPSILON*10; | |||
|  | 	static const btScalar	teps=SIMD_EPSILON*10; | |||
|  | 
 | |||
|  | 	const btVector3			n=btCross(b-a,c-a); | |||
|  | 	const btScalar			d=btDot(a,n); | |||
|  | 	const btScalar			den=btDot(rayNormalizedDirection,n); | |||
|  | 	if(!btFuzzyZero(den)) | |||
|  | 	{ | |||
|  | 		const btScalar		num=btDot(rayFrom,n)-d; | |||
|  | 		const btScalar		t=-num/den; | |||
|  | 		if((t>teps)&&(t<maxt)) | |||
|  | 		{ | |||
|  | 			const btVector3	hit=rayFrom+rayNormalizedDirection*t; | |||
|  | 			if(	(btDot(n,btCross(a-hit,b-hit))>ceps)	&&			 | |||
|  | 				(btDot(n,btCross(b-hit,c-hit))>ceps)	&& | |||
|  | 				(btDot(n,btCross(c-hit,a-hit))>ceps)) | |||
|  | 			{ | |||
|  | 				return(t); | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return(-1); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::pointersToIndices() | |||
|  | { | |||
|  | #define	PTR2IDX(_p_,_b_)	reinterpret_cast<btSoftBody::Node*>((_p_)-(_b_))
 | |||
|  | 	btSoftBody::Node*	base=m_nodes.size() ? &m_nodes[0] : 0; | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		if(m_nodes[i].m_leaf) | |||
|  | 		{ | |||
|  | 			m_nodes[i].m_leaf->data=*(void**)&i; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_links.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		m_links[i].m_n[0]=PTR2IDX(m_links[i].m_n[0],base); | |||
|  | 		m_links[i].m_n[1]=PTR2IDX(m_links[i].m_n[1],base); | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		m_faces[i].m_n[0]=PTR2IDX(m_faces[i].m_n[0],base); | |||
|  | 		m_faces[i].m_n[1]=PTR2IDX(m_faces[i].m_n[1],base); | |||
|  | 		m_faces[i].m_n[2]=PTR2IDX(m_faces[i].m_n[2],base); | |||
|  | 		if(m_faces[i].m_leaf) | |||
|  | 		{ | |||
|  | 			m_faces[i].m_leaf->data=*(void**)&i; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_anchors.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		m_anchors[i].m_node=PTR2IDX(m_anchors[i].m_node,base); | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_notes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		for(int j=0;j<m_notes[i].m_rank;++j) | |||
|  | 		{ | |||
|  | 			m_notes[i].m_nodes[j]=PTR2IDX(m_notes[i].m_nodes[j],base); | |||
|  | 		} | |||
|  | 	} | |||
|  | #undef	PTR2IDX
 | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::indicesToPointers(const int* map) | |||
|  | { | |||
|  | #define	IDX2PTR(_p_,_b_)	map?(&(_b_)[map[(((char*)_p_)-(char*)0)]]):	\
 | |||
|  | 	(&(_b_)[(((char*)_p_)-(char*)0)]) | |||
|  | 	btSoftBody::Node*	base=m_nodes.size() ? &m_nodes[0]:0; | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		if(m_nodes[i].m_leaf) | |||
|  | 		{ | |||
|  | 			m_nodes[i].m_leaf->data=&m_nodes[i]; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_links.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		m_links[i].m_n[0]=IDX2PTR(m_links[i].m_n[0],base); | |||
|  | 		m_links[i].m_n[1]=IDX2PTR(m_links[i].m_n[1],base); | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		m_faces[i].m_n[0]=IDX2PTR(m_faces[i].m_n[0],base); | |||
|  | 		m_faces[i].m_n[1]=IDX2PTR(m_faces[i].m_n[1],base); | |||
|  | 		m_faces[i].m_n[2]=IDX2PTR(m_faces[i].m_n[2],base); | |||
|  | 		if(m_faces[i].m_leaf) | |||
|  | 		{ | |||
|  | 			m_faces[i].m_leaf->data=&m_faces[i]; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_anchors.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		m_anchors[i].m_node=IDX2PTR(m_anchors[i].m_node,base); | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_notes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		for(int j=0;j<m_notes[i].m_rank;++j) | |||
|  | 		{ | |||
|  | 			m_notes[i].m_nodes[j]=IDX2PTR(m_notes[i].m_nodes[j],base); | |||
|  | 		} | |||
|  | 	} | |||
|  | #undef	IDX2PTR
 | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | int					btSoftBody::rayTest(const btVector3& rayFrom,const btVector3& rayTo, | |||
|  | 										btScalar& mint,eFeature::_& feature,int& index,bool bcountonly) const | |||
|  | { | |||
|  | 	int	cnt=0; | |||
|  | 	btVector3 dir = rayTo-rayFrom; | |||
|  | 	 | |||
|  | 
 | |||
|  | 	if(bcountonly||m_fdbvt.empty()) | |||
|  | 	{/* Full search	*/  | |||
|  | 		 | |||
|  | 		for(int i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			const btSoftBody::Face&	f=m_faces[i]; | |||
|  | 
 | |||
|  | 			const btScalar			t=RayFromToCaster::rayFromToTriangle(	rayFrom,rayTo,dir, | |||
|  | 				f.m_n[0]->m_x, | |||
|  | 				f.m_n[1]->m_x, | |||
|  | 				f.m_n[2]->m_x, | |||
|  | 				mint); | |||
|  | 			if(t>0) | |||
|  | 			{ | |||
|  | 				++cnt; | |||
|  | 				if(!bcountonly) | |||
|  | 				{ | |||
|  | 					feature=btSoftBody::eFeature::Face; | |||
|  | 					index=i; | |||
|  | 					mint=t; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	else | |||
|  | 	{/* Use dbvt	*/  | |||
|  | 		RayFromToCaster	collider(rayFrom,rayTo,mint); | |||
|  | 
 | |||
|  | 		btDbvt::rayTest(m_fdbvt.m_root,rayFrom,rayTo,collider); | |||
|  | 		if(collider.m_face) | |||
|  | 		{ | |||
|  | 			mint=collider.m_mint; | |||
|  | 			feature=btSoftBody::eFeature::Face; | |||
|  | 			index=(int)(collider.m_face-&m_faces[0]); | |||
|  | 			cnt=1; | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for (int i=0;i<m_tetras.size();i++) | |||
|  | 	{ | |||
|  | 		const btSoftBody::Tetra& tet = m_tetras[i]; | |||
|  | 		int tetfaces[4][3] = {{0,1,2},{0,1,3},{1,2,3},{0,2,3}}; | |||
|  | 		for (int f=0;f<4;f++) | |||
|  | 		{ | |||
|  | 
 | |||
|  | 			int index0=tetfaces[f][0]; | |||
|  | 			int index1=tetfaces[f][1]; | |||
|  | 			int index2=tetfaces[f][2]; | |||
|  | 			btVector3 v0=tet.m_n[index0]->m_x; | |||
|  | 			btVector3 v1=tet.m_n[index1]->m_x; | |||
|  | 			btVector3 v2=tet.m_n[index2]->m_x; | |||
|  | 
 | |||
|  | 
 | |||
|  | 		const btScalar			t=RayFromToCaster::rayFromToTriangle(	rayFrom,rayTo,dir, | |||
|  | 			v0,v1,v2, | |||
|  | 				mint); | |||
|  | 		if(t>0) | |||
|  | 			{ | |||
|  | 				++cnt; | |||
|  | 				if(!bcountonly) | |||
|  | 				{ | |||
|  | 					feature=btSoftBody::eFeature::Tetra; | |||
|  | 					index=i; | |||
|  | 					mint=t; | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return(cnt); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::initializeFaceTree() | |||
|  | { | |||
|  | 	m_fdbvt.clear(); | |||
|  | 	for(int i=0;i<m_faces.size();++i) | |||
|  | 	{ | |||
|  | 		Face&	f=m_faces[i]; | |||
|  | 		f.m_leaf=m_fdbvt.insert(VolumeOf(f,0),&f); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btVector3		btSoftBody::evaluateCom() const | |||
|  | { | |||
|  | 	btVector3	com(0,0,0); | |||
|  | 	if(m_pose.m_bframe) | |||
|  | 	{ | |||
|  | 		for(int i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			com+=m_nodes[i].m_x*m_pose.m_wgh[i]; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return(com); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | bool				btSoftBody::checkContact(	const btCollisionObjectWrapper* colObjWrap, | |||
|  | 											 const btVector3& x, | |||
|  | 											 btScalar margin, | |||
|  | 											 btSoftBody::sCti& cti) const | |||
|  | { | |||
|  | 	btVector3 nrm; | |||
|  | 	const btCollisionShape *shp = colObjWrap->getCollisionShape(); | |||
|  | //	const btRigidBody *tmpRigid = btRigidBody::upcast(colObjWrap->getCollisionObject());
 | |||
|  | 	//const btTransform &wtr = tmpRigid ? tmpRigid->getWorldTransform() : colObjWrap->getWorldTransform();
 | |||
|  | 	const btTransform &wtr = colObjWrap->getWorldTransform(); | |||
|  | 	//todo: check which transform is needed here
 | |||
|  | 
 | |||
|  | 	btScalar dst =  | |||
|  | 		m_worldInfo->m_sparsesdf.Evaluate(	 | |||
|  | 			wtr.invXform(x), | |||
|  | 			shp, | |||
|  | 			nrm, | |||
|  | 			margin); | |||
|  | 	if(dst<0) | |||
|  | 	{ | |||
|  | 		cti.m_colObj = colObjWrap->getCollisionObject(); | |||
|  | 		cti.m_normal = wtr.getBasis()*nrm; | |||
|  | 		cti.m_offset = -btDot( cti.m_normal, x - cti.m_normal * dst ); | |||
|  | 		return(true); | |||
|  | 	} | |||
|  | 	return(false); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::updateNormals() | |||
|  | { | |||
|  | 
 | |||
|  | 	const btVector3	zv(0,0,0); | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		m_nodes[i].m_n=zv; | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		btSoftBody::Face&	f=m_faces[i]; | |||
|  | 		const btVector3		n=btCross(f.m_n[1]->m_x-f.m_n[0]->m_x, | |||
|  | 			f.m_n[2]->m_x-f.m_n[0]->m_x); | |||
|  | 		f.m_normal=n.normalized(); | |||
|  | 		f.m_n[0]->m_n+=n; | |||
|  | 		f.m_n[1]->m_n+=n; | |||
|  | 		f.m_n[2]->m_n+=n; | |||
|  | 	} | |||
|  | 	for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		btScalar len = m_nodes[i].m_n.length(); | |||
|  | 		if (len>SIMD_EPSILON) | |||
|  | 			m_nodes[i].m_n /= len; | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::updateBounds() | |||
|  | { | |||
|  | 	/*if( m_acceleratedSoftBody )
 | |||
|  | 	{ | |||
|  | 		// If we have an accelerated softbody we need to obtain the bounds correctly
 | |||
|  | 		// For now (slightly hackily) just have a very large AABB
 | |||
|  | 		// TODO: Write get bounds kernel
 | |||
|  | 		// If that is updating in place, atomic collisions might be low (when the cloth isn't perfectly aligned to an axis) and we could
 | |||
|  | 		// probably do a test and exchange reasonably efficiently.
 | |||
|  | 
 | |||
|  | 		m_bounds[0] = btVector3(-1000, -1000, -1000); | |||
|  | 		m_bounds[1] = btVector3(1000, 1000, 1000); | |||
|  | 
 | |||
|  | 	} else {*/ | |||
|  | 		if(m_ndbvt.m_root) | |||
|  | 		{ | |||
|  | 			const btVector3&	mins=m_ndbvt.m_root->volume.Mins(); | |||
|  | 			const btVector3&	maxs=m_ndbvt.m_root->volume.Maxs(); | |||
|  | 			const btScalar		csm=getCollisionShape()->getMargin(); | |||
|  | 			const btVector3		mrg=btVector3(	csm, | |||
|  | 				csm, | |||
|  | 				csm)*1; // ??? to investigate...
 | |||
|  | 			m_bounds[0]=mins-mrg; | |||
|  | 			m_bounds[1]=maxs+mrg; | |||
|  | 			if(0!=getBroadphaseHandle()) | |||
|  | 			{					 | |||
|  | 				m_worldInfo->m_broadphase->setAabb(	getBroadphaseHandle(), | |||
|  | 					m_bounds[0], | |||
|  | 					m_bounds[1], | |||
|  | 					m_worldInfo->m_dispatcher); | |||
|  | 			} | |||
|  | 		} | |||
|  | 		else | |||
|  | 		{ | |||
|  | 			m_bounds[0]= | |||
|  | 				m_bounds[1]=btVector3(0,0,0); | |||
|  | 		}		 | |||
|  | 	//}
 | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::updatePose() | |||
|  | { | |||
|  | 	if(m_pose.m_bframe) | |||
|  | 	{ | |||
|  | 		btSoftBody::Pose&	pose=m_pose; | |||
|  | 		const btVector3		com=evaluateCom(); | |||
|  | 		/* Com			*/  | |||
|  | 		pose.m_com	=	com; | |||
|  | 		/* Rotation		*/  | |||
|  | 		btMatrix3x3		Apq; | |||
|  | 		const btScalar	eps=SIMD_EPSILON; | |||
|  | 		Apq[0]=Apq[1]=Apq[2]=btVector3(0,0,0); | |||
|  | 		Apq[0].setX(eps);Apq[1].setY(eps*2);Apq[2].setZ(eps*3); | |||
|  | 		for(int i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			const btVector3		a=pose.m_wgh[i]*(m_nodes[i].m_x-com); | |||
|  | 			const btVector3&	b=pose.m_pos[i]; | |||
|  | 			Apq[0]+=a.x()*b; | |||
|  | 			Apq[1]+=a.y()*b; | |||
|  | 			Apq[2]+=a.z()*b; | |||
|  | 		} | |||
|  | 		btMatrix3x3		r,s; | |||
|  | 		PolarDecompose(Apq,r,s); | |||
|  | 		pose.m_rot=r; | |||
|  | 		pose.m_scl=pose.m_aqq*r.transpose()*Apq; | |||
|  | 		if(m_cfg.maxvolume>1) | |||
|  | 		{ | |||
|  | 			const btScalar	idet=Clamp<btScalar>(	1/pose.m_scl.determinant(), | |||
|  | 				1,m_cfg.maxvolume); | |||
|  | 			pose.m_scl=Mul(pose.m_scl,idet); | |||
|  | 		} | |||
|  | 
 | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::updateArea(bool averageArea) | |||
|  | { | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	/* Face area		*/  | |||
|  | 	for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Face&		f=m_faces[i]; | |||
|  | 		f.m_ra	=	AreaOf(f.m_n[0]->m_x,f.m_n[1]->m_x,f.m_n[2]->m_x); | |||
|  | 	} | |||
|  | 	 | |||
|  | 	/* Node area		*/  | |||
|  | 
 | |||
|  | 	if (averageArea) | |||
|  | 	{ | |||
|  | 		btAlignedObjectArray<int>	counts; | |||
|  | 		counts.resize(m_nodes.size(),0); | |||
|  | 		for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			m_nodes[i].m_area	=	0; | |||
|  | 		} | |||
|  | 		for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			btSoftBody::Face&	f=m_faces[i]; | |||
|  | 			for(int j=0;j<3;++j) | |||
|  | 			{ | |||
|  | 				const int index=(int)(f.m_n[j]-&m_nodes[0]); | |||
|  | 				counts[index]++; | |||
|  | 				f.m_n[j]->m_area+=btFabs(f.m_ra); | |||
|  | 			} | |||
|  | 		} | |||
|  | 		for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			if(counts[i]>0) | |||
|  | 				m_nodes[i].m_area/=(btScalar)counts[i]; | |||
|  | 			else | |||
|  | 				m_nodes[i].m_area=0; | |||
|  | 		} | |||
|  | 	} | |||
|  | 	else | |||
|  | 	{ | |||
|  | 		// initialize node area as zero
 | |||
|  | 		for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			m_nodes[i].m_area=0;	 | |||
|  | 		} | |||
|  | 
 | |||
|  | 		for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			btSoftBody::Face&	f=m_faces[i]; | |||
|  | 
 | |||
|  | 			for(int j=0;j<3;++j) | |||
|  | 			{ | |||
|  | 				f.m_n[j]->m_area += f.m_ra; | |||
|  | 			} | |||
|  | 		} | |||
|  | 
 | |||
|  | 		for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 		{ | |||
|  | 			m_nodes[i].m_area *= 0.3333333f; | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | void				btSoftBody::updateLinkConstants() | |||
|  | {	 | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	/* Links		*/  | |||
|  | 	for(i=0,ni=m_links.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		Link&		l=m_links[i]; | |||
|  | 		Material&	m=*l.m_material; | |||
|  | 		l.m_c0	=	(l.m_n[0]->m_im+l.m_n[1]->m_im)/m.m_kLST; | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | void				btSoftBody::updateConstants() | |||
|  | { | |||
|  | 	resetLinkRestLengths(); | |||
|  | 	updateLinkConstants(); | |||
|  | 	updateArea(); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::initializeClusters() | |||
|  | { | |||
|  | 	int i; | |||
|  | 
 | |||
|  | 	for( i=0;i<m_clusters.size();++i) | |||
|  | 	{ | |||
|  | 		Cluster&	c=*m_clusters[i]; | |||
|  | 		c.m_imass=0; | |||
|  | 		c.m_masses.resize(c.m_nodes.size()); | |||
|  | 		for(int j=0;j<c.m_nodes.size();++j) | |||
|  | 		{ | |||
|  | 			if (c.m_nodes[j]->m_im==0) | |||
|  | 			{ | |||
|  | 				c.m_containsAnchor = true; | |||
|  | 				c.m_masses[j]	=	BT_LARGE_FLOAT; | |||
|  | 			} else | |||
|  | 			{ | |||
|  | 				c.m_masses[j]	=	btScalar(1.)/c.m_nodes[j]->m_im; | |||
|  | 			} | |||
|  | 			c.m_imass		+=	c.m_masses[j]; | |||
|  | 		} | |||
|  | 		c.m_imass		=	btScalar(1.)/c.m_imass; | |||
|  | 		c.m_com			=	btSoftBody::clusterCom(&c); | |||
|  | 		c.m_lv			=	btVector3(0,0,0); | |||
|  | 		c.m_av			=	btVector3(0,0,0); | |||
|  | 		c.m_leaf		=	0; | |||
|  | 		/* Inertia	*/  | |||
|  | 		btMatrix3x3&	ii=c.m_locii; | |||
|  | 		ii[0]=ii[1]=ii[2]=btVector3(0,0,0); | |||
|  | 		{ | |||
|  | 			int i,ni; | |||
|  | 
 | |||
|  | 			for(i=0,ni=c.m_nodes.size();i<ni;++i) | |||
|  | 			{ | |||
|  | 				const btVector3	k=c.m_nodes[i]->m_x-c.m_com; | |||
|  | 				const btVector3	q=k*k; | |||
|  | 				const btScalar	m=c.m_masses[i]; | |||
|  | 				ii[0][0]	+=	m*(q[1]+q[2]); | |||
|  | 				ii[1][1]	+=	m*(q[0]+q[2]); | |||
|  | 				ii[2][2]	+=	m*(q[0]+q[1]); | |||
|  | 				ii[0][1]	-=	m*k[0]*k[1]; | |||
|  | 				ii[0][2]	-=	m*k[0]*k[2]; | |||
|  | 				ii[1][2]	-=	m*k[1]*k[2]; | |||
|  | 			} | |||
|  | 		} | |||
|  | 		ii[1][0]=ii[0][1]; | |||
|  | 		ii[2][0]=ii[0][2]; | |||
|  | 		ii[2][1]=ii[1][2]; | |||
|  | 		 | |||
|  | 		ii = ii.inverse(); | |||
|  | 
 | |||
|  | 		/* Frame	*/  | |||
|  | 		c.m_framexform.setIdentity(); | |||
|  | 		c.m_framexform.setOrigin(c.m_com); | |||
|  | 		c.m_framerefs.resize(c.m_nodes.size()); | |||
|  | 		{ | |||
|  | 			int i; | |||
|  | 			for(i=0;i<c.m_framerefs.size();++i) | |||
|  | 			{ | |||
|  | 				c.m_framerefs[i]=c.m_nodes[i]->m_x-c.m_com; | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::updateClusters() | |||
|  | { | |||
|  | 	BT_PROFILE("UpdateClusters"); | |||
|  | 	int i; | |||
|  | 
 | |||
|  | 	for(i=0;i<m_clusters.size();++i) | |||
|  | 	{ | |||
|  | 		btSoftBody::Cluster&	c=*m_clusters[i]; | |||
|  | 		const int				n=c.m_nodes.size(); | |||
|  | 		//const btScalar			invn=1/(btScalar)n;
 | |||
|  | 		if(n) | |||
|  | 		{ | |||
|  | 			/* Frame				*/  | |||
|  | 			const btScalar	eps=btScalar(0.0001); | |||
|  | 			btMatrix3x3		m,r,s; | |||
|  | 			m[0]=m[1]=m[2]=btVector3(0,0,0); | |||
|  | 			m[0][0]=eps*1; | |||
|  | 			m[1][1]=eps*2; | |||
|  | 			m[2][2]=eps*3; | |||
|  | 			c.m_com=clusterCom(&c); | |||
|  | 			for(int i=0;i<c.m_nodes.size();++i) | |||
|  | 			{ | |||
|  | 				const btVector3		a=c.m_nodes[i]->m_x-c.m_com; | |||
|  | 				const btVector3&	b=c.m_framerefs[i]; | |||
|  | 				m[0]+=a[0]*b;m[1]+=a[1]*b;m[2]+=a[2]*b; | |||
|  | 			} | |||
|  | 			PolarDecompose(m,r,s); | |||
|  | 			c.m_framexform.setOrigin(c.m_com); | |||
|  | 			c.m_framexform.setBasis(r);		 | |||
|  | 			/* Inertia			*/  | |||
|  | #if 1/* Constant	*/ 
 | |||
|  | 			c.m_invwi=c.m_framexform.getBasis()*c.m_locii*c.m_framexform.getBasis().transpose(); | |||
|  | #else
 | |||
|  | #if 0/* Sphere	*/ 
 | |||
|  | 			const btScalar	rk=(2*c.m_extents.length2())/(5*c.m_imass); | |||
|  | 			const btVector3	inertia(rk,rk,rk); | |||
|  | 			const btVector3	iin(btFabs(inertia[0])>SIMD_EPSILON?1/inertia[0]:0, | |||
|  | 				btFabs(inertia[1])>SIMD_EPSILON?1/inertia[1]:0, | |||
|  | 				btFabs(inertia[2])>SIMD_EPSILON?1/inertia[2]:0); | |||
|  | 
 | |||
|  | 			c.m_invwi=c.m_xform.getBasis().scaled(iin)*c.m_xform.getBasis().transpose(); | |||
|  | #else/* Actual	*/ 		
 | |||
|  | 			c.m_invwi[0]=c.m_invwi[1]=c.m_invwi[2]=btVector3(0,0,0); | |||
|  | 			for(int i=0;i<n;++i) | |||
|  | 			{ | |||
|  | 				const btVector3	k=c.m_nodes[i]->m_x-c.m_com; | |||
|  | 				const btVector3		q=k*k; | |||
|  | 				const btScalar		m=1/c.m_nodes[i]->m_im; | |||
|  | 				c.m_invwi[0][0]	+=	m*(q[1]+q[2]); | |||
|  | 				c.m_invwi[1][1]	+=	m*(q[0]+q[2]); | |||
|  | 				c.m_invwi[2][2]	+=	m*(q[0]+q[1]); | |||
|  | 				c.m_invwi[0][1]	-=	m*k[0]*k[1]; | |||
|  | 				c.m_invwi[0][2]	-=	m*k[0]*k[2]; | |||
|  | 				c.m_invwi[1][2]	-=	m*k[1]*k[2]; | |||
|  | 			} | |||
|  | 			c.m_invwi[1][0]=c.m_invwi[0][1]; | |||
|  | 			c.m_invwi[2][0]=c.m_invwi[0][2]; | |||
|  | 			c.m_invwi[2][1]=c.m_invwi[1][2]; | |||
|  | 			c.m_invwi=c.m_invwi.inverse(); | |||
|  | #endif
 | |||
|  | #endif
 | |||
|  | 			/* Velocities			*/  | |||
|  | 			c.m_lv=btVector3(0,0,0); | |||
|  | 			c.m_av=btVector3(0,0,0); | |||
|  | 			{ | |||
|  | 				int i; | |||
|  | 
 | |||
|  | 				for(i=0;i<n;++i) | |||
|  | 				{ | |||
|  | 					const btVector3	v=c.m_nodes[i]->m_v*c.m_masses[i]; | |||
|  | 					c.m_lv	+=	v; | |||
|  | 					c.m_av	+=	btCross(c.m_nodes[i]->m_x-c.m_com,v); | |||
|  | 				} | |||
|  | 			} | |||
|  | 			c.m_lv=c.m_imass*c.m_lv*(1-c.m_ldamping); | |||
|  | 			c.m_av=c.m_invwi*c.m_av*(1-c.m_adamping); | |||
|  | 			c.m_vimpulses[0]	= | |||
|  | 				c.m_vimpulses[1]	= btVector3(0,0,0); | |||
|  | 			c.m_dimpulses[0]	= | |||
|  | 				c.m_dimpulses[1]	= btVector3(0,0,0); | |||
|  | 			c.m_nvimpulses		= 0; | |||
|  | 			c.m_ndimpulses		= 0; | |||
|  | 			/* Matching				*/  | |||
|  | 			if(c.m_matching>0) | |||
|  | 			{ | |||
|  | 				for(int j=0;j<c.m_nodes.size();++j) | |||
|  | 				{ | |||
|  | 					Node&			n=*c.m_nodes[j]; | |||
|  | 					const btVector3	x=c.m_framexform*c.m_framerefs[j]; | |||
|  | 					n.m_x=Lerp(n.m_x,x,c.m_matching); | |||
|  | 				} | |||
|  | 			}			 | |||
|  | 			/* Dbvt					*/  | |||
|  | 			if(c.m_collide) | |||
|  | 			{ | |||
|  | 				btVector3	mi=c.m_nodes[0]->m_x; | |||
|  | 				btVector3	mx=mi; | |||
|  | 				for(int j=1;j<n;++j) | |||
|  | 				{ | |||
|  | 					mi.setMin(c.m_nodes[j]->m_x); | |||
|  | 					mx.setMax(c.m_nodes[j]->m_x); | |||
|  | 				}			 | |||
|  | 				ATTRIBUTE_ALIGNED16(btDbvtVolume)	bounds=btDbvtVolume::FromMM(mi,mx); | |||
|  | 				if(c.m_leaf) | |||
|  | 					m_cdbvt.update(c.m_leaf,bounds,c.m_lv*m_sst.sdt*3,m_sst.radmrg); | |||
|  | 				else | |||
|  | 					c.m_leaf=m_cdbvt.insert(bounds,&c); | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 
 | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::cleanupClusters() | |||
|  | { | |||
|  | 	for(int i=0;i<m_joints.size();++i) | |||
|  | 	{ | |||
|  | 		m_joints[i]->Terminate(m_sst.sdt); | |||
|  | 		if(m_joints[i]->m_delete) | |||
|  | 		{ | |||
|  | 			btAlignedFree(m_joints[i]); | |||
|  | 			m_joints.remove(m_joints[i--]); | |||
|  | 		}	 | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::prepareClusters(int iterations) | |||
|  | { | |||
|  | 	for(int i=0;i<m_joints.size();++i) | |||
|  | 	{ | |||
|  | 		m_joints[i]->Prepare(m_sst.sdt,iterations); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::solveClusters(btScalar sor) | |||
|  | { | |||
|  | 	for(int i=0,ni=m_joints.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		m_joints[i]->Solve(m_sst.sdt,sor); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::applyClusters(bool drift) | |||
|  | { | |||
|  | 	BT_PROFILE("ApplyClusters"); | |||
|  | //	const btScalar					f0=m_sst.sdt;
 | |||
|  | 	//const btScalar					f1=f0/2;
 | |||
|  | 	btAlignedObjectArray<btVector3> deltas; | |||
|  | 	btAlignedObjectArray<btScalar> weights; | |||
|  | 	deltas.resize(m_nodes.size(),btVector3(0,0,0)); | |||
|  | 	weights.resize(m_nodes.size(),0); | |||
|  | 	int i; | |||
|  | 
 | |||
|  | 	if(drift) | |||
|  | 	{ | |||
|  | 		for(i=0;i<m_clusters.size();++i) | |||
|  | 		{ | |||
|  | 			Cluster&	c=*m_clusters[i]; | |||
|  | 			if(c.m_ndimpulses) | |||
|  | 			{ | |||
|  | 				c.m_dimpulses[0]/=(btScalar)c.m_ndimpulses; | |||
|  | 				c.m_dimpulses[1]/=(btScalar)c.m_ndimpulses; | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	 | |||
|  | 	for(i=0;i<m_clusters.size();++i) | |||
|  | 	{ | |||
|  | 		Cluster&	c=*m_clusters[i];	 | |||
|  | 		if(0<(drift?c.m_ndimpulses:c.m_nvimpulses)) | |||
|  | 		{ | |||
|  | 			const btVector3		v=(drift?c.m_dimpulses[0]:c.m_vimpulses[0])*m_sst.sdt; | |||
|  | 			const btVector3		w=(drift?c.m_dimpulses[1]:c.m_vimpulses[1])*m_sst.sdt; | |||
|  | 			for(int j=0;j<c.m_nodes.size();++j) | |||
|  | 			{ | |||
|  | 				const int			idx=int(c.m_nodes[j]-&m_nodes[0]); | |||
|  | 				const btVector3&	x=c.m_nodes[j]->m_x; | |||
|  | 				const btScalar		q=c.m_masses[j]; | |||
|  | 				deltas[idx]		+=	(v+btCross(w,x-c.m_com))*q; | |||
|  | 				weights[idx]	+=	q; | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	for(i=0;i<deltas.size();++i) | |||
|  | 	{ | |||
|  | 		if(weights[i]>0)  | |||
|  | 		{ | |||
|  | 			m_nodes[i].m_x+=deltas[i]/weights[i]; | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void					btSoftBody::dampClusters() | |||
|  | { | |||
|  | 	int i; | |||
|  | 
 | |||
|  | 	for(i=0;i<m_clusters.size();++i) | |||
|  | 	{ | |||
|  | 		Cluster&	c=*m_clusters[i];	 | |||
|  | 		if(c.m_ndamping>0) | |||
|  | 		{ | |||
|  | 			for(int j=0;j<c.m_nodes.size();++j) | |||
|  | 			{ | |||
|  | 				Node&			n=*c.m_nodes[j]; | |||
|  | 				if(n.m_im>0) | |||
|  | 				{ | |||
|  | 					const btVector3	vx=c.m_lv+btCross(c.m_av,c.m_nodes[j]->m_q-c.m_com); | |||
|  | 					if(vx.length2()<=n.m_v.length2()) | |||
|  | 						{ | |||
|  | 						n.m_v	+=	c.m_ndamping*(vx-n.m_v); | |||
|  | 						} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::Joint::Prepare(btScalar dt,int) | |||
|  | { | |||
|  | 	m_bodies[0].activate(); | |||
|  | 	m_bodies[1].activate(); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::LJoint::Prepare(btScalar dt,int iterations) | |||
|  | { | |||
|  | 	static const btScalar	maxdrift=4; | |||
|  | 	Joint::Prepare(dt,iterations); | |||
|  | 	m_rpos[0]		=	m_bodies[0].xform()*m_refs[0]; | |||
|  | 	m_rpos[1]		=	m_bodies[1].xform()*m_refs[1]; | |||
|  | 	m_drift			=	Clamp(m_rpos[0]-m_rpos[1],maxdrift)*m_erp/dt; | |||
|  | 	m_rpos[0]		-=	m_bodies[0].xform().getOrigin(); | |||
|  | 	m_rpos[1]		-=	m_bodies[1].xform().getOrigin(); | |||
|  | 	m_massmatrix	=	ImpulseMatrix(	m_bodies[0].invMass(),m_bodies[0].invWorldInertia(),m_rpos[0], | |||
|  | 		m_bodies[1].invMass(),m_bodies[1].invWorldInertia(),m_rpos[1]); | |||
|  | 	if(m_split>0) | |||
|  | 	{ | |||
|  | 		m_sdrift	=	m_massmatrix*(m_drift*m_split); | |||
|  | 		m_drift		*=	1-m_split; | |||
|  | 	} | |||
|  | 	m_drift	/=(btScalar)iterations; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::LJoint::Solve(btScalar dt,btScalar sor) | |||
|  | { | |||
|  | 	const btVector3		va=m_bodies[0].velocity(m_rpos[0]); | |||
|  | 	const btVector3		vb=m_bodies[1].velocity(m_rpos[1]); | |||
|  | 	const btVector3		vr=va-vb; | |||
|  | 	btSoftBody::Impulse	impulse; | |||
|  | 	impulse.m_asVelocity	=	1; | |||
|  | 	impulse.m_velocity		=	m_massmatrix*(m_drift+vr*m_cfm)*sor; | |||
|  | 	m_bodies[0].applyImpulse(-impulse,m_rpos[0]); | |||
|  | 	m_bodies[1].applyImpulse( impulse,m_rpos[1]); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::LJoint::Terminate(btScalar dt) | |||
|  | { | |||
|  | 	if(m_split>0) | |||
|  | 	{ | |||
|  | 		m_bodies[0].applyDImpulse(-m_sdrift,m_rpos[0]); | |||
|  | 		m_bodies[1].applyDImpulse( m_sdrift,m_rpos[1]); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::AJoint::Prepare(btScalar dt,int iterations) | |||
|  | { | |||
|  | 	static const btScalar	maxdrift=SIMD_PI/16; | |||
|  | 	m_icontrol->Prepare(this); | |||
|  | 	Joint::Prepare(dt,iterations); | |||
|  | 	m_axis[0]	=	m_bodies[0].xform().getBasis()*m_refs[0]; | |||
|  | 	m_axis[1]	=	m_bodies[1].xform().getBasis()*m_refs[1]; | |||
|  | 	m_drift		=	NormalizeAny(btCross(m_axis[1],m_axis[0])); | |||
|  | 	m_drift		*=	btMin(maxdrift,btAcos(Clamp<btScalar>(btDot(m_axis[0],m_axis[1]),-1,+1))); | |||
|  | 	m_drift		*=	m_erp/dt; | |||
|  | 	m_massmatrix=	AngularImpulseMatrix(m_bodies[0].invWorldInertia(),m_bodies[1].invWorldInertia()); | |||
|  | 	if(m_split>0) | |||
|  | 	{ | |||
|  | 		m_sdrift	=	m_massmatrix*(m_drift*m_split); | |||
|  | 		m_drift		*=	1-m_split; | |||
|  | 	} | |||
|  | 	m_drift	/=(btScalar)iterations; | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::AJoint::Solve(btScalar dt,btScalar sor) | |||
|  | { | |||
|  | 	const btVector3		va=m_bodies[0].angularVelocity(); | |||
|  | 	const btVector3		vb=m_bodies[1].angularVelocity(); | |||
|  | 	const btVector3		vr=va-vb; | |||
|  | 	const btScalar		sp=btDot(vr,m_axis[0]); | |||
|  | 	const btVector3		vc=vr-m_axis[0]*m_icontrol->Speed(this,sp); | |||
|  | 	btSoftBody::Impulse	impulse; | |||
|  | 	impulse.m_asVelocity	=	1; | |||
|  | 	impulse.m_velocity		=	m_massmatrix*(m_drift+vc*m_cfm)*sor; | |||
|  | 	m_bodies[0].applyAImpulse(-impulse); | |||
|  | 	m_bodies[1].applyAImpulse( impulse); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::AJoint::Terminate(btScalar dt) | |||
|  | { | |||
|  | 	if(m_split>0) | |||
|  | 	{ | |||
|  | 		m_bodies[0].applyDAImpulse(-m_sdrift); | |||
|  | 		m_bodies[1].applyDAImpulse( m_sdrift); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::CJoint::Prepare(btScalar dt,int iterations) | |||
|  | { | |||
|  | 	Joint::Prepare(dt,iterations); | |||
|  | 	const bool	dodrift=(m_life==0); | |||
|  | 	m_delete=(++m_life)>m_maxlife; | |||
|  | 	if(dodrift) | |||
|  | 	{ | |||
|  | 		m_drift=m_drift*m_erp/dt; | |||
|  | 		if(m_split>0) | |||
|  | 		{ | |||
|  | 			m_sdrift	=	m_massmatrix*(m_drift*m_split); | |||
|  | 			m_drift		*=	1-m_split; | |||
|  | 		} | |||
|  | 		m_drift/=(btScalar)iterations; | |||
|  | 	} | |||
|  | 	else | |||
|  | 	{ | |||
|  | 		m_drift=m_sdrift=btVector3(0,0,0); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::CJoint::Solve(btScalar dt,btScalar sor) | |||
|  | { | |||
|  | 	const btVector3		va=m_bodies[0].velocity(m_rpos[0]); | |||
|  | 	const btVector3		vb=m_bodies[1].velocity(m_rpos[1]); | |||
|  | 	const btVector3		vrel=va-vb; | |||
|  | 	const btScalar		rvac=btDot(vrel,m_normal); | |||
|  | 	btSoftBody::Impulse	impulse; | |||
|  | 	impulse.m_asVelocity	=	1; | |||
|  | 	impulse.m_velocity		=	m_drift; | |||
|  | 	if(rvac<0) | |||
|  | 	{ | |||
|  | 		const btVector3	iv=m_normal*rvac; | |||
|  | 		const btVector3	fv=vrel-iv; | |||
|  | 		impulse.m_velocity	+=	iv+fv*m_friction; | |||
|  | 	} | |||
|  | 	impulse.m_velocity=m_massmatrix*impulse.m_velocity*sor; | |||
|  | 	 | |||
|  | 	if (m_bodies[0].m_soft==m_bodies[1].m_soft) | |||
|  | 	{ | |||
|  | 		if ((impulse.m_velocity.getX() ==impulse.m_velocity.getX())&&(impulse.m_velocity.getY() ==impulse.m_velocity.getY())&& | |||
|  | 			(impulse.m_velocity.getZ() ==impulse.m_velocity.getZ())) | |||
|  | 		{ | |||
|  | 			if (impulse.m_asVelocity) | |||
|  | 			{ | |||
|  | 				if (impulse.m_velocity.length() <m_bodies[0].m_soft->m_maxSelfCollisionImpulse) | |||
|  | 				{ | |||
|  | 					 | |||
|  | 				} else | |||
|  | 				{ | |||
|  | 					m_bodies[0].applyImpulse(-impulse*m_bodies[0].m_soft->m_selfCollisionImpulseFactor,m_rpos[0]); | |||
|  | 					m_bodies[1].applyImpulse( impulse*m_bodies[0].m_soft->m_selfCollisionImpulseFactor,m_rpos[1]); | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} else | |||
|  | 	{ | |||
|  | 		m_bodies[0].applyImpulse(-impulse,m_rpos[0]); | |||
|  | 		m_bodies[1].applyImpulse( impulse,m_rpos[1]); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::CJoint::Terminate(btScalar dt) | |||
|  | { | |||
|  | 	if(m_split>0) | |||
|  | 	{ | |||
|  | 		m_bodies[0].applyDImpulse(-m_sdrift,m_rpos[0]); | |||
|  | 		m_bodies[1].applyDImpulse( m_sdrift,m_rpos[1]); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::applyForces() | |||
|  | { | |||
|  | 
 | |||
|  | 	BT_PROFILE("SoftBody applyForces"); | |||
|  | //	const btScalar					dt =			m_sst.sdt;
 | |||
|  | 	const btScalar					kLF =			m_cfg.kLF; | |||
|  | 	const btScalar					kDG =			m_cfg.kDG; | |||
|  | 	const btScalar					kPR =			m_cfg.kPR; | |||
|  | 	const btScalar					kVC =			m_cfg.kVC; | |||
|  | 	const bool						as_lift =		kLF>0; | |||
|  | 	const bool						as_drag =		kDG>0; | |||
|  | 	const bool						as_pressure =	kPR!=0; | |||
|  | 	const bool						as_volume =		kVC>0; | |||
|  | 	const bool						as_aero =		as_lift	|| | |||
|  | 													as_drag		; | |||
|  | 	//const bool						as_vaero =		as_aero	&&
 | |||
|  | 	//												(m_cfg.aeromodel < btSoftBody::eAeroModel::F_TwoSided);
 | |||
|  | 	//const bool						as_faero =		as_aero	&&
 | |||
|  | 	//												(m_cfg.aeromodel >= btSoftBody::eAeroModel::F_TwoSided);
 | |||
|  | 	const bool						use_medium =	as_aero; | |||
|  | 	const bool						use_volume =	as_pressure	|| | |||
|  | 		as_volume	; | |||
|  | 	btScalar						volume =		0; | |||
|  | 	btScalar						ivolumetp =		0; | |||
|  | 	btScalar						dvolumetv =		0; | |||
|  | 	btSoftBody::sMedium	medium; | |||
|  | 	if(use_volume) | |||
|  | 	{ | |||
|  | 		volume		=	getVolume(); | |||
|  | 		ivolumetp	=	1/btFabs(volume)*kPR; | |||
|  | 		dvolumetv	=	(m_pose.m_volume-volume)*kVC; | |||
|  | 	} | |||
|  | 	/* Per vertex forces			*/  | |||
|  | 	int i,ni; | |||
|  | 
 | |||
|  | 	for(i=0,ni=m_nodes.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		btSoftBody::Node&	n=m_nodes[i]; | |||
|  | 		if(n.m_im>0) | |||
|  | 		{ | |||
|  | 			if(use_medium) | |||
|  | 			{ | |||
|  | 				/* Aerodynamics			*/  | |||
|  | 				addAeroForceToNode(m_windVelocity, i); | |||
|  | 			} | |||
|  | 			/* Pressure				*/  | |||
|  | 			if(as_pressure) | |||
|  | 			{ | |||
|  | 				n.m_f	+=	n.m_n*(n.m_area*ivolumetp); | |||
|  | 			} | |||
|  | 			/* Volume				*/  | |||
|  | 			if(as_volume) | |||
|  | 			{ | |||
|  | 				n.m_f	+=	n.m_n*(n.m_area*dvolumetv); | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/* Per face forces				*/  | |||
|  | 	for(i=0,ni=m_faces.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 	//	btSoftBody::Face&	f=m_faces[i];
 | |||
|  | 
 | |||
|  | 		/* Aerodynamics			*/  | |||
|  | 		addAeroForceToFace(m_windVelocity, i);	 | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::PSolve_Anchors(btSoftBody* psb,btScalar kst,btScalar ti) | |||
|  | { | |||
|  | 	BT_PROFILE("PSolve_Anchors"); | |||
|  | 	const btScalar	kAHR=psb->m_cfg.kAHR*kst; | |||
|  | 	const btScalar	dt=psb->m_sst.sdt; | |||
|  | 	for(int i=0,ni=psb->m_anchors.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		const Anchor&		a=psb->m_anchors[i]; | |||
|  | 		const btTransform&	t=a.m_body->getWorldTransform(); | |||
|  | 		Node&				n=*a.m_node; | |||
|  | 		const btVector3		wa=t*a.m_local; | |||
|  | 		const btVector3		va=a.m_body->getVelocityInLocalPoint(a.m_c1)*dt; | |||
|  | 		const btVector3		vb=n.m_x-n.m_q; | |||
|  | 		const btVector3		vr=(va-vb)+(wa-n.m_x)*kAHR; | |||
|  | 		const btVector3		impulse=a.m_c0*vr*a.m_influence; | |||
|  | 		n.m_x+=impulse*a.m_c2; | |||
|  | 		a.m_body->applyImpulse(-impulse,a.m_c1); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | //
 | |||
|  | void btSoftBody::PSolve_RContacts(btSoftBody* psb, btScalar kst, btScalar ti) | |||
|  | { | |||
|  | 	BT_PROFILE("PSolve_RContacts"); | |||
|  | 	const btScalar	dt = psb->m_sst.sdt; | |||
|  | 	const btScalar	mrg = psb->getCollisionShape()->getMargin(); | |||
|  | 	btMultiBodyJacobianData jacobianData; | |||
|  | 	for(int i=0,ni=psb->m_rcontacts.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		const RContact&		c = psb->m_rcontacts[i]; | |||
|  | 		const sCti&			cti = c.m_cti;	 | |||
|  | 		if (cti.m_colObj->hasContactResponse())  | |||
|  | 		{ | |||
|  |             btVector3 va(0,0,0); | |||
|  |             btRigidBody* rigidCol=0; | |||
|  |             btMultiBodyLinkCollider* multibodyLinkCol=0; | |||
|  |             btScalar* deltaV; | |||
|  |              | |||
|  |             if (cti.m_colObj->getInternalType() == btCollisionObject::CO_RIGID_BODY) | |||
|  |             { | |||
|  |                 rigidCol = (btRigidBody*)btRigidBody::upcast(cti.m_colObj); | |||
|  |                 va = rigidCol ? rigidCol->getVelocityInLocalPoint(c.m_c1)*dt : btVector3(0,0,0); | |||
|  |             } | |||
|  | 			else if (cti.m_colObj->getInternalType() == btCollisionObject::CO_FEATHERSTONE_LINK) | |||
|  |             { | |||
|  |                 multibodyLinkCol = (btMultiBodyLinkCollider*)btMultiBodyLinkCollider::upcast(cti.m_colObj); | |||
|  |                 if (multibodyLinkCol) | |||
|  |                 { | |||
|  |                     const int ndof  = multibodyLinkCol->m_multiBody->getNumDofs() + 6; | |||
|  |                     jacobianData.m_jacobians.resize(ndof); | |||
|  |                     jacobianData.m_deltaVelocitiesUnitImpulse.resize(ndof); | |||
|  |                     btScalar* jac=&jacobianData.m_jacobians[0]; | |||
|  |                      | |||
|  |                     multibodyLinkCol->m_multiBody->fillContactJacobianMultiDof(multibodyLinkCol->m_link, c.m_node->m_x, cti.m_normal, jac, jacobianData.scratch_r, jacobianData.scratch_v, jacobianData.scratch_m); | |||
|  |                     deltaV = &jacobianData.m_deltaVelocitiesUnitImpulse[0]; | |||
|  |                     multibodyLinkCol->m_multiBody->calcAccelerationDeltasMultiDof(&jacobianData.m_jacobians[0],deltaV,jacobianData.scratch_r, jacobianData.scratch_v); | |||
|  |                      | |||
|  |                     btScalar vel = 0.0; | |||
|  |                     for (int j = 0; j < ndof ; ++j) { | |||
|  |                         vel += multibodyLinkCol->m_multiBody->getVelocityVector()[j] * jac[j]; | |||
|  |                     } | |||
|  |                     va = cti.m_normal*vel*dt; | |||
|  |                 } | |||
|  |             } | |||
|  |              | |||
|  | 			const btVector3		vb = c.m_node->m_x-c.m_node->m_q; | |||
|  | 			const btVector3		vr = vb-va; | |||
|  | 			const btScalar		dn = btDot(vr, cti.m_normal);		 | |||
|  | 			if(dn<=SIMD_EPSILON) | |||
|  | 			{ | |||
|  | 				const btScalar		dp = btMin( (btDot(c.m_node->m_x, cti.m_normal) + cti.m_offset), mrg ); | |||
|  | 				const btVector3		fv = vr - (cti.m_normal * dn); | |||
|  | 				// c0 is the impulse matrix, c3 is 1 - the friction coefficient or 0, c4 is the contact hardness coefficient
 | |||
|  | 				const btVector3		impulse = c.m_c0 * ( (vr - (fv * c.m_c3) + (cti.m_normal * (dp * c.m_c4))) * kst ); | |||
|  | 				c.m_node->m_x -= impulse * c.m_c2; | |||
|  |                  | |||
|  |                 if (cti.m_colObj->getInternalType() == btCollisionObject::CO_RIGID_BODY) | |||
|  |                 { | |||
|  |                     if (rigidCol) | |||
|  |                         rigidCol->applyImpulse(impulse,c.m_c1); | |||
|  |                 } | |||
|  |                 else if (cti.m_colObj->getInternalType() == btCollisionObject::CO_FEATHERSTONE_LINK) | |||
|  |                 { | |||
|  |                     if (multibodyLinkCol) | |||
|  |                     { | |||
|  |                         double multiplier = 0.5; | |||
|  |                         multibodyLinkCol->m_multiBody->applyDeltaVeeMultiDof(deltaV,-impulse.length()*multiplier); | |||
|  |                     } | |||
|  |                 } | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::PSolve_SContacts(btSoftBody* psb,btScalar,btScalar ti) | |||
|  | { | |||
|  | 	BT_PROFILE("PSolve_SContacts"); | |||
|  | 	 | |||
|  | 	for(int i=0,ni=psb->m_scontacts.size();i<ni;++i) | |||
|  | 	{ | |||
|  | 		const SContact&		c=psb->m_scontacts[i]; | |||
|  | 		const btVector3&	nr=c.m_normal; | |||
|  | 		Node&				n=*c.m_node; | |||
|  | 		Face&				f=*c.m_face; | |||
|  | 		const btVector3		p=BaryEval(	f.m_n[0]->m_x, | |||
|  | 			f.m_n[1]->m_x, | |||
|  | 			f.m_n[2]->m_x, | |||
|  | 			c.m_weights); | |||
|  | 		const btVector3		q=BaryEval(	f.m_n[0]->m_q, | |||
|  | 			f.m_n[1]->m_q, | |||
|  | 			f.m_n[2]->m_q, | |||
|  | 			c.m_weights);											 | |||
|  | 		const btVector3		vr=(n.m_x-n.m_q)-(p-q); | |||
|  | 		btVector3			corr(0,0,0); | |||
|  | 		btScalar dot = btDot(vr,nr); | |||
|  | 		if(dot<0) | |||
|  | 		{ | |||
|  | 			const btScalar	j=c.m_margin-(btDot(nr,n.m_x)-btDot(nr,p)); | |||
|  | 			corr+=c.m_normal*j; | |||
|  | 		} | |||
|  | 		corr			-=	ProjectOnPlane(vr,nr)*c.m_friction; | |||
|  | 		n.m_x			+=	corr*c.m_cfm[0]; | |||
|  | 		f.m_n[0]->m_x	-=	corr*(c.m_cfm[1]*c.m_weights.x()); | |||
|  | 		f.m_n[1]->m_x	-=	corr*(c.m_cfm[1]*c.m_weights.y()); | |||
|  | 		f.m_n[2]->m_x	-=	corr*(c.m_cfm[1]*c.m_weights.z()); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::PSolve_Links(btSoftBody* psb,btScalar kst,btScalar ti) | |||
|  | { | |||
|  | BT_PROFILE("PSolve_Links"); | |||
|  | 	for(int i=0,ni=psb->m_links.size();i<ni;++i) | |||
|  | 	{			 | |||
|  | 		Link&	l=psb->m_links[i]; | |||
|  | 		if(l.m_c0>0) | |||
|  | 		{ | |||
|  | 			Node&			a=*l.m_n[0]; | |||
|  | 			Node&			b=*l.m_n[1]; | |||
|  | 			const btVector3	del=b.m_x-a.m_x; | |||
|  | 			const btScalar	len=del.length2(); | |||
|  | 			if (l.m_c1+len > SIMD_EPSILON) | |||
|  | 			{ | |||
|  | 				const btScalar	k=((l.m_c1-len)/(l.m_c0*(l.m_c1+len)))*kst; | |||
|  | 				a.m_x-=del*(k*a.m_im); | |||
|  | 				b.m_x+=del*(k*b.m_im); | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void				btSoftBody::VSolve_Links(btSoftBody* psb,btScalar kst) | |||
|  | { | |||
|  | 	BT_PROFILE("VSolve_Links"); | |||
|  | 	for(int i=0,ni=psb->m_links.size();i<ni;++i) | |||
|  | 	{			 | |||
|  | 		Link&			l=psb->m_links[i]; | |||
|  | 		Node**			n=l.m_n; | |||
|  | 		const btScalar	j=-btDot(l.m_c3,n[0]->m_v-n[1]->m_v)*l.m_c2*kst; | |||
|  | 		n[0]->m_v+=	l.m_c3*(j*n[0]->m_im); | |||
|  | 		n[1]->m_v-=	l.m_c3*(j*n[1]->m_im); | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btSoftBody::psolver_t	btSoftBody::getSolver(ePSolver::_ solver) | |||
|  | { | |||
|  | 	switch(solver) | |||
|  | 	{ | |||
|  | 	case	ePSolver::Anchors:		 | |||
|  | 		return(&btSoftBody::PSolve_Anchors); | |||
|  | 	case	ePSolver::Linear:		 | |||
|  | 		return(&btSoftBody::PSolve_Links); | |||
|  | 	case	ePSolver::RContacts:	 | |||
|  | 		return(&btSoftBody::PSolve_RContacts); | |||
|  | 	case	ePSolver::SContacts:	 | |||
|  | 		return(&btSoftBody::PSolve_SContacts);	 | |||
|  | 		default: | |||
|  | 		{ | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return(0); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | btSoftBody::vsolver_t	btSoftBody::getSolver(eVSolver::_ solver) | |||
|  | { | |||
|  | 	switch(solver) | |||
|  | 	{ | |||
|  | 	case	eVSolver::Linear:		return(&btSoftBody::VSolve_Links); | |||
|  | 		default: | |||
|  | 		{ | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return(0); | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::defaultCollisionHandler(const btCollisionObjectWrapper* pcoWrap) | |||
|  | { | |||
|  | 
 | |||
|  | 	switch(m_cfg.collisions&fCollision::RVSmask) | |||
|  | 	{ | |||
|  | 	case	fCollision::SDF_RS: | |||
|  | 		{ | |||
|  | 			btSoftColliders::CollideSDF_RS	docollide;		 | |||
|  | 			btRigidBody*		prb1=(btRigidBody*) btRigidBody::upcast(pcoWrap->getCollisionObject()); | |||
|  | 			btTransform	wtr=pcoWrap->getWorldTransform(); | |||
|  | 
 | |||
|  | 			const btTransform	ctr=pcoWrap->getWorldTransform(); | |||
|  | 			const btScalar		timemargin=(wtr.getOrigin()-ctr.getOrigin()).length(); | |||
|  | 			const btScalar		basemargin=getCollisionShape()->getMargin(); | |||
|  | 			btVector3			mins; | |||
|  | 			btVector3			maxs; | |||
|  | 			ATTRIBUTE_ALIGNED16(btDbvtVolume)		volume; | |||
|  | 			pcoWrap->getCollisionShape()->getAabb(	pcoWrap->getWorldTransform(), | |||
|  | 				mins, | |||
|  | 				maxs); | |||
|  | 			volume=btDbvtVolume::FromMM(mins,maxs); | |||
|  | 			volume.Expand(btVector3(basemargin,basemargin,basemargin));		 | |||
|  | 			docollide.psb		=	this; | |||
|  | 			docollide.m_colObj1Wrap = pcoWrap; | |||
|  | 			docollide.m_rigidBody = prb1; | |||
|  | 
 | |||
|  | 			docollide.dynmargin	=	basemargin+timemargin; | |||
|  | 			docollide.stamargin	=	basemargin; | |||
|  | 			m_ndbvt.collideTV(m_ndbvt.m_root,volume,docollide); | |||
|  | 		} | |||
|  | 		break; | |||
|  | 	case	fCollision::CL_RS: | |||
|  | 		{ | |||
|  | 			btSoftColliders::CollideCL_RS	collider; | |||
|  | 			collider.ProcessColObj(this,pcoWrap); | |||
|  | 		} | |||
|  | 		break; | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | //
 | |||
|  | void			btSoftBody::defaultCollisionHandler(btSoftBody* psb) | |||
|  | { | |||
|  | 	const int cf=m_cfg.collisions&psb->m_cfg.collisions; | |||
|  | 	switch(cf&fCollision::SVSmask) | |||
|  | 	{ | |||
|  | 	case	fCollision::CL_SS: | |||
|  | 		{ | |||
|  | 			 | |||
|  | 			//support self-collision if CL_SELF flag set
 | |||
|  | 			if (this!=psb || psb->m_cfg.collisions&fCollision::CL_SELF) | |||
|  | 			{ | |||
|  | 				btSoftColliders::CollideCL_SS	docollide; | |||
|  | 				docollide.ProcessSoftSoft(this,psb); | |||
|  | 			} | |||
|  | 			 | |||
|  | 		} | |||
|  | 		break; | |||
|  | 	case	fCollision::VF_SS: | |||
|  | 		{ | |||
|  | 			//only self-collision for Cluster, not Vertex-Face yet
 | |||
|  | 			if (this!=psb) | |||
|  | 			{ | |||
|  | 				btSoftColliders::CollideVF_SS	docollide; | |||
|  | 				/* common					*/  | |||
|  | 				docollide.mrg=	getCollisionShape()->getMargin()+ | |||
|  | 					psb->getCollisionShape()->getMargin(); | |||
|  | 				/* psb0 nodes vs psb1 faces	*/  | |||
|  | 				docollide.psb[0]=this; | |||
|  | 				docollide.psb[1]=psb; | |||
|  | 				docollide.psb[0]->m_ndbvt.collideTT(	docollide.psb[0]->m_ndbvt.m_root, | |||
|  | 					docollide.psb[1]->m_fdbvt.m_root, | |||
|  | 					docollide); | |||
|  | 				/* psb1 nodes vs psb0 faces	*/  | |||
|  | 				docollide.psb[0]=psb; | |||
|  | 				docollide.psb[1]=this; | |||
|  | 				docollide.psb[0]->m_ndbvt.collideTT(	docollide.psb[0]->m_ndbvt.m_root, | |||
|  | 					docollide.psb[1]->m_fdbvt.m_root, | |||
|  | 					docollide); | |||
|  | 			} | |||
|  | 		} | |||
|  | 		break; | |||
|  | 	default: | |||
|  | 		{ | |||
|  | 			 | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | void btSoftBody::setWindVelocity( const btVector3 &velocity ) | |||
|  | { | |||
|  | 	m_windVelocity = velocity; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | const btVector3& btSoftBody::getWindVelocity() | |||
|  | { | |||
|  | 	return m_windVelocity; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | int	btSoftBody::calculateSerializeBufferSize()	const | |||
|  | { | |||
|  | 	int sz = sizeof(btSoftBodyData); | |||
|  | 	return sz; | |||
|  | } | |||
|  | 
 | |||
|  | 	///fills the dataBuffer and returns the struct name (and 0 on failure)
 | |||
|  | const char*	btSoftBody::serialize(void* dataBuffer, class btSerializer* serializer) const | |||
|  | { | |||
|  | 	btSoftBodyData* sbd = (btSoftBodyData*) dataBuffer; | |||
|  | 
 | |||
|  | 	btCollisionObject::serialize(&sbd->m_collisionObjectData, serializer); | |||
|  | 
 | |||
|  | 	btHashMap<btHashPtr,int>	m_nodeIndexMap; | |||
|  | 
 | |||
|  | 	sbd->m_numMaterials = m_materials.size(); | |||
|  | 	sbd->m_materials = sbd->m_numMaterials? (SoftBodyMaterialData**) serializer->getUniquePointer((void*)&m_materials): 0; | |||
|  | 
 | |||
|  | 	if (sbd->m_materials) | |||
|  | 	{ | |||
|  | 		int sz = sizeof(SoftBodyMaterialData*); | |||
|  | 		int numElem = sbd->m_numMaterials; | |||
|  | 		btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 		//SoftBodyMaterialData** memPtr = chunk->m_oldPtr;
 | |||
|  | 		SoftBodyMaterialData** memPtr = (SoftBodyMaterialData**)chunk->m_oldPtr; | |||
|  | 		for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 		{ | |||
|  | 			btSoftBody::Material* mat = m_materials[i]; | |||
|  | 			*memPtr = mat ? (SoftBodyMaterialData*)serializer->getUniquePointer((void*)mat) : 0; | |||
|  | 			if (!serializer->findPointer(mat)) | |||
|  | 			{ | |||
|  | 				//serialize it here
 | |||
|  | 				btChunk* chunk = serializer->allocate(sizeof(SoftBodyMaterialData),1); | |||
|  | 				SoftBodyMaterialData* memPtr = (SoftBodyMaterialData*)chunk->m_oldPtr; | |||
|  | 				memPtr->m_flags = mat->m_flags; | |||
|  | 				memPtr->m_angularStiffness = mat->m_kAST; | |||
|  | 				memPtr->m_linearStiffness = mat->m_kLST; | |||
|  | 				memPtr->m_volumeStiffness = mat->m_kVST; | |||
|  | 				serializer->finalizeChunk(chunk,"SoftBodyMaterialData",BT_SBMATERIAL_CODE,mat); | |||
|  | 			} | |||
|  | 		} | |||
|  | 		serializer->finalizeChunk(chunk,"SoftBodyMaterialData",BT_ARRAY_CODE,(void*) &m_materials); | |||
|  | 	} | |||
|  | 
 | |||
|  | 
 | |||
|  | 	 | |||
|  | 
 | |||
|  | 	sbd->m_numNodes = m_nodes.size(); | |||
|  | 	sbd->m_nodes = sbd->m_numNodes ? (SoftBodyNodeData*)serializer->getUniquePointer((void*)&m_nodes): 0; | |||
|  | 	if (sbd->m_nodes) | |||
|  | 	{ | |||
|  | 		int sz = sizeof(SoftBodyNodeData); | |||
|  | 		int numElem = sbd->m_numNodes; | |||
|  | 		btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 		SoftBodyNodeData* memPtr = (SoftBodyNodeData*)chunk->m_oldPtr; | |||
|  | 		for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 		{ | |||
|  | 			m_nodes[i].m_f.serializeFloat( memPtr->m_accumulatedForce); | |||
|  | 			memPtr->m_area = m_nodes[i].m_area; | |||
|  | 			memPtr->m_attach = m_nodes[i].m_battach; | |||
|  | 			memPtr->m_inverseMass = m_nodes[i].m_im; | |||
|  | 			memPtr->m_material = m_nodes[i].m_material? (SoftBodyMaterialData*)serializer->getUniquePointer((void*) m_nodes[i].m_material):0; | |||
|  | 			m_nodes[i].m_n.serializeFloat(memPtr->m_normal); | |||
|  | 			m_nodes[i].m_x.serializeFloat(memPtr->m_position); | |||
|  | 			m_nodes[i].m_q.serializeFloat(memPtr->m_previousPosition); | |||
|  | 			m_nodes[i].m_v.serializeFloat(memPtr->m_velocity); | |||
|  | 			m_nodeIndexMap.insert(&m_nodes[i],i); | |||
|  | 		} | |||
|  | 		serializer->finalizeChunk(chunk,"SoftBodyNodeData",BT_SBNODE_CODE,(void*) &m_nodes); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	sbd->m_numLinks = m_links.size(); | |||
|  | 	sbd->m_links = sbd->m_numLinks? (SoftBodyLinkData*) serializer->getUniquePointer((void*)&m_links[0]):0; | |||
|  | 	if (sbd->m_links) | |||
|  | 	{ | |||
|  | 		int sz = sizeof(SoftBodyLinkData); | |||
|  | 		int numElem = sbd->m_numLinks; | |||
|  | 		btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 		SoftBodyLinkData* memPtr = (SoftBodyLinkData*)chunk->m_oldPtr; | |||
|  | 		for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 		{ | |||
|  | 			memPtr->m_bbending = m_links[i].m_bbending; | |||
|  | 			memPtr->m_material = m_links[i].m_material? (SoftBodyMaterialData*)serializer->getUniquePointer((void*) m_links[i].m_material):0; | |||
|  | 			memPtr->m_nodeIndices[0] = m_links[i].m_n[0] ? m_links[i].m_n[0] - &m_nodes[0]: -1; | |||
|  | 			memPtr->m_nodeIndices[1] = m_links[i].m_n[1] ? m_links[i].m_n[1] - &m_nodes[0]: -1; | |||
|  | 			btAssert(memPtr->m_nodeIndices[0]<m_nodes.size()); | |||
|  | 			btAssert(memPtr->m_nodeIndices[1]<m_nodes.size()); | |||
|  | 			memPtr->m_restLength = m_links[i].m_rl; | |||
|  | 		} | |||
|  | 		serializer->finalizeChunk(chunk,"SoftBodyLinkData",BT_ARRAY_CODE,(void*) &m_links[0]); | |||
|  | 
 | |||
|  | 	} | |||
|  | 
 | |||
|  | 
 | |||
|  | 	sbd->m_numFaces = m_faces.size(); | |||
|  | 	sbd->m_faces = sbd->m_numFaces? (SoftBodyFaceData*) serializer->getUniquePointer((void*)&m_faces[0]):0; | |||
|  | 	if (sbd->m_faces) | |||
|  | 	{ | |||
|  | 		int sz = sizeof(SoftBodyFaceData); | |||
|  | 		int numElem = sbd->m_numFaces; | |||
|  | 		btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 		SoftBodyFaceData* memPtr = (SoftBodyFaceData*)chunk->m_oldPtr; | |||
|  | 		for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 		{ | |||
|  | 			memPtr->m_material = m_faces[i].m_material ?  (SoftBodyMaterialData*) serializer->getUniquePointer((void*)m_faces[i].m_material): 0; | |||
|  | 			m_faces[i].m_normal.serializeFloat(	memPtr->m_normal); | |||
|  | 			for (int j=0;j<3;j++) | |||
|  | 			{ | |||
|  | 				memPtr->m_nodeIndices[j] = m_faces[i].m_n[j]? m_faces[i].m_n[j] - &m_nodes[0]: -1; | |||
|  | 			} | |||
|  | 			memPtr->m_restArea = m_faces[i].m_ra; | |||
|  | 		} | |||
|  | 		serializer->finalizeChunk(chunk,"SoftBodyFaceData",BT_ARRAY_CODE,(void*) &m_faces[0]); | |||
|  | 	} | |||
|  | 
 | |||
|  | 
 | |||
|  | 	sbd->m_numTetrahedra = m_tetras.size(); | |||
|  | 	sbd->m_tetrahedra = sbd->m_numTetrahedra ? (SoftBodyTetraData*) serializer->getUniquePointer((void*)&m_tetras[0]):0; | |||
|  | 	if (sbd->m_tetrahedra) | |||
|  | 	{ | |||
|  | 		int sz = sizeof(SoftBodyTetraData); | |||
|  | 		int numElem = sbd->m_numTetrahedra; | |||
|  | 		btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 		SoftBodyTetraData* memPtr = (SoftBodyTetraData*)chunk->m_oldPtr; | |||
|  | 		for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 		{ | |||
|  | 			for (int j=0;j<4;j++) | |||
|  | 			{ | |||
|  | 				m_tetras[i].m_c0[j].serializeFloat(	memPtr->m_c0[j] ); | |||
|  | 				memPtr->m_nodeIndices[j] = m_tetras[j].m_n[j]? m_tetras[j].m_n[j]-&m_nodes[0] : -1; | |||
|  | 			} | |||
|  | 			memPtr->m_c1 = m_tetras[i].m_c1; | |||
|  | 			memPtr->m_c2 = m_tetras[i].m_c2; | |||
|  | 			memPtr->m_material = m_tetras[i].m_material ? (SoftBodyMaterialData*)serializer->getUniquePointer((void*) m_tetras[i].m_material): 0; | |||
|  | 			memPtr->m_restVolume = m_tetras[i].m_rv; | |||
|  | 		} | |||
|  | 		serializer->finalizeChunk(chunk,"SoftBodyTetraData",BT_ARRAY_CODE,(void*) &m_tetras[0]); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	sbd->m_numAnchors = m_anchors.size(); | |||
|  | 	sbd->m_anchors = sbd->m_numAnchors ? (SoftRigidAnchorData*) serializer->getUniquePointer((void*)&m_anchors[0]):0; | |||
|  | 	if (sbd->m_anchors) | |||
|  | 	{ | |||
|  | 		int sz = sizeof(SoftRigidAnchorData); | |||
|  | 		int numElem = sbd->m_numAnchors; | |||
|  | 		btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 		SoftRigidAnchorData* memPtr = (SoftRigidAnchorData*)chunk->m_oldPtr; | |||
|  | 		for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 		{ | |||
|  | 			m_anchors[i].m_c0.serializeFloat(memPtr->m_c0); | |||
|  | 			m_anchors[i].m_c1.serializeFloat(memPtr->m_c1); | |||
|  | 			memPtr->m_c2 = m_anchors[i].m_c2; | |||
|  | 			m_anchors[i].m_local.serializeFloat(memPtr->m_localFrame); | |||
|  | 			memPtr->m_nodeIndex = m_anchors[i].m_node? m_anchors[i].m_node-&m_nodes[0]: -1; | |||
|  | 			 | |||
|  | 			memPtr->m_rigidBody = m_anchors[i].m_body? (btRigidBodyData*)  serializer->getUniquePointer((void*)m_anchors[i].m_body): 0; | |||
|  | 			btAssert(memPtr->m_nodeIndex < m_nodes.size()); | |||
|  | 		} | |||
|  | 		serializer->finalizeChunk(chunk,"SoftRigidAnchorData",BT_ARRAY_CODE,(void*) &m_anchors[0]); | |||
|  | 	} | |||
|  | 	 | |||
|  | 
 | |||
|  | 	sbd->m_config.m_dynamicFriction = m_cfg.kDF; | |||
|  | 	sbd->m_config.m_baumgarte = m_cfg.kVCF; | |||
|  | 	sbd->m_config.m_pressure = m_cfg.kPR; | |||
|  | 	sbd->m_config.m_aeroModel = this->m_cfg.aeromodel; | |||
|  | 	sbd->m_config.m_lift = m_cfg.kLF; | |||
|  | 	sbd->m_config.m_drag = m_cfg.kDG; | |||
|  | 	sbd->m_config.m_positionIterations = m_cfg.piterations; | |||
|  | 	sbd->m_config.m_driftIterations = m_cfg.diterations; | |||
|  | 	sbd->m_config.m_clusterIterations = m_cfg.citerations; | |||
|  | 	sbd->m_config.m_velocityIterations = m_cfg.viterations; | |||
|  | 	sbd->m_config.m_maxVolume = m_cfg.maxvolume; | |||
|  | 	sbd->m_config.m_damping = m_cfg.kDP; | |||
|  | 	sbd->m_config.m_poseMatch = m_cfg.kMT; | |||
|  | 	sbd->m_config.m_collisionFlags = m_cfg.collisions; | |||
|  | 	sbd->m_config.m_volume = m_cfg.kVC; | |||
|  | 	sbd->m_config.m_rigidContactHardness = m_cfg.kCHR; | |||
|  | 	sbd->m_config.m_kineticContactHardness = m_cfg.kKHR; | |||
|  | 	sbd->m_config.m_softContactHardness = m_cfg.kSHR; | |||
|  | 	sbd->m_config.m_anchorHardness = m_cfg.kAHR; | |||
|  | 	sbd->m_config.m_timeScale = m_cfg.timescale; | |||
|  | 	sbd->m_config.m_maxVolume = m_cfg.maxvolume; | |||
|  | 	sbd->m_config.m_softRigidClusterHardness = m_cfg.kSRHR_CL; | |||
|  | 	sbd->m_config.m_softKineticClusterHardness = m_cfg.kSKHR_CL; | |||
|  | 	sbd->m_config.m_softSoftClusterHardness = m_cfg.kSSHR_CL; | |||
|  | 	sbd->m_config.m_softRigidClusterImpulseSplit = m_cfg.kSR_SPLT_CL; | |||
|  | 	sbd->m_config.m_softKineticClusterImpulseSplit = m_cfg.kSK_SPLT_CL; | |||
|  | 	sbd->m_config.m_softSoftClusterImpulseSplit = m_cfg.kSS_SPLT_CL; | |||
|  | 
 | |||
|  | 	//pose for shape matching
 | |||
|  | 	{ | |||
|  | 		sbd->m_pose = (SoftBodyPoseData*)serializer->getUniquePointer((void*)&m_pose); | |||
|  | 
 | |||
|  | 		int sz = sizeof(SoftBodyPoseData); | |||
|  | 		btChunk* chunk = serializer->allocate(sz,1); | |||
|  | 		SoftBodyPoseData* memPtr = (SoftBodyPoseData*)chunk->m_oldPtr; | |||
|  | 		 | |||
|  | 		m_pose.m_aqq.serializeFloat(memPtr->m_aqq); | |||
|  | 		memPtr->m_bframe = m_pose.m_bframe; | |||
|  | 		memPtr->m_bvolume = m_pose.m_bvolume; | |||
|  | 		m_pose.m_com.serializeFloat(memPtr->m_com); | |||
|  | 		 | |||
|  | 		memPtr->m_numPositions = m_pose.m_pos.size(); | |||
|  | 		memPtr->m_positions = memPtr->m_numPositions ? (btVector3FloatData*)serializer->getUniquePointer((void*)&m_pose.m_pos[0]): 0; | |||
|  | 		if (memPtr->m_numPositions) | |||
|  | 		{ | |||
|  | 			int numElem = memPtr->m_numPositions; | |||
|  | 			int sz = sizeof(btVector3Data); | |||
|  | 			btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 			btVector3FloatData* memPtr = (btVector3FloatData*)chunk->m_oldPtr; | |||
|  | 			for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 			{ | |||
|  | 				m_pose.m_pos[i].serializeFloat(*memPtr); | |||
|  | 			} | |||
|  | 			serializer->finalizeChunk(chunk,"btVector3FloatData",BT_ARRAY_CODE,(void*)&m_pose.m_pos[0]); | |||
|  | 		} | |||
|  | 		memPtr->m_restVolume = m_pose.m_volume; | |||
|  | 		m_pose.m_rot.serializeFloat(memPtr->m_rot); | |||
|  | 		m_pose.m_scl.serializeFloat(memPtr->m_scale); | |||
|  | 
 | |||
|  | 		memPtr->m_numWeigts = m_pose.m_wgh.size(); | |||
|  | 		memPtr->m_weights = memPtr->m_numWeigts? (float*) serializer->getUniquePointer((void*) &m_pose.m_wgh[0]) : 0; | |||
|  | 		if (memPtr->m_numWeigts) | |||
|  | 		{ | |||
|  | 			 | |||
|  | 			int numElem = memPtr->m_numWeigts; | |||
|  | 			int sz = sizeof(float); | |||
|  | 			btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 			float* memPtr = (float*) chunk->m_oldPtr; | |||
|  | 			for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 			{ | |||
|  | 				*memPtr = m_pose.m_wgh[i]; | |||
|  | 			} | |||
|  | 			serializer->finalizeChunk(chunk,"float",BT_ARRAY_CODE,(void*)&m_pose.m_wgh[0]); | |||
|  | 		} | |||
|  | 
 | |||
|  | 		serializer->finalizeChunk(chunk,"SoftBodyPoseData",BT_ARRAY_CODE,(void*)&m_pose); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	//clusters for convex-cluster collision detection
 | |||
|  | 
 | |||
|  | 	sbd->m_numClusters = m_clusters.size(); | |||
|  | 	sbd->m_clusters = sbd->m_numClusters? (SoftBodyClusterData*) serializer->getUniquePointer((void*)m_clusters[0]) : 0; | |||
|  | 	if (sbd->m_numClusters) | |||
|  | 	{ | |||
|  | 		int numElem = sbd->m_numClusters; | |||
|  | 		int sz = sizeof(SoftBodyClusterData); | |||
|  | 		btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 		SoftBodyClusterData* memPtr = (SoftBodyClusterData*) chunk->m_oldPtr; | |||
|  | 		for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 		{ | |||
|  | 			memPtr->m_adamping= m_clusters[i]->m_adamping; | |||
|  | 			m_clusters[i]->m_av.serializeFloat(memPtr->m_av); | |||
|  | 			memPtr->m_clusterIndex = m_clusters[i]->m_clusterIndex; | |||
|  | 			memPtr->m_collide = m_clusters[i]->m_collide; | |||
|  | 			m_clusters[i]->m_com.serializeFloat(memPtr->m_com); | |||
|  | 			memPtr->m_containsAnchor = m_clusters[i]->m_containsAnchor; | |||
|  | 			m_clusters[i]->m_dimpulses[0].serializeFloat(memPtr->m_dimpulses[0]); | |||
|  | 			m_clusters[i]->m_dimpulses[1].serializeFloat(memPtr->m_dimpulses[1]); | |||
|  | 			m_clusters[i]->m_framexform.serializeFloat(memPtr->m_framexform); | |||
|  | 			memPtr->m_idmass = m_clusters[i]->m_idmass; | |||
|  | 			memPtr->m_imass = m_clusters[i]->m_imass; | |||
|  | 			m_clusters[i]->m_invwi.serializeFloat(memPtr->m_invwi); | |||
|  | 			memPtr->m_ldamping = m_clusters[i]->m_ldamping; | |||
|  | 			m_clusters[i]->m_locii.serializeFloat(memPtr->m_locii); | |||
|  | 			m_clusters[i]->m_lv.serializeFloat(memPtr->m_lv); | |||
|  | 			memPtr->m_matching = m_clusters[i]->m_matching; | |||
|  | 			memPtr->m_maxSelfCollisionImpulse = m_clusters[i]->m_maxSelfCollisionImpulse; | |||
|  | 			memPtr->m_ndamping = m_clusters[i]->m_ndamping; | |||
|  | 			memPtr->m_ldamping = m_clusters[i]->m_ldamping; | |||
|  | 			memPtr->m_adamping = m_clusters[i]->m_adamping; | |||
|  | 			memPtr->m_selfCollisionImpulseFactor = m_clusters[i]->m_selfCollisionImpulseFactor; | |||
|  | 
 | |||
|  | 			memPtr->m_numFrameRefs = m_clusters[i]->m_framerefs.size(); | |||
|  | 			memPtr->m_numMasses = m_clusters[i]->m_masses.size(); | |||
|  | 			memPtr->m_numNodes = m_clusters[i]->m_nodes.size(); | |||
|  | 
 | |||
|  | 			memPtr->m_nvimpulses = m_clusters[i]->m_nvimpulses; | |||
|  | 			m_clusters[i]->m_vimpulses[0].serializeFloat(memPtr->m_vimpulses[0]); | |||
|  | 			m_clusters[i]->m_vimpulses[1].serializeFloat(memPtr->m_vimpulses[1]); | |||
|  | 			memPtr->m_ndimpulses = m_clusters[i]->m_ndimpulses; | |||
|  | 
 | |||
|  | 			 | |||
|  | 
 | |||
|  | 			memPtr->m_framerefs = memPtr->m_numFrameRefs? (btVector3FloatData*)serializer->getUniquePointer((void*)&m_clusters[i]->m_framerefs[0]) : 0; | |||
|  | 			if (memPtr->m_framerefs) | |||
|  | 			{ | |||
|  | 				int numElem = memPtr->m_numFrameRefs; | |||
|  | 				int sz = sizeof(btVector3FloatData); | |||
|  | 				btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 				btVector3FloatData* memPtr = (btVector3FloatData*) chunk->m_oldPtr; | |||
|  | 				for (int j=0;j<numElem;j++,memPtr++) | |||
|  | 				{ | |||
|  | 					m_clusters[i]->m_framerefs[j].serializeFloat(*memPtr); | |||
|  | 				} | |||
|  | 				serializer->finalizeChunk(chunk,"btVector3FloatData",BT_ARRAY_CODE,(void*)&m_clusters[i]->m_framerefs[0]); | |||
|  | 			} | |||
|  | 			 | |||
|  | 			memPtr->m_masses = memPtr->m_numMasses ? (float*) serializer->getUniquePointer((void*)&m_clusters[i]->m_masses[0]): 0; | |||
|  | 			if (memPtr->m_masses) | |||
|  | 			{ | |||
|  | 				int numElem = memPtr->m_numMasses; | |||
|  | 				int sz = sizeof(float); | |||
|  | 				btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 				float* memPtr = (float*) chunk->m_oldPtr; | |||
|  | 				for (int j=0;j<numElem;j++,memPtr++) | |||
|  | 				{ | |||
|  | 					*memPtr = m_clusters[i]->m_masses[j]; | |||
|  | 				} | |||
|  | 				serializer->finalizeChunk(chunk,"float",BT_ARRAY_CODE,(void*)&m_clusters[i]->m_masses[0]); | |||
|  | 			} | |||
|  | 
 | |||
|  | 			memPtr->m_nodeIndices  = memPtr->m_numNodes ? (int*) serializer->getUniquePointer((void*) &m_clusters[i]->m_nodes) : 0; | |||
|  | 			if (memPtr->m_nodeIndices ) | |||
|  | 			{ | |||
|  | 				int numElem = memPtr->m_numMasses; | |||
|  | 				int sz = sizeof(int); | |||
|  | 				btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 				int* memPtr = (int*) chunk->m_oldPtr; | |||
|  | 				for (int j=0;j<numElem;j++,memPtr++) | |||
|  | 				{ | |||
|  | 					int* indexPtr = m_nodeIndexMap.find(m_clusters[i]->m_nodes[j]); | |||
|  | 					btAssert(indexPtr); | |||
|  | 					*memPtr = *indexPtr; | |||
|  | 				} | |||
|  | 				serializer->finalizeChunk(chunk,"int",BT_ARRAY_CODE,(void*)&m_clusters[i]->m_nodes); | |||
|  | 			} | |||
|  | 		} | |||
|  | 		serializer->finalizeChunk(chunk,"SoftBodyClusterData",BT_ARRAY_CODE,(void*)m_clusters[0]); | |||
|  | 
 | |||
|  | 	} | |||
|  | 	 | |||
|  | 
 | |||
|  | 	 | |||
|  | 	sbd->m_numJoints = m_joints.size(); | |||
|  | 	sbd->m_joints = m_joints.size()? (btSoftBodyJointData*) serializer->getUniquePointer((void*)&m_joints[0]) : 0; | |||
|  | 
 | |||
|  | 	if (sbd->m_joints) | |||
|  | 	{ | |||
|  | 		int sz = sizeof(btSoftBodyJointData); | |||
|  | 		int numElem = m_joints.size(); | |||
|  | 		btChunk* chunk = serializer->allocate(sz,numElem); | |||
|  | 		btSoftBodyJointData* memPtr = (btSoftBodyJointData*)chunk->m_oldPtr; | |||
|  | 
 | |||
|  | 		for (int i=0;i<numElem;i++,memPtr++) | |||
|  | 		{ | |||
|  | 			memPtr->m_jointType = (int)m_joints[i]->Type(); | |||
|  | 			m_joints[i]->m_refs[0].serializeFloat(memPtr->m_refs[0]); | |||
|  | 			m_joints[i]->m_refs[1].serializeFloat(memPtr->m_refs[1]); | |||
|  | 			memPtr->m_cfm = m_joints[i]->m_cfm; | |||
|  | 			memPtr->m_erp = float(m_joints[i]->m_erp); | |||
|  | 			memPtr->m_split = float(m_joints[i]->m_split); | |||
|  | 			memPtr->m_delete = m_joints[i]->m_delete; | |||
|  | 			 | |||
|  | 			for (int j=0;j<4;j++) | |||
|  | 			{ | |||
|  | 				memPtr->m_relPosition[0].m_floats[j] = 0.f; | |||
|  | 				memPtr->m_relPosition[1].m_floats[j] = 0.f; | |||
|  | 			} | |||
|  | 			memPtr->m_bodyA = 0; | |||
|  | 			memPtr->m_bodyB = 0; | |||
|  | 			if (m_joints[i]->m_bodies[0].m_soft) | |||
|  | 			{ | |||
|  | 				memPtr->m_bodyAtype = BT_JOINT_SOFT_BODY_CLUSTER; | |||
|  | 				memPtr->m_bodyA = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[0].m_soft); | |||
|  | 			} | |||
|  | 			if (m_joints[i]->m_bodies[0].m_collisionObject) | |||
|  | 			{ | |||
|  | 				memPtr->m_bodyAtype = BT_JOINT_COLLISION_OBJECT; | |||
|  | 				memPtr->m_bodyA = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[0].m_collisionObject); | |||
|  | 			} | |||
|  | 			if (m_joints[i]->m_bodies[0].m_rigid) | |||
|  | 			{ | |||
|  | 				memPtr->m_bodyAtype = BT_JOINT_RIGID_BODY; | |||
|  | 				memPtr->m_bodyA = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[0].m_rigid); | |||
|  | 			} | |||
|  | 
 | |||
|  | 			if (m_joints[i]->m_bodies[1].m_soft) | |||
|  | 			{ | |||
|  | 				memPtr->m_bodyBtype = BT_JOINT_SOFT_BODY_CLUSTER; | |||
|  | 				memPtr->m_bodyB = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[1].m_soft); | |||
|  | 			} | |||
|  | 			if (m_joints[i]->m_bodies[1].m_collisionObject) | |||
|  | 			{ | |||
|  | 				memPtr->m_bodyBtype = BT_JOINT_COLLISION_OBJECT; | |||
|  | 				memPtr->m_bodyB = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[1].m_collisionObject); | |||
|  | 			} | |||
|  | 			if (m_joints[i]->m_bodies[1].m_rigid) | |||
|  | 			{ | |||
|  | 				memPtr->m_bodyBtype = BT_JOINT_RIGID_BODY; | |||
|  | 				memPtr->m_bodyB = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[1].m_rigid); | |||
|  | 			} | |||
|  | 		} | |||
|  | 		serializer->finalizeChunk(chunk,"btSoftBodyJointData",BT_ARRAY_CODE,(void*) &m_joints[0]); | |||
|  | 	} | |||
|  | 
 | |||
|  | 
 | |||
|  | 	return btSoftBodyDataName; | |||
|  | } | |||
|  | 
 |