use tiled atlas texture sampling, increase shader speed using inv sqrt
This commit is contained in:
parent
e92748c2d9
commit
c4226b36fe
34 changed files with 1045 additions and 520 deletions
|
|
@ -353,10 +353,10 @@
|
||||||
|
|
||||||
(pxl8.push_target)
|
(pxl8.push_target)
|
||||||
(pxl8.begin_frame_3d camera lights {
|
(pxl8.begin_frame_3d camera lights {
|
||||||
:ambient 30
|
:ambient 25
|
||||||
:fog_density 0.0
|
:fog_density 0.0
|
||||||
:celestial_dir [0.5 -0.8 0.3]
|
:celestial_dir [0.5 -0.8 0.3]
|
||||||
:celestial_intensity 0.5})
|
:celestial_intensity 0.3})
|
||||||
(pxl8.clear_depth)
|
(pxl8.clear_depth)
|
||||||
|
|
||||||
(sky.update-gradient 1 2 6 6 10 18)
|
(sky.update-gradient 1 2 6 6 10 18)
|
||||||
|
|
@ -364,7 +364,7 @@
|
||||||
(sky.render-stars smooth-cam-x eye-y smooth-cam-z 1.0 last-dt)
|
(sky.render-stars smooth-cam-x eye-y smooth-cam-z 1.0 last-dt)
|
||||||
(pxl8.clear_depth)
|
(pxl8.clear_depth)
|
||||||
|
|
||||||
(world:set_wireframe (menu.is-wireframe))
|
(pxl8.set_wireframe (menu.is-wireframe))
|
||||||
(world:render [smooth-cam-x eye-y smooth-cam-z])
|
(world:render [smooth-cam-x eye-y smooth-cam-z])
|
||||||
|
|
||||||
(when chunk
|
(when chunk
|
||||||
|
|
|
||||||
|
|
@ -136,18 +136,18 @@
|
||||||
(set selected-item nil)))
|
(set selected-item nil)))
|
||||||
|
|
||||||
(fn draw-gfx-panel []
|
(fn draw-gfx-panel []
|
||||||
(pxl8.gui_window 200 100 240 280 "GFX")
|
(pxl8.gui_window 200 60 240 220 "GFX")
|
||||||
|
|
||||||
(let [baked-label (if baked-lighting "Baked Lighting: On" "Baked Lighting: Off")]
|
(let [baked-label (if baked-lighting "Baked Lighting: On" "Baked Lighting: Off")]
|
||||||
(when (menu-button 40 215 147 210 30 baked-label)
|
(when (menu-button 40 215 107 210 24 baked-label)
|
||||||
(set baked-lighting (not baked-lighting))))
|
(set baked-lighting (not baked-lighting))))
|
||||||
|
|
||||||
(let [dynamic-label (if dynamic-lighting "Dynamic Lighting: On" "Dynamic Lighting: Off")]
|
(let [dynamic-label (if dynamic-lighting "Dynamic Lighting: On" "Dynamic Lighting: Off")]
|
||||||
(when (menu-button 41 215 182 210 30 dynamic-label)
|
(when (menu-button 41 215 134 210 24 dynamic-label)
|
||||||
(set dynamic-lighting (not dynamic-lighting))))
|
(set dynamic-lighting (not dynamic-lighting))))
|
||||||
|
|
||||||
(pxl8.gui_label 215 220 (.. "Render: " render-distance) 15)
|
(pxl8.gui_label 215 162 (.. "Render: " render-distance) 15)
|
||||||
(let [(changed new-val) (gui:slider_int 30 215 235 210 16 render-distance 1 8)]
|
(let [(changed new-val) (gui:slider_int 30 215 175 210 14 render-distance 1 8)]
|
||||||
(when changed
|
(when changed
|
||||||
(set render-distance new-val)
|
(set render-distance new-val)
|
||||||
(let [w (world-mod.World.get)
|
(let [w (world-mod.World.get)
|
||||||
|
|
@ -156,14 +156,14 @@
|
||||||
(when n (n:set_chunk_settings new-val sim-distance)))))
|
(when n (n:set_chunk_settings new-val sim-distance)))))
|
||||||
|
|
||||||
(let [tex-label (if textures "Textures: On" "Textures: Off")]
|
(let [tex-label (if textures "Textures: On" "Textures: Off")]
|
||||||
(when (menu-button 42 215 260 210 30 tex-label)
|
(when (menu-button 42 215 194 210 24 tex-label)
|
||||||
(set textures (not textures))))
|
(set textures (not textures))))
|
||||||
|
|
||||||
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
|
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
|
||||||
(when (menu-button 43 215 295 210 30 wire-label)
|
(when (menu-button 43 215 221 210 24 wire-label)
|
||||||
(set wireframe (not wireframe))))
|
(set wireframe (not wireframe))))
|
||||||
|
|
||||||
(when (menu-button 32 215 340 210 30 "Back")
|
(when (menu-button 32 215 248 210 24 "Back")
|
||||||
(set current-panel :main)
|
(set current-panel :main)
|
||||||
(set selected-item nil)))
|
(set selected-item nil)))
|
||||||
|
|
||||||
|
|
|
||||||
96
pxl8.sh
96
pxl8.sh
|
|
@ -8,7 +8,7 @@ if command -v ccache >/dev/null 2>&1; then
|
||||||
CC="ccache $CC"
|
CC="ccache $CC"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CFLAGS="-std=c23 -Wall -Wextra"
|
CFLAGS="-std=c23 -Wall -Wextra -Wno-missing-braces"
|
||||||
LIBS="-lm"
|
LIBS="-lm"
|
||||||
MODE="debug"
|
MODE="debug"
|
||||||
BUILDDIR=".build"
|
BUILDDIR=".build"
|
||||||
|
|
@ -170,8 +170,8 @@ compile_source_file() {
|
||||||
compile_shaders() {
|
compile_shaders() {
|
||||||
local build_mode="$1"
|
local build_mode="$1"
|
||||||
local shader_dir="src/gfx/shaders/cpu"
|
local shader_dir="src/gfx/shaders/cpu"
|
||||||
local so_dir=".build/shaders/cpu"
|
local so_dir=".build/$build_mode/shaders/cpu"
|
||||||
local obj_dir=".build/shaders/cpu/obj"
|
local obj_dir=".build/$build_mode/shaders/cpu/obj"
|
||||||
|
|
||||||
if [[ ! -d "$shader_dir" ]]; then
|
if [[ ! -d "$shader_dir" ]]; then
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -240,15 +240,17 @@ print_usage() {
|
||||||
echo " clean Remove build artifacts"
|
echo " clean Remove build artifacts"
|
||||||
echo " help Show this help message"
|
echo " help Show this help message"
|
||||||
echo " install Install pxl8 to ~/.local/bin"
|
echo " install Install pxl8 to ~/.local/bin"
|
||||||
|
echo " profile Profile with perf and generate flamegraph (Linux)"
|
||||||
echo " run Build and run pxl8 (optional: cart.pxc or folder)"
|
echo " run Build and run pxl8 (optional: cart.pxc or folder)"
|
||||||
echo " update Download/update all dependencies"
|
echo " update Download/update all dependencies"
|
||||||
echo " vendor Fetch source for dependencies (ex. SDL3)"
|
echo " vendor Fetch source for dependencies (ex. SDL3)"
|
||||||
echo
|
echo
|
||||||
echo -e "${BOLD}OPTIONS:${NC}"
|
echo -e "${BOLD}OPTIONS:${NC}"
|
||||||
echo " --all Clean both build artifacts and dependencies"
|
echo " --all Clean both build artifacts and dependencies"
|
||||||
echo " --cache Clear ccache (use with clean)"
|
echo " --cache Clear ccache (use with clean)"
|
||||||
echo " --deps Clean only dependencies"
|
echo " --deps Clean only dependencies"
|
||||||
echo " --release Build/run/clean in release mode (default: debug)"
|
echo " --duration=N Profile duration in seconds (default: 30)"
|
||||||
|
echo " --release Build/run/clean in release mode (default: debug)"
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_sdl3() {
|
setup_sdl3() {
|
||||||
|
|
@ -324,6 +326,20 @@ update_fennel() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_flamegraph() {
|
||||||
|
print_info "Fetching FlameGraph"
|
||||||
|
|
||||||
|
if [[ -d "lib/FlameGraph/.git" ]]; then
|
||||||
|
cd lib/FlameGraph && git pull --quiet origin master
|
||||||
|
cd - > /dev/null
|
||||||
|
else
|
||||||
|
rm -rf lib/FlameGraph
|
||||||
|
git clone --quiet https://github.com/brendangregg/FlameGraph.git lib/FlameGraph
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_info "Updated FlameGraph"
|
||||||
|
}
|
||||||
|
|
||||||
update_linenoise() {
|
update_linenoise() {
|
||||||
print_info "Fetching linenoise"
|
print_info "Fetching linenoise"
|
||||||
|
|
||||||
|
|
@ -690,6 +706,72 @@ case "$COMMAND" in
|
||||||
bash tools/aseprite/pxl8-ase.sh "$@"
|
bash tools/aseprite/pxl8-ase.sh "$@"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
profile)
|
||||||
|
if [[ "$(uname)" != "Linux" ]]; then
|
||||||
|
print_error "Profiling with perf is only supported on Linux"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v perf >/dev/null 2>&1; then
|
||||||
|
print_error "perf not found. Install linux-tools or perf package."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "lib/FlameGraph" ]]; then
|
||||||
|
mkdir -p lib
|
||||||
|
update_flamegraph
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$0" build || exit 1
|
||||||
|
|
||||||
|
PROFILE_DIR=".build/debug/profile"
|
||||||
|
mkdir -p "$PROFILE_DIR"
|
||||||
|
|
||||||
|
CART=""
|
||||||
|
PERF_DURATION=30
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ "$arg" =~ ^--duration=([0-9]+)$ ]]; then
|
||||||
|
PERF_DURATION="${BASH_REMATCH[1]}"
|
||||||
|
elif [[ "$arg" != "--release" ]] && [[ -z "$CART" ]]; then
|
||||||
|
CART="$arg"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z "$CART" ]] && CART="demo"
|
||||||
|
|
||||||
|
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
PERF_DATA="$PROFILE_DIR/perf_${TIMESTAMP}.data"
|
||||||
|
PERF_SCRIPT="$PROFILE_DIR/perf_${TIMESTAMP}.perf"
|
||||||
|
FOLDED="$PROFILE_DIR/perf_${TIMESTAMP}.folded"
|
||||||
|
SVG="$PROFILE_DIR/flamegraph_${TIMESTAMP}.svg"
|
||||||
|
|
||||||
|
print_info "Starting server..."
|
||||||
|
./bin/debug/pxl8d &
|
||||||
|
SERVER_PID=$!
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
trap "kill $SERVER_PID 2>/dev/null; wait $SERVER_PID 2>/dev/null" EXIT
|
||||||
|
|
||||||
|
print_info "Profiling pxl8 for ${PERF_DURATION}s (Ctrl+C to stop early)..."
|
||||||
|
perf record -F 99 -g --call-graph dwarf -o "$PERF_DATA" -- \
|
||||||
|
timeout "${PERF_DURATION}s" ./bin/debug/pxl8 "$CART" 2>/dev/null || true
|
||||||
|
|
||||||
|
print_info "Processing profile data..."
|
||||||
|
perf script -i "$PERF_DATA" > "$PERF_SCRIPT"
|
||||||
|
|
||||||
|
print_info "Generating flamegraph..."
|
||||||
|
lib/FlameGraph/stackcollapse-perf.pl "$PERF_SCRIPT" > "$FOLDED"
|
||||||
|
lib/FlameGraph/flamegraph.pl --cp --colors orange --title "pxl8 profile" "$FOLDED" > "$SVG"
|
||||||
|
|
||||||
|
rm -f "$PERF_DATA" "$PERF_SCRIPT" "$FOLDED"
|
||||||
|
|
||||||
|
print_info "Flamegraph: $SVG"
|
||||||
|
|
||||||
|
if command -v xdg-open >/dev/null 2>&1; then
|
||||||
|
xdg-open "$SVG" 2>/dev/null &
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
help|--help|-h|"")
|
help|--help|-h|"")
|
||||||
print_usage
|
print_usage
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,8 @@ fn main() {
|
||||||
|
|
||||||
let bindings = bindgen::Builder::default()
|
let bindings = bindgen::Builder::default()
|
||||||
.header(pxl8_src.join("core/pxl8_log.h").to_str().unwrap())
|
.header(pxl8_src.join("core/pxl8_log.h").to_str().unwrap())
|
||||||
.header(pxl8_src.join("sim/pxl8_sim.h").to_str().unwrap())
|
|
||||||
.header(pxl8_src.join("vxl/pxl8_vxl.h").to_str().unwrap())
|
|
||||||
.header(pxl8_src.join("math/pxl8_noise.h").to_str().unwrap())
|
.header(pxl8_src.join("math/pxl8_noise.h").to_str().unwrap())
|
||||||
|
.header(pxl8_src.join("sim/pxl8_sim.h").to_str().unwrap())
|
||||||
.clang_arg(format!("-I{}", pxl8_src.join("bsp").display()))
|
.clang_arg(format!("-I{}", pxl8_src.join("bsp").display()))
|
||||||
.clang_arg(format!("-I{}", pxl8_src.join("core").display()))
|
.clang_arg(format!("-I{}", pxl8_src.join("core").display()))
|
||||||
.clang_arg(format!("-I{}", pxl8_src.join("math").display()))
|
.clang_arg(format!("-I{}", pxl8_src.join("math").display()))
|
||||||
|
|
@ -50,6 +49,11 @@ fn main() {
|
||||||
.blocklist_item("FP_ZERO")
|
.blocklist_item("FP_ZERO")
|
||||||
.blocklist_item("FP_SUBNORMAL")
|
.blocklist_item("FP_SUBNORMAL")
|
||||||
.blocklist_item("FP_NORMAL")
|
.blocklist_item("FP_NORMAL")
|
||||||
|
.blocklist_type("pxl8_vec2")
|
||||||
|
.blocklist_type("pxl8_vec3")
|
||||||
|
.blocklist_type("pxl8_vec4")
|
||||||
|
.blocklist_type("pxl8_mat4")
|
||||||
|
.raw_line("pub use crate::math::{pxl8_vec2, pxl8_vec3, pxl8_vec4, pxl8_mat4};")
|
||||||
.clang_arg("-DPXL8_NO_SIMD")
|
.clang_arg("-DPXL8_NO_SIMD")
|
||||||
.use_core()
|
.use_core()
|
||||||
.rustified_enum(".*")
|
.rustified_enum(".*")
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,38 @@ impl Default for CellPortals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for Face {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
first_edge: self.first_edge,
|
||||||
|
lightmap_offset: self.lightmap_offset,
|
||||||
|
num_edges: self.num_edges,
|
||||||
|
plane_id: self.plane_id,
|
||||||
|
side: self.side,
|
||||||
|
styles: self.styles,
|
||||||
|
material_id: self.material_id,
|
||||||
|
aabb_min: self.aabb_min,
|
||||||
|
aabb_max: self.aabb_max,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Plane {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
dist: self.dist,
|
||||||
|
normal: self.normal,
|
||||||
|
type_: self.type_,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Vertex {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self { position: self.position }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Bsp {
|
pub struct Bsp {
|
||||||
inner: pxl8_bsp,
|
inner: pxl8_bsp,
|
||||||
pub cell_portals: Box<[CellPortals]>,
|
pub cell_portals: Box<[CellPortals]>,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,43 @@
|
||||||
use core::ops::{Add, Mul, Sub};
|
use core::ops::{Add, Mul, Sub};
|
||||||
|
|
||||||
use crate::pxl8::pxl8_vec3;
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Vec2 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
pub type Vec3 = pxl8_vec3;
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Vec3 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Vec4 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
pub w: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Mat4 {
|
||||||
|
pub m: [f32; 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type pxl8_vec2 = Vec2;
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type pxl8_vec3 = Vec3;
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type pxl8_vec4 = Vec4;
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type pxl8_mat4 = Mat4;
|
||||||
|
|
||||||
pub const VEC3_ZERO: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 0.0 };
|
pub const VEC3_ZERO: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 0.0 };
|
||||||
pub const VEC3_Y: Vec3 = Vec3 { x: 0.0, y: 1.0, z: 0.0 };
|
pub const VEC3_Y: Vec3 = Vec3 { x: 0.0, y: 1.0, z: 0.0 };
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use libm::sqrtf;
|
||||||
|
|
||||||
use crate::bsp::{Bsp, BspBuilder, CellPortals, Edge, Face, Leaf, Node, Plane, Portal, Vertex};
|
use crate::bsp::{Bsp, BspBuilder, CellPortals, Edge, Face, Leaf, Node, Plane, Portal, Vertex};
|
||||||
use crate::math::{Vec3, Vec3Ext};
|
use crate::math::{Vec3, Vec3Ext};
|
||||||
|
use crate::pxl8::{pxl8_vec3_cross, pxl8_vec3_dot, pxl8_vec3_normalize, pxl8_vec3_scale, pxl8_vec3_add, pxl8_vec3_sub};
|
||||||
|
|
||||||
pub const CELL_SIZE: f32 = 64.0;
|
pub const CELL_SIZE: f32 = 64.0;
|
||||||
pub const WALL_HEIGHT: f32 = 128.0;
|
pub const WALL_HEIGHT: f32 = 128.0;
|
||||||
|
|
@ -402,36 +403,139 @@ fn build_pvs_data(bsp: &mut BspBuilder, portals: &[CellPortals]) {
|
||||||
bsp.visdata = visdata;
|
bsp.visdata = visdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AO_NUM_SAMPLES: usize = 16;
|
||||||
|
const AO_RAY_LENGTH: f32 = 48.0;
|
||||||
|
|
||||||
|
fn generate_hemisphere_samples(normal: Vec3) -> [Vec3; AO_NUM_SAMPLES] {
|
||||||
|
let tangent = if normal.y.abs() < 0.9 {
|
||||||
|
unsafe { pxl8_vec3_normalize(pxl8_vec3_cross(normal, Vec3::new(0.0, 1.0, 0.0))) }
|
||||||
|
} else {
|
||||||
|
unsafe { pxl8_vec3_normalize(pxl8_vec3_cross(normal, Vec3::new(1.0, 0.0, 0.0))) }
|
||||||
|
};
|
||||||
|
let bitangent = unsafe { pxl8_vec3_cross(normal, tangent) };
|
||||||
|
|
||||||
|
let mut samples = [Vec3::new(0.0, 0.0, 0.0); AO_NUM_SAMPLES];
|
||||||
|
for i in 0..AO_NUM_SAMPLES {
|
||||||
|
let phi = (i as f32 / AO_NUM_SAMPLES as f32) * core::f32::consts::TAU;
|
||||||
|
let theta = ((i as f32 + 0.5) / AO_NUM_SAMPLES as f32) * (core::f32::consts::PI * 0.45);
|
||||||
|
let (sin_phi, cos_phi) = (libm::sinf(phi), libm::cosf(phi));
|
||||||
|
let (sin_theta, cos_theta) = (libm::sinf(theta), libm::cosf(theta));
|
||||||
|
|
||||||
|
let local_x = sin_theta * cos_phi;
|
||||||
|
let local_y = cos_theta;
|
||||||
|
let local_z = sin_theta * sin_phi;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let t_contrib = pxl8_vec3_scale(tangent, local_x);
|
||||||
|
let n_contrib = pxl8_vec3_scale(normal, local_y);
|
||||||
|
let b_contrib = pxl8_vec3_scale(bitangent, local_z);
|
||||||
|
samples[i] = pxl8_vec3_normalize(pxl8_vec3_add(pxl8_vec3_add(t_contrib, n_contrib), b_contrib));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
samples
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ray_triangle_intersect(origin: Vec3, dir: Vec3, v0: Vec3, v1: Vec3, v2: Vec3, max_dist: f32) -> bool {
|
||||||
|
let edge1 = unsafe { pxl8_vec3_sub(v1, v0) };
|
||||||
|
let edge2 = unsafe { pxl8_vec3_sub(v2, v0) };
|
||||||
|
let h = unsafe { pxl8_vec3_cross(dir, edge2) };
|
||||||
|
let a = unsafe { pxl8_vec3_dot(edge1, h) };
|
||||||
|
|
||||||
|
if a > -0.0001 && a < 0.0001 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let f = 1.0 / a;
|
||||||
|
let s = unsafe { pxl8_vec3_sub(origin, v0) };
|
||||||
|
let u = f * unsafe { pxl8_vec3_dot(s, h) };
|
||||||
|
if u < 0.0 || u > 1.0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let q = unsafe { pxl8_vec3_cross(s, edge1) };
|
||||||
|
let v = f * unsafe { pxl8_vec3_dot(dir, q) };
|
||||||
|
if v < 0.0 || u + v > 1.0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = f * unsafe { pxl8_vec3_dot(edge2, q) };
|
||||||
|
t > 0.001 && t < max_dist
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_vertex_ao(bsp: &BspBuilder, pos: Vec3, normal: Vec3) -> f32 {
|
||||||
|
let samples = generate_hemisphere_samples(normal);
|
||||||
|
let offset_pos = unsafe { pxl8_vec3_add(pos, pxl8_vec3_scale(normal, 0.5)) };
|
||||||
|
|
||||||
|
let mut occluded = 0;
|
||||||
|
|
||||||
|
for sample_dir in &samples {
|
||||||
|
'face_loop: for face in &bsp.faces {
|
||||||
|
if face.num_edges < 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut verts = [Vec3::new(0.0, 0.0, 0.0); 4];
|
||||||
|
let mut num_verts = 0usize;
|
||||||
|
|
||||||
|
for e in 0..face.num_edges.min(4) {
|
||||||
|
let surfedge_idx = (face.first_edge + e as u32) as usize;
|
||||||
|
if surfedge_idx >= bsp.surfedges.len() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let edge_idx = bsp.surfedges[surfedge_idx];
|
||||||
|
let vert_idx = if edge_idx >= 0 {
|
||||||
|
let ei = edge_idx as usize;
|
||||||
|
if ei >= bsp.edges.len() { continue; }
|
||||||
|
bsp.edges[ei].vertex[0] as usize
|
||||||
|
} else {
|
||||||
|
let ei = (-edge_idx) as usize;
|
||||||
|
if ei >= bsp.edges.len() { continue; }
|
||||||
|
bsp.edges[ei].vertex[1] as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
if vert_idx < bsp.vertices.len() {
|
||||||
|
verts[num_verts] = bsp.vertices[vert_idx].position;
|
||||||
|
num_verts += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if num_verts >= 3 {
|
||||||
|
if ray_triangle_intersect(offset_pos, *sample_dir, verts[0], verts[1], verts[2], AO_RAY_LENGTH) {
|
||||||
|
occluded += 1;
|
||||||
|
break 'face_loop;
|
||||||
|
}
|
||||||
|
if num_verts == 4 {
|
||||||
|
if ray_triangle_intersect(offset_pos, *sample_dir, verts[0], verts[2], verts[3], AO_RAY_LENGTH) {
|
||||||
|
occluded += 1;
|
||||||
|
break 'face_loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1.0 - (occluded as f32 / AO_NUM_SAMPLES as f32)
|
||||||
|
}
|
||||||
|
|
||||||
fn compute_vertex_light(
|
fn compute_vertex_light(
|
||||||
pos: Vec3,
|
pos: Vec3,
|
||||||
normal: Vec3,
|
normal: Vec3,
|
||||||
lights: &[LightSource],
|
lights: &[LightSource],
|
||||||
ambient: f32,
|
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
let mut total = ambient;
|
let mut total = 0.0;
|
||||||
|
|
||||||
for light in lights {
|
for light in lights {
|
||||||
let to_light = Vec3::new(
|
let to_light = unsafe { pxl8_vec3_sub(light.position, pos) };
|
||||||
light.position.x - pos.x,
|
let dist = unsafe { pxl8_vec3_dot(to_light, to_light) };
|
||||||
light.position.y - pos.y,
|
let dist = sqrtf(dist).max(1.0);
|
||||||
light.position.z - pos.z,
|
|
||||||
);
|
|
||||||
|
|
||||||
let dist_sq = to_light.x * to_light.x + to_light.y * to_light.y + to_light.z * to_light.z;
|
|
||||||
let dist = sqrtf(dist_sq).max(1.0);
|
|
||||||
|
|
||||||
if dist > light.radius {
|
if dist > light.radius {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inv_dist = 1.0 / dist;
|
let light_dir = unsafe { pxl8_vec3_normalize(to_light) };
|
||||||
let light_dir = Vec3::new(
|
let ndotl = unsafe { pxl8_vec3_dot(normal, light_dir) }.max(0.0);
|
||||||
to_light.x * inv_dist,
|
|
||||||
to_light.y * inv_dist,
|
|
||||||
to_light.z * inv_dist,
|
|
||||||
);
|
|
||||||
|
|
||||||
let ndotl = (normal.x * light_dir.x + normal.y * light_dir.y + normal.z * light_dir.z).max(0.0);
|
|
||||||
|
|
||||||
let attenuation = (1.0 - dist / light.radius).max(0.0);
|
let attenuation = (1.0 - dist / light.radius).max(0.0);
|
||||||
let attenuation = attenuation * attenuation;
|
let attenuation = attenuation * attenuation;
|
||||||
|
|
@ -442,12 +546,13 @@ fn compute_vertex_light(
|
||||||
total.min(1.0)
|
total.min(1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_bsp_vertex_lighting(bsp: &mut BspBuilder, lights: &[LightSource], ambient: f32) {
|
fn compute_bsp_vertex_lighting(bsp: &mut BspBuilder, lights: &[LightSource]) {
|
||||||
if bsp.vertices.is_empty() {
|
if bsp.vertices.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bsp.vertex_lights = vec![0u32; bsp.vertices.len()];
|
bsp.vertex_lights = vec![0u32; bsp.vertices.len()];
|
||||||
|
let mut vertex_normals: Vec<Option<Vec3>> = vec![None; bsp.vertices.len()];
|
||||||
|
|
||||||
for f in 0..bsp.faces.len() {
|
for f in 0..bsp.faces.len() {
|
||||||
let face = &bsp.faces[f];
|
let face = &bsp.faces[f];
|
||||||
|
|
@ -478,13 +583,27 @@ fn compute_bsp_vertex_lighting(bsp: &mut BspBuilder, lights: &[LightSource], amb
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pos = bsp.vertices[vert_idx].position;
|
vertex_normals[vert_idx] = Some(normal);
|
||||||
let light = compute_vertex_light(pos, normal, lights, ambient);
|
|
||||||
|
|
||||||
let light_byte = (light * 255.0) as u8;
|
let pos = bsp.vertices[vert_idx].position;
|
||||||
bsp.vertex_lights[vert_idx] = ((light_byte as u32) << 24) | 0x00FFFFFF;
|
let direct = compute_vertex_light(pos, normal, lights);
|
||||||
|
let direct_byte = ((direct * 255.0).min(255.0)) as u8;
|
||||||
|
|
||||||
|
bsp.vertex_lights[vert_idx] = (bsp.vertex_lights[vert_idx] & 0x00FFFFFF) | ((direct_byte as u32) << 24);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for vert_idx in 0..bsp.vertices.len() {
|
||||||
|
let normal = match vertex_normals[vert_idx] {
|
||||||
|
Some(n) => n,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let pos = bsp.vertices[vert_idx].position;
|
||||||
|
let ao = compute_vertex_ao(bsp, pos, normal);
|
||||||
|
let ao_byte = ((ao * 255.0).min(255.0)) as u8;
|
||||||
|
|
||||||
|
bsp.vertex_lights[vert_idx] = (bsp.vertex_lights[vert_idx] & 0xFF00FFFF) | ((ao_byte as u32) << 16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid) {
|
fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid) {
|
||||||
|
|
@ -926,17 +1045,27 @@ pub fn generate_rooms(params: &ProcgenParams) -> Bsp {
|
||||||
grid_to_bsp(&mut bsp, &grid);
|
grid_to_bsp(&mut bsp, &grid);
|
||||||
|
|
||||||
let light_height = 80.0;
|
let light_height = 80.0;
|
||||||
let lights: Vec<LightSource> = rooms.iter().map(|room| {
|
let fireball_pos = Vec3::new(384.0, light_height, 324.0);
|
||||||
|
let fireball_exclusion_radius = 150.0;
|
||||||
|
|
||||||
|
let lights: Vec<LightSource> = rooms.iter().filter_map(|room| {
|
||||||
let cx = (room.x as f32 + room.w as f32 / 2.0) * CELL_SIZE;
|
let cx = (room.x as f32 + room.w as f32 / 2.0) * CELL_SIZE;
|
||||||
let cy = (room.y as f32 + room.h as f32 / 2.0) * CELL_SIZE;
|
let cz = (room.y as f32 + room.h as f32 / 2.0) * CELL_SIZE;
|
||||||
LightSource {
|
|
||||||
position: Vec3::new(cx, light_height, cy),
|
let dx = cx - fireball_pos.x;
|
||||||
intensity: 0.8,
|
let dz = cz - fireball_pos.z;
|
||||||
radius: 300.0,
|
if dx * dx + dz * dz < fireball_exclusion_radius * fireball_exclusion_radius {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(LightSource {
|
||||||
|
position: Vec3::new(cx, light_height, cz),
|
||||||
|
intensity: 1.8,
|
||||||
|
radius: 160.0,
|
||||||
|
})
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
compute_bsp_vertex_lighting(&mut bsp, &lights, 0.1);
|
compute_bsp_vertex_lighting(&mut bsp, &lights);
|
||||||
|
|
||||||
bsp.into()
|
bsp.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,20 @@ impl Default for Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for Entity {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
pos: self.pos,
|
||||||
|
vel: self.vel,
|
||||||
|
yaw: self.yaw,
|
||||||
|
pitch: self.pitch,
|
||||||
|
flags: self.flags,
|
||||||
|
kind: self.kind,
|
||||||
|
_pad: self._pad,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Simulation {
|
pub struct Simulation {
|
||||||
pub entities: Vec<Entity>,
|
pub entities: Vec<Entity>,
|
||||||
pub free_list: Vec<u32>,
|
pub free_list: Vec<u32>,
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ static screen_rect project_portal_to_screen(const pxl8_bsp_portal* portal, const
|
||||||
}
|
}
|
||||||
|
|
||||||
static void collect_face_to_mesh(const pxl8_bsp* bsp, const pxl8_bsp_render_state* state,
|
static void collect_face_to_mesh(const pxl8_bsp* bsp, const pxl8_bsp_render_state* state,
|
||||||
u32 face_id, pxl8_mesh* mesh) {
|
u32 face_id, pxl8_mesh* mesh, u8 ambient) {
|
||||||
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
||||||
if (face->num_edges < 3) return;
|
if (face->num_edges < 3) return;
|
||||||
|
|
||||||
|
|
@ -179,7 +179,11 @@ static void collect_face_to_mesh(const pxl8_bsp* bsp, const pxl8_bsp_render_stat
|
||||||
|
|
||||||
u8 light = 255;
|
u8 light = 255;
|
||||||
if (bsp->vertex_lights && vert_idx < bsp->num_vertex_lights) {
|
if (bsp->vertex_lights && vert_idx < bsp->num_vertex_lights) {
|
||||||
light = (bsp->vertex_lights[vert_idx] >> 24) & 0xFF;
|
u32 packed = bsp->vertex_lights[vert_idx];
|
||||||
|
u8 direct = (packed >> 24) & 0xFF;
|
||||||
|
u8 ao = (packed >> 16) & 0xFF;
|
||||||
|
f32 combined = (f32)direct + ((f32)ambient / 255.0f) * (f32)ao;
|
||||||
|
light = (u8)(combined > 255.0f ? 255.0f : combined);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vertex vtx = {
|
pxl8_vertex vtx = {
|
||||||
|
|
@ -230,7 +234,7 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, const
|
||||||
pxl8_mesh* mesh = pxl8_mesh_create(64, 192);
|
pxl8_mesh* mesh = pxl8_mesh_create(64, 192);
|
||||||
if (!mesh) return;
|
if (!mesh) return;
|
||||||
|
|
||||||
collect_face_to_mesh(bsp, NULL, face_id, mesh);
|
collect_face_to_mesh(bsp, NULL, face_id, mesh, pxl8_gfx_get_ambient(gfx));
|
||||||
|
|
||||||
if (mesh->index_count > 0) {
|
if (mesh->index_count > 0) {
|
||||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||||
|
|
@ -272,7 +276,7 @@ void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp,
|
||||||
}
|
}
|
||||||
|
|
||||||
current_material = material_id;
|
current_material = material_id;
|
||||||
collect_face_to_mesh(bsp, state, face_id, mesh);
|
collect_face_to_mesh(bsp, state, face_id, mesh, pxl8_gfx_get_ambient(gfx));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mesh->index_count > 0 && current_material < state->num_materials) {
|
if (mesh->index_count > 0 && current_material < state->num_materials) {
|
||||||
|
|
@ -395,7 +399,7 @@ void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp,
|
||||||
}
|
}
|
||||||
|
|
||||||
current_material = material_id;
|
current_material = material_id;
|
||||||
collect_face_to_mesh(bsp, state, face_id, mesh);
|
collect_face_to_mesh(bsp, state, face_id, mesh, pxl8_gfx_get_ambient(gfx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,10 +449,3 @@ void pxl8_bsp_set_material(pxl8_bsp_render_state* state, u16 material_id, const
|
||||||
state->materials[material_id].u_offset = u_offset;
|
state->materials[material_id].u_offset = u_offset;
|
||||||
state->materials[material_id].v_offset = v_offset;
|
state->materials[material_id].v_offset = v_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_bsp_set_wireframe(pxl8_bsp_render_state* state, bool wireframe) {
|
|
||||||
if (!state || !state->materials) return;
|
|
||||||
for (u32 i = 0; i < state->num_materials; i++) {
|
|
||||||
state->materials[i].wireframe = wireframe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id,
|
||||||
const pxl8_gfx_material* material);
|
const pxl8_gfx_material* material);
|
||||||
void pxl8_bsp_set_material(pxl8_bsp_render_state* state, u16 material_id,
|
void pxl8_bsp_set_material(pxl8_bsp_render_state* state, u16 material_id,
|
||||||
const pxl8_gfx_material* material);
|
const pxl8_gfx_material* material);
|
||||||
void pxl8_bsp_set_wireframe(pxl8_bsp_render_state* state, bool wireframe);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,14 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define pxl8_likely(x) __builtin_expect(!!(x), 1)
|
||||||
|
#define pxl8_unlikely(x) __builtin_expect(!!(x), 0)
|
||||||
|
#else
|
||||||
|
#define pxl8_likely(x) (x)
|
||||||
|
#define pxl8_unlikely(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef pxl8_min
|
#ifndef pxl8_min
|
||||||
#define pxl8_min(a, b) ((a) < (b) ? (a) : (b))
|
#define pxl8_min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ typedef struct pxl8_atlas pxl8_atlas;
|
||||||
|
|
||||||
typedef struct pxl8_atlas_entry {
|
typedef struct pxl8_atlas_entry {
|
||||||
bool active;
|
bool active;
|
||||||
|
i32 h, w, x, y;
|
||||||
|
u8 log2_h, log2_w;
|
||||||
u32 texture_id;
|
u32 texture_id;
|
||||||
i32 x, y, w, h;
|
|
||||||
u32 tiled_base;
|
u32 tiled_base;
|
||||||
u8 log2_w;
|
|
||||||
} pxl8_atlas_entry;
|
} pxl8_atlas_entry;
|
||||||
|
|
||||||
static inline u32 pxl8_tile_addr(u32 u, u32 v, u8 log2_w) {
|
static inline u32 pxl8_tile_addr(u32 u, u32 v, u8 log2_w) {
|
||||||
|
|
|
||||||
|
|
@ -34,16 +34,9 @@ typedef struct pxl8_target_entry {
|
||||||
} pxl8_target_entry;
|
} pxl8_target_entry;
|
||||||
|
|
||||||
#define PXL8_MAX_FRAME_RESOURCES 512
|
#define PXL8_MAX_FRAME_RESOURCES 512
|
||||||
#define PXL8_TEXTURE_CACHE_SIZE 256
|
|
||||||
#define PXL8_STREAM_VB_SIZE (256 * 1024)
|
#define PXL8_STREAM_VB_SIZE (256 * 1024)
|
||||||
#define PXL8_STREAM_IB_SIZE (512 * 1024)
|
#define PXL8_STREAM_IB_SIZE (512 * 1024)
|
||||||
|
|
||||||
typedef struct pxl8_texture_cache_entry {
|
|
||||||
u32 texture_id;
|
|
||||||
pxl8_gfx_texture handle;
|
|
||||||
bool valid;
|
|
||||||
} pxl8_texture_cache_entry;
|
|
||||||
|
|
||||||
typedef struct pxl8_frame_resources {
|
typedef struct pxl8_frame_resources {
|
||||||
pxl8_gfx_texture textures[PXL8_MAX_FRAME_RESOURCES];
|
pxl8_gfx_texture textures[PXL8_MAX_FRAME_RESOURCES];
|
||||||
pxl8_gfx_buffer buffers[PXL8_MAX_FRAME_RESOURCES];
|
pxl8_gfx_buffer buffers[PXL8_MAX_FRAME_RESOURCES];
|
||||||
|
|
@ -85,13 +78,13 @@ struct pxl8_gfx {
|
||||||
pxl8_mat4 view_proj;
|
pxl8_mat4 view_proj;
|
||||||
pxl8_3d_frame frame;
|
pxl8_3d_frame frame;
|
||||||
|
|
||||||
pxl8_texture_cache_entry texture_cache[PXL8_TEXTURE_CACHE_SIZE];
|
|
||||||
pxl8_gfx_buffer stream_vb;
|
pxl8_gfx_buffer stream_vb;
|
||||||
pxl8_gfx_buffer stream_ib;
|
pxl8_gfx_buffer stream_ib;
|
||||||
u32 stream_vb_capacity;
|
u32 stream_vb_capacity;
|
||||||
u32 stream_ib_capacity;
|
u32 stream_ib_capacity;
|
||||||
u32 stream_vb_offset;
|
u32 stream_vb_offset;
|
||||||
u32 stream_ib_offset;
|
u32 stream_ib_offset;
|
||||||
|
bool wireframe;
|
||||||
};
|
};
|
||||||
|
|
||||||
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
||||||
|
|
@ -198,26 +191,6 @@ i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pxl8_gfx_texture texture_cache_lookup(pxl8_gfx* gfx, u32 texture_id) {
|
|
||||||
u32 slot = texture_id % PXL8_TEXTURE_CACHE_SIZE;
|
|
||||||
pxl8_texture_cache_entry* entry = &gfx->texture_cache[slot];
|
|
||||||
if (entry->valid && entry->texture_id == texture_id) {
|
|
||||||
return entry->handle;
|
|
||||||
}
|
|
||||||
return (pxl8_gfx_texture){PXL8_GFX_INVALID_ID};
|
|
||||||
}
|
|
||||||
|
|
||||||
static void texture_cache_insert(pxl8_gfx* gfx, u32 texture_id, pxl8_gfx_texture handle) {
|
|
||||||
u32 slot = texture_id % PXL8_TEXTURE_CACHE_SIZE;
|
|
||||||
pxl8_texture_cache_entry* entry = &gfx->texture_cache[slot];
|
|
||||||
if (entry->valid && entry->texture_id != texture_id) {
|
|
||||||
pxl8_destroy_texture(gfx->renderer, entry->handle);
|
|
||||||
}
|
|
||||||
entry->texture_id = texture_id;
|
|
||||||
entry->handle = handle;
|
|
||||||
entry->valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_gfx* pxl8_gfx_create(
|
pxl8_gfx* pxl8_gfx_create(
|
||||||
const pxl8_hal* hal,
|
const pxl8_hal* hal,
|
||||||
void* platform_data,
|
void* platform_data,
|
||||||
|
|
@ -496,6 +469,16 @@ void pxl8_gfx_present(pxl8_gfx* gfx) {
|
||||||
gfx->hal->present(gfx->platform_data);
|
gfx->hal->present(gfx->platform_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pxl8_gfx_reset_stats(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx || !gfx->renderer) return;
|
||||||
|
pxl8_renderer_reset_stats(gfx->renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pxl8_gfx_stats* pxl8_gfx_get_stats(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx || !gfx->renderer) return NULL;
|
||||||
|
return pxl8_renderer_get_stats(gfx->renderer);
|
||||||
|
}
|
||||||
|
|
||||||
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height) {
|
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height) {
|
||||||
pxl8_viewport vp = {0};
|
pxl8_viewport vp = {0};
|
||||||
vp.scale = fminf(bounds.w / (f32)width, bounds.h / (f32)height);
|
vp.scale = fminf(bounds.w / (f32)width, bounds.h / (f32)height);
|
||||||
|
|
@ -840,44 +823,7 @@ void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* mo
|
||||||
if (!pxl8_gfx_handle_valid(gfx->frame_pass)) return;
|
if (!pxl8_gfx_handle_valid(gfx->frame_pass)) return;
|
||||||
|
|
||||||
pxl8_frame_resources* res = &gfx->frame_res;
|
pxl8_frame_resources* res = &gfx->frame_res;
|
||||||
bool is_wireframe = material->wireframe;
|
bool is_wireframe = gfx->wireframe;
|
||||||
|
|
||||||
pxl8_gfx_texture tex_handle = (pxl8_gfx_texture){PXL8_GFX_INVALID_ID};
|
|
||||||
if (!is_wireframe && material->texture_id != UINT32_MAX) {
|
|
||||||
tex_handle = texture_cache_lookup(gfx, material->texture_id);
|
|
||||||
|
|
||||||
if (!pxl8_gfx_handle_valid(tex_handle)) {
|
|
||||||
const pxl8_atlas_entry* tex_entry = NULL;
|
|
||||||
if (gfx->atlas) {
|
|
||||||
tex_entry = pxl8_atlas_get_entry(gfx->atlas, material->texture_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tex_entry && tex_entry->active) {
|
|
||||||
const u8* atlas_pixels = pxl8_atlas_get_pixels(gfx->atlas);
|
|
||||||
u32 atlas_width = pxl8_atlas_get_width(gfx->atlas);
|
|
||||||
u8* tex_data = pxl8_calloc(tex_entry->w * tex_entry->h, 1);
|
|
||||||
if (tex_data) {
|
|
||||||
for (i32 row = 0; row < tex_entry->h; row++) {
|
|
||||||
memcpy(tex_data + row * tex_entry->w,
|
|
||||||
atlas_pixels + (tex_entry->y + row) * atlas_width + tex_entry->x,
|
|
||||||
tex_entry->w);
|
|
||||||
}
|
|
||||||
u32 tex_size = (u32)(tex_entry->w * tex_entry->h);
|
|
||||||
pxl8_gfx_texture_desc tex_desc = {
|
|
||||||
.width = (u32)tex_entry->w,
|
|
||||||
.height = (u32)tex_entry->h,
|
|
||||||
.format = PXL8_GFX_FORMAT_INDEXED8,
|
|
||||||
.data = { .ptr = tex_data, .size = tex_size },
|
|
||||||
};
|
|
||||||
tex_handle = pxl8_create_texture(gfx->renderer, &tex_desc);
|
|
||||||
pxl8_free(tex_data);
|
|
||||||
if (pxl8_gfx_handle_valid(tex_handle)) {
|
|
||||||
texture_cache_insert(gfx, material->texture_id, tex_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* shader_name = "lit";
|
const char* shader_name = "lit";
|
||||||
if (material->emissive || (!material->dynamic_lighting && !material->per_pixel)) {
|
if (material->emissive || (!material->dynamic_lighting && !material->per_pixel)) {
|
||||||
|
|
@ -885,7 +831,13 @@ void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* mo
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_gfx_pipeline_desc pipe_desc = {
|
pxl8_gfx_pipeline_desc pipe_desc = {
|
||||||
.blend = { .enabled = false },
|
.blend = {
|
||||||
|
.enabled = false,
|
||||||
|
.src = PXL8_GFX_BLEND_ONE,
|
||||||
|
.dst = PXL8_GFX_BLEND_ZERO,
|
||||||
|
.alpha_test = false,
|
||||||
|
.alpha_ref = 0,
|
||||||
|
},
|
||||||
.depth = { .test = true, .write = true, .compare = PXL8_GFX_COMPARE_LESS },
|
.depth = { .test = true, .write = true, .compare = PXL8_GFX_COMPARE_LESS },
|
||||||
.dither = material->dither,
|
.dither = material->dither,
|
||||||
.double_sided = material->double_sided,
|
.double_sided = material->double_sided,
|
||||||
|
|
@ -893,15 +845,36 @@ void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* mo
|
||||||
.rasterizer = { .cull = PXL8_GFX_CULL_BACK, .fill = is_wireframe ? PXL8_GFX_FILL_WIREFRAME : PXL8_GFX_FILL_SOLID },
|
.rasterizer = { .cull = PXL8_GFX_CULL_BACK, .fill = is_wireframe ? PXL8_GFX_FILL_WIREFRAME : PXL8_GFX_FILL_SOLID },
|
||||||
.shader = pxl8_shader_registry_get(shader_name),
|
.shader = pxl8_shader_registry_get(shader_name),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
switch (material->blend_mode) {
|
||||||
|
case PXL8_BLEND_ALPHA_TEST:
|
||||||
|
pipe_desc.blend.alpha_test = true;
|
||||||
|
pipe_desc.blend.alpha_ref = material->alpha;
|
||||||
|
break;
|
||||||
|
case PXL8_BLEND_ALPHA:
|
||||||
|
pipe_desc.blend.enabled = true;
|
||||||
|
pipe_desc.blend.src = PXL8_GFX_BLEND_SRC_ALPHA;
|
||||||
|
pipe_desc.blend.dst = PXL8_GFX_BLEND_INV_SRC_ALPHA;
|
||||||
|
break;
|
||||||
|
case PXL8_BLEND_ADDITIVE:
|
||||||
|
pipe_desc.blend.enabled = true;
|
||||||
|
pipe_desc.blend.src = PXL8_GFX_BLEND_ONE;
|
||||||
|
pipe_desc.blend.dst = PXL8_GFX_BLEND_ONE;
|
||||||
|
break;
|
||||||
|
case PXL8_BLEND_OPAQUE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
pxl8_gfx_pipeline pipeline = pxl8_create_pipeline(gfx->renderer, &pipe_desc);
|
pxl8_gfx_pipeline pipeline = pxl8_create_pipeline(gfx->renderer, &pipe_desc);
|
||||||
if (res->pipeline_count < PXL8_MAX_FRAME_RESOURCES) {
|
if (res->pipeline_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||||
res->pipelines[res->pipeline_count++] = pipeline;
|
res->pipelines[res->pipeline_count++] = pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_gfx_bindings_desc bind_desc = {
|
pxl8_gfx_bindings_desc bind_desc = {
|
||||||
.textures = { tex_handle },
|
.atlas = gfx->atlas,
|
||||||
.colormap = gfx->colormap,
|
.colormap = gfx->colormap,
|
||||||
.palette = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL,
|
.palette = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL,
|
||||||
|
.texture_id = material->texture_id,
|
||||||
};
|
};
|
||||||
pxl8_gfx_bindings bindings = pxl8_create_bindings(gfx->renderer, &bind_desc);
|
pxl8_gfx_bindings bindings = pxl8_create_bindings(gfx->renderer, &bind_desc);
|
||||||
if (res->bindings_count < PXL8_MAX_FRAME_RESOURCES) {
|
if (res->bindings_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||||
|
|
@ -1101,3 +1074,15 @@ u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index) {
|
||||||
u8 b = (abgr >> 16) & 0xFF;
|
u8 b = (abgr >> 16) & 0xFF;
|
||||||
return pxl8_palette_find_closest(gfx->palette, r, g, b);
|
return pxl8_palette_find_closest(gfx->palette, r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled) {
|
||||||
|
if (gfx) gfx->wireframe = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pxl8_gfx_get_wireframe(const pxl8_gfx* gfx) {
|
||||||
|
return gfx ? gfx->wireframe : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 pxl8_gfx_get_ambient(const pxl8_gfx* gfx) {
|
||||||
|
return gfx ? gfx->frame.uniforms.ambient : 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "pxl8_gui_palette.h"
|
#include "pxl8_gui_palette.h"
|
||||||
|
|
||||||
typedef struct pxl8_gfx pxl8_gfx;
|
typedef struct pxl8_gfx pxl8_gfx;
|
||||||
|
typedef struct pxl8_gfx_stats pxl8_gfx_stats;
|
||||||
|
|
||||||
typedef enum pxl8_gfx_effect {
|
typedef enum pxl8_gfx_effect {
|
||||||
PXL8_GFX_EFFECT_GLOWS = 0,
|
PXL8_GFX_EFFECT_GLOWS = 0,
|
||||||
|
|
@ -25,6 +26,8 @@ void pxl8_gfx_destroy(pxl8_gfx* gfx);
|
||||||
void pxl8_gfx_present(pxl8_gfx* gfx);
|
void pxl8_gfx_present(pxl8_gfx* gfx);
|
||||||
void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt);
|
void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt);
|
||||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx);
|
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx);
|
||||||
|
void pxl8_gfx_reset_stats(pxl8_gfx* gfx);
|
||||||
|
const pxl8_gfx_stats* pxl8_gfx_get_stats(pxl8_gfx* gfx);
|
||||||
|
|
||||||
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);
|
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);
|
||||||
|
|
||||||
|
|
@ -62,6 +65,11 @@ void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx);
|
||||||
u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b);
|
u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b);
|
||||||
u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index);
|
u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index);
|
||||||
|
|
||||||
|
void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled);
|
||||||
|
bool pxl8_gfx_get_wireframe(const pxl8_gfx* gfx);
|
||||||
|
|
||||||
|
u8 pxl8_gfx_get_ambient(const pxl8_gfx* gfx);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ typedef struct pxl8_gfx_material {
|
||||||
bool dynamic_lighting;
|
bool dynamic_lighting;
|
||||||
bool emissive;
|
bool emissive;
|
||||||
bool per_pixel;
|
bool per_pixel;
|
||||||
bool wireframe;
|
|
||||||
} pxl8_gfx_material;
|
} pxl8_gfx_material;
|
||||||
|
|
||||||
typedef struct pxl8_vertex {
|
typedef struct pxl8_vertex {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "pxl8_atlas.h"
|
#include "pxl8_atlas.h"
|
||||||
#include "pxl8_colormap.h"
|
#include "pxl8_colormap.h"
|
||||||
#include "pxl8_dither.h"
|
#include "pxl8_dither.h"
|
||||||
|
#include "pxl8_hal.h"
|
||||||
#include "pxl8_log.h"
|
#include "pxl8_log.h"
|
||||||
#include "pxl8_mem.h"
|
#include "pxl8_mem.h"
|
||||||
#include "pxl8_mesh.h"
|
#include "pxl8_mesh.h"
|
||||||
|
|
@ -11,6 +12,16 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#if PXL8_GFX_ENABLE_STATS
|
||||||
|
#define STATS_INC(stats, field, val) do { (stats)->field += (val); } while (0)
|
||||||
|
#define STATS_START() pxl8_get_ticks_ns()
|
||||||
|
#define STATS_ADD(stats, field, start) do { (stats)->field += pxl8_get_ticks_ns() - (start); } while (0)
|
||||||
|
#else
|
||||||
|
#define STATS_INC(stats, field, val) do { (void)(stats); } while (0)
|
||||||
|
#define STATS_START() 0
|
||||||
|
#define STATS_ADD(stats, field, start) do { (void)(stats); (void)(start); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
pxl8_vec4 clip_pos;
|
pxl8_vec4 clip_pos;
|
||||||
pxl8_vec3 world_pos;
|
pxl8_vec3 world_pos;
|
||||||
|
|
@ -31,25 +42,118 @@ typedef struct {
|
||||||
i32 y_start, y_end;
|
i32 y_start, y_end;
|
||||||
f32 inv_total;
|
f32 inv_total;
|
||||||
u32 target_width, target_height;
|
u32 target_width, target_height;
|
||||||
|
i32 clip_min_x, clip_min_y;
|
||||||
|
i32 clip_max_x, clip_max_y;
|
||||||
} tri_setup;
|
} tri_setup;
|
||||||
|
|
||||||
|
static inline bool depth_test_pass(pxl8_gfx_compare_func func, u16 src, u16 dst) {
|
||||||
|
switch (func) {
|
||||||
|
case PXL8_GFX_COMPARE_NEVER: return false;
|
||||||
|
case PXL8_GFX_COMPARE_LESS: return src < dst;
|
||||||
|
case PXL8_GFX_COMPARE_EQUAL: return src == dst;
|
||||||
|
case PXL8_GFX_COMPARE_LEQUAL: return src <= dst;
|
||||||
|
case PXL8_GFX_COMPARE_GREATER: return src > dst;
|
||||||
|
case PXL8_GFX_COMPARE_NOTEQUAL: return src != dst;
|
||||||
|
case PXL8_GFX_COMPARE_GEQUAL: return src >= dst;
|
||||||
|
case PXL8_GFX_COMPARE_ALWAYS: return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline f32 blend_factor_value(pxl8_gfx_blend_factor factor, f32 src_a, f32 dst_a) {
|
||||||
|
switch (factor) {
|
||||||
|
case PXL8_GFX_BLEND_ZERO: return 0.0f;
|
||||||
|
case PXL8_GFX_BLEND_ONE: return 1.0f;
|
||||||
|
case PXL8_GFX_BLEND_SRC_ALPHA: return src_a;
|
||||||
|
case PXL8_GFX_BLEND_INV_SRC_ALPHA: return 1.0f - src_a;
|
||||||
|
case PXL8_GFX_BLEND_DST_ALPHA: return dst_a;
|
||||||
|
case PXL8_GFX_BLEND_INV_DST_ALPHA: return 1.0f - dst_a;
|
||||||
|
}
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 palette_find_closest(const u32* palette, u8 r, u8 g, u8 b) {
|
||||||
|
if (!palette) return 0;
|
||||||
|
u8 best_idx = 1;
|
||||||
|
u32 best_dist = 0xFFFFFFFF;
|
||||||
|
for (u32 i = 1; i < 256; i++) {
|
||||||
|
u8 pr = palette[i] & 0xFF;
|
||||||
|
u8 pg = (palette[i] >> 8) & 0xFF;
|
||||||
|
u8 pb = (palette[i] >> 16) & 0xFF;
|
||||||
|
i32 dr = (i32)r - (i32)pr;
|
||||||
|
i32 dg = (i32)g - (i32)pg;
|
||||||
|
i32 db = (i32)b - (i32)pb;
|
||||||
|
u32 dist = (u32)(dr * dr + dg * dg + db * db);
|
||||||
|
if (dist < best_dist) {
|
||||||
|
best_dist = dist;
|
||||||
|
best_idx = (u8)i;
|
||||||
|
if (dist == 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 blend_indexed(
|
||||||
|
const pxl8_gfx_pipeline_desc* pipeline,
|
||||||
|
u8 src,
|
||||||
|
u8 dst,
|
||||||
|
const u32* palette,
|
||||||
|
const u8* colormap
|
||||||
|
) {
|
||||||
|
(void)colormap;
|
||||||
|
if (!pipeline || !pipeline->blend.enabled) return src;
|
||||||
|
if (src == 0) return dst;
|
||||||
|
if (!palette) return src;
|
||||||
|
|
||||||
|
f32 src_a = src == 0 ? 0.0f : 1.0f;
|
||||||
|
f32 dst_a = dst == 0 ? 0.0f : 1.0f;
|
||||||
|
f32 sf = blend_factor_value(pipeline->blend.src, src_a, dst_a);
|
||||||
|
f32 df = blend_factor_value(pipeline->blend.dst, src_a, dst_a);
|
||||||
|
|
||||||
|
if (sf == 1.0f && df == 0.0f) return src;
|
||||||
|
if (sf == 0.0f && df == 1.0f) return dst;
|
||||||
|
|
||||||
|
u8 sr = palette[src] & 0xFF;
|
||||||
|
u8 sg = (palette[src] >> 8) & 0xFF;
|
||||||
|
u8 sb = (palette[src] >> 16) & 0xFF;
|
||||||
|
|
||||||
|
u8 dr = palette[dst] & 0xFF;
|
||||||
|
u8 dg = (palette[dst] >> 8) & 0xFF;
|
||||||
|
u8 db = (palette[dst] >> 16) & 0xFF;
|
||||||
|
|
||||||
|
i32 out_r = (i32)(sr * sf + dr * df);
|
||||||
|
i32 out_g = (i32)(sg * sf + dg * df);
|
||||||
|
i32 out_b = (i32)(sb * sf + db * df);
|
||||||
|
|
||||||
|
if (out_r < 0) out_r = 0;
|
||||||
|
if (out_g < 0) out_g = 0;
|
||||||
|
if (out_b < 0) out_b = 0;
|
||||||
|
if (out_r > 255) out_r = 255;
|
||||||
|
if (out_g > 255) out_g = 255;
|
||||||
|
if (out_b > 255) out_b = 255;
|
||||||
|
|
||||||
|
return palette_find_closest(palette, (u8)out_r, (u8)out_g, (u8)out_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pxl8_vec4 vec4_lerp(pxl8_vec4 a, pxl8_vec4 b, f32 t) {
|
||||||
|
return (pxl8_vec4){
|
||||||
|
a.x + (b.x - a.x) * t,
|
||||||
|
a.y + (b.y - a.y) * t,
|
||||||
|
a.z + (b.z - a.z) * t,
|
||||||
|
a.w + (b.w - a.w) * t
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static raster_vertex lerp_raster_vertex(const raster_vertex* a, const raster_vertex* b, f32 t) {
|
static raster_vertex lerp_raster_vertex(const raster_vertex* a, const raster_vertex* b, f32 t) {
|
||||||
raster_vertex out;
|
return (raster_vertex){
|
||||||
out.clip_pos.x = a->clip_pos.x + (b->clip_pos.x - a->clip_pos.x) * t;
|
.clip_pos = vec4_lerp(a->clip_pos, b->clip_pos, t),
|
||||||
out.clip_pos.y = a->clip_pos.y + (b->clip_pos.y - a->clip_pos.y) * t;
|
.world_pos = pxl8_vec3_lerp(a->world_pos, b->world_pos, t),
|
||||||
out.clip_pos.z = a->clip_pos.z + (b->clip_pos.z - a->clip_pos.z) * t;
|
.normal = pxl8_vec3_lerp(a->normal, b->normal, t),
|
||||||
out.clip_pos.w = a->clip_pos.w + (b->clip_pos.w - a->clip_pos.w) * t;
|
.u = pxl8_lerp(a->u, b->u, t),
|
||||||
out.world_pos.x = a->world_pos.x + (b->world_pos.x - a->world_pos.x) * t;
|
.v = pxl8_lerp(a->v, b->v, t),
|
||||||
out.world_pos.y = a->world_pos.y + (b->world_pos.y - a->world_pos.y) * t;
|
.color = (u8)(a->color + (b->color - a->color) * t),
|
||||||
out.world_pos.z = a->world_pos.z + (b->world_pos.z - a->world_pos.z) * t;
|
.light = (u8)(a->light + (b->light - a->light) * t),
|
||||||
out.normal.x = a->normal.x + (b->normal.x - a->normal.x) * t;
|
};
|
||||||
out.normal.y = a->normal.y + (b->normal.y - a->normal.y) * t;
|
|
||||||
out.normal.z = a->normal.z + (b->normal.z - a->normal.z) * t;
|
|
||||||
out.u = a->u + (b->u - a->u) * t;
|
|
||||||
out.v = a->v + (b->v - a->v) * t;
|
|
||||||
out.color = (u8)(a->color + (b->color - a->color) * t);
|
|
||||||
out.light = (u8)(a->light + (b->light - a->light) * t);
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static i32 clip_triangle_near(
|
static i32 clip_triangle_near(
|
||||||
|
|
@ -101,26 +205,33 @@ static i32 clip_triangle_near(
|
||||||
static bool setup_tri(
|
static bool setup_tri(
|
||||||
tri_setup* setup,
|
tri_setup* setup,
|
||||||
const raster_vertex* vo0, const raster_vertex* vo1, const raster_vertex* vo2,
|
const raster_vertex* vo0, const raster_vertex* vo1, const raster_vertex* vo2,
|
||||||
u32 width, u32 height, bool double_sided
|
i32 viewport_x, i32 viewport_y, u32 viewport_w, u32 viewport_h,
|
||||||
|
i32 clip_min_x, i32 clip_min_y, i32 clip_max_x, i32 clip_max_y,
|
||||||
|
pxl8_gfx_cull_mode cull, bool double_sided
|
||||||
) {
|
) {
|
||||||
f32 hw = (f32)width * 0.5f;
|
if (viewport_w == 0 || viewport_h == 0) return false;
|
||||||
f32 hh = (f32)height * 0.5f;
|
|
||||||
|
|
||||||
setup->p0.x = hw + vo0->clip_pos.x / vo0->clip_pos.w * hw;
|
f32 hw = (f32)viewport_w * 0.5f;
|
||||||
setup->p0.y = hh - vo0->clip_pos.y / vo0->clip_pos.w * hh;
|
f32 hh = (f32)viewport_h * 0.5f;
|
||||||
|
|
||||||
|
setup->p0.x = (f32)viewport_x + hw + vo0->clip_pos.x / vo0->clip_pos.w * hw;
|
||||||
|
setup->p0.y = (f32)viewport_y + hh - vo0->clip_pos.y / vo0->clip_pos.w * hh;
|
||||||
setup->p0.z = vo0->clip_pos.z / vo0->clip_pos.w;
|
setup->p0.z = vo0->clip_pos.z / vo0->clip_pos.w;
|
||||||
|
|
||||||
setup->p1.x = hw + vo1->clip_pos.x / vo1->clip_pos.w * hw;
|
setup->p1.x = (f32)viewport_x + hw + vo1->clip_pos.x / vo1->clip_pos.w * hw;
|
||||||
setup->p1.y = hh - vo1->clip_pos.y / vo1->clip_pos.w * hh;
|
setup->p1.y = (f32)viewport_y + hh - vo1->clip_pos.y / vo1->clip_pos.w * hh;
|
||||||
setup->p1.z = vo1->clip_pos.z / vo1->clip_pos.w;
|
setup->p1.z = vo1->clip_pos.z / vo1->clip_pos.w;
|
||||||
|
|
||||||
setup->p2.x = hw + vo2->clip_pos.x / vo2->clip_pos.w * hw;
|
setup->p2.x = (f32)viewport_x + hw + vo2->clip_pos.x / vo2->clip_pos.w * hw;
|
||||||
setup->p2.y = hh - vo2->clip_pos.y / vo2->clip_pos.w * hh;
|
setup->p2.y = (f32)viewport_y + hh - vo2->clip_pos.y / vo2->clip_pos.w * hh;
|
||||||
setup->p2.z = vo2->clip_pos.z / vo2->clip_pos.w;
|
setup->p2.z = vo2->clip_pos.z / vo2->clip_pos.w;
|
||||||
|
|
||||||
f32 cross = (setup->p1.x - setup->p0.x) * (setup->p2.y - setup->p0.y) -
|
f32 cross = (setup->p1.x - setup->p0.x) * (setup->p2.y - setup->p0.y) -
|
||||||
(setup->p1.y - setup->p0.y) * (setup->p2.x - setup->p0.x);
|
(setup->p1.y - setup->p0.y) * (setup->p2.x - setup->p0.x);
|
||||||
if (!double_sided && cross >= 0.0f) return false;
|
if (!double_sided) {
|
||||||
|
if (cull == PXL8_GFX_CULL_BACK && cross >= 0.0f) return false;
|
||||||
|
if (cull == PXL8_GFX_CULL_FRONT && cross <= 0.0f) return false;
|
||||||
|
}
|
||||||
|
|
||||||
const raster_vertex* sorted[3] = {vo0, vo1, vo2};
|
const raster_vertex* sorted[3] = {vo0, vo1, vo2};
|
||||||
|
|
||||||
|
|
@ -142,8 +253,8 @@ static bool setup_tri(
|
||||||
|
|
||||||
i32 y0_int = (i32)floorf(setup->p0.y);
|
i32 y0_int = (i32)floorf(setup->p0.y);
|
||||||
i32 y2_int = (i32)ceilf(setup->p2.y) - 1;
|
i32 y2_int = (i32)ceilf(setup->p2.y) - 1;
|
||||||
setup->y_start = y0_int < 0 ? 0 : y0_int;
|
setup->y_start = y0_int < clip_min_y ? clip_min_y : y0_int;
|
||||||
setup->y_end = y2_int >= (i32)height ? (i32)height - 1 : y2_int;
|
setup->y_end = y2_int > clip_max_y ? clip_max_y : y2_int;
|
||||||
|
|
||||||
setup->w_recip.x = 1.0f / sorted[0]->clip_pos.w;
|
setup->w_recip.x = 1.0f / sorted[0]->clip_pos.w;
|
||||||
setup->w_recip.y = 1.0f / sorted[1]->clip_pos.w;
|
setup->w_recip.y = 1.0f / sorted[1]->clip_pos.w;
|
||||||
|
|
@ -171,8 +282,12 @@ static bool setup_tri(
|
||||||
setup->normal = sorted[0]->normal;
|
setup->normal = sorted[0]->normal;
|
||||||
|
|
||||||
setup->inv_total = 1.0f / total_height;
|
setup->inv_total = 1.0f / total_height;
|
||||||
setup->target_width = width;
|
setup->target_width = viewport_w;
|
||||||
setup->target_height = height;
|
setup->target_height = viewport_h;
|
||||||
|
setup->clip_min_x = clip_min_x;
|
||||||
|
setup->clip_min_y = clip_min_y;
|
||||||
|
setup->clip_max_x = clip_max_x;
|
||||||
|
setup->clip_max_y = clip_max_y;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -184,11 +299,24 @@ static void rasterize_triangle(
|
||||||
u32* light_accum,
|
u32* light_accum,
|
||||||
u32 fb_width,
|
u32 fb_width,
|
||||||
pxl8_shader_fn shader,
|
pxl8_shader_fn shader,
|
||||||
|
const pxl8_gfx_pipeline_desc* pipeline,
|
||||||
const pxl8_shader_bindings* bindings,
|
const pxl8_shader_bindings* bindings,
|
||||||
const pxl8_shader_uniforms* uniforms
|
const pxl8_shader_uniforms* uniforms,
|
||||||
|
pxl8_gfx_stats* stats
|
||||||
) {
|
) {
|
||||||
const i32 SUBDIV = 16;
|
const i32 SUBDIV = 16;
|
||||||
|
|
||||||
|
if (setup->y_start > setup->y_end) return;
|
||||||
|
|
||||||
|
bool depth_test = pipeline && pipeline->depth.test;
|
||||||
|
bool depth_write = pipeline && pipeline->depth.write;
|
||||||
|
pxl8_gfx_compare_func depth_compare = pipeline ? pipeline->depth.compare : PXL8_GFX_COMPARE_ALWAYS;
|
||||||
|
bool alpha_test = pipeline && pipeline->blend.alpha_test;
|
||||||
|
u8 alpha_ref = pipeline ? pipeline->blend.alpha_ref : 0;
|
||||||
|
bool blend_enabled = pipeline && pipeline->blend.enabled;
|
||||||
|
const u32* palette = bindings ? bindings->palette : NULL;
|
||||||
|
const u8* colormap = bindings ? bindings->colormap : NULL;
|
||||||
|
|
||||||
for (i32 y = setup->y_start; y <= setup->y_end; y++) {
|
for (i32 y = setup->y_start; y <= setup->y_end; y++) {
|
||||||
f32 yf = (f32)y + 0.5f;
|
f32 yf = (f32)y + 0.5f;
|
||||||
f32 alpha = (yf - setup->p0.y) * setup->inv_total;
|
f32 alpha = (yf - setup->p0.y) * setup->inv_total;
|
||||||
|
|
@ -265,8 +393,8 @@ static void rasterize_triangle(
|
||||||
|
|
||||||
i32 x_start = (i32)floorf(x_start_fp);
|
i32 x_start = (i32)floorf(x_start_fp);
|
||||||
i32 x_end = (i32)ceilf(x_end_fp) - 1;
|
i32 x_end = (i32)ceilf(x_end_fp) - 1;
|
||||||
if (x_start < 0) x_start = 0;
|
if (x_start < setup->clip_min_x) x_start = setup->clip_min_x;
|
||||||
if (x_end >= (i32)setup->target_width) x_end = (i32)setup->target_width - 1;
|
if (x_end > setup->clip_max_x) x_end = setup->clip_max_x;
|
||||||
if (x_start > x_end) continue;
|
if (x_start > x_end) continue;
|
||||||
|
|
||||||
f32 span_width = x_end_fp - x_start_fp;
|
f32 span_width = x_end_fp - x_start_fp;
|
||||||
|
|
@ -313,18 +441,10 @@ static void rasterize_triangle(
|
||||||
f32 u_end = (uw + duw * (f32)span_len) * pw_end;
|
f32 u_end = (uw + duw * (f32)span_len) * pw_end;
|
||||||
f32 v_end = (vw + dvw * (f32)span_len) * pw_end;
|
f32 v_end = (vw + dvw * (f32)span_len) * pw_end;
|
||||||
|
|
||||||
f32 l_start_fp = lw * pw_start;
|
f32 l_start_fp = pxl8_clamp(lw * pw_start, 0.0f, 255.0f);
|
||||||
f32 l_end_fp = (lw + dlw * (f32)span_len) * pw_end;
|
f32 l_end_fp = pxl8_clamp((lw + dlw * (f32)span_len) * pw_end, 0.0f, 255.0f);
|
||||||
f32 c_start_fp = cw * pw_start;
|
f32 c_start_fp = pxl8_clamp(cw * pw_start, 0.0f, 255.0f);
|
||||||
f32 c_end_fp = (cw + dcw * (f32)span_len) * pw_end;
|
f32 c_end_fp = pxl8_clamp((cw + dcw * (f32)span_len) * pw_end, 0.0f, 255.0f);
|
||||||
if (l_start_fp > 255.0f) l_start_fp = 255.0f;
|
|
||||||
if (l_start_fp < 0.0f) l_start_fp = 0.0f;
|
|
||||||
if (l_end_fp > 255.0f) l_end_fp = 255.0f;
|
|
||||||
if (l_end_fp < 0.0f) l_end_fp = 0.0f;
|
|
||||||
if (c_start_fp > 255.0f) c_start_fp = 255.0f;
|
|
||||||
if (c_start_fp < 0.0f) c_start_fp = 0.0f;
|
|
||||||
if (c_end_fp > 255.0f) c_end_fp = 255.0f;
|
|
||||||
if (c_end_fp < 0.0f) c_end_fp = 0.0f;
|
|
||||||
|
|
||||||
f32 wx_start = wxw * pw_start;
|
f32 wx_start = wxw * pw_start;
|
||||||
f32 wy_start = wyw * pw_start;
|
f32 wy_start = wyw * pw_start;
|
||||||
|
|
@ -352,32 +472,44 @@ static void rasterize_triangle(
|
||||||
f32 wz_a = wz_start;
|
f32 wz_a = wz_start;
|
||||||
|
|
||||||
for (i32 px = x; px <= span_end; px++) {
|
for (i32 px = x; px <= span_end; px++) {
|
||||||
f32 depth_norm = (z_a + 1.0f) * 0.5f;
|
f32 depth_norm = pxl8_clamp((z_a + 1.0f) * 0.5f, 0.0f, 1.0f);
|
||||||
if (depth_norm < 0.0f) depth_norm = 0.0f;
|
|
||||||
if (depth_norm > 1.0f) depth_norm = 1.0f;
|
|
||||||
u16 z16 = (u16)(depth_norm * 65535.0f);
|
u16 z16 = (u16)(depth_norm * 65535.0f);
|
||||||
|
|
||||||
if (z16 < zrow[px]) {
|
STATS_INC(stats, depth_tests, 1);
|
||||||
|
bool depth_pass = !depth_test || depth_test_pass(depth_compare, z16, zrow[px]);
|
||||||
|
if (depth_pass) {
|
||||||
|
STATS_INC(stats, depth_passes, 1);
|
||||||
pxl8_shader_ctx frag_ctx = {
|
pxl8_shader_ctx frag_ctx = {
|
||||||
.x = px,
|
.x = px,
|
||||||
.y = y,
|
.y = y,
|
||||||
.v_uv = { u_a, v_a },
|
.v_uv = { { u_a, v_a } },
|
||||||
.v_world = { wx_a, wy_a, wz_a },
|
.v_world = { wx_a, wy_a, wz_a },
|
||||||
.v_normal = setup->normal,
|
.v_normal = setup->normal,
|
||||||
.v_light = l_a / 255.0f,
|
.v_light = l_a / 255.0f,
|
||||||
.v_color = c_a,
|
.v_color = c_a,
|
||||||
.v_depth = z_a,
|
.v_depth = z_a,
|
||||||
.bindings = bindings,
|
|
||||||
.uniforms = uniforms,
|
|
||||||
.out_light_color = 0,
|
.out_light_color = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
u8 color = shader(&frag_ctx);
|
u8 color = shader(&frag_ctx, bindings, uniforms);
|
||||||
if (color != 0) {
|
STATS_INC(stats, shader_calls, 1);
|
||||||
prow[px] = color;
|
|
||||||
zrow[px] = z16;
|
if (!(alpha_test && color <= alpha_ref)) {
|
||||||
if (lrow && frag_ctx.out_light_color != 0) {
|
if (color != 0) {
|
||||||
lrow[px] = frag_ctx.out_light_color;
|
u8 out_color = color;
|
||||||
|
if (blend_enabled) {
|
||||||
|
out_color = blend_indexed(pipeline, color, prow[px], palette, colormap);
|
||||||
|
}
|
||||||
|
|
||||||
|
prow[px] = out_color;
|
||||||
|
if (depth_write) {
|
||||||
|
zrow[px] = z16;
|
||||||
|
}
|
||||||
|
STATS_INC(stats, pixels_written, 1);
|
||||||
|
if (lrow && frag_ctx.out_light_color != 0) {
|
||||||
|
lrow[px] = frag_ctx.out_light_color;
|
||||||
|
STATS_INC(stats, light_writes, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -406,9 +538,45 @@ static void rasterize_triangle(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void draw_line_clipped(
|
||||||
|
u8* fb,
|
||||||
|
u32 fb_w,
|
||||||
|
u32 fb_h,
|
||||||
|
i32 x0,
|
||||||
|
i32 y0,
|
||||||
|
i32 x1,
|
||||||
|
i32 y1,
|
||||||
|
u8 color,
|
||||||
|
i32 clip_min_x,
|
||||||
|
i32 clip_min_y,
|
||||||
|
i32 clip_max_x,
|
||||||
|
i32 clip_max_y,
|
||||||
|
pxl8_gfx_stats* stats
|
||||||
|
) {
|
||||||
|
i32 dx = abs(x1 - x0);
|
||||||
|
i32 dy = -abs(y1 - y0);
|
||||||
|
i32 sx = x0 < x1 ? 1 : -1;
|
||||||
|
i32 sy = y0 < y1 ? 1 : -1;
|
||||||
|
i32 err = dx + dy;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (x0 >= clip_min_x && x0 <= clip_max_x && y0 >= clip_min_y && y0 <= clip_max_y) {
|
||||||
|
if (x0 >= 0 && y0 >= 0 && x0 < (i32)fb_w && y0 < (i32)fb_h) {
|
||||||
|
fb[y0 * (i32)fb_w + x0] = color;
|
||||||
|
STATS_INC(stats, pixels_written, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (x0 == x1 && y0 == y1) break;
|
||||||
|
i32 e2 = 2 * err;
|
||||||
|
if (e2 >= dy) { err += dy; x0 += sx; }
|
||||||
|
if (e2 <= dx) { err += dx; y0 += sy; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define SLOT_INDEX(id) ((id) & 0xFFFF)
|
#define SLOT_INDEX(id) ((id) & 0xFFFF)
|
||||||
#define SLOT_GEN(id) ((id) >> 16)
|
#define SLOT_GEN(id) ((id) >> 16)
|
||||||
#define MAKE_ID(index, gen) (((u32)(gen) << 16) | (u32)(index))
|
#define MAKE_ID(index, gen) (((u32)(gen) << 16) | (u32)(index))
|
||||||
|
#define NEXT_GEN(gen) ((u16)((gen) == 0xFFFF ? 1 : (gen) + 1))
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
pxl8_gfx_bindings_desc desc;
|
pxl8_gfx_bindings_desc desc;
|
||||||
|
|
@ -432,18 +600,22 @@ typedef struct {
|
||||||
bool active;
|
bool active;
|
||||||
} pass_slot;
|
} pass_slot;
|
||||||
|
|
||||||
|
#define PXL8_PIPELINE_CACHE_SIZE 64
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 hash;
|
||||||
|
u32 slot_idx;
|
||||||
|
u32 last_used_frame;
|
||||||
|
bool valid;
|
||||||
|
} pipeline_cache_entry;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
pxl8_gfx_pipeline_desc desc;
|
pxl8_gfx_pipeline_desc desc;
|
||||||
u16 generation;
|
u16 generation;
|
||||||
bool active;
|
bool active;
|
||||||
|
bool cached;
|
||||||
} pipeline_slot;
|
} pipeline_slot;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
pxl8_gfx_sampler_desc desc;
|
|
||||||
u16 generation;
|
|
||||||
bool active;
|
|
||||||
} sampler_slot;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* data;
|
void* data;
|
||||||
u32 width;
|
u32 width;
|
||||||
|
|
@ -460,11 +632,13 @@ struct pxl8_renderer {
|
||||||
|
|
||||||
texture_slot textures[PXL8_GFX_MAX_TEXTURES];
|
texture_slot textures[PXL8_GFX_MAX_TEXTURES];
|
||||||
buffer_slot buffers[PXL8_GFX_MAX_BUFFERS];
|
buffer_slot buffers[PXL8_GFX_MAX_BUFFERS];
|
||||||
sampler_slot samplers[PXL8_GFX_MAX_SAMPLERS];
|
|
||||||
pipeline_slot pipelines[PXL8_GFX_MAX_PIPELINES];
|
pipeline_slot pipelines[PXL8_GFX_MAX_PIPELINES];
|
||||||
bindings_slot bindings[PXL8_GFX_MAX_BINDINGS];
|
bindings_slot bindings[PXL8_GFX_MAX_BINDINGS];
|
||||||
pass_slot passes[PXL8_GFX_MAX_PASSES];
|
pass_slot passes[PXL8_GFX_MAX_PASSES];
|
||||||
|
|
||||||
|
pipeline_cache_entry pipeline_cache[PXL8_PIPELINE_CACHE_SIZE];
|
||||||
|
u32 frame_counter;
|
||||||
|
|
||||||
pxl8_gfx_pass current_pass;
|
pxl8_gfx_pass current_pass;
|
||||||
pxl8_gfx_pipeline current_pipeline;
|
pxl8_gfx_pipeline current_pipeline;
|
||||||
pxl8_gfx_bindings current_bindings;
|
pxl8_gfx_bindings current_bindings;
|
||||||
|
|
@ -476,6 +650,7 @@ struct pxl8_renderer {
|
||||||
u32 scissor_w, scissor_h;
|
u32 scissor_w, scissor_h;
|
||||||
|
|
||||||
pxl8_shader_fn shader;
|
pxl8_shader_fn shader;
|
||||||
|
pxl8_gfx_stats stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pxl8_gfx_cmdbuf {
|
struct pxl8_gfx_cmdbuf {
|
||||||
|
|
@ -492,6 +667,7 @@ pxl8_renderer* pxl8_renderer_create(u32 width, u32 height) {
|
||||||
r->viewport_h = height;
|
r->viewport_h = height;
|
||||||
r->scissor_w = width;
|
r->scissor_w = width;
|
||||||
r->scissor_h = height;
|
r->scissor_h = height;
|
||||||
|
pxl8_renderer_reset_stats(r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -518,6 +694,15 @@ void pxl8_renderer_set_shader(pxl8_renderer* r, pxl8_shader_fn fn) {
|
||||||
if (r) r->shader = fn;
|
if (r) r->shader = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pxl8_renderer_reset_stats(pxl8_renderer* r) {
|
||||||
|
if (!r) return;
|
||||||
|
memset(&r->stats, 0, sizeof(r->stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
const pxl8_gfx_stats* pxl8_renderer_get_stats(const pxl8_renderer* r) {
|
||||||
|
return r ? &r->stats : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static u32 texture_byte_size(pxl8_gfx_texture_format fmt, u32 w, u32 h) {
|
static u32 texture_byte_size(pxl8_gfx_texture_format fmt, u32 w, u32 h) {
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case PXL8_GFX_FORMAT_INDEXED8: return w * h;
|
case PXL8_GFX_FORMAT_INDEXED8: return w * h;
|
||||||
|
|
@ -542,7 +727,7 @@ pxl8_gfx_texture pxl8_create_texture(pxl8_renderer* r, const pxl8_gfx_texture_de
|
||||||
s->height = desc->height;
|
s->height = desc->height;
|
||||||
s->format = desc->format;
|
s->format = desc->format;
|
||||||
s->usage = desc->usage;
|
s->usage = desc->usage;
|
||||||
s->generation = (s->generation + 1) ? s->generation + 1 : 1;
|
s->generation = NEXT_GEN(s->generation);
|
||||||
s->active = true;
|
s->active = true;
|
||||||
return (pxl8_gfx_texture){ MAKE_ID(i, s->generation) };
|
return (pxl8_gfx_texture){ MAKE_ID(i, s->generation) };
|
||||||
}
|
}
|
||||||
|
|
@ -569,7 +754,7 @@ pxl8_gfx_buffer pxl8_create_buffer(pxl8_renderer* r, const pxl8_gfx_buffer_desc*
|
||||||
s->append_pos = 0;
|
s->append_pos = 0;
|
||||||
s->type = desc->type;
|
s->type = desc->type;
|
||||||
s->usage = desc->usage;
|
s->usage = desc->usage;
|
||||||
s->generation = (s->generation + 1) ? s->generation + 1 : 1;
|
s->generation = NEXT_GEN(s->generation);
|
||||||
s->active = true;
|
s->active = true;
|
||||||
return (pxl8_gfx_buffer){ MAKE_ID(i, s->generation) };
|
return (pxl8_gfx_buffer){ MAKE_ID(i, s->generation) };
|
||||||
}
|
}
|
||||||
|
|
@ -578,26 +763,12 @@ pxl8_gfx_buffer pxl8_create_buffer(pxl8_renderer* r, const pxl8_gfx_buffer_desc*
|
||||||
return (pxl8_gfx_buffer){ PXL8_GFX_INVALID_ID };
|
return (pxl8_gfx_buffer){ PXL8_GFX_INVALID_ID };
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_gfx_sampler pxl8_create_sampler(pxl8_renderer* r, const pxl8_gfx_sampler_desc* desc) {
|
|
||||||
for (u32 i = 0; i < PXL8_GFX_MAX_SAMPLERS; i++) {
|
|
||||||
if (!r->samplers[i].active) {
|
|
||||||
sampler_slot* s = &r->samplers[i];
|
|
||||||
s->desc = *desc;
|
|
||||||
s->generation = (s->generation + 1) ? s->generation + 1 : 1;
|
|
||||||
s->active = true;
|
|
||||||
return (pxl8_gfx_sampler){ MAKE_ID(i, s->generation) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pxl8_error("Out of sampler slots");
|
|
||||||
return (pxl8_gfx_sampler){ PXL8_GFX_INVALID_ID };
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_gfx_pipeline pxl8_create_pipeline(pxl8_renderer* r, const pxl8_gfx_pipeline_desc* desc) {
|
pxl8_gfx_pipeline pxl8_create_pipeline(pxl8_renderer* r, const pxl8_gfx_pipeline_desc* desc) {
|
||||||
for (u32 i = 0; i < PXL8_GFX_MAX_PIPELINES; i++) {
|
for (u32 i = 0; i < PXL8_GFX_MAX_PIPELINES; i++) {
|
||||||
if (!r->pipelines[i].active) {
|
if (!r->pipelines[i].active) {
|
||||||
pipeline_slot* s = &r->pipelines[i];
|
pipeline_slot* s = &r->pipelines[i];
|
||||||
s->desc = *desc;
|
s->desc = *desc;
|
||||||
s->generation = (s->generation + 1) ? s->generation + 1 : 1;
|
s->generation = NEXT_GEN(s->generation);
|
||||||
s->active = true;
|
s->active = true;
|
||||||
return (pxl8_gfx_pipeline){ MAKE_ID(i, s->generation) };
|
return (pxl8_gfx_pipeline){ MAKE_ID(i, s->generation) };
|
||||||
}
|
}
|
||||||
|
|
@ -611,7 +782,7 @@ pxl8_gfx_bindings pxl8_create_bindings(pxl8_renderer* r, const pxl8_gfx_bindings
|
||||||
if (!r->bindings[i].active) {
|
if (!r->bindings[i].active) {
|
||||||
bindings_slot* s = &r->bindings[i];
|
bindings_slot* s = &r->bindings[i];
|
||||||
s->desc = *desc;
|
s->desc = *desc;
|
||||||
s->generation = (s->generation + 1) ? s->generation + 1 : 1;
|
s->generation = NEXT_GEN(s->generation);
|
||||||
s->active = true;
|
s->active = true;
|
||||||
return (pxl8_gfx_bindings){ MAKE_ID(i, s->generation) };
|
return (pxl8_gfx_bindings){ MAKE_ID(i, s->generation) };
|
||||||
}
|
}
|
||||||
|
|
@ -625,7 +796,7 @@ pxl8_gfx_pass pxl8_create_pass(pxl8_renderer* r, const pxl8_gfx_pass_desc* desc)
|
||||||
if (!r->passes[i].active) {
|
if (!r->passes[i].active) {
|
||||||
pass_slot* s = &r->passes[i];
|
pass_slot* s = &r->passes[i];
|
||||||
s->desc = *desc;
|
s->desc = *desc;
|
||||||
s->generation = (s->generation + 1) ? s->generation + 1 : 1;
|
s->generation = NEXT_GEN(s->generation);
|
||||||
s->active = true;
|
s->active = true;
|
||||||
return (pxl8_gfx_pass){ MAKE_ID(i, s->generation) };
|
return (pxl8_gfx_pass){ MAKE_ID(i, s->generation) };
|
||||||
}
|
}
|
||||||
|
|
@ -670,13 +841,6 @@ void pxl8_destroy_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf) {
|
||||||
s->active = false;
|
s->active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_destroy_sampler(pxl8_renderer* r, pxl8_gfx_sampler smp) {
|
|
||||||
u32 idx = SLOT_INDEX(smp.id);
|
|
||||||
if (idx < PXL8_GFX_MAX_SAMPLERS && r->samplers[idx].generation == SLOT_GEN(smp.id)) {
|
|
||||||
r->samplers[idx].active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void pxl8_destroy_pipeline(pxl8_renderer* r, pxl8_gfx_pipeline pip) {
|
void pxl8_destroy_pipeline(pxl8_renderer* r, pxl8_gfx_pipeline pip) {
|
||||||
u32 idx = SLOT_INDEX(pip.id);
|
u32 idx = SLOT_INDEX(pip.id);
|
||||||
if (idx < PXL8_GFX_MAX_PIPELINES && r->pipelines[idx].generation == SLOT_GEN(pip.id)) {
|
if (idx < PXL8_GFX_MAX_PIPELINES && r->pipelines[idx].generation == SLOT_GEN(pip.id)) {
|
||||||
|
|
@ -739,17 +903,17 @@ u32 pxl8_buffer_size(pxl8_renderer* r, pxl8_gfx_buffer buf) {
|
||||||
return r->buffers[SLOT_INDEX(buf.id)].size;
|
return r->buffers[SLOT_INDEX(buf.id)].size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* pxl8_texture_ptr(pxl8_renderer* r, pxl8_gfx_texture tex) {
|
void* pxl8_texture_get_data(pxl8_renderer* r, pxl8_gfx_texture tex) {
|
||||||
if (!VALID_TEX(r, tex)) return NULL;
|
if (!VALID_TEX(r, tex)) return NULL;
|
||||||
return r->textures[SLOT_INDEX(tex.id)].data;
|
return r->textures[SLOT_INDEX(tex.id)].data;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 pxl8_texture_width(pxl8_renderer* r, pxl8_gfx_texture tex) {
|
u32 pxl8_texture_get_width(pxl8_renderer* r, pxl8_gfx_texture tex) {
|
||||||
if (!VALID_TEX(r, tex)) return 0;
|
if (!VALID_TEX(r, tex)) return 0;
|
||||||
return r->textures[SLOT_INDEX(tex.id)].width;
|
return r->textures[SLOT_INDEX(tex.id)].width;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 pxl8_texture_height(pxl8_renderer* r, pxl8_gfx_texture tex) {
|
u32 pxl8_texture_get_height(pxl8_renderer* r, pxl8_gfx_texture tex) {
|
||||||
if (!VALID_TEX(r, tex)) return 0;
|
if (!VALID_TEX(r, tex)) return 0;
|
||||||
return r->textures[SLOT_INDEX(tex.id)].height;
|
return r->textures[SLOT_INDEX(tex.id)].height;
|
||||||
}
|
}
|
||||||
|
|
@ -759,18 +923,6 @@ pxl8_gfx_texture_format pxl8_texture_get_format(pxl8_renderer* r, pxl8_gfx_textu
|
||||||
return r->textures[SLOT_INDEX(tex.id)].format;
|
return r->textures[SLOT_INDEX(tex.id)].format;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* pxl8_texture_get_data(pxl8_renderer* r, pxl8_gfx_texture tex) {
|
|
||||||
return pxl8_texture_ptr(r, tex);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 pxl8_texture_get_width(pxl8_renderer* r, pxl8_gfx_texture tex) {
|
|
||||||
return pxl8_texture_width(r, tex);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 pxl8_texture_get_height(pxl8_renderer* r, pxl8_gfx_texture tex) {
|
|
||||||
return pxl8_texture_height(r, tex);
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_gfx_cmdbuf* pxl8_cmdbuf_create(u32 capacity) {
|
pxl8_gfx_cmdbuf* pxl8_cmdbuf_create(u32 capacity) {
|
||||||
pxl8_gfx_cmdbuf* cb = pxl8_malloc(sizeof(pxl8_gfx_cmdbuf));
|
pxl8_gfx_cmdbuf* cb = pxl8_malloc(sizeof(pxl8_gfx_cmdbuf));
|
||||||
cb->commands = pxl8_malloc(capacity * sizeof(pxl8_gfx_cmd));
|
cb->commands = pxl8_malloc(capacity * sizeof(pxl8_gfx_cmd));
|
||||||
|
|
@ -865,6 +1017,9 @@ static void execute_draw(
|
||||||
if (!VALID_PASS(r, r->current_pass)) return;
|
if (!VALID_PASS(r, r->current_pass)) return;
|
||||||
if (!VALID_PIPELINE(r, r->current_pipeline)) return;
|
if (!VALID_PIPELINE(r, r->current_pipeline)) return;
|
||||||
|
|
||||||
|
u64 exec_start = STATS_START();
|
||||||
|
STATS_INC(&r->stats, draw_calls, 1);
|
||||||
|
|
||||||
buffer_slot* vb = &r->buffers[SLOT_INDEX(cmd->vertex_buffer.id)];
|
buffer_slot* vb = &r->buffers[SLOT_INDEX(cmd->vertex_buffer.id)];
|
||||||
buffer_slot* ib = use_indices ? &r->buffers[SLOT_INDEX(cmd->index_buffer.id)] : NULL;
|
buffer_slot* ib = use_indices ? &r->buffers[SLOT_INDEX(cmd->index_buffer.id)] : NULL;
|
||||||
pass_slot* pass = &r->passes[SLOT_INDEX(r->current_pass.id)];
|
pass_slot* pass = &r->passes[SLOT_INDEX(r->current_pass.id)];
|
||||||
|
|
@ -872,10 +1027,12 @@ static void execute_draw(
|
||||||
|
|
||||||
if (!VALID_TEX(r, pass->desc.color.texture)) {
|
if (!VALID_TEX(r, pass->desc.color.texture)) {
|
||||||
pxl8_error("draw: invalid color texture");
|
pxl8_error("draw: invalid color texture");
|
||||||
|
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!VALID_TEX(r, pass->desc.depth.texture)) {
|
if (!VALID_TEX(r, pass->desc.depth.texture)) {
|
||||||
pxl8_error("draw: invalid depth texture");
|
pxl8_error("draw: invalid depth texture");
|
||||||
|
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -887,6 +1044,42 @@ static void execute_draw(
|
||||||
u32 fb_w = color_tex->width;
|
u32 fb_w = color_tex->width;
|
||||||
u32 fb_h = color_tex->height;
|
u32 fb_h = color_tex->height;
|
||||||
|
|
||||||
|
if (r->viewport_w == 0 || r->viewport_h == 0) {
|
||||||
|
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 vp_x = r->viewport_x;
|
||||||
|
i32 vp_y = r->viewport_y;
|
||||||
|
u32 vp_w = r->viewport_w;
|
||||||
|
u32 vp_h = r->viewport_h;
|
||||||
|
|
||||||
|
i32 clip_min_x = vp_x;
|
||||||
|
i32 clip_min_y = vp_y;
|
||||||
|
i32 clip_max_x = vp_x + (i32)vp_w - 1;
|
||||||
|
i32 clip_max_y = vp_y + (i32)vp_h - 1;
|
||||||
|
|
||||||
|
if (r->scissor_w > 0 && r->scissor_h > 0) {
|
||||||
|
i32 sc_min_x = r->scissor_x;
|
||||||
|
i32 sc_min_y = r->scissor_y;
|
||||||
|
i32 sc_max_x = r->scissor_x + (i32)r->scissor_w - 1;
|
||||||
|
i32 sc_max_y = r->scissor_y + (i32)r->scissor_h - 1;
|
||||||
|
|
||||||
|
if (sc_min_x > clip_min_x) clip_min_x = sc_min_x;
|
||||||
|
if (sc_min_y > clip_min_y) clip_min_y = sc_min_y;
|
||||||
|
if (sc_max_x < clip_max_x) clip_max_x = sc_max_x;
|
||||||
|
if (sc_max_y < clip_max_y) clip_max_y = sc_max_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clip_min_x < 0) clip_min_x = 0;
|
||||||
|
if (clip_min_y < 0) clip_min_y = 0;
|
||||||
|
if (clip_max_x >= (i32)fb_w) clip_max_x = (i32)fb_w - 1;
|
||||||
|
if (clip_max_y >= (i32)fb_h) clip_max_y = (i32)fb_h - 1;
|
||||||
|
if (clip_min_x > clip_max_x || clip_min_y > clip_max_y) {
|
||||||
|
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
u32* light_accum = NULL;
|
u32* light_accum = NULL;
|
||||||
if (VALID_TEX(r, pass->desc.light_accum.texture)) {
|
if (VALID_TEX(r, pass->desc.light_accum.texture)) {
|
||||||
texture_slot* light_tex = &r->textures[SLOT_INDEX(pass->desc.light_accum.texture.id)];
|
texture_slot* light_tex = &r->textures[SLOT_INDEX(pass->desc.light_accum.texture.id)];
|
||||||
|
|
@ -901,7 +1094,10 @@ static void execute_draw(
|
||||||
f32 near = 0.1f;
|
f32 near = 0.1f;
|
||||||
|
|
||||||
pxl8_shader_fn shader = pip->desc.shader;
|
pxl8_shader_fn shader = pip->desc.shader;
|
||||||
if (!shader) return;
|
if (!shader) {
|
||||||
|
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pxl8_shader_bindings shader_bindings = {0};
|
pxl8_shader_bindings shader_bindings = {0};
|
||||||
pxl8_shader_uniforms shader_uniforms = r->current_draw_params.shader;
|
pxl8_shader_uniforms shader_uniforms = r->current_draw_params.shader;
|
||||||
|
|
@ -913,19 +1109,26 @@ static void execute_draw(
|
||||||
shader_bindings.colormap = (const u8*)bnd->desc.colormap;
|
shader_bindings.colormap = (const u8*)bnd->desc.colormap;
|
||||||
shader_bindings.palette = bnd->desc.palette;
|
shader_bindings.palette = bnd->desc.palette;
|
||||||
|
|
||||||
pxl8_gfx_texture tex0 = bnd->desc.textures[0];
|
const pxl8_atlas* atlas = bnd->desc.atlas;
|
||||||
if (tex0.id != PXL8_GFX_INVALID_ID && VALID_TEX(r, tex0)) {
|
if (atlas && bnd->desc.texture_id != UINT32_MAX) {
|
||||||
texture_slot* ts = &r->textures[SLOT_INDEX(tex0.id)];
|
const pxl8_atlas_entry* entry = pxl8_atlas_get_entry(atlas, bnd->desc.texture_id);
|
||||||
shader_bindings.texture = ts->data;
|
if (entry && entry->active) {
|
||||||
shader_bindings.tex_width = ts->width;
|
shader_bindings.atlas = pxl8_atlas_get_pixels_tiled(atlas);
|
||||||
shader_bindings.tex_height = ts->height;
|
shader_bindings.width = (u32)entry->w;
|
||||||
|
shader_bindings.height = (u32)entry->h;
|
||||||
|
shader_bindings.use_tiled = true;
|
||||||
|
shader_bindings.tiled.base = entry->tiled_base;
|
||||||
|
shader_bindings.tiled.log2_w = entry->log2_w;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool double_sided = pip->desc.double_sided;
|
bool double_sided = pip->desc.double_sided;
|
||||||
|
pxl8_gfx_cull_mode cull_mode = pip->desc.rasterizer.cull;
|
||||||
bool is_wireframe = pip->desc.rasterizer.fill == PXL8_GFX_FILL_WIREFRAME;
|
bool is_wireframe = pip->desc.rasterizer.fill == PXL8_GFX_FILL_WIREFRAME;
|
||||||
|
|
||||||
for (u32 i = cmd->first_index; i < cmd->first_index + cmd->index_count; i += 3) {
|
for (u32 i = cmd->first_index; i < cmd->first_index + cmd->index_count; i += 3) {
|
||||||
|
STATS_INC(&r->stats, triangles, 1);
|
||||||
u16 i0, i1, i2;
|
u16 i0, i1, i2;
|
||||||
if (use_indices) {
|
if (use_indices) {
|
||||||
if (i + 2 >= ib->size / sizeof(u16)) break;
|
if (i + 2 >= ib->size / sizeof(u16)) break;
|
||||||
|
|
@ -956,65 +1159,98 @@ static void execute_draw(
|
||||||
rv1.clip_pos = pxl8_mat4_multiply_vec4(mvp, p1);
|
rv1.clip_pos = pxl8_mat4_multiply_vec4(mvp, p1);
|
||||||
rv2.clip_pos = pxl8_mat4_multiply_vec4(mvp, p2);
|
rv2.clip_pos = pxl8_mat4_multiply_vec4(mvp, p2);
|
||||||
|
|
||||||
pxl8_vec4 w0 = pxl8_mat4_multiply_vec4(r->current_draw_params.model, p0);
|
if (!is_wireframe) {
|
||||||
pxl8_vec4 w1 = pxl8_mat4_multiply_vec4(r->current_draw_params.model, p1);
|
pxl8_vec4 w0 = pxl8_mat4_multiply_vec4(r->current_draw_params.model, p0);
|
||||||
pxl8_vec4 w2 = pxl8_mat4_multiply_vec4(r->current_draw_params.model, p2);
|
pxl8_vec4 w1 = pxl8_mat4_multiply_vec4(r->current_draw_params.model, p1);
|
||||||
rv0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z};
|
pxl8_vec4 w2 = pxl8_mat4_multiply_vec4(r->current_draw_params.model, p2);
|
||||||
rv1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z};
|
rv0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z};
|
||||||
rv2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z};
|
rv1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z};
|
||||||
|
rv2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z};
|
||||||
|
|
||||||
rv0.normal = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(r->current_draw_params.model, v0->normal));
|
rv0.normal = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(r->current_draw_params.model, v0->normal));
|
||||||
rv1.normal = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(r->current_draw_params.model, v1->normal));
|
rv1.normal = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(r->current_draw_params.model, v1->normal));
|
||||||
rv2.normal = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(r->current_draw_params.model, v2->normal));
|
rv2.normal = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(r->current_draw_params.model, v2->normal));
|
||||||
|
|
||||||
rv0.u = v0->u; rv0.v = v0->v;
|
rv0.u = v0->u; rv0.v = v0->v;
|
||||||
rv1.u = v1->u; rv1.v = v1->v;
|
rv1.u = v1->u; rv1.v = v1->v;
|
||||||
rv2.u = v2->u; rv2.v = v2->v;
|
rv2.u = v2->u; rv2.v = v2->v;
|
||||||
|
|
||||||
rv0.color = v0->color;
|
rv0.color = v0->color;
|
||||||
rv1.color = v1->color;
|
rv1.color = v1->color;
|
||||||
rv2.color = v2->color;
|
rv2.color = v2->color;
|
||||||
|
|
||||||
rv0.light = v0->light;
|
rv0.light = v0->light;
|
||||||
rv1.light = v1->light;
|
rv1.light = v1->light;
|
||||||
rv2.light = v2->light;
|
rv2.light = v2->light;
|
||||||
|
} else {
|
||||||
|
rv0.world_pos = (pxl8_vec3){0, 0, 0};
|
||||||
|
rv1.world_pos = (pxl8_vec3){0, 0, 0};
|
||||||
|
rv2.world_pos = (pxl8_vec3){0, 0, 0};
|
||||||
|
rv0.normal = (pxl8_vec3){0, 0, 0};
|
||||||
|
rv1.normal = (pxl8_vec3){0, 0, 0};
|
||||||
|
rv2.normal = (pxl8_vec3){0, 0, 0};
|
||||||
|
rv0.u = 0.0f; rv0.v = 0.0f;
|
||||||
|
rv1.u = 0.0f; rv1.v = 0.0f;
|
||||||
|
rv2.u = 0.0f; rv2.v = 0.0f;
|
||||||
|
rv0.color = 0; rv1.color = 0; rv2.color = 0;
|
||||||
|
rv0.light = 0; rv1.light = 0; rv2.light = 0;
|
||||||
|
}
|
||||||
|
|
||||||
raster_vertex clipped[6];
|
raster_vertex clipped[6];
|
||||||
i32 clipped_count = clip_triangle_near(&rv0, &rv1, &rv2, near, clipped);
|
i32 clipped_count = clip_triangle_near(&rv0, &rv1, &rv2, near, clipped);
|
||||||
|
|
||||||
for (i32 t = 0; t < clipped_count; t += 3) {
|
for (i32 t = 0; t < clipped_count; t += 3) {
|
||||||
|
STATS_INC(&r->stats, clipped_triangles, 1);
|
||||||
if (is_wireframe) {
|
if (is_wireframe) {
|
||||||
f32 hw = (f32)fb_w * 0.5f;
|
f32 hw = (f32)vp_w * 0.5f;
|
||||||
f32 hh = (f32)fb_h * 0.5f;
|
f32 hh = (f32)vp_h * 0.5f;
|
||||||
|
|
||||||
raster_vertex* wv0 = &clipped[t];
|
raster_vertex* wv0 = &clipped[t];
|
||||||
raster_vertex* wv1 = &clipped[t+1];
|
raster_vertex* wv1 = &clipped[t+1];
|
||||||
raster_vertex* wv2 = &clipped[t+2];
|
raster_vertex* wv2 = &clipped[t+2];
|
||||||
|
|
||||||
i32 sx0 = (i32)(hw + wv0->clip_pos.x / wv0->clip_pos.w * hw);
|
i32 sx0 = (i32)((f32)vp_x + hw + wv0->clip_pos.x / wv0->clip_pos.w * hw);
|
||||||
i32 sy0 = (i32)(hh - wv0->clip_pos.y / wv0->clip_pos.w * hh);
|
i32 sy0 = (i32)((f32)vp_y + hh - wv0->clip_pos.y / wv0->clip_pos.w * hh);
|
||||||
i32 sx1 = (i32)(hw + wv1->clip_pos.x / wv1->clip_pos.w * hw);
|
i32 sx1 = (i32)((f32)vp_x + hw + wv1->clip_pos.x / wv1->clip_pos.w * hw);
|
||||||
i32 sy1 = (i32)(hh - wv1->clip_pos.y / wv1->clip_pos.w * hh);
|
i32 sy1 = (i32)((f32)vp_y + hh - wv1->clip_pos.y / wv1->clip_pos.w * hh);
|
||||||
i32 sx2 = (i32)(hw + wv2->clip_pos.x / wv2->clip_pos.w * hw);
|
i32 sx2 = (i32)((f32)vp_x + hw + wv2->clip_pos.x / wv2->clip_pos.w * hw);
|
||||||
i32 sy2 = (i32)(hh - wv2->clip_pos.y / wv2->clip_pos.w * hh);
|
i32 sy2 = (i32)((f32)vp_y + hh - wv2->clip_pos.y / wv2->clip_pos.w * hh);
|
||||||
|
|
||||||
|
f32 cross = (f32)(sx1 - sx0) * (f32)(sy2 - sy0) - (f32)(sy1 - sy0) * (f32)(sx2 - sx0);
|
||||||
|
if (!double_sided) {
|
||||||
|
if (cull_mode == PXL8_GFX_CULL_BACK && cross >= 0.0f) continue;
|
||||||
|
if (cull_mode == PXL8_GFX_CULL_FRONT && cross <= 0.0f) continue;
|
||||||
|
}
|
||||||
|
|
||||||
u8 wire_color = v0->color ? v0->color : 15;
|
u8 wire_color = v0->color ? v0->color : 15;
|
||||||
pxl8_draw_line(r, pass->desc.color.texture, sx0, sy0, sx1, sy1, wire_color);
|
draw_line_clipped(fb, fb_w, fb_h, sx0, sy0, sx1, sy1, wire_color,
|
||||||
pxl8_draw_line(r, pass->desc.color.texture, sx1, sy1, sx2, sy2, wire_color);
|
clip_min_x, clip_min_y, clip_max_x, clip_max_y, &r->stats);
|
||||||
pxl8_draw_line(r, pass->desc.color.texture, sx2, sy2, sx0, sy0, wire_color);
|
draw_line_clipped(fb, fb_w, fb_h, sx1, sy1, sx2, sy2, wire_color,
|
||||||
|
clip_min_x, clip_min_y, clip_max_x, clip_max_y, &r->stats);
|
||||||
|
draw_line_clipped(fb, fb_w, fb_h, sx2, sy2, sx0, sy0, wire_color,
|
||||||
|
clip_min_x, clip_min_y, clip_max_x, clip_max_y, &r->stats);
|
||||||
} else {
|
} else {
|
||||||
tri_setup setup;
|
tri_setup setup;
|
||||||
if (!setup_tri(&setup, &clipped[t], &clipped[t+1], &clipped[t+2], fb_w, fb_h, double_sided)) {
|
if (!setup_tri(&setup, &clipped[t], &clipped[t+1], &clipped[t+2],
|
||||||
|
vp_x, vp_y, vp_w, vp_h,
|
||||||
|
clip_min_x, clip_min_y, clip_max_x, clip_max_y,
|
||||||
|
cull_mode, double_sided)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rasterize_triangle(&setup, fb, zb, light_accum, fb_w, shader, &shader_bindings, &shader_uniforms);
|
u64 raster_start = STATS_START();
|
||||||
|
rasterize_triangle(&setup, fb, zb, light_accum, fb_w, shader, &pip->desc,
|
||||||
|
&shader_bindings, &shader_uniforms, &r->stats);
|
||||||
|
STATS_ADD(&r->stats, raster_ns, raster_start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb) {
|
void pxl8_gfx_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb) {
|
||||||
|
u64 submit_start = STATS_START();
|
||||||
for (u32 i = 0; i < cb->count; i++) {
|
for (u32 i = 0; i < cb->count; i++) {
|
||||||
pxl8_gfx_cmd* cmd = &cb->commands[i];
|
pxl8_gfx_cmd* cmd = &cb->commands[i];
|
||||||
switch (cmd->type) {
|
switch (cmd->type) {
|
||||||
|
|
@ -1070,10 +1306,7 @@ void pxl8_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb) {
|
||||||
r->buffers[i].append_pos = 0;
|
r->buffers[i].append_pos = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
STATS_ADD(&r->stats, submit_ns, submit_start);
|
||||||
|
|
||||||
void pxl8_gfx_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb) {
|
|
||||||
pxl8_submit(r, cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_clear(pxl8_renderer* r, pxl8_gfx_texture target, u8 color) {
|
void pxl8_clear(pxl8_renderer* r, pxl8_gfx_texture target, u8 color) {
|
||||||
|
|
@ -1221,8 +1454,8 @@ void pxl8_draw_circle_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_resolve(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture light_accum,
|
void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture light_accum,
|
||||||
const u32* palette, u32* output) {
|
const u32* palette, u32* output) {
|
||||||
if (!VALID_TEX(r, color)) return;
|
if (!VALID_TEX(r, color)) return;
|
||||||
texture_slot* cs = &r->textures[SLOT_INDEX(color.id)];
|
texture_slot* cs = &r->textures[SLOT_INDEX(color.id)];
|
||||||
|
|
||||||
|
|
@ -1256,9 +1489,9 @@ void pxl8_resolve(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture lig
|
||||||
bg += (i32)((f32)(lg - 128) * t * 2.0f);
|
bg += (i32)((f32)(lg - 128) * t * 2.0f);
|
||||||
bb += (i32)((f32)(lb - 128) * t * 2.0f);
|
bb += (i32)((f32)(lb - 128) * t * 2.0f);
|
||||||
|
|
||||||
if (br < 0) br = 0; if (br > 255) br = 255;
|
br = pxl8_clamp_byte(br);
|
||||||
if (bg < 0) bg = 0; if (bg > 255) bg = 255;
|
bg = pxl8_clamp_byte(bg);
|
||||||
if (bb < 0) bb = 0; if (bb > 255) bb = 255;
|
bb = pxl8_clamp_byte(bb);
|
||||||
|
|
||||||
base = (u32)br | ((u32)bg << 8) | ((u32)bb << 16) | 0xFF000000;
|
base = (u32)br | ((u32)bg << 8) | ((u32)bb << 16) | 0xFF000000;
|
||||||
}
|
}
|
||||||
|
|
@ -1267,8 +1500,3 @@ void pxl8_resolve(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture lig
|
||||||
output[i] = base | 0xFF000000;
|
output[i] = base | 0xFF000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture light_accum,
|
|
||||||
const u32* palette, u32* output) {
|
|
||||||
pxl8_resolve(r, color, light_accum, palette, output);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
#include "pxl8_render_types.h"
|
#include "pxl8_render_types.h"
|
||||||
#include "pxl8_shader.h"
|
#include "pxl8_shader.h"
|
||||||
|
|
||||||
|
#ifndef PXL8_GFX_ENABLE_STATS
|
||||||
|
#define PXL8_GFX_ENABLE_STATS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -11,9 +15,25 @@ extern "C" {
|
||||||
typedef struct pxl8_renderer pxl8_renderer;
|
typedef struct pxl8_renderer pxl8_renderer;
|
||||||
typedef struct pxl8_gfx_cmdbuf pxl8_gfx_cmdbuf;
|
typedef struct pxl8_gfx_cmdbuf pxl8_gfx_cmdbuf;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_stats {
|
||||||
|
u64 draw_calls;
|
||||||
|
u64 triangles;
|
||||||
|
u64 clipped_triangles;
|
||||||
|
u64 depth_tests;
|
||||||
|
u64 depth_passes;
|
||||||
|
u64 shader_calls;
|
||||||
|
u64 pixels_written;
|
||||||
|
u64 light_writes;
|
||||||
|
u64 submit_ns;
|
||||||
|
u64 execute_draw_ns;
|
||||||
|
u64 raster_ns;
|
||||||
|
} pxl8_gfx_stats;
|
||||||
|
|
||||||
pxl8_renderer* pxl8_renderer_create(u32 width, u32 height);
|
pxl8_renderer* pxl8_renderer_create(u32 width, u32 height);
|
||||||
void pxl8_renderer_destroy(pxl8_renderer* r);
|
void pxl8_renderer_destroy(pxl8_renderer* r);
|
||||||
void pxl8_renderer_set_shader(pxl8_renderer* r, pxl8_shader_fn fn);
|
void pxl8_renderer_set_shader(pxl8_renderer* r, pxl8_shader_fn fn);
|
||||||
|
void pxl8_renderer_reset_stats(pxl8_renderer* r);
|
||||||
|
const pxl8_gfx_stats* pxl8_renderer_get_stats(const pxl8_renderer* r);
|
||||||
|
|
||||||
u32 pxl8_renderer_get_width(const pxl8_renderer* r);
|
u32 pxl8_renderer_get_width(const pxl8_renderer* r);
|
||||||
u32 pxl8_renderer_get_height(const pxl8_renderer* r);
|
u32 pxl8_renderer_get_height(const pxl8_renderer* r);
|
||||||
|
|
@ -22,14 +42,12 @@ pxl8_gfx_bindings pxl8_create_bindings(pxl8_renderer* r, const pxl8_gfx_bindings
|
||||||
pxl8_gfx_buffer pxl8_create_buffer(pxl8_renderer* r, const pxl8_gfx_buffer_desc* desc);
|
pxl8_gfx_buffer pxl8_create_buffer(pxl8_renderer* r, const pxl8_gfx_buffer_desc* desc);
|
||||||
pxl8_gfx_pass pxl8_create_pass(pxl8_renderer* r, const pxl8_gfx_pass_desc* desc);
|
pxl8_gfx_pass pxl8_create_pass(pxl8_renderer* r, const pxl8_gfx_pass_desc* desc);
|
||||||
pxl8_gfx_pipeline pxl8_create_pipeline(pxl8_renderer* r, const pxl8_gfx_pipeline_desc* desc);
|
pxl8_gfx_pipeline pxl8_create_pipeline(pxl8_renderer* r, const pxl8_gfx_pipeline_desc* desc);
|
||||||
pxl8_gfx_sampler pxl8_create_sampler(pxl8_renderer* r, const pxl8_gfx_sampler_desc* desc);
|
|
||||||
pxl8_gfx_texture pxl8_create_texture(pxl8_renderer* r, const pxl8_gfx_texture_desc* desc);
|
pxl8_gfx_texture pxl8_create_texture(pxl8_renderer* r, const pxl8_gfx_texture_desc* desc);
|
||||||
|
|
||||||
void pxl8_destroy_bindings(pxl8_renderer* r, pxl8_gfx_bindings bnd);
|
void pxl8_destroy_bindings(pxl8_renderer* r, pxl8_gfx_bindings bnd);
|
||||||
void pxl8_destroy_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
void pxl8_destroy_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
||||||
void pxl8_destroy_pass(pxl8_renderer* r, pxl8_gfx_pass pass);
|
void pxl8_destroy_pass(pxl8_renderer* r, pxl8_gfx_pass pass);
|
||||||
void pxl8_destroy_pipeline(pxl8_renderer* r, pxl8_gfx_pipeline pip);
|
void pxl8_destroy_pipeline(pxl8_renderer* r, pxl8_gfx_pipeline pip);
|
||||||
void pxl8_destroy_sampler(pxl8_renderer* r, pxl8_gfx_sampler smp);
|
|
||||||
void pxl8_destroy_texture(pxl8_renderer* r, pxl8_gfx_texture tex);
|
void pxl8_destroy_texture(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
|
|
||||||
void pxl8_update_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf, const pxl8_gfx_range* data);
|
void pxl8_update_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf, const pxl8_gfx_range* data);
|
||||||
|
|
@ -39,11 +57,8 @@ void pxl8_update_texture(pxl8_renderer* r, pxl8_gfx_texture tex, const pxl8_gfx_
|
||||||
void* pxl8_buffer_ptr(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
void* pxl8_buffer_ptr(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
||||||
u32 pxl8_buffer_size(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
u32 pxl8_buffer_size(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
||||||
|
|
||||||
void* pxl8_texture_ptr(pxl8_renderer* r, pxl8_gfx_texture tex);
|
|
||||||
void* pxl8_texture_get_data(pxl8_renderer* r, pxl8_gfx_texture tex);
|
void* pxl8_texture_get_data(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
u32 pxl8_texture_width(pxl8_renderer* r, pxl8_gfx_texture tex);
|
|
||||||
u32 pxl8_texture_get_width(pxl8_renderer* r, pxl8_gfx_texture tex);
|
u32 pxl8_texture_get_width(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
u32 pxl8_texture_height(pxl8_renderer* r, pxl8_gfx_texture tex);
|
|
||||||
u32 pxl8_texture_get_height(pxl8_renderer* r, pxl8_gfx_texture tex);
|
u32 pxl8_texture_get_height(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
pxl8_gfx_texture_format pxl8_texture_get_format(pxl8_renderer* r, pxl8_gfx_texture tex);
|
pxl8_gfx_texture_format pxl8_texture_get_format(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
|
|
||||||
|
|
@ -61,7 +76,6 @@ void pxl8_set_viewport(pxl8_gfx_cmdbuf* cb, i32 x, i32 y, u32 w, u32 h);
|
||||||
|
|
||||||
void pxl8_draw(pxl8_gfx_cmdbuf* cb, pxl8_gfx_buffer vb, pxl8_gfx_buffer ib, u32 first, u32 count, u32 base_vertex);
|
void pxl8_draw(pxl8_gfx_cmdbuf* cb, pxl8_gfx_buffer vb, pxl8_gfx_buffer ib, u32 first, u32 count, u32 base_vertex);
|
||||||
|
|
||||||
void pxl8_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb);
|
|
||||||
void pxl8_gfx_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb);
|
void pxl8_gfx_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb);
|
||||||
|
|
||||||
void pxl8_clear(pxl8_renderer* r, pxl8_gfx_texture target, u8 color);
|
void pxl8_clear(pxl8_renderer* r, pxl8_gfx_texture target, u8 color);
|
||||||
|
|
@ -76,10 +90,8 @@ void pxl8_draw_rect_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y
|
||||||
void pxl8_draw_circle(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i32 cy, i32 radius, u8 color);
|
void pxl8_draw_circle(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i32 cy, i32 radius, u8 color);
|
||||||
void pxl8_draw_circle_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i32 cy, i32 radius, u8 color);
|
void pxl8_draw_circle_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i32 cy, i32 radius, u8 color);
|
||||||
|
|
||||||
void pxl8_resolve(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture light_accum,
|
|
||||||
const u32* palette, u32* output);
|
|
||||||
void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture light_accum,
|
void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture light_accum,
|
||||||
const u32* palette, u32* output);
|
const u32* palette, u32* output);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_atlas.h"
|
||||||
#include "pxl8_colormap.h"
|
#include "pxl8_colormap.h"
|
||||||
#include "pxl8_lights.h"
|
#include "pxl8_lights.h"
|
||||||
#include "pxl8_math.h"
|
#include "pxl8_math.h"
|
||||||
|
|
@ -10,17 +11,14 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PXL8_GFX_MAX_TEXTURES 256
|
|
||||||
#define PXL8_GFX_MAX_BUFFERS 512
|
|
||||||
#define PXL8_GFX_MAX_SAMPLERS 32
|
|
||||||
#define PXL8_GFX_MAX_PIPELINES 128
|
|
||||||
#define PXL8_GFX_MAX_BINDINGS 256
|
#define PXL8_GFX_MAX_BINDINGS 256
|
||||||
|
#define PXL8_GFX_MAX_BUFFERS 512
|
||||||
#define PXL8_GFX_MAX_PASSES 32
|
#define PXL8_GFX_MAX_PASSES 32
|
||||||
#define PXL8_GFX_MAX_BOUND_TEXTURES 4
|
#define PXL8_GFX_MAX_PIPELINES 128
|
||||||
|
#define PXL8_GFX_MAX_TEXTURES 256
|
||||||
|
|
||||||
typedef struct { u32 id; } pxl8_gfx_buffer;
|
typedef struct { u32 id; } pxl8_gfx_buffer;
|
||||||
typedef struct { u32 id; } pxl8_gfx_texture;
|
typedef struct { u32 id; } pxl8_gfx_texture;
|
||||||
typedef struct { u32 id; } pxl8_gfx_sampler;
|
|
||||||
typedef struct { u32 id; } pxl8_gfx_pipeline;
|
typedef struct { u32 id; } pxl8_gfx_pipeline;
|
||||||
typedef struct { u32 id; } pxl8_gfx_bindings;
|
typedef struct { u32 id; } pxl8_gfx_bindings;
|
||||||
typedef struct { u32 id; } pxl8_gfx_pass;
|
typedef struct { u32 id; } pxl8_gfx_pass;
|
||||||
|
|
@ -95,17 +93,6 @@ typedef enum pxl8_gfx_store_op {
|
||||||
PXL8_GFX_STORE_DONT_CARE,
|
PXL8_GFX_STORE_DONT_CARE,
|
||||||
} pxl8_gfx_store_op;
|
} pxl8_gfx_store_op;
|
||||||
|
|
||||||
typedef enum pxl8_gfx_filter {
|
|
||||||
PXL8_GFX_FILTER_NEAREST,
|
|
||||||
PXL8_GFX_FILTER_LINEAR,
|
|
||||||
} pxl8_gfx_filter;
|
|
||||||
|
|
||||||
typedef enum pxl8_gfx_wrap {
|
|
||||||
PXL8_GFX_WRAP_REPEAT,
|
|
||||||
PXL8_GFX_WRAP_CLAMP,
|
|
||||||
PXL8_GFX_WRAP_MIRROR,
|
|
||||||
} pxl8_gfx_wrap;
|
|
||||||
|
|
||||||
typedef struct pxl8_gfx_buffer_desc {
|
typedef struct pxl8_gfx_buffer_desc {
|
||||||
pxl8_gfx_range data;
|
pxl8_gfx_range data;
|
||||||
u32 capacity;
|
u32 capacity;
|
||||||
|
|
@ -122,13 +109,6 @@ typedef struct pxl8_gfx_texture_desc {
|
||||||
bool render_target;
|
bool render_target;
|
||||||
} pxl8_gfx_texture_desc;
|
} pxl8_gfx_texture_desc;
|
||||||
|
|
||||||
typedef struct pxl8_gfx_sampler_desc {
|
|
||||||
pxl8_gfx_filter min_filter;
|
|
||||||
pxl8_gfx_filter mag_filter;
|
|
||||||
pxl8_gfx_wrap wrap_u;
|
|
||||||
pxl8_gfx_wrap wrap_v;
|
|
||||||
} pxl8_gfx_sampler_desc;
|
|
||||||
|
|
||||||
typedef pxl8_shader_fn pxl8_gfx_shader;
|
typedef pxl8_shader_fn pxl8_gfx_shader;
|
||||||
|
|
||||||
typedef struct pxl8_gfx_pipeline_desc {
|
typedef struct pxl8_gfx_pipeline_desc {
|
||||||
|
|
@ -159,10 +139,10 @@ typedef struct pxl8_gfx_pipeline_desc {
|
||||||
} pxl8_gfx_pipeline_desc;
|
} pxl8_gfx_pipeline_desc;
|
||||||
|
|
||||||
typedef struct pxl8_gfx_bindings_desc {
|
typedef struct pxl8_gfx_bindings_desc {
|
||||||
pxl8_gfx_texture textures[PXL8_GFX_MAX_BOUND_TEXTURES];
|
const pxl8_atlas* atlas;
|
||||||
pxl8_gfx_sampler samplers[PXL8_GFX_MAX_BOUND_TEXTURES];
|
|
||||||
const pxl8_colormap* colormap;
|
const pxl8_colormap* colormap;
|
||||||
const u32* palette;
|
const u32* palette;
|
||||||
|
u32 texture_id;
|
||||||
} pxl8_gfx_bindings_desc;
|
} pxl8_gfx_bindings_desc;
|
||||||
|
|
||||||
typedef struct pxl8_gfx_pass_color_attachment {
|
typedef struct pxl8_gfx_pass_color_attachment {
|
||||||
|
|
|
||||||
|
|
@ -39,34 +39,42 @@ typedef struct pxl8_shader_uniforms {
|
||||||
u32 lights_count;
|
u32 lights_count;
|
||||||
bool textures;
|
bool textures;
|
||||||
f32 time;
|
f32 time;
|
||||||
bool wireframe;
|
|
||||||
} pxl8_shader_uniforms;
|
} pxl8_shader_uniforms;
|
||||||
|
|
||||||
typedef struct pxl8_shader_bindings {
|
typedef struct pxl8_shader_bindings {
|
||||||
const u8* colormap;
|
const u8* colormap;
|
||||||
const u32* palette;
|
const u32* palette;
|
||||||
const u8* texture;
|
const u8* atlas;
|
||||||
u32 tex_width;
|
u32 width, height;
|
||||||
u32 tex_height;
|
bool use_tiled;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
u32 stride;
|
||||||
|
u32 x, y;
|
||||||
|
} linear;
|
||||||
|
struct {
|
||||||
|
u32 base;
|
||||||
|
u8 log2_w;
|
||||||
|
} tiled;
|
||||||
|
};
|
||||||
} pxl8_shader_bindings;
|
} pxl8_shader_bindings;
|
||||||
|
|
||||||
typedef struct pxl8_shader_ctx {
|
typedef struct pxl8_shader_ctx {
|
||||||
i32 x, y;
|
i32 x, y;
|
||||||
|
|
||||||
pxl8_vec2 v_uv;
|
pxl8_vec2 v_uv;
|
||||||
pxl8_vec3 v_world;
|
pxl8_vec3 v_world;
|
||||||
pxl8_vec3 v_normal;
|
pxl8_vec3 v_normal;
|
||||||
f32 v_color;
|
f32 v_color;
|
||||||
f32 v_light;
|
f32 v_light;
|
||||||
f32 v_depth;
|
f32 v_depth;
|
||||||
|
|
||||||
const pxl8_shader_bindings* bindings;
|
|
||||||
const pxl8_shader_uniforms* uniforms;
|
|
||||||
|
|
||||||
u32 out_light_color;
|
u32 out_light_color;
|
||||||
} pxl8_shader_ctx;
|
} pxl8_shader_ctx;
|
||||||
|
|
||||||
typedef u8 (*pxl8_shader_fn)(pxl8_shader_ctx* ctx);
|
typedef u8 (*pxl8_shader_fn)(
|
||||||
|
pxl8_shader_ctx* ctx,
|
||||||
|
const pxl8_shader_bindings* bindings,
|
||||||
|
const pxl8_shader_uniforms* uniforms
|
||||||
|
);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,28 +5,10 @@
|
||||||
#include "pxl8_shader.h"
|
#include "pxl8_shader.h"
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline f32 pxl8_abs(f32 x) { return fabsf(x); }
|
|
||||||
static inline f32 pxl8_ceil(f32 x) { return ceilf(x); }
|
|
||||||
static inline f32 pxl8_cos(f32 x) { return cosf(x); }
|
|
||||||
static inline f32 pxl8_exp(f32 x) { return expf(x); }
|
|
||||||
static inline f32 pxl8_floor(f32 x) { return floorf(x); }
|
|
||||||
static inline f32 pxl8_log(f32 x) { return logf(x); }
|
|
||||||
static inline f32 pxl8_pow(f32 x, f32 y) { return powf(x, y); }
|
|
||||||
static inline f32 pxl8_sin(f32 x) { return sinf(x); }
|
|
||||||
static inline f32 pxl8_sqrt(f32 x) { return sqrtf(x); }
|
|
||||||
static inline f32 pxl8_tan(f32 x) { return tanf(x); }
|
|
||||||
|
|
||||||
static inline f32 pxl8_min(f32 a, f32 b) { return a < b ? a : b; }
|
|
||||||
static inline f32 pxl8_max(f32 a, f32 b) { return a > b ? a : b; }
|
|
||||||
static inline f32 pxl8_clamp(f32 x, f32 lo, f32 hi) { return pxl8_min(pxl8_max(x, lo), hi); }
|
|
||||||
static inline f32 pxl8_lerp(f32 a, f32 b, f32 t) { return a + (b - a) * t; }
|
|
||||||
|
|
||||||
static inline pxl8_vec2 pxl8_swizzle_xy(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.y}; }
|
static inline pxl8_vec2 pxl8_swizzle_xy(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.y}; }
|
||||||
static inline pxl8_vec2 pxl8_swizzle_xz(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.z}; }
|
static inline pxl8_vec2 pxl8_swizzle_xz(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.z}; }
|
||||||
static inline pxl8_vec2 pxl8_swizzle_xw(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.w}; }
|
static inline pxl8_vec2 pxl8_swizzle_xw(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.w}; }
|
||||||
|
|
@ -39,27 +21,39 @@ static inline pxl8_vec3 pxl8_swizzle_xyw(pxl8_vec4 v) { return (pxl8_vec3){v.x,
|
||||||
static inline pxl8_vec3 pxl8_swizzle_xzw(pxl8_vec4 v) { return (pxl8_vec3){v.x, v.z, v.w}; }
|
static inline pxl8_vec3 pxl8_swizzle_xzw(pxl8_vec4 v) { return (pxl8_vec3){v.x, v.z, v.w}; }
|
||||||
static inline pxl8_vec3 pxl8_swizzle_yzw(pxl8_vec4 v) { return (pxl8_vec3){v.y, v.z, v.w}; }
|
static inline pxl8_vec3 pxl8_swizzle_yzw(pxl8_vec4 v) { return (pxl8_vec3){v.y, v.z, v.w}; }
|
||||||
|
|
||||||
static inline u8 pxl8_sample_indexed(pxl8_shader_ctx* ctx, pxl8_vec2 uv) {
|
static inline u32 pxl8_tile_addr(u32 u, u32 v, u8 log2_w) {
|
||||||
const u8* tex = ctx->bindings->texture;
|
u32 tile_y = v >> 3;
|
||||||
if (!tex) return 0;
|
u32 tile_x = u >> 3;
|
||||||
u32 w = ctx->bindings->tex_width;
|
u32 local_y = v & 7;
|
||||||
u32 h = ctx->bindings->tex_height;
|
u32 local_x = u & 7;
|
||||||
if (w == 0 || h == 0) return 0;
|
return (tile_y << (log2_w + 3)) | (tile_x << 6) | (local_y << 3) | local_x;
|
||||||
i32 tx = (i32)(uv.x * (f32)w) & (i32)(w - 1);
|
|
||||||
i32 ty = (i32)(uv.y * (f32)h) & (i32)(h - 1);
|
|
||||||
return tex[ty * w + tx];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u8 pxl8_colormap_lookup(pxl8_shader_ctx* ctx, u8 color, u8 light) {
|
static inline u8 pxl8_sample_indexed(const pxl8_shader_bindings* b, pxl8_vec2 uv) {
|
||||||
const u8* cm = ctx->bindings->colormap;
|
if (!b || !b->atlas) return 0;
|
||||||
|
u32 w = b->width;
|
||||||
|
u32 h = b->height;
|
||||||
|
if (w == 0 || h == 0) return 0;
|
||||||
|
u32 u = (u32)(uv.x * (f32)w) & (w - 1);
|
||||||
|
u32 v = (u32)(uv.y * (f32)h) & (h - 1);
|
||||||
|
if (b->use_tiled) {
|
||||||
|
return b->atlas[b->tiled.base + pxl8_tile_addr(u, v, b->tiled.log2_w)];
|
||||||
|
} else {
|
||||||
|
return b->atlas[(b->linear.y + v) * b->linear.stride + b->linear.x + u];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 pxl8_colormap_lookup(const pxl8_shader_bindings* b, u8 color, u8 light) {
|
||||||
|
if (!b) return color;
|
||||||
|
const u8* cm = b->colormap;
|
||||||
if (!cm) return color;
|
if (!cm) return color;
|
||||||
u32 row = light >> 5;
|
u32 row = light >> 5;
|
||||||
return cm[(row << 8) | (u32)color];
|
return cm[(row << 8) | (u32)color];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline f32 pxl8_light_falloff(pxl8_shader_ctx* ctx, u32 light_idx) {
|
static inline f32 pxl8_light_falloff(const pxl8_shader_ctx* ctx, const pxl8_shader_uniforms* u, u32 light_idx) {
|
||||||
if (light_idx >= ctx->uniforms->lights_count) return 0.0f;
|
if (!u || light_idx >= u->lights_count) return 0.0f;
|
||||||
const pxl8_light* light = &ctx->uniforms->lights[light_idx];
|
const pxl8_light* light = &u->lights[light_idx];
|
||||||
f32 dx = light->position.x - ctx->v_world.x;
|
f32 dx = light->position.x - ctx->v_world.x;
|
||||||
f32 dy = light->position.y - ctx->v_world.y;
|
f32 dy = light->position.y - ctx->v_world.y;
|
||||||
f32 dz = light->position.z - ctx->v_world.z;
|
f32 dz = light->position.z - ctx->v_world.z;
|
||||||
|
|
@ -68,9 +62,9 @@ static inline f32 pxl8_light_falloff(pxl8_shader_ctx* ctx, u32 light_idx) {
|
||||||
return 1.0f - dist_sq * light->inv_radius_sq;
|
return 1.0f - dist_sq * light->inv_radius_sq;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 pxl8_light_color(pxl8_shader_ctx* ctx, u32 light_idx) {
|
static inline u32 pxl8_light_color(const pxl8_shader_uniforms* u, u32 light_idx) {
|
||||||
if (light_idx >= ctx->uniforms->lights_count) return 0;
|
if (!u || light_idx >= u->lights_count) return 0;
|
||||||
const pxl8_light* light = &ctx->uniforms->lights[light_idx];
|
const pxl8_light* light = &u->lights[light_idx];
|
||||||
return (u32)light->r | ((u32)light->g << 8) | ((u32)light->b << 16);
|
return (u32)light->r | ((u32)light->g << 8) | ((u32)light->b << 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
extern u8 pxl8_shader_lit(pxl8_shader_ctx* ctx);
|
extern u8 pxl8_shader_lit(pxl8_shader_ctx* ctx, const pxl8_shader_bindings* bindings, const pxl8_shader_uniforms* uniforms);
|
||||||
extern u8 pxl8_shader_unlit(pxl8_shader_ctx* ctx);
|
extern u8 pxl8_shader_unlit(pxl8_shader_ctx* ctx, const pxl8_shader_bindings* bindings, const pxl8_shader_uniforms* uniforms);
|
||||||
|
|
||||||
void pxl8_shader_registry_init(void) {}
|
void pxl8_shader_registry_init(void) {}
|
||||||
void pxl8_shader_registry_reload(void) {}
|
void pxl8_shader_registry_reload(void) {}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
|
#include "pxl8_macros.h"
|
||||||
#include "pxl8_shader.h"
|
#include "pxl8_shader.h"
|
||||||
#include "pxl8_shader_builtins.h"
|
#include "pxl8_shader_builtins.h"
|
||||||
|
|
||||||
u8 pxl8_shader_lit(pxl8_shader_ctx* ctx) {
|
u8 pxl8_shader_lit(
|
||||||
|
pxl8_shader_ctx* ctx,
|
||||||
|
const pxl8_shader_bindings* bindings,
|
||||||
|
const pxl8_shader_uniforms* uniforms
|
||||||
|
) {
|
||||||
u8 tex_idx = 0;
|
u8 tex_idx = 0;
|
||||||
if (ctx->bindings && ctx->bindings->texture) {
|
if (bindings && bindings->atlas) {
|
||||||
tex_idx = pxl8_sample_indexed(ctx, ctx->v_uv);
|
tex_idx = pxl8_sample_indexed(bindings, ctx->v_uv);
|
||||||
if (tex_idx == 0) return 0;
|
if (pxl8_unlikely(tex_idx == 0)) return 0;
|
||||||
} else {
|
} else {
|
||||||
if (ctx->uniforms && ctx->uniforms->dither) {
|
if (uniforms && uniforms->dither) {
|
||||||
tex_idx = pxl8_gfx_dither(ctx->v_color, (u32)ctx->x, (u32)ctx->y);
|
tex_idx = pxl8_gfx_dither(ctx->v_color, (u32)ctx->x, (u32)ctx->y);
|
||||||
} else {
|
} else {
|
||||||
f32 clamped = pxl8_clamp(ctx->v_color, 0.0f, 255.0f);
|
f32 clamped = pxl8_clamp(ctx->v_color, 0.0f, 255.0f);
|
||||||
|
|
@ -17,24 +22,16 @@ u8 pxl8_shader_lit(pxl8_shader_ctx* ctx) {
|
||||||
|
|
||||||
f32 light = ctx->v_light;
|
f32 light = ctx->v_light;
|
||||||
|
|
||||||
const pxl8_shader_uniforms* u = ctx->uniforms;
|
if (uniforms) {
|
||||||
if (u) {
|
f32 ambient = (f32)uniforms->ambient / 255.0f;
|
||||||
f32 ambient = (f32)u->ambient / 255.0f;
|
|
||||||
if (ambient > light) light = ambient;
|
if (ambient > light) light = ambient;
|
||||||
|
|
||||||
if (u->celestial_intensity > 0.0f) {
|
if (uniforms->celestial_intensity > 0.0f) {
|
||||||
f32 dx = u->celestial_dir.x;
|
f32 ndotl = -(ctx->v_normal.x * uniforms->celestial_dir.x +
|
||||||
f32 dy = u->celestial_dir.y;
|
ctx->v_normal.y * uniforms->celestial_dir.y +
|
||||||
f32 dz = u->celestial_dir.z;
|
ctx->v_normal.z * uniforms->celestial_dir.z);
|
||||||
f32 len = pxl8_sqrt(dx * dx + dy * dy + dz * dz);
|
if (ndotl > 0.0f) {
|
||||||
if (len > 0.0001f) {
|
light += ndotl * uniforms->celestial_intensity;
|
||||||
dx /= len;
|
|
||||||
dy /= len;
|
|
||||||
dz /= len;
|
|
||||||
f32 ndotl = -(ctx->v_normal.x * dx + ctx->v_normal.y * dy + ctx->v_normal.z * dz);
|
|
||||||
if (ndotl > 0.0f) {
|
|
||||||
light += ndotl * u->celestial_intensity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,16 +40,15 @@ u8 pxl8_shader_lit(pxl8_shader_ctx* ctx) {
|
||||||
f32 dyn_g = 0.0f;
|
f32 dyn_g = 0.0f;
|
||||||
f32 dyn_b = 0.0f;
|
f32 dyn_b = 0.0f;
|
||||||
|
|
||||||
for (u32 i = 0; i < u->lights_count; i++) {
|
for (u32 i = 0; i < uniforms->lights_count; i++) {
|
||||||
const pxl8_light* l = &u->lights[i];
|
const pxl8_light* l = &uniforms->lights[i];
|
||||||
f32 lx = l->position.x - ctx->v_world.x;
|
f32 lx = l->position.x - ctx->v_world.x;
|
||||||
f32 ly = l->position.y - ctx->v_world.y;
|
f32 ly = l->position.y - ctx->v_world.y;
|
||||||
f32 lz = l->position.z - ctx->v_world.z;
|
f32 lz = l->position.z - ctx->v_world.z;
|
||||||
f32 dist_sq = lx * lx + ly * ly + lz * lz;
|
f32 dist_sq = lx * lx + ly * ly + lz * lz;
|
||||||
if (dist_sq >= l->radius_sq) continue;
|
if (dist_sq >= l->radius_sq) continue;
|
||||||
|
|
||||||
f32 dist = pxl8_sqrt(dist_sq);
|
f32 inv_dist = pxl8_fast_inv_sqrt(dist_sq);
|
||||||
f32 inv_dist = dist > 0.0001f ? (1.0f / dist) : 0.0f;
|
|
||||||
f32 nx = lx * inv_dist;
|
f32 nx = lx * inv_dist;
|
||||||
f32 ny = ly * inv_dist;
|
f32 ny = ly * inv_dist;
|
||||||
f32 nz = lz * inv_dist;
|
f32 nz = lz * inv_dist;
|
||||||
|
|
@ -62,6 +58,10 @@ u8 pxl8_shader_lit(pxl8_shader_ctx* ctx) {
|
||||||
|
|
||||||
f32 falloff = 1.0f - dist_sq * l->inv_radius_sq;
|
f32 falloff = 1.0f - dist_sq * l->inv_radius_sq;
|
||||||
if (falloff <= 0.0f) continue;
|
if (falloff <= 0.0f) continue;
|
||||||
|
if (uniforms->dither && falloff < 0.33f) {
|
||||||
|
f32 threshold = (PXL8_BAYER_4X4[((u32)ctx->y & 3) * 4 + ((u32)ctx->x & 3)] + 0.5f) * (1.0f / 16.0f);
|
||||||
|
if (falloff < threshold * 0.33f) continue;
|
||||||
|
}
|
||||||
|
|
||||||
f32 strength = ((f32)l->intensity / 255.0f) * falloff * ndotl;
|
f32 strength = ((f32)l->intensity / 255.0f) * falloff * ndotl;
|
||||||
if (strength <= 0.0f) continue;
|
if (strength <= 0.0f) continue;
|
||||||
|
|
@ -73,7 +73,7 @@ u8 pxl8_shader_lit(pxl8_shader_ctx* ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dyn_strength > 0.0f) {
|
if (dyn_strength > 0.0f) {
|
||||||
f32 inv = 1.0f / dyn_strength;
|
f32 inv = pxl8_fast_rcp(dyn_strength);
|
||||||
u8 r = (u8)pxl8_clamp(dyn_r * inv, 0.0f, 255.0f);
|
u8 r = (u8)pxl8_clamp(dyn_r * inv, 0.0f, 255.0f);
|
||||||
u8 g = (u8)pxl8_clamp(dyn_g * inv, 0.0f, 255.0f);
|
u8 g = (u8)pxl8_clamp(dyn_g * inv, 0.0f, 255.0f);
|
||||||
u8 b = (u8)pxl8_clamp(dyn_b * inv, 0.0f, 255.0f);
|
u8 b = (u8)pxl8_clamp(dyn_b * inv, 0.0f, 255.0f);
|
||||||
|
|
@ -88,16 +88,16 @@ u8 pxl8_shader_lit(pxl8_shader_ctx* ctx) {
|
||||||
|
|
||||||
f32 light_f = light * 255.0f;
|
f32 light_f = light * 255.0f;
|
||||||
u8 light_u8 = (u8)light_f;
|
u8 light_u8 = (u8)light_f;
|
||||||
if (ctx->uniforms && ctx->uniforms->dither) {
|
if (uniforms && uniforms->dither) {
|
||||||
light_u8 = pxl8_gfx_dither(light_f, (u32)ctx->x, (u32)ctx->y);
|
light_u8 = pxl8_gfx_dither(light_f, (u32)ctx->x, (u32)ctx->y);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 shaded = pxl8_colormap_lookup(ctx, tex_idx, light_u8);
|
u8 shaded = pxl8_colormap_lookup(bindings, tex_idx, light_u8);
|
||||||
|
|
||||||
if (ctx->uniforms && ctx->uniforms->emissive) {
|
if (uniforms && uniforms->emissive) {
|
||||||
u32 rgb = 0x00FFFFFF;
|
u32 rgb = 0x00FFFFFF;
|
||||||
if (ctx->bindings && ctx->bindings->palette) {
|
if (bindings && bindings->palette) {
|
||||||
rgb = ctx->bindings->palette[tex_idx] & 0x00FFFFFF;
|
rgb = bindings->palette[tex_idx] & 0x00FFFFFF;
|
||||||
}
|
}
|
||||||
pxl8_set_light_tint(ctx, rgb, 1.0f);
|
pxl8_set_light_tint(ctx, rgb, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
#include "pxl8_shader.h"
|
#include "pxl8_shader.h"
|
||||||
#include "pxl8_shader_builtins.h"
|
#include "pxl8_shader_builtins.h"
|
||||||
|
|
||||||
u8 pxl8_shader_unlit(pxl8_shader_ctx* ctx) {
|
u8 pxl8_shader_unlit(
|
||||||
|
pxl8_shader_ctx* ctx,
|
||||||
|
const pxl8_shader_bindings* bindings,
|
||||||
|
const pxl8_shader_uniforms* uniforms
|
||||||
|
) {
|
||||||
u8 tex_idx = 0;
|
u8 tex_idx = 0;
|
||||||
if (ctx->bindings && ctx->bindings->texture) {
|
if (bindings && bindings->atlas) {
|
||||||
tex_idx = pxl8_sample_indexed(ctx, ctx->v_uv);
|
tex_idx = pxl8_sample_indexed(bindings, ctx->v_uv);
|
||||||
if (tex_idx == 0) return 0;
|
if (tex_idx == 0) return 0;
|
||||||
} else {
|
} else {
|
||||||
if (ctx->uniforms && ctx->uniforms->dither) {
|
if (uniforms && uniforms->dither) {
|
||||||
tex_idx = pxl8_gfx_dither(ctx->v_color, (u32)ctx->x, (u32)ctx->y);
|
tex_idx = pxl8_gfx_dither(ctx->v_color, (u32)ctx->x, (u32)ctx->y);
|
||||||
} else {
|
} else {
|
||||||
f32 clamped = pxl8_clamp(ctx->v_color, 0.0f, 255.0f);
|
f32 clamped = pxl8_clamp(ctx->v_color, 0.0f, 255.0f);
|
||||||
|
|
@ -15,10 +19,10 @@ u8 pxl8_shader_unlit(pxl8_shader_ctx* ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->uniforms && ctx->uniforms->emissive) {
|
if (uniforms && uniforms->emissive) {
|
||||||
u32 rgb = 0x00FFFFFF;
|
u32 rgb = 0x00FFFFFF;
|
||||||
if (ctx->bindings && ctx->bindings->palette) {
|
if (bindings && bindings->palette) {
|
||||||
rgb = ctx->bindings->palette[tex_idx] & 0x00FFFFFF;
|
rgb = bindings->palette[tex_idx] & 0x00FFFFFF;
|
||||||
}
|
}
|
||||||
pxl8_set_light_tint(ctx, rgb, 1.0f);
|
pxl8_set_light_tint(ctx, rgb, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,8 @@ pxl8.draw_line_3d = gfx.draw_line_3d
|
||||||
pxl8.draw_mesh = gfx.draw_mesh
|
pxl8.draw_mesh = gfx.draw_mesh
|
||||||
pxl8.end_frame_3d = gfx.end_frame_3d
|
pxl8.end_frame_3d = gfx.end_frame_3d
|
||||||
pxl8.project_points = gfx.project_points
|
pxl8.project_points = gfx.project_points
|
||||||
|
pxl8.set_wireframe = gfx.set_wireframe
|
||||||
|
pxl8.get_wireframe = gfx.get_wireframe
|
||||||
|
|
||||||
pxl8.Lights = effects.Lights
|
pxl8.Lights = effects.Lights
|
||||||
pxl8.create_lights = effects.Lights.new
|
pxl8.create_lights = effects.Lights.new
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,6 @@ function gfx.draw_mesh(mesh, opts)
|
||||||
emissive = opts.emissive or false,
|
emissive = opts.emissive or false,
|
||||||
per_pixel = opts.per_pixel or false,
|
per_pixel = opts.per_pixel or false,
|
||||||
texture_id = opts.texture or 0xFFFFFFFF,
|
texture_id = opts.texture or 0xFFFFFFFF,
|
||||||
wireframe = opts.wireframe or false,
|
|
||||||
})
|
})
|
||||||
C.pxl8_3d_draw_mesh(core.gfx, mesh._ptr, model, material)
|
C.pxl8_3d_draw_mesh(core.gfx, mesh._ptr, model, material)
|
||||||
end
|
end
|
||||||
|
|
@ -272,15 +271,22 @@ function gfx.begin_frame_3d(camera, lights, uniforms)
|
||||||
u.fog_density = uniforms.fog_density or 0.0
|
u.fog_density = uniforms.fog_density or 0.0
|
||||||
u.time = uniforms.time or 0.0
|
u.time = uniforms.time or 0.0
|
||||||
|
|
||||||
|
local cx, cy, cz
|
||||||
if uniforms.celestial_dir then
|
if uniforms.celestial_dir then
|
||||||
u.celestial_dir.x = uniforms.celestial_dir[1] or 0
|
cx = uniforms.celestial_dir[1] or 0
|
||||||
u.celestial_dir.y = uniforms.celestial_dir[2] or -1
|
cy = uniforms.celestial_dir[2] or -1
|
||||||
u.celestial_dir.z = uniforms.celestial_dir[3] or 0
|
cz = uniforms.celestial_dir[3] or 0
|
||||||
else
|
else
|
||||||
u.celestial_dir.x = 0
|
cx, cy, cz = 0, -1, 0
|
||||||
u.celestial_dir.y = -1
|
|
||||||
u.celestial_dir.z = 0
|
|
||||||
end
|
end
|
||||||
|
local len = math.sqrt(cx * cx + cy * cy + cz * cz)
|
||||||
|
if len > 0.0001 then
|
||||||
|
local inv = 1.0 / len
|
||||||
|
cx, cy, cz = cx * inv, cy * inv, cz * inv
|
||||||
|
end
|
||||||
|
u.celestial_dir.x = cx
|
||||||
|
u.celestial_dir.y = cy
|
||||||
|
u.celestial_dir.z = cz
|
||||||
u.celestial_intensity = uniforms.celestial_intensity or 0.0
|
u.celestial_intensity = uniforms.celestial_intensity or 0.0
|
||||||
|
|
||||||
local lights_ptr = lights and lights._ptr or nil
|
local lights_ptr = lights and lights._ptr or nil
|
||||||
|
|
@ -313,6 +319,14 @@ function gfx.create_vec3_array(count)
|
||||||
return ffi.new("pxl8_vec3[?]", count)
|
return ffi.new("pxl8_vec3[?]", count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function gfx.set_wireframe(enabled)
|
||||||
|
C.pxl8_gfx_set_wireframe(core.gfx, enabled)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.get_wireframe()
|
||||||
|
return C.pxl8_gfx_get_wireframe(core.gfx)
|
||||||
|
end
|
||||||
|
|
||||||
local Material = {}
|
local Material = {}
|
||||||
Material.__index = Material
|
Material.__index = Material
|
||||||
|
|
||||||
|
|
@ -327,7 +341,6 @@ function Material.new(opts)
|
||||||
emissive = opts.emissive or false,
|
emissive = opts.emissive or false,
|
||||||
per_pixel = opts.per_pixel or false,
|
per_pixel = opts.per_pixel or false,
|
||||||
texture_id = opts.texture or 0xFFFFFFFF,
|
texture_id = opts.texture or 0xFFFFFFFF,
|
||||||
wireframe = opts.wireframe or false,
|
|
||||||
})
|
})
|
||||||
return setmetatable({ _ptr = mat }, Material)
|
return setmetatable({ _ptr = mat }, Material)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -117,10 +117,6 @@ function World:set_sim_distance(distance)
|
||||||
C.pxl8_world_set_sim_distance(self._ptr, distance)
|
C.pxl8_world_set_sim_distance(self._ptr, distance)
|
||||||
end
|
end
|
||||||
|
|
||||||
function World:set_wireframe(enabled)
|
|
||||||
C.pxl8_world_set_wireframe(self._ptr, enabled)
|
|
||||||
end
|
|
||||||
|
|
||||||
function World:sweep(from_x, from_y, from_z, to_x, to_y, to_z, radius)
|
function World:sweep(from_x, from_y, from_z, to_x, to_y, to_z, radius)
|
||||||
local from = ffi.new("pxl8_vec3", {x = from_x, y = from_y, z = from_z})
|
local from = ffi.new("pxl8_vec3", {x = from_x, y = from_y, z = from_z})
|
||||||
local to = ffi.new("pxl8_vec3", {x = to_x, y = to_y, z = to_z})
|
local to = ffi.new("pxl8_vec3", {x = to_x, y = to_y, z = to_z})
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,50 @@
|
||||||
#include "pxl8_math.h"
|
#include "pxl8_math.h"
|
||||||
|
|
||||||
|
f32 pxl8_fast_inv_sqrt(f32 x) {
|
||||||
|
#if defined(PXL8_NO_SIMD)
|
||||||
|
f32 half = 0.5f * x;
|
||||||
|
i32 i = *(i32*)&x;
|
||||||
|
i = 0x5f3759df - (i >> 1);
|
||||||
|
x = *(f32*)&i;
|
||||||
|
x = x * (1.5f - half * x * x);
|
||||||
|
return x;
|
||||||
|
#elif defined(__x86_64__) || defined(_M_X64)
|
||||||
|
__m128 v = _mm_set_ss(x);
|
||||||
|
v = _mm_rsqrt_ss(v);
|
||||||
|
return _mm_cvtss_f32(v);
|
||||||
|
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||||
|
float32x2_t v = vdup_n_f32(x);
|
||||||
|
float32x2_t est = vrsqrte_f32(v);
|
||||||
|
est = vmul_f32(est, vrsqrts_f32(vmul_f32(v, est), est));
|
||||||
|
return vget_lane_f32(est, 0);
|
||||||
|
#else
|
||||||
|
f32 half = 0.5f * x;
|
||||||
|
i32 i = *(i32*)&x;
|
||||||
|
i = 0x5f3759df - (i >> 1);
|
||||||
|
x = *(f32*)&i;
|
||||||
|
x = x * (1.5f - half * x * x);
|
||||||
|
return x;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 pxl8_fast_rcp(f32 x) {
|
||||||
|
#if defined(PXL8_NO_SIMD)
|
||||||
|
return 1.0f / x;
|
||||||
|
#elif defined(__x86_64__) || defined(_M_X64)
|
||||||
|
__m128 v = _mm_set_ss(x);
|
||||||
|
__m128 rcp = _mm_rcp_ss(v);
|
||||||
|
rcp = _mm_add_ss(rcp, _mm_mul_ss(rcp, _mm_sub_ss(_mm_set_ss(1.0f), _mm_mul_ss(v, rcp))));
|
||||||
|
return _mm_cvtss_f32(rcp);
|
||||||
|
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||||
|
float32x2_t v = vdup_n_f32(x);
|
||||||
|
float32x2_t est = vrecpe_f32(v);
|
||||||
|
est = vmul_f32(est, vrecps_f32(v, est));
|
||||||
|
return vget_lane_f32(est, 0);
|
||||||
|
#else
|
||||||
|
return 1.0f / x;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
u32 pxl8_hash32(u32 x) {
|
u32 pxl8_hash32(u32 x) {
|
||||||
x ^= x >> 16;
|
x ^= x >> 16;
|
||||||
x *= 0x85EBCA6Bu;
|
x *= 0x85EBCA6Bu;
|
||||||
|
|
@ -18,10 +63,6 @@ u64 pxl8_hash64(u64 x) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 pxl8_lerp_f32(f32 a, f32 b, f32 t) {
|
|
||||||
return a + (b - a) * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
f32 pxl8_smoothstep(f32 t) {
|
f32 pxl8_smoothstep(f32 t) {
|
||||||
return t * t * (3.0f - 2.0f * t);
|
return t * t * (3.0f - 2.0f * t);
|
||||||
}
|
}
|
||||||
|
|
@ -56,11 +97,11 @@ 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_sq = pxl8_vec2_dot(v, v);
|
||||||
|
|
||||||
if (len < 1e-6f) return (pxl8_vec2){0};
|
if (len_sq < 1e-12f) return (pxl8_vec2){0};
|
||||||
|
|
||||||
return pxl8_vec2_scale(v, 1.0f / len);
|
return pxl8_vec2_scale(v, pxl8_fast_inv_sqrt(len_sq));
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b) {
|
pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b) {
|
||||||
|
|
@ -112,11 +153,11 @@ pxl8_vec3 pxl8_vec3_lerp(pxl8_vec3 a, pxl8_vec3 b, f32 t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
|
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
|
||||||
f32 len = pxl8_vec3_length(v);
|
f32 len_sq = pxl8_vec3_dot(v, v);
|
||||||
|
|
||||||
if (len < 1e-6f) return (pxl8_vec3){0};
|
if (len_sq < 1e-12f) return (pxl8_vec3){0};
|
||||||
|
|
||||||
return pxl8_vec3_scale(v, 1.0f / len);
|
return pxl8_vec3_scale(v, pxl8_fast_inv_sqrt(len_sq));
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_identity(void) {
|
pxl8_mat4 pxl8_mat4_identity(void) {
|
||||||
|
|
|
||||||
|
|
@ -15,50 +15,25 @@
|
||||||
#define PXL8_PI 3.14159265358979323846f
|
#define PXL8_PI 3.14159265358979323846f
|
||||||
#define PXL8_TAU (PXL8_PI * 2.0f)
|
#define PXL8_TAU (PXL8_PI * 2.0f)
|
||||||
|
|
||||||
static inline f32 pxl8_fast_inv_sqrt(f32 x) {
|
#define pxl8_min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
#if defined(PXL8_NO_SIMD)
|
#define pxl8_max(a, b) ((a) > (b) ? (a) : (b))
|
||||||
f32 half = 0.5f * x;
|
#define pxl8_clamp(x, lo, hi) ((x) < (lo) ? (lo) : ((x) > (hi) ? (hi) : (x)))
|
||||||
i32 i = *(i32*)&x;
|
#define pxl8_clamp_byte(x) pxl8_clamp(x, 0, 255)
|
||||||
i = 0x5f3759df - (i >> 1);
|
|
||||||
x = *(f32*)&i;
|
|
||||||
x = x * (1.5f - half * x * x);
|
|
||||||
return x;
|
|
||||||
#elif defined(__x86_64__) || defined(_M_X64)
|
|
||||||
__m128 v = _mm_set_ss(x);
|
|
||||||
v = _mm_rsqrt_ss(v);
|
|
||||||
return _mm_cvtss_f32(v);
|
|
||||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
|
||||||
float32x2_t v = vdup_n_f32(x);
|
|
||||||
float32x2_t est = vrsqrte_f32(v);
|
|
||||||
est = vmul_f32(est, vrsqrts_f32(vmul_f32(v, est), est));
|
|
||||||
return vget_lane_f32(est, 0);
|
|
||||||
#else
|
|
||||||
f32 half = 0.5f * x;
|
|
||||||
i32 i = *(i32*)&x;
|
|
||||||
i = 0x5f3759df - (i >> 1);
|
|
||||||
x = *(f32*)&i;
|
|
||||||
x = x * (1.5f - half * x * x);
|
|
||||||
return x;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline f32 pxl8_fast_rcp(f32 x) {
|
#define pxl8_abs(x) fabsf(x)
|
||||||
#if defined(PXL8_NO_SIMD)
|
#define pxl8_ceil(x) ceilf(x)
|
||||||
return 1.0f / x;
|
#define pxl8_floor(x) floorf(x)
|
||||||
#elif defined(__x86_64__) || defined(_M_X64)
|
#define pxl8_sqrt(x) sqrtf(x)
|
||||||
__m128 v = _mm_set_ss(x);
|
#define pxl8_sin(x) sinf(x)
|
||||||
__m128 rcp = _mm_rcp_ss(v);
|
#define pxl8_cos(x) cosf(x)
|
||||||
rcp = _mm_add_ss(rcp, _mm_mul_ss(rcp, _mm_sub_ss(_mm_set_ss(1.0f), _mm_mul_ss(v, rcp))));
|
#define pxl8_tan(x) tanf(x)
|
||||||
return _mm_cvtss_f32(rcp);
|
#define pxl8_exp(x) expf(x)
|
||||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
#define pxl8_log(x) logf(x)
|
||||||
float32x2_t v = vdup_n_f32(x);
|
#define pxl8_pow(x, y) powf(x, y)
|
||||||
float32x2_t est = vrecpe_f32(v);
|
#define pxl8_lerp(a, b, t) ((a) + ((b) - (a)) * (t))
|
||||||
est = vmul_f32(est, vrecps_f32(v, est));
|
|
||||||
return vget_lane_f32(est, 0);
|
f32 pxl8_fast_inv_sqrt(f32 x);
|
||||||
#else
|
f32 pxl8_fast_rcp(f32 x);
|
||||||
return 1.0f / x;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef union pxl8_vec2 {
|
typedef union pxl8_vec2 {
|
||||||
struct { f32 x, y; };
|
struct { f32 x, y; };
|
||||||
|
|
@ -66,15 +41,17 @@ typedef union pxl8_vec2 {
|
||||||
f32 v[2];
|
f32 v[2];
|
||||||
} pxl8_vec2;
|
} pxl8_vec2;
|
||||||
|
|
||||||
typedef struct pxl8_vec3 {
|
typedef union pxl8_vec3 {
|
||||||
f32 x, y, z;
|
struct { f32 x, y, z; };
|
||||||
|
f32 v[3];
|
||||||
} pxl8_vec3;
|
} pxl8_vec3;
|
||||||
|
|
||||||
typedef struct pxl8_vec4 {
|
typedef union pxl8_vec4 {
|
||||||
f32 x, y, z, w;
|
struct { f32 x, y, z, w; };
|
||||||
|
f32 v[4];
|
||||||
} pxl8_vec4;
|
} pxl8_vec4;
|
||||||
|
|
||||||
typedef struct pxl8_mat4 {
|
typedef union pxl8_mat4 {
|
||||||
f32 m[16];
|
f32 m[16];
|
||||||
} pxl8_mat4;
|
} pxl8_mat4;
|
||||||
|
|
||||||
|
|
@ -108,7 +85,6 @@ extern "C" {
|
||||||
u32 pxl8_hash32(u32 x);
|
u32 pxl8_hash32(u32 x);
|
||||||
u64 pxl8_hash64(u64 x);
|
u64 pxl8_hash64(u64 x);
|
||||||
|
|
||||||
f32 pxl8_lerp_f32(f32 a, f32 b, f32 t);
|
|
||||||
f32 pxl8_smoothstep(f32 t);
|
f32 pxl8_smoothstep(f32 t);
|
||||||
|
|
||||||
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b);
|
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b);
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ f32 pxl8_value_noise(f32 x, f32 z, u64 seed) {
|
||||||
f32 c01 = pxl8_noise2d(x0, z1, seed);
|
f32 c01 = pxl8_noise2d(x0, z1, seed);
|
||||||
f32 c11 = pxl8_noise2d(x1, z1, seed);
|
f32 c11 = pxl8_noise2d(x1, z1, seed);
|
||||||
|
|
||||||
f32 a = pxl8_lerp_f32(c00, c10, tx);
|
f32 a = pxl8_lerp(c00, c10, tx);
|
||||||
f32 b = pxl8_lerp_f32(c01, c11, tx);
|
f32 b = pxl8_lerp(c01, c11, tx);
|
||||||
return pxl8_lerp_f32(a, b, tz);
|
return pxl8_lerp(a, b, tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 pxl8_value_noise3d(f32 x, f32 y, f32 z, u64 seed) {
|
f32 pxl8_value_noise3d(f32 x, f32 y, f32 z, u64 seed) {
|
||||||
|
|
@ -50,15 +50,15 @@ f32 pxl8_value_noise3d(f32 x, f32 y, f32 z, u64 seed) {
|
||||||
f32 c011 = pxl8_noise3d(x0, y1, z1, seed);
|
f32 c011 = pxl8_noise3d(x0, y1, z1, seed);
|
||||||
f32 c111 = pxl8_noise3d(x1, y1, z1, seed);
|
f32 c111 = pxl8_noise3d(x1, y1, z1, seed);
|
||||||
|
|
||||||
f32 a00 = pxl8_lerp_f32(c000, c100, tx);
|
f32 a00 = pxl8_lerp(c000, c100, tx);
|
||||||
f32 a10 = pxl8_lerp_f32(c010, c110, tx);
|
f32 a10 = pxl8_lerp(c010, c110, tx);
|
||||||
f32 a01 = pxl8_lerp_f32(c001, c101, tx);
|
f32 a01 = pxl8_lerp(c001, c101, tx);
|
||||||
f32 a11 = pxl8_lerp_f32(c011, c111, tx);
|
f32 a11 = pxl8_lerp(c011, c111, tx);
|
||||||
|
|
||||||
f32 b0 = pxl8_lerp_f32(a00, a10, ty);
|
f32 b0 = pxl8_lerp(a00, a10, ty);
|
||||||
f32 b1 = pxl8_lerp_f32(a01, a11, ty);
|
f32 b1 = pxl8_lerp(a01, a11, ty);
|
||||||
|
|
||||||
return pxl8_lerp_f32(b0, b1, tz);
|
return pxl8_lerp(b0, b1, tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 pxl8_fbm(f32 x, f32 z, u64 seed, u32 octaves) {
|
f32 pxl8_fbm(f32 x, f32 z, u64 seed, u32 octaves) {
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size);\n"
|
"void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size);\n"
|
||||||
"void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx);\n"
|
"void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx);\n"
|
||||||
"i32 pxl8_gfx_get_width(pxl8_gfx* ctx);\n"
|
"i32 pxl8_gfx_get_width(pxl8_gfx* ctx);\n"
|
||||||
|
"void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled);\n"
|
||||||
|
"bool pxl8_gfx_get_wireframe(const pxl8_gfx* gfx);\n"
|
||||||
"void pxl8_2d_circle(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
"void pxl8_2d_circle(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||||
"void pxl8_2d_circle_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
"void pxl8_2d_circle_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||||
"void pxl8_2d_clear(pxl8_gfx* ctx, u32 color);\n"
|
"void pxl8_2d_clear(pxl8_gfx* ctx, u32 color);\n"
|
||||||
|
|
@ -272,7 +274,6 @@ static const char* pxl8_ffi_cdefs =
|
||||||
" bool dynamic_lighting;\n"
|
" bool dynamic_lighting;\n"
|
||||||
" bool emissive;\n"
|
" bool emissive;\n"
|
||||||
" bool per_pixel;\n"
|
" bool per_pixel;\n"
|
||||||
" bool wireframe;\n"
|
|
||||||
"} pxl8_gfx_material;\n"
|
"} pxl8_gfx_material;\n"
|
||||||
"\n"
|
"\n"
|
||||||
"typedef struct pxl8_vertex {\n"
|
"typedef struct pxl8_vertex {\n"
|
||||||
|
|
@ -422,7 +423,6 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"pxl8_ray pxl8_world_sweep(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\n"
|
"pxl8_ray pxl8_world_sweep(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\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_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);\n"
|
"void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);\n"
|
||||||
"void pxl8_world_set_wireframe(pxl8_world* world, bool enabled);\n"
|
|
||||||
"i32 pxl8_world_get_render_distance(const pxl8_world* world);\n"
|
"i32 pxl8_world_get_render_distance(const pxl8_world* world);\n"
|
||||||
"void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);\n"
|
"void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);\n"
|
||||||
"i32 pxl8_world_get_sim_distance(const pxl8_world* world);\n"
|
"i32 pxl8_world_get_sim_distance(const pxl8_world* world);\n"
|
||||||
|
|
|
||||||
|
|
@ -569,8 +569,3 @@ pxl8_vxl_render_state* pxl8_vxl_render_state_create(void) {
|
||||||
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state) {
|
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state) {
|
||||||
pxl8_free(state);
|
pxl8_free(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_vxl_set_wireframe(pxl8_vxl_render_state* state, bool wireframe) {
|
|
||||||
if (!state) return;
|
|
||||||
state->wireframe = wireframe;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,11 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct pxl8_vxl_render_state {
|
typedef struct pxl8_vxl_render_state {
|
||||||
bool wireframe;
|
u8 _unused;
|
||||||
} pxl8_vxl_render_state;
|
} pxl8_vxl_render_state;
|
||||||
|
|
||||||
pxl8_vxl_render_state* pxl8_vxl_render_state_create(void);
|
pxl8_vxl_render_state* pxl8_vxl_render_state_create(void);
|
||||||
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state);
|
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state);
|
||||||
void pxl8_vxl_set_wireframe(pxl8_vxl_render_state* state, bool wireframe);
|
|
||||||
|
|
||||||
typedef struct pxl8_vxl_mesh_config {
|
typedef struct pxl8_vxl_mesh_config {
|
||||||
bool ambient_occlusion;
|
bool ambient_occlusion;
|
||||||
|
|
|
||||||
|
|
@ -332,12 +332,10 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||||
|
|
||||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||||
|
|
||||||
bool wireframe = world->vxl_render_state && world->vxl_render_state->wireframe;
|
|
||||||
pxl8_gfx_material mat = {
|
pxl8_gfx_material mat = {
|
||||||
.texture_id = 0,
|
.texture_id = 0,
|
||||||
.dynamic_lighting = true,
|
.dynamic_lighting = true,
|
||||||
.alpha = 255,
|
.alpha = 255,
|
||||||
.wireframe = wireframe,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
i32 r = world->render_distance;
|
i32 r = world->render_distance;
|
||||||
|
|
@ -426,18 +424,6 @@ void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_
|
||||||
pxl8_bsp_set_material(world->bsp_render_state, material_id, material);
|
pxl8_bsp_set_material(world->bsp_render_state, material_id, material);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled) {
|
|
||||||
if (!world) return;
|
|
||||||
|
|
||||||
ensure_bsp_render_state(world);
|
|
||||||
if (world->bsp_render_state) {
|
|
||||||
pxl8_bsp_set_wireframe(world->bsp_render_state, enabled);
|
|
||||||
}
|
|
||||||
if (world->vxl_render_state) {
|
|
||||||
pxl8_vxl_set_wireframe(world->vxl_render_state, enabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i32 pxl8_world_get_render_distance(const pxl8_world* world) {
|
i32 pxl8_world_get_render_distance(const pxl8_world* world) {
|
||||||
if (!world) return 3;
|
if (!world) return 3;
|
||||||
return world->render_distance;
|
return world->render_distance;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
||||||
void pxl8_world_sync(pxl8_world* world, pxl8_net* net);
|
void pxl8_world_sync(pxl8_world* world, pxl8_net* net);
|
||||||
|
|
||||||
void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);
|
void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);
|
||||||
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled);
|
|
||||||
|
|
||||||
i32 pxl8_world_get_render_distance(const pxl8_world* world);
|
i32 pxl8_world_get_render_distance(const pxl8_world* world);
|
||||||
void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);
|
void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue