diff --git a/Makefile b/Makefile index 9b21116..3e5114f 100644 --- a/Makefile +++ b/Makefile @@ -84,5 +84,5 @@ clean: rm -f *.err src/**/*.err cleanall: - $(MAKE) clean - $(MAKE) all \ No newline at end of file + @$(MAKE) clean + @$(MAKE) all \ No newline at end of file diff --git a/WORKDOC.md b/WORKDOC.md index 17a5e7a..e434a6f 100644 --- a/WORKDOC.md +++ b/WORKDOC.md @@ -14,7 +14,7 @@ Affichage : **VGA Mode 13h** — 320×200, 256 couleurs, rendu Gouraud en virgul src/ main/main.c Point d'entrée, scène, boucle principale part3D/ - defines.h Types, macros fixed-point, prototypes, externs + defines.h Types, macros fixed-point, constantes écran, prototypes, externs engine.c Maths (sin/cos), matrices, pipeline de rendu geometry.c Génération de meshes (sphère, cylindre) + I/O mesh graph.c VGA init, palette, backbuffer/zbuffer @@ -33,11 +33,11 @@ Makefile Build via WSL + wcc386 Windows natif init_engine_math() Précalcul sin/cos (tables entières 16.16) add_sphere / add_cylinder Génération mesh en espace local (0,0,0) ─── Boucle principale ─── - kbhit / getch Gestion clavier + kbhit / getch Gestion clavier (touches étendues consommées en deux appels) mat3_rotate_x/y Construction matrices de rotation - mat3_mul (orbite) Composition de la matrice d'orbite + mat3_mul (orbite) Composition de la matrice d'orbite (sûre contre l'aliasing) → obj.rot = orbit Application uniforme à tous les objets - → obj.pos = orbit × base_pos + center Rotation des positions + → obj.pos = orbit × base_pos + center Rotation 3D complète des positions clear_buffers Effacement backbuffer + zbuffer (0xFFFF) render_universe Transformation + projection + Gouraud fill flip memcpy backbuffer → 0xA0000 (VGA) @@ -56,6 +56,20 @@ add_sphere / add_cylinder Génération mesh en espace local (0,0,0) --- +## Constantes écran + +Définies dans `defines.h` : + +```c +#define SCREEN_W 320 +#define SCREEN_H 200 +#define SCREEN_PIXELS (SCREEN_W * SCREEN_H) /* 64 000 */ +``` + +Utilisées partout dans `graph.c` et `raster.c` à la place du littéral `64000`. + +--- + ## Palette VGA 256 entrées organisées en **16 couleurs × 16 niveaux d'intensité**. @@ -98,16 +112,19 @@ La lumière est assimilée à la direction de vue (−Z monde). Le dot product d ```c nz = f_mul(n->x, rot.m[0][2]) + f_mul(n->y, rot.m[1][2]) + f_mul(n->z, rot.m[2][2]); -intensity = (nz >= 0) ? int_to_f(1) : ((-nz) * 14) + int_to_f(1); +intensity = (nz >= 0) ? int_to_f(1) : f_mul(-nz, int_to_f(14)) + int_to_f(1); ``` +`f_mul(-nz, int_to_f(14))` est une multiplication en virgule fixe 16.16 (≠ multiplication entière). + | nz | Signification | Intensité (shade) | |---|---|---| | −1.0 | Face directement vers caméra | 15 (max) | | 0 | Face tangentielle | 1 (ambiant) | | > 0 | Face opposée à la caméra | 1 (ambiant, face culléé) | -L'intensité est interpolée en virgule fixe 16.16 entre les sommets du triangle. +L'intensité est interpolée en virgule fixe 16.16 entre les sommets du triangle. +Le shade final est clampé dans [0, 15] — y compris vers le bas (intensité négative → 0). --- @@ -149,15 +166,17 @@ Les objets ont des positions de base fixes dans l'espace scène, définies par r | Sphère (universe[0]) | (+60, 0, 0) | | Cylindre (universe[1]) | (−60, 0, 0) | -Chaque frame, la matrice d'orbite `orbit = rotX(anglex) × rotY(angley)` est calculée et appliquée uniformément : +Chaque frame, la matrice d'orbite `orbit = rotX(anglex) × rotY(angley)` est calculée et appliquée uniformément à chaque objet (transform 3D complet sur `base_pos`) : ```c -universe[o].rot = orbit; -universe[o].pos.x = f_mul(base_x, orbit.m[0][0]); -universe[o].pos.y = f_mul(base_x, orbit.m[0][1]); -universe[o].pos.z = int_to_f(300) + f_mul(base_x, orbit.m[0][2]); +obj->rot = orbit; +obj->pos.x = f_mul(bx, orbit.m[0][0]) + f_mul(by, orbit.m[1][0]) + f_mul(bz, orbit.m[2][0]); +obj->pos.y = f_mul(bx, orbit.m[0][1]) + f_mul(by, orbit.m[1][1]) + f_mul(bz, orbit.m[2][1]); +obj->pos.z = int_to_f(300) + f_mul(bx, orbit.m[0][2]) + f_mul(by, orbit.m[1][2]) + f_mul(bz, orbit.m[2][2]); ``` +Les trois composantes de `base_pos` (bx, by, bz) sont toutes prises en compte — les objets à `base_pos.y != 0` ou `base_pos.z != 0` sont correctement transformés. + La caméra reste à l'origine (+ translations clavier). L'effet visuel est celui d'un observateur qui orbite autour de la scène. --- @@ -171,9 +190,12 @@ La caméra reste à l'origine (+ translations clavier). L'effet visuel est celui | A / E | Caméra haut / bas (axe Y) | | + / − | Orbite vertical (anglex ±3°) | | / / * | Orbite horizontal (angley ±3°) | +| 1 / 2 / 3 | Mode fil-de-fer / caché / solide | | R | Reset caméra + angles | | Esc | Quitter | +Les touches étendues (F1–F12, flèches…) retournent `0` depuis `getch()` ; le scan code suivant est consommé sans effet. + --- ## Points de vigilance @@ -184,4 +206,7 @@ La caméra reste à l'origine (+ translations clavier). L'effet visuel est celui | Caméra sans rotation | La caméra n'a pas de matrice de vue — elle regarde toujours en +Z. L'orbite est simulée en faisant tourner la scène. Ajouter une matrice de vue si un vrai look-around est nécessaire. | | Clamp pz | pz clampé à int_to_f(20). Si un objet passe derrière la caméra (pz < 0 avant clamp), il sera projeté incorrectement. | | Modes fil-de-fer | MODE_WIRE, MODE_HIDDEN opérationnels. Le champ `face.force_wire` permet de forcer le fil-de-fer face par face en MODE_SOLID. | +| Z-test | `scanline_gouraud_c` et `draw_line_z` utilisent tous les deux `zv < zbuf` (strict), garantissant un comportement cohérent. | +| mat3_mul | Sûre contre l'aliasing (res peut être identique à a ou b). | | I/O mesh | `save_mesh` / `load_mesh` / `free_mesh` disponibles dans `geometry.c` mais non utilisées depuis `main.c`. | +| Face.color | Le champ `face.color` a été supprimé : la couleur est portée par `obj->color`. `face.force_wire` reste le seul flag par-face. | diff --git a/src/main/main.c b/src/main/main.c index a8e3753..2aaf06d 100644 --- a/src/main/main.c +++ b/src/main/main.c @@ -1,129 +1,96 @@ #include "../part3D/defines.h" -int main() { - Mesh m_sphere, m_cyl; - Object3D universe[2]; - Camera cam; - Matrix3 rotX, rotY, orbit; - int anglex = 0, angley = 0, key = 0; +int main(void) { + Universe *uni; + Object3D *sph, *cyl; + Camera cam; + Matrix3 rotX, rotY, orbit; + int anglex, angley, key, render, o; + fixed bx, by, bz; + + anglex = angley = key = 0; + render = MODE_SOLID; init_engine_math(); - /* Allocation intégrale des Mesh */ - m_sphere.verts = (Vector3*)malloc(MAX_VERTEX * sizeof(Vector3)); - m_sphere.v_normals = (Vector3*)malloc(MAX_VERTEX * sizeof(Vector3)); - m_sphere.faces = (Face*)malloc(MAX_FACES * sizeof(Face)); - m_sphere.num_verts = m_sphere.num_faces = 0; + /* Un seul pool pour toute la scène */ + uni = (Universe*)malloc(sizeof(Universe)); + if (!uni) return 1; + uni->num_verts = uni->num_faces = uni->num_objects = 0; - m_cyl.verts = (Vector3*)malloc(MAX_VERTEX * sizeof(Vector3)); - m_cyl.v_normals = (Vector3*)malloc(MAX_VERTEX * sizeof(Vector3)); - m_cyl.faces = (Face*)malloc(MAX_FACES * sizeof(Face)); - m_cyl.num_verts = m_cyl.num_faces = 0; + /* --- Ajout des objets : géométrie locale en (0,0,0) --- */ + sph = add_sphere(uni, 0, 0, 0, int_to_f(30), 8, COLOR5, 0); + if (!sph) { free(uni); return 1; } + sph->base_pos.x = int_to_f(60); /* offset par rapport au centre d'orbite (0,0,300) */ + sph->base_pos.y = 0; + sph->base_pos.z = 0; + sph->color = COLOR4; /* jaune */ - if (!m_sphere.verts || !m_sphere.v_normals || !m_sphere.faces || - !m_cyl.verts || !m_cyl.v_normals || !m_cyl.faces) return 1; + cyl = add_cylinder(uni, 0, 0, 0, int_to_f(20), int_to_f(60), 12, COLOR14, 0); + if (!cyl) { free(uni); return 1; } + cyl->base_pos.x = int_to_f(-60); + cyl->base_pos.y = 0; + cyl->base_pos.z = 0; + cyl->color = COLOR2; /* vert */ - /* Génération en 0,0,0 (Espace Local) */ - add_sphere(&m_sphere, 0, 0, 0, int_to_f(30), 12, COLOR5, 0); - add_cylinder(&m_cyl, 0, 0, 0, int_to_f(20), int_to_f(60), 16, COLOR14, 0); + /* Pour ajouter un troisième objet : */ + /* + Object3D *sph2 = add_sphere(uni, 0, 0, 0, int_to_f(15), 6, COLOR6, 0); + sph2->base_pos.x = 0; sph2->base_pos.y = int_to_f(60); sph2->base_pos.z = 0; + sph2->color = COLOR6; + */ - /* Configuration des objets dans l'univers */ - universe[0].mesh = &m_sphere; - universe[0].pos.x = int_to_f(60); universe[0].pos.y = 0; universe[0].pos.z = int_to_f(300); - universe[0].color = 4; /* Jaune */ - mat3_identity(&universe[0].rot); + cam.pos.x = cam.pos.y = cam.pos.z = 0; - universe[1].mesh = &m_cyl; - universe[1].pos.x = int_to_f(-60); universe[1].pos.y = 0; universe[1].pos.z = int_to_f(300); - universe[1].color = 2; /* Vert */ - mat3_identity(&universe[1].rot); - - /* Caméra initiale */ - cam.pos.x = 0; cam.pos.y = 0; cam.pos.z = 0; - - init_vga(); + if (init_vga() != 0) { free(uni); return 1; } while (key != 27) { if (kbhit()) { key = getch(); - switch (key) { - case 'Z': - case 'z': - cam.pos.z += int_to_f(10); - break; - - case 'S': - case 's': - cam.pos.z -= int_to_f(10); - break; - - case 'Q': - case 'q': - cam.pos.x -= int_to_f(10); - break; - - case 'D': - case 'd': - cam.pos.x += int_to_f(10); - break; - - case 'A': - case 'a': - cam.pos.y -= int_to_f(10); - break; - - case 'E': - case 'e': - cam.pos.y += int_to_f(10); - break; - - case 'R': - case 'r': - cam.pos.x = cam.pos.y = cam.pos.z = 0; - anglex = angley = 0; - break; - - case '+': - anglex = (anglex + 3) % 360; - break; - - case '-': - anglex = ((anglex - 3) % 360 + 360) % 360; - break; - - case '/': - angley = (angley + 3) % 360; - break; - - case '*': - angley = ((angley - 3) % 360 + 360) % 360; - break; - - } + if (key == 0) { + getch(); /* touche étendue (F1-F12, flèches…) : consommer le scan code */ + } else { + switch (key) { + case 'Z': case 'z': cam.pos.z += int_to_f(10); break; + case 'S': case 's': cam.pos.z -= int_to_f(10); break; + case 'Q': case 'q': cam.pos.x -= int_to_f(10); break; + case 'D': case 'd': cam.pos.x += int_to_f(10); break; + case 'A': case 'a': cam.pos.y -= int_to_f(10); break; + case 'E': case 'e': cam.pos.y += int_to_f(10); break; + case 'R': case 'r': cam.pos.x = cam.pos.y = cam.pos.z = 0; anglex = angley = 0; break; + case '+': anglex = (anglex + 3) % 360; break; + case '-': anglex = ((anglex - 3) % 360 + 360) % 360; break; + case '/': angley = (angley + 3) % 360; break; + case '*': angley = ((angley - 3) % 360 + 360) % 360; break; + case '1': render = MODE_WIRE; break; + case '2': render = MODE_HIDDEN; break; + case '3': render = MODE_SOLID; break; + } + } } - /* Orbite : rotation de la scène entière autour du centre (0,0,300) */ + /* Orbite : rotation de la scène autour du centre (0,0,300) */ mat3_rotate_x(&rotX, anglex); mat3_rotate_y(&rotY, angley); mat3_mul(&orbit, &rotX, &rotY); - universe[0].rot = orbit; - universe[0].pos.x = f_mul(int_to_f( 60), orbit.m[0][0]); - universe[0].pos.y = f_mul(int_to_f( 60), orbit.m[0][1]); - universe[0].pos.z = int_to_f(300) + f_mul(int_to_f( 60), orbit.m[0][2]); - - universe[1].rot = orbit; - universe[1].pos.x = f_mul(int_to_f(-60), orbit.m[0][0]); - universe[1].pos.y = f_mul(int_to_f(-60), orbit.m[0][1]); - universe[1].pos.z = int_to_f(300) + f_mul(int_to_f(-60), orbit.m[0][2]); + for (o = 0; o < uni->num_objects; o++) { + Object3D *obj = &uni->objects[o]; + bx = obj->base_pos.x; + by = obj->base_pos.y; + bz = obj->base_pos.z; + obj->rot = orbit; + obj->pos.x = f_mul(bx, orbit.m[0][0]) + f_mul(by, orbit.m[1][0]) + f_mul(bz, orbit.m[2][0]); + obj->pos.y = f_mul(bx, orbit.m[0][1]) + f_mul(by, orbit.m[1][1]) + f_mul(bz, orbit.m[2][1]); + obj->pos.z = int_to_f(300) + f_mul(bx, orbit.m[0][2]) + f_mul(by, orbit.m[1][2]) + f_mul(bz, orbit.m[2][2]); + } clear_buffers(0); - render_universe(universe, 2, &cam, MODE_SOLID); + render_universe(uni, &cam, render); flip(); } close_vga(); - free(m_sphere.verts); free(m_sphere.v_normals); free(m_sphere.faces); - free(m_cyl.verts); free(m_cyl.v_normals); free(m_cyl.faces); + free(uni); return 0; } diff --git a/src/part3D/defines.h b/src/part3D/defines.h index 682b0b3..4f81193 100644 --- a/src/part3D/defines.h +++ b/src/part3D/defines.h @@ -12,8 +12,13 @@ #define MODE_HIDDEN 1 #define MODE_SOLID 2 +#define SCREEN_W 320 +#define SCREEN_H 200 +#define SCREEN_PIXELS (SCREEN_W * SCREEN_H) + #define MAX_VERTEX 5000 #define MAX_FACES 8000 +#define MAX_OBJECTS 64 typedef struct { uint8_t r, g, b; } RGB; @@ -44,23 +49,36 @@ static __inline fixed f_div(fixed a, fixed b) { if (b == 0) return 0; return (fi typedef struct { fixed m[3][3]; } Matrix3; typedef struct { fixed x, y, z; } Vector3; -typedef struct { int a, b, c; uint8_t color; uint8_t force_wire; } Face; -typedef struct { int x, y; fixed z; int intensity; } Point2D; -typedef struct { Vector3 *verts; Vector3 *v_normals; Face *faces; int num_verts, num_faces; } Mesh; +typedef struct { int a, b, c; uint8_t force_wire; } Face; +typedef struct { int x, y; fixed z; fixed intensity; } Point2D; - -/* Structure pour l'univers */ +/* Un objet dans l'univers : tranche du pool partagé + transform */ typedef struct { - Mesh *mesh; - Vector3 pos; + int vert_start, num_verts; /* indices dans Universe.verts/v_normals */ + int face_start, num_faces; /* indices dans Universe.faces */ + Vector3 pos; /* position monde courante (mise à jour par orbite) */ + Vector3 base_pos; /* offset par rapport au centre d'orbite */ Matrix3 rot; uint8_t color; } Object3D; +/* Pool unique pour toute la scène */ +typedef struct { + Vector3 verts[MAX_VERTEX]; + Vector3 v_normals[MAX_VERTEX]; + Face faces[MAX_FACES]; + int num_verts, num_faces; + Object3D objects[MAX_OBJECTS]; + int num_objects; +} Universe; + typedef struct { Vector3 pos; } Camera; +/* Mesh dynamique conservé pour save/load uniquement */ +typedef struct { Vector3 *verts; Vector3 *v_normals; Face *faces; int num_verts, num_faces; } Mesh; + /* Globales */ extern fixed sintab[360], costab[360]; extern Point2D v_cache[MAX_VERTEX]; @@ -75,12 +93,13 @@ void mat3_rotate_x(Matrix3 *m, int angle); void mat3_rotate_y(Matrix3 *m, int angle); void mat3_mul(Matrix3 *res, Matrix3 *a, Matrix3 *b); -void add_sphere(Mesh *m, fixed x, fixed y, fixed z, fixed r, int det, uint8_t col, uint8_t wire); -void add_cylinder(Mesh *m, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire); +/* Retournent un pointeur sur le nouvel Object3D, NULL si pool saturé */ +Object3D* add_sphere (Universe *uni, fixed x, fixed y, fixed z, fixed r, int det, uint8_t col, uint8_t wire); +Object3D* add_cylinder(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire); -void render_universe(Object3D *objs, int num_objs, Camera *cam, int mode); +void render_universe(Universe *uni, Camera *cam, int mode); -void init_vga(void); +int init_vga(void); void clear_buffers(uint8_t color); void flip(void); void close_vga(void); @@ -88,7 +107,6 @@ void close_vga(void); void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_color); void draw_line_z(Point2D *p1, Point2D *p2, uint8_t col, int z_test); -void normalize(Vector3 *v); void save_mesh(Mesh *m, const char *fn); Mesh *load_mesh(const char *fname); void free_mesh(Mesh *m); diff --git a/src/part3D/engine.c b/src/part3D/engine.c index 6462e9c..7c0ccc4 100644 --- a/src/part3D/engine.c +++ b/src/part3D/engine.c @@ -4,36 +4,28 @@ fixed sintab[360], costab[360]; Point2D v_cache[MAX_VERTEX]; void mat3_identity(Matrix3 *mat) { - memset(mat, 0, sizeof(Matrix3)); - mat->m[0][0] = mat->m[1][1] = mat->m[2][2] = int_to_f(1); + memset(mat, 0, sizeof(Matrix3)); + mat->m[0][0] = mat->m[1][1] = mat->m[2][2] = int_to_f(1); } -void init_engine_math() { - int i; - for (i = 0; i < 360; i++) { - double r = (double)i * 3.14159265 / 180.0; - sintab[i] = (fixed)(sin(r) * 65536.0); - costab[i] = (fixed)(cos(r) * 65536.0); - } -} - -void normalize(Vector3 *v) { - double dx = (double)v->x, dy = (double)v->y, dz = (double)v->z; - double d = sqrt(dx*dx + dy*dy + dz*dz); - if (d > 0) { - v->x = (fixed)((dx/d)*65536.0); v->y = (fixed)((dy/d)*65536.0); v->z = (fixed)((dz/d)*65536.0); - } +void init_engine_math(void) { + int i; + for (i = 0; i < 360; i++) { + double r = (double)i * 3.14159265 / 180.0; + sintab[i] = (fixed)(sin(r) * 65536.0); + costab[i] = (fixed)(cos(r) * 65536.0); + } } void mat3_mul(Matrix3 *res, Matrix3 *a, Matrix3 *b) { + Matrix3 tmp; int i, j; - for (i = 0; i < 3; i++) { - for (j = 0; j < 3; j++) { - res->m[i][j] = f_mul(a->m[i][0], b->m[0][j]) + - f_mul(a->m[i][1], b->m[1][j]) + - f_mul(a->m[i][2], b->m[2][j]); - } - } + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + tmp.m[i][j] = f_mul(a->m[i][0], b->m[0][j]) + + f_mul(a->m[i][1], b->m[1][j]) + + f_mul(a->m[i][2], b->m[2][j]); + *res = tmp; } void mat3_rotate_x(Matrix3 *m, int angle) { @@ -50,36 +42,45 @@ void mat3_rotate_y(Matrix3 *m, int angle) { m->m[0][0] = c; m->m[0][2] = s; m->m[2][0] = -s; m->m[2][2] = c; } -void render_universe(Object3D *objs, int num_objs, Camera *cam, int mode) { - int o, i, front; +void render_universe(Universe *uni, Camera *cam, int mode) { + int o, i, vi, front; fixed rx, ry, rz, pz, nz; uint8_t wire_col; + Vector3 *v, *n; + Object3D *obj; + Face *f; + Point2D *p1, *p2, *p3; - for (o = 0; o < num_objs; o++) { - Object3D *obj = &objs[o]; - Mesh *m = obj->mesh; + for (o = 0; o < uni->num_objects; o++) { + obj = &uni->objects[o]; - for (i = 0; i < m->num_verts; i++) { - Vector3 *v = &m->verts[i], *n = &m->v_normals[i]; + /* Transformation + projection de tous les sommets de cet objet */ + for (i = 0; i < obj->num_verts; i++) { + vi = obj->vert_start + i; + v = &uni->verts[vi]; + n = &uni->v_normals[vi]; rx = f_mul(v->x, obj->rot.m[0][0]) + f_mul(v->y, obj->rot.m[1][0]) + f_mul(v->z, obj->rot.m[2][0]) + obj->pos.x - cam->pos.x; ry = f_mul(v->x, obj->rot.m[0][1]) + f_mul(v->y, obj->rot.m[1][1]) + f_mul(v->z, obj->rot.m[2][1]) + obj->pos.y - cam->pos.y; rz = f_mul(v->x, obj->rot.m[0][2]) + f_mul(v->y, obj->rot.m[1][2]) + f_mul(v->z, obj->rot.m[2][2]) + obj->pos.z - cam->pos.z; pz = (rz < int_to_f(20)) ? int_to_f(20) : rz; - v_cache[i].z = pz; - v_cache[i].x = 160 + f_to_int(f_div(rx << 8, pz)); - v_cache[i].y = 100 - f_to_int(f_div(ry << 8, pz)); + v_cache[vi].z = pz; + v_cache[vi].x = 160 + f_to_int(f_div(rx << 8, pz)); + v_cache[vi].y = 100 - f_to_int(f_div(ry << 8, pz)); nz = f_mul(n->x, obj->rot.m[0][2]) + f_mul(n->y, obj->rot.m[1][2]) + f_mul(n->z, obj->rot.m[2][2]); - v_cache[i].intensity = (nz >= 0) ? int_to_f(1) : ((-nz) * 14) + int_to_f(1); + v_cache[vi].intensity = (nz >= 0) ? int_to_f(1) : f_mul(-nz, int_to_f(14)) + int_to_f(1); } wire_col = (uint8_t)((obj->color << 4) | 15); - for (i = 0; i < m->num_faces; i++) { - Face *f = &m->faces[i]; - Point2D *p1 = &v_cache[f->a], *p2 = &v_cache[f->b], *p3 = &v_cache[f->c]; + /* Rendu des faces */ + for (i = 0; i < obj->num_faces; i++) { + f = &uni->faces[obj->face_start + i]; + p1 = &v_cache[f->a]; + p2 = &v_cache[f->b]; + p3 = &v_cache[f->c]; front = ((p2->x - p1->x) * (p3->y - p1->y) - (p2->y - p1->y) * (p3->x - p1->x)) > 0; switch (mode) { diff --git a/src/part3D/geometry.c b/src/part3D/geometry.c index a30b33b..8f1a23c 100644 --- a/src/part3D/geometry.c +++ b/src/part3D/geometry.c @@ -1,151 +1,208 @@ #include "defines.h" -void add_face(Mesh *m, int a, int b, int c, uint8_t col, uint8_t wire) { - Face *f = &m->faces[m->num_faces++]; - f->a = a; - f->b = b; - f->c = c; - f->color = col; - f->force_wire = wire; +static void push_face(Universe *uni, Object3D *obj, int a, int b, int c, uint8_t wire) { + Face *f = &uni->faces[uni->num_faces++]; + f->a = a; f->b = b; f->c = c; + f->force_wire = wire; + obj->num_faces++; } -/* Génère une sphère avec des normales unitaires */ -void add_sphere(Mesh *m, fixed x, fixed y, fixed z, fixed r, int det, uint8_t col, uint8_t wire) { - int i, j, v_base = m->num_verts; +static Object3D *alloc_object(Universe *uni) { + Object3D *obj = &uni->objects[uni->num_objects++]; + obj->vert_start = uni->num_verts; + obj->face_start = uni->num_faces; + obj->num_verts = 0; + obj->num_faces = 0; + obj->pos.x = obj->pos.y = obj->pos.z = 0; + obj->base_pos.x = obj->base_pos.y = obj->base_pos.z = 0; + mat3_identity(&obj->rot); + return obj; +} + +Object3D *add_sphere(Universe *uni, fixed x, fixed y, fixed z, fixed r, int det, uint8_t col, uint8_t wire) { + int i, j, v_base, phi, th; + Object3D *obj; + Vector3 *v, *n; + + if (uni->num_objects >= MAX_OBJECTS) return NULL; + if (uni->num_verts + (det + 1) * det > MAX_VERTEX) return NULL; + if (uni->num_faces + det * det * 2 > MAX_FACES) return NULL; + + obj = alloc_object(uni); + obj->color = col; + v_base = uni->num_verts; + for (j = 0; j <= det; j++) { - fixed phi = (j * 180 / det) % 360; + phi = (j * 180 / det) % 360; for (i = 0; i < det; i++) { - fixed th = (i * 360 / det) % 360; - Vector3 *v = &m->verts[m->num_verts]; - Vector3 *n = &m->v_normals[m->num_verts]; + th = (i * 360 / det) % 360; + v = &uni->verts[uni->num_verts]; + n = &uni->v_normals[uni->num_verts]; n->x = f_mul(sintab[phi], costab[th]); n->y = costab[phi]; n->z = f_mul(sintab[phi], sintab[th]); - v->x = x + f_mul(r, n->x); v->y = y + f_mul(r, n->y); v->z = z + f_mul(r, n->z); - m->num_verts++; + uni->num_verts++; obj->num_verts++; } } + for (j = 0; j < det; j++) { for (i = 0; i < det; i++) { - int a = v_base + j * det + i; - int b = v_base + j * det + (i + 1) % det; + int a = v_base + j * det + i; + int b = v_base + j * det + (i + 1) % det; int c = v_base + (j + 1) * det + i; int d = v_base + (j + 1) * det + (i + 1) % det; - add_face(m, a, b, c, col, wire); - add_face(m, b, d, c, col, wire); + push_face(uni, obj, a, b, c, wire); + push_face(uni, obj, b, d, c, wire); } } + + return obj; } -/* Génère un cylindre fermé (corps + fonds) avec normales et winding corrects */ -void add_cylinder(Mesh *m, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire) { - int i, v_base = m->num_verts; - int cap_bot_center, cap_bot_rim, cap_top_center, cap_top_rim; - fixed half_h = h / 2; - fixed th, s, c; +Object3D *add_cylinder(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire) { + int i, v_base, cap_bot_center, cap_bot_rim, cap_top_center, cap_top_rim, th; + fixed half_h, s, cs; + Object3D *obj; - /* --- Corps : paires bas/haut, normales radiales --- */ + if (uni->num_objects >= MAX_OBJECTS) return NULL; + if (uni->num_verts + 2 + 4 * det > MAX_VERTEX) return NULL; + if (uni->num_faces + 4 * det > MAX_FACES) return NULL; + + obj = alloc_object(uni); + obj->color = col; + v_base = uni->num_verts; + half_h = h / 2; + + /* --- Corps --- */ for (i = 0; i < det; i++) { th = (i * 360 / det) % 360; - s = sintab[th]; c = costab[th]; + s = sintab[th]; cs = costab[th]; - m->v_normals[m->num_verts].x = c; m->v_normals[m->num_verts].y = 0; m->v_normals[m->num_verts].z = s; - m->verts[m->num_verts].x = x + f_mul(r, c); m->verts[m->num_verts].y = y - half_h; m->verts[m->num_verts].z = z + f_mul(r, s); - m->num_verts++; + uni->v_normals[uni->num_verts].x = cs; + uni->v_normals[uni->num_verts].y = 0; + uni->v_normals[uni->num_verts].z = s; + uni->verts[uni->num_verts].x = x + f_mul(r, cs); + uni->verts[uni->num_verts].y = y - half_h; + uni->verts[uni->num_verts].z = z + f_mul(r, s); + uni->num_verts++; obj->num_verts++; - m->v_normals[m->num_verts].x = c; m->v_normals[m->num_verts].y = 0; m->v_normals[m->num_verts].z = s; - m->verts[m->num_verts].x = x + f_mul(r, c); m->verts[m->num_verts].y = y + half_h; m->verts[m->num_verts].z = z + f_mul(r, s); - m->num_verts++; + uni->v_normals[uni->num_verts].x = cs; + uni->v_normals[uni->num_verts].y = 0; + uni->v_normals[uni->num_verts].z = s; + uni->verts[uni->num_verts].x = x + f_mul(r, cs); + uni->verts[uni->num_verts].y = y + half_h; + uni->verts[uni->num_verts].z = z + f_mul(r, s); + uni->num_verts++; obj->num_verts++; } - /* Faces corps (winding CCW = cross > 0 pour la face avant) */ for (i = 0; i < det; i++) { - int a = v_base + (i * 2); - int b = v_base + ((i * 2 + 2) % (det * 2)); - int c_v = a + 1; - int d = b + 1; - add_face(m, a, c_v, b, col, wire); /* bas_i, haut_i, bas_{i+1} */ - add_face(m, c_v, d, b, col, wire); /* haut_i, haut_{i+1}, bas_{i+1} */ + int a = v_base + (i * 2); + int b = v_base + ((i * 2 + 2) % (det * 2)); + int cv = a + 1; + int d = b + 1; + push_face(uni, obj, a, cv, b, wire); + push_face(uni, obj, cv, d, b, wire); } - /* --- Fond bas : centre + rebord, normale (0,-1,0) --- */ - cap_bot_center = m->num_verts; - m->v_normals[m->num_verts].x = 0; m->v_normals[m->num_verts].y = -int_to_f(1); m->v_normals[m->num_verts].z = 0; - m->verts[m->num_verts].x = x; m->verts[m->num_verts].y = y - half_h; m->verts[m->num_verts].z = z; - m->num_verts++; + /* --- Fond bas --- */ + cap_bot_center = uni->num_verts; + uni->v_normals[uni->num_verts].x = 0; + uni->v_normals[uni->num_verts].y = -int_to_f(1); + uni->v_normals[uni->num_verts].z = 0; + uni->verts[uni->num_verts].x = x; + uni->verts[uni->num_verts].y = y - half_h; + uni->verts[uni->num_verts].z = z; + uni->num_verts++; obj->num_verts++; - cap_bot_rim = m->num_verts; + cap_bot_rim = uni->num_verts; for (i = 0; i < det; i++) { th = (i * 360 / det) % 360; - s = sintab[th]; c = costab[th]; - m->v_normals[m->num_verts].x = 0; m->v_normals[m->num_verts].y = -int_to_f(1); m->v_normals[m->num_verts].z = 0; - m->verts[m->num_verts].x = x + f_mul(r, c); m->verts[m->num_verts].y = y - half_h; m->verts[m->num_verts].z = z + f_mul(r, s); - m->num_verts++; + s = sintab[th]; cs = costab[th]; + uni->v_normals[uni->num_verts].x = 0; + uni->v_normals[uni->num_verts].y = -int_to_f(1); + uni->v_normals[uni->num_verts].z = 0; + uni->verts[uni->num_verts].x = x + f_mul(r, cs); + uni->verts[uni->num_verts].y = y - half_h; + uni->verts[uni->num_verts].z = z + f_mul(r, s); + uni->num_verts++; obj->num_verts++; } - for (i = 0; i < det; i++) { - add_face(m, cap_bot_center, cap_bot_rim + i, cap_bot_rim + (i + 1) % det, col, wire); - } + for (i = 0; i < det; i++) + push_face(uni, obj, cap_bot_center, cap_bot_rim + i, cap_bot_rim + (i + 1) % det, wire); - /* --- Fond haut : centre + rebord, normale (0,+1,0) --- */ - cap_top_center = m->num_verts; - m->v_normals[m->num_verts].x = 0; m->v_normals[m->num_verts].y = int_to_f(1); m->v_normals[m->num_verts].z = 0; - m->verts[m->num_verts].x = x; m->verts[m->num_verts].y = y + half_h; m->verts[m->num_verts].z = z; - m->num_verts++; + /* --- Fond haut --- */ + cap_top_center = uni->num_verts; + uni->v_normals[uni->num_verts].x = 0; + uni->v_normals[uni->num_verts].y = int_to_f(1); + uni->v_normals[uni->num_verts].z = 0; + uni->verts[uni->num_verts].x = x; + uni->verts[uni->num_verts].y = y + half_h; + uni->verts[uni->num_verts].z = z; + uni->num_verts++; obj->num_verts++; - cap_top_rim = m->num_verts; + cap_top_rim = uni->num_verts; for (i = 0; i < det; i++) { th = (i * 360 / det) % 360; - s = sintab[th]; c = costab[th]; - m->v_normals[m->num_verts].x = 0; m->v_normals[m->num_verts].y = int_to_f(1); m->v_normals[m->num_verts].z = 0; - m->verts[m->num_verts].x = x + f_mul(r, c); m->verts[m->num_verts].y = y + half_h; m->verts[m->num_verts].z = z + f_mul(r, s); - m->num_verts++; + s = sintab[th]; cs = costab[th]; + uni->v_normals[uni->num_verts].x = 0; + uni->v_normals[uni->num_verts].y = int_to_f(1); + uni->v_normals[uni->num_verts].z = 0; + uni->verts[uni->num_verts].x = x + f_mul(r, cs); + uni->verts[uni->num_verts].y = y + half_h; + uni->verts[uni->num_verts].z = z + f_mul(r, s); + uni->num_verts++; obj->num_verts++; } - for (i = 0; i < det; i++) { - add_face(m, cap_top_center, cap_top_rim + (i + 1) % det, cap_top_rim + i, col, wire); - } + for (i = 0; i < det; i++) + push_face(uni, obj, cap_top_center, cap_top_rim + (i + 1) % det, cap_top_rim + i, wire); + + return obj; } +/* --- I/O sur Mesh dynamique (non utilisé dans le pipeline principal) --- */ + void save_mesh(Mesh *m, const char *fn) { FILE *f = fopen(fn, "wb"); if (!f) return; fwrite(&m->num_verts, sizeof(int), 1, f); fwrite(&m->num_faces, sizeof(int), 1, f); - fwrite(m->verts, sizeof(Vector3), m->num_verts, f); - fwrite(m->v_normals, sizeof(Vector3), m->num_verts, f); - fwrite(m->faces, sizeof(Face), m->num_faces, f); + fwrite(m->verts, sizeof(Vector3), m->num_verts, f); + fwrite(m->v_normals,sizeof(Vector3), m->num_verts, f); + fwrite(m->faces, sizeof(Face), m->num_faces, f); fclose(f); } -Mesh* load_mesh(const char *fname) { +Mesh *load_mesh(const char *fname) { Mesh *m; FILE *f = fopen(fname, "rb"); if (!f) return NULL; - m = (Mesh*)malloc(sizeof(Mesh)); + if (!m) return NULL; fread(&m->num_verts, sizeof(int), 1, f); fread(&m->num_faces, sizeof(int), 1, f); - - m->verts = (Vector3*)malloc(sizeof(Vector3) * m->num_verts); + m->verts = (Vector3*)malloc(sizeof(Vector3) * m->num_verts); m->v_normals = (Vector3*)malloc(sizeof(Vector3) * m->num_verts); - m->faces = (Face*)malloc(sizeof(Face) * m->num_faces); - - fread(m->verts, sizeof(Vector3), m->num_verts, f); + m->faces = (Face*) malloc(sizeof(Face) * m->num_faces); + if (!m->verts || !m->v_normals || !m->faces) { + free(m->verts); free(m->v_normals); free(m->faces); free(m); + fclose(f); + return NULL; + } + fread(m->verts, sizeof(Vector3), m->num_verts, f); fread(m->v_normals, sizeof(Vector3), m->num_verts, f); - fread(m->faces, sizeof(Face), m->num_faces, f); + fread(m->faces, sizeof(Face), m->num_faces, f); fclose(f); return m; } void free_mesh(Mesh *m) { if (!m) return; - if (m->verts) free(m->verts); - if (m->faces) free(m->faces); - if (m->v_normals) free(m->v_normals); + free(m->verts); + free(m->v_normals); + free(m->faces); free(m); } diff --git a/src/part3D/graph.c b/src/part3D/graph.c index 7680a40..83d81bb 100644 --- a/src/part3D/graph.c +++ b/src/part3D/graph.c @@ -19,48 +19,37 @@ RGB my_palette[16] = { {20, 20, 40} // 15: Bleu nuit }; - - uint8_t *backbuffer = NULL; uint16_t *zbuffer = NULL; uint8_t *vga = (uint8_t *)0xA0000; -void set_mode(int mode); -#pragma aux set_mode = "int 0x10" parm [ax]; - -void reset_mode(int mode); -#pragma aux reset_mode = "int 0x10" parm [ax]; +void set_vga_mode(int mode); +#pragma aux set_vga_mode = "int 0x10" parm [ax]; void clear_buffers(uint8_t color) { - memset(backbuffer, color, 64000); - memset(zbuffer, 0xFF, 64000 * sizeof(uint16_t)); + memset(backbuffer, color, SCREEN_PIXELS); + memset(zbuffer, 0xFF, SCREEN_PIXELS * sizeof(uint16_t)); } -void flip() { - memcpy(vga, backbuffer, 64000); +void flip(void) { + memcpy(vga, backbuffer, SCREEN_PIXELS); } -void close_vga() { - reset_mode(0x0003); // Retour au mode texte - if (backbuffer) free(backbuffer); - if (zbuffer) free(zbuffer); +void close_vga(void) { + set_vga_mode(0x0003); /* retour au mode texte */ + if (backbuffer) { free(backbuffer); backbuffer = NULL; } + if (zbuffer) { free(zbuffer); zbuffer = NULL; } } -// Prend 16 couleurs RGB (0-63) et remplit les 256 entrées du DAC +/* Prend 16 couleurs RGB (0-63) et remplit les 256 entrées du DAC */ void setup_vga_palette(RGB *base_colors) { int i, c; - // Port 0x3C8 : Index de départ (0) outp(0x3C8, 0); - for (c = 0; c < 16; c++) { for (i = 0; i < 16; i++) { - // Interpolation linéaire vers le noir (0,0,0) - // On utilise l'arithmétique entière : (color * intensity) / 15 uint8_t r = (uint8_t)((base_colors[c].r * i) / 15); uint8_t g = (uint8_t)((base_colors[c].g * i) / 15); uint8_t b = (uint8_t)((base_colors[c].b * i) / 15); - - // Port 0x3C9 : Envoi des composantes R, G, B outp(0x3C9, r); outp(0x3C9, g); outp(0x3C9, b); @@ -68,13 +57,16 @@ void setup_vga_palette(RGB *base_colors) { } } -void init_vga() { - // On passe en mode 13h (320x200x256) - set_mode(0x0013); - - // Allocations - backbuffer = (uint8_t *)malloc(64000); - zbuffer = (uint16_t *)malloc(64000 * sizeof(uint16_t)); - +int init_vga(void) { + set_vga_mode(0x0013); + backbuffer = (uint8_t *)malloc(SCREEN_PIXELS); + zbuffer = (uint16_t *)malloc(SCREEN_PIXELS * sizeof(uint16_t)); + if (!backbuffer || !zbuffer) { + set_vga_mode(0x0003); + free(backbuffer); backbuffer = NULL; + free(zbuffer); zbuffer = NULL; + return -1; + } setup_vga_palette(my_palette); + return 0; } diff --git a/src/part3D/raster.c b/src/part3D/raster.c index c2d11ec..5057483 100644 --- a/src/part3D/raster.c +++ b/src/part3D/raster.c @@ -1,31 +1,13 @@ #include "defines.h" -/* Prototypes pour les fonctions assembleur externes */ -extern void scanline_zonly_asm(int width, uint16_t *zbuf, int z, int dz_step); - -#pragma aux scanline_zonly_asm = \ - "test ecx, ecx" \ - "jz done_z" \ -"loop_z:" \ - "mov eax, edx" \ - "shr eax, 16" \ - "cmp ax, [esi]" \ - "jae skip_z" \ - "mov [esi], ax" \ -"skip_z:" \ - "add edx, ebx" \ - "add esi, 2" \ - "dec ecx" \ - "jnz loop_z" \ -"done_z:" \ - parm [ecx] [esi] [edx] [ebx] \ - modify [eax ecx esi edx]; - -/* Rendu d'une ligne avec interpolation Gouraud 16.16 */ -void scanline_gouraud_c(int width, uint8_t *dest, uint16_t *zbuf, int z, int dz_step, int intensity, int di_step, uint8_t base_color) { +/* Rendu d'une scanline avec interpolation Gouraud 16.16 */ +void scanline_gouraud_c(int width, uint8_t *dest, uint16_t *zbuf, + fixed z, fixed dz_step, fixed intensity, fixed di_step, + uint8_t base_color) { int i; - uint8_t color_offset; + int s; uint8_t shade; + uint8_t color_offset; uint16_t zv; color_offset = (uint8_t)(base_color << 4); @@ -34,9 +16,10 @@ void scanline_gouraud_c(int width, uint8_t *dest, uint16_t *zbuf, int z, int dz_ zv = (uint16_t)(z >> 16); if (zv < zbuf[i]) { zbuf[i] = zv; - /* Extraction de l'intensité 0-15 depuis le format fixed 16.16 */ - shade = (uint8_t)(intensity >> 16); - if (shade > 15) shade = 15; + s = intensity >> 16; + if (s < 0) shade = 0; + else if (s > 15) shade = 15; + else shade = (uint8_t)s; dest[i] = color_offset + shade; } z += dz_step; @@ -50,9 +33,10 @@ void swap_pt(Point2D **a, Point2D **b) { void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_color) { /* TOUTES les variables déclarées ici pour le C89 */ - int y, width, start_x, end_x, dz_step, di_step, clip_x, w, start_z, start_i, second_half, total_height; + int y, width, start_x, end_x, clip_x, w, second_half, total_height; fixed dx13, dz13, di13, xA, zA, iA, xB, zB, iB, dx12, dz12, di12, dx23, dz23, di23; fixed cx1, cz1, ci1, cx2, cz2, ci2, tx, tz, ti; + fixed dz_step, di_step, start_z, start_i; uint8_t *dest; uint16_t *zbuf; @@ -70,7 +54,12 @@ void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_c di13 = (p3->intensity - p1->intensity) / total_height; xA = int_to_f(p1->x); zA = p1->z; iA = p1->intensity; - xB = xA; zB = zA; iB = iA; + /* Sommet plat en haut : xB part de p2, pas de p1 */ + if (p1->y == p2->y) { + xB = int_to_f(p2->x); zB = p2->z; iB = p2->intensity; + } else { + xB = xA; zB = zA; iB = iA; + } /* Gradients pour les côtés courts (p1-p2 puis p2-p3) */ dx12 = (p2->y > p1->y) ? ((p2->x - p1->x) << 16) / (p2->y - p1->y) : 0; @@ -94,22 +83,22 @@ void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_c } start_x = f_to_int(cx1); - end_x = f_to_int(cx2); - width = end_x - start_x; + end_x = f_to_int(cx2) + 1; /* ceil : couvre le pixel de bord droit fractionnaire */ + width = end_x - start_x; - if (y >= 0 && y < 200 && width > 0) { + if (y >= 0 && y < SCREEN_H && width > 0) { dz_step = (cz2 - cz1) / width; di_step = (ci2 - ci1) / width; clip_x = (start_x < 0) ? 0 : start_x; w = width - (clip_x - start_x); - if (clip_x + w > 320) w = 320 - clip_x; + if (clip_x + w > SCREEN_W) w = SCREEN_W - clip_x; if (w > 0) { start_z = cz1 + (clip_x - start_x) * dz_step; start_i = ci1 + (clip_x - start_x) * di_step; - dest = backbuffer + (y * 320) + clip_x; - zbuf = zbuffer + (y * 320) + clip_x; + dest = backbuffer + (y * SCREEN_W) + clip_x; + zbuf = zbuffer + (y * SCREEN_W) + clip_x; scanline_gouraud_c(w, dest, zbuf, start_z, dz_step, start_i, di_step, base_color); } @@ -142,11 +131,11 @@ void draw_line_z(Point2D *p1, Point2D *p2, uint8_t col, int z_test) { x = p1->x; y = p1->y; while (1) { - if (x >= 0 && x < 320 && y >= 0 && y < 200) { + if (x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H) { zv = (uint16_t)f_to_int(cz); - if (!z_test || zv <= zbuffer[y * 320 + x]) { - backbuffer[y * 320 + x] = col; - if (z_test) zbuffer[y * 320 + x] = zv; + if (!z_test || zv < zbuffer[y * SCREEN_W + x]) { + backbuffer[y * SCREEN_W + x] = col; + if (z_test) zbuffer[y * SCREEN_W + x] = zv; } } if (x == p2->x && y == p2->y) break; @@ -156,37 +145,3 @@ void draw_line_z(Point2D *p1, Point2D *p2, uint8_t col, int z_test) { cz += z_step; } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -