Bullet's SoftBody physics example


#1

I became interested in porting bullet’s softbody physics after reading Josh’s post, http://discourse.urho3d.io/t/soft-body-physics/1313/1.
And the thing that I was really interested in was TriMesh softbody implementation, curious as to whether the softbody needed to be recreated every frame or if just updating the model’s vertex buffer would work.
This example shows trimesh softbody implementation that updates model’s vertex buffer. It’s no where near code complete, as I have neglected to write methods like settransform(), setmass(), etc., and I coded SoftBody class in PhysicsWorld.h/.cpp for ease of testing. But I decided to share this as there are others who are more interested in this than myself.

Edit: progress update 09/20/15
-This progress is collaborative efforts made by Mike, codingmonkey, and myself - this will eventually make it into the master branch, hopefully. I found out that there is no way to remove duplicate verts from Blender. So, I wrote a duplicate verts removal (pruning) routine and apply the softbody deformation back to the original model’s vertex buffer. The attached video shows this work.

Changes to the PhysicsWorld.h

. . .
class SoftBody;
. . .
class URHO3D_API PhysicsWorld : public Component, public btIDebugDraw
{
. . .

    btSoftBodyWorldInfo* GetSoftBodyInfo() { return m_softBodyWorldInfo; }
    void AddSoftBody(SoftBody *_pSoftBody);

. . .

    // softbody
    btSoftBodyWorldInfo	*m_softBodyWorldInfo;
    PODVector<SoftBody*> m_vpbtSoftBody;
};

class SoftBody : public Component
{
    OBJECT( SoftBody );
public:
    SoftBody(Context *_context);
    ~SoftBody();

    static void RegisterObject(Context* context);

    void ReleaseBody();

    bool CreateBodyFromTriMesh(VertexBuffer *_pVertexBuffer, IndexBuffer *_pIndexBuffer, bool randomizeConstraints = true);

    void UpdateVertexBuffer();
    btSoftBody* GetBody() { return m_pbtSoftBody; }

protected:
    virtual void OnNodeSet(Node* node);
    void AddBodyToWorld();
    void RemoveBodyFromWorld();

protected:
    WeakPtr<PhysicsWorld>   physicsWorld_;
    btSoftBody              *m_pbtSoftBody;
    VertexBuffer            *m_pVertexBuffer;
};

Changes to PhysicsWorld.cpp

#include <Bullet/BulletSoftBody/btSoftBody.h>
#include <Bullet/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h>
#include <Bullet/BulletSoftBody/btSoftRigidDynamicsWorld.h>
#include <Bullet/BulletSoftBody/btSoftBodyHelpers.h>
#define TEST_SOFTBODY

// changes in the constructor
PhysicsWorld::PhysicsWorld(Context* context) 
. . .
{
#ifndef TEST_SOFTBODY
        collisionConfiguration_ = new btDefaultCollisionConfiguration();
#else
	collisionConfiguration_ = new btSoftBodyRigidBodyCollisionConfiguration();
#endif

. . .
#ifndef TEST_SOFTBODY
        world_ = new btDiscreteDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_);
#else
	world_ = new btSoftRigidDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_, NULL);
#endif

. . .
#ifdef TEST_SOFTBODY
    m_softBodyWorldInfo = new btSoftBodyWorldInfo();
    m_softBodyWorldInfo->m_dispatcher = collisionDispatcher_;
    m_softBodyWorldInfo->m_broadphase = broadphase_;
    m_softBodyWorldInfo->air_density	=	(btScalar)1.2;
    m_softBodyWorldInfo->water_density	=	0;
    m_softBodyWorldInfo->water_offset	=	0;
    m_softBodyWorldInfo->water_normal	=	btVector3(0,0,0);
    m_softBodyWorldInfo->m_gravity.setValue(0,-10,0);
    m_softBodyWorldInfo->m_sparsesdf.Initialize();

    m_vpbtSoftBody.Clear();
#endif
}

// changes in dtor()
PhysicsWorld::~PhysicsWorld()
{
. . .
        for (PODVector<SoftBody*>::Iterator i = m_vpbtSoftBody.Begin(); i != m_vpbtSoftBody.End(); ++i)
            (*i)->ReleaseBody();

. . .
     if ( softBodyWorldInfo )
    {
	m_softBodyWorldInfo->m_dispatcher = 0;
	m_softBodyWorldInfo->m_broadphase = 0;

      delete m_softBodyWorldInfo;
      m_softBodyWorldInfo = 0;
    }


}
. . .

void PhysicsWorld::AddSoftBody(SoftBody *_pSoftBody)
{
    btSoftRigidDynamicsWorld *pSoftRigidWorld = (btSoftRigidDynamicsWorld*)world_; 
    pSoftRigidWorld->addSoftBody( _pSoftBody->GetBody() );

    m_vpbtSoftBody.Push( _pSoftBody );
}


void RegisterPhysicsLibrary(Context* context)
{
    CollisionShape::RegisterObject(context);
    RigidBody::RegisterObject(context);
    Constraint::RegisterObject(context);
    PhysicsWorld::RegisterObject(context);
    SoftBody::RegisterObject(context); // register softbody
}

//=============================================================================
//=============================================================================
SoftBody::SoftBody(Context *_context)
    : Component( _context )
    , m_pbtSoftBody( NULL )
    , m_pVertexBuffer( NULL )
{
}

SoftBody::~SoftBody()
{
    if ( m_pbtSoftBody )
    {
        delete m_pbtSoftBody;
        m_pbtSoftBody = NULL;
    }

    // we don't own the vertsbuffer
    m_pVertexBuffer = NULL;
}

void SoftBody::RegisterObject(Context* context)
{
    context->RegisterFactory<SoftBody>(SUBSYSTEM_CATEGORY);
}

void SoftBody::OnNodeSet(Node* node)
{
    if (node)
    {
        Scene* scene = GetScene();
        if (scene)
        {
            if (scene == node)
                LOGWARNING(GetTypeName() + " should not be created to the root scene node");

            physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();

            AddBodyToWorld();
        }
        else
            LOGERROR("Node is detached from scene, can not create rigid body");

        node->AddListener(this);
    }
}

void SoftBody::AddBodyToWorld()
{
    if ( m_pbtSoftBody )
    {
        if ( physicsWorld_ )
        {
            btSoftRigidDynamicsWorld *pSoftRigidWorld = (btSoftRigidDynamicsWorld *)physicsWorld_->GetWorld();
            pSoftRigidWorld->addSoftBody( m_pbtSoftBody );
        }
    }
}

void SoftBody::ReleaseBody()
{
    if ( m_pbtSoftBody )
    {
        RemoveBodyFromWorld();

        delete m_pbtSoftBody;
        m_pbtSoftBody = NULL;
    }
}

void SoftBody::RemoveBodyFromWorld()
{
    if ( m_pbtSoftBody )
    {
        if ( physicsWorld_ )
        {
            btSoftRigidDynamicsWorld *pSoftRigidWorld = (btSoftRigidDynamicsWorld *)physicsWorld_->GetWorld();
            pSoftRigidWorld->removeSoftBody( m_pbtSoftBody );
        }
    }
}

bool SoftBody::CreateBodyFromTriMesh(VertexBuffer *_pVertexBuffer, IndexBuffer *_pIndexBuffer, bool _randomizeConstraints)
{
    bool bConstructed = false;

    if ( _pVertexBuffer && _pIndexBuffer )
    {
        btAlignedObjectArray<bool>		chks;
        btAlignedObjectArray<btVector3>	vtx;

        // save vertexbuffer ptr
        m_pVertexBuffer = _pVertexBuffer;

        // copy vertex buffer
        const unsigned char *pVertexData = (const unsigned char*)m_pVertexBuffer->Lock( 0, m_pVertexBuffer->GetVertexCount() );

        if ( pVertexData )
        {
            unsigned numVertices = m_pVertexBuffer->GetVertexCount();
            unsigned vertexSize = m_pVertexBuffer->GetVertexSize();

            vtx.resize( numVertices );

            // copy the original verts
            for (unsigned i = 0; i < numVertices; ++i)
            {
                const Vector3& src = *reinterpret_cast<const Vector3*>(pVertexData + i * vertexSize);
                vtx[ i ] = ToBtVector3( src );
            }
            m_pVertexBuffer->Unlock();
        }

        // create softbody
        physicsWorld_ = GetScene()->GetComponent<PhysicsWorld>();
		m_pbtSoftBody = new btSoftBody( physicsWorld_->GetSoftBodyInfo(), vtx.size(), &vtx[0], 0);

        // copy indexbuffer
        const unsigned *pIndexData = (const unsigned *)_pIndexBuffer->Lock( 0, _pIndexBuffer->GetIndexCount() );
        const unsigned short *pUShortData = (const unsigned short *)pIndexData;
        if ( pIndexData )
        {
            unsigned numIndeces = _pIndexBuffer->GetIndexCount();
            unsigned indexSize = _pIndexBuffer->GetIndexSize();

            int ntriangles = (int)numIndeces/3;

            int	maxidx=0;
            int i;//,j,ni;

            if ( indexSize == sizeof(unsigned short) )
            {
                for (i = 0; i < (int)numIndeces; ++i)
                {
                    unsigned uidx = pUShortData[ i ];
                    maxidx = Max( uidx, maxidx );
                }
            }
            else if ( indexSize == sizeof(unsigned) )
            {
                for (i = 0; i < (int)numIndeces; ++i)
                {
                    unsigned uidx = pIndexData[ i ];
                    maxidx = Max( uidx, maxidx );
                }
            }
            ++maxidx;
            chks.resize(maxidx*maxidx,false);

            for( i = 0; i < (int)numIndeces; i += 3 )
            {
                int idx[3];
                if ( indexSize == sizeof(unsigned short) )
                {
                    idx[ 0 ] = (int)pUShortData[i];
                    idx[ 1 ] = (int)pUShortData[i + 1];
                    idx[ 2 ] = (int)pUShortData[i + 2];
                }
                else
                {
                    idx[ 0 ] = (int)pIndexData[i];
                    idx[ 1 ] = (int)pIndexData[i + 1];
                    idx[ 2 ] = (int)pIndexData[i + 2];
                }

                #define IDX(_x_,_y_) ((_y_)*maxidx+(_x_))
                for(int j=2,k=0;k<3;j=k++)
                {
                    if(!chks[IDX(idx[j],idx[k])])
                    {
                        chks[IDX(idx[j],idx[k])]=true;
                        chks[IDX(idx[k],idx[j])]=true;
                        m_pbtSoftBody->appendLink( idx[j], idx[k] );
                    }
                }
                #undef IDX
                m_pbtSoftBody->appendFace(idx[0],idx[1],idx[2]);
            }
            _pIndexBuffer->Unlock();
        }

        if ( _randomizeConstraints )
        {
            m_pbtSoftBody->randomizeConstraints();
        }

        // straight out of bullet's softbody demo for trimesh
        m_pbtSoftBody->m_materials[0]->m_kLST = 0.1;
        m_pbtSoftBody->m_cfg.kMT              =	0.05;

        btMatrix3x3	m;
        m.setEulerZYX(0,0,0);

        // create methods for these
        m_pbtSoftBody->transform(btTransform(m,btVector3(0,4,0)));
        m_pbtSoftBody->scale(btVector3(2,2,2));
        m_pbtSoftBody->setTotalMass(50,true);
        m_pbtSoftBody->setPose(true,true); // i don't know what this does

        bConstructed = true;
    }

    return bConstructed;
}

void SoftBody::UpdateVertexBuffer()
{
    if ( m_pbtSoftBody && m_pVertexBuffer )
    {
        unsigned char *pVertexData = (unsigned char*)m_pVertexBuffer->Lock( 0, m_pVertexBuffer->GetVertexCount() );

        // copy softbody verts back into the model vertexbuffer
        if ( pVertexData )
        {
            unsigned numVertices = m_pVertexBuffer->GetVertexCount();
            unsigned vertexSize = m_pVertexBuffer->GetVertexSize();

            // Copy the original vertex positions
            for ( unsigned i = 0; i < m_pbtSoftBody->m_nodes.size(); ++i )
            {
                btSoftBody::Node& n = m_pbtSoftBody->m_nodes[ i ];
                Vector3 &src = *reinterpret_cast<Vector3*>(pVertexData + i * vertexSize);
                src = ToVector3( n.m_x );
            }
            m_pVertexBuffer->Unlock();
        }
    }
}

Game side code. I added this to Physics.h/.cpp in Samples/11_Physics/

SoftBodyComponent

class SoftBodyComponent : public LogicComponent
{
    OBJECT(SoftBodyComponent);
public:
    SoftBodyComponent(Context *_context);
    ~SoftBodyComponent();

    static void RegisterObject(Context* context);

    void CreateTriMesh();
    
    virtual void PostUpdate(float timeStep);

protected:
    WeakPtr<SoftBody>    m_pSoftBody;
};
//=============================================================================
//=============================================================================
SoftBodyComponent::SoftBodyComponent(Context *_context)
    : LogicComponent( _context )
    , m_pSoftBody( NULL )
{
    SetUpdateEventMask( USE_POSTUPDATE );
}

SoftBodyComponent::~SoftBodyComponent()
{
}

void SoftBodyComponent::RegisterObject(Context* context)
{
    context->RegisterFactory<SoftBodyComponent>();
}

void SoftBodyComponent::CreateTriMesh()
{
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    Scene *scene = GetScene();

    // create model, clone of the model, and material
    Node* modelNode = scene->CreateChild( "SomeModel" );
    StaticModel *modelObject = modelNode->CreateComponent<StaticModel>();
    Model *pModel = cache->GetResource<Model>("Models/Mushroom.mdl");
    SharedPtr<Model> cloneModel = pModel->Clone();
    modelObject->SetModel( cloneModel );
    modelObject->SetMaterial(cache->GetResource<Material>("Materials/Mushroom.xml"));
    modelObject->SetCastShadows(true);

    // get the vertex and index buffers from the first geometry's first LOD level
    VertexBuffer *pVbuffer = cloneModel->GetGeometry(0, 0)->GetVertexBuffer(0);
    IndexBuffer *pIbuffer = cloneModel->GetGeometry(0, 0)->GetIndexBuffer();
    Node* softBodyNode = node_->CreateChild("SoftBodyNode");
    m_pSoftBody = softBodyNode->CreateComponent<SoftBody>();
    m_pSoftBody->CreateBodyFromTriMesh( pVbuffer, pIbuffer );
    scene->GetComponent<PhysicsWorld>()->AddSoftBody( m_pSoftBody );
}

void SoftBodyComponent::PostUpdate(float )
{
    if ( m_pSoftBody )
    {
        m_pSoftBody->UpdateVertexBuffer();
    }
}

Somewhere in your main game class

Register the SoftBodyComponent and create the component:

  SoftBodyComponent::RegisterObject(context);

. . .
    Node* softBodyNode = scene_->CreateChild("SomeSoftBody");
    m_pSoftBodyComponent = softBodyNode->CreateComponent<SoftBodyComponent>();
    m_pSoftBodyComponent->CreateTriMesh();

I think that’s everything. Let me know if there’s something missing.


Soft Body Physics
Soft Body Physics
Adding vertex weights to the model file
#2

thanks ill give it a go


#3

Thanks for this example.
Good starting point for moving forward to clothes and hair


#4

That’s hot. Any idea of what the performance will be like on android?


#5

This looks cool Lumak! Thanks for sharing :slight_smile: You should do a PR for next release. Yes, I’m also wondering about android performance.


#6

I’m not sure about performance on android or on any platform for that matter. Some optimization should be made. One is checking to see if a collision deformation has occurred before blindly copying the vertex buffer every frame.
This is my first time working with softbody physics and have concerns with bullet’s implementation in general. While running bullet’s demo, I noticed that objects can easily penetrate or punch-through some surfaces (also observed with the mushroom example). I’m not sure if it’s because of a) its intended to work as implemented on specific softbody types, b) some softbody parameter settings that are not set properly or c) caused by degenerative triangles. Further studies would be required to figure out what all the softbody parameter settings are, what they do, and what the optimal settings would be. If I were to use softbody physics in an application, I would first process models through NVidia’s NvTriStrip, http://www.nvidia.com/object/nvtristrip_library.html, to remove any degenerative triangles and hope that it can also eliminate what looks like tearing when there are, I think, duplicate vertices.
I’m just not all that familiar with softbody physics but hope that there are others in the community who are.


#7

Many thanks for sharing this Lumak :stuck_out_tongue:

For optimization, I will investigate if there is a sleep/rest threshold as there exists for RigidBody.

How do you account for position, rotation and scale? Currently transforms are hard-coded and I have some troubles feeding custom transforms to match softBodyNode transforms.


#8

The code sample that I provided is no where near complete. It was written primarily to test a trimesh softbody functionality. Basic node_ member variable access functions, such as set/get transforms, set scale, etc. were not written. If you want to write those, look for a line with a comment “// create methods for these” in SoftBody::CreateBodyFromTriMesh(…) function and you can see a list of functions that I neglected to write.


#9

Thanks, for now I give up.


#10

Made as a SoftBody component (nothing added, raw extraction, code conventions and simplification):

SoftBody.h

#pragma once

#include "../Scene/Component.h"

class btSoftBody;

namespace Urho3D
{

/// Physics soft body component.
class URHO3D_API SoftBody : public Component
{
    OBJECT(SoftBody);

public:
    /// Construct.
    SoftBody(Context* context);
    /// Destruct. Free the soft body and geometries.
    ~SoftBody();
    /// Register object factory.
    static void RegisterObject(Context* context);

    /// Handle logic post-update event where we update the vertex buffer.
    void HandlePostUpdate(StringHash eventType, VariantMap& eventData);

    /// Remove the soft body.
    void ReleaseBody();
    /// Create TriMesh from model's geometry.
    void CreateTriMesh();
    /// Create the soft body from a TriMesh.
    bool CreateBodyFromTriMesh(VertexBuffer* vertexBuffer, IndexBuffer* indexBuffer, bool randomizeConstraints = true);
    /// Return Bullet soft body.
    btSoftBody* GetBody() { return body_; }
    /// TODO.
    void SetPosition(const Vector3& position);

protected:
    /// Handle node being assigned.
    virtual void OnNodeSet(Node* node);
    /// Handle scene being assigned.
    virtual void OnSceneSet(Scene* scene);
    /// Handle node transform being dirtied.
//    virtual void OnMarkedDirty(Node* node);

private:
    /// Create the soft body, or re-add to the physics world with changed flags. Calls UpdateMass().
    void AddBodyToWorld();
    /// Remove the soft body from the physics world.
    void RemoveBodyFromWorld();

    /// Physics world.
    WeakPtr<PhysicsWorld> physicsWorld_;
    /// Bullet soft body.
    btSoftBody* body_;
    /// Vertex buffer.
    VertexBuffer* vertexBuffer_;
};

}

SoftBody .cpp

#include "../Precompiled.h"

#include "../Core/Context.h"
#include "../Core/CoreEvents.h"
#include "../Graphics/Geometry.h"
#include "../Graphics/IndexBuffer.h"
#include "../IO/Log.h"
#include "../Graphics/Material.h"
#include "../Graphics/Model.h"
#include "../Physics/PhysicsUtils.h"
#include "../Physics/PhysicsWorld.h"
#include "../Resource/ResourceCache.h"
#include "../Scene/Scene.h"
#include "../Scene/SceneEvents.h"
#include "../Physics/SoftBody.h"
#include "../Graphics/StaticModel.h"
#include "../Graphics/VertexBuffer.h"

#include <Bullet/BulletSoftBody/btSoftBody.h>
#include <Bullet/BulletSoftBody/btSoftRigidDynamicsWorld.h>
#include <Bullet/BulletSoftBody/btSoftBodyHelpers.h>

