From bdd8db60c3405c0229c4b595fe6644c38e643b98 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 13 Mar 2024 21:44:25 -0500 Subject: [PATCH] player focus dir relative to player. raw mouse input. crosshair. --- res/graphics/crosshair.ase | 3 + src/ase.c | 1 - src/config.h | 19 +++- src/draw.c | 8 +- src/entity.c | 27 ++++- src/entity.h | 6 +- src/font.c | 3 +- src/game.c | 34 ++---- src/game.h | 7 +- src/math.h | 36 ++++++ src/sys.h | 18 ++- src/sys_win32.c | 164 ++++++++++++++++++++++----- src/texture.c | 8 +- src/texture.h | 3 +- src/user.c | 220 ++++++++++++++++++++++--------------- src/user.h | 1 + src/world.c | 8 -- src/world.h | 5 +- 18 files changed, 388 insertions(+), 183 deletions(-) create mode 100644 res/graphics/crosshair.ase diff --git a/res/graphics/crosshair.ase b/res/graphics/crosshair.ase new file mode 100644 index 00000000..dbc07ae2 --- /dev/null +++ b/res/graphics/crosshair.ase @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dabc422b506ea27dc447e16ba9450e8f3f69e7237a0c055c97b247dc3223d927 +size 345 diff --git a/src/ase.c b/src/ase.c index d34829b9..61f21e9f 100644 --- a/src/ase.c +++ b/src/ase.c @@ -32,7 +32,6 @@ INTERNAL u32 peek_bits(struct bitbuf *bb, u32 nbits) val32 &= U32_MAX >> (32 - nbits); return val32; - } INTERNAL u32 consume_bits(struct bitbuf *bb, u32 nbits) diff --git a/src/config.h b/src/config.h index d9b5430d..ad1fd000 100644 --- a/src/config.h +++ b/src/config.h @@ -7,10 +7,7 @@ #define PIXELS_PER_UNIT 256 -#define AUDIO_ENABLED 0 -#define VSYNC_ENABLED 0 - -#define GAME_FPS 30 +#define GAME_FPS 50 #define USER_FRAME_LIMIT 300 @@ -18,4 +15,16 @@ * Delay ms = USER_INTERP_OFFSET_TICK_RATIO * Game tick rate * E.g: At 1.5, the user thread will render 49.5ms back in time (if game thread runs at 30FPS) */ -#define USER_INTERP_OFFSET_TICK_RATIO 1.5 +#define USER_INTERP_OFFSET_TICK_RATIO 1.2 + + +/* ========================== * + * Settings + * ========================== */ + +/* TODO: Move these to user-configurable settings */ + +#define AUDIO_ENABLED 0 +#define VSYNC_ENABLED 0 + +#define MOUSE_SENSITIVITY 1.5 diff --git a/src/draw.c b/src/draw.c index 09ff6e96..a5152e7b 100644 --- a/src/draw.c +++ b/src/draw.c @@ -295,13 +295,13 @@ void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 p struct clip_rect clip = { { - glyph->atlas_rect.x / font->texture.width, - glyph->atlas_rect.y / font->texture.height + glyph->atlas_rect.x / font->texture.size.x, + glyph->atlas_rect.y / font->texture.size.y }, { - (glyph->atlas_rect.x + glyph->atlas_rect.width) / font->texture.width, - (glyph->atlas_rect.y + glyph->atlas_rect.height) / font->texture.height + (glyph->atlas_rect.x + glyph->atlas_rect.width) / font->texture.size.x, + (glyph->atlas_rect.y + glyph->atlas_rect.height) / font->texture.size.y } }; diff --git a/src/entity.c b/src/entity.c index b536b97b..2359ca37 100644 --- a/src/entity.c +++ b/src/entity.c @@ -63,11 +63,18 @@ void entity_release(struct entity_store *store, struct entity *entity) store->first_free = handle; } - /* ========================== * - * Lookup + * Query * ========================== */ +struct entity_array entity_store_as_array(struct entity_store *store) +{ + return (struct entity_array) { + .entities = (struct entity *)store->arena.base, + .count = store->count + }; +} + /* Returns a valid entity or nil entity. Always safe to read result, need to check to write. */ struct entity *entity_from_handle(struct entity_store *store, struct entity_handle handle) { @@ -81,6 +88,22 @@ struct entity *entity_from_handle(struct entity_store *store, struct entity_hand return entity_nil(); } +struct entity *entity_find_first_with_prop(struct entity_store *store, enum entity_prop prop) +{ + struct entity_array entities_array = entity_store_as_array(store); + for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { + struct entity *ent = &entities_array.entities[entity_index]; + if (ent->valid && entity_has_prop(ent, prop)) { + return ent; + } + } + return entity_nil(); +} + +/* ========================== * + * Tree + * ========================== */ + void entity_link(struct entity_store *store, struct entity *parent, struct entity *child) { struct entity *first_child = entity_from_handle(store, parent->first); diff --git a/src/entity.h b/src/entity.h index 2681c501..d632bd9c 100644 --- a/src/entity.h +++ b/src/entity.h @@ -14,7 +14,6 @@ enum entity_prop { /* Test props */ ENTITY_PROP_TEST, - ENTITY_PROP_TEST_FOLLOW_MOUSE, ENTITY_PROP_TEST_SOUND_EMITTER, ENTITY_PROP_COUNT @@ -60,6 +59,7 @@ struct entity { /* ENTITY_PROP_PLAYER_CONTROLLED */ f32 player_max_speed; f32 player_acceleration; + struct v2 player_focus_dir; /* ====================================================================== */ /* Sprite */ @@ -146,7 +146,11 @@ void entity_store_copy_replace(struct entity_store *dest, struct entity_store *s /* Entity */ struct entity *entity_alloc(struct entity_store *store); void entity_release(struct entity_store *store, struct entity *entity); + +struct entity_array entity_store_as_array(struct entity_store *store); struct entity *entity_from_handle(struct entity_store *store, struct entity_handle handle); +struct entity *entity_find_first_with_prop(struct entity_store *store, enum entity_prop prop); + void entity_link(struct entity_store *store, struct entity *parent, struct entity *child); #endif diff --git a/src/font.c b/src/font.c index 1cdb4b48..264da243 100644 --- a/src/font.c +++ b/src/font.c @@ -119,8 +119,7 @@ INTERNAL void font_load_asset_task(void *vparams) /* Set font data */ font->texture = (struct texture) { .renderer_handle = texture_renderer_handle, - .width = result.image_data.width, - .height = result.image_data.height + .size = V2(result.image_data.width, result.image_data.height), }; font->glyphs_count = result.glyphs_count; font->point_size = point_size; diff --git a/src/game.c b/src/game.c index 183c422c..359e8d45 100644 --- a/src/game.c +++ b/src/game.c @@ -121,6 +121,7 @@ INTERNAL void game_update(void) entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); e->player_max_speed = 5.f; e->player_acceleration = 15.0f; + e->player_focus_dir = V2(0, -1); entity_enable_prop(e, ENTITY_PROP_ANIMATING); @@ -180,7 +181,7 @@ INTERNAL void game_update(void) entity_link(&L.world.entity_store, parent, e); if (sys_rand_u32() % 2 == 0) { - u64 parent_idx = sys_rand_u32() % world_get_entities(&L.world).count; + u64 parent_idx = sys_rand_u32() % entity_store_as_array(&L.world.entity_store).count; struct entity *rand_ent = entity_from_handle(&L.world.entity_store, (struct entity_handle) { .idx = parent_idx, .gen = 1 }); if (rand_ent->valid) { parent = rand_ent; @@ -208,13 +209,13 @@ INTERNAL void game_update(void) L.world.tick_ts = sys_timestamp(); L.world.dt = max_f64(0.0, (1.0 / GAME_FPS) * L.world.timescale); L.world.time += L.world.dt; - struct entity_array entities_array = world_get_entities(&L.world); + struct entity_array entities_array = entity_store_as_array(&L.world.entity_store); /* ========================== * * Process game cmds * ========================== */ - L.world.player_move_dir = V2(0, 0); + L.world.player_focus_move_dir = V2(0, 0); struct game_cmd_array game_cmds = pop_cmds(scratch.arena); for (u64 cmd_index = 0; cmd_index < game_cmds.count; ++cmd_index) { @@ -223,13 +224,8 @@ INTERNAL void game_update(void) switch (cmd.kind) { /* Movement */ case GAME_CMD_KIND_PLAYER_MOVE: { - struct v2 dir = cmd.dir; - L.world.player_move_dir = v2_add(L.world.player_move_dir, dir); - } break; - - /* Focus */ - case GAME_CMD_KIND_PLAYER_FOCUS: { - L.world.player_focus = cmd.pos; + L.world.player_move_dir = cmd.move_dir; + L.world.player_focus_move_dir = v2_add(L.world.player_focus_move_dir, cmd.focus_move_dir); } break; /* Clear level */ @@ -361,26 +357,20 @@ INTERNAL void game_update(void) } /* ========================== * - * Player look direction + * Player focus * ========================== */ if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { + /* Update focus */ + ent->player_focus_dir = v2_add(ent->player_focus_dir, L.world.player_focus_move_dir); + + /* Update view angle */ struct v2 ent_pos = ent->rel_xform.og; - struct v2 look_pos = L.world.player_focus; + struct v2 look_pos = v2_add(ent_pos, ent->player_focus_dir); f32 r = v2_angle_to_point(ent_pos, look_pos) + PI / 2; ent->rel_xform = xform_with_rotation(ent->rel_xform, r); } - /* ========================== * - * Update position from mouse - * ========================== */ - - /* ENTITY_PROP_TEST_FOLLOW_MOUSE */ - if (entity_has_prop(ent, ENTITY_PROP_TEST_FOLLOW_MOUSE)) { - ent->rel_xform.og = L.world.player_focus; - ent->test_start_rel_xform.og = L.world.player_focus; - } - /* ========================== * * Calculate xforms * ========================== */ diff --git a/src/game.h b/src/game.h index e6c8cc11..5ecb0d98 100644 --- a/src/game.h +++ b/src/game.h @@ -7,7 +7,6 @@ enum game_cmd_kind { GAME_CMD_KIND_NONE, GAME_CMD_KIND_PLAYER_MOVE, - GAME_CMD_KIND_PLAYER_FOCUS, /* Testing */ GAME_CMD_KIND_CLEAR_ALL, @@ -19,10 +18,8 @@ struct game_cmd { enum game_cmd_kind kind; /* GAME_CMD_KIND_PLAYER_MOVE */ - struct v2 dir; - - /* GAME_CMD_KIND_PLAYER_FOCUS */ - struct v2 pos; + struct v2 move_dir; + struct v2 focus_move_dir; }; struct game_cmd_array { diff --git a/src/math.h b/src/math.h index 87a12eb5..8f0f8911 100644 --- a/src/math.h +++ b/src/math.h @@ -323,6 +323,22 @@ INLINE struct v2 v2_round(struct v2 a) ); } +INLINE struct v2 v2_floor(struct v2 a) +{ + return V2( + (f32)math_floor(a.x), + (f32)math_floor(a.y) + ); +} + +INLINE struct v2 v2_ceil(struct v2 a) +{ + return V2( + (f32)math_ceil(a.x), + (f32)math_ceil(a.y) + ); +} + INLINE f32 v2_dot(struct v2 a, struct v2 b) { return a.x * b.x + a.y * b.y; @@ -751,4 +767,24 @@ INLINE struct quad quad_mul_xform(struct quad quad, struct xform m) }; } +INLINE struct quad quad_round(struct quad quad) +{ + return (struct quad) { + v2_round(quad.p1), + v2_round(quad.p2), + v2_round(quad.p3), + v2_round(quad.p4) + }; +} + +INLINE struct quad quad_floor(struct quad quad) +{ + return (struct quad) { + v2_floor(quad.p1), + v2_round(quad.p2), + v2_round(quad.p3), + v2_round(quad.p4) + }; +} + #endif diff --git a/src/sys.h b/src/sys.h index 72b9ad5b..2be3294a 100644 --- a/src/sys.h +++ b/src/sys.h @@ -13,10 +13,9 @@ enum sys_event_kind { SYS_EVENT_KIND_BUTTON_DOWN, SYS_EVENT_KIND_BUTTON_UP, - //SYS_EVENT_KIND_MOUSE_MOVE, - + SYS_EVENT_KIND_MOUSE_MOVE, + SYS_EVENT_KIND_RAW_MOUSE_MOVE, SYS_EVENT_KIND_TEXT, - SYS_EVENT_KIND_QUIT, SYS_EVENT_KIND_COUNT @@ -134,7 +133,10 @@ struct sys_event { u32 text_character; - struct v2 position; + struct v2 mouse_position; + struct v2 mouse_delta; + + struct v2 raw_mouse_delta; }; struct sys_event_array { @@ -276,14 +278,18 @@ struct sys_window_settings sys_window_get_settings(struct sys_window *sys_window void sys_window_show(struct sys_window *sys_window); +void sys_window_set_cursor_pos(struct sys_window *sys_window, struct v2 pos); + struct v2 sys_window_get_size(struct sys_window *sys_window); struct v2 sys_window_get_monitor_size(struct sys_window *sys_window); -struct v2 sys_window_get_mouse_pos(struct sys_window *sys_window); - /* Returns a platform specific representation of the window. E.g. `hwnd` on win32. */ u64 sys_window_get_internal_handle(struct sys_window *sys_window); +void sys_window_cursor_set_pos(struct sys_window *sys_window, struct v2 pos); +void sys_window_cursor_show(struct sys_window *sys_window); +void sys_window_cursor_hide(struct sys_window *sys_window); + /* ========================== * * Mutex * ========================== */ diff --git a/src/sys_win32.c b/src/sys_win32.c index 3b66bf1f..15e8c5ff 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -11,6 +11,7 @@ #include "util.h" #include +#include #include #include #include @@ -31,13 +32,21 @@ struct win32_condition_variable { struct win32_condition_variable *next_free; }; +enum win32_window_cursor_set_flag { + WIN32_WINDOW_CURSOR_SET_FLAG_NONE = 0x0, + WIN32_WINDOW_CURSOR_SET_FLAG_POSITION = 0x1, + WIN32_WINDOW_CURSOR_SET_FLAG_HIDE = 0x2, + WIN32_WINDOW_CURSOR_SET_FLAG_SHOW = 0x4 +}; + struct win32_window { u32 flags; HWND hwnd; + u32 tid; struct sync_flag ready_sf; - struct sys_mutex settings_mutex; + struct sys_rw_mutex settings_rw_mutex; struct sys_window_settings settings; i32 monitor_width; @@ -48,6 +57,10 @@ struct win32_window { * their pre-minimized values) */ i32 x, y, width, height; + struct v2 cursor_pos; + u32 cursor_set_flags; + struct v2 cursor_set_position; + b32 event_thread_shutdown; struct sys_thread event_thread; @@ -489,13 +502,13 @@ INTERNAL void win32_update_window_from_system(struct win32_window *window); INTERNAL void win32_window_process_event(struct win32_window *window, struct sys_event event) { + sys_mutex_lock(&window->event_callbacks_mutex); { - sys_mutex_lock(&window->event_callbacks_mutex); for (u64 i = 0; i < window->event_callbacks_count; ++i) { window->event_callbacks[i](event); } - sys_mutex_unlock(&window->event_callbacks_mutex); } + sys_mutex_unlock(&window->event_callbacks_mutex); } INTERNAL HWND win32_create_window(struct win32_window *window) @@ -541,6 +554,7 @@ INTERNAL void window_thread_entry_point(void *arg) /* Win32 limitation: Window must be initialized on same thread that processes events */ window->hwnd = win32_create_window(window); + window->tid = sys_thread_id(); win32_update_window_from_system(window); BringWindowToTop(window->hwnd); @@ -561,9 +575,33 @@ INTERNAL void window_thread_entry_point(void *arg) } break; } - /* TODO: update mouse pos w/ GetCursorPos */ + /* Update cursor */ + if (GetFocus() == window->hwnd) { + u32 cursor_flags = window->cursor_set_flags; + + /* Hide cursor */ + if (cursor_flags & WIN32_WINDOW_CURSOR_SET_FLAG_HIDE) { + while(ShowCursor(false) >= 0); + } + + /* Show cursor */ + if (cursor_flags & WIN32_WINDOW_CURSOR_SET_FLAG_SHOW) { + while(ShowCursor(true) < 0); + } + + /* Update position */ + if (cursor_flags & WIN32_WINDOW_CURSOR_SET_FLAG_POSITION) { + struct v2 window_space_pos = window->cursor_set_position; + POINT p = { window_space_pos.x, window_space_pos.y }; + ClientToScreen(window->hwnd, &p); + SetCursorPos(p.x, p.y); + } + + window->cursor_set_flags = 0; + } } + /* Destroy window hwnd */ DestroyWindow(window->hwnd); } @@ -588,7 +626,7 @@ INTERNAL struct win32_window *win32_window_alloc(void) window->ready_sf = sync_flag_alloc(); /* Allocate mutexes */ - window->settings_mutex = sys_mutex_alloc(); + window->settings_rw_mutex = sys_rw_mutex_alloc(); window->event_callbacks_mutex = sys_mutex_alloc(); /* Start window thread for processing events */ @@ -613,7 +651,7 @@ INTERNAL void win32_window_release(struct win32_window *window) /* Release mutexes */ sys_mutex_release(&window->event_callbacks_mutex); - sys_mutex_release(&window->settings_mutex); + sys_rw_mutex_release(&window->settings_rw_mutex); /* Release sync flag */ sync_flag_release(&window->ready_sf); @@ -729,17 +767,10 @@ INTERNAL void win32_update_window_from_settings(struct win32_window *window, str SetWindowTextA(hwnd, settings->title); } -INTERNAL struct v2 win32_window_get_mouse_pos(struct win32_window *window) +INTERNAL void win32_window_wake(struct win32_window *window) { - HWND hwnd = window->hwnd; - - struct v2 res = V2(0, 0); - POINT p; - if (GetCursorPos(&p) && ScreenToClient(hwnd, &p)) { - res = V2(p.x, p.y); - } - - return res; + /* Post a blank message to the window's thread message queue to wake it. */ + PostThreadMessageA(window->tid, 0, 0, 0); } INTERNAL LRESULT CALLBACK win32_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) @@ -867,7 +898,7 @@ INTERNAL LRESULT CALLBACK win32_window_proc(HWND hwnd, UINT msg, WPARAM wparam, (struct sys_event) { .kind = event_kind, .button = button, - .position = win32_window_get_mouse_pos(window) + .mouse_position = window->cursor_pos } ); } @@ -885,6 +916,53 @@ INTERNAL LRESULT CALLBACK win32_window_proc(HWND hwnd, UINT msg, WPARAM wparam, } } break; + /* Mouse move */ + case WM_MOUSEMOVE: { + i32 x = GET_X_LPARAM(lparam); + i32 y = GET_Y_LPARAM(lparam); + struct v2 old = window->cursor_pos; + window->cursor_pos = V2(x, y); + struct v2 delta = v2_sub(window->cursor_pos, old); + win32_window_process_event( + window, + (struct sys_event) { + .kind = SYS_EVENT_KIND_MOUSE_MOVE, + .mouse_position = window->cursor_pos, + .mouse_delta = delta + } + ); + } break; + + /* Raw mouse move */ + case WM_INPUT: { + struct temp_arena scratch = scratch_begin_no_conflict(); + + /* Read raw input buffer */ + UINT buff_size; + GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &buff_size, sizeof(RAWINPUTHEADER)); + u8 *buff = arena_push_array_zero(scratch.arena, u8, buff_size); + if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, buff, &buff_size, sizeof(RAWINPUTHEADER)) != buff_size) { + logf_error("GetRawInputData did not return correct size"); + break; + } + RAWINPUT *raw = (RAWINPUT *)buff; + + if (raw->header.dwType == RIM_TYPEMOUSE) { + i32 x = raw->data.mouse.lLastX; + i32 y = raw->data.mouse.lLastY; + struct v2 delta = V2(x, y); + win32_window_process_event( + window, + (struct sys_event) { + .kind = SYS_EVENT_KIND_RAW_MOUSE_MOVE, + .raw_mouse_delta = delta + } + ); + } + + scratch_end(scratch); + } break; + default: { result = DefWindowProcA(hwnd, msg, wparam, lparam); @@ -930,7 +1008,6 @@ void sys_window_unregister_event_callback(struct sys_window *sys_window, sys_win sys_mutex_lock(&window->event_callbacks_mutex); { - u64 count = window->event_callbacks_count; sys_window_event_callback_func *last = count > 0 ? window->event_callbacks[count - 1] : NULL; @@ -951,11 +1028,11 @@ void sys_window_update_settings(struct sys_window *sys_window, struct sys_window { __prof; struct win32_window *window = (struct win32_window *)sys_window->handle; - sys_mutex_lock(&window->settings_mutex); + sys_rw_mutex_lock_exclusive(&window->settings_rw_mutex); { win32_update_window_from_settings(window, settings); } - sys_mutex_unlock(&window->settings_mutex); + sys_rw_mutex_unlock_exclusive(&window->settings_rw_mutex); } /* TODO: Lock settings mutex for these functions */ @@ -970,7 +1047,7 @@ void sys_window_show(struct sys_window *sys_window) { struct win32_window *window = (struct win32_window *)sys_window->handle; HWND hwnd = window->hwnd; - sys_mutex_lock(&window->settings_mutex); + sys_rw_mutex_lock_exclusive(&window->settings_rw_mutex); { i32 show_cmd = SW_NORMAL; struct sys_window_settings *settings = &window->settings; @@ -983,7 +1060,7 @@ void sys_window_show(struct sys_window *sys_window) ShowWindow(hwnd, show_cmd); BringWindowToTop(hwnd); } - sys_mutex_unlock(&window->settings_mutex); + sys_rw_mutex_unlock_exclusive(&window->settings_rw_mutex); } struct v2 sys_window_get_size(struct sys_window *sys_window) @@ -998,18 +1075,34 @@ struct v2 sys_window_get_monitor_size(struct sys_window *sys_window) return V2((f32)window->monitor_width, (f32)window->monitor_height); } -struct v2 sys_window_get_mouse_pos(struct sys_window *sys_window) -{ - struct win32_window *window = (struct win32_window *)sys_window->handle; - return win32_window_get_mouse_pos(window); -} - u64 sys_window_get_internal_handle(struct sys_window *sys_window) { struct win32_window *window = (struct win32_window *)sys_window->handle; return (u64)window->hwnd; } +void sys_window_cursor_set_pos(struct sys_window *sys_window, struct v2 pos) +{ + struct win32_window *window = (struct win32_window *)sys_window->handle; + window->cursor_set_position = pos; + window->cursor_set_flags |= WIN32_WINDOW_CURSOR_SET_FLAG_POSITION; + win32_window_wake(window); +} + +void sys_window_cursor_show(struct sys_window *sys_window) +{ + struct win32_window *window = (struct win32_window *)sys_window->handle; + window->cursor_set_flags |= WIN32_WINDOW_CURSOR_SET_FLAG_SHOW; + win32_window_wake(window); +} + +void sys_window_cursor_hide(struct sys_window *sys_window) +{ + struct win32_window *window = (struct win32_window *)sys_window->handle; + window->cursor_set_flags |= WIN32_WINDOW_CURSOR_SET_FLAG_HIDE; + win32_window_wake(window); +} + /* ========================== * * Mutex * ========================== */ @@ -1719,6 +1812,21 @@ int CALLBACK WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, } } + /* Register raw input */ + { + RAWINPUTDEVICE rid = (RAWINPUTDEVICE) { + .usUsagePage = 0x01, /* HID_USAGE_PAGE_GENERIC */ + .usUsage = 0x02, /* HID_USAGE_GENERIC_MOUSE */ + //.dwFlags = RIDEV_NOLEGACY /* Adds mouse and also ignores legacy mouse messages */ + }; + + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + /* TODO: GetLastError */ + error_msg = "Failed to register raw input device"; + goto error; + } + } + error: if (error_msg) { MessageBoxExA(NULL, error_msg, "Fatal initialization error", MB_ICONSTOP, 0); diff --git a/src/texture.c b/src/texture.c index a588e355..b9d6653e 100644 --- a/src/texture.c +++ b/src/texture.c @@ -167,8 +167,7 @@ INTERNAL void texture_load_asset_task(void *vparams) struct asset_cache_store store = asset_cache_store_open(); texture = arena_push(store.arena, struct texture); *texture = (struct texture) { - .width = image_data.width, - .height = image_data.height, + .size = V2(image_data.width, image_data.height), .renderer_handle = handle }; asset_cache_store_close(&store); @@ -200,9 +199,8 @@ abort: struct asset_cache_store store = asset_cache_store_open(); texture = arena_push(store.arena, struct texture); *texture = (struct texture) { - .width = default_image_data.width, - .height = default_image_data.height, - .renderer_handle = handle + .renderer_handle = handle, + .size = V2(default_image_data.width, default_image_data.height) }; asset_cache_store_close(&store); } diff --git a/src/texture.h b/src/texture.h index 00df8789..5578f4a8 100644 --- a/src/texture.h +++ b/src/texture.h @@ -23,8 +23,7 @@ struct texture_anim { struct texture { struct renderer_handle renderer_handle; - u32 width; - u32 height; + struct v2 size; }; void texture_startup(void); diff --git a/src/user.c b/src/user.c index 187e1bd0..0f9b8f56 100644 --- a/src/user.c +++ b/src/user.c @@ -28,8 +28,9 @@ struct view { }; struct bind_state { - b32 is_held; /* Is this bind held down this frame */ - u32 num_presses; /* How many times was this bind pressed since last frame */ + b32 is_held; /* Is this bind held down this frame */ + u32 num_presses; /* How many times was this bind pressed since last frame */ + u32 num_releases; /* How many times was this bind released since last frame */ }; struct blend_tick { @@ -57,7 +58,7 @@ GLOBAL struct { b32 debug_camera; b32 debug_camera_panning; - struct v2 debug_camera_panning_from; + struct v2 debug_camera_pan_start; b32 debug_draw; @@ -71,6 +72,9 @@ GLOBAL struct { struct v2 screen_size; struct v2 screen_center; struct v2 screen_mouse; + struct v2 screen_mouse_delta; + struct v2 world_mouse; + struct v2 last_focus_screen_pos; } L = { 0 }, DEBUG_LVAR(L_user); /* ========================== * @@ -91,7 +95,8 @@ GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = { [SYS_BTN_F6] = USER_BIND_KIND_DEBUG_DRAW, [SYS_BTN_F7] = USER_BIND_KIND_DEBUG_CAMERA, [SYS_BTN_MWHEELUP] = USER_BIND_KIND_ZOOM_IN, - [SYS_BTN_MWHEELDOWN] = USER_BIND_KIND_ZOOM_OUT + [SYS_BTN_MWHEELDOWN] = USER_BIND_KIND_ZOOM_OUT, + [SYS_BTN_M3] = USER_BIND_KIND_PAN }; /* ========================== * @@ -314,22 +319,17 @@ INTERNAL struct xform view_get_xform(struct view view) return res; } -INTERNAL struct v2 view_xform_point(struct view view, struct v2 p) +INTERNAL struct v2 view_xform_v2(struct view view, struct v2 p) { struct xform mtx = view_get_xform(view); return xform_mul_v2(mtx, p); } -INTERNAL struct v2 view_inverse_xform_point(struct view view, struct v2 p) +INTERNAL struct v2 view_inverse_xform_v2(struct view view, struct v2 p) { return xform_invert_mul_v2(view_get_xform(view), p); } -INTERNAL struct v2 view_mouse_pos(struct view view) -{ - return view_inverse_xform_point(view, L.screen_mouse); -} - /* ========================== * * Update * ========================== */ @@ -395,7 +395,6 @@ INTERNAL void user_update(void) L.screen_center = v2_mul(L.screen_size, 0.5); /* Read input */ - L.screen_mouse = sys_window_get_mouse_pos(L.window); struct sys_event_array events = pop_sys_events(scratch.arena); /* ========================== * @@ -409,6 +408,8 @@ INTERNAL void user_update(void) }; } + L.screen_mouse_delta = V2(0, 0); + for (u64 entity_index = 0; entity_index < events.count; ++entity_index) { struct sys_event *event = &events.events[entity_index]; @@ -421,16 +422,6 @@ INTERNAL void user_update(void) app_quit(); } - /* Move camera/view */ - if (event->button == SYS_BTN_M3) { - if (event->kind == SYS_EVENT_KIND_BUTTON_DOWN) { - L.debug_camera_panning = true; - L.debug_camera_panning_from = view_mouse_pos(L.world_view); - } else if (event->kind == SYS_EVENT_KIND_BUTTON_UP) { - L.debug_camera_panning = false; - } - } - if (event->kind == SYS_EVENT_KIND_BUTTON_UP) { #if DEVELOPER /* Escape quit */ @@ -453,6 +444,16 @@ INTERNAL void user_update(void) } } + /* Update mouse pos */ + if (event->kind == SYS_EVENT_KIND_MOUSE_MOVE) { + L.screen_mouse = event->mouse_position; + } + + /* Update mouse delta */ + if (event->kind == SYS_EVENT_KIND_RAW_MOUSE_MOVE) { + L.screen_mouse_delta = v2_add(L.screen_mouse_delta, event->raw_mouse_delta); + } + /* Update bind states */ if ((event->kind == SYS_EVENT_KIND_BUTTON_DOWN || event->kind == SYS_EVENT_KIND_BUTTON_UP) && !event->is_repeat) { enum sys_btn button = event->button; @@ -463,52 +464,13 @@ INTERNAL void user_update(void) L.bind_states[bind].is_held = pressed; if (pressed) { ++L.bind_states[bind].num_presses; + } else { + ++L.bind_states[bind].num_releases; } } } } - /* ========================== * - * Process movement input - * ========================== */ - - /* Movement */ - { - struct v2 input_move_dir = { 0 }; - for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(L.bind_states); ++bind) { - struct bind_state state = L.bind_states[bind]; - - if (!state.is_held && state.num_presses <= 0) { - continue; - } - - switch (bind) { - /* Movement */ - case USER_BIND_KIND_MOVE_UP: { - input_move_dir.y -= 1; - } break; - - case USER_BIND_KIND_MOVE_DOWN: { - input_move_dir.y += 1; - } break; - - case USER_BIND_KIND_MOVE_LEFT: { - input_move_dir.x -= 1; - } break; - - case USER_BIND_KIND_MOVE_RIGHT: { - input_move_dir.x += 1; - } break; - - default: break; - } - } - queue_game_cmd(&cmd_list, (struct game_cmd) { - .kind = GAME_CMD_KIND_PLAYER_MOVE, - .dir = v2_norm(input_move_dir) - }); - } - /* ========================== * * Debug commands * ========================== */ @@ -532,18 +494,27 @@ INTERNAL void user_update(void) } if (L.bind_states[USER_BIND_KIND_DEBUG_CAMERA].num_presses > 0) { + if (!L.debug_camera) { + /* Move cursor to last crosshair pos */ + sys_window_cursor_set_pos(L.window, L.last_focus_screen_pos); + } L.debug_camera = !L.debug_camera; } if (L.debug_camera) { - - //L.world_view.rot += 1 * (f32)L.dt; + sys_window_cursor_show(L.window); /* Pan view */ - if (L.debug_camera_panning) { - struct v2 offset = v2_sub(L.debug_camera_panning_from, view_mouse_pos(L.world_view)); + if (L.bind_states[USER_BIND_KIND_PAN].is_held) { + if (!L.debug_camera_panning) { + L.debug_camera_pan_start = view_inverse_xform_v2(L.world_view, L.screen_mouse); + } + L.debug_camera_panning = true; + struct v2 offset = v2_sub(L.debug_camera_pan_start, view_inverse_xform_v2(L.world_view, L.screen_mouse)); L.world_view.center = v2_add(L.world_view.center, offset); - L.debug_camera_panning_from = view_mouse_pos(L.world_view); + L.debug_camera_pan_start = view_inverse_xform_v2(L.world_view, L.screen_mouse); + } else { + L.debug_camera_panning = false; } /* Zoom view */ @@ -566,14 +537,65 @@ INTERNAL void user_update(void) new_zoom = clamp_f32(new_zoom, zoom_min, zoom_max); } - struct v2 old_mouse = view_mouse_pos(L.world_view); + struct v2 old_mouse = view_inverse_xform_v2(L.world_view, L.screen_mouse); L.world_view.zoom = new_zoom; - struct v2 new_mouse = view_mouse_pos(L.world_view); + struct v2 new_mouse = view_inverse_xform_v2(L.world_view, L.screen_mouse); /* Offset view to zoom in on mouse */ struct v2 offset = v2_sub(old_mouse, new_mouse); L.world_view.center = v2_add(L.world_view.center, offset); } + } else { + /* Keep cursor invisible at center of screen */ + sys_window_cursor_hide(L.window); + sys_window_cursor_set_pos(L.window, L.screen_center); + } + + /* ========================== * + * Process movement input + * ========================== */ + + /* Movement */ + { + struct v2 player_move_dir = { 0 }; + for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(L.bind_states); ++bind) { + struct bind_state state = L.bind_states[bind]; + + if (!state.is_held && state.num_presses <= 0) { + continue; + } + + switch (bind) { + /* Movement */ + case USER_BIND_KIND_MOVE_UP: { + player_move_dir.y -= 1; + } break; + + case USER_BIND_KIND_MOVE_DOWN: { + player_move_dir.y += 1; + } break; + + case USER_BIND_KIND_MOVE_LEFT: { + player_move_dir.x -= 1; + } break; + + case USER_BIND_KIND_MOVE_RIGHT: { + player_move_dir.x += 1; + } break; + + default: break; + } + } + struct game_cmd cmd = (struct game_cmd) { + .kind = GAME_CMD_KIND_PLAYER_MOVE, + .move_dir = v2_norm(player_move_dir) + }; + if (!L.debug_camera) { + struct v2 focus_move_dir = v2_div(L.screen_mouse_delta, PIXELS_PER_UNIT * L.world_view.zoom); + focus_move_dir = v2_mul(focus_move_dir, MOUSE_SENSITIVITY); + cmd.focus_move_dir = focus_move_dir; + } + queue_game_cmd(&cmd_list, cmd); } /* ========================== * @@ -582,7 +604,6 @@ INTERNAL void user_update(void) /* Pull ticks */ - /* FIXME: use real game DT */ f64 blend_time_offset = (1.0 / GAME_FPS) * USER_INTERP_OFFSET_TICK_RATIO; f64 blend_time = L.time > blend_time_offset ? L.time - blend_time_offset : 0; @@ -610,9 +631,9 @@ INTERNAL void user_update(void) L.world.time = math_lerp_f64(t0->time, t1->time, (f64)tick_blend); /* Blend entities */ - struct entity_array t0_entities = world_get_entities(t0); - struct entity_array t1_entities = world_get_entities(t1); - struct entity_array world_entities = world_get_entities(&L.world); + struct entity_array t0_entities = entity_store_as_array(&t0->entity_store); + struct entity_array t1_entities = entity_store_as_array(&t1->entity_store); + struct entity_array world_entities = entity_store_as_array(&L.world.entity_store); u64 num_entities = min_u64(t0_entities.count, t1_entities.count); for (u64 i = 0; i < num_entities; ++i) { @@ -626,6 +647,7 @@ INTERNAL void user_update(void) e->acceleration = v2_lerp(e0->acceleration, e1->acceleration, tick_blend); e->velocity = v2_lerp(e0->velocity, e1->velocity, tick_blend); e->player_acceleration = math_lerp_f32(e0->player_acceleration, e1->player_acceleration, tick_blend); + e->player_focus_dir = v2_lerp(e0->player_focus_dir, e1->player_focus_dir, tick_blend); e->sprite_xform = xform_lerp(e0->sprite_xform, e1->sprite_xform, tick_blend); e->animation_time_in_frame = math_lerp_f64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend); @@ -638,13 +660,12 @@ INTERNAL void user_update(void) #endif } - struct entity_array entities_array = world_get_entities(&L.world); + struct entity_array entities_array = entity_store_as_array(&L.world.entity_store); /* ========================== * * Update view from game camera * ========================== */ - /* Find camera */ for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { struct entity *ent = &entities_array.entities[entity_index]; if (!ent->valid) continue; @@ -657,6 +678,7 @@ INTERNAL void user_update(void) L.world_view.center = center; L.world_view.rot = rot; L.world_view.zoom = zoom; + L.world_mouse = view_inverse_xform_v2(L.world_view, L.screen_mouse); } } @@ -776,6 +798,22 @@ INTERNAL void user_update(void) } } + /* Draw crosshair */ + if (!L.debug_camera && entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { + struct v2 focus_pos = v2_add(ent->world_xform.og, ent->player_focus_dir); + struct v2 focus_pos_screen = view_xform_v2(L.world_view, focus_pos); + u32 tint = RGBA_F(1, 1, 1, 0.5); + + struct texture *t = texture_load_async(STR("res/graphics/crosshair.ase")); + if (t) { + struct xform xf = XFORM_TRS(.t = focus_pos_screen, .s = t->size); + struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf); + draw_texture_quad(L.screen_canvas, DRAW_TEXTURE_PARAMS(.texture = t, .tint = tint), quad); + } + + L.last_focus_screen_pos = focus_pos_screen; + } + /* Debug draw info */ if (L.debug_draw && !is_camera) { struct temp_arena temp = arena_temp_begin(scratch.arena); @@ -790,7 +828,7 @@ INTERNAL void user_update(void) f32 offset = 1; struct v2 pos = v2_add(xf.og, v2_mul(V2(0, -1), offset)); - pos = view_xform_point(L.world_view, pos); + pos = view_xform_v2(L.world_view, pos); pos = v2_round(pos); struct string disp_name = ent->sprite_name; @@ -827,8 +865,21 @@ INTERNAL void user_update(void) f32 thickness = 5; f32 arrow_height = 15; - struct v2 start = view_xform_point(L.world_view, ent->world_xform.og); - struct v2 end = view_xform_point(L.world_view, parent->world_xform.og); + struct v2 start = view_xform_v2(L.world_view, ent->world_xform.og); + struct v2 end = view_xform_v2(L.world_view, parent->world_xform.og); + draw_solid_arrow_line(L.screen_canvas, start, end, thickness, arrow_height, color); + } + + /* Draw focus */ + if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { + u32 color = RGBA_F(0.75, 0, 0.75, 0.5); + f32 thickness = 3; + f32 arrow_height = 10; + + struct v2 focus_pos = v2_add(ent->world_xform.og, ent->player_focus_dir); + + struct v2 start = view_xform_v2(L.world_view, ent->world_xform.og); + struct v2 end = view_xform_v2(L.world_view, focus_pos); draw_solid_arrow_line(L.screen_canvas, start, end, thickness, arrow_height, color); } @@ -842,15 +893,6 @@ INTERNAL void user_update(void) /* Update listener using world view */ mixer_set_listener(L.world_view.center, V2(-math_sin(L.world_view.rot), -math_cos(L.world_view.rot))); - /* Send world mouse pos */ - struct v2 world_mouse = view_mouse_pos(L.world_view); - if (!L.debug_camera) { - queue_game_cmd(&cmd_list, (struct game_cmd) { - .kind = GAME_CMD_KIND_PLAYER_FOCUS, - .pos = world_mouse - }); - } - /* Debug draw info */ if (L.debug_draw) { struct temp_arena temp = arena_temp_begin(scratch.arena); @@ -880,7 +922,7 @@ INTERNAL void user_update(void) draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("world_view.zoom: %F"), FMT_FLOAT((f64)L.world_view.zoom))); pos.y += spacing; - draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("world_mouse: (%F, %F)"), FMT_FLOAT((f64)world_mouse.x), FMT_FLOAT((f64)world_mouse.y))); + draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("world_mouse: (%F, %F)"), FMT_FLOAT((f64)L.world_mouse.x), FMT_FLOAT((f64)L.world_mouse.y))); pos.y += spacing; draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("debug_camera: %F"), FMT_STR(L.debug_camera ? STR("true") : STR("false")))); diff --git a/src/user.h b/src/user.h index a57cbd20..6d09dcdc 100644 --- a/src/user.h +++ b/src/user.h @@ -18,6 +18,7 @@ enum user_bind_kind { USER_BIND_KIND_DEBUG_CAMERA, USER_BIND_KIND_ZOOM_IN, USER_BIND_KIND_ZOOM_OUT, + USER_BIND_KIND_PAN, USER_BIND_KIND_COUNT }; diff --git a/src/world.c b/src/world.c index 0d520bc2..95d7f0eb 100644 --- a/src/world.c +++ b/src/world.c @@ -22,11 +22,3 @@ void world_copy_replace(struct world *dest, struct world *src) scratch_end(scratch); } - -struct entity_array world_get_entities(struct world *world) -{ - return (struct entity_array) { - .entities = (struct entity *)world->entity_store.arena.base, - .count = world->entity_store.count - }; -} diff --git a/src/world.h b/src/world.h index 6920b248..58c4999a 100644 --- a/src/world.h +++ b/src/world.h @@ -12,14 +12,13 @@ struct world { f64 dt; f64 time; - struct v2 player_move_dir; /* Player movement direction */ - struct v2 player_focus; /* Mouse cursor pos in world coordinates */ + struct v2 player_move_dir; /* Player movement direction */ + struct v2 player_focus_move_dir; /* Focus movement direction */ struct entity_store entity_store; }; void world_alloc(struct world *world); void world_copy_replace(struct world *dest, struct world *src); -struct entity_array world_get_entities(struct world *world); #endif