diff options
Diffstat (limited to 'gfx/src/core/shader_program.c')
-rw-r--r-- | gfx/src/core/shader_program.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/gfx/src/core/shader_program.c b/gfx/src/core/shader_program.c new file mode 100644 index 0000000..3cbe48d --- /dev/null +++ b/gfx/src/core/shader_program.c | |||
@@ -0,0 +1,291 @@ | |||
1 | #include "shader_program.h" | ||
2 | |||
3 | #include "gl_util.h" | ||
4 | #include "shader.h" | ||
5 | #include "texture.h" | ||
6 | #include <gfx_assert.h> | ||
7 | |||
8 | #include <log/log.h> | ||
9 | |||
10 | #include <stdlib.h> | ||
11 | #include <string.h> | ||
12 | |||
13 | /// Creates an OpenGL shader program. | ||
14 | /// Returns non-zero on success, 0 on failure. | ||
15 | static GLuint create_program(GLuint vertex_shader, GLuint fragment_shader) { | ||
16 | const GLuint prog = glCreateProgram(); | ||
17 | if (prog == 0) { | ||
18 | LOGE("Failed creating shader program"); | ||
19 | return 0; | ||
20 | } | ||
21 | glAttachShader(prog, vertex_shader); | ||
22 | glAttachShader(prog, fragment_shader); | ||
23 | glLinkProgram(prog); | ||
24 | GLint result; | ||
25 | glGetProgramiv(prog, GL_LINK_STATUS, &result); | ||
26 | if (result == GL_FALSE) { | ||
27 | GLint log_len; | ||
28 | glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_len); | ||
29 | if (log_len > 0) { | ||
30 | char* log = calloc(log_len, sizeof(char)); | ||
31 | glGetProgramInfoLog(prog, log_len, NULL, log); | ||
32 | LOGE("Failed creating shader program: %s", log); | ||
33 | free(log); | ||
34 | } else { | ||
35 | LOGE("Failed creating shader program"); | ||
36 | } | ||
37 | glDeleteProgram(prog); | ||
38 | return 0; | ||
39 | } | ||
40 | ASSERT_GL; | ||
41 | return prog; | ||
42 | } | ||
43 | |||
44 | bool gfx_build_shader_program( | ||
45 | ShaderProgram* prog, const ShaderProgramDesc* desc) { | ||
46 | assert(prog); | ||
47 | assert(desc); | ||
48 | |||
49 | prog->id = create_program(desc->vertex_shader->id, desc->fragment_shader->id); | ||
50 | return prog->id != 0; | ||
51 | } | ||
52 | |||
53 | void gfx_del_shader_program(ShaderProgram* prog) { | ||
54 | assert(prog); | ||
55 | |||
56 | if (prog->id) { | ||
57 | glDeleteProgram(prog->id); | ||
58 | prog->id = 0; | ||
59 | } | ||
60 | ASSERT_GL; | ||
61 | } | ||
62 | |||
63 | void gfx_activate_shader_program(const ShaderProgram* prog) { | ||
64 | assert(prog); | ||
65 | glUseProgram(prog->id); | ||
66 | ASSERT_GL; | ||
67 | } | ||
68 | |||
69 | void gfx_deactivate_shader_program(const ShaderProgram* prog) { | ||
70 | assert(prog); | ||
71 | glUseProgram(0); | ||
72 | ASSERT_GL; | ||
73 | } | ||
74 | |||
75 | static void set_texture_uniform( | ||
76 | GLuint prog, const char* name, int texture_unit, const Texture* texture) { | ||
77 | assert(prog != 0); | ||
78 | assert(name); | ||
79 | assert(texture); | ||
80 | |||
81 | const GLint location = glGetUniformLocation(prog, name); | ||
82 | if (location >= 0) { | ||
83 | glActiveTexture(GL_TEXTURE0 + texture_unit); | ||
84 | glBindTexture(texture->target, texture->id); | ||
85 | glUniform1i(location, texture_unit); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | static void set_mat4_uniform( | ||
90 | GLuint prog, const char* name, const mat4* mats, size_t count) { | ||
91 | assert(prog != 0); | ||
92 | assert(name); | ||
93 | assert(mats); | ||
94 | |||
95 | const GLint location = glGetUniformLocation(prog, name); | ||
96 | if (location >= 0) { | ||
97 | glUniformMatrix4fv(location, count, GL_FALSE, (const float*)mats); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | static void set_vec3_uniform(GLuint prog, const char* name, vec3 value) { | ||
102 | assert(prog != 0); | ||
103 | assert(name); | ||
104 | |||
105 | const GLint location = glGetUniformLocation(prog, name); | ||
106 | if (location >= 0) { | ||
107 | glUniform3f(location, value.x, value.y, value.z); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | static void set_vec4_uniform(GLuint prog, const char* name, vec4 value) { | ||
112 | assert(prog != 0); | ||
113 | assert(name); | ||
114 | |||
115 | const GLint location = glGetUniformLocation(prog, name); | ||
116 | if (location >= 0) { | ||
117 | glUniform4f(location, value.x, value.y, value.z, value.w); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void set_float_uniform(GLuint prog, const char* name, float value) { | ||
122 | assert(prog != 0); | ||
123 | assert(name); | ||
124 | |||
125 | const GLint location = glGetUniformLocation(prog, name); | ||
126 | if (location >= 0) { | ||
127 | glUniform1f(location, value); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | void gfx_apply_uniforms(const ShaderProgram* prog) { | ||
132 | assert(prog); | ||
133 | |||
134 | int next_texture_unit = 0; | ||
135 | for (int i = 0; i < prog->num_uniforms; ++i) { | ||
136 | const ShaderUniform* uniform = &prog->uniforms[i]; | ||
137 | switch (uniform->type) { | ||
138 | case UniformTexture: | ||
139 | set_texture_uniform( | ||
140 | prog->id, uniform->name.str, next_texture_unit, | ||
141 | uniform->value.texture); | ||
142 | next_texture_unit++; | ||
143 | break; | ||
144 | case UniformMat4: | ||
145 | set_mat4_uniform(prog->id, uniform->name.str, &uniform->value.mat4, 1); | ||
146 | break; | ||
147 | case UniformVec3: | ||
148 | set_vec3_uniform(prog->id, uniform->name.str, uniform->value.vec3); | ||
149 | break; | ||
150 | case UniformVec4: | ||
151 | set_vec4_uniform(prog->id, uniform->name.str, uniform->value.vec4); | ||
152 | break; | ||
153 | case UniformFloat: | ||
154 | set_float_uniform(prog->id, uniform->name.str, uniform->value.scalar); | ||
155 | break; | ||
156 | case UniformMat4Array: | ||
157 | set_mat4_uniform( | ||
158 | prog->id, uniform->name.str, uniform->value.array.values, | ||
159 | uniform->value.array.count); | ||
160 | break; | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | // Get the ShaderUniform object by name from the shader program if it already | ||
166 | // exists, or allocate a new one otherwise. | ||
167 | static ShaderUniform* get_or_allocate_uniform( | ||
168 | ShaderProgram* prog, const char* name) { | ||
169 | assert(prog); | ||
170 | assert(name); | ||
171 | |||
172 | // First search for the uniform in the list. | ||
173 | for (int i = 0; i < prog->num_uniforms; ++i) { | ||
174 | ShaderUniform* uniform = &prog->uniforms[i]; | ||
175 | if (sstring_eq_cstr(uniform->name, name)) { | ||
176 | return uniform; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | // Create the uniform if it does not exist. | ||
181 | if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) { | ||
182 | FAIL("Exceeded the maximum number of uniforms per shader. Please increase " | ||
183 | "this value."); | ||
184 | return 0; | ||
185 | } | ||
186 | ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms]; | ||
187 | prog->num_uniforms++; | ||
188 | return uniform; | ||
189 | } | ||
190 | |||
191 | // The functions below save the value of a uniform in the shader program. If the | ||
192 | // uniform does not even exist, then there is no need to store the value. | ||
193 | |||
194 | void gfx_set_texture_uniform( | ||
195 | ShaderProgram* prog, const char* name, const Texture* texture) { | ||
196 | assert(prog); | ||
197 | assert(name); | ||
198 | assert(texture); | ||
199 | |||
200 | const GLint location = glGetUniformLocation(prog->id, name); | ||
201 | if (location < 0) { | ||
202 | return; | ||
203 | } | ||
204 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | ||
205 | assert(uniform); | ||
206 | uniform->name = sstring_make(name); | ||
207 | uniform->type = UniformTexture; | ||
208 | uniform->value.texture = texture; | ||
209 | } | ||
210 | |||
211 | void gfx_set_mat4_uniform( | ||
212 | ShaderProgram* prog, const char* name, const mat4* mat) { | ||
213 | assert(prog); | ||
214 | assert(name); | ||
215 | assert(mat); | ||
216 | |||
217 | const GLint location = glGetUniformLocation(prog->id, name); | ||
218 | if (location < 0) { | ||
219 | return; | ||
220 | } | ||
221 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | ||
222 | assert(uniform); | ||
223 | uniform->name = sstring_make(name); | ||
224 | uniform->type = UniformMat4; | ||
225 | uniform->value.mat4 = *mat; | ||
226 | } | ||
227 | |||
228 | void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) { | ||
229 | assert(prog); | ||
230 | assert(name); | ||
231 | |||
232 | const GLint location = glGetUniformLocation(prog->id, name); | ||
233 | if (location < 0) { | ||
234 | return; | ||
235 | } | ||
236 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | ||
237 | assert(uniform); | ||
238 | uniform->name = sstring_make(name); | ||
239 | uniform->type = UniformVec3; | ||
240 | uniform->value.vec3 = value; | ||
241 | } | ||
242 | |||
243 | void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) { | ||
244 | assert(prog); | ||
245 | assert(name); | ||
246 | |||
247 | const GLint location = glGetUniformLocation(prog->id, name); | ||
248 | if (location < 0) { | ||
249 | return; | ||
250 | } | ||
251 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | ||
252 | assert(uniform); | ||
253 | uniform->name = sstring_make(name); | ||
254 | uniform->type = UniformVec4; | ||
255 | uniform->value.vec4 = value; | ||
256 | } | ||
257 | |||
258 | void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) { | ||
259 | assert(prog); | ||
260 | assert(name); | ||
261 | |||
262 | // No need to store the uniform on our side if it does not exist in the | ||
263 | // program. | ||
264 | const GLint location = glGetUniformLocation(prog->id, name); | ||
265 | if (location < 0) { | ||
266 | return; | ||
267 | } | ||
268 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | ||
269 | assert(uniform); | ||
270 | uniform->name = sstring_make(name); | ||
271 | uniform->type = UniformFloat; | ||
272 | uniform->value.scalar = value; | ||
273 | } | ||
274 | |||
275 | void gfx_set_mat4_array_uniform( | ||
276 | ShaderProgram* prog, const char* name, const mat4* mats, size_t count) { | ||
277 | assert(prog); | ||
278 | assert(name); | ||
279 | assert(mats); | ||
280 | |||
281 | const GLint location = glGetUniformLocation(prog->id, name); | ||
282 | if (location < 0) { | ||
283 | return; | ||
284 | } | ||
285 | ShaderUniform* uniform = get_or_allocate_uniform(prog, name); | ||
286 | assert(uniform); | ||
287 | uniform->name = sstring_make(name); | ||
288 | uniform->type = UniformMat4Array; | ||
289 | uniform->value.array.count = count; | ||
290 | uniform->value.array.values = mats; | ||
291 | } | ||