2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
11 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.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
15 ///btSoftBody implementation by Nathanael Presson
17 #ifndef _BT_SOFT_BODY_INTERNALS_H
18 #define _BT_SOFT_BODY_INTERNALS_H
20 #include "btSoftBody.h"
22 #include "LinearMath/btQuickprof.h"
23 #include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h"
24 #include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
25 #include "BulletCollision/CollisionShapes/btConvexInternalShape.h"
26 #include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h"
34 btSymMatrix() : dim(0) {}
35 btSymMatrix(int n,const T& init=T()) { resize(n,init); }
36 void resize(int n,const T& init=T()) { dim=n;store.resize((n*(n+1))/2,init); }
37 int index(int c,int r) const { if(c>r) btSwap(c,r);btAssert(r<dim);return((r*(r+1))/2+c); }
38 T& operator()(int c,int r) { return(store[index(c,r)]); }
39 const T& operator()(int c,int r) const { return(store[index(c,r)]); }
40 btAlignedObjectArray<T> store;
45 // btSoftBodyCollisionShape
47 class btSoftBodyCollisionShape : public btConcaveShape
52 btSoftBodyCollisionShape(btSoftBody* backptr)
57 virtual ~btSoftBodyCollisionShape()
62 void processAllTriangles(btTriangleCallback* /*callback*/,const btVector3& /*aabbMin*/,const btVector3& /*aabbMax*/) const
68 ///getAabb returns the axis aligned bounding box in the coordinate frame of the given transform t.
69 virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
71 /* t should be identity, but better be safe than...fast? */
72 const btVector3 mins=m_body->m_bounds[0];
73 const btVector3 maxs=m_body->m_bounds[1];
74 const btVector3 crns[]={t*btVector3(mins.x(),mins.y(),mins.z()),
75 t*btVector3(maxs.x(),mins.y(),mins.z()),
76 t*btVector3(maxs.x(),maxs.y(),mins.z()),
77 t*btVector3(mins.x(),maxs.y(),mins.z()),
78 t*btVector3(mins.x(),mins.y(),maxs.z()),
79 t*btVector3(maxs.x(),mins.y(),maxs.z()),
80 t*btVector3(maxs.x(),maxs.y(),maxs.z()),
81 t*btVector3(mins.x(),maxs.y(),maxs.z())};
82 aabbMin=aabbMax=crns[0];
85 aabbMin.setMin(crns[i]);
86 aabbMax.setMax(crns[i]);
90 virtual int getShapeType() const
92 return SOFTBODY_SHAPE_PROXYTYPE;
94 virtual void setLocalScaling(const btVector3& /*scaling*/)
99 virtual const btVector3& getLocalScaling() const
101 static const btVector3 dummy(1,1,1);
104 virtual void calculateLocalInertia(btScalar /*mass*/,btVector3& /*inertia*/) const
109 virtual const char* getName()const
117 // btSoftClusterCollisionShape
119 class btSoftClusterCollisionShape : public btConvexInternalShape
122 const btSoftBody::Cluster* m_cluster;
124 btSoftClusterCollisionShape (const btSoftBody::Cluster* cluster) : m_cluster(cluster) { setMargin(0); }
127 virtual btVector3 localGetSupportingVertex(const btVector3& vec) const
129 btSoftBody::Node* const * n=&m_cluster->m_nodes[0];
130 btScalar d=dot(vec,n[0]->m_x);
132 for(int i=1,ni=m_cluster->m_nodes.size();i<ni;++i)
134 const btScalar k=dot(vec,n[i]->m_x);
139 virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const
141 return(localGetSupportingVertex(vec));
143 //notice that the vectors should be unit length
144 virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const
148 virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const
151 virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
154 virtual int getShapeType() const { return SOFTBODY_SHAPE_PROXYTYPE; }
157 virtual const char* getName()const {return "SOFTCLUSTER";}
159 virtual void setMargin(btScalar margin)
161 btConvexInternalShape::setMargin(margin);
163 virtual btScalar getMargin() const
174 template <typename T>
175 static inline void ZeroInitialize(T& value)
177 static const T zerodummy;
181 template <typename T>
182 static inline bool CompLess(const T& a,const T& b)
185 template <typename T>
186 static inline bool CompGreater(const T& a,const T& b)
189 template <typename T>
190 static inline T Lerp(const T& a,const T& b,btScalar t)
191 { return(a+(b-a)*t); }
193 template <typename T>
194 static inline T InvLerp(const T& a,const T& b,btScalar t)
195 { return((b+a*t-b*t)/(a*b)); }
197 static inline btMatrix3x3 Lerp( const btMatrix3x3& a,
198 const btMatrix3x3& b,
202 r[0]=Lerp(a[0],b[0],t);
203 r[1]=Lerp(a[1],b[1],t);
204 r[2]=Lerp(a[2],b[2],t);
208 static inline btVector3 Clamp(const btVector3& v,btScalar maxlength)
210 const btScalar sql=v.length2();
211 if(sql>(maxlength*maxlength))
212 return((v*maxlength)/btSqrt(sql));
217 template <typename T>
218 static inline T Clamp(const T& x,const T& l,const T& h)
219 { return(x<l?l:x>h?h:x); }
221 template <typename T>
222 static inline T Sq(const T& x)
225 template <typename T>
226 static inline T Cube(const T& x)
229 template <typename T>
230 static inline T Sign(const T& x)
231 { return((T)(x<0?-1:+1)); }
233 template <typename T>
234 static inline bool SameSign(const T& x,const T& y)
237 static inline btScalar ClusterMetric(const btVector3& x,const btVector3& y)
239 const btVector3 d=x-y;
240 return(btFabs(d[0])+btFabs(d[1])+btFabs(d[2]));
243 static inline btMatrix3x3 ScaleAlongAxis(const btVector3& a,btScalar s)
245 const btScalar xx=a.x()*a.x();
246 const btScalar yy=a.y()*a.y();
247 const btScalar zz=a.z()*a.z();
248 const btScalar xy=a.x()*a.y();
249 const btScalar yz=a.y()*a.z();
250 const btScalar zx=a.z()*a.x();
252 m[0]=btVector3(1-xx+xx*s,xy*s-xy,zx*s-zx);
253 m[1]=btVector3(xy*s-xy,1-yy+yy*s,yz*s-yz);
254 m[2]=btVector3(zx*s-zx,yz*s-yz,1-zz+zz*s);
258 static inline btMatrix3x3 Cross(const btVector3& v)
261 m[0]=btVector3(0,-v.z(),+v.y());
262 m[1]=btVector3(+v.z(),0,-v.x());
263 m[2]=btVector3(-v.y(),+v.x(),0);
267 static inline btMatrix3x3 Diagonal(btScalar x)
270 m[0]=btVector3(x,0,0);
271 m[1]=btVector3(0,x,0);
272 m[2]=btVector3(0,0,x);
276 static inline btMatrix3x3 Add(const btMatrix3x3& a,
277 const btMatrix3x3& b)
280 for(int i=0;i<3;++i) r[i]=a[i]+b[i];
284 static inline btMatrix3x3 Sub(const btMatrix3x3& a,
285 const btMatrix3x3& b)
288 for(int i=0;i<3;++i) r[i]=a[i]-b[i];
292 static inline btMatrix3x3 Mul(const btMatrix3x3& a,
296 for(int i=0;i<3;++i) r[i]=a[i]*b;
300 static inline void Orthogonalize(btMatrix3x3& m)
302 m[2]=cross(m[0],m[1]).normalized();
303 m[1]=cross(m[2],m[0]).normalized();
304 m[0]=cross(m[1],m[2]).normalized();
307 static inline btMatrix3x3 MassMatrix(btScalar im,const btMatrix3x3& iwi,const btVector3& r)
309 const btMatrix3x3 cr=Cross(r);
310 return(Sub(Diagonal(im),cr*iwi*cr));
314 static inline btMatrix3x3 ImpulseMatrix( btScalar dt,
317 const btMatrix3x3& iwi,
320 return(Diagonal(1/dt)*Add(Diagonal(ima),MassMatrix(imb,iwi,r)).inverse());
324 static inline btMatrix3x3 ImpulseMatrix( btScalar ima,const btMatrix3x3& iia,const btVector3& ra,
325 btScalar imb,const btMatrix3x3& iib,const btVector3& rb)
327 return(Add(MassMatrix(ima,iia,ra),MassMatrix(imb,iib,rb)).inverse());
331 static inline btMatrix3x3 AngularImpulseMatrix( const btMatrix3x3& iia,
332 const btMatrix3x3& iib)
334 return(Add(iia,iib).inverse());
338 static inline btVector3 ProjectOnAxis( const btVector3& v,
344 static inline btVector3 ProjectOnPlane( const btVector3& v,
347 return(v-ProjectOnAxis(v,a));
351 static inline void ProjectOrigin( const btVector3& a,
356 const btVector3 d=b-a;
357 const btScalar m2=d.length2();
360 const btScalar t=Clamp<btScalar>(-dot(a,d)/m2,0,1);
361 const btVector3 p=a+d*t;
362 const btScalar l2=p.length2();
371 static inline void ProjectOrigin( const btVector3& a,
377 const btVector3& q=cross(b-a,c-a);
378 const btScalar m2=q.length2();
381 const btVector3 n=q/btSqrt(m2);
382 const btScalar k=dot(a,n);
383 const btScalar k2=k*k;
386 const btVector3 p=n*k;
387 if( (dot(cross(a-p,b-p),q)>0)&&
388 (dot(cross(b-p,c-p),q)>0)&&
389 (dot(cross(c-p,a-p),q)>0))
396 ProjectOrigin(a,b,prj,sqd);
397 ProjectOrigin(b,c,prj,sqd);
398 ProjectOrigin(c,a,prj,sqd);
405 template <typename T>
406 static inline T BaryEval( const T& a,
409 const btVector3& coord)
411 return(a*coord.x()+b*coord.y()+c*coord.z());
414 static inline btVector3 BaryCoord( const btVector3& a,
419 const btScalar w[]={ cross(a-p,b-p).length(),
420 cross(b-p,c-p).length(),
421 cross(c-p,a-p).length()};
422 const btScalar isum=1/(w[0]+w[1]+w[2]);
423 return(btVector3(w[1]*isum,w[2]*isum,w[0]*isum));
427 static btScalar ImplicitSolve( btSoftBody::ImplicitFn* fn,
430 const btScalar accuracy,
431 const int maxiterations=256)
433 btScalar span[2]={0,1};
434 btScalar values[2]={fn->Eval(a),fn->Eval(b)};
435 if(values[0]>values[1])
437 btSwap(span[0],span[1]);
438 btSwap(values[0],values[1]);
440 if(values[0]>-accuracy) return(-1);
441 if(values[1]<+accuracy) return(-1);
442 for(int i=0;i<maxiterations;++i)
444 const btScalar t=Lerp(span[0],span[1],values[0]/(values[0]-values[1]));
445 const btScalar v=fn->Eval(Lerp(a,b,t));
446 if((t<=0)||(t>=1)) break;
447 if(btFabs(v)<accuracy) return(t);
449 { span[0]=t;values[0]=v; }
451 { span[1]=t;values[1]=v; }
457 static inline btVector3 NormalizeAny(const btVector3& v)
459 const btScalar l=v.length();
463 return(btVector3(0,0,0));
467 static inline btDbvtVolume VolumeOf( const btSoftBody::Face& f,
470 const btVector3* pts[]={ &f.m_n[0]->m_x,
473 btDbvtVolume vol=btDbvtVolume::FromPoints(pts,3);
474 vol.Expand(btVector3(margin,margin,margin));
479 static inline btVector3 CenterOf( const btSoftBody::Face& f)
481 return((f.m_n[0]->m_x+f.m_n[1]->m_x+f.m_n[2]->m_x)/3);
485 static inline btScalar AreaOf( const btVector3& x0,
489 const btVector3 a=x1-x0;
490 const btVector3 b=x2-x0;
491 const btVector3 cr=cross(a,b);
492 const btScalar area=cr.length();
497 static inline btScalar VolumeOf( const btVector3& x0,
502 const btVector3 a=x1-x0;
503 const btVector3 b=x2-x0;
504 const btVector3 c=x3-x0;
505 return(dot(a,cross(b,c)));
509 static void EvaluateMedium( const btSoftBodyWorldInfo* wfi,
511 btSoftBody::sMedium& medium)
513 medium.m_velocity = btVector3(0,0,0);
514 medium.m_pressure = 0;
515 medium.m_density = wfi->air_density;
516 if(wfi->water_density>0)
518 const btScalar depth=-(dot(x,wfi->water_normal)+wfi->water_offset);
521 medium.m_density = wfi->water_density;
522 medium.m_pressure = depth*wfi->water_density*wfi->m_gravity.length();
528 static inline void ApplyClampedForce( btSoftBody::Node& n,
532 const btScalar dtim=dt*n.m_im;
533 if((f*dtim).length2()>n.m_v.length2())
535 n.m_f-=ProjectOnAxis(n.m_v,f.normalized())/dtim;
544 static inline int MatchEdge( const btSoftBody::Node* a,
545 const btSoftBody::Node* b,
546 const btSoftBody::Node* ma,
547 const btSoftBody::Node* mb)
549 if((a==ma)&&(b==mb)) return(0);
550 if((a==mb)&&(b==ma)) return(1);
555 // btEigen : Extract eigen system,
556 // straitforward implementation of http://math.fullerton.edu/mathews/n2003/JacobiMethodMod.html
557 // outputs are NOT sorted.
561 static int system(btMatrix3x3& a,btMatrix3x3* vectors,btVector3* values=0)
563 static const int maxiterations=16;
564 static const btScalar accuracy=(btScalar)0.0001;
565 btMatrix3x3& v=*vectors;
567 vectors->setIdentity();
570 if(btFabs(a[p][q])<btFabs(a[0][2])) { p=0;q=2; }
571 if(btFabs(a[p][q])<btFabs(a[1][2])) { p=1;q=2; }
572 if(btFabs(a[p][q])>accuracy)
574 const btScalar w=(a[q][q]-a[p][p])/(2*a[p][q]);
575 const btScalar z=btFabs(w);
576 const btScalar t=w/(z*(btSqrt(1+w*w)+z));
577 if(t==t)/* [WARNING] let hope that one does not get thrown aways by some compilers... */
579 const btScalar c=1/btSqrt(t*t+1);
580 const btScalar s=c*t;
586 } while((++iterations)<maxiterations);
589 *values=btVector3(a[0][0],a[1][1],a[2][2]);
594 static inline void mulTPQ(btMatrix3x3& a,btScalar c,btScalar s,int p,int q)
596 const btScalar m[2][3]={ {a[p][0],a[p][1],a[p][2]},
597 {a[q][0],a[q][1],a[q][2]}};
600 for(i=0;i<3;++i) a[p][i]=c*m[0][i]-s*m[1][i];
601 for(i=0;i<3;++i) a[q][i]=c*m[1][i]+s*m[0][i];
603 static inline void mulPQ(btMatrix3x3& a,btScalar c,btScalar s,int p,int q)
605 const btScalar m[2][3]={ {a[0][p],a[1][p],a[2][p]},
606 {a[0][q],a[1][q],a[2][q]}};
609 for(i=0;i<3;++i) a[i][p]=c*m[0][i]-s*m[1][i];
610 for(i=0;i<3;++i) a[i][q]=c*m[1][i]+s*m[0][i];
615 // Polar decomposition,
616 // "Computing the Polar Decomposition with Applications", Nicholas J. Higham, 1986.
618 static inline int PolarDecompose( const btMatrix3x3& m,btMatrix3x3& q,btMatrix3x3& s)
620 static const btScalar half=(btScalar)0.5;
621 static const btScalar accuracy=(btScalar)0.0001;
622 static const int maxiterations=16;
625 q = Mul(m,1/btVector3(m[0][0],m[1][1],m[2][2]).length());
626 det = q.determinant();
627 if(!btFuzzyZero(det))
629 for(;i<maxiterations;++i)
631 q=Mul(Add(q,Mul(q.adjoint(),1/det).transpose()),half);
632 const btScalar ndet=q.determinant();
633 if(Sq(ndet-det)>accuracy) det=ndet; else break;
635 /* Final orthogonalization */
651 struct btSoftColliders
656 struct ClusterBase : btDbvt::ICollide
669 threshold =(btScalar)0;
671 bool SolveContact( const btGjkEpaSolver2::sResults& res,
672 btSoftBody::Body ba,btSoftBody::Body bb,
673 btSoftBody::CJoint& joint)
675 if(res.distance<margin)
677 const btVector3 ra=res.witnesses[0]-ba.xform().getOrigin();
678 const btVector3 rb=res.witnesses[1]-bb.xform().getOrigin();
679 const btVector3 va=ba.velocity(ra);
680 const btVector3 vb=bb.velocity(rb);
681 const btVector3 vrel=va-vb;
682 const btScalar rvac=dot(vrel,res.normal);
683 const btScalar depth=res.distance-margin;
684 const btVector3 iv=res.normal*rvac;
685 const btVector3 fv=vrel-iv;
686 joint.m_bodies[0] = ba;
687 joint.m_bodies[1] = bb;
688 joint.m_refs[0] = ra*ba.xform().getBasis();
689 joint.m_refs[1] = rb*bb.xform().getBasis();
690 joint.m_rpos[0] = ra;
691 joint.m_rpos[1] = rb;
697 joint.m_drift = depth*res.normal;
698 joint.m_normal = res.normal;
699 joint.m_delete = false;
700 joint.m_friction = fv.length2()<(-rvac*friction)?1:friction;
701 joint.m_massmatrix = ImpulseMatrix( ba.invMass(),ba.invWorldInertia(),joint.m_rpos[0],
702 bb.invMass(),bb.invWorldInertia(),joint.m_rpos[1]);
711 struct CollideCL_RS : ClusterBase
715 void Process(const btDbvtNode* leaf)
717 btSoftBody::Cluster* cluster=(btSoftBody::Cluster*)leaf->data;
718 btSoftClusterCollisionShape cshape(cluster);
719 const btConvexShape* rshape=(const btConvexShape*)prb->getCollisionShape();
720 btGjkEpaSolver2::sResults res;
721 if(btGjkEpaSolver2::SignedDistance( &cshape,btTransform::getIdentity(),
722 rshape,prb->getInterpolationWorldTransform(),
723 btVector3(1,0,0),res))
725 btSoftBody::CJoint joint;
726 if(SolveContact(res,cluster,prb,joint))
728 btSoftBody::CJoint* pj=new(btAlignedAlloc(sizeof(btSoftBody::CJoint),16)) btSoftBody::CJoint();
729 *pj=joint;psb->m_joints.push_back(pj);
730 if(prb->isStaticOrKinematicObject())
732 pj->m_erp *= psb->m_cfg.kSKHR_CL;
733 pj->m_split *= psb->m_cfg.kSK_SPLT_CL;
737 pj->m_erp *= psb->m_cfg.kSRHR_CL;
738 pj->m_split *= psb->m_cfg.kSR_SPLT_CL;
743 void Process(btSoftBody* ps,btRigidBody* pr)
747 idt = ps->m_sst.isdt;
748 margin = ps->getCollisionShape()->getMargin()+
749 pr->getCollisionShape()->getMargin();
750 friction = btMin(psb->m_cfg.kDF,prb->getFriction());
754 ATTRIBUTE_ALIGNED16(btDbvtVolume) volume;
755 pr->getCollisionShape()->getAabb(pr->getInterpolationWorldTransform(),mins,maxs);
756 volume=btDbvtVolume::FromMM(mins,maxs);
757 volume.Expand(btVector3(1,1,1)*margin);
758 btDbvt::collideTV(ps->m_cdbvt.m_root,volume,*this);
764 struct CollideCL_SS : ClusterBase
766 btSoftBody* bodies[2];
767 void Process(const btDbvtNode* la,const btDbvtNode* lb)
769 btSoftBody::Cluster* cla=(btSoftBody::Cluster*)la->data;
770 btSoftBody::Cluster* clb=(btSoftBody::Cluster*)lb->data;
771 btSoftClusterCollisionShape csa(cla);
772 btSoftClusterCollisionShape csb(clb);
773 btGjkEpaSolver2::sResults res;
774 if(btGjkEpaSolver2::SignedDistance( &csa,btTransform::getIdentity(),
775 &csb,btTransform::getIdentity(),
776 cla->m_com-clb->m_com,res))
778 btSoftBody::CJoint joint;
779 if(SolveContact(res,cla,clb,joint))
781 btSoftBody::CJoint* pj=new(btAlignedAlloc(sizeof(btSoftBody::CJoint),16)) btSoftBody::CJoint();
782 *pj=joint;bodies[0]->m_joints.push_back(pj);
783 pj->m_erp *= btMax(bodies[0]->m_cfg.kSSHR_CL,bodies[1]->m_cfg.kSSHR_CL);
784 pj->m_split *= (bodies[0]->m_cfg.kSS_SPLT_CL+bodies[1]->m_cfg.kSS_SPLT_CL)/2;
788 void Process(btSoftBody* psa,btSoftBody* psb)
790 idt = psa->m_sst.isdt;
791 margin = (psa->getCollisionShape()->getMargin()+psb->getCollisionShape()->getMargin())/2;
792 friction = btMin(psa->m_cfg.kDF,psb->m_cfg.kDF);
795 btDbvt::collideTT(psa->m_cdbvt.m_root,psb->m_cdbvt.m_root,*this);
801 struct CollideSDF_RS : btDbvt::ICollide
803 void Process(const btDbvtNode* leaf)
805 btSoftBody::Node* node=(btSoftBody::Node*)leaf->data;
808 void DoNode(btSoftBody::Node& n) const
810 const btScalar m=n.m_im>0?dynmargin:stamargin;
811 btSoftBody::RContact c;
813 psb->checkContact(prb,n.m_x,m,c.m_cti))
815 const btScalar ima=n.m_im;
816 const btScalar imb=prb->getInvMass();
817 const btScalar ms=ima+imb;
820 const btTransform& wtr=prb->getInterpolationWorldTransform();
821 const btMatrix3x3& iwi=prb->getInvInertiaTensorWorld();
822 const btVector3 ra=n.m_x-wtr.getOrigin();
823 const btVector3 va=prb->getVelocityInLocalPoint(ra)*psb->m_sst.sdt;
824 const btVector3 vb=n.m_x-n.m_q;
825 const btVector3 vr=vb-va;
826 const btScalar dn=dot(vr,c.m_cti.m_normal);
827 const btVector3 fv=vr-c.m_cti.m_normal*dn;
828 const btScalar fc=psb->m_cfg.kDF*prb->getFriction();
830 c.m_c0 = ImpulseMatrix(psb->m_sst.sdt,ima,imb,iwi,ra);
832 c.m_c2 = ima*psb->m_sst.sdt;
833 c.m_c3 = fv.length2()<(btFabs(dn)*fc)?0:1-fc;
834 c.m_c4 = prb->isStaticOrKinematicObject()?psb->m_cfg.kKHR:psb->m_cfg.kCHR;
835 psb->m_rcontacts.push_back(c);
848 struct CollideVF_SS : btDbvt::ICollide
850 void Process(const btDbvtNode* lnode,
851 const btDbvtNode* lface)
853 btSoftBody::Node* node=(btSoftBody::Node*)lnode->data;
854 btSoftBody::Face* face=(btSoftBody::Face*)lface->data;
855 btVector3 o=node->m_x;
857 btScalar d=SIMD_INFINITY;
858 ProjectOrigin( face->m_n[0]->m_x-o,
862 const btScalar m=mrg+(o-node->m_q).length()*2;
865 const btSoftBody::Node* n[]={face->m_n[0],face->m_n[1],face->m_n[2]};
866 const btVector3 w=BaryCoord(n[0]->m_x,n[1]->m_x,n[2]->m_x,p+o);
867 const btScalar ma=node->m_im;
868 btScalar mb=BaryEval(n[0]->m_im,n[1]->m_im,n[2]->m_im,w);
869 if( (n[0]->m_im<=0)||
875 const btScalar ms=ma+mb;
878 btSoftBody::SContact c;
879 c.m_normal = p/-btSqrt(d);
884 c.m_friction = btMax(psb[0]->m_cfg.kDF,psb[1]->m_cfg.kDF);
885 c.m_cfm[0] = ma/ms*psb[0]->m_cfg.kSHR;
886 c.m_cfm[1] = mb/ms*psb[1]->m_cfg.kSHR;
887 psb[0]->m_scontacts.push_back(c);
896 #endif //_BT_SOFT_BODY_INTERNALS_H