summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralaric <alaric@netmythos.org>2024-03-31 20:19:27 -0700
committeralaric <alaric@netmythos.org>2024-03-31 20:19:27 -0700
commitc43b1f972cd91b157f7d0997c5bdd9d94590d93d (patch)
tree819ceae67a4d7a88c16d798384cd7ce25cb5eac7
downloadmenger-c43b1f972cd91b157f7d0997c5bdd9d94590d93d.tar.gz
menger-c43b1f972cd91b157f7d0997c5bdd9d94590d93d.zip
Initial Commit
-rw-r--r--.gitignore1
-rw-r--r--build.zig37
-rw-r--r--build.zig.zon62
-rw-r--r--flake.lock146
-rw-r--r--flake.nix48
-rw-r--r--src/shell.html134
-rw-r--r--src/wasm_msponge.zig10
-rw-r--r--src/webgl.js273
-rw-r--r--src/webgl.zig155
9 files changed, 866 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4c80a22
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+zig-*
diff --git a/build.zig b/build.zig
new file mode 100644
index 0000000..d7173b3
--- /dev/null
+++ b/build.zig
@@ -0,0 +1,37 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+ const target = b.resolveTargetQuery(.{
+ .cpu_arch = .wasm32,
+ .os_tag = .freestanding,
+ });
+ const optimize = b.standardOptimizeOption(.{});
+
+ b.installFile("src/shell.html", "index.html");
+ b.installFile("src/webgl.js", "webgl.js");
+
+ const exe = b.addExecutable(.{
+ .name = "msponge",
+ .root_source_file = .{ .path = "src/wasm_msponge.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+ exe.entry = .disabled;
+ exe.root_module.export_symbol_names = &[_][]const u8{
+ "init",
+ "update",
+ };
+
+ b.installArtifact(exe);
+
+ const exe_unit_tests = b.addTest(.{
+ .root_source_file = .{ .path = "src/wasm_msponge.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
+
+ const test_step = b.step("test", "Run unit tests");
+ test_step.dependOn(&run_exe_unit_tests.step);
+}
diff --git a/build.zig.zon b/build.zig.zon
new file mode 100644
index 0000000..d77ca61
--- /dev/null
+++ b/build.zig.zon
@@ -0,0 +1,62 @@
+.{
+ .name = "menger-sponge",
+ // This is a [Semantic Version](https://semver.org/).
+ // In a future version of Zig it will be used for package deduplication.
+ .version = "0.0.0",
+
+ // This field is optional.
+ // This is currently advisory only; Zig does not yet do anything
+ // with this value.
+ //.minimum_zig_version = "0.11.0",
+
+ // This field is optional.
+ // Each dependency must either provide a `url` and `hash`, or a `path`.
+ // `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
+ // Once all dependencies are fetched, `zig build` no longer requires
+ // internet connectivity.
+ .dependencies = .{
+ // See `zig fetch --save <url>` for a command-line interface for adding dependencies.
+ //.example = .{
+ // // When updating this field to a new URL, be sure to delete the corresponding
+ // // `hash`, otherwise you are communicating that you expect to find the old hash at
+ // // the new URL.
+ // .url = "https://example.com/foo.tar.gz",
+ //
+ // // This is computed from the file contents of the directory of files that is
+ // // obtained after fetching `url` and applying the inclusion rules given by
+ // // `paths`.
+ // //
+ // // This field is the source of truth; packages do not come from a `url`; they
+ // // come from a `hash`. `url` is just one of many possible mirrors for how to
+ // // obtain a package matching this `hash`.
+ // //
+ // // Uses the [multihash](https://multiformats.io/multihash/) format.
+ // .hash = "...",
+ //
+ // // When this is provided, the package is found in a directory relative to the
+ // // build root. In this case the package's hash is irrelevant and therefore not
+ // // computed. This field and `url` are mutually exclusive.
+ // .path = "foo",
+ //},
+ },
+
+ // Specifies the set of files and directories that are included in this package.
+ // Only files and directories listed here are included in the `hash` that
+ // is computed for this package.
+ // Paths are relative to the build root. Use the empty string (`""`) to refer to
+ // the build root itself.
+ // A directory listed here means that all files within, recursively, are included.
+ .paths = .{
+ // This makes *all* files, recursively, included in this package. It is generally
+ // better to explicitly list the files and directories instead, to insure that
+ // fetching from tarballs, file system paths, and version control all result
+ // in the same contents hash.
+ "",
+ // For example...
+ //"build.zig",
+ //"build.zig.zon",
+ //"src",
+ //"LICENSE",
+ //"README.md",
+ },
+}
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..1567007
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,146 @@
+{
+ "nodes": {
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1696426674,
+ "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-compat_2": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1673956053,
+ "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1710146030,
+ "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake-utils_2": {
+ "locked": {
+ "lastModified": 1659877975,
+ "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1704290814,
+ "narHash": "sha256-LWvKHp7kGxk/GEtlrGYV68qIvPHkU9iToomNFGagixU=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-23.05",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1702350026,
+ "narHash": "sha256-A+GNZFZdfl4JdDphYKBJ5Ef1HOiFsP18vQe9mqjmUis=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "9463103069725474698139ab10f17a9d125da859",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-23.05",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs",
+ "zig": "zig"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ },
+ "zig": {
+ "inputs": {
+ "flake-compat": "flake-compat_2",
+ "flake-utils": "flake-utils_2",
+ "nixpkgs": "nixpkgs_2"
+ },
+ "locked": {
+ "lastModified": 1711800454,
+ "narHash": "sha256-uanB/jYcVbIq9+K7n8Iom1+gADAW8T+AjK2x7XdtxOY=",
+ "owner": "mitchellh",
+ "repo": "zig-overlay",
+ "rev": "7bd8979683da11946fe29f7654567356ac3b1989",
+ "type": "github"
+ },
+ "original": {
+ "owner": "mitchellh",
+ "repo": "zig-overlay",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..07d50eb
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,48 @@
+{
+ description = "The flake for Menger Sponge";
+
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
+ flake-utils.url = "github:numtide/flake-utils";
+ zig.url = "github:mitchellh/zig-overlay";
+
+ # Used for shell.nix
+ flake-compat = {
+ url = "github:edolstra/flake-compat";
+ flake = false;
+ };
+ };
+
+ outputs = {
+ self,
+ nixpkgs,
+ flake-utils,
+ ...
+ } @ inputs: let
+ overlays = [
+ # Other overlays
+ (final: prev: {
+ zigpkgs = inputs.zig.packages.${prev.system};
+ })
+ ];
+
+ # Our supported systems are the same supported systems as the Zig binaries
+ systems = builtins.attrNames inputs.zig.packages;
+ in
+ flake-utils.lib.eachSystem systems (
+ system: let
+ pkgs = import nixpkgs {inherit overlays system;};
+ in {
+ devShells.default = pkgs.mkShell {
+ nativeBuildInputs = with pkgs; [
+ zigpkgs.master
+ python3
+ ];
+ };
+
+ # For compatibility with older versions of the `nix` binary
+ devShell = self.devShells.${system}.default;
+ }
+ );
+}
+
diff --git a/src/shell.html b/src/shell.html
new file mode 100644
index 0000000..cc1e002
--- /dev/null
+++ b/src/shell.html
@@ -0,0 +1,134 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Menger Sponge</title>
+ <style>
+ body {
+ overflow: hidden;
+ min-width: 1280px;
+ min-height: 720px;
+ display: flex;
+ flex-direction: column;
+ margin: 0;
+ }
+ #app-canvas {
+ display: flex;
+ flex-grow: 1;
+ width: 100%;
+ height: 100%;
+ }
+ </style>
+ </head>
+ <body>
+ <canvas id="app-canvas" tabindex="0"></canvas>
+ <script src="webgl.js"></script>
+ <script>
+ canvas.width = canvas.clientWidth;
+ canvas.height = canvas.clientHeight;
+ let memory = null;
+ let last_frame_time = Date.now();
+
+ const writeString = (str, pointer, length) => {
+ if (!memory) {
+ return null;
+ }
+
+ const l = Math.min(length, str.length);
+ const from = new TextEncoder().encode(str.slice(0, l));
+ const to = new Uint8Array(memory.buffer, pointer, length);
+ to.set(from);
+ return l;
+ };
+
+ const readString = (pointer, length) => {
+ if (!memory) {
+ return null;
+ }
+
+ // Memory in WASM is one big buffer. We can read a string from the
+ // Zig/WASM space if we know the pointer and length.
+ return new TextDecoder().decode(
+ new Uint8Array(memory.buffer, pointer, length),
+ );
+ };
+
+
+ const consoleLog = (ptr, len) => {
+ console.log(readString(ptr, len));
+ }
+
+ const rand = () => {
+ return Math.random();
+ };
+
+ const loadTexture = (ptr, len, tex_id, ret_ptr) => {
+ const path = readString(ptr, len);
+ var image = new Image();
+ image.src = path;
+ console.log("loading ", path);
+ image.addEventListener('load', function() {
+ const ret_list = new Uint32Array(memory.buffer, ret_ptr, 3);
+ ret_list[0] = 1;
+ ret_list[1] = image.width;
+ ret_list[2] = image.height;
+ console.log(path, " loaded");
+ const tex = glTextures.get(tex_id);
+ gfx_ctx.bindTexture(gfx_ctx.TEXTURE_2D, tex);
+ gfx_ctx.texImage2D(gfx_ctx.TEXTURE_2D, 0, gfx_ctx.RGBA, gfx_ctx.RGBA, gfx_ctx.UNSIGNED_BYTE, image);
+ gfx_ctx.generateMipmap(gfx_ctx.TEXTURE_2D);
+ });
+ };
+
+ const keyRegistry = new Map();
+ canvas.addEventListener("keydown", (e) => {
+ const p = keyRegistry.get(e.code);
+ if (p) {
+ p[0] = 1;
+ }
+ });
+
+ canvas.addEventListener("keyup", (e) => {
+ const p = keyRegistry.get(e.code);
+ if (p) {
+ p[0] = 0;
+ }
+ });
+
+ const registerKeyInput = (ptr, len, out_ptr) => {
+ const code = readString(ptr, len);
+ const list = new Uint32Array(memory.buffer, out_ptr, 1);
+ keyRegistry.set(code, list);
+ };
+
+ const importObject = {
+ env: {
+ ...webgl,
+ consoleLog,
+ rand,
+ loadTexture,
+ registerKeyInput,
+ },
+ };
+
+ WebAssembly.instantiateStreaming(fetch("bin/msponge.wasm"), importObject).then(
+ (obj) => {
+ memory = obj.instance.exports.memory;
+ obj.instance.exports.init();
+
+ const update = obj.instance.exports.update;
+ function step() {
+ const time = Date.now();
+ const elapsed = time - last_frame_time;
+ update(elapsed / 1000);
+ last_frame_time = time;
+ window.requestAnimationFrame(step);
+ }
+
+ window.requestAnimationFrame(step);
+ }
+ );
+ </script>
+ </body>
+</html>
diff --git a/src/wasm_msponge.zig b/src/wasm_msponge.zig
new file mode 100644
index 0000000..376e011
--- /dev/null
+++ b/src/wasm_msponge.zig
@@ -0,0 +1,10 @@
+extern fn consoleLog(ptr: [*c]const u8, len: u32) void;
+
+export fn init() void {
+ const message = "Hello Menger Sponge!";
+ consoleLog(message.ptr, message.len);
+}
+
+export fn update(delta_time: f32) void {
+ _ = delta_time;
+}
diff --git a/src/webgl.js b/src/webgl.js
new file mode 100644
index 0000000..7c60142
--- /dev/null
+++ b/src/webgl.js
@@ -0,0 +1,273 @@
+const canvas = document.getElementById("app-canvas");
+const gfx_ctx = canvas.getContext("webgl2");
+if (gfx_ctx === null) {
+ alert("There was a problem initializing WebGL. Your browser or machine may not support it.");
+}
+
+gfx_ctx.enable(gfx_ctx.BLEND);
+gfx_ctx.enable(gfx_ctx.DEPTH_TEST);
+gfx_ctx.blendFunc(gfx_ctx.SRC_ALPHA, gfx_ctx.ONE_MINUS_SRC_ALPHA);
+
+let next_id = 0;
+const getId = () => {
+ next_id += 1;
+ return next_id - 1;
+};
+
+const glBuffers = new Map();
+const glShaders = new Map();
+const glPrograms = new Map();
+const glVertexArrays = new Map();
+const glUniformLocations = new Map();
+const glTextures = new Map();
+
+const attachShader = (program_id, shader_id) => {
+ const program = glPrograms.get(program_id);
+ const shader = glShaders.get(shader_id);
+ gfx_ctx.attachShader(program, shader);
+};
+
+const blendFunc = (sfactor, dfactor) => {
+ gfx_ctx.blendFunc(sfactor, dfactor);
+};
+
+const bindBuffer = (target, id) => {
+ gfx_ctx.bindBuffer(target, glBuffers.get(id));
+};
+
+const bufferData = (target, ptr, len, usage) => {
+ const floats = new Float32Array(memory.buffer, ptr, len);
+ if (len === 9) {
+ console.log(floats);
+ }
+ gfx_ctx.bufferData(target, floats, usage);
+};
+
+const bindVertexArray = (vao) => {
+ gfx_ctx.bindVertexArray(glVertexArrays.get(vao));
+};
+
+const createVertexArray = () => {
+ const id = getId();
+ glVertexArrays.set(id, gfx_ctx.createVertexArray());
+ return id;
+};
+
+const clearColor = (r, g, b, a) => {
+ gfx_ctx.clearColor(r, g, b, a);
+};
+
+const clear = (mask) => {
+ gfx_ctx.clear(mask);
+};
+
+const createBuffer = () => {
+ const id = getId();
+ glBuffers.set(id, gfx_ctx.createBuffer());
+ return id;
+};
+
+const createShader = (type) => {
+ const id = getId();
+ glShaders.set(id, gfx_ctx.createShader(type));
+ return id;
+};
+
+const compileShader = (shader) => {
+ gfx_ctx.compileShader(glShaders.get(shader));
+};
+
+const createProgram = (type) => {
+ const id = getId();
+ glPrograms.set(id, gfx_ctx.createProgram());
+ return id;
+};
+
+const deleteShader = (shader) => {
+ gfx_ctx.deleteShader(glShaders.get(shader));
+};
+
+const deleteProgram = (program) => {
+ gfx_ctx.deleteProgram(glPrograms.get(program));
+};
+
+const enable = (cap) => {
+ gfx_ctx.enable(cap);
+};
+
+
+const enableVertexAttribArray = (attrib_location) => {
+ gfx_ctx.enableVertexAttribArray(attrib_location);
+};
+
+const getShaderParameter = (shader, info) => {
+ return gfx_ctx.getShaderParameter(glShaders.get(shader), info);
+};
+
+const getShaderInfoLog = (shader, ptr, len) => {
+ const msg = gfx_ctx.getShaderInfoLog(glShaders.get(shader));
+ const r = writeString(msg, ptr, len);
+ return r;
+};
+
+const getProgramParameter = (program_id, info) => {
+ const program = glPrograms.get(program_id);
+ return gfx_ctx.getProgramParameter(program, info);
+};
+
+const getProgramInfoLog = (program, ptr, len) => {
+ const msg = gfx_ctx.getProgramInfoLog(glPrograms.get(program));
+ const r = writeString(msg, ptr, len);
+ return r;
+};
+
+const getAttribLocation = (program, ptr, len) => {
+ const attrib = readString(ptr, len);
+ return gfx_ctx.getAttribLocation(glPrograms.get(program), attrib);
+};
+
+const getUniformLocation = (program, ptr, len) => {
+ const uniform = readString(ptr, len);
+ const loc = gfx_ctx.getUniformLocation(glPrograms.get(program), uniform);
+ if (!loc) console.log("Uniform ", uniform, " could not be found");
+ if (!loc) return -1;
+
+ const id = getId();
+ glUniformLocations.set(id, loc);
+ return id;
+};
+
+const linkProgram = (program) => {
+ gfx_ctx.linkProgram(glPrograms.get(program));
+};
+
+const shaderSource = (shader, ptr, len) => {
+ const source = readString(ptr, len);
+ console.log(source);
+ gfx_ctx.shaderSource(glShaders.get(shader), source);
+};
+
+const useProgram = (prog) => {
+ gfx_ctx.useProgram(glPrograms.get(prog));
+};
+
+const uniform1i = (location, v0) => {
+ gfx_ctx.uniform1i(glUniformLocations.get(location), v0);
+};
+
+const uniform2f = (location, v0, v1) => {
+ gfx_ctx.uniform2f(glUniformLocations.get(location), v0, v1);
+};
+
+const uniform4f = (location, v0, v1, v2, v3) => {
+ gfx_ctx.uniform4f(glUniformLocations.get(location), v0, v1, v2, v3);
+};
+
+const uniformMatrix3fv = (location, ptr) => {
+ const u = glUniformLocations.get(location);
+ const floats = new Float32Array(memory.buffer, ptr, 9);
+
+ gfx_ctx.uniformMatrix3fv(glUniformLocations.get(location), false, floats);
+};
+
+const vertexAttribPointer = (loc, size, type, normalize, stride, offset) => {
+ gfx_ctx.vertexAttribPointer(loc, size, type, normalize, stride, offset);
+};
+
+const viewport = (x, y, width, height) => {
+ gfx_ctx.viewport(x, y, width, height);
+};
+
+const drawArrays = (mode, first, count) => {
+ gfx_ctx.drawArrays(mode, first, count);
+};
+
+const createTexture = () => {
+ const id = getId();
+ glTextures.set(id, gfx_ctx.createTexture());
+ return id;
+};
+
+const bindTexture = (target, tex_id) => {
+ const texture = glTextures.get(tex_id);
+ gfx_ctx.bindTexture(target, texture);
+};
+
+const texImage2D = (target, level, interal_format,
+ width, height, border, format,
+ data_type, ptr, count, offset) => {
+ const data = new Uint8Array(memory.buffer, ptr, count);
+ gfx_ctx.texImage2D(target, level, interal_format, width, height, border, format, data_type, data, offset);
+};
+
+const activeTexture = (tex_id) => {
+ gfx_ctx.activeTexture(tex_id);
+};
+
+const vertexAttribDivisor = (loc, divisor) => {
+ gfx_ctx.vertexAttribDivisor(loc, divisor);
+};
+
+const drawArraysInstanced = (mode, first, count, instanceCount) => {
+ gfx_ctx.drawArraysInstanced(mode, first, count, instanceCount);
+};
+
+const getScreenWidth = () => {
+ return gfx_ctx.drawingBufferWidth;
+};
+
+const getScreenHeight = () => {
+ return gfx_ctx.drawingBufferHeight;
+};
+
+const webgl = {
+ bindBuffer,
+ blendFunc,
+ bufferData,
+
+ createShader,
+ clear,
+ clearColor,
+ createBuffer,
+
+ enable,
+
+ shaderSource,
+ compileShader,
+ getShaderParameter,
+ deleteShader,
+ getShaderInfoLog,
+
+ createProgram,
+ deleteProgram,
+ attachShader,
+ linkProgram,
+ getProgramParameter,
+ getProgramInfoLog,
+ useProgram,
+
+ getAttribLocation,
+ getUniformLocation,
+ createVertexArray,
+ bindVertexArray,
+ enableVertexAttribArray,
+ vertexAttribPointer,
+ viewport,
+ drawArrays,
+
+ uniform1i,
+ uniform2f,
+ uniform4f,
+ uniformMatrix3fv,
+
+ createTexture,
+ bindTexture,
+ texImage2D,
+ activeTexture,
+
+ vertexAttribDivisor,
+ drawArraysInstanced,
+
+ getScreenWidth,
+ getScreenHeight,
+};
diff --git a/src/webgl.zig b/src/webgl.zig
new file mode 100644
index 0000000..b97c8fa
--- /dev/null
+++ b/src/webgl.zig
@@ -0,0 +1,155 @@
+pub const Buffer = extern struct { handle: u32 };
+pub const Shader = packed struct(u32) { handle: u32 };
+pub const Program = packed struct(u32) { handle: u32 };
+pub const Texture = packed struct(u32) { handle: u32 };
+
+pub const GLProgramInfo = enum(u32) {
+ delete_status = 0x8B80,
+ link_status = 0x8B82,
+};
+
+pub const GLShaderType = enum(u32) {
+ fragment_shader = 0x8B30,
+ vertex_shader = 0x8B31,
+};
+
+pub const GLShaderInfo = enum(u32) {
+ shader_type = 0x8B4F,
+ delete_status = 0x8B80,
+ compile_status = 0x8B81,
+};
+
+pub const GLBindingPoint = enum(u32) {
+ array_buffer = 0x8892,
+ element_array_buffer = 0x8893,
+};
+
+pub const GLUsagePattern = enum(u32) {
+ stream_draw = 0x88E0,
+ static_draw = 0x88E4,
+ dynamic_draw = 0x88E8,
+};
+
+pub const color_buffer_bit = 0x00004000;
+pub const depth_buffer_bit = 0x00000100;
+
+pub extern fn clearColor(r: f32, g: f32, b: f32, a: f32) void;
+pub extern fn clear(mask: u32) void;
+
+pub extern fn enable(cap: u32) void;
+pub extern fn blendFunc(sfactor: u32, dfactor: u32) void;
+
+pub extern fn createBuffer() Buffer;
+pub extern fn bindBuffer(target: GLBindingPoint, buffer: Buffer) void;
+pub extern fn bufferData(
+ target: GLBindingPoint,
+ ptr: [*c]const f32,
+ len: u32,
+ usage: GLUsagePattern,
+) void;
+
+pub extern fn createShader(shader_type: GLShaderType) Shader;
+pub extern fn shaderSource(shader: Shader, source_ptr: [*c]const u8, len: u32) void;
+pub extern fn compileShader(shader: Shader) void;
+pub extern fn getShaderParameter(shader: Shader, info: GLShaderInfo) u32;
+pub extern fn deleteShader(shader: Shader) void;
+pub extern fn getShaderInfoLog(shader: Shader, buf: [*c]u8, len: u32) u32;
+
+pub extern fn createProgram() Program;
+pub extern fn deleteProgram(program: Program) void;
+pub extern fn attachShader(program: Program, shader: Shader) void;
+pub extern fn linkProgram(program: Program) void;
+pub extern fn getProgramParameter(program: Program, info: GLProgramInfo) u32;
+pub extern fn getProgramInfoLog(program: Program, buf: [*c]const u8, len: u32) u32;
+pub extern fn useProgram(program: Program) void;
+
+pub extern fn getAttribLocation(program: Program, buf: [*c]const u8, len: u32) i32;
+pub extern fn getUniformLocation(program: Program, buf: [*c]const u8, len: u32) i32;
+
+pub extern fn uniform1i(location: i32, v0: i32) void;
+pub extern fn uniform2f(location: i32, v0: f32, v1: f32) void;
+pub extern fn uniform4f(location: i32, v0: f32, v1: f32, v2: f32, v3: f32) void;
+pub extern fn uniformMatrix3fv(location: i32, ptr: [*c]const @Vector(3, f32)) void;
+
+pub const VertexArrayObject = packed struct(u32) { handle: u32 };
+const GLType = enum(u32) {
+ i8 = 0x1400,
+ u8 = 0x1401,
+ i16 = 0x1402,
+ u16 = 0x1403,
+ i32 = 0x1404,
+ u32 = 0x1405,
+ f32 = 0x1406,
+};
+
+const GLBool = enum(i32) {
+ false = 0,
+ true = 1,
+};
+
+pub extern fn createVertexArray() VertexArrayObject;
+pub extern fn bindVertexArray(vao: VertexArrayObject) void;
+pub extern fn enableVertexAttribArray(attrib_location: i32) void;
+pub extern fn vertexAttribPointer(
+ attrib_location: u32,
+ size: i32,
+ gl_type: GLType,
+ normalize: GLBool,
+ stride: i32,
+ offset: i32,
+) void;
+
+pub extern fn viewport(x: i32, y: i32, width: i32, height: i32) void;
+
+const GLDrawMode = enum(u32) {
+ points = 0x0000,
+ lines = 0x0001,
+ line_loop = 0x0002,
+ line_strip = 0x0003,
+ triangles = 0x0004,
+ triangle_strip = 0x00005,
+ triangle_fan = 0x00006,
+};
+
+pub extern fn drawArrays(mode: GLDrawMode, first: i32, count: i32) void;
+
+pub extern fn createTexture() Texture;
+
+const TextureBindTarget = enum(u32) {
+ texture_2d = 0x0DE1,
+ texture_cube_map = 0x8514,
+ texture_3d = 0x806F,
+ texture_2d_array = 0x8C1A,
+};
+pub extern fn bindTexture(target: TextureBindTarget, texture: Texture) void;
+
+pub const texture_0 = 0x84c0;
+pub extern fn activeTexture(idx: u32) void;
+
+const TextureTarget = enum(u32) {
+ texture_2d = 0x0DE1,
+};
+const TextureFormat = enum(u32) {
+ rgba = 0x1908,
+};
+pub extern fn texImage2D(
+ target: TextureTarget,
+ level: i32,
+ internal_format: TextureFormat,
+ width: i32,
+ height: i32,
+ border: i32,
+ format: TextureFormat,
+ data_type: GLType,
+ ptr: *const anyopaque,
+ count: u32,
+ offset: u32,
+) void;
+
+pub extern fn vertexAttribDivisor(loc: i32, divisor: i32) void;
+pub extern fn drawArraysInstanced(
+ mode: GLDrawMode,
+ first: i32,
+ count: i32,
+ instace_count: i32,
+) void;