Physics Simulation Forum

 

All times are UTC




Post new topic Reply to topic  [ 39 posts ]  Go to page 1, 2, 3  Next
Author Message
PostPosted: Sat Apr 04, 2009 8:29 am 
Offline

Joined: Sat Apr 04, 2009 6:24 am
Posts: 8
Hello,

Did anyone around already developed some code to import an Ogre Mesh to a btSoftBody?

I have it almost working, but some vertices are not shared on the mesh, so I don't know how to link the triangles - It ends up loading the triangles correctly, but not linked to each other, so the object 'falls apart' on the simulation.

Here's my current code (two versions with the same result):

Code:
// using btSoftBodyHelpers::CreateFromTriMesh
   btScalar vertices[mVertexCount * 3];
   int i,j;
   for(i=0, j=0; i < mVertexCount; i++)
   {
      Vector3 v =  mVertexBuffer[i];
      vertices[j++] = v.x;
      vertices[j++] = v.y;
      vertices[j++] = v.z;
   }
   
   int ntriangles = mIndexCount / 3;
   int *indexes = (int*) mIndexBuffer;
   return btSoftBodyHelpers::CreateFromTriMesh(*worldInfo, vertices, indexes, ntriangles);

Code:
// doing basically what btSoftBodyHelpers::CreateFromTriMesh does
   unsigned int ntriangles = mIndexCount / 3;
   
   btAlignedObjectArray<bool>      chks;
   btAlignedObjectArray<btVector3>   vtx;
   chks.resize(mVertexCount*mVertexCount,false);
   vtx.resize(mVertexCount);
   
   unsigned int maxidx = mVertexCount;
   
   int i, j;
   for(i=0; i < mVertexCount; i++)
   {
      vtx[i] = Convert::toBullet(mVertexBuffer[i]);
   }
   
   btSoftBody*      psb=new btSoftBody(worldInfo,vtx.size(),&vtx[0],0);
   
#define IDX(_x_,_y_) ((_y_)*maxidx+(_x_))
   
   for( i=0; i < mIndexCount; i+=3)
   {
      const int idx[]={mIndexBuffer[i],mIndexBuffer[i+1],mIndexBuffer[i+2]};
      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;
            psb->appendLink(idx[j],idx[k]);
         }
      }
      printf("face %d %d %d \n", idx[0],idx[1],idx[2]);
      psb->appendFace(idx[0],idx[1],idx[2]);
      
   }
   
#undef IDX
   
   psb->randomizeConstraints();
   return(psb);


Thanks!
-Geovani


Top
 Profile  
 
PostPosted: Sun Apr 05, 2009 6:32 am 
Offline

Joined: Sat Feb 07, 2009 10:48 am
Posts: 10
Hi, Igvier
I've been working on the same thing.
You have to merge the verts, which is horrendously complicated.

You have to extract the mesh data, which may be split over multiple submeshes. Even if it is a single submesh, the way the UV mapping of textures work means there are multiple vertices for the same point. If derive your softmesh from this data it just falls apart like you say.

You need to build up a vector or array of merged vertices, and a lookup table so you can match the indices from the original mesh to the new merged vertices.
pseudocode:
for each vertex v1 in the mesh:
for each vertex v2 in MergedVertices:
if v1==v2:
store the index of v2 in a lookup table eg IndexLookup[index of v1] = index of v2
else:
add v1 to MergedVertices

then build up your Indices
for each int i in mesh indices:
store the value at IndexLookup[i] in MergedIndices

then create your softbody using MergedVertices & MergedIndices

Sounds simple.
However in reality, you need to deal with submeshes (and submeshes with shared vertices), which adds another loop around the whole thing, plus you still need to be able to work backwards to update the Ogre vertices for display.

I've tried doing it using vectors for efficiency, but it's so complicated - and using iterators instead of [] made it more so. If I started again I think I would use maps, I think they would make my system of lookup tables more understandable, although less efficient.

I just got it to the point where I can build a trimesh that doen't fall apart, but it's not working properly. If I load a simple textured cube, it stays together. But if I load anything more complicated(by which I mean subdivided cubes) the debug renderer won't display them. It's weird. I manually check the data of the cube (8 triangles per side) in debug mode and its *seems* to be correct, but it doesn't debug render. Frame rate dies, too. Its only 48 triangles. And yet if I use btSoftBodyHelpers::CreateEllipsoid that renders perfectly with many more triangles at a reasonable frame rate. Even my data was corrupt, you'd think I'd get a few random triangles - or an assert from Bullet.

Any ideas on that?


Top
 Profile  
 
PostPosted: Sun Apr 05, 2009 6:58 am 
Offline

Joined: Sat Feb 07, 2009 10:48 am
Posts: 10
Well after posting all that, I chanced upon your post over at OgreAddons, where you mentioned btSoftBody.appendLink()
If that does what I think it does (based on the context of your post), it would be a lot easier than the nonsense I've been trying, so I don't know why you were complaining about doing it manually for each node.

What does appendLink do? Does it joint nodes with in the softbody without having to change your original data. I sure wish there was more documentation! I guess I'll have to experiment.

If it does join nodes, then just search for matches and join them. As long as you do this before you start the simulation, matching verts should be easy to find:-)


If it does work, I have wasted a lot of time on my other solution ...


Top
 Profile  
 
PostPosted: Sun Apr 05, 2009 8:29 pm 
Offline

Joined: Sat Apr 04, 2009 6:24 am
Posts: 8
Hello Shockeye,

The appendLink seems to work just like that, by binding points, but I also couldn't find any documentation.
I tried to use appendLink for all the vertices with the same coordinates, but then the debugger stopped drawing it, so I don't know if there's something wrong with my code or with the debug drawer. :roll:
This is the code I used for the appendLink stuff (not very efficient yet as the same vector will be tested twice):
Code:
   for(i=0; i < mVertexCount; i++)
   {
      for( j=0; j < mVertexCount; j++)
      {
         if (j != i) {
            if (mVertexBuffer[i] == mVertexBuffer[j]) {
               printf("equal vertices :) %d %d \n", i, j);
               if(!chks[IDX(i, j)])
               {
                  chks[IDX(i, j)]=true;
                  chks[IDX(i, j)]=true;
                  psb->appendLink(i, j);
               }
            }
         }
      }


I was going to try remapping the indices as you did..
Thanks for your input. Maybe we can work together to find an easy solution for that. It's good to know I'm not alone =]

Cheers


Top
 Profile  
 
PostPosted: Mon Apr 06, 2009 3:41 am 
Offline

Joined: Sat Apr 04, 2009 6:24 am
Posts: 8
Hello again,

I got the dupes removed and working \o/
But still it would be much better to keep the vertices equal between ogre and bullet

This is what I'm doing:
Code:
      int dupVertices[mVertexCount];
      int dupVerticesCount = 0;
      int i,j;
      int newIndexes[mVertexCount];
      for(i=0; i < mVertexCount; i++)
      {
         Vector3 v1 =  mVertexBuffer[i];
         dupVertices[i] = -1;
         newIndexes[i] = i - dupVerticesCount;
         for(j=0; j < i; j++)
         {
            Vector3 v2 =  mVertexBuffer[j];
            if (v1 == v2) {
               dupVertices[i] = j;
               dupVerticesCount++;
               break;
            }
         }
      }
      printf("dupVerticesCount %d\n", dupVerticesCount);
      
      int newVertexCount = mVertexCount - dupVerticesCount;
      printf("newVertexCount %d\n", newVertexCount);
      btScalar vertices[newVertexCount * 3];
      for(i=0, j=0; i < mVertexCount; i++)
      {
         if (dupVertices[i] == -1) {
            Vector3 v =  mVertexBuffer[i];
            vertices[j++] = v.x;
            vertices[j++] = v.y;
            vertices[j++] = v.z;
         }
      }
      
      int indexes[mIndexCount];
      int idx, idxDup;
      for(i=0; i < mIndexCount; i++)
      {
         idx = mIndexBuffer[i];
         idxDup = dupVertices[idx];
         printf("dup %d\n", idxDup);
         idx = idxDup == -1 ? idx : idxDup;
         indexes[i] = newIndexes[idx];
      }
      int ntriangles = mIndexCount / 3;
      
      return btSoftBodyHelpers::CreateFromTriMesh(*worldInfo, vertices, indexes, ntriangles);


Cheers
Geovani


Last edited by lgvier on Wed Apr 08, 2009 12:55 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Tue Apr 07, 2009 12:05 pm 
Offline

