added camera panning and lots of input stuff

stable
Ivory 2024-09-23 02:55:22 -04:00
parent 91251651b7
commit 274cfb471b
10 changed files with 417 additions and 113 deletions

View File

@ -11,6 +11,12 @@ const Color = @import("Color.zig");
const Vec2i = @import("geometry/Vec2i.zig"); const Vec2i = @import("geometry/Vec2i.zig");
const Vec2f = @import("geometry/Vec2f.zig"); const Vec2f = @import("geometry/Vec2f.zig");
const Recti = @import("geometry/Recti.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: Vec2i = Vec2i.EAST.scale(100),
focus: Vec2f = Vec2f.ZERO, focus: Vec2f = Vec2f.ZERO,
@ -18,6 +24,39 @@ tile_size: i32 = 16,
window_size_offset_x: i32 = 0, window_size_offset_x: i32 = 0,
window_size_offset_y: 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 { pub fn create() !*Camera {
const self = Camera {}; const self = Camera {};
return try Scene.EntityPool.allocate(self); 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 { inline fn world_to_screen_vec2i(self: *const Camera, coords: Vec2i) Vec2i {
const tile_size_f: f32 = @floatFromInt(self.tile_size); const tile_size_f: f32 = @floatFromInt(self.tile_size);
const focus_x_screen: i32 = @intFromFloat(self.focus.x * tile_size_f); 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 focus_y_screen: i32 = @intFromFloat(self.focus.y * tile_size_f);
return Vec2i.create( return Vec2i.create(
self.tile_size * coords.x - focus_x_screen + self.window_size_offset_x, @as(i32, @intFromFloat(tile_size_f * 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.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( return Recti.from_ab(
self.world_to_screen_vec2i(box.a), self.world_to_screen_vec2f(box.a),
self.world_to_screen_vec2i(box.b), 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 { 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); sprite.draw(screen_pos, layer, color);
} }
@ -65,6 +159,28 @@ pub fn set_focus(self: *Camera, f: Vec2f) void {
self.focus = f; self.focus = f;
} }
pub fn focus_layer(_: *const Camera) Layer { pub fn mouse_layer(_: *const Camera) Layer {
return Layer.CAMERA; 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;
}

View File

@ -8,6 +8,7 @@ const Layer = @import("Layer.zig");
const Color = @import("Color.zig"); const Color = @import("Color.zig");
const Scene = @import("Scene.zig"); const Scene = @import("Scene.zig");
const assets = @import("assets.zig"); const assets = @import("assets.zig");
const Vec2i = @import("geometry/Vec2i.zig");
const c = @cImport({ const c = @cImport({
@cInclude("glad/glad.h"); @cInclude("glad/glad.h");
@ -22,6 +23,10 @@ const Error = error {
AlreadyRunning AlreadyRunning
}; };
pub const Debug = struct {
pub var CAMERA = true;
};
var mouse_x: i32 = 0; var mouse_x: i32 = 0;
var mouse_y: i32 = 0; var mouse_y: i32 = 0;
var hovered: bool = true; var hovered: bool = true;
@ -29,6 +34,7 @@ var window: ?*c.GLFWwindow = null;
var current_scene: ?Scene = null; var current_scene: ?Scene = null;
var next_scene: ?Scene = null; var next_scene: ?Scene = null;
var projection: Matrix4f = undefined; 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 { fn glfw_on_error(err: c_int, desc_c: [*c]const u8) callconv(.C) void {
const desc: *const u8 = @ptrCast(desc_c); 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 { fn glfw_on_mouse_move(_: ?*c.GLFWwindow, fx: f64, fy: f64) callconv(.C) void {
const x: i32 = @intFromFloat(fx); const x: i32 = @intFromFloat(fx);
const y: i32 = @intFromFloat(fy); const y: i32 = @intFromFloat(fy);
mouse_pos = Vec2i { .x = x, .y = y };
if (current_scene != null and hovered) { if (current_scene != null and hovered) {
current_scene.?.mouse_move(x, y); 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 { fn glfw_on_mouse_enter(_: ?*c.GLFWwindow, mouse_within: c_int) callconv(.C) void {
hovered = mouse_within == c.GLFW_TRUE; 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 } { 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); // c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE);
// create the window & cleanup // 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", .{}); std.log.err("Failed to open window", .{});
return error.Initialization; return error.Initialization;
}; };
@ -105,6 +124,7 @@ pub fn setup() !void {
_ = c.glfwSetWindowSizeCallback(window, glfw_on_resize); _ = c.glfwSetWindowSizeCallback(window, glfw_on_resize);
_ = c.glfwSetCursorPosCallback(window, glfw_on_mouse_move); _ = c.glfwSetCursorPosCallback(window, glfw_on_mouse_move);
_ = c.glfwSetCursorEnterCallback(window, glfw_on_mouse_enter); _ = c.glfwSetCursorEnterCallback(window, glfw_on_mouse_enter);
_ = c.glfwSetMouseButtonCallback(window, glfw_on_mouse_button);
const clearBrightness: f32 = 0.09; const clearBrightness: f32 = 0.09;
c.glClearColor(clearBrightness, clearBrightness, clearBrightness, 1.0); c.glClearColor(clearBrightness, clearBrightness, clearBrightness, 1.0);

View File

@ -14,18 +14,25 @@ const EntityOptions = struct {
ptr: *anyopaque, ptr: *anyopaque,
tag: Tag, tag: Tag,
_name: *const fn(*const anyopaque) []const u8,
_destroy: *const fn(*const anyopaque) void, _destroy: *const fn(*const anyopaque) void,
_update: *const fn(*anyopaque, f32) void, _update: *const fn(*anyopaque, f32) void,
_draw: *const fn(*const anyopaque) void, _draw: *const fn(*const anyopaque) void,
_draw_opacity: *const fn(*const anyopaque) void, _draw_opacity: *const fn(*const anyopaque) void,
_start: *const fn(*anyopaque, *Scene) void, _start: *const fn(*anyopaque, *Scene) void,
_resize: *const fn(*anyopaque, i32, i32) void, _resize: *const fn(*anyopaque, i32, i32) void,
_focusable: *const fn(*const anyopaque) bool,
_focus_layer: *const fn(*const anyopaque) Layer, // mouse functions
_focus_area: *const fn(*const anyopaque) Recti, _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, _mouse_move: *const fn(*anyopaque, i32, i32) void,
_focus: *const fn(*anyopaque) void, _mouse_in: *const fn(*anyopaque) void,
_blur: *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 { const ArrayTruthState = enum {
TRUE, TRUE,
@ -49,10 +56,10 @@ fn bool_array_state(arr: []const bool) ArrayTruthState {
} }
pub fn init(ptr: anytype, options: EntityOptions) Entity { pub fn init(ptr: anytype, options: EntityOptions) Entity {
std.debug.print("[Entity:init] creating entity {s} with tag \"{s}\"\n", .{ // std.debug.print("[Entity:init] creating entity {s} with tag \"{s}\"\n", .{
@typeName(@typeInfo(@TypeOf(ptr)).Pointer.child), // @typeName(@typeInfo(@TypeOf(ptr)).Pointer.child),
options.tag.type // options.tag.type
}); // });
const MutablePointer = @TypeOf(ptr); const MutablePointer = @TypeOf(ptr);
// @compileLog("Compiling Entity type for " ++ @typeName(MutablePointer)); // @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 var input_ready = ArrayTruthState.FALSE;
comptime { // comptime {
input_ready = bool_array_state(&[_]bool { input_ready = comptime bool_array_state(&[_]bool {
@hasDecl(type_info.Pointer.child, "focus_area"), @hasDecl(type_info.Pointer.child, "mouse_enabled"),
@hasDecl(type_info.Pointer.child, "focus_layer"), @hasDecl(type_info.Pointer.child, "mouse_area"),
@hasDecl(type_info.Pointer.child, "focus"), @hasDecl(type_info.Pointer.child, "mouse_layer"),
@hasDecl(type_info.Pointer.child, "blur"), @hasDecl(type_info.Pointer.child, "mouse_in"),
@hasDecl(type_info.Pointer.child, "mouse_out"),
@hasDecl(type_info.Pointer.child, "mouse_move"), @hasDecl(type_info.Pointer.child, "mouse_move"),
}); });
switch(input_ready) { // switch(input_ready) {
.TRUE => {}, // .TRUE => {},
.FALSE => {}, // .FALSE => {},
.EMPTY => {}, // .EMPTY => {},
.MIXED => @compileError(@typeName(type_info.Pointer.child) ++ " does not defien all methods required for input focus"), // .MIXED => @compileError(@typeName(type_info.Pointer.child) ++ " does not define all methods required for input focus"),
} // }
} // }
// @compileLog(MutablePointer); // @compileLog(MutablePointer);
// @compileLog(input_ready); // @compileLog(input_ready);
@ -95,47 +103,69 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity {
// @compileLog("const pointer type: " ++ @typeName(ConstPointer)); // @compileLog("const pointer type: " ++ @typeName(ConstPointer));
const gen = struct { const gen = struct {
pub fn _focusable(_: *const anyopaque) bool { // mouse functions
return input_ready == ArrayTruthState.TRUE; 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)); const self: ConstPointer = @ptrCast(@alignCast(pointer));
switch (input_ready) { if (@hasDecl(type_info.Pointer.child, "mouse_enabled")) {
.TRUE => return type_info.Pointer.child.focus_layer(self), type_info.Pointer.child.mouse_enabled(self);
else => unreachable, } 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)); const self: ConstPointer = @ptrCast(@alignCast(pointer));
switch(input_ready) { if (@hasDecl(type_info.Pointer.child, "mouse_layer")) {
.TRUE => return type_info.Pointer.child.focus_area(self), return type_info.Pointer.child.mouse_layer(self);
else => unreachable, } 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 { pub fn _mouse_move(pointer: *anyopaque, x: i32, y: i32) void {
const self: MutablePointer = @ptrCast(@alignCast(pointer)); const self: MutablePointer = @ptrCast(@alignCast(pointer));
switch(input_ready) { if (@hasDecl(type_info.Pointer.child, "mouse_move")) {
.TRUE => type_info.Pointer.child.mouse_move(self, x, y), type_info.Pointer.child.mouse_move(self, x, y);
else => unreachable,
} }
} }
pub fn _focus(pointer: *anyopaque) void { pub fn _mouse_in(pointer: *anyopaque) void {
const self: MutablePointer = @ptrCast(@alignCast(pointer)); const self: MutablePointer = @ptrCast(@alignCast(pointer));
switch(input_ready) { if (@hasDecl(type_info.Pointer.child, "mouse_in")) {
.TRUE => type_info.Pointer.child.focus(self), type_info.Pointer.child.mouse_in(self);
else => unreachable,
} }
} }
pub fn _blur(pointer: *anyopaque) void { pub fn _mouse_out(pointer: *anyopaque) void {
const self: MutablePointer = @ptrCast(@alignCast(pointer)); const self: MutablePointer = @ptrCast(@alignCast(pointer));
switch(input_ready) { if (@hasDecl(type_info.Pointer.child, "mouse_out")) {
.TRUE => type_info.Pointer.child.blur(self), type_info.Pointer.child.mouse_out(self);
else => unreachable,
} }
} }
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 { pub fn _name(_: *const anyopaque) []const u8 {
// const self: ConstPointer = @ptrCast(@alignCast(pointer)); // const self: ConstPointer = @ptrCast(@alignCast(pointer));
return type_info.Pointer.child; return @typeName(type_info.Pointer.child);
} }
pub fn _destroy(pointer: *const anyopaque) void { pub fn _destroy(pointer: *const anyopaque) void {
const self: ConstPointer = @ptrCast(@alignCast(pointer)); const self: ConstPointer = @ptrCast(@alignCast(pointer));
@ -176,18 +206,24 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity {
return Entity { return Entity {
.ptr = ptr, .ptr = ptr,
.tag = options.tag, .tag = options.tag,
._name = gen._name,
._destroy = gen._destroy, ._destroy = gen._destroy,
._start = gen._start, ._start = gen._start,
._resize = gen._resize, ._resize = gen._resize,
._update = gen._update, ._update = gen._update,
._draw = gen._draw, ._draw = gen._draw,
._draw_opacity = gen._draw_opacity, ._draw_opacity = gen._draw_opacity,
._focusable = gen._focusable,
._focus_layer = gen._focus_layer, // mouse stuff
._focus_area = gen._focus_area, ._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, ._mouse_move = gen._mouse_move,
._focus = gen._focus, ._mouse_in = gen._mouse_in,
._blur = gen._blur, ._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); self._resize(self.ptr, width, height);
} }
pub fn focus(self: *const Entity) void { pub fn name(self: *const Entity) []const u8 {
self._focus(self.ptr); return self._name(self.ptr);
} }
pub fn blur(self: *const Entity) void { // -=- mouse related items
self._blur(self.ptr); pub fn mouse_ready(self: *const Entity) bool {
return self._mouse_ready(self.ptr);
} }
// -=- focus related items pub fn mouse_enabled(self: *const Entity) bool {
return self._mouse_enabled(self.ptr);
// 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 focus_layer(self: *const Entity) Layer { pub fn mouse_layer(self: *const Entity) Layer {
return self._focus_layer(self.ptr); return self._mouse_layer(self.ptr);
} }
pub fn focus_area(self: *const Entity) Recti { pub fn mouse_area(self: *const Entity) Recti {
return self._focus_area(self.ptr); return self._mouse_area(self.ptr);
} }
pub fn mouse_move(self: *const Entity, x: i32, y: i32) void { pub fn mouse_move(self: *const Entity, x: i32, y: i32) void {
return self._mouse_move(self.ptr, x, y); 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 { pub fn allocate_array(comptime T: type, size: usize, default: T) ![]T {
const ptr = try std.heap.page_allocator.alloc(T, size); const ptr = try std.heap.page_allocator.alloc(T, size);
for (0..size) |idx| { for (0..size) |idx| {

View File

@ -8,6 +8,7 @@ fn layer(z_index: i32) Layer {
}; };
} }
pub const HIDDEN = layer(-1);
pub const FLOOR = layer(0); pub const FLOOR = layer(0);
pub const ENTITIES = layer(1); pub const ENTITIES = layer(1);
pub const CAMERA = layer(2); pub const CAMERA = layer(2);

View File

@ -14,17 +14,17 @@ const AutoHashMap = std.AutoHashMap;
// cache and list bothe store entities. treat entities as fat pointers. // cache and list bothe store entities. treat entities as fat pointers.
// Pointers into an arraylist WILL become invalid. // Pointers into an arraylist WILL become invalid.
focus_items: ArrayList(Entity) = undefined, mouse_ready_entities: ArrayList(Entity) = undefined,
entities: ArrayList(Entity) = undefined, entities: ArrayList(Entity) = undefined,
tagged_entities: AutoHashMap(Tag.ID, Entity) = undefined, tagged_entities: AutoHashMap(Tag.ID, Entity) = undefined,
mouse_inside: bool = false, mouse_inside: bool = false,
current_focus_item: ?Entity = null, current_hover_item: ?Entity = null,
mouse_pos: ?Vec2i = null, mouse_pos: ?Vec2i = null,
pub fn create() Scene { pub fn create() Scene {
var self = Scene {}; var self = Scene {};
self.entities = ArrayList(Entity).init(std.heap.page_allocator); 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); self.tagged_entities = AutoHashMap(Tag.ID, Entity).init(std.heap.page_allocator);
return self; return self;
} }
@ -39,11 +39,15 @@ pub fn destroy(self: *Scene) void {
pub fn start(self: *Scene) !void { pub fn start(self: *Scene) !void {
std.debug.print("[Scene:start] Scene starting...\n", .{}); std.debug.print("[Scene:start] Scene starting...\n", .{});
std.debug.print("[Scene:start] entities: {}\n", .{ self.entities.items.len }); 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(); for (self.entities.items) |*entity| {
while (tagged_entities_iterator.next()) |entry| { if (entity.tag.id == Tag.NONE.id) {
std.debug.print("[Scene:start] - {s}: {*}\n", .{ entry.key_ptr.*, entry.value_ptr }); 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| { for (self.entities.items) |entity| {
entity.start(self); entity.start(self);
} }
@ -84,13 +88,15 @@ pub fn add(self: *Scene, instance_ptr: anytype) !void {
if (entity.tag.id != Tag.NONE.id) { if (entity.tag.id != Tag.NONE.id) {
try self.tagged_entities.put(entity.tag.id, entity); 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 { 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 { 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. // 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| { if (self.mouse_pos) |mouse| {
var top_hovered: ?Entity = null; var top_hovered: ?Entity = null;
for (self.focus_items.items) |item| { for (self.mouse_ready_entities.items) |item| {
if (item.focusable() and item.focus_area().contains(mouse)) { if (item.mouse_ready() and item.mouse_area().contains(mouse)) {
top_hovered = item; top_hovered = item;
continue; continue;
} }
} }
if (top_hovered != null) { 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 we're changing hover target from something to something
if (self.current_focus_item.?.ptr != top_hovered.?.ptr) { if (self.current_hover_item.?.ptr != top_hovered.?.ptr) {
self.current_focus_item.?.blur(); self.current_hover_item.?.mouse_out();
self.current_focus_item = top_hovered; self.current_hover_item = top_hovered;
self.current_focus_item.?.focus(); self.current_hover_item.?.mouse_in();
} }
} else { } else {
// we are hovering something, and werent before. // we are hovering something, and werent before.
self.current_focus_item = top_hovered.?; self.current_hover_item = top_hovered.?;
self.current_focus_item.?.focus(); 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. // here we were hovering something, but now are not.
self.current_focus_item.?.blur(); self.current_hover_item.?.mouse_out();
self.current_focus_item = null; self.current_hover_item = null;
} }
} else if (self.current_focus_item != null) { } else if (self.current_hover_item != null) {
self.current_focus_item.?.blur(); self.current_hover_item.?.mouse_out();
self.current_focus_item = null; self.current_hover_item = null;
} }
} }
@ -142,8 +147,12 @@ pub fn draw(self: *Scene) void {
for (self.entities.items) |entity| { for (self.entities.items) |entity| {
entity.draw_opacity(); entity.draw_opacity();
} }
for (self.focus_items.items) |item| { for (self.mouse_ready_entities.items) |item| {
item.focus_area().draw(item.focus_layer(), Color.WHITE.with_opacity(0.3)); 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 }); // std.debug.print("[Scene:mouse_move] {} {} {}\n", .{ self, x, y });
self.mouse_pos = Vec2i { .x = x, .y = y }; self.mouse_pos = Vec2i { .x = x, .y = y };
self.mouse_inside = true; self.mouse_inside = true;
for (self.focus_items.items) |item| { for (self.mouse_ready_entities.items) |item| {
item.mouse_move(x, y); item.mouse_move(x, y);
} }
} }
@ -171,6 +180,25 @@ pub fn mouse_leave(self: *Scene) void {
self.mouse_pos = null; 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 // --- helpfer functions for entities themselves
pub const EntityPool = struct { pub const EntityPool = struct {
// an entity can choose to allocate itself however it pleases, but these // an entity can choose to allocate itself however it pleases, but these

View File

@ -30,23 +30,21 @@ pub fn destroy(self: *const Selection) void {
Scene.EntityPool.deallocate(self); 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, std.math.maxInt(i16), std.math.maxInt(i16));
return Recti.from_xywh(0, 0, 300, 1000); return Recti.from_xywh(0, 0, 300, 1000);
} }
pub fn focus_layer(_: *const Selection) Layer { pub fn mouse_layer(_: *const Selection) Layer {
return Layer.ENTITIES; return Layer.SELECTION;
} }
pub fn focus(self: *Selection) void { pub fn mouse_in(self: *Selection) void {
self.focused = true; 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; self.focused = false;
std.debug.print("[Selection:focus] Blurred\n", .{});
} }
pub fn mouse_move(_: *Selection, _: i32, _: i32) void { pub fn mouse_move(_: *Selection, _: i32, _: i32) void {

View File

@ -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 { pub fn to_recti(self: *const Rectf) Recti {
return Recti.from_xywh( return Recti.from_xywh(
@intCast(self.x), @intCast(self.x),

View File

@ -52,11 +52,9 @@ pub fn contains(self: *const Recti, point: Vec2i) bool {
} }
pub fn to_rectf(self: *const Recti) Rectf { pub fn to_rectf(self: *const Recti) Rectf {
return Rectf.from_xywh( return Rectf.from_ab(
@intCast(self.x), self.a.to_vec2f(),
@intCast(self.y), self.b.to_vec2f()
@intCast(self.w),
@intCast(self.h),
); );
} }

View File

@ -1,16 +1,53 @@
const Vec2f = @This(); 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, x: f32,
y: f32, y: f32,
pub fn create(x: f32, y: f32) Vec2f { pub fn create(x: f32, y: f32) Vec2f {
return .{ return Vec2f {
.x = x, .x = x,
.y = y, .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 ZERO = create(0, 0);
pub const ONE = create(1, 1); pub const ONE = create(1, 1);
pub const NORTH = create(0, -1); pub const NORTH = create(0, -1);

View File

@ -1,6 +1,15 @@
const Vec2i = @This(); const Vec2i = @This();
const Recti = @import("Recti.zig"); 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, x: i32,
y: 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); 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 ZERO = create(0, 0);
pub const ONE = create(1, 1); pub const ONE = create(1, 1);
pub const NORTH = create(0, -1); pub const NORTH = create(0, -1);