wip procgen
This commit is contained in:
parent
a653eae745
commit
79a678f162
18 changed files with 1317 additions and 127 deletions
|
|
@ -1,9 +1,9 @@
|
||||||
(local pxl8 (require :pxl8))
|
(local pxl8 (require :pxl8))
|
||||||
(local bsp_world (require :mod.bsp_world))
|
|
||||||
(local cube3d (require :mod.cube3d))
|
(local cube3d (require :mod.cube3d))
|
||||||
|
(local worldgen (require :mod.worldgen))
|
||||||
|
|
||||||
(var time 0)
|
(var time 0)
|
||||||
(var current-effect 1)
|
(var active-demo :logo)
|
||||||
(var particles nil)
|
(var particles nil)
|
||||||
(var fire-init false)
|
(var fire-init false)
|
||||||
(var rain-init false)
|
(var rain-init false)
|
||||||
|
|
@ -17,77 +17,68 @@
|
||||||
(var logo-sprite nil)
|
(var logo-sprite nil)
|
||||||
|
|
||||||
(global init (fn []
|
(global init (fn []
|
||||||
(bsp_world.init)
|
|
||||||
(cube3d.init)
|
(cube3d.init)
|
||||||
|
(worldgen.init)
|
||||||
(pxl8.load_palette "res/sprites/pxl8_logo.ase")
|
(pxl8.load_palette "res/sprites/pxl8_logo.ase")
|
||||||
(set logo-sprite (pxl8.load_sprite "res/sprites/pxl8_logo.ase"))
|
(set logo-sprite (pxl8.load_sprite "res/sprites/pxl8_logo.ase"))
|
||||||
(set particles (pxl8.particles_new 1000))))
|
(set particles (pxl8.particles_new 1000))))
|
||||||
|
|
||||||
(global update (fn [dt]
|
(global update (fn [dt]
|
||||||
(bsp_world.update dt)
|
|
||||||
(set time (+ time dt))
|
(set time (+ time dt))
|
||||||
|
|
||||||
(when (pxl8.key_pressed "1")
|
(when (pxl8.key_pressed "1") (set active-demo :logo))
|
||||||
(set current-effect 1))
|
(when (pxl8.key_pressed "2") (set active-demo :plasma))
|
||||||
(when (pxl8.key_pressed "2")
|
(when (pxl8.key_pressed "3") (set active-demo :tunnel))
|
||||||
(set current-effect 2))
|
(when (pxl8.key_pressed "4") (set active-demo :raster))
|
||||||
(when (pxl8.key_pressed "3")
|
|
||||||
(set current-effect 3))
|
|
||||||
(when (pxl8.key_pressed "4")
|
|
||||||
(set current-effect 4))
|
|
||||||
(when (pxl8.key_pressed "5")
|
(when (pxl8.key_pressed "5")
|
||||||
(set current-effect 5)
|
(set active-demo :fire)
|
||||||
(set fire-init false))
|
(set fire-init false))
|
||||||
(when (pxl8.key_pressed "6")
|
(when (pxl8.key_pressed "6")
|
||||||
(set current-effect 6)
|
(set active-demo :rain)
|
||||||
(set rain-init false))
|
(set rain-init false))
|
||||||
(when (pxl8.key_pressed "7")
|
(when (pxl8.key_pressed "7")
|
||||||
(set current-effect 7)
|
(set active-demo :snow)
|
||||||
(set snow-init false))
|
(set snow-init false))
|
||||||
(when (pxl8.key_pressed "8")
|
(when (pxl8.key_pressed "8") (set active-demo :cube3d))
|
||||||
(set current-effect 8))
|
(when (pxl8.key_pressed "9") (set active-demo :worldgen))
|
||||||
(when (pxl8.key_pressed "9")
|
(when (pxl8.key_pressed "=")
|
||||||
(set current-effect 0))
|
|
||||||
(when (pxl8.key_pressed "0")
|
|
||||||
(set use-famicube-palette (not use-famicube-palette))
|
(set use-famicube-palette (not use-famicube-palette))
|
||||||
(local palette-path (if use-famicube-palette "res/palettes/famicube.ase" "res/sprites/pxl8_logo.ase"))
|
(local palette-path (if use-famicube-palette "res/palettes/famicube.ase" "res/sprites/pxl8_logo.ase"))
|
||||||
(pxl8.load_palette palette-path))
|
(pxl8.load_palette palette-path))
|
||||||
|
|
||||||
(case current-effect
|
(case active-demo
|
||||||
1 (do
|
:logo (do
|
||||||
(set logo-x (+ logo-x (* logo-dx dt)))
|
(set logo-x (+ logo-x (* logo-dx dt)))
|
||||||
(set logo-y (+ logo-y (* logo-dy dt)))
|
(set logo-y (+ logo-y (* logo-dy dt)))
|
||||||
(when (or (< logo-x 0) (> logo-x 512))
|
(when (or (< logo-x 0) (> logo-x 512))
|
||||||
(set logo-dx (- logo-dx)))
|
(set logo-dx (- logo-dx)))
|
||||||
(when (or (< logo-y 0) (> logo-y 296))
|
(when (or (< logo-y 0) (> logo-y 296))
|
||||||
(set logo-dy (- logo-dy))))
|
(set logo-dy (- logo-dy))))
|
||||||
8 (cube3d.update dt)
|
:cube3d (cube3d.update dt)
|
||||||
_ nil)
|
:worldgen (worldgen.update dt))
|
||||||
|
|
||||||
(when particles
|
(when particles
|
||||||
(pxl8.particles_update particles dt))))
|
(pxl8.particles_update particles dt))))
|
||||||
|
|
||||||
(global frame (fn []
|
(global frame (fn []
|
||||||
(case current-effect
|
(case active-demo
|
||||||
0 (bsp_world.frame)
|
:logo (do
|
||||||
|
|
||||||
1 (do
|
|
||||||
(pxl8.clr 0)
|
(pxl8.clr 0)
|
||||||
(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 1)
|
:plasma (pxl8.vfx_plasma time 0.10 0.04 1)
|
||||||
|
|
||||||
3 (pxl8.vfx_tunnel time 2.0 0.25)
|
:tunnel (pxl8.vfx_tunnel time 2.0 0.25)
|
||||||
|
|
||||||
4 (do
|
:raster (do
|
||||||
(pxl8.clr 0)
|
(pxl8.clr 0)
|
||||||
(local bars [{:base_y 60 :amplitude 30 :height 16 :speed 2.0 :phase 0 :color 1 :fade_color 18}
|
(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 1 :fade_color 27}
|
{: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 1 :fade_color 24}])
|
{: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
|
:fire (do
|
||||||
(pxl8.clr 0)
|
(pxl8.clr 0)
|
||||||
(when particles
|
(when particles
|
||||||
(when (not fire-init)
|
(when (not fire-init)
|
||||||
|
|
@ -96,7 +87,7 @@
|
||||||
(set fire-init true))
|
(set fire-init true))
|
||||||
(pxl8.particles_render particles)))
|
(pxl8.particles_render particles)))
|
||||||
|
|
||||||
6 (do
|
:rain (do
|
||||||
(pxl8.clr 0)
|
(pxl8.clr 0)
|
||||||
(when particles
|
(when particles
|
||||||
(when (not rain-init)
|
(when (not rain-init)
|
||||||
|
|
@ -105,7 +96,7 @@
|
||||||
(set rain-init true))
|
(set rain-init true))
|
||||||
(pxl8.particles_render particles)))
|
(pxl8.particles_render particles)))
|
||||||
|
|
||||||
7 (do
|
:snow (do
|
||||||
(pxl8.clr 0)
|
(pxl8.clr 0)
|
||||||
(when particles
|
(when particles
|
||||||
(when (not snow-init)
|
(when (not snow-init)
|
||||||
|
|
@ -114,6 +105,8 @@
|
||||||
(set snow-init true))
|
(set snow-init true))
|
||||||
(pxl8.particles_render particles)))
|
(pxl8.particles_render particles)))
|
||||||
|
|
||||||
8 (cube3d.frame)
|
:cube3d (cube3d.frame)
|
||||||
|
|
||||||
|
:worldgen (worldgen.frame)
|
||||||
|
|
||||||
_ (pxl8.clr 0))))
|
_ (pxl8.clr 0))))
|
||||||
|
|
|
||||||
151
demo/mod/worldgen.fnl
Normal file
151
demo/mod/worldgen.fnl
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
(local pxl8 (require :pxl8))
|
||||||
|
(local debug-ui (require :mod.debug_ui))
|
||||||
|
|
||||||
|
(var world nil)
|
||||||
|
(var cam-x 1000)
|
||||||
|
(var cam-y 64)
|
||||||
|
(var cam-z 1000)
|
||||||
|
(var cam-yaw 0)
|
||||||
|
(var cam-pitch 0)
|
||||||
|
(var bob-time 0)
|
||||||
|
(var show-debug-ui false)
|
||||||
|
(var affine false)
|
||||||
|
(var fps 0)
|
||||||
|
(var fps-accumulator 0)
|
||||||
|
(var fps-frame-count 0)
|
||||||
|
|
||||||
|
(local move-speed 200)
|
||||||
|
(local turn-speed 2.0)
|
||||||
|
(local bob-speed 8.0)
|
||||||
|
(local bob-amount 4.0)
|
||||||
|
|
||||||
|
(fn init []
|
||||||
|
(set world (pxl8.world_new))
|
||||||
|
(let [result (pxl8.world_generate world {
|
||||||
|
:type pxl8.PROCGEN_CAVE
|
||||||
|
:width 32
|
||||||
|
:height 32
|
||||||
|
:seed 42
|
||||||
|
:density 0.45
|
||||||
|
:iterations 4})]
|
||||||
|
(if (< result 0)
|
||||||
|
(pxl8.error (.. "Failed to generate cave - result: " result))
|
||||||
|
(pxl8.info "Generated procedural cave"))))
|
||||||
|
|
||||||
|
(fn update [dt]
|
||||||
|
(set fps-accumulator (+ fps-accumulator dt))
|
||||||
|
(set fps-frame-count (+ fps-frame-count 1))
|
||||||
|
|
||||||
|
(when (>= fps-accumulator 0.25)
|
||||||
|
(set fps (/ fps-frame-count fps-accumulator))
|
||||||
|
(set fps-accumulator 0)
|
||||||
|
(set fps-frame-count 0))
|
||||||
|
|
||||||
|
(when (pxl8.key_pressed "F8")
|
||||||
|
(set show-debug-ui (not show-debug-ui))
|
||||||
|
(pxl8.ui_window_set_open "Debug Menu (F8)" show-debug-ui))
|
||||||
|
|
||||||
|
(when (pxl8.world_is_loaded world)
|
||||||
|
(let [forward-x (- (math.sin cam-yaw))
|
||||||
|
forward-z (- (math.cos cam-yaw))
|
||||||
|
right-x (math.cos cam-yaw)
|
||||||
|
right-z (- (math.sin cam-yaw))
|
||||||
|
cell-size 64
|
||||||
|
grid-min 0
|
||||||
|
grid-max (* 32 cell-size)]
|
||||||
|
|
||||||
|
(var moving false)
|
||||||
|
(var new-x cam-x)
|
||||||
|
(var new-z cam-z)
|
||||||
|
|
||||||
|
(when (pxl8.key_down "w")
|
||||||
|
(set new-x (+ new-x (* forward-x move-speed dt)))
|
||||||
|
(set new-z (+ new-z (* forward-z move-speed dt)))
|
||||||
|
(set moving true))
|
||||||
|
|
||||||
|
(when (pxl8.key_down "s")
|
||||||
|
(set new-x (- new-x (* forward-x move-speed dt)))
|
||||||
|
(set new-z (- new-z (* forward-z move-speed dt)))
|
||||||
|
(set moving true))
|
||||||
|
|
||||||
|
(when (pxl8.key_down "q")
|
||||||
|
(set new-x (- new-x (* right-x move-speed dt)))
|
||||||
|
(set new-z (- new-z (* right-z move-speed dt)))
|
||||||
|
(set moving true))
|
||||||
|
|
||||||
|
(when (pxl8.key_down "e")
|
||||||
|
(set new-x (+ new-x (* right-x move-speed dt)))
|
||||||
|
(set new-z (+ new-z (* right-z move-speed dt)))
|
||||||
|
(set moving true))
|
||||||
|
|
||||||
|
(when (and (>= new-x grid-min) (<= new-x grid-max)
|
||||||
|
(>= new-z grid-min) (<= new-z grid-max))
|
||||||
|
(set cam-x new-x)
|
||||||
|
(set cam-z new-z))
|
||||||
|
|
||||||
|
(when (or (pxl8.key_down "left") (pxl8.key_down "a"))
|
||||||
|
(set cam-yaw (+ cam-yaw (* turn-speed dt))))
|
||||||
|
|
||||||
|
(when (or (pxl8.key_down "right") (pxl8.key_down "d"))
|
||||||
|
(set cam-yaw (- cam-yaw (* turn-speed dt))))
|
||||||
|
|
||||||
|
(when (pxl8.key_down "up")
|
||||||
|
(set cam-pitch (+ cam-pitch (* turn-speed dt)))
|
||||||
|
(when (> cam-pitch 1.5) (set cam-pitch 1.5)))
|
||||||
|
|
||||||
|
(when (pxl8.key_down "down")
|
||||||
|
(set cam-pitch (- cam-pitch (* turn-speed dt)))
|
||||||
|
(when (< cam-pitch -1.5) (set cam-pitch -1.5)))
|
||||||
|
|
||||||
|
(if moving
|
||||||
|
(set bob-time (+ bob-time (* dt bob-speed)))
|
||||||
|
(let [target-phase (* (math.floor (/ bob-time math.pi)) math.pi)]
|
||||||
|
(set bob-time (+ (* bob-time 0.8) (* target-phase 0.2))))))))
|
||||||
|
|
||||||
|
(fn frame []
|
||||||
|
(pxl8.clr 0)
|
||||||
|
|
||||||
|
(when (pxl8.world_is_loaded world)
|
||||||
|
(let [bob-offset (* (math.sin bob-time) bob-amount)
|
||||||
|
eye-y (+ cam-y bob-offset)
|
||||||
|
forward-x (- (math.sin cam-yaw))
|
||||||
|
forward-z (- (math.cos cam-yaw))
|
||||||
|
target-x (+ cam-x forward-x)
|
||||||
|
target-y (+ eye-y (math.sin cam-pitch))
|
||||||
|
target-z (+ cam-z forward-z)]
|
||||||
|
|
||||||
|
(pxl8.text (.. "Pos: " (string.format "%.0f" cam-x) ","
|
||||||
|
(string.format "%.0f" cam-y) ","
|
||||||
|
(string.format "%.0f" cam-z)) 10 25 12)
|
||||||
|
|
||||||
|
(pxl8.clear_zbuffer)
|
||||||
|
(pxl8.set_backface_culling true)
|
||||||
|
(pxl8.set_wireframe false)
|
||||||
|
|
||||||
|
(let [aspect (/ (pxl8.get_width) (pxl8.get_height))
|
||||||
|
fov 1.047]
|
||||||
|
(pxl8.set_projection (pxl8.mat4_perspective fov aspect 1.0 4096.0)))
|
||||||
|
|
||||||
|
(pxl8.set_view (pxl8.mat4_lookat
|
||||||
|
[cam-x eye-y cam-z]
|
||||||
|
[target-x target-y target-z]
|
||||||
|
[0 1 0]))
|
||||||
|
|
||||||
|
(pxl8.set_model (pxl8.mat4_identity))
|
||||||
|
|
||||||
|
(pxl8.set_affine_textures affine)
|
||||||
|
(pxl8.world_render world [cam-x eye-y cam-z]))
|
||||||
|
|
||||||
|
(let [new-state (debug-ui.render {:show-debug-ui show-debug-ui
|
||||||
|
:fps fps
|
||||||
|
:wireframe false
|
||||||
|
:auto-rotate false
|
||||||
|
:orthographic false
|
||||||
|
:use-texture true
|
||||||
|
:affine affine})]
|
||||||
|
(when (not= new-state.show-debug-ui nil) (set show-debug-ui new-state.show-debug-ui))
|
||||||
|
(when (not= new-state.affine nil) (set affine new-state.affine)))))
|
||||||
|
|
||||||
|
{:init init
|
||||||
|
:update update
|
||||||
|
:frame frame}
|
||||||
|
|
@ -459,4 +459,21 @@ function pxl8.world_is_loaded(world)
|
||||||
return C.pxl8_world_is_loaded(world)
|
return C.pxl8_world_is_loaded(world)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function pxl8.world_generate(world, params)
|
||||||
|
local c_params = ffi.new("pxl8_procgen_params")
|
||||||
|
c_params.type = params.type or C.PXL8_PROCGEN_CAVE
|
||||||
|
c_params.width = params.width or 32
|
||||||
|
c_params.height = params.height or 32
|
||||||
|
c_params.depth = params.depth or 0
|
||||||
|
c_params.seed = params.seed or 0
|
||||||
|
c_params.density = params.density or 0.45
|
||||||
|
c_params.iterations = params.iterations or 4
|
||||||
|
c_params.type_params = nil
|
||||||
|
return C.pxl8_world_generate(world, c_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
pxl8.PROCGEN_CAVE = C.PXL8_PROCGEN_CAVE
|
||||||
|
pxl8.PROCGEN_DUNGEON = C.PXL8_PROCGEN_DUNGEON
|
||||||
|
pxl8.PROCGEN_TERRAIN = C.PXL8_PROCGEN_TERRAIN
|
||||||
|
|
||||||
return pxl8
|
return pxl8
|
||||||
|
|
|
||||||
11
src/pxl8.c
11
src/pxl8.c
|
|
@ -147,6 +147,17 @@ pxl8_game_result pxl8_update(pxl8_game* game) {
|
||||||
game->last_time = current_time;
|
game->last_time = current_time;
|
||||||
game->time += dt;
|
game->time += dt;
|
||||||
|
|
||||||
|
game->fps_accumulator += dt;
|
||||||
|
game->fps_frame_count++;
|
||||||
|
if (game->fps_accumulator >= 1.0f) {
|
||||||
|
game->fps = (f32)game->fps_frame_count / game->fps_accumulator;
|
||||||
|
if (!game->repl_mode) {
|
||||||
|
pxl8_debug("FPS: %.1f (%.2fms)", game->fps, (game->fps_accumulator / game->fps_frame_count) * 1000.0f);
|
||||||
|
}
|
||||||
|
game->fps_accumulator = 0.0f;
|
||||||
|
game->fps_frame_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
pxl8_script_check_reload(game->script);
|
pxl8_script_check_reload(game->script);
|
||||||
|
|
||||||
if (game->repl_mode && !game->repl_started) {
|
if (game->repl_mode && !game->repl_started) {
|
||||||
|
|
|
||||||
122
src/pxl8_bsp.c
122
src/pxl8_bsp.c
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -331,7 +332,47 @@ bool pxl8_bsp_is_leaf_visible(const pxl8_bsp* bsp, i32 leaf_from, i32 leaf_to) {
|
||||||
return (bsp->visdata[visofs + byte_idx] & (1 << bit_idx)) != 0;
|
return (bsp->visdata[visofs + byte_idx] & (1 << bit_idx)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 color) {
|
static inline bool face_in_frustum(const pxl8_bsp* bsp, u32 face_id, const pxl8_frustum* frustum) {
|
||||||
|
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
||||||
|
|
||||||
|
f32 min_x = 1e30f, min_y = 1e30f, min_z = 1e30f;
|
||||||
|
f32 max_x = -1e30f, max_y = -1e30f, max_z = -1e30f;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < face->num_edges; i++) {
|
||||||
|
i32 surfedge_idx = face->first_edge + i;
|
||||||
|
if (surfedge_idx >= (i32)bsp->num_surfedges) continue;
|
||||||
|
|
||||||
|
i32 edge_idx = bsp->surfedges[surfedge_idx];
|
||||||
|
u32 vert_idx;
|
||||||
|
|
||||||
|
if (edge_idx >= 0) {
|
||||||
|
if ((u32)edge_idx >= bsp->num_edges) continue;
|
||||||
|
vert_idx = bsp->edges[edge_idx].vertex[0];
|
||||||
|
} else {
|
||||||
|
edge_idx = -edge_idx;
|
||||||
|
if ((u32)edge_idx >= bsp->num_edges) continue;
|
||||||
|
vert_idx = bsp->edges[edge_idx].vertex[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vert_idx >= bsp->num_vertices) continue;
|
||||||
|
|
||||||
|
pxl8_vec3 v = bsp->vertices[vert_idx].position;
|
||||||
|
|
||||||
|
if (v.x < min_x) min_x = v.x;
|
||||||
|
if (v.x > max_x) max_x = v.x;
|
||||||
|
if (v.y < min_y) min_y = v.y;
|
||||||
|
if (v.y > max_y) max_y = v.y;
|
||||||
|
if (v.z < min_z) min_z = v.z;
|
||||||
|
if (v.z > max_z) max_z = v.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_vec3 aabb_min = {min_x, min_y, min_z};
|
||||||
|
pxl8_vec3 aabb_max = {max_x, max_y, max_z};
|
||||||
|
|
||||||
|
return pxl8_frustum_test_aabb(frustum, aabb_min, aabb_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id) {
|
||||||
if (!gfx || !bsp || face_id >= bsp->num_faces) return;
|
if (!gfx || !bsp || face_id >= bsp->num_faces) return;
|
||||||
|
|
||||||
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
||||||
|
|
@ -340,6 +381,19 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 c
|
||||||
pxl8_vec3 verts[64];
|
pxl8_vec3 verts[64];
|
||||||
u32 num_verts = 0;
|
u32 num_verts = 0;
|
||||||
|
|
||||||
|
const pxl8_bsp_plane* plane = &bsp->planes[face->plane_id];
|
||||||
|
|
||||||
|
u32 color;
|
||||||
|
bool use_texture = false;
|
||||||
|
|
||||||
|
if (fabsf(plane->normal.y) > 0.7f) {
|
||||||
|
color = (plane->normal.y > 0) ? 4 : 3;
|
||||||
|
} else {
|
||||||
|
color = 15;
|
||||||
|
use_texture = (texture_id > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (u32 i = 0; i < face->num_edges && num_verts < 64; i++) {
|
for (u32 i = 0; i < face->num_edges && num_verts < 64; i++) {
|
||||||
i32 surfedge_idx = face->first_edge + i;
|
i32 surfedge_idx = face->first_edge + i;
|
||||||
if (surfedge_idx >= (i32)bsp->num_surfedges) continue;
|
if (surfedge_idx >= (i32)bsp->num_surfedges) continue;
|
||||||
|
|
@ -362,9 +416,75 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 c
|
||||||
|
|
||||||
if (num_verts < 3) return;
|
if (num_verts < 3) return;
|
||||||
|
|
||||||
|
if (use_texture && face->texinfo_id < bsp->num_texinfo) {
|
||||||
|
const pxl8_bsp_texinfo* texinfo = &bsp->texinfo[face->texinfo_id];
|
||||||
|
f32 tex_scale = 64.0f;
|
||||||
|
|
||||||
|
for (u32 tri_idx = 1; tri_idx < num_verts - 1; tri_idx++) {
|
||||||
|
pxl8_vec3 v0 = verts[0];
|
||||||
|
pxl8_vec3 v1 = verts[tri_idx];
|
||||||
|
pxl8_vec3 v2 = verts[tri_idx + 1];
|
||||||
|
|
||||||
|
f32 u0 = (pxl8_vec3_dot(v0, texinfo->u_axis) + texinfo->u_offset) / tex_scale;
|
||||||
|
f32 v0_uv = (pxl8_vec3_dot(v0, texinfo->v_axis) + texinfo->v_offset) / tex_scale;
|
||||||
|
f32 u1 = (pxl8_vec3_dot(v1, texinfo->u_axis) + texinfo->u_offset) / tex_scale;
|
||||||
|
f32 v1_uv = (pxl8_vec3_dot(v1, texinfo->v_axis) + texinfo->v_offset) / tex_scale;
|
||||||
|
f32 u2 = (pxl8_vec3_dot(v2, texinfo->u_axis) + texinfo->u_offset) / tex_scale;
|
||||||
|
f32 v2_uv = (pxl8_vec3_dot(v2, texinfo->v_axis) + texinfo->v_offset) / tex_scale;
|
||||||
|
|
||||||
|
pxl8_3d_draw_triangle_textured(gfx, v0, v1, v2, u0, v0_uv, u1, v1_uv, u2, v2_uv, texture_id);
|
||||||
|
}
|
||||||
|
} else{
|
||||||
for (u32 i = 1; i < num_verts - 1; i++) {
|
for (u32 i = 1; i < num_verts - 1; i++) {
|
||||||
pxl8_3d_draw_triangle_raw(gfx, verts[0], verts[i], verts[i + 1], color);
|
pxl8_3d_draw_triangle_raw(gfx, verts[0], verts[i], verts[i + 1], color);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_bsp_render_solid(
|
||||||
|
pxl8_gfx* gfx,
|
||||||
|
const pxl8_bsp* bsp,
|
||||||
|
pxl8_vec3 camera_pos,
|
||||||
|
u32 texture_id
|
||||||
|
) {
|
||||||
|
if (!gfx || !bsp || bsp->num_faces == 0) return;
|
||||||
|
|
||||||
|
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||||
|
if (!frustum) return;
|
||||||
|
|
||||||
|
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
||||||
|
|
||||||
|
static u8* rendered_faces = NULL;
|
||||||
|
static u32 rendered_faces_capacity = 0;
|
||||||
|
|
||||||
|
if (rendered_faces_capacity < bsp->num_faces) {
|
||||||
|
rendered_faces = realloc(rendered_faces, bsp->num_faces);
|
||||||
|
if (!rendered_faces) return;
|
||||||
|
rendered_faces_capacity = bsp->num_faces;
|
||||||
|
pxl8_debug("Allocated face tracking buffer: %u bytes", bsp->num_faces);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(rendered_faces, 0, bsp->num_faces);
|
||||||
|
|
||||||
|
for (u32 leaf_id = 0; leaf_id < bsp->num_leafs; leaf_id++) {
|
||||||
|
if (camera_leaf >= 0 && !pxl8_bsp_is_leaf_visible(bsp, camera_leaf, leaf_id)) continue;
|
||||||
|
|
||||||
|
const pxl8_bsp_leaf* leaf = &bsp->leafs[leaf_id];
|
||||||
|
|
||||||
|
for (u32 i = 0; i < leaf->num_marksurfaces; i++) {
|
||||||
|
u32 surf_idx = leaf->first_marksurface + i;
|
||||||
|
if (surf_idx >= bsp->num_marksurfaces) continue;
|
||||||
|
|
||||||
|
u32 face_id = bsp->marksurfaces[surf_idx];
|
||||||
|
if (face_id >= bsp->num_faces) continue;
|
||||||
|
|
||||||
|
if (rendered_faces[face_id]) continue;
|
||||||
|
rendered_faces[face_id] = 1;
|
||||||
|
|
||||||
|
pxl8_bsp_render_face(gfx, bsp, face_id, texture_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_bsp_render_wireframe(
|
void pxl8_bsp_render_wireframe(
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,14 @@ void pxl8_bsp_render_face(
|
||||||
pxl8_gfx* gfx,
|
pxl8_gfx* gfx,
|
||||||
const pxl8_bsp* bsp,
|
const pxl8_bsp* bsp,
|
||||||
u32 face_id,
|
u32 face_id,
|
||||||
u32 color
|
u32 texture_id
|
||||||
|
);
|
||||||
|
|
||||||
|
void pxl8_bsp_render_solid(
|
||||||
|
pxl8_gfx* gfx,
|
||||||
|
const pxl8_bsp* bsp,
|
||||||
|
pxl8_vec3 camera_pos,
|
||||||
|
u32 texture_id
|
||||||
);
|
);
|
||||||
|
|
||||||
void pxl8_bsp_render_wireframe(
|
void pxl8_bsp_render_wireframe(
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@ typedef struct pxl8_game {
|
||||||
u64 last_time;
|
u64 last_time;
|
||||||
f32 time;
|
f32 time;
|
||||||
|
|
||||||
|
f32 fps_accumulator;
|
||||||
|
i32 fps_frame_count;
|
||||||
|
f32 fps;
|
||||||
|
|
||||||
bool repl_mode;
|
bool repl_mode;
|
||||||
bool repl_started;
|
bool repl_started;
|
||||||
bool running;
|
bool running;
|
||||||
|
|
|
||||||
276
src/pxl8_gfx.c
276
src/pxl8_gfx.c
|
|
@ -42,12 +42,18 @@ struct pxl8_gfx {
|
||||||
pxl8_mat4 model;
|
pxl8_mat4 model;
|
||||||
pxl8_mat4 projection;
|
pxl8_mat4 projection;
|
||||||
pxl8_mat4 view;
|
pxl8_mat4 view;
|
||||||
|
pxl8_mat4 mvp;
|
||||||
|
bool mvp_dirty;
|
||||||
|
pxl8_frustum frustum;
|
||||||
bool wireframe;
|
bool wireframe;
|
||||||
f32* zbuffer;
|
f32* zbuffer;
|
||||||
i32 zbuffer_height;
|
i32 zbuffer_height;
|
||||||
i32 zbuffer_width;
|
i32 zbuffer_width;
|
||||||
|
|
||||||
bool affine_textures;
|
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) {
|
static inline void pxl8_color_unpack(u32 color, u8* r, u8* g, u8* b, u8* a) {
|
||||||
|
|
@ -191,6 +197,7 @@ pxl8_gfx* pxl8_gfx_create(
|
||||||
gfx->model = pxl8_mat4_identity();
|
gfx->model = pxl8_mat4_identity();
|
||||||
gfx->projection = pxl8_mat4_identity();
|
gfx->projection = pxl8_mat4_identity();
|
||||||
gfx->view = pxl8_mat4_identity();
|
gfx->view = pxl8_mat4_identity();
|
||||||
|
gfx->mvp_dirty = true;
|
||||||
gfx->wireframe = false;
|
gfx->wireframe = false;
|
||||||
gfx->zbuffer = NULL;
|
gfx->zbuffer = NULL;
|
||||||
gfx->zbuffer_height = 0;
|
gfx->zbuffer_height = 0;
|
||||||
|
|
@ -404,6 +411,19 @@ void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom) {
|
||||||
void pxl8_clr(pxl8_gfx* gfx, u32 color) {
|
void pxl8_clr(pxl8_gfx* gfx, u32 color) {
|
||||||
if (!gfx || !gfx->framebuffer) return;
|
if (!gfx || !gfx->framebuffer) return;
|
||||||
|
|
||||||
|
static u32 frame_count = 0;
|
||||||
|
if (gfx->frame_triangle_count > 0) {
|
||||||
|
if (frame_count % 60 == 0) {
|
||||||
|
i32 fb_pixels = gfx->framebuffer_width * gfx->framebuffer_height;
|
||||||
|
f32 overdraw = (f32)gfx->frame_pixel_count / (f32)fb_pixels;
|
||||||
|
pxl8_debug("Frame triangles: %u, pixels: %u, overdraw: %.2fx",
|
||||||
|
gfx->frame_triangle_count, gfx->frame_pixel_count, overdraw);
|
||||||
|
}
|
||||||
|
frame_count++;
|
||||||
|
}
|
||||||
|
gfx->frame_triangle_count = 0;
|
||||||
|
gfx->frame_pixel_count = 0;
|
||||||
|
|
||||||
i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1;
|
i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1;
|
||||||
i32 size = gfx->framebuffer_width * gfx->framebuffer_height;
|
i32 size = gfx->framebuffer_width * gfx->framebuffer_height;
|
||||||
|
|
||||||
|
|
@ -786,9 +806,33 @@ static bool pxl8_3d_init_zbuffer(pxl8_gfx* gfx) {
|
||||||
|
|
||||||
void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx) {
|
void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx) {
|
||||||
if (!gfx || !gfx->zbuffer) return;
|
if (!gfx || !gfx->zbuffer) return;
|
||||||
for (i32 i = 0; i < gfx->zbuffer_width * gfx->zbuffer_height; i++) {
|
|
||||||
gfx->zbuffer[i] = 1e30f;
|
i32 count = gfx->zbuffer_width * gfx->zbuffer_height;
|
||||||
|
const f32 far_z = 1e30f;
|
||||||
|
|
||||||
|
#if defined(PXL8_SIMD_NEON)
|
||||||
|
float32x4_t far_vec = vdupq_n_f32(far_z);
|
||||||
|
i32 i = 0;
|
||||||
|
for (; i + 3 < count; i += 4) {
|
||||||
|
vst1q_f32(&gfx->zbuffer[i], far_vec);
|
||||||
}
|
}
|
||||||
|
for (; i < count; i++) {
|
||||||
|
gfx->zbuffer[i] = far_z;
|
||||||
|
}
|
||||||
|
#elif defined(PXL8_SIMD_SSE2)
|
||||||
|
__m128 far_vec = _mm_set1_ps(far_z);
|
||||||
|
i32 i = 0;
|
||||||
|
for (; i + 3 < count; i += 4) {
|
||||||
|
_mm_store_ps(&gfx->zbuffer[i], far_vec);
|
||||||
|
}
|
||||||
|
for (; i < count; i++) {
|
||||||
|
gfx->zbuffer[i] = far_z;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (i32 i = 0; i < count; i++) {
|
||||||
|
gfx->zbuffer[i] = far_z;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling) {
|
void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling) {
|
||||||
|
|
@ -799,16 +843,19 @@ void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling) {
|
||||||
void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat) {
|
void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
gfx->model = mat;
|
gfx->model = mat;
|
||||||
|
gfx->mvp_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat) {
|
void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
gfx->projection = mat;
|
gfx->projection = mat;
|
||||||
|
gfx->mvp_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_set_view(pxl8_gfx* gfx, pxl8_mat4 mat) {
|
void pxl8_3d_set_view(pxl8_gfx* gfx, pxl8_mat4 mat) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
gfx->view = mat;
|
gfx->view = mat;
|
||||||
|
gfx->mvp_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_set_wireframe(pxl8_gfx* gfx, bool wireframe) {
|
void pxl8_3d_set_wireframe(pxl8_gfx* gfx, bool wireframe) {
|
||||||
|
|
@ -821,7 +868,27 @@ void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine) {
|
||||||
gfx->affine_textures = affine;
|
gfx->affine_textures = affine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_update_mvp(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx->mvp_dirty) return;
|
||||||
|
|
||||||
|
gfx->mvp = pxl8_mat4_multiply(gfx->projection,
|
||||||
|
pxl8_mat4_multiply(gfx->view, gfx->model));
|
||||||
|
|
||||||
|
pxl8_mat4 vp = pxl8_mat4_multiply(gfx->projection, gfx->view);
|
||||||
|
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
||||||
|
|
||||||
|
gfx->mvp_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx) return NULL;
|
||||||
|
pxl8_update_mvp(gfx);
|
||||||
|
return &gfx->frustum;
|
||||||
|
}
|
||||||
|
|
||||||
static inline pxl8_vec4 pxl8_transform_vertex(pxl8_gfx* gfx, pxl8_vec3 pos) {
|
static inline pxl8_vec4 pxl8_transform_vertex(pxl8_gfx* gfx, pxl8_vec3 pos) {
|
||||||
|
pxl8_update_mvp(gfx);
|
||||||
|
|
||||||
pxl8_vec4 v = {
|
pxl8_vec4 v = {
|
||||||
.x = pos.x,
|
.x = pos.x,
|
||||||
.y = pos.y,
|
.y = pos.y,
|
||||||
|
|
@ -829,10 +896,7 @@ static inline pxl8_vec4 pxl8_transform_vertex(pxl8_gfx* gfx, pxl8_vec3 pos) {
|
||||||
.w = 1.0f,
|
.w = 1.0f,
|
||||||
};
|
};
|
||||||
|
|
||||||
pxl8_mat4 mvp = pxl8_mat4_multiply(gfx->projection,
|
return pxl8_mat4_multiply_vec4(gfx->mvp, v);
|
||||||
pxl8_mat4_multiply(gfx->view, gfx->model));
|
|
||||||
|
|
||||||
return pxl8_mat4_multiply_vec4(mvp, v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void pxl8_project_to_screen(pxl8_gfx* gfx, pxl8_vec4 clip, i32* x, i32* y, f32* z) {
|
static inline void pxl8_project_to_screen(pxl8_gfx* gfx, pxl8_vec4 clip, i32* x, i32* y, f32* z) {
|
||||||
|
|
@ -865,23 +929,82 @@ void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color)
|
||||||
pxl8_line(gfx, x0, y0, x1, y1, color);
|
pxl8_line(gfx, x0, y0, x1, y1, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void pxl8_fill_scanline(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) {
|
static inline void pxl8_fill_scanline_hicolor(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) {
|
||||||
if (y < 0 || y >= gfx->framebuffer_height) return;
|
if (y < 0 || y >= gfx->framebuffer_height) return;
|
||||||
if (xs > xe) {
|
if (xs > xe) {
|
||||||
i32 tmp = xs; xs = xe; xe = tmp;
|
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;
|
||||||
|
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++) {
|
for (i32 x = xs; x <= xe; x++) {
|
||||||
if (x >= 0 && x < gfx->framebuffer_width) {
|
i32 idx = zbuf_offset + x;
|
||||||
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]) {
|
if (z <= gfx->zbuffer[idx]) {
|
||||||
gfx->zbuffer[idx] = z;
|
gfx->zbuffer[idx] = z;
|
||||||
pxl8_pixel(gfx, x, y, color);
|
fb[fb_offset + x] = color;
|
||||||
}
|
}
|
||||||
|
z += dz;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_fill_scanline_indexed(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) {
|
||||||
|
if (y < 0 || y >= gfx->framebuffer_height) return;
|
||||||
|
if (xs > xe) {
|
||||||
|
i32 tmp = xs; xs = xe; xe = tmp;
|
||||||
|
f32 tmpz = z0; z0 = z1; z1 = tmpz;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xs < 0) xs = 0;
|
||||||
|
if (xe >= gfx->framebuffer_width) xe = gfx->framebuffer_width - 1;
|
||||||
|
if (xs > xe) return;
|
||||||
|
|
||||||
|
i32 width = xe - xs;
|
||||||
|
u8 idx_color = color & 0xFF;
|
||||||
|
|
||||||
|
if (width == 0) {
|
||||||
|
i32 idx = y * gfx->zbuffer_width + xs;
|
||||||
|
if (z0 <= gfx->zbuffer[idx]) {
|
||||||
|
gfx->zbuffer[idx] = z0;
|
||||||
|
i32 fb_idx = y * gfx->framebuffer_width + xs;
|
||||||
|
gfx->framebuffer[fb_idx] = idx_color;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 dz = (z1 - z0) / (f32)width;
|
||||||
|
f32 z = z0;
|
||||||
|
|
||||||
|
i32 zbuf_offset = y * gfx->zbuffer_width;
|
||||||
|
i32 fb_offset = y * gfx->framebuffer_width;
|
||||||
|
|
||||||
|
for (i32 x = xs; x <= xe; x++) {
|
||||||
|
i32 idx = zbuf_offset + x;
|
||||||
|
if (z <= gfx->zbuffer[idx]) {
|
||||||
|
gfx->zbuffer[idx] = z;
|
||||||
|
gfx->framebuffer[fb_offset + x] = idx_color;
|
||||||
|
}
|
||||||
|
z += dz;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -927,47 +1050,117 @@ static inline void pxl8_fill_scanline_textured(
|
||||||
tmpf = w0; w0 = w1; w1 = tmpf;
|
tmpf = w0; w0 = w1; w1 = tmpf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i32 span = xe - xs;
|
||||||
|
if (span <= 0) {
|
||||||
|
if (xs >= 0 && xs < gfx->framebuffer_width) {
|
||||||
|
i32 idx = y * gfx->zbuffer_width + xs;
|
||||||
|
if (z0 <= gfx->zbuffer[idx]) {
|
||||||
|
gfx->frame_pixel_count++;
|
||||||
|
f32 u = u0, v = v0;
|
||||||
|
if (!gfx->affine_textures && fabsf(w0) > 1e-6f) {
|
||||||
|
u /= w0;
|
||||||
|
v /= w0;
|
||||||
|
}
|
||||||
|
u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
|
||||||
|
if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) ||
|
||||||
|
(gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) {
|
||||||
|
gfx->zbuffer[idx] = z0;
|
||||||
|
pxl8_pixel(gfx, xs, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 inv_span = 1.0f / (f32)span;
|
||||||
|
f32 dz = (z1 - z0) * inv_span;
|
||||||
|
f32 du = (u1 - u0) * inv_span;
|
||||||
|
f32 dv = (v1 - v0) * inv_span;
|
||||||
|
f32 dw = (w1 - w0) * inv_span;
|
||||||
|
|
||||||
|
f32 z = z0;
|
||||||
|
f32 u = u0;
|
||||||
|
f32 v = v0;
|
||||||
|
f32 w = w0;
|
||||||
|
|
||||||
|
if (gfx->affine_textures) {
|
||||||
for (i32 x = xs; x <= xe; x++) {
|
for (i32 x = xs; x <= xe; x++) {
|
||||||
if (x >= 0 && x < gfx->framebuffer_width) {
|
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;
|
i32 idx = y * gfx->zbuffer_width + x;
|
||||||
if (z <= gfx->zbuffer[idx]) {
|
if (z <= gfx->zbuffer[idx]) {
|
||||||
f32 u = u0 + t * (u1 - u0);
|
gfx->frame_pixel_count++;
|
||||||
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);
|
u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
|
||||||
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) ||
|
||||||
if (color & 0xFF000000) {
|
(gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) {
|
||||||
gfx->zbuffer[idx] = z;
|
gfx->zbuffer[idx] = z;
|
||||||
pxl8_pixel(gfx, x, y, color);
|
pxl8_pixel(gfx, x, y, color);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
z += dz;
|
||||||
|
u += du;
|
||||||
|
v += dv;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (color != 0) {
|
i32 x = xs;
|
||||||
|
while (x <= xe) {
|
||||||
|
f32 w_inv = (fabsf(w) > 1e-6f) ? (1.0f / w) : 0.0f;
|
||||||
|
f32 u_corrected = u * w_inv;
|
||||||
|
f32 v_corrected = v * w_inv;
|
||||||
|
|
||||||
|
i32 span_end = (x + 16 <= xe) ? (x + 16) : (xe + 1);
|
||||||
|
i32 span_len = span_end - x;
|
||||||
|
|
||||||
|
f32 next_u, next_v, affine_du, affine_dv;
|
||||||
|
if (span_len > 1) {
|
||||||
|
f32 next_w = w + span_len * dw;
|
||||||
|
f32 next_w_inv = (fabsf(next_w) > 1e-6f) ? (1.0f / next_w) : 0.0f;
|
||||||
|
next_u = (u + span_len * du) * next_w_inv;
|
||||||
|
next_v = (v + span_len * dv) * next_w_inv;
|
||||||
|
f32 inv_span = 1.0f / (f32)span_len;
|
||||||
|
affine_du = (next_u - u_corrected) * inv_span;
|
||||||
|
affine_dv = (next_v - v_corrected) * inv_span;
|
||||||
|
} else {
|
||||||
|
affine_du = 0;
|
||||||
|
affine_dv = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 affine_u = u_corrected;
|
||||||
|
f32 affine_v = v_corrected;
|
||||||
|
|
||||||
|
for (; x < span_end; x++) {
|
||||||
|
if (x >= 0 && x < gfx->framebuffer_width) {
|
||||||
|
i32 idx = y * gfx->zbuffer_width + x;
|
||||||
|
if (z <= gfx->zbuffer[idx]) {
|
||||||
|
gfx->frame_pixel_count++;
|
||||||
|
u32 color = pxl8_sample_texture(gfx, texture_id, affine_u, affine_v);
|
||||||
|
if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) ||
|
||||||
|
(gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) {
|
||||||
gfx->zbuffer[idx] = z;
|
gfx->zbuffer[idx] = z;
|
||||||
pxl8_pixel(gfx, x, y, color);
|
pxl8_pixel(gfx, x, y, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
z += dz;
|
||||||
|
u += du;
|
||||||
|
v += dv;
|
||||||
|
w += dw;
|
||||||
|
affine_u += affine_du;
|
||||||
|
affine_v += affine_dv;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef void (*pxl8_scanline_func)(pxl8_gfx*, i32, i32, i32, f32, f32, u32);
|
||||||
|
|
||||||
static void pxl8_draw_flat_bottom_triangle(
|
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,
|
||||||
i32 x1, i32 y1, f32 z1,
|
i32 x1, i32 y1, f32 z1,
|
||||||
i32 x2, i32 y2, f32 z2,
|
i32 x2, i32 y2, f32 z2,
|
||||||
u32 color
|
u32 color,
|
||||||
|
pxl8_scanline_func fill_scanline
|
||||||
) {
|
) {
|
||||||
(void)z2;
|
(void)z2;
|
||||||
if (y1 == y0) return;
|
if (y1 == y0) return;
|
||||||
|
|
@ -979,7 +1172,7 @@ static void pxl8_draw_flat_bottom_triangle(
|
||||||
f32 cur_x2 = (f32)x0;
|
f32 cur_x2 = (f32)x0;
|
||||||
|
|
||||||
for (i32 y = y0; y <= y1; y++) {
|
for (i32 y = y0; y <= y1; y++) {
|
||||||
pxl8_fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color);
|
fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color);
|
||||||
cur_x1 += inv_slope_1;
|
cur_x1 += inv_slope_1;
|
||||||
cur_x2 += inv_slope_2;
|
cur_x2 += inv_slope_2;
|
||||||
}
|
}
|
||||||
|
|
@ -990,7 +1183,8 @@ static void pxl8_draw_flat_top_triangle(
|
||||||
i32 x0, i32 y0, f32 z0,
|
i32 x0, i32 y0, f32 z0,
|
||||||
i32 x1, i32 y1, f32 z1,
|
i32 x1, i32 y1, f32 z1,
|
||||||
i32 x2, i32 y2, f32 z2,
|
i32 x2, i32 y2, f32 z2,
|
||||||
u32 color
|
u32 color,
|
||||||
|
pxl8_scanline_func fill_scanline
|
||||||
) {
|
) {
|
||||||
(void)z2;
|
(void)z2;
|
||||||
if (y2 == y0) return;
|
if (y2 == y0) return;
|
||||||
|
|
@ -1002,7 +1196,7 @@ static void pxl8_draw_flat_top_triangle(
|
||||||
f32 cur_x2 = (f32)x2;
|
f32 cur_x2 = (f32)x2;
|
||||||
|
|
||||||
for (i32 y = y2; y > y0; y--) {
|
for (i32 y = y2; y > y0; y--) {
|
||||||
pxl8_fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color);
|
fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color);
|
||||||
cur_x1 -= inv_slope_1;
|
cur_x1 -= inv_slope_1;
|
||||||
cur_x2 -= inv_slope_2;
|
cur_x2 -= inv_slope_2;
|
||||||
}
|
}
|
||||||
|
|
@ -1022,6 +1216,8 @@ void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_v
|
||||||
void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
|
void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
|
||||||
if (!gfx || !pxl8_3d_init_zbuffer(gfx)) return;
|
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 v0 = pxl8_transform_vertex(gfx, tri.v[0].position);
|
||||||
pxl8_vec4 v1 = pxl8_transform_vertex(gfx, tri.v[1].position);
|
pxl8_vec4 v1 = pxl8_transform_vertex(gfx, tri.v[1].position);
|
||||||
pxl8_vec4 v2 = pxl8_transform_vertex(gfx, tri.v[2].position);
|
pxl8_vec4 v2 = pxl8_transform_vertex(gfx, tri.v[2].position);
|
||||||
|
|
@ -1065,17 +1261,21 @@ void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
|
||||||
|
|
||||||
u32 color = tri.v[0].color;
|
u32 color = tri.v[0].color;
|
||||||
|
|
||||||
|
pxl8_scanline_func fill_scanline = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR)
|
||||||
|
? pxl8_fill_scanline_hicolor
|
||||||
|
: pxl8_fill_scanline_indexed;
|
||||||
|
|
||||||
if (y1 == y2) {
|
if (y1 == y2) {
|
||||||
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color);
|
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline);
|
||||||
} else if (y0 == y1) {
|
} else if (y0 == y1) {
|
||||||
pxl8_draw_flat_top_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color);
|
pxl8_draw_flat_top_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline);
|
||||||
} else {
|
} else {
|
||||||
i32 x3 = x0 + (i32)(((f32)(y1 - y0) / (f32)(y2 - y0)) * (x2 - x0));
|
i32 x3 = x0 + (i32)(((f32)(y1 - y0) / (f32)(y2 - y0)) * (x2 - x0));
|
||||||
i32 y3 = y1;
|
i32 y3 = y1;
|
||||||
f32 z3 = z0 + ((f32)(y1 - y0) / (f32)(y2 - y0)) * (z2 - z0);
|
f32 z3 = z0 + ((f32)(y1 - y0) / (f32)(y2 - y0)) * (z2 - z0);
|
||||||
|
|
||||||
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, color);
|
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, color, fill_scanline);
|
||||||
pxl8_draw_flat_top_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color);
|
pxl8_draw_flat_top_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color, fill_scanline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ 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_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);
|
||||||
|
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx);
|
||||||
void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine);
|
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);
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ static i32 pxl8_key_code(const char* key_name) {
|
||||||
{"1", 30}, {"2", 31}, {"3", 32}, {"4", 33}, {"5", 34},
|
{"1", 30}, {"2", 31}, {"3", 32}, {"4", 33}, {"5", 34},
|
||||||
{"6", 35}, {"7", 36}, {"8", 37}, {"9", 38}, {"0", 39},
|
{"6", 35}, {"7", 36}, {"8", 37}, {"9", 38}, {"0", 39},
|
||||||
{"return", 40}, {"escape", 41}, {"backspace", 42}, {"tab", 43}, {"space", 44},
|
{"return", 40}, {"escape", 41}, {"backspace", 42}, {"tab", 43}, {"space", 44},
|
||||||
|
{"-", 45}, {"=", 46},
|
||||||
{"left", 80}, {"right", 79}, {"up", 82}, {"down", 81},
|
{"left", 80}, {"right", 79}, {"up", 82}, {"down", 81},
|
||||||
{"f1", 58}, {"f2", 59}, {"f3", 60}, {"f4", 61}, {"f5", 62}, {"f6", 63},
|
{"f1", 58}, {"f2", 59}, {"f3", 60}, {"f4", 61}, {"f5", 62}, {"f6", 63},
|
||||||
{"f7", 64}, {"f8", 65}, {"f9", 66}, {"f10", 67}, {"f11", 68}, {"f12", 69},
|
{"f7", 64}, {"f8", 65}, {"f9", 66}, {"f10", 67}, {"f11", 68}, {"f12", 69},
|
||||||
|
|
|
||||||
|
|
@ -250,3 +250,70 @@ pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up) {
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp) {
|
||||||
|
pxl8_frustum frustum;
|
||||||
|
const f32* m = vp.m;
|
||||||
|
|
||||||
|
frustum.planes[0].normal.x = m[3] - m[0];
|
||||||
|
frustum.planes[0].normal.y = m[7] - m[4];
|
||||||
|
frustum.planes[0].normal.z = m[11] - m[8];
|
||||||
|
frustum.planes[0].distance = m[15] - m[12];
|
||||||
|
|
||||||
|
frustum.planes[1].normal.x = m[3] + m[0];
|
||||||
|
frustum.planes[1].normal.y = m[7] + m[4];
|
||||||
|
frustum.planes[1].normal.z = m[11] + m[8];
|
||||||
|
frustum.planes[1].distance = m[15] + m[12];
|
||||||
|
|
||||||
|
frustum.planes[2].normal.x = m[3] + m[1];
|
||||||
|
frustum.planes[2].normal.y = m[7] + m[5];
|
||||||
|
frustum.planes[2].normal.z = m[11] + m[9];
|
||||||
|
frustum.planes[2].distance = m[15] + m[13];
|
||||||
|
|
||||||
|
frustum.planes[3].normal.x = m[3] - m[1];
|
||||||
|
frustum.planes[3].normal.y = m[7] - m[5];
|
||||||
|
frustum.planes[3].normal.z = m[11] - m[9];
|
||||||
|
frustum.planes[3].distance = m[15] - m[13];
|
||||||
|
|
||||||
|
frustum.planes[4].normal.x = m[3] - m[2];
|
||||||
|
frustum.planes[4].normal.y = m[7] - m[6];
|
||||||
|
frustum.planes[4].normal.z = m[11] - m[10];
|
||||||
|
frustum.planes[4].distance = m[15] - m[14];
|
||||||
|
|
||||||
|
frustum.planes[5].normal.x = m[3] + m[2];
|
||||||
|
frustum.planes[5].normal.y = m[7] + m[6];
|
||||||
|
frustum.planes[5].normal.z = m[11] + m[10];
|
||||||
|
frustum.planes[5].distance = m[15] + m[14];
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 6; i++) {
|
||||||
|
f32 len = pxl8_vec3_length(frustum.planes[i].normal);
|
||||||
|
if (len > 1e-6f) {
|
||||||
|
f32 inv_len = 1.0f / len;
|
||||||
|
frustum.planes[i].normal = pxl8_vec3_scale(frustum.planes[i].normal, inv_len);
|
||||||
|
frustum.planes[i].distance *= inv_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return frustum;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pxl8_frustum_test_aabb(const pxl8_frustum* frustum, pxl8_vec3 min, pxl8_vec3 max) {
|
||||||
|
for (i32 i = 0; i < 6; i++) {
|
||||||
|
pxl8_vec3 normal = frustum->planes[i].normal;
|
||||||
|
f32 d = frustum->planes[i].distance;
|
||||||
|
|
||||||
|
pxl8_vec3 p_vertex = {
|
||||||
|
(normal.x >= 0.0f) ? max.x : min.x,
|
||||||
|
(normal.y >= 0.0f) ? max.y : min.y,
|
||||||
|
(normal.z >= 0.0f) ? max.z : min.z
|
||||||
|
};
|
||||||
|
|
||||||
|
f32 dist = pxl8_vec3_dot(normal, p_vertex) + d;
|
||||||
|
|
||||||
|
if (dist < 0.0f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,15 @@ typedef struct pxl8_mat4 {
|
||||||
f32 m[16];
|
f32 m[16];
|
||||||
} pxl8_mat4;
|
} pxl8_mat4;
|
||||||
|
|
||||||
|
typedef struct pxl8_plane {
|
||||||
|
pxl8_vec3 normal;
|
||||||
|
f32 distance;
|
||||||
|
} pxl8_plane;
|
||||||
|
|
||||||
|
typedef struct pxl8_frustum {
|
||||||
|
pxl8_plane planes[6];
|
||||||
|
} pxl8_frustum;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -49,6 +58,9 @@ pxl8_mat4 pxl8_mat4_rotate_z(f32 angle);
|
||||||
pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z);
|
pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z);
|
||||||
pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z);
|
pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z);
|
||||||
|
|
||||||
|
pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp);
|
||||||
|
bool pxl8_frustum_test_aabb(const pxl8_frustum* frustum, pxl8_vec3 min, pxl8_vec3 max);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
472
src/pxl8_procgen.c
Normal file
472
src/pxl8_procgen.c
Normal file
|
|
@ -0,0 +1,472 @@
|
||||||
|
#include "pxl8_procgen.h"
|
||||||
|
|
||||||
|
#include "pxl8_macros.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct cave_grid {
|
||||||
|
u8* cells;
|
||||||
|
i32 width;
|
||||||
|
i32 height;
|
||||||
|
} cave_grid;
|
||||||
|
|
||||||
|
static u32 prng_state = 0;
|
||||||
|
|
||||||
|
static void prng_seed(u32 seed) {
|
||||||
|
prng_state = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 prng_next(void) {
|
||||||
|
prng_state ^= prng_state << 13;
|
||||||
|
prng_state ^= prng_state >> 17;
|
||||||
|
prng_state ^= prng_state << 5;
|
||||||
|
return prng_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static f32 prng_float(void) {
|
||||||
|
return (f32)prng_next() / (f32)0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cave_grid* cave_grid_create(i32 width, i32 height) {
|
||||||
|
cave_grid* grid = malloc(sizeof(cave_grid));
|
||||||
|
if (!grid) return NULL;
|
||||||
|
|
||||||
|
grid->width = width;
|
||||||
|
grid->height = height;
|
||||||
|
grid->cells = calloc(width * height, sizeof(u8));
|
||||||
|
|
||||||
|
if (!grid->cells) {
|
||||||
|
free(grid);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cave_grid_destroy(cave_grid* grid) {
|
||||||
|
if (!grid) return;
|
||||||
|
free(grid->cells);
|
||||||
|
free(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 cave_grid_get(const cave_grid* grid, i32 x, i32 y) {
|
||||||
|
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return grid->cells[y * grid->width + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cave_grid_set(cave_grid* grid, i32 x, i32 y, u8 value) {
|
||||||
|
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
grid->cells[y * grid->width + x] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static i32 cave_grid_count_neighbors(const cave_grid* grid, i32 x, i32 y) {
|
||||||
|
i32 count = 0;
|
||||||
|
for (i32 dy = -1; dy <= 1; dy++) {
|
||||||
|
for (i32 dx = -1; dx <= 1; dx++) {
|
||||||
|
if (dx == 0 && dy == 0) continue;
|
||||||
|
if (cave_grid_get(grid, x + dx, y + dy)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calculate_texture_axes(const pxl8_vec3 normal, pxl8_vec3* u_axis, pxl8_vec3* v_axis) {
|
||||||
|
if (fabsf(normal.y) > 0.9f) {
|
||||||
|
*u_axis = (pxl8_vec3){1.0f, 0.0f, 0.0f};
|
||||||
|
*v_axis = (pxl8_vec3){0.0f, 0.0f, 1.0f};
|
||||||
|
} else if (fabsf(normal.x) > 0.7f) {
|
||||||
|
*u_axis = (pxl8_vec3){0.0f, 1.0f, 0.0f};
|
||||||
|
*v_axis = (pxl8_vec3){0.0f, 0.0f, 1.0f};
|
||||||
|
} else {
|
||||||
|
*u_axis = (pxl8_vec3){1.0f, 0.0f, 0.0f};
|
||||||
|
*v_axis = (pxl8_vec3){0.0f, 1.0f, 0.0f};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cave_grid_initialize(cave_grid* grid, f32 density) {
|
||||||
|
for (i32 y = 0; y < grid->height; y++) {
|
||||||
|
for (i32 x = 0; x < grid->width; x++) {
|
||||||
|
u8 value = (prng_float() < density) ? 1 : 0;
|
||||||
|
cave_grid_set(grid, x, y, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cave_grid_smooth(cave_grid* grid) {
|
||||||
|
cave_grid* temp = cave_grid_create(grid->width, grid->height);
|
||||||
|
if (!temp) return;
|
||||||
|
|
||||||
|
for (i32 y = 0; y < grid->height; y++) {
|
||||||
|
for (i32 x = 0; x < grid->width; x++) {
|
||||||
|
i32 neighbors = cave_grid_count_neighbors(grid, x, y);
|
||||||
|
u8 value = (neighbors > 4) ? 1 : 0;
|
||||||
|
cave_grid_set(temp, x, y, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(grid->cells, temp->cells, grid->width * grid->height);
|
||||||
|
cave_grid_destroy(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
|
||||||
|
i32 vertex_count = 0;
|
||||||
|
i32 face_count = 0;
|
||||||
|
i32 floor_ceiling_count = 0;
|
||||||
|
|
||||||
|
for (i32 y = 0; y < grid->height; y++) {
|
||||||
|
for (i32 x = 0; x < grid->width; x++) {
|
||||||
|
if (cave_grid_get(grid, x, y) == 0) {
|
||||||
|
if (cave_grid_get(grid, x - 1, y) == 1) face_count++;
|
||||||
|
if (cave_grid_get(grid, x + 1, y) == 1) face_count++;
|
||||||
|
if (cave_grid_get(grid, x, y - 1) == 1) face_count++;
|
||||||
|
if (cave_grid_get(grid, x, y + 1) == 1) face_count++;
|
||||||
|
floor_ceiling_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
face_count += floor_ceiling_count * 2;
|
||||||
|
vertex_count = face_count * 4;
|
||||||
|
|
||||||
|
pxl8_debug("Cave generation: %dx%d grid -> %d faces, %d vertices",
|
||||||
|
grid->width, grid->height, face_count, vertex_count);
|
||||||
|
|
||||||
|
bsp->vertices = calloc(vertex_count, sizeof(pxl8_bsp_vertex));
|
||||||
|
bsp->faces = calloc(face_count, sizeof(pxl8_bsp_face));
|
||||||
|
bsp->planes = calloc(face_count, sizeof(pxl8_bsp_plane));
|
||||||
|
bsp->edges = calloc(vertex_count, sizeof(pxl8_bsp_edge));
|
||||||
|
bsp->surfedges = calloc(vertex_count, sizeof(i32));
|
||||||
|
bsp->texinfo = calloc(face_count, sizeof(pxl8_bsp_texinfo));
|
||||||
|
|
||||||
|
if (!bsp->vertices || !bsp->faces || !bsp->planes || !bsp->edges || !bsp->surfedges || !bsp->texinfo) {
|
||||||
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 vert_idx = 0;
|
||||||
|
i32 face_idx = 0;
|
||||||
|
i32 edge_idx = 0;
|
||||||
|
|
||||||
|
const f32 cell_size = 64.0f;
|
||||||
|
const f32 wall_height = 128.0f;
|
||||||
|
|
||||||
|
for (i32 y = 0; y < grid->height; y++) {
|
||||||
|
for (i32 x = 0; x < grid->width; x++) {
|
||||||
|
if (cave_grid_get(grid, x, y) == 0) {
|
||||||
|
f32 fx = (f32)x * cell_size;
|
||||||
|
f32 fy = (f32)y * cell_size;
|
||||||
|
|
||||||
|
if (cave_grid_get(grid, x - 1, y) == 1) {
|
||||||
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
||||||
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy};
|
||||||
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
||||||
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
||||||
|
|
||||||
|
bsp->planes[face_idx].normal = (pxl8_vec3){-1, 0, 0};
|
||||||
|
bsp->planes[face_idx].dist = -fx;
|
||||||
|
|
||||||
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
|
bsp->faces[face_idx].texinfo_id = face_idx;
|
||||||
|
|
||||||
|
calculate_texture_axes(bsp->planes[face_idx].normal,
|
||||||
|
&bsp->texinfo[face_idx].u_axis,
|
||||||
|
&bsp->texinfo[face_idx].v_axis);
|
||||||
|
bsp->texinfo[face_idx].u_offset = 0.0f;
|
||||||
|
bsp->texinfo[face_idx].v_offset = 0.0f;
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 4; i++) {
|
||||||
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
||||||
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
vert_idx += 4;
|
||||||
|
edge_idx += 4;
|
||||||
|
face_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cave_grid_get(grid, x + 1, y) == 1) {
|
||||||
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx + cell_size, 0, fy};
|
||||||
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
||||||
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
||||||
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
||||||
|
|
||||||
|
bsp->planes[face_idx].normal = (pxl8_vec3){1, 0, 0};
|
||||||
|
bsp->planes[face_idx].dist = fx + cell_size;
|
||||||
|
|
||||||
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
|
bsp->faces[face_idx].texinfo_id = face_idx;
|
||||||
|
|
||||||
|
calculate_texture_axes(bsp->planes[face_idx].normal,
|
||||||
|
&bsp->texinfo[face_idx].u_axis,
|
||||||
|
&bsp->texinfo[face_idx].v_axis);
|
||||||
|
bsp->texinfo[face_idx].u_offset = 0.0f;
|
||||||
|
bsp->texinfo[face_idx].v_offset = 0.0f;
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 4; i++) {
|
||||||
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
||||||
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
vert_idx += 4;
|
||||||
|
edge_idx += 4;
|
||||||
|
face_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cave_grid_get(grid, x, y - 1) == 1) {
|
||||||
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
||||||
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy};
|
||||||
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
||||||
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy};
|
||||||
|
|
||||||
|
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, -1};
|
||||||
|
bsp->planes[face_idx].dist = -fy;
|
||||||
|
|
||||||
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
|
bsp->faces[face_idx].texinfo_id = face_idx;
|
||||||
|
|
||||||
|
calculate_texture_axes(bsp->planes[face_idx].normal,
|
||||||
|
&bsp->texinfo[face_idx].u_axis,
|
||||||
|
&bsp->texinfo[face_idx].v_axis);
|
||||||
|
bsp->texinfo[face_idx].u_offset = 0.0f;
|
||||||
|
bsp->texinfo[face_idx].v_offset = 0.0f;
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 4; i++) {
|
||||||
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
||||||
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
vert_idx += 4;
|
||||||
|
edge_idx += 4;
|
||||||
|
face_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cave_grid_get(grid, x, y + 1) == 1) {
|
||||||
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
||||||
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
||||||
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
||||||
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
||||||
|
|
||||||
|
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, 1};
|
||||||
|
bsp->planes[face_idx].dist = fy + cell_size;
|
||||||
|
|
||||||
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
|
bsp->faces[face_idx].texinfo_id = face_idx;
|
||||||
|
|
||||||
|
calculate_texture_axes(bsp->planes[face_idx].normal,
|
||||||
|
&bsp->texinfo[face_idx].u_axis,
|
||||||
|
&bsp->texinfo[face_idx].v_axis);
|
||||||
|
bsp->texinfo[face_idx].u_offset = 0.0f;
|
||||||
|
bsp->texinfo[face_idx].v_offset = 0.0f;
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 4; i++) {
|
||||||
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
||||||
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
vert_idx += 4;
|
||||||
|
edge_idx += 4;
|
||||||
|
face_idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i32 y = 0; y < grid->height; y++) {
|
||||||
|
for (i32 x = 0; x < grid->width; x++) {
|
||||||
|
if (cave_grid_get(grid, x, y) == 0) {
|
||||||
|
f32 fx = (f32)x * cell_size;
|
||||||
|
f32 fy = (f32)y * cell_size;
|
||||||
|
|
||||||
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
||||||
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
||||||
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
||||||
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, 0, fy};
|
||||||
|
|
||||||
|
bsp->planes[face_idx].normal = (pxl8_vec3){0, 1, 0};
|
||||||
|
bsp->planes[face_idx].dist = 0;
|
||||||
|
|
||||||
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
|
bsp->faces[face_idx].texinfo_id = face_idx;
|
||||||
|
|
||||||
|
calculate_texture_axes(bsp->planes[face_idx].normal,
|
||||||
|
&bsp->texinfo[face_idx].u_axis,
|
||||||
|
&bsp->texinfo[face_idx].v_axis);
|
||||||
|
bsp->texinfo[face_idx].u_offset = 0.0f;
|
||||||
|
bsp->texinfo[face_idx].v_offset = 0.0f;
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 4; i++) {
|
||||||
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
||||||
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
vert_idx += 4;
|
||||||
|
edge_idx += 4;
|
||||||
|
face_idx++;
|
||||||
|
|
||||||
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, wall_height, fy};
|
||||||
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
||||||
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
||||||
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
||||||
|
|
||||||
|
bsp->planes[face_idx].normal = (pxl8_vec3){0, -1, 0};
|
||||||
|
bsp->planes[face_idx].dist = -wall_height;
|
||||||
|
|
||||||
|
bsp->faces[face_idx].plane_id = face_idx;
|
||||||
|
bsp->faces[face_idx].num_edges = 4;
|
||||||
|
bsp->faces[face_idx].first_edge = edge_idx;
|
||||||
|
bsp->faces[face_idx].texinfo_id = face_idx;
|
||||||
|
|
||||||
|
calculate_texture_axes(bsp->planes[face_idx].normal,
|
||||||
|
&bsp->texinfo[face_idx].u_axis,
|
||||||
|
&bsp->texinfo[face_idx].v_axis);
|
||||||
|
bsp->texinfo[face_idx].u_offset = 0.0f;
|
||||||
|
bsp->texinfo[face_idx].v_offset = 0.0f;
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 4; i++) {
|
||||||
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||||
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
||||||
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
vert_idx += 4;
|
||||||
|
edge_idx += 4;
|
||||||
|
face_idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bsp->num_vertices = vertex_count;
|
||||||
|
bsp->num_faces = face_count;
|
||||||
|
bsp->num_planes = face_count;
|
||||||
|
bsp->num_edges = vertex_count;
|
||||||
|
bsp->num_surfedges = vertex_count;
|
||||||
|
bsp->num_texinfo = face_count;
|
||||||
|
|
||||||
|
bsp->leafs = calloc(1, sizeof(pxl8_bsp_leaf));
|
||||||
|
bsp->marksurfaces = calloc(face_count, sizeof(u16));
|
||||||
|
|
||||||
|
if (!bsp->leafs || !bsp->marksurfaces) {
|
||||||
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
bsp->num_leafs = 1;
|
||||||
|
bsp->num_marksurfaces = face_count;
|
||||||
|
|
||||||
|
bsp->leafs[0].first_marksurface = 0;
|
||||||
|
bsp->leafs[0].num_marksurfaces = face_count;
|
||||||
|
bsp->leafs[0].contents = -2;
|
||||||
|
|
||||||
|
for (i32 i = 0; i < face_count; i++) {
|
||||||
|
bsp->marksurfaces[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PXL8_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pxl8_result procgen_cave(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
|
||||||
|
prng_seed(params->seed);
|
||||||
|
|
||||||
|
cave_grid* grid = cave_grid_create(params->width, params->height);
|
||||||
|
if (!grid) {
|
||||||
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
cave_grid_initialize(grid, params->density);
|
||||||
|
|
||||||
|
for (i32 i = 0; i < params->iterations; i++) {
|
||||||
|
cave_grid_smooth(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_result result = cave_to_bsp(bsp, grid);
|
||||||
|
cave_grid_destroy(grid);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
|
||||||
|
if (!bsp || !params) {
|
||||||
|
return PXL8_ERROR_NULL_POINTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (params->type) {
|
||||||
|
case PXL8_PROCGEN_CAVE:
|
||||||
|
return procgen_cave(bsp, params);
|
||||||
|
|
||||||
|
case PXL8_PROCGEN_DUNGEON:
|
||||||
|
pxl8_error("Dungeon generation not yet implemented");
|
||||||
|
return PXL8_ERROR_NOT_INITIALIZED;
|
||||||
|
|
||||||
|
case PXL8_PROCGEN_TERRAIN:
|
||||||
|
pxl8_error("Terrain generation not yet implemented");
|
||||||
|
return PXL8_ERROR_NOT_INITIALIZED;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pxl8_error("Unknown procgen type: %d", params->type);
|
||||||
|
return PXL8_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params) {
|
||||||
|
if (!buffer || !params) return;
|
||||||
|
|
||||||
|
prng_seed(params->seed);
|
||||||
|
|
||||||
|
u8 min_val = 255, max_val = 0;
|
||||||
|
|
||||||
|
for (i32 y = 0; y < params->height; y++) {
|
||||||
|
for (i32 x = 0; x < params->width; x++) {
|
||||||
|
i32 ix = (i32)((f32)x * params->scale);
|
||||||
|
i32 iy = (i32)((f32)y * params->scale);
|
||||||
|
|
||||||
|
u32 block_hash = (ix * 374761393 + iy * 668265263) ^ params->seed;
|
||||||
|
block_hash ^= block_hash >> 13;
|
||||||
|
block_hash ^= block_hash << 17;
|
||||||
|
block_hash ^= block_hash >> 5;
|
||||||
|
|
||||||
|
u32 pixel_hash = (x * 1597334677 + y * 3812015801) ^ params->seed;
|
||||||
|
pixel_hash ^= pixel_hash >> 13;
|
||||||
|
pixel_hash ^= pixel_hash << 17;
|
||||||
|
pixel_hash ^= pixel_hash >> 5;
|
||||||
|
|
||||||
|
u32 combined = (block_hash * 3 + pixel_hash) / 4;
|
||||||
|
|
||||||
|
u32 value_range = params->variation + 1;
|
||||||
|
i32 value = params->base_color + (combined % value_range);
|
||||||
|
|
||||||
|
if (value < 0) value = 0;
|
||||||
|
if (value > 15) value = 15;
|
||||||
|
|
||||||
|
u8 final_value = (u8)value;
|
||||||
|
buffer[y * params->width + x] = final_value;
|
||||||
|
|
||||||
|
if (final_value < min_val) min_val = final_value;
|
||||||
|
if (final_value > max_val) max_val = final_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_debug("Generated texture %dx%d: values range %u-%u (base=%u, variation=%u)",
|
||||||
|
params->width, params->height, min_val, max_val,
|
||||||
|
params->base_color, params->variation);
|
||||||
|
}
|
||||||
56
src/pxl8_procgen.h
Normal file
56
src/pxl8_procgen.h
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_bsp.h"
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
typedef enum pxl8_procgen_type {
|
||||||
|
PXL8_PROCGEN_CAVE,
|
||||||
|
PXL8_PROCGEN_DUNGEON,
|
||||||
|
PXL8_PROCGEN_TERRAIN
|
||||||
|
} pxl8_procgen_type;
|
||||||
|
|
||||||
|
typedef struct pxl8_procgen_params {
|
||||||
|
pxl8_procgen_type type;
|
||||||
|
|
||||||
|
i32 width;
|
||||||
|
i32 height;
|
||||||
|
i32 depth;
|
||||||
|
u32 seed;
|
||||||
|
|
||||||
|
f32 density;
|
||||||
|
i32 iterations;
|
||||||
|
|
||||||
|
void* type_params;
|
||||||
|
} pxl8_procgen_params;
|
||||||
|
|
||||||
|
typedef struct pxl8_procgen_cave_params {
|
||||||
|
i32 min_cave_size;
|
||||||
|
} pxl8_procgen_cave_params;
|
||||||
|
|
||||||
|
typedef struct pxl8_procgen_dungeon_params {
|
||||||
|
i32 room_count;
|
||||||
|
i32 min_room_size;
|
||||||
|
i32 max_room_size;
|
||||||
|
i32 corridor_width;
|
||||||
|
} pxl8_procgen_dungeon_params;
|
||||||
|
|
||||||
|
typedef struct pxl8_procgen_tex_params {
|
||||||
|
u32 seed;
|
||||||
|
i32 width;
|
||||||
|
i32 height;
|
||||||
|
f32 scale;
|
||||||
|
f32 roughness;
|
||||||
|
u8 base_color;
|
||||||
|
u8 variation;
|
||||||
|
} pxl8_procgen_tex_params;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params);
|
||||||
|
void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -208,13 +208,31 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"pxl8_mat4 pxl8_mat4_scale(float x, float y, float z);\n"
|
"pxl8_mat4 pxl8_mat4_scale(float x, float y, float z);\n"
|
||||||
"pxl8_mat4 pxl8_mat4_translate(float x, float y, float z);\n"
|
"pxl8_mat4 pxl8_mat4_translate(float x, float y, float z);\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
"typedef enum pxl8_procgen_type {\n"
|
||||||
|
" PXL8_PROCGEN_CAVE = 0,\n"
|
||||||
|
" PXL8_PROCGEN_DUNGEON = 1,\n"
|
||||||
|
" PXL8_PROCGEN_TERRAIN = 2\n"
|
||||||
|
"} pxl8_procgen_type;\n"
|
||||||
|
"\n"
|
||||||
|
"typedef struct pxl8_procgen_params {\n"
|
||||||
|
" pxl8_procgen_type type;\n"
|
||||||
|
" int width;\n"
|
||||||
|
" int height;\n"
|
||||||
|
" int depth;\n"
|
||||||
|
" unsigned int seed;\n"
|
||||||
|
" float density;\n"
|
||||||
|
" int iterations;\n"
|
||||||
|
" void* type_params;\n"
|
||||||
|
"} pxl8_procgen_params;\n"
|
||||||
|
"\n"
|
||||||
"typedef struct pxl8_world pxl8_world;\n"
|
"typedef struct pxl8_world pxl8_world;\n"
|
||||||
"pxl8_world* pxl8_world_create(void);\n"
|
"pxl8_world* pxl8_world_create(void);\n"
|
||||||
"void pxl8_world_destroy(pxl8_world* world);\n"
|
"void pxl8_world_destroy(pxl8_world* world);\n"
|
||||||
|
"int pxl8_world_generate(pxl8_world* world, const pxl8_procgen_params* params);\n"
|
||||||
|
"bool pxl8_world_is_loaded(const pxl8_world* world);\n"
|
||||||
"int pxl8_world_load(pxl8_world* world, const char* path);\n"
|
"int pxl8_world_load(pxl8_world* world, const char* path);\n"
|
||||||
"void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n"
|
"void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n"
|
||||||
"void pxl8_world_unload(pxl8_world* world);\n"
|
"void pxl8_world_unload(pxl8_world* world);\n"
|
||||||
"bool pxl8_world_is_loaded(const pxl8_world* world);\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"typedef struct pxl8_ui pxl8_ui;\n"
|
"typedef struct pxl8_ui pxl8_ui;\n"
|
||||||
"typedef struct { unsigned char bg_color; unsigned int sprite_id; int corner_size; int edge_size; int padding; } pxl8_frame_theme;\n"
|
"typedef struct { unsigned char bg_color; unsigned int sprite_id; int corner_size; int edge_size; int padding; } pxl8_frame_theme;\n"
|
||||||
|
|
|
||||||
|
|
@ -294,9 +294,11 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_EVENT_KEY_DOWN: {
|
case SDL_EVENT_KEY_DOWN: {
|
||||||
|
#ifdef DEBUG
|
||||||
if (event->key.key == SDLK_ESCAPE) {
|
if (event->key.key == SDLK_ESCAPE) {
|
||||||
game->running = false;
|
game->running = false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SDL_Scancode scancode = event->key.scancode;
|
SDL_Scancode scancode = event->key.scancode;
|
||||||
if (scancode < 256) {
|
if (scancode < 256) {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@
|
||||||
|
|
||||||
#include "pxl8_bsp.h"
|
#include "pxl8_bsp.h"
|
||||||
#include "pxl8_macros.h"
|
#include "pxl8_macros.h"
|
||||||
|
#include "pxl8_procgen.h"
|
||||||
#include "pxl8_world.h"
|
#include "pxl8_world.h"
|
||||||
|
|
||||||
struct pxl8_world {
|
struct pxl8_world {
|
||||||
pxl8_bsp bsp;
|
pxl8_bsp bsp;
|
||||||
bool loaded;
|
bool loaded;
|
||||||
|
bool wireframe;
|
||||||
u32 wireframe_color;
|
u32 wireframe_color;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -34,6 +36,29 @@ void pxl8_world_destroy(pxl8_world* world) {
|
||||||
free(world);
|
free(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pxl8_result pxl8_world_generate(pxl8_world* world, const pxl8_procgen_params* params) {
|
||||||
|
if (!world || !params) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
if (world->loaded) {
|
||||||
|
pxl8_bsp_destroy(&world->bsp);
|
||||||
|
world->loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&world->bsp, 0, sizeof(pxl8_bsp));
|
||||||
|
|
||||||
|
pxl8_result result = pxl8_procgen(&world->bsp, params);
|
||||||
|
if (result != PXL8_OK) {
|
||||||
|
pxl8_error("Failed to generate world");
|
||||||
|
pxl8_bsp_destroy(&world->bsp);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
world->loaded = true;
|
||||||
|
pxl8_info("Generated world");
|
||||||
|
|
||||||
|
return PXL8_OK;
|
||||||
|
}
|
||||||
|
|
||||||
pxl8_result pxl8_world_load(pxl8_world* world, const char* path) {
|
pxl8_result pxl8_world_load(pxl8_world* world, const char* path) {
|
||||||
if (!world || !path) return PXL8_ERROR_INVALID_ARGUMENT;
|
if (!world || !path) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
|
@ -59,7 +84,39 @@ pxl8_result pxl8_world_load(pxl8_world* world, const char* path) {
|
||||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||||
if (!world || !gfx || !world->loaded) return;
|
if (!world || !gfx || !world->loaded) return;
|
||||||
|
|
||||||
|
static bool texture_generated = false;
|
||||||
|
static u32 wall_texture_id = 0;
|
||||||
|
if (!texture_generated) {
|
||||||
|
u8 texture_data[64 * 64];
|
||||||
|
|
||||||
|
pxl8_procgen_tex_params tex_params = {
|
||||||
|
.seed = 12345,
|
||||||
|
.width = 64,
|
||||||
|
.height = 64,
|
||||||
|
.scale = 0.25f,
|
||||||
|
.roughness = 0.5f,
|
||||||
|
.base_color = 1,
|
||||||
|
.variation = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
pxl8_procgen_tex(texture_data, &tex_params);
|
||||||
|
|
||||||
|
pxl8_result result = pxl8_gfx_create_texture(gfx, texture_data, 64, 64);
|
||||||
|
if (result >= 0) {
|
||||||
|
wall_texture_id = (u32)result;
|
||||||
|
pxl8_gfx_upload_atlas(gfx);
|
||||||
|
texture_generated = true;
|
||||||
|
pxl8_info("Generated stone texture with ID: %u", wall_texture_id);
|
||||||
|
} else {
|
||||||
|
pxl8_error("Failed to create wall texture: %d", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world->wireframe) {
|
||||||
pxl8_bsp_render_wireframe(gfx, &world->bsp, camera_pos, world->wireframe_color);
|
pxl8_bsp_render_wireframe(gfx, &world->bsp, camera_pos, world->wireframe_color);
|
||||||
|
} else {
|
||||||
|
pxl8_bsp_render_solid(gfx, &world->bsp, camera_pos, wall_texture_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_world_unload(pxl8_world* world) {
|
void pxl8_world_unload(pxl8_world* world) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "pxl8_gfx.h"
|
#include "pxl8_gfx.h"
|
||||||
#include "pxl8_math.h"
|
#include "pxl8_math.h"
|
||||||
|
#include "pxl8_procgen.h"
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
typedef struct pxl8_world pxl8_world;
|
typedef struct pxl8_world pxl8_world;
|
||||||
|
|
@ -13,12 +14,12 @@ extern "C" {
|
||||||
pxl8_world* pxl8_world_create(void);
|
pxl8_world* pxl8_world_create(void);
|
||||||
void pxl8_world_destroy(pxl8_world* world);
|
void pxl8_world_destroy(pxl8_world* world);
|
||||||
|
|
||||||
|
pxl8_result pxl8_world_generate(pxl8_world* world, const pxl8_procgen_params* params);
|
||||||
|
bool pxl8_world_is_loaded(const pxl8_world* world);
|
||||||
pxl8_result pxl8_world_load(pxl8_world* world, const char* path);
|
pxl8_result pxl8_world_load(pxl8_world* world, const char* path);
|
||||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
||||||
void pxl8_world_unload(pxl8_world* world);
|
void pxl8_world_unload(pxl8_world* world);
|
||||||
|
|
||||||
bool pxl8_world_is_loaded(const pxl8_world* world);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue