Use of btStridingMeshInterface
-
- Posts: 29
- Joined: Mon May 19, 2008 5:01 pm
Use of btStridingMeshInterface
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?
* 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?
-
- Posts: 14
- Joined: Fri Jul 04, 2008 2:23 pm
Re: Use of btStridingMeshInterface
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.
2) Pass the vertex/index data to the shape constructor, but keep it around for your own use:
which lets me modify the vertex/index data or let BULLET do that.
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);
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);
-
- Posts: 29
- Joined: Mon May 19, 2008 5:01 pm
Re: Use of btStridingMeshInterface
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?
As for btTriangleCallback, could you explain me how to use it in more details?
-
- Posts: 29
- Joined: Mon May 19, 2008 5:01 pm
Re: Use of btStridingMeshInterface
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.
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.
-
- Posts: 66
- Joined: Fri Oct 12, 2007 6:28 pm
- Location: San Diego
Re: Use of btStridingMeshInterface
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.
-
- Posts: 29
- Joined: Mon May 19, 2008 5:01 pm
Re: Use of btStridingMeshInterface
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).
-
- Site Admin
- Posts: 4221
- Joined: Sun Jun 26, 2005 6:43 pm
- Location: California, USA
Re: Use of btStridingMeshInterface
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?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.
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.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.
But if your data is really that irregular we can consider adding another virtual method for 'InternalProcessAllTriangles'.
Thanks for the feedback,
Erwin
-
- Posts: 29
- Joined: Mon May 19, 2008 5:01 pm
Re: Use of btStridingMeshInterface
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)
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)
-
- Site Admin
- Posts: 4221
- Joined: Sun Jun 26, 2005 6:43 pm
- Location: California, USA
Re: Use of btStridingMeshInterface
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?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.
Agreed, we can do this. But please confirm that your datastructure just fits fine with the striding mesh interface.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)
Thanks,
Erwin
-
- Posts: 29
- Joined: Mon May 19, 2008 5:01 pm
Re: Use of btStridingMeshInterface
Confirmed : it does NOT work with the regular InternalProcssAllTriangles()
Here is my code :
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...
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);
}
-
- Site Admin
- Posts: 4221
- Joined: Sun Jun 26, 2005 6:43 pm
- Location: California, USA
Re: Use of btStridingMeshInterface
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?
Thanks,
Erwin
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);
Erwin
-
- Posts: 29
- Joined: Mon May 19, 2008 5:01 pm
Re: Use of btStridingMeshInterface
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.
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.
-
- Site Admin
- Posts: 4221
- Joined: Sun Jun 26, 2005 6:43 pm
- Location: California, USA
Re: Use of btStridingMeshInterface
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.
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
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.
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.Still requires patching (unsigned indices...)
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).Also, I noticed using debug output that the mesh data was processed a lot of times each loop
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
-
- Posts: 29
- Joined: Mon May 19, 2008 5:01 pm
Re: Use of btStridingMeshInterface
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...Non-moving (static) terrain / environment is usually concave, so you should not use btConvexTriangleMeshShape for that, but a combination of btTriangleIndexVertexArray+btBvhTriangleMeshShape.
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...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.
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).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 the original version DOES ignore these parameters as well...Note that your derived version of InternalProcessAllTriangles ignores the aabbMin/aabbMax. It is better to stick with the default implementation and not overriding it.
-
- Site Admin
- Posts: 4221
- Joined: Sun Jun 26, 2005 6:43 pm
- Location: California, USA
Re: Use of btStridingMeshInterface
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).fullmetalcoder wrote: but my character is represented as a convex mesh as concave/cocave collision is not supported...
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.Irrlicht does use unsigned indices and some meshes do use the full 16bit range for their indices.
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.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 the original version DOES ignore these parameters as well...Note that your derived version of InternalProcessAllTriangles ignores the aabbMin/aabbMax. It is better to stick with the default implementation and not overriding it.
Thanks for the feedback,
Erwin