/*!\file animations.c
 *
 * \brief Votre espace de liberté : c'est ici que vous pouvez ajouter
 * vos fonctions de transition et d'animation avant de les faire
 * référencées dans le tableau _animations du fichier \ref window.c
 *
 * Des squelettes d'animations et de transitions sont fournis pour
 * comprendre le fonctionnement de la bibliothèque. En bonus des
 * exemples dont un fondu en GLSL.
 *
 * \author Farès BELHADJ, amsi@up8.edu
 * \date April 12, 2023
 */

#include <GL4D/gl4dh.h>
#include "audioHelper.h"
#include "animations.h"
#include <assert.h>
#include <stdlib.h>
#include <GL4D/gl4dg.h>
#include <GL4D/gl4df.h>
#include <SDL_image.h>
#include <SDL_ttf.h>

extern void initNoiseTextures(void);
extern void useNoiseTextures(GLuint pid, int shift);
extern void unuseNoiseTextures(int shift);
extern void freeNoiseTextures(void);

static void initText(GLuint * ptId, const char * text, SDL_Color fcolor, int fsize, int tside);
static void bee_draw(GLfloat velocity);
static void grid_draw(GLfloat tx, GLfloat sy, GLfloat tz);

static const int _grid_s = 512;

static GLuint _disk = 0, _sphere = 0, _grid = 0, _beeTex = 0, _gridTex = 0, _colorsTex = 0, _pId[] = { 0, 0, 0 };
static GLfloat _bee[] = {0.0f, 0.1f, 0.0f}, _obee[] = {0.0f, 0.1f, 0.0f};

static GLuint _quad = 0;
static GLuint _texProgId = 0;
static GLuint _texOut = 0, _blurRadius = 0;

void fondu(void (* a0)(int), void (* a1)(int), Uint32 t, Uint32 et, int state) {
  /* INITIALISEZ VOS VARIABLES */
  int vp[4], i;
  GLint tId;
  static GLint vp0[4] = { 0, 0, 1920, 1080 };
  static GLuint tex[2], pId;
  switch(state) {
  case GL4DH_INIT:
    /* INITIALISEZ VOTRE TRANSITION (SES VARIABLES <STATIC>s) */
    glGetIntegerv(GL_VIEWPORT, vp0);
    glGenTextures(2, tex);
    for(i = 0; i < 2; i++) {
      glBindTexture(GL_TEXTURE_2D, tex[i]);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, vp0[2], vp0[3], 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    }
    pId = gl4duCreateProgram("<vs>shaders/basic.vs", "<fs>shaders/mix.fs", NULL);
    return;
  case GL4DH_FREE:
    /* LIBERER LA MEMOIRE UTILISEE PAR LES <STATIC>s */
    if(tex[0]) {
      glDeleteTextures(2, tex);
      tex[0] = tex[1] = 0;
    }
    return;
  case GL4DH_UPDATE_WITH_AUDIO:
    /* METTRE A JOUR LES DEUX ANIMATIONS EN FONCTION DU SON */
    if(a0) a0(state);
    if(a1) a1(state);
    return;
  default: /* GL4DH_DRAW */
    /* RECUPERER L'ID DE LA DERNIERE TEXTURE ATTACHEE AU FRAMEBUFFER */
    glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &tId);
    /* JOUER LES DEUX ANIMATIONS */
    if(a0) {
      glGetIntegerv(GL_VIEWPORT, vp);
      glViewport(0, 0, vp0[2], vp0[3]);
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,  tex[0],  0);
      a0(state);
      glViewport(vp[0], vp[1], vp[2], vp[3]);
    }
    if(a1) {
      glGetIntegerv(GL_VIEWPORT, vp);
      glViewport(0, 0, vp0[2], vp0[3]);
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,  tex[1],  0);
      a1(state);
      glViewport(vp[0], vp[1], vp[2], vp[3]);
    }
    /* MIXER LES DEUX ANIMATIONS */
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,  tId,  0);
    glDisable(GL_DEPTH);
    glUseProgram(pId);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex[0]);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, tex[1]);
    if(et / (GLfloat)t > 1) {
      fprintf(stderr, "%d-%d -- %f\n", et, t, et / (GLfloat)t);
      exit(0);
    }
    glUniform1f(glGetUniformLocation(pId, "dt"), et / (GLfloat)t);
    glUniform1i(glGetUniformLocation(pId, "tex0"), 0);
    glUniform1i(glGetUniformLocation(pId, "tex1"), 1);
    gl4duBindMatrix("pm");
    gl4duPushMatrix();
    gl4duLoadIdentityf();
    gl4duSendMatrices();
    gl4duPopMatrix();
    gl4dgDraw(_quad);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, 0);
    return;
  }
}

void huit(int state) {
  static int perm = 0;
  static GLuint tid = 0;
  static GLfloat a = 0.1f;
  static const float pi_2 = M_PI / 2.0f;
  switch(state) {
  case GL4DH_INIT: {
    SDL_Color c = { 255, 255, 255, 255 };
    initText(&tid, "8", c, 1280, 1280);
    return;
  }
  case GL4DH_FREE: glDeleteTextures(1, &tid); return;
  case GL4DH_UPDATE_WITH_AUDIO: {
    int v = ahPathLength();
    perm = (v > 100) ? (v - 100) / 2 : 0;
    return;
  }
  default:
    {/* GL4DH_DRAW */
      GLboolean dt = glIsEnabled(GL_DEPTH_TEST), bl = glIsEnabled(GL_BLEND);
      GLfloat s = sin(a);
      MON_TEMPS;
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glClear(GL_COLOR_BUFFER_BIT);
      if  (dt)  glDisable(GL_DEPTH_TEST);
      if (!bl)  glEnable (GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glUseProgram(_texProgId);
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, tid);
      glUniform1i(glGetUniformLocation(_texProgId, "tx"), 0);
      glUniform1i(glGetUniformLocation(_texProgId, "colorize"), 1);
      //ICI il faudrait coloriser en rouge vers la fin
      glUniform4f(glGetUniformLocation(_texProgId, "color"), 1.0f, 1.0f, 1.0f, 1.0f);
      glUniform2f(glGetUniformLocation(_texProgId, "pt"), 0.0f, 0.0f);
      glUniform1i(glGetUniformLocation(_texProgId, "beep"), 0);
      gl4duBindMatrix("pm");
      gl4duPushMatrix();
      gl4duScalef(0.5f, 1.0f, 1.0f);
      gl4duScalef(s, s, 1.0f);
      gl4duTranslatef(0.0f, 0.106f, 0.0f);
      gl4duSendMatrices();
      gl4duPopMatrix();
      gl4dgDraw(_quad);
      glUseProgram(0);
      if  (dt)  glEnable (GL_DEPTH_TEST);
      if (!bl)  glDisable(GL_BLEND);
      if(a < pi_2) {
	a += delta * 0.07;
	if(a > pi_2 - 0.00001f)
	  a = pi_2 - 0.00001f;
      }
      /* RECUPERER L'ID DE LA DERNIERE TEXTURE ATTACHEE AU FRAMEBUFFER */
      GLint fbotex;
      glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);
      if(perm) {
	static int ft = 1;
	gl4dfScattering(fbotex, fbotex, perm, 0, 0, GL_FALSE);
	if(ft && duree > 15.0f) {
	  ahSetNeige(1);
	  ft = 0;
	}
      }
      if(duree > 20.0f) {
	gl4dfConvTex2Tex(fbotex, _texOut, GL_FALSE);
	_blurRadius = (GLuint)(128.0f * powf( (pi_2 - a) / pi_2, 0.666f ));
      }
      gl4dfBlur(fbotex, fbotex, (int)(128.0f * powf( (pi_2 - a) / pi_2, 0.666f )), 1, 0, GL_FALSE);
      return;
    }
  }
}

void noir(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default: /* GL4DH_DRAW */
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    return;
  }
}

void rouge(int state) {
  static float bf = 1.0f;
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default:
    {/* GL4DH_DRAW */
      GLboolean dt = glIsEnabled(GL_DEPTH_TEST), bl = glIsEnabled(GL_BLEND);
      MON_TEMPS;
      gl4dfScattering(_texOut, _texOut, 21, 0, 0, GL_FALSE);
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glClear(GL_COLOR_BUFFER_BIT);
      if  (dt)  glDisable(GL_DEPTH_TEST);
      if (!bl)  glEnable (GL_BLEND);
      glUseProgram(_texProgId);
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, _texOut);
      glUniform1i(glGetUniformLocation(_texProgId, "tx"), 0);
      glUniform1i(glGetUniformLocation(_texProgId, "colorize"), 1);
      glUniform4f(glGetUniformLocation(_texProgId, "color"), 1000.0f, 0.0f, 0.0f, 1.0f);
      glUniform2f(glGetUniformLocation(_texProgId, "pt"), 0.0f, 0.0f);
      glUniform1i(glGetUniformLocation(_texProgId, "beep"), 0);
      gl4duBindMatrix("pm");
      gl4duPushMatrix();
      gl4duLoadIdentityf();
      gl4duSendMatrices();
      gl4duPopMatrix();
      gl4dgDraw(_quad);
      glUniform1i(glGetUniformLocation(_texProgId, "colorize"), 0);
      glUseProgram(0);
      if  (dt)  glEnable (GL_DEPTH_TEST);
      if (!bl)  glDisable(GL_BLEND);
      /* RECUPERER L'ID DE LA DERNIERE TEXTURE ATTACHEE AU FRAMEBUFFER */
      GLint fbotex;
      glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);
      gl4dfBlur(fbotex, fbotex, (int)(_blurRadius * bf), 1, 0, GL_FALSE);
      if(bf > 0.0f)
	bf -= delta / 7.5f;
      bf = bf < 0.0f ? 0.0f : bf;
      return;
    }
  }
}

void vert(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default: /* GL4DH_DRAW */
    ahSetStep(1);
    glClearColor(0.0f, 0.35f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    return;
  }
}

static int _beep_number = 0;

