Les interfaces 3D deviennent de plus en plus présentes sur nos projets. Tout d’abord réservées aux jeux, ces interfaces sont de plus en plus utilisées au sein d’applications ou de serious games. Nous dépassons donc le cadre des jeux et nous devons coupler ces nouvelles interfaces et ces nouveaux usages avec de plus en plus de logique métier.
Dans cet exemple, nous utiliserons BabylonJS et AngularJS afin de créer une interface 3D permettant d’allumer nos lampes Philips HUE et d’avoir une interaction avec l’application AngularJS.

La stack technique

Notre application repose sur le serveur que nous avons développé dans la première partie. Vous pouvez retrouver également le code sur notre Github.
Notre logique d’application pourra être embarquée dans l’application AngularJs. L’intérêt ici sera de coupler AngularJS et BabylonJS pour les faire fonctionner de concert. On partira de notre Starter Typescript AngularJS. Celui-ci est basé sur la version 1.5 d’AngularJs mais ce que nous présentons ici pourrait fonctionner également avec la version 2.0

Qu’est ce que BabylonJS ? BabylonJS est un framework WebGL initié notamment par deux français (David Catuhe & David Rousset) et maintenant supporté par une communauté. BabylonJs permet d’interagir avec l’éco-système 3D (3dsMax, Blender, …) grâce à de nombreux plugins.
Nous vous invitons à aller voir le site babylonjs.com afin d’avoir une meilleure vision des possibilités de ce framework et de naviguer à travers les différentes démos.

Ci dessous le schéma de la solution que nous allons mettre en oeuvre :

Architecture

Création de la scène

Avant de commencer à utiliser BabylonJs, nous allons créer une scène sous 3ds max. Nous ne ferons pas un cours sur la modélisation dans cet exemple. En 3D, vous avez soit la possibilité de générer l’ensemble de votre scène en code (on appelle cela la génération procédurale) ou d’importer des modèles 3D directement dans votre scène (utiliser les deux méthodes en parallèle permet d’avoir un rendu travaillé sur les objets et d’avoir une scène infinie). La scène dans notre cas représente une lampe que nous allons exporter grâce au plugin d’export.

Exemple de scène 3D

 

Avant toute chose il faut installer le plugin permettant l’export de la scène 3D. Cet export nous permettra d’avoir un fichier .babylon que nous pourrons alors importer dans notre scène WebGL. Le fichier de type .babylon nous permet ici de faire la transition entre le monde BabylonJs et les différents format 3D qui peuvent exister, issus des logiciels de modelage (3ds Max, blender, …).

Sous 3dsMax, un menu Babylon apparaît lorsque le plugin a bien été installé.

Menu Babylon dans 3dsMax

 

Vous pouvez maintenant exporter votre scène au format BabylonJs. Le fichier .babylon.manifest, qui peut être généré en même temps, permet d’indiquer si vous souhaitez mettre en cache navigateur votre modèle une fois celui-ci chargé une première fois. Ce comportement peut être source de bug quand vous développez car si vous regénérez votre fichier .babylon, il faudra bien penser à nettoyer votre cache navigateur pour voir apparaître vos modifications.

Exporter Babylon

Création de la directive

Maintenant que votre scène est prête, il faut créer une directive AngularJs permettant d’insérer le canvas pour le rendu de la scène. Dans la philosophie AngularJs la directive existe pour séparer la manipulation du DOM HTML de la logique contenu dans le controller. Comme nous allons devoir créer un canvas dans notre page, nous laisserons donc cette tâche à notre directive. De plus la séparation en directive nous permettra également d’isoler le code gérant notre scène 3D.

Dans le fichier babylonSurface.Directive.ts nous allons donc créer la directive. La propriété template contiendra notre Canvas dans lequel le rendu BabylonJs s’exécutera. La méthode unboundLink nous servira dans l’étape suivante pour déclarer le moteur BabylonJs. L’interface IBabylonSurfaceDirectiveScope contient la variable newColor pour faire transiter la couleur entre le controller AngularJs et notre scène 3D.

 

Toujours dans cette directive, nous allons déclarer le moteur BabylonJs afin de pouvoir effectuer le rendu de la scène. Nous injecterons également dans la directive notre service (‘business.light’) permettant de faire le lien avec notre API. Pour pouvoir instancier le moteur BabylonJs (BABYLON.Engine), nous devons lui passer en paramètre la surface de rendu, dans notre cas le canvas de la directive (this.canvasRender). Pour charger une scène issue de 3dsMax, il faut utiliser la méthode BABYLON.SceneLoader.Load en lui passant en paramètre le fichier *.babylon. Celle-ci prend également en paramètre une callBack permettant d’être notifiée quand le fichier a fini d’être chargé. Sur une scène plus compliquée, nous pouvons également utiliser l’AssetManager.

 

