diff options
author | alaric <alaric@netmythos.org> | 2024-04-01 03:58:23 -0700 |
---|---|---|
committer | alaric <alaric@netmythos.org> | 2024-04-01 03:58:23 -0700 |
commit | e4d1a7e30c586e07441e65d3062bbc7f67b1aea5 (patch) | |
tree | 3c1924efe792757221e4cafb95576ca480efdd70 | |
parent | c43b1f972cd91b157f7d0997c5bdd9d94590d93d (diff) | |
download | menger-e4d1a7e30c586e07441e65d3062bbc7f67b1aea5.tar.gz menger-e4d1a7e30c586e07441e65d3062bbc7f67b1aea5.zip |
Fancy metaprogramming for shader uniforms
-rw-r--r-- | src/fragment.glsl | 8 | ||||
-rw-r--r-- | src/matrix.zig | 80 | ||||
-rw-r--r-- | src/vertex.glsl | 8 | ||||
-rw-r--r-- | src/wasm.zig | 13 | ||||
-rw-r--r-- | src/wasm_msponge.zig | 17 | ||||
-rw-r--r-- | src/webgl.js | 1 | ||||
-rw-r--r-- | src/webgl.zig | 284 | ||||
-rw-r--r-- | src/webgl_bindings.zig | 156 |
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; |