void beep(int state) {
  static float height = 0.0f;
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: {
    height = ahAverage() / 300.0f;
    return;
  }
  default:
    {
      /* GL4DH_DRAW */
      static int ft = 1;
      GLint fbotex = 0;
      GLboolean dt = glIsEnabled(GL_DEPTH_TEST), bl = glIsEnabled(GL_BLEND);
      if(ft) {
	ahSetStep(2);
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);
	glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);
	gl4dfConvTex2Tex(fbotex, _texOut, GL_FALSE);
	ft = 0;
      }
      MON_TEMPS;
      static float px = 0.0f, py = 0.5f;
      float x = duree / 2.2f, y = 0.5f + height / 2.0f;
      if(_beep_number == 1)
	y = 0.5f;
      while(x > 1.0f) x-= 1.0f;
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glClear(GL_COLOR_BUFFER_BIT);
      if  (dt)  glDisable(GL_DEPTH_TEST);
      if (!bl)  glEnable (GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glUseProgram(_texProgId);
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, _texOut);
      glUniform1i(glGetUniformLocation(_texProgId, "tx"), 0);
      glUniform1i(glGetUniformLocation(_texProgId, "colorize"), 1);
      glUniform1i(glGetUniformLocation(_texProgId, "beep"), 1);
      glUniform4f(glGetUniformLocation(_texProgId, "color"), 0.2f, 1.0f, 0.2f, 1.0f);
      gl4duBindMatrix("pm");
      gl4duPushMatrix();
      gl4duLoadIdentityf();
      gl4duSendMatrices();
      gl4duPopMatrix();

      glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);

      const int imax = 20;
      for(int i = 0; i <= imax; ++i) {
	float p = i / (float)imax;
	float ix = (1.0f - p) * px + p * x;
	float iy = (1.0f - p) * py + p * y;
	glUniform2f(glGetUniformLocation(_texProgId, "pt"), ix, iy);
	gl4dgDraw(_quad);
	gl4dfBlur(fbotex, _texOut, 4, 2, 0, GL_FALSE);
      }
      glUniform1i(glGetUniformLocation(_texProgId, "colorize"), 0);
      glUniform1i(glGetUniformLocation(_texProgId, "beep"), 0);
      glUseProgram(0);
      if  (dt)  glEnable (GL_DEPTH_TEST);
      if (!bl)  glDisable(GL_BLEND);
      px = x; py = y;
      return;
    }
  }
}

void beep2(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default: /* GL4DH_DRAW */
    _beep_number = 1;
    ahSetStep(3);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    beep(state);
    return;
  }
}

static float sig(float x) { return 1.0 / (1.0 + exp(-6.0 * x)); }

static void fond(double t) {
  t = t < 0.0 ? 0.0 : t;
  glUseProgram(_pId[2]);
  glUniform1i(glGetUniformLocation(_pId[2], "tex1"), 1);
  glUniform2f(glGetUniformLocation(_pId[2], "tt"), 1.0f, 1.0f);
  glUniform1f(glGetUniformLocation(_pId[2], "ts"), 1.0f);
  glUniform1f(glGetUniformLocation(_pId[2], "temps"), t);
  glActiveTexture(GL_TEXTURE1);
  glBindTexture(GL_TEXTURE_1D, _colorsTex);
  gl4duBindMatrix("model");
  gl4duLoadIdentityf();
  gl4duPushMatrix(); {
    gl4duTranslatef(0.0f, 0.0f, -990.0f + _bee[2]);
    gl4duScalef(999.0f, 999.0f, 1.0f);
    gl4duSendMatrices();
  } gl4duPopMatrix();
  gl4dgDraw(_quad);
  glBindTexture(GL_TEXTURE_1D, 0);
  glActiveTexture(GL_TEXTURE0);
  glUniform1i(glGetUniformLocation(_pId[2], "tex1"), 0);

  //SCAT
  GLint fbotex;
  glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
					GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);
  gl4dfScattering(fbotex, fbotex, 7, 3, 0, GL_FALSE);
  gl4dfMedian(fbotex, fbotex, 1, GL_FALSE);
  gl4dfBlur(fbotex, fbotex, 21, 1, 0, GL_FALSE);
}

static float etale(float t0, float t, float et, float n) {
  float i = (t - t0) / et;
  if(i > 1.0f) i = 1.0f;
  return powf(i, n);
}

