summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--assets/platform_f.glsl15
-rw-r--r--assets/platform_v.glsl19
-rw-r--r--gui.h1
-rw-r--r--main.c6
-rw-r--r--make.bat6
-rw-r--r--sim.c203
-rw-r--r--sim.h81
-rw-r--r--time.c16
9 files changed, 327 insertions, 22 deletions
diff --git a/Makefile b/Makefile
index bd5bb54..4c96a7d 100644
--- a/Makefile
+++ b/Makefile
@@ -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
+}
diff --git a/gui.h b/gui.h
index a0560b7..9d13303 100644
--- a/gui.h
+++ b/gui.h
@@ -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;
diff --git a/main.c b/main.c
index 11c1b6c..aae22c8 100644
--- a/main.c
+++ b/main.c
@@ -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};
diff --git a/make.bat b/make.bat
index 426a642..a85f7d6 100644
--- a/make.bat
+++ b/make.bat
@@ -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
diff --git a/sim.c b/sim.c
index bbb26d6..2c0f67d 100644
--- a/sim.c
+++ b/sim.c
@@ -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();
diff --git a/sim.h b/sim.h
index cb05fb9..e52023a 100644
--- a/sim.h
+++ b/sim.h
@@ -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];
diff --git a/time.c b/time.c
index d689e65..b616463 100644
--- a/time.c
+++ b/time.c
@@ -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);
}