From 9463e07e2813cea94b9fd430b2fad938cad82673 Mon Sep 17 00:00:00 2001 From: kkard2 Date: Sun, 21 Sep 2025 14:09:45 +0200 Subject: add perspective matrix and mesh drawing --- example.h | 9 +++++ examples/perspective.c | 48 +++++++++++++++++++++++++++ platform_win32.c | 17 +++++++--- sponge.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 examples/perspective.c diff --git a/example.h b/example.h index df671e1..29212aa 100644 --- a/example.h +++ b/example.h @@ -1,8 +1,11 @@ #include +#define PI ((float)3.14159265358979323846) + void init(); void draw_frame(sponge_Texture c); void mouse_move(int32_t x, int32_t y); +void draw_frame_3d(sponge_Texture c, float *depths); #ifdef SPONGE_EXAMPLE_IMPLEMENTATION @@ -14,4 +17,10 @@ void init() {} void mouse_move(int32_t x, int32_t y) {} #endif // SPONGE_EXAMPLE_MOUSE_MOVE_DEFINED +#ifndef SPONGE_EXAMPLE_DRAW_FRAME_3D_DEFINED +void draw_frame_3d(sponge_Texture c, float *depths) { + draw_frame(c); +} +#endif // SPONGE_EXAMPLE_DRAW_FRAME_3D_DEFINED + #endif // SPONGE_EXAMPLE_IMPLEMENTATION diff --git a/examples/perspective.c b/examples/perspective.c new file mode 100644 index 0000000..5a2e68a --- /dev/null +++ b/examples/perspective.c @@ -0,0 +1,48 @@ +#include "../sponge.h" +#define SPONGE_EXAMPLE_IMPLEMENTATION +#define SPONGE_EXAMPLE_MOUSE_MOVE_DEFINED +#define SPONGE_EXAMPLE_DRAW_FRAME_3D_DEFINED +#include "../example.h" + +#define FOV (60.0f / PI * 2.0f) + +static int32_t mouse_x; +static int32_t mouse_y; + +void mouse_move(int32_t x, int32_t y) { + mouse_x = x; + mouse_y = y; +} + +void draw_frame_3d(sponge_Texture c, float *depths) { + sponge_clear_3d(c, depths, sponge_color32_make(0xFF000000)); + + sponge_Vec3 positions[] = { + sponge_vec3_make(-0.5f, 0.5f, 0.0f), + sponge_vec3_make( 0.5f, 0.5f, 0.0f), + sponge_vec3_make(-0.5f, -0.5f, 0.0f), + sponge_vec3_make( 0.5f, -0.5f, 0.0f), + }; + + sponge_Color32 colors[] = { + sponge_color32_make(0xFF00FFFF), + sponge_color32_make(0xFFFFFFFF), + sponge_color32_make(0xFF0000FF), + sponge_color32_make(0xFFFF00FF), + }; + + int32_t triangles[] = { + 0, 1, 2, + 2, 1, 3, + }; + + sponge_Mat4 model = sponge_mat4_identity(); + model = sponge_mat4_mul_mat4(model, sponge_mat4_rotate( + (float)(mouse_y - ((int32_t)c.height / 2)) / (float)c.height * PI, + (float)(mouse_x - ((int32_t)c.width / 2)) / (float)c.width * PI, + 0.0f)); + model = sponge_mat4_mul_mat4(model, sponge_mat4_translate(0.0f, 0.0f, -5.0f)); + sponge_Mat4 view = sponge_mat4_identity(); + sponge_Mat4 proj = sponge_mat4_projection(FOV, (float)c.width / (float)c.height, 1.0f, 10.0f); + sponge_draw_mesh_col(c, depths, model, view, proj, positions, colors, triangles, 6); +} diff --git a/platform_win32.c b/platform_win32.c index a8facc4..b8bb4b3 100644 --- a/platform_win32.c +++ b/platform_win32.c @@ -22,16 +22,23 @@ static size_t pixels_buffer_count; static sponge_Texture canvas; +static float *depths; static BITMAPINFO bmi; -int update_canvas(sponge_Texture *canvas, uint32_t new_width, uint32_t new_height) { +int update_canvas(sponge_Texture *canvas, float **depths, uint32_t new_width, uint32_t new_height) { size_t new_count = new_width * new_height; if (new_count > pixels_buffer_count) { sponge_Color32 *new_pixels_buffer = malloc(new_count * sizeof(uint32_t)); - if (!new_pixels_buffer) + float *new_depths = malloc(new_count * sizeof(float)); + if (!new_pixels_buffer || !new_depths) { + free(new_pixels_buffer); + free(new_pixels_buffer); return 0; + } free(canvas->pixels); + free(*depths); canvas->pixels = new_pixels_buffer; + *depths = new_depths; pixels_buffer_count = new_count; } canvas->width = new_width; @@ -73,7 +80,7 @@ LRESULT __stdcall WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) uint32_t width = LOWORD(lParam); uint32_t height = HIWORD(lParam); // TODO(kard): handle properly? (what does that even mean) - assert(update_canvas(&canvas, width, height)); + assert(update_canvas(&canvas, &depths, width, height)); } case WM_MOUSEMOVE: { int32_t x = GET_X_LPARAM(lParam); @@ -118,7 +125,7 @@ int __stdcall WinMain( if (hwnd == NULL) return 1; - update_canvas(&canvas, DEFAULT_WIDTH, DEFAULT_HEIGHT); + update_canvas(&canvas, &depths, DEFAULT_WIDTH, DEFAULT_HEIGHT); ShowWindow(hwnd, nShowCmd); @@ -154,7 +161,7 @@ int __stdcall WinMain( if (!running) break; if (sponge_texture_valid(canvas)) - draw_frame(canvas); + draw_frame_3d(canvas, depths); QueryPerformanceCounter(&qpc_end); qpc_draw.QuadPart = qpc_end.QuadPart - qpc_start.QuadPart; diff --git a/sponge.h b/sponge.h index 5c9e435..7a4c283 100644 --- a/sponge.h +++ b/sponge.h @@ -1,7 +1,7 @@ #include +#include typedef union { - // do not rely on byte order of this field (unless you only support one platform) uint32_t value; struct { uint8_t b; @@ -90,6 +90,7 @@ sponge_Mat4 sponge_mat4_translate(float x, float y, float z); // TODO(kard): quaternion type beat sponge_Mat4 sponge_mat4_rotate(float x, float y, float z); // y -> x -> z order sponge_Mat4 sponge_mat4_scale(float x, float y, float z); +sponge_Mat4 sponge_mat4_projection(float fov_y, float aspect, float near, float far); // right-handed, looking down -z // checks if tex.width or tex.height is equal to 0 @@ -98,6 +99,7 @@ int32_t sponge_texture_valid(sponge_Texture tex); // first argument is always rendering target (canvas) void sponge_clear (sponge_Texture c, sponge_Color32 col); +void sponge_clear_3d (sponge_Texture c, float *depths, sponge_Color32 col); void sponge_draw_rect (sponge_Texture c, sponge_Vec2I v0, sponge_Vec2I v1, sponge_Color32 col); void sponge_draw_line (sponge_Texture c, sponge_Vec2I v0, sponge_Vec2I v1, sponge_Color32 col); @@ -127,6 +129,13 @@ void sponge_draw_triangle_uv( sponge_Vec2I v0, sponge_Vec2I v1, sponge_Vec2I v2, sponge_Vec2 uv0, sponge_Vec2 uv1, sponge_Vec2 uv2, sponge_Texture tex); + +void sponge_draw_mesh_col( + sponge_Texture c, float *depths, + sponge_Mat4 model, sponge_Mat4 view, sponge_Mat4 proj, + sponge_Vec3 *positions, sponge_Color32 *colors, int32_t *triangles, size_t triangles_count); + + #define SPONGE_CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val))) #define SPONGE_MIN(x, y) ((x) < (y) ? (x) : (y)) #define SPONGE_MAX(x, y) ((x) > (y) ? (x) : (y)) @@ -299,6 +308,16 @@ sponge_Mat4 sponge_mat4_scale(float x, float y, float z) { return m; } +sponge_Mat4 sponge_mat4_projection(float fov_y, float aspect, float znear, float zfar) { + float f = 1.0f / tanf(fov_y / 2.0f); + sponge_Mat4 m; + m.c00 = f / aspect; m.c01 = 0.0f; m.c02 = 0.0f; m.c03 = 0.0f; + m.c10 = 0.0f; m.c11 = f; m.c12 = 0.0f; m.c13 = 0.0f; + m.c20 = 0.0f; m.c21 = 0.0f; m.c22 = -zfar / (zfar - znear); m.c23 = (-znear * zfar) / (zfar - znear); + m.c30 = 0.0f; m.c31 = 0.0f; m.c32 = -1.0f; m.c33 = 0.0f; + return m; +} + int32_t sponge_texture_valid(sponge_Texture tex) { return tex.width != 0 && tex.height != 0 && tex.stride_pixels != 0; @@ -314,6 +333,15 @@ void sponge_clear(sponge_Texture c, sponge_Color32 col) { } } } +void sponge_clear_3d(sponge_Texture c, float *depths, sponge_Color32 col) { + sponge_clear(c, col); + float *row = depths; + for (uint32_t y = 0; y < c.height; y++, row += c.stride_pixels) { + for (uint32_t x = 0; x < c.width; x++) { + row[x] = INFINITY; + } + } +} void sponge_draw_rect(sponge_Texture c, sponge_Vec2I v0, sponge_Vec2I v1, sponge_Color32 col) { SPONGE_ASSERT(sponge_texture_valid(c)); @@ -474,4 +502,63 @@ void sponge_draw_triangle_uv( } } + +void sponge_draw_mesh_col( + sponge_Texture c, float *depths, + sponge_Mat4 model, sponge_Mat4 view, sponge_Mat4 proj, + sponge_Vec3 *positions, sponge_Color32 *colors, int32_t *triangles, size_t triangles_count +) { + sponge_Mat4 mvp = sponge_mat4_mul_mat4(sponge_mat4_mul_mat4(model, view), proj); + for (size_t i = 0; i < triangles_count; i += 3) { + int32_t t0 = triangles[i + 0]; + int32_t t1 = triangles[i + 1]; + int32_t t2 = triangles[i + 2]; + + sponge_Vec3 v0 = positions[t0]; + sponge_Vec3 v1 = positions[t1]; + sponge_Vec3 v2 = positions[t2]; + + v0 = sponge_vec3_mul_mat4(v0, mvp); + v1 = sponge_vec3_mul_mat4(v1, mvp); + v2 = sponge_vec3_mul_mat4(v2, mvp); + + // TODO(kard): clipping + + sponge_Vec2I v0i = sponge_vec2i_make((int32_t)((v0.x + 1.0f) * (c.width / 2)), (int32_t)((v0.y + 1.0f) * (c.height / 2))); + sponge_Vec2I v1i = sponge_vec2i_make((int32_t)((v1.x + 1.0f) * (c.width / 2)), (int32_t)((v1.y + 1.0f) * (c.height / 2))); + sponge_Vec2I v2i = sponge_vec2i_make((int32_t)((v2.x + 1.0f) * (c.width / 2)), (int32_t)((v2.y + 1.0f) * (c.height / 2))); + v0i.y = c.height - v0i.y; + v1i.y = c.height - v1i.y; + v2i.y = c.height - v2i.y; + + + sponge_Vec2I min, max; + float area2; + sponge_draw_triangle_init(c, v0i, v1i, v2i, &min, &max, &area2); + sponge_Color32 *row = c.pixels + (min.y * c.stride_pixels); + float *depth_row = depths + (min.y * c.stride_pixels); + + for (int32_t y = min.y; y <= max.y; y++, row += c.stride_pixels, depth_row += c.stride_pixels) { + for (int32_t x = min.x; x <= max.x; x++) { + float w0, w1, w2; + if (sponge_draw_triangle_iter(sponge_vec2i_make(x, y), v0i, v1i, v2i, area2, &w0, &w1, &w2)) { + float depth = (v0.z * w0) + (v1.z * w1) + (v2.z * w2); + if (depth < depth_row[x] && depth <= 1.0f && depth >= 0.0f) { + depth_row[x] = depth; + + sponge_ColorF c0 = sponge_color32_to_colorf(colors[t0]); + sponge_ColorF c1 = sponge_color32_to_colorf(colors[t1]); + sponge_ColorF c2 = sponge_color32_to_colorf(colors[t2]); + sponge_ColorF result = sponge_vec4_make(0.0f, 0.0f, 0.0f, 0.0f); + result = sponge_vec4_add(result, sponge_vec4_mul(c0, w0)); + result = sponge_vec4_add(result, sponge_vec4_mul(c1, w1)); + result = sponge_vec4_add(result, sponge_vec4_mul(c2, w2)); + row[x] = sponge_colorf_to_color32(result); + } + } + } + } + } +} + #endif // SPONGE_IMPLEMENTATION -- cgit v1.3.1