diff options
author | alaric <alaric@netmythos.org> | 2024-04-05 06:23:00 -0700 |
---|---|---|
committer | alaric <alaric@netmythos.org> | 2024-04-05 06:23:00 -0700 |
commit | de6948de605bb8b74052f2afd73b361abbdf8d50 (patch) | |
tree | dd83e54279f0427ed9e02b881d1989f8707a7cbb | |
parent | 92b20e1bf706b62466f9952465011d6a91364805 (diff) | |
download | menger-de6948de605bb8b74052f2afd73b361abbdf8d50.tar.gz menger-de6948de605bb8b74052f2afd73b361abbdf8d50.zip |
It is actually a Menger Sponge now
-rw-r--r-- | src/vertex.glsl | 4 | ||||
-rw-r--r-- | src/wasm_msponge.zig | 160 | ||||
-rw-r--r-- | src/webgl.zig | 9 |
3 files changed, 163 insertions, 10 deletions
diff --git a/src/vertex.glsl b/src/vertex.glsl index 175565b..df628cf 100644 --- a/src/vertex.glsl +++ b/src/vertex.glsl @@ -2,12 +2,14 @@ in vec4 pos; in vec3 col; +in vec3 offset; uniform mat4 mat; out vec4 a_col; void main() { - gl_Position = pos * mat; + vec4 p = pos + vec4(offset, 0); + gl_Position = (p * mat); a_col = vec4(col, 1); } diff --git a/src/wasm_msponge.zig b/src/wasm_msponge.zig index 69cf211..943fd38 100644 --- a/src/wasm_msponge.zig +++ b/src/wasm_msponge.zig @@ -12,12 +12,19 @@ const MyProgram = webgl.Program( &.{ .{ .identifier = "pos", .shader_name = "pos", .kind = .vec4 }, .{ .identifier = "col", .shader_name = "col", .kind = .vec3 }, + .{ + .identifier = "offset", + .shader_name = "offset", + .kind = .vec3, + .instance_divisor = 1, + }, }, ); var prog: MyProgram = undefined; var vao: webgl.VertexArrayObject = undefined; var pos_buffer: webgl.Buffer = undefined; var col_buffer: webgl.Buffer = undefined; +var offset_buffer: webgl.Buffer = undefined; export fn init() void { wasm.print("Hello Menger Sponge {d}!", .{2}); @@ -25,8 +32,10 @@ export fn init() void { vao = webgl.VertexArrayObject.init(); pos_buffer = webgl.createBuffer(); col_buffer = webgl.createBuffer(); + offset_buffer = webgl.createBuffer(); prog.setVertexAttribPointer(.pos, vao, pos_buffer, .{}); prog.setVertexAttribPointer(.col, vao, col_buffer, .{}); + prog.setVertexAttribPointer(.offset, vao, offset_buffer, .{}); webgl.bindBuffer(.array_buffer, pos_buffer); webgl.bufferData(.array_buffer, &box_verts, .static_draw); @@ -34,6 +43,8 @@ export fn init() void { webgl.bindBuffer(.array_buffer, col_buffer); webgl.bufferData(.array_buffer, &col_data, .static_draw); + setupSubdividedCube(4, mengerTest); + webgl.enable(.cull_face); webgl.enable(.depth_test); } @@ -45,7 +56,7 @@ const col_data: [18 * 6]f32 = blk: { const tri = red ++ green ++ blue; break :blk tri ** 12; }; -const box_verts = boxVertices(100, 100, 100); +const box_verts = boxVertices(1, 1, 1); fn multAllMat4(in: [][3]f32, mat: Mat4) void { for (in) |*r| { @@ -132,6 +143,114 @@ fn boxVertices(x: f32, y: f32, z: f32) [18 * 6]f32 { var timer: f32 = 0; +const max_depth = 4; +var offsets: [pow(u32, 27, max_depth)][3]f32 = undefined; +var cube_count: i32 = 0; +var current_depth: u32 = 1; +var base_scale: f32 = 1; + +fn updateMenger(depth: u32) void { + setupSubdividedCube(depth, mengerTest); +} + +fn setupSubdividedCube(divisions: u32, condFn: *const fn (u32, u32, u32) bool) void { + webgl.bindBuffer(.array_buffer, offset_buffer); + const m_offsets = subdividedCube(divisions, condFn); + const m_ptr: [*]const f32 = @ptrCast(m_offsets); + const m_len = m_offsets.len * 3; + wasm.print("{d} cubes generated", .{m_offsets.len}); + webgl.bufferData(.array_buffer, m_ptr[0..m_len], .dynamic_draw); + cube_count = @intCast(m_offsets.len); + current_depth = divisions; +} + +fn subdividedCube(divisions: u32, condFn: *const fn (u32, u32, u32) bool) []const [3]f32 { + const cubes_per_side = pow(u32, 3, divisions); + base_scale = 1.0 / @as(f32, @floatFromInt(cubes_per_side)); + + var len: u32 = 0; + for (0..cubes_per_side) |z| { + for (0..cubes_per_side) |y| { + for (0..cubes_per_side) |x| { + if (condFn(x, y, z)) { + const c: @Vector(3, f32) = .{ + @floatFromInt(x), + @floatFromInt(y), + @floatFromInt(z), + }; + const adj: @Vector(3, f32) = @splat(@as(f32, @floatFromInt(cubes_per_side)) / 3.0); + offsets[len] = c - adj; + len += 1; + } + } + } + } + + return offsets[0..len]; +} + +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; +} + +fn mengerOffsets(depth: u32) []const [3]f32 { + //if (depth == 0) return &[1][3]f32{.{ 0, 0, 0 }}; + const cubes_per_side = pow(u32, 3, depth); + base_scale = 1.0 / @as(f32, @floatFromInt(cubes_per_side)); + + var len: u32 = 0; + for (0..cubes_per_side) |z| { + for (0..cubes_per_side) |y| { + for (0..cubes_per_side) |x| { + if (isValidIndex(x, y, z)) { + const c: @Vector(3, f32) = .{ + @floatFromInt(x), + @floatFromInt(y), + @floatFromInt(z), + }; + const adj: @Vector(3, f32) = @splat(@as(f32, @floatFromInt(cubes_per_side)) / 3.0); + offsets[len] = c - adj; + len += 1; + } + } + } + } + + return offsets[0..len]; +} + +fn mengerTest(x: u32, y: u32, z: u32) bool { + const meng_x = !(x > 0 and (x - 1) % 3 == 0); + const meng_y = !(y > 0 and (y - 1) % 3 == 0); + const meng_z = !(z > 0 and (z - 1) % 3 == 0); + const tot = @as(u8, @intFromBool(meng_x)) + @as(u8, @intFromBool(meng_y)) + @as(u8, @intFromBool(meng_z)); + + if (x >= 3 or y >= 3 or z >= 3) { + return tot > 1 and mengerTest(@divFloor(x, 3), @divFloor(y, 3), @divFloor(z, 3)); + } else { + return tot > 1; + } +} + +fn inverseMenger(x: u32, y: u32, z: u32) bool { + return !mengerTest(x, y, z); +} + +fn isValidIndex(x: u32, y: u32, z: u32) bool { + const meng_x = !(x > 0 and (x - 1) % 3 == 0); + const meng_y = !(y > 0 and (y - 1) % 3 == 0); + const meng_z = !(z > 0 and (z - 1) % 3 == 0); + const tot = @as(u8, @intFromBool(meng_x)) + @as(u8, @intFromBool(meng_y)) + @as(u8, @intFromBool(meng_z)); + return tot > 1; +} + export fn update(delta_time: f32) void { timer += delta_time; const bg = webgl.Color.fromVec(.{ 1, 1, 1, 1 }); @@ -144,23 +263,48 @@ export fn update(delta_time: f32) void { const screen_dims = webgl.getScreenSize(); const aspect = screen_dims[0] / screen_dims[1]; const proj = perspectiveMatrix(degToRad(60), aspect, 1, 2000); - const tlate = Mat4.translation(.{ @sin(timer * 4) * 100, 0, -200 }); - const rot = Mat4.yRotation(timer).multiply(Mat4.xRotation(timer)); - const s = (@sin(timer) * 0.5 + 0.5); + const tlate = Mat4.translation(.{ 0, 0, -225 }); + const rot = Mat4.yRotation(timer).multiply(Mat4.xRotation(0)).multiply(Mat4.zRotation(0)); + const s: f32 = 150 * base_scale; const scale = Mat4.scale(.{ s, s, s }); const model = scale.multiply(rot.multiply(tlate)); const final_mat = model.multiply(proj); - const m: matrix.Matrix(f32, 1, 4) = .{ .data = @bitCast([4]f32{ 0, 0, 0, 0 }) }; - wasm.print("{d}", .{m.multiply(final_mat).data}); prog.setUniform(.mat, final_mat); - webgl.drawArrays(.triangles, 0, 36); + + webgl.drawArraysInstanced(.triangles, 0, 36, cube_count); + //webgl.drawArrays(.triangles, 0, 36); + prog.setUniform(.color, .{ 0.0, 0.0, 0.0 }); for (0..12) |i| { - webgl.drawArrays(.line_loop, @intCast(i * 3), 3); + //webgl.drawArrays(.line_loop, @intCast(i * 3), 3); + webgl.drawArraysInstanced(.line_loop, @intCast(i * 3), 3, cube_count); } } +fn isPerfectSquare(val: f32) bool { + const s = @sqrt(val); + return s * s == val; +} + +fn isFibonacci(val: u32) bool { + const n: f32 = @floatFromInt(val); + return isPerfectSquare(5 * n * n + 4) and isPerfectSquare(5 * n * n - 4); +} + +fn myTest(x: u32, y: u32, z: u32) bool { + const total = x + y + z; + _ = total; + const total_f: f32 = @floatFromInt(x + y + z); + _ = total_f; + return isFibonacci(y) and x * x < z * z; +} + +fn evenLatticeTest(x: u32, y: u32, z: u32) bool { + const total = x + y + z; + return total % 2 == 0; +} + fn degToRad(deg: f32) f32 { return deg * pi / 180; } diff --git a/src/webgl.zig b/src/webgl.zig index 9a373f6..1e1c5a7 100644 --- a/src/webgl.zig +++ b/src/webgl.zig @@ -44,6 +44,7 @@ const AttributeInfo = struct { identifier: [:0]const u8, shader_name: []const u8, kind: enum { vec2, vec3, vec4 }, + instance_divisor: ?u16 = null, }; pub const Buffer = bindings.Buffer; @@ -129,6 +130,7 @@ pub fn Program( for (attributes, 0..) |a, i| { const h = bindings.getAttribLocation(handle, a.shader_name.ptr, a.shader_name.len); if (h < 0) return error.FailedToGetUniformLocation; + attribute_handles[i] = h; } @@ -201,13 +203,17 @@ pub fn Program( stride, offset, ); + + if (attr_info.instance_divisor) |d| { + bindings.vertexAttribDivisor(attr_handle, d); + } } }; } pub fn clear(col: Color) void { bindings.clearColor(col.r, col.g, col.b, col.a); - bindings.clear(bindings.color_buffer_bit); + bindings.clear(bindings.color_buffer_bit | bindings.depth_buffer_bit); } pub fn getScreenSize() [2]f32 { @@ -221,6 +227,7 @@ pub fn viewportToScreen() void { } pub const drawArrays = bindings.drawArrays; +pub const drawArraysInstanced = bindings.drawArraysInstanced; fn loadShader(shader_type: bindings.ShaderType, source: []const u8) !u32 { const shader = bindings.createShader(shader_type); |