diff --git a/src/ecs/Input.zig b/src/ecs/Input.zig index dc6f46f..665050f 100644 --- a/src/ecs/Input.zig +++ b/src/ecs/Input.zig @@ -3,6 +3,11 @@ const Allocator = std.mem.Allocator; const Input = @This(); +pub const ScrollDirection = enum { + up, + down +}; + pub const KeyCode = enum(u32) { space = 32, apostrophe = 39, diff --git a/src/ecs/ecs.zig b/src/ecs/ecs.zig index 432ffde..b0aaf8d 100644 --- a/src/ecs/ecs.zig +++ b/src/ecs/ecs.zig @@ -1,3 +1,5 @@ +const std = @import("std"); + pub const components = @import("components.zig"); pub const entities = @import("entities.zig"); pub const Input = @import("Input.zig"); @@ -11,3 +13,31 @@ pub const Pool = entities.Pool; pub const Resources = entities.Resources; pub const System = *const fn (*Pool) anyerror!void; pub const SystemGroup = []const System; + +pub const hooks = struct { + pub const Scroll = *const fn (*Pool, direction: Input.ScrollDirection) anyerror!void; + pub const Key = *const fn (*Pool, key: Input.KeyCode) anyerror!void; + + pub var key: std.ArrayList(Key) = undefined; + pub var scroll: std.ArrayList(Scroll) = undefined; + + pub const Layer = enum { + key, + scroll + }; + + pub fn init(allocator: std.mem.Allocator) !void { + key = std.ArrayList(Key).init(allocator); + scroll = std.ArrayList(Scroll).init(allocator); + } + + pub fn addHook(comptime layer: Layer, hook: anytype) !void { + var list = comptime switch (layer) { + .key => &key, + .scroll => &scroll, + }; + + try list.append(hook); + } +}; + diff --git a/src/ecs/entities.zig b/src/ecs/entities.zig index 3bf5599..da52611 100644 --- a/src/ecs/entities.zig +++ b/src/ecs/entities.zig @@ -8,7 +8,8 @@ const Camera = rendering.Camera; const ecs = @import("ecs.zig"); const Input = ecs.Input; -pub const System = ecs.System; + +pub const System = *const fn (*Pool) anyerror!void; pub const SystemGroup = []const System; pub const SyncGroup = []const System; diff --git a/src/ecs/hooks.zig b/src/ecs/hooks.zig new file mode 100644 index 0000000..f9037c3 --- /dev/null +++ b/src/ecs/hooks.zig @@ -0,0 +1,24 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +pub var key: std.ArrayList(ecs.System) = undefined; +pub var scroll: std.ArrayList(ecs.System) = undefined; + +pub const Layer = enum { + key, + scroll +}; + +pub fn init(allocator: Allocator) !void { + key = std.ArrayList(ecs.System).init(allocator); + scroll = std.ArrayList(ecs.System).init(allocator); +} + +pub fn addHook(layer: Layer, hook: ecs.System) !void { + var list = switch (layer) { + .key => &key, + .scroll => &scroll, + }; + + list.append(hook); +} diff --git a/src/math.zig b/src/math.zig index 6768c52..97f8265 100644 --- a/src/math.zig +++ b/src/math.zig @@ -3,6 +3,7 @@ pub const tan = std.math.tan; pub const cos = std.math.cos; pub const sin = std.math.sin; pub const rad = std.math.degreesToRadians; +pub const deg = std.math.radiansToDegrees; pub const sqrt = std.math.sqrt; pub const Axis = struct { @@ -190,6 +191,31 @@ pub const Quaternion = extern struct { }; } + //pub fn rotateVector(q: Quaternion, v: @Vector(3, f32)) @Vector(3, f32) { + // const quaternion = q.normalize(); + // const u = @Vector(3, f32){quaternion.x, quaternion.y, quaternion.z}; + // const s = quaternion.w; + // + // return scaleVector(u, 2.0 * dot(u, v)) + // + scaleVector(v, s*s - dot(u, u)) + // + scaleVector(cross(u, v), 2.0 * s); + //} + + pub fn rotateVector(self: Quaternion, vec: @Vector(3, f32)) @Vector(3, f32) { + const vec_quat: Quaternion = .{ .w = 0, .x = vec[0], .y = vec[1], .z = vec[2] }; + + const conj: Quaternion = .{ + .w = self.w, + .x = -self.x, + .y = -self.y, + .z = -self.z, + }; + + const rotated = self.mul(vec_quat).mul(conj); + + return @Vector(3, f32){ rotated.x, rotated.y, rotated.z }; + } + inline fn mul(a: Quaternion, b: Quaternion) Quaternion { return .{ @@ -210,7 +236,7 @@ pub const Quaternion = extern struct { }; } - fn matrix(q: Quaternion) Matrix { + pub fn matrix(q: Quaternion) Matrix { const x2 = q.x + q.x; const y2 = q.y + q.y; const z2 = q.z + q.z; @@ -247,3 +273,7 @@ pub fn cross(a: @Vector(3, f32), b: @Vector(3, f32)) @Vector(3, f32) { pub fn normalize(a: @Vector(3, f32)) @Vector(3, f32) { return a / @as(@Vector(3, f32), @splat(@sqrt(dot(a, a)))); } + +pub inline fn scaleVector(a: @Vector(3, f32), s: f32) @Vector(3, f32) { + return a * @as(@Vector(3, f32), @splat(s)); +} diff --git a/src/rendering/Camera.zig b/src/rendering/Camera.zig index 4e27850..29e1c1c 100644 --- a/src/rendering/Camera.zig +++ b/src/rendering/Camera.zig @@ -14,38 +14,44 @@ pub const Uniform = struct { position: @Vector(3, f32), target: @Vector(3, f32) = .{ 0.0, 0.0, -1.0 }, up: @Vector(3, f32) = .{ 0.0, 1.0, 0.0 }, -speed: f32 = 5, +speed: f32 = 10, pitch: f32 = -45, yaw: f32 = 0, -distance: f32 = 5.0, pub fn getProjection(width: usize, height: usize) math.Matrix { return math.Matrix.perspective(math.rad(45.0), (@as(f32, @floatFromInt(width)) / @as(f32, @floatFromInt(height))), 0.1, 100.0); } pub fn getView(self: *Camera) math.Matrix { + _ = self.getTarget(); return math.Matrix.lookAt(self.position, math.rad(self.yaw), math.rad(self.pitch)); } -pub fn getTarget(self: *Camera) @Vector(3, f32) { - const direction: @Vector(3, f32) = .{ +pub inline fn getDirection(self: *Camera) @Vector(3, f32) { + return .{ math.sin(math.rad(self.yaw)) * math.cos(math.rad(self.pitch)), math.sin(math.rad(self.pitch)), math.cos(math.rad(self.yaw)) * math.cos(math.rad(self.pitch)), }; +} - const t = (self.position[1] - (self.position[1] - self.distance)) / direction[1]; +pub fn getTarget(self: *Camera) @Vector(3, f32) { + const direction = self.getDirection(); + + const t = self.position[1] / direction[1]; const target: @Vector(3, f32) = .{ self.position[0] + (t*direction[0]), - (self.position[1] - self.distance), - self.position[2] + (t*direction[2]), + 0.0, + self.position[2] - (t*direction[2]), }; - //target[2] = 0.0; - - std.debug.print("{} {} {}\n", .{direction, t, target}); - return target; } +pub fn rotateAround(self: *Camera, pivot: @Vector(3, f32), angle: f32) void { + self.yaw -= math.deg(angle); + var rotation = math.Quaternion.fromAxisAngle(.{0.0, 1.0, 0.0}, angle); + self.position = rotation.rotateVector(self.position - pivot) + pivot; +} + diff --git a/src/sideros.zig b/src/sideros.zig index 6d76dcd..54d2660 100644 --- a/src/sideros.zig +++ b/src/sideros.zig @@ -138,6 +138,8 @@ export fn sideros_init(init: api.GameInit) callconv(.c) void { .input = &input, }; + ecs.hooks.init(allocator) catch @panic("TODO: handle this"); + ecs.hooks.addHook(.scroll, systems.zoomCamera) catch @panic("TODO handle this"); pool = ecs.Pool.init(allocator, &resources) catch @panic("TODO: Gracefully handle error"); // TODO(ernesto): I think this @ptrCast are unavoidable but maybe not? renderer = Renderer.init(allocator, @ptrCast(init.instance), @ptrCast(init.surface)) catch @panic("TODO: Gracefully handle error"); @@ -178,3 +180,9 @@ export fn sideros_key_callback(key: u32, release: bool) callconv(.c) void { } } } + +export fn sideros_scroll_callback(up: bool) callconv(.c) void { + for (ecs.hooks.scroll.items) |hook| { + hook(&pool, if (up) .up else .down) catch @panic("TODO: actually handle this"); + } +} diff --git a/src/sideros_api.h b/src/sideros_api.h index 93e90f9..ac74cb2 100644 --- a/src/sideros_api.h +++ b/src/sideros_api.h @@ -13,4 +13,5 @@ typedef struct { void sideros_init(GameInit init); void sideros_update(GameUpdate state); void sideros_key_callback(unsigned int key, bool release); +void sideros_scroll_callback(bool up); void sideros_cleanup(void); diff --git a/src/systems.zig b/src/systems.zig index fa9c42c..afbe7c6 100644 --- a/src/systems.zig +++ b/src/systems.zig @@ -1,6 +1,7 @@ const ecs = @import("ecs"); const math = @import("math"); const std = @import("std"); +const Input = ecs.Input; pub fn render(pool: *ecs.Pool) anyerror!void { var renderer = pool.resources.renderer; @@ -32,17 +33,38 @@ pub fn moveCamera(pool: *ecs.Pool) !void { const input = pool.resources.input; var camera = pool.resources.camera; const mul = @as(@Vector(3, f32), @splat(camera.speed * pool.resources.delta_time)); + var forward = camera.getDirection(); + forward[0] = -forward[0]; + forward[1] = 0.0; + const left = math.cross(forward, camera.up); if (input.isKeyDown(.w)) { - camera.position += @as(@Vector(3, f32), .{0.0, 0.0, 1.0}) * mul; + camera.position += forward * mul; } if (input.isKeyDown(.s)) { - camera.position += @as(@Vector(3, f32), .{0.0, 0.0, -1.0}) * mul; + camera.position -= forward * mul; } if (input.isKeyDown(.a)) { - camera.position -= @as(@Vector(3, f32), .{1.0, 0.0, 0.0}) * mul; + camera.position += left * mul; } if (input.isKeyDown(.d)) { - camera.position += @as(@Vector(3, f32), .{1.0, 0.0, 0.0}) * mul; + camera.position -= left * mul; + } + if (input.isKeyDown(.q)) { + camera.rotateAround(camera.getTarget(), math.rad(100.0 * pool.resources.delta_time)); + } + if (input.isKeyDown(.e)) { + camera.rotateAround(camera.getTarget(), math.rad(-100.0 * pool.resources.delta_time)); + } +} + +pub fn zoomCamera(pool: *ecs.Pool, direction: Input.ScrollDirection) !void { + var camera = pool.resources.camera; + var camera_direction = camera.getDirection(); + camera_direction[0] = -camera_direction[0]; + if (direction == .up) { + camera.position += camera_direction; + } else { + camera.position -= camera_direction; } } diff --git a/src/xorg.zig b/src/xorg.zig index c3019c3..db0993a 100644 --- a/src/xorg.zig +++ b/src/xorg.zig @@ -154,7 +154,7 @@ pub fn main() !void { const screen = iter.data; const mask = c.XCB_CW_EVENT_MASK; - const value = c.XCB_EVENT_MASK_EXPOSURE | c.XCB_EVENT_MASK_KEY_PRESS | c.XCB_EVENT_MASK_KEY_RELEASE; + const value = c.XCB_EVENT_MASK_EXPOSURE | c.XCB_EVENT_MASK_KEY_PRESS | c.XCB_EVENT_MASK_KEY_RELEASE | c.XCB_EVENT_MASK_BUTTON_PRESS; const window = c.xcb_generate_id(connection); _ = c.xcb_create_window(connection, c.XCB_COPY_FROM_PARENT, window, screen.*.root, 0, 0, 800, 600, 10, c.XCB_WINDOW_CLASS_INPUT_OUTPUT, screen.*.root_visual, mask, &value); @@ -190,6 +190,14 @@ pub fn main() !void { const key = c.xcb_key_symbols_get_keysym(keysyms, ev.detail, 0); sideros.sideros_key_callback(mapKeysym(key), true); }, + c.XCB_BUTTON_PRESS => { + const ev: *c.xcb_button_press_event_t = @ptrCast(e); + switch (ev.detail) { + 4 => sideros.sideros_scroll_callback(true), + 5 => sideros.sideros_scroll_callback(false), + else => {}, + } + }, else => {}, } std.c.free(e);