dos-3D-Engine/WORKDOC.md
2026-05-10 02:29:00 +02:00

187 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WORKDOC — Moteur 3D DOS (TESTING)
## 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.
Affichage : **VGA Mode 13h** — 320×200, 256 couleurs, rendu Gouraud en virgule fixe 16.16.
---
## Structure du projet
```
src/
main/main.c Point d'entrée, scène, boucle principale
part3D/
defines.h Types, macros fixed-point, 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
raster.c Rastériseur triangle Gouraud, tracé de ligne Z-buffered
build/
app.exe Binaire DOS final (CauseWay)
link.lnk Script de link généré automatiquement
Makefile Build via WSL + wcc386 Windows natif
```
---
## Pipeline de rendu
```
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
mat3_rotate_x/y Construction matrices de rotation
mat3_mul (orbite) Composition de la matrice d'orbite
→ obj.rot = orbit Application uniforme à tous les objets
→ obj.pos = orbit × base_pos + center Rotation des positions
clear_buffers Effacement backbuffer + zbuffer (0xFFFF)
render_universe Transformation + projection + Gouraud fill
flip memcpy backbuffer → 0xA0000 (VGA)
```
---
## Arithmétique virgule fixe 16.16
| Macro/fonction | Opération |
|---|---|
| `int_to_f(a)` | `a << 16` |
| `f_to_int(a)` | `a >> 16` |
| `f_mul(a,b)` | `(int64_t)a * b >> 16` |
| `f_div(a,b)` | `(int64_t)a << 16 / b` |
---
## 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`).
La palette est chargée dans le DAC VGA au démarrage via `setup_vga_palette()`.
---
## Projection perspective
```c
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.
`pz` est clampé à `int_to_f(20)` minimum pour éviter la division par zéro.
---
## Back-face culling et convention de winding
Le pipeline applique la **transposée** de la matrice de rotation aux sommets (convention colonne Watcom). Cette transposition inverse le handedness des triangles projetés.
**Convention retenue :** winding CCW vu de l'extérieur → produit vectoriel 2D **> 0** en espace écran → face visible.
```c
front = ((p2->x - p1->x) * (p3->y - p1->y)
- (p2->y - p1->y) * (p3->x - p1->x)) > 0;
```
Toutes les géométries (sphère, corps cylindre, fonds cylindre) respectent cette convention.
---
## Shading Gouraud
La lumière est assimilée à la direction de vue (−Z monde). Le dot product de la normale rotée avec (0,0,−1) est `−nz`.
```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);
```
| 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.
---
## Génération des meshes
### 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).
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 θ)`.
`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)`.
`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)`.
`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.
---
## Orbite de l'observateur (`main.c`)
Les objets ont des positions de base fixes dans l'espace scène, définies par rapport au centre `(0, 0, 300)` :
| Objet | Offset base |
|---|---|
| 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 :
```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]);
```
La caméra reste à l'origine (+ translations clavier). L'effet visuel est celui d'un observateur qui orbite autour de la scène.
---
## Contrôles clavier
| Touche | Action |
|---|---|
| Z / S | Caméra avance / recule (axe Z) |
| Q / D | Caméra gauche / droite (axe X) |
| A / E | Caméra haut / bas (axe Y) |
| + / − | Orbite vertical (anglex ±3°) |
| / / * | Orbite horizontal (angley ±3°) |
| R | Reset caméra + angles |
| Esc | Quitter |
---
## Points de vigilance
| Sujet | Note |
|---|---|
| Winding | Toutes les géométries doivent respecter la convention CCW (cross > 0). Vérifier à chaque ajout de primitive. |
| 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. |
| I/O mesh | `save_mesh` / `load_mesh` / `free_mesh` disponibles dans `geometry.c` mais non utilisées depuis `main.c`. |