update Readme

This commit is contained in:
Frater 2026-05-15 14:10:40 +02:00
commit d2775dbd4a
7 changed files with 193 additions and 18 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ voyager.code-workspace
.vscode .vscode
build build
*.err *.err
assets/fond-320200.iff

View file

@ -14,6 +14,17 @@ Affichage : **VGA Mode 13h** — 320×200, 256 couleurs, rendu Gouraud en virgul
* **Génération procédurale :** Création dynamique de géométries (Cubes, Sphères, Cylindres, Cônes, Plans, Tores, Surfaces mathématiques). * **Génération procédurale :** Création dynamique de géométries (Cubes, Sphères, Cylindres, Cônes, Plans, Tores, Surfaces mathématiques).
* **Mode "Minimal Player" :** Capacité d'exporter la scène entière en un header statique C pour compiler un exécutable ultra-léger sans les fonctions de génération mathématique. * **Mode "Minimal Player" :** Capacité d'exporter la scène entière en un header statique C pour compiler un exécutable ultra-léger sans les fonctions de génération mathématique.
---
## Exemples
![Screenshot FlatShading](/assets/Capture-Flatshading.png)
Vue en flatshading
![Screenshot Gouraud](/assets/Capture-Gouraud.png)
Vue en Gouraud
--- ---
## Structure du projet ## Structure du projet

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
assets/Capture-Gouraud.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -7,6 +7,7 @@ int main(void) {
Object3D *tank, *pws1, *pws2, *inst1, *inst2; Object3D *tank, *pws1, *pws2, *inst1, *inst2;
Camera cam; Camera cam;
Matrix3 rotX, rotY, orbit; Matrix3 rotX, rotY, orbit;
Image *theme_img = NULL;
Image *bg_img = NULL; Image *bg_img = NULL;
int special=0; int special=0;
int anglex, angley, key, render, o; int anglex, angley, key, render, o;
@ -15,7 +16,6 @@ int main(void) {
printf("Moteur 3D DOS (TESTING) - Version 0.15\n"); printf("Moteur 3D DOS (TESTING) - Version 0.15\n");
printf("Compile le %s a %s\n\n", __DATE__, __TIME__); printf("Compile le %s a %s\n\n", __DATE__, __TIME__);
printf("Appuyez sur une touche pour demarrer le rendu...\n"); printf("Appuyez sur une touche pour demarrer le rendu...\n");
getch();
anglex = angley = key = 0; anglex = angley = key = 0;
render = MODE_SOLID; render = MODE_SOLID;
@ -31,88 +31,104 @@ int main(void) {
/* --- Construction de la sonde Voyager --- */ /* --- Construction de la sonde Voyager --- */
/* 1. Bus principal (décagone blanc) */ /* 1. Bus principal (décagone blanc) */
bus = add_cylinder(uni, 0, 0, 0, int_to_f(12), int_to_f(10), 10, COLOR7, 0); bus = add_cylinder(uni, 0, 0, 0, int_to_f(12), int_to_f(10), 10, COLOR9, 0);
set_object_pos(bus, 0, 0, 0); set_object_pos(bus, 0, 0, 0);
/* 1b. Réservoir d'hydrazine central (sphère dépassant sous le bus) */ /* 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); tank = add_sphere(uni, 0, 0, 0, int_to_f(6), 8, COLOR9, 0);
set_object_pos(tank, 0, int_to_f(-3), 0); set_object_pos(tank, 0, int_to_f(-3), 0);
/* 2a. Antenne parabolique (Paraboloïde avec courbure réaliste) */ /* 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); dish = add_paraboloid(uni, 0, 0, 0, int_to_f(25), int_to_f(8), 24, 4, COLOR9, 0);
/* Pointant déjà vers le haut (+Y), on abaisse simplement sa base pour se reposer sur le bus */ /* 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); set_object_pos(dish, 0, int_to_f(9), 0);
/* 2b. Mât central de l'antenne (treillis triangulaire) */ /* 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); feed_boom = add_cylinder(uni, 0, 0, 0, int_to_f(1)/2, int_to_f(10), 3, COLOR9, 1);
set_object_pos(feed_boom, 0, int_to_f(10), 0); set_object_pos(feed_boom, 0, int_to_f(10), 0);
/* 2c. Sous-réflecteur de l'antenne (petit cône inversé) */ /* 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); sub_dish = add_cone(uni, 0, 0, 0, int_to_f(3), int_to_f(2), 12, COLOR9, 0);
bake_object_rotation(uni, sub_dish, 180, 0, 0); bake_object_rotation(uni, sub_dish, 180, 0, 0);
set_object_pos(sub_dish, 0, int_to_f(16), 0); set_object_pos(sub_dish, 0, int_to_f(16), 0);
/* 3. Mât du RTG (Générateur thermoélectrique, structure en treillis carrée) */ /* 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); rtg_boom = add_cylinder(uni, 0, 0, 0, int_to_f(2), int_to_f(40), 4, COLOR8, 1);
bake_object_rotation(uni, rtg_boom, 0, 0, 90); /* Couché sur l'axe X */ 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); 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) */ /* 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); rtg1 = add_cylinder(uni, 0, 0, 0, int_to_f(4), int_to_f(5), 8, COLOR8, 0);
bake_object_rotation(uni, rtg1, 0, 0, 90); bake_object_rotation(uni, rtg1, 0, 0, 90);
set_object_pos(rtg1, int_to_f(48), int_to_f(-2), 0); 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); rtg2 = add_cylinder(uni, 0, 0, 0, int_to_f(4), int_to_f(5), 8, COLOR8, 0);
bake_object_rotation(uni, rtg2, 0, 0, 90); bake_object_rotation(uni, rtg2, 0, 0, 90);
set_object_pos(rtg2, int_to_f(54), int_to_f(-2), 0); 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); rtg3 = add_cylinder(uni, 0, 0, 0, int_to_f(4), int_to_f(5), 8, COLOR8, 0);
bake_object_rotation(uni, rtg3, 0, 0, 90); bake_object_rotation(uni, rtg3, 0, 0, 90);
set_object_pos(rtg3, int_to_f(60), int_to_f(-2), 0); 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) */ /* 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); mag_boom = add_cylinder(uni, 0, 0, 0, int_to_f(1), int_to_f(70), 3, COLOR8, 1);
bake_object_rotation(uni, mag_boom, 0, 0, 90); bake_object_rotation(uni, mag_boom, 0, 0, 90);
set_object_pos(mag_boom, int_to_f(-40), int_to_f(-2), 0); 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) */ /* 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); sci_boom = add_cylinder(uni, 0, 0, 0, int_to_f(2), int_to_f(30), 4, COLOR8, 1);
bake_object_rotation(uni, sci_boom, 90, 0, 0); /* Couché sur l'axe Z */ 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)); set_object_pos(sci_boom, 0, int_to_f(-2), int_to_f(-20));
/* 7. Plateforme et caméras (cube gris) */ /* 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); cam_box = add_box(uni, 0, 0, 0, int_to_f(6), int_to_f(6), int_to_f(6), COLOR8, 0);
set_object_pos(cam_box, 0, int_to_f(-2), int_to_f(-35)); 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) */ /* 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); record = add_cylinder(uni, 0, 0, 0, int_to_f(3), int_to_f(1), 10, COLOR5, 0);
bake_object_rotation(uni, record, 0, 0, 90); bake_object_rotation(uni, record, 0, 0, 90);
set_object_pos(record, int_to_f(12), int_to_f(0), int_to_f(5)); 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 */ /* 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); pws1 = add_cylinder(uni, 0, 0, 0, int_to_f(1)/2, int_to_f(60), 3, COLOR8, 1);
bake_object_rotation(uni, pws1, 0, 0, 90); /* 1. On le couche sur l'axe X */ 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 */ 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)); 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); pws2 = add_cylinder(uni, 0, 0, 0, int_to_f(1)/2, int_to_f(60), 3, COLOR8, 1);
bake_object_rotation(uni, pws2, 0, 0, 90); /* 1. On le couche sur l'axe X */ 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 */ 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)); 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) */ /* 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); inst1 = add_cylinder(uni, 0, 0, 0, int_to_f(2), int_to_f(8), 6, COLOR9, 0);
bake_object_rotation(uni, inst1, 90, 0, 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)); 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); inst2 = add_cone(uni, 0, 0, 0, int_to_f(2), int_to_f(6), 8, COLOR9, 0);
bake_object_rotation(uni, inst2, 90, 0, 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)); 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; cam.pos.x = cam.pos.y = cam.pos.z = 0;
getch();
if (init_vga() != 0) { free(uni); return 1; } if (init_vga() != 0) { free(uni); return 1; }
/* --- Thème de la palette --- */
/* Charge une image (ex: IFF 16 couleurs) pour en extraire la palette de base */
{
RGB base_palette[256]; /* Prévoir assez grand au cas où */
theme_img = load_iff8("THEME.LBM", base_palette);
if (theme_img) {
printf("Theme de couleurs 'THEME.LBM' charge. Generation des rampes de Gouraud...\n");
setup_vga_palette(base_palette);
remap_image_to_gouraud(theme_img);
bg_img = theme_img; /* On la conserve pour l'afficher en fond d'écran ! */
}
}
/* Optionnel : charger un fond (BMP 8-bit de 320x200 couleurs indexees) */ /* 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 ! */ /* S'assurer que le fichier est bien généré avec les couleurs de votre palette moteur ! */
/* bg_img = load_bmp8("stars.bmp", NULL); */ /* bg_img = load_bmp8("stars.bmp", NULL); */
@ -185,6 +201,7 @@ int main(void) {
flip(); flip();
} }
/* theme_img a déjà été libéré s'il a été chargé */
if (bg_img) free_image(bg_img); if (bg_img) free_image(bg_img);
close_vga(); close_vga();
free(uni); free(uni);

View file

@ -153,5 +153,7 @@ void free_mesh(Mesh *m);
Image *create_image(int width, int height); Image *create_image(int width, int height);
void free_image(Image *img); void free_image(Image *img);
Image *load_bmp8(const char *filename, RGB *pal_out); Image *load_bmp8(const char *filename, RGB *pal_out);
Image *load_iff8(const char *filename, RGB *pal_out);
void remap_image_to_gouraud(Image *img);
#endif #endif

View file

@ -20,6 +20,28 @@ void free_image(Image *img) {
} }
} }
void remap_image_to_gouraud(Image *img) {
int i, total;
if (!img || !img->data) return;
total = img->width * img->height;
for (i = 0; i < total; i++) {
/* Transforme l'index 0-15 vers l'index de luminosité max de sa rampe */
img->data[i] = (img->data[i] & 0x0F) << 4 | 15;
}
}
static uint32_t read_be32(FILE *f) {
uint8_t b[4];
if (fread(b, 1, 4, f) != 4) return 0;
return ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | b[3];
}
static uint16_t read_be16(FILE *f) {
uint8_t b[2];
if (fread(b, 1, 2, f) != 2) return 0;
return (b[0] << 8) | b[1];
}
Image *load_bmp8(const char *filename, RGB *pal_out) { Image *load_bmp8(const char *filename, RGB *pal_out) {
FILE *f; FILE *f;
uint8_t header[54]; uint8_t header[54];
@ -79,6 +101,128 @@ Image *load_bmp8(const char *filename, RGB *pal_out) {
} }
} }
fclose(f);
return img;
}
Image *load_iff8(const char *filename, RGB *pal_out) {
FILE *f;
char buf[4];
char type[4];
int is_ilbm, is_pbm;
int width = 0, height = 0, nPlanes = 0, masking = 0, compression = 0;
long body_offset = 0;
Image *img;
int planes_to_read, row_bytes;
uint8_t *row_buf;
int y, p;
f = fopen(filename, "rb");
if (!f) return NULL;
if (fread(buf, 1, 4, f) != 4 || strncmp(buf, "FORM", 4) != 0) { fclose(f); return NULL; }
read_be32(f); /* Ignore form_size */
if (fread(type, 1, 4, f) != 4) { fclose(f); return NULL; }
is_ilbm = (strncmp(type, "ILBM", 4) == 0);
is_pbm = (strncmp(type, "PBM ", 4) == 0);
if (!is_ilbm && !is_pbm) { fclose(f); return NULL; }
while (fread(buf, 1, 4, f) == 4) {
uint32_t chunk_size = read_be32(f);
long next_chunk = ftell(f) + chunk_size;
if (chunk_size & 1) next_chunk++; /* Les chunks IFF sont alignés sur 16-bit */
if (strncmp(buf, "BMHD", 4) == 0) {
width = read_be16(f);
height = read_be16(f);
fseek(f, 4, SEEK_CUR); /* Skip x, y */
nPlanes = fgetc(f);
masking = fgetc(f);
compression = fgetc(f);
}
else if (strncmp(buf, "CMAP", 4) == 0 && pal_out) {
int num_colors = chunk_size / 3;
int i;
if (num_colors > 256) num_colors = 256;
for (i = 0; i < num_colors; i++) {
pal_out[i].r = (uint8_t)(fgetc(f) >> 2); /* Conversion 8-bit vers VGA DAC 6-bit */
pal_out[i].g = (uint8_t)(fgetc(f) >> 2);
pal_out[i].b = (uint8_t)(fgetc(f) >> 2);
}
}
else if (strncmp(buf, "BODY", 4) == 0) {
body_offset = ftell(f);
break; /* Fin de l'en-tête, les données d'image commencent ici */
}
fseek(f, next_chunk, SEEK_SET);
}
if (!width || !height || !nPlanes || body_offset == 0) { fclose(f); return NULL; }
img = create_image(width, height);
if (!img) { fclose(f); return NULL; }
fseek(f, body_offset, SEEK_SET);
planes_to_read = is_ilbm ? (nPlanes + (masking == 1 ? 1 : 0)) : 1;
row_bytes = is_ilbm ? ((width + 15) / 16) * 2 : (width + 1) & ~1;
row_buf = (uint8_t *)malloc(row_bytes * planes_to_read);
if (!row_buf) { free_image(img); fclose(f); return NULL; }
for (y = 0; y < height; y++) {
for (p = 0; p < planes_to_read; p++) {
uint8_t *plane_dst = row_buf + p * row_bytes;
int bytes_to_read = row_bytes;
if (compression == 0) {
fread(plane_dst, 1, bytes_to_read, f);
} else if (compression == 1) {
while (bytes_to_read > 0 && !feof(f)) {
int n = (signed char)fgetc(f);
if (n >= 0 && n <= 127) { /* Copie littérale */
int count = n + 1;
int read_len = (count > bytes_to_read) ? bytes_to_read : count;
fread(plane_dst, 1, read_len, f);
if (count > read_len) fseek(f, count - read_len, SEEK_CUR);
plane_dst += read_len;
bytes_to_read -= read_len;
} else if (n >= -127 && n <= -1) { /* RLE (répétition) */
int count = -n + 1;
int val = fgetc(f);
int fill_len = (count > bytes_to_read) ? bytes_to_read : count;
memset(plane_dst, val, fill_len);
plane_dst += fill_len;
bytes_to_read -= fill_len;
} /* n == -128 est un no-op en PackBits */
}
}
}
if (is_pbm) {
/* PBM est chunky : copie directe */
memcpy(img->data + y * width, row_buf, width);
} else if (is_ilbm) {
/* ILBM est planar : on recombine les bits en chunky */
uint8_t *dest_row = img->data + y * width;
int x;
memset(dest_row, 0, width);
for (p = 0; p < nPlanes; p++) {
uint8_t *plane_src = row_buf + p * row_bytes;
for (x = 0; x < width; x++) {
int byte_idx = x / 8;
int bit_idx = 7 - (x % 8);
if (plane_src[byte_idx] & (1 << bit_idx)) {
dest_row[x] |= (1 << p);
}
}
}
}
}
free(row_buf);
fclose(f); fclose(f);
return img; return img;
} }