wip procgen
This commit is contained in:
parent
a653eae745
commit
79a678f162
18 changed files with 1317 additions and 127 deletions
300
src/pxl8_gfx.c
300
src/pxl8_gfx.c
|
|
@ -42,12 +42,18 @@ struct pxl8_gfx {
|
|||
pxl8_mat4 model;
|
||||
pxl8_mat4 projection;
|
||||
pxl8_mat4 view;
|
||||
pxl8_mat4 mvp;
|
||||
bool mvp_dirty;
|
||||
pxl8_frustum frustum;
|
||||
bool wireframe;
|
||||
f32* zbuffer;
|
||||
i32 zbuffer_height;
|
||||
i32 zbuffer_width;
|
||||
|
||||
bool affine_textures;
|
||||
|
||||
u32 frame_triangle_count;
|
||||
u32 frame_pixel_count;
|
||||
};
|
||||
|
||||
static inline void pxl8_color_unpack(u32 color, u8* r, u8* g, u8* b, u8* a) {
|
||||
|
|
@ -191,6 +197,7 @@ pxl8_gfx* pxl8_gfx_create(
|
|||
gfx->model = pxl8_mat4_identity();
|
||||
gfx->projection = pxl8_mat4_identity();
|
||||
gfx->view = pxl8_mat4_identity();
|
||||
gfx->mvp_dirty = true;
|
||||
gfx->wireframe = false;
|
||||
gfx->zbuffer = NULL;
|
||||
gfx->zbuffer_height = 0;
|
||||
|
|
@ -403,10 +410,23 @@ void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom) {
|
|||
|
||||
void pxl8_clr(pxl8_gfx* gfx, u32 color) {
|
||||
if (!gfx || !gfx->framebuffer) return;
|
||||
|
||||
|
||||
static u32 frame_count = 0;
|
||||
if (gfx->frame_triangle_count > 0) {
|
||||
if (frame_count % 60 == 0) {
|
||||
i32 fb_pixels = gfx->framebuffer_width * gfx->framebuffer_height;
|
||||
f32 overdraw = (f32)gfx->frame_pixel_count / (f32)fb_pixels;
|
||||
pxl8_debug("Frame triangles: %u, pixels: %u, overdraw: %.2fx",
|
||||
gfx->frame_triangle_count, gfx->frame_pixel_count, overdraw);
|
||||
}
|
||||
frame_count++;
|
||||
}
|
||||
gfx->frame_triangle_count = 0;
|
||||
gfx->frame_pixel_count = 0;
|
||||
|
||||
i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1;
|
||||
i32 size = gfx->framebuffer_width * gfx->framebuffer_height;
|
||||
|
||||
|
||||
if (bytes_per_pixel == 4) {
|
||||
u32* fb32 = (u32*)gfx->framebuffer;
|
||||
for (i32 i = 0; i < size; i++) {
|
||||
|
|
@ -786,9 +806,33 @@ static bool pxl8_3d_init_zbuffer(pxl8_gfx* gfx) {
|
|||
|
||||
void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->zbuffer) return;
|
||||
for (i32 i = 0; i < gfx->zbuffer_width * gfx->zbuffer_height; i++) {
|
||||
gfx->zbuffer[i] = 1e30f;
|
||||
|
||||
i32 count = gfx->zbuffer_width * gfx->zbuffer_height;
|
||||
const f32 far_z = 1e30f;
|
||||
|
||||
#if defined(PXL8_SIMD_NEON)
|
||||
float32x4_t far_vec = vdupq_n_f32(far_z);
|
||||
i32 i = 0;
|
||||
for (; i + 3 < count; i += 4) {
|
||||
vst1q_f32(&gfx->zbuffer[i], far_vec);
|
||||
}
|
||||
for (; i < count; i++) {
|
||||
gfx->zbuffer[i] = far_z;
|
||||
}
|
||||
#elif defined(PXL8_SIMD_SSE2)
|
||||
__m128 far_vec = _mm_set1_ps(far_z);
|
||||
i32 i = 0;
|
||||
for (; i + 3 < count; i += 4) {
|
||||
_mm_store_ps(&gfx->zbuffer[i], far_vec);
|
||||
}
|
||||
for (; i < count; i++) {
|
||||
gfx->zbuffer[i] = far_z;
|
||||
}
|
||||
#else
|
||||
for (i32 i = 0; i < count; i++) {
|
||||
gfx->zbuffer[i] = far_z;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling) {
|
||||
|
|
@ -799,16 +843,19 @@ void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling) {
|
|||
void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat) {
|
||||
if (!gfx) return;
|
||||
gfx->model = mat;
|
||||
gfx->mvp_dirty = true;
|
||||
}
|
||||
|
||||
void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat) {
|
||||
if (!gfx) return;
|
||||
gfx->projection = mat;
|
||||
gfx->mvp_dirty = true;
|
||||
}
|
||||
|
||||
void pxl8_3d_set_view(pxl8_gfx* gfx, pxl8_mat4 mat) {
|
||||
if (!gfx) return;
|
||||
gfx->view = mat;
|
||||
gfx->mvp_dirty = true;
|
||||
}
|
||||
|
||||
void pxl8_3d_set_wireframe(pxl8_gfx* gfx, bool wireframe) {
|
||||
|
|
@ -821,7 +868,27 @@ void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine) {
|
|||
gfx->affine_textures = affine;
|
||||
}
|
||||
|
||||
static inline void pxl8_update_mvp(pxl8_gfx* gfx) {
|
||||
if (!gfx->mvp_dirty) return;
|
||||
|
||||
gfx->mvp = pxl8_mat4_multiply(gfx->projection,
|
||||
pxl8_mat4_multiply(gfx->view, gfx->model));
|
||||
|
||||
pxl8_mat4 vp = pxl8_mat4_multiply(gfx->projection, gfx->view);
|
||||
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
||||
|
||||
gfx->mvp_dirty = false;
|
||||
}
|
||||
|
||||
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx) {
|
||||
if (!gfx) return NULL;
|
||||
pxl8_update_mvp(gfx);
|
||||
return &gfx->frustum;
|
||||
}
|
||||
|
||||
static inline pxl8_vec4 pxl8_transform_vertex(pxl8_gfx* gfx, pxl8_vec3 pos) {
|
||||
pxl8_update_mvp(gfx);
|
||||
|
||||
pxl8_vec4 v = {
|
||||
.x = pos.x,
|
||||
.y = pos.y,
|
||||
|
|
@ -829,10 +896,7 @@ static inline pxl8_vec4 pxl8_transform_vertex(pxl8_gfx* gfx, pxl8_vec3 pos) {
|
|||
.w = 1.0f,
|
||||
};
|
||||
|
||||
pxl8_mat4 mvp = pxl8_mat4_multiply(gfx->projection,
|
||||
pxl8_mat4_multiply(gfx->view, gfx->model));
|
||||
|
||||
return pxl8_mat4_multiply_vec4(mvp, v);
|
||||
return pxl8_mat4_multiply_vec4(gfx->mvp, v);
|
||||
}
|
||||
|
||||
static inline void pxl8_project_to_screen(pxl8_gfx* gfx, pxl8_vec4 clip, i32* x, i32* y, f32* z) {
|
||||
|
|
@ -865,23 +929,82 @@ void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color)
|
|||
pxl8_line(gfx, x0, y0, x1, y1, color);
|
||||
}
|
||||
|
||||
static inline void pxl8_fill_scanline(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) {
|
||||
static inline void pxl8_fill_scanline_hicolor(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) {
|
||||
if (y < 0 || y >= gfx->framebuffer_height) return;
|
||||
if (xs > xe) {
|
||||
i32 tmp = xs; xs = xe; xe = tmp;
|
||||
f32 tmpz = z0; z0 = z1; z1 = tmpz;
|
||||
}
|
||||
|
||||
for (i32 x = xs; x <= xe; x++) {
|
||||
if (x >= 0 && x < gfx->framebuffer_width) {
|
||||
f32 t = (xe == xs) ? 0.0f : (f32)(x - xs) / (f32)(xe - xs);
|
||||
f32 z = z0 + t * (z1 - z0);
|
||||
if (xs < 0) xs = 0;
|
||||
if (xe >= gfx->framebuffer_width) xe = gfx->framebuffer_width - 1;
|
||||
if (xs > xe) return;
|
||||
|
||||
i32 idx = y * gfx->zbuffer_width + x;
|
||||
if (z <= gfx->zbuffer[idx]) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
pxl8_pixel(gfx, x, y, color);
|
||||
}
|
||||
i32 width = xe - xs;
|
||||
if (width == 0) {
|
||||
i32 idx = y * gfx->zbuffer_width + xs;
|
||||
if (z0 <= gfx->zbuffer[idx]) {
|
||||
gfx->zbuffer[idx] = z0;
|
||||
i32 fb_idx = y * gfx->framebuffer_width + xs;
|
||||
((u32*)gfx->framebuffer)[fb_idx] = color;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
f32 dz = (z1 - z0) / (f32)width;
|
||||
f32 z = z0;
|
||||
|
||||
i32 zbuf_offset = y * gfx->zbuffer_width;
|
||||
i32 fb_offset = y * gfx->framebuffer_width;
|
||||
u32* fb = (u32*)gfx->framebuffer;
|
||||
|
||||
for (i32 x = xs; x <= xe; x++) {
|
||||
i32 idx = zbuf_offset + x;
|
||||
if (z <= gfx->zbuffer[idx]) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
fb[fb_offset + x] = color;
|
||||
}
|
||||
z += dz;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void pxl8_fill_scanline_indexed(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) {
|
||||
if (y < 0 || y >= gfx->framebuffer_height) return;
|
||||
if (xs > xe) {
|
||||
i32 tmp = xs; xs = xe; xe = tmp;
|
||||
f32 tmpz = z0; z0 = z1; z1 = tmpz;
|
||||
}
|
||||
|
||||
if (xs < 0) xs = 0;
|
||||
if (xe >= gfx->framebuffer_width) xe = gfx->framebuffer_width - 1;
|
||||
if (xs > xe) return;
|
||||
|
||||
i32 width = xe - xs;
|
||||
u8 idx_color = color & 0xFF;
|
||||
|
||||
if (width == 0) {
|
||||
i32 idx = y * gfx->zbuffer_width + xs;
|
||||
if (z0 <= gfx->zbuffer[idx]) {
|
||||
gfx->zbuffer[idx] = z0;
|
||||
i32 fb_idx = y * gfx->framebuffer_width + xs;
|
||||
gfx->framebuffer[fb_idx] = idx_color;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
f32 dz = (z1 - z0) / (f32)width;
|
||||
f32 z = z0;
|
||||
|
||||
i32 zbuf_offset = y * gfx->zbuffer_width;
|
||||
i32 fb_offset = y * gfx->framebuffer_width;
|
||||
|
||||
for (i32 x = xs; x <= xe; x++) {
|
||||
i32 idx = zbuf_offset + x;
|
||||
if (z <= gfx->zbuffer[idx]) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
gfx->framebuffer[fb_offset + x] = idx_color;
|
||||
}
|
||||
z += dz;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -927,47 +1050,117 @@ static inline void pxl8_fill_scanline_textured(
|
|||
tmpf = w0; w0 = w1; w1 = tmpf;
|
||||
}
|
||||
|
||||
for (i32 x = xs; x <= xe; x++) {
|
||||
if (x >= 0 && x < gfx->framebuffer_width) {
|
||||
f32 t = (xe == xs) ? 0.0f : (f32)(x - xs) / (f32)(xe - xs);
|
||||
f32 z = z0 + t * (z1 - z0);
|
||||
|
||||
i32 idx = y * gfx->zbuffer_width + x;
|
||||
if (z <= gfx->zbuffer[idx]) {
|
||||
f32 u = u0 + t * (u1 - u0);
|
||||
f32 v = v0 + t * (v1 - v0);
|
||||
|
||||
if (!gfx->affine_textures) {
|
||||
f32 w = w0 + t * (w1 - w0);
|
||||
if (fabsf(w) > 1e-6f) {
|
||||
u /= w;
|
||||
v /= w;
|
||||
}
|
||||
i32 span = xe - xs;
|
||||
if (span <= 0) {
|
||||
if (xs >= 0 && xs < gfx->framebuffer_width) {
|
||||
i32 idx = y * gfx->zbuffer_width + xs;
|
||||
if (z0 <= gfx->zbuffer[idx]) {
|
||||
gfx->frame_pixel_count++;
|
||||
f32 u = u0, v = v0;
|
||||
if (!gfx->affine_textures && fabsf(w0) > 1e-6f) {
|
||||
u /= w0;
|
||||
v /= w0;
|
||||
}
|
||||
|
||||
u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
|
||||
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
||||
if (color & 0xFF000000) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
pxl8_pixel(gfx, x, y, color);
|
||||
}
|
||||
} else {
|
||||
if (color != 0) {
|
||||
if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) ||
|
||||
(gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) {
|
||||
gfx->zbuffer[idx] = z0;
|
||||
pxl8_pixel(gfx, xs, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
f32 inv_span = 1.0f / (f32)span;
|
||||
f32 dz = (z1 - z0) * inv_span;
|
||||
f32 du = (u1 - u0) * inv_span;
|
||||
f32 dv = (v1 - v0) * inv_span;
|
||||
f32 dw = (w1 - w0) * inv_span;
|
||||
|
||||
f32 z = z0;
|
||||
f32 u = u0;
|
||||
f32 v = v0;
|
||||
f32 w = w0;
|
||||
|
||||
if (gfx->affine_textures) {
|
||||
for (i32 x = xs; x <= xe; x++) {
|
||||
if (x >= 0 && x < gfx->framebuffer_width) {
|
||||
i32 idx = y * gfx->zbuffer_width + x;
|
||||
if (z <= gfx->zbuffer[idx]) {
|
||||
gfx->frame_pixel_count++;
|
||||
u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
|
||||
if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) ||
|
||||
(gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
pxl8_pixel(gfx, x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
z += dz;
|
||||
u += du;
|
||||
v += dv;
|
||||
}
|
||||
} else {
|
||||
i32 x = xs;
|
||||
while (x <= xe) {
|
||||
f32 w_inv = (fabsf(w) > 1e-6f) ? (1.0f / w) : 0.0f;
|
||||
f32 u_corrected = u * w_inv;
|
||||
f32 v_corrected = v * w_inv;
|
||||
|
||||
i32 span_end = (x + 16 <= xe) ? (x + 16) : (xe + 1);
|
||||
i32 span_len = span_end - x;
|
||||
|
||||
f32 next_u, next_v, affine_du, affine_dv;
|
||||
if (span_len > 1) {
|
||||
f32 next_w = w + span_len * dw;
|
||||
f32 next_w_inv = (fabsf(next_w) > 1e-6f) ? (1.0f / next_w) : 0.0f;
|
||||
next_u = (u + span_len * du) * next_w_inv;
|
||||
next_v = (v + span_len * dv) * next_w_inv;
|
||||
f32 inv_span = 1.0f / (f32)span_len;
|
||||
affine_du = (next_u - u_corrected) * inv_span;
|
||||
affine_dv = (next_v - v_corrected) * inv_span;
|
||||
} else {
|
||||
affine_du = 0;
|
||||
affine_dv = 0;
|
||||
}
|
||||
|
||||
f32 affine_u = u_corrected;
|
||||
f32 affine_v = v_corrected;
|
||||
|
||||
for (; x < span_end; x++) {
|
||||
if (x >= 0 && x < gfx->framebuffer_width) {
|
||||
i32 idx = y * gfx->zbuffer_width + x;
|
||||
if (z <= gfx->zbuffer[idx]) {
|
||||
gfx->frame_pixel_count++;
|
||||
u32 color = pxl8_sample_texture(gfx, texture_id, affine_u, affine_v);
|
||||
if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) ||
|
||||
(gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
pxl8_pixel(gfx, x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
z += dz;
|
||||
u += du;
|
||||
v += dv;
|
||||
w += dw;
|
||||
affine_u += affine_du;
|
||||
affine_v += affine_dv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*pxl8_scanline_func)(pxl8_gfx*, i32, i32, i32, f32, f32, u32);
|
||||
|
||||
static void pxl8_draw_flat_bottom_triangle(
|
||||
pxl8_gfx* gfx,
|
||||
i32 x0, i32 y0, f32 z0,
|
||||
i32 x1, i32 y1, f32 z1,
|
||||
i32 x2, i32 y2, f32 z2,
|
||||
u32 color
|
||||
u32 color,
|
||||
pxl8_scanline_func fill_scanline
|
||||
) {
|
||||
(void)z2;
|
||||
if (y1 == y0) return;
|
||||
|
|
@ -979,7 +1172,7 @@ static void pxl8_draw_flat_bottom_triangle(
|
|||
f32 cur_x2 = (f32)x0;
|
||||
|
||||
for (i32 y = y0; y <= y1; y++) {
|
||||
pxl8_fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color);
|
||||
fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color);
|
||||
cur_x1 += inv_slope_1;
|
||||
cur_x2 += inv_slope_2;
|
||||
}
|
||||
|
|
@ -990,7 +1183,8 @@ static void pxl8_draw_flat_top_triangle(
|
|||
i32 x0, i32 y0, f32 z0,
|
||||
i32 x1, i32 y1, f32 z1,
|
||||
i32 x2, i32 y2, f32 z2,
|
||||
u32 color
|
||||
u32 color,
|
||||
pxl8_scanline_func fill_scanline
|
||||
) {
|
||||
(void)z2;
|
||||
if (y2 == y0) return;
|
||||
|
|
@ -1002,7 +1196,7 @@ static void pxl8_draw_flat_top_triangle(
|
|||
f32 cur_x2 = (f32)x2;
|
||||
|
||||
for (i32 y = y2; y > y0; y--) {
|
||||
pxl8_fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color);
|
||||
fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color);
|
||||
cur_x1 -= inv_slope_1;
|
||||
cur_x2 -= inv_slope_2;
|
||||
}
|
||||
|
|
@ -1022,6 +1216,8 @@ void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_v
|
|||
void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
|
||||
if (!gfx || !pxl8_3d_init_zbuffer(gfx)) return;
|
||||
|
||||
gfx->frame_triangle_count++;
|
||||
|
||||
pxl8_vec4 v0 = pxl8_transform_vertex(gfx, tri.v[0].position);
|
||||
pxl8_vec4 v1 = pxl8_transform_vertex(gfx, tri.v[1].position);
|
||||
pxl8_vec4 v2 = pxl8_transform_vertex(gfx, tri.v[2].position);
|
||||
|
|
@ -1065,17 +1261,21 @@ void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
|
|||
|
||||
u32 color = tri.v[0].color;
|
||||
|
||||
pxl8_scanline_func fill_scanline = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR)
|
||||
? pxl8_fill_scanline_hicolor
|
||||
: pxl8_fill_scanline_indexed;
|
||||
|
||||
if (y1 == y2) {
|
||||
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color);
|
||||
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline);
|
||||
} else if (y0 == y1) {
|
||||
pxl8_draw_flat_top_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color);
|
||||
pxl8_draw_flat_top_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline);
|
||||
} else {
|
||||
i32 x3 = x0 + (i32)(((f32)(y1 - y0) / (f32)(y2 - y0)) * (x2 - x0));
|
||||
i32 y3 = y1;
|
||||
f32 z3 = z0 + ((f32)(y1 - y0) / (f32)(y2 - y0)) * (z2 - z0);
|
||||
|
||||
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, color);
|
||||
pxl8_draw_flat_top_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color);
|
||||
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, color, fill_scanline);
|
||||
pxl8_draw_flat_top_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color, fill_scanline);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue