A 3D game engine from scratch.
at main 404 lines 12 kB view raw
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}