summaryrefslogtreecommitdiff
path: root/sim.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sim.cpp')
-rw-r--r--sim.cpp427
1 files changed, 427 insertions, 0 deletions
diff --git a/sim.cpp b/sim.cpp
new file mode 100644
index 0000000..d2ae5ba
--- /dev/null
+++ b/sim.cpp
@@ -0,0 +1,427 @@
+#include "gui.hpp"
+#ifdef _WIN32
+#include <windows.h>
+#include "lib/glcorearb.h"
+#endif
+#include <GL/gl.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <ctype.h>
+
+#define MATH_GL
+#include "math.cpp"
+#include "sim.hpp"
+#include "time.cpp"
+#include "util.cpp"
+#include "base.cpp"
+
+// how much to scale up objects for Box2D
+#define B2_SCALE 30
+#define B2_INV_SCALE (1.0f / B2_SCALE)
+
+static b2Vec2 v2_to_b2(v2 v) {
+ return b2Vec2(v.x, v.y);
+}
+
+// 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 (fp) {
+ 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 {
+ logln("File does not exist: %s.", filename);
+ 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 shader_platform_load(GL *gl, ShaderPlatform *shader) {
+ ShaderBase *base = &shader->base;
+ shader_load(gl, base, "assets/platform_v.glsl", "assets/platform_f.glsl");
+ shader->vertex_p1 = shader_attrib_location(gl, base, "vertex_p1");
+ shader->vertex_p2 = shader_attrib_location(gl, base, "vertex_p2");
+ shader->uniform_thickness = shader_uniform_location(gl, base, "thickness");
+ shader->uniform_transform = shader_uniform_location(gl, base, "transform");
+}
+
+static void shader_ball_load(GL *gl, ShaderBall *shader) {
+ ShaderBase *base = &shader->base;
+ shader_load(gl, base, "assets/ball_v.glsl", "assets/ball_f.glsl");
+ shader->uniform_transform = shader_uniform_location(gl, base, "transform");
+ shader->uniform_center = shader_uniform_location(gl, base, "center");
+ shader->uniform_radius = shader_uniform_location(gl, base, "radius");
+}
+
+static void shaders_load(State *state) {
+ GL *gl = &state->gl;
+ shader_platform_load(gl, &state->shader_platform);
+ shader_ball_load(gl, &state->shader_ball);
+}
+
+#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);
+ if (shader_needs_reloading(&state->shader_ball.base))
+ shader_ball_load(gl, &state->shader_ball);
+}
+#endif
+
+// get endpoints and bounding box of platform
+static void platform_get_coords(State *state, Platform *platform, v2 coords[6]) {
+ float radius = platform->size * 0.5f;
+ v2 thickness_r = v2_polar(state->platform_thickness, platform->angle - HALF_PIf);
+ v2 platform_r = v2_polar(radius, platform->angle);
+ v2 endpoint1 = v2_add(platform->center, platform_r);
+ v2 endpoint2 = v2_sub(platform->center, platform_r);
+ coords[0] = endpoint1;
+ coords[1] = endpoint2;
+ coords[2] = v2_sub(endpoint1, thickness_r);
+ coords[3] = v2_sub(endpoint2, thickness_r);
+ coords[4] = v2_add(endpoint2, thickness_r);
+ coords[5] = v2_add(endpoint1, thickness_r);
+}
+
+// render the given platforms
+static void platforms_render(State *state, Platform *platforms, u32 nplatforms) {
+ GL *gl = &state->gl;
+ ShaderPlatform *shader = &state->shader_platform;
+ float thickness = state->platform_thickness;
+
+ shader_start_using(gl, &shader->base);
+
+ gl->Uniform1f(shader->uniform_thickness, thickness);
+ gl->UniformMatrix4fv(shader->uniform_transform, 1, GL_FALSE, state->transform.e);
+
+ glBegin(GL_QUADS);
+ glColor3f(1,0,1);
+ for (Platform *platform = platforms, *end = platform + nplatforms; platform != end; ++platform) {
+ v2 coords[6];
+ platform_get_coords(state, platform, coords);
+
+ #if 1
+ gl->VertexAttrib2f(shader->vertex_p1, coords[0].x, coords[0].y);
+ gl->VertexAttrib2f(shader->vertex_p2, coords[1].x, coords[1].y);
+ v2_gl_vertex(coords[2]);
+ v2_gl_vertex(coords[3]);
+ v2_gl_vertex(coords[4]);
+ v2_gl_vertex(coords[5]);
+ #else
+ v2_gl_vertex(endpoint1);
+ v2_gl_vertex(endpoint2);
+ #endif
+ }
+ glEnd();
+ shader_stop_using(gl);
+}
+
+// render the ball
+static void ball_render(State *state) {
+ GL *gl = &state->gl;
+ Ball *ball = &state->ball;
+ float ball_x = ball->pos.x, ball_y = ball->pos.y;
+ float ball_r = ball->radius;
+ ShaderBall *shader = &state->shader_ball;
+
+ shader_start_using(gl, &shader->base);
+
+ gl->UniformMatrix4fv(shader->uniform_transform, 1, GL_FALSE, state->transform.e);
+ gl->Uniform2f(shader->uniform_center, ball_x, ball_y);
+ gl->Uniform1f(shader->uniform_radius, ball_r);
+
+ glBegin(GL_QUADS);
+ glColor3f(1,1,1);
+ glVertex2f(ball_x-ball_r, ball_y-ball_r);
+ glVertex2f(ball_x-ball_r, ball_y+ball_r);
+ glVertex2f(ball_x+ball_r, ball_y+ball_r);
+ glVertex2f(ball_x+ball_r, ball_y-ball_r);
+ glEnd();
+ shader_stop_using(gl);
+}
+
+
+static b2Body *platform_to_body(State *state, Platform *platform) {
+ b2World *world = state->world;
+
+ float half_size = platform->size * 0.5f * B2_SCALE;
+ v2 center = v2_scale(platform->center, B2_SCALE);
+
+ b2BodyDef body_def;
+ body_def.position.Set(center.x, center.y);
+ b2Body *body = world->CreateBody(&body_def);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(half_size, state->platform_thickness, b2Vec2(0, 0), platform->angle);
+ body->CreateFixture(&shape, 0.0f);
+ return body;
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+#ifdef __cplusplus
+extern "C"
+#endif
+void sim_frame(Frame *frame) {
+ if (frame->memory_size < sizeof(State)) {
+ printf("Not enough memory (got %lu, require %lu).\n", (ulong)frame->memory_size, (ulong)sizeof(State));
+ frame->close = true;
+ return;
+ }
+ State *state = (State *)frame->memory;
+ Ball *ball = &state->ball;
+ 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;
+
+ state->win_width = width;
+ state->win_height = height;
+ state->gl_width = (float)width / (float)height;
+ state->dt = (float)frame->dt;
+
+ state->transform = m4_ortho(0, state->gl_width, 0, 1, -1, +1);
+
+ // set up GL
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glOrtho(0, state->gl_width, 0, 1, -1, +1);
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if (state->magic_number != MAGIC_NUMBER || keys_pressed[KEY_F5]) {
+ memset(state, 0, sizeof *state);
+ }
+
+
+ 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->platform_thickness = 0.005f;
+
+ { // initialize platforms
+ state->nplatforms = 2;
+ Platform *p = &state->platforms[0];
+ p->center = V2(0.5f, 0.5f);
+ p->angle = PIf * 0.46f;
+ p->size = 0.3f;
+ ++p;
+ p->center = V2(0.4f, 0.5f);
+ p->angle = PIf * 0.54f;
+ p->size = 0.3f;
+ }
+
+ ball->radius = 0.02f;
+ ball->pos = V2(0.5f, 0.8f);
+
+ b2Vec2 gravity(0, -30.0f);
+ b2World *world = state->world = new b2World(gravity);
+
+ b2BodyDef ground_body_def;
+ ground_body_def.position.Set(0.0f, -10.0f);
+ b2Body *ground_body = world->CreateBody(&ground_body_def);
+
+ b2PolygonShape ground_shape;
+ ground_shape.SetAsBox(50.0f, 10.0f);
+ ground_body->CreateFixture(&ground_shape, 0.0f);
+
+ b2BodyDef ball_def;
+ ball_def.type = b2_dynamicBody;
+ ball_def.position.Set(ball->pos.x * B2_SCALE, ball->pos.y * B2_SCALE);
+ b2Body *ball_body = ball->body = world->CreateBody(&ball_def);
+
+ b2CircleShape ball_shape;
+ ball_shape.m_radius = ball->radius * B2_SCALE;
+
+ b2FixtureDef ball_fixture;
+ ball_fixture.shape = &ball_shape;
+ ball_fixture.density = 1.0f;
+ ball_fixture.friction = 0.3f;
+ ball_fixture.restitution = 0.9f; // bounciness
+
+ ball_body->CreateFixture(&ball_fixture);
+
+ for (u32 i = 0; i < state->nplatforms; ++i)
+ platform_to_body(state, &state->platforms[i]);
+
+ state->initialized = true;
+ #if DEBUG
+ state->magic_number = MAGIC_NUMBER;
+ #endif
+ }
+ if (input->keys_pressed[KEY_ESCAPE]) {
+ frame->close = true;
+ return;
+ }
+
+#if DEBUG
+ shaders_reload_if_necessary(state);
+#endif
+
+ b2World *world = state->world;
+
+ // simulate physics
+ {
+ float time_step = 0.01f; // fixed time step
+ float dt = state->dt;
+ if (dt > 100) dt = 100; // prevent floating-point problems for very large dt's
+ while (dt >= time_step) {
+ world->Step(time_step, 8, 3); // step using recommended parameters
+ dt -= time_step;
+ }
+ b2Vec2 ball_pos = ball->body->GetPosition();
+ ball->pos.x = ball_pos.x * B2_INV_SCALE;
+ ball->pos.y = ball_pos.y * B2_INV_SCALE;
+ }
+
+ platforms_render(state, state->platforms, state->nplatforms);
+ ball_render(state);
+
+ #if DEBUG
+ GLuint error = glGetError();
+ if (error) {
+ printf("!!! GL ERROR: %u\n", error);
+ }
+ #endif
+}