module: event handlers are defined ahead of time

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2024-03-24 17:31:12 -07:00 committed by Stephen Gutekanst
parent 0fc3bf6545
commit 3bfafe102d
12 changed files with 1099 additions and 967 deletions

View file

@ -20,6 +20,11 @@ pub const components = struct {
pub const follower = void;
};
pub const events = .{
.{ .global = .init, .handler = init },
.{ .global = .tick, .handler = tick },
};
// Each module must have a globally unique name declared, it is impossible to use two modules with
// the same name in a program. To avoid name conflicts, we follow naming conventions:
//
@ -33,7 +38,8 @@ pub const components = struct {
pub const name = .game;
pub const Mod = mach.Mod(@This());
pub fn init(
// TODO(engine): remove need for returning an error here
fn init(
engine: *mach.Engine.Mod,
renderer: *Renderer.Mod,
game: *Mod,
@ -56,7 +62,8 @@ pub fn init(
};
}
pub fn tick(
// TODO(engine): remove need for returning an error here
fn tick(
engine: *mach.Engine.Mod,
renderer: *Renderer.Mod,
game: *Mod,

View file

@ -26,13 +26,19 @@ pub const components = struct {
pub const scale = f32;
};
pub const events = .{
.{ .global = .init, .handler = init },
.{ .global = .deinit, .handler = deinit },
.{ .global = .tick, .handler = tick },
};
// TODO: this shouldn't be a packed struct, it should be extern.
const UniformBufferObject = packed struct {
offset: Vec3.Vector,
scale: f32,
};
pub fn init(
fn init(
engine: *mach.Engine.Mod,
renderer: *Mod,
) !void {
@ -97,7 +103,7 @@ pub fn init(
shader_module.release();
}
pub fn deinit(
fn deinit(
renderer: *Mod,
) !void {
renderer.state.pipeline.release();
@ -106,7 +112,7 @@ pub fn deinit(
renderer.state.uniform_buffer.release();
}
pub fn tick(
fn tick(
engine: *mach.Engine.Mod,
renderer: *Mod,
) !void {

View file

@ -40,12 +40,17 @@ const d0 = 0.000001;
pub const name = .game;
pub const Mod = mach.Mod(@This());
pub const events = .{
.{ .global = .init, .handler = init },
.{ .global = .tick, .handler = tick },
};
pub const Pipeline = enum(u32) {
default,
text,
};
pub fn init(
fn init(
engine: *mach.Engine.Mod,
sprite_mod: *Sprite.Mod,
text_mod: *Text.Mod,
@ -91,7 +96,7 @@ pub fn init(
};
}
pub fn tick(
fn tick(
engine: *mach.Engine.Mod,
sprite_mod: *Sprite.Mod,
text_mod: *Text.Mod,

View file

@ -8,6 +8,12 @@ const assets = @import("assets");
pub const name = .game_text;
pub const Mod = mach.Mod(@This());
pub const events = .{
.{ .global = .deinit, .handler = deinit },
.{ .local = .init, .handler = init },
.{ .local = .prepare, .handler = prepare },
};
const RegionMap = std.AutoArrayHashMapUnmanaged(u21, mach.gfx.Atlas.Region);
texture_atlas: mach.gfx.Atlas,
@ -16,7 +22,7 @@ ft: ft.Library,
face: ft.Face,
regions: RegionMap = .{},
pub fn deinit(
fn deinit(
engine: *mach.Engine.Mod,
text_mod: *Mod,
) !void {
@ -27,97 +33,95 @@ pub fn deinit(
text_mod.state.regions.deinit(engine.allocator);
}
pub const local = struct {
pub fn init(
engine: *mach.Engine.Mod,
text_mod: *Mod,
) !void {
const device = engine.state.device;
fn init(
engine: *mach.Engine.Mod,
text_mod: *Mod,
) !void {
const device = engine.state.device;
// rgba32_pixels
const img_size = gpu.Extent3D{ .width = 1024, .height = 1024 };
// rgba32_pixels
const img_size = gpu.Extent3D{ .width = 1024, .height = 1024 };
// Create a GPU texture
const texture = device.createTexture(&.{
.size = img_size,
.format = .rgba8_unorm,
.usage = .{
.texture_binding = true,
.copy_dst = true,
.render_attachment = true,
},
});
// Create a GPU texture
const texture = device.createTexture(&.{
.size = img_size,
.format = .rgba8_unorm,
.usage = .{
.texture_binding = true,
.copy_dst = true,
.render_attachment = true,
},
});
var s = &text_mod.state;
s.texture = texture;
s.texture_atlas = try mach.gfx.Atlas.init(
engine.allocator,
img_size.width,
.rgba,
);
var s = &text_mod.state;
s.texture = texture;
s.texture_atlas = try mach.gfx.Atlas.init(
engine.allocator,
img_size.width,
.rgba,
);
// TODO: state fields' default values do not work
s.regions = .{};
// TODO: state fields' default values do not work
s.regions = .{};
s.ft = try ft.Library.init();
s.face = try s.ft.createFaceMemory(assets.roboto_medium_ttf, 0);
s.ft = try ft.Library.init();
s.face = try s.ft.createFaceMemory(assets.roboto_medium_ttf, 0);
text_mod.send(.prepare, .{&[_]u21{ '?', '!', 'a', 'b', '#', '@', '%', '$', '&', '^', '*', '+', '=', '<', '>', '/', ':', ';', 'Q', '~' }});
}
text_mod.send(.prepare, .{&[_]u21{ '?', '!', 'a', 'b', '#', '@', '%', '$', '&', '^', '*', '+', '=', '<', '>', '/', ':', ';', 'Q', '~' }});
}
pub fn prepare(
engine: *mach.Engine.Mod,
text_mod: *Mod,
codepoints: []const u21,
) !void {
const device = engine.state.device;
const queue = device.getQueue();
var s = &text_mod.state;
fn prepare(
engine: *mach.Engine.Mod,
text_mod: *Mod,
codepoints: []const u21,
) !void {
const device = engine.state.device;
const queue = device.getQueue();
var s = &text_mod.state;
for (codepoints) |codepoint| {
const font_size = 48 * 1;
try s.face.setCharSize(font_size * 64, 0, 50, 0);
try s.face.loadChar(codepoint, .{ .render = true });
const glyph = s.face.glyph();
const metrics = glyph.metrics();
for (codepoints) |codepoint| {
const font_size = 48 * 1;
try s.face.setCharSize(font_size * 64, 0, 50, 0);
try s.face.loadChar(codepoint, .{ .render = true });
const glyph = s.face.glyph();
const metrics = glyph.metrics();
const glyph_bitmap = glyph.bitmap();
const glyph_width = glyph_bitmap.width();
const glyph_height = glyph_bitmap.rows();
const glyph_bitmap = glyph.bitmap();
const glyph_width = glyph_bitmap.width();
const glyph_height = glyph_bitmap.rows();
// Add 1 pixel padding to texture to avoid bleeding over other textures
const margin = 1;
const glyph_data = try engine.allocator.alloc([4]u8, (glyph_width + (margin * 2)) * (glyph_height + (margin * 2)));
defer engine.allocator.free(glyph_data);
const glyph_buffer = glyph_bitmap.buffer().?;
for (glyph_data, 0..) |*data, i| {
const x = i % (glyph_width + (margin * 2));
const y = i / (glyph_width + (margin * 2));
if (x < margin or x > (glyph_width + margin) or y < margin or y > (glyph_height + margin)) {
data.* = [4]u8{ 0, 0, 0, 0 };
} else {
const alpha = glyph_buffer[((y - margin) * glyph_width + (x - margin)) % glyph_buffer.len];
data.* = [4]u8{ 0, 0, 0, alpha };
}
// Add 1 pixel padding to texture to avoid bleeding over other textures
const margin = 1;
const glyph_data = try engine.allocator.alloc([4]u8, (glyph_width + (margin * 2)) * (glyph_height + (margin * 2)));
defer engine.allocator.free(glyph_data);
const glyph_buffer = glyph_bitmap.buffer().?;
for (glyph_data, 0..) |*data, i| {
const x = i % (glyph_width + (margin * 2));
const y = i / (glyph_width + (margin * 2));
if (x < margin or x > (glyph_width + margin) or y < margin or y > (glyph_height + margin)) {
data.* = [4]u8{ 0, 0, 0, 0 };
} else {
const alpha = glyph_buffer[((y - margin) * glyph_width + (x - margin)) % glyph_buffer.len];
data.* = [4]u8{ 0, 0, 0, alpha };
}
var glyph_atlas_region = try s.texture_atlas.reserve(engine.allocator, glyph_width + (margin * 2), glyph_height + (margin * 2));
s.texture_atlas.set(glyph_atlas_region, @as([*]const u8, @ptrCast(glyph_data.ptr))[0 .. glyph_data.len * 4]);
glyph_atlas_region.x += margin;
glyph_atlas_region.y += margin;
glyph_atlas_region.width -= margin * 2;
glyph_atlas_region.height -= margin * 2;
try s.regions.put(engine.allocator, codepoint, glyph_atlas_region);
_ = metrics;
}
var glyph_atlas_region = try s.texture_atlas.reserve(engine.allocator, glyph_width + (margin * 2), glyph_height + (margin * 2));
s.texture_atlas.set(glyph_atlas_region, @as([*]const u8, @ptrCast(glyph_data.ptr))[0 .. glyph_data.len * 4]);
// rgba32_pixels
const img_size = gpu.Extent3D{ .width = 1024, .height = 1024 };
const data_layout = gpu.Texture.DataLayout{
.bytes_per_row = @as(u32, @intCast(img_size.width * 4)),
.rows_per_image = @as(u32, @intCast(img_size.height)),
};
queue.writeTexture(&.{ .texture = s.texture }, &data_layout, &img_size, s.texture_atlas.data);
glyph_atlas_region.x += margin;
glyph_atlas_region.y += margin;
glyph_atlas_region.width -= margin * 2;
glyph_atlas_region.height -= margin * 2;
try s.regions.put(engine.allocator, codepoint, glyph_atlas_region);
_ = metrics;
}
};
// rgba32_pixels
const img_size = gpu.Extent3D{ .width = 1024, .height = 1024 };
const data_layout = gpu.Texture.DataLayout{
.bytes_per_row = @as(u32, @intCast(img_size.width * 4)),
.rows_per_image = @as(u32, @intCast(img_size.height)),
};
queue.writeTexture(&.{ .texture = s.texture }, &data_layout, &img_size, s.texture_atlas.data);
}

View file

@ -41,11 +41,16 @@ const d0 = 0.000001;
pub const name = .game;
pub const Mod = mach.Mod(@This());
pub const events = .{
.{ .global = .init, .handler = init },
.{ .global = .tick, .handler = tick },
};
pub const Pipeline = enum(u32) {
default,
};
pub fn init(
fn init(
engine: *mach.Engine.Mod,
sprite_mod: *Sprite.Mod,
game: *Mod,
@ -82,7 +87,7 @@ pub fn init(
};
}
pub fn tick(
fn tick(
engine: *mach.Engine.Mod,
sprite_mod: *Sprite.Mod,
game: *Mod,

View file

@ -44,6 +44,12 @@ const d0 = 0.000001;
pub const name = .game;
pub const Mod = mach.Mod(@This());
pub const events = .{
.{ .global = .init, .handler = init },
.{ .global = .deinit, .handler = deinit },
.{ .global = .tick, .handler = tick },
};
pub const Pipeline = enum(u32) {
default,
};
@ -58,7 +64,7 @@ const text1: []const []const u8 = &.{
const text2: []const []const u8 = &.{"!$?😊"};
pub fn init(
fn init(
engine: *mach.Engine.Mod,
text_mod: *Text.Mod,
game: *Mod,
@ -122,11 +128,11 @@ pub fn init(
};
}
pub fn deinit(engine: *mach.Engine.Mod) !void {
fn deinit(engine: *mach.Engine.Mod) !void {
_ = engine;
}
pub fn tick(
fn tick(
engine: *mach.Engine.Mod,
text_mod: *Text.Mod,
game: *Mod,