summaryrefslogtreecommitdiff
path: root/honey/shader.lua
blob: 6b281f73241123e738124695643d9bb4c95caed7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
local gl = honey.gl

local module = {}
setmetatable(module, {__index=_G})
setfenv(1, module)


Shader = {}
Shader.__index = Shader

local function compileShader(source, type)
	local shader = gl.CreateShader(type)
	gl.ShaderSource(shader, source)
	gl.CompileShader(shader)
	return shader
end

local function readFile(filename)
	local f, err = io.open(filename)
	if not f then error(err) end
	local str = f:read("*a")
	f:close()
	return str
end

function Shader.new(_, sources)
	local self = {}
	self.locations = {}
	self.links = {}

	if sources.vertexFile then
		sources.vertex = readFile(sources.vertexFile)
	end
	if sources.fragmentFile then
		sources.fragment = readFile(sources.fragmentFile)
	end

	local shaders = {}
	if sources.vertex then
		table.insert(shaders, compileShader(sources.vertex, gl.VERTEX_SHADER))
	end
	if sources.fragment then
		table.insert(shaders, compileShader(sources.fragment, gl.FRAGMENT_SHADER))
	end

	self.program = gl.CreateProgram()
	for _, shader in ipairs(shaders) do
		gl.AttachShader(self.program, shader)
	end
	gl.LinkProgram(self.program)
	for _, shader in ipairs(shaders) do
		gl.DeleteShader(shader)
	end

	self.__gc = honey.util.gc_canary(function()
		gl.DeleteProgram(self.program)
	end)

	setmetatable(self, Shader)
	return self
end
setmetatable(Shader, {__call=Shader.new})


function Shader.getLocation(self, name)
	if self.locations[name] then
		return self.locations[name]
	end

	local location = gl.GetUniformLocation(self.program, name)
	self.locations[name] = location
	return location
end


function Shader.use(self)
	gl.UseProgram(self.program)
end


function Shader.setInt(self, name, value)
	local location = self:getLocation(name)
	gl.Uniform1i(location, value)
end
function Shader.setFloat(self, name, value)
	local location = self:getLocation(name)
	gl.Uniform1f(location, value)
end

function Shader.setVec3(self, name, value)
	local location = self:getLocation(name)
	gl.Uniform3f(location, value[1], value[2], value[3])
end
function Shader.setVec4(self, name, value)
	local location = self:getLocation(name)
	gl.Uniform3f(location, value[1], value[2], value[3], value[4])
end

function Shader.setMatrix(self, name, matrix)
	local location = self:getLocation(name)
	gl.UniformMatrix4fv(location, false, matrix.data)
end


function Shader.configure(self, tbl)
	local processKey = function(key, set)
		local subtbl = tbl[key]
		if subtbl then
			for name, value in pairs(subtbl) do 
				self[set](self, name, value) 
			end
		end
	end

	processKey("int", "setInt")
	processKey("float", "setFloat")
	processKey("vec3", "setVec3")
	processKey("vec4", "setVec4")
	processKey("matrix", "setMatrix")
end


local shaderCache = {}
function loadShader(vertex, fragment)
	local id = vertex .. "+" .. fragment
	if not shaderCache[id] then
		local shader = Shader{vertexFile=vertex, fragmentFile=fragment}
		shaderCache[id] = shader
	end
	return shaderCache[id]
end
function clearShaderCache()
	shaderCache = {}
end

return module