208 lines
5.8 KiB
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
|