refactor atlas implementation
This commit is contained in:
parent
6008ebf5ed
commit
2d1ae9578b
12 changed files with 825 additions and 261 deletions
|
|
@ -8,6 +8,17 @@
|
||||||
(var wireframe true)
|
(var wireframe true)
|
||||||
(var time 0)
|
(var time 0)
|
||||||
(var zoom 5.0)
|
(var zoom 5.0)
|
||||||
|
(var texture-id nil)
|
||||||
|
(var use-texture false)
|
||||||
|
(var affine false)
|
||||||
|
(var texture-initialized false)
|
||||||
|
|
||||||
|
(fn init-texture []
|
||||||
|
(when (not texture-initialized)
|
||||||
|
(pxl8.load_palette "sprites/pxl8_logo.ase")
|
||||||
|
(set texture-id (pxl8.load_sprite "sprites/pxl8_logo.ase"))
|
||||||
|
(pxl8.upload_atlas)
|
||||||
|
(set texture-initialized true)))
|
||||||
|
|
||||||
(fn make-cube-vertices []
|
(fn make-cube-vertices []
|
||||||
[[-1 -1 -1] [1 -1 -1] [1 1 -1] [-1 1 -1]
|
[[-1 -1 -1] [1 -1 -1] [1 1 -1] [-1 1 -1]
|
||||||
|
|
@ -21,6 +32,20 @@
|
||||||
[3 2 6] [3 6 7]
|
[3 2 6] [3 6 7]
|
||||||
[4 5 1] [4 1 0]])
|
[4 5 1] [4 1 0]])
|
||||||
|
|
||||||
|
(fn make-cube-faces-with-uvs []
|
||||||
|
[{:tri [0 1 2] :uvs [[0 0] [1 0] [1 1]]}
|
||||||
|
{:tri [0 2 3] :uvs [[0 0] [1 1] [0 1]]}
|
||||||
|
{:tri [1 5 6] :uvs [[0 0] [1 0] [1 1]]}
|
||||||
|
{:tri [1 6 2] :uvs [[0 0] [1 1] [0 1]]}
|
||||||
|
{:tri [5 4 7] :uvs [[0 0] [1 0] [1 1]]}
|
||||||
|
{:tri [5 7 6] :uvs [[0 0] [1 1] [0 1]]}
|
||||||
|
{:tri [4 0 3] :uvs [[0 0] [1 0] [1 1]]}
|
||||||
|
{:tri [4 3 7] :uvs [[0 0] [1 1] [0 1]]}
|
||||||
|
{:tri [3 2 6] :uvs [[0 0] [1 0] [1 1]]}
|
||||||
|
{:tri [3 6 7] :uvs [[0 0] [1 1] [0 1]]}
|
||||||
|
{:tri [4 5 1] :uvs [[0 0] [1 0] [1 1]]}
|
||||||
|
{:tri [4 1 0] :uvs [[0 0] [1 1] [0 1]]}])
|
||||||
|
|
||||||
(fn get-face-color [face-idx]
|
(fn get-face-color [face-idx]
|
||||||
(let [colors [12 22 30 16 28 20]]
|
(let [colors [12 22 30 16 28 20]]
|
||||||
(. colors (+ 1 (% face-idx 6)))))
|
(. colors (+ 1 (% face-idx 6)))))
|
||||||
|
|
@ -43,10 +68,16 @@
|
||||||
|
|
||||||
(when (pxl8.key_pressed " ")
|
(when (pxl8.key_pressed " ")
|
||||||
(set wireframe (not wireframe)))
|
(set wireframe (not wireframe)))
|
||||||
(when (pxl8.key_pressed "r")
|
(when (pxl8.key_pressed "f")
|
||||||
(set auto-rotate (not auto-rotate)))
|
(set affine (not affine)))
|
||||||
(when (pxl8.key_pressed "p")
|
(when (pxl8.key_pressed "p")
|
||||||
(set orthographic (not orthographic)))
|
(set orthographic (not orthographic)))
|
||||||
|
(when (pxl8.key_pressed "r")
|
||||||
|
(set auto-rotate (not auto-rotate)))
|
||||||
|
(when (pxl8.key_pressed "t")
|
||||||
|
(set use-texture (not use-texture))
|
||||||
|
(when use-texture
|
||||||
|
(init-texture)))
|
||||||
|
|
||||||
(when (pxl8.key_down "=")
|
(when (pxl8.key_down "=")
|
||||||
(set zoom (- zoom (* dt 2.0))))
|
(set zoom (- zoom (* dt 2.0))))
|
||||||
|
|
@ -63,8 +94,9 @@
|
||||||
(pxl8.clr 0)
|
(pxl8.clr 0)
|
||||||
|
|
||||||
(pxl8.clear_zbuffer)
|
(pxl8.clear_zbuffer)
|
||||||
(pxl8.set_wireframe wireframe)
|
(pxl8.set_affine_textures affine)
|
||||||
(pxl8.set_backface_culling true)
|
(pxl8.set_backface_culling true)
|
||||||
|
(pxl8.set_wireframe wireframe)
|
||||||
|
|
||||||
(if orthographic
|
(if orthographic
|
||||||
(let [size (* 2.5 (/ zoom 5.0))
|
(let [size (* 2.5 (/ zoom 5.0))
|
||||||
|
|
@ -84,15 +116,36 @@
|
||||||
(pxl8.mat4_multiply (pxl8.mat4_rotate_z angle-z)))]
|
(pxl8.mat4_multiply (pxl8.mat4_rotate_z angle-z)))]
|
||||||
(pxl8.set_model model))
|
(pxl8.set_model model))
|
||||||
|
|
||||||
(let [vertices (make-cube-vertices)
|
(let [vertices (make-cube-vertices)]
|
||||||
faces (make-cube-faces)]
|
(if (and use-texture texture-id)
|
||||||
(each [i face (ipairs faces)]
|
(let [faces (make-cube-faces-with-uvs)]
|
||||||
(let [[i0 i1 i2] face
|
(each [i face-data (ipairs faces)]
|
||||||
v0 (. vertices (+ 1 i0))
|
(let [tri-indices face-data.tri
|
||||||
v1 (. vertices (+ 1 i1))
|
tri-uvs face-data.uvs
|
||||||
v2 (. vertices (+ 1 i2))
|
v0 (. vertices (+ 1 (. tri-indices 1)))
|
||||||
color (get-face-color (math.floor (/ (- i 1) 2)))]
|
v1 (. vertices (+ 1 (. tri-indices 2)))
|
||||||
(pxl8.draw_triangle_3d v0 v1 v2 color)))))
|
v2 (. vertices (+ 1 (. tri-indices 3)))
|
||||||
|
uv0 (. tri-uvs 1)
|
||||||
|
uv1 (. tri-uvs 2)
|
||||||
|
uv2 (. tri-uvs 3)]
|
||||||
|
(pxl8.draw_triangle_3d_textured
|
||||||
|
v0 v1 v2
|
||||||
|
uv0 uv1 uv2
|
||||||
|
texture-id))))
|
||||||
|
(let [faces (make-cube-faces)]
|
||||||
|
(each [i face (ipairs faces)]
|
||||||
|
(let [[i0 i1 i2] face
|
||||||
|
v0 (. vertices (+ 1 i0))
|
||||||
|
v1 (. vertices (+ 1 i1))
|
||||||
|
v2 (. vertices (+ 1 i2))
|
||||||
|
color (get-face-color (math.floor (/ (- i 1) 2)))]
|
||||||
|
(pxl8.draw_triangle_3d v0 v1 v2 color))))))
|
||||||
|
|
||||||
|
(pxl8.text "WASD/QE: Rotate | +/-: Zoom | Space: Wire | R: Auto | P: Proj" 5 5 15)
|
||||||
|
(pxl8.text (.. "T: Texture | F: Affine | Mode: "
|
||||||
|
(if wireframe "Wire" "Fill")
|
||||||
|
" | Texture: " (if use-texture "On" "Off")
|
||||||
|
" | Mapping: " (if affine "Affine" "Persp")) 5 15 15))
|
||||||
|
|
||||||
{:update cube-update
|
{:update cube-update
|
||||||
:frame cube-frame}
|
:frame cube-frame}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
(var fire-init false)
|
(var fire-init false)
|
||||||
(var rain-init false)
|
(var rain-init false)
|
||||||
(var snow-init false)
|
(var snow-init false)
|
||||||
|
(var use-nes-palette false)
|
||||||
|
|
||||||
(var logo-x 256)
|
(var logo-x 256)
|
||||||
(var logo-y 148)
|
(var logo-y 148)
|
||||||
|
|
@ -15,7 +16,7 @@
|
||||||
(var logo-sprite nil)
|
(var logo-sprite nil)
|
||||||
|
|
||||||
(global init (fn []
|
(global init (fn []
|
||||||
(pxl8.load_palette "palettes/gruvbox.ase")
|
(pxl8.load_palette "sprites/pxl8_logo.ase")
|
||||||
(set logo-sprite (pxl8.load_sprite "sprites/pxl8_logo.ase"))
|
(set logo-sprite (pxl8.load_sprite "sprites/pxl8_logo.ase"))
|
||||||
(set particles (pxl8.particles_new 1000))))
|
(set particles (pxl8.particles_new 1000))))
|
||||||
|
|
||||||
|
|
@ -41,6 +42,12 @@
|
||||||
(set snow-init false))
|
(set snow-init false))
|
||||||
(when (pxl8.key_pressed "8")
|
(when (pxl8.key_pressed "8")
|
||||||
(set current-effect 8))
|
(set current-effect 8))
|
||||||
|
(when (pxl8.key_pressed "9")
|
||||||
|
(set use-nes-palette (not use-nes-palette))
|
||||||
|
(local palette-path (if use-nes-palette "palettes/nes.ase" "sprites/pxl8_logo.ase"))
|
||||||
|
(print (.. "Switching to palette: " palette-path))
|
||||||
|
(pxl8.load_palette palette-path)
|
||||||
|
(print "Palette loaded"))
|
||||||
|
|
||||||
(case current-effect
|
(case current-effect
|
||||||
1 (do
|
1 (do
|
||||||
|
|
@ -63,15 +70,15 @@
|
||||||
(when logo-sprite
|
(when logo-sprite
|
||||||
(pxl8.sprite logo-sprite logo-x logo-y 128 64)))
|
(pxl8.sprite logo-sprite logo-x logo-y 128 64)))
|
||||||
|
|
||||||
2 (pxl8.vfx_plasma time 0.10 0.04 0)
|
2 (pxl8.vfx_plasma time 0.10 0.04 1)
|
||||||
|
|
||||||
3 (pxl8.vfx_tunnel time 2.0 0.25)
|
3 (pxl8.vfx_tunnel time 2.0 0.25)
|
||||||
|
|
||||||
4 (do
|
4 (do
|
||||||
(pxl8.clr 0)
|
(pxl8.clr 0)
|
||||||
(local bars [{:base_y 60 :amplitude 30 :height 16 :speed 2.0 :phase 0 :color 20 :fade_color 10}
|
(local bars [{:base_y 60 :amplitude 30 :height 16 :speed 2.0 :phase 0 :color 1 :fade_color 18}
|
||||||
{:base_y 180 :amplitude 35 :height 16 :speed 1.8 :phase 2.0 :color 26 :fade_color 10}
|
{:base_y 180 :amplitude 35 :height 16 :speed 1.8 :phase 2.0 :color 1 :fade_color 27}
|
||||||
{:base_y 300 :amplitude 25 :height 16 :speed 2.2 :phase 4.0 :color 14 :fade_color 10}])
|
{:base_y 300 :amplitude 25 :height 16 :speed 2.2 :phase 4.0 :color 1 :fade_color 24}])
|
||||||
(pxl8.vfx_raster_bars bars time))
|
(pxl8.vfx_raster_bars bars time))
|
||||||
|
|
||||||
5 (do
|
5 (do
|
||||||
|
|
|
||||||
BIN
demo/palettes/nes.ase
Normal file
BIN
demo/palettes/nes.ase
Normal file
Binary file not shown.
|
|
@ -69,6 +69,22 @@ function pxl8.load_sprite(filepath)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function pxl8.create_texture(pixels, width, height)
|
||||||
|
local pixel_data = ffi.new("u8[?]", width * height)
|
||||||
|
for i = 0, width * height - 1 do
|
||||||
|
pixel_data[i] = pixels[i + 1] or 0
|
||||||
|
end
|
||||||
|
local result = C.pxl8_gfx_create_texture(gfx, pixel_data, width, height)
|
||||||
|
if result < 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function pxl8.upload_atlas()
|
||||||
|
C.pxl8_gfx_upload_atlas(gfx)
|
||||||
|
end
|
||||||
|
|
||||||
-- log
|
-- log
|
||||||
function pxl8.info(msg)
|
function pxl8.info(msg)
|
||||||
C.pxl8_lua_info(msg)
|
C.pxl8_lua_info(msg)
|
||||||
|
|
@ -267,6 +283,10 @@ function pxl8.set_wireframe(wireframe)
|
||||||
C.pxl8_3d_set_wireframe(gfx, wireframe)
|
C.pxl8_3d_set_wireframe(gfx, wireframe)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function pxl8.set_affine_textures(affine)
|
||||||
|
C.pxl8_3d_set_affine_textures(gfx, affine)
|
||||||
|
end
|
||||||
|
|
||||||
function pxl8.set_backface_culling(culling)
|
function pxl8.set_backface_culling(culling)
|
||||||
C.pxl8_3d_set_backface_culling(gfx, culling)
|
C.pxl8_3d_set_backface_culling(gfx, culling)
|
||||||
end
|
end
|
||||||
|
|
@ -278,6 +298,14 @@ function pxl8.draw_triangle_3d(v0, v1, v2, color)
|
||||||
C.pxl8_3d_draw_triangle_raw(gfx, vec0, vec1, vec2, color)
|
C.pxl8_3d_draw_triangle_raw(gfx, vec0, vec1, vec2, color)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function pxl8.draw_triangle_3d_textured(v0, v1, v2, uv0, uv1, uv2, texture_id)
|
||||||
|
local vec0 = ffi.new("pxl8_vec3", {x = v0[1], y = v0[2], z = v0[3]})
|
||||||
|
local vec1 = ffi.new("pxl8_vec3", {x = v1[1], y = v1[2], z = v1[3]})
|
||||||
|
local vec2 = ffi.new("pxl8_vec3", {x = v2[1], y = v2[2], z = v2[3]})
|
||||||
|
C.pxl8_3d_draw_triangle_textured(gfx, vec0, vec1, vec2,
|
||||||
|
uv0[1], uv0[2], uv1[1], uv1[2], uv2[1], uv2[2], texture_id)
|
||||||
|
end
|
||||||
|
|
||||||
function pxl8.draw_line_3d(p0, p1, color)
|
function pxl8.draw_line_3d(p0, p1, color)
|
||||||
local vec0 = ffi.new("pxl8_vec3", {x = p0[1], y = p0[2], z = p0[3]})
|
local vec0 = ffi.new("pxl8_vec3", {x = p0[1], y = p0[2], z = p0[3]})
|
||||||
local vec1 = ffi.new("pxl8_vec3", {x = p1[1], y = p1[2], z = p1[3]})
|
local vec1 = ffi.new("pxl8_vec3", {x = p1[1], y = p1[2], z = p1[3]})
|
||||||
|
|
|
||||||
|
|
@ -245,11 +245,6 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pxl8_gfx_init_atlas(app.gfx, 1024, 1024) != PXL8_OK) {
|
|
||||||
pxl8_error("Failed to initialize sprite atlas");
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.ui = pxl8_ui_create(app.gfx);
|
app.ui = pxl8_ui_create(app.gfx);
|
||||||
if (!app.ui) {
|
if (!app.ui) {
|
||||||
pxl8_error("Failed to create UI");
|
pxl8_error("Failed to create UI");
|
||||||
|
|
|
||||||
|
|
@ -260,16 +260,11 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
||||||
chunk_header.chunk_type = read_u16_le(chunk_data + 4);
|
chunk_header.chunk_type = read_u16_le(chunk_data + 4);
|
||||||
|
|
||||||
const u8* chunk_payload = chunk_data + 6;
|
const u8* chunk_payload = chunk_data + 6;
|
||||||
|
|
||||||
pxl8_debug("Found chunk: type=0x%04X, size=%d", chunk_header.chunk_type, chunk_header.chunk_size);
|
|
||||||
switch (chunk_header.chunk_type) {
|
switch (chunk_header.chunk_type) {
|
||||||
case PXL8_ASE_CHUNK_OLD_PALETTE: // 0x0004
|
case PXL8_ASE_CHUNK_OLD_PALETTE: // 0x0004
|
||||||
if (!ase_file->palette.colors) {
|
if (!ase_file->palette.colors) {
|
||||||
result = parse_old_palette_chunk(chunk_payload, &ase_file->palette);
|
result = parse_old_palette_chunk(chunk_payload, &ase_file->palette);
|
||||||
pxl8_debug("Parsed old palette: %d colors, indices %d-%d",
|
|
||||||
ase_file->palette.entry_count, ase_file->palette.first_color, ase_file->palette.last_color);
|
|
||||||
} else {
|
|
||||||
pxl8_debug("Ignoring old palette (0x0004) - new palette (0x2019) already loaded");
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -284,11 +279,6 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
||||||
|
|
||||||
result = parse_layer_chunk(chunk_payload, &ase_file->layers[ase_file->layer_count]);
|
result = parse_layer_chunk(chunk_payload, &ase_file->layers[ase_file->layer_count]);
|
||||||
if (result == PXL8_OK) {
|
if (result == PXL8_OK) {
|
||||||
pxl8_debug("Parsed layer %d: '%s', blend_mode=%d, opacity=%d",
|
|
||||||
ase_file->layer_count,
|
|
||||||
ase_file->layers[ase_file->layer_count].name ? ase_file->layers[ase_file->layer_count].name : "(unnamed)",
|
|
||||||
ase_file->layers[ase_file->layer_count].blend_mode,
|
|
||||||
ase_file->layers[ase_file->layer_count].opacity);
|
|
||||||
ase_file->layer_count++;
|
ase_file->layer_count++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -296,14 +286,11 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
||||||
|
|
||||||
case PXL8_ASE_CHUNK_CEL: { // 0x2005
|
case PXL8_ASE_CHUNK_CEL: { // 0x2005
|
||||||
pxl8_ase_cel cel = {0};
|
pxl8_ase_cel cel = {0};
|
||||||
pxl8_debug("Found CEL chunk: size=%d", chunk_header.chunk_size - 6);
|
|
||||||
result = parse_cel_chunk(chunk_payload, chunk_header.chunk_size - 6, &cel);
|
result = parse_cel_chunk(chunk_payload, chunk_header.chunk_size - 6, &cel);
|
||||||
if (result == PXL8_OK && cel.pixel_data) {
|
if (result == PXL8_OK && cel.pixel_data) {
|
||||||
pxl8_debug("CEL data loaded: %dx%d, type=%d, first pixel = %d", cel.width, cel.height, cel.cel_type, cel.pixel_data[0]);
|
|
||||||
u32 copy_width = (cel.width < frame->width) ? cel.width : frame->width;
|
u32 copy_width = (cel.width < frame->width) ? cel.width : frame->width;
|
||||||
u32 copy_height = (cel.height < frame->height) ? cel.height : frame->height;
|
u32 copy_height = (cel.height < frame->height) ? cel.height : frame->height;
|
||||||
|
|
||||||
pxl8_debug("Copying cel to frame: cel_pos=(%d,%d), copy_size=%dx%d", cel.x, cel.y, copy_width, copy_height);
|
|
||||||
for (u32 y = 0; y < copy_height; y++) {
|
for (u32 y = 0; y < copy_height; y++) {
|
||||||
u32 src_offset = y * cel.width;
|
u32 src_offset = y * cel.width;
|
||||||
u32 dst_offset = (y + cel.y) * frame->width + cel.x;
|
u32 dst_offset = (y + cel.y) * frame->width + cel.x;
|
||||||
|
|
@ -311,20 +298,16 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
||||||
for (u32 x = 0; x < copy_width; x++) {
|
for (u32 x = 0; x < copy_width; x++) {
|
||||||
u8 src_pixel = cel.pixel_data[src_offset + x];
|
u8 src_pixel = cel.pixel_data[src_offset + x];
|
||||||
bool is_transparent = false;
|
bool is_transparent = false;
|
||||||
|
|
||||||
if (src_pixel < ase_file->palette.entry_count && ase_file->palette.colors) {
|
if (src_pixel < ase_file->palette.entry_count && ase_file->palette.colors) {
|
||||||
u32 color = ase_file->palette.colors[src_pixel];
|
u32 color = ase_file->palette.colors[src_pixel];
|
||||||
is_transparent = ((color >> 24) & 0xFF) == 0;
|
is_transparent = ((color >> 24) & 0xFF) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_transparent) {
|
if (!is_transparent) {
|
||||||
frame->pixels[dst_offset + x] = src_pixel;
|
frame->pixels[dst_offset + x] = src_pixel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (y < 3) {
|
|
||||||
pxl8_debug("Row %d: cel[%d]=%d, frame[%d]=%d",
|
|
||||||
y, src_offset, cel.pixel_data[src_offset], dst_offset, frame->pixels[dst_offset]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SDL_free(cel.pixel_data);
|
SDL_free(cel.pixel_data);
|
||||||
|
|
@ -337,8 +320,6 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
||||||
SDL_free(ase_file->palette.colors);
|
SDL_free(ase_file->palette.colors);
|
||||||
}
|
}
|
||||||
result = parse_palette_chunk(chunk_payload, &ase_file->palette);
|
result = parse_palette_chunk(chunk_payload, &ase_file->palette);
|
||||||
pxl8_debug("Parsed new palette: %d colors, indices %d-%d",
|
|
||||||
ase_file->palette.entry_count, ase_file->palette.first_color, ase_file->palette.last_color);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
783
src/pxl8_gfx.c
783
src/pxl8_gfx.c
|
|
@ -38,22 +38,46 @@ pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height) {
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct pxl8_atlas_entry {
|
typedef struct pxl8_atlas_entry {
|
||||||
|
bool active;
|
||||||
char path[256];
|
char path[256];
|
||||||
u32 sprite_id;
|
u32 texture_id;
|
||||||
|
|
||||||
i32 x, y, w, h;
|
i32 x, y, w, h;
|
||||||
} pxl8_atlas_entry;
|
} pxl8_atlas_entry;
|
||||||
|
|
||||||
|
typedef struct pxl8_skyline_node {
|
||||||
|
i32 x, y, width;
|
||||||
|
} pxl8_skyline_node;
|
||||||
|
|
||||||
|
typedef struct pxl8_skyline_fit {
|
||||||
|
bool found;
|
||||||
|
u32 node_idx;
|
||||||
|
pxl8_point pos;
|
||||||
|
} pxl8_skyline_fit;
|
||||||
|
|
||||||
|
struct pxl8_atlas {
|
||||||
|
u32 height, width;
|
||||||
|
u8* pixels;
|
||||||
|
SDL_Texture* texture;
|
||||||
|
|
||||||
|
bool dirty;
|
||||||
|
|
||||||
|
u32 entry_capacity, entry_count;
|
||||||
|
pxl8_atlas_entry* entries;
|
||||||
|
|
||||||
|
u32 free_capacity, free_count;
|
||||||
|
u32* free_list;
|
||||||
|
|
||||||
|
u32 skyline_capacity, skyline_count;
|
||||||
|
pxl8_skyline_node* skyline;
|
||||||
|
};
|
||||||
|
|
||||||
struct pxl8_gfx {
|
struct pxl8_gfx {
|
||||||
SDL_Renderer* renderer;
|
SDL_Renderer* renderer;
|
||||||
SDL_Texture* framebuffer_texture;
|
SDL_Texture* framebuffer_texture;
|
||||||
SDL_Texture* sprite_atlas_texture;
|
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
|
|
||||||
u8* atlas;
|
pxl8_atlas* atlas;
|
||||||
bool atlas_dirty;
|
|
||||||
pxl8_atlas_entry* atlas_entries;
|
|
||||||
u32 atlas_entries_cap;
|
|
||||||
u32 atlas_entries_len;
|
|
||||||
|
|
||||||
pxl8_color_mode color_mode;
|
pxl8_color_mode color_mode;
|
||||||
u8* framebuffer;
|
u8* framebuffer;
|
||||||
|
|
@ -64,12 +88,6 @@ struct pxl8_gfx {
|
||||||
u32* palette;
|
u32* palette;
|
||||||
u32 palette_size;
|
u32 palette_size;
|
||||||
|
|
||||||
u32 sprite_atlas_height;
|
|
||||||
u32 sprite_atlas_width;
|
|
||||||
u32 sprite_frame_height;
|
|
||||||
u32 sprite_frame_width;
|
|
||||||
u32 sprite_frames_per_row;
|
|
||||||
|
|
||||||
pxl8_viewport viewport;
|
pxl8_viewport viewport;
|
||||||
|
|
||||||
bool backface_culling;
|
bool backface_culling;
|
||||||
|
|
@ -80,8 +98,305 @@ struct pxl8_gfx {
|
||||||
f32* zbuffer;
|
f32* zbuffer;
|
||||||
i32 zbuffer_height;
|
i32 zbuffer_height;
|
||||||
i32 zbuffer_width;
|
i32 zbuffer_width;
|
||||||
|
|
||||||
|
bool affine_textures;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static pxl8_skyline_fit pxl8_skyline_find_position(pxl8_skyline_node* skyline, u32 skyline_count, u32 atlas_w, u32 atlas_h, u32 rect_w, u32 rect_h) {
|
||||||
|
pxl8_skyline_fit result = {.found = false};
|
||||||
|
i32 best_y = INT32_MAX;
|
||||||
|
i32 best_x = 0;
|
||||||
|
u32 best_idx = 0;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < skyline_count; i++) {
|
||||||
|
i32 x = skyline[i].x;
|
||||||
|
i32 y = skyline[i].y;
|
||||||
|
|
||||||
|
if (x + (i32)rect_w > (i32)atlas_w) continue;
|
||||||
|
|
||||||
|
i32 max_y = y;
|
||||||
|
for (u32 j = i; j < skyline_count && skyline[j].x < x + (i32)rect_w; j++) {
|
||||||
|
if (skyline[j].y > max_y) {
|
||||||
|
max_y = skyline[j].y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_y + (i32)rect_h > (i32)atlas_h) continue;
|
||||||
|
|
||||||
|
if (max_y < best_y || (max_y == best_y && x < best_x)) {
|
||||||
|
best_y = max_y;
|
||||||
|
best_x = x;
|
||||||
|
best_idx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best_y != INT32_MAX) {
|
||||||
|
result.found = true;
|
||||||
|
result.pos.x = best_x;
|
||||||
|
result.pos.y = best_y;
|
||||||
|
result.node_idx = best_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pxl8_skyline_add_rect(pxl8_skyline_node** skyline, u32* skyline_count, u32* skyline_capacity, pxl8_point pos, u32 w, u32 h) {
|
||||||
|
u32 node_idx = 0;
|
||||||
|
for (u32 i = 0; i < *skyline_count; i++) {
|
||||||
|
if ((*skyline)[i].x == pos.x) {
|
||||||
|
node_idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_skyline_node new_node = {pos.x, pos.y + (i32)h, (i32)w};
|
||||||
|
|
||||||
|
u32 nodes_to_remove = 0;
|
||||||
|
for (u32 i = node_idx; i < *skyline_count; i++) {
|
||||||
|
if ((*skyline)[i].x < pos.x + (i32)w) {
|
||||||
|
nodes_to_remove++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*skyline_count - nodes_to_remove + 1 > *skyline_capacity) {
|
||||||
|
*skyline_capacity = (*skyline_count - nodes_to_remove + 1) * 2;
|
||||||
|
*skyline = (pxl8_skyline_node*)SDL_realloc(*skyline, *skyline_capacity * sizeof(pxl8_skyline_node));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes_to_remove > 0) {
|
||||||
|
SDL_memmove(&(*skyline)[node_idx + 1], &(*skyline)[node_idx + nodes_to_remove],
|
||||||
|
(*skyline_count - node_idx - nodes_to_remove) * sizeof(pxl8_skyline_node));
|
||||||
|
}
|
||||||
|
|
||||||
|
(*skyline)[node_idx] = new_node;
|
||||||
|
*skyline_count = *skyline_count - nodes_to_remove + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pxl8_skyline_compact(pxl8_skyline_node* skyline, u32* skyline_count) {
|
||||||
|
for (u32 i = 0; i < *skyline_count - 1; ) {
|
||||||
|
if (skyline[i].y == skyline[i + 1].y) {
|
||||||
|
skyline[i].width += skyline[i + 1].width;
|
||||||
|
SDL_memmove(&skyline[i + 1], &skyline[i + 2],
|
||||||
|
(*skyline_count - i - 2) * sizeof(pxl8_skyline_node));
|
||||||
|
(*skyline_count)--;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static pxl8_atlas* pxl8_atlas_create(SDL_Renderer* renderer, u32 width, u32 height, pxl8_color_mode color_mode) {
|
||||||
|
pxl8_atlas* atlas = (pxl8_atlas*)SDL_calloc(1, sizeof(pxl8_atlas));
|
||||||
|
if (!atlas) return NULL;
|
||||||
|
|
||||||
|
atlas->height = height;
|
||||||
|
atlas->width = width;
|
||||||
|
|
||||||
|
i32 bytes_per_pixel = (color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1;
|
||||||
|
atlas->pixels = (u8*)SDL_calloc(width * height, bytes_per_pixel);
|
||||||
|
if (!atlas->pixels) {
|
||||||
|
SDL_free(atlas);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32,
|
||||||
|
SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||||
|
if (!atlas->texture) {
|
||||||
|
SDL_free(atlas->pixels);
|
||||||
|
SDL_free(atlas);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas->entry_capacity = 64;
|
||||||
|
atlas->entries = (pxl8_atlas_entry*)SDL_calloc(atlas->entry_capacity, sizeof(pxl8_atlas_entry));
|
||||||
|
if (!atlas->entries) {
|
||||||
|
SDL_DestroyTexture(atlas->texture);
|
||||||
|
SDL_free(atlas->pixels);
|
||||||
|
SDL_free(atlas);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas->free_capacity = 16;
|
||||||
|
atlas->free_list = (u32*)SDL_calloc(atlas->free_capacity, sizeof(u32));
|
||||||
|
if (!atlas->free_list) {
|
||||||
|
SDL_free(atlas->entries);
|
||||||
|
SDL_DestroyTexture(atlas->texture);
|
||||||
|
SDL_free(atlas->pixels);
|
||||||
|
SDL_free(atlas);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas->skyline_capacity = 16;
|
||||||
|
atlas->skyline = (pxl8_skyline_node*)SDL_calloc(atlas->skyline_capacity, sizeof(pxl8_skyline_node));
|
||||||
|
if (!atlas->skyline) {
|
||||||
|
SDL_free(atlas->free_list);
|
||||||
|
SDL_free(atlas->entries);
|
||||||
|
SDL_DestroyTexture(atlas->texture);
|
||||||
|
SDL_free(atlas->pixels);
|
||||||
|
SDL_free(atlas);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas->skyline[0] = (pxl8_skyline_node){0, 0, (i32)width};
|
||||||
|
atlas->skyline_count = 1;
|
||||||
|
|
||||||
|
return atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pxl8_atlas_destroy(pxl8_atlas* atlas) {
|
||||||
|
if (!atlas) return;
|
||||||
|
|
||||||
|
SDL_free(atlas->free_list);
|
||||||
|
SDL_free(atlas->entries);
|
||||||
|
SDL_free(atlas->skyline);
|
||||||
|
if (atlas->texture) SDL_DestroyTexture(atlas->texture);
|
||||||
|
SDL_free(atlas->pixels);
|
||||||
|
SDL_free(atlas);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, const char* path, pxl8_color_mode color_mode);
|
||||||
|
static bool pxl8_atlas_expand(pxl8_atlas* atlas, SDL_Renderer* renderer, pxl8_color_mode color_mode);
|
||||||
|
|
||||||
|
static u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, const char* path, pxl8_color_mode color_mode) {
|
||||||
|
if (!atlas || !pixels) return UINT32_MAX;
|
||||||
|
|
||||||
|
pxl8_skyline_fit fit = pxl8_skyline_find_position(atlas->skyline, atlas->skyline_count, atlas->width, atlas->height, w, h);
|
||||||
|
if (!fit.found) {
|
||||||
|
if (!pxl8_atlas_expand(atlas, atlas->texture ? SDL_GetRendererFromTexture(atlas->texture) : NULL, color_mode)) {
|
||||||
|
return UINT32_MAX;
|
||||||
|
}
|
||||||
|
fit = pxl8_skyline_find_position(atlas->skyline, atlas->skyline_count, atlas->width, atlas->height, w, h);
|
||||||
|
if (!fit.found) return UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 texture_id;
|
||||||
|
if (atlas->free_count > 0) {
|
||||||
|
texture_id = atlas->free_list[--atlas->free_count];
|
||||||
|
} else {
|
||||||
|
if (atlas->entry_count >= atlas->entry_capacity) {
|
||||||
|
atlas->entry_capacity *= 2;
|
||||||
|
atlas->entries = (pxl8_atlas_entry*)SDL_realloc(atlas->entries,
|
||||||
|
atlas->entry_capacity * sizeof(pxl8_atlas_entry));
|
||||||
|
}
|
||||||
|
texture_id = atlas->entry_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_atlas_entry* entry = &atlas->entries[texture_id];
|
||||||
|
entry->active = true;
|
||||||
|
entry->texture_id = texture_id;
|
||||||
|
entry->x = fit.pos.x;
|
||||||
|
entry->y = fit.pos.y;
|
||||||
|
entry->w = w;
|
||||||
|
entry->h = h;
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
strncpy(entry->path, path, sizeof(entry->path) - 1);
|
||||||
|
entry->path[sizeof(entry->path) - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
snprintf(entry->path, sizeof(entry->path), "procgen_%u", texture_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 bytes_per_pixel = (color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1;
|
||||||
|
for (u32 y = 0; y < h; y++) {
|
||||||
|
for (u32 x = 0; x < w; x++) {
|
||||||
|
u32 src_idx = y * w + x;
|
||||||
|
u32 dst_idx = (fit.pos.y + y) * atlas->width + (fit.pos.x + x);
|
||||||
|
|
||||||
|
if (bytes_per_pixel == 4) {
|
||||||
|
((u32*)atlas->pixels)[dst_idx] = ((const u32*)pixels)[src_idx];
|
||||||
|
} else {
|
||||||
|
atlas->pixels[dst_idx] = pixels[src_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_skyline_add_rect(&atlas->skyline, &atlas->skyline_count, &atlas->skyline_capacity, fit.pos, w, h);
|
||||||
|
pxl8_skyline_compact(atlas->skyline, &atlas->skyline_count);
|
||||||
|
|
||||||
|
atlas->dirty = true;
|
||||||
|
|
||||||
|
return texture_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pxl8_atlas_expand(pxl8_atlas* atlas, SDL_Renderer* renderer, pxl8_color_mode color_mode) {
|
||||||
|
if (!atlas || atlas->width >= 4096) return false;
|
||||||
|
|
||||||
|
u32 new_size = atlas->width * 2;
|
||||||
|
u32 old_width = atlas->width;
|
||||||
|
i32 bytes_per_pixel = (color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1;
|
||||||
|
|
||||||
|
u8* new_pixels = (u8*)SDL_calloc(new_size * new_size, bytes_per_pixel);
|
||||||
|
if (!new_pixels) return false;
|
||||||
|
|
||||||
|
SDL_Texture* new_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32,
|
||||||
|
SDL_TEXTUREACCESS_STREAMING, new_size, new_size);
|
||||||
|
if (!new_texture) {
|
||||||
|
SDL_free(new_pixels);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_skyline_node* new_skyline = (pxl8_skyline_node*)SDL_calloc(16, sizeof(pxl8_skyline_node));
|
||||||
|
if (!new_skyline) {
|
||||||
|
SDL_DestroyTexture(new_texture);
|
||||||
|
SDL_free(new_pixels);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
new_skyline[0] = (pxl8_skyline_node){0, 0, (i32)new_size};
|
||||||
|
u32 new_skyline_count = 1;
|
||||||
|
u32 new_skyline_capacity = 16;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < atlas->entry_count; i++) {
|
||||||
|
if (!atlas->entries[i].active) continue;
|
||||||
|
|
||||||
|
pxl8_skyline_fit fit = pxl8_skyline_find_position(new_skyline, new_skyline_count, new_size, new_size,
|
||||||
|
atlas->entries[i].w, atlas->entries[i].h);
|
||||||
|
if (!fit.found) {
|
||||||
|
SDL_free(new_skyline);
|
||||||
|
SDL_DestroyTexture(new_texture);
|
||||||
|
SDL_free(new_pixels);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 y = 0; y < (u32)atlas->entries[i].h; y++) {
|
||||||
|
for (u32 x = 0; x < (u32)atlas->entries[i].w; x++) {
|
||||||
|
u32 src_idx = (atlas->entries[i].y + y) * old_width + (atlas->entries[i].x + x);
|
||||||
|
u32 dst_idx = (fit.pos.y + y) * new_size + (fit.pos.x + x);
|
||||||
|
if (bytes_per_pixel == 4) {
|
||||||
|
((u32*)new_pixels)[dst_idx] = ((u32*)atlas->pixels)[src_idx];
|
||||||
|
} else {
|
||||||
|
new_pixels[dst_idx] = atlas->pixels[src_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas->entries[i].x = fit.pos.x;
|
||||||
|
atlas->entries[i].y = fit.pos.y;
|
||||||
|
|
||||||
|
pxl8_skyline_add_rect(&new_skyline, &new_skyline_count, &new_skyline_capacity, fit.pos,
|
||||||
|
atlas->entries[i].w, atlas->entries[i].h);
|
||||||
|
pxl8_skyline_compact(new_skyline, &new_skyline_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_DestroyTexture(atlas->texture);
|
||||||
|
SDL_free(atlas->pixels);
|
||||||
|
SDL_free(atlas->skyline);
|
||||||
|
|
||||||
|
atlas->pixels = new_pixels;
|
||||||
|
atlas->texture = new_texture;
|
||||||
|
atlas->skyline = new_skyline;
|
||||||
|
atlas->skyline_count = new_skyline_count;
|
||||||
|
atlas->skyline_capacity = new_skyline_capacity;
|
||||||
|
atlas->width = new_size;
|
||||||
|
atlas->height = new_size;
|
||||||
|
atlas->dirty = true;
|
||||||
|
|
||||||
|
pxl8_debug("Atlas expanded to %ux%u", atlas->width, atlas->height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static u32 pxl8_get_palette_size(pxl8_color_mode mode) {
|
static u32 pxl8_get_palette_size(pxl8_color_mode mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case PXL8_COLOR_MODE_HICOLOR: return 0;
|
case PXL8_COLOR_MODE_HICOLOR: return 0;
|
||||||
|
|
@ -140,6 +455,10 @@ i32 pxl8_gfx_get_width(const pxl8_gfx* gfx) {
|
||||||
return gfx ? gfx->framebuffer_width : 0;
|
return gfx ? gfx->framebuffer_width : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 pxl8_gfx_get_palette_size(const pxl8_gfx* gfx) {
|
||||||
|
return gfx ? gfx->palette_size : 0;
|
||||||
|
}
|
||||||
|
|
||||||
pxl8_gfx* pxl8_gfx_create(pxl8_color_mode mode, pxl8_resolution resolution, const char* title, i32 window_width, i32 window_height) {
|
pxl8_gfx* pxl8_gfx_create(pxl8_color_mode mode, pxl8_resolution resolution, const char* title, i32 window_width, i32 window_height) {
|
||||||
pxl8_gfx* gfx = (pxl8_gfx*)SDL_calloc(1, sizeof(*gfx));
|
pxl8_gfx* gfx = (pxl8_gfx*)SDL_calloc(1, sizeof(*gfx));
|
||||||
if (!gfx) {
|
if (!gfx) {
|
||||||
|
|
@ -228,6 +547,7 @@ pxl8_gfx* pxl8_gfx_create(pxl8_color_mode mode, pxl8_resolution resolution, cons
|
||||||
gfx->zbuffer = NULL;
|
gfx->zbuffer = NULL;
|
||||||
gfx->zbuffer_height = 0;
|
gfx->zbuffer_height = 0;
|
||||||
gfx->zbuffer_width = 0;
|
gfx->zbuffer_width = 0;
|
||||||
|
gfx->affine_textures = false;
|
||||||
|
|
||||||
gfx->initialized = true;
|
gfx->initialized = true;
|
||||||
return gfx;
|
return gfx;
|
||||||
|
|
@ -235,165 +555,99 @@ pxl8_gfx* pxl8_gfx_create(pxl8_color_mode mode, pxl8_resolution resolution, cons
|
||||||
|
|
||||||
void pxl8_gfx_destroy(pxl8_gfx* gfx) {
|
void pxl8_gfx_destroy(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
|
|
||||||
|
pxl8_atlas_destroy(gfx->atlas);
|
||||||
|
|
||||||
if (gfx->framebuffer_texture) {
|
if (gfx->framebuffer_texture) {
|
||||||
SDL_DestroyTexture(gfx->framebuffer_texture);
|
SDL_DestroyTexture(gfx->framebuffer_texture);
|
||||||
gfx->framebuffer_texture = NULL;
|
gfx->framebuffer_texture = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gfx->sprite_atlas_texture) {
|
|
||||||
SDL_DestroyTexture(gfx->sprite_atlas_texture);
|
|
||||||
gfx->sprite_atlas_texture = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gfx->renderer) {
|
if (gfx->renderer) {
|
||||||
SDL_DestroyRenderer(gfx->renderer);
|
SDL_DestroyRenderer(gfx->renderer);
|
||||||
gfx->renderer = NULL;
|
gfx->renderer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gfx->window) {
|
if (gfx->window) {
|
||||||
SDL_DestroyWindow(gfx->window);
|
SDL_DestroyWindow(gfx->window);
|
||||||
gfx->window = NULL;
|
gfx->window = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_free(gfx->framebuffer);
|
SDL_free(gfx->framebuffer);
|
||||||
SDL_free(gfx->palette);
|
SDL_free(gfx->palette);
|
||||||
SDL_free(gfx->atlas);
|
|
||||||
SDL_free(gfx->atlas_entries);
|
|
||||||
SDL_free(gfx->zbuffer);
|
SDL_free(gfx->zbuffer);
|
||||||
|
|
||||||
SDL_free(gfx);
|
SDL_free(gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_result pxl8_gfx_init_atlas(pxl8_gfx* gfx, u32 width, u32 height) {
|
|
||||||
if (!gfx || !gfx->initialized) return PXL8_ERROR_INVALID_ARGUMENT;
|
pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width, u32 height) {
|
||||||
|
if (!gfx || !gfx->initialized || !pixels) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||||
gfx->sprite_atlas_width = width;
|
|
||||||
gfx->sprite_atlas_height = height;
|
|
||||||
|
|
||||||
i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1;
|
|
||||||
gfx->atlas = (u8*)SDL_calloc(width * height, bytes_per_pixel);
|
|
||||||
if (!gfx->atlas) {
|
if (!gfx->atlas) {
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
gfx->atlas = pxl8_atlas_create(gfx->renderer, 1024, 1024, gfx->color_mode);
|
||||||
|
if (!gfx->atlas) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx->sprite_atlas_texture = SDL_CreateTexture(
|
u32 texture_id = pxl8_atlas_add_texture(gfx->atlas, pixels, width, height, NULL, gfx->color_mode);
|
||||||
gfx->renderer,
|
if (texture_id == UINT32_MAX) {
|
||||||
SDL_PIXELFORMAT_RGBA32,
|
pxl8_error("Texture doesn't fit in atlas");
|
||||||
SDL_TEXTUREACCESS_STREAMING,
|
return PXL8_ERROR_INVALID_SIZE;
|
||||||
width,
|
|
||||||
height
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!gfx->sprite_atlas_texture) {
|
|
||||||
SDL_free(gfx->atlas);
|
|
||||||
gfx->atlas = NULL;
|
|
||||||
return PXL8_ERROR_SYSTEM_FAILURE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx->atlas_entries_cap = 256;
|
pxl8_debug("Created texture %u: %ux%u at (%d,%d)",
|
||||||
gfx->atlas_entries = (pxl8_atlas_entry*)SDL_calloc(gfx->atlas_entries_cap, sizeof(pxl8_atlas_entry));
|
texture_id, width, height,
|
||||||
if (!gfx->atlas_entries) {
|
gfx->atlas->entries[texture_id].x,
|
||||||
SDL_DestroyTexture(gfx->sprite_atlas_texture);
|
gfx->atlas->entries[texture_id].y);
|
||||||
SDL_free(gfx->atlas);
|
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
return texture_id;
|
||||||
}
|
|
||||||
|
|
||||||
gfx->sprite_frames_per_row = width / 128;
|
|
||||||
if (gfx->sprite_frames_per_row == 0) gfx->sprite_frames_per_row = 1;
|
|
||||||
|
|
||||||
return PXL8_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) {
|
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) {
|
||||||
if (!gfx || !gfx->initialized || !path) return PXL8_ERROR_INVALID_ARGUMENT;
|
if (!gfx || !gfx->initialized || !path) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||||
|
|
||||||
if (!gfx->atlas) {
|
if (!gfx->atlas) {
|
||||||
pxl8_error("Atlas not initialized");
|
gfx->atlas = pxl8_atlas_create(gfx->renderer, 1024, 1024, gfx->color_mode);
|
||||||
return PXL8_ERROR_NOT_INITIALIZED;
|
if (!gfx->atlas) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (u32 i = 0; i < gfx->atlas_entries_len; i++) {
|
for (u32 i = 0; i < gfx->atlas->entry_count; i++) {
|
||||||
if (strcmp(gfx->atlas_entries[i].path, path) == 0) {
|
if (gfx->atlas->entries[i].active && strcmp(gfx->atlas->entries[i].path, path) == 0) {
|
||||||
return PXL8_OK;
|
return gfx->atlas->entries[i].texture_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_ase_file ase_file;
|
pxl8_ase_file ase_file;
|
||||||
pxl8_result result = pxl8_ase_load(path, &ase_file);
|
pxl8_result result = pxl8_ase_load(path, &ase_file);
|
||||||
if (result != PXL8_OK) {
|
if (result != PXL8_OK) {
|
||||||
pxl8_error("Failed to load ASE file: %s", path);
|
pxl8_error("Failed to load ASE file: %s", path);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ase_file.frame_count == 0) {
|
if (ase_file.frame_count == 0) {
|
||||||
pxl8_error("No frames in ASE file");
|
pxl8_error("No frames in ASE file");
|
||||||
pxl8_ase_destroy(&ase_file);
|
pxl8_ase_destroy(&ase_file);
|
||||||
return PXL8_ERROR_INVALID_FORMAT;
|
return PXL8_ERROR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 sprite_w = ase_file.header.width;
|
u32 sprite_w = ase_file.header.width;
|
||||||
u32 sprite_h = ase_file.header.height;
|
u32 sprite_h = ase_file.header.height;
|
||||||
|
|
||||||
if (gfx->sprite_frame_width == 0 || gfx->sprite_frame_height == 0) {
|
u32 texture_id = pxl8_atlas_add_texture(gfx->atlas, ase_file.frames[0].pixels, sprite_w, sprite_h, path, gfx->color_mode);
|
||||||
gfx->sprite_frame_width = sprite_w;
|
|
||||||
gfx->sprite_frame_height = sprite_h;
|
pxl8_ase_destroy(&ase_file);
|
||||||
}
|
|
||||||
|
if (texture_id == UINT32_MAX) {
|
||||||
u32 id = gfx->atlas_entries_len;
|
|
||||||
u32 frames_per_row = gfx->sprite_frames_per_row;
|
|
||||||
u32 atlas_x = (id % frames_per_row) * gfx->sprite_frame_width;
|
|
||||||
u32 atlas_y = (id / frames_per_row) * gfx->sprite_frame_height;
|
|
||||||
|
|
||||||
if (atlas_x + sprite_w > gfx->sprite_atlas_width ||
|
|
||||||
atlas_y + sprite_h > gfx->sprite_atlas_height) {
|
|
||||||
pxl8_error("Sprite doesn't fit in atlas");
|
pxl8_error("Sprite doesn't fit in atlas");
|
||||||
pxl8_ase_destroy(&ase_file);
|
|
||||||
return PXL8_ERROR_INVALID_SIZE;
|
return PXL8_ERROR_INVALID_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1;
|
pxl8_debug("Loaded sprite %u: %ux%u at (%d,%d)",
|
||||||
|
texture_id, sprite_w, sprite_h,
|
||||||
for (u32 y = 0; y < sprite_h; y++) {
|
gfx->atlas->entries[texture_id].x,
|
||||||
for (u32 x = 0; x < sprite_w; x++) {
|
gfx->atlas->entries[texture_id].y);
|
||||||
u32 frame_idx = y * sprite_w + x;
|
|
||||||
u32 atlas_idx = (atlas_y + y) * gfx->sprite_atlas_width + (atlas_x + x);
|
return texture_id;
|
||||||
|
|
||||||
if (bytes_per_pixel == 4) {
|
|
||||||
((u32*)gfx->atlas)[atlas_idx] = ((u32*)ase_file.frames[0].pixels)[frame_idx];
|
|
||||||
} else {
|
|
||||||
gfx->atlas[atlas_idx] = ase_file.frames[0].pixels[frame_idx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gfx->atlas_entries_len >= gfx->atlas_entries_cap) {
|
|
||||||
gfx->atlas_entries_cap *= 2;
|
|
||||||
pxl8_atlas_entry* new_entries = (pxl8_atlas_entry*)SDL_realloc(
|
|
||||||
gfx->atlas_entries,
|
|
||||||
gfx->atlas_entries_cap * sizeof(pxl8_atlas_entry)
|
|
||||||
);
|
|
||||||
if (!new_entries) {
|
|
||||||
pxl8_ase_destroy(&ase_file);
|
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
gfx->atlas_entries = new_entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx->atlas_entries[id].x = atlas_x;
|
|
||||||
gfx->atlas_entries[id].y = atlas_y;
|
|
||||||
gfx->atlas_entries[id].w = sprite_w;
|
|
||||||
gfx->atlas_entries[id].h = sprite_h;
|
|
||||||
gfx->atlas_entries[id].sprite_id = id;
|
|
||||||
strncpy(gfx->atlas_entries[id].path, path, sizeof(gfx->atlas_entries[id].path) - 1);
|
|
||||||
gfx->atlas_entries[id].path[sizeof(gfx->atlas_entries[id].path) - 1] = '\0';
|
|
||||||
gfx->atlas_entries_len++;
|
|
||||||
|
|
||||||
gfx->atlas_dirty = true;
|
|
||||||
|
|
||||||
pxl8_ase_destroy(&ase_file);
|
|
||||||
pxl8_debug("Loaded sprite %u: %ux%u at (%u,%u)", id, sprite_w, sprite_h, atlas_x, atlas_y);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path) {
|
pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path) {
|
||||||
|
|
@ -417,18 +671,12 @@ pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path) {
|
||||||
|
|
||||||
u32 copy_size = (ase_file.palette.entry_count < gfx->palette_size) ? ase_file.palette.entry_count : gfx->palette_size;
|
u32 copy_size = (ase_file.palette.entry_count < gfx->palette_size) ? ase_file.palette.entry_count : gfx->palette_size;
|
||||||
memcpy(gfx->palette, ase_file.palette.colors, copy_size * sizeof(u32));
|
memcpy(gfx->palette, ase_file.palette.colors, copy_size * sizeof(u32));
|
||||||
|
|
||||||
if (gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && gfx->framebuffer_texture) {
|
u32 last_color = (copy_size > 0) ? gfx->palette[copy_size - 1] : 0xFF000000;
|
||||||
SDL_Color colors[256];
|
for (u32 i = copy_size; i < gfx->palette_size; i++) {
|
||||||
for (u32 i = 0; i < 256 && i < gfx->palette_size; i++) {
|
gfx->palette[i] = last_color;
|
||||||
colors[i].r = (gfx->palette[i] >> 16) & 0xFF;
|
|
||||||
colors[i].g = (gfx->palette[i] >> 8) & 0xFF;
|
|
||||||
colors[i].b = gfx->palette[i] & 0xFF;
|
|
||||||
colors[i].a = (gfx->palette[i] >> 24) & 0xFF;
|
|
||||||
}
|
|
||||||
SDL_SetPaletteColors(SDL_CreateSurfacePalette(NULL), colors, 0, 256);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_ase_destroy(&ase_file);
|
pxl8_ase_destroy(&ase_file);
|
||||||
pxl8_debug("Loaded palette with %u colors", copy_size);
|
pxl8_debug("Loaded palette with %u colors", copy_size);
|
||||||
|
|
||||||
|
|
@ -475,19 +723,19 @@ void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gfx_upload_atlas(pxl8_gfx* gfx) {
|
void pxl8_gfx_upload_atlas(pxl8_gfx* gfx) {
|
||||||
if (!gfx || !gfx->initialized || !gfx->sprite_atlas_texture || !gfx->atlas_dirty) return;
|
if (!gfx || !gfx->initialized || !gfx->atlas || !gfx->atlas->texture || !gfx->atlas->dirty) return;
|
||||||
|
|
||||||
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
||||||
SDL_UpdateTexture(gfx->sprite_atlas_texture, NULL, gfx->atlas,
|
SDL_UpdateTexture(gfx->atlas->texture, NULL, gfx->atlas->pixels,
|
||||||
gfx->sprite_atlas_width * 4);
|
gfx->atlas->width * 4);
|
||||||
} else {
|
} else {
|
||||||
pxl8_upload_indexed_texture(gfx->sprite_atlas_texture, gfx->atlas,
|
pxl8_upload_indexed_texture(gfx->atlas->texture, gfx->atlas->pixels,
|
||||||
gfx->palette, gfx->palette_size,
|
gfx->palette, gfx->palette_size,
|
||||||
gfx->sprite_atlas_width, gfx->sprite_atlas_height,
|
gfx->atlas->width, gfx->atlas->height,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx->atlas_dirty = false;
|
gfx->atlas->dirty = false;
|
||||||
pxl8_debug("Atlas uploaded to GPU");
|
pxl8_debug("Atlas uploaded to GPU");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -703,9 +951,10 @@ void pxl8_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
||||||
|
|
||||||
|
|
||||||
void pxl8_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h) {
|
void pxl8_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h) {
|
||||||
if (!gfx || !gfx->atlas || !gfx->framebuffer || sprite_id >= gfx->atlas_entries_len) return;
|
if (!gfx || !gfx->atlas || !gfx->framebuffer || sprite_id >= gfx->atlas->entry_count) return;
|
||||||
|
if (!gfx->atlas->entries[sprite_id].active) return;
|
||||||
pxl8_atlas_entry* entry = &gfx->atlas_entries[sprite_id];
|
|
||||||
|
pxl8_atlas_entry* entry = &gfx->atlas->entries[sprite_id];
|
||||||
|
|
||||||
i32 clip_left = (x < 0) ? -x : 0;
|
i32 clip_left = (x < 0) ? -x : 0;
|
||||||
i32 clip_top = (y < 0) ? -y : 0;
|
i32 clip_top = (y < 0) ? -y : 0;
|
||||||
|
|
@ -724,30 +973,30 @@ void pxl8_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h) {
|
||||||
bool is_unclipped = (clip_left == 0 && clip_top == 0 && clip_right == 0 && clip_bottom == 0);
|
bool is_unclipped = (clip_left == 0 && clip_top == 0 && clip_right == 0 && clip_bottom == 0);
|
||||||
|
|
||||||
if (is_1to1_scale && is_unclipped && pxl8_is_simd_aligned(w)) {
|
if (is_1to1_scale && is_unclipped && pxl8_is_simd_aligned(w)) {
|
||||||
const u8* sprite_data = gfx->atlas + entry->y * gfx->sprite_atlas_width + entry->x;
|
const u8* sprite_data = gfx->atlas->pixels + entry->y * gfx->atlas->width + entry->x;
|
||||||
|
|
||||||
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
||||||
pxl8_blit_simd_hicolor((u32*)gfx->framebuffer, gfx->framebuffer_width,
|
pxl8_blit_simd_hicolor((u32*)gfx->framebuffer, gfx->framebuffer_width,
|
||||||
(const u32*)sprite_data, gfx->sprite_atlas_width, x, y, w, h);
|
(const u32*)sprite_data, gfx->atlas->width, x, y, w, h);
|
||||||
} else {
|
} else {
|
||||||
pxl8_blit_simd_indexed(gfx->framebuffer, gfx->framebuffer_width,
|
pxl8_blit_simd_indexed(gfx->framebuffer, gfx->framebuffer_width,
|
||||||
sprite_data, gfx->sprite_atlas_width, x, y, w, h);
|
sprite_data, gfx->atlas->width, x, y, w, h);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (i32 py = 0; py < draw_height; py++) {
|
for (i32 py = 0; py < draw_height; py++) {
|
||||||
for (i32 px = 0; px < draw_width; px++) {
|
for (i32 px = 0; px < draw_width; px++) {
|
||||||
i32 src_x = entry->x + ((px + clip_left) * entry->w) / w;
|
i32 src_x = entry->x + ((px + clip_left) * entry->w) / w;
|
||||||
i32 src_y = entry->y + ((py + clip_top) * entry->h) / h;
|
i32 src_y = entry->y + ((py + clip_top) * entry->h) / h;
|
||||||
i32 src_idx = src_y * gfx->sprite_atlas_width + src_x;
|
i32 src_idx = src_y * gfx->atlas->width + src_x;
|
||||||
i32 dest_idx = (dest_y + py) * gfx->framebuffer_width + (dest_x + px);
|
i32 dest_idx = (dest_y + py) * gfx->framebuffer_width + (dest_x + px);
|
||||||
|
|
||||||
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
||||||
u32 pixel = ((u32*)gfx->atlas)[src_idx];
|
u32 pixel = ((u32*)gfx->atlas->pixels)[src_idx];
|
||||||
if (pixel & 0xFF000000) {
|
if (pixel & 0xFF000000) {
|
||||||
((u32*)gfx->framebuffer)[dest_idx] = pixel;
|
((u32*)gfx->framebuffer)[dest_idx] = pixel;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
u8 pixel = gfx->atlas[src_idx];
|
u8 pixel = gfx->atlas->pixels[src_idx];
|
||||||
if (pixel != 0) {
|
if (pixel != 0) {
|
||||||
gfx->framebuffer[dest_idx] = pixel;
|
gfx->framebuffer[dest_idx] = pixel;
|
||||||
}
|
}
|
||||||
|
|
@ -907,6 +1156,11 @@ void pxl8_3d_set_wireframe(pxl8_gfx* gfx, bool wireframe) {
|
||||||
gfx->wireframe = wireframe;
|
gfx->wireframe = wireframe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine) {
|
||||||
|
if (!gfx) return;
|
||||||
|
gfx->affine_textures = affine;
|
||||||
|
}
|
||||||
|
|
||||||
static inline pxl8_vec4 pxl8_transform_vertex(pxl8_gfx* gfx, pxl8_vec3 pos) {
|
static inline pxl8_vec4 pxl8_transform_vertex(pxl8_gfx* gfx, pxl8_vec3 pos) {
|
||||||
pxl8_vec4 v = {
|
pxl8_vec4 v = {
|
||||||
.x = pos.x,
|
.x = pos.x,
|
||||||
|
|
@ -971,6 +1225,81 @@ static inline void pxl8_fill_scanline(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 pxl8_sample_texture(pxl8_gfx* gfx, u32 texture_id, f32 u, f32 v) {
|
||||||
|
if (!gfx->atlas || texture_id >= gfx->atlas->entry_count) return 0;
|
||||||
|
if (!gfx->atlas->entries[texture_id].active) return 0;
|
||||||
|
|
||||||
|
pxl8_atlas_entry* entry = &gfx->atlas->entries[texture_id];
|
||||||
|
|
||||||
|
u = u - floorf(u);
|
||||||
|
v = v - floorf(v);
|
||||||
|
|
||||||
|
i32 tx = (i32)(u * entry->w) % entry->w;
|
||||||
|
i32 ty = (i32)(v * entry->h) % entry->h;
|
||||||
|
|
||||||
|
i32 atlas_x = entry->x + tx;
|
||||||
|
i32 atlas_y = entry->y + ty;
|
||||||
|
i32 idx = atlas_y * gfx->atlas->width + atlas_x;
|
||||||
|
|
||||||
|
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
||||||
|
return ((u32*)gfx->atlas->pixels)[idx];
|
||||||
|
} else {
|
||||||
|
return gfx->atlas->pixels[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
gfx->zbuffer[idx] = z;
|
||||||
|
pxl8_pixel(gfx, x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void pxl8_draw_flat_bottom_triangle(
|
static void pxl8_draw_flat_bottom_triangle(
|
||||||
pxl8_gfx* gfx,
|
pxl8_gfx* gfx,
|
||||||
i32 x0, i32 y0, f32 z0,
|
i32 x0, i32 y0, f32 z0,
|
||||||
|
|
@ -1087,3 +1416,145 @@ void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
|
||||||
pxl8_draw_flat_top_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color);
|
pxl8_draw_flat_top_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct pxl8_textured_vertex {
|
||||||
|
i32 x, y;
|
||||||
|
f32 z, u, v, w;
|
||||||
|
} pxl8_textured_vertex;
|
||||||
|
|
||||||
|
static void pxl8_draw_flat_bottom_triangle_textured(
|
||||||
|
pxl8_gfx* gfx,
|
||||||
|
pxl8_textured_vertex v0,
|
||||||
|
pxl8_textured_vertex v1,
|
||||||
|
pxl8_textured_vertex v2,
|
||||||
|
u32 texture_id
|
||||||
|
) {
|
||||||
|
if (v1.y == v0.y) return;
|
||||||
|
|
||||||
|
f32 inv_slope_1 = (f32)(v1.x - v0.x) / (f32)(v1.y - v0.y);
|
||||||
|
f32 inv_slope_2 = (f32)(v2.x - v0.x) / (f32)(v2.y - v0.y);
|
||||||
|
f32 inv_slope_z1 = (v1.z - v0.z) / (f32)(v1.y - v0.y);
|
||||||
|
f32 inv_slope_z2 = (v2.z - v0.z) / (f32)(v2.y - v0.y);
|
||||||
|
f32 inv_slope_u1 = (v1.u - v0.u) / (f32)(v1.y - v0.y);
|
||||||
|
f32 inv_slope_u2 = (v2.u - v0.u) / (f32)(v2.y - v0.y);
|
||||||
|
f32 inv_slope_v1 = (v1.v - v0.v) / (f32)(v1.y - v0.y);
|
||||||
|
f32 inv_slope_v2 = (v2.v - v0.v) / (f32)(v2.y - v0.y);
|
||||||
|
f32 inv_slope_w1 = (v1.w - v0.w) / (f32)(v1.y - v0.y);
|
||||||
|
f32 inv_slope_w2 = (v2.w - v0.w) / (f32)(v2.y - v0.y);
|
||||||
|
|
||||||
|
f32 cur_x1 = (f32)v0.x, cur_x2 = (f32)v0.x;
|
||||||
|
f32 cur_z1 = v0.z, cur_z2 = v0.z;
|
||||||
|
f32 cur_u1 = v0.u, cur_u2 = v0.u;
|
||||||
|
f32 cur_v1 = v0.v, cur_v2 = v0.v;
|
||||||
|
f32 cur_w1 = v0.w, cur_w2 = v0.w;
|
||||||
|
|
||||||
|
for (i32 y = v0.y; y <= v1.y; y++) {
|
||||||
|
pxl8_fill_scanline_textured(gfx, y, (i32)cur_x1, (i32)cur_x2,
|
||||||
|
cur_z1, cur_z2, cur_u1, cur_v1, cur_w1, cur_u2, cur_v2, cur_w2, texture_id);
|
||||||
|
cur_x1 += inv_slope_1; cur_x2 += inv_slope_2;
|
||||||
|
cur_z1 += inv_slope_z1; cur_z2 += inv_slope_z2;
|
||||||
|
cur_u1 += inv_slope_u1; cur_u2 += inv_slope_u2;
|
||||||
|
cur_v1 += inv_slope_v1; cur_v2 += inv_slope_v2;
|
||||||
|
cur_w1 += inv_slope_w1; cur_w2 += inv_slope_w2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pxl8_draw_flat_top_triangle_textured(
|
||||||
|
pxl8_gfx* gfx,
|
||||||
|
pxl8_textured_vertex v0,
|
||||||
|
pxl8_textured_vertex v1,
|
||||||
|
pxl8_textured_vertex v2,
|
||||||
|
u32 texture_id
|
||||||
|
) {
|
||||||
|
if (v2.y == v0.y) return;
|
||||||
|
|
||||||
|
f32 inv_slope_1 = (f32)(v2.x - v0.x) / (f32)(v2.y - v0.y);
|
||||||
|
f32 inv_slope_2 = (f32)(v2.x - v1.x) / (f32)(v2.y - v1.y);
|
||||||
|
f32 inv_slope_z1 = (v2.z - v0.z) / (f32)(v2.y - v0.y);
|
||||||
|
f32 inv_slope_z2 = (v2.z - v1.z) / (f32)(v2.y - v1.y);
|
||||||
|
f32 inv_slope_u1 = (v2.u - v0.u) / (f32)(v2.y - v0.y);
|
||||||
|
f32 inv_slope_u2 = (v2.u - v1.u) / (f32)(v2.y - v1.y);
|
||||||
|
f32 inv_slope_v1 = (v2.v - v0.v) / (f32)(v2.y - v0.y);
|
||||||
|
f32 inv_slope_v2 = (v2.v - v1.v) / (f32)(v2.y - v1.y);
|
||||||
|
f32 inv_slope_w1 = (v2.w - v0.w) / (f32)(v2.y - v0.y);
|
||||||
|
f32 inv_slope_w2 = (v2.w - v1.w) / (f32)(v2.y - v1.y);
|
||||||
|
|
||||||
|
f32 cur_x1 = (f32)v2.x, cur_x2 = (f32)v2.x;
|
||||||
|
f32 cur_z1 = v2.z, cur_z2 = v2.z;
|
||||||
|
f32 cur_u1 = v2.u, cur_u2 = v2.u;
|
||||||
|
f32 cur_v1 = v2.v, cur_v2 = v2.v;
|
||||||
|
f32 cur_w1 = v2.w, cur_w2 = v2.w;
|
||||||
|
|
||||||
|
for (i32 y = v2.y; y > v0.y; y--) {
|
||||||
|
pxl8_fill_scanline_textured(gfx, y, (i32)cur_x1, (i32)cur_x2,
|
||||||
|
cur_z1, cur_z2, cur_u1, cur_v1, cur_w1, cur_u2, cur_v2, cur_w2, texture_id);
|
||||||
|
cur_x1 -= inv_slope_1; cur_x2 -= inv_slope_2;
|
||||||
|
cur_z1 -= inv_slope_z1; cur_z2 -= inv_slope_z2;
|
||||||
|
cur_u1 -= inv_slope_u1; cur_u2 -= inv_slope_u2;
|
||||||
|
cur_v1 -= inv_slope_v1; cur_v2 -= inv_slope_v2;
|
||||||
|
cur_w1 -= inv_slope_w1; cur_w2 -= inv_slope_w2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, f32 u0, f32 v0f, f32 u1, f32 v1f, f32 u2, f32 v2f, u32 texture_id) {
|
||||||
|
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_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 = cv0.w;
|
||||||
|
|
||||||
|
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 = cv1.w;
|
||||||
|
|
||||||
|
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 = cv2.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) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tv[1].y == tv[2].y) {
|
||||||
|
pxl8_draw_flat_bottom_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id);
|
||||||
|
} else if (tv[0].y == tv[1].y) {
|
||||||
|
pxl8_draw_flat_top_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id);
|
||||||
|
} 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_draw_flat_bottom_triangle_textured(gfx, tv[0], tv[1], v3, texture_id);
|
||||||
|
pxl8_draw_flat_top_triangle_textured(gfx, tv[1], v3, tv[2], texture_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "pxl8_math.h"
|
#include "pxl8_math.h"
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
typedef struct pxl8_atlas pxl8_atlas;
|
||||||
typedef struct pxl8_gfx pxl8_gfx;
|
typedef struct pxl8_gfx pxl8_gfx;
|
||||||
|
|
||||||
typedef enum pxl8_blend_mode {
|
typedef enum pxl8_blend_mode {
|
||||||
|
|
@ -48,6 +49,7 @@ typedef struct pxl8_effects {
|
||||||
|
|
||||||
typedef struct pxl8_triangle {
|
typedef struct pxl8_triangle {
|
||||||
pxl8_vertex v[3];
|
pxl8_vertex v[3];
|
||||||
|
u32 texture_id;
|
||||||
} pxl8_triangle;
|
} pxl8_triangle;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
@ -61,12 +63,13 @@ pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx);
|
||||||
pxl8_color_mode pxl8_gfx_get_color_mode(pxl8_gfx* gfx);
|
pxl8_color_mode pxl8_gfx_get_color_mode(pxl8_gfx* gfx);
|
||||||
u8* pxl8_gfx_get_framebuffer(pxl8_gfx* gfx);
|
u8* pxl8_gfx_get_framebuffer(pxl8_gfx* gfx);
|
||||||
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx);
|
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx);
|
||||||
|
u32 pxl8_gfx_get_palette_size(const pxl8_gfx* gfx);
|
||||||
void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height);
|
void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height);
|
||||||
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx);
|
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx);
|
||||||
pxl8_result pxl8_gfx_init_atlas(pxl8_gfx* gfx, u32 width, u32 height);
|
|
||||||
pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx);
|
pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx);
|
||||||
pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path);
|
pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path);
|
||||||
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path);
|
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path);
|
||||||
|
pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width, u32 height);
|
||||||
void pxl8_gfx_present(pxl8_gfx* gfx);
|
void pxl8_gfx_present(pxl8_gfx* gfx);
|
||||||
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
|
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
|
||||||
void pxl8_gfx_upload_atlas(pxl8_gfx* gfx);
|
void pxl8_gfx_upload_atlas(pxl8_gfx* gfx);
|
||||||
|
|
@ -96,6 +99,8 @@ void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx);
|
||||||
void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color);
|
void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color);
|
||||||
void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri);
|
void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri);
|
||||||
void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);
|
void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);
|
||||||
|
void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, f32 u0, f32 v0f, f32 u1, f32 v1f, f32 u2, f32 v2f, u32 texture_id);
|
||||||
|
void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine);
|
||||||
void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling);
|
void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling);
|
||||||
void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat);
|
void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat);
|
||||||
void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);
|
void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,21 @@
|
||||||
#include "pxl8_simd.h"
|
#include "pxl8_simd.h"
|
||||||
|
|
||||||
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b) {
|
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b) {
|
||||||
return (pxl8_vec2) {
|
return (pxl8_vec2){
|
||||||
.x = a.x + b.x,
|
.x = a.x + b.x,
|
||||||
.y = a.y + b.y,
|
.y = a.y + b.y,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec2 pxl8_vec2_sub(pxl8_vec2 a, pxl8_vec2 b) {
|
pxl8_vec2 pxl8_vec2_sub(pxl8_vec2 a, pxl8_vec2 b) {
|
||||||
return (pxl8_vec2) {
|
return (pxl8_vec2){
|
||||||
.x = a.x - b.x,
|
.x = a.x - b.x,
|
||||||
.y = a.y - b.y,
|
.y = a.y - b.y,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec2 pxl8_vec2_scale(pxl8_vec2 v, f32 s) {
|
pxl8_vec2 pxl8_vec2_scale(pxl8_vec2 v, f32 s) {
|
||||||
return (pxl8_vec2) {
|
return (pxl8_vec2){
|
||||||
.x = v.x * s,
|
.x = v.x * s,
|
||||||
.y = v.y * s,
|
.y = v.y * s,
|
||||||
};
|
};
|
||||||
|
|
@ -34,7 +34,9 @@ f32 pxl8_vec2_length(pxl8_vec2 v) {
|
||||||
|
|
||||||
pxl8_vec2 pxl8_vec2_normalize(pxl8_vec2 v) {
|
pxl8_vec2 pxl8_vec2_normalize(pxl8_vec2 v) {
|
||||||
f32 len = pxl8_vec2_length(v);
|
f32 len = pxl8_vec2_length(v);
|
||||||
|
|
||||||
if (len < 1e-6f) return (pxl8_vec2){0};
|
if (len < 1e-6f) return (pxl8_vec2){0};
|
||||||
|
|
||||||
return pxl8_vec2_scale(v, 1.0f / len);
|
return pxl8_vec2_scale(v, 1.0f / len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,7 +45,7 @@ pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b) {
|
||||||
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
|
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
|
||||||
pxl8_simd_vec_f32 result = pxl8_simd_add_f32(va, vb);
|
pxl8_simd_vec_f32 result = pxl8_simd_add_f32(va, vb);
|
||||||
|
|
||||||
return (pxl8_vec3) {
|
return (pxl8_vec3){
|
||||||
.x = result.f32_array[0],
|
.x = result.f32_array[0],
|
||||||
.y = result.f32_array[1],
|
.y = result.f32_array[1],
|
||||||
.z = result.f32_array[2],
|
.z = result.f32_array[2],
|
||||||
|
|
@ -55,7 +57,7 @@ pxl8_vec3 pxl8_vec3_sub(pxl8_vec3 a, pxl8_vec3 b) {
|
||||||
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
|
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
|
||||||
pxl8_simd_vec_f32 result = pxl8_simd_sub_f32(va, vb);
|
pxl8_simd_vec_f32 result = pxl8_simd_sub_f32(va, vb);
|
||||||
|
|
||||||
return (pxl8_vec3) {
|
return (pxl8_vec3){
|
||||||
.x = result.f32_array[0],
|
.x = result.f32_array[0],
|
||||||
.y = result.f32_array[1],
|
.y = result.f32_array[1],
|
||||||
.z = result.f32_array[2],
|
.z = result.f32_array[2],
|
||||||
|
|
@ -66,7 +68,7 @@ pxl8_vec3 pxl8_vec3_scale(pxl8_vec3 v, f32 s) {
|
||||||
pxl8_simd_vec_f32 vv = pxl8_simd_set_f32(v.x, v.y, v.z, 0);
|
pxl8_simd_vec_f32 vv = pxl8_simd_set_f32(v.x, v.y, v.z, 0);
|
||||||
pxl8_simd_vec_f32 result = pxl8_simd_scale_f32(vv, s);
|
pxl8_simd_vec_f32 result = pxl8_simd_scale_f32(vv, s);
|
||||||
|
|
||||||
return (pxl8_vec3) {
|
return (pxl8_vec3){
|
||||||
.x = result.f32_array[0],
|
.x = result.f32_array[0],
|
||||||
.y = result.f32_array[1],
|
.y = result.f32_array[1],
|
||||||
.z = result.f32_array[2],
|
.z = result.f32_array[2],
|
||||||
|
|
@ -76,11 +78,12 @@ pxl8_vec3 pxl8_vec3_scale(pxl8_vec3 v, f32 s) {
|
||||||
f32 pxl8_vec3_dot(pxl8_vec3 a, pxl8_vec3 b) {
|
f32 pxl8_vec3_dot(pxl8_vec3 a, pxl8_vec3 b) {
|
||||||
pxl8_simd_vec_f32 va = pxl8_simd_set_f32(a.x, a.y, a.z, 0);
|
pxl8_simd_vec_f32 va = pxl8_simd_set_f32(a.x, a.y, a.z, 0);
|
||||||
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
|
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
|
||||||
|
|
||||||
return pxl8_simd_dot3_f32(va, vb);
|
return pxl8_simd_dot3_f32(va, vb);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec3 pxl8_vec3_cross(pxl8_vec3 a, pxl8_vec3 b) {
|
pxl8_vec3 pxl8_vec3_cross(pxl8_vec3 a, pxl8_vec3 b) {
|
||||||
return (pxl8_vec3) {
|
return (pxl8_vec3){
|
||||||
.x = a.y * b.z - a.z * b.y,
|
.x = a.y * b.z - a.z * b.y,
|
||||||
.y = a.z * b.x - a.x * b.z,
|
.y = a.z * b.x - a.x * b.z,
|
||||||
.z = a.x * b.y - a.y * b.x,
|
.z = a.x * b.y - a.y * b.x,
|
||||||
|
|
@ -93,18 +96,23 @@ f32 pxl8_vec3_length(pxl8_vec3 v) {
|
||||||
|
|
||||||
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
|
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
|
||||||
f32 len = pxl8_vec3_length(v);
|
f32 len = pxl8_vec3_length(v);
|
||||||
|
|
||||||
if (len < 1e-6f) return (pxl8_vec3){0};
|
if (len < 1e-6f) return (pxl8_vec3){0};
|
||||||
|
|
||||||
return pxl8_vec3_scale(v, 1.0f / len);
|
return pxl8_vec3_scale(v, 1.0f / len);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_identity(void) {
|
pxl8_mat4 pxl8_mat4_identity(void) {
|
||||||
pxl8_mat4 mat = {0};
|
pxl8_mat4 mat = {0};
|
||||||
|
|
||||||
mat.m[0] = mat.m[5] = mat.m[10] = mat.m[15] = 1.0f;
|
mat.m[0] = mat.m[5] = mat.m[10] = mat.m[15] = 1.0f;
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
|
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
|
||||||
pxl8_mat4 result = {0};
|
pxl8_mat4 mat = {0};
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
for (i32 i = 0; i < 4; i++) {
|
||||||
for (i32 j = 0; j < 4; j++) {
|
for (i32 j = 0; j < 4; j++) {
|
||||||
pxl8_simd_vec_f32 row = pxl8_simd_set_f32(
|
pxl8_simd_vec_f32 row = pxl8_simd_set_f32(
|
||||||
|
|
@ -113,34 +121,35 @@ pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
|
||||||
pxl8_simd_vec_f32 col = pxl8_simd_set_f32(
|
pxl8_simd_vec_f32 col = pxl8_simd_set_f32(
|
||||||
b.m[0 * 4 + j], b.m[1 * 4 + j], b.m[2 * 4 + j], b.m[3 * 4 + j]
|
b.m[0 * 4 + j], b.m[1 * 4 + j], b.m[2 * 4 + j], b.m[3 * 4 + j]
|
||||||
);
|
);
|
||||||
result.m[i * 4 + j] = pxl8_simd_dot4_f32(row, col);
|
mat.m[i * 4 + j] = pxl8_simd_dot4_f32(row, col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
||||||
pxl8_simd_vec_f32 vec = pxl8_simd_set_f32(v.x, v.y, v.z, v.w);
|
|
||||||
pxl8_vec4 result;
|
|
||||||
|
|
||||||
pxl8_simd_vec_f32 row0 = pxl8_simd_set_f32(m.m[0], m.m[1], m.m[2], m.m[3]);
|
pxl8_simd_vec_f32 row0 = pxl8_simd_set_f32(m.m[0], m.m[1], m.m[2], m.m[3]);
|
||||||
pxl8_simd_vec_f32 row1 = pxl8_simd_set_f32(m.m[4], m.m[5], m.m[6], m.m[7]);
|
pxl8_simd_vec_f32 row1 = pxl8_simd_set_f32(m.m[4], m.m[5], m.m[6], m.m[7]);
|
||||||
pxl8_simd_vec_f32 row2 = pxl8_simd_set_f32(m.m[8], m.m[9], m.m[10], m.m[11]);
|
pxl8_simd_vec_f32 row2 = pxl8_simd_set_f32(m.m[8], m.m[9], m.m[10], m.m[11]);
|
||||||
pxl8_simd_vec_f32 row3 = pxl8_simd_set_f32(m.m[12], m.m[13], m.m[14], m.m[15]);
|
pxl8_simd_vec_f32 row3 = pxl8_simd_set_f32(m.m[12], m.m[13], m.m[14], m.m[15]);
|
||||||
|
pxl8_simd_vec_f32 vec = pxl8_simd_set_f32(v.x, v.y, v.z, v.w);
|
||||||
|
|
||||||
result.x = pxl8_simd_dot4_f32(row0, vec);
|
return (pxl8_vec4){
|
||||||
result.y = pxl8_simd_dot4_f32(row1, vec);
|
.x = pxl8_simd_dot4_f32(row0, vec),
|
||||||
result.z = pxl8_simd_dot4_f32(row2, vec);
|
.y = pxl8_simd_dot4_f32(row1, vec),
|
||||||
result.w = pxl8_simd_dot4_f32(row3, vec);
|
.z = pxl8_simd_dot4_f32(row2, vec),
|
||||||
|
.w = pxl8_simd_dot4_f32(row3, vec),
|
||||||
return result;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z) {
|
pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z) {
|
||||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||||
|
|
||||||
mat.m[3] = x;
|
mat.m[3] = x;
|
||||||
mat.m[7] = y;
|
mat.m[7] = y;
|
||||||
mat.m[11] = z;
|
mat.m[11] = z;
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,10 +157,12 @@ pxl8_mat4 pxl8_mat4_rotate_x(f32 angle) {
|
||||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||||
f32 c = cosf(angle);
|
f32 c = cosf(angle);
|
||||||
f32 s = sinf(angle);
|
f32 s = sinf(angle);
|
||||||
|
|
||||||
mat.m[5] = c;
|
mat.m[5] = c;
|
||||||
mat.m[6] = -s;
|
mat.m[6] = -s;
|
||||||
mat.m[9] = s;
|
mat.m[9] = s;
|
||||||
mat.m[10] = c;
|
mat.m[10] = c;
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,10 +170,12 @@ pxl8_mat4 pxl8_mat4_rotate_y(f32 angle) {
|
||||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||||
f32 c = cosf(angle);
|
f32 c = cosf(angle);
|
||||||
f32 s = sinf(angle);
|
f32 s = sinf(angle);
|
||||||
|
|
||||||
mat.m[0] = c;
|
mat.m[0] = c;
|
||||||
mat.m[2] = s;
|
mat.m[2] = s;
|
||||||
mat.m[8] = -s;
|
mat.m[8] = -s;
|
||||||
mat.m[10] = c;
|
mat.m[10] = c;
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,18 +183,22 @@ pxl8_mat4 pxl8_mat4_rotate_z(f32 angle) {
|
||||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||||
f32 c = cosf(angle);
|
f32 c = cosf(angle);
|
||||||
f32 s = sinf(angle);
|
f32 s = sinf(angle);
|
||||||
|
|
||||||
mat.m[0] = c;
|
mat.m[0] = c;
|
||||||
mat.m[1] = -s;
|
mat.m[1] = -s;
|
||||||
mat.m[4] = s;
|
mat.m[4] = s;
|
||||||
mat.m[5] = c;
|
mat.m[5] = c;
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z) {
|
pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z) {
|
||||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||||
|
|
||||||
mat.m[0] = x;
|
mat.m[0] = x;
|
||||||
mat.m[5] = y;
|
mat.m[5] = y;
|
||||||
mat.m[10] = z;
|
mat.m[10] = z;
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,11 +230,11 @@ pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up) {
|
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up) {
|
||||||
|
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||||
pxl8_vec3 f = pxl8_vec3_normalize(pxl8_vec3_sub(center, eye));
|
pxl8_vec3 f = pxl8_vec3_normalize(pxl8_vec3_sub(center, eye));
|
||||||
pxl8_vec3 s = pxl8_vec3_normalize(pxl8_vec3_cross(f, up));
|
pxl8_vec3 s = pxl8_vec3_normalize(pxl8_vec3_cross(f, up));
|
||||||
pxl8_vec3 u = pxl8_vec3_cross(s, f);
|
pxl8_vec3 u = pxl8_vec3_cross(s, f);
|
||||||
|
|
||||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
|
||||||
mat.m[0] = s.x;
|
mat.m[0] = s.x;
|
||||||
mat.m[1] = s.y;
|
mat.m[1] = s.y;
|
||||||
mat.m[2] = s.z;
|
mat.m[2] = s.z;
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"void pxl8_gfx_fade_palette(pxl8_gfx* ctx, u8 start, u8 count, f32 amount, u32 target_color);\n"
|
"void pxl8_gfx_fade_palette(pxl8_gfx* ctx, u8 start, u8 count, f32 amount, u32 target_color);\n"
|
||||||
"i32 pxl8_gfx_load_palette(pxl8_gfx* ctx, const char* filepath);\n"
|
"i32 pxl8_gfx_load_palette(pxl8_gfx* ctx, const char* filepath);\n"
|
||||||
"i32 pxl8_gfx_load_sprite(pxl8_gfx* ctx, const char* filepath, u32* sprite_id);\n"
|
"i32 pxl8_gfx_load_sprite(pxl8_gfx* ctx, const char* filepath, u32* sprite_id);\n"
|
||||||
|
"i32 pxl8_gfx_create_texture(pxl8_gfx* ctx, const u8* pixels, u32 width, u32 height);\n"
|
||||||
|
"void pxl8_gfx_upload_atlas(pxl8_gfx* ctx);\n"
|
||||||
"typedef struct pxl8_input_state pxl8_input_state;\n"
|
"typedef struct pxl8_input_state pxl8_input_state;\n"
|
||||||
"bool pxl8_key_down(const pxl8_input_state* input, i32 key);\n"
|
"bool pxl8_key_down(const pxl8_input_state* input, i32 key);\n"
|
||||||
"bool pxl8_key_pressed(const pxl8_input_state* input, i32 key);\n"
|
"bool pxl8_key_pressed(const pxl8_input_state* input, i32 key);\n"
|
||||||
|
|
@ -133,6 +135,8 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx);\n"
|
"void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx);\n"
|
||||||
"void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color);\n"
|
"void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color);\n"
|
||||||
"void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);\n"
|
"void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);\n"
|
||||||
|
"void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, f32 u0, f32 v0, f32 u1, f32 v1, f32 u2, f32 v2, u32 texture_id);\n"
|
||||||
|
"void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine);\n"
|
||||||
"void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling);\n"
|
"void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling);\n"
|
||||||
"void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
"void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
||||||
"void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
"void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,11 @@ struct pxl8_particles {
|
||||||
void* userdata;
|
void* userdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
void pxl8_vfx_plasma(pxl8_gfx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset) {
|
void pxl8_vfx_plasma(pxl8_gfx* gfx, f32 time, f32 scale1, f32 scale2, u8 palette_offset) {
|
||||||
if (!ctx || !pxl8_gfx_get_framebuffer(ctx)) return;
|
if (!gfx || !pxl8_gfx_get_framebuffer(gfx)) return;
|
||||||
|
|
||||||
for (i32 y = 0; y < pxl8_gfx_get_height(ctx); y++) {
|
for (i32 y = 0; y < pxl8_gfx_get_height(gfx); y++) {
|
||||||
for (i32 x = 0; x < pxl8_gfx_get_width(ctx); x++) {
|
for (i32 x = 0; x < pxl8_gfx_get_width(gfx); x++) {
|
||||||
f32 v1 = sinf(x * scale1 + time);
|
f32 v1 = sinf(x * scale1 + time);
|
||||||
f32 v2 = sinf(y * scale1 + time * 0.7f);
|
f32 v2 = sinf(y * scale1 + time * 0.7f);
|
||||||
f32 v3 = sinf((x + y) * scale2 + time * 1.3f);
|
f32 v3 = sinf((x + y) * scale2 + time * 1.3f);
|
||||||
|
|
@ -44,13 +44,13 @@ void pxl8_vfx_plasma(pxl8_gfx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette
|
||||||
if (normalized > 1.0f) normalized = 1.0f;
|
if (normalized > 1.0f) normalized = 1.0f;
|
||||||
u8 color = palette_offset + (u8)(15.0f * normalized);
|
u8 color = palette_offset + (u8)(15.0f * normalized);
|
||||||
|
|
||||||
pxl8_pixel(ctx, x, y, color);
|
pxl8_pixel(gfx, x, y, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_vfx_raster_bars(pxl8_gfx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time) {
|
void pxl8_vfx_raster_bars(pxl8_gfx* gfx, pxl8_raster_bar* bars, u32 bar_count, f32 time) {
|
||||||
if (!ctx || !bars) return;
|
if (!gfx || !bars) return;
|
||||||
|
|
||||||
for (u32 i = 0; i < bar_count; i++) {
|
for (u32 i = 0; i < bar_count; i++) {
|
||||||
pxl8_raster_bar* bar = &bars[i];
|
pxl8_raster_bar* bar = &bars[i];
|
||||||
|
|
@ -72,24 +72,24 @@ void pxl8_vfx_raster_bars(pxl8_gfx* ctx, pxl8_raster_bar* bars, u32 bar_count, f
|
||||||
color_idx = bar->color - 1;
|
color_idx = bar->color - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_rect_fill(ctx, 0, y_int + dy, pxl8_gfx_get_width(ctx), 1, color_idx);
|
pxl8_rect_fill(gfx, 0, y_int + dy, pxl8_gfx_get_width(gfx), 1, color_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
|
void pxl8_vfx_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy) {
|
||||||
if (!ctx || !pxl8_gfx_get_framebuffer(ctx)) return;
|
if (!gfx || !pxl8_gfx_get_framebuffer(gfx)) return;
|
||||||
|
|
||||||
f32 cos_a = cosf(angle);
|
f32 cos_a = cosf(angle);
|
||||||
f32 sin_a = sinf(angle);
|
f32 sin_a = sinf(angle);
|
||||||
|
|
||||||
u8* temp_buffer = (u8*)SDL_malloc(pxl8_gfx_get_width(ctx) * pxl8_gfx_get_height(ctx));
|
u8* temp_buffer = (u8*)SDL_malloc(pxl8_gfx_get_width(gfx) * pxl8_gfx_get_height(gfx));
|
||||||
if (!temp_buffer) return;
|
if (!temp_buffer) return;
|
||||||
|
|
||||||
SDL_memcpy(temp_buffer, pxl8_gfx_get_framebuffer(ctx), pxl8_gfx_get_width(ctx) * pxl8_gfx_get_height(ctx));
|
SDL_memcpy(temp_buffer, pxl8_gfx_get_framebuffer(gfx), pxl8_gfx_get_width(gfx) * pxl8_gfx_get_height(gfx));
|
||||||
|
|
||||||
for (i32 y = 0; y < pxl8_gfx_get_height(ctx); y++) {
|
for (i32 y = 0; y < pxl8_gfx_get_height(gfx); y++) {
|
||||||
for (i32 x = 0; x < pxl8_gfx_get_width(ctx); x++) {
|
for (i32 x = 0; x < pxl8_gfx_get_width(gfx); x++) {
|
||||||
f32 dx = x - cx;
|
f32 dx = x - cx;
|
||||||
f32 dy = y - cy;
|
f32 dy = y - cy;
|
||||||
|
|
||||||
|
|
@ -99,8 +99,8 @@ void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
|
||||||
i32 sx = (i32)src_x;
|
i32 sx = (i32)src_x;
|
||||||
i32 sy = (i32)src_y;
|
i32 sy = (i32)src_y;
|
||||||
|
|
||||||
if (sx >= 0 && sx < pxl8_gfx_get_width(ctx) && sy >= 0 && sy < pxl8_gfx_get_height(ctx)) {
|
if (sx >= 0 && sx < pxl8_gfx_get_width(gfx) && sy >= 0 && sy < pxl8_gfx_get_height(gfx)) {
|
||||||
pxl8_gfx_get_framebuffer(ctx)[y * pxl8_gfx_get_width(ctx) + x] = temp_buffer[sy * pxl8_gfx_get_width(ctx) + sx];
|
pxl8_gfx_get_framebuffer(gfx)[y * pxl8_gfx_get_width(gfx) + x] = temp_buffer[sy * pxl8_gfx_get_width(gfx) + sx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,38 +108,41 @@ void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
|
||||||
SDL_free(temp_buffer);
|
SDL_free(temp_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_vfx_tunnel(pxl8_gfx* ctx, f32 time, f32 speed, f32 twist) {
|
void pxl8_vfx_tunnel(pxl8_gfx* gfx, f32 time, f32 speed, f32 twist) {
|
||||||
if (!ctx || !pxl8_gfx_get_framebuffer(ctx)) return;
|
if (!gfx || !pxl8_gfx_get_framebuffer(gfx)) return;
|
||||||
|
|
||||||
f32 cx = pxl8_gfx_get_width(ctx) / 2.0f;
|
f32 cx = pxl8_gfx_get_width(gfx) / 2.0f;
|
||||||
f32 cy = pxl8_gfx_get_height(ctx) / 2.0f;
|
f32 cy = pxl8_gfx_get_height(gfx) / 2.0f;
|
||||||
|
u32 palette_size = pxl8_gfx_get_palette_size(gfx);
|
||||||
for (i32 y = 0; y < pxl8_gfx_get_height(ctx); y++) {
|
if (palette_size == 0) palette_size = 256;
|
||||||
for (i32 x = 0; x < pxl8_gfx_get_width(ctx); x++) {
|
|
||||||
|
for (i32 y = 0; y < pxl8_gfx_get_height(gfx); y++) {
|
||||||
|
for (i32 x = 0; x < pxl8_gfx_get_width(gfx); x++) {
|
||||||
f32 dx = x - cx;
|
f32 dx = x - cx;
|
||||||
f32 dy = y - cy;
|
f32 dy = y - cy;
|
||||||
f32 dist = sqrtf(dx * dx + dy * dy);
|
f32 dist = sqrtf(dx * dx + dy * dy);
|
||||||
|
|
||||||
if (dist > 1.0f) {
|
if (dist > 1.0f) {
|
||||||
f32 angle = atan2f(dy, dx);
|
f32 angle = atan2f(dy, dx);
|
||||||
f32 u = angle * 0.159f + twist * sinf(time * 0.5f);
|
f32 u = angle * 0.159f + twist * sinf(time * 0.5f);
|
||||||
f32 v = 256.0f / dist + time * speed;
|
f32 v = 256.0f / dist + time * speed;
|
||||||
|
|
||||||
u8 tx = (u8)((i32)(u * 256.0f) & 0xFF);
|
u8 tx = (u8)((i32)(u * 256.0f) & 0xFF);
|
||||||
u8 ty = (u8)((i32)(v * 256.0f) & 0xFF);
|
u8 ty = (u8)((i32)(v * 256.0f) & 0xFF);
|
||||||
u8 color = tx ^ ty;
|
u8 pattern = tx ^ ty;
|
||||||
|
u8 color = (pattern / 8) % palette_size;
|
||||||
pxl8_pixel(ctx, x, y, color);
|
|
||||||
|
pxl8_pixel(gfx, x, y, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_vfx_water_ripple(pxl8_gfx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping) {
|
void pxl8_vfx_water_ripple(pxl8_gfx* gfx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping) {
|
||||||
if (!ctx || !height_map) return;
|
if (!gfx || !height_map) return;
|
||||||
|
|
||||||
i32 w = pxl8_gfx_get_width(ctx);
|
i32 w = pxl8_gfx_get_width(gfx);
|
||||||
i32 h = pxl8_gfx_get_height(ctx);
|
i32 h = pxl8_gfx_get_height(gfx);
|
||||||
|
|
||||||
static f32* prev_height = NULL;
|
static f32* prev_height = NULL;
|
||||||
if (!prev_height) {
|
if (!prev_height) {
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@ void pxl8_vfx_snow(pxl8_particles* particles, i32 width, f32 wind);
|
||||||
void pxl8_vfx_sparks(pxl8_particles* particles, i32 x, i32 y, u32 color);
|
void pxl8_vfx_sparks(pxl8_particles* particles, i32 x, i32 y, u32 color);
|
||||||
void pxl8_vfx_starfield(pxl8_particles* particles, f32 speed, f32 spread);
|
void pxl8_vfx_starfield(pxl8_particles* particles, f32 speed, f32 spread);
|
||||||
|
|
||||||
void pxl8_vfx_plasma(pxl8_gfx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset);
|
void pxl8_vfx_plasma(pxl8_gfx* gfx, f32 time, f32 scale1, f32 scale2, u8 palette_offset);
|
||||||
void pxl8_vfx_raster_bars(pxl8_gfx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time);
|
void pxl8_vfx_raster_bars(pxl8_gfx* gfx, pxl8_raster_bar* bars, u32 bar_count, f32 time);
|
||||||
void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy);
|
void pxl8_vfx_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy);
|
||||||
void pxl8_vfx_tunnel(pxl8_gfx* ctx, f32 time, f32 speed, f32 twist);
|
void pxl8_vfx_tunnel(pxl8_gfx* gfx, f32 time, f32 speed, f32 twist);
|
||||||
void pxl8_vfx_water_ripple(pxl8_gfx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);
|
void pxl8_vfx_water_ripple(pxl8_gfx* gfx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue