render to aspect-ratio-locked viewport

This commit is contained in:
jacob 2024-03-18 00:54:00 -05:00
parent 4a68e0c6c7
commit e3ffcaa2a1
6 changed files with 174 additions and 98 deletions

View File

@ -266,27 +266,27 @@ typedef i32 b32;
typedef u64 umm; typedef u64 umm;
#endif #endif
#define U8_MAX (0xFF) #define U8_MAX (0xFF)
#define U16_MAX (0xFFFF) #define U16_MAX (0xFFFF)
#define U32_MAX (0xFFFFFFFF) #define U32_MAX (0xFFFFFFFF)
#define U64_MAX (0xFFFFFFFFFFFFFFFFULL) #define U64_MAX (0xFFFFFFFFFFFFFFFFULL)
#define I8_MAX (0x7F) #define I8_MAX (0x7F)
#define I16_MAX (0x7FFF) #define I16_MAX (0x7FFF)
#define I32_MAX (0x7FFFFFFF) #define I32_MAX (0x7FFFFFFF)
#define I64_MAX (0x7FFFFFFFFFFFFFFFLL) #define I64_MAX (0x7FFFFFFFFFFFFFFFLL)
#define I8_MIN ((i8)-0x80) #define I8_MIN ((i8)-0x80)
#define I16_MIN ((i16)-0x8000) #define I16_MIN ((i16)-0x8000)
#define I32_MIN ((i32)-0x80000000) #define I32_MIN ((i32)-0x80000000)
#define I64_MIN ((i64)-0x8000000000000000LL) #define I64_MIN ((i64)-0x8000000000000000LL)
#define F32_INFINITY (1.0 / 0.0f) #define F32_INFINITY (1.0 / 0.0f)
#define F32_MAX (3.402823466e+38F) #define F32_MAX (3.402823466e+38F)
#define F32_MIN (1.175494351e-38F) #define F32_MIN (1.175494351e-38F)
#define PI ((f32)3.14159265358979323846) #define PI ((f32)3.14159265358979323846)
#define TAU ((f32)6.28318530717958647693) #define TAU ((f32)6.28318530717958647693)
/* ========================== * /* ========================== *
* Atomics * Atomics
@ -444,11 +444,17 @@ struct mat4x4 {
f32 e[4][4]; 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 { 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 */ /* Values expected to be normalized 0.0 -> 1.0 */
#define CLIP_ALL ((struct clip_rect) { { 0.0f, 0.0f }, { 1.0f, 1.0f } }) #define CLIP_ALL ((struct clip_rect) { { 0.0f, 0.0f }, { 1.0f, 1.0f } })
struct clip_rect { 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 (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) } #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 quad {
struct v2 p1, p2, p3, p4; struct v2 p1, p2, p3, p4;
}; };

View File

@ -5,7 +5,8 @@
* system. */ * system. */
#define RESOURCES_EMBEDDED !(DEVELOPER) #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 PIXELS_PER_UNIT 256
#define GAME_FPS 50 #define GAME_FPS 50

View File

@ -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_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 * Texture

View File

@ -124,8 +124,11 @@ GLOBAL struct {
ID3D11DeviceContext *devcon; ID3D11DeviceContext *devcon;
IDXGISwapChain1 *swapchain; IDXGISwapChain1 *swapchain;
ID3D11RenderTargetView *back_buffer_view; ID3D11RenderTargetView *backbuffer_view;
D3D11_VIEWPORT viewport; /* Here for caching/comparison */
/* Here for caching/comparison */
struct v2 backbuffer_size;
struct rect viewport;
ID3D11BlendState *blend_state; ID3D11BlendState *blend_state;
ID3D11RasterizerState *rasterizer_state; ID3D11RasterizerState *rasterizer_state;
@ -789,35 +792,37 @@ void renderer_canvas_send_to_gpu(struct renderer_canvas *canvas)
* Present canvas * Present canvas
* ========================== */ * ========================== */
INTERNAL void recreate_backbuffer_and_viewport(f32 width, f32 height) INTERNAL void resize_backbuffer(struct v2 size)
{ {
__prof; __prof;
/* TODO: error handling */ /* TODO: error handling */
/* Release all outstanding references to the swap chain's buffers. */ /* Release all outstanding references to the swap chain's buffers. */
if (L.back_buffer_view) { if (L.backbuffer_view) {
ID3D11RenderTargetView_Release(L.back_buffer_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. */ /* Get buffer and create a render-target-view. */
ID3D11Texture2D *back_buffer_texture = NULL; ID3D11Texture2D *backbuffer_texture = NULL;
IDXGISwapChain_GetBuffer(L.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&back_buffer_texture); IDXGISwapChain_GetBuffer(L.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&backbuffer_texture);
ID3D11Device_CreateRenderTargetView(L.dev, (ID3D11Resource *)back_buffer_texture, NULL, &L.back_buffer_view); ID3D11Device_CreateRenderTargetView(L.dev, (ID3D11Resource *)backbuffer_texture, NULL, &L.backbuffer_view);
ID3D11Texture2D_Release(back_buffer_texture); ID3D11Texture2D_Release(backbuffer_texture);
}
/* Set up the viewport */ INTERNAL void resize_viewport(struct rect viewport)
L.viewport = (D3D11_VIEWPORT){ {
.Width = width, D3D11_VIEWPORT d3d11_viewport = {
.Height = height, .Width = viewport.width,
.Height = viewport.height,
.MinDepth = 0.0f, .MinDepth = 0.0f,
.MaxDepth = 1.0f, .MaxDepth = 1.0f,
.TopLeftX = 0, .TopLeftX = viewport.x,
.TopLeftY = 0 .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). /* 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). * research if that is smart first).
* *
* I'm thinking we may also just need to lock texture modification access while presenting */ * 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; __prof;
/* Resize back buffer */ /* Resize back buffer */
D3D11_VIEWPORT old_vp = L.viewport; if (!v2_eq(L.backbuffer_size, screen_size)) {
if (old_vp.Width != viewport_width || old_vp.Height != viewport_height) { resize_backbuffer(screen_size);
recreate_backbuffer_and_viewport(viewport_width, viewport_height); 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 */ /* Clear back buffer */
f32 clear_color[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; f32 clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
ID3D11DeviceContext_ClearRenderTargetView(L.devcon, L.back_buffer_view, clear_color); ID3D11DeviceContext_ClearRenderTargetView(L.devcon, L.backbuffer_view, clear_color);
/* Set draw mode */ /* Set draw mode */
ID3D11DeviceContext_IASetPrimitiveTopology(L.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 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 /* Fill and set constant buffer
* NOTE: We're only doing this once per canvas, rather than once per draw call since * NOTE: We're only doing this once per canvas, rather than once per draw call since
* the only constant right now is VP. */ * 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); send_constant_buffer_data(L.vs_constant_buffer, vp_matrix);
ID3D11DeviceContext_VSSetConstantBuffers(L.devcon, 0, 1, &L.vs_constant_buffer); 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); IDXGISwapChain1_Present(L.swapchain, vsync, 0);
__profframe(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 u32 cap_index = 0;
static b32 ready_to_read = false; static b32 ready_to_read = false;
ID3D11Texture2D *back_buffer = NULL; ID3D11Texture2D *backbuffer = NULL;
IDXGISwapChain_GetBuffer(L.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *) & back_buffer); IDXGISwapChain_GetBuffer(L.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&backbuffer);
struct prof_cap *write_cap = &staging_caps[cap_index]; struct prof_cap *write_cap = &staging_caps[cap_index];
*write_cap = (struct prof_cap) { .size = V2(width, height) }; *write_cap = (struct prof_cap) { .size = V2(width, height) };
{ {
D3D11_TEXTURE2D_DESC staging_desc; D3D11_TEXTURE2D_DESC staging_desc;
ID3D11Texture2D_GetDesc(back_buffer, &staging_desc); ID3D11Texture2D_GetDesc(backbuffer, &staging_desc);
staging_desc.Usage = D3D11_USAGE_STAGING; staging_desc.Usage = D3D11_USAGE_STAGING;
staging_desc.BindFlags = 0; staging_desc.BindFlags = 0;
staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
ID3D11Device_CreateTexture2D(L.dev, &staging_desc, NULL, &write_cap->texture); ID3D11Device_CreateTexture2D(L.dev, &staging_desc, NULL, &write_cap->texture);
} }
ID3D11DeviceContext_CopyResource(L.devcon, (ID3D11Resource *)write_cap->texture, (ID3D11Resource *)back_buffer); ID3D11DeviceContext_CopyResource(L.devcon, (ID3D11Resource *)write_cap->texture, (ID3D11Resource *)backbuffer);
ID3D11Texture2D_Release(back_buffer); ID3D11Texture2D_Release(backbuffer);
++cap_index; ++cap_index;
if (cap_index >= ARRAY_COUNT(staging_caps)) { if (cap_index >= ARRAY_COUNT(staging_caps)) {

View File

@ -1626,7 +1626,7 @@ u32 sys_rand_u32(void)
void sys_panic_raw(char *msg_cstr) 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); MessageBoxExA(NULL, msg_cstr, "Fatal error", MB_ICONSTOP, 0);
ASSERT(false); ASSERT(false);
sys_exit(); sys_exit();
@ -1813,7 +1813,7 @@ int CALLBACK WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
wc->lpszClassName = "power_play_window_class"; wc->lpszClassName = "power_play_window_class";
wc->hCursor = LoadCursor(NULL, IDC_ARROW); wc->hCursor = LoadCursor(NULL, IDC_ARROW);
wc->style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 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->lpfnWndProc = win32_window_proc;
wc->hInstance = instance; wc->hInstance = instance;

View File

@ -36,7 +36,8 @@ GLOBAL struct {
struct sys_window *window; struct sys_window *window;
struct renderer_canvas *world_canvas; 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_last_frame;
struct xform world_view; struct xform world_view;
@ -60,8 +61,11 @@ GLOBAL struct {
f64 time; f64 time;
f64 dt; f64 dt;
struct v2 screen_size; struct v2 screen_size;
struct v2 screen_center;
struct v2 screen_cursor; 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; struct v2 world_cursor;
} L = { 0 }, DEBUG_LVAR(L_user); } 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)); struct quad quad = quad_from_rect(RECT(0, 0, 1, -1));
quad = quad_mul_xform(quad_scale(quad, 0.075), xf); 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.viewport_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_arrow_ray(L.viewport_canvas, pos, y_ray, thickness, arrowhead_len, color_y);
draw_solid_quad(L.screen_canvas, quad, color); draw_solid_quad(L.viewport_canvas, quad, color);
} }
/* TODO: remove this (testing) */ /* 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 vel_ray = xform_basis_mul_v2(L.world_view, ent->velocity);
struct v2 acc_ray = xform_basis_mul_v2(L.world_view, ent->acceleration); 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.viewport_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, acc_ray, thickness, arrow_len, color_acc);
} }
INTERNAL void user_update(void) 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.dt = max_f64(0.0, cur_time - L.time);
L.time += L.dt; L.time += L.dt;
/* Get screen dimensions */ /* Calculate screen & viewport dimensions */
L.screen_size = sys_window_get_size(L.window); {
L.screen_center = v2_mul(L.screen_size, 0.5); /* 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 * Produce interpolated tick
@ -469,6 +496,7 @@ INTERNAL void user_update(void)
/* Update mouse pos */ /* Update mouse pos */
if (event->kind == SYS_EVENT_KIND_CURSOR_MOVE) { if (event->kind == SYS_EVENT_KIND_CURSOR_MOVE) {
L.screen_cursor = event->cursor_position; L.screen_cursor = event->cursor_position;
L.viewport_cursor = v2_sub(L.screen_cursor, L.viewport_screen_offset);;
} }
/* Update bind states */ /* Update bind states */
@ -520,12 +548,12 @@ INTERNAL void user_update(void)
/* Pan view */ /* Pan view */
if (L.bind_states[USER_BIND_KIND_PAN].is_held) { if (L.bind_states[USER_BIND_KIND_PAN].is_held) {
if (!L.debug_camera_panning) { 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; 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.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 { } else {
L.debug_camera_panning = false; L.debug_camera_panning = false;
} }
@ -548,7 +576,7 @@ INTERNAL void user_update(void)
} }
/* Zoom to cursor */ /* 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_translate(L.world_view, world_cursor);
L.world_view = xform_scale(L.world_view, V2(zoom, zoom)); L.world_view = xform_scale(L.world_view, V2(zoom, zoom));
L.world_view = xform_translate(L.world_view, v2_neg(world_cursor)); 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; zoom = zoom > 0 ? zoom : 1;
struct trs trs = TRS( struct trs trs = TRS(
.t = v2_sub(L.screen_center, center), .t = v2_sub(L.viewport_center, center),
.r = rot, .r = rot,
.s = v2_mul(V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT), zoom) .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_translate(L.world_view, pivot);
L.world_view = xform_trs_pivot_rs(L.world_view, trs, 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 * Update listener
@ -578,17 +606,26 @@ INTERNAL void user_update(void)
{ {
struct v2 up = V2(0, -1); 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)); struct v2 listener_dir = v2_norm(xform_basis_invert_mul_v2(L.world_view, up));
mixer_set_listener(listener_pos, listener_dir); 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 * 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 color = RGBA(0x3f, 0x3f, 0x3f, 0xFF);
u32 x_color = RGBA(0x3f, 0, 0, 0xFF); u32 x_color = RGBA(0x3f, 0, 0, 0xFF);
u32 y_color = RGBA(0, 0x3f, 0, 0xFF); u32 y_color = RGBA(0, 0x3f, 0, 0xFF);
@ -599,25 +636,25 @@ INTERNAL void user_update(void)
i64 cols = 20; i64 cols = 20;
/* Draw column lines */ /* 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) { for (i64 col = starty; col <= (starty + cols); ++col) {
u32 line_color = color; u32 line_color = color;
if (col == 0) { if (col == 0) {
line_color = y_color; line_color = y_color;
} }
struct v2 pos = V2(col, starty); struct v2 pos = xform_mul_v2(L.world_view, V2(col, starty));
draw_solid_ray(L.world_canvas, pos, col_ray, thickness, line_color); 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) { for (i64 row = startx; row <= (startx + rows); ++row) {
u32 line_color = color; u32 line_color = color;
if (row == 0) { if (row == 0) {
line_color = x_color; line_color = x_color;
} }
struct v2 pos = V2(startx, row); struct v2 pos = xform_mul_v2(L.world_view, V2(startx, row));
draw_solid_ray(L.world_canvas, pos, row_ray, thickness, line_color); 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 #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 start = xform_mul_v2(L.world_view, ent->world_xform.og);
struct v2 end = xform_mul_v2(L.world_view, parent->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 */ /* Draw aim */
@ -762,7 +799,7 @@ INTERNAL void user_update(void)
f32 arrow_height = 10; f32 arrow_height = 10;
struct v2 pos = xform_mul_v2(L.world_view, ent->world_xform.og); 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); 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); arena_temp_end(temp);
@ -771,8 +808,8 @@ INTERNAL void user_update(void)
/* Draw crosshair or show cursor */ /* Draw crosshair or show cursor */
if (!L.debug_camera) { if (!L.debug_camera) {
struct v2 crosshair_pos = L.screen_cursor; struct v2 crosshair_pos = L.viewport_cursor;
u32 tint = RGBA_F(1, 1, 1, 1 ); u32 tint = RGBA_F(1, 1, 1, 1);
struct v2 size = V2(0, 0); struct v2 size = V2(0, 0);
struct texture *t = texture_load_async(STR("res/graphics/crosshair.ase")); struct texture *t = texture_load_async(STR("res/graphics/crosshair.ase"));
@ -780,11 +817,15 @@ INTERNAL void user_update(void)
size = t->size; size = t->size;
struct xform xf = XFORM_TRS(.t = crosshair_pos, .s = size); struct xform xf = XFORM_TRS(.t = crosshair_pos, .s = size);
struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf); 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_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 { } else {
sys_window_cursor_disable_clip(L.window); sys_window_cursor_disable_clip(L.window);
sys_window_cursor_show(L.window); sys_window_cursor_show(L.window);
@ -854,31 +895,40 @@ INTERNAL void user_update(void)
struct v2 pos = V2(10, 8); struct v2 pos = V2(10, 8);
struct font *font = font_load(STR("res/fonts/fixedsys.ttf"), 12.0f); 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; 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; 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; 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; 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; 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; 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; 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; 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; pos.y += spacing;
arena_temp_end(temp); arena_temp_end(temp);
@ -892,12 +942,14 @@ INTERNAL void user_update(void)
* ========================== */ * ========================== */
/* Send canvases to GPU */ /* 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.world_canvas);
renderer_canvas_send_to_gpu(L.screen_canvas); renderer_canvas_send_to_gpu(L.viewport_canvas);
/* Set canvas views before presenting */ /* 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.world_canvas, L.world_view);
renderer_canvas_set_view(L.screen_canvas, XFORM_IDENT); renderer_canvas_set_view(L.viewport_canvas, XFORM_IDENT);
/* Present */ /* Present */
i32 vsync = VSYNC_ENABLED; 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 *); struct renderer_canvas **canvases = arena_dry_push(scratch.arena, struct renderer_canvas *);
u64 canvases_count = 0; 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) { if (!tick_is_first_frame) {
/* Only render world if not on first frame */
*arena_push(scratch.arena, struct renderer_canvas *) = L.world_canvas; *arena_push(scratch.arena, struct renderer_canvas *) = L.world_canvas;
++canvases_count; ++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; ++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); scratch_end(scratch);
} }
@ -951,7 +1010,8 @@ void user_startup(struct sys_window *window)
L.world_canvas = renderer_canvas_alloc(); 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.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; L.window = window;
sys_window_register_event_callback(L.window, &window_event_callback); sys_window_register_event_callback(L.window, &window_event_callback);