/* melite8b.c - visualizza un pianeta e una navetta in 3D
 *  usando le primitive viste, muove la telecamera usando camera.cpp
 *  aggiornando di conseguenza il radar.
 *  E' presente un sole che fornisce la luce per illuminare il pianeta
 *  mentre il satellite e' ottenuto col metodo NPR.
 *  -- Aggiunto texture mapping sul pianeta.
 * 
 *  Autore: Andrea Fusiello, Andrea Colombari 2004
 *
 *  arrow keys      - muovono la telecamera come un pilota muove un aereo
 *  SPACE key       - accellera
 *  - key           - decellera
 *  Escape Key      - esce dal programma
 */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

/* segue inclusione di una piccola libreria con alcune definizioni di tipo e utils */
#include <eliteutils.h>
/* segue file .h dove si definisce la forma e le proprieta' del satellite */
#include <coriolis.h> 
/* segue inclusione libreria gestione telecamera in C++ */
#include <camera.h>

/* NEW: contiene libreria per leggere texture da file */
#include "sgi.h"

/* Function Prototypes */

GLvoid initgfx( GLvoid );
GLvoid keyboard( GLubyte, GLint, GLint );
GLvoid specialkeys( GLint, GLint, GLint );
GLvoid drawScene( GLvoid );
GLvoid drawControlPanel();
void checkError( char * );
void printHelp( char * );
GLvoid reshape ( GLsizei, GLsizei );
GLvoid animate( GLvoid );
GLvoid visibility( GLint );

/* NEW: funzione per il caricamento della texture da porre sul pianeta */
GLvoid initTexture(GLuint textureArray[], char *imageFileName, int textureID);


/* Global Definitions */
#define MAX_TEXTURES   10      /* NEW: numero massimo di texture da caricare */

#define KEY_ESC	27	/* ascii value for the escape key */
#define CAM_SPEED_STEP   0.00005f /* step di incremento velocita' */  
#define CAM_ROT_STEP     1.0f    /* step di incremento angolo */
#define CAM_POS_STEP     0.005f   /* step di incremento posizione */

/* telecamera */
Camera g_camera(-1, -1, -5);
GLsizei width, height, ctrl_panel_height;
long g_frames = 0;


/* segue la definizione delle posizioni (x,y,z,w) delle sorgenti luminose: 
   si noti che per la luce proveniente dal sole la posizione ha w=1, mentre
   le altre hanno w=0, quindi LIGHT1 e LIGHT2 sono luci posizionate ad infinito, 
   dove (x,y,z) specificano la direzione di provenienza della luce.
   
   Prima specifichiamo la posizione della luce simulante il sole */
GLfloat g_light0_pos[] = { 500.0, 50.0, 0.0, 1.0};

/* quindi direzioniamo le luci per NPR
   NB. Si noti che:
       - la direzione 1 e' una riscalatura delle coordinate del sole in modo
         che la luce provenga proprio dal sole
       - la 2 ha direzione inversa della 1 */
GLfloat g_light1_pos[] = { 1.0, 0.1, 0.0, 0.0}; /* come se provenisse dal sole */
GLfloat g_light2_pos[] = {-1.0,-0.1, 0.0, 0.0};

/* NEW: array contenenente gli ID delle texture caricate */
GLuint g_textureArray[MAX_TEXTURES];		



/* ----------------------------------------------------------*/
int
main( int argc, char *argv[] )
{
	glutInit( &argc, argv );

	width = glutGet( GLUT_SCREEN_WIDTH ) / 2; 
	height = glutGet( GLUT_SCREEN_HEIGHT ) / 2;
	ctrl_panel_height = 50;
           
	glutInitWindowPosition( width / 2, height / 2 );
	glutInitWindowSize( width, height );
    
    /* aggiungiamo double buffering per le animazioni e lo z buffer */
	glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
    
	glutCreateWindow( argv[0] );

	initgfx();

	glutKeyboardFunc( keyboard );
	glutSpecialFunc( specialkeys );
	glutDisplayFunc( drawScene ); 

    /* aggiungiamo l'aggiornameto di width e height per il corretto aspect ratio */
    glutReshapeFunc( reshape );
    
    /* aggiungiamo la funzione per animare pianeta e satellite */
    glutIdleFunc( animate ); 
    
    /* aggiungiamo la funzione per gestire la visibilita' della finestra */
    glutVisibilityFunc( visibility );    

    printHelp( argv[0] );

	glutMainLoop();
    return(0);
}

