diff --git a/src/Camera.zig b/src/Camera.zig index 2a09799..b867e60 100644 --- a/src/Camera.zig +++ b/src/Camera.zig @@ -20,7 +20,7 @@ window_size_offset_y: i32 = 0, pub fn create() !*Camera { const self = Camera {}; - return try Scene.allocate(self); + return try Scene.EntityPool.allocate(self); } pub fn resize(self: *Camera, width: i32, height: i32) void { @@ -29,7 +29,7 @@ pub fn resize(self: *Camera, width: i32, height: i32) void { } pub fn destroy(self: *const Camera) void { - Scene.deallocate(self); + Scene.EntityPool.deallocate(self); } pub fn entity(self: *Camera) Entity { @@ -64,3 +64,7 @@ pub fn draw_sprite_i(self: *const Camera, sprite: *const Sprite, world_pos: Rect pub fn set_focus(self: *Camera, f: Vec2f) void { self.focus = f; } + +pub fn focus_layer(_: *const Camera) Layer { + return Layer.CAMERA; +} diff --git a/src/Color.zig b/src/Color.zig index 0d55cb3..b68cc96 100644 --- a/src/Color.zig +++ b/src/Color.zig @@ -30,7 +30,7 @@ pub fn with_opacity(self: *const Color, a: f32) Color { .r = self.r, .g = self.g, .b = self.b, - .a = a + .a = a * self.a, }; } diff --git a/src/Engine.zig b/src/Engine.zig index 44c7cfd..89ed52d 100644 --- a/src/Engine.zig +++ b/src/Engine.zig @@ -22,6 +22,9 @@ const Error = error { AlreadyRunning }; +var mouse_x: i32 = 0; +var mouse_y: i32 = 0; +var hovered: bool = true; var window: ?*c.GLFWwindow = null; var current_scene: ?Scene = null; var next_scene: ?Scene = null; @@ -29,18 +32,30 @@ var projection: Matrix4f = undefined; fn glfw_on_error(err: c_int, desc_c: [*c]const u8) callconv(.C) void { const desc: *const u8 = @ptrCast(desc_c); - std.log.err("[Engine:glfw_on_error] glfw error {x:0>8}: {s}", .{ err, desc }); + std.log.err("[Engine:glfw_on_error] glfw error {x:0>8}: {s}\n", .{ err, desc }); } -fn glfw_on_resize (_: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { - std.debug.print("[Engine:glfw_on_resize] {?}: {d} x {d}\n", .{ window, width, height }); +fn glfw_on_resize(_: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { + std.debug.print("[Engine:glfw_on_resize] {d} x {d}\n", .{ width, height }); c.glViewport(0, 0, width, height); projection = Matrix4f.orthographic(0, @floatFromInt(width), @floatFromInt(height), 0, 0, 100); shaders.set_projection_matrix(&projection); if (current_scene != null) current_scene.?.resize(width, height); } -fn get_size() struct { i32, i32 } { +fn glfw_on_mouse_move(_: ?*c.GLFWwindow, fx: f64, fy: f64) callconv(.C) void { + const x: i32 = @intFromFloat(fx); + const y: i32 = @intFromFloat(fy); + if (current_scene != null and hovered) { + current_scene.?.mouse_move(x, y); + } +} + +fn glfw_on_mouse_enter(_: ?*c.GLFWwindow, mouse_within: c_int) callconv(.C) void { + hovered = mouse_within == c.GLFW_TRUE; +} + +fn get_window_size() struct { i32, i32 } { var width: c_int = undefined; var height: c_int = undefined; c.glfwGetFramebufferSize(window, @ptrCast(&width), @ptrCast(&height)); @@ -88,6 +103,8 @@ pub fn setup() !void { try assets.load(); _ = c.glfwSetWindowSizeCallback(window, glfw_on_resize); + _ = c.glfwSetCursorPosCallback(window, glfw_on_mouse_move); + _ = c.glfwSetCursorEnterCallback(window, glfw_on_mouse_enter); const clearBrightness: f32 = 0.09; c.glClearColor(clearBrightness, clearBrightness, clearBrightness, 1.0); @@ -99,7 +116,7 @@ pub fn setup() !void { c.glDepthFunc(c.GL_LEQUAL); c.glDepthMask(c.GL_TRUE); - const width, const height = get_size(); + const width, const height = get_window_size(); c.glViewport(0, 0, width, height); projection = Matrix4f.orthographic(0, @floatFromInt(width), @floatFromInt(height), 0, 0, 100); shaders.set_projection_matrix(&projection); @@ -129,8 +146,12 @@ pub fn run() Error!void { // start our scene try current_scene.?.start(); // and send it the appropriate resize. - const width, const height = get_size(); + const width, const height = get_window_size(); current_scene.?.resize(width, height); + if(hovered) { + current_scene.?.mouse_enter(); + current_scene.?.mouse_move(mouse_x, mouse_y); + } } // setup for drawing c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT); diff --git a/src/Entity.zig b/src/Entity.zig index 89207a8..ed42475 100644 --- a/src/Entity.zig +++ b/src/Entity.zig @@ -20,6 +20,33 @@ _draw: *const fn(*const anyopaque) void, _draw_opacity: *const fn(*const anyopaque) void, _start: *const fn(*anyopaque, *Scene) void, _resize: *const fn(*anyopaque, i32, i32) void, +_focusable: *const fn(*const anyopaque) bool, +_focus_layer: *const fn(*const anyopaque) Layer, +_focus_area: *const fn(*const anyopaque) Recti, +_mouse_move: *const fn(*anyopaque, i32, i32) void, +_focus: *const fn(*anyopaque) void, +_blur: *const fn(*anyopaque) void, + +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", .{ @@ -41,12 +68,75 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity { else => @compileError("Entity pointer must point to a struct or opaque type"), } + comptime var input_ready = ArrayTruthState.FALSE; + comptime { + input_ready = bool_array_state(&[_]bool { + @hasDecl(type_info.Pointer.child, "focus_area"), + @hasDecl(type_info.Pointer.child, "focus_layer"), + @hasDecl(type_info.Pointer.child, "focus"), + @hasDecl(type_info.Pointer.child, "blur"), + @hasDecl(type_info.Pointer.child, "mouse_move"), + }); + + switch(input_ready) { + .TRUE => {}, + .FALSE => {}, + .EMPTY => {}, + .MIXED => @compileError(@typeName(type_info.Pointer.child) ++ " does not defien all methods required for input focus"), + } + } + + // @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 { + pub fn _focusable(_: *const anyopaque) bool { + return input_ready == ArrayTruthState.TRUE; + } + pub fn _focus_layer(pointer: *const anyopaque) Layer { + const self: ConstPointer = @ptrCast(@alignCast(pointer)); + switch (input_ready) { + .TRUE => return type_info.Pointer.child.focus_layer(self), + else => unreachable, + } + } + pub fn _focus_area(pointer: *const anyopaque) Recti { + const self: ConstPointer = @ptrCast(@alignCast(pointer)); + switch(input_ready) { + .TRUE => return type_info.Pointer.child.focus_area(self), + else => unreachable, + } + } + pub fn _mouse_move(pointer: *anyopaque, x: i32, y: i32) void { + const self: MutablePointer = @ptrCast(@alignCast(pointer)); + switch(input_ready) { + .TRUE => type_info.Pointer.child.mouse_move(self, x, y), + else => unreachable, + } + } + pub fn _focus(pointer: *anyopaque) void { + const self: MutablePointer = @ptrCast(@alignCast(pointer)); + switch(input_ready) { + .TRUE => type_info.Pointer.child.focus(self), + else => unreachable, + } + } + pub fn _blur(pointer: *anyopaque) void { + const self: MutablePointer = @ptrCast(@alignCast(pointer)); + switch(input_ready) { + .TRUE => type_info.Pointer.child.blur(self), + else => unreachable, + } + } + pub fn _name(_: *const anyopaque) []const u8 { + // const self: ConstPointer = @ptrCast(@alignCast(pointer)); + return type_info.Pointer.child; + } pub fn _destroy(pointer: *const anyopaque) void { const self: ConstPointer = @ptrCast(@alignCast(pointer)); type_info.Pointer.child.destroy(self); @@ -83,7 +173,7 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity { } }; - return .{ + return Entity { .ptr = ptr, .tag = options.tag, ._destroy = gen._destroy, @@ -92,6 +182,12 @@ pub fn init(ptr: anytype, options: EntityOptions) Entity { ._update = gen._update, ._draw = gen._draw, ._draw_opacity = gen._draw_opacity, + ._focusable = gen._focusable, + ._focus_layer = gen._focus_layer, + ._focus_area = gen._focus_area, + ._mouse_move = gen._mouse_move, + ._focus = gen._focus, + ._blur = gen._blur, }; } @@ -119,6 +215,33 @@ pub fn resize(self: *const Entity, width: i32, height: i32) void { self._resize(self.ptr, width, height); } +pub fn focus(self: *const Entity) void { + self._focus(self.ptr); +} + +pub fn blur(self: *const Entity) void { + self._blur(self.ptr); +} + +// -=- focus related items + +// defines if the entity has functions to define its focus area +pub fn focusable(self: *const Entity) bool { + return self._focusable(self.ptr); +} + +pub fn focus_layer(self: *const Entity) Layer { + return self._focus_layer(self.ptr); +} + +pub fn focus_area(self: *const Entity) Recti { + return self._focus_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 allocate_array(comptime T: type, size: usize, default: T) ![]T { const ptr = try std.heap.page_allocator.alloc(T, size); for (0..size) |idx| { diff --git a/src/Layer.zig b/src/Layer.zig index f5085f7..4949af9 100644 --- a/src/Layer.zig +++ b/src/Layer.zig @@ -10,3 +10,5 @@ fn layer(z_index: i32) Layer { pub const FLOOR = layer(0); pub const ENTITIES = layer(1); +pub const CAMERA = layer(2); +pub const SELECTION = layer(3); diff --git a/src/Pawn.zig b/src/Pawn.zig index 663d308..662a03d 100644 --- a/src/Pawn.zig +++ b/src/Pawn.zig @@ -21,7 +21,7 @@ pub fn random() !*Pawn { @intFromFloat(@floor(prng.random().float(f32) * @as(f32, @floatFromInt(Terrain.CHUNK_SIZE)))), @intFromFloat(@floor(prng.random().float(f32) * @as(f32, @floatFromInt(Terrain.CHUNK_SIZE)))) ); - return try Scene.allocate(self); + return try Scene.EntityPool.allocate(self); } pub fn create() !*Pawn { @@ -34,7 +34,7 @@ pub fn start(self: *Pawn, scene: *const Scene) void { } pub fn destroy(self: *const Pawn) void { - Scene.deallocate(self); + Scene.EntityPool.deallocate(self); } pub fn entity(self: *Pawn) Entity { diff --git a/src/Scene.zig b/src/Scene.zig index 260fe7d..7fe82f3 100644 --- a/src/Scene.zig +++ b/src/Scene.zig @@ -6,6 +6,7 @@ const Layer = @import("Layer.zig"); const Color = @import("Color.zig"); const Entity = @import("Entity.zig"); const Terrain = @import("Terrain.zig"); +const Vec2i = @import("geometry/Vec2i.zig"); const Tag = @import("Tag.zig"); const std = @import("std"); const ArrayList = std.ArrayList; @@ -13,12 +14,17 @@ const AutoHashMap = std.AutoHashMap; // cache and list bothe store entities. treat entities as fat pointers. // Pointers into an arraylist WILL become invalid. +focus_items: ArrayList(Entity) = undefined, entities: ArrayList(Entity) = undefined, tagged_entities: AutoHashMap(Tag.ID, Entity) = undefined, +mouse_inside: bool = false, +current_focus_item: ?Entity = null, +mouse_pos: ?Vec2i = null, pub fn create() Scene { var self = Scene {}; self.entities = ArrayList(Entity).init(std.heap.page_allocator); + self.focus_items = ArrayList(Entity).init(std.heap.page_allocator); self.tagged_entities = AutoHashMap(Tag.ID, Entity).init(std.heap.page_allocator); return self; } @@ -60,7 +66,7 @@ pub fn add(self: *Scene, instance_ptr: anytype) !void { @compileError( "Cannot add to scene type " ++ @typeName(instance_ptr_type) ++ - "Must be a mutable pointer\n" + ". Must be a mutable pointer\n" ); if (instance_ptr_type_info.Pointer.size != .One) @compileError("Pointer size must be One"); @@ -78,12 +84,55 @@ pub fn add(self: *Scene, instance_ptr: anytype) !void { if (entity.tag.id != Tag.NONE.id) { try self.tagged_entities.put(entity.tag.id, entity); } + if (entity.focusable()) { + try self.focus_items.append(entity); + } +} + +fn entity_layer_less_than(_: void, lhs: Entity, rhs: Entity) bool { + return lhs.focus_layer().z_index < rhs.focus_layer().z_index; } pub fn update(self: *Scene, dt: f32) void { for (self.entities.items) |entity| { entity.update(dt); } + + // this whole operation is wildly stupid and could be amde more efficient. + std.mem.sort(Entity, self.focus_items.items, {}, entity_layer_less_than); + if (self.mouse_pos) |mouse| { + var top_hovered: ?Entity = null; + + for (self.focus_items.items) |item| { + if (item.focusable() and item.focus_area().contains(mouse)) { + top_hovered = item; + continue; + } + } + + if (top_hovered != null) { + if (self.current_focus_item != null) { + // if we're changing hover target from something to something + if (self.current_focus_item.?.ptr != top_hovered.?.ptr) { + self.current_focus_item.?.blur(); + self.current_focus_item = top_hovered; + self.current_focus_item.?.focus(); + } + } else { + // we are hovering something, and werent before. + self.current_focus_item = top_hovered.?; + self.current_focus_item.?.focus(); + } + } else if (self.current_focus_item != null) { + // here we were hovering something, but now are not. + self.current_focus_item.?.blur(); + self.current_focus_item = null; + } + } else if (self.current_focus_item != null) { + self.current_focus_item.?.blur(); + self.current_focus_item = null; + } + } pub fn draw(self: *Scene) void { @@ -93,6 +142,9 @@ pub fn draw(self: *Scene) void { for (self.entities.items) |entity| { entity.draw_opacity(); } + for (self.focus_items.items) |item| { + item.focus_area().draw(item.focus_layer(), Color.WHITE.with_opacity(0.3)); + } } pub fn resize(self: *Scene, width: i32, height: i32) void { @@ -101,16 +153,35 @@ pub fn resize(self: *Scene, width: i32, height: 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 { .x = x, .y = y }; + self.mouse_inside = true; + for (self.focus_items.items) |item| { + item.mouse_move(x, y); + } +} + +pub fn mouse_enter(self: *Scene) void { + self.mouse_inside = true; +} + +pub fn mouse_leave(self: *Scene) void { + self.mouse_inside = false; + self.mouse_pos = null; +} + // --- 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; + } -// 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); -} + pub fn deallocate(instance: anytype) void { + std.heap.page_allocator.destroy(instance); + } +}; diff --git a/src/Selection.zig b/src/Selection.zig new file mode 100644 index 0000000..c233c31 --- /dev/null +++ b/src/Selection.zig @@ -0,0 +1,67 @@ +const Selection = @This(); + +const Scene = @import("./Scene.zig"); +const Entity = @import("./Entity.zig"); +const Tag = @import("./Tag.zig"); +const Layer = @import("./Layer.zig"); +const Engine = @import("./Engine.zig"); +const Recti = @import("./geometry/Recti.zig"); +const std = @import("std"); + +const TAG = Tag.create(Selection); + +selection: std.ArrayList(Selectable), +focused: bool = false, + +pub fn create() !*Selection { + return try Scene.EntityPool.allocate(Selection { + .selection = std.ArrayList(Selectable).init(std.heap.page_allocator), + }); +} + +pub fn entity(self: *Selection) Entity { + return Entity.init(self, .{ + .tag = TAG, + }); +} + +pub fn destroy(self: *const Selection) void { + self.selection.deinit(); + Scene.EntityPool.deallocate(self); +} + +pub fn focus_area(_: *const Selection) Recti { + // return Recti.from_xywh(0, 0, std.math.maxInt(i16), std.math.maxInt(i16)); + return Recti.from_xywh(0, 0, 300, 1000); +} + +pub fn focus_layer(_: *const Selection) Layer { + return Layer.ENTITIES; +} + +pub fn focus(self: *Selection) void { + self.focused = true; + std.debug.print("[Selection:focus] Focused\n", .{}); +} + +pub fn blur(self: *Selection) void { + self.focused = false; + std.debug.print("[Selection:focus] Blurred\n", .{}); +} + +pub fn mouse_move(_: *Selection, _: i32, _: i32) void { + // std.debug.print("[Selection:mouse_move] {}, {}\n", .{ x, y }); +} + +const Selectable = struct { + ptr: *anyopaque, + + pub fn init(item: anytype) Selectable { + // const target_type = @typeInfo(@TypeOf(item)); + // const MutablePointer = @typeInfo(@TypeOf(item)); + + return Selectable { + .ptr = item + }; + } +}; diff --git a/src/Sprite.zig b/src/Sprite.zig index e8d196a..2ea146a 100644 --- a/src/Sprite.zig +++ b/src/Sprite.zig @@ -26,6 +26,7 @@ pub fn create(tex: *const Texture, rect: Recti) Sprite { pub fn draw(self: *const Sprite, screen_pos: Recti, layer: Layer, color: Color) void { self.texture.bind(c.GL_TEXTURE0); + shaders.enable_textures(); c.glBegin(c.GL_QUADS); { c.glVertexAttrib2f(shaders.TEXCOORD_ATTRIBUTE_ID, self.texture_area.a.x, self.texture_area.a.y); diff --git a/src/Terrain.zig b/src/Terrain.zig index d20a17c..6e78801 100644 --- a/src/Terrain.zig +++ b/src/Terrain.zig @@ -107,7 +107,7 @@ fn generate_tiles(self: *Terrain) void { } pub fn create(seed: i64) !*Terrain { - var self: *Terrain = try Scene.allocate(Terrain { + var self: *Terrain = try Scene.EntityPool.allocate(Terrain { .tiles = try Entity.allocate_array(Tile, CHUNK_LENGTH, Tile.NULL), .growth_noise = Noise.create(seed, 10, 0.03), .texture_noise = Noise.create(seed + 1, 1, 3), @@ -134,7 +134,7 @@ pub fn destroy(self: *const Terrain) void { self.stone_color_noise.destroy(); Entity.free_array(self.tiles); - Scene.deallocate(self); + Scene.EntityPool.deallocate(self); } fn chance(percent: f32) bool { @@ -173,6 +173,7 @@ pub fn update(self: *Terrain, dt: f32) void { } } + pub fn draw(self: *const Terrain) void { for(0..CHUNK_LENGTH) |idx| { const tile = self.tiles[idx]; diff --git a/src/geometry/Recti.zig b/src/geometry/Recti.zig index 03365a9..a75f41d 100644 --- a/src/geometry/Recti.zig +++ b/src/geometry/Recti.zig @@ -2,6 +2,10 @@ const Vec2i = @import("./Vec2i.zig"); const Vec2f = @import("./Vec2f.zig"); const Rectf = @import("./Rectf.zig"); const std = @import("std"); +const c = @cImport({ + @cInclude("glad/glad.h"); + @cInclude("GLFW/glfw3.h"); +}); const Recti = @This(); @@ -40,6 +44,13 @@ pub fn from_ab(a: Vec2i, b: Vec2i) Recti { }; } +pub fn contains(self: *const Recti, point: Vec2i) bool { + return point.x >= self.a.x + and point.x < self.b.x + and point.y >= self.a.y + and point.y < self.b.y; +} + pub fn to_rectf(self: *const Recti) Rectf { return Rectf.from_xywh( @intCast(self.x), @@ -57,3 +68,72 @@ pub fn scalef(self: *const Recti, scale: Vec2f) Rectf { @as(f32, @floatFromInt(self.h)) / scale.y, ); } + +const Color = @import("../Color.zig"); +const Layer = @import("../Layer.zig"); +const shaders = @import("../shaders.zig"); +const assets = @import("../assets.zig"); + +pub fn draw(self: *const Recti, layer: Layer, color: Color) void { + self.draw_filled(layer, color.with_opacity(0.2)); + self.draw_outline(layer, color); +} + +pub fn draw_filled(self: *const Recti, layer: Layer, color: Color) void { + shaders.disable_textures(); + + c.glBegin(c.GL_QUADS); + + { + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.a.x, self.a.y, layer.z_index); + + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.b.x, self.a.y, layer.z_index); + + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.b.x, self.b.y, layer.z_index); + + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.a.x, self.b.y, layer.z_index); + } + + c.glEnd(); +} + +pub fn draw_outline(self: *const Recti, layer: Layer, color: Color) void { + + shaders.disable_textures(); + + c.glBegin(c.GL_LINES); + { + // top + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.a.x, self.a.y, layer.z_index); + + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.b.x, self.a.y, layer.z_index); + + // right + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.b.x, self.a.y, layer.z_index); + + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.b.x, self.b.y, layer.z_index); + + // bottom + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.b.x, self.b.y, layer.z_index); + + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.a.x, self.b.y, layer.z_index); + + // left + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.a.x, self.b.y, layer.z_index); + + c.glVertexAttrib4f(shaders.COLOR_ATTRIBUTE_ID, color.r, color.g, color.b, color.a); + c.glVertex3i(self.a.x, self.a.y, layer.z_index); + } + c.glEnd(); +} diff --git a/src/gui.zig b/src/gui.zig new file mode 100644 index 0000000..9a8d860 --- /dev/null +++ b/src/gui.zig @@ -0,0 +1,8 @@ + + + + + +const Button = struct { + +} diff --git a/src/scenes.zig b/src/scenes.zig index b9ffff6..edaf56c 100644 --- a/src/scenes.zig +++ b/src/scenes.zig @@ -2,7 +2,7 @@ const Scene = @import("Scene.zig"); const Terrain = @import("Terrain.zig"); const Pawn = @import("Pawn.zig"); const Camera = @import("Camera.zig"); - +const Selection = @import("Selection.zig"); pub fn game() !Scene { var scene = Scene.create(); @@ -10,6 +10,7 @@ pub fn game() !Scene { // second try is for allocating terrain on the heap... try scene.add(try Camera.create()); try scene.add(try Terrain.create(123)); + try scene.add(try Selection.create()); for (0..5) |_| { try scene.add(try Pawn.random()); } diff --git a/src/shaders.zig b/src/shaders.zig index e36211d..cd7d9b2 100644 --- a/src/shaders.zig +++ b/src/shaders.zig @@ -33,6 +33,7 @@ const VERT_SHADER_TEXT = const FRAG_SHADER_TEXT = \\ #version 330 \\ uniform sampler2D uTexture0; + \\ uniform bool uNoTexture; \\ \\ in vec4 vColor; \\ in vec2 vTexCoord; @@ -40,14 +41,18 @@ const FRAG_SHADER_TEXT = \\ out vec4 color; \\ \\ void main() { - \\ vec4 texColor = color = texture(uTexture0, vTexCoord); - \\ if(texColor == vec4(1, 0, 1, 1) || texColor == vec4(1, 0, 0, 1) || texColor.w == 0.0) { - \\ discard; - \\ return; + \\ if (!uNoTexture) { + \\ vec4 texColor = color = texture(uTexture0, vTexCoord); + \\ if(texColor == vec4(1, 0, 1, 1) || texColor == vec4(1, 0, 0, 1) || texColor.w == 0.0) { + \\ discard; + \\ return; + \\ } + \\ color = texColor * vColor; + \\ } else { + \\ color = vColor; + \\ // color = vec4(1, 1, 1, 1); + \\ // color = vec4(vTexCoord.x, vTexCoord.y, 0, 1); \\ } - \\ color = texColor * vColor; - \\ // color = vColor; - \\ // color = vec4(vTexCoord.x, vTexCoord.y, 0, 1); \\ } ; @@ -56,12 +61,13 @@ pub const COLOR_ATTRIBUTE_ID = 1; pub const TEXCOORD_ATTRIBUTE_ID = 2; var flat: Shader = undefined; -var active_shader: *const Shader = undefined; +var active_shader: *Shader = undefined; pub fn load() !void { // if (active_shader != undefined) return Error.AlreadyInitialized; flat = try Shader.compile(VERT_SHADER_TEXT, FRAG_SHADER_TEXT); flat.enable(); + flat.enable_textures(); return; } @@ -70,20 +76,31 @@ pub fn set_projection_matrix(matrix: *const Matrix4f) void { active_shader.set_projection_matrix(matrix); } +pub fn enable_textures() void { + active_shader.enable_textures(); +} + +pub fn disable_textures() void { + active_shader.disable_textures(); +} + const Shader = struct { vertex_shader: c.GLuint, fragment_shader: c.GLuint, program_handle: c.GLuint, + textures_enabled: bool = false, + const Error = error { CompilationFailed }; - fn enable(self: *const Shader) void { + fn enable(self: *Shader) void { c.glUseProgram(self.program_handle); active_shader = self; c.glUniform1i(c.glGetUniformLocation(self.program_handle, "uTexture0"), 0); + self.enable_textures(); } fn compile(vert_shader_text: []const u8, frag_shader_text: []const u8) !Shader { @@ -148,6 +165,20 @@ const Shader = struct { const attribute_id: c.GLint = self.get_uniform_id("uProjection"); c.glUniformMatrix4fv(attribute_id, 1, c.GL_FALSE, @ptrCast(&matrix.values)); } + + fn enable_textures(self: *Shader) void { + if (self.textures_enabled) return; + self.textures_enabled = true; + const attr_id: c.GLint = self.get_uniform_id("uNoTexture"); + c.glUniform1i(attr_id, c.GL_FALSE); + } + + fn disable_textures(self: *Shader) void { + if (!self.textures_enabled) return; + self.textures_enabled = false; + const attr_id: c.GLint = self.get_uniform_id("uNoTexture"); + c.glUniform1i(attr_id, c.GL_TRUE); + } };