running ok
This commit is contained in:
parent
c6d6f787f7
commit
8e30b7692f
8 changed files with 371 additions and 356 deletions
4
Makefile
4
Makefile
|
|
@ -84,5 +84,5 @@ clean:
|
||||||
rm -f *.err src/**/*.err
|
rm -f *.err src/**/*.err
|
||||||
|
|
||||||
cleanall:
|
cleanall:
|
||||||
$(MAKE) clean
|
@$(MAKE) clean
|
||||||
$(MAKE) all
|
@$(MAKE) all
|
||||||
47
WORKDOC.md
47
WORKDOC.md
|
|
@ -14,7 +14,7 @@ Affichage : **VGA Mode 13h** — 320×200, 256 couleurs, rendu Gouraud en virgul
|
||||||
src/
|
src/
|
||||||
main/main.c Point d'entrée, scène, boucle principale
|
main/main.c Point d'entrée, scène, boucle principale
|
||||||
part3D/
|
part3D/
|
||||||
defines.h Types, macros fixed-point, prototypes, externs
|
defines.h Types, macros fixed-point, constantes écran, prototypes, externs
|
||||||
engine.c Maths (sin/cos), matrices, pipeline de rendu
|
engine.c Maths (sin/cos), matrices, pipeline de rendu
|
||||||
geometry.c Génération de meshes (sphère, cylindre) + I/O mesh
|
geometry.c Génération de meshes (sphère, cylindre) + I/O mesh
|
||||||
graph.c VGA init, palette, backbuffer/zbuffer
|
graph.c VGA init, palette, backbuffer/zbuffer
|
||||||
|
|
@ -33,11 +33,11 @@ Makefile Build via WSL + wcc386 Windows natif
|
||||||
init_engine_math() Précalcul sin/cos (tables entières 16.16)
|
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)
|
add_sphere / add_cylinder Génération mesh en espace local (0,0,0)
|
||||||
─── Boucle principale ───
|
─── Boucle principale ───
|
||||||
kbhit / getch Gestion clavier
|
kbhit / getch Gestion clavier (touches étendues consommées en deux appels)
|
||||||
mat3_rotate_x/y Construction matrices de rotation
|
mat3_rotate_x/y Construction matrices de rotation
|
||||||
mat3_mul (orbite) Composition de la matrice d'orbite
|
mat3_mul (orbite) Composition de la matrice d'orbite (sûre contre l'aliasing)
|
||||||
→ obj.rot = orbit Application uniforme à tous les objets
|
→ obj.rot = orbit Application uniforme à tous les objets
|
||||||
→ obj.pos = orbit × base_pos + center Rotation des positions
|
→ obj.pos = orbit × base_pos + center Rotation 3D complète des positions
|
||||||
clear_buffers Effacement backbuffer + zbuffer (0xFFFF)
|
clear_buffers Effacement backbuffer + zbuffer (0xFFFF)
|
||||||
render_universe Transformation + projection + Gouraud fill
|
render_universe Transformation + projection + Gouraud fill
|
||||||
flip memcpy backbuffer → 0xA0000 (VGA)
|
flip memcpy backbuffer → 0xA0000 (VGA)
|
||||||
|
|
@ -56,6 +56,20 @@ add_sphere / add_cylinder Génération mesh en espace local (0,0,0)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Constantes écran
|
||||||
|
|
||||||
|
Définies dans `defines.h` :
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define SCREEN_W 320
|
||||||
|
#define SCREEN_H 200
|
||||||
|
#define SCREEN_PIXELS (SCREEN_W * SCREEN_H) /* 64 000 */
|
||||||
|
```
|
||||||
|
|
||||||
|
Utilisées partout dans `graph.c` et `raster.c` à la place du littéral `64000`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Palette VGA
|
## Palette VGA
|
||||||
|
|
||||||
256 entrées organisées en **16 couleurs × 16 niveaux d'intensité**.
|
256 entrées organisées en **16 couleurs × 16 niveaux d'intensité**.
|
||||||
|
|
@ -98,16 +112,19 @@ La lumière est assimilée à la direction de vue (−Z monde). Le dot product d
|
||||||
|
|
||||||
```c
|
```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]);
|
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);
|
intensity = (nz >= 0) ? int_to_f(1) : f_mul(-nz, int_to_f(14)) + int_to_f(1);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`f_mul(-nz, int_to_f(14))` est une multiplication en virgule fixe 16.16 (≠ multiplication entière).
|
||||||
|
|
||||||
| nz | Signification | Intensité (shade) |
|
| nz | Signification | Intensité (shade) |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| −1.0 | Face directement vers caméra | 15 (max) |
|
| −1.0 | Face directement vers caméra | 15 (max) |
|
||||||
| 0 | Face tangentielle | 1 (ambiant) |
|
| 0 | Face tangentielle | 1 (ambiant) |
|
||||||
| > 0 | Face opposée à la caméra | 1 (ambiant, face culléé) |
|
| > 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).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -149,15 +166,17 @@ Les objets ont des positions de base fixes dans l'espace scène, définies par r
|
||||||
| Sphère (universe[0]) | (+60, 0, 0) |
|
| Sphère (universe[0]) | (+60, 0, 0) |
|
||||||
| Cylindre (universe[1]) | (−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 :
|
Chaque frame, la matrice d'orbite `orbit = rotX(anglex) × rotY(angley)` est calculée et appliquée uniformément à chaque objet (transform 3D complet sur `base_pos`) :
|
||||||
|
|
||||||
```c
|
```c
|
||||||
universe[o].rot = orbit;
|
obj->rot = orbit;
|
||||||
universe[o].pos.x = f_mul(base_x, orbit.m[0][0]);
|
obj->pos.x = f_mul(bx, orbit.m[0][0]) + f_mul(by, orbit.m[1][0]) + f_mul(bz, orbit.m[2][0]);
|
||||||
universe[o].pos.y = f_mul(base_x, orbit.m[0][1]);
|
obj->pos.y = f_mul(bx, orbit.m[0][1]) + f_mul(by, orbit.m[1][1]) + f_mul(bz, orbit.m[2][1]);
|
||||||
universe[o].pos.z = int_to_f(300) + f_mul(base_x, orbit.m[0][2]);
|
obj->pos.z = int_to_f(300) + f_mul(bx, orbit.m[0][2]) + f_mul(by, orbit.m[1][2]) + f_mul(bz, orbit.m[2][2]);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Les trois composantes de `base_pos` (bx, by, bz) sont toutes prises en compte — les objets à `base_pos.y != 0` ou `base_pos.z != 0` sont correctement transformés.
|
||||||
|
|
||||||
La caméra reste à l'origine (+ translations clavier). L'effet visuel est celui d'un observateur qui orbite autour de la scène.
|
La caméra reste à l'origine (+ translations clavier). L'effet visuel est celui d'un observateur qui orbite autour de la scène.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -171,9 +190,12 @@ La caméra reste à l'origine (+ translations clavier). L'effet visuel est celui
|
||||||
| A / E | Caméra haut / bas (axe Y) |
|
| A / E | Caméra haut / bas (axe Y) |
|
||||||
| + / − | Orbite vertical (anglex ±3°) |
|
| + / − | Orbite vertical (anglex ±3°) |
|
||||||
| / / * | Orbite horizontal (angley ±3°) |
|
| / / * | Orbite horizontal (angley ±3°) |
|
||||||
|
| 1 / 2 / 3 | Mode fil-de-fer / caché / solide |
|
||||||
| R | Reset caméra + angles |
|
| R | Reset caméra + angles |
|
||||||
| Esc | Quitter |
|
| Esc | Quitter |
|
||||||
|
|
||||||
|
Les touches étendues (F1–F12, flèches…) retournent `0` depuis `getch()` ; le scan code suivant est consommé sans effet.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Points de vigilance
|
## Points de vigilance
|
||||||
|
|
@ -184,4 +206,7 @@ La caméra reste à l'origine (+ translations clavier). L'effet visuel est celui
|
||||||
| 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
||||||
|
| Z-test | `scanline_gouraud_c` et `draw_line_z` utilisent tous les deux `zv < zbuf` (strict), garantissant un comportement cohérent. |
|
||||||
|
| mat3_mul | Sûre contre l'aliasing (res peut être identique à a ou b). |
|
||||||
| I/O mesh | `save_mesh` / `load_mesh` / `free_mesh` disponibles dans `geometry.c` mais non utilisées depuis `main.c`. |
|
| I/O mesh | `save_mesh` / `load_mesh` / `free_mesh` disponibles dans `geometry.c` mais non utilisées depuis `main.c`. |
|
||||||
|
| Face.color | Le champ `face.color` a été supprimé : la couleur est portée par `obj->color`. `face.force_wire` reste le seul flag par-face. |
|
||||||
|
|
|
||||||
169
src/main/main.c
169
src/main/main.c
|
|
@ -1,129 +1,96 @@
|
||||||
#include "../part3D/defines.h"
|
#include "../part3D/defines.h"
|
||||||
|
|
||||||
int main() {
|
int main(void) {
|
||||||
Mesh m_sphere, m_cyl;
|
Universe *uni;
|
||||||
Object3D universe[2];
|
Object3D *sph, *cyl;
|
||||||
Camera cam;
|
Camera cam;
|
||||||
Matrix3 rotX, rotY, orbit;
|
Matrix3 rotX, rotY, orbit;
|
||||||
int anglex = 0, angley = 0, key = 0;
|
int anglex, angley, key, render, o;
|
||||||
|
fixed bx, by, bz;
|
||||||
|
|
||||||
|
anglex = angley = key = 0;
|
||||||
|
render = MODE_SOLID;
|
||||||
|
|
||||||
init_engine_math();
|
init_engine_math();
|
||||||
|
|
||||||
/* Allocation intégrale des Mesh */
|
/* Un seul pool pour toute la scène */
|
||||||
m_sphere.verts = (Vector3*)malloc(MAX_VERTEX * sizeof(Vector3));
|
uni = (Universe*)malloc(sizeof(Universe));
|
||||||
m_sphere.v_normals = (Vector3*)malloc(MAX_VERTEX * sizeof(Vector3));
|
if (!uni) return 1;
|
||||||
m_sphere.faces = (Face*)malloc(MAX_FACES * sizeof(Face));
|
uni->num_verts = uni->num_faces = uni->num_objects = 0;
|
||||||
m_sphere.num_verts = m_sphere.num_faces = 0;
|
|
||||||
|
|
||||||
m_cyl.verts = (Vector3*)malloc(MAX_VERTEX * sizeof(Vector3));
|
/* --- Ajout des objets : géométrie locale en (0,0,0) --- */
|
||||||
m_cyl.v_normals = (Vector3*)malloc(MAX_VERTEX * sizeof(Vector3));
|
sph = add_sphere(uni, 0, 0, 0, int_to_f(30), 8, COLOR5, 0);
|
||||||
m_cyl.faces = (Face*)malloc(MAX_FACES * sizeof(Face));
|
if (!sph) { free(uni); return 1; }
|
||||||
m_cyl.num_verts = m_cyl.num_faces = 0;
|
sph->base_pos.x = int_to_f(60); /* offset par rapport au centre d'orbite (0,0,300) */
|
||||||
|
sph->base_pos.y = 0;
|
||||||
|
sph->base_pos.z = 0;
|
||||||
|
sph->color = COLOR4; /* jaune */
|
||||||
|
|
||||||
if (!m_sphere.verts || !m_sphere.v_normals || !m_sphere.faces ||
|
cyl = add_cylinder(uni, 0, 0, 0, int_to_f(20), int_to_f(60), 12, COLOR14, 0);
|
||||||
!m_cyl.verts || !m_cyl.v_normals || !m_cyl.faces) return 1;
|
if (!cyl) { free(uni); return 1; }
|
||||||
|
cyl->base_pos.x = int_to_f(-60);
|
||||||
|
cyl->base_pos.y = 0;
|
||||||
|
cyl->base_pos.z = 0;
|
||||||
|
cyl->color = COLOR2; /* vert */
|
||||||
|
|
||||||
/* Génération en 0,0,0 (Espace Local) */
|
/* Pour ajouter un troisième objet : */
|
||||||
add_sphere(&m_sphere, 0, 0, 0, int_to_f(30), 12, COLOR5, 0);
|
/*
|
||||||
add_cylinder(&m_cyl, 0, 0, 0, int_to_f(20), int_to_f(60), 16, COLOR14, 0);
|
Object3D *sph2 = add_sphere(uni, 0, 0, 0, int_to_f(15), 6, COLOR6, 0);
|
||||||
|
sph2->base_pos.x = 0; sph2->base_pos.y = int_to_f(60); sph2->base_pos.z = 0;
|
||||||
|
sph2->color = COLOR6;
|
||||||
|
*/
|
||||||
|
|
||||||
/* Configuration des objets dans l'univers */
|
cam.pos.x = cam.pos.y = cam.pos.z = 0;
|
||||||
universe[0].mesh = &m_sphere;
|
|
||||||
universe[0].pos.x = int_to_f(60); universe[0].pos.y = 0; universe[0].pos.z = int_to_f(300);
|
|
||||||
universe[0].color = 4; /* Jaune */
|
|
||||||
mat3_identity(&universe[0].rot);
|
|
||||||
|
|
||||||
universe[1].mesh = &m_cyl;
|
if (init_vga() != 0) { free(uni); return 1; }
|
||||||
universe[1].pos.x = int_to_f(-60); universe[1].pos.y = 0; universe[1].pos.z = int_to_f(300);
|
|
||||||
universe[1].color = 2; /* Vert */
|
|
||||||
mat3_identity(&universe[1].rot);
|
|
||||||
|
|
||||||
/* Caméra initiale */
|
|
||||||
cam.pos.x = 0; cam.pos.y = 0; cam.pos.z = 0;
|
|
||||||
|
|
||||||
init_vga();
|
|
||||||
|
|
||||||
while (key != 27) {
|
while (key != 27) {
|
||||||
if (kbhit()) {
|
if (kbhit()) {
|
||||||
key = getch();
|
key = getch();
|
||||||
switch (key) {
|
if (key == 0) {
|
||||||
case 'Z':
|
getch(); /* touche étendue (F1-F12, flèches…) : consommer le scan code */
|
||||||
case 'z':
|
} else {
|
||||||
cam.pos.z += int_to_f(10);
|
switch (key) {
|
||||||
break;
|
case 'Z': case 'z': cam.pos.z += int_to_f(10); break;
|
||||||
|
case 'S': case 's': cam.pos.z -= int_to_f(10); break;
|
||||||
case 'S':
|
case 'Q': case 'q': cam.pos.x -= int_to_f(10); break;
|
||||||
case 's':
|
case 'D': case 'd': cam.pos.x += int_to_f(10); break;
|
||||||
cam.pos.z -= int_to_f(10);
|
case 'A': case 'a': cam.pos.y -= int_to_f(10); break;
|
||||||
break;
|
case 'E': case 'e': cam.pos.y += int_to_f(10); break;
|
||||||
|
case 'R': case 'r': cam.pos.x = cam.pos.y = cam.pos.z = 0; anglex = angley = 0; break;
|
||||||
case 'Q':
|
case '+': anglex = (anglex + 3) % 360; break;
|
||||||
case 'q':
|
case '-': anglex = ((anglex - 3) % 360 + 360) % 360; break;
|
||||||
cam.pos.x -= int_to_f(10);
|
case '/': angley = (angley + 3) % 360; break;
|
||||||
break;
|
case '*': angley = ((angley - 3) % 360 + 360) % 360; break;
|
||||||
|
case '1': render = MODE_WIRE; break;
|
||||||
case 'D':
|
case '2': render = MODE_HIDDEN; break;
|
||||||
case 'd':
|
case '3': render = MODE_SOLID; break;
|
||||||
cam.pos.x += int_to_f(10);
|
}
|
||||||
break;
|
}
|
||||||
|
|
||||||
case 'A':
|
|
||||||
case 'a':
|
|
||||||
cam.pos.y -= int_to_f(10);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'E':
|
|
||||||
case 'e':
|
|
||||||
cam.pos.y += int_to_f(10);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'R':
|
|
||||||
case 'r':
|
|
||||||
cam.pos.x = cam.pos.y = cam.pos.z = 0;
|
|
||||||
anglex = angley = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '+':
|
|
||||||
anglex = (anglex + 3) % 360;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '-':
|
|
||||||
anglex = ((anglex - 3) % 360 + 360) % 360;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '/':
|
|
||||||
angley = (angley + 3) % 360;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '*':
|
|
||||||
angley = ((angley - 3) % 360 + 360) % 360;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Orbite : rotation de la scène entière autour du centre (0,0,300) */
|
/* Orbite : rotation de la scène autour du centre (0,0,300) */
|
||||||
mat3_rotate_x(&rotX, anglex);
|
mat3_rotate_x(&rotX, anglex);
|
||||||
mat3_rotate_y(&rotY, angley);
|
mat3_rotate_y(&rotY, angley);
|
||||||
mat3_mul(&orbit, &rotX, &rotY);
|
mat3_mul(&orbit, &rotX, &rotY);
|
||||||
|
|
||||||
universe[0].rot = orbit;
|
for (o = 0; o < uni->num_objects; o++) {
|
||||||
universe[0].pos.x = f_mul(int_to_f( 60), orbit.m[0][0]);
|
Object3D *obj = &uni->objects[o];
|
||||||
universe[0].pos.y = f_mul(int_to_f( 60), orbit.m[0][1]);
|
bx = obj->base_pos.x;
|
||||||
universe[0].pos.z = int_to_f(300) + f_mul(int_to_f( 60), orbit.m[0][2]);
|
by = obj->base_pos.y;
|
||||||
|
bz = obj->base_pos.z;
|
||||||
universe[1].rot = orbit;
|
obj->rot = orbit;
|
||||||
universe[1].pos.x = f_mul(int_to_f(-60), orbit.m[0][0]);
|
obj->pos.x = f_mul(bx, orbit.m[0][0]) + f_mul(by, orbit.m[1][0]) + f_mul(bz, orbit.m[2][0]);
|
||||||
universe[1].pos.y = f_mul(int_to_f(-60), orbit.m[0][1]);
|
obj->pos.y = f_mul(bx, orbit.m[0][1]) + f_mul(by, orbit.m[1][1]) + f_mul(bz, orbit.m[2][1]);
|
||||||
universe[1].pos.z = int_to_f(300) + f_mul(int_to_f(-60), orbit.m[0][2]);
|
obj->pos.z = int_to_f(300) + f_mul(bx, orbit.m[0][2]) + f_mul(by, orbit.m[1][2]) + f_mul(bz, orbit.m[2][2]);
|
||||||
|
}
|
||||||
|
|
||||||
clear_buffers(0);
|
clear_buffers(0);
|
||||||
render_universe(universe, 2, &cam, MODE_SOLID);
|
render_universe(uni, &cam, render);
|
||||||
flip();
|
flip();
|
||||||
}
|
}
|
||||||
|
|
||||||
close_vga();
|
close_vga();
|
||||||
free(m_sphere.verts); free(m_sphere.v_normals); free(m_sphere.faces);
|
free(uni);
|
||||||
free(m_cyl.verts); free(m_cyl.v_normals); free(m_cyl.faces);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,13 @@
|
||||||
#define MODE_HIDDEN 1
|
#define MODE_HIDDEN 1
|
||||||
#define MODE_SOLID 2
|
#define MODE_SOLID 2
|
||||||
|
|
||||||
|
#define SCREEN_W 320
|
||||||
|
#define SCREEN_H 200
|
||||||
|
#define SCREEN_PIXELS (SCREEN_W * SCREEN_H)
|
||||||
|
|
||||||
#define MAX_VERTEX 5000
|
#define MAX_VERTEX 5000
|
||||||
#define MAX_FACES 8000
|
#define MAX_FACES 8000
|
||||||
|
#define MAX_OBJECTS 64
|
||||||
|
|
||||||
typedef struct { uint8_t r, g, b; } RGB;
|
typedef struct { uint8_t r, g, b; } RGB;
|
||||||
|
|
||||||
|
|
@ -44,23 +49,36 @@ static __inline fixed f_div(fixed a, fixed b) { if (b == 0) return 0; return (fi
|
||||||
|
|
||||||
typedef struct { fixed m[3][3]; } Matrix3;
|
typedef struct { fixed m[3][3]; } Matrix3;
|
||||||
typedef struct { fixed x, y, z; } Vector3;
|
typedef struct { fixed x, y, z; } Vector3;
|
||||||
typedef struct { int a, b, c; uint8_t color; uint8_t force_wire; } Face;
|
typedef struct { int a, b, c; uint8_t force_wire; } Face;
|
||||||
typedef struct { int x, y; fixed z; int intensity; } Point2D;
|
typedef struct { int x, y; fixed z; fixed intensity; } Point2D;
|
||||||
typedef struct { Vector3 *verts; Vector3 *v_normals; Face *faces; int num_verts, num_faces; } Mesh;
|
|
||||||
|
|
||||||
|
/* Un objet dans l'univers : tranche du pool partagé + transform */
|
||||||
/* Structure pour l'univers */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Mesh *mesh;
|
int vert_start, num_verts; /* indices dans Universe.verts/v_normals */
|
||||||
Vector3 pos;
|
int face_start, num_faces; /* indices dans Universe.faces */
|
||||||
|
Vector3 pos; /* position monde courante (mise à jour par orbite) */
|
||||||
|
Vector3 base_pos; /* offset par rapport au centre d'orbite */
|
||||||
Matrix3 rot;
|
Matrix3 rot;
|
||||||
uint8_t color;
|
uint8_t color;
|
||||||
} Object3D;
|
} Object3D;
|
||||||
|
|
||||||
|
/* Pool unique pour toute la scène */
|
||||||
|
typedef struct {
|
||||||
|
Vector3 verts[MAX_VERTEX];
|
||||||
|
Vector3 v_normals[MAX_VERTEX];
|
||||||
|
Face faces[MAX_FACES];
|
||||||
|
int num_verts, num_faces;
|
||||||
|
Object3D objects[MAX_OBJECTS];
|
||||||
|
int num_objects;
|
||||||
|
} Universe;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Vector3 pos;
|
Vector3 pos;
|
||||||
} Camera;
|
} Camera;
|
||||||
|
|
||||||
|
/* Mesh dynamique conservé pour save/load uniquement */
|
||||||
|
typedef struct { Vector3 *verts; Vector3 *v_normals; Face *faces; int num_verts, num_faces; } Mesh;
|
||||||
|
|
||||||
/* Globales */
|
/* Globales */
|
||||||
extern fixed sintab[360], costab[360];
|
extern fixed sintab[360], costab[360];
|
||||||
extern Point2D v_cache[MAX_VERTEX];
|
extern Point2D v_cache[MAX_VERTEX];
|
||||||
|
|
@ -75,12 +93,13 @@ void mat3_rotate_x(Matrix3 *m, int angle);
|
||||||
void mat3_rotate_y(Matrix3 *m, int angle);
|
void mat3_rotate_y(Matrix3 *m, int angle);
|
||||||
void mat3_mul(Matrix3 *res, Matrix3 *a, Matrix3 *b);
|
void mat3_mul(Matrix3 *res, Matrix3 *a, Matrix3 *b);
|
||||||
|
|
||||||
void add_sphere(Mesh *m, fixed x, fixed y, fixed z, fixed r, int det, uint8_t col, uint8_t wire);
|
/* Retournent un pointeur sur le nouvel Object3D, NULL si pool saturé */
|
||||||
void add_cylinder(Mesh *m, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire);
|
Object3D* add_sphere (Universe *uni, fixed x, fixed y, fixed z, fixed r, int det, uint8_t col, uint8_t wire);
|
||||||
|
Object3D* add_cylinder(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire);
|
||||||
|
|
||||||
void render_universe(Object3D *objs, int num_objs, Camera *cam, int mode);
|
void render_universe(Universe *uni, Camera *cam, int mode);
|
||||||
|
|
||||||
void init_vga(void);
|
int init_vga(void);
|
||||||
void clear_buffers(uint8_t color);
|
void clear_buffers(uint8_t color);
|
||||||
void flip(void);
|
void flip(void);
|
||||||
void close_vga(void);
|
void close_vga(void);
|
||||||
|
|
@ -88,7 +107,6 @@ void close_vga(void);
|
||||||
void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_color);
|
void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_color);
|
||||||
void draw_line_z(Point2D *p1, Point2D *p2, uint8_t col, int z_test);
|
void draw_line_z(Point2D *p1, Point2D *p2, uint8_t col, int z_test);
|
||||||
|
|
||||||
void normalize(Vector3 *v);
|
|
||||||
void save_mesh(Mesh *m, const char *fn);
|
void save_mesh(Mesh *m, const char *fn);
|
||||||
Mesh *load_mesh(const char *fname);
|
Mesh *load_mesh(const char *fname);
|
||||||
void free_mesh(Mesh *m);
|
void free_mesh(Mesh *m);
|
||||||
|
|
|
||||||
|
|
@ -4,36 +4,28 @@ fixed sintab[360], costab[360];
|
||||||
Point2D v_cache[MAX_VERTEX];
|
Point2D v_cache[MAX_VERTEX];
|
||||||
|
|
||||||
void mat3_identity(Matrix3 *mat) {
|
void mat3_identity(Matrix3 *mat) {
|
||||||
memset(mat, 0, sizeof(Matrix3));
|
memset(mat, 0, sizeof(Matrix3));
|
||||||
mat->m[0][0] = mat->m[1][1] = mat->m[2][2] = int_to_f(1);
|
mat->m[0][0] = mat->m[1][1] = mat->m[2][2] = int_to_f(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_engine_math() {
|
void init_engine_math(void) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < 360; i++) {
|
for (i = 0; i < 360; i++) {
|
||||||
double r = (double)i * 3.14159265 / 180.0;
|
double r = (double)i * 3.14159265 / 180.0;
|
||||||
sintab[i] = (fixed)(sin(r) * 65536.0);
|
sintab[i] = (fixed)(sin(r) * 65536.0);
|
||||||
costab[i] = (fixed)(cos(r) * 65536.0);
|
costab[i] = (fixed)(cos(r) * 65536.0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void normalize(Vector3 *v) {
|
|
||||||
double dx = (double)v->x, dy = (double)v->y, dz = (double)v->z;
|
|
||||||
double d = sqrt(dx*dx + dy*dy + dz*dz);
|
|
||||||
if (d > 0) {
|
|
||||||
v->x = (fixed)((dx/d)*65536.0); v->y = (fixed)((dy/d)*65536.0); v->z = (fixed)((dz/d)*65536.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mat3_mul(Matrix3 *res, Matrix3 *a, Matrix3 *b) {
|
void mat3_mul(Matrix3 *res, Matrix3 *a, Matrix3 *b) {
|
||||||
|
Matrix3 tmp;
|
||||||
int i, j;
|
int i, j;
|
||||||
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++)
|
||||||
for (j = 0; j < 3; j++) {
|
for (j = 0; j < 3; j++)
|
||||||
res->m[i][j] = f_mul(a->m[i][0], b->m[0][j]) +
|
tmp.m[i][j] = f_mul(a->m[i][0], b->m[0][j]) +
|
||||||
f_mul(a->m[i][1], b->m[1][j]) +
|
f_mul(a->m[i][1], b->m[1][j]) +
|
||||||
f_mul(a->m[i][2], b->m[2][j]);
|
f_mul(a->m[i][2], b->m[2][j]);
|
||||||
}
|
*res = tmp;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mat3_rotate_x(Matrix3 *m, int angle) {
|
void mat3_rotate_x(Matrix3 *m, int angle) {
|
||||||
|
|
@ -50,36 +42,45 @@ void mat3_rotate_y(Matrix3 *m, int angle) {
|
||||||
m->m[0][0] = c; m->m[0][2] = s; m->m[2][0] = -s; m->m[2][2] = c;
|
m->m[0][0] = c; m->m[0][2] = s; m->m[2][0] = -s; m->m[2][2] = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_universe(Object3D *objs, int num_objs, Camera *cam, int mode) {
|
void render_universe(Universe *uni, Camera *cam, int mode) {
|
||||||
int o, i, front;
|
int o, i, vi, front;
|
||||||
fixed rx, ry, rz, pz, nz;
|
fixed rx, ry, rz, pz, nz;
|
||||||
uint8_t wire_col;
|
uint8_t wire_col;
|
||||||
|
Vector3 *v, *n;
|
||||||
|
Object3D *obj;
|
||||||
|
Face *f;
|
||||||
|
Point2D *p1, *p2, *p3;
|
||||||
|
|
||||||
for (o = 0; o < num_objs; o++) {
|
for (o = 0; o < uni->num_objects; o++) {
|
||||||
Object3D *obj = &objs[o];
|
obj = &uni->objects[o];
|
||||||
Mesh *m = obj->mesh;
|
|
||||||
|
|
||||||
for (i = 0; i < m->num_verts; i++) {
|
/* Transformation + projection de tous les sommets de cet objet */
|
||||||
Vector3 *v = &m->verts[i], *n = &m->v_normals[i];
|
for (i = 0; i < obj->num_verts; i++) {
|
||||||
|
vi = obj->vert_start + i;
|
||||||
|
v = &uni->verts[vi];
|
||||||
|
n = &uni->v_normals[vi];
|
||||||
|
|
||||||
rx = f_mul(v->x, obj->rot.m[0][0]) + f_mul(v->y, obj->rot.m[1][0]) + f_mul(v->z, obj->rot.m[2][0]) + obj->pos.x - cam->pos.x;
|
rx = f_mul(v->x, obj->rot.m[0][0]) + f_mul(v->y, obj->rot.m[1][0]) + f_mul(v->z, obj->rot.m[2][0]) + obj->pos.x - cam->pos.x;
|
||||||
ry = f_mul(v->x, obj->rot.m[0][1]) + f_mul(v->y, obj->rot.m[1][1]) + f_mul(v->z, obj->rot.m[2][1]) + obj->pos.y - cam->pos.y;
|
ry = f_mul(v->x, obj->rot.m[0][1]) + f_mul(v->y, obj->rot.m[1][1]) + f_mul(v->z, obj->rot.m[2][1]) + obj->pos.y - cam->pos.y;
|
||||||
rz = f_mul(v->x, obj->rot.m[0][2]) + f_mul(v->y, obj->rot.m[1][2]) + f_mul(v->z, obj->rot.m[2][2]) + obj->pos.z - cam->pos.z;
|
rz = f_mul(v->x, obj->rot.m[0][2]) + f_mul(v->y, obj->rot.m[1][2]) + f_mul(v->z, obj->rot.m[2][2]) + obj->pos.z - cam->pos.z;
|
||||||
|
|
||||||
pz = (rz < int_to_f(20)) ? int_to_f(20) : rz;
|
pz = (rz < int_to_f(20)) ? int_to_f(20) : rz;
|
||||||
v_cache[i].z = pz;
|
v_cache[vi].z = pz;
|
||||||
v_cache[i].x = 160 + f_to_int(f_div(rx << 8, pz));
|
v_cache[vi].x = 160 + f_to_int(f_div(rx << 8, pz));
|
||||||
v_cache[i].y = 100 - f_to_int(f_div(ry << 8, pz));
|
v_cache[vi].y = 100 - f_to_int(f_div(ry << 8, pz));
|
||||||
|
|
||||||
nz = f_mul(n->x, obj->rot.m[0][2]) + f_mul(n->y, obj->rot.m[1][2]) + f_mul(n->z, obj->rot.m[2][2]);
|
nz = f_mul(n->x, obj->rot.m[0][2]) + f_mul(n->y, obj->rot.m[1][2]) + f_mul(n->z, obj->rot.m[2][2]);
|
||||||
v_cache[i].intensity = (nz >= 0) ? int_to_f(1) : ((-nz) * 14) + int_to_f(1);
|
v_cache[vi].intensity = (nz >= 0) ? int_to_f(1) : f_mul(-nz, int_to_f(14)) + int_to_f(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
wire_col = (uint8_t)((obj->color << 4) | 15);
|
wire_col = (uint8_t)((obj->color << 4) | 15);
|
||||||
|
|
||||||
for (i = 0; i < m->num_faces; i++) {
|
/* Rendu des faces */
|
||||||
Face *f = &m->faces[i];
|
for (i = 0; i < obj->num_faces; i++) {
|
||||||
Point2D *p1 = &v_cache[f->a], *p2 = &v_cache[f->b], *p3 = &v_cache[f->c];
|
f = &uni->faces[obj->face_start + i];
|
||||||
|
p1 = &v_cache[f->a];
|
||||||
|
p2 = &v_cache[f->b];
|
||||||
|
p3 = &v_cache[f->c];
|
||||||
front = ((p2->x - p1->x) * (p3->y - p1->y) - (p2->y - p1->y) * (p3->x - p1->x)) > 0;
|
front = ((p2->x - p1->x) * (p3->y - p1->y) - (p2->y - p1->y) * (p3->x - p1->x)) > 0;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
|
|
||||||
|
|
@ -1,151 +1,208 @@
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
void add_face(Mesh *m, int a, int b, int c, uint8_t col, uint8_t wire) {
|
static void push_face(Universe *uni, Object3D *obj, int a, int b, int c, uint8_t wire) {
|
||||||
Face *f = &m->faces[m->num_faces++];
|
Face *f = &uni->faces[uni->num_faces++];
|
||||||
f->a = a;
|
f->a = a; f->b = b; f->c = c;
|
||||||
f->b = b;
|
f->force_wire = wire;
|
||||||
f->c = c;
|
obj->num_faces++;
|
||||||
f->color = col;
|
|
||||||
f->force_wire = wire;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Génère une sphère avec des normales unitaires */
|
static Object3D *alloc_object(Universe *uni) {
|
||||||
void add_sphere(Mesh *m, fixed x, fixed y, fixed z, fixed r, int det, uint8_t col, uint8_t wire) {
|
Object3D *obj = &uni->objects[uni->num_objects++];
|
||||||
int i, j, v_base = m->num_verts;
|
obj->vert_start = uni->num_verts;
|
||||||
|
obj->face_start = uni->num_faces;
|
||||||
|
obj->num_verts = 0;
|
||||||
|
obj->num_faces = 0;
|
||||||
|
obj->pos.x = obj->pos.y = obj->pos.z = 0;
|
||||||
|
obj->base_pos.x = obj->base_pos.y = obj->base_pos.z = 0;
|
||||||
|
mat3_identity(&obj->rot);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object3D *add_sphere(Universe *uni, fixed x, fixed y, fixed z, fixed r, int det, uint8_t col, uint8_t wire) {
|
||||||
|
int i, j, v_base, phi, th;
|
||||||
|
Object3D *obj;
|
||||||
|
Vector3 *v, *n;
|
||||||
|
|
||||||
|
if (uni->num_objects >= MAX_OBJECTS) return NULL;
|
||||||
|
if (uni->num_verts + (det + 1) * det > MAX_VERTEX) return NULL;
|
||||||
|
if (uni->num_faces + det * det * 2 > MAX_FACES) return NULL;
|
||||||
|
|
||||||
|
obj = alloc_object(uni);
|
||||||
|
obj->color = col;
|
||||||
|
v_base = uni->num_verts;
|
||||||
|
|
||||||
for (j = 0; j <= det; j++) {
|
for (j = 0; j <= det; j++) {
|
||||||
fixed phi = (j * 180 / det) % 360;
|
phi = (j * 180 / det) % 360;
|
||||||
for (i = 0; i < det; i++) {
|
for (i = 0; i < det; i++) {
|
||||||
fixed th = (i * 360 / det) % 360;
|
th = (i * 360 / det) % 360;
|
||||||
Vector3 *v = &m->verts[m->num_verts];
|
v = &uni->verts[uni->num_verts];
|
||||||
Vector3 *n = &m->v_normals[m->num_verts];
|
n = &uni->v_normals[uni->num_verts];
|
||||||
|
|
||||||
n->x = f_mul(sintab[phi], costab[th]);
|
n->x = f_mul(sintab[phi], costab[th]);
|
||||||
n->y = costab[phi];
|
n->y = costab[phi];
|
||||||
n->z = f_mul(sintab[phi], sintab[th]);
|
n->z = f_mul(sintab[phi], sintab[th]);
|
||||||
|
|
||||||
v->x = x + f_mul(r, n->x);
|
v->x = x + f_mul(r, n->x);
|
||||||
v->y = y + f_mul(r, n->y);
|
v->y = y + f_mul(r, n->y);
|
||||||
v->z = z + f_mul(r, n->z);
|
v->z = z + f_mul(r, n->z);
|
||||||
m->num_verts++;
|
uni->num_verts++; obj->num_verts++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < det; j++) {
|
for (j = 0; j < det; j++) {
|
||||||
for (i = 0; i < det; i++) {
|
for (i = 0; i < det; i++) {
|
||||||
int a = v_base + j * det + i;
|
int a = v_base + j * det + i;
|
||||||
int b = v_base + j * det + (i + 1) % det;
|
int b = v_base + j * det + (i + 1) % det;
|
||||||
int c = v_base + (j + 1) * det + i;
|
int c = v_base + (j + 1) * det + i;
|
||||||
int d = v_base + (j + 1) * det + (i + 1) % det;
|
int d = v_base + (j + 1) * det + (i + 1) % det;
|
||||||
add_face(m, a, b, c, col, wire);
|
push_face(uni, obj, a, b, c, wire);
|
||||||
add_face(m, b, d, c, col, wire);
|
push_face(uni, obj, b, d, c, wire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Génère un cylindre fermé (corps + fonds) avec normales et winding corrects */
|
Object3D *add_cylinder(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire) {
|
||||||
void add_cylinder(Mesh *m, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire) {
|
int i, v_base, cap_bot_center, cap_bot_rim, cap_top_center, cap_top_rim, th;
|
||||||
int i, v_base = m->num_verts;
|
fixed half_h, s, cs;
|
||||||
int cap_bot_center, cap_bot_rim, cap_top_center, cap_top_rim;
|
Object3D *obj;
|
||||||
fixed half_h = h / 2;
|
|
||||||
fixed th, s, c;
|
|
||||||
|
|
||||||
/* --- Corps : paires bas/haut, normales radiales --- */
|
if (uni->num_objects >= MAX_OBJECTS) return NULL;
|
||||||
|
if (uni->num_verts + 2 + 4 * det > MAX_VERTEX) return NULL;
|
||||||
|
if (uni->num_faces + 4 * det > MAX_FACES) return NULL;
|
||||||
|
|
||||||
|
obj = alloc_object(uni);
|
||||||
|
obj->color = col;
|
||||||
|
v_base = uni->num_verts;
|
||||||
|
half_h = h / 2;
|
||||||
|
|
||||||
|
/* --- Corps --- */
|
||||||
for (i = 0; i < det; i++) {
|
for (i = 0; i < det; i++) {
|
||||||
th = (i * 360 / det) % 360;
|
th = (i * 360 / det) % 360;
|
||||||
s = sintab[th]; c = costab[th];
|
s = sintab[th]; cs = costab[th];
|
||||||
|
|
||||||
m->v_normals[m->num_verts].x = c; m->v_normals[m->num_verts].y = 0; m->v_normals[m->num_verts].z = s;
|
uni->v_normals[uni->num_verts].x = cs;
|
||||||
m->verts[m->num_verts].x = x + f_mul(r, c); m->verts[m->num_verts].y = y - half_h; m->verts[m->num_verts].z = z + f_mul(r, s);
|
uni->v_normals[uni->num_verts].y = 0;
|
||||||
m->num_verts++;
|
uni->v_normals[uni->num_verts].z = s;
|
||||||
|
uni->verts[uni->num_verts].x = x + f_mul(r, cs);
|
||||||
|
uni->verts[uni->num_verts].y = y - half_h;
|
||||||
|
uni->verts[uni->num_verts].z = z + f_mul(r, s);
|
||||||
|
uni->num_verts++; obj->num_verts++;
|
||||||
|
|
||||||
m->v_normals[m->num_verts].x = c; m->v_normals[m->num_verts].y = 0; m->v_normals[m->num_verts].z = s;
|
uni->v_normals[uni->num_verts].x = cs;
|
||||||
m->verts[m->num_verts].x = x + f_mul(r, c); m->verts[m->num_verts].y = y + half_h; m->verts[m->num_verts].z = z + f_mul(r, s);
|
uni->v_normals[uni->num_verts].y = 0;
|
||||||
m->num_verts++;
|
uni->v_normals[uni->num_verts].z = s;
|
||||||
|
uni->verts[uni->num_verts].x = x + f_mul(r, cs);
|
||||||
|
uni->verts[uni->num_verts].y = y + half_h;
|
||||||
|
uni->verts[uni->num_verts].z = z + f_mul(r, s);
|
||||||
|
uni->num_verts++; obj->num_verts++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Faces corps (winding CCW = cross > 0 pour la face avant) */
|
|
||||||
for (i = 0; i < det; i++) {
|
for (i = 0; i < det; i++) {
|
||||||
int a = v_base + (i * 2);
|
int a = v_base + (i * 2);
|
||||||
int b = v_base + ((i * 2 + 2) % (det * 2));
|
int b = v_base + ((i * 2 + 2) % (det * 2));
|
||||||
int c_v = a + 1;
|
int cv = a + 1;
|
||||||
int d = b + 1;
|
int d = b + 1;
|
||||||
add_face(m, a, c_v, b, col, wire); /* bas_i, haut_i, bas_{i+1} */
|
push_face(uni, obj, a, cv, b, wire);
|
||||||
add_face(m, c_v, d, b, col, wire); /* haut_i, haut_{i+1}, bas_{i+1} */
|
push_face(uni, obj, cv, d, b, wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Fond bas : centre + rebord, normale (0,-1,0) --- */
|
/* --- Fond bas --- */
|
||||||
cap_bot_center = m->num_verts;
|
cap_bot_center = uni->num_verts;
|
||||||
m->v_normals[m->num_verts].x = 0; m->v_normals[m->num_verts].y = -int_to_f(1); m->v_normals[m->num_verts].z = 0;
|
uni->v_normals[uni->num_verts].x = 0;
|
||||||
m->verts[m->num_verts].x = x; m->verts[m->num_verts].y = y - half_h; m->verts[m->num_verts].z = z;
|
uni->v_normals[uni->num_verts].y = -int_to_f(1);
|
||||||
m->num_verts++;
|
uni->v_normals[uni->num_verts].z = 0;
|
||||||
|
uni->verts[uni->num_verts].x = x;
|
||||||
|
uni->verts[uni->num_verts].y = y - half_h;
|
||||||
|
uni->verts[uni->num_verts].z = z;
|
||||||
|
uni->num_verts++; obj->num_verts++;
|
||||||
|
|
||||||
cap_bot_rim = m->num_verts;
|
cap_bot_rim = uni->num_verts;
|
||||||
for (i = 0; i < det; i++) {
|
for (i = 0; i < det; i++) {
|
||||||
th = (i * 360 / det) % 360;
|
th = (i * 360 / det) % 360;
|
||||||
s = sintab[th]; c = costab[th];
|
s = sintab[th]; cs = costab[th];
|
||||||
m->v_normals[m->num_verts].x = 0; m->v_normals[m->num_verts].y = -int_to_f(1); m->v_normals[m->num_verts].z = 0;
|
uni->v_normals[uni->num_verts].x = 0;
|
||||||
m->verts[m->num_verts].x = x + f_mul(r, c); m->verts[m->num_verts].y = y - half_h; m->verts[m->num_verts].z = z + f_mul(r, s);
|
uni->v_normals[uni->num_verts].y = -int_to_f(1);
|
||||||
m->num_verts++;
|
uni->v_normals[uni->num_verts].z = 0;
|
||||||
|
uni->verts[uni->num_verts].x = x + f_mul(r, cs);
|
||||||
|
uni->verts[uni->num_verts].y = y - half_h;
|
||||||
|
uni->verts[uni->num_verts].z = z + f_mul(r, s);
|
||||||
|
uni->num_verts++; obj->num_verts++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < det; i++) {
|
for (i = 0; i < det; i++)
|
||||||
add_face(m, cap_bot_center, cap_bot_rim + i, cap_bot_rim + (i + 1) % det, col, wire);
|
push_face(uni, obj, cap_bot_center, cap_bot_rim + i, cap_bot_rim + (i + 1) % det, wire);
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Fond haut : centre + rebord, normale (0,+1,0) --- */
|
/* --- Fond haut --- */
|
||||||
cap_top_center = m->num_verts;
|
cap_top_center = uni->num_verts;
|
||||||
m->v_normals[m->num_verts].x = 0; m->v_normals[m->num_verts].y = int_to_f(1); m->v_normals[m->num_verts].z = 0;
|
uni->v_normals[uni->num_verts].x = 0;
|
||||||
m->verts[m->num_verts].x = x; m->verts[m->num_verts].y = y + half_h; m->verts[m->num_verts].z = z;
|
uni->v_normals[uni->num_verts].y = int_to_f(1);
|
||||||
m->num_verts++;
|
uni->v_normals[uni->num_verts].z = 0;
|
||||||
|
uni->verts[uni->num_verts].x = x;
|
||||||
|
uni->verts[uni->num_verts].y = y + half_h;
|
||||||
|
uni->verts[uni->num_verts].z = z;
|
||||||
|
uni->num_verts++; obj->num_verts++;
|
||||||
|
|
||||||
cap_top_rim = m->num_verts;
|
cap_top_rim = uni->num_verts;
|
||||||
for (i = 0; i < det; i++) {
|
for (i = 0; i < det; i++) {
|
||||||
th = (i * 360 / det) % 360;
|
th = (i * 360 / det) % 360;
|
||||||
s = sintab[th]; c = costab[th];
|
s = sintab[th]; cs = costab[th];
|
||||||
m->v_normals[m->num_verts].x = 0; m->v_normals[m->num_verts].y = int_to_f(1); m->v_normals[m->num_verts].z = 0;
|
uni->v_normals[uni->num_verts].x = 0;
|
||||||
m->verts[m->num_verts].x = x + f_mul(r, c); m->verts[m->num_verts].y = y + half_h; m->verts[m->num_verts].z = z + f_mul(r, s);
|
uni->v_normals[uni->num_verts].y = int_to_f(1);
|
||||||
m->num_verts++;
|
uni->v_normals[uni->num_verts].z = 0;
|
||||||
|
uni->verts[uni->num_verts].x = x + f_mul(r, cs);
|
||||||
|
uni->verts[uni->num_verts].y = y + half_h;
|
||||||
|
uni->verts[uni->num_verts].z = z + f_mul(r, s);
|
||||||
|
uni->num_verts++; obj->num_verts++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < det; i++) {
|
for (i = 0; i < det; i++)
|
||||||
add_face(m, cap_top_center, cap_top_rim + (i + 1) % det, cap_top_rim + i, col, wire);
|
push_face(uni, obj, cap_top_center, cap_top_rim + (i + 1) % det, cap_top_rim + i, wire);
|
||||||
}
|
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- I/O sur Mesh dynamique (non utilisé dans le pipeline principal) --- */
|
||||||
|
|
||||||
void save_mesh(Mesh *m, const char *fn) {
|
void save_mesh(Mesh *m, const char *fn) {
|
||||||
FILE *f = fopen(fn, "wb");
|
FILE *f = fopen(fn, "wb");
|
||||||
if (!f) return;
|
if (!f) return;
|
||||||
fwrite(&m->num_verts, sizeof(int), 1, f);
|
fwrite(&m->num_verts, sizeof(int), 1, f);
|
||||||
fwrite(&m->num_faces, sizeof(int), 1, f);
|
fwrite(&m->num_faces, sizeof(int), 1, f);
|
||||||
fwrite(m->verts, sizeof(Vector3), m->num_verts, f);
|
fwrite(m->verts, sizeof(Vector3), m->num_verts, f);
|
||||||
fwrite(m->v_normals, sizeof(Vector3), m->num_verts, f);
|
fwrite(m->v_normals,sizeof(Vector3), m->num_verts, f);
|
||||||
fwrite(m->faces, sizeof(Face), m->num_faces, f);
|
fwrite(m->faces, sizeof(Face), m->num_faces, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh* load_mesh(const char *fname) {
|
Mesh *load_mesh(const char *fname) {
|
||||||
Mesh *m;
|
Mesh *m;
|
||||||
FILE *f = fopen(fname, "rb");
|
FILE *f = fopen(fname, "rb");
|
||||||
if (!f) return NULL;
|
if (!f) return NULL;
|
||||||
|
|
||||||
m = (Mesh*)malloc(sizeof(Mesh));
|
m = (Mesh*)malloc(sizeof(Mesh));
|
||||||
|
if (!m) return NULL;
|
||||||
fread(&m->num_verts, sizeof(int), 1, f);
|
fread(&m->num_verts, sizeof(int), 1, f);
|
||||||
fread(&m->num_faces, sizeof(int), 1, f);
|
fread(&m->num_faces, sizeof(int), 1, f);
|
||||||
|
m->verts = (Vector3*)malloc(sizeof(Vector3) * m->num_verts);
|
||||||
m->verts = (Vector3*)malloc(sizeof(Vector3) * m->num_verts);
|
|
||||||
m->v_normals = (Vector3*)malloc(sizeof(Vector3) * m->num_verts);
|
m->v_normals = (Vector3*)malloc(sizeof(Vector3) * m->num_verts);
|
||||||
m->faces = (Face*)malloc(sizeof(Face) * m->num_faces);
|
m->faces = (Face*) malloc(sizeof(Face) * m->num_faces);
|
||||||
|
if (!m->verts || !m->v_normals || !m->faces) {
|
||||||
fread(m->verts, sizeof(Vector3), m->num_verts, f);
|
free(m->verts); free(m->v_normals); free(m->faces); free(m);
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fread(m->verts, sizeof(Vector3), m->num_verts, f);
|
||||||
fread(m->v_normals, sizeof(Vector3), m->num_verts, f);
|
fread(m->v_normals, sizeof(Vector3), m->num_verts, f);
|
||||||
fread(m->faces, sizeof(Face), m->num_faces, f);
|
fread(m->faces, sizeof(Face), m->num_faces, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
void free_mesh(Mesh *m) {
|
void free_mesh(Mesh *m) {
|
||||||
if (!m) return;
|
if (!m) return;
|
||||||
if (m->verts) free(m->verts);
|
free(m->verts);
|
||||||
if (m->faces) free(m->faces);
|
free(m->v_normals);
|
||||||
if (m->v_normals) free(m->v_normals);
|
free(m->faces);
|
||||||
free(m);
|
free(m);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,48 +19,37 @@ RGB my_palette[16] = {
|
||||||
{20, 20, 40} // 15: Bleu nuit
|
{20, 20, 40} // 15: Bleu nuit
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t *backbuffer = NULL;
|
uint8_t *backbuffer = NULL;
|
||||||
uint16_t *zbuffer = NULL;
|
uint16_t *zbuffer = NULL;
|
||||||
uint8_t *vga = (uint8_t *)0xA0000;
|
uint8_t *vga = (uint8_t *)0xA0000;
|
||||||
|
|
||||||
void set_mode(int mode);
|
void set_vga_mode(int mode);
|
||||||
#pragma aux set_mode = "int 0x10" parm [ax];
|
#pragma aux set_vga_mode = "int 0x10" parm [ax];
|
||||||
|
|
||||||
void reset_mode(int mode);
|
|
||||||
#pragma aux reset_mode = "int 0x10" parm [ax];
|
|
||||||
|
|
||||||
void clear_buffers(uint8_t color) {
|
void clear_buffers(uint8_t color) {
|
||||||
memset(backbuffer, color, 64000);
|
memset(backbuffer, color, SCREEN_PIXELS);
|
||||||
memset(zbuffer, 0xFF, 64000 * sizeof(uint16_t));
|
memset(zbuffer, 0xFF, SCREEN_PIXELS * sizeof(uint16_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void flip() {
|
void flip(void) {
|
||||||
memcpy(vga, backbuffer, 64000);
|
memcpy(vga, backbuffer, SCREEN_PIXELS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_vga() {
|
void close_vga(void) {
|
||||||
reset_mode(0x0003); // Retour au mode texte
|
set_vga_mode(0x0003); /* retour au mode texte */
|
||||||
if (backbuffer) free(backbuffer);
|
if (backbuffer) { free(backbuffer); backbuffer = NULL; }
|
||||||
if (zbuffer) free(zbuffer);
|
if (zbuffer) { free(zbuffer); zbuffer = NULL; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prend 16 couleurs RGB (0-63) et remplit les 256 entrées du DAC
|
/* Prend 16 couleurs RGB (0-63) et remplit les 256 entrées du DAC */
|
||||||
void setup_vga_palette(RGB *base_colors) {
|
void setup_vga_palette(RGB *base_colors) {
|
||||||
int i, c;
|
int i, c;
|
||||||
// Port 0x3C8 : Index de départ (0)
|
|
||||||
outp(0x3C8, 0);
|
outp(0x3C8, 0);
|
||||||
|
|
||||||
for (c = 0; c < 16; c++) {
|
for (c = 0; c < 16; c++) {
|
||||||
for (i = 0; i < 16; i++) {
|
for (i = 0; i < 16; i++) {
|
||||||
// Interpolation linéaire vers le noir (0,0,0)
|
|
||||||
// On utilise l'arithmétique entière : (color * intensity) / 15
|
|
||||||
uint8_t r = (uint8_t)((base_colors[c].r * i) / 15);
|
uint8_t r = (uint8_t)((base_colors[c].r * i) / 15);
|
||||||
uint8_t g = (uint8_t)((base_colors[c].g * i) / 15);
|
uint8_t g = (uint8_t)((base_colors[c].g * i) / 15);
|
||||||
uint8_t b = (uint8_t)((base_colors[c].b * i) / 15);
|
uint8_t b = (uint8_t)((base_colors[c].b * i) / 15);
|
||||||
|
|
||||||
// Port 0x3C9 : Envoi des composantes R, G, B
|
|
||||||
outp(0x3C9, r);
|
outp(0x3C9, r);
|
||||||
outp(0x3C9, g);
|
outp(0x3C9, g);
|
||||||
outp(0x3C9, b);
|
outp(0x3C9, b);
|
||||||
|
|
@ -68,13 +57,16 @@ void setup_vga_palette(RGB *base_colors) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_vga() {
|
int init_vga(void) {
|
||||||
// On passe en mode 13h (320x200x256)
|
set_vga_mode(0x0013);
|
||||||
set_mode(0x0013);
|
backbuffer = (uint8_t *)malloc(SCREEN_PIXELS);
|
||||||
|
zbuffer = (uint16_t *)malloc(SCREEN_PIXELS * sizeof(uint16_t));
|
||||||
// Allocations
|
if (!backbuffer || !zbuffer) {
|
||||||
backbuffer = (uint8_t *)malloc(64000);
|
set_vga_mode(0x0003);
|
||||||
zbuffer = (uint16_t *)malloc(64000 * sizeof(uint16_t));
|
free(backbuffer); backbuffer = NULL;
|
||||||
|
free(zbuffer); zbuffer = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
setup_vga_palette(my_palette);
|
setup_vga_palette(my_palette);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,13 @@
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
/* Prototypes pour les fonctions assembleur externes */
|
/* Rendu d'une scanline avec interpolation Gouraud 16.16 */
|
||||||
extern void scanline_zonly_asm(int width, uint16_t *zbuf, int z, int dz_step);
|
void scanline_gouraud_c(int width, uint8_t *dest, uint16_t *zbuf,
|
||||||
|
fixed z, fixed dz_step, fixed intensity, fixed di_step,
|
||||||
#pragma aux scanline_zonly_asm = \
|
uint8_t base_color) {
|
||||||
"test ecx, ecx" \
|
|
||||||
"jz done_z" \
|
|
||||||
"loop_z:" \
|
|
||||||
"mov eax, edx" \
|
|
||||||
"shr eax, 16" \
|
|
||||||
"cmp ax, [esi]" \
|
|
||||||
"jae skip_z" \
|
|
||||||
"mov [esi], ax" \
|
|
||||||
"skip_z:" \
|
|
||||||
"add edx, ebx" \
|
|
||||||
"add esi, 2" \
|
|
||||||
"dec ecx" \
|
|
||||||
"jnz loop_z" \
|
|
||||||
"done_z:" \
|
|
||||||
parm [ecx] [esi] [edx] [ebx] \
|
|
||||||
modify [eax ecx esi edx];
|
|
||||||
|
|
||||||
/* Rendu d'une ligne avec interpolation Gouraud 16.16 */
|
|
||||||
void scanline_gouraud_c(int width, uint8_t *dest, uint16_t *zbuf, int z, int dz_step, int intensity, int di_step, uint8_t base_color) {
|
|
||||||
int i;
|
int i;
|
||||||
uint8_t color_offset;
|
int s;
|
||||||
uint8_t shade;
|
uint8_t shade;
|
||||||
|
uint8_t color_offset;
|
||||||
uint16_t zv;
|
uint16_t zv;
|
||||||
|
|
||||||
color_offset = (uint8_t)(base_color << 4);
|
color_offset = (uint8_t)(base_color << 4);
|
||||||
|
|
@ -34,9 +16,10 @@ void scanline_gouraud_c(int width, uint8_t *dest, uint16_t *zbuf, int z, int dz_
|
||||||
zv = (uint16_t)(z >> 16);
|
zv = (uint16_t)(z >> 16);
|
||||||
if (zv < zbuf[i]) {
|
if (zv < zbuf[i]) {
|
||||||
zbuf[i] = zv;
|
zbuf[i] = zv;
|
||||||
/* Extraction de l'intensité 0-15 depuis le format fixed 16.16 */
|
s = intensity >> 16;
|
||||||
shade = (uint8_t)(intensity >> 16);
|
if (s < 0) shade = 0;
|
||||||
if (shade > 15) shade = 15;
|
else if (s > 15) shade = 15;
|
||||||
|
else shade = (uint8_t)s;
|
||||||
dest[i] = color_offset + shade;
|
dest[i] = color_offset + shade;
|
||||||
}
|
}
|
||||||
z += dz_step;
|
z += dz_step;
|
||||||
|
|
@ -50,9 +33,10 @@ void swap_pt(Point2D **a, Point2D **b) {
|
||||||
|
|
||||||
void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_color) {
|
void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_color) {
|
||||||
/* TOUTES les variables déclarées ici pour le C89 */
|
/* TOUTES les variables déclarées ici pour le C89 */
|
||||||
int y, width, start_x, end_x, dz_step, di_step, clip_x, w, start_z, start_i, second_half, total_height;
|
int y, width, start_x, end_x, clip_x, w, second_half, total_height;
|
||||||
fixed dx13, dz13, di13, xA, zA, iA, xB, zB, iB, dx12, dz12, di12, dx23, dz23, di23;
|
fixed dx13, dz13, di13, xA, zA, iA, xB, zB, iB, dx12, dz12, di12, dx23, dz23, di23;
|
||||||
fixed cx1, cz1, ci1, cx2, cz2, ci2, tx, tz, ti;
|
fixed cx1, cz1, ci1, cx2, cz2, ci2, tx, tz, ti;
|
||||||
|
fixed dz_step, di_step, start_z, start_i;
|
||||||
uint8_t *dest;
|
uint8_t *dest;
|
||||||
uint16_t *zbuf;
|
uint16_t *zbuf;
|
||||||
|
|
||||||
|
|
@ -70,7 +54,12 @@ void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_c
|
||||||
di13 = (p3->intensity - p1->intensity) / total_height;
|
di13 = (p3->intensity - p1->intensity) / total_height;
|
||||||
|
|
||||||
xA = int_to_f(p1->x); zA = p1->z; iA = p1->intensity;
|
xA = int_to_f(p1->x); zA = p1->z; iA = p1->intensity;
|
||||||
xB = xA; zB = zA; iB = iA;
|
/* Sommet plat en haut : xB part de p2, pas de p1 */
|
||||||
|
if (p1->y == p2->y) {
|
||||||
|
xB = int_to_f(p2->x); zB = p2->z; iB = p2->intensity;
|
||||||
|
} else {
|
||||||
|
xB = xA; zB = zA; iB = iA;
|
||||||
|
}
|
||||||
|
|
||||||
/* Gradients pour les côtés courts (p1-p2 puis p2-p3) */
|
/* Gradients pour les côtés courts (p1-p2 puis p2-p3) */
|
||||||
dx12 = (p2->y > p1->y) ? ((p2->x - p1->x) << 16) / (p2->y - p1->y) : 0;
|
dx12 = (p2->y > p1->y) ? ((p2->x - p1->x) << 16) / (p2->y - p1->y) : 0;
|
||||||
|
|
@ -94,22 +83,22 @@ void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_c
|
||||||
}
|
}
|
||||||
|
|
||||||
start_x = f_to_int(cx1);
|
start_x = f_to_int(cx1);
|
||||||
end_x = f_to_int(cx2);
|
end_x = f_to_int(cx2) + 1; /* ceil : couvre le pixel de bord droit fractionnaire */
|
||||||
width = end_x - start_x;
|
width = end_x - start_x;
|
||||||
|
|
||||||
if (y >= 0 && y < 200 && width > 0) {
|
if (y >= 0 && y < SCREEN_H && width > 0) {
|
||||||
dz_step = (cz2 - cz1) / width;
|
dz_step = (cz2 - cz1) / width;
|
||||||
di_step = (ci2 - ci1) / width;
|
di_step = (ci2 - ci1) / width;
|
||||||
|
|
||||||
clip_x = (start_x < 0) ? 0 : start_x;
|
clip_x = (start_x < 0) ? 0 : start_x;
|
||||||
w = width - (clip_x - start_x);
|
w = width - (clip_x - start_x);
|
||||||
if (clip_x + w > 320) w = 320 - clip_x;
|
if (clip_x + w > SCREEN_W) w = SCREEN_W - clip_x;
|
||||||
|
|
||||||
if (w > 0) {
|
if (w > 0) {
|
||||||
start_z = cz1 + (clip_x - start_x) * dz_step;
|
start_z = cz1 + (clip_x - start_x) * dz_step;
|
||||||
start_i = ci1 + (clip_x - start_x) * di_step;
|
start_i = ci1 + (clip_x - start_x) * di_step;
|
||||||
dest = backbuffer + (y * 320) + clip_x;
|
dest = backbuffer + (y * SCREEN_W) + clip_x;
|
||||||
zbuf = zbuffer + (y * 320) + clip_x;
|
zbuf = zbuffer + (y * SCREEN_W) + clip_x;
|
||||||
|
|
||||||
scanline_gouraud_c(w, dest, zbuf, start_z, dz_step, start_i, di_step, base_color);
|
scanline_gouraud_c(w, dest, zbuf, start_z, dz_step, start_i, di_step, base_color);
|
||||||
}
|
}
|
||||||
|
|
@ -142,11 +131,11 @@ void draw_line_z(Point2D *p1, Point2D *p2, uint8_t col, int z_test) {
|
||||||
x = p1->x; y = p1->y;
|
x = p1->x; y = p1->y;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (x >= 0 && x < 320 && y >= 0 && y < 200) {
|
if (x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H) {
|
||||||
zv = (uint16_t)f_to_int(cz);
|
zv = (uint16_t)f_to_int(cz);
|
||||||
if (!z_test || zv <= zbuffer[y * 320 + x]) {
|
if (!z_test || zv < zbuffer[y * SCREEN_W + x]) {
|
||||||
backbuffer[y * 320 + x] = col;
|
backbuffer[y * SCREEN_W + x] = col;
|
||||||
if (z_test) zbuffer[y * 320 + x] = zv;
|
if (z_test) zbuffer[y * SCREEN_W + x] = zv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (x == p2->x && y == p2->y) break;
|
if (x == p2->x && y == p2->y) break;
|
||||||
|
|
@ -156,37 +145,3 @@ void draw_line_z(Point2D *p1, Point2D *p2, uint8_t col, int z_test) {
|
||||||
cz += z_step;
|
cz += z_step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue