/**
* @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.
*/

#include "perspective_sys.h"

/* Si défini, la vue 3D prend en compte l'orientation des surfaces pour l'affichage (les faces intérieures sont ignorées). */
//#define SENS_SURFACES_GL

#if defined(PSYS_WINDOWS)
    //#define GL_GLEXT_PROTOTYPES
    #define GETPROC_VBO
    #define GETPROC_VBA
    #define GETPROC_FBO
#elif defined(PSYS_POSIX)

    /* Uniquement à des fins des compatibilité sur les anciennes machines: */
    #define GETPROC_VBA
    #define GETPROC_FBO
    /** *** **/

    #ifndef GL_GLEXT_PROTOTYPES
        #define GL_GLEXT_PROTOTYPES
    #endif // GL_GLEXT_PROTOTYPES
#endif // PSYS_WINDOWS

//#include <GL/glew.h>

#include "perspective_gl.h"
#include <GL/glext.h>

#if defined(PSYS_LINUX)
    #include <GL/glx.h>
#elif defined(PSYS_WINDOWS)
    #include <GL/wglext.h>
#endif // PSYS_LINUX

//using namespace PGL;

#include <fstream>
#include <cstring>

//#ifdef DEBUG
//#define DIFF_DIV_COULEUR_R PGL_NORM_BYTE_FLOAT100)
//#define DIFF_DIV_COULEUR_V PGL_NORM_BYTE_FLOAT128)
//#define DIFF_DIV_COULEUR_B PGL_NORM_BYTE_FLOAT128)
//#else
#define DIFF_DIV_COULEUR_R PGL_NORM_BYTE_FLOAT(65)
#define DIFF_DIV_COULEUR_V PGL_NORM_BYTE_FLOAT(65)
#define DIFF_DIV_COULEUR_B PGL_NORM_BYTE_FLOAT(50)
//#endif // DEBUG

#ifdef DEBUG
#define DEBUG_GL
//#define DEBUG_GL_EXT
//#define DEBUG_GLSL
#endif // DEBUG

/* Configuration GLSL: */
#define NOM_MATRICE_MODEL "matModel"
#define NOM_MATRICE_PROJECTIONS "matProjections"
#define NOM_MATRICE_NORMALS "matNormals"
#define NOM_VEC_LIGHT0 "LightPos"
#define NOM_ATTR_VERTEX "Vertex"
#define NOM_ATTR_NORMAL "Normal"

#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
#define NOM_ATTR_COLOR "Color"
#endif // FORCE_VERSION_GL

/* Post */
#define NOM_ATTR_POST_COORDS "Coords"

#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
#define NOM_ATTR_POST_FRAGCOLOR "FragColor"
#endif // FORCE_VERSION_GL

#define NOM_POST_TEXTURE "Texture"
#define NOM_POST_RESOLUTION "Resolution"
#define NOM_POST_COULEUR_FOND "BackgroundColor"
#define NOM_POST_CHRONO "Time"

static const char * const VShader_Defaut = "uniform mat4 " NOM_MATRICE_MODEL";\n\
uniform mat4 " NOM_MATRICE_PROJECTIONS ";\n\
//uniform mat4 " NOM_MATRICE_NORMALS ";\n\
//uniform vec3 " NOM_VEC_LIGHT0 ";\n\
attribute vec3 " NOM_ATTR_VERTEX ";\n\
//attribute vec3 " NOM_ATTR_NORMAL ";\n\
\n\
varying vec4 color;\n\
//varying vec3 light_pos;\n\
\n\
void main(void)\n\
{\n\
    //light_pos = " NOM_VEC_LIGHT0 ";\n\
    color = gl_Color;\n\
    gl_Position = matProjections * matModel * vec4(Vertex, 1);\n\
}\n";

static const char * const FShader_Defaut = "varying vec4 color;\n\
//varying vec3 light_pos;\n\
\n\
void main(void)\n\
{\n\
    gl_FragColor = color;\n\
}\n\
";
/* ******************** */

static const char * const VShader_Post_Defaut = "attribute vec2 " NOM_ATTR_POST_COORDS ";\n\
//uniform sampler2D " NOM_POST_TEXTURE ";\n\
varying vec2 f_texcoord;\n\
\n\
void main(void)\n\
{\n\
    gl_Position = vec4(" NOM_ATTR_POST_COORDS ", 0.0, 1.0);\n\
    f_texcoord = (" NOM_ATTR_POST_COORDS " + 1.0) * 0.5;\n\
}\n\
";

static const char * const FShader_Post_Defaut = "uniform sampler2D " NOM_POST_TEXTURE ";\n\
//uniform vec2 " NOM_POST_RESOLUTION ";\n\
//uniform vec3 " NOM_POST_COULEUR_FOND ";\n\
//uniform float " NOM_POST_CHRONO ";\n\
varying vec2 f_texcoord;\n\
\n\
void main(void)\n\
{\n\
    gl_FragColor = texture2D(" NOM_POST_TEXTURE ", f_texcoord);\n\
}\n\
";

namespace PGL
{
    PCouleur_GL PCouleur_GL::Div() const /* Couleur divergeante */
    {
#define DIV_COULEUR_GL(x, diff)  (x>(PGL_NORM_BYTE_FLOAT(255)-(diff)) ? x-(diff) : x+(diff))
        return PGL::PCouleur_GL(DIV_COULEUR_GL(vc[0], DIFF_DIV_COULEUR_R), DIV_COULEUR_GL(vc[1], DIFF_DIV_COULEUR_V), DIV_COULEUR_GL(vc[2], DIFF_DIV_COULEUR_B), vc[3]);
#undef DIV_COULEUR_GL
    }

    PCouleur_GL PCouleur_GL::Div2() const /* Couleur divergeante 2 (restauration par rapport à la première fonction) */
    {
#define DIV_COULEUR2_GL(x, diff) (x>(PGL_NORM_BYTE_FLOAT(255)-(diff)) ? x+(diff) : x-(diff))
        return PCouleur_GL(DIV_COULEUR2_GL(vc[0], DIFF_DIV_COULEUR_R), DIV_COULEUR2_GL(vc[1], DIFF_DIV_COULEUR_V), DIV_COULEUR2_GL(vc[2], DIFF_DIV_COULEUR_B), vc[3]);
#undef DIV_COULEUR2_GL
    }

    inline float sqrtR_GL(float v)
    {
        const float xhalf = v * 0.5f;
        pint32 *x_ptr = reinterpret_cast<pint32*>(&v);
        *x_ptr = 0x5f3759df - ((*x_ptr) >> 1);
        v = v * (1.5f - xhalf * v * v);
//        x = x * (1.5f - xhalf * x * x);
        return (v);
    }

    PVertex_GL& PVertex_GL::Normalise()
    /* Normalise le vecteur */
    {
        GLcoord xxyyzz = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
        if (CompareE(xxyyzz, 0.))
        {
            v[0] = v[1] = v[2] = 0.;
            return *this;
        }

        const GLcoord inv = PGL::sqrtR_GL(xxyyzz);
        v[0] *= inv;
        v[1] *= inv;
        v[2] *= inv;
        return *this;
    }

    PVertex_GL PVertice3_GL::VecteurNormal() const
    /* Calcul du vecteur normal. */
    {
        PVertex_GL ux(v[1].X() - v[0].X(), v[1].Y() - v[0].Y(), v[1].Z() - v[0].Z());
        PVertex_GL vx(v[2].X() - v[0].X(), v[2].Y() - v[0].Y(), v[2].Z() - v[0].Z());

        GLcoord x = (ux.Y() * vx.Z()) - (ux.Z() * vx.Y());
        GLcoord y = (ux.Z() * vx.X()) - (ux.X() * vx.Z());
        GLcoord z = (ux.X() * vx.Y()) - (ux.Y() * vx.X());

        PVertex_GL p(x, y, z);
        p.Normalise();
        return p;
    }
}

#ifdef DEBUG_GL

//#define BOITE_ENGLOBANTE_GL /* Affichage de la boite englobante du modèle (taille 1*1*1 ; 0,0,0 au centre) */
//#define RAYON_CAMERA_GL /* Affichage du rayon de la caméra */

#include <iostream>
bool ControleErreursGL(const char *s=0, pint id_ligne=-1)
{
    const GLenum err = glGetError();
    if (err == GL_NO_ERROR)
    {
        return false;
    }

    if (s)
    {
        if (id_ligne == -1)
            std::cerr << "ControleErreursGL (" << s << ", err=" << err << ")";
        else
            std::cerr << "ControleErreursGL (" << s << " " << id_ligne << ", err=" << err << ")";
    }

    if (err & GL_INVALID_ENUM)
        std::cerr << ", GL_INVALID_ENUM";
    if (err & GL_INVALID_VALUE)
        std::cerr << ", GL_INVALID_VALUE";
    if (err & GL_INVALID_OPERATION)
        std::cerr << ", GL_INVALID_OPERATION";
#if !defined(PSYS_WINDOWS)
    if (err & GL_INVALID_FRAMEBUFFER_OPERATION) // Pas implémenté sur Windows
        std::cerr << ", GL_INVALID_FRAMEBUFFER_OPERATION";
#endif // PSYS_WINDOWS
    if (err & GL_OUT_OF_MEMORY)
        std::cerr << ", GL_OUT_OF_MEMORY";
    if (err & GL_STACK_UNDERFLOW)
        std::cerr << ", GL_STACK_UNDERFLOW";
    if (err & GL_STACK_OVERFLOW)
        std::cerr << ", GL_STACK_OVERFLOW";

    if (err & GL_INVALID_FRAMEBUFFER_OPERATION) /* glext */
        std::cerr << ", GL_INVALID_FRAMEBUFFER_OPERATION";

//    if (err & GL_CONTEXT_LOST) /* OpenGL 4.5 */
//        std::cerr << ", GL_CONTEXT_LOST";

    std::cerr << std::endl;

    return true;
}

#define CONTROLE_ERRGL ControleErreursGL("perspective_GL ligne", __LINE__)

void CurrentBufferGL(const char *s=0, pint ligne=-1)
{
    GLint drawFboId = 0, readFboId = 0;
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFboId);
    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readFboId);

    if (s)
    {
        if (ligne != -1)
        {
            std::cout << "CurrentBufferGL(" << s << ":" << ligne << ") :: w:" << drawFboId << ", r:" << readFboId << std::endl;
        }
        else
        {
            std::cout << "CurrentBufferGL(" << s << ") :: w:" << drawFboId << ", r:" << readFboId << std::endl;
        }
    }
    else
    {
        if (ligne != -1)
        {
            std::cout << "CurrentBufferGL(" << ligne << ") :: w:" << drawFboId << ", r:" << readFboId << std::endl;
        }
        else
        {
            std::cout << "CurrentBufferGL() :: w:" << drawFboId << ", r:" << readFboId << std::endl;
        }
    }
}

#define CONTROLE_BUFFER_GL CurrentBufferGL("perspective_GL ligne", __LINE__)

#else

bool ControleErreursGL()
/* Renvoi true si il y a des erreurs GL. */
{
    return (glGetError() != GL_NO_ERROR);
}

#define CONTROLE_ERRGL ControleErreursGL()
#define CONTROLE_BUFFER_GL /* TEMP */

#endif // DEBUG_GL

#ifndef MAX
    #define MAX(x, y) (((x)>(y)) ? (x) : (y))
#endif // MAX

namespace PGL
{
    namespace Extensions_GL
    {
//        bool Init();
//        bool ValideExtension(const char *ext);
//        bool SupportShaders();
//        bool SupportVBO();
//        bool SupportVBA();
        void *getProcAdress(const char *s);

        /* Shaders, programs, etc... */
        GLuint glCreateShader(GLenum type);
        void glDeleteShader(GLuint shader);
        void glShaderSource(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
        void glCompileShader(GLuint shader);
        void glGetShaderiv(GLuint shader, GLenum pname, GLint *params);
        void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
        GLuint glCreateProgram();
        void glDeleteProgram(GLuint program);
        void glLinkProgram(GLuint program);
        void glProgramParameteri(GLuint program, GLenum pname, GLint value);
        void glValidateProgram(GLuint program);
        void glAttachShader(GLuint program, GLuint shader);
        void glDetachShader(GLuint program, GLuint shader);
        void glGetProgramiv(GLuint program, GLenum pname, GLint *params);
        void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
        void glGetProgramBinary(GLuint program, GLsizei bufsize, GLsizei *length, GLenum *binaryFormat, void *binary);
        void glProgramBinary(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length);
        void glUseProgram(GLuint program);
        GLint glGetUniformLocation(GLuint program, const GLchar *name);
        void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
        void glUniform2f(GLint location, GLfloat v1, GLfloat v2);
        void glUniform3f(GLint location, GLfloat v1, GLfloat v2, GLfloat v3);
        void glUniform1i(GLint location, GLint v1);
        void glUniform1f(GLint location, GLfloat v1);
        GLint glGetAttribLocation(GLint location, const GLchar *name);
        void glEnableVertexAttribArray(GLint index);
        void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
        void glDisableVertexAttribArray(GLuint index);
        GLboolean glIsShader(GLuint shader);
        GLboolean glIsProgram(GLuint program);

        /* VBO */
        void glGenBuffers(GLsizei n, GLuint *buffers);
        void glBindBuffer(GLenum target, GLuint buffer);
        void glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
        void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
        void glDeleteBuffers(GLsizei n, const GLuint *buffers);
        void glGetBufferParameteriv(GLenum target, GLenum pname, GLint *params);
        GLvoid* glMapBuffer(GLenum target, GLenum access);
        GLboolean glUnmapBuffer(GLenum target);
        GLboolean glIsBuffer(GLuint buffer);

        /* VBA */
        void glBindVertexArray (GLuint array);
        void glDeleteVertexArrays (GLsizei n, const GLuint *arrays);
        void glGenVertexArrays (GLsizei n, GLuint *arrays);
        GLboolean glIsVertexArray (GLuint array);

        /* FBO */
        GLboolean glIsRenderbuffer (GLuint renderbuffer);
        void glBindRenderbuffer (GLenum target, GLuint renderbuffer);
        void glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers);
        void glGenRenderbuffers (GLsizei n, GLuint *renderbuffers);
        void glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
        void glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params);
        GLboolean glIsFramebuffer (GLuint framebuffer);
        void glBindFramebuffer (GLenum target, GLuint framebuffer);
        void glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers);
        void glGenFramebuffers (GLsizei n, GLuint *framebuffers);
        GLenum glCheckFramebufferStatus (GLenum target);
        void glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
        void glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
        void glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
        void glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
        void glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params);
        void glGenerateMipmap (GLenum target);
#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
        void glBindFragDataLocation(GLuint program, GLuint color, const GLchar *name);
        GLint glGetFragDataLocation(GLuint program, const GLchar *name);
#endif // FORCE_VERSION_GL
    }
}

namespace PGL
{
    namespace Extensions_GL_priv
    {
        bool valide_init = false;
        bool valide_support_shaders = false;
        bool valide_support_vbo = false;
        bool valide_support_vba = false;
        bool valide_support_fbo = false;

        GLuint (APIENTRY *_glCreateShader)(GLenum) = nullptr;
        void   (APIENTRY *_glDeleteShader)(GLuint) = nullptr;
        void   (APIENTRY *_glShaderSource)(GLuint, GLsizei, const GLchar * const *, const GLint *) = nullptr;
        void   (APIENTRY *_glCompileShader)(GLuint) = nullptr;
        void   (APIENTRY *_glGetShaderiv)(GLuint, GLenum, GLint *) = nullptr;
        void   (APIENTRY *_glGetShaderInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *) = nullptr;
        GLuint (APIENTRY *_glCreateProgram)() = nullptr;
        void   (APIENTRY *_glDeleteProgram)(GLuint) = nullptr;
        void   (APIENTRY *_glLinkProgram)(GLuint) = nullptr;
        void   (APIENTRY *_glValidateProgram)(GLuint) = nullptr;
        void   (APIENTRY *_glProgramParameteri)(GLuint, GLenum, GLint) = nullptr;
        void   (APIENTRY *_glAttachShader)(GLuint, GLuint) = nullptr;
        void   (APIENTRY *_glDetachShader)(GLuint, GLuint) = nullptr;
        void   (APIENTRY *_glGetProgramiv)(GLuint, GLenum, GLint *) = nullptr;
        void   (APIENTRY *_glGetProgramInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *) = nullptr;
        void   (APIENTRY *_glGetProgramBinary)(GLuint, GLsizei, GLsizei *, GLenum *, void *) = nullptr;
        void   (APIENTRY *_glProgramBinary)(GLuint, GLenum, const void *, GLsizei) = nullptr;
        void   (APIENTRY *_glUseProgram)(GLuint) = nullptr;
        GLint  (APIENTRY *_glGetUniformLocation)(GLuint, const GLchar *) = nullptr;
        void   (APIENTRY *_glUniformMatrix4fv)(GLint, GLsizei, GLboolean, const GLfloat *) = nullptr;
        void   (APIENTRY *_glUniform2f)(GLint, GLfloat, GLfloat) = nullptr;
        void   (APIENTRY *_glUniform3f)(GLint, GLfloat, GLfloat, GLfloat) = nullptr;
        void   (APIENTRY *_glUniform1i)(GLint, GLint) = nullptr;
        void   (APIENTRY *_glUniform1f)(GLint, GLfloat) = nullptr;
        GLint  (APIENTRY *_glGetAttribLocation)(GLint, const GLchar *) = nullptr;
        void   (APIENTRY *_glEnableVertexAttribArray)(GLint) = nullptr;
        void   (APIENTRY *_glVertexAttribPointer)(GLuint, GLint, GLenum, GLboolean, GLsizei, const void *) = nullptr;
        void   (APIENTRY *_glDisableVertexAttribArray)(GLuint) = nullptr;
        GLboolean (APIENTRY *_glIsShader)(GLuint) = nullptr;
        GLboolean (APIENTRY *_glIsProgram)(GLuint) = nullptr;

#ifdef GETPROC_VBO
        void (APIENTRY *_glGenBuffersARB) (GLsizei, GLuint *) = nullptr;
        void (APIENTRY *_glBindBufferARB) (GLenum, GLuint) = nullptr;
        void (APIENTRY *_glBufferDataARB) (GLenum, GLsizeiptrARB, const GLvoid *, GLenum) = nullptr;
        void (APIENTRY *_glBufferSubDataARB) (GLenum, GLintptrARB, GLsizeiptrARB, const GLvoid *) = nullptr;
        void (APIENTRY *_glDeleteBuffersARB) (GLsizei, const GLuint *) = nullptr;
        void (APIENTRY *_glGetBufferParameterivARB) (GLenum, GLenum, GLint *) = nullptr;
        GLvoid* (APIENTRY *_glMapBufferARB) (GLenum, GLenum) = nullptr;
        GLboolean (APIENTRY *_glUnmapBufferARB) (GLenum) = nullptr;
        GLboolean (APIENTRY *_glIsBuffer) (GLuint) = nullptr;
#endif // GETPROC_VBO

#ifdef GETPROC_VBA
        void (APIENTRY *_glBindVertexArrayARB) (GLuint) = nullptr;
        void (APIENTRY *_glDeleteVertexArraysARB) (GLsizei, const GLuint *) = nullptr;
        void (APIENTRY *_glGenVertexArraysARB) (GLsizei, GLuint *) = nullptr;
        GLboolean (APIENTRY *_glIsVertexArrayARB) (GLuint) = nullptr;
#endif // GETPROC_VBA

#ifdef GETPROC_FBO
        GLboolean (APIENTRY *_glIsRenderbuffer) (GLuint renderbuffer) = nullptr;
        void (APIENTRY *_glBindRenderbuffer) (GLenum target, GLuint renderbuffer) = nullptr;
        void (APIENTRY *_glDeleteRenderbuffers) (GLsizei n, const GLuint *renderbuffers) = nullptr;
        void (APIENTRY *_glGenRenderbuffers) (GLsizei n, GLuint *renderbuffers) = nullptr;
        void (APIENTRY *_glRenderbufferStorage) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height) = nullptr;
        void (APIENTRY *_glGetRenderbufferParameteriv) (GLenum target, GLenum pname, GLint *params) = nullptr;
        GLboolean (APIENTRY *_glIsFramebuffer) (GLuint framebuffer) = nullptr;
        void (APIENTRY *_glBindFramebuffer) (GLenum target, GLuint framebuffer) = nullptr;
        void (APIENTRY *_glDeleteFramebuffers) (GLsizei n, const GLuint *framebuffers) = nullptr;
        void (APIENTRY *_glGenFramebuffers) (GLsizei n, GLuint *framebuffers) = nullptr;
        GLenum (APIENTRY *_glCheckFramebufferStatus) (GLenum target) = nullptr;
        void (APIENTRY *_glFramebufferTexture1D) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) = nullptr;
        void (APIENTRY *_glFramebufferTexture2D) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) = nullptr;
        void (APIENTRY *_glFramebufferTexture3D) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) = nullptr;
        void (APIENTRY *_glFramebufferRenderbuffer) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) = nullptr;
        void (APIENTRY *_glGetFramebufferAttachmentParameteriv) (GLenum target, GLenum attachment, GLenum pname, GLint *params) = nullptr;
        void (APIENTRY *_glGenerateMipmap) (GLenum target) = nullptr;
    #if FORCE_VERSION_GL >= VERSION_GL(3, 2)
        void (APIENTRY *_glBindFragDataLocation)(GLuint, GLuint, const GLchar *) = nullptr;
        GLint (APIENTRY *_glGetFragDataLocation)(GLuint, const GLchar *) = nullptr;
    #endif // FORCE_VERSION_GL
#endif // GETPROC_FBO

        inline bool ValideFctExtension(const void *p)
        {
            return (Extensions_GL_priv::valide_init && Ptest_ptr(p));
        }
    }
}

namespace PGL
{
    bool Extensions_GL::Init()
    {
        if (Extensions_GL_priv::valide_init)
        {
            return true;
        }

        pint cpt_fcts = 0;
        pint valide = 0;

        Extensions_GL_priv::valide_support_shaders = Extensions_GL::SupportShaders();
        Extensions_GL_priv::valide_support_vbo = Extensions_GL::SupportVBO();
        Extensions_GL_priv::valide_support_vba = Extensions_GL::SupportVBA();
        Extensions_GL_priv::valide_support_fbo = Extensions_GL::SupportFBO();

#ifdef DEBUG
#define ASSIGNE_PROTO_EXT(FCT, PROTO) ++cpt_fcts; if (Extensions_GL_priv::_##FCT == nullptr)\
{\
    Extensions_GL_priv::_##FCT = nullptr; \
    Extensions_GL_priv::_##FCT = PROTO Extensions_GL::getProcAdress(#FCT);\
    if (Extensions_GL_priv::_##FCT != nullptr)\
    {\
        ++valide;\
    }\
    else \
    { \
        std::cout << "Extensions_GL :: Erreur d'initialisation de la fonction " << #FCT << std::endl; \
    } \
}
#else
#define ASSIGNE_PROTO_EXT(FCT, PROTO) ++cpt_fcts; if (Extensions_GL_priv::_##FCT == nullptr)\
{\
    Extensions_GL_priv::_##FCT = nullptr; \
    Extensions_GL_priv::_##FCT = PROTO Extensions_GL::getProcAdress(#FCT);\
    if (Extensions_GL_priv::_##FCT != nullptr)\
    {\
        ++valide;\
    }\
}
#endif // DEBUG

        /* shaders */
        if (Extensions_GL_priv::valide_support_shaders)
        {
            ASSIGNE_PROTO_EXT( glCreateShader, (GLuint (APIENTRY *)(GLenum)) );
            ASSIGNE_PROTO_EXT( glDeleteShader, (void (APIENTRY *)(GLuint)) );
            ASSIGNE_PROTO_EXT( glShaderSource, (void (APIENTRY *)(GLuint, GLsizei, const GLchar * const *, const GLint *)) );
            ASSIGNE_PROTO_EXT( glCompileShader, (void (APIENTRY *)(GLuint)) );
            ASSIGNE_PROTO_EXT( glGetShaderiv, (void (APIENTRY *)(GLuint, GLenum, GLint *)) );
            ASSIGNE_PROTO_EXT( glGetShaderInfoLog, (void (APIENTRY *)(GLuint, GLsizei, GLsizei *, GLchar *)) );
            ASSIGNE_PROTO_EXT( glCreateProgram, (GLuint (APIENTRY *)()) );
            ASSIGNE_PROTO_EXT( glDeleteProgram, (void (APIENTRY *)(GLuint)) );
            ASSIGNE_PROTO_EXT( glLinkProgram, (void (APIENTRY *)(GLuint)) );
            ASSIGNE_PROTO_EXT( glValidateProgram, (void (APIENTRY *)(GLuint)) );
            ASSIGNE_PROTO_EXT( glAttachShader, (void (APIENTRY *)(GLuint, GLuint)) );
            ASSIGNE_PROTO_EXT( glDetachShader, (void (APIENTRY *)(GLuint, GLuint)) );
            ASSIGNE_PROTO_EXT( glProgramParameteri, (void (APIENTRY *)(GLuint, GLenum, GLint)) );
            ASSIGNE_PROTO_EXT( glGetProgramiv, (void (APIENTRY *)(GLuint, GLenum, GLint *)) );
            ASSIGNE_PROTO_EXT( glGetProgramBinary, (void (APIENTRY *)(GLuint, GLsizei, GLsizei *, GLenum *, void *)) );
            ASSIGNE_PROTO_EXT( glProgramBinary, (void (APIENTRY *)(GLuint, GLenum, const void *, GLsizei)) );
            ASSIGNE_PROTO_EXT( glUseProgram, (void (APIENTRY *)(GLuint)) );
            ASSIGNE_PROTO_EXT( glGetUniformLocation, (GLint (APIENTRY *)(GLuint, const GLchar *)) );
            ASSIGNE_PROTO_EXT( glUniformMatrix4fv, (void (APIENTRY *)(GLint, GLsizei, GLboolean, const GLfloat *)) );
            ASSIGNE_PROTO_EXT( glUniform2f, (void (APIENTRY *)(GLint, GLfloat, GLfloat)) );
            ASSIGNE_PROTO_EXT( glUniform3f, (void (APIENTRY *)(GLint, GLfloat, GLfloat, GLfloat)) );
            ASSIGNE_PROTO_EXT( glUniform1i, (void (APIENTRY *)(GLint, GLint)) );
            ASSIGNE_PROTO_EXT( glUniform1f, (void (APIENTRY *)(GLint, GLfloat)) );
            ASSIGNE_PROTO_EXT( glGetAttribLocation, (GLint (APIENTRY *)(GLint, const GLchar *)) );
            ASSIGNE_PROTO_EXT( glEnableVertexAttribArray, (void (APIENTRY *)(GLint)) );
            ASSIGNE_PROTO_EXT( glVertexAttribPointer, (void (APIENTRY *)(GLuint, GLint, GLenum, GLboolean, GLsizei, const void *)) );
            ASSIGNE_PROTO_EXT( glDisableVertexAttribArray, (void (APIENTRY *)(GLuint)) );
            ASSIGNE_PROTO_EXT( glIsShader, (GLboolean (APIENTRY *)(GLuint)) );
            ASSIGNE_PROTO_EXT( glIsProgram, (GLboolean (APIENTRY *)(GLuint)) );
            ASSIGNE_PROTO_EXT( glGetProgramInfoLog, (void (APIENTRY *)(GLuint, GLsizei, GLsizei *, GLchar *)) );

#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
            ASSIGNE_PROTO_EXT( glBindFragDataLocation, (void (APIENTRY *)(GLuint, GLuint, const GLchar *)) );
            ASSIGNE_PROTO_EXT( glGetFragDataLocation, (GLint (APIENTRY *)(GLuint, const GLchar *)) );
#endif // FORCE_VERSION_GL
        }

        /* vbo */
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::valide_support_vbo)
        {
            ASSIGNE_PROTO_EXT ( glGenBuffersARB, (void (APIENTRY *) (GLsizei, GLuint *)) );
            ASSIGNE_PROTO_EXT ( glBindBufferARB, (void (APIENTRY *) (GLenum, GLuint)) );
            ASSIGNE_PROTO_EXT ( glBufferDataARB, (void (APIENTRY *) (GLenum, GLsizeiptrARB, const GLvoid *data, GLenum)) );
            ASSIGNE_PROTO_EXT ( glBufferSubDataARB, (void (APIENTRY *) (GLenum, GLintptrARB, GLsizeiptrARB, const GLvoid *)) );
            ASSIGNE_PROTO_EXT ( glDeleteBuffersARB, (void (APIENTRY *) (GLsizei, const GLuint *)) );
            ASSIGNE_PROTO_EXT ( glGetBufferParameterivARB, (void (APIENTRY *) (GLenum, GLenum, GLint *)) );
            ASSIGNE_PROTO_EXT ( glMapBufferARB, (GLvoid* (APIENTRY *) (GLenum, GLenum)) );
            ASSIGNE_PROTO_EXT ( glUnmapBufferARB, (GLboolean (APIENTRY *) (GLenum)) );
            ASSIGNE_PROTO_EXT ( glIsBuffer, (GLboolean (APIENTRY *) (GLuint)) );
        }
#endif // GETPROC_VBO

        /* vba */
#ifdef GETPROC_VBA
        if (Extensions_GL_priv::valide_support_vba)
        {
            ASSIGNE_PROTO_EXT ( glBindVertexArrayARB, (void (APIENTRY *) (GLuint)) );
            ASSIGNE_PROTO_EXT ( glDeleteVertexArraysARB, (void (APIENTRY *) (GLsizei, const GLuint *)) );
            ASSIGNE_PROTO_EXT ( glGenVertexArraysARB, (void (APIENTRY *) (GLsizei, GLuint *)) );
            ASSIGNE_PROTO_EXT ( glIsVertexArrayARB, (GLboolean (APIENTRY *) (GLuint)) );
        }
#endif // GETPROC_VBA

#ifdef GETPROC_FBO
        if (Extensions_GL_priv::valide_support_fbo)
        {
            ASSIGNE_PROTO_EXT ( glIsRenderbuffer, (GLboolean (APIENTRY *) (GLuint renderbuffer)) );
            ASSIGNE_PROTO_EXT ( glBindRenderbuffer, (void (APIENTRY *) (GLenum target, GLuint renderbuffer)) );
            ASSIGNE_PROTO_EXT ( glDeleteRenderbuffers, (void (APIENTRY *) (GLsizei n, const GLuint *renderbuffers)) );
            ASSIGNE_PROTO_EXT ( glGenRenderbuffers, (void (APIENTRY *) (GLsizei n, GLuint *renderbuffers)) );
            ASSIGNE_PROTO_EXT ( glRenderbufferStorage, (void (APIENTRY *) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) );
            ASSIGNE_PROTO_EXT ( glGetRenderbufferParameteriv, (void (APIENTRY *) (GLenum target, GLenum pname, GLint *params)) );
            ASSIGNE_PROTO_EXT ( glIsFramebuffer, (GLboolean (APIENTRY *) (GLuint framebuffer)) );
            ASSIGNE_PROTO_EXT ( glBindFramebuffer, (void (APIENTRY *) (GLenum target, GLuint framebuffer)) );
            ASSIGNE_PROTO_EXT ( glDeleteFramebuffers, (void (APIENTRY *) (GLsizei n, const GLuint *framebuffers)) );
            ASSIGNE_PROTO_EXT ( glGenFramebuffers, (void (APIENTRY *) (GLsizei n, GLuint *framebuffers)) );
            ASSIGNE_PROTO_EXT ( glCheckFramebufferStatus, (GLenum (APIENTRY *) (GLenum target)) );
            ASSIGNE_PROTO_EXT ( glFramebufferTexture1D, (void (APIENTRY *) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) );
            ASSIGNE_PROTO_EXT ( glFramebufferTexture2D, (void (APIENTRY *) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) );
            ASSIGNE_PROTO_EXT ( glFramebufferTexture3D, (void (APIENTRY *) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)) );
            ASSIGNE_PROTO_EXT ( glFramebufferRenderbuffer, (void (APIENTRY *) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) );
            ASSIGNE_PROTO_EXT ( glGetFramebufferAttachmentParameteriv, (void (APIENTRY *) (GLenum target, GLenum attachment, GLenum pname, GLint *params)) );
            ASSIGNE_PROTO_EXT ( glGenerateMipmap, (void (APIENTRY *) (GLenum target)) );
        }
#endif // GETPROC_FBO

#undef ASSIGNE_PROTO_EXT

        Extensions_GL_priv::valide_init = true;

#ifdef DEBUG_GL
        std::cout << "Extensions_GL::Init() :: Validation des fonctions : " << valide << "/" << cpt_fcts << std::endl;
#endif // DEBUG_GL

        return ((valide > 0) && (valide==cpt_fcts));
    }

