diff --git a/geometry.c b/geometry.c index 84ab96c..c25c568 100644 --- a/geometry.c +++ b/geometry.c @@ -1,10 +1,10 @@ -/*!\file geometry.h +/*!\file geometry.h * * \brief quelques surfaces basiques sous forme polygonale : un plan * (quad), un cube et une sphere. * * \author Farès BELHADJ, amsi@up8.edu - * \date November, 2021. + * \date November, 2021. */ #include "rasterize.h" #include @@ -17,141 +17,141 @@ * quadrilatère "debout" et à la profondeur 0. Il fait la hauteur et * la largeur du cube unitaire (-1 à 1).*/ surface_t * mk_quad(void) { - static const float - data[] = { - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, - 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, - 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, -1.0f - }; - static const int order[] = { 0, 1, 2, 2, 1, 3 }; - surface_t * s; - /* on met du jaune partout */ - const vec4 color0 = { 1.0f, 1.0f, 0.0f, 1.0f }; - triangle_t t[2]; - int i, j, k, o; - for(i = 0, o = 0; i < 2; ++i) - for(j = 0; j < 3; ++j, ++o) { - k = order[o] * 8; - t[i].v[j].position = *(vec4 *)&(data[k]); - t[i].v[j].position.w = 1.0f; - t[i].v[j].normal = *(vec3 *)&(data[k + 3]); - t[i].v[j].texCoord = *(vec2 *)&(data[k + 6]); - t[i].v[j].color0 = color0; - } - s = new_surface(t, 2, 1, 1); - snormals(s); - return s; + static const float + data[] = { + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, + 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, -1.0f + }; + static const int order[] = { 0, 1, 2, 2, 1, 3 }; + surface_t * s; + /* on met du jaune partout */ + const vec4 color0 = { 1.0f, 1.0f, 0.0f, 1.0f }; + triangle_t t[2]; + int i, j, k, o; + for(i = 0, o = 0; i < 2; ++i) + for(j = 0; j < 3; ++j, ++o) { + k = order[o] * 8; + t[i].v[j].position = *(vec4 *)&(data[k]); + t[i].v[j].position.w = 1.0f; + t[i].v[j].normal = *(vec3 *)&(data[k + 3]); + t[i].v[j].texCoord = *(vec2 *)&(data[k + 6]); + t[i].v[j].color0 = color0; + } + s = new_surface(t, 2, 1, 1); + snormals(s); + return s; } /*!\brief fabrique et renvoie une surface représentant un * cube unitaire (de -1 à 1).*/ surface_t * mk_cube(void) { - const float - data[] = { - /* front */ - -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, - 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, - /* back */ - 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, - -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, - 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, - -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, - /* right */ - 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, - /* left */ - -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, - -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, - /* top */ - -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, - -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, - /* bottom */ - -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f - }; - const int order[] = { 0, 1, 2, 2, 1, 3 }; - surface_t * s; - /* on met du vert-clair partout */ - const vec4 color0 = { 0.5f, 1.0f, 0.0f, 1.0f }; - triangle_t t[12]; - int i, j, k, o; - for(i = 0, o = 0; i < 12; ++i) - for(j = 0; j < 3; ++j, ++o) { - k = 8 * (order[o % 6] + 4 * (i / 2)); - t[i].v[j].position = *(vec4 *)&(data[k]); - t[i].v[j].position.w = 1.0f; - t[i].v[j].normal = *(vec3 *)&(data[k + 3]); - t[i].v[j].texCoord = *(vec2 *)&(data[k + 6]); - t[i].v[j].color0 = color0; - } - s = new_surface(t, 12, 1, 1); - snormals(s); - return s; + const float + data[] = { + /* front */ + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + /* back */ + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + /* right */ + 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + /* left */ + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + /* top */ + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + /* bottom */ + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f + }; + const int order[] = { 0, 1, 2, 2, 1, 3 }; + surface_t * s; + /* on met du vert-clair partout */ + const vec4 color0 = { 0.5f, 1.0f, 0.0f, 1.0f }; + triangle_t t[12]; + int i, j, k, o; + for(i = 0, o = 0; i < 12; ++i) + for(j = 0; j < 3; ++j, ++o) { + k = 8 * (order[o % 6] + 4 * (i / 2)); + t[i].v[j].position = *(vec4 *)&(data[k]); + t[i].v[j].position.w = 1.0f; + t[i].v[j].normal = *(vec3 *)&(data[k + 3]); + t[i].v[j].texCoord = *(vec2 *)&(data[k + 6]); + t[i].v[j].color0 = color0; + } + s = new_surface(t, 12, 1, 1); + snormals(s); + return s; } /*!\brief fabrique et renvoie une surface représentant une sphère * centrée en zéro et de rayon 1. Elle est découpée en \a longitudes * longitudes et \a latitudes latitudes. */ surface_t * mk_sphere(int longitudes, int latitudes) { - triangle_t * t; - vertex_t * data; - double phi, theta, r, y; - double c2MPI_Long = 2.0 * M_PI / longitudes; - double cMPI_Lat = M_PI / latitudes; - /* on met du vert-clair partout */ - const vec4 color0 = { 0.5f, 1.0f, 0.0f, 1.0f }; - int z, nz, x, nx, zw, nzw, k, n = 2 * longitudes * latitudes; - assert(n); - data = malloc((longitudes + 1) * (latitudes + 1) * sizeof *data); - assert(data); - t = malloc(n * sizeof *t); - assert(t); - for(z = 0, k = 0; z <= latitudes; ++z) { - theta = -M_PI_2 + z * cMPI_Lat; - y = sin(theta); - r = cos(theta); - for(x = 0; x <= longitudes; ++x, ++k) { - phi = x * c2MPI_Long; - data[k].position.x = r * cos(phi); - data[k].position.y = y; - data[k].position.z = r * sin(phi); - data[k].position.w = 1.0f; - data[k].texCoord.x = phi / (2.0 * M_PI); - data[k].texCoord.y = (theta + M_PI_2) / M_PI; - data[k].color0 = color0; - /* gcc 7.5 et plus abusent : data[k].normal = *(vec3 *)&(data[k].position); */ - data[k].normal.x = data[k].position.x; - data[k].normal.y = data[k].position.y; - data[k].normal.z = data[k].position.z; + triangle_t * t; + vertex_t * data; + double phi, theta, r, y; + double c2MPI_Long = 2.0 * M_PI / longitudes; + double cMPI_Lat = M_PI / latitudes; + /* on met du vert-clair partout */ + const vec4 color0 = { 0.5f, 1.0f, 0.0f, 1.0f }; + int z, nz, x, nx, zw, nzw, k, n = 2 * longitudes * latitudes; + assert(n); + data = malloc((longitudes + 1) * (latitudes + 1) * sizeof *data); + assert(data); + t = malloc(n * sizeof *t); + assert(t); + for(z = 0, k = 0; z <= latitudes; ++z) { + theta = -M_PI_2 + z * cMPI_Lat; + y = sin(theta); + r = cos(theta); + for(x = 0; x <= longitudes; ++x, ++k) { + phi = x * c2MPI_Long; + data[k].position.x = r * cos(phi); + data[k].position.y = y; + data[k].position.z = r * sin(phi); + data[k].position.w = 1.0f; + data[k].texCoord.x = phi / (2.0 * M_PI); + data[k].texCoord.y = (theta + M_PI_2) / M_PI; + data[k].color0 = color0; + /* gcc 7.5 et plus abusent : data[k].normal = *(vec3 *)&(data[k].position); */ + data[k].normal.x = data[k].position.x; + data[k].normal.y = data[k].position.y; + data[k].normal.z = data[k].position.z; + } } - } - for(z = 0, k = 0; z < latitudes; ++z) { - nz = z + 1; - zw = z * (longitudes + 1); - nzw = nz * (longitudes + 1); - for(x = 0; x < longitudes; ++x) { - nx = x + 1; - t[k].v[0] = data[zw + x]; - t[k].v[1] = data[nzw + x]; - t[k].v[2] = data[zw + nx]; - tnormal(&t[k]);++k; - t[k].v[0] = data[zw + nx]; - t[k].v[1] = data[nzw + x]; - t[k].v[2] = data[nzw + nx]; - tnormal(&t[k]);++k; + for(z = 0, k = 0; z < latitudes; ++z) { + nz = z + 1; + zw = z * (longitudes + 1); + nzw = nz * (longitudes + 1); + for(x = 0; x < longitudes; ++x) { + nx = x + 1; + t[k].v[0] = data[zw + x]; + t[k].v[1] = data[nzw + x]; + t[k].v[2] = data[zw + nx]; + tnormal(&t[k]);++k; + t[k].v[0] = data[zw + nx]; + t[k].v[1] = data[nzw + x]; + t[k].v[2] = data[nzw + nx]; + tnormal(&t[k]);++k; + } } - } - free(data); - return new_surface(t, n, 0, 1); + free(data); + return new_surface(t, n, 0, 1); } diff --git a/rasterize.c b/rasterize.c index e2bbcf7..f1a0846 100644 --- a/rasterize.c +++ b/rasterize.c @@ -3,7 +3,7 @@ * principalement que du cas du triangle. * * CE CODE A EU COMME POINT DE DÉPART CE QUI A ÉTÉ FAIT - * EN COURS, IL EST COMPLÉTÉ PAR L'ENSEIGNANT POUR ÊTRE + * EN COURS, IL EST COMPLÉTÉ PAR L'ENSEIGNANT POUR ÊTRE * FONCTIONNEL MAIS IL DES CHOSES À AMÉLIORER. * * \author Farès BELHADJ, amsi@up8.edu @@ -33,7 +33,7 @@ static inline GLubyte red(GLuint c); static inline GLubyte green(GLuint c); static inline GLubyte blue(GLuint c); static inline GLubyte alpha(GLuint c); -static void pquit(void); +static void pquit(void); /*!\brief la texture courante à utiliser en cas de mapping de texture */ static GLuint * _tex = NULL; @@ -53,71 +53,72 @@ static int _perpective_correction = 0; /*!\brief transforme et rastérise l'ensemble des triangles de la * surface. */ void transform_n_rasterize(surface_t * s, float * model_view_matrix, float * projection_matrix) { - int i; - /* la première fois allouer le depth buffer */ - if(_depth == NULL) { - _depth = calloc(gl4dpGetWidth() * gl4dpGetHeight(), sizeof *_depth); - assert(_depth); - atexit(pquit); - } - /* si projection_matrix[15] est à 1, c'est une projection orthogonale, pas - * besoin de correction de perspective */ - _perpective_correction = projection_matrix[15] == 1.0f ? 0 : 1; - /* le viewport est fixe ; \todo peut devenir paramétrable ... */ - float viewport[] = { 0.0f, 0.0f, (float)gl4dpGetWidth(), (float)gl4dpGetHeight() }; - stransform(s, model_view_matrix, projection_matrix, viewport); - /* mettre en place la texture qui sera utilisée pour mapper la surface */ - if(s->options & SO_USE_TEXTURE) - set_texture(s->tex_id); - for(i = 0; i < s->n; ++i) { - /* si le triangle est déclaré CULL (par exemple en backface), le rejeter */ - if(s->t[i].state & PS_CULL ) continue; - /* on rejette aussi les triangles complètement out */ - if(s->t[i].state & PS_TOTALLY_OUT) continue; - /* "hack" pas terrible permettant de rejeter les triangles - * partiellement out dont au moins un sommet est TOO_FAR (trop - * éloigné). Voir le fichier transformations.c pour voir comment - * améliorer ce traitement. */ - if( s->t[i].state & PS_PARTIALLY_OUT && - ( (s->t[i].v[0].state & PS_TOO_FAR) || - (s->t[i].v[1].state & PS_TOO_FAR) || - (s->t[i].v[2].state & PS_TOO_FAR) ) ) - continue; - fill_triangle(s, &(s->t[i])); - } + int i; + /* la première fois allouer le depth buffer */ + if(_depth == NULL) { + _depth = calloc(gl4dpGetWidth() * gl4dpGetHeight(), sizeof *_depth); + assert(_depth); + atexit(pquit); + } + /* si projection_matrix[15] est à 1, c'est une projection orthogonale, pas + * besoin de correction de perspective */ + _perpective_correction = projection_matrix[15] == 1.0f ? 0 : 1; + /* le viewport est fixe ; \todo peut devenir paramétrable ... */ + float viewport[] = { 0.0f, 0.0f, (float)gl4dpGetWidth(), (float)gl4dpGetHeight() }; + stransform(s, model_view_matrix, projection_matrix, viewport); + /* mettre en place la texture qui sera utilisée pour mapper la surface */ + if(s->options & SO_USE_TEXTURE) + set_texture(s->tex_id); + for(i = 0; i < s->n; ++i) { + /* si le triangle est déclaré CULL (par exemple en backface), le rejeter */ + if(s->t[i].state & PS_CULL ) continue; + /* on rejette aussi les triangles complètement out */ + if(s->t[i].state & PS_TOTALLY_OUT) continue; + /* "hack" pas terrible permettant de rejeter les triangles + * partiellement out dont au moins un sommet est TOO_FAR (trop + * éloigné). Voir le fichier transformations.c pour voir comment + * améliorer ce traitement. */ + if( s->t[i].state & PS_PARTIALLY_OUT && + ( (s->t[i].v[0].state & PS_TOO_FAR) || + (s->t[i].v[1].state & PS_TOO_FAR) || + (s->t[i].v[2].state & PS_TOO_FAR) ) + ) + continue; + fill_triangle(s, &(s->t[i])); + } } /*!\brief effacer le buffer de profondeur (à chaque frame) pour * réaliser le z-test */ void clear_depth_map(void) { - if(_depth) { - memset(_depth, 0, gl4dpGetWidth() * gl4dpGetHeight() * sizeof *_depth); - } + if(_depth) { + memset(_depth, 0, gl4dpGetWidth() * gl4dpGetHeight() * sizeof *_depth); + } } /*!\brief met en place une texture pour être mappée sur la surface en cours */ void set_texture(GLuint screen) { - GLuint old_id = gl4dpGetTextureId(); /* au cas où */ - gl4dpSetScreen(screen); - _tex = gl4dpGetPixels(); - _texW = gl4dpGetWidth(); - _texH = gl4dpGetHeight(); - if(old_id) - gl4dpSetScreen(old_id); + GLuint old_id = gl4dpGetTextureId(); /* au cas où */ + gl4dpSetScreen(screen); + _tex = gl4dpGetPixels(); + _texW = gl4dpGetWidth(); + _texH = gl4dpGetHeight(); + if(old_id) + gl4dpSetScreen(old_id); } /*!\brief met à jour la fonction d'interpolation et de coloriage * (shadingfunc) de la surface en fonction de ses options */ void updatesfuncs(surface_t * s) { - int t; - if(s->options & SO_USE_TEXTURE) { - s->interpolatefunc = (t = s->options & SO_COLOR_MATERIAL) ? metainterpolate_all : metainterpolate_only_tex; - s->shadingfunc = (s->options & SO_USE_COLOR) ? (t ? shading_all_CM : shading_all) : shading_only_tex; - } else { - s->interpolatefunc = (t = s->options & SO_COLOR_MATERIAL) ? metainterpolate_only_color : metainterpolate_none; - s->shadingfunc = (s->options & SO_USE_COLOR) ? (t ? shading_only_color_CM : shading_only_color) : shading_none;; - } + int t; + if(s->options & SO_USE_TEXTURE) { + s->interpolatefunc = (t = s->options & SO_COLOR_MATERIAL) ? metainterpolate_all : metainterpolate_only_tex; + s->shadingfunc = (s->options & SO_USE_COLOR) ? (t ? shading_all_CM : shading_all) : shading_only_tex; + } else { + s->interpolatefunc = (t = s->options & SO_COLOR_MATERIAL) ? metainterpolate_only_color : metainterpolate_none; + s->shadingfunc = (s->options & SO_USE_COLOR) ? (t ? shading_only_color_CM : shading_only_color) : shading_none;; + } } /*!\brief fonction principale de ce fichier, elle dessine un triangle @@ -125,331 +126,331 @@ void updatesfuncs(surface_t * s) { * (interpolations bilinaires des attributs du sommet). */ inline void fill_triangle(surface_t * s, triangle_t * t) { - vertex_t * aG = NULL, * aD = NULL; - int bas, median, haut, n, signe, i, h = gl4dpGetHeight(); - if(t->v[0].y < t->v[1].y) { - if(t->v[0].y < t->v[2].y) { - bas = 0; - if(t->v[1].y < t->v[2].y) { - median = 1; - haut = 2; - } else { - median = 2; - haut = 1; - } - } else { - bas = 2; - median = 0; - haut = 1; + vertex_t * aG = NULL, * aD = NULL; + int bas, median, haut, n, signe, i, h = gl4dpGetHeight(); + if(t->v[0].y < t->v[1].y) { + if(t->v[0].y < t->v[2].y) { + bas = 0; + if(t->v[1].y < t->v[2].y) { + median = 1; + haut = 2; + } else { + median = 2; + haut = 1; + } + } else { + bas = 2; + median = 0; + haut = 1; + } + } else { /* p0 au dessus de p1 */ + if(t->v[1].y < t->v[2].y) { + bas = 1; + if(t->v[0].y < t->v[2].y) { + median = 0; + haut = 2; + } else { + median = 2; + haut = 0; + } + } else { + bas = 2; + median = 1; + haut = 0; + } } - } else { /* p0 au dessus de p1 */ - if(t->v[1].y < t->v[2].y) { - bas = 1; - if(t->v[0].y < t->v[2].y) { - median = 0; - haut = 2; - } else { - median = 2; - haut = 0; - } + n = t->v[haut].y - t->v[bas].y + 1; + aG = malloc(n * sizeof *aG); + assert(aG); + aD = malloc(n * sizeof *aD); + assert(aD); + /* est-ce que Pm est à gauche (+) ou à droite (-) de la droite (Pb->Ph) ? */ + /* idée TODO?, un produit vectoriel pourrait s'avérer mieux */ + if(t->v[haut].x == t->v[bas].x || t->v[haut].y == t->v[bas].y) { + /* eq de la droite x = t->v[haut].x; ou y = t->v[haut].y; */ + signe = (t->v[median].x > t->v[haut].x) ? -1 : 1; } else { - bas = 2; - median = 1; - haut = 0; + /* eq ax + y + c = 0 */ + float a, c, x; + a = (t->v[haut].y - t->v[bas].y) / (float)(t->v[bas].x - t->v[haut].x); + c = -a * t->v[haut].x - t->v[haut].y; + /* on trouve le x sur la droite au même y que le median et on compare */ + x = -(c + t->v[median].y) / a; + signe = (t->v[median].x >= x) ? -1 : 1; } - } - n = t->v[haut].y - t->v[bas].y + 1; - aG = malloc(n * sizeof *aG); - assert(aG); - aD = malloc(n * sizeof *aD); - assert(aD); - /* est-ce que Pm est à gauche (+) ou à droite (-) de la droite (Pb->Ph) ? */ - /* idée TODO?, un produit vectoriel pourrait s'avérer mieux */ - if(t->v[haut].x == t->v[bas].x || t->v[haut].y == t->v[bas].y) { - /* eq de la droite x = t->v[haut].x; ou y = t->v[haut].y; */ - signe = (t->v[median].x > t->v[haut].x) ? -1 : 1; - } else { - /* eq ax + y + c = 0 */ - float a, c, x; - a = (t->v[haut].y - t->v[bas].y) / (float)(t->v[bas].x - t->v[haut].x); - c = -a * t->v[haut].x - t->v[haut].y; - /* on trouve le x sur la droite au même y que le median et on compare */ - x = -(c + t->v[median].y) / a; - signe = (t->v[median].x >= x) ? -1 : 1; - } - if(signe < 0) { /* aG reçoit Ph->Pb, et aD reçoit Ph->Pm puis Pm vers Pb */ - abscisses(s, &(t->v[haut]), &(t->v[bas]), aG, 1); - abscisses(s, &(t->v[haut]), &(t->v[median]), aD, 1); - abscisses(s, &(t->v[median]), &(t->v[bas]), &aD[t->v[haut].y - t->v[median].y], 0); - } else { /* aG reçoit Ph->Pm puis Pm vers Pb, et aD reçoit Ph->Pb */ - abscisses(s, &(t->v[haut]), &(t->v[bas]), aD, 1); - abscisses(s, &(t->v[haut]), &(t->v[median]), aG, 1); - abscisses(s, &(t->v[median]), &(t->v[bas]), &aG[t->v[haut].y - t->v[median].y], 0); - } - for(i = 0; i < n; ++i) { - if( aG[i].y >= 0 && aG[i].y < h && - ( (aG[i].z >= 0 && aG[i].z <= 1) || (aD[i].z >= 0 && aD[i].z <= 1) ) ) - horizontal_line(s, &aG[i], &aD[i]); - } - free(aG); - free(aD); + if(signe < 0) { /* aG reçoit Ph->Pb, et aD reçoit Ph->Pm puis Pm vers Pb */ + abscisses(s, &(t->v[haut]), &(t->v[bas]), aG, 1); + abscisses(s, &(t->v[haut]), &(t->v[median]), aD, 1); + abscisses(s, &(t->v[median]), &(t->v[bas]), &aD[t->v[haut].y - t->v[median].y], 0); + } else { /* aG reçoit Ph->Pm puis Pm vers Pb, et aD reçoit Ph->Pb */ + abscisses(s, &(t->v[haut]), &(t->v[bas]), aD, 1); + abscisses(s, &(t->v[haut]), &(t->v[median]), aG, 1); + abscisses(s, &(t->v[median]), &(t->v[bas]), &aG[t->v[haut].y - t->v[median].y], 0); + } + for(i = 0; i < n; ++i) { + if( aG[i].y >= 0 && aG[i].y < h && + ( (aG[i].z >= 0 && aG[i].z <= 1) || (aD[i].z >= 0 && aD[i].z <= 1) ) ) + horizontal_line(s, &aG[i], &aD[i]); + } + free(aG); + free(aD); } /*!\brief utilise Br'65 pour determiner les abscisses des segments du * triangle à remplir (par \a horizontal_line). */ inline void abscisses(surface_t * s, vertex_t * p0, vertex_t * p1, vertex_t * absc, int replace) { - int u = p1->x - p0->x, v = p1->y - p0->y, pasX = u < 0 ? -1 : 1, pasY = v < 0 ? -1 : 1; - float dmax = sqrtf(u * u + v * v), p; - u = abs(u); v = abs(v); - if(u > v) { // 1er octan - if(replace) { - int objX = (u + 1) * pasX; - int delta = u - 2 * v, incH = -2 * v, incO = 2 * u - 2 * v; - for (int x = 0, y = 0, k = 0; x != objX; x += pasX) { - absc[k].x = x + p0->x; - absc[k].y = y + p0->y; - p = sqrtf(x * x + y * y) / dmax; - s->interpolatefunc(&absc[k], p0, p1, 1.0f - p, p); - if(delta < 0) { - ++k; - y += pasY; - delta += incO; - } else - delta += incH; - } - } else { - int objX = (u + 1) * pasX; - int delta = u - 2 * v, incH = -2 * v, incO = 2 * u - 2 * v; - for (int x = 0, y = 0, k = 0, done = 0; x != objX; x += pasX) { - if(!done) { - absc[k].x = x + p0->x; - absc[k].y = y + p0->y; - p = sqrtf(x * x + y * y) / dmax; - s->interpolatefunc(&absc[k], p0, p1, 1.0f - p, p); - done = 1; - } - if(delta < 0) { - ++k; - done = 0; - y += pasY; - delta += incO; - } else - delta += incH; - } + int u = p1->x - p0->x, v = p1->y - p0->y, pasX = u < 0 ? -1 : 1, pasY = v < 0 ? -1 : 1; + float dmax = sqrtf(u * u + v * v), p; + u = abs(u); v = abs(v); + if(u > v) { // 1er octan + if(replace) { + int objX = (u + 1) * pasX; + int delta = u - 2 * v, incH = -2 * v, incO = 2 * u - 2 * v; + for (int x = 0, y = 0, k = 0; x != objX; x += pasX) { + absc[k].x = x + p0->x; + absc[k].y = y + p0->y; + p = sqrtf(x * x + y * y) / dmax; + s->interpolatefunc(&absc[k], p0, p1, 1.0f - p, p); + if(delta < 0) { + ++k; + y += pasY; + delta += incO; + } else + delta += incH; + } + } else { + int objX = (u + 1) * pasX; + int delta = u - 2 * v, incH = -2 * v, incO = 2 * u - 2 * v; + for (int x = 0, y = 0, k = 0, done = 0; x != objX; x += pasX) { + if(!done) { + absc[k].x = x + p0->x; + absc[k].y = y + p0->y; + p = sqrtf(x * x + y * y) / dmax; + s->interpolatefunc(&absc[k], p0, p1, 1.0f - p, p); + done = 1; + } + if(delta < 0) { + ++k; + done = 0; + y += pasY; + delta += incO; + } else + delta += incH; + } + } + } else { // 2eme octan + int objY = (v + 1) * pasY; + int delta = v - 2 * u, incH = -2 * u, incO = 2 * v - 2 * u; + for (int x = 0, y = 0, k = 0; y != objY; y += pasY) { + absc[k].x = x + p0->x; + absc[k].y = y + p0->y; + p = sqrtf(x * x + y * y) / dmax; + s->interpolatefunc(&absc[k], p0, p1, 1.0f - p, p); + ++k; + if(delta < 0) { + x += pasX; + delta += incO; + } else + delta += incH; + } } - } else { // 2eme octan - int objY = (v + 1) * pasY; - int delta = v - 2 * u, incH = -2 * u, incO = 2 * v - 2 * u; - for (int x = 0, y = 0, k = 0; y != objY; y += pasY) { - absc[k].x = x + p0->x; - absc[k].y = y + p0->y; - p = sqrtf(x * x + y * y) / dmax; - s->interpolatefunc(&absc[k], p0, p1, 1.0f - p, p); - ++k; - if(delta < 0) { - x += pasX; - delta += incO; - } else - delta += incH; - } - } } /*!\brief remplissage par droite horizontale entre deux abscisses */ inline void horizontal_line(surface_t * s, vertex_t * vG, vertex_t * vD) { - int w = gl4dpGetWidth(), x, yw = vG->y * w; - GLuint * image = gl4dpGetPixels(); - float dmax = vD->x - vG->x, p, deltap; - vertex_t v; - /* il reste d'autres optims possibles */ - for(x = vG->x, p = 0.0f, deltap = 1.0f / dmax; x <= vD->x; ++x, p += deltap) - if(x >= 0 && x < w) { - s->interpolatefunc(&v, vG, vD, 1.0f - p, p); - if(v.z < 0 || v.z > 1 || v.z < _depth[yw + x]) { continue; } - s->shadingfunc(s, &image[yw + x], &v); - _depth[yw + x] = v.z; - } + int w = gl4dpGetWidth(), x, yw = vG->y * w; + GLuint * image = gl4dpGetPixels(); + float dmax = vD->x - vG->x, p, deltap; + vertex_t v; + /* il reste d'autres optims possibles */ + for(x = vG->x, p = 0.0f, deltap = 1.0f / dmax; x <= vD->x; ++x, p += deltap) + if(x >= 0 && x < w) { + s->interpolatefunc(&v, vG, vD, 1.0f - p, p); + if(v.z < 0 || v.z > 1 || v.z < _depth[yw + x]) { continue; } + s->shadingfunc(s, &image[yw + x], &v); + _depth[yw + x] = v.z; + } } /*!\brief aucune couleur n'est inscrite */ inline void shading_none(surface_t * s, GLuint * pcolor, vertex_t * v) { - //vide pour l'instant, à prévoir le z-buffer + //vide pour l'instant, à prévoir le z-buffer } /*!\brief la couleur du pixel est tirée uniquement de la texture */ inline void shading_only_tex(surface_t * s, GLuint * pcolor, vertex_t * v) { - int xt, yt, ct; - GLubyte r, g, b, a; - xt = (int)(v->texCoord.x * (_texW - EPSILON)); - if(xt < 0) { - xt = xt % (-_texW); - while(xt < 0) xt += _texW; - } else - xt = xt % _texW; - yt = (int)(v->texCoord.y * (_texH - EPSILON)); - if(yt < 0) { - yt = yt % (-_texH); - while(yt < 0) yt += _texH; - } else - yt = yt % _texH; - ct = yt * _texW + xt; - *pcolor = _tex[yt * _texW + xt]; - r = (GLubyte)( red(_tex[ct]) * v->li); - g = (GLubyte)(green(_tex[ct]) * v->li); - b = (GLubyte)( blue(_tex[ct]) * v->li); - a = (GLubyte) alpha(_tex[ct]); - *pcolor = rgba(r, g, b, a); + int xt, yt, ct; + GLubyte r, g, b, a; + xt = (int)(v->texCoord.x * (_texW - EPSILON)); + if(xt < 0) { + xt = xt % (-_texW); + while(xt < 0) xt += _texW; + } else + xt = xt % _texW; + yt = (int)(v->texCoord.y * (_texH - EPSILON)); + if(yt < 0) { + yt = yt % (-_texH); + while(yt < 0) yt += _texH; + } else + yt = yt % _texH; + ct = yt * _texW + xt; + *pcolor = _tex[yt * _texW + xt]; + r = (GLubyte)( red(_tex[ct]) * v->li); + g = (GLubyte)(green(_tex[ct]) * v->li); + b = (GLubyte)( blue(_tex[ct]) * v->li); + a = (GLubyte) alpha(_tex[ct]); + *pcolor = rgba(r, g, b, a); } /*!\brief la couleur du pixel est tirée de la couleur interpolée */ inline void shading_only_color_CM(surface_t * s, GLuint * pcolor, vertex_t * v) { - GLubyte r, g, b, a; - r = (GLubyte)(v->li * v->icolor.x * (255 + EPSILON)); - g = (GLubyte)(v->li * v->icolor.y * (255 + EPSILON)); - b = (GLubyte)(v->li * v->icolor.z * (255 + EPSILON)); - a = (GLubyte)(v->icolor.w * (255 + EPSILON)); - *pcolor = rgba(r, g, b, a); + GLubyte r, g, b, a; + r = (GLubyte)(v->li * v->icolor.x * (255 + EPSILON)); + g = (GLubyte)(v->li * v->icolor.y * (255 + EPSILON)); + b = (GLubyte)(v->li * v->icolor.z * (255 + EPSILON)); + a = (GLubyte)(v->icolor.w * (255 + EPSILON)); + *pcolor = rgba(r, g, b, a); } /*!\brief la couleur du pixel est tirée de la couleur diffuse de la * surface */ inline void shading_only_color(surface_t * s, GLuint * pcolor, vertex_t * v) { - GLubyte r, g, b, a; - r = (GLubyte)(v->li * s->dcolor.x * (255 + EPSILON)); - g = (GLubyte)(v->li * s->dcolor.y * (255 + EPSILON)); - b = (GLubyte)(v->li * s->dcolor.z * (255 + EPSILON)); - a = (GLubyte)(s->dcolor.w * (255 + EPSILON)); - *pcolor = rgba(r, g, b, a); + GLubyte r, g, b, a; + r = (GLubyte)(v->li * s->dcolor.x * (255 + EPSILON)); + g = (GLubyte)(v->li * s->dcolor.y * (255 + EPSILON)); + b = (GLubyte)(v->li * s->dcolor.z * (255 + EPSILON)); + a = (GLubyte)(s->dcolor.w * (255 + EPSILON)); + *pcolor = rgba(r, g, b, a); } /*!\brief la couleur du pixel est le produit de la couleur interpolée * et de la texture */ inline void shading_all_CM(surface_t * s, GLuint * pcolor, vertex_t * v) { - GLubyte r, g, b, a; - int xt, yt, ct; - xt = (int)(v->texCoord.x * (_texW - EPSILON)); - if(xt < 0) { - xt = xt % (-_texW); - while(xt < 0) xt += _texW; - } else - xt = xt % _texW; - yt = (int)(v->texCoord.y * (_texH - EPSILON)); - if(yt < 0) { - yt = yt % (-_texH); - while(yt < 0) yt += _texH; - } else - yt = yt % _texH; - ct = yt * _texW + xt; - r = (GLubyte)(( red(_tex[ct]) + EPSILON) * v->li * v->icolor.x); - g = (GLubyte)((green(_tex[ct]) + EPSILON) * v->li * v->icolor.y); - b = (GLubyte)(( blue(_tex[ct]) + EPSILON) * v->li * v->icolor.z); - a = (GLubyte)((alpha(_tex[ct]) + EPSILON) * v->icolor.w); - *pcolor = rgba(r, g, b, a); + GLubyte r, g, b, a; + int xt, yt, ct; + xt = (int)(v->texCoord.x * (_texW - EPSILON)); + if(xt < 0) { + xt = xt % (-_texW); + while(xt < 0) xt += _texW; + } else + xt = xt % _texW; + yt = (int)(v->texCoord.y * (_texH - EPSILON)); + if(yt < 0) { + yt = yt % (-_texH); + while(yt < 0) yt += _texH; + } else + yt = yt % _texH; + ct = yt * _texW + xt; + r = (GLubyte)(( red(_tex[ct]) + EPSILON) * v->li * v->icolor.x); + g = (GLubyte)((green(_tex[ct]) + EPSILON) * v->li * v->icolor.y); + b = (GLubyte)(( blue(_tex[ct]) + EPSILON) * v->li * v->icolor.z); + a = (GLubyte)((alpha(_tex[ct]) + EPSILON) * v->icolor.w); + *pcolor = rgba(r, g, b, a); } /*!\brief la couleur du pixel est le produit de la couleur diffuse * de la surface et de la texture */ inline void shading_all(surface_t * s, GLuint * pcolor, vertex_t * v) { - GLubyte r, g, b, a; - int xt, yt, ct; - xt = (int)(v->texCoord.x * (_texW - EPSILON)); - if(xt < 0) { - xt = xt % (-_texW); - while(xt < 0) xt += _texW; - } else - xt = xt % _texW; - yt = (int)(v->texCoord.y * (_texH - EPSILON)); - if(yt < 0) { - yt = yt % (-_texH); - while(yt < 0) yt += _texH; - } else - yt = yt % _texH; - ct = yt * _texW + xt; - r = (GLubyte)(( red(_tex[ct]) + EPSILON) * v->li * s->dcolor.x); - g = (GLubyte)((green(_tex[ct]) + EPSILON) * v->li * s->dcolor.y); - b = (GLubyte)(( blue(_tex[ct]) + EPSILON) * v->li * s->dcolor.z); - a = (GLubyte)((alpha(_tex[ct]) + EPSILON) * s->dcolor.w); - *pcolor = rgba(r, g, b, a); + GLubyte r, g, b, a; + int xt, yt, ct; + xt = (int)(v->texCoord.x * (_texW - EPSILON)); + if(xt < 0) { + xt = xt % (-_texW); + while(xt < 0) xt += _texW; + } else + xt = xt % _texW; + yt = (int)(v->texCoord.y * (_texH - EPSILON)); + if(yt < 0) { + yt = yt % (-_texH); + while(yt < 0) yt += _texH; + } else + yt = yt % _texH; + ct = yt * _texW + xt; + r = (GLubyte)(( red(_tex[ct]) + EPSILON) * v->li * s->dcolor.x); + g = (GLubyte)((green(_tex[ct]) + EPSILON) * v->li * s->dcolor.y); + b = (GLubyte)(( blue(_tex[ct]) + EPSILON) * v->li * s->dcolor.z); + a = (GLubyte)((alpha(_tex[ct]) + EPSILON) * s->dcolor.w); + *pcolor = rgba(r, g, b, a); } /*!\brief interpolation de plusieurs floattants (entre \a s et \a e) * de la structure vertex_t en utilisant \a a et \a b, les - * facteurs \a fa et \a fb, le tout dans \a r + * facteurs \a fa et \a fb, le tout dans \a r * \todo un pointeur de fonction pour éviter un test s'il faut * un _perpective_correction != 0 ??? */ inline void interpolate(vertex_t * r, vertex_t * a, vertex_t * b, float fa, float fb, int s, int e) { - int i; - float * pr = (float *)&(r->texCoord); - float * pa = (float *)&(a->texCoord); - float * pb = (float *)&(b->texCoord); - /* Correction de l'interpolation par rapport à la perspective, le z - * joue un rôle dans les distances, il est nécessaire de le - * réintégrer en modifiant les facteurs de proportion. - * lien utile : https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes - */ - if(_perpective_correction) { - float z = 1.0f / (fa / a->zmod + fb / b->zmod); - if(e == 8) { /* attention il faut que cet indice colle avec la position de la proriété z à partir de l'adresse texCoord */ - pr[e] = fa * pa[e] + fb * pb[e]; - e = 7; + int i; + float * pr = (float *)&(r->texCoord); + float * pa = (float *)&(a->texCoord); + float * pb = (float *)&(b->texCoord); + /* Correction de l'interpolation par rapport à la perspective, le z + * joue un rôle dans les distances, il est nécessaire de le + * réintégrer en modifiant les facteurs de proportion. + * lien utile : https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes + */ + if(_perpective_correction) { + float z = 1.0f / (fa / a->zmod + fb / b->zmod); + if(e == 8) { /* attention il faut que cet indice colle avec la position de la proriété z à partir de l'adresse texCoord */ + pr[e] = fa * pa[e] + fb * pb[e]; + e = 7; + } + fa = z * fa / a->zmod; fb = z * fb / b->zmod; } - fa = z * fa / a->zmod; fb = z * fb / b->zmod; - } - for(i = s; i <= e; ++i) - pr[i] = fa * pa[i] + fb * pb[i]; + for(i = s; i <= e; ++i) + pr[i] = fa * pa[i] + fb * pb[i]; } /*!\brief meta-fonction pour appeler \a interpolate, demande * uniquement l'interpolation des z */ inline void metainterpolate_none(vertex_t * r, vertex_t * a, vertex_t * b, float fa, float fb) { - interpolate(r, a, b, fa, fb, 6, 8); + interpolate(r, a, b, fa, fb, 6, 8); } /*!\brief meta-fonction pour appeler \a interpolate, demande * uniquement l'interpolation des coord. de texture et les z */ inline void metainterpolate_only_tex(vertex_t * r, vertex_t * a, vertex_t * b, float fa, float fb) { - interpolate(r, a, b, fa, fb, 0, 1); - interpolate(r, a, b, fa, fb, 6, 8); + interpolate(r, a, b, fa, fb, 0, 1); + interpolate(r, a, b, fa, fb, 6, 8); } /*!\brief meta-fonction pour appeler \a interpolate, demande * uniquement l'interpolation des couleurs et les z */ inline void metainterpolate_only_color(vertex_t * r, vertex_t * a, vertex_t * b, float fa, float fb) { - interpolate(r, a, b, fa, fb, 2, 8); + interpolate(r, a, b, fa, fb, 2, 8); } /*!\brief meta-fonction pour appeler \a interpolate, demande * l'interpolation de l'ensemble des attributs */ inline void metainterpolate_all(vertex_t * r, vertex_t * a, vertex_t * b, float fa, float fb) { - interpolate(r, a, b, fa, fb, 0, 8); + interpolate(r, a, b, fa, fb, 0, 8); } GLuint rgba(GLubyte r, GLubyte g, GLubyte b, GLubyte a) { - return RGBA(r, g, b, a); + return RGBA(r, g, b, a); } GLubyte red(GLuint c) { - return RED(c); + return RED(c); } GLubyte green(GLuint c) { - return GREEN(c); + return GREEN(c); } GLubyte blue(GLuint c) { - return BLUE(c); + return BLUE(c); } GLubyte alpha(GLuint c) { - return ALPHA(c); + return ALPHA(c); } /*!\brief au moment de quitter le programme désallouer la mémoire * utilisée pour _depth */ void pquit(void) { - if(_depth) { - free(_depth); - _depth = NULL; - } + if(_depth) { + free(_depth); + _depth = NULL; + } } diff --git a/rasterize.h b/rasterize.h index 3313a97..637f20d 100644 --- a/rasterize.h +++ b/rasterize.h @@ -1,10 +1,10 @@ -/*!\file rasterize.h +/*!\file rasterize.h * * \brief structures de données et protos de fonctions externes pour * réaliser un moteur de rendu DIY par rastérisation. * * \author Farès BELHADJ, amsi@up8.edu - * \date November 17, 2021. + * \date November 17, 2021. */ #ifndef RASTERIZE_H_SEEN @@ -19,72 +19,72 @@ # ifdef __cplusplus extern "C" { # endif - - typedef enum pstate_t pstate_t; - typedef enum soptions_t soptions_t; - typedef struct vec4 vec4; - typedef struct vec3 vec3; - typedef struct vec2 vec2; - typedef struct vertex_t vertex_t; - typedef struct triangle_t triangle_t; - typedef struct surface_t surface_t; - /*!\brief états pour les sommets ou les triangles */ - enum pstate_t { - PS_NONE = 0, - PS_TOTALLY_OUT = 1, - PS_PARTIALLY_OUT = 2, - PS_CULL = 4, /* si en BACKFACE et que - SO_CULL_BACKFACES est actif */ - PS_TOO_FAR = 8, - PS_OUT_LEFT = 16, - PS_OUT_RIGHT = 32, - PS_OUT_BOTTOM = 64, - PS_OUT_TOP = 128, - PS_OUT_NEAR = 256, - PS_OUT_FAR = 512 - }; +typedef enum pstate_t pstate_t; +typedef enum soptions_t soptions_t; +typedef struct vec4 vec4; +typedef struct vec3 vec3; +typedef struct vec2 vec2; +typedef struct vertex_t vertex_t; +typedef struct triangle_t triangle_t; +typedef struct surface_t surface_t; - /*!\brief options pour les surfaces */ - enum soptions_t { - SO_NONE = 0, /* la surface n'a pas de rendu - "couleur" */ - SO_USE_TEXTURE = 1, /* utiliser la texture pour - colorer (multiplication si - SO_USE_COLOR est actif) */ - SO_USE_COLOR = 2, /* utiliser la couleur de la - surface ou des sommets pour - colorer (multiplication si - SO_USE_TEXTURE est actif) */ - SO_COLOR_MATERIAL = 4, /* utiliser la couleur aux - sommets si actif - (nécessite aussi - l'activation de - SO_USE_COLOR) */ - SO_CULL_BACKFACES = 8, /* active le fait de cacher - les faces arrières */ - SO_USE_LIGHTING = 16, /* active le calcul d'ombre - propre (Gouraud sur - diffus) */ - SO_DEFAULT = SO_CULL_BACKFACES | SO_USE_COLOR /* comportement - par - défaut */ - }; +/*!\brief états pour les sommets ou les triangles */ +enum pstate_t { + PS_NONE = 0, + PS_TOTALLY_OUT = 1, + PS_PARTIALLY_OUT = 2, + PS_CULL = 4, /* si en BACKFACE et que + SO_CULL_BACKFACES est actif */ + PS_TOO_FAR = 8, + PS_OUT_LEFT = 16, + PS_OUT_RIGHT = 32, + PS_OUT_BOTTOM = 64, + PS_OUT_TOP = 128, + PS_OUT_NEAR = 256, + PS_OUT_FAR = 512 +}; - struct vec4 { +/*!\brief options pour les surfaces */ +enum soptions_t { + SO_NONE = 0, /* la surface n'a pas de rendu + "couleur" */ + SO_USE_TEXTURE = 1, /* utiliser la texture pour + colorer (multiplication si + SO_USE_COLOR est actif) */ + SO_USE_COLOR = 2, /* utiliser la couleur de la + surface ou des sommets pour + colorer (multiplication si + SO_USE_TEXTURE est actif) */ + SO_COLOR_MATERIAL = 4, /* utiliser la couleur aux + sommets si actif + (nécessite aussi + l'activation de + SO_USE_COLOR) */ + SO_CULL_BACKFACES = 8, /* active le fait de cacher + les faces arrières */ + SO_USE_LIGHTING = 16, /* active le calcul d'ombre + propre (Gouraud sur + diffus) */ + SO_DEFAULT = SO_CULL_BACKFACES | SO_USE_COLOR /* comportement + par + défaut */ +}; + +struct vec4 { float x /* r */, y/* g */, z /* b */, w /* a */; - }; +}; - struct vec2 { - float x /* s */, y /* t */; - }; +struct vec2 { + float x /* s */, y /* t */; +}; - struct vec3 { +struct vec3 { float x /* r */, y/* g */, z/* b */; - }; +}; - /*!\brief le sommet et l'ensemble de ses attributs */ - struct vertex_t { +/*!\brief le sommet et l'ensemble de ses attributs */ +struct vertex_t { vec4 position; vec4 color0; /* début des données à partir desquelles on peut interpoler en masse */ @@ -92,68 +92,68 @@ extern "C" { vec4 icolor; /* couleur à interpoler */ float li; /* intensité de lumière (lambertien) */ float zmod; /* z après modelview, sert à corriger - l'interpolation par rapport à une projection en - perspective */ + l'interpolation par rapport à une projection en + perspective */ float z; /* ce z représente la depth */ /* fin des données à partir desquelles on peut interpoler */ vec3 normal; /* interpolez les normales si vous implémentez Phong */ int x, y; enum pstate_t state; - }; +}; - /*!\brief le triangle */ - struct triangle_t { +/*!\brief le triangle */ +struct triangle_t { vertex_t v[3]; - vec3 normal; + vec3 normal; enum pstate_t state; - }; +}; - /*!\brief la surface englobe plusieurs triangles et des options - * telles que le type de rendu, la couleur diffuse ou la texture. - */ - struct surface_t { +/*!\brief la surface englobe plusieurs triangles et des options + * telles que le type de rendu, la couleur diffuse ou la texture. + */ +struct surface_t { int n; triangle_t * t; GLuint tex_id; vec4 dcolor; /* couleur diffuse, ajoutez une couleur ambiante et - spéculaire si vous souhaitez compléter le - modèle */ + spéculaire si vous souhaitez compléter le + modèle */ soptions_t options; /* paramétrage du rendu de la surface */ void (*interpolatefunc)(vertex_t *, vertex_t *, vertex_t *, float, float); void (*shadingfunc)(surface_t *, GLuint *, vertex_t *); - }; - - /* dans rasterize.c */ - extern void transform_n_rasterize(surface_t * s, float * model_view_matrix, float * projection_matrix); - extern void clear_depth_map(void); - extern void set_texture(GLuint screen); - extern void updatesfuncs(surface_t * s); + }; - /* dans vtranform.c */ - extern vertex_t vtransform(surface_t * s, vertex_t v, float * model_view_matrix, float * ti_model_view_matrix, float * projection_matrix, float * viewport); - extern void stransform(surface_t * s, float * model_view_matrix, float * projection_matrix, float * viewport); - extern void mult_matrix(float * res, float * m); - extern void translate(float * m, float tx, float ty, float tz); - extern void rotate(float * m, float angle, float x, float y, float z); - extern void scale(float * m, float sx, float sy, float sz); - extern void lookAt(float * m, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ); - - /* dans surface.c */ - extern void tnormal(triangle_t * t); - extern void snormals(surface_t * s); - extern void tnormals2vertices(surface_t * s); - extern void set_texture_id(surface_t * s, GLuint tex_id); - extern void set_diffuse_color(surface_t * s, vec4 dcolor); - extern void enable_surface_option(surface_t * s, soptions_t option); - extern void disable_surface_option(surface_t * s, soptions_t option); - extern surface_t * new_surface(triangle_t * t, int n, int duplicateTriangles, int hasNormals); - extern void free_surface(surface_t * s); - extern GLuint get_texture_from_BMP(const char * filename); +/* dans rasterize.c */ +extern void transform_n_rasterize(surface_t * s, float * model_view_matrix, float * projection_matrix); +extern void clear_depth_map(void); +extern void set_texture(GLuint screen); +extern void updatesfuncs(surface_t * s); - /* dans geometry.c */ - extern surface_t * mk_quad(void); - extern surface_t * mk_cube(void); - extern surface_t * mk_sphere(int longitudes, int latitudes); +/* dans vtranform.c */ +extern vertex_t vtransform(surface_t * s, vertex_t v, float * model_view_matrix, float * ti_model_view_matrix, float * projection_matrix, float * viewport); +extern void stransform(surface_t * s, float * model_view_matrix, float * projection_matrix, float * viewport); +extern void mult_matrix(float * res, float * m); +extern void translate(float * m, float tx, float ty, float tz); +extern void rotate(float * m, float angle, float x, float y, float z); +extern void scale(float * m, float sx, float sy, float sz); +extern void lookAt(float * m, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ); + +/* dans surface.c */ +extern void tnormal(triangle_t * t); +extern void snormals(surface_t * s); +extern void tnormals2vertices(surface_t * s); +extern void set_texture_id(surface_t * s, GLuint tex_id); +extern void set_diffuse_color(surface_t * s, vec4 dcolor); +extern void enable_surface_option(surface_t * s, soptions_t option); +extern void disable_surface_option(surface_t * s, soptions_t option); +extern surface_t * new_surface(triangle_t * t, int n, int duplicateTriangles, int hasNormals); +extern void free_surface(surface_t * s); +extern GLuint get_texture_from_BMP(const char * filename); + +/* dans geometry.c */ +extern surface_t * mk_quad(void); +extern surface_t * mk_cube(void); +extern surface_t * mk_sphere(int longitudes, int latitudes); # ifdef __cplusplus } # endif diff --git a/surface.c b/surface.c index 41065fd..c9a59c2 100644 --- a/surface.c +++ b/surface.c @@ -13,14 +13,14 @@ /*!\brief calcule le vecteur normal à un triangle */ void tnormal(triangle_t * t) { vec3 u = { - t->v[1].position.x - t->v[0].position.x, - t->v[1].position.y - t->v[0].position.y, - t->v[1].position.z - t->v[0].position.z + t->v[1].position.x - t->v[0].position.x, + t->v[1].position.y - t->v[0].position.y, + t->v[1].position.z - t->v[0].position.z }; vec3 v = { - t->v[2].position.x - t->v[0].position.x, - t->v[2].position.y - t->v[0].position.y, - t->v[2].position.z - t->v[0].position.z + t->v[2].position.x - t->v[0].position.x, + t->v[2].position.y - t->v[0].position.y, + t->v[2].position.z - t->v[0].position.z }; MVEC3CROSS((float *)&(t->normal), (float *)&u, (float *)&v); MVEC3NORMALIZE((float *)&(t->normal)); @@ -28,40 +28,40 @@ void tnormal(triangle_t * t) { /*!\brief calcule les vecteurs normaux aux triangles de la surface */ void snormals(surface_t * s) { - int i; - for(i = 0; i < s->n; ++i) - tnormal(&(s->t[i])); + int i; + for(i = 0; i < s->n; ++i) + tnormal(&(s->t[i])); } /*!\brief affecte les normales aux triangles de la surface à ses vertices */ void tnormals2vertices(surface_t * s) { - int i; - for(i = 0; i < s->n; ++i) - s->t[i].v[0].normal = s->t[i].v[1].normal = s->t[i].v[2].normal = s->t[i].normal; + int i; + for(i = 0; i < s->n; ++i) + s->t[i].v[0].normal = s->t[i].v[1].normal = s->t[i].v[2].normal = s->t[i].normal; } /*!\brief affecte l'identifiant de texture de la surface */ void set_texture_id(surface_t * s, GLuint tex_id) { - s->tex_id = tex_id; + s->tex_id = tex_id; } /*!\brief affecte la couleur diffuse de la surface */ void set_diffuse_color(surface_t * s, vec4 dcolor) { - s->dcolor = dcolor; + s->dcolor = dcolor; } /*!\brief active une option de la surface */ void enable_surface_option(surface_t * s, soptions_t option) { - if(!(s->options & option)) - s->options |= option; - updatesfuncs(s); + if(!(s->options & option)) + s->options |= option; + updatesfuncs(s); } /*!\brief désactive une option de la surface */ void disable_surface_option(surface_t * s, soptions_t option) { - if(s->options & option) - s->options ^= option; - updatesfuncs(s); + if(s->options & option) + s->options ^= option; + updatesfuncs(s); } /*!\brief créé et renvoie une surface (allouée) à partir de \a n @@ -74,53 +74,53 @@ void disable_surface_option(surface_t * s, soptions_t option) { * faux (0) elle force le calcul des normales par triangle et les * affecte aux sommets. */ surface_t * new_surface(triangle_t * t, int n, int duplicate_triangles, int has_normals) { - const vec4 dcolor = { 0.42f, 0.1f, 0.1f, 1.0f }; - surface_t * s = malloc(1 * sizeof *s); - assert(s); - s->n = n; - if(duplicate_triangles) { - s->t = malloc(s->n * sizeof *(s->t)); - assert(s->t); - memcpy(s->t, t, s->n * sizeof *(s->t)); - } else - s->t = t; - set_diffuse_color(s, dcolor); - s->options = SO_DEFAULT; - s->tex_id = 0; - updatesfuncs(s); - if(!has_normals) { - snormals(s); - tnormals2vertices(s); - } - return s; + const vec4 dcolor = { 0.42f, 0.1f, 0.1f, 1.0f }; + surface_t * s = malloc(1 * sizeof *s); + assert(s); + s->n = n; + if(duplicate_triangles) { + s->t = malloc(s->n * sizeof *(s->t)); + assert(s->t); + memcpy(s->t, t, s->n * sizeof *(s->t)); + } else + s->t = t; + set_diffuse_color(s, dcolor); + s->options = SO_DEFAULT; + s->tex_id = 0; + updatesfuncs(s); + if(!has_normals) { + snormals(s); + tnormals2vertices(s); + } + return s; } /*!\brief libère la mémoire utilisée par la surface */ void free_surface(surface_t * s) { - free(s->t); - free(s); + free(s->t); + free(s); } /*!\brief charge et fabrique un identifiant pour une texture issue * d'un fichier BMP */ GLuint get_texture_from_BMP(const char * filename) { - GLuint id, old_id; - /* chargement d'une image dans une surface SDL */ - SDL_Surface * s = SDL_LoadBMP(filename); - assert(s); - old_id = gl4dpGetTextureId(); /* au cas où */ - /* création d'un screen GL4Dummies aux dimensions de la texture */ - id = gl4dpInitScreenWithDimensions(s->w, s->h); - /* copie de la surface SDL vers le screen en cours */ - { - GLuint * p = gl4dpGetPixels(); - SDL_Surface * d = SDL_CreateRGBSurface(0, s->w, s->h, 32, R_MASK, G_MASK, B_MASK, A_MASK); - SDL_BlitSurface(s, NULL, d, NULL); - memcpy(p, d->pixels, d->w * d->h * sizeof *p); - SDL_FreeSurface(d); - } - /* libération de la surface SDL */ - SDL_FreeSurface(s); - if(old_id) - gl4dpSetScreen(old_id); - return id; + GLuint id, old_id; + /* chargement d'une image dans une surface SDL */ + SDL_Surface * s = SDL_LoadBMP(filename); + assert(s); + old_id = gl4dpGetTextureId(); /* au cas où */ + /* création d'un screen GL4Dummies aux dimensions de la texture */ + id = gl4dpInitScreenWithDimensions(s->w, s->h); + /* copie de la surface SDL vers le screen en cours */ + { + GLuint * p = gl4dpGetPixels(); + SDL_Surface * d = SDL_CreateRGBSurface(0, s->w, s->h, 32, R_MASK, G_MASK, B_MASK, A_MASK); + SDL_BlitSurface(s, NULL, d, NULL); + memcpy(p, d->pixels, d->w * d->h * sizeof *p); + SDL_FreeSurface(d); + } + /* libération de la surface SDL */ + SDL_FreeSurface(s); + if(old_id) + gl4dpSetScreen(old_id); + return id; } diff --git a/vtransform.c b/vtransform.c index 22fe596..e113a79 100644 --- a/vtransform.c +++ b/vtransform.c @@ -20,59 +20,59 @@ static inline void clip2_unit_cube(triangle_t * t); matrice de model-view \a model_view_matrix et de projection \a projection_matrix. \a ti_model_view_matrix est la transposée de l'inverse de la matrice \a model_view_matrix.*/ vertex_t vtransform(surface_t * s, vertex_t v, float * model_view_matrix, float * ti_model_view_matrix, float * projection_matrix, float * viewport) { - float dist = 1.0f; - vec4 r1, r2; - v.state = PS_NONE; - MMAT4XVEC4((float *)&r1, model_view_matrix, (float *)&(v.position)); - MMAT4XVEC4((float *)&r2, projection_matrix, (float *)&r1); - r2.x /= r2.w; - r2.y /= r2.w; - r2.z /= r2.w; - r2.w = 1.0f; - /* dist doit être à 1 ci-après */ - if(r2.x < -dist) v.state |= PS_OUT_LEFT; - if(r2.x > dist) v.state |= PS_OUT_RIGHT; - if(r2.y < -dist) v.state |= PS_OUT_BOTTOM; - if(r2.y > dist) v.state |= PS_OUT_TOP; - if(r2.z < -dist) v.state |= PS_OUT_NEAR; - if(r2.z > dist) v.state |= PS_OUT_FAR; - /* "hack" pas terrible permettant d'éviter les gros triangles - partiellement hors-champ. Modifier dist pour jouer sur la taille - (une fois projetés) des triangles qu'on laisse passer (plus c'est - gros plus c'est lent avec les gros triangles). La "vraie" - solution est obtenue en calculant l'intersection exacte entre le - triangle et le cube unitaire ; attention, ceci produit - potentiellement une nouvelle liste de triangles à chaque frame, - et les attributs des sommets doivent être recalculés. */ - dist = 10.0f; - if(r2.x < -dist || r2.x > dist || r2.y < -dist || r2.y > dist || r2.z < -dist || r2.z > dist) { - v.state |= PS_TOO_FAR; + float dist = 1.0f; + vec4 r1, r2; + v.state = PS_NONE; + MMAT4XVEC4((float *)&r1, model_view_matrix, (float *)&(v.position)); + MMAT4XVEC4((float *)&r2, projection_matrix, (float *)&r1); + r2.x /= r2.w; + r2.y /= r2.w; + r2.z /= r2.w; + r2.w = 1.0f; + /* dist doit être à 1 ci-après */ + if(r2.x < -dist) v.state |= PS_OUT_LEFT; + if(r2.x > dist) v.state |= PS_OUT_RIGHT; + if(r2.y < -dist) v.state |= PS_OUT_BOTTOM; + if(r2.y > dist) v.state |= PS_OUT_TOP; + if(r2.z < -dist) v.state |= PS_OUT_NEAR; + if(r2.z > dist) v.state |= PS_OUT_FAR; + /* "hack" pas terrible permettant d'éviter les gros triangles + partiellement hors-champ. Modifier dist pour jouer sur la taille + (une fois projetés) des triangles qu'on laisse passer (plus c'est + gros plus c'est lent avec les gros triangles). La "vraie" + solution est obtenue en calculant l'intersection exacte entre le + triangle et le cube unitaire ; attention, ceci produit + potentiellement une nouvelle liste de triangles à chaque frame, + et les attributs des sommets doivent être recalculés. */ + dist = 10.0f; + if(r2.x < -dist || r2.x > dist || r2.y < -dist || r2.y > dist || r2.z < -dist || r2.z > dist) { + v.state |= PS_TOO_FAR; + return v; + } + /* Gouraud */ + if(s->options & SO_USE_LIGHTING) { + /* la lumière est positionnelle et fixe dans la scène. \todo dans + scene.c la rendre modifiable, voire aussi pouvoir la placer par + rapport aux objets (elle subirait la matrice modèle). */ + const vec4 lp[1] = { {0.0f, 0.0f, 1.0f} }; + vec4 ld = {lp[0].x - r1.x, lp[0].y - r1.y, lp[0].z - r1.z, lp[0].w - r1.w}; + float n[4] = {v.normal.x, v.normal.y, v.normal.z, 0.0f}, res[4]; + MMAT4XVEC4(res, ti_model_view_matrix, n); + MVEC3NORMALIZE(res); + MVEC3NORMALIZE((float *)&ld); + v.li = MVEC3DOT(res, (float *)&ld); + v.li = MIN(MAX(0.0f, v.li), 1.0f); + } else + v.li = 1.0f; + v.icolor = v.color0; + /* Mapping du cube unitaire vers l'écran */ + v.x = viewport[0] + ((r2.x + 1.0f) * 0.5f) * (viewport[2] - EPSILON); + v.y = viewport[1] + ((r2.y + 1.0f) * 0.5f) * (viewport[3] - EPSILON); + v.z = pow((-r2.z + 1.0f) * 0.5f, 0.5); + /* sinon pour near = 0.1f et far = 10.0f on peut rendre non linéaire la depth avec */ + /* v.z = 1.0f - (1.0f / r2.z - 1.0f / 0.1f) / (1.0f / 10.0f - 1.0f / 0.1f); */ + v.zmod = r1.z; return v; - } - /* Gouraud */ - if(s->options & SO_USE_LIGHTING) { - /* la lumière est positionnelle et fixe dans la scène. \todo dans - scene.c la rendre modifiable, voire aussi pouvoir la placer par - rapport aux objets (elle subirait la matrice modèle). */ - const vec4 lp[1] = { {0.0f, 0.0f, 1.0f} }; - vec4 ld = {lp[0].x - r1.x, lp[0].y - r1.y, lp[0].z - r1.z, lp[0].w - r1.w}; - float n[4] = {v.normal.x, v.normal.y, v.normal.z, 0.0f}, res[4]; - MMAT4XVEC4(res, ti_model_view_matrix, n); - MVEC3NORMALIZE(res); - MVEC3NORMALIZE((float *)&ld); - v.li = MVEC3DOT(res, (float *)&ld); - v.li = MIN(MAX(0.0f, v.li), 1.0f); - } else - v.li = 1.0f; - v.icolor = v.color0; - /* Mapping du cube unitaire vers l'écran */ - v.x = viewport[0] + ((r2.x + 1.0f) * 0.5f) * (viewport[2] - EPSILON); - v.y = viewport[1] + ((r2.y + 1.0f) * 0.5f) * (viewport[3] - EPSILON); - v.z = pow((-r2.z + 1.0f) * 0.5f, 0.5); - /* sinon pour near = 0.1f et far = 10.0f on peut rendre non linéaire la depth avec */ - /* v.z = 1.0f - (1.0f / r2.z - 1.0f / 0.1f) / (1.0f / 10.0f - 1.0f / 0.1f); */ - v.zmod = r1.z; - return v; } /*!\brief projette le triangle \a t à l'écran (\a W x \a H) selon la @@ -82,132 +82,132 @@ vertex_t vtransform(surface_t * s, vertex_t v, float * model_view_matrix, float * surface. Elle utilise aussi \a clip2_unit_cube pour connaître l'état * du triangle par rapport au cube unitaire. * - * \see vtransform + * \see vtransform * \see clip2_unit_cube */ void stransform(surface_t * s, float * model_view_matrix, float * projection_matrix, float * viewport) { - int i, j; - float ti_model_view_matrix[16]; - triangle_t vcull; - /* calcul de la transposée de l'inverse de la matrice model-view - pour la transformation des normales et le calcul du lambertien - utilisé par le shading Gouraud dans vtransform. */ - memcpy(ti_model_view_matrix, model_view_matrix, sizeof ti_model_view_matrix); - MMAT4INVERSE(ti_model_view_matrix); - MMAT4TRANSPOSE(ti_model_view_matrix); - for(i = 0; i < s->n; ++i) { - s->t[i].state = PS_NONE; - for(j = 0; j < 3; ++j) { - s->t[i].v[j] = vtransform(s, s->t[i].v[j], model_view_matrix, ti_model_view_matrix, projection_matrix, viewport); - if(s->options & SO_CULL_BACKFACES) { - vcull.v[j].position.x = s->t[i].v[j].x; - vcull.v[j].position.y = s->t[i].v[j].y; - vcull.v[j].position.z = 0.0f; - } + int i, j; + float ti_model_view_matrix[16]; + triangle_t vcull; + /* calcul de la transposée de l'inverse de la matrice model-view + pour la transformation des normales et le calcul du lambertien + utilisé par le shading Gouraud dans vtransform. */ + memcpy(ti_model_view_matrix, model_view_matrix, sizeof ti_model_view_matrix); + MMAT4INVERSE(ti_model_view_matrix); + MMAT4TRANSPOSE(ti_model_view_matrix); + for(i = 0; i < s->n; ++i) { + s->t[i].state = PS_NONE; + for(j = 0; j < 3; ++j) { + s->t[i].v[j] = vtransform(s, s->t[i].v[j], model_view_matrix, ti_model_view_matrix, projection_matrix, viewport); + if(s->options & SO_CULL_BACKFACES) { + vcull.v[j].position.x = s->t[i].v[j].x; + vcull.v[j].position.y = s->t[i].v[j].y; + vcull.v[j].position.z = 0.0f; + } + } + if(s->options & SO_CULL_BACKFACES) { + tnormal(&vcull); + if(vcull.normal.z <= 0.0f) { + s->t[i].state |= PS_CULL; + continue; + } + } + clip2_unit_cube(&(s->t[i])); } - if(s->options & SO_CULL_BACKFACES) { - tnormal(&vcull); - if(vcull.normal.z <= 0.0f) { - s->t[i].state |= PS_CULL; - continue; - } - } - clip2_unit_cube(&(s->t[i])); - } } /*!\brief multiplie deux matrices : \a res = \a res x \a m */ void mult_matrix(float * res, float * m) { - /* res = res x m */ - float cpy[16]; - memcpy(cpy, res, sizeof cpy); - MMAT4XMAT4(res, cpy, m); + /* res = res x m */ + float cpy[16]; + memcpy(cpy, res, sizeof cpy); + MMAT4XMAT4(res, cpy, m); } /*!\brief ajoute (multiplication droite) une translation à la matrice * \a m */ void translate(float * m, float tx, float ty, float tz) { - float mat[] = { 1.0f, 0.0f, 0.0f, tx, - 0.0f, 1.0f, 0.0f, ty, - 0.0f, 0.0f, 1.0f, tz, - 0.0f, 0.0f, 0.0f, 1.0f }; - mult_matrix(m, mat); + float mat[] = { 1.0f, 0.0f, 0.0f, tx, + 0.0f, 1.0f, 0.0f, ty, + 0.0f, 0.0f, 1.0f, tz, + 0.0f, 0.0f, 0.0f, 1.0f }; + mult_matrix(m, mat); } /*!\brief ajoute (multiplication droite) une rotation à la matrice \a * m */ void rotate(float * m, float angle, float x, float y, float z) { - float n = sqrtf(x * x + y * y + z * z); - if ( n > 0.0f ) { - float a, s, c, cc, x2, y2, z2, xy, yz, zx, xs, ys, zs; - float mat[] = { 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - s = sinf ( a = (angle * (float)M_PI / 180.0f) ); - cc = 1.0f - (c = cosf ( a )); - x /= n; y /= n; z /= n; - x2 = x * x; y2 = y * y; z2 = z * z; - xy = x * y; yz = y * z; zx = z * x; - xs = x * s; ys = y * s; zs = z * s; - mat[0] = (cc * x2) + c; - mat[1] = (cc * xy) - zs; - mat[2] = (cc * zx) + ys; - /* mat[3] = 0.0f; */ - mat[4] = (cc * xy) + zs; - mat[5] = (cc * y2) + c; - mat[6] = (cc * yz) - xs; - /* mat[7] = 0.0f; */ - mat[8] = (cc * zx) - ys; - mat[9] = (cc * yz) + xs; - mat[10] = (cc * z2) + c; - /* mat[11] = 0.0f; */ - /* mat[12] = 0.0f; mat[= 0.0f; mat[14] = 0.0f; mat[15] = 1.0f; */ - mult_matrix(m, mat); - } + float n = sqrtf(x * x + y * y + z * z); + if ( n > 0.0f ) { + float a, s, c, cc, x2, y2, z2, xy, yz, zx, xs, ys, zs; + float mat[] = { 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + s = sinf ( a = (angle * (float)M_PI / 180.0f) ); + cc = 1.0f - (c = cosf ( a )); + x /= n; y /= n; z /= n; + x2 = x * x; y2 = y * y; z2 = z * z; + xy = x * y; yz = y * z; zx = z * x; + xs = x * s; ys = y * s; zs = z * s; + mat[0] = (cc * x2) + c; + mat[1] = (cc * xy) - zs; + mat[2] = (cc * zx) + ys; + /* mat[3] = 0.0f; */ + mat[4] = (cc * xy) + zs; + mat[5] = (cc * y2) + c; + mat[6] = (cc * yz) - xs; + /* mat[7] = 0.0f; */ + mat[8] = (cc * zx) - ys; + mat[9] = (cc * yz) + xs; + mat[10] = (cc * z2) + c; + /* mat[11] = 0.0f; */ + /* mat[12] = 0.0f; mat[= 0.0f; mat[14] = 0.0f; mat[15] = 1.0f; */ + mult_matrix(m, mat); + } } /*!\brief ajoute (multiplication droite) un scale à la matrice \a m */ void scale(float * m, float sx, float sy, float sz) { - float mat[] = { sx , 0.0f, 0.0f, 0.0f, - 0.0f, sy, 0.0f, 0.0f, - 0.0f, 0.0f, sz, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - mult_matrix(m, mat); + float mat[] = { sx , 0.0f, 0.0f, 0.0f, + 0.0f, sy, 0.0f, 0.0f, + 0.0f, 0.0f, sz, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + mult_matrix(m, mat); } /*!\brief simule une free camera, voir la doc de gluLookAt */ void lookAt(float * m, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ) { - float forward[3], side[3], up[3]; - float mat[] = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - forward[0] = centerX - eyeX; - forward[1] = centerY - eyeY; - forward[2] = centerZ - eyeZ; - up[0] = upX; - up[1] = upY; - up[2] = upZ; - MVEC3NORMALIZE(forward); - /* side = forward x up */ - MVEC3CROSS(side, forward, up); - MVEC3NORMALIZE(side); - /* up = side x forward */ - MVEC3CROSS(up, side, forward); - mat[0] = side[0]; - mat[1] = side[1]; - mat[2] = side[2]; - mat[4] = up[0]; - mat[5] = up[1]; - mat[6] = up[2]; - mat[8] = -forward[0]; - mat[9] = -forward[1]; - mat[10] = -forward[2]; - mult_matrix(m, mat); - translate(m, -eyeX, -eyeY, -eyeZ); + float forward[3], side[3], up[3]; + float mat[] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + forward[0] = centerX - eyeX; + forward[1] = centerY - eyeY; + forward[2] = centerZ - eyeZ; + up[0] = upX; + up[1] = upY; + up[2] = upZ; + MVEC3NORMALIZE(forward); + /* side = forward x up */ + MVEC3CROSS(side, forward, up); + MVEC3NORMALIZE(side); + /* up = side x forward */ + MVEC3CROSS(up, side, forward); + mat[0] = side[0]; + mat[1] = side[1]; + mat[2] = side[2]; + mat[4] = up[0]; + mat[5] = up[1]; + mat[6] = up[2]; + mat[8] = -forward[0]; + mat[9] = -forward[1]; + mat[10] = -forward[2]; + mult_matrix(m, mat); + translate(m, -eyeX, -eyeY, -eyeZ); } /*!\brief intersection triangle-cube unitaire, à compléter (voir le @@ -215,18 +215,18 @@ void lookAt(float * m, float eyeX, float eyeY, float eyeZ, float centerX, float void clip2_unit_cube(triangle_t * t) { int i, oleft = 0, oright = 0, obottom = 0, otop = 0, onear = 0, ofar = 0; for (i = 0; i < 3; ++i) { - if(t->v[i].state & PS_OUT_LEFT) ++oleft; - if(t->v[i].state & PS_OUT_RIGHT) ++oright; - if(t->v[i].state & PS_OUT_BOTTOM) ++obottom; - if(t->v[i].state & PS_OUT_TOP) ++otop; - if(t->v[i].state & PS_OUT_NEAR) ++onear; - if(t->v[i].state & PS_OUT_FAR) ++ofar; + if(t->v[i].state & PS_OUT_LEFT) ++oleft; + if(t->v[i].state & PS_OUT_RIGHT) ++oright; + if(t->v[i].state & PS_OUT_BOTTOM) ++obottom; + if(t->v[i].state & PS_OUT_TOP) ++otop; + if(t->v[i].state & PS_OUT_NEAR) ++onear; + if(t->v[i].state & PS_OUT_FAR) ++ofar; } if(!(oleft | oright | obottom | otop | onear | ofar)) - return; + return; if(oleft == 3 || oright == 3 || obottom == 3 || otop == 3 || onear == 3 || ofar == 3) { - t->state |= PS_TOTALLY_OUT; - return; + t->state |= PS_TOTALLY_OUT; + return; } t->state |= PS_PARTIALLY_OUT; /* le cas PARTIALLY_OUT n'est pas réellement géré. Il serait diff --git a/window.c b/window.c index 1bc24c5..528206c 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,4 @@ -/*!\file window.c +/*!\file window.c * \brief Utilisation du raster DIY comme pipeline de rendu 3D. Cet * exemple montre les géométries disponibles et quelques * transformations dessus. @@ -42,172 +42,172 @@ static float _ycam = 3.0f; /*!\brief paramètre l'application et lance la boucle infinie. */ int main(int argc, char ** argv) { - /* tentative de création d'une fenêtre pour GL4Dummies */ - if(!gl4duwCreateWindow(argc, argv, /* args du programme */ - "The DIY Rasterizer", /* titre */ - 10, 10, 800, 600, /* 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; - } - init(); - /* mettre en place la fonction d'interception clavier */ - gl4duwKeyDownFunc(key); - /* 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; + /* tentative de création d'une fenêtre pour GL4Dummies */ + if(!gl4duwCreateWindow(argc, argv, /* args du programme */ + "The DIY Rasterizer", /* titre */ + 10, 10, 800, 600, /* 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; + } + init(); + /* mettre en place la fonction d'interception clavier */ + gl4duwKeyDownFunc(key); + /* 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) { - GLuint id; - vec4 r = {1, 0, 0, 1}, g = {0, 1, 0, 1}, b = {0, 0, 1, 1}; - /* création d'un screen GL4Dummies (texture dans laquelle nous - * pouvons dessiner) aux dimensions de la fenêtre. IMPORTANT de - * créer le screen avant d'utiliser les fonctions liées au - * textures */ - gl4dpInitScreen(); - /* Pour forcer la désactivation de la synchronisation verticale */ - SDL_GL_SetSwapInterval(0); - /* on créé nos trois type de surfaces */ - _quad = mk_quad(); /* ça fait 2 triangles */ - _cube = mk_cube(); /* ça fait 2x6 triangles */ - _sphere = mk_sphere(12, 12); /* ça fait 12x12x2 trianles ! */ - /* on change les couleurs de surfaces */ - _quad->dcolor = r; _cube->dcolor = b; _sphere->dcolor = g; - /* on leur rajoute à toutes la même texture */ - id = get_texture_from_BMP("images/tex.bmp"); - set_texture_id( _quad, id); - set_texture_id( _cube, id); - set_texture_id(_sphere, id); - /* si _use_tex != 0, on active l'utilisation de la texture pour les - * trois */ - if(_use_tex) { - enable_surface_option( _quad, SO_USE_TEXTURE); - enable_surface_option( _cube, SO_USE_TEXTURE); - enable_surface_option(_sphere, SO_USE_TEXTURE); - } - /* si _use_lighting != 0, on active l'ombrage */ - if(_use_lighting) { - enable_surface_option( _quad, SO_USE_LIGHTING); - enable_surface_option( _cube, SO_USE_LIGHTING); - enable_surface_option(_sphere, SO_USE_LIGHTING); - } - /* on désactive le back cull face pour le quadrilatère, ainsi on - * peut voir son arrière quand le lighting est inactif */ - disable_surface_option(_quad, SO_CULL_BACKFACES); - /* mettre en place la fonction à appeler en cas de sortie */ - atexit(sortie); + GLuint id; + vec4 r = {1, 0, 0, 1}, g = {0, 1, 0, 1}, b = {0, 0, 1, 1}; + /* création d'un screen GL4Dummies (texture dans laquelle nous + * pouvons dessiner) aux dimensions de la fenêtre. IMPORTANT de + * créer le screen avant d'utiliser les fonctions liées au + * textures */ + gl4dpInitScreen(); + /* Pour forcer la désactivation de la synchronisation verticale */ + SDL_GL_SetSwapInterval(0); + /* on créé nos trois type de surfaces */ + _quad = mk_quad(); /* ça fait 2 triangles */ + _cube = mk_cube(); /* ça fait 2x6 triangles */ + _sphere = mk_sphere(12, 12); /* ça fait 12x12x2 trianles ! */ + /* on change les couleurs de surfaces */ + _quad->dcolor = r; _cube->dcolor = b; _sphere->dcolor = g; + /* on leur rajoute à toutes la même texture */ + id = get_texture_from_BMP("images/tex.bmp"); + set_texture_id( _quad, id); + set_texture_id( _cube, id); + set_texture_id(_sphere, id); + /* si _use_tex != 0, on active l'utilisation de la texture pour les + * trois */ + if(_use_tex) { + enable_surface_option( _quad, SO_USE_TEXTURE); + enable_surface_option( _cube, SO_USE_TEXTURE); + enable_surface_option(_sphere, SO_USE_TEXTURE); + } + /* si _use_lighting != 0, on active l'ombrage */ + if(_use_lighting) { + enable_surface_option( _quad, SO_USE_LIGHTING); + enable_surface_option( _cube, SO_USE_LIGHTING); + enable_surface_option(_sphere, SO_USE_LIGHTING); + } + /* on désactive le back cull face pour le quadrilatère, ainsi on + * peut voir son arrière quand le lighting est inactif */ + disable_surface_option(_quad, SO_CULL_BACKFACES); + /* mettre en place la fonction à appeler en cas de sortie */ + atexit(sortie); } /*!\brief la fonction appelée à chaque display. */ void draw(void) { - static float a = 0.0f; - float model_view_matrix[16], projection_matrix[16], nmv[16]; - /* effacer l'écran et le buffer de profondeur */ - gl4dpClearScreen(); - clear_depth_map(); - /* 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 projection_matrix */ - MFRUSTUM(projection_matrix, -0.05f, 0.05f, -0.05f, 0.05f, 0.1f, 1000.0f); - /* charger la matrice identité dans model-view */ - MIDENTITY(model_view_matrix); - /* on place la caméra en arrière-haut, elle regarde le centre de la scène */ - lookAt(model_view_matrix, 0, _ycam, 10, 0, 0, 0, 0, 1, 0); - /* le quadrilatère est mis à gauche et tourne autour de son axe x */ - memcpy(nmv, model_view_matrix, sizeof nmv); /* copie model_view_matrix dans nmv */ - translate(nmv, -3.0f, 0.0f, 0.0f); - rotate(nmv, a, 1.0f, 0.0f, 0.0f); - transform_n_rasterize(_quad, nmv, projection_matrix); - /* le cube est mis à droite et tourne autour de son axe z */ - memcpy(nmv, model_view_matrix, sizeof nmv); /* copie model_view_matrix dans nmv */ - translate(nmv, 3.0f, 0.0f, 0.0f); - rotate(nmv, a, 0.0f, 0.0f, 1.0f); - transform_n_rasterize(_cube, nmv, projection_matrix); - /* la sphère est laissée au centre et tourne autour de son axe y */ - memcpy(nmv, model_view_matrix, sizeof nmv); /* copie model_view_matrix dans nmv */ - rotate(nmv, a, 0.0f, 1.0f, 0.0f); - transform_n_rasterize(_sphere, nmv, projection_matrix); - /* déclarer qu'on a changé des pixels du screen (en bas niveau) */ - gl4dpScreenHasChanged(); - /* fonction permettant de raffraîchir l'ensemble de la fenêtre*/ - gl4dpUpdateScreen(NULL); - a += 0.1f; + static float a = 0.0f; + float model_view_matrix[16], projection_matrix[16], nmv[16]; + /* effacer l'écran et le buffer de profondeur */ + gl4dpClearScreen(); + clear_depth_map(); + /* 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 projection_matrix */ + MFRUSTUM(projection_matrix, -0.05f, 0.05f, -0.05f, 0.05f, 0.1f, 1000.0f); + /* charger la matrice identité dans model-view */ + MIDENTITY(model_view_matrix); + /* on place la caméra en arrière-haut, elle regarde le centre de la scène */ + lookAt(model_view_matrix, 0, _ycam, 10, 0, 0, 0, 0, 1, 0); + /* le quadrilatère est mis à gauche et tourne autour de son axe x */ + memcpy(nmv, model_view_matrix, sizeof nmv); /* copie model_view_matrix dans nmv */ + translate(nmv, -3.0f, 0.0f, 0.0f); + rotate(nmv, a, 1.0f, 0.0f, 0.0f); + transform_n_rasterize(_quad, nmv, projection_matrix); + /* le cube est mis à droite et tourne autour de son axe z */ + memcpy(nmv, model_view_matrix, sizeof nmv); /* copie model_view_matrix dans nmv */ + translate(nmv, 3.0f, 0.0f, 0.0f); + rotate(nmv, a, 0.0f, 0.0f, 1.0f); + transform_n_rasterize(_cube, nmv, projection_matrix); + /* la sphère est laissée au centre et tourne autour de son axe y */ + memcpy(nmv, model_view_matrix, sizeof nmv); /* copie model_view_matrix dans nmv */ + rotate(nmv, a, 0.0f, 1.0f, 0.0f); + transform_n_rasterize(_sphere, nmv, projection_matrix); + /* déclarer qu'on a changé des pixels du screen (en bas niveau) */ + gl4dpScreenHasChanged(); + /* fonction permettant de raffraîchir l'ensemble de la fenêtre*/ + gl4dpUpdateScreen(NULL); + a += 0.1f; } /*!\brief intercepte l'événement clavier pour modifier les options. */ void key(int keycode) { - switch(keycode) { - case GL4DK_UP: - _ycam += 0.05f; - break; - case GL4DK_DOWN: - _ycam -= 0.05f; - break; - case GL4DK_t: /* 't' la texture */ - _use_tex = !_use_tex; - if(_use_tex) { - enable_surface_option( _quad, SO_USE_TEXTURE); - enable_surface_option( _cube, SO_USE_TEXTURE); - enable_surface_option(_sphere, SO_USE_TEXTURE); - } else { - disable_surface_option( _quad, SO_USE_TEXTURE); - disable_surface_option( _cube, SO_USE_TEXTURE); - disable_surface_option(_sphere, SO_USE_TEXTURE); + switch(keycode) { + case GL4DK_UP: + _ycam += 0.05f; + break; + case GL4DK_DOWN: + _ycam -= 0.05f; + break; + case GL4DK_t: /* 't' la texture */ + _use_tex = !_use_tex; + if(_use_tex) { + enable_surface_option( _quad, SO_USE_TEXTURE); + enable_surface_option( _cube, SO_USE_TEXTURE); + enable_surface_option(_sphere, SO_USE_TEXTURE); + } else { + disable_surface_option( _quad, SO_USE_TEXTURE); + disable_surface_option( _cube, SO_USE_TEXTURE); + disable_surface_option(_sphere, SO_USE_TEXTURE); + } + break; + case GL4DK_c: /* 'c' utiliser la couleur */ + _use_color = !_use_color; + if(_use_color) { + enable_surface_option( _quad, SO_USE_COLOR); + enable_surface_option( _cube, SO_USE_COLOR); + enable_surface_option(_sphere, SO_USE_COLOR); + } else { + disable_surface_option( _quad, SO_USE_COLOR); + disable_surface_option( _cube, SO_USE_COLOR); + disable_surface_option(_sphere, SO_USE_COLOR); + } + break; + case GL4DK_l: /* 'l' utiliser l'ombrage par la méthode Gouraud */ + _use_lighting = !_use_lighting; + if(_use_lighting) { + enable_surface_option( _quad, SO_USE_LIGHTING); + enable_surface_option( _cube, SO_USE_LIGHTING); + enable_surface_option(_sphere, SO_USE_LIGHTING); + } else { + disable_surface_option( _quad, SO_USE_LIGHTING); + disable_surface_option( _cube, SO_USE_LIGHTING); + disable_surface_option(_sphere, SO_USE_LIGHTING); + } + break; + default: break; } - break; - case GL4DK_c: /* 'c' utiliser la couleur */ - _use_color = !_use_color; - if(_use_color) { - enable_surface_option( _quad, SO_USE_COLOR); - enable_surface_option( _cube, SO_USE_COLOR); - enable_surface_option(_sphere, SO_USE_COLOR); - } else { - disable_surface_option( _quad, SO_USE_COLOR); - disable_surface_option( _cube, SO_USE_COLOR); - disable_surface_option(_sphere, SO_USE_COLOR); - } - break; - case GL4DK_l: /* 'l' utiliser l'ombrage par la méthode Gouraud */ - _use_lighting = !_use_lighting; - if(_use_lighting) { - enable_surface_option( _quad, SO_USE_LIGHTING); - enable_surface_option( _cube, SO_USE_LIGHTING); - enable_surface_option(_sphere, SO_USE_LIGHTING); - } else { - disable_surface_option( _quad, SO_USE_LIGHTING); - disable_surface_option( _cube, SO_USE_LIGHTING); - disable_surface_option(_sphere, SO_USE_LIGHTING); - } - break; - default: break; - } } /*!\brief à appeler à la sortie du programme. */ void sortie(void) { - /* on libère nos trois surfaces */ - if(_quad) { - free_surface(_quad); - _quad = NULL; - } - if(_cube) { - free_surface(_cube); - _cube = NULL; - } - if(_sphere) { - free_surface(_sphere); - _sphere = NULL; - } - /* libère tous les objets produits par GL4Dummies, ici - * principalement les screen */ - gl4duClean(GL4DU_ALL); + /* on libère nos trois surfaces */ + if(_quad) { + free_surface(_quad); + _quad = NULL; + } + if(_cube) { + free_surface(_cube); + _cube = NULL; + } + if(_sphere) { + free_surface(_sphere); + _sphere = NULL; + } + /* libère tous les objets produits par GL4Dummies, ici + * principalement les screen */ + gl4duClean(GL4DU_ALL); }