
/*! \addtogroup draw glDraw( ) et la famille

cf \ref tuto6GL.cpp

glDraw( ) est une famille de fonctions, et selon la description du maillage (sommets partagés, ou pas) il faut utiliser :
    - glDrawElements( ) : lorsque le maillage est décrit par des sommets partagés / indexés, les triangles sont décrits par des triplets d'indices,
    - glDrawArrays( ) : lorsque le maillage est décrit uniquement par les coordonnées de ses sommets, les triangles sont décrits par des triplets de positions.

la classe Mesh peut stocker les 2 types de maillages : il suffit de vérifier qu'un index buffer existe (cf Mesh::index_count() > 0 ou Mesh::index_buffer_size() > 0) 
pour savoir qu'il faut utiliser glDrawElements( ) plutot que glDrawArrays( ). cf \ref tuto4GL pour des exemples.

_remarque : quel est l'intêret d'utiliser des sommets partagés ?_

la description de l'objet est plus compacte...
un indice est un entier stocké sur 4, 2 ou 1 octets, un sommet est a priori 3 floats qui occuppent 4 octets chacuns, soit 12 octets au total. si l'on ajoute les 
normales (3 floats) et les coordonnées de textures (2 floats), un sommet occuppe 3x4 + 3x4 + 2x4 = 32 octets.

un simple cube composé de 8 sommets (soit 8x32 = 256 octets) et de 12 triangles indexés (12x3x4 = 144 octets) occuppe 400 octets.
sans utiliser l'indexation, il faut décrire les 3 sommets des 12 triangles : 12x3x32 = 1152 octets... et autre amélioration, les vertex shaders ne transforment que 8 sommets dans un cas, 
contre 36 dans l'autre...


### glDrawArrays et Mesh

s'il n'existe pas d'index buffer dans l'objet, il suffit de créer un vertex buffer et de configurer un vao :
\code
Mesh mesh = { ... };

// configuration
GLuint vao= 0;
GLuint vertex_buffer= 0;

glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.vertex_buffer_size(), mesh.vertex_buffer(), GL_STATIC_DRAW);

// configure l'attribut 0, cf layout(location= 0) in vec3 position, dans le vertex shader
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttrib(0);

// nettoyage
glBindVertexArray(0);   // !! pourquoi en premier ? !!
glBindBuffer(GL_ARRAY_BUFFER, 0);

// draw
glBindVertexArray(vao);
glUseProgram( ... );
glUniform( ... );

glDrawArrays(GL_TRIANGLES, /* first */ 0, /* count */ mesh.vertex_count());
\endcode

### glDrawElements et Mesh

la démarche est la même, il faut créer l'index buffer, en plus du vertex buffer et bien sur configurer le vao :
\code
Mesh mesh = { ... };

// configuration
GLuint vao= 0;
GLuint vertex_buffer= 0;
GLuint index_buffer= 0;

glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.vertex_buffer_size(), mesh.vertex_buffer(), GL_STATIC_DRAW);

// configure l'attribut 0, cf layout(location= 0) in vec3 position, dans le vertex shader
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttrib(0);

if(mesh.index_count() > 0)
{
    glGenBuffers(1, &index_buffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.index_buffer_size(), mesh.index_buffer(), GL_STATIC_DRAW);
    
    // !! la configuration du vao est implicite au moment du BindBuffer(GL_ELEMENT_ARRAY_BUFFER) !!
}

// nettoyage
glBindVertexArray(0);   // !! pourquoi en premier ? !!
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

// draw
glBindVertexArray(vao);
glUseProgram( ... );
glUniform( ... );

// choisir le draw
if(mesh.index_count() == 0)
    // pas d'index buffer
    glDrawArrays(GL_TRIANGLES, /* first */ 0, /* count */ mesh.vertex_count());
else
    // avec un index buffer
    glDrawElements(GL_TRIANGLES, /* count */ mesh.index_count(), /* index type */ GL_UNSIGNED_INT, /* offset */ 0);
\endcode

les paramètres type et offset permettent d'itérer sur les indices stockés dans l'index buffer associé au vao.

__remarque :__ read_mesh( ) ne construit pas, pour l'instant, d'index buffer, donc un objet chargé par read_mesh( ) peut etre affiché directement par glDrawArrays().


en consultant la doc d'openGL, d'autres fonctions existent, à quoi servent-elles ? 
- glDrawArraysInstanced( ), glDrawElementsInstanced( ) : permettent de dessiner plusieurs copies du même objet,
- glDrawElementsBaseVertex( ) : permet de dessiner facilement un objet indexé, lorsque les attributs de ces sommets ne sont pas au début du vertex buffer... 


## draw instanced, dessiner plusieurs copies 

dans pas mal de cas, il est nécessaire d'afficher plusieurs fois le même objet ou des variantes du même objet. par exemple, des unités dans un jeu de stratégie, 
des arbres pour afficher une forêt, des touffes d'herbes pour une prairie, des _cubes_, etc.

malheureusement, la solution directe, faire un draw par copie n'est __vraiment pas__ une solution efficace. cf \ref tuto_time.cpp et (les explications \ref time) pour 
une démonstration.

glDrawArraysInstanced( ) / glDrawElementsInstanced() fournissent une solution (partielle) à ce problème. les fonctions prennent un paramètre 
supplémentaire (par rappport à glDrawArrays() / glDrawElements()), le nombre de copies à dessiner. 

parfait, reste un petit problème à régler... toutes les copies sont identiques et sont dessinées au même endroit, ce qui ne sert à rien. il faut pouvoir, au minimum,
les placer à des endroits différents dans la scène / le monde.

il y a 2 deux moyens : on peut stocker les propriétés de chaque copie dans un buffer et modifier le vertex array object en fonction, et/ou utiliser l'indice de la copie 
en cours de traitement (cf int gl_InstanceID) dans le vertex shader pour calculer des propriétés uniques à chaque instance.

exemple : dessiner n copies alignées avec gl_InstanceID :
il suffit de modifier le vertex shader pour calculer une position qui dépend de gl_InstanceID
\code
// vertex shader
in vec3 position;

uniform mat4 mvpMatrix;

void main( )
{
    // int gl_InstanceID varie de 0 a N, le nombre d'instances dessinees
    vec3 p= position + vec3(gl_InstanceID * 10, 0, 0);
    gl_Position= mvpMatrix * vec3(p, 1);
}
\endcode

et dans l'application :
\code
glBindVertexArray( ... );
glUseProgram( ... );
glUniform( ... );

glDrawArraysInstanced(GL_TRIANGLES, /* first */ 0, /* count */ 36, /* instance count */ 10); // dessine N= 10 copies
\endcode
permet de dessiner 10 cubes, par exemple. exemple complet dans \ref tuto6GL.cpp et \ref instanceID.glsl. \ref tuto_time.cpp utilise aussi cette solution 
pour dessiner des objets disposés sur une grille.


l'autre solution nécessite de créer un buffer pour stocker les paramètres de chaque instance et de configurer le format de sommet avec glVertexAttribDivisor(index, 1). 
le vertex shader doit également déclarer et utiliser l'attribut supplémentaire cf \ref instance_buffer.glsl :
\code
in vec3 position;               // attribut du sommet
in vec3 instance_position;      // l'attribut d'instance se declare "normalement", 
// c'est l'application qui doit correctement configurer le vao...

void main( )
{
    vec3 p= position + instance_position;
    gl_Position= mvpMatrix * vec4(p, 1);
    
    vertex_normal= mat3(normalMatrix) * normal;
}
\endcode

et voici comment \ref tuto6GL_buffer.cpp configure le vao, pour l'attribut d'instance :
\code
    std::vector<vec3> positions;
    
    // remplir les positions
    { ... }
    
    // creer le buffer
    glGenBuffers(1, &m_instance_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, m_instance_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vec3) * positions.size(), &positions.front().x, GL_STATIC_DRAW);

    // configure l'attribut,  vec3 instance_position
    GLint index= glGetAttribLocation(program, "instance_position");
    
    // configure le vao pour l'attribut d'instance
    glVertexAttribPointer(index, 3, GL_FLOAT, GL_FALSE, /* stride */ 0, /* offset */ 0);
    glEnableVertexAttribArray(index);
    
    glVertexAttribDivisor(index, 1);    // !! c'est la seule difference entre un attribut de sommet et un attribut d'instance !!
\endcode

__remarque :__ pourquoi divisor pour nommer une propriete d'instance ? euh, c'est pareil dans directx ? l'idée est de décrire à quel moment l'attribut 
change de valeur, pour chaque sommet (divisor == 0), ou pour chaque instance (divisor == 1), toutes les 2 instances (divisor == 2), etc.

__remarque :__ oui, ça permet d'avoir des propriétés d'instances qui sont définies pour un groupe d'instances, mais ce n'est pas très flexible...


## draw base vertex, organisation des données

si les données de plusieurs objets sont rangées dans le même vertex buffer, ca permet de ne configurer et de n'utiliser qu'un seul vao, ce qui peut simplifier 
le code d'affichage de plusieurs objets et permet aussi au driver de faire moins de travail.

si les sommets des objets A et B se trouvent dans le même buffer : 

`AAAAAA + BBB` 

on peut les afficher en utilisant le paramètre first de glDrawArrays( ):
\code
GLuint vao= { ... };
glBindVertexArray(vao);
glUseProgram( ... );
glUniform( ... );

glDrawArrays(GL_TRIANGLES, /* first */ 0, /* count */ 6);       // dessine A
glDrawArrays(GL_TRIANGLES, /* first */ 6, /* count */ 3);       // dessine B
\endcode

et il aussi possible de dessiner les 2 objets avec un seul draw :
\code
GLuint vao= { ... };
glBindVertexArray(vao);
glUseProgram( ... );
glUniform( ... );

glDrawArrays(GL_TRIANGLES, /* first */ 0, /* count */ 9);       // dessine A et B
\endcode

__remarque :__ si les objets ne sont pas placés les uns après les autres dans le vertex buffer, il faudra utiliser un draw par intervalle d'objets, ou utiliser glMultiDrawArrays()...

si les sommets des objets A, B, et C se trouvent dans le même buffer :

`AAAAAA + BBB + CCC`

et que l'on veut afficher A et C, il faudra utiliser 2 DrawArrays() ou préparer les paramètres de MultiDrawArrays( ) :
\code
std::vector<GLint> first;
std::vector<GLSizei> count;

// draw A
first.push_back(0);
count.push_back(6);

// draw C
first.push_back(9);
count.push_back(3);

glMultiDrawArrays(GL_TRIANGLES, /* first */ &first.front(), /* count */ &count.front(), /* draw count */ first.size());
\endcode

### et avec glDrawElements( ) ?

La solution précédente est simple à utiliser tant que l'affichage des objets peut se faire avec glDrawArrays(), si les objets utilisent un index buffer / des sommets partagés,
glDrawElements( ) ne permet pas directement de faire la même chose... _pourquoi ?_

si les sommets des objets A et B se trouvent dans le même buffer : 

`AAAA + BBB` 

et que leur indexation est : `012023` pour le quad A et `012` pour le triangle B, et qu'ils sont dans le même index buffer : 

`012023 + 012`

que va dessiner :
\code
// dessiner un triangle indexé à partir du 6ième indice du buffer
glDrawElements(GL_TRIANGLES, /* count */ 3, /* index type */ GL_UNSIGNED_INT, /* offset */ sizeof(unsigned int) * 6); 
\endcode

- réponse A : le triangle A0A1A2,
- réponse B : le triangle B0B1B2 ?

_indication :_ quel est l'indice du sommet 0 de l'objet B rangé dans le vertex buffer ? habituellement c'est 0 ? non ??

_réponse :_ ça dépend du nombre de sommets du premier objet rangé dans le buffer... 

et c'est très exactement cette valeur qui sert de paramètre base_vertex à glDrawElementBaseVertex( ) :
\code
// dessiner un triangle indexé à partir du 6ième indice du buffer
glDrawElementsBaseVertex(GL_TRIANGLES, /* count */ 3, /* index type */ GL_UNSIGNED_INT, /* offset */ sizeof(unsigned int) * 6, /* base vertex */ 4); 
// dont les sommets se trouvent a partir du 4ieme sommet dans le vertex buffer...
\endcode

__bonus :__ il y a une autre solution, qui utilise toujours glDrawElements() mais qui demande un peu plus de préparation... que se passe-t-il si l'index buffer contient :

`012023 + 456`


## et les autres ?

les glDrawInstancedBaseInstance( ) permettent de faire la même chose avec les buffers d'attributs d'instances.

l'autre draw interressant et plus souple que draw instanced s'appelle glMultiDraw( ) et surtout glMultiDrawIndirect( ), mais il n'est utilisable qu'à partir d'openGL 4.3.

todo 

*/