/* ----------------------------------------------------------*/
void printHelp( char *progname )
{
	fprintf(stdout, 
		"\n%s - elite in 3D with a realistic driving that uses pitch and roll\n\n"
		"arrow keys - move camera as a pilot moves a plane\n"
		"SPACE key  - speed up\n"
		"- key      - slow down\n"
		"Escape Key - exit the program\n\n",
		progname);
}

/* ----------------------------------------------------------*/
GLvoid initgfx( GLvoid )
{
    
  /* -- impostazioni luci... */
  
  /* impostazioni generali della luce */
  {
    GLfloat ambient_light[]={ 0.0, 0.0, 0.0, 1.0};
    GLfloat infinite[] = {0.0};
	GLfloat one_side[] = {0.0};
	
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient_light); /* poniamo a zero per
	  non intaccare NPR, sara' LIGHT0 ad avere una componente ambiente */
	glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, infinite); /* nella galassia possiamo
	  supporre distanze elevate alleggerendo cosi' la computazione */
	glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, one_side); /* non ha senso
	  illuminare l'interno degli oggetti */
  }

  /* impostazione luce principale (sole) */
  {
    GLfloat ambient_light[]={ 0.15, 0.15, 0.15, 1.0};

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_light); /* LIGHT0 dovra' essere
	  disabilitata durante il NPR per usare solo la luce controllata */
    /* NB. GL_SPECULAR di LIGHT0 e' per default [1.0, 1.0, 1.0] */
  }
  
  /* Seguono impostazioni delle luci controllate per il NPR (v. note)
     Supponendo che coriolis sia grigio kd=(0.5 0.5 0.5) e prendendo
     yellow = 0.5, blue = 0.5, alfa = 0.5 e beta = 0.5 si ha
	 
     Kwarm = [0.5, 0.5, 0.0] + 0.5 * [0.5, 0.5, 0.5] = [0.75, 0.75, 0.25]
     Kcool = [0.0, 0.0, 0.5] + 0.5 * [0.5, 0.5, 0.5] = [0.25, 0.25, 0.75] 
     Kwarm - Kcool = [0.5  0.5 -0.5] */      
  {
    GLfloat light_diffuse1[]={ 0.25,  0.25, -0.25, 1.0}; /* (Kwarm - Kcool)/2 */ 
    GLfloat light_diffuse2[]={-0.25, -0.25,  0.25, 1.0}; /* (Kcool - Kwarm)/2 */
    GLfloat light_ambient[] ={ 0.25,  0.25,  0.25, 1.0}; /* (Kcool + Kwarm)/4 anziche' 
      2 perche' ci sono due luci che contribuisco alla luce ambientale */  
    /* Impostazione della componente diffusa delle luci 1 e 2 */
    glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse1);
    glLightfv(GL_LIGHT2, GL_DIFFUSE, light_diffuse2);
    /* Luci che contribuiscono all'ambiente per PNR */  
    glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT2, GL_AMBIENT, light_ambient);
    /* NB. GL_SPECULAR delle luci diverse da LIGHT0 e' per default [0.0, 0.0, 0.0] */
  }


  /* -- abilita funzionalita' di OpenGL */  
  glShadeModel(GL_SMOOTH); /* enable smooth shading */
  glEnable(GL_DEPTH_TEST); /* enable z buffer */
  glEnable(GL_CULL_FACE); /* enable cull face */
  glCullFace(GL_BACK);

  /* abilitiamo l'utilizzo della luce */
  glEnable(GL_LIGHTING); /* enable lighting */
  glEnable(GL_LIGHT0);  /* accende luce 0 */

  /* NEW: inizializza due texture; notare l'ID diverso */
  initTexture(g_textureArray, "sun.sgi", 0);
  initTexture(g_textureArray, "earth.sgi", 1); 

  /* -- set clear color to black */
  glClearColor( 0.0, 0.0, 0.0, 1.0 );

  /* setta la telecamera */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  /* senza il fattore corretto gli angoli non si mantengono! */
  gluPerspective( 45.0, (GLdouble) width / (GLdouble) (height-ctrl_panel_height), 0.1, 1e3 );
        
}

/* ----------------------------------------------------------*/
void checkError( char *label )
{
	GLenum error;
	while ( (error = glGetError()) != GL_NO_ERROR )
		printf( "%s: %s\n", label, gluErrorString(error) );
}

/* ----------------------------------------------------------*/
GLvoid keyboard( GLubyte key, GLint x, GLint y )
{   
	switch (key) {

    case ' ': 
        g_camera.accelerate(CAM_SPEED_STEP);
        glutPostRedisplay();
        break;

    case '-': 
        g_camera.accelerate(-CAM_SPEED_STEP);
        glutPostRedisplay();
        break;

    case 'w': 
        g_camera.step(CAM_POS_STEP);
        glutPostRedisplay();
        break;

    case 'q': 
        g_camera.step(-CAM_POS_STEP);
        glutPostRedisplay();
        break;

	case KEY_ESC:	/* Exit when the Escape key is pressed */
        fprintf(stdout, "\n");
		exit(0);
	}
}

/* ----------------------------------------------------------*/
GLvoid specialkeys( GLint key, GLint u, GLint v )
{   
	switch (key) {

    case GLUT_KEY_RIGHT:
        g_camera.roll(CAM_ROT_STEP);
        glutPostRedisplay();
        break;

    case GLUT_KEY_LEFT: 
        g_camera.roll(-CAM_ROT_STEP);
        glutPostRedisplay(); 
        break;

    case GLUT_KEY_DOWN:
        g_camera.pitch(-CAM_ROT_STEP);
        glutPostRedisplay();
        break;

    case GLUT_KEY_UP: 
        g_camera.pitch(CAM_ROT_STEP);
        glutPostRedisplay(); 
        break;

	}
}