Joined: Sat Feb 07, 2009 10:48 am
Posts: 10
Well, I tried appendLink and I can't seem to get it to work. I ripped out my horrible vertex merging code and just put the mesh straight into CreateFromTriMesh, and that worked fine, the softbody renders ok with the debugdrawer, and then falls to pieces as usual.

Then I added my new code which finds the matching verts and calls appendlink on them. No there's no debug render. Also I get an error :
"Windows has triggered a breakpoint in BtOgreSoftbodyTest.exe.

This may be due to a corruption of the heap, which indicates a bug in BtOgreSoftbodyTest.exe or any of the DLLs it has loaded."

So I figure I must be using appendLink incorrectly. I've been assuming that in:
"mSoftBody->appendLink(int node0,int node1)", the nodes indices would just be equivilent to the indices of the vertices.
Is this correct?

This is my code
Code:

//(std::vector<Ogre::Vector3>MergedVertices is just all submeshes merged, duplicated vertices have not been merged
std::vector<int> LinkedVertices; //flags so i don't link them twice
      LinkedVertices.assign(MergedVertices.size(),0);
      for(v1=MergedVertices.begin();v1!=MergedVertices.end();++v1)
      {
         //find matching verts
         int v1_idx = v1-MergedVertices.begin(); //get index of v1
         if(!LinkedVertices[v1_idx]) //if not already linked
         {
            for(v2=MergedVertices.begin();v2!=MergedVertices.end();++v2)
            {
               
               int v2_idx = v2-MergedVertices.begin(); //get index of v2
               if((!LinkedVertices[v2_idx])&&(*v1)==(*v2))
               {
                           //link the nodes
                   mSoftBody->appendLink(v1_idx,v2_idx);
                  LinkedVertices[v1_idx]=1; //flag these as linked
                  LinkedVertices[v2_idx]=1;
               }
            }
         }
      }


I wonder if it would be better to get the nodes out of the softbody and compare them rather than working from the original mesh data. Then I could use the appendLink(btSoftBody::node*,btSoftBody::node*) overload and be certain I was refering to the correct nodes. How do you get access to the nodes?


Top
 Profile  
 
PostPosted: Tue Apr 07, 2009 12:40 pm 
Offline

Joined: Sat Feb 07, 2009 10:48 am
Posts: 10
It was easy - m_nodes is public - who'd have thunk it? :D

However, accessing the nodes directly didn't work either. :(


Top
 Profile  
 
PostPosted: Tue Apr 07, 2009 11:19 pm 
Offline

Joined: Sat Apr 04, 2009 6:24 am
Posts: 8
Hello,

Removing the dupes seems a better approach now.. I guess the overhead of removing the dupes is compensated during the simulation by having less vertices to process :-)
Take a look at the code I submitted on my last post in this thread.. It's working great even with submeshes and I can update back ogre with the same arrays (dupVertices and newIndexes)

Cheers


Top
 Profile  
 
PostPosted: Fri Apr 10, 2009 6:52 am 
Offline

Joined: Sat Feb 07, 2009 10:48 am
Posts: 10
Hi Igvier,
I took your advice and went back to merging the verts before creating the softbody. After reviewing your code, I realized my previous attempt was too complicated. I was trying to to keep track of submeshes and needlessly complicating things. When I read:
Quote:
It's working great even with submeshes and I can update back ogre with the same arrays (dupVertices and newIndexes)

And I was thinking huh? That code looks way too simple. Then I realized how dumb I was, because of course you have retrieve the data from the ogremesh each frame anyway( because pointer to vertex data is invalidated each frame ) and so you can easily recalculate how the indices map to your lookup table.

My version doesn't remove the surplus vertices yet, I just adjust the indices so far, but it works properly anyway. I'll add that feature later, but I've started working on rendering in Ogre. I'm just using a slightly modified version of the updateOgreMeshFromBulletMesh function found at this post on the Ogre Addon forum: http://www.ogre3d.org/addonforums/viewtopic.php?f=12&t=7476, which you are familar with. I'll need to write a version that can handle more than one submesh, but it works (sort of) with a single submesh.

It modifies the shape of the mesh perfectly, but doesn't seem to update the position of the whole object. Do you know if softbodies can use a MotionState to update the object position? I had assumed they would move on a node by node basis, but it must be relative to a centre of gravity or center of bounding box or something.

Also, do you know anything about bSoftBody::m_userIndexMapping? I wonder if that provides a way updating the rendering mesh automatically on each simulation step.


Top
 Profile  
 
PostPosted: Fri Apr 10, 2009 12:32 pm 
Offline

Joined: Sat Apr 04, 2009 6:24 am
Posts: 8
Hello,

Please find attached the files I created to do both conversions (bullet>ogre&ogre>bullet). I'm using btogre and I sent those files to the author (nikki) so I guess it will be incorporated to the project soon.
btogre is much simpler than ogrebullet because it doesn't extend any classes from ogre nor bullet, it just gives you the tools to integrate them. :wink:

About the position of the object, I'm applying the rotation and translation from bullet to ogre on each frame. I dunno if there's a better way to do it, but that's little overhead compared to the mesh sync anyway.

Hope that helps :mrgreen:
-Geovani


Attachments:
ogre_softbodies.zip [4.41 KiB]
Downloaded 626 times
Top
 Profile  
 
PostPosted: Sat Apr 11, 2009 12:58 am 
Offline

Joined: Sat Feb 07, 2009 10:48 am
Posts: 10
Thanks, lgvier

Quote:
I'm using btogre and I sent those files to the author (nikki) so I guess it will be incorporated to the project soon.


That's what I was hoping to do when I'd finished, but I guess I don't need to now :D
My implementation never got past the experimenting stage.


Top
 Profile  
 
PostPosted: Wed Sep 16, 2009 12:21 pm 
Offline

Joined: Wed Sep 16, 2009 12:10 pm
Posts: 3
Hi all -- first post here.

I've been using the ogre soft body code posted by lgvier. Nice work!

Things seem to be mostly working, but I have a couple of questions. I'm trying to reproduce the bullet "pressure" demo in ogre, and there appears to be a lighting problem with the way that lgvier's code updates the vertices of the objects.

As the sphere drops, it deforms and bounces appropriately. However, the shading of the sphere isn't correct.

The problem appears to be that as bullet updates the triangles in the ogre mesh, the lighting somehow doesn't "know" that. The sphere continues to be illuminated as if it hadn't been deformed at all, meaning that as the sphere slowly rotates, the shading rotates with it (the underside of the sphere should be dark, because of shadows, but as the sphere rotates, the dark underside rotates with it -- eventually becoming the top of the sphere). The shadows appear to have the same problem.

I imagine it's a simple issue -- I probably need to tell ogre to update some sort of information about how the triangles in the sphere mesh are oriented with respect to the lights. That would probably fix both the shading and the shadows. I've searched, but can't seem to find anything.

Any thoughts?

I can post code or a video, if it would help.


Top
 Profile  
 
PostPosted: Wed Sep 16, 2009 2:31 pm 
Offline

Joined: Sat Apr 04, 2009 6:24 am
Posts: 8
Hello,

I later discovered that bullet doesn't update the position of soft bodies, just its vertices' positions, and Ogre seems to have some problem with that - I guess because the vertex position is so different from the location of the object. So I'm applying part of the change to the node position and it seems to fix this kind of problem...
Pls update BtOgreSoftBody::updateOgreMesh() and give it a try:

