/**
* @file perspective_gl.h
* @brief Classe d'aide à l'affichage pour les scènes Perspective3D.
* @author Florian Joncour
* @date 2013-2018
* @copyright Ces sources font partie de l'interface de programmation pour la bibliothèque Perspective3D,
un outils de modélisation 3D à partir de vues orthographiques 2D.
Ces sources sont fournies à titre gracieux, l'utilisateur a la liberté de les exploiter
pour créer des applications dérivées à condition de respecter les conditions suivantes:

    1) Ces sources ne peuvent être modifiées.
    2) Le dérivé ne peut être exploité à des fins commerciaux.
    3) Aucune licence commerciale de la bibliothèque dont est tiré ce fichier ne peut être rendue publique.
    4) Toute application exploitant la présente bibliothèque doit explicitement faire mention de l'usage et du rôle de Perspective3D.

Ces conditions peuvent toutefois être modifiées avec l'accord de l'auteur.
*/

#ifndef PERSPECTIVE_GL_H
#define PERSPECTIVE_GL_H

#define PERSPECTIVE_GL /* Active ou pas le support spécifique pour Perspective */

#ifdef PERSPECTIVE_GL
#include "API/perspective_api.h"
#endif // PERSPECTIVE_GL

#ifdef PSYS_MACOSX
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else // !PSYS_MACOSX
#include <GL/gl.h>
#include <GL/glu.h>
#ifndef GL_MULTISAMPLE /* Win 7 (MinGW) */
#define GL_MULTISAMPLE 0x809D
#endif // !GL_MULTISAMPLE
#endif // PSYS_MACOSX

#define VERSION_GL(majeur, mineur) ((majeur*100 + mineur*10))
#define VERSION_GL_STR(majeur, mineur, rev) #majeur #mineur #rev

//#ifdef DEBUG
//#ifndef FORCE_VERSION_GL
//#define FORCE_VERSION_GL VERSION_GL(3, 2)
//#define FORCE_VERSION_GLSL_STR VERSION_GL_STR(1, 5, 0)
//#endif // !FORCE_VERSION_GL
//#else
#define FORCE_VERSION_GL VERSION_GL(2, 0)
//#endif // DEBUG

#ifndef PGL_NORM_BYTE_FLOAT
#define PGL_NORM_BYTE_FLOAT(x) (double(x) * 0.00392156862745098) /* 1.0 / 255.0 */
#endif // PGL_NORM_BYTE_FLOAT

namespace PGL
{
    /**
     * @brief Modes d'affichage.
     */
    enum MODES_AFFICHAGE { MODE_SCENE=0, MODE_SOLIDE, MODE_ARRETES, MODE_FILAIRE };
    enum CONTEXTES_PROGRAM { CONTEXTE_FILAIRE=0, CONTEXTE_MODELE, CONTEXTE_POST };
    enum TYPE_MODELES { MODELE_NUL=0, MODELE_SOMMETS=1, MODELE_FILAIRE=2, MODELE_FACES=4 };

#define PGL_MODE_AFFICHAGE_DEFAUT PGL::MODE_SCENE

//#ifdef DEBUG
//#define PGL_MODE_AFFICHAGE_DEFAUT MODE_SOLIDE
//#else
//#define PGL_MODE_AFFICHAGE_DEFAUT MODE_SCENE
//#endif // DEBUG

#define ECHELLE_DEFAUT_PGL .9
#define TAILLE_ORIGINE_PGL 0.05f
#define TAILLE_ORIGINE_NEG_PGL -0.005f

#define COULEUR_FOND_GL_HEX "#1e252f"
#define COULEUR_FOND_PGL_FLOAT .117f, .145f, .184f
#define COULEUR_FOND_PGL_INT 30, 37, 47

#define GLcoord GLfloat
#define GLcoord_type GL_FLOAT

#ifndef GLchar
    typedef char GLchar;
#endif // GLchar

    namespace Extensions_GL
    {
        bool Init();
        bool ValideExtension(const char *ext);
        bool SupportShaders();
        bool SupportVBO();
        bool SupportVBA();
        bool SupportFBO();
    }

    /**
     * @brief Vertice pour OpenGL.
     */
    class PVertex_GL
    {
        public:
            inline explicit PVertex_GL() { v[0]=v[1]=v[2]=0.; }
            inline explicit PVertex_GL(GLcoord x, GLcoord y, GLcoord z) { defCoords(x, y, z); }
            inline explicit PVertex_GL(const Perspective3D::Pvec3 &p) { defCoords(p.X(), p.Y(), p.Z()); }
            inline explicit PVertex_GL(const Perspective3D::Ppoint3D_min &p) { defCoords(p.X(), p.Y(), p.Z()); }
            inline explicit PVertex_GL(const Perspective3D::Ptriangle3D &tr) { defCoords(tr.V1().X(), tr.V1().Y(), tr.V1().Z()); }

            inline PVertex_GL &operator = (const Perspective3D::Pvec3 &p) { defCoords(p.X(), p.Y(), p.Z()); return *this; }

            inline PVertex_GL operator * (const GLcoord s) const { return PVertex_GL(v[0]*s, v[1]*s, v[2]*s); }
            inline PVertex_GL operator / (const GLcoord s) const { return PVertex_GL(v[0]/s, v[1]/s, v[2]/s); }
            inline PVertex_GL operator + (const GLcoord s) const { return PVertex_GL(v[0]+s, v[1]+s, v[2]+s); }
            inline PVertex_GL operator - (const GLcoord s) const { return PVertex_GL(v[0]-s, v[1]-s, v[2]-s); }

            inline PVertex_GL operator * (const PVertex_GL &s) const { return PVertex_GL(v[0]*s.v[0], v[1]*s.v[1], v[2]*s.v[2]); }
            inline PVertex_GL operator / (const PVertex_GL &s) const { return PVertex_GL(v[0]/s.v[0], v[1]/s.v[1], v[2]/s.v[2]); }
            inline PVertex_GL operator + (const PVertex_GL &s) const { return PVertex_GL(v[0]+s.v[0], v[1]+s.v[1], v[2]+s.v[2]); }
            inline PVertex_GL operator - (const PVertex_GL &s) const { return PVertex_GL(v[0]-s.v[0], v[1]-s.v[1], v[2]-s.v[2]); }

            inline operator Perspective3D::Pvec3() const { return Perspective3D::Pvec3(v[0], v[1], v[2]); }
            inline operator Perspective3D::Ppoint3D_min() const { return Perspective3D::Ppoint3D_min(v[0], v[1], v[2]); }

