diff options
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | base.cpp (renamed from base.c) | 0 | ||||
-rw-r--r-- | gui.hpp (renamed from gui.h) | 0 | ||||
-rw-r--r-- | main.cpp (renamed from main.c) | 20 | ||||
-rw-r--r-- | math.cpp (renamed from math.c) | 2 | ||||
-rw-r--r-- | sim.cpp (renamed from sim.c) | 185 | ||||
-rw-r--r-- | sim.hpp (renamed from sim.h) | 6 | ||||
-rw-r--r-- | time.cpp (renamed from time.c) | 12 | ||||
-rw-r--r-- | util.cpp (renamed from util.c) | 0 |
9 files changed, 126 insertions, 111 deletions
@@ -1,10 +1,10 @@ -WARNINGS=-Wall -Wextra -Wshadow -Wconversion -Wpedantic -pedantic -std=gnu11 -Wno-unused-function -Wno-fixed-enum-extension -Wimplicit-fallthrough -LIBS=-lSDL2 -lGL -ldl +WARNINGS=-Wall -Wextra -Wshadow -Wconversion -Wpedantic -pedantic -std=gnu++11 -Wno-unused-function -Wno-fixed-enum-extension -Wimplicit-fallthrough +LIBS=-ldl `pkg-config --libs --cflags box2d sdl2 gl` DEBUG_CFLAGS=$(CFLAGS) $(WARNINGS) $(LIBS) -DDEBUG -O0 -g obj/debug: physics obj/sim.so touch obj/debug -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) +physics: main.cpp gui.hpp sim.cpp time.cpp + $(CXX) main.cpp -o $@ $(DEBUG_CFLAGS) +obj/sim.so: *.[ch]* + $(CXX) sim.cpp -fPIC -shared -o $@ $(DEBUG_CFLAGS) touch obj/sim.so_changed @@ -1,13 +1,13 @@ #ifdef _WIN32 #include <windows.h> #endif -#include "gui.h" +#include "gui.hpp" #if DEBUG typedef void (*SimFrameFn)(Frame *); #else -#include "sim.c" +#include "sim.cpp" #endif -#include "time.c" +#include "time.cpp" #ifdef _WIN32 #include <SDL.h> @@ -168,15 +168,15 @@ static SDL_Scancode key_to_scancode(Key key, bool *shift) { case KEY_0: return SDL_SCANCODE_0; // SDL_SCANCODE_0 != SDL_SCANCODE_1 - 1 default: if (key >= KEY_A && key <= KEY_Z) { - return SDL_SCANCODE_A + (key - KEY_A); + return (SDL_Scancode)(SDL_SCANCODE_A + (key - KEY_A)); } else if (key >= KEY_1 && key <= KEY_9) { - return SDL_SCANCODE_1 + (key - KEY_1); + return (SDL_Scancode)(SDL_SCANCODE_1 + (key - KEY_1)); } else if (key >= KEY_F1 && key <= KEY_F12) { - return SDL_SCANCODE_F1 + (key - KEY_F1); + return (SDL_Scancode)(SDL_SCANCODE_F1 + (key - KEY_F1)); } break; } - return 0; + return (SDL_Scancode)0; } #ifdef _WIN32 @@ -193,10 +193,10 @@ int WinMain( #else int main(void) { #endif - Frame frame = {0}; + Frame frame = {}; Input *input = &frame.input; #if DEBUG - struct timespec dynlib_last_modified = {0}; + struct timespec dynlib_last_modified = {}; SimFrameFn sim_frame = NULL; #if __unix__ void *dynlib = NULL; @@ -220,7 +220,7 @@ int main(void) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - SDL_GLContext *glctx = SDL_GL_CreateContext(window); + SDL_GLContext glctx = SDL_GL_CreateContext(window); if (!glctx) { die("%s", SDL_GetError()); } @@ -622,7 +622,7 @@ static void gl_color2f(float v, float a) { // color is 0xRRGGBBAA static void gl_rgbacolor(u32 color) { - glColor4ub((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + glColor4ub((u8)(color >> 24), (u8)(color >> 16), (u8)(color >> 8), (u8)color); } // color is 0xRRGGBB @@ -1,4 +1,4 @@ -#include "gui.h" +#include "gui.hpp" #ifdef _WIN32 #include <windows.h> #include "lib/glcorearb.h" @@ -13,11 +13,19 @@ #include <ctype.h> #define MATH_GL -#include "math.c" -#include "sim.h" -#include "time.c" -#include "util.c" -#include "base.c" +#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) { @@ -157,11 +165,26 @@ static void shaders_reload_if_necessary(State *state) { } #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 = 0.005f; + float thickness = state->platform_thickness; shader_start_using(gl, &shader->base); @@ -171,19 +194,16 @@ static void platforms_render(State *state, Platform *platforms, u32 nplatforms) glBegin(GL_QUADS); glColor3f(1,0,1); for (Platform *platform = platforms, *end = platform + nplatforms; platform != end; ++platform) { - // calculate endpoints of platform - 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 coords[6]; + platform_get_coords(state, platform, coords); #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)); + 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); @@ -217,60 +237,21 @@ static void ball_render(State *state) { shader_stop_using(gl); } -// calculate new position/velocity of ball -static void ball_update(State *state, float dt) { - Ball *ball = &state->ball; - float ball_r_squared = ball->radius * ball->radius; - float gravity = 0.001f; - v2 *vel = &ball->vel; - v2 acceleration = V2(0, -gravity); - // apply velocity to position - v2 ball_pos = v2_add(ball->pos, v2_scale(*vel, dt)); - // correct position according to acceleration (p' = p + v⋅t + 1/2⋅a⋅t²) - ball_pos = v2_add(ball_pos, v2_scale(acceleration, 0.5f * dt * dt)); - // apply acceleration to velocity - *vel = v2_add(*vel, acceleration); - for (Platform *platform = state->platforms, *end = platform + state->nplatforms; - platform != end; ++platform) { - v2 platform_center = platform->center; - float platform_size = platform->size; - float platform_angle = platform->angle; - v2 platform_length = v2_polar(platform_size, platform_angle); - platform_center = v2_sub(platform_center, ball_pos); // now the center of the ball is (0, 0) - v2 platform_pos = v2_sub(platform_center, v2_scale(platform_length, 0.5f)); - - // do some math to figure out if the ball & platform intersect - float dot = v2_dot(platform_length, platform_pos); - float len_squared = v2_dot(platform_length, platform_length); - float pos_squared = v2_dot(platform_pos, platform_pos); - float discriminant = dot * dot - len_squared * (pos_squared - ball_r_squared); - if (discriminant < 0) { - // full line (not segment) defined by platform doesn't even intersect ball, let alone the platform proper - } else { - float one_over_len_squared = 1.0f / len_squared; - float d1 = (-dot - sqrtf(discriminant)) * one_over_len_squared; - float d2 = (-dot + sqrtf(discriminant)) * one_over_len_squared; - // points platform_pos + d1 * platform_len and - // platform_pos + d2 * platform_len - // are where the platform intersects the circle. - // if either is between 0 and 1, then the intersection actually happens on the platform - if ((d1 >= 0 && d1 <= 1) || (d2 >= 0 && d2 <= 1)) { - // platform intersects ball - float d = (d1 >= 0 && d1 <= 1) ? d1 : d2; - v2 collide_pos = v2_add(platform_pos, v2_scale(platform_length, d)); - - // calculate new velocity after collision - float vel_angle = -atan2f(vel->y, vel->x); - float vel_angle_relative_to_platform = vel_angle - platform->angle; - float new_angle = vel_angle + (PIf - 2 * vel_angle_relative_to_platform); - *vel = v2_polar(v2_len(*vel), new_angle); - ball_pos = v2_add(ball->pos, v2_scale(*vel, dt)); - break; - } - } - } - ball->pos = ball_pos; +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 @@ -286,6 +267,7 @@ void sim_frame(Frame *frame) { 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; @@ -356,25 +338,53 @@ void sim_frame(Frame *frame) { 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.3f; - p->size = 0.2f; + p->angle = PIf * 0.46f; + p->size = 0.3f; ++p; p->center = V2(0.4f, 0.5f); - p->angle = PIf * 0.7f; - p->size = 0.2f; + p->angle = PIf * 0.54f; + p->size = 0.3f; } - Ball *ball = &state->ball; - ball->radius = 0.002f; + ball->radius = 0.02f; ball->pos = V2(0.5f, 0.8f); - ball->vel = V2(0, 0); + 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; @@ -389,19 +399,20 @@ void sim_frame(Frame *frame) { shaders_reload_if_necessary(state); #endif + b2World *world = state->world; + // simulate physics { + float time_step = 0.01f; // fixed time step float dt = state->dt; - float physics_step = 0.01f; // maximum dt for each physics step - if (dt > 100) - dt = 100; // make sure that dt -= physics_step actually does something (if dt is very large, it might not) - // do a number of fixed dt steps - while (dt > physics_step) { - ball_update(state, physics_step); - dt -= physics_step; + 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; } - // update for remaining time - ball_update(state, dt); + 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); @@ -1,3 +1,4 @@ +#include <Box2D.h> // enums with a specified width are a clang C extension & available in C++11 #if defined __clang__ || __cplusplus >= 201103L #define ENUM_U8 typedef enum : u8 @@ -111,8 +112,8 @@ typedef struct { typedef struct { v2 pos; // position - v2 vel; // velocity float radius; + b2Body *body; } Ball; typedef struct { @@ -128,8 +129,11 @@ typedef struct { ShaderPlatform shader_platform; ShaderBall shader_ball; + b2World *world; + Ball ball; + float platform_thickness; u32 nplatforms; Platform platforms[1000]; @@ -4,13 +4,13 @@ static struct timespec time_last_modified(char const *filename) { #if __unix__ - struct stat statbuf = {0}; + struct stat statbuf = {}; stat(filename, &statbuf); return statbuf.st_mtim; #else // windows' _stat does not have st_mtim - struct _stat statbuf = {0}; - struct timespec ts = {0}; + struct _stat statbuf = {}; + struct timespec ts = {}; _stat(filename, &statbuf); ts.tv_sec = statbuf.st_mtime; return ts; @@ -36,9 +36,9 @@ static struct timespec timespec_max(struct timespec a, struct timespec b) { // sleep for a certain number of nanoseconds static void sleep_ns(u64 ns) { #if __unix__ - struct timespec rem = {0}, req = { - ns / 1000000000, - ns % 1000000000 + struct timespec rem = {}, req = { + (time_t)(ns / 1000000000), + (long)(ns % 1000000000) }; while (nanosleep(&req, &rem) == EINTR) // sleep interrupted by signal |