    bool Extensions_GL::ValideExtension(const char *ext)
    {
#if FORCE_VERSION_GL >= VERSION_GL(3, 0)
        GLint n_exts = 0;
        glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts);

        if (!n_exts)
        {
#ifdef DEBUG_GL
            std::cout << "Exdtensions_GL::ValideExtension :: On est pas dans un profil d'affichage !" << std::endl;
            CONTROLE_ERRGL;
#endif // DEBUG_GL
            return false;
        }

        const char *(APIENTRY *getstringi)(GLenum, GLuint) = (const char *(APIENTRY *)(GLenum, GLuint)) Extensions_GL::getProcAdress("glGetStringi");

        if (!Ptest_ptr(getstringi))
        {
            return false;
        }

        const puint n_exts_c = n_exts;
        for(puint i = 0; i<n_exts_c; ++i)
        {
            const char *ptr = (const char *) getstringi(GL_EXTENSIONS, i);
            if (strcmp(ext, ptr))
            {
#ifdef DEBUG_GL_EXT
                std::cout << "ValideExtension " << ext << " : Oui !" << std::endl;
#endif // DEBUG_GL_EXT
                return true;
            }
        }

#else /* !if FORCE_VERSION_GL >= VERSION_GL(3, 0) */
        const char *liste_exts = (const char*) glGetString(GL_EXTENSIONS);

        if (!Ptest_ptr(liste_exts))
        {
#ifdef DEBUG_GL
            std::cout << "Extensions_GL::ValideExtension :: On est pas dans un profil d'affichage !" << std::endl;
            CONTROLE_ERRGL;
#endif // DEBUG_GL
            return false;
        }

        char const * ptr = nullptr;
        const size_t lg_ext = strlen(ext);

        while ( (ptr = strstr(liste_exts, ext)) != nullptr )
        {
            if (strncmp(ptr, ext, lg_ext) == 0)
            {
                if (ptr[lg_ext] == ' ')
                {
#ifdef DEBUG_GL_EXT
                    std::cout << "ValideExtension " << ext << " : Oui !" << std::endl;
#endif // DEBUG_GL_EXT
                    return true;
                }
            }
            liste_exts = ptr + strlen(ptr);
        }

#endif /* if FORCE_VERSION_GL >= VERSION_GL(3, 0) */

#ifdef DEBUG_GL_EXT
        std::cout << "ValideExtension " << ext << " : Non !" << std::endl;
#endif // DEBUG_GL_EXT
        return false;
    }

    void *Extensions_GL::getProcAdress(const char *s)
    {
#if defined(PSYS_LINUX)
        return (void *) (*glXGetProcAddressARB)((const GLubyte*) s);
#elif defined(PSYS_WINDOWS)
        //return (void *) wglGetProcAddress((LPCSTR) s);

        void *p = (void *)wglGetProcAddress((LPCSTR)s);

//        std::cout << "getProcAdress(" << s << ")" << ":" << p << std::endl;

        if(p == nullptr ||
                (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) ||
                (p == (void*)-1) )
        {
            HMODULE module = LoadLibraryA("opengl32.dll");
            p = (void *)GetProcAddress(module, (LPCSTR) s);
        }

        return p;

#else
        return nullptr;
#endif // PSYS_LINUX
    }

    bool Extensions_GL::SupportShaders()
    {
        if (Extensions_GL_priv::valide_init)
        {
            return Extensions_GL_priv::valide_support_shaders;
        }
        return (ValideExtension("GL_ARB_shading_language_100") &&
                ValideExtension("GL_ARB_shader_objects") &&
                ValideExtension("GL_ARB_fragment_shader") &&
                ValideExtension("GL_ARB_vertex_shader"));
    }

    bool Extensions_GL::SupportVBO()
    {
        if (Extensions_GL_priv::valide_init)
        {
            return Extensions_GL_priv::valide_support_vbo;
        }
        return ValideExtension("GL_ARB_vertex_buffer_object");
    }

    bool Extensions_GL::SupportVBA()
    {
        if (Extensions_GL_priv::valide_init)
        {
            return Extensions_GL_priv::valide_support_vba;
        }
        return ValideExtension("GL_ARB_vertex_array_object");
    }

    bool Extensions_GL::SupportFBO()
    {
        if (Extensions_GL_priv::valide_init)
        {
            return Extensions_GL_priv::valide_support_fbo;
        }
        return ValideExtension("GL_ARB_framebuffer_object");
    }

    GLuint Extensions_GL::glCreateShader(GLenum type)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glCreateShader)))
        {
            return Extensions_GL_priv::_glCreateShader(type);
        }
        return 0;
    }

    void Extensions_GL::glDeleteShader(GLuint shader)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glDeleteShader)))
        {
            Extensions_GL_priv::_glDeleteShader(shader);
        }
    }

    void Extensions_GL::glShaderSource(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glShaderSource)))
        {
            Extensions_GL_priv::_glShaderSource(shader, count, string, length);
        }
    }

    void Extensions_GL::glCompileShader(GLuint shader)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glCompileShader)))
        {
            Extensions_GL_priv::_glCompileShader(shader);
        }
    }

    void Extensions_GL::glGetShaderiv(GLuint shader, GLenum pname, GLint *params)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetShaderiv)))
        {
            Extensions_GL_priv::_glGetShaderiv(shader, pname, params);
        }
    }

    void Extensions_GL::glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetShaderInfoLog)))
        {
            Extensions_GL_priv::_glGetShaderInfoLog(shader, bufSize, length, infoLog);
        }
    }

    GLuint Extensions_GL::glCreateProgram()
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glCreateProgram)))
        {
            return Extensions_GL_priv::_glCreateProgram();
        }
        return 0;
    }

    void Extensions_GL::glDeleteProgram(GLuint program)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glDeleteProgram)))
        {
            Extensions_GL_priv::_glDeleteProgram(program);
        }
    }

    void Extensions_GL::glLinkProgram(GLuint program)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glLinkProgram)))
        {
            Extensions_GL_priv::_glLinkProgram(program);
        }
    }

    void Extensions_GL::glProgramParameteri(GLuint program, GLenum pname, GLint value)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glProgramParameteri)))
        {
            Extensions_GL_priv::_glProgramParameteri(program, pname, value);
        }
    }

    void Extensions_GL::glValidateProgram(GLuint program)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glValidateProgram)))
        {
            Extensions_GL_priv::_glValidateProgram(program);
        }
    }

    void Extensions_GL::glAttachShader(GLuint program, GLuint shader)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glAttachShader)))
        {
            Extensions_GL_priv::_glAttachShader(program, shader);
        }
    }

    void Extensions_GL::glDetachShader(GLuint program, GLuint shader)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glDetachShader)))
        {
            Extensions_GL_priv::_glDetachShader(program, shader);
        }
    }

    void Extensions_GL::glGetProgramiv(GLuint program, GLenum pname, GLint *params)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetProgramiv)))
        {
            Extensions_GL_priv::_glGetProgramiv(program, pname, params);
        }
    }

    void Extensions_GL::glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetProgramInfoLog)))
        {
            Extensions_GL_priv::_glGetProgramInfoLog(program, bufSize, length, infoLog);
        }
    }

    void Extensions_GL::glGetProgramBinary(GLuint program, GLsizei bufsize, GLsizei *length, GLenum *binaryFormat, void *binary)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetProgramBinary)))
        {
            Extensions_GL_priv::_glGetProgramBinary(program, bufsize, length, binaryFormat, binary);
        }
        else
        {
            (*length) = 0;
        }
    }

    void Extensions_GL::glProgramBinary(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glProgramBinary)))
        {
            Extensions_GL_priv::_glProgramBinary(program, binaryFormat, binary, length);
        }
    }

    void Extensions_GL::glUseProgram(GLuint program)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glUseProgram)))
        {
            Extensions_GL_priv::_glUseProgram(program);
        }
    }

    GLint Extensions_GL::glGetUniformLocation(GLuint program, const GLchar *name)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetUniformLocation)))
        {
            return Extensions_GL_priv::_glGetUniformLocation(program, name);

//            GLint r = Extensions_GL_priv::_glGetUniformLocation(program, name);
//            std::cout << "glGetUniformLocation(" << program << ", " << name << ") : " << r << std::endl;
//            CONTROLE_ERRGL;
//            return r;
        }
        return -1;
    }

    void Extensions_GL::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glUniformMatrix4fv)))
        {
            Extensions_GL_priv::_glUniformMatrix4fv(location, count, transpose, value);
        }
    }

    void Extensions_GL::glUniform2f(GLint location, GLfloat v1, GLfloat v2)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glUniform2f)))
        {
            Extensions_GL_priv::_glUniform2f(location, v1, v2);
        }
    }

    void Extensions_GL::glUniform3f(GLint location, GLfloat v1, GLfloat v2, GLfloat v3)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glUniform3f)))
        {
            Extensions_GL_priv::_glUniform3f(location, v1, v2, v3);
        }
    }

    void Extensions_GL::glUniform1i(GLint location, GLint v1)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glUniform1i)))
        {
            Extensions_GL_priv::_glUniform1i(location, v1);
        }
    }

    void Extensions_GL::glUniform1f(GLint location, GLfloat v1)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glUniform1f)))
        {
            Extensions_GL_priv::_glUniform1f(location, v1);
        }
    }

    GLint Extensions_GL::glGetAttribLocation(GLint location, const GLchar *name)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetAttribLocation)))
        {
            return Extensions_GL_priv::_glGetAttribLocation(location, name);

//            GLint r = Extensions_GL_priv::_glGetAttribLocation(location, name);
//            std::cout << "glGetAttribLocation(" << location << ", " << name << ") : " << r << std::endl;
//            CONTROLE_ERRGL;
//            return r;
        }
        return -1;
    }

    void Extensions_GL::glEnableVertexAttribArray(GLint index)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glEnableVertexAttribArray)))
        {
            Extensions_GL_priv::_glEnableVertexAttribArray(index);
        }
    }

    void Extensions_GL::glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glVertexAttribPointer)))
        {
            Extensions_GL_priv::_glVertexAttribPointer(index, size, type, normalized, stride, pointer);
        }
    }

    void Extensions_GL::glDisableVertexAttribArray(GLuint index)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glDisableVertexAttribArray)))
        {
            Extensions_GL_priv::_glDisableVertexAttribArray(index);
        }
    }

    GLboolean Extensions_GL::glIsShader(GLuint shader)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glIsShader)))
        {
            return Extensions_GL_priv::_glIsShader(shader);
        }
        return GL_FALSE;
    }

    GLboolean Extensions_GL::glIsProgram(GLuint program)
    {
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glIsProgram)))
        {
            return Extensions_GL_priv::_glIsProgram(program);
        }
        return GL_FALSE;
    }

    void Extensions_GL::glGenBuffers(GLsizei n, GLuint *buffers)
    {
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGenBuffersARB)))
        {
            Extensions_GL_priv::_glGenBuffersARB(n, buffers);
        }
#else
        ::glGenBuffers(n, buffers);
#endif // GETPROC_VBO
    }

    void Extensions_GL::glBindBuffer(GLenum target, GLuint buffer)
    {
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glBindBufferARB)))
        {
            Extensions_GL_priv::_glBindBufferARB(target, buffer);
        }
#else
        ::glBindBuffer(target, buffer);
#endif // GETPROC_VBO
    }

    void Extensions_GL::glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)
    {
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glBufferDataARB)))
        {
            Extensions_GL_priv::_glBufferDataARB(target, size, data, usage);
        }
#else
        ::glBufferData(target, size, data, usage);
#endif // GETPROC_VBO
    }

    void Extensions_GL::glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data)
    {
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glBufferSubDataARB)))
        {
            Extensions_GL_priv::_glBufferSubDataARB(target, offset, size, data);
        }
#else
        ::glBufferSubData(target, offset, size, data);
#endif // GETPROC_VBO
    }

    void Extensions_GL::glDeleteBuffers(GLsizei n, const GLuint *buffers)
    {
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glDeleteBuffersARB)))
        {
            Extensions_GL_priv::_glDeleteBuffersARB(n, buffers);
        }
#else
        ::glDeleteBuffers(n, buffers);
#endif // GETPROC_VBO
    }

    void Extensions_GL::glGetBufferParameteriv(GLenum target, GLenum pname, GLint *params)
    {
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetBufferParameterivARB)))
        {
            Extensions_GL_priv::_glGetBufferParameterivARB(target, pname, params);
        }
#else
        ::glGetBufferParameteriv(target, pname, params);
#endif // GETPROC_VBO
    }

    GLvoid* Extensions_GL::glMapBuffer(GLenum target, GLenum access)
    {
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glMapBufferARB)))
        {
            return Extensions_GL_priv::_glMapBufferARB(target, access);
        }
        return nullptr;
#else
        return ::glMapBuffer(target, access);
#endif // GETPROC_VBO
    }

    GLboolean Extensions_GL::glUnmapBuffer(GLenum target)
    {
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glUnmapBufferARB)))
        {
            return Extensions_GL_priv::_glUnmapBufferARB(target);
        }
        return GL_FALSE;
#else
        return ::glUnmapBuffer(target);
#endif // GETPROC_VBO
    }

    GLboolean Extensions_GL::glIsBuffer(GLuint buffer)
    {
#ifdef GETPROC_VBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glIsBuffer)))
        {
            return Extensions_GL_priv::_glIsBuffer(buffer);
        }
        return GL_FALSE;
#else
        return ::glIsBuffer(buffer);
#endif // GETPROC_VBO
    }

    void Extensions_GL::glBindVertexArray(GLuint array)
    {
#ifdef GETPROC_VBA
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glBindVertexArrayARB)))
        {
            Extensions_GL_priv::_glBindVertexArrayARB(array);
        }
#else
        ::glBindVertexArray(array);
#endif // GETPROC_VBA
    }

    void Extensions_GL::glDeleteVertexArrays(GLsizei n, const GLuint *arrays)
    {
#ifdef GETPROC_VBA
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glDeleteVertexArraysARB)))
        {
            Extensions_GL_priv::_glDeleteVertexArraysARB(n, arrays);
        }
#else
        ::glDeleteVertexArrays(n, arrays);
#endif // GETPROC_VBA
    }

    void Extensions_GL::glGenVertexArrays(GLsizei n, GLuint *arrays)
    {
#ifdef GETPROC_VBA
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGenVertexArraysARB)))
        {
            Extensions_GL_priv::_glGenVertexArraysARB(n, arrays);
        }
#else
        ::glGenVertexArrays(n, arrays);
#endif // GETPROC_VBA
    }

    GLboolean Extensions_GL::glIsVertexArray(GLuint array)
    {
#ifdef GETPROC_VBA
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glIsVertexArrayARB)))
        {
            return Extensions_GL_priv::_glIsVertexArrayARB(array);
        }
        return GL_FALSE;
#else
        return ::glIsVertexArray(array);
#endif // GETPROC_VBA
    }


    GLboolean Extensions_GL::glIsRenderbuffer (GLuint renderbuffer)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glIsRenderbuffer)))
        {
            return Extensions_GL_priv::_glIsRenderbuffer(renderbuffer);
        }
        return GL_FALSE;
#else
        return ::glIsRenderbuffer(renderbuffer);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glBindRenderbuffer (GLenum target, GLuint renderbuffer)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glBindRenderbuffer)))
        {
            Extensions_GL_priv::_glBindRenderbuffer(target, renderbuffer);
        }
#else
        ::glBindRenderbuffer(target, renderbuffer);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glDeleteRenderbuffers)))
        {
            Extensions_GL_priv::_glDeleteRenderbuffers(n, renderbuffers);
        }
#else
        ::glDeleteRenderbuffers(n, renderbuffers);
#endif // GETPROC_VBO
    }

    void Extensions_GL::glGenRenderbuffers (GLsizei n, GLuint *renderbuffers)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGenRenderbuffers)))
        {
            Extensions_GL_priv::_glGenRenderbuffers(n, renderbuffers);
        }
#else
        ::glGenRenderbuffers(n, renderbuffers);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glRenderbufferStorage)))
        {
            Extensions_GL_priv::_glRenderbufferStorage(target, internalformat, width, height);
        }
#else
        ::glRenderbufferStorage(target, internalformat, width, height);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetRenderbufferParameteriv)))
        {
            Extensions_GL_priv::_glGetRenderbufferParameteriv(target, pname, params);
        }
#else
        ::glGetRenderbufferParameteriv(target, pname, params);
#endif // GETPROC_FBO
    }

    GLboolean Extensions_GL::glIsFramebuffer (GLuint framebuffer)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glIsFramebuffer)))
        {
            return Extensions_GL_priv::_glIsFramebuffer(framebuffer);
        }
        return GL_FALSE;
#else
        return ::glIsFramebuffer(framebuffer);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glBindFramebuffer (GLenum target, GLuint framebuffer)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glBindFramebuffer)))
        {
            Extensions_GL_priv::_glBindFramebuffer(target, framebuffer);
        }
#else
        ::glBindFramebuffer(target, framebuffer);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glDeleteFramebuffers)))
        {
            Extensions_GL_priv::_glDeleteFramebuffers(n, framebuffers);
        }
#else
        ::glDeleteFramebuffers(n, framebuffers);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glGenFramebuffers (GLsizei n, GLuint *framebuffers)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGenFramebuffers)))
        {
            Extensions_GL_priv::_glGenFramebuffers(n, framebuffers);
        }
#else
        ::glGenFramebuffers(n, framebuffers);
#endif // GETPROC_FBO
    }

    GLenum Extensions_GL::glCheckFramebufferStatus (GLenum target)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glCheckFramebufferStatus)))
        {
            return Extensions_GL_priv::_glCheckFramebufferStatus(target);
        }
        return GL_FALSE;
#else
        return ::glCheckFramebufferStatus(target);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glFramebufferTexture1D)))
        {
            Extensions_GL_priv::_glFramebufferTexture1D(target, attachment, textarget, texture, level);
        }
#else
        ::glFramebufferTexture1D(target, attachment, textarget, texture, level);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glFramebufferTexture2D)))
        {
            Extensions_GL_priv::_glFramebufferTexture2D(target, attachment, textarget, texture, level);
        }
#else
        ::glFramebufferTexture2D(target, attachment, textarget, texture, level);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glFramebufferTexture3D)))
        {
            Extensions_GL_priv::_glFramebufferTexture3D(target, attachment, textarget, texture, level, zoffset);
        }
#else
        ::glFramebufferTexture3D(target, attachment, textarget, texture, level, zoffset);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glFramebufferRenderbuffer)))
        {
            Extensions_GL_priv::_glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
        }
#else
        ::glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetFramebufferAttachmentParameteriv)))
        {
            Extensions_GL_priv::_glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
        }
#else
        ::glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
#endif // GETPROC_FBO
    }

    void Extensions_GL::glGenerateMipmap (GLenum target)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGenerateMipmap)))
        {
            Extensions_GL_priv::_glGenerateMipmap(target);
        }
#else
        ::glGenerateMipmap(target);
#endif // GETPROC_FBO
    }

#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
    void Extensions_GL::glBindFragDataLocation(GLuint program, GLuint color, const GLchar *name)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glBindFragDataLocation)))
        {
            return Extensions_GL_priv::_glBindFragDataLocation(program, color, name);
        }
#else
        ::glBindFragDataLocation(program, color, name);
#endif // GETPROC_FBO
    }

    GLint Extensions_GL::glGetFragDataLocation(GLuint program, const GLchar *name)
    {
#ifdef GETPROC_FBO
        if (Extensions_GL_priv::ValideFctExtension(reinterpret_cast<void*>(Extensions_GL_priv::_glGetFragDataLocation)))
        {
            return Extensions_GL_priv::_glGetFragDataLocation(program, name);
        }
        return -1;
#else
        return ::glGetFragDataLocation(program, name);
#endif // GETPROC_FBO
    }
#endif // FORCE_VERSION_GL


} // namespace PGL

namespace PGL
{
    Shader_GL::Shader_GL(GLenum type) : type_shader(type), valide_compilation(false)
    {
        if (Extensions_GL::SupportShaders())
        {
            shader = Extensions_GL::glCreateShader(type);
        }
        else
        {
            shader = 0;
        }
    }

    Shader_GL::~Shader_GL()
    {
        if (shader)
        {
            Extensions_GL::glDeleteShader(shader);
        }
    }

    bool Shader_GL::Reactive()
    {
        if (Extensions_GL::SupportShaders())
        {
            Desactive();
            shader = Extensions_GL::glCreateShader(type_shader);
            return true;
        }
        shader = 0;
        return false;
    }

    bool Shader_GL::Desactive()
    {
        if (shader)
        {
            Extensions_GL::glDeleteShader(shader);
            shader = 0;
            return true;
        }
        return false;
    }

    bool Shader_GL::ChargeSource(const char *s)
    {
        if (!ValideSupport())
        {
            return false;
        }

        journal_comp.clear();

        Extensions_GL::glShaderSource(shader, 1, (const GLchar * const *) &s, nullptr);
        Extensions_GL::glCompileShader(shader);

        GLint compile_status = GL_TRUE;
        Extensions_GL::glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);

        valide_compilation = (compile_status == GL_TRUE);

        if (!valide_compilation)
        {
            GLint taille_log = 0;
            Extensions_GL::glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &taille_log);
            char *log = new char[taille_log+1];
            log[taille_log] = '\0';
            Extensions_GL::glGetShaderInfoLog(shader, taille_log, &taille_log, log);

#ifdef DEBUG
            std::cout << "Erreur de compilation d'un shader :: " << log << std::endl;
#endif // DEBUG
            if (taille_log > 1)
            {
                journal_comp += log;
            }
            delete[] log;
        }

        return valide_compilation;
    }
} // namespace PGL

namespace PGL
{
    Program_GL::Program_GL() : vertex_shader(GL_VERTEX_SHADER), pixel_shader(GL_FRAGMENT_SHADER), valide_liage(false)
    {
        if (Extensions_GL::SupportShaders())
        {
            program = Extensions_GL::glCreateProgram();
        }
        else
        {
            program = 0;
        }
    }

    Program_GL::~Program_GL()
    {
        Extensions_GL::glDeleteProgram(program);
    }

    bool Program_GL::Reactive()
    {
        if (Extensions_GL::SupportShaders())
        {
            Desactive(); /* Au cas où... */
            vertex_shader.Reactive();
            pixel_shader.Reactive();
            program = Extensions_GL::glCreateProgram();

//            std::cout << "Reactive shader : " << program << std::endl;
//            CONTROLE_ERRGL;

            return true;
        }

//        std::cout << "Reactive shader : pas de support ?!" << std::endl;

        program = 0;
        return false;
    }

    bool Program_GL::Desactive()
    {
        DesactivePrograms();

        if (vertex_shader.ValideSupport())
        {
            Extensions_GL::glDetachShader(program, vertex_shader.Shader());
            vertex_shader.Desactive();
        }

        if (pixel_shader.ValideSupport())
        {
            Extensions_GL::glDetachShader(program, pixel_shader.Shader());
            pixel_shader.Desactive();
        }

        if (valide_liage)
        {
            valide_liage = false;
        }
        if (program != 0)
        {
            Extensions_GL::glDeleteProgram(program);
            program = 0;
            return true;
        }
        return false;
    }

