359 lines
13 KiB
Lua
359 lines
13 KiB
Lua
|
|
-- 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
|
|
|
|
-- ---------------------------------------------------------------------------------------- |