player focus dir relative to player. raw mouse input. crosshair.

This commit is contained in:
jacob 2024-03-13 21:44:25 -05:00
parent aa8dd7a4fa
commit bdd8db60c3
18 changed files with 388 additions and 183 deletions

BIN
res/graphics/crosshair.ase (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -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)

View File

@ -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

View File

@ -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
}
};

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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
* ========================== */

View File

@ -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 {

View File

@ -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

View File

@ -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
* ========================== */

View File

@ -11,6 +11,7 @@
#include "util.h"
#include <Windows.h>
#include <windowsx.h>
#include <ShlObj_core.h>
#include <fileapi.h>
#include <dwmapi.h>
@ -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);
{
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);

View File

@ -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);
}

View File

@ -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);

View File

@ -30,6 +30,7 @@ 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 */
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"))));

View File

@ -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
};

View File

@ -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
};
}

View File

@ -13,13 +13,12 @@ struct world {
f64 time;
struct v2 player_move_dir; /* Player movement direction */
struct v2 player_focus; /* Mouse cursor pos in world coordinates */
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