Code:
   void BtOgreSoftBody::updateOgreMesh()
   {
      Ogre::Node *ogreNode = mEntity->getParentNode();
      
      //printf("updateOgreMesh %d %s %s\n", internalId, mEntity->getName().c_str(), ogreNode->getName().c_str());
      
      MeshPtr mesh = mEntity->getMesh();
      Mesh::SubMeshIterator subMeshI = mesh->getSubMeshIterator();
      SubMesh* subMesh = NULL;
      
      VertexData* vData = NULL;
      VertexDeclaration* vDeclaration = NULL;
      const VertexElement* vPosElement = NULL;
      
      bool isSharedVerticesAdded = false;
      unsigned short bufferIndex = 0;
      HardwareVertexBufferSharedPtr vBuffer;
      
      // Can not do arithmetic operations on void*
      unsigned char* lockedMem = NULL;
      float* vPosition;
      
      btSoftBody::tNodeArray& btNodes = mSoftBody->m_nodes;
      //printf("Bullet nodes size %d\n", btNodes.size());
      
      int ogreVertexIdx = 0;
                btVector3 btPosOffset;
      
      while (subMeshI.hasMoreElements()) {
         subMesh = subMeshI.getNext();
         
         if (subMesh->useSharedVertices) {
            
            if (isSharedVerticesAdded) {
               continue;
            }
            
            vData = mesh->sharedVertexData;
            
            // We need to add shared vertices only once
            isSharedVerticesAdded = true;
         } else  {
            vData = subMesh->vertexData;
         }
         
         vDeclaration = vData->vertexDeclaration;
         vPosElement = vDeclaration->findElementBySemantic(VES_POSITION);
         
         bufferIndex = vPosElement->getSource();
         vBuffer = vData->vertexBufferBinding->getBuffer(bufferIndex);
         
         // Lock the buffer before reading from it
         lockedMem = static_cast<unsigned char*>(vBuffer->lock(HardwareBuffer::HBL_DISCARD));
         


         // Read each vertex
         for (unsigned int i = 0; i < vData->vertexCount; ++i) {
            vPosElement->baseVertexPointerToElement(lockedMem, &vPosition);
            
            int idx = getBulletIndex(ogreVertexIdx);
                                const btVector3 &btPos = btNodes[idx].m_x;
                                if (ogreVertexIdx == 0) {
                                    btPosOffset = btPos;
                                }

                                *vPosition++ = btPos.x() - btPosOffset.x();
                                *vPosition++ = btPos.y() - btPosOffset.y();
                                *vPosition = btPos.z() - btPosOffset.z();
            
            // Point to the next vertex
            lockedMem += vBuffer->getVertexSize();
            
            ogreVertexIdx++;
         }
         
         vBuffer->unlock();
      }

                btTransform transform = mSoftBody->getWorldTransform();
                btQuaternion rot = transform.getRotation();
                ogreNode->setOrientation(rot.w(), rot.x(), rot.y(), rot.z());
                btVector3 pos = transform.getOrigin();
                ogreNode->setPosition(pos.x() + btPosOffset.x(), pos.y() + btPosOffset.y(), pos.z() + btPosOffset.z());
      
   }


good luck =]


Top
 Profile  
 
PostPosted: Wed Sep 30, 2009 3:17 pm 
Offline

Joined: Wed Sep 16, 2009 12:10 pm
Posts: 3
Thanks for the code update! I tried it out, and it didn't help. I think I may know why.

I've been working on it a bit more, and have noticed a few things:

1) When you call mSoftBody->getWorldTransform(), it always returns an identity transformation -- position at the origin, no rotation. That's because the whole notion of world transforms doesn't apply to soft bodies; there's no such thing as global transformation. Instead, the position and orientation of the individual triangles are all represented explicitly.

That means that this code:
Code:
     btTransform transform = mSoftBody->getWorldTransform();
     btQuaternion rot = transform.getRotation();
     ogreNode->setOrientation(rot.w(), rot.x(), rot.y(), rot.z());
     btVector3 pos = transform.getOrigin();
     ogreNode->setPosition(pos.x() + btPosOffset.x(), pos.y() + btPosOffset.y(), pos.z() + btPosOffset.z());


is equivalent to this:

Code:
ogreNode->setPosition(btPosOffset.x(), btPosOffset.y(), btPosOffset.z());


(ie, there's no useful information coming out of getWorldTransform())

2) You're using the zero vertex as a reference point for the node, and updating the node position based on that. I hadn't thought about this general approach, and it seems useful: update the node statistics based on aggregate statistics computed over all of the individual triangles in the soft mesh. A more general version of using the zero vertex would be to compute the mean of all of the triangles in the mesh.

However, this doesn't solve my problem, which is rotation. Somehow, we'd need to compute an aggregate node rotation as a function of all of the positions/orientations of the individual nodes, relative to their original positions. Blech.

An alternative is still to try and inform Ogre that the underlying mesh has changed, and that it needs to update its internal statistics. That might be easier.

Thanks for your help. I'll keep working on it.

-- David

P.S. I see in the bullet code some global transform stuff for collision clusters. Maybe it would be useful here (my rough idea was to wrap each softbody in a collision cluster, so we could reference its transform)...


Top
 Profile  
 
PostPosted: Fri Nov 20, 2009 4:37 am 
Offline

Joined: Thu Jul 23, 2009 8:54 pm
Posts: 2
Hi, did you ever find a solution to this issue of rotating a softbody object? The idea of wrapping a softbody in a collision cluster seems to make sense.

--yarri


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 39 posts ]  Go to page 1, 2, 3  Next

All times are UTC


Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group