/* 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 #include #include /* 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 */ void initgfx( void ); void keyboard( GLubyte, GLint, GLint ); void specialkeys( GLint, GLint, GLint ); void drawScene( void ); void drawControlPanel(); void checkError( char * ); void printHelp( char * ); void reshape ( GLsizei, GLsizei ); void animate( void ); void visibility( GLint ); /* NEW: funzione per il caricamento della texture da porre sul pianeta */ void 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); } /* ----------------------------------------------------------*/ void initgfx( void ) { /* -- 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) ); } /* ----------------------------------------------------------*/ void 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); } } /* ----------------------------------------------------------*/ void 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; } } /* ----------------------------------------------------------*/ void drawScene( void ) { /* 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())/(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(); } /* ----------------------------------------------------------*/ void 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 ); } /* ----------------------------------------------------------*/ void animate( void ) { 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(); } /* ----------------------------------------------------------*/ void 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 */