diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-07 19:03:04 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-07 19:03:04 +0800 |
| commit | dfa9a616c3d89a935a4c0bf68f7801581d8b5037 (patch) | |
| tree | dbedd97ded9a227751555744fc246943af6e6c77 /src/konsofren.h | |
| parent | 5eaa99c67ca44bd48d8e2eae7f906c942d565d67 (diff) | |
Move headers to kon/ and add per-lib implementation stubs
Diffstat (limited to 'src/konsofren.h')
| -rw-r--r-- | src/konsofren.h | 1012 |
1 files changed, 0 insertions, 1012 deletions
diff --git a/src/konsofren.h b/src/konsofren.h deleted file mode 100644 index b7ab033..0000000 --- a/src/konsofren.h +++ /dev/null @@ -1,1012 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * konsofren.h - * - * Copyright (c) 2026 KonAki - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef KONSOFREN_H -#define KONSOFREN_H - -#include <stdint.h> -#include <stdlib.h> -#include <math.h> - -#define KON_BACKGROUND_COLOR 0xFF05050A - -typedef struct kon_framebuffer { - int width, height; - uint32_t *data; -} kon_framebuffer_t; - -typedef kon_framebuffer_t kon_image; - -typedef enum kon_imageFormat { - konFormatRGBA8 = 0, - konFormatABGR8, - konFormatARGB8, - konFormatBGRA8 -} kon_imageFormat_t; - -#define KON_PI 3.14159265358979323846f -#define KON_TAU (2.0f * KON_PI) - -#define KON_DEG2RAD(d) ((float)(d) * (KON_PI / 180.0f)) -#define KON_RAD2DEG(r) ((float)(r) * (180.0f / KON_PI)) - -/* kon_vec3_t built from degrees, handy for rotations: KON_DEG3(0, 45, 0) */ -#define KON_DEG3(x, y, z) ((kon_vec3_t){KON_DEG2RAD(x), KON_DEG2RAD(y), KON_DEG2RAD(z)}) - -#define KON_VEC2(x, y) ((kon_vec2_t){(float)(x), (float)(y)}) -#define KON_VEC3(x, y, z) ((kon_vec3_t){(float)(x), (float)(y), (float)(z)}) -#define KON_VEC4(x, y, z, w) ((kon_vec4_t){(float)(x), (float)(y), (float)(z), (float)(w)}) - -#define KON_VEC3_ZERO KON_VEC3(0, 0, 0) -#define KON_VEC3_ONE KON_VEC3(1, 1, 1) -#define KON_VEC3_UP KON_VEC3(0, 1, 0) - -/* right-handed, +Y up, camera looks down -Z, column-major: m[column * 4 + row] */ - -typedef struct kon_vec2 { float x, y; } kon_vec2_t; -typedef struct kon_vec3 { float x, y, z; } kon_vec3_t; -typedef struct kon_vec4 { float x, y, z, w; } kon_vec4_t; -typedef struct kon_mat4 { float m[16]; } kon_mat4_t; - -/* game is the fixed-size framebuffer you draw into, screen always matches the window */ -typedef struct kon_display { - kon_framebuffer_t *game; - kon_framebuffer_t *screen; - int integerOnly; - - /* where the game image sits inside screen, kept up to date by the functions below */ - int viewX, viewY, viewWidth, viewHeight; - int *columnMap; -} kon_display_t; - -typedef enum kon_projection { - KON_PROJECTION_PERSPECTIVE = 0, - KON_PROJECTION_ORTHOGRAPHIC -} kon_projection_t; - -typedef struct kon_camera { - kon_vec3_t position; - kon_vec3_t target; - kon_vec3_t up; - kon_projection_t projection; - float fov; /* vertical field of view in radians, perspective only */ - float orthoSize; /* half the visible height in world units, orthographic only */ - float nearZ, farZ; -} kon_camera_t; - -/* edges holds pairs of indices into vertices */ -typedef struct kon_wireMesh { - const kon_vec3_t *vertices; - const uint16_t *edges; - int edgeCount; -} kon_wireMesh_t; - -/*** 2D declarations ***/ - -/*** framebuffer declarations ***/ - -kon_framebuffer_t *kon_createFramebuffer(int width, int height); -void kon_freeFramebuffer(kon_framebuffer_t *fb); -void kon_putPixel(kon_framebuffer_t *fb, int x, int y, uint32_t color); -void kon_clearFramebuffer(kon_framebuffer_t *fb, uint32_t color); -void kon_resizeFramebuffer(kon_framebuffer_t *fb, int width, int height); -void kon_exportPixels(kon_framebuffer_t *fb, kon_imageFormat_t format, uint8_t *out); - -/*** image declarations ***/ - -kon_image *kon_loadImage(const uint8_t *pixels, int width, int height, kon_imageFormat_t format); -void kon_freeImage(kon_image *image); - -void kon_drawImage(kon_framebuffer_t *fb, int x, int y, int width, int height, kon_image *image); - -/*** draw function declarations ***/ - -void kon_drawRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color); -void kon_fillRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color); -void kon_drawLine(kon_framebuffer_t *fb, int x0, int y0, int x1, int y1, uint32_t color); -void kon_drawCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color); -void kon_fillCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color); - -/*** math declarations ***/ - -/*** scalar declarations ***/ - -float kon_clamp(float v, float min, float max); -float kon_lerp(float a, float b, float t); - -/*** vec2 declarations ***/ - -kon_vec2_t kon_vec2Add(kon_vec2_t a, kon_vec2_t b); -kon_vec2_t kon_vec2Sub(kon_vec2_t a, kon_vec2_t b); -kon_vec2_t kon_vec2Scale(kon_vec2_t a, float s); -float kon_vec2Dot(kon_vec2_t a, kon_vec2_t b); - -/*** vec3 declarations ***/ - -kon_vec3_t kon_vec3Add(kon_vec3_t a, kon_vec3_t b); -kon_vec3_t kon_vec3Sub(kon_vec3_t a, kon_vec3_t b); -kon_vec3_t kon_vec3Scale(kon_vec3_t a, float s); -float kon_vec3Dot(kon_vec3_t a, kon_vec3_t b); -kon_vec3_t kon_vec3Cross(kon_vec3_t a, kon_vec3_t b); -float kon_vec3Length(kon_vec3_t a); -kon_vec3_t kon_vec3Normalize(kon_vec3_t a); -kon_vec3_t kon_vec3Lerp(kon_vec3_t a, kon_vec3_t b, float t); - -/*** mat4 declarations ***/ - -kon_mat4_t kon_mat4Identity(void); -kon_mat4_t kon_mat4Mul(kon_mat4_t a, kon_mat4_t b); -kon_vec4_t kon_mat4MulVec4(kon_mat4_t m, kon_vec4_t v); -kon_vec3_t kon_mat4MulPoint(kon_mat4_t m, kon_vec3_t p); - -/* like blender's local/global: the plain versions work along the model's own axes, - the World versions along the world's axes. neither rotate nor scale ever moves the model's position. - angles are euler angles in radians (x = pitch, y = yaw, z = roll), see KON_DEG3 */ -kon_mat4_t kon_mat4Translate(kon_mat4_t m, kon_vec3_t v); -kon_mat4_t kon_mat4Rotate(kon_mat4_t m, kon_vec3_t angles); -kon_mat4_t kon_mat4Scale(kon_mat4_t m, kon_vec3_t v); -kon_mat4_t kon_mat4TranslateWorld(kon_mat4_t m, kon_vec3_t v); -kon_mat4_t kon_mat4RotateWorld(kon_mat4_t m, kon_vec3_t angles); -kon_mat4_t kon_mat4ScaleWorld(kon_mat4_t m, kon_vec3_t v); - -kon_mat4_t kon_mat4FromTranslation(kon_vec3_t v); -kon_mat4_t kon_mat4FromRotation(kon_vec3_t angles); -kon_mat4_t kon_mat4FromAxisAngle(kon_vec3_t axis, float angle); -kon_mat4_t kon_mat4FromScale(kon_vec3_t v); -kon_mat4_t kon_mat4FromTransform(kon_vec3_t position, kon_vec3_t angles, kon_vec3_t scale); - -kon_mat4_t kon_mat4LookAt(kon_vec3_t eye, kon_vec3_t target, kon_vec3_t up); -kon_mat4_t kon_mat4Perspective(float fovY, float aspect, float nearZ, float farZ); -kon_mat4_t kon_mat4Ortho(float left, float right, float bottom, float top, float nearZ, float farZ); - -/*** display declarations ***/ - - -kon_display_t *kon_createDisplay(int gameWidth, int gameHeight, int windowWidth, int windowHeight); -void kon_freeDisplay(kon_display_t *display); - -void kon_setGameResolution(kon_display_t *display, int gameWidth, int gameHeight); -void kon_setIntegerScaling(kon_display_t *display, int integerOnly); -void kon_resizeDisplay(kon_display_t *display, int windowWidth, int windowHeight); - -/* scales game into screen, then blit display->screen to the window */ -void kon_presentDisplay(kon_display_t *display); - -/* returns 0 if the window position is on a bar, outside the game image */ -int kon_windowToGame(const kon_display_t *display, int windowX, int windowY, int *gameX, int *gameY); - -/*** 3D declarations ***/ - -/*** camera declarations ***/ - -kon_camera_t kon_cameraDefault(void); -kon_mat4_t kon_cameraView(const kon_camera_t *camera); -kon_mat4_t kon_cameraProjection(const kon_camera_t *camera, int width, int height); -kon_mat4_t kon_cameraViewProjection(const kon_camera_t *camera, int width, int height); - -/*** 3D draw declarations ***/ - -/* returns 0 if the point is behind the camera */ -int kon_worldToScreen(const kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t point, kon_vec2_t *out); - -void kon_drawLine3D(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t a, kon_vec3_t b, uint32_t color); -void kon_drawWireMesh(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_mat4_t model, const kon_wireMesh_t *mesh, uint32_t color); - -/*** implementation ***/ - -#ifdef KONSOFREN_IMPLEMENTATION - - -/*** private helper ***/ - -static inline uint32_t kon_blendColor(uint32_t dst, uint32_t src) { - - uint8_t src_a = (uint8_t)((src >> 24) & 0xFF); - uint8_t src_r = (uint8_t)((src >> 16) & 0xFF); - uint8_t src_g = (uint8_t)((src >> 8) & 0xFF); - uint8_t src_b = (uint8_t)((src >> 0) & 0xFF); - - if (src_a == 0xFF) { - return src; - } - - uint8_t dst_a = (uint8_t)((dst >> 24) & 0xFF); - uint8_t dst_r = (uint8_t)((dst >> 16) & 0xFF); - uint8_t dst_g = (uint8_t)((dst >> 8) & 0xFF); - uint8_t dst_b = (uint8_t)((dst >> 0) & 0xFF); - - uint8_t inv_a = (uint8_t)(255 - src_a); - - uint8_t out_r = (uint8_t)((src_r * src_a + dst_r * inv_a) / 255); - uint8_t out_g = (uint8_t)((src_g * src_a + dst_g * inv_a) / 255); - uint8_t out_b = (uint8_t)((src_b * src_a + dst_b * inv_a) / 255); - - return ((uint32_t)dst_a << 24) | ((uint32_t)out_r << 16) | ((uint32_t)out_g << 8) | (uint32_t)out_b; -} - -/* Cohen-Sutherland: clips the line to the framebuffer, returns 0 if nothing is visible */ -static int kon_outCode_(double x, double y, int width, int height) { - int code = 0; - if (x < 0) code |= 1; else if (x > width - 1) code |= 2; - if (y < 0) code |= 4; else if (y > height - 1) code |= 8; - return code; -} - -static int kon_clipLine_(int width, int height, int *x0, int *y0, int *x1, int *y1) { - double ax = *x0, ay = *y0, bx = *x1, by = *y1; - double maxX = width - 1, maxY = height - 1; - int ca = kon_outCode_(ax, ay, width, height); - int cb = kon_outCode_(bx, by, width, height); - - while (ca | cb) { - if (ca & cb) return 0; - - int out = ca ? ca : cb; - double x, y; - - if (out & 8) { - x = ax + (bx - ax) * (maxY - ay) / (by - ay); - y = maxY; - } else if (out & 4) { - x = ax + (bx - ax) * (0 - ay) / (by - ay); - y = 0; - } else if (out & 2) { - y = ay + (by - ay) * (maxX - ax) / (bx - ax); - x = maxX; - } else { - y = ay + (by - ay) * (0 - ax) / (bx - ax); - x = 0; - } - - if (out == ca) { - ax = x; ay = y; - ca = kon_outCode_(ax, ay, width, height); - } else { - bx = x; by = y; - cb = kon_outCode_(bx, by, width, height); - } - } - - *x0 = (int)(ax + 0.5); *y0 = (int)(ay + 0.5); - *x1 = (int)(bx + 0.5); *y1 = (int)(by + 0.5); - return 1; -} - -/*** framebuffer implementation ***/ - -kon_framebuffer_t *kon_createFramebuffer(int width, int height) { - kon_framebuffer_t *fb = malloc(sizeof(kon_framebuffer_t)); - if (!fb) return NULL; - - fb->data = calloc((size_t)width * (size_t)height, sizeof(uint32_t)); - if (!fb->data) { - free(fb); - return NULL; - } - - fb->width = width; - fb->height = height; - return fb; -} - -void kon_freeFramebuffer(kon_framebuffer_t *fb) { - if (!fb) return; - - free(fb->data); - free(fb); -} - -void kon_putPixel(kon_framebuffer_t *fb, int x, int y, uint32_t color) { - if (!fb) return; - - if (x < 0 || x >= fb->width || y < 0 || y >= fb->height) return; - - uint32_t dst = fb->data[y * fb->width + x]; - fb->data[y * fb->width + x] = kon_blendColor(dst, color); -} - -void kon_clearFramebuffer(kon_framebuffer_t *fb, uint32_t color) { - if (!fb) return; - - for (int i = 0; i < fb->width * fb->height; i++) { - /* did it directly to not check overhead because of the if in bounds check */ - fb->data[i] = color; - } -} - -void kon_resizeFramebuffer(kon_framebuffer_t *fb, int width, int height) { - if (!fb) return; - - uint32_t *tmp = realloc(fb->data, (size_t)width * (size_t)height * sizeof(uint32_t)); - if (!tmp) return; - - fb->data = tmp; - fb->width = width; - fb->height = height; - - kon_clearFramebuffer(fb, KON_BACKGROUND_COLOR); -} - -void kon_exportPixels(kon_framebuffer_t *fb, kon_imageFormat_t format, uint8_t *out) { - if (!fb || !out) return; - - int fbSize = fb->width * fb->height; - - for (int i = 0; i < fbSize; i++) { - uint32_t color = fb->data[i]; - - uint8_t a = (color >> 24) & 0xFF; - uint8_t r = (color >> 16) & 0xFF; - uint8_t g = (color >> 8) & 0xFF; - uint8_t b = (color >> 0) & 0xFF; - - switch (format) { - case konFormatRGBA8: - out[i * 4 + 0] = r; - out[i * 4 + 1] = g; - out[i * 4 + 2] = b; - out[i * 4 + 3] = a; - break; - case konFormatABGR8: - out[i * 4 + 0] = a; - out[i * 4 + 1] = b; - out[i * 4 + 2] = g; - out[i * 4 + 3] = r; - break; - case konFormatARGB8: - out[i * 4 + 0] = a; - out[i * 4 + 1] = r; - out[i * 4 + 2] = g; - out[i * 4 + 3] = b; - break; - case konFormatBGRA8: - out[i * 4 + 0] = b; - out[i * 4 + 1] = g; - out[i * 4 + 2] = r; - out[i * 4 + 3] = a; - break; - default: - return; - } - } -} - -/*** draw functions implementation ***/ - -void kon_drawRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color) { - if (!fb) return; - - kon_drawLine(fb, x, y, x + width, y, color); - kon_drawLine(fb, x, y + height, x + width, y + height, color); - kon_drawLine(fb, x, y, x, y + height, color); - kon_drawLine(fb, x + width, y, x + width, y + height, color); -} - -void kon_fillRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color) { - if (!fb) return; - - if (width < 0 || height < 0) return; - - if (x < 0) { - width += x; - x = 0; - } - if (y < 0) { - height += y; - y = 0; - } - - if (x + width > fb->width ) width = fb->width - x; - if (y + height > fb->height) height = fb->height - y; - - for (int offset_y = 0; offset_y < height; offset_y++) { - for (int offset_x = 0; offset_x < width; offset_x++) { - uint32_t dst = fb->data[(offset_y + y) * fb->width + (offset_x + x)]; - fb->data[(y + offset_y) * fb->width + (x + offset_x)] = kon_blendColor(dst, color); - - } - } -} - -void kon_drawLine(kon_framebuffer_t *fb, int x0, int y0, int x1, int y1, uint32_t color) { - if (!fb) return; - - if (!kon_clipLine_(fb->width, fb->height, &x0, &y0, &x1, &y1)) return; - - int dx = abs(x1 - x0); - int dy = abs(y1 - y0); - - int sx = (x0 < x1) ? 1 : -1; - int sy = (y0 < y1) ? 1 : -1; - - int err = dx - dy; - - for (;;) { - kon_putPixel(fb, x0, y0, color); - - if (x0 == x1 && y0 == y1) break; - - int e2 = 2 * err; - - if (e2 > -dy) { - err -= dy; - x0 += sx; - } - - if (e2 < dx) { - err += dx; - y0 += sy; - } - } -} - -void kon_drawCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color) { - if (!fb) return; - if (radius <= 0) return; - - int x = radius; - int y = 0; - int err = 1 - radius; - - while (x >= y) { - kon_putPixel(fb, center_x + x, center_y + y, color); - kon_putPixel(fb, center_x + y, center_y + x, color); - kon_putPixel(fb, center_x - y, center_y + x, color); - kon_putPixel(fb, center_x - x, center_y + y, color); - kon_putPixel(fb, center_x - x, center_y - y, color); - kon_putPixel(fb, center_x - y, center_y - x, color); - kon_putPixel(fb, center_x + y, center_y - x, color); - kon_putPixel(fb, center_x + x, center_y - y, color); - - y += 1; - - if (err < 0) { - err += 2 * y + 1; - } else { - x -= 1; - err += 2 * (y - x) + 1; - } - } -} - -void kon_fillCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color) { - if (!fb) return; - if (radius <= 0) return; - - int x = radius; - int y = 0; - int err = 1 - radius; - - while (x >= y) { - kon_drawLine(fb, center_x - x, center_y + y, center_x + x, center_y + y, color); - kon_drawLine(fb, center_x - x, center_y - y, center_x + x, center_y - y, color); - kon_drawLine(fb, center_x - y, center_y + x, center_x + y, center_y + x, color); - kon_drawLine(fb, center_x - y, center_y - x, center_x + y, center_y - x, color); - - y += 1; - - if (err < 0) { - err += 2 * y + 1; - } else { - x -= 1; - err += 2 * (y - x) + 1; - } - } -} - -/*** image implementation ***/ - -kon_image *kon_loadImage(const uint8_t *pixels, int width, int height, kon_imageFormat_t format) { - kon_image *image = malloc(sizeof(kon_image)); - if (!image) return NULL; - - image->data = malloc((size_t)width * (size_t)height * sizeof(uint32_t)); - if (!image->data) { - free(image); - return NULL; - } - - image->width = width; - image->height = height; - - for (int i = 0; i < width * height; i++) { - uint8_t r, g, b, a; - - switch(format) { - case konFormatRGBA8: - r = pixels[i * 4 + 0]; - g = pixels[i * 4 + 1]; - b = pixels[i * 4 + 2]; - a = pixels[i * 4 + 3]; - break; - case konFormatABGR8: - a = pixels[i * 4 + 0]; - b = pixels[i * 4 + 1]; - g = pixels[i * 4 + 2]; - r = pixels[i * 4 + 3]; - break; - case konFormatARGB8: - a = pixels[i * 4 + 0]; - r = pixels[i * 4 + 1]; - g = pixels[i * 4 + 2]; - b = pixels[i * 4 + 3]; - break; - case konFormatBGRA8: - b = pixels[i * 4 + 0]; - g = pixels[i * 4 + 1]; - r = pixels[i * 4 + 2]; - a = pixels[i * 4 + 3]; - break; - default: - free(image->data); - free(image); - return NULL; - } - - image->data[i] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b; - } - - return image; -} -void kon_freeImage(kon_image *image) { - kon_freeFramebuffer(image); -} - -void kon_drawImage(kon_framebuffer_t *fb, int x, int y, int width, int height, kon_image *image) { - if (!fb || !image) return; - - /* just trying to not draw when it's unnecessary */ - if (image->width <= 0 || image->height <= 0) return; - if (width <= 0 || height <= 0) return; - if (x >= fb->width || y >= fb->height) return; - - /* using this loop to make sure we only the parts of the image that are on screen */ - for (int iy = 0; iy < height; iy++) { - for (int ix = 0; ix < width; ix++) { - int src_x = ix * image->width / width; - int src_y = iy * image->height / height; - - uint32_t color = image->data[src_y * image->width + src_x]; - kon_putPixel(fb, x+ix, y+iy, color); - } - } -} - -/*** math implementation ***/ - - -/*** scalar ***/ - -float kon_clamp(float v, float min, float max) { return v < min ? min : (v > max ? max : v); } -float kon_lerp(float a, float b, float t) { return a + (b - a) * t; } - -/*** vec2 ***/ - -kon_vec2_t kon_vec2Add(kon_vec2_t a, kon_vec2_t b) { return (kon_vec2_t){a.x + b.x, a.y + b.y}; } -kon_vec2_t kon_vec2Sub(kon_vec2_t a, kon_vec2_t b) { return (kon_vec2_t){a.x - b.x, a.y - b.y}; } -kon_vec2_t kon_vec2Scale(kon_vec2_t a, float s) { return (kon_vec2_t){a.x * s, a.y * s}; } -float kon_vec2Dot(kon_vec2_t a, kon_vec2_t b) { return a.x * b.x + a.y * b.y; } - -/*** vec3 ***/ - -kon_vec3_t kon_vec3Add(kon_vec3_t a, kon_vec3_t b) { return (kon_vec3_t){a.x + b.x, a.y + b.y, a.z + b.z}; } -kon_vec3_t kon_vec3Sub(kon_vec3_t a, kon_vec3_t b) { return (kon_vec3_t){a.x - b.x, a.y - b.y, a.z - b.z}; } -kon_vec3_t kon_vec3Scale(kon_vec3_t a, float s) { return (kon_vec3_t){a.x * s, a.y * s, a.z * s}; } -float kon_vec3Dot(kon_vec3_t a, kon_vec3_t b) { return a.x * b.x + a.y * b.y + a.z * b.z; } - -kon_vec3_t kon_vec3Cross(kon_vec3_t a, kon_vec3_t b) { - return (kon_vec3_t){ - a.y * b.z - a.z * b.y, - a.z * b.x - a.x * b.z, - a.x * b.y - a.y * b.x - }; -} - -float kon_vec3Length(kon_vec3_t a) { return sqrtf(kon_vec3Dot(a, a)); } - -kon_vec3_t kon_vec3Normalize(kon_vec3_t a) { - float len = kon_vec3Length(a); - return len > 0.0f ? kon_vec3Scale(a, 1.0f / len) : a; -} - -kon_vec3_t kon_vec3Lerp(kon_vec3_t a, kon_vec3_t b, float t) { - return (kon_vec3_t){kon_lerp(a.x, b.x, t), kon_lerp(a.y, b.y, t), kon_lerp(a.z, b.z, t)}; -} - -/*** mat4 ***/ - -kon_mat4_t kon_mat4Identity(void) { - kon_mat4_t r = {{0}}; - r.m[0] = r.m[5] = r.m[10] = r.m[15] = 1.0f; - return r; -} - -/* returns a * b, b is applied first */ -kon_mat4_t kon_mat4Mul(kon_mat4_t a, kon_mat4_t b) { - kon_mat4_t r; - for (int c = 0; c < 4; c++) { - for (int row = 0; row < 4; row++) { - float sum = 0.0f; - for (int k = 0; k < 4; k++) { - sum += a.m[k * 4 + row] * b.m[c * 4 + k]; - } - r.m[c * 4 + row] = sum; - } - } - return r; -} - -kon_vec4_t kon_mat4MulVec4(kon_mat4_t m, kon_vec4_t v) { - return (kon_vec4_t){ - m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12] * v.w, - m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13] * v.w, - m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14] * v.w, - m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15] * v.w - }; -} - -kon_vec3_t kon_mat4MulPoint(kon_mat4_t m, kon_vec3_t p) { - kon_vec4_t r = kon_mat4MulVec4(m, (kon_vec4_t){p.x, p.y, p.z, 1.0f}); - return (kon_vec3_t){r.x, r.y, r.z}; -} - -kon_mat4_t kon_mat4FromTranslation(kon_vec3_t v) { - kon_mat4_t r = kon_mat4Identity(); - r.m[12] = v.x; - r.m[13] = v.y; - r.m[14] = v.z; - return r; -} - -kon_mat4_t kon_mat4FromScale(kon_vec3_t v) { - kon_mat4_t r = kon_mat4Identity(); - r.m[0] = v.x; - r.m[5] = v.y; - r.m[10] = v.z; - return r; -} - -kon_mat4_t kon_mat4FromAxisAngle(kon_vec3_t axis, float angle) { - kon_vec3_t a = kon_vec3Normalize(axis); - float c = cosf(angle), s = sinf(angle), t = 1.0f - c; - kon_mat4_t r = kon_mat4Identity(); - r.m[0] = t * a.x * a.x + c; - r.m[1] = t * a.x * a.y + s * a.z; - r.m[2] = t * a.x * a.z - s * a.y; - r.m[4] = t * a.x * a.y - s * a.z; - r.m[5] = t * a.y * a.y + c; - r.m[6] = t * a.y * a.z + s * a.x; - r.m[8] = t * a.x * a.z + s * a.y; - r.m[9] = t * a.y * a.z - s * a.x; - r.m[10] = t * a.z * a.z + c; - return r; -} - -/* applied in order roll (z), pitch (x), yaw (y) */ -kon_mat4_t kon_mat4FromRotation(kon_vec3_t angles) { - kon_mat4_t x = kon_mat4FromAxisAngle(KON_VEC3(1, 0, 0), angles.x); - kon_mat4_t y = kon_mat4FromAxisAngle(KON_VEC3(0, 1, 0), angles.y); - kon_mat4_t z = kon_mat4FromAxisAngle(KON_VEC3(0, 0, 1), angles.z); - return kon_mat4Mul(y, kon_mat4Mul(x, z)); -} - -kon_mat4_t kon_mat4FromTransform(kon_vec3_t position, kon_vec3_t angles, kon_vec3_t scale) { - kon_mat4_t r = kon_mat4Mul(kon_mat4FromRotation(angles), kon_mat4FromScale(scale)); - return kon_mat4Mul(kon_mat4FromTranslation(position), r); -} - -kon_mat4_t kon_mat4Translate(kon_mat4_t m, kon_vec3_t v) { return kon_mat4Mul(m, kon_mat4FromTranslation(v)); } -kon_mat4_t kon_mat4Rotate(kon_mat4_t m, kon_vec3_t angles) { return kon_mat4Mul(m, kon_mat4FromRotation(angles)); } -kon_mat4_t kon_mat4Scale(kon_mat4_t m, kon_vec3_t v) { return kon_mat4Mul(m, kon_mat4FromScale(v)); } -kon_mat4_t kon_mat4TranslateWorld(kon_mat4_t m, kon_vec3_t v) { return kon_mat4Mul(kon_mat4FromTranslation(v), m); } - -/* pre-multiplying would also swing the position around the world origin, so put it back */ -kon_mat4_t kon_mat4RotateWorld(kon_mat4_t m, kon_vec3_t angles) { - kon_mat4_t r = kon_mat4Mul(kon_mat4FromRotation(angles), m); - r.m[12] = m.m[12]; - r.m[13] = m.m[13]; - r.m[14] = m.m[14]; - return r; -} - -kon_mat4_t kon_mat4ScaleWorld(kon_mat4_t m, kon_vec3_t v) { - kon_mat4_t r = kon_mat4Mul(kon_mat4FromScale(v), m); - r.m[12] = m.m[12]; - r.m[13] = m.m[13]; - r.m[14] = m.m[14]; - return r; -} - -kon_mat4_t kon_mat4LookAt(kon_vec3_t eye, kon_vec3_t target, kon_vec3_t up) { - kon_vec3_t f = kon_vec3Normalize(kon_vec3Sub(target, eye)); - kon_vec3_t s = kon_vec3Normalize(kon_vec3Cross(f, up)); - kon_vec3_t u = kon_vec3Cross(s, f); - - kon_mat4_t r = kon_mat4Identity(); - r.m[0] = s.x; r.m[4] = s.y; r.m[8] = s.z; r.m[12] = -kon_vec3Dot(s, eye); - r.m[1] = u.x; r.m[5] = u.y; r.m[9] = u.z; r.m[13] = -kon_vec3Dot(u, eye); - r.m[2] = -f.x; r.m[6] = -f.y; r.m[10] = -f.z; r.m[14] = kon_vec3Dot(f, eye); - return r; -} - -/* fovY in radians, aspect = width / height */ -kon_mat4_t kon_mat4Perspective(float fovY, float aspect, float nearZ, float farZ) { - kon_mat4_t r = {{0}}; - float f = 1.0f / tanf(fovY * 0.5f); - r.m[0] = f / aspect; - r.m[5] = f; - r.m[10] = (farZ + nearZ) / (nearZ - farZ); - r.m[11] = -1.0f; - r.m[14] = (2.0f * farZ * nearZ) / (nearZ - farZ); - return r; -} - -kon_mat4_t kon_mat4Ortho(float left, float right, float bottom, float top, float nearZ, float farZ) { - kon_mat4_t r = kon_mat4Identity(); - r.m[0] = 2.0f / (right - left); - r.m[5] = 2.0f / (top - bottom); - r.m[10] = -2.0f / (farZ - nearZ); - r.m[12] = -(right + left) / (right - left); - r.m[13] = -(top + bottom) / (top - bottom); - r.m[14] = -(farZ + nearZ) / (farZ - nearZ); - return r; -} - - -/*** display implementation ***/ - -static void kon_updateView_(kon_display_t *display) { - int gameWidth = display->game->width, gameHeight = display->game->height; - int windowWidth = display->screen->width, windowHeight = display->screen->height; - - int scaleX = windowWidth / gameWidth; - int scaleY = windowHeight / gameHeight; - int scale = (scaleX < scaleY) ? scaleX : scaleY; - - if (display->integerOnly && scale >= 1) { - display->viewWidth = gameWidth * scale; - display->viewHeight = gameHeight * scale; - } else if ((int64_t)windowWidth * gameHeight <= (int64_t)windowHeight * gameWidth) { - /* window is narrower than the game: bars on top and bottom */ - display->viewWidth = windowWidth; - display->viewHeight = (int)((int64_t)windowWidth * gameHeight / gameWidth); - } else { - /* window is wider than the game: bars on the sides */ - display->viewHeight = windowHeight; - display->viewWidth = (int)((int64_t)windowHeight * gameWidth / gameHeight); - } - - display->viewX = (windowWidth - display->viewWidth) / 2; - display->viewY = (windowHeight - display->viewHeight) / 2; - - /* which game column each view column shows, worked out once instead of for every pixel */ - int *map = realloc(display->columnMap, (size_t)windowWidth * sizeof(int)); - if (!map) { - display->viewWidth = 0; - return; - } - display->columnMap = map; - - for (int x = 0; x < display->viewWidth; x++) { - map[x] = (int)((int64_t)x * gameWidth / display->viewWidth); - } -} - -kon_display_t *kon_createDisplay(int gameWidth, int gameHeight, int windowWidth, int windowHeight) { - if (gameWidth <= 0 || gameHeight <= 0 || windowWidth <= 0 || windowHeight <= 0) return NULL; - - kon_display_t *display = calloc(1, sizeof(kon_display_t)); - if (!display) return NULL; - - display->game = kon_createFramebuffer(gameWidth, gameHeight); - display->screen = kon_createFramebuffer(windowWidth, windowHeight); - if (!display->game || !display->screen) { - kon_freeDisplay(display); - return NULL; - } - - kon_clearFramebuffer(display->game, KON_BACKGROUND_COLOR); - kon_updateView_(display); - return display; -} - -void kon_freeDisplay(kon_display_t *display) { - if (!display) return; - - kon_freeFramebuffer(display->game); - kon_freeFramebuffer(display->screen); - free(display->columnMap); - free(display); -} - -void kon_setGameResolution(kon_display_t *display, int gameWidth, int gameHeight) { - if (!display || gameWidth <= 0 || gameHeight <= 0) return; - - kon_resizeFramebuffer(display->game, gameWidth, gameHeight); - kon_updateView_(display); -} - -void kon_setIntegerScaling(kon_display_t *display, int integerOnly) { - if (!display) return; - - display->integerOnly = integerOnly; - kon_updateView_(display); -} - -void kon_resizeDisplay(kon_display_t *display, int windowWidth, int windowHeight) { - if (!display || windowWidth <= 0 || windowHeight <= 0) return; - - kon_resizeFramebuffer(display->screen, windowWidth, windowHeight); - kon_updateView_(display); -} - -void kon_presentDisplay(kon_display_t *display) { - if (!display) return; - - const uint32_t barColor = 0xFF000000; - kon_framebuffer_t *game = display->game; - kon_framebuffer_t *screen = display->screen; - int rightX = display->viewX + display->viewWidth; - - for (int y = 0; y < screen->height; y++) { - uint32_t *row = &screen->data[(size_t)y * (size_t)screen->width]; - - if (display->viewWidth <= 0 || y < display->viewY || y >= display->viewY + display->viewHeight) { - for (int x = 0; x < screen->width; x++) row[x] = barColor; - continue; - } - - int srcY = (int)((int64_t)(y - display->viewY) * game->height / display->viewHeight); - const uint32_t *srcRow = &game->data[(size_t)srcY * (size_t)game->width]; - - for (int x = 0; x < display->viewX; x++) row[x] = barColor; - for (int x = 0; x < display->viewWidth; x++) row[display->viewX + x] = srcRow[display->columnMap[x]]; - for (int x = rightX; x < screen->width; x++) row[x] = barColor; - } -} - -int kon_windowToGame(const kon_display_t *display, int windowX, int windowY, int *gameX, int *gameY) { - if (!display || display->viewWidth <= 0 || display->viewHeight <= 0) return 0; - - int localX = windowX - display->viewX; - int localY = windowY - display->viewY; - if (localX < 0 || localX >= display->viewWidth || localY < 0 || localY >= display->viewHeight) return 0; - - if (gameX) *gameX = (int)((int64_t)localX * display->game->width / display->viewWidth); - if (gameY) *gameY = (int)((int64_t)localY * display->game->height / display->viewHeight); - return 1; -} - -/*** 3D implementation ***/ - - -/*** private helper ***/ - -static kon_vec2_t kon_clipToScreen_(const kon_framebuffer_t *fb, kon_vec4_t clip) { - /* clamped so the later float to int conversion can't overflow */ - float ndcX = kon_clamp(clip.x / clip.w, -1.0e5f, 1.0e5f); - float ndcY = kon_clamp(clip.y / clip.w, -1.0e5f, 1.0e5f); - - /* screen +Y points down, NDC +Y points up */ - return KON_VEC2((ndcX + 1.0f) * 0.5f * (float)fb->width, (1.0f - ndcY) * 0.5f * (float)fb->height); -} - -/*** camera implementation ***/ - -kon_camera_t kon_cameraDefault(void) { - kon_camera_t camera; - camera.position = KON_VEC3(0, 0, 5); - camera.target = KON_VEC3_ZERO; - camera.up = KON_VEC3_UP; - camera.projection = KON_PROJECTION_PERSPECTIVE; - camera.fov = KON_DEG2RAD(60); - camera.orthoSize = 5.0f; - camera.nearZ = 0.1f; - camera.farZ = 100.0f; - return camera; -} - -kon_mat4_t kon_cameraView(const kon_camera_t *camera) { - return kon_mat4LookAt(camera->position, camera->target, camera->up); -} - -kon_mat4_t kon_cameraProjection(const kon_camera_t *camera, int width, int height) { - float aspect = (height > 0) ? (float)width / (float)height : 1.0f; - - if (camera->projection == KON_PROJECTION_ORTHOGRAPHIC) { - float halfH = camera->orthoSize; - float halfW = halfH * aspect; - return kon_mat4Ortho(-halfW, halfW, -halfH, halfH, camera->nearZ, camera->farZ); - } - - return kon_mat4Perspective(camera->fov, aspect, camera->nearZ, camera->farZ); -} - -kon_mat4_t kon_cameraViewProjection(const kon_camera_t *camera, int width, int height) { - return kon_mat4Mul(kon_cameraProjection(camera, width, height), kon_cameraView(camera)); -} - -/*** 3D draw implementation ***/ - -int kon_worldToScreen(const kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t point, kon_vec2_t *out) { - if (!fb || !out) return 0; - - kon_vec4_t clip = kon_mat4MulVec4(viewProjection, KON_VEC4(point.x, point.y, point.z, 1)); - - /* in front of the near plane when z >= -w */ - if (clip.z < -clip.w || clip.w <= 0.0f) return 0; - - *out = kon_clipToScreen_(fb, clip); - return 1; -} - -void kon_drawLine3D(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t a, kon_vec3_t b, uint32_t color) { - if (!fb) return; - - kon_vec4_t ca = kon_mat4MulVec4(viewProjection, KON_VEC4(a.x, a.y, a.z, 1)); - kon_vec4_t cb = kon_mat4MulVec4(viewProjection, KON_VEC4(b.x, b.y, b.z, 1)); - - /* clip against the near plane (z + w >= 0) before dividing, so points behind the camera never get projected */ - float da = ca.z + ca.w; - float db = cb.z + cb.w; - - if (da < 0.0f && db < 0.0f) return; - - if (da < 0.0f || db < 0.0f) { - float t = da / (da - db); - kon_vec4_t hit = KON_VEC4( - ca.x + (cb.x - ca.x) * t, - ca.y + (cb.y - ca.y) * t, - ca.z + (cb.z - ca.z) * t, - ca.w + (cb.w - ca.w) * t - ); - - if (da < 0.0f) ca = hit; - else cb = hit; - } - - if (ca.w <= 0.0f || cb.w <= 0.0f) return; - - kon_vec2_t sa = kon_clipToScreen_(fb, ca); - kon_vec2_t sb = kon_clipToScreen_(fb, cb); - - kon_drawLine(fb, (int)sa.x, (int)sa.y, (int)sb.x, (int)sb.y, color); -} - -void kon_drawWireMesh(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_mat4_t model, const kon_wireMesh_t *mesh, uint32_t color) { - if (!fb || !mesh) return; - - kon_mat4_t mvp = kon_mat4Mul(viewProjection, model); - - for (int i = 0; i < mesh->edgeCount; i++) { - kon_vec3_t a = mesh->vertices[mesh->edges[i * 2 + 0]]; - kon_vec3_t b = mesh->vertices[mesh->edges[i * 2 + 1]]; - kon_drawLine3D(fb, mvp, a, b, color); - } -} - -#endif /* end of KONSOFREN_IMPLEMENTATION */ - -#endif |