/* ----------------------------------------------------------*/
GLvoid drawScene( GLvoid )
{

    /* ogni volta che disegno incremento il numero dei frame */
    g_frames++;
    
	/* Do all your OpenGL rendering here */

    /* cancelliamo il  buffer video e lo z buffer */
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


    /* ------------------------------------------------------------------------- */
  	/* attiva il viewport principale */
  	glViewport( 0, ctrl_panel_height, width, height-ctrl_panel_height); 
        
    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity();

    glPushMatrix();
       
        g_camera.look();    /* chiama la gluLookAt con i parametri opportuni */
    
        /* posizionando la luce dopo la telecamera
           la luce risulta fissa nel mondo */ 
        glLightfv(GL_LIGHT0, GL_POSITION, g_light0_pos);
        glLightfv(GL_LIGHT1, GL_POSITION, g_light1_pos);
        glLightfv(GL_LIGHT2, GL_POSITION, g_light2_pos);

        /* satellite */
        {
          glPushMatrix();
                    
          /* accendo luci per fare NPR e spengo la 0 */
          glEnable(GL_LIGHT1);  /* accende luce 1 */
          glEnable(GL_LIGHT2);  /* accende luce 2 */
          glDisable(GL_LIGHT0); /* disabilito luce 0 in quanto uso 1 e 2 */
		  
            /* nessuna traslazione = posizionata in (0,0,0) */
            glRotatef(glutGet(GLUT_ELAPSED_TIME)*0.012, 0.0, 0.0, 1.0); /* ruotante */
            drawEliteObject2(coriolis_numberOfFaces, coriolis_vertex, coriolis_face, coriolis_material);
	  
          /* spengo luci per fare NPR e riaccendo la 0 */
          glEnable(GL_LIGHT0); /* riaccendo luce 0 */
          glDisable(GL_LIGHT1);  /* spegne luce 1 */
          glDisable(GL_LIGHT2);  /* spegne luce 2 */

          glPopMatrix();
        }

        /* NEW: pianeta texturizzato */
        {
          /* NEW: static GLfloat planetGreen[] = { 0.0f, 0.8f, 0.0f, 1.0f }; */
          GLUquadric* a = gluNewQuadric();
        
          /* salva attributi (colori, dimensioni linee, ecc...) */
          glPushAttrib(GL_ALL_ATTRIB_BITS);
        
          glEnable(GL_TEXTURE_2D); /* NEW: enable 2D texture mapping */
          
          glPushMatrix();

            glTranslatef( 0, 0, 260 );
            glRotatef( -90, 1.0, 0.0, 0.0 );
            glRotatef(glutGet(GLUT_ELAPSED_TIME)*0.012, 0.0, 0.0, 1.0); /* ruotante */

            /* Prima colorevamo noi... 
            glMaterialfv(GL_FRONT, GL_DIFFUSE, planetGreen); 
            glMaterialfv(GL_FRONT, GL_AMBIENT, planetGreen);            
            glutSolidSphere( 40, 24, 24 ); */
            
            /* Ora usiamo la texture... */
            //glBindTexture(GL_TEXTURE_2D, g_textureArray[0]);
            glBindTexture(GL_TEXTURE_2D, g_textureArray[1]);
            /* NEW: abilito la generazione automatica delle coordinate texture 
               altrimenti avrei dovuto usare glTexCoord */
            gluQuadricTexture( a, GL_TRUE );
            gluSphere(a, 40, 24, 24); 

          glPopMatrix();
        
          glDisable(GL_TEXTURE_2D); /* NEW: disable 2D texture mapping */

          glPopAttrib();
        }
                
        /* sole */
        {
          static GLfloat sunYellow[]={1.0, 1.0, 0.0, 1.0}; 
        
          /* salva attributi (colori, dimensioni linee, ecc...) */
          glPushAttrib(GL_ALL_ATTRIB_BITS);
          glPushMatrix();
          
          /* utilizziamo glColorMaterial + glColor invece di glMaterial */
          glEnable(GL_COLOR_MATERIAL);
          
            glTranslatef(500, 50, 0);

            /* Specifichiamo le proprieta' del materiale
               su cui si agira' con glColor. 
               Dal momento che il sole brilla di luce propria emette luce */
            glColorMaterial(GL_FRONT, GL_EMISSION);
            glColor3fv(sunYellow);  /* Queste due istruzioni equivalgono a 
                                       glMaterialfv(GL_FRONT, GL_EMISSION, sunYellow); */
               
            glutSolidSphere(100, 24, 24); /* disegna sfera con GLUT */
          
          glDisable(GL_COLOR_MATERIAL);

          glPopMatrix();
          glPopAttrib();
         }
         
    glPopMatrix();


    /* Stiamo per lavorare in 2D, quindi disabilitiamo un po' di cose... */
    glDisable(GL_LIGHTING); /* disable lighting */
    glDisable(GL_DEPTH_TEST); /* disable z buffer */
    glDisable(GL_CULL_FACE); /* disable face culling */
    
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    
    /* ------------------------------------------------------------------------- */
    /* Inseriamo il mirino utilizzando pero' proiezione ortografica */    

    /* Crea nuova vista ortogonale */
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();

    glLoadIdentity();
    gluOrtho2D( 0, (GLdouble) width / (GLdouble) (height-ctrl_panel_height), 0, 1);

    /* Resetta modelview */
    glMatrixMode(GL_MODELVIEW);  
    glLoadIdentity();

    glPushMatrix();
        /* disegna il mirino */
        drawSight((GLdouble) width / (GLdouble) (height-ctrl_panel_height)*0.5, 0.5);
    glPopMatrix();
    
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();


    /* ------------------------------------------------------------------------- */
    /* Attiva il viewport del pannello, in basso  */
    glViewport( 0, 0, width, ctrl_panel_height);     
    
    /* Salva la projection e costruisce una vista ortogonale */
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();

    glLoadIdentity();
    /* vista ortgogonale, con fattore d'aspetto corretto, 
       in modo che non ci sia distorsone */
    gluOrtho2D(0, (GLfloat)width/ctrl_panel_height, 0, 1);

    /* Resetta modelview */
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glPushMatrix();
	    drawControlPanel();
    glPopMatrix();  
    
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

    glPopAttrib();

    /* Riabilitiamo quanto precedentemente disabilitato */
    glEnable(GL_LIGHTING); /* riattiviamo le luci */
    glEnable(GL_DEPTH_TEST); /* riabilitiamo z buffering */
    glEnable(GL_CULL_FACE); /* riabilitiamo face culling */

    /* per non vedere il frame buffer mentre viene disegnato */
    glutSwapBuffers();
    
    checkError( "drawScene" );
}