            inline void operator *= (const GLcoord s) { v[0]*=s; v[1]*=s; v[2]*=s; }
            inline void operator += (const GLcoord s) { v[0]+=s; v[1]+=s; v[2]+=s; }

            inline void defCoords(GLcoord x, GLcoord y, GLcoord z) { v[0]=x; v[1]=y; v[2] = z; }
            inline void defX(GLcoord x) { v[0] = x; }
            inline void defY(GLcoord y) { v[1] = y; }
            inline void defZ(GLcoord z) { v[2] = z; }

            inline GLcoord &refX() { return v[0]; }
            inline GLcoord &refY() { return v[1]; }
            inline GLcoord &refZ() { return v[2]; }

            inline GLcoord Distance2(const PVertex_GL &p) const { return ( POW2(POSITIFD(v[0] - p.v[0])) + POW2(POSITIFD(v[1] - p.v[1])) + POW2(POSITIFD(v[2] - p.v[2])) ); }
            inline GLcoord Distance (const PVertex_GL &p) const { return RCARREF(Distance2(p)); }

            inline PVertex_GL centre(const PVertex_GL &p) const
            {
                return PVertex_GL(((v[0]+p.v[0]) * 0.5), ((v[1]+p.v[1]) * 0.5), ((v[2]+p.v[2]) * 0.5));
            }

            PVertex_GL& Normalise();
            /* Normalise le vecteur */

            const GLcoord *Tableau() const { return v; }

            inline GLcoord X() const { return v[0]; }
            inline GLcoord Y() const { return v[1]; }
            inline GLcoord Z() const { return v[2]; }

            static puint NombreVertices() { return 1; }
            static puint Taille() { return 3; }
        private:
            GLcoord v[3];
    };
    typedef std::vector<PVertex_GL> PVectVert_GL;

    /**
     * @brief Segment pour OpenGL (deux vertices).
     */
    class PVertice2_GL
    {
        public:
            inline explicit PVertice2_GL() { v[0]=v[1]=PVertex_GL(); }
            inline explicit PVertice2_GL(const PVertex_GL &vt1, const PVertex_GL &vt2) { v[0]=vt1; v[1]=vt2; }
            inline explicit PVertice2_GL(const Perspective3D::Pvec3 &p1, const Perspective3D::Pvec3 &p2) { v[0]=p1; v[1]=p2; }
            inline explicit PVertice2_GL(const Perspective3D::Ptriangle3D &tr) { v[0]=tr.V1(); v[1]=tr.V2(); }
            inline const PVertex_GL &V1() const { return v[0]; }
            inline const PVertex_GL &V2() const { return v[1]; }

            static puint NombreVertices() { return 2; }
            static puint Taille() { return 6; }
        private:
            PVertex_GL v[2];
    };

    /**
     * @brief Triangle pour OpenGL (trois vertices).
     */
    class PVertice3_GL
    {
        public:
            inline explicit PVertice3_GL() { v[0]=v[1]=v[2]=PVertex_GL(); }
            inline explicit PVertice3_GL(const PVertex_GL &vt1, const PVertex_GL &vt2, const PVertex_GL &vt3) { v[0]=vt1; v[1]=vt2; v[2]=vt3; }
            inline explicit PVertice3_GL(const Perspective3D::Pvec3 &p1, const Perspective3D::Pvec3 &p2, const Perspective3D::Pvec3 &p3) { v[0]=p1; v[1]=p2; v[2]=p3; }
            inline explicit PVertice3_GL(const Perspective3D::Ptriangle3D &tr) { v[0]=tr.V1(); v[1]=tr.V2(); v[2]=tr.V3(); }
            inline const PVertex_GL &V1() const { return v[0]; }
            inline const PVertex_GL &V2() const { return v[1]; }
            inline const PVertex_GL &V3() const { return v[2]; }

            PVertex_GL VecteurNormal() const;
            /* Calcul du vecteur normal. */

            static puint NombreVertices() { return 3; }
            static puint Taille() { return 9; }
        private:
            PVertex_GL v[3];
    };

    /**
     * @brief Couleur 32 bits pour OpenGL.
     */
    class PCouleur_GL
    {
        public:
            inline explicit PCouleur_GL() { vc[0]=vc[1]=vc[2]=0; vc[3]=1.0; }
            inline explicit PCouleur_GL(GLcoord r, GLcoord v, GLcoord b, GLcoord alpha=1.0) { defRVBA(r, v, b, alpha); }
            inline explicit PCouleur_GL(const Perspective3D::PCouleur &c, GLubyte alpha=255) { defCouleur(c, alpha); }

            inline void defCouleur(const PCouleur_GL &c) { vc[0]=c.vc[0]; vc[1]=c.vc[1]; vc[2]=c.vc[2]; vc[3]=c.vc[3]; }
            inline void defCouleur(const Perspective3D::PCouleur &c, GLubyte alpha) { vc[0]=PGL_NORM_BYTE_FLOAT(c.R()); vc[1]=PGL_NORM_BYTE_FLOAT(c.V()); vc[2]=PGL_NORM_BYTE_FLOAT(c.B()); vc[3]=PGL_NORM_BYTE_FLOAT(alpha); }
            inline void defCouleur(const Perspective3D::PCouleur &c) { vc[0]=PGL_NORM_BYTE_FLOAT(c.R()); vc[1]=PGL_NORM_BYTE_FLOAT(c.V()); vc[2]=PGL_NORM_BYTE_FLOAT(c.B()); vc[3]=PGL_NORM_BYTE_FLOAT(c.Alpha()); }
            inline void defCouleur(const Perspective3D::PCouleur_RAL &c) { vc[0]=PGL_NORM_BYTE_FLOAT(c.r); vc[1]=PGL_NORM_BYTE_FLOAT(c.v); vc[2]=PGL_NORM_BYTE_FLOAT(c.b); vc[3]=1.0; }

            inline void defRVB(GLcoord r, GLcoord v, GLcoord b) { vc[0]=r; vc[1]=v; vc[2]=b; }
            inline void defRVBA(GLcoord r, GLcoord v, GLcoord b, GLcoord alpha=1.0) { vc[0]=r; vc[1]=v; vc[2]=b; vc[3]=alpha; }
            inline void defAlpha(GLcoord alpha) { vc[3] = alpha; }

            inline GLcoord R() const { return vc[0]; }
            inline GLcoord V() const { return vc[1]; }
            inline GLcoord B() const { return vc[2]; }
            inline GLcoord Alpha() const { return vc[3]; }
            const GLcoord *Tableau() const { return vc; }
            static puint Taille() { return 4; }

