diff --git a/README.md b/README.md index e434a6f..40e1747 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ## Vue d'ensemble -Moteur 3D software en C ciblant le DOS 32-bit via l'extender **CauseWay** (ou DOS/4GW). -Compilateur : **OpenWatcom wcc386** exécuté depuis WSL. +Moteur 3D software en C ciblant le DOS 32-bit via l'extender **CauseWay** (ou DOS/4GW). +Compilateur : **OpenWatcom wcc386** exécuté depuis WSL. Affichage : **VGA Mode 13h** — 320×200, 256 couleurs, rendu Gouraud en virgule fixe 16.16. --- @@ -72,9 +72,9 @@ 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é**. -Index pixel = `(base_color << 4) | shade` avec shade ∈ [0..15]. -Les 16 couleurs de base sont dans `my_palette[]` (`graph.c`). +256 entrées organisées en **16 couleurs × 16 niveaux d'intensité**. +Index pixel = `(base_color << 4) | shade` avec shade ∈ [0..15]. +Les 16 couleurs de base sont dans `my_palette[]` (`graph.c`). La palette est chargée dans le DAC VGA au démarrage via `setup_vga_palette()`. --- @@ -86,7 +86,7 @@ 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)); ``` -`<< 8` = distance focale implicite de 256 pixels. +`<< 8` = distance focale implicite de 256 pixels. `pz` est clampé à `int_to_f(20)` minimum pour éviter la division par zéro. --- @@ -123,7 +123,7 @@ intensity = (nz >= 0) ? int_to_f(1) : f_mul(-nz, int_to_f(14)) + int_to_f(1); | 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). --- @@ -132,29 +132,65 @@ Le shade final est clampé dans [0, 15] — y compris vers le bas (intensité n ### Sphère (`add_sphere`) -Paramètres : centre local, rayon `r`, détail `det` (nombre de subdivisions). -Génère `(det+1) × det` sommets et `det × det × 2` faces. -Les normales sont les vecteurs de position unitaires (normales sphériques exactes). +Paramètres : centre local, rayon `r`, détail `det` (nombre de subdivisions). +Génère `(det+1) × det` sommets et `det × det × 2` faces. +Les normales sont les vecteurs de position unitaires (normales sphériques exactes). Winding : `add_face(a, b, c)` et `add_face(b, d, c)` — CCW vu de l'extérieur. ### Cylindre (`add_cylinder`) Paramètres : centre local, rayon `r`, hauteur `h`, détail `det`. -**Corps (barrel) :** -`det` paires de sommets bas/haut. Normales radiales `(cos θ, 0, sin θ)`. +**Corps (barrel) :** +`det` paires de sommets bas/haut. Normales radiales `(cos θ, 0, sin θ)`. `2×det` faces. Winding : `(bas_i, haut_i, bas_{i+1})` et `(haut_i, haut_{i+1}, bas_{i+1})`. -**Fond bas :** -1 sommet centre + `det` sommets de rebord. Normale `(0, −1, 0)`. +**Fond bas :** +1 sommet centre + `det` sommets de rebord. Normale `(0, −1, 0)`. `det` faces en éventail depuis le centre. Winding : `(centre, rebord[i], rebord[i+1])`. -**Fond haut :** -1 sommet centre + `det` sommets de rebord. Normale `(0, +1, 0)`. +**Fond haut :** +1 sommet centre + `det` sommets de rebord. Normale `(0, +1, 0)`. `det` faces en éventail depuis le centre. Winding inversé (normale opposée) : `(centre, rebord[i+1], rebord[i])`. **Total par cylindre (det=16) :** 2+4×det = 66 sommets, 4×det = 64 faces. +### Boîte / Cube (`add_box`) +Paramètres : centre local, dimensions indépendantes `dx, dy, dz`. +Génère 24 sommets (4 par face pour ne pas lisser les arêtes avec Gouraud) et 12 faces. +Normales : alignées sur les axes orthogonaux (±X, ±Y, ±Z). + +### Cône (`add_cone`) +Paramètres : centre local, rayon `r`, hauteur `h`, détail `det`. +Dérivé du cylindre, mais la boucle supérieure converge en un point unique avec des sommets dupliqués pour préserver l'interpolation radiale des normales du corps. Ne possède qu'un "fond bas". + +### Plan / Grille (`add_plane`) +Paramètres : centre local, largeur `w`, profondeur `d`, segments `seg_x`, `seg_z`. +Génère une surface plane subdivisée (idéal pour les sols et l'ombrage Gouraud par point). +Normales : dirigées vers le haut (+Y). + +### Tore (`add_torus`) +Paramètres : centre local, rayon principal `r_main`, rayon du tube `r_tube`, subdivisions `seg_main`, `seg_tube`. +Génère un anneau ("donut") via deux boucles trigonométriques imbriquées. Excellente primitive de test pour l'éclairage. + +### Surface Mathématique (`add_math_surface`) +Paramètres : similaires au plan, plus un pointeur de fonction `SurfaceFunc`. +Cette primitive génère une grille dont la hauteur Y est calculée dynamiquement par la fonction utilisateur passée en paramètre. +La fonction utilisateur doit respecter la signature : `fixed ma_fonction(fixed x, fixed z)`. + +``` +/* Exemple de formule : le "Sombrero" */ +fixed mon_onde(fixed x, fixed z) { + double dx = (double)x / 65536.0; + double dz = (double)z / 65536.0; + double dist = sqrt(dx * dx + dz * dz); + return (fixed)(20.0 * sin(dist / 10.0) * 65536.0); +} + +/* Appel dans le main */ +add_math_surface(uni, 0, 0, 0, int_to_f(120), int_to_f(120), 30, 30, COLOR6, 0, mon_onde); +``` + --- ## Orbite de l'observateur (`main.c`) diff --git a/src/part3D/defines.h b/src/part3D/defines.h index 720a6bf..949dcba 100644 --- a/src/part3D/defines.h +++ b/src/part3D/defines.h @@ -87,6 +87,10 @@ extern uint16_t *zbuffer; extern uint8_t *vga; /* Prototypes */ + +/* Type pour les fonctions de génération de surface : reçoit x, z et renvoie y */ +typedef fixed (*SurfaceFunc)(fixed x, fixed z); + void init_engine_math(void); void mat3_identity(Matrix3 *mat); void mat3_rotate_x(Matrix3 *m, int angle); @@ -103,8 +107,10 @@ 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_math_surface(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_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, + uint8_t col, uint8_t wire, SurfaceFunc func); void render_universe(Universe *uni, Camera *cam, int mode); diff --git a/src/part3D/geometry.c b/src/part3D/geometry.c index 5130191..c7308b2 100644 --- a/src/part3D/geometry.c +++ b/src/part3D/geometry.c @@ -245,7 +245,9 @@ Object3D *add_plane(Universe *uni, fixed x, fixed y, fixed z, fixed w, fixed d, return obj; } -Object3D *add_math_surface(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_math_surface(Universe *uni, fixed x, fixed y, fixed z, + fixed w, fixed d, int seg_x, int seg_z, + uint8_t col, uint8_t wire, SurfaceFunc func) { int i, j, v_base; Object3D *obj; fixed h_w = w / 2, h_d = d / 2; @@ -256,28 +258,22 @@ Object3D *add_math_surface(Universe *uni, fixed x, fixed y, fixed z, fixed w, fi if (uni->num_verts + (seg_x + 1) * (seg_z + 1) > MAX_VERTEX) return NULL; if (uni->num_faces + seg_x * seg_z * 2 > MAX_FACES) return NULL; - obj = alloc_object(uni); obj->color = col; v_base = uni->num_verts; - step_x = w / seg_x; step_z = d / seg_z; + obj = alloc_object(uni); + obj->color = col; + v_base = uni->num_verts; + step_x = w / seg_x; + step_z = d / seg_z; for (j = 0; j <= seg_z; j++) { for (i = 0; i <= seg_x; i++) { fixed vx = x - h_w + i * step_x; fixed vz = z - h_d + j * step_z; - /* --- Application de la formule mathématique --- */ - /* 1. Conversion en flottant pour éviter les débordements avec x*x */ - double dx = (double)(vx - x) / 65536.0; - double dz = (double)(vz - z) / 65536.0; - - /* 2. La formule : Y = Amplitude * sin(Fréquence * sqrt(X^2 + Z^2)) */ - double dist = sqrt(dx * dx + dz * dz); - double fy = 20.0 * sin(dist / 10.0); /* 20.0 = amplitude, 10.0 = étalement */ - uni->verts[uni->num_verts].x = vx; - uni->verts[uni->num_verts].y = y + (fixed)(fy * 65536.0); + /* Appel de la fonction passée en paramètre pour obtenir la hauteur */ + uni->verts[uni->num_verts].y = y + func(vx, vz); uni->verts[uni->num_verts].z = vz; - /* Normale provisoire vers le haut */ 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; @@ -285,7 +281,6 @@ Object3D *add_math_surface(Universe *uni, fixed x, fixed y, fixed z, fixed w, fi } } - /* Génération des faces (identique au plan) */ for (j = 0; j < seg_z; j++) { for (i = 0; i < seg_x; i++) { int a = v_base + j * (seg_x + 1) + i;