/*
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

#ifndef _BT_SOFT_BODY_INTERNALS_H
#define _BT_SOFT_BODY_INTERNALS_H

#include "btSoftBody.h"


#include "LinearMath/btQuickprof.h"
#include "LinearMath/btPolarDecomposition.h"
#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h"
#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
#include "BulletCollision/CollisionShapes/btConvexInternalShape.h"
#include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h"
#include <string.h> //for memset
//
// btSymMatrix
//
template <typename T>
struct btSymMatrix
{
    btSymMatrix() : dim(0)                  {}
    btSymMatrix(int n,const T& init=T())    { resize(n,init); }
    void                    resize(int n,const T& init=T())         { dim=n;store.resize((n*(n+1))/2,init); }
    int                     index(int c,int r) const                { if(c>r) btSwap(c,r);btAssert(r<dim);return((r*(r+1))/2+c); }
    T&                      operator()(int c,int r)                 { return(store[index(c,r)]); }
    const T&                operator()(int c,int r) const           { return(store[index(c,r)]); }
    btAlignedObjectArray<T> store;
    int                     dim;
};  

//
// btSoftBodyCollisionShape
//
class btSoftBodyCollisionShape : public btConcaveShape
{
public:
    btSoftBody*                     m_body;

    btSoftBodyCollisionShape(btSoftBody* backptr)
    {
        m_shapeType = SOFTBODY_SHAPE_PROXYTYPE;
        m_body=backptr;
    }

    virtual ~btSoftBodyCollisionShape()
    {

    }

    void    processAllTriangles(btTriangleCallback* /*callback*/,const btVector3& /*aabbMin*/,const btVector3& /*aabbMax*/) const
    {
        //not yet
        btAssert(0);
    }

    ///getAabb returns the axis aligned bounding box in the coordinate frame of the given transform t.
    virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
    {
        /* t is usually identity, except when colliding against btCompoundShape. See Issue 512 */
        const btVector3 mins=m_body->m_bounds[0];
        const btVector3 maxs=m_body->m_bounds[1];
        const btVector3 crns[]={t*btVector3(mins.x(),mins.y(),mins.z()),
            t*btVector3(maxs.x(),mins.y(),mins.z()),
            t*btVector3(maxs.x(),maxs.y(),mins.z()),
            t*btVector3(mins.x(),maxs.y(),mins.z()),
            t*btVector3(mins.x(),mins.y(),maxs.z()),
            t*btVector3(maxs.x(),mins.y(),maxs.z()),
            t*btVector3(maxs.x(),maxs.y(),maxs.z()),
            t*btVector3(mins.x(),maxs.y(),maxs.z())};
        aabbMin=aabbMax=crns[0];
        for(int i=1;i<8;++i)
        {
            aabbMin.setMin(crns[i]);
            aabbMax.setMax(crns[i]);
        }
    }


    virtual void    setLocalScaling(const btVector3& /*scaling*/)
    {       
        ///na
    }
    virtual const btVector3& getLocalScaling() const
    {
        static const btVector3 dummy(1,1,1);
        return dummy;
    }
    virtual void    calculateLocalInertia(btScalar /*mass*/,btVector3& /*inertia*/) const
    {
        ///not yet
        btAssert(0);
    }
    virtual const char* getName()const
    {
        return "SoftBody";
    }

};

//
// btSoftClusterCollisionShape
//
class btSoftClusterCollisionShape : public btConvexInternalShape
{
public:
    const btSoftBody::Cluster*  m_cluster;

    btSoftClusterCollisionShape (const btSoftBody::Cluster* cluster) : m_cluster(cluster) { setMargin(0); }


    virtual btVector3   localGetSupportingVertex(const btVector3& vec) const
    {
        btSoftBody::Node* const *                       n=&m_cluster->m_nodes[0];
        btScalar                                        d=btDot(vec,n[0]->m_x);
        int                                             j=0;
        for(int i=1,ni=m_cluster->m_nodes.size();i<ni;++i)
        {
            const btScalar  k=btDot(vec,n[i]->m_x);
            if(k>d) { d=k;j=i; }
        }
        return(n[j]->m_x);
    }
    virtual btVector3   localGetSupportingVertexWithoutMargin(const btVector3& vec)const
    {
        return(localGetSupportingVertex(vec));
    }
    //notice that the vectors should be unit length
    virtual void    batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const
    {}


