commit 80d8141b5a8d8313052b91091428f80ca029b582 Author: Frater <13979047+saintfrater@users.noreply.github.com> Date: Sat May 9 14:37:14 2026 +0200 fistcommit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..31e0c57 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +voyager.code-workspace +.vscode +build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f6b252a --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +# --- 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) +else + # Chemins Debian (à adapter selon ton install Linux) + WATCOM := /opt/watcom + CC := $(WATCOM)/binl64/wcc386 + AS := nasm + LINK := $(WATCOM)/binl64/wlink + FIX_PATH = $1 +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) + +# --- 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") + +# 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)) + +# --- Règles --- +all: build/app.exe + +# Création du dossier build miroir de src +build/%.obj: src/%.cpp + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $< -fo=$(call FIX_PATH,$@) + +build/%.obj: src/%.asm + @mkdir -p $(dir $@) + $(AS) -f obj $< -o $(call FIX_PATH,$@) + +build/app.exe: $(OBJS) + $(LINK) $(LFLAGS) name $(call FIX_PATH,$@) file {$(foreach f,$(OBJS),$(call FIX_PATH,$f))} + +clean: + rm -rf build/* \ No newline at end of file diff --git a/src/main/main.c b/src/main/main.c new file mode 100644 index 0000000..029692f --- /dev/null +++ b/src/main/main.c @@ -0,0 +1,25 @@ +#define MAIN_C + +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; + + // 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; +} + diff --git a/src/part3D/defines.h b/src/part3D/defines.h new file mode 100644 index 0000000..d5677eb --- /dev/null +++ b/src/part3D/defines.h @@ -0,0 +1,8 @@ +#include +#include +#include +#include +#include +#include + +typedef int32_t fixed; \ No newline at end of file diff --git a/src/part3D/engine.c b/src/part3D/engine.c new file mode 100644 index 0000000..715a78c --- /dev/null +++ b/src/part3D/engine.c @@ -0,0 +1,56 @@ +#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; + +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++) { + 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); +} + +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; + 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); + } +} + +#endif diff --git a/src/part3D/geometry.c b/src/part3D/geometry.c new file mode 100644 index 0000000..80b3f05 --- /dev/null +++ b/src/part3D/geometry.c @@ -0,0 +1,112 @@ +#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]; + +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}; +} + +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++) { + fixed sin_phi = sintab[(j * 180 / det) % 360]; + fixed cos_phi = costab[(j * 180 / det) % 360]; + for (int i = 0; i < det; i++) { + 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)}; + 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); + } + } +} + +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++; + } + 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); + } +} + +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); +} + +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; +} + +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); + } + } + } + } +} diff --git a/src/part3D/graph.c b/src/part3D/graph.c new file mode 100644 index 0000000..ad8317f --- /dev/null +++ b/src/part3D/graph.c @@ -0,0 +1,57 @@ +#define GRAPH_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 +};  + +typedef struct { + uint8_t *screen; + uint8_t *backbuffer; + uint16_t *zbuffer; +} VideoSystem; + +VideoSystem vid; + +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 clear_buffers(uint8_t color) { + memset(vid.backbuffer, color, 64000); + memset(vid.zbuffer, 0xFF, 64000 * sizeof(uint16_t)); // Z-buffer à l'infini +} + +void flip() { + memcpy(vid.screen, vid.backbuffer, 64000); +} + +void close_vga() { + #pragma aux reset_mode = "int 0x10" parm [ax]; + reset_mode(0x0003); + free(vid.backbuffer); + free(vid.zbuffer); +} diff --git a/src/part3D/raster.c b/src/part3D/raster.c new file mode 100644 index 0000000..d3ad8b2 --- /dev/null +++ b/src/part3D/raster.c @@ -0,0 +1,186 @@ +#define RASTER_H + +uint8_t *backbuffer, *vga = (uint8_t*)0xA0000; +uint16_t *zbuffer; + + + +// 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); + +#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]; + +#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]; + +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; + while (1) { + if (x >= 0 && x < 320 && y >= 0 && y < 200) { + uint16_t 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; + if (e2 > -dy) { err -= dy; x += sx; } + if (e2 < dx) { err += dx; y += sy; } + cz += z_step; + } +} + +void swap_pt(Point2D **a, Point2D **b) { + Point2D *t = *a; *a = *b; *b = t; +} + +void fill_triangle_gouraud(Point2D *p1, Point2D *p2, Point2D *p3, uint8_t base_color) { + 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; + 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; + + fixed xA = int_to_f(p1->x), zA = p1->z, iA = int_to_f(p1->intensity); + fixed xB = xA, zB = zA, iB = iA; + + fixed dx12 = 0, dz12 = 0, di12 = 0; + fixed dx23 = 0, dz23 = 0, di23 = 0; + + if (p2->y > p1->y) { + dx12 = ((p2->x - p1->x) << 16) / (p2->y - p1->y); + dz12 = (p2->z - p1->z) / (p2->y - p1->y); + di12 = ((p2->intensity - p1->intensity) << 16) / (p2->y - p1->y); + } + if (p3->y > p2->y) { + dx23 = ((p3->x - p2->x) << 16) / (p3->y - p2->y); + dz23 = (p3->z - p2->z) / (p3->y - p2->y); + 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; + + 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; + } + + int start_x = f_to_int(cx1), end_x = f_to_int(cx2); + int 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; + + int clip_x = start_x < 0 ? 0 : start_x; + if (clip_x < 320) { + int 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; + + 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); + } + } + } + + xA += dx13; zA += dz13; iA += di13; + if (!second_half) { + xB += dx12; zB += dz12; iB += di12; + } else { + if (y == p2->y) { xB = int_to_f(p2->x); zB = p2->z; iB = int_to_f(p2->intensity); } + xB += dx23; zB += dz23; iB += di23; + } + } +} + +void fill_triangle_zonly(Point2D *p1, Point2D *p2, Point2D *p3) { + 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; + if (total_height == 0) return; + + fixed dx13 = ((p3->x - p1->x) << 16) / total_height; + fixed dz13 = (p3->z - p1->z) / total_height; + + fixed xA = int_to_f(p1->x), zA = p1->z; + fixed xB = xA, zB = zA; + + fixed dx12 = 0, dz12 = 0, dx23 = 0, dz23 = 0; + + if (p2->y > p1->y) { + dx12 = ((p2->x - p1->x) << 16) / (p2->y - p1->y); + dz12 = (p2->z - p1->z) / (p2->y - p1->y); + } + if (p3->y > p2->y) { + dx23 = ((p3->x - p2->x) << 16) / (p3->y - p2->y); + 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; + + if (cx1 > cx2) { + fixed tx = cx1; cx1 = cx2; cx2 = tx; + fixed 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; + + if (y >= 0 && y < 200 && width > 0) { + int dz_step = (cz2 - cz1) / width; + int clip_x = start_x < 0 ? 0 : start_x; + + if (clip_x < 320) { + int 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; + scanline_zonly_asm(w, zbuf, start_z, dz_step); + } + } + } + + xA += dx13; zA += dz13; + if (!second_half) { + xB += dx12; zB += dz12; + } else { + if (y == p2->y) { xB = int_to_f(p2->x); zB = p2->z; } + xB += dx23; zB += dz23; + } + } +} \ No newline at end of file