diff --git a/src/common.h b/src/common.h index 5b5fb5a7..054acac6 100644 --- a/src/common.h +++ b/src/common.h @@ -266,27 +266,27 @@ typedef i32 b32; typedef u64 umm; #endif -#define U8_MAX (0xFF) -#define U16_MAX (0xFFFF) -#define U32_MAX (0xFFFFFFFF) -#define U64_MAX (0xFFFFFFFFFFFFFFFFULL) +#define U8_MAX (0xFF) +#define U16_MAX (0xFFFF) +#define U32_MAX (0xFFFFFFFF) +#define U64_MAX (0xFFFFFFFFFFFFFFFFULL) -#define I8_MAX (0x7F) -#define I16_MAX (0x7FFF) -#define I32_MAX (0x7FFFFFFF) -#define I64_MAX (0x7FFFFFFFFFFFFFFFLL) +#define I8_MAX (0x7F) +#define I16_MAX (0x7FFF) +#define I32_MAX (0x7FFFFFFF) +#define I64_MAX (0x7FFFFFFFFFFFFFFFLL) -#define I8_MIN ((i8)-0x80) -#define I16_MIN ((i16)-0x8000) -#define I32_MIN ((i32)-0x80000000) -#define I64_MIN ((i64)-0x8000000000000000LL) +#define I8_MIN ((i8)-0x80) +#define I16_MIN ((i16)-0x8000) +#define I32_MIN ((i32)-0x80000000) +#define I64_MIN ((i64)-0x8000000000000000LL) #define F32_INFINITY (1.0 / 0.0f) #define F32_MAX (3.402823466e+38F) #define F32_MIN (1.175494351e-38F) -#define PI ((f32)3.14159265358979323846) -#define TAU ((f32)6.28318530717958647693) +#define PI ((f32)3.14159265358979323846) +#define TAU ((f32)6.28318530717958647693) /* ========================== * * Atomics @@ -444,11 +444,17 @@ struct mat4x4 { f32 e[4][4]; }; -#define RECT(x, y, width, height) (struct rect) { (x), (y), (width), (height) } +#define RECT(_x, _y, _width, _height) (struct rect) { .x = (_x), .y = (_y), .width = (_width), .height = (_height) } +#define RECT_FROM_V2(_pos, _size) (struct rect) { .pos = (_pos), .size = (_size) } struct rect { - f32 x, y, width, height; + union { + struct { f32 x, y, width, height; }; + struct { struct v2 pos, size; }; + }; }; +INLINE b32 rect_eq(struct rect r1, struct rect r2) { return r1.x == r2.x && r1.y == r2.y && r1.width == r2.width && r1.height == r2.height; } + /* Values expected to be normalized 0.0 -> 1.0 */ #define CLIP_ALL ((struct clip_rect) { { 0.0f, 0.0f }, { 1.0f, 1.0f } }) struct clip_rect { @@ -457,7 +463,6 @@ struct clip_rect { #define QUAD_UNIT_SQUARE (struct quad) { V2(0, 0), V2(0, 1), V2(1, 1), V2(1, 0) } #define QUAD_UNIT_SQUARE_CENTERED (struct quad) { V2(-0.5f, -0.5f), V2(0.5f, -0.5f), V2(0.5f, 0.5f), V2(-0.5f, 0.5f) } - struct quad { struct v2 p1, p2, p3, p4; }; diff --git a/src/config.h b/src/config.h index f84f8db1..18c1ce41 100644 --- a/src/config.h +++ b/src/config.h @@ -5,7 +5,8 @@ * system. */ #define RESOURCES_EMBEDDED !(DEVELOPER) -#define ASPECT_RATIO (16.0 / 9.0) +//#define ASPECT_RATIO (16.0 / 9.0) +#define ASPECT_RATIO (4.0 / 3.0) #define PIXELS_PER_UNIT 256 #define GAME_FPS 50 diff --git a/src/renderer.h b/src/renderer.h index d92d5a55..b43b5bd8 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -72,7 +72,7 @@ void renderer_canvas_ensure_texture_cmd(struct renderer_canvas *canvas, struct t void renderer_canvas_send_to_gpu(struct renderer_canvas *canvas); -void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_count, f32 viewport_width, f32 viewport_height, i32 vsync); +void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_count, struct v2 screen_size, struct rect viewport, i32 vsync); /* ========================== * * Texture diff --git a/src/renderer_d3d11.c b/src/renderer_d3d11.c index 29d4caed..7fd20cde 100644 --- a/src/renderer_d3d11.c +++ b/src/renderer_d3d11.c @@ -124,8 +124,11 @@ GLOBAL struct { ID3D11DeviceContext *devcon; IDXGISwapChain1 *swapchain; - ID3D11RenderTargetView *back_buffer_view; - D3D11_VIEWPORT viewport; /* Here for caching/comparison */ + ID3D11RenderTargetView *backbuffer_view; + + /* Here for caching/comparison */ + struct v2 backbuffer_size; + struct rect viewport; ID3D11BlendState *blend_state; ID3D11RasterizerState *rasterizer_state; @@ -789,35 +792,37 @@ void renderer_canvas_send_to_gpu(struct renderer_canvas *canvas) * Present canvas * ========================== */ -INTERNAL void recreate_backbuffer_and_viewport(f32 width, f32 height) +INTERNAL void resize_backbuffer(struct v2 size) { __prof; /* TODO: error handling */ /* Release all outstanding references to the swap chain's buffers. */ - if (L.back_buffer_view) { - ID3D11RenderTargetView_Release(L.back_buffer_view); + if (L.backbuffer_view) { + ID3D11RenderTargetView_Release(L.backbuffer_view); } - IDXGISwapChain_ResizeBuffers(L.swapchain, 0, (UINT)width, (UINT)height, DXGI_FORMAT_UNKNOWN, 0); + IDXGISwapChain_ResizeBuffers(L.swapchain, 0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); /* Get buffer and create a render-target-view. */ - ID3D11Texture2D *back_buffer_texture = NULL; - IDXGISwapChain_GetBuffer(L.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&back_buffer_texture); + ID3D11Texture2D *backbuffer_texture = NULL; + IDXGISwapChain_GetBuffer(L.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&backbuffer_texture); - ID3D11Device_CreateRenderTargetView(L.dev, (ID3D11Resource *)back_buffer_texture, NULL, &L.back_buffer_view); - ID3D11Texture2D_Release(back_buffer_texture); + ID3D11Device_CreateRenderTargetView(L.dev, (ID3D11Resource *)backbuffer_texture, NULL, &L.backbuffer_view); + ID3D11Texture2D_Release(backbuffer_texture); +} - /* Set up the viewport */ - L.viewport = (D3D11_VIEWPORT){ - .Width = width, - .Height = height, +INTERNAL void resize_viewport(struct rect viewport) +{ + D3D11_VIEWPORT d3d11_viewport = { + .Width = viewport.width, + .Height = viewport.height, .MinDepth = 0.0f, .MaxDepth = 1.0f, - .TopLeftX = 0, - .TopLeftY = 0 + .TopLeftX = viewport.x, + .TopLeftY = viewport.y }; - ID3D11DeviceContext_RSSetViewports(L.devcon, 1, &L.viewport); + ID3D11DeviceContext_RSSetViewports(L.devcon, 1, &d3d11_viewport); } /* TODO: Lock canvas or at least global state? (in-case multi-threaded present). @@ -825,21 +830,26 @@ INTERNAL void recreate_backbuffer_and_viewport(f32 width, f32 height) * research if that is smart first). * * I'm thinking we may also just need to lock texture modification access while presenting */ -void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_count, f32 viewport_width, f32 viewport_height, i32 vsync) +void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_count, struct v2 screen_size, struct rect viewport, i32 vsync) { __prof; /* Resize back buffer */ - D3D11_VIEWPORT old_vp = L.viewport; - if (old_vp.Width != viewport_width || old_vp.Height != viewport_height) { - recreate_backbuffer_and_viewport(viewport_width, viewport_height); + if (!v2_eq(L.backbuffer_size, screen_size)) { + resize_backbuffer(screen_size); + L.backbuffer_size = screen_size; } - ID3D11DeviceContext_OMSetRenderTargets(L.devcon, 1, &L.back_buffer_view, NULL); + if (!rect_eq(L.viewport, viewport)) { + resize_viewport(viewport); + L.viewport = viewport; + } + + ID3D11DeviceContext_OMSetRenderTargets(L.devcon, 1, &L.backbuffer_view, NULL); /* Clear back buffer */ - f32 clear_color[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - ID3D11DeviceContext_ClearRenderTargetView(L.devcon, L.back_buffer_view, clear_color); + f32 clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + ID3D11DeviceContext_ClearRenderTargetView(L.devcon, L.backbuffer_view, clear_color); /* Set draw mode */ ID3D11DeviceContext_IASetPrimitiveTopology(L.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); @@ -850,7 +860,7 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou /* Fill and set constant buffer * NOTE: We're only doing this once per canvas, rather than once per draw call since * the only constant right now is VP. */ - struct mat4x4 vp_matrix = calculate_vp(canvas->view, viewport_width, viewport_height); + struct mat4x4 vp_matrix = calculate_vp(canvas->view, viewport.width, viewport.height); send_constant_buffer_data(L.vs_constant_buffer, vp_matrix); ID3D11DeviceContext_VSSetConstantBuffers(L.devcon, 0, 1, &L.vs_constant_buffer); @@ -898,7 +908,7 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou IDXGISwapChain1_Present(L.swapchain, vsync, 0); __profframe(0); } - renderer_capture_image_for_profiler(viewport_width, viewport_height); + renderer_capture_image_for_profiler(viewport.width, viewport.height); } /* ========================== * @@ -989,22 +999,22 @@ INTERNAL void renderer_capture_image_for_profiler(f32 width, f32 height) static u32 cap_index = 0; static b32 ready_to_read = false; - ID3D11Texture2D *back_buffer = NULL; - IDXGISwapChain_GetBuffer(L.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *) & back_buffer); + ID3D11Texture2D *backbuffer = NULL; + IDXGISwapChain_GetBuffer(L.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&backbuffer); struct prof_cap *write_cap = &staging_caps[cap_index]; *write_cap = (struct prof_cap) { .size = V2(width, height) }; { D3D11_TEXTURE2D_DESC staging_desc; - ID3D11Texture2D_GetDesc(back_buffer, &staging_desc); + ID3D11Texture2D_GetDesc(backbuffer, &staging_desc); staging_desc.Usage = D3D11_USAGE_STAGING; staging_desc.BindFlags = 0; staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; ID3D11Device_CreateTexture2D(L.dev, &staging_desc, NULL, &write_cap->texture); } - ID3D11DeviceContext_CopyResource(L.devcon, (ID3D11Resource *)write_cap->texture, (ID3D11Resource *)back_buffer); - ID3D11Texture2D_Release(back_buffer); + ID3D11DeviceContext_CopyResource(L.devcon, (ID3D11Resource *)write_cap->texture, (ID3D11Resource *)backbuffer); + ID3D11Texture2D_Release(backbuffer); ++cap_index; if (cap_index >= ARRAY_COUNT(staging_caps)) { diff --git a/src/sys_win32.c b/src/sys_win32.c index 86b647c5..f8fc7e60 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -1626,7 +1626,7 @@ u32 sys_rand_u32(void) void sys_panic_raw(char *msg_cstr) { - app_quit(); + /* FIXME: Exit other threads before showing message box */ MessageBoxExA(NULL, msg_cstr, "Fatal error", MB_ICONSTOP, 0); ASSERT(false); sys_exit(); @@ -1813,7 +1813,7 @@ int CALLBACK WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, wc->lpszClassName = "power_play_window_class"; wc->hCursor = LoadCursor(NULL, IDC_ARROW); wc->style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wc->hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + //wc->hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wc->lpfnWndProc = win32_window_proc; wc->hInstance = instance; diff --git a/src/user.c b/src/user.c index 8fddf40e..1dd84921 100644 --- a/src/user.c +++ b/src/user.c @@ -36,7 +36,8 @@ GLOBAL struct { struct sys_window *window; struct renderer_canvas *world_canvas; - struct renderer_canvas *screen_canvas; + struct renderer_canvas *viewport_bg_canvas; + struct renderer_canvas *viewport_canvas; struct xform world_view_last_frame; struct xform world_view; @@ -60,8 +61,11 @@ GLOBAL struct { f64 time; f64 dt; struct v2 screen_size; - struct v2 screen_center; struct v2 screen_cursor; + struct v2 viewport_screen_offset; + struct v2 viewport_size; + struct v2 viewport_center; + struct v2 viewport_cursor; struct v2 world_cursor; } L = { 0 }, DEBUG_LVAR(L_user); @@ -304,9 +308,9 @@ INTERNAL void debug_draw_xform(struct xform xf) struct quad quad = quad_from_rect(RECT(0, 0, 1, -1)); quad = quad_mul_xform(quad_scale(quad, 0.075), xf); - draw_solid_arrow_ray(L.screen_canvas, pos, x_ray, thickness, arrowhead_len, color_x); - draw_solid_arrow_ray(L.screen_canvas, pos, y_ray, thickness, arrowhead_len, color_y); - draw_solid_quad(L.screen_canvas, quad, color); + draw_solid_arrow_ray(L.viewport_canvas, pos, x_ray, thickness, arrowhead_len, color_x); + draw_solid_arrow_ray(L.viewport_canvas, pos, y_ray, thickness, arrowhead_len, color_y); + draw_solid_quad(L.viewport_canvas, quad, color); } /* TODO: remove this (testing) */ @@ -322,8 +326,8 @@ INTERNAL void debug_draw_movement(struct entity *ent) struct v2 vel_ray = xform_basis_mul_v2(L.world_view, ent->velocity); struct v2 acc_ray = xform_basis_mul_v2(L.world_view, ent->acceleration); - draw_solid_arrow_ray(L.screen_canvas, pos, vel_ray, thickness, arrow_len, color_vel); - draw_solid_arrow_ray(L.screen_canvas, pos, acc_ray, thickness, arrow_len, color_acc); + draw_solid_arrow_ray(L.viewport_canvas, pos, vel_ray, thickness, arrow_len, color_vel); + draw_solid_arrow_ray(L.viewport_canvas, pos, acc_ray, thickness, arrow_len, color_acc); } INTERNAL void user_update(void) @@ -338,9 +342,32 @@ INTERNAL void user_update(void) L.dt = max_f64(0.0, cur_time - L.time); L.time += L.dt; - /* Get screen dimensions */ - L.screen_size = sys_window_get_size(L.window); - L.screen_center = v2_mul(L.screen_size, 0.5); + /* Calculate screen & viewport dimensions */ + { + /* Get screen dimensions */ + L.screen_size = sys_window_get_size(L.window); + + /* Enforce viewport at aspect ratio */ + f32 aspect_ratio = ASPECT_RATIO; + f32 width = L.screen_size.x; + f32 height = L.screen_size.y; + if (width / height > aspect_ratio) { + width = height * aspect_ratio; + } else { + height = width / aspect_ratio; + } + L.viewport_size = V2(width, height); + + /* Center viewport in window */ + f32 x = 0; + f32 y = 0; + x = math_round(L.screen_size.x / 2 - width / 2); + y = math_round(L.screen_size.y / 2 - height / 2); + L.viewport_screen_offset = V2(x, y); + + /* Get center */ + L.viewport_center = v2_mul(L.viewport_size, 0.5); + } /* ========================== * * Produce interpolated tick @@ -469,6 +496,7 @@ INTERNAL void user_update(void) /* Update mouse pos */ if (event->kind == SYS_EVENT_KIND_CURSOR_MOVE) { L.screen_cursor = event->cursor_position; + L.viewport_cursor = v2_sub(L.screen_cursor, L.viewport_screen_offset);; } /* Update bind states */ @@ -520,12 +548,12 @@ INTERNAL void user_update(void) /* Pan view */ if (L.bind_states[USER_BIND_KIND_PAN].is_held) { if (!L.debug_camera_panning) { - L.debug_camera_pan_start = xform_invert_mul_v2(L.world_view, L.screen_cursor); + L.debug_camera_pan_start = xform_invert_mul_v2(L.world_view, L.viewport_cursor); } L.debug_camera_panning = true; - struct v2 offset = v2_sub(L.debug_camera_pan_start, xform_invert_mul_v2(L.world_view, L.screen_cursor)); + struct v2 offset = v2_sub(L.debug_camera_pan_start, xform_invert_mul_v2(L.world_view, L.viewport_cursor)); L.world_view = xform_translate(L.world_view, v2_neg(offset)); - L.debug_camera_pan_start = xform_invert_mul_v2(L.world_view, L.screen_cursor); + L.debug_camera_pan_start = xform_invert_mul_v2(L.world_view, L.viewport_cursor); } else { L.debug_camera_panning = false; } @@ -548,7 +576,7 @@ INTERNAL void user_update(void) } /* Zoom to cursor */ - struct v2 world_cursor = xform_invert_mul_v2(L.world_view, L.screen_cursor); + struct v2 world_cursor = xform_invert_mul_v2(L.world_view, L.viewport_cursor); L.world_view = xform_translate(L.world_view, world_cursor); L.world_view = xform_scale(L.world_view, V2(zoom, zoom)); L.world_view = xform_translate(L.world_view, v2_neg(world_cursor)); @@ -560,7 +588,7 @@ INTERNAL void user_update(void) zoom = zoom > 0 ? zoom : 1; struct trs trs = TRS( - .t = v2_sub(L.screen_center, center), + .t = v2_sub(L.viewport_center, center), .r = rot, .s = v2_mul(V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT), zoom) ); @@ -570,7 +598,7 @@ INTERNAL void user_update(void) L.world_view = xform_translate(L.world_view, pivot); L.world_view = xform_trs_pivot_rs(L.world_view, trs, pivot); } - L.world_cursor = xform_invert_mul_v2(L.world_view, L.screen_cursor); + L.world_cursor = xform_invert_mul_v2(L.world_view, L.viewport_cursor); /* ========================== * * Update listener @@ -578,17 +606,26 @@ INTERNAL void user_update(void) { struct v2 up = V2(0, -1); - struct v2 listener_pos = xform_invert_mul_v2(L.world_view, L.screen_center); + struct v2 listener_pos = xform_invert_mul_v2(L.world_view, L.viewport_center); struct v2 listener_dir = v2_norm(xform_basis_invert_mul_v2(L.world_view, up)); mixer_set_listener(listener_pos, listener_dir); } + /* ========================== * + * Draw test BG + * ========================== */ + + { + u32 color = RGBA_F(0.2f, 0.2f, 0.2f, 1.f); + draw_solid_rect(L.viewport_bg_canvas, RECT(0, 0, L.viewport_size.x, L.viewport_size.y), color); + } + /* ========================== * * Draw test grid * ========================== */ { - f32 thickness = 3.f / xform_get_scale(L.world_view).x; + f32 thickness = 3.f; u32 color = RGBA(0x3f, 0x3f, 0x3f, 0xFF); u32 x_color = RGBA(0x3f, 0, 0, 0xFF); u32 y_color = RGBA(0, 0x3f, 0, 0xFF); @@ -599,25 +636,25 @@ INTERNAL void user_update(void) i64 cols = 20; /* Draw column lines */ - struct v2 col_ray = V2(0, rows); + struct v2 col_ray = xform_basis_mul_v2(L.world_view, V2(0, rows)); for (i64 col = starty; col <= (starty + cols); ++col) { u32 line_color = color; if (col == 0) { line_color = y_color; } - struct v2 pos = V2(col, starty); - draw_solid_ray(L.world_canvas, pos, col_ray, thickness, line_color); + struct v2 pos = xform_mul_v2(L.world_view, V2(col, starty)); + draw_solid_ray(L.viewport_bg_canvas, pos, col_ray, thickness, line_color); } - struct v2 row_ray = V2(cols, 0); + struct v2 row_ray = xform_basis_mul_v2(L.world_view, V2(cols, 0)); for (i64 row = startx; row <= (startx + rows); ++row) { u32 line_color = color; if (row == 0) { line_color = x_color; } - struct v2 pos = V2(startx, row); - draw_solid_ray(L.world_canvas, pos, row_ray, thickness, line_color); + struct v2 pos = xform_mul_v2(L.world_view, V2(startx, row)); + draw_solid_ray(L.viewport_bg_canvas, pos, row_ray, thickness, line_color); } } @@ -736,7 +773,7 @@ INTERNAL void user_update(void) ); - draw_text(L.screen_canvas, disp_font, pos, text); + draw_text(L.viewport_canvas, disp_font, pos, text); } #endif @@ -752,7 +789,7 @@ INTERNAL void user_update(void) struct v2 start = xform_mul_v2(L.world_view, ent->world_xform.og); struct v2 end = xform_mul_v2(L.world_view, parent->world_xform.og); - draw_solid_arrow_line(L.screen_canvas, start, end, thickness, arrow_height, color); + draw_solid_arrow_line(L.viewport_canvas, start, end, thickness, arrow_height, color); } /* Draw aim */ @@ -762,7 +799,7 @@ INTERNAL void user_update(void) f32 arrow_height = 10; struct v2 pos = xform_mul_v2(L.world_view, ent->world_xform.og); struct v2 aim_ray = xform_basis_mul_v2(L.world_view, ent->player_aim); - draw_solid_arrow_ray(L.screen_canvas, pos, aim_ray, thickness, arrow_height, color); + draw_solid_arrow_ray(L.viewport_canvas, pos, aim_ray, thickness, arrow_height, color); } arena_temp_end(temp); @@ -771,8 +808,8 @@ INTERNAL void user_update(void) /* Draw crosshair or show cursor */ if (!L.debug_camera) { - struct v2 crosshair_pos = L.screen_cursor; - u32 tint = RGBA_F(1, 1, 1, 1 ); + struct v2 crosshair_pos = L.viewport_cursor; + u32 tint = RGBA_F(1, 1, 1, 1); struct v2 size = V2(0, 0); struct texture *t = texture_load_async(STR("res/graphics/crosshair.ase")); @@ -780,11 +817,15 @@ INTERNAL void user_update(void) size = t->size; struct xform xf = XFORM_TRS(.t = crosshair_pos, .s = 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); + draw_texture_quad(L.viewport_canvas, DRAW_TEXTURE_PARAMS(.texture = t, .tint = tint), quad); } + struct rect cursor_clip = RECT_FROM_V2(L.viewport_screen_offset, L.viewport_size); + cursor_clip.pos = v2_add(cursor_clip.pos, v2_mul(size, 0.5f)); + cursor_clip.pos = v2_add(cursor_clip.pos, V2(1, 1)); + cursor_clip.size = v2_sub(cursor_clip.size, size); sys_window_cursor_hide(L.window); - sys_window_cursor_enable_clip(L.window, RECT(size.x / 2, size.y / 2, L.screen_size.x - size.x, L.screen_size.y - size.y)); + sys_window_cursor_enable_clip(L.window, cursor_clip); } else { sys_window_cursor_disable_clip(L.window); sys_window_cursor_show(L.window); @@ -854,31 +895,40 @@ INTERNAL void user_update(void) struct v2 pos = V2(10, 8); struct font *font = font_load(STR("res/fonts/fixedsys.ttf"), 12.0f); - draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("time: %F"), FMT_FLOAT((f64)L.time))); + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("time: %F"), FMT_FLOAT((f64)L.time))); pos.y += spacing; - draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("screen_size: (%F, %F)"), FMT_FLOAT((f64)L.screen_size.x), FMT_FLOAT((f64)L.screen_size.y))); + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("screen_size: (%F, %F)"), FMT_FLOAT((f64)L.screen_size.x), FMT_FLOAT((f64)L.screen_size.y))); pos.y += spacing; - draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("screen_center: (%F, %F)"), FMT_FLOAT((f64)L.screen_center.x), FMT_FLOAT((f64)L.screen_center.y))); + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("screen_cursor: (%F, %F)"), FMT_FLOAT((f64)L.screen_cursor.x), FMT_FLOAT((f64)L.screen_cursor.y))); pos.y += spacing; - draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("screen_cursor: (%F, %F)"), FMT_FLOAT((f64)L.screen_cursor.x), FMT_FLOAT((f64)L.screen_cursor.y))); + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_screen_offset: (%F, %F)"), FMT_FLOAT((f64)L.viewport_screen_offset.x), FMT_FLOAT((f64)L.viewport_screen_offset.y))); pos.y += spacing; - draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("world_view.og: (%F, %F)"), FMT_FLOAT((f64)L.world_view.og.x), FMT_FLOAT((f64)L.world_view.og.y))); + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_size: (%F, %F)"), FMT_FLOAT((f64)L.viewport_size.x), FMT_FLOAT((f64)L.viewport_size.y))); pos.y += spacing; - draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("world_view rotation: %F"), FMT_FLOAT((f64)xform_get_rotation(L.world_view)))); + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_center: (%F, %F)"), FMT_FLOAT((f64)L.viewport_center.x), FMT_FLOAT((f64)L.viewport_center.y))); pos.y += spacing; - draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("world_view scale: (%F, %F)"), FMT_FLOAT((f64)xform_get_scale(L.world_view).x), FMT_FLOAT((f64)xform_get_scale(L.world_view).x))); + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_cursor: (%F, %F)"), FMT_FLOAT((f64)L.viewport_cursor.x), FMT_FLOAT((f64)L.viewport_cursor.y))); pos.y += spacing; - draw_text(L.screen_canvas, font, pos, string_format(temp.arena, STR("world_cursor: (%F, %F)"), FMT_FLOAT((f64)L.world_cursor.x), FMT_FLOAT((f64)L.world_cursor.y))); + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view.og: (%F, %F)"), FMT_FLOAT((f64)L.world_view.og.x), FMT_FLOAT((f64)L.world_view.og.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")))); + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view rotation: %F"), FMT_FLOAT((f64)xform_get_rotation(L.world_view)))); + pos.y += spacing; + + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view scale: (%F, %F)"), FMT_FLOAT((f64)xform_get_scale(L.world_view).x), FMT_FLOAT((f64)xform_get_scale(L.world_view).x))); + pos.y += spacing; + + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("world_cursor: (%F, %F)"), FMT_FLOAT((f64)L.world_cursor.x), FMT_FLOAT((f64)L.world_cursor.y))); + pos.y += spacing; + + draw_text(L.viewport_canvas, font, pos, string_format(temp.arena, STR("debug_camera: %F"), FMT_STR(L.debug_camera ? STR("true") : STR("false")))); pos.y += spacing; arena_temp_end(temp); @@ -892,12 +942,14 @@ INTERNAL void user_update(void) * ========================== */ /* Send canvases to GPU */ + renderer_canvas_send_to_gpu(L.viewport_bg_canvas); renderer_canvas_send_to_gpu(L.world_canvas); - renderer_canvas_send_to_gpu(L.screen_canvas); + renderer_canvas_send_to_gpu(L.viewport_canvas); /* Set canvas views before presenting */ + renderer_canvas_set_view(L.viewport_bg_canvas, XFORM_IDENT); renderer_canvas_set_view(L.world_canvas, L.world_view); - renderer_canvas_set_view(L.screen_canvas, XFORM_IDENT); + renderer_canvas_set_view(L.viewport_canvas, XFORM_IDENT); /* Present */ i32 vsync = VSYNC_ENABLED; @@ -905,17 +957,24 @@ INTERNAL void user_update(void) struct renderer_canvas **canvases = arena_dry_push(scratch.arena, struct renderer_canvas *); u64 canvases_count = 0; { - /* Only render world if not on first frame */ + /* Viewport background canvas */ + *arena_push(scratch.arena, struct renderer_canvas *) = L.viewport_bg_canvas; + ++canvases_count; + + /* World canvas */ if (!tick_is_first_frame) { + /* Only render world if not on first frame */ *arena_push(scratch.arena, struct renderer_canvas *) = L.world_canvas; ++canvases_count; } - *arena_push(scratch.arena, struct renderer_canvas *) = L.screen_canvas; + /* Viewport canvas */ + *arena_push(scratch.arena, struct renderer_canvas *) = L.viewport_canvas; ++canvases_count; } - renderer_canvas_present(canvases, canvases_count, L.screen_size.x, L.screen_size.y, vsync); + + renderer_canvas_present(canvases, canvases_count, L.screen_size, RECT_FROM_V2(L.viewport_screen_offset, L.viewport_size), vsync); scratch_end(scratch); } @@ -951,7 +1010,8 @@ void user_startup(struct sys_window *window) L.world_canvas = renderer_canvas_alloc(); L.world_view = XFORM_TRS(.t = V2(0, 0), .r = 0, .s = V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT)); - L.screen_canvas = renderer_canvas_alloc(); + L.viewport_bg_canvas = renderer_canvas_alloc(); + L.viewport_canvas = renderer_canvas_alloc(); L.window = window; sys_window_register_event_callback(L.window, &window_event_callback);