diff --git a/.gitignore b/.gitignore index 31e0c57..5ca7531 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ voyager.code-workspace .vscode build +*.err diff --git a/Makefile b/Makefile index f6b252a..27ed13a 100644 --- a/Makefile +++ b/Makefile @@ -1,50 +1,88 @@ -# --- Détection de l'OS et Chemins --- -ifeq ($(OS),Windows_NT) - # Chemins Windows pour les outils (utilisés par WSL) - WATCOM := D:/WatCom - NASM := C:/Program Files/NASM/nasm.exe - CC := $(WATCOM)/binnt64/wcc386.exe - AS := $(NASM) - LINK := $(WATCOM)/binnt64/wlink.exe - FIX_PATH = $(subst /,\,$1) +# --- Options de Build --- +# Valeurs possibles : causeway (recommandé), dos4g (classique) +EXTENDER = causeway +STACK_SIZE = 64k + +WATCOM_WIN = D:\WatCom +WATCOM_WSL = /mnt/d/WatCom +NASM_WIN = /mnt/c/Program\ Files/NASM/nasm.exe + +# --- Configuration Dynamique du Linker --- +ifeq ($(EXTENDER), causeway) + # Configuration optimisée pour CauseWay + L_SYS = system causeway + L_OPTS = option osname='CauseWay' \ + option stub=$(WATCOM_WIN)/binw/cwstub.exe \ + option stack=$(STACK_SIZE) else - # Chemins Debian (à adapter selon ton install Linux) - WATCOM := /opt/watcom - CC := $(WATCOM)/binl64/wcc386 - AS := nasm - LINK := $(WATCOM)/binl64/wlink - FIX_PATH = $1 + # Configuration classique pour DOS/4GW + L_SYS = system dos4g + L_OPTS = option stack=$(STACK_SIZE) endif -# --- Configuration --- -INC_DIRS := -i="$(WATCOM)/h" -i="$(WATCOM)/CustomH" -LIB_DIRS := libpath "$(WATCOM)/lib386" libpath "$(WATCOM)/lib386/dos" libpath "$(WATCOM)/CustomLib" -CFLAGS := -bt=dos -6r -fp6 -ox -oh -ot -ei -zp8 $(INC_DIRS) -LFLAGS := system dos4g $(LIB_DIRS) +# Binaires Windows exécutés par WSL +CC = $(WATCOM_WSL)/binnt64/wcc386.exe +CXX = $(WATCOM_WSL)/binnt64/wpp386.exe +AS = $(NASM_WIN) +LINK = $(WATCOM_WSL)/binnt64/wlink.exe -# --- Détection AUTOMATIQUE des sources --- -# Cherche récursivement tous les .cpp, .c et .asm dans src/ -SRC_CPP := $(shell find src -name "*.cpp") -SRC_ASM := $(shell find src -name "*.asm") +# --- Flags de Performance --- +# On utilise wslpath pour que les outils Windows comprennent les chemins Linux +INC_FLAGS = -i="$(WATCOM_WIN)\h" -i="$(WATCOM_WIN)\CustomH" +CFLAGS = -bt=dos -6s -fp6 -ox -od -oh -ot -ei -zp8 $(INC_FLAGS) +LFLAGS = $(L_SYS) $(L_OPTS) \ + libpath "$(WATCOM_WIN)\lib386" \ + libpath "$(WATCOM_WIN)\lib386\dos" \ + libpath "$(WATCOM_WIN)\CustomLib" -# Génère la liste des objets correspondants dans build/ -OBJS := $(patsubst src/%.cpp, build/%.obj, $(SRC_CPP)) -OBJS += $(patsubst src/%.asm, build/%.obj, $(SRC_ASM)) +# --- Détection des Sources --- +# On force le shell à trouver les fichiers +SRCS_CPP := $(shell find src -name "*.cpp") +SRCS_C := $(shell find src -name "*.c") +SRCS_ASM := $(shell find src -name "*.asm") + +# Création de la liste des objets (src/main/main.cpp -> build/main.obj) +OBJS := $(patsubst src/%.cpp, build/%.obj, $(SRCS_CPP)) \ + $(patsubst src/%.c, build/%.obj, $(SRCS_C)) \ + $(patsubst src/%.asm, build/%.obj, $(SRCS_ASM)) # --- Règles --- -all: build/app.exe +all: prepare build/app.exe + +prepare: + @mkdir -p build + @# Crée les sous-dossiers dans build pour correspondre à src + @$(foreach dir, $(sort $(dir $(SRCS_CPP) $(SRCS_C) $(SRCS_ASM))), mkdir -p $(subst src,build,$(dir));) -# Création du dossier build miroir de src build/%.obj: src/%.cpp @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $< -fo=$(call FIX_PATH,$@) + $(CXX) $(CFLAGS) $< -fo='$(shell wslpath -w $@)' + @rm -f $*.err + +build/%.obj: src/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $< -fo='$(shell wslpath -w $@)' + @rm -f $*.err build/%.obj: src/%.asm @mkdir -p $(dir $@) - $(AS) -f obj $< -o $(call FIX_PATH,$@) + $(AS) -f obj $< -o '$(shell wslpath -w $@)' -build/app.exe: $(OBJS) - $(LINK) $(LFLAGS) name $(call FIX_PATH,$@) file {$(foreach f,$(OBJS),$(call FIX_PATH,$f))} +# --- PARTIE LINK CORRIGÉE --- +build/app.exe: $(OBJS) MakeFile + @if [ -z "$(OBJS)" ]; then echo "ERREUR: Aucun objet trouvé!"; exit 1; fi + @echo "Generating linker file: build/link.lnk" + @printf '%s\n' '$(LFLAGS)' > build/link.lnk + @printf 'name %s\n' '$(shell wslpath -w $@)' >> build/link.lnk + @$(foreach f,$(OBJS),printf 'file %s\n' '$(shell wslpath -w $f)' >> build/link.lnk;) + @echo "Linking..." + $(LINK) @build/link.lnk clean: - rm -rf build/* \ No newline at end of file + rm -rf build + # Nettoyage des logs d'erreurs + rm -f *.err src/**/*.err + +cleanall: + clean + all \ No newline at end of file diff --git a/src/main/main.c b/src/main/main.c index 029692f..e2267ed 100644 --- a/src/main/main.c +++ b/src/main/main.c @@ -1,25 +1,76 @@ -#define MAIN_C +#include "../part3D/defines.h" int main() { - init_engine_math(); // Allocation initiale large pour accumulation - Mesh scene; scene.verts = (Vector3*)malloc(5000*sizeof(Vector3)); - scene.v_normals = (Vector3*)malloc(5000*sizeof(Vector3)); - scene.faces = (Face*)malloc(8000*sizeof(Face)); - scene.num_verts = scene.num_faces = 0; + Mesh scene; + Matrix3 rot, tmpX, tmpY; + int angX = 0, angY = 0; + int key = 0; + + printf("STARTING\n"); + + init_engine_math(); + + scene.verts = (Vector3*)malloc(MAX_VERTEX*sizeof(Vector3)); + scene.v_normals = (Vector3*)malloc(MAX_VERTEX*sizeof(Vector3)); + scene.faces = (Face*)malloc(MAX_FACES*sizeof(Face)); + + if (!scene.verts || !scene.v_normals || !scene.faces) { + printf("Erreur : Memoire insuffisante pour le Mesh.\n"); + return 1; + } + + scene.num_verts = 0; + scene.num_faces = 0; // Création d'objets mixtes add_cylinder(&scene, int_to_f(-50), 0, int_to_f(300), int_to_f(20), int_to_f(80), 16, 2, 0); // Plein add_sphere(&scene, int_to_f(50), 0, int_to_f(300), int_to_f(30), 12, 4, 1); // Toujours filaire - init_vga();  - Matrix3 rot; mat3_identity(&rot); -    - while(!kbhit()) { - clear_buffers(); - render_scene(&scene, &rot, MODE_SOLID); // Mode global : Plein - flip(); - } - close_vga(); return 0; + init_vga(); + mat3_identity(&rot); + + while (key != 27) { // 27 = Touche Echap pour quitter + if (kbhit()) { + key = getch(); + // Gestion ZQSD (Sensibilité de 5 degrés) + if (key == 'z' || key == 'Z') angX = (angX + 5) % 360; + if (key == 's' || key == 'S') angX = (angX + 355) % 360; + if (key == 'q' || key == 'Q') angY = (angY + 355) % 360; + if (key == 'd' || key == 'D') angY = (angY + 5) % 360; + + // Reconstruction de la matrice de rotation combinée + mat3_rotate_x(&tmpX, angX); + mat3_rotate_y(&tmpY, angY); + mat3_mul(&rot, &tmpX, &tmpY); + } + + clear_buffers(0); + render_scene(&scene, &rot, MODE_WIRE); + flip(); + } + close_vga(); + return 0; } +/* +int main() { + init_engine_math(); + init_vga(); // On alloue les buffers ici + + // VÉRIFICATION MANUELLE ICI + if (backbuffer == NULL || zbuffer == NULL) { + printf("Erreur d'allocation des buffers video\n"); + return 1; + } + + while(!kbhit()) { + // Test ultra-basique : on remplit l'écran de rouge + // SANS utiliser le z-buffer pour l'instant + memset(backbuffer, 4, 64000); + flip(); + } + close_vga(); + return 0; +} +*/ diff --git a/src/part3D/defines.h b/src/part3D/defines.h index d5677eb..a11395b 100644 --- a/src/part3D/defines.h +++ b/src/part3D/defines.h @@ -1,3 +1,6 @@ +#ifndef DEFINE_H +#define DEFINE_H + #include #include #include @@ -5,4 +8,99 @@ #include #include -typedef int32_t fixed; \ No newline at end of file +#define MODE_WIRE 0 // Transparent totalv_cache +#define MODE_HIDDEN 1 // Fil de fer avec surfaces cachées +#define MODE_SOLID 2 // Plein (Gouraud) + +#define MAX_VERTEX 5000 +#define MAX_FACES 8000 + +typedef int32_t fixed; + +#define f_to_int(a) ((a) >> 16) +#define int_to_f(a) ((a) << 16) +// #define f_mul(a, b) ((fixed)(((int64_t)(a) * (b)) >> 16)) + +// --- Remplacement des inlines --- +static __inline fixed f_mul(fixed a, fixed b) { return (fixed)(((int64_t)a * b) >> 16); } +static __inline fixed f_div(fixed a, fixed b) { if (b == 0) return 0; return (fixed)(((int64_t)a << 16) / b); } + +// Matrice 3x3 pour la rotation des normales et des sommets +typedef struct { + fixed m[3][3]; +} Matrix3; + +typedef struct { + fixed x, y, z; +} Vector3; + +typedef struct { + int a, b, c; + uint8_t color; + uint8_t force_wire; +} Face; + +typedef struct { + int x, y; + fixed z; + int intensity; +} Point2D; + +typedef struct { + Vector3 *verts; + Vector3 *v_normals; + Face *faces; + int num_verts, num_faces; +} Mesh; + +typedef struct { + Mesh *mesh; // Pointeur vers le modèle 3D (partagé si plusieurs sphères) + Vector3 position; // Position dans le monde + Matrix3 rotation; // Rotation locale de l'objet +} Object3D; + +typedef struct { uint8_t r, g, b; } RGB; + +// --- Ajout des déclarations de tables --- +extern fixed sintab[360]; +extern fixed costab[360]; +extern fixed inv_table[321]; +extern Point2D v_cache[MAX_VERTEX]; +extern uint8_t *backbuffer; +extern uint16_t *zbuffer; +extern uint8_t *vga; + +// Engine.C +void init_engine_math(); +void normalize(Vector3 *v); +void mat3_rotate_x(Matrix3 *m, int angle); +void mat3_rotate_y(Matrix3 *m, int angle); +void mat3_mul(Matrix3 *res, Matrix3 *a, Matrix3 *b); + +// geometry.c +void add_face(Mesh *m, int a, int b, int c, uint8_t col, uint8_t wire); +void add_sphere(Mesh *m, fixed x, fixed y, fixed z, fixed r, 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); +void save_mesh(Mesh *m, const char *fn); +Mesh* load_mesh(const char *fname); +void free_mesh(Mesh *m); +void render_scene(Mesh *m, Matrix3 *rot, int global_mode); + +// Raster.c +void draw_line_z(Point2D *p1, Point2D *p2, uint8_t col, int z_test); +void swap_pt(Point2D **a, Point2D **b); +void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_color); +void fill_triangle_zonly(Point2D *p1, Point2D *p2, Point2D *p3); + +// Graph.c +void init_vga(); +void clear_buffers(uint8_t color); +void flip(); +void close_vga(); + +// Math.c +void mat3_identity(Matrix3 *mat); +void init_engine_math(); +void normalize(Vector3 *v); + +#endif // DEFINE_H diff --git a/src/part3D/engine.c b/src/part3D/engine.c index 715a78c..1b87143 100644 --- a/src/part3D/engine.c +++ b/src/part3D/engine.c @@ -1,49 +1,25 @@ -#ifndef ENGINE_H -#define ENGINE_H - #include "defines.h" -#define f_to_int(a) ((a) >> 16) -#define int_to_f(a) ((a) << 16) -// #define f_mul(a, b) ((fixed)(((int64_t)(a) * (b)) >> 16)) -inline fixed f_mul(fixed a, fixed b) { return (fixed)(((int64_t)a * b) >> 16); } -inline fixed f_div(fixed a, fixed b) { - if (b == 0) return 0; // Évite la division par zéro - return (fixed)(((int64_t)a << 16) / b); -} - -// Matrice 3x3 pour la rotation des normales et des sommets -typedef struct { fixed m[3][3]; } Matrix3; +fixed sintab[360], costab[360], inv_table[321]; +Point2D v_cache[MAX_VERTEX]; void mat3_identity(Matrix3 *mat) { memset(mat, 0, sizeof(Matrix3)); mat->m[0][0] = mat->m[1][1] = mat->m[2][2] = int_to_f(1); } -static fixed sintab[360], costab[360], inv_table[321]; - void init_engine_math() { - for (int i = 0; i < 360; i++) { + int i; + + for (i = 0; i < 360; i++) { double r = (double)i * 3.14159265 / 180.0; sintab[i] = (fixed)(sin(r) * 65536.0); costab[i] = (fixed)(cos(r) * 65536.0); } inv_table[0] = 0; - for (int i = 1; i <= 320; i++) inv_table[i] = (int_to_f(1) / i); + for (i = 1; i <= 320; i++) inv_table[i] = (int_to_f(1) / i); } -typedef struct { fixed x, y, z; } Vector3; - -typedef struct { int a, b, c; uint8_t color; uint8_t force_wire; } Face; -typedef struct { int x, y; fixed z; int intensity; } Point2D; - -typedef struct { - Vector3 *verts; - Vector3 *v_normals; - Face *faces; - int num_verts, num_faces; -} Mesh; - // Normalisation pour le Gouraud (à la création uniquement) void normalize(Vector3 *v) { double dx = (double)v->x, dy = (double)v->y, dz = (double)v->z; @@ -53,4 +29,92 @@ void normalize(Vector3 *v) { } } -#endif +// Génère une matrice de rotation sur l'axe X +void mat3_rotate_x(Matrix3 *m, int angle) { + fixed c = costab[angle % 360]; + fixed s = sintab[angle % 360]; + mat3_identity(m); + m->m[1][1] = c; m->m[1][2] = -s; + m->m[2][1] = s; m->m[2][2] = c; +} + +// Génère une matrice de rotation sur l'axe Y +void mat3_rotate_y(Matrix3 *m, int angle) { + fixed c = costab[angle % 360]; + fixed s = sintab[angle % 360]; + mat3_identity(m); + m->m[0][0] = c; m->m[0][2] = s; + m->m[2][0] = -s; m->m[2][2] = c; +} + +// Multiplie deux matrices 3x3 : res = a * b +void mat3_mul(Matrix3 *res, Matrix3 *a, Matrix3 *b) { + int i, j; + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + res->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][2], b->m[2][j]); + } + } +} + +void render_scene(Mesh *m, Matrix3 *rot, int global_mode) { + int i; + fixed pz, rx, ry, rz; + Vector3 *v; + Vector3 *n; + fixed nz; + int intensity; + + + for (i = 0; i < m->num_verts; i++) { + v = &m->verts[i]; + n = &m->v_normals[i]; + + // --- 1. Rotation de la normale (pour la lumière) --- + 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 = f_to_int(nz * 15); + if (intensity < 0) intensity = 0; + + // --- 2. Rotation du sommet (LA PIÈCE MANQUANTE) --- + // On applique la multiplication Matrice * Vecteur + rx = f_mul(v->x, rot->m[0][0]) + f_mul(v->y, rot->m[1][0]) + f_mul(v->z, rot->m[2][0]); + ry = f_mul(v->x, rot->m[0][1]) + f_mul(v->y, rot->m[1][1]) + f_mul(v->z, rot->m[2][1]); + rz = f_mul(v->x, rot->m[0][2]) + f_mul(v->y, rot->m[1][2]) + f_mul(v->z, rot->m[2][2]); + + // --- 3. Projection avec les coordonnées tournées --- + pz = rz; // On utilise le Z tourné + if (pz < int_to_f(10)) pz = int_to_f(10); + + v_cache[i].z = pz; + v_cache[i].intensity = intensity; + v_cache[i].x = 160 + f_to_int(f_div(rx << 8, pz)); // On utilise rx tourné + v_cache[i].y = 100 - f_to_int(f_div(ry << 8, pz)); // On utilise ry tourné + } + + for (i = 0; i < m->num_faces; i++) { + Face *f = &m->faces[i]; + Point2D *p1 = &v_cache[f->a]; + Point2D *p2 = &v_cache[f->b]; + Point2D *p3 = &v_cache[f->c]; + int back = (p2->x - p1->x) * (p3->y - p1->y) - (p2->y - p1->y) * (p3->x - p1->x); + + if (global_mode == MODE_WIRE) { + draw_line_z(p1, p2, f->color, 0); + draw_line_z(p2, p3, f->color, 0); + draw_line_z(p3, p1, f->color, 0); + } else { + if (back < 0) { + if (f->force_wire || global_mode == MODE_HIDDEN) { + fill_triangle_zonly(p1, p2, p3); + draw_line_z(p1, p2, f->color, 1); + draw_line_z(p2, p3, f->color, 1); + draw_line_z(p3, p1, f->color, 1); // Ajouté pour fermer le triangle + } else { + fill_triangle_gouraud(p1, p2, p3, f->color); + } + } + } + } +} diff --git a/src/part3D/geometry.c b/src/part3D/geometry.c index 80b3f05..366c3a6 100644 --- a/src/part3D/geometry.c +++ b/src/part3D/geometry.c @@ -1,112 +1,118 @@ -#define GEOMETRY_H - -#define MODE_WIRE 0 // Transparent total -#define MODE_HIDDEN 1 // Fil de fer avec surfaces cachées -#define MODE_SOLID 2  // Plein (Gouraud) - -Point2D v_cache[5000]; +#include "defines.h" void add_face(Mesh *m, int a, int b, int c, uint8_t col, uint8_t wire) { - m->faces[m->num_faces++] = (Face){a, b, c, col, wire}; + Face *f = &m->faces[m->num_faces++]; + f->a = a; + f->b = b; + f->c = c; + f->color = col; + f->force_wire = wire; } void add_sphere(Mesh *m, fixed x, fixed y, fixed z, fixed r, int det, uint8_t col, uint8_t wire) { - int v_base = m->num_verts; - for (int j = 0; j <= det; j++) { + int i, j, v_base; + v_base = m->num_verts; + + for (j = 0; j <= det; j++) { fixed sin_phi = sintab[(j * 180 / det) % 360]; fixed cos_phi = costab[(j * 180 / det) % 360]; - for (int i = 0; i < det; i++) { + for (i = 0; i < det; i++) { + Vector3 *v = &m->verts[m->num_verts]; + Vector3 *n = &m->v_normals[m->num_verts]; fixed sin_th = sintab[(i * 360 / det) % 360]; fixed cos_th = costab[(i * 360 / det) % 360]; - m->verts[m->num_verts] = (Vector3){x + f_mul(r, f_mul(sin_phi, cos_th)), y + f_mul(r, cos_phi), z + f_mul(r, f_mul(sin_phi, sin_th))}; - m->v_normals[m->num_verts] = (Vector3){f_mul(sin_phi, cos_th), cos_phi, f_mul(sin_phi, sin_th)}; + + v->x = x + f_mul(r, f_mul(sin_phi, cos_th)); + v->y = y + f_mul(r, cos_phi); + v->z = z + f_mul(r, f_mul(sin_phi, sin_th)); + + n->x = f_mul(sin_phi, cos_th); + n->y = cos_phi; + n->z = f_mul(sin_phi, sin_th); + m->num_verts++; } } - for (int j = 0; j < det; j++) { - for (int i = 0; i < det; i++) { - int a = v_base + j * det + i, b = v_base + j * det + (i + 1) % det; - int c = v_base + (j + 1) * det + i, d = v_base + (j + 1) * det + (i + 1) % det; - add_face(m, a, b, c, col, wire); add_face(m, b, d, c, col, wire); + for (j = 0; j < det; j++) { + for (i = 0; i < det; i++) { + int a = v_base + j * det + i; + int b = v_base + j * det + (i + 1) % det; + int c = v_base + (j + 1) * det + i; + int d = v_base + (j + 1) * det + (i + 1) % det; + add_face(m, a, b, c, col, wire); + add_face(m, b, d, c, col, 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 v_base = m->num_verts; - for (int i = 0; i < det; i++) { - fixed s = sintab[(i * 360 / det) % 360], c = costab[(i * 360 / det) % 360]; - m->verts[m->num_verts] = (Vector3){x + f_mul(r, c), y - h / 2, z + f_mul(r, s)}; - m->v_normals[m->num_verts] = (Vector3){c, 0, s}; m->num_verts++; - m->verts[m->num_verts] = (Vector3){x + f_mul(r, c), y + h / 2, z + f_mul(r, s)}; - m->v_normals[m->num_verts] = (Vector3){c, 0, s}; m->num_verts++; + int i, v_base; + v_base = m->num_verts; + + for (i = 0; i < det; i++) { + Vector3 *v1 = &m->verts[m->num_verts]; + Vector3 *n1 = &m->v_normals[m->num_verts]; + fixed s = sintab[(i * 360 / det) % 360]; + fixed c = costab[(i * 360 / det) % 360]; + + v1->x = x + f_mul(r, c); v1->y = y - h / 2; v1->z = z + f_mul(r, s); + n1->x = c; n1->y = 0; n1->z = s; + m->num_verts++; + + { // Bloc pour v2/n2 + Vector3 *v2 = &m->verts[m->num_verts]; + Vector3 *n2 = &m->v_normals[m->num_verts]; + v2->x = x + f_mul(r, c); v2->y = y + h / 2; v2->z = z + f_mul(r, s); + n2->x = c; n2->y = 0; n2->z = s; + m->num_verts++; + } } - for (int i = 0; i < det; i++) { - int a = v_base + i * 2, b = v_base + ((i * 2 + 2) % (det * 2)), c = a + 1, d = b + 1; - add_face(m, a, b, c, col, wire); add_face(m, b, d, c, col, wire); + for (i = 0; i < det; i++) { + int a = v_base + i * 2; + int b = v_base + ((i * 2 + 2) % (det * 2)); + int c = a + 1; + int d = b + 1; + add_face(m, a, b, c, col, wire); + add_face(m, b, d, c, col, wire); } } void save_mesh(Mesh *m, const char *fn) { - FILE *f = fopen(fn, "wb");  - fwrite(&m->num_verts, 4, 1, f);  - fwrite(&m->num_faces, 4, 1, f); - fwrite(m->verts, 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);  - fclose(f); + FILE *f = fopen(fn, "wb"); + if (!f) return; + fwrite(&m->num_verts, sizeof(int), 1, f); + fwrite(&m->num_faces, sizeof(int), 1, f); + fwrite(m->verts, 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); + fclose(f); } Mesh* load_mesh(const char *fname) { - FILE *f = fopen(fname, "rb"); - if (!f) return NULL; - Mesh *m = (Mesh*)malloc(sizeof(Mesh)); - fread(&m->num_verts, 4, 1, f); - fread(&m->num_faces, 4, 1, f); - fread(&m->is_wire_only, 1, 1, f); - m->verts = (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); - fread(m->verts, 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); - fclose(f); - return m; + Mesh *m; + FILE *f = fopen(fname, "rb"); + if (!f) return NULL; + + m = (Mesh*)malloc(sizeof(Mesh)); + fread(&m->num_verts, sizeof(int), 1, f); + fread(&m->num_faces, sizeof(int), 1, f); + // Suppression de is_wire_only car absent du .h + + m->verts = (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); + + fread(m->verts, 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); + fclose(f); + return m; } void free_mesh(Mesh *m) { - if (!m) return; - if (m->verts) free(m->verts); - if (m->faces) free(m->faces); - if (m->v_normals) free(m->v_normals); - free(m); -} - -void render_scene(Mesh *m, Matrix3 *rot, int global_mode) { - for (int i = 0; i < m->num_verts; i++) { - Vector3 *v = &m->verts[i]; Vector3 *n = &m->v_normals[i]; - fixed 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]); - int intensity = f_to_int(nz * 15); if (intensity < 0) intensity = 0; - v_cache[i].z = v->z; v_cache[i].intensity = intensity; - v_cache[i].x = 160 + f_to_int(f_div(v->x << 8, v->z)); - v_cache[i].y = 100 - f_to_int(f_div(v->y << 8, v->z)); - } - - for (int i = 0; i < m->num_faces; i++) { - Face *f = &m->faces[i]; Point2D *p1 = &v_cache[f->a], *p2 = &v_cache[f->b], *p3 = &v_cache[f->c]; - int back = (p2->x - p1->x) * (p3->y - p1->y) - (p2->y - p1->y) * (p3->x - p1->x); -    - if (global_mode == MODE_WIRE) { - draw_line_z(p1, p2, f->color, 0); draw_line_z(p2, p3, f->color, 0); draw_line_z(p3, p1, f->color, 0); - } else { - if (back < 0) { - if (f->force_wire || global_mode == MODE_HIDDEN) { - fill_triangle_zonly(p1, p2, p3); // Pas de couleur, juste Z - draw_line_z(p1, p2, f->color, 1); draw_line_z(p2, p3, f->color, 1); - } else { - fill_triangle_gouraud(p1, p2, p3, f->color); - } - } - } - } + if (!m) return; + if (m->verts) free(m->verts); + if (m->faces) free(m->faces); + if (m->v_normals) free(m->v_normals); + free(m); } diff --git a/src/part3D/graph.c b/src/part3D/graph.c index ad8317f..4138ff1 100644 --- a/src/part3D/graph.c +++ b/src/part3D/graph.c @@ -1,57 +1,80 @@ -#define GRAPH_H +#include "defines.h" RGB my_palette[16] = { - {0, 0, 0},  // 0: Noir - {63, 0, 0}, // 1: Rouge vif - {0, 63, 0}, // 2: Vert vif - {0, 0, 63}, // 3: Bleu vif - {63, 63, 0}, // 4: Jaune - {63, 0, 63}, // 5: Magenta - {0, 63, 63}, // 6: Cyan - {63, 63, 63},// 7: Blanc - {31, 0, 0}, // 8: Rouge sombre - {0, 31, 0}, // 9: Vert sombre - {0, 0, 31}, // 10: Bleu sombre - {31, 31, 31},// 11: Gris - {63, 31, 0}, // 12: Orange - {31, 63, 0}, // 13: Citron vert - {40, 20, 0}, // 14: Marron - {20, 20, 40} // 15: Bleu nuit -};  + {0, 0, 0}, // 0: Noir + {63, 0, 0}, // 1: Rouge vif + {0, 63, 0}, // 2: Vert vif + {0, 0, 63}, // 3: Bleu vif + {63, 63, 0}, // 4: Jaune + {63, 0, 63}, // 5: Magenta + {0, 63, 63}, // 6: Cyan + {63, 63, 63}, // 7: Blanc + {31, 0, 0}, // 8: Rouge sombre + {0, 31, 0}, // 9: Vert sombre + {0, 0, 31}, // 10: Bleu sombre + {31, 31, 31}, // 11: Gris + {63, 31, 0}, // 12: Orange + {31, 63, 0}, // 13: Citron vert + {40, 20, 0}, // 14: Marron + {20, 20, 40} // 15: Bleu nuit +}; -typedef struct { - uint8_t *screen; - uint8_t *backbuffer; - uint16_t *zbuffer; -} VideoSystem; +uint8_t *backbuffer = NULL; +uint16_t *zbuffer = NULL; +uint8_t *vga = (uint8_t *)0xA0000; -VideoSystem vid; +void set_mode(int mode); +#pragma aux set_mode = "int 0x10" parm [ax]; -void init_vga() { - // Mode 13h via interruption BIOS - union { struct { uint8_t al, ah; } b; uint16_t w; } r; - r.b.al = 0x13; r.b.ah = 0x00; - // Utilisation de l'inline assembly Watcom - #pragma aux set_mode = "int 0x10" parm [ax]; - set_mode(0x0013); - - vid.screen = (uint8_t *)0xA0000; - vid.backbuffer = (uint8_t *)malloc(64000); - vid.zbuffer = (uint16_t *)malloc(64000 * sizeof(uint16_t)); -} +void reset_mode(int mode); +#pragma aux reset_mode = "int 0x10" parm [ax]; void clear_buffers(uint8_t color) { - memset(vid.backbuffer, color, 64000); - memset(vid.zbuffer, 0xFF, 64000 * sizeof(uint16_t)); // Z-buffer à l'infini + memset(backbuffer, color, 64000); + memset(zbuffer, 0xFF, 64000 * sizeof(uint16_t)); } void flip() { - memcpy(vid.screen, vid.backbuffer, 64000); + memcpy(vga, backbuffer, 64000); } void close_vga() { - #pragma aux reset_mode = "int 0x10" parm [ax]; - reset_mode(0x0003); - free(vid.backbuffer); - free(vid.zbuffer); + reset_mode(0x0003); // Retour au mode texte + if (backbuffer) free(backbuffer); + if (zbuffer) free(zbuffer); } + +// Prend 16 couleurs RGB (0-63) et remplit les 256 entrées du DAC +void setup_vga_palette(RGB *base_colors) { + int i, c; + // Port 0x3C8 : Index de départ (0) + outp(0x3C8, 0); + + for (c = 0; c < 16; c++) { + 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 g = (uint8_t)((base_colors[c].g * 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, g); + outp(0x3C9, b); + } + } +} + +void init_vga() { + // On passe en mode 13h (320x200x256) + set_mode(0x0013); + + // Allocations + backbuffer = (uint8_t *)malloc(64000); + zbuffer = (uint16_t *)malloc(64000 * sizeof(uint16_t)); + + // Note : Pense à ajouter ici une boucle pour envoyer my_palette + // aux ports 0x3C8/0x3C9 si tu veux tes couleurs personnalisées. + // setup_vga_palette(my_palette); +} \ No newline at end of file diff --git a/src/part3D/raster.c b/src/part3D/raster.c index d3ad8b2..408d015 100644 --- a/src/part3D/raster.c +++ b/src/part3D/raster.c @@ -1,42 +1,101 @@ -#define RASTER_H - -uint8_t *backbuffer, *vga = (uint8_t*)0xA0000; -uint16_t *zbuffer; - - +#include "defines.h" // Prototypes pour le compilateur C -extern void scanline_gouraud_asm(int width, uint8_t *dest, uint16_t *zbuf, int z, int dz_step, int intensity, int di_step); -extern void scanline_zonly_asm(int width, uint16_t *zbuf, int z, int dz_step); +// extern void scanline_gouraud_asm(int width, uint8_t *dest, uint16_t *zbuf, int z, int dz_step, int intensity, int di_step); +extern void scanline_zonly_asm(int width, uint16_t *zbuf, int z, int dz_step); +/* #pragma aux scanline_gouraud_asm = \ - "test ecx, ecx" "jz d" \ - "l:" "mov ax, dx" "cmp ax, [esi]" "jae s" "mov [esi], ax" "mov [edi], bh" \ - "s:" "add edx, ebp" "add ebx, eax" "inc edi" "add esi, 2" "dec ecx" "jnz l" "d:" \ - parm [ecx] [edi] [esi] [edx] [ebp] [ebx] [eax] modify [ax ecx edi esi edx ebx]; + "test ecx, ecx" \ + "jz done" \ +"loop_l:" \ + "mov eax, edx" \ + "shr eax, 16" /* EAX = Z entier * / \ + "cmp ax, [esi]" \ + "jae skip_p" \ + "mov [esi], ax" /* Z-Write * / \ + "mov eax, ebx" \ + "shr eax, 8" \ + "mov [edi], al" /* Color-Write * / \ +"skip_p:" \ + "add edx, ebp" /* + dz_step (EBP) * / \ + "add ebx, [esp+4]" /* + di_step (LU SUR LA PILE) * / \ + "inc edi" \ + "add esi, 2" \ + "dec ecx" \ + "jnz loop_l" \ +"done:" \ + parm [ecx] [edi] [esi] [edx] [ebp] [ebx] \ + modify [eax ecx edi esi edx ebx]; +*/ #pragma aux scanline_zonly_asm = \ - "test ecx, ecx" "jz d" \ - "l:" "mov ax, dx" "cmp ax, [esi]" "jae s" "mov [esi], ax" \ - "s:" "add edx, ebx" "add esi, 2" "dec ecx" "jnz l" "d:" \ - parm [ecx] [esi] [edx] [ebx] modify [ax ecx esi edx]; + "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]; + + +#include "defines.h" + +// Ajout de base_color en paramètre +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; + uint8_t shade; + + // On calcule le décalage de bloc (ex: couleur 1 commence à l'index 16) + uint8_t color_offset = (uint8_t)(base_color << 4); + + for (i = 0; i < width; i++) { + uint16_t zv = (uint16_t)(z >> 16); + if (zv < zbuf[i]) { + zbuf[i] = zv; + // On extrait l'intensité (on travaille en 16.16 pour la précision) + shade = (uint8_t)(intensity >> 16); + if (shade > 15) shade = 15; // Sécurité pour ne pas déborder sur la couleur suivante + + dest[i] = color_offset + shade; + } + z += dz_step; + intensity += di_step; + } +} void draw_line_z(Point2D *p1, Point2D *p2, uint8_t col, int z_test) { - int dx = abs(p2->x - p1->x), dy = abs(p2->y - p1->y); - int sx = (p1->x < p2->x) ? 1 : -1, sy = (p1->y < p2->y) ? 1 : -1; - int err = dx - dy, steps = (dx > dy) ? dx : dy; - fixed z_step = (steps > 0) ? (p2->z - p1->z) / steps : 0, cz = p1->z; - int x = p1->x, y = p1->y; + int dx, dy, sx, sy, err, steps, x, y, e2; + fixed z_step, cz; + uint16_t zv; + + dx = abs(p2->x - p1->x); dy = abs(p2->y - p1->y); + sx = (p1->x < p2->x) ? 1 : -1; sy = (p1->y < p2->y) ? 1 : -1; + err = dx - dy; + steps = (dx > dy) ? dx : dy; + z_step = (steps > 0) ? (p2->z - p1->z) / steps : 0; + cz = p1->z; + x = p1->x; y = p1->y; + while (1) { if (x >= 0 && x < 320 && y >= 0 && y < 200) { - uint16_t zv = (uint16_t)f_to_int(cz); + zv = (uint16_t)f_to_int(cz); if (!z_test || zv <= zbuffer[y * 320 + x]) { backbuffer[y * 320 + x] = col; if (z_test) zbuffer[y * 320 + x] = zv; } } if (x == p2->x && y == p2->y) break; - int e2 = 2 * err; + e2 = 2 * err; if (e2 > -dy) { err -= dy; x += sx; } if (e2 < dx) { err += dx; y += sy; } cz += z_step; @@ -48,23 +107,27 @@ void swap_pt(Point2D **a, Point2D **b) { } void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_color) { + int y, total_height, width, start_x, end_x, dz_step, intensity_1, intensity_2, di_step, clip_x, w, start_z, start_i, second_half; + fixed dx13, dz13, di13, xA, zA, iA, xB, zB, iB, dx12, dz12, di12, dx23, dz23, di23, cx1, cz1, ci1, cx2, cz2, ci2, tx, tz, ti; + uint8_t *dest; + uint16_t *zbuf; + if (p1->y > p2->y) swap_pt(&p1, &p2); if (p1->y > p3->y) swap_pt(&p1, &p3); if (p2->y > p3->y) swap_pt(&p2, &p3); - int total_height = p3->y - p1->y; + total_height = p3->y - p1->y; if (total_height == 0) return; - // Incréments pour l'arête principale (p1 -> p3) - fixed dx13 = ((p3->x - p1->x) << 16) / total_height; - fixed dz13 = (p3->z - p1->z) / total_height; - fixed di13 = ((p3->intensity - p1->intensity) << 16) / total_height; + dx13 = ((p3->x - p1->x) << 16) / total_height; + dz13 = (p3->z - p1->z) / total_height; + di13 = ((p3->intensity - p1->intensity) << 16) / total_height; - fixed xA = int_to_f(p1->x), zA = p1->z, iA = int_to_f(p1->intensity); - fixed xB = xA, zB = zA, iB = iA; + xA = int_to_f(p1->x); zA = p1->z; iA = int_to_f(p1->intensity); + xB = xA; zB = zA; iB = iA; - fixed dx12 = 0, dz12 = 0, di12 = 0; - fixed dx23 = 0, dz23 = 0, di23 = 0; + dx12 = 0; dz12 = 0; di12 = 0; + dx23 = 0; dz23 = 0; di23 = 0; if (p2->y > p1->y) { dx12 = ((p2->x - p1->x) << 16) / (p2->y - p1->y); @@ -77,37 +140,50 @@ void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_c di23 = ((p3->intensity - p2->intensity) << 16) / (p3->y - p2->y); } - for (int y = p1->y; y < p3->y; y++) { - int second_half = (y >= p2->y); - fixed cx1 = xA, cz1 = zA, ci1 = iA; - fixed cx2 = xB, cz2 = zB, ci2 = iB; + for (y = p1->y; y < p3->y; y++) { + second_half = (y >= p2->y); + cx1 = xA; cz1 = zA; ci1 = iA; + cx2 = xB; cz2 = zB; ci2 = iB; - if (cx1 > cx2) { // Tri horizontal pour tracer de gauche à droite - fixed tx = cx1; cx1 = cx2; cx2 = tx; - fixed tz = cz1; cz1 = cz2; cz2 = tz; - fixed ti = ci1; ci1 = ci2; ci2 = ti; + if (cx1 > cx2) { + tx = cx1; cx1 = cx2; cx2 = tx; + tz = cz1; cz1 = cz2; cz2 = tz; + ti = ci1; ci1 = ci2; ci2 = ti; } - int start_x = f_to_int(cx1), end_x = f_to_int(cx2); - int width = end_x - start_x; + start_x = f_to_int(cx1); end_x = f_to_int(cx2); + width = end_x - start_x; if (y >= 0 && y < 200 && width > 0) { - int dz_step = (cz2 - cz1) / width; - int intensity_1 = f_to_int(ci1), intensity_2 = f_to_int(ci2); - int di_step = ((intensity_2 - intensity_1) << 8) / width; + dz_step = (cz2 - cz1) / width; + // intensity_1 = f_to_int(ci1); + // intensity_2 = f_to_int(ci2); + + intensity_1 = p1->intensity; + intensity_2 = p2->intensity; + + // di_step = ((intensity_2 - intensity_1) << 8) / width; + di_step = (ci2 - ci1) / width; + + clip_x = start_x < 0 ? 0 : start_x; - int clip_x = start_x < 0 ? 0 : start_x; if (clip_x < 320) { - int w = width - (clip_x - start_x); + w = width - (clip_x - start_x); if (clip_x + w > 320) w = 320 - clip_x; if (w > 0) { - int start_z = cz1 + (clip_x - start_x) * dz_step; - int start_i = ((base_color + intensity_1) << 8) + (clip_x - start_x) * di_step; + start_z = cz1 + (clip_x - start_x) * dz_step; + /*start_i = ((base_color + intensity_1) << 8) + (clip_x - start_x) * di_step; + dest = backbuffer + (y * 320) + clip_x; + zbuf = zbuffer + (y * 320) + clip_x; + //scanline_gouraud_asm(w, dest, zbuf, start_z, dz_step, start_i, di_step); + scanline_gouraud_c(w, dest, zbuf, start_z, dz_step, start_i, di_step);*/ - uint8_t *dest = vid.backbuffer + (y * 320) + clip_x; - uint16_t *zbuf = vid.zbuffer + (y * 320) + clip_x; - scanline_gouraud_asm(w, dest, zbuf, start_z, dz_step, start_i, di_step); + start_i = ci1 + (clip_x - start_x) * di_step; + + dest = backbuffer + (y * 320) + clip_x; + zbuf = zbuffer + (y * 320) + clip_x; + scanline_gouraud_c(w, dest, zbuf, start_z, dz_step, start_i, di_step, base_color); } } } @@ -123,20 +199,24 @@ void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_c } void fill_triangle_zonly(Point2D *p1, Point2D *p2, Point2D *p3) { + int y, total_height, width, start_x, end_x, dz_step, clip_x, w, start_z, second_half; + fixed dx13, dz13, xA, zA, xB, zB, dx12, dz12, dx23, dz23, cx1, cz1, cx2, cz2, tx, tz; + uint16_t *zbuf; + if (p1->y > p2->y) swap_pt(&p1, &p2); if (p1->y > p3->y) swap_pt(&p1, &p3); if (p2->y > p3->y) swap_pt(&p2, &p3); - int total_height = p3->y - p1->y; + total_height = p3->y - p1->y; if (total_height == 0) return; - fixed dx13 = ((p3->x - p1->x) << 16) / total_height; - fixed dz13 = (p3->z - p1->z) / total_height; + dx13 = ((p3->x - p1->x) << 16) / total_height; + dz13 = (p3->z - p1->z) / total_height; - fixed xA = int_to_f(p1->x), zA = p1->z; - fixed xB = xA, zB = zA; + xA = int_to_f(p1->x); zA = p1->z; + xB = xA; zB = zA; - fixed dx12 = 0, dz12 = 0, dx23 = 0, dz23 = 0; + dx12 = 0; dz12 = 0; dx23 = 0; dz23 = 0; if (p2->y > p1->y) { dx12 = ((p2->x - p1->x) << 16) / (p2->y - p1->y); @@ -147,29 +227,29 @@ void fill_triangle_zonly(Point2D *p1, Point2D *p2, Point2D *p3) { dz23 = (p3->z - p2->z) / (p3->y - p2->y); } - for (int y = p1->y; y < p3->y; y++) { - int second_half = (y >= p2->y); - fixed cx1 = xA, cz1 = zA, cx2 = xB, cz2 = zB; + for (y = p1->y; y < p3->y; y++) { + second_half = (y >= p2->y); + cx1 = xA; cz1 = zA; cx2 = xB; cz2 = zB; if (cx1 > cx2) { - fixed tx = cx1; cx1 = cx2; cx2 = tx; - fixed tz = cz1; cz1 = cz2; cz2 = tz; + tx = cx1; cx1 = cx2; cx2 = tx; + tz = cz1; cz1 = cz2; cz2 = tz; } - int start_x = f_to_int(cx1), end_x = f_to_int(cx2); - int width = end_x - start_x; + start_x = f_to_int(cx1); end_x = f_to_int(cx2); + width = end_x - start_x; if (y >= 0 && y < 200 && width > 0) { - int dz_step = (cz2 - cz1) / width; - int clip_x = start_x < 0 ? 0 : start_x; + dz_step = (cz2 - cz1) / width; + clip_x = start_x < 0 ? 0 : start_x; if (clip_x < 320) { - int w = width - (clip_x - start_x); + w = width - (clip_x - start_x); if (clip_x + w > 320) w = 320 - clip_x; if (w > 0) { - int start_z = cz1 + (clip_x - start_x) * dz_step; - uint16_t *zbuf = vid.zbuffer + (y * 320) + clip_x; + start_z = cz1 + (clip_x - start_x) * dz_step; + zbuf = zbuffer + (y * 320) + clip_x; scanline_zonly_asm(w, zbuf, start_z, dz_step); } } @@ -183,4 +263,4 @@ void fill_triangle_zonly(Point2D *p1, Point2D *p2, Point2D *p3) { xB += dx23; zB += dz23; } } -} \ No newline at end of file +}