diff options
author | alaric <alaric@netmythos.org> | 2024-03-30 01:00:07 -0700 |
---|---|---|
committer | alaric <alaric@netmythos.org> | 2024-03-30 01:00:07 -0700 |
commit | 17223eee868dfcd8f12bb2fd3b75acc0b2d0529a (patch) | |
tree | e1a5858fba61902e694ecb1e257f5cc7557feeab | |
parent | a9f46fb682935d9637d4466dbb22aad2475859b7 (diff) | |
download | colordots-17223eee868dfcd8f12bb2fd3b75acc0b2d0529a.tar.gz colordots-17223eee868dfcd8f12bb2fd3b75acc0b2d0529a.zip |
Many colored dots
-rw-r--r-- | src/fragment.glsl | 7 | ||||
-rw-r--r-- | src/matrix.zig | 2 | ||||
-rw-r--r-- | src/shell.html | 4 | ||||
-rw-r--r-- | src/vertex.glsl | 25 | ||||
-rw-r--r-- | src/wasm_ttd.zig | 332 | ||||
-rw-r--r-- | src/webgl.js | 14 | ||||
-rw-r--r-- | src/webgl.zig | 8 |
7 files changed, 158 insertions, 234 deletions
diff --git a/src/fragment.glsl b/src/fragment.glsl index 5df9a71..03d65f6 100644 --- a/src/fragment.glsl +++ b/src/fragment.glsl @@ -1,14 +1,11 @@ #version 300 es precision highp float; -uniform vec4 u_color; -uniform sampler2D u_texture; - -in vec2 v_texcoord; +in vec3 v_color; out vec4 outColor; void main() { //outColor = texture(u_texture, v_texcoord * vec2(1, -1)) * u_color; - outColor = u_color; + outColor = vec4(v_color, 1.0); } diff --git a/src/matrix.zig b/src/matrix.zig index 5aff1dd..025af7c 100644 --- a/src/matrix.zig +++ b/src/matrix.zig @@ -1,9 +1,9 @@ pub fn Matrix(comptime T: type, rows: usize, cols: usize) type { - const RowT = [rows]T; 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: { diff --git a/src/shell.html b/src/shell.html index 0f4b598..e05b7e0 100644 --- a/src/shell.html +++ b/src/shell.html @@ -19,7 +19,11 @@ <body> <canvas id="ttd-canvas" width="1280" height="720" tabindex="0"></canvas> <script src="webgl.js"></script> + <script type="text/javascript" src="https://spectorcdn.babylonjs.com/spector.bundle.js"></script> <script> + var spector = new SPECTOR.Spector(); + spector.displayUI(); + spector.spyCanvases(); let memory = null; let last_frame_time = Date.now(); diff --git a/src/vertex.glsl b/src/vertex.glsl index f3e9a1e..fcdcd5d 100644 --- a/src/vertex.glsl +++ b/src/vertex.glsl @@ -1,18 +1,31 @@ #version 300 es -in vec2 a_position; -in vec2 a_texcoord; +in vec2 in_pos; +in float color; +in float scale; +in vec2 translation; uniform vec2 u_resolution; -uniform mat3 u_matrix; out vec2 v_texcoord; +out vec3 v_color; + +// Function from IƱigo Quiles +// https://www.shadertoy.com/view/MsS3Wc +vec3 hsb2rgb( in vec3 c ){ + vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), + 6.0)-3.0)-1.0, + 0.0, + 1.0 ); + rgb = rgb*rgb*(3.0-2.0*rgb); + return c.z * mix(vec3(1.0), rgb, c.y); +} void main() { - vec2 position = (vec3(a_position, 1) * u_matrix).xy; + vec2 position = (in_pos * scale) + translation; vec2 zeroToOne = position / u_resolution; vec2 zeroToTwo = zeroToOne * 2.0; vec2 clipSpace = zeroToTwo - 1.0; - gl_Position = vec4(clipSpace * vec2(1, 1), 0, 1); - v_texcoord = a_texcoord; + gl_Position = vec4(clipSpace, 0, 1); + v_color = hsb2rgb(vec3(color, 1.0, 1.0)); } diff --git a/src/wasm_ttd.zig b/src/wasm_ttd.zig index 03d5941..78b65e7 100644 --- a/src/wasm_ttd.zig +++ b/src/wasm_ttd.zig @@ -1,6 +1,8 @@ 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; @@ -27,48 +29,15 @@ const pi = 3.1415926535; var prog: webgl.Program = undefined; var pos_attr_location: i32 = undefined; -var tex_attr_location: i32 = undefined; +var col_attr_location: i32 = undefined; +var translation_attr_location: i32 = undefined; +var scale_attr_location: i32 = undefined; var res_uniform_location: i32 = undefined; -var color_uniform_location: i32 = undefined; -var mat_uniform_location: i32 = undefined; -var tex_uniform_location: i32 = undefined; var pos_buffer: webgl.Buffer = undefined; -var tex_buffer: webgl.Buffer = undefined; -var tex_info: LoadTextureInfo = .{}; +var col_buffer: webgl.Buffer = undefined; +var translation_buffer: webgl.Buffer = undefined; +var scale_buffer: webgl.Buffer = undefined; var vao: webgl.VertexArrayObject = undefined; -var tex: webgl.Texture = undefined; - -const Texture = struct { - tex: webgl.Texture, - width: u32 = 0, - height: u32 = 0, - location: i32 = 0, - - fn draw(self: Texture, x: f32, y: f32) void { - webgl.useProgram(prog); - webgl.bindBuffer(.array_buffer, pos_buffer); - webgl.bufferData(.array_buffer, &square_verts, square_verts.len, .dynamic_draw); - webgl.uniform1i(tex_uniform_location, self.location); - - webgl.uniform2f(res_uniform_location, 1280, 720); - webgl.uniform4f(color_uniform_location, Color.white.r, Color.white.g, Color.white.b, Color.white.a); - - const mat = Mat3.multiply(Mat3.scale(@floatFromInt(self.width), @floatFromInt(self.height)), Mat3.translation(.{ x, y })); - webgl.uniformMatrix3fv(mat_uniform_location, &mat.data); - webgl.drawArrays(.triangles, 0, 6); - } -}; - -var jellybee: Texture = .{ .tex = undefined, .location = 1 }; - -const square_verts = [_]f32{ - 0, 0, - 1, 0, - 0, 1, - 0, 1, - 1, 0, - 1, 1, -}; var timer: f32 = 0.0; var rects: [128]Rectangle = undefined; @@ -152,15 +121,14 @@ const Star = struct { pdist: f32, angle: f32, initial_len: f32, - col: Color, + hue: f32, }; const max_dist = 100; var rng: LinearCongruentialGenerator = undefined; -const star_count = 1000; +const star_count = 30000; var stars: [star_count]Star = undefined; -var accounted_for_degs: [360]bool = [1]bool{false} ** 360; var right: u32 = 0; var pright: u32 = 0; @@ -171,13 +139,14 @@ var pup: u32 = 0; var down: u32 = 0; var pdown: u32 = 0; +const circle = circlePoints(32); + export fn init() void { const f_seed = rand() * (pow(f32, 2, 16) + 1); const seed: u64 = @intFromFloat(f_seed); rng = LinearCongruentialGenerator.ZX81(seed); const width = getScreenWidth(); - _ = width; const height = getScreenHeight(); for (&stars) |*s| { @@ -185,69 +154,63 @@ export fn init() void { s.initial_len = lerp(0, height / 2, rng.randFloat(f32)); s.dist = max_dist; s.pdist = max_dist; - s.col = .{ - .r = rng.randFloat(f32), - .g = rng.randFloat(f32), - .b = rng.randFloat(f32), - }; + s.hue = rng.randFloat(f32); } - //const msg = "Welcome to TBD"; - //consoleLog(msg.ptr, msg.len); const vert = loadShader(.vertex_shader, vs_source) catch return; const frag = loadShader(.fragment_shader, fs_source) catch return; prog = createGLProgram(vert, frag) catch return; webgl.useProgram(prog); - const attr_name = "a_position"; + const attr_name = "in_pos"; pos_attr_location = webgl.getAttribLocation(prog, attr_name.ptr, attr_name.len); - res_uniform_location = getUniformLocation(prog, "u_resolution") catch unreachable; - //tex_uniform_location = getUniformLocation(prog, "u_texture") catch unreachable; - color_uniform_location = getUniformLocation(prog, "u_color") catch unreachable; - mat_uniform_location = getUniformLocation(prog, "u_matrix") catch unreachable; - //webgl.viewport(0, 0, @intFromFloat(width), @intFromFloat(height)); + const col_attr = "color"; + col_attr_location = webgl.getAttribLocation(prog, col_attr.ptr, col_attr.len); + + const translation_attr = "translation"; + translation_attr_location = webgl.getAttribLocation( + prog, + translation_attr.ptr, + translation_attr.len, + ); + + const scale_attr = "scale"; + scale_attr_location = webgl.getAttribLocation( + prog, + scale_attr.ptr, + scale_attr.len, + ); + + res_uniform_location = getUniformLocation(prog, "u_resolution") catch unreachable; + webgl.uniform2f(res_uniform_location, width, height); pos_buffer = webgl.createBuffer(); webgl.bindBuffer(.array_buffer, pos_buffer); + webgl.bufferData(.array_buffer, @ptrCast(&circle), circle.len * 2, .static_draw); vao = webgl.createVertexArray(); webgl.bindVertexArray(vao); webgl.enableVertexAttribArray(pos_attr_location); webgl.vertexAttribPointer(@intCast(pos_attr_location), 2, .f32, .false, 0, 0); - //for (&rects, &cols) |*r, *c| { - // const x = lerp(100, 1180, rand()); - // const y = lerp(100, 620, rand()); - // const w = lerp(100, 200, rand()); - // const h = lerp(100, 200, rand()); - // r.pos = .{ x, y }; - // r.size = .{ w, h }; - // c.* = .{ rand(), rand(), rand() }; - //} - - webgl.activeTexture(webgl.texture_0); - tex = webgl.createTexture(); - webgl.bindTexture(.texture_2d, tex); - - const px = [4]u8{ 255, 255, 255, 255 }; - webgl.texImage2D(.texture_2d, 0, .rgba, 1, 1, 0, .rgba, .u8, &px, px.len, 0); - - //webgl.activeTexture(webgl.texture_0 + 1); - //jellybee.tex = webgl.createTexture(); - //webgl.bindTexture(.texture_2d, jellybee.tex); + 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); - //webgl.texImage2D(.texture_2d, 0, .rgba, 1, 1, 0, .rgba, .u8, &px, px.len, 0); - //const p = "assets/jellybee.png"; - //loadTexture(p.ptr, p.len, jellybee.tex, &tex_info); + 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 t_attr = "a_texcoord"; - //tex_attr_location = webgl.getAttribLocation(prog, t_attr.ptr, t_attr.len); - //tex_buffer = webgl.createBuffer(); - //webgl.bindBuffer(.array_buffer, tex_buffer); - //webgl.bufferData(.array_buffer, &square_verts, square_verts.len, .static_draw); - //webgl.enableVertexAttribArray(tex_attr_location); - //webgl.vertexAttribPointer(@intCast(tex_attr_location), 2, .f32, .true, 0, 0); + 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); registerKey(.up, &up); registerKey(.right, &right); @@ -278,7 +241,40 @@ fn rad2deg(rads: f32) f32 { var frame_counter: u32 = 0; var speed: f32 = 0.1; -var sub_count: u32 = star_count / 2; +var sub_count: u32 = star_count; + +const batch_capacity = 90000; +var translation_data: [batch_capacity][2]f32 = undefined; +var scale_data: [batch_capacity]f32 = undefined; +var color_data: [batch_capacity]f32 = undefined; + +const DrawBatch = struct { + translation: [][2]f32, + scales: []f32, + colors: []f32, + len: u32 = 0, + + fn addInstance(self: *DrawBatch, 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) return error.OutOfSpace; + + self.translation[self.len] = translation; + self.colors[self.len] = hue; + self.scales[self.len] = scale; + self.len += 1; + } + + fn draw(self: DrawBatch) void { + webgl.bindBuffer(.array_buffer, translation_buffer); + webgl.bufferData(.array_buffer, @ptrCast(self.translation), self.len * 2, .dynamic_draw); + + webgl.bindBuffer(.array_buffer, scale_buffer); + webgl.bufferData(.array_buffer, @ptrCast(self.scales), self.len, .dynamic_draw); + + webgl.bindBuffer(.array_buffer, col_buffer); + webgl.bufferData(.array_buffer, @ptrCast(self.colors), self.len, .dynamic_draw); + webgl.drawArraysInstanced(.triangles, 0, circle.len, @intCast(self.len)); + } +}; export fn update(elapsed_time: f32) void { const report_freq = 100; @@ -288,19 +284,6 @@ export fn update(elapsed_time: f32) void { const avg = timer / report_freq; formatLog("{d:.2} Average FPS", .{1.0 / avg}); timer = 0; - - for (&stars) |s| { - const deg = @round(rad2deg(s.angle)); - const i: u32 = @intFromFloat(@min(deg, 359)); - accounted_for_degs[i] = true; - } - - var count: u32 = 0; - for (&accounted_for_degs) |a| { - if (!a) count += 1; - } - - formatLog("{d} unseen degrees", .{count}); } //while (timer >= 2 * pi) { // timer -= 2 * pi; @@ -308,8 +291,6 @@ export fn update(elapsed_time: f32) void { const right_pressed = right != 0 and pright == 0; const left_pressed = left != 0 and pleft == 0; - const up_pressed = up != 0 and pup == 0; - const down_pressed = down != 0 and pdown == 0; if (right_pressed) { speed += 0.1; @@ -319,13 +300,13 @@ export fn update(elapsed_time: f32) void { } speed = @min(1.0, @max(speed, -1.0)); - if (up_pressed) { + if (up != 0) { if (sub_count < star_count) { sub_count += 100; } } - if (down_pressed) { + if (down != 0) { if (sub_count > 0) { sub_count -= 100; } @@ -337,11 +318,19 @@ export fn update(elapsed_time: f32) void { pdown = down; webgl.viewport(0, 0, 1280, 720); + webgl.useProgram(prog); + webgl.bindVertexArray(vao); webgl.uniform2f(res_uniform_location, 1280, 720); - webgl.clearColor(0.1, 0.1, 0.1, 1.0); + webgl.clearColor(0.0, 0.0, 0.0, 1.0); webgl.clear(webgl.color_buffer_bit); + var batch: DrawBatch = .{ + .translation = &translation_data, + .colors = &color_data, + .scales = &scale_data, + }; + const width = getScreenWidth(); const height = getScreenHeight(); for (stars[0..sub_count]) |*s| { @@ -349,15 +338,20 @@ export fn update(elapsed_time: f32) void { s.dist -= speed; const plen = lerp(1000, s.initial_len, s.pdist / max_dist); const psx = (@cos(s.angle) * plen) + width * 0.5; + _ = psx; const psy = (@sin(s.angle) * plen) + height * 0.5; + _ = psy; 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(8, 0, s.dist / max_dist); - drawLine(&.{ .{ psx, psy }, .{ sx, sy } }, s.col); - drawCircle(.{ sx, sy }, scale, s.col); + batch.addInstance(.{ sx, sy }, s.hue, scale) catch unreachable; + if (batch.len == batch_capacity) { + batch.draw(); + batch.len = 0; + } if (sx < 0 or sx > 1280 or sy < 0 or sy > height) { s.angle = lerp(0, 2 * pi, rng.randFloat(f32)); @@ -366,9 +360,9 @@ export fn update(elapsed_time: f32) void { s.pdist = max_dist; } } -} -const Mat3 = @import("matrix.zig").Matrix(f32, 3, 3); + batch.draw(); +} fn lerp(a: f32, b: f32, v: f32) f32 { return a + (b - a) * v; @@ -397,23 +391,9 @@ const Color = struct { const white: Color = .{ .r = 1.0, .g = 1.0, .b = 1.0, .a = 1.0 }; }; -fn setDrawColor(col: Color) void { - webgl.uniform4f(color_uniform_location, col.r, col.g, col.b, col.a); -} - -fn drawCircle(pos: @Vector(2, f32), radius: f32, col: Color) void { - webgl.uniform4f(color_uniform_location, col.r, col.g, col.b, col.a); - webgl.uniform1i(tex_uniform_location, 0); - const segments = 32; +fn circlePoints(comptime segments: u32) [segments * 3][2]f32 { const segment_rads = (2 * pi) / @as(comptime_float, segments); - - const mat = Mat3.multiply(Mat3.scale(radius, radius), Mat3.translation(pos)); - webgl.uniformMatrix3fv(mat_uniform_location, &mat.data); - const center: @Vector(2, f32) = @splat(0); - - webgl.bindBuffer(.array_buffer, pos_buffer); - var points: [segments * 3][2]f32 = undefined; - var tris: TriangleGroup = .{ .points = &points }; + var r: [segments * 3][2]f32 = undefined; for (0..segments) |s| { const sf: f32 = @floatFromInt(s); const start_angle = segment_rads * sf; @@ -426,107 +406,15 @@ fn drawCircle(pos: @Vector(2, f32), radius: f32, col: Color) void { const end_y = @sin(end_angle); const end: @Vector(2, f32) = .{ end_x, end_y }; - tris.addTriangle(&.{ center, start, end }) catch unreachable; - } - tris.draw(); -} - -const TriangleGroup = struct { - points: [][2]f32 = &.{}, - len: usize = 0, - - fn addTriangle(self: *TriangleGroup, points: *const [3][2]f32) !void { - if (self.len + 3 > self.points.len) return error.NoMoreSpace; - - @memcpy(self.points[self.len .. self.len + 3], points); - self.len += 3; - } - - fn draw(self: *const TriangleGroup) void { - webgl.bufferData(.array_buffer, @ptrCast(self.points), self.len * 3, .dynamic_draw); - - webgl.drawArrays(.triangles, 0, @intCast(self.len)); - } -}; - -fn drawLine(points: *const [2][2]f32, col: Color) void { - setDrawColor(col); - webgl.uniformMatrix3fv(mat_uniform_location, &Mat3.identity.data); - webgl.bufferData(.array_buffer, @ptrCast(points), 4, .dynamic_draw); - - webgl.drawArrays(.lines, 0, 2); -} - -fn drawTri(points: *const [3][2]f32) void { - //webgl.useProgram(prog); - //webgl.uniform1i(tex_uniform_location, 0); - webgl.bufferData(.array_buffer, @ptrCast(points), 6, .dynamic_draw); - - webgl.drawArrays(.triangles, 0, 3); -} - -fn drawRectangle(rec: Rectangle, col: ?Color) void { - webgl.uniform1i(tex_uniform_location, 0); - if (col) |c| { - setDrawColor(c); + const i = s * 3; + r[i] = @Vector(2, f32){ 0, 0 }; + r[i + 1] = start; + r[i + 2] = end; } - webgl.useProgram(prog); - webgl.bindBuffer(.array_buffer, pos_buffer); - webgl.bufferData(.array_buffer, &square_verts, square_verts.len, .dynamic_draw); - webgl.uniform2f(res_uniform_location, 1280, 720); - - const mat = Mat3.multiply(Mat3.scale(rec.size[0], rec.size[1]), Mat3.translation(rec.pos)); - webgl.uniformMatrix3fv(mat_uniform_location, &mat.data); - webgl.drawArrays(.triangles, 0, 6); + return r; } -fn drawRectangleLines(rec: Rectangle, col: ?Color) void { - if (col) |c| { - setDrawColor(c); - } - const t: Rectangle = .{ - .pos = rec.pos + @Vector(2, f32){ 0, rec.size[1] }, - .size = .{ rec.size[0], 1 }, - }; - const b: Rectangle = .{ .pos = rec.pos, .size = .{ rec.size[0], 1 } }; - const l: Rectangle = .{ .pos = rec.pos, .size = .{ 1, rec.size[1] } }; - const r: Rectangle = .{ - .pos = rec.pos + @Vector(2, f32){ rec.size[0], 0 }, - .size = .{ 1, rec.size[1] }, - }; - drawRectangle(t, null); - drawRectangle(b, null); - drawRectangle(l, null); - drawRectangle(r, null); -} - -const f_vertices = [_]f32{ - // left column - 0, 0, - 30, 0, - 0, 150, - 0, 150, - 30, 0, - 30, 150, - - // top rung - 30, 0, - 100, 0, - 30, 30, - 30, 30, - 100, 0, - 100, 30, - - // middle rung - 30, 60, - 67, 60, - 30, 90, - 30, 90, - 67, 60, - 67, 90, -}; - fn getUniformLocation(program: webgl.Program, name: []const u8) !i32 { const loc = webgl.getUniformLocation(program, name.ptr, name.len); if (loc < 0) return error.FailedToGetLocation; diff --git a/src/webgl.js b/src/webgl.js index cce477a..7df5be8 100644 --- a/src/webgl.js +++ b/src/webgl.js @@ -39,6 +39,9 @@ const bindBuffer = (target, 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); }; @@ -192,6 +195,14 @@ 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; }; @@ -241,6 +252,9 @@ const webgl = { texImage2D, activeTexture, + vertexAttribDivisor, + drawArraysInstanced, + getScreenWidth, getScreenHeight, }; diff --git a/src/webgl.zig b/src/webgl.zig index a3e1ec3..4a1658d 100644 --- a/src/webgl.zig +++ b/src/webgl.zig @@ -141,3 +141,11 @@ pub extern fn texImage2D( 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; |