
/*! \addtogroup tuto4GL configurer un format de sommet, vertex array object

cf \ref tuto4GL.cpp

pour dessiner des triangles, il faut décrire les informations associées aux sommets, indiquer ou les trouver, leur organisation
mémoire, et indiquer à quelles entrées du vertex shader elles sont associées.

le cas simple permettant de dessiner quelques triangles est présenté dans \ref tuto3GL.

\section vao vertex array object

c'est un objet openGL, appelé vertex array object, qui stocke la description du format de sommets. 

la création des objets openGL utilise des fonctions de la forme glGenXXX( int n, GLuint *names ). cette famille de fonctions
permet de créer plusieurs objets en même temps et renvoye un tableau d'identifiants des nouveaux objets. pour en créer 
un seul, on peut utiliser :
\code
GLuint vao;
glGenVertexArrays(1, &vao);
\endcode

il ne reste plus qu'à le sélectionner pour configurer le pipeline :
\code
glBindVertexArray(vao);
\endcode

les informations des sommets, les attributs, sont stockées dans un ou plusieurs vertex buffers, qui ne sont que des 
tableaux alloués dans la mémoire de la carte graphique. cf \ref buffers pour les créer et leur affecter des données.

le vertex array object stocke, pour chaque attribut déclaré par le vertex shader :
	- le type de l'attribut, float, int, etc, paramètre type, GL_FLOAT, GL_INTEGER, etc.
	- le nombre de composants de l'attribut, 1 pour une valeur scalaire, ou 2, 3, 4 pour un vecteur, paramètre size,
	- l'identifiant du buffer dans lequel se trouve les valeurs, implicite, c'est le buffer sélectionné sur 
	GL_ARRAY_BUFFER, cf \ref buffers,
	- la position de la première valeur dans le buffer, paramètre offset,
	- la distance entre chaque valeur dans le buffer, paramètre stride.

l'indexation des sommets (l'index buffer) peut aussi être associé à un vertex array objet.

la configuration se fait en plusieurs étapes :
	- récupérer l'identifiant de l'attribut dans le vertex shader, cf glGetAttribLocation( ),
	- sélectionner le vertex buffer sur GL_ARRAY_BUFFER,
	- apeller glVertexAttribPointer( ),
	- utiliser l'attribut, cf glEnableVertexAttribArray( )
	
exemple : le cas classique, le vertex buffer, identifiant buffer, contient les positions des sommets. 
le vertex shader déclare : `in vec3 position;`
\code
	// selectionner le vertex array object a configurer, si necessaire
	// glBindVertexArray(vao);
	
	// recuperer l'identifiant de l'attribut declare dans le vertex shader
	GLint attribute= glGetAttribLocation(program, "position");
	if(attribute < 0)
		// probleme, l'attribut n'existe pas... erreur de compilation / link du program ?
	
	// selectionner le vertex buffer contenant les donnees
	glBindBuffer(GL_ARRAY_BUFFER, buffer);
	
	// configurer l'attribut
	glVertexAttribPointer(attribute, 
		3, GL_FLOAT,	// size et type, position est un vec3 dans le vertex shader
		GL_FALSE,		// pas de normalisation des valeurs
		0,				// stride 0, les valeurs sont les unes a la suite des autres
		0				// offset 0, les valeurs sont au debut du buffer
	);
	glEnableVertexAttribArray(attribute);
\endcode

exemple : si le maillage est décrit par des sommets partagés, l'index buffer stocke des triplets d'indices pour décrire chaque triangle. 
Les opérations sont les mêmes que pour un vertex buffer, par contre, il faut sélectionner le buffer sur GL_ELEMENT_ARRAY_BUFFER :

\code
	// selectionner le vertex array object a configurer, si necessaire
	// glBindVertexArray(vao);

	// selectionner l'index buffer et configurer le vao
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
\endcode


__remarque : nettoyage / revenir à l'état par défaut__

il y a en général un index buffer et un vertex buffer sélectionné ainsi que le vao. quelle est la bonne manière de les désélectionner :
	- réponse A :  
	\code
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
	\endcode
	- réponse B :
	\code
	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	\endcode
	
_rappel :_ certains paramètres sont implicites... le vao en cours de configuration, par exemple

## et avec plusieurs attributs ?

il faut décrire chaque attribut déclaré. 

exemple : position_buffer contient les positions, normal_buffer contient les normales des sommets,
et les attributs sont déclarés comme : `in vec3 position; in vec3 normal;` dans le vertex shader :
\code
	// selectionner le vertex array object a configurer, si necessaire
	// glBindVertexArray(vao);
	
	// recuperer l'attribut
	GLint position_attribute= glGetAttribLocation(program, "position");
	glBindBuffer(GL_ARRAY_BUFFER, position_buffer);
	glVertexAttribPointer(position_attribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(position_attribute);
	
	// recuperer l'attribut
	GLint normal_attribute= glGetAttribLocation(program, "normal");
	glBindBuffer(GL_ARRAY_BUFFER, normal_buffer);
	glVertexAttribPointer(normal_attribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(normal_attribute);
\endcode

exemple complet : \ref tuto4GL_normals.cpp

pour récupérer la liste des attributs déclarés par le vertex shader et vérifier qu'ils sont bien associés à un buffer, 
cf \ref shader_reflect

## et si un attribut n'existe plus dans le vertex shader ?? ( glGetAttribLocation( ) < 0 )

le compilateur et le linker des shaders sont capables de déterminer qu'un attribut n'est pas nécessaire pour exécuter un shader. par exemple :

\code
// vertex shader
in vec3 position;
in vec3 normal;

uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;

// normal est un attribut du vertex shader, il n'est pas disponible dans le fragment shader, donc :
out vec3 view_normal;
// il faut declarer un varying, un resultat du vertex shader

void main( )
{
    gl_Position= mvpMatrix * vec4(position, 1);
    view_normal= mat3(mvMatrix) * normal;   // uniquement une rotation, mat3 suffit 
    
}
\endcode

\code
// recupere la normale calculee par le vertex shader, meme type, meme nom, mais in au lieu de out
in vec3 view_normal;
// rappel: interpolation en fonction de la position du fragment dans le triangle

out vec4 fragment_color;

void main( )
{
    fragment_color= vec4(color, 1);
}
#endif
\endcode

la couleur des fragments est constante, elle ne depend pas de la normale calculée par le vertex shader, et glGetAttibLocation( ... "normal" ) renvoie -1 
dans ce cas, alors que l'attribut normal est correctement déclaré. 

si le fragment shader est modifié pour utiliser la normale :

\code
in vec3 view_normal;
out vec4 fragment_color;

void main( )
{
    fragment_color= vec4(color * normalize(view_normal).z, 1);
}
#endif
\endcode

glGetAttribLocation( ... "normal" ) renverra bien un identifiant valide dans ce cas. il faut prévoir les 2 cas dans le programme, il y a 2 manières de le gérer,
	- soit faire le test, et ne configurer le vertex array object que si l'attribut est utilisé / nécessaire
		\code
		// recuperer l'attribut
		GLint attribute= glGetAttribLocation(program, "normal");
		if(attribute >= 0)
		{
			glBindBuffer(GL_ARRAY_BUFFER, buffer);
			glVertexAttribPointer(attribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
			glEnableVertexAttribArray(attribute);
		}
		\endcode
	
	- soit ne pas faire le test et toujours configurer le vertex array, le shader n'utilisera que ce qui est nécessaire et/ou openGL ignore les attributs non valides...
		\code
		// recuperer l'attribut
		GLint attribute= glGetAttribLocation(program, "normal");
		glBindBuffer(GL_ARRAY_BUFFER, buffer);
		glVertexAttribPointer(attribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
		glEnableVertexAttribArray(attribute);
		\endcode
	
_remarque :_ les attributs peuvent être numérotés directement par le vertex shader, et dans ce cas l'utilisation de glGetAttribLocation( ) n'est pas 
nécessaire, cf la section \ref location.

## et si les attributs sont dans le même vertex buffer ?

les paramètres stride et offset de glVertexAttribPointer( ) permettent d'organiser les données assez librement :
	- offset donne la position de la première valeur dans le buffer (en octets),
	- stride donne la distance à la laquelle se trouve la valeur suivante (en octets),
ce qui permet d'itérer sur tous les attributs sans problèmes.

_remarque :_ stride = 0 est interprété comme l'organisation par défaut, les valeurs se suivent, 
	c'est équivalent à stride= sizeof()

exemple : position et normale dans le même buffer, toutes les positions, puis toutes les normales : `PPPPPPPPNNNNNNNN`
\code
	offset(position)= 0;
	stride(position)= 0;		// par definition stride(position) == sizeof(vec3)
	
	offset(normal)= sizeof(vec3) * positions.size();
	stride(normal)= 0;			// ou stride(normal) == sizeof(vec3)
\endcode

exemple : alterner les donnees, position + normale sommet 0, position + normale sommet 1, etc : `PNPNPNPNPNPNPN`
\code    
	offset(position)= 0;
	stride(position)= sizeof(vec3) + sizeof(vec3);  // la prochaine position se trouve apres la normale du sommet
	
	offset(normal)= sizeof(vec3);					// la premiere normale se trouve apres la premiere position
	stride(normal)= sizeof(vec3) + sizeof(vec3);
\endcode

cf \ref single_buffer pour un exemple complet de création.

\section buffers vertex buffers et index buffer

les buffers sont des tableaux alloués dans la mémoire de la carte graphique, il faut leur donner une dimension (en octets) 
et transférer des données, cf glBufferData( ). 

on peut également modifier le contenu d'un buffer à n'importe quel moment, cf glBufferSubData( ). en fonction de la fréquence 
de modifications, il faut choisir l'option correspondante lors de la création, cf le paramètre usage de glBufferData( ). l'option 
par défaut est GL_STATIC_DRAW qui indique que le buffer est utilisé par glDraw( ) et que son contenu ne devrait pas changer.

mais bien sur, il faut commencer par créer l'objet openGL, cf glGenBuffers( ) :
\code
GLuint buffer;
glGenBuffers(1, &buffer);
\endcode

pour pouvoir manipuler un buffer, il faut le sélectionner, comme les autres objets openGL, cf glBindBuffer( ) :
	- les vertex buffers sont sélectionnés sur GL_ARRAY_BUFFER,
	- les index buffers, sur GL_ELEMENT_ARRAY_BUFFER.

exemple : créer un vertex buffer pour stocker les positions d'un mesh, cf Mesh::vertex_buffer() et Mesh::vertex_buffer_size(), 
pour récupérer les infos nécessaires :
\code
	Mesh mesh= ... ;

	// cree un nouveau buffer
	GLuint buffer;
	glGenBuffers(1, &buffer);
	
	// selectionner le buffer comme un vertex buffer
	glBindBuffer(GL_ARRAY_BUFFER, buffer);
	
	// dimensionner et initialiser le buffer selectionne sur array_buffer
	glBufferData(GL_ARRAY_BUFFER, mesh.vertex_buffer_size(), mesh.vertex_buffer(), GL_STATIC_DRAW);
\endcode

exemple : créer un vertex buffer, le dimensionner et changer son contenu
\code
GLuint buffer;
unsigned int buffer_size;
int init( )
{
	Mesh mesh= ... ;
	
	// cree un nouveau buffer
	glGenBuffers(1, &buffer);
	
	// selectionner le buffer comme un vertex buffer
	glBindBuffer(GL_ARRAY_BUFFER, buffer);
	
	// dimensionner le buffer selectionne sur array_buffer
	buffer_size= mesh.vertex_buffer_size();
	glBufferData(GL_ARRAY_BUFFER, buffer_size, /* data */ NULL, GL_STATIC_DRAW);
	// pas de donnees pour initialiser le contenu du buffer...
	...
}

int draw( )
{
	// calculer les valeurs a copier dans le buffer
	vec3 *data= { ... };
	
	// transferer les nouvelles valeurs
	// selectionner le buffer
	glBindBuffer(GL_ARRAY_BUFFER, buffer)
	glBufferSubData(GL_ARRAY_BUFFER, /* offset */ 0, /* length */ buffer_size, data);
	// offset et length permettent de ne modifier qu'une partie du buffer
	...
}
\endcode


\section single_buffer exemple : utiliser un seul buffer pour stocker les attributs et configurer le vao

le buffer est remplit avec toutes les positions, suivies de toutes les texcoords, et enfin toutes les normales :
\code
	Mesh mesh= read_mesh( ... );
	GLuint vao;
	GLuint buffer;

	// creer et selectionner le vao
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	// creer, initialiser le buffer : positions + normals + texcoords du mesh
	glGenBuffers(1, &buffer);
	glBindBuffer(GL_ARRAY_BUFFER, buffer);

	// taille totale du buffer
	size_t size= mesh.vertex_buffer_size() + mesh.texcoord_buffer_size() + mesh.normal_buffer_size();
	glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_STATIC_DRAW);

	// transfere les positions des sommets
	size_t offset= 0;
	size= mesh.vertex_buffer_size();
	glBufferSubData(GL_ARRAY_BUFFER, offset, size, mesh.vertex_buffer());
	// et configure l'attribut 0, vec3 position
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, /* stride */ 0, (const GLvoid *) offset);
	glEnableVertexAttribArray(0);

	// transfere les texcoords des sommets
	offset= offset + size;
	size= mesh.texcoord_buffer_size();
	glBufferSubData(GL_ARRAY_BUFFER, offset, size, mesh.texcoord_buffer());
	// et configure l'attribut 1, vec2 texcoord
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, /* stride */ 0, (const GLvoid *) offset);
	glEnableVertexAttribArray(1);

	// transfere les normales des sommets
	offset= offset + size;
	size= mesh.normal_buffer_size();
	glBufferSubData(GL_ARRAY_BUFFER, offset, size, mesh.normal_buffer());
	// et configure l'attribut 2, vec3 normal
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, /* stride */ 0, (const GLvoid *) offset);
	glEnableVertexAttribArray(2);
\endcode


\section location et sans utiliser glGetAttribLocation( ) ?

il suffit de donner l'identifiant directement dans le source du vertex shader avec `layout(location= id) in vec3 attribute;`:
\code
layout(location= 0) in vec3 position;
\endcode

l'exemple de création de buffer / configuration de vao précédent suppose que position est l'attribut 0, texcoord est l'attribut 1 et normal est l'attribut 2, 
ce qui se déclare comme ça :
\code
layout(location= 0) in vec3 position;
layout(location= 1) in vec2 texcoord;
layout(location= 2) in vec3 normal;
\endcode

et la création des buffers / configuration du vao peut etre simplifiée, puisque l'on connait les identifiants des attributs. cf l'exemple précédent \ref single_buffer.
 */
