pxl8/src/lua/pxl8/gfx3d.lua

208 lines
5.8 KiB
Lua

local ffi = require("ffi")
local C = ffi.C
local core = require("pxl8.core")
local gfx3d = {}
local Camera3D = {}
Camera3D.__index = Camera3D
function Camera3D.new()
local cam = C.pxl8_3d_camera_create()
if cam == nil then
return nil
end
return setmetatable({ _ptr = cam }, Camera3D)
end
function Camera3D:destroy()
if self._ptr then
C.pxl8_3d_camera_destroy(self._ptr)
self._ptr = nil
end
end
function Camera3D:get_forward()
local v = C.pxl8_3d_camera_get_forward(self._ptr)
return {v.x, v.y, v.z}
end
function Camera3D:get_position()
local v = C.pxl8_3d_camera_get_position(self._ptr)
return {v.x, v.y, v.z}
end
function Camera3D:get_right()
local v = C.pxl8_3d_camera_get_right(self._ptr)
return {v.x, v.y, v.z}
end
function Camera3D:get_up()
local v = C.pxl8_3d_camera_get_up(self._ptr)
return {v.x, v.y, v.z}
end
function Camera3D:get_view()
return C.pxl8_3d_camera_get_view(self._ptr)
end
function Camera3D:get_projection()
return C.pxl8_3d_camera_get_projection(self._ptr)
end
function Camera3D:lookat(eye, target, up)
up = up or {0, 1, 0}
local eye_vec = ffi.new("pxl8_vec3", {x = eye[1], y = eye[2], z = eye[3]})
local target_vec = ffi.new("pxl8_vec3", {x = target[1], y = target[2], z = target[3]})
local up_vec = ffi.new("pxl8_vec3", {x = up[1], y = up[2], z = up[3]})
C.pxl8_3d_camera_lookat(self._ptr, eye_vec, target_vec, up_vec)
end
function Camera3D:set_perspective(fov, aspect, near, far)
C.pxl8_3d_camera_set_perspective(self._ptr, fov, aspect, near, far)
end
function Camera3D:set_position(x, y, z)
local pos = ffi.new("pxl8_vec3", {x = x, y = y, z = z})
C.pxl8_3d_camera_set_position(self._ptr, pos)
end
function Camera3D:set_rotation(pitch, yaw, roll)
C.pxl8_3d_camera_set_rotation(self._ptr, pitch, yaw or 0, roll or 0)
end
function Camera3D:update(dt)
C.pxl8_3d_camera_update(self._ptr, dt)
end
gfx3d.Camera3D = Camera3D
local Mesh = {}
Mesh.__index = Mesh
function Mesh.new(vertices, indices)
local vertex_count = #vertices
local index_count = #indices
local mesh = C.pxl8_mesh_create(vertex_count, index_count)
if mesh == nil then
return nil
end
local self = setmetatable({ _ptr = mesh }, Mesh)
for _, v in ipairs(vertices) do
local vert = ffi.new("pxl8_vertex", {
position = {x = v.x or 0, y = v.y or 0, z = v.z or 0},
normal = {x = v.nx or 0, y = v.ny or 0, z = v.nz or 0},
u = v.u or 0,
v = v.v or 0,
color = v.color or 0,
light = v.light or 255,
})
C.pxl8_mesh_push_vertex(mesh, vert)
end
for i = 1, #indices, 3 do
C.pxl8_mesh_push_triangle(mesh, indices[i], indices[i+1], indices[i+2])
end
return self
end
function Mesh:destroy()
if self._ptr then
C.pxl8_mesh_destroy(self._ptr)
self._ptr = nil
end
end
function Mesh:clear()
if self._ptr then
C.pxl8_mesh_clear(self._ptr)
end
end
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
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", {
texture_id = opts.texture or 0,
alpha = opts.alpha or 255,
blend_mode = opts.blend_mode or 0,
dither = opts.dither ~= false,
double_sided = opts.double_sided or false,
dynamic_lighting = opts.lighting or false,
per_pixel = opts.per_pixel or false,
vertex_color_passthrough = opts.passthrough 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)
uniforms = uniforms or {}
local u = ffi.new("pxl8_3d_uniforms")
u.ambient = uniforms.ambient or 0
u.fog_color = uniforms.fog_color or 0
u.fog_density = uniforms.fog_density or 0.0
u.time = uniforms.time or 0.0
if uniforms.celestial_dir then
u.celestial_dir.x = uniforms.celestial_dir[1] or 0
u.celestial_dir.y = uniforms.celestial_dir[2] or -1
u.celestial_dir.z = uniforms.celestial_dir[3] or 0
else
u.celestial_dir.x = 0
u.celestial_dir.y = -1
u.celestial_dir.z = 0
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)
end
function gfx3d.clear(color)
C.pxl8_3d_clear(core.gfx, color or 0)
end
function gfx3d.clear_depth()
C.pxl8_3d_clear_depth(core.gfx)
end
function gfx3d.draw_line(p0, p1, color)
local vec0 = ffi.new("pxl8_vec3", {x = p0[1], y = p0[2], z = p0[3]})
local vec1 = ffi.new("pxl8_vec3", {x = p1[1], y = p1[2], z = p1[3]})
C.pxl8_3d_draw_line(core.gfx, vec0, vec1, color)
end
function gfx3d.end_frame()
C.pxl8_3d_end_frame(core.gfx)
end
return gfx3d