This commit is contained in:
Thomas FORGIONE 2015-09-07 17:11:52 +02:00
parent 1da915d263
commit 33e310021e
16 changed files with 686 additions and 0 deletions

1
rapport/conclusion.tex Normal file
View File

@ -0,0 +1 @@
\part*{Conclusion}

BIN
rapport/img/new/01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 KiB

BIN
rapport/img/new/02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

BIN
rapport/img/new/03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

BIN
rapport/img/new/04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 KiB

BIN
rapport/img/new/buttons.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 KiB

17
rapport/img/new/resize.m Normal file
View File

@ -0,0 +1,17 @@
i_min = 167;
j_min = 391;
i_max = 933;
j_max = 1524;
repo = 'screenshots';
shots = dir(repo);
for i = 1:length(shots)
i
if (strcmp(shots(i).name, '.') || strcmp(shots(i).name, '..'))
continue
end
im = imread(strcat(repo, '/', shots(i).name));
imwrite(im(i_min:i_max,j_min:j_max,:), strcat('/home/thomas/stage/rapport/rapport/img/new/', shots(i).name)) ;
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 KiB

188
rapport/interface.tex Normal file
View File

@ -0,0 +1,188 @@
\part{L'interface}
\section{Interactions élémentaires}
\paragraph{}
La première interface a été pensée pour être la plus simple possible.
L'utilisateur contrôle une caméra qui se déplace librement dans un modèle 3D.
\paragraph{}
La translation de la caméra est contrôlée par le clavier : les touches Z, Q, S,
et D servent respectivement à avancer, aller à gauche, reculer et aller à
droite (de même que les touches fléchées).
\paragraph{}
On peut pivoter la caméra de plusieurs manières :
\begin{itemize}
\item via le pavé numérique (2, 4, 6, et 8 pour tourner respectivement vers
le bas, vers la gauche, vers la droite et vers le haut)
\item via la souris, comme \emph{drag-n-drop}, en cliquant un point de la
scène et en le déplaçant
\item via la souris, en mode \emph{pointer-lock}, comme dans un jeu video
de tir
\end{itemize}
\section{Les recommandations}
\paragraph{}
Les recommandations sont là pour suggérer des points de vue à l'utilisateur.
Elles permettent d'aider la navigation. Elles sont affichées sous forme
d'objets 3D ajoutés à la scène. Deux affichages ont été testés.
\subsection{Les \emph{viewports}}
\paragraph{}
Les \emph{viewports} sont les affichages les plus simples : ils représentent
une caméra, avec son centre optique et son plan image.
\begin{figure}[H]
\centering
\includegraphics[scale=0.275]{img/new/01.png}
\caption{Une recommandation \emph{viewport}}
\end{figure}
Cette façon d'afficher une recommandation a l'avantage d'être simple, de ne pas
beaucoup masquer le reste des modèles et suggère assez bien l'idée d'un
\emph{point de vue recommandé}, mais elle a l'inconvéniant d'être ambigüe à
cause de la perspective (dans cette image, il peut être difficile de savoir si
le point de vue et vers le modèle ou vers nous).
\subsection{Les flèches}
\subsubsection{Principe}
Les flèches sont supposées être plus intuitives pour un utilisateur qui n'a pas
l'habitude des \emph{viewports} précédemment utilisés. Plutôt que de suggérer
un point de vue, elles suggèrent le mouvemement qui va mener à ce point de vue.
\begin{figure}[H]
\centering
\includegraphics[scale=0.275]{img/new/02.png}
\caption{Des recommandations flèches}
\end{figure}
\subsubsection{Courbure de la flèche}
Pour le dessin des flèches, plusieurs choses sont à prendre en compte :
\begin{itemize}
\item il faut éviter que la flèche soit trop \emph{présente} à l'écran et
qu'elle obstrue trop le reste de la scène
\item il faut que la flèche soit dans un plan qui ne soit pas orthogonal au
plan image de la caméra. En effet, si la flèche est dans un plan
orthogonal au plan image de la caméra, elle se projettera comme un
segment et on ne pourra pas voir la courbure de la flèche.
\end{itemize}
\paragraph{}
Pour trouver la courbure de la flèche, nous posons $C$ le centre de la caméra,
$R$ le centre de la recommandation, et $R'$ le vecteur qui donne la direction
de la recommandation. Nous cherchons ensuite un polynôme $f$ tel que :
$$\left\{\begin{array}{lcl}
f(0) & = & C \\
f(1) & = & R \\
f'(1) & = & \lambda R' \text{ avec } \lambda \in \mathbb{R}^{+}
\end{array}\right.$$
\paragraph{}
Le problème de ce polynôme est qu'il ne vérifie aucune des contraintes énoncées
précédemment : puisque $f(0) = C$, le bout de la flèche est dans la caméra, et
va donc masquer toute la scène, et la courbe va en plus se projeter sur une
ligne sur l'écran.
\paragraph{}
Pour solutionner le premier problème, nous nous contenterons d'afficher
seulement la flèche pour des instants $t \in [0.5, 1]$.
\paragraph{}
Pour solutionner le deuxième problème, nous allons translater le centre de la
caméra vers le bas et le côté de la direction de la recommandation, pour
accentuer la courbure de la flèche, et nous résolvons plutôt le système suivant
:
$$\left\{\begin{array}{lcl}
f(0) & = & C - e_z + \lambda R' \\
f(1) & = & R \\
f'(1) & = & \lambda R' \text{ avec } \lambda \in \mathbb{R}^{+}
\end{array}\right.$$
\subsection{Les interactions}
\subsubsection{Au survol}
\indent Cette fonctionnalité est inspirée des récents lecteurs video sur le
web. Lorsque l'on regarde une video, on a la barre de \emph{seeking} en bas et
passer le curseur sur cette barre affiche l'image de la vidéo à l'instant visé.
Nous avons simplement adapté cette techniques à nos recommandations : lorsque
le curseur survole une recommandation, une prévisualisation est affichée dans
une petite boite au voisinage du curseur.
\begin{figure}[H]
\centering
\includegraphics[scale=0.275]{img/new/03.png}
\caption{Une prévisualisation}
\end{figure}
\subsubsection{Au clic}
\indent Lors d'un clic sur une recommandation, la caméra suit un mouvement
fluide jusqu'au point de vue recommandé. La trajectoire est définié par un
polynôme interpolant tel que :
\begin{itemize}
\item la position initiale est la position de la caméra
\item la position finale est la position de la recommandation
\item la dérivée de la trajectoire à l'instant final est la direction de la
recommandation
\end{itemize}
\paragraph{}
Ce mouvement fluide est là pour ne pas perturber l'utilisateur qui pourrait
\emph{se perde} si jamais il était téléporté
\paragraph{}
De plus, les recommandations se comportent comme des liens hypertextes : elles
sont bleues si elles n'ont jamais été clicées, et deviennent violettes si
l'utilisateur les a déjà consommées. Ceci est fait pour qu'un utilisateur
puisse savoir par où il est passé, et ce qui lui reste encore à visiter.
\section{Autres éléments de navigation}
\paragraph{}
Pour faciliter la navigation, quelques autres éléments de navigation sont
présents.
\begin{figure}[H]
\centering
\begin{tikzpicture}[]
\node (myfirstpic) at (0,0) {\includegraphics[scale=0.35]{img/new/buttons.png}};
\tikzvline{-7}{1}
\tikzvline{-5.8}{2}
\tikzvline{-5.2}{3}
\tikzvline{-4}{4}
\tikzvline{-1.5}{5}
\draw (8,5) -- (7,5);
\draw (8,5) node[right]{6};
\draw (-8,5) -- (-7,5);
\draw (-8,5) node[left]{7};
\end{tikzpicture}
\caption{Les différents éléments de l'interface}
\end{figure}
\paragraph{}
\begin{enumerate}
\item \emph{Reset camera} : pour chaque scène, une position initiale est
définie. Cliquer sur ce bouton ramène la caméra à sa position initiale.
\item \emph{Previous} : à chaque clic sur une recommandation, les positions
intiales et finales sont sauvegardées. Cliquer sur ce bouton ramène à
la position précédente.
\item \emph{Next} : cliquer sur ce bouton ramène à la position suivante.
\item \emph{Pointer lock} : permet de passer du mode \emph{pointer-lock} au
mode \emph{drag-n-drop} et vice-versa.
\item \emph{Music} : un lecteur qui contrôle une petite musique qui permet
de se mettre dans l'ambiance de la scène.
\item \emph{Coin gauge} : une jauge qui représente l'avancement de la
récupération des pièces.
\item \emph{FPS counter} : indique la période de rafraîchissement du rendu.
\end{enumerate}

1
rapport/intro.tex Normal file
View File

@ -0,0 +1 @@
\part*{Introduction}

220
rapport/streaming.tex Normal file
View File

@ -0,0 +1,220 @@
\part{Streaming de modèle 3D}
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{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}

111
rapport/techno.tex Normal file
View File

@ -0,0 +1,111 @@
\part{Choix des technologies et prise en main}
\paragraph{}
La première phase de stage était de choisir les technologies qui allaient être
utilisées par la suite. Nous cherchions des technologies permettant la
visualisation 3D sur un navigateur web afin de pouvoir faire une étude
utilisateur simplement.
\section{Côté client}
\paragraph{}
Pour le côté client, il y avait plusieurs possibilités :
\begin{itemize}
\item WebGL, la spécification des fonctions permettant la 3D dans le
navigateur
\item Une librairie facilitant l'utilisation de WebGL
\item Du code C++ compilé en JavaScript grâce à Emscripten
\item N'importe quel moteur graphique qui puisse exporter vers JavaScript
\end{itemize}
\paragraph{}
La plupart des moteurs graphiques exportant vers JavaScript sont putôt lourd à
prendre en main, et nous voulions garder des solutions simples, c'est pourquoi
nous avons utilisé une librairie libre nommée \threejs permettant une
utilisation facile de WebGL.
\paragraph{}
Pour des raisons de simplicité, nous avons décidé de développer le code client
pour Google Chrome et Firefox, les autres navigateurs ne sont donc pas
(officiellement) supportés.
\section{Côté serveur}
\paragraph{}
Dans un premier temps, seul le côté client était pris en compte. Les programmes
étaient écrits en JavaScript et ne nécessitaient pas de serveur. Quand les
problématiques de dynamicité sont arrivées, il a fallu choisir une technologie
pour le côté serveur, et là, tous les langages étaient possibles.
\paragraph{}
Plusieurs langages et framework ont été téstés. Quand les problématiques
étaient encore simples (passage d'un paramètre dans une requête), on a commencé
par utiliser le php, puis on s'est tourné vers des scripts CGI en python. Quand
de plus nombreuses pages ont été nécessaires, on a commencé à chercher un vrai
framework, et on s'est penché sur Django (framework web pour Python) qui est
très pratique mais assez coûteux en mémoire vive (le serveur était alors
herbergé sur une petite machine de 512Mo de RAM).
\paragraph{}
Quand les problématiques de streaming ont commencé à apparaître, nous avons
choisi la simplicité en utilisant Node.js pour le côté serveur (un serveur
écrit en JavaScript) à cause de la présence d'une librairie nommée \socketio
qui s'avère très pratique pour la communication entre le client et le serveur.
Pour des raisons pratiques, le serveur a été herbergé sur un cloud gratuit
(OpenShift).
\section{Base de données}
\paragraph{}
Pour le système de gestion de base de données, nous avons choisi Postgres (qui
est libre et qui a largement fait ses preuves). OpenShift propose d'héberger
lui-même la base de données, mais la version gratuite ne proposant qu'1 Go
d'espace de stockage, nous avons préféré l'héberger nous-même.
\section{Développement, debug et déploiement}
\paragraph{}
Pour éviter d'avoir des fichiers trop longs, nous avons choisi de séparer les
sources dans de nombreux fichiers de taille plus petite, et de les fusionner
automatiquement. Pour le développement, ils seront simplement concaténés grâce
à un script développé spécialement pour cela, qui mime les paramètres de
Closure Compiler qui sera utilisé pour la fusion au moment du le déploiement
(ce dernier permet non seulement la fusion des fichiers mais aussi la
minification\footnote{la minification sert notamment à réduire la taille du
script : n'oublions pas que nous parlons de serveur web, et il est donc
intéressant de réduire la taille des programmes de sorte à les charger plus
rapidement} (effacement des commentaires et des retours à la ligne,
simplifications des noms de variables et plus \footnote{en JavaScript, il est
plus court d'écrire \texttt{!0} pour \texttt{true} par exemple.}). Pour le
développement, on a utilisé \href{https://github.com/remy/nodemon}{nodemon} et
inotify, qui permettent de relancer le serveur local lorsqu'une modification
est détectée (la fusion des fichiers est donc réeffectuée).
\paragraph{}
En ce qui concerne le versionnage des fichiers, nous avons utilisé Git avec
deux \emph{repositories} :
\begin{itemize}
\item le premier, hébergé sur
\href{https://github.com/tforgione/3dinterface}{Github}, sert au
développement, et contient les fichiers fractionnés ainsi que les
outils permettant la génération des fichiers fusionnés.
\item le deuxième, hebergé chez OpenShift, qui contient la version finale
du programme, permet de déployer le code du serveur quand les
\emph{commits} sont \emph{pushés}.
\end{itemize}
\paragraph{}
Pour nous aider au debug, nous avons utilisé \href{http://jshint.com/}{JSHint}
qui nous aide à détecter les erreurs potentielles liées aux subtilités du
langage.
\section{Documentation}
Au delà des rapports, deux documentations sont présentes.
\subsection{\href{https://github.com/tforgione/3dinterface/wiki}{\emph{Github Wiki}}}
Github permet la création de Wiki pour chaque \emph{repository} et nous nous en
sommes servi pour de la documentation de haut niveau : il ne présente que des
aspects théoriques de ce qui a été réalisé pendant ce projet.
\subsection{\href{http://l3d.no-ip.org/}{L3D}}
Pour de la documentation de plus bas niveau (comment chaque classe ou méthode
fonctionne) nous avons utilisé \jsdoc (équivalent de javadoc mais pour
JavaScript) et nous générons automatiquement des pages html pour avoir une
documentation lisible et à jour sans avoir à parcourir le code.

148
rapport/userstudy.tex Normal file
View File

@ -0,0 +1,148 @@
\part{L'étude utilisateur}
\paragraph{}
Pour tester le comportement des utilisateurs face aux recommandations, nous
avons dissimulé des pièces rouges à travers ces modèles, et nous avons demandé
à des utilisateurs de les trouver.
\begin{figure}[H]
\centering
\includegraphics[scale=0.275]{img/new/04.png}
\caption{Une pièce rouge}
\end{figure}
\paragraph{}
Pour éviter la dépendance entre les recomendations et les pièces rouges (si les
recommandations visent les pièces rouges, il est évident qu'il sera très facile
de les trouver avec les recommandations), un système de tirage aléatoire de
pièces rouges a été fait.
\section{Déroulement de l'expérience}
\subsection{Première page}
La première page présente rapidement l'expérience. Elle vérifie aussi le
navigateur : si le client est sur Google Chrome ou Firefox, un lien apparaîtra
pour passer à la suite, sinon, un message d'erreur s'affichera.
\subsection{Identification}
Cette page nous permet d'en savoir un peu plus sur l'utilisateur : nous allons
demander l'age, le sexe et les habitudes en terme de jeux video de
l'utilisateur (nous avons considéré que la capacité des utilisateurs à manier
notre interface allait dépendre fortement de leur habitude aux jeux video).
Nous leur demandons notamment de noter leurs capacités en terme de jeux videos
(entre 1 et 5 étoiles).
\subsection{Tutoriel}
Ensuite, nous demandons à l'utilisateur de suivre un tutoriel : c'est une sorte
de réelle expérience mais guidée. Des messages indiquant les interactions à
faire aideront l'utilisateur à s'habituer à cette interface. On y présente les
moyens de déplacer la caméra, puis les recommandations et les différents
boutons de l'interface.
\subsection{Les trois vraies expériences}
C'est ensuite que la \emph{vraie} partie de l'étude utilisateur commence : à 3
reprises, l'utilisateur va se retrouver dans une scène avec certaines pièces
rouges à trouver, et un certain style de recommandations\footnote{aucune
recommandation sera considéré comme un style de recommandation, de sorte à
comparer la présence à l'absence de recommandation pour la navigation} pour
l'aider. Nous expliquerons dans la sous-section \ref{selection} comment le
choix de la scène et du style de recommandation sera fait.
\paragraph{}
Dans chacune des expériences, l'utilisateur devra chercher des pièces rouges,
en s'aidant (ou pas) des recommandations. L'expérience se terminera soit quand
l'utilisateur aura trouvé les huit pièces rouges, soit une minute après avoir
trouvé la 6\up{ème} pièce rouge. Pour éviter que l'utilisateur soit
\emph{frustré} de ne pas avoir trouvé toutes les pièces, nous n'indiquons pas
clairement le nombre de pièces qu'il a, ou qu'il lui reste à trouver.
Simplement, une \emph{vague} idée de sa progression.
\subsection{Le \emph{feedback}}
Après avoir fini ces expériences, l'utilisateur se retrouvera sur un formulaire
lui demandant son avis quand à la difficulté de l'interface, et l'utilité des
recommandations pour se déplacer dans la scène.
\section{Génération des expériences}
\subsection{Choix de la scène et du style de recommandations\label{selection}}
\paragraph{}
Il y a trois scènes disponibles, et sur chaque scène, nous avons placé des
pièces dans de nombreuses positions. Lorsqu'un utilisateur commence une
expérience, l'algorithme de selection fonctionne de façon à faire des
expériences sur les mêmes scènes avec les mêmes dispositions de pièces mais des
styles de recommandations différents pour des utilisateurs de même niveau de
sorte à pouvoir comparer l'efficacité des recommandations.
\paragraph{}
Nous cherchons en fait des expériences qui permettraient de compléter les trio
d'expériences avec des pièces identiques sur des scènes identiques mais avec
des styles de recomendations différents. Si une telle expérience n'existe pas
(les trios sont déjà complets, ou bien il n'existe pas d'expérience pour un
niveau d'utilisateur donné), la scène, ainsi que les pièces à trouver et le
style de recommandations seront choisi aléatoirement : on choisira une scène et
un style de recommandations que l'utilisateur n'a pas encore effectué, et on
choisira 8 pièces aléatoirement parmi les positions possibles que nous avons
fixées au préalable.
\subsection{Positions possibles des pièces}
Pour choisir les positions possibles des pièces dans chaque scène, un petit
outil à été développé permettant de se déplacer dans une scène et des créer des
pièces en cliquant. Cliquer sur une paroie de la scène crée une pièce devant
cette paroie, et cliquer sur une pièce la supprime. Un bouton permet d'envoyer
la liste des pièces par mail, lorsque l'on a fini de créer des pièces.
\section{Collecte des informations}
Ces expérienes n'auront d'interêt que si nous sommes capables de les analyser
par la suite et de comprendre comment les utilisateurs interagissent avec les
recommandations. Nous avons donc mis en place un système de collecte des
actions de l'utilisateur basé sur les XmlHttpRequests de JavaScript.
Côté serveur, il y a quelques urls qui permettent d'enregistrer des
informations dans des tables prévues à cet effet. Chaque évènement contient la
date à laquelle il a été envoyé par le client ainsi que l'id du client et de
l'expérience qu'il est en train de faire (ces deux cerniers sont stockés dans
la session sur le serveur). Les évènements enregistrés sont les suivants :
\paragraph{ArrowClicked} : crée quand l'utilisateur clique une recommandation,
accompagné de l'id de la recommandation cliquée.
\paragraph{CoinClicked} : crée quand l'utilisateur récupère une pièce rouge.
\paragraph{KeyboardEvent} : crée quand l'utilisateur appuie ou relâche une
touche du clavier, accompagné de la position courante de la caméra et d'un
booléen indiquant si elle a été appuyée ou relâchée. Dans le cas du
\emph{drag-n-drop} ou du \emph{pointer lock}, on créera de temps en temps
quelques évènements de type \texttt{KeyboardEvent} non associée à une touche de
sorte à connaître l'angle de la caméra.
\paragraph{ResetClicked} : crée quand l'utilisateur réinitialise la position de
la caméra.
\paragraph{PreviousNextClicked} : crée quand l'utilisateur clique sur les
boutons précédente ou suivante, on stockera la position finale en base de
données.
\paragraph{Hovered} : crée quand l'utilisateur survole ou sort d'une
recommandation avec le curseur.
\paragraph{PointerLocked} : dans le cas où l'utilisateur utilise l'option
\emph{pointer lock}, il sera crée au moment où le pointeur sera capturé et où
il sera libéré.
\paragraph{SwitchedLockOption} : crée quand l'utilisateur change d'option entre
\emph{pointer locked} et \emph{drag-n-drop}.
\paragraph{FpsCounter} : chaque seconde, cet évènement est crée pour connaître
le \emph{framerate} du client, de sorte à savoir si les performances de sa
machine ont pu lui poser problème dans ces expériences.
\section{Replay}
Afin de nous assurer que toutes les informations nécessaires étaient bel et
bien récupérées en base de données, nous avons mis en place un programme
permettant de rejouer les expériences stockées.
\paragraph{}
Pour le replay, nous avons simplement considéré les évènements
\texttt{ArrowClicked}, \texttt{CoinClicked}, \texttt{KeyboardEvent},
\texttt{ResetClicked} et \texttt{PreviousNextClicked}, les autres évènements
n'ayant pas d'influence sur la position de la caméra.