commit 9bdcd6a7afa5feab002d9d8e02682eb911026853
parent 03c31f0aff35f83f528646d4a36338a09595911b
Author: William Casarin <jb55@jb55.com>
Date: Sun, 23 Sep 2018 12:33:11 -0700
shader hot reloading
Diffstat:
11 files changed, 372 insertions(+), 238 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,14 +1,14 @@
NAME ?= polyadvent
BIN ?= $(NAME)
PREFIX ?= /usr/local
-CFLAGS = -ggdb -Ofast -I src -Wall -Werror -Wextra -std=c99 \
+DEFS= -DGLFW_INCLUDE_NONE -DDEBUG
+CFLAGS = $(DEFS) -ggdb -Ofast -I src -Wall -Werror -Wextra -std=c99 \
-Wno-unused-function \
-Wno-unused-parameter \
-Wno-unused-variable \
-Wno-cast-align \
-Wno-padded
LDFLAGS = -lSDL2 -lGL
-DEFS= -DGLFW_INCLUDE_NONE
SRC=src
OBJS = $(SRC)/window.o
@@ -52,7 +52,7 @@ include $(OBJS:.o=.d)
rm -f $@.$$$$
$(BIN): $(OBJS)
- $(CC) $(CFLAGS) $(DEFS) $^ $(LDFLAGS) -o $@
+ $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
install: $(BIN)
install -d $(PREFIX)/bin
diff --git a/etc/shaders/test.f.glsl b/etc/shaders/test.f.glsl
@@ -14,7 +14,7 @@ uniform bool diffuse_on;
uniform mat4 normal_matrix;
vec3 apply_fog(in vec3 rgb, in float distance, in vec3 ray_orig, in vec3 ray_dir) {
- const float b = 0.00046;
+ const float b = 0.00036;
const float v = 1.0;
const float zs = 1.0;
diff --git a/etc/shaders/test.v.glsl b/etc/shaders/test.v.glsl
@@ -20,23 +20,27 @@ out vec3 v_ray;
void main()
{
- vec4 v4_normal = vec4(normal, 1);
- vec4 trans_normal = normal_matrix * v4_normal;
- vec4 v4_pos = vec4(position, 1.0);
- gl_Position = mvp * v4_pos;
- v_light = dot(trans_normal, vec4(normalize(light_dir), 0));
-
- if (position.z <= 1.0)
- v_color = vec3(0.0, 0.5, 0.79);
- else if (position.z <= 2.0)
- v_color = vec3(0.9176, 0.8156, 0.6588);
- else if (position.z <= 5.0)
- v_color = vec3(0.6274, 0.749, 0.156);
- else if (position.z <= 100.0)
- v_color = vec3(0.5, 0.5, 0.5);
- else
- v_color = vec3(1.0, 1.0, 1.0) * 0.9;
-
- v_normal = trans_normal.xyz;
- v_ray = camera_position - (world * v4_pos).xyz;
+ vec4 v4_normal = vec4(normal, 1);
+ vec4 trans_normal = normal_matrix * v4_normal;
+ vec4 v4_pos = vec4(position, 1.0);
+ gl_Position = mvp * v4_pos;
+ v_light = dot(trans_normal, vec4(normalize(light_dir), 0));
+
+ // if (position.z <= 1.0)
+ // v_color = vec3(0.0, 0.5, 0.79);
+ // else if (position.z <= 2.0)
+ // v_color = vec3(0.9176, 0.8156, 0.6588);
+ // else if (position.z <= 20.0)
+ // v_color = vec3(0.6274, 0.749, 0.156);
+ // else if (position.z <= 60.0)
+ // v_color = vec3(0.8784, 0.8784, 0.7);
+ // else if (position.z <= 100.0)
+ // v_color = vec3(0.5, 0.5, 0.5)9
+ // else
+ // v_color = vec3(1.0, 1.0, 1.0) * 0.9;
+
+ v_color = vec3(position.z*0.05, position.z*0.005, position.z*0.0001) * 0.5;
+
+ v_normal = trans_normal.xyz;
+ v_ray = camera_position - (world * v4_pos).xyz;
}
diff --git a/src/file.c b/src/file.c
@@ -3,8 +3,17 @@
#include <stdlib.h>
#include "file.h"
-void *
-file_contents(const char *filename, size_t *length) {
+#include <sys/stat.h>
+
+time_t file_mtime(const char *filename) {
+ // TODO: windows file_mtime
+ struct stat stats;
+ stat(filename, &stats);
+
+ return stats.st_mtime;
+}
+
+void *file_contents(const char *filename, size_t *length) {
FILE *f = fopen(filename, "r");
void *buffer;
diff --git a/src/file.h b/src/file.h
@@ -1,6 +1,11 @@
#ifndef POLYADVEMT_FILE_H
#define POLYADVEMT_FILE_H
+#include <time.h>
+
+time_t
+file_mtime(const char *filename);
+
void *
file_contents(const char *filename, size_t *length);
diff --git a/src/game.c b/src/game.c
@@ -27,7 +27,7 @@ void game_init(struct game *game) {
struct terrain *terrain = &game->terrain;
mat4 *light_dir = game->test_resources.light_dir;
- const double size = 5000;
+ const double size = 20000;
const double pdist = 1.7;
terrain->settings = (struct perlin_settings){
diff --git a/src/game.h b/src/game.h
@@ -6,6 +6,7 @@
#include "input.h"
#include "node.h"
#include "terrain.h"
+#include "shader.h"
#define PLAYER_HEIGHT 1.7
@@ -14,36 +15,37 @@
* NOTE: just for testing right now
*/
struct resources {
- struct vbo vertex_buffer, element_buffer, normal_buffer;
- GLuint vertex_shader, fragment_shader, program;
-
- struct uniforms {
- GLint camera_position;
- GLint light_dir;
- GLint mvp;
- GLint normal_matrix;
- GLint view;
- GLint fog_on;
- GLint diffuse_on;
- GLint model_view;
- GLint world;
- } uniforms;
-
- struct attributes {
- gpu_addr position;
- gpu_addr normal;
- } attributes;
-
- struct node root;
- struct node player;
- struct node camera;
- struct node terrain_node;
-
- bool fog_on, diffuse_on;
-
- float test_mvp[MAT4_ELEMS];
- float light_dir[3];
- float camera_persp[MAT4_ELEMS];
+ struct vbo vertex_buffer, element_buffer, normal_buffer;
+
+ struct gpu_program program;
+
+ struct uniforms {
+ GLint camera_position;
+ GLint light_dir;
+ GLint mvp;
+ GLint normal_matrix;
+ GLint view;
+ GLint fog_on;
+ GLint diffuse_on;
+ GLint model_view;
+ GLint world;
+ } uniforms;
+
+ struct attributes {
+ gpu_addr position;
+ gpu_addr normal;
+ } attributes;
+
+ struct node root;
+ struct node player;
+ struct node camera;
+ struct node terrain_node;
+
+ bool fog_on, diffuse_on;
+
+ float test_mvp[MAT4_ELEMS];
+ float light_dir[3];
+ float camera_persp[MAT4_ELEMS];
};
struct game {
diff --git a/src/render.c b/src/render.c
@@ -56,89 +56,93 @@ static const GLushort cube_indices[] = {
void
init_gl(struct resources *resources, int width, int height) {
- float tmp_matrix[16];
- glEnable(GL_DEPTH_TEST);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
-
- // VBOs
- make_vertex_buffer(
- GL_ARRAY_BUFFER,
- cube_vertices,
- sizeof(cube_vertices),
- &resources->vertex_buffer
- );
+ struct shader vertex, fragment;
+ float tmp_matrix[16];
+ int ok = 0;
- // cube normals
- make_vertex_buffer(
- GL_ARRAY_BUFFER,
- cube_normals,
- sizeof(cube_normals),
- &resources->normal_buffer
- );
+ glEnable(GL_DEPTH_TEST);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
- // cube indices
- make_index_buffer(
- GL_ELEMENT_ARRAY_BUFFER,
- cube_indices,
- sizeof(cube_indices),
- &resources->element_buffer
- );
+ // VBOs
+ make_vertex_buffer(
+ GL_ARRAY_BUFFER,
+ cube_vertices,
+ sizeof(cube_vertices),
+ &resources->vertex_buffer
+ );
- // Shaders
- resources->vertex_shader = make_shader(
- GL_VERTEX_SHADER,
- SHADER("test.v.glsl")
- );
+ // cube normals
+ make_vertex_buffer(
+ GL_ARRAY_BUFFER,
+ cube_normals,
+ sizeof(cube_normals),
+ &resources->normal_buffer
+ );
- assert(resources->vertex_shader != 0);
- resources->fragment_shader = make_shader(
- GL_FRAGMENT_SHADER,
- SHADER("test.f.glsl")
- );
- assert(resources->fragment_shader != 0);
+ // cube indices
+ make_index_buffer(
+ GL_ELEMENT_ARRAY_BUFFER,
+ cube_indices,
+ sizeof(cube_indices),
+ &resources->element_buffer
+ );
+
+ // Shaders
+ ok =
+ make_shader(GL_VERTEX_SHADER, SHADER("test.v.glsl"), &vertex);
+
+ assert(ok);
+
+ ok =
+ make_shader(GL_FRAGMENT_SHADER, SHADER("test.f.glsl"), &fragment);
+
+ assert(ok);
- // camera
- mat4_perspective(90 /* fov */, (float)width / height, 1, 5000, resources->camera_persp);
+ // camera
+ mat4_perspective(90 /* fov */,
+ (float)width / height,
+ 1,
+ 5000,
+ resources->camera_persp);
- // Shader program
- resources->program =
- make_program(resources->vertex_shader, resources->fragment_shader);
+ // Shader program
+ ok =
+ make_program(&vertex, &fragment, &resources->program);
- assert(resources->program != 0);
+ assert(ok);
- // Program variables
- resources->uniforms.camera_position
- = glGetUniformLocation(resources->program, "camera_position");
+ // Program variables
+ resources->uniforms.camera_position =
+ glGetUniformLocation(resources->program.handle, "camera_position");
- resources->uniforms.light_dir
- = glGetUniformLocation(resources->program, "light_dir");
+ resources->uniforms.light_dir =
+ glGetUniformLocation(resources->program.handle, "light_dir");
- resources->uniforms.world
- = glGetUniformLocation(resources->program, "world");
+ resources->uniforms.world =
+ glGetUniformLocation(resources->program.handle, "world");
- resources->uniforms.fog_on
- = glGetUniformLocation(resources->program, "fog_on");
+ resources->uniforms.fog_on =
+ glGetUniformLocation(resources->program.handle, "fog_on");
- resources->uniforms.diffuse_on
- = glGetUniformLocation(resources->program, "diffuse_on");
+ resources->uniforms.diffuse_on =
+ glGetUniformLocation(resources->program.handle, "diffuse_on");
- resources->uniforms.mvp
- = glGetUniformLocation(resources->program, "mvp");
+ resources->uniforms.mvp =
+ glGetUniformLocation(resources->program.handle, "mvp");
- resources->uniforms.model_view
- = glGetUniformLocation(resources->program, "model_view");
+ resources->uniforms.model_view =
+ glGetUniformLocation(resources->program.handle, "model_view");
- resources->uniforms.normal_matrix
- = glGetUniformLocation(resources->program, "normal_matrix");
+ resources->uniforms.normal_matrix =
+ glGetUniformLocation(resources->program.handle, "normal_matrix");
- resources->attributes.normal
- = (gpu_addr)glGetAttribLocation(resources->program, "normal");
+ resources->attributes.normal =
+ (gpu_addr)glGetAttribLocation(resources->program.handle, "normal");
- resources->attributes.position
- = (gpu_addr)glGetAttribLocation(resources->program, "position");
+ resources->attributes.position =
+ (gpu_addr)glGetAttribLocation(resources->program.handle, "position");
- assert(resources->program != 0);
}
@@ -214,7 +218,7 @@ void render (struct game *game, struct geometry *geom) {
struct node *player = &res->player;
struct node *camera = &res->camera;
- glUseProgram(res->program);
+ glUseProgram(res->program.handle);
/* static float v3[] = { 1, 1, 0 }; */
/* v3[1] = fade_factor * 1.4f; */
diff --git a/src/shader.c b/src/shader.c
@@ -1,17 +1,18 @@
#include <stdlib.h>
#include <stdio.h>
+#include <assert.h>
#include "file.h"
#include "gl.h"
#include "debug.h"
#include "shader.h"
-GLuint
-make_shader(GLenum type, const char *filename) {
+
+
+int make_shader(GLenum type, const char *filename, struct shader *shader) {
size_t length;
GLchar *source = (GLchar *)file_contents(filename, &length);
- GLuint shader;
GLint shader_ok;
if (!source)
@@ -19,42 +20,130 @@ make_shader(GLenum type, const char *filename) {
source[length] = '\0';
- shader = glCreateShader(type);
- glShaderSource(shader, 1, (const GLchar**)&source, (GLint*)&length);
+ shader->filename = filename;
+ shader->type = type;
+ shader->handle = glCreateShader(type);
+
+ glShaderSource(shader->handle, 1, (const GLchar**)&source, (GLint*)&length);
free(source);
- glCompileShader(shader);
+ glCompileShader(shader->handle);
- glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok);
+ glGetShaderiv(shader->handle, GL_COMPILE_STATUS, &shader_ok);
if (!shader_ok) {
fprintf(stderr, "Failed to compile %s:\n", filename);
- show_info_log(shader);
- glDeleteShader(shader);
+ show_info_log(shader->handle);
+ glDeleteShader(shader->handle);
return 0;
}
- return shader;
+#ifdef DEBUG
+ shader->load_mtime =
+ file_mtime(shader->filename);
+#endif
+
+ return 1;
}
+#ifdef DEBUG
+int reload_program(struct gpu_program *program) {
+ struct shader vert, frag;
+ struct shader *pvert, *pfrag;
+ time_t frag_mtime, vert_mtime;
+ int ok;
-GLuint
-make_program(GLuint vertex_shader, GLuint fragment_shader) {
- GLint program_ok;
- GLuint program = glCreateProgram();
+ vert_mtime = file_mtime(program->vertex.filename);
+ frag_mtime = file_mtime(program->fragment.filename);
- if (vertex_shader)
- glAttachShader(program, vertex_shader);
+ int vert_changed =
+ vert_mtime != program->vertex.load_mtime;
- glAttachShader(program, fragment_shader);
- glLinkProgram(program);
+ int frag_changed =
+ frag_mtime != program->fragment.load_mtime;
- glGetProgramiv(program, GL_LINK_STATUS, &program_ok);
- if (!program_ok) {
- fprintf(stderr, "Failed to link shader program:\n");
- show_info_log(program);
- glDeleteProgram(program);
- return 0;
- }
- return program;
+ if (!vert_changed && !frag_changed)
+ return 2;
+
+ // recompile vertex shader
+ if (vert_changed) {
+ glDeleteShader(program->vertex.handle);
+
+ ok =
+ make_shader(GL_VERTEX_SHADER,
+ program->vertex.filename,
+ &vert);
+
+ if (!ok)
+ return 0;
+ }
+
+ // recompile fragment shader
+ if (frag_changed) {
+ glDeleteShader(program->fragment.handle);
+
+ ok =
+ make_shader(GL_FRAGMENT_SHADER,
+ program->fragment.filename,
+ &frag);
+
+ if (!ok)
+ return 0;
+ }
+
+ if (vert_changed) {
+ glDeleteShader(program->vertex.handle);
+ pvert = |
+ vert.load_mtime = vert_mtime;
+ }
+ else
+ pvert = &program->vertex;
+
+ if (frag_changed) {
+ glDeleteShader(program->fragment.handle);
+ pfrag = &frag;
+ frag.load_mtime = frag_mtime;
+ }
+ else
+ pfrag = &program->fragment;
+
+ glDeleteProgram(program->handle);
+
+ make_program(pvert, pfrag, program);
+
+ return 1;
+}
+#endif
+
+int
+make_program(struct shader *vertex, struct shader *fragment,
+ struct gpu_program *program)
+{
+ GLint program_ok;
+
+ // TODO: relax these constraints
+ assert(vertex);
+ assert(fragment);
+
+ program->handle =
+ glCreateProgram();
+
+ program->fragment = *fragment;
+ program->vertex = *vertex;
+
+ glAttachShader(program->handle, vertex->handle);
+ glAttachShader(program->handle, fragment->handle);
+
+ glLinkProgram(program->handle);
+
+ glGetProgramiv(program->handle, GL_LINK_STATUS, &program_ok);
+
+ if (!program_ok) {
+ fprintf(stderr, "Failed to link shader program:\n");
+ show_info_log(program->handle);
+ glDeleteProgram(program->handle);
+ return 0;
+ }
+
+ return 1;
}
diff --git a/src/shader.h b/src/shader.h
@@ -1,9 +1,35 @@
#ifndef POLYADVENT_SHADER_H
#define POLYADVENT_SHADER_H
+#include <time.h>
+#include "gl.h"
+
#define SHADER(f) "etc/shaders/" f
-GLuint make_shader(GLenum type, const char *filename);
-GLuint make_program(GLuint vertex_shader, GLuint fragment_shader);
+struct shader {
+ GLenum type;
+ GLuint handle;
+ const char *filename;
+#ifdef DEBUG
+ time_t load_mtime;
+#endif
+};
+
+struct gpu_program {
+ struct shader vertex;
+ struct shader fragment;
+ GLuint handle;
+};
+
+
+#ifdef DEBUG
+int reload_program(struct gpu_program *program);
+#endif
+
+int make_shader(GLenum type, const char *filename, struct shader *shader);
+
+int make_program(struct shader *vertex,
+ struct shader *fragment,
+ struct gpu_program *program);
#endif /* POLYADVENT_SHADER_H */
diff --git a/src/update.c b/src/update.c
@@ -8,6 +8,8 @@
#include "camera.h"
#include "poisson.h"
#include "uniform.h"
+#include "shader.h"
+#include "file.h"
static void movement(struct game *game, struct node *node, float speed_mult) {
float amt = 0.03;
@@ -151,98 +153,91 @@ static void player_movement(struct game *game) {
}
-void update (struct game *game, u32 dt) {
- static int passed = 0;
- static double last_ox, last_oy, last_oz;
- static int last_gen_time = 50;
- static int toggle_fog = 0, toggle_diffuse = 0;
- static float n = 1;
- static int first = 1;
- struct resources *res = &game->test_resources;
- static int stopped = 0;
- struct perlin_settings *ts = &game->terrain.settings;
- struct node *tnode = &game->test_resources.terrain_node;
- struct node *root = &game->test_resources.root;
- float *light = res->light_dir;
-
- if (first) {
- update_terrain(game);
- first = 0;
- }
+#ifdef DEBUG
+static int try_reload_shaders(struct resources *res) {
+ int ret;
+ printf("reloading shaders... ");
- if (game->input.modifiers & KMOD_LALT) {
- movement(game, &res->camera, 1.0);
- }
- else if (game->input.modifiers & KMOD_RCTRL) {
- movement(game, &res->terrain_node, 5.0);
- }
- else {
- player_movement(game);
- }
+ ret = reload_program(&res->program);
- if (game->input.keystates[SDL_SCANCODE_C])
- printf("light_dir %f %f %f\n", light[0], light[1], light[2]);
-
- if (game->input.keystates[SDL_SCANCODE_F])
- toggle_fog = 1;
-
- if (game->input.keystates[SDL_SCANCODE_G])
- toggle_diffuse = 1;
-
- int space_down = game->input.keystates[SDL_SCANCODE_SPACE];
-
- if (space_down) {
- if (!stopped) {
- printf("terrain amp %f exp %f freq %f (%d ms)\n",
- ts->amplitude,
- ts->exp,
- ts->freq,
- last_gen_time);
- stopped = 1;
- }
- else {
- stopped = 0;
- }
- }
+ if (ret == 2)
+ printf("nothing to reload\n");
+ else if (ret == 1)
+ printf("success.\n");
+ else
+ printf("failed.\n");
- if (space_down ) {
- passed += dt;
- } else {
- if (toggle_fog) {
- res->fog_on = !res->fog_on;
- toggle_fog = 0;
- }
- if (toggle_diffuse) {
- res->diffuse_on = !res->diffuse_on;
- toggle_diffuse = 0;
- }
- passed = 0;
-
- double ox = tnode->pos[0];
- double oy = tnode->pos[1];
-
- bool changed = last_ox != ox || last_oy != oy || last_oz != tnode->pos[2];
-
-
- if (!stopped && changed) {
- int t1 = SDL_GetTicks();
- update_terrain(game);
- last_ox = ts->ox = ox;
- last_oy = ts->oy = oy;
- last_oz = tnode->pos[2] = max(tnode->pos[2], 5.0);
- int t2 = SDL_GetTicks();
- last_gen_time = t2 - t1;
-
- n += 0.01f;
- }
- }
- /* res->light_dir[0] = fabs(cos(n)); */
- /* res->light_dir[1] = fabs(sin(n+50.0)); */
- /* res->light_dir[2] = 1; */
- /* res->light_dir[2] = fabs(sin(n+100.0)*cos(n)); */
- n += 0.001f;
+ return ret;
+}
+#endif
- node_recalc(root);
+void update (struct game *game, u32 dt) {
+ static double last_ox, last_oy, last_oz;
+ static int toggle_fog = 0, toggle_diffuse = 0;
+ static float n = 1;
+ static int first = 1;
+ struct resources *res = &game->test_resources;
+ struct perlin_settings *ts = &game->terrain.settings;
+ struct node *tnode = &game->test_resources.terrain_node;
+ struct node *root = &game->test_resources.root;
+ float *light = res->light_dir;
+
+ if (first) {
+ update_terrain(game);
+ first = 0;
+ }
+
+ if (game->input.modifiers & KMOD_LALT) {
+ movement(game, &res->camera, 1.0);
+ }
+ else if (game->input.modifiers & KMOD_RCTRL) {
+ movement(game, &res->terrain_node, 5.0);
+ }
+ else {
+ player_movement(game);
+ }
+
+#ifdef DEBUG
+ if (game->input.keystates[SDL_SCANCODE_R])
+ try_reload_shaders(res);
+#endif
+
+ if (game->input.keystates[SDL_SCANCODE_C])
+ printf("light_dir %f %f %f\n", light[0], light[1], light[2]);
+
+ if (game->input.keystates[SDL_SCANCODE_F])
+ toggle_fog = 1;
+
+ if (game->input.keystates[SDL_SCANCODE_G])
+ toggle_diffuse = 1;
+
+ if (toggle_fog) {
+ res->fog_on = !res->fog_on;
+ toggle_fog = 0;
+ }
+
+ if (toggle_diffuse) {
+ res->diffuse_on = !res->diffuse_on;
+ toggle_diffuse = 0;
+ }
+
+ double ox = tnode->pos[0];
+ double oy = tnode->pos[1];
+
+ bool changed = last_ox != ox || last_oy != oy || last_oz != tnode->pos[2];
+
+ if (changed) {
+ update_terrain(game);
+ last_ox = ts->ox = ox;
+ last_oy = ts->oy = oy;
+ last_oz = tnode->pos[2] = max(tnode->pos[2], 5.0);
+
+ n += 0.01f;
+ }
+
+ n += 0.001f;
+
+ node_recalc(root);
}