diff options
author | alaric <alaric@netmythos.org> | 2024-04-14 23:47:59 -0700 |
---|---|---|
committer | alaric <alaric@netmythos.org> | 2024-04-14 23:47:59 -0700 |
commit | bdea9f29202ca4131d2adde62c65e505b5758d6c (patch) | |
tree | 76cc9f757697117d10e3301278541729e9cb9f02 | |
parent | e0f0c28d54e3931b7ec26474260f83269a1932c2 (diff) | |
download | colordots-master.tar.gz colordots-master.zip |
-rw-r--r-- | build.zig | 46 | ||||
-rw-r--r-- | build.zig.zon | 34 | ||||
-rw-r--r-- | src/main.zig | 420 | ||||
-rw-r--r-- | src/matrix.zig | 80 | ||||
-rw-r--r-- | src/shell.html | 134 | ||||
-rw-r--r-- | src/wasm_cdots.zig | 537 | ||||
-rw-r--r-- | src/webgl.js | 261 | ||||
-rw-r--r-- | src/webgl.zig | 152 |
8 files changed, 451 insertions, 1213 deletions
@@ -1,37 +1,45 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const target = b.resolveTargetQuery(.{ + // The engine only supports wasm right now so if you set a whitelist here, + // you won't have to remember to specify your target each time you compile. + const wasm_target: std.Target.Query = .{ .cpu_arch = .wasm32, .os_tag = .freestanding, + }; + const target = b.standardTargetOptions(.{ + .whitelist = &.{wasm_target}, + .default_target = wasm_target, }); const optimize = b.standardOptimizeOption(.{}); - b.installFile("src/shell.html", "index.html"); - b.installFile("src/webgl.js", "webgl.js"); + // Import the dependency via your build.zig.zon + const engine = b.dependency("engine", .{}); const exe = b.addExecutable(.{ - .name = "cdots", - .root_source_file = .{ .path = "src/wasm_cdots.zig" }, + // IMPORTANT! Right now the shell expects a file named game.wasm in the bin directory. + // I will make it flexible eventually. But right now if you change this line, + // Your project won't work right. + .name = "game", + .root_source_file = .{ .path = "src/main.zig" }, .target = target, .optimize = optimize, }); + // WASM doesn't use a normal entry point. The HTML/JS shell code is going + // to handle calling our functions. exe.entry = .disabled; - exe.root_module.export_symbol_names = &[_][]const u8{ - "init", - "update", - }; - - b.installArtifact(exe); + exe.root_module.addImport("engine", engine.module("engine")); - const exe_unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/wasm_cdots.zig" }, - .target = target, - .optimize = optimize, + // Here we install a pre-written HTML/JS shell. After building, if you + // run a webserver with your zig-out dir as the root, you should see your + // program. + const shell_files = engine.namedWriteFiles("shell"); + const install_shell = b.addInstallDirectory(.{ + .source_dir = .{ .generated = &shell_files.generated_directory }, + .install_dir = .prefix, + .install_subdir = "", }); + b.getInstallStep().dependOn(&install_shell.step); - 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); + b.installArtifact(exe); } diff --git a/build.zig.zon b/build.zig.zon index 5b06257..2196254 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -15,37 +15,11 @@ // 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", - //}, + .engine = .{ + .url = "https://git.netmythos.org/engine.git/snapshot/engine-89ed3b75abde2bc08eb1638d5c0becc8606e6a95.tar.gz", + .hash = "1220ee36744e9bf24ac28d47d5cd461a7b32f2d7640ca49e665db834fc45f8c72910", + }, }, - - // 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 diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..5975572 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,420 @@ +const std = @import("std"); +const engine = @import("engine"); +comptime { + _ = engine; +} +const webgl = engine.webgl; +const random = engine.random; + +const Mat3 = engine.matrix.Matrix(f32, 3, 3); + +//extern fn consoleLog(ptr: [*c]const u8, len: u32) void; +//extern fn rand() f32; +//extern fn getScreenWidth() f32; +//extern fn getScreenHeight() f32; +//extern fn registerKeyInput(ptr: [*c]const u8, len: u32, out: *u32) void; + +const vs_source = @embedFile("vertex.glsl"); +const fs_source = @embedFile("fragment.glsl"); + +const pi = 3.1415926535; +const max_dist = 100; +const star_count = 100000; +const circle = circlePoints(32); +const max_speed = 2; + +var rng: LinearCongruentialGenerator = undefined; +var stars: [star_count]Star = undefined; + +var up_pressed: bool = false; +var down_pressed: bool = false; +var right_pressed: bool = false; +var left_pressed: bool = false; + +var circle_program: CircleProgram = undefined; +var circle_vao: webgl.VertexArrayObject = undefined; +var pos_buffer: webgl.Buffer = undefined; +var translation_buffer: webgl.Buffer = undefined; +var color_buffer: webgl.Buffer = undefined; +var scale_buffer: webgl.Buffer = undefined; + +var line_program: LineProgram = undefined; +var line_vao: webgl.VertexArrayObject = undefined; +var line_pos_buf: webgl.Buffer = undefined; +var line_col_buf: webgl.Buffer = undefined; + +var frame_counter: u32 = 0; + +const batch_capacity = 90000; +var translation_data: [batch_capacity][2]f32 = undefined; +var line_data: [batch_capacity][2][2]f32 = undefined; +var scale_data: [batch_capacity]f32 = undefined; +var color_data: [batch_capacity]f32 = undefined; + +var timer: f32 = 0.0; +var anim_t: f32 = 0; + +var speed: f32 = 1; +var sub_count: u32 = star_count / 2; +var color_mode: enum(u8) { rainbow, solid, angle, depth } = .rainbow; + +const DrawBatch = struct { + translation: [][2]f32, + scales: []f32, + colors: []f32, + lines: [][2][2]f32, + len: u32 = 0, + + fn addInstance(self: *DrawBatch, translation: [2]f32, past_translation: [2]f32, hue: f32, scale: f32) !void { + if (self.len == self.translation.len or self.len == self.colors.len or self.len == self.scales.len or self.len == self.lines.len) return error.OutOfSpace; + + self.translation[self.len] = translation; + self.colors[self.len] = hue; + self.scales[self.len] = scale; + self.lines[self.len] = .{ past_translation, translation }; + self.len += 1; + } + + fn draw(self: DrawBatch) void { + const dims = webgl.getScreenSize(); + const width = dims[0]; + const height = dims[1]; + circle_vao.bind(); + circle_program.use(); + circle_program.setUniform(.resolution, .{ width, height }); + translation_buffer.bindAndFill(.array_buffer, .vec2, self.translation, .dynamic_draw); + scale_buffer.bindAndFill(.array_buffer, .f32, self.scales, .dynamic_draw); + color_buffer.bindAndFill(.array_buffer, .f32, self.colors, .dynamic_draw); + + webgl.drawArraysInstanced(.triangles, 0, circle.len, @intCast(self.len)); + + line_vao.bind(); + line_program.use(); + line_program.setUniform(.resolution, .{ width, height }); + + const l_mp: [*]const f32 = @ptrCast(self.lines.ptr); + const l_pos: []const f32 = l_mp[0 .. self.lines.len * 4]; + line_pos_buf.bindAndFill(.array_buffer, .f32, l_pos, .dynamic_draw); + + line_col_buf.bindAndFill(.array_buffer, .f32, self.colors, .dynamic_draw); + + webgl.drawArraysInstanced(.lines, 0, 2, @intCast(self.len)); + } +}; + +const KeyboardKey = enum { + up, + down, + left, + right, + c, +}; + +const LineProgram = webgl.Program( + &.{ + .{ .identifier = "resolution", .shader_name = "u_resolution", .kind = .vec2 }, + }, + &.{ + .{ .identifier = "pos", .shader_name = "in_pos", .kind = .vec4, .instance_divisor = 1 }, + .{ .identifier = "color", .shader_name = "color", .kind = .f32, .instance_divisor = 1 }, + }, +); + +const CircleProgram = webgl.Program( + &.{ + .{ .identifier = "resolution", .shader_name = "u_resolution", .kind = .vec2 }, + }, + &.{ + .{ .identifier = "pos", .shader_name = "in_pos", .kind = .vec2 }, + .{ .identifier = "color", .shader_name = "color", .kind = .f32, .instance_divisor = 1 }, + .{ .identifier = "scale", .shader_name = "scale", .kind = .f32, .instance_divisor = 1 }, + .{ .identifier = "translation", .shader_name = "translation", .kind = .vec2, .instance_divisor = 1 }, + }, +); + +const Star = struct { + dist: f32, + pdist: f32, + angle: f32, + initial_len: f32, + hue: f32, +}; + +pub const LinearCongruentialGenerator = struct { + mod: u64, + mul: u64, + inc: u64, + seed: u64, + + const Self = @This(); + /// Move to the next seed value internally and return that value + pub fn next(self: *Self) u64 { + const r = (self.seed *% self.mul +% self.inc) % self.mod; + self.seed = r; + return r; + } + + /// Generate an int of type T with a value from min to max (inclusive) + pub fn randInt(self: *Self, comptime T: type, min: T, max: T) T { + assert(max > min); + const range: u64 = @as(u64, @intCast(max - min)) + 1; + assert(self.mod >= range); + const val: T = @intCast(self.next() % range); + return min + val; + } + + pub fn randFloat(self: *Self, comptime T: type) T { + const pct: T = @as(T, @floatFromInt(self.next())) / @as(T, @floatFromInt(self.mod)); + return pct; + } + + pub fn randEnum(self: *Self, comptime T: type) T { + const info = @typeInfo(T); + if (info != .Enum) @compileError("Cannot call randEnum on type " ++ @typeName(T)); + const fields = info.Enum.fields; + const vals = comptime blk: { + var result: [fields.len]T = undefined; + for (fields, 0..) |f, i| { + result[i] = @field(T, f.name); + } + break :blk result; + }; + const i = self.randInt(usize, 0, fields.len - 1); + return vals[i]; + } + + pub fn randBool(self: *Self) bool { + return self.next() % 2 == 0; + } + + pub fn ZX81(seed: u64) Self { + const mod = pow(u64, 2, 16) + 1; + return .{ + .seed = seed, + .mod = mod, + .mul = 75, + .inc = 74, + }; + } +}; + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +pub fn pow(comptime T: type, base: T, exp: T) T { + if (exp == 0) { + return 1; + } else if (exp > 0) { + return base * pow(T, base, exp - 1); + } else if (exp < 0) { + return pow(T, base, exp + 1) / base; + } + unreachable; +} + +pub fn init() void { + const seed: u64 = random.randomSeed(); + rng = LinearCongruentialGenerator.ZX81(seed); + + circle_program = CircleProgram.init(@embedFile("vertex.glsl"), fs_source) catch |err| { + engine.print("Failed to create CircleProgram. {}", .{err}); + return; + }; + circle_vao = webgl.VertexArrayObject.init(); + pos_buffer = webgl.Buffer.init(); + translation_buffer = webgl.Buffer.init(); + color_buffer = webgl.Buffer.init(); + scale_buffer = webgl.Buffer.init(); + circle_program.setVertexAttribPointer(.pos, circle_vao, pos_buffer); + pos_buffer.bindAndFill(.array_buffer, .vec2, &circle, .static_draw); + circle_program.setVertexAttribPointer(.scale, circle_vao, scale_buffer); + circle_program.setVertexAttribPointer(.color, circle_vao, color_buffer); + circle_program.setVertexAttribPointer(.translation, circle_vao, translation_buffer); + + line_program = LineProgram.init(@embedFile("line_vertex.glsl"), fs_source) catch |err| { + engine.print("Failed to create CircleProgram. {}", .{err}); + return; + }; + line_vao = webgl.VertexArrayObject.init(); + line_pos_buf = webgl.Buffer.init(); + line_col_buf = webgl.Buffer.init(); + line_program.setVertexAttribPointer(.pos, line_vao, line_pos_buf); + line_program.setVertexAttribPointer(.color, line_vao, line_col_buf); + + const dims = webgl.getScreenSize(); + const height = dims[1]; + + for (&stars) |*s| { + s.angle = lerp(0, 2 * pi, rng.randFloat(f32)); + s.initial_len = lerp(0, height / 2, rng.randFloat(f32)); + s.dist = max_dist; + s.pdist = max_dist; + s.hue = rng.randFloat(f32); + } +} + +fn rad2deg(rads: f32) f32 { + return rads * (180.0 / pi); +} + +pub fn update(elapsed_time: f32) void { + const report_freq = 100; + frame_counter += 1; + timer += elapsed_time; + anim_t += elapsed_time; + if (frame_counter % report_freq == 0) { + const avg = timer / report_freq; + engine.print("{d:.2} Average FPS", .{1.0 / avg}); + timer = 0; + } + + if (right_pressed) { + speed += 0.1; + } + if (left_pressed) { + speed -= 0.1; + } + speed = @min(max_speed, @max(speed, -max_speed)); + + const count_delta = 500; + if (up_pressed) { + if (sub_count + count_delta <= star_count) { + sub_count += count_delta; + } + } + + if (down_pressed) { + if (sub_count >= count_delta) { + sub_count -= count_delta; + } + } + + const dims = webgl.getScreenSize(); + const width = dims[0]; + const height = dims[1]; + + webgl.viewport(0, 0, @intFromFloat(width), @intFromFloat(height)); + circle_program.use(); + circle_vao.bind(); + circle_program.setUniform(.resolution, .{ width, height }); + + webgl.clear(engine.Color.black); + + var batch: DrawBatch = .{ + .translation = &translation_data, + .colors = &color_data, + .scales = &scale_data, + .lines = &line_data, + }; + + for (stars[0..sub_count]) |*s| { + s.pdist = s.dist; + s.dist -= speed; + s.angle += @cos(anim_t) * 0.05; + const plen = lerp(1000, s.initial_len, s.pdist / max_dist); + const psx = (@cos(s.angle) * plen) + width * 0.5; + const psy = (@sin(s.angle) * plen) + height * 0.5; + + const len = lerp(1000, s.initial_len, s.dist / max_dist); + const sx = (@cos(s.angle) * len) + width * 0.5; + const sy = (@sin(s.angle) * len) + height * 0.5; + const scale = lerp(16, 0, s.dist / max_dist); + + const col = switch (color_mode) { + .rainbow => s.hue, + .solid => @sin(anim_t * 0.1) * 0.5 + 1, + .angle => (s.angle / (2 * pi)) + (@cos(anim_t) + 1) / 2, + .depth => s.dist / (max_dist * 0.5), + }; + + batch.addInstance(.{ sx, sy }, .{ psx, psy }, col, scale) catch unreachable; + if (batch.len == batch_capacity) { + batch.draw(); + batch.len = 0; + } + + if (sx < 0 or sx > width or sy < 0 or sy > height + 32) { + s.angle = lerp(0, 8 * pi, rng.randFloat(f32)); + s.initial_len = lerp(0, height, rng.randFloat(f32)); + s.dist = max_dist; + s.pdist = max_dist; + } + } + + batch.draw(); +} + +pub fn handleInput(ev: engine.input.InputEvent) void { + const SpecialKeys = engine.wasm.SpecialKeyCodepoints; + const up = @intFromEnum(SpecialKeys.arrow_up); + const down = @intFromEnum(SpecialKeys.arrow_down); + const left = @intFromEnum(SpecialKeys.arrow_left); + const right = @intFromEnum(SpecialKeys.arrow_right); + switch (ev) { + .key => |k| { + switch (k.codepoint) { + 'c' => { + if (k.is_down and !k.is_repeat) { + if (color_mode == .depth) { + color_mode = .rainbow; + } else { + color_mode = @enumFromInt(@intFromEnum(color_mode) + 1); + } + } + }, + up => { + up_pressed = k.is_down; + }, + down => { + down_pressed = k.is_down; + }, + left => { + left_pressed = k.is_down; + }, + right => { + right_pressed = k.is_down; + }, + else => {}, + } + }, + else => {}, + } +} + +fn lerp(a: f32, b: f32, v: f32) f32 { + return a + (b - a) * v; +} + +fn invLerp(a: f32, b: f32, v: f32) f32 { + return (v - a) / (b - a); +} + +fn mapLerp(from_a: f32, from_b: f32, to_a: f32, to_b: f32, v: f32) f32 { + const pct = invLerp(from_a, from_b, v); + return lerp(to_a, to_b, pct); +} + +fn circlePoints(comptime segments: u32) [segments * 3][2]f32 { + const segment_rads = (2 * pi) / @as(comptime_float, segments); + var r: [segments * 3][2]f32 = undefined; + for (0..segments) |s| { + const sf: f32 = @floatFromInt(s); + const start_angle = segment_rads * sf; + const start_x = @cos(start_angle); + const start_y = @sin(start_angle); + const start: @Vector(2, f32) = .{ start_x, start_y }; + + const end_angle = segment_rads * (sf + 1); + const end_x = @cos(end_angle); + const end_y = @sin(end_angle); + const end: @Vector(2, f32) = .{ end_x, end_y }; + + const i = s * 3; + r[i] = @Vector(2, f32){ 0, 0 }; + r[i + 1] = start; + r[i + 2] = end; + } + + return r; +} diff --git a/src/matrix.zig b/src/matrix.zig deleted file mode 100644 index 025af7c..0000000 --- a/src/matrix.zig +++ /dev/null @@ -1,80 +0,0 @@ -pub fn Matrix(comptime T: type, rows: usize, cols: usize) type { - const RowVec = @Vector(rows, T); - const ColVec = @Vector(cols, T); - - return struct { - pub const RowT = [rows]T; - const Self = @This(); - - pub const identity: Self = blk: { - const fill = if (T == bool) true else 1; - const empty = if (T == bool) false else 0; - var r: [cols]@Vector(rows, T) = undefined; - for (0..cols) |c| { - var irow: RowT = [1]T{empty} ** rows; - irow[c] = fill; - r[c] = irow; - } - break :blk .{ .data = r }; - }; - - data: [cols]RowT, - - pub fn multiply(a: Self, b: Self) Self { - comptime assert(rows == cols); - var result: Self = undefined; - for (0..rows) |ri| { - const r = a.row(ri); - for (0..cols) |ci| { - const c = b.data[ci]; - result.data[ci][ri] = @reduce(.Add, r * c); - } - } - return result; - } - - pub fn translation(t: @Vector(cols - 1, T)) Self { - var base = Self.identity; - for (0..cols - 1) |ci| { - base.data[ci][rows - 1] = t[ci]; - } - return base; - } - - pub fn rotation(angle: T) Self { - if (rows < 2 or cols < 2) @compileError("Called rotation on a Matrix smaller than 2x2"); - const c = @cos(angle); - const s = @sin(angle); - var base = Self.identity; - base.data[0][0] = c; - base.data[0][1] = -s; - base.data[1][0] = s; - base.data[1][1] = c; - return base; - } - - pub fn scale(sx: T, sy: T) Self { - var r = identity; - r.data[0][0] = sx; - r.data[1][1] = sy; - return r; - } - - pub fn row(self: Self, ri: usize) RowVec { - var r: RowVec = undefined; - for (0..cols) |ci| { - r[ci] = self.data[ci][ri]; - } - return r; - } - - pub fn col(self: Self, ci: usize) ColVec { - const start = ci * rows; - return self.data[start .. start + cols]; - } - }; -} - -fn assert(ok: bool) void { - if (!ok) unreachable; -} diff --git a/src/shell.html b/src/shell.html deleted file mode 100644 index d4f5c85..0000000 --- a/src/shell.html +++ /dev/null @@ -1,134 +0,0 @@ -<!doctype html> -<html lang="en"> - <head> - <meta charset="UTF-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <title>Color Dots</title> - <style> - body { - overflow: hidden; - min-height: 720px; - min-width: 1280px; - 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/cdots.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_cdots.zig b/src/wasm_cdots.zig deleted file mode 100644 index 967bb7c..0000000 --- a/src/wasm_cdots.zig +++ /dev/null @@ -1,537 +0,0 @@ -const std = @import("std"); -const webgl = @import("webgl.zig"); - -const Mat3 = @import("matrix.zig").Matrix(f32, 3, 3); - -extern fn consoleLog(ptr: [*c]const u8, len: u32) void; -extern fn rand() f32; -extern fn getScreenWidth() f32; -extern fn getScreenHeight() f32; -extern fn registerKeyInput(ptr: [*c]const u8, len: u32, out: *u32) void; - -const vs_source = @embedFile("vertex.glsl"); -const fs_source = @embedFile("fragment.glsl"); - -const pi = 3.1415926535; -const max_dist = 100; -const star_count = 100000; -const circle = circlePoints(32); -const max_speed = 2; - -var rng: LinearCongruentialGenerator = undefined; -var stars: [star_count]Star = undefined; - -var right: u32 = 0; -var left: u32 = 0; -var up: u32 = 0; -var down: u32 = 0; -var c_key: u32 = 0; -var c_was_pressed: u32 = 0; - -var circle_program: CircleProgram = undefined; -var line_program: LineProgram = undefined; - -var frame_counter: u32 = 0; - -const batch_capacity = 90000; -var translation_data: [batch_capacity][2]f32 = undefined; -var line_data: [batch_capacity][2][2]f32 = undefined; -var scale_data: [batch_capacity]f32 = undefined; -var color_data: [batch_capacity]f32 = undefined; - -var timer: f32 = 0.0; -var anim_t: f32 = 0; - -var speed: f32 = 1; -var sub_count: u32 = star_count / 2; -var color_mode: enum(u8) { rainbow, solid, angle, depth } = .rainbow; - -const DrawBatch = struct { - translation: [][2]f32, - scales: []f32, - colors: []f32, - lines: [][2][2]f32, - len: u32 = 0, - - fn addInstance(self: *DrawBatch, translation: [2]f32, past_translation: [2]f32, hue: f32, scale: f32) !void { - if (self.len == self.translation.len or self.len == self.colors.len or self.len == self.scales.len or self.len == self.lines.len) return error.OutOfSpace; - - self.translation[self.len] = translation; - self.colors[self.len] = hue; - self.scales[self.len] = scale; - self.lines[self.len] = .{ past_translation, translation }; - self.len += 1; - } - - fn draw(self: DrawBatch) void { - const width = getScreenWidth(); - const height = getScreenHeight(); - webgl.bindVertexArray(circle_program.vao); - webgl.useProgram(circle_program.prog); - webgl.uniform2f(circle_program.res_uniform_location, width, height); - webgl.bindBuffer(.array_buffer, circle_program.translation_buffer); - webgl.bufferData(.array_buffer, @ptrCast(self.translation), self.len * 2, .dynamic_draw); - - webgl.bindBuffer(.array_buffer, circle_program.scale_buffer); - webgl.bufferData(.array_buffer, @ptrCast(self.scales), self.len, .dynamic_draw); - - webgl.bindBuffer(.array_buffer, circle_program.col_buffer); - webgl.bufferData(.array_buffer, @ptrCast(self.colors), self.len, .dynamic_draw); - webgl.drawArraysInstanced(.triangles, 0, circle.len, @intCast(self.len)); - - webgl.bindVertexArray(line_program.vao); - webgl.useProgram(line_program.prog); - webgl.uniform2f(line_program.res_uniform_location, width, height); - - webgl.bindBuffer(.array_buffer, line_program.pos_buffer); - webgl.bufferData(.array_buffer, @ptrCast(self.lines), self.len * 2 * 2, .dynamic_draw); - - webgl.bindBuffer(.array_buffer, line_program.col_buffer); - webgl.bufferData(.array_buffer, @ptrCast(self.colors), self.len, .dynamic_draw); - webgl.drawArraysInstanced(.lines, 0, 2, @intCast(self.len)); - } -}; - -const KeyboardKey = enum { - up, - down, - left, - right, - c, -}; - -const LineProgram = struct { - prog: webgl.Program, - vao: webgl.VertexArrayObject, - - res_uniform_location: i32, - - pos_buffer: webgl.Buffer, - col_buffer: webgl.Buffer, - - fn init() !LineProgram { - const vert = loadShader(.vertex_shader, @embedFile("line_vertex.glsl")) catch return error.FailedToLoadVertexShader; - const frag = loadShader(.fragment_shader, fs_source) catch return error.FailedToLoadFragmentShader; - const prog = createGLProgram(vert, frag) catch return error.FailedToCreateProgram; - webgl.useProgram(prog); - - const attr_name = "in_pos"; - const pos_attr_location = webgl.getAttribLocation(prog, attr_name.ptr, attr_name.len); - - const col_attr = "color"; - const col_attr_location = webgl.getAttribLocation(prog, col_attr.ptr, col_attr.len); - - const res_uniform_location = getUniformLocation(prog, "u_resolution") catch unreachable; - - const pos_buffer = webgl.createBuffer(); - webgl.bindBuffer(.array_buffer, pos_buffer); - webgl.bufferData(.array_buffer, @ptrCast(&[_]f32{ 0, 0, 0, 0 }), 2, .static_draw); - - const vao = webgl.createVertexArray(); - webgl.bindVertexArray(vao); - webgl.enableVertexAttribArray(pos_attr_location); - webgl.vertexAttribPointer(@intCast(pos_attr_location), 4, .f32, .false, 0, 0); - webgl.vertexAttribDivisor(pos_attr_location, 1); - - const col_buffer = webgl.createBuffer(); - webgl.bindBuffer(.array_buffer, col_buffer); - webgl.enableVertexAttribArray(col_attr_location); - webgl.vertexAttribPointer(@intCast(col_attr_location), 1, .f32, .false, 0, 0); - webgl.vertexAttribDivisor(col_attr_location, 1); - - return .{ - .prog = prog, - .pos_buffer = pos_buffer, - .col_buffer = col_buffer, - .res_uniform_location = res_uniform_location, - .vao = vao, - }; - } -}; - -const CircleProgram = struct { - prog: webgl.Program, - vao: webgl.VertexArrayObject, - - res_uniform_location: i32, - - translation_buffer: webgl.Buffer, - scale_buffer: webgl.Buffer, - col_buffer: webgl.Buffer, - - fn init() !CircleProgram { - const vert = loadShader(.vertex_shader, @embedFile("vertex.glsl")) catch return error.FailedToLoadVertexShader; - const frag = loadShader(.fragment_shader, fs_source) catch return error.FailedToLoadFragmentShader; - const prog = createGLProgram(vert, frag) catch return error.FailedToCreateProgram; - webgl.useProgram(prog); - - const attr_name = "in_pos"; - const pos_attr_location = webgl.getAttribLocation(prog, attr_name.ptr, attr_name.len); - - const col_attr = "color"; - const col_attr_location = webgl.getAttribLocation(prog, col_attr.ptr, col_attr.len); - - const translation_attr = "translation"; - const translation_attr_location = webgl.getAttribLocation( - prog, - translation_attr.ptr, - translation_attr.len, - ); - - const scale_attr = "scale"; - const scale_attr_location = webgl.getAttribLocation( - prog, - scale_attr.ptr, - scale_attr.len, - ); - - const res_uniform_location = getUniformLocation(prog, "u_resolution") catch unreachable; - - const pos_buffer = webgl.createBuffer(); - webgl.bindBuffer(.array_buffer, pos_buffer); - webgl.bufferData(.array_buffer, @ptrCast(&circle), circle.len * 2, .static_draw); - - const vao = webgl.createVertexArray(); - webgl.bindVertexArray(vao); - webgl.enableVertexAttribArray(pos_attr_location); - webgl.vertexAttribPointer(@intCast(pos_attr_location), 2, .f32, .false, 0, 0); - - const col_buffer = webgl.createBuffer(); - webgl.bindBuffer(.array_buffer, col_buffer); - webgl.enableVertexAttribArray(col_attr_location); - webgl.vertexAttribPointer(@intCast(col_attr_location), 1, .f32, .false, 0, 0); - webgl.vertexAttribDivisor(col_attr_location, 1); - - const scale_buffer = webgl.createBuffer(); - webgl.bindBuffer(.array_buffer, scale_buffer); - webgl.enableVertexAttribArray(scale_attr_location); - webgl.vertexAttribPointer(@intCast(scale_attr_location), 1, .f32, .false, 0, 0); - webgl.vertexAttribDivisor(scale_attr_location, 1); - - const translation_buffer = webgl.createBuffer(); - webgl.bindBuffer(.array_buffer, translation_buffer); - webgl.enableVertexAttribArray(translation_attr_location); - webgl.vertexAttribPointer(@intCast(translation_attr_location), 2, .f32, .false, 0, 0); - webgl.vertexAttribDivisor(translation_attr_location, 1); - - return .{ - .prog = prog, - .translation_buffer = translation_buffer, - .col_buffer = col_buffer, - .scale_buffer = scale_buffer, - .res_uniform_location = res_uniform_location, - .vao = vao, - }; - } -}; - -const Star = struct { - dist: f32, - pdist: f32, - angle: f32, - initial_len: f32, - hue: f32, -}; - -pub const LinearCongruentialGenerator = struct { - mod: u64, - mul: u64, - inc: u64, - seed: u64, - - const Self = @This(); - /// Move to the next seed value internally and return that value - pub fn next(self: *Self) u64 { - const r = (self.seed *% self.mul +% self.inc) % self.mod; - self.seed = r; - return r; - } - - /// Generate an int of type T with a value from min to max (inclusive) - pub fn randInt(self: *Self, comptime T: type, min: T, max: T) T { - assert(max > min); - const range: u64 = @as(u64, @intCast(max - min)) + 1; - assert(self.mod >= range); - const val: T = @intCast(self.next() % range); - return min + val; - } - - pub fn randFloat(self: *Self, comptime T: type) T { - const pct: T = @as(T, @floatFromInt(self.next())) / @as(T, @floatFromInt(self.mod)); - return pct; - } - - pub fn randEnum(self: *Self, comptime T: type) T { - const info = @typeInfo(T); - if (info != .Enum) @compileError("Cannot call randEnum on type " ++ @typeName(T)); - const fields = info.Enum.fields; - const vals = comptime blk: { - var result: [fields.len]T = undefined; - for (fields, 0..) |f, i| { - result[i] = @field(T, f.name); - } - break :blk result; - }; - const i = self.randInt(usize, 0, fields.len - 1); - return vals[i]; - } - - pub fn randBool(self: *Self) bool { - return self.next() % 2 == 0; - } - - pub fn ZX81(seed: u64) Self { - const mod = pow(u64, 2, 16) + 1; - return .{ - .seed = seed, - .mod = mod, - .mul = 75, - .inc = 74, - }; - } -}; - -fn formatLog(comptime fmt: []const u8, args: anytype) void { - var buf: [512]u8 = undefined; - const m = std.fmt.bufPrint(&buf, fmt, args) catch return; - consoleLog(m.ptr, m.len); -} - -fn assert(ok: bool) void { - if (!ok) unreachable; -} - -pub fn pow(comptime T: type, base: T, exp: T) T { - if (exp == 0) { - return 1; - } else if (exp > 0) { - return base * pow(T, base, exp - 1); - } else if (exp < 0) { - return pow(T, base, exp + 1) / base; - } - unreachable; -} - -export fn init() void { - const f_seed = rand() * (pow(f32, 2, 16) + 1); - const seed: u64 = @intFromFloat(f_seed); - rng = LinearCongruentialGenerator.ZX81(seed); - - circle_program = CircleProgram.init() catch |err| { - formatLog("Failed to create CircleProgram. {}", .{err}); - return; - }; - - line_program = LineProgram.init() catch |err| { - formatLog("Failed to create CircleProgram. {}", .{err}); - return; - }; - - const height = getScreenHeight(); - - for (&stars) |*s| { - s.angle = lerp(0, 2 * pi, rng.randFloat(f32)); - s.initial_len = lerp(0, height / 2, rng.randFloat(f32)); - s.dist = max_dist; - s.pdist = max_dist; - s.hue = rng.randFloat(f32); - } - - registerKey(.up, &up); - registerKey(.right, &right); - registerKey(.left, &left); - registerKey(.down, &down); - registerKey(.c, &c_key); -} - -fn registerKey(key: KeyboardKey, out: *u32) void { - const key_code = switch (key) { - .up => "ArrowUp", - .down => "ArrowDown", - .left => "ArrowLeft", - .right => "ArrowRight", - .c => "KeyC", - }; - registerKeyInput(key_code.ptr, key_code.len, out); -} - -fn rad2deg(rads: f32) f32 { - return rads * (180.0 / pi); -} - -export fn update(elapsed_time: f32) void { - const report_freq = 100; - frame_counter += 1; - timer += elapsed_time; - anim_t += elapsed_time; - if (frame_counter % report_freq == 0) { - const avg = timer / report_freq; - formatLog("{d:.2} Average FPS", .{1.0 / avg}); - timer = 0; - } - - const next_col_mode = c_key != 0 and c_was_pressed == 0; - c_was_pressed = c_key; - if (next_col_mode) { - if (color_mode == .depth) { - color_mode = .rainbow; - } else { - color_mode = @enumFromInt(@intFromEnum(color_mode) + 1); - } - } - - if (right != 0) { - speed += 0.1; - } - if (left != 0) { - speed -= 0.1; - } - speed = @min(max_speed, @max(speed, -max_speed)); - - const count_delta = 500; - if (up != 0) { - if (sub_count + count_delta <= star_count) { - sub_count += count_delta; - } - } - - if (down != 0) { - if (sub_count >= count_delta) { - sub_count -= count_delta; - } - } - - const width = getScreenWidth(); - const height = getScreenHeight(); - - webgl.viewport(0, 0, @intFromFloat(width), @intFromFloat(height)); - webgl.useProgram(circle_program.prog); - webgl.bindVertexArray(circle_program.vao); - webgl.uniform2f(circle_program.res_uniform_location, height, height); - - webgl.clearColor(0.0, 0.0, 0.0, 1.0); - webgl.clear(webgl.color_buffer_bit | webgl.depth_buffer_bit); - - var batch: DrawBatch = .{ - .translation = &translation_data, - .colors = &color_data, - .scales = &scale_data, - .lines = &line_data, - }; - - for (stars[0..sub_count]) |*s| { - s.pdist = s.dist; - s.dist -= speed; - s.angle += @cos(anim_t) * 0.05; - const plen = lerp(1000, s.initial_len, s.pdist / max_dist); - const psx = (@cos(s.angle) * plen) + width * 0.5; - const psy = (@sin(s.angle) * plen) + height * 0.5; - - const len = lerp(1000, s.initial_len, s.dist / max_dist); - const sx = (@cos(s.angle) * len) + width * 0.5; - const sy = (@sin(s.angle) * len) + height * 0.5; - const scale = lerp(16, 0, s.dist / max_dist); - - const col = switch (color_mode) { - .rainbow => s.hue, - .solid => @sin(anim_t * 0.1) * 0.5 + 1, - .angle => (s.angle / (2 * pi)) + (@cos(anim_t) + 1) / 2, - .depth => s.dist / (max_dist * 0.5), - }; - - batch.addInstance(.{ sx, sy }, .{ psx, psy }, col, scale) catch unreachable; - if (batch.len == batch_capacity) { - batch.draw(); - batch.len = 0; - } - - if (sx < 0 or sx > width or sy < 0 or sy > height + 32) { - s.angle = lerp(0, 8 * pi, rng.randFloat(f32)); - s.initial_len = lerp(0, height, rng.randFloat(f32)); - s.dist = max_dist; - s.pdist = max_dist; - } - } - - batch.draw(); -} - -fn lerp(a: f32, b: f32, v: f32) f32 { - return a + (b - a) * v; -} - -fn invLerp(a: f32, b: f32, v: f32) f32 { - return (v - a) / (b - a); -} - -fn mapLerp(from_a: f32, from_b: f32, to_a: f32, to_b: f32, v: f32) f32 { - const pct = invLerp(from_a, from_b, v); - return lerp(to_a, to_b, pct); -} - -fn circlePoints(comptime segments: u32) [segments * 3][2]f32 { - const segment_rads = (2 * pi) / @as(comptime_float, segments); - var r: [segments * 3][2]f32 = undefined; - for (0..segments) |s| { - const sf: f32 = @floatFromInt(s); - const start_angle = segment_rads * sf; - const start_x = @cos(start_angle); - const start_y = @sin(start_angle); - const start: @Vector(2, f32) = .{ start_x, start_y }; - - const end_angle = segment_rads * (sf + 1); - const end_x = @cos(end_angle); - const end_y = @sin(end_angle); - const end: @Vector(2, f32) = .{ end_x, end_y }; - - const i = s * 3; - r[i] = @Vector(2, f32){ 0, 0 }; - r[i + 1] = start; - r[i + 2] = end; - } - - return r; -} - -fn getUniformLocation(program: webgl.Program, name: []const u8) !i32 { - const loc = webgl.getUniformLocation(program, name.ptr, name.len); - if (loc < 0) return error.FailedToGetLocation; - - return loc; -} - -fn loadShader(shader_type: webgl.GLShaderType, source: []const u8) !webgl.Shader { - const shader = webgl.createShader(shader_type); - errdefer webgl.deleteShader(shader); - - webgl.shaderSource(shader, source.ptr, source.len); - webgl.compileShader(shader); - - if (webgl.getShaderParameter(shader, .compile_status) == 0) { - var buf: [512]u8 = undefined; - const len = webgl.getShaderInfoLog(shader, &buf, buf.len); - const msg = buf[0..len]; - consoleLog(msg.ptr, msg.len); - return error.CompilationFailed; - } - - return shader; -} - -fn createGLProgram(vert: webgl.Shader, frag: webgl.Shader) !webgl.Program { - const program = webgl.createProgram(); - errdefer webgl.deleteProgram(program); - - webgl.attachShader(program, vert); - webgl.attachShader(program, frag); - webgl.linkProgram(program); - if (webgl.getProgramParameter(program, .link_status) == 0) { - var buf: [512]u8 = undefined; - const len = webgl.getProgramInfoLog(program, &buf, buf.len); - const msg = buf[0..len]; - consoleLog(msg.ptr, msg.len); - return error.LinkFailed; - } - - return program; -} diff --git a/src/webgl.js b/src/webgl.js deleted file mode 100644 index 572773c..0000000 --- a/src/webgl.js +++ /dev/null @@ -1,261 +0,0 @@ -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 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 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 createShader = (type) => { - const id = getId(); - glShaders.set(id, gfx_ctx.createShader(type)); - return id; -}; - -const shaderSource = (shader, ptr, len) => { - const source = readString(ptr, len); - console.log(source); - gfx_ctx.shaderSource(glShaders.get(shader), source); -}; - -const compileShader = (shader) => { - gfx_ctx.compileShader(glShaders.get(shader)); -}; - -const getShaderParameter = (shader, info) => { - return gfx_ctx.getShaderParameter(glShaders.get(shader), info); -}; - -const deleteShader = (shader) => { - gfx_ctx.deleteShader(glShaders.get(shader)); -}; - -const getShaderInfoLog = (shader, ptr, len) => { - const msg = gfx_ctx.getShaderInfoLog(glShaders.get(shader)); - const r = writeString(msg, ptr, len); - return r; -}; - -const createProgram = (type) => { - const id = getId(); - glPrograms.set(id, gfx_ctx.createProgram()); - return id; -}; - -const deleteProgram = (program) => { - gfx_ctx.deleteProgram(glPrograms.get(program)); -}; - -const attachShader = (program_id, shader_id) => { - const program = glPrograms.get(program_id); - const shader = glShaders.get(shader_id); - gfx_ctx.attachShader(program, shader); -}; - -const linkProgram = (program) => { - gfx_ctx.linkProgram(glPrograms.get(program)); -}; - -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 createVertexArray = () => { - const id = getId(); - glVertexArrays.set(id, gfx_ctx.createVertexArray()); - return id; -}; - -const bindVertexArray = (vao) => { - gfx_ctx.bindVertexArray(glVertexArrays.get(vao)); -}; - -const enableVertexAttribArray = (attrib_location) => { - gfx_ctx.enableVertexAttribArray(attrib_location); -}; - -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 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 glTextures = new Map(); - -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 = { - clear, - clearColor, - createBuffer, - bindBuffer, - bufferData, - - createShader, - 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 deleted file mode 100644 index 0343bff..0000000 --- a/src/webgl.zig +++ /dev/null @@ -1,152 +0,0 @@ -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 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; |