Création du controller

Le but de cet article est de montrer l’interaction entre l’application AngularJS et la scène 3D. Cette interaction va être assez simple, le principe est de sélectionner une couleur dans l’application AngularJS puis de la transférer à la scène qui l’enverra au travers de l’API jusqu’à la lampe. Nous aurions pu réaliser ce picker directement dans la scène 3D mais nous n’aurions pas pu tester l’interaction entre les deux frameworks.

Dans le controller (HomeController) attaché à la page qui hébergera notre directive, nous définissons le scope AngularJS suivant :

 

Dans le constructeur du controller nous implémentons la méthode changeColor. Celle ci sera appelée pour valider la couleur du picker, la convertir en notre modèle et la transférer à notre directive. (Pour alléger le code nous aurions pu mettre aussi cette transformation directement dans un filter).

 

La variable this.$scope.myColor est bindée sur un color picker (package bower angular-color-picker). Grâce à ce color picker nous allons pouvoir changer la couleur de notre lampe.

Ensuite au niveau de la partie html, nous passons notre variable de scope à notre directive :

 

Nous pouvons accéder à la variable newColor dans la directive car nous l’avons déclarée dans le scope de celle-ci lors de sa création :

 

A l’heure actuelle nous pouvons faire passer la couleur de notre controller AngularJS à notre directive (et donc changer la couleur dans notre scène 3d) cependant il nous manque encore la liaison vers la lampe physique. Pour réaliser cette logique nous allons architecturer un peu notre code en séparant la déclaration du moteur et la logique d’affichage de la scène en la mettant dans une nouvelle classe nommée WorldManager.

La première étape dans notre directive est donc de passer à notre classe WorldManager la variable de scope newColor. Celle-ci étant passée par référence nous aurons bien tous les changements d’état  (ex: Changement de couleur à travers le color picker). La classe WorldManager exposera deux méthodes createScene (qui permettra d’initialiser notre scène en configurant les interactions avec les objets, les caméras, …) et la méthode renderLoop (qui mettra à jour notre scène en fonction de l’état de la variable newColor)

 

La seconde étape est de compléter la méthode createScene. Comme indiqué plus haut, elle permet de configurer notre scène. La première action est d’éteindre la lumière de la lampe. Puis nous allons rendre pickable (pouvoir interagir avec un mesh et notre souris) l’interrupteur de la lampe. Pour réaliser cela, BabylonJS projète le point d’impact de notre souris  et vérifie s’il y a une collision avec un objet dit pickable. Pour simplifier le code nous utiliserons l’ActionManager de BabylonJS que nous attachons à l’interrupteur. L’ActionManager permet d’attacher une réponse à un event se produisant sur l’objet sur lequel il est attaché. Dans notre cas lors d’un clic gauche sur l’interrupteur, nous allons lancer une animation actionnant l’interrupteur ainsi que la gestion de la lumière avec la méthode ManageLight (savoir si nous devons allumer ou éteindre la lumière de la scène et de quelle couleur nous devons l’afficher)

Communication bidirectionnelle

Notre serveur étant capable de pousser de la données vers les clients pour les avertir lors d’un changement d’état de la part d’un autre client, nous allons maintenant doter notre scène 3D de cette fonctionnalité.

Pour gérer la partie WebSocket, nous ajoutons une méthode realData dans la couche d’accès aux données dans le fichier data.light.ts. Nous allons utiliser la bibliothèque socket.io disponible à travers npm socket.io-client. Il faut d’abord créer le WebSocket grâce à la méthode io.connect à laquelle il faut passer l’url du serveur créé dans la première partie (Par défaut localhost:3000). Sur la réception du nom de l’event (eventName) nous appliquerons la fonction passée en paramètre sous forme de callback.

 

Pour utiliser cette communication bidirectionnelle, nous allons ajouter le support de cette méthode dans le constructeur de la classe WorldManager. En paramètre nous lui passerons la callBack à exécuter en réponse à l’event nommé ‘stateLight’ lorsque celui ci passera à travers la WebSocket.

La Callback permettra de mettre à jour la couleur de la lampe, ou bien de l’éteindre en fonction du message qui sera passé à la WebSocket.

 

Voici le rendu final de la scène

rendu final

 

Conclusion

Grâce à cet exemple nous avons pu mettre en place un client 3D basé sur babylonJS de façon assez simple. Bien que ce framework soit très bien pour les jeux videos, il peut tout a fait être couplé avec une application de type AngularJS afin d’avoir une interaction de l’application vers la scène mais également du serveur d’API vers la scène 3D.

Partager