namespace Urho3D
{

extern const char* PHYSICS_CATEGORY;

SoftBody::SoftBody(Context* context) :
    Component(context),
    body_(NULL),
    vertexBuffer_(NULL)
{
}

SoftBody::~SoftBody()
{
    if (body_)
    {
        delete body_;
        body_ = NULL;
    }

    // We don't own the vertsbuffer
    vertexBuffer_ = NULL;
}

void SoftBody::RegisterObject(Context* context)
{
    context->RegisterFactory<SoftBody>(PHYSICS_CATEGORY);
}

void SoftBody::OnNodeSet(Node* node)
{
    if (node)
        node->AddListener(this);
}

void SoftBody::OnSceneSet(Scene* scene)
{
    if (scene)
    {
        if (scene == node_)
            LOGWARNING(GetTypeName() + " should not be created to the root scene node");

        physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();
        physicsWorld_->AddSoftBody(this);

        AddBodyToWorld();
    }
    else
    {
        ReleaseBody();

        if (physicsWorld_)
            physicsWorld_->RemoveSoftBody(this);
    }
}

void SoftBody::AddBodyToWorld()
{
    if (!physicsWorld_)
        return;

    if (body_)
    {
        btSoftRigidDynamicsWorld* world = (btSoftRigidDynamicsWorld*)physicsWorld_->GetWorld();
        world->addSoftBody(body_);
    }
}

void SoftBody::ReleaseBody()
{
    if (body_)
    {
        RemoveBodyFromWorld();
        delete body_;
        body_ = NULL;
    }
}

void SoftBody::RemoveBodyFromWorld()
{
    if (body_)
    {
        if (physicsWorld_)
        {
            btSoftRigidDynamicsWorld* pSoftRigidWorld = (btSoftRigidDynamicsWorld *)physicsWorld_->GetWorld();
            pSoftRigidWorld->removeSoftBody(body_);
        }
    }
}

void SoftBody::CreateTriMesh()
{
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    Scene* scene = GetScene();

    // Get model
    StaticModel* model = node_->GetComponent<StaticModel>();
    if (!model)
        return;
    Model* originalModel = model->GetModel();
    if (!originalModel)
        return;

    // Clone model
    SharedPtr<Model> cloneModel = originalModel->Clone();
    model->SetModel(cloneModel);

    // Get the vertex and index buffers from the first geometry's first LOD level
    VertexBuffer* vertexBuffer = cloneModel->GetGeometry(0, 0)->GetVertexBuffer(0);
    IndexBuffer* indexBuffer = cloneModel->GetGeometry(0, 0)->GetIndexBuffer();

    // Cretae soft body from TriMesh
    CreateBodyFromTriMesh(vertexBuffer, indexBuffer);
}

bool SoftBody::CreateBodyFromTriMesh(VertexBuffer* vertexBuffer, IndexBuffer* indexBuffer, bool randomizeConstraints)
{
    bool bConstructed = false;

    if (vertexBuffer && indexBuffer)
    {
        btAlignedObjectArray<bool> chks;
        btAlignedObjectArray<btVector3> vtx;

        // Save vertexbuffer ptr
        vertexBuffer_ = vertexBuffer;

        // Copy vertex buffer
        const unsigned char* pVertexData = (const unsigned char*)vertexBuffer_->Lock(0, vertexBuffer_->GetVertexCount());

        if (pVertexData)
        {
            unsigned numVertices = vertexBuffer_->GetVertexCount();
            unsigned vertexSize = vertexBuffer_->GetVertexSize();

            vtx.resize(numVertices);

            // Copy the original verts
            for (unsigned i = 0; i < numVertices; ++i)
            {
                const Vector3& src = *reinterpret_cast<const Vector3*>(pVertexData + i * vertexSize);
                vtx[i] = ToBtVector3(src);
            }
            vertexBuffer_->Unlock();
        }

        // Create softbody
        physicsWorld_ = GetScene()->GetComponent<PhysicsWorld>();
        body_ = new btSoftBody(physicsWorld_->GetSoftBodyInfo(), vtx.size(), &vtx[0], 0);

        // Copy indexbuffer
        const unsigned* pIndexData = (const unsigned*)indexBuffer->Lock(0, indexBuffer->GetIndexCount());
        const unsigned short* pUShortData = (const unsigned short*)pIndexData;
        if (pIndexData)
        {
            unsigned numIndices = indexBuffer->GetIndexCount();
            unsigned indexSize = indexBuffer->GetIndexSize();

            int ntriangles = (int)numIndices / 3;

            int maxidx = 0;
            int i; //,j,ni;

            if (indexSize == sizeof(unsigned short))
            {
                for (i = 0; i < (int)numIndices; ++i)
                {
                    unsigned uidx = pUShortData[i];
                    maxidx = Max(uidx, maxidx);
                }
            }
            else if (indexSize == sizeof(unsigned))
            {
                for (i = 0; i < (int)numIndices; ++i)
                {
                    unsigned uidx = pIndexData[i];
                    maxidx = Max(uidx, maxidx);
                }
            }
            ++maxidx;
            chks.resize(maxidx * maxidx, false);

            for (i = 0; i < (int)numIndices; i += 3)
            {
                int idx[3];
                if (indexSize == sizeof(unsigned short))
                {
                    idx[0] = (int)pUShortData[i];
                    idx[1] = (int)pUShortData[i + 1];
                    idx[2] = (int)pUShortData[i + 2];
                }
                else
                {
                    idx[0] = (int)pIndexData[i];
                    idx[1] = (int)pIndexData[i + 1];
                    idx[2] = (int)pIndexData[i + 2];
                }

                #define IDX(_x_, _y_) ((_y_) * maxidx + (_x_))
                for (int j=2, k=0; k<3; j = k++)
                {
                    if (!chks[IDX(idx[j], idx[k])])
                    {
                        chks[IDX(idx[j], idx[k])] = true;
                        chks[IDX(idx[k], idx[j])] = true;
                        body_->appendLink(idx[j], idx[k]);
                    }
                }
                #undef IDX
                body_->appendFace(idx[0], idx[1], idx[2]);
            }
            indexBuffer->Unlock();
        }

        if (randomizeConstraints)
            body_->randomizeConstraints();

        // Straight out of Bullet's softbody demo for trimesh
        body_->m_materials[0]->m_kLST = 0.1;
        body_->m_cfg.kMT = 0.05;

        btMatrix3x3 m;
        m.setEulerZYX(0, 0, 0);

        // Create methods for these
        body_->transform(btTransform(m, btVector3(0, 4, 0)));
        body_->scale(btVector3(2, 2, 2));
        body_->setTotalMass(50, true);
        body_->setPose(true, true);

        bConstructed = true;
    }

    AddBodyToWorld();
    SubscribeToEvent(E_POSTUPDATE, HANDLER(SoftBody, HandlePostUpdate));

    return bConstructed;
}

void SoftBody::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
{
    // Update vertex buffer
    if (body_ && vertexBuffer_)
    {
        unsigned char* pVertexData = (unsigned char*)vertexBuffer_->Lock(0, vertexBuffer_->GetVertexCount());

        // Copy soft body vertices back into the model vertex buffer
        if (pVertexData)
        {
            unsigned numVertices = vertexBuffer_->GetVertexCount();
            unsigned vertexSize = vertexBuffer_->GetVertexSize();

            // Copy original vertex positions
            for (unsigned i = 0; i < body_->m_nodes.size(); ++i)
            {
                btSoftBody::Node& n = body_->m_nodes[i];
                Vector3& src = *reinterpret_cast<Vector3*>(pVertexData + i * vertexSize);
                src = ToVector3(n.m_x);
            }
            vertexBuffer_->Unlock();
        }
    }
}

void SoftBody::SetPosition(const Vector3& position)
{
    if (body_)
    {
        body_->transform(btTransform(btQuaternion::getIdentity(), ToBtVector3(position)));
        MarkNetworkUpdate();
    }
}

}

11_Physics.h

#include <Urho3D/Physics/SoftBody.h>

11_Physics.cpp

    // Create mushroom
    Node* softBodyNode = scene_->CreateChild("SomeSoftBody");
    StaticModel* model = softBodyNode->CreateComponent<StaticModel>();
    model->SetModel(cache->GetResource<Model>("Models/Mushroom.mdl"));
    model->SetMaterial(cache->GetResource<Material>("Materials/Mushroom.xml"));
    model->SetCastShadows(true);

    // Create SoftBody component
    SoftBody* body = softBodyNode->CreateComponent<SoftBody>();
    body->CreateTriMesh();

#11

PhysicsWorld.h

[spoiler][code]
//
// Copyright © 2008-2015 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the “Software”), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#pragma once

#include “…/Container/HashSet.h”
#include “…/IO/VectorBuffer.h”
#include “…/Math/BoundingBox.h”
#include “…/Math/Sphere.h”
#include “…/Math/Vector3.h”
#include “…/Scene/Component.h”

#include <Bullet/LinearMath/btIDebugDraw.h>

class btCollisionConfiguration;
class btCollisionShape;
class btBroadphaseInterface;
class btConstraintSolver;
class btDiscreteDynamicsWorld;
class btDispatcher;
class btDynamicsWorld;
class btPersistentManifold;
class btSoftBodyWorldInfo; //===============================

