improve 3d renderer
This commit is contained in:
parent
f19b06d705
commit
59be43d80e
17 changed files with 556 additions and 486 deletions
614
src/pxl8_gfx.c
614
src/pxl8_gfx.c
|
|
@ -10,6 +10,7 @@
|
|||
#include "pxl8_hal.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_sys.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_sprite_cache_entry {
|
||||
|
|
@ -52,8 +53,6 @@ struct pxl8_gfx {
|
|||
|
||||
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) {
|
||||
|
|
@ -71,38 +70,6 @@ static inline u8 pxl8_color_lerp_channel(u8 c1, u8 c2, f32 t) {
|
|||
return c1 + (i32)((c2 - c1) * t);
|
||||
}
|
||||
|
||||
static u32 pxl8_get_palette_size(pxl8_color_mode mode) {
|
||||
switch (mode) {
|
||||
case PXL8_COLOR_MODE_HICOLOR: return 0;
|
||||
case PXL8_COLOR_MODE_FAMI: return 64;
|
||||
case PXL8_COLOR_MODE_MEGA: return 512;
|
||||
case PXL8_COLOR_MODE_GBA: return 32768;
|
||||
case PXL8_COLOR_MODE_SNES: return 32768;
|
||||
default: return 256;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height) {
|
||||
switch (resolution) {
|
||||
case PXL8_RESOLUTION_240x160:
|
||||
*width = 240; *height = 160; break;
|
||||
case PXL8_RESOLUTION_320x180:
|
||||
*width = 320; *height = 180; break;
|
||||
case PXL8_RESOLUTION_320x240:
|
||||
*width = 320; *height = 240; break;
|
||||
case PXL8_RESOLUTION_640x360:
|
||||
*width = 640; *height = 360; break;
|
||||
case PXL8_RESOLUTION_640x480:
|
||||
*width = 640; *height = 480; break;
|
||||
case PXL8_RESOLUTION_800x600:
|
||||
*width = 800; *height = 600; break;
|
||||
case PXL8_RESOLUTION_960x540:
|
||||
*width = 960; *height = 540; break;
|
||||
default:
|
||||
*width = 640; *height = 360; break;
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
||||
pxl8_bounds bounds = {0};
|
||||
if (!gfx) {
|
||||
|
|
@ -149,11 +116,9 @@ pxl8_gfx* pxl8_gfx_create(
|
|||
gfx->platform_data = platform_data;
|
||||
|
||||
gfx->color_mode = mode;
|
||||
pxl8_gfx_get_resolution_dimensions(
|
||||
resolution,
|
||||
&gfx->framebuffer_width,
|
||||
&gfx->framebuffer_height
|
||||
);
|
||||
pxl8_size size = pxl8_get_resolution_dimensions(resolution);
|
||||
gfx->framebuffer_width = size.w;
|
||||
gfx->framebuffer_height = size.h;
|
||||
|
||||
if (!gfx->platform_data) {
|
||||
pxl8_error("Platform data cannot be NULL");
|
||||
|
|
@ -404,18 +369,6 @@ void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom) {
|
|||
void pxl8_clear(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_trace("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;
|
||||
|
|
@ -903,7 +856,7 @@ 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_hicolor(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) {
|
||||
static inline void pxl8_fill_scanline(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;
|
||||
|
|
@ -915,109 +868,57 @@ static inline void pxl8_fill_scanline_hicolor(pxl8_gfx* gfx, i32 y, i32 xs, i32
|
|||
if (xs > xe) return;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32 pxl8_sample_texture(pxl8_gfx* gfx, u32 texture_id, f32 u, f32 v) {
|
||||
if (!gfx->atlas) return 0;
|
||||
|
||||
const pxl8_atlas_entry* entry = pxl8_atlas_get_entry(gfx->atlas, texture_id);
|
||||
if (!entry || !entry->active) {
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
pxl8_warn("Texture sampling failed: texture_id=%u entry=%p active=%d",
|
||||
texture_id, (void*)entry, entry ? entry->active : 0);
|
||||
warned = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
i32 tex_u = (i32)(u * entry->w);
|
||||
i32 tex_v = (i32)(v * entry->h);
|
||||
|
||||
i32 tx = tex_u % entry->w;
|
||||
if (tx < 0) tx += entry->w;
|
||||
i32 ty = tex_v % entry->h;
|
||||
if (ty < 0) ty += entry->h;
|
||||
|
||||
i32 atlas_x = entry->x + tx;
|
||||
i32 atlas_y = entry->y + ty;
|
||||
u32 atlas_width = pxl8_atlas_get_width(gfx->atlas);
|
||||
const u8* atlas_pixels = pxl8_atlas_get_pixels(gfx->atlas);
|
||||
i32 idx = atlas_y * atlas_width + atlas_x;
|
||||
|
||||
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
||||
return ((const u32*)atlas_pixels)[idx];
|
||||
u32* fb = (u32*)gfx->framebuffer;
|
||||
if (width == 0) {
|
||||
i32 idx = zbuf_offset + xs;
|
||||
if (z0 <= gfx->zbuffer[idx]) {
|
||||
gfx->zbuffer[idx] = z0;
|
||||
fb[fb_offset + xs] = color;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
f32 dz = (z1 - z0) / (f32)width;
|
||||
f32 z = z0;
|
||||
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
return atlas_pixels[idx];
|
||||
u8 idx_color = color & 0xFF;
|
||||
if (width == 0) {
|
||||
i32 idx = zbuf_offset + xs;
|
||||
if (z0 <= gfx->zbuffer[idx]) {
|
||||
gfx->zbuffer[idx] = z0;
|
||||
gfx->framebuffer[fb_offset + xs] = idx_color;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
f32 dz = (z1 - z0) / (f32)width;
|
||||
f32 z = z0;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void pxl8_fill_scanline_textured_hicolor(
|
||||
static inline void pxl8_fill_scanline_textured(
|
||||
pxl8_gfx* gfx, i32 y, i32 xs, i32 xe,
|
||||
f32 z0, f32 z1,
|
||||
f32 u0, f32 v0, f32 w0,
|
||||
|
|
@ -1034,22 +935,58 @@ static inline void pxl8_fill_scanline_textured_hicolor(
|
|||
tmpf = w0; w0 = w1; w1 = tmpf;
|
||||
}
|
||||
|
||||
const pxl8_atlas_entry* entry = pxl8_atlas_get_entry(gfx->atlas, texture_id);
|
||||
if (!entry || !entry->active) return;
|
||||
|
||||
const u8* atlas_pixels = pxl8_atlas_get_pixels(gfx->atlas);
|
||||
u32 atlas_width = pxl8_atlas_get_width(gfx->atlas);
|
||||
i32 tex_w = entry->w;
|
||||
i32 tex_h = entry->h;
|
||||
bool is_pow2 = ((tex_w & (tex_w - 1)) == 0) && ((tex_h & (tex_h - 1)) == 0) && (tex_w == tex_h);
|
||||
i32 tex_mask = tex_w - 1;
|
||||
i32 atlas_x_base = entry->x;
|
||||
i32 atlas_y_base = entry->y;
|
||||
bool is_hicolor = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR);
|
||||
bool affine = gfx->affine_textures;
|
||||
|
||||
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) {
|
||||
f32 tex_u = u0, tex_v = v0;
|
||||
if (!affine && fabsf(w0) > 1e-6f) {
|
||||
f32 perspective_w = 1.0f / w0;
|
||||
u *= perspective_w;
|
||||
v *= perspective_w;
|
||||
tex_u *= perspective_w;
|
||||
tex_v *= perspective_w;
|
||||
}
|
||||
u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
|
||||
if (color & 0xFF000000) {
|
||||
gfx->zbuffer[idx] = z0;
|
||||
pxl8_pixel(gfx, xs, y, color);
|
||||
|
||||
i32 tu = (i32)(tex_u * tex_w);
|
||||
i32 tv = (i32)(tex_v * tex_h);
|
||||
i32 tx, ty;
|
||||
if (is_pow2) {
|
||||
tx = tu & tex_mask;
|
||||
ty = tv & tex_mask;
|
||||
} else {
|
||||
tx = tu % tex_w;
|
||||
if (tx < 0) tx += tex_w;
|
||||
ty = tv % tex_h;
|
||||
if (ty < 0) ty += tex_h;
|
||||
}
|
||||
|
||||
i32 atlas_idx = (atlas_y_base + ty) * atlas_width + (atlas_x_base + tx);
|
||||
u32 color = is_hicolor ? ((const u32*)atlas_pixels)[atlas_idx] : atlas_pixels[atlas_idx];
|
||||
|
||||
if (is_hicolor) {
|
||||
if (color & 0xFF000000) {
|
||||
gfx->zbuffer[idx] = z0;
|
||||
((u32*)gfx->framebuffer)[y * gfx->framebuffer_width + xs] = color;
|
||||
}
|
||||
} else {
|
||||
if (color != 0) {
|
||||
gfx->zbuffer[idx] = z0;
|
||||
gfx->framebuffer[y * gfx->framebuffer_width + xs] = (u8)color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1071,92 +1008,40 @@ static inline void pxl8_fill_scanline_textured_hicolor(
|
|||
if (x >= 0 && x < gfx->framebuffer_width) {
|
||||
i32 idx = y * gfx->zbuffer_width + x;
|
||||
if (z <= gfx->zbuffer[idx]) {
|
||||
gfx->frame_pixel_count++;
|
||||
|
||||
f32 tex_u = u, tex_v = v;
|
||||
if (!gfx->affine_textures && fabsf(w) > 1e-6f) {
|
||||
if (!affine && fabsf(w) > 1e-6f) {
|
||||
f32 perspective_w = 1.0f / w;
|
||||
tex_u *= perspective_w;
|
||||
tex_v *= perspective_w;
|
||||
}
|
||||
u32 color = pxl8_sample_texture(gfx, texture_id, tex_u, tex_v);
|
||||
if (color & 0xFF000000) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
pxl8_pixel(gfx, x, y, color);
|
||||
|
||||
i32 tu = (i32)(tex_u * tex_w);
|
||||
i32 tv = (i32)(tex_v * tex_h);
|
||||
i32 tx, ty;
|
||||
if (is_pow2) {
|
||||
tx = tu & tex_mask;
|
||||
ty = tv & tex_mask;
|
||||
} else {
|
||||
tx = tu % tex_w;
|
||||
if (tx < 0) tx += tex_w;
|
||||
ty = tv % tex_h;
|
||||
if (ty < 0) ty += tex_h;
|
||||
}
|
||||
}
|
||||
}
|
||||
z += dz;
|
||||
u += du;
|
||||
v += dv;
|
||||
w += dw;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void pxl8_fill_scanline_textured_indexed(
|
||||
pxl8_gfx* gfx, i32 y, i32 xs, i32 xe,
|
||||
f32 z0, f32 z1,
|
||||
f32 u0, f32 v0, f32 w0,
|
||||
f32 u1, f32 v1, f32 w1,
|
||||
u32 texture_id
|
||||
) {
|
||||
if (y < 0 || y >= gfx->framebuffer_height) return;
|
||||
if (xs > xe) {
|
||||
i32 tmp = xs; xs = xe; xe = tmp;
|
||||
f32 tmpf;
|
||||
tmpf = z0; z0 = z1; z1 = tmpf;
|
||||
tmpf = u0; u0 = u1; u1 = tmpf;
|
||||
tmpf = v0; v0 = v1; v1 = tmpf;
|
||||
tmpf = w0; w0 = w1; w1 = tmpf;
|
||||
}
|
||||
i32 atlas_idx = (atlas_y_base + ty) * atlas_width + (atlas_x_base + tx);
|
||||
u32 color = is_hicolor ? ((const u32*)atlas_pixels)[atlas_idx] : atlas_pixels[atlas_idx];
|
||||
|
||||
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) {
|
||||
f32 perspective_w = 1.0f / w0;
|
||||
u *= perspective_w;
|
||||
v *= perspective_w;
|
||||
}
|
||||
u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
|
||||
if (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;
|
||||
|
||||
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++;
|
||||
f32 tex_u = u, tex_v = v;
|
||||
if (!gfx->affine_textures && fabsf(w) > 1e-6f) {
|
||||
f32 perspective_w = 1.0f / w;
|
||||
tex_u *= perspective_w;
|
||||
tex_v *= perspective_w;
|
||||
}
|
||||
u32 color = pxl8_sample_texture(gfx, texture_id, tex_u, tex_v);
|
||||
if (color != 0) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
pxl8_pixel(gfx, x, y, color);
|
||||
if (is_hicolor) {
|
||||
if (color & 0xFF000000) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
((u32*)gfx->framebuffer)[y * gfx->framebuffer_width + x] = color;
|
||||
}
|
||||
} else {
|
||||
if (color != 0) {
|
||||
gfx->zbuffer[idx] = z;
|
||||
gfx->framebuffer[y * gfx->framebuffer_width + x] = (u8)color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1228,19 +1113,21 @@ 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);
|
||||
|
||||
if (v0.w <= 0 || v1.w <= 0 || v2.w <= 0) return;
|
||||
pxl8_vec4 tv0 = v0;
|
||||
pxl8_vec4 tv1 = v1;
|
||||
pxl8_vec4 tv2 = v2;
|
||||
u32 tc0 = tri.v[0].color;
|
||||
|
||||
i32 x0, y0, x1, y1, x2, y2;
|
||||
f32 z0, z1, z2;
|
||||
pxl8_project_to_screen(gfx, v0, &x0, &y0, &z0);
|
||||
pxl8_project_to_screen(gfx, v1, &x1, &y1, &z1);
|
||||
pxl8_project_to_screen(gfx, v2, &x2, &y2, &z2);
|
||||
pxl8_project_to_screen(gfx, tv0, &x0, &y0, &z0);
|
||||
pxl8_project_to_screen(gfx, tv1, &x1, &y1, &z1);
|
||||
pxl8_project_to_screen(gfx, tv2, &x2, &y2, &z2);
|
||||
|
||||
if (gfx->backface_culling) {
|
||||
i32 cross = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
|
||||
|
|
@ -1248,10 +1135,9 @@ void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
|
|||
}
|
||||
|
||||
if (gfx->wireframe) {
|
||||
u32 color = tri.v[0].color;
|
||||
pxl8_line(gfx, x0, y0, x1, y1, color);
|
||||
pxl8_line(gfx, x1, y1, x2, y2, color);
|
||||
pxl8_line(gfx, x2, y2, x0, y0, color);
|
||||
pxl8_line(gfx, x0, y0, x1, y1, tc0);
|
||||
pxl8_line(gfx, x1, y1, x2, y2, tc0);
|
||||
pxl8_line(gfx, x2, y2, x0, y0, tc0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1271,23 +1157,19 @@ void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
|
|||
f32 tmp_f = z1; z1 = z2; z2 = tmp_f;
|
||||
}
|
||||
|
||||
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;
|
||||
pxl8_scanline_func fill_scanline = pxl8_fill_scanline;
|
||||
|
||||
if (y1 == y2) {
|
||||
pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline, true);
|
||||
pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, tc0, fill_scanline, true);
|
||||
} else if (y0 == y1) {
|
||||
pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline, false);
|
||||
pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, tc0, fill_scanline, false);
|
||||
} 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_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, color, fill_scanline, true);
|
||||
pxl8_scan_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color, fill_scanline, false);
|
||||
pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, tc0, fill_scanline, true);
|
||||
pxl8_scan_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, tc0, fill_scanline, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1312,7 +1194,7 @@ static void pxl8_scan_triangle_textured(
|
|||
f32 inv_slope_u1, inv_slope_u2, inv_slope_v1, inv_slope_v2, inv_slope_w1, inv_slope_w2;
|
||||
|
||||
if (is_bottom) {
|
||||
if (v1.y == v0.y || v1.y < v0.y || v1.y - v0.y > gfx->framebuffer_height) return;
|
||||
if (v1.y == v0.y || v1.y < v0.y) return;
|
||||
y_start = v0.y;
|
||||
y_end = v1.y;
|
||||
y_step = 1;
|
||||
|
|
@ -1332,7 +1214,7 @@ static void pxl8_scan_triangle_textured(
|
|||
inv_slope_w1 = (v1.w - v0.w) / (f32)(v1.y - v0.y);
|
||||
inv_slope_w2 = (v2.w - v0.w) / (f32)(v2.y - v0.y);
|
||||
} else {
|
||||
if (v2.y == v0.y || v2.y < v0.y || v2.y - v0.y > gfx->framebuffer_height) return;
|
||||
if (v2.y == v0.y || v2.y < v0.y) return;
|
||||
y_start = v2.y;
|
||||
y_end = v0.y;
|
||||
y_step = -1;
|
||||
|
|
@ -1381,6 +1263,83 @@ static void pxl8_scan_triangle_textured(
|
|||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
pxl8_vec4 clip;
|
||||
f32 u, v;
|
||||
} pxl8_clip_vert;
|
||||
|
||||
typedef struct {
|
||||
i32 x, y;
|
||||
f32 z, u, v, w;
|
||||
} pxl8_screen_vert;
|
||||
|
||||
static void pxl8_clip_screen_axis(pxl8_screen_vert* in, i32 in_count, pxl8_screen_vert* out, i32* out_count, i32 plane_pos, bool is_x, bool is_min) {
|
||||
*out_count = 0;
|
||||
|
||||
for (i32 i = 0; i < in_count; i++) {
|
||||
pxl8_screen_vert curr = in[i];
|
||||
pxl8_screen_vert next = in[(i + 1) % in_count];
|
||||
|
||||
i32 curr_val = is_x ? curr.x : curr.y;
|
||||
i32 next_val = is_x ? next.x : next.y;
|
||||
|
||||
bool curr_inside = is_min ? (curr_val >= plane_pos) : (curr_val <= plane_pos);
|
||||
bool next_inside = is_min ? (next_val >= plane_pos) : (next_val <= plane_pos);
|
||||
|
||||
if (curr_inside) {
|
||||
out[(*out_count)++] = curr;
|
||||
}
|
||||
|
||||
if (curr_inside != next_inside) {
|
||||
i32 denom = next_val - curr_val;
|
||||
if (denom != 0) {
|
||||
f32 t = (f32)(plane_pos - curr_val) / (f32)denom;
|
||||
out[*out_count] = (pxl8_screen_vert){
|
||||
.x = curr.x + (i32)(t * (next.x - curr.x)),
|
||||
.y = curr.y + (i32)(t * (next.y - curr.y)),
|
||||
.z = curr.z + t * (next.z - curr.z),
|
||||
.u = curr.u + t * (next.u - curr.u),
|
||||
.v = curr.v + t * (next.v - curr.v),
|
||||
.w = curr.w + t * (next.w - curr.w)
|
||||
};
|
||||
(*out_count)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pxl8_clip_near_plane(pxl8_clip_vert* in, i32 in_count, pxl8_clip_vert* out, i32* out_count, f32 near) {
|
||||
*out_count = 0;
|
||||
|
||||
for (i32 i = 0; i < in_count; i++) {
|
||||
pxl8_clip_vert curr = in[i];
|
||||
pxl8_clip_vert next = in[(i + 1) % in_count];
|
||||
|
||||
bool curr_inside = curr.clip.w >= near;
|
||||
bool next_inside = next.clip.w >= near;
|
||||
|
||||
if (curr_inside) {
|
||||
out[(*out_count)++] = curr;
|
||||
}
|
||||
|
||||
if (curr_inside != next_inside) {
|
||||
f32 t = (near - curr.clip.w) / (next.clip.w - curr.clip.w);
|
||||
|
||||
out[*out_count] = (pxl8_clip_vert){
|
||||
.clip = {
|
||||
curr.clip.x + t * (next.clip.x - curr.clip.x),
|
||||
curr.clip.y + t * (next.clip.y - curr.clip.y),
|
||||
curr.clip.z + t * (next.clip.z - curr.clip.z),
|
||||
near
|
||||
},
|
||||
.u = curr.u + t * (next.u - curr.u),
|
||||
.v = curr.v + t * (next.v - curr.v)
|
||||
};
|
||||
(*out_count)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_draw_triangle_textured(
|
||||
pxl8_gfx* gfx,
|
||||
pxl8_vec3 v0,
|
||||
|
|
@ -1396,67 +1355,114 @@ void pxl8_3d_draw_triangle_textured(
|
|||
) {
|
||||
if (!gfx || !pxl8_3d_init_zbuffer(gfx)) return;
|
||||
|
||||
|
||||
pxl8_vec4 cv0 = pxl8_transform_vertex(gfx, v0);
|
||||
pxl8_vec4 cv1 = pxl8_transform_vertex(gfx, v1);
|
||||
pxl8_vec4 cv2 = pxl8_transform_vertex(gfx, v2);
|
||||
|
||||
if (cv0.w <= 0 || cv1.w <= 0 || cv2.w <= 0) return;
|
||||
pxl8_clip_vert input[3] = {
|
||||
{cv0, u0, v0f},
|
||||
{cv1, u1, v1f},
|
||||
{cv2, u2, v2f}
|
||||
};
|
||||
|
||||
pxl8_textured_vertex tv[3];
|
||||
f32 z_tmp;
|
||||
pxl8_project_to_screen(gfx, cv0, &tv[0].x, &tv[0].y, &z_tmp);
|
||||
tv[0].z = z_tmp;
|
||||
tv[0].u = gfx->affine_textures ? u0 : u0 / cv0.w;
|
||||
tv[0].v = gfx->affine_textures ? v0f : v0f / cv0.w;
|
||||
tv[0].w = gfx->affine_textures ? 1.0f : 1.0f / cv0.w;
|
||||
pxl8_clip_vert clipped[8];
|
||||
i32 clipped_count = 0;
|
||||
|
||||
pxl8_project_to_screen(gfx, cv1, &tv[1].x, &tv[1].y, &z_tmp);
|
||||
tv[1].z = z_tmp;
|
||||
tv[1].u = gfx->affine_textures ? u1 : u1 / cv1.w;
|
||||
tv[1].v = gfx->affine_textures ? v1f : v1f / cv1.w;
|
||||
tv[1].w = gfx->affine_textures ? 1.0f : 1.0f / cv1.w;
|
||||
f32 near_clip = 0.1f;
|
||||
pxl8_clip_near_plane(input, 3, clipped, &clipped_count, near_clip);
|
||||
|
||||
pxl8_project_to_screen(gfx, cv2, &tv[2].x, &tv[2].y, &z_tmp);
|
||||
tv[2].z = z_tmp;
|
||||
tv[2].u = gfx->affine_textures ? u2 : u2 / cv2.w;
|
||||
tv[2].v = gfx->affine_textures ? v2f : v2f / cv2.w;
|
||||
tv[2].w = gfx->affine_textures ? 1.0f : 1.0f / cv2.w;
|
||||
if (clipped_count < 3) return;
|
||||
|
||||
if (gfx->backface_culling) {
|
||||
i32 cross =
|
||||
(tv[1].x - tv[0].x) * (tv[2].y - tv[0].y) - (tv[1].y - tv[0].y) * (tv[2].x - tv[0].x);
|
||||
if (cross >= 0) return;
|
||||
}
|
||||
i32 guard_band = (gfx->framebuffer_width > gfx->framebuffer_height ? gfx->framebuffer_width : gfx->framebuffer_height) * 2;
|
||||
|
||||
if (tv[0].y > tv[1].y) {
|
||||
pxl8_textured_vertex tmp = tv[0]; tv[0] = tv[1]; tv[1] = tmp;
|
||||
}
|
||||
if (tv[0].y > tv[2].y) {
|
||||
pxl8_textured_vertex tmp = tv[0]; tv[0] = tv[2]; tv[2] = tmp;
|
||||
}
|
||||
if (tv[1].y > tv[2].y) {
|
||||
pxl8_textured_vertex tmp = tv[1]; tv[1] = tv[2]; tv[2] = tmp;
|
||||
}
|
||||
for (i32 tri = 0; tri < clipped_count - 2; tri++) {
|
||||
pxl8_vec4 c0 = clipped[0].clip;
|
||||
pxl8_vec4 c1 = clipped[tri + 1].clip;
|
||||
pxl8_vec4 c2 = clipped[tri + 2].clip;
|
||||
f32 tu0 = clipped[0].u;
|
||||
f32 tv0 = clipped[0].v;
|
||||
f32 tu1 = clipped[tri + 1].u;
|
||||
f32 tv1 = clipped[tri + 1].v;
|
||||
f32 tu2 = clipped[tri + 2].u;
|
||||
f32 tv2 = clipped[tri + 2].v;
|
||||
|
||||
pxl8_scanline_textured_func fill_scanline = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR)
|
||||
? pxl8_fill_scanline_textured_hicolor
|
||||
: pxl8_fill_scanline_textured_indexed;
|
||||
i32 sx0, sy0, sx1, sy1, sx2, sy2;
|
||||
f32 z0, z1, z2;
|
||||
pxl8_project_to_screen(gfx, c0, &sx0, &sy0, &z0);
|
||||
pxl8_project_to_screen(gfx, c1, &sx1, &sy1, &z1);
|
||||
pxl8_project_to_screen(gfx, c2, &sx2, &sy2, &z2);
|
||||
|
||||
if (tv[1].y == tv[2].y) {
|
||||
pxl8_scan_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id, fill_scanline, true);
|
||||
} else if (tv[0].y == tv[1].y) {
|
||||
pxl8_scan_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id, fill_scanline, false);
|
||||
} else {
|
||||
f32 t = (f32)(tv[1].y - tv[0].y) / (f32)(tv[2].y - tv[0].y);
|
||||
pxl8_textured_vertex v3;
|
||||
v3.x = tv[0].x + (i32)(t * (tv[2].x - tv[0].x));
|
||||
v3.y = tv[1].y;
|
||||
v3.z = tv[0].z + t * (tv[2].z - tv[0].z);
|
||||
v3.u = tv[0].u + t * (tv[2].u - tv[0].u);
|
||||
v3.v = tv[0].v + t * (tv[2].v - tv[0].v);
|
||||
v3.w = tv[0].w + t * (tv[2].w - tv[0].w);
|
||||
f32 w0_recip = 1.0f / c0.w;
|
||||
f32 w1_recip = 1.0f / c1.w;
|
||||
f32 w2_recip = 1.0f / c2.w;
|
||||
|
||||
pxl8_scan_triangle_textured(gfx, tv[0], tv[1], v3, texture_id, fill_scanline, true);
|
||||
pxl8_scan_triangle_textured(gfx, tv[1], v3, tv[2], texture_id, fill_scanline, false);
|
||||
pxl8_screen_vert screen_tri[3] = {
|
||||
{sx0, sy0, z0, tu0 * w0_recip, tv0 * w0_recip, w0_recip},
|
||||
{sx1, sy1, z1, tu1 * w1_recip, tv1 * w1_recip, w1_recip},
|
||||
{sx2, sy2, z2, tu2 * w2_recip, tv2 * w2_recip, w2_recip}
|
||||
};
|
||||
|
||||
pxl8_screen_vert temp1[8], temp2[8];
|
||||
i32 count1, count2;
|
||||
|
||||
pxl8_clip_screen_axis(screen_tri, 3, temp1, &count1, -guard_band, true, true);
|
||||
if (count1 < 3) continue;
|
||||
pxl8_clip_screen_axis(temp1, count1, temp2, &count2, guard_band, true, false);
|
||||
if (count2 < 3) continue;
|
||||
pxl8_clip_screen_axis(temp2, count2, temp1, &count1, -guard_band, false, true);
|
||||
if (count1 < 3) continue;
|
||||
pxl8_clip_screen_axis(temp1, count1, temp2, &count2, guard_band, false, false);
|
||||
if (count2 < 3) continue;
|
||||
|
||||
for (i32 i = 1; i < count2 - 1; i++) {
|
||||
pxl8_screen_vert v0 = temp2[0];
|
||||
pxl8_screen_vert v1 = temp2[i];
|
||||
pxl8_screen_vert v2 = temp2[i + 1];
|
||||
|
||||
pxl8_textured_vertex tv[3];
|
||||
tv[0].x = v0.x; tv[0].y = v0.y; tv[0].z = v0.z;
|
||||
tv[0].u = v0.u; tv[0].v = v0.v; tv[0].w = v0.w;
|
||||
tv[1].x = v1.x; tv[1].y = v1.y; tv[1].z = v1.z;
|
||||
tv[1].u = v1.u; tv[1].v = v1.v; tv[1].w = v1.w;
|
||||
tv[2].x = v2.x; tv[2].y = v2.y; tv[2].z = v2.z;
|
||||
tv[2].u = v2.u; tv[2].v = v2.v; tv[2].w = v2.w;
|
||||
|
||||
if (gfx->backface_culling) {
|
||||
i32 cross =
|
||||
(tv[1].x - tv[0].x) * (tv[2].y - tv[0].y) - (tv[1].y - tv[0].y) * (tv[2].x - tv[0].x);
|
||||
if (cross >= 0) continue;
|
||||
}
|
||||
|
||||
if (tv[0].y > tv[1].y) {
|
||||
pxl8_textured_vertex tmp = tv[0]; tv[0] = tv[1]; tv[1] = tmp;
|
||||
}
|
||||
if (tv[0].y > tv[2].y) {
|
||||
pxl8_textured_vertex tmp = tv[0]; tv[0] = tv[2]; tv[2] = tmp;
|
||||
}
|
||||
if (tv[1].y > tv[2].y) {
|
||||
pxl8_textured_vertex tmp = tv[1]; tv[1] = tv[2]; tv[2] = tmp;
|
||||
}
|
||||
|
||||
pxl8_scanline_textured_func fill_scanline = pxl8_fill_scanline_textured;
|
||||
|
||||
if (tv[1].y == tv[2].y) {
|
||||
pxl8_scan_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id, fill_scanline, true);
|
||||
} else if (tv[0].y == tv[1].y) {
|
||||
pxl8_scan_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id, fill_scanline, false);
|
||||
} else {
|
||||
f32 t = (f32)(tv[1].y - tv[0].y) / (f32)(tv[2].y - tv[0].y);
|
||||
pxl8_textured_vertex v3;
|
||||
v3.x = tv[0].x + (i32)(t * (tv[2].x - tv[0].x));
|
||||
v3.y = tv[1].y;
|
||||
v3.z = tv[0].z + t * (tv[2].z - tv[0].z);
|
||||
v3.u = tv[0].u + t * (tv[2].u - tv[0].u);
|
||||
v3.v = tv[0].v + t * (tv[2].v - tv[0].v);
|
||||
v3.w = tv[0].w + t * (tv[2].w - tv[0].w);
|
||||
|
||||
pxl8_scan_triangle_textured(gfx, tv[0], tv[1], v3, texture_id, fill_scanline, true);
|
||||
pxl8_scan_triangle_textured(gfx, tv[1], v3, tv[2], texture_id, fill_scanline, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue