diff --git a/build.zig b/build.zig index 72a125c9..67ec218c 100644 --- a/build.zig +++ b/build.zig @@ -10,9 +10,13 @@ pub fn build(b: *std.build.Builder) !void { // Standard release options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. - const mode = b.standardReleaseOptions(); - - const exe = b.addExecutable("Cubyzig", "src/main.zig"); + const optimize = b.standardOptimizeOption(.{}); + const exe = b.addExecutable(.{ + .name = "Cubyzig", + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); exe.addIncludePath("include"); exe.linkLibC(); { // compile glfw from source: @@ -40,11 +44,10 @@ pub fn build(b: *std.build.Builder) !void { } } exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c"}, &[_][]const u8{"-g"}); - exe.addPackage(freetype.pkg); - exe.addPackage(freetype.harfbuzz_pkg); + exe.addAnonymousModule("gui", .{.source_file = .{.path = "src/gui/gui.zig"}}); + exe.addModule("freetype", freetype.module(b)); + exe.addModule("harfbuzz", freetype.harfbuzzModule(b)); freetype.link(b, exe, .{ .harfbuzz = .{} }); - exe.setTarget(target); - exe.setBuildMode(mode); //exe.sanitize_thread = true; exe.install(); @@ -57,9 +60,11 @@ pub fn build(b: *std.build.Builder) !void { const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); - const exe_tests = b.addTest("src/main.zig"); - exe_tests.setTarget(target); - exe_tests.setBuildMode(mode); + const exe_tests = b.addTest(.{ + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); const test_step = b.step("test", "Run unit tests"); test_step.dependOn(&exe_tests.step); diff --git a/mach-freetype b/mach-freetype index 9cfd1177..0fed6938 160000 --- a/mach-freetype +++ b/mach-freetype @@ -1 +1 @@ -Subproject commit 9cfd1177292533c68f7d4d9a2a123722c8b494f4 +Subproject commit 0fed693866fb86d9e94169ca06338353b3e40511 diff --git a/src/entity.zig b/src/entity.zig index 2d56f2a6..f0ccd864 100644 --- a/src/entity.zig +++ b/src/entity.zig @@ -127,8 +127,8 @@ pub const ClientEntityManager = struct { const xCenter = (1 + projectedPos[0]/projectedPos[3])*@intToFloat(f32, main.Window.width/2); const yCenter = (1 - projectedPos[1]/projectedPos[3])*@intToFloat(f32, main.Window.height/2); - graphics.Draw.setColor(0xffff00ff); - graphics.Draw.rect(.{xCenter, yCenter}, .{100, 20}); // TODO: Text rendering. + graphics.draw.setColor(0xffff00ff); + graphics.draw.rect(.{xCenter, yCenter}, .{100, 20}); // TODO: Text rendering. } } diff --git a/src/graphics.zig b/src/graphics.zig index d4d2c0a7..55e3ef86 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -29,7 +29,7 @@ fn fileToString(allocator: Allocator, path: []const u8) ![]u8 { return file.readToEndAlloc(allocator, std.math.maxInt(usize)); } -pub const Draw = struct { +pub const draw = struct { var color: i32 = 0; var clip: ?Vec4i = null; @@ -541,7 +541,7 @@ pub const TextBuffer = struct { c.glUniform1f(TextRendering.uniforms.ratio, fontScaling); c.glActiveTexture(c.GL_TEXTURE0); c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0]); - c.glBindVertexArray(Draw.rectVAO); + c.glBindVertexArray(draw.rectVAO); const lineWraps: []f32 = try main.threadAllocator.alloc(f32, self.lineBreakIndices.items.len - 1); defer main.threadAllocator.free(lineWraps); var i: usize = 0; @@ -563,14 +563,14 @@ pub const TextBuffer = struct { y = _y/fontScaling; if(line.isUnderline) y += 15 else y += 8; - Draw.setColor(line.color | @as(u32, 0xff000000)); + draw.setColor(line.color | @as(u32, 0xff000000)); for(lineWraps) |lineWrap| { const lineStart = @max(0, line.start); const lineEnd = @min(lineWrap, line.end); if(lineStart < lineEnd) { var start = Vec2f{lineStart*fontScaling + _x, y*fontScaling}; const dim = Vec2f{(lineEnd - lineStart)*fontScaling, fontScaling}; - Draw.rect(start, dim); + draw.rect(start, dim); } line.start -= lineWrap; line.end -= lineWrap; @@ -725,20 +725,20 @@ const TextRendering = struct { }; pub fn init() !void { - Draw.initCircle(); - Draw.initDrawRect(); - Draw.initImage(); - Draw.initLine(); - Draw.initRect(); + draw.initCircle(); + draw.initDrawRect(); + draw.initImage(); + draw.initLine(); + draw.initRect(); try TextRendering.init(); } pub fn deinit() void { - Draw.deinitCircle(); - Draw.deinitDrawRect(); - Draw.deinitImage(); - Draw.deinitLine(); - Draw.deinitRect(); + draw.deinitCircle(); + draw.deinitDrawRect(); + draw.deinitImage(); + draw.deinitLine(); + draw.deinitRect(); TextRendering.deinit(); } diff --git a/src/gui/GuiWindow.zig b/src/gui/GuiWindow.zig new file mode 100644 index 00000000..113f9829 --- /dev/null +++ b/src/gui/GuiWindow.zig @@ -0,0 +1,343 @@ +const std = @import("std"); + +const main = @import("root"); +const graphics = main.graphics; +const draw = graphics.draw; +const settings = main.settings; +const vec = main.vec; +const Vec2f = vec.Vec2f; +const Vec2i = vec.Vec2i; + +const gui = @import("gui.zig"); + +const GuiWindow = @This(); + +const AttachmentPoint = enum { + lower, + middle, + upper, +}; + +const OrientationLine = struct { + pos: f32, + start: f32, + end: f32, +}; + +const RelativePosition = union(enum) { + ratio: f32, + attachedToFrame: struct { + selfAttachmentPoint: AttachmentPoint, + otherAttachmentPoint: AttachmentPoint, + }, + relativeToWindow: struct { + reference: *GuiWindow, + ratio: f32, + }, + attachedToWindow: struct { + reference: *GuiWindow, + selfAttachmentPoint: AttachmentPoint, + otherAttachmentPoint: AttachmentPoint, + }, +}; + +pos: Vec2f = undefined, +size: Vec2f = undefined, +contentSize: Vec2f, +scale: f32 = 1, +spacing: f32 = 0, +relativePosition: [2]RelativePosition = .{.{.ratio = 0.5}, .{.ratio = 0.5}}, +showTitleBar: bool = true, +title: []const u8, +id: []const u8, + +/// Called every frame. +renderFn: *const fn()void, +/// Called every frame for the currently selected window. +updateFn: *const fn()void = &defaultFunction, + +onOpenFn: *const fn()void = &defaultFunction, + +onCloseFn: *const fn()void = &defaultFunction, + +var grabPosition: ?Vec2f = null; +var selfPositionWhenGrabbed: Vec2f = undefined; + +pub fn defaultFunction() void {} + +pub fn leftMouseButtonPressed(self: *const GuiWindow) void { + const scale = @floor(settings.guiScale*self.scale); // TODO + var mousePosition = main.Window.getMousePosition(); + mousePosition -= self.pos; + mousePosition /= @splat(2, scale); + if(mousePosition[1] < 16) { + grabPosition = main.Window.getMousePosition(); + selfPositionWhenGrabbed = self.pos; + } else { + // TODO: Call window function. + } +} + +pub fn leftMouseButtonReleased(self: *const GuiWindow) void { + _ = self; // TODO + grabPosition = null; +} + +fn snapToOtherWindow(self: *GuiWindow) void { + const scale = @floor(settings.guiScale*self.scale); // TODO + for(self.relativePosition) |*relPos, i| { + var minDist: f32 = settings.guiScale*2; + var minWindow: ?*GuiWindow = null; + var selfAttachment: AttachmentPoint = undefined; + var otherAttachment: AttachmentPoint = undefined; + outer: for(gui.openWindows.items) |other| { + // Check if they touch: + const start = @max(self.pos[i^1], other.pos[i^1]); + const end = @min(self.pos[i^1] + self.size[i^1]*scale, other.pos[i^1] + other.size[i^1]*@floor(settings.guiScale*other.scale)); + if(start >= end) continue; + // Detect cycles: + var win: ?*GuiWindow = other; + while(win) |_win| { + if(win == self) continue :outer; + switch(_win.relativePosition[i]) { + .ratio => { + win = null; + }, + .attachedToFrame => { + win = null; + }, + .relativeToWindow => |relativeToWindow| { + win = relativeToWindow.reference; + }, + .attachedToWindow => |attachedToWindow| { + win = attachedToWindow.reference; + }, + } + } + + const dist1 = @fabs(self.pos[i] - other.pos[i] - other.size[i]*@floor(settings.guiScale*other.scale)); // TODO: scale + if(dist1 < minDist) { + minDist = dist1; + minWindow = other; + selfAttachment = .lower; + otherAttachment = .upper; + } + const dist2 = @fabs(self.pos[i] + self.size[i]*scale - other.pos[i]); + if(dist2 < minDist) { + minDist = dist2; + minWindow = other; + selfAttachment = .upper; + otherAttachment = .lower; + } + } + if(minWindow) |other| { + relPos.* = .{.attachedToWindow = .{.reference = other, .selfAttachmentPoint = selfAttachment, .otherAttachmentPoint = otherAttachment}}; + } + } +} + +fn positionRelativeToFrame(self: *GuiWindow) void { + const scale = @floor(settings.guiScale*self.scale); // TODO + const windowSize = main.Window.getWindowSize(); + for(self.relativePosition) |*relPos, i| { + // Snap to the center: + if(@fabs(self.pos[i] + self.size[i]*scale - windowSize[i]/2) <= settings.guiScale*2) { + relPos.* = .{.attachedToFrame = .{ + .selfAttachmentPoint = .upper, + .otherAttachmentPoint = .middle, + }}; + } else if(@fabs(self.pos[i] + self.size[i]*scale/2 - windowSize[i]/2) <= settings.guiScale*2) { + relPos.* = .{.attachedToFrame = .{ + .selfAttachmentPoint = .middle, + .otherAttachmentPoint = .middle, + }}; + } else if(@fabs(self.pos[i] - windowSize[i]/2) <= settings.guiScale*2) { + relPos.* = .{.attachedToFrame = .{ + .selfAttachmentPoint = .lower, + .otherAttachmentPoint = .middle, + }}; + } else { + var ratio: f32 = (self.pos[i] + self.size[i]*scale/2)/windowSize[i]; + if(self.pos[i] <= 0) { + ratio = 0; + } else if(self.pos[i] + self.size[i]*scale >= windowSize[i]) { + ratio = 1; + } + relPos.* = .{.ratio = ratio}; + } + } +} + +fn positionRelativeToConnectedWindow(self: *GuiWindow, other: *GuiWindow, i: usize) void { + const scale = @floor(settings.guiScale*self.scale); // TODO + const otherSize = other.size*@splat(2, @floor(settings.guiScale*other.scale)); // TODO: scale + const relPos = &self.relativePosition[i]; + // Snap to the center: + if(@fabs(self.pos[i] + self.size[i]*scale - (other.pos[i] + otherSize[i]/2)) <= settings.guiScale*2) { + relPos.* = .{.attachedToWindow = .{ + .reference = other, + .selfAttachmentPoint = .upper, + .otherAttachmentPoint = .middle, + }}; + } else if(@fabs(self.pos[i] + self.size[i]*scale/2 - (other.pos[i] + otherSize[i]/2)) <= settings.guiScale*2) { + relPos.* = .{.attachedToWindow = .{ + .reference = other, + .selfAttachmentPoint = .middle, + .otherAttachmentPoint = .middle, + }}; + } else if(@fabs(self.pos[i] - (other.pos[i] + otherSize[i]/2)) <= settings.guiScale*2) { + relPos.* = .{.attachedToWindow = .{ + .reference = other, + .selfAttachmentPoint = .lower, + .otherAttachmentPoint = .middle, + }}; + // Snap to the edges: + } else if(@fabs(self.pos[i] - other.pos[i]) <= settings.guiScale*2) { + relPos.* = .{.attachedToWindow = .{ + .reference = other, + .selfAttachmentPoint = .lower, + .otherAttachmentPoint = .lower, + }}; + } else if(@fabs(self.pos[i] + self.size[i]*scale - (other.pos[i] + otherSize[i])) <= settings.guiScale*2) { + relPos.* = .{.attachedToWindow = .{ + .reference = other, + .selfAttachmentPoint = .upper, + .otherAttachmentPoint = .upper, + }}; + } else { + self.relativePosition[i] = .{.relativeToWindow = .{ + .reference = other, + .ratio = (self.pos[i] + self.size[i]*scale/2 - other.pos[i])/otherSize[i] + }}; + } +} + +pub fn update(self: *GuiWindow) !void { + const scale = @floor(settings.guiScale*self.scale); // TODO + const mousePosition = main.Window.getMousePosition(); + const windowSize = main.Window.getWindowSize(); + if(grabPosition) |_grabPosition| { + self.relativePosition[0] = .{.ratio = undefined}; + self.relativePosition[1] = .{.ratio = undefined}; + self.pos = (mousePosition - _grabPosition) + selfPositionWhenGrabbed; + self.snapToOtherWindow(); + if(self.relativePosition[0] == .ratio and self.relativePosition[1] == .ratio) { + self.positionRelativeToFrame(); + } else if(self.relativePosition[0] == .ratio) { + self.positionRelativeToConnectedWindow(self.relativePosition[1].attachedToWindow.reference, 0); + } else if(self.relativePosition[1] == .ratio) { + self.positionRelativeToConnectedWindow(self.relativePosition[0].attachedToWindow.reference, 1); + } + self.pos = @max(self.pos, Vec2f{0, 0}); + self.pos = @min(self.pos, windowSize - self.size*@splat(2, scale)); + gui.updateWindowPositions(); + } +} + +pub fn updateWindowPosition(self: *GuiWindow) void { + const scale = @floor(settings.guiScale*self.scale); // TODO + const windowSize = main.Window.getWindowSize(); + for(self.relativePosition) |relPos, i| { + switch(relPos) { + .ratio => |ratio| { + self.pos[i] = windowSize[i]*ratio - self.size[i]*scale/2; + self.pos[i] = @max(self.pos[i], 0); + self.pos[i] = @min(self.pos[i], windowSize[i] - self.size[i]*scale); + }, + .attachedToFrame => |attachedToFrame| { + const otherPos = switch(attachedToFrame.otherAttachmentPoint) { + .lower => 0, + .middle => 0.5*windowSize[i], + .upper => windowSize[i], + }; + self.pos[i] = switch(attachedToFrame.selfAttachmentPoint) { + .lower => otherPos, + .middle => otherPos - 0.5*self.size[i]*scale, + .upper => otherPos - self.size[i]*scale, + }; + }, + .attachedToWindow => |attachedToWindow| { + const other = attachedToWindow.reference; + const otherPos = switch(attachedToWindow.otherAttachmentPoint) { + .lower => other.pos[i], + .middle => other.pos[i] + 0.5*other.size[i]*@floor(settings.guiScale*other.scale), // TODO: scale + .upper => other.pos[i] + other.size[i]*@floor(settings.guiScale*other.scale), // TODO: scale + }; + self.pos[i] = switch(attachedToWindow.selfAttachmentPoint) { + .lower => otherPos, + .middle => otherPos - 0.5*self.size[i]*scale, + .upper => otherPos - self.size[i]*scale, + }; + }, + .relativeToWindow => |relativeToWindow| { + const other = relativeToWindow.reference; + const otherSize = other.size[i]*@floor(settings.guiScale*other.scale); // TODO: scale + const otherPos = other.pos[i]; + self.pos[i] = otherPos + relativeToWindow.ratio*otherSize - self.size[i]*scale/2; + }, + } + } +} + +fn drawOrientationLines(self: *const GuiWindow) void { + const scale = @floor(settings.guiScale*self.scale); // TODO + draw.setColor(0x80000000); + const windowSize = main.Window.getWindowSize(); + for(self.relativePosition) |relPos, i| { + switch(relPos) { + .ratio, .relativeToWindow => { + continue; + }, + .attachedToFrame => |attachedToFrame| { + const pos = switch(attachedToFrame.otherAttachmentPoint) { + .lower => 0, + .middle => 0.5*windowSize[i], + .upper => windowSize[i], + }; + if(i == 0) { + draw.line(.{pos, 0}, .{pos, windowSize[i^1]}); + } else { + draw.line(.{0, pos}, .{windowSize[i^1], pos}); + } + }, + .attachedToWindow => |attachedToWindow| { + const other = attachedToWindow.reference; + const otherSize = other.size*@splat(2, @floor(settings.guiScale*other.scale)); // TODO: scale + const pos = switch(attachedToWindow.otherAttachmentPoint) { + .lower => other.pos[i], + .middle => other.pos[i] + 0.5*otherSize[i], + .upper => other.pos[i] + otherSize[i], + }; + const start = @min(self.pos[i^1], other.pos[i^1]); + const end = @max(self.pos[i^1] + self.size[i^1]*scale, other.pos[i^1] + otherSize[i^1]); + if(i == 0) { + draw.line(.{pos, start}, .{pos, end}); + } else { + draw.line(.{start, pos}, .{end, pos}); + } + }, + } + } +} + +pub fn render(self: *const GuiWindow) !void { + const scale = @floor(settings.guiScale*self.scale); // TODO + draw.setColor(0xff808080); + draw.rect(self.pos, self.size*@splat(2, scale)); + if(self.showTitleBar) { + var text = try graphics.TextBuffer.init(main.threadAllocator, self.title, .{}, false); + defer text.deinit(); + const titleDimension = try text.calculateLineBreaks(16*scale, self.size[0]*scale); + if(self == gui.selectedWindow) { + draw.setColor(0xff80b080); + } else { + draw.setColor(0xffb08080); + } + draw.rect(self.pos, Vec2f{self.size[0]*scale, titleDimension[1]}); + try text.render(self.pos[0] + self.size[0]*scale/2 - titleDimension[0]/2, self.pos[1], 16*scale); + + } + if(self == gui.selectedWindow and grabPosition != null) { + self.drawOrientationLines(); + } +} \ No newline at end of file diff --git a/src/gui/gui.zig b/src/gui/gui.zig new file mode 100644 index 00000000..02f80fa3 --- /dev/null +++ b/src/gui/gui.zig @@ -0,0 +1,142 @@ +const std = @import("std"); + +const main = @import("root"); +const graphics = main.graphics; +const draw = graphics.draw; +const settings = main.settings; +const vec = main.vec; +const Vec2f = vec.Vec2f; + +pub const GuiWindow = @import("GuiWindow.zig"); + +pub const hotbar = @import("windows/hotbar.zig"); +pub const healthbar = @import("windows/healthbar.zig"); + +var windowList: std.ArrayList(*GuiWindow) = undefined; +var hudWindows: std.ArrayList(*GuiWindow) = undefined; +pub var openWindows: std.ArrayList(*GuiWindow) = undefined; +pub var selectedWindow: ?*GuiWindow = null; // TODO: Make private. + +pub fn init() !void { + windowList = std.ArrayList(*GuiWindow).init(main.globalAllocator); + hudWindows = std.ArrayList(*GuiWindow).init(main.globalAllocator); + openWindows = std.ArrayList(*GuiWindow).init(main.globalAllocator); + try hotbar.init(); + try healthbar.init(); +} + +pub fn deinit() void { + windowList.deinit(); + hudWindows.deinit(); + openWindows.deinit(); +} + +pub fn addWindow(window: *GuiWindow, isHudWindow: bool) !void { + for(windowList.items) |other| { + if(std.mem.eql(u8, window.id, other.id)) { + std.log.err("Duplicate window id: {s}", .{window.id}); + return; + } + } + if(isHudWindow) { + try hudWindows.append(window); + window.showTitleBar = false; + } + try windowList.append(window); +} + +pub fn openWindow(id: []const u8) !void { + defer updateWindowPositions(); + var wasFound: bool = false; + outer: for(windowList.items) |window| { + if(std.mem.eql(u8, window.id, id)) { + wasFound = true; + for(openWindows.items) |_openWindow| { + if(_openWindow == window) { + std.log.warn("Window with id {s} is already open.", .{id}); + continue :outer; + } + } + window.showTitleBar = true; + try openWindows.append(window); + window.pos = .{0, 0}; + window.size = window.contentSize; + window.onOpenFn(); + return; + } + } + std.log.warn("Could not find window with id {s}.", .{id}); +} + +pub fn closeWindow(window: *GuiWindow) void { + defer updateWindowPositions(); + if(selectedWindow == window) { + selectedWindow = null; + } + for(openWindows.items) |_openWindow, i| { + if(_openWindow == window) { + openWindows.swapRemove(i); + } + } + window.onCloseFn(); +} + +pub fn leftMouseButtonPressed() void { + selectedWindow = null; + var selectedI: usize = 0; + for(openWindows.items) |window, i| { + var mousePosition = main.Window.getMousePosition(); + mousePosition -= window.pos; + mousePosition /= @splat(2, window.scale*settings.guiScale); + if(@reduce(.And, mousePosition >= Vec2f{0, 0}) and @reduce(.And, mousePosition < window.size)) { + selectedWindow = window; + selectedI = i; + } + } + if(selectedWindow) |_selectedWindow| { + _selectedWindow.leftMouseButtonPressed(); + _ = openWindows.orderedRemove(selectedI); + openWindows.appendAssumeCapacity(_selectedWindow); + } +} + +pub fn leftMouseButtonReleased() void { + var oldWindow = selectedWindow; + selectedWindow = null; + for(openWindows.items) |window| { + var mousePosition = main.Window.getMousePosition(); + mousePosition -= window.pos; + mousePosition /= @splat(2, window.scale*settings.guiScale); + if(@reduce(.And, mousePosition >= Vec2f{0, 0}) and @reduce(.And, mousePosition < window.size)) { + selectedWindow = window; + } + } + if(selectedWindow != oldWindow) { // Unselect the window if the mouse left it. + selectedWindow = null; + } + if(oldWindow) |_oldWindow| { + _oldWindow.leftMouseButtonReleased(); + } +} + +pub fn updateWindowPositions() void { + var wasChanged: bool = false; + for(openWindows.items) |window| { + const oldPos = window.pos; + window.updateWindowPosition(); + const newPos = window.pos; + if(vec.lengthSquare(oldPos - newPos) >= 1e-3) { + wasChanged = true; + } + } + if(wasChanged) @call(.always_tail, updateWindowPositions, .{}); // Very efficient O(n²) algorithm :P +} + +pub fn updateAndRenderGui() !void { + if(selectedWindow) |selected| { + try selected.update(); + } + for(openWindows.items) |window| { + try window.render(); + } +} \ No newline at end of file diff --git a/src/gui/windows/healthbar.zig b/src/gui/windows/healthbar.zig new file mode 100644 index 00000000..f5fddb0e --- /dev/null +++ b/src/gui/windows/healthbar.zig @@ -0,0 +1,25 @@ +const main = @import("root"); +const Vec2f = main.vec.Vec2f; + +const gui = @import("../gui.zig"); +const GuiWindow = gui.GuiWindow; + +var healthbarWindow: GuiWindow = undefined; +pub fn init() !void { + healthbarWindow = GuiWindow{ + .contentSize = Vec2f{128, 16}, + .title = "Health Bar", + .id = "cubyz:healthbar", + .renderFn = &render, + .updateFn = &update, + }; + try gui.addWindow(&healthbarWindow, true); +} + +pub fn render() void { + +} + +pub fn update() void { + +} \ No newline at end of file diff --git a/src/gui/windows/hotbar.zig b/src/gui/windows/hotbar.zig new file mode 100644 index 00000000..e7a82775 --- /dev/null +++ b/src/gui/windows/hotbar.zig @@ -0,0 +1,44 @@ + +const main = @import("root"); +const Vec2f = main.vec.Vec2f; + +const gui = @import("../gui.zig"); +const GuiWindow = gui.GuiWindow; + +var hotbarWindow: GuiWindow = undefined; +var hotbarWindow2: GuiWindow = undefined; +var hotbarWindow3: GuiWindow = undefined; +pub fn init() !void { + hotbarWindow = GuiWindow{ + .contentSize = Vec2f{64*8, 64}, + .title = "Hotbar", + .id = "cubyz:hotbar", + .renderFn = &render, + .updateFn = &update, + }; + try gui.addWindow(&hotbarWindow, true); + hotbarWindow2 = GuiWindow{ + .contentSize = Vec2f{64*8, 64}, + .title = "Hotbar2", + .id = "cubyz:hotbar2", + .renderFn = &render, + .updateFn = &update, + }; + try gui.addWindow(&hotbarWindow2, true); + hotbarWindow3 = GuiWindow{ + .contentSize = Vec2f{64*8, 64}, + .title = "Hotbar3", + .id = "cubyz:hotbar3", + .renderFn = &render, + .updateFn = &update, + }; + try gui.addWindow(&hotbarWindow3, true); +} + +pub fn render() void { + +} + +pub fn update() void { + +} \ No newline at end of file diff --git a/src/main.zig b/src/main.zig index 6d8fefca..bd38bd22 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,22 +1,27 @@ const std = @import("std"); -const assets = @import("assets.zig"); -const blocks = @import("blocks.zig"); -const chunk = @import("chunk.zig"); -const entity = @import("entity.zig"); -const game = @import("game.zig"); -const graphics = @import("graphics.zig"); -const itemdrop = @import("itemdrop.zig"); -const items = @import("items.zig"); -const models = @import("models.zig"); -const network = @import("network.zig"); -const renderer = @import("renderer.zig"); -const rotation = @import("rotation.zig"); -const settings = @import("settings.zig"); -const utils = @import("utils.zig"); +const gui = @import("gui"); -const Vec2f = @import("vec.zig").Vec2f; -const Vec3d = @import("vec.zig").Vec3d; +pub const assets = @import("assets.zig"); +pub const blocks = @import("blocks.zig"); +pub const chunk = @import("chunk.zig"); +pub const entity = @import("entity.zig"); +pub const game = @import("game.zig"); +pub const graphics = @import("graphics.zig"); +pub const itemdrop = @import("itemdrop.zig"); +pub const items = @import("items.zig"); +pub const JsonElement = @import("json.zig"); +pub const models = @import("models.zig"); +pub const network = @import("network.zig"); +pub const random = @import("random.zig"); +pub const renderer = @import("renderer.zig"); +pub const rotation = @import("rotation.zig"); +pub const settings = @import("settings.zig"); +pub const utils = @import("utils.zig"); +pub const vec = @import("vec.zig"); + +const Vec2f = vec.Vec2f; +const Vec3d = vec.Vec3d; pub const c = @cImport ({ @cInclude("glad/glad.h"); @@ -31,8 +36,8 @@ pub var threadPool: utils.ThreadPool = undefined; var logFile: std.fs.File = undefined; // overwrite the log function: pub const std_options = struct { - pub const log_level = .debug; - pub fn logFn( + pub const log_level = .debug; + pub fn logFn( comptime level: std.log.Level, comptime _: @Type(.EnumLiteral), comptime format: []const u8, @@ -59,8 +64,10 @@ pub const std_options = struct { const Key = struct { pressed: bool = false, key: c_int = c.GLFW_KEY_UNKNOWN, + mouseButton: c_int = -1, scancode: c_int = 0, releaseAction: ?*const fn() void = null, + pressAction: ?*const fn() void = null, }; pub var keyboard: struct { forward: Key = Key{.key = c.GLFW_KEY_W}, @@ -71,6 +78,9 @@ pub var keyboard: struct { jump: Key = Key{.key = c.GLFW_KEY_SPACE}, fall: Key = Key{.key = c.GLFW_KEY_LEFT_SHIFT}, fullscreen: Key = Key{.key = c.GLFW_KEY_F11, .releaseAction = &Window.toggleFullscreen}, + leftMouseButton: Key = Key{.mouseButton = c.GLFW_MOUSE_BUTTON_LEFT, .pressAction = &gui.leftMouseButtonPressed, .releaseAction = &gui.leftMouseButtonReleased}, + rightMouseButton: Key = Key{.mouseButton = c.GLFW_MOUSE_BUTTON_RIGHT}, + middleMouseButton: Key = Key{.mouseButton = c.GLFW_MOUSE_BUTTON_MIDDLE}, } = .{}; pub const Window = struct { @@ -90,6 +100,9 @@ pub const Window = struct { if(key == @field(keyboard, field.name).key) { if(key != c.GLFW_KEY_UNKNOWN or scancode == @field(keyboard, field.name).scancode) { @field(keyboard, field.name).pressed = true; + if(@field(keyboard, field.name).pressAction) |pressAction| { + pressAction(); + } } } } @@ -111,6 +124,7 @@ pub const Window = struct { width = @intCast(u31, newWidth); height = @intCast(u31, newHeight); renderer.updateViewport(width, height, settings.fov); + gui.updateWindowPositions(); } // Mouse deltas are averaged over multiple frames using a circular buffer: const deltasLen: u2 = 3; @@ -137,6 +151,28 @@ pub const Window = struct { ignoreDataAfterRecentGrab = false; currentPos = newPos; } + fn mouseButton(_: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.C) void { + _ = mods; + if(action == c.GLFW_PRESS) { + inline for(@typeInfo(@TypeOf(keyboard)).Struct.fields) |field| { + if(button == @field(keyboard, field.name).mouseButton) { + @field(keyboard, field.name).pressed = true; + if(@field(keyboard, field.name).pressAction) |pressAction| { + pressAction(); + } + } + } + } else if(action == c.GLFW_RELEASE) { + inline for(@typeInfo(@TypeOf(keyboard)).Struct.fields) |field| { + if(button == @field(keyboard, field.name).mouseButton) { + @field(keyboard, field.name).pressed = false; + if(@field(keyboard, field.name).releaseAction) |releaseAction| { + releaseAction(); + } + } + } + } + } fn glDebugOutput(_: c_uint, _: c_uint, _: c_uint, severity: c_uint, length: c_int, message: [*c]const u8, _: ?*const anyopaque) callconv(.C) void { if(severity == c.GL_DEBUG_SEVERITY_HIGH) { std.log.err("OpenGL {}:{s}", .{severity, message[0..@intCast(usize, length)]}); @@ -162,6 +198,14 @@ pub const Window = struct { } } + pub fn getMousePosition() Vec2f { + return GLFWCallbacks.currentPos; + } + + pub fn getWindowSize() Vec2f { + return Vec2f{@intToFloat(f32, width), @intToFloat(f32, height)}; + } + fn init() !void { _ = c.glfwSetErrorCallback(GLFWCallbacks.errorCallback); @@ -180,6 +224,7 @@ pub const Window = struct { _ = c.glfwSetKeyCallback(window, GLFWCallbacks.keyCallback); _ = c.glfwSetFramebufferSizeCallback(window, GLFWCallbacks.framebufferSize); _ = c.glfwSetCursorPosCallback(window, GLFWCallbacks.cursorPosition); + _ = c.glfwSetMouseButtonCallback(window, GLFWCallbacks.mouseButton); c.glfwMakeContextCurrent(window); @@ -257,6 +302,37 @@ pub fn main() !void { try graphics.init(); defer graphics.deinit(); + try gui.init(); + defer gui.deinit(); + try gui.openWindow("cubyz:hotbar"); + try gui.openWindow("cubyz:hotbar2"); + try gui.openWindow("cubyz:hotbar3"); + try gui.openWindow("cubyz:healthbar"); + + c.glCullFace(c.GL_BACK); + c.glEnable(c.GL_BLEND); + c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); + + while(c.glfwWindowShouldClose(Window.window) == 0) { + { // Check opengl errors: + const err = c.glGetError(); + if(err != 0) { + std.log.err("Got opengl error: {}", .{err}); + } + } + c.glfwSwapBuffers(Window.window); + c.glfwPollEvents(); + + { // Render the GUI + c.glClearColor(0.5, 1, 1, 1); + c.glClear(c.GL_DEPTH_BUFFER_BIT | c.GL_STENCIL_BUFFER_BIT | c.GL_COLOR_BUFFER_BIT); + c.glDisable(c.GL_CULL_FACE); + c.glDisable(c.GL_DEPTH_TEST); + try gui.updateAndRenderGui(); + } + } + if(true) return; // TODO + try rotation.init(); defer rotation.deinit(); @@ -333,20 +409,14 @@ pub fn main() !void { { // Render the GUI c.glDisable(c.GL_CULL_FACE); c.glDisable(c.GL_DEPTH_TEST); - + try gui.updateAndRenderGui(); //const dim = try buffer2.calculateLineBreaks(32, 200); //try buffer.render(100, 200, 32); - //graphics.Draw.setColor(0xff008000); - //graphics.Draw.rect(.{100, 400}, .{200, dim[1]}); + //graphics.draw.setColor(0xff008000); + //graphics.draw.rect(.{100, 400}, .{200, dim[1]}); //try buffer2.render(100, 400, 32); //_ = try buffer3.calculateLineBreaks(32, 600); //try buffer3.render(400, 400, 32); - - //graphics.Draw.setColor(0xff0000ff); - //graphics.Draw.rect(Vec2f{.x = 100, .y = 100}, Vec2f{.x = 200, .y = 100}); - //graphics.Draw.circle(Vec2f{.x = 200, .y = 200}, 59); - //graphics.Draw.setColor(0xffff00ff); - //graphics.Draw.line(Vec2f{.x = 0, .y = 0}, Vec2f{.x = 1920, .y = 1080}); } } } diff --git a/src/renderer.zig b/src/renderer.zig index 844e264e..33839a2a 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -327,7 +327,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo // if(Window.getRenderTarget() != null) // Window.getRenderTarget().bind(); - c.glBindVertexArray(graphics.Draw.rectVAO); + c.glBindVertexArray(graphics.draw.rectVAO); c.glDisable(c.GL_DEPTH_TEST); c.glDisable(c.GL_CULL_FACE); c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); @@ -419,7 +419,7 @@ const Bloom = struct { colorExtractShader.bind(); buffers.bindTextures(); extractedBuffer.bind(); - c.glBindVertexArray(graphics.Draw.rectVAO); + c.glBindVertexArray(graphics.draw.rectVAO); c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); } @@ -428,7 +428,7 @@ const Bloom = struct { c.glActiveTexture(c.GL_TEXTURE3); c.glBindTexture(c.GL_TEXTURE_2D, extractedBuffer.texture); buffer1.bind(); - c.glBindVertexArray(graphics.Draw.rectVAO); + c.glBindVertexArray(graphics.draw.rectVAO); c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); } @@ -437,7 +437,7 @@ const Bloom = struct { c.glActiveTexture(c.GL_TEXTURE3); c.glBindTexture(c.GL_TEXTURE_2D, buffer1.texture); buffer2.bind(); - c.glBindVertexArray(graphics.Draw.rectVAO); + c.glBindVertexArray(graphics.draw.rectVAO); c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); } @@ -446,7 +446,7 @@ const Bloom = struct { c.glActiveTexture(c.GL_TEXTURE3); c.glBindTexture(c.GL_TEXTURE_2D, buffer2.texture); buffer1.bind(); - c.glBindVertexArray(graphics.Draw.rectVAO); + c.glBindVertexArray(graphics.draw.rectVAO); c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); } @@ -455,7 +455,7 @@ const Bloom = struct { c.glActiveTexture(c.GL_TEXTURE3); c.glBindTexture(c.GL_TEXTURE_2D, buffer1.texture); buffers.bind(); - c.glBindVertexArray(graphics.Draw.rectVAO); + c.glBindVertexArray(graphics.draw.rectVAO); c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); } diff --git a/src/settings.zig b/src/settings.zig index 37b66f31..f9127c61 100644 --- a/src/settings.zig +++ b/src/settings.zig @@ -31,6 +31,8 @@ pub var playerName: []const u8 = "quanturmdoelvloper"; pub var lastUsedIPAddress: []const u8 = "127.0.0.1"; +pub var guiScale: f32 = 2; + pub fn init() !void { const json = blk: { @@ -117,8 +119,6 @@ pub fn deinit() void { // // private static Language currentLanguage = null; // -// public static int GUI_SCALE = 2; -// // public static boolean musicOnOff = true; //Turn on or off the music // // /**Not actually a setting, but stored here anyways.*/