A 3D game engine from scratch.
1// (c) 2020 Vlad-Stefan Harbuz <vlad@vladh.net>
2
3#include "../src_external/pstr.h"
4#include "memory.hpp"
5#include "logs.hpp"
6#include "files.hpp"
7#include "shaders.hpp"
8#include "intrinsics.hpp"
9
10
11const char*
12shaders::shader_type_to_string(shaders::Type shader_type)
13{
14 if (shader_type == shaders::Type::none) {
15 return "none";
16 } else if (shader_type == shaders::Type::standard) {
17 return "standard";
18 } else if (shader_type == shaders::Type::depth) {
19 return "depth";
20 } else {
21 logs::error("Could not get string for shaders::Type: %d", shader_type);
22 return "<unknown>";
23 }
24}
25
26
27shaders::Type
28shaders::shader_type_from_string(const char* str)
29{
30 if (strcmp(str, "none") == 0) {
31 return shaders::Type::none;
32 } else if (strcmp(str, "standard") == 0) {
33 return shaders::Type::standard;
34 } else if (strcmp(str, "depth") == 0) {
35 return shaders::Type::depth;
36 } else {
37 logs::fatal("Could not parse shaders::Type: %s", str);
38 return shaders::Type::none;
39 }
40}
41
42
43bool
44shaders::is_shader_asset_valid(shaders::Asset *shader_asset)
45{
46 return shader_asset->program > 0;
47}
48
49
50void
51shaders::set_int(
52 shaders::Asset *shader_asset, const char *uniform_name, u32 value
53) {
54 i32 location = get_uniform_location(shader_asset, uniform_name);
55 if (location >= 0) {
56 glUniform1i(location, value);
57 }
58}
59
60
61void
62shaders::set_bool(
63 shaders::Asset *shader_asset, const char *uniform_name, bool value
64) {
65 set_int(shader_asset, uniform_name, (u32)value);
66}
67
68
69void
70shaders::set_float(
71 shaders::Asset *shader_asset, const char *uniform_name, float value
72) {
73 i32 location = get_uniform_location(shader_asset, uniform_name);
74 if (location >= 0) {
75 glUniform1f(location, value);
76 }
77}
78
79
80void
81shaders::set_vec2(shaders::Asset *shader_asset, const char *uniform_name, v2 *value)
82{
83 i32 location = get_uniform_location(shader_asset, uniform_name);
84 if (location >= 0) {
85 glUniform2fv(location, 1, glm::value_ptr(*value));
86 }
87}
88
89
90void
91shaders::set_vec3(shaders::Asset *shader_asset, const char *uniform_name, v3 *value)
92{
93 i32 location = get_uniform_location(shader_asset, uniform_name);
94 if (location >= 0) {
95 glUniform3fv(location, 1, glm::value_ptr(*value));
96 }
97}
98
99
100void
101shaders::set_vec4(shaders::Asset *shader_asset, const char *uniform_name, v4 *value)
102{
103 i32 location = get_uniform_location(shader_asset, uniform_name);
104 if (location >= 0) {
105 glUniform4fv(location, 1, glm::value_ptr(*value));
106 }
107}
108
109
110void
111shaders::set_mat2(shaders::Asset *shader_asset, const char *uniform_name, m2 *mat)
112{
113 i32 location = get_uniform_location(shader_asset, uniform_name);
114 if (location >= 0) {
115 glUniformMatrix2fv(location, 1, GL_FALSE, glm::value_ptr(*mat));
116 }
117}
118
119
120void
121shaders::set_mat3(shaders::Asset *shader_asset, const char *uniform_name, m3 *mat)
122{
123 i32 location = get_uniform_location(shader_asset, uniform_name);
124 if (location >= 0) {
125 glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(*mat));
126 }
127}
128
129
130void
131shaders::set_mat4_multiple(
132 shaders::Asset *shader_asset, u32 n, const char *uniform_name, m4 *mat
133) {
134 i32 location = get_uniform_location(shader_asset, uniform_name);
135 if (location >= 0) {
136 glUniformMatrix4fv(location, n, GL_FALSE, glm::value_ptr(*mat));
137 }
138}
139
140
141void
142shaders::set_mat4(shaders::Asset *shader_asset, const char *uniform_name, m4 *mat)
143{
144 i32 location = get_uniform_location(shader_asset, uniform_name);
145 if (location >= 0) {
146 glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(*mat));
147 }
148}
149
150
151void
152shaders::reset_texture_units(shaders::Asset *shader_asset)
153{
154 shader_asset->n_texture_units = 0;
155}
156
157
158u32
159shaders::add_texture_unit(
160 shaders::Asset *shader_asset,
161 u32 new_texture_unit,
162 GLenum new_texture_unit_type
163) {
164 u32 idx = ++shader_asset->n_texture_units;
165 shader_asset->texture_units[idx] = new_texture_unit;
166 shader_asset->texture_unit_types[idx] = new_texture_unit_type;
167 return idx;
168}
169
170
171void
172shaders::load_shader_asset(shaders::Asset *shader_asset, memory::Pool *memory_pool)
173{
174 shader_asset->did_set_texture_uniforms = false;
175
176 if (!pstr_is_empty(shader_asset->geom_path)) {
177 shader_asset->program = make_program(
178 make_shader(shader_asset->vert_path, load_file(memory_pool, shader_asset->vert_path),
179 GL_VERTEX_SHADER),
180 make_shader(shader_asset->frag_path, load_frag_file(memory_pool, shader_asset->frag_path),
181 GL_FRAGMENT_SHADER),
182 make_shader(shader_asset->geom_path, load_file(memory_pool, shader_asset->geom_path),
183 GL_GEOMETRY_SHADER));
184 } else {
185 shader_asset->program = make_program(
186 make_shader(shader_asset->vert_path, load_file(memory_pool, shader_asset->vert_path),
187 GL_VERTEX_SHADER),
188 make_shader(shader_asset->frag_path, load_frag_file(memory_pool, shader_asset->frag_path),
189 GL_FRAGMENT_SHADER));
190 }
191
192 load_uniforms(shader_asset);
193}
194
195
196shaders::Asset*
197shaders::init_shader_asset(
198 shaders::Asset *shader_asset,
199 memory::Pool *memory_pool,
200 const char *new_name, shaders::Type new_type,
201 const char *vert_path, const char *frag_path, const char *geom_path
202) {
203 *shader_asset = {};
204 pstr_copy(shader_asset->name, MAX_DEBUG_NAME_LENGTH, new_name);
205 shader_asset->type = new_type;
206 shader_asset->n_texture_units = 0;
207 shader_asset->did_set_texture_uniforms = false;
208 memset(shader_asset->texture_units, 0, sizeof(shader_asset->texture_units));
209 memset(shader_asset->texture_unit_types, 0, sizeof(shader_asset->texture_unit_types));
210 if (!vert_path) {
211 logs::warning("Loading shader asset with no vertex shader");
212 }
213 if (!frag_path) {
214 logs::warning("Loading shader asset with no fragment shader");
215 }
216 if (vert_path) {
217 pstr_copy(shader_asset->vert_path, MAX_PATH, vert_path);
218 }
219 if (frag_path) {
220 pstr_copy(shader_asset->frag_path, MAX_PATH, frag_path);
221 }
222 if (geom_path) {
223 pstr_copy(shader_asset->geom_path, MAX_PATH, geom_path);
224 }
225 load_shader_asset(shader_asset, memory_pool);
226 return shader_asset;
227}
228
229
230void
231shaders::destroy_shader_asset(shaders::Asset *shader_asset)
232{
233 glDeleteProgram(shader_asset->program);
234}
235
236
237void
238shaders::assert_shader_status_ok(u32 new_shader, const char *path)
239{
240 i32 status;
241 glGetShaderiv(new_shader, GL_COMPILE_STATUS, &status);
242
243 if (status != 1) {
244 char message[MAX_GENEROUS_STRING_LENGTH];
245 glGetShaderInfoLog(new_shader, MAX_GENEROUS_STRING_LENGTH, NULL, message);
246 logs::info("Compiling shader %s: (status %d) (message %s)", path, status, message);
247 logs::error("Shader compilation failed");
248 exit(EXIT_FAILURE);
249 }
250}
251
252
253void
254shaders::assert_program_status_ok(u32 new_program)
255{
256 i32 status;
257 glGetProgramiv(new_program, GL_LINK_STATUS, &status);
258
259 if (status != 1) {
260 char message[MAX_GENEROUS_STRING_LENGTH];
261 glGetProgramInfoLog(new_program, MAX_GENEROUS_STRING_LENGTH, NULL, message);
262 logs::info("Compiling program %d: (status %d) (message %s)", new_program, status, message);
263 logs::error("Program loading failed");
264 exit(EXIT_FAILURE);
265 }
266}
267
268
269u32
270shaders::make_shader(const char *path, const char *source, GLenum shader_type)
271{
272 u32 shader = glCreateShader(shader_type);
273 glShaderSource(shader, 1, &source, NULL);
274 glCompileShader(shader);
275 assert_shader_status_ok(shader, path);
276 return shader;
277}
278
279
280u32
281shaders::make_program(u32 vertex_shader, u32 fragment_shader)
282{
283 u32 new_program = glCreateProgram();
284 glAttachShader(new_program, vertex_shader);
285 glAttachShader(new_program, fragment_shader);
286 glLinkProgram(new_program);
287 glDetachShader(new_program, vertex_shader);
288 glDetachShader(new_program, fragment_shader);
289 assert_program_status_ok(new_program);
290 return new_program;
291}
292
293
294u32
295shaders::make_program(
296 u32 vertex_shader, u32 fragment_shader, u32 geometry_shader
297) {
298 u32 new_program = glCreateProgram();
299 glAttachShader(new_program, vertex_shader);
300 glAttachShader(new_program, fragment_shader);
301 glAttachShader(new_program, geometry_shader);
302 glLinkProgram(new_program);
303 glDetachShader(new_program, vertex_shader);
304 glDetachShader(new_program, fragment_shader);
305 glDetachShader(new_program, geometry_shader);
306 assert_program_status_ok(new_program);
307 return new_program;
308}
309
310
311char const *
312shaders::load_file(memory::Pool *memory_pool, const char *path)
313{
314 char full_path[MAX_PATH];
315 strcpy(full_path, SHADER_DIR); // TODO: Fix unsafe strings?
316 strcat(full_path, path);
317 u32 f1_size = files::get_file_size(SHADER_COMMON_PATH);
318 u32 f2_size = files::get_file_size(full_path);
319 char *file_memory = (char*)memory::push(memory_pool, f1_size + f2_size + 1, full_path);
320 files::load_file(file_memory, SHADER_COMMON_PATH);
321 files::load_file(file_memory + f1_size, full_path);
322 return file_memory;
323}
324
325
326char const *
327shaders::load_frag_file(memory::Pool *memory_pool, const char *path)
328{
329 char full_path[MAX_PATH];
330 strcpy(full_path, SHADER_DIR); // TODO: Fix unsafe strings?
331 strcat(full_path, path);
332 u32 f1_size = files::get_file_size(SHADER_COMMON_PATH);
333 u32 f2_size = files::get_file_size(SHADER_COMMON_FRAGMENT_PATH);
334 u32 f3_size = files::get_file_size(full_path);
335 char *file_memory = (char*)memory::push(memory_pool, f1_size + f2_size + f3_size + 1, full_path);
336 files::load_file(file_memory, SHADER_COMMON_PATH);
337 files::load_file(file_memory + f1_size, SHADER_COMMON_FRAGMENT_PATH);
338 files::load_file(file_memory + f1_size + f2_size, full_path);
339 return file_memory;
340}
341
342
343i32
344shaders::get_uniform_location(
345 shaders::Asset *shader_asset,
346 const char *uniform_name
347) {
348 // Look up in cache.
349 for (u32 idx = 0; idx < shader_asset->n_intrinsic_uniforms; idx++) {
350 if (strcmp(shader_asset->intrinsic_uniform_names[idx], uniform_name) == 0) {
351 return shader_asset->intrinsic_uniform_locations[idx];
352 }
353 }
354
355 // Missed cache, look up with `glGetUniformLocation()`.
356 GLint location = glGetUniformLocation(shader_asset->program, uniform_name);
357
358 if (location == -1) {
359 logs::error("Could not get uniform: %s", uniform_name);
360 } else {
361 logs::info("Missed uniform cache for %s", uniform_name);
362 }
363
364 return location;
365}
366
367
368void
369shaders::load_uniforms(shaders::Asset *shader_asset)
370{
371 for (u16 idx = 0; idx < MAX_N_UNIFORMS; idx++) {
372 shader_asset->intrinsic_uniform_locations[idx] = -1;
373 }
374
375 // Bind uniform block
376 u32 uniform_block_index = glGetUniformBlockIndex(shader_asset->program, "shader_common");
377 glUniformBlockBinding(shader_asset->program, uniform_block_index, 0);
378
379 // Load uniforms
380 // NOTE: We may want to skip all shader_asset stuff, because fallback on loading the locations
381 // in `shader::set_*` anyway. But, it's kind of cool to know we're loading everything we can
382 // up front in shader_asset function.
383 u32 new_n_intrinsic_uniforms = 0;
384 GLint n_active_uniforms;
385 char uniform_name[MAX_UNIFORM_NAME_LENGTH];
386 GLint uniform_name_length;
387 GLint uniform_size;
388 GLenum uniform_type;
389
390 glGetProgramiv(shader_asset->program, GL_ACTIVE_UNIFORMS, &n_active_uniforms);
391
392 for (GLint idx = 0; idx < n_active_uniforms; idx++) {
393 glGetActiveUniform(shader_asset->program, (GLuint)idx, MAX_UNIFORM_NAME_LENGTH,
394 &uniform_name_length, &uniform_size, &uniform_type, uniform_name);
395 GLint location = glGetUniformLocation(shader_asset->program, uniform_name);
396 if (location != -1) {
397 shader_asset->intrinsic_uniform_locations[new_n_intrinsic_uniforms] = location;
398 strcpy(shader_asset->intrinsic_uniform_names[new_n_intrinsic_uniforms], uniform_name);
399 new_n_intrinsic_uniforms++;
400 }
401 }
402
403 shader_asset->n_intrinsic_uniforms = new_n_intrinsic_uniforms;
404}