    virtual void    calculateLocalInertia(btScalar mass,btVector3& inertia) const
    {}

    virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
    {}

    virtual int getShapeType() const { return SOFTBODY_SHAPE_PROXYTYPE; }

    //debugging
    virtual const char* getName()const {return "SOFTCLUSTER";}

    virtual void    setMargin(btScalar margin)
    {
        btConvexInternalShape::setMargin(margin);
    }
    virtual btScalar    getMargin() const
    {
        return btConvexInternalShape::getMargin();
    }
};

//
// Inline's
//

//
template <typename T>
static inline void          ZeroInitialize(T& value)
{
    memset(&value,0,sizeof(T));
}
//
template <typename T>
static inline bool          CompLess(const T& a,const T& b)
{ return(a<b); }
//
template <typename T>
static inline bool          CompGreater(const T& a,const T& b)
{ return(a>b); }
//
template <typename T>
static inline T             Lerp(const T& a,const T& b,btScalar t)
{ return(a+(b-a)*t); }
//
template <typename T>
static inline T             InvLerp(const T& a,const T& b,btScalar t)
{ return((b+a*t-b*t)/(a*b)); }
//
static inline btMatrix3x3   Lerp(   const btMatrix3x3& a,
                                 const btMatrix3x3& b,
                                 btScalar t)
{
    btMatrix3x3 r;
    r[0]=Lerp(a[0],b[0],t);
    r[1]=Lerp(a[1],b[1],t);
    r[2]=Lerp(a[2],b[2],t);
    return(r);
}
//
static inline btVector3     Clamp(const btVector3& v,btScalar maxlength)
{
    const btScalar sql=v.length2();
    if(sql>(maxlength*maxlength))
        return((v*maxlength)/btSqrt(sql));
    else
        return(v);
}
//
template <typename T>
static inline T             Clamp(const T& x,const T& l,const T& h)
{ return(x<l?l:x>h?h:x); }
//
template <typename T>
static inline T             Sq(const T& x)
{ return(x*x); }
//
template <typename T>
static inline T             Cube(const T& x)
{ return(x*x*x); }
//
template <typename T>
static inline T             Sign(const T& x)
{ return((T)(x<0?-1:+1)); }
//
template <typename T>
static inline bool          SameSign(const T& x,const T& y)
{ return((x*y)>0); }
//
static inline btScalar      ClusterMetric(const btVector3& x,const btVector3& y)
{
    const btVector3 d=x-y;
    return(btFabs(d[0])+btFabs(d[1])+btFabs(d[2]));
}
//
static inline btMatrix3x3   ScaleAlongAxis(const btVector3& a,btScalar s)
{
    const btScalar  xx=a.x()*a.x();
    const btScalar  yy=a.y()*a.y();
    const btScalar  zz=a.z()*a.z();
    const btScalar  xy=a.x()*a.y();
    const btScalar  yz=a.y()*a.z();
    const btScalar  zx=a.z()*a.x();
    btMatrix3x3     m;
    m[0]=btVector3(1-xx+xx*s,xy*s-xy,zx*s-zx);
    m[1]=btVector3(xy*s-xy,1-yy+yy*s,yz*s-yz);
    m[2]=btVector3(zx*s-zx,yz*s-yz,1-zz+zz*s);
    return(m);
}
//
static inline btMatrix3x3   Cross(const btVector3& v)
{
    btMatrix3x3 m;
    m[0]=btVector3(0,-v.z(),+v.y());
    m[1]=btVector3(+v.z(),0,-v.x());
    m[2]=btVector3(-v.y(),+v.x(),0);
    return(m);
}
//
static inline btMatrix3x3   Diagonal(btScalar x)
{
    btMatrix3x3 m;
    m[0]=btVector3(x,0,0);
    m[1]=btVector3(0,x,0);
    m[2]=btVector3(0,0,x);
    return(m);
}
//
static inline btMatrix3x3   Add(const btMatrix3x3& a,
                                const btMatrix3x3& b)
{
    btMatrix3x3 r;
    for(int i=0;i<3;++i) r[i]=a[i]+b[i];
    return(r);
}
//
static inline btMatrix3x3   Sub(const btMatrix3x3& a,
                                const btMatrix3x3& b)
{
    btMatrix3x3 r;
    for(int i=0;i<3;++i) r[i]=a[i]-b[i];
    return(r);
}
//
static inline btMatrix3x3   Mul(const btMatrix3x3& a,
                                btScalar b)
{
    btMatrix3x3 r;
    for(int i=0;i<3;++i) r[i]=a[i]*b;
    return(r);
}
//
static inline void          Orthogonalize(btMatrix3x3& m)
{
    m[2]=btCross(m[0],m[1]).normalized();
    m[1]=btCross(m[2],m[0]).normalized();
    m[0]=btCross(m[1],m[2]).normalized();
}
//
static inline btMatrix3x3   MassMatrix(btScalar im,const btMatrix3x3& iwi,const btVector3& r)
{
    const btMatrix3x3   cr=Cross(r);
    return(Sub(Diagonal(im),cr*iwi*cr));
}

