add imgui and tracy
This commit is contained in:
parent
11fc686caa
commit
0174a3f3f0
70
build.zig
70
build.zig
@ -1,9 +1,13 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const sokol = @import("sokol");
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) !void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const has_imgui = b.option(bool, "imgui", "ImGui integration") orelse (optimize == .Debug);
|
||||||
|
const has_tracy = b.option(bool, "tracy", "Tracy integration") orelse (optimize == .Debug);
|
||||||
|
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = "game_2025_12_13",
|
.name = "game_2025_12_13",
|
||||||
.root_module = b.createModule(.{
|
.root_module = b.createModule(.{
|
||||||
@ -12,6 +16,70 @@ pub fn build(b: *std.Build) void {
|
|||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
const exe_mod = exe.root_module;
|
||||||
|
|
||||||
|
const tracy_dependency = b.dependency("tracy", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.tracy_enable = has_tracy,
|
||||||
|
.tracy_only_localhost = true
|
||||||
|
});
|
||||||
|
exe_mod.linkLibrary(tracy_dependency.artifact("tracy"));
|
||||||
|
exe_mod.addImport("tracy", tracy_dependency.module("tracy"));
|
||||||
|
|
||||||
|
const stb_dependency = b.dependency("stb", .{});
|
||||||
|
exe_mod.addIncludePath(stb_dependency.path("."));
|
||||||
|
|
||||||
|
const sokol_c_dependency = b.dependency("sokol_c", .{});
|
||||||
|
exe_mod.addIncludePath(sokol_c_dependency.path("util"));
|
||||||
|
|
||||||
|
const fontstash_dependency = b.dependency("fontstash", .{});
|
||||||
|
exe_mod.addIncludePath(fontstash_dependency.path("src"));
|
||||||
|
|
||||||
|
const sokol_dependency = b.dependency("sokol", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.with_sokol_imgui = has_imgui
|
||||||
|
});
|
||||||
|
|
||||||
|
exe_mod.addImport("sokol", sokol_dependency.module("sokol"));
|
||||||
|
exe_mod.linkLibrary(sokol_dependency.artifact("sokol_clib"));
|
||||||
|
|
||||||
|
var cflags_buffer: [64][]const u8 = undefined;
|
||||||
|
var cflags = std.ArrayListUnmanaged([]const u8).initBuffer(&cflags_buffer);
|
||||||
|
switch (sokol.resolveSokolBackend(.auto, target.result)) {
|
||||||
|
.d3d11 => try cflags.appendBounded("-DSOKOL_D3D11"),
|
||||||
|
.metal => try cflags.appendBounded("-DSOKOL_METAL"),
|
||||||
|
.gl => try cflags.appendBounded("-DSOKOL_GLCORE"),
|
||||||
|
.gles3 => try cflags.appendBounded("-DSOKOL_GLES3"),
|
||||||
|
.wgpu => try cflags.appendBounded("-DSOKOL_WGPU"),
|
||||||
|
else => @panic("unknown sokol backend"),
|
||||||
|
}
|
||||||
|
exe_mod.addIncludePath(b.path("src/libs"));
|
||||||
|
exe_mod.addCSourceFile(.{
|
||||||
|
.file = b.path("src/libs/sokol_fontstash_impl.c"),
|
||||||
|
.flags = cflags.items
|
||||||
|
});
|
||||||
|
|
||||||
|
exe_mod.addCSourceFile(.{
|
||||||
|
.file = b.path("src/libs/stb_image.c"),
|
||||||
|
.flags = &.{}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (has_imgui) {
|
||||||
|
if (b.lazyDependency("cimgui", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
})) |cimgui_dependency| {
|
||||||
|
sokol_dependency.artifact("sokol_clib").addIncludePath(cimgui_dependency.path("src"));
|
||||||
|
exe_mod.addImport("cimgui", cimgui_dependency.module("cimgui"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = b.addOptions();
|
||||||
|
options.addOption(bool, "has_imgui", has_imgui);
|
||||||
|
options.addOption(bool, "has_tracy", has_tracy);
|
||||||
|
exe_mod.addOptions("options", options);
|
||||||
|
|
||||||
b.installArtifact(exe);
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
|||||||
@ -1,81 +1,38 @@
|
|||||||
.{
|
.{
|
||||||
// This is the default name used by packages depending on this one. For
|
|
||||||
// example, when a user runs `zig fetch --save <url>`, this field is used
|
|
||||||
// as the key in the `dependencies` table. Although the user can choose a
|
|
||||||
// different name, most users will stick with this provided value.
|
|
||||||
//
|
|
||||||
// It is redundant to include "zig" in this name because it is already
|
|
||||||
// within the Zig package namespace.
|
|
||||||
.name = .game_2025_12_13,
|
.name = .game_2025_12_13,
|
||||||
// This is a [Semantic Version](https://semver.org/).
|
|
||||||
// In a future version of Zig it will be used for package deduplication.
|
|
||||||
.version = "0.0.0",
|
.version = "0.0.0",
|
||||||
// Together with name, this represents a globally unique package
|
.fingerprint = 0x5704f7ae3ffdd7f8,
|
||||||
// identifier. This field is generated by the Zig toolchain when the
|
|
||||||
// package is first created, and then *never changes*. This allows
|
|
||||||
// unambiguous detection of one package being an updated version of
|
|
||||||
// another.
|
|
||||||
//
|
|
||||||
// When forking a Zig project, this id should be regenerated (delete the
|
|
||||||
// field and run `zig build`) if the upstream project is still maintained.
|
|
||||||
// Otherwise, the fork is *hostile*, attempting to take control over the
|
|
||||||
// original project's identity. Thus it is recommended to leave the comment
|
|
||||||
// on the following line intact, so that it shows up in code reviews that
|
|
||||||
// modify the field.
|
|
||||||
.fingerprint = 0x5704f7ae3ffdd7f8, // Changing this has security and trust implications.
|
|
||||||
// Tracks the earliest Zig version that the package considers to be a
|
|
||||||
// supported use case.
|
|
||||||
.minimum_zig_version = "0.15.2",
|
.minimum_zig_version = "0.15.2",
|
||||||
// This field is optional.
|
|
||||||
// Each dependency must either provide a `url` and `hash`, or a `path`.
|
|
||||||
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
|
|
||||||
// Once all dependencies are fetched, `zig build` no longer requires
|
|
||||||
// internet connectivity.
|
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
|
.sokol = .{
|
||||||
//.example = .{
|
.url = "git+https://github.com/floooh/sokol-zig.git#1e233203b41893a8bf9c1c91933eba98204b6ed8",
|
||||||
// // When updating this field to a new URL, be sure to delete the corresponding
|
.hash = "sokol-0.1.0-pb1HK42FNgDb5sqnsadiO2qabkfUX8jXP_DheOZGcD1W",
|
||||||
// // `hash`, otherwise you are communicating that you expect to find the old hash at
|
},
|
||||||
// // the new URL. If the contents of a URL change this will result in a hash mismatch
|
.cimgui = .{
|
||||||
// // which will prevent zig from using it.
|
.url = "git+https://github.com/floooh/dcimgui.git#33c99ef426b68030412b5a4b11487a23da9d4f13",
|
||||||
// .url = "https://example.com/foo.tar.gz",
|
.hash = "cimgui-0.1.0-44ClkQRJlABdFMKRqIG8KDD6jy1eQbgPO335NziPYjmL",
|
||||||
//
|
.lazy = true,
|
||||||
// // This is computed from the file contents of the directory of files that is
|
},
|
||||||
// // obtained after fetching `url` and applying the inclusion rules given by
|
.tracy = .{
|
||||||
// // `paths`.
|
.url = "git+https://github.com/sagehane/zig-tracy.git#80933723efe9bf840fe749b0bfc0d610f1db1669",
|
||||||
// //
|
.hash = "zig_tracy-0.0.5-aOIqsX1tAACKaRRB-sraMLuNiMASXi_y-4FtRuw4cTpx",
|
||||||
// // This field is the source of truth; packages do not come from a `url`; they
|
},
|
||||||
// // come from a `hash`. `url` is just one of many possible mirrors for how to
|
.stb = .{
|
||||||
// // obtain a package matching this `hash`.
|
.url = "git+https://github.com/nothings/stb.git#f1c79c02822848a9bed4315b12c8c8f3761e1296",
|
||||||
// //
|
.hash = "N-V-__8AABQ7TgCnPlp8MP4YA8znrjd6E-ZjpF1rvrS8J_2I",
|
||||||
// // Uses the [multihash](https://multiformats.io/multihash/) format.
|
},
|
||||||
// .hash = "...",
|
.sokol_c = .{
|
||||||
//
|
.url = "git+https://github.com/floooh/sokol.git#c66a1f04e6495d635c5e913335ab2308281e0492",
|
||||||
// // When this is provided, the package is found in a directory relative to the
|
.hash = "N-V-__8AAC3eYABB1DVLb4dkcEzq_xVeEZZugVfQ6DoNQBDN",
|
||||||
// // build root. In this case the package's hash is irrelevant and therefore not
|
},
|
||||||
// // computed. This field and `url` are mutually exclusive.
|
.fontstash = .{
|
||||||
// .path = "foo",
|
.url = "git+https://github.com/memononen/fontstash.git#b5ddc9741061343740d85d636d782ed3e07cf7be",
|
||||||
//
|
.hash = "N-V-__8AAA9xHgAxdLYPmlNTy6qzv9IYqiIePEHQUOPWYQ_6",
|
||||||
// // When this is set to `true`, a package is declared to be lazily
|
},
|
||||||
// // fetched. This makes the dependency only get fetched if it is
|
|
||||||
// // actually used.
|
|
||||||
// .lazy = false,
|
|
||||||
//},
|
|
||||||
},
|
},
|
||||||
// Specifies the set of files and directories that are included in this package.
|
|
||||||
// Only files and directories listed here are included in the `hash` that
|
|
||||||
// is computed for this package. Only files listed here will remain on disk
|
|
||||||
// when using the zig package manager. As a rule of thumb, one should list
|
|
||||||
// files required for compilation plus any license(s).
|
|
||||||
// Paths are relative to the build root. Use the empty string (`""`) to refer to
|
|
||||||
// the build root itself.
|
|
||||||
// A directory listed here means that all files within, recursively, are included.
|
|
||||||
.paths = .{
|
.paths = .{
|
||||||
"build.zig",
|
"build.zig",
|
||||||
"build.zig.zon",
|
"build.zig.zon",
|
||||||
"src",
|
"src",
|
||||||
// For example...
|
|
||||||
//"LICENSE",
|
|
||||||
//"README.md",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/assets/roboto-font/LICENSE.txt
Normal file
93
src/assets/roboto-font/LICENSE.txt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://openfontlicense.org
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
src/assets/roboto-font/Roboto-Bold.ttf
Normal file
BIN
src/assets/roboto-font/Roboto-Bold.ttf
Normal file
Binary file not shown.
BIN
src/assets/roboto-font/Roboto-Italic.ttf
Normal file
BIN
src/assets/roboto-font/Roboto-Italic.ttf
Normal file
Binary file not shown.
BIN
src/assets/roboto-font/Roboto-Regular.ttf
Normal file
BIN
src/assets/roboto-font/Roboto-Regular.ttf
Normal file
Binary file not shown.
1375
src/graphics.zig
Normal file
1375
src/graphics.zig
Normal file
File diff suppressed because it is too large
Load Diff
502
src/imgui.zig
Normal file
502
src/imgui.zig
Normal file
@ -0,0 +1,502 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Math = @import("./math.zig");
|
||||||
|
const options = @import("options");
|
||||||
|
pub const ig = @import("cimgui");
|
||||||
|
const Vec2 = Math.Vec2;
|
||||||
|
const Vec3 = Math.Vec3;
|
||||||
|
const Vec4 = Math.Vec4;
|
||||||
|
|
||||||
|
const sokol = @import("sokol");
|
||||||
|
const sapp = sokol.app;
|
||||||
|
const simgui = sokol.imgui;
|
||||||
|
|
||||||
|
const enabled = options.has_imgui;
|
||||||
|
|
||||||
|
var global_allocator: ?std.mem.Allocator = null;
|
||||||
|
|
||||||
|
pub const WindowOptions = struct {
|
||||||
|
name: [*c]const u8,
|
||||||
|
pos: ?Vec2 = null,
|
||||||
|
size: ?Vec2 = null,
|
||||||
|
collapsed: ?bool = null,
|
||||||
|
open: ?*bool = null
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SliderOptions = struct {
|
||||||
|
label: [*c]const u8,
|
||||||
|
value: *f32,
|
||||||
|
min: f32,
|
||||||
|
max: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn toImVec2(vec2: Vec2) ig.ImVec2 {
|
||||||
|
return ig.ImVec2{
|
||||||
|
.x = vec2.x,
|
||||||
|
.y = vec2.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn structCast(T: type, value: anytype) T {
|
||||||
|
return @as(*T, @ptrFromInt(@intFromPtr(&value))).*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(gpa: std.mem.Allocator, desc: simgui.Desc) void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global_allocator = gpa;
|
||||||
|
simgui.setup(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addFont(ttf_data: []const u8, font_size: f32) void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var font_config: ig.ImFontConfig = .{};
|
||||||
|
font_config.FontDataOwnedByAtlas = false;
|
||||||
|
font_config.OversampleH = 2;
|
||||||
|
font_config.OversampleV = 2;
|
||||||
|
font_config.GlyphMaxAdvanceX = std.math.floatMax(f32);
|
||||||
|
font_config.RasterizerMultiply = 1.0;
|
||||||
|
font_config.RasterizerDensity = 1.0;
|
||||||
|
font_config.EllipsisChar = 0;
|
||||||
|
|
||||||
|
const io = ig.igGetIO();
|
||||||
|
_ = ig.ImFontAtlas_AddFontFromMemoryTTF(
|
||||||
|
io.*.Fonts,
|
||||||
|
@constCast(@ptrCast(ttf_data.ptr)),
|
||||||
|
@intCast(ttf_data.len),
|
||||||
|
font_size,
|
||||||
|
&font_config,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
simgui.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handleEvent(ev: sapp.Event) bool {
|
||||||
|
if (enabled) {
|
||||||
|
return simgui.handleEvent(ev);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn newFrame(desc: simgui.FrameDesc) void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
simgui.newFrame(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
simgui.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn beginWindow(opts: WindowOptions) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.pos) |pos| {
|
||||||
|
ig.igSetNextWindowPos(toImVec2(pos), ig.ImGuiCond_Once);
|
||||||
|
}
|
||||||
|
if (opts.size) |size| {
|
||||||
|
ig.igSetNextWindowSize(toImVec2(size), ig.ImGuiCond_Once);
|
||||||
|
}
|
||||||
|
if (opts.collapsed) |collapsed| {
|
||||||
|
ig.igSetNextWindowCollapsed(collapsed, ig.ImGuiCond_Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igSetNextWindowBgAlpha(1);
|
||||||
|
|
||||||
|
var open = ig.igBegin(opts.name, opts.open, ig.ImGuiWindowFlags_None);
|
||||||
|
if (opts.open) |opts_open| {
|
||||||
|
if (opts_open.* == false) {
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!open) {
|
||||||
|
endWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
return open;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endWindow() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn textFmt(comptime fmt: []const u8, args: anytype) void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatted = std.fmt.allocPrintSentinel(global_allocator, fmt, args, 0) catch return;
|
||||||
|
defer global_allocator.free(formatted);
|
||||||
|
|
||||||
|
text(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text(text_z: [*c]const u8) void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igText("%s", text_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn beginDisabled(disabled: bool) void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igBeginDisabled(disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endDisabled() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igEndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn button(label: [*c]const u8) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igButton(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slider(opts: SliderOptions) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igSliderFloat(opts.label, opts.value, opts.min, opts.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checkbox(label: [*c]const u8, value: *bool) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igCheckbox(label, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn beginTabBar(id: [*c]const u8) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igBeginTabBar(id, ig.ImGuiTabBarFlags_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endTabBar() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igEndTabBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn beginTabItem(label: [*c]const u8) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igBeginTabItem(label, null, ig.ImGuiTabItemFlags_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endTabItem() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igEndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn beginGroup() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igBeginGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endGroup() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igEndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sameLine() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igSameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn beginTable(id: [*c]const u8, columns: u32, flags: ig.ImGuiTableFlags) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igBeginTable(id, @intCast(columns), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endTable() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igEndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tableNextColumn() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = ig.igTableNextColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tableNextRow() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = ig.igTableNextRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tableSetColumnIndex(index: usize) void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = ig.igTableSetColumnIndex(@intCast(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tableSetupColumn(label: [*c]const u8, flags: ig.ImGuiTableColumnFlags) void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igTableSetupColumn(label, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tableHeadersRow() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igTableHeadersRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ID = union(enum) {
|
||||||
|
string: []const u8,
|
||||||
|
int: i32
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn pushID(id: ID) void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
.string => |str| ig.igPushIDStr(str.ptr, str.ptr + str.len),
|
||||||
|
.int => |int| ig.igPushIDInt(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn popID() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igPopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const TreeNodeFlags = packed struct {
|
||||||
|
selected: bool = false,
|
||||||
|
framed: bool = false,
|
||||||
|
allow_overlap: bool = false,
|
||||||
|
no_tree_pushOnOpen: bool = false,
|
||||||
|
no_auto_open_on_log: bool = false,
|
||||||
|
default_open: bool = false,
|
||||||
|
open_on_double_click: bool = false,
|
||||||
|
open_on_arrow: bool = false,
|
||||||
|
leaf: bool = false,
|
||||||
|
bullet: bool = false,
|
||||||
|
frame_padding: bool = false,
|
||||||
|
span_avail_width: bool = false,
|
||||||
|
span_full_width: bool = false,
|
||||||
|
span_label_width: bool = false,
|
||||||
|
span_all_columns: bool = false,
|
||||||
|
label_span_all_columns: bool = false,
|
||||||
|
nav_left_jumps_back_here: bool = false,
|
||||||
|
collapsing_header: bool = false,
|
||||||
|
|
||||||
|
fn toInt(self: TreeNodeFlags) u32 {
|
||||||
|
// TODO: Try using comptime to reduce this duplication.
|
||||||
|
// Would be great if `toInt()` could be replaced with just a @bitCast
|
||||||
|
//
|
||||||
|
// If the underlying C enum is exhaustive, maybe a bitcast could be performed?
|
||||||
|
// If the order of enums is correct
|
||||||
|
const flags = .{
|
||||||
|
.{ self.selected, ig.ImGuiTreeNodeFlags_Selected },
|
||||||
|
.{ self.framed, ig.ImGuiTreeNodeFlags_Framed },
|
||||||
|
.{ self.allow_overlap, ig.ImGuiTreeNodeFlags_AllowOverlap },
|
||||||
|
.{ self.no_tree_pushOnOpen, ig.ImGuiTreeNodeFlags_NoTreePushOnOpen },
|
||||||
|
.{ self.no_auto_open_on_log, ig.ImGuiTreeNodeFlags_NoAutoOpenOnLog },
|
||||||
|
.{ self.default_open, ig.ImGuiTreeNodeFlags_DefaultOpen },
|
||||||
|
.{ self.open_on_double_click, ig.ImGuiTreeNodeFlags_OpenOnDoubleClick },
|
||||||
|
.{ self.open_on_arrow, ig.ImGuiTreeNodeFlags_OpenOnArrow },
|
||||||
|
.{ self.leaf, ig.ImGuiTreeNodeFlags_Leaf },
|
||||||
|
.{ self.bullet, ig.ImGuiTreeNodeFlags_Bullet },
|
||||||
|
.{ self.frame_padding, ig.ImGuiTreeNodeFlags_FramePadding },
|
||||||
|
.{ self.span_avail_width, ig.ImGuiTreeNodeFlags_SpanAvailWidth },
|
||||||
|
.{ self.span_full_width, ig.ImGuiTreeNodeFlags_SpanFullWidth },
|
||||||
|
.{ self.span_label_width, ig.ImGuiTreeNodeFlags_SpanLabelWidth },
|
||||||
|
.{ self.span_all_columns, ig.ImGuiTreeNodeFlags_SpanAllColumns },
|
||||||
|
.{ self.label_span_all_columns, ig.ImGuiTreeNodeFlags_LabelSpanAllColumns },
|
||||||
|
.{ self.nav_left_jumps_back_here, ig.ImGuiTreeNodeFlags_NavLeftJumpsBackHere },
|
||||||
|
.{ self.collapsing_header, ig.ImGuiTreeNodeFlags_CollapsingHeader },
|
||||||
|
};
|
||||||
|
|
||||||
|
var sum: u32 = 0;
|
||||||
|
inline for (flags) |flag_pair| {
|
||||||
|
if (flag_pair[0]) {
|
||||||
|
sum += flag_pair[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn treeNode(label: [*c]const u8, flags: TreeNodeFlags) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igTreeNodeEx(label, @intCast(flags.toInt()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn treePop() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igTreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isItemClicked() bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igIsItemClicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isItemToggledOpen() bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igIsItemToggledOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn colorPicker4(label: [*c]const u8, color: *Vec4) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igColorPicker4(label, color.asArray().ptr, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn colorEdit4(label: [*c]const u8, color: *Vec4) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igColorEdit4(label, color.asArray().ptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn beginCombo(label: [*c]const u8, preview_value: [*c]const u8) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igBeginCombo(label, preview_value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endCombo() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igEndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selectable(label: [*c]const u8, selected: bool) bool {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ig.igSelectableEx(label, selected, 0, .{ });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setItemDefaultFocus() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igSetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn combo(label: [*c]const u8, items: []const [*c]const u8, selected: *usize) void {
|
||||||
|
if (beginCombo(label, items[selected.*])) {
|
||||||
|
defer endCombo();
|
||||||
|
|
||||||
|
for (0.., items) |i, item| {
|
||||||
|
const is_selected = selected.* == i;
|
||||||
|
if (selectable(item, is_selected)) {
|
||||||
|
selected.* = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_selected) {
|
||||||
|
setItemDefaultFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn separator() void {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ig.igSeparator();
|
||||||
|
}
|
||||||
85
src/libs/sokol_fontstash_impl.c
Normal file
85
src/libs/sokol_fontstash_impl.c
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#include "stdlib.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "stdbool.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#define FONTSTASH_IMPLEMENTATION
|
||||||
|
#include "fontstash.h"
|
||||||
|
|
||||||
|
#include "sokol/sokol_gfx.h"
|
||||||
|
#include "sokol/sokol_gl.h"
|
||||||
|
#define SOKOL_FONTSTASH_IMPL
|
||||||
|
#include "sokol_fontstash.h"
|
||||||
|
|
||||||
|
typedef struct FONSstate FONSstate;
|
||||||
|
|
||||||
|
// Expose private functions so that `getTextBoundsUtf8` could be implemented in zig
|
||||||
|
FONSstate* zig_fons__getState(FONScontext* stash)
|
||||||
|
{
|
||||||
|
return fons__getState(stash);
|
||||||
|
}
|
||||||
|
|
||||||
|
FONSfont* zig_getFont(FONScontext* stash, int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= stash->nfonts) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
FONSfont *font = stash->fonts[index];
|
||||||
|
if (font->data == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool zig_isTopLeft(FONScontext* stash)
|
||||||
|
{
|
||||||
|
return stash->params.flags & FONS_ZERO_TOPLEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int zig_getGlyphIndex(FONSglyph* glyph)
|
||||||
|
{
|
||||||
|
return glyph->index;
|
||||||
|
}
|
||||||
|
|
||||||
|
float zig_fons__tt_getPixelHeightScale(FONSfont *font, float size)
|
||||||
|
{
|
||||||
|
return fons__tt_getPixelHeightScale(&font->font, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
float zig_fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize)
|
||||||
|
{
|
||||||
|
return fons__getVertAlign(stash, font, align, isize);
|
||||||
|
}
|
||||||
|
|
||||||
|
FONSglyph* zig_fons__getGlyph(
|
||||||
|
FONScontext* stash,
|
||||||
|
FONSfont* font,
|
||||||
|
unsigned int codepoint,
|
||||||
|
short isize,
|
||||||
|
short iblur
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return fons__getGlyph(stash, font, codepoint, isize, iblur);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zig_fons__getQuad(
|
||||||
|
FONScontext* stash,
|
||||||
|
FONSfont* font,
|
||||||
|
int prevGlyphIndex, FONSglyph* glyph,
|
||||||
|
float scale,
|
||||||
|
float spacing,
|
||||||
|
float* x, float* y,
|
||||||
|
FONSquad* q
|
||||||
|
)
|
||||||
|
{
|
||||||
|
fons__getQuad(
|
||||||
|
stash,
|
||||||
|
font,
|
||||||
|
prevGlyphIndex,
|
||||||
|
glyph,
|
||||||
|
scale,
|
||||||
|
spacing,
|
||||||
|
x, y,
|
||||||
|
q
|
||||||
|
);
|
||||||
|
}
|
||||||
2
src/libs/stb_image.c
Normal file
2
src/libs/stb_image.c
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
326
src/main.zig
326
src/main.zig
@ -1,6 +1,328 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const tracy = @import("tracy");
|
||||||
|
const Gfx = @import("./graphics.zig");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const Window = @import("./window.zig");
|
||||||
|
const Event = Window.Event;
|
||||||
|
const MouseButton = Window.MouseButton;
|
||||||
|
|
||||||
|
const Math = @import("./math.zig");
|
||||||
|
const Vec2 = Math.Vec2;
|
||||||
|
|
||||||
|
const sokol = @import("sokol");
|
||||||
|
const slog = sokol.log;
|
||||||
|
const sg = sokol.gfx;
|
||||||
|
const sapp = sokol.app;
|
||||||
|
const sglue = sokol.glue;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.ui);
|
||||||
|
|
||||||
|
var window: Window = undefined;
|
||||||
|
var event_queue: std.ArrayListUnmanaged(Event) = .empty;
|
||||||
|
var mouse_inside_window: bool = false;
|
||||||
|
var event_queue_full_shown = false;
|
||||||
|
var sokol_logger: sapp.Logger = .{ .func = sokolLogCallback };
|
||||||
|
|
||||||
|
fn signalHandler(sig: i32) callconv(.c) void {
|
||||||
|
_ = sig;
|
||||||
|
sapp.requestQuit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initErrorable() !void {
|
||||||
|
// const allocator = window.app.allocator;
|
||||||
|
|
||||||
|
try Gfx.init(.{
|
||||||
|
.allocator = window.gpa,
|
||||||
|
.logger = @as(*sokol.gfx.Logger, @ptrFromInt(@intFromPtr(&sokol_logger))).*,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn init() void {
|
||||||
|
var zone = tracy.initZone(@src(), .{ });
|
||||||
|
defer zone.deinit();
|
||||||
|
|
||||||
|
initErrorable() catch |e| {
|
||||||
|
log.err("init() failed: {}", .{e});
|
||||||
|
sapp.requestQuit();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cStrToZig(c_str: [*c]const u8) [:0]const u8 {
|
||||||
|
return @import("std").mem.span(c_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sokolLogFmt(log_level: u32, comptime format: []const u8, args: anytype) void {
|
||||||
|
const log_sokol = std.log.scoped(.sokol);
|
||||||
|
|
||||||
|
if (log_level == 0) {
|
||||||
|
log_sokol.err(format, args);
|
||||||
|
} else if (log_level == 1) {
|
||||||
|
log_sokol.err(format, args);
|
||||||
|
} else if (log_level == 2) {
|
||||||
|
log_sokol.warn(format, args);
|
||||||
|
} else {
|
||||||
|
log_sokol.info(format, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sokolLogCallback(tag: [*c]const u8, log_level: u32, log_item: u32, message: [*c]const u8, line_nr: u32, filename: [*c]const u8, user_data: ?*anyopaque) callconv(.c) void {
|
||||||
|
_ = user_data;
|
||||||
|
|
||||||
|
if (filename != null) {
|
||||||
|
const format = "[{s}][id:{}] {s}:{}: {s}";
|
||||||
|
const args = .{
|
||||||
|
cStrToZig(tag orelse "-"),
|
||||||
|
log_item,
|
||||||
|
std.fs.path.basename(cStrToZig(filename orelse "-")),
|
||||||
|
line_nr,
|
||||||
|
cStrToZig(message orelse "")
|
||||||
|
};
|
||||||
|
|
||||||
|
sokolLogFmt(log_level, format, args);
|
||||||
|
} else {
|
||||||
|
const format = "[{s}][id:{}] {s}";
|
||||||
|
const args = .{
|
||||||
|
cStrToZig(tag orelse "-"),
|
||||||
|
log_item,
|
||||||
|
cStrToZig(message orelse "")
|
||||||
|
};
|
||||||
|
|
||||||
|
sokolLogFmt(log_level, format, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frameErrorable() !void {
|
||||||
|
Gfx.beginFrame();
|
||||||
|
defer Gfx.endFrame();
|
||||||
|
|
||||||
|
// try window.frame(event_queue.items);
|
||||||
|
event_queue.clearRetainingCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn frame() void {
|
||||||
|
tracy.frameMark();
|
||||||
|
|
||||||
|
const zone = tracy.initZone(@src(), .{ });
|
||||||
|
defer zone.deinit();
|
||||||
|
|
||||||
|
frameErrorable() catch |e| {
|
||||||
|
log.err("frame() failed: {}", .{e});
|
||||||
|
if (@errorReturnTrace()) |trace| {
|
||||||
|
std.debug.dumpStackTrace(trace.*);
|
||||||
|
}
|
||||||
|
sapp.requestQuit();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn cleanup() void {
|
||||||
|
const zone = tracy.initZone(@src(), .{ });
|
||||||
|
defer zone.deinit();
|
||||||
|
|
||||||
|
// window.beforeExit() catch |e| {
|
||||||
|
// log.err("window.beforeExit() failed: {}", .{e});
|
||||||
|
// if (@errorReturnTrace()) |trace| {
|
||||||
|
// std.debug.dumpStackTrace(trace.*);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const allocator = window.app.allocator;
|
||||||
|
|
||||||
|
window.deinit();
|
||||||
|
Gfx.deinit();
|
||||||
|
// event_queue.deinit(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn appendToEventQueue(e_ptr: [*c]const sapp.Event) !bool {
|
||||||
|
const e = e_ptr.*;
|
||||||
|
blk: switch (e.type) {
|
||||||
|
.MOUSE_DOWN => {
|
||||||
|
const mouse_button = Window.MouseButton.fromSokol(e.mouse_button) orelse break :blk;
|
||||||
|
|
||||||
|
try appendEvent(Event{
|
||||||
|
.mouse_pressed = .{
|
||||||
|
.button = mouse_button,
|
||||||
|
.position = Vec2.init(e.mouse_x, e.mouse_y)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.MOUSE_UP => {
|
||||||
|
const mouse_button = MouseButton.fromSokol(e.mouse_button) orelse break :blk;
|
||||||
|
|
||||||
|
try appendEvent(Event{
|
||||||
|
.mouse_released = .{
|
||||||
|
.button = mouse_button,
|
||||||
|
.position = Vec2.init(e.mouse_x, e.mouse_y)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.MOUSE_MOVE => {
|
||||||
|
if (!mouse_inside_window) {
|
||||||
|
try appendEvent(Event{
|
||||||
|
.mouse_enter = Vec2.init(e.mouse_x, e.mouse_y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try appendEvent(Event{
|
||||||
|
.mouse_move = Vec2.init(e.mouse_x, e.mouse_y)
|
||||||
|
});
|
||||||
|
|
||||||
|
mouse_inside_window = true;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.MOUSE_ENTER => {
|
||||||
|
if (!mouse_inside_window) {
|
||||||
|
try appendEvent(Event{
|
||||||
|
.mouse_enter = Vec2.init(e.mouse_x, e.mouse_y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mouse_inside_window = true;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.RESIZED => {
|
||||||
|
if (mouse_inside_window) {
|
||||||
|
try appendEvent(Event{
|
||||||
|
.mouse_leave = {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try appendEvent(Event{
|
||||||
|
.window_resize = {}
|
||||||
|
});
|
||||||
|
|
||||||
|
mouse_inside_window = false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.MOUSE_LEAVE => {
|
||||||
|
if (mouse_inside_window) {
|
||||||
|
try appendEvent(Event{
|
||||||
|
.mouse_leave = {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mouse_inside_window = false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.MOUSE_SCROLL => {
|
||||||
|
try appendEvent(Event{
|
||||||
|
.mouse_scroll = Vec2.init(e.scroll_x, e.scroll_y)
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.KEY_DOWN => {
|
||||||
|
try appendEvent(Event{
|
||||||
|
.key_pressed = .{
|
||||||
|
.code = @enumFromInt(@intFromEnum(e.key_code)),
|
||||||
|
.repeat = e.key_repeat
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.KEY_UP => {
|
||||||
|
try appendEvent(Event{
|
||||||
|
.key_released = @enumFromInt(@intFromEnum(e.key_code))
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.CHAR => {
|
||||||
|
try appendEvent(Event{
|
||||||
|
.char = @intCast(e.char_code)
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
.QUIT_REQUESTED => {
|
||||||
|
// TODO: handle quit request. Maybe show confirmation window in certain cases.
|
||||||
|
},
|
||||||
|
else => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn appendEvent(e: Event) !void {
|
||||||
|
event_queue.appendBounded(e) catch return error.EventQueueFull;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn event(e_ptr: [*c]const sapp.Event) void {
|
||||||
|
const zone = tracy.initZone(@src(), .{ });
|
||||||
|
defer zone.deinit();
|
||||||
|
|
||||||
|
if (Gfx.event(e_ptr)) {
|
||||||
|
appendEvent(Event{
|
||||||
|
.mouse_leave = {}
|
||||||
|
}) catch {};
|
||||||
|
mouse_inside_window = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const consumed_event = appendToEventQueue(e_ptr) catch |e| switch (e) {
|
||||||
|
error.EventQueueFull => blk: {
|
||||||
|
if (!event_queue_full_shown) {
|
||||||
|
log.warn("Event queue is full! Frame is taking too long to process", .{});
|
||||||
|
event_queue_full_shown = true;
|
||||||
|
}
|
||||||
|
break :blk false;
|
||||||
|
},
|
||||||
|
// else => blk: {
|
||||||
|
// log.err("Failed to append event to queue: {}", .{e});
|
||||||
|
// if (@errorReturnTrace()) |trace| {
|
||||||
|
// std.debug.dumpStackTrace(trace.*);
|
||||||
|
// }
|
||||||
|
// break :blk false;
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (consumed_event) {
|
||||||
|
event_queue_full_shown = false;
|
||||||
|
sapp.consumeEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
// Prints to stderr, ignoring potential errors.
|
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||||
std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
|
defer _ = debug_allocator.deinit();
|
||||||
|
|
||||||
|
var gpa: std.mem.Allocator = undefined;
|
||||||
|
if (builtin.mode == .ReleaseFast) {
|
||||||
|
gpa = std.heap.smp_allocator;
|
||||||
|
} else {
|
||||||
|
gpa = debug_allocator.allocator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Use tracy TracingAllocator
|
||||||
|
|
||||||
|
tracy.setThreadName("Main");
|
||||||
|
|
||||||
|
try Window.init(&window, gpa);
|
||||||
|
|
||||||
|
var sa: std.posix.Sigaction = .{
|
||||||
|
.handler = .{ .handler = signalHandler },
|
||||||
|
.mask = std.posix.sigemptyset(),
|
||||||
|
.flags = std.posix.SA.RESTART,
|
||||||
|
};
|
||||||
|
std.posix.sigaction(std.posix.SIG.INT, &sa, null);
|
||||||
|
|
||||||
|
sapp.run(.{
|
||||||
|
.init_cb = init,
|
||||||
|
.frame_cb = frame,
|
||||||
|
.cleanup_cb = cleanup,
|
||||||
|
.event_cb = event,
|
||||||
|
.width = 640,
|
||||||
|
.height = 480,
|
||||||
|
.icon = .{ .sokol_default = true },
|
||||||
|
.high_dpi = true,
|
||||||
|
.sample_count = 4,
|
||||||
|
.window_title = "Game",
|
||||||
|
.logger = sokol_logger,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
399
src/math.zig
Normal file
399
src/math.zig
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
|
pub const bytes_per_kib = 1024;
|
||||||
|
pub const bytes_per_mib = bytes_per_kib * 1024;
|
||||||
|
pub const bytes_per_gib = bytes_per_mib * 1024;
|
||||||
|
|
||||||
|
pub const bytes_per_kb = 1000;
|
||||||
|
pub const bytes_per_mb = bytes_per_kb * 1000;
|
||||||
|
pub const bytes_per_gb = bytes_per_mb * 1000;
|
||||||
|
|
||||||
|
pub const Vec2 = extern struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
|
||||||
|
pub const zero = init(0, 0);
|
||||||
|
|
||||||
|
pub fn init(x: f32, y: f32) Vec2 {
|
||||||
|
return Vec2{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initAngle(angle: f32) Vec2 {
|
||||||
|
return Vec2{
|
||||||
|
.x = @cos(angle),
|
||||||
|
.y = @sin(angle),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotateLeft90(self: Vec2) Vec2 {
|
||||||
|
return Vec2.init(self.y, -self.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotateRight90(self: Vec2) Vec2 {
|
||||||
|
return Vec2.init(-self.y, self.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flip(self: Vec2) Vec2 {
|
||||||
|
return Vec2.init(-self.x, -self.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(self: Vec2, other: Vec2) Vec2 {
|
||||||
|
return Vec2.init(
|
||||||
|
self.x + other.x,
|
||||||
|
self.y + other.y,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub(self: Vec2, other: Vec2) Vec2 {
|
||||||
|
return Vec2.init(
|
||||||
|
self.x - other.x,
|
||||||
|
self.y - other.y,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multiplyScalar(self: Vec2, value: f32) Vec2 {
|
||||||
|
return Vec2.init(
|
||||||
|
self.x * value,
|
||||||
|
self.y * value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multiply(self: Vec2, other: Vec2) Vec2 {
|
||||||
|
return Vec2.init(
|
||||||
|
self.x * other.x,
|
||||||
|
self.y * other.y,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn divide(self: Vec2, other: Vec2) Vec2 {
|
||||||
|
return Vec2.init(
|
||||||
|
self.x / other.x,
|
||||||
|
self.y / other.y,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn divideScalar(self: Vec2, value: f32) Vec2 {
|
||||||
|
return Vec2.init(
|
||||||
|
self.x / value,
|
||||||
|
self.y / value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(self: Vec2) f32 {
|
||||||
|
return @sqrt(self.x*self.x + self.y*self.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn distance(self: Vec2, other: Vec2) f32 {
|
||||||
|
return self.sub(other).length();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn limitLength(self: Vec2, max_length: f32) Vec2 {
|
||||||
|
const self_length = self.length();
|
||||||
|
if (self_length > max_length) {
|
||||||
|
return Vec2.init(self.x / self_length * max_length, self.y / self_length * max_length);
|
||||||
|
} else {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normalized(self: Vec2) Vec2 {
|
||||||
|
const self_length = self.length();
|
||||||
|
if (self_length == 0) {
|
||||||
|
return Vec2.init(0, 0);
|
||||||
|
}
|
||||||
|
return Vec2.init(self.x / self_length, self.y / self_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initScalar(value: f32) Vec2 {
|
||||||
|
return Vec2.init(value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eql(self: Vec2, other: Vec2) bool {
|
||||||
|
return self.x == other.x and self.y == other.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(self: Vec2, writer: *std.io.Writer) std.io.Writer.Error!void {
|
||||||
|
try writer.print("Vec2{{ {d}, {d} }}", .{ self.x, self.y });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Vec3 = extern struct {
|
||||||
|
x: f32, y: f32, z: f32,
|
||||||
|
|
||||||
|
pub const zero = init(0, 0, 0);
|
||||||
|
|
||||||
|
pub fn init(x: f32, y: f32, z: f32) Vec3 {
|
||||||
|
return Vec3{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.z = z,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initScalar(value: f32) Vec3 {
|
||||||
|
return Vec3.init(value, value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asArray(self: *Vec3) []f32 {
|
||||||
|
const ptr: [*]f32 = @alignCast(@ptrCast(@as(*anyopaque, @ptrCast(self))));
|
||||||
|
return ptr[0..3];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lerp(a: Vec3, b: Vec3, t: f32) Vec3 {
|
||||||
|
return Vec3.init(
|
||||||
|
std.math.lerp(a.x, b.x, t),
|
||||||
|
std.math.lerp(a.y, b.y, t),
|
||||||
|
std.math.lerp(a.z, b.z, t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clamp(self: Vec3, min_value: f32, max_value: f32) Vec3 {
|
||||||
|
return Vec3.init(
|
||||||
|
std.math.clamp(self.x, min_value, max_value),
|
||||||
|
std.math.clamp(self.y, min_value, max_value),
|
||||||
|
std.math.clamp(self.z, min_value, max_value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Vec4 = extern struct {
|
||||||
|
x: f32, y: f32, z: f32, w: f32,
|
||||||
|
|
||||||
|
pub const zero = init(0, 0, 0, 0);
|
||||||
|
|
||||||
|
pub fn init(x: f32, y: f32, z: f32, w: f32) Vec4 {
|
||||||
|
return Vec4{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.z = z,
|
||||||
|
.w = w
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initVec3XYZ(vec3: Vec3, w: f32) Vec4 {
|
||||||
|
return init(vec3.x, vec3.y, vec3.z, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initScalar(value: f32) Vec4 {
|
||||||
|
return Vec4.init(value, value, value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multiplyMat4(left: Vec4, right: Mat4) Vec4 {
|
||||||
|
var result: Vec4 = undefined;
|
||||||
|
|
||||||
|
// TODO: SIMD
|
||||||
|
|
||||||
|
result.x = left.x * right.columns[0][0];
|
||||||
|
result.y = left.x * right.columns[0][1];
|
||||||
|
result.z = left.x * right.columns[0][2];
|
||||||
|
result.w = left.x * right.columns[0][3];
|
||||||
|
|
||||||
|
result.x += left.y * right.columns[1][0];
|
||||||
|
result.y += left.y * right.columns[1][1];
|
||||||
|
result.z += left.y * right.columns[1][2];
|
||||||
|
result.w += left.y * right.columns[1][3];
|
||||||
|
|
||||||
|
result.x += left.z * right.columns[2][0];
|
||||||
|
result.y += left.z * right.columns[2][1];
|
||||||
|
result.z += left.z * right.columns[2][2];
|
||||||
|
result.w += left.z * right.columns[2][3];
|
||||||
|
|
||||||
|
result.x += left.w * right.columns[3][0];
|
||||||
|
result.y += left.w * right.columns[3][1];
|
||||||
|
result.z += left.w * right.columns[3][2];
|
||||||
|
result.w += left.w * right.columns[3][3];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multiply(left: Vec4, right: Vec4) Vec4 {
|
||||||
|
return init(
|
||||||
|
left.x * right.x,
|
||||||
|
left.y * right.y,
|
||||||
|
left.z * right.z,
|
||||||
|
left.w * right.w
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asArray(self: *Vec4) []f32 {
|
||||||
|
const ptr: [*]f32 = @alignCast(@ptrCast(@as(*anyopaque, @ptrCast(self))));
|
||||||
|
return ptr[0..4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initArray(array: []const f32) Vec4 {
|
||||||
|
return Vec4.init(array[0], array[1], array[2], array[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lerp(a: Vec4, b: Vec4, t: f32) Vec4 {
|
||||||
|
return Vec4.init(
|
||||||
|
std.math.lerp(a.x, b.x, t),
|
||||||
|
std.math.lerp(a.y, b.y, t),
|
||||||
|
std.math.lerp(a.z, b.z, t),
|
||||||
|
std.math.lerp(a.w, b.w, t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clamp(self: Vec4, min_value: f32, max_value: f32) Vec4 {
|
||||||
|
return Vec4.init(
|
||||||
|
std.math.clamp(self.x, min_value, max_value),
|
||||||
|
std.math.clamp(self.y, min_value, max_value),
|
||||||
|
std.math.clamp(self.z, min_value, max_value),
|
||||||
|
std.math.clamp(self.w, min_value, max_value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toVec3XYZ(self: Vec4) Vec3 {
|
||||||
|
return Vec3.init(self.x, self.y, self.z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Mat4 = extern struct {
|
||||||
|
columns: [4][4]f32,
|
||||||
|
|
||||||
|
pub fn initZero() Mat4 {
|
||||||
|
var self: Mat4 = undefined;
|
||||||
|
@memset(self.asArray(), 0);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initIdentity() Mat4 {
|
||||||
|
return Mat4.initDiagonal(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initDiagonal(value: f32) Mat4 {
|
||||||
|
var self = Mat4.initZero();
|
||||||
|
self.columns[0][0] = value;
|
||||||
|
self.columns[1][1] = value;
|
||||||
|
self.columns[2][2] = value;
|
||||||
|
self.columns[3][3] = value;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multiply(left: Mat4, right: Mat4) Mat4 {
|
||||||
|
var self: Mat4 = undefined;
|
||||||
|
|
||||||
|
inline for (.{ 0, 1, 2, 3 }) |i| {
|
||||||
|
var column = Vec4.initArray(&right.columns[i]).multiplyMat4(left);
|
||||||
|
@memcpy(&self.columns[i], column.asArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initScale(scale: Vec3) Mat4 {
|
||||||
|
var self = Mat4.initIdentity();
|
||||||
|
self.columns[0][0] = scale.x;
|
||||||
|
self.columns[1][1] = scale.y;
|
||||||
|
self.columns[2][2] = scale.z;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initTranslate(offset: Vec3) Mat4 {
|
||||||
|
var self = Mat4.initIdentity();
|
||||||
|
self.columns[3][0] = offset.x;
|
||||||
|
self.columns[3][1] = offset.y;
|
||||||
|
self.columns[3][2] = offset.z;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asArray(self: *Mat4) []f32 {
|
||||||
|
const ptr: [*]f32 = @alignCast(@ptrCast(@as(*anyopaque, @ptrCast(&self.columns))));
|
||||||
|
return ptr[0..16];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Rect = struct {
|
||||||
|
pos: Vec2,
|
||||||
|
size: Vec2,
|
||||||
|
|
||||||
|
pub const zero = Rect{
|
||||||
|
.pos = Vec2.zero,
|
||||||
|
.size = Vec2.zero
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(x: f32, y: f32, width: f32, height: f32) Rect {
|
||||||
|
return Rect{
|
||||||
|
.pos = Vec2.init(x, y),
|
||||||
|
.size = Vec2.init(width, height)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clip(self: Rect, other: Rect) Rect {
|
||||||
|
const left_edge = @max(self.left(), other.left());
|
||||||
|
const right_edge = @min(self.right(), other.right());
|
||||||
|
const top_edge = @max(self.top(), other.top());
|
||||||
|
const bottom_edge = @min(self.bottom(), other.bottom());
|
||||||
|
return Rect.init(
|
||||||
|
left_edge,
|
||||||
|
top_edge,
|
||||||
|
right_edge - left_edge,
|
||||||
|
bottom_edge - top_edge
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn left(self: Rect) f32 {
|
||||||
|
return self.pos.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn right(self: Rect) f32 {
|
||||||
|
return self.pos.x + self.size.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn top(self: Rect) f32 {
|
||||||
|
return self.pos.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bottom(self: Rect) f32 {
|
||||||
|
return self.pos.y + self.size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isInside(self: Rect, pos: Vec2) bool {
|
||||||
|
const x_overlap = self.pos.x <= pos.x and pos.x < self.pos.x + self.size.x;
|
||||||
|
const y_overlap = self.pos.y <= pos.y and pos.y < self.pos.y + self.size.y;
|
||||||
|
return x_overlap and y_overlap;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Line = struct {
|
||||||
|
p0: Vec2,
|
||||||
|
p1: Vec2
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn isInsideRect(rect_pos: Vec2, rect_size: Vec2, pos: Vec2) bool {
|
||||||
|
const rect = Rect{
|
||||||
|
.pos = rect_pos,
|
||||||
|
.size = rect_size
|
||||||
|
};
|
||||||
|
return rect.isInside(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rgba(r: u8, g: u8, b: u8, a: f32) Vec4 {
|
||||||
|
assert(0 <= a and a <= 1);
|
||||||
|
return Vec4.init(
|
||||||
|
@as(f32, @floatFromInt(r)) / 255,
|
||||||
|
@as(f32, @floatFromInt(g)) / 255,
|
||||||
|
@as(f32, @floatFromInt(b)) / 255,
|
||||||
|
a,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rgb(r: u8, g: u8, b: u8) Vec4 {
|
||||||
|
return rgba(r, g, b, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rgb_hex(text: []const u8) ?Vec4 {
|
||||||
|
if (text.len != 7) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (text[0] != '#') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const r = std.fmt.parseInt(u8, text[1..3], 16) catch return null;
|
||||||
|
const g = std.fmt.parseInt(u8, text[3..5], 16) catch return null;
|
||||||
|
const b = std.fmt.parseInt(u8, text[5..7], 16) catch return null;
|
||||||
|
return rgb(r, g, b);
|
||||||
|
}
|
||||||
187
src/window.zig
Normal file
187
src/window.zig
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const sokol = @import("sokol");
|
||||||
|
|
||||||
|
const Math = @import("./math.zig");
|
||||||
|
const Vec2 = Math.Vec2;
|
||||||
|
|
||||||
|
const Window = @This();
|
||||||
|
|
||||||
|
pub const MouseButton = enum {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
middle,
|
||||||
|
|
||||||
|
pub fn fromSokol(mouse_button: sokol.app.Mousebutton) ?MouseButton {
|
||||||
|
return switch(mouse_button) {
|
||||||
|
.LEFT => MouseButton.left,
|
||||||
|
.RIGHT => MouseButton.right,
|
||||||
|
.MIDDLE => MouseButton.middle,
|
||||||
|
else => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const KeyCode = enum(std.math.IntFittingRange(0, sokol.app.max_keycodes-1)) {
|
||||||
|
SPACE = 32,
|
||||||
|
APOSTROPHE = 39,
|
||||||
|
COMMA = 44,
|
||||||
|
MINUS = 45,
|
||||||
|
PERIOD = 46,
|
||||||
|
SLASH = 47,
|
||||||
|
_0 = 48,
|
||||||
|
_1 = 49,
|
||||||
|
_2 = 50,
|
||||||
|
_3 = 51,
|
||||||
|
_4 = 52,
|
||||||
|
_5 = 53,
|
||||||
|
_6 = 54,
|
||||||
|
_7 = 55,
|
||||||
|
_8 = 56,
|
||||||
|
_9 = 57,
|
||||||
|
SEMICOLON = 59,
|
||||||
|
EQUAL = 61,
|
||||||
|
A = 65,
|
||||||
|
B = 66,
|
||||||
|
C = 67,
|
||||||
|
D = 68,
|
||||||
|
E = 69,
|
||||||
|
F = 70,
|
||||||
|
G = 71,
|
||||||
|
H = 72,
|
||||||
|
I = 73,
|
||||||
|
J = 74,
|
||||||
|
K = 75,
|
||||||
|
L = 76,
|
||||||
|
M = 77,
|
||||||
|
N = 78,
|
||||||
|
O = 79,
|
||||||
|
P = 80,
|
||||||
|
Q = 81,
|
||||||
|
R = 82,
|
||||||
|
S = 83,
|
||||||
|
T = 84,
|
||||||
|
U = 85,
|
||||||
|
V = 86,
|
||||||
|
W = 87,
|
||||||
|
X = 88,
|
||||||
|
Y = 89,
|
||||||
|
Z = 90,
|
||||||
|
LEFT_BRACKET = 91,
|
||||||
|
BACKSLASH = 92,
|
||||||
|
RIGHT_BRACKET = 93,
|
||||||
|
GRAVE_ACCENT = 96,
|
||||||
|
WORLD_1 = 161,
|
||||||
|
WORLD_2 = 162,
|
||||||
|
ESCAPE = 256,
|
||||||
|
ENTER = 257,
|
||||||
|
TAB = 258,
|
||||||
|
BACKSPACE = 259,
|
||||||
|
INSERT = 260,
|
||||||
|
DELETE = 261,
|
||||||
|
RIGHT = 262,
|
||||||
|
LEFT = 263,
|
||||||
|
DOWN = 264,
|
||||||
|
UP = 265,
|
||||||
|
PAGE_UP = 266,
|
||||||
|
PAGE_DOWN = 267,
|
||||||
|
HOME = 268,
|
||||||
|
END = 269,
|
||||||
|
CAPS_LOCK = 280,
|
||||||
|
SCROLL_LOCK = 281,
|
||||||
|
NUM_LOCK = 282,
|
||||||
|
PRINT_SCREEN = 283,
|
||||||
|
PAUSE = 284,
|
||||||
|
F1 = 290,
|
||||||
|
F2 = 291,
|
||||||
|
F3 = 292,
|
||||||
|
F4 = 293,
|
||||||
|
F5 = 294,
|
||||||
|
F6 = 295,
|
||||||
|
F7 = 296,
|
||||||
|
F8 = 297,
|
||||||
|
F9 = 298,
|
||||||
|
F10 = 299,
|
||||||
|
F11 = 300,
|
||||||
|
F12 = 301,
|
||||||
|
F13 = 302,
|
||||||
|
F14 = 303,
|
||||||
|
F15 = 304,
|
||||||
|
F16 = 305,
|
||||||
|
F17 = 306,
|
||||||
|
F18 = 307,
|
||||||
|
F19 = 308,
|
||||||
|
F20 = 309,
|
||||||
|
F21 = 310,
|
||||||
|
F22 = 311,
|
||||||
|
F23 = 312,
|
||||||
|
F24 = 313,
|
||||||
|
F25 = 314,
|
||||||
|
KP_0 = 320,
|
||||||
|
KP_1 = 321,
|
||||||
|
KP_2 = 322,
|
||||||
|
KP_3 = 323,
|
||||||
|
KP_4 = 324,
|
||||||
|
KP_5 = 325,
|
||||||
|
KP_6 = 326,
|
||||||
|
KP_7 = 327,
|
||||||
|
KP_8 = 328,
|
||||||
|
KP_9 = 329,
|
||||||
|
KP_DECIMAL = 330,
|
||||||
|
KP_DIVIDE = 331,
|
||||||
|
KP_MULTIPLY = 332,
|
||||||
|
KP_SUBTRACT = 333,
|
||||||
|
KP_ADD = 334,
|
||||||
|
KP_ENTER = 335,
|
||||||
|
KP_EQUAL = 336,
|
||||||
|
LEFT_SHIFT = 340,
|
||||||
|
LEFT_CONTROL = 341,
|
||||||
|
LEFT_ALT = 342,
|
||||||
|
LEFT_SUPER = 343,
|
||||||
|
RIGHT_SHIFT = 344,
|
||||||
|
RIGHT_CONTROL = 345,
|
||||||
|
RIGHT_ALT = 346,
|
||||||
|
RIGHT_SUPER = 347,
|
||||||
|
MENU = 348,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Event = union(enum) {
|
||||||
|
mouse_pressed: struct {
|
||||||
|
button: MouseButton,
|
||||||
|
position: Vec2,
|
||||||
|
},
|
||||||
|
mouse_released: struct {
|
||||||
|
button: MouseButton,
|
||||||
|
position: Vec2,
|
||||||
|
},
|
||||||
|
mouse_move: Vec2,
|
||||||
|
mouse_enter: Vec2,
|
||||||
|
mouse_leave,
|
||||||
|
mouse_scroll: Vec2,
|
||||||
|
key_pressed: struct {
|
||||||
|
code: KeyCode,
|
||||||
|
repeat: bool
|
||||||
|
},
|
||||||
|
key_released: KeyCode,
|
||||||
|
window_resize,
|
||||||
|
char: u21,
|
||||||
|
};
|
||||||
|
|
||||||
|
gpa: Allocator,
|
||||||
|
events: std.ArrayList(Event),
|
||||||
|
|
||||||
|
pub fn init(self: *Window, gpa: Allocator) !void {
|
||||||
|
self.* = Window{
|
||||||
|
.gpa = gpa,
|
||||||
|
.events = .empty
|
||||||
|
// .last_frame_at_ns = std.time.nanoTimestamp(),
|
||||||
|
// .frame_arena = ArenaAllocator.init(gpa),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Window) void {
|
||||||
|
const gpa = self.gpa;
|
||||||
|
_ = gpa; // autofix
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user