\part{Streaming de modèle 3D\label{streaming}} Le but ultime de ce projet est de biaiser l'utilisateur avec les recommandations de sorte à être capable de prévoir ses déplacements futurs, et ainsi précharger les parties du modèle qui vont être vues. Cette section présente le travail qui a été réalisé dans le domaine du chargement de modèle. \paragraph{} Évidemment, cette partie est celle qui comment après la fin de la première partie : il faut nous seulement connaître l'influence des recommandations sur l'utilisateur, ensuite être capable de prévoir le comportement de l'utilisateur, et enfin s'en servir pour précharger les bonnes parties du modèles. Tout ceci n'étant pas encore possible, le travail qui a été fait est nettement plus simpliste : il n'y aura aucune prévision du comportement ici. \section*{Introduction} Notre problématique ici est de transférer des modèles 3D sur le réseau. Les modèles sont stockés sur le serveur au format \texttt{.obj} et sont constitués : \begin{itemize} \item des materiaux (\texttt{usemtl}) : cela définit le matériau utilisé pour les faces qui vont suivre \item de sommets (\emph{vertices}) : des points 3D \item de coordonnées de textures : des points en 2D qui référence un point d'une image \item de normales : des vecteurs en 3D \item de faces : une liste de 3 ou 4 sommets (représentés par leurs indices), avec éventuellement leurs coordonnées de textures et/ou normales \end{itemize} \paragraph{} La seule contrainte d'ordre des éléments est qu'une face qui contient des sommets, coordonnées de textures ou normales n'arrive pas avant ses sommets, coordonnées de textures ou normales dans le fichier : si l'on parcourt les lignes du fichier, on doit déjà avoir toutes les informations sur la face. \paragraph{} Généralement, le fichier \texttt{.obj} vient souvent avec un fichier \texttt{.mtl} qui contient la définition des matériaux (leurs noms, leurs textures, leurs constantes...). \section{Architecture} Puisque cette partie traite à la fois du client et du serveur, les fichiers peuvent sembler éparpillés. Les fichiers concernés sont les fichiers du répertoire \texttt{geo} pour le serveur et le fichier \texttt{ProgressiveLoader.js} qui est le client. \subsection{Serveur} Nous avons rappelé que les modèles au format \texttt{.obj} contenaient plusieurs \emph{sous-modèles} avec des matériau différents, le serveur connait les classes suivantes : \begin{itemize} \item la classe \texttt{Mesh}, qui représente un \emph{sous-modèle}, avec ses faces, ses coordonnées de textures, ses normales, son matériau \item la classe \texttt{MeshContainer}, qui représente un modèle au format \texttt{.obj}, en tant que liste de \texttt{Mesh} \item la classe \texttt{MeshStreamer}, qui permettra d'envoyer un modèle au client \end{itemize} \paragraph{} Pour avoir une bonne performance, il faut éviter de re-parser les fichiers \texttt{.obj} et de recréer les \texttt{MeshContainer} à chaque requête. \paragraph{} Dans le fichier \texttt{MeshContainer.js}, une liste de modèle à charger (ainsi que leur chemin permet au serveur de créer les \texttt{MeshContainer} au démarrage, et ainsi, le \texttt{MeshStreamer} n'a qu'à prendre une référence vers ce \texttt{MeshContainer} (qui existe déjà, ce qui réduit la latence). \subsection{Client} Le client de la partie \emph{streaming} de ce projet est contenue dans la classe \texttt{ProgressiveLoader}. Celle-ci crée des modèles 3D vides, puis établit la connexion avec le serveur et remplit les modèles au fur et à mesure que les informations arrivent du serveur. Dans les sections suivantes, nous allons montrer les différentes stratégies de \emph{streaming} que nous avons mises en place. \section{Streaming linéaire} La première étape de cette fonctionnalité a été de faire un système client-serveur permettant le streaming de modèle 3D. En effet, les \emph{loaders} de modèles présents dans la libraire \threejs ne permettent pas le chargement progressif : ils se content d'envoyer une requête vers le fichier contenant le modèle et à créer un modèle une fois que le fichier est chargé complètement. \paragraph{} Pour commencer, nous avons donc utilisé \socketio, une librairie permettant de gérer les sockets facilement avec JavaScript et Nodejs, pour faire une première version simpliste du streaming : on travaillait sur un modèle ne contenant que des sommets et des faces (donc pas de textures ni de normales) et le protocole fonctionnait ainsi : \begin{figure}[H] \centering \begin{tikzpicture}[] \draw (0,0) node[above]{Client}; \draw (0,0) -- (0,-6); \draw (5,0) node[above]{Serveur}; \draw (5,0) -- (5,-6); \draw (2.5,-1.3) node[rotate=-12] {Path jusqu'au modèle}; \draw[->] (0,-1) -- (5, -2); \draw (2.5,-2.3) node[rotate=12] {Des éléments}; \draw[<-] (0,-3) -- (5, -2); \draw (2.5,-3.3) node[rotate=-12] {ACK}; \draw[->] (0,-3) -- (5, -4); \draw (2.5,-4.3) node[rotate=12] {D'autres éléments}; \draw[<-] (0,-5) -- (5, -4); \draw (2.5,-6) node{$\vdots$}; \end{tikzpicture} \caption{Transmission élémentaire\label{transmission1}} \end{figure} \paragraph{} Le serveur envoyait alors les éléments dans l'ordre dans lequel ils étaient présents dans le fichier du modèle 3D. Le gros inconvéniant de cette méthode est que souvent, les sommets sont présents au début du fichier, et les faces vers la fin : nous recevons donc des informations de sommets au début qui ne nous permettent rien d'afficher, puis toutes les faces d'un coup, ce qui fait que le streaming n'est pas aussi progressif que l'on souhaiterais. \paragraph{} Dans le cas d'un modèle sans coordonnées de textures et sans normales, la seule condition pour pouvoir afficher une face est d'avoir envoyé les sommets qui la composent. On peut donc améliorer la fluidité en réarrangeant le fichier \texttt{.obj} : il suffit de faire apparaître les faces dès que les sommets sont disponibles. \paragraph{} Dans le cas où l'on souhaite gérer les textures et les normales, utiliser cette technique est beaucoup plus compliqué puisqu'il faut aussi décider de l'ordre relatif entre les sommets, coordonnées de textures et normales : en effet, pour envoyer une face le plus tôt possible, il faut que toutes ces composantes soient envoyés, et il est donc nécessaire de mélanger les sommets, coordonnées de texture et normales. \section{Streaming linéaire amélioré} La remarque précédente conduit directement à cette méthode. Le principe reste le même que celui précédent (figure \ref{transmission1}), mais les éléments seront envoyés différemment : on va en fait parcourir les faces directement. \paragraph{} Le serveur va garder en mémoire ce qui a déjà été envoyé et ce qui ne l'a pas été, et envoyer les faces dans l'ordre : si certains éléments des faces n'ont pas encore été envoyé, on les enverra juste avant d'envoyer la face en question. \paragraph{} On peut de cette façon à la fois gérer les coordonnées de texture et les normales, tout en envoyant les faces le plus tôt possible (on peut aussi noter que si certains sommets / coordonnées de textures / normales sont inutilisés dans les faces, ils ne seront pas envoyés et on s'évite donc de transférer des données inutiles). \paragraph{} C'est dans cette version que nous avons commencé à nous intéresser à la façon de gérer les matériaux. Dans \threejs, un objet 3D est lié à un materiau, et nous sommes donc obligés de créer autant d'objets que de materiaux. Pour cela, nous avons légèrement modifié notre protocole : \begin{figure}[H] \centering \begin{tikzpicture}[] \draw (0,0) node[above]{Client}; \draw (0,0) -- (0,-8); \draw (5,0) node[above]{Serveur}; \draw (5,0) -- (5,-8); \draw (2.5,-1.3) node[rotate=-12] {Path jusqu'au modèle}; \draw[->] (0,-1) -- (5, -2); \draw (2.5,-2.3) node[rotate=12] {Liste des matériaux}; \draw[<-] (0,-3) -- (5, -2); \draw[dashed, ->] (-0.1,-3) -- (-0.1,-4); \draw (2.5,-3.3) node[rotate=-12] {ACK}; \draw[->] (0,-3) -- (5, -4); \draw (2.5,-4.3) node[rotate=12] {Des éléments}; \draw[<-] (0,-5) -- (5, -4); \draw (2.5,-5.3) node[rotate=-12] {ACK}; \draw[->] (0,-5) -- (5, -6); \draw (2.5,-6.3) node[rotate=12] {D'autres éléments}; \draw[<-] (0,-7) -- (5, -6); \draw (2.5,-8) node{$\vdots$}; \end{tikzpicture} \caption{Transmission avec gestion des matériaux} \end{figure} \paragraph{} Les directives de matériau à utiliser (\texttt{usemtl}) seront ignorées, et les faces seront envoyées avec l'indice de l'objet auquel elles appartiennent, ce qui permettra au client de savoir dans quel objet (et donc avec quel matériau) elles doivent être ajoutées. \section{Streaming intelligent} C'est la dernière version du streaming qui a été faite sur ce projet. À chaque transfert, le client envoie sa position au serveur (ainsi que les plans définissant son \emph{frustum}\footnote{les bords du champ de vision de la caméra}) et le serveur va parcourir les faces du modèle en cherchant celles qui apparaissent dans le \emph{frustum}\footnote{dans cette version, on considère qu'une face apparaît dans le \emph{frsutum} si un de ces sommets y appartient. Évidemment, une face très grande pourrait appraître dans le \emph{frustum} sans qu'aucun de ses sommets n'y soit, mais nous n'avons pas traité ce cas particulier ici.}. On évite ainsi d'envoyer les faces du modèle qui sont derrière la caméra et que l'utilisateur ne voit pas. \paragraph{} Bien sûr, si il n'y a plus de faces dans le \emph{frustum} de la caméra, on va envoyer les faces en suivant la méthode de la section précédente. \begin{figure}[H] \centering \begin{tikzpicture}[] \draw (0,0) node[above]{Client}; \draw (0,0) -- (0,-8); \draw (5,0) node[above]{Serveur}; \draw (5,0) -- (5,-8); \draw (2.5,-1.3) node[rotate=-12] {Path jusqu'au modèle}; \draw[->] (0,-1) -- (5, -2); \draw (2.5,-2.3) node[rotate=12] {Liste des matériaux}; \draw[<-] (0,-3) -- (5, -2); \draw[dashed, ->] (-0.1,-3) -- (-0.1,-4); \draw (2.5,-3.3) node[rotate=-12] {Caméra / \emph{frustum}}; \draw[->] (0,-3) -- (5, -4); \draw (2.5,-4.3) node[rotate=12] {Des éléments}; \draw[<-] (0,-5) -- (5, -4); \draw (2.5,-5.3) node[rotate=-12] {Caméra / \emph{frustum}}; \draw[->] (0,-5) -- (5, -6); \draw (2.5,-6.3) node[rotate=12] {D'autres éléments}; \draw[<-] (0,-7) -- (5, -6); \draw (2.5,-8) node{$\vdots$}; \end{tikzpicture} \caption{Version finale} \end{figure}