├── .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 | --------------------------------------------------------------------------------