textuer
This commit is contained in:
+54
-18
@@ -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
@@ -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]);
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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).
|
||||||
|
|||||||
Reference in New Issue
Block a user