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