            inline bool operator==(const PCouleur_GL& cv) const
            {
                return ((vc[0] == cv.vc[0]) && (vc[1] == cv.vc[1]) && (vc[2] == cv.vc[2]) && (vc[3] == cv.vc[3]));
            }

            inline bool operator!=(const PCouleur_GL& cv) const
            {
                return ((vc[0] != cv.vc[0]) || (vc[1] != cv.vc[1]) || (vc[2] != cv.vc[2]) || (vc[3] != cv.vc[3]));
            }

            PCouleur_GL Div() const; /* Couleur divergeante */
            PCouleur_GL Div2() const; /* Couleur divergeante 2 (restauration par rapport à la première fonction) */

        private:
            GLcoord vc[4];
    };
    typedef std::vector<PCouleur_GL> PVectCouleur_GL;

    /**
     * @brief Matrice (minimale) compatible OpenGL.
     */
    class PMatriceGL4
    {
        public:
            explicit PMatriceGL4();

            explicit PMatriceGL4(GLcoord m00, GLcoord m01, GLcoord m02, GLcoord m03,         // 1ère colonne
                        GLcoord m04, GLcoord m05, GLcoord m06, GLcoord m07,  // 2ème colonne
                        GLcoord m08, GLcoord m09, GLcoord m10, GLcoord m11,  // 3ème colonne
                        GLcoord m12, GLcoord m13, GLcoord m14, GLcoord m15); // 4ème colonne

            explicit PMatriceGL4(const GLcoord m[16]);

            void Copie(float *m) const;
            void Copie(double *m) const;

            void Identite();

            void defOrtho(GLcoord gauche, GLcoord droite, GLcoord bas, GLcoord haut, GLcoord proche, GLcoord loin); /* Similaire à glOrtho */

            void Inverse();

            void Rotation(GLcoord angle, GLcoord x, GLcoord y, GLcoord z);

            inline void Rotation(const GLcoord angle, const PVertex_GL &v)
            {
                Rotation(angle, v.X(), v.Y(), v.Z());
            }

            void Translation(GLcoord x, GLcoord y, GLcoord z);

            inline void Translation(const PVertex_GL &v)
            {
                Translation(v.X(), v.Y(), v.Z());
            }

            void Position(GLcoord x, GLcoord y, GLcoord z);

            inline void Position(const PVertex_GL &v)
            {
                Position(v.X(), v.Y(), v.Z());
            }

            void Echelle(GLcoord n);
            void Echelle (GLcoord x, GLcoord y, GLcoord z);

            void RotationX(GLcoord angle);
            void RotationY(GLcoord angle);
            void RotationZ(GLcoord angle);

            void RotationXYZ(GLcoord angle_x, GLcoord angle_y, GLcoord angle_z);

            void Transpose();

            void defAxeX(GLcoord x, GLcoord y, GLcoord z);
            void defAxeY(GLcoord x, GLcoord y, GLcoord z);
            void defAxeZ(GLcoord x, GLcoord y, GLcoord z);
            void defOrigine(GLcoord x, GLcoord y, GLcoord z);

            PVertex_GL AxeX() const;
            PVertex_GL AxeY() const;
            PVertex_GL AxeZ() const;
            PVertex_GL Origine() const;

            PMatriceGL4 operator*(const PMatriceGL4 &m) const;
            PVertex_GL operator*(const PVertex_GL &p) const;

            GLcoord CalcW(const PVertex_GL &p) const;

            inline const GLcoord *Tableau() const
            {
                return matrice;
            }

            inline GLcoord *TableauNonConst()
            {
                return matrice;
            }

            static puint Taille() { return 16; }

        protected:
            static GLcoord facteur(GLcoord m0, GLcoord m1, GLcoord m2, GLcoord m3, GLcoord m4, GLcoord m5, GLcoord m6, GLcoord m7, GLcoord m8);

        private:
            GLcoord matrice[16];
    };

    /**
     * @brief Shader OpenGL.
     */
    class Shader_GL
    {
        public:
            explicit Shader_GL(GLenum type);
            ~Shader_GL();

            bool Reactive();
            bool Desactive();

            bool ChargeSource(const char *s);
            /* Charge et compile la source. */

            GLuint Shader() const { return shader; }

            inline bool ValideSupport() const { return shader != 0; }
            inline bool ValideCompilation() const { return valide_compilation; }
            inline const std::string &JournalCompilation() const { return journal_comp; }

        private:
            GLuint type_shader;
            GLuint shader;
            bool valide_compilation;
            std::string journal_comp;
    };

    /**
     * @brief Program OpenGL.
     */
    class Program_GL
    {
        public:
            explicit Program_GL();
            ~Program_GL();

            bool Reactive();
            bool Desactive();

            bool AjoutVertexShader(const char *source);
            bool AjoutPixelShader(const char *source);

            bool AjoutVertexShaderFichier(const char *chemin);
            bool AjoutPixelShaderFichier(const char *chemin);

            bool LinkProgram();
            /* Lie le program au processus. */

            bool DumpBinaire(const std::string  &chemin, pint type=1) const;
            /* Dump mémoire du program avec ses shaders compilés. */

            inline GLuint IdProgram() const { return program; }
            static void DesactivePrograms();

            inline bool ValideSupport() const { return program != 0; }
            inline bool ValideLiage() const { return ValideSupport() && valide_liage; }

            bool UseProgram() const;

            GLint ReservationUniform(const char *nom) const;
            bool ActivationUniformMatrix(GLint id, const PMatriceGL4 &m) const;
            bool ActivationUniformVect(GLint id, const PVertex_GL &v) const;
            bool ActivationUniformInt(GLint id, int v) const;
            bool ActivationUniformFloat(GLint id, float v) const;

            GLint ReservationAttribut(const char *nom) const;
            bool ActivationTableauAttributs2F(GLint index, const GLvoid *t) const;
            bool ActivationTableauAttributs3F(GLint index, const GLvoid *t) const;
            bool ActivationTableauAttributs4F(GLint index, const GLvoid *t) const;
            bool ActivationTableauAttributs3B(GLint index, const GLvoid *t) const;
            bool DesactiveAttribut(GLint index) const;


            inline bool ValideCompilationVertexShader() const { return vertex_shader.ValideCompilation(); }
            inline bool ValideCompilationPixelShader() const { return pixel_shader.ValideCompilation(); }
            inline const std::string &JournalCompilationVertexShader() const { return vertex_shader.JournalCompilation(); }
            inline const std::string &JournalCompilationPixelShader() const { return pixel_shader.JournalCompilation(); }
            inline const std::string &JournalLiageShaders() const { return journal_liage; }

