update Readme
This commit is contained in:
parent
0d98a2077b
commit
d2775dbd4a
7 changed files with 193 additions and 18 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@ voyager.code-workspace
|
|||
.vscode
|
||||
build
|
||||
*.err
|
||||
assets/fond-320200.iff
|
||||
|
|
|
|||
11
README.md
11
README.md
|
|
@ -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).
|
||||
* **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
|
||||
|
||||

|
||||
Vue en flatshading
|
||||
|
||||

|
||||
Vue en Gouraud
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Structure du projet
|
||||
|
|
|
|||
BIN
assets/Capture-Flatshading.png
Normal file
BIN
assets/Capture-Flatshading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
BIN
assets/Capture-Gouraud.png
Normal file
BIN
assets/Capture-Gouraud.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
|
|
@ -7,6 +7,7 @@ int main(void) {
|
|||
Object3D *tank, *pws1, *pws2, *inst1, *inst2;
|
||||
Camera cam;
|
||||
Matrix3 rotX, rotY, orbit;
|
||||
Image *theme_img = NULL;
|
||||
Image *bg_img = NULL;
|
||||
int special=0;
|
||||
int anglex, angley, key, render, o;
|
||||
|
|
@ -15,7 +16,6 @@ int main(void) {
|
|||
printf("Moteur 3D DOS (TESTING) - Version 0.15\n");
|
||||
printf("Compile le %s a %s\n\n", __DATE__, __TIME__);
|
||||
printf("Appuyez sur une touche pour demarrer le rendu...\n");
|
||||
getch();
|
||||
|
||||
anglex = angley = key = 0;
|
||||
render = MODE_SOLID;
|
||||
|
|
@ -31,88 +31,104 @@ int main(void) {
|
|||
/* --- Construction de la sonde Voyager --- */
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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 */
|
||||
set_object_pos(dish, 0, int_to_f(9), 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);
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
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) */
|
||||
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 */
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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 */
|
||||
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);
|
||||
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));
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
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, 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);
|
||||
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, -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);
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
|
||||
getch();
|
||||
|
||||
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) */
|
||||
/* S'assurer que le fichier est bien généré avec les couleurs de votre palette moteur ! */
|
||||
/* bg_img = load_bmp8("stars.bmp", NULL); */
|
||||
|
|
@ -185,6 +201,7 @@ int main(void) {
|
|||
flip();
|
||||
}
|
||||
|
||||
/* theme_img a déjà été libéré s'il a été chargé */
|
||||
if (bg_img) free_image(bg_img);
|
||||
close_vga();
|
||||
free(uni);
|
||||
|
|
|
|||
|
|
@ -153,5 +153,7 @@ 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);
|
||||
Image *load_iff8(const char *filename, RGB *pal_out);
|
||||
void remap_image_to_gouraud(Image *img);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
FILE *f;
|
||||
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);
|
||||
return img;
|
||||
}
|
||||
Loading…
Reference in a new issue