preliminary work on ecs
This commit is contained in:
parent
d5d2f1b8d2
commit
7ce5902ec9
5 changed files with 147 additions and 102 deletions
BIN
assets/core.wasm
Normal file
BIN
assets/core.wasm
Normal file
Binary file not shown.
|
|
@ -1,111 +1,18 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub const Position = struct {
|
const COMPONENT_NUMBER = 2;
|
||||||
|
|
||||||
|
pub const Position = packed struct {
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
z: f32,
|
z: f32,
|
||||||
|
|
||||||
|
pub const id: usize = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Speed = struct {
|
pub const Speed = packed struct {
|
||||||
speed: f32,
|
speed: f32,
|
||||||
|
|
||||||
|
pub const id: usize = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.entities -= 1;
|
|
||||||
self.free_ids.append(id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
|
|
|
||||||
96
src/ecs/entities.zig
Normal file
96
src/ecs/entities.zig
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
36
src/ecs/sparse.zig
Normal file
36
src/ecs/sparse.zig
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ const math = @import("math.zig");
|
||||||
const Parser = @import("mods/parse.zig");
|
const Parser = @import("mods/parse.zig");
|
||||||
const vm = @import("mods/vm.zig");
|
const vm = @import("mods/vm.zig");
|
||||||
const wasm = @import("mods/wasm.zig");
|
const wasm = @import("mods/wasm.zig");
|
||||||
|
const components = @import("ecs/components.zig");
|
||||||
|
const entities = @import("ecs/entities.zig");
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
|
@ -23,10 +25,14 @@ pub fn main() !void {
|
||||||
//defer runtime.deinit(allocator);
|
//defer runtime.deinit(allocator);
|
||||||
|
|
||||||
//var parameters = [_]usize{};
|
//var parameters = [_]usize{};
|
||||||
//try runtime.callExternal(allocator, "fibonacci", ¶meters);
|
//try runtime.callExternal(allocator, "calculate_fibonacci", ¶meters);
|
||||||
const w = try window.Window.create(800, 600, "sideros");
|
const w = try window.Window.create(800, 600, "sideros");
|
||||||
defer w.destroy();
|
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
|
// TODO(luccie-cmd): Renderer.create shouldn't return an error
|
||||||
var r = try Renderer.create(allocator, w);
|
var r = try Renderer.create(allocator, w);
|
||||||
defer r.destroy();
|
defer r.destroy();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue