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.fragment then local shader = compileShader(readFile(sources.fragment), gl.FRAGMENT_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 --===== builtin shaders =====-- builtin["builtin.basic3d.vert"] = [[ #version 410 core layout (location = 0) in vec3 in_position; layout (location = 1) in vec3 in_normal; layout (location = 2) in vec2 in_texture; uniform mat4 model; uniform mat4 view; uniform mat4 projection; out vec3 position; out vec3 normal; out vec2 tex; void main() { gl_Position = projection * view * model * vec4(in_position, 1.0); position = in_position; normal = in_normal; tex = in_texture; } ]] builtin["builtin.flat.frag"] = [[ #version 410 core out vec4 frag_color; in vec3 position; in vec3 normal; in vec2 tex; uniform sampler2D surface; void main() { frag_color = texture(surface, tex); } ]] return module