summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralaric <alaric@netmythos.org>2024-04-01 03:58:23 -0700
committeralaric <alaric@netmythos.org>2024-04-01 03:58:23 -0700
commite4d1a7e30c586e07441e65d3062bbc7f67b1aea5 (patch)
tree3c1924efe792757221e4cafb95576ca480efdd70
parentc43b1f972cd91b157f7d0997c5bdd9d94590d93d (diff)
downloadmenger-e4d1a7e30c586e07441e65d3062bbc7f67b1aea5.tar.gz
menger-e4d1a7e30c586e07441e65d3062bbc7f67b1aea5.zip
Fancy metaprogramming for shader uniforms
-rw-r--r--src/fragment.glsl8
-rw-r--r--src/matrix.zig80
-rw-r--r--src/vertex.glsl8
-rw-r--r--src/wasm.zig13
-rw-r--r--src/wasm_msponge.zig17
-rw-r--r--src/webgl.js1
-rw-r--r--src/webgl.zig284
-rw-r--r--src/webgl_bindings.zig156
8 files changed, 413 insertions, 154 deletions
diff --git a/src/fragment.glsl b/src/fragment.glsl
new file mode 100644
index 0000000..1aa44f8
--- /dev/null
+++ b/src/fragment.glsl
@@ -0,0 +1,8 @@
+#version 300 es
+precision highp float;
+
+out vec4 outColor;
+
+void main() {
+ outColor = vec4(1, 0, 0, 1); // red
+}
diff --git a/src/matrix.zig b/src/matrix.zig
new file mode 100644
index 0000000..025af7c
--- /dev/null
+++ b/src/matrix.zig
@@ -0,0 +1,80 @@
+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/vertex.glsl b/src/vertex.glsl
new file mode 100644
index 0000000..cf72553
--- /dev/null
+++ b/src/vertex.glsl
@@ -0,0 +1,8 @@
+#version 300 es
+
+uniform vec2 pos;
+
+void main() {
+ gl_Position = vec4(pos, 0, 1); // center
+ gl_PointSize = 120.0;
+}
diff --git a/src/wasm.zig b/src/wasm.zig
new file mode 100644
index 0000000..bbea2db
--- /dev/null
+++ b/src/wasm.zig
@@ -0,0 +1,13 @@
+//! The zig interface for accessing functions from the web/javascript
+//! side of the engine. Whenever the engine has a more concrete identity,
+//! this should probably get a more expressive filename.
+
+const std = @import("std");
+
+extern fn consoleLog(ptr: [*c]const u8, len: u32) void;
+
+pub fn print(comptime fmt: []const u8, args: anytype) void {
+ var buf: [512]u8 = undefined;
+ const msg = std.fmt.bufPrint(&buf, fmt, args) catch "There was an error formatting your output string";
+ consoleLog(msg.ptr, msg.len);
+}
diff --git a/src/wasm_msponge.zig b/src/wasm_msponge.zig
index 376e011..8d43fe7 100644
--- a/src/wasm_msponge.zig
+++ b/src/wasm_msponge.zig
@@ -1,10 +1,21 @@
-extern fn consoleLog(ptr: [*c]const u8, len: u32) void;
+const wasm = @import("wasm.zig");
+const webgl = @import("webgl.zig");
+
+const MyProgram = webgl.Program(&.{.{ .identifier = "pos", .shader_name = "pos", .kind = .vec2 }});
+var prog: MyProgram = undefined;
export fn init() void {
- const message = "Hello Menger Sponge!";
- consoleLog(message.ptr, message.len);
+ wasm.print("Hello Menger Sponge {d}!", .{2});
+ prog = MyProgram.init(@embedFile("vertex.glsl"), @embedFile("fragment.glsl")) catch return;
}
export fn update(delta_time: f32) void {
_ = delta_time;
+ const bg = webgl.Color.fromVec(.{ 1, 0, 1, 1 });
+ webgl.viewportToScreen();
+ webgl.clear(bg);
+
+ prog.use();
+ prog.setUniform(.pos, .{ 0.5, 0.5 });
+ webgl.drawArrays(.points, 0, 1);
}
diff --git a/src/webgl.js b/src/webgl.js
index 7c60142..823ec53 100644
--- a/src/webgl.js
+++ b/src/webgl.js
@@ -143,7 +143,6 @@ const linkProgram = (program) => {
const shaderSource = (shader, ptr, len) => {
const source = readString(ptr, len);
- console.log(source);
gfx_ctx.shaderSource(glShaders.get(shader), source);
};
diff --git a/src/webgl.zig b/src/webgl.zig
index b97c8fa..145abbd 100644
--- a/src/webgl.zig
+++ b/src/webgl.zig
@@ -1,155 +1,139 @@
-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,
+const std = @import("std");
+const wasm = @import("wasm.zig");
+const bindings = @import("webgl_bindings.zig");
+
+pub const Color = struct {
+ r: f32 = 0,
+ g: f32 = 0,
+ b: f32 = 0,
+ a: f32 = 1,
+
+ pub const red: Color = Color.fromVec(.{ 1, 0, 0, 1 });
+
+ pub fn fromVec(val: @Vector(4, f32)) Color {
+ return .{
+ .r = val[0],
+ .g = val[1],
+ .b = val[2],
+ .a = val[3],
+ };
+ }
};
-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,
-};
+//pub const Program = packed struct(u32) { handle: u32 };
-const GLBool = enum(i32) {
- false = 0,
- true = 1,
+const UniformInfo = struct {
+ identifier: [:0]const u8,
+ shader_name: []const u8,
+ kind: enum { vec2 },
};
-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;
+pub fn Program(comptime uniforms: []const UniformInfo) type {
+ const UniEnum: type = blk: {
+ const EnumField = std.builtin.Type.EnumField;
+ var fields: []const EnumField = &.{};
+ for (uniforms, 0..) |u, i| {
+ const field: EnumField = .{ .name = u.identifier, .value = i };
+ fields = &(fields[0..i].* ++ [1]EnumField{field});
+ }
+
+ const info: std.builtin.Type.Enum = .{
+ .fields = fields,
+ .decls = &.{},
+ .is_exhaustive = false,
+ .tag_type = u32,
+ };
+ break :blk @Type(.{ .Enum = info });
+ };
+ return struct {
+ handle: u32,
+ uniform_handles: [uniforms.len]i32,
+
+ const Self = @This();
+ pub fn init(vert_src: []const u8, frag_src: []const u8) !Self {
+ const vert = try loadShader(.vertex, vert_src);
+ errdefer bindings.deleteShader(vert);
+ const frag = try loadShader(.fragment, frag_src);
+ errdefer bindings.deleteShader(frag);
+
+ const handle = try createProgram(vert, frag);
+
+ var uniform_handles: [uniforms.len]i32 = undefined;
+ for (uniforms, 0..) |u, i| {
+ const h = bindings.getUniformLocation(handle, u.shader_name.ptr, u.shader_name.len);
+ if (h < 0) return error.FailedToGetUniformLocation;
+ uniform_handles[i] = h;
+ }
+
+ return .{ .handle = handle, .uniform_handles = uniform_handles };
+ }
+
+ pub fn use(self: Self) void {
+ bindings.useProgram(self.handle);
+ }
+
+ pub fn setUniform(self: Self, comptime uniform: UniEnum, val: anytype) void {
+ const handle = self.uniform_handles[@intFromEnum(uniform)];
+ const info = uniforms[@intFromEnum(uniform)];
+
+ switch (info.kind) {
+ .vec2 => {
+ bindings.uniform2f(handle, val[0], val[1]);
+ },
+ }
+ }
+ };
+}
+
+pub fn clear(col: Color) void {
+ bindings.clearColor(col.r, col.g, col.b, col.a);
+ bindings.clear(bindings.color_buffer_bit);
+}
+
+pub fn getScreenSize() [2]f32 {
+ return .{ bindings.getScreenWidth(), bindings.getScreenHeight() };
+}
+
+pub const viewport = bindings.viewport;
+pub fn viewportToScreen() void {
+ const dims = getScreenSize();
+ viewport(0, 0, @intFromFloat(dims[0]), @intFromFloat(dims[1]));
+}
+
+pub const drawArrays = bindings.drawArrays;
+
+fn loadShader(shader_type: bindings.ShaderType, source: []const u8) !u32 {
+ const shader = bindings.createShader(shader_type);
+ errdefer bindings.deleteShader(shader);
+
+ bindings.shaderSource(shader, source.ptr, source.len);
+ bindings.compileShader(shader);
+
+ if (bindings.getShaderParameter(shader, .compile_status) == 0) {
+ var buf: [512]u8 = undefined;
+ const len = bindings.getShaderInfoLog(shader, &buf, buf.len);
+ const msg = buf[0..len];
+ wasm.print("{s}", .{msg});
+ return error.CompilationFailed;
+ }
+
+ return shader;
+}
+
+fn createProgram(vert: u32, frag: u32) !u32 {
+ const program = bindings.createProgram();
+ errdefer bindings.deleteProgram(program);
+
+ bindings.attachShader(program, vert);
+ bindings.attachShader(program, frag);
+ bindings.linkProgram(program);
+ if (bindings.getProgramParameter(program, .link_status) == 0) {
+ var buf: [512]u8 = undefined;
+ const len = bindings.getProgramInfoLog(program, &buf, buf.len);
+ const msg = buf[0..len];
+ wasm.print("{s}", .{msg});
+ return error.LinkFailed;
+ }
+
+ return program;
+}
diff --git a/src/webgl_bindings.zig b/src/webgl_bindings.zig
new file mode 100644
index 0000000..b152353
--- /dev/null
+++ b/src/webgl_bindings.zig
@@ -0,0 +1,156 @@
+pub const Buffer = extern struct { handle: u32 };
+pub const Texture = packed struct(u32) { handle: u32 };
+
+pub const GLProgramInfo = enum(u32) {
+ delete_status = 0x8B80,
+ link_status = 0x8B82,
+};
+
+pub const ShaderInfo = 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 getScreenHeight() f32;
+pub extern fn getScreenWidth() f32;
+
+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 const ShaderType = enum(u32) {
+ fragment = 0x8B30,
+ vertex = 0x8B31,
+};
+
+pub extern fn createShader(shader_type: ShaderType) u32;
+pub extern fn shaderSource(shader: u32, source_ptr: [*c]const u8, len: u32) void;
+pub extern fn compileShader(shader: u32) void;
+pub extern fn getShaderParameter(shader: u32, info: ShaderInfo) u32;
+pub extern fn deleteShader(shader: u32) void;
+pub extern fn getShaderInfoLog(shader: u32, buf: [*c]u8, len: u32) u32;
+
+pub extern fn createProgram() u32;
+pub extern fn deleteProgram(program: u32) void;
+pub extern fn attachShader(program: u32, shader: u32) void;
+pub extern fn linkProgram(program: u32) void;
+pub extern fn getProgramParameter(program: u32, info: GLProgramInfo) u32;
+pub extern fn getProgramInfoLog(program: u32, buf: [*c]const u8, len: u32) u32;
+pub extern fn useProgram(program: u32) void;
+
+pub extern fn getAttribLocation(program: u32, buf: [*c]const u8, len: u32) i32;
+pub extern fn getUniformLocation(program: u32, 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 DrawMode = 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: DrawMode, 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: DrawMode,
+ first: i32,
+ count: i32,
+ instace_count: i32,
+) void;