/*!\file window.c * \brief Utilisation du raster "maison" pour finaliser le pipeline de * rendu 3D. Ici on peut voir les géométries disponibles. * \author Farès BELHADJ, amsi@up8.edu * \date December 4, 2020. * \todo pour les étudiant(e)s : changer la variation de l'angle de * rotation pour qu'il soit dépendant du temps et non du framerate */ #include /* inclusion des entêtes de fonctions de gestion de primitives simples * de dessin. La lettre p signifie aussi bien primitive que * pédagogique. */ #include /* inclure notre bibliothèque "maison" de rendu */ #include "moteur.h" /* inclusion des entêtes de fonctions de création et de gestion de * fenêtres système ouvrant un contexte favorable à GL4dummies. Cette * partie est dépendante de la bibliothèque SDL2 */ #include #include /* protos de fonctions locales (static) */ static void init(void); static void draw(void); static void key(int keycode); static void sortie(void); static void pmotion(int x, int y); static void mouse(int button, int state, int x, int y); static void goto_obj(float * mvMat); static void move_to(float posx, float posy , float posz); static void draw_object(float * nmv, float * projMat, surface_t * obj, float ma, float mx, float my, float tx, float ty, float tz, float s, float ra, float rx, float ry); static void get_params(void); static int get_sign(float x, float y); /*!\brief un identifiant pour l'écran (de dessin) */ static uint _screenId = 0; // obj contains all planets, the Sun, Pluto, all the moons and their parameters. static object_t _obj[28]; /* des variable d'états pour activer/désactiver des options de rendu */ static int _use_tex = 1, _use_color = 0, _use_lighting = 1; typedef struct cam_t cam_t; // camera structure, borrowed from sample3d_01-1.6 of GL4Dummies samples. struct cam_t { float x, y, z; float theta; }; static cam_t _cam = {0, 1.0f, 10, 0}; // movement speed. static float _v = 0.1f; // window size. static int _wW = 1200, _wH = 900; // middle of the window. static int _xm = 600, _ym = 450; static int _pause = 0; // boolean value for the pause. static int _movement = 1; // boolean to allow movement. static int _overview = 0; // boolean to toggle overview (view from the top). static int _p = -1; // the object (sun, planets, pluto) number to move. static float _s = 1.0f; // multiplier for angle (for speeding planets movement and rotation). static float _a = 0.0f; // rotation angle. static float _r = 0.0f; // -//- static Mix_Chunk * bsound; // background sound. /*!\brief paramètre l'application et lance la boucle infinie. */ int main(int argc, char ** argv) { if (SDL_Init(SDL_INIT_AUDIO) == -1) { fprintf(stderr, "SDL_Init: %s\n,", Mix_GetError()); exit(3); } /* tentative de création d'une fenêtre pour GL4Dummies */ if (!gl4duwCreateWindow(argc, argv, /* args du programme */ "Solar System", /* titre */ 10, 10, _wW, _wH, /* x, y, largeur, heuteur */ GL4DW_SHOWN) /* état visible */) { /* ici si échec de la création souvent lié à un problème d'absence * de contexte graphique ou d'impossibilité d'ouverture d'un * contexte OpenGL (au moins 3.2) */ return 1; } /* Pour forcer la désactivation de la synchronisation verticale */ SDL_GL_SetSwapInterval(0); init(); /* création d'un screen GL4Dummies (texture dans laquelle nous * pouvons dessiner) aux dimensions de la fenêtre */ _screenId = gl4dpInitScreen(); /* mettre en place la fonction d'interception clavier */ gl4duwMouseFunc(mouse); gl4duwKeyDownFunc(key); gl4duwPassiveMotionFunc(pmotion); /* mettre en place la fonction de display */ gl4duwDisplayFunc(draw); /* boucle infinie pour éviter que le programme ne s'arrête et ferme * la fenêtre immédiatement */ gl4duwMainLoop(); return 0; } /*!\brief init de nos données, spécialement les trois surfaces * utilisées dans ce code */ void init(void) { uint id[28], i; int flags, initted; flags = MIX_INIT_MP3; initted = Mix_Init(flags); if ((initted & flags) != flags) { fprintf(stderr, "Mix_Init: Failed to init required mp3 support!\n"); fprintf(stderr, "Mix_Init: %s\n,", Mix_GetError()); exit(2); } if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) == -1) { fprintf(stderr, "Mix_OpenAudio: %s\n,", Mix_GetError()); exit(4); } if (bsound == NULL) bsound = Mix_LoadWAV("./space.wav"); if (Mix_PlayChannel(-1, bsound, 0) < 0) fprintf(stderr, "Mix_PlayChannel: %s\n", Mix_GetError()); // create all spheres. for (i = 0; i < 28; ++i) { _obj[i].s = mkSphere(12, 12); } // get all textures. id[0] = getTexFromBMP("images/2k-sun.bmp"); id[1] = getTexFromBMP("images/2k-mercury.bmp"); id[2] = getTexFromBMP("images/2k-venus-surface.bmp"); id[3] = getTexFromBMP("images/2k-earth-daymap.bmp"); id[4] = getTexFromBMP("images/2k-moon.bmp"); id[5] = getTexFromBMP("images/2k-mars.bmp"); id[6] = getTexFromBMP("images/moons/phobos.bmp"); id[7] = getTexFromBMP("images/moons/deimos.bmp"); id[8] = getTexFromBMP("images/2k-jupiter.bmp"); id[9] = getTexFromBMP("images/moons/io.bmp"); id[10] = getTexFromBMP("images/moons/europa.bmp"); id[11] = getTexFromBMP("images/moons/ganymede.bmp"); id[12] = getTexFromBMP("images/moons/callisto.bmp"); id[13] = getTexFromBMP("images/2k-saturn.bmp"); id[14] = getTexFromBMP("images/moons/enceladus.bmp"); id[15] = getTexFromBMP("images/moons/dione.bmp"); id[16] = getTexFromBMP("images/moons/rhea.bmp"); id[17] = getTexFromBMP("images/moons/titan.bmp"); id[18] = getTexFromBMP("images/moons/iapetus.bmp"); id[19] = getTexFromBMP("images/2k-uranus.bmp"); id[20] = getTexFromBMP("images/moons/ariel.bmp"); id[21] = getTexFromBMP("images/moons/umbriel.bmp"); id[22] = getTexFromBMP("images/moons/titania.bmp"); id[23] = getTexFromBMP("images/moons/oberon.bmp"); id[24] = getTexFromBMP("images/2k-neptune.bmp"); id[25] = getTexFromBMP("images/moons/triton.bmp"); id[26] = getTexFromBMP("images/pluto.bmp"); id[27] = getTexFromBMP("images/moons/charon.bmp"); // https://upload.wikimedia.org/wikipedia/commons/4/4f/Moons_of_solar_system_v7.jpg // most of the biggest moons are here. // set texture to an object id and set all options. for (i = 0; i < 28; ++i) { setTexId(_obj[i].s, id[i]); disableSurfaceOption(_obj[i].s, SO_USE_COLOR); if (_use_tex) enableSurfaceOption(_obj[i].s, SO_USE_TEXTURE); if (_use_lighting) enableSurfaceOption(_obj[i].s, SO_USE_LIGHTING); } get_params(); atexit(sortie); } static void get_params(void) { int i; FILE * f; f = fopen("vars.txt", "r"); for (i = 0; i < 28; ++i) { fscanf(f, "%d %f %f %f %f %f %f %f %f %f %f", &_obj[i].id, &_obj[i].vars[0], &_obj[i].vars[1], &_obj[i].vars[2], &_obj[i].vars[3], &_obj[i].vars[4], &_obj[i].vars[5], &_obj[i].vars[6], &_obj[i].vars[7], &_obj[i].vars[8], &_obj[i].vars[9]); // debug /*fprintf(stderr, "%d %f %f %f %f %f %f %f %f %f %f\n", _obj[i].id, _obj[i].vars[0], _obj[i].vars[1], _obj[i].vars[2], _obj[i].vars[3], _obj[i].vars[4], _obj[i].vars[5], _obj[i].vars[6], _obj[i].vars[7], _obj[i].vars[8], _obj[i].vars[9]);*/ } fclose(f); } // get sign for further move calculation. static int get_sign(float x, float y) { if (x > y) return 1; return -1; } // move to the coordinate. static void move_to(float posx, float posy , float posz){ float epsilon = 0.1f; // precision for comparing if (fabsf(_cam.x - posx) > epsilon) { _cam.x = _cam.x - (get_sign(_cam.x, posx) * 0.09f); } if (fabsf(_cam.y - posy) > epsilon) { _cam.y = _cam.y - (get_sign(_cam.y, posy) * 0.09f); } if (fabsf(_cam.z - posz) > epsilon) { _cam.z = _cam.z - (get_sign(_cam.z, posz) * 0.09f); } } // go to a choosen object (planet, star, pluto(dwarf)) static void goto_obj(float * mvMat) { switch (_p) { case 1: // MERCURY _a = 0; // reset angle to 0. lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[1].vars[3], _obj[1].vars[4], _obj[1].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[1].vars[3], _obj[1].vars[4], 0.5f); break; case 2: // VENUS _a = 0; lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[2].vars[3], _obj[2].vars[4], _obj[2].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[2].vars[3], _obj[2].vars[4], 1.0f); break; case 3: // EARTH _a = 0; lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[3].vars[3], _obj[3].vars[4], _obj[3].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[3].vars[3], _obj[3].vars[4], 1.0f); break; case 4: // MARS _a = 0; lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[5].vars[3], _obj[5].vars[4], _obj[5].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[5].vars[3], _obj[5].vars[4], 0.5f); break; case 5: // JUPITER _a = 0; lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[8].vars[3], _obj[8].vars[4], _obj[8].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[8].vars[3], _obj[8].vars[4], 8.0f); break; case 6: // SATURN _a = 0; lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[13].vars[3], _obj[13].vars[4], _obj[13].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[13].vars[3], _obj[13].vars[4], 8.0f); break; case 7: // URANUS _a = 0; lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[19].vars[3], _obj[19].vars[4], _obj[19].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[19].vars[3], _obj[19].vars[4], 5.0f); break; case 8: // NEPTUNE _a = 0; lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[24].vars[3], _obj[24].vars[4], _obj[24].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[24].vars[3], _obj[24].vars[4], 4.0f); break; case 9: // PLUTO _a = 0; lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[26].vars[3], _obj[26].vars[4], _obj[26].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[26].vars[3], _obj[26].vars[4], 0.4f); break; case 0: // SUN _a = 0; lookAt(mvMat, _cam.x, _cam.y, _cam.z, _obj[0].vars[3], _obj[0].vars[4], _obj[0].vars[5], 0.0f, 1.0f, 0.0f); move_to(_obj[0].vars[3], _obj[0].vars[4], 10.0f); break; } } static void draw_object(float * nmv, float * projMat, surface_t * obj, float ma, float mx, float my, float tx, float ty, float tz, float s, float ra, float rx, float ry) { rotate(nmv, ma, mx, my, 0); // orbit movement translate(nmv, tx, ty, tz); scale(nmv, s, s, s); rotate(nmv, ra, rx, ry, 0); // rotation transform_n_raster(obj, nmv, projMat); } /*!\brief la fonction appelée à chaque display. */ void draw(void) { static double t0 = 0, t, dt; int i; float mvMat[16], projMat[16], nmv[16], cpy[16]; t = gl4dGetElapsedTime(); dt = (t - t0) / 1000.0; t0 = t; /* effacer l'écran et le buffer de profondeur */ gl4dpClearScreen(); clearDepth(); /* des macros facilitant le travail avec des matrices et des * vecteurs se trouvent dans la bibliothèque GL4Dummies, dans le * fichier gl4dm.h */ /* charger un frustum dans projMat */ MFRUSTUM(projMat, -0.005f, 0.005f, -0.005f, 0.005f, 0.01f, 1000.0f); /* charger la matrice identité dans model-view */ MIDENTITY(mvMat); // Y ----> 1.0 - (_ym - (_wH >> 1)) / (float)_wH * 5 if (_movement) lookAt(mvMat, _cam.x, _cam.y, _cam.z, _cam.x - sin(_cam.theta), 1.0, _cam.z - cos(_cam.theta), 0.0, 1.0, 0.0); else goto_obj(mvMat); // SUN memcpy(nmv, mvMat, sizeof nmv); /* copie mvMat dans nmv */ draw_object(nmv, projMat, _obj[0].s, _r/_obj[0].vars[0], _obj[0].vars[1], _obj[0].vars[2], _obj[0].vars[3], _obj[0].vars[4], _obj[0].vars[5], _obj[0].vars[6], _obj[0].vars[7], _obj[0].vars[8], _obj[0].vars[9]); for (i = 1; i < 28; ++i) { if(_obj[i].id == 1) { // if object is a planet memcpy(nmv, mvMat, sizeof nmv); draw_object(nmv, projMat, _obj[i].s, _a/_obj[i].vars[0], _obj[i].vars[1], _obj[i].vars[2], _obj[i].vars[3], _obj[i].vars[4], _obj[i].vars[5], _obj[i].vars[6], _r/_obj[i].vars[7], _obj[i].vars[8], _obj[i].vars[9]); if (i <= 27) { if (_obj[i+1].id == 2) memcpy(cpy, nmv, sizeof cpy); } } else if (_obj[i].id == 2) { // if object is a moon memcpy(nmv, cpy, sizeof nmv); draw_object(nmv, projMat, _obj[i].s, _r/_obj[i].vars[0], _obj[i].vars[1], _obj[i].vars[2], _obj[i].vars[3], _obj[i].vars[4], _obj[i].vars[5], _obj[i].vars[6], _obj[i].vars[7], _obj[i].vars[8], _obj[i].vars[9]); } } /* déclarer qu'on a changé (en bas niveau) des pixels du screen */ gl4dpScreenHasChanged(); /* fonction permettant de raffraîchir l'ensemble de la fenêtre*/ gl4dpUpdateScreen(NULL); if (!_pause){ _a += ((360.0 * dt) / 60) * _s; // 360 in 1 minute so 1 day = 1 min _r += ((360.0 * dt) / 60) * _s; // 360 in 1 minute so 1 day = 1 min } } /*!\brief intercepte l'événement clavier pour modifier les options. */ void key(int keycode) { double step = 5.0; int i; switch(keycode) { case GL4DK_UP: if (_movement) _cam.y += step; break; case GL4DK_DOWN: if (_movement) _cam.y -= step; break; case GL4DK_RIGHT: if (!_pause) _s += 30.0f; break; case GL4DK_LEFT: if (!_pause) _s -= 30.0f; break; case GL4DK_w: if (_movement) { _cam.x += -_v * step * sin(_cam.theta); _cam.z += -_v * step * cos(_cam.theta); } break; case GL4DK_s: if (_movement) { _cam.x += _v * step * sin(_cam.theta); _cam.z += _v * step * cos(_cam.theta); } break; case GL4DK_a: if (_movement) _cam.theta += step * 0.01; break; case GL4DK_d: if (_movement) _cam.theta -= step * 0.01; break; case GL4DK_p: if (_pause == 0) _pause = 1; else _pause = 0; break; case GL4DK_MINUS: _v -= 0.01f; if (_v < 0.01f){ _v = 0.01f; } break; case GL4DK_EQUALS: _v += 0.01f; if (_v > 0.5f){ _v = 0.5f; } break; case GL4DK_o: if (_movement) { if (!_overview) { _overview = !_overview; _cam.x = 0.0f; _cam.y = 200.0f; _cam.z = 0.0f; _cam.theta = 0.0f; } else { _overview = !_overview; _cam.x = 0.0f; _cam.y = 1.0f; _cam.z = 10.0f; _cam.theta = 0.0f; } } break; case GL4DK_r: _a = _r; _p = -1; _pause = 0; _movement = 1; _overview = 0; break; case GL4DK_m: _s = 1; break; case GL4DK_q: exit(0); break; case GL4DK_0: _p = 0; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_1: _p = 1; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_2: _p = 2; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_3: _p = 3; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_4: _p = 4; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_5: _p = 5; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_6: _p = 6; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_7: _p = 7; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_8: _p = 8; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_9: _p = 9; /*_pause = 1;*/ _movement = 0; _overview = 0; break; case GL4DK_t: /* 't' la texture */ _use_tex = !_use_tex; if(_use_tex) { for (i = 0; i < 28; ++i) { enableSurfaceOption(_obj[i].s, SO_USE_TEXTURE); } } else { for (i = 0; i < 28; ++i) { disableSurfaceOption(_obj[i].s, SO_USE_TEXTURE); } } break; case GL4DK_c: /* 'c' utiliser la couleur */ _use_color = !_use_color; if(_use_color) { for (i = 0; i < 28; ++i) { enableSurfaceOption(_obj[i].s, SO_USE_COLOR); } } else { for (i = 0; i < 28; ++i) { disableSurfaceOption(_obj[i].s, SO_USE_COLOR); } } break; case GL4DK_l: /* 'l' utiliser l'ombrage par la méthode Gouraud */ _use_lighting = !_use_lighting; if(_use_lighting) { for (i = 0; i < 28; ++i) { enableSurfaceOption(_obj[i].s, SO_USE_LIGHTING); } } else { for (i = 0; i < 28; ++i) { disableSurfaceOption(_obj[i].s, SO_USE_LIGHTING); } } break; default: break; } } // handler of mouse motion. static void pmotion(int x, int y) { if (_movement){ _xm = x; _ym = y; _cam.theta = -(_xm - (_wW >> 1)) / (float)_wW*10; } } // handler of mouse buttons. static void mouse(int button, int state, int x, int y) { if (_movement){ double dtheta = M_PI; if (button == GL4D_BUTTON_LEFT) _cam.y += dtheta; if (button == GL4D_BUTTON_RIGHT) _cam.y -= dtheta; if (button == GL4D_BUTTON_MIDDLE) _cam.y = 1.0f; } } /*!\brief à appeler à la sortie du programme. */ void sortie(void) { int i; Mix_CloseAudio(); Mix_Quit(); for (i = 0; i < 28; ++i) { if(_obj[i].s){ freeSurface(_obj[i].s); _obj[i].s = NULL; } } if (bsound) Mix_FreeChunk(bsound); bsound = NULL; /* libère tous les objets produits par GL4Dummies, ici * principalement les screen */ gl4duClean(GL4DU_ALL); }