namespace Urho3D
{

class CollisionShape;
class Deserializer;
class Constraint;
class Model;
class Node;
class Ray;
class RigidBody;
class Scene;
class Serializer;
class XMLElement;

//=====================================
class SoftBody;
class IndexBuffer;
class VertexBuffer;
//=====================================

struct CollisionGeometryData;

/// Physics raycast hit.
struct URHO3D_API PhysicsRaycastResult
{
/// Construct with defaults.
PhysicsRaycastResult() :
body_(0)
{
}

/// Test for inequality, added to prevent GCC from complaining.
bool operator !=(const PhysicsRaycastResult& rhs) const
{
    return position_ != rhs.position_ || normal_ != rhs.normal_ || distance_ != rhs.distance_ || body_ != rhs.body_;
}

/// Hit worldspace position.
Vector3 position_;
/// Hit worldspace normal.
Vector3 normal_;
/// Hit distance from ray origin.
float distance_;
/// Rigid body that was hit.
RigidBody* body_;

};

/// Delayed world transform assignment for parented rigidbodies.
struct DelayedWorldTransform
{
/// Rigid body.
RigidBody* rigidBody_;
/// Parent rigid body.
RigidBody* parentRigidBody_;
/// New world position.
Vector3 worldPosition_;
/// New world rotation.
Quaternion worldRotation_;
};

static const float DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY = 100.0f;

/// Physics simulation world component. Should be added only to the root scene node.
class URHO3D_API PhysicsWorld : public Component, public btIDebugDraw
{
OBJECT(PhysicsWorld);

friend void InternalPreTickCallback(btDynamicsWorld* world, btScalar timeStep);
friend void InternalTickCallback(btDynamicsWorld* world, btScalar timeStep);

public:
/// Construct.
PhysicsWorld(Context* scontext);
/// Destruct.
virtual ~PhysicsWorld();
/// Register object factory.
static void RegisterObject(Context* context);

//=========================================
// softbody
btSoftBodyWorldInfo* GetSoftBodyInfo() { return m_softBodyWorldInfo; }
btSoftBodyWorldInfo* m_softBodyWorldInfo;
//=========================================

/// Check if an AABB is visible for debug drawing.
virtual bool isVisible(const btVector3& aabbMin, const btVector3& aabbMax);
/// Draw a physics debug line.
virtual void drawLine(const btVector3& from, const btVector3& to, const btVector3& color);
/// Log warning from the physics engine.
virtual void reportErrorWarning(const char* warningString);
/// Draw a physics debug contact point. Not implemented.
virtual void drawContactPoint
    (const btVector3& pointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color);
/// Draw physics debug 3D text. Not implemented.
virtual void draw3dText(const btVector3& location, const char* textString);

/// Set debug draw flags.
virtual void setDebugMode(int debugMode) { debugMode_ = debugMode; }

/// Return debug draw flags.
virtual int getDebugMode() const { return debugMode_; }

/// Visualize the component as debug geometry.
virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);

/// Step the simulation forward.
void Update(float timeStep);
/// Refresh collisions only without updating dynamics.
void UpdateCollisions();
/// Set simulation substeps per second.
void SetFps(int fps);
/// Set gravity.
void SetGravity(const Vector3& gravity);
/// Set maximum number of physics substeps per frame. 0 (default) is unlimited. Positive values cap the amount. Use a negative value to enable an adaptive timestep. This may cause inconsistent physics behavior.
void SetMaxSubSteps(int num);
/// Set number of constraint solver iterations.
void SetNumIterations(int num);
/// Set whether to interpolate between simulation steps.
void SetInterpolation(bool enable);
/// Set whether to use Bullet's internal edge utility for trimesh collisions. Disabled by default.
void SetInternalEdge(bool enable);
/// Set split impulse collision mode. This is more accurate, but slower. Disabled by default.
void SetSplitImpulse(bool enable);
/// Set maximum angular velocity for network replication.
void SetMaxNetworkAngularVelocity(float velocity);
/// Perform a physics world raycast and return all hits.
void Raycast
    (PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
/// Perform a physics world raycast and return the closest hit.
void RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
/// Perform a physics world swept sphere test and return the closest hit.
void SphereCast
    (PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
/// Perform a physics world swept convex test using a user-supplied collision shape and return the first hit.
void ConvexCast(PhysicsRaycastResult& result, CollisionShape* shape, const Vector3& startPos, const Quaternion& startRot,
    const Vector3& endPos, const Quaternion& endRot, unsigned collisionMask = M_MAX_UNSIGNED);
/// Perform a physics world swept convex test using a user-supplied Bullet collision shape and return the first hit.
void ConvexCast(PhysicsRaycastResult& result, btCollisionShape* shape, const Vector3& startPos, const Quaternion& startRot,
    const Vector3& endPos, const Quaternion& endRot, unsigned collisionMask = M_MAX_UNSIGNED);
/// Invalidate cached collision geometry for a model.
void RemoveCachedGeometry(Model* model);
/// Return rigid bodies by a sphere query.
void GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& sphere, unsigned collisionMask = M_MAX_UNSIGNED);
/// Return rigid bodies by a box query.
void GetRigidBodies(PODVector<RigidBody*>& result, const BoundingBox& box, unsigned collisionMask = M_MAX_UNSIGNED);
/// Return rigid bodies that have been in collision with a specific body on the last simulation step.
void GetRigidBodies(PODVector<RigidBody*>& result, const RigidBody* body);

/// Return gravity.
Vector3 GetGravity() const;

/// Return maximum number of physics substeps per frame.
int GetMaxSubSteps() const { return maxSubSteps_; }

/// Return number of constraint solver iterations.
int GetNumIterations() const;

/// Return whether interpolation between simulation steps is enabled.
bool GetInterpolation() const { return interpolation_; }

/// Return whether Bullet's internal edge utility for trimesh collisions is enabled.
bool GetInternalEdge() const { return internalEdge_; }

/// Return whether split impulse collision mode is enabled.
bool GetSplitImpulse() const;

/// Return simulation steps per second.
int GetFps() const { return fps_; }

/// Return maximum angular velocity for network replication.
float GetMaxNetworkAngularVelocity() const { return maxNetworkAngularVelocity_; }

/// Add a rigid body to keep track of. Called by RigidBody.
void AddRigidBody(RigidBody* body);
/// Remove a rigid body. Called by RigidBody.
void RemoveRigidBody(RigidBody* body);

//====================================
/// Add a soft body to keep track of. Called by SoftBody.
void AddSoftBody(SoftBody* body);
/// Remove a soft body. Called by SoftBody.
void RemoveSoftBody(SoftBody* body);
//====================================

/// Add a collision shape to keep track of. Called by CollisionShape.
void AddCollisionShape(CollisionShape* shape);
/// Remove a collision shape. Called by CollisionShape.
void RemoveCollisionShape(CollisionShape* shape);
/// Add a constraint to keep track of. Called by Constraint.
void AddConstraint(Constraint* joint);
/// Remove a constraint. Called by Constraint.
void RemoveConstraint(Constraint* joint);
/// Add a delayed world transform assignment. Called by RigidBody.
void AddDelayedWorldTransform(const DelayedWorldTransform& transform);
/// Add debug geometry to the debug renderer.
void DrawDebugGeometry(bool depthTest);
/// Set debug renderer to use. Called both by PhysicsWorld itself and physics components.
void SetDebugRenderer(DebugRenderer* debug);
/// Set debug geometry depth test mode. Called both by PhysicsWorld itself and physics components.
void SetDebugDepthTest(bool enable);

/// Return the Bullet physics world.
btDiscreteDynamicsWorld* GetWorld() { return world_; }

/// Clean up the geometry cache.
void CleanupGeometryCache();

/// Return trimesh collision geometry cache.
HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >& GetTriMeshCache() { return triMeshCache_; }

/// Return convex collision geometry cache.
HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >& GetConvexCache() { return convexCache_; }

/// Set node dirtying to be disregarded.
void SetApplyingTransforms(bool enable) { applyingTransforms_ = enable; }

/// Return whether node dirtying should be disregarded.
bool IsApplyingTransforms() const { return applyingTransforms_; }

protected:
/// Handle scene being assigned.
virtual void OnSceneSet(Scene* scene);

private:
/// Handle the scene subsystem update event, step simulation here.
void HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData);
/// Trigger update before each physics simulation step.
void PreStep(float timeStep);
/// Trigger update after each physics simulation step.
void PostStep(float timeStep);
/// Send accumulated collision events.
void SendCollisionEvents();

/// Bullet collision configuration.
btCollisionConfiguration* collisionConfiguration_;
/// Bullet collision dispatcher.
btDispatcher* collisionDispatcher_;
/// Bullet collision broadphase.
btBroadphaseInterface* broadphase_;
/// Bullet constraint solver.
btConstraintSolver* solver_;
/// Bullet physics world.
btDiscreteDynamicsWorld* world_;
/// Extra weak pointer to scene to allow for cleanup in case the world is destroyed before other components.
WeakPtr<Scene> scene_;
/// Rigid bodies in the world.
PODVector<RigidBody*> rigidBodies_;

//===================================
/// Soft bodies in the world.
PODVector<SoftBody*> softBodies_;
//===================================

/// Collision shapes in the world.
PODVector<CollisionShape*> collisionShapes_;
/// Constraints in the world.
PODVector<Constraint*> constraints_;
/// Collision pairs on this frame.
HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*> currentCollisions_;
/// Collision pairs on the previous frame. Used to check if a collision is "new." Manifolds are not guaranteed to exist anymore.
HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*> previousCollisions_;
/// Delayed (parented) world transform assignments.
HashMap<RigidBody*, DelayedWorldTransform> delayedWorldTransforms_;
/// Cache for trimesh geometry data by model and LOD level.
HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> > triMeshCache_;
/// Cache for convex geometry data by model and LOD level.
HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> > convexCache_;
/// Preallocated event data map for physics collision events.
VariantMap physicsCollisionData_;
/// Preallocated event data map for node collision events.
VariantMap nodeCollisionData_;
/// Preallocated buffer for physics collision contact data.
VectorBuffer contacts_;
/// Simulation substeps per second.
unsigned fps_;
/// Maximum number of simulation substeps per frame. 0 (default) unlimited, or negative values for adaptive timestep.
int maxSubSteps_;
/// Time accumulator for non-interpolated mode.
float timeAcc_;
/// Maximum angular velocity for network replication.
float maxNetworkAngularVelocity_;
/// Interpolation flag.
bool interpolation_;
/// Use internal edge utility flag.
bool internalEdge_;
/// Applying transforms flag.
bool applyingTransforms_;
/// Debug renderer.
DebugRenderer* debugRenderer_;
/// Debug draw flags.
int debugMode_;
/// Debug draw depth test mode.
bool debugDepthTest_;

};

/// Register Physics library objects.
void URHO3D_API RegisterPhysicsLibrary(Context* context);

}

