Further ECS-ification, use systems

main
copygirl 1 month ago
parent fc0e5efc8c
commit b1895bbef8
  1. 73
      src/main.zig
  2. 56
      src/renderer.zig

@ -5,20 +5,23 @@ const core = @import("mach").core;
const Renderer = @import("./renderer.zig");
const flecszigble = @import("flecs-zig-ble");
const World = flecszigble.World(void);
const Context = flecszigble.Context(void);
const World = Context.World;
const Iter = Context.Iter;
const flecs = flecszigble.flecs;
const OnLoad = flecs.pipeline.OnLoad;
const OnUpdate = flecs.pipeline.OnUpdate;
const OnStore = flecs.pipeline.OnStore;
pub const App = @This();
gpa: GeneralPurposeAllocator,
allocator: std.mem.Allocator,
random: std.Random,
world: *World,
random: std.rand.Random,
renderer: *Renderer,
app_timer: core.Timer,
title_timer: core.Timer,
pub fn init(app: *App) !void {
try core.init(.{
// Request high performance = prefer dedicated GPU when available.
@ -35,6 +38,11 @@ pub fn init(app: *App) !void {
app.gpa = GeneralPurposeAllocator{};
app.allocator = app.gpa.allocator();
// Create a pseudo-random number generator, but initialize it with
// a constant seed so we always get the same result when launching.
var prng = std.Random.DefaultPrng.init(0);
app.random = prng.random();
// Initialize flecs-zig-ble and create a new Flecs world.
//
// Flecs is a library for using Entity Component System (ECS) design
@ -45,32 +53,43 @@ pub fn init(app: *App) !void {
// and modified. For example by using systems, you are able to get
// entities that match a set of components, and modify their values.
flecszigble.init(app.allocator);
app.world = try World.init();
const world = try World.init();
app.world = world;
// Create a pseudo-random number generator, but initialize it with
// a constant seed so we always get the same result when launching.
var prng = std.rand.DefaultPrng.init(0);
app.random = prng.random();
// Create a singleton component for accessing the `App` from ECS.
_ = try world.singleton("App", *App, app);
app.renderer = try Renderer.init(app);
// TODO: The way we register systems using flecs-zig-ble is still very WIP.
_ = try world.system("PollEvents", pollEvents, OnLoad, "App");
const s = try world.system("UpdateWindowTitle", updateWindowTitle, OnStore, "");
// Set the update interval of the `UpdateWindowTitle` system to 1 second.
_ = flecszigble.c.ecs_set_interval(world.raw, s.raw, 1.0);
app.app_timer = try core.Timer.start();
app.title_timer = try core.Timer.start();
app.renderer = try Renderer.init(app);
}
pub fn deinit(app: *App) void {
// Using `defer` here, so we can specify them
// in the order they were created in `init`.
defer core.deinit();
defer _ = app.gpa.deinit(); // TODO: Check for memory leaks?
defer _ = app.gpa.deinit();
defer app.world.deinit();
defer app.renderer.deinit();
}
/// Update function called by Mach Core, which we'll just use to update Flecs.
/// This will then process all the systems we've registered in our pipeline.
pub fn update(app: *App) !bool {
// Read events from the OS such as input.
var iter = core.pollEvents();
while (iter.next()) |event| {
return !app.world.progress(0.0);
}
/// Read events from the OS such as input.
pub fn pollEvents(it: Iter) void {
const app = it.field(*App, 1)[0];
var pollIter = core.pollEvents();
while (pollIter.next()) |event| {
switch (event) {
// Allow the renderer to act on the window being resized.
// This is required so we can resize necessary buffers.
@ -78,19 +97,17 @@ pub fn update(app: *App) !bool {
// Close the window when requested, such as when
// pressing the X button in the window title bar.
.close => return true,
.close => it.world.quit(),
else => {},
}
}
}
app.renderer.update();
// Update the window title to show FPS and input frequency.
if (app.title_timer.read() >= 1.0) {
app.title_timer.reset();
try core.printTitle("Triangle [ {d}fps ] [ Input {d}hz ]", .{ core.frameRate(), core.inputRate() });
}
return false;
/// Update the window title to show FPS and input frequency.
pub fn updateWindowTitle(_: Iter) void {
core.printTitle(
"Triangle [ {d}fps ] [ Input {d}hz ]",
.{ core.frameRate(), core.inputRate() },
) catch @panic("Title too long!");
}

@ -18,7 +18,12 @@ const VertexData = primitives.VertexData;
const PrimitiveData = primitives.PrimitiveData;
const flecszigble = @import("flecs-zig-ble");
const Entity = flecszigble.Entity(void);
const Context = flecszigble.Context(void);
const Entity = Context.Entity;
const Iter = Context.Iter;
const flecs = flecszigble.flecs;
const OnStore = flecs.pipeline.OnStore;
const Transform = struct { value: Mat };
const CameraPerspective = struct {
@ -46,6 +51,7 @@ const ObjectData = struct {
const Renderer = @This();
app: *App,
time: f32 = 0.0,
pipeline: *gpu.RenderPipeline,
view_proj_buffer: *gpu.Buffer,
@ -56,7 +62,6 @@ depth_texture_view: ?*gpu.TextureView = null,
primitive_data: []PrimitiveData,
object_data: []ObjectData,
camera_entity: Entity,
pub fn init(app: *App) !*Renderer {
// A string buffer used to format objects' labels.
@ -225,11 +230,22 @@ pub fn init(app: *App) !*Renderer {
};
}
_ = try app.world.component(Transform);
_ = try app.world.component(CameraPerspective);
// Register components necessary for the camera.
_ = try app.world.component("Transform", Transform);
_ = try app.world.component("CameraPerspective", CameraPerspective);
const camera_entity = try app.world.entity(
.{ .name = "Camera", .symbol = "Camera" },
.{ Transform, CameraPerspective },
);
camera_entity.set(CameraPerspective, .{
.field_of_view = 45.0,
.near_plane = 0.05,
.far_plane = 80.0,
});
const camera_entity = try app.world.entity(.{ .name = "Camera" }, .{ Transform, CameraPerspective });
camera_entity.set(CameraPerspective, .{ .field_of_view = 45.0, .near_plane = 0.05, .far_plane = 80.0 });
const render_expr = "App, [in] CameraPerspective(Camera), [out] Transform(Camera)";
_ = try app.world.system("Render", render, OnStore, render_expr);
const result = try app.allocator.create(Renderer);
result.* = .{
@ -239,7 +255,6 @@ pub fn init(app: *App) !*Renderer {
.camera_bind_group = camera_bind_group,
.primitive_data = primitive_data,
.object_data = object_data,
.camera_entity = camera_entity,
};
// Initialize the depth texture.
@ -250,8 +265,8 @@ pub fn init(app: *App) !*Renderer {
}
pub fn deinit(self: *Renderer) void {
// Using `defer` here, so we can specify them
// in the order they were created in `init`.
// Using `defer` here, so we can specify resources we
// want to free in the order they were created in `init`.
defer self.app.allocator.destroy(self);
defer self.pipeline.release();
@ -278,34 +293,39 @@ pub fn resize(self: *Renderer) void {
self.recreateDepthTexture();
}
pub fn update(self: *Renderer) void {
pub fn render(it: Iter) void {
const app = it.field(*App, 1)[0];
const camera_perspective = it.field(CameraPerspective, 2)[0];
const camera_transform = &it.field(Transform, 3)[0];
const self = app.renderer;
self.time += it.deltaTime();
// Set up a view matrix from the camera transform.
// This moves everything to be relative to the camera.
// TODO: Actually implement camera transform instead of hardcoding a look-at matrix.
// const view_matrix = zm.inverse(app.camera_transform);
const time = self.app.app_timer.read();
const camera_distance = 8.0;
const x = @cos(time * std.math.tau / 20) * camera_distance;
const z = @sin(time * std.math.tau / 20) * camera_distance;
const x = @cos(self.time * std.math.tau / 20) * camera_distance;
const z = @sin(self.time * std.math.tau / 20) * camera_distance;
const camera_pos = vec(x, 2.0, z, 1.0);
const view_matrix = zm.lookAtLh(camera_pos, vec(0, 0, 0, 1), vec(0, 1, 0, 1));
// Setting the transform here doesn't do anything because it's not used
// anywhere. In the future we would want to set the camera transform
// outside of the rendering step, and then get and use it here, instead.
self.camera_entity.set(Transform, .{ .value = view_matrix });
camera_transform.* = .{ .value = view_matrix };
// TODO: Not sure if this is the proper transform, or actually inverted.
// Set up a projection matrix using the size of the window.
// The perspective projection will make things further away appear smaller.
const width: f32 = @floatFromInt(core.descriptor.width);
const height: f32 = @floatFromInt(core.descriptor.height);
const perspective = self.camera_entity.get(CameraPerspective).?;
const proj_matrix = zm.perspectiveFovLh(
std.math.degreesToRadians(f32, perspective.field_of_view),
std.math.degreesToRadians(f32, camera_perspective.field_of_view),
width / height,
perspective.near_plane,
perspective.far_plane,
camera_perspective.near_plane,
camera_perspective.far_plane,
);
const view_proj_matrix = zm.mul(view_matrix, proj_matrix);

Loading…
Cancel
Save