void beebegin(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default:
    {
      GLboolean dt = glIsEnabled(GL_DEPTH_TEST), bl = glIsEnabled(GL_BLEND);
      MON_TEMPS;
      /* GL4DH_DRAW */
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glEnable(GL_DEPTH_TEST);
      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      gl4duBindMatrix("view");
      gl4duLoadIdentityf();
      GLint vp[4];
      glGetIntegerv(GL_VIEWPORT, vp);
      static float vbat = 0.0f;
      static const float d_e0 = 2.5f, d_e1 = 3.33f, d_e2 = 3.33f;
      static const float d_e3 = 3.33f, d_e4 = 3.0f, d_e5 = 2.0f, e1_alt = 4.0f;
      static const float rcam0[] = { 0.0f, 0.5f, 2.0f, 0.0f, 1.0f, 0.0f };
      static float rcam[] = { 0.0f, e1_alt, 0.0f, -10.0f, 0.0f, -1.0f };
      static const float v0 = 0.26652f;
      if(rcam[3] == -10.0f) {
	rcam[1] = v0 - _bee[1];
	rcam[2] = -v0;
	rcam[3] = -sqrt(2.0f) * vp[3] / (GLfloat)vp[2];
	rcam[5] = -sqrt(2.0f);
      }
      if(duree < d_e0 + d_e1) { /* étape 0 et 1 */
	if(duree > d_e0) {
	  float iv = etale(d_e0, duree, d_e1, 1.3f), siv = sin(iv * M_PI / 2.0f), siiv = sin((1.0f - iv) * M_PI / 2.0f);
	  rcam[1] = v0 - _bee[1] + (e1_alt - v0) * pow(siv, 3.0f);
	  rcam[2] = -v0 * pow(siiv, 2.0f);
	}
	gl4duLookAtf(rcam[0] + _bee[0], rcam[1] + _bee[1], rcam[2] + _bee[2], _bee[0], _bee[1], rcam[2] + _bee[2], rcam[3], rcam[4], rcam[5]);
      } else { /* étape 2 et plus */
	if(duree < d_e0 + d_e1 + d_e2) { /* seulement étape 2 */
	  float e = etale(d_e0 + d_e1, duree, d_e2, 2.0f);
	  vbat = M_PI / 3.0f * (0.1f * e + 0.9f * fabs(sin((5.0f * M_PI / 2.0f) * e)));
	} else {
	  vbat = (rand() / (RAND_MAX + 1.0f)) / 20.0f + M_PI / 3.0f;
	  if(duree < d_e0 + d_e1 + d_e2 + d_e3) { /* seulement étape 3 */
	    float e = etale(d_e0 + d_e1 + d_e2, duree, d_e3, 2.0f);
	    for(int i = 0; i < 6; ++i)
	      rcam[i] = e * rcam0[i] + (1.0f - e) * rcam[i];
	  } else { /* étape 4 et plus */
	    static float ve4 = 0.0f;
	    _bee[2] -= ve4 * delta; //5.1f * delta;
	    ve4 += 1.5f * delta;
	    //frottements
	    ve4 -= 0.8f * ve4 * delta * ve4 * delta;
	    /* printf("ve4%f ", ve4); */
	    for(int i = 0; i < 3; ++i) {
	      float obj = _bee[i] - rcam0[i];
	      rcam[2] += (obj - (_bee[i] - rcam[i])) / 1.5f;
	    }
	  }
	}
	gl4duLookAtf(rcam[0] + _bee[0], rcam[1] + _bee[1], rcam[2] + _bee[2], _bee[0], _bee[1], _bee[2], rcam[3], rcam[4], rcam[5]);
      }

      GLfloat Lp[] = {
	0.0f, 40.0f, 3.0f, 1.0f,
	16.5 * cos(7.0 * duree) + rcam[0] + _bee[0],           2.0f,
	-15.0f + 20.9 * sin(8.0 * duree) + rcam[2] + _bee[2],  1.0f,
	1.0f + 9.5 * cos(9.0 * duree) + rcam[0] + _bee[0],     1.5f,
	2.0f + 20.9 * sin(-12.0 * duree) + rcam[2] + _bee[2],  1.0f,
	-4.0f + 9.5 * cos(3.0 * duree) + rcam[0] + _bee[0],    1.4f,
	10.0f + 20.9 * sin(6.0 * duree) + rcam[2] + _bee[2],   1.0f
      };
      GLfloat Lc[] = {
	0.8f, 0.8f, 0.4f, 1.0f,
	1.0f, 1.0f, 1.0f, 1.0f,
	1.0f, 0.1f, 0.1f, 1.0f,
	0.1f, 1.0f, 0.1f, 1.0f,
      };
      float pondf = MAX(duree - (d_e0 + d_e1 + d_e2 + d_e3 + d_e4), 0.0f);
      pondf /= d_e5;
      int nbL = sizeof Lc / (4 * sizeof *Lc);
      for(int i = 1; i < nbL; ++i) {
	Lc[i * 4 + 0] *= pondf;
	Lc[i * 4 + 1] *= pondf;
	Lc[i * 4 + 2] *= pondf;
      }
      glUseProgram(_pId[0]);
      glUniform1i(glGetUniformLocation(_pId[0], "nbL"), nbL);
      glUniform4fv(glGetUniformLocation(_pId[0], "Lp"), nbL, Lp);
      glUniform4fv(glGetUniformLocation(_pId[0], "Lc"), nbL, Lc);
      glUseProgram(_pId[1]);
      glUniform1i(glGetUniformLocation(_pId[1], "nbL"), nbL);
      glUniform4fv(glGetUniformLocation(_pId[1], "Lp"), nbL, Lp);
      glUniform4fv(glGetUniformLocation(_pId[1], "Lc"), nbL, Lc);
      fond(pondf - d_e5);
      grid_draw(rcam[0] + _bee[0], 0.0f, rcam[2] + _bee[2]);
      bee_draw(vbat);
      /* printf("rc0%f rc1%f rc2%f vbat%f pondf%f pondf2f%f\n", rcam[0], rcam[1], rcam[2], */
      /* 	     vbat, pondf, pondf - d_e5); */
      glUseProgram(0);
      if (!dt) glDisable(GL_DEPTH_TEST);
      if (!bl) glDisable(GL_BLEND);
      return;
    }
  }
}

static void subbeeX(float d[4]) {
  GLboolean dt = glIsEnabled(GL_DEPTH_TEST), bl = glIsEnabled(GL_BLEND);
  MON_TEMPS;
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  gl4duBindMatrix("view");
  gl4duLoadIdentityf();
  static const float vbat = 1.085f, ve4 = 4.95f;
  float rcam[] = { 0.0f + d[0], 0.5f + d[1], 2.0f + d[2] };
  _bee[2] -= ve4 * delta;
  gl4duLookAtf(rcam[0] + _bee[0], rcam[1] + _bee[1], rcam[2] + _bee[2],
	       _bee[0], _bee[1], _bee[2],
	       0.0f, 1.0f, 0.0f);
  
  GLfloat Lp[] = {
    0.0f, 40.0f, 3.0f, 1.0f,

    16.5 * cos(7.0 * duree) + rcam[0] + _bee[0], 1.5f + rcam[1],
    -15.0f + 20.9 * sin(8.0 * duree) + rcam[2] + _bee[2],  1.0f,
    
    1.0f + 9.5 * cos(9.0 * duree) + rcam[0] + _bee[0], 1.0f + 3.0f * rcam[1],
    2.0f + 20.9 * sin(-12.0 * duree) + rcam[2] + _bee[2],  1.0f,

    -4.0f + 9.5 * cos(3.0 * duree) + rcam[0] + _bee[0], 0.9f + 2.0f * rcam[1],
    10.0f + 20.9 * sin(6.0 * duree) + rcam[2] + _bee[2],   1.0f
  };
  GLfloat Lc[] = {
    0.8f, 0.8f, 0.4f, 1.0f,
    1.0f, 1.0f, 1.0f, 1.0f,
    1.0f, 0.1f, 0.1f, 1.0f,
    0.1f, 1.0f, 0.1f, 1.0f,
  };
  float pf1 = 3.633341f + duree, pf2 = 1.633341f + duree; 
  int nbL = sizeof Lc / (4 * sizeof *Lc);
  for(int i = 1; i < nbL; ++i) {
    Lc[i * 4 + 0] *= pf1;
    Lc[i * 4 + 1] *= pf1;
    Lc[i * 4 + 2] *= pf1;
  }
  glUseProgram(_pId[0]);
  glUniform1i(glGetUniformLocation(_pId[0], "nbL"), nbL);
  glUniform4fv(glGetUniformLocation(_pId[0], "Lp"), nbL, Lp);
  glUniform4fv(glGetUniformLocation(_pId[0], "Lc"), nbL, Lc);
  glUseProgram(_pId[1]);
  glUniform1i(glGetUniformLocation(_pId[1], "nbL"), nbL);
  glUniform4fv(glGetUniformLocation(_pId[1], "Lp"), nbL, Lp);
  glUniform4fv(glGetUniformLocation(_pId[1], "Lc"), nbL, Lc);
  fond(pf2);
  grid_draw(rcam[0] + _bee[0], d[3], rcam[2] + _bee[2]);
  bee_draw(vbat);
  glUseProgram(0);
  if (!dt) glDisable(GL_DEPTH_TEST);
  if (!bl) glDisable(GL_BLEND);
}

static void subbeeY(float d[4]) {
  GLboolean dt = glIsEnabled(GL_DEPTH_TEST), bl = glIsEnabled(GL_BLEND);
  MON_TEMPS;
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  gl4duBindMatrix("view");
  gl4duLoadIdentityf();
  static const float vbat = 1.085f, ve4 = 4.95f;
  float rcam[] = { 0.0f + d[0], 0.5f + d[1], 2.0f + d[2] };
  _bee[2] -= ve4 * delta;
  gl4duLookAtf(rcam[0] + _bee[0], rcam[1] + _bee[1], rcam[2] + _bee[2],
	       _bee[0], _bee[1], _bee[2],
	       0.0f, 1.0f, 0.0f);
  
  GLfloat Lp[] = {
    0.0f, 4000.0f, 3.0f, 1.0f,

    16.5 * cos(7.0 * duree) + rcam[0] + _bee[0], 1.5f + rcam[1],
    -15.0f + 20.9 * sin(8.0 * duree) + rcam[2] + _bee[2],  1.0f,
    
    1.0f + 9.5 * cos(9.0 * duree) + rcam[0] + _bee[0], 1.0f + 3.0f * rcam[1],
    2.0f + 20.9 * sin(-12.0 * duree) + rcam[2] + _bee[2],  1.0f,

    -4.0f + 9.5 * cos(3.0 * duree) + rcam[0] + _bee[0], 0.9f + 2.0f * rcam[1],
    10.0f + 20.9 * sin(6.0 * duree) + rcam[2] + _bee[2],   1.0f
  };
  GLfloat Lc[] = {
    0.2f, 0.2f, 0.2f, 1.0f,
    0.5f, 0.5f, 2.0f, 1.0f,
    2.0f, 0.8f, 0.8f, 1.0f,
    0.3f, 0.3f, 0.8f, 1.0f,
  };
  float pf1 = 3.633341f + duree, pf2 = 1.633341f + duree; 
  int nbL = sizeof Lc / (4 * sizeof *Lc);
  for(int i = 1; i < nbL; ++i) {
    Lc[i * 4 + 0] *= pf1;
    Lc[i * 4 + 1] *= pf1;
    Lc[i * 4 + 2] *= pf1;
  }
  glUseProgram(_pId[0]);
  glUniform1i(glGetUniformLocation(_pId[0], "nbL"), nbL);
  glUniform4fv(glGetUniformLocation(_pId[0], "Lp"), nbL, Lp);
  glUniform4fv(glGetUniformLocation(_pId[0], "Lc"), nbL, Lc);
  glUseProgram(_pId[1]);
  glUniform1i(glGetUniformLocation(_pId[1], "nbL"), nbL);
  glUniform4fv(glGetUniformLocation(_pId[1], "Lp"), nbL, Lp);
  glUniform4fv(glGetUniformLocation(_pId[1], "Lc"), nbL, Lc);
  fond(pf2);
  grid_draw(rcam[0] + _bee[0], d[3], rcam[2] + _bee[2]);
  bee_draw(vbat);
  glUseProgram(0);
  if (!dt) glDisable(GL_DEPTH_TEST);
  if (!bl) glDisable(GL_BLEND);
}