[/code][/spoiler]

PhysicsWorld.cpp

[spoiler][code]
//
// Copyright © 2008-2015 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the “Software”), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include “…/Precompiled.h”

#include “…/Core/Context.h”
#include “…/Core/Mutex.h”
#include “…/Core/Profiler.h”
#include “…/Graphics/DebugRenderer.h”
#include “…/Graphics/Model.h”
#include “…/IO/Log.h”
#include “…/Math/Ray.h”
#include “…/Physics/CollisionShape.h”
#include “…/Physics/Constraint.h”
#include “…/Physics/PhysicsEvents.h”
#include “…/Physics/PhysicsUtils.h”
#include “…/Physics/PhysicsWorld.h”
#include “…/Physics/RigidBody.h”
#include “…/Scene/Scene.h”
#include “…/Scene/SceneEvents.h”
#include “…/Physics/SoftBody.h”

#include <Bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <Bullet/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
#include <Bullet/BulletCollision/CollisionDispatch/btInternalEdgeUtility.h>
#include <Bullet/BulletCollision/CollisionShapes/btBoxShape.h>
#include <Bullet/BulletCollision/CollisionShapes/btSphereShape.h>
#include <Bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>
#include <Bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>

//==========================================
#include <Bullet/BulletSoftBody/btSoftBody.h>
#include <Bullet/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h>
#include <Bullet/BulletSoftBody/btSoftRigidDynamicsWorld.h>
#define TEST_SOFTBODY
//==========================================

extern ContactAddedCallback gContactAddedCallback;

namespace Urho3D
{

const char* PHYSICS_CATEGORY = “Physics”;
extern const char* SUBSYSTEM_CATEGORY;

static const int MAX_SOLVER_ITERATIONS = 256;
static const int DEFAULT_FPS = 60;
static const Vector3 DEFAULT_GRAVITY = Vector3(0.0f, -9.81f, 0.0f);

static bool CompareRaycastResults(const PhysicsRaycastResult& lhs, const PhysicsRaycastResult& rhs)
{
return lhs.distance_ < rhs.distance_;
}

void InternalPreTickCallback(btDynamicsWorld* world, btScalar timeStep)
{
static_cast<PhysicsWorld*>(world->getWorldUserInfo())->PreStep(timeStep);
}

void InternalTickCallback(btDynamicsWorld* world, btScalar timeStep)
{
static_cast<PhysicsWorld*>(world->getWorldUserInfo())->PostStep(timeStep);
}

static bool CustomMaterialCombinerCallback(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0Wrap, int partId0,
int index0, const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1)
{
btAdjustInternalEdgeContacts(cp, colObj1Wrap, colObj0Wrap, partId1, index1);

cp.m_combinedFriction = colObj0Wrap->getCollisionObject()->getFriction() * colObj1Wrap->getCollisionObject()->getFriction();
cp.m_combinedRestitution =
    colObj0Wrap->getCollisionObject()->getRestitution() * colObj1Wrap->getCollisionObject()->getRestitution();

return true;

}

/// Callback for physics world queries.
struct PhysicsQueryCallback : public btCollisionWorld::ContactResultCallback
{
/// Construct.
PhysicsQueryCallback(PODVector<RigidBody*>& result, unsigned collisionMask) :
result_(result),
collisionMask_(collisionMask)
{
}

/// Add a contact result.
virtual btScalar addSingleResult(btManifoldPoint&, const btCollisionObjectWrapper* colObj0Wrap, int, int,
    const btCollisionObjectWrapper* colObj1Wrap, int, int)
{
    RigidBody* body = reinterpret_cast<RigidBody*>(colObj0Wrap->getCollisionObject()->getUserPointer());
    if (body && !result_.Contains(body) && (body->GetCollisionLayer() & collisionMask_))
        result_.Push(body);
    body = reinterpret_cast<RigidBody*>(colObj1Wrap->getCollisionObject()->getUserPointer());
    if (body && !result_.Contains(body) && (body->GetCollisionLayer() & collisionMask_))
        result_.Push(body);
    return 0.0f;
}

/// Found rigid bodies.
PODVector<RigidBody*>& result_;
/// Collision mask for the query.
unsigned collisionMask_;

};

PhysicsWorld::PhysicsWorld(Context* context) :
Component(context),
collisionConfiguration_(0),
collisionDispatcher_(0),
broadphase_(0),
solver_(0),
world_(0),
fps_(DEFAULT_FPS),
maxSubSteps_(0),
timeAcc_(0.0f),
maxNetworkAngularVelocity_(DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY),
interpolation_(true),
internalEdge_(true),
applyingTransforms_(false),
debugRenderer_(0),
debugMode_(btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawConstraints | btIDebugDraw::DBG_DrawConstraintLimits)
{
gContactAddedCallback = CustomMaterialCombinerCallback;

//===============================================
// collisionConfiguration_ = new btDefaultCollisionConfiguration();
#ifndef TEST_SOFTBODY
collisionConfiguration_ = new btDefaultCollisionConfiguration();
#else
collisionConfiguration_ = new btSoftBodyRigidBodyCollisionConfiguration();
#endif
//===============================================

collisionDispatcher_ = new btCollisionDispatcher(collisionConfiguration_);
broadphase_ = new btDbvtBroadphase();
solver_ = new btSequentialImpulseConstraintSolver();

//===============================================
// world_ = new btDiscreteDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_);
#ifndef TEST_SOFTBODY
world_ = new btDiscreteDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_);
#else
world_ = new btSoftRigidDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_, NULL);
#endif
//===============================================

world_->setGravity(ToBtVector3(DEFAULT_GRAVITY));
world_->getDispatchInfo().m_useContinuous = true;
world_->getSolverInfo().m_splitImpulse = false; // Disable by default for performance
world_->setDebugDrawer(this);
world_->setInternalTickCallback(InternalPreTickCallback, static_cast<void*>(this), true);
world_->setInternalTickCallback(InternalTickCallback, static_cast<void*>(this), false);

//==========================================
#ifdef TEST_SOFTBODY
m_softBodyWorldInfo = new btSoftBodyWorldInfo();
m_softBodyWorldInfo->m_dispatcher = collisionDispatcher_;
m_softBodyWorldInfo->m_broadphase = broadphase_;
m_softBodyWorldInfo->air_density = (btScalar)1.2;
m_softBodyWorldInfo->water_density = 0;
m_softBodyWorldInfo->water_offset = 0;
m_softBodyWorldInfo->water_normal = btVector3(0, 0, 0);
m_softBodyWorldInfo->m_gravity.setValue(0, -10, 0);
m_softBodyWorldInfo->m_sparsesdf.Initialize();

    softBodies_.Clear();
#endif

//===========================================
}

PhysicsWorld::~PhysicsWorld()
{
if (scene_)
{
// Force all remaining constraints, rigid bodies and collision shapes to release themselves
for (PODVector<Constraint*>::Iterator i = constraints_.Begin(); i != constraints_.End(); ++i)
(*i)->ReleaseConstraint();

    for (PODVector<RigidBody*>::Iterator i = rigidBodies_.Begin(); i != rigidBodies_.End(); ++i)
        (*i)->ReleaseBody();

    for (PODVector<CollisionShape*>::Iterator i = collisionShapes_.Begin(); i != collisionShapes_.End(); ++i)
        (*i)->ReleaseShape();

//====================================
for (PODVector<SoftBody*>::Iterator i = softBodies_.Begin(); i != softBodies_.End(); ++i)
(*i)->ReleaseBody();
//====================================
}

delete world_;
world_ = 0;

delete solver_;
solver_ = 0;

delete broadphase_;
broadphase_ = 0;

delete collisionDispatcher_;
collisionDispatcher_ = 0;

delete collisionConfiguration_;
collisionConfiguration_ = 0;

//=================================
if (m_softBodyWorldInfo)
{
m_softBodyWorldInfo->m_dispatcher = 0;
m_softBodyWorldInfo->m_broadphase = 0;

    delete m_softBodyWorldInfo;
    m_softBodyWorldInfo = 0;
}

//=================================
}

void PhysicsWorld::RegisterObject(Context* context)
{
context->RegisterFactory(SUBSYSTEM_CATEGORY);

MIXED_ACCESSOR_ATTRIBUTE("Gravity", GetGravity, SetGravity, Vector3, DEFAULT_GRAVITY, AM_DEFAULT);
ATTRIBUTE("Physics FPS", int, fps_, DEFAULT_FPS, AM_DEFAULT);
ATTRIBUTE("Max Substeps", int, maxSubSteps_, 0, AM_DEFAULT);
ACCESSOR_ATTRIBUTE("Solver Iterations", GetNumIterations, SetNumIterations, int, 10, AM_DEFAULT);
ATTRIBUTE("Net Max Angular Vel.", float, maxNetworkAngularVelocity_, DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY, AM_DEFAULT);
ATTRIBUTE("Interpolation", bool, interpolation_, true, AM_FILE);
ATTRIBUTE("Internal Edge Utility", bool, internalEdge_, true, AM_DEFAULT);
ACCESSOR_ATTRIBUTE("Split Impulse", GetSplitImpulse, SetSplitImpulse, bool, false, AM_DEFAULT);

}

bool PhysicsWorld::isVisible(const btVector3& aabbMin, const btVector3& aabbMax)
{
if (debugRenderer_)
return debugRenderer_->IsInside(BoundingBox(ToVector3(aabbMin), ToVector3(aabbMax)));
else
return false;
}

void PhysicsWorld::drawLine(const btVector3& from, const btVector3& to, const btVector3& color)
{
if (debugRenderer_)
debugRenderer_->AddLine(ToVector3(from), ToVector3(to), Color(color.x(), color.y(), color.z()), debugDepthTest_);
}

void PhysicsWorld::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
{
if (debug)
{
PROFILE(PhysicsDrawDebug);

    debugRenderer_ = debug;
    debugDepthTest_ = depthTest;
    world_->debugDrawWorld();
    debugRenderer_ = 0;
}

}

