sokol-luajit/engine/geometry/meshes.lua

359 lines
13 KiB
Lua
Raw Normal View History

-- A mesh is a specific mesh resource.
-- It contains _only_
-- - vertices * required
-- - indices * required
-- - texcoords
-- - normals
-- - colors
-- - tangents
-- - custom stream data.
-- These are created as buffers and can be used as needed.
local ffi = require("ffi")
local utils = require("lua.utils")
local imageutils = require("lua.loaders.image-utils")
local cgltf = require("ffi.sokol.cgltf")
local tinsert = table.insert
-- --------------------------------------------------------------------------------------
local shc = require("tools.shader_compiler.shc_compile").init( "worldbuilder", false )
-- ----------------------------------------------------------------------------------------
local all_meshes = {
materials = {}, -- A material shader cache.
}
-- ----------------------------------------------------------------------------------------
local MESH_TYPE = {
MTYPE_TRIANGLES = 1,
MTYPE_TRI_STRIP = 2,
MTYPE_POINTS = 3, -- These use special shaders
MTYPE_LINES = 4, -- These use special shaders
}
-- ----------------------------------------------------------------------------------------
local mesh = {
id = 0, -- internal id for the engine
priority = 0, -- rendering information
binid = 0, -- current render bin assignment (transparent/opaque/custom)
type = MESH_TYPE.MTYPE_TRIANGLES,
vertices = nil, -- vert buf
indices = nil, -- This can be nil. If so, triangles are rendered in order.
streams = {}, -- The above attached streams of data (uvs, norms, etc)
}
--- Load the types into the main library (so external systems can use it)
for k,v in pairs(MESH_TYPE) do
mesh[k]=v
end
-- ----------------------------------------------------------------------------------------
-- Buffer creation tool
mesh.create_buffer = function(name, buffers)
-- Make a mesh table with the info we need to build the pipeline and bindings.
-- Buffers need to be interleaved with data sets, so we do this here.
local vertcount = 0
local stride = 0
local attribs = {}
local float_size = ffi.sizeof("float")
local offset = 0
local vsize = buffers.vsize or 3
if(buffers.vertices) then
vertcount = buffers.vcount or ffi.sizeof(buffers.vertices) / (vsize * float_size)
stride = vsize
if(vsize == 4) then
tinsert(attribs, { offset = offset, format = sg.SG_VERTEXFORMAT_FLOAT4 })
else
tinsert(attribs, { offset = offset, format = sg.SG_VERTEXFORMAT_FLOAT3 })
end
if(buffers.uvs) then
stride = stride + 2
offset = offset + 3 * float_size
tinsert(attribs, { offset = offset, format = sg.SG_VERTEXFORMAT_FLOAT2 })
end
if(buffers.normals) then
stride = stride + 3
offset = offset + 5 * float_size
tinsert(attribs, { offset = offset, format = sg.SG_VERTEXFORMAT_FLOAT3 })
end
if(buffers.colors) then
stride = stride + 4
offset = offset + 8 * float_size
tinsert(attribs, { offset = offset, format = sg.SG_VERTEXFORMAT_FLOAT4 })
end
end
local buffs = {
vbuf = nil,
vcount = vertcount,
ibuf = nil,
icount = buffers.icount or 0,
index_type = buffers.itype or nil,
sbuf = nil,
scount = 0,
stride = stride,
attrs = attribs,
depth = {},
}
local uvptr = nil
local nptr = nil
local cptr = nil
if(buffers.vertices) then
local buffer = ffi.new("float[?]", vertcount * stride)
local vptr = ffi.cast("float *", buffers.vertices)
if(buffers.uvs) then uvptr = ffi.cast("float *", buffers.uvs) end
if(buffers.normals) then nptr = ffi.cast("float *", buffers.normals) end
if(buffers.colors) then cptr = ffi.cast("float *", buffers.colors) end
-- Copy in interlaced!
local ptr = ffi.cast("float *", buffer)
for i=1, vertcount do
ffi.copy(ptr, vptr, stride * float_size)
vptr = vptr + vsize
ptr = ptr + vsize
if(buffers.uvs) then
ffi.copy(ptr, uvptr, 2* float_size)
uvptr = uvptr + 2
ptr = ptr + 2
end
if(buffers.normals) then
ffi.copy(ptr, nptr, 3* float_size)
nptr = nptr + 3
ptr = ptr + 3
end
if(buffers.colors) then
ffi.copy(ptr, cptr, 4* float_size)
cptr = cptr + 4
ptr = ptr + 4
end
end
-- buffer should now have interlaced data
if(ffi.sizeof(buffer) > 0) then
local buffer_desc = ffi.new("sg_buffer_desc[1]")
buffer_desc[0].type = sg.SG_BUFFERTYPE_VERTEXBUFFER
buffer_desc[0].data.ptr = buffer
buffer_desc[0].data.size = ffi.sizeof(buffer)
buffer_desc[0].label = name.."-vertices"
buffs.vbuf = sg.sg_make_buffer(buffer_desc)
else
return nil
end
end
if(buffers.indices) then
if(ffi.sizeof(buffers.indices) > 0) then
local buffer_desc = ffi.new("sg_buffer_desc[1]")
buffer_desc[0].type = sg.SG_BUFFERTYPE_INDEXBUFFER
buffer_desc[0].data.ptr = buffers.indices
buffer_desc[0].data.size = ffi.sizeof(buffers.indices)
buffer_desc[0].label = name.."-indices"
buffs.ibuf = sg.sg_make_buffer(buffer_desc)
else
return nil
end
end
if(buffers.storage) then
if(ffi.sizeof(buffers.storage) > 0) then
local buffer_desc = ffi.new("sg_buffer_desc[1]")
buffer_desc[0].type = sg.SG_BUFFERTYPE_STORAGEBUFFER
buffer_desc[0].data.ptr = buffers.storage
buffer_desc[0].data.size = ffi.sizeof(buffers.storage)
buffer_desc[0].label = name.."-storage"
buffs.sbuf = sg.sg_make_buffer(buffer_desc)
else
return nil
end
end
return buffs
end
-- ----------------------------------------------------------------------------------------
-- Create an image to be used to texture geom or render textures
mesh.image = function(name, rt, width, height, format, samples )
local img_desc = ffi.new("sg_image_desc[1]")
img_desc[0].render_target = rt or false
img_desc[0].width = width or 512
img_desc[0].height = height or 512
img_desc[0].pixel_format = format or sg.SG_PIXELFORMAT_RGBA8
img_desc[0].sample_count = samples or 1
img_desc[0].label = name
return sg.sg_make_image(img_desc)
end
-- ----------------------------------------------------------------------------------------
-- Create a material from shader and params
mesh.material = function(name, shaderfile, params)
local cached = all_meshes.materials[shaderfile]
if(cached) then return cached end
local shader = shc.compile(shaderfile, nil, name)
local shd = sg.sg_make_shader(shader)
local sampler_desc = ffi.new("sg_sampler_desc")
sampler_desc.min_filter = sg.SG_FILTER_LINEAR
sampler_desc.mag_filter = sg.SG_FILTER_LINEAR
sampler_desc.wrap_u = sg.SG_WRAP_REPEAT
sampler_desc.wrap_v = sg.SG_WRAP_REPEAT
sampler_desc.wrap_w = sg.SG_WRAP_REPEAT -- only needed for 3D textures
local default_sampler = sg.sg_make_sampler(sampler_desc)
if(shd) then
local material = {
shader = shd,
params = params,
name = name,
base_color_smp = default_sampler,
}
all_meshes.materials[shaderfile] = material
return material
else
pprint("[Error mesh.material] Could not create material: "..tostring(name))
return nil
end
end
-- ----------------------------------------------------------------------------------------
-- Model makes a pipeline
mesh.make_mesh = function(name, buffers)
local mesh = { layout = {}, depth = {}, aabb = buffers.aabb }
local buffercount = utils.tcount(buffers)
if(buffercount == 0) then
pprint("[Error mesh.make_mesh] No buffers to process!")
return nil
end
mesh.layout.buffers = ffi.new("sg_vertex_buffer_layout_state[8]")
mesh.layout.attrs = ffi.new("sg_vertex_attr_state[16]")
-- One attr for each buffer
for bi, buffer in ipairs(buffers) do
if(buffer) then
mesh.layout.buffers[bi-1].stride = buffer.stride * ffi.sizeof("float")
end
if(buffer.index_type) then
mesh.index_type = buffer.index_type
end
if(buffer.cull_mode) then
mesh.cullmode = buffer.cullmode
end
if(buffer.attrs) then
for i, attr in ipairs(buffer.attrs) do
mesh.layout.attrs[i-1].format = attr.format
mesh.layout.attrs[i-1].offset = attr.offset
end
end
if(mesh.depth) then
mesh.depth = ffi.new("sg_depth_state")
if(buffer.depth.write_enabled) then
mesh.depth.write_enabled = buffer.depth.write_enabled
end
if(buffer.depth.compare) then
mesh.depth.compare = buffer.depth.compare
end
end
mesh.vbuf = buffer.vbuf
mesh.ibuf = buffer.ibuf
mesh.sbuf = buffer.sbuf
end
return mesh
end
-- ----------------------------------------------------------------------------------------
-- Model makes a pipeline
mesh.model = function(name, prim, mesh, material)
-- Stores material with mesh - this should be an index
-- mesh.material = material
local pipe_desc = ffi.new("sg_pipeline_desc[1]", {})
pipe_desc[0].layout.buffers = mesh.layout.buffers
pipe_desc[0].layout.attrs = mesh.layout.attrs
pipe_desc[0].shader = material.shader
if(mesh.index_type) then pipe_desc[0].index_type = mesh.index_type end
if(mesh.cull_mode) then pipe_desc[0].cull_mode = mesh.cull_mode end
if(mesh.depth) then
if(mesh.depth.write_enabled) then pipe_desc[0].depth.write_enabled = mesh.depth.write_enabled end
if(mesh.depth.compare) then pipe_desc[0].depth.compare = mesh.depth.compare end
end
local prim_alpha_mode = cgltf.cgltf_alpha_mode_opaque
local prim_alpha_cutoff = 0.0
if(prim.material) then
if(prim.material.alpha_mode == cgltf.cgltf_alpha_mode_blend) then
pipe_desc[0].colors[0].blend.enabled = true
pipe_desc[0].colors[0].blend.src_factor_rgb = sg.SG_BLENDFACTOR_SRC_ALPHA
pipe_desc[0].colors[0].blend.dst_factor_rgb = sg.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA
pipe_desc[0].colors[0].blend.src_factor_alpha = sg.SG_BLENDFACTOR_ONE
pipe_desc[0].colors[0].blend.dst_factor_alpha = sg.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA
elseif(prim.material.alpha_mode == cgltf.cgltf_alpha_mode_mask) then
end
prim_alpha_mode = prim.material.alpha_mode
prim_alpha_cutoff = prim.material.alpha_cutoff
end
pipe_desc[0].label = name.."-pipeline"
-- pipe_desc[0].sample_count = 1
-- pipe_desc[0].depth.pixel_format = sg.SG_PIXELFORMAT_DEPTH
local pipeline = sg.sg_make_pipeline(pipe_desc)
local binding = ffi.new("sg_bindings[1]", {})
binding[0].vertex_buffers[0] = mesh.vbuf
if(mesh.ibuf) then binding[0].index_buffer = mesh.ibuf end
local mesh_mat = prim.material
local tex = nil
if(mesh_mat and mesh_mat.images) then
if(mesh_mat.images.base_color ~= nil) then
local img = mesh_mat.images.base_color.img or sg.sg_make_image(mesh_mat.images.base_color.info)
binding[0].images[0] = img
tex = true
end
end
if(tex == nil) then
binding[0].images[0] = imageutils.default_white_image
end
if(material.base_color_smp ~= nil) then
binding[0].samplers[0] = material.base_color_smp -- the sampler
end
return {
pip = pipeline,
bind = binding,
alpha = { mode = prim_alpha_mode, cutoff = prim_alpha_cutoff }
}
end
-- ----------------------------------------------------------------------------------------
return mesh
-- ----------------------------------------------------------------------------------------