polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

commit eb14d3882d3db43f762989e0f8225415b4cdda1c
parent cff1ee0c2406cfd95201db625be7560c69d9038a
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 13 Nov 2018 12:07:06 -0800

input edge states, free camera

Diffstat:
MMakefile | 1-
Msrc/entity.h | 1+
Msrc/event.c | 52----------------------------------------------------
Msrc/event.h | 2--
Msrc/game.c | 36+++++++++++++++++++++++++++++-------
Msrc/game.h | 8+++++++-
Msrc/input.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/input.h | 29+++++++++++++++++++++++++++++
Msrc/main.c | 11+++++++++--
Msrc/orbit.c | 1+
Msrc/render.c | 8++++----
Msrc/render.h | 2+-
Msrc/update.c | 44++++++++++++++++++++++++--------------------
13 files changed, 200 insertions(+), 92 deletions(-)

diff --git a/Makefile b/Makefile @@ -17,7 +17,6 @@ OBJS += $(SRC)/camera.o OBJS += $(SRC)/debug.o OBJS += $(SRC)/delaunay.o OBJS += $(SRC)/entity.o -OBJS += $(SRC)/event.o OBJS += $(SRC)/fbo.o OBJS += $(SRC)/file.o OBJS += $(SRC)/game.o diff --git a/src/entity.h b/src/entity.h @@ -26,6 +26,7 @@ struct entity_id { }; struct entity *init_entity(struct entity *); +struct entity *delete_entities(); void init_entity_system(); struct entity *get_entity(struct entity_id *); struct entity *get_all_entities(u32 *count, struct entity_id **ids); diff --git a/src/event.c b/src/event.c @@ -4,55 +4,3 @@ #include "input.h" #include "game.h" -void process_events(struct game *game, float *camera) { - SDL_Event event; - int mdx = 0; - int mdy = 0; - - struct input *input = &game->input; - - input->last_mx = game->input.mx; - input->last_my = game->input.my; - - input_reset(&game->input); - - while (SDL_PollEvent(&event)) { - switch (event.type) { - case SDL_MOUSEWHEEL: - input->wheel_x = event.wheel.x; - input->wheel_y = event.wheel.y; - break; - case SDL_KEYDOWN: - case SDL_KEYUP: - handle_key(input, event.key); - break; - case SDL_MOUSEBUTTONDOWN: - if (event.button.button <= MOUSE_BUTTONS) - input->mbuttons[event.button.button-1] = 1; - - break; - case SDL_MOUSEBUTTONUP: - if (event.button.button <= MOUSE_BUTTONS) - input->mbuttons[event.button.button-1] = 0; - break; - case SDL_MOUSEMOTION: - input->mx = event.motion.x; - input->my = event.motion.y; - input->mdx += event.motion.xrel; - input->mdy += event.motion.yrel; - break; - case SDL_WINDOWEVENT: - switch (event.window.event) { - case SDL_WINDOWEVENT_RESIZED: - handle_resize(game, event.window.data1, event.window.data2); - break; - } - break; - case SDL_QUIT: - SDL_Quit(); - exit(0); - } - - } - -} diff --git a/src/event.h b/src/event.h @@ -6,6 +6,4 @@ #include "input.h" #include "game.h" -void process_events(struct game *game, float *camera); - #endif /* PA_EVENT_H */ diff --git a/src/game.c b/src/game.c @@ -20,6 +20,10 @@ mat4 *cam_init = (float[16]){ -71.766136, -47.881512, -44.216671, 1.000000 }; +int was_key_pressed_this_frame(struct game *game, int scancode) { + return is_key_down_on_frame(&game->input, scancode, game->frame); +} + static void camera_update(struct node *node) { mat4 *persp = (float*)node->custom_update_data; mat4 *mat = (float*)node->mat; @@ -34,6 +38,12 @@ struct entity *get_player(struct resources *res) { return player; } +struct entity *get_terrain_entity(struct terrain *t) { + struct entity *ent = get_entity(&t->entity_id); + assert(ent); + return ent; +} + static void init_user_settings(struct user_settings *settings) { SDL_SetRelativeMouseMode(SDL_TRUE); settings->mouse_sens = 0.1; @@ -48,7 +58,6 @@ void game_init(struct game *game, int width, int height) { struct resources *res = &game->test_resources; mat4 *mvp = res->test_mvp; struct node *root = &res->root; - struct node *camera = &res->camera.node; struct node *sun_camera = &res->sun_camera; struct terrain *terrain = &game->terrain; struct entity *player; @@ -56,6 +65,8 @@ void game_init(struct game *game, int width, int height) { mat4 *light_dir = res->light_dir; int ok = 0; + game->frame = 0; + const double size = 10000.0; terrain->settings = (struct perlin_settings){ @@ -104,22 +115,25 @@ void game_init(struct game *game, int width, int height) { node_init(root); node_init(sun_camera); - init_orbit(&res->camera); + init_orbit(&res->orbit_camera); + // ENTITIES // player entity player = new_entity(&res->player_id); + assert(res->player_id.index == 1); ok = load_model(&player->model, "pirate-officer"); assert(ok); player->node.label = "player"; node_attach(&player->node, root); - /* node_attach(&res->camera.node, &player->node); */ node_translate(&player->node, V3(terrain->size/2.,terrain->size/2.,0.0)); - res->camera.coords.azimuth = -quat_yaw(player->node.orientation) - RAD(90.0); - res->camera.coords.inclination = RAD(60); - res->camera.coords.radius = 5.0; + res->orbit_camera.coords.azimuth = -quat_yaw(player->node.orientation) - RAD(90.0); + res->orbit_camera.coords.inclination = RAD(60); + res->orbit_camera.coords.radius = 5.0; + + res->camera_node = &res->orbit_camera.node; struct entity *tower = new_entity(NULL); ok = load_model(&tower->model, "tower"); @@ -137,10 +151,18 @@ void game_init(struct game *game, int width, int height) { // player init root->label = "root"; - camera->label = "camera"; input_init(&game->input); + // free camera + node_init(&res->free_camera); + res->free_camera.label = "free_camera"; + node_attach(&res->free_camera, &player->node); + quat_axis_angle(V3(1,0,0), -45, res->free_camera.orientation); + node_rotate(&res->free_camera, V3(100, 0, 0)); + node_translate(&res->free_camera, V3(0,-40,20)); + + // FBO STUFF init_fbo(&res->shadow_buffer); resize_fbos(player, &res->shadow_buffer, res->proj_ortho, width, height); diff --git a/src/game.h b/src/game.h @@ -60,7 +60,9 @@ struct resources { struct node root; struct entity_id player_id; - struct orbit camera; + struct orbit orbit_camera; + struct node free_camera; + const struct node *camera_node; struct node sun_camera; u32 test_cubemap; @@ -84,6 +86,7 @@ struct game { int counter; int seed; float dt; + u64 frame; struct user_settings user_settings; struct ui ui; struct resources test_resources; @@ -92,7 +95,10 @@ struct game { }; struct entity *get_player(struct resources *); +struct entity *get_terrain_entity(struct terrain *); void game_init(struct game *game, int width, int height); void should_update(struct game *game); +int was_key_pressed_this_frame(struct game *game, int scancode); +int is_free_camera(struct game *game); #endif /* PA_GAME_H */ diff --git a/src/input.c b/src/input.c @@ -3,9 +3,95 @@ #include "util.h" #include "common.h" +static void key_down(struct input *input, int scancode, u64 current_frame) { + input->modifiers = SDL_GetModState(); + + struct key_edge *edge = &input->key_edge_states[scancode]; + + if (edge->is_down) + return; + + edge->down_frame = current_frame; + edge->is_down = 1; + + // might be useful to know when it was last up + /* edge->up_frame = 0; */ +} + +static void key_up(struct input *input, int scancode, u64 current_frame) { + struct key_edge *edge = &input->key_edge_states[scancode]; + + edge->up_frame = current_frame; + edge->is_down = 0; +} + + +void process_events(struct input *input, u64 current_frame) { + SDL_Event event; + + input->last_mx = input->mx; + input->last_my = input->my; + + input_reset(input); + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_MOUSEWHEEL: + input->wheel_x = event.wheel.x; + input->wheel_y = event.wheel.y; + break; + case SDL_KEYDOWN: + key_down(input, event.key.keysym.scancode, current_frame); + break; + case SDL_KEYUP: + key_up(input, event.key.keysym.scancode, current_frame); + break; + case SDL_MOUSEBUTTONDOWN: + if (event.button.button <= MOUSE_BUTTONS) + input->mbuttons[event.button.button-1] = 1; + + break; + case SDL_MOUSEBUTTONUP: + if (event.button.button <= MOUSE_BUTTONS) + input->mbuttons[event.button.button-1] = 0; + break; + case SDL_MOUSEMOTION: + input->mx = event.motion.x; + input->my = event.motion.y; + input->mdx += event.motion.xrel; + input->mdy += event.motion.yrel; + break; + case SDL_WINDOWEVENT: + switch (event.window.event) { + case SDL_WINDOWEVENT_RESIZED: + if (!event.window.data1) + continue; + printf("resizing %d %d\n", event.window.data1, event.window.data2); + input->resized_width = event.window.data1; + input->resized_height = event.window.data2; + assert(input->resized_width); + assert(input->resized_height); + break; + } + break; + case SDL_QUIT: + SDL_Quit(); + exit(0); + } + + } + + if (input->resized_width) + printf("checking resize %d %d\n", input->resized_width, + input->resized_height); + +} + void input_init(struct input *input) { /* memset(input->keys, 0, sizeof(input->keys[0]) * ARRAY_SIZE(input->keys)); */ input->keystates = SDL_GetKeyboardState(NULL); + assert(sizeof(input->key_edge_states) == SDL_NUM_SCANCODES * sizeof(input->key_edge_states[0])); + memset(input->key_edge_states, 0, sizeof(input->key_edge_states)); input->mx = 0; input->my = 0; input->mdx = 0; @@ -14,11 +100,16 @@ void input_init(struct input *input) { input->wheel_y = 0; input->last_mx = 0; input->last_my = 0; + input->resized_height = 0; + input->resized_width = 0; assert(input->keystates); } -void handle_key(struct input *input, SDL_KeyboardEvent key) { - input->modifiers = SDL_GetModState(); +int is_key_down_on_frame(struct input *input, u8 scancode, u64 frame) { + struct key_edge *edge = &input->key_edge_states[scancode]; + + // is_down is implied, but do it for good measure + return edge->down_frame == frame && edge->is_down; } @@ -27,6 +118,8 @@ void input_reset(struct input *input) { input->mdy = 0; input->wheel_x = 0; input->wheel_y = 0; + input->resized_height = 0; + input->resized_width = 0; } int input_is_dragging(struct input *input, int mouse_button) { diff --git a/src/input.h b/src/input.h @@ -14,6 +14,22 @@ #define MOUSE_BUTTONS 5 +/* #define KEY_BUFFER_SIZE 32 */ + +#define FLAG_KEY_DOWN (1<<0) +#define FLAG_KEY_UP (1<<1) + +enum key_edge_state { + EDGE_KEY_DOWN = 1 << 0, + EDGE_KEY_UP = 1 << 1, +}; + +struct key_edge { + int is_down; + u64 down_frame; + u64 up_frame; +}; + struct input { /* enum key_state keys[0x7F-0x1F]; */ u8 const *keystates; @@ -22,14 +38,27 @@ struct input { int mdx, mdy; float wheel_x, wheel_y; int mbuttons[MOUSE_BUTTONS]; + int n_frame_down_keys; + int n_frame_up_keys; + int resized_width; + int resized_height; + /* u64 down_key_frames[KEY_BUFFER_SIZE]; */ + /* u64 up_key_frames[KEY_BUFFER_SIZE]; */ + /* u8 frame_down_keys[KEY_BUFFER_SIZE]; */ + /* u8 frame_up_keys[KEY_BUFFER_SIZE]; */ + struct key_edge key_edge_states[SDL_NUM_SCANCODES]; }; int input_is_dragging(struct input *input, int mouse_button); +int is_key_down_on_frame(struct input *input, u8 scancode, u64 frame); + void input_init(struct input *input); void input_reset(struct input *input); void handle_key(struct input *input, SDL_KeyboardEvent); +void process_events(struct input *input, u64 current_frame); + #endif /* POLYADVENT_INPUT_H */ diff --git a/src/main.c b/src/main.c @@ -73,13 +73,20 @@ int main(void) struct render_config default_config = { .draw_ui = 0, .is_depth_pass = 0, - .camera = game.test_resources.camera.node.mat, + .camera = game.test_resources.camera_node->mat, .projection = game.test_resources.proj_persp, .depth_vp = depth_vp }; while (1) { - process_events(&game, game.test_resources.proj_persp); + game.frame++; + process_events(&game.input, game.frame); + if (game.input.resized_height) { + printf("handling resize %d %d\n", game.input.resized_width, + game.input.resized_height); + handle_resize(&game, game.input.resized_width, game.input.resized_height); + } + default_config.camera = game.test_resources.camera_node->mat; double new_time = hires_time_in_seconds(); double frame_time = new_time - last; game.dt = frame_time; diff --git a/src/orbit.c b/src/orbit.c @@ -73,6 +73,7 @@ static void orbit_node_update(struct node *node) { void init_orbit(struct orbit *orbit) { node_init(&orbit->node); + orbit->node.label = "orbit_camera"; /* orbit->node.custom_update = orbit_node_update; */ /* orbit->node.custom_update_data = orbit; */ } diff --git a/src/render.c b/src/render.c @@ -260,7 +260,7 @@ void render (struct game *game, struct render_config *config) { mat4 *projection = config->projection; mat4 *light = res->light_dir; - float *camera = config->camera; + const mat4 *camera = config->camera; u32 num_entities; struct entity *entities = @@ -276,7 +276,7 @@ void render (struct game *game, struct render_config *config) { /* mat4_multiply(view_proj, res->skybox.node.mat, mvp); */ - mat4_inverse(camera, view); + mat4_inverse((float*)camera, view); mat4_multiply(projection, view, view_proj); if (config->is_depth_pass) { @@ -287,7 +287,7 @@ void render (struct game *game, struct render_config *config) { glCullFace(GL_BACK); } - mat4_inverse(camera, view); + mat4_inverse((float *)camera, view); mat4_multiply(projection, view, view_proj); glBindTexture(GL_TEXTURE_CUBE_MAP, res->skybox.model.texture); @@ -351,7 +351,7 @@ void render (struct game *game, struct render_config *config) { } if (!config->is_depth_pass) { - mat4_inverse(camera, view); + mat4_inverse((float*)camera, view); mat4_remove_translations(view); mat4_multiply(projection, view, view_proj); diff --git a/src/render.h b/src/render.h @@ -8,7 +8,7 @@ struct game; struct render_config { int draw_ui; int is_depth_pass; - float *camera; + const float *camera; float *projection; float *depth_vp; }; diff --git a/src/update.c b/src/update.c @@ -8,6 +8,7 @@ #include "camera.h" #include "poisson.h" #include "uniform.h" +#include "game.h" #include "mat_util.h" #include "shader.h" #include "file.h" @@ -55,7 +56,7 @@ static void movement(struct game *game, struct node *node, float speed_mult) { /* if (game->input.keystates[SDL_SCANCODE_DOWN]) */ /* node_translate(node, V3(0, 0, -amt)); */ - if (game->input.keystates[SDL_SCANCODE_P]) { + if (was_key_pressed_this_frame(game, SDL_SCANCODE_P)) { printf("%f %f %f\n", node->pos[0], node->pos[1], @@ -312,15 +313,14 @@ void orbit_update_from_mouse(struct orbit *camera, struct input *input, } -static void orbit_keep_above_ground(struct game *game) { - struct resources *res = &game->test_resources; - - if (!(get_entity(&game->terrain.entity_id)->flags & ENT_INVISIBLE)) { - float *camera_world = node_world(&res->camera.node); +static void camera_keep_above_ground(struct terrain *terrain, + const struct node *camera) { + if (!(get_entity(&terrain->entity_id)->flags & ENT_INVISIBLE)) { + float *camera_world = node_world((struct node*)camera); /* float *target = node_world(&get_player(res)->node); */ /* spherical_pos(&res->camera.coords, target, camera_world); */ float cam_terrain_z = - game->terrain.fn(&game->terrain, camera_world[0], camera_world[1]); + terrain->fn(terrain, camera_world[0], camera_world[1]); const float bias = 2.0; @@ -331,18 +331,17 @@ static void orbit_keep_above_ground(struct game *game) { static void player_update(struct game *game, struct entity *player) { - struct orbit *camera = &game->test_resources.camera; + struct resources *res = &game->test_resources; + struct orbit *camera = &res->orbit_camera; + orbit_update_from_mouse(camera, &game->input, game->user_settings.mouse_sens, player, game->dt); - orbit_keep_above_ground(game); - - /* look_at(node_world(&camera->node), node_world(&player->node), UP_VEC, */ - /* camera->node.mat); */ + camera_keep_above_ground(&game->terrain, res->camera_node); // move player camera toward camera orientation if (input_is_dragging(&game->input, SDL_BUTTON_RIGHT)) { - float yaw = game->test_resources.camera.coords.azimuth; + float yaw = game->test_resources.orbit_camera.coords.azimuth; quat_axis_angle(V3(0.0, 0.0, 1.0), -yaw - RAD(90), player->node.orientation); } player_terrain_collision(&game->terrain, player); @@ -372,25 +371,30 @@ void update (struct game *game) { /* vec3_scale(camera_dir, -1, camera_dir); */ if (game->input.modifiers & KMOD_LALT) { - movement(game, &res->camera.node, 1.0); + if (res->camera_node == &res->free_camera) + movement(game, &res->free_camera, 1.0); } else { player_movement(game, player); } player_update(game, player); - -#ifdef DEBUG - if (game->input.keystates[SDL_SCANCODE_R]) + if (was_key_pressed_this_frame(game, SDL_SCANCODE_R)) try_reload_shaders(res); -#endif - if (game->input.keystates[SDL_SCANCODE_C]) + if (was_key_pressed_this_frame(game, SDL_SCANCODE_C)) printf("light_dir %f %f %f\n", light[0], light[1], light[2]); - if (game->input.keystates[SDL_SCANCODE_F]) + if (was_key_pressed_this_frame(game, SDL_SCANCODE_F)) toggle_fog = 1; + if (was_key_pressed_this_frame(game, SDL_SCANCODE_EQUALS)) { + if (res->camera_node != &res->free_camera) + res->camera_node = &res->free_camera; + else + res->camera_node = &res->orbit_camera.node; + } + if (toggle_fog) { res->fog_on = !res->fog_on; toggle_fog = 0;