void PhysicsWorld::reportErrorWarning(const char* warningString)
{
LOGWARNING("Physics: " + String(warningString));
}

void PhysicsWorld::drawContactPoint(const btVector3& pointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime,
const btVector3& color)
{
}

void PhysicsWorld::draw3dText(const btVector3& location, const char* textString)
{
}

void PhysicsWorld::Update(float timeStep)
{
PROFILE(UpdatePhysics);

float internalTimeStep = 1.0f / fps_;
int maxSubSteps = (int)(timeStep * fps_) + 1;
if (maxSubSteps_ < 0)
{
    internalTimeStep = timeStep;
    maxSubSteps = 1;
}
else if (maxSubSteps_ > 0)
    maxSubSteps = Min(maxSubSteps, maxSubSteps_);

delayedWorldTransforms_.Clear();

if (interpolation_)
    world_->stepSimulation(timeStep, maxSubSteps, internalTimeStep);
else
{
    timeAcc_ += timeStep;
    while (timeAcc_ >= internalTimeStep && maxSubSteps > 0)
    {
        world_->stepSimulation(internalTimeStep, 0, internalTimeStep);
        timeAcc_ -= internalTimeStep;
        --maxSubSteps;
    }
}

// Apply delayed (parented) world transforms now
while (!delayedWorldTransforms_.Empty())
{
    for (HashMap<RigidBody*, DelayedWorldTransform>::Iterator i = delayedWorldTransforms_.Begin();
         i != delayedWorldTransforms_.End(); ++i)
    {
        const DelayedWorldTransform& transform = i->second_;

        // If parent's transform has already been assigned, can proceed
        if (!delayedWorldTransforms_.Contains(transform.parentRigidBody_))
        {
            transform.rigidBody_->ApplyWorldTransform(transform.worldPosition_, transform.worldRotation_);
            delayedWorldTransforms_.Erase(i);
        }
    }
}

}

void PhysicsWorld::UpdateCollisions()
{
world_->performDiscreteCollisionDetection();
}

void PhysicsWorld::SetFps(int fps)
{
fps_ = (unsigned)Clamp(fps, 1, 1000);

MarkNetworkUpdate();

}

void PhysicsWorld::SetGravity(const Vector3& gravity)
{
world_->setGravity(ToBtVector3(gravity));

MarkNetworkUpdate();

}

void PhysicsWorld::SetMaxSubSteps(int num)
{
maxSubSteps_ = num;
MarkNetworkUpdate();
}

void PhysicsWorld::SetNumIterations(int num)
{
num = Clamp(num, 1, MAX_SOLVER_ITERATIONS);
world_->getSolverInfo().m_numIterations = num;

MarkNetworkUpdate();

}

void PhysicsWorld::SetInterpolation(bool enable)
{
interpolation_ = enable;
}

void PhysicsWorld::SetInternalEdge(bool enable)
{
internalEdge_ = enable;

MarkNetworkUpdate();

}

void PhysicsWorld::SetSplitImpulse(bool enable)
{
world_->getSolverInfo().m_splitImpulse = enable;

MarkNetworkUpdate();

}

void PhysicsWorld::SetMaxNetworkAngularVelocity(float velocity)
{
maxNetworkAngularVelocity_ = Clamp(velocity, 1.0f, 32767.0f);

MarkNetworkUpdate();

}

void PhysicsWorld::Raycast(PODVector& result, const Ray& ray, float maxDistance, unsigned collisionMask)
{
PROFILE(PhysicsRaycast);

if (maxDistance >= M_INFINITY)
    LOGWARNING("Infinite maxDistance in physics raycast is not supported");

btCollisionWorld::AllHitsRayResultCallback
    rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_));
rayCallback.m_collisionFilterGroup = (short)0xffff;
rayCallback.m_collisionFilterMask = (short)collisionMask;

world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);

for (int i = 0; i < rayCallback.m_collisionObjects.size(); ++i)
{
    PhysicsRaycastResult newResult;
    newResult.body_ = static_cast<RigidBody*>(rayCallback.m_collisionObjects[i]->getUserPointer());
    newResult.position_ = ToVector3(rayCallback.m_hitPointWorld[i]);
    newResult.normal_ = ToVector3(rayCallback.m_hitNormalWorld[i]);
    newResult.distance_ = (newResult.position_ - ray.origin_).Length();
    result.Push(newResult);
}

Sort(result.Begin(), result.End(), CompareRaycastResults);

}

void PhysicsWorld::RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask)
{
PROFILE(PhysicsRaycastSingle);

if (maxDistance >= M_INFINITY)
    LOGWARNING("Infinite maxDistance in physics raycast is not supported");

btCollisionWorld::ClosestRayResultCallback
    rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_));
rayCallback.m_collisionFilterGroup = (short)0xffff;
rayCallback.m_collisionFilterMask = (short)collisionMask;

world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);

if (rayCallback.hasHit())
{
    result.position_ = ToVector3(rayCallback.m_hitPointWorld);
    result.normal_ = ToVector3(rayCallback.m_hitNormalWorld);
    result.distance_ = (result.position_ - ray.origin_).Length();
    result.body_ = static_cast<RigidBody*>(rayCallback.m_collisionObject->getUserPointer());
}
else
{
    result.position_ = Vector3::ZERO;
    result.normal_ = Vector3::ZERO;
    result.distance_ = M_INFINITY;
    result.body_ = 0;
}

}

void PhysicsWorld::SphereCast(PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask)
{
PROFILE(PhysicsSphereCast);

if (maxDistance >= M_INFINITY)
    LOGWARNING("Infinite maxDistance in physics sphere cast is not supported");

btSphereShape shape(radius);

btCollisionWorld::ClosestConvexResultCallback
    convexCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_));
convexCallback.m_collisionFilterGroup = (short)0xffff;
convexCallback.m_collisionFilterMask = (short)collisionMask;

world_->convexSweepTest(&shape, btTransform(btQuaternion::getIdentity(), convexCallback.m_convexFromWorld),
    btTransform(btQuaternion::getIdentity(), convexCallback.m_convexToWorld), convexCallback);

if (convexCallback.hasHit())
{
    result.body_ = static_cast<RigidBody*>(convexCallback.m_hitCollisionObject->getUserPointer());
    result.position_ = ToVector3(convexCallback.m_hitPointWorld);
    result.normal_ = ToVector3(convexCallback.m_hitNormalWorld);
    result.distance_ = (result.position_ - ray.origin_).Length();
}
else
{
    result.body_ = 0;
    result.position_ = Vector3::ZERO;
    result.normal_ = Vector3::ZERO;
    result.distance_ = M_INFINITY;
}

}

void PhysicsWorld::ConvexCast(PhysicsRaycastResult& result, CollisionShape* shape, const Vector3& startPos,
const Quaternion& startRot, const Vector3& endPos, const Quaternion& endRot, unsigned collisionMask)
{
if (!shape || !shape->GetCollisionShape())
{
LOGERROR(“Null collision shape for convex cast”);
result.body_ = 0;
result.position_ = Vector3::ZERO;
result.normal_ = Vector3::ZERO;
result.distance_ = M_INFINITY;
return;
}

// If shape is attached in a rigidbody, set its collision group temporarily to 0 to make sure it is not returned in the sweep result
RigidBody* bodyComp = shape->GetComponent<RigidBody>();
btRigidBody* body = bodyComp ? bodyComp->GetBody() : (btRigidBody*)0;
btBroadphaseProxy* proxy = body ? body->getBroadphaseProxy() : (btBroadphaseProxy*)0;
short group = 0;
if (proxy)
{
    group = proxy->m_collisionFilterGroup;
    proxy->m_collisionFilterGroup = 0;
}

// Take the shape's offset position & rotation into account
Node* shapeNode = shape->GetNode();
Matrix3x4 startTransform(startPos, startRot, shapeNode ? shapeNode->GetWorldScale() : Vector3::ONE);
Matrix3x4 endTransform(endPos, endRot, shapeNode ? shapeNode->GetWorldScale() : Vector3::ONE);
Vector3 effectiveStartPos = startTransform * shape->GetPosition();
Vector3 effectiveEndPos = endTransform * shape->GetPosition();
Quaternion effectiveStartRot = startRot * shape->GetRotation();
Quaternion effectiveEndRot = endRot * shape->GetRotation();

ConvexCast(result, shape->GetCollisionShape(), effectiveStartPos, effectiveStartRot, effectiveEndPos, effectiveEndRot, collisionMask);

// Restore the collision group
if (proxy)
    proxy->m_collisionFilterGroup = group;

}

void PhysicsWorld::ConvexCast(PhysicsRaycastResult& result, btCollisionShape* shape, const Vector3& startPos,
const Quaternion& startRot, const Vector3& endPos, const Quaternion& endRot, unsigned collisionMask)
{
if (!shape)
{
LOGERROR(“Null collision shape for convex cast”);
result.body_ = 0;
result.position_ = Vector3::ZERO;
result.normal_ = Vector3::ZERO;
result.distance_ = M_INFINITY;
return;
}

if (!shape->isConvex())
{
    LOGERROR("Can not use non-convex collision shape for convex cast");
    result.body_ = 0;
    result.position_ = Vector3::ZERO;
    result.normal_ = Vector3::ZERO;
    result.distance_ = M_INFINITY;
    return;
}

PROFILE(PhysicsConvexCast);

btCollisionWorld::ClosestConvexResultCallback convexCallback(ToBtVector3(startPos), ToBtVector3(endPos));
convexCallback.m_collisionFilterGroup = (short)0xffff;
convexCallback.m_collisionFilterMask = (short)collisionMask;

world_->convexSweepTest(static_cast<btConvexShape*>(shape), btTransform(ToBtQuaternion(startRot),
        convexCallback.m_convexFromWorld), btTransform(ToBtQuaternion(endRot), convexCallback.m_convexToWorld),
    convexCallback);

if (convexCallback.hasHit())
{
    result.body_ = static_cast<RigidBody*>(convexCallback.m_hitCollisionObject->getUserPointer());
    result.position_ = ToVector3(convexCallback.m_hitPointWorld);
    result.normal_ = ToVector3(convexCallback.m_hitNormalWorld);
    result.distance_ = (result.position_ - startPos).Length();
}
else
{
    result.body_ = 0;
    result.position_ = Vector3::ZERO;
    result.normal_ = Vector3::ZERO;
    result.distance_ = M_INFINITY;
}

}

void PhysicsWorld::RemoveCachedGeometry(Model* model)
{
for (HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator i = triMeshCache_.Begin();
i != triMeshCache_.End():wink:
{
HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator current = i++;
if (current->first_.first_ == model)
triMeshCache_.Erase(current);
}
for (HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator i = convexCache_.Begin();
i != convexCache_.End():wink:
{
HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator current = i++;
if (current->first_.first_ == model)
convexCache_.Erase(current);
}
}

void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& sphere, unsigned collisionMask)
{
PROFILE(PhysicsSphereQuery);

result.Clear();

btSphereShape sphereShape(sphere.radius_);
btRigidBody* tempRigidBody = new btRigidBody(1.0f, 0, &sphereShape);
tempRigidBody->setWorldTransform(btTransform(btQuaternion::getIdentity(), ToBtVector3(sphere.center_)));
// Need to activate the temporary rigid body to get reliable results from static, sleeping objects
tempRigidBody->activate();
world_->addRigidBody(tempRigidBody);

PhysicsQueryCallback callback(result, collisionMask);
world_->contactTest(tempRigidBody, callback);

world_->removeRigidBody(tempRigidBody);
delete tempRigidBody;

}

void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const BoundingBox& box, unsigned collisionMask)
{
PROFILE(PhysicsBoxQuery);

result.Clear();

btBoxShape boxShape(ToBtVector3(box.HalfSize()));
btRigidBody* tempRigidBody = new btRigidBody(1.0f, 0, &boxShape);
tempRigidBody->setWorldTransform(btTransform(btQuaternion::getIdentity(), ToBtVector3(box.Center())));
tempRigidBody->activate();
world_->addRigidBody(tempRigidBody);

PhysicsQueryCallback callback(result, collisionMask);
world_->contactTest(tempRigidBody, callback);

world_->removeRigidBody(tempRigidBody);
delete tempRigidBody;

}

void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const RigidBody* body)
{
PROFILE(GetCollidingBodies);

result.Clear();

for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
     i != currentCollisions_.End(); ++i)
{
    if (i->first_.first_ == body)
        result.Push(i->first_.second_);
    else if (i->first_.second_ == body)
        result.Push(i->first_.first_);
}

}

Vector3 PhysicsWorld::GetGravity() const
{
return ToVector3(world_->getGravity());
}

int PhysicsWorld::GetNumIterations() const
{
return world_->getSolverInfo().m_numIterations;
}

bool PhysicsWorld::GetSplitImpulse() const
{
return world_->getSolverInfo().m_splitImpulse != 0;
}

void PhysicsWorld::AddRigidBody(RigidBody* body)
{
rigidBodies_.Push(body);
}

void PhysicsWorld::RemoveRigidBody(RigidBody* body)
{
rigidBodies_.Remove(body);
// Remove possible dangling pointer from the delayedWorldTransforms structure
delayedWorldTransforms_.Erase(body);
}

//===================================
void PhysicsWorld::AddSoftBody(SoftBody* body)
{
softBodies_.Push(body);
}

void PhysicsWorld::RemoveSoftBody(SoftBody* body)
{
softBodies_.Remove(body);
}
//===================================

void PhysicsWorld::AddCollisionShape(CollisionShape* shape)
{
collisionShapes_.Push(shape);
}

void PhysicsWorld::RemoveCollisionShape(CollisionShape* shape)
{
collisionShapes_.Remove(shape);
}

void PhysicsWorld::AddConstraint(Constraint* constraint)
{
constraints_.Push(constraint);
}

void PhysicsWorld::RemoveConstraint(Constraint* constraint)
{
constraints_.Remove(constraint);
}

void PhysicsWorld::AddDelayedWorldTransform(const DelayedWorldTransform& transform)
{
delayedWorldTransforms_[transform.rigidBody_] = transform;
}

void PhysicsWorld::DrawDebugGeometry(bool depthTest)
{
DebugRenderer* debug = GetComponent();
DrawDebugGeometry(debug, depthTest);
}

void PhysicsWorld::SetDebugRenderer(DebugRenderer* debug)
{
debugRenderer_ = debug;
}

void PhysicsWorld::SetDebugDepthTest(bool enable)
{
debugDepthTest_ = enable;
}

void PhysicsWorld::CleanupGeometryCache()
{
// Remove cached shapes whose only reference is the cache itself
for (HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator i = triMeshCache_.Begin();
i != triMeshCache_.End():wink:
{
HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator current = i++;
if (current->second_.Refs() == 1)
triMeshCache_.Erase(current);
}
for (HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator i = convexCache_.Begin();
i != convexCache_.End():wink:
{
HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator current = i++;
if (current->second_.Refs() == 1)
convexCache_.Erase(current);
}
}

void PhysicsWorld::OnSceneSet(Scene* scene)
{
// Subscribe to the scene subsystem update, which will trigger the physics simulation step
if (scene)
{
scene_ = GetScene();
SubscribeToEvent(scene_, E_SCENESUBSYSTEMUPDATE, HANDLER(PhysicsWorld, HandleSceneSubsystemUpdate));
}
else
UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
}

void PhysicsWorld::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
{
using namespace SceneSubsystemUpdate;

Update(eventData[P_TIMESTEP].GetFloat());

}

void PhysicsWorld::PreStep(float timeStep)
{
// Send pre-step event
using namespace PhysicsPreStep;

VariantMap& eventData = GetEventDataMap();
eventData[P_WORLD] = this;
eventData[P_TIMESTEP] = timeStep;
SendEvent(E_PHYSICSPRESTEP, eventData);

// Start profiling block for the actual simulation step

#ifdef URHO3D_PROFILING
Profiler* profiler = GetSubsystem();
if (profiler)
profiler->BeginBlock(“StepSimulation”);
#endif
}

void PhysicsWorld::PostStep(float timeStep)
{
#ifdef URHO3D_PROFILING
Profiler* profiler = GetSubsystem();
if (profiler)
profiler->EndBlock();
#endif

SendCollisionEvents();

// Send post-step event
using namespace PhysicsPostStep;

VariantMap& eventData = GetEventDataMap();
eventData[P_WORLD] = this;
eventData[P_TIMESTEP] = timeStep;
SendEvent(E_PHYSICSPOSTSTEP, eventData);

}

void PhysicsWorld::SendCollisionEvents()
{
PROFILE(SendCollisionEvents);

currentCollisions_.Clear();
physicsCollisionData_.Clear();
nodeCollisionData_.Clear();

int numManifolds = collisionDispatcher_->getNumManifolds();

if (numManifolds)
{
    physicsCollisionData_[PhysicsCollision::P_WORLD] = this;

    for (int i = 0; i < numManifolds; ++i)
    {
        btPersistentManifold* contactManifold = collisionDispatcher_->getManifoldByIndexInternal(i);
        // First check that there are actual contacts, as the manifold exists also when objects are close but not touching
        if (!contactManifold->getNumContacts())
            continue;

        const btCollisionObject* objectA = contactManifold->getBody0();
        const btCollisionObject* objectB = contactManifold->getBody1();

        RigidBody* bodyA = static_cast<RigidBody*>(objectA->getUserPointer());
        RigidBody* bodyB = static_cast<RigidBody*>(objectB->getUserPointer());
        // If it's not a rigidbody, maybe a ghost object
        if (!bodyA || !bodyB)
            continue;

        // Skip collision event signaling if both objects are static, or if collision event mode does not match
        if (bodyA->GetMass() == 0.0f && bodyB->GetMass() == 0.0f)
            continue;
        if (bodyA->GetCollisionEventMode() == COLLISION_NEVER || bodyB->GetCollisionEventMode() == COLLISION_NEVER)
            continue;
        if (bodyA->GetCollisionEventMode() == COLLISION_ACTIVE && bodyB->GetCollisionEventMode() == COLLISION_ACTIVE &&
            !bodyA->IsActive() && !bodyB->IsActive())
            continue;

        WeakPtr<RigidBody> bodyWeakA(bodyA);
        WeakPtr<RigidBody> bodyWeakB(bodyB);

        Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> > bodyPair;
        if (bodyA < bodyB)
            bodyPair = MakePair(bodyWeakA, bodyWeakB);
        else
            bodyPair = MakePair(bodyWeakB, bodyWeakA);

        // First only store the collision pair as weak pointers and the manifold pointer, so user code can safely destroy
        // objects during collision event handling
        currentCollisions_[bodyPair] = contactManifold;
    }

    for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
         i != currentCollisions_.End(); ++i)
    {
        RigidBody* bodyA = i->first_.first_;
        RigidBody* bodyB = i->first_.second_;
        if (!bodyA || !bodyB)
            continue;

        btPersistentManifold* contactManifold = i->second_;

        Node* nodeA = bodyA->GetNode();
        Node* nodeB = bodyB->GetNode();
        WeakPtr<Node> nodeWeakA(nodeA);
        WeakPtr<Node> nodeWeakB(nodeB);

        bool trigger = bodyA->IsTrigger() || bodyB->IsTrigger();
        bool newCollision = !previousCollisions_.Contains(i->first_);

        physicsCollisionData_[PhysicsCollision::P_NODEA] = nodeA;
        physicsCollisionData_[PhysicsCollision::P_NODEB] = nodeB;
        physicsCollisionData_[PhysicsCollision::P_BODYA] = bodyA;
        physicsCollisionData_[PhysicsCollision::P_BODYB] = bodyB;
        physicsCollisionData_[PhysicsCollision::P_TRIGGER] = trigger;

        contacts_.Clear();

        for (int j = 0; j < contactManifold->getNumContacts(); ++j)
        {
            btManifoldPoint& point = contactManifold->getContactPoint(j);
            contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
            contacts_.WriteVector3(ToVector3(point.m_normalWorldOnB));
            contacts_.WriteFloat(point.m_distance1);
            contacts_.WriteFloat(point.m_appliedImpulse);
        }

        physicsCollisionData_[PhysicsCollision::P_CONTACTS] = contacts_.GetBuffer();

        // Send separate collision start event if collision is new
        if (newCollision)
        {
            SendEvent(E_PHYSICSCOLLISIONSTART, physicsCollisionData_);
            // Skip rest of processing if either of the nodes or bodies is removed as a response to the event
            if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                continue;
        }

        // Then send the ongoing collision event
        SendEvent(E_PHYSICSCOLLISION, physicsCollisionData_);
        if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
            continue;

        nodeCollisionData_[NodeCollision::P_BODY] = bodyA;
        nodeCollisionData_[NodeCollision::P_OTHERNODE] = nodeB;
        nodeCollisionData_[NodeCollision::P_OTHERBODY] = bodyB;
        nodeCollisionData_[NodeCollision::P_TRIGGER] = trigger;
        nodeCollisionData_[NodeCollision::P_CONTACTS] = contacts_.GetBuffer();

        if (newCollision)
        {
            nodeA->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData_);
            if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                continue;
        }

        nodeA->SendEvent(E_NODECOLLISION, nodeCollisionData_);
        if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
            continue;

        contacts_.Clear();
        for (int j = 0; j < contactManifold->getNumContacts(); ++j)
        {
            btManifoldPoint& point = contactManifold->getContactPoint(j);
            contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
            contacts_.WriteVector3(-ToVector3(point.m_normalWorldOnB));
            contacts_.WriteFloat(point.m_distance1);
            contacts_.WriteFloat(point.m_appliedImpulse);
        }

        nodeCollisionData_[NodeCollision::P_BODY] = bodyB;
        nodeCollisionData_[NodeCollision::P_OTHERNODE] = nodeA;
        nodeCollisionData_[NodeCollision::P_OTHERBODY] = bodyA;
        nodeCollisionData_[NodeCollision::P_CONTACTS] = contacts_.GetBuffer();

        if (newCollision)
        {
            nodeB->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData_);
            if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                continue;
        }

        nodeB->SendEvent(E_NODECOLLISION, nodeCollisionData_);
    }
}