        protected:
            char * ChargeFichier(const char *chemin);

        private:
            GLuint program;
            Shader_GL vertex_shader;
            Shader_GL pixel_shader;
            std::string journal_liage;
            bool valide_liage;
    };

    /**
     * @brief Liaison uniform C++ -> GLSL.
     */
    class Uniform_GL
    {
        public:
            explicit Uniform_GL(const Program_GL &prog);

            inline GLint IdUniform() const { return uniform; }

            bool Valide() const;

            bool Reservation(const char *nom);
            /* Réservation du mot et liage. */

            bool Desactive();

            /* Méthodes d'activation entre le paramètre uniforme et les données: */
            bool Activation(const PMatriceGL4 &matice) const;
            bool Activation(const PVertex_GL &vertex) const;
            bool Activation(float f) const;
            bool Activation(int i) const;

        private:
            const Program_GL &program;
            GLint uniform;
    };

    class Attribute_GL
    {
        public:
            explicit Attribute_GL(const Program_GL &prog);

            inline GLint IdAttribute() const { return attribute; }

            bool Valide() const;

            bool Desactive() const;

            bool Reservation(const char *nom);
            /* Réservation du mot et liage. */

            /* Méthodes d'activation entre l'attribut et les données: */
            bool Activation(const PVectVert_GL &vertices) const;
            bool Activation(const PVectCouleur_GL &couleurs) const;

        private:
            const Program_GL &program;
            GLint attribute;
    };

    /**
     * @brief Gère un tampon mémoire à destination d'OpenGL. Passera par un VertexArray ou un VBO en fonction du système.
     */
    class PTampon_GL
    {
        public:
            /**
             * @brief PTampon_GL
             * @param type_entites GL_POINTS, GL_LINES ou GL_TRIANGLES.
             */
            explicit PTampon_GL(GLint type_entites);
            ~PTampon_GL();

            GLint IdBuffer() const { return id_vbo; }

            bool ActiveVBO(GLenum type_usage_tampon); // GL_STATIC_DRAW, GL_DYNAMIC_DRAW
            bool VBOActif() const;
            bool Bind() const;
            void UnBind() const;
            bool DesactiveVBO();

            void Reinit(bool desactive_vbo=true);

            bool Ajout(const PVertex_GL &v, const PCouleur_GL &c);

            bool Ajout(const PVertex_GL &v1, const PVertex_GL &v2, const PCouleur_GL &c);
            bool Ajout(const PVertice2_GL &v, const PCouleur_GL &c);

            bool Ajout(const PVertex_GL &v1, const PVertex_GL &v2, const PVertex_GL &v3, const PVertex_GL &norm, const PCouleur_GL &c);
            bool Ajout(const PVertex_GL &v1, const PVertex_GL &v2, const PVertex_GL &v3, const PVertex_GL &norm1, const PVertex_GL &norm2, const PVertex_GL &norm3, const PCouleur_GL &c);
            bool Ajout(const PVertice3_GL &v, const PVertex_GL &norm, const PCouleur_GL &c);
            bool Ajout(const PVertice3_GL &v, const PCouleur_GL &c);

            inline puint Taille() const
            {
                return ( (type_ents == GL_TRIANGLES) ? PVertice3_GL::NombreVertices() : ((type_ents == GL_LINES) ? PVertice2_GL::NombreVertices() : (type_ents == GL_POINTS ? PVertex_GL::NombreVertices() : 0)) );
            }

            inline PVectVert_GL& Vertices()
            {
                return tampon_vertices;
            }

            inline const PVectVert_GL& VerticesConst() const
            {
                return tampon_vertices;
            }

            inline PVectVert_GL& Norms()
            {
                return tampon_norms;
            }

            inline const PVectVert_GL& NormsConst() const
            {
                return tampon_norms;
            }

            inline PVectCouleur_GL& Couleurs()
            {
                return tampon_couleurs;
            }

            inline const PVectCouleur_GL& CouleursConst() const
            {
                return tampon_couleurs;
            }

            /**
             * @brief Transfert des données sur la carte vidéo.
             */
            bool Transfert() const;

            /**
             * @brief Affichage
             */
            void Affichage() const;

            /**
             * @brief Affichage avec liaison des vertices à un attribut GLSL.
             */
            void Affichage(const Attribute_GL &attribute_vertices) const;

            /**
             * @brief Affichage avec liaison des vertices et des normals à des attributs GLSL (en principe utilisé pour l'affichage des triangles).
             */
            void Affichage(const Attribute_GL &attribute_vertices, const Attribute_GL &attribute_normals) const;

            /**
             * @brief Affichage avec liaison des vertices à un attribut GLSL.
             */
            void Affichage3(const Attribute_GL &attribute_vertices, const Attribute_GL &attribute_color) const;

            /**
             * @brief Affichage avec liaison des vertices et des normals à des attributs GLSL (en principe utilisé pour l'affichage des triangles).
             */
            void Affichage3(const Attribute_GL &attribute_vertices, const Attribute_GL &attribute_normals, const Attribute_GL &attribute_color) const;

        protected:
            GLint type_ents;
            bool utilise_vbo;
            bool utilise_vba;
            GLuint id_vbo;
            GLenum target_vbo;
            GLenum usage_vbo;

            PVectVert_GL tampon_vertices;
            PVectCouleur_GL tampon_couleurs;
            PVectVert_GL tampon_norms;
    };

    /**
     * @brief Tampon GL de flottants.
     */
    class PTamponF_GL
    {
        public:
            explicit PTamponF_GL();
            ~PTamponF_GL();

            GLint IdBuffer() const { return id_vbo; }

            bool ValideSupport() const;
            bool Valide(bool controle_gl=false) const;

            bool Active();
            bool Desactive();
            bool Reactive();

            bool Bind() const;
            void UnBind() const;

            bool Transfert(const GLcoord *v, puint n) const;

        protected:
            GLuint id_vbo;
    };

    class PTexture_GL
    {
        public:
            explicit PTexture_GL(bool linear=false, bool repeat=false);
            ~PTexture_GL();

            inline GLuint IdTexture() const { return id_texture; }

            bool Valide(bool controle_gl=false) const;

            bool Desactive();
            bool Reactive();

            bool Bind() const;
            void UnBind() const;

            bool TexImage2D_RGB(GLuint width, GLuint height, const char *data) const;
            bool TexImage2D_RGBA(GLuint width, GLuint height, const char *data) const;

            bool TexImage2D_BGR(GLuint width, GLuint height, const char *data) const;
            bool TexImage2D_BGRA(GLuint width, GLuint height, const char *data) const;

