mouse stuff, mostly
parent
07fb08b35e
commit
7a54564597
|
|
@ -1,11 +1,11 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Recti = @import("geometry/Recti.zig");
|
|
||||||
const Matrix4f = @import("geometry/Matrix4f.zig");
|
|
||||||
const Layer = @import("Layer.zig");
|
|
||||||
const Color = @import("Color.zig");
|
|
||||||
const Scene = @import("Scene.zig");
|
|
||||||
const Vec2i = @import("geometry/Vec2i.zig");
|
|
||||||
const c = @import("c");
|
const c = @import("c");
|
||||||
|
const engine = @import("engine");
|
||||||
|
const Vec2i = engine.geometry.Vec2i;
|
||||||
|
const Scene = engine.Scene;
|
||||||
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const MouseEvent = engine.MouseListener.Event;
|
||||||
|
|
||||||
const SDL_GetError = c.SDL_GetError;
|
const SDL_GetError = c.SDL_GetError;
|
||||||
const SDL_Log = c.SDL_Log;
|
const SDL_Log = c.SDL_Log;
|
||||||
|
|
@ -34,13 +34,17 @@ pub var sdl_data: SDL_Data = .{
|
||||||
.window = undefined,
|
.window = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
var hovered: bool = true;
|
var gpa = std.heap.GeneralPurposeAllocator(.{});
|
||||||
var window_width: i32 = 0;
|
var allocator = gpa.allocator();
|
||||||
var window_height: i32 = 0;
|
|
||||||
pub var mouse_pos: ?Vec2i = null;
|
|
||||||
|
|
||||||
|
pub var window_width: i32 = 800;
|
||||||
|
pub var window_height: i32 = 600;
|
||||||
|
|
||||||
|
const SceneLoadFunction = *const fn (*Scene) Allocator.Error!void;
|
||||||
|
var scene_arena = ArenaAllocator.init(std.heap.page_allocator);
|
||||||
var current_scene: ?Scene = null;
|
var current_scene: ?Scene = null;
|
||||||
var next_scene: ?Scene = null;
|
var next_scene_fn: ?SceneLoadFunction = null;
|
||||||
|
|
||||||
var running: bool = false;
|
var running: bool = false;
|
||||||
|
|
||||||
const LoaderFn = *const fn (*c.SDL_Window, *c.SDL_Renderer) void;
|
const LoaderFn = *const fn (*c.SDL_Window, *c.SDL_Renderer) void;
|
||||||
|
|
@ -54,7 +58,7 @@ pub fn setup() !void {
|
||||||
}
|
}
|
||||||
var temp_window: ?*c.SDL_Window = null;
|
var temp_window: ?*c.SDL_Window = null;
|
||||||
var temp_renderer: ?*c.SDL_Renderer = null;
|
var temp_renderer: ?*c.SDL_Renderer = null;
|
||||||
if (!c.SDL_CreateWindowAndRenderer("SDL3", 800, 600, 0, &temp_window, &temp_renderer)) {
|
if (!c.SDL_CreateWindowAndRenderer("SDL3", window_width, window_height, 0, &temp_window, &temp_renderer)) {
|
||||||
std.debug.panic("SDL failed to initialize window or renderer with error: {s}", .{ SDL_GetError() });
|
std.debug.panic("SDL failed to initialize window or renderer with error: {s}", .{ SDL_GetError() });
|
||||||
}
|
}
|
||||||
sdl_data.window = temp_window.?;
|
sdl_data.window = temp_window.?;
|
||||||
|
|
@ -76,34 +80,48 @@ pub fn destroy() void {
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_scene(scene: Scene) void {
|
pub fn set_scene(scene_fn: SceneLoadFunction) !void {
|
||||||
next_scene = scene;
|
next_scene_fn = scene_fn;
|
||||||
|
if(!running) try switch_scene();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn switch_scene() !void {
|
||||||
|
if (next_scene_fn == null) return error.NoNextScene;
|
||||||
|
if (current_scene != null) {
|
||||||
|
current_scene.?.destroy(scene_arena.allocator());
|
||||||
|
// bool successful, always true when free all...
|
||||||
|
_ = scene_arena.reset(.free_all);
|
||||||
|
current_scene = null;
|
||||||
|
}
|
||||||
|
const create_scene = next_scene_fn.?;
|
||||||
|
current_scene = try Scene.init(scene_arena.allocator());
|
||||||
|
try create_scene(¤t_scene.?);
|
||||||
|
try current_scene.?.start();
|
||||||
|
|
||||||
|
next_scene_fn = null;
|
||||||
|
// TODO give the scene the events it needs to contextualize it
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn run() !void {
|
pub fn run() !void {
|
||||||
if (!sdl_data.ready) return error.WindowOrRendererNotInitialized;
|
if (!sdl_data.ready) return error.WindowOrRendererNotInitialized;
|
||||||
if (next_scene == null) return error.NoScene;
|
if (current_scene == null) return error.NoScene;
|
||||||
running = true;
|
running = true;
|
||||||
|
|
||||||
var event: SDL_Event = undefined;
|
var event: SDL_Event = undefined;
|
||||||
while (running) {
|
while (running) {
|
||||||
_ = c.SDL_SetRenderDrawColor(sdl_data.renderer, 10, 10, 10, 255);
|
_ = c.SDL_SetRenderDrawColor(sdl_data.renderer, 10, 10, 10, 255);
|
||||||
_ = c.SDL_RenderClear(sdl_data.renderer);
|
_ = c.SDL_RenderClear(sdl_data.renderer);
|
||||||
|
|
||||||
|
if(next_scene_fn != null) try switch_scene();
|
||||||
|
|
||||||
if (next_scene != null) {
|
engine.Debug.Display.reset();
|
||||||
if (current_scene != null) current_scene.?.destroy();
|
|
||||||
current_scene = next_scene;
|
|
||||||
next_scene = null;
|
|
||||||
// start our scene
|
|
||||||
try current_scene.?.start();
|
|
||||||
// and send it the appropriate resize.
|
|
||||||
current_scene.?.resize(window_width, window_height);
|
|
||||||
if (hovered) current_scene.?.mouse_enter();
|
|
||||||
if (mouse_pos) |pos| current_scene.?.mouse_move(pos.x, pos.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
current_scene.?.draw();
|
current_scene.?.draw();
|
||||||
|
|
||||||
|
|
||||||
|
engine.Debug.Display.render(sdl_data.renderer);
|
||||||
_ = c.SDL_RenderPresent(sdl_data.renderer);
|
_ = c.SDL_RenderPresent(sdl_data.renderer);
|
||||||
|
|
||||||
while(c.SDL_PollEvent(&event)) {
|
while(c.SDL_PollEvent(&event)) {
|
||||||
|
|
@ -111,17 +129,18 @@ pub fn run() !void {
|
||||||
c.SDL_EVENT_QUIT => running = false, // ignore close requested, unless doing autosaves.
|
c.SDL_EVENT_QUIT => running = false, // ignore close requested, unless doing autosaves.
|
||||||
c.SDL_EVENT_WINDOW_CLOSE_REQUESTED => {},
|
c.SDL_EVENT_WINDOW_CLOSE_REQUESTED => {},
|
||||||
|
|
||||||
c.SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED => set_window_size(event.window.data1, event.window.data2),
|
c.SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED => window_resized(event.window.data1, event.window.data2),
|
||||||
c.SDL_EVENT_WINDOW_RESIZED => {}, // ignore resized bc it doesnt happen on startup.
|
c.SDL_EVENT_WINDOW_RESIZED => {}, // ignore resized bc it doesnt happen on startup.
|
||||||
c.SDL_EVENT_WINDOW_SAFE_AREA_CHANGED => {}, // same with safe area, might have
|
c.SDL_EVENT_WINDOW_SAFE_AREA_CHANGED => {}, // same with safe area, might have
|
||||||
|
|
||||||
c.SDL_EVENT_WINDOW_MOVED => {},
|
c.SDL_EVENT_WINDOW_MOVED => {},
|
||||||
c.SDL_EVENT_WINDOW_MOUSE_ENTER => mouse_enter(),
|
c.SDL_EVENT_WINDOW_MOUSE_ENTER => {},
|
||||||
c.SDL_EVENT_WINDOW_MOUSE_LEAVE => mouse_leave(),
|
c.SDL_EVENT_WINDOW_MOUSE_LEAVE => mouse_leave(), // only triggers when no buttons are held.
|
||||||
|
|
||||||
c.SDL_EVENT_MOUSE_MOTION => mouse_move(event.motion.x, event.motion.y),
|
c.SDL_EVENT_MOUSE_MOTION => mouse_move(event.motion.x, event.motion.y),
|
||||||
c.SDL_EVENT_MOUSE_BUTTON_DOWN => mouse_button_down(event.button.button),
|
c.SDL_EVENT_MOUSE_BUTTON_DOWN => mouse_button_down(event.button.button, event.motion.x, event.motion.y),
|
||||||
c.SDL_EVENT_MOUSE_BUTTON_UP => mouse_button_up(event.button.button),
|
c.SDL_EVENT_MOUSE_BUTTON_UP => mouse_button_up(event.button.button, event.motion.x, event.motion.y),
|
||||||
|
c.SDL_EVENT_MOUSE_WHEEL => {},
|
||||||
|
|
||||||
c.SDL_EVENT_KEY_DOWN => {},
|
c.SDL_EVENT_KEY_DOWN => {},
|
||||||
c.SDL_EVENT_KEY_UP => {},
|
c.SDL_EVENT_KEY_UP => {},
|
||||||
|
|
@ -139,38 +158,34 @@ pub fn run() !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_window_size(width: i32, height: i32) void {
|
fn window_resized(width: i32, height: i32) void {
|
||||||
window_width = width;
|
window_width = width;
|
||||||
window_height = height;
|
window_height = height;
|
||||||
std.debug.print("[Engine:set_window_size] {d} x {d}\n", .{ width, height });
|
// if (current_scene != null) current_scene.?.resize(width, height);
|
||||||
if (current_scene != null) current_scene.?.resize(width, height);
|
}
|
||||||
|
|
||||||
|
fn mouse_leave() void {
|
||||||
|
if (current_scene == null) return;
|
||||||
|
current_scene.?.mouse_leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_move(fx: f32, fy: f32) void {
|
fn mouse_move(fx: f32, fy: f32) void {
|
||||||
const x: i32 = @intFromFloat(std.math.round(fx));
|
const x: i32 = @intFromFloat(std.math.round(fx));
|
||||||
const y: i32 = @intFromFloat(std.math.round(fy));
|
const y: i32 = @intFromFloat(std.math.round(fy));
|
||||||
mouse_pos = Vec2i { .x = x, .y = y };
|
|
||||||
|
|
||||||
if (current_scene != null and hovered) {
|
|
||||||
current_scene.?.mouse_move(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_enter() void {
|
|
||||||
hovered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_leave() void {
|
|
||||||
hovered = false;
|
|
||||||
mouse_pos = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_button_down(button: i32) void {
|
|
||||||
if (current_scene == null) return;
|
if (current_scene == null) return;
|
||||||
current_scene.?.mouse_down(button);
|
current_scene.?.mouse_move(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_button_up(button: i32) void {
|
fn mouse_button_down(button: c_int, fx: f32, fy: f32) void {
|
||||||
|
const x: i32 = @intFromFloat(std.math.round(fx));
|
||||||
|
const y: i32 = @intFromFloat(std.math.round(fy));
|
||||||
if (current_scene == null) return;
|
if (current_scene == null) return;
|
||||||
current_scene.?.mouse_up(button);
|
current_scene.?.mouse_down(button, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_button_up(button: c_int, fx: f32, fy: f32) void {
|
||||||
|
const x: i32 = @intFromFloat(std.math.round(fx));
|
||||||
|
const y: i32 = @intFromFloat(std.math.round(fy));
|
||||||
|
if (current_scene == null) return;
|
||||||
|
current_scene.?.mouse_up(button, x, y);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const c = @import("c");
|
||||||
|
const engine = @import("engine");
|
||||||
|
const Control = engine.Control;
|
||||||
|
|
||||||
|
pub const camera = true;
|
||||||
|
pub const mouse = true;
|
||||||
|
|
||||||
|
// later make this expandable in some way using an allocator.
|
||||||
|
|
||||||
|
pub const Display = struct {
|
||||||
|
var debug_buffer: [10][200]u8 = undefined;
|
||||||
|
|
||||||
|
pub fn reset() void {
|
||||||
|
inline for (0..debug_buffer.len) |idx| {
|
||||||
|
debug_buffer[idx][0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty_line() !usize {
|
||||||
|
for (0..debug_buffer.len) |idx| {
|
||||||
|
if (debug_buffer[idx][0] == 0) {
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error.DebugBufferOverflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_line(comptime fmt: []const u8, args: anytype) void {
|
||||||
|
const line_idx: usize = empty_line()
|
||||||
|
catch @panic("debug buffer overflow (too many lines)");
|
||||||
|
_ = std.fmt.bufPrintZ(&debug_buffer[line_idx], fmt, args)
|
||||||
|
catch @panic("debug buffer overflow (line too long)");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(renderer: *c.SDL_Renderer) void {
|
||||||
|
_ = c.SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
||||||
|
const x = 10;
|
||||||
|
var y: f32 = 10;
|
||||||
|
const line_height = 10;
|
||||||
|
inline for (0..debug_buffer.len) |idx| {
|
||||||
|
_ = c.SDL_RenderDebugText(renderer, x, y, &debug_buffer[idx]);
|
||||||
|
y = y + line_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -7,191 +7,40 @@ const Scene = engine.Scene;
|
||||||
const Recti = engine.geometry.Recti;
|
const Recti = engine.geometry.Recti;
|
||||||
const Layer = engine.Layer;
|
const Layer = engine.Layer;
|
||||||
|
|
||||||
const EntityOptions = struct {
|
pub const Options = struct {
|
||||||
tag: Tag = Tag.NONE,
|
tag: Tag = Tag.NONE,
|
||||||
layer: Layer,
|
};
|
||||||
|
|
||||||
|
const VTable = struct {
|
||||||
|
_start: *const fn(*anyopaque, *Scene) void,
|
||||||
|
_update: *const fn(*anyopaque, f32) void,
|
||||||
|
_draw: *const fn(*const anyopaque, layer: Layer) void,
|
||||||
|
_destroy: *const fn(*const anyopaque) void,
|
||||||
};
|
};
|
||||||
|
|
||||||
ptr: *anyopaque,
|
ptr: *anyopaque,
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
layer: Layer,
|
name: []const u8,
|
||||||
|
vtable: VTable,
|
||||||
|
|
||||||
_name: *const fn(*const anyopaque) []const u8,
|
pub fn from(ptr: anytype, options: Options) Entity {
|
||||||
_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,
|
|
||||||
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
_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,
|
|
||||||
MIXED,
|
|
||||||
FALSE,
|
|
||||||
EMPTY,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn bool_array_state(arr: []const bool) ArrayTruthState {
|
|
||||||
var any = false;
|
|
||||||
var all = true;
|
|
||||||
for (arr) |item| {
|
|
||||||
any = any or item;
|
|
||||||
all = all and item;
|
|
||||||
}
|
|
||||||
if (any and all) return .TRUE;
|
|
||||||
if (any and !all) return .MIXED;
|
|
||||||
if (!any and all) return .EMPTY;
|
|
||||||
if (!any and !all) return .FALSE;
|
|
||||||
return .FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
// });
|
|
||||||
const MutablePointer = @TypeOf(ptr);
|
const MutablePointer = @TypeOf(ptr);
|
||||||
// @compileLog("Compiling Entity type for " ++ @typeName(MutablePointer));
|
|
||||||
|
|
||||||
const type_info = @typeInfo(MutablePointer);
|
const type_info = @typeInfo(MutablePointer);
|
||||||
|
|
||||||
// ensure the passed type is a mutable pointer
|
// ensure the passed type is a mutable pointer
|
||||||
if(type_info != .Pointer) @compileError("Entity implementation must be a pointer");
|
if(type_info != .Pointer) @compileError("Entity implementation must be a pointer");
|
||||||
// to a single thing
|
// to a single thing
|
||||||
if(type_info.Pointer.size != .One) @compileError("Entity pointer must be a single item pointer");
|
if(type_info.Pointer.size != .One) @compileError("Entity pointer must be a single item pointer");
|
||||||
// where that thing is a struct or opaque
|
// where that thing is a struct
|
||||||
switch (@typeInfo(type_info.Pointer.child)) {
|
if (@typeInfo(type_info.Pointer.child) != .Struct) @compileError("Entity pointer must point to a struct");
|
||||||
.Struct => {},
|
|
||||||
else => @compileError("Entity pointer must point to a struct or opaque type"),
|
|
||||||
}
|
|
||||||
|
|
||||||
comptime var input_ready = ArrayTruthState.FALSE;
|
const ConstPointer = *const type_info.Pointer.child;
|
||||||
// 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 define all methods required for input focus"),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @compileLog(MutablePointer);
|
|
||||||
// @compileLog(input_ready);
|
|
||||||
|
|
||||||
// also take the const pointer type, as we will use both mutable an immutable pointers.
|
|
||||||
// some entities mutate and some do not
|
|
||||||
const ConstPointer = *const @typeInfo(MutablePointer).Pointer.child;
|
|
||||||
// @compileLog("const pointer type: " ++ @typeName(ConstPointer));
|
|
||||||
|
|
||||||
const gen = struct {
|
const gen = struct {
|
||||||
// mouse functions
|
|
||||||
pub fn _mouse_ready(_: *const anyopaque) bool {
|
|
||||||
return input_ready != ArrayTruthState.FALSE;
|
|
||||||
}
|
|
||||||
pub fn _mouse_enabled(pointer: *const anyopaque) bool {
|
|
||||||
const self: ConstPointer = @ptrCast(@alignCast(pointer));
|
|
||||||
if (@hasDecl(type_info.Pointer.child, "mouse_enabled")) {
|
|
||||||
type_info.Pointer.child.mouse_enabled(self);
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn _mouse_layer(pointer: *const anyopaque) Layer {
|
|
||||||
const self: ConstPointer = @ptrCast(@alignCast(pointer));
|
|
||||||
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));
|
|
||||||
if (@hasDecl(type_info.Pointer.child, "mouse_move")) {
|
|
||||||
type_info.Pointer.child.mouse_move(self, x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn _mouse_in(pointer: *anyopaque) void {
|
|
||||||
const self: MutablePointer = @ptrCast(@alignCast(pointer));
|
|
||||||
if (@hasDecl(type_info.Pointer.child, "mouse_in")) {
|
|
||||||
type_info.Pointer.child.mouse_in(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn _mouse_out(pointer: *anyopaque) void {
|
|
||||||
const self: MutablePointer = @ptrCast(@alignCast(pointer));
|
|
||||||
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 @typeName(type_info.Pointer.child);
|
|
||||||
}
|
|
||||||
pub fn _destroy(pointer: *const anyopaque) void {
|
|
||||||
const self: ConstPointer = @ptrCast(@alignCast(pointer));
|
|
||||||
type_info.Pointer.child.destroy(self);
|
|
||||||
}
|
|
||||||
pub fn _start(pointer: *anyopaque, scene: *Scene) void {
|
pub fn _start(pointer: *anyopaque, scene: *Scene) void {
|
||||||
const self: MutablePointer = @ptrCast(@alignCast(pointer));
|
const self: MutablePointer = @ptrCast(@alignCast(pointer));
|
||||||
if(@hasDecl(type_info.Pointer.child, "start")) {
|
if(@hasDecl(type_info.Pointer.child, "start")) {
|
||||||
const return_type = @typeInfo(@typeInfo(@TypeOf(type_info.Pointer.child.start)).Fn.return_type.?);
|
type_info.Pointer.child.start(self, scene);
|
||||||
// @compileLog(return_type);
|
|
||||||
switch (return_type) {
|
|
||||||
.Void => type_info.Pointer.child.start(self, scene),
|
|
||||||
.ErrorUnion => {
|
|
||||||
type_info.Pointer.child.start(self, scene) catch @panic("fuck");
|
|
||||||
},
|
|
||||||
else => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn _resize(pointer: *anyopaque, width: i32, height: i32) void {
|
|
||||||
const self: MutablePointer = @ptrCast(@alignCast(pointer));
|
|
||||||
if(@hasDecl(type_info.Pointer.child, "resize")) {
|
|
||||||
type_info.Pointer.child.resize(self, width, height);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn _update(pointer: *anyopaque, dt: f32) void {
|
pub fn _update(pointer: *anyopaque, dt: f32) void {
|
||||||
|
|
@ -200,16 +49,16 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity {
|
||||||
type_info.Pointer.child.update(self, dt);
|
type_info.Pointer.child.update(self, dt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn _draw(pointer: *const anyopaque) void {
|
pub fn _draw(pointer: *const anyopaque, layer: Layer) void {
|
||||||
const self: ConstPointer = @ptrCast(@alignCast(pointer));
|
const self: ConstPointer = @ptrCast(@alignCast(pointer));
|
||||||
if(@hasDecl(type_info.Pointer.child, "draw")) {
|
if(@hasDecl(type_info.Pointer.child, "draw")) {
|
||||||
type_info.Pointer.child.draw(self);
|
type_info.Pointer.child.draw(self, layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn _draw_opacity(pointer: *const anyopaque) void {
|
pub fn _destroy(pointer: *const anyopaque) void {
|
||||||
const self: ConstPointer = @ptrCast(@alignCast(pointer));
|
const self: ConstPointer = @ptrCast(@alignCast(pointer));
|
||||||
if(@hasDecl(type_info.Pointer.child, "draw_opacity")) {
|
if(@hasDecl(type_info.Pointer.child, "destroy")) {
|
||||||
type_info.Pointer.child.draw_opacity(self);
|
type_info.Pointer.child.destroy(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -217,101 +66,28 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity {
|
||||||
return Entity {
|
return Entity {
|
||||||
.ptr = ptr,
|
.ptr = ptr,
|
||||||
.tag = options.tag,
|
.tag = options.tag,
|
||||||
.layer = options.layer,
|
.name = @typeName(type_info.Pointer.child),
|
||||||
._name = gen._name,
|
.vtable = VTable {
|
||||||
._destroy = gen._destroy,
|
._destroy = gen._destroy,
|
||||||
._start = gen._start,
|
._start = gen._start,
|
||||||
._resize = gen._resize,
|
._update = gen._update,
|
||||||
._update = gen._update,
|
._draw = gen._draw,
|
||||||
._draw = gen._draw,
|
}
|
||||||
._draw_opacity = gen._draw_opacity,
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
._mouse_in = gen._mouse_in,
|
|
||||||
._mouse_out = gen._mouse_out,
|
|
||||||
._mouse_down = gen._mouse_down,
|
|
||||||
._mouse_up = gen._mouse_up,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn start(self: *const Entity, scene: *Scene) void {
|
||||||
|
self.vtable._start(self.ptr, scene);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update(self: *const Entity, dt: f32) void {
|
pub fn update(self: *const Entity, dt: f32) void {
|
||||||
self._update(self.ptr, dt);
|
self.vtable._update(self.ptr, dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *const Entity) void {
|
pub fn draw(self: *const Entity, layer: Layer) void {
|
||||||
self._draw(self.ptr);
|
self.vtable._draw(self.ptr, layer);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_opacity(self: *const Entity) void {
|
|
||||||
self._draw_opacity(self.ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *const Entity) void {
|
pub fn destroy(self: *const Entity) void {
|
||||||
self._destroy(self.ptr);
|
self.vtable._destroy(self.ptr);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(self: *const Entity, scene: *Scene) void {
|
|
||||||
self._start(self.ptr, scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(self: *const Entity, width: i32, height: i32) void {
|
|
||||||
self._resize(self.ptr, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(self: *const Entity) []const u8 {
|
|
||||||
return self._name(self.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -=- mouse related items
|
|
||||||
pub fn mouse_ready(self: *const Entity) bool {
|
|
||||||
return self._mouse_ready(self.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mouse_enabled(self: *const Entity) bool {
|
|
||||||
return self._mouse_enabled(self.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mouse_layer(self: *const Entity) Layer {
|
|
||||||
return self._mouse_layer(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| {
|
|
||||||
ptr[idx] = default;
|
|
||||||
}
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn free_array(ptr: anytype) void {
|
|
||||||
std.heap.page_allocator.free(ptr);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
const Layer = @This();
|
|
||||||
|
|
||||||
z_index: i32,
|
|
||||||
|
|
||||||
fn layer(z_index: i32) Layer {
|
|
||||||
return .{
|
|
||||||
.z_index = z_index
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const HIDDEN = layer(-1);
|
|
||||||
pub const FLOOR = layer(0);
|
|
||||||
pub const ENTITIES = layer(1);
|
|
||||||
pub const CAMERA = layer(2);
|
|
||||||
pub const SELECTION = layer(3);
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
const MouseListener = @This();
|
||||||
|
|
||||||
|
const engine = @import("engine");
|
||||||
|
const std = @import("std");
|
||||||
|
const c = @import("c");
|
||||||
|
|
||||||
|
const Scene = engine.Scene;
|
||||||
|
const Recti = engine.geometry.Recti;
|
||||||
|
const Layer = engine.Layer;
|
||||||
|
const Vec2i = engine.geometry.Vec2i;
|
||||||
|
|
||||||
|
pub const Button = enum(c_int) {
|
||||||
|
Left = @intCast(c.SDL_BUTTON_LEFT),
|
||||||
|
Right = @intCast(c.SDL_BUTTON_RIGHT),
|
||||||
|
Middle = @intCast(c.SDL_BUTTON_MIDDLE),
|
||||||
|
|
||||||
|
pub fn count() comptime_int {
|
||||||
|
return engine.enum_to_array(Button).len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_idx(button: Button) usize {
|
||||||
|
return switch(button) {
|
||||||
|
.Left => 0,
|
||||||
|
.Right => 1,
|
||||||
|
.Middle => 2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_idx(idx: usize) Button {
|
||||||
|
return switch(idx) {
|
||||||
|
0 => .Left,
|
||||||
|
1 => .Right,
|
||||||
|
2 => .Middle,
|
||||||
|
else => std.debug.panic("Cannot convert {d} to Button", .{ idx }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Properties = struct {
|
||||||
|
area: Recti,
|
||||||
|
layer: Layer,
|
||||||
|
button: Button,
|
||||||
|
click_through: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Event = union(enum) {
|
||||||
|
MouseIn: struct { pos: Vec2i },
|
||||||
|
MouseOut: void,
|
||||||
|
MouseMove: struct { pos: Vec2i },
|
||||||
|
MouseDown: struct { button: Button, pos: Vec2i },
|
||||||
|
MouseUp: struct { button: Button, pos: Vec2i },
|
||||||
|
};
|
||||||
|
|
||||||
|
const VTable = struct {
|
||||||
|
_mouse_properties: *const fn(*const anyopaque) Properties,
|
||||||
|
_mouse_event: *const fn(*anyopaque, Event) void,
|
||||||
|
_name: *const fn() []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
ptr: *anyopaque,
|
||||||
|
vtable: VTable,
|
||||||
|
|
||||||
|
pub fn is_capable(T: type) bool {
|
||||||
|
const type_info = @typeInfo(T);
|
||||||
|
if (type_info != .Pointer) return false;
|
||||||
|
if (type_info.Pointer.size != .One) return false;
|
||||||
|
if (type_info.Pointer.is_const) return false;
|
||||||
|
if (type_info.Pointer.is_allowzero) return false;
|
||||||
|
if (!@hasDecl(type_info.Pointer.child, "mouse_properties")) return false;
|
||||||
|
if (!@hasDecl(type_info.Pointer.child, "mouse_event")) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from(impl: anytype) MouseListener {
|
||||||
|
if (!comptime is_capable(@TypeOf(impl)))
|
||||||
|
@compileError(@typeName(@TypeOf(impl)) ++ " could not be made a MouseListener");
|
||||||
|
|
||||||
|
const StructType = @typeInfo(@TypeOf(impl)).Pointer.child;
|
||||||
|
const MutablePointer = *StructType;
|
||||||
|
const ConstPointer = *const StructType;
|
||||||
|
|
||||||
|
const gen = struct {
|
||||||
|
pub fn _mouse_properties(ptr: *const anyopaque) Properties {
|
||||||
|
const self: ConstPointer = @ptrCast(@alignCast(ptr));
|
||||||
|
return StructType.mouse_properties(self);
|
||||||
|
}
|
||||||
|
pub fn _mouse_event(ptr: *anyopaque, event: Event) void {
|
||||||
|
const self: MutablePointer = @ptrCast(@alignCast(ptr));
|
||||||
|
return StructType.mouse_event(self, event);
|
||||||
|
}
|
||||||
|
pub fn _name() []const u8 {
|
||||||
|
return @typeName(StructType);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return MouseListener {
|
||||||
|
.ptr = impl,
|
||||||
|
.vtable = VTable {
|
||||||
|
._mouse_properties = gen._mouse_properties,
|
||||||
|
._mouse_event = gen._mouse_event,
|
||||||
|
._name = gen._name,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mouse_event(self: MouseListener, event: Event) void {
|
||||||
|
self.vtable._mouse_event(self.ptr, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mouse_properties(self: MouseListener) Properties {
|
||||||
|
return self.vtable._mouse_properties(self.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(self: MouseListener) []const u8 {
|
||||||
|
return self.vtable._name();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mouse_in(self: MouseListener, pos: Vec2i) void {
|
||||||
|
self.vtable._mouse_event(self.ptr, Event {
|
||||||
|
.MouseIn = .{
|
||||||
|
.pos = pos
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mouse_out(self: MouseListener) void {
|
||||||
|
self.vtable._mouse_event(self.ptr, Event {
|
||||||
|
.MouseOut = undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asc(_: void, a: MouseListener, b: MouseListener) bool {
|
||||||
|
return @intFromEnum(a.mouse_properties().layer) < @intFromEnum(b.mouse_properties().layer);
|
||||||
|
}
|
||||||
|
|
@ -1,44 +1,79 @@
|
||||||
const Scene = @This();
|
const Scene = @This();
|
||||||
|
|
||||||
const Recti = @import("geometry/Recti.zig");
|
|
||||||
const Layer = @import("Layer.zig");
|
|
||||||
const Color = @import("Color.zig");
|
|
||||||
const engine = @import("engine");
|
const engine = @import("engine");
|
||||||
const Entity = engine.Entity;
|
|
||||||
const Vec2i = @import("geometry/Vec2i.zig");
|
|
||||||
const Tag = @import("Tag.zig");
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const c = @import("c");
|
const c = @import("c");
|
||||||
const ArrayList = std.ArrayList;
|
const ArrayListUnmanaged = std.ArrayListUnmanaged;
|
||||||
const AutoHashMap = std.AutoHashMap;
|
const AutoHashMapUnmanaged = std.AutoHashMapUnmanaged;
|
||||||
|
const Entity = engine.Entity;
|
||||||
|
const Tag = engine.Tag;
|
||||||
|
const Vec2i = engine.geometry.Vec2i;
|
||||||
|
const MouseListener = engine.MouseListener;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Color = engine.Color;
|
||||||
|
|
||||||
// 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.
|
||||||
mouse_ready_entities: ArrayList(Entity),
|
mouse_listeners: ArrayListUnmanaged(MouseListener) = undefined,
|
||||||
entities: ArrayList(Entity),
|
entities: ArrayListUnmanaged(Entity) = undefined,
|
||||||
entities_to_add: ArrayList(Entity),
|
entities_to_add: ArrayListUnmanaged(Entity) = undefined,
|
||||||
tagged_entities: AutoHashMap(Tag.ID, Entity),
|
tagged_entities: AutoHashMapUnmanaged(engine.Tag, Entity) = undefined,
|
||||||
mouse_inside: bool = false,
|
started: bool = false,
|
||||||
current_hover_item: ?Entity = null,
|
arena: *std.heap.ArenaAllocator,
|
||||||
|
active_mouse_listeners: [MouseListener.Button.count()] ?MouseListener = blk: {
|
||||||
|
const len = MouseListener.Button.count();
|
||||||
|
var arr: [len]?MouseListener = undefined;
|
||||||
|
for (0..len) |idx| {
|
||||||
|
arr[idx] = null;
|
||||||
|
}
|
||||||
|
break :blk arr;
|
||||||
|
},
|
||||||
|
mouse_listener_locks: [MouseListener.Button.count()]bool = blk: {
|
||||||
|
var arr: [MouseListener.Button.count()]bool = undefined;
|
||||||
|
for(0..arr.len) |idx| arr[idx] = false;
|
||||||
|
break :blk arr;
|
||||||
|
},
|
||||||
mouse_pos: ?Vec2i = null,
|
mouse_pos: ?Vec2i = null,
|
||||||
|
|
||||||
started: bool = false,
|
pub fn init(allocator: Allocator) !Scene {
|
||||||
|
// const self: = allocator.create(Scene);
|
||||||
pub fn create() Scene {
|
const arena = try allocator.create(std.heap.ArenaAllocator);
|
||||||
const self = Scene {
|
arena.* = std.heap.ArenaAllocator.init(allocator);
|
||||||
.entities = ArrayList(Entity).init(std.heap.page_allocator),
|
|
||||||
.entities_to_add = ArrayList(Entity).init(std.heap.page_allocator),
|
// var self = Scene { .arena = std.heap.ArenaAllocator.init(std.heap.page_allocator) };
|
||||||
.mouse_ready_entities = ArrayList(Entity).init(std.heap.page_allocator),
|
// const allocator = self.arena.allocator();
|
||||||
.tagged_entities = AutoHashMap(Tag.ID, Entity).init(std.heap.page_allocator),
|
//
|
||||||
|
// self.entities = ArrayList(Entity).init(allocator);
|
||||||
|
// self.entities_to_add = ArrayList(Entity).init(allocator);
|
||||||
|
// self.mouse_listeners = ArrayList(MouseListener).init(allocator);
|
||||||
|
// self.tagged_entities = AutoHashMap(Tag, Entity).init(allocator);
|
||||||
|
//
|
||||||
|
return Scene {
|
||||||
|
.arena = arena,
|
||||||
|
.entities = try ArrayListUnmanaged(Entity).initCapacity(arena.allocator(), 10),
|
||||||
|
.entities_to_add = try ArrayListUnmanaged(Entity).initCapacity(arena.allocator(), 10),
|
||||||
|
.mouse_listeners = try ArrayListUnmanaged(MouseListener).initCapacity(arena.allocator(), 10),
|
||||||
|
.tagged_entities = std.AutoHashMapUnmanaged(Tag, Entity) {},
|
||||||
};
|
};
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *Scene) void {
|
// the allocator parameter needs to be the same allocator
|
||||||
|
// that was given on init. this is because the arenaallocator
|
||||||
|
// itself is stored in it. everything else is stored in the
|
||||||
|
// arena, which is getting cleared in one fell swoop.
|
||||||
|
pub fn destroy(self: *Scene, allocator: Allocator) void {
|
||||||
|
// this should effectively be a no-op as most entities
|
||||||
|
// should be allocating with the scene's arena allocator
|
||||||
|
// but in case theyre not, we make sure to call destroy
|
||||||
|
// on them.
|
||||||
for (self.entities.items) |entity| {
|
for (self.entities.items) |entity| {
|
||||||
entity.destroy();
|
entity.destroy();
|
||||||
}
|
}
|
||||||
self.entities.deinit();
|
// first we clear everything in the arena (entity data and
|
||||||
|
// managed caches of entities / Mouse Listeners / etc)
|
||||||
|
self.arena.deinit();
|
||||||
|
// then we free the arena state from the parent allocator.
|
||||||
|
allocator.destroy(self.arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(self: *Scene) !void {
|
pub fn start(self: *Scene) !void {
|
||||||
|
|
@ -48,92 +83,54 @@ pub fn start(self: *Scene) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(self: *const Scene, tag: Tag, comptime T: type) *T {
|
pub fn get(self: *const Scene, tag: Tag, comptime T: type) *T {
|
||||||
if (tag.id == Tag.NONE.id) @panic("Cannot find by Tag.NONE");
|
if (tag == Tag.NONE) @panic("Cannot find by Tag.NONE");
|
||||||
const tag_id = tag.id;
|
const entity: Entity = self.tagged_entities.get(tag) orelse @panic("Could not find entity");
|
||||||
const entity: Entity = self.tagged_entities.get(tag_id) orelse @panic("Could not find entity");
|
|
||||||
const opaque_ptr = entity.ptr;
|
const opaque_ptr = entity.ptr;
|
||||||
const real_ptr: *T = @ptrCast(@alignCast(opaque_ptr));
|
const real_ptr: *T = @ptrCast(@alignCast(opaque_ptr));
|
||||||
return real_ptr;
|
return real_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(self: *Scene, instance_ptr: anytype) !void {
|
pub fn add(self: *Scene, entity: Entity) !void {
|
||||||
const instance_ptr_type = @TypeOf(instance_ptr);
|
|
||||||
const instance_ptr_type_info = @typeInfo(instance_ptr_type);
|
|
||||||
|
|
||||||
if (instance_ptr_type_info != .Pointer)
|
|
||||||
@compileError(
|
|
||||||
"Cannot add to scene type " ++
|
|
||||||
@typeName(instance_ptr_type) ++
|
|
||||||
". Must be a mutable pointer\n"
|
|
||||||
);
|
|
||||||
if (instance_ptr_type_info.Pointer.size != .One)
|
|
||||||
@compileError("Pointer size must be One");
|
|
||||||
if (instance_ptr_type_info.Pointer.is_const)
|
|
||||||
@compileError("Pointer must be mutable");
|
|
||||||
|
|
||||||
const instance_type = instance_ptr_type_info.Pointer.child;
|
|
||||||
|
|
||||||
if (!@hasDecl(instance_type, "entity"))
|
|
||||||
@compileError("Pointer must be to a struct with fn entity() Entity");
|
|
||||||
|
|
||||||
// again, entities are fat pointers. they are okay to be copied
|
|
||||||
const entity: Entity = instance_ptr.entity();
|
|
||||||
std.debug.print("[Scene:add] Queueing {s}\n", .{
|
std.debug.print("[Scene:add] Queueing {s}\n", .{
|
||||||
entity.name()
|
entity.name
|
||||||
});
|
});
|
||||||
try self.entities_to_add.append(entity);
|
try self.entities_to_add.append(self.arena.allocator(), entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_mouse_listener(self: *Scene, mouse_listener: MouseListener) void {
|
||||||
|
self.mouse_listeners.append(self.arena.allocator(), mouse_listener) catch @panic("OOM");
|
||||||
|
std.mem.sort(MouseListener, self.mouse_listeners.items, {}, MouseListener.asc);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ingest_queued_entities(self: *Scene) !void {
|
fn ingest_queued_entities(self: *Scene) !void {
|
||||||
// TODO make this a while loop so entities that add entities add them on the same frame.
|
// TODO make this a while loop so entities that add entities add them on the same frame.
|
||||||
var temp_entities: ArrayList(Entity) = try ArrayList(Entity).initCapacity(std.heap.page_allocator, self.entities_to_add.items.len);
|
var temp_entities = try ArrayListUnmanaged(Entity)
|
||||||
|
.initCapacity(self.arena.allocator(), self.entities_to_add.items.len);
|
||||||
|
|
||||||
for(self.entities_to_add.items) |entity| {
|
for(self.entities_to_add.items) |entity| {
|
||||||
std.debug.print("[Scene:ingest_queued_entities] Adding {s}\n", .{
|
std.debug.print("[Scene:ingest_queued_entities] Adding {s}\n", .{
|
||||||
entity.name()
|
entity.name
|
||||||
});
|
});
|
||||||
try temp_entities.append(entity);
|
try temp_entities.append(self.arena.allocator(), entity);
|
||||||
try self.entities.append(entity);
|
try self.entities.append(self.arena.allocator(), entity);
|
||||||
|
|
||||||
if (entity.tag.id != Tag.NONE.id) {
|
if (entity.tag != Tag.NONE) {
|
||||||
try self.tagged_entities.put(entity.tag.id, entity);
|
try self.tagged_entities.put(self.arena.allocator(), entity.tag, entity);
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.mouse_ready()) {
|
|
||||||
try self.mouse_ready_entities.append(entity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.entities_to_add.clearAndFree();
|
self.entities_to_add.clearAndFree(self.arena.allocator());
|
||||||
|
|
||||||
std.mem.sort(Entity, self.mouse_ready_entities.items, {}, entity_layer_less_than);
|
|
||||||
std.mem.sort(Entity, self.entities.items, {}, entity_draw_layer_less_than);
|
|
||||||
|
|
||||||
|
|
||||||
// std.debug.print("[Scene.ingest_queued_entities] Sorted:\n", .{});
|
|
||||||
// for(self.entities.items) |*entity| {
|
|
||||||
// std.debug.print("[Scene:ingest_queued_entities] {d}: {s}\n", .{ entity.layer.z_index, entity.name() });
|
|
||||||
// }
|
|
||||||
|
|
||||||
std.debug.print("[Scene:ingest_queued_entities] Starting {} entities\n", .{ temp_entities.items.len });
|
std.debug.print("[Scene:ingest_queued_entities] Starting {} entities\n", .{ temp_entities.items.len });
|
||||||
|
|
||||||
for(temp_entities.items) |entity| {
|
for(temp_entities.items) |entity| {
|
||||||
entity.start(self);
|
entity.start(self);
|
||||||
|
|
||||||
if (entity.tag.id == Tag.NONE.id) {
|
if (entity.tag == Tag.NONE) {
|
||||||
std.debug.print("[Scene:ingest_queued_entities] - {s}\n", .{ entity.name() });
|
std.debug.print("[Scene:ingest_queued_entities] - {s}\n", .{ entity.name });
|
||||||
} else {
|
} else {
|
||||||
std.debug.print("[Scene:ingest_queued_entities] - {s} [{s}]\n", .{ entity.name(), entity.tag.type });
|
std.debug.print("[Scene:ingest_queued_entities] - {s} [{s}]\n", .{ entity.name, @tagName(entity.tag) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn entity_draw_layer_less_than(_: void, lhs: Entity, rhs: Entity) bool {
|
|
||||||
return lhs.layer.z_index < rhs.layer.z_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn entity_layer_less_than(_: void, lhs: Entity, rhs: Entity) bool {
|
|
||||||
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 {
|
||||||
|
|
@ -146,113 +143,144 @@ pub fn update(self: *Scene, dt: f32) !void {
|
||||||
entity.update(dt);
|
entity.update(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this whole operation is wildly stupid and could be amde more efficient.
|
self.update_hover_context();
|
||||||
if (self.mouse_pos) |mouse| {
|
|
||||||
var top_hovered: ?Entity = null;
|
|
||||||
|
|
||||||
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_hover_item != null) {
|
|
||||||
// if we're changing hover target from something to something
|
|
||||||
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_hover_item = top_hovered.?;
|
|
||||||
self.current_hover_item.?.mouse_in();
|
|
||||||
}
|
|
||||||
} else if (self.current_hover_item != null) {
|
|
||||||
// here we were hovering something, but now are not.
|
|
||||||
self.current_hover_item.?.mouse_out();
|
|
||||||
self.current_hover_item = null;
|
|
||||||
}
|
|
||||||
} else if (self.current_hover_item != null) {
|
|
||||||
self.current_hover_item.?.mouse_out();
|
|
||||||
self.current_hover_item = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn draw(self: *Scene) void {
|
pub fn draw(self: *Scene) void {
|
||||||
for (self.entities.items) |entity| {
|
for (engine.enum_to_array(engine.Layer)) |layer| {
|
||||||
entity.draw();
|
for (self.entities.items) |entity| {
|
||||||
|
entity.draw(layer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (self.entities.items) |entity| {
|
|
||||||
entity.draw_opacity();
|
// for (self.mouse_listeners.items) |item| {
|
||||||
}
|
// if (self.current_hover_item != null and item.ptr == self.current_hover_item.?.ptr) {
|
||||||
for (self.mouse_ready_entities.items) |item| {
|
// item.mouse_properties().area.draw(Color.LIME.with_opacity(0.3));
|
||||||
if (self.current_hover_item != null and item.ptr == self.current_hover_item.?.ptr) {
|
// } else {
|
||||||
// item.mouse_area().draw(item.mouse_layer(), Color.LIME.with_opacity(0.3));
|
// item.mouse_properties().area.draw(Color.WHITE.with_opacity(0.3));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (engine.Debug.mouse) {
|
||||||
|
if (self.mouse_pos) |mouse| {
|
||||||
|
engine.Debug.Display.add_line("Mouse Pos: ({}, {})", .{ mouse.x, mouse.y });
|
||||||
} else {
|
} else {
|
||||||
// item.mouse_area().draw(item.mouse_layer(), Color.WHITE.with_opacity(0.3));
|
engine.Debug.Display.add_line("Mouse Pos: Unknown", .{});
|
||||||
|
}
|
||||||
|
engine.Debug.Display.add_line("Mouse Listeners: {}", .{ self.active_listener_count() });
|
||||||
|
for(&self.active_mouse_listeners, 0..) |*listener, button_idx| {
|
||||||
|
if(listener.* == null) continue;
|
||||||
|
const button = MouseListener.Button.from_idx(button_idx);
|
||||||
|
engine.Debug.Display.add_line(" {s}: {s}{s}", .{
|
||||||
|
@tagName(button),
|
||||||
|
listener.*.?.name(),
|
||||||
|
if (self.mouse_listener_locks[button_idx]) " (locked)" else ""
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(self: *Scene, width: i32, height: i32) void {
|
fn active_listener_count(self: *Scene) u32 {
|
||||||
for (self.entities.items) |entity| {
|
var count: u32 = 0;
|
||||||
entity.resize(width, height);
|
for(&self.active_mouse_listeners) |*listener| {
|
||||||
|
if(listener.* == null) continue;
|
||||||
|
count = count + 1;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_hovered_mouse_listener_for_button(self: *Scene, button: MouseListener.Button) ?MouseListener {
|
||||||
|
if (self.mouse_pos == null) return null;
|
||||||
|
for (self.mouse_listeners.items) |listener| {
|
||||||
|
const props = listener.mouse_properties();
|
||||||
|
if (!props.area.contains(self.mouse_pos.?)) continue;
|
||||||
|
if (button == props.button) return listener;
|
||||||
|
if (props.click_through) continue;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_hover_context(self: *Scene) void {
|
||||||
|
for (0..self.mouse_listener_locks.len) |button_idx| {
|
||||||
|
// if this mouse listener is locked then forget updating it.
|
||||||
|
if (self.mouse_listener_locks[button_idx]) continue;
|
||||||
|
|
||||||
|
// otherwise, grab the button enum and current / previous hovered
|
||||||
|
const button = MouseListener.Button.from_idx(button_idx);
|
||||||
|
const current_hovered = self.get_hovered_mouse_listener_for_button(button);
|
||||||
|
const previous_hovered = self.active_mouse_listeners[button_idx];
|
||||||
|
|
||||||
|
// if they are equal, we dont care.
|
||||||
|
if (current_hovered == null and previous_hovered == null) continue;
|
||||||
|
if (current_hovered != null and previous_hovered != null
|
||||||
|
and current_hovered.?.ptr == previous_hovered.?.ptr) continue;
|
||||||
|
|
||||||
|
if (previous_hovered) |previous| {
|
||||||
|
previous.mouse_out();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_hovered) |current| {
|
||||||
|
// mouse pos MUST exist here, because get_hovered_listener_for_button
|
||||||
|
// will return null if we have no position.
|
||||||
|
current.mouse_in(self.mouse_pos.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we dont unwrap the optional here to propogate a null value
|
||||||
|
self.active_mouse_listeners[button_idx] = current_hovered;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_move(self: *Scene, x: i32, y: i32) void {
|
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.create(x, y);
|
||||||
self.mouse_pos = Vec2i { .x = x, .y = y };
|
|
||||||
self.mouse_inside = true;
|
// this may need to be made to go to EVERYONE,
|
||||||
for (self.mouse_ready_entities.items) |item| {
|
// but for now its just going to go to the active
|
||||||
item.mouse_move(x, y);
|
// listeners.
|
||||||
|
for (self.active_mouse_listeners) |mouse_listener| {
|
||||||
|
if(mouse_listener) |listener| {
|
||||||
|
listener.mouse_event(MouseListener.Event {
|
||||||
|
.MouseMove = .{
|
||||||
|
.pos = Vec2i.create(x, y),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_enter(self: *Scene) void {
|
pub fn mouse_down(self: *Scene, sdl_button: c_int, x: i32, y: i32) void {
|
||||||
self.mouse_inside = true;
|
if (self.mouse_pos == null) return;
|
||||||
|
const button: MouseListener.Button = @enumFromInt(sdl_button);
|
||||||
|
const index = button.to_idx();
|
||||||
|
self.mouse_listener_locks[index] = true;
|
||||||
|
if (self.active_mouse_listeners[index]) |listener| {
|
||||||
|
listener.mouse_event(MouseListener.Event {
|
||||||
|
.MouseDown = .{
|
||||||
|
.button = @enumFromInt(sdl_button),
|
||||||
|
.pos = Vec2i.create(x, y),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mouse_up(self: *Scene, sdl_button: c_int, x: i32, y: i32) void {
|
||||||
|
if (self.mouse_pos == null) return;
|
||||||
|
const button: MouseListener.Button = @enumFromInt(sdl_button);
|
||||||
|
const index = button.to_idx();
|
||||||
|
self.mouse_listener_locks[index] = false;
|
||||||
|
if (self.active_mouse_listeners[index]) |listener| {
|
||||||
|
listener.mouse_event(MouseListener.Event {
|
||||||
|
.MouseUp = .{
|
||||||
|
.button = @enumFromInt(sdl_button),
|
||||||
|
.pos = Vec2i.create(x, y),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_leave(self: *Scene) void {
|
pub fn mouse_leave(self: *Scene) void {
|
||||||
self.mouse_inside = false;
|
|
||||||
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
|
|
||||||
pub const EntityPool = struct {
|
|
||||||
// an entity can choose to allocate itself however it pleases, but these
|
|
||||||
// are good default functions that just throw things on the heap.
|
|
||||||
pub fn allocate(instance: anytype) !*@TypeOf(instance) {
|
|
||||||
const ptr = try std.heap.page_allocator.create(@TypeOf(instance));
|
|
||||||
ptr.* = instance;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deallocate(instance: anytype) void {
|
|
||||||
std.heap.page_allocator.destroy(instance);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
const Sprite = @This();
|
const Sprite = @This();
|
||||||
|
|
||||||
const Rectf = @import("geometry/Rectf.zig");
|
|
||||||
const Recti = @import("geometry/Recti.zig");
|
|
||||||
const Vec2f = @import("geometry/Vec2f.zig");
|
|
||||||
const Color = @import("Color.zig");
|
|
||||||
const Texture = @import("Texture.zig");
|
|
||||||
const Layer = @import("Layer.zig");
|
|
||||||
const engine = @import("engine");
|
const engine = @import("engine");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const c = @import("c");
|
const c = @import("c");
|
||||||
|
|
||||||
|
const Recti = engine.geometry.Recti;
|
||||||
|
const Rectf = engine.geometry.Rectf;
|
||||||
|
const Vec2f = engine.geometry.Vec2f;
|
||||||
|
const Layer = engine.Layer;
|
||||||
|
const Color = engine.Color;
|
||||||
|
const Texture = engine.Texture;
|
||||||
|
|
||||||
texture_area: Recti = undefined,
|
texture_area: Recti = undefined,
|
||||||
texture: *const Texture = undefined,
|
texture: *const Texture = undefined,
|
||||||
|
|
||||||
|
|
@ -27,10 +28,7 @@ pub fn get_src_uv(self: *const Sprite) Rectf {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *const Sprite, screen_pos: Recti, layer: Layer, color: Color) void {
|
pub fn draw(self: *const Sprite, screen_pos: Recti, color: Color) void {
|
||||||
// _ = screen_pos;
|
|
||||||
_ = layer;
|
|
||||||
// _ = color;
|
|
||||||
_ = c.SDL_SetRenderDrawColor(engine.Control.sdl_data.renderer, color.u.r, color.u.g, color.u.b, color.u.a);
|
_ = c.SDL_SetRenderDrawColor(engine.Control.sdl_data.renderer, color.u.r, color.u.g, color.u.b, color.u.a);
|
||||||
_ = c.SDL_SetTextureColorModFloat(self.texture.handle, color.f.r, color.f.g, color.f.b);
|
_ = c.SDL_SetTextureColorModFloat(self.texture.handle, color.f.r, color.f.g, color.f.b);
|
||||||
_ = c.SDL_RenderTexture(
|
_ = c.SDL_RenderTexture(
|
||||||
|
|
@ -39,5 +37,4 @@ pub fn draw(self: *const Sprite, screen_pos: Recti, layer: Layer, color: Color)
|
||||||
&self.texture_area.to_sdl(),
|
&self.texture_area.to_sdl(),
|
||||||
&screen_pos.to_sdl()
|
&screen_pos.to_sdl()
|
||||||
);
|
);
|
||||||
// std.debug.print("drew: {}\n", .{ drew });
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
const Tag = @This();
|
|
||||||
const std = @import("std");
|
|
||||||
pub const ID = *const anyopaque;
|
|
||||||
|
|
||||||
id: ID,
|
|
||||||
type: []const u8,
|
|
||||||
|
|
||||||
fn create_id_for_type(comptime T: type) ID {
|
|
||||||
// create a unique struct for each type this is called with.
|
|
||||||
const unique_struct = struct {
|
|
||||||
// this simply gives the struct a size, so the pointer is not
|
|
||||||
// optimized into oblivion.
|
|
||||||
var x: u8 = 0;
|
|
||||||
|
|
||||||
// capture the type so zig knows not to make this
|
|
||||||
// struct the same for each call. if the type isnt captured
|
|
||||||
// zig will create one struct in memory for each unique call.
|
|
||||||
// ie: the function will be unique, but this struct wont be.
|
|
||||||
comptime {
|
|
||||||
_ = T;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// @compileLog(T);
|
|
||||||
// @compileLog(&unique_struct.x);
|
|
||||||
|
|
||||||
// return the pointer to the only data within the struct,
|
|
||||||
// again so the compiler doesnt optimize this into non-existence.
|
|
||||||
return &unique_struct.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const NONE = Tag {
|
|
||||||
.type = "None",
|
|
||||||
.id = create_id_for_type(struct {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn create(comptime t: type) Tag {
|
|
||||||
// @compileLog(@typeName(t));
|
|
||||||
return Tag {
|
|
||||||
.type = @typeName(t),
|
|
||||||
.id = create_id_for_type(t),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn equals(self: *const Tag, other: Tag) bool {
|
|
||||||
return self.id == other.id;
|
|
||||||
}
|
|
||||||
|
|
@ -68,15 +68,12 @@ pub fn scalef(self: *const Recti, scale: Vec2f) Rectf {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *const Recti, layer: Layer, color: Color) void {
|
pub fn draw(self: *const Recti, color: Color) void {
|
||||||
self.draw_filled(layer, color.with_opacity(0.2));
|
self.draw_filled(color.with_opacity(0.2));
|
||||||
self.draw_outline(layer, color);
|
self.draw_outline(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_filled(self: *const Recti, layer: Layer, color: Color) void {
|
pub fn draw_filled(self: *const Recti, color: Color) void {
|
||||||
// _ = self;
|
|
||||||
_ = layer;
|
|
||||||
// _ = color;
|
|
||||||
_ = c.SDL_SetRenderDrawColorFloat(engine.Control.sdl_data.renderer, color.f.r, color.f.g, color.f.b, color.f.a);
|
_ = c.SDL_SetRenderDrawColorFloat(engine.Control.sdl_data.renderer, color.f.r, color.f.g, color.f.b, color.f.a);
|
||||||
_ = c.SDL_RenderFillRect(engine.Control.sdl_data.renderer, &self.to_sdl());
|
_ = c.SDL_RenderFillRect(engine.Control.sdl_data.renderer, &self.to_sdl());
|
||||||
}
|
}
|
||||||
|
|
@ -90,10 +87,7 @@ pub fn to_sdl(self: *const Recti) c.SDL_FRect {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_outline(self: *const Recti, layer: Layer, color: Color) void {
|
pub fn draw_outline(self: *const Recti, color: Color) void {
|
||||||
_ = self;
|
_ = c.SDL_SetRenderDrawColorFloat(engine.Control.sdl_data.renderer, color.f.r, color.f.g, color.f.b, color.f.a);
|
||||||
_ = layer;
|
_ = c.SDL_RenderRect(engine.Control.sdl_data.renderer, &self.to_sdl());
|
||||||
_ = color;
|
|
||||||
// _ = c.SDL_SetRenderDrawColorFloat(engine.Control.sdl_data.renderer, color.f.r, color.f.g, color.f.b, color.f.a);
|
|
||||||
// _ = c.SDL_RenderRect(engine.Control.sdl_data.renderer, &self.to_sdl());
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
const Vec2f = @This();
|
const Vec2f = @This();
|
||||||
const Vec2i = @import("Vec2i.zig");
|
|
||||||
const Layer = @import("../Layer.zig");
|
const engine = @import("engine");
|
||||||
const Color = @import("../Color.zig");
|
const c = @import("c");
|
||||||
|
const Vec2i = engine.geometry.Vec2i;
|
||||||
|
const Layer = engine.Layer;
|
||||||
|
const Color = engine.Color;
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
x: f32,
|
x: f32,
|
||||||
|
|
@ -21,11 +24,8 @@ pub fn to_vec2i(self: *const Vec2f) Vec2i {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *const Vec2f, radius: f32, layer: Layer, color: Color) void {
|
pub fn draw(self: *const Vec2f, comptime radius: f32, color: Color) void {
|
||||||
_ = self;
|
self.to_vec2i().draw(radius, color);
|
||||||
_ = radius;
|
|
||||||
_ = layer;
|
|
||||||
_ = color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ZERO = create(0, 0);
|
pub const ZERO = create(0, 0);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
const Vec2i = @This();
|
const Vec2i = @This();
|
||||||
|
|
||||||
const Recti = @import("Recti.zig");
|
|
||||||
const Vec2f = @import("Vec2f.zig");
|
|
||||||
const Layer = @import("../Layer.zig");
|
|
||||||
const Color = @import("../Color.zig");
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const engine = @import("engine");
|
||||||
|
const c = @import("c");
|
||||||
|
const Recti = engine.geometry.Recti;
|
||||||
|
const Vec2f = engine.geometry.Vec2f;
|
||||||
|
const Layer = engine.Layer;
|
||||||
|
const Color = engine.Color;
|
||||||
|
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
|
|
@ -34,11 +36,57 @@ pub fn add(self: Vec2i, other: Vec2i) Vec2i {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *const Vec2i, radius: f32, layer: Layer, color: Color) void {
|
fn round_up_to_multiple_of_8(n: i32) i32 {
|
||||||
_ = self;
|
// fkn magic. -8 is all 1s, last 3 bits 0 so its a mask.
|
||||||
_ = radius;
|
return (n + (8 - 1)) & -8;
|
||||||
_ = layer;
|
}
|
||||||
_ = color;
|
|
||||||
|
|
||||||
|
// TODO: comptime the entire array and just render with one offset call to render points.
|
||||||
|
// effectively memoize shit with comptime!!!
|
||||||
|
pub fn draw(self: *const Vec2i, comptime radius: i32, color: Color) void {
|
||||||
|
const cx = self.x;
|
||||||
|
const cy = self.y;
|
||||||
|
|
||||||
|
const fradius: f32 = @floatFromInt(radius);
|
||||||
|
const arr_size: usize = comptime @intCast(
|
||||||
|
round_up_to_multiple_of_8(
|
||||||
|
@intFromFloat(std.math.ceil(
|
||||||
|
fradius * 8.0 * std.math.sqrt1_2
|
||||||
|
))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var points: [arr_size]c.SDL_FPoint = undefined;
|
||||||
|
var draw_count: usize = 0;
|
||||||
|
const diameter = (radius * 2);
|
||||||
|
var x: i32 = (radius - 1);
|
||||||
|
var y: i32 = 0;
|
||||||
|
var tx: i32 = 1;
|
||||||
|
var ty: i32 = 1;
|
||||||
|
var err: i32 = (tx - diameter);
|
||||||
|
while (x >= y) {
|
||||||
|
points[draw_count + 0] = c.SDL_FPoint { .x = @floatFromInt(cx + x), .y = @floatFromInt(cy - y) };
|
||||||
|
points[draw_count + 1] = c.SDL_FPoint { .x = @floatFromInt(cx + x), .y = @floatFromInt(cy + y) };
|
||||||
|
points[draw_count + 2] = c.SDL_FPoint { .x = @floatFromInt(cx - x), .y = @floatFromInt(cy - y) };
|
||||||
|
points[draw_count + 3] = c.SDL_FPoint { .x = @floatFromInt(cx - x), .y = @floatFromInt(cy + y) };
|
||||||
|
points[draw_count + 4] = c.SDL_FPoint { .x = @floatFromInt(cx + y), .y = @floatFromInt(cy - x) };
|
||||||
|
points[draw_count + 5] = c.SDL_FPoint { .x = @floatFromInt(cx + y), .y = @floatFromInt(cy + x) };
|
||||||
|
points[draw_count + 6] = c.SDL_FPoint { .x = @floatFromInt(cx - y), .y = @floatFromInt(cy - x) };
|
||||||
|
points[draw_count + 7] = c.SDL_FPoint { .x = @floatFromInt(cx - y), .y = @floatFromInt(cy + x) };
|
||||||
|
draw_count = draw_count + 8;
|
||||||
|
if (err <= 0) {
|
||||||
|
y = y + 1;
|
||||||
|
err = err + ty;
|
||||||
|
ty = ty + 2;
|
||||||
|
}
|
||||||
|
if (err > 0) {
|
||||||
|
x = x - 1;
|
||||||
|
tx = tx + 2;
|
||||||
|
err = err + (tx - diameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = c.SDL_SetRenderDrawColor(engine.Control.sdl_data.renderer, color.u.r, color.u.g, color.u.b, color.u.a);
|
||||||
|
_ = c.SDL_RenderPoints(engine.Control.sdl_data.renderer, &points, @intCast(draw_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_vec2f(self: *const Vec2i) Vec2f {
|
pub fn to_vec2f(self: *const Vec2i) Vec2f {
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,45 @@
|
||||||
pub const Control = @import("Control.zig");
|
pub const Control = @import("Control.zig");
|
||||||
pub const Scene = @import("Scene.zig");
|
pub const Scene = @import("Scene.zig");
|
||||||
pub const Tag = @import("Tag.zig");
|
|
||||||
pub const Layer = @import("Layer.zig");
|
|
||||||
pub const Interface = @import("Interface.zig");
|
pub const Interface = @import("Interface.zig");
|
||||||
pub const Entity = @import("Entity.zig");
|
|
||||||
pub const Color = @import("Color.zig");
|
pub const Color = @import("Color.zig");
|
||||||
pub const Noise = @import("Noise.zig");
|
pub const Noise = @import("Noise.zig");
|
||||||
pub const Sprite = @import("Sprite.zig");
|
pub const Sprite = @import("Sprite.zig");
|
||||||
pub const Texture = @import("Texture.zig");
|
pub const Texture = @import("Texture.zig");
|
||||||
|
|
||||||
pub const geometry = @import("geometry/root.zig");
|
pub const geometry = @import("geometry/root.zig");
|
||||||
|
pub const Debug = @import("Debug.zig");
|
||||||
|
|
||||||
|
pub const Entity = @import("Entity.zig");
|
||||||
|
pub const MouseListener = @import("MouseListener.zig");
|
||||||
|
|
||||||
pub const Debug = struct {
|
pub const Layer = enum(u8) {
|
||||||
pub const camera = true;
|
HIDDEN = 0,
|
||||||
|
FLOOR = 1,
|
||||||
|
ENTITIES = 2,
|
||||||
|
CAMERA = 3,
|
||||||
|
SELECTION = 4,
|
||||||
|
OVERLAY = 128,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Tag = enum(u8) {
|
||||||
|
NONE = 0,
|
||||||
|
CAMERA = 1,
|
||||||
|
TERRAIN = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// this will convert an enum type to an array of all enum members
|
||||||
|
pub fn enum_to_array(comptime e: anytype) [@typeInfo(e).Enum.fields.len]e {
|
||||||
|
return comptime blk: {
|
||||||
|
const len = @typeInfo(e).Enum.fields.len;
|
||||||
|
var arr: [len]e = undefined;
|
||||||
|
for(0..len) |idx| {
|
||||||
|
const val: e = @enumFromInt(@typeInfo(e).Enum.fields[idx].value);
|
||||||
|
// @compileLog(val);
|
||||||
|
arr[idx] = val;
|
||||||
|
}
|
||||||
|
break :blk arr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn enum_length(comptime e: anytype) comptime_int {
|
||||||
|
// return comptime enum_to_array(e).len;
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,26 @@ const Vec2i = engine.geometry.Vec2i;
|
||||||
const Vec2f = engine.geometry.Vec2f;
|
const Vec2f = engine.geometry.Vec2f;
|
||||||
const Recti = engine.geometry.Recti;
|
const Recti = engine.geometry.Recti;
|
||||||
const Rectf = engine.geometry.Rectf;
|
const Rectf = engine.geometry.Rectf;
|
||||||
|
const MouseListener = engine.MouseListener;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub const TAG = engine.Tag.create(Camera);
|
const DragData = struct {
|
||||||
|
mouse_start_pos: Vec2i,
|
||||||
|
focus_start_pos: Vec2f,
|
||||||
|
mouse_current_pos: Vec2i,
|
||||||
|
};
|
||||||
|
|
||||||
// focus: Vec2i = Vec2i.EAST.scale(100),
|
|
||||||
focus: Vec2f = Vec2f.ZERO,
|
focus: Vec2f = Vec2f.ZERO,
|
||||||
tile_size: i32 = 16,
|
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_start_mouse_pos: ?Vec2i = null,
|
||||||
drag_focus: ?Vec2f = null,
|
// focus_when_drag_started: ?Vec2f = null,
|
||||||
|
drag_data: ?DragData = null,
|
||||||
|
mouse_in: bool = false,
|
||||||
|
|
||||||
fn draw_line_vec2i(a: Vec2i, b: Vec2i, layer: Layer, color: Color) void {
|
fn draw_line_vec2i(a: Vec2i, b: Vec2i, color: Color) void {
|
||||||
_ = layer;
|
|
||||||
_ = c.SDL_SetRenderDrawColor(engine.Control.sdl_data.renderer, color.u.r, color.u.g, color.u.b, color.u.a);
|
_ = c.SDL_SetRenderDrawColor(engine.Control.sdl_data.renderer, color.u.r, color.u.g, color.u.b, color.u.a);
|
||||||
_ = c.SDL_RenderLine(
|
_ = c.SDL_RenderLine(
|
||||||
engine.Control.sdl_data.renderer,
|
engine.Control.sdl_data.renderer,
|
||||||
|
|
@ -44,9 +50,10 @@ fn draw_line_vec2f(a: Vec2f, b: Vec2f, layer: Layer, color: Color) void {
|
||||||
@panic("Not Implemented");
|
@panic("Not Implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create() !*Camera {
|
pub fn create(allocator: Allocator) !*Camera {
|
||||||
const self = Camera {};
|
const self = try allocator.create(Camera);
|
||||||
return try Scene.EntityPool.allocate(self);
|
self.* = Camera {};
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(self: *Camera, width: i32, height: i32) void {
|
pub fn resize(self: *Camera, width: i32, height: i32) void {
|
||||||
|
|
@ -55,44 +62,43 @@ pub fn resize(self: *Camera, width: i32, height: i32) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *const Camera) void {
|
pub fn destroy(self: *const Camera) void {
|
||||||
Scene.EntityPool.deallocate(self);
|
_ = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entity(self: *Camera) Entity {
|
pub fn start(self: *Camera, scene: *Scene) void {
|
||||||
return Entity.init(self, .{
|
scene.register_mouse_listener(MouseListener.from(self));
|
||||||
.tag = TAG,
|
|
||||||
.layer = Layer.CAMERA
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(self: *Camera, _: f32) void {
|
pub fn update(self: *Camera, _: f32) void {
|
||||||
if (self.drag_pos != null and self.drag_focus != null and engine.Control.mouse_pos != null) {
|
_ = self;
|
||||||
// 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.Control.mouse_pos.?);
|
|
||||||
|
|
||||||
self.focus = Vec2f {
|
pub fn draw(self: *const Camera, layer: Layer) void {
|
||||||
.x = self.drag_focus.?.x + (drag_start_world.x - drag_current_world.x),
|
switch (layer) {
|
||||||
.y = self.drag_focus.?.y + (drag_start_world.y - drag_current_world.y),
|
.CAMERA => {
|
||||||
};
|
if (engine.Debug.camera and self.drag_data != null) {
|
||||||
|
draw_line_vec2i(self.drag_data.?.mouse_start_pos, self.drag_data.?.mouse_current_pos, Color.WHITE);
|
||||||
|
self.drag_data.?.mouse_current_pos.draw(4, Color.CYAN);
|
||||||
|
self.drag_data.?.mouse_start_pos.draw(4, Color.RED);
|
||||||
|
|
||||||
|
const start_focus_screen = self.world_to_screen_vec2f(self.drag_data.?.focus_start_pos);
|
||||||
|
const current_focus_screen = self.world_to_screen_vec2f(self.focus);
|
||||||
|
|
||||||
|
draw_line_vec2i(start_focus_screen, current_focus_screen, Color.BLUE);
|
||||||
|
start_focus_screen.draw(4, Color.ORANGE);
|
||||||
|
current_focus_screen.draw(4, Color.LIME);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *const Camera) void {
|
fn window_size_offset_x() i32 {
|
||||||
if (engine.Debug.camera and self.drag_pos != null and self.drag_focus != null) {
|
return @divFloor(engine.Control.window_width, 2);
|
||||||
if(engine.Control.mouse_pos != null) {
|
}
|
||||||
draw_line_vec2i(self.drag_pos.?, engine.Control.mouse_pos.?, Layer.CAMERA, Color.WHITE);
|
|
||||||
engine.Control.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.?);
|
fn window_size_offset_y() i32 {
|
||||||
const current_focus_screen = self.world_to_screen_vec2f(self.focus);
|
return @divFloor(engine.Control.window_height, 2);
|
||||||
|
|
||||||
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 {
|
||||||
|
|
@ -101,8 +107,8 @@ inline fn world_to_screen_vec2i(self: *const Camera, coords: Vec2i) Vec2i {
|
||||||
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(
|
||||||
@as(i32, @intFromFloat(tile_size_f * coords.x)) - focus_x_screen + self.window_size_offset_x,
|
@as(i32, @intFromFloat(tile_size_f * coords.x)) - focus_x_screen + window_size_offset_x(),
|
||||||
@as(i32, @intFromFloat(tile_size_f * coords.y)) - focus_y_screen + self.window_size_offset_y
|
@as(i32, @intFromFloat(tile_size_f * coords.y)) - focus_y_screen + window_size_offset_y()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,8 +118,8 @@ inline fn world_to_screen_vec2f(self: *const Camera, coords: Vec2f) Vec2i {
|
||||||
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(
|
||||||
@as(i32, @intFromFloat(tile_size_f * coords.x)) - focus_x_screen + self.window_size_offset_x,
|
@as(i32, @intFromFloat(tile_size_f * coords.x)) - focus_x_screen + window_size_offset_x(),
|
||||||
@as(i32, @intFromFloat(tile_size_f * coords.y)) - focus_y_screen + self.window_size_offset_y
|
@as(i32, @intFromFloat(tile_size_f * coords.y)) - focus_y_screen + window_size_offset_y()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,8 +128,8 @@ inline fn screen_to_world_vec2i(self: *const Camera, coords: Vec2i) Vec2f {
|
||||||
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);
|
||||||
|
|
||||||
const pre_x: f32 = @floatFromInt(coords.x + focus_x_screen - self.window_size_offset_x);
|
const pre_x: f32 = @floatFromInt(coords.x + focus_x_screen - window_size_offset_x());
|
||||||
const pre_y: f32 = @floatFromInt(coords.y + focus_y_screen - self.window_size_offset_y);
|
const pre_y: f32 = @floatFromInt(coords.y + focus_y_screen - window_size_offset_y());
|
||||||
|
|
||||||
return Vec2f {
|
return Vec2f {
|
||||||
.x = pre_x / tile_size_f,
|
.x = pre_x / tile_size_f,
|
||||||
|
|
@ -138,36 +144,51 @@ inline fn world_to_screen_recti(self: *const Camera, box: Rectf) Recti {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, color: Color) void {
|
||||||
const screen_pos = self.world_to_screen_recti(world_pos.to_rectf());
|
const screen_pos = self.world_to_screen_recti(world_pos.to_rectf());
|
||||||
sprite.draw(screen_pos, layer, color);
|
sprite.draw(screen_pos, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_focus(self: *Camera, f: Vec2f) void {
|
pub fn set_focus(self: *Camera, f: Vec2f) void {
|
||||||
self.focus = f;
|
self.focus = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_layer(_: *const Camera) Layer {
|
pub fn mouse_properties(_: *const Camera) MouseListener.Properties {
|
||||||
return Layer.CAMERA;
|
return MouseListener.Properties {
|
||||||
|
.area = Recti.from_xywh(0, 0, engine.Control.window_width, engine.Control.window_height),
|
||||||
|
.layer = .CAMERA,
|
||||||
|
.button = .Middle,
|
||||||
|
.click_through = true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_area(_: *const Camera) Recti {
|
pub fn mouse_event(self: *Camera, event: MouseListener.Event) void {
|
||||||
return Recti.from_xywh(0, 0, 10_000, 10_000);
|
switch(event) {
|
||||||
}
|
.MouseDown => {
|
||||||
|
if (event.MouseDown.button != .Middle) return;
|
||||||
|
self.drag_data = DragData {
|
||||||
|
.mouse_start_pos = event.MouseDown.pos,
|
||||||
|
.focus_start_pos = self.focus,
|
||||||
|
.mouse_current_pos = event.MouseDown.pos,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.MouseUp => {
|
||||||
|
if (event.MouseUp.button != .Middle) return;
|
||||||
|
self.drag_data = null;
|
||||||
|
},
|
||||||
|
.MouseIn => self.mouse_in = true,
|
||||||
|
.MouseOut => self.mouse_in = false,
|
||||||
|
.MouseMove => {
|
||||||
|
if (self.drag_data == null) return;
|
||||||
|
self.drag_data.?.mouse_current_pos = event.MouseMove.pos;
|
||||||
|
|
||||||
pub fn mouse_down(self: *Camera, button: i32) bool {
|
const drag_start_world = self.screen_to_world_vec2i(self.drag_data.?.mouse_start_pos);
|
||||||
if (button == c.SDL_BUTTON_MIDDLE) {
|
const drag_current_world = self.screen_to_world_vec2i(event.MouseMove.pos);
|
||||||
if (engine.Control.mouse_pos == null) return false;
|
|
||||||
self.drag_pos = engine.Control.mouse_pos.?;
|
|
||||||
self.drag_focus = self.focus;
|
|
||||||
return true;
|
|
||||||
} else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mouse_up(self: *Camera, button: i32) bool {
|
self.focus = Vec2f {
|
||||||
if (self.drag_pos == null) return false;
|
.x = self.drag_data.?.focus_start_pos.x + (drag_start_world.x - drag_current_world.x),
|
||||||
if (button != c.SDL_BUTTON_MIDDLE) return false;
|
.y = self.drag_data.?.focus_start_pos.y + (drag_start_world.y - drag_current_world.y),
|
||||||
self.drag_pos = null;
|
};
|
||||||
self.drag_focus = null;
|
}
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
const Terrain = @This();
|
const Terrain = @This();
|
||||||
|
|
||||||
const engine = @import("engine");
|
const engine = @import("engine");
|
||||||
|
const std = @import("std");
|
||||||
|
const c = @import("c");
|
||||||
const Scene = engine.Scene;
|
const Scene = engine.Scene;
|
||||||
const Entity = engine.Entity;
|
const Entity = engine.Entity;
|
||||||
const Vec2i = engine.geometry.Vec2i;
|
const Vec2i = engine.geometry.Vec2i;
|
||||||
|
const Vec2f = engine.geometry.Vec2f;
|
||||||
const Color = engine.Color;
|
const Color = engine.Color;
|
||||||
const Recti = engine.geometry.Recti;
|
const Recti = engine.geometry.Recti;
|
||||||
const Noise = engine.Noise;
|
const Noise = engine.Noise;
|
||||||
const Layer = engine.Layer;
|
const Layer = engine.Layer;
|
||||||
const c = @import("c");
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
const allocator = std.heap.page_allocator;
|
|
||||||
var prng = std.rand.DefaultPrng.init(0);
|
var prng = std.rand.DefaultPrng.init(0);
|
||||||
|
|
||||||
const Camera = @import("Camera.zig");
|
const Camera = @import("Camera.zig");
|
||||||
const assets = @import("assets.zig");
|
const assets = @import("assets.zig");
|
||||||
|
|
||||||
const TAG = @import("engine").Tag.create(Terrain);
|
|
||||||
|
|
||||||
pub const CHUNK_SIZE = 48;
|
pub const CHUNK_SIZE = 48;
|
||||||
const CHUNK_LENGTH = CHUNK_SIZE * CHUNK_SIZE;
|
const CHUNK_LENGTH = CHUNK_SIZE * CHUNK_SIZE;
|
||||||
|
|
||||||
|
|
@ -29,23 +28,16 @@ const GROWTH_RATE: f32 = 0.5;
|
||||||
|
|
||||||
chunks: std.ArrayList(*Chunk),
|
chunks: std.ArrayList(*Chunk),
|
||||||
generator: WorldGenerator,
|
generator: WorldGenerator,
|
||||||
|
|
||||||
camera: *Camera = undefined,
|
camera: *Camera = undefined,
|
||||||
scene: ?*Scene = null,
|
scene: ?*Scene = null,
|
||||||
|
allocator: Allocator,
|
||||||
|
|
||||||
pub fn entity(self: *Terrain) Entity {
|
pub fn start(self: *Terrain, scene: *Scene) void {
|
||||||
return Entity.init(self, .{
|
self.camera = scene.get(engine.Tag.CAMERA, Camera);
|
||||||
.tag = TAG,
|
self.camera.set_focus(Vec2f.create(CHUNK_SIZE / 2, CHUNK_SIZE / 2));
|
||||||
.layer = Layer.HIDDEN
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(self: *Terrain, scene: *Scene) !void {
|
|
||||||
self.camera = scene.get(Camera.TAG, Camera);
|
|
||||||
// self.camera.set_focus(Vec2f.create(CHUNK_SIZE / 2, CHUNK_SIZE / 2));
|
|
||||||
self.scene = scene;
|
self.scene = scene;
|
||||||
|
|
||||||
for (self.chunks.items) |chunk| try scene.add(chunk);
|
for (self.chunks.items) |chunk| scene.add(Entity.from(chunk, .{})) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contrast(n: f32, blend_strength: f32) f32 {
|
fn contrast(n: f32, blend_strength: f32) f32 {
|
||||||
|
|
@ -62,16 +54,20 @@ fn contrast(n: f32, blend_strength: f32) f32 {
|
||||||
|
|
||||||
fn gen_chunk(self: *Terrain, chunk_pos: Vec2i) !void {
|
fn gen_chunk(self: *Terrain, chunk_pos: Vec2i) !void {
|
||||||
std.debug.print("[Terrain:gen_chunk] ({}, {})\n", .{ chunk_pos.x, chunk_pos.y });
|
std.debug.print("[Terrain:gen_chunk] ({}, {})\n", .{ chunk_pos.x, chunk_pos.y });
|
||||||
const chunk = try Chunk.generate(chunk_pos, &self.generator);
|
const chunk = try Chunk.generate(self.allocator, chunk_pos, &self.generator);
|
||||||
try self.chunks.append(chunk);
|
try self.chunks.append(chunk);
|
||||||
if (self.scene != null) try self.scene.?.add(chunk);
|
|
||||||
|
// gen chunk sometimes happens before start
|
||||||
|
if(self.scene) |scene| try scene.add(Entity.from(chunk, .{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(seed: i64) !*Terrain {
|
pub fn create(allocator: Allocator, seed: i64) !*Terrain {
|
||||||
var self: *Terrain = try Scene.EntityPool.allocate(Terrain {
|
var self: *Terrain = try allocator.create(Terrain);
|
||||||
.chunks = std.ArrayList(*Chunk).init(std.heap.page_allocator),
|
self.* = Terrain {
|
||||||
|
.chunks = std.ArrayList(*Chunk).init(allocator),
|
||||||
.generator = try WorldGenerator.create(seed),
|
.generator = try WorldGenerator.create(seed),
|
||||||
});
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
|
||||||
try self.gen_chunk(Vec2i.ZERO);
|
try self.gen_chunk(Vec2i.ZERO);
|
||||||
try self.gen_chunk(Vec2i { .x = -1, .y = 0 });
|
try self.gen_chunk(Vec2i { .x = -1, .y = 0 });
|
||||||
|
|
@ -81,7 +77,7 @@ pub fn create(seed: i64) !*Terrain {
|
||||||
pub fn destroy(self: *const Terrain) void {
|
pub fn destroy(self: *const Terrain) void {
|
||||||
self.generator.destroy();
|
self.generator.destroy();
|
||||||
self.chunks.deinit();
|
self.chunks.deinit();
|
||||||
Scene.EntityPool.deallocate(self);
|
// Scene.EntityPool.deallocate(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn chance(percent: f32) bool {
|
// fn chance(percent: f32) bool {
|
||||||
|
|
@ -142,7 +138,6 @@ pub fn destroy(self: *const Terrain) void {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
const Chunk = struct {
|
const Chunk = struct {
|
||||||
pub const Tile = struct {
|
pub const Tile = struct {
|
||||||
texture_idx: usize,
|
texture_idx: usize,
|
||||||
|
|
@ -165,18 +160,21 @@ const Chunk = struct {
|
||||||
tiles: []Tile = undefined,
|
tiles: []Tile = undefined,
|
||||||
chunk_pos: Vec2i,
|
chunk_pos: Vec2i,
|
||||||
chunk_pos_world: Vec2i,
|
chunk_pos_world: Vec2i,
|
||||||
|
|
||||||
camera: *Camera = undefined,
|
camera: *Camera = undefined,
|
||||||
|
allocator: Allocator,
|
||||||
|
|
||||||
pub fn generate(chunk_pos: Vec2i, generator: *const WorldGenerator) !*Chunk {
|
pub fn generate(allocator: Allocator, chunk_pos: Vec2i, generator: *const WorldGenerator) !*Chunk {
|
||||||
var self = try Scene.EntityPool.allocate(Chunk {
|
var self = try allocator.create(Chunk);
|
||||||
|
self.* = Chunk {
|
||||||
.chunk_pos = chunk_pos,
|
.chunk_pos = chunk_pos,
|
||||||
.chunk_pos_world = Vec2i {
|
.chunk_pos_world = Vec2i {
|
||||||
.x = chunk_pos.x * CHUNK_SIZE,
|
.x = chunk_pos.x * CHUNK_SIZE,
|
||||||
.y = chunk_pos.y * CHUNK_SIZE,
|
.y = chunk_pos.y * CHUNK_SIZE,
|
||||||
},
|
},
|
||||||
.tiles = try Entity.allocate_array(Tile, CHUNK_LENGTH, Tile.NULL),
|
.tiles = try allocator.alloc(Tile, CHUNK_LENGTH),
|
||||||
});
|
// .tiles = try Entity.allocate_array(Tile, CHUNK_LENGTH, Tile.NULL),
|
||||||
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
|
||||||
self.generate_tiles(generator);
|
self.generate_tiles(generator);
|
||||||
|
|
||||||
|
|
@ -184,8 +182,7 @@ const Chunk = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *const Chunk) void {
|
pub fn destroy(self: *const Chunk) void {
|
||||||
Entity.free_array(self.tiles);
|
self.allocator.free(self.tiles);
|
||||||
Scene.EntityPool.deallocate(self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entity(self: *Chunk) Entity {
|
pub fn entity(self: *Chunk) Entity {
|
||||||
|
|
@ -195,7 +192,7 @@ const Chunk = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(self: *Chunk, scene: *Scene) void {
|
pub fn start(self: *Chunk, scene: *Scene) void {
|
||||||
self.camera = scene.get(Camera.TAG, Camera);
|
self.camera = scene.get(engine.Tag.CAMERA, Camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_tiles(self: *Chunk, generator: *const WorldGenerator) void {
|
fn generate_tiles(self: *Chunk, generator: *const WorldGenerator) void {
|
||||||
|
|
@ -210,21 +207,26 @@ const Chunk = struct {
|
||||||
|
|
||||||
var rect_test: c.SDL_FRect = .{};
|
var rect_test: c.SDL_FRect = .{};
|
||||||
|
|
||||||
pub fn draw(self: *const Chunk) void {
|
pub fn draw(self: *const Chunk, layer: Layer) void {
|
||||||
for(0..CHUNK_LENGTH) |idx| {
|
switch (layer) {
|
||||||
const tile = self.tiles[idx];
|
.FLOOR => {
|
||||||
const x, const y = index_to_xy(@intCast(idx));
|
for(0..CHUNK_LENGTH) |idx| {
|
||||||
const rect = Recti.from_xywh(x, y, 1, 1);
|
const tile = self.tiles[idx];
|
||||||
const sprite = assets.terrain[0][tile.texture_idx];
|
const x, const y = index_to_xy(@intCast(idx));
|
||||||
self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, tile.grass_color);
|
const rect = Recti.from_xywh(x, y, 1, 1);
|
||||||
|
const sprite = assets.terrain[0][tile.texture_idx];
|
||||||
|
self.camera.draw_sprite_i(sprite, rect, tile.grass_color);
|
||||||
|
|
||||||
// switch(tile.texture_idx) {
|
// switch(tile.texture_idx) {
|
||||||
// 0 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.WHITE),
|
// 0 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.WHITE),
|
||||||
// 1 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.RED),
|
// 1 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.RED),
|
||||||
// 2 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.GREEN),
|
// 2 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.GREEN),
|
||||||
// 3 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.BLUE),
|
// 3 => self.camera.draw_sprite_i(sprite, rect, Layer.FLOOR, Color.BLUE),
|
||||||
// else => {}
|
// else => {}
|
||||||
// }
|
// }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,6 @@ pub fn main() !void {
|
||||||
try Engine.Control.setup();
|
try Engine.Control.setup();
|
||||||
defer Engine.Control.destroy();
|
defer Engine.Control.destroy();
|
||||||
|
|
||||||
Engine.Control.set_scene(try scenes.game());
|
try Engine.Control.set_scene(scenes.game);
|
||||||
try Engine.Control.run();
|
try Engine.Control.run();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,23 @@
|
||||||
|
const std = @import("std");
|
||||||
const engine = @import("engine");
|
const engine = @import("engine");
|
||||||
const Scene = engine.Scene;
|
const Scene = engine.Scene;
|
||||||
const Camera = @import("Camera.zig");
|
const Camera = @import("Camera.zig");
|
||||||
const Selection = @import("Selection.zig");
|
const Selection = @import("Selection.zig");
|
||||||
const Terrain = @import("Terrain.zig");
|
const Terrain = @import("Terrain.zig");
|
||||||
|
const Entity = engine.Entity;
|
||||||
|
const Tag = engine.Tag;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub fn game() !Scene {
|
pub fn game(scene: *Scene) Allocator.Error!void {
|
||||||
var scene = Scene.create();
|
|
||||||
// first try is for allocating more memory in entities
|
const camera = try Camera.create(scene.arena.allocator());
|
||||||
// second try is for allocating terrain on the heap...
|
try scene.add(Entity.from(camera, .{ .tag = Tag.CAMERA }));
|
||||||
try scene.add(try Camera.create());
|
|
||||||
try scene.add(try Terrain.create(123));
|
const terrain = try Terrain.create(scene.arena.allocator(), 123);
|
||||||
try scene.add(try Selection.create());
|
try scene.add(Entity.from(terrain, .{ .tag = Tag.TERRAIN }));
|
||||||
|
|
||||||
|
// try scene.add(try Selection.create());
|
||||||
// for (0..5) |_| {
|
// for (0..5) |_| {
|
||||||
// try scene.add(try Pawn.random());
|
// try scene.add(try Pawn.random());
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return scene;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue