sokol-luajit/engine/threed.lua
2026-07-31 00:29:09 +09:30

247 lines
9.4 KiB
Lua

local sapp = require("sokol_app")
local nk = sg
local stb = require("stb")
local hmm = require("hmm")
local hutils = require("hmm_utils")
local ffi = require("ffi")
local utils = require("lua.utils")
local cameramgr = require("lua.engine.camera_manager")
local geomutils = require("lua.loaders.geometry-utils")
local tinsert = table.insert
local tremove = table.remove
-- --------------------------------------------------------------------------------------
-- TODO: Make better handler for this.
local MAX_STATES = 1024
local CAM_DISTANCE = 6.0
-- --------------------------------------------------------------------------------------
-- This is a global - prob not a great idea, but there should only ever be one per process!
threed_renderer = {
render_queue = {},
model_load_queue = {},
-- A mapped list of model files by the file name
-- These indicate whether a model is loaded, if it has an id and if it
-- has had its data initialised
model_files = {},
default_cam = cameramgr.add("default", 60.0, 1, 0.01, CAM_DISTANCE * 2),
}
-- --------------------------------------------------------------------------------------
local shc = require("tools.shader_compiler.shc_compile").init( "worldbuilder", false )
local shader = nil
-- --------------------------------------------------------------------------------------
threed_renderer.make_cube = function()
shader = shader or shc.compile("lua/engine/shaders/cube_simple.glsl")
-- Make a fake model to use for testing
local state = ffi.new("internal_state[1]")
local binding = ffi.new("sg_bindings[1]", {})
local vertices = ffi.new("float[168]", {
-1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
-1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
-1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0,
-1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0,
-1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
-1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
1.0, -1.0, -1.0, 1.0, 0.5, 0.0, 1.0,
1.0, 1.0, -1.0, 1.0, 0.5, 0.0, 1.0,
1.0, 1.0, 1.0, 1.0, 0.5, 0.0, 1.0,
1.0, -1.0, 1.0, 1.0, 0.5, 0.0, 1.0,
-1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0,
-1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0,
1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0,
1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0,
-1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0,
-1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0,
1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0,
1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0
})
local buffer_desc = ffi.new("sg_buffer_desc[1]")
buffer_desc[0].data.ptr = vertices
buffer_desc[0].data.size = ffi.sizeof(vertices)
buffer_desc[0].label = "cube-vertices"
local vbuf = sg.sg_make_buffer(buffer_desc)
local indices = ffi.new("uint16_t[36]", {
0, 1, 2, 0, 2, 3,
6, 5, 4, 7, 6, 4,
8, 9, 10, 8, 10, 11,
14, 13, 12, 15, 14, 12,
16, 17, 18, 16, 18, 19,
22, 21, 20, 23, 22, 20
})
local ibuffer_desc = ffi.new("sg_buffer_desc[1]", {})
ibuffer_desc[0].type = sg.SG_BUFFERTYPE_INDEXBUFFER
ibuffer_desc[0].data.ptr = indices
ibuffer_desc[0].data.size = ffi.sizeof(indices)
ibuffer_desc[0].label = "cube-indices"
local ibuf = sg.sg_make_buffer(ibuffer_desc)
local shd = sg.sg_make_shader(shader)
local pipe_desc = ffi.new("sg_pipeline_desc[1]", {})
pipe_desc[0].layout.buffers[0].stride = 28
pipe_desc[0].layout.attrs[0].format = sg.SG_VERTEXFORMAT_FLOAT3
pipe_desc[0].layout.attrs[1].format = sg.SG_VERTEXFORMAT_FLOAT4
pipe_desc[0].shader = shd
pipe_desc[0].index_type = sg.SG_INDEXTYPE_UINT16
pipe_desc[0].cull_mode = sg.SG_CULLMODE_BACK
pipe_desc[0].depth.write_enabled = true
pipe_desc[0].depth.compare = sg.SG_COMPAREFUNC_LESS_EQUAL
pipe_desc[0].label = "cube-pipeline"
state[0].pip = sg.sg_make_pipeline(pipe_desc)
binding[0].vertex_buffers[0] = vbuf
binding[0].index_buffer = ibuf
state[0].bind = binding
return { state = state, binding = binding }
end
-- --------------------------------------------------------------------------------------
local function render_model( dt, model_rect )
local aabb = model_rect.model.data.mesh.aabb
local maxx = aabb.max.x - aabb.min.x
local maxy = aabb.max.y - aabb.min.y
local maxz = aabb.max.z - aabb.min.z
local maxsize = math.sqrt( maxx * maxx + maxy * maxy + maxz * maxz)
local sc = CAM_DISTANCE * 0.85 / maxsize * model_rect.model.scale
local offset = hmm.HMM_V3(aabb.min.x + maxx * 0.5, aabb.min.y + maxy * 0.5, aabb.min.z + maxz * 0.5)
offset.x, offset.y, offset.z = offset.x * sc, offset.y * sc, offset.x * sc
local w, h = model_rect.w, model_rect.h
-- Dont render if there is no width or height?
if(w <= 0 or h <= 0) then return end
cameramgr.set_nearfar( model_rect.cam, 0.01, CAM_DISTANCE * 2 * model_rect.model.scale )
cameramgr.set_aspect( model_rect.cam, w/h )
cameramgr.lookat( model_rect.cam, hmm.HMM_V3(0.0, (offset.y + 1.5), -CAM_DISTANCE), hmm.HMM_V3(0.0, offset.y, 0.0), hmm.HMM_V3(0.0, -1.0, 0.0))
local view_proj = cameramgr.get_view_proj(model_rect.cam)
local view = cameramgr.get_view(model_rect.cam)
local model_all_geom = model_rect.model.data.mesh.all_geom
model_rect.model.data.mesh.camera = model_rect.cam
cameramgr.update_viewport( model_rect.cam, hmm.HMM_V4(0, 0, 1024, 1024))
cameramgr.update_scissor( model_rect.cam, hmm.HMM_V4(0, 0, 1024, 1024))
local thiscam = cameramgr.get_id( model_rect.cam )
for i, geom_id in ipairs(model_all_geom) do
local geom = geomutils.all_objs[geom_id]
geom.rx = 0.0
geom.ry = geom.ry + dt * 3.0
-- geom.fs_params[0].alpha_cutoff = 0.9
-- geom.fs_params[0].alpha_mode = 2
geom.dstate[0].state = 0x01
local pos = hmm.HMM_V3(0, 0, 0)
local angles = hmm.HMM_V3(geom.rx, geom.ry, 0.0)
local model = geomutils.model_matrix( geom.transform, pos, angles, sc)
geom.vs_params[0].mvp = hmm.HMM_MulM4(view_proj, model)
end
end
-- --------------------------------------------------------------------------------------
-- Hides all the vertex buffers associated with this model
threed_renderer.hide_model = function(model)
if (model.loaded == true) then
local model_all_geom = model.data.mesh.all_geom
for i, geom_id in ipairs(model_all_geom) do
local geom = geomutils.all_objs[geom_id]
geom.dstate[0].state = 0x0
end
end
end
-- --------------------------------------------------------------------------------------
-- This doesnt directly load a model in case it happens during an incorrent phase of rendering
threed_renderer.load_model = function(filename, params)
-- Check first if it hasnt been loaded already!
local is_loaded = threed_renderer.model_load_queue[filename]
if(is_loaded) then
return is_loaded
end
local new_model = { loaded = nil, filename = filename, params = params }
threed_renderer.model_load_queue[filename] = new_model
return new_model
end
-- --------------------------------------------------------------------------------------
-- Draw the model based on an xy rect position (not 3d space).
-- This queues model rects for drawing in the correct phase of the frame.
threed_renderer.draw_model = function(model, x, y, w, h)
if (model.loaded == true) then
tinsert(threed_renderer.render_queue, { cam=model.data.camera, model=model, x=x, y=y, w=w, h=h })
end
end
-- --------------------------------------------------------------------------------------
-- Iterate the queued rects and render models into them
-- Rects should _not_ be here if they are hidden (ie in a hidden tab)
threed_renderer.load_models = function()
if(utils.tcount(threed_renderer.model_load_queue) == 0) then return end
-- print("queued models", count)
for k,model_load in pairs(threed_renderer.model_load_queue) do
if(model_load.loaded == nil and model_load.params.do_load) then
model_load.data = model_load.params.do_load( model_load )
if(model_load.data) then
-- model_load.data = threed_renderer.make_cube()
model_load.loaded = true
else
-- Remove model that cant be loaded.
threed_renderer.model_load_queue[k] = nil
end
end
end
end
-- --------------------------------------------------------------------------------------
-- Iterate the queued rects and render models into them
-- Rects should _not_ be here if they are hidden (ie in a hidden tab)
threed_renderer.render_rects = function( dt )
local count = #threed_renderer.render_queue
-- print("queued models", count)
for i=1, count do
local model_rect = threed_renderer.render_queue[i]
render_model(dt, model_rect)
end
end
-- --------------------------------------------------------------------------------------