From 274cfb471b6c8788380723fb3a598ede59a54a81 Mon Sep 17 00:00:00 2001 From: Ivory Date: Mon, 23 Sep 2024 02:55:22 -0400 Subject: [PATCH] added camera panning and lots of input stuff --- src/Camera.zig | 130 ++++++++++++++++++++++++++++-- src/Engine.zig | 22 +++++- src/Entity.zig | 174 ++++++++++++++++++++++++++--------------- src/Layer.zig | 1 + src/Scene.zig | 86 +++++++++++++------- src/Selection.zig | 12 ++- src/geometry/Rectf.zig | 17 ++++ src/geometry/Recti.zig | 8 +- src/geometry/Vec2f.zig | 41 +++++++++- src/geometry/Vec2i.zig | 39 +++++++++ 10 files changed, 417 insertions(+), 113 deletions(-) diff --git a/src/Camera.zig b/src/Camera.zig index b867e60..8011f79 100644 --- a/src/Camera.zig +++ b/src/Camera.zig @@ -11,6 +11,12 @@ const Color = @import("Color.zig"); const Vec2i = @import("geometry/Vec2i.zig"); const Vec2f = @import("geometry/Vec2f.zig"); const Recti = @import("geometry/Recti.zig"); +const Rectf = @import("geometry/Rectf.zig"); +const shaders = @import("shaders.zig"); +const c = @cImport({ + @cInclude("glad/glad.h"); + @cInclude("GLFW/glfw3.h"); +}); // focus: Vec2i = Vec2i.EAST.scale(100), focus: Vec2f = Vec2f.ZERO, @@ -18,6 +24,39 @@ tile_size: i32 = 16, window_size_offset_x: i32 = 0, window_size_offset_y: i32 = 0, +drag_pos: ?Vec2i = null, +drag_focus: ?Vec2f = null, + +fn draw_line_vec2i(a: Vec2i, b: Vec2i, layer: Layer, color: Color) void { + shaders.disable_textures(); + + c.glBegin(c.GL_LINES); + { + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(a.x, a.y, layer.z_index); + + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(b.x, b.y, layer.z_index); + } + + c.glEnd(); +} + +fn draw_line_vec2f(a: Vec2f, b: Vec2f, layer: Layer, color: Color) void { + shaders.disable_textures(); + + c.glBegin(c.GL_LINES); + { + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3f(a.x, a.y, @as(f32, @floatFromInt(layer.z_index))); + + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3f(b.x, b.y, @as(f32, @floatFromInt(layer.z_index))); + } + + c.glEnd(); +} + pub fn create() !*Camera { const self = Camera {}; return try Scene.EntityPool.allocate(self); @@ -38,26 +77,81 @@ pub fn entity(self: *Camera) Entity { }); } +pub fn update(self: *Camera, _: f32) void { + if (self.drag_pos != null and self.drag_focus != null and Engine.mouse_pos != null) { + // new focus = OG focus + difference between drag start and drag now + const drag_start_world = self.screen_to_world_vec2i(self.drag_pos.?); + const drag_current_world = self.screen_to_world_vec2i(Engine.mouse_pos.?); + + self.focus = Vec2f { + .x = self.drag_focus.?.x + (drag_start_world.x - drag_current_world.x), + .y = self.drag_focus.?.y + (drag_start_world.y - drag_current_world.y), + }; + } +} + +pub fn draw(self: *const Camera) void { + if (Engine.Debug.CAMERA and self.drag_pos != null and self.drag_focus != null) { + if(Engine.mouse_pos != null) { + draw_line_vec2i(self.drag_pos.?, Engine.mouse_pos.?, Layer.CAMERA, Color.WHITE); + Engine.mouse_pos.?.draw(4, Layer.CAMERA, Color.CYAN); + } + self.drag_pos.?.draw(4, Layer.CAMERA, Color.RED); + + const start_focus_screen = self.world_to_screen_vec2f(self.drag_focus.?); + const current_focus_screen = self.world_to_screen_vec2f(self.focus); + + draw_line_vec2i(start_focus_screen, current_focus_screen, Layer.CAMERA, Color.BLUE); + start_focus_screen.draw(4, Layer.CAMERA, Color.ORANGE); + current_focus_screen.draw(4, Layer.CAMERA, Color.LIME); + } +} + inline fn world_to_screen_vec2i(self: *const Camera, coords: Vec2i) Vec2i { const tile_size_f: f32 = @floatFromInt(self.tile_size); const focus_x_screen: i32 = @intFromFloat(self.focus.x * tile_size_f); const focus_y_screen: i32 = @intFromFloat(self.focus.y * tile_size_f); return Vec2i.create( - self.tile_size * coords.x - focus_x_screen + self.window_size_offset_x, - self.tile_size * coords.y - focus_y_screen + self.window_size_offset_y + @as(i32, @intFromFloat(tile_size_f * coords.x)) - focus_x_screen + self.window_size_offset_x, + @as(i32, @intFromFloat(tile_size_f * coords.y)) - focus_y_screen + self.window_size_offset_y ); } -inline fn world_to_screen_recti(self: *const Camera, box: Recti) Recti { +inline fn world_to_screen_vec2f(self: *const Camera, coords: Vec2f) Vec2i { + const tile_size_f: f32 = @floatFromInt(self.tile_size); + const focus_x_screen: i32 = @intFromFloat(self.focus.x * tile_size_f); + const focus_y_screen: i32 = @intFromFloat(self.focus.y * tile_size_f); + + return Vec2i.create( + @as(i32, @intFromFloat(tile_size_f * coords.x)) - focus_x_screen + self.window_size_offset_x, + @as(i32, @intFromFloat(tile_size_f * coords.y)) - focus_y_screen + self.window_size_offset_y + ); +} + +inline fn screen_to_world_vec2i(self: *const Camera, coords: Vec2i) Vec2f { + const tile_size_f: f32 = @floatFromInt(self.tile_size); + const focus_x_screen: i32 = @intFromFloat(self.focus.x * tile_size_f); + const focus_y_screen: i32 = @intFromFloat(self.focus.y * tile_size_f); + + const pre_x: f32 = @floatFromInt(coords.x + focus_x_screen - self.window_size_offset_x); + const pre_y: f32 = @floatFromInt(coords.y + focus_y_screen - self.window_size_offset_y); + + return Vec2f { + .x = pre_x / tile_size_f, + .y = pre_y / tile_size_f, + }; +} + +inline fn world_to_screen_recti(self: *const Camera, box: Rectf) Recti { return Recti.from_ab( - self.world_to_screen_vec2i(box.a), - self.world_to_screen_vec2i(box.b), + self.world_to_screen_vec2f(box.a), + self.world_to_screen_vec2f(box.b), ); } pub fn draw_sprite_i(self: *const Camera, sprite: *const Sprite, world_pos: Recti, layer: Layer, color: Color) void { - const screen_pos = self.world_to_screen_recti(world_pos); + const screen_pos = self.world_to_screen_recti(world_pos.to_rectf()); sprite.draw(screen_pos, layer, color); } @@ -65,6 +159,28 @@ pub fn set_focus(self: *Camera, f: Vec2f) void { self.focus = f; } -pub fn focus_layer(_: *const Camera) Layer { +pub fn mouse_layer(_: *const Camera) Layer { return Layer.CAMERA; } + +pub fn mouse_area(_: *const Camera) Recti { + return Recti.from_xywh(0, 0, 10_000, 10_000); +} + +pub fn mouse_down(self: *Camera, button: i32) bool { + // middle mouse + if (button == 2) { + if (Engine.mouse_pos == null) return false; + self.drag_pos = Engine.mouse_pos.?; + self.drag_focus = self.focus; + return true; + } else return false; +} + +pub fn mouse_up(self: *Camera, button: i32) bool { + if (self.drag_pos == null) return false; + if (button != 2) return false; + self.drag_pos = null; + self.drag_focus = null; + return true; +} diff --git a/src/Engine.zig b/src/Engine.zig index 89ed52d..cb8232b 100644 --- a/src/Engine.zig +++ b/src/Engine.zig @@ -8,6 +8,7 @@ const Layer = @import("Layer.zig"); const Color = @import("Color.zig"); const Scene = @import("Scene.zig"); const assets = @import("assets.zig"); +const Vec2i = @import("geometry/Vec2i.zig"); const c = @cImport({ @cInclude("glad/glad.h"); @@ -22,6 +23,10 @@ const Error = error { AlreadyRunning }; +pub const Debug = struct { + pub var CAMERA = true; +}; + var mouse_x: i32 = 0; var mouse_y: i32 = 0; var hovered: bool = true; @@ -29,6 +34,7 @@ var window: ?*c.GLFWwindow = null; var current_scene: ?Scene = null; var next_scene: ?Scene = null; var projection: Matrix4f = undefined; +pub var mouse_pos: ?Vec2i = null; fn glfw_on_error(err: c_int, desc_c: [*c]const u8) callconv(.C) void { const desc: *const u8 = @ptrCast(desc_c); @@ -46,6 +52,7 @@ fn glfw_on_resize(_: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) v fn glfw_on_mouse_move(_: ?*c.GLFWwindow, fx: f64, fy: f64) callconv(.C) void { const x: i32 = @intFromFloat(fx); const y: i32 = @intFromFloat(fy); + mouse_pos = Vec2i { .x = x, .y = y }; if (current_scene != null and hovered) { current_scene.?.mouse_move(x, y); } @@ -53,6 +60,18 @@ fn glfw_on_mouse_move(_: ?*c.GLFWwindow, fx: f64, fy: f64) callconv(.C) void { fn glfw_on_mouse_enter(_: ?*c.GLFWwindow, mouse_within: c_int) callconv(.C) void { hovered = mouse_within == c.GLFW_TRUE; + if (!hovered) mouse_pos = null; +} + +fn glfw_on_mouse_button(_: ?*c.GLFWwindow, button: i32, pressed: i32, _: i32) callconv(.C) void { + // std.debug.print("[Engine:glfw_on_mouse_button] {}, {}, {}\n", .{ x, y, z }); + if (current_scene != null) { + if (pressed == c.GLFW_TRUE) { + current_scene.?.mouse_down(button); + } else { + current_scene.?.mouse_up(button); + } + } } fn get_window_size() struct { i32, i32 } { @@ -83,7 +102,7 @@ pub fn setup() !void { // c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE); // create the window & cleanup - window = c.glfwCreateWindow(1920, 1080, "My Title", null, null) orelse { + window = c.glfwCreateWindow(1920, 1080, "Hadean, maybe", null, null) orelse { std.log.err("Failed to open window", .{}); return error.Initialization; }; @@ -105,6 +124,7 @@ pub fn setup() !void { _ = c.glfwSetWindowSizeCallback(window, glfw_on_resize); _ = c.glfwSetCursorPosCallback(window, glfw_on_mouse_move); _ = c.glfwSetCursorEnterCallback(window, glfw_on_mouse_enter); + _ = c.glfwSetMouseButtonCallback(window, glfw_on_mouse_button); const clearBrightness: f32 = 0.09; c.glClearColor(clearBrightness, clearBrightness, clearBrightness, 1.0); diff --git a/src/Entity.zig b/src/Entity.zig index ed42475..1b5cfe9 100644 --- a/src/Entity.zig +++ b/src/Entity.zig @@ -14,18 +14,25 @@ const EntityOptions = struct { ptr: *anyopaque, tag: Tag, + +_name: *const fn(*const anyopaque) []const u8, _destroy: *const fn(*const anyopaque) void, _update: *const fn(*anyopaque, f32) void, _draw: *const fn(*const anyopaque) void, _draw_opacity: *const fn(*const anyopaque) void, _start: *const fn(*anyopaque, *Scene) void, _resize: *const fn(*anyopaque, i32, i32) void, -_focusable: *const fn(*const anyopaque) bool, -_focus_layer: *const fn(*const anyopaque) Layer, -_focus_area: *const fn(*const anyopaque) Recti, + +// mouse functions +_mouse_ready: *const fn(*const anyopaque) bool, +_mouse_enabled: *const fn(*const anyopaque) bool, +_mouse_area: *const fn(*const anyopaque) Recti, +_mouse_layer: *const fn(*const anyopaque) Layer, _mouse_move: *const fn(*anyopaque, i32, i32) void, -_focus: *const fn(*anyopaque) void, -_blur: *const fn(*anyopaque) void, +_mouse_in: *const fn(*anyopaque) void, +_mouse_out: *const fn(*anyopaque) void, +_mouse_down: *const fn(*anyopaque, i32) bool, +_mouse_up: *const fn(*anyopaque, i32) bool, const ArrayTruthState = enum { TRUE, @@ -49,10 +56,10 @@ fn bool_array_state(arr: []const bool) ArrayTruthState { } pub fn init(ptr: anytype, options: EntityOptions) Entity { - std.debug.print("[Entity:init] creating entity {s} with tag \"{s}\"\n", .{ - @typeName(@typeInfo(@TypeOf(ptr)).Pointer.child), - options.tag.type - }); + // std.debug.print("[Entity:init] creating entity {s} with tag \"{s}\"\n", .{ + // @typeName(@typeInfo(@TypeOf(ptr)).Pointer.child), + // options.tag.type + // }); const MutablePointer = @TypeOf(ptr); // @compileLog("Compiling Entity type for " ++ @typeName(MutablePointer)); @@ -69,22 +76,23 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity { } comptime var input_ready = ArrayTruthState.FALSE; - comptime { - input_ready = bool_array_state(&[_]bool { - @hasDecl(type_info.Pointer.child, "focus_area"), - @hasDecl(type_info.Pointer.child, "focus_layer"), - @hasDecl(type_info.Pointer.child, "focus"), - @hasDecl(type_info.Pointer.child, "blur"), + // comptime { + input_ready = comptime bool_array_state(&[_]bool { + @hasDecl(type_info.Pointer.child, "mouse_enabled"), + @hasDecl(type_info.Pointer.child, "mouse_area"), + @hasDecl(type_info.Pointer.child, "mouse_layer"), + @hasDecl(type_info.Pointer.child, "mouse_in"), + @hasDecl(type_info.Pointer.child, "mouse_out"), @hasDecl(type_info.Pointer.child, "mouse_move"), }); - switch(input_ready) { - .TRUE => {}, - .FALSE => {}, - .EMPTY => {}, - .MIXED => @compileError(@typeName(type_info.Pointer.child) ++ " does not defien all methods required for input focus"), - } - } + // switch(input_ready) { + // .TRUE => {}, + // .FALSE => {}, + // .EMPTY => {}, + // .MIXED => @compileError(@typeName(type_info.Pointer.child) ++ " does not define all methods required for input focus"), + // } + // } // @compileLog(MutablePointer); // @compileLog(input_ready); @@ -95,47 +103,69 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity { // @compileLog("const pointer type: " ++ @typeName(ConstPointer)); const gen = struct { - pub fn _focusable(_: *const anyopaque) bool { - return input_ready == ArrayTruthState.TRUE; + // mouse functions + pub fn _mouse_ready(_: *const anyopaque) bool { + return input_ready != ArrayTruthState.FALSE; } - pub fn _focus_layer(pointer: *const anyopaque) Layer { + pub fn _mouse_enabled(pointer: *const anyopaque) bool { const self: ConstPointer = @ptrCast(@alignCast(pointer)); - switch (input_ready) { - .TRUE => return type_info.Pointer.child.focus_layer(self), - else => unreachable, + if (@hasDecl(type_info.Pointer.child, "mouse_enabled")) { + type_info.Pointer.child.mouse_enabled(self); + } else { + return true; } } - pub fn _focus_area(pointer: *const anyopaque) Recti { + pub fn _mouse_layer(pointer: *const anyopaque) Layer { const self: ConstPointer = @ptrCast(@alignCast(pointer)); - switch(input_ready) { - .TRUE => return type_info.Pointer.child.focus_area(self), - else => unreachable, + if (@hasDecl(type_info.Pointer.child, "mouse_layer")) { + return type_info.Pointer.child.mouse_layer(self); + } else { + return Layer.HIDDEN; + } + } + pub fn _mouse_area(pointer: *const anyopaque) Recti { + const self: ConstPointer = @ptrCast(@alignCast(pointer)); + if (@hasDecl(type_info.Pointer.child, "mouse_area")) { + return type_info.Pointer.child.mouse_area(self); + } else { + return Recti.from_xywh(-10, -10, 0, 0); } } pub fn _mouse_move(pointer: *anyopaque, x: i32, y: i32) void { const self: MutablePointer = @ptrCast(@alignCast(pointer)); - switch(input_ready) { - .TRUE => type_info.Pointer.child.mouse_move(self, x, y), - else => unreachable, + if (@hasDecl(type_info.Pointer.child, "mouse_move")) { + type_info.Pointer.child.mouse_move(self, x, y); } } - pub fn _focus(pointer: *anyopaque) void { + pub fn _mouse_in(pointer: *anyopaque) void { const self: MutablePointer = @ptrCast(@alignCast(pointer)); - switch(input_ready) { - .TRUE => type_info.Pointer.child.focus(self), - else => unreachable, + if (@hasDecl(type_info.Pointer.child, "mouse_in")) { + type_info.Pointer.child.mouse_in(self); } } - pub fn _blur(pointer: *anyopaque) void { + pub fn _mouse_out(pointer: *anyopaque) void { const self: MutablePointer = @ptrCast(@alignCast(pointer)); - switch(input_ready) { - .TRUE => type_info.Pointer.child.blur(self), - else => unreachable, + if (@hasDecl(type_info.Pointer.child, "mouse_out")) { + type_info.Pointer.child.mouse_out(self); } } + pub fn _mouse_down(pointer: *anyopaque, button: i32) bool { + const self: MutablePointer = @ptrCast(@alignCast(pointer)); + if (@hasDecl(type_info.Pointer.child, "mouse_down")) { + return type_info.Pointer.child.mouse_down(self, button); + } else return false; + } + pub fn _mouse_up(pointer: *anyopaque, button: i32) bool { + const self: MutablePointer = @ptrCast(@alignCast(pointer)); + if (@hasDecl(type_info.Pointer.child, "mouse_up")) { + return type_info.Pointer.child.mouse_up(self, button); + } else return false; + } + + // general methods pub fn _name(_: *const anyopaque) []const u8 { // const self: ConstPointer = @ptrCast(@alignCast(pointer)); - return type_info.Pointer.child; + return @typeName(type_info.Pointer.child); } pub fn _destroy(pointer: *const anyopaque) void { const self: ConstPointer = @ptrCast(@alignCast(pointer)); @@ -176,18 +206,24 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity { return Entity { .ptr = ptr, .tag = options.tag, + ._name = gen._name, ._destroy = gen._destroy, ._start = gen._start, ._resize = gen._resize, ._update = gen._update, ._draw = gen._draw, ._draw_opacity = gen._draw_opacity, - ._focusable = gen._focusable, - ._focus_layer = gen._focus_layer, - ._focus_area = gen._focus_area, + + // mouse stuff + ._mouse_ready = gen._mouse_ready, + ._mouse_enabled = gen._mouse_enabled, + ._mouse_layer = gen._mouse_layer, + ._mouse_area = gen._mouse_area, ._mouse_move = gen._mouse_move, - ._focus = gen._focus, - ._blur = gen._blur, + ._mouse_in = gen._mouse_in, + ._mouse_out = gen._mouse_out, + ._mouse_down = gen._mouse_down, + ._mouse_up = gen._mouse_up, }; } @@ -215,33 +251,47 @@ pub fn resize(self: *const Entity, width: i32, height: i32) void { self._resize(self.ptr, width, height); } -pub fn focus(self: *const Entity) void { - self._focus(self.ptr); +pub fn name(self: *const Entity) []const u8 { + return self._name(self.ptr); } -pub fn blur(self: *const Entity) void { - self._blur(self.ptr); +// -=- mouse related items +pub fn mouse_ready(self: *const Entity) bool { + return self._mouse_ready(self.ptr); } -// -=- focus related items - -// defines if the entity has functions to define its focus area -pub fn focusable(self: *const Entity) bool { - return self._focusable(self.ptr); +pub fn mouse_enabled(self: *const Entity) bool { + return self._mouse_enabled(self.ptr); } -pub fn focus_layer(self: *const Entity) Layer { - return self._focus_layer(self.ptr); +pub fn mouse_layer(self: *const Entity) Layer { + return self._mouse_layer(self.ptr); } -pub fn focus_area(self: *const Entity) Recti { - return self._focus_area(self.ptr); +pub fn mouse_area(self: *const Entity) Recti { + return self._mouse_area(self.ptr); } pub fn mouse_move(self: *const Entity, x: i32, y: i32) void { return self._mouse_move(self.ptr, x, y); } +pub fn mouse_in(self: *const Entity) void { + self._mouse_in(self.ptr); +} + +pub fn mouse_out(self: *const Entity) void { + self._mouse_out(self.ptr); +} + +pub fn mouse_down(self: *const Entity, button: i32) bool { + return self._mouse_down(self.ptr, button); +} + +pub fn mouse_up(self: *const Entity, button: i32) bool { + return self._mouse_up(self.ptr, button); +} + pub fn allocate_array(comptime T: type, size: usize, default: T) ![]T { const ptr = try std.heap.page_allocator.alloc(T, size); for (0..size) |idx| { diff --git a/src/Layer.zig b/src/Layer.zig index 4949af9..497aa77 100644 --- a/src/Layer.zig +++ b/src/Layer.zig @@ -8,6 +8,7 @@ fn layer(z_index: i32) Layer { }; } +pub const HIDDEN = layer(-1); pub const FLOOR = layer(0); pub const ENTITIES = layer(1); pub const CAMERA = layer(2); diff --git a/src/Scene.zig b/src/Scene.zig index 7fe82f3..80905a6 100644 --- a/src/Scene.zig +++ b/src/Scene.zig @@ -14,17 +14,17 @@ const AutoHashMap = std.AutoHashMap; // cache and list bothe store entities. treat entities as fat pointers. // Pointers into an arraylist WILL become invalid. -focus_items: ArrayList(Entity) = undefined, +mouse_ready_entities: ArrayList(Entity) = undefined, entities: ArrayList(Entity) = undefined, tagged_entities: AutoHashMap(Tag.ID, Entity) = undefined, mouse_inside: bool = false, -current_focus_item: ?Entity = null, +current_hover_item: ?Entity = null, mouse_pos: ?Vec2i = null, pub fn create() Scene { var self = Scene {}; self.entities = ArrayList(Entity).init(std.heap.page_allocator); - self.focus_items = ArrayList(Entity).init(std.heap.page_allocator); + self.mouse_ready_entities = ArrayList(Entity).init(std.heap.page_allocator); self.tagged_entities = AutoHashMap(Tag.ID, Entity).init(std.heap.page_allocator); return self; } @@ -39,11 +39,15 @@ pub fn destroy(self: *Scene) void { pub fn start(self: *Scene) !void { std.debug.print("[Scene:start] Scene starting...\n", .{}); std.debug.print("[Scene:start] entities: {}\n", .{ self.entities.items.len }); - std.debug.print("[Scene:start] tagged entities: {}\n", .{ self.tagged_entities.count() }); - var tagged_entities_iterator = self.tagged_entities.iterator(); - while (tagged_entities_iterator.next()) |entry| { - std.debug.print("[Scene:start] - {s}: {*}\n", .{ entry.key_ptr.*, entry.value_ptr }); + + for (self.entities.items) |*entity| { + if (entity.tag.id == Tag.NONE.id) { + std.debug.print("[Scene:start] - {s}\n", .{ entity.name() }); + } else { + std.debug.print("[Scene:start] - {s} [{s}]\n", .{ entity.name(), entity.tag.type }); + } } + for (self.entities.items) |entity| { entity.start(self); } @@ -84,13 +88,15 @@ pub fn add(self: *Scene, instance_ptr: anytype) !void { if (entity.tag.id != Tag.NONE.id) { try self.tagged_entities.put(entity.tag.id, entity); } - if (entity.focusable()) { - try self.focus_items.append(entity); + + if (entity.mouse_ready()) { + try self.mouse_ready_entities.append(entity); + std.mem.sort(Entity, self.mouse_ready_entities.items, {}, entity_layer_less_than); } } fn entity_layer_less_than(_: void, lhs: Entity, rhs: Entity) bool { - return lhs.focus_layer().z_index < rhs.focus_layer().z_index; + return lhs.mouse_layer().z_index < rhs.mouse_layer().z_index; } pub fn update(self: *Scene, dt: f32) void { @@ -99,38 +105,37 @@ pub fn update(self: *Scene, dt: f32) void { } // this whole operation is wildly stupid and could be amde more efficient. - std.mem.sort(Entity, self.focus_items.items, {}, entity_layer_less_than); if (self.mouse_pos) |mouse| { var top_hovered: ?Entity = null; - for (self.focus_items.items) |item| { - if (item.focusable() and item.focus_area().contains(mouse)) { + for (self.mouse_ready_entities.items) |item| { + if (item.mouse_ready() and item.mouse_area().contains(mouse)) { top_hovered = item; continue; } } if (top_hovered != null) { - if (self.current_focus_item != null) { + if (self.current_hover_item != null) { // if we're changing hover target from something to something - if (self.current_focus_item.?.ptr != top_hovered.?.ptr) { - self.current_focus_item.?.blur(); - self.current_focus_item = top_hovered; - self.current_focus_item.?.focus(); + if (self.current_hover_item.?.ptr != top_hovered.?.ptr) { + self.current_hover_item.?.mouse_out(); + self.current_hover_item = top_hovered; + self.current_hover_item.?.mouse_in(); } } else { // we are hovering something, and werent before. - self.current_focus_item = top_hovered.?; - self.current_focus_item.?.focus(); + self.current_hover_item = top_hovered.?; + self.current_hover_item.?.mouse_in(); } - } else if (self.current_focus_item != null) { + } else if (self.current_hover_item != null) { // here we were hovering something, but now are not. - self.current_focus_item.?.blur(); - self.current_focus_item = null; + self.current_hover_item.?.mouse_out(); + self.current_hover_item = null; } - } else if (self.current_focus_item != null) { - self.current_focus_item.?.blur(); - self.current_focus_item = null; + } else if (self.current_hover_item != null) { + self.current_hover_item.?.mouse_out(); + self.current_hover_item = null; } } @@ -142,8 +147,12 @@ pub fn draw(self: *Scene) void { for (self.entities.items) |entity| { entity.draw_opacity(); } - for (self.focus_items.items) |item| { - item.focus_area().draw(item.focus_layer(), Color.WHITE.with_opacity(0.3)); + for (self.mouse_ready_entities.items) |item| { + if (self.current_hover_item != null and item.ptr == self.current_hover_item.?.ptr) { + item.mouse_area().draw(item.mouse_layer(), Color.LIME.with_opacity(0.3)); + } else { + item.mouse_area().draw(item.mouse_layer(), Color.WHITE.with_opacity(0.3)); + } } } @@ -157,7 +166,7 @@ pub fn mouse_move(self: *Scene, x: i32, y: i32) void { // std.debug.print("[Scene:mouse_move] {} {} {}\n", .{ self, x, y }); self.mouse_pos = Vec2i { .x = x, .y = y }; self.mouse_inside = true; - for (self.focus_items.items) |item| { + for (self.mouse_ready_entities.items) |item| { item.mouse_move(x, y); } } @@ -171,6 +180,25 @@ pub fn mouse_leave(self: *Scene) void { self.mouse_pos = null; } +pub fn mouse_down(self: *Scene, button: i32) void { + // std.debug.print("[Scene:mouse_down] {?}\n", .{ self.mouse_pos }); + if (self.mouse_pos == null) return; + for (self.mouse_ready_entities.items) |entity| { + if (entity.mouse_area().contains(self.mouse_pos.?)) { + if (entity.mouse_down(button)) return; + } + } +} + +pub fn mouse_up(self: *Scene, button: i32) void { + if (self.mouse_pos == null) return; + for (self.mouse_ready_entities.items) |entity| { + if (entity.mouse_area().contains(self.mouse_pos.?)) { + if (entity.mouse_up(button)) return; + } + } +} + // --- helpfer functions for entities themselves pub const EntityPool = struct { // an entity can choose to allocate itself however it pleases, but these diff --git a/src/Selection.zig b/src/Selection.zig index c233c31..83f0ca7 100644 --- a/src/Selection.zig +++ b/src/Selection.zig @@ -30,23 +30,21 @@ pub fn destroy(self: *const Selection) void { Scene.EntityPool.deallocate(self); } -pub fn focus_area(_: *const Selection) Recti { +pub fn mouse_area(_: *const Selection) Recti { // return Recti.from_xywh(0, 0, std.math.maxInt(i16), std.math.maxInt(i16)); return Recti.from_xywh(0, 0, 300, 1000); } -pub fn focus_layer(_: *const Selection) Layer { - return Layer.ENTITIES; +pub fn mouse_layer(_: *const Selection) Layer { + return Layer.SELECTION; } -pub fn focus(self: *Selection) void { +pub fn mouse_in(self: *Selection) void { self.focused = true; - std.debug.print("[Selection:focus] Focused\n", .{}); } -pub fn blur(self: *Selection) void { +pub fn mouse_out(self: *Selection) void { self.focused = false; - std.debug.print("[Selection:focus] Blurred\n", .{}); } pub fn mouse_move(_: *Selection, _: i32, _: i32) void { diff --git a/src/geometry/Rectf.zig b/src/geometry/Rectf.zig index d4b884b..8aa1982 100644 --- a/src/geometry/Rectf.zig +++ b/src/geometry/Rectf.zig @@ -22,6 +22,23 @@ pub fn from_xywh(x: f32, y: f32, w: f32, h: f32) Rectf { }; } +pub fn from_ab(a: Vec2f, b: Vec2f) Rectf { + const tlx = @min(a.x, b.x); + const tly = @min(a.y, b.y); + const brx = @max(a.x, b.x); + const bry = @max(a.y, b.y); + const width = brx - tlx; + const height = bry - tly; + return .{ + .x = tlx, + .y = tly, + .w = width, + .h = height, + .a = Vec2f.create(tlx, tly), + .b = Vec2f.create(brx, bry), + }; +} + pub fn to_recti(self: *const Rectf) Recti { return Recti.from_xywh( @intCast(self.x), diff --git a/src/geometry/Recti.zig b/src/geometry/Recti.zig index a75f41d..64c802a 100644 --- a/src/geometry/Recti.zig +++ b/src/geometry/Recti.zig @@ -52,11 +52,9 @@ pub fn contains(self: *const Recti, point: Vec2i) bool { } pub fn to_rectf(self: *const Recti) Rectf { - return Rectf.from_xywh( - @intCast(self.x), - @intCast(self.y), - @intCast(self.w), - @intCast(self.h), + return Rectf.from_ab( + self.a.to_vec2f(), + self.b.to_vec2f() ); } diff --git a/src/geometry/Vec2f.zig b/src/geometry/Vec2f.zig index b01db3d..1cfac35 100644 --- a/src/geometry/Vec2f.zig +++ b/src/geometry/Vec2f.zig @@ -1,16 +1,53 @@ - const Vec2f = @This(); +const Vec2i = @import("Vec2i.zig"); +const Layer = @import("../Layer.zig"); +const Color = @import("../Color.zig"); +const shaders = @import("../shaders.zig"); +const std = @import("std"); +const c = @cImport({ + @cInclude("glad/glad.h"); + @cInclude("GLFW/glfw3.h"); +}); x: f32, y: f32, pub fn create(x: f32, y: f32) Vec2f { - return .{ + return Vec2f { .x = x, .y = y, }; } +pub fn to_vec2i(self: *const Vec2f) Vec2i { + return Vec2i { + .x = @intFromFloat(self.x), + .y = @intFromFloat(self.y), + }; +} + +pub fn draw(self: *const Vec2f, radius: f32, layer: Layer, color: Color) void { + shaders.disable_textures(); + + const segments = 8; + + c.glBegin(c.GL_POLYGON); + { + for (0..segments) |segment| { + const theta = (2.0 * std.math.pi * @as(f32, @floatFromInt(segment))) / segments; + const x = radius * std.math.cos(theta); + const y = radius * std.math.sin(theta); + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3f( + self.x + x, + self.y + y, + @as(f32, @floatFromInt(layer.z_index)) + ); + } + } + c.glEnd(); +} + pub const ZERO = create(0, 0); pub const ONE = create(1, 1); pub const NORTH = create(0, -1); diff --git a/src/geometry/Vec2i.zig b/src/geometry/Vec2i.zig index 01a9355..aad1319 100644 --- a/src/geometry/Vec2i.zig +++ b/src/geometry/Vec2i.zig @@ -1,6 +1,15 @@ const Vec2i = @This(); const Recti = @import("Recti.zig"); +const Vec2f = @import("Vec2f.zig"); +const Layer = @import("../Layer.zig"); +const Color = @import("../Color.zig"); +const shaders = @import("../shaders.zig"); +const std = @import("std"); +const c = @cImport({ + @cInclude("glad/glad.h"); + @cInclude("GLFW/glfw3.h"); +}); x: i32, y: i32, @@ -23,6 +32,36 @@ pub fn to_unit_recti(self: *const Vec2i) Recti { return Recti.from_xywh(self.x, self.y, 1, 1); } +pub fn draw(self: *const Vec2i, radius: f32, layer: Layer, color: Color) void { + shaders.disable_textures(); + + const segments = 8; + + c.glBegin(c.GL_POLYGON); + { + for (0..segments) |segment| { + const theta = (2.0 * std.math.pi * @as(f32, @floatFromInt(segment))) / segments; + const x = radius * std.math.cos(theta); + const y = radius * std.math.sin(theta); + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3f( + @as(f32, @floatFromInt(self.x)) + x, + @as(f32, @floatFromInt(self.y)) + y, + @as(f32, @floatFromInt(layer.z_index)) + ); + } + } + c.glEnd(); +} + +pub fn to_vec2f(self: *const Vec2i) Vec2f { + return Vec2f { + .x = @floatFromInt(self.x), + .y = @floatFromInt(self.y), + }; +} + + pub const ZERO = create(0, 0); pub const ONE = create(1, 1); pub const NORTH = create(0, -1);