187 lines
6.5 KiB
Markdown
187 lines
6.5 KiB
Markdown
# 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`. |
|