polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

commit 534606327d29470da8f36a285081e6dcad721f8c
parent 4b1cce3d38e145ca6e13e7b16b8e4b308f13ebf6
Author: William Casarin <jb55@jb55.com>
Date:   Mon, 29 Oct 2018 17:00:47 -0700

dependency tracking on includes for reload_shader

Diffstat:
Metc/shaders/lighting.glsl | 17+++++++++++++++++
Metc/shaders/terrain.glsl | 4++--
Metc/shaders/test.f.glsl | 1+
Metc/shaders/ui.f.glsl | 2+-
Msrc/game.c | 3++-
Msrc/game.h | 14++++++++++----
Msrc/render.c | 23++++++++++++++---------
Msrc/shader.c | 41++++++++++++++++++++++++++++++++++-------
Msrc/shader.h | 10++++++----
Msrc/ui.c | 9+++++----
Msrc/ui.h | 6++++--
Msrc/update.c | 28++++++++++++++++++----------
12 files changed, 114 insertions(+), 44 deletions(-)

diff --git a/etc/shaders/lighting.glsl b/etc/shaders/lighting.glsl @@ -11,6 +11,23 @@ vec3 standard_light(vec3 color) { return color * costheta; } + +const vec3 spherical_harmonics[4] = vec3[]( + vec3( 0.754554516862612, 0.748542953903366, 0.790921515418539 ), + vec3( -0.083856548007422, 0.092533500963210, 0.322764661032516 ), + vec3( 0.308152705331738, 0.366796330467391, 0.466698181299906 ), + vec3( -0.188884931542396, -0.277402551592231, -0.377844212327557 ) +); + +vec3 irradiance_spherical_harmonics(const vec3 n) { + return max( + spherical_harmonics[0] + + spherical_harmonics[1] * n.y + + spherical_harmonics[2] * n.z + + spherical_harmonics[3] * n.x + , 0.0); +} + vec3 gamma_correct(vec3 color) { return pow(color, vec3(1.0/2.2)); } diff --git a/etc/shaders/terrain.glsl b/etc/shaders/terrain.glsl @@ -17,6 +17,7 @@ const int nlands = 6; #include lighting.glsl + const vec4 land[nlands] = vec4[]( vec4(0.0, 0.5, 0.79, 0.0), // 0 - water vec4(0.9176, 0.8156, 0.6588, 1.0), // 1 - sand @@ -33,8 +34,7 @@ void main() shadow_coord = depth_mvp * v4_pos; vec3 color = land[0].xyz; - - for (int i = 0; i < nlands-1; i++) { + for (int i = 0; i < nlands-1; i++) { color = mix(color, land[i+1].xyz, diff --git a/etc/shaders/test.f.glsl b/etc/shaders/test.f.glsl @@ -49,6 +49,7 @@ void main() { color = frag; float shadow_map_z = texture(shadow_map, shadow_coord.xy).z; + if (light_dir.z > 0.0 && shadow_map_z < shadow_coord.z ) { float factor = 1.0/dot(light_dir, vec3(0.0, 0.0, 1.0)); visibility = clamp(0.2 * factor, 0.5, 1.0); diff --git a/etc/shaders/ui.f.glsl b/etc/shaders/ui.f.glsl @@ -11,5 +11,5 @@ uniform sampler2D screen_texture; void main() { // fragmentColor = vec4(color + diffuse, 1.0); - fragmentColor = texture(screen_texture, v_tex_coords) + vec4(v_color, 1.0) * 0.000001; + fragmentColor = texture(screen_texture, v_tex_coords) + vec4(v_color, 0.0) * 0.0001; } diff --git a/src/game.c b/src/game.c @@ -67,7 +67,8 @@ void game_init(struct game *game, int width, int height) { res->proj_ortho ); - create_ui(&game->ui, width, height); + create_ui(&game->ui, width, height, &res->programs[UI_PROGRAM]); + check_gl(); terrain_init(terrain); diff --git a/src/game.h b/src/game.h @@ -14,6 +14,15 @@ #define PLAYER_HEIGHT 1.73 +#define MAX_PROGRAMS 12 + +enum program_type { + DEFAULT_PROGRAM, + TERRAIN_PROGRAM, + UI_PROGRAM, + NUM_PROGRAMS, +}; + /* * Global data used by our render callback: * NOTE: just for testing right now @@ -22,10 +31,7 @@ struct resources { struct vbo vertex_buffer, element_buffer, normal_buffer; struct fbo shadow_buffer; - struct gpu_program program; - struct gpu_program smooth_program; - struct gpu_program terrain_program; - struct gpu_program ui_program; + struct gpu_program programs[NUM_PROGRAMS]; struct uniforms { GLint camera_position; diff --git a/src/render.c b/src/render.c @@ -81,17 +81,19 @@ init_gl(struct resources *resources, int width, int height) { resources->proj_persp); // Shader program - ok = make_program(&terrain_vertex, &fragment, &resources->terrain_program); + ok = make_program(&terrain_vertex, &fragment, + &resources->programs[TERRAIN_PROGRAM]); + assert(ok && "terrain program"); check_gl(); - ok = make_program(&vertex, &fragment, &resources->program); + ok = make_program(&vertex, &fragment, &resources->programs[DEFAULT_PROGRAM]); assert(ok && "vertex-color program"); check_gl(); GLuint programs[] = - { resources->terrain_program.handle - , resources->program.handle + { resources->programs[TERRAIN_PROGRAM].handle + , resources->programs[DEFAULT_PROGRAM].handle }; // uniforms shared between all shaders @@ -139,7 +141,8 @@ init_gl(struct resources *resources, int width, int height) { // TODO: auto-generate these somehow? resources->attributes.color = - (gpu_addr)glGetAttribLocation(resources->program.handle, "color"); + (gpu_addr)glGetAttribLocation(resources->programs[DEFAULT_PROGRAM] + .handle, "color"); check_gl(); } @@ -185,6 +188,8 @@ void render (struct game *game, struct render_config *config) { }; int is_shadow = config->draw_ui == 0; + int terrain_program = game->test_resources.programs[TERRAIN_PROGRAM].handle; + int default_program = game->test_resources.programs[DEFAULT_PROGRAM].handle; for (size_t i = 0; i < ARRAY_SIZE(entities); ++i) { struct entity *entity = entities[i]; @@ -192,9 +197,9 @@ void render (struct game *game, struct render_config *config) { continue; // TODO this is a bit wonky, refactor this if (i == 0) - glUseProgram(game->test_resources.terrain_program.handle); + glUseProgram(terrain_program); else if (i == 1) - glUseProgram(game->test_resources.program.handle); + glUseProgram(default_program); check_gl(); mat4_inverse(camera, view); @@ -242,8 +247,8 @@ void render (struct game *game, struct render_config *config) { check_gl(); } - /* if (config->draw_ui) */ - /* render_ui(&game->ui, view); */ + if (config->draw_ui) + render_ui(&game->ui, view); //player // y tho diff --git a/src/shader.c b/src/shader.c @@ -12,7 +12,9 @@ #define MAX_LINES 4096 static int file_buf_count = 0; -static char *file_buffers[12]; +static char *file_buffers[MAX_SHADER_INCLUDES]; +static char *file_names[MAX_SHADER_INCLUDES]; +static int file_name_lens[MAX_SHADER_INCLUDES]; static char *line_buff[MAX_LINES]; static u32 line_lens[MAX_LINES]; @@ -36,8 +38,12 @@ static char **resolve_imports(char *contents, int *nlines) { int size = sizeof("#include"); if (memcmp(line, "#include ", size) == 0) { char *filename = line + size; - snprintf(fname_buf, 32, SHADER("%.*s"), line_len-size-1, filename); + int file_name_len = line_len - size - 1; + snprintf(fname_buf, 32, SHADER("%.*s"), file_name_len, filename); + file_name_lens[file_buf_count] = file_name_len; + file_names[file_buf_count] = filename; /* printf("got include %s\n", fname_buf); */ + // TODO (perf): cache file contents based on filename resolved_contents = file_contents(fname_buf, &file_len); file_buffers[file_buf_count++] = resolved_contents; resolve_imports(resolved_contents, nlines); @@ -55,6 +61,7 @@ static char **resolve_imports(char *contents, int *nlines) { int make_shader(GLenum type, const char *filename, struct shader *shader) { size_t length; int nlines = 0; + static char fname_buf[32]; GLchar *source = (GLchar *)file_contents(filename, &length); if (!source) return 0; @@ -63,6 +70,7 @@ int make_shader(GLenum type, const char *filename, struct shader *shader) { GLint shader_ok; + shader->n_includes = 0; shader->filename = filename; shader->type = type; shader->handle = glCreateShader(type); @@ -71,8 +79,19 @@ int make_shader(GLenum type, const char *filename, struct shader *shader) { (const int*)line_lens); free(source); - for (int i = 0; i < file_buf_count; ++i) + + // shader dependency stuff + for (int i = 0; i < file_buf_count; ++i) { + assert(i < MAX_SHADER_INCLUDES); + char *p = shader->includes[shader->n_includes]; + int name_len = file_name_lens[i]; + assert(name_len < MAX_INCLUDE_FNAME_LEN); + snprintf(p, MAX_INCLUDE_FNAME_LEN, SHADER("%.*s"), name_len, file_names[i]); + shader->include_mtimes[shader->n_includes] = file_mtime(p); + /* printf("including %s as dep of %s\n", p, name_len, shader->filename); */ + shader->n_includes++; free(file_buffers[i]); + } file_buf_count = 0; glCompileShader(shader->handle); @@ -86,10 +105,8 @@ int make_shader(GLenum type, const char *filename, struct shader *shader) { return 0; } -#ifdef DEBUG shader->load_mtime = file_mtime(shader->filename); -#endif return 1; } @@ -111,6 +128,17 @@ int reload_program(struct gpu_program *program) { int frag_changed = frag_mtime != program->fragment.load_mtime; + for (int i = 0; i < program->vertex.n_includes; ++i) { + time_t include_mtime = program->vertex.include_mtimes[i]; + time_t new_mtime = file_mtime(program->vertex.includes[i]); + vert_changed |= include_mtime != new_mtime; + } + + for (int i = 0; i < program->fragment.n_includes; ++i) { + time_t include_mtime = program->fragment.include_mtimes[i]; + frag_changed |= include_mtime != file_mtime(program->fragment.includes[i]); + } + if (!vert_changed && !frag_changed) return 2; @@ -174,8 +202,7 @@ make_program(struct shader *vertex, struct shader *fragment, assert(vertex); assert(fragment); - program->handle = - glCreateProgram(); + program->handle = glCreateProgram(); program->fragment = *fragment; program->vertex = *vertex; diff --git a/src/shader.h b/src/shader.h @@ -6,13 +6,17 @@ #define SHADER(f) "etc/shaders/" f +#define MAX_SHADER_INCLUDES 8 +#define MAX_INCLUDE_FNAME_LEN 32 + struct shader { GLenum type; GLuint handle; + int n_includes; const char *filename; -#ifdef DEBUG + char includes[MAX_SHADER_INCLUDES][MAX_INCLUDE_FNAME_LEN]; + time_t include_mtimes[MAX_SHADER_INCLUDES]; time_t load_mtime; -#endif }; struct gpu_program { @@ -22,9 +26,7 @@ struct gpu_program { }; -#ifdef DEBUG int reload_program(struct gpu_program *program); -#endif int make_shader(GLenum type, const char *filename, struct shader *shader); diff --git a/src/ui.c b/src/ui.c @@ -50,7 +50,7 @@ static void create_quad(struct geometry *geom) void render_ui(struct ui *ui, float *view) { static float mvp[MAT4_ELEMS]; glDisable(GL_DEPTH_TEST); - glUseProgram(ui->shader.handle); + glUseProgram(ui->shader->handle); check_gl(); float uipos[2] = {0.01, 0.01}; @@ -85,11 +85,12 @@ void resize_ui(struct ui *ui, int width, int height) { } -void create_ui(struct ui *ui, int width, int height) { +void create_ui(struct ui *ui, int width, int height, struct gpu_program *shader) { struct shader vertex; struct shader fragment; int ok = 0; + ui->shader = shader; create_quad(&ui->quad); check_gl(); @@ -99,10 +100,10 @@ void create_ui(struct ui *ui, int width, int height) { ok = make_shader(GL_FRAGMENT_SHADER, SHADER("ui.f.glsl"), &fragment); assert(ok && "ui fragment shader"); - ok = make_program(&vertex, &fragment, &ui->shader); + ok = make_program(&vertex, &fragment, ui->shader); assert(ok && "ui program"); - GLuint program = ui->shader.handle; + GLuint program = ui->shader->handle; ui->uniforms.mvp = glGetUniformLocation(program, "mvp"); ui->uniforms.uipos = glGetUniformLocation(program, "uipos"); diff --git a/src/ui.h b/src/ui.h @@ -7,7 +7,7 @@ #include "geometry.h" struct ui { - struct gpu_program shader; + struct gpu_program *shader; struct geometry quad; struct attributes attrs; @@ -21,7 +21,9 @@ struct ui { float ortho[MAT4_ELEMS]; }; -void create_ui(struct ui *ui, int width, int height); +void create_ui(struct ui *ui, int width, int height, + struct gpu_program *); + void resize_ui(struct ui *ui, int width, int height); void render_ui(struct ui *ui, float *camera); diff --git a/src/update.c b/src/update.c @@ -183,16 +183,24 @@ static void player_movement(struct game *game) { #ifdef DEBUG static int try_reload_shaders(struct resources *res) { int ret; - printf("reloading shaders... "); - - ret = reload_program(&res->program); + for (int i = 0; i < NUM_PROGRAMS; ++i) { + struct gpu_program *program = &res->programs[i]; + ret = reload_program(program); + + if (ret == 2) {} + else if (ret == 1) + printf("reload %s + %s success.\n", + program->vertex.filename, + program->fragment.filename); + else + printf("reload %s + %s failed.\n", + program->vertex.filename, + program->fragment.filename); + + // failure ok, clear any errors + glGetError(); + } - if (ret == 2) - printf("nothing to reload\n"); - else if (ret == 1) - printf("success.\n"); - else - printf("failed.\n"); @@ -227,7 +235,7 @@ void resize_fbos(struct game *game, int width, int height) { // TODO: match based on some real concept of time static void day_night_cycle(float n, struct resources *res) { float darkest = 0.3; - float val = n*100.0; + float val = 9950.0; float roots = vec3_dot(res->light_dir, V3(0.0, 0.0, 1.0)); float intensity = clamp(roots, darkest, 1.0); float light_pos[3];