        protected:
            bool TexImage2D(GLuint width, GLuint height, const char *data, bool rgba=true, bool inverse_rgb=false) const;
            bool TexParameter() const;

            GLuint id_texture;
            bool gl_linear;
            bool gl_repeat;
    };

    class PRenderBuffer_GL
    {
        public:
            explicit PRenderBuffer_GL();
            ~PRenderBuffer_GL();

            inline GLuint IdRenderBuffer() const { return id_render_buffer; }

            bool Valide(bool controle_gl=false) const;

            bool Desactive();
            bool Reactive();

            bool Bind() const;
            void UnBind() const;

            bool Transfert(GLsizei screen_width, GLsizei screen_height) const;

        protected:
            GLuint id_render_buffer;
    };

    /**
     * @brief Gestionnaire de shaders GL.
     */
    class Shaders_GL
    {
        public:
            explicit Shaders_GL();
            ~Shaders_GL();

            static const char* VShaderDefaut();
            static const char* FShaderDefaut();

            inline const Program_GL &Program() const { return program; }

            bool InitialisationShader(const char *source_vertex_shader, const char *source_pixel_shader);
            inline const std::string &JournalCompilationShaders() const { return journal_compilation_shaders; }

            bool DumpShaderBinaire(const std::string &chemin) const;

            inline bool ValideSupport() const { return program.ValideSupport(); }
            inline bool ValideLiage() const { return program.ValideLiage(); }

            bool Desactive();
            bool Activation(const PMatriceGL4 &matrice_affichage, const PMatriceGL4 &matrice_projections);
            bool Activation(const PMatriceGL4 &matrice_affichage, const PMatriceGL4 &matrice_projections, const PMatriceGL4 &matrice_normals);
            bool Activation(const PMatriceGL4 &matrice_affichage, const PMatriceGL4 &matrice_projections, const PMatriceGL4 &matrice_normals, const PVertex_GL &position_lumiere);
            bool Reactive();

            bool Affichage(const PTampon_GL &tampon) const;

        protected:
            inline void BloqueProgram() { bloque_program = true; }
            inline void ActiveProgram() { bloque_program = false; }
            inline bool ExecProgram() const { return !bloque_program; }

            bool AssignationAttributsShaderModele();
            bool DesactivationAttributsShadersModele();

            bool bloque_program;
            Program_GL program;

            Uniform_GL uniform_matrice_affichage;
            Uniform_GL uniform_matrice_projections;
            Uniform_GL uniform_matrice_normals;
            Uniform_GL uniform_vec_light0;
            Attribute_GL attribute_vertex;
            Attribute_GL attribute_normals;
#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
            Attribute_GL attribute_color;
#endif // FORCE_VERSION_GL
            std::string journal_compilation_shaders;
    };

    /**
     * @brief Shader de post-traitement (appliqué sur un FrameBuffer).
     */
    class PFrameBuffer_GL
    {
        public:
            explicit PFrameBuffer_GL();
            ~PFrameBuffer_GL();

            static const char *VShaderDefaut();
            static const char *FShaderDefaut();

            void Desactive();
            void DesactiveProgram();

            bool Gen(puint taille_ecran_x, puint taille_ecran_y);
            bool InitialisationShaders(const char * vshader, const char * pshader);

            bool DumpShaderBinaire(const std::string &chemin) const;

            bool SauvegardeFrameBufferInitial();
            bool RestaureFrameBufferInitial() const;

            inline GLuint IdBuffer() const { return fbo; }
            inline const PRenderBuffer_GL& Render() const { return render; }
            inline const PTexture_GL& Texture() const { return texture; }

            bool Valide() const;
            bool Bind();
            void UnBind() const;

            void defTailleEcran(puint x, puint y);

            void defCouleurFond(const PCouleur_GL &couleur);
            bool Affichage();

            bool ValideLiageShaders() const;
            inline const std::string &JournalCompilationShaders() const { return journal_compilation_shaders; }

            inline bool UtiliseChronoShaderPost() const { return utilise_chrono; }

        private:
            inline void BloqueProgram() { bloque_program = true; }
            inline void ActiveProgram() { bloque_program = false; }
            inline bool ExecProgram() const { return !bloque_program; }

        protected:
#ifdef DEBUG
            bool checkFramebufferStatus() const;
#endif // DEBUG

            GLuint framebuffer_0;
            GLuint fbo;
            GLuint vbo_fbo_vertices;
            GLuint vbo_fbo_colors;
            PTexture_GL texture;
            PRenderBuffer_GL render;
            bool bloque_program;

            GLsizei resolution_x;
            GLsizei resolution_y;

            GLfloat chrono;
            bool utilise_chrono;

            GLfloat couleur_fond[4];
            GLint uniform_fbo_texture;
            GLint uniform_fbo_couleur_fond;
            GLint uniform_fbo_chrono;
            GLint uniform_fbo_resolution;
            GLint attribute_v_coord_postproc;
            Program_GL program;
            std::string journal_compilation_shaders;
    };

    struct Params_GL
    {
            Params_GL(float ep_points_=8.f, float ep_lignes_=2.f, bool eclairage_dyn_=true, bool eclairage_uni_=false, bool mode_ortho_=false, bool active_shaders_=false, bool active_fbo_=false);

            float ep_points;
            float ep_lignes;
            bool eclairage_dyn;
            bool eclairage_uni;
            bool mode_ortho;
            bool active_shaders;
            bool active_fbo;

            PCouleur_GL couleur_fond;
    };

    /**
     * @brief Moteur d'affichage de base. Il est possible d'hériter de cet objet pour ajouter de nouveaux tampons d'objets à afficher.
     */
    class Moteur_GL
    {
        public:
            explicit Moteur_GL(const Params_GL &params);
            ~Moteur_GL();

            void VideDiv();

            /**
             * @brief Initialisation de l'affichage. Ne doit en principe être appelé qu'une fois, avant l'affichage.
             */
            void InitialisationGL();

            /**
             * @brief Réinitialisation de l'état de l'affichage (peut être appelé avant chaque passe d'affichage pour s'assurer que l'état GL soit valide).
             */
            void ReinitEtatGL();

            /**
             * @brief Initialisation d'une nouvelle d'un nouvel affichage.
             */
            void InitAffichage(bool affiche_origine, bool autorise_post_traitement=true);

            /**
             * @brief Finalise l'affichage (exécution du post-traitement).
             */
            void FinAffichage();

            /**
             * @brief ExecShaderPost Exécution du shader de post-traitement
             */
            bool ExecShaderPost(bool force=false);

            /**
             * @brief AffichageFilaire Affiche un tampon avec le shader dédié au modèle filaire.
             */
            bool AffichageFilaire(const PTampon_GL &tampon) const;

            /**
//             * @brief AffichageModele Affiche un tampon avec le shader dédié au modèle des surfaces.
             */
            bool AffichageModele(const PTampon_GL &tampon) const;

            /**
             * @brief Bloque le fil d'exécution appelant le temps que l'affichage soit réalisé.
             */
            void SyncAffichage(bool flush=false) const;

            /**
             * @brief Calcul de la projection du curseur de la souris. Renvoi true si le curseur est placé sur un pixel actif, sinon false.
             */
            bool CalcProjectionCurseur(pint x, pint y);

            inline const PVertex_GL &ProjectionCurseur() const
            {
                return projection_curseur;
            }

            /**
             * @brief Initialisation des shaders (dont on donne le code source en argument, ou nullptr si l'on ne souhaite pas utiliser l'un des shaders)
             */
            const std::string &InitialisationShaders(const char *source_vertex_shader, const char *source_pixel_shader, puint contexte=CONTEXTE_MODELE);

            /**
             * @brief Export du code binaire du shader (après son initialisation).
             */
            bool DumpShaderBin(const std::string &chemin, puint contexte=CONTEXTE_MODELE) const;

            /**
             * @brief ChangeEtatShaders Active/desactive les shaders. Si aucun shader n'a été défini, il faut faire appel à la fonction InitialisationShaders() pour les générer.
             */
            bool defEtatShaders(bool etat, bool init=false);

            /**
             * @brief Initialisation des matrices.
             */
            void IdentiteMatrices();

            /**
             * @brief Assigne le type de projection ortho ou perspective.
             */
            void defTypeProjection(bool mode_ortho);

            /**
             * @brief Assigne les angles de rotation pour la navigation.
             */
            void defRotationCamera(const PVertex_GL &r);

            void defRotationCameraX(const GLcoord x);

            void defRotationCameraY(const GLcoord y);

            void defRotationCameraZ(const GLcoord z);

            inline const PVertex_GL &RotationCamera() const { return rotation_camera; }

            /**
             * @brief Défini l'échelle d'affichage (change la matrice de la scène).
             */
            void defEchelleAffichage(GLcoord v, bool relatif=false);

            /**
             * @brief Renvoi l'echelle d'affichage.
             */
            inline GLcoord EchelleAffichage() const
            {
                return echelle_affichage;
            }

            /**
             * @brief Renvoi le champ de vue.
             */
            inline GLcoord ChampVue() const
            {
                return fov;
            }

            /**
             * @brief Assigne la taille du cadre d'affichage.
             */
            void defTailleAffichage(pint largeur, pint hauteur);

            /**
             * @return Renvoi la position latérale de la caméra.
             */
            const PVertex_GL &TranslationCamera() const;

            /**
             * @brief Déplacement latéral 2D de la caméra (d'après le point de vue)
             */
            void defTranslationCamera(pfloat x, pfloat y, bool relatif=false);

            /**
             * @brief Déplace frontalement la caméra.
             */
            void defAvanceCamera(pfloat z, bool relatif=false);

            /**
             * @brief Met à jour la couleur de fond d'écran.
             */
            void defCouleurFond(const PCouleur_GL &c);

            /**
             * @brief Assigne la couleur de fond par défaut.
             */
            void ReinitCouleurFond();

            /**
             * @brief Renvoi la couleur de fond.
             */
            inline const PCouleur_GL &CouleurFond() const
            {
                return ParametresGL().couleur_fond;
            }

            /**
             * @brief Renvoi la couleur de fond réelle (en lisant un pixel particulier). Peut différer de la couleur de fond définie en fonction des shaders.
             */
            PCouleur_GL CouleurFondReel() const;

            /**
             * @return Renvoi la matrice de la scene.
             */
            inline PMatriceGL4 &MatriceScene()
            {
                return matrice_scene;
            }

            /**
             * @return Renvoi la matrice du modèle (le contenu de la scène).
             */
            inline PMatriceGL4 &MatriceModele()
            {
                return matrice_solide;
            }

            /**
             * @return Renvoi la matrice de projection.
             */
            inline PMatriceGL4 &MatriceProjection()
            {
                return matrice_projections;
            }

            /**
             * @return Renvoi la matrice des vecteurs normaux.
             */
            inline PMatriceGL4 &MatriceNormals()
            {
                return matrice_normals;
            }

            /**
             * @return Renvoi la matrice d'affichage (servira à projeter la scène).
             */
            inline PMatriceGL4 &MatriceAffichage()
            {
                return matrice_scene_solide;
            }

            void defEclairageDynamique(bool etat);

            inline bool AffichageEncours() const { return bloque_affichage; }

        protected:
            /**
             * @brief Calcul de la position de la lumière dynamique.
             */
            void CalcPositionLumiere();

            inline const PVertex_GL &PositionLumiere() const
            {
                return position_lumiere;
            }

            void defChampVue(GLcoord v, bool relatif=false);

            /**
             * @brief GenDiv Genère le modèle divers (entités supplémentaires qui ne sont pas nécessairement parti de la scène).
             */
            void GenDivers(bool affiche_origine);

            /**
             * @brief AffichageDivers Affiche le modèle divers (entités supplémentaires qui ne sont pas nécessairement parti de la scène).
             */
            void AffichageDivers() const;

            /**
             * @brief AjoutSommetDiv Ajoute un sommet dans le modèle divers (entités supplémentaires qui ne sont pas nécessairement parti de la scène).
             */
            void AjoutSommetDiv(const PVertex_GL &point, const PCouleur_GL &couleur);

            /**
             * @brief AjoutLigneDiv Ajoute un segment dans le modèle divers (entités supplémentaires qui ne sont pas nécessairement parti de la scène).
             */
            void AjoutLigneDiv(const PVertice2_GL &ligne, const PCouleur_GL &couleur);

            /**
             * @brief AjoutTriangleDiv Ajoute un triangle dans le modèle divers (entités supplémentaires qui ne sont pas nécessairement parti de la scène).
             */
            void AjoutTriangleDiv(const PVertice3_GL &triangle, const PVertex_GL &norm, const PCouleur_GL &couleur);

            void GenOrigineDiv(const PVertex_GL &v);
            void GenMatriceDiv(const PMatriceGL4 &m);

            void CalculeMatriceProjections(PMatriceGL4 &matrice, pint taille_x, pint taille_y);
            void CalcMatricesAffichage();

            void InitialisationEclairage(bool statique=false) const;

            PVertex_GL DeProj(GLcoord x, GLcoord y, GLcoord z);

            inline const Params_GL &ParametresGL() const { return parametres; }

            inline void BloqueAffichage() { bloque_affichage = true; SyncAffichage(true); }
            inline void ActiveAffichage() { bloque_affichage = false; }
            inline bool ExecAffichage() const { return !AffichageEncours(); }

            inline bool UtiliseFBO() const  { return utilise_fbo_tex; }

            inline bool ChronoShaderPost() const
            {
                if (UtiliseFBO() && parametres.active_fbo)
                {
                    return framebuffer.Valide() && framebuffer.UtiliseChronoShaderPost();
                }
                return false;
            }

        private:
            Params_GL parametres;
            bool bloque_affichage;
            bool utilise_fbo_tex;

//            bool mode_vue_libre; /* Navigation type FPS. */

            pint taille_affichage_x;
            pint taille_affichage_y;

            GLcoord fov;
            GLcoord echelle_affichage;

            Shaders_GL program_filaire, program_modele;
            PFrameBuffer_GL framebuffer;

            PVertex_GL rotation_camera;
            PVertex_GL Translation_camera; /* Translation latérale de la caméra. */
            PMatriceGL4 matrice_scene; /* Matrice de la scène (caméra). */
            PMatriceGL4 matrice_solide; /* Matrice du solide. */
            PMatriceGL4 matrice_projections;
            PMatriceGL4 matrice_scene_solide; /* Matrice d'affichage (matrice_scene * matrice_solide) */
            PMatriceGL4 matrice_normals;

            PVertex_GL projection_curseur;
            PVertex_GL position_lumiere;

            /* Entités supplémentaires (sommets, filaire, triangles...) */
            PTampon_GL sommets_div;
            PTampon_GL filaire_div;
            PTampon_GL triangles_div;
    };

#ifdef PERSPECTIVE_GL
    struct Params_Perspective_GL
    {
            explicit Params_Perspective_GL(float opacite_solide_=100.f, pint idcouleur_solide_=0, bool mode_2d_=false);

            void defImprimante3D(bool etat, float x, float y, float z);

            float opacite_solide;
            pint idcouleur_solide;
            bool mode_2d;
            bool lissage;
            bool imprimante3d;
            float plateau_imprimante3d_x, plateau_imprimante3d_y, imprimante3d_z;
            pint type_affichage;

            PCouleur_GL couleur_sommets;
            PCouleur_GL couleur_segments;
            PCouleur_GL couleur_imprimante;
            PCouleur_GL couleur_repere_sommets;
            PCouleur_GL couleur_repere_segments;
            PCouleur_GL couleur_repere_faces;
    };

    /**
     * @brief Classe d'affichage pour une scène Perspective3D.
     * @details Perspective_GL fait office d'interface à OpenGL pour gérer l'initialisation, les matrices de transformation (donc la navigation), l'éclairage et l'affichage du contenu d'une scène Perspective3D (PScene).
     */
    class Perspective_GL : public Moteur_GL
    {
        public:
            explicit Perspective_GL(const Params_GL &params_gl=Params_GL(), const Params_Perspective_GL &params_pgl=Params_Perspective_GL());
            ~Perspective_GL();

            /**
             * @brief Initialisation de l'affichage. Ne doit en principe être appelé qu'une fois, avant l'affichage.
             */
            void InitialisationAffichage();

            /**
             * @brief Réinitialisation de l'état de l'affichage (peut être appelé avant chaque passe d'affichage pour s'assurer que l'état GL soit valide).
             */
            void InitialisationEtatAffichage();

            /**
             * @brief Envoi le contenu de la scène à OpenGL.
             */
            void AfficheScene();

            /**
             * @brief ExecShaderPost Mise à jour simple de l'affichage (données temporelles par exemple, en exécutant le shader de post-traitement), sans re-générer le modèle.
             */
            bool ExecShaderPost(bool force=false);

            inline PVectCouleur_GL &CouleursTriangles()
            {
                return triangles_surfaces.Couleurs();
            }

            /**
             * @brief Renvoi le vecteur contenant les couleurs des segments de la scène.
             */
            inline PVectCouleur_GL &CouleursLignes()
            {
                return filaire.Couleurs();
            }

            /**
             * @brief Renvoi la couleur des segments d'après le modèle de couleurs
             */
            inline const PCouleur_GL &CouleurReferenceSegments() const
            {
                return parametres.couleur_segments;
            }

            /**
             * @brief Renvoi le vecteur contenant les couleurs des sommets de la scène.
             */
            inline PVectCouleur_GL &CouleursSommets()
            {
                return sommets.Couleurs();
            }

            /**
             * @brief Renvoi la couleur des sommets d'après le modèle de couleurs
             */
            inline const PCouleur_GL &CouleurReferenceSommets() const
            {
                return parametres.couleur_sommets;
            }

            /**
             * @brief Renvoi le vecteur contenant les sommets de la scène.
             */
            inline const PVectVert_GL &Sommets() const
            {
                return sommets.VerticesConst();
            }

            /**
             * @brief Renvoi le vecteur contenant les segments (modèle filaire de la scène.
             */
            inline const PVectVert_GL &Filaire() const
            {
                return filaire.VerticesConst();
            }

            /**
             * @brief Renvoi le vecteur contenant les centres des surfaces de la scène.
             */
            inline const PVectVert_GL &CentreSurfaces() const
            {
                return centre_surfaces.VerticesConst();
            }

            /**
             * @brief Assigne les couleurs pour l'affichage de la scène.
             */
            void defCouleursScene(const PCouleur_GL &couleur_sommets, const PCouleur_GL &couleur_segments, const PCouleur_GL &couleur_imprimante, const PCouleur_GL &couleur_reperes_sommets, const PCouleur_GL &couleur_reperes_segments, const PCouleur_GL &couleur_reperes_faces);

            /**
             * @brief Prépare l'affichage d'une scène Perspective3D.
             * @param scene La scène à afficher.
             * @param affiche_plateau Affiche le plateau d'impression 3D
             * @param boite_x Taille de la zone imprimable en X (taille du plateau d'impression)
             * @param boite_y Taille de la zone imprimable en Y (taille du plateau d'impression)
             * @param boite_z Taille de la zone imprimable en Z (hauteur d'impression maximale)
             */
            void PrepareScene(const Perspective3D::PScene3D *scene_ptr);

            /**
             * @brief Vide la scène (il n'y aura plus rien à afficher !)
             */
            void VideScene();

            /**
             * @return Renvoi l'échelle utilisée dans la scène dans la scène (peut varier d'un affichage à l'autre).
             */
            inline GLcoord EchelleScene() const { return Echelle_scene; }

            /**
             * @brief OrigineScene
             * @return Renvoi la position de l'origine utilisée dans la scène.
             */
            inline Perspective3D::Pvec3 OrigineScene() const { return Origine_scene; }

            /**
             * @brief Converti un point dans l'espace global pour l'adapter à l'affichage de la scène.
             * @param p Le Point à convertir.
             * @return Renvoi une vertice.
             */
            inline PVertex_GL ConvPointGlobal(Perspective3D::Pvec3 &p) const { return PVertex_GL((p - Origine_scene) * Echelle_scene); }

            /**
             * @brief defImprimante3D Defini les paramètres du plateau de l'imprimante 3D.
             */
            void defImprimante3D(bool etat, float x, float y, float z);

            /**
             * @brief Défini l'état d'acftivation du lissage des ombrages
             */
            void defEtatLissage(bool etat);

            /**
             * @brief MiseaJourVBO Met à jour les tampons d'affichage dynamiques.
             */
            void MiseaJourVBO(puint modeles=MODELE_FACES);

            /**
             * @brief Assigne le niveau de transparence du solide.
             */
            void defOpaciteSolide(pint val);

            /**
             * @brief Renvoi la transparence attendue du solide (en % d'opacité, appliqué sur les surfaces uniquement).
             */
            inline pint OpaciteSolide() const { return parametres.opacite_solide; }

            /**
             * @brief Met à jour la couleur de la scène depuis une couleure RAL.
             */
            void defIdCouleurScene(puint ral);

            inline pint IdCouleurScene() const { return IdCouleurSolide; }

            /**
             * @brief Assigne dans c la couleur suivant la configuration.
             */
            bool SelectionCouleur(PCouleur_GL &c) const;

            /**
             * @brief Assigne dans c la couleur suivant la configuration.
             */
            void SelectionCouleur(PCouleur_GL &c, const PVertex_GL &norm) const;

            /**
             * @brief Change le mode d'affichage, voir l'enum ::MODES_AFFICHAGE.
             */
            void defAffichage(int id); /* Change le mode d'affichage */

            /**
             * @brief Affiche un sommet unique pour repérer un point précis sur le solide.
             */
            void defSommetRepere(const PVertex_GL &s, bool init=false);

            /**
             * @brief Retire le sommet de repère.
             */
            void SupprimeSommetRepere();

            /**
             * @brief Affiche le centre d'une surface.
             */
            void AffichageCentreSurface(int id=IDNUL);

            /**
             * @brief Affiche le centre d'un segment.
             */
            void AffichageCentreSegment(int id=IDNUL);

            /**
             * @brief Affiche un point.
             */
            void AffichagePoint(int id);

            inline bool ModeAffichageSurfaces() const
            {
                return aff_surfaces;
            }

            inline bool ModeAffichageModele() const
            {
                return aff_modele;
            }

            inline bool ModeAffichageSommets() const
            {
                return aff_sommets;
            }

            inline bool ModeAffichageFilaire() const
            {
                return aff_filaire;
            }

        protected:
            void PrepareScenePriv(const Perspective3D::PScene3D *scene_ptr, bool force_couleurs_scene=false, bool normal_par_sommet=false);
            void AfficheSurfaces(bool affiche_arretes=false) const;
            void AfficheProjections() const;
            void AfficheDivers() const;
            void AfficheDeveloppes() const;
            void AfficheFilaire() const;
            void AfficheImprimante() const;
            void AfficheSommets() const;
            void AfficheTextes() const;

        private:
            Params_Perspective_GL parametres;

            GLcoord Echelle_scene;
            Perspective3D::Pvec3 Origine_scene;

            pint IdCouleurSolide;
            pint IdSelectionPoint;
            pint IdSelectionSegment;
            pint IdSelectionSurfaces;

            bool aff_origine;
            bool aff_arretes;
            bool aff_surfaces;
            bool aff_modele;
            bool aff_filaire;
            bool aff_projections;
            bool aff_developpes;
            bool aff_sommets;
            bool aff_imprimante;

            bool aff_sommet_repere;
            PVertex_GL sommets_repere[3];

            /* Entités de la scène. */
            PTampon_GL sommets;
            PTampon_GL filaire;
            PTampon_GL imprimante;
            PTampon_GL projections;
            PTampon_GL divers;
            PTampon_GL textes;
            PTampon_GL developpes;
            PTampon_GL triangles_surfaces;
            PTampon_GL centre_surfaces; // Centre des surfaces pour gérer les collisions.
    };

#endif // PERSPECTIVE_GL

} // namespace PGL

#ifdef DEBUG
#include <iostream>

    inline std::ostream& operator<<(std::ostream& out, const PGL::PVertex_GL &p)
    {
        out << std::fixed << "Vertex_GL[ " << p.X() << ", " << p.Y() << ", " << p.Z() << " ]";
        return out;
    }

    inline std::ostream& operator<<(std::ostream& out, const PGL::PVertice2_GL &l)
    {
        out << std::fixed << "Vertice2_GL[ " << l.V1() << ", " << l.V2() << " ]";
        return out;
    }

    inline std::ostream& operator<<(std::ostream& out, const PGL::PVertice3_GL &tr)
    {
        out << std::fixed << "Vertice3_GL[ " << tr.V1() << ", " << tr.V2() << ", " << tr.V3() << " ]";
        return out;
    }

    inline std::ostream& operator<<(std::ostream& out, const PGL::PCouleur_GL &c)
    {
        out << std::fixed << "Couleur_GL[ " << (c.R()) << ", " << (c.V()) << ", " << (c.B()) << ", " << (c.Alpha()) << " ]";
        return out;
    }

    inline std::ostream& operator<<(std::ostream& os, const PGL::PMatriceGL4 &matrice)
    {
        const GLcoord *m = matrice.Tableau();
        os << "PMatriceGL4\n[\n";
        os << std::fixed << "  X[ " << m[0] << ", " << m[1] << ", " << m[2]  << ", " << m[3] << " ]\n"
           << "  Y[ " << m[4] << ", " << m[5] << ", " << m[6]  << ", " << m[7] << " ]\n"
           << "  Z[ " << m[8] << ", " << m[9] << ", " << m[10] << ", " << m[11] << " ]\n"
           << "  W[ " << m[12] << ", " << m[13] << ", " << m[14] << ", " << m[15] << " ]\n";
        os << "]";
        return os;
    }
#endif // DEBUG

#endif // PERSPECTIVE_GL_H
