diff --git a/src/app.zig b/src/app.zig index d21427c..023e541 100644 --- a/src/app.zig +++ b/src/app.zig @@ -15,6 +15,9 @@ const Gfx = Platform.Gfx; const Audio = Platform.Audio; const ImGUI = Platform.ImGUI; const Rect = Platform.Rect; +const Color = Platform.Color; + +const CanvasScaler = @import("./canvas_scaler.zig"); const App = @This(); @@ -324,9 +327,16 @@ pub fn frame(self: *App, plt: Platform.Frame) !void { dir_y = 0; } - Gfx.setClearColor(.rgb(40, 40, 40)); + const clear_color = Color.rgb(40, 40, 40); + Gfx.setClearColor(clear_color); const tile_size = Vec2.init(48, 48); + var canvas = CanvasScaler{ + .size = Vec2.initFromInt(usize, self.tilemap.width, self.tilemap.height).multiply(tile_size) + }; + canvas.push(); + defer canvas.pop(clear_color); + for (0..self.tilemap.height) |y| { for (0..self.tilemap.width) |x| { const tile = self.tilemap.get(x, y); diff --git a/src/canvas_scaler.zig b/src/canvas_scaler.zig new file mode 100644 index 0000000..4af4568 --- /dev/null +++ b/src/canvas_scaler.zig @@ -0,0 +1,71 @@ +const Math = @import("math"); +const Vec2 = Math.Vec2; + +const Platform = @import("platform/root.zig"); +const Gfx = Platform.Gfx; +const Color = Platform.Color; + +const Canvas = @This(); + +size: Vec2, + +fn getScale(self: Canvas) f32 { + const window_size = Gfx.getWindowSize(); + const canvas_size = self.size; + + const scale_x = window_size.x / canvas_size.x; + const scale_y = window_size.y / canvas_size.y; + return @min(scale_x, scale_y); +} + +fn getScaledCanvas(self: Canvas) Vec2 { + return self.size.multiplyScalar(self.getScale()); +} + +pub fn push(self: Canvas) void { + Gfx.transformPush(); + + const scale = self.getScale(); + const window_size = Gfx.getWindowSize(); + const scaled_canvas = self.size.multiplyScalar(scale); + + Gfx.transformTranslate(.init( + (window_size.x - scaled_canvas.x) / 2, + (window_size.y - scaled_canvas.y) / 2, + )); + + Gfx.transformScale(.init(scale, scale)); +} + +pub fn pop(self: Canvas, color: Color) void { + Gfx.transformPop(); + + const scale = self.getScale(); + const window_size = Gfx.getWindowSize(); + const scaled_canvas = self.size.multiplyScalar(scale); + if (window_size.x > scaled_canvas.x) { + const margin_x = (window_size.x - scaled_canvas.x) / 2; + Gfx.drawRectangle( + .init(0, 0), + .init(margin_x, window_size.y), + color + ); + Gfx.drawRectangle( + .init(window_size.x-margin_x, 0), + .init(margin_x, window_size.y), + color + ); + } else { + const margin_y = (window_size.y - scaled_canvas.y) / 2; + Gfx.drawRectangle( + .init(0, 0), + .init(window_size.x, margin_y), + color + ); + Gfx.drawRectangle( + .init(0, window_size.y-margin_y), + .init(window_size.x, margin_y), + color + ); + } +} diff --git a/src/platform/graphics.zig b/src/platform/graphics.zig index 8bb68c8..70993e1 100644 --- a/src/platform/graphics.zig +++ b/src/platform/graphics.zig @@ -28,6 +28,7 @@ const Frame = struct { quads: std.ArrayList(VertexQuad), transforms: std.ArrayList(TransformFrame), + window_size: Vec2, clear_color: Color = .black, pass_started: bool = false }; @@ -176,7 +177,8 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void { .index = 0, .quads = .initBuffer(&self.quads_buffer), .transforms = .initBuffer(&self.transforms_buffer), - .clear_color = .black + .clear_color = .black, + .window_size = .init(0, 0) } }; @@ -248,24 +250,6 @@ pub fn deinit() void { sg.shutdown(); } -fn createProjectionMatrix(screen_size: Vec2) Mat4 { - return Mat4.initIdentity() - // Convert normalized [-1; 1] coordinates to [0; 1] - .multiply(Mat4.initTranslate(.{ .x = -1, .y = -1, .z = 0 })) - .multiply(Mat4.initScale(.{ .x = 2, .y = 2, .z = 0 })) - - // Make the top-left corner (0,0) - .multiply(Mat4.initTranslate(.{ .x = 0, .y = 1, .z = 0 })) - .multiply(Mat4.initScale(.{ .x = 1, .y = -1, .z = 0 })) - - // Scretch [0; 1] so that they map 1-to-1 on to screen coordinates. - .multiply(Mat4.initScale(.{ - .x = 1/screen_size.x, - .y = 1/screen_size.y, - .z = 0 - })); -} - fn toSokolColor(color: Color) sokol.gfx.Color { // TODO: Assert and do cast return sokol.gfx.Color{ @@ -331,7 +315,8 @@ pub fn beginFrame() void { self.frame = Frame{ .index = self.frame.index, .quads = self.frame.quads, - .transforms = self.frame.transforms + .transforms = self.frame.transforms, + .window_size = Vec2.init(sapp.widthf(), sapp.heightf()) }; self.frame.transforms.appendAssumeCapacity(TransformFrame{ @@ -412,10 +397,14 @@ pub fn showDebug() void { pub fn setClearColor(color: Color) void { const self = &g_state; - self.frame.clear_color = color; } +pub fn getWindowSize() Vec2 { + const self = &g_state; + return self.frame.window_size; +} + pub fn getFrameIndex() u32 { const self = &g_state; return self.frame.index; @@ -467,16 +456,29 @@ pub fn flush() void { self.bindings.vertex_buffer_offsets[0] = sg.appendBuffer(vertex_buffer, data); - // TODO: Rectify `window_size` vs `gfx.window_size` const window_size = Vec2.init(sapp.widthf(), sapp.heightf()); - var vs_params: shd.VsParams = .{ - // TODO: Maybe 'view' matrix is not needed. - // We probably should only have a combined 'mvp' matrix - .view = .initIdentity(), - .projection = createProjectionMatrix(window_size) - }; + var mvp = Mat4.initIdentity(); + { + // Convert normalized [-1; 1] coordinates to [0; 1] + mvp = mvp.multiply(Mat4.initTranslate(.{ .x = -1, .y = -1, .z = 0 })); + mvp = mvp.multiply(Mat4.initScale(.{ .x = 2, .y = 2, .z = 0 })); + // Make the top-left corner (0,0) + mvp = mvp.multiply(Mat4.initTranslate(.{ .x = 0, .y = 1, .z = 0 })); + mvp = mvp.multiply(Mat4.initScale(.{ .x = 1, .y = -1, .z = 0 })); + + // Scretch [0; 1] so that they map 1-to-1 on to screen coordinates. + mvp = mvp.multiply(Mat4.initScale(.{ + .x = 1/window_size.x, + .y = 1/window_size.y, + .z = 0 + })); + } + + var vs_params: shd.VsParams = .{ + .mvp = mvp + }; var fs_params: shd.FsParams = .{ .texture_mode = 0, }; @@ -750,13 +752,6 @@ pub fn drawTexture(id: Texture.Id, pos: Vec2, size: Vec2, color: Color) void { }); } -fn bindView(view: sg.View) void { - _ = view; // autofix - const self = &g_state; - _ = self; // autofix - -} - pub fn setDefaultSampler(sampler: Sampler) void { const self = &g_state; self.default_sampler = sampler; diff --git a/src/platform/shader.glsl b/src/platform/shader.glsl index 3dc847b..82a15c6 100644 --- a/src/platform/shader.glsl +++ b/src/platform/shader.glsl @@ -10,12 +10,11 @@ out vec4 color; out vec4 uv; layout(binding = 0) uniform vs_params { - mat4 view; - mat4 projection; + mat4 mvp; }; void main() { - gl_Position = projection * view * vec4(position, 0, 1); + gl_Position = mvp * vec4(position, 0, 1); color = color0; uv = vec4(texcoord0, 0.0, 1.0); }