void bee2(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default:
    {
      static float d[4];
      subbeeX(d);
      //EFFECT
      GLint fbotex;
      glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
					    GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);
      gl4dfBlur(fbotex, fbotex, 3, 1, 0, GL_FALSE);
      gl4dfSobelSetMixMode(GL4DF_SOBEL_MIX_NONE);
      gl4dfSobelSetResultMode(GL4DF_SOBEL_RESULT_INV_RGB);
      gl4dfSobel(fbotex, fbotex, GL_FALSE);
      return;
    }
  }
}

void bee3(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default:
    {
      static float d[4];
      subbeeX(d);
      //EFFECT
      GLint fbotex;
      glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
					    GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);
      gl4dfBlur(fbotex, fbotex, 3, 1, 0, GL_FALSE);
      gl4dfSobelSetMixMode(GL4DF_SOBEL_MIX_NONE);
      gl4dfSobelSetResultMode(GL4DF_SOBEL_RESULT_RGB);
      gl4dfSobel(fbotex, fbotex, GL_FALSE);
      return;
    }
  }
}

void bee4(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default:
    {
      MON_TEMPS;
      static int ft = 1;
      static GLfloat b[3];
      if(ft) {
	ft = 0;
	memcpy(b, _bee, sizeof b);
      }
      float sa = 2.0f * sig(duree - 1.0f);
      float sb = 2.5f * sig((duree - 1.9f) / 1.9f);
      float sc = 3.5f * sig((duree - 3.0f) / 3.0f);
      _bee[1] = b[1] + sb;
      _bee[2] = b[2] - (sb + sc);
      float d[4] = { 0.0f, -sb + sc, sa, 0.0f };
      if(duree > 6.0)
	d[3] = MIN(pow(duree - 6.0f, 0.9f), 2.0f);;
      subbeeX(d);
      b[2] += _bee[2] - (b[2] - (sb + sc));
      //EFFECT
      GLint fbotex;
      glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
					    GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);
      //gl4dfBlur(fbotex, fbotex, 3, 1, 0, GL_FALSE);
      /* if(duree > 6.0) { */
      /* 	static int tour = 0; */
      /* 	if(tour % 1 == 0) { */
      /* 	  gl4dfSobelSetResultMode(GL4DF_SOBEL_RESULT_INV_LUMINANCE); */
      /* 	  gl4dfSobelSetMixFactor(0.9); */
      /* 	  gl4dfSobelSetMixMode(GL4DF_SOBEL_MIX_ADD); */
      /* 	} else { */
      /* 	  gl4dfSobelSetResultMode(GL4DF_SOBEL_RESULT_INV_LUMINANCE); */
      /* 	  gl4dfSobelSetMixMode(GL4DF_SOBEL_MIX_NONE); */
      /* 	} */
      /* 	++tour; */
      /* } else { */
      /* 	gl4dfSobelSetResultMode(GL4DF_SOBEL_RESULT_INV_LUMINANCE); */
      /* } */
      gl4dfSobelSetResultMode(GL4DF_SOBEL_RESULT_INV_LUMINANCE);
      gl4dfSobelSetMixFactor(0.9);
      gl4dfSobelSetMixMode(GL4DF_SOBEL_MIX_ADD);
      gl4dfSobel(fbotex, fbotex, GL_FALSE);
      return;
    }
  }
}

void bee5(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default:
    {
      MON_TEMPS;
      float d[4] = { 0.0f, 1.0f, 2.0f, 2.0011f };
      subbeeY(d);
      return;
    }
  }
}

void bee6(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default:
    {
      MON_TEMPS;
      float d[4] = { 0.0f, 1.0f, 2.0f, 2.0011f };
      subbeeY(d);
      GLint fbotex;
      glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);
      gl4dfScattering(fbotex, fbotex, 13, 0, 0, GL_FALSE);
      gl4dfBlur(fbotex, fbotex, 10 + 2.0 * duree, 1, 0, GL_FALSE);
      return;
    }
  }
}

void bee7(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default:
    {
      MON_TEMPS;
      float d[4] = { 0.0f, 1.0f, 2.0f, 2.0011f };
      subbeeY(d);
      GLint fbotex;
      glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &fbotex);
      gl4dfScattering(fbotex, fbotex, 13, 0, 0, GL_FALSE);
      //gl4dfMedian(fbotex, fbotex, 2, GL_FALSE);
      gl4dfBlur(fbotex, fbotex, 10 + 4.0 * duree, 1, 0, GL_FALSE);
      return;
    }
  }
}

void bleu(int state) {
  switch(state) {
  case GL4DH_INIT: return;
  case GL4DH_FREE: return;
  case GL4DH_UPDATE_WITH_AUDIO: return;
  default: /* GL4DH_DRAW */
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    return;
  }
}

static void _quit(void) {
  if(_texOut) {
    glDeleteTextures(1, &_texOut);
    _texOut = 0;
  }
  if(_beeTex) {
    glDeleteTextures(1, &_beeTex);
    _beeTex = 0;
  }
  if(_gridTex) {
    glDeleteTextures(1, &_gridTex);
    _gridTex = 0;
  }
  if(_colorsTex) {
    glDeleteTextures(1, &_colorsTex);
    _colorsTex = 0;
  }
  initNoiseTextures();
}

