I. INTRODUCTION
II. LE MOTEUR 3D Arbre BSP Optimisation Textures Interface Objets
Comme son nom l'indique : un arbre BSP (Binary Space Partitionning) découpe l'espace en sections binaires. Ce partitionnement permet de positionner la caméra dans l'espace par rapport aux facettes et ainsi de savoir dans quel ordre les afficher. II.1.a Exemple Simple
Dans cet exemple chaque noeud de l'arbre contient un segment qui correspondra à une facette verticale orientée par sa normale. L'espace (ici le plan) sera partitionné par la droite associée au segment courant. Pour créer un arbre BSP on commence par prendre le premier segment et le placer à la racine de l'arbre. Ensuite on insère dans l'arbre un noeud pour chaque segment de la scène. L'arbre étant binaire, chaque noeud a deux fils : un pour les segments se trouvant devant le segment courant et un autre pour les segments se trouvant derrière. ![]() Dans cet exemple les segments (B et D) se trouvant devant la facette A sont placés à sa droite dans l'arbre et les segments (C) se trouvant derrière sont placés à gauche. Nous pouvons aussi remarquer que le segment D se trouve à la fois devant et derrière le segment B. Le segment D est donc découpé en deux segments : D1 se trouvant devant B et D2 se trouvant derrière B. Ensuite pour afficher la scène on va parcourir l'arbre de manière récursive. Pour chaque noeud de l'arbre on va comparer la position de la caméra avec le segment courant : si la caméra est devant le segment associé au noeud courant on procédera à l'affichage des segments se trouvant derrière le segment courant en premier. Ensuite, on affichera ceux se trouvant devant. Si la caméra est derrière le segment courant on affiche d'abord les segments se trouvant devant, puis ceux se trouvant derrière. Dans notre exemple : ![]() La caméra est devant A on va donc afficher C, puis A, puis passer au noeud contenant B. La caméra se trouve devant B on va donc afficher D2, puis B et pour finir D1.
Chaque noeud contient un plan orienté qui servira au partitionnement de l'espace. Le parcours de l'arbre se fera à partir de ces plans orientés de la même manière que pour les segments de l'exemple précédent.
II.2.a Les listes de visibilité
![]() Dans cet exemple chaque salle du plan correspond a une feuille. Si nous nous trouvons dans la feuille 1 il est certain que quelque soit la direction où l'on regarde et quelque soit la position de la caméra nous ne pourrons voir la feuille 4. Donc la liste de visibilité de la feuille 1 sera 1,2,3. De même pour la feuilles 4 la liste de visibilité sera 2,3,4. Pour les feuilles 2 et 3 toutes les feuilles seront visibles. Pour des scènes de grande taille avec beaucoup de salles cette méthode peut réduire grandement le nombre de facettes à afficher.
![]() Dans cet exemple la boite englobante (en pointillés) des feuilles 3,4 et 5 est hors de la pyramide de vue de la camera. Donc, les feuilles 3,4 et 5 ne seront pas affichées alors qu'elles font partie de la liste de visibilité de la feuille 1.
II.3.a Les coordonnées de textures dans un fichier BSP
Ceci est fait en effectuant le produit scalaire entre les coordonnées du sommet avec vS (respectivement vT) et en ajoutant la distance dS (respectivement dT). Le résultat obtenu S (respectivement T) nous donne la coordonnée horizontale (respectivement verticale) sur la texture.
T=Sommet.vT+dT
![]() Dans l'exemple ci-dessus on peut remarquer que des détails de la textures disparaissent lorsque l'on réduit la texture sans se préoccuper de l'aliasing. Alors que le résultat avec anti-aliasing est bien meilleur. Plusieurs solutions d'anti-aliasing sont possibles pour résoudre ce problème:
Cette librairie permet entre autre de gérer les périphériques du système utilisés par le programme. GLUT a été utilisé principalement pour gérer le clavier. Malheureusement GLUT n'est pas complète dans ce domaine et ne permet pas de détecter lorsqu'une touche a été relâchée. Elle se contente de générer un événement lorsqu'une touche a été enfoncée. Ceci a causé quelques problèmes lors du développement de l'interface et a bridé nos objectifs de développement du logiciel. Il aurait été possible d'utiliser une autre bibliothèque complémentaire d'openGL, appelée GLAUX, mais nous n'avions pas une version de la librairie permettant d'utiliser les possibilités des cartes 3D installées dans nos ordinateurs. Une autre solution aurait été d'utiliser des fonctions spécifiques au système. Mais ceci aurait limité la probabilité de notre programme et nous aurait fait dépasser le temps que nous avions pour développer le logiciel.
Ces objets ne font pas partie de l'arbre BSP et donc les facettes qui les constituent ne sont pas triées à l'affichage. Ceci implique l'utilisation d'une autre méthode pour pouvoir afficher correctement ces objets à l'écran. Cette méthode est celle du Z-Buffer. Le Z-Buffer est un tableau dont chaque case correspond à un pixel de l'image. Dans chacune de ces cases se trouve la distance entre la facette affichée et la camera. Cette distance sera utilisée pour déterminer si un objet est visible ou pas. Lorsque l'on affiche la scène définie par l'arbre BSP on remplit chaque case du tableau par cette distance. Après avoir effectué cette opération, on affiche les facettes correspondant aux objets représentant les différents utilisateurs. Les pixels des facettes de ces objets ne seront pas affichés à l'écran si la distance entre eux et la caméra est plus grande que celle déjà placée dans le Z-Buffer. Cette opération est effectuée de manière efficace par openGL, pour peu que l'on ait une carte accélératrice 3D. III. Détection de Collision. Première approche Solution Adoptée
Le principe consistait à dire que nous nous trouvions dans une feuille, ce qui est pour le moins évident puisque où que nous nous trouvions dans le niveau notre position correspondait à l'emplacement d'une feuille dans l'arbre. Ainsi on pouvait tester toutes collisions éventuelles avec l'une des faces qui se trouvait dans cette feuille. Ensuite si l'on pouvait sortir de cette feuille, on cherchait la feuille dans laquelle on devait se trouver après l'éventuel déplacement et on effectuait les mêmes tests sur cette nouvelle feuille. ![]() Le premier problème survenu est que certaines feuilles représentent un volume spatial si petit que celles-ci n'étaient pas testées. On pouvait donc passer à travers certains murs qui semblait ne pas exister!!! ![]() Le deuxième problème était encore bien plus ennuyeux puisqu'il s'agissait de détecter la présence ou non d'un sol sous nos pieds. A priori rien de bien complexe. Il suffisait de tester les faces de la feuille où l'on se trouvait qui possède une normale en z ( i.e. vers le haut). La surprise que nous réservait le BSP était que certaines feuilles sont coupées horizontalement, surtout lorsqu'on s'approchait des marches, ce qui devenait encore plus problématique pour gérer la montée des marches. En effet, le test pour trouver dans quelle feuille l'on se trouvait s'effectuait au niveau de la caméra. On se retrouvait donc dans certaines feuilles sans sol ni plafond.... ![]() Les tests ont encore continués pour trouver une méthode s'adaptant à ces problèmes. Mais de nouveaux problèmes sont alors apparus toujours dus à la spécificité du BSP et à la façon dont il est construit. Il s'est donc avéré que le peu de connaissance que nous avions au sujet de la construction de l'arbre par rapport à la modélisation de l'ESSI, le peu de documents disponibles quant à cette même construction et à la façon dont la détection de collision est effectuée ne permettait pas d'utiliser cette méthode là de manière efficace.
On possède une liste de pointeurs sur des feuilles. En fait toutes les feuilles de chaque niveau sont ici représentées. Dans chaque feuille toutes les faces sont représentées par leur boite englobante (ou bouding box) et leur normale. Chaque feuille est elle aussi représentée par sa boite englobante générale ainsi que toutes ses faces. ![]() On procède ensuite à un test qui nous permet de savoir si le mouvement effectué doit traverser une face pour être effectivement appliqué. Ainsi on pourra réduire le mouvement latéral (en x ou en y) selon la nature de la face rencontrée. Pour la détection du sol on utilisera le même système en sachant que l'on veut rester à une certaine distance du sol lors de nos déplacements. On effectuera ces tests dans l'ordre indiqué au dessus, i.e. que les déplacements latéraux (en x et y) seront calculés en premier pour pouvoir mettre à jour la hauteur z à laquelle on se trouve après ce déplacement s'il est réussi. En outre, un test de gravité a lieu. C'est à dire que si aucune face n'est trouvée juste en dessous de la personne il tombera jusqu'à la prochaine face. On attendra alors que l'utilisateur fasse à nouveau un mouvement. VI. IMPLÉMENTATION RÉSEAU Objectifs. Réalisation. Améliorations.
VI.1 Objectifs.
Pour un maximum de réalisme, l'aspect réseau de ce projet devait présenter les caractéristiques suivantes:
Approche unicast Client/Serveur :Un client envoie son message au serveur qui s'occupe de le redistribuer aux destinataires. Pour un message, et N destinataires, le client envoie 1 paquet, et le serveur retransmet N paquets.![]() Cela permet une synchronisation des utilisateurs, un contrôle
de flux et un contrôle des données.
Une autre approche Unicast:Un client diffuse successivement à tous les clients. Pour 1 message, et N clients, il envoie N paquets.![]() L'approche unicast nécessite d'envoyer autant de messages qu'il existe de processus destination, ce qui peut entraîner une surcharge importante du réseau et donc un temps de latence élevé. Approche multicast ou broadcast:Un client diffuse à tous les clients, tel un émetteur radio. Pour 1 message et N clients, il envoie 1 Paquet.![]() L'avantage majeur d'une approche Broadcast/Multicast réside dans
le fait que dans ce cas, le nombre de paquets émis est extrêmement
réduit, et donc plus efficace.
VI.2. Réalisation.
Les différentes classes réalisées sont: ![]() Pour pouvoir converser entre eux, les différents clients doivent pouvoir être identifiés de façon unique. L'IDServer est une mini base de donnée des identifiants disponibles. Lors de son lancement, le client émet une requête vers l'IDServer, afin d'obtenir un identifiant unique. Une fois celui ci obtenu, le client peut émettre sur le canal multicast sans risque de se voir confondre avec un autre client. Lors de sa déconnection, le client doit indiquer à l'IDServer qu'il quitte la partie et que par conséquent son identifiant pourra par la suite être utilisé par un autre client. Durant la période de communication "normale" du client, c.a.d
après son initialisation et son identification, plusieurs threads
ayant chacun un rôle spécifique sont lancés.
L'émission des données concernant le clientDans cette partie, seules les informations minimales sont envoyées. Les données traitées concernent principalement les déplacements. A chaque déplacement du personnage, un paquet réduit dont le corps représente une action telle que avancer, reculer, tourner à gauche ou tourner à droite, est envoyé.La réception des informations en provenance des autres clients.Le client récupère dans cette partie les informations en provenance des autres clients et met à jour une base de données représentant l'état global du système.L'émission périodique de données complètes décrivant le joueur.En utilisant le protocole UDP Multicast, on s'expose à une perte de paquets possible. Pour pallier ce problème, on envoie de façon régulière un paquet d'information spécial dit Heartbeat décrivant de façon complète l'état du client. Ce paquet permet de remettre à jour l'état global du système pour les clients qui auraient perdus des informations.La destruction des informations concernant les clients inactifs.On a vu ci dessus que chaque client envoit de façon régulière des informations. Si, au bout d'un temps donné, on constate qu'on ne reçoit plus d'informations en provenance d'un client, on considère que celui ci s'est déconnecté. Toutes les informations le concernant sont alors supprimées.VI.3 Améliorations.
Cependant, le système de gestion du clavier utilisé (GLUT) ne permet pas de différencier l'enfoncement d'une touche du relâchement de cette touche. C'est pourquoi une autre méthode a été utilisée. Tant que l'utilisateur effectue une action (avance par exemple), un flux continu de paquets est émis indiquant l'action courante. En fait, pour pouvoir réaliser la méthode présentée précédemment, il serait nécessaire d'utiliser des primitives propres au système d'exploitation utilisé (ici Windows). On perdrait alors en portabilité, mais on gagnerait en efficacité. V. CONCLUSION
Au niveau de la détection de collision, le sujet était plus qu'intéressant. On regrettera l'absence de documentations à propos de ce sujet. Mais de ce fait la création des algorithmes de détection en était d'autant plus intéressante. L'utilisation de cartes 3DFX sous Windows nous a permis d'expérimenter un environnement hard/soft inhabituel à l'ESSI. Ceci était particulièrement intéressant. On pourra regretter la mauvaise configuration des machines HP, qui nous aura fait perdre un temps non négligeable et nous aura empêché de développer le programme sur cette plateforme comme cela était initialement prévu. ![]() ![]() ![]() VI. RÉFÉRENCES |