btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

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

btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by lgvier »

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: Select all

// 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: Select all

// 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
Shockeye
Posts: 10
Joined: Sat Feb 07, 2009 10:48 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by Shockeye »

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 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?
Shockeye
Posts: 10
Joined: Sat Feb 07, 2009 10:48 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by Shockeye »

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 ...
lgvier
Posts: 8
Joined: Sat Apr 04, 2009 6:24 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by lgvier »

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: Select all

	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
lgvier
Posts: 8
Joined: Sat Apr 04, 2009 6:24 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by lgvier »

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: Select all

		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.
Shockeye
Posts: 10
Joined: Sat Feb 07, 2009 10:48 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by Shockeye »

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: Select all


//(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?
Shockeye
Posts: 10
Joined: Sat Feb 07, 2009 10:48 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by Shockeye »

It was easy - m_nodes is public - who'd have thunk it? :D

However, accessing the nodes directly didn't work either. :(
lgvier
Posts: 8
Joined: Sat Apr 04, 2009 6:24 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by lgvier »

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
Shockeye
Posts: 10
Joined: Sat Feb 07, 2009 10:48 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by Shockeye »

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:
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/viewt ... =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.
lgvier
Posts: 8
Joined: Sat Apr 04, 2009 6:24 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by lgvier »

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
You do not have the required permissions to view the files attached to this post.
Shockeye
Posts: 10
Joined: Sat Feb 07, 2009 10:48 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by Shockeye »

Thanks, lgvier
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.
wingated
Posts: 3
Joined: Wed Sep 16, 2009 12:10 pm

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by wingated »

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.
lgvier
Posts: 8
Joined: Sat Apr 04, 2009 6:24 am

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by lgvier »

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: Select all

	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 =]
wingated
Posts: 3
Joined: Wed Sep 16, 2009 12:10 pm

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by wingated »

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: Select all

	  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: Select all

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)...
yarri
Posts: 2
Joined: Thu Jul 23, 2009 8:54 pm

Re: btSoftBodyHelpers::CreateFromTriMesh - Ogre version?

Post by yarri »

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