void animationsInit(void) {
  GLint vp[4];
  glGetIntegerv(GL_VIEWPORT, vp);
  gl4duGenMatrix(GL_FLOAT, "pm");
  gl4duBindMatrix("pm");
  gl4duLoadIdentityf();
  gl4duOrthof(-1.0f, 1.0f, -vp[3] / (GLfloat)vp[2], vp[3] / (GLfloat)vp[2], -1.0f, 10.0f);
  if(!_quad)
    _quad = gl4dgGenQuadf();
  if(!_texProgId)
    _texProgId = gl4duCreateProgram("<vs>shaders/basic.vs", "<fs>shaders/tex.fs", NULL);
  if(!_texOut) {
    glGenTextures(1, &_texOut);
    glBindTexture(GL_TEXTURE_2D, _texOut);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, vp[2], vp[3], 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glBindTexture(GL_TEXTURE_2D, 0);
  }

  _pId[0] = gl4duCreateProgram("<vs>shaders/basic3d.vs", "<fs>shaders/perlin.fs", NULL);
  _pId[1] = gl4duCreateProgram("<vs>shaders/perlin.vs", "<fs>shaders/perlin.fs", NULL);
  _pId[2] = gl4duCreateProgram("<vs>shaders/basic3d.vs", "<fs>shaders/fond.fs", NULL);
  gl4duGenMatrix(GL_FLOAT, "model");
  gl4duGenMatrix(GL_FLOAT, "view");
  gl4duGenMatrix(GL_FLOAT, "proj");
  gl4duBindMatrix("proj");
  gl4duLoadIdentityf();
  gl4duFrustumf(-0.005f, 0.005f, -0.005f * vp[3] / (GLfloat)vp[2], 0.005f * vp[3] / (GLfloat)vp[2], 0.01f, 1000.0f);
  _disk = gl4dgGenDiskf(20);
  _sphere = gl4dgGenSpheref(20, 10);
  _grid = gl4dgGenGrid2df(_grid_s, _grid_s);
  /* texture de la bee */
  glGenTextures(1, &_beeTex);
  glBindTexture(GL_TEXTURE_2D, _beeTex);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  static const GLubyte rayure[] = {
      0, 0, 0, 255,
    255, 0, 0, 255,
    255, 0, 0, 255
  };
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, rayure);
  glBindTexture(GL_TEXTURE_2D, 0);

  /* texture A-E-Ciel */
  GLuint aec[] = { RGB(255,0,0), RGB(255,255,0), RGB(0,255,0), RGB(0,255,255), RGB(0,0,255), RGB(255,0,255) };
  glGenTextures(1, &_colorsTex);
  glBindTexture(GL_TEXTURE_1D, _colorsTex);
  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, aec);
  glBindTexture(GL_TEXTURE_1D, 0);

  /* texture de la grille */
  glGenTextures(1, &_gridTex);
  glBindTexture(GL_TEXTURE_2D, _gridTex);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
#ifdef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
  GLfloat max;
  glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max);
#endif
  static GLubyte dalle[4 * 128 * 128];
  for(int i = 0; i < 128 * 128; ++i) {
    if(i / 128 < 5 || (i % 128) < 5) {
      dalle[4 * i + 0] = dalle[4 * i + 1] = dalle[4 * i + 2] = 0;
      dalle[4 * i + 3] = 255;
    } else {
      dalle[4 * i + 0] = dalle[4 * i + 1] = dalle[4 * i + 2] = 255;
      dalle[4 * i + 3] = 55;
    }
  }
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, dalle);
  glBindTexture(GL_TEXTURE_2D, 0);
  initNoiseTextures();
  atexit(_quit);
}

void initText(GLuint * ptId, const char * text, SDL_Color fcolor, int fsize, int tside) {
  static int firstTime = 1;
  SDL_Surface * d, * s;
  TTF_Font * font = NULL;
  if(firstTime) {
    if(TTF_Init() == -1) {
      fprintf(stderr, "TTF_Init: %s\n", TTF_GetError());
      exit(2);
    }
    firstTime = 0;
  }
  if(*ptId == 0) {
    glGenTextures(1, ptId);
    glBindTexture(GL_TEXTURE_2D, *ptId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  }
  if( !(font = TTF_OpenFont("/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf", fsize)) &&
      !(font = TTF_OpenFont("/Applications/LibreOffice.app/Contents/Resources/fonts/truetype/LiberationMono-Regular.ttf", fsize)) ) {
    fprintf(stderr, "TTF_OpenFont: %s\n", TTF_GetError());
    return;
  }
  d = TTF_RenderUTF8_Blended_Wrapped(font, text, fcolor, tside);
  if(d == NULL) {
    TTF_CloseFont(font);
    fprintf(stderr, "Erreur lors du TTF_RenderText\n");
    return;
  }
  s = SDL_CreateRGBSurface(0, d->w, d->h, 32, R_MASK, G_MASK, B_MASK, A_MASK);
  assert(s);
  SDL_BlitSurface(d, NULL, s, NULL);
  SDL_FreeSurface(d);
  glBindTexture(GL_TEXTURE_2D, *ptId);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s->w, s->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, s->pixels);
  SDL_FreeSurface(s);
  TTF_CloseFont(font);
  glBindTexture(GL_TEXTURE_2D, 0);
}

static void vers(GLfloat *a, GLfloat b, GLfloat f) {
  GLfloat d = *a - b, s = SIGN(d);
  d = d * s;
  if(d > 1.0)
    *a -= s * d / f;
  else if(d > 0.01)
    *a -= s * d * d / f;
}

void bee_draw(GLfloat velocity) {
  int i;
  GLfloat dx, dz;
  static GLfloat theta = 1001.0f;
  static const GLfloat white[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
  static const GLfloat black[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
  glUseProgram(_pId[0]);
  glUniform1i(glGetUniformLocation(_pId[0], "corps"), 2);
  glUniform1i(glGetUniformLocation(_pId[0], "hasTex"), 1);
  glUniform1i(glGetUniformLocation(_pId[0], "myTex"), 0);
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, _beeTex);
  gl4duBindMatrix("model");
  gl4duLoadIdentityf();
  gl4duPushMatrix(); {
    gl4duTranslatef(_bee[0], _bee[1], _bee[2]);
    /* dz = _bee[2] - _obee[2]; */
    /* dx = _bee[0] - _obee[0]; */
    /* if(dz != 0.0f) dz = atan2(dz, dx); */
    /* else dz = dx <= 0.0f ? M_PI : 0.0f; */
    /* if(theta > 1000.0f) theta = dz; */
    /* else { */
    /*   GLfloat co = cos(theta), so = sin(theta), cc = cos(dz), sc = sin(dz); */
    /*   vers(&co, cc, 10.0f); */
    /*   vers(&so, sc, 10.0f); */
    /*   if(so != 0.0f) theta = atan2(so, co); */
    /*   else theta = co <= 0.0f ? M_PI : 0.0f; */
    /* } */
    /* gl4duRotatef(theta * 180.0f / M_PI, 0, 1, 0); */
    gl4duRotatef(180.0f, 0, 1, 0);
    _obee[0] = _bee[0];
    _obee[2] = _bee[2];
    gl4duPushMatrix(); {
      gl4duScalef(0.12, 0.12, 0.4);
      gl4duSendMatrices();
    } gl4duPopMatrix();
    glUniform4fv(glGetUniformLocation(_pId[0], "couleur"), 1, white);
    gl4dgDraw(_sphere);
    gl4duPushMatrix(); {
      gl4duTranslatef(0, 0.05f, 0.48f);
      gl4duScalef(0.1, 0.1, 0.1);
      gl4duSendMatrices();
    } gl4duPopMatrix();
    glUniform4fv(glGetUniformLocation(_pId[0], "couleur"), 1, black);
    gl4dgDraw(_sphere);
    for(i = -1; i < 2; i += 2) {
      gl4duPushMatrix(); {
	static float ang = 0.0f;
	ang += velocity;
	gl4duRotatef(i * 30 * sin(ang), 0, 0, 1);
	gl4duTranslatef(-0.3f * i, 0.08f, 0);
	gl4duRotatef(-10.0f, 1, 0, 0);
	gl4duScalef(0.3f, 0.2f, 0.1f);
	gl4duSendMatrices();
      } gl4duPopMatrix();
      glDisable(GL_CULL_FACE);
      glUniform4f(glGetUniformLocation(_pId[0], "couleur"), black[0], black[1], black[2], 0.5f);
      gl4dgDraw(_disk);
      glEnable(GL_CULL_FACE);
    }
  } gl4duPopMatrix();
  glUniform1i(glGetUniformLocation(_pId[0], "corps"), 0);
  glUniform1i(glGetUniformLocation(_pId[0], "hasTex"), 0);
}

void grid_draw(GLfloat tx, GLfloat sy, GLfloat tz) {
  GLboolean bln = glIsEnabled(GL_BLEND);
  //if(!bln)
  glEnable(GL_BLEND);
  glUseProgram(_pId[1]);
  gl4duBindMatrix("model");
  gl4duLoadIdentityf();
  if(sy > 2.001f) {
    glUniform4f(glGetUniformLocation(_pId[1], "couleur"), 0.14f, 0.1f, 0.05f, 1.0f);
    glUniform1i(glGetUniformLocation(_pId[1], "hasTex"), 0);
  } else {
    glUniform4f(glGetUniformLocation(_pId[1], "couleur"), 0.1f, 0.1f, 0.5f, 1.0f);
    glUniform1i(glGetUniformLocation(_pId[1], "hasTex"), 1);
  }
  glUniform2f(glGetUniformLocation(_pId[1], "offset"), 1.0f / (_grid_s - 1.0f), 1.0f / (_grid_s - 1.0f));
  glUniform1i(glGetUniformLocation(_pId[1], "tex"), 0);
  glUniform1i(glGetUniformLocation(_pId[1], "sol"), 1);
  glUniform2f(glGetUniformLocation(_pId[1], "tt"), tx, tz);
  glUniform1f(glGetUniformLocation(_pId[1], "ts"), _grid_s >> 3);
  glUniform1f(glGetUniformLocation(_pId[1], "sy"), sy);
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, _gridTex);
  gl4duPushMatrix(); {
    gl4duTranslatef(tx, 0.0f, tz);
    gl4duScalef(_grid_s >> 3, 1.0f, _grid_s >> 3);
    gl4duTranslatef(0.0f, -1.0f, 0.0f);
    gl4duSendMatrices();
  } gl4duPopMatrix();
  useNoiseTextures(_pId[1], 1);
  gl4dgDraw(_grid);
  unuseNoiseTextures(1);
  glUniform2f(glGetUniformLocation(_pId[1], "tt"), 0.0f, 0.0f);
  glUniform1f(glGetUniformLocation(_pId[1], "ts"), 1.0f);
  glUniform1i(glGetUniformLocation(_pId[1], "hasTex"), 0);
  glUniform1i(glGetUniformLocation(_pId[1], "sol"), 0);
  glUniform1f(glGetUniformLocation(_pId[1], "sy"), 0.0f);
  if(!bln)
    glDisable(GL_BLEND);
}

