Automatically calculate draw_texture scale from image resolution by default (closes #65)
continuous-integration/drone/push Build is passing Details

This commit is contained in:
kayomn 2024-07-26 00:14:28 +01:00
parent a63eefd7d4
commit 13c58bf106
6 changed files with 105 additions and 83 deletions

109
build.zig
View File

@ -12,77 +12,61 @@ const Project = struct {
optimize: std.builtin.OptimizeMode, optimize: std.builtin.OptimizeMode,
imports: ImportList, imports: ImportList,
pub fn demos_step(self: *const Project, b: *std.Build) *std.Build.Step { pub fn find_demos(self: Project, step: *std.Build.Step) void {
const Demos = struct { const absolute_path = step.owner.path("demos/").getPath(step.owner);
step: std.Build.Step,
project: *const Project,
const Self = @This(); var dir = std.fs.openDirAbsolute(absolute_path, .{.iterate = true}) catch |open_error| {
std.log.warn("failed to open demos directory at {s} with error {s}, skipping...", .{
absolute_path,
@errorName(open_error),
});
fn make(step: *std.Build.Step, _: std.Progress.Node) anyerror!void { return;
const demos: *const Self = @fieldParentPtr("step", step); };
const absolute_path = step.owner.path("demos/").getPath(step.owner);
var dir = try std.fs.openDirAbsolute(absolute_path, .{.iterate = true});
defer { defer {
dir.close(); dir.close();
} }
var entries = try dir.walk(step.owner.allocator); var entries = dir.walk(step.owner.allocator) catch @panic("OOM");
defer { defer {
entries.deinit(); entries.deinit();
} }
while (try entries.next()) |entry| { while (entries.next() catch @panic("I/O failure")) |entry| {
if (entry.kind != .file or !std.mem.endsWith(u8, entry.path, ".zig")) { if (entry.kind != .file or !std.mem.endsWith(u8, entry.path, ".zig")) {
continue; continue;
}
const source_path = step.owner.pathJoin(&.{"demos", entry.basename});
var path_buffer = [_:0]u8{0} ** 255;
const demo = step.owner.addExecutable(.{
.name = try std.fmt.bufPrint(&path_buffer, "{s}.out", .{std.fs.path.stem(entry.basename)}),
.root_source_file = step.owner.path(source_path),
.target = demos.project.target,
.optimize = demos.project.optimize,
});
for (demos.project.imports.items) |import| {
demo.root_module.addImport(import.name, import.module);
}
step.owner.installArtifact(demo);
}
step.state = .success;
} }
};
const demos = b.allocator.create(Demos) catch { const source_path = step.owner.pathJoin(&.{"demos", entry.basename});
@panic("OOM"); var path_buffer = [_:0]u8{0} ** 255;
};
demos.* = .{ const demo = step.owner.addExecutable(.{
.step = std.Build.Step.init(.{ .name = std.fmt.bufPrint(&path_buffer, "{s}.out", .{std.fs.path.stem(entry.basename)}) catch {
.id = .custom, @panic("a demo file name is too long");
.name = "demos", },
.makeFn = Demos.make,
.owner = b,
}),
.project = self, .root_source_file = step.owner.path(source_path),
}; .target = self.target,
.optimize = self.optimize,
});
return &demos.step; for (self.imports.items) |import| {
demo.root_module.addImport(import.name, import.module);
}
step.dependOn(&step.owner.addInstallArtifact(demo, .{
.dest_dir = .{
.override = .{.custom = "../demos"},
},
}).step);
}
} }
pub fn find_tests(self: Project, b: *std.Build) !void { pub fn find_tests(self: Project, step: *std.Build.Step) void {
const tests = b.step("tests", "Build and run tests");
for (self.imports.items) |import| { for (self.imports.items) |import| {
tests.dependOn(&b.addRunArtifact(b.addTest(.{ step.dependOn(&step.owner.addRunArtifact(step.owner.addTest(.{
.root_source_file = import.module.root_source_file.?, .root_source_file = import.module.root_source_file.?,
.target = self.target, .target = self.target,
.optimize = self.optimize, .optimize = self.optimize,
@ -177,11 +161,7 @@ const Project = struct {
}; };
pub fn build(b: *std.Build) !void { pub fn build(b: *std.Build) !void {
const project = b.allocator.create(Project) catch { var project = Project{
@panic("OOM");
};
project.* = .{
.imports = ImportList.init(b.allocator), .imports = ImportList.init(b.allocator),
.target = b.standardTargetOptions(.{}), .target = b.standardTargetOptions(.{}),
.optimize = b.standardOptimizeOption(.{}), .optimize = b.standardOptimizeOption(.{}),
@ -274,7 +254,6 @@ pub fn build(b: *std.Build) !void {
gfx_module.link_libc = true; gfx_module.link_libc = true;
b.step("demos", "Build demos").dependOn(project.demos_step(b)); project.find_demos(b.step("demos", "Build demos"));
project.find_tests(b.step("tests", "Build and run tests"));
try project.find_tests(b);
} }

View File

@ -50,15 +50,14 @@ fn render(commands: gfx.Commands, effects: ona.Write(Effects), app: ona.Read(ona
const display_width: f32 = @floatFromInt(display.state.width); const display_width: f32 = @floatFromInt(display.state.width);
const display_height: f32 = @floatFromInt(display.state.height); const display_height: f32 = @floatFromInt(display.state.height);
const display_transform = gfx.Transform2D{ const display_transform = gfx.transform_2d(.{
.origin = .{display_width / 2, display_height / 2}, .translation = .{display_width / 2, display_height / 2},
.xbasis = .{display_width, 0}, });
.ybasis = .{0, display_height},
};
try commands.draw_texture(.{ try commands.draw_texture(.{
.texture = .default, .texture = .default,
.transform = display_transform, .transform = display_transform,
.resolution = .{display.state.width, display.state.height},
}); });
try commands.set_effect(.{ try commands.set_effect(.{
@ -72,7 +71,6 @@ fn render(commands: gfx.Commands, effects: ona.Write(Effects), app: ona.Read(ona
}); });
try commands.set_target(.{ try commands.set_target(.{
.texture = null,
.clear_color = null, .clear_color = null,
.clear_depth = null, .clear_depth = null,
.clear_stencil = null, .clear_stencil = null,

View File

@ -48,7 +48,7 @@ fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display:
const random = visuals.state.random.random(); const random = visuals.state.random.random();
const width: f32 = @floatFromInt(display.state.width); const width: f32 = @floatFromInt(display.state.width);
const height: f32 = @floatFromInt(display.state.height); const height: f32 = @floatFromInt(display.state.height);
const icon_scale = .{64, 64}; const icon_scale = .{8, 8};
for (events.messages()) |event| { for (events.messages()) |event| {
switch (event) { switch (event) {
@ -56,7 +56,7 @@ fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display:
try visuals.state.spawned_keyboards.push_grow(.{ try visuals.state.spawned_keyboards.push_grow(.{
.lifetime_seconds = 2.5 + (5 * random.float(f32)), .lifetime_seconds = 2.5 + (5 * random.float(f32)),
.transform = gfx.Transform2D.from_simplified(.{ .transform = gfx.transform_2d(.{
.translation = .{width * random.float(f32), height}, .translation = .{width * random.float(f32), height},
.rotation = std.math.pi * random.float(f32), .rotation = std.math.pi * random.float(f32),
.scale = icon_scale, .scale = icon_scale,
@ -68,7 +68,7 @@ fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display:
try visuals.state.spawned_mouses.push_grow(.{ try visuals.state.spawned_mouses.push_grow(.{
.lifetime_seconds = 2.5 + (5 * random.float(f32)), .lifetime_seconds = 2.5 + (5 * random.float(f32)),
.transform = gfx.Transform2D.from_simplified(.{ .transform = gfx.transform_2d(.{
.translation = visuals.state.mouse_position, .translation = visuals.state.mouse_position,
.rotation = std.math.pi * random.float(f32), .rotation = std.math.pi * random.float(f32),
.scale = icon_scale, .scale = icon_scale,

View File

@ -178,6 +178,7 @@ pub const Commands = struct {
pub const DrawTextureCommand = struct { pub const DrawTextureCommand = struct {
texture: Texture, texture: Texture,
transform: Transform2D, transform: Transform2D,
resolution: ?@Vector(2, u16) = null,
}; };
pub const SetEffectCommand = struct { pub const SetEffectCommand = struct {
@ -357,13 +358,34 @@ pub const Transform2D = extern struct {
pub const Vector = @Vector(2, f32); pub const Vector = @Vector(2, f32);
pub fn from_simplified(simplified: Simplified) Transform2D { pub fn scaled(self: Transform2D, scale: Vector) Transform2D {
const rotation_skew = simplified.rotation + simplified.skew; var transform = self;
const scale_x, const scale_y = scale;
transform.xbasis *= @splat(scale_x);
transform.ybasis *= @splat(scale_y);
return transform;
}
pub fn transformed(self: Transform2D, other: Transform2D) Transform2D {
const xbasis_x, const xbasis_y = other.xbasis;
const ybasis_x, const ybasis_y = other.ybasis;
const origin_x, const origin_y = other.origin;
return .{ return .{
.xbasis = simplified.scale * Vector{std.math.cos(simplified.rotation), std.math.sin(simplified.rotation)}, .xbasis =
.ybasis = simplified.scale * Vector{-std.math.sin(rotation_skew), std.math.cos(rotation_skew)}, (self.xbasis * @as(Vector, @splat(xbasis_x))) +
.origin = simplified.translation, (self.ybasis * @as(Vector, @splat(xbasis_y))),
.ybasis =
(self.xbasis * @as(Vector, @splat(ybasis_x))) +
(self.ybasis * @as(Vector, @splat(ybasis_y))),
.origin =
(self.xbasis * @as(Vector, @splat(origin_x))) +
(self.ybasis * @as(Vector, @splat(origin_y))) +
self.origin,
}; };
} }
@ -586,3 +608,21 @@ pub fn synchronize(exclusive: ona.Exclusive(&.{Assets, Display})) !void {
assets.frame_rendered.set(); assets.frame_rendered.set();
} }
} }
pub fn transform_2d(simplified: Transform2D.Simplified) Transform2D {
const rotation_skew = simplified.rotation + simplified.skew;
return .{
.xbasis = simplified.scale * Transform2D.Vector{
std.math.cos(simplified.rotation),
std.math.sin(simplified.rotation),
},
.ybasis = simplified.scale * Transform2D.Vector{
-std.math.sin(rotation_skew),
std.math.cos(rotation_skew),
},
.origin = simplified.translation,
};
}

View File

@ -96,8 +96,13 @@ const Frame = struct {
try self.texture_batch_buffers.push_grow(instance_buffer); try self.texture_batch_buffers.push_grow(instance_buffer);
} }
const texture = resources.get_texture(command.texture).?;
_ = sokol.gfx.appendBuffer(self.texture_batch_buffers.get().?.*, sokol.gfx.asRange(&DrawTexture{ _ = sokol.gfx.appendBuffer(self.texture_batch_buffers.get().?.*, sokol.gfx.asRange(&DrawTexture{
.transform = command.transform, .transform = command.transform.scaled(@floatFromInt(command.resolution orelse .{
texture.width,
texture.height,
})),
})); }));
self.drawn_count += 1; self.drawn_count += 1;

View File

@ -8,7 +8,7 @@ layout (location = 3) in vec2 instance_ybasis;
layout (location = 4) in vec2 instance_origin; layout (location = 4) in vec2 instance_origin;
layout (location = 5) in vec4 instance_tint; layout (location = 5) in vec4 instance_tint;
layout (location = 6) in float instance_depth; layout (location = 6) in float instance_depth;
layout (location = 7) in vec4 instance_rect; layout (location = 7) in vec4 instance_clip;
layout (location = 0) out vec4 color; layout (location = 0) out vec4 color;
layout (location = 1) out vec2 uv; layout (location = 1) out vec2 uv;
@ -20,10 +20,10 @@ layout (binding = 0) uniform View {
void main() { void main() {
const vec2 world_position = instance_origin + mesh_xy.x * instance_xbasis + mesh_xy.y * instance_ybasis; const vec2 world_position = instance_origin + mesh_xy.x * instance_xbasis + mesh_xy.y * instance_ybasis;
const vec2 projected_position = (projection_matrix * vec4(world_position, 0, 1)).xy; const vec2 projected_position = (projection_matrix * vec4(world_position, 0, 1)).xy;
const vec2 rect_size = instance_rect.zw - instance_rect.xy; const vec2 rect_size = instance_clip.zw - instance_clip.xy;
const vec4 screen_position = vec4(projected_position, instance_depth, 1.0); const vec4 screen_position = vec4(projected_position, instance_depth, 1.0);
gl_Position = screen_position; gl_Position = screen_position;
color = instance_tint; color = instance_tint;
uv = instance_rect.xy + (mesh_uv * rect_size); uv = instance_clip.xy + (mesh_uv * rect_size);
} }