This commit is contained in:
veclavtlica
2026-09-14 00:02:08 +03:00
parent 578c4a6d4f
commit c350d65592
5 changed files with 90 additions and 26 deletions
+54 -18
View File
@@ -5,6 +5,7 @@
#ifndef COM_GEM_H #ifndef COM_GEM_H
#define COM_GEM_H #define COM_GEM_H
#include "fix.h"
#include "mat.h" #include "mat.h"
#include "pnt.h" #include "pnt.h"
#include "vec.h" #include "vec.h"
@@ -60,38 +61,73 @@ static inline uint8_t com_gem_clip_vis_project(com_vec_t a, com_pnt_t *out) {
} }
static inline void com_gem_draw_triangle(com_pnt_t v0, com_pnt_t v1, static inline void com_gem_draw_triangle(com_pnt_t v0, com_pnt_t v1,
com_pnt_t v2, uint8_t *buffer) { com_pnt_t v2, uint8_t *out) {
// 1. Compute the bounding box of the triangle
int minX = com_vec_min(com_vec_from(v0.s.x, v1.s.x, v2.s.x)); int minX = com_vec_min(com_vec_from(v0.s.x, v1.s.x, v2.s.x));
int minY = com_vec_min(com_vec_from(v0.s.y, v1.s.y, v2.s.y)); int minY = com_vec_min(com_vec_from(v0.s.y, v1.s.y, v2.s.y));
int maxX = com_vec_max(com_vec_from(v0.s.x, v1.s.x, v2.s.x)); int maxX = com_vec_max(com_vec_from(v0.s.x, v1.s.x, v2.s.x));
int maxY = com_vec_max(com_vec_from(v0.s.y, v1.s.y, v2.s.y)); int maxY = com_vec_max(com_vec_from(v0.s.y, v1.s.y, v2.s.y));
// 2. Clip the bounding box against screen dimensions to avoid out-of-bounds
// errors
// minX = CLAMP(minX, 0, 480 - 1);
// minY = CLAMP(minY, 0, HEIGHT - 1);
// maxX = CLAMP(maxX, 0, 480 - 1);
// maxY = CLAMP(maxY, 0, HEIGHT - 1);
// 3. Loop over all pixels inside the bounding box
for (int y = minY; y <= maxY; y++) { for (int y = minY; y <= maxY; y++) {
for (int x = minX; x <= maxX; x++) { for (int x = minX; x <= maxX; x++) {
com_pnt_t p = {.s = {x, y}}; com_pnt_t p = {.s = {x, y}};
// Test the pixel center against all 3 edges // Test the pixel center against all 3 edges
int w0 = com_pnt_edge_orient(v1, v2, p); com_vec_t ws = com_pnt_edge_weight(v0, v1, v2, p);
int w1 = com_pnt_edge_orient(v2, v0, p);
int w2 = com_pnt_edge_orient(v0, v1, p);
// If the point is inside or on all edges, draw the pixel // If the point is inside or on all edges, draw the pixel
// (Assumes Clockwise vertex ordering) // (Assumes Clockwise vertex ordering)
if (w0 >= 0 && w1 >= 0 && w2 >= 0) { if (ws.a[0] >= 0 && ws.a[1] >= 0 && ws.a[2] >= 0) {
printf("%i, %i\n", y, x); printf("%i, %i\n", y, x);
buffer[(y * 640 + x) * 4 + 0] = '#'; out[(y * 640 + x) * 4 + 0] = 125;
buffer[(y * 640 + x) * 4 + 1] = '#'; out[(y * 640 + x) * 4 + 1] = 125;
buffer[(y * 640 + x) * 4 + 2] = '#'; out[(y * 640 + x) * 4 + 2] = 125;
buffer[(y * 640 + x) * 4 + 3] = '#'; }
}
}
}
static inline void com_gem_draw_triangle_textured(com_pnt_t v0, com_pnt_t v1,
com_pnt_t v2, uint8_t *out,
com_pnt_t uv0, com_pnt_t uv1,
com_pnt_t uv2, uint8_t *tex) {
int minX = com_vec_min(com_vec_from(v0.s.x, v1.s.x, v2.s.x));
int minY = com_vec_min(com_vec_from(v0.s.y, v1.s.y, v2.s.y));
int maxX = com_vec_max(com_vec_from(v0.s.x, v1.s.x, v2.s.x));
int maxY = com_vec_max(com_vec_from(v0.s.y, v1.s.y, v2.s.y));
com_fix_t const area = com_pnt_area(v0, v1, v2);
for (int y = minY; y <= maxY; y++) {
for (int x = minX; x <= maxX; x++) {
com_pnt_t p = {.s = {x, y}};
/* TODO: Move to a separate weighting function over vec. */
// Test the pixel center against all 3 edges
com_vec_t ws = com_pnt_edge_weight(v0, v1, v2, p);
// If the point is inside or on all edges, draw the pixel
// (Assumes Clockwise vertex ordering)
if (ws.a[0] >= 0 && ws.a[1] >= 0 && ws.a[2] >= 0) {
// Barycentric weights
ws = com_vec_div(ws, com_vec_scalar(area));
// Affine interpolation of UVs.
com_fix_t const u =
(ws.a[0] * uv0.a[0] + ws.a[1] * uv1.a[0] + ws.a[2] * uv2.a[0]) >>
COM_FIX_FRACBITS;
com_fix_t const v =
(ws.a[0] * uv0.a[1] + ws.a[1] * uv1.a[1] + ws.a[2] * uv2.a[1]) >>
COM_FIX_FRACBITS;
// Nearest-neighbor texture lookup, in [0..32) range.
int32_t tx =
(int32_t)((u + (COM_FIX_FRACUNIT / 2)) / (COM_FIX_FRACUNIT / 16));
int32_t ty =
(int32_t)((v + (COM_FIX_FRACUNIT / 2)) / (COM_FIX_FRACUNIT / 16));
out[(y * 640 + x) * 4 + 0] = tex[(ty * 32 + tx) * 3 + 0];
out[(y * 640 + x) * 4 + 1] = tex[(ty * 32 + tx) * 3 + 1];
out[(y * 640 + x) * 4 + 2] = tex[(ty * 32 + tx) * 3 + 2];
} }
} }
} }
+11 -1
View File
@@ -71,13 +71,23 @@ static inline com_fix_t com_pnt_max(com_pnt_t a) {
/* TODO: Safeguard by com_fix_mul()? */ /* TODO: Safeguard by com_fix_mul()? */
/* Tests whether a point is lying right to a line produced by l0 and l1. /* Tests whether a point is lying right to a line produced by l0 and l1.
This assumes that l0 and l1 are in clockwise order. This assumes that l0 and l1 are in clockwise order.
Result is positive or zero if it holds true, otherwise it's negative. */ Result is positive or zero if it holds true, otherwise it's negative.
This also produces area of a triangle! */
static inline com_fix_t com_pnt_edge_orient(com_pnt_t l0, com_pnt_t l1, static inline com_fix_t com_pnt_edge_orient(com_pnt_t l0, com_pnt_t l1,
com_pnt_t p) { com_pnt_t p) {
return (l0.s.x - l1.s.x) * (p.s.y - l1.s.y) - return (l0.s.x - l1.s.x) * (p.s.y - l1.s.y) -
(l0.s.y - l1.s.y) * (p.s.x - l1.s.x); (l0.s.y - l1.s.y) * (p.s.x - l1.s.x);
} }
#define com_pnt_area(m_p0, m_p1, m_p2) (com_pnt_edge_orient(m_p0, m_p1, m_p2))
static inline com_vec_t com_pnt_edge_weight(com_pnt_t v0, com_pnt_t v1,
com_pnt_t v2, com_pnt_t p) {
return (com_vec_t){.s = {.x = com_pnt_edge_orient(v1, v2, p),
.y = com_pnt_edge_orient(v2, v0, p),
.z = com_pnt_edge_orient(v0, v1, p)}};
}
static inline void com_pnt_print(com_pnt_t a) { static inline void com_pnt_print(com_pnt_t a) {
com_fix_print(a.a[0]); com_fix_print(a.a[0]);
com_fix_print(a.a[1]); com_fix_print(a.a[1]);
+4
View File
@@ -24,6 +24,10 @@ static inline com_vec_t com_vec_from(com_fix_t x, com_fix_t y, com_fix_t z) {
return (com_vec_t){.s = {.x = x, .y = y, .z = z}}; return (com_vec_t){.s = {.x = x, .y = y, .z = z}};
} }
static inline com_vec_t com_vec_scalar(com_fix_t s) {
return (com_vec_t){.s = {.x = s, .y = s, .z = s}};
}
static inline com_vec_t com_vec_identity(void) { static inline com_vec_t com_vec_identity(void) {
return (com_vec_t){.s = {.x = COM_FIX_FRACUNIT, return (com_vec_t){.s = {.x = COM_FIX_FRACUNIT,
.y = COM_FIX_FRACUNIT, .y = COM_FIX_FRACUNIT,
+17 -2
View File
@@ -145,9 +145,13 @@ extern int plr_display_x11_main(int argc, char *argv[]) {
com_vec_from(5 * COM_FIX_FRACUNIT, 0, 2 * COM_FIX_FRACUNIT); com_vec_from(5 * COM_FIX_FRACUNIT, 0, 2 * COM_FIX_FRACUNIT);
com_vec_t v1 = com_vec_from(5 * COM_FIX_FRACUNIT, 1 * COM_FIX_FRACUNIT, com_vec_t v1 = com_vec_from(5 * COM_FIX_FRACUNIT, 1 * COM_FIX_FRACUNIT,
4 * COM_FIX_FRACUNIT); 4 * COM_FIX_FRACUNIT);
com_vec_t v2 = com_vec_from(5 * COM_FIX_FRACUNIT, 2 * COM_FIX_FRACUNIT, com_vec_t v2 = com_vec_from(5 * COM_FIX_FRACUNIT, 4 * COM_FIX_FRACUNIT,
4 * COM_FIX_FRACUNIT); 4 * COM_FIX_FRACUNIT);
com_pnt_t uv0 = com_pnt_from(0 * COM_FIX_FRACUNIT, 0 * COM_FIX_FRACUNIT);
com_pnt_t uv1 = com_pnt_from(1 * COM_FIX_FRACUNIT, 0 * COM_FIX_FRACUNIT);
com_pnt_t uv2 = com_pnt_from(1 * COM_FIX_FRACUNIT, 1 * COM_FIX_FRACUNIT);
com_vec_t c0 = com_gem_vec_project_clip(mvp, v0); com_vec_t c0 = com_gem_vec_project_clip(mvp, v0);
com_vec_t c1 = com_gem_vec_project_clip(mvp, v1); com_vec_t c1 = com_gem_vec_project_clip(mvp, v1);
com_vec_t c2 = com_gem_vec_project_clip(mvp, v2); com_vec_t c2 = com_gem_vec_project_clip(mvp, v2);
@@ -161,7 +165,18 @@ extern int plr_display_x11_main(int argc, char *argv[]) {
com_pnt_print(p1); com_pnt_print(p1);
com_pnt_print(p2); com_pnt_print(p2);
com_gem_draw_triangle(p0, p1, p2, (uint8_t *)pixel_buffer); uint8_t tex[32 * 32 * 3] = {0};
for (int y = 0; y < 32; ++y) {
for (int x = 0; x < 32; ++x) {
tex[(y * 32 + x) * 3 + 0] = (x / 2 + y / 2) % 2 == 0 ? 175 : 0;
tex[(y * 32 + x) * 3 + 1] = (x / 2 + y / 2) % 2 == 0 ? 0 : 0;
tex[(y * 32 + x) * 3 + 2] = (x / 2 + y / 2) % 2 == 0 ? 175 : 0;
}
}
com_gem_draw_triangle_textured(p0, p1, p2, (uint8_t *)pixel_buffer, uv0,
uv1, uv2, tex);
// com_gem_draw_triangle(p0, p1, p2, (uint8_t *)pixel_buffer);
// Draw the complete image onto the window when exposed // Draw the complete image onto the window when exposed
XPutImage(display, XPutImage(display,
+4 -5
View File
@@ -1,14 +1,13 @@
Pustina -- Player Brightstone -- Player
Locked step multiplayer oriented engine with great restrictions. Locked step multiplayer retro oriented engine with great restrictions.
We do not use abstractions, as the simplicity of the required set of features should allow for platform-specific implementations. We do not use abstractions, as the simplicity of the required set of features should allow for platform-specific implementations.
---- Restrictions ---- ---- Restrictions ----
* Fixed sized paletted frambuffer, defaulted to 640x480. Scaling by 2 is possible. * Fixed sized paletted frambuffer, defaulted to 640x480.
* Deterministic logic via Q15.16 fixed point numbers. * Deterministic logic via Q15.16 fixed point numbers.
* .gam image format, derived from .gif. RLE and Delta Frame Optimiztion is added, palette is predefined. * .gam image format, derived from .gif. RLE and Delta Frame Optimiztion is added, palette is predefined.
* Images are 16x16 or multiples of it, aligning to the grid. * Textures are 32x32 pixels.
* Tile view is capped at 29x29, the rest is allocated to interface.
* 30 FPS display rate. * 30 FPS display rate.
* Fonts are in ASCII bitmaps. Tile slicing commands are issued to render them in. * Fonts are in ASCII bitmaps. Tile slicing commands are issued to render them in.
* Audio samples are in our own .sam format (s8 frame delta + lzw). * Audio samples are in our own .sam format (s8 frame delta + lzw).