From 3275ae4948fd2c1bb8da780214cbb741dc3178be Mon Sep 17 00:00:00 2001 From: sanine Date: Fri, 12 May 2023 01:16:46 -0500 Subject: begin refactor --- honey/asset/image.lua | 49 ++++++++++++++++ honey/asset/mesh.lua | 150 +++++++++++++++++++++++++++++++++++++++++++++++++ honey/asset/shader.lua | 143 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 342 insertions(+) create mode 100644 honey/asset/image.lua create mode 100644 honey/asset/mesh.lua create mode 100644 honey/asset/shader.lua (limited to 'honey/asset') diff --git a/honey/asset/image.lua b/honey/asset/image.lua new file mode 100644 index 0000000..c5bd145 --- /dev/null +++ b/honey/asset/image.lua @@ -0,0 +1,49 @@ +local gl = honey.gl + +local module = {} +setmetatable(module, {__index=_G}) +setfenv(1, module) + + +local cache = {} + +-- load an image into a gl texture +local function loadImage(filename) + local data, width, height = honey.image.load(filename, 4) + local texture = gl.GenTextures() + gl.BindTexture(gl.TEXTURE_2D, texture) + gl.TexImage2D( + gl.TEXTURE_2D, 0, + gl.RGBA, width, height, + gl.RGBA, gl.UNSIGNED_BYTE, data + ) + gl.GenerateMipmap(gl.TEXTURE_2D) + honey.image.destroy(data) + return texture +end + +-- cached get a texture +get = function(filename) + if not cache[filename] then + cache[filename] = loadImage(filename) + end + return cache[filename] +end + +-- remove a texture from the cache +forget = function(filename) + local texture = cache[filename] + if texture then + gl.DeleteTextures(texture) + end + cache[filename] = nil +end + +-- remove all textures from the cache +clearCache = function(filename) + for key in pairs(cache) do + forget(key) + end +end + +return module diff --git a/honey/asset/mesh.lua b/honey/asset/mesh.lua new file mode 100644 index 0000000..876c5e4 --- /dev/null +++ b/honey/asset/mesh.lua @@ -0,0 +1,150 @@ +local gl = honey.gl + +local module = {} +setmetatable(module, {__index=_G}) +setfenv(1, module) + + +-- append the 8 floats per vertex to the vertices table +local function insertVertex(vertices, attrib, vertex) + local pos = 3*vertex.v_idx + for i=1,3 do + table.insert(vertices, attrib.vertices[pos+i]) + end + + local normal = 3*vertex.vn_idx + for i=1,3 do + table.insert(vertices, attrib.normals[normal+i]) + end + + local tex = 2*vertex.vt_idx + for i=1,2 do + table.insert(vertices, attrib.texcoords[tex+i]) + end +end + + +-- creates a table of floats representing vertex data +-- and a table of ints representing face indices within the vertices +local function loadShape(shape, attrib, debug) + local vertices = {} + local indices = {} + + local start = shape.face_offset + local finish = start + shape.length + for i=start,finish-1 do + assert(attrib.face_num_verts[i+1] == 3, "non-triangular face!") + for j=0,2 do + local vertex = attrib.faces[(3*i) + j + 1] + insertVertex(vertices, attrib, vertex, debug) + table.insert(indices, #indices) + end + end + + return vertices, indices +end + + +-- public helper function (used in some other places, like trimesh construction) +-- packages the vertices & indices tables from loadShape into a single array +loadFile = function(filename) + local flags = honey.tinyobj.FLAG_TRIANGULATE + local attrib, shapes, materials = honey.tinyobj.parse_obj(filename, flags) + + local meshes = {} + for _, shape in ipairs(shapes) do + local vertices, indices = loadShape(shape, attrib, debug) + table.insert(meshes, {vertices=vertices, indices=indices}) + end + return meshes +end + + +-- helper function to create a gl vertex array object from (vertices, indices) pair +local function createMesh(vertices, indices) + vao = gl.GenVertexArrays() + vbo = gl.GenBuffers() + ebo = gl.GenBuffers() + + gl.BindVertexArray(vao) + gl.BindBuffer(gl.ARRAY_BUFFER, vbo) + gl.BufferData(gl.ARRAY_BUFFER, gl.FLOAT, vertices, gl.STATIC_DRAW) + + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo) + gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, gl.UNSIGNED_INT, indices, gl.STATIC_DRAW) + + local stride = 8 + local offset = 0 + + -- position + gl.VertexAttribPointer(0, 3, false, stride, offset) + gl.EnableVertexAttribArray(0) + offset = offset+3 + + -- normal + gl.VertexAttribPointer(1, 3, false, stride, offset) + gl.EnableVertexAttribArray(1) + offset = offset+3 + + -- texture + gl.VertexAttribPointer(2, 2, false, stride, offset) + gl.EnableVertexAttribArray(2) + offset = offset+2 + + return vao, vbo, ebo, #vertices +end + + +local cache = {} + + +-- get a gl vertex array object + vertex count +get = function(filename, index) + if not cache[filename] then + local c = {} + local meshes = loadFile(filename) + for _, m in ipairs(meshes) do + local vao, vbo, ebo, count = createMesh(m.vertices, m.indices) + table.insert(c, { vao=vao, vbo=vbo, ebo=ebo, count=count }) + end + cache[filename] = c + end + local m = cache[filename][index] + return m.vao, m.count +end + + +-- forget a mesh +forget = function(filename) + if not cache[filename] then return end + for _, m in ipairs(cache[filename]) do + gl.DeleteBuffers(m.vbo) + gl.DeleteBuffers(m.ebo) + gl.DeleteVertexArrays(m.vao) + end + cache[filename] = nil +end + + +-- clear the cache +clearCache = function() + for key in pairs(cache) do + forget(key) + end +end + + +--===== builtin meshes =====-- + +function createBuiltins() + cache["builtin.quad"] = {Mesh( + { 0, 0, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 1, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 1, + 1, 1, 0, 0, 1, 0, 1, 1 }, + { 0, 1, 3, 0, 3, 2 } + )} +end + + +return module diff --git a/honey/asset/shader.lua b/honey/asset/shader.lua new file mode 100644 index 0000000..0e8c93a --- /dev/null +++ b/honey/asset/shader.lua @@ -0,0 +1,143 @@ +local gl = honey.gl + +local module = {} +setmetatable(module, {__index=_G}) +setfenv(1, module) + +-- compile glsl source code +local function compileShader(source, type) + local shader = gl.CreateShader(type) + gl.ShaderSource(shader, source) + gl.CompileShader(shader) + return shader +end + +-- helper function +local builtin = {} +local function readFile(filename) + -- support built-in shaders + if builtin[filename] then return builtin[filename] end + + local f, err = io.open(filename) + if not f then error(err) end + local str = f:read("*a") + f:close() + return str +end + + +-- build a shader program from provided source files +local function buildProgram(sources) + local shaders = {} + + -- load & compile shaders + if sources.vertex then + local shader = compileShader(readFile(sources.vertex), gl.VERTEX_SHADER) + table.insert(shaders, shader) + end + if sources.vertex then + local shader = compileShader(readFile(sources.vertex), gl.VERTEX_SHADER) + table.insert(shaders, shader) + end + + -- link shaders + local program = gl.CreateProgram() + for _, shader in ipairs(shaders) do + gl.AttachShader(program, shader) + end + gl.LinkProgram(program) + + -- clean up + for _, shader in ipairs(shaders) do + gl.DeleteShader(shader) + end + + return program +end + + +-- public uniform setters +function setInt(program, name, value) + local location = gl.GetUniformLocation(program, name) + gl.Uniform1i(location, value) +end +function setFloat(program, name, value) + local location = gl.GetUniformLocation(program, name) + gl.Uniform1f(location, value) +end +function setVec3(program, name, value) + local location = gl.GetUniformLocation(program, name) + gl.Uniform3f(location, value[1], value[2], value[3]) +end +function setVec4(program, name, value) + local location = gl.GetUniformLocation(program, name) + gl.Uniform3f(location, value[1], value[2], value[3], value[4]) +end +function setMatrix(program, name, matrix) + local location = gl.GetUniformLocation(program, name) + gl.UniformMatrix4fv(location, false, matrix.data) +end + + +function configure(program, tbl) + local processKey = function(key, set) + local subtbl = tbl[key] + if subtbl then + for name, value in pairs(subtbl) do + set(program, name, value) + end + end + end + + processKey("int", setInt) + processKey("float", setFloat) + processKey("vec3", setVec3) + processKey("vec4", setVec4) + processKey("matrix", setMatrix) +end + + +--===== public asset cache functions =====-- + +local cache = {} + + +-- generate id string based on shader sources +local function shaderId(sources) + return string.format( + "%s;%s", + sources.vertex or "nil", + sources.fragment or "nil" + ) +end + +-- get a cached shader program +get = function(sources) + local id = shaderId(sources) + if not cache[id] then + cache[id] = buildProgram(sources) + end + return cache[id] +end + + +-- remove a cached shader program +forget = function(sources) + local id = shaderId(sources) + if cache[id] then + gl.DeleteProgram(cache[id]) + cache[id] = nil + end +end + + +-- clear the cache +clearCache = function() + for key, program in pairs(cache) do + gl.DeleteProgram(program) + cache[key] = nil + end +end + + +return module -- cgit v1.2.1