├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── docs.yml │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.zig ├── build.zig.zon ├── examples ├── blend.zig ├── bufferoffsets.zig ├── build.zig ├── clear.zig ├── cube.zig ├── debugtext-print.zig ├── debugtext-userfont.zig ├── debugtext.zig ├── instancing-compute.zig ├── instancing.zig ├── math.zig ├── mrt.zig ├── noninterleaved.zig ├── offscreen.zig ├── quad.zig ├── saudio.zig ├── sgl-context.zig ├── sgl-points.zig ├── sgl.zig ├── shaders │ ├── blend.glsl │ ├── blend.glsl.zig │ ├── bufferoffsets.glsl │ ├── bufferoffsets.glsl.zig │ ├── cube.glsl │ ├── cube.glsl.zig │ ├── instancing-compute.glsl │ ├── instancing-compute.glsl.zig │ ├── instancing.glsl │ ├── instancing.glsl.zig │ ├── mrt.glsl │ ├── mrt.glsl.zig │ ├── noninterleaved.glsl │ ├── noninterleaved.glsl.zig │ ├── offscreen.glsl │ ├── offscreen.glsl.zig │ ├── quad.glsl │ ├── quad.glsl.zig │ ├── shapes.glsl │ ├── shapes.glsl.zig │ ├── texcube.glsl │ ├── texcube.glsl.zig │ ├── triangle.glsl │ ├── triangle.glsl.zig │ ├── vertexpull.glsl │ └── vertexpull.glsl.zig ├── shapes.zig ├── texcube.zig ├── triangle.zig └── vertexpull.zig ├── src └── sokol │ ├── app.zig │ ├── audio.zig │ ├── c │ ├── sokol_app.c │ ├── sokol_app.h │ ├── sokol_audio.c │ ├── sokol_audio.h │ ├── sokol_debugtext.c │ ├── sokol_debugtext.h │ ├── sokol_defines.h │ ├── sokol_fetch.c │ ├── sokol_fetch.h │ ├── sokol_gfx.c │ ├── sokol_gfx.h │ ├── sokol_gl.c │ ├── sokol_gl.h │ ├── sokol_glue.c │ ├── sokol_glue.h │ ├── sokol_imgui.c │ ├── sokol_imgui.h │ ├── sokol_log.c │ ├── sokol_log.h │ ├── sokol_shape.c │ ├── sokol_shape.h │ ├── sokol_time.c │ └── sokol_time.h │ ├── debugtext.zig │ ├── fetch.zig │ ├── gfx.zig │ ├── gl.zig │ ├── glue.zig │ ├── imgui.zig │ ├── log.zig │ ├── shape.zig │ ├── sokol.zig │ ├── time.zig │ └── web │ └── shell.html └── tools └── fixdoctar.zig /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | [**.zig] 3 | end_of_line=lf 4 | indent_size=4 5 | [**.yml] 6 | indent_size=2 7 | [**] 8 | indent_style=space 9 | trim_trailing_whitespace=true 10 | insert_final_newline=true 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.zig text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@main 11 | - uses: goto-bus-stop/setup-zig@v2 12 | - name: gen-docs 13 | run: zig build docs 14 | - name: upload-artifact 15 | uses: actions/upload-artifact@main 16 | with: 17 | name: sokol-zig-docs 18 | path: zig-out/docs 19 | deploy: 20 | needs: build 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@main 24 | with: 25 | repository: floooh/sokol-zig-docs 26 | ssh-key: ${{secrets.SOKOL_ZIG_DOCS_DEPLOY}} 27 | - uses: actions/download-artifact@main 28 | with: 29 | name: sokol-zig-docs 30 | - name: "commit and push" 31 | run: | 32 | git config user.email "none" 33 | git config user.name "GH Action" 34 | git add . 35 | git diff-index --quiet HEAD || git commit -m "updated (${{ github.run_number }})" 36 | git push 37 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Zig 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, macos-latest, windows-latest] 10 | runs-on: ${{matrix.os}} 11 | steps: 12 | - uses: actions/checkout@main 13 | - uses: goto-bus-stop/setup-zig@v2 14 | - name: prepare-linux 15 | if: runner.os == 'Linux' 16 | run: | 17 | sudo apt-get update 18 | sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev 19 | - name: build Native 20 | run: zig build examples --summary all 21 | - name: build Wasm 22 | run: zig build examples --summary all -Dtarget=wasm32-emscripten 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .DS_Store 3 | zig-*/ 4 | .zig-*/ 5 | NUL 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## CHANGELOG 2 | 3 | > NOTE: this changelog is only for changes to the sokol-zig 'scaffolding', e.g. changes to build.zig, 4 | to the example code or the supported Zig version. For actual Sokol header changes, see the 5 | [sokol changelog](https://github.com/floooh/sokol/blob/master/CHANGELOG.md). 6 | 7 | ### 27-Apr-2025 8 | 9 | The sokol C library can now be built as dynamic link library via `-Ddynamic_linkage`. 10 | See PR https://github.com/floooh/sokol-zig/pull/116 for details. Many thanks to 11 | @remzisenel for the PR! 12 | 13 | ### 29-Mar-2025 14 | 15 | Added a new build option `dont_link_system_libs`. When this is provided, upstream 16 | projects need to take care of linking the correct system libraries required 17 | by the sokol headers themselves (see issue https://github.com/floooh/sokol-zig/issues/109 for details). 18 | 19 | ### 23-Mar-2025 20 | 21 | Change the sokol-shdc dependency from lazy to static. Using a lazy dependency 22 | turned out to be too much hassle to invoke the shader compiler from 23 | upstream projects (those shouldn't import their own sokol-shdc dependency 24 | because the sokol-shdc sub-dependency of the sokol-zig dependency will always 25 | point to the matching sokol-shdc version). See the [pacman.zig/build.zig](https://github.com/floooh/pacman.zig/blob/main/build.zig) 26 | for an example of how to run the shader compiler from an upstream project. 27 | 28 | Also, when building the examples, the shaders will be automatically recompiled 29 | when needed. 30 | 31 | ### 22-Mar-2025 32 | 33 | The sokol-shdc shader compiler is now integrated as a (lazy) Zig package dependency, 34 | and a build.zig.zon and build.zig (with helper code to invoke the shader compiler) 35 | has been added to the [sokol-tools-bin repository](https://github.com/floooh/sokol-tools-bin). 36 | 37 | Recompiling the example shaders is now done as part of the `examples` build step 38 | like this: 39 | 40 | ``` 41 | zig build examples -Dshaders 42 | ``` 43 | 44 | The `-Dshaders` will trigger pulling the lazy sokol-shdc binaries dependency. 45 | 46 | ### 21-Mar-2025 47 | 48 | Some build.zig and package structure cleanup: 49 | 50 | - the examples are no longer part of the build.zig.zon package 51 | - examples are no longer automatically built on `zig build`, instead 52 | use `zig build examples` 53 | - ...a minor breaking change for the Emscripten linker step: this also 54 | no longer attaches itself automatically to the `install` step, e.g. just 55 | running `zig build` will not automatically run the Emscripten linker steps. 56 | Instead use the result of `emLinkStep()` to setup the standard install dependencies 57 | yourself like this: 58 | ```zig 59 | const link_step = try sokol.emLinkStep(b, .{ ... }); 60 | b.getInstallStep().dependOn(&link_step.step); 61 | ``` 62 | Also see the updated example build.zig code in the readme. 63 | - build.zig: remove zig 0.13.x vs 0.14.x compatibility hacks 64 | 65 | ### 08-Dec-2024 66 | 67 | The sokol_imgui.h compilation is now more configurable by allowing to override 68 | the cimgui.h header path (default: `cimgui.h`) and the C API function prefix 69 | (default: `ig`), see the [sokol-zig-imgui-sample/build.zig](https://github.com/floooh/sokol-zig-imgui-sample/blob/main/build.zig) 70 | for an example. 71 | 72 | > Narrator: this didn't quite work out because the idea was to remove the prefix 73 | alltogether for the TranslateC-generated Zig bindings, but then some Dear ImGui 74 | functions collide with Win32 OS functions (Set/GetCursorPos and SetWindowPos). 75 | 76 | ### 07-Dec-2024 77 | 78 | Some build.zig.zon cleanup: 79 | 80 | - change name from `sokol-zig` to `sokol` 81 | - remove readme and changelog from `.paths` 82 | - update emsdk dependency to emsdk 3.1.73 83 | 84 | ### 31-Aug-2024 85 | 86 | Fix for a [breaking naming convention change](https://github.com/ziglang/zig/commit/0fe3fd01ddc2cd49c6a2b939577d16b9d2c65ea9) 87 | in Zig's `builtin.Type`. Since only a small code area in the bindings is affected (the `asRange` helper 88 | function) I decided to implement a fix that works both for zig 0.13.0 and the current 89 | HEAD version. 90 | 91 | E.g. if you're on Zig 0.13.0, you can safely update and if you are on the Zig HEAD 92 | version you definitely need to update. 93 | 94 | More details in PR: https://github.com/floooh/sokol/pull/1100 95 | 96 | ### 23-Aug-2024 97 | 98 | Important change for WASM/web builds: Merged PR #77, this changes the 99 | Emscripten link step option `.shell_file_path` from an absolute path string to 100 | a Zig build system `LazyPath`. This requires a small change in build.zig 101 | when creating the Emscripten link step via `emLinkStep()`. See the 102 | updated code example in the readme for details (just remove a `.getPath(b)`). 103 | 104 | ### 03-Jun-2024 105 | 106 | - the Emscripten SDK dependency has been updated to 3.1.61 107 | - the Emscripten specific parts of build.zig have been updated to be more 108 | 'idiomatic' now. This also has the nice side effect that the dirty check for 109 | the Emscripten linker step now works as expected (e.g. the step will do 110 | nothing if the output is uptodate) 111 | 112 | ### 01-Jun-2024 113 | 114 | - added bindings for sokol_imgui.h (please read the section `## Dear ImGui support` 115 | in the readme, and also check out this [example project](https://github.com/floooh/sokol-zig-imgui-sample)) 116 | - the sokol C library name has been renamed from `sokol` to `sokol_clib`, and 117 | is now exposed to the outside world via `installArtifact()` (this allows a user of 118 | the sokol dependency to lookup the CompileStep for the sokol C library via 119 | `dep_sokol.artifact("sokol_clib"))` which is important to inject a cimgui 120 | header search path (e.g. via `dep_sokol.artifact("sokol_clib").addIncludePath(cimgui_root);`) 121 | 122 | ### 20-Apr-2024 123 | 124 | - update the emsdk dependency to 3.1.57 125 | - some minor build.zig code cleanup 126 | - test with Zig 0.12.0 release 127 | 128 | ### 29-Feb-2024 129 | 130 | **BREAKING CHANGES** 131 | 132 | - The examples have been updated for the 'render pass cleanup' in sokol-gfx, please 133 | see the [sokol changelog](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) 134 | for details! 135 | 136 | ### 17-Jan-2024 137 | 138 | - Switched the master branch to support the Zig nightly versions, this is different from before 139 | where the master branch worked against the last stable Zig version. Previous stable Zig versions 140 | will be supported in 'archival branches' which remain frozen in time. 141 | - Fixed the build.zig for the latest API changes in zig-0.12.0 and also did a general code cleanup. 142 | - Switched over to use sokol-zig exclusively as package via the Zig package manager. The old 143 | way of integrating the bindings as git submodule is no longer supported. 144 | - Integrate with the Emscripten SDK which enables straightforward support for building 145 | Zig WebGL/WebGPU applications that run in web browsers (see README for details). 146 | The way the Emscripten SDK is integrated isn't the 'final form' form though. Eventually 147 | I want to move the Emscripten stuff into a separate `emsdk-zig` package, and rewrite the 148 | linker integration to be more 'Zig build system idiomatic'. 149 | 150 | Most of the work was done by @kassane, many thanks! 151 | 152 | Relevant PRs: https://github.com/floooh/sokol-zig/pull/50, https://github.com/floooh/sokol-zig/pull/51. 153 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | zlib/libpng license 2 | 3 | Copyright (c) 2020 Andre Weissflog 4 | 5 | This software is provided 'as-is', without any express or implied warranty. 6 | In no event will the authors be held liable for any damages arising from the 7 | use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software in a 15 | product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 18 | 2. Altered source versions must be plainly marked as such, and must not 19 | be misrepresented as being the original software. 20 | 21 | 3. This notice may not be removed or altered from any source 22 | distribution. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml)[![Docs](https://github.com/floooh/sokol-zig/actions/workflows/docs.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/docs.yml) 2 | 3 | Auto-generated Zig bindings for the [sokol headers](https://github.com/floooh/sokol). 4 | 5 | [Auto-generated docs](https://floooh.github.io/sokol-zig-docs) (wip) 6 | 7 | For Zig version 0.14.0+ 8 | 9 | In case of breaking changes in Zig, the bindings might fall behind. Please don't hesitate to 10 | ping me via a Github issue, or even better, provide a PR :) 11 | 12 | Support for stable Zig versions is in branches (e.g. `zig-0.12.0`), those versions are 'frozen in time' though. 13 | 14 | Related projects: 15 | 16 | - [pacman.zig](https://github.com/floooh/pacman.zig) 17 | - [chipz emulators](https://github.com/floooh/chipz) 18 | - [Dear ImGui sample project](https://github.com/floooh/sokol-zig-imgui-sample) 19 | 20 | ## Building the samples 21 | 22 | Supported platforms are: Windows, macOS, Linux (with X11) and web 23 | 24 | On Linux install the following packages: libglu1-mesa-dev, mesa-common-dev, xorg-dev, libasound-dev 25 | (or generally: the dev packages required for X11, GL and ALSA development) 26 | 27 | To build the platform-native samples: 28 | 29 | ```sh 30 | # build all examples: 31 | zig build examples 32 | # build and run individual examples 33 | zig build run-clear 34 | zig build run-triangle 35 | zig build run-quad 36 | zig build run-bufferoffsets 37 | zig build run-cube 38 | zig build run-noninterleaved 39 | zig build run-texcube 40 | zig build run-offscreen 41 | zig build run-instancing 42 | zig build run-mrt 43 | zig build run-saudio 44 | zig build run-sgl 45 | zig build run-sgl-context 46 | zig build run-sgl-points 47 | zig build run-debugtext 48 | zig build run-debugtext-print 49 | zig build run-debugtext-userfont 50 | zig build run-shapes 51 | ``` 52 | 53 | (also run ```zig build -l``` to get a list of build targets) 54 | 55 | By default, the backend 3D API will be selected based on the target platform: 56 | 57 | - macOS: Metal 58 | - Windows: D3D11 59 | - Linux: GL 60 | 61 | To force the GL backend on macOS or Windows, build with ```-Dgl=true```: 62 | 63 | ``` 64 | > zig build -Dgl=true run-clear 65 | ``` 66 | 67 | The ```clear``` sample prints the selected backend to the terminal: 68 | 69 | ``` 70 | sokol-zig ➤ zig build -Dgl=true run-clear 71 | Backend: .sokol.gfx.Backend.GLCORE33 72 | ``` 73 | 74 | For the web-samples, run: 75 | 76 | ```sh 77 | zig build examples -Dtarget=wasm32-emscripten 78 | # or to build and run one of the samples 79 | zig build run-clear -Dtarget=wasm32-emscripten 80 | ... 81 | ``` 82 | 83 | When building with target `wasm32-emscripten` for the first time, the build script will 84 | install and activate the Emscripten SDK into the Zig package cache for the latest SDK 85 | version. There is currently no build system functionality to update or delete the Emscripten SDK 86 | after this first install. The current workaround is to delete the global Zig cache 87 | (run `zig env` to see where the Zig cache resides). 88 | 89 | Improving the Emscripten SDK integration with the Zig build system is planned for the future. 90 | 91 | 92 | ## How to integrate sokol-zig into your project 93 | 94 | Add a build.zig.zon file to your project which has at least a `.sokol` dependency: 95 | 96 | ```zig 97 | .{ 98 | .name = "my_project", 99 | .version = "0.1.0", 100 | .paths = .{ 101 | "src", 102 | "build.zig", 103 | "build.zig.zon", 104 | }, 105 | .dependencies = .{ 106 | .sokol = .{ 107 | .url = "git+https://github.com/floooh/sokol-zig.git#[commit-hash]", 108 | .hash = "[content-hash]", 109 | }, 110 | }, 111 | } 112 | ``` 113 | 114 | The easiest way to populate or update the `sokol` dependency is to run this on the cmdline: 115 | 116 | ``` 117 | zig fetch --save=sokol git+https://github.com/floooh/sokol-zig.git 118 | ``` 119 | 120 | This will automatically use the latest sokol-zig commit. 121 | 122 | For a native-only project, a `build.zig` file looks entirely vanilla: 123 | 124 | ```zig 125 | const std = @import("std"); 126 | const Build = std.Build; 127 | const OptimizeMode = std.builtin.OptimizeMode; 128 | 129 | pub fn build(b: *Build) !void { 130 | const target = b.standardTargetOptions(.{}); 131 | const optimize = b.standardOptimizeOption(.{}); 132 | const dep_sokol = b.dependency("sokol", .{ 133 | .target = target, 134 | .optimize = optimize, 135 | }); 136 | const hello = b.addExecutable(.{ 137 | .name = "hello", 138 | .target = target, 139 | .optimize = optimize, 140 | .root_source_file = b.path("src/hello.zig"), 141 | }); 142 | hello.root_module.addImport("sokol", dep_sokol.module("sokol")); 143 | b.installArtifact(hello); 144 | const run = b.addRunArtifact(hello); 145 | b.step("run", "Run hello").dependOn(&run.step); 146 | } 147 | ``` 148 | 149 | If you also want to run on the web via `-Dtarget=wasm32-emscripten`, the web platform 150 | build must look special, because Emscripten must be used for linking, and to run 151 | the build result in a browser, a special run step must be created. 152 | 153 | Such a 'hybrid' build script might look like this (copied straight from [pacman.zig](https://github.com/floooh/pacman.zig)): 154 | 155 | ```zig 156 | const std = @import("std"); 157 | const Build = std.Build; 158 | const OptimizeMode = std.builtin.OptimizeMode; 159 | const sokol = @import("sokol"); 160 | 161 | pub fn build(b: *Build) !void { 162 | const target = b.standardTargetOptions(.{}); 163 | const optimize = b.standardOptimizeOption(.{}); 164 | const dep_sokol = b.dependency("sokol", .{ 165 | .target = target, 166 | .optimize = optimize, 167 | }); 168 | 169 | // special case handling for native vs web build 170 | if (target.result.cpu.arch.isWasm()) { 171 | try buildWeb(b, target, optimize, dep_sokol); 172 | } else { 173 | try buildNative(b, target, optimize, dep_sokol); 174 | } 175 | } 176 | 177 | // this is the regular build for all native platforms, nothing surprising here 178 | fn buildNative(b: *Build, target: Build.ResolvedTarget, optimize: OptimizeMode, dep_sokol: *Build.Dependency) !void { 179 | const pacman = b.addExecutable(.{ 180 | .name = "pacman", 181 | .target = target, 182 | .optimize = optimize, 183 | .root_source_file = b.path("src/pacman.zig"), 184 | }); 185 | pacman.root_module.addImport("sokol", dep_sokol.module("sokol")); 186 | b.installArtifact(pacman); 187 | const run = b.addRunArtifact(pacman); 188 | b.step("run", "Run pacman").dependOn(&run.step); 189 | } 190 | 191 | // for web builds, the Zig code needs to be built into a library and linked with the Emscripten linker 192 | fn buildWeb(b: *Build, target: Build.ResolvedTarget, optimize: OptimizeMode, dep_sokol: *Build.Dependency) !void { 193 | const pacman = b.addStaticLibrary(.{ 194 | .name = "pacman", 195 | .target = target, 196 | .optimize = optimize, 197 | .root_source_file = b.path("src/pacman.zig"), 198 | }); 199 | pacman.root_module.addImport("sokol", dep_sokol.module("sokol")); 200 | 201 | // create a build step which invokes the Emscripten linker 202 | const emsdk = dep_sokol.builder.dependency("emsdk", .{}); 203 | const link_step = try sokol.emLinkStep(b, .{ 204 | .lib_main = pacman, 205 | .target = target, 206 | .optimize = optimize, 207 | .emsdk = emsdk, 208 | .use_webgl2 = true, 209 | .use_emmalloc = true, 210 | .use_filesystem = false, 211 | .shell_file_path = dep_sokol.path("src/sokol/web/shell.html"), 212 | }); 213 | // attach Emscripten linker output to default install step 214 | b.getInstallStep().dependOn(&link_step.step); 215 | // ...and a special run step to start the web build output via 'emrun' 216 | const run = sokol.emRunStep(b, .{ .name = "pacman", .emsdk = emsdk }); 217 | run.step.dependOn(&link_step.step); 218 | b.step("run", "Run pacman").dependOn(&run.step); 219 | } 220 | ``` 221 | 222 | ## Using sokol headers in C code 223 | 224 | The sokol-zig build.zig exposes a C library artifact called `sokol_clib`. 225 | 226 | You can lookup the build step for this library via: 227 | 228 | ```zig 229 | const dep_sokol = b.dependency("sokol", .{ 230 | .target = target, 231 | .optimize = optimize, 232 | }); 233 | const sokol_clib = dep_sokol.artifact("sokol_clib"); 234 | ``` 235 | 236 | ...once you have that library artifact, 'link' it to your compile step which contains 237 | your own C code: 238 | 239 | ```zig 240 | const my_clib = ...; 241 | my_clib.linkLibrary(sokol_clib); 242 | ``` 243 | 244 | This makes the Sokol C headers available to your C code in a `sokol/` subdirectory: 245 | 246 | ```c 247 | #include "sokol/sokol_app.h" 248 | #include "sokol/sokol_gfx.h" 249 | // etc... 250 | ``` 251 | 252 | Keep in mind that the implementation is already provided in the `sokol_clib` 253 | static link library (e.g. don't try to build the Sokol implementations yourself 254 | via the `SOKOL_IMPL` macro). 255 | 256 | 257 | ## wasm32-emscripten caveats 258 | 259 | - Zig allocators use the `@returnAddress` builtin, which isn't supported in the Emscripten 260 | runtime out of the box (you'll get a runtime error in the browser's Javascript console 261 | looking like this: `Cannot use convertFrameToPC (needed by __builtin_return_address) without -sUSE_OFFSET_CONVERTER`. 262 | To link with `-sUSE_OFFSET_CONVERTER`, simply set the `.use_offset_converter` option 263 | in the Emscripten linker step in your build.zig: 264 | 265 | ```zig 266 | const link_step = try sokol.emLinkStep(b, .{ 267 | // ...other settings here 268 | .use_offset_converter = true, 269 | }); 270 | ``` 271 | 272 | - the Zig stdlib only has limited support for the `wasm32-emscripten` 273 | target, for instance using `std.fs` functions will most likely fail 274 | to compile (the sokol-zig bindings might add more sokol headers 275 | in the future to fill some of the gaps) 276 | 277 | ## Dear ImGui support 278 | 279 | The sokol-zig bindings come with sokol_imgui.h (exposed as the Zig package 280 | `sokol.imgui`), but integration into a project's build.zig requires some extra 281 | steps, mainly because I didn't want to add a 282 | [cimgui](https://github.com/cimgui/cimgui) dependency to the sokol-zig package 283 | (especially since cimgui uses git submodule which are not supported by the Zig 284 | package manager). 285 | 286 | The main steps to create Dear ImGui apps with sokol-zig are: 287 | 288 | 1. 'bring your own cimgui' 289 | 2. tell the sokol dependency that it needs to include sokol_imgui.h into 290 | the compiled C library: 291 | ```zig 292 | const dep_sokol = b.dependency("sokol", .{ 293 | .target = target, 294 | .optimize = optimize, 295 | .with_sokol_imgui = true, 296 | }); 297 | ``` 298 | 3. inject the path to the cimgui directory into the sokol dependency so 299 | that C compilation works (this needs to find the `cimgui.h` header) 300 | 301 | ```zig 302 | dep_sokol.artifact("sokol_clib").addIncludePath(cimgui_root); 303 | ``` 304 | 305 | Also see the following example project: 306 | 307 | https://github.com/floooh/sokol-zig-imgui-sample/ 308 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .sokol, 3 | .version = "0.1.0", 4 | .minimum_zig_version = "0.14.0", 5 | .fingerprint = 0xf9f6f48d2b47bda5, 6 | .paths = .{ 7 | "src", 8 | "build.zig", 9 | "build.zig.zon", 10 | }, 11 | .dependencies = .{ 12 | .emsdk = .{ 13 | .url = "git+https://github.com/emscripten-core/emsdk#4.0.3", 14 | .hash = "N-V-__8AAOG3BQCJ9cn-N2swm2o5cLmDhmdHmtwNngOChK78", 15 | }, 16 | .shdc = .{ 17 | .url = "git+https://github.com/floooh/sokol-tools-bin#90d2b9267813cbfb66e26c8e3eca5cc866f947e0", 18 | .hash = "sokolshdc-0.1.0-r2KZDtzckwTGGk0k4xotl5r1-15ary_9-vvFzv5cUxKn", 19 | }, 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /examples/blend.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // blend.zig 3 | // Test/demonstrate blend modes. 4 | //------------------------------------------------------------------------------ 5 | const std = @import("std"); 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const vec3 = @import("math.zig").Vec3; 12 | const mat4 = @import("math.zig").Mat4; 13 | const shd = @import("shaders/blend.glsl.zig"); 14 | 15 | const NUM_BLEND_FACTORS = 15; 16 | 17 | const state = struct { 18 | var pass_action: sg.PassAction = .{}; 19 | var bind: sg.Bindings = .{}; 20 | var pip: [NUM_BLEND_FACTORS][NUM_BLEND_FACTORS]sg.Pipeline = undefined; 21 | var bg_pip: sg.Pipeline = .{}; 22 | var r: f32 = 0; 23 | var tick: f32 = 0; 24 | }; 25 | 26 | export fn init() void { 27 | sg.setup(.{ 28 | .pipeline_pool_size = NUM_BLEND_FACTORS * NUM_BLEND_FACTORS + 1, 29 | .environment = sglue.environment(), 30 | .logger = .{ .func = slog.func }, 31 | }); 32 | 33 | state.pass_action.colors[0].load_action = .DONTCARE; 34 | state.pass_action.depth.load_action = .DONTCARE; 35 | state.pass_action.stencil.load_action = .DONTCARE; 36 | 37 | state.bind.vertex_buffers[0] = sg.makeBuffer(.{ 38 | .data = sg.asRange(&[_]f32{ 39 | // pos color 40 | -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.5, 41 | 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.5, 42 | -1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.5, 43 | 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.5, 44 | }), 45 | }); 46 | 47 | // pipeline object for rendering the background 48 | state.bg_pip = sg.makePipeline(.{ 49 | .shader = sg.makeShader(shd.bgShaderDesc(sg.queryBackend())), 50 | .layout = init: { 51 | var l = sg.VertexLayoutState{}; 52 | l.buffers[0].stride = 28; 53 | l.attrs[shd.ATTR_bg_position].format = .FLOAT2; 54 | break :init l; 55 | }, 56 | .primitive_type = .TRIANGLE_STRIP, 57 | }); 58 | 59 | // lot of pipeline objects for rendering the blended quads 60 | const shader = sg.makeShader(shd.quadShaderDesc(sg.queryBackend())); 61 | for (0..NUM_BLEND_FACTORS) |src| { 62 | for (0..NUM_BLEND_FACTORS) |dst| { 63 | state.pip[src][dst] = sg.makePipeline(.{ 64 | .layout = init: { 65 | var l = sg.VertexLayoutState{}; 66 | l.attrs[shd.ATTR_quad_position].format = .FLOAT3; 67 | l.attrs[shd.ATTR_quad_color0].format = .FLOAT4; 68 | break :init l; 69 | }, 70 | .shader = shader, 71 | .primitive_type = .TRIANGLE_STRIP, 72 | .blend_color = .{ .r = 1.0, .g = 0.0, .b = 0.0, .a = 1.0 }, 73 | .colors = init: { 74 | var c: [4]sg.ColorTargetState = @splat(.{}); 75 | c[0] = .{ 76 | .blend = .{ 77 | .enabled = true, 78 | .src_factor_rgb = @enumFromInt(src + 1), 79 | .dst_factor_rgb = @enumFromInt(dst + 1), 80 | .src_factor_alpha = .ONE, 81 | .dst_factor_alpha = .ZERO, 82 | }, 83 | }; 84 | break :init c; 85 | }, 86 | }); 87 | } 88 | } 89 | } 90 | 91 | export fn frame() void { 92 | const time: f32 = @floatCast(sapp.frameDuration() * 60.0); 93 | 94 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 95 | 96 | // draw background 97 | state.tick += 1.0 * time; 98 | const bg_fs_params: shd.BgFsParams = .{ .tick = state.tick }; 99 | sg.applyPipeline(state.bg_pip); 100 | sg.applyBindings(state.bind); 101 | sg.applyUniforms(shd.UB_bg_fs_params, sg.asRange(&bg_fs_params)); 102 | sg.draw(0, 4, 1); 103 | 104 | // draw the blended quads 105 | const proj = mat4.persp(90.0, sapp.widthf() / sapp.heightf(), 0.01, 100.0); 106 | const view = mat4.lookat(.{ .x = 0, .y = 0, .z = 25.0 }, vec3.zero(), vec3.up()); 107 | const view_proj = mat4.mul(proj, view); 108 | 109 | state.r += 0.6 * time; 110 | var r0 = state.r; 111 | for (0..NUM_BLEND_FACTORS) |src| { 112 | for (0..NUM_BLEND_FACTORS) |dst| { 113 | // compute model-view-proj matrix 114 | const shift = NUM_BLEND_FACTORS / 2; 115 | const t: vec3 = .{ 116 | .x = (@as(f32, @floatFromInt(dst)) - shift) * 3.0, 117 | .y = (@as(f32, @floatFromInt(src)) - shift) * 2.2, 118 | .z = 0.0, 119 | }; 120 | const model = mat4.mul(mat4.translate(t), mat4.rotate(r0, vec3.up())); 121 | const quad_vs_params: shd.QuadVsParams = .{ 122 | .mvp = mat4.mul(view_proj, model), 123 | }; 124 | sg.applyPipeline(state.pip[src][dst]); 125 | sg.applyBindings(state.bind); 126 | sg.applyUniforms(shd.UB_quad_vs_params, sg.asRange(&quad_vs_params)); 127 | sg.draw(0, 4, 1); 128 | r0 += 0.6; 129 | } 130 | } 131 | sg.endPass(); 132 | sg.commit(); 133 | } 134 | 135 | export fn cleanup() void { 136 | sg.shutdown(); 137 | } 138 | 139 | pub fn main() void { 140 | sapp.run(.{ 141 | .init_cb = init, 142 | .frame_cb = frame, 143 | .cleanup_cb = cleanup, 144 | .width = 800, 145 | .height = 600, 146 | .sample_count = 4, 147 | .window_title = "blend.zig", 148 | .icon = .{ .sokol_default = true }, 149 | .logger = .{ .func = slog.func }, 150 | }); 151 | } 152 | -------------------------------------------------------------------------------- /examples/bufferoffsets.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // bufferoffsets.zig 3 | // 4 | // Render separate geometries in vertex- and index-buffers with 5 | // buffer offsets. 6 | //------------------------------------------------------------------------------ 7 | const sokol = @import("sokol"); 8 | const slog = sokol.log; 9 | const sg = sokol.gfx; 10 | const sapp = sokol.app; 11 | const sglue = sokol.glue; 12 | const shd = @import("shaders/bufferoffsets.glsl.zig"); 13 | 14 | const state = struct { 15 | var pass_action: sg.PassAction = .{}; 16 | var pip: sg.Pipeline = .{}; 17 | var bind: sg.Bindings = .{}; 18 | }; 19 | 20 | const Vertex = extern struct { x: f32, y: f32, r: f32, g: f32, b: f32 }; 21 | 22 | export fn init() void { 23 | sg.setup(.{ 24 | .environment = sglue.environment(), 25 | .logger = .{ .func = slog.func }, 26 | }); 27 | 28 | // clear to a blue-ish color 29 | state.pass_action.colors[0] = .{ 30 | .load_action = .CLEAR, 31 | .clear_value = .{ .r = 0.5, .g = 0.5, .b = 1, .a = 1 }, 32 | }; 33 | 34 | // a 2D triangle and quad in 1 vertex buffer and 1 index buffer 35 | state.bind.vertex_buffers[0] = sg.makeBuffer(.{ 36 | .data = sg.asRange(&[_]Vertex{ 37 | // zig fmt: off 38 | // triangle vertices 39 | .{ .x = 0.0, .y = 0.55, .r = 1.0, .g = 0.0, .b = 0.0 }, 40 | .{ .x = 0.25, .y = 0.05, .r = 0.0, .g = 1.0, .b = 0.0 }, 41 | .{ .x = -0.25, .y = 0.05, .r = 0.0, .g = 0.0, .b = 1.0 }, 42 | // quad vertices 43 | .{ .x = -0.25, .y = -0.05, .r = 0.0, .g = 0.0, .b = 1.0 }, 44 | .{ .x = 0.25, .y = -0.05, .r = 0.0, .g = 1.0, .b = 0.0 }, 45 | .{ .x = 0.25, .y = -0.55, .r = 1.0, .g = 0.0, .b = 0.0 }, 46 | .{ .x = -0.25, .y = -0.55, .r = 1.0, .g = 1.0, .b = 0.0 }, 47 | // zig fmt: on 48 | }), 49 | }); 50 | state.bind.index_buffer = sg.makeBuffer(.{ 51 | .usage = .{ .index_buffer = true }, 52 | .data = sg.asRange(&[_]u16{ 53 | // triangle indices 54 | 0, 1, 2, 55 | // quad indices 56 | 0, 1, 2, 57 | 0, 2, 3, 58 | }), 59 | }); 60 | 61 | // a shader and pipeline object 62 | state.pip = sg.makePipeline(.{ 63 | .shader = sg.makeShader(shd.bufferoffsetsShaderDesc(sg.queryBackend())), 64 | .layout = init: { 65 | var l = sg.VertexLayoutState{}; 66 | l.attrs[shd.ATTR_bufferoffsets_position].format = .FLOAT2; 67 | l.attrs[shd.ATTR_bufferoffsets_color0].format = .FLOAT3; 68 | break :init l; 69 | }, 70 | .index_type = .UINT16, 71 | }); 72 | } 73 | 74 | export fn frame() void { 75 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 76 | sg.applyPipeline(state.pip); 77 | 78 | // render the triangle 79 | state.bind.vertex_buffer_offsets[0] = 0; 80 | state.bind.index_buffer_offset = 0; 81 | sg.applyBindings(state.bind); 82 | sg.draw(0, 3, 1); 83 | 84 | // render the quad 85 | state.bind.vertex_buffer_offsets[0] = 3 * @sizeOf(Vertex); 86 | state.bind.index_buffer_offset = 3 * @sizeOf(u16); 87 | sg.applyBindings(state.bind); 88 | sg.draw(0, 6, 1); 89 | 90 | sg.endPass(); 91 | sg.commit(); 92 | } 93 | 94 | export fn cleanup() void { 95 | sg.shutdown(); 96 | } 97 | 98 | pub fn main() void { 99 | sapp.run(.{ 100 | .init_cb = init, 101 | .frame_cb = frame, 102 | .cleanup_cb = cleanup, 103 | .width = 800, 104 | .height = 600, 105 | .icon = .{ .sokol_default = true }, 106 | .window_title = "bufferoffsets.zig", 107 | .logger = .{ .func = slog.func }, 108 | }); 109 | } 110 | -------------------------------------------------------------------------------- /examples/build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Build = std.Build; 3 | const OptimizeMode = std.builtin.OptimizeMode; 4 | -------------------------------------------------------------------------------- /examples/clear.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // clear.zig 3 | // 4 | // Just clear the framebuffer with an animated color. 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const print = @import("std").debug.print; 12 | 13 | const state = struct { 14 | var pass_action: sg.PassAction = .{}; 15 | }; 16 | 17 | export fn init() void { 18 | sg.setup(.{ 19 | .environment = sglue.environment(), 20 | .logger = .{ .func = slog.func }, 21 | }); 22 | state.pass_action.colors[0] = .{ 23 | .load_action = .CLEAR, 24 | .clear_value = .{ .r = 1, .g = 1, .b = 0, .a = 1 }, 25 | }; 26 | print("Backend: {}\n", .{sg.queryBackend()}); 27 | } 28 | 29 | export fn frame() void { 30 | const g = state.pass_action.colors[0].clear_value.g + 0.01; 31 | state.pass_action.colors[0].clear_value.g = if (g > 1.0) 0.0 else g; 32 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 33 | sg.endPass(); 34 | sg.commit(); 35 | } 36 | 37 | export fn cleanup() void { 38 | sg.shutdown(); 39 | } 40 | 41 | pub fn main() void { 42 | sapp.run(.{ 43 | .init_cb = init, 44 | .frame_cb = frame, 45 | .cleanup_cb = cleanup, 46 | .width = 640, 47 | .height = 480, 48 | .icon = .{ .sokol_default = true }, 49 | .window_title = "clear.zig", 50 | .logger = .{ .func = slog.func }, 51 | .win32_console_attach = true, 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /examples/cube.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // cube.zig 3 | // 4 | // Shader with uniform data. 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const vec3 = @import("math.zig").Vec3; 12 | const mat4 = @import("math.zig").Mat4; 13 | const shd = @import("shaders/cube.glsl.zig"); 14 | 15 | const state = struct { 16 | var rx: f32 = 0.0; 17 | var ry: f32 = 0.0; 18 | var pip: sg.Pipeline = .{}; 19 | var bind: sg.Bindings = .{}; 20 | var pass_action: sg.PassAction = .{}; 21 | const view: mat4 = mat4.lookat(.{ .x = 0.0, .y = 1.5, .z = 6.0 }, vec3.zero(), vec3.up()); 22 | }; 23 | 24 | export fn init() void { 25 | sg.setup(.{ 26 | .environment = sglue.environment(), 27 | .logger = .{ .func = slog.func }, 28 | }); 29 | 30 | // cube vertex buffer 31 | state.bind.vertex_buffers[0] = sg.makeBuffer(.{ 32 | .data = sg.asRange(&[_]f32{ 33 | // positions colors 34 | -1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 35 | 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 36 | 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 37 | -1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 38 | 39 | -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 40 | 1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 41 | 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 42 | -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 43 | 44 | -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 45 | -1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 46 | -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 47 | -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 48 | 49 | 1.0, -1.0, -1.0, 1.0, 0.5, 0.0, 1.0, 50 | 1.0, 1.0, -1.0, 1.0, 0.5, 0.0, 1.0, 51 | 1.0, 1.0, 1.0, 1.0, 0.5, 0.0, 1.0, 52 | 1.0, -1.0, 1.0, 1.0, 0.5, 0.0, 1.0, 53 | 54 | -1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0, 55 | -1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0, 56 | 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0, 57 | 1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0, 58 | 59 | -1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 60 | -1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0, 61 | 1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0, 62 | 1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 63 | }), 64 | }); 65 | 66 | // cube index buffer 67 | state.bind.index_buffer = sg.makeBuffer(.{ 68 | .usage = .{ .index_buffer = true }, 69 | .data = sg.asRange(&[_]u16{ 70 | 0, 1, 2, 0, 2, 3, 71 | 6, 5, 4, 7, 6, 4, 72 | 8, 9, 10, 8, 10, 11, 73 | 14, 13, 12, 15, 14, 12, 74 | 16, 17, 18, 16, 18, 19, 75 | 22, 21, 20, 23, 22, 20, 76 | }), 77 | }); 78 | 79 | // shader and pipeline object 80 | state.pip = sg.makePipeline(.{ 81 | .shader = sg.makeShader(shd.cubeShaderDesc(sg.queryBackend())), 82 | .layout = init: { 83 | var l = sg.VertexLayoutState{}; 84 | l.attrs[shd.ATTR_cube_position].format = .FLOAT3; 85 | l.attrs[shd.ATTR_cube_color0].format = .FLOAT4; 86 | break :init l; 87 | }, 88 | .index_type = .UINT16, 89 | .depth = .{ 90 | .compare = .LESS_EQUAL, 91 | .write_enabled = true, 92 | }, 93 | .cull_mode = .BACK, 94 | }); 95 | 96 | // framebuffer clear color 97 | state.pass_action.colors[0] = .{ .load_action = .CLEAR, .clear_value = .{ .r = 0.25, .g = 0.5, .b = 0.75, .a = 1 } }; 98 | } 99 | 100 | export fn frame() void { 101 | const dt: f32 = @floatCast(sapp.frameDuration() * 60); 102 | state.rx += 1.0 * dt; 103 | state.ry += 2.0 * dt; 104 | const vs_params = computeVsParams(state.rx, state.ry); 105 | 106 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 107 | sg.applyPipeline(state.pip); 108 | sg.applyBindings(state.bind); 109 | sg.applyUniforms(shd.UB_vs_params, sg.asRange(&vs_params)); 110 | sg.draw(0, 36, 1); 111 | sg.endPass(); 112 | sg.commit(); 113 | } 114 | 115 | export fn cleanup() void { 116 | sg.shutdown(); 117 | } 118 | 119 | pub fn main() void { 120 | sapp.run(.{ 121 | .init_cb = init, 122 | .frame_cb = frame, 123 | .cleanup_cb = cleanup, 124 | .width = 800, 125 | .height = 600, 126 | .sample_count = 4, 127 | .icon = .{ .sokol_default = true }, 128 | .window_title = "cube.zig", 129 | .logger = .{ .func = slog.func }, 130 | }); 131 | } 132 | 133 | fn computeVsParams(rx: f32, ry: f32) shd.VsParams { 134 | const rxm = mat4.rotate(rx, .{ .x = 1.0, .y = 0.0, .z = 0.0 }); 135 | const rym = mat4.rotate(ry, .{ .x = 0.0, .y = 1.0, .z = 0.0 }); 136 | const model = mat4.mul(rxm, rym); 137 | const aspect = sapp.widthf() / sapp.heightf(); 138 | const proj = mat4.persp(60.0, aspect, 0.01, 10.0); 139 | return shd.VsParams{ .mvp = mat4.mul(mat4.mul(proj, state.view), model) }; 140 | } 141 | -------------------------------------------------------------------------------- /examples/debugtext-print.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // debugtext-print.zig 3 | // 4 | // Demonstrates formatted printing with sokol.debugtext 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const stm = sokol.time; 12 | const sdtx = @import("sokol").debugtext; 13 | 14 | // only needed when using std.fmt directly instead of sokol.debugtext.print() 15 | const fmt = @import("std").fmt; 16 | 17 | // font slots 18 | const KC854 = 0; 19 | const C64 = 1; 20 | const ORIC = 2; 21 | 22 | const Color = struct { r: u8, g: u8, b: u8 }; 23 | 24 | const state = struct { 25 | var pass_action: sg.PassAction = .{}; 26 | var frame_count: u32 = 0; 27 | var time_stamp: u64 = 0; 28 | const colors = [_]Color{ 29 | .{ .r = 0xf4, .g = 0x43, .b = 0x36 }, 30 | .{ .r = 0x21, .g = 0x96, .b = 0xf3 }, 31 | .{ .r = 0x4c, .g = 0xaf, .b = 0x50 }, 32 | }; 33 | }; 34 | 35 | export fn init() void { 36 | // setup sokol.time and sokol.gfx 37 | stm.setup(); 38 | sg.setup(.{ 39 | .environment = sglue.environment(), 40 | .logger = .{ .func = slog.func }, 41 | }); 42 | 43 | // setup sokol.debugtext with 3 builtin fonts 44 | sdtx.setup(.{ 45 | .fonts = init: { 46 | var f: [8]sdtx.FontDesc = @splat(.{}); 47 | f[KC854] = sdtx.fontKc854(); 48 | f[C64] = sdtx.fontC64(); 49 | f[ORIC] = sdtx.fontOric(); 50 | break :init f; 51 | }, 52 | .logger = .{ .func = slog.func }, 53 | }); 54 | 55 | // pass-action for clearing to blue-ish 56 | state.pass_action.colors[0] = .{ 57 | .load_action = .CLEAR, 58 | .clear_value = .{ .r = 0, .g = 0.125, .b = 0.25, .a = 1 }, 59 | }; 60 | } 61 | 62 | export fn frame() void { 63 | state.frame_count += 1; 64 | const frame_time = stm.ms(stm.laptime(&state.time_stamp)); 65 | 66 | sdtx.canvas(sapp.widthf() * 0.5, sapp.heightf() * 0.5); 67 | sdtx.origin(3, 3); 68 | 69 | inline for (.{ KC854, C64, ORIC }) |font| { 70 | const color = state.colors[font]; 71 | sdtx.font(font); 72 | sdtx.color3b(color.r, color.g, color.b); 73 | const world_str = if (0 == (state.frame_count & (1 << 7))) "Welt" else "World"; 74 | sdtx.print("Hello '{s}'!\n", .{world_str}); 75 | sdtx.print("\tFrame Time:\t\t{d:.3}ms\n", .{frame_time}); 76 | sdtx.print("\tFrame Count:\t{d}\t0x{X:0>4}\n", .{ state.frame_count, state.frame_count }); 77 | sdtx.moveY(2); 78 | } 79 | sdtx.font(KC854); 80 | sdtx.color3b(255, 128, 0); 81 | 82 | // can also use sdtx.Writer with std.fmt directly: 83 | const writer: sdtx.Writer = .{}; 84 | fmt.format(writer, "using std.fmt directly ({d})\n", .{state.frame_count}) catch unreachable; 85 | 86 | // render the frame via sokol.gfx 87 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 88 | sdtx.draw(); 89 | sg.endPass(); 90 | sg.commit(); 91 | } 92 | 93 | export fn cleanup() void { 94 | sdtx.shutdown(); 95 | sg.shutdown(); 96 | } 97 | 98 | pub fn main() void { 99 | sapp.run(.{ 100 | .init_cb = init, 101 | .frame_cb = frame, 102 | .cleanup_cb = cleanup, 103 | .width = 640, 104 | .height = 480, 105 | .icon = .{ .sokol_default = true }, 106 | .window_title = "debugtext-print.zig", 107 | .logger = .{ .func = slog.func }, 108 | }); 109 | } 110 | -------------------------------------------------------------------------------- /examples/debugtext-userfont.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // debugtext-userfont.zig 3 | // 4 | // How to use a user-defined font with sokol.debugtext 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const sdtx = sokol.debugtext; 12 | 13 | // use font-slot 1 for the user-defined font, there's no particular 14 | // reason why this isn't 0, except for testing 15 | const UserFont = 1; 16 | 17 | const Color = struct { 18 | r: u8, 19 | g: u8, 20 | b: u8, 21 | }; 22 | 23 | const state = struct { 24 | var pass_action: sg.PassAction = .{}; 25 | var frame_count: u32 = 0; 26 | const color_palette = [_]Color{ 27 | .{ .r = 0xf4, .g = 0x43, .b = 0x36 }, 28 | .{ .r = 0xe9, .g = 0x1e, .b = 0x63 }, 29 | .{ .r = 0x9c, .g = 0x27, .b = 0xb0 }, 30 | .{ .r = 0x67, .g = 0x3a, .b = 0xb7 }, 31 | .{ .r = 0x3f, .g = 0x51, .b = 0xb5 }, 32 | .{ .r = 0x21, .g = 0x96, .b = 0xf3 }, 33 | .{ .r = 0x03, .g = 0xa9, .b = 0xf4 }, 34 | .{ .r = 0x00, .g = 0xbc, .b = 0xd4 }, 35 | .{ .r = 0x00, .g = 0x96, .b = 0x88 }, 36 | .{ .r = 0x4c, .g = 0xaf, .b = 0x50 }, 37 | .{ .r = 0x8b, .g = 0xc3, .b = 0x4a }, 38 | .{ .r = 0xcd, .g = 0xdc, .b = 0x39 }, 39 | .{ .r = 0xff, .g = 0xeb, .b = 0x3b }, 40 | .{ .r = 0xff, .g = 0xc1, .b = 0x07 }, 41 | .{ .r = 0xff, .g = 0x98, .b = 0x00 }, 42 | .{ .r = 0xff, .g = 0x57, .b = 0x22 }, 43 | }; 44 | }; 45 | 46 | export fn init() void { 47 | sg.setup(.{ 48 | .environment = sglue.environment(), 49 | .logger = .{ .func = slog.func }, 50 | }); 51 | 52 | // setup sokol.debugtext with a user-defined font as the only font 53 | // NOTE that the user font only provides pixel data for the 54 | // characters 0x20 to 0x9F (inclusive) 55 | sdtx.setup(.{ 56 | .fonts = init: { 57 | var f: [8]sdtx.FontDesc = @splat(.{}); 58 | f[UserFont] = .{ 59 | .data = sdtx.asRange(&user_font), 60 | .first_char = 0x20, 61 | .last_char = 0x9F, 62 | }; 63 | break :init f; 64 | }, 65 | .logger = .{ .func = slog.func }, 66 | }); 67 | 68 | // pass-action to clear background to blue-ish 69 | state.pass_action.colors[0] = .{ 70 | .load_action = .CLEAR, 71 | .clear_value = .{ .r = 0, .g = 0.125, .b = 0.25, .a = 1 }, 72 | }; 73 | } 74 | 75 | export fn frame() void { 76 | state.frame_count += 1; 77 | 78 | sdtx.canvas(sapp.widthf() * 0.25, sapp.heightf() * 0.25); 79 | sdtx.origin(1, 2); 80 | sdtx.font(UserFont); 81 | sdtx.color3b(0xFF, 0x17, 0x44); 82 | sdtx.puts("Hello 8-bit ATARI font:\n\n"); 83 | var line: u32 = 0; 84 | for (0x20..0xA0) |c| { 85 | if ((c & 15) == 0) { 86 | sdtx.puts("\n\t"); 87 | line += 1; 88 | } 89 | // color scrolling effect: 90 | const color = state.color_palette[(c + line + (state.frame_count / 2)) & 15]; 91 | sdtx.color3b(color.r, color.g, color.b); 92 | sdtx.putc(@intCast(c)); 93 | } 94 | 95 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 96 | sdtx.draw(); 97 | sg.endPass(); 98 | sg.commit(); 99 | } 100 | 101 | export fn cleanup() void { 102 | sdtx.shutdown(); 103 | sg.shutdown(); 104 | } 105 | 106 | pub fn main() void { 107 | sapp.run(.{ 108 | .init_cb = init, 109 | .frame_cb = frame, 110 | .cleanup_cb = cleanup, 111 | .width = 800, 112 | .height = 600, 113 | .icon = .{ .sokol_default = true }, 114 | .window_title = "debugtext-userfont.zig", 115 | .logger = .{ .func = slog.func }, 116 | }); 117 | } 118 | 119 | // Font data extracted from Atari 400 ROM at address 0xE000, 120 | // and reshuffled to map to ASCII. Each character is 8 bytes, 121 | // 1 bit per pixel in an 8x8 matrix. 122 | const user_font = [128 * 8]u8{ 123 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 124 | 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // 21 125 | 0x00, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, // 22 126 | 0x00, 0x66, 0xFF, 0x66, 0x66, 0xFF, 0x66, 0x00, // 23 127 | 0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00, // 24 128 | 0x00, 0x66, 0x6C, 0x18, 0x30, 0x66, 0x46, 0x00, // 25 129 | 0x1C, 0x36, 0x1C, 0x38, 0x6F, 0x66, 0x3B, 0x00, // 26 130 | 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, // 27 131 | 0x00, 0x0E, 0x1C, 0x18, 0x18, 0x1C, 0x0E, 0x00, // 28 132 | 0x00, 0x70, 0x38, 0x18, 0x18, 0x38, 0x70, 0x00, // 29 133 | 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // 2A 134 | 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // 2B 135 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, // 2C 136 | 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, // 2D 137 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // 2E 138 | 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // 2F 139 | 0x00, 0x3C, 0x66, 0x6E, 0x76, 0x66, 0x3C, 0x00, // 30 140 | 0x00, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E, 0x00, // 31 141 | 0x00, 0x3C, 0x66, 0x0C, 0x18, 0x30, 0x7E, 0x00, // 32 142 | 0x00, 0x7E, 0x0C, 0x18, 0x0C, 0x66, 0x3C, 0x00, // 33 143 | 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0x7E, 0x0C, 0x00, // 34 144 | 0x00, 0x7E, 0x60, 0x7C, 0x06, 0x66, 0x3C, 0x00, // 35 145 | 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, // 36 146 | 0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x00, // 37 147 | 0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // 38 148 | 0x00, 0x3C, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00, // 39 149 | 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, // 3A 150 | 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, // 3B 151 | 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00, // 3C 152 | 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, // 3D 153 | 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, // 3E 154 | 0x00, 0x3C, 0x66, 0x0C, 0x18, 0x00, 0x18, 0x00, // 3F 155 | 0x00, 0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x3E, 0x00, // 40 156 | 0x00, 0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x00, // 41 157 | 0x00, 0x7C, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, // 42 158 | 0x00, 0x3C, 0x66, 0x60, 0x60, 0x66, 0x3C, 0x00, // 43 159 | 0x00, 0x78, 0x6C, 0x66, 0x66, 0x6C, 0x78, 0x00, // 44 160 | 0x00, 0x7E, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00, // 45 161 | 0x00, 0x7E, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00, // 46 162 | 0x00, 0x3E, 0x60, 0x60, 0x6E, 0x66, 0x3E, 0x00, // 47 163 | 0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // 48 164 | 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, // 49 165 | 0x00, 0x06, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, // 4A 166 | 0x00, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x00, // 4B 167 | 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, // 4C 168 | 0x00, 0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x00, // 4D 169 | 0x00, 0x66, 0x76, 0x7E, 0x7E, 0x6E, 0x66, 0x00, // 4E 170 | 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 4F 171 | 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00, // 50 172 | 0x00, 0x3C, 0x66, 0x66, 0x66, 0x6C, 0x36, 0x00, // 51 173 | 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x00, // 52 174 | 0x00, 0x3C, 0x60, 0x3C, 0x06, 0x06, 0x3C, 0x00, // 53 175 | 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 54 176 | 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7E, 0x00, // 55 177 | 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 56 178 | 0x00, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00, // 57 179 | 0x00, 0x66, 0x66, 0x3C, 0x3C, 0x66, 0x66, 0x00, // 58 180 | 0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // 59 181 | 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, // 5A 182 | 0x00, 0x1E, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00, // 5B 183 | 0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, // 5C 184 | 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, // 5D 185 | 0x00, 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, // 5E 186 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, // 5F 187 | 0x00, 0x18, 0x3C, 0x7E, 0x7E, 0x3C, 0x18, 0x00, // 60 188 | 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, // 61 189 | 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, // 62 190 | 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x00, // 63 191 | 0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, // 64 192 | 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, // 65 193 | 0x00, 0x0E, 0x18, 0x3E, 0x18, 0x18, 0x18, 0x00, // 66 194 | 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x7C, // 67 195 | 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x00, // 68 196 | 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3C, 0x00, // 69 197 | 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3C, // 6A 198 | 0x00, 0x60, 0x60, 0x6C, 0x78, 0x6C, 0x66, 0x00, // 6B 199 | 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 6C 200 | 0x00, 0x00, 0x66, 0x7F, 0x7F, 0x6B, 0x63, 0x00, // 6D 201 | 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, // 6E 202 | 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // 6F 203 | 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, // 70 204 | 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06, // 71 205 | 0x00, 0x00, 0x7C, 0x66, 0x60, 0x60, 0x60, 0x00, // 72 206 | 0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00, // 73 207 | 0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x0E, 0x00, // 74 208 | 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, // 75 209 | 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 76 210 | 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x3E, 0x36, 0x00, // 77 211 | 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // 78 212 | 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x0C, 0x78, // 79 213 | 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, // 7A 214 | 0x00, 0x18, 0x3C, 0x7E, 0x7E, 0x18, 0x3C, 0x00, // 7B 215 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 7C 216 | 0x00, 0x7E, 0x78, 0x7C, 0x6E, 0x66, 0x06, 0x00, // 7D 217 | 0x08, 0x18, 0x38, 0x78, 0x38, 0x18, 0x08, 0x00, // 7E 218 | 0x10, 0x18, 0x1C, 0x1E, 0x1C, 0x18, 0x10, 0x00, // 7F 219 | 0x00, 0x36, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00, // 80 220 | 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18, // 81 221 | 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // 82 222 | 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00, // 83 223 | 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18, // 84 224 | 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, // 85 225 | 0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xC0, // 86 226 | 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, // 87 227 | 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, // 88 228 | 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // 89 229 | 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, // 8A 230 | 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // 8B 231 | 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // 8C 232 | 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8D 233 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // 8E 234 | 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // 8F 235 | 0x00, 0x1C, 0x1C, 0x77, 0x77, 0x08, 0x1C, 0x00, // 90 236 | 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18, // 91 237 | 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 92 238 | 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 93 239 | 0x00, 0x00, 0x3C, 0x7E, 0x7E, 0x7E, 0x3C, 0x00, // 94 240 | 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // 95 241 | 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 96 242 | 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 97 243 | 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 98 244 | 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // 99 245 | 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00, // 9A 246 | 0x78, 0x60, 0x78, 0x60, 0x7E, 0x18, 0x1E, 0x00, // 9B 247 | 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x00, // 9C 248 | 0x00, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, // 9D 249 | 0x00, 0x18, 0x30, 0x7E, 0x30, 0x18, 0x00, 0x00, // 9E 250 | 0x00, 0x18, 0x0C, 0x7E, 0x0C, 0x18, 0x00, 0x00, // 9F 251 | }; 252 | -------------------------------------------------------------------------------- /examples/debugtext.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // debugtext.zig 3 | // 4 | // Basic test and demo for the sokol.debugtext module 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const sdtx = sokol.debugtext; 12 | 13 | // font indices 14 | const KC853 = 0; 15 | const KC854 = 1; 16 | const Z1013 = 2; 17 | const CPC = 3; 18 | const C64 = 4; 19 | const ORIC = 5; 20 | 21 | var pass_action: sg.PassAction = .{}; 22 | 23 | export fn init() void { 24 | sg.setup(.{ 25 | .environment = sglue.environment(), 26 | .logger = .{ .func = slog.func }, 27 | }); 28 | 29 | // setup sokol-debugtext with all builtin fonts 30 | sdtx.setup(.{ 31 | .fonts = init: { 32 | var f: [8]sdtx.FontDesc = @splat(.{}); 33 | f[KC853] = sdtx.fontKc853(); 34 | f[KC854] = sdtx.fontKc854(); 35 | f[Z1013] = sdtx.fontZ1013(); 36 | f[CPC] = sdtx.fontCpc(); 37 | f[C64] = sdtx.fontC64(); 38 | f[ORIC] = sdtx.fontOric(); 39 | break :init f; 40 | }, 41 | .logger = .{ .func = slog.func }, 42 | }); 43 | 44 | pass_action.colors[0] = .{ 45 | .load_action = .CLEAR, 46 | .clear_value = .{ .r = 0, .g = 0.125, .b = 0.25, .a = 1 }, 47 | }; 48 | } 49 | 50 | // print all characters in a font 51 | fn printFont(font_index: u32, title: [:0]const u8, r: u8, g: u8, b: u8) void { 52 | sdtx.font(font_index); 53 | sdtx.color3b(r, g, b); 54 | sdtx.puts(title); 55 | for (32..256) |c| { 56 | sdtx.putc(@intCast(c)); 57 | if (((c + 1) & 63) == 0) { 58 | sdtx.crlf(); 59 | } 60 | } 61 | sdtx.crlf(); 62 | } 63 | 64 | export fn frame() void { 65 | // set virtual canvas size to half display size so that 66 | // glyphs are 16x16 display pixels 67 | sdtx.canvas(sapp.widthf() * 0.5, sapp.heightf() * 0.5); 68 | sdtx.origin(0.0, 2.0); 69 | sdtx.home(); 70 | 71 | // draw all font characters 72 | printFont(KC853, "KC85/3:\n", 0xf4, 0x43, 0x36); 73 | printFont(KC854, "KC85/4:\n", 0x21, 0x96, 0xf3); 74 | printFont(Z1013, "Z1013:\n", 0x4c, 0xaf, 0x50); 75 | printFont(CPC, "Amstrad CPC:\n", 0xff, 0xeb, 0x3b); 76 | printFont(C64, "C64:\n", 0x79, 0x86, 0xcb); 77 | printFont(ORIC, "Oric Atmos:\n", 0xff, 0x98, 0x00); 78 | 79 | // do the actual rendering 80 | sg.beginPass(.{ .action = pass_action, .swapchain = sglue.swapchain() }); 81 | sdtx.draw(); 82 | sg.endPass(); 83 | sg.commit(); 84 | } 85 | 86 | export fn cleanup() void { 87 | sdtx.shutdown(); 88 | sg.shutdown(); 89 | } 90 | 91 | pub fn main() void { 92 | sapp.run(.{ 93 | .init_cb = init, 94 | .frame_cb = frame, 95 | .cleanup_cb = cleanup, 96 | .width = 1024, 97 | .height = 600, 98 | .icon = .{ .sokol_default = true }, 99 | .window_title = "debugtext.zig", 100 | .logger = .{ .func = slog.func }, 101 | }); 102 | } 103 | -------------------------------------------------------------------------------- /examples/instancing-compute.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // instancing-compute.zig 3 | // 4 | // Like instancing.zig, but update particle positions via compute shader. 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const vec3 = @import("math.zig").Vec3; 12 | const mat4 = @import("math.zig").Mat4; 13 | const shd = @import("shaders/instancing-compute.glsl.zig"); 14 | 15 | const max_particles: usize = 512 * 1024; 16 | const num_particles_emitted_per_frame: usize = 10; 17 | 18 | const state = struct { 19 | var num_particles: usize = 0; 20 | var ry: f32 = 0.0; 21 | const compute = struct { 22 | var pip: sg.Pipeline = .{}; 23 | var bind: sg.Bindings = .{}; 24 | }; 25 | const display = struct { 26 | var pip: sg.Pipeline = .{}; 27 | var bind: sg.Bindings = .{}; 28 | var pass_action: sg.PassAction = .{}; 29 | const view: mat4 = mat4.lookat(.{ .x = 0, .y = 1.5, .z = 12.0 }, vec3.zero(), vec3.up()); 30 | }; 31 | }; 32 | 33 | export fn init() void { 34 | sg.setup(.{ 35 | .environment = sglue.environment(), 36 | .logger = .{ .func = slog.func }, 37 | }); 38 | 39 | // if compute shaders not supported, clear to red color and early out 40 | if (!sg.queryFeatures().compute) { 41 | state.display.pass_action.colors[0] = .{ 42 | .load_action = .CLEAR, 43 | .clear_value = .{ .r = 1, .g = 0, .b = 0, .a = 1 }, 44 | }; 45 | return; 46 | } 47 | 48 | // regular clear color 49 | state.display.pass_action.colors[0] = .{ 50 | .load_action = .CLEAR, 51 | .clear_value = .{ .r = 0, .g = 0.1, .b = 0.2, .a = 1 }, 52 | }; 53 | 54 | // a zero-initialized storage buffer for the particle state 55 | const sbuf = sg.makeBuffer(.{ 56 | .usage = .{ .storage_buffer = true }, 57 | .size = max_particles * @sizeOf(shd.Particle), 58 | .label = "particle-buffer", 59 | }); 60 | state.compute.bind.storage_buffers[shd.SBUF_cs_ssbo] = sbuf; 61 | state.display.bind.storage_buffers[shd.SBUF_vs_ssbo] = sbuf; 62 | 63 | // a compute shader and pipeline object for updating the particle state 64 | state.compute.pip = sg.makePipeline(.{ 65 | .compute = true, 66 | .shader = sg.makeShader(shd.updateShaderDesc(sg.queryBackend())), 67 | .label = "update-pipeline", 68 | }); 69 | 70 | // vertex and index buffer for particle geometry 71 | const r = 0.05; 72 | state.display.bind.vertex_buffers[0] = sg.makeBuffer(.{ 73 | .data = sg.asRange(&[_]f32{ 74 | 0.0, -r, 0.0, 1.0, 0.0, 0.0, 1.0, 75 | r, 0.0, r, 0.0, 1.0, 0.0, 1.0, 76 | r, 0.0, -r, 0.0, 0.0, 1.0, 1.0, 77 | -r, 0.0, -r, 1.0, 1.0, 0.0, 1.0, 78 | -r, 0.0, r, 0.0, 1.0, 1.0, 1.0, 79 | 0.0, r, 0.0, 1.0, 0.0, 1.0, 1.0, 80 | }), 81 | .label = "geometry-vbuf", 82 | }); 83 | state.display.bind.index_buffer = sg.makeBuffer(.{ 84 | .usage = .{ .index_buffer = true }, 85 | .data = sg.asRange(&[_]u16{ 86 | 2, 1, 0, 3, 2, 0, 87 | 4, 3, 0, 1, 4, 0, 88 | 5, 1, 2, 5, 2, 3, 89 | 5, 3, 4, 5, 4, 1, 90 | }), 91 | .label = "geometry-ibuf", 92 | }); 93 | 94 | // shader and pipeline for rendering the particles, this uses 95 | // the compute-updated storage buffer to provide the particle positions 96 | state.display.pip = sg.makePipeline(.{ 97 | .shader = sg.makeShader(shd.displayShaderDesc(sg.queryBackend())), 98 | .layout = brk: { 99 | var layout: sg.VertexLayoutState = .{}; 100 | layout.attrs[0] = .{ .format = .FLOAT3 }; 101 | layout.attrs[1] = .{ .format = .FLOAT4 }; 102 | break :brk layout; 103 | }, 104 | .index_type = .UINT16, 105 | .depth = .{ 106 | .compare = .LESS_EQUAL, 107 | .write_enabled = true, 108 | }, 109 | .cull_mode = .BACK, 110 | .label = "render-pipeline", 111 | }); 112 | 113 | // one-time init of particle velocities via a compute shader 114 | const pip = sg.makePipeline(.{ 115 | .compute = true, 116 | .shader = sg.makeShader(shd.initShaderDesc(sg.queryBackend())), 117 | }); 118 | sg.beginPass(.{ .compute = true }); 119 | sg.applyPipeline(pip); 120 | sg.applyBindings(state.compute.bind); 121 | sg.dispatch(max_particles / 64, 1, 1); 122 | sg.endPass(); 123 | sg.destroyPipeline(pip); 124 | } 125 | 126 | export fn frame() void { 127 | if (!sg.queryFeatures().compute) { 128 | drawFallback(); 129 | return; 130 | } 131 | 132 | state.num_particles += num_particles_emitted_per_frame; 133 | if (state.num_particles > max_particles) { 134 | state.num_particles = max_particles; 135 | } 136 | const dt: f32 = @floatCast(sapp.frameDuration()); 137 | 138 | // compute pass to update particle positions 139 | const cs_params: shd.CsParams = .{ 140 | .dt = dt, 141 | .num_particles = @intCast(state.num_particles), 142 | }; 143 | sg.beginPass(.{ .compute = true, .label = "compute-pass" }); 144 | sg.applyPipeline(state.compute.pip); 145 | sg.applyBindings(state.compute.bind); 146 | sg.applyUniforms(shd.UB_cs_params, sg.asRange(&cs_params)); 147 | sg.dispatch(@intCast((state.num_particles + 63) / 64), 1, 1); 148 | sg.endPass(); 149 | 150 | // render pass to render the particles via instancing, with the 151 | // instance positions coming from the storage buffer 152 | state.ry += 60.0 * dt; 153 | const vs_params = computeVsParams(1.0, state.ry); 154 | sg.beginPass(.{ 155 | .action = state.display.pass_action, 156 | .swapchain = sglue.swapchain(), 157 | .label = "render-pass", 158 | }); 159 | sg.applyPipeline(state.display.pip); 160 | sg.applyBindings(state.display.bind); 161 | sg.applyUniforms(shd.UB_vs_params, sg.asRange(&vs_params)); 162 | sg.draw(0, 24, @intCast(state.num_particles)); 163 | sg.endPass(); 164 | sg.commit(); 165 | } 166 | 167 | export fn cleanup() void { 168 | sg.shutdown(); 169 | } 170 | 171 | pub fn main() void { 172 | sapp.run(.{ 173 | .init_cb = init, 174 | .frame_cb = frame, 175 | .cleanup_cb = cleanup, 176 | .width = 800, 177 | .height = 600, 178 | .sample_count = 4, 179 | .icon = .{ .sokol_default = true }, 180 | .window_title = "instancing-compute.zig", 181 | .logger = .{ .func = slog.func }, 182 | }); 183 | } 184 | 185 | fn computeVsParams(rx: f32, ry: f32) shd.VsParams { 186 | const rxm = mat4.rotate(rx, .{ .x = 1.0, .y = 0.0, .z = 0.0 }); 187 | const rym = mat4.rotate(ry, .{ .x = 0.0, .y = 1.0, .z = 0.0 }); 188 | const model = mat4.mul(rxm, rym); 189 | const aspect = sapp.widthf() / sapp.heightf(); 190 | const proj = mat4.persp(60.0, aspect, 0.01, 50.0); 191 | return shd.VsParams{ .mvp = mat4.mul(mat4.mul(proj, state.display.view), model) }; 192 | } 193 | 194 | fn drawFallback() void { 195 | sg.beginPass(.{ .action = state.display.pass_action, .swapchain = sglue.swapchain(), .label = "render-pass" }); 196 | sg.endPass(); 197 | sg.commit(); 198 | } 199 | -------------------------------------------------------------------------------- /examples/instancing.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // instancing.zig 3 | // 4 | // Demonstrate simple hardware-instancing using a static geometry buffer 5 | // and a dynamic instance-data buffer. 6 | //------------------------------------------------------------------------------ 7 | const sokol = @import("sokol"); 8 | const slog = sokol.log; 9 | const sg = sokol.gfx; 10 | const sapp = sokol.app; 11 | const sglue = sokol.glue; 12 | const vec3 = @import("math.zig").Vec3; 13 | const mat4 = @import("math.zig").Mat4; 14 | const shd = @import("shaders/instancing.glsl.zig"); 15 | 16 | const max_particles: usize = 512 * 1024; 17 | const num_particles_emitted_per_frame: usize = 10; 18 | 19 | const state = struct { 20 | var pass_action: sg.PassAction = .{}; 21 | var pip: sg.Pipeline = .{}; 22 | var bind: sg.Bindings = .{}; 23 | var ry: f32 = 0.0; 24 | var cur_num_particles: u32 = 0; 25 | // view matrix doesn't change 26 | const view: mat4 = mat4.lookat(.{ .x = 0.0, .y = 1.5, .z = 12.0 }, vec3.zero(), vec3.up()); 27 | // un-initialized particle buffer to not bloat the executable 28 | var pos: [max_particles]vec3 = undefined; 29 | var vel: [max_particles]vec3 = undefined; 30 | }; 31 | 32 | export fn init() void { 33 | sg.setup(.{ 34 | .environment = sglue.environment(), 35 | .logger = .{ .func = slog.func }, 36 | }); 37 | 38 | // pass action to clear frame buffer to black 39 | state.pass_action.colors[0] = .{ 40 | .load_action = .CLEAR, 41 | .clear_value = .{ .r = 0, .g = 0, .b = 0, .a = 1 }, 42 | }; 43 | 44 | // a vertex buffer for the static particle geometry, goes into vertex buffer slot 0 45 | const r = 0.05; 46 | state.bind.vertex_buffers[0] = sg.makeBuffer(.{ 47 | .data = sg.asRange(&[_]f32{ 48 | 0.0, -r, 0.0, 1.0, 0.0, 0.0, 1.0, 49 | r, 0.0, r, 0.0, 1.0, 0.0, 1.0, 50 | r, 0.0, -r, 0.0, 0.0, 1.0, 1.0, 51 | -r, 0.0, -r, 1.0, 1.0, 0.0, 1.0, 52 | -r, 0.0, r, 0.0, 1.0, 1.0, 1.0, 53 | 0.0, r, 0.0, 1.0, 0.0, 1.0, 1.0, 54 | }), 55 | }); 56 | 57 | // an index buffer for the static geometry 58 | state.bind.index_buffer = sg.makeBuffer(.{ 59 | .usage = .{ .index_buffer = true }, 60 | .data = sg.asRange(&[_]u16{ 61 | 2, 1, 0, 3, 2, 0, 62 | 4, 3, 0, 1, 4, 0, 63 | 5, 1, 2, 5, 2, 3, 64 | 5, 3, 4, 5, 4, 1, 65 | }), 66 | }); 67 | 68 | // an empty dynamic vertex buffer for the instancing data, goes in vertex buffer slot 1 69 | state.bind.vertex_buffers[1] = sg.makeBuffer(.{ 70 | .usage = .{ .stream_update = true }, 71 | .size = max_particles * @sizeOf(vec3), 72 | }); 73 | 74 | // shader and pipeline object 75 | // NOTE how the vertex layout is setup for instancing, with the instancing 76 | // data provided by buffer-slot 1: 77 | state.pip = sg.makePipeline(.{ 78 | .shader = sg.makeShader(shd.instancingShaderDesc(sg.queryBackend())), 79 | .layout = init: { 80 | var l = sg.VertexLayoutState{}; 81 | l.buffers[1].step_func = .PER_INSTANCE; 82 | l.attrs[shd.ATTR_instancing_pos] = .{ .format = .FLOAT3, .buffer_index = 0 }; // positions 83 | l.attrs[shd.ATTR_instancing_color0] = .{ .format = .FLOAT4, .buffer_index = 0 }; // colors 84 | l.attrs[shd.ATTR_instancing_inst_pos] = .{ .format = .FLOAT3, .buffer_index = 1 }; // instance positions 85 | break :init l; 86 | }, 87 | .index_type = .UINT16, 88 | .cull_mode = .BACK, 89 | .depth = .{ 90 | .compare = .LESS_EQUAL, 91 | .write_enabled = true, 92 | }, 93 | }); 94 | } 95 | 96 | export fn frame() void { 97 | const frame_time: f32 = @floatCast(sapp.frameDuration()); 98 | 99 | // emit new particles 100 | for (0..num_particles_emitted_per_frame) |_| { 101 | if (state.cur_num_particles < max_particles) { 102 | state.pos[state.cur_num_particles] = vec3.zero(); 103 | state.vel[state.cur_num_particles] = .{ 104 | .x = rand(-0.5, 0.5), 105 | .y = rand(2.0, 2.5), 106 | .z = rand(-0.5, 0.5), 107 | }; 108 | state.cur_num_particles += 1; 109 | } else { 110 | break; 111 | } 112 | } 113 | 114 | // update particle positions 115 | for (0..max_particles) |i| { 116 | const vel = &state.vel[i]; 117 | const pos = &state.pos[i]; 118 | vel.y -= 1.0 * frame_time; 119 | pos.* = vec3.add(pos.*, vec3.mul(vel.*, frame_time)); 120 | if (pos.y < -2.0) { 121 | pos.y = -1.8; 122 | vel.y = -vel.y; 123 | vel.* = vec3.mul(vel.*, 0.8); 124 | } 125 | } 126 | 127 | // update instance data 128 | sg.updateBuffer(state.bind.vertex_buffers[1], sg.asRange(state.pos[0..state.cur_num_particles])); 129 | 130 | // compute vertex shader parameters (the mvp matrix) 131 | state.ry += 1.0; 132 | const vs_params = computeVsParams(1.0, state.ry); 133 | 134 | // and finally draw... 135 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 136 | sg.applyPipeline(state.pip); 137 | sg.applyBindings(state.bind); 138 | sg.applyUniforms(shd.UB_vs_params, sg.asRange(&vs_params)); 139 | sg.draw(0, 24, state.cur_num_particles); 140 | sg.endPass(); 141 | sg.commit(); 142 | } 143 | 144 | export fn cleanup() void { 145 | sg.shutdown(); 146 | } 147 | 148 | pub fn main() void { 149 | sapp.run(.{ 150 | .init_cb = init, 151 | .frame_cb = frame, 152 | .cleanup_cb = cleanup, 153 | .width = 800, 154 | .height = 600, 155 | .sample_count = 4, 156 | .icon = .{ .sokol_default = true }, 157 | .window_title = "instancing.zig", 158 | .logger = .{ .func = slog.func }, 159 | }); 160 | } 161 | 162 | fn computeVsParams(rx: f32, ry: f32) shd.VsParams { 163 | const rxm = mat4.rotate(rx, .{ .x = 1.0, .y = 0.0, .z = 0.0 }); 164 | const rym = mat4.rotate(ry, .{ .x = 0.0, .y = 1.0, .z = 0.0 }); 165 | const model = mat4.mul(rxm, rym); 166 | const aspect = sapp.widthf() / sapp.heightf(); 167 | const proj = mat4.persp(60.0, aspect, 0.01, 50.0); 168 | return shd.VsParams{ .mvp = mat4.mul(mat4.mul(proj, state.view), model) }; 169 | } 170 | 171 | fn xorshift32() u32 { 172 | const static = struct { 173 | var x: u32 = 0x12345678; 174 | }; 175 | var x = static.x; 176 | x ^= x << 13; 177 | x ^= x >> 17; 178 | x ^= x << 5; 179 | static.x = x; 180 | return x; 181 | } 182 | 183 | fn rand(min_val: f32, max_val: f32) f32 { 184 | return (@as(f32, @floatFromInt(xorshift32() & 0xFFFF)) / 0x10000) * (max_val - min_val) + min_val; 185 | } 186 | -------------------------------------------------------------------------------- /examples/math.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // math.zig 3 | // 4 | // minimal vector math helper functions, just the stuff needed for 5 | // the sokol-samples 6 | // 7 | // Ported from HandmadeMath.h 8 | //------------------------------------------------------------------------------ 9 | const assert = @import("std").debug.assert; 10 | const math = @import("std").math; 11 | 12 | fn radians(deg: f32) f32 { 13 | return deg * (math.pi / 180.0); 14 | } 15 | 16 | pub const Vec2 = extern struct { 17 | x: f32, 18 | y: f32, 19 | 20 | pub fn zero() Vec2 { 21 | return Vec2{ .x = 0.0, .y = 0.0 }; 22 | } 23 | 24 | pub fn new(x: f32, y: f32) Vec2 { 25 | return Vec2{ .x = x, .y = y }; 26 | } 27 | }; 28 | 29 | pub const Vec3 = extern struct { 30 | x: f32, 31 | y: f32, 32 | z: f32, 33 | 34 | pub fn zero() Vec3 { 35 | return Vec3{ .x = 0.0, .y = 0.0, .z = 0.0 }; 36 | } 37 | 38 | pub fn new(x: f32, y: f32, z: f32) Vec3 { 39 | return Vec3{ .x = x, .y = y, .z = z }; 40 | } 41 | 42 | pub fn up() Vec3 { 43 | return Vec3{ .x = 0.0, .y = 1.0, .z = 0.0 }; 44 | } 45 | 46 | pub fn len(v: Vec3) f32 { 47 | return math.sqrt(Vec3.dot(v, v)); 48 | } 49 | 50 | pub fn add(left: Vec3, right: Vec3) Vec3 { 51 | return Vec3{ .x = left.x + right.x, .y = left.y + right.y, .z = left.z + right.z }; 52 | } 53 | 54 | pub fn sub(left: Vec3, right: Vec3) Vec3 { 55 | return Vec3{ .x = left.x - right.x, .y = left.y - right.y, .z = left.z - right.z }; 56 | } 57 | 58 | pub fn mul(v: Vec3, s: f32) Vec3 { 59 | return Vec3{ .x = v.x * s, .y = v.y * s, .z = v.z * s }; 60 | } 61 | 62 | pub fn norm(v: Vec3) Vec3 { 63 | const l = Vec3.len(v); 64 | if (l != 0.0) { 65 | return Vec3{ .x = v.x / l, .y = v.y / l, .z = v.z / l }; 66 | } else { 67 | return Vec3.zero(); 68 | } 69 | } 70 | 71 | pub fn cross(v0: Vec3, v1: Vec3) Vec3 { 72 | return Vec3{ .x = (v0.y * v1.z) - (v0.z * v1.y), .y = (v0.z * v1.x) - (v0.x * v1.z), .z = (v0.x * v1.y) - (v0.y * v1.x) }; 73 | } 74 | 75 | pub fn dot(v0: Vec3, v1: Vec3) f32 { 76 | return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z; 77 | } 78 | }; 79 | 80 | pub const Mat4 = extern struct { 81 | m: [4][4]f32, 82 | 83 | pub fn identity() Mat4 { 84 | return Mat4{ 85 | .m = [_][4]f32{ .{ 1.0, 0.0, 0.0, 0.0 }, .{ 0.0, 1.0, 0.0, 0.0 }, .{ 0.0, 0.0, 1.0, 0.0 }, .{ 0.0, 0.0, 0.0, 1.0 } }, 86 | }; 87 | } 88 | 89 | pub fn zero() Mat4 { 90 | return Mat4{ 91 | .m = [_][4]f32{ .{ 0.0, 0.0, 0.0, 0.0 }, .{ 0.0, 0.0, 0.0, 0.0 }, .{ 0.0, 0.0, 0.0, 0.0 }, .{ 0.0, 0.0, 0.0, 0.0 } }, 92 | }; 93 | } 94 | 95 | pub fn mul(left: Mat4, right: Mat4) Mat4 { 96 | var res = Mat4.zero(); 97 | for (0..4) |col| { 98 | for (0..4) |row| { 99 | res.m[col][row] = left.m[0][row] * right.m[col][0] + 100 | left.m[1][row] * right.m[col][1] + 101 | left.m[2][row] * right.m[col][2] + 102 | left.m[3][row] * right.m[col][3]; 103 | } 104 | } 105 | return res; 106 | } 107 | 108 | pub fn persp(fov: f32, aspect: f32, near: f32, far: f32) Mat4 { 109 | var res = Mat4.identity(); 110 | const t = math.tan(fov * (math.pi / 360.0)); 111 | res.m[0][0] = 1.0 / t; 112 | res.m[1][1] = aspect / t; 113 | res.m[2][3] = -1.0; 114 | res.m[2][2] = (near + far) / (near - far); 115 | res.m[3][2] = (2.0 * near * far) / (near - far); 116 | res.m[3][3] = 0.0; 117 | return res; 118 | } 119 | 120 | pub fn lookat(eye: Vec3, center: Vec3, up: Vec3) Mat4 { 121 | var res = Mat4.zero(); 122 | 123 | const f = Vec3.norm(Vec3.sub(center, eye)); 124 | const s = Vec3.norm(Vec3.cross(f, up)); 125 | const u = Vec3.cross(s, f); 126 | 127 | res.m[0][0] = s.x; 128 | res.m[0][1] = u.x; 129 | res.m[0][2] = -f.x; 130 | 131 | res.m[1][0] = s.y; 132 | res.m[1][1] = u.y; 133 | res.m[1][2] = -f.y; 134 | 135 | res.m[2][0] = s.z; 136 | res.m[2][1] = u.z; 137 | res.m[2][2] = -f.z; 138 | 139 | res.m[3][0] = -Vec3.dot(s, eye); 140 | res.m[3][1] = -Vec3.dot(u, eye); 141 | res.m[3][2] = Vec3.dot(f, eye); 142 | res.m[3][3] = 1.0; 143 | 144 | return res; 145 | } 146 | 147 | pub fn rotate(angle: f32, axis_unorm: Vec3) Mat4 { 148 | var res = Mat4.identity(); 149 | 150 | const axis = Vec3.norm(axis_unorm); 151 | const sin_theta = math.sin(radians(angle)); 152 | const cos_theta = math.cos(radians(angle)); 153 | const cos_value = 1.0 - cos_theta; 154 | 155 | res.m[0][0] = (axis.x * axis.x * cos_value) + cos_theta; 156 | res.m[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); 157 | res.m[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); 158 | res.m[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); 159 | res.m[1][1] = (axis.y * axis.y * cos_value) + cos_theta; 160 | res.m[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); 161 | res.m[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); 162 | res.m[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); 163 | res.m[2][2] = (axis.z * axis.z * cos_value) + cos_theta; 164 | 165 | return res; 166 | } 167 | 168 | pub fn translate(translation: Vec3) Mat4 { 169 | var res = Mat4.identity(); 170 | res.m[3][0] = translation.x; 171 | res.m[3][1] = translation.y; 172 | res.m[3][2] = translation.z; 173 | return res; 174 | } 175 | }; 176 | 177 | test "Vec3.zero" { 178 | const v = Vec3.zero(); 179 | assert(v.x == 0.0 and v.y == 0.0 and v.z == 0.0); 180 | } 181 | 182 | test "Vec3.new" { 183 | const v = Vec3.new(1.0, 2.0, 3.0); 184 | assert(v.x == 1.0 and v.y == 2.0 and v.z == 3.0); 185 | } 186 | 187 | test "Mat4.ident" { 188 | const m = Mat4.identity(); 189 | for (m.m, 0..) |row, y| { 190 | for (row, 0..) |val, x| { 191 | if (x == y) { 192 | assert(val == 1.0); 193 | } else { 194 | assert(val == 0.0); 195 | } 196 | } 197 | } 198 | } 199 | 200 | test "Mat4.mul" { 201 | const l = Mat4.identity(); 202 | const r = Mat4.identity(); 203 | const m = Mat4.mul(l, r); 204 | for (m.m, 0..) |row, y| { 205 | for (row, 0..) |val, x| { 206 | if (x == y) { 207 | assert(val == 1.0); 208 | } else { 209 | assert(val == 0.0); 210 | } 211 | } 212 | } 213 | } 214 | 215 | fn eq(val: f32, cmp: f32) bool { 216 | const delta: f32 = 0.00001; 217 | return (val > (cmp - delta)) and (val < (cmp + delta)); 218 | } 219 | 220 | test "Mat4.persp" { 221 | const m = Mat4.persp(60.0, 1.33333337, 0.01, 10.0); 222 | 223 | assert(eq(m.m[0][0], 1.73205)); 224 | assert(eq(m.m[0][1], 0.0)); 225 | assert(eq(m.m[0][2], 0.0)); 226 | assert(eq(m.m[0][3], 0.0)); 227 | 228 | assert(eq(m.m[1][0], 0.0)); 229 | assert(eq(m.m[1][1], 2.30940)); 230 | assert(eq(m.m[1][2], 0.0)); 231 | assert(eq(m.m[1][3], 0.0)); 232 | 233 | assert(eq(m.m[2][0], 0.0)); 234 | assert(eq(m.m[2][1], 0.0)); 235 | assert(eq(m.m[2][2], -1.00200)); 236 | assert(eq(m.m[2][3], -1.0)); 237 | 238 | assert(eq(m.m[3][0], 0.0)); 239 | assert(eq(m.m[3][1], 0.0)); 240 | assert(eq(m.m[3][2], -0.02002)); 241 | assert(eq(m.m[3][3], 0.0)); 242 | } 243 | 244 | test "Mat4.lookat" { 245 | const m = Mat4.lookat(.{ .x = 0.0, .y = 1.5, .z = 6.0 }, Vec3.zero(), Vec3.up()); 246 | 247 | assert(eq(m.m[0][0], 1.0)); 248 | assert(eq(m.m[0][1], 0.0)); 249 | assert(eq(m.m[0][2], 0.0)); 250 | assert(eq(m.m[0][3], 0.0)); 251 | 252 | assert(eq(m.m[1][0], 0.0)); 253 | assert(eq(m.m[1][1], 0.97014)); 254 | assert(eq(m.m[1][2], 0.24253)); 255 | assert(eq(m.m[1][3], 0.0)); 256 | 257 | assert(eq(m.m[2][0], 0.0)); 258 | assert(eq(m.m[2][1], -0.24253)); 259 | assert(eq(m.m[2][2], 0.97014)); 260 | assert(eq(m.m[2][3], 0.0)); 261 | 262 | assert(eq(m.m[3][0], 0.0)); 263 | assert(eq(m.m[3][1], 0.0)); 264 | assert(eq(m.m[3][2], -6.18465)); 265 | assert(eq(m.m[3][3], 1.0)); 266 | } 267 | 268 | test "Mat4.rotate" { 269 | const m = Mat4.rotate(2.0, .{ .x = 0.0, .y = 1.0, .z = 0.0 }); 270 | 271 | assert(eq(m.m[0][0], 0.99939)); 272 | assert(eq(m.m[0][1], 0.0)); 273 | assert(eq(m.m[0][2], -0.03489)); 274 | assert(eq(m.m[0][3], 0.0)); 275 | 276 | assert(eq(m.m[1][0], 0.0)); 277 | assert(eq(m.m[1][1], 1.0)); 278 | assert(eq(m.m[1][2], 0.0)); 279 | assert(eq(m.m[1][3], 0.0)); 280 | 281 | assert(eq(m.m[2][0], 0.03489)); 282 | assert(eq(m.m[2][1], 0.0)); 283 | assert(eq(m.m[2][2], 0.99939)); 284 | assert(eq(m.m[2][3], 0.0)); 285 | 286 | assert(eq(m.m[3][0], 0.0)); 287 | assert(eq(m.m[3][1], 0.0)); 288 | assert(eq(m.m[3][2], 0.0)); 289 | assert(eq(m.m[3][3], 1.0)); 290 | } 291 | -------------------------------------------------------------------------------- /examples/mrt.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // mrt.zig 3 | // 4 | // Rendering with multiple-rendertargets, and reallocate render targets 5 | // on window resize events. 6 | // 7 | // NOTE: the rotation direction will appear different on the different 8 | // backend 3D APIs. This is because of the different image origin conventions 9 | // in GL vs D3D vs Metal. We don't care about those differences in this sample 10 | // (using the sokol shader compiler allows to easily 'normalize' those differences. 11 | //------------------------------------------------------------------------------ 12 | const math = @import("std").math; 13 | const sokol = @import("sokol"); 14 | const slog = sokol.log; 15 | const sg = sokol.gfx; 16 | const sapp = sokol.app; 17 | const sglue = sokol.glue; 18 | const vec2 = @import("math.zig").Vec2; 19 | const vec3 = @import("math.zig").Vec3; 20 | const mat4 = @import("math.zig").Mat4; 21 | const shd = @import("shaders/mrt.glsl.zig"); 22 | 23 | const offscreen_sample_count = 1; 24 | 25 | const state = struct { 26 | const offscreen = struct { 27 | var pass_action: sg.PassAction = .{}; 28 | var attachments_desc: sg.AttachmentsDesc = .{}; 29 | var attachments: sg.Attachments = .{}; 30 | var pip: sg.Pipeline = .{}; 31 | var bind: sg.Bindings = .{}; 32 | }; 33 | const fsq = struct { 34 | var pip: sg.Pipeline = .{}; 35 | var bind: sg.Bindings = .{}; 36 | }; 37 | const dbg = struct { 38 | var pip: sg.Pipeline = .{}; 39 | var bind: sg.Bindings = .{}; 40 | }; 41 | const default = struct { 42 | var pass_action: sg.PassAction = .{}; 43 | }; 44 | var rx: f32 = 0.0; 45 | var ry: f32 = 0.0; 46 | const view: mat4 = mat4.lookat(.{ .x = 0.0, .y = 1.5, .z = 6.0 }, vec3.zero(), vec3.up()); 47 | }; 48 | 49 | export fn init() void { 50 | sg.setup(.{ 51 | .environment = sglue.environment(), 52 | .logger = .{ .func = slog.func }, 53 | }); 54 | 55 | // setup pass action for default render pass 56 | state.default.pass_action.colors[0] = .{ .load_action = .DONTCARE }; 57 | state.default.pass_action.depth = .{ .load_action = .DONTCARE }; 58 | state.default.pass_action.stencil = .{ .load_action = .DONTCARE }; 59 | 60 | // set pass action for offscreen render pass 61 | state.offscreen.pass_action.colors[0] = .{ 62 | .load_action = .CLEAR, 63 | .clear_value = .{ .r = 0.25, .g = 0, .b = 0, .a = 1 }, 64 | }; 65 | state.offscreen.pass_action.colors[1] = .{ 66 | .load_action = .CLEAR, 67 | .clear_value = .{ .r = 0, .g = 0.25, .b = 0, .a = 1 }, 68 | }; 69 | state.offscreen.pass_action.colors[2] = .{ 70 | .load_action = .CLEAR, 71 | .clear_value = .{ .r = 0, .g = 0, .b = 0.25, .a = 1 }, 72 | }; 73 | 74 | // setup the offscreen render pass resources this will also be called when the window resizes 75 | createOffscreenAttachments(sapp.width(), sapp.height()); 76 | 77 | // create vertex buffer for a cube 78 | const cube_vbuf = sg.makeBuffer(.{ 79 | .data = sg.asRange(&[_]f32{ 80 | // positions brightness 81 | -1.0, -1.0, -1.0, 1.0, 82 | 1.0, -1.0, -1.0, 1.0, 83 | 1.0, 1.0, -1.0, 1.0, 84 | -1.0, 1.0, -1.0, 1.0, 85 | 86 | -1.0, -1.0, 1.0, 0.8, 87 | 1.0, -1.0, 1.0, 0.8, 88 | 1.0, 1.0, 1.0, 0.8, 89 | -1.0, 1.0, 1.0, 0.8, 90 | 91 | -1.0, -1.0, -1.0, 0.6, 92 | -1.0, 1.0, -1.0, 0.6, 93 | -1.0, 1.0, 1.0, 0.6, 94 | -1.0, -1.0, 1.0, 0.6, 95 | 96 | 1.0, -1.0, -1.0, 0.4, 97 | 1.0, 1.0, -1.0, 0.4, 98 | 1.0, 1.0, 1.0, 0.4, 99 | 1.0, -1.0, 1.0, 0.4, 100 | 101 | -1.0, -1.0, -1.0, 0.5, 102 | -1.0, -1.0, 1.0, 0.5, 103 | 1.0, -1.0, 1.0, 0.5, 104 | 1.0, -1.0, -1.0, 0.5, 105 | 106 | -1.0, 1.0, -1.0, 0.7, 107 | -1.0, 1.0, 1.0, 0.7, 108 | 1.0, 1.0, 1.0, 0.7, 109 | 1.0, 1.0, -1.0, 0.7, 110 | }), 111 | }); 112 | 113 | // index buffer for a cube 114 | const cube_ibuf = sg.makeBuffer(.{ 115 | .usage = .{ .index_buffer = true }, 116 | .data = sg.asRange(&[_]u16{ 117 | 0, 1, 2, 0, 2, 3, 118 | 6, 5, 4, 7, 6, 4, 119 | 8, 9, 10, 8, 10, 11, 120 | 14, 13, 12, 15, 14, 12, 121 | 16, 17, 18, 16, 18, 19, 122 | 22, 21, 20, 23, 22, 20, 123 | }), 124 | }); 125 | 126 | // resource bindings for offscreen rendering 127 | state.offscreen.bind.vertex_buffers[0] = cube_vbuf; 128 | state.offscreen.bind.index_buffer = cube_ibuf; 129 | 130 | // shader and pipeline state object for rendering cube into MRT render targets 131 | state.offscreen.pip = sg.makePipeline(.{ 132 | .shader = sg.makeShader(shd.offscreenShaderDesc(sg.queryBackend())), 133 | .layout = init: { 134 | var l = sg.VertexLayoutState{}; 135 | l.attrs[shd.ATTR_offscreen_pos].format = .FLOAT3; 136 | l.attrs[shd.ATTR_offscreen_bright0].format = .FLOAT; 137 | break :init l; 138 | }, 139 | .index_type = .UINT16, 140 | .cull_mode = .BACK, 141 | .sample_count = offscreen_sample_count, 142 | .depth = .{ 143 | .pixel_format = .DEPTH, 144 | .compare = .LESS_EQUAL, 145 | .write_enabled = true, 146 | }, 147 | .color_count = 3, 148 | }); 149 | 150 | // a vertex buffer to render a fullscreen quad 151 | const quad_vbuf = sg.makeBuffer(.{ 152 | .data = sg.asRange(&[_]f32{ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0 }), 153 | }); 154 | 155 | // shader and pipeline object to render a fullscreen quad which composes 156 | // the 3 offscreen render targets into the default framebuffer 157 | state.fsq.pip = sg.makePipeline(.{ 158 | .shader = sg.makeShader(shd.fsqShaderDesc(sg.queryBackend())), 159 | .layout = init: { 160 | var l = sg.VertexLayoutState{}; 161 | l.attrs[shd.ATTR_fsq_pos].format = .FLOAT2; 162 | break :init l; 163 | }, 164 | .primitive_type = .TRIANGLE_STRIP, 165 | }); 166 | 167 | // a sampler to sample the offscreen render target as texture 168 | const smp = sg.makeSampler(.{ 169 | .min_filter = .LINEAR, 170 | .mag_filter = .LINEAR, 171 | .wrap_u = .CLAMP_TO_EDGE, 172 | .wrap_v = .CLAMP_TO_EDGE, 173 | }); 174 | 175 | // resource bindings to render the fullscreen quad (composed from the 176 | // offscreen render target textures 177 | state.fsq.bind.vertex_buffers[0] = quad_vbuf; 178 | for (0..2) |i| { 179 | state.fsq.bind.images[i] = state.offscreen.attachments_desc.colors[i].image; 180 | } 181 | state.fsq.bind.samplers[shd.SMP_smp] = smp; 182 | 183 | // shader, pipeline and resource bindings to render debug visualization quads 184 | state.dbg.pip = sg.makePipeline(.{ 185 | .shader = sg.makeShader(shd.dbgShaderDesc(sg.queryBackend())), 186 | .layout = init: { 187 | var l = sg.VertexLayoutState{}; 188 | l.attrs[shd.ATTR_dbg_pos].format = .FLOAT2; 189 | break :init l; 190 | }, 191 | .primitive_type = .TRIANGLE_STRIP, 192 | }); 193 | 194 | // resource bindings to render the debug visualization 195 | // (the required images will be filled in during rendering) 196 | state.dbg.bind.vertex_buffers[0] = quad_vbuf; 197 | state.dbg.bind.samplers[shd.SMP_smp] = smp; 198 | } 199 | 200 | export fn frame() void { 201 | const dt: f32 = @floatCast(sapp.frameDuration() * 60.0); 202 | state.rx += 1.0 * dt; 203 | state.ry += 2.0 * dt; 204 | 205 | // compute shader uniform data 206 | const offscreen_params: shd.OffscreenParams = .{ .mvp = computeMVP(state.rx, state.ry) }; 207 | const fsq_params: shd.FsqParams = .{ 208 | .offset = .{ 209 | .x = math.sin(state.rx * 0.01) * 0.1, 210 | .y = math.cos(state.ry * 0.01) * 0.1, 211 | }, 212 | }; 213 | 214 | // render cube into MRT offscreen render targets 215 | sg.beginPass(.{ .action = state.offscreen.pass_action, .attachments = state.offscreen.attachments }); 216 | sg.applyPipeline(state.offscreen.pip); 217 | sg.applyBindings(state.offscreen.bind); 218 | sg.applyUniforms(shd.UB_offscreen_params, sg.asRange(&offscreen_params)); 219 | sg.draw(0, 36, 1); 220 | sg.endPass(); 221 | 222 | // render fullscreen quad with the composed offscreen-render images, 223 | // 3 a small debug view quads at the bottom of the screen 224 | sg.beginPass(.{ .action = state.default.pass_action, .swapchain = sglue.swapchain() }); 225 | sg.applyPipeline(state.fsq.pip); 226 | sg.applyBindings(state.fsq.bind); 227 | sg.applyUniforms(shd.UB_fsq_params, sg.asRange(&fsq_params)); 228 | sg.draw(0, 4, 1); 229 | sg.applyPipeline(state.dbg.pip); 230 | inline for (0..3) |i| { 231 | sg.applyViewport(i * 100, 0, 100, 100, false); 232 | state.dbg.bind.images[shd.IMG_tex] = state.offscreen.attachments_desc.colors[i].image; 233 | sg.applyBindings(state.dbg.bind); 234 | sg.draw(0, 4, 1); 235 | } 236 | sg.endPass(); 237 | sg.commit(); 238 | } 239 | 240 | export fn cleanup() void { 241 | sg.shutdown(); 242 | } 243 | 244 | export fn event(ev: [*c]const sapp.Event) void { 245 | if (ev.*.type == .RESIZED) { 246 | createOffscreenAttachments(ev.*.framebuffer_width, ev.*.framebuffer_height); 247 | } 248 | } 249 | 250 | pub fn main() void { 251 | sapp.run(.{ 252 | .init_cb = init, 253 | .frame_cb = frame, 254 | .cleanup_cb = cleanup, 255 | .event_cb = event, 256 | .width = 800, 257 | .height = 600, 258 | .sample_count = 4, 259 | .icon = .{ .sokol_default = true }, 260 | .window_title = "mrt.zig", 261 | .logger = .{ .func = slog.func }, 262 | }); 263 | } 264 | 265 | // compute model-view-projection matrix 266 | fn computeMVP(rx: f32, ry: f32) mat4 { 267 | const rxm = mat4.rotate(rx, .{ .x = 1.0, .y = 0.0, .z = 0.0 }); 268 | const rym = mat4.rotate(ry, .{ .x = 0.0, .y = 1.0, .z = 0.0 }); 269 | const model = mat4.mul(rxm, rym); 270 | const aspect = sapp.widthf() / sapp.heightf(); 271 | const proj = mat4.persp(60.0, aspect, 0.01, 10.0); 272 | return mat4.mul(mat4.mul(proj, state.view), model); 273 | } 274 | 275 | // helper function to create or re-create render target images and pass object for offscreen rendering 276 | fn createOffscreenAttachments(width: i32, height: i32) void { 277 | // destroy previous resources (can be called with invalid ids) 278 | sg.destroyAttachments(state.offscreen.attachments); 279 | for (state.offscreen.attachments_desc.colors) |att| { 280 | sg.destroyImage(att.image); 281 | } 282 | sg.destroyImage(state.offscreen.attachments_desc.depth_stencil.image); 283 | 284 | // create offscreen render target images and pass 285 | const color_img_desc: sg.ImageDesc = .{ 286 | .usage = .{ .render_attachment = true }, 287 | .width = width, 288 | .height = height, 289 | .sample_count = offscreen_sample_count, 290 | }; 291 | var depth_img_desc = color_img_desc; 292 | depth_img_desc.pixel_format = .DEPTH; 293 | 294 | for (0..3) |i| { 295 | state.offscreen.attachments_desc.colors[i].image = sg.makeImage(color_img_desc); 296 | } 297 | state.offscreen.attachments_desc.depth_stencil.image = sg.makeImage(depth_img_desc); 298 | state.offscreen.attachments = sg.makeAttachments(state.offscreen.attachments_desc); 299 | 300 | // update the fullscreen-quad texture bindings 301 | for (0..3) |i| { 302 | state.fsq.bind.images[i] = state.offscreen.attachments_desc.colors[i].image; 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /examples/noninterleaved.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // noninterleaved.zig 3 | // 4 | // How to use non-interleaved vertex data (vertex components in 5 | // separate non-interleaved chunks in the same vertex buffers). Note 6 | // that only 4 separate chunks are currently possible because there 7 | // are 4 vertex buffer bind slots in sg_bindings, but you can keep 8 | // several related vertex components interleaved in the same chunk. 9 | //------------------------------------------------------------------------------ 10 | const sokol = @import("sokol"); 11 | const slog = sokol.log; 12 | const sg = sokol.gfx; 13 | const sapp = sokol.app; 14 | const sglue = sokol.glue; 15 | const vec3 = @import("math.zig").Vec3; 16 | const mat4 = @import("math.zig").Mat4; 17 | const shd = @import("shaders/noninterleaved.glsl.zig"); 18 | 19 | const state = struct { 20 | var rx: f32 = 0.0; 21 | var ry: f32 = 0.0; 22 | var pip: sg.Pipeline = .{}; 23 | var bind: sg.Bindings = .{}; 24 | var pass_action: sg.PassAction = .{}; 25 | // the view matrix doesn't change 26 | const view: mat4 = mat4.lookat(.{ .x = 0.0, .y = 1.5, .z = 6.0 }, vec3.zero(), vec3.up()); 27 | }; 28 | 29 | export fn init() void { 30 | sg.setup(.{ 31 | .environment = sglue.environment(), 32 | .logger = .{ .func = slog.func }, 33 | }); 34 | 35 | // cube vertex buffer, NOTE how the vertex components are separate 36 | const vbuf = sg.makeBuffer(.{ 37 | .data = sg.asRange(&[_]f32{ 38 | // positions 39 | -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 40 | -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 41 | -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 42 | 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 43 | -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 44 | -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 45 | // colors 46 | 1.0, 0.5, 0.0, 1.0, 1.0, 0.5, 0.0, 1.0, 1.0, 0.5, 0.0, 1.0, 47 | 1.0, 0.5, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0, 48 | 0.5, 1.0, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 1.0, 49 | 0.5, 0.0, 1.0, 1.0, 0.5, 0.0, 1.0, 1.0, 0.5, 0.0, 1.0, 1.0, 50 | 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 51 | 1.0, 0.5, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 52 | 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 1.0, 53 | 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.5, 1.0, 54 | }), 55 | }); 56 | 57 | // cube index buffer 58 | const ibuf = sg.makeBuffer(.{ 59 | .usage = .{ .index_buffer = true }, 60 | .data = sg.asRange(&[_]u16{ 61 | 0, 1, 2, 0, 2, 3, 62 | 6, 5, 4, 7, 6, 4, 63 | 8, 9, 10, 8, 10, 11, 64 | 14, 13, 12, 15, 14, 12, 65 | 16, 17, 18, 16, 18, 19, 66 | 22, 21, 20, 23, 22, 20, 67 | }), 68 | }); 69 | 70 | // shader and pipeline object 71 | state.pip = sg.makePipeline(.{ 72 | .shader = sg.makeShader(shd.noninterleavedShaderDesc(sg.queryBackend())), 73 | // NOTE how the vertex components are pulled from different buffer bind slots 74 | .layout = init: { 75 | var l = sg.VertexLayoutState{}; 76 | l.attrs[shd.ATTR_noninterleaved_position] = .{ .format = .FLOAT3, .buffer_index = 0 }; 77 | l.attrs[shd.ATTR_noninterleaved_color0] = .{ .format = .FLOAT4, .buffer_index = 1 }; 78 | break :init l; 79 | }, 80 | .index_type = .UINT16, 81 | .cull_mode = .BACK, 82 | .depth = .{ 83 | .compare = .LESS_EQUAL, 84 | .write_enabled = true, 85 | }, 86 | }); 87 | 88 | // fill the resource bindings, note how the same vertex 89 | // buffer is bound to the first two slots, and the vertex-buffer-offsets 90 | // are used to point to the position- and color-components. 91 | state.bind.vertex_buffers[0] = vbuf; 92 | state.bind.vertex_buffers[1] = vbuf; 93 | // position vertex components are at the start of the buffer 94 | state.bind.vertex_buffer_offsets[0] = 0; 95 | // color vertex components follow after the positions 96 | state.bind.vertex_buffer_offsets[1] = 24 * 3 * @sizeOf(f32); 97 | state.bind.index_buffer = ibuf; 98 | } 99 | 100 | export fn frame() void { 101 | const dt: f32 = @floatCast(sapp.frameDuration() * 60); 102 | state.rx += 1.0 * dt; 103 | state.ry += 2.0 * dt; 104 | const vs_params = computeVsParams(state.rx, state.ry); 105 | 106 | sg.beginPass(.{ .swapchain = sglue.swapchain() }); 107 | sg.applyPipeline(state.pip); 108 | sg.applyBindings(state.bind); 109 | sg.applyUniforms(shd.UB_vs_params, sg.asRange(&vs_params)); 110 | sg.draw(0, 36, 1); 111 | sg.endPass(); 112 | sg.commit(); 113 | } 114 | 115 | export fn cleanup() void { 116 | sg.shutdown(); 117 | } 118 | 119 | pub fn main() void { 120 | sapp.run(.{ 121 | .init_cb = init, 122 | .frame_cb = frame, 123 | .cleanup_cb = cleanup, 124 | .width = 800, 125 | .height = 600, 126 | .sample_count = 4, 127 | .icon = .{ .sokol_default = true }, 128 | .logger = .{ .func = slog.func }, 129 | .window_title = "noninterleaved.zig", 130 | }); 131 | } 132 | 133 | fn computeVsParams(rx: f32, ry: f32) shd.VsParams { 134 | const rxm = mat4.rotate(rx, .{ .x = 1.0, .y = 0.0, .z = 0.0 }); 135 | const rym = mat4.rotate(ry, .{ .x = 0.0, .y = 1.0, .z = 0.0 }); 136 | const model = mat4.mul(rxm, rym); 137 | const aspect = sapp.widthf() / sapp.heightf(); 138 | const proj = mat4.persp(60.0, aspect, 0.01, 10.0); 139 | return shd.VsParams{ .mvp = mat4.mul(mat4.mul(proj, state.view), model) }; 140 | } 141 | -------------------------------------------------------------------------------- /examples/offscreen.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // offscreen.zig 3 | // 4 | // Render to an offscreen rendertarget texture, and use this texture 5 | // for rendering to the display. 6 | //------------------------------------------------------------------------------ 7 | const sokol = @import("sokol"); 8 | const slog = sokol.log; 9 | const sg = sokol.gfx; 10 | const sapp = sokol.app; 11 | const sglue = sokol.glue; 12 | const sshape = sokol.shape; 13 | const vec3 = @import("math.zig").Vec3; 14 | const mat4 = @import("math.zig").Mat4; 15 | const shd = @import("shaders/offscreen.glsl.zig"); 16 | 17 | const offscreen_sample_count = 1; 18 | 19 | const state = struct { 20 | const offscreen = struct { 21 | var pass_action: sg.PassAction = .{}; 22 | var attachments: sg.Attachments = .{}; 23 | var pip: sg.Pipeline = .{}; 24 | var bind: sg.Bindings = .{}; 25 | }; 26 | const default = struct { 27 | var pass_action: sg.PassAction = .{}; 28 | var pip: sg.Pipeline = .{}; 29 | var bind: sg.Bindings = .{}; 30 | }; 31 | var donut: sshape.ElementRange = .{}; 32 | var sphere: sshape.ElementRange = .{}; 33 | var rx: f32 = 0.0; 34 | var ry: f32 = 0.0; 35 | }; 36 | 37 | export fn init() void { 38 | sg.setup(.{ 39 | .environment = sglue.environment(), 40 | .logger = .{ .func = slog.func }, 41 | }); 42 | 43 | // default pass action: clear to blue-ish 44 | state.default.pass_action.colors[0] = .{ 45 | .load_action = .CLEAR, 46 | .clear_value = .{ .r = 0.25, .g = 0.45, .b = 0.65, .a = 1.0 }, 47 | }; 48 | 49 | // offscreen pass action: clear to black 50 | state.offscreen.pass_action.colors[0] = .{ 51 | .load_action = .CLEAR, 52 | .clear_value = .{ .r = 0.25, .g = 0.25, .b = 0.25, .a = 1.0 }, 53 | }; 54 | 55 | // a render pass with one color- and one depth-attachment image 56 | var img_desc = sg.ImageDesc{ 57 | .usage = .{ .render_attachment = true }, 58 | .width = 256, 59 | .height = 256, 60 | .pixel_format = .RGBA8, 61 | .sample_count = offscreen_sample_count, 62 | }; 63 | const color_img = sg.makeImage(img_desc); 64 | img_desc.pixel_format = .DEPTH; 65 | const depth_img = sg.makeImage(img_desc); 66 | 67 | var atts_desc = sg.AttachmentsDesc{}; 68 | atts_desc.colors[0].image = color_img; 69 | atts_desc.depth_stencil.image = depth_img; 70 | state.offscreen.attachments = sg.makeAttachments(atts_desc); 71 | 72 | // a donut shape which is rendered into the offscreen render target, and 73 | // a sphere shape which is rendered into the default framebuffer 74 | var vertices: [4000]sshape.Vertex = undefined; 75 | var indices: [24000]u16 = undefined; 76 | var buf: sshape.Buffer = .{ 77 | .vertices = .{ .buffer = sshape.asRange(&vertices) }, 78 | .indices = .{ .buffer = sshape.asRange(&indices) }, 79 | }; 80 | buf = sshape.buildTorus(buf, .{ .radius = 0.5, .ring_radius = 0.3, .sides = 20, .rings = 36 }); 81 | state.donut = sshape.elementRange(buf); 82 | buf = sshape.buildSphere(buf, .{ 83 | .radius = 0.5, 84 | .slices = 72, 85 | .stacks = 40, 86 | }); 87 | state.sphere = sshape.elementRange(buf); 88 | 89 | const vbuf = sg.makeBuffer(sshape.vertexBufferDesc(buf)); 90 | const ibuf = sg.makeBuffer(sshape.indexBufferDesc(buf)); 91 | 92 | // shader and pipeline object for offscreen rendering 93 | state.offscreen.pip = sg.makePipeline(.{ 94 | .shader = sg.makeShader(shd.offscreenShaderDesc(sg.queryBackend())), 95 | .layout = init: { 96 | var l = sg.VertexLayoutState{}; 97 | l.buffers[0] = sshape.vertexBufferLayoutState(); 98 | l.attrs[shd.ATTR_offscreen_position] = sshape.positionVertexAttrState(); 99 | l.attrs[shd.ATTR_offscreen_normal] = sshape.normalVertexAttrState(); 100 | break :init l; 101 | }, 102 | .index_type = .UINT16, 103 | .cull_mode = .BACK, 104 | .sample_count = offscreen_sample_count, 105 | .depth = .{ 106 | .pixel_format = .DEPTH, 107 | .compare = .LESS_EQUAL, 108 | .write_enabled = true, 109 | }, 110 | .colors = init: { 111 | var c: [4]sg.ColorTargetState = @splat(.{}); 112 | c[0].pixel_format = .RGBA8; 113 | break :init c; 114 | }, 115 | }); 116 | 117 | // shader and pipeline object for the default render pass 118 | state.default.pip = sg.makePipeline(.{ 119 | .shader = sg.makeShader(shd.defaultShaderDesc(sg.queryBackend())), 120 | .layout = init: { 121 | var l = sg.VertexLayoutState{}; 122 | l.buffers[0] = sshape.vertexBufferLayoutState(); 123 | l.attrs[shd.ATTR_default_position] = sshape.positionVertexAttrState(); 124 | l.attrs[shd.ATTR_default_normal] = sshape.normalVertexAttrState(); 125 | l.attrs[shd.ATTR_default_texcoord0] = sshape.texcoordVertexAttrState(); 126 | break :init l; 127 | }, 128 | .index_type = .UINT16, 129 | .cull_mode = .BACK, 130 | .depth = .{ 131 | .compare = .LESS_EQUAL, 132 | .write_enabled = true, 133 | }, 134 | }); 135 | 136 | // a sampler object for sampling the render target texture 137 | const smp = sg.makeSampler(.{ 138 | .min_filter = .LINEAR, 139 | .mag_filter = .LINEAR, 140 | .wrap_u = .REPEAT, 141 | .wrap_v = .REPEAT, 142 | }); 143 | 144 | // resource bindings to render a non-textured cube (into the offscreen render target) 145 | state.offscreen.bind.vertex_buffers[0] = vbuf; 146 | state.offscreen.bind.index_buffer = ibuf; 147 | 148 | // resource bindings to render a textured cube, using the offscreen render target as texture 149 | state.default.bind.vertex_buffers[0] = vbuf; 150 | state.default.bind.index_buffer = ibuf; 151 | state.default.bind.images[shd.IMG_tex] = color_img; 152 | state.default.bind.samplers[shd.SMP_smp] = smp; 153 | } 154 | 155 | export fn frame() void { 156 | const dt: f32 = @floatCast(sapp.frameDuration() * 60); 157 | state.rx += 1 * dt; 158 | state.ry += 2 * dt; 159 | const aspect = sapp.widthf() / sapp.heightf(); 160 | 161 | // the offscreen pass, rendering a rotating untextured donut into a render target image 162 | sg.beginPass(.{ .action = state.offscreen.pass_action, .attachments = state.offscreen.attachments }); 163 | sg.applyPipeline(state.offscreen.pip); 164 | sg.applyBindings(state.offscreen.bind); 165 | sg.applyUniforms(shd.UB_vs_params, sg.asRange(&computeVsParams(state.rx, state.ry, 1.0, 2.5))); 166 | sg.draw(state.donut.base_element, state.donut.num_elements, 1); 167 | sg.endPass(); 168 | 169 | // and the display pass, rendering a rotating textured sphere, using the previously 170 | // rendered offscreen render target as texture 171 | sg.beginPass(.{ .action = state.default.pass_action, .swapchain = sglue.swapchain() }); 172 | sg.applyPipeline(state.default.pip); 173 | sg.applyBindings(state.default.bind); 174 | sg.applyUniforms(shd.UB_vs_params, sg.asRange(&computeVsParams(-state.rx * 0.25, state.ry * 0.25, aspect, 2))); 175 | sg.draw(state.sphere.base_element, state.sphere.num_elements, 1); 176 | sg.endPass(); 177 | 178 | sg.commit(); 179 | } 180 | 181 | export fn cleanup() void { 182 | sg.shutdown(); 183 | } 184 | 185 | pub fn main() void { 186 | sapp.run(.{ 187 | .init_cb = init, 188 | .frame_cb = frame, 189 | .cleanup_cb = cleanup, 190 | .sample_count = 4, 191 | .width = 800, 192 | .height = 600, 193 | .icon = .{ .sokol_default = true }, 194 | .window_title = "offscreen.zig", 195 | .logger = .{ .func = slog.func }, 196 | }); 197 | } 198 | 199 | fn computeVsParams(rx: f32, ry: f32, aspect: f32, eye_dist: f32) shd.VsParams { 200 | const proj = mat4.persp(45, aspect, 0.01, 10); 201 | const view = mat4.lookat(.{ .x = 0, .y = 0, .z = eye_dist }, vec3.zero(), vec3.up()); 202 | const view_proj = mat4.mul(proj, view); 203 | const rxm = mat4.rotate(rx, .{ .x = 1, .y = 0, .z = 0 }); 204 | const rym = mat4.rotate(ry, .{ .x = 0, .y = 1, .z = 0 }); 205 | const model = mat4.mul(rxm, rym); 206 | return shd.VsParams{ .mvp = mat4.mul(view_proj, model) }; 207 | } 208 | -------------------------------------------------------------------------------- /examples/quad.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // quad.zig 3 | // 4 | // Simple 2D rendering with vertex- and index-buffer. 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const shd = @import("shaders/quad.glsl.zig"); 12 | 13 | const state = struct { 14 | var bind: sg.Bindings = .{}; 15 | var pip: sg.Pipeline = .{}; 16 | var pass_action: sg.PassAction = .{}; 17 | }; 18 | 19 | export fn init() void { 20 | sg.setup(.{ 21 | .environment = sglue.environment(), 22 | .logger = .{ .func = slog.func }, 23 | }); 24 | 25 | // a vertex buffer 26 | state.bind.vertex_buffers[0] = sg.makeBuffer(.{ 27 | .data = sg.asRange(&[_]f32{ 28 | // positions colors 29 | -0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0, 30 | 0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 31 | 0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 32 | -0.5, -0.5, 0.5, 1.0, 1.0, 0.0, 1.0, 33 | }), 34 | }); 35 | 36 | // an index buffer 37 | state.bind.index_buffer = sg.makeBuffer(.{ 38 | .usage = .{ .index_buffer = true }, 39 | .data = sg.asRange(&[_]u16{ 0, 1, 2, 0, 2, 3 }), 40 | }); 41 | 42 | // a shader and pipeline state object 43 | state.pip = sg.makePipeline(.{ 44 | .shader = sg.makeShader(shd.quadShaderDesc(sg.queryBackend())), 45 | .layout = init: { 46 | var l = sg.VertexLayoutState{}; 47 | l.attrs[shd.ATTR_quad_position].format = .FLOAT3; 48 | l.attrs[shd.ATTR_quad_color0].format = .FLOAT4; 49 | break :init l; 50 | }, 51 | .index_type = .UINT16, 52 | }); 53 | 54 | // clear to black 55 | state.pass_action.colors[0] = .{ 56 | .load_action = .CLEAR, 57 | .clear_value = .{ .r = 0, .g = 0, .b = 0, .a = 1 }, 58 | }; 59 | } 60 | 61 | export fn frame() void { 62 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 63 | sg.applyPipeline(state.pip); 64 | sg.applyBindings(state.bind); 65 | sg.draw(0, 6, 1); 66 | sg.endPass(); 67 | sg.commit(); 68 | } 69 | 70 | export fn cleanup() void { 71 | sg.shutdown(); 72 | } 73 | 74 | pub fn main() void { 75 | sapp.run(.{ 76 | .init_cb = init, 77 | .frame_cb = frame, 78 | .cleanup_cb = cleanup, 79 | .width = 640, 80 | .height = 480, 81 | .icon = .{ .sokol_default = true }, 82 | .window_title = "quad.zig", 83 | .logger = .{ .func = slog.func }, 84 | }); 85 | } 86 | -------------------------------------------------------------------------------- /examples/saudio.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // saudio.zig 3 | // Test sokol-audio zig bindings 4 | //------------------------------------------------------------------------------ 5 | const sokol = @import("sokol"); 6 | const slog = sokol.log; 7 | const sg = sokol.gfx; 8 | const sapp = sokol.app; 9 | const saudio = sokol.audio; 10 | const sglue = sokol.glue; 11 | 12 | const NumSamples = 32; 13 | 14 | const state = struct { 15 | var pass_action: sg.PassAction = .{}; 16 | var even_odd: u32 = 0; 17 | var sample_pos: usize = 0; 18 | var samples: [NumSamples]f32 = undefined; 19 | }; 20 | 21 | export fn init() void { 22 | sg.setup(.{ 23 | .environment = sglue.environment(), 24 | .logger = .{ .func = slog.func }, 25 | }); 26 | saudio.setup(.{ 27 | .logger = .{ .func = slog.func }, 28 | }); 29 | state.pass_action.colors[0] = .{ 30 | .load_action = .CLEAR, 31 | .clear_value = .{ .r = 1, .g = 0.5, .b = 0, .a = 1 }, 32 | }; 33 | } 34 | 35 | export fn frame() void { 36 | for (0..@intCast(saudio.expect())) |_| { 37 | if (state.sample_pos == NumSamples) { 38 | state.sample_pos = 0; 39 | _ = saudio.push(&(state.samples[0]), NumSamples); 40 | } 41 | state.samples[state.sample_pos] = if (0 != (state.even_odd & 0x20)) 0.1 else -0.1; 42 | state.even_odd += 1; 43 | state.sample_pos += 1; 44 | } 45 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 46 | sg.endPass(); 47 | sg.commit(); 48 | } 49 | 50 | export fn cleanup() void { 51 | saudio.shutdown(); 52 | sg.shutdown(); 53 | } 54 | 55 | pub fn main() void { 56 | sapp.run(.{ 57 | .init_cb = init, 58 | .frame_cb = frame, 59 | .cleanup_cb = cleanup, 60 | .width = 640, 61 | .height = 480, 62 | .icon = .{ .sokol_default = true }, 63 | .window_title = "saudio.zig", 64 | .logger = .{ .func = slog.func }, 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /examples/sgl-context.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // sgl-context.zig 3 | // 4 | // Demonstrates how to render into different render passes with sokol-gl. 5 | // using contexts. 6 | //------------------------------------------------------------------------------ 7 | const sokol = @import("sokol"); 8 | const slog = sokol.log; 9 | const sg = sokol.gfx; 10 | const sapp = sokol.app; 11 | const sglue = sokol.glue; 12 | const sgl = sokol.gl; 13 | const math = @import("std").math; 14 | 15 | const state = struct { 16 | const offscreen = struct { 17 | var pass_action: sg.PassAction = .{}; 18 | var attachments: sg.Attachments = .{}; 19 | var img: sg.Image = .{}; 20 | var sgl_ctx: sgl.Context = .{}; 21 | }; 22 | const display = struct { 23 | var pass_action: sg.PassAction = .{}; 24 | var smp: sg.Sampler = .{}; 25 | var sgl_pip: sgl.Pipeline = .{}; 26 | }; 27 | }; 28 | 29 | const offscreen_pixel_format = sg.PixelFormat.RGBA8; 30 | const offscreen_sample_count = 1; 31 | const offscreen_width = 32; 32 | const offscreen_height = 32; 33 | 34 | export fn init() void { 35 | // setup sokol-gfx 36 | sg.setup(.{ 37 | .environment = sglue.environment(), 38 | .logger = .{ .func = slog.func }, 39 | }); 40 | 41 | // setup sokol-gl with the default context compatible with the default 42 | // render pass (which means just keep pixelformats and sample count at defaults) 43 | // 44 | // reduce the vertex- and command-count though, otherwise we just waste memory 45 | // 46 | sgl.setup(.{ 47 | .max_vertices = 64, 48 | .max_commands = 16, 49 | .logger = .{ .func = slog.func }, 50 | }); 51 | 52 | // initialize a pass action struct for the default pass to clear to a light-blue color 53 | state.display.pass_action.colors[0] = .{ 54 | .load_action = .CLEAR, 55 | .clear_value = .{ .r = 0.5, .g = 0.7, .b = 1, .a = 1 }, 56 | }; 57 | 58 | // create a sokol-gl pipeline object for 3D rendering into the default pass 59 | state.display.sgl_pip = sgl.contextMakePipeline(sgl.defaultContext(), .{ 60 | .cull_mode = .BACK, 61 | .depth = .{ 62 | .write_enabled = true, 63 | .compare = .LESS_EQUAL, 64 | }, 65 | }); 66 | 67 | // create a sokol-gl context compatible with the offscreen render pass 68 | // (specific color pixel format, no depth-stencil-surface, no MSAA) 69 | state.offscreen.sgl_ctx = sgl.makeContext(.{ 70 | .max_vertices = 8, 71 | .max_commands = 4, 72 | .color_format = offscreen_pixel_format, 73 | .depth_format = .NONE, 74 | .sample_count = offscreen_sample_count, 75 | }); 76 | 77 | // create an offscreen render target texture, pass-attachments object and pass-action 78 | state.offscreen.img = sg.makeImage(.{ 79 | .usage = .{ .render_attachment = true }, 80 | .width = offscreen_width, 81 | .height = offscreen_height, 82 | .pixel_format = offscreen_pixel_format, 83 | .sample_count = offscreen_sample_count, 84 | }); 85 | 86 | var atts_desc = sg.AttachmentsDesc{}; 87 | atts_desc.colors[0].image = state.offscreen.img; 88 | state.offscreen.attachments = sg.makeAttachments(atts_desc); 89 | 90 | state.offscreen.pass_action.colors[0] = .{ 91 | .load_action = .CLEAR, 92 | .clear_value = .{ .r = 0, .g = 0, .b = 0, .a = 1 }, 93 | }; 94 | 95 | // sampler for sampling the offscreen render target 96 | state.display.smp = sg.makeSampler(.{ 97 | .wrap_u = .CLAMP_TO_EDGE, 98 | .wrap_v = .CLAMP_TO_EDGE, 99 | .min_filter = .NEAREST, 100 | .mag_filter = .NEAREST, 101 | }); 102 | } 103 | 104 | export fn frame() void { 105 | const a = sgl.asRadians(@floatFromInt(sapp.frameCount())); 106 | 107 | // draw a rotating quad into the offscreen render target texture 108 | sgl.setContext(state.offscreen.sgl_ctx); 109 | sgl.defaults(); 110 | sgl.matrixModeModelview(); 111 | sgl.rotate(a, 0, 0, 1); 112 | drawQuad(); 113 | 114 | // draw a rotating 3D cube, using the offscreen render target as texture 115 | sgl.setContext(sgl.defaultContext()); 116 | sgl.defaults(); 117 | sgl.enableTexture(); 118 | sgl.texture(state.offscreen.img, state.display.smp); 119 | sgl.loadPipeline(state.display.sgl_pip); 120 | sgl.matrixModeProjection(); 121 | sgl.perspective(sgl.asRadians(45.0), sapp.widthf() / sapp.heightf(), 0.1, 100.0); 122 | const eye = .{ 123 | math.sin(a) * 6.0, 124 | math.sin(a) * 3.0, 125 | math.cos(a) * 6.0, 126 | }; 127 | sgl.matrixModeModelview(); 128 | sgl.lookat(eye[0], eye[1], eye[2], 0, 0, 0, 0, 1, 0); 129 | drawCube(); 130 | 131 | // do the actual offscreen and display rendering in sokol-gfx passes 132 | sg.beginPass(.{ .action = state.offscreen.pass_action, .attachments = state.offscreen.attachments }); 133 | sgl.contextDraw(state.offscreen.sgl_ctx); 134 | sg.endPass(); 135 | sg.beginPass(.{ .action = state.display.pass_action, .swapchain = sglue.swapchain() }); 136 | sgl.contextDraw(sgl.defaultContext()); 137 | sg.endPass(); 138 | sg.commit(); 139 | } 140 | 141 | export fn cleanup() void { 142 | sgl.shutdown(); 143 | sg.shutdown(); 144 | } 145 | 146 | pub fn main() void { 147 | sapp.run(.{ 148 | .init_cb = init, 149 | .frame_cb = frame, 150 | .cleanup_cb = cleanup, 151 | .width = 800, 152 | .height = 600, 153 | .sample_count = 4, 154 | .icon = .{ .sokol_default = true }, 155 | .window_title = "sgl-context.zig", 156 | .logger = .{ .func = slog.func }, 157 | }); 158 | } 159 | 160 | fn drawQuad() void { 161 | sgl.beginQuads(); 162 | sgl.v2fC3b(0.0, -1.0, 255, 0, 0); 163 | sgl.v2fC3b(1.0, 0.0, 0, 0, 255); 164 | sgl.v2fC3b(0.0, 1.0, 0, 255, 255); 165 | sgl.v2fC3b(-1.0, 0.0, 0, 255, 0); 166 | sgl.end(); 167 | } 168 | 169 | fn drawCube() void { 170 | sgl.beginQuads(); 171 | sgl.v3fT2f(-1.0, 1.0, -1.0, 0.0, 1.0); 172 | sgl.v3fT2f(1.0, 1.0, -1.0, 1.0, 1.0); 173 | sgl.v3fT2f(1.0, -1.0, -1.0, 1.0, 0.0); 174 | sgl.v3fT2f(-1.0, -1.0, -1.0, 0.0, 0.0); 175 | sgl.v3fT2f(-1.0, -1.0, 1.0, 0.0, 1.0); 176 | sgl.v3fT2f(1.0, -1.0, 1.0, 1.0, 1.0); 177 | sgl.v3fT2f(1.0, 1.0, 1.0, 1.0, 0.0); 178 | sgl.v3fT2f(-1.0, 1.0, 1.0, 0.0, 0.0); 179 | sgl.v3fT2f(-1.0, -1.0, 1.0, 0.0, 1.0); 180 | sgl.v3fT2f(-1.0, 1.0, 1.0, 1.0, 1.0); 181 | sgl.v3fT2f(-1.0, 1.0, -1.0, 1.0, 0.0); 182 | sgl.v3fT2f(-1.0, -1.0, -1.0, 0.0, 0.0); 183 | sgl.v3fT2f(1.0, -1.0, 1.0, 0.0, 1.0); 184 | sgl.v3fT2f(1.0, -1.0, -1.0, 1.0, 1.0); 185 | sgl.v3fT2f(1.0, 1.0, -1.0, 1.0, 0.0); 186 | sgl.v3fT2f(1.0, 1.0, 1.0, 0.0, 0.0); 187 | sgl.v3fT2f(1.0, -1.0, -1.0, 0.0, 1.0); 188 | sgl.v3fT2f(1.0, -1.0, 1.0, 1.0, 1.0); 189 | sgl.v3fT2f(-1.0, -1.0, 1.0, 1.0, 0.0); 190 | sgl.v3fT2f(-1.0, -1.0, -1.0, 0.0, 0.0); 191 | sgl.v3fT2f(-1.0, 1.0, -1.0, 0.0, 1.0); 192 | sgl.v3fT2f(-1.0, 1.0, 1.0, 1.0, 1.0); 193 | sgl.v3fT2f(1.0, 1.0, 1.0, 1.0, 0.0); 194 | sgl.v3fT2f(1.0, 1.0, -1.0, 0.0, 0.0); 195 | sgl.end(); 196 | } 197 | -------------------------------------------------------------------------------- /examples/sgl-points.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // sgl-points.zig 3 | // 4 | // Test sokol-gl point rendering. 5 | // 6 | // (port of this C sample: https://floooh.github.io/sokol-html5/sgl-points-sapp.html) 7 | //------------------------------------------------------------------------------ 8 | const sokol = @import("sokol"); 9 | const slog = sokol.log; 10 | const sg = sokol.gfx; 11 | const sapp = sokol.app; 12 | const sgl = sokol.gl; 13 | const sglue = sokol.glue; 14 | const math = @import("std").math; 15 | 16 | const Rgb = struct { r: f32, g: f32, b: f32 }; 17 | const state = struct { 18 | var pass_action = sg.PassAction{}; 19 | }; 20 | 21 | const palette = [16]Rgb{ 22 | .{ .r = 0.957, .g = 0.263, .b = 0.212 }, 23 | .{ .r = 0.914, .g = 0.118, .b = 0.388 }, 24 | .{ .r = 0.612, .g = 0.153, .b = 0.690 }, 25 | .{ .r = 0.404, .g = 0.227, .b = 0.718 }, 26 | .{ .r = 0.247, .g = 0.318, .b = 0.710 }, 27 | .{ .r = 0.129, .g = 0.588, .b = 0.953 }, 28 | .{ .r = 0.012, .g = 0.663, .b = 0.957 }, 29 | .{ .r = 0.000, .g = 0.737, .b = 0.831 }, 30 | .{ .r = 0.000, .g = 0.588, .b = 0.533 }, 31 | .{ .r = 0.298, .g = 0.686, .b = 0.314 }, 32 | .{ .r = 0.545, .g = 0.765, .b = 0.290 }, 33 | .{ .r = 0.804, .g = 0.863, .b = 0.224 }, 34 | .{ .r = 1.000, .g = 0.922, .b = 0.231 }, 35 | .{ .r = 1.000, .g = 0.757, .b = 0.027 }, 36 | .{ .r = 1.000, .g = 0.596, .b = 0.000 }, 37 | .{ .r = 1.000, .g = 0.341, .b = 0.133 }, 38 | }; 39 | 40 | export fn init() void { 41 | sg.setup(.{ 42 | .environment = sglue.environment(), 43 | .logger = .{ .func = slog.func }, 44 | }); 45 | sgl.setup(.{ 46 | .logger = .{ .func = slog.func }, 47 | }); 48 | state.pass_action.colors[0] = .{ 49 | .load_action = .CLEAR, 50 | .clear_value = .{ .r = 0, .g = 0, .b = 0 }, 51 | }; 52 | } 53 | 54 | fn computeColor(t: f32) Rgb { 55 | const idx0 = @as(usize, @intFromFloat(t * 16)) % 16; 56 | const idx1 = (idx0 + 1) % 16; 57 | const l = (t * 16) - @floor(t * 16); 58 | const c0 = palette[idx0]; 59 | const c1 = palette[idx1]; 60 | return .{ 61 | .r = (c0.r * (1 - l)) + (c1.r * l), 62 | .g = (c0.g * (1 - l)) + (c1.g * l), 63 | .b = (c0.b * (1 - l)) + (c1.b * l), 64 | }; 65 | } 66 | 67 | export fn frame() void { 68 | const angle: f32 = @floatFromInt(sapp.frameCount() % 360); 69 | 70 | sgl.defaults(); 71 | sgl.beginPoints(); 72 | var psize: f32 = 5; 73 | for (0..300) |i| { 74 | const a = sgl.asRadians(angle + @as(f32, @floatFromInt(i))); 75 | const color = computeColor(@as(f32, @floatFromInt((sapp.frameCount() + i) % 300)) / 300); 76 | const r = math.sin(a * 4.0); 77 | const s = math.sin(a); 78 | const c = math.cos(a); 79 | const x = s * r; 80 | const y = c * r; 81 | sgl.c3f(color.r, color.g, color.b); 82 | sgl.pointSize(psize); 83 | sgl.v2f(x, y); 84 | psize *= 1.005; 85 | } 86 | sgl.end(); 87 | 88 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 89 | sgl.draw(); 90 | sg.endPass(); 91 | sg.commit(); 92 | } 93 | 94 | export fn cleanup() void { 95 | sgl.shutdown(); 96 | sg.shutdown(); 97 | } 98 | 99 | pub fn main() void { 100 | sapp.run(.{ 101 | .init_cb = init, 102 | .frame_cb = frame, 103 | .cleanup_cb = cleanup, 104 | .width = 512, 105 | .height = 512, 106 | .icon = .{ .sokol_default = true }, 107 | .window_title = "sgl-points.zig", 108 | .logger = .{ .func = slog.func }, 109 | }); 110 | } 111 | -------------------------------------------------------------------------------- /examples/sgl.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // sgl.zig 3 | // 4 | // sokol_gl.h / sokol.sgl sample program. 5 | //------------------------------------------------------------------------------ 6 | const std = @import("std"); 7 | const sokol = @import("sokol"); 8 | const slog = sokol.log; 9 | const sg = sokol.gfx; 10 | const sapp = sokol.app; 11 | const sgl = sokol.gl; 12 | const sglue = sokol.glue; 13 | const math = @import("std").math; 14 | 15 | const state = struct { 16 | var pass_action: sg.PassAction = .{}; 17 | var img: sg.Image = .{}; 18 | var smp: sg.Sampler = .{}; 19 | var pip3d: sgl.Pipeline = .{}; 20 | const quad = struct { 21 | var rot: f32 = 0.0; 22 | }; 23 | const cube = struct { 24 | var rot_x: f32 = 0.0; 25 | var rot_y: f32 = 0.0; 26 | }; 27 | const texcube = struct { 28 | var time_accum: f32 = 0.0; 29 | }; 30 | }; 31 | 32 | export fn init() void { 33 | // setup sokol-gfx 34 | sg.setup(.{ 35 | .environment = sglue.environment(), 36 | .logger = .{ .func = slog.func }, 37 | }); 38 | // setup sokol-gl 39 | sgl.setup(.{ 40 | .logger = .{ .func = slog.func }, 41 | }); 42 | 43 | // a checkerboard texture 44 | const img_width = 8; 45 | const img_height = 8; 46 | state.img = sg.makeImage(.{ 47 | .width = img_width, 48 | .height = img_height, 49 | .data = init: { 50 | var data = sg.ImageData{}; 51 | data.subimage[0][0] = sg.asRange(&pixels: { 52 | var res = std.mem.zeroes([img_width][img_height]u32); 53 | for (0..img_height) |y| { 54 | for (0..img_width) |x| { 55 | res[y][x] = if (0 == (y ^ x) & 1) 0xFF_00_00_00 else 0xFF_FF_FF_FF; 56 | } 57 | } 58 | break :pixels res; 59 | }); 60 | break :init data; 61 | }, 62 | }); 63 | 64 | // ...and a sampler 65 | state.smp = sg.makeSampler(.{ 66 | .min_filter = .NEAREST, 67 | .mag_filter = .NEAREST, 68 | }); 69 | 70 | // create a pipeline object for 3d rendering, with less-equal 71 | // depth-test and cull-face enabled, note that we don't provide 72 | // a shader, vertex-layout, pixel formats and sample count here, 73 | // these are all filled in by sokol-gl 74 | state.pip3d = sgl.makePipeline(.{ 75 | .depth = .{ 76 | .write_enabled = true, 77 | .compare = .LESS_EQUAL, 78 | }, 79 | .cull_mode = .BACK, 80 | }); 81 | 82 | // pass-action to clear to black 83 | state.pass_action.colors[0] = .{ 84 | .load_action = .CLEAR, 85 | .clear_value = .{ .r = 0, .g = 0, .b = 0, .a = 1 }, 86 | }; 87 | } 88 | 89 | export fn frame() void { 90 | // frame time 'normalized' to 60fps 91 | const dt: f32 = @floatCast(sapp.frameDuration() * 60); 92 | 93 | // compute viewport rectangles so that the views are horizontally 94 | // centered and keep a 1:1 aspect ratio 95 | const dw = sapp.widthf(); 96 | const dh = sapp.heightf(); 97 | const ww = dh * 0.5; 98 | const hh = dh * 0.5; 99 | const x0 = dw * 0.5 - hh; 100 | const x1 = dw * 0.5; 101 | const y0 = 0.0; 102 | const y1 = dh * 0.5; 103 | 104 | sgl.viewportf(x0, y0, ww, hh, true); 105 | drawTriangle(); 106 | sgl.viewportf(x1, y0, ww, hh, true); 107 | drawQuad(dt); 108 | sgl.viewportf(x0, y1, ww, hh, true); 109 | drawCubes(dt); 110 | sgl.viewportf(x1, y1, ww, hh, true); 111 | drawTexCube(dt); 112 | sgl.viewportf(0, 0, dw, dh, true); 113 | 114 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 115 | sgl.draw(); 116 | sg.endPass(); 117 | sg.commit(); 118 | } 119 | 120 | export fn cleanup() void { 121 | sgl.shutdown(); 122 | sg.shutdown(); 123 | } 124 | 125 | fn drawTriangle() void { 126 | sgl.defaults(); 127 | sgl.beginTriangles(); 128 | sgl.v2fC3b(0.0, 0.5, 255, 0, 0); 129 | sgl.v2fC3b(-0.5, -0.5, 0, 0, 255); 130 | sgl.v2fC3b(0.5, -0.5, 0, 255, 0); 131 | sgl.end(); 132 | } 133 | 134 | fn drawQuad(dt: f32) void { 135 | state.quad.rot += 1.0 * dt; 136 | const scale: f32 = 1.0 + math.sin(sgl.asRadians(state.quad.rot)) * 0.5; 137 | sgl.defaults(); 138 | sgl.rotate(sgl.asRadians(state.quad.rot), 0.0, 0.0, 1.0); 139 | sgl.scale(scale, scale, 1.0); 140 | sgl.beginQuads(); 141 | sgl.v2fC3b(-0.5, -0.5, 255, 255, 0); 142 | sgl.v2fC3b(0.5, -0.5, 0, 255, 0); 143 | sgl.v2fC3b(0.5, 0.5, 0, 0, 255); 144 | sgl.v2fC3b(-0.5, 0.5, 255, 0, 0); 145 | sgl.end(); 146 | } 147 | 148 | // vertex specification for a cube with colored sides and texture coords 149 | fn drawCube() void { 150 | sgl.beginQuads(); 151 | sgl.c3f(1.0, 0.0, 0.0); 152 | sgl.v3fT2f(-1.0, 1.0, -1.0, -1.0, 1.0); 153 | sgl.v3fT2f(1.0, 1.0, -1.0, 1.0, 1.0); 154 | sgl.v3fT2f(1.0, -1.0, -1.0, 1.0, -1.0); 155 | sgl.v3fT2f(-1.0, -1.0, -1.0, -1.0, -1.0); 156 | sgl.c3f(0.0, 1.0, 0.0); 157 | sgl.v3fT2f(-1.0, -1.0, 1.0, -1.0, 1.0); 158 | sgl.v3fT2f(1.0, -1.0, 1.0, 1.0, 1.0); 159 | sgl.v3fT2f(1.0, 1.0, 1.0, 1.0, -1.0); 160 | sgl.v3fT2f(-1.0, 1.0, 1.0, -1.0, -1.0); 161 | sgl.c3f(0.0, 0.0, 1.0); 162 | sgl.v3fT2f(-1.0, -1.0, 1.0, -1.0, 1.0); 163 | sgl.v3fT2f(-1.0, 1.0, 1.0, 1.0, 1.0); 164 | sgl.v3fT2f(-1.0, 1.0, -1.0, 1.0, -1.0); 165 | sgl.v3fT2f(-1.0, -1.0, -1.0, -1.0, -1.0); 166 | sgl.c3f(1.0, 0.5, 0.0); 167 | sgl.v3fT2f(1.0, -1.0, 1.0, -1.0, 1.0); 168 | sgl.v3fT2f(1.0, -1.0, -1.0, 1.0, 1.0); 169 | sgl.v3fT2f(1.0, 1.0, -1.0, 1.0, -1.0); 170 | sgl.v3fT2f(1.0, 1.0, 1.0, -1.0, -1.0); 171 | sgl.c3f(0.0, 0.5, 1.0); 172 | sgl.v3fT2f(1.0, -1.0, -1.0, -1.0, 1.0); 173 | sgl.v3fT2f(1.0, -1.0, 1.0, 1.0, 1.0); 174 | sgl.v3fT2f(-1.0, -1.0, 1.0, 1.0, -1.0); 175 | sgl.v3fT2f(-1.0, -1.0, -1.0, -1.0, -1.0); 176 | sgl.c3f(1.0, 0.0, 0.5); 177 | sgl.v3fT2f(-1.0, 1.0, -1.0, -1.0, 1.0); 178 | sgl.v3fT2f(-1.0, 1.0, 1.0, 1.0, 1.0); 179 | sgl.v3fT2f(1.0, 1.0, 1.0, 1.0, -1.0); 180 | sgl.v3fT2f(1.0, 1.0, -1.0, -1.0, -1.0); 181 | sgl.end(); 182 | } 183 | 184 | fn drawCubes(dt: f32) void { 185 | state.cube.rot_x += 1.0 * dt; 186 | state.cube.rot_y += 2.0 * dt; 187 | 188 | sgl.defaults(); 189 | sgl.loadPipeline(state.pip3d); 190 | 191 | sgl.matrixModeProjection(); 192 | sgl.perspective(sgl.asRadians(45.0), 1.0, 0.1, 100.0); 193 | 194 | sgl.matrixModeModelview(); 195 | sgl.translate(0.0, 0.0, -12.0); 196 | sgl.rotate(sgl.asRadians(state.cube.rot_x), 1.0, 0.0, 0.0); 197 | sgl.rotate(sgl.asRadians(state.cube.rot_y), 0.0, 1.0, 0.0); 198 | drawCube(); 199 | sgl.pushMatrix(); 200 | sgl.translate(0.0, 0.0, 3.0); 201 | sgl.scale(0.5, 0.5, 0.5); 202 | sgl.rotate(-2.0 * sgl.asRadians(state.cube.rot_x), 1.0, 0.0, 0.0); 203 | sgl.rotate(-2.0 * sgl.asRadians(state.cube.rot_y), 0.0, 1.0, 0.0); 204 | drawCube(); 205 | sgl.pushMatrix(); 206 | sgl.translate(0.0, 0.0, 3.0); 207 | sgl.scale(0.5, 0.5, 0.5); 208 | sgl.rotate(-3.0 * sgl.asRadians(state.cube.rot_x), 1.0, 0.0, 0.0); 209 | sgl.rotate(3.0 * sgl.asRadians(state.cube.rot_y), 0.0, 0.0, 1.0); 210 | drawCube(); 211 | sgl.popMatrix(); 212 | sgl.popMatrix(); 213 | } 214 | 215 | fn drawTexCube(dt: f32) void { 216 | state.texcube.time_accum += dt; 217 | const a = sgl.asRadians(state.texcube.time_accum); 218 | 219 | // texture matrix rotation and scale 220 | const tex_rot = a * 0.5; 221 | const tex_scale = 1.0 + math.sin(a) * 0.5; 222 | 223 | // compute an orbiting eye position for testing sgl.lookat() 224 | const eye_x = math.sin(a) * 6.0; 225 | const eye_y = math.sin(a) * 3.0; 226 | const eye_z = math.cos(a) * 6.0; 227 | 228 | sgl.defaults(); 229 | sgl.loadPipeline(state.pip3d); 230 | 231 | sgl.enableTexture(); 232 | sgl.texture(state.img, state.smp); 233 | 234 | sgl.matrixModeProjection(); 235 | sgl.perspective(sgl.asRadians(45.0), 1.0, 0.1, 100.0); 236 | sgl.matrixModeModelview(); 237 | sgl.lookat(eye_x, eye_y, eye_z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); 238 | sgl.matrixModeTexture(); 239 | sgl.rotate(tex_rot, 0.0, 0.0, 1.0); 240 | sgl.scale(tex_scale, tex_scale, 1.0); 241 | drawCube(); 242 | } 243 | 244 | pub fn main() void { 245 | sapp.run(.{ 246 | .init_cb = init, 247 | .frame_cb = frame, 248 | .cleanup_cb = cleanup, 249 | .width = 512, 250 | .height = 512, 251 | .sample_count = 4, 252 | .icon = .{ .sokol_default = true }, 253 | .window_title = "sgl.zig", 254 | .logger = .{ .func = slog.func }, 255 | }); 256 | } 257 | -------------------------------------------------------------------------------- /examples/shaders/blend.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // shaders for blend-sapp sample 3 | //------------------------------------------------------------------------------ 4 | @header const m = @import("../math.zig") 5 | @ctype mat4 m.Mat4 6 | 7 | @vs vs_bg 8 | in vec2 position; 9 | void main() { 10 | gl_Position = vec4(position, 0.5, 1.0); 11 | } 12 | @end 13 | 14 | @fs fs_bg 15 | layout(binding=0) uniform bg_fs_params { 16 | float tick; 17 | }; 18 | 19 | out vec4 frag_color; 20 | 21 | void main() { 22 | vec2 xy = fract((gl_FragCoord.xy-vec2(tick)) / 50.0); 23 | frag_color = vec4(vec3(xy.x*xy.y), 1.0); 24 | } 25 | @end 26 | 27 | @program bg vs_bg fs_bg 28 | 29 | @vs vs_quad 30 | layout(binding=0) uniform quad_vs_params { 31 | mat4 mvp; 32 | }; 33 | 34 | in vec4 position; 35 | in vec4 color0; 36 | 37 | out vec4 color; 38 | 39 | void main() { 40 | gl_Position = mvp * position; 41 | color = color0; 42 | } 43 | @end 44 | 45 | @fs fs_quad 46 | in vec4 color; 47 | out vec4 frag_color; 48 | void main() { 49 | frag_color = color; 50 | } 51 | @end 52 | 53 | @program quad vs_quad fs_quad 54 | -------------------------------------------------------------------------------- /examples/shaders/bufferoffsets.glsl: -------------------------------------------------------------------------------- 1 | @vs vs 2 | in vec4 position; 3 | in vec4 color0; 4 | 5 | out vec4 color; 6 | 7 | void main() { 8 | gl_Position = position; 9 | color = color0; 10 | } 11 | @end 12 | 13 | @fs fs 14 | in vec4 color; 15 | out vec4 frag_color; 16 | 17 | void main() { 18 | frag_color = color; 19 | } 20 | @end 21 | 22 | @program bufferoffsets vs fs 23 | -------------------------------------------------------------------------------- /examples/shaders/cube.glsl: -------------------------------------------------------------------------------- 1 | @header const m = @import("../math.zig") 2 | @ctype mat4 m.Mat4 3 | 4 | @vs vs 5 | layout(binding = 0) uniform vs_params { 6 | mat4 mvp; 7 | }; 8 | 9 | in vec4 position; 10 | in vec4 color0; 11 | 12 | out vec4 color; 13 | 14 | void main() { 15 | gl_Position = mvp * position; 16 | color = color0; 17 | } 18 | @end 19 | 20 | @fs fs 21 | in vec4 color; 22 | out vec4 frag_color; 23 | 24 | void main() { 25 | frag_color = color; 26 | } 27 | @end 28 | 29 | @program cube vs fs 30 | -------------------------------------------------------------------------------- /examples/shaders/instancing-compute.glsl: -------------------------------------------------------------------------------- 1 | @header const m = @import("../math.zig") 2 | @ctype mat4 m.Mat4 3 | 4 | // shared data structures 5 | @block common 6 | struct particle { 7 | vec4 pos; 8 | vec4 vel; 9 | }; 10 | @end 11 | 12 | // compute-shader for initializing pseudo-random particle velocities 13 | @cs cs_init 14 | @include_block common 15 | 16 | layout(binding=0) buffer cs_ssbo { 17 | particle prt[]; 18 | }; 19 | 20 | layout(local_size_x=64, local_size_y=1, local_size_z=1) in; 21 | 22 | uint xorshift32(uint x) { 23 | x ^= x<<13; 24 | x ^= x>>17; 25 | x ^= x<<5; 26 | return x; 27 | } 28 | 29 | void main() { 30 | uint idx = gl_GlobalInvocationID.x; 31 | uint x = xorshift32(0x12345678 ^ idx); 32 | uint y = xorshift32(x); 33 | uint z = xorshift32(y); 34 | prt[idx].pos = vec4(0); 35 | prt[idx].vel = vec4( 36 | (float(x & 0x7FFF) / 0x7FFF) - 0.5f, 37 | (float(y & 0x7FFF) / 0x7FFF) * 0.5f + 2.0f, 38 | (float(z & 0x7FFF) / 0x7FFF) - 0.5f, 39 | 0); 40 | } 41 | @end 42 | @program init cs_init 43 | 44 | // compute-shader for updating particle positions 45 | @cs cs_update 46 | @include_block common 47 | 48 | layout(binding=0) uniform cs_params { 49 | float dt; 50 | int num_particles; 51 | }; 52 | 53 | layout(binding=0) buffer cs_ssbo { 54 | particle prt[]; 55 | }; 56 | 57 | layout(local_size_x=64, locaL_size_y=1, local_size_z=1) in; 58 | 59 | void main() { 60 | uint idx = gl_GlobalInvocationID.x; 61 | if (idx >= num_particles) { 62 | return; 63 | } 64 | vec4 pos = prt[idx].pos; 65 | vec4 vel = prt[idx].vel; 66 | vel.y -= 1.0 * dt; 67 | pos += vel * dt; 68 | if (pos.y < -2.0) { 69 | pos.y = -1.8; 70 | vel *= vec4(0.8, -0.8, 0.8, 0.0); 71 | } 72 | prt[idx].pos = pos; 73 | prt[idx].vel = vel; 74 | } 75 | @end 76 | @program update cs_update 77 | 78 | // vertex- and fragment-shader for rendering the particles 79 | @vs vs 80 | @include_block common 81 | 82 | layout(binding=0) uniform vs_params { 83 | mat4 mvp; 84 | }; 85 | 86 | layout(binding=0) readonly buffer vs_ssbo { 87 | particle prt[]; 88 | }; 89 | 90 | in vec3 pos; 91 | in vec4 color0; 92 | 93 | out vec4 color; 94 | 95 | void main() { 96 | vec3 inst_pos = prt[gl_InstanceIndex].pos.xyz; 97 | vec4 pos = vec4(pos + inst_pos, 1.0); 98 | gl_Position = mvp * pos; 99 | color = color0; 100 | } 101 | @end 102 | 103 | @fs fs 104 | in vec4 color; 105 | out vec4 frag_color; 106 | void main() { 107 | frag_color = color; 108 | } 109 | @end 110 | 111 | @program display vs fs 112 | -------------------------------------------------------------------------------- /examples/shaders/instancing.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // shaders for instancing-sapp sample 3 | //------------------------------------------------------------------------------ 4 | @header const m = @import("../math.zig") 5 | @ctype mat4 m.Mat4 6 | 7 | @vs vs 8 | layout(binding=0) uniform vs_params { 9 | mat4 mvp; 10 | }; 11 | 12 | in vec3 pos; 13 | in vec4 color0; 14 | in vec3 inst_pos; 15 | 16 | out vec4 color; 17 | 18 | void main() { 19 | vec4 pos = vec4(pos + inst_pos, 1.0); 20 | gl_Position = mvp * pos; 21 | color = color0; 22 | } 23 | @end 24 | 25 | @fs fs 26 | in vec4 color; 27 | out vec4 frag_color; 28 | void main() { 29 | frag_color = color; 30 | } 31 | @end 32 | 33 | @program instancing vs fs 34 | -------------------------------------------------------------------------------- /examples/shaders/mrt.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // shaders for mrt-sapp sample 3 | //------------------------------------------------------------------------------ 4 | @header const m = @import("../math.zig") 5 | @ctype mat4 m.Mat4 6 | @ctype vec2 m.Vec2 7 | 8 | // shaders for offscreen-pass rendering 9 | @vs vs_offscreen 10 | 11 | layout(binding=0) uniform offscreen_params { 12 | mat4 mvp; 13 | }; 14 | 15 | in vec4 pos; 16 | in float bright0; 17 | 18 | out float bright; 19 | 20 | void main() { 21 | gl_Position = mvp * pos; 22 | bright = bright0; 23 | } 24 | @end 25 | 26 | @fs fs_offscreen 27 | in float bright; 28 | 29 | layout(location=0) out vec4 frag_color_0; 30 | layout(location=1) out vec4 frag_color_1; 31 | layout(location=2) out vec4 frag_color_2; 32 | 33 | void main() { 34 | frag_color_0 = vec4(bright, 0.0, 0.0, 1.0); 35 | frag_color_1 = vec4(0.0, bright, 0.0, 1.0); 36 | frag_color_2 = vec4(0.0, 0.0, bright, 1.0); 37 | } 38 | @end 39 | 40 | @program offscreen vs_offscreen fs_offscreen 41 | 42 | // shaders for rendering a fullscreen-quad in default pass 43 | @vs vs_fsq 44 | @glsl_options flip_vert_y 45 | 46 | layout(binding=0) uniform fsq_params { 47 | vec2 offset; 48 | }; 49 | 50 | in vec2 pos; 51 | 52 | out vec2 uv0; 53 | out vec2 uv1; 54 | out vec2 uv2; 55 | 56 | void main() { 57 | gl_Position = vec4(pos*2.0-1.0, 0.5, 1.0); 58 | uv0 = pos + vec2(offset.x, 0.0); 59 | uv1 = pos + vec2(0.0, offset.y); 60 | uv2 = pos; 61 | } 62 | @end 63 | 64 | @fs fs_fsq 65 | layout(binding=0) uniform texture2D tex0; 66 | layout(binding=1) uniform texture2D tex1; 67 | layout(binding=2) uniform texture2D tex2; 68 | layout(binding=0) uniform sampler smp; 69 | 70 | in vec2 uv0; 71 | in vec2 uv1; 72 | in vec2 uv2; 73 | 74 | out vec4 frag_color; 75 | 76 | void main() { 77 | vec3 c0 = texture(sampler2D(tex0, smp), uv0).xyz; 78 | vec3 c1 = texture(sampler2D(tex1, smp), uv1).xyz; 79 | vec3 c2 = texture(sampler2D(tex2, smp), uv2).xyz; 80 | frag_color = vec4(c0 + c1 + c2, 1.0); 81 | } 82 | @end 83 | 84 | @program fsq vs_fsq fs_fsq 85 | 86 | // shaders for rendering a debug visualization 87 | @vs vs_dbg 88 | @glsl_options flip_vert_y 89 | 90 | in vec2 pos; 91 | out vec2 uv; 92 | 93 | void main() { 94 | gl_Position = vec4(pos*2.0-1.0, 0.5, 1.0); 95 | uv = pos; 96 | } 97 | @end 98 | 99 | @fs fs_dbg 100 | layout(binding=0) uniform texture2D tex; 101 | layout(binding=0) uniform sampler smp; 102 | 103 | in vec2 uv; 104 | out vec4 frag_color; 105 | 106 | void main() { 107 | frag_color = vec4(texture(sampler2D(tex, smp), uv).xyz, 1.0); 108 | } 109 | @end 110 | 111 | @program dbg vs_dbg fs_dbg 112 | -------------------------------------------------------------------------------- /examples/shaders/noninterleaved.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Shader code for noninterleaved-sapp sample. 3 | //------------------------------------------------------------------------------ 4 | @header const m = @import("../math.zig") 5 | @ctype mat4 m.Mat4 6 | 7 | @vs vs 8 | layout(binding=0) uniform vs_params { 9 | mat4 mvp; 10 | }; 11 | 12 | in vec4 position; 13 | in vec4 color0; 14 | out vec4 color; 15 | 16 | void main() { 17 | gl_Position = mvp * position; 18 | color = color0; 19 | } 20 | @end 21 | 22 | @fs fs 23 | in vec4 color; 24 | out vec4 frag_color; 25 | 26 | void main() { 27 | frag_color = color; 28 | } 29 | @end 30 | 31 | @program noninterleaved vs fs 32 | -------------------------------------------------------------------------------- /examples/shaders/offscreen.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // shaders for offscreen-sapp sample 3 | //------------------------------------------------------------------------------ 4 | @header const m = @import("../math.zig") 5 | @ctype mat4 m.Mat4 6 | 7 | // shared code for all shaders 8 | @block uniforms 9 | layout(binding=0) uniform vs_params { 10 | mat4 mvp; 11 | }; 12 | @end 13 | 14 | // offscreen rendering shaders 15 | @vs vs_offscreen 16 | @include_block uniforms 17 | 18 | in vec4 position; 19 | in vec4 normal; 20 | out vec4 nrm; 21 | 22 | void main() { 23 | gl_Position = mvp * position; 24 | nrm = normal; 25 | } 26 | @end 27 | 28 | @fs fs_offscreen 29 | in vec4 nrm; 30 | out vec4 frag_color; 31 | 32 | void main() { 33 | frag_color = vec4(nrm.xyz * 0.5 + 0.5, 1.0); 34 | } 35 | @end 36 | 37 | @program offscreen vs_offscreen fs_offscreen 38 | 39 | // default-pass shaders 40 | @vs vs_default 41 | @include_block uniforms 42 | 43 | in vec4 position; 44 | in vec4 normal; 45 | in vec2 texcoord0; 46 | out vec4 nrm; 47 | out vec2 uv; 48 | 49 | void main() { 50 | gl_Position = mvp * position; 51 | uv = texcoord0; 52 | nrm = mvp * normal; 53 | } 54 | @end 55 | 56 | @fs fs_default 57 | layout(binding=0) uniform texture2D tex; 58 | layout(binding=0) uniform sampler smp; 59 | 60 | in vec4 nrm; 61 | in vec2 uv; 62 | 63 | out vec4 frag_color; 64 | 65 | void main() { 66 | vec4 c = texture(sampler2D(tex, smp), uv * vec2(20.0, 10.0)); 67 | float l = clamp(dot(nrm.xyz, normalize(vec3(1.0, 1.0, -1.0))), 0.0, 1.0) * 2.0; 68 | frag_color = vec4(c.xyz * (l + 0.25), 1.0); 69 | } 70 | @end 71 | 72 | @program default vs_default fs_default 73 | -------------------------------------------------------------------------------- /examples/shaders/quad.glsl: -------------------------------------------------------------------------------- 1 | /* quad vertex shader */ 2 | @vs vs 3 | in vec4 position; 4 | in vec4 color0; 5 | out vec4 color; 6 | 7 | void main() { 8 | gl_Position = position; 9 | color = color0; 10 | } 11 | @end 12 | 13 | /* quad fragment shader */ 14 | @fs fs 15 | in vec4 color; 16 | out vec4 frag_color; 17 | 18 | void main() { 19 | frag_color = color; 20 | } 21 | @end 22 | 23 | /* quad shader program */ 24 | @program quad vs fs 25 | 26 | -------------------------------------------------------------------------------- /examples/shaders/shapes.glsl: -------------------------------------------------------------------------------- 1 | @header const m = @import("../math.zig") 2 | @ctype mat4 m.Mat4 3 | 4 | @vs vs 5 | layout(binding=0) uniform vs_params { 6 | float draw_mode; 7 | mat4 mvp; 8 | }; 9 | 10 | in vec4 position; 11 | in vec3 normal; 12 | in vec2 texcoord; 13 | in vec4 color0; 14 | 15 | out vec4 color; 16 | 17 | void main() { 18 | gl_Position = mvp * position; 19 | if (draw_mode == 0.0) { 20 | color = vec4((normal + 1.0) * 0.5, 1.0); 21 | } 22 | else if (draw_mode == 1.0) { 23 | color = vec4(texcoord, 0.0, 1.0); 24 | } 25 | else { 26 | color = color0; 27 | } 28 | } 29 | @end 30 | 31 | @fs fs 32 | in vec4 color; 33 | out vec4 frag_color; 34 | 35 | void main() { 36 | frag_color = color; 37 | } 38 | @end 39 | 40 | @program shapes vs fs 41 | -------------------------------------------------------------------------------- /examples/shaders/texcube.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Shader code for texcube-sapp sample. 3 | // 4 | // NOTE: This source file also uses the '#pragma sokol' form of the 5 | // custom tags. 6 | //------------------------------------------------------------------------------ 7 | #pragma sokol @header const m = @import("../math.zig") 8 | #pragma sokol @ctype mat4 m.Mat4 9 | 10 | #pragma sokol @vs vs 11 | layout(binding=0) uniform vs_params { 12 | mat4 mvp; 13 | }; 14 | 15 | in vec4 pos; 16 | in vec4 color0; 17 | in vec2 texcoord0; 18 | 19 | out vec4 color; 20 | out vec2 uv; 21 | 22 | void main() { 23 | gl_Position = mvp * pos; 24 | color = color0; 25 | uv = texcoord0 * 5.0; 26 | } 27 | #pragma sokol @end 28 | 29 | #pragma sokol @fs fs 30 | layout(binding=0) uniform texture2D tex; 31 | layout(binding=0) uniform sampler smp; 32 | 33 | in vec4 color; 34 | in vec2 uv; 35 | out vec4 frag_color; 36 | 37 | void main() { 38 | frag_color = texture(sampler2D(tex, smp), uv) * color; 39 | } 40 | #pragma sokol @end 41 | 42 | #pragma sokol @program texcube vs fs 43 | -------------------------------------------------------------------------------- /examples/shaders/triangle.glsl: -------------------------------------------------------------------------------- 1 | @vs vs 2 | in vec4 position; 3 | in vec4 color0; 4 | 5 | out vec4 color; 6 | 7 | void main() { 8 | gl_Position = position; 9 | color = color0; 10 | } 11 | @end 12 | 13 | @fs fs 14 | in vec4 color; 15 | out vec4 frag_color; 16 | 17 | void main() { 18 | frag_color = color; 19 | } 20 | @end 21 | 22 | @program triangle vs fs 23 | -------------------------------------------------------------------------------- /examples/shaders/vertexpull.glsl: -------------------------------------------------------------------------------- 1 | @header const m = @import("../math.zig") 2 | @ctype mat4 m.Mat4 3 | 4 | @vs vs 5 | layout(binding=0) uniform vs_params { 6 | mat4 mvp; 7 | }; 8 | 9 | // NOTE: 'vertex' is a reserved name in MSL 10 | struct sb_vertex { 11 | vec3 pos; 12 | vec4 color; 13 | }; 14 | 15 | layout(binding=0) readonly buffer ssbo { 16 | sb_vertex vtx[]; 17 | }; 18 | 19 | out vec4 color; 20 | 21 | void main() { 22 | gl_Position = mvp * vec4(vtx[gl_VertexIndex].pos, 1.0); 23 | color = vtx[gl_VertexIndex].color; 24 | } 25 | @end 26 | 27 | @fs fs 28 | in vec4 color; 29 | out vec4 frag_color; 30 | 31 | void main() { 32 | frag_color = color; 33 | } 34 | @end 35 | 36 | @program vertexpull vs fs -------------------------------------------------------------------------------- /examples/shapes.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // shapes.zig 3 | // 4 | // Simple sokol.shape demo. 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const sdtx = sokol.debugtext; 12 | const sshape = sokol.shape; 13 | const vec3 = @import("math.zig").Vec3; 14 | const mat4 = @import("math.zig").Mat4; 15 | const assert = @import("std").debug.assert; 16 | const shd = @import("shaders/shapes.glsl.zig"); 17 | 18 | const Shape = struct { 19 | pos: vec3 = vec3.zero(), 20 | draw: sshape.ElementRange = .{}, 21 | }; 22 | 23 | const NUM_SHAPES = 5; 24 | 25 | const state = struct { 26 | var pass_action: sg.PassAction = .{}; 27 | var pip: sg.Pipeline = .{}; 28 | var bind: sg.Bindings = .{}; 29 | var vs_params: shd.VsParams = undefined; 30 | var shapes: [NUM_SHAPES]Shape = .{ 31 | .{ .pos = .{ .x = -1, .y = 1, .z = 0 } }, 32 | .{ .pos = .{ .x = 1, .y = 1, .z = 0 } }, 33 | .{ .pos = .{ .x = -2, .y = -1, .z = 0 } }, 34 | .{ .pos = .{ .x = 2, .y = -1, .z = 0 } }, 35 | .{ .pos = .{ .x = 0, .y = -1, .z = 0 } }, 36 | }; 37 | var rx: f32 = 0.0; 38 | var ry: f32 = 0.0; 39 | const view = mat4.lookat(.{ .x = 0.0, .y = 1.5, .z = 6.0 }, vec3.zero(), vec3.up()); 40 | }; 41 | 42 | export fn init() void { 43 | sg.setup(.{ 44 | .environment = sglue.environment(), 45 | .logger = .{ .func = slog.func }, 46 | }); 47 | 48 | var sdtx_desc: sdtx.Desc = .{ 49 | .logger = .{ .func = slog.func }, 50 | }; 51 | sdtx_desc.fonts[0] = sdtx.fontOric(); 52 | sdtx.setup(sdtx_desc); 53 | 54 | // pass-action for clearing to black 55 | state.pass_action.colors[0] = .{ 56 | .load_action = .CLEAR, 57 | .clear_value = .{ .r = 0, .g = 0, .b = 0, .a = 1 }, 58 | }; 59 | 60 | // shader- and pipeline-object 61 | state.pip = sg.makePipeline(.{ 62 | .shader = sg.makeShader(shd.shapesShaderDesc(sg.queryBackend())), 63 | .layout = init: { 64 | var l = sg.VertexLayoutState{}; 65 | l.buffers[0] = sshape.vertexBufferLayoutState(); 66 | l.attrs[shd.ATTR_shapes_position] = sshape.positionVertexAttrState(); 67 | l.attrs[shd.ATTR_shapes_normal] = sshape.normalVertexAttrState(); 68 | l.attrs[shd.ATTR_shapes_texcoord] = sshape.texcoordVertexAttrState(); 69 | l.attrs[shd.ATTR_shapes_color0] = sshape.colorVertexAttrState(); 70 | break :init l; 71 | }, 72 | .index_type = .UINT16, 73 | .cull_mode = .NONE, 74 | .depth = .{ 75 | .compare = .LESS_EQUAL, 76 | .write_enabled = true, 77 | }, 78 | }); 79 | 80 | // generate shape geometries 81 | var vertices: [6 * 1024]sshape.Vertex = undefined; 82 | var indices: [16 * 1024]u16 = undefined; 83 | var buf: sshape.Buffer = .{ 84 | .vertices = .{ .buffer = sshape.asRange(&vertices) }, 85 | .indices = .{ .buffer = sshape.asRange(&indices) }, 86 | }; 87 | buf = sshape.buildBox(buf, .{ 88 | .width = 1.0, 89 | .height = 1.0, 90 | .depth = 1.0, 91 | .tiles = 10, 92 | .random_colors = true, 93 | }); 94 | state.shapes[0].draw = sshape.elementRange(buf); 95 | buf = sshape.buildPlane(buf, .{ 96 | .width = 1.0, 97 | .depth = 1.0, 98 | .tiles = 10, 99 | .random_colors = true, 100 | }); 101 | state.shapes[1].draw = sshape.elementRange(buf); 102 | buf = sshape.buildSphere(buf, .{ 103 | .radius = 0.75, 104 | .slices = 36, 105 | .stacks = 20, 106 | .random_colors = true, 107 | }); 108 | state.shapes[2].draw = sshape.elementRange(buf); 109 | buf = sshape.buildCylinder(buf, .{ 110 | .radius = 0.5, 111 | .height = 1.5, 112 | .slices = 36, 113 | .stacks = 10, 114 | .random_colors = true, 115 | }); 116 | state.shapes[3].draw = sshape.elementRange(buf); 117 | buf = sshape.buildTorus(buf, .{ 118 | .radius = 0.5, 119 | .ring_radius = 0.3, 120 | .rings = 36, 121 | .sides = 18, 122 | .random_colors = true, 123 | }); 124 | state.shapes[4].draw = sshape.elementRange(buf); 125 | assert(buf.valid); 126 | 127 | // one vertex- and index-buffer for all shapes 128 | state.bind.vertex_buffers[0] = sg.makeBuffer(sshape.vertexBufferDesc(buf)); 129 | state.bind.index_buffer = sg.makeBuffer(sshape.indexBufferDesc(buf)); 130 | } 131 | 132 | export fn frame() void { 133 | // help text 134 | sdtx.canvas(sapp.widthf() * 0.5, sapp.heightf() * 0.5); 135 | sdtx.pos(0.5, 0.5); 136 | sdtx.puts("press key to switch draw mode:\n\n"); 137 | sdtx.puts(" 1: vertex normals\n"); 138 | sdtx.puts(" 2: texture coords\n"); 139 | sdtx.puts(" 3: vertex colors\n"); 140 | 141 | // view-project matrix 142 | const proj = mat4.persp(60.0, sapp.widthf() / sapp.heightf(), 0.01, 10.0); 143 | const view_proj = mat4.mul(proj, state.view); 144 | 145 | // model-rotation matrix 146 | const dt: f32 = @floatCast(sapp.frameDuration() * 60); 147 | state.rx += 1.0 * dt; 148 | state.ry += 1.0 * dt; 149 | const rxm = mat4.rotate(state.rx, .{ .x = 1, .y = 0, .z = 0 }); 150 | const rym = mat4.rotate(state.ry, .{ .x = 0, .y = 1, .z = 0 }); 151 | const rm = mat4.mul(rxm, rym); 152 | 153 | // render shapes... 154 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 155 | sg.applyPipeline(state.pip); 156 | sg.applyBindings(state.bind); 157 | for (state.shapes) |shape| { 158 | // per-shape model-view-projection matrix 159 | const model = mat4.mul(mat4.translate(shape.pos), rm); 160 | state.vs_params.mvp = mat4.mul(view_proj, model); 161 | sg.applyUniforms(shd.UB_vs_params, sg.asRange(&state.vs_params)); 162 | sg.draw(shape.draw.base_element, shape.draw.num_elements, 1); 163 | } 164 | sdtx.draw(); 165 | sg.endPass(); 166 | sg.commit(); 167 | } 168 | 169 | export fn input(event: ?*const sapp.Event) void { 170 | const ev = event.?; 171 | if (ev.type == .KEY_DOWN) { 172 | state.vs_params.draw_mode = switch (ev.key_code) { 173 | ._1 => 0.0, 174 | ._2 => 1.0, 175 | ._3 => 2.0, 176 | else => state.vs_params.draw_mode, 177 | }; 178 | } 179 | } 180 | 181 | export fn cleanup() void { 182 | sdtx.shutdown(); 183 | sg.shutdown(); 184 | } 185 | 186 | pub fn main() void { 187 | sapp.run(.{ 188 | .init_cb = init, 189 | .frame_cb = frame, 190 | .event_cb = input, 191 | .cleanup_cb = cleanup, 192 | .width = 800, 193 | .height = 600, 194 | .sample_count = 4, 195 | .icon = .{ .sokol_default = true }, 196 | .window_title = "shapes.zig", 197 | .logger = .{ .func = slog.func }, 198 | }); 199 | } 200 | -------------------------------------------------------------------------------- /examples/texcube.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // texcube.zig 3 | // 4 | // Texture creation, rendering with texture, packed vertex components. 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const vec3 = @import("math.zig").Vec3; 12 | const mat4 = @import("math.zig").Mat4; 13 | const shd = @import("shaders/texcube.glsl.zig"); 14 | 15 | const state = struct { 16 | var rx: f32 = 0.0; 17 | var ry: f32 = 0.0; 18 | var pass_action: sg.PassAction = .{}; 19 | var pip: sg.Pipeline = .{}; 20 | var bind: sg.Bindings = .{}; 21 | const view: mat4 = mat4.lookat(.{ .x = 0.0, .y = 1.5, .z = 6.0 }, vec3.zero(), vec3.up()); 22 | }; 23 | 24 | // a vertex struct with position, color and uv-coords 25 | const Vertex = extern struct { x: f32, y: f32, z: f32, color: u32, u: i16, v: i16 }; 26 | 27 | export fn init() void { 28 | sg.setup(.{ 29 | .environment = sglue.environment(), 30 | .logger = .{ .func = slog.func }, 31 | }); 32 | 33 | // Cube vertex buffer with packed vertex formats for color and texture coords. 34 | // Note that a vertex format which must be portable across all 35 | // backends must only use the normalized integer formats 36 | // (BYTE4N, UBYTE4N, SHORT2N, SHORT4N), which can be converted 37 | // to floating point formats in the vertex shader inputs. 38 | // The reason is that D3D11 cannot convert from non-normalized 39 | // formats to floating point inputs (only to integer inputs), 40 | // and WebGL2 / GLES2 don't support integer vertex shader inputs. 41 | // 42 | state.bind.vertex_buffers[0] = sg.makeBuffer(.{ 43 | .data = sg.asRange(&[_]Vertex{ 44 | // zig fmt: off 45 | .{ .x = -1.0, .y = -1.0, .z = -1.0, .color = 0xFF0000FF, .u = 0, .v = 0 }, 46 | .{ .x = 1.0, .y = -1.0, .z = -1.0, .color = 0xFF0000FF, .u = 32767, .v = 0 }, 47 | .{ .x = 1.0, .y = 1.0, .z = -1.0, .color = 0xFF0000FF, .u = 32767, .v = 32767 }, 48 | .{ .x = -1.0, .y = 1.0, .z = -1.0, .color = 0xFF0000FF, .u = 0, .v = 32767 }, 49 | 50 | .{ .x = -1.0, .y = -1.0, .z = 1.0, .color = 0xFF00FF00, .u = 0, .v = 0 }, 51 | .{ .x = 1.0, .y = -1.0, .z = 1.0, .color = 0xFF00FF00, .u = 32767, .v = 0 }, 52 | .{ .x = 1.0, .y = 1.0, .z = 1.0, .color = 0xFF00FF00, .u = 32767, .v = 32767 }, 53 | .{ .x = -1.0, .y = 1.0, .z = 1.0, .color = 0xFF00FF00, .u = 0, .v = 32767 }, 54 | 55 | .{ .x = -1.0, .y = -1.0, .z = -1.0, .color = 0xFFFF0000, .u = 0, .v = 0 }, 56 | .{ .x = -1.0, .y = 1.0, .z = -1.0, .color = 0xFFFF0000, .u = 32767, .v = 0 }, 57 | .{ .x = -1.0, .y = 1.0, .z = 1.0, .color = 0xFFFF0000, .u = 32767, .v = 32767 }, 58 | .{ .x = -1.0, .y = -1.0, .z = 1.0, .color = 0xFFFF0000, .u = 0, .v = 32767 }, 59 | 60 | .{ .x = 1.0, .y = -1.0, .z = -1.0, .color = 0xFFFF007F, .u = 0, .v = 0 }, 61 | .{ .x = 1.0, .y = 1.0, .z = -1.0, .color = 0xFFFF007F, .u = 32767, .v = 0 }, 62 | .{ .x = 1.0, .y = 1.0, .z = 1.0, .color = 0xFFFF007F, .u = 32767, .v = 32767 }, 63 | .{ .x = 1.0, .y = -1.0, .z = 1.0, .color = 0xFFFF007F, .u = 0, .v = 32767 }, 64 | 65 | .{ .x = -1.0, .y = -1.0, .z = -1.0, .color = 0xFFFF7F00, .u = 0, .v = 0 }, 66 | .{ .x = -1.0, .y = -1.0, .z = 1.0, .color = 0xFFFF7F00, .u = 32767, .v = 0 }, 67 | .{ .x = 1.0, .y = -1.0, .z = 1.0, .color = 0xFFFF7F00, .u = 32767, .v = 32767 }, 68 | .{ .x = 1.0, .y = -1.0, .z = -1.0, .color = 0xFFFF7F00, .u = 0, .v = 32767 }, 69 | 70 | .{ .x = -1.0, .y = 1.0, .z = -1.0, .color = 0xFF007FFF, .u = 0, .v = 0 }, 71 | .{ .x = -1.0, .y = 1.0, .z = 1.0, .color = 0xFF007FFF, .u = 32767, .v = 0 }, 72 | .{ .x = 1.0, .y = 1.0, .z = 1.0, .color = 0xFF007FFF, .u = 32767, .v = 32767 }, 73 | .{ .x = 1.0, .y = 1.0, .z = -1.0, .color = 0xFF007FFF, .u = 0, .v = 32767 }, 74 | // zig fmt: on 75 | }), 76 | }); 77 | 78 | // cube index buffer 79 | state.bind.index_buffer = sg.makeBuffer(.{ 80 | .usage = .{ .index_buffer = true }, 81 | .data = sg.asRange(&[_]u16{ 82 | 0, 1, 2, 0, 2, 3, 83 | 6, 5, 4, 7, 6, 4, 84 | 8, 9, 10, 8, 10, 11, 85 | 14, 13, 12, 15, 14, 12, 86 | 16, 17, 18, 16, 18, 19, 87 | 22, 21, 20, 23, 22, 20, 88 | }), 89 | }); 90 | 91 | // create a small checker-board texture 92 | state.bind.images[shd.IMG_tex] = sg.makeImage(.{ 93 | .width = 4, 94 | .height = 4, 95 | .data = init: { 96 | var data = sg.ImageData{}; 97 | data.subimage[0][0] = sg.asRange(&[4 * 4]u32{ 98 | 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFF000000, 99 | 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 100 | 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFF000000, 101 | 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 102 | }); 103 | break :init data; 104 | }, 105 | }); 106 | 107 | // ...and a sampler object with default attributes 108 | state.bind.samplers[shd.SMP_smp] = sg.makeSampler(.{}); 109 | 110 | // shader and pipeline object 111 | state.pip = sg.makePipeline(.{ 112 | .shader = sg.makeShader(shd.texcubeShaderDesc(sg.queryBackend())), 113 | .layout = init: { 114 | var l = sg.VertexLayoutState{}; 115 | l.attrs[shd.ATTR_texcube_pos].format = .FLOAT3; 116 | l.attrs[shd.ATTR_texcube_color0].format = .UBYTE4N; 117 | l.attrs[shd.ATTR_texcube_texcoord0].format = .SHORT2N; 118 | break :init l; 119 | }, 120 | .index_type = .UINT16, 121 | .depth = .{ 122 | .compare = .LESS_EQUAL, 123 | .write_enabled = true, 124 | }, 125 | .cull_mode = .BACK, 126 | }); 127 | 128 | // pass action for clearing the frame buffer 129 | state.pass_action.colors[0] = .{ 130 | .load_action = .CLEAR, 131 | .clear_value = .{ .r = 0.25, .g = 0.5, .b = 0.75, .a = 1 }, 132 | }; 133 | } 134 | 135 | export fn frame() void { 136 | const dt: f32 = @floatCast(sapp.frameDuration() * 60); 137 | 138 | state.rx += 1.0 * dt; 139 | state.ry += 2.0 * dt; 140 | const vs_params = computeVsParams(state.rx, state.ry); 141 | 142 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 143 | sg.applyPipeline(state.pip); 144 | sg.applyBindings(state.bind); 145 | sg.applyUniforms(shd.UB_vs_params, sg.asRange(&vs_params)); 146 | sg.draw(0, 36, 1); 147 | sg.endPass(); 148 | sg.commit(); 149 | } 150 | 151 | export fn cleanup() void { 152 | sg.shutdown(); 153 | } 154 | 155 | pub fn main() void { 156 | sapp.run(.{ 157 | .init_cb = init, 158 | .frame_cb = frame, 159 | .cleanup_cb = cleanup, 160 | .width = 800, 161 | .height = 600, 162 | .sample_count = 4, 163 | .icon = .{ .sokol_default = true }, 164 | .window_title = "texcube.zig", 165 | .logger = .{ .func = slog.func }, 166 | }); 167 | } 168 | 169 | fn computeVsParams(rx: f32, ry: f32) shd.VsParams { 170 | const rxm = mat4.rotate(rx, .{ .x = 1.0, .y = 0.0, .z = 0.0 }); 171 | const rym = mat4.rotate(ry, .{ .x = 0.0, .y = 1.0, .z = 0.0 }); 172 | const model = mat4.mul(rxm, rym); 173 | const aspect = sapp.widthf() / sapp.heightf(); 174 | const proj = mat4.persp(60.0, aspect, 0.01, 10.0); 175 | return shd.VsParams{ .mvp = mat4.mul(mat4.mul(proj, state.view), model) }; 176 | } 177 | -------------------------------------------------------------------------------- /examples/triangle.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // triangle.zig 3 | // 4 | // Vertex buffer, shader, pipeline state object. 5 | //------------------------------------------------------------------------------ 6 | const sokol = @import("sokol"); 7 | const slog = sokol.log; 8 | const sg = sokol.gfx; 9 | const sapp = sokol.app; 10 | const sglue = sokol.glue; 11 | const shd = @import("shaders/triangle.glsl.zig"); 12 | 13 | const state = struct { 14 | var bind: sg.Bindings = .{}; 15 | var pip: sg.Pipeline = .{}; 16 | }; 17 | 18 | export fn init() void { 19 | sg.setup(.{ 20 | .environment = sglue.environment(), 21 | .logger = .{ .func = slog.func }, 22 | }); 23 | 24 | // create vertex buffer with triangle vertices 25 | state.bind.vertex_buffers[0] = sg.makeBuffer(.{ 26 | .data = sg.asRange(&[_]f32{ 27 | // positions colors 28 | 0.0, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0, 29 | 0.5, -0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 30 | -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 31 | }), 32 | }); 33 | 34 | // create a shader and pipeline object 35 | state.pip = sg.makePipeline(.{ 36 | .shader = sg.makeShader(shd.triangleShaderDesc(sg.queryBackend())), 37 | .layout = init: { 38 | var l = sg.VertexLayoutState{}; 39 | l.attrs[shd.ATTR_triangle_position].format = .FLOAT3; 40 | l.attrs[shd.ATTR_triangle_color0].format = .FLOAT4; 41 | break :init l; 42 | }, 43 | }); 44 | } 45 | 46 | export fn frame() void { 47 | // default pass-action clears to grey 48 | sg.beginPass(.{ .swapchain = sglue.swapchain() }); 49 | sg.applyPipeline(state.pip); 50 | sg.applyBindings(state.bind); 51 | sg.draw(0, 3, 1); 52 | sg.endPass(); 53 | sg.commit(); 54 | } 55 | 56 | export fn cleanup() void { 57 | sg.shutdown(); 58 | } 59 | 60 | pub fn main() void { 61 | sapp.run(.{ 62 | .init_cb = init, 63 | .frame_cb = frame, 64 | .cleanup_cb = cleanup, 65 | .width = 640, 66 | .height = 480, 67 | .icon = .{ .sokol_default = true }, 68 | .window_title = "triangle.zig", 69 | .logger = .{ .func = slog.func }, 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /examples/vertexpull.zig: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // vertexpull.zig 3 | // 4 | // Pull vertices from a storage buffer instead of using fixed-function 5 | // vertex input. 6 | //------------------------------------------------------------------------------ 7 | const sokol = @import("sokol"); 8 | const slog = sokol.log; 9 | const sg = sokol.gfx; 10 | const sapp = sokol.app; 11 | const sglue = sokol.glue; 12 | const vec3 = @import("math.zig").Vec3; 13 | const mat4 = @import("math.zig").Mat4; 14 | const shd = @import("shaders/vertexpull.glsl.zig"); 15 | 16 | const state = struct { 17 | var rx: f32 = 0.0; 18 | var ry: f32 = 0.0; 19 | var pip: sg.Pipeline = .{}; 20 | var bind: sg.Bindings = .{}; 21 | var pass_action: sg.PassAction = .{}; 22 | const view: mat4 = mat4.lookat(.{ .x = 0.0, .y = 1.5, .z = 6.0 }, vec3.zero(), vec3.up()); 23 | }; 24 | 25 | export fn init() void { 26 | sg.setup(.{ 27 | .environment = sglue.environment(), 28 | .logger = .{ .func = slog.func }, 29 | }); 30 | 31 | // if storage buffers are not supported on the current backend, just render a red screen 32 | if (!sg.queryFeatures().compute) { 33 | state.pass_action.colors[0] = .{ 34 | .load_action = .CLEAR, 35 | .clear_value = .{ .r = 1, .g = 0, .b = 0, .a = 1 }, 36 | }; 37 | return; 38 | } 39 | 40 | // otherwise set regular clear color 41 | state.pass_action.colors[0] = .{ 42 | .load_action = .CLEAR, 43 | .clear_value = .{ .r = 0.75, .g = 0.5, .b = 0.25, .a = 1 }, 44 | }; 45 | 46 | // a storage buffer with the cube vertex data 47 | state.bind.storage_buffers[shd.SBUF_ssbo] = sg.makeBuffer(.{ 48 | .usage = .{ .storage_buffer = true }, 49 | .data = sg.asRange(&[_]shd.SbVertex{ 50 | // zig fmt: off 51 | .{ .pos = .{ -1.0, -1.0, -1.0 }, .color = .{ 1.0, 0.0, 0.0, 1.0 } }, 52 | .{ .pos = .{ 1.0, -1.0, -1.0 }, .color = .{ 1.0, 0.0, 0.0, 1.0 } }, 53 | .{ .pos = .{ 1.0, 1.0, -1.0 }, .color = .{ 1.0, 0.0, 0.0, 1.0 } }, 54 | .{ .pos = .{ -1.0, 1.0, -1.0 }, .color = .{ 1.0, 0.0, 0.0, 1.0 } }, 55 | .{ .pos = .{ -1.0, -1.0, 1.0 }, .color = .{ 0.0, 1.0, 0.0, 1.0 } }, 56 | .{ .pos = .{ 1.0, -1.0, 1.0 }, .color = .{ 0.0, 1.0, 0.0, 1.0 } }, 57 | .{ .pos = .{ 1.0, 1.0, 1.0 }, .color = .{ 0.0, 1.0, 0.0, 1.0 } }, 58 | .{ .pos = .{ -1.0, 1.0, 1.0 }, .color = .{ 0.0, 1.0, 0.0, 1.0 } }, 59 | .{ .pos = .{ -1.0, -1.0, -1.0 }, .color = .{ 0.0, 0.0, 1.0, 1.0 } }, 60 | .{ .pos = .{ -1.0, 1.0, -1.0 }, .color = .{ 0.0, 0.0, 1.0, 1.0 } }, 61 | .{ .pos = .{ -1.0, 1.0, 1.0 }, .color = .{ 0.0, 0.0, 1.0, 1.0 } }, 62 | .{ .pos = .{ -1.0, -1.0, 1.0 }, .color = .{ 0.0, 0.0, 1.0, 1.0 } }, 63 | .{ .pos = .{ 1.0, -1.0, -1.0 }, .color = .{ 1.0, 0.5, 0.0, 1.0 } }, 64 | .{ .pos = .{ 1.0, 1.0, -1.0 }, .color = .{ 1.0, 0.5, 0.0, 1.0 } }, 65 | .{ .pos = .{ 1.0, 1.0, 1.0 }, .color = .{ 1.0, 0.5, 0.0, 1.0 } }, 66 | .{ .pos = .{ 1.0, -1.0, 1.0 }, .color = .{ 1.0, 0.5, 0.0, 1.0 } }, 67 | .{ .pos = .{ -1.0, -1.0, -1.0 }, .color = .{ 0.0, 0.5, 1.0, 1.0 } }, 68 | .{ .pos = .{ -1.0, -1.0, 1.0 }, .color = .{ 0.0, 0.5, 1.0, 1.0 } }, 69 | .{ .pos = .{ 1.0, -1.0, 1.0 }, .color = .{ 0.0, 0.5, 1.0, 1.0 } }, 70 | .{ .pos = .{ 1.0, -1.0, -1.0 }, .color = .{ 0.0, 0.5, 1.0, 1.0 } }, 71 | .{ .pos = .{ -1.0, 1.0, -1.0 }, .color = .{ 1.0, 0.0, 0.5, 1.0 } }, 72 | .{ .pos = .{ -1.0, 1.0, 1.0 }, .color = .{ 1.0, 0.0, 0.5, 1.0 } }, 73 | .{ .pos = .{ 1.0, 1.0, 1.0 }, .color = .{ 1.0, 0.0, 0.5, 1.0 } }, 74 | .{ .pos = .{ 1.0, 1.0, -1.0 }, .color = .{ 1.0, 0.0, 0.5, 1.0 } }, 75 | // zig fmt: on 76 | }), 77 | .label = "vertices", 78 | }); 79 | 80 | // a regular index buffer 81 | state.bind.index_buffer = sg.makeBuffer(.{ 82 | .usage = .{ .index_buffer = true }, 83 | .data = sg.asRange(&[_]u16{ 84 | 0, 1, 2, 0, 2, 3, 85 | 6, 5, 4, 7, 6, 4, 86 | 8, 9, 10, 8, 10, 11, 87 | 14, 13, 12, 15, 14, 12, 88 | 16, 17, 18, 16, 18, 19, 89 | 22, 21, 20, 23, 22, 20, 90 | }), 91 | .label = "indices", 92 | }); 93 | 94 | // shader and pipeline object, note that the pipeline desc doesn't have a vertex layout 95 | state.pip = sg.makePipeline(.{ 96 | .shader = sg.makeShader(shd.vertexpullShaderDesc(sg.queryBackend())), 97 | .index_type = .UINT16, 98 | .depth = .{ 99 | .compare = .LESS_EQUAL, 100 | .write_enabled = true, 101 | }, 102 | .cull_mode = .BACK, 103 | .label = "pipeline", 104 | }); 105 | } 106 | 107 | export fn frame() void { 108 | const dt: f32 = @floatCast(sapp.frameDuration() * 60); 109 | state.rx += 1.0 * dt; 110 | state.ry += 2.0 * dt; 111 | const vs_params = computeVsParams(state.rx, state.ry); 112 | 113 | sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); 114 | if (sg.queryFeatures().compute) { 115 | sg.applyPipeline(state.pip); 116 | sg.applyBindings(state.bind); 117 | sg.applyUniforms(shd.UB_vs_params, sg.asRange(&vs_params)); 118 | sg.draw(0, 36, 1); 119 | } 120 | sg.endPass(); 121 | sg.commit(); 122 | } 123 | 124 | export fn cleanup() void { 125 | sg.shutdown(); 126 | } 127 | 128 | pub fn main() void { 129 | sapp.run(.{ 130 | .init_cb = init, 131 | .frame_cb = frame, 132 | .cleanup_cb = cleanup, 133 | .width = 800, 134 | .height = 600, 135 | .sample_count = 4, 136 | .icon = .{ .sokol_default = true }, 137 | .window_title = "vertexpull.zig", 138 | .logger = .{ .func = slog.func }, 139 | }); 140 | } 141 | 142 | fn computeVsParams(rx: f32, ry: f32) shd.VsParams { 143 | const rxm = mat4.rotate(rx, .{ .x = 1.0, .y = 0.0, .z = 0.0 }); 144 | const rym = mat4.rotate(ry, .{ .x = 0.0, .y = 1.0, .z = 0.0 }); 145 | const model = mat4.mul(rxm, rym); 146 | const aspect = sapp.widthf() / sapp.heightf(); 147 | const proj = mat4.persp(60.0, aspect, 0.01, 10.0); 148 | return shd.VsParams{ .mvp = mat4.mul(mat4.mul(proj, state.view), model) }; 149 | } 150 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_app.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_APP_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_app.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_audio.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_AUDIO_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_audio.h" 6 | 7 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_debugtext.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_DEBUGTEXT_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_gfx.h" 6 | #include "sokol_debugtext.h" 7 | 8 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_defines.h: -------------------------------------------------------------------------------- 1 | #if !defined(__ANDROID__) 2 | #define SOKOL_NO_ENTRY 3 | #endif 4 | #if defined(_WIN32) 5 | #define SOKOL_WIN32_FORCE_MAIN 6 | #endif 7 | // FIXME: macOS Zig HACK without this, some C stdlib headers throw errors 8 | #if defined(__APPLE__) 9 | #include 10 | #endif 11 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_fetch.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_FETCH_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_fetch.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_gfx.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_GFX_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_gfx.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_gl.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_GL_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_gfx.h" 6 | #include "sokol_gl.h" 7 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_glue.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_GLUE_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_app.h" 6 | #include "sokol_gfx.h" 7 | #include "sokol_glue.h" 8 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_glue.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL) 2 | #define SOKOL_GLUE_IMPL 3 | #endif 4 | #ifndef SOKOL_GLUE_INCLUDED 5 | /* 6 | sokol_glue.h -- glue helper functions for sokol headers 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_GLUE_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | ...optionally provide the following macros to override defaults: 17 | 18 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 19 | SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) 20 | SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL 21 | SOKOL_API_IMPL - public function implementation prefix (default: -) 22 | 23 | If sokol_glue.h is compiled as a DLL, define the following before 24 | including the declaration or implementation: 25 | 26 | SOKOL_DLL 27 | 28 | On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) 29 | or __declspec(dllimport) as needed. 30 | 31 | OVERVIEW 32 | ======== 33 | sokol_glue.h provides glue helper functions between sokol_gfx.h and sokol_app.h, 34 | so that sokol_gfx.h doesn't need to depend on sokol_app.h but can be 35 | used with different window system glue libraries. 36 | 37 | PROVIDED FUNCTIONS 38 | ================== 39 | 40 | sg_environment sglue_environment(void) 41 | 42 | Returns an sg_environment struct initialized by calling sokol_app.h 43 | functions. Use this in the sg_setup() call like this: 44 | 45 | sg_setup(&(sg_desc){ 46 | .environment = sglue_environment(), 47 | ... 48 | }); 49 | 50 | sg_swapchain sglue_swapchain(void) 51 | 52 | Returns an sg_swapchain struct initialized by calling sokol_app.h 53 | functions. Use this in sg_begin_pass() for a 'swapchain pass' like 54 | this: 55 | 56 | sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain(), ... }); 57 | 58 | LICENSE 59 | ======= 60 | zlib/libpng license 61 | 62 | Copyright (c) 2018 Andre Weissflog 63 | 64 | This software is provided 'as-is', without any express or implied warranty. 65 | In no event will the authors be held liable for any damages arising from the 66 | use of this software. 67 | 68 | Permission is granted to anyone to use this software for any purpose, 69 | including commercial applications, and to alter it and redistribute it 70 | freely, subject to the following restrictions: 71 | 72 | 1. The origin of this software must not be misrepresented; you must not 73 | claim that you wrote the original software. If you use this software in a 74 | product, an acknowledgment in the product documentation would be 75 | appreciated but is not required. 76 | 77 | 2. Altered source versions must be plainly marked as such, and must not 78 | be misrepresented as being the original software. 79 | 80 | 3. This notice may not be removed or altered from any source 81 | distribution. 82 | */ 83 | #define SOKOL_GLUE_INCLUDED 84 | 85 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL) 86 | #define SOKOL_GLUE_API_DECL SOKOL_API_DECL 87 | #endif 88 | #ifndef SOKOL_GLUE_API_DECL 89 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL) 90 | #define SOKOL_GLUE_API_DECL __declspec(dllexport) 91 | #elif defined(_WIN32) && defined(SOKOL_DLL) 92 | #define SOKOL_GLUE_API_DECL __declspec(dllimport) 93 | #else 94 | #define SOKOL_GLUE_API_DECL extern 95 | #endif 96 | #endif 97 | 98 | #ifndef SOKOL_GFX_INCLUDED 99 | #error "Please include sokol_gfx.h before sokol_glue.h" 100 | #endif 101 | 102 | #ifdef __cplusplus 103 | extern "C" { 104 | #endif 105 | 106 | SOKOL_GLUE_API_DECL sg_environment sglue_environment(void); 107 | SOKOL_GLUE_API_DECL sg_swapchain sglue_swapchain(void); 108 | 109 | #ifdef __cplusplus 110 | } /* extern "C" */ 111 | #endif 112 | #endif /* SOKOL_GLUE_INCLUDED */ 113 | 114 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 115 | #ifdef SOKOL_GLUE_IMPL 116 | #define SOKOL_GLUE_IMPL_INCLUDED (1) 117 | #include /* memset */ 118 | 119 | #ifndef SOKOL_APP_INCLUDED 120 | #error "Please include sokol_app.h before the sokol_glue.h implementation" 121 | #endif 122 | 123 | #ifndef SOKOL_API_IMPL 124 | #define SOKOL_API_IMPL 125 | #endif 126 | 127 | 128 | SOKOL_API_IMPL sg_environment sglue_environment(void) { 129 | sg_environment env; 130 | memset(&env, 0, sizeof(env)); 131 | env.defaults.color_format = (sg_pixel_format) sapp_color_format(); 132 | env.defaults.depth_format = (sg_pixel_format) sapp_depth_format(); 133 | env.defaults.sample_count = sapp_sample_count(); 134 | env.metal.device = sapp_metal_get_device(); 135 | env.d3d11.device = sapp_d3d11_get_device(); 136 | env.d3d11.device_context = sapp_d3d11_get_device_context(); 137 | env.wgpu.device = sapp_wgpu_get_device(); 138 | return env; 139 | } 140 | 141 | SOKOL_API_IMPL sg_swapchain sglue_swapchain(void) { 142 | sg_swapchain swapchain; 143 | memset(&swapchain, 0, sizeof(swapchain)); 144 | swapchain.width = sapp_width(); 145 | swapchain.height = sapp_height(); 146 | swapchain.sample_count = sapp_sample_count(); 147 | swapchain.color_format = (sg_pixel_format)sapp_color_format(); 148 | swapchain.depth_format = (sg_pixel_format)sapp_depth_format(); 149 | swapchain.metal.current_drawable = sapp_metal_get_current_drawable(); 150 | swapchain.metal.depth_stencil_texture = sapp_metal_get_depth_stencil_texture(); 151 | swapchain.metal.msaa_color_texture = sapp_metal_get_msaa_color_texture(); 152 | swapchain.d3d11.render_view = sapp_d3d11_get_render_view(); 153 | swapchain.d3d11.resolve_view = sapp_d3d11_get_resolve_view(); 154 | swapchain.d3d11.depth_stencil_view = sapp_d3d11_get_depth_stencil_view(); 155 | swapchain.wgpu.render_view = sapp_wgpu_get_render_view(); 156 | swapchain.wgpu.resolve_view = sapp_wgpu_get_resolve_view(); 157 | swapchain.wgpu.depth_stencil_view = sapp_wgpu_get_depth_stencil_view(); 158 | swapchain.gl.framebuffer = sapp_gl_get_framebuffer(); 159 | return swapchain; 160 | } 161 | 162 | #endif /* SOKOL_GLUE_IMPL */ 163 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_imgui.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #ifndef CIMGUI_HEADER_PATH 3 | #define CIMGUI_HEADER_PATH "cimgui.h" 4 | #endif 5 | // NOTE: this is only needed for the old cimgui.h bindings 6 | #define CIMGUI_DEFINE_ENUMS_AND_STRUCTS 7 | #define SOKOL_IMGUI_IMPL 8 | #include CIMGUI_HEADER_PATH 9 | #endif 10 | #include "sokol_defines.h" 11 | #include "sokol_app.h" 12 | #include "sokol_gfx.h" 13 | #include "sokol_imgui.h" 14 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_log.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_LOG_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_log.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_log.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_LOG_IMPL) 2 | #define SOKOL_LOG_IMPL 3 | #endif 4 | #ifndef SOKOL_LOG_INCLUDED 5 | /* 6 | sokol_log.h -- common logging callback for sokol headers 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Example code: https://github.com/floooh/sokol-samples 11 | 12 | Do this: 13 | #define SOKOL_IMPL or 14 | #define SOKOL_LOG_IMPL 15 | before you include this file in *one* C or C++ file to create the 16 | implementation. 17 | 18 | Optionally provide the following defines when building the implementation: 19 | 20 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 21 | SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) 22 | SOKOL_LOG_API_DECL - public function declaration prefix (default: extern) 23 | SOKOL_API_DECL - same as SOKOL_GFX_API_DECL 24 | SOKOL_API_IMPL - public function implementation prefix (default: -) 25 | 26 | Optionally define the following for verbose output: 27 | 28 | SOKOL_DEBUG - by default this is defined if _DEBUG is defined 29 | 30 | 31 | OVERVIEW 32 | ======== 33 | sokol_log.h provides a default logging callback for other sokol headers. 34 | 35 | To use the default log callback, just include sokol_log.h and provide 36 | a function pointer to the 'slog_func' function when setting up the 37 | sokol library: 38 | 39 | For instance with sokol_audio.h: 40 | 41 | #include "sokol_log.h" 42 | ... 43 | saudio_setup(&(saudio_desc){ .logger.func = slog_func }); 44 | 45 | Logging output goes to stderr and/or a platform specific logging subsystem 46 | (which means that in some scenarios you might see logging messages duplicated): 47 | 48 | - Windows: stderr + OutputDebugStringA() 49 | - macOS/iOS/Linux: stderr + syslog() 50 | - Emscripten: console.info()/warn()/error() 51 | - Android: __android_log_write() 52 | 53 | On Windows with sokol_app.h also note the runtime config items to make 54 | stdout/stderr output visible on the console for WinMain() applications 55 | via sapp_desc.win32_console_attach or sapp_desc.win32_console_create, 56 | however when running in a debugger on Windows, the logging output should 57 | show up on the debug output UI panel. 58 | 59 | In debug mode, a log message might look like this: 60 | 61 | [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: 62 | SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas 63 | 64 | The source path and line number is formatted like compiler errors, in some IDEs (like VSCode) 65 | such error messages are clickable. 66 | 67 | In release mode, logging is less verbose as to not bloat the executable with string data, but you still get 68 | enough information to identify the type and location of an error: 69 | 70 | [sspine][error][id:12][line:3472] 71 | 72 | RULES FOR WRITING YOUR OWN LOGGING FUNCTION 73 | =========================================== 74 | - must be re-entrant because it might be called from different threads 75 | - must treat **all** provided string pointers as optional (can be null) 76 | - don't store the string pointers, copy the string data instead 77 | - must not return for log level panic 78 | 79 | LICENSE 80 | ======= 81 | zlib/libpng license 82 | 83 | Copyright (c) 2023 Andre Weissflog 84 | 85 | This software is provided 'as-is', without any express or implied warranty. 86 | In no event will the authors be held liable for any damages arising from the 87 | use of this software. 88 | 89 | Permission is granted to anyone to use this software for any purpose, 90 | including commercial applications, and to alter it and redistribute it 91 | freely, subject to the following restrictions: 92 | 93 | 1. The origin of this software must not be misrepresented; you must not 94 | claim that you wrote the original software. If you use this software in a 95 | product, an acknowledgment in the product documentation would be 96 | appreciated but is not required. 97 | 98 | 2. Altered source versions must be plainly marked as such, and must not 99 | be misrepresented as being the original software. 100 | 101 | 3. This notice may not be removed or altered from any source 102 | distribution. 103 | */ 104 | #define SOKOL_LOG_INCLUDED (1) 105 | #include 106 | 107 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_LOG_API_DECL) 108 | #define SOKOL_LOG_API_DECL SOKOL_API_DECL 109 | #endif 110 | #ifndef SOKOL_LOG_API_DECL 111 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_LOG_IMPL) 112 | #define SOKOL_LOG_API_DECL __declspec(dllexport) 113 | #elif defined(_WIN32) && defined(SOKOL_DLL) 114 | #define SOKOL_LOG_API_DECL __declspec(dllimport) 115 | #else 116 | #define SOKOL_LOG_API_DECL extern 117 | #endif 118 | #endif 119 | 120 | #ifdef __cplusplus 121 | extern "C" { 122 | #endif 123 | 124 | /* 125 | Plug this function into the 'logger.func' struct item when initializing any of the sokol 126 | headers. For instance for sokol_audio.h it would look like this: 127 | 128 | saudio_setup(&(saudio_desc){ 129 | .logger = { 130 | .func = slog_func 131 | } 132 | }); 133 | */ 134 | SOKOL_LOG_API_DECL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data); 135 | 136 | #ifdef __cplusplus 137 | } // extern "C" 138 | #endif 139 | #endif // SOKOL_LOG_INCLUDED 140 | 141 | // ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ 142 | // ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ 143 | // ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ 144 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 145 | // ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ 146 | // 147 | // >>implementation 148 | #ifdef SOKOL_LOG_IMPL 149 | #define SOKOL_LOG_IMPL_INCLUDED (1) 150 | 151 | #ifndef SOKOL_API_IMPL 152 | #define SOKOL_API_IMPL 153 | #endif 154 | #ifndef SOKOL_DEBUG 155 | #ifndef NDEBUG 156 | #define SOKOL_DEBUG 157 | #endif 158 | #endif 159 | #ifndef SOKOL_ASSERT 160 | #include 161 | #define SOKOL_ASSERT(c) assert(c) 162 | #endif 163 | 164 | #ifndef _SOKOL_PRIVATE 165 | #if defined(__GNUC__) || defined(__clang__) 166 | #define _SOKOL_PRIVATE __attribute__((unused)) static 167 | #else 168 | #define _SOKOL_PRIVATE static 169 | #endif 170 | #endif 171 | 172 | #ifndef _SOKOL_UNUSED 173 | #define _SOKOL_UNUSED(x) (void)(x) 174 | #endif 175 | 176 | // platform detection 177 | #if defined(__APPLE__) 178 | #define _SLOG_APPLE (1) 179 | #elif defined(__EMSCRIPTEN__) 180 | #define _SLOG_EMSCRIPTEN (1) 181 | #elif defined(_WIN32) 182 | #define _SLOG_WINDOWS (1) 183 | #elif defined(__ANDROID__) 184 | #define _SLOG_ANDROID (1) 185 | #elif defined(__linux__) || defined(__unix__) 186 | #define _SLOG_LINUX (1) 187 | #else 188 | #error "sokol_log.h: unknown platform" 189 | #endif 190 | 191 | #include // abort 192 | #include // fputs 193 | #include // size_t 194 | 195 | #if defined(_SLOG_EMSCRIPTEN) 196 | #include 197 | #elif defined(_SLOG_WINDOWS) 198 | #ifndef WIN32_LEAN_AND_MEAN 199 | #define WIN32_LEAN_AND_MEAN 200 | #endif 201 | #ifndef NOMINMAX 202 | #define NOMINMAX 203 | #endif 204 | #include 205 | #elif defined(_SLOG_ANDROID) 206 | #include 207 | #elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE) 208 | #include 209 | #endif 210 | 211 | // size of line buffer (on stack!) in bytes including terminating zero 212 | #define _SLOG_LINE_LENGTH (512) 213 | 214 | _SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, char* end) { 215 | if (str) { 216 | char c; 217 | while (((c = *str++) != 0) && (dst < (end - 1))) { 218 | *dst++ = c; 219 | } 220 | } 221 | *dst = 0; 222 | return dst; 223 | } 224 | 225 | _SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) { 226 | const size_t max_digits_and_null = 11; 227 | if (buf_size < max_digits_and_null) { 228 | return 0; 229 | } 230 | char* p = buf + max_digits_and_null; 231 | *--p = 0; 232 | do { 233 | *--p = '0' + (x % 10); 234 | x /= 10; 235 | } while (x != 0); 236 | return p; 237 | } 238 | 239 | #if defined(_SLOG_EMSCRIPTEN) 240 | EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), { 241 | const str = UTF8ToString(c_str); 242 | switch (level) { 243 | case 0: console.error(str); break; 244 | case 1: console.error(str); break; 245 | case 2: console.warn(str); break; 246 | default: console.info(str); break; 247 | } 248 | }) 249 | #endif 250 | 251 | SOKOL_API_IMPL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) { 252 | _SOKOL_UNUSED(user_data); 253 | 254 | const char* log_level_str; 255 | switch (log_level) { 256 | case 0: log_level_str = "panic"; break; 257 | case 1: log_level_str = "error"; break; 258 | case 2: log_level_str = "warning"; break; 259 | default: log_level_str = "info"; break; 260 | } 261 | 262 | // build log output line 263 | char line_buf[_SLOG_LINE_LENGTH]; 264 | char* str = line_buf; 265 | char* end = line_buf + sizeof(line_buf); 266 | char num_buf[32]; 267 | if (tag) { 268 | str = _slog_append("[", str, end); 269 | str = _slog_append(tag, str, end); 270 | str = _slog_append("]", str, end); 271 | } 272 | str = _slog_append("[", str, end); 273 | str = _slog_append(log_level_str, str, end); 274 | str = _slog_append("]", str, end); 275 | str = _slog_append("[id:", str, end); 276 | str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end); 277 | str = _slog_append("]", str, end); 278 | // if a filename is provided, build a clickable log message that's compatible with compiler error messages 279 | if (filename) { 280 | str = _slog_append(" ", str, end); 281 | #if defined(_MSC_VER) 282 | // MSVC compiler error format 283 | str = _slog_append(filename, str, end); 284 | str = _slog_append("(", str, end); 285 | str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); 286 | str = _slog_append("): ", str, end); 287 | #else 288 | // gcc/clang compiler error format 289 | str = _slog_append(filename, str, end); 290 | str = _slog_append(":", str, end); 291 | str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); 292 | str = _slog_append(":0: ", str, end); 293 | #endif 294 | } 295 | else { 296 | str = _slog_append("[line:", str, end); 297 | str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); 298 | str = _slog_append("] ", str, end); 299 | } 300 | if (message) { 301 | str = _slog_append("\n\t", str, end); 302 | str = _slog_append(message, str, end); 303 | } 304 | str = _slog_append("\n\n", str, end); 305 | if (0 == log_level) { 306 | str = _slog_append("ABORTING because of [panic]\n", str, end); 307 | (void)str; 308 | } 309 | 310 | // print to stderr? 311 | #if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE) 312 | fputs(line_buf, stderr); 313 | #endif 314 | 315 | // platform specific logging calls 316 | #if defined(_SLOG_WINDOWS) 317 | OutputDebugStringA(line_buf); 318 | #elif defined(_SLOG_ANDROID) 319 | int prio; 320 | switch (log_level) { 321 | case 0: prio = ANDROID_LOG_FATAL; break; 322 | case 1: prio = ANDROID_LOG_ERROR; break; 323 | case 2: prio = ANDROID_LOG_WARN; break; 324 | default: prio = ANDROID_LOG_INFO; break; 325 | } 326 | __android_log_write(prio, "SOKOL", line_buf); 327 | #elif defined(_SLOG_EMSCRIPTEN) 328 | slog_js_log(log_level, line_buf); 329 | #endif 330 | if (0 == log_level) { 331 | abort(); 332 | } 333 | } 334 | #endif // SOKOL_LOG_IMPL 335 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_shape.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_SHAPE_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_gfx.h" 6 | #include "sokol_shape.h" 7 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_time.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_TIME_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_time.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_time.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL) 2 | #define SOKOL_TIME_IMPL 3 | #endif 4 | #ifndef SOKOL_TIME_INCLUDED 5 | /* 6 | sokol_time.h -- simple cross-platform time measurement 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_TIME_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | Optionally provide the following defines with your own implementations: 17 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 18 | SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) 19 | SOKOL_API_DECL - same as SOKOL_TIME_API_DECL 20 | SOKOL_API_IMPL - public function implementation prefix (default: -) 21 | 22 | If sokol_time.h is compiled as a DLL, define the following before 23 | including the declaration or implementation: 24 | 25 | SOKOL_DLL 26 | 27 | On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) 28 | or __declspec(dllimport) as needed. 29 | 30 | void stm_setup(); 31 | Call once before any other functions to initialize sokol_time 32 | (this calls for instance QueryPerformanceFrequency on Windows) 33 | 34 | uint64_t stm_now(); 35 | Get current point in time in unspecified 'ticks'. The value that 36 | is returned has no relation to the 'wall-clock' time and is 37 | not in a specific time unit, it is only useful to compute 38 | time differences. 39 | 40 | uint64_t stm_diff(uint64_t new, uint64_t old); 41 | Computes the time difference between new and old. This will always 42 | return a positive, non-zero value. 43 | 44 | uint64_t stm_since(uint64_t start); 45 | Takes the current time, and returns the elapsed time since start 46 | (this is a shortcut for "stm_diff(stm_now(), start)") 47 | 48 | uint64_t stm_laptime(uint64_t* last_time); 49 | This is useful for measuring frame time and other recurring 50 | events. It takes the current time, returns the time difference 51 | to the value in last_time, and stores the current time in 52 | last_time for the next call. If the value in last_time is 0, 53 | the return value will be zero (this usually happens on the 54 | very first call). 55 | 56 | uint64_t stm_round_to_common_refresh_rate(uint64_t duration) 57 | This oddly named function takes a measured frame time and 58 | returns the closest "nearby" common display refresh rate frame duration 59 | in ticks. If the input duration isn't close to any common display 60 | refresh rate, the input duration will be returned unchanged as a fallback. 61 | The main purpose of this function is to remove jitter/inaccuracies from 62 | measured frame times, and instead use the display refresh rate as 63 | frame duration. 64 | NOTE: for more robust frame timing, consider using the 65 | sokol_app.h function sapp_frame_duration() 66 | 67 | Use the following functions to convert a duration in ticks into 68 | useful time units: 69 | 70 | double stm_sec(uint64_t ticks); 71 | double stm_ms(uint64_t ticks); 72 | double stm_us(uint64_t ticks); 73 | double stm_ns(uint64_t ticks); 74 | Converts a tick value into seconds, milliseconds, microseconds 75 | or nanoseconds. Note that not all platforms will have nanosecond 76 | or even microsecond precision. 77 | 78 | Uses the following time measurement functions under the hood: 79 | 80 | Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() 81 | MacOS/iOS: mach_absolute_time() 82 | emscripten: emscripten_get_now() 83 | Linux+others: clock_gettime(CLOCK_MONOTONIC) 84 | 85 | zlib/libpng license 86 | 87 | Copyright (c) 2018 Andre Weissflog 88 | 89 | This software is provided 'as-is', without any express or implied warranty. 90 | In no event will the authors be held liable for any damages arising from the 91 | use of this software. 92 | 93 | Permission is granted to anyone to use this software for any purpose, 94 | including commercial applications, and to alter it and redistribute it 95 | freely, subject to the following restrictions: 96 | 97 | 1. The origin of this software must not be misrepresented; you must not 98 | claim that you wrote the original software. If you use this software in a 99 | product, an acknowledgment in the product documentation would be 100 | appreciated but is not required. 101 | 102 | 2. Altered source versions must be plainly marked as such, and must not 103 | be misrepresented as being the original software. 104 | 105 | 3. This notice may not be removed or altered from any source 106 | distribution. 107 | */ 108 | #define SOKOL_TIME_INCLUDED (1) 109 | #include 110 | 111 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL) 112 | #define SOKOL_TIME_API_DECL SOKOL_API_DECL 113 | #endif 114 | #ifndef SOKOL_TIME_API_DECL 115 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL) 116 | #define SOKOL_TIME_API_DECL __declspec(dllexport) 117 | #elif defined(_WIN32) && defined(SOKOL_DLL) 118 | #define SOKOL_TIME_API_DECL __declspec(dllimport) 119 | #else 120 | #define SOKOL_TIME_API_DECL extern 121 | #endif 122 | #endif 123 | 124 | #ifdef __cplusplus 125 | extern "C" { 126 | #endif 127 | 128 | SOKOL_TIME_API_DECL void stm_setup(void); 129 | SOKOL_TIME_API_DECL uint64_t stm_now(void); 130 | SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); 131 | SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks); 132 | SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time); 133 | SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks); 134 | SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks); 135 | SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks); 136 | SOKOL_TIME_API_DECL double stm_us(uint64_t ticks); 137 | SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks); 138 | 139 | #ifdef __cplusplus 140 | } /* extern "C" */ 141 | #endif 142 | #endif // SOKOL_TIME_INCLUDED 143 | 144 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 145 | #ifdef SOKOL_TIME_IMPL 146 | #define SOKOL_TIME_IMPL_INCLUDED (1) 147 | #include /* memset */ 148 | 149 | #ifndef SOKOL_API_IMPL 150 | #define SOKOL_API_IMPL 151 | #endif 152 | #ifndef SOKOL_ASSERT 153 | #include 154 | #define SOKOL_ASSERT(c) assert(c) 155 | #endif 156 | #ifndef _SOKOL_PRIVATE 157 | #if defined(__GNUC__) || defined(__clang__) 158 | #define _SOKOL_PRIVATE __attribute__((unused)) static 159 | #else 160 | #define _SOKOL_PRIVATE static 161 | #endif 162 | #endif 163 | 164 | #if defined(_WIN32) 165 | #ifndef WIN32_LEAN_AND_MEAN 166 | #define WIN32_LEAN_AND_MEAN 167 | #endif 168 | #include 169 | typedef struct { 170 | uint32_t initialized; 171 | LARGE_INTEGER freq; 172 | LARGE_INTEGER start; 173 | } _stm_state_t; 174 | #elif defined(__APPLE__) && defined(__MACH__) 175 | #include 176 | typedef struct { 177 | uint32_t initialized; 178 | mach_timebase_info_data_t timebase; 179 | uint64_t start; 180 | } _stm_state_t; 181 | #elif defined(__EMSCRIPTEN__) 182 | #include 183 | typedef struct { 184 | uint32_t initialized; 185 | double start; 186 | } _stm_state_t; 187 | #else /* anything else, this will need more care for non-Linux platforms */ 188 | #ifdef ESP8266 189 | // On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined 190 | #define CLOCK_MONOTONIC 0 191 | #endif 192 | #include 193 | typedef struct { 194 | uint32_t initialized; 195 | uint64_t start; 196 | } _stm_state_t; 197 | #endif 198 | static _stm_state_t _stm; 199 | 200 | /* prevent 64-bit overflow when computing relative timestamp 201 | see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 202 | */ 203 | #if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) 204 | _SOKOL_PRIVATE int64_t _stm_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { 205 | int64_t q = value / denom; 206 | int64_t r = value % denom; 207 | return q * numer + r * numer / denom; 208 | } 209 | #endif 210 | 211 | SOKOL_API_IMPL void stm_setup(void) { 212 | memset(&_stm, 0, sizeof(_stm)); 213 | _stm.initialized = 0xABCDABCD; 214 | #if defined(_WIN32) 215 | QueryPerformanceFrequency(&_stm.freq); 216 | QueryPerformanceCounter(&_stm.start); 217 | #elif defined(__APPLE__) && defined(__MACH__) 218 | mach_timebase_info(&_stm.timebase); 219 | _stm.start = mach_absolute_time(); 220 | #elif defined(__EMSCRIPTEN__) 221 | _stm.start = emscripten_get_now(); 222 | #else 223 | struct timespec ts; 224 | clock_gettime(CLOCK_MONOTONIC, &ts); 225 | _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; 226 | #endif 227 | } 228 | 229 | SOKOL_API_IMPL uint64_t stm_now(void) { 230 | SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); 231 | uint64_t now; 232 | #if defined(_WIN32) 233 | LARGE_INTEGER qpc_t; 234 | QueryPerformanceCounter(&qpc_t); 235 | now = (uint64_t) _stm_int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); 236 | #elif defined(__APPLE__) && defined(__MACH__) 237 | const uint64_t mach_now = mach_absolute_time() - _stm.start; 238 | now = (uint64_t) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); 239 | #elif defined(__EMSCRIPTEN__) 240 | double js_now = emscripten_get_now() - _stm.start; 241 | now = (uint64_t) (js_now * 1000000.0); 242 | #else 243 | struct timespec ts; 244 | clock_gettime(CLOCK_MONOTONIC, &ts); 245 | now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; 246 | #endif 247 | return now; 248 | } 249 | 250 | SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { 251 | if (new_ticks > old_ticks) { 252 | return new_ticks - old_ticks; 253 | } 254 | else { 255 | return 1; 256 | } 257 | } 258 | 259 | SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { 260 | return stm_diff(stm_now(), start_ticks); 261 | } 262 | 263 | SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { 264 | SOKOL_ASSERT(last_time); 265 | uint64_t dt = 0; 266 | uint64_t now = stm_now(); 267 | if (0 != *last_time) { 268 | dt = stm_diff(now, *last_time); 269 | } 270 | *last_time = now; 271 | return dt; 272 | } 273 | 274 | // first number is frame duration in ns, second number is tolerance in ns, 275 | // the resulting min/max values must not overlap! 276 | static const uint64_t _stm_refresh_rates[][2] = { 277 | { 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms 278 | { 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms 279 | { 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms 280 | { 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25 281 | { 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms 282 | { 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms 283 | { 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms 284 | { 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms 285 | { 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms 286 | { 0, 0 }, // keep the last element always at zero 287 | }; 288 | 289 | SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) { 290 | uint64_t ns; 291 | int i = 0; 292 | while (0 != (ns = _stm_refresh_rates[i][0])) { 293 | uint64_t tol = _stm_refresh_rates[i][1]; 294 | if ((ticks > (ns - tol)) && (ticks < (ns + tol))) { 295 | return ns; 296 | } 297 | i++; 298 | } 299 | // fallthrough: didn't fit into any buckets 300 | return ticks; 301 | } 302 | 303 | SOKOL_API_IMPL double stm_sec(uint64_t ticks) { 304 | return (double)ticks / 1000000000.0; 305 | } 306 | 307 | SOKOL_API_IMPL double stm_ms(uint64_t ticks) { 308 | return (double)ticks / 1000000.0; 309 | } 310 | 311 | SOKOL_API_IMPL double stm_us(uint64_t ticks) { 312 | return (double)ticks / 1000.0; 313 | } 314 | 315 | SOKOL_API_IMPL double stm_ns(uint64_t ticks) { 316 | return (double)ticks; 317 | } 318 | #endif /* SOKOL_TIME_IMPL */ 319 | 320 | -------------------------------------------------------------------------------- /src/sokol/glue.zig: -------------------------------------------------------------------------------- 1 | // machine generated, do not edit 2 | 3 | // 4 | // sokol_glue.h -- glue helper functions for sokol headers 5 | // 6 | // Project URL: https://github.com/floooh/sokol 7 | // 8 | // Do this: 9 | // #define SOKOL_IMPL or 10 | // #define SOKOL_GLUE_IMPL 11 | // before you include this file in *one* C or C++ file to create the 12 | // implementation. 13 | // 14 | // ...optionally provide the following macros to override defaults: 15 | // 16 | // SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 17 | // SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) 18 | // SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL 19 | // SOKOL_API_IMPL - public function implementation prefix (default: -) 20 | // 21 | // If sokol_glue.h is compiled as a DLL, define the following before 22 | // including the declaration or implementation: 23 | // 24 | // SOKOL_DLL 25 | // 26 | // On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) 27 | // or __declspec(dllimport) as needed. 28 | // 29 | // OVERVIEW 30 | // ======== 31 | // sokol_glue.h provides glue helper functions between sokol_gfx.h and sokol_app.h, 32 | // so that sokol_gfx.h doesn't need to depend on sokol_app.h but can be 33 | // used with different window system glue libraries. 34 | // 35 | // PROVIDED FUNCTIONS 36 | // ================== 37 | // 38 | // sg_environment sglue_environment(void) 39 | // 40 | // Returns an sg_environment struct initialized by calling sokol_app.h 41 | // functions. Use this in the sg_setup() call like this: 42 | // 43 | // sg_setup(&(sg_desc){ 44 | // .environment = sglue_environment(), 45 | // ... 46 | // }); 47 | // 48 | // sg_swapchain sglue_swapchain(void) 49 | // 50 | // Returns an sg_swapchain struct initialized by calling sokol_app.h 51 | // functions. Use this in sg_begin_pass() for a 'swapchain pass' like 52 | // this: 53 | // 54 | // sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain(), ... }); 55 | // 56 | // LICENSE 57 | // ======= 58 | // zlib/libpng license 59 | // 60 | // Copyright (c) 2018 Andre Weissflog 61 | // 62 | // This software is provided 'as-is', without any express or implied warranty. 63 | // In no event will the authors be held liable for any damages arising from the 64 | // use of this software. 65 | // 66 | // Permission is granted to anyone to use this software for any purpose, 67 | // including commercial applications, and to alter it and redistribute it 68 | // freely, subject to the following restrictions: 69 | // 70 | // 1. The origin of this software must not be misrepresented; you must not 71 | // claim that you wrote the original software. If you use this software in a 72 | // product, an acknowledgment in the product documentation would be 73 | // appreciated but is not required. 74 | // 75 | // 2. Altered source versions must be plainly marked as such, and must not 76 | // be misrepresented as being the original software. 77 | // 78 | // 3. This notice may not be removed or altered from any source 79 | // distribution. 80 | 81 | const builtin = @import("builtin"); 82 | const sg = @import("gfx.zig"); 83 | 84 | // helper function to convert a C string to a Zig string slice 85 | fn cStrToZig(c_str: [*c]const u8) [:0]const u8 { 86 | return @import("std").mem.span(c_str); 87 | } 88 | extern fn sglue_environment() sg.Environment; 89 | 90 | pub fn environment() sg.Environment { 91 | return sglue_environment(); 92 | } 93 | 94 | extern fn sglue_swapchain() sg.Swapchain; 95 | 96 | pub fn swapchain() sg.Swapchain { 97 | return sglue_swapchain(); 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/sokol/log.zig: -------------------------------------------------------------------------------- 1 | // machine generated, do not edit 2 | 3 | // 4 | // sokol_log.h -- common logging callback for sokol headers 5 | // 6 | // Project URL: https://github.com/floooh/sokol 7 | // 8 | // Example code: https://github.com/floooh/sokol-samples 9 | // 10 | // Do this: 11 | // #define SOKOL_IMPL or 12 | // #define SOKOL_LOG_IMPL 13 | // before you include this file in *one* C or C++ file to create the 14 | // implementation. 15 | // 16 | // Optionally provide the following defines when building the implementation: 17 | // 18 | // SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 19 | // SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) 20 | // SOKOL_LOG_API_DECL - public function declaration prefix (default: extern) 21 | // SOKOL_API_DECL - same as SOKOL_GFX_API_DECL 22 | // SOKOL_API_IMPL - public function implementation prefix (default: -) 23 | // 24 | // Optionally define the following for verbose output: 25 | // 26 | // SOKOL_DEBUG - by default this is defined if _DEBUG is defined 27 | // 28 | // 29 | // OVERVIEW 30 | // ======== 31 | // sokol_log.h provides a default logging callback for other sokol headers. 32 | // 33 | // To use the default log callback, just include sokol_log.h and provide 34 | // a function pointer to the 'slog_func' function when setting up the 35 | // sokol library: 36 | // 37 | // For instance with sokol_audio.h: 38 | // 39 | // #include "sokol_log.h" 40 | // ... 41 | // saudio_setup(&(saudio_desc){ .logger.func = slog_func }); 42 | // 43 | // Logging output goes to stderr and/or a platform specific logging subsystem 44 | // (which means that in some scenarios you might see logging messages duplicated): 45 | // 46 | // - Windows: stderr + OutputDebugStringA() 47 | // - macOS/iOS/Linux: stderr + syslog() 48 | // - Emscripten: console.info()/warn()/error() 49 | // - Android: __android_log_write() 50 | // 51 | // On Windows with sokol_app.h also note the runtime config items to make 52 | // stdout/stderr output visible on the console for WinMain() applications 53 | // via sapp_desc.win32_console_attach or sapp_desc.win32_console_create, 54 | // however when running in a debugger on Windows, the logging output should 55 | // show up on the debug output UI panel. 56 | // 57 | // In debug mode, a log message might look like this: 58 | // 59 | // [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: 60 | // SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas 61 | // 62 | // The source path and line number is formatted like compiler errors, in some IDEs (like VSCode) 63 | // such error messages are clickable. 64 | // 65 | // In release mode, logging is less verbose as to not bloat the executable with string data, but you still get 66 | // enough information to identify the type and location of an error: 67 | // 68 | // [sspine][error][id:12][line:3472] 69 | // 70 | // RULES FOR WRITING YOUR OWN LOGGING FUNCTION 71 | // =========================================== 72 | // - must be re-entrant because it might be called from different threads 73 | // - must treat **all** provided string pointers as optional (can be null) 74 | // - don't store the string pointers, copy the string data instead 75 | // - must not return for log level panic 76 | // 77 | // LICENSE 78 | // ======= 79 | // zlib/libpng license 80 | // 81 | // Copyright (c) 2023 Andre Weissflog 82 | // 83 | // This software is provided 'as-is', without any express or implied warranty. 84 | // In no event will the authors be held liable for any damages arising from the 85 | // use of this software. 86 | // 87 | // Permission is granted to anyone to use this software for any purpose, 88 | // including commercial applications, and to alter it and redistribute it 89 | // freely, subject to the following restrictions: 90 | // 91 | // 1. The origin of this software must not be misrepresented; you must not 92 | // claim that you wrote the original software. If you use this software in a 93 | // product, an acknowledgment in the product documentation would be 94 | // appreciated but is not required. 95 | // 96 | // 2. Altered source versions must be plainly marked as such, and must not 97 | // be misrepresented as being the original software. 98 | // 99 | // 3. This notice may not be removed or altered from any source 100 | // distribution. 101 | 102 | const builtin = @import("builtin"); 103 | 104 | // helper function to convert a C string to a Zig string slice 105 | fn cStrToZig(c_str: [*c]const u8) [:0]const u8 { 106 | return @import("std").mem.span(c_str); 107 | } 108 | /// Plug this function into the 'logger.func' struct item when initializing any of the sokol 109 | /// headers. For instance for sokol_audio.h it would look like this: 110 | /// 111 | /// saudio_setup(&(saudio_desc){ 112 | /// .logger = { 113 | /// .func = slog_func 114 | /// } 115 | /// }); 116 | extern fn slog_func([*c]const u8, u32, u32, [*c]const u8, u32, [*c]const u8, ?*anyopaque) void; 117 | 118 | /// Plug this function into the 'logger.func' struct item when initializing any of the sokol 119 | /// headers. For instance for sokol_audio.h it would look like this: 120 | /// 121 | /// saudio_setup(&(saudio_desc){ 122 | /// .logger = { 123 | /// .func = slog_func 124 | /// } 125 | /// }); 126 | pub const func = slog_func; 127 | -------------------------------------------------------------------------------- /src/sokol/sokol.zig: -------------------------------------------------------------------------------- 1 | pub const log = @import("log.zig"); 2 | pub const gfx = @import("gfx.zig"); 3 | pub const app = @import("app.zig"); 4 | pub const time = @import("time.zig"); 5 | pub const audio = @import("audio.zig"); 6 | pub const gl = @import("gl.zig"); 7 | pub const debugtext = @import("debugtext.zig"); 8 | pub const shape = @import("shape.zig"); 9 | pub const glue = @import("glue.zig"); 10 | pub const fetch = @import("fetch.zig"); 11 | pub const imgui = @import("imgui.zig"); 12 | -------------------------------------------------------------------------------- /src/sokol/time.zig: -------------------------------------------------------------------------------- 1 | // machine generated, do not edit 2 | 3 | // 4 | // sokol_time.h -- simple cross-platform time measurement 5 | // 6 | // Project URL: https://github.com/floooh/sokol 7 | // 8 | // Do this: 9 | // #define SOKOL_IMPL or 10 | // #define SOKOL_TIME_IMPL 11 | // before you include this file in *one* C or C++ file to create the 12 | // implementation. 13 | // 14 | // Optionally provide the following defines with your own implementations: 15 | // SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 16 | // SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) 17 | // SOKOL_API_DECL - same as SOKOL_TIME_API_DECL 18 | // SOKOL_API_IMPL - public function implementation prefix (default: -) 19 | // 20 | // If sokol_time.h is compiled as a DLL, define the following before 21 | // including the declaration or implementation: 22 | // 23 | // SOKOL_DLL 24 | // 25 | // On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) 26 | // or __declspec(dllimport) as needed. 27 | // 28 | // void stm_setup(); 29 | // Call once before any other functions to initialize sokol_time 30 | // (this calls for instance QueryPerformanceFrequency on Windows) 31 | // 32 | // uint64_t stm_now(); 33 | // Get current point in time in unspecified 'ticks'. The value that 34 | // is returned has no relation to the 'wall-clock' time and is 35 | // not in a specific time unit, it is only useful to compute 36 | // time differences. 37 | // 38 | // uint64_t stm_diff(uint64_t new, uint64_t old); 39 | // Computes the time difference between new and old. This will always 40 | // return a positive, non-zero value. 41 | // 42 | // uint64_t stm_since(uint64_t start); 43 | // Takes the current time, and returns the elapsed time since start 44 | // (this is a shortcut for "stm_diff(stm_now(), start)") 45 | // 46 | // uint64_t stm_laptime(uint64_t* last_time); 47 | // This is useful for measuring frame time and other recurring 48 | // events. It takes the current time, returns the time difference 49 | // to the value in last_time, and stores the current time in 50 | // last_time for the next call. If the value in last_time is 0, 51 | // the return value will be zero (this usually happens on the 52 | // very first call). 53 | // 54 | // uint64_t stm_round_to_common_refresh_rate(uint64_t duration) 55 | // This oddly named function takes a measured frame time and 56 | // returns the closest "nearby" common display refresh rate frame duration 57 | // in ticks. If the input duration isn't close to any common display 58 | // refresh rate, the input duration will be returned unchanged as a fallback. 59 | // The main purpose of this function is to remove jitter/inaccuracies from 60 | // measured frame times, and instead use the display refresh rate as 61 | // frame duration. 62 | // NOTE: for more robust frame timing, consider using the 63 | // sokol_app.h function sapp_frame_duration() 64 | // 65 | // Use the following functions to convert a duration in ticks into 66 | // useful time units: 67 | // 68 | // double stm_sec(uint64_t ticks); 69 | // double stm_ms(uint64_t ticks); 70 | // double stm_us(uint64_t ticks); 71 | // double stm_ns(uint64_t ticks); 72 | // Converts a tick value into seconds, milliseconds, microseconds 73 | // or nanoseconds. Note that not all platforms will have nanosecond 74 | // or even microsecond precision. 75 | // 76 | // Uses the following time measurement functions under the hood: 77 | // 78 | // Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() 79 | // MacOS/iOS: mach_absolute_time() 80 | // emscripten: emscripten_get_now() 81 | // Linux+others: clock_gettime(CLOCK_MONOTONIC) 82 | // 83 | // zlib/libpng license 84 | // 85 | // Copyright (c) 2018 Andre Weissflog 86 | // 87 | // This software is provided 'as-is', without any express or implied warranty. 88 | // In no event will the authors be held liable for any damages arising from the 89 | // use of this software. 90 | // 91 | // Permission is granted to anyone to use this software for any purpose, 92 | // including commercial applications, and to alter it and redistribute it 93 | // freely, subject to the following restrictions: 94 | // 95 | // 1. The origin of this software must not be misrepresented; you must not 96 | // claim that you wrote the original software. If you use this software in a 97 | // product, an acknowledgment in the product documentation would be 98 | // appreciated but is not required. 99 | // 100 | // 2. Altered source versions must be plainly marked as such, and must not 101 | // be misrepresented as being the original software. 102 | // 103 | // 3. This notice may not be removed or altered from any source 104 | // distribution. 105 | 106 | const builtin = @import("builtin"); 107 | 108 | // helper function to convert a C string to a Zig string slice 109 | fn cStrToZig(c_str: [*c]const u8) [:0]const u8 { 110 | return @import("std").mem.span(c_str); 111 | } 112 | extern fn stm_setup() void; 113 | 114 | pub fn setup() void { 115 | stm_setup(); 116 | } 117 | 118 | extern fn stm_now() u64; 119 | 120 | pub fn now() u64 { 121 | return stm_now(); 122 | } 123 | 124 | extern fn stm_diff(u64, u64) u64; 125 | 126 | pub fn diff(new_ticks: u64, old_ticks: u64) u64 { 127 | return stm_diff(new_ticks, old_ticks); 128 | } 129 | 130 | extern fn stm_since(u64) u64; 131 | 132 | pub fn since(start_ticks: u64) u64 { 133 | return stm_since(start_ticks); 134 | } 135 | 136 | extern fn stm_laptime([*c]u64) u64; 137 | 138 | pub fn laptime(last_time: *u64) u64 { 139 | return stm_laptime(last_time); 140 | } 141 | 142 | extern fn stm_round_to_common_refresh_rate(u64) u64; 143 | 144 | pub fn roundToCommonRefreshRate(frame_ticks: u64) u64 { 145 | return stm_round_to_common_refresh_rate(frame_ticks); 146 | } 147 | 148 | extern fn stm_sec(u64) f64; 149 | 150 | pub fn sec(ticks: u64) f64 { 151 | return stm_sec(ticks); 152 | } 153 | 154 | extern fn stm_ms(u64) f64; 155 | 156 | pub fn ms(ticks: u64) f64 { 157 | return stm_ms(ticks); 158 | } 159 | 160 | extern fn stm_us(u64) f64; 161 | 162 | pub fn us(ticks: u64) f64 { 163 | return stm_us(ticks); 164 | } 165 | 166 | extern fn stm_ns(u64) f64; 167 | 168 | pub fn ns(ticks: u64) f64 { 169 | return stm_ns(ticks); 170 | } 171 | 172 | -------------------------------------------------------------------------------- /src/sokol/web/shell.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sokol 7 | 29 | 30 | 31 | 32 | 50 | {{{ SCRIPT }}} 51 | 52 | 53 | -------------------------------------------------------------------------------- /tools/fixdoctar.zig: -------------------------------------------------------------------------------- 1 | //! repackage the autodocs sources.tar to only contain sokol sources 2 | //! (which reduces the size from 13 MBytes to abour 500 KBytes) 3 | const std = @import("std"); 4 | const log = std.log; 5 | const Allocator = std.mem.Allocator; 6 | 7 | pub fn main() !void { 8 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); 9 | defer arena_state.deinit(); 10 | const arena = arena_state.allocator(); 11 | 12 | // parse args 13 | const prefix = try arg(arena, "--prefix"); 14 | const input_dir = try arg(arena, "--input"); 15 | const output_path = try arg(arena, "--output"); 16 | log.info("fixdoctar called with:", .{}); 17 | log.info("--prefix: {s}", .{prefix}); 18 | log.info("--input_dir: {s}", .{input_dir}); 19 | log.info("--output_dir: {s}", .{output_path}); 20 | 21 | // iterate over sources.tar file, find relevant files and write to output tar file 22 | const inp_path = try std.fs.path.join(arena, &.{ input_dir, "sources.tar" }); 23 | const inp_file = std.fs.cwd().openFile(inp_path, .{}) catch |err| { 24 | fatal("failed to open input file '{s}' with {}", .{ inp_path, err }); 25 | }; 26 | defer inp_file.close(); 27 | const outp_file = std.fs.cwd().createFile(output_path, .{}) catch |err| { 28 | fatal("failed to open output file '{s}' with {}", .{ output_path, err }); 29 | }; 30 | defer outp_file.close(); 31 | 32 | var tar_writer = std.tar.writer(outp_file.writer()); 33 | var file_name_buffer: [1024]u8 = undefined; 34 | var link_name_buffer: [1024]u8 = undefined; 35 | var iter = std.tar.iterator(inp_file.reader(), .{ 36 | .file_name_buffer = &file_name_buffer, 37 | .link_name_buffer = &link_name_buffer, 38 | }); 39 | while (try iter.next()) |tar_item| { 40 | switch (tar_item.kind) { 41 | .file => { 42 | if (std.mem.startsWith(u8, tar_item.name, prefix)) { 43 | try tar_writer.writeFileStream(tar_item.name, tar_item.size, tar_item.reader(), .{ .mode = tar_item.mode }); 44 | } 45 | }, 46 | else => continue, 47 | } 48 | } 49 | log.info("Done.", .{}); 50 | return std.process.cleanExit(); 51 | } 52 | 53 | fn fatal(comptime fmt: []const u8, args: anytype) noreturn { 54 | std.log.err(fmt, args); 55 | std.process.exit(5); 56 | } 57 | 58 | fn arg(allocator: Allocator, key: []const u8) ![]const u8 { 59 | var arg_iter = try std.process.argsWithAllocator(allocator); 60 | defer arg_iter.deinit(); 61 | while (arg_iter.next()) |cur| { 62 | if (std.mem.eql(u8, key, cur)) { 63 | const val = arg_iter.next() orelse fatal("expected arg after {s}", .{key}); 64 | return allocator.dupe(u8, val); 65 | } 66 | } 67 | fatal("expected arg {s}", .{key}); 68 | } 69 | --------------------------------------------------------------------------------