diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | assets/platform_f.glsl | 15 | ||||
-rw-r--r-- | assets/platform_v.glsl | 19 | ||||
-rw-r--r-- | gui.h | 1 | ||||
-rw-r--r-- | main.c | 6 | ||||
-rw-r--r-- | make.bat | 6 | ||||
-rw-r--r-- | sim.c | 203 | ||||
-rw-r--r-- | sim.h | 81 | ||||
-rw-r--r-- | time.c | 16 |
9 files changed, 327 insertions, 22 deletions
@@ -3,7 +3,7 @@ LIBS=-lSDL2 -lGL -ldl DEBUG_CFLAGS=$(CFLAGS) $(WARNINGS) $(LIBS) -DDEBUG -O0 -g obj/debug: physics obj/sim.so touch obj/debug -physics: main.c gui.h +physics: main.c gui.h sim.c time.c $(CC) main.c -o $@ $(DEBUG_CFLAGS) obj/sim.so: *.[ch] $(CC) sim.c -fPIC -shared -o $@ $(DEBUG_CFLAGS) diff --git a/assets/platform_f.glsl b/assets/platform_f.glsl new file mode 100644 index 0000000..1969a46 --- /dev/null +++ b/assets/platform_f.glsl @@ -0,0 +1,15 @@ +varying vec4 color; +varying vec2 p1, p2; +varying vec2 pos; +uniform float thickness; + +void main() { + // thanks to https://www.youtube.com/watch?v=PMltMdi1Wzg + float h = clamp(dot(pos-p1, p2-p1) / dot(p2-p1, p2-p1), 0.0, 1.0); + float d = length(pos - p1 - (p2-p1) * h); + + float v = max(thickness - d, 0.0); + v /= thickness; + + gl_FragColor = color * v; +} diff --git a/assets/platform_v.glsl b/assets/platform_v.glsl new file mode 100644 index 0000000..472f9df --- /dev/null +++ b/assets/platform_v.glsl @@ -0,0 +1,19 @@ +// "position" within line (0,0) = bottom-left corner, (1,1) = top-right corner +attribute vec2 vertex_p1, vertex_p2; +varying vec4 color; +varying vec2 p1, p2; +varying vec2 pos; +uniform float thickness; + +void main() { + gl_Position = gl_Vertex; + pos = gl_Vertex.xy; + color = gl_Color; +#if 1 + p1 = vertex_p1 + thickness * normalize(vertex_p2 - vertex_p1); + p2 = vertex_p2 + thickness * normalize(vertex_p1 - vertex_p2); +#else + p1 = vertex_p1; + p2 = vertex_p2; +#endif +} @@ -87,6 +87,7 @@ typedef struct { void *memory; size_t memory_size; double dt; // time in seconds between this frame and the last one + void (*(*get_gl_proc)(char const *))(void); // get a GL function char title[64]; // window title } Frame; @@ -213,7 +213,7 @@ int main(void) { } SDL_Window *window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - 720, 720, SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL); + 720, 720, SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); if (!window) { die("%s", SDL_GetError()); } @@ -250,6 +250,8 @@ int main(void) { die("Couldn't allocate memory (%lu bytes).", (ulong)frame.memory_size); } + frame.get_gl_proc = (void (*(*)(char const *))(void))SDL_GL_GetProcAddress; + Uint32 last_frame_ticks = SDL_GetTicks(); while (1) { @@ -279,7 +281,7 @@ int main(void) { #else #define DYNLIB_EXT "dll" #endif - struct timespec new_dynlib_last_modified = last_modified("obj/sim." DYNLIB_EXT "_changed"); + struct timespec new_dynlib_last_modified = time_last_modified("obj/sim." DYNLIB_EXT "_changed"); if (timespec_cmp(dynlib_last_modified, new_dynlib_last_modified) != 0) { // reload dynlib char new_filename[256] = {0}; @@ -7,9 +7,9 @@ if not exist obj mkdir obj SET CFLAGS=/nologo /W3 /D_CRT_SECURE_NO_WARNINGS /I SDL2/include SDL2/lib/x64/SDL2main.lib SDL2/lib/x64/SDL2.lib opengl32.lib if _%1 == _ ( - cl main.c /DDEBUG /DEBUG /Zi %CFLAGS% /Fo:obj/urbs /Fe:urbs + cl main.c /DDEBUG /DEBUG /Zi %CFLAGS% /Fo:obj/urbs /Fe:physics cl sim.c /DDEBUG /DEBUG /LD %CFLAGS% /Fo:obj/sim /Fe:obj/sim echo > obj\sim.dll_changed ) -if _%1 == _release cl main.c /O2 %CFLAGS% /Fe:urbs -if _%1 == _profile cl main.c /O2 /DPROFILE %CFLAGS% /Fe:urbs +if _%1 == _release cl main.c /O2 %CFLAGS% /Fe:physics +if _%1 == _profile cl main.c /O2 /DPROFILE %CFLAGS% /Fe:physics @@ -19,25 +19,161 @@ #include "util.c" #include "base.c" +// compile a vertex or fragment shader +static GLuint shader_compile_from_file(GL *gl, char const *filename, GLenum shader_type) { + FILE *fp = fopen(filename, "rb"); + if (filename) { + char code[16384] = {0}; + char log[4096] = {0}; + char const *const_code = code; + fread(code, 1, sizeof code, fp); + GLuint shader = gl->CreateShader(shader_type); + if (shader == 0) { + logln("Couldn't create shader: %u",glGetError()); + } + gl->ShaderSource(shader, 1, &const_code, NULL); + gl->CompileShader(shader); + gl->GetShaderInfoLog(shader, sizeof log - 1, NULL, log); + if (*log) { + logln("Error compiling shader:\n%s", log); + gl->DeleteShader(shader); + return 0; + } + return shader; + } else { + return 0; + } +} + +#if DEBUG +static struct timespec shader_get_last_modified(ShaderBase *shader) { + // the last modified time of the shader is whichever of the vertex/fragment shaders was modified last + return timespec_max( + time_last_modified(shader->vertex_filename), + time_last_modified(shader->fragment_filename) + ); +} +#endif + +static GLuint shader_attrib_location(GL *gl, ShaderBase *shader, char const *attrib) { + GLint loc = gl->GetAttribLocation(shader->program, attrib); + if (loc == -1) { + printf("Couldn't find vertex attribute %s.\n", attrib); + return 0; + } + return (GLuint)loc; +} + +static GLint shader_uniform_location(GL *gl, ShaderBase *shader, char const *uniform) { + GLint loc = gl->GetUniformLocation(shader->program, uniform); + if (loc == -1) { + printf("Couldn't find uniform: %s.\n", uniform); + return -1; + } + return loc; +} + +// compile a full shader program +static void shader_load(GL *gl, ShaderBase *shader, char const *vertex_filename, char const *fragment_filename) { +#if DEBUG + str_cpy(shader->vertex_filename, sizeof shader->vertex_filename, vertex_filename); + str_cpy(shader->fragment_filename, sizeof shader->fragment_filename, fragment_filename); + shader->last_modified = shader_get_last_modified(shader); + + if (shader->program) gl->DeleteProgram(shader->program); +#endif + + + GLuint vertex_shader = shader_compile_from_file(gl, vertex_filename, GL_VERTEX_SHADER); + GLuint fragment_shader = shader_compile_from_file(gl, fragment_filename, GL_FRAGMENT_SHADER); + GLuint program = gl->CreateProgram(); + gl->AttachShader(program, vertex_shader); + gl->AttachShader(program, fragment_shader); + gl->LinkProgram(program); + char log[4096] = {0}; + gl->GetProgramInfoLog(program, sizeof log - 1, NULL, log); + if (*log) { + logln("Error linking shader:\n%s", log); + gl->DeleteProgram(program); + program = 0; + } + gl->DeleteShader(vertex_shader); + gl->DeleteShader(fragment_shader); + shader->program = program; + + GLenum err = glGetError(); + if (err) { + logln("Error loading shader %s/%s: %u.", vertex_filename, fragment_filename, err); + } else { + logln("Loaded shader %s/%s as %u.", vertex_filename, fragment_filename, program); + } +} + +static void shader_start_using(GL *gl, ShaderBase *shader) { + gl->UseProgram(shader->program); +} + +static void shader_stop_using(GL *gl) { + gl->UseProgram(0); +} + +#if DEBUG +static bool shader_needs_reloading(ShaderBase *shader) { + return !timespec_eq(shader->last_modified, shader_get_last_modified(shader)); +} +#endif -static void platforms_render(Platform *platforms, u32 nplatforms) { -static float t = 0.0f; +static void shader_platform_load(GL *gl, ShaderPlatform *shader) { + shader_load(gl, &shader->base, "assets/platform_v.glsl", "assets/platform_f.glsl"); + shader->vertex_p1 = shader_attrib_location(gl, &shader->base, "vertex_p1"); + shader->vertex_p2 = shader_attrib_location(gl, &shader->base, "vertex_p2"); + shader->uniform_thickness = shader_uniform_location(gl, &shader->base, "thickness"); + // @TODO: transform matrix +} + +static void shaders_load(State *state) { + GL *gl = &state->gl; + shader_platform_load(gl, &state->shader_platform); +} + +#if DEBUG +static void shaders_reload_if_necessary(State *state) { + GL *gl = &state->gl; + if (shader_needs_reloading(&state->shader_platform.base)) + shader_platform_load(gl, &state->shader_platform); +} +#endif + + +static void platforms_render(State *state, Platform *platforms, u32 nplatforms) { + GL *gl = &state->gl; + ShaderPlatform *shader = &state->shader_platform; glColor3f(1,0,1); + float thickness = 0.01f; + shader_start_using(gl, &shader->base); + gl->Uniform1f(shader->uniform_thickness, thickness); glBegin(GL_QUADS); for (Platform *platform = platforms, *end = platform + nplatforms; platform != end; ++platform) { - platform->angle = t += 0.01f; // calculate endpoints of platform - float radius = platform->size * 0.5f; + maybe_unused float radius = platform->size * 0.5f; v2 endpoint1 = v2_add(platform->center, v2_polar(radius, platform->angle)); v2 endpoint2 = v2_sub(platform->center, v2_polar(radius, platform->angle)); - v2 thickness = v2_polar(0.005f, platform->angle - HALF_PIf); - v2_gl_vertex(v2_sub(endpoint1, thickness)); - v2_gl_vertex(v2_sub(endpoint2, thickness)); - v2_gl_vertex(v2_add(endpoint2, thickness)); - v2_gl_vertex(v2_add(endpoint1, thickness)); + #if 1 + v2 r = v2_polar(thickness, platform->angle - HALF_PIf); + gl->VertexAttrib2f(shader->vertex_p1, endpoint1.x, endpoint1.y); + gl->VertexAttrib2f(shader->vertex_p2, endpoint2.x, endpoint2.y); + v2_gl_vertex(v2_sub(endpoint1, r)); + v2_gl_vertex(v2_sub(endpoint2, r)); + v2_gl_vertex(v2_add(endpoint2, r)); + v2_gl_vertex(v2_add(endpoint1, r)); + #else + v2_gl_vertex(endpoint1); + v2_gl_vertex(endpoint2); + #endif } glEnd(); + shader_stop_using(gl); } #ifdef _WIN32 @@ -55,6 +191,7 @@ void sim_frame(Frame *frame) { State *state = (State *)frame->memory; i32 width = frame->width, height = frame->height; Input *input = &frame->input; + GL *gl = &state->gl; maybe_unused u8 *keys_pressed = input->keys_pressed; maybe_unused bool *keys_down = input->keys_down; @@ -75,12 +212,50 @@ void sim_frame(Frame *frame) { if (!state->initialized) { logln("Initializing..."); strcpy(frame->title, "physics"); - + + void (*(*get_gl_proc)(char const *))(void) = frame->get_gl_proc; + + // get GL functions + #define required_gl_proc(name) if (!(gl->name = (GL ## name)get_gl_proc("gl" #name))) { printf("Couldn't get GL proc: %s.\n", #name); exit(-1); } + #define optional_gl_proc(name) gl->name = (GL ## name)get_gl_proc("gl" #name) + required_gl_proc(AttachShader); + required_gl_proc(CompileShader); + required_gl_proc(CreateProgram); + required_gl_proc(CreateShader); + required_gl_proc(DeleteProgram); + required_gl_proc(DeleteShader); + required_gl_proc(GetAttribLocation); + required_gl_proc(GetProgramInfoLog); + required_gl_proc(GetProgramiv); + required_gl_proc(GetShaderInfoLog); + required_gl_proc(GetShaderiv); + required_gl_proc(GetUniformLocation); + required_gl_proc(LinkProgram); + required_gl_proc(ShaderSource); + required_gl_proc(Uniform1f); + required_gl_proc(Uniform2f); + required_gl_proc(Uniform3f); + required_gl_proc(Uniform4f); + required_gl_proc(Uniform1i); + required_gl_proc(Uniform2i); + required_gl_proc(Uniform3i); + required_gl_proc(Uniform4i); + required_gl_proc(UniformMatrix4fv); + required_gl_proc(UseProgram); + required_gl_proc(VertexAttrib1f); + required_gl_proc(VertexAttrib2f); + required_gl_proc(VertexAttrib3f); + required_gl_proc(VertexAttrib4f); + #undef optional_gl_proc + #undef required_gl_proc + + shaders_load(state); + state->nplatforms = 1; Platform *p = &state->platforms[0]; p->center = V2(0.5f, 0.5f); - p->angle = PIf * 0.3f; + p->angle = 0;//PIf * 0.3f; p->size = 0.2f; state->initialized = true; @@ -93,7 +268,11 @@ void sim_frame(Frame *frame) { return; } - platforms_render(state->platforms, state->nplatforms); +#if DEBUG + shaders_reload_if_necessary(state); +#endif + + platforms_render(state, state->platforms, state->nplatforms); #if DEBUG GLuint error = glGetError(); @@ -18,6 +18,84 @@ #define maybe_unused #endif +typedef void (APIENTRY *GLAttachShader)(GLuint program, GLuint shader); +typedef void (APIENTRY *GLCompileShader)(GLuint shader); +typedef GLuint (APIENTRY *GLCreateProgram)(void); +typedef GLuint (APIENTRY *GLCreateShader)(GLenum shaderType); +typedef void (APIENTRY *GLDeleteProgram)(GLuint program); +typedef void (APIENTRY *GLDeleteShader)(GLuint shader); +typedef GLint (APIENTRY *GLGetAttribLocation)(GLuint program, const char *name); +typedef void (APIENTRY *GLGetProgramInfoLog)(GLuint program, GLsizei maxLength, GLsizei *length, char *infoLog); +typedef void (APIENTRY *GLGetProgramiv)(GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRY *GLGetShaderInfoLog)(GLuint shader, GLsizei maxLength, GLsizei *length, char *infoLog); +typedef void (APIENTRY *GLGetShaderiv)(GLuint shader, GLenum pname, GLint *params); +typedef GLint (APIENTRY *GLGetUniformLocation)(GLuint program, char const *name); +typedef void (APIENTRY *GLLinkProgram)(GLuint program); +typedef void (APIENTRY *GLShaderSource)(GLuint shader, GLsizei count, const char *const *string, const GLint *length); +typedef void (APIENTRY *GLUniform1f)(GLint location, GLfloat v0); +typedef void (APIENTRY *GLUniform2f)(GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRY *GLUniform3f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRY *GLUniform4f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRY *GLUniform1i)(GLint location, GLint v0); +typedef void (APIENTRY *GLUniform2i)(GLint location, GLint v0, GLint v1); +typedef void (APIENTRY *GLUniform3i)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRY *GLUniform4i)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRY *GLUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRY *GLUseProgram)(GLuint program); +typedef void (APIENTRY *GLVertexAttrib1f)(GLuint index, GLfloat v0); +typedef void (APIENTRY *GLVertexAttrib2f)(GLuint index, GLfloat v0, GLfloat v1); +typedef void (APIENTRY *GLVertexAttrib3f)(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRY *GLVertexAttrib4f)(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + +typedef struct { + GLAttachShader AttachShader; + GLCompileShader CompileShader; + GLCreateProgram CreateProgram; + GLCreateShader CreateShader; + GLDeleteProgram DeleteProgram; + GLDeleteShader DeleteShader; + GLGetAttribLocation GetAttribLocation; + GLGetProgramInfoLog GetProgramInfoLog; + GLGetProgramiv GetProgramiv; + GLGetShaderInfoLog GetShaderInfoLog; + GLGetShaderiv GetShaderiv; + GLGetUniformLocation GetUniformLocation; + GLLinkProgram LinkProgram; + GLShaderSource ShaderSource; + GLUniform1f Uniform1f; + GLUniform2f Uniform2f; + GLUniform3f Uniform3f; + GLUniform4f Uniform4f; + GLUniform1i Uniform1i; + GLUniform2i Uniform2i; + GLUniform3i Uniform3i; + GLUniform4i Uniform4i; + GLUniformMatrix4fv UniformMatrix4fv; + GLUseProgram UseProgram; + GLVertexAttrib1f VertexAttrib1f; + GLVertexAttrib2f VertexAttrib2f; + GLVertexAttrib3f VertexAttrib3f; + GLVertexAttrib4f VertexAttrib4f; +} GL; + +typedef struct { + GLuint program; +#if DEBUG + char vertex_filename[32]; + char fragment_filename[32]; + struct timespec last_modified; +#endif +} ShaderBase; + +typedef GLuint VertexAttributeLocation; +typedef GLint UniformLocation; + +typedef struct { + ShaderBase base; + VertexAttributeLocation vertex_p1, vertex_p2; + UniformLocation uniform_thickness; +} ShaderPlatform; + typedef struct { v2 center; float size; @@ -28,6 +106,9 @@ typedef struct { bool initialized; i32 win_width, win_height; // width,height of window + GL gl; // gl functions + ShaderPlatform shader_platform; + u32 nplatforms; Platform platforms[1000]; @@ -2,7 +2,7 @@ #include <errno.h> #include <sys/stat.h> -static struct timespec last_modified(char const *filename) { +static struct timespec time_last_modified(char const *filename) { #if __unix__ struct stat statbuf = {0}; stat(filename, &statbuf); @@ -25,6 +25,14 @@ static int timespec_cmp(struct timespec a, struct timespec b) { return 0; } +static bool timespec_eq(struct timespec a, struct timespec b) { + return timespec_cmp(a, b) == 0; +} + +static struct timespec timespec_max(struct timespec a, struct timespec b) { + return timespec_cmp(a, b) < 0 ? b : a; +} + // sleep for a certain number of nanoseconds static void sleep_ns(u64 ns) { #if __unix__ @@ -42,16 +50,16 @@ static void sleep_ns(u64 ns) { } // sleep for microseconds -static void sleep_us(u64 us) { +static void time_sleep_us(u64 us) { sleep_ns(us * 1000); } // sleep for milliseconds -static void sleep_ms(u64 ms) { +static void time_sleep_ms(u64 ms) { sleep_ns(ms * 1000000); } // sleep for seconds -static void sleep_s(u64 s) { +static void time_sleep_s(u64 s) { sleep_ns(s * 1000000000); } |