Use of btStridingMeshInterface

fullmetalcoder
Posts: 29
Joined: Mon May 19, 2008 5:01 pm

Use of btStridingMeshInterface

Post by fullmetalcoder »

I'm trying to share data between Bullet and a 3D engine (Irrlicht in this case) by using btStridingMeshInterface and I got two problems :

* when I use a btBvhTriangleMeshShape an assert failure prevents the program from starting :
src/BulletCollision/CollisionShapes/btOptimizedBvh.h:361: void btOptimizedBvh::quantize(short unsigned int*, const btVector3&, int) const: Assertion `point.getZ() >=m_bvhAabbMin.getZ()' failed.
* when I use a btConvexTriangleMeshShape no error/warning/assert failure popup but my mesh is not visible in debug draw so presumably not loaded properly while a printf() statement confirms that getLockedReadOnlyVertexBase() is called

Any hints?
voxel
Posts: 14
Joined: Fri Jul 04, 2008 2:23 pm

Re: Use of btStridingMeshInterface

Post by voxel »

Using the btStringMeshInterface was my first choice when I wanted to share data between BULLET and OGRE, but I soon realized there are easier ways:

1) Subclass btTriangleCallback and implement processTriangle() that will allow you to walk through the triangles in a callback.

Code: Select all

DebugDrawManagerTriangleCallback drawCallback();
concaveMesh->processAllTriangles(&drawCallback,aabbMin,aabbMax);
2) Pass the vertex/index data to the shape constructor, but keep it around for your own use:

Code: Select all

// Example - something like this but using your engine data types
size_t vertex_count;
size_t index_count;
Ogre::Vector3 *vertices;
unsigned long *indices;

unsigned int numFaces = index_count / 3;
int vertStride = sizeof(Ogre::Vector3);
int indexStride = 3*sizeof(unsigned long);		
btTriangleIndexVertexArray* va = new btTriangleIndexVertexArray(numFaces,
			(int*)indices,
			indexStride,
			vertex_count,(btScalar*)vertices,vertStride);
btBvhTriangleMeshShape* triShape = new btBvhTriangleMeshShape(va, true);

which lets me modify the vertex/index data or let BULLET do that.
fullmetalcoder
Posts: 29
Joined: Mon May 19, 2008 5:01 pm

Re: Use of btStridingMeshInterface

Post by fullmetalcoder »

THanks for the info. I will see what I can do with btTriangleIndexVertexArray

As for btTriangleCallback, could you explain me how to use it in more details?
fullmetalcoder
Posts: 29
Joined: Mon May 19, 2008 5:01 pm

Re: Use of btStridingMeshInterface

Post by fullmetalcoder »

btStridingMeshInterface is the way to go actually and I solved my problem but in a very dirty way : I add to patch Bullet in the first place...

The patch is extremely simple. Make the InternalProcessAllTriangle function virtual and reimplement it (it's the only one that uses getLockedReadOnlyIndexVertexBase() anyway) to access your data properly. It is extremely important in my case because the vertex data is not stroed simply as an array of geometric info but as an array of struct which contains other data and the vertex position is in the middle of the struct so everything is screwed up.

Another advantage of this method is potential speedup, especially with highly fragmented mesh data (many subparts) because it gets rid of the innerloop switch() statemement and leads to more compact machine code.

I asked for the virtualization of the method on Bullet bugtracker. Let's hope it will make it into Bullet 2.70.
reltham
Posts: 66
Joined: Fri Oct 12, 2007 6:28 pm
Location: San Diego

Re: Use of btStridingMeshInterface

Post by reltham »

Probably a better solution would be to split out the part of InternalProcessAllTriangles that actually does the access to your data and virtualize that bit. Having all of InternalProcessAllTriangles exposed seems wrong to me.
fullmetalcoder
Posts: 29
Joined: Mon May 19, 2008 5:01 pm

Re: Use of btStridingMeshInterface

Post by fullmetalcoder »

The only call that access my data in InternalProcessAllTriangles is getLockedReadOnlyIndexVertexBase. It is already virtualized but, as I explained above this approach did not work for me because of the way my vertex data (owned by the 3D engine) is stored. Besides, as I stated above, this approach is counter-productive because it induces complexities (eg PHY_SHORT, PHY_INT for indices and PHY_FLOAT, PHY_DOUBLE for vertices) in both user code AND library code while the purpose of this class is clearly to be low-level to give as much flexibility as possible to achieve proper data sharing between Bullet and any other part of the application that might use it (most probably a 3D engine).
User avatar
Erwin Coumans
Site Admin
Posts: 4232
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Use of btStridingMeshInterface

Post by Erwin Coumans »

fullmetalcoder wrote:It is extremely important in my case because the vertex data is not stroed simply as an array of geometric info but as an array of struct which contains other data and the vertex position is in the middle of the struct so everything is screwed up.
It would be helpful if you can provide a detailed example how your memory layout is. Are those structures all variable size? It should be possible to use striding and point to a vertex in the middle of another struct and use striding. Another option is to use 'parts', have you considered that?
Another advantage of this method is potential speedup, especially with highly fragmented mesh data (many subparts) because it gets rid of the innerloop switch() statemement and leads to more compact machine code.

I asked for the virtualization of the method on Bullet bugtracker. Let's hope it will make it into Bullet 2.70.
The actual switch statement shouldn't hurt performance much. It should be neglectibly because usually performance goes into other areas (traversing the AABB tree, accessing the actual triangle data and performing the convex collision detection between triangle(s) and other objects.

But if your data is really that irregular we can consider adding another virtual method for 'InternalProcessAllTriangles'.
Thanks for the feedback,
Erwin
fullmetalcoder
Posts: 29
Joined: Mon May 19, 2008 5:01 pm

Re: Use of btStridingMeshInterface

Post by fullmetalcoder »

More informations on the data I use here :
http://irrlicht.convextech.ca/IrrlichtD ... ertex.html
http://irrlicht.convextech.ca/IrrlichtD ... oords.html

My vertex data is basically arrays of either of these structure. I guess a sizeof is enough to get the array stride
but there is an offset needed as the position information is in the middle of the structure. Also the presence of
functions may screw up that offset guess (and so does alignment anyway). Actually I tried to do it the regular
way but it did not work, presumably because of the offset and/or stride.

From a purely practical point of view, regardless of the performance, making this method virtual is :
1) (extremely) useful
2) completely harmless (few people use btStridingMeshInterface and fewer will be willing to reimp this method)
User avatar
Erwin Coumans
Site Admin
Posts: 4232
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Use of btStridingMeshInterface

Post by Erwin Coumans »

fullmetalcoder wrote: My vertex data is basically arrays of either of these structure. I guess a sizeof is enough to get the array stride
but there is an offset needed as the position information is in the middle of the structure.
Indeed, you should be able to use the sizeof as striding, and point to the address of the first position element 'x'. Functions or alignment won't have any impact, so this should just work fine, can you confirm this?
From a purely practical point of view, regardless of the performance, making this method virtual is :
1) (extremely) useful
2) completely harmless (few people use btStridingMeshInterface and fewer will be willing to reimp this method)
Agreed, we can do this. But please confirm that your datastructure just fits fine with the striding mesh interface.

Thanks,
Erwin
fullmetalcoder
Posts: 29
Joined: Mon May 19, 2008 5:01 pm

Re: Use of btStridingMeshInterface

Post by fullmetalcoder »

Confirmed : it does NOT work with the regular InternalProcssAllTriangles()
Here is my code :

Code: Select all

		virtual void InternalProcessAllTriangles(btInternalTriangleIndexCallback* callback,
												const btVector3& aabbMin,const btVector3& aabbMax) const
		{
			#ifdef _TEST_BULLET_IMPL_
			btStridingMeshInterface::InternalProcessAllTriangles(callback, aabbMin, aabbMax);
			
			return;
			#endif
			
			(void)aabbMin;
			(void)aabbMax;
			
			btVector3 triangle[3];
			
			for ( int j = 0; j < m_mesh->getMeshBufferCount(); ++j )
			{
				irr::scene::IMeshBuffer *mb = m_mesh->getMeshBuffer(j);
				
				irr::u32 ic = mb->getIndexCount();
				irr::u16 *indices = mb->getIndices();
				
				btVector3 scaling = getScaling();
				
				if ( mb->getVertexType() == irr::video::EVT_STANDARD )
				{
					irr::video::S3DVertex *vertices = (irr::video::S3DVertex*)mb->getVertices();
					
					for ( irr::u32 i = 0; i < ic; i += 3 )
					{
						const irr::video::S3DVertex& v0 = vertices[indices[i + 0]];
						
						triangle[0].setValue(
										v0.Pos.X * scaling.getX(),
										v0.Pos.Y * scaling.getY(),
										v0.Pos.Z * scaling.getZ()
									);
						
						const irr::video::S3DVertex& v1 = vertices[indices[i + 1]];
						
						triangle[1].setValue(
										v1.Pos.X * scaling.getX(),
										v1.Pos.Y * scaling.getY(),
										v1.Pos.Z * scaling.getZ()
									);
						
						const irr::video::S3DVertex& v2 = vertices[indices[i + 2]];
						
						triangle[2].setValue(
										v2.Pos.X * scaling.getX(),
										v2.Pos.Y * scaling.getY(),
										v2.Pos.Z * scaling.getZ()
									);
						
						callback->internalProcessTriangleIndex(triangle, i, j);
					}
				} else if ( mb->getVertexType() == irr::video::EVT_2TCOORDS ) {
					irr::video::S3DVertex2TCoords *vertices = (irr::video::S3DVertex2TCoords*)mb->getVertices();
					
					for ( irr::u32 i = 0; i < ic; i += 3 )
					{
						const irr::video::S3DVertex& v0 = vertices[indices[i + 0]];
						
						triangle[0].setValue(
										v0.Pos.X * scaling.getX(),
										v0.Pos.Y * scaling.getY(),
										v0.Pos.Z * scaling.getZ()
									);
						
						const irr::video::S3DVertex& v1 = vertices[indices[i + 1]];
						
						triangle[1].setValue(
										v1.Pos.X * scaling.getX(),
										v1.Pos.Y * scaling.getY(),
										v1.Pos.Z * scaling.getZ()
									);
						
						const irr::video::S3DVertex& v2 = vertices[indices[i + 2]];
						
						triangle[2].setValue(
										v2.Pos.X * scaling.getX(),
										v2.Pos.Y * scaling.getY(),
										v2.Pos.Z * scaling.getZ()
									);
						
						callback->internalProcessTriangleIndex(triangle, i, j);
					}
				}
			}
		}

		virtual void getLockedReadOnlyVertexIndexBase(
							const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& stride,
							const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,
							int subpart) const
		{
			static const int vertexDataOffset = sizeof(irr::video::SColor) + sizeof(irr::core::vector3df);
			
			irr::scene::IMeshBuffer *mb = m_mesh->getMeshBuffer(subpart);
			
			*vertexbase = (const unsigned char*)mb->getVertices() + vertexDataOffset;
			numverts = mb->getVertexCount();
			type = PHY_FLOAT;
			
			irr::video::E_VERTEX_TYPE vt = mb->getVertexType();
			
			if ( vt == irr::video::EVT_2TCOORDS )
				stride = sizeof(irr::video::S3DVertex2TCoords);
			else if ( vt == irr::video::EVT_TANGENTS )
				stride = sizeof(irr::video::S3DVertexTangents);
			else
				stride = sizeof(irr::video::S3DVertex);
			
			*indexbase = (const unsigned char*)mb->getIndices();
			indexstride = 3 * sizeof(irr::u16);
			numfaces = mb->getIndexCount() / 3;
			indicestype = PHY_UNSIGNED_SHORT;
			
			//printf("\treading part %i/%i : %i faces\n", subpart + 1, m_mesh->getMeshBufferCount(), numfaces);
		}
		
If I define _TEST_BULLET_IMPL_ the test mesh is not drawn by the debug drawer but it is if I compile without this define...
User avatar
Erwin Coumans
Site Admin
Posts: 4232
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Use of btStridingMeshInterface

Post by Erwin Coumans »

It seems the btTriangleIndexVertexArray is not constructed correctly (we can also try to solve the issue offline, I send an email).

If the mesh contains several parts with different offset/striding, you can create an empty and use 'addIndexedMesh' for each mesh part. Have you tried to directly access the first X component like in the following snippet, and pass this vertexPointer into the btTriangleIndexVertexArray / indexed mesh?

Code: Select all

irr::video::S3DVertex *vtxarray = (irr::video::S3DVertex*)mb->getVertices();
const char* vertexPointer = &(vertices[0].Pos.X);
Thanks,
Erwin
fullmetalcoder
Posts: 29
Joined: Mon May 19, 2008 5:01 pm

Re: Use of btStridingMeshInterface

Post by fullmetalcoder »

btTriangleIndexVertexArray? I'm not using this class but btConvexTriangleMeshShape and btStridingMeshInterface.

Anyway the "regular" interface now works fine. The problem actually lied in the offset and your suggestion of accessing the first component and using its address does work. Still requires patching (unsigned indices...) but making the method virtual is no longer required (though it can't hurt...).

Also, I noticed using debug output that the mesh data was processed a lot of times each loop : InternalProcessAllTriangles is called 46 times per update step whith only one such trimesh and a single other trimesh used for environment (terrain generated by Irrlicht). Completely off-topic but it seems a lot to me, maybe there is room for some improvements here.
User avatar
Erwin Coumans
Site Admin
Posts: 4232
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Use of btStridingMeshInterface

Post by Erwin Coumans »

Non-moving (static) terrain / environment is usually concave, so you should not use btConvexTriangleMeshShape for that, but a combination of btTriangleIndexVertexArray+btBvhTriangleMeshShape.

btConvexTriangleMeshShape is just for convex shapes, is that your purpose? If so, it is better to create a btConvexHullShape and only add the vertices (and don't bother re-using/indexing). Most convex objects contain only few vertices, if there are more then 100 there is usually something wrong.
Still requires patching (unsigned indices...)
Are you sure? Why do you need this patch, what kind of error do you get? As long as your indices are unsigned, the cast to signed integer is unlikely to cause issues.
Also, I noticed using debug output that the mesh data was processed a lot of times each loop
InternalProcessAllTriangles is called once for each dynamic object that has AABB overlap with a mesh. Ignore / disable debug rendering (debug rendering is very slow and should only be used for debugging, so its performance doesn't matter much).

Note that your derived version of InternalProcessAllTriangles ignores the aabbMin/aabbMax. It is better to stick with the default implementation and not overriding it.

Hope this helps,
Erwin
fullmetalcoder
Posts: 29
Joined: Mon May 19, 2008 5:01 pm

Re: Use of btStridingMeshInterface

Post by fullmetalcoder »

Non-moving (static) terrain / environment is usually concave, so you should not use btConvexTriangleMeshShape for that, but a combination of btTriangleIndexVertexArray+btBvhTriangleMeshShape.
My terrain is concave and I use a btTriangleMesh for it but my character is represented as a convex mesh as concave/cocave collision is not supported...
Are you sure? Why do you need this patch, what kind of error do you get? As long as your indices are unsigned, the cast to signed integer is unlikely to cause issues.
Darn yes I'm sure! Irrlicht does use unsigned indices and some meshes do use the full 16bit range for their indices. Casting becomes an issue when it leads to data corruption and data corruption for indices can lead to random segfaults so that's definitely not what I want...
InternalProcessAllTriangles is called once for each dynamic object that has AABB overlap with a mesh. Ignore / disable debug rendering (debug rendering is very slow and should only be used for debugging, so its performance doesn't matter much).
Even with debug drawing turned off InternalProcessTriangles() is called 12 times per update step for my character mesh while there is only one thing to collide with... And it turns out that debug drawing is not repsonsible for these call to InternalProcessAllTriangles(). It looks like that every call to convexSweepTest() leads to 12 call to InternalProcessAllTriangles() instead. Which is a bit preocupying given that there is only two objects (and that these figures concern only one of them).
Note that your derived version of InternalProcessAllTriangles ignores the aabbMin/aabbMax. It is better to stick with the default implementation and not overriding it.
Note that the original version DOES ignore these parameters as well...
User avatar
Erwin Coumans
Site Admin
Posts: 4232
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Use of btStridingMeshInterface

Post by Erwin Coumans »

fullmetalcoder wrote: but my character is represented as a convex mesh as concave/cocave collision is not supported...
So if you really want to use a convex hull of the mesh for your character (instead of the more common capsule) it is recommended to use a btConvexHullShape and only add the vertices. Please do not use the btConvexTriangleMeshShape (we should make it more clear it is very inefficient).
Irrlicht does use unsigned indices and some meshes do use the full 16bit range for their indices.
Good point, if using the full range of 16bit indices is the issue, we need to add support for signed int indeed. Consider it fixed for Bullet 2.70.
And it turns out that debug drawing is not repsonsible for these call to InternalProcessAllTriangles(). It looks like that every call to convexSweepTest() leads to 12 call to InternalProcessAllTriangles() instead.
Note that your derived version of InternalProcessAllTriangles ignores the aabbMin/aabbMax. It is better to stick with the default implementation and not overriding it.
Note that the original version DOES ignore these parameters as well...
InternalProcessTriangles should not be called at all. Are you sure you are creating a btBvhTriangleMeshShape for concave and btConvexHullShape for moving convex meshes? btBvhTriangleMeshShape:: processAllTriangles uses the AABB tree to cull triangles, so it uses the aabbMin/aabbMax instead of calling btStridingMeshInterface::InternalProcessAllTriangles.

Thanks for the feedback,
Erwin
Post Reply