I. Introduction▲
Bullet est une librairie de détection de collisions gérant des objets dynamiques comme des boites, sphères,
capsules et cetera pour rendre les scènes dynamiquement plus réalistes. Elle est utilisable dans
des applications temps réel, par exemple des jeux.
La librairie est open-source, gratuite
pour utilisation commerciale, sous licence zlib.
Il y a un forum pour la communauté autour de cette librairie traitant de détections de collisions
et physiques à l'adresse suivante :
Lien : http://www.bulletphysics.com/Bullet/phpBB3/
II. Téléchargement et compilation▲
II-A. Pré-requis▲
Vous devez avoir de l'expérience avec C++ et OpenGL ainsi qu'une librairie pour créer un contexte OpenGL comme SFML, SDL ou encore GLUT.
II-B. Téléchargement▲
Vous pouvez télécharger le code source de bullet physics depuis Google Code.
Lien : http://code.google.com/p/bullet/downloads/list
II-C. Compilation▲
Bullet est livré avec des fichiers de projets auto-générés pour Microsoft Visual Studio 6, 7, 7.1 et 8. Le
fichier de projet principal se trouve dans Bullet/msvc/8/wksbullet.sln (remplacez 8
avec votre version de M. Visual Studio).
Sur d'autres plateformes, comme Linux ou Mac OS-X, Bullet beut être compilé utilisant make,
cmakehttp://www.cmake.org, ou jamhttp://www.perforce.com/jam/jam.html.
cmake peut auto-générer des fichiers de configuration pour Xcode, KDevelop, MSVC et d'autres
systèmes de compilation.
Tapez juste cmake dans une console, étant dans le dossier principal de Bullet.
Si vous n'utilisez pas Microsoft Visual Studio ou cmake, vous pouvez lancer ./autogen.sh./configure
pour créer des fichiers Makefile et Jam ensuite tapez make ou jam.
Jam est un système de construction qui peut construire (compiler) la librairie, les démos mais aussi
d'auto-générer des fichiers de projet Microsoft Visual Studio. Si vous n'avez pas jam d'installé,
vous pouvez le télécharger depuis ftp://ftp.perforce.com/jam [ftp://ftp.perforce.com/jam]
II-D. Tester les démos▲
Essayez d'exécuter et expérimentez avec l'exécutable BasicDemo comme point de départ. Bullet peut être utilisé de plusieurs façons : en tant que simulation de corps rigides, librairie de détection de collisions ou bas niveau comme GJK calculabilité du plus proche point.
III. Types de données basiques et la librairie mathématique▲
Les types de données basiques, gestion de mémoire et conteneurs sont localisés dans Bullet/src/LinearMath.
btScalar : un nombre à virgule flottante.
De façon à pouvoir compiler en simple précision en virgule flottante ou double précision, Bullet
utilise le type de donnée btScalar. Par défaut, btScalar est un typedef de float. Il
peut être double en définissant BT_USE_DOUBLE_PRECISION en haut du fichier Bullet/src/LinearMath/btScalar.h.
btVector3 : les positions 3D et les vecteurs peuvent être représentés en utilisant btVector3
qui contient 3 composants scalaires x, y, z.
Plusieurs opérations peuvent être effectuées sur un btVector3, par exemple, addition, soustraction
et la longueur d'un vecteur.
btQuaternion et btMatrix3x3
Les orientations 3D et rotations peuvent être représentées en utilisant btQuaternion ou btMatrix3x3.
btTransform est une combinaison d'une position et d'une orientation.
Il peut être utilisé pour transformer des points et vecteurs d'un espace de coordonnées dans un autre.
Aucune mise à l'échelle ou de cisaillement n'est autorisé.
Bullet utilise un système de coordonnées droitier :
Figure 1 : Système de coordonnées droitier
btTransformUtil et btAabbUtil fournissent des fonctions de transformations et des AABB
(Aligned Axis Bounding Box).
Aligned Axis Bounding Box ou AABB signifient : boite englobante qui
reste toujours orientée sur les axes X, Y et Z.
Pour augmenter les performances un calcul est avant tout fait sur les boites englobantes des objets
pour savoir lesquelles se chevauchent. Les AABB qui ne se chevauchent pas, leurs objets ne nécessitent
pas de calculs de collisions par exemple.
Figure 2 : Représentation d'une boite englobante ou AABB ainsi que sa représentation en 2D
à droite
IV. Les types d'objets rigides▲
IV-A. Les primitives convexes▲
btBoxShape : boite définie par la moitié de longueur des côtés.
Très utile pour représenter des objets cubiques, voir Figure 3.
Figure 3 : Objet dynamique en forme de boite à gauche et un objet qu'on peut représenter
par une boite dans un jeu, à droite
btSphereShape : sphère définie par son rayon.
Peut représenter les objets sphériques comme les ballons de foot, billes de billard et cetera,
voir Figure 4.
Figure 4 : Objet dynamique sphérique à gauche et des objets qu'on peut représenter par une
sphère, à droite
btCapsuleShape: capsule autour de l'axe Y.
btCapsuleShapeX/Z peuvent être utilisés pour spécifier autour de l'axe X ou Z.
Figure 5 : Objet dynamique en forme de capsule
btCylinderShape : cylindre autour de l'axe Y.
btCylinderShapeX/Z peuvent être utilisés pour spécifier autour de l'axe X ou Z.
Forme cylindrique, utile pour représenter des bidons et cetera, voir Figure 6.
Figure 6 : Objet dynamique en forme de cylindre à gauche, avec on peut représenter un bidon,
à droite
btConeShape : cône autour de l'axe Y.
btConeShapeX/Z peuvent être utilisés pour spécifier autour de l'axe X ou Z.
Figure 7 : Objet dynamique en forme de cône
btMultiSphereShape : pour créer un convex hull à partir de plusieurs sphères qui peuvent
par exemple être utilisées pour créer une capsule.
Objet dynamique composé de plusieurs sphères pour représenter certaines formes. Les sphères peuvent
être animées également. Voir Figure 8.
Figure 8 : Objet dynamique composé de plusieurs sphères. Ceci est un seul objet.
IV-B. Formes composées (Compound shapes)▲
btCompoundShape
De multiples formes convexes peuvent être combinées dans un compound shape. Ceci deviens une forme
concave composée de multiples sous formes convexes, appelées des formes enfant. Chaque forme enfant
a sa propre transformation locale, relative à btCompoundShape.
Objet composé de plusieurs formes pour représenter des modèles 3D sans passer par des tableaux
de sommets. Voir Figure 9.
Figure 9 : Exemple de compound shape ou forme composée. Ceci est un seul objet.
IV-C. Formes convexes à partir de modèles 3D (Convex Hull Shapes)▲
btConvexHullShape
Bullet supporte plusieurs façons de représenter des formes convexes à partir de meshes triangulés.
La façon la plus simple et facile est de créer un btConvexHullShape et de passer un tableau
de sommets.
IV-D. Formes concaves statiques à partir de modèles 3D (Concave Triangle Meshes)▲
Pour des objets statiques de l'environnement, un moyen très efficace pour représenter les maillages de triangles est d'utiliser un btBvhTriangleMeshShape.
IV-E. Décomposition convexe (Convex decomposition)▲
Idéalement, des modèles 3D concave devraient être utilisés seulement pour représenter du décor statique.
Autrement, son enveloppe convexe doit être utilisée en faisant passer le maillage à btConvexHullShape.
Si une forme convexe unique n'est pas assez détaillée, des parties convexes multiples peuvent être
combinées en un objet composite appelé btCompoundShape.
La décomposition convexe peut être utilisée pour décomposer le maillage concave en plusieurs parties
convexes. Regarder Demos/ConvexDecompositionDemo pour une façon automatique de faire une
décomposition convexe.
IV-F. Hauteur du champ (Height field)▲
Bullet fournit un soutien pour le cas particulier d'un terrain 2D concave par l'intermédiaire du btHeightfieldTerrainShape.
Regardez Demos/TerrainDemo pour son utilisation. Voir Figure 10.
Figure 10 : Exemple d'un hauteur de champ (texture 2D) à gauche et sa transformation en 3D
à droite
IV-G. Plan infini▲
btStaticPlaneShape : comme son nom l'indique btStaticPlaneShape représente un plan infini ou de taille définie. Idéal pour faire un sol pour ses tests et expérimentations rapides. Cette forme se doit d'être statique.
V. Intégration dans notre application▲
V-A. Le header Bullet▲
On commence par inclure Bullet dans notre application :
#include
"btBulletDynamicsCommon.h"
V-B. Déclarations▲
Ensuite on déclare le nom du monde physique :
btDiscreteDynamicsWorld *
myWorld;
La classe btBroadphaseInterface fournit une interface pour détecter les objets où leurs AABB
se chevauchent.
btBroadphaseInterface *
myBroadphase;
btCollisionDispatcher supporte des algorithmes qui peuvent gérer des pairs de collisions
ConvexConvex et ConvexConcave. Temps de l'impact, le point le plus proche et pénétration de profondeur.
btCollisionDispatcher *
myDispatcher;
btCollisionConfiguration permet de configurer les allocataires de mémoire.
btDefaultCollisionConfiguration *
myCollisionConfiguration;
btSequentialImpulseConstraintSolver est une implémentation SIMD rapide de la méthode Projected
Gauss Seidel (iterative LCP).
btSequentialImpulseConstraintSolver *
mySequentialImpulseConstraintSolver;
Position, orientation.
btTransform myTransform;
btDefaultMotionState fournit une implémentation pour synchroniser les transformations.
btDefaultMotionState *
myMotionState,
*
myMotionState_Sol;
Une matrice OpenGL, pour récupérer la position, rotation d'un objet.
btScalar matrix[16
];
Le corps d'une boite et de notre sol.
btRigidBody est la classe principale des objets rigides
btRigidBody *
body,
*
body_sol;
V-C. Une boite OpenGL▲
Une fonction pour afficher une boite à l'aide d'OpenGL
void
myBox(LDEfloat x, LDEfloat y, LDEfloat z)
{
glPushMatrix();
glScalef(x,y,z);
glBegin (GL_QUADS);
//face 1
glNormal3i(-
1
, 1
,-
1
);
glVertex3i(-
1
, 1
,-
1
); glVertex3i( 1
, 1
,-
1
);
glVertex3i( 1
,-
1
,-
1
); glVertex3i(-
1
,-
1
,-
1
);
//face 2
glNormal3i(-
1
,-
1
,-
1
);
glVertex3i(-
1
,-
1
,-
1
); glVertex3i( 1
,-
1
,-
1
);
glVertex3i( 1
,-
1
, 1
); glVertex3i(-
1
,-
1
, 1
);
// face 3
glNormal3i( 1
,-
1
, 1
);
glVertex3i( 1
,-
1
, 1
); glVertex3i( 1
,-
1
,-
1
);
glVertex3i( 1
, 1
,-
1
); glVertex3i( 1
, 1
, 1
);
//face 4
glNormal3i( 1
, 1
,-
1
);
glVertex3i( 1
, 1
,-
1
); glVertex3i(-
1
, 1
,-
1
);
glVertex3i(-
1
, 1
, 1
); glVertex3i( 1
, 1
, 1
);
//face 5
glNormal3i(-
1
, 1
, 1
);
glVertex3i(-
1
, 1
, 1
); glVertex3i(-
1
, 1
,-
1
);
glVertex3i(-
1
,-
1
,-
1
); glVertex3i(-
1
,-
1
, 1
);
//face 6
glNormal3i( 1
,-
1
, 1
);
glVertex3i( 1
,-
1
, 1
); glVertex3i( 1
, 1
, 1
);
glVertex3i(-
1
, 1
, 1
); glVertex3i(-
1
,-
1
, 1
);
glEnd();
glPopMatrix();
}
Figure 11 : Le code donne cette magnifique boite
V-D. Initialisation▲
Contient la configuration par défaut pour la mémoire, la collision
myCollisionConfiguration =
new
btDefaultCollisionConfiguration();
Le collision dispatcher par défaut. Pour du processus parallèle utilisez un dispatcher différent.
Regardez Extras/BulletMultiThreaded.
myDispatcher =
new
btCollisionDispatcher(myCollisionConfiguration);
Initialisation du broadphase (détecteur des objets où leurs AABB se chevauchent)
myBroadphase =
new
btDbvtBroadphase();
Constraint solver par défaut. Pour du processus parallèle utilisez un constrint solver différent.
Regardez Extras/BulletMultiThreaded.
mySequentialImpulseConstraintSolver =
new
btSequentialImpulseConstraintSolver;
On initialise le monde physique Bullet.
myWorld =
new
btDiscreteDynamicsWorld(myDispatcher,myBroadphase,mySequentialImpulseConstraintSolver,myCollisionConfiguration);
On définit la gravité, de façon à ce que les objets tombent vers le bas (-Y).
myWorld->
setGravity( btVector3(0
,-
10
,0
) );
V-E. Création de la boite dynamique▲
On déclare une forme et on l'initialise en tant que boite de la taille 1,1,1 (x, y, z)
btCollisionShape*
shape =
new
btBoxShape( btVector3(1
,1
,1
) );
On initialise notre btTransform et on lui dit une position (la position de la boite)
myTransform.setIdentity();
myTransform.setOrigin( btVector3(10
,5
,0
) );
L'inertie de la boite
btVector3 localInertia(0
,0
,0
);
Le poids de la boite
btScalar mass =
0.5
f;
On calcule l'inertie suivant le poids.
Note : inutile de calculer l'inertie si la boite n'aurait pas de poids puisqu'elle deviendrait
statique.
if
( mass )
shape->
calculateLocalInertia( mass, localInertia );
Il est conseillé d'utiliser motionState car il fournit des capacités d'interpolation et synchronise
seulement les objets "actifs".
myMotionState =
new
btDefaultMotionState(myTransform);
On regroupe les informations de la boite à partir de la masse, l'inertie et cetera
btRigidBody::
btRigidBodyConstructionInfo myBoxRigidBodyConstructionInfo( mass, myMotionState, shape, localInertia );
On construis le corps de la boite à partir de l'information regroupée
body =
new
btRigidBody(myBoxRigidBodyConstructionInfo);
Et enfin on ajoute notre boite dans le monde Bullet
myWorld->
addRigidBody(body);
V-F. Création du sol▲
La création du sol consiste à créer une boite comme indiqué au dessus mais en la mettant statique.
// Forme en tant que boite
btCollisionShape*
shape_sol =
new
btBoxShape( btVector3(10
,1
,10
) );
myTransform.setIdentity();
// Position du sol
myTransform.setOrigin( btVector3(0
,0
,0
) );
btVector3 localInertiaSol(0
,0
,0
);
mass =
0
; // Le fait que le poids de cet objet soit zéro le rends statique
myMotionState_Sol =
new
btDefaultMotionState(myTransform);
btRigidBody::
btRigidBodyConstructionInfo sol_info( mass, myMotionState_Sol, shape_sol, localInertiaSol );
body_sol =
new
btRigidBody(sol_info);
// On ajoute le sol dans le monde Bullet
myWorld->
addRigidBody(body_sol);
VI. La boucle d'affichage de l'application/jeu▲
VI-A. Mise à jour des transformations▲
Dans la boucle d'affichage on devra appeler la fonction stepSimulation pour calculer et mettre
à jour les transformations des objets dynamiques.
0.001 doit être le frametime de votre application/jeu. C'est à dire, le temps que prend une
frame pour tout exécuter/afficher.
if
( myWorld )
myWorld->
stepSimulation( 0.001
);
VI-B. Récupération des transformations et affichage des objets▲
On recupère la matrice OpenGL de la boite. C'est grâce à cette matrice qu'on appliquera la transformation effectuée par la librairie Bullet à l'objet qu'on affichera avec OpenGL.
myMotionState->
m_graphicsWorldTrans.getOpenGLMatrix( matrix );
On affiche la boite à l'aide d'OpenGL
/* grâce à glPushMatrix (ouverture) et glPopMatrix (fermeture) on retiens les transformations seulement appliquées à cette boite */
glPushMatrix();
/* On applique les transformations à la boite */
glMultMatrixf( matrix );
/* Ensuite, on affiche la boite */
myBox(1
,1
,1
);
glPopMatrix();
On affiche le sol (toujours une boite)
/* On affiche le sol */
myBox(10
,1
,10
);
VII. Conclusion▲
VII-A. Vidéo de ce qu'on peut arriver à faire avec un peu de motivation▲
Capture d'écran. Par Arkham46.
Lien : youtube : Bullet
Il est facile d'ajouter d'autres types d'objets dynamiques en initialisant btCollisionShape
avec btSphereShape par exemple. Vous avez les autres formes indiquées plus haut.
VII-B. Conclusion▲
Bullet physics est multi plateforme, largement utilisé, gratuit pour utilisation commerciale, open source et a une belle communauté, un excellent choix.
VIII. Code source▲
Voici le code source complet. La gestion du fenêtrage est faite à l'aide de la SFML.
Lien : main.cpp.rar
IX. Remerciements▲
Merci à raptor70 sans qui cet article n'aurait pas existé ainsi que pour son aide et ses idées.
Merci à Arkham46 pour sa précieuse aide ainsi que pour l'image GIF du résultat.