improve sw renderer

This commit is contained in:
asrael 2026-01-21 23:19:50 -06:00
parent 415d424057
commit 39ee0fefb7
89 changed files with 9380 additions and 2307 deletions

View file

@ -1,13 +1,15 @@
local anim = require("pxl8.anim")
local bytes = require("pxl8.bytes")
local core = require("pxl8.core")
local effects = require("pxl8.effects")
local gfx2d = require("pxl8.gfx2d")
local gfx3d = require("pxl8.gfx3d")
local gui = require("pxl8.gui")
local input = require("pxl8.input")
local math3d = require("pxl8.math")
local math = require("pxl8.math")
local net = require("pxl8.net")
local particles = require("pxl8.particles")
local procgen = require("pxl8.procgen")
local sfx = require("pxl8.sfx")
local tilemap = require("pxl8.tilemap")
local transition = require("pxl8.transition")
@ -27,16 +29,20 @@ pxl8.debug = core.debug
pxl8.trace = core.trace
pxl8.quit = core.quit
pxl8.rng_seed = core.rng_seed
pxl8.rng_next = core.rng_next
pxl8.hash32 = math.hash32
pxl8.rng_f32 = core.rng_f32
pxl8.rng_next = core.rng_next
pxl8.rng_range = core.rng_range
pxl8.rng_seed = core.rng_seed
pxl8.find_color = core.find_color
pxl8.palette_color = core.palette_color
pxl8.palette_index = core.palette_index
pxl8.ramp_index = core.ramp_index
pxl8.set_colormap = core.set_colormap
pxl8.set_palette = core.set_palette
pxl8.set_palette_rgb = core.set_palette_rgb
pxl8.update_palette_deps = core.update_palette_deps
pxl8.clear = gfx2d.clear
pxl8.pixel = gfx2d.pixel
@ -78,18 +84,28 @@ pxl8.Anim = anim.Anim
pxl8.create_anim = anim.Anim.new
pxl8.create_anim_from_ase = anim.Anim.from_ase
pxl8.bounds = math3d.bounds
pxl8.bounds = math.bounds
pxl8.Camera3D = gfx3d.Camera3D
pxl8.create_camera_3d = gfx3d.Camera3D.new
pxl8.Mesh = gfx3d.Mesh
pxl8.begin_frame_3d = gfx3d.begin_frame
pxl8.clear_3d = gfx3d.clear
pxl8.clear_depth = gfx3d.clear_depth
pxl8.create_camera_3d = gfx3d.Camera3D.new
pxl8.create_mesh = gfx3d.Mesh.new
pxl8.create_vec3_array = gfx3d.create_vec3_array
pxl8.draw_line_3d = gfx3d.draw_line
pxl8.draw_mesh = gfx3d.draw_mesh
pxl8.end_frame_3d = gfx3d.end_frame
pxl8.Mesh = gfx3d.Mesh
pxl8.create_mesh = gfx3d.Mesh.new
pxl8.project_points = gfx3d.project_points
pxl8.GLOW_CIRCLE = effects.GLOW_CIRCLE
pxl8.GLOW_DIAMOND = effects.GLOW_DIAMOND
pxl8.GLOW_SHAFT = effects.GLOW_SHAFT
pxl8.Glows = effects.Glows
pxl8.create_glows = effects.Glows.new
pxl8.Lights = effects.Lights
pxl8.create_lights = effects.Lights.new
pxl8.Compressor = sfx.Compressor
pxl8.create_compressor = sfx.Compressor.new
@ -103,16 +119,18 @@ pxl8.create_gui = gui.Gui.new
pxl8.gui_label = gui.label
pxl8.gui_window = gui.window
pxl8.mat4_identity = math3d.mat4_identity
pxl8.mat4_lookat = math3d.mat4_lookat
pxl8.mat4_multiply = math3d.mat4_multiply
pxl8.mat4_ortho = math3d.mat4_ortho
pxl8.mat4_perspective = math3d.mat4_perspective
pxl8.mat4_rotate_x = math3d.mat4_rotate_x
pxl8.mat4_rotate_y = math3d.mat4_rotate_y
pxl8.mat4_rotate_z = math3d.mat4_rotate_z
pxl8.mat4_scale = math3d.mat4_scale
pxl8.mat4_translate = math3d.mat4_translate
pxl8.mat4_identity = math.mat4_identity
pxl8.mat4_lookat = math.mat4_lookat
pxl8.mat4_multiply = math.mat4_multiply
pxl8.mat4_multiply_vec3 = math.mat4_multiply_vec3
pxl8.mat4_multiply_vec4 = math.mat4_multiply_vec4
pxl8.mat4_orthographic = math.mat4_orthographic
pxl8.mat4_perspective = math.mat4_perspective
pxl8.mat4_rotate_x = math.mat4_rotate_x
pxl8.mat4_rotate_y = math.mat4_rotate_y
pxl8.mat4_rotate_z = math.mat4_rotate_z
pxl8.mat4_scale = math.mat4_scale
pxl8.mat4_translate = math.mat4_translate
pxl8.Net = net.Net
pxl8.create_net = net.Net.new
@ -141,9 +159,10 @@ pxl8.pack_u64_le = bytes.pack_u64_le
pxl8.Particles = particles.Particles
pxl8.create_particles = particles.Particles.new
pxl8.Graph = procgen.Graph
pxl8.create_graph = procgen.create_graph
pxl8.PROCGEN_ROOMS = world.PROCGEN_ROOMS
pxl8.PROCGEN_TERRAIN = world.PROCGEN_TERRAIN
pxl8.procgen_tex = world.procgen_tex
pxl8.SfxContext = sfx.SfxContext
pxl8.SfxNode = sfx.SfxNode