//
static inline btMatrix3x3   ImpulseMatrix(  btScalar dt,
                                          btScalar ima,
                                          btScalar imb,
                                          const btMatrix3x3& iwi,
                                          const btVector3& r)
{
    return(Diagonal(1/dt)*Add(Diagonal(ima),MassMatrix(imb,iwi,r)).inverse());
}

//
static inline btMatrix3x3   ImpulseMatrix(  btScalar ima,const btMatrix3x3& iia,const btVector3& ra,
                                          btScalar imb,const btMatrix3x3& iib,const btVector3& rb)  
{
    return(Add(MassMatrix(ima,iia,ra),MassMatrix(imb,iib,rb)).inverse());
}

//
static inline btMatrix3x3   AngularImpulseMatrix(   const btMatrix3x3& iia,
                                                 const btMatrix3x3& iib)
{
    return(Add(iia,iib).inverse());
}

//
static inline btVector3     ProjectOnAxis(  const btVector3& v,
                                          const btVector3& a)
{
    return(a*btDot(v,a));
}
//
static inline btVector3     ProjectOnPlane( const btVector3& v,
                                           const btVector3& a)
{
    return(v-ProjectOnAxis(v,a));
}

//
static inline void          ProjectOrigin(  const btVector3& a,
                                          const btVector3& b,
                                          btVector3& prj,
                                          btScalar& sqd)
{
    const btVector3 d=b-a;
    const btScalar  m2=d.length2();
    if(m2>SIMD_EPSILON)
    {   
        const btScalar  t=Clamp<btScalar>(-btDot(a,d)/m2,0,1);
        const btVector3 p=a+d*t;
        const btScalar  l2=p.length2();
        if(l2<sqd)
        {
            prj=p;
            sqd=l2;
        }
    }
}
//
static inline void          ProjectOrigin(  const btVector3& a,
                                          const btVector3& b,
                                          const btVector3& c,
                                          btVector3& prj,
                                          btScalar& sqd)
{
    const btVector3&    q=btCross(b-a,c-a);
    const btScalar      m2=q.length2();
    if(m2>SIMD_EPSILON)
    {
        const btVector3 n=q/btSqrt(m2);
        const btScalar  k=btDot(a,n);
        const btScalar  k2=k*k;
        if(k2<sqd)
        {
            const btVector3 p=n*k;
            if( (btDot(btCross(a-p,b-p),q)>0)&&
                (btDot(btCross(b-p,c-p),q)>0)&&
                (btDot(btCross(c-p,a-p),q)>0))
            {           
                prj=p;
                sqd=k2;
            }
            else
            {
                ProjectOrigin(a,b,prj,sqd);
                ProjectOrigin(b,c,prj,sqd);
                ProjectOrigin(c,a,prj,sqd);
            }
        }
    }
}

//
template <typename T>
static inline T             BaryEval(       const T& a,
                                     const T& b,
                                     const T& c,
                                     const btVector3& coord)
{
    return(a*coord.x()+b*coord.y()+c*coord.z());
}
//
static inline btVector3     BaryCoord(  const btVector3& a,
                                      const btVector3& b,
                                      const btVector3& c,
                                      const btVector3& p)
{
    const btScalar  w[]={   btCross(a-p,b-p).length(),
        btCross(b-p,c-p).length(),
        btCross(c-p,a-p).length()};
    const btScalar  isum=1/(w[0]+w[1]+w[2]);
    return(btVector3(w[1]*isum,w[2]*isum,w[0]*isum));
}

//
static btScalar             ImplicitSolve(  btSoftBody::ImplicitFn* fn,
                                          const btVector3& a,
                                          const btVector3& b,
                                          const btScalar accuracy,
                                          const int maxiterations=256)
{
    btScalar    span[2]={0,1};
    btScalar    values[2]={fn->Eval(a),fn->Eval(b)};
    if(values[0]>values[1])
    {
        btSwap(span[0],span[1]);
        btSwap(values[0],values[1]);
    }
    if(values[0]>-accuracy) return(-1);
    if(values[1]<+accuracy) return(-1);
    for(int i=0;i<maxiterations;++i)
    {
        const btScalar  t=Lerp(span[0],span[1],values[0]/(values[0]-values[1]));
        const btScalar  v=fn->Eval(Lerp(a,b,t));
        if((t<=0)||(t>=1))      break;
        if(btFabs(v)<accuracy)  return(t);
        if(v<0)
        { span[0]=t;values[0]=v; }
        else
        { span[1]=t;values[1]=v; }
    }
    return(-1);
}

//
static inline btVector3     NormalizeAny(const btVector3& v)
{
    const btScalar l=v.length();
    if(l>SIMD_EPSILON)
        return(v/l);
    else
        return(btVector3(0,0,0));
}

//
static inline btDbvtVolume  VolumeOf(   const btSoftBody::Face& f,
                                     btScalar margin)
{
    const btVector3*    pts[]={ &f.m_n[0]->m_x,
        &f.m_n[1]->m_x,
        &f.m_n[2]->m_x};
    btDbvtVolume        vol=btDbvtVolume::FromPoints(pts,3);
    vol.Expand(btVector3(margin,margin,margin));
    return(vol);
}

//
static inline btVector3         CenterOf(   const btSoftBody::Face& f)
{
    return((f.m_n[0]->m_x+f.m_n[1]->m_x+f.m_n[2]->m_x)/3);
}

//
static inline btScalar          AreaOf(     const btVector3& x0,
                                       const btVector3& x1,
                                       const btVector3& x2)
{
    const btVector3 a=x1-x0;
    const btVector3 b=x2-x0;
    const btVector3 cr=btCross(a,b);
    const btScalar  area=cr.length();
    return(area);
}

//
static inline btScalar      VolumeOf(   const btVector3& x0,
                                     const btVector3& x1,
                                     const btVector3& x2,
                                     const btVector3& x3)
{
    const btVector3 a=x1-x0;
    const btVector3 b=x2-x0;
    const btVector3 c=x3-x0;
    return(btDot(a,btCross(b,c)));
}

//
static void                 EvaluateMedium( const btSoftBodyWorldInfo* wfi,
                                           const btVector3& x,
                                           btSoftBody::sMedium& medium)
{
    medium.m_velocity   =   btVector3(0,0,0);
    medium.m_pressure   =   0;
    medium.m_density    =   wfi->air_density;
    if(wfi->water_density>0)
    {
        const btScalar  depth=-(btDot(x,wfi->water_normal)+wfi->water_offset);
        if(depth>0)
        {
            medium.m_density    =   wfi->water_density;
            medium.m_pressure   =   depth*wfi->water_density*wfi->m_gravity.length();
        }
    }
}

//
static inline void          ApplyClampedForce(  btSoftBody::Node& n,
                                              const btVector3& f,
                                              btScalar dt)
{
    const btScalar  dtim=dt*n.m_im;
    if((f*dtim).length2()>n.m_v.length2())
    {/* Clamp   */ 
        n.m_f-=ProjectOnAxis(n.m_v,f.normalized())/dtim;                        
    }
    else
    {/* Apply   */ 
        n.m_f+=f;
    }
}

//
static inline int       MatchEdge(  const btSoftBody::Node* a,
                                  const btSoftBody::Node* b,
                                  const btSoftBody::Node* ma,
                                  const btSoftBody::Node* mb)
{
    if((a==ma)&&(b==mb)) return(0);
    if((a==mb)&&(b==ma)) return(1);
    return(-1);
}

//
// btEigen : Extract eigen system,
// straitforward implementation of http://math.fullerton.edu/mathews/n2003/JacobiMethodMod.html
// outputs are NOT sorted.
//
struct  btEigen
{
    static int          system(btMatrix3x3& a,btMatrix3x3* vectors,btVector3* values=0)
    {
        static const int        maxiterations=16;
        static const btScalar   accuracy=(btScalar)0.0001;
        btMatrix3x3&            v=*vectors;
        int                     iterations=0;
        vectors->setIdentity();
        do  {
            int             p=0,q=1;
            if(btFabs(a[p][q])<btFabs(a[0][2])) { p=0;q=2; }
            if(btFabs(a[p][q])<btFabs(a[1][2])) { p=1;q=2; }
            if(btFabs(a[p][q])>accuracy)
            {
                const btScalar  w=(a[q][q]-a[p][p])/(2*a[p][q]);
                const btScalar  z=btFabs(w);
                const btScalar  t=w/(z*(btSqrt(1+w*w)+z));
                if(t==t)/* [WARNING] let hope that one does not get thrown aways by some compilers... */ 
                {
                    const btScalar  c=1/btSqrt(t*t+1);
                    const btScalar  s=c*t;
                    mulPQ(a,c,s,p,q);
                    mulTPQ(a,c,s,p,q);
                    mulPQ(v,c,s,p,q);
                } else break;
            } else break;
        } while((++iterations)<maxiterations);
        if(values)
        {
            *values=btVector3(a[0][0],a[1][1],a[2][2]);
        }
        return(iterations);
    }
private:
    static inline void  mulTPQ(btMatrix3x3& a,btScalar c,btScalar s,int p,int q)
    {
        const btScalar  m[2][3]={   {a[p][0],a[p][1],a[p][2]},
        {a[q][0],a[q][1],a[q][2]}};
        int i;

        for(i=0;i<3;++i) a[p][i]=c*m[0][i]-s*m[1][i];
        for(i=0;i<3;++i) a[q][i]=c*m[1][i]+s*m[0][i];
    }
    static inline void  mulPQ(btMatrix3x3& a,btScalar c,btScalar s,int p,int q)
    {
        const btScalar  m[2][3]={   {a[0][p],a[1][p],a[2][p]},
        {a[0][q],a[1][q],a[2][q]}};
        int i;

        for(i=0;i<3;++i) a[i][p]=c*m[0][i]-s*m[1][i];
        for(i=0;i<3;++i) a[i][q]=c*m[1][i]+s*m[0][i];
    }
};

//
// Polar decomposition,
// "Computing the Polar Decomposition with Applications", Nicholas J. Higham, 1986.
//
static inline int           PolarDecompose( const btMatrix3x3& m,btMatrix3x3& q,btMatrix3x3& s)
{
    static const btPolarDecomposition polar;  
    return polar.decompose(m, q, s);
}

