├── .gitattributes
├── example
├── src
│ ├── inline.png
│ ├── scenes
│ │ ├── imgui.zig
│ │ ├── shader
│ │ │ └── red.fragment
│ │ ├── shaders.zig
│ │ ├── rendertarget.zig
│ │ ├── renderer.zig
│ │ ├── colliders.zig
│ │ └── spatialhash_squares.zig
│ └── main.zig
└── assets
│ ├── public-sans.ttf
│ └── texture
│ ├── ico.png
│ ├── logo.png
│ └── sheet.png
├── src
├── dep
│ ├── stb
│ │ ├── stb_image_wrapper.c
│ │ ├── stb_leakcheck.h
│ │ ├── stb_include.h
│ │ ├── stb_easy_font.h
│ │ └── stb_divide.h
│ ├── cimgui
│ │ └── imgui
│ │ │ └── imconfig.h
│ └── gl
│ │ └── glad
│ │ └── include
│ │ └── KHR
│ │ └── khrplatform.h
├── zt
│ ├── shader
│ │ ├── renderer.fragment
│ │ └── renderer.vertex
│ ├── timeManager.zig
│ ├── shader.zig
│ ├── renderTarget.zig
│ ├── allocators.zig
│ ├── texture.zig
│ ├── styler.zig
│ ├── customComponents.zig
│ ├── renderer.zig
│ ├── generateBuffer.zig
│ ├── app.zig
│ └── spatialHash.zig
├── zt.zig
└── pkg
│ └── stb_image.zig
├── .devcontainer
├── Dockerfile
├── devcontainer.json
└── install-zig.sh
├── .gitignore
├── zig.mod
├── Makefile
├── LICENSE
└── readme.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | src/dep/** linguist-vendored
2 |
3 | *.zig text eol=lf
4 |
--------------------------------------------------------------------------------
/example/src/inline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonSnowbd/ZT/HEAD/example/src/inline.png
--------------------------------------------------------------------------------
/src/dep/stb/stb_image_wrapper.c:
--------------------------------------------------------------------------------
1 | #define STB_IMAGE_IMPLEMENTATION
2 | #include "stb_image.h"
--------------------------------------------------------------------------------
/example/assets/public-sans.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonSnowbd/ZT/HEAD/example/assets/public-sans.ttf
--------------------------------------------------------------------------------
/example/assets/texture/ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonSnowbd/ZT/HEAD/example/assets/texture/ico.png
--------------------------------------------------------------------------------
/example/assets/texture/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonSnowbd/ZT/HEAD/example/assets/texture/logo.png
--------------------------------------------------------------------------------
/example/assets/texture/sheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonSnowbd/ZT/HEAD/example/assets/texture/sheet.png
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/devcontainers/typescript-node:0-18
2 | ADD . .
3 | RUN bash ./install-zig.sh
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | zig-out/
2 | zig-cache/
3 | /release/
4 | /debug/
5 | /build/
6 | /build-*/
7 | /docgen_tmp/
8 | imgui.ini
9 | workspace
10 | **/.DS_Store
--------------------------------------------------------------------------------
/zig.mod:
--------------------------------------------------------------------------------
1 | id: iz2vhqchpoifkzmhs6puc9lwgksgp3oxsp7qu8os78yxi1wx
2 | name: ZT
3 | main: build.zig
4 | description: A zig based Imgui Application framework
5 | dependencies:
--------------------------------------------------------------------------------
/example/src/scenes/imgui.zig:
--------------------------------------------------------------------------------
1 | const zt = @import("zt");
2 | const main = @import("../main.zig");
3 | const ig = @import("imgui");
4 |
5 | pub fn update(ctx: *main.SampleApplication.Context) void {
6 | _ = ctx;
7 | ig.igShowDemoWindow(null);
8 | }
9 |
--------------------------------------------------------------------------------
/src/zt/shader/renderer.fragment:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | out vec4 FragColor;
4 |
5 | in vec4 VertexColor;
6 | in vec2 VertexTexPos;
7 |
8 | uniform sampler2D sprite;
9 |
10 | void main()
11 | {
12 | FragColor = texture(sprite, VertexTexPos) * VertexColor;
13 | }
--------------------------------------------------------------------------------
/example/src/scenes/shader/red.fragment:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | out vec4 FragColor;
4 |
5 | in vec4 VertexColor;
6 | in vec2 VertexTexPos;
7 |
8 | uniform sampler2D sprite;
9 |
10 | void main()
11 | {
12 | FragColor = texture(sprite, VertexTexPos) * VertexColor;
13 | FragColor.g = 0.0;
14 | FragColor.b = 0.0;
15 | }
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | LEVEL := Debug
2 | TARGET := aarch64-macos-none
3 |
4 | ZIG := /usr/local/lib/zig/lib
5 |
6 | build:
7 | zig build -Dtarget=$(TARGET) -Doptimize=$(LEVEL) -freference-trace
8 |
9 | release: export LEVEL=ReleaseFast
10 | release: build
11 |
12 | clean:
13 | rm -rf zig-cache
14 | rm -rf zig-out
15 |
16 | .PHONY: debug release clean
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Default Linux Universal",
3 | "build": {
4 | "dockerfile": "Dockerfile"
5 | },
6 | "customizations": {
7 | "vscode": {
8 | "extensions": [
9 | "ziglang.vscode-zig"
10 | ]
11 | }
12 | },
13 | "appPort": 7651,
14 | "containerEnv": {
15 | "SHELL": "/usr/bin/zsh"
16 | }
17 | }
--------------------------------------------------------------------------------
/src/zt/shader/renderer.vertex:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | layout (location = 0) in vec3 vertPos;
4 | layout (location = 1) in vec4 vertCol;
5 | layout (location = 2) in vec2 texPos;
6 |
7 | uniform mat4 View;
8 | uniform mat4 Projection;
9 |
10 | out vec4 VertexColor;
11 | out vec2 VertexTexPos;
12 |
13 | void main()
14 | {
15 | VertexTexPos = texPos;
16 | VertexColor = vertCol;
17 | gl_Position = Projection * View * vec4(vertPos, 1.0);
18 | }
--------------------------------------------------------------------------------
/src/zt/timeManager.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const glfw = @import("glfw");
3 |
4 | const Self = @This();
5 |
6 | _previous: f64 = 0.0,
7 | lifetime: f32 = 0.0,
8 | /// Do not use this for calculations! It is for display, as it is smoothed for easy viewing.
9 | fps: f32 = 0.0,
10 | dt: f32 = 0.0,
11 |
12 | pub fn init() @This() {
13 | return .{
14 | ._previous = glfw.getTime(),
15 | };
16 | }
17 | pub fn tick(self: *Self) void {
18 | const lap = glfw.getTime();
19 |
20 | // No need to worry about overflow, unless a frame intends to last for over a month.
21 | self.dt = @as(f32, @floatCast(lap - self._previous));
22 | self.lifetime += self.dt;
23 |
24 | // Smoothly introduce new fps measurements.
25 | const smoothing: f32 = 0.99;
26 | const newFps = 1.0 / self.dt;
27 | self.fps = (self.fps * smoothing) + (newFps * (1.0 - smoothing));
28 |
29 | self._previous = lap;
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Josiah P.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/zt/shader.zig:
--------------------------------------------------------------------------------
1 | const gl = @import("gl");
2 |
3 | const Self = @This();
4 |
5 | id: c_uint = 0,
6 | dead: bool = true,
7 |
8 | pub fn from(shaderID: c_uint) Self {
9 | return .{
10 | .id = shaderID,
11 | };
12 | }
13 | pub fn init(vert: [*:0]const u8, frag: [*:0]const u8) Self {
14 | var self: Self = .{};
15 |
16 | const vertId = gl.glCreateShader(gl.GL_VERTEX_SHADER);
17 | gl.glShaderSource(vertId, 1, &vert, null);
18 | gl.glCompileShader(vertId);
19 |
20 | const fragId = gl.glCreateShader(gl.GL_FRAGMENT_SHADER);
21 | gl.glShaderSource(fragId, 1, &frag, null);
22 | gl.glCompileShader(fragId);
23 |
24 | self.id = gl.glCreateProgram();
25 | gl.glAttachShader(self.id, vertId);
26 | gl.glAttachShader(self.id, fragId);
27 | gl.glLinkProgram(self.id);
28 |
29 | gl.glDeleteShader(vertId);
30 | gl.glDeleteShader(fragId);
31 |
32 | gl.glUseProgram(0);
33 |
34 | self.dead = false;
35 |
36 | return self;
37 | }
38 | pub fn deinit(self: *Self) void {
39 | gl.glDeleteProgram(self.id);
40 | self.dead = true;
41 | }
42 | pub fn bind(self: *Self) void {
43 | gl.glUseProgram(self.id);
44 | }
45 | pub fn unbind(self: *Self) void {
46 | _ = self;
47 | gl.glUseProgram(0);
48 | }
49 |
--------------------------------------------------------------------------------
/.devcontainer/install-zig.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ZIG_VERSION="${VERSION:-"master"}"
4 |
5 | set -e
6 |
7 | if [ "$(id -u)" -ne 0 ]; then
8 | echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
9 | exit 1
10 | fi
11 |
12 | # Clean up
13 | rm -rf /var/lib/apt/lists/*
14 |
15 | ARCH="$(uname -m)"
16 |
17 | # Checks if packages are installed and installs them if not
18 | check_packages() {
19 | if ! dpkg -s "$@" >/dev/null 2>&1; then
20 | if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
21 | echo "Running apt-get update..."
22 | apt-get update -y
23 | fi
24 | apt-get -y install --no-install-recommends "$@"
25 | fi
26 | }
27 |
28 | # make sure we have curl and jq
29 | check_packages ca-certificates curl xz-utils jq \
30 | libxrandr-dev \
31 | libxcursor-dev \
32 | libxinerama-dev \
33 | libxi-dev \
34 | libgl1-mesa-dev
35 |
36 | # remove existing instalations
37 | rm -rf /usr/local/lib/zig
38 |
39 | # make sure /usr/local/lib/zig exists
40 | mkdir -p /usr/local/lib/zig
41 |
42 | INDEX_URL="https://ziglang.org/download/index.json"
43 |
44 | if [[ "$ZIG_VERSION" == "latest" || "$ZIG_VERSION" == "current" || "$ZIG_VERSION" == "lts" ]]
45 | then
46 | # for latest we download the latest *release* version
47 | DOWNLOAD_URL=$(curl -sSL $INDEX_URL | jq -r 'to_entries[1].value."'"$ARCH"'-linux".tarball')
48 | else
49 | DOWNLOAD_URL=$(curl -sSL $INDEX_URL | jq -r '."'"$ZIG_VERSION"'"."'"$ARCH"'-linux".tarball')
50 | fi
51 |
52 | # download binary, untar and ln into /usr/local/bin
53 | curl -sSL $DOWNLOAD_URL | tar xJ -C /usr/local/lib/zig --strip-components 1
54 | chmod +x /usr/local/lib/zig/zig
55 |
56 | rm /usr/local/bin/zig || true
57 |
58 | ln -s /usr/local/lib/zig/zig /usr/local/bin/zig
59 |
60 | # Clean up
61 | rm -rf /var/lib/apt/lists/*
62 |
63 | ## 🐉
64 | # to make zig work with imgui's dep tree for libc, we must ensure a file exists
65 | touch /usr/local/lib/zig/lib/libc/include/generic-musl/bits/syscall.h
66 | ## 🐉
67 |
68 | echo "Done!"
--------------------------------------------------------------------------------
/src/zt/renderTarget.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const zt = @import("../zt.zig");
3 | const gl = @import("gl");
4 |
5 | const Self = @This();
6 |
7 | target: zt.gl.Texture = undefined,
8 | bufferId: c_uint = undefined,
9 |
10 | /// Do not modify, this is handled internally to restore previous viewports.
11 | _previous_viewport: [4]c_int = undefined,
12 | /// Do not modify, this is handled internally to know when to resize.
13 | _current_size: [2]c_int = .{ 0, 0 },
14 |
15 | pub fn init(width: c_int, height: c_int) Self {
16 | var self: Self = .{};
17 |
18 | // FBO
19 | gl.glGenFramebuffers(1, &self.bufferId);
20 | gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.bufferId);
21 |
22 | // TEX
23 | self.target = zt.gl.Texture.initBlank(width, height);
24 | gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, self.target.id, 0);
25 | self._current_size[0] = width;
26 | self._current_size[1] = height;
27 |
28 | gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0);
29 | return self;
30 | }
31 | pub fn deinit(self: *Self) void {
32 | self.target.deinit();
33 | }
34 |
35 | pub fn bind(self: *Self) void {
36 | gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.bufferId);
37 | gl.glGetIntegerv(gl.GL_VIEWPORT, &self._previous_viewport);
38 | gl.glViewport(0, 0, @intFromFloat(self.target.width), @intFromFloat(self.target.height));
39 | }
40 | pub fn unbind(self: *Self) void {
41 | gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0);
42 | gl.glViewport(self._previous_viewport[0], self._previous_viewport[1], self._previous_viewport[2], self._previous_viewport[3]);
43 | }
44 | pub fn resize(self: *Self, x: c_int, y: c_int) void {
45 | if (x != self._current_size[0] or y != self._current_size[1]) {
46 | self.target.bind();
47 | gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, x, y, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, null);
48 | self.target.width = @floatFromInt(x);
49 | self.target.height = @floatFromInt(y);
50 | self._current_size[0] = x;
51 | self._current_size[1] = y;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example/src/scenes/shaders.zig:
--------------------------------------------------------------------------------
1 | const zt = @import("zt");
2 | const main = @import("../main.zig");
3 | const ig = @import("imgui");
4 | const zg = zt.custom_components;
5 |
6 | var rotation: f32 = 0.0;
7 | var scale: f32 = 1.0;
8 |
9 | var shaderActive: bool = true;
10 |
11 | pub fn update(ctx: *main.SampleApplication.Context) void {
12 | control();
13 |
14 | var render = ctx.data.render;
15 | const io = ig.igGetIO();
16 | render.updateRenderSize(io.*.DisplaySize);
17 | render.updateCamera(.{}, scale, rotation);
18 |
19 | // Without a shader:
20 | render.sprite(ctx.data.sheet, zt.math.vec2(-30, 0), 0, zt.math.vec2(32, 32), zt.math.Vec4.white, zt.math.vec2(0.5, 0.5), zt.math.rect(16, 0, 16, 16));
21 |
22 | if (shaderActive) {
23 | // Pushing and popping shaders is a one-stack deal, I don't see the need to have nested shaders.
24 | render.updateShader(&ctx.data.redShader);
25 | render.sprite(ctx.data.sheet, zt.math.vec2(30, 0), 0, zt.math.vec2(32, 32), zt.math.Vec4.white, zt.math.vec2(0.5, 0.5), zt.math.rect(16, 0, 16, 16));
26 | render.updateShader(null);
27 |
28 | // and because of this, you can push as many times as you want, and pop once to get back to default.
29 | } else {
30 | render.sprite(ctx.data.sheet, zt.math.vec2(30, 0), 0, zt.math.vec2(32, 32), zt.math.Vec4.white, zt.math.vec2(0.5, 0.5), zt.math.rect(16, 0, 16, 16));
31 | }
32 |
33 | render.flush();
34 | }
35 |
36 | fn control() void {
37 | const io = ig.igGetIO();
38 | ig.igSetNextWindowPos(io.*.DisplaySize, ig.ImGuiCond_Appearing, .{ .x = 1, .y = 1 });
39 | if (ig.igBegin("Renderer Demo Settings", null, ig.ImGuiWindowFlags_None)) {
40 | ig.igPushItemWidth(ig.igGetWindowWidth() * 0.5);
41 | _ = ig.igDragFloat("Camera Rotation", &rotation, 0.02, zt.math.toRadians(-360.0), zt.math.toRadians(360.0), "%.3f", ig.ImGuiSliderFlags_None);
42 | _ = ig.igDragFloat("Camera Zoom", &scale, 0.02, 0.1, 16, "%.3f", ig.ImGuiSliderFlags_None);
43 | _ = zg.edit("Shader On", &shaderActive);
44 | ig.igPopItemWidth();
45 | }
46 | ig.igEnd();
47 | }
48 |
--------------------------------------------------------------------------------
/src/zt/allocators.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const mem = std.mem;
3 |
4 | pub fn RingBufferGenerate(comptime size: usize) type {
5 | return struct {
6 | buffer: [size]u8 = undefined,
7 | const Self = @This();
8 | var allocator: mem.Allocator = mem.Allocator{
9 | .allocFn = alloc,
10 | .resizeFn = mem.Allocator.noResize,
11 | };
12 | var end_index: usize = 0;
13 | pub fn getAllocator(self: *Self) mem.Allocator {
14 | return mem.Allocator{ .ptr = self, .vtable = &mem.Allocator.VTable{ .alloc = alloc, .resize = mem.Allocator.noResize, .free = mem.Allocator.noFree } };
15 | }
16 | fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
17 | _ = ret_addr;
18 | var a: *Self = @ptrCast(ctx);
19 | const addr = @intFromPtr(&a.buffer) + end_index;
20 | const adjusted_addr = if (ptr_align > 0) mem.alignForward(usize, addr, @as(usize, @intCast(ptr_align))) else addr;
21 | const adjusted_index = end_index + (adjusted_addr - addr);
22 | const new_end_index = adjusted_index + len;
23 |
24 | if (new_end_index > a.buffer.len) {
25 | if (len > a.buffer.len) {
26 | std.debug.panic("tmp allocated more than is in our temp allocator.", .{});
27 | unreachable;
28 | }
29 | end_index = len;
30 | return @as([*]u8, @ptrCast(a.buffer[0..len]));
31 | }
32 | end_index = new_end_index;
33 |
34 | return @as([*]u8, @ptrCast(a.buffer[adjusted_index..new_end_index]));
35 | }
36 | };
37 | }
38 |
39 | var internal = RingBufferGenerate(1024 * 1024 * 3){};
40 | /// You can use this to allocate temp memory that you never have to free.
41 | var internalBuffer: mem.Allocator = internal.getAllocator();
42 |
43 | /// Returns a ring buffer that uses a page allocator in a loop of memory.
44 | /// Do not use this for permanent memory, but instead for throw away memory, no need
45 | /// to free anything that you use this to allocate.
46 | pub fn ring() mem.Allocator {
47 | return internalBuffer;
48 | }
49 |
--------------------------------------------------------------------------------
/src/zt.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | pub const App = @import("zt/app.zig").App;
4 | pub const Allocators = @import("zt/allocators.zig");
5 | /// Forwarding known_folders.zig, a popular zig framework for finding predetermined folders cross platform.
6 | pub const known_folders = @import("pkg/known_folders.zig");
7 | pub const custom_components = @import("zt/customComponents.zig");
8 | pub const math = @import("imgui").zlm;
9 | pub const imgui_style = @import("zt/styler.zig");
10 |
11 | /// Everything inside of `Game` is designed around making a game easier to work on. Things like physics, spatial hashing,
12 | /// and general purpose rendering.
13 | pub const game = struct {
14 | /// A simple and very fast spatial hash with customizable entry type and bucket size.
15 | pub const SpatialHash = @import("zt/spatialHash.zig");
16 | /// Lots of functions for overlap testing shapes of different kinds.
17 | pub const Physics = @import("zt/physics.zig");
18 | /// A renderer made with `GenerateBuffer`, contains everything you need for a fast and simple graphic renderer.
19 | pub const Renderer = @import("zt/renderer.zig");
20 | };
21 |
22 | /// These are thin wrappers around basic opengl constructs. Perfect for use in applications that need graphical capability,
23 | /// and for games.
24 | pub const gl = struct {
25 | /// Creates an FBO and texture, allows for binding and unbinding to toggle what you're rendering to with opengl.
26 | pub const RenderTarget = @import("zt/renderTarget.zig");
27 | /// Creates a Shader Program in opengl from @embedFile or other bytes.
28 | pub const Shader = @import("zt/shader.zig");
29 | /// Uses stb_image to load textures into opengl, with wrapper struct for basic information.
30 | pub const Texture = @import("zt/texture.zig");
31 | /// Generates a basic opengl buffer automatically, based on a vertex struct.
32 | pub const GenerateBuffer = @import("zt/generateBuffer.zig").GenerateBuffer;
33 | };
34 |
35 | /// Finds the folder of the binary, and sets the operating system's working directory
36 | /// to it. Useful to keep relative file loading working properly(especially using `zig build run`)
37 | /// when ran from any location.
38 | pub fn makeCwd() !void {
39 | const folder = (try known_folders.getPath(std.heap.c_allocator, known_folders.KnownFolder.executable_dir)).?;
40 | try std.os.chdir(folder);
41 | }
42 |
43 | /// Takes a relative path from the executable's cwd, and returns an absolute path to the resource. Great for making
44 | /// sure your application gets the right resources no matter where its launched from.
45 | pub inline fn path(subpath: []const u8) []const u8 {
46 | return pathEx(Allocators.ring(), subpath);
47 | }
48 | /// Same as path, but you own the memory.
49 | pub fn pathEx(allocator: std.mem.Allocator, subpath: []const u8) []const u8 {
50 | const executablePath = known_folders.getPath(allocator, .executable_dir) catch unreachable;
51 | defer allocator.free(executablePath.?);
52 | return std.fs.path.joinZ(allocator, &[_][]const u8{ executablePath.?, subpath }) catch unreachable;
53 | }
54 |
--------------------------------------------------------------------------------
/example/src/scenes/rendertarget.zig:
--------------------------------------------------------------------------------
1 | const zt = @import("zt");
2 | const main = @import("../main.zig");
3 | const ig = @import("imgui");
4 | const zg = zt.custom_components;
5 |
6 | var rotation: f32 = 0.0;
7 | var zoom: f32 = 1.0;
8 |
9 | // We're just going to lazy init it when the example loads this scene.
10 | var rt: ?zt.gl.RenderTarget = null;
11 | fn ensure() void {
12 | if (rt == null) {
13 | rt = zt.gl.RenderTarget.init(200, 200);
14 | }
15 | }
16 |
17 | pub fn update(ctx: *main.SampleApplication.Context) void {
18 | const io = ig.igGetIO();
19 | ensure();
20 | control(ctx);
21 | var render = ctx.data.render;
22 | render.updateRenderSize(io.*.DisplaySize);
23 | render.updateCamera(.{}, zoom, rotation);
24 | render.sprite(ctx.data.sheet, .{}, 0, zt.math.vec2(32, 32), zt.math.Vec4.white, zt.math.vec2(0.5, 0.5), zt.math.rect(16, 0, 16, 16));
25 | const pos = render.worldToScreen(.{}); // Cache where the rt is focused
26 | render.flush();
27 |
28 | // And this is a screenspace transform!
29 | // This part wont be affected by position/zoom/rotation. Make sure you flush between the changes.
30 | render.updateCameraScreenSpace();
31 | render.rectangleHollow(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), zt.math.rect(pos.x - 100, pos.y - 100, 200, 200), 0, 3, zt.math.vec4(1.0, 1.0, 0.0, 1.0));
32 | render.flush();
33 | }
34 |
35 | fn control(ctx: *main.SampleApplication.Context) void {
36 | const io = ig.igGetIO();
37 | ig.igSetNextWindowPos(io.*.DisplaySize, ig.ImGuiCond_Appearing, .{ .x = 1, .y = 1 });
38 | if (ig.igBegin("RenderTarget Demo Settings", null, ig.ImGuiWindowFlags_None)) {
39 | ig.igPushItemWidth(ig.igGetWindowWidth() * 0.5);
40 | _ = ig.igDragFloat("Camera Rotation", &rotation, 0.02, zt.math.toRadians(-360.0), zt.math.toRadians(360.0), "%.3f", ig.ImGuiSliderFlags_None);
41 | _ = ig.igDragFloat("Camera Zoom", &zoom, 0.02, 0.1, 16, "%.3f", ig.ImGuiSliderFlags_None);
42 | ig.igPopItemWidth();
43 | }
44 | ig.igEnd();
45 |
46 | // Display the rendertarget
47 | ig.igSetNextWindowPos(zt.math.vec2(io.*.DisplaySize.x, 0), ig.ImGuiCond_Appearing, .{ .x = 1, .y = 0 });
48 | if (ig.igBegin("RenderTarget Demo Viewer", null, ig.ImGuiWindowFlags_NoScrollbar)) {
49 | var contentSpace: zt.math.Vec2 = .{};
50 | ig.igGetContentRegionAvail(&contentSpace);
51 | const ratio = contentSpace.x / rt.?.target.width;
52 |
53 | // With opengl and imgui, you need to flip the y source vectors.
54 | const uv1 = zt.math.vec2(0, 1);
55 | const uv2 = zt.math.vec2(1, 0);
56 | const size = zt.math.vec2(contentSpace.x, rt.?.target.height * ratio);
57 | ig.igImage(rt.?.target.imguiId(), size, uv1, uv2, zt.math.Vec4.white, zt.math.Vec4.white);
58 |
59 | if (ig.igButton("Update RT", .{})) {
60 | drawIntoRT(ctx);
61 | }
62 | ig.igSameLine(0, 2);
63 | if (ig.igButton("Nearest", .{})) {
64 | rt.?.target.setNearestFilter();
65 | }
66 | ig.igSameLine(0, 2);
67 | if (ig.igButton("Linear", .{})) {
68 | rt.?.target.setLinearFilter();
69 | }
70 | }
71 | ig.igEnd();
72 | }
73 |
74 | fn drawIntoRT(ctx: *main.SampleApplication.Context) void {
75 | const gl = @import("gl");
76 |
77 | var render = ctx.data.render;
78 | rt.?.bind();
79 | // We need to manually clear the buffer:
80 | gl.glClear(gl.GL_COLOR_BUFFER_BIT);
81 | render.updateRenderSize(zt.math.vec2(rt.?.target.width, rt.?.target.height));
82 | render.updateCamera(.{}, zoom, rotation);
83 | render.sprite(ctx.data.sheet, .{}, 0, zt.math.vec2(32, 32), zt.math.Vec4.white, zt.math.vec2(0.5, 0.5), zt.math.rect(16, 0, 16, 16));
84 | render.flush();
85 | rt.?.unbind();
86 | }
87 |
--------------------------------------------------------------------------------
/example/src/scenes/renderer.zig:
--------------------------------------------------------------------------------
1 | const zt = @import("zt");
2 | const main = @import("../main.zig");
3 | const ig = @import("imgui");
4 | const zg = zt.custom_components;
5 |
6 | var rotation: f32 = 0.0;
7 | var scale: f32 = 1.0;
8 |
9 | var lineStart: zt.math.Vec2 = .{ .x = -200, .y = 200 };
10 | var lineEnd: zt.math.Vec2 = .{ .x = 200, .y = 200 };
11 | var startColor: zt.math.Vec4 = zt.math.Vec4.white;
12 | var endColor: zt.math.Vec4 = zt.math.Vec4.white;
13 | var thickness: f32 = 2;
14 |
15 | var radius: f32 = 10;
16 |
17 | pub fn update(ctx: *main.SampleApplication.Context) void {
18 | control(ctx);
19 |
20 | var render = ctx.data.render;
21 | const io = ig.igGetIO();
22 |
23 | // It's important to set the render size, then the camera. This applies the matrices used to display all the sprites.
24 | render.updateRenderSize(io.*.DisplaySize);
25 | render.updateCamera(.{}, scale, rotation);
26 |
27 | var y: i32 = -4;
28 | var x: i32 = -4;
29 | while (y <= 4) : (y += 1) {
30 | while (x <= 4) : (x += 1) {
31 | const pos = zt.math.vec2(32 * @as(f32, @floatFromInt(x)), 32 * @as(f32, @floatFromInt(y)));
32 | render.sprite(ctx.data.sheet, pos, 0, zt.math.vec2(32, 32), zt.math.Vec4.white, zt.math.vec2(0.5, 0.5), zt.math.rect(16, 0, 16, 16));
33 | }
34 | x = -4;
35 | }
36 |
37 | render.sprite(ctx.data.inlined, .{}, 0, zt.math.vec2(32, 32), zt.math.Vec4.white, null, null);
38 |
39 | // Text in the renderer is very rudimentary and not really intended for ingame use in world space.
40 | // All renderer does is add text to imgui's background drawlist. As such you should transform world to screenspace.
41 | const message =
42 | \\Sadly, text is forwarded by imgui and not affected by matrices.
43 | \\Note this also means nothing but imgui itself can be infront of this text.
44 | \\But fear not! It wouldn't be difficult to implement your own text if needed.
45 | ;
46 | render.text(render.worldToScreen(.{ .x = -128 - 16, .y = -190 }), message, zt.math.Vec4.white);
47 |
48 | // You can also draw lines. In this example I want a solid white line, so the source rect is somewhere on the sheet
49 | // that is pure white.
50 | render.line(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), lineStart, lineEnd, 0, thickness, startColor, endColor);
51 |
52 | // Surround the grid with a hollow rect.
53 | render.rectangleHollow(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), zt.math.rect(-128, -128, 256, 256), 0, 2.0, zt.math.vec4(0.0, 1.0, 0.0, 1.0));
54 |
55 | // Surround the grid with a hollow rect.
56 | render.circle(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), zt.math.vec2(0, 200), radius, 0, zt.math.vec4(0.0, 1.0, 0.0, 1.0));
57 |
58 | // Renderer also has no clue when a frame ends, so you must manually flush at the end of every render cycle.
59 | // It is not required to do this between texture changes, as this is handled internally.
60 | render.flush();
61 | }
62 |
63 | fn control(ctx: *main.SampleApplication.Context) void {
64 | const io = ig.igGetIO();
65 | ig.igSetNextWindowPos(io.*.DisplaySize, ig.ImGuiCond_Appearing, .{ .x = 1, .y = 1 });
66 | if (ig.igBegin("Renderer Demo Settings", null, ig.ImGuiWindowFlags_None)) {
67 | ig.igPushItemWidth(ig.igGetWindowWidth() * 0.5);
68 | _ = ig.igDragFloat("Camera Rotation", &rotation, 0.02, zt.math.toRadians(-360.0), zt.math.toRadians(360.0), "%.3f", ig.ImGuiSliderFlags_None);
69 | _ = ig.igDragFloat("Camera Zoom", &scale, 0.02, 0.1, 16, "%.3f", ig.ImGuiSliderFlags_None);
70 | ig.igSeparator();
71 | _ = zg.editDrag("Line Start", 0.1, &lineStart);
72 | _ = zg.editDrag("Line End", 0.1, &lineEnd);
73 | _ = zg.editDrag("Line Thickness", 0.1, &thickness);
74 | _ = zg.editDrag("Line Start color", 0.1, &startColor);
75 | _ = zg.editDrag("Line End color", 0.1, &endColor);
76 | ig.igSeparator();
77 | _ = zg.editDrag("Circle Radius", 0.1, &radius);
78 | _ = zg.editDrag("Circle Resolution", 0.1, &ctx.data.render.resolution);
79 | ig.igPopItemWidth();
80 | }
81 | ig.igEnd();
82 | }
83 |
--------------------------------------------------------------------------------
/example/src/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const zt = @import("zt");
3 | const ig = @import("imgui");
4 | const zg = zt.custom_components;
5 |
6 | const scenes_n = [_][]const u8{
7 | "2D Rendering",
8 | "2D Shaders",
9 | "2D Render Targets",
10 | "2D Spatial Hash (Squares)",
11 | "2D Collider Support",
12 | "ImGui Demo",
13 | };
14 | const scenes = [_]*const fn (*SampleApplication.Context) void{
15 | @import("scenes/renderer.zig").update,
16 | @import("scenes/shaders.zig").update,
17 | @import("scenes/rendertarget.zig").update,
18 | @import("scenes/spatialhash_squares.zig").update,
19 | @import("scenes/colliders.zig").update,
20 | @import("scenes/imgui.zig").update,
21 | };
22 |
23 | pub const embeddedImage: []const u8 = @embedFile("inline.png");
24 |
25 | /// SampleData will be available through the application's context.
26 | pub const SampleData = struct {
27 | currentScene: usize = 0,
28 | render: zt.game.Renderer = undefined,
29 | inlined: zt.gl.Texture = undefined,
30 | sheet: zt.gl.Texture = undefined,
31 | redShader: zt.gl.Shader = undefined,
32 | consoleOpen: bool = true,
33 | };
34 | pub const SampleApplication = zt.App(SampleData);
35 |
36 | pub fn main() !void {
37 | var context = try SampleApplication.begin(std.heap.c_allocator);
38 |
39 | // Lets customize!
40 | const io = ig.igGetIO();
41 | const font = context.addFont(zt.path("public-sans.ttf"), 14.0);
42 | io.*.FontDefault = font;
43 |
44 | // Set up state
45 | context.settings.energySaving = false; // Some examples are games, and will benefit from this.
46 | context.data.render = zt.game.Renderer.init();
47 | context.data.sheet = try zt.gl.Texture.init(zt.path("texture/sheet.png"));
48 | context.data.sheet.setNearestFilter(); // Pixel art looks better with nearest filters.
49 | // Creating a shader from `zt.game.Renderer` only needs a fragment shader's source as the vertex shader
50 | // will be provided by `zt.game.Renderer`. If you need more flexibility than this you'll want to
51 | // edit ZT itself, or create your own buffer type.
52 | context.data.redShader = zt.game.Renderer.createShader(@embedFile("scenes/shader/red.fragment"));
53 | context.data.inlined = try zt.gl.Texture.initMemory(embeddedImage);
54 |
55 | context.setWindowSize(1280, 720);
56 | context.setWindowTitle("ZT Demo");
57 | context.setWindowIcon(zt.path("texture/ico.png"));
58 |
59 | // You control your own main loop, all you got to do is call begin and end frame,
60 | // and zt will handle the rest.
61 | while (context.open) {
62 | context.beginFrame();
63 | inspectContext(context);
64 |
65 | // Call into the selected demo:
66 | const index = std.math.clamp(context.data.currentScene, 0, scenes.len - 1);
67 | scenes[index](context);
68 |
69 | context.endFrame();
70 | }
71 |
72 | context.data.sheet.deinit();
73 | context.data.render.deinit();
74 | context.deinit();
75 | }
76 |
77 | // This is a simple side panel that will display information about the scene, your context, and settings.
78 | fn inspectContext(ctx: *SampleApplication.Context) void {
79 |
80 | // Basic toggle
81 | const glfw = @import("glfw");
82 | const io = ig.igGetIO();
83 | if (io.*.KeysData[@intFromEnum(glfw.Key.grave_accent)].DownDuration == 0.0) {
84 | ctx.data.consoleOpen = !ctx.data.consoleOpen;
85 | }
86 | if (!ctx.data.consoleOpen) return;
87 |
88 | const flags =
89 | ig.ImGuiWindowFlags_NoMove |
90 | ig.ImGuiWindowFlags_NoTitleBar |
91 | ig.ImGuiWindowFlags_NoResize;
92 |
93 | ig.igSetNextWindowPos(zt.math.vec2(8, 8), ig.ImGuiCond_Always, .{});
94 | ig.igSetNextWindowSize(zt.math.vec2(300, io.*.DisplaySize.y - 16), ig.ImGuiCond_Always);
95 | if (ig.igBegin("Context Inspection", null, flags)) {
96 | ig.igText("Data Settings");
97 | // zg.edit("Current Scene", &ctx.data.currentScene);
98 | if (ig.igBeginListBox("##Listbox pog", .{})) {
99 | var i: usize = 0;
100 | while (i < scenes.len) : (i += 1) {
101 | if (ig.igSelectable_Bool(scenes_n[i].ptr, i == ctx.data.currentScene, ig.ImGuiSelectableFlags_SpanAvailWidth, .{})) {
102 | ctx.data.currentScene = i;
103 | }
104 | }
105 | ig.igEndListBox();
106 | }
107 | ig.igSeparator();
108 |
109 | ig.igText("Settings");
110 | _ = zg.edit("Energy Saving", &ctx.settings.energySaving);
111 | if (ig.igCheckbox("V Sync", &ctx.settings.vsync)) {
112 | // The vsync setting is only a getter, setting it does nothing.
113 | // So on change, we follow through with the real call that changes it.
114 | ctx.setVsync(ctx.settings.vsync);
115 | }
116 | _ = zg.edit("ig.ImGui Active (Warning!!)", &ctx.settings.imguiActive);
117 | ig.igSeparator();
118 |
119 | ig.igText("Information");
120 | zg.text("{d:.1}fps", .{ctx.time.fps});
121 | }
122 | ig.igEnd();
123 | }
124 |
--------------------------------------------------------------------------------
/example/src/scenes/colliders.zig:
--------------------------------------------------------------------------------
1 | const zt = @import("zt");
2 | const main = @import("../main.zig");
3 | const ig = @import("imgui");
4 | const zg = zt.custom_components;
5 |
6 | var rotation: f32 = 0.0;
7 | var scale: f32 = 1.0;
8 |
9 | var verts = [_]zt.math.Vec2{ zt.math.vec2(-70, 0), zt.math.vec2(0, -50), zt.math.vec2(-20, 0), zt.math.vec2(0, 50) };
10 |
11 | var rect: zt.game.Physics.Shape = zt.game.Physics.Shape.rectangle(.{}, .{ .x = 100, .y = 50 });
12 | var rectPos: zt.math.Vec2 = .{};
13 | var line: zt.game.Physics.Shape = zt.game.Physics.Shape.line(.{}, .{ .x = 100, .y = 100 });
14 | var linePos: zt.math.Vec2 = .{};
15 | var circle: zt.game.Physics.Shape = zt.game.Physics.Shape.circle(20.0);
16 | var circlePos: zt.math.Vec2 = .{};
17 | var point: zt.game.Physics.Shape = zt.game.Physics.Shape.point(.{});
18 | var pointPos: zt.math.Vec2 = .{};
19 | var poly: zt.game.Physics.Shape = zt.game.Physics.Shape{ .Polygon = .{ .vertices = &verts } };
20 | var polyPos: zt.math.Vec2 = .{};
21 |
22 | pub fn update(ctx: *main.SampleApplication.Context) void {
23 | control();
24 |
25 | var render = ctx.data.render;
26 | const io = ig.igGetIO();
27 |
28 | // It's important to set the render size, then the camera. This applies the matrices used to display all the sprites.
29 | render.updateRenderSize(io.*.DisplaySize);
30 | render.updateCamera(.{}, scale, rotation);
31 |
32 | // Line Checks.
33 | {
34 | const col = if (line.overlaps(linePos, rect, rectPos) or
35 | line.overlaps(linePos, circle, circlePos) or
36 | line.overlaps(linePos, poly, polyPos) or
37 | line.overlaps(linePos, point, pointPos)) zt.math.Vec4.white else zt.math.vec4(0.0, 0.5, 0.5, 0.7);
38 | render.line(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), linePos.add(line.Line.start), linePos.add(line.Line.end), 0, 4.0, col, col);
39 | }
40 | // Circle Checks.
41 | {
42 | const col = if (circle.overlaps(circlePos, rect, rectPos) or
43 | circle.overlaps(circlePos, line, linePos) or
44 | circle.overlaps(circlePos, poly, polyPos) or
45 | circle.overlaps(circlePos, point, pointPos)) zt.math.Vec4.white else zt.math.vec4(0.0, 0.5, 0.5, 0.7);
46 | render.circle(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), circlePos, circle.Circle.radius, 0, col);
47 | }
48 | // Rect Checks.
49 | {
50 | const col = if (rect.overlaps(rectPos, line, linePos) or
51 | rect.overlaps(rectPos, circle, circlePos) or
52 | rect.overlaps(rectPos, poly, polyPos) or
53 | rect.overlaps(rectPos, point, pointPos)) zt.math.Vec4.white else zt.math.vec4(0.0, 0.5, 0.5, 0.7);
54 |
55 | var renderPos = rect.Rectangle;
56 | renderPos.position = renderPos.position.add(rectPos);
57 | render.rectangleHollow(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), renderPos, 0, 4.0, col);
58 | }
59 | // Point Checks.
60 | {
61 | const col = if (point.overlaps(pointPos, line, linePos) or
62 | point.overlaps(pointPos, circle, circlePos) or
63 | point.overlaps(pointPos, rect, rectPos) or
64 | point.overlaps(pointPos, poly, polyPos)) zt.math.Vec4.white else zt.math.vec4(0.0, 0.5, 0.5, 0.7);
65 |
66 | const renderPos = zt.math.rect(point.Point.x + pointPos.x - 1, point.Point.y + pointPos.y - 1, 2, 2);
67 | render.rectangleHollow(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), renderPos, 0, 4.0, col);
68 | }
69 |
70 | // Poly Checks.
71 | {
72 | const col = if (poly.overlaps(polyPos, line, linePos) or
73 | poly.overlaps(polyPos, circle, circlePos) or
74 | poly.overlaps(polyPos, point, pointPos) or
75 | poly.overlaps(polyPos, rect, rectPos)) zt.math.Vec4.white else zt.math.vec4(0.0, 0.5, 0.5, 0.7);
76 |
77 | for (poly.Polygon.vertices, 0..) |v, i| {
78 | const next = if (i + 1 == poly.Polygon.vertices.len) poly.Polygon.vertices[0] else poly.Polygon.vertices[i + 1];
79 | const pos = v.add(polyPos);
80 | const nextPos = next.add(polyPos);
81 | render.line(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), pos, nextPos, 0, 4.0, col, col);
82 | }
83 | }
84 |
85 | render.flush();
86 | }
87 |
88 | fn control() void {
89 | const io = ig.igGetIO();
90 | ig.igSetNextWindowPos(io.*.DisplaySize, ig.ImGuiCond_Appearing, .{ .x = 1, .y = 1 });
91 | if (ig.igBegin("Renderer Demo Settings", null, ig.ImGuiWindowFlags_None)) {
92 | ig.igPushItemWidth(ig.igGetWindowWidth() * 0.5);
93 | _ = ig.igDragFloat("Camera Rotation", &rotation, 0.02, zt.math.toRadians(-360.0), zt.math.toRadians(360.0), "%.3f", ig.ImGuiSliderFlags_None);
94 | _ = ig.igDragFloat("Camera Zoom", &scale, 0.02, 0.1, 16, "%.3f", ig.ImGuiSliderFlags_None);
95 | ig.igSeparator();
96 | _ = zg.editDrag("Line Pos", 0.1, &linePos);
97 | _ = zg.editDrag("Rect Pos", 0.1, &rectPos);
98 | _ = zg.editDrag("Circle Pos", 0.1, &circlePos);
99 | _ = zg.editDrag("Point Pos", 0.1, &pointPos);
100 | _ = zg.editDrag("Poly Pos", 0.1, &polyPos);
101 | ig.igPopItemWidth();
102 | }
103 | ig.igEnd();
104 | }
105 |
--------------------------------------------------------------------------------
/src/pkg/stb_image.zig:
--------------------------------------------------------------------------------
1 | pub const struct__iobuf = extern struct {
2 | _ptr: [*c]u8,
3 | _cnt: c_int,
4 | _base: [*c]u8,
5 | _flag: c_int,
6 | _file: c_int,
7 | _charbuf: c_int,
8 | _bufsiz: c_int,
9 | _tmpfname: [*c]u8,
10 | };
11 | pub const FILE = struct__iobuf;
12 | pub const stbi_uc = u8;
13 | pub const stbi_us = c_ushort;
14 | pub const stbi_io_callbacks = extern struct {
15 | read: ?*const fn (?*anyopaque, [*c]u8, c_int) callconv(.C) c_int,
16 | skip: ?*const fn (?*anyopaque, c_int) callconv(.C) void,
17 | eof: ?*const fn (?*anyopaque) callconv(.C) c_int,
18 | };
19 | pub extern fn stbi_load_from_memory(buffer: [*c]const stbi_uc, len: c_int, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]stbi_uc;
20 | pub extern fn stbi_load_from_callbacks(clbk: [*c]const stbi_io_callbacks, user: ?*anyopaque, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]stbi_uc;
21 | pub extern fn stbi_load(filename: [*c]const u8, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]stbi_uc;
22 | pub extern fn stbi_load_from_file(f: [*c]FILE, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]stbi_uc;
23 | pub extern fn stbi_load_gif_from_memory(buffer: [*c]const stbi_uc, len: c_int, delays: [*c][*c]c_int, x: [*c]c_int, y: [*c]c_int, z: [*c]c_int, comp: [*c]c_int, req_comp: c_int) [*c]stbi_uc;
24 | pub extern fn stbi_load_16_from_memory(buffer: [*c]const stbi_uc, len: c_int, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]stbi_us;
25 | pub extern fn stbi_load_16_from_callbacks(clbk: [*c]const stbi_io_callbacks, user: ?*anyopaque, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]stbi_us;
26 | pub extern fn stbi_load_16(filename: [*c]const u8, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]stbi_us;
27 | pub extern fn stbi_load_from_file_16(f: [*c]FILE, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]stbi_us;
28 | pub extern fn stbi_loadf_from_memory(buffer: [*c]const stbi_uc, len: c_int, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]f32;
29 | pub extern fn stbi_loadf_from_callbacks(clbk: [*c]const stbi_io_callbacks, user: ?*anyopaque, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]f32;
30 | pub extern fn stbi_loadf(filename: [*c]const u8, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]f32;
31 | pub extern fn stbi_loadf_from_file(f: [*c]FILE, x: [*c]c_int, y: [*c]c_int, channels_in_file: [*c]c_int, desired_channels: c_int) [*c]f32;
32 | pub extern fn stbi_hdr_to_ldr_gamma(gamma: f32) void;
33 | pub extern fn stbi_hdr_to_ldr_scale(scale: f32) void;
34 | pub extern fn stbi_ldr_to_hdr_gamma(gamma: f32) void;
35 | pub extern fn stbi_ldr_to_hdr_scale(scale: f32) void;
36 | pub extern fn stbi_is_hdr_from_callbacks(clbk: [*c]const stbi_io_callbacks, user: ?*anyopaque) c_int;
37 | pub extern fn stbi_is_hdr_from_memory(buffer: [*c]const stbi_uc, len: c_int) c_int;
38 | pub extern fn stbi_is_hdr(filename: [*c]const u8) c_int;
39 | pub extern fn stbi_is_hdr_from_file(f: [*c]FILE) c_int;
40 | pub extern fn stbi_failure_reason() [*c]const u8;
41 | pub extern fn stbi_image_free(retval_from_stbi_load: ?*anyopaque) void;
42 | pub extern fn stbi_info_from_memory(buffer: [*c]const stbi_uc, len: c_int, x: [*c]c_int, y: [*c]c_int, comp: [*c]c_int) c_int;
43 | pub extern fn stbi_info_from_callbacks(clbk: [*c]const stbi_io_callbacks, user: ?*anyopaque, x: [*c]c_int, y: [*c]c_int, comp: [*c]c_int) c_int;
44 | pub extern fn stbi_is_16_bit_from_memory(buffer: [*c]const stbi_uc, len: c_int) c_int;
45 | pub extern fn stbi_is_16_bit_from_callbacks(clbk: [*c]const stbi_io_callbacks, user: ?*anyopaque) c_int;
46 | pub extern fn stbi_info(filename: [*c]const u8, x: [*c]c_int, y: [*c]c_int, comp: [*c]c_int) c_int;
47 | pub extern fn stbi_info_from_file(f: [*c]FILE, x: [*c]c_int, y: [*c]c_int, comp: [*c]c_int) c_int;
48 | pub extern fn stbi_is_16_bit(filename: [*c]const u8) c_int;
49 | pub extern fn stbi_is_16_bit_from_file(f: [*c]FILE) c_int;
50 | pub extern fn stbi_set_unpremultiply_on_load(flag_true_if_should_unpremultiply: c_int) void;
51 | pub extern fn stbi_convert_iphone_png_to_rgb(flag_true_if_should_convert: c_int) void;
52 | pub extern fn stbi_set_flip_vertically_on_load(flag_true_if_should_flip: c_int) void;
53 | pub extern fn stbi_set_unpremultiply_on_load_thread(flag_true_if_should_unpremultiply: c_int) void;
54 | pub extern fn stbi_convert_iphone_png_to_rgb_thread(flag_true_if_should_convert: c_int) void;
55 | pub extern fn stbi_set_flip_vertically_on_load_thread(flag_true_if_should_flip: c_int) void;
56 | pub extern fn stbi_zlib_decode_malloc_guesssize(buffer: [*c]const u8, len: c_int, initial_size: c_int, outlen: [*c]c_int) [*c]u8;
57 | pub extern fn stbi_zlib_decode_malloc_guesssize_headerflag(buffer: [*c]const u8, len: c_int, initial_size: c_int, outlen: [*c]c_int, parse_header: c_int) [*c]u8;
58 | pub extern fn stbi_zlib_decode_malloc(buffer: [*c]const u8, len: c_int, outlen: [*c]c_int) [*c]u8;
59 | pub extern fn stbi_zlib_decode_buffer(obuffer: [*c]u8, olen: c_int, ibuffer: [*c]const u8, ilen: c_int) c_int;
60 | pub extern fn stbi_zlib_decode_noheader_malloc(buffer: [*c]const u8, len: c_int, outlen: [*c]c_int) [*c]u8;
61 | pub extern fn stbi_zlib_decode_noheader_buffer(obuffer: [*c]u8, olen: c_int, ibuffer: [*c]const u8, ilen: c_int) c_int;
62 |
--------------------------------------------------------------------------------
/src/zt/texture.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const stb = @import("stb_image");
3 | const gl = @import("gl");
4 | const Self = @This();
5 |
6 | id: c_uint = undefined,
7 | width: f32 = undefined,
8 | height: f32 = undefined,
9 | dead: bool = true,
10 |
11 | /// Loads an already existing opengl texture from a c_uint
12 | /// inDepth optionally inspects the opengl texture to fill in texture width/height information.
13 | pub fn from(id: c_uint, inDepth: bool) Self {
14 | var self: Self = .{ .id = id, .dead = false };
15 | if (inDepth) {
16 | self.updateInformation();
17 | }
18 | return self;
19 | }
20 | /// Takes a file path and loads it into opengl using stb_image.
21 | pub fn init(filePath: []const u8) !Self {
22 | const ownedFp: [:0]const u8 = try std.heap.c_allocator.dupeZ(u8, filePath);
23 | defer std.heap.c_allocator.free(ownedFp);
24 | var w: c_int = 0;
25 | var h: c_int = 0;
26 | var numChannels: c_int = 0;
27 | const data = stb.stbi_load(ownedFp.ptr, &w, &h, &numChannels, 0);
28 | var self = Self{};
29 |
30 | self.width = @floatFromInt(w);
31 | self.height = @floatFromInt(h);
32 |
33 | gl.glGenTextures(1, &self.id);
34 | gl.glBindTexture(gl.GL_TEXTURE_2D, self.id);
35 |
36 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_REPEAT);
37 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_REPEAT);
38 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR);
39 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR);
40 | switch (numChannels) {
41 | 3 => {
42 | gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, w, h, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, data);
43 | },
44 | 4 => {
45 | gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, w, h, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, data);
46 | },
47 | else => {
48 | std.debug.print("ERROR! Failed to compile texture {s} with {any} channels.\n", .{ filePath, numChannels });
49 | gl.glBindTexture(gl.GL_TEXTURE_2D, 0);
50 | return error.FailedToInit;
51 | },
52 | }
53 | gl.glGenerateMipmap(gl.GL_TEXTURE_2D);
54 | stb.stbi_image_free(data);
55 | self.dead = false;
56 |
57 | gl.glBindTexture(gl.GL_TEXTURE_2D, 0); // Init isnt an explicit bind, so reset.
58 |
59 | return self;
60 | }
61 | pub fn initBlank(width: c_int, height: c_int) Self {
62 | var self = Self{};
63 | self.width = @floatFromInt(width);
64 | self.height = @floatFromInt(height);
65 | gl.glGenTextures(1, &self.id);
66 | gl.glBindTexture(gl.GL_TEXTURE_2D, self.id);
67 | gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, null);
68 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR);
69 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR);
70 | gl.glBindTexture(gl.GL_TEXTURE_2D, 0);
71 |
72 | return self;
73 | }
74 | pub fn initMemory(slice: []const u8) !Self {
75 | var w: c_int = 0;
76 | var h: c_int = 0;
77 | var numChannels: c_int = 0;
78 | const data = stb.stbi_load_from_memory(slice.ptr, @intCast(slice.len), &w, &h, &numChannels, 0);
79 |
80 | var self = Self{};
81 |
82 | self.width = @floatFromInt(w);
83 | self.height = @floatFromInt(h);
84 |
85 | gl.glGenTextures(1, &self.id);
86 | gl.glBindTexture(gl.GL_TEXTURE_2D, self.id);
87 |
88 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_REPEAT);
89 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_REPEAT);
90 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR);
91 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR);
92 | switch (numChannels) {
93 | 3 => {
94 | gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, w, h, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, data);
95 | },
96 | 4 => {
97 | gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, w, h, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, data);
98 | },
99 | else => {
100 | std.debug.print("ERROR! Failed to compile memory texture with {any} channels.\n", .{numChannels});
101 | gl.glBindTexture(gl.GL_TEXTURE_2D, 0);
102 | return error.FailedToInit;
103 | },
104 | }
105 | gl.glGenerateMipmap(gl.GL_TEXTURE_2D);
106 | stb.stbi_image_free(data);
107 | self.dead = false;
108 |
109 | gl.glBindTexture(gl.GL_TEXTURE_2D, 0); // Init isnt an explicit bind, so reset.
110 |
111 | return self;
112 | }
113 | pub fn deinit(self: *Self) void {
114 | gl.glDeleteTextures(1, &self.id);
115 | self.dead = true;
116 | }
117 | /// using Texture.from(c_uint) is a naive cast that wont query size to generate information.
118 | fn updateInformation(self: *Self) void {
119 | self.bind();
120 | var w: c_int = 0;
121 | var h: c_int = 0;
122 | gl.glGetTexLevelParameteriv(gl.GL_TEXTURE_2D, 0, gl.GL_TEXTURE_WIDTH, &w);
123 | gl.glGetTexLevelParameteriv(gl.GL_TEXTURE_2D, 0, gl.GL_TEXTURE_HEIGHT, &h);
124 | self.width = @floatFromInt(w);
125 | self.height = @floatFromInt(h);
126 | }
127 | pub fn bind(self: *Self) void {
128 | gl.glActiveTexture(gl.GL_TEXTURE0);
129 | gl.glBindTexture(gl.GL_TEXTURE_2D, self.id);
130 | }
131 | pub fn unbind(self: *Self) void {
132 | _ = self;
133 | gl.glBindTexture(gl.GL_TEXTURE_2D, 0);
134 | }
135 | pub fn setNearestFilter(self: *Self) void {
136 | gl.glBindTexture(gl.GL_TEXTURE_2D, self.id);
137 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST);
138 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST);
139 | gl.glBindTexture(gl.GL_TEXTURE_2D, 0); // Clear
140 | }
141 | pub fn setLinearFilter(self: *Self) void {
142 | gl.glBindTexture(gl.GL_TEXTURE_2D, self.id);
143 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR);
144 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR);
145 | gl.glBindTexture(gl.GL_TEXTURE_2D, 0); // Clear
146 | }
147 | /// Use this to get the correct Texture ID for use in imgui.
148 | pub fn imguiId(self: *Self) *anyopaque {
149 | return @as(*anyopaque, @ptrFromInt(self.id));
150 | }
151 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ## DISCONTINUED
6 |
7 | I had hoped to come back to this when Zig was a bit more mature to actively maintain ZT,
8 | but the decision to remove LLVM from the zig toolchain has killed any advantage I viewed
9 | as a benefit for the language, having a universal consistent toolchain for C that I could
10 | rely on existing for everyone who would use Zig was an immeasurable benefit.
11 |
12 | The need for an external package, or a self installed version of C turned my interest in
13 | a consistent and powerful language built to turn C code into amazing Zig companions, into
14 | the mess I didn't want to come back to from other languages that try to do what Zig did.
15 |
16 | The toolchain that made me confident that replication was perfectly stable is now (im pretty sure? I'd love to be wrong about this) back to
17 | hoping end users have the right toolchain installed next to zig, installation instructions, "try it on clang/msvc/llvm instead" that I wanted
18 | to avoid from other languages. Sorry, loved the time I spent with it, maybe check out Mach and adding your own imgui layer in that, if it doesnt
19 | have one already.
20 |
21 | ## Old readme
22 |
23 | A zig-contained library for Windows and Ubuntu that automatically compiles and
24 | links ImGui, OpenGL, stb_image, and GLFW into typed packages.
25 |
26 | ZT will always target the latest dev version of Zig, and will
27 | create a branch for stable releases when convenient.
28 |
29 | Check out the [wiki](https://github.com/JonSnowbd/ZT/wiki) for documentation and help.
30 |
31 | For Applications
32 |
33 |
34 |
35 |
36 | Get your applications done quick with industry standard ImGui library, used
37 | by a bunch of applications for its convenience and power.
38 |
39 | Using ImGui is as simple as calling the functions! ZT will render
40 | everything for you without ever needing to touch gamedev code.
41 |
42 |
43 |
44 | For Games
45 |
46 |
47 |
48 |
49 | With ImGui at the forefront for free, debugging and creating editors
50 | for your game is as smooth as it can be without deciding anything for you
51 |
52 |
53 |
54 | # Overview
55 |
56 | To work with ZT You will need:
57 |
58 | - Zig 0.11.* Main branch build. Nightly might work as well (last tested `0.12.0-dev.1856+94c63f31f`).
59 | - Ubuntu: `sudo apt install build-essential xorg-dev`
60 |
61 | ### Current Status
62 |
63 | - ZT when used for the purpose of GL/ImGui libraries is very stable
64 | - ZT.App is still receiving breaking changes as I find where I can make
65 | the library more flexible for casual use, but overall I find it convenient for
66 | applications and games
67 |
68 | See [the example](/example/src/main.zig) for what ZT.App has to offer.
69 |
70 | ### Why
71 |
72 | ZT is intended for an extremely broad group of developers in zig realtime graphics and applications, as it does not railroad you into
73 | using its app+windowing interface to function, and is better viewed as the following goals being accomplished without
74 | any resistance:
75 |
76 | - Linear Algebra and Math types
77 | - Cross platform windowing (GLFW) within Zig
78 | - OpenGL binding within Zig
79 | - Completely up to date (c)ImGui Docking branch bindings within Zig
80 | - STB_Image bindings within Zig
81 | - (Optional) barebones wrappers around basic opengl constructs like shaders/textures/buffers
82 |
83 | and additionally a ready to go combination of all 3 that lets you just immediately use close to the metal
84 | OpenGL constructs to just work on your application with convenience for use as desktop application code such as
85 | Energy Saving mode.
86 |
87 | # Getting Started
88 |
89 | First you'll want to clone this into your zig project's folder, and `const ztBuild = @import("path/to/ZT/build.zig")`
90 | in your own `build.zig` to import this framework's build.zig, and that will expose some important functions
91 | to link ZT into your project.
92 |
93 | - `ztBuild.link(exe)` will add ZT's packages to your exe and link the source files for GLFW/GL/ImGui
94 | - (optional) `ztBuild.addBinaryContent("path/to/binContent")` adds binary content to your zig-out folder output, basically the folder structure
95 | ends up being as if `path/to/binContent` was the root folder containing your executable. This is smart and will skip older assets.
96 |
97 | So with `ztBuild` imported you just `ztBuild.link(exe)` and you can start importing and using
98 | ZT, or if you so choose, completely ignore ZT and use raw opengl/glfw/imgui.
99 |
100 | Then getting started is as easy as this:
101 |
102 | ```Zig
103 | const std = @import("std");
104 | const zt = @import("zt");
105 |
106 | /// SampleData will be available through the context anywhere.
107 | const SampleData = struct {
108 | yourData: i32 = 0,
109 | };
110 |
111 | const SampleApplication = zt.App(SampleData);
112 |
113 | pub fn main() !void {
114 | var context = try SampleApplication.begin(std.heap.c_allocator);
115 | // Config here,
116 | while(context.open) {
117 | context.beginFrame();
118 | // Application code here!
119 | context.data.yourData += 1;
120 | context.endFrame();
121 | }
122 | // Unload here
123 | context.deinit();
124 | }
125 | ```
126 |
127 | For a more indepth example [see the example file that shows opengl rendering mixed with imgui and more](example/src/main.zig)
128 |
129 | ## Gotcha:
130 |
131 | - ZT.App.begin sets its own GLFW user pointer! Its important too, so use something else for your storage, or if you really want the functionality,
132 | let me know and I'll see how I can enable your usecase within ZT.
133 | - By linking ZT the following packages are available to your app on both windows and ubuntu: `zt`, `gl`, `glfw`, `imgui`, `stb_image`
134 | - ImVec2 and ImVec4 are both substituted with zlm's Vec2 and Vec4 structs respectively, you can use both interchangeably.
135 | - Disabling power saving mode will let GLFW handle buffer flip timing, so likely will be at vsync fps rather than on every
136 | event, unless you disable vsync.
137 | - Need direct access to the input queue? Your context contains an ArrayList of tagged unions that summarizes every input event.
138 | Try to use this instead of overriding GLFW event callbacks.
139 |
140 | ## Credits
141 |
142 | - Example Font - https://github.com/uswds/public-sans
143 | - Inspiration and Code Snippets - https://github.com/digitalcreature/ube (Thanks sammy for all the help!)
144 |
--------------------------------------------------------------------------------
/src/zt/styler.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const ig = @import("imgui");
3 | const zt = @import("../zt.zig");
4 | const math = zt.math;
5 |
6 | /// Sets imgui style to be compact, does not affect colors. Recommended to follow this up
7 | /// with your own custom colors, or one from this file `styleColor*()`
8 | pub fn styleSizeCompact() void {
9 | const style = ig.igGetStyle();
10 |
11 | // Paddings
12 | style.*.WindowPadding = .{ .x = 4, .y = 4 };
13 | style.*.FramePadding = .{ .x = 2, .y = 2 };
14 | style.*.CellPadding = .{ .x = 4, .y = 2 };
15 | style.*.ItemSpacing = .{ .x = 10, .y = 2 };
16 | style.*.ItemInnerSpacing = .{ .x = 2, .y = 2 };
17 | style.*.IndentSpacing = 12;
18 | style.*.ScrollbarSize = 4;
19 |
20 | // Borders
21 | style.*.TabBorderSize = 0;
22 | style.*.ChildBorderSize = 0;
23 | style.*.FrameBorderSize = 0;
24 | style.*.PopupBorderSize = 0;
25 | style.*.WindowBorderSize = 0;
26 |
27 | // Rounding
28 | style.*.TabRounding = 2;
29 | style.*.GrabRounding = 0;
30 | style.*.ChildRounding = 0;
31 | style.*.FrameRounding = 2;
32 | style.*.PopupRounding = 0;
33 | style.*.WindowRounding = 2;
34 | style.*.ScrollbarRounding = 0;
35 |
36 | // Align
37 | style.*.WindowTitleAlign = .{ .x = 0.5, .y = 0.5 };
38 | style.*.WindowMenuButtonPosition = ig.ImGuiDir_None;
39 | }
40 |
41 | /// Provide 4 colors, contrast, and if it is a light theme, and reroute will automatically
42 | /// style each of your imgui colors. If you're changing this and re-building often, prefer to use
43 | /// styleColorEditor() to toy with this in real time.
44 | pub fn styleColorCustom(background: ig.ImVec4, foreground: ig.ImVec4, highlight: ig.ImVec4, special: ig.ImVec4, contrast: f32, isLightTheme: bool) void {
45 | const bg0 = if (isLightTheme) background.brighten(contrast) else background.brighten(-contrast);
46 | const bg1 = background;
47 | const bg2 = if (isLightTheme) background.brighten(-contrast) else background.brighten(contrast);
48 |
49 | const fg0 = if (isLightTheme) foreground.brighten(contrast) else foreground.brighten(-contrast);
50 | const fg1 = foreground;
51 | const fg2 = if (isLightTheme) foreground.brighten(-contrast) else foreground.brighten(contrast);
52 |
53 | const hl0 = if (isLightTheme) highlight.brighten(contrast) else highlight.brighten(-contrast);
54 | const hl1 = highlight;
55 | const hl2 = if (isLightTheme) highlight.brighten(-contrast) else highlight.brighten(contrast);
56 |
57 | const sp0 = if (isLightTheme) special.brighten(contrast) else special.brighten(-contrast);
58 | const sp1 = special;
59 | const sp2 = if (isLightTheme) special.brighten(-contrast) else special.brighten(contrast);
60 |
61 | const style = ig.igGetStyle();
62 | style.*.Colors[ig.ImGuiCol_Text] = fg1;
63 | style.*.Colors[ig.ImGuiCol_TextDisabled] = fg0;
64 | style.*.Colors[ig.ImGuiCol_WindowBg] = bg0;
65 | style.*.Colors[ig.ImGuiCol_ChildBg] = bg0;
66 | style.*.Colors[ig.ImGuiCol_PopupBg] = bg0;
67 | style.*.Colors[ig.ImGuiCol_Border] = bg0.brighten(-0.5);
68 | style.*.Colors[ig.ImGuiCol_BorderShadow] = bg0.brighten(-0.5);
69 | style.*.Colors[ig.ImGuiCol_FrameBg] = if (isLightTheme) bg1.brighten(-0.33) else bg1.brighten(0.33);
70 | style.*.Colors[ig.ImGuiCol_FrameBgHovered] = bg2;
71 | style.*.Colors[ig.ImGuiCol_FrameBgActive] = bg2.brighten(0.7);
72 | style.*.Colors[ig.ImGuiCol_TitleBg] = hl0;
73 | style.*.Colors[ig.ImGuiCol_TitleBgActive] = hl1;
74 | style.*.Colors[ig.ImGuiCol_TitleBgCollapsed] = hl0;
75 | style.*.Colors[ig.ImGuiCol_MenuBarBg] = bg0.brighten(-0.1);
76 | style.*.Colors[ig.ImGuiCol_ScrollbarBg] = bg0;
77 | style.*.Colors[ig.ImGuiCol_ScrollbarGrab] = fg0;
78 | style.*.Colors[ig.ImGuiCol_ScrollbarGrabHovered] = fg1;
79 | style.*.Colors[ig.ImGuiCol_ScrollbarGrabActive] = fg2;
80 | style.*.Colors[ig.ImGuiCol_CheckMark] = hl0;
81 | style.*.Colors[ig.ImGuiCol_SliderGrab] = hl0;
82 | style.*.Colors[ig.ImGuiCol_SliderGrabActive] = hl2;
83 | style.*.Colors[ig.ImGuiCol_Button] = hl0;
84 | style.*.Colors[ig.ImGuiCol_ButtonHovered] = hl1;
85 | style.*.Colors[ig.ImGuiCol_ButtonActive] = hl2;
86 | style.*.Colors[ig.ImGuiCol_Header] = bg1;
87 | style.*.Colors[ig.ImGuiCol_HeaderHovered] = bg1;
88 | style.*.Colors[ig.ImGuiCol_HeaderActive] = bg2;
89 | style.*.Colors[ig.ImGuiCol_Separator] = bg2;
90 | style.*.Colors[ig.ImGuiCol_SeparatorHovered] = sp0;
91 | style.*.Colors[ig.ImGuiCol_SeparatorActive] = sp1;
92 | style.*.Colors[ig.ImGuiCol_ResizeGrip] = sp0;
93 | style.*.Colors[ig.ImGuiCol_ResizeGripHovered] = sp1;
94 | style.*.Colors[ig.ImGuiCol_ResizeGripActive] = sp2;
95 | style.*.Colors[ig.ImGuiCol_Tab] = bg2;
96 | style.*.Colors[ig.ImGuiCol_TabHovered] = bg2;
97 | style.*.Colors[ig.ImGuiCol_TabActive] = bg2.brighten(0.3);
98 | style.*.Colors[ig.ImGuiCol_TabUnfocused] = bg1;
99 | style.*.Colors[ig.ImGuiCol_TabUnfocusedActive] = bg2;
100 | style.*.Colors[ig.ImGuiCol_DockingPreview] = hl1;
101 | style.*.Colors[ig.ImGuiCol_DockingEmptyBg] = hl0;
102 | style.*.Colors[ig.ImGuiCol_PlotLines] = ig.ImVec4{ .x = 0.61, .y = 0.61, .z = 0.61, .w = 1.00 };
103 | style.*.Colors[ig.ImGuiCol_PlotLinesHovered] = ig.ImVec4{ .x = 1.00, .y = 0.43, .z = 0.35, .w = 1.00 };
104 | style.*.Colors[ig.ImGuiCol_PlotHistogram] = ig.ImVec4{ .x = 0.90, .y = 0.70, .z = 0.00, .w = 1.00 };
105 | style.*.Colors[ig.ImGuiCol_PlotHistogramHovered] = ig.ImVec4{ .x = 1.00, .y = 0.60, .z = 0.00, .w = 1.00 };
106 | style.*.Colors[ig.ImGuiCol_TableHeaderBg] = ig.ImVec4{ .x = 0.19, .y = 0.19, .z = 0.20, .w = 1.00 };
107 | style.*.Colors[ig.ImGuiCol_TableBorderStrong] = ig.ImVec4{ .x = 0.31, .y = 0.31, .z = 0.35, .w = 1.00 };
108 | style.*.Colors[ig.ImGuiCol_TableBorderLight] = ig.ImVec4{ .x = 0.23, .y = 0.23, .z = 0.25, .w = 1.00 };
109 | style.*.Colors[ig.ImGuiCol_TableRowBg] = ig.ImVec4{ .x = 0.00, .y = 0.00, .z = 0.00, .w = 0.00 };
110 | style.*.Colors[ig.ImGuiCol_TableRowBgAlt] = ig.ImVec4{ .x = 1.00, .y = 1.00, .z = 1.00, .w = 0.06 };
111 | style.*.Colors[ig.ImGuiCol_TextSelectedBg] = ig.ImVec4{ .x = 0.26, .y = 0.59, .z = 0.98, .w = 0.35 };
112 | style.*.Colors[ig.ImGuiCol_DragDropTarget] = ig.ImVec4{ .x = 1.00, .y = 1.00, .z = 0.00, .w = 0.90 };
113 | style.*.Colors[ig.ImGuiCol_NavHighlight] = ig.ImVec4{ .x = 0.26, .y = 0.59, .z = 0.98, .w = 1.00 };
114 | style.*.Colors[ig.ImGuiCol_NavWindowingHighlight] = ig.ImVec4{ .x = 1.00, .y = 1.00, .z = 1.00, .w = 0.70 };
115 | style.*.Colors[ig.ImGuiCol_NavWindowingDimBg] = ig.ImVec4{ .x = 0.80, .y = 0.80, .z = 0.80, .w = 0.20 };
116 | style.*.Colors[ig.ImGuiCol_ModalWindowDimBg] = ig.ImVec4{ .x = 0.80, .y = 0.80, .z = 0.80, .w = 0.35 };
117 | }
118 |
119 | var edit_bg: math.Vec4 = math.Vec4.new(0.1, 0.1, 0.15, 1.0);
120 | var edit_fg: math.Vec4 = math.Vec4.new(0.89, 0.89, 0.91, 1.0);
121 | var edit_sp: math.Vec4 = math.Vec4.new(0.9, 0.34, 0.2, 1.0);
122 | var edit_hl: math.Vec4 = math.Vec4.new(0.9, 0.34, 0.2, 1.0);
123 | var edit_diff: f32 = 0.2;
124 | var edit_isLight: bool = false;
125 | fn stringVec(vec: math.Vec4) []const u8 {
126 | return zt.custom_components.fmtTextForImgui("reroute.math.Vec4{{.x={d:.2},.y={d:.2},.z={d:.2},.w={d:.2}}}", .{ vec.x, vec.y, vec.z, vec.w });
127 | }
128 |
129 | /// A slate/orange dark theme
130 | pub fn styleColorOrangeSlate() void {
131 | styleColorCustom(math.Vec4{ .x = 0.16, .y = 0.19, .z = 0.22, .w = 1.00 }, math.Vec4{ .x = 0.89, .y = 0.89, .z = 0.89, .w = 1.00 }, math.Vec4{ .x = 0.90, .y = 0.34, .z = 0.20, .w = 1.00 }, math.Vec4{ .x = 0.00, .y = 0.00, .z = 0.00, .w = 0.36 }, 0.30, false);
132 | }
133 |
--------------------------------------------------------------------------------
/src/dep/stb/stb_leakcheck.h:
--------------------------------------------------------------------------------
1 | // stb_leakcheck.h - v0.6 - quick & dirty malloc leak-checking - public domain
2 | // LICENSE
3 | //
4 | // See end of file.
5 |
6 | #ifdef STB_LEAKCHECK_IMPLEMENTATION
7 | #undef STB_LEAKCHECK_IMPLEMENTATION // don't implement more than once
8 |
9 | // if we've already included leakcheck before, undefine the macros
10 | #ifdef malloc
11 | #undef malloc
12 | #undef free
13 | #undef realloc
14 | #endif
15 |
16 | #ifndef STB_LEAKCHECK_OUTPUT_PIPE
17 | #define STB_LEAKCHECK_OUTPUT_PIPE stdout
18 | #endif
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | typedef struct malloc_info stb_leakcheck_malloc_info;
26 |
27 | struct malloc_info
28 | {
29 | const char *file;
30 | int line;
31 | size_t size;
32 | stb_leakcheck_malloc_info *next,*prev;
33 | };
34 |
35 | static stb_leakcheck_malloc_info *mi_head;
36 |
37 | void *stb_leakcheck_malloc(size_t sz, const char *file, int line)
38 | {
39 | stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) malloc(sz + sizeof(*mi));
40 | if (mi == NULL) return mi;
41 | mi->file = file;
42 | mi->line = line;
43 | mi->next = mi_head;
44 | if (mi_head)
45 | mi->next->prev = mi;
46 | mi->prev = NULL;
47 | mi->size = (int) sz;
48 | mi_head = mi;
49 | return mi+1;
50 | }
51 |
52 | void stb_leakcheck_free(void *ptr)
53 | {
54 | if (ptr != NULL) {
55 | stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1;
56 | mi->size = ~mi->size;
57 | #ifndef STB_LEAKCHECK_SHOWALL
58 | if (mi->prev == NULL) {
59 | assert(mi_head == mi);
60 | mi_head = mi->next;
61 | } else
62 | mi->prev->next = mi->next;
63 | if (mi->next)
64 | mi->next->prev = mi->prev;
65 | free(mi);
66 | #endif
67 | }
68 | }
69 |
70 | void *stb_leakcheck_realloc(void *ptr, size_t sz, const char *file, int line)
71 | {
72 | if (ptr == NULL) {
73 | return stb_leakcheck_malloc(sz, file, line);
74 | } else if (sz == 0) {
75 | stb_leakcheck_free(ptr);
76 | return NULL;
77 | } else {
78 | stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1;
79 | if (sz <= mi->size)
80 | return ptr;
81 | else {
82 | #ifdef STB_LEAKCHECK_REALLOC_PRESERVE_MALLOC_FILELINE
83 | void *q = stb_leakcheck_malloc(sz, mi->file, mi->line);
84 | #else
85 | void *q = stb_leakcheck_malloc(sz, file, line);
86 | #endif
87 | if (q) {
88 | memcpy(q, ptr, mi->size);
89 | stb_leakcheck_free(ptr);
90 | }
91 | return q;
92 | }
93 | }
94 | }
95 |
96 | static void stblkck_internal_print(const char *reason, stb_leakcheck_malloc_info *mi)
97 | {
98 | #if defined(_MSC_VER) && _MSC_VER < 1900 // 1900=VS 2015
99 | // Compilers that use the old MS C runtime library don't have %zd
100 | // and the older ones don't even have %lld either... however, the old compilers
101 | // without "long long" don't support 64-bit targets either, so here's the
102 | // compromise:
103 | #if _MSC_VER < 1400 // before VS 2005
104 | fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %8d bytes at %p\n", reason, mi->file, mi->line, (int)mi->size, (void*)(mi+1));
105 | #else
106 | fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %16lld bytes at %p\n", reason, mi->file, mi->line, (long long)mi->size, (void*)(mi+1));
107 | #endif
108 | #else
109 | // Assume we have %zd on other targets.
110 | #ifdef __MINGW32__
111 | __mingw_fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %zd bytes at %p\n", reason, mi->file, mi->line, mi->size, (void*)(mi+1));
112 | #else
113 | fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %zd bytes at %p\n", reason, mi->file, mi->line, mi->size, (void*)(mi+1));
114 | #endif
115 | #endif
116 | }
117 |
118 | void stb_leakcheck_dumpmem(void)
119 | {
120 | stb_leakcheck_malloc_info *mi = mi_head;
121 | while (mi) {
122 | if ((ptrdiff_t) mi->size >= 0)
123 | stblkck_internal_print("LEAKED", mi);
124 | mi = mi->next;
125 | }
126 | #ifdef STB_LEAKCHECK_SHOWALL
127 | mi = mi_head;
128 | while (mi) {
129 | if ((ptrdiff_t) mi->size < 0)
130 | stblkck_internal_print("FREED ", mi);
131 | mi = mi->next;
132 | }
133 | #endif
134 | }
135 | #endif // STB_LEAKCHECK_IMPLEMENTATION
136 |
137 | #if !defined(INCLUDE_STB_LEAKCHECK_H) || !defined(malloc)
138 | #define INCLUDE_STB_LEAKCHECK_H
139 |
140 | #include // we want to define the macros *after* stdlib to avoid a slew of errors
141 |
142 | #define malloc(sz) stb_leakcheck_malloc(sz, __FILE__, __LINE__)
143 | #define free(p) stb_leakcheck_free(p)
144 | #define realloc(p,sz) stb_leakcheck_realloc(p,sz, __FILE__, __LINE__)
145 |
146 | extern void * stb_leakcheck_malloc(size_t sz, const char *file, int line);
147 | extern void * stb_leakcheck_realloc(void *ptr, size_t sz, const char *file, int line);
148 | extern void stb_leakcheck_free(void *ptr);
149 | extern void stb_leakcheck_dumpmem(void);
150 |
151 | #endif // INCLUDE_STB_LEAKCHECK_H
152 |
153 |
154 | /*
155 | ------------------------------------------------------------------------------
156 | This software is available under 2 licenses -- choose whichever you prefer.
157 | ------------------------------------------------------------------------------
158 | ALTERNATIVE A - MIT License
159 | Copyright (c) 2017 Sean Barrett
160 | Permission is hereby granted, free of charge, to any person obtaining a copy of
161 | this software and associated documentation files (the "Software"), to deal in
162 | the Software without restriction, including without limitation the rights to
163 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
164 | of the Software, and to permit persons to whom the Software is furnished to do
165 | so, subject to the following conditions:
166 | The above copyright notice and this permission notice shall be included in all
167 | copies or substantial portions of the Software.
168 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
169 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
170 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
171 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
172 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
173 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
174 | SOFTWARE.
175 | ------------------------------------------------------------------------------
176 | ALTERNATIVE B - Public Domain (www.unlicense.org)
177 | This is free and unencumbered software released into the public domain.
178 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
179 | software, either in source code form or as a compiled binary, for any purpose,
180 | commercial or non-commercial, and by any means.
181 | In jurisdictions that recognize copyright laws, the author or authors of this
182 | software dedicate any and all copyright interest in the software to the public
183 | domain. We make this dedication for the benefit of the public at large and to
184 | the detriment of our heirs and successors. We intend this dedication to be an
185 | overt act of relinquishment in perpetuity of all present and future rights to
186 | this software under copyright law.
187 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
188 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
189 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
190 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
191 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
192 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
193 | ------------------------------------------------------------------------------
194 | */
195 |
--------------------------------------------------------------------------------
/src/zt/customComponents.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const zt = @import("../zt.zig");
3 | const ig = @import("imgui");
4 |
5 | /// You can't remove the background from this, but you can make it invisible with
6 | /// style.Colors.
7 | pub fn viewPort() ig.ImGuiID {
8 | const dockNodeFlags = ig.ImGuiDockNodeFlags_PassthruCentralNode;
9 | const windowFlags =
10 | ig.ImGuiWindowFlags_NoCollapse |
11 | ig.ImGuiWindowFlags_NoDecoration |
12 | ig.ImGuiWindowFlags_NoDocking |
13 | ig.ImGuiWindowFlags_NoMove |
14 | ig.ImGuiWindowFlags_NoResize |
15 | ig.ImGuiWindowFlags_NoScrollbar |
16 | ig.ImGuiWindowFlags_NoTitleBar |
17 | ig.ImGuiWindowFlags_NoNavFocus |
18 | ig.ImGuiWindowFlags_NoBackground |
19 | ig.ImGuiWindowFlags_NoFocusOnAppearing |
20 | ig.ImGuiWindowFlags_NoMouseInputs |
21 | ig.ImGuiWindowFlags_NoInputs |
22 | ig.ImGuiWindowFlags_NoBringToFrontOnFocus;
23 |
24 | const mainView = ig.igGetMainViewport();
25 |
26 | const pos: ig.ImVec2 = mainView.*.WorkPos;
27 | const size: ig.ImVec2 = mainView.*.WorkSize;
28 |
29 | ig.igSetNextWindowPos(pos, ig.ImGuiCond_Always, .{});
30 | ig.igSetNextWindowSize(size, ig.ImGuiCond_Always);
31 |
32 | ig.igPushStyleVar_Vec2(ig.ImGuiStyleVar_WindowPadding, .{});
33 | _ = ig.igBegin("###DockSpace", null, windowFlags);
34 | const id = ig.igGetID_Str("DefaultDockingViewport");
35 | _ = ig.igDockSpace(id, .{}, dockNodeFlags, ig.ImGuiWindowClass_ImGuiWindowClass());
36 |
37 | ig.igEnd();
38 | ig.igPopStyleVar(1);
39 |
40 | return id;
41 | }
42 |
43 | /// If you ever need to format a string for use inside imgui, this will work the same as any format function.
44 | pub inline fn fmtTextForImgui(comptime fmt: []const u8, args: anytype) [:0]const u8 {
45 | const alloc = zt.Allocators.ring();
46 | return std.fmt.allocPrintZ(alloc, fmt, args) catch unreachable;
47 | }
48 | /// Uses a ring allocator to spit out imgui text using zig.ig's formatting library.
49 | pub fn text(comptime fmt: []const u8, args: anytype) void {
50 | const textFormatted = fmtTextForImgui(fmt, args);
51 | ig.igText("%s", textFormatted.ptr);
52 | }
53 | /// Uses a ring allocator to spit out imgui text using zig's formatting library, wrapping if needed.
54 | pub fn textWrap(comptime fmt: []const u8, args: anytype) void {
55 | const textFormatted = fmtTextForImgui(fmt, args);
56 | ig.igTextWrapped("%s", textFormatted.ptr);
57 | }
58 | /// Uses a ring allocator to spit out imgui text using zig's formatting library, in the disabled color.
59 | pub fn textDisabled(comptime fmt: []const u8, args: anytype) void {
60 | const textFormatted = fmtTextForImgui(fmt, args);
61 | ig.igTextDisabled("%s", textFormatted.ptr);
62 | }
63 | /// Uses a ring allocator to spit out imgui text using zig's formatting library with a custom color.
64 | pub fn textColor(comptime fmt: []const u8, color: ig.ImVec4, args: anytype) void {
65 | const textFormatted = fmtTextForImgui(fmt, args);
66 | ig.igTextColored(color, "%s", textFormatted.ptr);
67 | }
68 |
69 | /// Attempts to create a general editor for most structs, including math structs. This isnt always what you want, and in
70 | /// those cases its always better to layout your own editor. This is biased towards creating drag inputs.
71 | pub fn editDrag(label: []const u8, speed: f32, ptr: anytype) bool {
72 | // Array buffers are weird. Lets sort them out first.
73 | const ti: std.builtin.Type = @typeInfo(@TypeOf(ptr.*));
74 | if (ti == .Array) {
75 | if (ti.Array.child == u8) {
76 | return ig.igInputText(label.ptr, ptr, @intCast(ti.Array.len), ig.ImGuiInputTextFlags_None, null, null);
77 | }
78 | }
79 | const fmax = std.math.floatMax(f32);
80 | switch (@TypeOf(ptr)) {
81 | *bool => {
82 | return ig.igCheckbox(label.ptr, ptr);
83 | },
84 | *i32 => {
85 | const imin = std.math.minInt(i32);
86 | const imax = std.math.maxInt(i32);
87 | return ig.igDragInt(label.ptr, ptr, speed, @intCast(imin), @intCast(imax), "%i", ig.ImGuiSliderFlags_NoRoundToFormat);
88 | },
89 | *f32 => {
90 | return ig.igDragFloat(label.ptr, ptr, speed, -fmax, fmax, "%.2f", ig.ImGuiSliderFlags_NoRoundToFormat);
91 | },
92 | *usize => {
93 | const cast = @as(c_int, @intCast(ptr.*));
94 | const result = ig.igInputInt(label.ptr, &cast, 1, 5, ig.ImGuiInputTextFlags_None);
95 | if (result) {
96 | ptr.* = @intCast(std.math.max(0, cast));
97 | }
98 | return result;
99 | },
100 | *zt.math.Vec2 => {
101 | var cast: [2]f32 = .{ ptr.*.x, ptr.*.y };
102 | const result = ig.igDragFloat2(label.ptr, &cast, speed, -fmax, fmax, "%.2f", ig.ImGuiSliderFlags_NoRoundToFormat);
103 | if (result) {
104 | ptr.* = zt.math.vec2(cast[0], cast[1]);
105 | }
106 | return result;
107 | },
108 | *zt.math.Vec3 => {
109 | var cast: [3]f32 = .{ ptr.*.x, ptr.*.y, ptr.*.z };
110 | const result = ig.igDragFloat3(label.ptr, &cast, speed, -fmax, fmax, "%.2f", ig.ImGuiSliderFlags_NoRoundToFormat);
111 | if (result) {
112 | ptr.* = zt.math.vec3(cast[0], cast[1], cast[2]);
113 | }
114 | return result;
115 | },
116 | *zt.math.Vec4 => {
117 | var cast: [4]f32 = .{ ptr.*.x, ptr.*.y, ptr.*.z, ptr.*.w };
118 | const result = ig.igColorEdit4(label.ptr, &cast, ig.ImGuiColorEditFlags_Float);
119 | if (result) {
120 | ptr.* = zt.math.vec4(cast[0], cast[1], cast[2], cast[3]);
121 | }
122 | return result;
123 | },
124 | *zt.math.Rect => {
125 | var cast: [4]f32 = .{ ptr.*.position.x, ptr.*.position.y, ptr.*.size.x, ptr.*.size.y };
126 | const result = ig.igDragFloat4(label.ptr, &cast, speed, -fmax, fmax, "%.2f", ig.ImGuiSliderFlags_NoRoundToFormat);
127 | if (result) {
128 | ptr.* = zt.math.rect(cast[0], cast[1], cast[2], cast[3]);
129 | }
130 | return result;
131 | },
132 | else => {
133 | std.debug.print("No editor found for type {s}\n", .{@typeName(@TypeOf(ptr))});
134 | return false;
135 | },
136 | }
137 | }
138 |
139 | pub fn edit(label: []const u8, ptr: anytype) bool {
140 | // Array buffers are weird. Lets sort them out first.
141 | const ti: std.builtin.Type = @typeInfo(@TypeOf(ptr.*));
142 | if (ti == .Array) {
143 | if (ti.Array.child == u8) {
144 | return ig.igInputText(label.ptr, ptr, @intCast(ti.Array.len), ig.ImGuiInputTextFlags_None, null, null);
145 | }
146 | }
147 | switch (@TypeOf(ptr)) {
148 | *bool => {
149 | return ig.igCheckbox(label.ptr, ptr);
150 | },
151 | *i32 => {
152 | return ig.igInputInt(label.ptr, ptr, 1, 3, ig.ImGuiInputTextFlags_None);
153 | },
154 | *f32 => {
155 | return ig.igInputFloat(label.ptr, ptr, 1, 3, "%.2f", ig.ImGuiInputTextFlags_None);
156 | },
157 | *usize => {
158 | const cast = @as(c_int, @intCast(ptr.*));
159 | const result = ig.igInputInt(label.ptr, &cast, 1, 5, ig.ImGuiInputTextFlags_None);
160 | if (result) {
161 | ptr.* = @intCast(std.math.max(0, cast));
162 | }
163 | return result;
164 | },
165 | *zt.math.Vec2 => {
166 | const cast: [2]f32 = .{ ptr.*.x, ptr.*.y };
167 | const result = ig.igInputFloat2(label.ptr, &cast, "%.2f", ig.ImGuiInputTextFlags_None);
168 | if (result) {
169 | ptr.* = zt.math.vec2(cast[0], cast[1]);
170 | }
171 | return result;
172 | },
173 | *zt.math.Vec3 => {
174 | const cast: [3]f32 = .{ ptr.*.x, ptr.*.y, ptr.*.z };
175 | const result = ig.igInputFloat3(label.ptr, &cast, "%.2f", ig.ImGuiInputTextFlags_None);
176 | if (result) {
177 | ptr.* = zt.math.vec3(cast[0], cast[1], cast[2]);
178 | }
179 | return result;
180 | },
181 | *zt.math.Vec4 => {
182 | const cast: [4]f32 = .{ ptr.*.x, ptr.*.y, ptr.*.z, ptr.*.w };
183 | const result = ig.igColorEdit4(label.ptr, &cast, ig.ImGuiColorEditFlags_Float);
184 | if (result) {
185 | ptr.* = zt.math.vec4(cast[0], cast[1], cast[2], cast[3]);
186 | }
187 | return result;
188 | },
189 | *zt.math.Rect => {
190 | const cast: [4]f32 = .{ ptr.*.position.x, ptr.*.position.y, ptr.*.size.x, ptr.*.size.y };
191 | const result = ig.igInputFloat4(label.ptr, &cast, "%.2f", ig.ImGuiInputTextFlags_None);
192 | if (result) {
193 | ptr.* = zt.math.rect(cast[0], cast[1], cast[2], cast[3]);
194 | }
195 | return result;
196 | },
197 | else => {
198 | std.debug.print("No editor found for type {s}\n", .{@typeName(@TypeOf(ptr))});
199 | return false;
200 | },
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/example/src/scenes/spatialhash_squares.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const zt = @import("zt");
3 | const main = @import("../main.zig");
4 | const ig = @import("imgui");
5 | const zg = zt.custom_components;
6 |
7 | const Hash = zt.game.SpatialHash.Generate(usize, .{ .bucketSize = 80 });
8 | var rng: std.rand.Random = undefined;
9 | const blip = struct {
10 | aabb: zt.math.Rect = .{},
11 | color: zt.math.Vec4 = .{},
12 | pub fn generate(within: zt.math.Rect) blip {
13 | // This is slow, but I'm lazy and this is just a demo, soooooo...
14 | var self: blip = .{};
15 | self.aabb.size.x = std.math.clamp(rng.float(f32) * 100.0, 5.0, within.size.x);
16 | self.aabb.size.y = std.math.clamp(rng.float(f32) * 100.0, 5.0, within.size.y);
17 | self.aabb.position.x = std.math.clamp(rng.float(f32) * within.size.x, 0, within.size.x - self.aabb.size.x) + within.position.x;
18 | self.aabb.position.y = std.math.clamp(rng.float(f32) * within.size.y, 0, within.size.y - self.aabb.size.y) + within.position.y;
19 |
20 | // Little bit of transparency! Incase they overlap.
21 | self.color = zt.math.vec4(rng.float(f32), rng.float(f32), rng.float(f32), 0.33);
22 | return self;
23 | }
24 | };
25 |
26 | var rotation: f32 = 0.0;
27 | var scale: f32 = 1.0;
28 | var pos: zt.math.Vec2 = .{};
29 |
30 | // I'm avoiding allocations here, in your application you probably want an `std.ArrayList(blip)`
31 | var array: std.ArrayList(blip) = undefined;
32 | var hash: Hash = undefined;
33 | var bounds: zt.math.Rect = zt.math.rect(-400, -400, 800, 800);
34 | var inited = false;
35 | var spawned: i32 = 0;
36 |
37 | // This is the storage for when the user is querying with a rectangle.
38 | var rect_Q: zt.math.Rect = .{};
39 | // This is the storage for when the user is querying with a line.
40 | var line_Q: zt.math.Vec2 = .{};
41 |
42 | const queryStyle = enum { point, line, rect };
43 |
44 | var currentStyle: queryStyle = .point;
45 |
46 | pub fn update(ctx: *main.SampleApplication.Context) void {
47 | // Basic scene initialization:
48 | const io = ig.igGetIO();
49 | sceneSetup(ctx);
50 |
51 | var render = ctx.data.render;
52 |
53 | render.updateRenderSize(io.*.DisplaySize);
54 | render.updateCamera(pos, scale, rotation);
55 |
56 | for (array.items) |*b| {
57 | render.sprite(ctx.data.sheet, b.aabb.position, 0.0, b.aabb.size, b.color, null, zt.math.rect(131, 84, 2, 2));
58 | }
59 |
60 | // Generate the query based on the user input!
61 | switch (currentStyle) {
62 | // For a point, we just use the mouse position.
63 | .point => {
64 | var mouse = render.screenToWorld(io.*.MousePos);
65 | render.sprite(ctx.data.sheet, mouse.sub(zt.math.vec2(-1, 1)), 0.0, zt.math.vec2(2, 2), zt.math.Vec4.white, null, zt.math.rect(131, 84, 2, 2));
66 | const query = hash.queryPoint(mouse);
67 | for (query) |i| {
68 | const b: blip = array.items[i];
69 |
70 | if (zt.game.Physics.isPointInRect(mouse, b.aabb)) {
71 | render.sprite(ctx.data.sheet, b.aabb.position, 0.0, b.aabb.size, zt.math.vec4(0.0, 1.0, 0.0, 0.5), null, zt.math.rect(131, 84, 2, 2));
72 | } else {
73 | render.sprite(ctx.data.sheet, b.aabb.position, 0.0, b.aabb.size, zt.math.vec4(1.0, 1.0, 0.0, 0.5), null, zt.math.rect(131, 84, 2, 2));
74 | }
75 | }
76 | },
77 | // We simulate a line drawing by not dragging the start node when holding lmb, and querying the 2.
78 | .line => {
79 | const lineCol = zt.math.vec4(0.0, 0.0, 0.0, 1.0);
80 | const mouse = render.screenToWorld(io.*.MousePos);
81 | if (!io.*.MouseDown[ig.ImGuiMouseButton_Left]) {
82 | line_Q = mouse;
83 | } else {}
84 | const query = hash.queryLine(line_Q, mouse);
85 | for (query) |i| {
86 | const b: blip = array.items[i];
87 |
88 | if (zt.game.Physics.isLineInRect(line_Q, mouse, b.aabb.position, b.aabb.size)) {
89 | render.sprite(ctx.data.sheet, b.aabb.position, 0.0, b.aabb.size, zt.math.vec4(0.0, 1.0, 0.0, 0.5), null, zt.math.rect(131, 84, 2, 2));
90 | } else {
91 | render.sprite(ctx.data.sheet, b.aabb.position, 0.0, b.aabb.size, zt.math.vec4(1.0, 1.0, 0.0, 0.5), null, zt.math.rect(131, 84, 2, 2));
92 | }
93 | }
94 | render.line(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), line_Q, mouse, 0, 5.0, lineCol, lineCol);
95 | },
96 | .rect => {
97 | const lineCol = zt.math.vec4(0.0, 0.0, 0.0, 1.0);
98 | var mouse = render.screenToWorld(io.*.MousePos);
99 | if (!io.*.MouseDown[ig.ImGuiMouseButton_Left]) {
100 | rect_Q.position = mouse;
101 | } else {
102 | rect_Q.size = mouse.sub(rect_Q.position);
103 | }
104 | const query = hash.queryAABB(rect_Q.position, rect_Q.size);
105 | for (query) |i| {
106 | const b: blip = array.items[i];
107 |
108 | if (zt.game.Physics.isRectInRect(rect_Q.position, rect_Q.size, b.aabb.position, b.aabb.size)) {
109 | render.sprite(ctx.data.sheet, b.aabb.position, 0.0, b.aabb.size, zt.math.vec4(0.0, 1.0, 0.0, 0.5), null, zt.math.rect(131, 84, 2, 2));
110 | } else {
111 | render.sprite(ctx.data.sheet, b.aabb.position, 0.0, b.aabb.size, zt.math.vec4(1.0, 1.0, 0.0, 0.5), null, zt.math.rect(131, 84, 2, 2));
112 | }
113 | }
114 | render.rectangleHollow(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), rect_Q, 0, 2.0, lineCol);
115 | },
116 | }
117 |
118 | render.rectangleHollow(ctx.data.sheet, zt.math.rect(131, 84, 2, 2), bounds, 0, 2.0, zt.math.vec4(0.2, 1.0, 0.2, 0.8));
119 | render.flush();
120 | }
121 |
122 | fn control() void {
123 | const io = ig.igGetIO();
124 | ig.igSetNextWindowPos(io.*.DisplaySize, ig.ImGuiCond_Appearing, .{ .x = 1, .y = 1 });
125 | if (ig.igBegin("Spatial Hash Demo", null, ig.ImGuiWindowFlags_None)) {
126 | ig.igPushItemWidth(ig.igGetWindowWidth() * 0.5);
127 | _ = ig.igDragFloat("Camera Rotation", &rotation, 0.02, zt.math.toRadians(-360.0), zt.math.toRadians(360.0), "%.3f", ig.ImGuiSliderFlags_None);
128 | _ = ig.igDragFloat("Camera Zoom", &scale, 0.02, 0.1, 16, "%.3f", ig.ImGuiSliderFlags_None);
129 | ig.igSeparator();
130 | _ = zg.editDrag("Within Bounds", 2, &bounds);
131 |
132 | var generation: ?usize = null;
133 | if (ig.igButton("Spawn 10 items", .{})) {
134 | generation = 10;
135 | }
136 | if (ig.igButton("Spawn 100 items", .{})) {
137 | generation = 100;
138 | }
139 | if (ig.igButton("Spawn 1000 items", .{})) {
140 | generation = 1000;
141 | }
142 |
143 | if (generation) |len| {
144 | var i: usize = 0;
145 | while (i < len) : (i += 1) {
146 | const new = blip.generate(bounds);
147 |
148 | const index = array.items.len;
149 | array.append(new) catch unreachable;
150 |
151 | hash.addAABB(index, array.items[index].aabb.position, array.items[index].aabb.size);
152 | }
153 | spawned += @intCast(len);
154 | }
155 | if (ig.igBeginListBox("## INPUT STYLE", .{})) {
156 | if (ig.igSelectable_Bool("Point", currentStyle == .point, ig.ImGuiSelectableFlags_SpanAvailWidth, .{})) {
157 | currentStyle = .point;
158 | }
159 | if (ig.igSelectable_Bool("Line", currentStyle == .line, ig.ImGuiSelectableFlags_SpanAvailWidth, .{})) {
160 | currentStyle = .line;
161 | }
162 | if (ig.igSelectable_Bool("Rect", currentStyle == .rect, ig.ImGuiSelectableFlags_SpanAvailWidth, .{})) {
163 | currentStyle = .rect;
164 | }
165 | ig.igEndListBox();
166 | }
167 | zg.textWrap("You've spawned {any} squares.", .{spawned});
168 | ig.igTextWrapped("Use middle mouse to scroll, and mousewheel to zoom");
169 | ig.igPopItemWidth();
170 | }
171 | ig.igEnd();
172 | }
173 |
174 | fn sceneSetup(ctx: *main.SampleApplication.Context) void {
175 | const io = ig.igGetIO();
176 | if (!inited) {
177 | var prng = std.rand.DefaultPrng.init(blk: {
178 | var seed: u64 = undefined;
179 | std.os.getrandom(std.mem.asBytes(&seed)) catch {
180 | std.debug.print("OS getRandom failed.", .{});
181 | std.process.exit(1);
182 | };
183 | break :blk seed;
184 | });
185 | rng = prng.random();
186 | array = std.ArrayList(blip).init(ctx.allocator);
187 | hash = Hash.init(ctx.allocator);
188 | inited = true;
189 | }
190 | // Mouse Controls for this scene, pretty convenient considering the aabb size.
191 | if (io.*.MouseDown[ig.ImGuiMouseButton_Middle]) {
192 | pos = pos.add(io.*.MouseDelta.scaleDiv(scale));
193 | }
194 | if (io.*.MouseWheel != 0) {
195 | scale = std.math.clamp(scale + (io.*.MouseWheel * 0.5), 0.5, 16);
196 | }
197 | control();
198 | }
199 |
--------------------------------------------------------------------------------
/src/dep/cimgui/imgui/imconfig.h:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------------
2 | // COMPILE-TIME OPTIONS FOR DEAR IMGUI
3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
5 | //-----------------------------------------------------------------------------
6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
7 | // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
8 | //-----------------------------------------------------------------------------
9 | // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
10 | // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
11 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
12 | // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
13 | //-----------------------------------------------------------------------------
14 |
15 | #pragma once
16 |
17 | //---- Define assertion handler. Defaults to calling assert().
18 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
19 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
20 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
21 |
22 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
23 | // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
24 | // DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
25 | // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
26 | //#define IMGUI_API __declspec( dllexport )
27 | //#define IMGUI_API __declspec( dllimport )
28 |
29 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
30 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
31 | //#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
32 |
33 | //---- Disable all of Dear ImGui or don't implement standard windows/tools.
34 | // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
35 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
36 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
37 | //#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
38 |
39 | //---- Don't implement some functions to reduce linkage requirements.
40 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
41 | //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
42 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
43 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
44 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
45 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
46 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
47 | //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
48 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
49 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
50 | //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
51 |
52 | //---- Include imgui_user.h at the end of imgui.h as a convenience
53 | //#define IMGUI_INCLUDE_IMGUI_USER_H
54 |
55 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
56 | //#define IMGUI_USE_BGRA_PACKED_COLOR
57 |
58 | //---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
59 | //#define IMGUI_USE_WCHAR32
60 |
61 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
62 | // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
63 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
64 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
65 | //#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
66 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
67 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
68 |
69 | //---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
70 | // Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
71 | //#define IMGUI_USE_STB_SPRINTF
72 |
73 | //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
74 | // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
75 | // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
76 | //#define IMGUI_ENABLE_FREETYPE
77 |
78 | //---- Use stb_truetype to build and rasterize the font atlas (default)
79 | // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
80 | //#define IMGUI_ENABLE_STB_TRUETYPE
81 |
82 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
83 | // This will be inlined as part of ImVec2 and ImVec4 class declarations.
84 | /*
85 | #define IM_VEC2_CLASS_EXTRA \
86 | constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
87 | operator MyVec2() const { return MyVec2(x,y); }
88 |
89 | #define IM_VEC4_CLASS_EXTRA \
90 | constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
91 | operator MyVec4() const { return MyVec4(x,y,z,w); }
92 | */
93 | //---- ...Or use Dear ImGui's own very basic math operators.
94 | //#define IMGUI_DEFINE_MATH_OPERATORS
95 |
96 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
97 | // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
98 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
99 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
100 | //#define ImDrawIdx unsigned int
101 |
102 | //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
103 | //struct ImDrawList;
104 | //struct ImDrawCmd;
105 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
106 | //#define ImDrawCallback MyImDrawCallback
107 |
108 | //---- Debug Tools: Macro to break in Debugger
109 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
110 | //#define IM_DEBUG_BREAK IM_ASSERT(0)
111 | //#define IM_DEBUG_BREAK __debugbreak()
112 |
113 | //---- Debug Tools: Enable slower asserts
114 | //#define IMGUI_DEBUG_PARANOID
115 |
116 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
117 | /*
118 | namespace ImGui
119 | {
120 | void MyFunction(const char* name, const MyMatrix44& v);
121 | }
122 | */
123 |
--------------------------------------------------------------------------------
/src/dep/stb/stb_include.h:
--------------------------------------------------------------------------------
1 | // stb_include.h - v0.02 - parse and process #include directives - public domain
2 | //
3 | // To build this, in one source file that includes this file do
4 | // #define STB_INCLUDE_IMPLEMENTATION
5 | //
6 | // This program parses a string and replaces lines of the form
7 | // #include "foo"
8 | // with the contents of a file named "foo". It also embeds the
9 | // appropriate #line directives. Note that all include files must
10 | // reside in the location specified in the path passed to the API;
11 | // it does not check multiple directories.
12 | //
13 | // If the string contains a line of the form
14 | // #inject
15 | // then it will be replaced with the contents of the string 'inject' passed to the API.
16 | //
17 | // Options:
18 | //
19 | // Define STB_INCLUDE_LINE_GLSL to get GLSL-style #line directives
20 | // which use numbers instead of filenames.
21 | //
22 | // Define STB_INCLUDE_LINE_NONE to disable output of #line directives.
23 | //
24 | // Standard libraries:
25 | //
26 | // stdio.h FILE, fopen, fclose, fseek, ftell
27 | // stdlib.h malloc, realloc, free
28 | // string.h strcpy, strncmp, memcpy
29 | //
30 | // Credits:
31 | //
32 | // Written by Sean Barrett.
33 | //
34 | // Fixes:
35 | // Michal Klos
36 |
37 | #ifndef STB_INCLUDE_STB_INCLUDE_H
38 | #define STB_INCLUDE_STB_INCLUDE_H
39 |
40 | // Do include-processing on the string 'str'. To free the return value, pass it to free()
41 | char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]);
42 |
43 | // Concatenate the strings 'strs' and do include-processing on the result. To free the return value, pass it to free()
44 | char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]);
45 |
46 | // Load the file 'filename' and do include-processing on the string therein. note that
47 | // 'filename' is opened directly; 'path_to_includes' is not used. To free the return value, pass it to free()
48 | char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256]);
49 |
50 | #endif
51 |
52 |
53 | #ifdef STB_INCLUDE_IMPLEMENTATION
54 |
55 | #include
56 | #include
57 | #include
58 |
59 | static char *stb_include_load_file(char *filename, size_t *plen)
60 | {
61 | char *text;
62 | size_t len;
63 | FILE *f = fopen(filename, "rb");
64 | if (f == 0) return 0;
65 | fseek(f, 0, SEEK_END);
66 | len = (size_t) ftell(f);
67 | if (plen) *plen = len;
68 | text = (char *) malloc(len+1);
69 | if (text == 0) return 0;
70 | fseek(f, 0, SEEK_SET);
71 | fread(text, 1, len, f);
72 | fclose(f);
73 | text[len] = 0;
74 | return text;
75 | }
76 |
77 | typedef struct
78 | {
79 | int offset;
80 | int end;
81 | char *filename;
82 | int next_line_after;
83 | } include_info;
84 |
85 | static include_info *stb_include_append_include(include_info *array, int len, int offset, int end, char *filename, int next_line)
86 | {
87 | include_info *z = (include_info *) realloc(array, sizeof(*z) * (len+1));
88 | z[len].offset = offset;
89 | z[len].end = end;
90 | z[len].filename = filename;
91 | z[len].next_line_after = next_line;
92 | return z;
93 | }
94 |
95 | static void stb_include_free_includes(include_info *array, int len)
96 | {
97 | int i;
98 | for (i=0; i < len; ++i)
99 | free(array[i].filename);
100 | free(array);
101 | }
102 |
103 | static int stb_include_isspace(int ch)
104 | {
105 | return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
106 | }
107 |
108 | // find location of all #include and #inject
109 | static int stb_include_find_includes(char *text, include_info **plist)
110 | {
111 | int line_count = 1;
112 | int inc_count = 0;
113 | char *s = text, *start;
114 | include_info *list = NULL;
115 | while (*s) {
116 | // parse is always at start of line when we reach here
117 | start = s;
118 | while (*s == ' ' || *s == '\t')
119 | ++s;
120 | if (*s == '#') {
121 | ++s;
122 | while (*s == ' ' || *s == '\t')
123 | ++s;
124 | if (0==strncmp(s, "include", 7) && stb_include_isspace(s[7])) {
125 | s += 7;
126 | while (*s == ' ' || *s == '\t')
127 | ++s;
128 | if (*s == '"') {
129 | char *t = ++s;
130 | while (*t != '"' && *t != '\n' && *t != '\r' && *t != 0)
131 | ++t;
132 | if (*t == '"') {
133 | char *filename = (char *) malloc(t-s+1);
134 | memcpy(filename, s, t-s);
135 | filename[t-s] = 0;
136 | s=t;
137 | while (*s != '\r' && *s != '\n' && *s != 0)
138 | ++s;
139 | // s points to the newline, so s-start is everything except the newline
140 | list = stb_include_append_include(list, inc_count++, start-text, s-text, filename, line_count+1);
141 | }
142 | }
143 | } else if (0==strncmp(s, "inject", 6) && (stb_include_isspace(s[6]) || s[6]==0)) {
144 | while (*s != '\r' && *s != '\n' && *s != 0)
145 | ++s;
146 | list = stb_include_append_include(list, inc_count++, start-text, s-text, NULL, line_count+1);
147 | }
148 | }
149 | while (*s != '\r' && *s != '\n' && *s != 0)
150 | ++s;
151 | if (*s == '\r' || *s == '\n') {
152 | s = s + (s[0] + s[1] == '\r' + '\n' ? 2 : 1);
153 | }
154 | ++line_count;
155 | }
156 | *plist = list;
157 | return inc_count;
158 | }
159 |
160 | // avoid dependency on sprintf()
161 | static void stb_include_itoa(char str[9], int n)
162 | {
163 | int i;
164 | for (i=0; i < 8; ++i)
165 | str[i] = ' ';
166 | str[i] = 0;
167 |
168 | for (i=1; i < 8; ++i) {
169 | str[7-i] = '0' + (n % 10);
170 | n /= 10;
171 | if (n == 0)
172 | break;
173 | }
174 | }
175 |
176 | static char *stb_include_append(char *str, size_t *curlen, char *addstr, size_t addlen)
177 | {
178 | str = (char *) realloc(str, *curlen + addlen);
179 | memcpy(str + *curlen, addstr, addlen);
180 | *curlen += addlen;
181 | return str;
182 | }
183 |
184 | char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename, char error[256])
185 | {
186 | char temp[4096];
187 | include_info *inc_list;
188 | int i, num = stb_include_find_includes(str, &inc_list);
189 | size_t source_len = strlen(str);
190 | char *text=0;
191 | size_t textlen=0, last=0;
192 | for (i=0; i < num; ++i) {
193 | text = stb_include_append(text, &textlen, str+last, inc_list[i].offset - last);
194 | // write out line directive for the include
195 | #ifndef STB_INCLUDE_LINE_NONE
196 | #ifdef STB_INCLUDE_LINE_GLSL
197 | if (textlen != 0) // GLSL #version must appear first, so don't put a #line at the top
198 | #endif
199 | {
200 | strcpy(temp, "#line ");
201 | stb_include_itoa(temp+6, 1);
202 | strcat(temp, " ");
203 | #ifdef STB_INCLUDE_LINE_GLSL
204 | stb_include_itoa(temp+15, i+1);
205 | #else
206 | strcat(temp, "\"");
207 | if (inc_list[i].filename == 0)
208 | strcmp(temp, "INJECT");
209 | else
210 | strcat(temp, inc_list[i].filename);
211 | strcat(temp, "\"");
212 | #endif
213 | strcat(temp, "\n");
214 | text = stb_include_append(text, &textlen, temp, strlen(temp));
215 | }
216 | #endif
217 | if (inc_list[i].filename == 0) {
218 | if (inject != 0)
219 | text = stb_include_append(text, &textlen, inject, strlen(inject));
220 | } else {
221 | char *inc;
222 | strcpy(temp, path_to_includes);
223 | strcat(temp, "/");
224 | strcat(temp, inc_list[i].filename);
225 | inc = stb_include_file(temp, inject, path_to_includes, error);
226 | if (inc == NULL) {
227 | stb_include_free_includes(inc_list, num);
228 | return NULL;
229 | }
230 | text = stb_include_append(text, &textlen, inc, strlen(inc));
231 | free(inc);
232 | }
233 | // write out line directive
234 | #ifndef STB_INCLUDE_LINE_NONE
235 | strcpy(temp, "\n#line ");
236 | stb_include_itoa(temp+6, inc_list[i].next_line_after);
237 | strcat(temp, " ");
238 | #ifdef STB_INCLUDE_LINE_GLSL
239 | stb_include_itoa(temp+15, 0);
240 | #else
241 | strcat(temp, filename != 0 ? filename : "source-file");
242 | #endif
243 | text = stb_include_append(text, &textlen, temp, strlen(temp));
244 | // no newlines, because we kept the #include newlines, which will get appended next
245 | #endif
246 | last = inc_list[i].end;
247 | }
248 | text = stb_include_append(text, &textlen, str+last, source_len - last + 1); // append '\0'
249 | stb_include_free_includes(inc_list, num);
250 | return text;
251 | }
252 |
253 | char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename, char error[256])
254 | {
255 | char *text;
256 | char *result;
257 | int i;
258 | size_t length=0;
259 | for (i=0; i < count; ++i)
260 | length += strlen(strs[i]);
261 | text = (char *) malloc(length+1);
262 | length = 0;
263 | for (i=0; i < count; ++i) {
264 | strcpy(text + length, strs[i]);
265 | length += strlen(strs[i]);
266 | }
267 | result = stb_include_string(text, inject, path_to_includes, filename, error);
268 | free(text);
269 | return result;
270 | }
271 |
272 | char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256])
273 | {
274 | size_t len;
275 | char *result;
276 | char *text = stb_include_load_file(filename, &len);
277 | if (text == NULL) {
278 | strcpy(error, "Error: couldn't load '");
279 | strcat(error, filename);
280 | strcat(error, "'");
281 | return 0;
282 | }
283 | result = stb_include_string(text, inject, path_to_includes, filename, error);
284 | free(text);
285 | return result;
286 | }
287 |
288 | #if 0 // @TODO, GL_ARB_shader_language_include-style system that doesn't touch filesystem
289 | char *stb_include_preloaded(char *str, char *inject, char *includes[][2], char error[256])
290 | {
291 |
292 | }
293 | #endif
294 |
295 | #endif // STB_INCLUDE_IMPLEMENTATION
296 |
--------------------------------------------------------------------------------
/src/dep/gl/glad/include/KHR/khrplatform.h:
--------------------------------------------------------------------------------
1 | #ifndef __khrplatform_h_
2 | #define __khrplatform_h_
3 |
4 | /*
5 | ** Copyright (c) 2008-2018 The Khronos Group Inc.
6 | **
7 | ** Permission is hereby granted, free of charge, to any person obtaining a
8 | ** copy of this software and/or associated documentation files (the
9 | ** "Materials"), to deal in the Materials without restriction, including
10 | ** without limitation the rights to use, copy, modify, merge, publish,
11 | ** distribute, sublicense, and/or sell copies of the Materials, and to
12 | ** permit persons to whom the Materials are furnished to do so, subject to
13 | ** the following conditions:
14 | **
15 | ** The above copyright notice and this permission notice shall be included
16 | ** in all copies or substantial portions of the Materials.
17 | **
18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
25 | */
26 |
27 | /* Khronos platform-specific types and definitions.
28 | *
29 | * The master copy of khrplatform.h is maintained in the Khronos EGL
30 | * Registry repository at https://github.com/KhronosGroup/EGL-Registry
31 | * The last semantic modification to khrplatform.h was at commit ID:
32 | * 67a3e0864c2d75ea5287b9f3d2eb74a745936692
33 | *
34 | * Adopters may modify this file to suit their platform. Adopters are
35 | * encouraged to submit platform specific modifications to the Khronos
36 | * group so that they can be included in future versions of this file.
37 | * Please submit changes by filing pull requests or issues on
38 | * the EGL Registry repository linked above.
39 | *
40 | *
41 | * See the Implementer's Guidelines for information about where this file
42 | * should be located on your system and for more details of its use:
43 | * http://www.khronos.org/registry/implementers_guide.pdf
44 | *
45 | * This file should be included as
46 | * #include
47 | * by Khronos client API header files that use its types and defines.
48 | *
49 | * The types in khrplatform.h should only be used to define API-specific types.
50 | *
51 | * Types defined in khrplatform.h:
52 | * khronos_int8_t signed 8 bit
53 | * khronos_uint8_t unsigned 8 bit
54 | * khronos_int16_t signed 16 bit
55 | * khronos_uint16_t unsigned 16 bit
56 | * khronos_int32_t signed 32 bit
57 | * khronos_uint32_t unsigned 32 bit
58 | * khronos_int64_t signed 64 bit
59 | * khronos_uint64_t unsigned 64 bit
60 | * khronos_intptr_t signed same number of bits as a pointer
61 | * khronos_uintptr_t unsigned same number of bits as a pointer
62 | * khronos_ssize_t signed size
63 | * khronos_usize_t unsigned size
64 | * khronos_float_t signed 32 bit floating point
65 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds
66 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in
67 | * nanoseconds
68 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds
69 | * khronos_boolean_enum_t enumerated boolean type. This should
70 | * only be used as a base type when a client API's boolean type is
71 | * an enum. Client APIs which use an integer or other type for
72 | * booleans cannot use this as the base type for their boolean.
73 | *
74 | * Tokens defined in khrplatform.h:
75 | *
76 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
77 | *
78 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
79 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
80 | *
81 | * Calling convention macros defined in this file:
82 | * KHRONOS_APICALL
83 | * KHRONOS_APIENTRY
84 | * KHRONOS_APIATTRIBUTES
85 | *
86 | * These may be used in function prototypes as:
87 | *
88 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
89 | * int arg1,
90 | * int arg2) KHRONOS_APIATTRIBUTES;
91 | */
92 |
93 | #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
94 | # define KHRONOS_STATIC 1
95 | #endif
96 |
97 | /*-------------------------------------------------------------------------
98 | * Definition of KHRONOS_APICALL
99 | *-------------------------------------------------------------------------
100 | * This precedes the return type of the function in the function prototype.
101 | */
102 | #if defined(KHRONOS_STATIC)
103 | /* If the preprocessor constant KHRONOS_STATIC is defined, make the
104 | * header compatible with static linking. */
105 | # define KHRONOS_APICALL
106 | #elif defined(_WIN32)
107 | # define KHRONOS_APICALL __declspec(dllimport)
108 | #elif defined (__SYMBIAN32__)
109 | # define KHRONOS_APICALL IMPORT_C
110 | #elif defined(__ANDROID__)
111 | # define KHRONOS_APICALL __attribute__((visibility("default")))
112 | #else
113 | # define KHRONOS_APICALL
114 | #endif
115 |
116 | /*-------------------------------------------------------------------------
117 | * Definition of KHRONOS_APIENTRY
118 | *-------------------------------------------------------------------------
119 | * This follows the return type of the function and precedes the function
120 | * name in the function prototype.
121 | */
122 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
123 | /* Win32 but not WinCE */
124 | # define KHRONOS_APIENTRY __stdcall
125 | #else
126 | # define KHRONOS_APIENTRY
127 | #endif
128 |
129 | /*-------------------------------------------------------------------------
130 | * Definition of KHRONOS_APIATTRIBUTES
131 | *-------------------------------------------------------------------------
132 | * This follows the closing parenthesis of the function prototype arguments.
133 | */
134 | #if defined (__ARMCC_2__)
135 | #define KHRONOS_APIATTRIBUTES __softfp
136 | #else
137 | #define KHRONOS_APIATTRIBUTES
138 | #endif
139 |
140 | /*-------------------------------------------------------------------------
141 | * basic type definitions
142 | *-----------------------------------------------------------------------*/
143 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
144 |
145 |
146 | /*
147 | * Using
148 | */
149 | #include
150 | typedef int32_t khronos_int32_t;
151 | typedef uint32_t khronos_uint32_t;
152 | typedef int64_t khronos_int64_t;
153 | typedef uint64_t khronos_uint64_t;
154 | #define KHRONOS_SUPPORT_INT64 1
155 | #define KHRONOS_SUPPORT_FLOAT 1
156 |
157 | #elif defined(__VMS ) || defined(__sgi)
158 |
159 | /*
160 | * Using
161 | */
162 | #include
163 | typedef int32_t khronos_int32_t;
164 | typedef uint32_t khronos_uint32_t;
165 | typedef int64_t khronos_int64_t;
166 | typedef uint64_t khronos_uint64_t;
167 | #define KHRONOS_SUPPORT_INT64 1
168 | #define KHRONOS_SUPPORT_FLOAT 1
169 |
170 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
171 |
172 | /*
173 | * Win32
174 | */
175 | typedef __int32 khronos_int32_t;
176 | typedef unsigned __int32 khronos_uint32_t;
177 | typedef __int64 khronos_int64_t;
178 | typedef unsigned __int64 khronos_uint64_t;
179 | #define KHRONOS_SUPPORT_INT64 1
180 | #define KHRONOS_SUPPORT_FLOAT 1
181 |
182 | #elif defined(__sun__) || defined(__digital__)
183 |
184 | /*
185 | * Sun or Digital
186 | */
187 | typedef int khronos_int32_t;
188 | typedef unsigned int khronos_uint32_t;
189 | #if defined(__arch64__) || defined(_LP64)
190 | typedef long int khronos_int64_t;
191 | typedef unsigned long int khronos_uint64_t;
192 | #else
193 | typedef long long int khronos_int64_t;
194 | typedef unsigned long long int khronos_uint64_t;
195 | #endif /* __arch64__ */
196 | #define KHRONOS_SUPPORT_INT64 1
197 | #define KHRONOS_SUPPORT_FLOAT 1
198 |
199 | #elif 0
200 |
201 | /*
202 | * Hypothetical platform with no float or int64 support
203 | */
204 | typedef int khronos_int32_t;
205 | typedef unsigned int khronos_uint32_t;
206 | #define KHRONOS_SUPPORT_INT64 0
207 | #define KHRONOS_SUPPORT_FLOAT 0
208 |
209 | #else
210 |
211 | /*
212 | * Generic fallback
213 | */
214 | #include
215 | typedef int32_t khronos_int32_t;
216 | typedef uint32_t khronos_uint32_t;
217 | typedef int64_t khronos_int64_t;
218 | typedef uint64_t khronos_uint64_t;
219 | #define KHRONOS_SUPPORT_INT64 1
220 | #define KHRONOS_SUPPORT_FLOAT 1
221 |
222 | #endif
223 |
224 |
225 | /*
226 | * Types that are (so far) the same on all platforms
227 | */
228 | typedef signed char khronos_int8_t;
229 | typedef unsigned char khronos_uint8_t;
230 | typedef signed short int khronos_int16_t;
231 | typedef unsigned short int khronos_uint16_t;
232 |
233 | /*
234 | * Types that differ between LLP64 and LP64 architectures - in LLP64,
235 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
236 | * to be the only LLP64 architecture in current use.
237 | */
238 | #ifdef _WIN64
239 | typedef signed long long int khronos_intptr_t;
240 | typedef unsigned long long int khronos_uintptr_t;
241 | typedef signed long long int khronos_ssize_t;
242 | typedef unsigned long long int khronos_usize_t;
243 | #else
244 | typedef signed long int khronos_intptr_t;
245 | typedef unsigned long int khronos_uintptr_t;
246 | typedef signed long int khronos_ssize_t;
247 | typedef unsigned long int khronos_usize_t;
248 | #endif
249 |
250 | #if KHRONOS_SUPPORT_FLOAT
251 | /*
252 | * Float type
253 | */
254 | typedef float khronos_float_t;
255 | #endif
256 |
257 | #if KHRONOS_SUPPORT_INT64
258 | /* Time types
259 | *
260 | * These types can be used to represent a time interval in nanoseconds or
261 | * an absolute Unadjusted System Time. Unadjusted System Time is the number
262 | * of nanoseconds since some arbitrary system event (e.g. since the last
263 | * time the system booted). The Unadjusted System Time is an unsigned
264 | * 64 bit value that wraps back to 0 every 584 years. Time intervals
265 | * may be either signed or unsigned.
266 | */
267 | typedef khronos_uint64_t khronos_utime_nanoseconds_t;
268 | typedef khronos_int64_t khronos_stime_nanoseconds_t;
269 | #endif
270 |
271 | /*
272 | * Dummy value used to pad enum types to 32 bits.
273 | */
274 | #ifndef KHRONOS_MAX_ENUM
275 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF
276 | #endif
277 |
278 | /*
279 | * Enumerated boolean type
280 | *
281 | * Values other than zero should be considered to be true. Therefore
282 | * comparisons should not be made against KHRONOS_TRUE.
283 | */
284 | typedef enum {
285 | KHRONOS_FALSE = 0,
286 | KHRONOS_TRUE = 1,
287 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
288 | } khronos_boolean_enum_t;
289 |
290 | #endif /* __khrplatform_h_ */
291 |
--------------------------------------------------------------------------------
/src/zt/renderer.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const zt = @import("../zt.zig");
3 | const gl = @import("gl");
4 |
5 | const Self = @This();
6 | pub const VertShaderSource = @embedFile("shader/renderer.vertex");
7 | pub const FragShaderSource = @embedFile("shader/renderer.fragment");
8 |
9 | pub const Vertex = extern struct { pos: zt.math.Vec3, col: zt.math.Vec4, tex: zt.math.Vec2 };
10 | internal: zt.gl.GenerateBuffer(Vertex, 4086) = undefined,
11 | viewMatrix: zt.math.Mat4 = zt.math.Mat4.identity,
12 | inverseViewMatrix: zt.math.Mat4 = zt.math.Mat4.identity,
13 | projectionMatrix: zt.math.Mat4 = zt.math.Mat4.identity,
14 | currentTexture: ?zt.gl.Texture = null,
15 | defaultShader: zt.gl.Shader = undefined,
16 | /// Internal render size, do not edit. Is set by `updateRenderSize`.
17 | buildSize: zt.math.Vec2 = .{},
18 | resolution: i32 = 2,
19 |
20 | pub fn createShader(fragment: [*:0]const u8) zt.gl.Shader {
21 | return zt.gl.Shader.init(VertShaderSource, fragment);
22 | }
23 |
24 | pub fn init() @This() {
25 | var renderer = Self{
26 | .defaultShader = zt.gl.Shader.init(VertShaderSource, FragShaderSource),
27 | .projectionMatrix = zt.math.Mat4.createOrthogonal(0, 1280, 720, 0, -128, 128),
28 | .viewMatrix = zt.math.Mat4.identity,
29 | };
30 | renderer.internal = zt.gl.GenerateBuffer(Vertex, 4086).init(renderer.defaultShader);
31 | return renderer;
32 | }
33 | pub fn deinit(self: *Self) void {
34 | self.internal.deinit();
35 | }
36 | /// Sets the render size, perfect to modify if you need to render into a differently sized frame buffer. Otherwise call
37 | /// this every frame to your `zt.App.width, height`
38 | pub fn updateRenderSize(self: *Self, size: zt.math.Vec2) void {
39 | self.projectionMatrix = zt.math.Mat4.createOrthogonal(0, size.x, size.y, 0, -128, 128);
40 | self.buildSize = size;
41 | }
42 | /// Given a position, zoom, and rotation, this function emulates a traditional 2d camera by directly modifying
43 | /// the view matrix.
44 | pub fn updateCamera(self: *Self, position: zt.math.Vec2, zoom: f32, rotation: f32) void {
45 | self.viewMatrix = zt.math.Mat4.batchMul(&.{
46 | zt.math.Mat4.createTranslationXYZ(position.x, position.y, 0), // translate
47 | zt.math.Mat4.createZRotation(rotation), // Rotation
48 | zt.math.Mat4.createScale(zoom, zoom, 1.0), // Scale
49 | zt.math.Mat4.createTranslationXYZ(self.buildSize.x * 0.5, self.buildSize.y * 0.5, 0), // center
50 | });
51 | self.inverseViewMatrix = self.viewMatrix.invert().?;
52 | }
53 | /// Resets the camera back to screenspace.
54 | pub fn updateCameraScreenSpace(self: *Self) void {
55 | self.viewMatrix = zt.math.Mat4.identity;
56 | self.inverseViewMatrix = self.viewMatrix.invert().?;
57 | }
58 |
59 | /// Sets the shader used by the internal buffer. Pass in null to revert to the default shader.
60 | pub fn updateShader(self: *Self, shader: ?*zt.gl.Shader) void {
61 | self.flush();
62 | if (shader) |realShader| {
63 | self.internal.shader = realShader.*;
64 | } else {
65 | self.internal.shader = self.defaultShader;
66 | }
67 | }
68 |
69 | /// The simplest sprite method. Passing null to normalized origin will draw top-left based. Passing null to source will
70 | /// draw the whole texture. Note; normalized origin is multiplicative. 1,1 will draw the texture from bottom right, providing
71 | /// beyond 0 and 1 is supported if the anchor needs to be
72 | pub inline fn sprite(self: *Self, texture: zt.gl.Texture, pos: zt.math.Vec2, z: f32, size: zt.math.Vec2, color: zt.math.Vec4, normOrigin: ?zt.math.Vec2, src: ?zt.math.Rect) void {
73 | var offset: zt.math.Vec2 = if (normOrigin) |no| .{ .x = -(size.x * no.x), .y = -(size.y * no.y) } else .{};
74 | const source: zt.math.Rect =
75 | if (src) |s|
76 | zt.math.rect(s.position.x / texture.width, s.position.y / texture.height, s.size.x / texture.width, s.size.y / texture.height)
77 | else
78 | zt.math.rect(0, 0, 1, 1);
79 |
80 | if (size.x < 0.0) {
81 | offset.x -= size.x;
82 | }
83 |
84 | self.spriteEx(texture, pos.x + offset.x, pos.y + offset.y, z, size.x, size.y, source.position.x, source.position.y, source.size.x, source.size.y, color, color, color, color);
85 | }
86 | /// If you want to submit the vertices yourself, this is the way to do it. Submit them in order: Top Left, Top Right,
87 | /// Bottom Left, Bottom Right.
88 | pub fn spriteManual(self: *Self, texture: zt.gl.Texture, tl: Vertex, tr: Vertex, bl: Vertex, br: Vertex) void {
89 | if (self.currentTexture == null) {
90 | self.currentTexture = texture;
91 | } else if (self.currentTexture.?.id != texture.id) {
92 | self.flush();
93 | self.currentTexture = texture;
94 | }
95 | self.internal.addQuad(bl, tl, tr, br) catch |err| {
96 | if (err == error.NeedsFlush) {
97 | self.flush();
98 | self.internal.addQuad(bl, tl, tr, br) catch unreachable;
99 | return;
100 | }
101 | std.debug.panic("Rendering error: {s}", .{@errorName(err)});
102 | };
103 | }
104 | /// Use this method if you want to avoid using zt math types such as Vec2, and would prefer to just input raw floats.
105 | pub fn spriteEx(self: *Self, texture: zt.gl.Texture, x: f32, y: f32, z: f32, w: f32, h: f32, sx: f32, sy: f32, sw: f32, sh: f32, colTl: zt.math.Vec4, colTr: zt.math.Vec4, colBl: zt.math.Vec4, colBr: zt.math.Vec4) void {
106 | if (self.currentTexture == null) {
107 | self.currentTexture = texture;
108 | } else if (self.currentTexture.?.id != texture.id) {
109 | self.flush();
110 | self.currentTexture = texture;
111 | }
112 | const start: zt.math.Vec3 = zt.math.Vec3.new(x, y, z);
113 | const end: zt.math.Vec3 = zt.math.Vec3.new(x + w, y + h, z);
114 |
115 | const tl = Vertex{
116 | .pos = .{ .x = start.x, .y = start.y, .z = start.z },
117 | .col = colTl,
118 | .tex = .{ .x = sx, .y = sy },
119 | };
120 | const tr = Vertex{
121 | .pos = .{ .x = end.x, .y = start.y, .z = start.z },
122 | .col = colTr,
123 | .tex = .{ .x = sx + sw, .y = sy },
124 | };
125 | const bl = Vertex{
126 | .pos = .{ .x = start.x, .y = end.y, .z = start.z },
127 | .col = colBl,
128 | .tex = .{ .x = sx, .y = sy + sh },
129 | };
130 | const br = Vertex{
131 | .pos = .{ .x = end.x, .y = end.y, .z = start.z },
132 | .col = colBr,
133 | .tex = .{ .x = sx + sw, .y = sy + sh },
134 | };
135 |
136 | self.internal.addQuad(bl, tl, tr, br) catch |err| {
137 | if (err == error.NeedsFlush) {
138 | self.flush();
139 | self.internal.addQuad(bl, tl, tr, br) catch unreachable;
140 | return;
141 | }
142 | std.debug.panic("Rendering error: {s}", .{@errorName(err)});
143 | };
144 | }
145 |
146 | /// For expected blank line behaviour, sourceRect should point to a spot on the sheet that is pure white.
147 | /// Otherwise you can point wherever you want for a textured line.
148 | pub fn line(self: *Self, texture: zt.gl.Texture, sourceRect: ?zt.math.Rect, start: zt.math.Vec2, end: zt.math.Vec2, z: f32, width: f32, colStarT: zt.math.Vec4, colEnd: zt.math.Vec4) void {
149 | if (self.currentTexture == null) {
150 | self.currentTexture = texture;
151 | } else if (self.currentTexture.?.id != texture.id) {
152 | self.flush();
153 | self.currentTexture = texture;
154 | }
155 | const source: zt.math.Rect =
156 | if (sourceRect) |s|
157 | zt.math.rect(s.position.x / texture.width, s.position.y / texture.height, s.size.x / texture.width, s.size.y / texture.height)
158 | else
159 | zt.math.rect(0, 0, 1, 1);
160 |
161 | const direction: zt.math.Vec2 = end.sub(start).normalize();
162 | const leftOffset: zt.math.Vec3 = zt.math.vec3(direction.y, -direction.x, 0).scale(width * 0.5);
163 | const rightOffset: zt.math.Vec3 = leftOffset.scale(-1);
164 |
165 | const tl = Vertex{
166 | .pos = zt.math.vec3(start.x, start.y, z).add(leftOffset),
167 | .col = colStarT,
168 | .tex = .{ .x = source.position.x, .y = source.position.y },
169 | };
170 | const tr = Vertex{
171 | .pos = zt.math.vec3(start.x, start.y, z).add(rightOffset),
172 | .col = colStarT,
173 | .tex = .{ .x = source.position.x + source.size.x, .y = source.position.y },
174 | };
175 | const bl = Vertex{
176 | .pos = zt.math.vec3(end.x, end.y, z).add(leftOffset),
177 | .col = colEnd,
178 | .tex = .{ .x = source.position.x, .y = source.position.y + source.size.y },
179 | };
180 | const br = Vertex{
181 | .pos = zt.math.vec3(end.x, end.y, z).add(rightOffset),
182 | .col = colEnd,
183 | .tex = .{ .x = source.position.x + source.size.x, .y = source.position.y + source.size.y },
184 | };
185 |
186 | self.internal.addQuad(bl, tl, tr, br) catch |err| {
187 | if (err == error.NeedsFlush) {
188 | self.flush();
189 | self.internal.addQuad(bl, tl, tr, br) catch unreachable;
190 | return;
191 | }
192 | std.debug.panic("Rendering error: {s}", .{@errorName(err)});
193 | };
194 | }
195 |
196 | pub fn circle(self: *Self, texture: zt.gl.Texture, sourceRect: ?zt.math.Rect, target: zt.math.Vec2, radius: f32, z: f32, col: zt.math.Vec4) void {
197 | if (self.currentTexture == null) {
198 | self.currentTexture = texture;
199 | } else if (self.currentTexture.?.id != texture.id) {
200 | self.flush();
201 | self.currentTexture = texture;
202 | }
203 | const twoPi: f32 = 2.0 * std.math.pi;
204 |
205 | const addition: i32 = std.math.clamp(@as(i32, @intFromFloat(@round(radius / 100.0))) * 10, 0, 20);
206 | const triCount: i32 = (@as(i32, @intFromFloat(twoPi)) * self.resolution) + addition;
207 | var i: i32 = 0;
208 |
209 | const source: zt.math.Rect =
210 | if (sourceRect) |s|
211 | zt.math.rect(s.position.x / texture.width, s.position.y / texture.height, s.size.x / texture.width, s.size.y / texture.height)
212 | else
213 | zt.math.rect(0, 0, 1, 1);
214 |
215 | while (i < triCount) : (i += 1) {
216 | const c = Vertex{
217 | .pos = zt.math.vec3(target.x, target.y, z),
218 | .col = col,
219 | .tex = .{ .x = source.position.x, .y = source.position.y },
220 | };
221 | const l = Vertex{
222 | .pos = zt.math.vec3(target.x + (radius * @cos(@as(f32, @floatFromInt(i)) * twoPi / @as(f32, @floatFromInt(triCount)))), target.y + (radius * @sin(@as(f32, @floatFromInt(i)) * twoPi / @as(f32, @floatFromInt(triCount)))), z),
223 | .col = col,
224 | .tex = .{ .x = source.position.x + source.size.x, .y = source.position.y },
225 | };
226 | const r = Vertex{
227 | .pos = zt.math.vec3(target.x + (radius * @cos(@as(f32, @floatFromInt(i + 1)) * twoPi / @as(f32, @floatFromInt(triCount)))), target.y + (radius * @sin(@as(f32, @floatFromInt(i + 1)) * twoPi / @as(f32, @floatFromInt(triCount)))), z),
228 | .col = col,
229 | .tex = .{ .x = source.position.x, .y = source.position.y + source.size.y },
230 | };
231 | self.internal.addTri(c, l, r) catch {
232 | self.flush();
233 | self.internal.addTri(c, l, r) catch unreachable;
234 | };
235 | }
236 | }
237 |
238 | /// For expected blank line behaviour, sourceRect should point to a spot on the sheet that is pure white.
239 | /// It is not recommended to use this textured.
240 | pub fn rectangleHollow(self: *Self, texture: zt.gl.Texture, sourceRect: ?zt.math.Rect, target: zt.math.Rect, z: f32, thickness: f32, col: zt.math.Vec4) void {
241 | var tl = target.position;
242 | var tr = target.position.add(.{ .x = target.size.x });
243 | var bl = target.position.add(.{ .y = target.size.y });
244 | var br = target.position.add(target.size);
245 | self.line(texture, sourceRect, tl, tr.add(.{ .x = thickness * 0.5 }), z, thickness, col, col);
246 | self.line(texture, sourceRect, tr, br.add(.{ .y = thickness * 0.5 }), z, thickness, col, col);
247 | self.line(texture, sourceRect, br, bl.add(.{ .x = -thickness * 0.5 }), z, thickness, col, col);
248 | self.line(texture, sourceRect, bl, tl.add(.{ .y = -thickness * 0.5 }), z, thickness, col, col);
249 | }
250 |
251 | pub fn text(self: *Self, pos: zt.math.Vec2, string: []const u8, col: zt.math.Vec4) void {
252 | _ = self;
253 | const ig = @import("imgui");
254 | const drawlist = ig.igGetBackgroundDrawList_Nil();
255 |
256 | const colCast: [4]u8 = .{
257 | @intFromFloat(255 * col.x),
258 | @intFromFloat(255 * col.y),
259 | @intFromFloat(255 * col.z),
260 | @intFromFloat(255 * col.w),
261 | };
262 | ig.ImDrawList_AddText_Vec2(drawlist, pos, @bitCast(colCast), string.ptr, null);
263 | }
264 |
265 | pub fn clear(self: *Self) void {
266 | self.internal.clear();
267 | }
268 | /// If there is nothing to draw, nothing will happen, so make sure to call this at the end of every render phase that might
269 | /// have dangling sprites to blit!
270 | pub fn flush(self: *Self) void {
271 | if (self.currentTexture == null or self.internal.indCount == 0) {
272 | return;
273 | }
274 | gl.glEnable(gl.GL_BLEND);
275 | gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA);
276 | self.internal.setUniform("View", self.viewMatrix);
277 | self.internal.setUniform("Projection", self.projectionMatrix);
278 | self.internal.pushDynamic();
279 | self.currentTexture.?.bind();
280 | self.internal.flush();
281 | self.internal.clear();
282 | self.currentTexture.?.unbind();
283 | }
284 |
285 | pub fn worldToScreen(self: *Self, point: zt.math.Vec2) zt.math.Vec2 {
286 | return point.transform4(self.viewMatrix);
287 | }
288 |
289 | pub fn screenToWorld(self: *Self, point: zt.math.Vec2) zt.math.Vec2 {
290 | return point.transform4(self.inverseViewMatrix);
291 | }
292 |
--------------------------------------------------------------------------------
/src/dep/stb/stb_easy_font.h:
--------------------------------------------------------------------------------
1 | // stb_easy_font.h - v1.1 - bitmap font for 3D rendering - public domain
2 | // Sean Barrett, Feb 2015
3 | //
4 | // Easy-to-deploy,
5 | // reasonably compact,
6 | // extremely inefficient performance-wise,
7 | // crappy-looking,
8 | // ASCII-only,
9 | // bitmap font for use in 3D APIs.
10 | //
11 | // Intended for when you just want to get some text displaying
12 | // in a 3D app as quickly as possible.
13 | //
14 | // Doesn't use any textures, instead builds characters out of quads.
15 | //
16 | // DOCUMENTATION:
17 | //
18 | // int stb_easy_font_width(char *text)
19 | // int stb_easy_font_height(char *text)
20 | //
21 | // Takes a string and returns the horizontal size and the
22 | // vertical size (which can vary if 'text' has newlines).
23 | //
24 | // int stb_easy_font_print(float x, float y,
25 | // char *text, unsigned char color[4],
26 | // void *vertex_buffer, int vbuf_size)
27 | //
28 | // Takes a string (which can contain '\n') and fills out a
29 | // vertex buffer with renderable data to draw the string.
30 | // Output data assumes increasing x is rightwards, increasing y
31 | // is downwards.
32 | //
33 | // The vertex data is divided into quads, i.e. there are four
34 | // vertices in the vertex buffer for each quad.
35 | //
36 | // The vertices are stored in an interleaved format:
37 | //
38 | // x:float
39 | // y:float
40 | // z:float
41 | // color:uint8[4]
42 | //
43 | // You can ignore z and color if you get them from elsewhere
44 | // This format was chosen in the hopes it would make it
45 | // easier for you to reuse existing vertex-buffer-drawing code.
46 | //
47 | // If you pass in NULL for color, it becomes 255,255,255,255.
48 | //
49 | // Returns the number of quads.
50 | //
51 | // If the buffer isn't large enough, it will truncate.
52 | // Expect it to use an average of ~270 bytes per character.
53 | //
54 | // If your API doesn't draw quads, build a reusable index
55 | // list that allows you to render quads as indexed triangles.
56 | //
57 | // void stb_easy_font_spacing(float spacing)
58 | //
59 | // Use positive values to expand the space between characters,
60 | // and small negative values (no smaller than -1.5) to contract
61 | // the space between characters.
62 | //
63 | // E.g. spacing = 1 adds one "pixel" of spacing between the
64 | // characters. spacing = -1 is reasonable but feels a bit too
65 | // compact to me; -0.5 is a reasonable compromise as long as
66 | // you're scaling the font up.
67 | //
68 | // LICENSE
69 | //
70 | // See end of file for license information.
71 | //
72 | // VERSION HISTORY
73 | //
74 | // (2020-02-02) 1.1 make everything static so can compile it in more than one src file
75 | // (2017-01-15) 1.0 space character takes same space as numbers; fix bad spacing of 'f'
76 | // (2016-01-22) 0.7 width() supports multiline text; add height()
77 | // (2015-09-13) 0.6 #include ; updated license
78 | // (2015-02-01) 0.5 First release
79 | //
80 | // CONTRIBUTORS
81 | //
82 | // github:vassvik -- bug report
83 | // github:podsvirov -- fix multiple definition errors
84 |
85 | #if 0
86 | // SAMPLE CODE:
87 | //
88 | // Here's sample code for old OpenGL; it's a lot more complicated
89 | // to make work on modern APIs, and that's your problem.
90 | //
91 | void print_string(float x, float y, char *text, float r, float g, float b)
92 | {
93 | static char buffer[99999]; // ~500 chars
94 | int num_quads;
95 |
96 | num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer));
97 |
98 | glColor3f(r,g,b);
99 | glEnableClientState(GL_VERTEX_ARRAY);
100 | glVertexPointer(2, GL_FLOAT, 16, buffer);
101 | glDrawArrays(GL_QUADS, 0, num_quads*4);
102 | glDisableClientState(GL_VERTEX_ARRAY);
103 | }
104 | #endif
105 |
106 | #ifndef INCLUDE_STB_EASY_FONT_H
107 | #define INCLUDE_STB_EASY_FONT_H
108 |
109 | #include
110 | #include
111 |
112 | static struct stb_easy_font_info_struct {
113 | unsigned char advance;
114 | unsigned char h_seg;
115 | unsigned char v_seg;
116 | } stb_easy_font_charinfo[96] = {
117 | { 6, 0, 0 }, { 3, 0, 0 }, { 5, 1, 1 }, { 7, 1, 4 },
118 | { 7, 3, 7 }, { 7, 6, 12 }, { 7, 8, 19 }, { 4, 16, 21 },
119 | { 4, 17, 22 }, { 4, 19, 23 }, { 23, 21, 24 }, { 23, 22, 31 },
120 | { 20, 23, 34 }, { 22, 23, 36 }, { 19, 24, 36 }, { 21, 25, 36 },
121 | { 6, 25, 39 }, { 6, 27, 43 }, { 6, 28, 45 }, { 6, 30, 49 },
122 | { 6, 33, 53 }, { 6, 34, 57 }, { 6, 40, 58 }, { 6, 46, 59 },
123 | { 6, 47, 62 }, { 6, 55, 64 }, { 19, 57, 68 }, { 20, 59, 68 },
124 | { 21, 61, 69 }, { 22, 66, 69 }, { 21, 68, 69 }, { 7, 73, 69 },
125 | { 9, 75, 74 }, { 6, 78, 81 }, { 6, 80, 85 }, { 6, 83, 90 },
126 | { 6, 85, 91 }, { 6, 87, 95 }, { 6, 90, 96 }, { 7, 92, 97 },
127 | { 6, 96,102 }, { 5, 97,106 }, { 6, 99,107 }, { 6,100,110 },
128 | { 6,100,115 }, { 7,101,116 }, { 6,101,121 }, { 6,101,125 },
129 | { 6,102,129 }, { 7,103,133 }, { 6,104,140 }, { 6,105,145 },
130 | { 7,107,149 }, { 6,108,151 }, { 7,109,155 }, { 7,109,160 },
131 | { 7,109,165 }, { 7,118,167 }, { 6,118,172 }, { 4,120,176 },
132 | { 6,122,177 }, { 4,122,181 }, { 23,124,182 }, { 22,129,182 },
133 | { 4,130,182 }, { 22,131,183 }, { 6,133,187 }, { 22,135,191 },
134 | { 6,137,192 }, { 22,139,196 }, { 6,144,197 }, { 22,147,198 },
135 | { 6,150,202 }, { 19,151,206 }, { 21,152,207 }, { 6,155,209 },
136 | { 3,160,210 }, { 23,160,211 }, { 22,164,216 }, { 22,165,220 },
137 | { 22,167,224 }, { 22,169,228 }, { 21,171,232 }, { 21,173,233 },
138 | { 5,178,233 }, { 22,179,234 }, { 23,180,238 }, { 23,180,243 },
139 | { 23,180,248 }, { 22,189,248 }, { 22,191,252 }, { 5,196,252 },
140 | { 3,203,252 }, { 5,203,253 }, { 22,210,253 }, { 0,214,253 },
141 | };
142 |
143 | static unsigned char stb_easy_font_hseg[214] = {
144 | 97,37,69,84,28,51,2,18,10,49,98,41,65,25,81,105,33,9,97,1,97,37,37,36,
145 | 81,10,98,107,3,100,3,99,58,51,4,99,58,8,73,81,10,50,98,8,73,81,4,10,50,
146 | 98,8,25,33,65,81,10,50,17,65,97,25,33,25,49,9,65,20,68,1,65,25,49,41,
147 | 11,105,13,101,76,10,50,10,50,98,11,99,10,98,11,50,99,11,50,11,99,8,57,
148 | 58,3,99,99,107,10,10,11,10,99,11,5,100,41,65,57,41,65,9,17,81,97,3,107,
149 | 9,97,1,97,33,25,9,25,41,100,41,26,82,42,98,27,83,42,98,26,51,82,8,41,
150 | 35,8,10,26,82,114,42,1,114,8,9,73,57,81,41,97,18,8,8,25,26,26,82,26,82,
151 | 26,82,41,25,33,82,26,49,73,35,90,17,81,41,65,57,41,65,25,81,90,114,20,
152 | 84,73,57,41,49,25,33,65,81,9,97,1,97,25,33,65,81,57,33,25,41,25,
153 | };
154 |
155 | static unsigned char stb_easy_font_vseg[253] = {
156 | 4,2,8,10,15,8,15,33,8,15,8,73,82,73,57,41,82,10,82,18,66,10,21,29,1,65,
157 | 27,8,27,9,65,8,10,50,97,74,66,42,10,21,57,41,29,25,14,81,73,57,26,8,8,
158 | 26,66,3,8,8,15,19,21,90,58,26,18,66,18,105,89,28,74,17,8,73,57,26,21,
159 | 8,42,41,42,8,28,22,8,8,30,7,8,8,26,66,21,7,8,8,29,7,7,21,8,8,8,59,7,8,
160 | 8,15,29,8,8,14,7,57,43,10,82,7,7,25,42,25,15,7,25,41,15,21,105,105,29,
161 | 7,57,57,26,21,105,73,97,89,28,97,7,57,58,26,82,18,57,57,74,8,30,6,8,8,
162 | 14,3,58,90,58,11,7,74,43,74,15,2,82,2,42,75,42,10,67,57,41,10,7,2,42,
163 | 74,106,15,2,35,8,8,29,7,8,8,59,35,51,8,8,15,35,30,35,8,8,30,7,8,8,60,
164 | 36,8,45,7,7,36,8,43,8,44,21,8,8,44,35,8,8,43,23,8,8,43,35,8,8,31,21,15,
165 | 20,8,8,28,18,58,89,58,26,21,89,73,89,29,20,8,8,30,7,
166 | };
167 |
168 | typedef struct
169 | {
170 | unsigned char c[4];
171 | } stb_easy_font_color;
172 |
173 | static int stb_easy_font_draw_segs(float x, float y, unsigned char *segs, int num_segs, int vertical, stb_easy_font_color c, char *vbuf, int vbuf_size, int offset)
174 | {
175 | int i,j;
176 | for (i=0; i < num_segs; ++i) {
177 | int len = segs[i] & 7;
178 | x += (float) ((segs[i] >> 3) & 1);
179 | if (len && offset+64 <= vbuf_size) {
180 | float y0 = y + (float) (segs[i]>>4);
181 | for (j=0; j < 4; ++j) {
182 | * (float *) (vbuf+offset+0) = x + (j==1 || j==2 ? (vertical ? 1 : len) : 0);
183 | * (float *) (vbuf+offset+4) = y0 + ( j >= 2 ? (vertical ? len : 1) : 0);
184 | * (float *) (vbuf+offset+8) = 0.f;
185 | * (stb_easy_font_color *) (vbuf+offset+12) = c;
186 | offset += 16;
187 | }
188 | }
189 | }
190 | return offset;
191 | }
192 |
193 | static float stb_easy_font_spacing_val = 0;
194 | static void stb_easy_font_spacing(float spacing)
195 | {
196 | stb_easy_font_spacing_val = spacing;
197 | }
198 |
199 | static int stb_easy_font_print(float x, float y, char *text, unsigned char color[4], void *vertex_buffer, int vbuf_size)
200 | {
201 | char *vbuf = (char *) vertex_buffer;
202 | float start_x = x;
203 | int offset = 0;
204 |
205 | stb_easy_font_color c = { 255,255,255,255 }; // use structure copying to avoid needing depending on memcpy()
206 | if (color) { c.c[0] = color[0]; c.c[1] = color[1]; c.c[2] = color[2]; c.c[3] = color[3]; }
207 |
208 | while (*text && offset < vbuf_size) {
209 | if (*text == '\n') {
210 | y += 12;
211 | x = start_x;
212 | } else {
213 | unsigned char advance = stb_easy_font_charinfo[*text-32].advance;
214 | float y_ch = advance & 16 ? y+1 : y;
215 | int h_seg, v_seg, num_h, num_v;
216 | h_seg = stb_easy_font_charinfo[*text-32 ].h_seg;
217 | v_seg = stb_easy_font_charinfo[*text-32 ].v_seg;
218 | num_h = stb_easy_font_charinfo[*text-32+1].h_seg - h_seg;
219 | num_v = stb_easy_font_charinfo[*text-32+1].v_seg - v_seg;
220 | offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_hseg[h_seg], num_h, 0, c, vbuf, vbuf_size, offset);
221 | offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_vseg[v_seg], num_v, 1, c, vbuf, vbuf_size, offset);
222 | x += advance & 15;
223 | x += stb_easy_font_spacing_val;
224 | }
225 | ++text;
226 | }
227 | return (unsigned) offset/64;
228 | }
229 |
230 | static int stb_easy_font_width(char *text)
231 | {
232 | float len = 0;
233 | float max_len = 0;
234 | while (*text) {
235 | if (*text == '\n') {
236 | if (len > max_len) max_len = len;
237 | len = 0;
238 | } else {
239 | len += stb_easy_font_charinfo[*text-32].advance & 15;
240 | len += stb_easy_font_spacing_val;
241 | }
242 | ++text;
243 | }
244 | if (len > max_len) max_len = len;
245 | return (int) ceil(max_len);
246 | }
247 |
248 | static int stb_easy_font_height(char *text)
249 | {
250 | float y = 0;
251 | int nonempty_line=0;
252 | while (*text) {
253 | if (*text == '\n') {
254 | y += 12;
255 | nonempty_line = 0;
256 | } else {
257 | nonempty_line = 1;
258 | }
259 | ++text;
260 | }
261 | return (int) ceil(y + (nonempty_line ? 12 : 0));
262 | }
263 | #endif
264 |
265 | /*
266 | ------------------------------------------------------------------------------
267 | This software is available under 2 licenses -- choose whichever you prefer.
268 | ------------------------------------------------------------------------------
269 | ALTERNATIVE A - MIT License
270 | Copyright (c) 2017 Sean Barrett
271 | Permission is hereby granted, free of charge, to any person obtaining a copy of
272 | this software and associated documentation files (the "Software"), to deal in
273 | the Software without restriction, including without limitation the rights to
274 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
275 | of the Software, and to permit persons to whom the Software is furnished to do
276 | so, subject to the following conditions:
277 | The above copyright notice and this permission notice shall be included in all
278 | copies or substantial portions of the Software.
279 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
280 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
281 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
282 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
283 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
284 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
285 | SOFTWARE.
286 | ------------------------------------------------------------------------------
287 | ALTERNATIVE B - Public Domain (www.unlicense.org)
288 | This is free and unencumbered software released into the public domain.
289 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
290 | software, either in source code form or as a compiled binary, for any purpose,
291 | commercial or non-commercial, and by any means.
292 | In jurisdictions that recognize copyright laws, the author or authors of this
293 | software dedicate any and all copyright interest in the software to the public
294 | domain. We make this dedication for the benefit of the public at large and to
295 | the detriment of our heirs and successors. We intend this dedication to be an
296 | overt act of relinquishment in perpetuity of all present and future rights to
297 | this software under copyright law.
298 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
299 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
300 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
301 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
302 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
303 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
304 | ------------------------------------------------------------------------------
305 | */
306 |
--------------------------------------------------------------------------------
/src/zt/generateBuffer.zig:
--------------------------------------------------------------------------------
1 | const gl = @import("gl");
2 | const std = @import("std");
3 | const builtin = @import("builtin");
4 | const zt = @import("../zt.zig");
5 |
6 | fn reportErr(msg: []const u8) void {
7 | if (builtin.mode == .Debug) {
8 | var err = gl.glGetError();
9 | while (err != gl.GL_NO_ERROR) {
10 | switch (err) {
11 | gl.GL_INVALID_ENUM => {
12 | std.debug.print("{s}\nOPENGL ERROR: INVALID ENUM\n", .{msg});
13 | },
14 | gl.GL_INVALID_VALUE => {
15 | std.debug.print("{s}\nOPENGL ERROR: INVALID VALUE\n", .{msg});
16 | },
17 | gl.GL_INVALID_OPERATION => {
18 | std.debug.print("{s}\nOPENGL ERROR: INVALID OPERATION\n", .{msg});
19 | },
20 | gl.GL_OUT_OF_MEMORY => {
21 | std.debug.print("{s}\nOPENGL ERROR: OUT OF MEMORY\n", .{msg});
22 | },
23 | else => {
24 | std.debug.print("{s}\nOPENGL ERROR: UNKNOWN ERROR\n", .{msg});
25 | },
26 | }
27 |
28 | err = gl.glGetError();
29 | }
30 | }
31 | }
32 |
33 | /// Provide T as a struct to represent a vertex. Compatible types inside of struct are:
34 | /// `f32, zt.math.Vec2, zt.math.Vec3, zt.math.Vec4`
35 | /// and each will be mapped in order to vert shader's layout indices.
36 | /// The resulting buffer can contain many quads and tris together.
37 | /// V is a maximum vertex count before flush is requested by an error on add*() functions.
38 | pub fn GenerateBuffer(comptime T: type, comptime V: usize) type {
39 | return struct {
40 | pub const VertexLimit = V;
41 | pub const IndexLimit = V * 6;
42 | vaoId: c_uint = undefined,
43 | vboId: c_uint = undefined,
44 | iboId: c_uint = undefined,
45 |
46 | vertices: [V]T = undefined,
47 | vertCount: usize = 0,
48 | // Worst case scenario every single draw is a quad, so * 6.
49 | indices: [IndexLimit]c_uint = undefined,
50 | indCount: usize = 0,
51 | shader: zt.gl.Shader = undefined,
52 |
53 | dirty: bool = true,
54 |
55 | pub fn init(shader: zt.gl.Shader) @This() {
56 | var self = @This(){};
57 | self.shader = shader;
58 |
59 | gl.glGenBuffers(1, &self.vboId);
60 | gl.glGenBuffers(1, &self.iboId);
61 |
62 | // Create VAO
63 | gl.glGenVertexArrays(1, &self.vaoId);
64 |
65 | gl.glBindVertexArray(self.vaoId);
66 | gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vboId);
67 | gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.iboId);
68 |
69 | var currentOffset: usize = 0;
70 | const stride: c_int = @intCast(@sizeOf(T));
71 |
72 | inline for (std.meta.fields(T), 0..) |field, i| {
73 | switch (field.type) {
74 | bool => {
75 | gl.glVertexAttribPointer(@intCast(i), 1, gl.GL_BOOL, gl.GL_FALSE, stride, @as(*allowzero anyopaque, @ptrFromInt(currentOffset)));
76 | gl.glEnableVertexAttribArray(@as(c_uint, @intCast(i)));
77 | currentOffset += @sizeOf(bool);
78 | },
79 | // TODO: Figure out opengl's api for bytes and shorts in uniforms.
80 | // i8 => {
81 | // gl.glVertexAttribPointer(@intCast(c_uint, i), 1, gl.GL_BYTE, gl.GL_FALSE, stride, @intToPtr(*allowzero anyopaque, currentOffset));
82 | // gl.glEnableVertexAttribArray(@intCast(c_uint, i));
83 | // currentOffset += @sizeOf(i8);
84 | // },
85 | // u8 => {
86 | // gl.glVertexAttribPointer(@intCast(c_uint, i), 1, gl.GL_UNSIGNED_BYTE, gl.GL_FALSE, stride, @intToPtr(*allowzero anyopaque, currentOffset));
87 | // gl.glEnableVertexAttribArray(@intCast(c_uint, i));
88 | // currentOffset += @sizeOf(u8);
89 | // },
90 | // i16 => {
91 | // gl.glVertexAttribPointer(@intCast(c_uint, i), 1, gl.GL_SHORT, gl.GL_FALSE, stride, @intToPtr(*allowzero anyopaque, currentOffset));
92 | // gl.glEnableVertexAttribArray(@intCast(c_uint, i));
93 | // currentOffset += @sizeOf(i16);
94 | // },
95 | // u16 => {
96 | // gl.glVertexAttribPointer(@intCast(c_uint, i), 1, gl.GL_UNSIGNED_SHORT, gl.GL_FALSE, stride, @intToPtr(*allowzero anyopaque, currentOffset));
97 | // gl.glEnableVertexAttribArray(@intCast(c_uint, i));
98 | // currentOffset += @sizeOf(u16);
99 | // },
100 | i32 => {
101 | gl.glVertexAttribIPointer(@as(c_uint, @intCast(i)), 1, gl.GL_INT, stride, @as(*allowzero anyopaque, @ptrFromInt(currentOffset)));
102 | gl.glEnableVertexAttribArray(@as(c_uint, @intCast(i)));
103 | currentOffset += @sizeOf(i32);
104 | },
105 | u32 => {
106 | gl.glVertexAttribIPointer(@as(c_uint, @intCast(i)), 1, gl.GL_UNSIGNED_INT, stride, @as(*allowzero anyopaque, @ptrFromInt(currentOffset)));
107 | gl.glEnableVertexAttribArray(@as(c_uint, @intCast(i)));
108 | currentOffset += @sizeOf(u32);
109 | },
110 | f32 => {
111 | gl.glVertexAttribPointer(@as(c_uint, @intCast(i)), 1, gl.GL_FLOAT, gl.GL_FALSE, stride, @as(*allowzero anyopaque, @ptrFromInt(currentOffset)));
112 | gl.glEnableVertexAttribArray(@as(c_uint, @intCast(i)));
113 | currentOffset += 4;
114 | },
115 | zt.math.Vec2 => {
116 | gl.glVertexAttribPointer(@as(c_uint, @intCast(i)), 2, gl.GL_FLOAT, gl.GL_FALSE, stride, @as(*allowzero anyopaque, @ptrFromInt(currentOffset)));
117 | gl.glEnableVertexAttribArray(@as(c_uint, @intCast(i)));
118 | currentOffset += 8;
119 | },
120 | zt.math.Vec3 => {
121 | gl.glVertexAttribPointer(@as(c_uint, @intCast(i)), 3, gl.GL_FLOAT, gl.GL_FALSE, stride, @as(*allowzero anyopaque, @ptrFromInt(currentOffset)));
122 | gl.glEnableVertexAttribArray(@as(c_uint, @intCast(i)));
123 | currentOffset += 12;
124 | },
125 | zt.math.Vec4 => {
126 | gl.glVertexAttribPointer(@as(c_uint, @intCast(i)), 4, gl.GL_FLOAT, gl.GL_FALSE, stride, @as(*allowzero anyopaque, @ptrFromInt(currentOffset)));
127 | gl.glEnableVertexAttribArray(@as(c_uint, @intCast(i)));
128 | currentOffset += 16;
129 | },
130 | else => {
131 | @compileError("Vertex Struct had types incompatible with automatic buffers:" ++ @typeName(field.field_type));
132 | },
133 | }
134 | }
135 |
136 | gl.glBindVertexArray(0);
137 | return self;
138 | }
139 | pub fn deinit(self: *@This()) void {
140 | gl.glDeleteVertexArrays(1, &self.vaoId);
141 | gl.glDeleteBuffers(1, &self.vboId);
142 | gl.glDeleteBuffers(1, &self.iboId);
143 | reportErr("Deleting the buffers:");
144 | }
145 | pub fn bind(self: *@This()) void {
146 | gl.glBindVertexArray(self.vaoId);
147 | self.shader.bind();
148 | gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vboId);
149 | gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.iboId);
150 | reportErr("Binding the buffers:");
151 | }
152 | pub fn unbind(self: *@This()) void {
153 | gl.glBindVertexArray(0);
154 | self.shader.unbind();
155 | gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0);
156 | gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0);
157 | reportErr("Unbinding the buffers:");
158 | }
159 | pub fn clear(self: *@This()) void {
160 | self.vertCount = 0;
161 | self.indCount = 0;
162 | self.dirty = true;
163 | }
164 |
165 | pub fn addTri(self: *@This(), v1: T, v2: T, v3: T) !void {
166 | if (self.vertCount + 3 >= V) {
167 | return error.NeedsFlush;
168 | }
169 | self.vertices[self.vertCount + 0] = v1;
170 | self.vertices[self.vertCount + 1] = v2;
171 | self.vertices[self.vertCount + 2] = v3;
172 |
173 | self.indices[self.indCount + 0] = (@intCast(self.vertCount + 0));
174 | self.indices[self.indCount + 1] = (@intCast(self.vertCount + 1));
175 | self.indices[self.indCount + 2] = (@intCast(self.vertCount + 2));
176 |
177 | self.indCount += 3;
178 | self.vertCount += 3;
179 | self.dirty = true;
180 | }
181 | pub fn addQuad(self: *@This(), bl: T, tl: T, tr: T, br: T) !void {
182 | if (self.vertCount + 4 >= V) {
183 | return error.NeedsFlush;
184 | }
185 | self.vertices[self.vertCount + 0] = bl;
186 | self.vertices[self.vertCount + 1] = tl;
187 | self.vertices[self.vertCount + 2] = tr;
188 | self.vertices[self.vertCount + 3] = br;
189 |
190 | self.indices[self.indCount + 0] = @intCast(self.vertCount + 0);
191 | self.indices[self.indCount + 1] = @intCast(self.vertCount + 1);
192 | self.indices[self.indCount + 2] = @intCast(self.vertCount + 3);
193 | self.indices[self.indCount + 3] = @intCast(self.vertCount + 1);
194 | self.indices[self.indCount + 4] = @intCast(self.vertCount + 2);
195 | self.indices[self.indCount + 5] = @intCast(self.vertCount + 3);
196 |
197 | self.vertCount += 4;
198 | self.indCount += 6;
199 | self.dirty = true;
200 | }
201 | /// Commits to opengl.gl with the currently added sprites in a static memory location. Use this if you are going
202 | /// to very rarely push again. You can still flush as many times as needed.
203 | pub fn pushStatic(self: *@This()) void {
204 | if (!self.dirty) {
205 | return;
206 | }
207 | self.bind();
208 | const vertSize: c_longlong = @intCast(@sizeOf(T) * self.vertCount);
209 | const indSize: c_longlong = @intCast(@sizeOf(c_uint) * self.indCount);
210 | gl.glBufferData(gl.GL_ARRAY_BUFFER, vertSize, &self.vertices, gl.GL_STATIC_DRAW);
211 | gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, indSize, &self.indices, gl.GL_STATIC_DRAW);
212 | self.unbind();
213 | self.dirty = false;
214 | }
215 | /// Commits to opengl.gl with the currently added sprites in a dynamic memory location. Use this if you are pushing
216 | /// and flushing once a frame.
217 | pub fn pushDynamic(self: *@This()) void {
218 | if (!self.dirty) {
219 | return;
220 | }
221 | self.bind();
222 | const vertSize: c_longlong = @intCast(@sizeOf(T) * self.vertCount);
223 | const indSize: c_longlong = @intCast(@sizeOf(c_uint) * self.indCount);
224 | gl.glBufferData(gl.GL_ARRAY_BUFFER, vertSize, &self.vertices, gl.GL_DYNAMIC_DRAW);
225 | gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, indSize, &self.indices, gl.GL_DYNAMIC_DRAW);
226 | self.unbind();
227 | self.dirty = false;
228 | }
229 | /// Commits to opengl.gl with the currently added sprites in a streaming memory location. Use this if you are going
230 | /// to be pushing and flushing multiple times per frame.
231 | pub fn pushStream(self: *@This()) void {
232 | if (!self.dirty) {
233 | return;
234 | }
235 | self.bind();
236 | const vertSize: c_longlong = @intCast(@sizeOf(T) * self.vertCount);
237 | const indSize: c_longlong = @intCast(@sizeOf(c_uint) * self.indCount);
238 | gl.glBufferData(gl.GL_ARRAY_BUFFER, vertSize, &self.vertices, gl.GL_STREAM_DRAW);
239 | gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, indSize, &self.indices, gl.GL_STREAM_DRAW);
240 | self.unbind();
241 | self.dirty = false;
242 | reportErr("Pushing the buffers:");
243 | }
244 |
245 | /// Draws the currently pushed data to the screen. Note the data is not cleared, leaving you the option to maintain
246 | /// the current vertices every frame if so desired.
247 | pub fn flush(self: *@This()) void {
248 | self.bind();
249 | gl.glDrawElements(gl.GL_TRIANGLES, @intCast(self.indCount), gl.GL_UNSIGNED_INT, null);
250 | self.unbind();
251 | reportErr("Flushing the buffers:");
252 | }
253 |
254 | pub fn setUniform(self: *@This(), comptime uniName: []const u8, uniform: anytype) void {
255 | const loc: c_int = gl.glGetUniformLocation(self.shader.id, uniName.ptr);
256 | self.shader.bind();
257 | if (loc != -1) {
258 | switch (@TypeOf(uniform)) {
259 | bool => {
260 | gl.glUniform1i(loc, if (uniform) 1 else 0);
261 | reportErr("Setting a uniform bool(i32):");
262 | },
263 | i32 => {
264 | gl.glUniform1i(loc, uniform);
265 | reportErr("Setting a uniform i32:");
266 | },
267 | u32 => {
268 | gl.glUniform1ui(loc, uniform);
269 | reportErr("Setting a uniform u32:");
270 | },
271 | f32 => {
272 | gl.glUniform1f(loc, uniform);
273 | reportErr("Setting a uniform f32:");
274 | },
275 | zt.math.Vec2 => {
276 | gl.glUniform2f(loc, uniform.x, uniform.y);
277 | reportErr("Setting a uniform vec2:");
278 | },
279 | zt.math.Vec3 => {
280 | gl.glUniform3f(loc, uniform.x, uniform.y, uniform.z);
281 | reportErr("Setting a uniform vec3:");
282 | },
283 | zt.math.Vec4 => {
284 | gl.glUniform4f(loc, uniform.x, uniform.y, uniform.z, uniform.w);
285 | reportErr("Setting a uniform vec4:");
286 | },
287 | zt.math.Mat4 => {
288 | gl.glUniformMatrix4fv(loc, 1, 0, &uniform.inlined());
289 | reportErr("Setting a uniform mat4:");
290 | },
291 | else => {
292 | @compileError("You cannot use that type in a genbuffer's uniform.");
293 | },
294 | }
295 | }
296 | self.shader.unbind();
297 | reportErr("Setting a uniform (location not found):");
298 | }
299 | };
300 | }
301 |
--------------------------------------------------------------------------------
/src/dep/stb/stb_divide.h:
--------------------------------------------------------------------------------
1 | // stb_divide.h - v0.94 - public domain - Sean Barrett, Feb 2010
2 | // Three kinds of divide/modulus of signed integers.
3 | //
4 | // HISTORY
5 | //
6 | // v0.94 Fix integer overflow issues
7 | // v0.93 2020-02-02 Write useful exit() value from main()
8 | // v0.92 2019-02-25 Fix warning
9 | // v0.91 2010-02-27 Fix euclidean division by INT_MIN for non-truncating C
10 | // Check result with 64-bit math to catch such cases
11 | // v0.90 2010-02-24 First public release
12 | //
13 | // USAGE
14 | //
15 | // In *ONE* source file, put:
16 | //
17 | // #define STB_DIVIDE_IMPLEMENTATION
18 | // // #define C_INTEGER_DIVISION_TRUNCATES // see Note 1
19 | // // #define C_INTEGER_DIVISION_FLOORS // see Note 2
20 | // #include "stb_divide.h"
21 | //
22 | // Other source files should just include stb_divide.h
23 | //
24 | // Note 1: On platforms/compilers that you know signed C division
25 | // truncates, you can #define C_INTEGER_DIVISION_TRUNCATES.
26 | //
27 | // Note 2: On platforms/compilers that you know signed C division
28 | // floors (rounds to negative infinity), you can #define
29 | // C_INTEGER_DIVISION_FLOORS.
30 | //
31 | // You can #define STB_DIVIDE_TEST in which case the implementation
32 | // will generate a main() and compiling the result will create a
33 | // program that tests the implementation. Run it with no arguments
34 | // and any output indicates an error; run it with any argument and
35 | // it will also print the test results. Define STB_DIVIDE_TEST_64
36 | // to a 64-bit integer type to avoid overflows in the result-checking
37 | // which give false negatives.
38 | //
39 | // ABOUT
40 | //
41 | // This file provides three different consistent divide/mod pairs
42 | // implemented on top of arbitrary C/C++ division, including correct
43 | // handling of overflow of intermediate calculations:
44 | //
45 | // trunc: a/b truncates to 0, a%b has same sign as a
46 | // floor: a/b truncates to -inf, a%b has same sign as b
47 | // eucl: a/b truncates to sign(b)*inf, a%b is non-negative
48 | //
49 | // Not necessarily optimal; I tried to keep it generally efficient,
50 | // but there may be better ways.
51 | //
52 | // Briefly, for those who are not familiar with the problem, we note
53 | // the reason these divides exist and are interesting:
54 | //
55 | // 'trunc' is easy to implement in hardware (strip the signs,
56 | // compute, reapply the signs), thus is commonly defined
57 | // by many languages (including C99)
58 | //
59 | // 'floor' is simple to define and better behaved than trunc;
60 | // for example it divides integers into fixed-size buckets
61 | // without an extra-wide bucket at 0, and for a fixed
62 | // divisor N there are only |N| possible moduli.
63 | //
64 | // 'eucl' guarantees fixed-sized buckets *and* a non-negative
65 | // modulus and defines division to be whatever is needed
66 | // to achieve that result.
67 | //
68 | // See "The Euclidean definition of the functions div and mod"
69 | // by Raymond Boute (1992), or "Division and Modulus for Computer
70 | // Scientists" by Daan Leijen (2001)
71 | //
72 | // We assume of the built-in C division:
73 | // (a) modulus is the remainder for the corresponding division
74 | // (b) a/b truncates if a and b are the same sign
75 | //
76 | // Property (a) requires (a/b)*b + (a%b)==a, and is required by C.
77 | // Property (b) seems to be true of all hardware but is *not* satisfied
78 | // by the euclidean division operator we define, so it's possibly not
79 | // always true. If any such platform turns up, we can add more cases.
80 | // (Possibly only stb_div_trunc currently relies on property (b).)
81 | //
82 | // LICENSE
83 | //
84 | // See end of file for license information.
85 |
86 |
87 | #ifndef INCLUDE_STB_DIVIDE_H
88 | #define INCLUDE_STB_DIVIDE_H
89 |
90 | #ifdef __cplusplus
91 | extern "C" {
92 | #endif
93 |
94 | extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by);
95 | extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by);
96 | extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by);
97 | extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by);
98 | extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by);
99 | extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
100 |
101 | #ifdef __cplusplus
102 | }
103 | #endif
104 |
105 | #ifdef STB_DIVIDE_IMPLEMENTATION
106 |
107 | #if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901
108 | #ifndef C_INTEGER_DIVISION_TRUNCATES
109 | #define C_INTEGER_DIVISION_TRUNCATES
110 | #endif
111 | #endif
112 |
113 | #ifndef INT_MIN
114 | #include // if you have no limits.h, #define INT_MIN yourself
115 | #endif
116 |
117 | // the following macros are designed to allow testing
118 | // other platforms by simulating them
119 | #ifndef STB_DIVIDE_TEST_FLOOR
120 | #define stb__div(a,b) ((a)/(b))
121 | #define stb__mod(a,b) ((a)%(b))
122 | #else
123 | // implement floor-style divide on trunc platform
124 | #ifndef C_INTEGER_DIVISION_TRUNCATES
125 | #error "floor test requires truncating division"
126 | #endif
127 | #undef C_INTEGER_DIVISION_TRUNCATES
128 | int stb__div(int v1, int v2)
129 | {
130 | int q = v1/v2, r = v1%v2;
131 | if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
132 | return q-1;
133 | else
134 | return q;
135 | }
136 |
137 | int stb__mod(int v1, int v2)
138 | {
139 | int r = v1%v2;
140 | if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
141 | return r+v2;
142 | else
143 | return r;
144 | }
145 | #endif
146 |
147 | int stb_div_trunc(int v1, int v2)
148 | {
149 | #ifdef C_INTEGER_DIVISION_TRUNCATES
150 | return v1/v2;
151 | #else
152 | if (v1 >= 0 && v2 <= 0)
153 | return -stb__div(-v1,v2); // both negative to avoid overflow
154 | if (v1 <= 0 && v2 >= 0)
155 | if (v1 != INT_MIN)
156 | return -stb__div(v1,-v2); // both negative to avoid overflow
157 | else
158 | return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point
159 | else
160 | return v1/v2; // same sign, so expect truncation
161 | #endif
162 | }
163 |
164 | int stb_div_floor(int v1, int v2)
165 | {
166 | #ifdef C_INTEGER_DIVISION_FLOORS
167 | return v1/v2;
168 | #else
169 | if (v1 >= 0 && v2 < 0) {
170 | if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows
171 | return -stb__div((v2+1)-v1,v2); // nope, so just compute it
172 | else
173 | return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0);
174 | }
175 | if (v1 < 0 && v2 >= 0) {
176 | if (v1 != INT_MIN) {
177 | if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows
178 | return -stb__div((v1+1)-v2,-v2); // nope, so just compute it
179 | else
180 | return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0);
181 | } else // it must be possible to compute -(v1+v2) without overflowing
182 | return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1);
183 | } else
184 | return v1/v2; // same sign, so expect truncation
185 | #endif
186 | }
187 |
188 | int stb_div_eucl(int v1, int v2)
189 | {
190 | int q,r;
191 | #ifdef C_INTEGER_DIVISION_TRUNCATES
192 | q = v1/v2;
193 | r = v1%v2;
194 | #else
195 | // handle every quadrant separately, since we can't rely on q and r flor
196 | if (v1 >= 0)
197 | if (v2 >= 0)
198 | return stb__div(v1,v2);
199 | else if (v2 != INT_MIN)
200 | q = -stb__div(v1,-v2), r = stb__mod(v1,-v2);
201 | else
202 | q = 0, r = v1;
203 | else if (v1 != INT_MIN)
204 | if (v2 >= 0)
205 | q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2);
206 | else if (v2 != INT_MIN)
207 | q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2);
208 | else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2
209 | q = 1, r = v1-q*v2;
210 | else // if v1 is INT_MIN, we have to move away from overflow place
211 | if (v2 >= 0)
212 | q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2);
213 | else if (v2 != INT_MIN)
214 | q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2);
215 | else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow
216 | q = 1, r = 0;
217 | #endif
218 | if (r >= 0)
219 | return q;
220 | else
221 | return q + (v2 > 0 ? -1 : 1);
222 | }
223 |
224 | int stb_mod_trunc(int v1, int v2)
225 | {
226 | #ifdef C_INTEGER_DIVISION_TRUNCATES
227 | return v1%v2;
228 | #else
229 | if (v1 >= 0) { // modulus result should always be positive
230 | int r = stb__mod(v1,v2);
231 | if (r >= 0)
232 | return r;
233 | else
234 | return r - (v2 < 0 ? v2 : -v2);
235 | } else { // modulus result should always be negative
236 | int r = stb__mod(v1,v2);
237 | if (r <= 0)
238 | return r;
239 | else
240 | return r + (v2 < 0 ? v2 : -v2);
241 | }
242 | #endif
243 | }
244 |
245 | int stb_mod_floor(int v1, int v2)
246 | {
247 | #ifdef C_INTEGER_DIVISION_FLOORS
248 | return v1%v2;
249 | #else
250 | if (v2 >= 0) { // result should always be positive
251 | int r = stb__mod(v1,v2);
252 | if (r >= 0)
253 | return r;
254 | else
255 | return r + v2;
256 | } else { // result should always be negative
257 | int r = stb__mod(v1,v2);
258 | if (r <= 0)
259 | return r;
260 | else
261 | return r + v2;
262 | }
263 | #endif
264 | }
265 |
266 | int stb_mod_eucl(int v1, int v2)
267 | {
268 | int r = stb__mod(v1,v2);
269 |
270 | if (r >= 0)
271 | return r;
272 | else
273 | return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow]
274 | }
275 |
276 | #ifdef STB_DIVIDE_TEST
277 | #include
278 | #include
279 | #include
280 |
281 | int show=0;
282 | int err=0;
283 |
284 | void stbdiv_check(int q, int r, int a, int b, char *type, int dir)
285 | {
286 | if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) {
287 | fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r);
288 | err++;
289 | } else
290 | if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid
291 | if (r <= -abs(b) || r >= abs(b)) {
292 | fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r);
293 | err++;
294 | }
295 | #ifdef STB_DIVIDE_TEST_64
296 | {
297 | STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b;
298 | if (q64*b64+r64 != a64) {
299 | fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
300 | err++;
301 | }
302 | }
303 | #else
304 | if (q*b+r != a) {
305 | fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
306 | err++;
307 | }
308 | #endif
309 | }
310 |
311 | void test(int a, int b)
312 | {
313 | int q,r;
314 | if (show) printf("(%+11d,%+d) | ", a,b);
315 | q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b);
316 | if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "trunc",a);
317 | q = stb_div_floor(a,b), r = stb_mod_floor(a,b);
318 | if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "floor",b);
319 | q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b);
320 | if (show) printf("(%+11d,%+2d)\n", q,r); stbdiv_check(q,r,a,b, "euclidean",1);
321 | }
322 |
323 | void testh(int a, int b)
324 | {
325 | int q,r;
326 | if (show) printf("(%08x,%08x) |\n", a,b);
327 | q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); stbdiv_check(q,r,a,b, "trunc",a);
328 | if (show) printf(" (%08x,%08x)", q,r);
329 | q = stb_div_floor(a,b), r = stb_mod_floor(a,b); stbdiv_check(q,r,a,b, "floor",b);
330 | if (show) printf(" (%08x,%08x)", q,r);
331 | q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); stbdiv_check(q,r,a,b, "euclidean",1);
332 | if (show) printf(" (%08x,%08x)\n ", q,r);
333 | }
334 |
335 | int main(int argc, char **argv)
336 | {
337 | if (argc > 1) show=1;
338 |
339 | test(8,3);
340 | test(8,-3);
341 | test(-8,3);
342 | test(-8,-3);
343 | test(1,2);
344 | test(1,-2);
345 | test(-1,2);
346 | test(-1,-2);
347 | test(8,4);
348 | test(8,-4);
349 | test(-8,4);
350 | test(-8,-4);
351 |
352 | test(INT_MAX,1);
353 | test(INT_MIN,1);
354 | test(INT_MIN+1,1);
355 | test(INT_MAX,-1);
356 | //test(INT_MIN,-1); // this traps in MSVC, so we leave it untested
357 | test(INT_MIN+1,-1);
358 | test(INT_MIN,-2);
359 | test(INT_MIN+1,2);
360 | test(INT_MIN+1,-2);
361 | test(INT_MAX,2);
362 | test(INT_MAX,-2);
363 | test(INT_MIN+1,2);
364 | test(INT_MIN+1,-2);
365 | test(INT_MIN,2);
366 | test(INT_MIN,-2);
367 | test(INT_MIN,7);
368 | test(INT_MIN,-7);
369 | test(INT_MIN+1,4);
370 | test(INT_MIN+1,-4);
371 |
372 | testh(-7, INT_MIN);
373 | testh(-1, INT_MIN);
374 | testh(1, INT_MIN);
375 | testh(7, INT_MIN);
376 |
377 | testh(INT_MAX-1, INT_MIN);
378 | testh(INT_MAX, INT_MIN);
379 | testh(INT_MIN, INT_MIN);
380 | testh(INT_MIN+1, INT_MIN);
381 |
382 | testh(INT_MAX-1, INT_MAX);
383 | testh(INT_MAX , INT_MAX);
384 | testh(INT_MIN , INT_MAX);
385 | testh(INT_MIN+1, INT_MAX);
386 |
387 | return err > 0 ? 1 : 0;
388 | }
389 | #endif // STB_DIVIDE_TEST
390 | #endif // STB_DIVIDE_IMPLEMENTATION
391 | #endif // INCLUDE_STB_DIVIDE_H
392 |
393 | /*
394 | ------------------------------------------------------------------------------
395 | This software is available under 2 licenses -- choose whichever you prefer.
396 | ------------------------------------------------------------------------------
397 | ALTERNATIVE A - MIT License
398 | Copyright (c) 2017 Sean Barrett
399 | Permission is hereby granted, free of charge, to any person obtaining a copy of
400 | this software and associated documentation files (the "Software"), to deal in
401 | the Software without restriction, including without limitation the rights to
402 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
403 | of the Software, and to permit persons to whom the Software is furnished to do
404 | so, subject to the following conditions:
405 | The above copyright notice and this permission notice shall be included in all
406 | copies or substantial portions of the Software.
407 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
408 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
409 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
410 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
411 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
412 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
413 | SOFTWARE.
414 | ------------------------------------------------------------------------------
415 | ALTERNATIVE B - Public Domain (www.unlicense.org)
416 | This is free and unencumbered software released into the public domain.
417 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
418 | software, either in source code form or as a compiled binary, for any purpose,
419 | commercial or non-commercial, and by any means.
420 | In jurisdictions that recognize copyright laws, the author or authors of this
421 | software dedicate any and all copyright interest in the software to the public
422 | domain. We make this dedication for the benefit of the public at large and to
423 | the detriment of our heirs and successors. We intend this dedication to be an
424 | overt act of relinquishment in perpetuity of all present and future rights to
425 | this software under copyright law.
426 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
427 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
428 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
429 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
430 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
431 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
432 | ------------------------------------------------------------------------------
433 | */
434 |
--------------------------------------------------------------------------------
/src/zt/app.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const stb = @import("stb_image");
3 | const ImGuiImplementation = @import("imguiImplementation.zig");
4 | const TimeManager = @import("timeManager.zig");
5 |
6 | const zt = @import("../zt.zig");
7 | const gl = @import("gl");
8 | const glfw = @import("glfw");
9 | const ig = @import("imgui");
10 |
11 | var initialized: bool = false;
12 |
13 | /// Default GLFW error handling callback
14 | fn errorCallback(error_code: glfw.ErrorCode, description: [:0]const u8) void {
15 | std.log.err("glfw: {}: {s}\n", .{ error_code, description });
16 | }
17 |
18 | pub fn App(comptime Data: type) type {
19 | return struct {
20 | const Self = @This();
21 | pub const InputEvent = union(enum) {
22 | keyboard: struct {
23 | key: c_int,
24 | scan: c_int,
25 | action: c_int,
26 | mods: c_int,
27 | },
28 | mouseWheel: struct {
29 | x: f64,
30 | y: f64,
31 | },
32 | mouseButton: struct {
33 | key: c_int,
34 | action: c_int,
35 | mods: c_int,
36 | },
37 | mousePosition: struct {
38 | x: f64,
39 | y: f64,
40 | },
41 | character: struct {
42 | value: c_uint,
43 | },
44 | };
45 | pub const Settings = struct {
46 | energySaving: bool = true,
47 | imguiActive: bool = true,
48 | /// Do not edit, this simply tells you whether or not the vsync is on. To modify this state
49 | /// use `context.setVsync(true)`
50 | vsync: bool = true,
51 | };
52 | pub const Context = struct {
53 | _updateSeconds: f32 = -1.0,
54 | allocator: std.mem.Allocator = undefined,
55 | data: Data = undefined,
56 | settings: Settings = .{},
57 | window: ?glfw.Window = undefined,
58 | input: std.ArrayList(InputEvent) = undefined,
59 | time: TimeManager = undefined,
60 | open: bool = false,
61 |
62 | pub fn deinit(self: *Context) void {
63 | self.input.deinit();
64 | ImGuiImplementation.Shutdown();
65 | glfw.terminate();
66 | self.allocator.destroy(self);
67 | }
68 |
69 | /// Ticks forward timer, clears OpenGL, enqueues events, and dictates to imgui
70 | /// that we are starting a new frame. Basically
71 | pub fn beginFrame(self: *Context) void {
72 | self.open = !self.window.?.shouldClose();
73 | self.time.tick();
74 |
75 | glfw.pollEvents();
76 | gl.glClear(gl.GL_COLOR_BUFFER_BIT);
77 | ig.igNewFrame();
78 | }
79 | /// Draws all the imgui commands, and flips the buffer to display the drawn image
80 | pub fn endFrame(self: *Context) void {
81 | const io = ig.igGetIO();
82 | // Render
83 | if (self.settings.imguiActive) {
84 | ig.igRender();
85 | const dd = ig.igGetDrawData();
86 | ImGuiImplementation.RenderDrawData(dd);
87 | } else {
88 | ig.igEndFrame();
89 | }
90 |
91 | self.input.items.len = 0;
92 |
93 | self.window.?.swapBuffers();
94 | io.*.DeltaTime = self.time.dt;
95 | if (self.settings.energySaving) {
96 | if (self._updateSeconds > 0.0) {
97 | self._updateSeconds -= self.time.dt;
98 | } else {
99 | glfw.waitEvents();
100 | }
101 | }
102 | }
103 | /// Changes the context window's position. In some operating systems I believe this is
104 | /// a no-op
105 | pub fn setWindowPosition(self: *Context, x: c_int, y: c_int) void {
106 | self.window.?.setPos(.{ .x = x, .y = y });
107 | }
108 | /// Changes the context window's size
109 | pub fn setWindowSize(self: *Context, width: c_int, height: c_int) void {
110 | self.window.?.setSize(.{ .width = @intCast(width), .height = @intCast(height) });
111 | }
112 | /// Changes the context window's title(the text on the window's title bar, typically)
113 | pub fn setWindowTitle(self: *Context, string: [*:0]const u8) void {
114 | self.window.?.setTitle(string);
115 | }
116 | /// If true, buffer swaps will happen each possible frame in line with your monitor hz,
117 | /// but if false, will buffer swap as fast as it can.
118 | pub fn setVsync(self: *Context, on: bool) void {
119 | if (on) {
120 | glfw.swapInterval(1);
121 | } else {
122 | glfw.swapInterval(0);
123 | }
124 | self.settings.vsync = on;
125 | }
126 | /// Give a path to a .png for the window icon. Note this does not affect the
127 | /// binary icon, and you would rather want to use post builds specific to
128 | /// each platform.
129 | pub fn setWindowIcon(self: *Context, path: []const u8) void {
130 | _ = self;
131 | _ = path;
132 | std.debug.print("ERROR: Loading icons not implemented\n", .{});
133 | }
134 | /// Tells ZT to close the window
135 | pub fn close(self: *Context) void {
136 | self.window.?.setShouldClose(true);
137 | }
138 | /// If you have an animation that needs to play you can queue an amount of seconds that will ignore energy saving mode
139 | pub fn setAnimationFrames(self: *Context, seconds: f32) void {
140 | self._updateSeconds = seconds;
141 | }
142 | /// If you are using multithreading or anything that will require the UI to update from outside, this can force a redraw,
143 | /// if you are using energy saving mode
144 | pub fn forceUpdate(self: *Context) void {
145 | _ = self;
146 | glfw.glfwPostEmptyEvent();
147 | }
148 | pub fn addFont(self: *Context, path: []const u8, pxSize: f32) *ig.ImFont {
149 | const io = ig.igGetIO();
150 | const newFont: *ig.ImFont = ig.ImFontAtlas_AddFontFromFileTTF(io.*.Fonts, path.ptr, pxSize, null, ig.ImFontAtlas_GetGlyphRangesDefault(io.*.Fonts));
151 | self.rebuildFont();
152 | return newFont;
153 | }
154 | /// This will destroy and rebuild the font texture used for imgui.
155 | /// This is also called for you when you add and remove fonts
156 | pub fn rebuildFont(self: *Context) void {
157 | _ = self;
158 | const io = ig.igGetIO();
159 | // Delete the old texture
160 | const texId = @as(c_uint, @intCast(@intFromPtr(io.*.Fonts.*.TexID)));
161 | gl.glDeleteTextures(1, &texId);
162 |
163 | // Generate new texture
164 | var newTexId: c_uint = 0;
165 | var pixels: [*c]u8 = undefined;
166 | var fontTexWidth: i32 = undefined;
167 | var fontTexHeight: i32 = undefined;
168 | ig.ImFontAtlas_GetTexDataAsRGBA32(io.*.Fonts, &pixels, &fontTexWidth, &fontTexHeight, null);
169 |
170 | // Upload texture to graphics system
171 | gl.glGenTextures(1, &newTexId);
172 | gl.glBindTexture(gl.GL_TEXTURE_2D, newTexId);
173 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR);
174 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR);
175 | if (@hasDecl(gl, "GL_UNPACK_ROW_LENGTH"))
176 | gl.glPixelStorei(gl.GL_UNPACK_ROW_LENGTH, 0);
177 | gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, fontTexWidth, fontTexHeight, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pixels);
178 | // Store our identifier
179 | io.*.Fonts.*.TexID = @as(*anyopaque, @ptrFromInt(newTexId));
180 | ImGuiImplementation.g_FontTexture = newTexId;
181 | }
182 | };
183 |
184 | /// If you need glfw to init before starting, use this to init glfw and
185 | /// other libraries without actually spinning up the context
186 | pub fn preInit() !void {
187 | if (!initialized) {
188 | glfw.setErrorCallback(errorCallback);
189 |
190 | if (!glfw.init(.{})) {
191 | std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
192 | std.process.exit(1);
193 | }
194 |
195 | initialized = true;
196 | }
197 | }
198 |
199 | pub fn begin(applicationAllocator: std.mem.Allocator) !*Context {
200 | return beginWithData(applicationAllocator, .{});
201 | }
202 | /// Starts the entire application! Returns errors if anything along the pipeline fails.
203 | pub fn beginWithData(applicationAllocator: std.mem.Allocator, data: Data) !*Context {
204 | var self: *Context = try applicationAllocator.create(Context);
205 | self.* = .{};
206 | self.data = data;
207 | self.allocator = applicationAllocator;
208 | self.input = std.ArrayList(InputEvent).init(applicationAllocator);
209 | try preInit();
210 |
211 | // Create our window
212 |
213 | const hints = glfw.Window.Hints{ .context_version_major = 3, .context_version_minor = 3, .opengl_profile = glfw.Window.Hints.OpenGLProfile.opengl_core_profile };
214 |
215 | self.window = glfw.Window.create(800, 600, "Hello, mach-glfw!", null, null, hints) orelse {
216 | std.log.err("failed to create GLFW window: {?s}", .{glfw.getErrorString()});
217 | std.process.exit(1);
218 | };
219 |
220 | self.open = true;
221 | glfw.makeContextCurrent(self.window);
222 |
223 | if (gl.gladLoadGL() < 0) {
224 | return error.GladLoadError;
225 | }
226 |
227 | ImGuiImplementation.init("#version 330");
228 |
229 | self.window.?.setUserPointer(self);
230 | _ = self.window.?.setFramebufferSizeCallback(windowSizeChanged);
231 | _ = self.window.?.setKeyCallback(inputCallback);
232 | _ = self.window.?.setCharCallback(charCallback);
233 | _ = self.window.?.setMouseButtonCallback(mousebuttonCallback);
234 | _ = self.window.?.setCursorPosCallback(cursorCallback);
235 | _ = self.window.?.setScrollCallback(mouseWheelCallback);
236 | _ = self.window.?.setMaximizeCallback(windowMaximizeChanged);
237 |
238 | gl.glClearColor(0.1, 0.1, 0.12, 1.0);
239 | const size = self.window.?.getSize();
240 | gl.glViewport(0, 0, @as(c_int, @intCast(size.width)), @as(c_int, @intCast(size.height)));
241 | const io = ig.igGetIO();
242 | io.*.ConfigFlags |= ig.ImGuiConfigFlags_DockingEnable;
243 | io.*.DisplaySize = .{ .x = @as(f32, @floatFromInt(size.width)), .y = @as(f32, @floatFromInt(size.height)) };
244 | self.time = TimeManager.init();
245 | return self;
246 | }
247 |
248 | // Callbacks
249 | fn windowSizeChanged(win: glfw.Window, newWidth: u32, newHeight: u32) void {
250 | _ = win;
251 | gl.glViewport(0, 0, @as(c_int, @intCast(newWidth)), @as(c_int, @intCast(newHeight)));
252 | const io = ig.igGetIO();
253 | io.*.DisplaySize = .{ .x = @as(f32, @floatFromInt(newWidth)), .y = @as(f32, @floatFromInt(newHeight)) };
254 | }
255 | fn windowMaximizeChanged(win: glfw.Window, maximized: bool) void {
256 | _ = maximized;
257 | const size = win.getSize();
258 | gl.glViewport(0, 0, @as(c_int, @intCast(size.width)), @as(c_int, @intCast(size.height)));
259 | const io = ig.igGetIO();
260 | io.*.DisplaySize = .{ .x = @as(f32, @floatFromInt(size.width)), .y = @as(f32, @floatFromInt(size.height)) };
261 | }
262 | fn inputCallback(win: glfw.Window, key: glfw.Key, scan: i32, action: glfw.Action, mods: glfw.Mods) void {
263 | _ = mods;
264 | const context: *Context = win.getUserPointer(Context).?;
265 | const io = ig.igGetIO();
266 | var pressed = false;
267 | if (action == glfw.Action.press or action == glfw.Action.repeat) {
268 | pressed = true;
269 | }
270 | io.*.KeysDown[@as(usize, @intCast(@intFromEnum(key)))] = pressed;
271 | io.*.KeyShift = io.*.KeysDown[@intFromEnum(glfw.Key.left_shift)] or io.*.KeysDown[@intFromEnum(glfw.Key.right_shift)];
272 | io.*.KeyCtrl = io.*.KeysDown[@intFromEnum(glfw.Key.left_control)] or io.*.KeysDown[@intFromEnum(glfw.Key.right_control)];
273 | io.*.KeyAlt = io.*.KeysDown[@intFromEnum(glfw.Key.left_alt)] or io.*.KeysDown[@intFromEnum(glfw.Key.right_alt)];
274 | context.input.append(.{
275 | .keyboard = .{
276 | .key = @intFromEnum(key),
277 | .scan = scan,
278 | .action = @intFromEnum(action),
279 | .mods = 0, // modsToInt(mods)
280 | },
281 | }) catch unreachable;
282 | }
283 | fn mouseWheelCallback(win: glfw.Window, x: f64, y: f64) void {
284 | const context: *Context = win.getUserPointer(Context).?;
285 | const io = ig.igGetIO();
286 | io.*.MouseWheel = @as(f32, @floatCast(y));
287 | io.*.MouseWheelH = @as(f32, @floatCast(x));
288 | context.input.append(.{ .mouseWheel = .{
289 | .x = x,
290 | .y = y,
291 | } }) catch unreachable;
292 | }
293 | fn mousebuttonCallback(win: glfw.Window, key: glfw.MouseButton, action: glfw.Action, mods: glfw.Mods) void {
294 | _ = mods;
295 | const context: *Context = win.getUserPointer(Context).?;
296 | const io = ig.igGetIO();
297 | var pressed = false;
298 | if (action == glfw.Action.press or action == glfw.Action.repeat) {
299 | pressed = true;
300 | }
301 | switch (key) {
302 | glfw.MouseButton.left => {
303 | io.*.MouseDown[ig.ImGuiMouseButton_Left] = pressed;
304 | },
305 | glfw.MouseButton.middle => {
306 | io.*.MouseDown[ig.ImGuiMouseButton_Middle] = pressed;
307 | },
308 | glfw.MouseButton.right => {
309 | io.*.MouseDown[ig.ImGuiMouseButton_Right] = pressed;
310 | },
311 | else => {},
312 | }
313 | context.input.append(.{
314 | .mouseButton = .{
315 | .key = @intFromEnum(key),
316 | .action = @intFromEnum(action),
317 | .mods = 0, // modsToInt(mods)
318 | },
319 | }) catch unreachable;
320 | }
321 | fn cursorCallback(win: glfw.Window, x: f64, y: f64) void {
322 | const context: *Context = win.getUserPointer(Context).?;
323 | const io = ig.igGetIO();
324 | io.*.MousePos = .{ .x = @as(f32, @floatCast(x)), .y = @as(f32, @floatCast(y)) };
325 | context.input.append(.{ .mousePosition = .{
326 | .x = x,
327 | .y = y,
328 | } }) catch unreachable;
329 | }
330 | fn charCallback(win: glfw.Window, char: u21) void {
331 | const context: *Context = win.getUserPointer(Context).?;
332 | const io = ig.igGetIO();
333 | ig.ImGuiIO_AddInputCharacter(io, char);
334 | context.input.append(.{ .character = .{
335 | .value = char,
336 | } }) catch unreachable;
337 | }
338 | };
339 | }
340 |
--------------------------------------------------------------------------------
/src/zt/spatialHash.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const math = @import("imgui").zlm;
3 |
4 | inline fn fabs(v: anytype) @TypeOf(v) {
5 | return if (v < 0) -v else v; // TODO: only way to support zig11 vs nightly @fabs vs fabs(v)
6 | }
7 |
8 | // @TODO: More settings to streamline spatial hash usage for other purposes. Maybe even
9 | // make it so you can provide your own coordinate type and functions?
10 | pub const SpatialHashSettings = struct {
11 | /// The height and width of each bucket inside the hash.
12 | bucketSize: f32 = 256,
13 | };
14 | pub fn Generate(comptime T: type, comptime spatialSettings: SpatialHashSettings) type {
15 | // const VisType: type = if (spatialSettings.visualizable) SpatialVisualization else void;
16 | return struct {
17 | const context = struct {
18 | pub fn hash(self: @This(), value: math.Vec2) u64 {
19 | _ = self;
20 | return std.hash.Wyhash.hash(438193475, &std.mem.toBytes(value));
21 | }
22 | pub fn eql(self: @This(), lhs: math.Vec2, rhs: math.Vec2) bool {
23 | _ = self;
24 | return lhs.x == rhs.x and lhs.y == rhs.y;
25 | }
26 | };
27 | const Self = @This();
28 | /// Some basic settings about the spatial hash, as given at type generation.
29 | pub const settings = spatialSettings;
30 | /// This is the inverse of the bucket size, the formula will
31 | /// result in the 'hash' that locates the buckets in this spatial hash.
32 | pub const cellInverse: f32 = 1.0 / spatialSettings.bucketSize;
33 | /// A Bucket contains all the targets inside of an imaginary cell generated by the spatial hash.
34 | pub const Bucket = std.AutoArrayHashMap(T, void);
35 | /// The HashType defines what
36 | pub const HashType = std.HashMap(math.Vec2, Bucket, context, 80);
37 |
38 | allocator: std.mem.Allocator,
39 | /// A HashMap of (Vec2 -> Bucket) to contain all the buckets as new ones appear.
40 | hashBins: HashType,
41 | /// This is a temporary holding bucket of every target inside of a query. This is used for each query
42 | /// and as such modifying the spatial hash, or starting a new query will change this bucket.
43 | holding: Bucket,
44 |
45 | /// Creates a spatial hash instance and allocates memory for the bucket structures.
46 | pub fn init(allocator: std.mem.Allocator) Self {
47 | return .{
48 | .allocator = allocator,
49 | .hashBins = HashType.init(allocator),
50 | .holding = Bucket.init(allocator),
51 | // .visualization = if (spatialSettings.visualizable) .{} else {},
52 | };
53 | }
54 | /// Deallocates all the memory associated with this spatial hash. Note if T is not a pointer,
55 | /// then this will result in the loss of data.
56 | pub fn deinit(self: *Self) void {
57 | var iterator = self.hashBins.iterator();
58 | while (iterator.next()) |bin| {
59 | bin.value_ptr.deinit();
60 | }
61 | self.holding.deinit();
62 | self.hashBins.deinit();
63 | }
64 |
65 | // === ADDS ===
66 |
67 | /// Adds the target to the spatial hash, into every bucket that it spans.
68 | pub fn addAABB(self: *Self, target: T, position: math.Vec2, size: math.Vec2) void {
69 | const start = vecToIndex(position).add(.{ .x = settings.bucketSize * 0.5, .y = settings.bucketSize * 0.5 });
70 | const stop = vecToIndex(position.add(size)).add(.{ .x = settings.bucketSize * 0.5, .y = settings.bucketSize * 0.5 });
71 | var current = start;
72 |
73 | while (current.x <= stop.x) {
74 | while (current.y <= stop.y) {
75 | var bin = self.getBin(current);
76 | bin.put(target, {}) catch unreachable;
77 | current.y += settings.bucketSize;
78 | }
79 | current.y = start.y;
80 | current.x += settings.bucketSize;
81 | }
82 | }
83 | /// Adds the target to the spatial hash, into one single bucket.
84 | pub fn addPoint(self: *Self, target: T, position: math.Vec2) void {
85 | var result = self.getBin(position);
86 | result.put(target, {}) catch unreachable;
87 | }
88 |
89 | // === REMOVALS ===
90 |
91 | /// Removes the target from the spatial hash buckets that it spans. Make sure to provide
92 | /// the same coordinates that it was added with.
93 | pub fn removeAABB(self: *Self, target: T, position: math.Vec2, size: math.Vec2) void {
94 | const stop = position.add(size);
95 | var current = position;
96 |
97 | while (current.x <= stop.x) : (current.x += settings.bucketSize) {
98 | while (current.y <= stop.y) : (current.y += settings.bucketSize) {
99 | var bin = self.getBin(current);
100 | _ = bin.swapRemove(target);
101 | }
102 | }
103 | }
104 | /// Removes the target from the spatial hash's singular bucket. Make sure to provide
105 | /// the same coordinate that it was added with.
106 | pub fn removePoint(self: *Self, target: T, position: math.Vec2) void {
107 | const result = self.getBin(position);
108 | _ = result.swapRemove(target);
109 | }
110 |
111 | // === QUERIES ===
112 |
113 | /// Returns an array of each T inside of the given rectangle.
114 | /// Note that broad phase physics like this is not accurate, instead this opts to return in general
115 | /// what *could* be a possible collision.
116 | pub fn queryAABB(self: *Self, position: math.Vec2, size: math.Vec2) []T {
117 | self.holding.unmanaged.clearRetainingCapacity();
118 | const start = vecToIndex(position).add(.{ .x = settings.bucketSize * 0.5, .y = settings.bucketSize * 0.5 });
119 | const stop = vecToIndex(position.add(size)).add(.{ .x = settings.bucketSize * 0.5, .y = settings.bucketSize * 0.5 });
120 | var current = start;
121 |
122 | while (current.x <= stop.x) {
123 | while (current.y <= stop.y) {
124 | var bin = self.getBin(current);
125 | for (bin.keys()) |value| {
126 | self.holding.put(value, {}) catch unreachable;
127 | }
128 | current.y += settings.bucketSize;
129 | }
130 | current.y = start.y;
131 | current.x += settings.bucketSize;
132 | }
133 | return self.holding.keys();
134 | }
135 | /// Returns an array of each T inside of the given point's bucket.
136 | /// Note that broad phase physics like this is not accurate, instead this opts to return in general
137 | /// what *could* be a possible collision.
138 | pub fn queryPoint(self: *Self, point: math.Vec2) []T {
139 | self.holding.unmanaged.clearRetainingCapacity();
140 | const bin = self.getBin(point);
141 | for (bin.keys()) |value| {
142 | self.holding.put(value, {}) catch unreachable;
143 | }
144 | return self.holding.keys();
145 | }
146 |
147 | inline fn queryLineLow(self: *Self, queryStart: math.Vec2, queryEnd: math.Vec2) void {
148 | var delta = queryEnd.sub(queryStart);
149 | var yi = settings.bucketSize;
150 | var current = queryStart;
151 |
152 | if (delta.y < 0) {
153 | yi = -settings.bucketSize;
154 | delta.y = -delta.y;
155 | }
156 |
157 | var D = (2 * delta.y) - delta.x;
158 |
159 | while (current.x < queryEnd.x) {
160 | // Plot:
161 | var bin = self.getBin(current);
162 | for (bin.keys()) |value| {
163 | self.holding.put(value, {}) catch unreachable;
164 | }
165 |
166 | if (D > 0) {
167 | current.y = current.y + yi;
168 | D = D + (2 * (delta.y - delta.x));
169 | } else {
170 | D = D + 2 * delta.y;
171 | }
172 |
173 | current.x += settings.bucketSize;
174 | }
175 | }
176 | inline fn queryLineHigh(self: *Self, queryStart: math.Vec2, queryEnd: math.Vec2) void {
177 | var delta = queryEnd.sub(queryStart);
178 | var xi = settings.bucketSize;
179 | var current = queryStart;
180 |
181 | if (delta.x < 0) {
182 | xi = -settings.bucketSize;
183 | delta.x = -delta.x;
184 | }
185 |
186 | var D = (2 * delta.x) - delta.y;
187 |
188 | while (current.y < queryEnd.y) {
189 | // Plot:
190 | var bin = self.getBin(current);
191 | for (bin.keys()) |value| {
192 | self.holding.put(value, {}) catch unreachable;
193 | }
194 |
195 | if (D > 0) {
196 | current.x = current.x + xi;
197 | D = D + (2 * (delta.x - delta.y));
198 | } else {
199 | D = D + 2 * delta.x;
200 | }
201 |
202 | current.y += settings.bucketSize;
203 | }
204 | }
205 | /// Returns an array of each T inside every bucket along this line's path.
206 | /// Note that broad phase physics like this is not accurate, instead this opts to return in general
207 | /// what *could* be a possible collision.
208 | pub fn queryLine(self: *Self, queryStart: math.Vec2, queryEnd: math.Vec2) []T {
209 | self.holding.unmanaged.clearRetainingCapacity();
210 |
211 | // Had some edge issues with some quadrants not including start/end.
212 | {
213 | const bin = self.getBin(queryStart);
214 | for (bin.keys()) |value| {
215 | self.holding.put(value, {}) catch unreachable;
216 | }
217 | }
218 | {
219 | const bin = self.getBin(queryEnd);
220 | for (bin.keys()) |value| {
221 | self.holding.put(value, {}) catch unreachable;
222 | }
223 | }
224 |
225 | if (fabs(queryEnd.y - queryStart.y) < fabs(queryEnd.x - queryStart.x)) {
226 | if (queryStart.x > queryEnd.x) {
227 | self.queryLineLow(queryEnd, queryStart);
228 | } else {
229 | self.queryLineLow(queryStart, queryEnd);
230 | }
231 | } else {
232 | if (queryStart.y > queryEnd.y) {
233 | self.queryLineHigh(queryEnd, queryStart);
234 | } else {
235 | self.queryLineHigh(queryStart, queryEnd);
236 | }
237 | }
238 |
239 | return self.holding.keys();
240 | }
241 |
242 | inline fn getBin(self: *Self, position: math.Vec2) *Bucket {
243 | const hash = vecToIndex(position);
244 | const result = self.hashBins.getOrPut(hash) catch unreachable;
245 | if (!result.found_existing) {
246 | result.value_ptr.* = Bucket.init(self.allocator);
247 | }
248 | return result.value_ptr;
249 | }
250 | inline fn vecToIndex(vec: math.Vec2) math.Vec2 {
251 | return .{ .x = floatToIndex(vec.x), .y = floatToIndex(vec.y) };
252 | }
253 | inline fn floatToIndex(float: f32) f32 {
254 | return (@floor(float * cellInverse)) / cellInverse;
255 | }
256 | };
257 | }
258 |
259 | test "speed testing spatial hash" {
260 | var gpa = std.heap.GeneralPurposeAllocator(.{}){};
261 | std.debug.print("\n> Spatial hash Speedtest with GPA Allocator:\n", .{});
262 |
263 | var hash = Generate(usize, .{ .bucketSize = 50 }).init(&gpa.allocator);
264 | defer hash.deinit();
265 |
266 | var rand = std.rand.DefaultPrng.init(3741837483).random;
267 | var clock = std.time.Timer.start() catch unreachable;
268 | _ = clock.lap();
269 | var i: usize = 0;
270 | while (i < 10000) : (i += 1) {
271 | const randX = rand.float(f32) * 200;
272 | const randY = rand.float(f32) * 200;
273 | hash.addPoint(i, math.vec2(randX, randY));
274 | }
275 | var time = clock.lap();
276 | std.debug.print(">> Took {d:.2}ms to create 10,000 points on a hash of usize.\n", .{@as(f64, @floatFromInt(time)) / 1000000.0});
277 |
278 | while (i < 20000) : (i += 1) {
279 | const randX = rand.float(f32) * 200;
280 | const randY = rand.float(f32) * 200;
281 | hash.addPoint(i, math.vec2(randX, randY));
282 | }
283 | time = clock.lap();
284 | std.debug.print(">> Took {d:.2}ms to create 10,000 more points on a hash of usize.\n", .{@as(f64, @floatFromInt(time)) / 1000000.0});
285 |
286 | i = 0;
287 | var visited: i32 = 0;
288 | while (i < 200) : (i += 1) {
289 | for (hash.queryPoint(.{ .x = rand.float(f32) * 200, .y = rand.float(f32) * 200 })) |_| {
290 | visited += 1;
291 | }
292 | }
293 | time = clock.lap();
294 | std.debug.print(">> Took {d:.2}ms to point iterate over a bucket 200 times, and visited {any} items.\n", .{ @as(f64, @floatFromInt(time)) / 1000000.0, visited });
295 | }
296 |
297 | test "spatial point insertion/remove/query" {
298 | const assert = @import("std").debug.assert;
299 |
300 | const hash = Generate(i32, .{ .bucketSize = 64 }).init(std.testing.allocator);
301 | defer hash.deinit();
302 |
303 | hash.addPoint(40, .{ .x = 20, .y = 20 });
304 | hash.addPoint(80, .{ .x = 100, .y = 100 });
305 |
306 | {
307 | const data = hash.queryPoint(.{ .x = 10, .y = 10 });
308 | assert(data.len == 1);
309 | assert(data[0] == 40);
310 | }
311 | {
312 | hash.addPoint(100, .{ .x = 40, .y = 40 });
313 | const data = hash.queryPoint(.{ .x = 10, .y = 10 });
314 | assert(data[0] == 40);
315 | assert(data[1] == 100);
316 | assert(data.len == 2);
317 | }
318 | {
319 | hash.removePoint(100, .{ .x = 40, .y = 40 });
320 | const data = hash.queryPoint(.{ .x = 10, .y = 10 });
321 | assert(data[0] == 40);
322 | assert(data.len == 1);
323 | }
324 | }
325 |
326 | test "spatial rect insertion/remove/query" {
327 | const assert = @import("std").debug.assert;
328 | const hash = Generate(i32, .{ .bucketSize = 100 }).init(std.testing.allocator);
329 | defer hash.deinit();
330 |
331 | hash.addAABB(1, math.vec2(50, 50), math.vec2(100, 100));
332 | {
333 | const data = hash.queryAABB(math.vec2(0, 0), math.vec2(150, 150));
334 | assert(data.len == 1);
335 | }
336 |
337 | hash.addAABB(2, math.vec2(150, 150), math.vec2(100, 100));
338 | {
339 | const data = hash.queryAABB(math.vec2(0, 0), math.vec2(100, 100));
340 | assert(data.len == 2);
341 | }
342 |
343 | hash.removeAABB(2, math.vec2(150, 150), math.vec2(100, 100));
344 | {
345 | const data = hash.queryAABB(math.vec2(0, 0), math.vec2(100, 100));
346 | assert(data.len == 1);
347 | }
348 | }
349 | test "spatial line query" {
350 | const assert = @import("std").debug.assert;
351 | var hash = Generate(i32, .{ .bucketSize = 100 }).init(std.testing.allocator);
352 | defer hash.deinit();
353 |
354 | // formation like
355 | // * * *
356 | //
357 | //
358 | // * *
359 | //
360 | //
361 | //
362 | //
363 | //
364 | //
365 | // *
366 |
367 | hash.addPoint(1, math.vec2(20, 20));
368 | hash.addPoint(2, math.vec2(350, 350));
369 | hash.addPoint(3, math.vec2(350, 20));
370 | hash.addPoint(4, math.vec2(20, 350));
371 | hash.addPoint(5, math.vec2(20, 3500));
372 | hash.addPoint(6, math.vec2(3500, 20));
373 | {
374 | // horizontal, should have 2.
375 | var data = hash.queryLine(math.vec2(20, 20), math.vec2(520, 20));
376 | assert(data.len == 2);
377 | // diagonal, should have 2.
378 | data = hash.queryLine(math.vec2(0, 0), math.vec2(400, 400));
379 | assert(data.len == 2);
380 | // Reverse diagonal, should have 2.
381 | data = hash.queryLine(math.vec2(400, 400), math.vec2(0, 0));
382 | assert(data.len == 2);
383 | // vertical, also 2.
384 | data = hash.queryLine(math.vec2(20, 20), math.vec2(20, 520));
385 | assert(data.len == 2);
386 | }
387 | }
388 |
--------------------------------------------------------------------------------