View file

@ -20,27 +20,40 @@ function core.get_fps()
end
function core.palette_color(index)
local pal = C.pxl8_gfx_get_palette(core.gfx)
local pal = C.pxl8_gfx_palette(core.gfx)
if pal == nil then return 0 end
return C.pxl8_palette_color(pal, index)
end
function core.palette_index(color)
local pal = C.pxl8_gfx_get_palette(core.gfx)
local pal = C.pxl8_gfx_palette(core.gfx)
if pal == nil then return -1 end
return C.pxl8_palette_index(pal, color)
end
function core.set_palette_rgb(index, r, g, b)
local pal = C.pxl8_gfx_get_palette(core.gfx)
local pal = C.pxl8_gfx_palette(core.gfx)
if pal == nil then return end
C.pxl8_palette_set_rgb(pal, index, r, g, b)
end
function core.set_palette(colors, count)
C.pxl8_gfx_set_palette_colors(core.gfx, colors, count)
end
function core.set_colormap(data, size)
local cm = C.pxl8_gfx_colormap(core.gfx)
if cm == nil then return end
C.pxl8_set_colormap(cm, data, size)
end
function core.update_palette_deps()
C.pxl8_gfx_blend_tables_update(core.gfx)
C.pxl8_gfx_colormap_update(core.gfx)
end
function core.ramp_index(position)
local pal = C.pxl8_gfx_get_palette(core.gfx)
local pal = C.pxl8_gfx_palette(core.gfx)
if pal == nil then return 0 end
return C.pxl8_palette_ramp_index(pal, position)
end

View file

@ -8,25 +8,72 @@ effects.GLOW_CIRCLE = 0
effects.GLOW_DIAMOND = 1
effects.GLOW_SHAFT = 2
function effects.glows(glows)
if not glows or #glows == 0 then return end
local Glows = {}
Glows.__index = Glows
local count = #glows
local glow_array = ffi.new("pxl8_glow_source[?]", count)
for i, g in ipairs(glows) do
local idx = i - 1
glow_array[idx].x = g.x or 0
glow_array[idx].y = g.y or 0
glow_array[idx].radius = g.radius or 8
glow_array[idx].intensity = g.intensity or 255
glow_array[idx].color = g.color or 15
glow_array[idx].depth = g.depth or 0xFFFF
glow_array[idx].height = g.height or 0
glow_array[idx].shape = g.shape or 0
function Glows.new(capacity)
local ptr = C.pxl8_glows_create(capacity or 1000)
if ptr == nil then
return nil
end
C.pxl8_gfx_apply_effect(core.gfx, C.PXL8_GFX_EFFECT_GLOWS, glow_array, count)
return setmetatable({ _ptr = ptr }, Glows)
end
function Glows:add(x, y, radius, intensity, color, shape)
C.pxl8_glows_add(self._ptr, x, y, radius or 8, intensity or 255, color or 15, shape or 0)
end
function Glows:clear()
C.pxl8_glows_clear(self._ptr)
end
function Glows:count()
return C.pxl8_glows_count(self._ptr)
end
function Glows:destroy()
if self._ptr then
C.pxl8_glows_destroy(self._ptr)
self._ptr = nil
end
end
function Glows:render()
C.pxl8_glows_render(self._ptr, core.gfx)
end
effects.Glows = Glows
local Lights = {}
Lights.__index = Lights
function Lights.new(capacity)
local ptr = C.pxl8_lights_create(capacity or 256)
if ptr == nil then
return nil
end
return setmetatable({ _ptr = ptr }, Lights)
end
function Lights:add(x, y, z, r, g, b, intensity, radius)
C.pxl8_lights_add(self._ptr, x, y, z, r or 255, g or 255, b or 255, intensity or 255, radius or 10)
end
function Lights:clear()
C.pxl8_lights_clear(self._ptr)
end
function Lights:count()
return C.pxl8_lights_count(self._ptr)
end
function Lights:destroy()
if self._ptr then
C.pxl8_lights_destroy(self._ptr)
self._ptr = nil
end
end
effects.Lights = Lights
return effects

View file

@ -75,6 +75,15 @@ function Camera3D:update(dt)
C.pxl8_3d_camera_update(self._ptr, dt)
end
function Camera3D:world_to_screen(x, y, z, width, height)
local pos = ffi.new("pxl8_vec3", {x = x, y = y, z = z})
local result = C.pxl8_3d_camera_world_to_screen(self._ptr, pos, width, height)
if result.visible then
return {x = result.x, y = result.y, depth = result.depth}
end
return nil
end
gfx3d.Camera3D = Camera3D
local Mesh = {}
@ -123,14 +132,15 @@ gfx3d.Mesh = Mesh
function gfx3d.draw_mesh(mesh, opts)
opts = opts or {}
local model = ffi.new("pxl8_mat4")
model.m[0] = 1
model.m[5] = 1
model.m[10] = 1
local s = opts.scale or 1
model.m[0] = s
model.m[5] = s
model.m[10] = s
model.m[15] = 1
if opts.x then model.m[12] = opts.x end
if opts.y then model.m[13] = opts.y end
if opts.z then model.m[14] = opts.z end
local material = ffi.new("pxl8_material", {
local material = ffi.new("pxl8_gfx_material", {
texture_id = opts.texture or 0,
alpha = opts.alpha or 255,
blend_mode = opts.blend_mode or 0,
@ -139,12 +149,13 @@ function gfx3d.draw_mesh(mesh, opts)
dynamic_lighting = opts.lighting or false,
per_pixel = opts.per_pixel or false,
vertex_color_passthrough = opts.passthrough or false,
wireframe = opts.wireframe or false,
emissive_intensity = opts.emissive or 0.0,
})
C.pxl8_3d_draw_mesh(core.gfx, mesh._ptr, model, material)
end
function gfx3d.begin_frame(camera, uniforms)
function gfx3d.begin_frame(camera, lights, uniforms)
uniforms = uniforms or {}
local u = ffi.new("pxl8_3d_uniforms")
@ -164,27 +175,8 @@ function gfx3d.begin_frame(camera, uniforms)
end
u.celestial_intensity = uniforms.celestial_intensity or 0.0
u.num_lights = 0
if uniforms.lights then
for i, light in ipairs(uniforms.lights) do
if i > 16 then break end
local idx = i - 1
u.lights[idx].position.x = light.x or 0
u.lights[idx].position.y = light.y or 0
u.lights[idx].position.z = light.z or 0
u.lights[idx].r = light.r or 255
u.lights[idx].g = light.g or 255
u.lights[idx].b = light.b or 255
u.lights[idx].intensity = light.intensity or 255
u.lights[idx].radius = light.radius or 100
local radius_sq = u.lights[idx].radius * u.lights[idx].radius
u.lights[idx].radius_sq = radius_sq
u.lights[idx].inv_radius_sq = radius_sq > 0 and (1.0 / radius_sq) or 0
u.num_lights = i
end
end
C.pxl8_3d_begin_frame(core.gfx, camera._ptr, u)
local lights_ptr = lights and lights._ptr or nil
C.pxl8_3d_begin_frame(core.gfx, camera._ptr, lights_ptr, u)
end
function gfx3d.clear(color)
@ -205,4 +197,12 @@ function gfx3d.end_frame()
C.pxl8_3d_end_frame(core.gfx)
end
function gfx3d.project_points(input, output, count, transform)
C.pxl8_3d_project_points(core.gfx, input, output, count, transform)
end
function gfx3d.create_vec3_array(count)
return ffi.new("pxl8_vec3[?]", count)
end
return gfx3d

View file

@ -16,7 +16,7 @@ function Gui.new()
end
function Gui:begin_frame()
C.pxl8_gui_begin_frame(self._ptr)
C.pxl8_gui_begin_frame(self._ptr, core.gfx)
end
function Gui:button(id, x, y, w, h, label)
@ -43,7 +43,7 @@ function Gui:destroy()
end
function Gui:end_frame()
C.pxl8_gui_end_frame(self._ptr)
C.pxl8_gui_end_frame(self._ptr, core.gfx)
end
function Gui:get_cursor_pos()

View file

@ -1,53 +1,65 @@
local ffi = require("ffi")
local C = ffi.C
local math3d = {}
local math = {}
function math3d.mat4_identity()
function math.hash32(x)
return C.pxl8_hash32(x)
end
function math.mat4_identity()
return C.pxl8_mat4_identity()
end
function math3d.mat4_mul(a, b)
return C.pxl8_mat4_mul(a, b)
function math.mat4_multiply(a, b)
return C.pxl8_mat4_multiply(a, b)
end
function math3d.mat4_translate(x, y, z)
function math.mat4_multiply_vec3(m, v)
return C.pxl8_mat4_multiply_vec3(m, v)
end
function math.mat4_multiply_vec4(m, v)
return C.pxl8_mat4_multiply_vec4(m, v)
end
function math.mat4_translate(x, y, z)
return C.pxl8_mat4_translate(x, y, z)
end
function math3d.mat4_rotate_x(angle)
function math.mat4_rotate_x(angle)
return C.pxl8_mat4_rotate_x(angle)
end
function math3d.mat4_rotate_y(angle)
function math.mat4_rotate_y(angle)
return C.pxl8_mat4_rotate_y(angle)
end
function math3d.mat4_rotate_z(angle)
function math.mat4_rotate_z(angle)
return C.pxl8_mat4_rotate_z(angle)
end
function math3d.mat4_scale(x, y, z)
function math.mat4_scale(x, y, z)
return C.pxl8_mat4_scale(x, y, z)
end
function math3d.mat4_ortho(left, right, bottom, top, near, far)
return C.pxl8_mat4_ortho(left, right, bottom, top, near, far)
function math.mat4_orthographic(left, right, bottom, top, near, far)
return C.pxl8_mat4_orthographic(left, right, bottom, top, near, far)
end
function math3d.mat4_perspective(fov, aspect, near, far)
function math.mat4_perspective(fov, aspect, near, far)
return C.pxl8_mat4_perspective(fov, aspect, near, far)
end
function math3d.mat4_lookat(eye, center, up)
function math.mat4_lookat(eye, center, up)
local eye_vec = ffi.new("pxl8_vec3", {x = eye[1], y = eye[2], z = eye[3]})
local center_vec = ffi.new("pxl8_vec3", {x = center[1], y = center[2], z = center[3]})
local up_vec = ffi.new("pxl8_vec3", {x = up[1], y = up[2], z = up[3]})
return C.pxl8_mat4_lookat(eye_vec, center_vec, up_vec)
end
function math3d.bounds(x, y, w, h)
function math.bounds(x, y, w, h)
return ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h})
end
return math3d
return math

View file

@ -12,7 +12,7 @@ function Particles.new(max_count)
if ps == nil then
return nil
end
local pal = C.pxl8_gfx_get_palette(core.gfx)
local pal = C.pxl8_gfx_palette(core.gfx)
if pal ~= nil then
C.pxl8_particles_set_palette(ps, pal)
end

99
src/lua/pxl8/procgen.lua Normal file
View file

@ -0,0 +1,99 @@
local ffi = require("ffi")
local C = ffi.C
local core = require("pxl8.core")
local procgen = {}
procgen.OP_CONST = C.PXL8_OP_CONST
procgen.OP_INPUT_AGE = C.PXL8_OP_INPUT_AGE
procgen.OP_INPUT_SEED = C.PXL8_OP_INPUT_SEED
procgen.OP_INPUT_TIME = C.PXL8_OP_INPUT_TIME
procgen.OP_INPUT_X = C.PXL8_OP_INPUT_X
procgen.OP_INPUT_Y = C.PXL8_OP_INPUT_Y
procgen.OP_ABS = C.PXL8_OP_ABS
procgen.OP_ADD = C.PXL8_OP_ADD
procgen.OP_CEIL = C.PXL8_OP_CEIL
procgen.OP_CLAMP = C.PXL8_OP_CLAMP
procgen.OP_COS = C.PXL8_OP_COS
procgen.OP_DIV = C.PXL8_OP_DIV
procgen.OP_FLOOR = C.PXL8_OP_FLOOR
procgen.OP_FRACT = C.PXL8_OP_FRACT
procgen.OP_GRADIENT_LINEAR = C.PXL8_OP_GRADIENT_LINEAR
procgen.OP_GRADIENT_RADIAL = C.PXL8_OP_GRADIENT_RADIAL
procgen.OP_LERP = C.PXL8_OP_LERP
procgen.OP_MAX = C.PXL8_OP_MAX
procgen.OP_MIN = C.PXL8_OP_MIN
procgen.OP_MOD = C.PXL8_OP_MOD
procgen.OP_MUL = C.PXL8_OP_MUL
procgen.OP_NEGATE = C.PXL8_OP_NEGATE
procgen.OP_NOISE_FBM = C.PXL8_OP_NOISE_FBM
procgen.OP_NOISE_PERLIN = C.PXL8_OP_NOISE_PERLIN
procgen.OP_NOISE_RIDGED = C.PXL8_OP_NOISE_RIDGED
procgen.OP_NOISE_TURBULENCE = C.PXL8_OP_NOISE_TURBULENCE
procgen.OP_NOISE_VALUE = C.PXL8_OP_NOISE_VALUE
procgen.OP_POW = C.PXL8_OP_POW
procgen.OP_QUANTIZE = C.PXL8_OP_QUANTIZE
procgen.OP_SELECT = C.PXL8_OP_SELECT
procgen.OP_SIN = C.PXL8_OP_SIN
procgen.OP_SMOOTHSTEP = C.PXL8_OP_SMOOTHSTEP
procgen.OP_SQRT = C.PXL8_OP_SQRT
procgen.OP_SUB = C.PXL8_OP_SUB
procgen.OP_VORONOI_CELL = C.PXL8_OP_VORONOI_CELL
procgen.OP_VORONOI_EDGE = C.PXL8_OP_VORONOI_EDGE
procgen.OP_VORONOI_ID = C.PXL8_OP_VORONOI_ID
local Graph = {}
Graph.__index = Graph
function Graph.new(capacity)
capacity = capacity or 64
local ptr = C.pxl8_graph_create(capacity)
if ptr == nil then
return nil
end
return setmetatable({ _ptr = ptr }, Graph)
end
function Graph:destroy()
if self._ptr then
C.pxl8_graph_destroy(self._ptr)
self._ptr = nil
end
end
function Graph:clear()
C.pxl8_graph_clear(self._ptr)
end
function Graph:add_node(op, in0, in1, in2, in3, param)
return C.pxl8_graph_add_node(self._ptr, op, in0 or 0, in1 or 0, in2 or 0, in3 or 0, param or 0)
end
function Graph:set_output(reg)
C.pxl8_graph_set_output(self._ptr, reg)
end
function Graph:set_seed(seed)
C.pxl8_graph_set_seed(self._ptr, seed)
end
function Graph:eval_texture(width, height)
width = width or 64
height = height or 64
local buffer = ffi.new("u8[?]", width * height)
C.pxl8_graph_eval_texture(self._ptr, buffer, width, height)
local tex_id = C.pxl8_gfx_create_texture(core.gfx, buffer, width, height)
if tex_id < 0 then
return nil
end
return tex_id
end
procgen.Graph = Graph
function procgen.create_graph(capacity)
return Graph.new(capacity)
end
return procgen

View file

@ -85,36 +85,14 @@ function World:resolve_collision(from_x, from_y, from_z, to_x, to_y, to_z, radiu
return result.x, result.y, result.z
end
function World:set_wireframe(enabled)
C.pxl8_world_set_wireframe(self._ptr, enabled)
end
function World:unload()
C.pxl8_world_unload(self._ptr)
end
world.World = World
function world.procgen_tex(params)
local width = params.width or 64
local height = params.height or 64
local buffer = ffi.new("u8[?]", width * height)
local tex_params = ffi.new("pxl8_procgen_tex_params")
local name = params.name or ""
ffi.copy(tex_params.name, name, math.min(#name, 15))
tex_params.seed = params.seed or 0
tex_params.width = width
tex_params.height = height
tex_params.scale = params.scale or 1.0
tex_params.roughness = params.roughness or 0.0
tex_params.base_color = params.base_color or 0
tex_params.variation = params.variation or 0
C.pxl8_procgen_tex(buffer, tex_params)
local tex_id = C.pxl8_gfx_create_texture(core.gfx, buffer, width, height)
if tex_id < 0 then
return nil
end
return tex_id
end
return world