add screen scalar

This commit is contained in:
Rokas Puzonas 2026-09-15 00:10:38 +03:00
parent 796ffa5b2d
commit 782ccae6e2
4 changed files with 114 additions and 39 deletions

View File

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

71
src/canvas_scaler.zig Normal file
View File

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

View File

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

View File

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