You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
553 lines
20 KiB
553 lines
20 KiB
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <sys/time.h>
|
|
#include <SDL.h>
|
|
|
|
#define ASSERT(_e, ...) if (!(_e)) { fprintf(stderr, __VA_ARGS__); exit(1); }
|
|
|
|
typedef float f32;
|
|
typedef double f64;
|
|
typedef uint8_t u8;
|
|
typedef uint16_t u16;
|
|
typedef uint32_t u32;
|
|
typedef uint64_t u64;
|
|
typedef int8_t i8;
|
|
typedef int16_t i16;
|
|
typedef int32_t i32;
|
|
typedef int64_t i64;
|
|
typedef size_t usize;
|
|
typedef ssize_t isize;
|
|
|
|
#define SCREEN_SIZE_X 640
|
|
#define SCREEN_SIZE_Y 360
|
|
|
|
#define TILE_WIDTH 1.0f
|
|
#define WALL_HEIGHT 1.2f
|
|
|
|
typedef struct v2_s {f32 x, y;} v2;
|
|
typedef struct v2i_s { i32 x, y;} v2i;
|
|
|
|
#define dot(v0, v1) \
|
|
({ const v2 _v0 = (v0), _v1 = (v1); (_v0.x * _v1.x) + (_v0.y * _v1.y); })
|
|
#define length(v) ({ const v2 _v = (v); sqrtf(dot(_v, _v)); })
|
|
#define normalize(u) ({ \
|
|
const v2 _u = (u); \
|
|
const f32 l = length(_u); \
|
|
(v2) { _u.x/l, _u.y/l }; \
|
|
})
|
|
#define rotr(v) ({ const v2 _v = (v); (v2) { -_v.y, _v.x }; })
|
|
#define min(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a < _b ? _a : _b; })
|
|
#define max(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a > _b ? _a : _b; })
|
|
|
|
static u8 MAPDATA[8*13] = {
|
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 0, 0, 0, 0, 0, 0, 1,
|
|
1, 0, 0, 3, 0, 0, 4, 1,
|
|
1, 0, 0, 0, 0, 0, 0, 1,
|
|
1, 0, 0, 0, 0, 0, 4, 1,
|
|
1, 0, 2, 0, 0, 0, 0, 1,
|
|
1, 0, 0, 0, 0, 0, 0, 1,
|
|
1, 0, 0, 0, 0, 0, 0, 1,
|
|
1, 0, 2, 2, 0, 0, 0, 1,
|
|
1, 0, 2, 2, 0, 3, 0, 1,
|
|
1, 0, 0, 0, 0, 3, 0, 1,
|
|
1, 0, 0, 0, 0, 0, 0, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
};
|
|
|
|
enum KeyboardKeyState {
|
|
KeyboardKeyState_Depressed = 0, // No recent event, key is still up
|
|
KeyboardKeyState_Released = 1, // Last event was a released event
|
|
KeyboardKeyState_Held = 2, // No recent event, key is still down
|
|
KeyboardKeyState_Pressed = 3, // Last event was a pressed event
|
|
KeyboardKeyState_COUNT = 4
|
|
};
|
|
|
|
struct KeyBoardState {
|
|
enum KeyboardKeyState up;
|
|
enum KeyboardKeyState down;
|
|
enum KeyboardKeyState right;
|
|
enum KeyboardKeyState left;
|
|
enum KeyboardKeyState a;
|
|
enum KeyboardKeyState s;
|
|
enum KeyboardKeyState d;
|
|
enum KeyboardKeyState w;
|
|
enum KeyboardKeyState q;
|
|
enum KeyboardKeyState e;
|
|
|
|
enum KeyboardKeyState one;
|
|
enum KeyboardKeyState two;
|
|
enum KeyboardKeyState three;
|
|
enum KeyboardKeyState four;
|
|
enum KeyboardKeyState five;
|
|
enum KeyboardKeyState six;
|
|
enum KeyboardKeyState seven;
|
|
enum KeyboardKeyState eight;
|
|
};
|
|
|
|
void clear_keyboard_state(struct KeyBoardState* kbs) {
|
|
kbs->up = KeyboardKeyState_Depressed;
|
|
kbs->down = KeyboardKeyState_Depressed;
|
|
kbs->right = KeyboardKeyState_Depressed;
|
|
kbs->left = KeyboardKeyState_Depressed;
|
|
kbs->a = KeyboardKeyState_Depressed;
|
|
kbs->s = KeyboardKeyState_Depressed;
|
|
kbs->d = KeyboardKeyState_Depressed;
|
|
kbs->w = KeyboardKeyState_Depressed;
|
|
kbs->q = KeyboardKeyState_Depressed;
|
|
kbs->e = KeyboardKeyState_Depressed;
|
|
|
|
kbs->one = KeyboardKeyState_Depressed;
|
|
kbs->two = KeyboardKeyState_Depressed;
|
|
kbs->three = KeyboardKeyState_Depressed;
|
|
kbs->four = KeyboardKeyState_Depressed;
|
|
kbs->five = KeyboardKeyState_Depressed;
|
|
kbs->six = KeyboardKeyState_Depressed;
|
|
kbs->seven = KeyboardKeyState_Depressed;
|
|
kbs->eight = KeyboardKeyState_Depressed;
|
|
}
|
|
|
|
void decay_keyboard_state(struct KeyBoardState* kbs) {
|
|
static enum KeyboardKeyState to_depressed_state[KeyboardKeyState_COUNT] = {
|
|
KeyboardKeyState_Depressed,
|
|
KeyboardKeyState_Depressed,
|
|
KeyboardKeyState_Held,
|
|
KeyboardKeyState_Held
|
|
};
|
|
|
|
kbs->up = to_depressed_state[kbs->up];
|
|
kbs->down = to_depressed_state[kbs->down];
|
|
kbs->right = to_depressed_state[kbs->right];
|
|
kbs->left = to_depressed_state[kbs->left];
|
|
kbs->a = to_depressed_state[kbs->a];
|
|
kbs->s = to_depressed_state[kbs->s];
|
|
kbs->d = to_depressed_state[kbs->d];
|
|
kbs->w = to_depressed_state[kbs->w];
|
|
kbs->q = to_depressed_state[kbs->q];
|
|
kbs->e = to_depressed_state[kbs->e];
|
|
|
|
kbs->one = to_depressed_state[kbs->one];
|
|
kbs->two = to_depressed_state[kbs->two];
|
|
kbs->three = to_depressed_state[kbs->three];
|
|
kbs->four = to_depressed_state[kbs->four];
|
|
kbs->five = to_depressed_state[kbs->five];
|
|
kbs->six = to_depressed_state[kbs->six];
|
|
kbs->seven = to_depressed_state[kbs->seven];
|
|
kbs->eight = to_depressed_state[kbs->eight];
|
|
}
|
|
|
|
bool is_pressed(enum KeyboardKeyState state) {
|
|
static bool lookup[KeyboardKeyState_COUNT] = {0, 0, 1, 1};
|
|
return lookup[state];
|
|
}
|
|
|
|
// TODO: Could we store the pixels in column-major? We're always rendering
|
|
// in vertical lines, so I suspect that would be more efficient.
|
|
struct {
|
|
SDL_Window *window;
|
|
SDL_Texture *texture;
|
|
SDL_Renderer *renderer;
|
|
u32 pixels[SCREEN_SIZE_X * SCREEN_SIZE_Y];
|
|
bool quit;
|
|
|
|
v2 camera_pos;
|
|
v2 camera_dir;
|
|
v2 camera_dir_rotr;
|
|
f32 camera_width;
|
|
f32 camera_height;
|
|
f32 camera_z;
|
|
|
|
v2 player_speed;
|
|
f32 player_omega;
|
|
|
|
struct KeyBoardState keyboard_state;
|
|
} state;
|
|
|
|
|
|
static void tick(f32 dt) {
|
|
|
|
v2 input_dir = {0.0, 0.0}; // In the body frame, which is right-handed, so y points left.
|
|
if (is_pressed(state.keyboard_state.w)) {
|
|
input_dir.x += 1.0;
|
|
}
|
|
if (is_pressed(state.keyboard_state.s)) {
|
|
input_dir.x -= 1.0;
|
|
}
|
|
if (is_pressed(state.keyboard_state.d)) {
|
|
input_dir.y -= 1.0;
|
|
}
|
|
if (is_pressed(state.keyboard_state.a)) {
|
|
input_dir.y += 1.0;
|
|
}
|
|
|
|
int input_rot_dir = 0; // Right-hand rotation in plane (CCW)
|
|
if (is_pressed(state.keyboard_state.q)) {
|
|
input_rot_dir += 1;
|
|
}
|
|
if (is_pressed(state.keyboard_state.e)) {
|
|
input_rot_dir -= 1;
|
|
}
|
|
|
|
if (is_pressed(state.keyboard_state.three)) {
|
|
state.camera_z *= 0.95;
|
|
printf("camera z: %.3f\n", state.camera_z);
|
|
}
|
|
if (is_pressed(state.keyboard_state.four)) {
|
|
state.camera_z /= 0.95;
|
|
printf("camera z: %.3f\n", state.camera_z);
|
|
}
|
|
if (is_pressed(state.keyboard_state.five)) {
|
|
state.camera_height *= 0.95;
|
|
printf("camera height: %.3f\n", state.camera_height);
|
|
}
|
|
if (is_pressed(state.keyboard_state.six)) {
|
|
state.camera_height /= 0.95;
|
|
printf("camera height: %.3f\n", state.camera_height);
|
|
}
|
|
if (is_pressed(state.keyboard_state.seven)) {
|
|
state.camera_width *= 0.95;
|
|
printf("camera width: %.3f\n", state.camera_width);
|
|
}
|
|
if (is_pressed(state.keyboard_state.eight)) {
|
|
state.camera_width /= 0.95;
|
|
printf("camera width: %.3f\n", state.camera_width);
|
|
}
|
|
|
|
// Update the player's velocity
|
|
const f32 kPlayerInputAccel = 7.5;
|
|
const f32 kPlayerInputAngularAccel = 9.5;
|
|
const f32 kPlayerMaxSpeed = 7.0;
|
|
const f32 kPlayerMaxOmega = 7.0;
|
|
const f32 kAirFriction = 0.9;
|
|
const f32 kAirFrictionRot = 0.85;
|
|
|
|
// Note: Speed is in the global frame
|
|
state.player_speed.x += (state.camera_dir.x*input_dir.x + state.camera_dir_rotr.x*input_dir.y) * kPlayerInputAccel * dt;
|
|
state.player_speed.y += (state.camera_dir.y*input_dir.x + state.camera_dir_rotr.y*input_dir.y) * kPlayerInputAccel * dt;
|
|
state.player_omega += input_rot_dir * kPlayerInputAngularAccel * dt;
|
|
|
|
// Clamp the velocity to a maximum magnitude
|
|
f32 speed = length(state.player_speed);
|
|
if (speed > kPlayerMaxSpeed) {
|
|
state.player_speed.x *= kPlayerMaxSpeed / speed;
|
|
state.player_speed.y *= kPlayerMaxSpeed / speed;
|
|
}
|
|
if (state.player_omega > kPlayerMaxOmega) {
|
|
state.player_omega *= kPlayerMaxOmega / state.player_omega;
|
|
} else if (state.player_omega < -kPlayerMaxOmega) {
|
|
state.player_omega *= - kPlayerMaxOmega / state.player_omega;
|
|
}
|
|
|
|
// Update the player's position
|
|
state.camera_pos.x += state.player_speed.x * dt;
|
|
state.camera_pos.y += state.player_speed.y * dt;
|
|
|
|
// Update the player's rotational heading
|
|
float theta = atan2(state.camera_dir.y, state.camera_dir.x);
|
|
theta += state.player_omega * dt;
|
|
state.camera_dir = ((v2) {cos(theta), sin(theta)});
|
|
state.camera_dir_rotr = rotr((state.camera_dir));
|
|
|
|
// Apply air friction
|
|
state.player_speed.x *= kAirFriction;
|
|
state.player_speed.y *= kAirFriction;
|
|
state.player_omega *= kAirFrictionRot;
|
|
}
|
|
|
|
|
|
// Fill all pixels in the vertical line at x between y0 and y1 with the given color.
|
|
static void draw_column(int x, int y0, int y1, u32 color) {
|
|
for (int y = y0; y <= y1; y++) {
|
|
state.pixels[(y * SCREEN_SIZE_X) + x] = color;
|
|
}
|
|
}
|
|
|
|
static void render() {
|
|
static u32 color_wall[4] = {
|
|
0xFFFF0000,
|
|
0xFF00FF00,
|
|
0xFF00FFFF,
|
|
0xFF0000FF
|
|
};
|
|
static u32 color_wall_light[4] = {
|
|
0xFFFF3333,
|
|
0xFF66FF66,
|
|
0xFF88FFFF,
|
|
0xFF3333FF
|
|
};
|
|
const u32 color_floor = 0xFF666666;
|
|
const u32 color_ceil = 0xFF444444;
|
|
|
|
// Get camera location's cell coordinates
|
|
int x_ind_cam = (int)(floorf(state.camera_pos.x / TILE_WIDTH));
|
|
int y_ind_cam = (int)(floorf(state.camera_pos.y / TILE_WIDTH));
|
|
f32 x_rem_cam = state.camera_pos.x - TILE_WIDTH*x_ind_cam;
|
|
f32 y_rem_cam = state.camera_pos.y - TILE_WIDTH*y_ind_cam;
|
|
|
|
for (int x = 0; x < SCREEN_SIZE_X; x++) {
|
|
|
|
// Camera to pixel column
|
|
const f32 dw = state.camera_width/2 - (state.camera_width*x)/SCREEN_SIZE_X;
|
|
const v2 cp = {
|
|
state.camera_dir.x + dw*state.camera_dir_rotr.x,
|
|
state.camera_dir.y + dw*state.camera_dir_rotr.y
|
|
};
|
|
|
|
// Distance from the camera to the column
|
|
const f32 cam_len = length( (cp) );
|
|
|
|
// Ray direction through this column
|
|
const v2 dir = {cp.x / cam_len, cp.y /cam_len};
|
|
|
|
// Start at the camera pos
|
|
int x_ind = x_ind_cam;
|
|
int y_ind = y_ind_cam;
|
|
f32 x_rem = x_rem_cam;
|
|
f32 y_rem = y_rem_cam;
|
|
|
|
// We will be raycasting through cells of unit width.
|
|
// Our ray's position vs time is:
|
|
// x(t) = x_rem + dir.x * dt
|
|
// y(t) = y_rem + dir.y * dt
|
|
|
|
// We cross x = 0 if dir.x < 0, at dt = -x_rem/dir.x
|
|
// We cross x = TILE_WIDTH if dir.x > 0, at dt = (TILE_WIDTH-x_rem)/dir.x
|
|
// We cross y = 0 if dir.y < 0, at dt = -y_rem/dir.y
|
|
// We cross y = TILE_WIDTH if dir.y > 0, at dt = (TILE_WIDTH-y_rem)/dir.y
|
|
|
|
// We can generalize this to:
|
|
// dx_ind_dir = -1 if dir.x < 0, at dt = -1/dir.x * x_rem + 0.0
|
|
// dx_ind_dir = 1 if dir.x > 0, at dt = -1/dir.x * x_rem + TILE_WIDTH/dir.x
|
|
// dx_ind_dir = 0 if dir.x = 0, at dt = 0 * x_rem + INFINITY
|
|
// dy_ind_dir = -1 if dir.y < 0, at dt = -1/dir.y * y_rem + 0.0
|
|
// dy_ind_dir = 1 if dir.y > 0, at dt = -1/dir.y * y_rem + TILE_WIDTH/dir.y
|
|
// dy_ind_dir = 0 if dir.x = 0, at dt = 0 * y_rem + INFINITY
|
|
|
|
int dx_ind_dir = 0;
|
|
f32 dx_a = 0.0;
|
|
f32 dx_b = INFINITY;
|
|
if (dir.x < 0) {
|
|
dx_ind_dir = -1;
|
|
dx_a = -1.0f/dir.x;
|
|
dx_b = 0.0;
|
|
} else if (dir.x > 0) {
|
|
dx_ind_dir = 1;
|
|
dx_a = -1.0f/dir.x;
|
|
dx_b = TILE_WIDTH/dir.x;
|
|
}
|
|
|
|
int dy_ind_dir = 0;
|
|
f32 dy_a = 0.0;
|
|
f32 dy_b = INFINITY;
|
|
if (dir.y < 0) {
|
|
dy_ind_dir = -1;
|
|
dy_a = -1.0f/dir.y;
|
|
dy_b = 0.0;
|
|
} else if (dir.y > 0) {
|
|
dy_ind_dir = 1;
|
|
dy_a = -1.0f/dir.y;
|
|
dy_b = TILE_WIDTH/dir.y;
|
|
}
|
|
|
|
// Step through cells until we hit an occupied cell
|
|
int n_steps = 0;
|
|
int dx_ind, dy_ind;
|
|
while (n_steps < 100) {
|
|
n_steps += 1;
|
|
|
|
f32 dt_best = INFINITY;
|
|
dx_ind = 0;
|
|
dy_ind = 0;
|
|
|
|
f32 dt_x = dx_a*x_rem + dx_b;
|
|
f32 dt_y = dy_a*y_rem + dy_b;
|
|
if (dt_x < dt_y) {
|
|
dt_best = dt_x;
|
|
dx_ind = dx_ind_dir;
|
|
dy_ind = 0;
|
|
} else {
|
|
dt_best = dt_y;
|
|
dx_ind = 0;
|
|
dy_ind = dy_ind_dir;
|
|
}
|
|
|
|
// Move up to the next cell
|
|
x_ind += dx_ind;
|
|
y_ind += dy_ind;
|
|
x_rem += dir.x * dt_best - TILE_WIDTH*dx_ind;
|
|
y_rem += dir.y * dt_best - TILE_WIDTH*dy_ind;
|
|
|
|
// Check to see if the new cell is solid
|
|
if (MAPDATA[y_ind*8 + x_ind] > 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Calculate the collision location
|
|
const v2 collision = {
|
|
TILE_WIDTH*x_ind + x_rem,
|
|
TILE_WIDTH*y_ind + y_rem
|
|
};
|
|
|
|
// Calculate the ray length
|
|
const f32 ray_len = length( ((v2) {collision.x - state.camera_pos.x, collision.y - state.camera_pos.y}) );
|
|
|
|
// Calculate the pixel bounds that we fill the wall in for
|
|
int y_lo = (int)(SCREEN_SIZE_Y/2.0f - cam_len*state.camera_z/ray_len * SCREEN_SIZE_Y / state.camera_height);
|
|
int y_hi = (int)(SCREEN_SIZE_Y/2.0f + cam_len*(WALL_HEIGHT - state.camera_z)/ray_len * SCREEN_SIZE_Y / state.camera_height);
|
|
y_lo = max(y_lo, 0);
|
|
y_hi = min(y_hi, SCREEN_SIZE_Y-1);
|
|
|
|
u32 color_wall_to_render = (dx_ind == 0) ? color_wall[MAPDATA[y_ind*8 + x_ind]-1] : color_wall_light[MAPDATA[y_ind*8 + x_ind]-1];
|
|
|
|
draw_column(x, 0, y_lo-1, color_floor);
|
|
draw_column(x, y_lo, y_hi, color_wall_to_render);
|
|
draw_column(x, y_hi + 1, SCREEN_SIZE_Y-1, color_ceil);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
// Initialize SDL
|
|
ASSERT(
|
|
SDL_Init(SDL_INIT_VIDEO) == 0,
|
|
"SDL initialization failed: %s\n",
|
|
SDL_GetError()
|
|
);
|
|
|
|
// Create a window
|
|
state.window = SDL_CreateWindow(
|
|
"TOOM",
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(1),
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(1),
|
|
SCREEN_SIZE_X,
|
|
SCREEN_SIZE_Y,
|
|
SDL_WINDOW_ALLOW_HIGHDPI);
|
|
ASSERT(state.window, "Error creating SDL window: %s\n", SDL_GetError());
|
|
|
|
// Create a renderer
|
|
state.renderer = SDL_CreateRenderer(state.window, -1, SDL_RENDERER_PRESENTVSYNC);
|
|
ASSERT(state.renderer, "Error creating SDL renderer: %s\n", SDL_GetError());
|
|
|
|
// Create a texture
|
|
state.texture = SDL_CreateTexture(
|
|
state.renderer,
|
|
SDL_PIXELFORMAT_ABGR8888,
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
SCREEN_SIZE_X,
|
|
SCREEN_SIZE_Y);
|
|
ASSERT(state.texture, "Error creating SDL texture: %s\n", SDL_GetError());
|
|
|
|
// Init camera
|
|
state.camera_pos = (v2) { 5.0f, 5.0f };
|
|
state.camera_dir = ((v2) {cos(0.0), sin(0.0)});
|
|
state.camera_dir_rotr = rotr((state.camera_dir));
|
|
state.camera_width = 1.5f;
|
|
state.camera_height = state.camera_width * SCREEN_SIZE_Y / SCREEN_SIZE_X;
|
|
state.camera_z = 0.4;
|
|
|
|
// Init player state
|
|
state.player_speed = (v2) { 0.0f, 0.0f };
|
|
state.player_omega = 0.0f;
|
|
|
|
// Init keyboard
|
|
clear_keyboard_state(&state.keyboard_state);
|
|
|
|
// Time structs
|
|
struct timeval timeval_start, timeval_end;
|
|
|
|
// Main loop
|
|
u32 time_prev_tick = SDL_GetTicks();
|
|
state.quit = 0;
|
|
while (state.quit == 0) {
|
|
const u32 time_start = SDL_GetTicks();
|
|
gettimeofday(&timeval_start, NULL);
|
|
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
if (event.type == SDL_QUIT) {
|
|
state.quit = 1;
|
|
break;
|
|
} else if (event.type == SDL_KEYDOWN) {
|
|
switch (event.key.keysym.sym) {
|
|
case (SDLK_UP) : state.keyboard_state.up = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_DOWN) : state.keyboard_state.down = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_LEFT) : state.keyboard_state.left = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_RIGHT) : state.keyboard_state.right = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_a) : state.keyboard_state.a = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_s) : state.keyboard_state.s = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_d) : state.keyboard_state.d = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_w) : state.keyboard_state.w = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_q) : state.keyboard_state.q = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_e) : state.keyboard_state.e = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_1) : state.keyboard_state.one = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_2) : state.keyboard_state.two = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_3) : state.keyboard_state.three = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_4) : state.keyboard_state.four = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_5) : state.keyboard_state.five = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_6) : state.keyboard_state.six = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_7) : state.keyboard_state.seven = KeyboardKeyState_Pressed; break;
|
|
case (SDLK_8) : state.keyboard_state.eight = KeyboardKeyState_Pressed; break;
|
|
}
|
|
} else if (event.type == SDL_KEYUP) {
|
|
switch (event.key.keysym.sym) {
|
|
case (SDLK_UP) : state.keyboard_state.up = KeyboardKeyState_Released; break;
|
|
case (SDLK_DOWN) : state.keyboard_state.down = KeyboardKeyState_Released; break;
|
|
case (SDLK_LEFT) : state.keyboard_state.left = KeyboardKeyState_Released; break;
|
|
case (SDLK_RIGHT) : state.keyboard_state.right = KeyboardKeyState_Released; break;
|
|
case (SDLK_a) : state.keyboard_state.a = KeyboardKeyState_Released; break;
|
|
case (SDLK_s) : state.keyboard_state.s = KeyboardKeyState_Released; break;
|
|
case (SDLK_d) : state.keyboard_state.d = KeyboardKeyState_Released; break;
|
|
case (SDLK_w) : state.keyboard_state.w = KeyboardKeyState_Released; break;
|
|
case (SDLK_q) : state.keyboard_state.q = KeyboardKeyState_Released; break;
|
|
case (SDLK_e) : state.keyboard_state.e = KeyboardKeyState_Released; break;
|
|
case (SDLK_1) : state.keyboard_state.one = KeyboardKeyState_Released; break;
|
|
case (SDLK_2) : state.keyboard_state.two = KeyboardKeyState_Released; break;
|
|
case (SDLK_3) : state.keyboard_state.three = KeyboardKeyState_Released; break;
|
|
case (SDLK_4) : state.keyboard_state.four = KeyboardKeyState_Released; break;
|
|
case (SDLK_5) : state.keyboard_state.five = KeyboardKeyState_Released; break;
|
|
case (SDLK_6) : state.keyboard_state.six = KeyboardKeyState_Released; break;
|
|
case (SDLK_7) : state.keyboard_state.seven = KeyboardKeyState_Released; break;
|
|
case (SDLK_8) : state.keyboard_state.eight = KeyboardKeyState_Released; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Move to more accurate timing?
|
|
const u32 time_tick_start = SDL_GetTicks();
|
|
const f32 dt = (time_tick_start - time_prev_tick) / 1000.0f;
|
|
tick(dt);
|
|
time_prev_tick = time_tick_start;
|
|
|
|
render();
|
|
|
|
decay_keyboard_state(&state.keyboard_state);
|
|
|
|
// Get timer end for all the non-SDL stuff
|
|
gettimeofday(&timeval_end, NULL);
|
|
f64 game_ms_elapsed = (timeval_end.tv_sec - timeval_start.tv_sec) * 1000.0; // sec to ms
|
|
game_ms_elapsed += (timeval_end.tv_usec - timeval_start.tv_usec) / 1000.0; // us to ms
|
|
// printf("Game: %.3f ms, %.1f fps\n", game_ms_elapsed, 1000.0f / max(1.0f, game_ms_elapsed));
|
|
|
|
SDL_UpdateTexture(state.texture, NULL, state.pixels, SCREEN_SIZE_X * 4);
|
|
SDL_RenderCopyEx(
|
|
state.renderer,
|
|
state.texture,
|
|
NULL,
|
|
NULL,
|
|
0.0,
|
|
NULL,
|
|
SDL_FLIP_VERTICAL);
|
|
|
|
// SDL_RENDERER_PRESENTVSYNC means this is syncronized with the monitor refresh rate. (30Hz)
|
|
SDL_RenderPresent(state.renderer);
|
|
|
|
const u32 time_end = SDL_GetTicks();
|
|
const u32 ms_elapsed = time_end - time_start;
|
|
const f32 fps = 1000.0f / max(1, ms_elapsed);
|
|
// printf("FPS: %.1f\n", fps);
|
|
}
|
|
|
|
SDL_DestroyWindow(state.window);
|
|
return 0;
|
|
}
|
|
|