//
// btSoftColliders
//
struct btSoftColliders
{
    //
    // ClusterBase
    //
    struct  ClusterBase : btDbvt::ICollide
    {
        btScalar            erp;
        btScalar            idt;
        btScalar            m_margin;
        btScalar            friction;
        btScalar            threshold;
        ClusterBase()
        {
            erp         =(btScalar)1;
            idt         =0;
            m_margin        =0;
            friction    =0;
            threshold   =(btScalar)0;
        }
        bool                SolveContact(   const btGjkEpaSolver2::sResults& res,
            btSoftBody::Body ba,const btSoftBody::Body bb,
            btSoftBody::CJoint& joint)
        {
            if(res.distance<m_margin)
            {
                btVector3 norm = res.normal;
                norm.normalize();//is it necessary?

                const btVector3     ra=res.witnesses[0]-ba.xform().getOrigin();
                const btVector3     rb=res.witnesses[1]-bb.xform().getOrigin();
                const btVector3     va=ba.velocity(ra);
                const btVector3     vb=bb.velocity(rb);
                const btVector3     vrel=va-vb;
                const btScalar      rvac=btDot(vrel,norm);
                 btScalar       depth=res.distance-m_margin;
                
//              printf("depth=%f\n",depth);
                const btVector3     iv=norm*rvac;
                const btVector3     fv=vrel-iv;
                joint.m_bodies[0]   =   ba;
                joint.m_bodies[1]   =   bb;
                joint.m_refs[0]     =   ra*ba.xform().getBasis();
                joint.m_refs[1]     =   rb*bb.xform().getBasis();
                joint.m_rpos[0]     =   ra;
                joint.m_rpos[1]     =   rb;
                joint.m_cfm         =   1;
                joint.m_erp         =   1;
                joint.m_life        =   0;
                joint.m_maxlife     =   0;
                joint.m_split       =   1;
                
                joint.m_drift       =   depth*norm;

                joint.m_normal      =   norm;
//              printf("normal=%f,%f,%f\n",res.normal.getX(),res.normal.getY(),res.normal.getZ());
                joint.m_delete      =   false;
                joint.m_friction    =   fv.length2()<(rvac*friction*rvac*friction)?1:friction;
                joint.m_massmatrix  =   ImpulseMatrix(  ba.invMass(),ba.invWorldInertia(),joint.m_rpos[0],
                    bb.invMass(),bb.invWorldInertia(),joint.m_rpos[1]);

                return(true);
            }
            return(false);
        }
    };
    //
    // CollideCL_RS
    //
    struct  CollideCL_RS : ClusterBase
    {
        btSoftBody*     psb;
        const btCollisionObjectWrapper* m_colObjWrap;

        void        Process(const btDbvtNode* leaf)
        {
            btSoftBody::Cluster*        cluster=(btSoftBody::Cluster*)leaf->data;
            btSoftClusterCollisionShape cshape(cluster);
            
            const btConvexShape*        rshape=(const btConvexShape*)m_colObjWrap->getCollisionShape();

            ///don't collide an anchored cluster with a static/kinematic object
            if(m_colObjWrap->getCollisionObject()->isStaticOrKinematicObject() && cluster->m_containsAnchor)
                return;

            btGjkEpaSolver2::sResults   res;        
            if(btGjkEpaSolver2::SignedDistance( &cshape,btTransform::getIdentity(),
                rshape,m_colObjWrap->getWorldTransform(),
                btVector3(1,0,0),res))
            {
                btSoftBody::CJoint  joint;
                if(SolveContact(res,cluster,m_colObjWrap->getCollisionObject(),joint))//prb,joint))
                {
                    btSoftBody::CJoint* pj=new(btAlignedAlloc(sizeof(btSoftBody::CJoint),16)) btSoftBody::CJoint();
                    *pj=joint;psb->m_joints.push_back(pj);
                    if(m_colObjWrap->getCollisionObject()->isStaticOrKinematicObject())
                    {
                        pj->m_erp   *=  psb->m_cfg.kSKHR_CL;
                        pj->m_split *=  psb->m_cfg.kSK_SPLT_CL;
                    }
                    else
                    {
                        pj->m_erp   *=  psb->m_cfg.kSRHR_CL;
                        pj->m_split *=  psb->m_cfg.kSR_SPLT_CL;
                    }
                }
            }
        }
        void        ProcessColObj(btSoftBody* ps,const btCollisionObjectWrapper* colObWrap)
        {
            psb         =   ps;
            m_colObjWrap            =   colObWrap;
            idt         =   ps->m_sst.isdt;
            m_margin        =   m_colObjWrap->getCollisionShape()->getMargin()+psb->getCollisionShape()->getMargin();
            ///Bullet rigid body uses multiply instead of minimum to determine combined friction. Some customization would be useful.
            friction    =   btMin(psb->m_cfg.kDF,m_colObjWrap->getCollisionObject()->getFriction());
            btVector3           mins;
            btVector3           maxs;

            ATTRIBUTE_ALIGNED16(btDbvtVolume)       volume;
            colObWrap->getCollisionShape()->getAabb(colObWrap->getWorldTransform(),mins,maxs);
            volume=btDbvtVolume::FromMM(mins,maxs);
            volume.Expand(btVector3(1,1,1)*m_margin);
            ps->m_cdbvt.collideTV(ps->m_cdbvt.m_root,volume,*this);
        }   
    };
    //
    // CollideCL_SS
    //
    struct  CollideCL_SS : ClusterBase
    {
        btSoftBody* bodies[2];
        void        Process(const btDbvtNode* la,const btDbvtNode* lb)
        {
            btSoftBody::Cluster*        cla=(btSoftBody::Cluster*)la->data;
            btSoftBody::Cluster*        clb=(btSoftBody::Cluster*)lb->data;


            bool connected=false;
            if ((bodies[0]==bodies[1])&&(bodies[0]->m_clusterConnectivity.size()))
            {
                connected = bodies[0]->m_clusterConnectivity[cla->m_clusterIndex+bodies[0]->m_clusters.size()*clb->m_clusterIndex];
            }

            if (!connected)
            {
                btSoftClusterCollisionShape csa(cla);
                btSoftClusterCollisionShape csb(clb);
                btGjkEpaSolver2::sResults   res;        
                if(btGjkEpaSolver2::SignedDistance( &csa,btTransform::getIdentity(),
                    &csb,btTransform::getIdentity(),
                    cla->m_com-clb->m_com,res))
                {
                    btSoftBody::CJoint  joint;
                    if(SolveContact(res,cla,clb,joint))
                    {
                        btSoftBody::CJoint* pj=new(btAlignedAlloc(sizeof(btSoftBody::CJoint),16)) btSoftBody::CJoint();
                        *pj=joint;bodies[0]->m_joints.push_back(pj);
                        pj->m_erp   *=  btMax(bodies[0]->m_cfg.kSSHR_CL,bodies[1]->m_cfg.kSSHR_CL);
                        pj->m_split *=  (bodies[0]->m_cfg.kSS_SPLT_CL+bodies[1]->m_cfg.kSS_SPLT_CL)/2;
                    }
                }
            } else
            {
                static int count=0;
                count++;
                //printf("count=%d\n",count);
                
            }
        }
        void        ProcessSoftSoft(btSoftBody* psa,btSoftBody* psb)
        {
            idt         =   psa->m_sst.isdt;
            //m_margin      =   (psa->getCollisionShape()->getMargin()+psb->getCollisionShape()->getMargin())/2;
            m_margin        =   (psa->getCollisionShape()->getMargin()+psb->getCollisionShape()->getMargin());
            friction    =   btMin(psa->m_cfg.kDF,psb->m_cfg.kDF);
            bodies[0]   =   psa;
            bodies[1]   =   psb;
            psa->m_cdbvt.collideTT(psa->m_cdbvt.m_root,psb->m_cdbvt.m_root,*this);
        }   
    };
    //
    // CollideSDF_RS
    //
    struct  CollideSDF_RS : btDbvt::ICollide
    {
        void        Process(const btDbvtNode* leaf)
        {
            btSoftBody::Node*   node=(btSoftBody::Node*)leaf->data;
            DoNode(*node);
        }
        void        DoNode(btSoftBody::Node& n) const
        {
            const btScalar          m=n.m_im>0?dynmargin:stamargin;
            btSoftBody::RContact    c;

            if( (!n.m_battach)&&
                psb->checkContact(m_colObj1Wrap,n.m_x,m,c.m_cti))
            {
                const btScalar  ima=n.m_im;
                const btScalar  imb= m_rigidBody? m_rigidBody->getInvMass() : 0.f;
                const btScalar  ms=ima+imb;
                if(ms>0)
                {
                    const btTransform&  wtr=m_rigidBody?m_rigidBody->getWorldTransform() : m_colObj1Wrap->getCollisionObject()->getWorldTransform();
                    static const btMatrix3x3    iwiStatic(0,0,0,0,0,0,0,0,0);
                    const btMatrix3x3&  iwi=m_rigidBody?m_rigidBody->getInvInertiaTensorWorld() : iwiStatic;
                    const btVector3     ra=n.m_x-wtr.getOrigin();
                    const btVector3     va=m_rigidBody ? m_rigidBody->getVelocityInLocalPoint(ra)*psb->m_sst.sdt : btVector3(0,0,0);
                    const btVector3     vb=n.m_x-n.m_q; 
                    const btVector3     vr=vb-va;
                    const btScalar      dn=btDot(vr,c.m_cti.m_normal);
                    const btVector3     fv=vr-c.m_cti.m_normal*dn;
                    const btScalar      fc=psb->m_cfg.kDF*m_colObj1Wrap->getCollisionObject()->getFriction();
                    c.m_node    =   &n;
                    c.m_c0      =   ImpulseMatrix(psb->m_sst.sdt,ima,imb,iwi,ra);
                    c.m_c1      =   ra;
                    c.m_c2      =   ima*psb->m_sst.sdt;
                    c.m_c3      =   fv.length2()<(dn*fc*dn*fc)?0:1-fc;
                    c.m_c4      =   m_colObj1Wrap->getCollisionObject()->isStaticOrKinematicObject()?psb->m_cfg.kKHR:psb->m_cfg.kCHR;
                    psb->m_rcontacts.push_back(c);
                    if (m_rigidBody)
                        m_rigidBody->activate();
                }
            }
        }
        btSoftBody*     psb;
        const btCollisionObjectWrapper* m_colObj1Wrap;
        btRigidBody*    m_rigidBody;
        btScalar        dynmargin;
        btScalar        stamargin;
    };
    //
    // CollideVF_SS
    //
    struct  CollideVF_SS : btDbvt::ICollide
    {
        void        Process(const btDbvtNode* lnode,
            const btDbvtNode* lface)
        {
            btSoftBody::Node*   node=(btSoftBody::Node*)lnode->data;
            btSoftBody::Face*   face=(btSoftBody::Face*)lface->data;
            btVector3           o=node->m_x;
            btVector3           p;
            btScalar            d=SIMD_INFINITY;
            ProjectOrigin(  face->m_n[0]->m_x-o,
                face->m_n[1]->m_x-o,
                face->m_n[2]->m_x-o,
                p,d);
            const btScalar  m=mrg+(o-node->m_q).length()*2;
            if(d<(m*m))
            {
                const btSoftBody::Node* n[]={face->m_n[0],face->m_n[1],face->m_n[2]};
                const btVector3         w=BaryCoord(n[0]->m_x,n[1]->m_x,n[2]->m_x,p+o);
                const btScalar          ma=node->m_im;
                btScalar                mb=BaryEval(n[0]->m_im,n[1]->m_im,n[2]->m_im,w);
                if( (n[0]->m_im<=0)||
                    (n[1]->m_im<=0)||
                    (n[2]->m_im<=0))
                {
                    mb=0;
                }
                const btScalar  ms=ma+mb;
                if(ms>0)
                {
                    btSoftBody::SContact    c;
                    c.m_normal      =   p/-btSqrt(d);
                    c.m_margin      =   m;
                    c.m_node        =   node;
                    c.m_face        =   face;
                    c.m_weights     =   w;
                    c.m_friction    =   btMax(psb[0]->m_cfg.kDF,psb[1]->m_cfg.kDF);
                    c.m_cfm[0]      =   ma/ms*psb[0]->m_cfg.kSHR;
                    c.m_cfm[1]      =   mb/ms*psb[1]->m_cfg.kSHR;
                    psb[0]->m_scontacts.push_back(c);
                }
            }   
        }
        btSoftBody*     psb[2];
        btScalar        mrg;
    };
};

#endif //_BT_SOFT_BODY_INTERNALS_H