    bool Program_GL::AjoutVertexShader(const char *source)
    {
        if (!ValideSupport())
        {
            return false;
        }

        if (!vertex_shader.ValideSupport())
        {
            vertex_shader = Shader_GL(GL_VERTEX_SHADER);
        }
        else if (vertex_shader.ValideCompilation())
        {
            Extensions_GL::glDetachShader(program, vertex_shader.Shader());
        }

        if (vertex_shader.ChargeSource(source))
        {
            Extensions_GL::glAttachShader(program, vertex_shader.Shader());
            return true;
        }
        return false;
    }

    bool Program_GL::AjoutPixelShader(const char *source)
    {
        if (!ValideSupport())
        {
            return false;
        }

        if (!pixel_shader.ValideSupport())
        {
            pixel_shader = Shader_GL(GL_FRAGMENT_SHADER);
        }
        else if (pixel_shader.ValideCompilation())
        {
            Extensions_GL::glDetachShader(program, pixel_shader.Shader());
        }

        if (pixel_shader.ChargeSource(source))
        {
            Extensions_GL::glAttachShader(program, pixel_shader.Shader());
            return true;
        }
        return false;
    }

    bool Program_GL::AjoutPixelShaderFichier(const char *chemin)
    {
        char *t = ChargeFichier(chemin);

        if (Ptest_ptr(t))
        {
            bool valide = AjoutPixelShader(t);
            delete[] t;
            return valide;
        }
        return false;
    }

    bool Program_GL::AjoutVertexShaderFichier(const char *chemin)
    {
        char *t = ChargeFichier(chemin);

        if (Ptest_ptr(t))
        {
            bool valide = AjoutVertexShader(t);
            delete[] t;
            return valide;
        }
        return false;
    }

    bool Program_GL::LinkProgram()
    {
        journal_liage.clear();

        if (program == 0)
        {
            return false;
        }

        GLint resultat = GL_TRUE;

        Extensions_GL::glProgramParameteri(program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);

        Extensions_GL::glLinkProgram(program);
        Extensions_GL::glGetProgramiv(program, GL_LINK_STATUS, &resultat);
        valide_liage = (resultat == GL_TRUE);

        if (!valide_liage)
        {
            GLint taille_log = 0;
            Extensions_GL::glGetProgramiv(program, GL_INFO_LOG_LENGTH, &taille_log);
            char *log = new char[taille_log+1];
            log[taille_log] = '\0';
            Extensions_GL::glGetProgramInfoLog(program, taille_log, &taille_log, log);

#ifdef DEBUG
            std::cout << "Erreur de liage d'un program :: " << log << ":" << resultat << std::endl;
#endif // DEBUG
            if (taille_log > 1)
            {
                journal_liage += log;
            }

            delete[] log;
            return false;
        }

        if (valide_liage)
        {
            Extensions_GL::glValidateProgram(program);
            Extensions_GL::glGetProgramiv(program, GL_VALIDATE_STATUS, &resultat);
            valide_liage = (resultat == GL_TRUE);
        }

#ifdef DEBUG
        if (!valide_liage)
        {
            GLint taille_log = 0;
            Extensions_GL::glGetProgramiv(program, GL_INFO_LOG_LENGTH, &taille_log);
            char *log = new char[taille_log+1];
            log[taille_log] = '\0';
            Extensions_GL::glGetProgramInfoLog(program, taille_log, &taille_log, log);

            std::cout << "Erreur de validation d'un program :: " << log << std::endl;

            if (taille_log > 1)
                journal_liage += log;
            delete[] log;
            return false;
        }
#endif // DEBUG

        return valide_liage;
    }

    bool Program_GL::DumpBinaire(const std::string &chemin, pint type) const
    /* Dump mémoire du shader compilé */
    {
        if (!ValideLiage())
        {
            return false;
        }

        std::ofstream fichier(chemin);
        if (!fichier.is_open())
        {
            return false;
        }

//        UseProgram();

//        const GLint taille_max = (1024*1024)*3; /* 3Mo */
//        const GLint taille_m_vs = taille_max;
//        const GLint taille_m_ps = taille_max;

        GLint taille_m_vs = 0;
        Extensions_GL::glGetProgramiv(vertex_shader.Shader(), GL_PROGRAM_BINARY_LENGTH, &taille_m_vs);

        GLint num_formats_vs = 0;
        glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats_vs);
        GLenum *formats_vs = new GLenum[num_formats_vs];

        char *binaire_vs = new char[taille_m_vs+1];
        GLint taille_vs=0;
        Extensions_GL::glGetProgramBinary(vertex_shader.Shader(), taille_m_vs, &taille_vs, formats_vs, binaire_vs);

        GLint num_formats_ps = 0;
        glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats_ps);
        GLenum *formats_ps = new GLenum[num_formats_ps];

        GLint taille_m_ps = 0;
        Extensions_GL::glGetProgramiv(pixel_shader.Shader(), GL_PROGRAM_BINARY_LENGTH, &taille_m_ps);
        char *binaire_ps = new char[taille_m_ps+1];
        GLint taille_ps=0;
        Extensions_GL::glGetProgramBinary(pixel_shader.Shader(), taille_m_ps, &taille_ps, formats_ps, binaire_ps);

        bool valide_liage_x = true;
        if (glGetError() == GL_INVALID_OPERATION)
        {
            GLint resultat_liage=0;
            Extensions_GL::glGetProgramiv(program, GL_LINK_STATUS, &resultat_liage);
            valide_liage_x = (resultat_liage == GL_TRUE);

#ifdef DEBUG
            std::cerr << "Program_GL::DumpBinaire :: Opération invalide ?! : ((" << taille_m_vs << " < " << taille_vs << "), (" << taille_m_ps << " < " << taille_ps << ")) || (" << valide_liage << " & " << valide_liage_x << ")" << std::endl;
#endif // DEBUG
            if (!valide_liage_x)
            {
                fichier.close();
                delete[] formats_vs;
                delete[] formats_ps;
                delete[] binaire_vs;
                delete[] binaire_ps;
                return false;
            }
        }

        const pint nombre_elements_entete = 8;

        /* Ecriture de l'entête: */
        GLint i_v = sizeof(GLint)*nombre_elements_entete; /* Taille de l'entête */
        fichier.write((char *)&i_v, sizeof(GLint));
        i_v = type; /* Type (1=program) */
        fichier.write((char *)&i_v, sizeof(GLint));
        i_v = 0; /* Réservé */
        fichier.write((char *)&i_v, sizeof(GLint));
        i_v = 0; /* Réservé */
        fichier.write((char *)&i_v, sizeof(GLint));
        i_v = 0; /* Réservé */
        fichier.write((char *)&i_v, sizeof(GLint));
        i_v = 0; /* Réservé */
        fichier.write((char *)&i_v, sizeof(GLint));
        i_v = 0; /* Réservé */
        fichier.write((char *)&i_v, sizeof(GLint));
        i_v = 0; /* Réservé */
        fichier.write((char *)&i_v, sizeof(GLint));

        /* Ecriture du bloc Vertex Shader */
        {
            /* Ecriture du dump mémoire Vertex Shader: */
            if (taille_vs)
            {
                i_v = taille_vs+sizeof(GLint); /* Taille du vertex shader + informations de format */
                fichier.write((char *)&i_v, sizeof(GLint));
                i_v = formats_vs[0]; /* Format du vertex shader */
                fichier.write((char *)&i_v, sizeof(GLint));
                fichier.write(binaire_vs, taille_vs);
            }
            else
            {
                i_v = sizeof(GLint);
                fichier.write((char *)&i_v, sizeof(GLint));
            }
        }

        /* Ecriture du bloc Pixel Shader */
        {
            if (taille_ps)
            {
                i_v = taille_ps+sizeof(GLint); /* Taille du pixel shader + informations de format */
                fichier.write((char *)&i_v, sizeof(GLint));
                i_v = formats_ps[0]; /* Format du pixel shader */
                fichier.write((char *)&i_v, sizeof(GLint));
                fichier.write(binaire_ps, taille_ps);
            }
            else
            {
                i_v = sizeof(GLint);
                fichier.write((char *)&i_v, sizeof(GLint));
            }
        }
        fichier.close();

#ifdef DEBUG
        std::cout << "Program_GL::DumpBinaire(" << chemin << ", " << type << ") : " << taille_vs << "(" << taille_m_vs << ")" << " : " << taille_ps << " (" << taille_m_ps << ")" << std::endl;
        std::cout << "      Formats : " << num_formats_vs << ":" << num_formats_ps << std::endl;
#endif // DEBUG

//        DesactivePrograms();

        delete[] formats_vs;
        delete[] formats_ps;
        delete[] binaire_vs;
        delete[] binaire_ps;
        return (valide_liage_x && (taille_vs || taille_ps));
    }

    bool Program_GL::UseProgram() const
    {
        if (valide_liage && program != 0)
        {
            Extensions_GL::glUseProgram(program);
            return true;
        }
        return false;
    }

    void Program_GL::DesactivePrograms()
    {
        Extensions_GL::glUseProgram(0);
    }

    GLint Program_GL::ReservationUniform(const char *nom) const
    {
//        std::cout << "Réservation uniform : " << nom << " : Valide liage : " << valide_liage << std::endl;

        if (valide_liage)
        {
            return Extensions_GL::glGetUniformLocation(program, nom);
        }
        return -1;
    }

    bool Program_GL::ActivationUniformMatrix(GLint id, const PGL::PMatriceGL4 &m) const
    {
        if (valide_liage)
        {
            if (id != -1)
            {
                Extensions_GL::glUniformMatrix4fv(id, 1, false, m.Tableau());
                return true;
            }
        }
        return false;
    }

    bool Program_GL::ActivationUniformVect(GLint id, const PGL::PVertex_GL &v) const
    {
        if (valide_liage)
        {
            if (id != -1)
            {
//                glUniform3fv(id, 1, v.Tableau());
                Extensions_GL::glUniform3f(id, v.X(), v.Y(), v.Z());
                return true;
            }
        }
        return false;
    }

    bool Program_GL::ActivationUniformInt(GLint id, int v) const
    {
        if (valide_liage)
        {
            if (id != -1)
            {
                Extensions_GL::glUniform1i(id, GLint(v));
                return true;
            }
        }
        return false;
    }

    bool Program_GL::ActivationUniformFloat(GLint id, float v) const
    {
        if (valide_liage)
        {
            if (id != -1)
            {
                Extensions_GL::glUniform1f(id, GLfloat(v));
                return true;
            }
        }
        return false;
    }

    GLint Program_GL::ReservationAttribut(const char *nom) const
    {
        if (valide_liage)
        {
            return Extensions_GL::glGetAttribLocation(program, nom);
        }
        return -1;
    }

    bool Program_GL::ActivationTableauAttributs2F(GLint index, const GLvoid *t) const
    {
        if (index < 0)
        {
            return false;
        }
        Extensions_GL::glEnableVertexAttribArray(index);
        Extensions_GL::glVertexAttribPointer(index, 2, GLcoord_type, GL_FALSE, 0, t);
        return true;
    }

    bool Program_GL::ActivationTableauAttributs3F(GLint index, const GLvoid *t) const
    {
        if (index < 0)
            return false;
        Extensions_GL::glEnableVertexAttribArray(index);
        Extensions_GL::glVertexAttribPointer(index, 3, GLcoord_type, GL_FALSE, 0, t);
        return true;
    }

    bool Program_GL::ActivationTableauAttributs4F(GLint index, const GLvoid *t) const
    {
        if (index < 0)
        {
            return false;
        }
        Extensions_GL::glEnableVertexAttribArray(index);
        Extensions_GL::glVertexAttribPointer(index, 4, GLcoord_type, GL_FALSE, 0, t);
        return true;
    }

    bool Program_GL::ActivationTableauAttributs3B(GLint index, const GLvoid *t) const
    {
        if (index < 0)
        {
            return false;
        }
        Extensions_GL::glEnableVertexAttribArray(index);
        Extensions_GL::glVertexAttribPointer(index, 4, GL_BYTE, GL_FALSE, 0, t);
        return true;
    }

    bool Program_GL::DesactiveAttribut(GLint index) const
    {
        if (index < 0)
        {
            return false;
        }
        Extensions_GL::glDisableVertexAttribArray(index);
        //    index = -1;
        return true;
    }

    char * Program_GL::ChargeFichier(const char *chemin)
    {
        if (!Ptest_ptr(chemin))
        {
            return nullptr;
        }
        std::ifstream fichier(chemin, std::ifstream::in | std::ifstream::binary);
        if (!fichier.is_open())
        {
#ifdef DEBUG
            std::cout << "Program_GL::ChargeFichier(" << chemin << ") :: Impossible d'ouvrir le fichier." << std::endl;
#endif // DEBUG
            return nullptr;
        }

        /* Recupère la taille du fichier: */
        fichier.seekg(0, fichier.end);
        pint taille_tampon_comp = fichier.tellg();
        fichier.seekg(0, fichier.beg);

        char *tampon_source = new char [taille_tampon_comp];

        fichier.read(tampon_source, taille_tampon_comp);

        fichier.close();
        return tampon_source;
    }
} // namespace PGL

namespace PGL
{
    Uniform_GL::Uniform_GL(const Program_GL &prog) : program(prog), uniform(-1)
    {
    }

    bool Uniform_GL::Valide() const
    {
        return (program.ValideLiage()) && (uniform != -1);
    }

    bool Uniform_GL::Reservation(const char *nom)
    /* Réservation du mot et liage. */
    {
        if (program.ValideLiage() && uniform == -1)
        {
            uniform = program.ReservationUniform(nom);
#ifdef DEBUG_GLSL
            std::cout << "Réservation uniform : " << nom << " : " << uniform << std::endl;
#endif // DEBUG_GLSL
            return uniform != -1;
        }
#ifdef DEBUG_GLSL
            std::cout << "Tentative de réservation uniform : " << nom << " : " << program.ValideLiage() << "&" << uniform << std::endl;
#endif // DEBUG_GLSL
        return false;
    }

    bool Uniform_GL::Desactive()
    {
        if (Valide())
        {
//            return program.DesactiveAttribut(uniform);
            uniform = -1;
            return true;
        }
        return false;
    }

    bool Uniform_GL::Activation(const PGL::PMatriceGL4 &matice) const
    {
//        std::cout << "Activation uniform (Matrice): " << Valide() << " : " << uniform << std::endl;

        if (Valide())
        {
            return program.ActivationUniformMatrix(uniform, matice);
        }
        return false;
    }

    bool Uniform_GL::Activation(const PGL::PVertex_GL &vertex) const
    {
//        std::cout << "Activation uniform (Vertex): " << Valide() << " : " << uniform << std::endl;

        if (Valide())
        {
            return program.ActivationUniformVect(uniform, vertex);
        }
        return false;
    }

    bool Uniform_GL::Activation(float f) const
    {
        if (Valide())
        {
            return program.ActivationUniformFloat(uniform, f);
        }
        return false;
    }

    bool Uniform_GL::Activation(int i) const
    {
        if (Valide())
        {
            return program.ActivationUniformInt(uniform, i);
        }
        return false;
    }

    Attribute_GL::Attribute_GL(const Program_GL &prog) : program(prog), attribute(-1)
    {
    }

    bool Attribute_GL::Valide() const
    {
        return (program.ValideLiage()) && (attribute != -1);
    }

    bool Attribute_GL::Desactive() const
    {
        if (Valide())
        {
            return program.DesactiveAttribut(attribute);
        }
        return false;
    }

    bool Attribute_GL::Reservation(const char *nom)
    {
        if (program.ValideLiage() && attribute == -1)
        {
            attribute = program.ReservationAttribut(nom);
#ifdef DEBUG_GLSL
            std::cout << "Réservation attribut : " << nom << " : " << attribute << std::endl;
#endif // DEBUG_GLSL
            return attribute != -1;
        }
        return false;
    }

    bool Attribute_GL::Activation(const PVectVert_GL &vertices) const
    {
//        std::cout << "Activation attribut (vertices) : " << Valide() << std::endl;
        if (Valide())
            return program.ActivationTableauAttributs3F(attribute, vertices.data());
        return false;
    }

    bool Attribute_GL::Activation(const PVectCouleur_GL &couleurs) const
    {
        if (Valide())
            return program.ActivationTableauAttributs4F(attribute, couleurs.data());
        return false;
    }
} // namespace PGL

namespace PGL
{
    GLcoord PGL::PMatriceGL4::facteur(GLcoord m0, GLcoord m1, GLcoord m2, GLcoord m3, GLcoord m4, GLcoord m5, GLcoord m6, GLcoord m7, GLcoord m8)
    {
        return m0 * (m4 * m8 - m5 * m7) - m1 * (m3 * m8 - m5 * m6) + m2 * (m3 * m7 - m4 * m6);
    }

    PMatriceGL4::PMatriceGL4()
    {
        Identite();
    }

    PMatriceGL4::PMatriceGL4(GLcoord m00, GLcoord m01, GLcoord m02, GLcoord m03, GLcoord m04, GLcoord m05, GLcoord m06, GLcoord m07, GLcoord m08, GLcoord m09, GLcoord m10, GLcoord m11, GLcoord m12, GLcoord m13, GLcoord m14, GLcoord m15)
    {
        matrice[0] = m00;  matrice[1] = m01;  matrice[2] = m02;  matrice[3] = m03;
        matrice[4] = m04;  matrice[5] = m05;  matrice[6] = m06;  matrice[7] = m07;
        matrice[8] = m08;  matrice[9] = m09;  matrice[10]= m10;  matrice[11]= m11;
        matrice[12]= m12;  matrice[13]= m13;  matrice[14]= m14;  matrice[15]= m15;
    }

    PMatriceGL4::PMatriceGL4(const GLcoord m[16])
    {
        matrice[0] = m[0];  matrice[1] = m[1];  matrice[2] = m[2];  matrice[3] = m[3];
        matrice[4] = m[4];  matrice[5] = m[5];  matrice[6] = m[6];  matrice[7] = m[7];
        matrice[8] = m[8];  matrice[9] = m[9];  matrice[10]= m[10];  matrice[11]= m[11];
        matrice[12]= m[12];  matrice[13]= m[13];  matrice[14]= m[14];  matrice[15]= m[15];
    }

    void PGL::PMatriceGL4::Copie(double *m) const
    {
        m[0] = matrice[0];  m[1] = matrice[1];  m[2] = matrice[2];  m[3] = matrice[3];
        m[4] = matrice[4];  m[5] = matrice[5];  m[6] = matrice[6];  m[7] = matrice[7];
        m[8] = matrice[8];  m[9] = matrice[9];  m[10]= matrice[10];  m[11]= matrice[11];
        m[12]= matrice[12];  m[13]= matrice[13];  m[14]= matrice[14];  m[15]= matrice[15];
    }

    void PGL::PMatriceGL4::Copie(float *m) const
    {
        m[0] = matrice[0];  m[1] = matrice[1];  m[2] = matrice[2];  m[3] = matrice[3];
        m[4] = matrice[4];  m[5] = matrice[5];  m[6] = matrice[6];  m[7] = matrice[7];
        m[8] = matrice[8];  m[9] = matrice[9];  m[10]= matrice[10];  m[11]= matrice[11];
        m[12]= matrice[12];  m[13]= matrice[13];  m[14]= matrice[14];  m[15]= matrice[15];
    }

    void PGL::PMatriceGL4::Identite()
    {
        matrice[0] = matrice[5] = matrice[10] = matrice[15] = 1.0f;
        matrice[1] = matrice[2] = matrice[3] = matrice[4] = matrice[6] = matrice[7] = matrice[8] = matrice[9] = matrice[11] = matrice[12] = matrice[13] = matrice[14] = 0.0f;
    }

    void PGL::PMatriceGL4::defOrtho(GLcoord gauche, GLcoord droite, GLcoord bas, GLcoord haut, GLcoord proche, GLcoord loin)
    {
        const GLcoord delta_x = (droite - gauche);
        const GLcoord delta_y = (haut - bas);
        const GLcoord delta_z = (loin - proche);
        matrice[0]  =  2.f / delta_x;
        matrice[5]  =  2.f / delta_y;
        matrice[10] = -2.f / delta_z;
        matrice[12] = -(droite + gauche) / delta_x;
        matrice[13] = -(haut + bas) / delta_y;
        matrice[14] = -(loin + proche) / delta_z;
    }

    void PGL::PMatriceGL4::Translation(GLcoord x, GLcoord y, GLcoord z)
    {
        matrice[0] += matrice[3] * x;   matrice[4] += matrice[7] * x;   matrice[8] += matrice[11] * x;   matrice[12] += matrice[15] * x;
        matrice[1] += matrice[3] * y;   matrice[5] += matrice[7] * y;   matrice[9] += matrice[11] * y;   matrice[13] += matrice[15] * y;
        matrice[2] += matrice[3] * z;   matrice[6] += matrice[7] * z;   matrice[10] += matrice[11] * z;   matrice[14] += matrice[15] * z;
    }

    void PGL::PMatriceGL4::Position(GLcoord x, GLcoord y, GLcoord z)
    {
        matrice[12] = x;
        matrice[13] = y;
        matrice[14] = z;
    }

    void PGL::PMatriceGL4::Echelle(GLcoord n)
    {
        matrice[0] *= n;   matrice[4] *= n;   matrice[8] *= n;   matrice[12] *= n;
        matrice[1] *= n;   matrice[5] *= n;   matrice[9] *= n;   matrice[13] *= n;
        matrice[2] *= n;   matrice[6] *= n;   matrice[10]*= n;   matrice[14] *= n;
    }

    void PGL::PMatriceGL4::Echelle(GLcoord x, GLcoord y, GLcoord z)
    {
        matrice[0] *= x;   matrice[4] *= y;   matrice[8] *= z;   //matrice[12] *= x;
        matrice[1] *= x;   matrice[5] *= y;   matrice[9] *= z;   //matrice[13] *= y;
        matrice[2] *= x;   matrice[6] *= y;   matrice[10]*= z;   //matrice[14] *= z;
    }

    void PGL::PMatriceGL4::Inverse() // Inversion générique
    {
        pfloat cf0 = facteur(matrice[5],matrice[6],matrice[7], matrice[9],matrice[10],matrice[11], matrice[13],matrice[14],matrice[15]);
        pfloat cf1 = facteur(matrice[4],matrice[6],matrice[7], matrice[8],matrice[10],matrice[11], matrice[12],matrice[14],matrice[15]);
        pfloat cf2 = facteur(matrice[4],matrice[5],matrice[7], matrice[8],matrice[9], matrice[11], matrice[12],matrice[13],matrice[15]);
        pfloat cf3 = facteur(matrice[4],matrice[5],matrice[6], matrice[8],matrice[9], matrice[10], matrice[12],matrice[13],matrice[14]);

        pfloat determinant = matrice[0] * cf0 - matrice[1] * cf1 + matrice[2] * cf2 - matrice[3] * cf3;
        if(POSITIFD(determinant) <= EPSILON_FIXE)
        {
            Identite();
            return;
        }

        pfloat cf4 =  facteur(matrice[1],matrice[2],matrice[3], matrice[9],matrice[10],matrice[11], matrice[13],matrice[14],matrice[15]);
        pfloat cf5 =  facteur(matrice[0],matrice[2],matrice[3], matrice[8],matrice[10],matrice[11], matrice[12],matrice[14],matrice[15]);
        pfloat cf6 =  facteur(matrice[0],matrice[1],matrice[3], matrice[8],matrice[9], matrice[11], matrice[12],matrice[13],matrice[15]);
        pfloat cf7 =  facteur(matrice[0],matrice[1],matrice[2], matrice[8],matrice[9], matrice[10], matrice[12],matrice[13],matrice[14]);

        pfloat cf8 =  facteur(matrice[1],matrice[2],matrice[3], matrice[5],matrice[6], matrice[7],  matrice[13],matrice[14],matrice[15]);
        pfloat cf9 =  facteur(matrice[0],matrice[2],matrice[3], matrice[4],matrice[6], matrice[7],  matrice[12],matrice[14],matrice[15]);
        pfloat cf10 = facteur(matrice[0],matrice[1],matrice[3], matrice[4],matrice[5], matrice[7],  matrice[12],matrice[13],matrice[15]);
        pfloat cf11 = facteur(matrice[0],matrice[1],matrice[2], matrice[4],matrice[5], matrice[6],  matrice[12],matrice[13],matrice[14]);

        pfloat cf12 = facteur(matrice[1],matrice[2],matrice[3], matrice[5],matrice[6], matrice[7],  matrice[9], matrice[10],matrice[11]);
        pfloat cf13 = facteur(matrice[0],matrice[2],matrice[3], matrice[4],matrice[6], matrice[7],  matrice[8], matrice[10],matrice[11]);
        pfloat cf14 = facteur(matrice[0],matrice[1],matrice[3], matrice[4],matrice[5], matrice[7],  matrice[8], matrice[9], matrice[11]);
        pfloat cf15 = facteur(matrice[0],matrice[1],matrice[2], matrice[4],matrice[5], matrice[6],  matrice[8], matrice[9], matrice[10]);

        pfloat inv = 1. / determinant;
        matrice[0] =  inv * cf0;
        matrice[1] = -inv * cf4;
        matrice[2] =  inv * cf8;
        matrice[3] = -inv * cf12;

        matrice[4] = -inv * cf1;
        matrice[5] =  inv * cf5;
        matrice[6] = -inv * cf9;
        matrice[7] =  inv * cf13;

        matrice[8] =  inv * cf2;
        matrice[9] = -inv * cf6;
        matrice[10]=  inv * cf10;
        matrice[11]= -inv * cf14;

        matrice[12]= -inv * cf3;
        matrice[13]=  inv * cf7;
        matrice[14]= -inv * cf11;
        matrice[15]=  inv * cf15;
    }

    void PGL::PMatriceGL4::Rotation(GLcoord angle, GLcoord x, GLcoord y, GLcoord z)
    /* Rotation selon l'axe. */
    {
        angle *= DTOR;
        GLcoord c = COSF(angle);
        GLcoord s = SINF(angle);
        GLcoord c1 = 1.0f - c;
        GLcoord m0 = matrice[0],  m4 = matrice[4],  m8 = matrice[8],  m12= matrice[12],
                m1 = matrice[1],  m5 = matrice[5],  m9 = matrice[9],  m13= matrice[13],
                m2 = matrice[2],  m6 = matrice[6],  m10= matrice[10], m14= matrice[14];

        GLcoord r0 = x * x * c1 + c;
        GLcoord r1 = x * y * c1 + z * s;
        GLcoord r2 = x * z * c1 - y * s;
        GLcoord r4 = x * y * c1 - z * s;
        GLcoord r5 = y * y * c1 + c;
        GLcoord r6 = y * z * c1 + x * s;
        GLcoord r8 = x * z * c1 + y * s;
        GLcoord r9 = y * z * c1 - x * s;
        GLcoord r10= z * z * c1 + c;

        matrice[0] = r0 * m0 + r4 * m1 + r8 * m2;
        matrice[1] = r1 * m0 + r5 * m1 + r9 * m2;
        matrice[2] = r2 * m0 + r6 * m1 + r10* m2;
        matrice[4] = r0 * m4 + r4 * m5 + r8 * m6;
        matrice[5] = r1 * m4 + r5 * m5 + r9 * m6;
        matrice[6] = r2 * m4 + r6 * m5 + r10* m6;
        matrice[8] = r0 * m8 + r4 * m9 + r8 * m10;
        matrice[9] = r1 * m8 + r5 * m9 + r9 * m10;
        matrice[10]= r2 * m8 + r6 * m9 + r10* m10;
        matrice[12]= r0 * m12+ r4 * m13+ r8 * m14;
        matrice[13]= r1 * m12+ r5 * m13+ r9 * m14;
        matrice[14]= r2 * m12+ r6 * m13+ r10* m14;
    }

