Bullet Physics
|
A common task when working with a physics simulation is to have some sort of fixed map, this comes up in most simulations that have gravity so that at least your objects will not just fall forever.
For basic platforms feel free to use any of the built in collision shapes, but in most cases your map may be designed in a 3rd party program such as blender.
When working with arbitrary geometry like this, the class you'll want to use is btBvhTriangleMeshShape
, breaking the name of this function down we can see that we first have Bvh, which stands for "Bounding volume hierarchy", here's a quick description pulled from wikipedia:
A bounding volume hierarchy (BVH) is a tree structure on a set of geometric objects. All geometric objects, which form the leaf nodes of the tree, are wrapped in bounding volumes
The next part of the name is Triangle Mesh.
At this point the name of this function should tell that you that when you use this class you'll have an internal BVH representation for acceleration purposes, it builds this acceleration structure from
Focusing on the signature for this function, we can see that it expects a btStridingMeshInterface
, which mentions that that is an "interface class for high performance generic access to triangle meshes", if you don't already know, an interface class is one that's not meant to be initialized. Taking a look at it's inheritance diagram you'll be able to find subclasses that actually provide an implementation for this concept.
The class we'll first focus our attention on is btTriangleMesh
. The general process of using a btTriangleMesh
is like this:
btTriangleMesh
btCollisionShape
with btBvhTriangleMeshShape
and pass in the triangle mesh we've built up in the previous stepsbtCollisionShape
A sample interaction could look like this:
While programatically creating collision shapes can work, we'll need a way to load in arbitrary vertex information so that we can load in assets created by artists and files created in software such as obj files.
Before going any further, please become aquainted with the obj format.
For the most part we'll you'll want to load in your obj files through some obj loader program that has been already created and is external from bullet. The datastructure that you store it in will determine how you will end up loading it into bullet, but we can make a few safe assumptions.
We know that in general a model is a collection of meshes, and a mesh is a collection of vertices along with attributes stores on those vertices such as normals, texture coordinates and so on.
On top of this your obj file defines faces by passing indices that map to certain vertices, the benefit is that you don't have to repeat the same verticies again. For example if we were working on the corner of a cube, the corner vertex would have to be referenced three times, if we didn't do it this way then we'd have to list out that vertex 3 times for each triangle at that corner and doing it this way makes the obj file bigger for no reason.
Therefore we'll assume that your datastructure has an array of meshes, and in each mesh we create an array of indicies which is generated by iterating over each face in the mesh and then inserting each predefined (by the obj file itself) vertex index for the face into the overarching mesh index array, at the end this array should be 3 * the number of faces on the mesh. A god name for this variable could be sequential_face_indices
, because of the way it was generated by iterating sequentially over each face and then just inserting each vertex index directly into it. Then we can create our triangle mesh as follows
From this point onward, we can reference the code for the method createMeshInterface
in Extras/Serialize/BulletWorldImporter/btWorldImporter.cpp
.
The datastructure we were working with already has sequential_face_indices
, but it looks like this code doesn't have that defined, so during the first part of the process it defines that, in the second stage it didn't define the vertex array so it also creates that, the vertex array is simply created by moving through the vertices in the current group defined in the obj file (a group is like the same thing as a mesh but in their format, see the original link to obj file for more info) and then simply appending it into an array, note the order follows the same order as the obj file.
Along the way it stores the sequential_face_indices
along with the vertices
into an object that has the type btIndexedMesh
, this object doesn't take in these two pieces of data upon initialization so we have to pass them in after the fact like this:
Then after storing this information into indexed_mesh
, we can then construct a btTriangleIndexVertexArray
by doing
At which point, we can then constuct a btBvhTriangleMeshShape
from this data like this
All in all