summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorenzo Torres <torres@sideros.org>2025-03-20 23:40:20 +0100
committerLorenzo Torres <torres@sideros.org>2025-03-20 23:40:20 +0100
commit7ce5902ec94376837a1a287878bcc2caa678dcbc (patch)
tree3ef7c1cb73cfd609f6326e99c9a75227b31f24d8
parentd5d2f1b8d237ed47b3f65aa4f28290c734d41dae (diff)
preliminary work on ecs
-rw-r--r--assets/core.wasmbin0 -> 2912 bytes
-rw-r--r--src/ecs/components.zig107
-rw-r--r--src/ecs/entities.zig96
-rw-r--r--src/ecs/sparse.zig36
-rw-r--r--src/main.zig8
5 files changed, 146 insertions, 101 deletions
diff --git a/assets/core.wasm b/assets/core.wasm
new file mode 100644
index 0000000..fddfb91
--- /dev/null
+++ b/assets/core.wasm
Binary files differ
diff --git a/src/ecs/components.zig b/src/ecs/components.zig
index bb333c9..5c35cbf 100644
--- a/src/ecs/components.zig
+++ b/src/ecs/components.zig
@@ -1,111 +1,18 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
-pub const Position = struct {
+const COMPONENT_NUMBER = 2;
+
+pub const Position = packed struct {
x: f32,
y: f32,
z: f32,
-};
-pub const Speed = struct {
- speed: f32,
+ pub const id: usize = 0;
};
-pub const Pool = struct {
- comptime sets_map: std.StringHashMap([]const u8) = std.StringHashMap([]const u8).init(allocator),
- speed_set: SparseSet(Speed),
- allocator: Allocator,
- free_ids: std.ArrayList(usize),
- entities: usize,
-
- pub fn init(allocator: Allocator) !Pool {
- try sets_map.put(@typeName(Speed), "speed_set");
-
- return Pool{
- .speed_set = try SparseSet(Speed).init(allocator),
- .allocator = allocator,
- .free_ids = try std.ArrayList(usize).initCapacity(allocator, 100),
- .entities = 0,
- };
- }
-
- pub fn deinit(self: *Pool) void {
- self.sets_map.deinit();
- self.speed_set.deinit();
- }
-
- pub fn addComponent(self: *Pool, comptime T: type, id: usize, component: T) void {
- var set = @field(self, try self.sets_map.get(@typeName(T)));
-
- set.insert(id, component);
- }
-
- pub fn insert(self: *Pool) !usize {
- const id = self.free_ids.pop() orelse self.entities;
-
- self.entities += 1;
- return id;
- }
-
- pub fn remove(self: *Pool, id: usize) !usize {
- if (self.speed_set.hasComponent(id)) {
- self.speed_set.remove(id);
- }
+pub const Speed = packed struct {
+ speed: f32,
- self.entities -= 1;
- self.free_ids.append(id);
- }
+ pub const id: usize = 1;
};
-
-pub fn SparseSet(comptime T: type) type {
- return struct {
- sparse: std.ArrayList(usize),
- dense: std.ArrayList(usize),
- components: std.ArrayList(T),
-
- pub fn init(allocator: Allocator) !@This() {
- return @This(){
- .sparse = try std.ArrayList(usize).initCapacity(allocator, 10),
- .dense = try std.ArrayList(usize).initCapacity(allocator, 10),
- .components = try std.ArrayList(T).initCapacity(allocator, 10),
- };
- }
-
- pub fn deinit(self: *@This()) void {
- self.sparse.deinit();
- self.dense.deinit();
- self.components.deinit();
- }
-
- pub fn hasComponent(self: *@This(), id: usize) bool {
- return self.dense.items[self.sparse.items[id]] == id;
- }
-
- pub fn insert(self: *@This(), id: usize, component: T) !void {
- const dense_index = self.dense.items.len;
- try self.dense.append(id);
- try self.components.append(component);
- try self.sparse.append(dense_index);
- }
-
- pub fn remove(self: *@This(), id: usize) !void {
- const index = self.sparse.items[id];
- const last = self.dense.getLast();
- self.sparse.items[last] = index;
- _ = self.dense.swapRemove(index);
- _ = self.components.swapRemove(index);
- }
- };
-}
-
-pub fn test_sparse() !void {
- var gpa = std.heap.GeneralPurposeAllocator(.{}){};
- const allocator = gpa.allocator();
-
- var pool = try Pool.init(allocator);
- defer pool.deinit();
-
- const entity = try pool.insert();
- std.debug.print("new entity: {d}\n", .{entity});
- pool.addComponent(Speed, entity, .{ .speed = 5.0 });
-}
diff --git a/src/ecs/entities.zig b/src/ecs/entities.zig
new file mode 100644
index 0000000..6d091e2
--- /dev/null
+++ b/src/ecs/entities.zig
@@ -0,0 +1,96 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const components = @import("components.zig");
+const sparse = @import("sparse.zig");
+
+const System = *const fn (Pool) void;
+const SystemGroup = std.ArrayList(System);
+
+pub const Pool = struct {
+ // Components
+ position: sparse.SparseSet(components.Position),
+ speed: sparse.SparseSet(components.Speed),
+
+ system_groups: std.ArrayList(SystemGroup),
+ thread_pool: std.Thread.Pool,
+ wait_group: std.Thread.WaitGroup,
+ mutex: std.Thread.Mutex,
+ last_entity: usize,
+ free_ids: std.ArrayList(usize),
+
+ component_flags: std.AutoHashMap(usize, usize),
+
+ pub fn init(allocator: Allocator) !@This() {
+ var thread_pool: std.Thread.Pool = undefined;
+ try thread_pool.init(.{
+ .allocator = allocator,
+ .n_jobs = 4,
+ });
+
+ return @This(){
+ .position = sparse.SparseSet(components.Position).init(allocator),
+ .speed = sparse.SparseSet(components.Speed).init(allocator),
+
+ .system_groups = std.ArrayList(SystemGroup).init(allocator),
+ .thread_pool = thread_pool,
+ .wait_group = .{},
+ .mutex = .{},
+ .last_entity = 0,
+ .free_ids = std.ArrayList(usize).init(allocator),
+ .component_flags = std.AutoHashMap(usize, usize).init(allocator),
+ };
+ }
+
+ pub fn tick(self: *@This()) void {
+ for (self.system_groups) |group| {
+ self.thread_pool.spawnWg(&self.wait_group, struct {
+ fn run(pool: *Pool) void {
+ for (group) |system| {
+ system(pool);
+ }
+ }
+ }.run, .{self});
+ }
+ self.wait_group.wait();
+ }
+
+ pub fn createEntity(self: *@This()) !usize {
+ const id = self.free_ids.pop() orelse self.last_entity;
+ self.last_entity += 1;
+ try self.component_flags.put(2, 0x2);
+
+ return id;
+ }
+
+ pub fn destroyEntity(self: *@This(), entity: usize) void {
+ self.free_ids.append(entity);
+
+ const flags = self.component_flags.get(entity);
+ for (0..components.COMPONENT_NUMBER) |i| {
+ if (((flags >> i) & 0x1) != 0x0) {
+ self.removeComponent(entity, i);
+ }
+ }
+ }
+
+ pub fn addComponent(self: *@This(), entity: usize, component: anytype) !void {
+ var set = switch (@TypeOf(component)) {
+ components.Speed => self.speed,
+ components.Position => self.position,
+ else => unreachable,
+ };
+
+ try self.component_flags.put(entity, self.component_flags.get(entity).? | (0x1 << @TypeOf(component).id));
+ try set.addEntity(entity, component);
+ }
+
+ pub fn removeComponent(self: *@This(), entity: usize, component_id: usize) void {
+ const set = switch (component_id) {
+ components.Speed.id => self.speed,
+ components.Position.id => self.position,
+ };
+
+ self.component_flags.put(entity, self.component_flags.get(entity) & ~(0x1 << component_id));
+ set.removeEntity(entity);
+ }
+};
diff --git a/src/ecs/sparse.zig b/src/ecs/sparse.zig
new file mode 100644
index 0000000..28915a5
--- /dev/null
+++ b/src/ecs/sparse.zig
@@ -0,0 +1,36 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+
+pub fn SparseSet(comptime T: type) type {
+ return struct {
+ sparse: std.ArrayList(usize),
+ dense: std.ArrayList(usize),
+ components: std.ArrayList(T),
+
+ pub fn init(allocator: Allocator) @This() {
+ return @This(){
+ .sparse = std.ArrayList(usize).init(allocator),
+ .dense = std.ArrayList(usize).init(allocator),
+ .components = std.ArrayList(T).init(allocator),
+ };
+ }
+
+ pub fn addEntity(self: *@This(), entity: usize, component: T) !void {
+ if (entity >= self.sparse.items.len) {
+ try self.sparse.resize(entity + 10);
+ }
+
+ self.sparse.items[entity] = self.dense.items.len;
+ try self.dense.append(entity);
+ try self.components.append(component);
+ }
+
+ pub fn removeEntity(self: *@This(), entity: usize) void {
+ const last_index = self.dense.getLast();
+ const dense_index = self.sparse.items[entity];
+ self.dense.swapRemove(dense_index);
+ self.components.swapRemove(dense_index);
+ self.sparse.items[last_index] = dense_index;
+ }
+ };
+}
diff --git a/src/main.zig b/src/main.zig
index 636dbff..9c3e59d 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -8,6 +8,8 @@ const math = @import("math.zig");
const Parser = @import("mods/parse.zig");
const vm = @import("mods/vm.zig");
const wasm = @import("mods/wasm.zig");
+const components = @import("ecs/components.zig");
+const entities = @import("ecs/entities.zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@@ -23,10 +25,14 @@ pub fn main() !void {
//defer runtime.deinit(allocator);
//var parameters = [_]usize{};
- //try runtime.callExternal(allocator, "fibonacci", &parameters);
+ //try runtime.callExternal(allocator, "calculate_fibonacci", &parameters);
const w = try window.Window.create(800, 600, "sideros");
defer w.destroy();
+ var pool = try entities.Pool.init(allocator);
+ _ = try pool.createEntity();
+ //try pool.addComponent(entity, components.Speed{ .speed = 0.0 });
+
// TODO(luccie-cmd): Renderer.create shouldn't return an error
var r = try Renderer.create(allocator, w);
defer r.destroy();