    void PGL::PMatriceGL4::RotationX(GLcoord angle)
    {
        angle *= DTOR;
        GLcoord c = COSF(angle);
        GLcoord s = SINF(angle);
        GLcoord m1 = matrice[1],  m2 = matrice[2],
                m5 = matrice[5],  m6 = matrice[6],
                m9 = matrice[9],  m10= matrice[10],
                m13= matrice[13], m14= matrice[14];

        matrice[1] = m1 * c + m2 *-s;
        matrice[2] = m1 * s + m2 * c;
        matrice[5] = m5 * c + m6 *-s;
        matrice[6] = m5 * s + m6 * c;
        matrice[9] = m9 * c + m10*-s;
        matrice[10]= m9 * s + m10* c;
        matrice[13]= m13* c + m14*-s;
        matrice[14]= m13* s + m14* c;
    }

    void PGL::PMatriceGL4::RotationY(GLcoord angle)
    {
        angle *= DTOR;
        GLcoord c = COSF(angle);
        GLcoord s = SINF(angle);
        GLcoord m0 = matrice[0],  m2 = matrice[2],
                m4 = matrice[4],  m6 = matrice[6],
                m8 = matrice[8],  m10= matrice[10],
                m12= matrice[12], m14= matrice[14];

        matrice[0] = m0 * c + m2 * s;
        matrice[2] = m0 *-s + m2 * c;
        matrice[4] = m4 * c + m6 * s;
        matrice[6] = m4 *-s + m6 * c;
        matrice[8] = m8 * c + m10* s;
        matrice[10]= m8 *-s + m10* c;
        matrice[12]= m12* c + m14* s;
        matrice[14]= m12*-s + m14* c;
    }

    void PGL::PMatriceGL4::RotationZ(GLcoord angle)
    {
        angle *= DTOR;
        GLcoord c = COSF(angle);
        GLcoord s = SINF(angle);
        GLcoord m0 = matrice[0],  m1 = matrice[1],
                m4 = matrice[4],  m5 = matrice[5],
                m8 = matrice[8],  m9 = matrice[9],
                m12= matrice[12], m13= matrice[13];

        matrice[0] = m0 * c + m1 *-s;
        matrice[1] = m0 * s + m1 * c;
        matrice[4] = m4 * c + m5 *-s;
        matrice[5] = m4 * s + m5 * c;
        matrice[8] = m8 * c + m9 *-s;
        matrice[9] = m8 * s + m9 * c;
        matrice[12]= m12* c + m13*-s;
        matrice[13]= m12* s + m13* c;
    }

    void PGL::PMatriceGL4::RotationXYZ(GLcoord angle_x, GLcoord angle_y, GLcoord angle_z)
    {
        angle_x *= DTOR;
        angle_y *= DTOR;
        angle_z *= DTOR;

        GLcoord A       = COSF(angle_x);
        GLcoord B       = SINF(angle_x);
        GLcoord C       = COSF(angle_y);
        GLcoord D       = SINF(angle_y);
        GLcoord E       = COSF(angle_z);
        GLcoord F       = SINF(angle_z);
        GLcoord AD      =   A * D;
        GLcoord BD      =   B * D;

        matrice[0]  =  C * E;
        matrice[1]  = -C * F;
        matrice[2]  =  D;
        matrice[4]  =  BD * E + A * F;
        matrice[5]  = -BD * F + A * E;
        matrice[6]  = -B * C;
        matrice[8]  = -AD * E + B * F;
        matrice[9]  =  AD * F + B * E;
        matrice[10] =  A * C;

        matrice[3] = matrice[7] = matrice[11] = matrice[12] = matrice[13] = matrice[14] = 0;
        matrice[15]= 1;
    }

    void PGL::PMatriceGL4::Transpose()
    {
        PerspectiveSys::swap(matrice[1], matrice[4]);
        PerspectiveSys::swap(matrice[2], matrice[8]);
        PerspectiveSys::swap(matrice[3], matrice[12]);
        PerspectiveSys::swap(matrice[6], matrice[9]);
        PerspectiveSys::swap(matrice[7], matrice[13]);
        PerspectiveSys::swap(matrice[11], matrice[14]);
    }

    void PGL::PMatriceGL4::defAxeX(GLcoord x, GLcoord y, GLcoord z)
    {
        matrice[0] = x;
        matrice[1] = y;
        matrice[2] = z;
    }

    void PGL::PMatriceGL4::defAxeY(GLcoord x, GLcoord y, GLcoord z)
    {
        matrice[4] = x;
        matrice[5] = y;
        matrice[6] = z;
    }

    void PGL::PMatriceGL4::defAxeZ(GLcoord x, GLcoord y, GLcoord z)
    {
        matrice[8] = x;
        matrice[9] = y;
        matrice[10] = z;
    }

    void PGL::PMatriceGL4::defOrigine(GLcoord x, GLcoord y, GLcoord z)
    {
        matrice[12] = x;
        matrice[13] = y;
        matrice[14] = z;
    }

    PVertex_GL PGL::PMatriceGL4::AxeX() const
    {
        return PGL::PVertex_GL(matrice[0], matrice[1], matrice[2]);
    }

    PVertex_GL PGL::PMatriceGL4::AxeY() const
    {
        return PGL::PVertex_GL(matrice[4], matrice[5], matrice[6]);
    }

    PVertex_GL PGL::PMatriceGL4::AxeZ() const
    {
        return PGL::PVertex_GL(matrice[8], matrice[9], matrice[10]);
    }

    PVertex_GL PGL::PMatriceGL4::Origine() const
    {
        return PGL::PVertex_GL(matrice[12], matrice[13], matrice[14]);
    }

    PVertex_GL PGL::PMatriceGL4::operator*(const PGL::PVertex_GL& p) const
    {
        return PGL::PVertex_GL(matrice[0]*p.X() + matrice[4]*p.Y() + matrice[8]*p.Z(),
                matrice[1]*p.X() + matrice[5]*p.Y() + matrice[9]*p.Z(),
                matrice[2]*p.X() + matrice[6]*p.Y() + matrice[10]*p.Z());

//        return PGL::PVertex_GL(matrice[0]*p.X() + matrice[4]*p.Y() + matrice[8]*p.Z() + matrice[12],
//                matrice[1]*p.X() + matrice[5]*p.Y() + matrice[9]*p.Z() + matrice[13],
//                matrice[2]*p.X() + matrice[6]*p.Y() + matrice[10]*p.Z()) + matrice[14];
    }

    GLcoord PGL::PMatriceGL4::CalcW(const PGL::PVertex_GL &p) const
    {
        return (matrice[3]*p.X() + matrice[7]*p.Y() + matrice[11]*p.Z() + matrice[15]);
    }

    PMatriceGL4 PGL::PMatriceGL4::operator*(const PGL::PMatriceGL4& m) const
    {
        return PGL::PMatriceGL4(matrice[0]*m.matrice[0]  + matrice[4]*m.matrice[1]  + matrice[8]*m.matrice[2]  + matrice[12]*m.matrice[3],   matrice[1]*m.matrice[0]  + matrice[5]*m.matrice[1]  + matrice[9]*m.matrice[2]  + matrice[13]*m.matrice[3],   matrice[2]*m.matrice[0]  + matrice[6]*m.matrice[1]  + matrice[10]*m.matrice[2]  + matrice[14]*m.matrice[3],   matrice[3]*m.matrice[0]  + matrice[7]*m.matrice[1]  + matrice[11]*m.matrice[2]  + matrice[15]*m.matrice[3],
                matrice[0]*m.matrice[4]  + matrice[4]*m.matrice[5]  + matrice[8]*m.matrice[6]  + matrice[12]*m.matrice[7],   matrice[1]*m.matrice[4]  + matrice[5]*m.matrice[5]  + matrice[9]*m.matrice[6]  + matrice[13]*m.matrice[7],   matrice[2]*m.matrice[4]  + matrice[6]*m.matrice[5]  + matrice[10]*m.matrice[6]  + matrice[14]*m.matrice[7],   matrice[3]*m.matrice[4]  + matrice[7]*m.matrice[5]  + matrice[11]*m.matrice[6]  + matrice[15]*m.matrice[7],
                matrice[0]*m.matrice[8]  + matrice[4]*m.matrice[9]  + matrice[8]*m.matrice[10] + matrice[12]*m.matrice[11],  matrice[1]*m.matrice[8]  + matrice[5]*m.matrice[9]  + matrice[9]*m.matrice[10] + matrice[13]*m.matrice[11],  matrice[2]*m.matrice[8]  + matrice[6]*m.matrice[9]  + matrice[10]*m.matrice[10] + matrice[14]*m.matrice[11],  matrice[3]*m.matrice[8]  + matrice[7]*m.matrice[9]  + matrice[11]*m.matrice[10] + matrice[15]*m.matrice[11],
                matrice[0]*m.matrice[12] + matrice[4]*m.matrice[13] + matrice[8]*m.matrice[14] + matrice[12]*m.matrice[15],  matrice[1]*m.matrice[12] + matrice[5]*m.matrice[13] + matrice[9]*m.matrice[14] + matrice[13]*m.matrice[15],  matrice[2]*m.matrice[12] + matrice[6]*m.matrice[13] + matrice[10]*m.matrice[14] + matrice[14]*m.matrice[15],  matrice[3]*m.matrice[12] + matrice[7]*m.matrice[13] + matrice[11]*m.matrice[14] + matrice[15]*m.matrice[15]);
    }
} // namespace PGL

namespace PGL
{
    PTampon_GL::PTampon_GL(GLint type_entites) : type_ents(type_entites), utilise_vbo(false), utilise_vba(false), id_vbo(0), target_vbo(0), usage_vbo(0)
    {
    }

    PTampon_GL::~PTampon_GL()
    {
        if (VBOActif())
        {
            Extensions_GL::glDeleteBuffers(1, &id_vbo);
        }
    }

    bool PTampon_GL::ActiveVBO(GLenum type_usage_tampon)
    {
        if (Extensions_GL::SupportVBO())
        {
            if (VBOActif())
            {
                return true;
            }

            Extensions_GL::glGenBuffers(1, &id_vbo);
            if (id_vbo != 0)
            {
//                std::cout << "Utilisation des VBO : " << type_usage_tampon << " !!!" << std::endl;
                target_vbo = GL_ARRAY_BUFFER;
                usage_vbo = type_usage_tampon;
                utilise_vbo = true;
                return true;
            }
        }
        return false;
    }

    bool PTampon_GL::VBOActif() const
    {
//        std::cout << "VBO actif ? (" << id_vbo << ") : " << utilise_vbo << ", " << bool(Extensions_GL::glIsBuffer(id_vbo)) << std::endl;
        if (utilise_vbo && id_vbo != 0)
        {
            return Extensions_GL::glIsBuffer(id_vbo);
        }
        return false;
    }

    bool PTampon_GL::Bind() const
    {
        if (utilise_vbo && id_vbo != 0)
        {
            Extensions_GL::glBindBuffer(target_vbo, id_vbo);
            return Extensions_GL::glIsBuffer(id_vbo);
        }
        return false;
    }

    void PTampon_GL::UnBind() const
    {
        Extensions_GL::glBindBuffer(target_vbo, 0);
//        if (utilise_vbo && id_vbo != 0)
//        {
//            Extensions_GL::glBindBuffer(target_vbo, 0);
//            return true;
//        }
//        return false;
    }

    bool PTampon_GL::DesactiveVBO()
    {
        if (VBOActif())
        {
            Extensions_GL::glDeleteBuffers(1, &id_vbo);
            id_vbo = 0;
            utilise_vbo = false;
            target_vbo = 0;
            usage_vbo = 0;
            return true;
        }
        return false;
    }

    void PTampon_GL::Reinit(bool desactive_vbo)
    {
//        std::cout << "Reinit Tampon !" << std::endl;
        if (desactive_vbo)
        {
            DesactiveVBO();
        }
        tampon_couleurs.clear();
        tampon_vertices.clear();
        tampon_norms.clear();
    }

    bool PTampon_GL::Ajout(const PGL::PVertex_GL &v, const PGL::PCouleur_GL &c)
    {
        if (type_ents == GL_POINTS)
        {
            tampon_vertices.push_back(v);
            tampon_couleurs.push_back(c);
            return true;
        }
        else if (type_ents == GL_LINES)
        {
            tampon_vertices.push_back(v);
            tampon_couleurs.push_back(c);
            tampon_vertices.push_back(v);
            tampon_couleurs.push_back(c);
            return true;
        }
        else if (type_ents == GL_TRIANGLES)
        {
            tampon_vertices.push_back(v);
            tampon_couleurs.push_back(c);
            tampon_norms.push_back(PVertex_GL(0., 0., -1.));
            tampon_vertices.push_back(v);
            tampon_couleurs.push_back(c);
            tampon_norms.push_back(PVertex_GL(0., 0., -1.));
            tampon_vertices.push_back(v);
            tampon_couleurs.push_back(c);
            tampon_norms.push_back(PVertex_GL(0., 0., -1.));
            return true;
        }
        return false;
    }

    bool PTampon_GL::Ajout(const PGL::PVertex_GL &v1, const PGL::PVertex_GL &v2, const PGL::PCouleur_GL &c)
    {
        if (type_ents == GL_POINTS)
        {
            tampon_vertices.push_back(v1);
            tampon_couleurs.push_back(c);
            tampon_vertices.push_back(v2);
            tampon_couleurs.push_back(c);
        }
        else if (type_ents == GL_LINES)
        {
            tampon_vertices.push_back(v1);
            tampon_couleurs.push_back(c);
            tampon_vertices.push_back(v2);
            tampon_couleurs.push_back(c);
            return true;
        }
        else if (type_ents == GL_TRIANGLES)
        {
            tampon_vertices.push_back(v1);
            tampon_couleurs.push_back(c);
            tampon_norms.push_back(PVertex_GL(0., 0., -1.));
            tampon_vertices.push_back(v2);
            tampon_couleurs.push_back(c);
            tampon_norms.push_back(PVertex_GL(0., 0., -1.));
            tampon_vertices.push_back(v2);
            tampon_couleurs.push_back(c);
            tampon_norms.push_back(PVertex_GL(0., 0., -1.));
            return true;
        }
        return false;
    }

    bool PTampon_GL::Ajout(const PGL::PVertice2_GL &v, const PGL::PCouleur_GL &c)
    {
        return Ajout(v.V1(), v.V2(), c);
    }

    bool PTampon_GL::Ajout(const PGL::PVertex_GL &v1, const PGL::PVertex_GL &v2, const PGL::PVertex_GL &v3, const PVertex_GL &norm1, const PVertex_GL &norm2, const PVertex_GL &norm3, const PGL::PCouleur_GL &c)
    {
        if (type_ents == GL_POINTS)
        {
            tampon_vertices.push_back(v1);
            tampon_couleurs.push_back(c);
            tampon_vertices.push_back(v2);
            tampon_couleurs.push_back(c);
            tampon_vertices.push_back(v3);
            tampon_couleurs.push_back(c);
        }
        else if (type_ents == GL_LINES)
        {
            tampon_vertices.push_back(v1);
            tampon_couleurs.push_back(c);
            tampon_vertices.push_back(v2);
            tampon_couleurs.push_back(c);

            tampon_vertices.push_back(v2);
            tampon_couleurs.push_back(c);
            tampon_vertices.push_back(v3);
            tampon_couleurs.push_back(c);

            tampon_vertices.push_back(v3);
            tampon_couleurs.push_back(c);
            tampon_vertices.push_back(v1);
            tampon_couleurs.push_back(c);
            return true;
        }
        else if (type_ents == GL_TRIANGLES)
        {
            tampon_vertices.push_back(v1);
            tampon_norms.push_back(norm1);
            tampon_couleurs.push_back(c);

            tampon_vertices.push_back(v2);
            tampon_norms.push_back(norm2);
            tampon_couleurs.push_back(c);

            tampon_vertices.push_back(v3);
            tampon_norms.push_back(norm3);
            tampon_couleurs.push_back(c);
            return true;
        }
        return false;
    }

    bool PTampon_GL::Ajout(const PGL::PVertex_GL &v1, const PGL::PVertex_GL &v2, const PGL::PVertex_GL &v3, const PGL::PVertex_GL &norm, const PGL::PCouleur_GL &c)
    {
        return Ajout(v1, v2, v3, norm, norm, norm, c);
    }

    bool PTampon_GL::Ajout(const PGL::PVertice3_GL &v, const PGL::PVertex_GL &norm, const PGL::PCouleur_GL &c)
    {
        return Ajout(v.V1(), v.V2(), v.V3(), norm, c);
    }

    bool PTampon_GL::Ajout(const PGL::PVertice3_GL &v, const PGL::PCouleur_GL &c)
    {
        return Ajout(v.V1(), v.V2(), v.V3(), v.VecteurNormal(), c);
    }

    bool PTampon_GL::Transfert() const
    {
        if (Bind())
        {
            static const unsigned long mult_vertices = PGL::PVertex_GL::Taille() * sizeof(GLcoord);
            static const unsigned long mult_couleurs = PGL::PCouleur_GL::Taille() * sizeof(GLcoord);

            const unsigned long taille_vertices = tampon_vertices.size() * mult_vertices;
            const unsigned long taille_couleurs = tampon_couleurs.size() * mult_couleurs;

            if (type_ents == GL_POINTS)
            {
                Extensions_GL::glBufferData(target_vbo, taille_vertices + taille_couleurs, 0, usage_vbo);
                Extensions_GL::glBufferSubData(target_vbo, 0, taille_vertices, tampon_vertices.data());
                Extensions_GL::glBufferSubData(target_vbo, taille_vertices, taille_couleurs, tampon_couleurs.data());
                UnBind();
                return true;
            }
            else if (type_ents == GL_LINES)
            {
                Extensions_GL::glBufferData(target_vbo, taille_vertices + taille_couleurs, 0, usage_vbo);
                Extensions_GL::glBufferSubData(target_vbo, 0, taille_vertices, tampon_vertices.data());
                Extensions_GL::glBufferSubData(target_vbo, taille_vertices, taille_couleurs, tampon_couleurs.data());
                UnBind();
                return true;
            }
            else if (type_ents == GL_TRIANGLES)
            {
                const unsigned long taille_norms = tampon_norms.size() * mult_vertices;

                Extensions_GL::glBufferData(target_vbo, taille_vertices + taille_couleurs + taille_norms, 0, usage_vbo);
                Extensions_GL::glBufferSubData(target_vbo, 0, taille_vertices, tampon_vertices.data());
                Extensions_GL::glBufferSubData(target_vbo, taille_vertices, taille_couleurs, tampon_couleurs.data());
                Extensions_GL::glBufferSubData(target_vbo, taille_vertices+taille_couleurs, taille_norms, tampon_norms.data());
                UnBind();
                return true;
            }
            return false;
        }

        /* Aucun transfert (si pas de vbo)... */
        return true;
    }

    void PTampon_GL::Affichage(const Attribute_GL &attribute_vertices) const
    {
        attribute_vertices.Activation(VerticesConst());
        Affichage();
        attribute_vertices.Desactive();
    }

    void PTampon_GL::Affichage(const Attribute_GL &attribute_vertices, const Attribute_GL &attribute_normals) const
    {
        //std::cout << "Nouvel affichage tampon triangles : " << VerticesConst().size() << std::endl;
        attribute_vertices.Activation(VerticesConst());
        attribute_normals.Activation(NormsConst());
        Affichage();
        attribute_vertices.Desactive();
        attribute_normals.Desactive();
    }

    void PTampon_GL::Affichage3(const Attribute_GL &attribute_vertices, const Attribute_GL &attribute_color) const
    {
        attribute_vertices.Activation(VerticesConst());
        attribute_color.Activation(CouleursConst());
        Affichage();
        attribute_vertices.Desactive();
        attribute_color.Desactive();
    }

    void PTampon_GL::Affichage3(const Attribute_GL &attribute_vertices, const Attribute_GL &attribute_normals, const Attribute_GL &attribute_color) const
    {
        //std::cout << "Nouvel affichage tampon triangles : " << VerticesConst().size() << std::endl;
        attribute_vertices.Activation(VerticesConst());
        attribute_normals.Activation(NormsConst());
        attribute_color.Activation(CouleursConst());
        Affichage();
        attribute_vertices.Desactive();
        attribute_normals.Desactive();
        attribute_color.Desactive();
    }

    void PTampon_GL::Affichage() const
    {
//        std::cout << "PTampon_GL::Affichage() : vbo=" << utilise_vbo << " (" << id_vbo << ")" << ", taille entités=" << Taille() << ", taille vertices=" << tampon_vertices.size() << ", taille norms=" << tampon_norms.size() << ", taille couleurs=" << tampon_couleurs.size() << std::endl;

//        UnBind(); /* Au cas où... */

        if (Bind())
        {
            static const unsigned long mult_vertices = PGL::PVertex_GL::Taille() * sizeof(GLcoord);
            static const unsigned long mult_couleurs = PGL::PCouleur_GL::Taille() * sizeof(GLcoord);
            const unsigned long taille_vertices = tampon_vertices.size()*mult_vertices;
            const unsigned long taille_couleurs = tampon_couleurs.size()*mult_couleurs;

            if (type_ents == GL_POINTS)
            {
                glEnableClientState(GL_COLOR_ARRAY);
                glEnableClientState(GL_VERTEX_ARRAY);

                glColorPointer(PCouleur_GL::Taille(), GLcoord_type, 0, (void*)(taille_vertices));
                glVertexPointer(PVertex_GL::Taille(), GLcoord_type, 0, 0);

                glDrawArrays(GL_POINTS, 0, tampon_vertices.size());

                glDisableClientState(GL_VERTEX_ARRAY);
                glDisableClientState(GL_COLOR_ARRAY);

                UnBind();
            }
            else if (type_ents == GL_LINES)
            {
                glEnableClientState(GL_COLOR_ARRAY);
                glEnableClientState(GL_VERTEX_ARRAY);

                glColorPointer(PCouleur_GL::Taille(), GLcoord_type, 0, (void*)(taille_vertices));
                glVertexPointer(PVertex_GL::Taille(), GLcoord_type, 0, 0);

                glDrawArrays(GL_LINES, 0, tampon_vertices.size());

                glDisableClientState(GL_VERTEX_ARRAY);
                glDisableClientState(GL_COLOR_ARRAY);

                UnBind();
            }
            else if (type_ents == GL_TRIANGLES)
            {
//                const unsigned long taille_norms = tampon_norms.size()*mult_vertices;

                glEnableClientState(GL_NORMAL_ARRAY);
                glEnableClientState(GL_COLOR_ARRAY);
                glEnableClientState(GL_VERTEX_ARRAY);

                glNormalPointer(GLcoord_type, 0, (void*)(taille_vertices+taille_couleurs));
                glColorPointer(PCouleur_GL::Taille(), GLcoord_type, 0, (void*)(taille_vertices));
                glVertexPointer(PVertex_GL::Taille(), GLcoord_type, 0, 0);

                glDrawArrays(GL_TRIANGLES, 0, tampon_vertices.size());

                glDisableClientState(GL_VERTEX_ARRAY);
                glDisableClientState(GL_COLOR_ARRAY);
                glDisableClientState(GL_NORMAL_ARRAY);

                UnBind();
            }
        }
        else
        {
            if (type_ents == GL_POINTS)
            {
                glEnableClientState(GL_COLOR_ARRAY);
                glEnableClientState(GL_VERTEX_ARRAY);

                glColorPointer(PCouleur_GL::Taille(), GLcoord_type, 0, tampon_couleurs.data());
                glVertexPointer(PVertex_GL::Taille(), GLcoord_type, 0, tampon_vertices.data());

                glDrawArrays(GL_POINTS, 0, tampon_vertices.size());

                glDisableClientState(GL_VERTEX_ARRAY);
                glDisableClientState(GL_COLOR_ARRAY);
            }
            else if (type_ents == GL_LINES)
            {
                glEnableClientState(GL_COLOR_ARRAY);
                glEnableClientState(GL_VERTEX_ARRAY);

                glColorPointer(PCouleur_GL::Taille(), GLcoord_type, 0, tampon_couleurs.data());
                glVertexPointer(PVertex_GL::Taille(), GLcoord_type, 0, tampon_vertices.data());

                glDrawArrays(GL_LINES, 0, tampon_vertices.size());

                glDisableClientState(GL_VERTEX_ARRAY);
                glDisableClientState(GL_COLOR_ARRAY);
            }
            else if (type_ents == GL_TRIANGLES)
            {
                glEnableClientState(GL_NORMAL_ARRAY);
                glEnableClientState(GL_COLOR_ARRAY);
                glEnableClientState(GL_VERTEX_ARRAY);

                glNormalPointer(GLcoord_type, 0, tampon_norms.data());
                glColorPointer(PCouleur_GL::Taille(), GLcoord_type, 0, tampon_couleurs.data());
                glVertexPointer(PVertex_GL::Taille(), GLcoord_type, 0, tampon_vertices.data());

                glDrawArrays(GL_TRIANGLES, 0, tampon_vertices.size());

                glDisableClientState(GL_VERTEX_ARRAY);
                glDisableClientState(GL_COLOR_ARRAY);
                glDisableClientState(GL_NORMAL_ARRAY);
            }
        }
    }
} // namespace PGL

namespace PGL
{
    PTamponF_GL::PTamponF_GL() : id_vbo(0)
    {
    }

    PTamponF_GL::~PTamponF_GL()
    {
    }

    bool PTamponF_GL::ValideSupport() const
    {
        return Extensions_GL::SupportVBO();
    }

    bool PTamponF_GL::Valide(bool controle_gl) const
    {
        if (ValideSupport() && id_vbo != 0)
        {
            return controle_gl ? (Extensions_GL::glIsBuffer(id_vbo)) : true;
        }
        return false;
    }

    bool PTamponF_GL::Active()
    {
        if (ValideSupport())
        {
            if (Valide())
            {
                return false;
            }
            Extensions_GL::glGenBuffers(1, &id_vbo);
            return id_vbo != 0;
        }
        return false;
    }

    bool PTamponF_GL::Desactive()
    {
        if (Valide())
        {
            Extensions_GL::glDeleteBuffers(1, &id_vbo);
            id_vbo = 0;
            return true;
        }
        return false;
    }

    bool PTamponF_GL::Reactive()
    {
        Desactive();
        if (ValideSupport())
        {
            Extensions_GL::glGenBuffers(1, &id_vbo);
            return id_vbo != 0;
        }
        return false;
    }

    bool PTamponF_GL::Bind() const
    {
        if (Valide())
        {
            Extensions_GL::glBindBuffer(GL_ARRAY_BUFFER, id_vbo);
            return true;
        }
        return false;
    }

    void PTamponF_GL::UnBind() const
    {
        Extensions_GL::glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    bool PTamponF_GL::Transfert(const GLcoord *v, puint n) const
    {
        if (Bind())
        {
//            std::cout << "PTamponF_GL::Transfert(" << n << ")" << std::endl;
            Extensions_GL::glBufferData(GL_ARRAY_BUFFER, n, v, GL_STATIC_DRAW);
            UnBind();

            return true;
        }
        return false;
    }

} // namespace PGL

namespace PGL
{
    PTexture_GL::PTexture_GL(bool linear, bool repeat) : id_texture(0), gl_linear(linear), gl_repeat(repeat)
    {
//        glActiveTexture(GL_TEXTURE0);
        glGenTextures(1, &id_texture);
    }

    PTexture_GL::~PTexture_GL()
    {
        glDeleteTextures(1, &id_texture);
    }

    bool PTexture_GL::Valide(bool controle_gl) const
    {
        if (id_texture != 0)
        {
            return controle_gl ? glIsTexture(id_texture) : true;
        }
        return false;
    }

    bool PTexture_GL::Desactive()
    {
        if (Valide())
        {
            glDeleteTextures(1, &id_texture);
            id_texture = 0;
            return true;
        }
        return false;
    }