/* ----------------------------------------------------------*/
void drawControlPanel()
{ 
  /* disegna il pannello di controllo in un vieport 
     di altezza 1 e larghezza dipendente da quella della finestra */
 
  GLfloat v;  /* velocita' tra 0 e 1; */
  float x,y,z; 
  Point3 u;
  int t;
  
     
  /* -- barra della velocita' */
  glLineWidth( 1 );
  glColor3f( 0, 0, 1);
  glBegin( GL_LINE_LOOP ); /* quadrato */
  glVertex2f(1, 0.5);
  glVertex2f(2, 0.5);
  glVertex2f(2, 0);
  glVertex2f(1, 0);
  glEnd();
 
  v = (g_camera.getSpeed())/(g_camera.MAXSPEED);
  if ( v < 0.8 )
    glColor3f( 1, 1, 0);
  else
  glColor3f( 1, 0, 0);
  glBegin( GL_QUADS);  
    glVertex3f(1.05+1.0*v, 0, 0.1);  
    glVertex3f(1.05+1.0*v, 0.5, 0.1);   
    glVertex3f(1.05, 0.5, 0.1);
    glVertex3f(1.05, 0, 0.1); 
  glEnd(); 

  /* posizioniamo la scritta */
  glColor3f( 0, 0, 1);
  glRasterPos2f(2.1, 0.1);
  renderBitmapString(GLUT_BITMAP_HELVETICA_18, "SP"); 


  /* -- radar */
  glColor3f( 0, 0, 1);
  glLineWidth( 0.2 );
  glBegin( GL_LINES ); /* cerchio */
    for(t=0; t< 200; t++)
      glVertex3f(0.5+0.5*cos(t/200.0*2*PI), 0.5+0.5*sin(t/200.0*2*PI),0.1);
  glEnd();

  /* crocetta */
  glColor3f( 1, 1, 1);
  glLineWidth( 0.2 );
  glBegin( GL_LINES); 
    glVertex3f(1, 0.5,-0.1);
    glVertex3f(0, 0.5,-0.1);
    glVertex3f(0.5, 1,-0.1);
    glVertex3f(0.5, 0,-0.1);
  glEnd();

  /* puntino verde (fronte) o rosso (retro) */
  u = Normalize(g_camera.getPos());
  x = -ProdScal(u,g_camera.getRight());
  y = -ProdScal(u,g_camera.getUp());
  z = -ProdScal(u,g_camera.getView());

  glColor3f( 0, 1, 0);
  glPointSize(4);
  glBegin(GL_POINTS);  
    if ( z < 0 ) /* uso la z per disegnarlo dietro */
      glColor3f( 0.78, 0.25, 0);
    else
      glColor3f( 0, 1, 0);
    glVertex2f(0.5 + 0.5*x, 0.5+0.5*y); 
  glEnd();
}


