summaryrefslogtreecommitdiff
path: root/gastro/src
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2026-07-09 23:51:55 -0400
committerLLLL Colonq <llll@colonq>2026-07-09 23:51:55 -0400
commit2bdcaf319b1d74ffbaccf08a58336f804761beab (patch)
treec21de6df74ec79b5574ad2b89fcc275d10847802 /gastro/src
Refactor into monorepo
Diffstat (limited to 'gastro/src')
-rw-r--r--gastro/src/gastro.c311
-rw-r--r--gastro/src/main.c242
-rw-r--r--gastro/src/trig.c268
3 files changed, 821 insertions, 0 deletions
diff --git a/gastro/src/gastro.c b/gastro/src/gastro.c
new file mode 100644
index 0000000..2b175fe
--- /dev/null
+++ b/gastro/src/gastro.c
@@ -0,0 +1,311 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <lcq/gastro.h>
+
+#define MAX_ATTRS 16
+
+static i64 min2(i64 x, i64 y) { return x < y ? x : y; }
+static i64 min3(i64 x, i64 y, i64 z) { return min2(min2(x, y), z); }
+static i64 max2(i64 x, i64 y) { return x > y ? x : y; }
+static i64 max3(i64 x, i64 y, i64 z) { return max2(max2(x, y), z); }
+
+gastro_fix gastro_fix_new(i64 x) {
+ assert(x < ((i64) 1 << (sizeof(gastro_fix) * 8 - GASTRO_FIXED_POINT)));
+ return x << GASTRO_FIXED_POINT;
+}
+i64 gastro_fix_to_i64(gastro_fix x) {
+ return x >> GASTRO_FIXED_POINT;
+}
+gastro_fix gastro_fix_from_double(double x) {
+ double shift = (double) (1 << GASTRO_FIXED_POINT);
+ return (gastro_fix) (x * shift);
+}
+double gastro_fix_to_double(gastro_fix x) {
+ double shift = (double) (1 << GASTRO_FIXED_POINT);
+ return (double) x / shift;
+}
+gastro_fix gastro_fix_mul(gastro_fix x, gastro_fix y) {
+ return (gastro_fix) (x * y) >> GASTRO_FIXED_POINT;
+}
+gastro_fix gastro_fix_div(gastro_fix x, gastro_fix y) {
+ return (x * (1 << GASTRO_FIXED_POINT)) / y;
+}
+gastro_fix gastro_fix_sqrt(gastro_fix s) {
+ gastro_fix x = GASTRO_FIX_ONE;
+ i64 i;
+ for (i = 0; i < 5; ++i) {
+ x = gastro_fix_mul(GASTRO_FIX_HALF, x + gastro_fix_div(s, x));
+ }
+ return x;
+}
+
+gastro_vec2 gastro_vec2_new(gastro_fix x, gastro_fix y) {
+ gastro_vec2 ret;
+ ret.x = x; ret.y = y;
+ return ret;
+}
+gastro_vec2 gastro_vec2_add(gastro_vec2 x, gastro_vec2 y) {
+ return gastro_vec2_new(x.x + y.x, x.y + y.y);
+}
+gastro_vec2 gastro_vec2_sub(gastro_vec2 x, gastro_vec2 y) {
+ return gastro_vec2_new(x.x - y.x, x.y - y.y);
+}
+gastro_fix gastro_vec2_length(gastro_vec2 v) {
+ gastro_fix sq = gastro_fix_mul(v.x, v.x) + gastro_fix_mul(v.y, v.y);
+ return gastro_fix_sqrt((gastro_fix) sq);
+}
+gastro_fix gastro_vec2_cross(gastro_vec2 v, gastro_vec2 u) {
+ return gastro_fix_mul(v.x, u.y) - gastro_fix_mul(u.x, v.y);
+}
+gastro_vec3 gastro_vec3_new(gastro_fix x, gastro_fix y, gastro_fix z) {
+ gastro_vec3 ret;
+ ret.x = x; ret.y = y; ret.z = z;
+ return ret;
+}
+void gastro_vec3_print(gastro_vec3 v) {
+ printf("<%f, %f, %f>\n", gastro_fix_to_double(v.x), gastro_fix_to_double(v.y), gastro_fix_to_double(v.z));
+}
+gastro_vec2 gastro_vec3_xy(gastro_vec3 v) { return gastro_vec2_new(v.x, v.y); }
+gastro_vec3 gastro_vec3_perspective(gastro_fix nearz, gastro_vec3 p) {
+ gastro_vec3 ret;
+ ret.x = gastro_fix_mul(gastro_fix_div(nearz, p.z), p.x);
+ ret.y = gastro_fix_mul(gastro_fix_div(nearz, p.z), p.y);
+ ret.z = p.z;
+ return ret;
+}
+gastro_fix gastro_vec3_bary_interpolate(gastro_vec3 bary, gastro_fix x, gastro_fix y, gastro_fix z) {
+ return gastro_fix_mul(bary.x, x) + gastro_fix_mul(bary.y, y) + gastro_fix_mul(bary.z, z);
+}
+gastro_fix gastro_vec3_bary_interpolate_inv(gastro_vec3 bary, gastro_fix x, gastro_fix y, gastro_fix z) {
+ return gastro_fix_div(GASTRO_FIX_ONE,
+ gastro_vec3_bary_interpolate(bary,
+ gastro_fix_div(GASTRO_FIX_ONE, x),
+ gastro_fix_div(GASTRO_FIX_ONE, y),
+ gastro_fix_div(GASTRO_FIX_ONE, z)
+ )
+ );
+}
+
+gastro_vec4 gastro_vec4_new(gastro_fix x, gastro_fix y, gastro_fix z, gastro_fix w) {
+ gastro_vec4 ret;
+ ret.x = x; ret.y = y; ret.z = z; ret.w = w;
+ return ret;
+}
+void gastro_vec4_print(gastro_vec4 v) {
+ printf("<%f, %f, %f, %f>\n", gastro_fix_to_double(v.x), gastro_fix_to_double(v.y), gastro_fix_to_double(v.z), gastro_fix_to_double(v.w));
+}
+gastro_vec3 gastro_vec4_xyz(gastro_vec4 v) { return gastro_vec3_new(v.x, v.y, v.z); }
+gastro_fix gastro_vec4_idx(gastro_vec4 v, i64 idx) {
+ assert(idx >= 0 && idx < 4);
+ switch (idx) {
+ case 0: return v.x;
+ case 1: return v.y;
+ case 2: return v.z;
+ case 3: return v.w;
+ default: return 0;
+ }
+}
+gastro_fix gastro_vec4_dot(gastro_vec4 a, gastro_vec4 b) {
+ return gastro_fix_mul(a.x, b.x)
+ + gastro_fix_mul(a.y, b.y)
+ + gastro_fix_mul(a.z, b.z)
+ + gastro_fix_mul(a.w, b.w);
+}
+
+gastro_mat4x4 gastro_mat4x4_new(
+ gastro_fix e0, gastro_fix e1, gastro_fix e2, gastro_fix e3,
+ gastro_fix e4, gastro_fix e5, gastro_fix e6, gastro_fix e7,
+ gastro_fix e8, gastro_fix e9, gastro_fix e10, gastro_fix e11,
+ gastro_fix e12, gastro_fix e13, gastro_fix e14, gastro_fix e15) {
+ gastro_mat4x4 ret;
+ ret.rows[0] = gastro_vec4_new( e0, e1, e2, e3);
+ ret.rows[1] = gastro_vec4_new( e4, e5, e6, e7);
+ ret.rows[2] = gastro_vec4_new( e8, e9, e10, e11);
+ ret.rows[3] = gastro_vec4_new(e12, e13, e14, e15);
+ return ret;
+}
+void gastro_mat4x4_print(gastro_mat4x4 m) {
+ i64 row;
+ for (row = 0; row < 4; ++row) {
+ printf("%c %14f, %14f, %14f, %14f, %c\n",
+ row == 0 ? '[' : ' ',
+ gastro_fix_to_double(m.rows[row].x),
+ gastro_fix_to_double(m.rows[row].y),
+ gastro_fix_to_double(m.rows[row].z),
+ gastro_fix_to_double(m.rows[row].w),
+ row == 3 ? ']' : ' ');
+ }
+}
+gastro_vec4 gastro_mat4x4_mul_vec4(gastro_mat4x4 m, gastro_vec4 v) {
+ return gastro_vec4_new(
+ gastro_vec4_dot(m.rows[0], v),
+ gastro_vec4_dot(m.rows[1], v),
+ gastro_vec4_dot(m.rows[2], v),
+ gastro_vec4_dot(m.rows[3], v));
+}
+gastro_mat4x4 gastro_mat4x4_mul_mat4x4(gastro_mat4x4 n, gastro_mat4x4 m) {
+ gastro_mat4x4 ret;
+ i64 row, col;
+ for (row = 0; row < 4; ++row) {
+ gastro_fix comps[4];
+ for (col = 0; col < 4; ++col) {
+ comps[col] = gastro_vec4_dot(
+ gastro_vec4_new(
+ gastro_vec4_idx(m.rows[0], col),
+ gastro_vec4_idx(m.rows[1], col),
+ gastro_vec4_idx(m.rows[2], col),
+ gastro_vec4_idx(m.rows[3], col)),
+ n.rows[row]);
+ }
+ ret.rows[row] = gastro_vec4_new(comps[0], comps[1], comps[2], comps[3]);
+ }
+ return ret;
+}
+
+gastro_color gastro_color_new(u8 r, u8 g, u8 b, u8 a) {
+ gastro_color ret;
+ ret.r = r; ret.g = g; ret.b = b; ret.a = a;
+ return ret;
+}
+
+void gastro_ctx_init(gastro_ctx *ret, u32 width, u32 height, gastro_color *pixels, gastro_fix *depth) {
+ ret->width = width;
+ ret->height = height;
+ ret->pixels = pixels;
+ ret->depth = depth;
+}
+
+gastro_vec3 gastro_vec3_denormalize(gastro_ctx *ctx, gastro_vec3 p) {
+ gastro_vec3 ret;
+ ret.x = gastro_fix_mul(p.x + GASTRO_FIX_ONE, gastro_fix_new(ctx->width / 2));
+ ret.y = gastro_fix_mul(p.y + GASTRO_FIX_ONE, gastro_fix_new(ctx->height / 2));
+ ret.z = p.z;
+ return ret;
+}
+
+void gastro_draw_pixel(gastro_ctx *ctx, gastro_color col, gastro_fix z, i64 x, i64 y) {
+ i64 idx = y * ctx->width + x;
+ if (x < 0 || x >= ctx->width || y < 0 || y >= ctx->height) return;
+ if (z > ctx->depth[idx]) return;
+ ctx->pixels[idx] = col;
+ ctx->depth[idx] = z;
+}
+
+void gastro_draw_clear(gastro_ctx *ctx, gastro_color col) {
+ i64 x, y;
+ for (x = 0; x < ctx->width; ++x) {
+ for (y = 0; y < ctx->height; ++y) {
+ i64 idx = y * ctx->width + x;
+ ctx->pixels[idx] = col;
+ ctx->depth[idx] = INT64_MAX;
+ }
+ }
+}
+
+gastro_vertex gastro_program_vertex(gastro_ctx *ctx, gastro_program *p, gastro_vec3 v, gastro_fix *attrs, i64 attrs_len) {
+ gastro_vertex ret;
+ gastro_vec4 res = p->vertex(ctx, v, attrs, attrs_len);
+ res.x = gastro_fix_div(res.x, res.w);
+ res.y = gastro_fix_div(res.y, res.w);
+ /* res.z = gastro_fix_div(res.z, res.w); */
+ ret.v = gastro_vec4_xyz(res);
+ ret.attrs = attrs;
+ return ret;
+}
+gastro_color gastro_program_fragment(gastro_ctx *ctx, gastro_program *p, gastro_vec3 v, gastro_fix *attrs, i64 attrs_len) {
+ gastro_color ret;
+ ret = p->fragment(ctx, v, attrs, attrs_len);
+ return ret;
+}
+
+/* return negative if x is to the right of the line from p0 to p1 */
+static gastro_fix compare_edge(gastro_vec2 x, gastro_vec2 p0, gastro_vec2 p1) {
+ gastro_vec2 pd = gastro_vec2_sub(p1, p0);
+ gastro_vec2 xd = gastro_vec2_sub(x, p0);
+ gastro_fix cross = gastro_vec2_cross(xd, pd);
+ return cross;
+}
+static bool triangle_contains(gastro_vec3 *bary, gastro_vec2 x, gastro_vec2 p0, gastro_vec2 p1, gastro_vec2 p2) {
+ gastro_fix p0p1 = compare_edge(x, p0, p1);
+ gastro_fix p1p2 = compare_edge(x, p1, p2);
+ gastro_fix p2p0 = compare_edge(x, p2, p0);
+ gastro_fix area = compare_edge(p2, p0, p1);
+ if (area == 0) return false;
+ bary->x = gastro_fix_div(p1p2, area);
+ bary->y = gastro_fix_div(p2p0, area);
+ bary->z = gastro_fix_div(p0p1, area);
+ return p0p1 <= 0 && p1p2 <= 0 && p2p0 <= 0;
+}
+void gastro_render_triangle(gastro_ctx *ctx, gastro_program *p, gastro_vertex n0, gastro_vertex n1, gastro_vertex n2, i64 attrs_len) {
+ /* assume clockwise winding order of points */
+ /* p0 -> p1, p1 -> p2, p2 -> p0 */
+ i64 x, y, minx, miny, maxx, maxy;
+ gastro_fix attrs[MAX_ATTRS];
+ /* convert all points from -1 to 1 range (normalized) into pixels */
+ gastro_vec3 p0 = gastro_vec3_denormalize(ctx, n0.v); gastro_vec2 v0 = gastro_vec3_xy(p0);
+ gastro_vec3 p1 = gastro_vec3_denormalize(ctx, n1.v); gastro_vec2 v1 = gastro_vec3_xy(p1);
+ gastro_vec3 p2 = gastro_vec3_denormalize(ctx, n2.v); gastro_vec2 v2 = gastro_vec3_xy(p2);
+ /* compute a bounding box for the triangle - we know only these pixels could possible be drawn */
+ minx = min3(gastro_fix_to_i64(p0.x), gastro_fix_to_i64(p1.x), gastro_fix_to_i64(p2.x));
+ maxx = max3(gastro_fix_to_i64(p0.x), gastro_fix_to_i64(p1.x), gastro_fix_to_i64(p2.x));
+ miny = min3(gastro_fix_to_i64(p0.y), gastro_fix_to_i64(p1.y), gastro_fix_to_i64(p2.y));
+ maxy = max3(gastro_fix_to_i64(p0.y), gastro_fix_to_i64(p1.y), gastro_fix_to_i64(p2.y));
+ for (x = minx; x <= maxx; ++x) { /* for each pixel in the bounding box: */
+ for (y = miny; y <= maxy; ++y) {
+ i64 idx = y * ctx->width + x;
+ /* consider the center of the pixel, not the top left corner */
+ gastro_vec2 centered = gastro_vec2_new(
+ gastro_fix_new(x) + GASTRO_FIX_HALF,
+ gastro_fix_new(y) + GASTRO_FIX_HALF
+ );
+ gastro_vec3 bary;
+ /* if the center of the pixel is in the triangle */
+ if (triangle_contains(&bary, centered, v0, v1, v2)) {
+ i64 a;
+ gastro_color col;
+ /* find the depth of this pixel by using the barycentric coordinates */
+ /* we must interpolate the reciprocals to be perspective-correct, since p0 etc. are already projected */
+ /* i don't understand why this works very well yet */
+ gastro_fix z = gastro_vec3_bary_interpolate_inv(bary, p0.z, p1.z, p2.z);
+ if (z > ctx->depth[idx]) continue;
+ gastro_vec3 interp_v = gastro_vec3_new(
+ gastro_vec3_bary_interpolate(bary, p0.x, p1.x, p2.x),
+ gastro_vec3_bary_interpolate(bary, p0.y, p1.y, p2.y),
+ z
+ );
+ /* interpolate all of the attributes as well */
+ /* again, to be perspective correct, we do some funny math */
+ for (a = 0; a < attrs_len; ++a) {
+ gastro_fix n0z = gastro_fix_div(n0.attrs[a], p0.z);
+ gastro_fix n1z = gastro_fix_div(n1.attrs[a], p1.z);
+ gastro_fix n2z = gastro_fix_div(n2.attrs[a], p2.z);
+ attrs[a] = gastro_fix_mul(
+ z,
+ gastro_vec3_bary_interpolate(bary, n0z, n1z, n2z)
+ );
+ }
+ /* actually compute the color for and write the pixel */
+ col = gastro_program_fragment(ctx, p, interp_v, attrs, attrs_len);
+ gastro_draw_pixel(ctx, col, z, x, y);
+ ctx->pixels[idx] = col;
+ ctx->depth[idx] = z;
+ }
+ }
+ }
+}
+
+static gastro_vertex get_vertex(gastro_ctx *ctx, gastro_program *p, gastro_fix *vs, i64 elem_size, i64 idx) {
+ i64 i = idx * elem_size;
+ return gastro_program_vertex(ctx, p, gastro_vec3_new(vs[i], vs[i+1], vs[i+2]), &vs[i+3], elem_size - 3);
+}
+void gastro_render_triangles(gastro_ctx *ctx, gastro_program *p, gastro_fix *vs, i64 elem_size, i64 *idxs, i64 num_triangles) {
+ i64 i;
+ for (i = 0; i < 3 * num_triangles; i += 3) {
+ gastro_vertex v0 = get_vertex(ctx, p, vs, elem_size, idxs[i]);
+ gastro_vertex v1 = get_vertex(ctx, p, vs, elem_size, idxs[i + 1]);
+ gastro_vertex v2 = get_vertex(ctx, p, vs, elem_size, idxs[i + 2]);
+ gastro_render_triangle(ctx, p, v0, v1, v2, elem_size - 3);
+ }
+}
diff --git a/gastro/src/main.c b/gastro/src/main.c
new file mode 100644
index 0000000..af2e3aa
--- /dev/null
+++ b/gastro/src/main.c
@@ -0,0 +1,242 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <raylib.h>
+
+#include <lcq/qoi.h>
+
+#include <lcq/gastro.h>
+
+/* #include "../converted_model.h" */
+
+i64 off = 0;
+qoi_color *TEXTURE;
+i64 TEXTURE_WIDTH;
+i64 TEXTURE_HEIGHT;
+
+#define QUADIDX(p0, p1, p2, p3) p0, p1, p2, p2, p3, p0
+#define COLOR(r, g, b) gastro_fix_new(r), gastro_fix_new(g), gastro_fix_new(b)
+static void draw_quad(gastro_ctx *ctx, gastro_program *p) {
+ gastro_fix sz = GASTRO_FIX_HALF;
+ gastro_fix verts[] = {
+ /* front */
+ -sz, -sz, -sz, 0, 0,
+ +sz, -sz, -sz, GASTRO_FIX_ONE, 0,
+ +sz, +sz, -sz, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ -sz, +sz, -sz, 0, GASTRO_FIX_ONE
+ };
+ i64 idxs[] = {
+ QUADIDX(0, 1, 2, 3), /* front */
+ };
+ gastro_render_triangles(ctx, p, verts, 5, idxs, 2);
+}
+static void draw_cube(gastro_ctx *ctx, gastro_program *p) {
+ static gastro_fix verts[] = {
+ /* front */
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, 0, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, 0, GASTRO_FIX_ONE,
+ /* right */
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, 0, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, 0, GASTRO_FIX_ONE,
+ /* left */
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, 0, 0,
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, 0, GASTRO_FIX_ONE,
+ /* top */
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, 0, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, 0, GASTRO_FIX_ONE,
+ /* bottom */
+ -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, 0, 0,
+ +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, 0, GASTRO_FIX_ONE,
+ /* back */
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, 0, 0,
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, +GASTRO_FIX_HALF, 0, GASTRO_FIX_ONE
+ };
+ static i64 idxs[] = {
+ QUADIDX(0, 1, 2, 3), /* front */
+ QUADIDX(4, 5, 6, 7), /* right */
+ QUADIDX(8, 9, 10, 11), /* left */
+ QUADIDX(12, 13, 14, 15), /* top */
+ QUADIDX(16, 17, 18, 19), /* bottom */
+ QUADIDX(20, 21, 22, 23) /* back */
+ };
+ gastro_render_triangles(ctx, p, verts, 5, idxs, 12);
+}
+static void draw_pyramid(gastro_ctx *ctx, gastro_program *p) {
+ static gastro_fix verts[] = {
+ /* front */
+ 0, +GASTRO_FIX_HALF, 0, 0, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ /* right */
+ 0, +GASTRO_FIX_HALF, 0, 0, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ /* left */
+ 0, +GASTRO_FIX_HALF, 0, 0, 0,
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ /* back */
+ 0, +GASTRO_FIX_HALF, 0, 0, 0,
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ /* bottom */
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, 0, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, GASTRO_FIX_ONE, 0,
+ +GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, GASTRO_FIX_ONE, GASTRO_FIX_ONE,
+ -GASTRO_FIX_HALF, -GASTRO_FIX_HALF, +GASTRO_FIX_HALF, 0, GASTRO_FIX_ONE,
+ };
+ static i64 idxs[] = {
+ 0, 1, 2,
+ 3, 4, 5,
+ 6, 7, 8,
+ 9, 10, 11,
+ QUADIDX(12, 13, 14, 15),
+ };
+ gastro_render_triangles(ctx, p, verts, 5, idxs, 6);
+}
+
+static gastro_vec4 vertex_shader(gastro_ctx *ctx, gastro_vec3 in, gastro_fix *attrs, i64 attrs_len) {
+ gastro_fix x = gastro_fix_from_double((double) off / 1000.0);
+ gastro_vec4 v = gastro_vec4_new(in.x, in.y, in.z, GASTRO_FIX_ONE);
+ gastro_mat4x4 scale = gastro_mat4x4_new(
+ gastro_fix_from_double(1.0), 0, 0, 0,
+ 0, gastro_fix_from_double(1.0), 0, 0,
+ 0, 0, gastro_fix_from_double(1.0), 0,
+ 0, 0, 0, GASTRO_FIX_ONE
+ );
+ u8 theta = (u8) (off / 4);
+ /* u8 theta = 13; */
+ gastro_mat4x4 rot = gastro_mat4x4_new(
+ GASTRO_FIX_ONE, 0, 0, 0,
+ 0, gastro_cos(theta), -gastro_sin(theta), 0,
+ 0, gastro_sin(theta), gastro_cos(theta), 0,
+ 0, 0, 0, GASTRO_FIX_ONE
+ );
+ gastro_mat4x4 trans = gastro_mat4x4_new(
+ GASTRO_FIX_ONE, 0, 0, gastro_fix_new(0), /* x,*/
+ 0, GASTRO_FIX_ONE, 0, gastro_fix_new(1),
+ 0, 0, GASTRO_FIX_ONE, gastro_fix_new(-3),
+ 0, 0, 0, GASTRO_FIX_ONE
+ );
+ gastro_mat4x4 proj = gastro_mat4x4_new(
+ GASTRO_FIX_ONE, 0, 0, 0,
+ 0, GASTRO_FIX_ONE, 0, 0,
+ 0, 0, GASTRO_FIX_ONE, 0,
+ 0, 0, GASTRO_FIX_ONE, 0
+ );
+ v = gastro_mat4x4_mul_vec4(scale, v);
+ v = gastro_mat4x4_mul_vec4(rot, v);
+ v = gastro_mat4x4_mul_vec4(trans, v);
+ v = gastro_mat4x4_mul_vec4(proj, v);
+ return v;
+}
+
+static gastro_color fragment_shader_texture(gastro_ctx *ctx, gastro_vec3 in, gastro_fix *attrs, i64 attrs_len) {
+ gastro_fix u = attrs[0];
+ gastro_fix v = attrs[1];
+ i64 x = gastro_fix_to_i64(gastro_fix_mul(gastro_fix_new(TEXTURE_WIDTH), u));
+ i64 y = gastro_fix_to_i64(gastro_fix_mul(gastro_fix_new(TEXTURE_HEIGHT), v));
+ qoi_color c = TEXTURE[y * TEXTURE_WIDTH + x];
+ return gastro_color_new(c.r, c.g, c.b, c.a);
+}
+
+static gastro_color fragment_shader_teapot(gastro_ctx *ctx, gastro_vec3 in, gastro_fix *attrs, i64 attrs_len) {
+ u8 col = (u8) gastro_fix_to_i64(
+ gastro_fix_mul(
+ gastro_fix_new(0xff),
+ gastro_fix_div(in.z, gastro_fix_new(8))
+ )
+ );
+ return gastro_color_new(0xff - col, 0xff - col, 0xff - col, 0xff);
+}
+
+void load_image(char *path) {
+ FILE *f;
+ u8 *buf;
+ i64 len;
+ qoi_decoder q;
+ qoi_header h;
+ qoi_color *pixels;
+ if (!(f = fopen(path, "r"))) {
+ fprintf(stderr, "failed to open file: %s\n", path);
+ return;
+ }
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ buf = calloc((size_t) len, sizeof(u8));
+ fread(buf, sizeof(u8), (size_t) len, f);
+ qoi_decoder_new(&q, buf, len);
+ if (!qoi_decode_header(&q, &h)) {
+ fprintf(stderr, "failed to decode header\n");
+ return;
+ }
+ pixels = calloc(h.width * h.height, sizeof(qoi_color));
+ qoi_decode(&q, &h, pixels);
+ TEXTURE = pixels;
+ TEXTURE_WIDTH = h.width;
+ TEXTURE_HEIGHT = h.height;
+}
+
+int main(int argc, char **argv) {
+ gastro_ctx ctx;
+ u32 width = 256, height = 256;
+ gastro_color *pixels = calloc(width * height, sizeof(gastro_color));
+ gastro_fix *depth = calloc(width * height, sizeof(gastro_fix));
+ gastro_program p;
+ gastro_ctx_init(&ctx, width, height, pixels, depth);
+ p.vertex = vertex_shader;
+ p.fragment = fragment_shader_texture;
+
+ load_image("mark2.qoi");
+
+ /* gastro_draw_clear(&ctx, gastro_color_new(0, 0, 0, 0xff)); */
+ /* draw_model(&ctx, &p); */
+
+ InitWindow(800, 600, "test");
+ {
+ Image im;
+ Texture2D tex;
+ im.data = ctx.pixels;
+ im.width = (int) ctx.width;
+ im.height = (int) ctx.height;
+ im.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+ im.mipmaps = 1;
+ tex = LoadTextureFromImage(im);
+ SetTargetFPS(300);
+ while (!WindowShouldClose()) {
+ i64 i;
+ Vector2 pos;
+ pos.x = 10; pos.y = 10;
+ gastro_draw_clear(&ctx, gastro_color_new(0, 0, 0, 0xff));
+ for (i = 0; i < 1; ++i) {
+ draw_pyramid(&ctx, &p);
+ }
+ /* draw_quad(&ctx, &p); */
+ /* draw_model(&ctx, &p); */
+ off += 1;
+ off %= 256 * 64;
+ BeginDrawing();
+ ClearBackground(RED);
+ UpdateTexture(tex, ctx.pixels);
+ DrawTextureEx(tex, pos, 0.0, 4.0, WHITE);
+ DrawFPS(10, 10);
+ EndDrawing();
+ }
+ }
+ CloseWindow();
+ (void) argc; (void) argv;
+ return 0;
+}
diff --git a/gastro/src/trig.c b/gastro/src/trig.c
new file mode 100644
index 0000000..7f838c1
--- /dev/null
+++ b/gastro/src/trig.c
@@ -0,0 +1,268 @@
+#include <lcq/gastro/trig.h>
+
+static gastro_fix SIN_TABLE[256] = {
+ 0x0,
+ 0x648,
+ 0xc8f,
+ 0x12d5,
+ 0x1917,
+ 0x1f56,
+ 0x2590,
+ 0x2bc4,
+ 0x31f1,
+ 0x3817,
+ 0x3e33,
+ 0x4447,
+ 0x4a50,
+ 0x504d,
+ 0x563e,
+ 0x5c22,
+ 0x61f7,
+ 0x67bd,
+ 0x6d74,
+ 0x7319,
+ 0x78ad,
+ 0x7e2e,
+ 0x839c,
+ 0x88f5,
+ 0x8e39,
+ 0x9368,
+ 0x987f,
+ 0x9d7f,
+ 0xa267,
+ 0xa736,
+ 0xabeb,
+ 0xb085,
+ 0xb504,
+ 0xb968,
+ 0xbdae,
+ 0xc1d8,
+ 0xc5e4,
+ 0xc9d1,
+ 0xcd9f,
+ 0xd14d,
+ 0xd4db,
+ 0xd848,
+ 0xdb94,
+ 0xdebe,
+ 0xe1c5,
+ 0xe4aa,
+ 0xe76b,
+ 0xea09,
+ 0xec83,
+ 0xeed8,
+ 0xf109,
+ 0xf314,
+ 0xf4fa,
+ 0xf6ba,
+ 0xf853,
+ 0xf9c7,
+ 0xfb14,
+ 0xfc3b,
+ 0xfd3a,
+ 0xfe13,
+ 0xfec4,
+ 0xff4e,
+ 0xffb1,
+ 0xffec,
+ 0xffff,
+ 0xffec,
+ 0xffb1,
+ 0xff4e,
+ 0xfec4,
+ 0xfe13,
+ 0xfd3a,
+ 0xfc3b,
+ 0xfb14,
+ 0xf9c7,
+ 0xf853,
+ 0xf6ba,
+ 0xf4fa,
+ 0xf314,
+ 0xf109,
+ 0xeed8,
+ 0xec83,
+ 0xea09,
+ 0xe76b,
+ 0xe4aa,
+ 0xe1c5,
+ 0xdebe,
+ 0xdb94,
+ 0xd848,
+ 0xd4db,
+ 0xd14d,
+ 0xcd9f,
+ 0xc9d1,
+ 0xc5e4,
+ 0xc1d8,
+ 0xbdae,
+ 0xb968,
+ 0xb504,
+ 0xb085,
+ 0xabeb,
+ 0xa736,
+ 0xa267,
+ 0x9d7f,
+ 0x987f,
+ 0x9368,
+ 0x8e39,
+ 0x88f5,
+ 0x839c,
+ 0x7e2e,
+ 0x78ad,
+ 0x7319,
+ 0x6d74,
+ 0x67bd,
+ 0x61f7,
+ 0x5c22,
+ 0x563e,
+ 0x504d,
+ 0x4a50,
+ 0x4447,
+ 0x3e33,
+ 0x3817,
+ 0x31f1,
+ 0x2bc4,
+ 0x2590,
+ 0x1f56,
+ 0x1917,
+ 0x12d5,
+ 0xc8f,
+ 0x648,
+ 0x0,
+ (i64) 0xfffffffffffff9b8,
+ (i64) 0xfffffffffffff371,
+ (i64) 0xffffffffffffed2b,
+ (i64) 0xffffffffffffe6e9,
+ (i64) 0xffffffffffffe0aa,
+ (i64) 0xffffffffffffda70,
+ (i64) 0xffffffffffffd43c,
+ (i64) 0xffffffffffffce0f,
+ (i64) 0xffffffffffffc7e9,
+ (i64) 0xffffffffffffc1cd,
+ (i64) 0xffffffffffffbbb9,
+ (i64) 0xffffffffffffb5b0,
+ (i64) 0xffffffffffffafb3,
+ (i64) 0xffffffffffffa9c2,
+ (i64) 0xffffffffffffa3de,
+ (i64) 0xffffffffffff9e09,
+ (i64) 0xffffffffffff9843,
+ (i64) 0xffffffffffff928c,
+ (i64) 0xffffffffffff8ce7,
+ (i64) 0xffffffffffff8753,
+ (i64) 0xffffffffffff81d2,
+ (i64) 0xffffffffffff7c64,
+ (i64) 0xffffffffffff770b,
+ (i64) 0xffffffffffff71c7,
+ (i64) 0xffffffffffff6c98,
+ (i64) 0xffffffffffff6781,
+ (i64) 0xffffffffffff6281,
+ (i64) 0xffffffffffff5d99,
+ (i64) 0xffffffffffff58ca,
+ (i64) 0xffffffffffff5415,
+ (i64) 0xffffffffffff4f7b,
+ (i64) 0xffffffffffff4afc,
+ (i64) 0xffffffffffff4698,
+ (i64) 0xffffffffffff4252,
+ (i64) 0xffffffffffff3e28,
+ (i64) 0xffffffffffff3a1c,
+ (i64) 0xffffffffffff362f,
+ (i64) 0xffffffffffff3261,
+ (i64) 0xffffffffffff2eb3,
+ (i64) 0xffffffffffff2b25,
+ (i64) 0xffffffffffff27b8,
+ (i64) 0xffffffffffff246c,
+ (i64) 0xffffffffffff2142,
+ (i64) 0xffffffffffff1e3b,
+ (i64) 0xffffffffffff1b56,
+ (i64) 0xffffffffffff1895,
+ (i64) 0xffffffffffff15f7,
+ (i64) 0xffffffffffff137d,
+ (i64) 0xffffffffffff1128,
+ (i64) 0xffffffffffff0ef7,
+ (i64) 0xffffffffffff0cec,
+ (i64) 0xffffffffffff0b06,
+ (i64) 0xffffffffffff0946,
+ (i64) 0xffffffffffff07ad,
+ (i64) 0xffffffffffff0639,
+ (i64) 0xffffffffffff04ec,
+ (i64) 0xffffffffffff03c5,
+ (i64) 0xffffffffffff02c6,
+ (i64) 0xffffffffffff01ed,
+ (i64) 0xffffffffffff013c,
+ (i64) 0xffffffffffff00b2,
+ (i64) 0xffffffffffff004f,
+ (i64) 0xffffffffffff0014,
+ (i64) 0xffffffffffff0001,
+ (i64) 0xffffffffffff0014,
+ (i64) 0xffffffffffff004f,
+ (i64) 0xffffffffffff00b2,
+ (i64) 0xffffffffffff013c,
+ (i64) 0xffffffffffff01ed,
+ (i64) 0xffffffffffff02c6,
+ (i64) 0xffffffffffff03c5,
+ (i64) 0xffffffffffff04ec,
+ (i64) 0xffffffffffff0639,
+ (i64) 0xffffffffffff07ad,
+ (i64) 0xffffffffffff0946,
+ (i64) 0xffffffffffff0b06,
+ (i64) 0xffffffffffff0cec,
+ (i64) 0xffffffffffff0ef7,
+ (i64) 0xffffffffffff1128,
+ (i64) 0xffffffffffff137d,
+ (i64) 0xffffffffffff15f7,
+ (i64) 0xffffffffffff1895,
+ (i64) 0xffffffffffff1b56,
+ (i64) 0xffffffffffff1e3b,
+ (i64) 0xffffffffffff2142,
+ (i64) 0xffffffffffff246c,
+ (i64) 0xffffffffffff27b8,
+ (i64) 0xffffffffffff2b25,
+ (i64) 0xffffffffffff2eb3,
+ (i64) 0xffffffffffff3261,
+ (i64) 0xffffffffffff362f,
+ (i64) 0xffffffffffff3a1c,
+ (i64) 0xffffffffffff3e28,
+ (i64) 0xffffffffffff4252,
+ (i64) 0xffffffffffff4698,
+ (i64) 0xffffffffffff4afc,
+ (i64) 0xffffffffffff4f7b,
+ (i64) 0xffffffffffff5415,
+ (i64) 0xffffffffffff58ca,
+ (i64) 0xffffffffffff5d99,
+ (i64) 0xffffffffffff6281,
+ (i64) 0xffffffffffff6781,
+ (i64) 0xffffffffffff6c98,
+ (i64) 0xffffffffffff71c7,
+ (i64) 0xffffffffffff770b,
+ (i64) 0xffffffffffff7c64,
+ (i64) 0xffffffffffff81d2,
+ (i64) 0xffffffffffff8753,
+ (i64) 0xffffffffffff8ce7,
+ (i64) 0xffffffffffff928c,
+ (i64) 0xffffffffffff9843,
+ (i64) 0xffffffffffff9e09,
+ (i64) 0xffffffffffffa3de,
+ (i64) 0xffffffffffffa9c2,
+ (i64) 0xffffffffffffafb3,
+ (i64) 0xffffffffffffb5b0,
+ (i64) 0xffffffffffffbbb9,
+ (i64) 0xffffffffffffc1cd,
+ (i64) 0xffffffffffffc7e9,
+ (i64) 0xffffffffffffce0f,
+ (i64) 0xffffffffffffd43c,
+ (i64) 0xffffffffffffda70,
+ (i64) 0xffffffffffffe0aa,
+ (i64) 0xffffffffffffe6e9,
+ (i64) 0xffffffffffffed2b,
+ (i64) 0xfffffffffffff371,
+ (i64) 0xfffffffffffff9b8
+};
+
+gastro_fix gastro_sin(u8 turns) {
+ return SIN_TABLE[turns];
+}
+
+gastro_fix gastro_cos(u8 turns) {
+ return SIN_TABLE[(u8) (turns + 64)];
+}