    bool PTexture_GL::Reactive()
    {
        if (id_texture != 0)
        {
            Desactive();
        }
        glGenTextures(1, &id_texture);
        return (id_texture != 0);
    }

    bool PTexture_GL::Bind() const
    {
        if (Valide(false))
        {
            glBindTexture(GL_TEXTURE_2D, id_texture);
            return glIsTexture(id_texture);
        }
        else
        {
//            std::cout << "PTexture_GL::Bind() (foiré) : " << id_texture << ":" << pint(glIsTexture(id_texture)) << std::endl;
        }
        return false;
    }

    void PTexture_GL::UnBind() const
    {
        glBindTexture(GL_TEXTURE_2D, 0);
    }

    bool PTexture_GL::TexImage2D_RGB(GLuint width, GLuint height, const char *data) const
    {
        return TexImage2D(width, height, data, false, false);
    }

    bool PTexture_GL::TexImage2D_RGBA(GLuint width, GLuint height, const char *data) const
    {
        return TexImage2D(width, height, data, true, false);
    }

    bool PTexture_GL::TexImage2D_BGR(GLuint width, GLuint height, const char *data) const
    {
        return TexImage2D(width, height, data, false, true);
    }

    bool PTexture_GL::TexImage2D_BGRA(GLuint width, GLuint height, const char *data) const
    {
        return TexImage2D(width, height, data, true, true);
    }

    bool PTexture_GL::TexImage2D(GLuint width, GLuint height, const char *data, bool rgba, bool inverse_rgb) const
    {
        if (Bind())
        {
            if (rgba)
            {
                if (inverse_rgb)
                {
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GLcoord_type, data);
                }
                else
                {
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GLcoord_type, data);
                }
            }
            else
            {
                if (inverse_rgb)
                {
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GLcoord_type, data);
                }
                else
                {
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GLcoord_type, data);
                }
            }
            TexParameter();
            UnBind();
            return true;
        }
        return false;
    }

    bool PTexture_GL::TexParameter() const
    {
        if (gl_linear)
        {
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        }
        else
        {
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        }

        if (gl_repeat)
        {
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        }
        else
        {
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        }
        return true;
    }
}

namespace PGL
{
    PRenderBuffer_GL::PRenderBuffer_GL() : id_render_buffer(0)
    {
        if (Extensions_GL::SupportFBO())
        {
            Extensions_GL::glGenRenderbuffers(1, &id_render_buffer);
        }
    }

    PRenderBuffer_GL::~PRenderBuffer_GL()
    {
        if (Extensions_GL::SupportFBO())
        {
            Extensions_GL::glDeleteRenderbuffers(1, &id_render_buffer);
        }
    }

    bool PRenderBuffer_GL::Valide(bool controle_gl) const
    {
        if (Extensions_GL::SupportFBO() && id_render_buffer != 0)
        {
            return controle_gl ? (Extensions_GL::glIsRenderbuffer(id_render_buffer)) : true;
        }
        return false;
    }

    bool PRenderBuffer_GL::Desactive()
    {
        if (Valide())
        {
            Extensions_GL::glDeleteRenderbuffers(1, &id_render_buffer);
            id_render_buffer = 0;
            return true;
        }
        return false;
    }

    bool PRenderBuffer_GL::Reactive()
    {
        if (id_render_buffer != 0)
        {
            Desactive();
        }
        Extensions_GL::glGenRenderbuffers(1, &id_render_buffer);
        return (id_render_buffer != 0);
    }

    bool PRenderBuffer_GL::Bind() const
    {
        if (Valide(false))
        {
            Extensions_GL::glBindRenderbuffer(GL_RENDERBUFFER, id_render_buffer);
            return Extensions_GL::glIsRenderbuffer(id_render_buffer);
        }
        return false;
    }

    void PRenderBuffer_GL::UnBind() const
    {
        if (Extensions_GL::SupportFBO())
        {
            Extensions_GL::glBindRenderbuffer(GL_RENDERBUFFER, 0);
        }
    }

    bool PRenderBuffer_GL::Transfert(GLsizei screen_width, GLsizei screen_height) const
    {
        if (Bind())
        {
            Extensions_GL::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, screen_width, screen_height);
            UnBind();
            return true;
        }
        return false;
    }
}

namespace PGL
{
    PFrameBuffer_GL::PFrameBuffer_GL() : framebuffer_0(0), fbo(0), vbo_fbo_vertices(0), texture(true, false), render(), bloque_program(false), program()
    {
        utilise_chrono = false;

        /* COULEUR_FOND_PGL_FLOAT */
        couleur_fond[0] = .117f;
        couleur_fond[1] = .145f;
        couleur_fond[2] = .176f;
        couleur_fond[3] = 1.0f;
    }

    PFrameBuffer_GL::~PFrameBuffer_GL()
    {
        Desactive();
    }

    void PFrameBuffer_GL::Desactive()
    {
        BloqueProgram();
        render.Desactive();
        texture.Desactive();
        Extensions_GL::glDeleteFramebuffers(1, &fbo);
        fbo = 0;
        program.Desactive();
    }

    void PFrameBuffer_GL::DesactiveProgram()
    {
        BloqueProgram();
        program.Desactive();
    }

    bool PFrameBuffer_GL::Valide() const
    {
#ifdef DEBUG_GL
        if (!checkFramebufferStatus())
        {
            return false;
        }
#else
        if (Extensions_GL::glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        {
            return false;
        }
#endif // DEBUG_GL
//        std::cout << "PFrameBuffer_GL::Valide() :: " << IdBuffer() << ", " << ExecProgram() << ", " << texture.IdTexture() << ", " << render.IdRenderBuffer() << ", " << program.ValideLiage() << std::endl;
        return (IdBuffer() != 0) && ExecProgram() && program.ValideLiage();
    }

    bool PFrameBuffer_GL::ValideLiageShaders() const
    {
        return program.ValideLiage();
    }

    bool PFrameBuffer_GL::Gen(puint taille_ecran_x, puint taille_ecran_y)
    {
        if (!Extensions_GL::SupportFBO())
        {
            return false;
        }

        resolution_x = taille_ecran_x;
        resolution_y = taille_ecran_y;
        chrono = 0.0;

        /* Texture */
        texture.TexImage2D_RGBA(taille_ecran_x, taille_ecran_y, nullptr);

        /* Depth buffer */
        render.Transfert(taille_ecran_x, taille_ecran_y);

        /* Framebuffer to link everything together */
        Extensions_GL::glGenFramebuffers(1, &fbo);
        Extensions_GL::glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        Extensions_GL::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.IdTexture(), 0);
        Extensions_GL::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render.IdRenderBuffer());

        GLenum status;
        if ((status = Extensions_GL::glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE)
        {
            Extensions_GL::glDeleteBuffers(1, &fbo);
            fbo = 0;

//            std::cout << "glCheckFramebufferStatus error : " <<  status << std::endl;
        }
        else
        {
            /* init_resources */
            const GLfloat taille_surface_x = 1.0;
            const GLfloat taille_surface_y = 1.0;
            static const GLfloat surface_vertices[] = {
               -taille_surface_x, -taille_surface_y,
                taille_surface_x, -taille_surface_y,
               -taille_surface_x,  taille_surface_y,
                taille_surface_x,  taille_surface_y,
            };

            Extensions_GL::glGenBuffers(1, &vbo_fbo_vertices);
            Extensions_GL::glBindBuffer(GL_ARRAY_BUFFER, vbo_fbo_vertices);
            Extensions_GL::glBufferData(GL_ARRAY_BUFFER, sizeof(surface_vertices), surface_vertices, GL_STATIC_DRAW);
            Extensions_GL::glBindBuffer(GL_ARRAY_BUFFER, 0);

//            if (InitShaders(vshader, pshader))
//            {
//                std::cout << "Liage du program correct !!!" << std::endl;

//            }
            Extensions_GL::glBindFramebuffer(GL_FRAMEBUFFER, 0);
            ActiveProgram();
            return true;
        }
        return false;
    }

    bool PFrameBuffer_GL::InitialisationShaders(const char * vshader, const char * pshader)
    {
//        std::cout << "PFrameBuffer_GL::InitShaders() : " << vbo_fbo_vertices << std::endl;

        journal_compilation_shaders.clear();

        if (vbo_fbo_vertices == 0)
        {
            return false;
        }

        Extensions_GL::glBindFramebuffer(GL_FRAMEBUFFER, vbo_fbo_vertices);

        bool v1 = Ptest_ptr(vshader) ? program.AjoutVertexShader(vshader) : true;
        bool v2 = Ptest_ptr(pshader) ? program.AjoutPixelShader(pshader) : true;

        if (!v1 && program.JournalCompilationVertexShader().size() > 1)
        {
            journal_compilation_shaders += "\nVertex:\n";
            journal_compilation_shaders += program.JournalCompilationVertexShader();
            journal_compilation_shaders += "\n";
        }

        if (!v2 && program.JournalCompilationPixelShader().size() > 1)
        {
            journal_compilation_shaders += "\nFragments:\n";
            journal_compilation_shaders += program.JournalCompilationPixelShader();
            journal_compilation_shaders += "\n";
        }

        if (v1 && v2 && (Ptest_ptr(vshader) || Ptest_ptr(pshader)))
        {
            if (program.LinkProgram())
            {
#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
                Extensions_GL::glBindFragDataLocation(program.IdProgram(), 0, NOM_ATTR_POST_FRAGCOLOR);
#endif // FORCE_VERSION_GL

                if ((attribute_v_coord_postproc = program.ReservationAttribut(NOM_ATTR_POST_COORDS)) == -1)
                {
                    journal_compilation_shaders += "\nWarning : Attribute " NOM_ATTR_POST_COORDS " unused.";
                }

                if ((uniform_fbo_texture = program.ReservationUniform(NOM_POST_TEXTURE)) == -1)
                {
                    journal_compilation_shaders += "\nWarning : Uniform " NOM_POST_TEXTURE " unused.";
                }

                if ((uniform_fbo_resolution = program.ReservationUniform(NOM_POST_RESOLUTION)) == -1)
                {
//                    journal_compilation_shaders += "\nUniform " NOM_POST_RESOLUTION " unused.";
                }

                if ((uniform_fbo_couleur_fond = program.ReservationUniform(NOM_POST_COULEUR_FOND)) == -1)
                {
//                    journal_compilation_shaders += "\nUniform " NOM_POST_COULEUR_FOND " unused.";
                }

                if ((uniform_fbo_chrono = program.ReservationUniform(NOM_POST_CHRONO)) == -1)
                {
//                    journal_compilation_shaders += "\nUniform " NOM_POST_CHRONO " unused.";
                    utilise_chrono = false;
                }
                else
                {
                    utilise_chrono = true;
                }

//                std::cout << "PFrameBuffer_GL::InitShaders : " << attribute_v_coord_postproc << ", " << attribute_v_color_postproc << ", " << uniform_fbo_texture << ", " << uniform_fbo_resolution << ", " << uniform_fbo_couleur_fond << ", " << uniform_fbo_chrono << std::endl;
                Extensions_GL::glBindFramebuffer(GL_FRAMEBUFFER, 0);
                return true;
            }
            else
            {
                if (program.JournalLiageShaders().size() > 1)
                {
                    journal_compilation_shaders += "\nLinking:\n";
                    journal_compilation_shaders += program.JournalLiageShaders();
                    journal_compilation_shaders += "\n";
                }
            }
        }
        else
        {
//            journal_compilation_shaders += "\n(Linking = 0)\n";

            if (!v1)
            {
                if (program.ValideLiage())
                {
                    program.Desactive();
                }
            }
            else if (!v2)
            {
                if (program.ValideLiage())
                {
                    program.Desactive();
                }
            }
        }

        attribute_v_coord_postproc = 0;
        uniform_fbo_resolution = 0;
        uniform_fbo_couleur_fond = 0;
        uniform_fbo_chrono = 0;
        uniform_fbo_texture = 0;
        utilise_chrono = false;
        Extensions_GL::glBindFramebuffer(GL_FRAMEBUFFER, 0);
        return false;
    }

    bool PFrameBuffer_GL::DumpShaderBinaire(const std::string &chemin) const
    {
        return program.DumpBinaire(chemin, 2);
    }


    bool PFrameBuffer_GL::SauvegardeFrameBufferInitial()
    {
        GLint drawFboId = 0;
        glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFboId);

        framebuffer_0 = drawFboId;
        return (framebuffer_0 != 0);
    }

    bool PFrameBuffer_GL::RestaureFrameBufferInitial() const
    {
//        std::cout << "PFrameBuffer_GL::RestaureFrameBufferInitial() : " << framebuffer_0 << " (id FBO:" << IdBuffer() << ")" << std::endl;
        if (framebuffer_0 != 0)
        {
            Extensions_GL::glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_0);
            return true;
        }
        return false;
    }

    bool PFrameBuffer_GL::Bind()
    {
        if (IdBuffer() != 0)
        {
            SauvegardeFrameBufferInitial();
            Extensions_GL::glBindFramebuffer(GL_FRAMEBUFFER, IdBuffer());
            return true;
        }
        return false;
    }

    void PFrameBuffer_GL::UnBind() const
    {
        if (!RestaureFrameBufferInitial())
        {
            Extensions_GL::glBindFramebuffer(GL_FRAMEBUFFER, 0);
        }
    }

    void PFrameBuffer_GL::defTailleEcran(puint x, puint y)
    {
//        resolution_x = x/2;
//        resolution_y = y/2;
        resolution_x = x;
        resolution_y = y;

        texture.TexImage2D_RGBA(resolution_x, resolution_y, nullptr);
        render.Transfert(resolution_x, resolution_y);
    }

    void PFrameBuffer_GL::defCouleurFond(const PCouleur_GL &couleur)
    {
        couleur_fond[0] = (couleur.R());
        couleur_fond[1] = (couleur.V());
        couleur_fond[2] = (couleur.B());
        couleur_fond[3] = (couleur.Alpha());
    }

    const char * PFrameBuffer_GL::VShaderDefaut()
    {
        return VShader_Post_Defaut;
    }

    const char * PFrameBuffer_GL::FShaderDefaut()
    {
        return FShader_Post_Defaut;
    }

    bool PFrameBuffer_GL::Affichage()
    {
        if (utilise_chrono)
        {
            chrono += 0.04f; /* Donnée de temps (on part sur une moyenne de 25 FPS) */
//            std::cout << "Mise à jour affichage : " << chrono << std::endl;
        }
        if (Valide())
        {
            UnBind(); /* Au cas où... */
//            glClearColor(0.0, 0.0, 0.0, 1.0);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//            render.Bind();
            program.UseProgram();

            texture.Bind();
            Extensions_GL::glUniform1i(uniform_fbo_texture, /*GL_TEXTURE*/0);

            Extensions_GL::glUniform2f(uniform_fbo_resolution, resolution_x, resolution_y);
            Extensions_GL::glUniform3f(uniform_fbo_couleur_fond, couleur_fond[0], couleur_fond[1], couleur_fond[2]);
            Extensions_GL::glUniform1f(uniform_fbo_chrono, chrono);

            Extensions_GL::glEnableVertexAttribArray(attribute_v_coord_postproc);
            Extensions_GL::glBindBuffer(GL_ARRAY_BUFFER, vbo_fbo_vertices);
            Extensions_GL::glVertexAttribPointer(attribute_v_coord_postproc, 2, GL_FLOAT, GL_FALSE, 0, 0);

            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            Extensions_GL::glDisableVertexAttribArray(attribute_v_coord_postproc);
            Extensions_GL::glBindBuffer(GL_ARRAY_BUFFER, 0);

            texture.UnBind();
            program.DesactivePrograms();

//            UnBind();

//            glEnable(GL_DEPTH_TEST);
            return true;
        }
        return false;
    }

#ifdef DEBUG_GL
    bool PFrameBuffer_GL::checkFramebufferStatus() const
    {
        GLenum status = Extensions_GL::glCheckFramebufferStatus(GL_FRAMEBUFFER);
        switch(status)
        {
            case GL_FRAMEBUFFER_COMPLETE:
//                std::cout << "Framebuffer complete." << std::endl;
                return true;

            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
                std::cout << "[ERROR] Framebuffer incomplete: Attachment is NOT complete." << std::endl;
                return false;

            case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
                std::cout << "[ERROR] Framebuffer incomplete: No image is attached to FBO." << std::endl;
                return false;

//            case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
//                std::cout << "[ERROR] Framebuffer incomplete: Attached images have different dimensions." << std::endl;
//                return false;

//            case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
//                std::cout << "[ERROR] Framebuffer incomplete: Color attached images have different internal formats." << std::endl;
//                return false;

            case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
                std::cout << "[ERROR] Framebuffer incomplete: Draw buffer." << std::endl;
                return false;

            case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
                std::cout << "[ERROR] Framebuffer incomplete: Read buffer." << std::endl;
                return false;

            case GL_FRAMEBUFFER_UNSUPPORTED:
                std::cout << "[ERROR] Framebuffer incomplete: Unsupported by FBO implementation." << std::endl;
                return false;

            default:
                std::cout << "[ERROR] Framebuffer incomplete: Unknown error, status=" << status << std::endl;
                return false;
        }
    }
#endif // DEBUG_GL
} //namespace PGL

namespace PGL
{
    Shaders_GL::Shaders_GL() : bloque_program(false), program(),
        uniform_matrice_affichage(program), uniform_matrice_projections(program), uniform_matrice_normals(program), uniform_vec_light0(program), attribute_vertex(program), attribute_normals(program)
    #if FORCE_VERSION_GL >= VERSION_GL(3, 2)
        , attribute_color(program)
    #endif // FORCE_VERSION_GL

    {

    }

    Shaders_GL::~Shaders_GL()
    {
        DesactivationAttributsShadersModele();
        program.Desactive();
    }

    const char * Shaders_GL::VShaderDefaut()
    {
        return VShader_Defaut;
    }

    const char * Shaders_GL::FShaderDefaut()
    {
        return FShader_Defaut;
    }

    bool Shaders_GL::InitialisationShader(const char *source_vertex_shader, const char *source_pixel_shader)
    {
        setlocale(LC_NUMERIC, "C");

        journal_compilation_shaders.clear();

//        if (source_vertex_shader != 0)
//        {
//            std::cout << "vshaders : " << source_vertex_shader << std::endl;
//        }
//        if (source_pixel_shader != 0)
//        {
//            std::cout << "fshaders : " << source_pixel_shader << std::endl;
//        }


        if (program.ValideLiage())
        {
#ifdef DEBUG_GLSL
            std::cout << "\n\n    Désactivation d'un program existant !" << std::endl;
#endif // DEBUG_GLSL

//            program.Reactive();
            Desactive();
            program.Reactive();

#ifdef DEBUG_GLSL
            std::cout << "Après nouvelle activation : " << program.IdProgram() << std::endl;
#endif // DEBUG_GLSL
        }

        bool v1 = ( Ptest_ptr(source_vertex_shader) && (*source_vertex_shader != '\0') ) ? program.AjoutVertexShader(source_vertex_shader) : true;
        bool v2 = ( Ptest_ptr(source_pixel_shader) && (*source_pixel_shader != '\0') ) ? program.AjoutPixelShader(source_pixel_shader) : true;

        if (!v1)
        {
            journal_compilation_shaders += "\nVertex:\n";
            journal_compilation_shaders += program.JournalCompilationVertexShader();
            journal_compilation_shaders += "\n";
        }

        if (!v2)
        {
            journal_compilation_shaders += "\nFragments:\n";
            journal_compilation_shaders += program.JournalCompilationPixelShader();
            journal_compilation_shaders += "\n";
        }

        if (v1 && v2 && (Ptest_ptr(source_vertex_shader) || Ptest_ptr(source_pixel_shader)))
        {
            if (program.LinkProgram())
            {
                setlocale(LC_ALL, "");
                bool v = AssignationAttributsShaderModele();
                if (!v)
                {
#ifdef DEBUG_GLSL
                    std::cout << "Erreur d'attributs shader : " << source_vertex_shader << std::endl;
#endif // DEBUG_GLSL
                }
//                std::cout << "Program : " << program.IdProgram() << " Valide !" << std::endl;
                ActiveProgram();
                return v;
            }
            else
            {
                journal_compilation_shaders += "\nLinking:\n";
                journal_compilation_shaders += program.JournalLiageShaders();
                journal_compilation_shaders += "\n";

#ifdef DEBUG_GLSL
                std::cout << "Erreur de liage du program : " << program.IdProgram() << std::endl;
#endif // DEBUG_GLSL
            }
        }
        else
        {
            if (!v1)
            {
#ifdef DEBUG_GLSL
                std::cout << "Erreur d'initialisation du vertex shader : (support=" << program.ValideSupport() << ", liage=" << program.ValideLiage() << ") :: " << source_vertex_shader;
#endif // DEBUG_GLSL
                if (program.ValideLiage())
                {
                    program.Desactive();
                }
            }
            else if (!v2)
            {
#ifdef DEBUG_GLSL
                std::cout << "Erreur d'initialisation du pixel shader : (support=" << program.ValideSupport() << ", liage=" << program.ValideLiage() << ") :: " << source_pixel_shader;
#endif // DEBUG_GLSL
                if (program.ValideLiage())
                {
                    program.Desactive();
                }
            }
        }

        setlocale(LC_ALL, "");
        return false;
    }

    bool Shaders_GL::DumpShaderBinaire(const std::string &chemin) const
    {
        return program.DumpBinaire(chemin, 1);
    }

    bool Shaders_GL::Desactive()
    {
        BloqueProgram();
        if (DesactivationAttributsShadersModele())
        {
        }
        return program.Desactive();

//        if (program.Desactive())
//        {
//            return DesactivationAttributsShadersModele();
//        }
        return false;
    }

    bool Shaders_GL::Activation(const PGL::PMatriceGL4 &matrice_affichage, const PGL::PMatriceGL4 &matrice_projections)
    {
        if (ExecProgram() && program.ValideLiage())
        {
            if (program.UseProgram())
            {
                pint compteur = 0;
                pint cpt_valide = 0;

                if (uniform_matrice_affichage.Activation(matrice_affichage)) { ++cpt_valide; } ++compteur;
                if (uniform_matrice_projections.Activation(matrice_projections)) { ++cpt_valide; } ++compteur;

#ifdef DEBUG_GLSL
                if (compteur != cpt_valide)
                {
                    std::cout << "Attention, l'activation de " << compteur-cpt_valide << "/" << compteur << " attributs ou variables uniform ont foirés." << std::endl;
                }
#endif // DEBUG_GLSL
                return (compteur == cpt_valide);
            }
        }
        return false;
    }

    bool Shaders_GL::Activation(const PGL::PMatriceGL4 &matrice_affichage, const PGL::PMatriceGL4 &matrice_projections, const PGL::PMatriceGL4 &matrice_normals)
    {
        if (ExecProgram() && program.ValideLiage())
        {
            if (program.UseProgram())
            {
                pint compteur = 0;
                pint cpt_valide = 0;

                if (uniform_matrice_affichage.Activation(matrice_affichage)) { ++cpt_valide; } ++compteur;
                if (uniform_matrice_projections.Activation(matrice_projections)) { ++cpt_valide; } ++compteur;
                if (uniform_matrice_normals.Activation(matrice_normals)) { ++cpt_valide; } ++compteur;

#ifdef DEBUG_GLSL
                if (compteur != cpt_valide)
                {
                    std::cout << "Attention, l'activation de " << compteur-cpt_valide << "/" << compteur << " attributs ou variables uniform ont foirés." << std::endl;
                }
#endif // DEBUG_GLSL

                return (compteur == cpt_valide);
            }
        }
        return false;
    }

    bool Shaders_GL::Activation(const PGL::PMatriceGL4 &matrice_affichage, const PGL::PMatriceGL4 &matrice_projections, const PGL::PMatriceGL4 &matrice_normals, const PVertex_GL &position_lumiere)
    {
        if (ExecProgram() && program.ValideLiage())
        {
            if (program.UseProgram())
            {
                pint compteur = 0;
                pint cpt_valide = 0;

                if (uniform_matrice_affichage.Activation(matrice_affichage)) { cpt_valide |= 1; }; compteur |= 1;
                if (uniform_matrice_projections.Activation(matrice_projections)) { cpt_valide |= 2; }; compteur |= 2;
                if (uniform_matrice_normals.Activation(matrice_normals)) { cpt_valide |= 4; }; compteur |= 4;
                if (uniform_vec_light0.Activation(position_lumiere)) { cpt_valide |= 8; }; compteur |= 8;

#ifdef DEBUG_GLSL
                if (compteur != cpt_valide)
                {
                    std::cout << "Attention, l'activation d'une ou plusieurs variables uniform a foiré : ";
                    if (!(cpt_valide & 1))
                    {
                        std::cout << "uniform_matrice_affichage" << ", ";
                    }
                    if (!(cpt_valide & 2))
                    {
                        std::cout << "uniform_matrice_projections" << ", ";
                    }
                    if (!(cpt_valide & 4))
                    {
                        std::cout << "uniform_matrice_normals" << ", ";
                    }
                    if (!(cpt_valide & 8))
                    {
                        std::cout << "uniform_vec_light0" << ", ";
                    }
                    std::cout << std::endl;
//                    std::cout << "Attention, l'activation de " << compteur-cpt_valide << "/" << 4 << " attributs ou variables uniform ont foirés." << std::endl;
                }
#endif // DEBUG_GLSL

                return (compteur == cpt_valide);
            }
        }
        return false;
    }

    bool Shaders_GL::Reactive()
    {
        if (DesactivationAttributsShadersModele())
        {
        }
        program.Reactive();
        bool r = AssignationAttributsShaderModele();
        ActiveProgram();
        return r;
    }

    bool Shaders_GL::Affichage(const PTampon_GL &tampon) const
    {
        if (ExecProgram() && ValideLiage())
        {
            program.UseProgram();
#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
            if (attribute_vertex.Valide() && attribute_color.Valide())
            {
                if (attribute_normals.Valide())
                {
                    tampon.Affichage3(attribute_vertex, attribute_normals, attribute_color);
                }
                else
                {
                    tampon.Affichage3(attribute_vertex, attribute_color);
                }
                return true;
            }
#else
            if (attribute_vertex.Valide())
            {
                if (attribute_normals.Valide())
                {
                    tampon.Affichage(attribute_vertex, attribute_normals);
                }
                else
                {
                    tampon.Affichage(attribute_vertex);
                }
                return true;
            }
#endif // FORCE_VERSION_GL

#ifdef DEBUG_GL
            else
            {
                std::cout << "Shaders_GL::Affichage :: Attribut invalide : vertex=" << attribute_vertex.Valide()
    #if FORCE_VERSION_GL >= VERSION_GL(3, 2)
                    << ", color=" << attribute_color.Valide()
    #endif // FORCE_VERSION_GL
                    << std::endl;
            }
#endif // DEBUG_GL
        }

        tampon.Affichage();
        return true;
    }

    bool Shaders_GL::AssignationAttributsShaderModele()
    {
        pint cpt_invalide = 0;
        pint cpt_attributs = 0;

        if (!uniform_matrice_affichage.Reservation(NOM_MATRICE_MODEL))
        {
            journal_compilation_shaders += "\nWarning : Uniform " NOM_MATRICE_MODEL " unused.";
#ifdef DEBUG_GLSL
            std::cout << "AssignationAttributsShaderModele() : Attention, l'initialisation de l'attribut uniform_matrice_affichage a foiré." << std::endl;
#endif // DEBUG_GLSL
            ++cpt_invalide;
        }
        ++cpt_attributs;

        if (!uniform_matrice_projections.Reservation(NOM_MATRICE_PROJECTIONS))
        {
            journal_compilation_shaders += "\nWarning : Uniform " NOM_MATRICE_PROJECTIONS " unused.";
#ifdef DEBUG_GLSL
            std::cout << "AssignationAttributsShaderModele() : Attention, l'initialisation de l'attribut uniform_matrice_projections a foiré." << std::endl;
#endif // DEBUG_GLSL
            ++cpt_invalide;
        }
        ++cpt_attributs;

        if (!uniform_matrice_normals.Reservation(NOM_MATRICE_NORMALS))
        {
#ifdef DEBUG_GLSL
            std::cout << "AssignationAttributsShaderModele() : Attention, l'initialisation de l'attribut uniform_matrice_normals a foiré." << std::endl;
#endif // DEBUG_GLSL
            ++cpt_invalide;
        }
        ++cpt_attributs;

        if (!uniform_vec_light0.Reservation(NOM_VEC_LIGHT0))
        {
#ifdef DEBUG_GLSL
            std::cout << "AssignationAttributsShaderModele() : Attention, l'initialisation de l'attribut uniform_vec_light0 a foiré." << std::endl;
#endif // DEBUG_GLSL
            ++cpt_invalide;
        }
        ++cpt_attributs;

        if (!attribute_vertex.Reservation(NOM_ATTR_VERTEX))
        {
            journal_compilation_shaders += "\nAttribute " NOM_ATTR_VERTEX " unused.";
#ifdef DEBUG_GLSL
            std::cout << "AssignationAttributsShaderModele() : Attention, l'initialisation de l'attribut attribute_vertex a foiré." << std::endl;
#endif // DEBUG_GLSL
            ++cpt_invalide;
        }
        ++cpt_attributs;

#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
        if (!attribute_color.Reservation(NOM_ATTR_COLOR))
        {
            journal_compilation_shaders += "\nAttribute " NOM_ATTR_COLOR " unused.";
#ifdef DEBUG_GLSL
            std::cout << "AssignationAttributsShaderModele() : Attention, l'initialisation de l'attribut attribute_color a foiré." << std::endl;
#endif // DEBUG_GLSL
            ++cpt_invalide;
        }
        ++cpt_attributs;
#endif // FORCE_VERSION_GL

        if (!attribute_normals.Reservation(NOM_ATTR_NORMAL))
        {
#ifdef DEBUG_GLSL
            std::cout << "AssignationAttributsShaderModele() : Attention, l'initialisation de l'attribut attribute_normals a foiré." << std::endl;
#endif // DEBUG_GLSL
            ++cpt_invalide;
        }
        ++cpt_attributs;

#ifdef DEBUG_GLSL
        if (cpt_invalide)
        {
            std::cout << "Attention, l'initialisation de " << cpt_invalide << "/" << cpt_attributs << " attributs ou variables uniform ont foirés." << std::endl;
        }
#endif // DEBUG_GLSL

        return cpt_invalide == 0;
    }

    bool Shaders_GL::DesactivationAttributsShadersModele()
    {
        pint compteur = 0;
        pint cpt_valide = 0;

        if (uniform_matrice_affichage.Desactive()) { ++cpt_valide; } ++compteur;
        if (uniform_matrice_projections.Desactive()) { ++cpt_valide; } ++compteur;
        if (uniform_matrice_normals.Desactive()) { ++cpt_valide; } ++compteur;
        if (uniform_vec_light0.Desactive()) { ++cpt_valide; } ++compteur;
        if (attribute_vertex.Desactive()) { ++cpt_valide; } ++compteur;
#if FORCE_VERSION_GL >= VERSION_GL(3, 2)
        if (attribute_color.Desactive()) { ++cpt_valide; } ++compteur;
#endif // FORCE_VERSION_GL
        if (attribute_normals.Desactive()) { ++cpt_valide; } ++compteur;

#ifdef DEBUG_GLSL
        if (compteur != cpt_valide)
        {
            std::cout << "Attention, la désactivation de " << compteur-cpt_valide << "/" << compteur << " attributs ou variables uniform ont foirés." << std::endl;
        }
#endif // DEBUG_GLSL

        return compteur == cpt_valide;
    }
} // namespace PGL

namespace PGL
{
    Params_GL::Params_GL(float ep_points_, float ep_lignes_, bool eclairage_dyn_, bool eclairage_uni_, bool mode_ortho_, bool active_shaders_, bool active_fbo_) :
        ep_points(ep_points_), ep_lignes(ep_lignes_), eclairage_dyn(eclairage_dyn_), eclairage_uni(eclairage_uni_), mode_ortho(mode_ortho_),
        active_shaders(active_shaders_), active_fbo(active_fbo_),
        couleur_fond(COULEUR_FOND_PGL_FLOAT)
    {
    }

    Moteur_GL::Moteur_GL(const Params_GL &params) : parametres(params), bloque_affichage(false), utilise_fbo_tex(false),
        taille_affichage_x(0), taille_affichage_y(0),
        fov(90.), echelle_affichage(ECHELLE_DEFAUT_PGL),
        sommets_div(GL_POINTS), filaire_div(GL_LINES), triangles_div(GL_TRIANGLES)
    {
//        mode_vue_libre = true;

        Translation_camera.defCoords(0.,0.,0.);
        projection_curseur.defCoords(0., 0., 0.);
        position_lumiere.defCoords(0., 0., 0.);
    }

    Moteur_GL::~Moteur_GL()
    {
        if (program_filaire.ValideLiage())
        {
            program_filaire.Desactive();
        }
        if (program_modele.ValideLiage())
        {
            program_modele.Desactive();
        }

        if (framebuffer.Valide())
        {
            framebuffer.Desactive();
        }

        sommets_div.DesactiveVBO();
        filaire_div.DesactiveVBO();
        triangles_div.DesactiveVBO();
    }

    void Moteur_GL::InitialisationEclairage(bool statique) const
    {
        glEnable(GL_LIGHTING);
        if (statique)
        {
            const GLfloat amb_f = 0.6f;
            const GLfloat diff_f = 0.6f;
            GLfloat ambient[] = {amb_f,amb_f,amb_f, 1.f};
            GLfloat diffuse[] = {diff_f,diff_f,diff_f, 1.f};
            GLfloat position[] = {-5., -5., -5., 0.};
            glEnable(GL_COLOR_MATERIAL);
            glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
            glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
            glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
            glLightfv(GL_LIGHT0, GL_POSITION, position);
            glEnable(GL_LIGHT0);
            glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT, ambient);
            glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE, diffuse);
        }
        else /* Active la lumière dynamique */
        {
            const GLfloat v_ambient = .4f;
            const GLfloat v_diffuse = .6f;
            const GLfloat v_specular = 1.f;
            const GLfloat v_ambient_back = 0.05f;
            const GLfloat v_diffuse_back = 0.05f;
            static const GLfloat ambient[] = {v_ambient, v_ambient, v_ambient, 1.f};
            static const GLfloat diffuse[] = {v_diffuse, v_diffuse, v_diffuse, 1.f};
            static const GLfloat specular[] = {v_specular, v_specular, v_specular, 1.f};

            static const GLfloat front_mat_shininess[] = { 20.f };
            static const GLfloat front_mat_specular[]  = { v_ambient, v_ambient, v_ambient, 1.f };
            static const GLfloat front_mat_diffuse[]   = { v_diffuse, v_diffuse, v_diffuse, 1.f };
            static const GLfloat back_mat_shininess[]  = { 10.f };
            static const GLfloat back_mat_specular[]   = { v_ambient_back, v_ambient_back, v_ambient_back, 1.f };
            static const GLfloat back_mat_diffuse[]    = { v_diffuse_back, v_diffuse_back, v_diffuse_back, 1.f };

            glEnable(GL_LIGHT0);

//            glEnable(GL_DEPTH_TEST);
//            glDepthFunc(GL_LEQUAL);

//            glEnable(GL_COLOR_MATERIAL);
//            glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);

            glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
            glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
            glLightfv(GL_LIGHT0, GL_SPECULAR, specular);

//            GLfloat position[] = { 0.0, 3.0, 2.0, 0.0 };
//            glLightfv(GL_LIGHT0, GL_POSITION, position);

//            GLfloat lmodel_ambient[] = { 0.4, 0.4, 0.4, 1.0 };
//            GLfloat local_view[] = { 0.0 };
//            glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
//            glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);

            const GLfloat pos_lum[] = { position_lumiere.X(), position_lumiere.Y(), position_lumiere.Z(), 0 };
            glLightfv(GL_LIGHT0, GL_POSITION, pos_lum);

            glMaterialfv(GL_FRONT, GL_SHININESS, front_mat_shininess);
            glMaterialfv(GL_FRONT, GL_SPECULAR, front_mat_specular);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, front_mat_diffuse);
            glMaterialfv(GL_BACK, GL_SHININESS, back_mat_shininess);
            glMaterialfv(GL_BACK, GL_SPECULAR, back_mat_specular);
            glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);

            if (parametres.eclairage_uni) // Ignore le sens des faces pour l'éclairage
            {
                static const GLfloat lmodel_twoside[] = { 1 };
                glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
            }

        }
#ifdef DEBUG_GL
        CONTROLE_ERRGL;
#endif // DEBUG_GL
        glDisable(GL_LIGHTING);
    }

    void Moteur_GL::AffichageDivers() const
    {
//        std::cout << "Affichage divers moteur !" << std::endl;
        if (sommets_div.VerticesConst().size())
        {
//            glDisable(GL_DEPTH_TEST);

            glEnable(GL_POLYGON_OFFSET_POINT);
            glPolygonOffset(1., 1.5);
            glPointSize(parametres.ep_points * 1.5);
//            std::cout << "Affichage sommets div : " << sommets_div.VerticesConst().size() << std::endl;
            AffichageFilaire(sommets_div);
            glDisable(GL_POLYGON_OFFSET_POINT);

//            glEnable(GL_DEPTH_TEST);
        }

        if (filaire_div.VerticesConst().size())
        {
            glEnable(GL_POLYGON_OFFSET_LINE);
            glPolygonOffset(1., 2);

            glEnable(GL_BLEND); /* Active le canal alpha (transparence). */
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glLineWidth(parametres.ep_lignes * 1.5);
            AffichageFilaire(filaire_div);
            glDisable(GL_BLEND);
            glDisable(GL_POLYGON_OFFSET_LINE);
        }

        if (triangles_div.VerticesConst().size())
        {
            glEnable(GL_POLYGON_OFFSET_FILL);
            glPolygonOffset(1, 1e-5); // Décalage sur le Z-buffer pour placer les triangles en arrière (et faire apparaitre les points et lignes)
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

            if (parametres.eclairage_dyn)
            {
                glEnable(GL_LIGHTING);
                glEnable(GL_LIGHT0);
                glShadeModel(GL_SMOOTH); /* Gouraud */
            }

            if (parametres.eclairage_uni)
            {
                glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
            }
            else
            {
                glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
            }

            glEnable(GL_COLOR_MATERIAL);

            glEnable(GL_BLEND); /* Active le canal alpha (transparence). */
            AffichageModele(triangles_div);
            glDisable(GL_BLEND);
            glDisable(GL_POLYGON_OFFSET_FILL);

            if (parametres.eclairage_dyn)
            {
                glDisable(GL_LIGHT0);
                glDisable(GL_LIGHTING);
            }
        }
    }

    void Moteur_GL::defEclairageDynamique(bool etat)
    {
        if (parametres.eclairage_dyn != etat)
        {
            parametres.eclairage_dyn = etat;

            if (!etat)
            {
                glDisable(GL_LIGHT0);
                glDisable(GL_LIGHTING);
            }
            else
            {
                InitialisationEclairage(false);
            }
        }
    }

    PVertex_GL Moteur_GL::DeProj(GLcoord x, GLcoord y, GLcoord z)
    {
        GLdouble x_, y_, z_;
        GLint vp[4] = { 0, taille_affichage_y, taille_affichage_x, -taille_affichage_y };

        const GLcoord *t1 = MatriceAffichage().Tableau();
        const GLcoord *t2 = MatriceProjection().Tableau();
//        gluUnProject(x, y, z, t1, t2, vp, &x_, &y_, &z_);

        const GLdouble m1f[16] = { GLdouble(t1[0]),  GLdouble(t1[1]),  GLdouble(t1[2]),  GLdouble(t1[3]),
                                   GLdouble(t1[4]),  GLdouble(t1[5]),  GLdouble(t1[6]),  GLdouble(t1[7]),
                                   GLdouble(t1[8]),  GLdouble(t1[9]),  GLdouble(t1[10]), GLdouble(t1[11]),
                                   GLdouble(t1[12]), GLdouble(t1[13]), GLdouble(t1[14]), GLdouble(t1[15]) };

        const GLdouble m2f[16] = { GLdouble(t2[0]),  GLdouble(t2[1]),  GLdouble(t2[2]),  GLdouble(t2[3]),
                                   GLdouble(t2[4]),  GLdouble(t2[5]),  GLdouble(t2[6]),  GLdouble(t2[7]),
                                   GLdouble(t2[8]),  GLdouble(t2[9]),  GLdouble(t2[10]), GLdouble(t2[11]),
                                   GLdouble(t2[12]), GLdouble(t2[13]), GLdouble(t2[14]), GLdouble(t2[15]) };

        gluUnProject(x, y, z, m1f, m2f, vp, &x_, &y_, &z_);
        return PGL::PVertex_GL(x_, y_, z_);
    }

    bool Moteur_GL::CalcProjectionCurseur(pint x, pint y)
    {
        if (!ExecAffichage())
        {
            return false;
        }

        GLfloat profondeur=1000.;

        if (utilise_fbo_tex)
        {
            framebuffer.Bind();
            glReadPixels(x, taille_affichage_y-1-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &profondeur);
            framebuffer.UnBind();
        }
        else
        {
            glReadPixels(x, taille_affichage_y-1-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &profondeur);
        }

#ifdef DEBUG_GL
        CONTROLE_ERRGL;
#endif // DEBUG_GL
        projection_curseur = DeProj(x, y, profondeur);
//        std::cout << "CalcProjectionCurseur(" << x << ", " << y << ") : projection_curseur=(" << projection_curseur.X() << "," << projection_curseur.Y() << "," << projection_curseur.Z() << ") : profondeur=" << profondeur << std::endl;
        return (profondeur < 1.); /* Contrôle si le pixel sous le curseur ne fait pas partie du fond d'après le Z-buffer. */
        //    return true;
    }

    void Moteur_GL::CalcPositionLumiere()
    {
        position_lumiere = DeProj(GLcoord(taille_affichage_x)*0.5f, GLcoord(taille_affichage_y)*0.5f, -0.5f);

        const float distance = 1.f; //1.5f;
        position_lumiere *= distance;
//        position_lumiere = MatriceAffichage() * position_lumiere;

//        std::cout << "Position de la lumière : " << position_lumiere.X() << ", " << position_lumiere.Y() << ", " << position_lumiere.Z() << std::endl;
    }

    void Moteur_GL::CalcMatricesAffichage()
    {
//        if (mode_vue_libre && false)
//        {
//            if (!CompareE(rotation_camera.Z(), 0.))
//                matrice_scene.Rotation(rotation_camera.Z(), 0., 0., 1.);
//            if (!CompareE(rotation_camera.Y(), 0.))
//                matrice_scene.Rotation(rotation_camera.Y(), 0., 1., 0.);
//            if (!CompareE(rotation_camera.X(), 0.))
//                matrice_scene.Rotation(rotation_camera.X(), 1., 0., 0.);

//            //        PGL::PVertex_GL tr = matrice_scene * Translation_camera;
//            //        matrice_solide.Translation(tr.X(), tr.Y(), tr.Z());

//            //        matrice_solide = matrice_scene;
//            matrice_solide.Inverse();
//            matrice_solide.Translation(Translation_camera.X(), Translation_camera.Y(), Translation_camera.Z());
//        }
//        else
//        {
        if (!CompareE(rotation_camera.Z(), 0.))
        {
            matrice_solide.Rotation(rotation_camera.Z(), 0., 0., 1.);
        }
        if (!CompareE(rotation_camera.Y(), 0.))
        {
            matrice_solide.Rotation(rotation_camera.Y(), 0., 1., 0.);
        }
        if (!CompareE(rotation_camera.X(), 0.))
        {
            matrice_solide.Rotation(rotation_camera.X(), 1., 0., 0.);
        }

        matrice_solide.Translation(Translation_camera);
//    }

//    if (parametres.mode_ortho)
//    {
//        CalculeMatriceProjections((float(taille_affichage_x)*echelle_affichage)+.5f, (float(taille_affichage_y)*echelle_affichage)+.5f);
//    }
//    else
//{
        matrice_solide.Echelle(echelle_affichage);

#ifdef DEBUG
//        std::cout << "Solide :: " << matrice_solide << std::endl;
//        std::cout << "Scene :: " << matrice_scene << std::endl;
#endif // DEBUG_GL

//    }

        matrice_scene_solide = matrice_scene * matrice_solide;

        matrice_normals = matrice_scene_solide;
        matrice_normals.Inverse();
        matrice_normals.Transpose();
    }

    void Moteur_GL::IdentiteMatrices()
    {
        MatriceScene().Identite();
        MatriceModele().Identite();
        MatriceAffichage().Identite();
    }

    void Moteur_GL::defTypeProjection(bool mode_ortho)
    {
        parametres.mode_ortho = mode_ortho;
    }

    void Moteur_GL::CalculeMatriceProjections(PMatriceGL4 &matrice, pint taille_x, pint taille_y)
    {
        matrice.Identite();
        GLcoord *mat = matrice.TableauNonConst();

        const GLcoord proche = 0.01;
        const GLcoord loin = 1000.;
        //    const GLcoord delta_z = loin - proche;
        const GLcoord delta_z_m = 1. / (loin - proche);

//        std::cout << "CalculeMatriceProjections(" << taille_x << ", " << taille_y << ") mode_ortho=" << parametres.mode_ortho << std::endl;

        if (parametres.mode_ortho)
        {
            pint d_max = (taille_x > taille_y) ? taille_x : taille_y;
            glViewport(((taille_x - d_max)/2), ((taille_y - d_max)/2), d_max, d_max);

            mat[10] = (loin + proche) * delta_z_m;
            mat[14] = -(2.0 * loin * proche) * delta_z_m;

//            glViewport(0, 0, taille_x, taille_y);
//            matrice_projections.defOrtho(-.5, .5, aspect_ratio*-.5, aspect_ratio*.5, -1., 1);
        }
        else
        {
            GLcoord aspect_ratio = GLcoord(taille_x) / GLcoord(taille_y);

            glViewport(0, 0, taille_x, taille_y);

            GLcoord xmax = proche * tan(DEG_RAD(fov*.5));
            GLcoord xmin = -xmax;
            GLcoord ymin = xmin / aspect_ratio;
            GLcoord ymax = xmax / aspect_ratio;

            mat[0] = (2.0 * proche) / (xmax - xmin);
            mat[5] = (2.0 * proche) / (ymax - ymin);
            mat[10] = (loin + proche) * delta_z_m;
            mat[15] = 0;

            mat[8] = -(xmax + xmin) / (xmax - xmin);
            mat[9] = -(ymax + ymin) / (ymax - ymin);
            mat[11] = 1.0;
            mat[14] = -(2.0 * loin * proche) * delta_z_m;
        }
//        std::cout << "CalculeMatriceProjections() Projection : " << matrice;
    }

    void Moteur_GL::defRotationCamera(const PGL::PVertex_GL &r)
    {
        rotation_camera = r;
    }

    void Moteur_GL::defRotationCameraX(const GLcoord x)
    {
        rotation_camera.refX() = x;
    }

    void Moteur_GL::defRotationCameraY(const GLcoord y)
    {
        rotation_camera.refY() = y;
    }

    void Moteur_GL::defRotationCameraZ(const GLcoord z)
    {
        rotation_camera.refZ() = z;
    }

    void Moteur_GL::defEchelleAffichage(GLcoord v, bool relatif)
    {
        if (relatif)
        {
            echelle_affichage += v;
        }
        else
        {
            echelle_affichage = v;
        }
    }

    void Moteur_GL::defChampVue(GLcoord v, bool relatif)
    {
        if (relatif)
        {
            fov += v;
        }
        else
        {
            fov = v;
        }
    }

    void Moteur_GL::defTailleAffichage(pint largeur, pint hauteur)
    {
        taille_affichage_x = largeur;
        taille_affichage_y = hauteur;
        CalculeMatriceProjections(MatriceProjection(), taille_affichage_x, taille_affichage_y);

        if (parametres.active_fbo && framebuffer.Valide())
        {
            framebuffer.defTailleEcran(largeur, hauteur);
        }
    }

    const PGL::PVertex_GL &Moteur_GL::TranslationCamera() const
    {
        return Translation_camera;
    }

    void Moteur_GL::defTranslationCamera(pfloat x, pfloat y, bool relatif)
    {
        if (relatif)
        {
            Translation_camera.defCoords(Translation_camera.X()+x, Translation_camera.Y()+y, Translation_camera.Z());
        }
        else
        {
            Translation_camera.defCoords(x, y, Translation_camera.Z());
        }
    }

    void Moteur_GL::defAvanceCamera(pfloat z, bool relatif)
    {
        if (relatif)
        {
            Translation_camera.defCoords(Translation_camera.X(), Translation_camera.Y(), Translation_camera.Z()+z);
        }
        else
        {
            Translation_camera.defCoords(Translation_camera.X(), Translation_camera.Y(), z);
        }
    }

    void Moteur_GL::defCouleurFond(const PGL::PCouleur_GL &c)
    {
//        std::cout << "Couleur du fond : " << c << std::endl;

        parametres.couleur_fond = c;
        framebuffer.defCouleurFond(c);

//        glClearColor(c.R(), c.V(), c.B(), c.Alpha());
    }

    void Moteur_GL::ReinitCouleurFond()
    {
        defCouleurFond(PCouleur_GL(COULEUR_FOND_PGL_FLOAT, 1.0));
    }

    PCouleur_GL Moteur_GL::CouleurFondReel() const
    {
        GLfloat couleurs[4];
        glReadPixels(0, taille_affichage_y-1, 1, 1, GL_RGB, GL_FLOAT, couleurs);
        return PCouleur_GL(couleurs[0], couleurs[1], couleurs[2], 1.0);
    }

    void Moteur_GL::ReinitEtatGL()
    {
        glEnable(GL_BLEND); /* Active le canal alpha (transparence). */
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        /* Anti-aliasing: */
        glEnable(GL_LINE_SMOOTH);
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

        glEnable(GL_POINT_SMOOTH);

#if FORCE_VERSION_GL < VERSION_GL(3, 0)
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
#endif // FORCE_VERSION_GL

//        glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

        glEnable(GL_DEPTH_TEST);
//        glClearDepth(1);
        glDepthMask(1);
        glDepthFunc(GL_LEQUAL);
//        glDepthFunc(GL_ALWAYS);

        glEnable(GL_POLYGON_OFFSET_LINE);
        glPolygonOffset(1., 2.);

        glEnable(GL_POLYGON_OFFSET_POINT);
        glPolygonOffset(1., 2.5);

        glEnable(GL_MULTISAMPLE);
        glFrontFace(GL_CCW); /* Ordre des coordonnées (sens anti-horaire); */
    }

    void Moteur_GL::InitialisationGL()
    {
        ReinitEtatGL();

        ReinitCouleurFond();
        glClearStencil(0);

        if (parametres.active_shaders)
        {
            defEtatShaders(true, true);

            const std::string& log_shader_filaire = InitialisationShaders(Shaders_GL::VShaderDefaut(), Shaders_GL::FShaderDefaut(), CONTEXTE_FILAIRE);
            if (log_shader_filaire.size() > 1) /* Erreur ?! */
            {
#ifdef DEBUG_GL
                std::cout << "Erreur d'initialisation du shader (défaut) pour le modèle filaire : " << log_shader_filaire << std::endl;
#endif // DEBUG_GL
            }
        }
        else
        {
            defEtatShaders(false, true);
        }

        if (parametres.active_fbo)
        {
            framebuffer.Gen(256, 256);
        }

        if (parametres.eclairage_dyn)
        {
            InitialisationEclairage(false);
        }

//        ExecAffichage();

#ifdef DEBUG_GL
        CONTROLE_ERRGL;
#endif // DEBUG_GL
    }

    void Moteur_GL::InitAffichage(bool affiche_origine, bool autorise_post_traitement)
    {
        if (!ExecAffichage())
        {
            return;
        }

#ifdef DEBUG_GL
//        CONTROLE_BUFFER_GL;
        CONTROLE_ERRGL;
#endif // DEBUG_GL

        utilise_fbo_tex = false;
        if (parametres.active_fbo && autorise_post_traitement && framebuffer.Valide())
        {
            utilise_fbo_tex = framebuffer.Bind();
//            if (utilise_fbo_tex)
//            {
//                std::cout << "Bind framebuffer !!" << std::endl;
//            }
//            else
//            {
//                std::cout << "Erreur bind framebuffer ?!" << std::endl;
//            }
#ifdef DEBUG_GL
//            CONTROLE_BUFFER_GL;
            CONTROLE_ERRGL;
#endif // DEBUG_GL
        }

        glClearColor(parametres.couleur_fond.R(), parametres.couleur_fond.V(), parametres.couleur_fond.B(), parametres.couleur_fond.Alpha());
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

#ifdef DEBUG_GL
        CONTROLE_ERRGL;
#endif // DEBUG_GL

        if (parametres.mode_ortho)
        {
            /* Dans le cas de l'utilisation de QOpenGLWidget, pour remplacer l'appel à glViewport() */
            CalculeMatriceProjections(MatriceProjection(), taille_affichage_x, taille_affichage_y);
        }
        CalcMatricesAffichage();

        glMatrixMode(GL_PROJECTION);
        glLoadMatrixf(MatriceProjection().Tableau());

        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixf(MatriceAffichage().Tableau());

//        std::cout << "Matrice de projection : " << MatriceProjection() << std::endl;
//        std::cout << "Matrice d'affichage : " << MatriceAffichage() << std::endl;

        if (parametres.eclairage_dyn)
        {
            CalcPositionLumiere();
            const GLfloat pos_lum[] = { GLfloat(position_lumiere.X()), GLfloat(position_lumiere.Y()), GLfloat(position_lumiere.Z()), 0 };
            glLightfv(GL_LIGHT0, GL_POSITION, pos_lum);
        }

        GenDivers(affiche_origine);

        if (parametres.active_shaders)
        {
            if (program_modele.ValideLiage())
            {
                program_modele.Activation(MatriceAffichage(), MatriceProjection(), MatriceNormals(), PositionLumiere());
            }

            if (program_filaire.ValideLiage())
            {
                program_filaire.Activation(MatriceAffichage(), MatriceProjection());
            }
        }

#ifdef DEBUG_GL
        CONTROLE_ERRGL;
#endif // DEBUG_GL
    }

    bool Moteur_GL::ExecShaderPost(bool force)
    {
        if (!parametres.active_shaders)
        {
            return false;
        }
        if (force || framebuffer.UtiliseChronoShaderPost())
        {
            if (parametres.active_fbo && utilise_fbo_tex && framebuffer.Valide() && !AffichageEncours())
            {
//                CONTROLE_BUFFER_GL;
                framebuffer.Affichage();
//                CONTROLE_BUFFER_GL;
                return true;
            }
        }
        return false;
    }

    void Moteur_GL::FinAffichage()
    {
        if (!ExecAffichage())
        {
            return;
        }

#ifdef DEBUG_GL
        CONTROLE_ERRGL;
#endif // DEBUG_GL

        ExecShaderPost(true);

//#warning TEMP, TEST affichage ATH (déconne si utilisation du FBO):
//#ifdef DEBUG
//        if (true)
//        {
////            Extensions_GL::glBindFramebuffer(GL_FRAMEBUFFER, 0);
//            Program_GL::DesactivePrograms();
//            static PMatriceGL4 matrice_proj_ident;
//            static PMatriceGL4 matrice_mod_ident;

//            CalculeMatriceProjections(matrice_proj_ident, 256, 256);
////            matrice_proj_ident.defOrigine(0, 0, -0.5);

//            matrice_mod_ident.Identite();
//            matrice_mod_ident.Rotation(45, 0., 1., 0.);
//            matrice_mod_ident.Rotation(-30, 1., 0., 0.);
//            matrice_mod_ident.Translation(0., 0., 0.1);

//            glMatrixMode(GL_PROJECTION);
//            glLoadMatrixf(matrice_proj_ident.Tableau());
//            glMatrixMode(GL_MODELVIEW);
////            glLoadMatrixf(matrice_mod_ident.Tableau());
//            glLoadMatrixf(matrice_solide.Tableau());

//            static PTampon_GL tampon_arretes(GL_LINES);
//            static PTampon_GL tampon_cube(GL_TRIANGLES);

//            tampon_cube.Reinit();
//            tampon_arretes.Reinit();

//            static const PCouleur_GL couleur_cube(1.0, 1.0, 1.0, 1.0);
//            static const PCouleur_GL couleur_arretes(0.6, 0.6, 0.6, 0.6);
//            const float taille_demi_cube = 0.3;
//            const float profondeur = 0.01;

//            static const PVertex_GL p1(-taille_demi_cube, -taille_demi_cube, -taille_demi_cube+profondeur);
//            static const PVertex_GL p2( taille_demi_cube, -taille_demi_cube, -taille_demi_cube+profondeur);
//            static const PVertex_GL p3( taille_demi_cube,  taille_demi_cube, -taille_demi_cube+profondeur);
//            static const PVertex_GL p4(-taille_demi_cube,  taille_demi_cube, -taille_demi_cube+profondeur);
//            static const PVertex_GL p5(-taille_demi_cube, -taille_demi_cube,  taille_demi_cube+profondeur);
//            static const PVertex_GL p6( taille_demi_cube, -taille_demi_cube,  taille_demi_cube+profondeur);
//            static const PVertex_GL p7( taille_demi_cube,  taille_demi_cube,  taille_demi_cube+profondeur);
//            static const PVertex_GL p8(-taille_demi_cube,  taille_demi_cube,  taille_demi_cube+profondeur);

//            tampon_cube.Ajout(PVertice3_GL(p1, p2, p3), couleur_cube);
//            tampon_cube.Ajout(PVertice3_GL(p1, p3, p4), couleur_cube);

//            tampon_cube.Ajout(PVertice3_GL(p7, p6, p5), couleur_cube);
//            tampon_cube.Ajout(PVertice3_GL(p7, p5, p8), couleur_cube);

//            tampon_cube.Ajout(PVertice3_GL(p2, p6, p7), couleur_cube);
//            tampon_cube.Ajout(PVertice3_GL(p2, p7, p3), couleur_cube);

//            tampon_cube.Ajout(PVertice3_GL(p1, p4, p8), couleur_cube);
//            tampon_cube.Ajout(PVertice3_GL(p1, p8, p5), couleur_cube);

//            tampon_cube.Ajout(PVertice3_GL(p4, p3, p7), couleur_cube);
//            tampon_cube.Ajout(PVertice3_GL(p4, p7, p8), couleur_cube);

//            tampon_cube.Ajout(PVertice3_GL(p1, p5, p6), couleur_cube);
//            tampon_cube.Ajout(PVertice3_GL(p1, p6, p2), couleur_cube);

//            tampon_cube.Affichage();

//            tampon_arretes.Ajout(PVertice2_GL(p1, p2), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p2, p3), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p3, p4), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p4, p1), couleur_arretes);

//            tampon_arretes.Ajout(PVertice2_GL(p5, p6), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p6, p7), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p7, p8), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p8, p5), couleur_arretes);

//            tampon_arretes.Ajout(PVertice2_GL(p2, p6), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p6, p7), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p7, p3), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p3, p2), couleur_arretes);

//            tampon_arretes.Ajout(PVertice2_GL(p1, p5), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p5, p8), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p8, p4), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p1, p1), couleur_arretes);

//            tampon_arretes.Ajout(PVertice2_GL(p4, p3), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p3, p7), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p7, p8), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p8, p4), couleur_arretes);

//            tampon_arretes.Ajout(PVertice2_GL(p1, p2), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p2, p6), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p6, p5), couleur_arretes);
//            tampon_arretes.Ajout(PVertice2_GL(p5, p1), couleur_arretes);

//            tampon_arretes.Affichage();
//        }
//#endif // DEBUG

//        std::cout << "Couleurs de fond : " << CouleurFond() << " :: " << CouleurFondReel() << std::endl;

#ifdef DEBUG_GL
        CONTROLE_ERRGL;
#endif // DEBUG_GL
    }

    bool Moteur_GL::AffichageFilaire(const PTampon_GL &tampon) const
    {
        if (!ExecAffichage())
        {
            return false;
        }
        return program_filaire.Affichage(tampon);
    }

    bool Moteur_GL::AffichageModele(const PTampon_GL &tampon) const
    {
        if (!ExecAffichage())
        {
            return false;
        }
        return program_modele.Affichage(tampon);
    }

    void Moteur_GL::SyncAffichage(bool flush) const
    {
        if (flush)
        {
            glFlush();
        }
        glFinish();
    }

    const std::string& Moteur_GL::InitialisationShaders(const char *source_vertex_shader, const char *source_pixel_shader, puint contexte)
    {
        static const std::string str_vide("");
        static const std::string str_nil("(nil)");

        const bool valide_ptrs = Ptest_ptr(source_vertex_shader) || Ptest_ptr(source_pixel_shader);
        if (!valide_ptrs)
        {
            return str_vide;
        }

#if FORCE_VERSION_GL >= VERSION_GL(3, 2) /* Macros compatibilité core GL >=3.2 */
        const std::string str_vertex_shader = source_vertex_shader ? (std::string("#version " FORCE_VERSION_GLSL_STR " core\n#define attribute in\n#define varying out\n#define gl_Color Color\nattribute vec4 " NOM_ATTR_COLOR ";\n") + std::string(source_vertex_shader)) : std::string();
        const std::string str_pixel_shader = source_pixel_shader ? (std::string("#version " FORCE_VERSION_GLSL_STR " core\n#define varying in\n#define texture2D texture\n#define gl_FragColor " NOM_ATTR_POST_FRAGCOLOR "\nout vec4 " NOM_ATTR_POST_FRAGCOLOR ";\n") + std::string(source_pixel_shader)) : std::string();
#else
        const std::string str_vertex_shader(Ptest_ptr(source_vertex_shader) ? source_vertex_shader : "");
        const std::string str_pixel_shader(Ptest_ptr(source_pixel_shader) ? source_pixel_shader : "");
#endif // FORCE_VERSION_GL

#ifdef DEBUG_GLSL
        std::cout << "Shaders après post_traitement : Vertex:\n" << str_vertex_shader << std::endl;
        std::cout << "Shaders après post_traitement : Pixel:\n" << str_pixel_shader << std::endl;
#endif // DEBUG_GL

        if (contexte == CONTEXTE_FILAIRE)
        {
            if (!parametres.active_shaders) { return str_nil; }
            BloqueAffichage();
            if (program_filaire.InitialisationShader(str_vertex_shader.data(), str_pixel_shader.data()))
            {
            }
#ifdef DEBUG_GLSL
            else
            {
                std::cout << "Erreur d'initialisation d'attributs de shaders pour le modèle filaire (" << (source_vertex_shader!=0) << "&" << (source_pixel_shader!=0) << ")." << std::endl;
            }
#endif // DEBUG_GL
            ActiveAffichage();
            defTailleAffichage(taille_affichage_x, taille_affichage_y); /* Force le re-calcul des matrices de projection. */
            return program_filaire.JournalCompilationShaders();
        }
        else if (contexte == CONTEXTE_MODELE)
        {
            if (!parametres.active_shaders) { return str_nil; }
            BloqueAffichage();
            if (program_modele.InitialisationShader(str_vertex_shader.data(), str_pixel_shader.data()))
            {
            }
#ifdef DEBUG_GLSL
            else
            {
                std::cout << "Erreur d'initialisation d'attributs de shader pour le modèle (" << (source_vertex_shader!=0) << "&" << (source_pixel_shader!=0) << ")." << std::endl;
            }
#endif // DEBUG_GL
            ActiveAffichage();
            defTailleAffichage(taille_affichage_x, taille_affichage_y); /* Force le re-calcul des matrices de projection. */
            return program_modele.JournalCompilationShaders();
        }
        else if (contexte == CONTEXTE_POST)
        {
            if (!parametres.active_fbo) { return str_nil; }
            BloqueAffichage();
            if (framebuffer.InitialisationShaders(str_vertex_shader.data(), str_pixel_shader.data()))
            {
            }
#ifdef DEBUG_GLSL
            else
            {
                std::cout << "Erreur d'initialisation d'attributs de shader pour le post-traitement (" << (source_vertex_shader!=0) << "&" << (source_pixel_shader!=0 )<< ")." << std::endl;
            }
#endif // DEBUG_GL
            ActiveAffichage();
            defTailleAffichage(taille_affichage_x, taille_affichage_y); /* Force le re-calcul des matrices de projection. */
            return framebuffer.JournalCompilationShaders();
        }
        return str_nil;
    }

    bool Moteur_GL::DumpShaderBin(const std::string &chemin, puint contexte) const
    {
        if (contexte == CONTEXTE_FILAIRE)
        {
            return program_filaire.DumpShaderBinaire(chemin);
        }
        else if (contexte == CONTEXTE_MODELE)
        {
            return program_modele.DumpShaderBinaire(chemin);
        }
        else if (contexte == CONTEXTE_POST)
        {
            return framebuffer.DumpShaderBinaire(chemin);
        }
        return false;
    }

    bool Moteur_GL::defEtatShaders(bool etat, bool init)
    /* Active/Desactive les shaders. */
    {
        if ((parametres.active_shaders == etat) && !init)
        {
            return false;
        }

        const bool ancien_etat = parametres.active_shaders;
        parametres.active_shaders = etat;

        if (ancien_etat && !init) /* Desactive */
        {
            if (program_filaire.ValideLiage())
            {
                program_filaire.Desactive();
            }
            if (program_modele.ValideLiage())
            {
                program_modele.Desactive();
            }
            if (framebuffer.ValideLiageShaders())
            {
                framebuffer.DesactiveProgram();
            }
        }

        if (parametres.active_shaders && (!ancien_etat || init)) /* Activation */
        {
            return true;

//            if (!program_filaire.ValideSupport())
//            {
//                if (!program_filaire.Reactive())
//                    return false;
//            }

//            if (!program_filaire.ValideLiage())
//            {
//                if (!InitialisationShaders(Shaders_GL::VShaderDefaut(), Shaders_GL::FShaderDefaut(), CONTEXTE_FILAIRE))
//                {
//                    return false;
//                }
//            }

//            if (!init)
//            {
////                return program_modele.Reactive();
//                return program_modele.Desactive();

////                DesactivationAttributsShadersModele();
////                bool v = program_modele.Reactive();
////                if (v)
////                {
////                    return AssignationAttributsShaderModele();
////                }
////                return false;
//            }
//            else
//            {

//            }
//            return true;
        }
        return true;
    }

    void Moteur_GL::GenOrigineDiv(const PGL::PVertex_GL &v)
    /* Affiche une origine. */
    {
        static const PGL::PCouleur_GL couleur_X(PGL_NORM_BYTE_FLOAT(204), PGL_NORM_BYTE_FLOAT(77), PGL_NORM_BYTE_FLOAT(128));
        static const PGL::PCouleur_GL couleur_Y(PGL_NORM_BYTE_FLOAT(102), PGL_NORM_BYTE_FLOAT(205), PGL_NORM_BYTE_FLOAT(128));
        static const PGL::PCouleur_GL couleur_Z(PGL_NORM_BYTE_FLOAT(25), PGL_NORM_BYTE_FLOAT(205), PGL_NORM_BYTE_FLOAT(205));

        // X
        AjoutLigneDiv(PVertice2_GL(PVertex_GL(-v.X()+TAILLE_ORIGINE_NEG_PGL, -v.Y(), -v.Z()),
                                   PGL::PVertex_GL(-v.X()+TAILLE_ORIGINE_PGL, -v.Y(), -v.Z())),
                      couleur_X);

        // Y
        AjoutLigneDiv(PVertice2_GL(PVertex_GL(-v.X(), -v.Y()+TAILLE_ORIGINE_NEG_PGL,-v.Z()),
                                   PGL::PVertex_GL(-v.X(), -v.Y()+TAILLE_ORIGINE_PGL, -v.Z())),
                      couleur_Y);
        // Z
        AjoutLigneDiv(PVertice2_GL(PVertex_GL(-v.X(), -v.Y(), -v.Z()+TAILLE_ORIGINE_NEG_PGL),
                                   PGL::PVertex_GL(-v.X(), -v.Y(), -v.Z()+TAILLE_ORIGINE_PGL)),
                      couleur_Z);
    }

    void Moteur_GL::GenMatriceDiv(const PGL::PMatriceGL4 &m)
    /* Affiche une matrice. */
    {
        static const PGL::PCouleur_GL couleur_X(PGL_NORM_BYTE_FLOAT(204), PGL_NORM_BYTE_FLOAT(77), PGL_NORM_BYTE_FLOAT(128));
        static const PGL::PCouleur_GL couleur_Y(PGL_NORM_BYTE_FLOAT(102), PGL_NORM_BYTE_FLOAT(205), PGL_NORM_BYTE_FLOAT(128));
        static const PGL::PCouleur_GL couleur_Z(PGL_NORM_BYTE_FLOAT(25), PGL_NORM_BYTE_FLOAT(205), PGL_NORM_BYTE_FLOAT(205));
        static const PGL::PCouleur_GL couleur_diago(PGL_NORM_BYTE_FLOAT(205), PGL_NORM_BYTE_FLOAT(205), PGL_NORM_BYTE_FLOAT(205));

        GLfloat taille_espace = 1.;
        PGL::PVertex_GL origine = m.Origine(); // m * PGL::PVertex_GL(0., 0., 0.);
        PGL::PVertex_GL px = m * PGL::PVertex_GL(taille_espace, 0., 0.);
        PGL::PVertex_GL py = m * PGL::PVertex_GL(0., taille_espace, 0.);
        PGL::PVertex_GL pz = m * PGL::PVertex_GL(0., 0., taille_espace);

        PGL::PVertex_GL p_diago = ((m * PGL::PVertex_GL(.57735, .57735, .57735)) * taille_espace);

        AjoutLigneDiv(PVertice2_GL(origine, origine+px), couleur_X);
        AjoutLigneDiv(PVertice2_GL(origine, origine+py), couleur_Y);
        AjoutLigneDiv(PVertice2_GL(origine, origine+pz), couleur_Z);
        AjoutLigneDiv(PVertice2_GL(origine, origine+p_diago), couleur_diago);
    }

    void Moteur_GL::GenDivers(bool affiche_origine)
    {
        VideDiv();

        static const PGL::PVertex_GL point_origine(0., 0., 0.);
        static const PGL::PCouleur_GL couleur_blanc(1.0, 1.0, 1.0);

        if (affiche_origine)
        {
            const PGL::PVertex_GL coords_origine = MatriceScene().Origine();
            GenOrigineDiv(coords_origine);
        }

//#ifdef DEBUG
//        GenMatriceDiv(matrice_scene_solide);
//        GenMatriceDiv(matrice_normals);
//        GenMatriceDiv(matrice_scene);
//        GenMatriceDiv(matrice_solide);
//#endif // DEBUG

        // Affichage du rayon de la caméra:
#ifdef RAYON_CAMERA_GL
        AjoutSommetDiv(projection_curseur, couleur_blanc);
        AjoutSommetDiv(point_origine, couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(projection_curseur, point_origine), couleur_blanc);
#endif // RAYON_CAMERA_GL

#ifdef DEBUG_GL
        if (parametres.eclairage_dyn)
        {
//            std::cout << "Position de la lumière : " << position_lumiere << std::endl;
            AjoutSommetDiv(position_lumiere, couleur_blanc);
            AjoutLigneDiv(PVertice2_GL(position_lumiere, PVertex_GL(0., 0., 0.)), couleur_blanc);
        }
#endif // DEBUG_GL

        // Affichage de la boite englobante:
#ifdef BOITE_ENGLOBANTE_GL
        const float demi_largeur_boite = .5f;

        /*
         8-------7
        /|      /|
       / |     / |
      4--|----3  |
      |  5----|--6
      | /     | /
      1-------2
    */

        static const PGL::PVertex_GL vb1(-demi_largeur_boite, -demi_largeur_boite, -demi_largeur_boite);
        static const PGL::PVertex_GL vb2(demi_largeur_boite, -demi_largeur_boite, -demi_largeur_boite);
        static const PGL::PVertex_GL vb3(demi_largeur_boite, demi_largeur_boite, -demi_largeur_boite);
        static const PGL::PVertex_GL vb4(-demi_largeur_boite, demi_largeur_boite, -demi_largeur_boite);

        static const PGL::PVertex_GL vb5(-demi_largeur_boite, -demi_largeur_boite, demi_largeur_boite);
        static const PGL::PVertex_GL vb6(demi_largeur_boite, -demi_largeur_boite, demi_largeur_boite);
        static const PGL::PVertex_GL vb7(demi_largeur_boite, demi_largeur_boite, demi_largeur_boite);
        static const PGL::PVertex_GL vb8(-demi_largeur_boite, demi_largeur_boite, demi_largeur_boite);

        AjoutLigneDiv(PVertice2_GL(vb1, vb2), couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(vb2, vb3), couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(vb3, vb4), couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(vb4, vb1), couleur_blanc);

        AjoutLigneDiv(PVertice2_GL(vb5, vb6), couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(vb6, vb7), couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(vb7, vb8), couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(vb8, vb5), couleur_blanc);

        AjoutLigneDiv(PVertice2_GL(vb5, vb1), couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(vb6, vb2), couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(vb7, vb3), couleur_blanc);
        AjoutLigneDiv(PVertice2_GL(vb8, vb4), couleur_blanc);

//        Test, surfaces du cube :
//        static const PGL::PCouleur_GL couleur_blanc_trans(1.0, 1.0, 1.0, 1.0);

//        AjoutTriangleDiv(PVertice3_GL(vb1, vb2, vb3), PGL::PVertex_GL(0., 0., -1.), couleur_blanc_trans);
//        AjoutTriangleDiv(PVertice3_GL(vb1, vb3, vb4), PGL::PVertex_GL(0., 0., -1.), couleur_blanc_trans);

//        AjoutTriangleDiv(PVertice3_GL(vb5, vb6, vb7), PGL::PVertex_GL(0., 0., 1.), couleur_blanc_trans);
//        AjoutTriangleDiv(PVertice3_GL(vb5, vb7, vb8), PGL::PVertex_GL(0., 0., 1.), couleur_blanc_trans);

//        AjoutTriangleDiv(PVertice3_GL(vb1, vb5, vb8), PGL::PVertex_GL(-1., 0., 0.), couleur_blanc_trans);
//        AjoutTriangleDiv(PVertice3_GL(vb1, vb8, vb4), PGL::PVertex_GL(-1., 0., 0.), couleur_blanc_trans);

//        AjoutTriangleDiv(PVertice3_GL(vb2, vb6, vb7), PGL::PVertex_GL(1., 0., 0.), couleur_blanc_trans);
//        AjoutTriangleDiv(PVertice3_GL(vb2, vb7, vb3), PGL::PVertex_GL(1., 0., 0.), couleur_blanc_trans);

//        AjoutTriangleDiv(PVertice3_GL(vb1, vb2, vb6), PGL::PVertex_GL(0., -1, 0.), couleur_blanc_trans);
//        AjoutTriangleDiv(PVertice3_GL(vb1, vb6, vb5), PGL::PVertex_GL(0., -1, 0.), couleur_blanc_trans);

//        AjoutTriangleDiv(PVertice3_GL(vb4, vb3, vb7), PGL::PVertex_GL(0., 1, 0.), couleur_blanc_trans);
//        AjoutTriangleDiv(PVertice3_GL(vb4, vb7, vb8), PGL::PVertex_GL(0., 1, 0.), couleur_blanc_trans);

#endif // BOITE_ENGLOBANTE_GL
    }

    void Moteur_GL::VideDiv()
    {
        sommets_div.Reinit();
        filaire_div.Reinit();
        triangles_div.Reinit();
    }

    void Moteur_GL::AjoutSommetDiv(const PGL::PVertex_GL &point, const PGL::PCouleur_GL &couleur)
    {
        sommets_div.Ajout(point, couleur);
    }

    void Moteur_GL::AjoutLigneDiv(const PGL::PVertice2_GL &ligne, const PGL::PCouleur_GL &couleur)
    {
        filaire_div.Ajout(ligne, couleur);
    }

    void Moteur_GL::AjoutTriangleDiv(const PGL::PVertice3_GL &triangle, const PGL::PVertex_GL &norm, const PGL::PCouleur_GL &couleur)
    {
        triangles_div.Ajout(triangle, norm, couleur);
    }
} // namespace PGL


#ifdef PERSPECTIVE_GL

namespace PGL
{
    Params_Perspective_GL::Params_Perspective_GL(float opacite_solide_, pint idcouleur_solide_, bool mode_2d_) :
        opacite_solide(opacite_solide_), idcouleur_solide(idcouleur_solide_), mode_2d(mode_2d_), lissage(false),
        imprimante3d(false), plateau_imprimante3d_x(0), plateau_imprimante3d_y(0), imprimante3d_z(0),
        type_affichage(PGL_MODE_AFFICHAGE_DEFAUT),
        couleur_sommets(COULEUR_POINTS_F), couleur_segments(COULEUR_LIGNES_F), couleur_imprimante(PGL_NORM_BYTE_FLOAT(102), PGL_NORM_BYTE_FLOAT(102), PGL_NORM_BYTE_FLOAT(102), 1.0), couleur_repere_sommets(COULEUR_SELECT_POINTS_F), couleur_repere_faces(COULEUR_REP_SURFACES_F)
    {
    }

    void Params_Perspective_GL::defImprimante3D(bool etat, float x, float y, float z)
    {
        imprimante3d = etat;
        plateau_imprimante3d_x = x;
        plateau_imprimante3d_y = y;
        imprimante3d_z = z;
    }

    Perspective_GL::Perspective_GL(const Params_GL &params_gl, const Params_Perspective_GL &params_pgl) : Moteur_GL(params_gl), parametres(params_pgl),
        Echelle_scene(1.),
        IdSelectionPoint(IDNUL), IdSelectionSegment(IDNUL), IdSelectionSurfaces(IDNUL),
        aff_origine(true), aff_arretes(false), aff_surfaces(true), aff_modele(true), aff_filaire(true), aff_projections(true),
        aff_developpes(params_pgl.mode_2d), aff_sommets(false), aff_imprimante(false), aff_sommet_repere(false),
        sommets(GL_POINTS), filaire(GL_LINES), imprimante(GL_LINES), projections(GL_LINES), divers(GL_LINES), textes(GL_LINES), developpes(GL_LINES), triangles_surfaces(GL_TRIANGLES), centre_surfaces(GL_POINTS)
    {
        Origine_scene.defCoords(0.,0.,0.);
        defIdCouleurScene(parametres.idcouleur_solide);
        defAffichage(parametres.type_affichage);
    }

    Perspective_GL::~Perspective_GL()
    {
        sommets.DesactiveVBO();
        filaire.DesactiveVBO();
        imprimante.DesactiveVBO();
        projections.DesactiveVBO();
        divers.DesactiveVBO();
        textes.DesactiveVBO();
        developpes.DesactiveVBO();
        triangles_surfaces.DesactiveVBO();
        centre_surfaces.DesactiveVBO();
    }

    void Perspective_GL::InitialisationAffichage()
    {
        Moteur_GL::InitialisationGL();
    }

    void Perspective_GL::InitialisationEtatAffichage()
    {
        Moteur_GL::ReinitEtatGL();
    }

    void Perspective_GL::VideScene()
    {
        const bool desactive_vbos = true;
        sommets.Reinit(desactive_vbos);
        filaire.Reinit(desactive_vbos);

        imprimante.Reinit(desactive_vbos);

        projections.Reinit(desactive_vbos);
        divers.Reinit(desactive_vbos);
        developpes.Reinit(desactive_vbos);
        triangles_surfaces.Reinit(desactive_vbos);
        centre_surfaces.Reinit(desactive_vbos);
        textes.Reinit(desactive_vbos);
        Moteur_GL::VideDiv();
    }

    void Perspective_GL::defImprimante3D(bool etat, float x, float y, float z)
    {
//        std::cout << "Nouvel état d'imprimante : " << etat << std::endl;
        parametres.defImprimante3D(etat, x, y, z);
    }

    void Perspective_GL::defEtatLissage(bool etat)
    {
        parametres.lissage = etat;
    }

    void Perspective_GL::MiseaJourVBO(puint modeles)
    {
        if (modeles == MODELE_NUL)
        {
            return;
        }

//        std::cout << "MiseaJourVBO : (filaire :" << (modeles & TYPE_MODELES::MODELE_SOMMETS) << "&" << sommets.VBOActif() << "), " << "(segments :" << (modeles & TYPE_MODELES::MODELE_FILAIRE) << "&" << filaire.VBOActif() << "), " << "(triangles :" << (modeles & TYPE_MODELES::MODELE_FACES) << "&" << triangles_surfaces.VBOActif() << ")." << std::endl;
//        std::cout << "Sommets : " << sommets.VerticesConst().size() << ", Filaire : " << filaire.VerticesConst().size() << ", Triangles : " << triangles_surfaces.VerticesConst().size() << std::endl;

        BloqueAffichage();

        if ((modeles & MODELE_SOMMETS) && sommets.VBOActif())
        {
            sommets.Transfert();
        }

        if ((modeles & MODELE_FILAIRE) && filaire.VBOActif())
        {
            filaire.Transfert();
        }

        if ((modeles & MODELE_FACES) && triangles_surfaces.VBOActif())
        {
            triangles_surfaces.Transfert();
        }
        ActiveAffichage();
    }

    void Perspective_GL::defOpaciteSolide(pint val)
    /* Assigne le niveau de transparence */
    {
        parametres.opacite_solide = val >= 0 ? (val <= 100 ? val : 100) : 0;
        float transparence_f = float(parametres.opacite_solide) * 2.55; /* 1 / 255 */

        /* Assigne la transparence sur les triangles */
        const GLcoord calc_alpha = static_cast<GLcoord>(transparence_f);

        PVectCouleur_GL &couleurs_triangles = triangles_surfaces.Couleurs();
        const puint n_couleurs = couleurs_triangles.size();

        for(puint i=0; i<n_couleurs; )
        {
            PGL::PCouleur_GL &c1 = couleurs_triangles[i];
            PGL::PCouleur_GL &c2 = couleurs_triangles[++i];
            PGL::PCouleur_GL &c3 = couleurs_triangles[++i];
            ++i;
            c1.defAlpha(calc_alpha);
            c2.defAlpha(calc_alpha);
            c3.defAlpha(calc_alpha);
        }
        MiseaJourVBO(MODELE_FACES);
    }

    void Perspective_GL::defIdCouleurScene(puint ral)
    /* Met à jour la couleur de la scène depuis une couleure RAL. */
    {
//        std::cout << "Perspective_GL::defIdCouleurScene : " << ral << std::endl;
        IdCouleurSolide = ral;

        PVectCouleur_GL &couleurs_triangles = triangles_surfaces.Couleurs();
        const PVectVert_GL &norms_triangles = triangles_surfaces.Norms();

        const puint n_couleurs = couleurs_triangles.size();

        for(puint i=0; i<n_couleurs; ++i)
        {
//            std::cout << ":: " << norms_triangles[i].X() << " : " << norms_triangles[i].Y() << " : " << norms_triangles[i].Z() << std::endl;
            SelectionCouleur(couleurs_triangles[i], norms_triangles[i]);
        }

        MiseaJourVBO(MODELE_FACES);
    }

    void Perspective_GL::AfficheScene()
    {
        if (!ExecAffichage())
        {
            return;
        }

        bool autorise_shaders_post = (!aff_arretes) && (aff_surfaces);

        Moteur_GL::InitAffichage(aff_origine, autorise_shaders_post);

        if ((IdSelectionPoint != IDNUL) && ((static_cast<std::size_t>(IdSelectionPoint)) < Sommets().size()))
        {
//            std::cout << "Ajout de sommet (selection du point : " << IdSelectionPoint << std::endl;
            AjoutSommetDiv(Sommets()[IdSelectionPoint], parametres.couleur_repere_sommets);
        }

        if (IdSelectionSegment != IDNUL && ((static_cast<std::size_t>(IdSelectionSegment)) < (Filaire().size()/PVertice2_GL::NombreVertices())))
        {
            const PVertex_GL &v1 = Filaire()[(IdSelectionSegment*PVertice2_GL::NombreVertices())];
            const PVertex_GL &v2 = Filaire()[(IdSelectionSegment*PVertice2_GL::NombreVertices())+1];
            AjoutSommetDiv(v1.centre(v2), parametres.couleur_repere_segments);
        }

        if ((IdSelectionSurfaces != IDNUL) && ((static_cast<std::size_t>(IdSelectionSurfaces)) < centre_surfaces.VerticesConst().size()))
        {
            AjoutSommetDiv(PVertex_GL(centre_surfaces.VerticesConst()[IdSelectionSurfaces]), parametres.couleur_repere_faces);
        }

        if (aff_sommet_repere)
        {
            AjoutSommetDiv(sommets_repere[0], PGL::PCouleur_GL(0.0, 1.0, 0.0));
            AjoutLigneDiv(PVertice2_GL(sommets_repere[1], sommets_repere[2]), PGL::PCouleur_GL(1.0, 0.0, 0.0));
        }

        if (aff_surfaces)
        {
            if (aff_modele)
            {
                AfficheSurfaces(false);
            }
        }

        if (aff_developpes)
        {
            AfficheDeveloppes();
        }

        if (aff_imprimante)
        {
            AfficheImprimante();
        }

        if (aff_arretes)
        {
            AfficheSurfaces(true);
        }

        if (aff_surfaces)
        {
//            if (aff_modele)
//            {
//                AfficheSurfaces(false);
//            }

            if (aff_filaire)
            {
                AfficheDivers();
                AfficheFilaire(); /* Modèle filaire */
            }

            if (aff_projections)
            {
                AfficheTextes();
                AfficheProjections(); /* Modèle des projections. */
            }

            if (aff_sommets) /* Points */
            {
                AfficheSommets();
            }
        }

        Moteur_GL::AffichageDivers();

        Moteur_GL::FinAffichage();
#ifdef DEBUG_GL
        CONTROLE_ERRGL;
#endif // DEBUG_GL
    }

    bool Perspective_GL::ExecShaderPost(bool force)
    /* Mise à jour de l'affichage (données temporelles par exemple, sans re-générer le modèle
        (à moins de forcer la mise à jour, elle ne sera pas effectuée si le shader de post-traitement ne fait pas appel aux données de temps). */
    {
        return Moteur_GL::ExecShaderPost(force);
    }

    void Perspective_GL::AfficheSurfaces(bool affiche_arretes) const
    {
        if (ParametresGL().eclairage_dyn)
        {
            glEnable(GL_LIGHTING);
            glEnable(GL_LIGHT0);
            glShadeModel(GL_SMOOTH); /* Gouraud */
        }

#ifdef SENS_SURFACES_GL // Ignore les faces intérieures (ignore les surfaces dont le vecteur normal est dans la mauvaise direction
        glCullFace(GL_BACK);
        glEnable(GL_CULL_FACE);
#endif // SENS_SURFACES_GL

        bool transparence = false;

        if (affiche_arretes) // Affichage des arrêtes
        {
            glLineWidth(1.);
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//            glColor4f(COULEUR_ARRETES, TransparenceSolide);
        }
        else // Modèle plein
        {
            glEnable(GL_POLYGON_OFFSET_FILL);
            glPolygonOffset(1, 1e-5); // Décalage sur le Z-buffer pour placer les triangles en arrière (et faire apparaitre les points et lignes).
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

//            if (parametres.opacite_solide < 100)
            {
                glEnable(GL_BLEND); /* Active le canal alpha (transparence). */
//                std::cout << "Utilisation de la transparence ! : " << parametres.opacite_solide << std::endl;
                transparence = true;
            }
        }

        if (ParametresGL().eclairage_uni)
        {
            glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
        }
        else
        {
            glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
        }

        glEnable(GL_COLOR_MATERIAL);

//        AfficheTableauTriangles(triangles, norms_triangles, couleurs_triangles);

//        std::cout << "Affichage des triangles. Valide attributs : " << attribute_vertex.Valide() << " : " << attribute_normals.Valide() << std::endl;

//        triangles_surfaces.Affichage(attribute_vertex, attribute_colors, attribute_normals);
        Moteur_GL::AffichageModele(triangles_surfaces);

        glPointSize(ParametresGL().ep_points);

        if (!affiche_arretes)
        {
            glDisable(GL_POLYGON_OFFSET_FILL);
        }

#ifdef SENS_SURFACES_GL
        glDisable(GL_CULL_FACE);
#endif // SENS_SURFACES_GL

        if (transparence)
        {
            glDisable(GL_BLEND);
        }

        if (ParametresGL().eclairage_dyn)
        {
            glDisable(GL_LIGHT0);
            glDisable(GL_LIGHTING);
        }
    }

    void Perspective_GL::AfficheProjections() const
    {
        glEnable(GL_BLEND); /* Active le canal alpha (transparence). */
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glLineWidth(ParametresGL().ep_lignes * 1.2);
        AffichageFilaire(projections);

        glDisable(GL_BLEND);
    }

    void Perspective_GL::AfficheDivers() const
    {
        glEnable(GL_BLEND); /* Active le canal alpha (transparence). */
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glLineWidth(ParametresGL().ep_lignes * 1.2);
        AffichageFilaire(divers);

        glDisable(GL_BLEND);
    }

    void Perspective_GL::AfficheTextes() const
    {
        glLineWidth(ParametresGL().ep_lignes * 1.2);
        AffichageFilaire(textes);
    }

    void Perspective_GL::AfficheDeveloppes() const
    {
        glLineWidth(ParametresGL().ep_lignes);
        AffichageFilaire(developpes);
    }

    void Perspective_GL::AfficheFilaire() const
    {
        glEnable(GL_BLEND); /* Active le canal alpha (transparence). */
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glLineWidth(ParametresGL().ep_lignes);

        AffichageFilaire(filaire);
        glDisable(GL_BLEND);
    }

    void Perspective_GL::AfficheImprimante() const
    {
//        glEnable(GL_BLEND); /* Active le canal alpha (transparence). */
//        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glLineWidth(1.f);
        AffichageFilaire(imprimante);
//        glDisable(GL_BLEND);
    }

    void Perspective_GL::AfficheSommets() const
    {
        glPointSize(ParametresGL().ep_points);
        AffichageFilaire(sommets);
        AffichageFilaire(centre_surfaces);
    }

    bool Perspective_GL::SelectionCouleur(PCouleur_GL &c) const
    /* Assigne dans c la couleur suivant la configuration */
    {
        if (IdCouleurSolide) // RAL défini ?
        {
            const Perspective3D::PCouleur_RAL &ral = Perspective3D::RAL::Index(IdCouleurSolide);
            c.defCouleur(ral);

//            std::cout << "Perspective_GL::SelectionCouleur :: " << c.R() << " : " << c.V() << " : " << c.B() << std::endl;
            return true;
        }
        return false;
    }

    void Perspective_GL::SelectionCouleur(PCouleur_GL &c, const PGL::PVertex_GL &norm) const
    /* Assigne dans c la couleur suivant la configuration */
    {
        if (IdCouleurSolide) // RAL défini ?
        {
            const Perspective3D::PCouleur_RAL &ral = Perspective3D::RAL::Index(IdCouleurSolide);
            c.defCouleur(ral);
        }
        else
        {
            /* Génère une couleur depuis le vecteur normal donné en argument */
            const float mult = 1.5;
            c.defRVB( POSITIFD(norm.X())*mult, POSITIFD(norm.Y())*mult, POSITIFD(norm.Z())*mult );
        }
//        std::cout << "Perspective_GL::SelectionCouleur :: " << c.R() << " : " << c.V() << " : " << c.B() << std::endl;
    }

    void Perspective_GL::defAffichage(int id)
    /* Change le mode d'affichage */
    {
        parametres.type_affichage = id;
        if (aff_developpes) /* Cas particulier, on affiche le modèle des développés, on est en mode 2D. */
        {
            aff_origine = false;
            aff_surfaces = false;
            aff_arretes = false;
            aff_modele = false;
            aff_filaire = false;
            aff_projections = false;
            aff_sommets = false;
            return;
        }

        if (id == MODE_SCENE)  /* Affichage complet (surfaces, filaire et points) */
        {
            aff_origine = true;
            aff_surfaces = true;
            aff_arretes = false;
            aff_modele = true;
            aff_filaire = true;
            aff_projections = true;
            aff_sommets = false;
        }
        else if (id == MODE_SOLIDE) /* Rendu, (surfaces, filaire) */
        {
            aff_origine = false;
            aff_surfaces = true;
            aff_arretes = false;
            aff_modele = true;
//            aff_filaire = !ParametresGL().active_shaders;
            aff_filaire = true;
            aff_projections = false;
            aff_sommets = false;
        }
        else if (id == MODE_ARRETES) /* Uniquement arrêtes */
        {
            aff_origine = false;
            aff_surfaces = true;
            aff_arretes = true;
            aff_modele = false;
#ifdef DEBUG
            aff_filaire = true;
            aff_projections = true;
#else
            aff_filaire = false;
            aff_projections = false;
#endif // DEBUG
            aff_sommets = false;
        }
        else if (id == MODE_FILAIRE) /* Modèle filaire uniquement */
        {
            aff_origine = false;
            aff_surfaces = true;
            aff_arretes = false;
            aff_modele = false;
            aff_filaire = true;
            aff_projections = true;
            aff_sommets = false;
        }
    }

    void Perspective_GL::defSommetRepere(const PGL::PVertex_GL &s, bool init)
    {
        aff_sommet_repere = true;
        if (init)
        {
            sommets_repere[0] = sommets_repere[1] = s;
        }
        else
        {
            sommets_repere[1] = sommets_repere[2];
        }
        sommets_repere[2] = s;
    }

    void Perspective_GL::SupprimeSommetRepere()
    {
        aff_sommet_repere = false;
    }

    void Perspective_GL::AffichageCentreSurface(int id)
    {
        IdSelectionSurfaces = id;
    }

    void Perspective_GL::AffichageCentreSegment(int id)
    {
        IdSelectionSegment = id;
    }

    void Perspective_GL::AffichagePoint(int id)
    {
        IdSelectionPoint = id;
    }

    void Perspective_GL::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)
    {
        parametres.couleur_sommets = couleur_sommets;
        parametres.couleur_segments = couleur_segments;
        parametres.couleur_imprimante = couleur_imprimante;
        parametres.couleur_repere_sommets = couleur_reperes_sommets;
        parametres.couleur_repere_segments = couleur_reperes_segments;
        parametres.couleur_repere_faces = couleur_reperes_faces;
    }

    void Perspective_GL::PrepareScene(const Perspective3D::PScene3D *scene_ptr)
    {
        BloqueAffichage();
#ifdef DEBUG
        PrepareScenePriv(scene_ptr, true, parametres.lissage);
#else
        PrepareScenePriv(scene_ptr, false, parametres.lissage);
#endif // DEBUG
        ActiveAffichage();
    }

    void Perspective_GL::PrepareScenePriv(const Perspective3D::PScene3D *scene_ptr, bool force_couleurs_scene, bool normal_par_sommet)
    {
        VideScene();

        if (!scene_ptr)
        {
            return;
        }

        const Perspective3D::PScene3D &scene = *scene_ptr;

        sommets.ActiveVBO(GL_DYNAMIC_DRAW);
        filaire.ActiveVBO(GL_DYNAMIC_DRAW);
        textes.ActiveVBO(GL_STATIC_DRAW);
        imprimante.ActiveVBO(GL_STATIC_DRAW);
        projections.ActiveVBO(GL_STATIC_DRAW);
        divers.ActiveVBO(GL_STATIC_DRAW);
        triangles_surfaces.ActiveVBO(GL_DYNAMIC_DRAW);
        centre_surfaces.ActiveVBO(GL_STATIC_DRAW);

        scene.Verrouille(); /* On verouille la scène, aucune modification ne pourra y être faite le temps de la copie. */
        Perspective3D::PScene3D scene_copie(scene);
        scene.DeVerrouille();

        /* A partir d'ici on travaille sur une copie, donc aucun soucis avec l'instance de Perspective éventuellement en cours... */

        const Perspective3D::Pmaillage3D &mesh = scene_copie.Maillage();
        const Perspective3D::PStdVectSommets3D &sommets_mesh = mesh.Sommets();

        GLcoord echelle = 1.0;

        Perspective3D::Pvec3 origine_scene(0., 0., 0.);

        if (aff_developpes) /* Cas particulier, Affichage du modèle des développés (patrons, projections spécifiques, etc...) */
        {
            developpes.ActiveVBO(GL_STATIC_DRAW);
            if (scene_copie.Developpe().size())
            {
                echelle = scene_copie.EchelleDeveloppe();
                const puint taille_dev = scene_copie.Developpe().size();
                for (puint i=0; i<taille_dev; ++i)
                {
                    const Perspective3D::Ptriangle3D &s = scene_copie.Developpe()[i];
                    PGL::PVertex_GL v1((s.V1().X()-origine_scene.X()) * echelle, (s.V1().Y()-origine_scene.Y()) * echelle, (s.V1().Z()-origine_scene.Z()) * echelle);
                    PGL::PVertex_GL v2((s.V2().X()-origine_scene.X()) * echelle, (s.V2().Y()-origine_scene.Y()) * echelle, (s.V2().Z()-origine_scene.Z()) * echelle);
                    developpes.Ajout(v1, v2, PGL::PCouleur_GL(s.CouleurConst()));
                }
            }

            developpes.Transfert();
//            std::cout << "Traçage du développé : " << scene.DeveloppeConst().size() << std::endl;
            return;
        }

        const bool affiche_imprimante_3d = parametres.imprimante3d;
//        std::cout << "Etat imprimante 3D : " << affiche_imprimante_3d << std::endl;

        if (affiche_imprimante_3d) // Si on affiche le plateau, on adapte l'echelle d'affichage en fonction du format de l'imprimante et non à partir du modèle.
        {
            origine_scene.defCoords(scene_copie.Centre().X(), (parametres.imprimante3d_z*.5f), scene_copie.Centre().Z());
            echelle = 1. / (MAX(MAX(parametres.plateau_imprimante3d_x, parametres.plateau_imprimante3d_y), parametres.imprimante3d_z));
        }
        else // Echelle et origine d'après le modèle
        {
            origine_scene = scene_copie.Centre();
            echelle = scene_copie.Echelle();
        }

        Origine_scene = origine_scene;
        Echelle_scene = echelle;

        const puint taille_surfaces = mesh.Surfaces().size();

        PGL::PCouleur_GL couleur_surfaces;
        SelectionCouleur(couleur_surfaces);

        const puint alpha_solide = puint(float(parametres.opacite_solide)*2.55f);

        for(puint it=0; it<taille_surfaces; ++it)
        {
            const Perspective3D::PSurface3D &surf = mesh.Surfaces()[it];
//            if (!surf.Actif()) { continue; }

            PGL::PVertex_GL vcs((surf.Centre().X()-origine_scene.X())*echelle, (surf.Centre().Y()-origine_scene.Y())*echelle, (surf.Centre().Z()-origine_scene.Z())*echelle);
            centre_surfaces.Ajout(vcs, parametres.couleur_repere_faces);

            const puint taille_tri = surf.TrianglesConst().size();
            for (puint i=0; i<taille_tri; ++i)
            {
                const Perspective3D::Ptriangle3D &s = surf.TrianglesConst()[i];

                if (s.Actif())
                {
                    PGL::PVertex_GL v1((s.V1().X()-origine_scene.X())*echelle, (s.V1().Y()-origine_scene.Y())*echelle, (s.V1().Z()-origine_scene.Z())*echelle);
                    PGL::PVertex_GL v2((s.V2().X()-origine_scene.X())*echelle, (s.V2().Y()-origine_scene.Y())*echelle, (s.V2().Z()-origine_scene.Z())*echelle);
                    PGL::PVertex_GL v3((s.V3().X()-origine_scene.X())*echelle, (s.V3().Y()-origine_scene.Y())*echelle, (s.V3().Z()-origine_scene.Z())*echelle);
                    const PGL::PVertex_GL norm_s(s.Norm());

//                    const float mul_norm_sommet = 0.5;
//                    PGL::PVertex_GL norm1 = normal_par_sommet ? (PGL::PVertex_GL(sommets_mesh[s.V1().Id()].Norm()*mul_norm_sommet)+norm_s).Normalise() : norm_s;
//                    PGL::PVertex_GL norm2 = normal_par_sommet ? (PGL::PVertex_GL(sommets_mesh[s.V2().Id()].Norm()*mul_norm_sommet)+norm_s).Normalise() : norm_s;
//                    PGL::PVertex_GL norm3 = normal_par_sommet ? (PGL::PVertex_GL(sommets_mesh[s.V3().Id()].Norm()*mul_norm_sommet)+norm_s).Normalise() : norm_s;

                    PGL::PVertex_GL norm1 = normal_par_sommet ? (PGL::PVertex_GL(sommets_mesh[s.V1().Id()].Norm())) : norm_s;
                    PGL::PVertex_GL norm2 = normal_par_sommet ? (PGL::PVertex_GL(sommets_mesh[s.V2().Id()].Norm())) : norm_s;
                    PGL::PVertex_GL norm3 = normal_par_sommet ? (PGL::PVertex_GL(sommets_mesh[s.V3().Id()].Norm())) : norm_s;

#ifdef DEBUG
//                    std::cout << "Perspective_GL::PrepareScenePriv :: Norm Triangle surface : " << norm_s.X() << " : " << norm_s.Y() << " : " << norm_s.Z() << " :: " << surf.Norm().X() << ":" << surf.Norm().Y() << ":" << surf.Norm().Z() << std::endl;
//                    std::cout << "Perspective_GL::PrepareScenePriv :: Couleur Triangle surface : " << ((int) s.CouleurConst().R()) << " : " << ((int) s.CouleurConst().V()) << " : " << ((int) s.CouleurConst().B()) << std::endl;
#endif // DEBUG

                    if (s.CouleurConst().Actif()) /* Couleur active ? */
                    {
                        PGL::PCouleur_GL c(s.CouleurConst(), alpha_solide);
                        c.defAlpha(alpha_solide);
                        triangles_surfaces.Ajout(v1, v2, v3, norm1, norm2, norm3, c);
                    }
                    else
                    {
                        PGL::PCouleur_GL c;
                        PGL::PVertex_GL n(s.Norm());
                        SelectionCouleur(c, n);
                        c.defAlpha(alpha_solide);
                        triangles_surfaces.Ajout(v1, v2, v3, norm1, norm2, norm3, PGL::PCouleur_GL(c));
                    }
                }
                else
                {
#ifdef DEBUG_GL
//                    std::cout << "Perspective_GL::PrepareScenePriv() :: Ignore la surface : " << s.Id() << "..." << std::endl;
#endif // DEBUG_GL
                }
            }
        }

        if (mesh.Filaire().size())
        {
            /* Affichage du modèle filaire: */
            const puint taille_fil = mesh.Filaire().size();
            for (puint i=0; i<taille_fil; ++i)
            {
                const Perspective3D::PSegment3D &s = mesh.Filaire()[i];

                if (s.Actif())
                {
                    PGL::PVertex_GL v1((s.V1().X()-origine_scene.X()) * echelle, (s.V1().Y()-origine_scene.Y()) * echelle, (s.V1().Z()-origine_scene.Z()) * echelle);
                    PGL::PVertex_GL v2((s.V2().X()-origine_scene.X()) * echelle, (s.V2().Y()-origine_scene.Y()) * echelle, (s.V2().Z()-origine_scene.Z()) * echelle);

                    if ((force_couleurs_scene || !mesh.Surfaces().size()) && s.CouleurConst().Actif()) /* Couleur active et pas de surfaces ? */
                    {
                        filaire.Ajout(v1, v2, PGL::PCouleur_GL(s.CouleurConst()));
                    }
                    else
                    {
                        filaire.Ajout(v1, v2, PGL::PCouleur_GL(parametres.couleur_segments));
                    }
                }
            }
        }

        if (scene_copie.Projections().size())
        {
            /* Affichage du modèle des projections: */
            const puint taille_proj = scene_copie.Projections().size();
            for (puint i=0; i<taille_proj; ++i)
            {
                const Perspective3D::PSegment3D &s = scene_copie.Projections()[i];
                PGL::PVertex_GL v1((s.V1().X()-origine_scene.X()) * echelle, (s.V1().Y()-origine_scene.Y()) * echelle, (s.V1().Z()-origine_scene.Z()) * echelle);
                PGL::PVertex_GL v2((s.V2().X()-origine_scene.X()) * echelle, (s.V2().Y()-origine_scene.Y()) * echelle, (s.V2().Z()-origine_scene.Z()) * echelle);
                projections.Ajout(v1, v2, PGL::PCouleur_GL(s.CouleurConst()));
            }
        }

        if (scene_copie.Divers().size())
        {
            /* Affichage du modèle divers: */
            const puint taille_div = scene_copie.Divers().size();

            for (puint i=0; i<taille_div; ++i)
            {
                const Perspective3D::Ptriangle3D &s = scene_copie.Divers()[i];
                PGL::PVertex_GL v1((s.V1().X()-origine_scene.X()) * echelle, (s.V1().Y()-origine_scene.Y()) * echelle, (s.V1().Z()-origine_scene.Z()) * echelle);
                PGL::PVertex_GL v2((s.V2().X()-origine_scene.X()) * echelle, (s.V2().Y()-origine_scene.Y()) * echelle, (s.V2().Z()-origine_scene.Z()) * echelle);
                divers.Ajout(v1, v2, PGL::PCouleur_GL(s.CouleurConst()));
            }
        }

        if (scene_copie.Textes3D().size())
        {
            const puint taille_textes = scene_copie.Textes3D().size();
            for (puint i1=0; i1<taille_textes; ++i1)
            {
                const Perspective3D::PStdVectSegments3D &trs = scene_copie.Textes3D()[i1].Vertices();

                const puint n_trs = trs.size();
                for(puint i2=0; i2<n_trs; ++i2)
                {
                    const Perspective3D::PSegment3D &s = trs[i2];
                    PGL::PVertex_GL v1((s.V1().X()-origine_scene.X()) * echelle, (s.V1().Y()-origine_scene.Y()) * echelle, (s.V1().Z()-origine_scene.Z()) * echelle);
                    PGL::PVertex_GL v2((s.V2().X()-origine_scene.X()) * echelle, (s.V2().Y()-origine_scene.Y()) * echelle, (s.V2().Z()-origine_scene.Z()) * echelle);
                    textes.Ajout(v1, v2, PGL::PCouleur_GL(s.CouleurConst()));
                }
            }
        }

        if (mesh.Sommets().size())
        {
            /* Affichage du modèle des arrêtes: */
            const puint taille_sommets = mesh.Sommets().size();
            for (puint i=0; i<taille_sommets; ++i)
            {
                const Perspective3D::PSommet3D &s = mesh.Sommets()[i];
                if (s.Actif())
                {
                    const PGL::PVertex_GL v((s.V1().X()-origine_scene.X()) * echelle, (s.V1().Y()-origine_scene.Y()) * echelle, (s.V1().Z()-origine_scene.Z()) * echelle);

                    if (force_couleurs_scene && s.CouleurConst().Actif())
                    {
                        sommets.Ajout(v, PGL::PCouleur_GL(s.CouleurConst()));
                    }
                    else
                    {
                        sommets.Ajout(v, parametres.couleur_sommets);
                    }
                }
            }
        }

        if (affiche_imprimante_3d)
        {
            /* Affichage du plateau d'impression (attention, par rapport à OpenGL, la profondeur Z est en Y, la hauteur Y devient la profondeur Z. */
            aff_imprimante = true;
            const puint n_divisions_grille = 20;

            pfloat coord_z_imprimante = origine_scene.Y() * echelle;


            float demi_pl_x = (parametres.plateau_imprimante3d_x * .5f) * echelle;
            float demi_pl_y = (parametres.plateau_imprimante3d_y * .5f) * echelle;

            float diff_grille_x = (parametres.plateau_imprimante3d_x * echelle) / n_divisions_grille;
            float diff_grille_y = (parametres.plateau_imprimante3d_y * echelle) / n_divisions_grille;

            float grille_x = -demi_pl_x;
            float grille_y = -demi_pl_y;

            const PGL::PCouleur_GL &couleur_imprimante = parametres.couleur_imprimante;

            // Grille (X pour l'imprimante):
            for(puint i=0; i<n_divisions_grille+1; ++i)
            {
                imprimante.Ajout(PVertex_GL(grille_x, -coord_z_imprimante, -demi_pl_y), PGL::PVertex_GL(grille_x, -coord_z_imprimante, demi_pl_y), couleur_imprimante);
                grille_x += diff_grille_x;
            }

            // Grille (Y pour l'imprimante):
            for(puint i=0; i<n_divisions_grille+1; ++i)
            {
                imprimante.Ajout(PVertex_GL(-demi_pl_x, -coord_z_imprimante, grille_y), PGL::PVertex_GL(demi_pl_x, -coord_z_imprimante, grille_y), couleur_imprimante);
                grille_y += diff_grille_y;
            }

            // Boite (Z pour l'imprimante):
            // Haut
            imprimante.Ajout(PVertex_GL(-demi_pl_x, coord_z_imprimante, -demi_pl_y), PGL::PVertex_GL(demi_pl_x, coord_z_imprimante, -demi_pl_y), couleur_imprimante);
            imprimante.Ajout(PVertex_GL(demi_pl_x, coord_z_imprimante, -demi_pl_y), PGL::PVertex_GL(demi_pl_x, coord_z_imprimante, demi_pl_y), couleur_imprimante);
            imprimante.Ajout(PVertex_GL(demi_pl_x, coord_z_imprimante, demi_pl_y), PGL::PVertex_GL(-demi_pl_x, coord_z_imprimante, demi_pl_y), couleur_imprimante);
            imprimante.Ajout(PVertex_GL(-demi_pl_x, coord_z_imprimante, demi_pl_y), PGL::PVertex_GL(-demi_pl_x, coord_z_imprimante, -demi_pl_y), couleur_imprimante);

            // Segments verticaux:
            imprimante.Ajout(PVertex_GL(-demi_pl_x, -coord_z_imprimante, -demi_pl_y), PGL::PVertex_GL(-demi_pl_x, coord_z_imprimante, -demi_pl_y), couleur_imprimante);
            imprimante.Ajout(PVertex_GL(demi_pl_x, -coord_z_imprimante, -demi_pl_y), PGL::PVertex_GL(demi_pl_x, coord_z_imprimante, -demi_pl_y), couleur_imprimante);
            imprimante.Ajout(PVertex_GL(demi_pl_x, -coord_z_imprimante, demi_pl_y), PGL::PVertex_GL(demi_pl_x, coord_z_imprimante, demi_pl_y), couleur_imprimante);
            imprimante.Ajout(PVertex_GL(-demi_pl_x, -coord_z_imprimante, demi_pl_y), PGL::PVertex_GL(-demi_pl_x, coord_z_imprimante, demi_pl_y), couleur_imprimante);
        }

        sommets.Transfert();
        filaire.Transfert();
        textes.Transfert();
        imprimante.Transfert();
        projections.Transfert();
        divers.Transfert();
        triangles_surfaces.Transfert();
        centre_surfaces.Transfert();
    }
} // namespace PGL

#endif // PERSPECTIVE_GL
