From a89bea929a26b8ca119e08e6d50cfd15000ee919 Mon Sep 17 00:00:00 2001 From: Frater <13979047+saintfrater@users.noreply.github.com> Date: Fri, 15 May 2026 12:57:47 +0200 Subject: [PATCH] add parabole --- src/main/main.c | 112 +++++++++++++++++++++------ src/part3D/defines.h | 15 ++++ src/part3D/engine.c | 11 +-- src/part3D/geometry.c | 171 ++++++++++++++++++++++++++++++++++++++++++ src/part3D/graph.c | 74 ++++++++++++++++++ src/part3D/image.c | 84 +++++++++++++++++++++ 6 files changed, 441 insertions(+), 26 deletions(-) create mode 100644 src/part3D/image.c diff --git a/src/main/main.c b/src/main/main.c index 3fd22f4..00125fb 100644 --- a/src/main/main.c +++ b/src/main/main.c @@ -2,9 +2,12 @@ int main(void) { Universe *uni; - Object3D *sph, *cyl, *tmp_obj; + Object3D *bus, *dish, *rtg_boom, *rtg1, *rtg2, *rtg3; + Object3D *mag_boom, *sci_boom, *cam_box, *record, *feed_boom, *sub_dish; + Object3D *tank, *pws1, *pws2, *inst1, *inst2; Camera cam; Matrix3 rotX, rotY, orbit; + Image *bg_img = NULL; int special=0; int anglex, angley, key, render, o; fixed bx, by, bz, distance; @@ -20,36 +23,95 @@ int main(void) { if (!uni) return 1; uni->num_verts = uni->num_faces = uni->num_objects = 0; - /* --- Ajout des objets : géométrie locale en (0,0,0) --- */ - sph = add_sphere(uni, 0, 0, 0, int_to_f(30), 8, COLOR4, 0); - if (!sph) { free(uni); return 1; } - set_object_pos(sph,int_to_f(70),0,0); - //set_object_color(COLOR4); + /* --- Construction de la sonde Voyager --- */ - cyl = add_cylinder(uni, 0, 0, 0, int_to_f(20), int_to_f(60), 12, COLOR2, 0); - if (!cyl) { free(uni); return 1; } - set_object_pos(cyl,int_to_f(-70),0,0); - //set_object_color(COLOR2); + /* 1. Bus principal (décagone blanc) */ + bus = add_cylinder(uni, 0, 0, 0, int_to_f(12), int_to_f(10), 10, COLOR7, 0); + set_object_pos(bus, 0, 0, 0); - //tmp_obj = add_cone(uni, 0, 0, 0, int_to_f(20), int_to_f(60), 12, COLOR1, 0); - tmp_obj = add_torus(uni, 0, 0, 0, int_to_f(20), int_to_f(10), 12, 12, COLOR1, 0); + /* 1b. Réservoir d'hydrazine central (sphère dépassant sous le bus) */ + tank = add_sphere(uni, 0, 0, 0, int_to_f(6), 8, COLOR7, 0); + set_object_pos(tank, 0, int_to_f(-3), 0); + /* 2a. Antenne parabolique (Paraboloïde avec courbure réaliste) */ + dish = add_paraboloid(uni, 0, 0, 0, int_to_f(25), int_to_f(8), 24, 4, COLOR7, 0); + /* Pointant déjà vers le haut (+Y), on abaisse simplement sa base pour se reposer sur le bus */ + set_object_pos(dish, 0, int_to_f(9), 0); - // tmp_obj = add_math_surface(uni, 0, 0, 0, int_to_f(120), int_to_f(120), 30, 30, COLOR2, 0); - // set_object_pos(tmp_obj, 0, int_to_f(-40), 0); + /* 2b. Mât central de l'antenne (treillis triangulaire) */ + feed_boom = add_cylinder(uni, 0, 0, 0, int_to_f(1)/2, int_to_f(10), 3, COLOR7, 1); + set_object_pos(feed_boom, 0, int_to_f(10), 0); + /* 2c. Sous-réflecteur de l'antenne (petit cône inversé) */ + sub_dish = add_cone(uni, 0, 0, 0, int_to_f(3), int_to_f(2), 12, COLOR7, 0); + bake_object_rotation(uni, sub_dish, 180, 0, 0); + set_object_pos(sub_dish, 0, int_to_f(16), 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; - */ + /* 3. Mât du RTG (Générateur thermoélectrique, structure en treillis carrée) */ + rtg_boom = add_cylinder(uni, 0, 0, 0, int_to_f(2), int_to_f(40), 4, COLOR11, 1); + bake_object_rotation(uni, rtg_boom, 0, 0, 90); /* Couché sur l'axe X */ + set_object_pos(rtg_boom, int_to_f(25), int_to_f(-2), 0); + + /* 4. RTG (Générateurs thermoélectriques, 3 segments en série) */ + rtg1 = add_cylinder(uni, 0, 0, 0, int_to_f(4), int_to_f(5), 8, COLOR11, 0); + bake_object_rotation(uni, rtg1, 0, 0, 90); + set_object_pos(rtg1, int_to_f(48), int_to_f(-2), 0); + + rtg2 = add_cylinder(uni, 0, 0, 0, int_to_f(4), int_to_f(5), 8, COLOR11, 0); + bake_object_rotation(uni, rtg2, 0, 0, 90); + set_object_pos(rtg2, int_to_f(54), int_to_f(-2), 0); + + rtg3 = add_cylinder(uni, 0, 0, 0, int_to_f(4), int_to_f(5), 8, COLOR11, 0); + bake_object_rotation(uni, rtg3, 0, 0, 90); + set_object_pos(rtg3, int_to_f(60), int_to_f(-2), 0); + + /* 5. Mât du magnétomètre (Très long, structure en treillis triangulaire) */ + mag_boom = add_cylinder(uni, 0, 0, 0, int_to_f(1), int_to_f(70), 3, COLOR11, 1); + bake_object_rotation(uni, mag_boom, 0, 0, 90); + set_object_pos(mag_boom, int_to_f(-40), int_to_f(-2), 0); + + /* 6. Mât de la plateforme scientifique (vers l'avant, axe Z, treillis carrée) */ + sci_boom = add_cylinder(uni, 0, 0, 0, int_to_f(2), int_to_f(30), 4, COLOR11, 1); + bake_object_rotation(uni, sci_boom, 90, 0, 0); /* Couché sur l'axe Z */ + set_object_pos(sci_boom, 0, int_to_f(-2), int_to_f(-20)); + + /* 7. Plateforme et caméras (cube gris) */ + cam_box = add_box(uni, 0, 0, 0, int_to_f(6), int_to_f(6), int_to_f(6), COLOR11, 0); + set_object_pos(cam_box, 0, int_to_f(-2), int_to_f(-35)); + + /* 8. Le célèbre Golden Record ! (Petit cylindre jaune/or collé sur le flanc) */ + record = add_cylinder(uni, 0, 0, 0, int_to_f(3), int_to_f(1), 10, COLOR4, 0); + bake_object_rotation(uni, record, 0, 0, 90); + set_object_pos(record, int_to_f(12), int_to_f(0), int_to_f(5)); + + /* 9. Antennes PWS (Plasma Wave Subsystem) - Longs tubes filaires en V */ + pws1 = add_cylinder(uni, 0, 0, 0, int_to_f(1)/2, int_to_f(60), 3, COLOR11, 1); + bake_object_rotation(uni, pws1, 0, 0, 90); /* 1. On le couche sur l'axe X */ + bake_object_rotation(uni, pws1, 0, 45, 0); /* 2. On l'écarte en V sur le plan XZ */ + set_object_pos(pws1, int_to_f(-20), int_to_f(-5), int_to_f(20)); + + pws2 = add_cylinder(uni, 0, 0, 0, int_to_f(1)/2, int_to_f(60), 3, COLOR11, 1); + bake_object_rotation(uni, pws2, 0, 0, 90); /* 1. On le couche sur l'axe X */ + bake_object_rotation(uni, pws2, 0, -45, 0);/* 2. On l'écarte en V sur le plan XZ */ + set_object_pos(pws2, int_to_f(-20), int_to_f(-5), int_to_f(-20)); + + /* 10. Instruments optiques sur la plateforme scientifique (Télescopes/Caméras) */ + inst1 = add_cylinder(uni, 0, 0, 0, int_to_f(2), int_to_f(8), 6, COLOR7, 0); + bake_object_rotation(uni, inst1, 90, 0, 0); + set_object_pos(inst1, int_to_f(-2), int_to_f(1), int_to_f(-38)); + + inst2 = add_cone(uni, 0, 0, 0, int_to_f(2), int_to_f(6), 8, COLOR7, 0); + bake_object_rotation(uni, inst2, 90, 0, 0); + set_object_pos(inst2, int_to_f(2), int_to_f(1), int_to_f(-38)); cam.pos.x = cam.pos.y = cam.pos.z = 0; if (init_vga() != 0) { free(uni); return 1; } + /* Optionnel : charger un fond (BMP 8-bit de 320x200 couleurs indexees) */ + /* S'assurer que le fichier est bien généré avec les couleurs de votre palette moteur ! */ + /* bg_img = load_bmp8("stars.bmp", NULL); */ + while (key != 27) { if (kbhit()) { special = 0; @@ -104,11 +166,19 @@ int main(void) { obj->pos.z = distance + f_mul(bx, orbit.m[0][2]) + f_mul(by, orbit.m[1][2]) + f_mul(bz, orbit.m[2][2]); } - clear_buffers(0); + if (bg_img) { + /* Dessiner le fond (qui écrase tout) et vider juste le Z-Buffer */ + blit_image(bg_img, 0, 0); + clear_zbuffer(); + } else { + clear_buffers(0); + } + render_universe(uni, &cam, render); flip(); } + if (bg_img) free_image(bg_img); close_vga(); free(uni); return 0; diff --git a/src/part3D/defines.h b/src/part3D/defines.h index 7bd83f5..9bb0d30 100644 --- a/src/part3D/defines.h +++ b/src/part3D/defines.h @@ -56,6 +56,12 @@ typedef struct { fixed x, y, z; } Vector3; typedef struct { int a, b, c; uint8_t force_wire; } Face; typedef struct { int x, y; fixed z; fixed intensity; } Point2D; +typedef struct { + int width; + int height; + uint8_t *data; +} Image; + /* Un objet dans l'univers : tranche du pool partagé + transform */ typedef struct { int vert_start, num_verts; /* indices dans Universe.verts/v_normals */ @@ -117,6 +123,7 @@ Object3D* add_sphere (Universe *uni, fixed x, fixed y, fixed z, fixed r, Object3D* add_cylinder (Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire); Object3D* add_cone (Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire); Object3D* add_plane (Universe *uni, fixed x, fixed y, fixed z, fixed w, fixed d, int seg_x, int seg_z, uint8_t col, uint8_t wire); +Object3D* add_paraboloid(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, int rings, uint8_t col, uint8_t wire); Object3D* add_torus (Universe *uni, fixed x, fixed y, fixed z, fixed r_main, fixed r_tube, int seg_main, int seg_tube, uint8_t col, uint8_t wire); Object3D* add_math_surface(Universe *uni, fixed x, fixed y, fixed z, fixed w, fixed d, int seg_x, int seg_z, @@ -126,6 +133,10 @@ void render_universe(Universe *uni, Camera *cam, int mode); int init_vga(void); void clear_buffers(uint8_t color); +void clear_zbuffer(void); +void blit_image(Image *img, int x, int y); +void blit_image_transparent(Image *img, int x, int y, uint8_t color_key); +void apply_256_palette(RGB *pal); void flip(void); void close_vga(void); @@ -136,4 +147,8 @@ void save_mesh(Mesh *m, const char *fn); Mesh *load_mesh(const char *fname); void free_mesh(Mesh *m); +Image *create_image(int width, int height); +void free_image(Image *img); +Image *load_bmp8(const char *filename, RGB *pal_out); + #endif diff --git a/src/part3D/engine.c b/src/part3D/engine.c index 4e05fd9..a163938 100644 --- a/src/part3D/engine.c +++ b/src/part3D/engine.c @@ -61,6 +61,7 @@ void mat3_rotate_z(Matrix3 *m, int angle) { } /* Fait pivoter la géométrie locale de l'objet de façon permanente */ +/* void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az) { Matrix3 rx, ry, rz, tmp, rot; Vector3 *v, *n; @@ -70,20 +71,20 @@ void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az) if (!obj || !uni) return; - /* Composition de la matrice de rotation */ + /* Composition de la matrice de rotation * / mat3_rotate_x(&rx, ax); mat3_rotate_y(&ry, ay); mat3_rotate_z(&rz, az); mat3_mul(&tmp, &rx, &ry); mat3_mul(&rot, &tmp, &rz); - /* Application à tous les sommets et normales de l'objet */ + /* Application à tous les sommets et normales de l'objet * / for (i = 0; i < obj->num_verts; i++) { vi = obj->vert_start + i; v = &uni->verts[vi]; n = &uni->v_normals[vi]; - /* Sommets */ + /* Sommets * / vx = f_mul(v->x, rot.m[0][0]) + f_mul(v->y, rot.m[1][0]) + f_mul(v->z, rot.m[2][0]); vy = f_mul(v->x, rot.m[0][1]) + f_mul(v->y, rot.m[1][1]) + f_mul(v->z, rot.m[2][1]); vz = f_mul(v->x, rot.m[0][2]) + f_mul(v->y, rot.m[1][2]) + f_mul(v->z, rot.m[2][2]); @@ -91,7 +92,7 @@ void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az) v->y = vy; v->z = vz; - /* Normales (doivent aussi pivoter pour l'ombrage Gouraud !) */ + /* Normales (doivent aussi pivoter pour l'ombrage Gouraud !) * / vx = f_mul(n->x, rot.m[0][0]) + f_mul(n->y, rot.m[1][0]) + f_mul(n->z, rot.m[2][0]); vy = f_mul(n->x, rot.m[0][1]) + f_mul(n->y, rot.m[1][1]) + f_mul(n->z, rot.m[2][1]); vz = f_mul(n->x, rot.m[0][2]) + f_mul(n->y, rot.m[1][2]) + f_mul(n->z, rot.m[2][2]); @@ -100,7 +101,7 @@ void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az) n->z = vz; } } - +*/ void render_universe(Universe *uni, Camera *cam, int mode) { int o, i, vi, front; fixed rx, ry, rz, pz, nz; diff --git a/src/part3D/geometry.c b/src/part3D/geometry.c index da58e82..ea6b430 100644 --- a/src/part3D/geometry.c +++ b/src/part3D/geometry.c @@ -43,6 +43,68 @@ void set_object_color(Object3D *obj, uint8_t col) { obj->color = col; } +/* Rotation permanente (baking) de la géométrie locale de l'objet */ +void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az) { + int i; + fixed cx, sx, cy, sy, cz, sz; + fixed x, y, z, tx, ty, tz; + Vector3 *v, *n; + + if (!uni || !obj) return; + + /* Normalisation des angles (0-359) */ + ax = ((ax % 360) + 360) % 360; + ay = ((ay % 360) + 360) % 360; + az = ((az % 360) + 360) % 360; + + cx = costab[ax]; sx = sintab[ax]; + cy = costab[ay]; sy = sintab[ay]; + cz = costab[az]; sz = sintab[az]; + + for (i = 0; i < obj->num_verts; i++) { + v = &uni->verts[obj->vert_start + i]; + n = &uni->v_normals[obj->vert_start + i]; + + /* --- Transformation du Sommet --- */ + x = v->x; y = v->y; z = v->z; + if (ax) { + ty = f_mul(y, cx) - f_mul(z, sx); + tz = f_mul(y, sx) + f_mul(z, cx); + y = ty; z = tz; + } + if (ay) { + tx = f_mul(x, cy) + f_mul(z, sy); + tz = -f_mul(x, sy) + f_mul(z, cy); + x = tx; z = tz; + } + if (az) { + tx = f_mul(x, cz) - f_mul(y, sz); + ty = f_mul(x, sz) + f_mul(y, cz); + x = tx; y = ty; + } + v->x = x; v->y = y; v->z = z; + + /* --- Transformation de la Normale --- */ + x = n->x; y = n->y; z = n->z; + if (ax) { + ty = f_mul(y, cx) - f_mul(z, sx); + tz = f_mul(y, sx) + f_mul(z, cx); + y = ty; z = tz; + } + if (ay) { + tx = f_mul(x, cy) + f_mul(z, sy); + tz = -f_mul(x, sy) + f_mul(z, cy); + x = tx; z = tz; + } + if (az) { + tx = f_mul(x, cz) - f_mul(y, sz); + ty = f_mul(x, sz) + f_mul(y, cz); + x = tx; y = ty; + } + n->x = x; n->y = y; n->z = z; + } +} + Object3D *add_box(Universe *uni, fixed x, fixed y, fixed z, fixed dx, fixed dy, fixed dz, uint8_t col, uint8_t wire) { int i, j, v_base; Object3D *obj; @@ -232,6 +294,115 @@ Object3D *add_cone(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, i return obj; } +Object3D *add_paraboloid(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, int rings, uint8_t col, uint8_t wire) { + int i, j, v_base_in, v_base_out; + int th, r_cur, r_next, a, b, c, d; + double t, rad, height, dy_dr, nx_base, ny_base, len, angle; + Object3D *obj; + fixed half_h = h / 2; + + if (uni->num_objects >= MAX_OBJECTS) return NULL; + if (uni->num_verts + 2 * (1 + rings * det) > MAX_VERTEX) return NULL; + if (uni->num_faces + 2 * (det + (rings - 1) * det * 2) > MAX_FACES) return NULL; + + obj = alloc_object(uni); + obj->color = col; + + /* --- FACE INTERIEURE (Bol de l'antenne, normale vers l'avant +Y) --- */ + v_base_in = 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++; + + for (i = 1; i <= rings; i++) { + t = (double)i / rings; + rad = ((double)r / 65536.0) * t; + height = ((double)h / 65536.0) * (t * t); /* Courbe y = x^2 */ + dy_dr = 2.0 * ((double)h / 65536.0) * t / ((double)r / 65536.0); /* Dérivée */ + nx_base = -dy_dr; + ny_base = 1.0; + len = sqrt(nx_base * nx_base + ny_base * ny_base); + nx_base /= len; ny_base /= len; + + for (j = 0; j < det; j++) { + th = (j * 360 / det) % 360; + angle = th * 3.14159265 / 180.0; + uni->v_normals[uni->num_verts].x = (fixed)(nx_base * cos(angle) * 65536.0); + uni->v_normals[uni->num_verts].y = (fixed)(ny_base * 65536.0); + uni->v_normals[uni->num_verts].z = (fixed)(nx_base * sin(angle) * 65536.0); + uni->verts[uni->num_verts].x = x + (fixed)(rad * cos(angle) * 65536.0); + uni->verts[uni->num_verts].y = y - half_h + (fixed)(height * 65536.0); + uni->verts[uni->num_verts].z = z + (fixed)(rad * sin(angle) * 65536.0); + uni->num_verts++; obj->num_verts++; + } + } + for (j = 0; j < det; j++) { + push_face(uni, obj, v_base_in, v_base_in + 1 + ((j + 1) % det), v_base_in + 1 + j, wire); + } + for (i = 1; i < rings; i++) { + r_cur = v_base_in + 1 + (i - 1) * det; + r_next = v_base_in + 1 + i * det; + for (j = 0; j < det; j++) { + a = r_cur + j; + b = r_cur + ((j + 1) % det); + c = r_next + j; + d = r_next + ((j + 1) % det); + push_face(uni, obj, a, b, c, wire); + push_face(uni, obj, b, d, c, wire); + } + } + + /* --- FACE EXTERIEURE (Dos de l'antenne, normale vers l'arrière -Y) --- */ + v_base_out = 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++; + + for (i = 1; i <= rings; i++) { + t = (double)i / rings; + rad = ((double)r / 65536.0) * t; + height = ((double)h / 65536.0) * (t * t); + dy_dr = 2.0 * ((double)h / 65536.0) * t / ((double)r / 65536.0); + nx_base = dy_dr; /* Normale inversée */ + ny_base = -1.0; + len = sqrt(nx_base * nx_base + ny_base * ny_base); + nx_base /= len; ny_base /= len; + + for (j = 0; j < det; j++) { + th = (j * 360 / det) % 360; + angle = th * 3.14159265 / 180.0; + uni->v_normals[uni->num_verts].x = (fixed)(nx_base * cos(angle) * 65536.0); + uni->v_normals[uni->num_verts].y = (fixed)(ny_base * 65536.0); + uni->v_normals[uni->num_verts].z = (fixed)(nx_base * sin(angle) * 65536.0); + uni->verts[uni->num_verts].x = x + (fixed)(rad * cos(angle) * 65536.0); + uni->verts[uni->num_verts].y = y - half_h + (fixed)(height * 65536.0); + uni->verts[uni->num_verts].z = z + (fixed)(rad * sin(angle) * 65536.0); + uni->num_verts++; obj->num_verts++; + } + } + for (j = 0; j < det; j++) { + push_face(uni, obj, v_base_out, v_base_out + 1 + j, v_base_out + 1 + ((j + 1) % det), wire); /* Winding inversé */ + } + for (i = 1; i < rings; i++) { + r_cur = v_base_out + 1 + (i - 1) * det; + r_next = v_base_out + 1 + i * det; + for (j = 0; j < det; j++) { + a = r_cur + j; b = r_cur + ((j + 1) % det); c = r_next + j; d = r_next + ((j + 1) % det); + push_face(uni, obj, a, c, b, wire); /* Winding inversé */ + push_face(uni, obj, b, c, d, wire); + } + } + return obj; +} + Object3D *add_plane(Universe *uni, fixed x, fixed y, fixed z, fixed w, fixed d, int seg_x, int seg_z, uint8_t col, uint8_t wire) { int i, j, v_base; Object3D *obj; diff --git a/src/part3D/graph.c b/src/part3D/graph.c index 83d81bb..1eb7f50 100644 --- a/src/part3D/graph.c +++ b/src/part3D/graph.c @@ -31,6 +31,80 @@ void clear_buffers(uint8_t color) { memset(zbuffer, 0xFF, SCREEN_PIXELS * sizeof(uint16_t)); } +void clear_zbuffer(void) { + memset(zbuffer, 0xFF, SCREEN_PIXELS * sizeof(uint16_t)); +} + +void blit_image(Image *img, int x, int y) { + int j; + int sx, sy, dx, dy, w, h; + uint8_t *src, *dst; + + if (!img || !img->data || !backbuffer) return; + + sx = 0; sy = 0; + dx = x; dy = y; + w = img->width; h = img->height; + + if (dx < 0) { sx = -dx; w += dx; dx = 0; } + if (dy < 0) { sy = -dy; h += dy; dy = 0; } + if (dx + w > SCREEN_W) w = SCREEN_W - dx; + if (dy + h > SCREEN_H) h = SCREEN_H - dy; + + if (w <= 0 || h <= 0) return; + + src = img->data + sy * img->width + sx; + dst = backbuffer + dy * SCREEN_W + dx; + + for (j = 0; j < h; j++) { + memcpy(dst, src, w); + src += img->width; + dst += SCREEN_W; + } +} + +void blit_image_transparent(Image *img, int x, int y, uint8_t color_key) { + int i, j; + int sx, sy, dx, dy, w, h; + uint8_t *src, *dst; + + if (!img || !img->data || !backbuffer) return; + + sx = 0; sy = 0; + dx = x; dy = y; + w = img->width; h = img->height; + + if (dx < 0) { sx = -dx; w += dx; dx = 0; } + if (dy < 0) { sy = -dy; h += dy; dy = 0; } + if (dx + w > SCREEN_W) w = SCREEN_W - dx; + if (dy + h > SCREEN_H) h = SCREEN_H - dy; + + if (w <= 0 || h <= 0) return; + + src = img->data + sy * img->width + sx; + dst = backbuffer + dy * SCREEN_W + dx; + + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + if (src[i] != color_key) { + dst[i] = src[i]; + } + } + src += img->width; + dst += SCREEN_W; + } +} + +void apply_256_palette(RGB *pal) { + int i; + outp(0x3C8, 0); + for (i = 0; i < 256; i++) { + outp(0x3C9, pal[i].r); + outp(0x3C9, pal[i].g); + outp(0x3C9, pal[i].b); + } +} + void flip(void) { memcpy(vga, backbuffer, SCREEN_PIXELS); } diff --git a/src/part3D/image.c b/src/part3D/image.c new file mode 100644 index 0000000..acdce11 --- /dev/null +++ b/src/part3D/image.c @@ -0,0 +1,84 @@ +#include "defines.h" + +Image *create_image(int width, int height) { + Image *img = (Image *)malloc(sizeof(Image)); + if (!img) return NULL; + img->width = width; + img->height = height; + img->data = (uint8_t *)malloc(width * height); + if (!img->data) { + free(img); + return NULL; + } + return img; +} + +void free_image(Image *img) { + if (img) { + if (img->data) free(img->data); + free(img); + } +} + +Image *load_bmp8(const char *filename, RGB *pal_out) { + FILE *f; + uint8_t header[54]; + uint32_t data_offset, header_size, width, height; + int32_t h32; + uint16_t bpp; + int is_top_down = 0; + Image *img; + int i, y; + uint8_t pal_buf[1024]; + + f = fopen(filename, "rb"); + if (!f) return NULL; + + if (fread(header, 1, 54, f) != 54) { fclose(f); return NULL; } + if (header[0] != 'B' || header[1] != 'M') { fclose(f); return NULL; } + + /* Lecture sécurisée (évite les soucis d'alignement mémoire x86) */ + data_offset = header[10] | (header[11] << 8) | (header[12] << 16) | (header[13] << 24); + header_size = header[14] | (header[15] << 8) | (header[16] << 16) | (header[17] << 24); + width = header[18] | (header[19] << 8) | (header[20] << 16) | (header[21] << 24); + h32 = header[22] | (header[23] << 8) | (header[24] << 16) | (header[25] << 24); + bpp = header[28] | (header[29] << 8); + + if (h32 < 0) { is_top_down = 1; height = -h32; } + else { height = h32; } + + if (bpp != 8) { fclose(f); return NULL; } + + img = create_image(width, height); + if (!img) { fclose(f); return NULL; } + + if (pal_out) { + fseek(f, 14 + header_size, SEEK_SET); + fread(pal_buf, 1, 1024, f); + for (i = 0; i < 256; i++) { + /* Le DAC VGA s'attend à des valeurs de 0 à 63 (>> 2) */ + pal_out[i].b = pal_buf[i*4] >> 2; + pal_out[i].g = pal_buf[i*4+1] >> 2; + pal_out[i].r = pal_buf[i*4+2] >> 2; + } + } + + fseek(f, data_offset, SEEK_SET); + + /* Les scanlines d'un BMP sont alignées sur des multiples de 4 octets */ + { + int row_padded = (width + 3) & (~3); + uint8_t *row_data = (uint8_t *)malloc(row_padded); + if (row_data) { + for (i = 0; i < height; i++) { + y = is_top_down ? i : (height - 1 - i); + fread(row_data, 1, row_padded, f); + memcpy(&img->data[y * width], row_data, width); + } + free(row_data); + } + } + + fclose(f); + return img; +} \ No newline at end of file