Coupling SoftBodies with my rendering engine...

Zeal
Posts: 47
Joined: Thu Oct 18, 2007 6:49 am

Coupling SoftBodies with my rendering engine...

Post by Zeal »

I am trying to implement SoftBodies, but im a little fuzzy on how exactly to couple things with my rendering engine (Ogre). I have my 'btSoftRigidDynamicsWorld' setup, and 'btSoftBodyHelpers::CreateEllipsoid' seems to work (I can add the body to the world with no errors). I also know how to create dynamic vertex buffers using Ogre (I assume dynamic vertex buffers are the optimal way to render soft bodies?). And lastly I have a bit of code (shown below) that finds all active soft bodies every frame.

What I dont quite understand is, when a soft body is active/updating, how do I 'extract' its vertex information, and then pass that information to Ogre in a format it will be able to work with? For example, every frame I look for active soft bodies via...

Code: Select all

	int size = int(mWorld->getNumCollisionObjects());
	for ( int i = 0; i < size; ++i ) {

		btCollisionObject* colObj = mWorld->getCollisionObjectArray()[i];
		if ( colObj->isActive() ) {

			//we found a active soft body!
			if ( colObj->getInternalType() == btCollisionObject::CO_SOFT_BODY ) {
				
				btSoftBody* body = btSoftBody::upcast(colObj);
				if ( body ) {
				
					//here is where I need to extract the vertex information, in a format
					//that I can pass to Ogre...

				}

			}
		}
	}
Neither the collision object nor the body seem to have any 'vertex list' parameter. I know the answer has to be somewhere in the demo files, but there is an aweful lot there to wade through.

Thanks for any help!

*also it would be nice if somebody could explain the basic steps behind taking a body/collision shape, and constructiong the vertex/index list for your rendering engine.
gunnar
Posts: 18
Joined: Fri Jun 20, 2008 2:01 pm

Re: Coupling SoftBodies with my rendering engine...

Post by gunnar »

This may be completely wrong, but playing with the softbody demoes some, I put together this piece of code to extract the vertex data from the softbody. As for passing this stuff to Ogre.. no idea, but you can probably update Ogre meshes vertex buffers in the same loop.

Code: Select all

btSoftBody* body = btSoftBody::upcast(colObj);

for(int i = 0 ; i < body->m_faces.size() ; i++) {
          btSoftBody::Face face = body->m_faces[i];

          std::cout << face.m_n[0]->m_x.x() << " " << face.m_n[0]->m_x.y() << " " << face.m_n[0]->m_x.z() << std::endl;
          face.m_n[0]->m_x.x();
          face.m_n[0]->m_x.y();
          face.m_n[0]->m_x.z(); 
          
          face.m_n[1]->m_x.x();
          face.m_n[1]->m_x.y();
          face.m_n[1]->m_x.z(); 
          
          face.m_n[2]->m_x.x();
          face.m_n[2]->m_x.y();
          face.m_n[2]->m_x.z(); 
        }
Zeal
Posts: 47
Joined: Thu Oct 18, 2007 6:49 am

Re: Coupling SoftBodies with my rendering engine...

Post by Zeal »

Ok thanks that helps. I didnt even think about looking for public members (I assumed everything would only be exposed via 'getters').

Anyway, so I see now how to get a listing of all the faces/triangles, but I really need the actual vertex/index list. I Dont know how I can figure that out just from a list of faces/triangles...
gunnar
Posts: 18
Joined: Fri Jun 20, 2008 2:01 pm

Re: Coupling SoftBodies with my rendering engine...

Post by gunnar »

Zeal wrote: Anyway, so I see now how to get a listing of all the faces/triangles, but I really need the actual vertex/index list. I Dont know how I can figure that out just from a list of faces/triangles...
Well.. you have the vertex/index list right there. If you create a softbody from a trimesh, the surely the list of indices won't change, so you just have to update the vertices and normals, that you get from the softbody. If you study the bullet-2.69/src/BulletSoftBody/btSoftBody.h file, you'll see that the internals of the softbody are layd out a bit differently than a raw pointer to a index+vertex array, so I believe you'll have to gather the neccesary data from the nodes and faces. But then again, I could be wrong, as I have only studied the code.
Zeal
Posts: 47
Joined: Thu Oct 18, 2007 6:49 am

Re: Coupling SoftBodies with my rendering engine...

Post by Zeal »

Well.. you have the vertex/index list right there.
Kinda not really. I mean what I really want to see is a list of vertices/indices, not faces. I dont see anywhere in the softbody class where this information is stored. Am I missing something?

Or are you suggesting I can somehow extract a vertex list from the list of faces? That doesnt sound easy considering im sure some faces share the same vertex...

*btw what exactly is a "Node"?
gunnar
Posts: 18
Joined: Fri Jun 20, 2008 2:01 pm

Re: Coupling SoftBodies with my rendering engine...

Post by gunnar »

I mean what I really want to see is a list of vertices/indices, not faces. I dont see anywhere in the softbody class where this information is stored. Am I missing something?
I don't think its that easy.
Or are you suggesting I can somehow extract a vertex list from the list of faces? That doesnt sound easy considering im sure some faces share the same vertex...
Yes, thats what I think you have to do. The easiest would be to loop through all the faces and create the arrays of indices and vertices from that 1:1, but this would be quite silly. I have not done this myself, but I would imagine that a better approach would be to create a wrapper around the btsoftbody, that handles the initialisation of the softbodies, and keeps an array of indices + an array of pointers to the btsoftbody vertices. Then for each frame you render, you just loop through the vertex pointer array and update vertex buffers in ogre, as you already have the indices.
*btw what exactly is a "Node"?
A Node in this context is just an internal structure of the softbody, it holds the position, normal and whatnot of a face. Here from btSoftBody.h:

Code: Select all

struct Node : Feature {
  btVector3 m_x; // Position
  btVector3 m_q; // Previous step position
  btVector3	m_v; // Velocity
  btVector3 m_f; // Force accumulator
  btVector3 m_n; // Normal
  btScalar m_im; // 1/mass
  btScalar m_area; // Area
  btDbvt::Node* m_leaf; // Leaf data
  int m_battach:1; // Attached
};

struct Face : Feature {
  Node* m_n[3]; // Node pointers
  btVector3 m_normal; // Normal
  btScalar m_ra; // Rest area
  btDbvt::Node* m_leaf; // Leaf data
};
Zeal
Posts: 47
Joined: Thu Oct 18, 2007 6:49 am

Re: Coupling SoftBodies with my rendering engine...

Post by Zeal »

I would imagine that a better approach would be to create a wrapper around the btsoftbody, that handles the initialisation of the softbodies
Hmm but at what point do we see this list of vertices/indices (and WHERE)? And can anyone else confirm that there is no way to extract this information AFTER the body has been created? That seems odd...
Zeal
Posts: 47
Joined: Thu Oct 18, 2007 6:49 am

Re: Coupling SoftBodies with my rendering engine...

Post by Zeal »

Ok I have done a little more research, please correct me if I am wrong...

It seems like a "Node" is the same as a 'vertex' (in the rendering sense of the word), correct? But for a given body, I dont see where a list of these nodes is stored... Also for efficiencies sake im sure there has to be a index buffer being stored somewhere too, but where I have no idea...
gunnar
Posts: 18
Joined: Fri Jun 20, 2008 2:01 pm

Re: Coupling SoftBodies with my rendering engine...

Post by gunnar »

Hmm but at what point do we see this list of vertices/indices (and WHERE)? And can anyone else confirm that there is no way to extract this information AFTER the body has been created? That seems odd...
You wanted the code, you got it! :wink: Here goes:

Include things

Code: Select all

#include <map>
#include <vector>
Create a cube

Code: Select all

const int NUM_TRIANGLES = 12;
const int NUM_VERTICES = 8;
const int NUM_INDICES  = NUM_TRIANGLES * 3;

btScalar gVertices[NUM_VERTICES * 3] = {
  // Front
  btScalar(-0.5), btScalar(-0.5), btScalar(-0.5),
  btScalar(0.5), btScalar(-0.5), btScalar(-0.5),
  btScalar(0.5), btScalar(0.5), btScalar(-0.5),
  btScalar(-0.5), btScalar(0.5), btScalar(-0.5),
  //Back
  btScalar(-0.5), btScalar(-0.5), btScalar(0.5),
  btScalar(0.5), btScalar(-0.5), btScalar(0.5),
  btScalar(0.5), btScalar(0.5), btScalar(0.5),
  btScalar(-0.5), btScalar(0.5), btScalar(0.5)
};

int gIndices[NUM_INDICES][3] = {
  //Front
  {0,1,2},
  {0,2,3},
  //Back
  {4,5,6},
  {4,6,7},
  //Bottom
  {0,1,5},
  {0,5,4},
  //Top
  {3,2,6},
  {3,6,7},
  //Left side
  {4,0,3},
  {4,3,7},
  //Right side
  {1,5,6},
  {1,6,2}
};
Create the softbody

Code: Select all

btSoftBody* cubeSoftBody = btSoftBodyHelpers::CreateFromTriMesh(pdemo->m_softBodyWorldInfo, gVertices, &gIndices[0][0], NUM_TRIANGLES);
cubeSoftBody->m_cfg.kDF = 0.5;
cubeSoftBody->m_materials[0]->m_kLST = 0.1;
cubeSoftBody->m_cfg.kMT = 0.05;
cubeSoftBody->randomizeConstraints();
cubeSoftBody->scale(btVector3(6,6,6));
cubeSoftBody->setTotalMass(100,true);
cubeSoftBody->setPose(true,true);
Create placeholders for indices and vertices (this is the stuff you wanted, right?)

Code: Select all

//Vertices
std::map<int, btSoftBody::Node*> vertices;

//Indices
std::vector<int> indices;
Extract the vertex and index information from the btSoftBody (only needed when the softbody is created)

Code: Select all

//Temporary placeholder for nodes
std::map<btSoftBody::Node*, int> node_map;

for(int i = 0 ; i < cubeSoftBody->m_faces.size() ; i++) {
  btSoftBody::Face face = cubeSoftBody->m_faces[i];

  for(int j = 0 ; j < 3 ; j++) {
    if(node_map.find(face.m_n[j]) == node_map.end()) {
      node_map.insert(std::make_pair(face.m_n[j], node_map.size()));
    }
  }

  for(int j = 0 ; j < 3 ; j++) {
    indices.push_back(node_map.find(face.m_n[j])->second);
  }
}

//Reverse node->index to index->node (should be unique on both sides)
std::map<btSoftBody::Node*, int>::const_iterator node_iter;
for(node_iter=node_map.begin(); node_iter != node_map.end(); ++node_iter) {
  vertices.insert(std::make_pair(node_iter->second, node_iter->first));
}

//Add softbody
pdemo->getSoftDynamicsWorld()->addSoftBody(cubeSoftBody);
And now, all you need to do to sync your vertexbuffers every frame in Ogre is to iterate through the indices (vector) OR vertices (map).

Code: Select all

//Iterate through the indices
for(int i = 0 ; i < indices.size() ; i++) {
  btSoftBody::Node* node = vertices.find(indices.at(i))->second;
  std::cout << "update vertex with index: " << i << " [v](" << node->m_x.x() << " " << node->m_x.y() << " " << node->m_x.z() << ")" << std::endl;
}

//OR

//Iterate through the vertices
std::map<int, btSoftBody::Node*>::const_iterator it;
for(it=vertices.begin(); it != vertices.end(); ++it) {
  int v_index = it->first;
  btSoftBody::Node* node = it->second;
  std::cout << "update vertex with index: " << v_index << " [v](" << node->m_x.x() << " " << node->m_x.y() << " " << node->m_x.z() << ")" << std::endl;
}
It may not be the best of code, but It works for me.. Let me know if things are still unclear.
gunnar
Posts: 18
Joined: Fri Jun 20, 2008 2:01 pm

Re: Coupling SoftBodies with my rendering engine...

Post by gunnar »

Or put in other words (or code), this is the functionality you would need in your wrapper class:

PS: C++ is not my "mothers tongue", so beware of some blunders in here. The code is at least tested with a cube and the infamous bunny mesh :)

Code: Select all

/** immutable class that holds mesh data about a softbody */
class MeshData {
public:
  const std::map<int, btSoftBody::Node*> vertices; //Vertices <vertex index, vertex>
  const std::vector<int> indices; //Indices
  MeshData(std::map<int, btSoftBody::Node*> sb_vertices, std::vector<int> sb_indices) : vertices(sb_vertices), indices(sb_indices) {}
  ~MeshData() {}
};

/** Extract vertices and indices from a softbody */
static MeshData* getMeshDataFromSoftBody(const btSoftBody* const softBody) {

  //Node map
  std::map<btSoftBody::Node*, int> node_map;

  //Vertices and indices
  std::map<int, btSoftBody::Node*> sb_vertices;
  std::vector<int> sb_indices;

  //Loop through the faces
  for(int i = 0 ; i < softBody->m_faces.size() ; i++) {
    btSoftBody::Face face = softBody->m_faces[i];

    for(int j = 0 ; j < 3 ; j++) {
      if(node_map.find(face.m_n[j]) == node_map.end()) {
        int index_to_add = node_map.size();
        node_map.insert(std::make_pair(face.m_n[j], index_to_add));
      }
    }

    for(int j = 0 ; j < 3 ; j++) {
      sb_indices.push_back(node_map.find(face.m_n[j])->second);
    }
  }

  //Reverse node->index to index->node (should be unique on both sides)
  std::map<btSoftBody::Node*, int>::const_iterator node_iter;
  for(node_iter=node_map.begin(); node_iter != node_map.end(); ++node_iter) {
    sb_vertices.insert(std::make_pair(node_iter->second, node_iter->first));
  }

  // Create a new MeshData instance
  MeshData* meshData = new MeshData(sb_vertices, sb_indices);

  return meshData;
}

/** Sync mesh data with graphics engine */
static void syncGraphicsEngine(const MeshData* const meshData) {
    // == Loop through vertices (clever way)
    std::map<int, btSoftBody::Node*>::const_iterator it;
    for(it=meshData->vertices.begin(); it != meshData->vertices.end(); ++it) {
      int v_index = it->first;
      btSoftBody::Node* node = it->second;

      //TODO: UPDATE THE GRAPHICS ENGINE VERTEX BUFFERS HERE

      std::cout << "update vertex with index: " << v_index << " [v](" << node->m_x.x() << " " << node->m_x.y() << " " << node->m_x.z() << ")" << std::endl;
    }
    std::cout << std::endl;
    // ==
}

/** Print the contents of a meshdata object instance */
static void printMeshData(const MeshData* const meshData) {
  //List vertices
  std::cout << "vertices:" << std::endl;
  std::map<int, btSoftBody::Node*>::const_iterator iter;
  for(iter=meshData->vertices.begin(); iter != meshData->vertices.end(); ++iter) {
    btSoftBody::Node* node = iter->second;
    std::cout << iter->first << " [v](" << node->m_x.x() << " " << node->m_x.y() << " " << node->m_x.z() << ")" << std::endl;
  }
  std::cout << std::endl;

  //List indices
  std::cout << "indices:" << std::endl;
  for(int i = 0 ; i < meshData->indices.size() ; i++) {
    if(i > 0) {
      if(i > 0 && i % 3 == 0) {
        std::cout << std::endl;
      } else {
        std::cout << ",";
      }
    }
    std::cout << meshData->indices.at(i);
  }
  std::cout << std::endl << std::endl;
}
And to use this code, you could do something like shown in the code block below (tested in SoftBody.cpp bullet demo). You would want to keep a pointer to the MeshData object instance in your wrapper class, so you don't have to recreate it every frame, and use the information in there to sync your graphics engine.

Code: Select all

static void Init_BunnyMatch(SoftDemo* pdemo) {
  const int NUM_TRIANGLES = 12;
  const int NUM_VERTICES = 8;
  const int NUM_INDICES  = NUM_TRIANGLES * 3;

  btScalar gVertices[NUM_VERTICES * 3] = {
    // Front
    btScalar(-0.5), btScalar(-0.5), btScalar(-0.5),
    btScalar(0.5), btScalar(-0.5), btScalar(-0.5),
    btScalar(0.5), btScalar(0.5), btScalar(-0.5),
    btScalar(-0.5), btScalar(0.5), btScalar(-0.5),
    //Back
    btScalar(-0.5), btScalar(-0.5), btScalar(0.5),
    btScalar(0.5), btScalar(-0.5), btScalar(0.5),
    btScalar(0.5), btScalar(0.5), btScalar(0.5),
    btScalar(-0.5), btScalar(0.5), btScalar(0.5)
  };

  int gIndices[NUM_TRIANGLES][3] = {
        //Front
        {0,1,2}, {0,2,3},
        //Back
        {4,5,6}, {4,6,7},
        //Bottom
        {0,1,5}, {0,5,4},
        //Top
        {3,2,6}, {3,6,7},
        //Left side
        {4,0,3}, {4,3,7},
        //Right side
        {1,5,6}, {1,6,2}
  };

  btSoftBody* cubeSoftBody = btSoftBodyHelpers::CreateFromTriMesh(pdemo->m_softBodyWorldInfo, gVertices, &gIndices[0][0], NUM_TRIANGLES);
  cubeSoftBody->m_cfg.kDF = 0.5;
  cubeSoftBody->m_materials[0]->m_kLST = 0.1;
  cubeSoftBody->m_cfg.kMT = 0.05;
  cubeSoftBody->randomizeConstraints();
  cubeSoftBody->scale(btVector3(6,6,6));
  cubeSoftBody->setTotalMass(100,true);
  cubeSoftBody->setPose(true,true);

  MeshData* meshData = getMeshDataFromSoftBody(cubeSoftBody);

  pdemo->getSoftDynamicsWorld()->addSoftBody(cubeSoftBody);

  syncGraphicsEngine(meshData);

  printMeshData(meshData);
}
Zeal
Posts: 47
Joined: Thu Oct 18, 2007 6:49 am

Re: Coupling SoftBodies with my rendering engine...

Post by Zeal »

OK so it seems like this is what you were talking about originally, just 'figuring out' the vertex/index list via the face list. Its a bit messy, but I suppose it does 'work'.

The only thing that bothers me is, we still dont know where the node/index list is stored. Bullet HAS to be keeping track of this information somewhere, right? It just seems so silly for us to generate it twice, when it has to be there somewhere already...
Zeal
Posts: 47
Joined: Thu Oct 18, 2007 6:49 am

Re: Coupling SoftBodies with my rendering engine...

Post by Zeal »

Anyone else have any insight? I still cant find where Bullet is maintaining a list of nodes/indices, it has to be there somewhere right?

Or can anyone confirm that such a list is NOT stored anywhere? And that the above method of iterating through the faces is the only way to build such a list?
Nathanael
Posts: 78
Joined: Mon Nov 13, 2006 1:44 am

Re: Coupling SoftBodies with my rendering engine...

Post by Nathanael »

Vertex positions are stored in btSoftBody::Node::m_x,
so:

Code: Select all

btSoftBody*      body=...;
const btVector* positions=&body->m_nodes[0].m_x;
const int           stride=sizeof(btSoftBody::Node);
/* Update positions in your display mesh */
Triangle are stored in btSoftBody::Face, so you can retrieve vertex index with:

Code: Select all

btSoftBody*      body=...;
const btSoftBody::Node* nodebase=&body->m_nodes[0];
for(int i=0;i<body->m_faces.size();++i)
{
const btSoftFace::Face& face=body->m_faces[i];
const int indices[]={ int(face.m_n[0]-nodebase),
                            int(face.m_n[1]-nodebase),
                            int(face.m_n[2]-nodebase)};
/* Do some */
}
Hope it help.
Zeal
Posts: 47
Joined: Thu Oct 18, 2007 6:49 am

Re: Coupling SoftBodies with my rendering engine...

Post by Zeal »

Hmm so youre saying there is no 'index list' stored by default? You have to generate/store it on your own? I would have thought for performance/efficiencies sake Bullet would be maintaining this sort of stuff on its own. I guess it doesnt need/care about index lists?

*oh one last side question. Do ALL bodies (rigid and soft) have nodes/faces? I just cant imagine what the node/face list for a sphere would look like..
User avatar
nikki
Posts: 22
Joined: Sat Jun 14, 2008 3:38 pm
Location: Doha, Qatar.

Re: Coupling SoftBodies with my rendering engine...

Post by nikki »

I'm not very sure, but I guess that the sphere collision is checked using the radius, not points.