/* ----------------------------------------------------------*/
GLvoid reshape( GLsizei w, GLsizei h )
{
    /* aggiorniamo dimensioni con i valori correnti */
    width = w;
    height = h;

    /* aggiornamento settaggi relativi alla visione in soggettiva del gioco */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective( 45.0, (GLdouble) width / (GLdouble) (height-ctrl_panel_height), 0.1, 1e3 );
}


/* ----------------------------------------------------------*/
GLvoid animate( GLvoid )
{ 

  long dtime;
  static long currentTime=0, previousTime=0;
  static long nearTime=0;
  
  /* restituisce il tempo in ms da glutInit o dall'ultima chiamata */
  currentTime = glutGet(GLUT_ELAPSED_TIME);
  dtime = currentTime - previousTime;
  
  /* questo serve per calcolare il fps ogni secondo (circa) */
  if (dtime > 1000)
  {
      fprintf(stdout, "\r%d fps", (int)(1e3 * (double) g_frames/(double) dtime) );
      fflush(stdout);
      g_frames = 0;
      previousTime = currentTime;
  }
 
  /* muoviamo la nostra navetta spaziale (ossia la telecamera) in base
     al tempo trascorso dall'ultimo rendering */
  dtime = currentTime - nearTime;
  nearTime = currentTime;
  g_camera.advance(dtime);
  
  glutPostRedisplay();
}



/* ----------------------------------------------------------*/
GLvoid visibility( int state ) 
{
   /* restart the animation function if we were
    * animated when the window was hidden
    */
   if (state == GLUT_VISIBLE) {
       glutIdleFunc( animate );
   } else {
       glutIdleFunc( NULL );
   }
}

/* ---------------------------------------------------------- */
void initTexture(GLuint textureArray[], char  *imageFileName, int textureID)
{
  GLsizei imageWidth, imageHeight, components;
  unsigned char *image, *scaled_image;
 
  /* NEW: legge texture, formato SGI (RGB) .
   Attenzione: deve essere di dimensioni potenze di 2, non c'e' controllo */
  image = read_texture(imageFileName, &imageWidth, &imageHeight, &components);

  fprintf(stderr,"Input image size: %dx%d\n", imageWidth, imageHeight );

  /* NEW: Registra la texture con il suo ID */
  glGenTextures(1, &textureArray[textureID]);

  /* NEW: Bind the texture to the texture arrays index */
  glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);
  
  /* NEW: I due comandi sopra non sono indispensabili, servono per registrare 
     in anticipo le texture che serviranno, da collegare poi agli oggetti 
     con il glBindTexture(). Se serve una texture sola, basta il comando
     successivo */

  /* NEW: allocazione + riscalatura + creazione della texture */
  {
    int newImgWidth = (int)(imageWidth/2), 
        newImgHeight = (int)(imageHeight/2);
        
    /* NEW: allocazione della memoria per l'iimagine riscalata */
    scaled_image = (unsigned char *) 
      malloc(newImgWidth * newImgHeight * 4 * sizeof(unsigned char));

    /* NEW: scale a texture */
    gluScaleImage(GL_RGBA, 
      imageWidth, imageHeight, GL_UNSIGNED_BYTE, image, 
      newImgWidth, newImgHeight, GL_UNSIGNED_BYTE, scaled_image);
  
    /* NEW: create texture */
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newImgWidth, newImgHeight,
	  0, GL_RGBA, GL_UNSIGNED_BYTE, scaled_image);
  }
  
  /* NEW: Setting the minification and magnification filters
   * to nearest instead of linear, may run faster on some 
   * platforms, with possibly lower quality 
   */
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

  /* NEW: modo modulate (default) oppure decal */
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  /* image e' stata memorizzata come texture da OpenGL e non serve piu' */
  free(image);
  
}

/* Credits:
   Riccardo Giannitrapani per la scrittura di bitmap
   Ben Humphrey (DigiBen@GameTutorials.com) per la classe camera
   Tutorial  SGI (quello che c'e' sulla pagina web del lab) in generale */

