parent
e5d1a3ffd8
commit
0c6477c852
14 changed files with 338 additions and 37 deletions
@ -1,3 +0,0 @@ |
|||||||
[submodule "flecs"] |
|
||||||
path = libs/flecs |
|
||||||
url = https://github.com/SanderMertens/flecs.git |
|
@ -0,0 +1,3 @@ |
|||||||
|
pub usingnamespace @cImport({ |
||||||
|
@cInclude("flecs.h"); |
||||||
|
}); |
@ -0,0 +1 @@ |
|||||||
|
const c = @import("./c.zig"); |
@ -0,0 +1,16 @@ |
|||||||
|
const c = @import("./c.zig"); |
||||||
|
|
||||||
|
const World = @import("./world.zig").World; |
||||||
|
|
||||||
|
pub fn Entity(comptime ctx: anytype) type { |
||||||
|
return struct { |
||||||
|
world: *World(ctx), |
||||||
|
raw: c.ecs_entity_t, |
||||||
|
|
||||||
|
const Self = @This(); |
||||||
|
|
||||||
|
pub fn fromRaw(world: *World(ctx), raw: c.ecs_entity_t) Self { |
||||||
|
return .{ .world = world, .raw = raw }; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
const c = @import("./c.zig"); |
||||||
|
|
||||||
|
const World = @import("./world.zig").World; |
||||||
|
|
||||||
|
pub fn Id(comptime ctx: anytype) type { |
||||||
|
return struct { |
||||||
|
world: *World(ctx), |
||||||
|
raw: c.ecs_id_t, |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
const c = @import("./c.zig"); |
||||||
|
|
||||||
|
const World = @import("./world.zig").World; |
||||||
|
|
||||||
|
pub fn Iter(comptime ctx: anytype) type { |
||||||
|
return struct { |
||||||
|
world: *World(ctx), |
||||||
|
raw: c.ecs_iter_t, |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
pub usingnamespace @import("./component.zig"); |
||||||
|
pub usingnamespace @import("./entity.zig"); |
||||||
|
pub usingnamespace @import("./id.zig"); |
||||||
|
pub usingnamespace @import("./iter.zig"); |
||||||
|
pub usingnamespace @import("./system.zig"); |
||||||
|
pub usingnamespace @import("./world.zig"); |
||||||
|
|
||||||
|
pub const c = @import("./c.zig"); |
||||||
|
|
||||||
|
pub fn Context(comptime ctx: anytype) type { |
||||||
|
return struct { |
||||||
|
pub const Entity = @import("./entity.zig").Entity(ctx); |
||||||
|
pub const Id = @import("./id.zig").Id(ctx); |
||||||
|
pub const Iter = @import("./iter.zig").Iter(ctx); |
||||||
|
pub const World = @import("./world.zig").World(ctx); |
||||||
|
|
||||||
|
pub fn Lookup(comptime T: type) type { |
||||||
|
_ = T; // Only necessary to create a unique type. |
||||||
|
return struct { |
||||||
|
pub var id: c.ecs_id_t = 0; |
||||||
|
}; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
test { |
||||||
|
const std = @import("std"); |
||||||
|
std.testing.refAllDecls(@This()); |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
const std = @import("std"); |
||||||
|
|
||||||
|
/// Gets the simplified type name of the specified type. |
||||||
|
/// That is, without any namespace qualifiers. |
||||||
|
pub fn simpleTypeName(comptime T: type) [:0]const u8 { |
||||||
|
const fullName = @typeName(T); |
||||||
|
const index = std.mem.lastIndexOf(u8, fullName, "."); |
||||||
|
return if (index) |i| fullName[(i + 1)..] else fullName; |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
const std = @import("std"); |
||||||
|
const Allocator = std.mem.Allocator; |
||||||
|
|
||||||
|
const c = @import("./c.zig"); |
||||||
|
const util = @import("./util.zig"); |
||||||
|
|
||||||
|
pub fn World(comptime ctx: anytype) type { |
||||||
|
const Lookup = @import("./main.zig").Context(ctx).Lookup; |
||||||
|
const Entity = @import("./entity.zig").Entity(ctx); |
||||||
|
|
||||||
|
return struct { |
||||||
|
raw: *c.ecs_world_t, |
||||||
|
allocator: Allocator, |
||||||
|
|
||||||
|
const Self = @This(); |
||||||
|
|
||||||
|
pub fn init(alloc: Allocator) !*Self { |
||||||
|
var result = try alloc.create(Self); |
||||||
|
result.raw = c.ecs_init().?; |
||||||
|
result.allocator = alloc; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn initWithArgs(alloc: Allocator, args: [][*:0]u8) !*Self { |
||||||
|
var result = try alloc.create(Self); |
||||||
|
result.raw = c.ecs_init_w_args(args.len, args.ptr).?; |
||||||
|
result.allocator = alloc; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void { |
||||||
|
_ = c.ecs_fini(self.raw); |
||||||
|
self.allocator.destroy(self); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn progress(self: *Self, delta_time: f32) bool { |
||||||
|
return c.ecs_progress(self.raw, delta_time); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn entity( |
||||||
|
self: *Self, |
||||||
|
comptime add: []const type, |
||||||
|
) Entity { |
||||||
|
var desc = std.mem.zeroes(c.ecs_entity_desc_t); |
||||||
|
if (add.len > c.FLECS_ID_DESC_MAX) |
||||||
|
@compileLog("add.len > FLECS_ID_DESC_MAX"); |
||||||
|
inline for (add, 0..) |T, i| |
||||||
|
desc.add[i] = Lookup(T).id; |
||||||
|
const result = c.ecs_entity_init(self.*.raw, &desc); |
||||||
|
return Entity.fromRaw(self, result); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn component( |
||||||
|
self: *Self, |
||||||
|
comptime T: type, |
||||||
|
) Entity { |
||||||
|
const name = util.simpleTypeName(T); |
||||||
|
const entDesc = std.mem.zeroInit(c.ecs_entity_desc_t, .{ |
||||||
|
.name = name, |
||||||
|
.symbol = name, |
||||||
|
.use_low_id = true, |
||||||
|
}); |
||||||
|
const compDesc = std.mem.zeroInit(c.ecs_component_desc_t, .{ |
||||||
|
.entity = c.ecs_entity_init(self.raw, &entDesc), |
||||||
|
.type = .{ .size = @sizeOf(T), .alignment = @alignOf(T) }, |
||||||
|
}); |
||||||
|
Lookup(T).id = c.ecs_component_init(self.raw, &compDesc); |
||||||
|
return Entity.fromRaw(self, Lookup(T).id); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn system( |
||||||
|
self: *Self, |
||||||
|
name: [:0]const u8, |
||||||
|
callback: c.ecs_iter_action_t, |
||||||
|
phase: ?Entity, |
||||||
|
expr: [:0]const u8, |
||||||
|
) Entity { |
||||||
|
var entDesc = std.mem.zeroes(c.ecs_entity_desc_t); |
||||||
|
entDesc.name = name; |
||||||
|
if (phase) |p| { |
||||||
|
entDesc.add[0] = c.ecs_pair(c.EcsDependsOn, p.raw); |
||||||
|
entDesc.add[1] = p.raw; |
||||||
|
} |
||||||
|
var sysDesc = std.mem.zeroes(c.ecs_system_desc_t); |
||||||
|
sysDesc.entity = c.ecs_entity_init(self.raw, &entDesc); |
||||||
|
sysDesc.query.filter.expr = expr; |
||||||
|
sysDesc.callback = callback; |
||||||
|
const result = c.ecs_system_init(self.raw, *sysDesc); |
||||||
|
return Entity.fromRaw(self, result); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
test { |
||||||
|
_ = @import("../src/main.zig"); |
||||||
|
_ = @import("./world.zig"); |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
const std = @import("std"); |
||||||
|
const expect = std.testing.expect; |
||||||
|
const expectEql = std.testing.expectEqual; |
||||||
|
const expectStrEql = std.testing.expectEqualStrings; |
||||||
|
|
||||||
|
const flecs = @import("../src/main.zig"); |
||||||
|
const c = flecs.c; |
||||||
|
|
||||||
|
const context = flecs.Context(void); |
||||||
|
const Entity = context.Entity; |
||||||
|
const Iter = context.Iter; |
||||||
|
const Id = context.Id; |
||||||
|
|
||||||
|
pub const MAX_SYS_COLUMNS = 20; |
||||||
|
pub const MAX_ENTITIES = 256; |
||||||
|
pub const MAX_INVOCATIONS = 1024; |
||||||
|
|
||||||
|
pub const Probe = struct { |
||||||
|
system: c.ecs_entity_t, |
||||||
|
event: c.ecs_entity_t, |
||||||
|
eventId: c.ecs_id_t, |
||||||
|
offset: i32, |
||||||
|
count: i32, |
||||||
|
invoked: i32, |
||||||
|
termCount: i32, |
||||||
|
termIndex: i32, |
||||||
|
e: [MAX_ENTITIES]c.ecs_entity_t, |
||||||
|
c: [MAX_SYS_COLUMNS][MAX_INVOCATIONS]c.ecs_entity_t, |
||||||
|
s: [MAX_SYS_COLUMNS][MAX_INVOCATIONS]c.ecs_entity_t, |
||||||
|
param: ?*anyopaque, |
||||||
|
|
||||||
|
pub fn init() Probe { |
||||||
|
return std.mem.zeroes(Probe); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn probeSystemWithContext(ctx: *Probe, it: *Iter) !void { |
||||||
|
ctx.*.param = it.*.param; |
||||||
|
ctx.*.system = it.*.system; |
||||||
|
ctx.*.event = it.*.event; |
||||||
|
ctx.*.eventId = it.*.eventId; |
||||||
|
ctx.*.offset = 0; |
||||||
|
ctx.*.termCount = it.*.fieldCount; |
||||||
|
ctx.*.termIndex = it.*.termIndex; |
||||||
|
|
||||||
|
for (0..ctx.*.term_count) |i| { |
||||||
|
ctx.*.c[ctx.*.invoked][i] = it.*.ids[i]; |
||||||
|
ctx.*.s[ctx.*.invoked][i] = it.fieldSource(i + 1); |
||||||
|
|
||||||
|
const e = it.fieldId(i + 1); |
||||||
|
try expect(e != 0); |
||||||
|
} |
||||||
|
|
||||||
|
for (0..it.*.count) |i| { |
||||||
|
if (i + ctx.*.count < 256) { |
||||||
|
ctx.*.e[i + ctx.*.count] = it.*.entities[i]; |
||||||
|
} else { |
||||||
|
// Can't store more than that, tests shouldn't |
||||||
|
// rely on getting back more than 256 results. |
||||||
|
unreachable; |
||||||
|
} |
||||||
|
} |
||||||
|
ctx.*.count += it.*.count; |
||||||
|
|
||||||
|
ctx.*.invoked += 1; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn probeIter(it: *Iter) !void { |
||||||
|
var ctx = c.ecs_get_context(it.*.world); |
||||||
|
if (ctx == null) ctx = it.*.ctx; |
||||||
|
if (ctx) |ct| { |
||||||
|
var p: *Probe = @ptrCast(ct); |
||||||
|
p.probeSystemWithContext(it); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,71 @@ |
|||||||
|
// Reimplementations of the following tests from Flecs: |
||||||
|
// https://github.com/SanderMertens/flecs/blob/master/test/api/src/World.c |
||||||
|
|
||||||
|
const std = @import("std"); |
||||||
|
const expect = std.testing.expect; |
||||||
|
const expectEql = std.testing.expectEqual; |
||||||
|
const expectStrEql = std.testing.expectEqualStrings; |
||||||
|
|
||||||
|
const util = @import("./util.zig"); |
||||||
|
|
||||||
|
const flecs = @import("../src/main.zig"); |
||||||
|
const c = flecs.c; |
||||||
|
|
||||||
|
const context = flecs.Context(void); |
||||||
|
const Lookup = context.Lookup; |
||||||
|
const World = context.World; |
||||||
|
const Iter = context.Iter; |
||||||
|
const Entity = context.Entity; |
||||||
|
|
||||||
|
const Position = struct { x: f32, y: f32 }; |
||||||
|
const Velocity = struct { x: f32, y: f32 }; |
||||||
|
|
||||||
|
fn move(it: *Iter) void { |
||||||
|
var pos = it.field(Position, 1); |
||||||
|
var vel = it.field(Velocity, 2); |
||||||
|
util.Probe.probeIter(it); |
||||||
|
|
||||||
|
for (0..it.count) |row| { |
||||||
|
var p = &pos[row]; |
||||||
|
var v = &vel[row]; |
||||||
|
p.*.x += v.*.x * it.delta_time; |
||||||
|
p.*.y += v.*.y * it.delta_time; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
test "World_progress_w_0" { |
||||||
|
var world = try World.init(std.testing.allocator); |
||||||
|
defer world.deinit(); |
||||||
|
|
||||||
|
_ = world.component(Position); |
||||||
|
_ = world.component(Velocity); |
||||||
|
|
||||||
|
// const e1 = world.entity(&.{ Position, Velocity }); |
||||||
|
|
||||||
|
// // const phase = Entity.fromRaw(world, c.EcsOnUpdate); |
||||||
|
// // const move_system = world.system("move", move, phase, "Position, Velocity"); |
||||||
|
|
||||||
|
// var ctx = util.Probe.init(); |
||||||
|
// c.ecs_set_context(world.raw, &ctx); |
||||||
|
|
||||||
|
// // e1.set(Position, .{ 0, 0 }); |
||||||
|
// // e1.set(Velocity, .{ 1, 2 }); |
||||||
|
|
||||||
|
// _ = world.progress(0); |
||||||
|
|
||||||
|
// try expectEql(ctx.count, 1); |
||||||
|
// try expectEql(ctx.invoked, 1); |
||||||
|
// // try expectEql(ctx.system, move_system); |
||||||
|
// try expectEql(ctx.termCount, 2); |
||||||
|
// try expectEql(ctx.param, null); |
||||||
|
|
||||||
|
// try expectEql(ctx.e[0], e1.raw); |
||||||
|
// try expectEql(ctx.c[0][0], Lookup(Position).id); |
||||||
|
// try expectEql(ctx.c[0][1], Lookup(Velocity).id); |
||||||
|
// try expectEql(ctx.s[0][0], 0); |
||||||
|
// try expectEql(ctx.s[0][1], 0); |
||||||
|
|
||||||
|
// // const p = try e1.get(Position); |
||||||
|
// // try expect(p.x != 0); |
||||||
|
// // try expect(p.y != 0); |
||||||
|
} |
Loading…
Reference in new issue