\fresh{}
\section{JavaScript client\label{d3i:js-implementation}}

\subsection{Media engine}

\subsubsection{THREE.js}

On the web browser, the best way to perform 3D rendering is to use WebGL\@.
However, WebGL is very low level and it can be really painful to write code, even to render a simple triangle.
For example, \href{https://www.tutorialspoint.com/webgl/webgl_drawing_a_triangle.htm}{this tutorial}'s code contains 121 lines of javascript, 46 being code (not comments or empty lines) to render a simple, non-textured triangle.
For this reason, it seems unreasonable to build a system like the one we are describing in raw WebGL\@.
There are many libraires that wrap WebGL code and that help people building 3D interfaces, and \href{https://threejs.org}{THREE.js} is probably one of the most popular.

THREE.js acts as a 3D engine built on WebGL\@.
It provides classes to deal with everything we need:
\begin{itemize}
    \item the \textbf{Renderer} class contains all the WebGL code needed to render a scene on the web page;
    \item the \textbf{Object} class contain all the boilerplate needed to manage the tree structure of the content, it contains a transform and it can have children that are other objects;
    \item the \textbf{Scene} class is the root object, it contains all of the objects we want to render and it is passed as argument to the render function;
    \item the \textbf{Geometry} and \textbf{BufferGeometry} classes are the classes that hold the vertices buffers, we will discuss that more in Section~\ref{d3i:geometries};
    \item the \textbf{Material} class is the class that holds the properties used to render geometry (the most important information being the texture), there are many classes derived from Material, and the developper can choose what material he wants for its objects;
    \item the \textbf{Mesh} class is the class that links the geometry and the material, it derives the Object class and can thus be added to a scene and renderer.
\end{itemize}
A snippet of the basic usage of these classes is given in Listing~\ref{d3i:three-hello-world}.

\begin{figure}[th]
\lstinputlisting[%
        language=javascript,
        caption={A THREE.js \emph{hello world}},
        label=d3i:three-hello-world,
    ]{assets/dash-3d-implementation/base.js}
\end{figure}

\subsubsection{Geometries\label{d3i:geometries}}

Geometries are the classes that hold the vertices, texture coordinates, normals and faces.
There are two most important geometry classes in THREE.js:
\begin{itemize}
    \item the \textbf{Geometry} class, which is made to be developper friendly and allows easy editing but can suffer issues of performance;
    \item the \textbf{BufferGeometry} class, which is harder to use for a developper, but allows better performance since the developper controls and data is transmitted to the GPU\@.
\end{itemize}

Of course, in this work, we are concerned about performance of our system, and we will not be able to use normal geometries.
However, the way our system works, the way changes happen to the 3D content is always the same: we only add faces and textures to the model.
Therefore, we made a class that derives BufferGeometry, and that makes it more convenient for us.
\begin{itemize}
    \item It has a constructor that takes as parameter the number of faces: it allocates all the memory needed for our buffers so we do not have to reallocate it which would be inefficient.
    \item It keeps track of the number of faces it is currently holding: it can then avoid rendering faces that have not been filled and knows where to put new faces.
    \item It provides a method that adds a face to the geometry.
    \item It also keeps track of what part of the buffers has been transmitted to the GPU\@: THREE.js allows us to set the range of the buffer that we want to update and we are able to update only what is necessary.
\end{itemize}

\subsubsection{Our 3D model class\label{d3i:model-class}}

As said in the previous sections, a geometry and a material a bound together in a mesh.
This means that we are forced to have has many meshes as there are materials in our model.
To make this easy to manage, we made a \textbf{Model} class, that holds everything we need.
We can add vertices, faces, and materials to this model, and it will internally deal with the right geometries, materials and meshes.
In order to avoid having many models that have the same material (which would harm performance since it would increase the number of \texttt{glDrawArray} calls), it automatically merges faces that share the same material in the same buffer geometry, as shown in Figure~\ref{d3i:render-structure}.

\begin{figure}[ht]
    \centering

    \begin{tikzpicture}

        \node[align=center] at(1.5, -1) {DASH-3D\\Structure};

        \node at(-1.5, -3) {\texttt{seg0.obj}};
        \draw[fill=Pink] (0, -2) rectangle (3, -3);
        \node at(1.5, -2.5) {Material 1};
        \draw[fill=PaleGreen] (0, -3) rectangle (3, -4);
        \node at(1.5, -3.5) {Material 2};

        \node at(-1.5, -6) {\texttt{seg1.obj}};
        \draw[fill=Pink] (0, -5) rectangle (3, -7);
        \node at(1.5, -6) {Material 1};

        \node at(-1.5, -9) {\texttt{seg2.obj}};
        \draw[fill=PaleGreen] (0, -8) rectangle (3, -9);
        \node at(1.5, -8.5) {Material 2};
        \draw[fill=LightBlue] (0, -9) rectangle (3, -10);
        \node at(1.5, -9.5) {Material 3};

        \node[align=center] at (7.5, -1) {Renderer\\Structure};

        \node at(10.5, -3.5) {Object 1};
        \draw[fill=Pink] (6, -2) rectangle (9, -5);
        \node at(7.5, -3.5) {Material 1};

        \node at(10.5, -7) {Object 2};
        \draw[fill=PaleGreen] (6, -6) rectangle (9, -8);
        \node at(7.5, -7) {Material 2};

        \node at(10.5, -9.5) {Object 3};
        \draw[fill=LightBlue] (6, -9) rectangle (9, -10);
        \node at(7.5, -9.5) {Material 3};

        \node[minimum width=0.5cm,minimum height=2cm] (O1) at (6.25, -3.5) {};
        \draw[-{Latex[length=3mm]}, color=FireBrick] (3, -2.5) -- (O1);
        \draw[-{Latex[length=3mm]}, color=FireBrick] (3, -6) -- (O1);

        \node[minimum width=0.5cm,minimum height=2cm] (O2) at (6.25, -7) {};
        \draw[-{Latex[length=3mm]}, color=DarkGreen] (3, -3.5) -- (O2);
        \draw[-{Latex[length=3mm]}, color=DarkGreen] (3, -8.5) -- (O2);

        \node[minimum width=3cm,minimum height=2cm] (O3) at (7.5, -9.5) {};
        \draw[-{Latex[length=3mm]}, color=RoyalBlue] (3, -9.5) -- (O3);

        \node at (1.5, -10.75) {$\vdots$};
        \node at (7.5, -10.75) {$\vdots$};
    \end{tikzpicture}

    \caption{Restructuration of the content on the renderer\label{d3i:render-structure}}
\end{figure}

\subsection{Access client}

In order to be able to implement our DASH-3D client, we need to implement the access client, which is responsible for deciding what to download and download it.
To do so, we use the strategy pattern, as shown in Figure~\ref{d3i:dash-loader}.
We have a base class named \texttt{LoadingPolicy} that contain some attributes and functions to keep data about what has been downloaded that a derived class can use to make smart decisions, and exposes a function named \texttt{nextSegment} that takes two arguments:
\begin{itemize}
    \item the MPD, so that a strategy can know all the metadata of the segments before making its decision;
    \item the camera, because the best segment depends on the position of the camera.
\end{itemize}

The greedy, greedy predictive and proposed policies from the previous chapter are all classes that derive from \texttt{LoadingPolicy}.
Then, the main class responsible for the loading of segments is the \texttt{DashLoader} class.
It uses \texttt{XMLHttpRequest}s, which are the usual way of making HTTP requests in JavaScript, and it calls the corresponding parser on the results of those requests.
The \texttt{DashLoader} class accepts as parameter a function that will be called each time some data has been downloaded and parsed: this data can contain vertices, texture coordinates, normals, materials or textures, and they can all be added to the \texttt{Model} class that we described in Section~\ref{d3i:model-class}.

\begin{figure}[ht]
    \centering
    \begin{tikzpicture}[scale=0.65]
        \draw (0, 0) rectangle (5, -4);
        \draw (0, -1) -- (5, -1);
        \node at (2.5, -0.5) {DashClient};
        \node[right] at (0, -1.5) {\scriptsize loadNextSegment()};

        \draw (5, -2) -- (8, -2);

        \draw (8, 0) rectangle (14, -4);
        \draw (8, -1) -- (14, -1);
        \node at (11, -0.5) {LoadingPolicy};
        \node[right] at (8, -1.5) {\scriptsize nextSegment(mpd, camera)};

        \draw (1, -6) rectangle (7, -10);
        \draw (1, -7) -- (7, -7);
        \node at (4, -6.5) {Greedy};
        \node[right] at (1, -7.5) {\scriptsize nextSegment(mpd, camera)};

        \draw (8, -6) rectangle (14, -10);
        \draw (8, -7) -- (14, -7);
        \node at (11, -6.5) {GreedyPredictive};
        \node[right] at (8, -7.5) {\scriptsize nextSegment(mpd, camera)};

        \draw (15, -6) rectangle (21, -10);
        \draw (15, -7) -- (21, -7);
        \node at (18, -6.5) {Proposed};
        \node[right] at (15, -7.5) {\scriptsize nextSegment(mpd, camera)};

        \draw[-{Triangle[open, length=3mm, width=3mm]}] (4, -6) -- (4, -5) -- (11, -5) -- (11, -4);
        \draw (11, -6) -- (11, -5) -- (8, -5);
        \draw (18, -6) -- (18, -5) -- (8, -5);
    \end{tikzpicture}
    \caption{Class diagram of our DASH client\label{d3i:dash-loader}}
\end{figure}

\subsection{Performance}

In JavaScript, there is no way of doing parallel computing without using \emph{web workers}.
A web worker is a script in JavaScript that runs in the background, on a separate thread and that can communicate with the main script by sending and receiving messages.
Since our system has many tasks to do, it seems natural to use workers to manage the streaming without impacting the framerate of the renderer.
However, what a worker can do is very limited, since it cannot access the variables of the main script.
Because of this, we are forced to run the renderer on the main script, where it can access the HTML page, and we move all the other tasks to the worker (the access client, the control engine and the segment parsers), and since the main script is the one communicating with the GPU, it will still have to update the model with the parsed content it receives from the worker.
Using the worker does not so much improve the framerate of the system, but it reduces the latency that occurs when receiving a new segment, which can be very frustrating since in a single thread scenario, each time a segment is received, the interface freezes for around half a second.
A sequence diagram of what happens when downloading, parsing and rendering content is shown in Figure~\ref{d3i:sequence}.

\begin{figure}[ht]

    \centering

    \begin{tikzpicture}
        \node at(0, 1) {Main script};
        \draw[->, color=LightGray] (0, 0.5) -- (0, -17.5);

        \node at(2.5, 1) {Worker};
        \draw[->, color=LightGray] (2.5, 0.5) -- (2.5, -17.5);

        \node at(10, 1) {Server};
        \draw[->, color=LightGray] (10, 0.5) -- (10, -17.5);

        % MPD
        \draw[color=blue] (0, 0) -- (2.5, -0.1) -- (10, -0.5);
        \draw[color=blue, fill=PaleLightBlue] (10, -0.5) -- (10, -1.5) -- (2.5, -2) -- (2.5, -1) -- cycle;
        \node[color=blue, rotate=5] at (6.25, -1.25) {Download};
        \node[color=blue, above] at(1.25, 0.0) {Ask MPD};
        \draw[color=blue, fill=LightBlue] (2.375, -2) rectangle (2.625, -2.9);
        \node[color=blue, right=0.2cm] at(2.5, -2.45) {Parse MPD};
        \draw[color=blue] (2.5, -2.9) -- (0, -3);
        \draw[color=blue, fill=LightBlue] (-0.125, -3.0) rectangle(0.125, -3.5);
        \node[color=blue, left=0.2cm] at (0.0, -3.25) {Update model};

        % Ask segments
        \begin{scope}[shift={(0, -3.5)}]
            \draw[color=red] (0, 0) -- (2.5, -0.1);
            \node[color=red, above] at(1.25, 0.0) {Ask segment};

            \draw[color=red, fill=Pink] (2.375, -0.1) rectangle (2.625, -1);
            \node[color=red, right=0.2cm] at (2.5, -0.55) {Compute utilities};

            \draw[color=red] (2.5, -1) -- (10, -1.5);
            \draw[color=red, fill=PalePink] (10, -1.5) -- (10, -2.5) -- (2.5, -3) -- (2.5, -2) -- cycle;
            \node[color=red, rotate=5] at (6.25, -2.25) {Download};

            \draw[color=red, fill=Pink] (2.375, -3) rectangle (2.625, -3.9);
            \node[color=red, right=0.2cm] at(2.5, -3.45) {Parse segment};

            \draw[color=red] (2.5, -3.9) -- (0, -4);
            \draw[color=red, fill=Pink] (-0.125, -4.0) rectangle(0.125, -4.5);
            \node[color=red, left=0.2cm] at (0.0, -4.25) {Update model};
        \end{scope}

        % Ask more segments
        \begin{scope}[shift={(0, -8)}]
            \draw[color=DarkGreen] (0, 0) -- (2.5, -0.1);
            \node[color=DarkGreen, above] at(1.25, 0.0) {Ask segment};

            \draw[color=DarkGreen, fill=PaleGreen] (2.375, -0.1) rectangle (2.625, -1);
            \node[color=DarkGreen, right=0.2cm] at (2.5, -0.55) {Compute utilities};

            \draw[color=DarkGreen] (2.5, -1) -- (10, -1.5);
            \draw[color=DarkGreen, fill=PalePaleGreen] (10, -1.5) -- (10, -2.5) -- (2.5, -3) -- (2.5, -2) -- cycle;
            \node[color=DarkGreen, rotate=5] at (6.25, -2.25) {Download};

            \draw[color=DarkGreen, fill=PaleGreen] (2.375, -3) rectangle (2.625, -3.9);
            \node[color=DarkGreen, right=0.2cm] at(2.5, -3.45) {Parse segment};

            \draw[color=DarkGreen] (2.5, -3.9) -- (0, -4);
            \draw[color=DarkGreen, fill=PaleGreen] (-0.125, -4.0) rectangle(0.125, -4.5);
            \node[color=DarkGreen, left=0.2cm] at (0.0, -4.25) {Update model};
        \end{scope}

        % Ask even more segments
        \begin{scope}[shift={(0, -12.5)}]
            \draw[color=purple] (0, 0) -- (2.5, -0.1);
            \node[color=purple, above] at(1.25, 0.0) {Ask segment};

            \draw[color=purple, fill=Plum] (2.375, -0.1) rectangle (2.625, -1);
            \node[color=purple, right=0.2cm] at (2.5, -0.55) {Compute utilities};

            \draw[color=purple] (2.5, -1) -- (10, -1.5);
            \draw[color=purple, fill=PalePlum] (10, -1.5) -- (10, -2.5) -- (2.5, -3) -- (2.5, -2) -- cycle;
            \node[color=purple, rotate=5] at (6.25, -2.25) {Download};

            \draw[color=purple, fill=Plum] (2.375, -3) rectangle (2.625, -3.9);
            \node[color=purple, right=0.2cm] at(2.5, -3.45) {Parse segment};

            \draw[color=purple] (2.5, -3.9) -- (0, -4);
            \draw[color=purple, fill=Plum] (-0.125, -4.0) rectangle(0.125, -4.5);
            \node[color=purple, left=0.2cm] at (0.0, -4.25) {Update model};
        \end{scope}

        \foreach \x in {0,...,5}
        {
            \draw[color=Goldenrod, fill=LemonChiffon] (-0.125, -\x/2) rectangle (0.125, -\x/2-0.5);
        }
        \node[color=Goldenrod, left=0.2cm] at (0.0, -1.5) {Render};

        \foreach \x in {0,...,7}
        {
            \draw[color=Goldenrod, fill=LemonChiffon] (-0.125, -\x/2-3.5) rectangle (0.125, -\x/2-4);
            \draw[color=Goldenrod, fill=LemonChiffon] (-0.125, -\x/2-8) rectangle (0.125, -\x/2-8.5);
            \draw[color=Goldenrod, fill=LemonChiffon] (-0.125, -\x/2-12.5) rectangle (0.125, -\x/2-13);
        }
        \node[color=Goldenrod, left=0.2cm] at (0.0, -5.5) {Render};
        \node[color=Goldenrod, left=0.2cm] at (0.0, -10) {Render};
        \node[color=Goldenrod, left=0.2cm] at (0.0, -14.5) {Render};

    \end{tikzpicture}

    \caption{Repartition of the tasks on the main script and the worker\label{d3i:sequence}}

\end{figure}