// Send collision end events as applicable
{
    physicsCollisionData_[PhysicsCollisionEnd::P_WORLD] = this;

    for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator
             i = previousCollisions_.Begin(); i != previousCollisions_.End(); ++i)
    {
        if (!currentCollisions_.Contains(i->first_))
        {
            RigidBody* bodyA = i->first_.first_;
            RigidBody* bodyB = i->first_.second_;
            if (!bodyA || !bodyB)
                continue;

            bool trigger = bodyA->IsTrigger() || bodyB->IsTrigger();

            // Skip collision event signaling if both objects are static, or if collision event mode does not match
            if (bodyA->GetMass() == 0.0f && bodyB->GetMass() == 0.0f)
                continue;
            if (bodyA->GetCollisionEventMode() == COLLISION_NEVER || bodyB->GetCollisionEventMode() == COLLISION_NEVER)
                continue;
            if (bodyA->GetCollisionEventMode() == COLLISION_ACTIVE && bodyB->GetCollisionEventMode() == COLLISION_ACTIVE &&
                !bodyA->IsActive() && !bodyB->IsActive())
                continue;

            Node* nodeA = bodyA->GetNode();
            Node* nodeB = bodyB->GetNode();
            WeakPtr<Node> nodeWeakA(nodeA);
            WeakPtr<Node> nodeWeakB(nodeB);

            physicsCollisionData_[PhysicsCollisionEnd::P_BODYA] = bodyA;
            physicsCollisionData_[PhysicsCollisionEnd::P_BODYB] = bodyB;
            physicsCollisionData_[PhysicsCollisionEnd::P_NODEA] = nodeA;
            physicsCollisionData_[PhysicsCollisionEnd::P_NODEB] = nodeB;
            physicsCollisionData_[PhysicsCollisionEnd::P_TRIGGER] = trigger;

            SendEvent(E_PHYSICSCOLLISIONEND, physicsCollisionData_);
            // Skip rest of processing if either of the nodes or bodies is removed as a response to the event
            if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                continue;

            nodeCollisionData_[NodeCollisionEnd::P_BODY] = bodyA;
            nodeCollisionData_[NodeCollisionEnd::P_OTHERNODE] = nodeB;
            nodeCollisionData_[NodeCollisionEnd::P_OTHERBODY] = bodyB;
            nodeCollisionData_[NodeCollisionEnd::P_TRIGGER] = trigger;

            nodeA->SendEvent(E_NODECOLLISIONEND, nodeCollisionData_);
            if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                continue;

            nodeCollisionData_[NodeCollisionEnd::P_BODY] = bodyB;
            nodeCollisionData_[NodeCollisionEnd::P_OTHERNODE] = nodeA;
            nodeCollisionData_[NodeCollisionEnd::P_OTHERBODY] = bodyA;

            nodeB->SendEvent(E_NODECOLLISIONEND, nodeCollisionData_);
        }
    }
}

previousCollisions_ = currentCollisions_;

}

void RegisterPhysicsLibrary(Context* context)
{
CollisionShape::RegisterObject(context);
RigidBody::RegisterObject(context);
Constraint::RegisterObject(context);
PhysicsWorld::RegisterObject(context);
SoftBody::RegisterObject(context); // Register softbody ============================
}

}
[/code][/spoiler]


#12

Great work man! Thanks.
Next step I guess is using vertexes group for pinning and use vertex.color for btSoftBody.m_node stiffness ?
also maybe add air vector for interface SoftBody component ?

did you trying to do stress test with high count of SB component on desktop pc ? how many fps you are got with 1000 SB in scene ?)


#13

Thanks, the most important thing that is missing is the ability to sync body and node positions, and for now I’m stuck with this. Any help is welcome.


#14

I think that you need change this
class URHO3D_API SoftBody : public Component
to this
class URHO3D_API SoftBody : public Component, public btMotionState

in this case you are got two virtual functions

/// Return initial world transform to Bullet. virtual void getWorldTransform(btTransform& worldTrans) const; /// Update world transform from Bullet. virtual void setWorldTransform(const btTransform& worldTrans);

I’m also stuck but with implementing who will be rendered simulated cloth? SB by it’s own or it will be just put processed cloth into other drawable, maybe StaticModel like in yours implementation.

My current implementation of SB but I do not tested it yet and don’t know works it or no )
SoftBody.h

SoftBody.cpp


#15

I rewrite some parts of my implementation of SB and it’s all most like Mike’s SB with staticmodel + softbody.
It still have a few weird problems, by I try solve it one by one.
And main problem is:
I do not understand why my SB falling though floor plane and do not bounced from it.
I tried various collision flags for SB body in component.
body_->m_cfg.collisions = btSoftBody::fCollision::CL_RS + btSoftBody::fCollision::CL_SS;
and various CollisionShapes for plane
but in still no working properly

there is my current test code

and this is some changes that I made for physics
github.com/MonkeyFirst/Urho3D/tree/sbtest

now I have worked manual pinnig and sphere.mdl are now hangs on this vertex and do not fall down but still collision with RigidBody not working


#16

I downloaded your branch tag. I’ll try to reproduce this bug today.


#17

Currently I found issue why sphere falling though floor plane.
The problem lays in case of different placement of the physic body_ and his graphic representation - StaticModel.
If body_ lay on plane(floor) at some moment but his StaticModel still placed in air, but if we create some other colliders and push they into empty space where placed non-visible physic body_ - StaticModel began move.
it’s all very complicated.

I found some useful examples with Ogre’s SofBody
and get some methods from this topics: bulletphysics.org/Bullet/phpBB3/ … c9bd045dca

I do not understand if I create SB from original mesh (with dupVerts, from as it is)
btSoftBodyWorldInfo* softBodyWorldInfo_ = GetPhysicsWorld()->GetSoftBodyWorld();
body_ = btSoftBodyHelpers::CreateFromTriMesh(*softBodyWorldInfo_, vertices[0], &indexes[0], ntriangles);

Is it still need to do SB->appendLink() and SB->appendFace() after creation or no ?

Also Bullent provides the btTriangleIndexVertexArray, and I don’t know maybe needed use it for create mesh and only then pass it in btSoftBodyHelpers::CreateFromTriMesh ?


#18

Good to hear that you worked it out. I got your code working but the physics behavior is very different than my original - the mushroom caps get flattened whey the model hits the ground for some reason.

In regards to the renderables and softbody vertex buffer, both need to use the same vertex buffer, otherwise, you’ll see a mismatch.

Steps to use the vertex buffer would be:

  1. clone the model. the original model’s verts/index buffer should not be used in case the model is instanced several times in the scene.
  2. remove duplicate verts/indeces from the cloned model’s verts buffer (this should be moved to the VertexBuffer class eventually) -> new verts and index buffer
  3. assign the new verts/index buffer to the StaticModelt->SetModel( cloneModel );
  4. create softbody from the same verts/index buffer which creates appLinks and Faces

Awesome job with the PhysicsWorld and SoftBody class updates.

edit: I had the same exact problem with the softbody not becoming active and had to call 1) SetMass() to place the softbody into the world and then 2) manually call Activate() to activate the body.


#19

I figured out what was causing the differences in physics behavior from my sample code to what I built from your branch tag. I’ll list them:
a) calling setInterpolationWorldTransform() causes the object to be placed offset from the position specified and sometimes got buried in the ground - I changed this to setTransform() and the object is placed where I specified.
b) whenever setTotalMass() is called, a call to setPose() must be called as the object’s face mass/inverse mass is dependent on totalMass
c) higher the mass of the object, harder for colliding objects to penetrate the softbody and the colliding objects bounce off at faster velocity, lesser mass would cause objects to penetrate more easily. - changed it to mass of 50 and colliders didn’t penetrate on the caps.


#20

Uploaded a progress video, see the OP.