Bullet's SoftBody physics example

Share your helpful Urho3D code snippets, samples and tutorials here.

Bullet's SoftBody physics example

PostPosted by Lumak » 31 Aug 2015, 14:42

I became interested in porting bullet's softbody physics after reading Josh's post, http://urho3d.prophpbb.com/topic1361.html.
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
Code: Select all
. . .
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
Code: Select all
#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
Code: Select all
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:
Code: Select all
  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.
Last edited by Lumak on 20 Sep 2015, 21:21, edited 1 time in total.
Lumak
Have many posts
Have many posts
 
Posts: 425
Joined: 08 Jun 2015, 15:38

Re: Bullet's SoftBody physics example

PostPosted by dragonCASTjosh » 31 Aug 2015, 14:52

thanks ill give it a go
User avatar
dragonCASTjosh
Moderator
Moderator
 
Posts: 205
Joined: 04 Aug 2015, 18:59

Re: Bullet's SoftBody physics example

PostPosted by codingmonkey » 31 Aug 2015, 16:39

Thanks for this example.
Good starting point for moving forward to clothes and hair
User avatar
codingmonkey
Have many posts
Have many posts
 
Posts: 517
Joined: 21 Oct 2014, 19:26
Location: Russian Federation, Novosibirsk

Re: Bullet's SoftBody physics example

PostPosted by practicing01 » 31 Aug 2015, 17:56

That's hot. Any idea of what the performance will be like on android?
Image
User avatar
practicing01
Active user
Active user
 
Posts: 145
Joined: 03 Oct 2014, 11:34
Location: Puerto Rico

Re: Bullet's SoftBody physics example

PostPosted by rasteron » 31 Aug 2015, 23:24

This looks cool Lumak! Thanks for sharing :) You should do a PR for next release. Yes, I'm also wondering about android performance.
User avatar
rasteron
Have many posts
Have many posts
 
Posts: 437
Joined: 07 Mar 2014, 07:46
Location: web

Re: Bullet's SoftBody physics example

PostPosted by Lumak » 01 Sep 2015, 06:02

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.
Lumak
Have many posts
Have many posts
 
Posts: 425
Joined: 08 Jun 2015, 15:38

Re: Bullet's SoftBody physics example

PostPosted by Mike » 01 Sep 2015, 09:11

Many thanks for sharing this Lumak :P

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.
User avatar
Mike
Moderator
Moderator
 
Posts: 353
Joined: 16 Jan 2014, 20:35
Location: France

Re: Bullet's SoftBody physics example

PostPosted by Lumak » 02 Sep 2015, 12:41

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.
Lumak
Have many posts
Have many posts
 
Posts: 425
Joined: 08 Jun 2015, 15:38

Re: Bullet's SoftBody physics example

PostPosted by Mike » 02 Sep 2015, 16:07

Thanks, for now I give up.
User avatar
Mike
Moderator
Moderator
 
Posts: 353
Joined: 16 Jan 2014, 20:35
Location: France

Re: Bullet's SoftBody physics example

PostPosted by Mike » 03 Sep 2015, 10:26

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

SoftBody.h
Show spoiler
Code: Select all
#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
Show spoiler
Code: Select all
#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
Show spoiler
Code: Select all
#include <Urho3D/Physics/SoftBody.h>


11_Physics.cpp
Show spoiler
Code: Select all
    // 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();
User avatar
Mike
Moderator
Moderator
 
Posts: 353
Joined: 16 Jan 2014, 20:35
Location: France

Next

Return to Code Exchange

Who is online

Users browsing this forum: No registered users and 1 guest

cron