/* melite7b.c - visualizza un pianeta e una navetta in 3D * usando le primitive viste, muove la telecamera usando camera.cpp * aggiornando di conseguenza il radar. * -- Aggiunto il sole con relativo lighting e realizzato NPR di coriolis. * * 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" /* 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 ); /* Global Definitions */ /* telecamera */ Camera g_camera(-1, -1, -5); GLsizei width, height, ctrl_panel_height; long g_frames = 0; /* NEW: 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}; #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 */ 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 ) { /* -- NEW: impostazioni luci... */ /* NEW: 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 */ } /* NEW: 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] */ } /* NEW: 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); /* NEW: abilitiamo l'utilizzo della luce */ glEnable(GL_LIGHTING); /* enable lighting */ glEnable(GL_LIGHT0); /* accende luce 0 */ /* -- 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 */ /* NEW: 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(); /* NEW: 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); /* NEW: 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(); } /* pianeta */ { static GLfloat planetGreen[] = { 0.0f, 0.8f, 0.0f, 1.0f }; /* NEW: salva attributi (colori, dimensioni linee, ecc...) */ glPushAttrib(GL_ALL_ATTRIB_BITS); glPushMatrix(); glTranslatef( 0, 0, 260 ); glRotatef(glutGet(GLUT_ELAPSED_TIME)*0.012, 0.0, 0.0, 1.0); /* ruotante */ /* Prima colorevamo specificando il colore con glColor3fv( darkgreen ); ora usiamo la luce e le proprieta' del materiale */ /* NEW: Proprieta' del materiale del pianeta */ glMaterialfv(GL_FRONT, GL_DIFFUSE, planetGreen); glMaterialfv(GL_FRONT, GL_AMBIENT, planetGreen); glutSolidSphere( 40, 24, 24 ); glPopMatrix(); glPopAttrib(); } /* sole */ { static GLfloat sunYellow[]={1.0, 1.0, 0.0, 1.0}; /* NEW: salva attributi (colori, dimensioni linee, ecc...) */ glPushAttrib(GL_ALL_ATTRIB_BITS); glPushMatrix(); /* NEW: utilizziamo glColorMaterial + glColor invece di glMaterial */ glEnable(GL_COLOR_MATERIAL); glTranslatef(500, 50, 0); /* NEW: 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(); /* NEW: 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(); /* NEW: 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 ); } } /* 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 */