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