aboutsummaryrefslogtreecommitdiff
path: root/web/pw-frontend/src/lib/visualizer/webgl/text.ts
diff options
context:
space:
mode:
Diffstat (limited to 'web/pw-frontend/src/lib/visualizer/webgl/text.ts')
-rw-r--r--web/pw-frontend/src/lib/visualizer/webgl/text.ts192
1 files changed, 192 insertions, 0 deletions
diff --git a/web/pw-frontend/src/lib/visualizer/webgl/text.ts b/web/pw-frontend/src/lib/visualizer/webgl/text.ts
new file mode 100644
index 0000000..3f1cec6
--- /dev/null
+++ b/web/pw-frontend/src/lib/visualizer/webgl/text.ts
@@ -0,0 +1,192 @@
+import type { Dictionary } from "./util";
+import type { Shader, UniformMatrix3fv } from "./shader";
+import { Texture } from "./texture";
+import { DefaultRenderable } from "./renderer";
+import { IndexBuffer, VertexBuffer } from "./buffer";
+import { VertexBufferLayout, VertexArray } from "./vertexBufferLayout";
+
+
+export enum Align {
+ Begin,
+ End,
+ Middle,
+}
+
+export class GlypInfo {
+ x: number;
+ y: number;
+ width: number;
+}
+
+export class FontInfo {
+ letterHeight: number;
+ spaceWidth: number;
+ spacing: number;
+ textureWidth: number;
+ textureHeight: number;
+ glyphInfos: Dictionary<GlypInfo>;
+}
+
+export class LabelFactory {
+ texture: Texture;
+ font: FontInfo;
+ shader: Shader;
+
+ constructor(gl: WebGLRenderingContext, loc: string, font: FontInfo, shader: Shader) {
+ this.texture = Texture.fromImage(gl, loc, 'font');
+ this.font = font;
+ this.shader = shader;
+ }
+
+ build(gl: WebGLRenderingContext, transform?: UniformMatrix3fv): Label {
+ return new Label(gl, this.shader, this.texture, this.font, transform);
+ }
+}
+
+export class Label {
+ inner: DefaultRenderable;
+
+ font: FontInfo;
+
+ constructor(gl: WebGLRenderingContext, shader: Shader, tex: Texture, font: FontInfo, transform?: UniformMatrix3fv) {
+ this.font = font;
+
+ const uniforms = transform ? { "u_trans": transform, "u_trans_next": transform, } : {};
+ const ib = new IndexBuffer(gl, []);
+ const vb_pos = new VertexBuffer(gl, []);
+ const vb_tex = new VertexBuffer(gl, []);
+
+ const layout_pos = new VertexBufferLayout();
+ layout_pos.push(gl.FLOAT, 2, 4, "a_position");
+
+ const layout_tex = new VertexBufferLayout();
+ layout_tex.push(gl.FLOAT, 2, 4, "a_texCoord");
+
+ const vao = new VertexArray();
+ vao.addBuffer(vb_pos, layout_pos);
+ vao.addBuffer(vb_tex, layout_tex);
+
+ this.inner = new DefaultRenderable(ib, vao, shader, [tex], uniforms);
+ }
+
+ getRenderable(): DefaultRenderable {
+ return this.inner;
+ }
+
+ setText(gl: WebGLRenderingContext, text: string, h_align = Align.Begin, v_align = Align.Begin) {
+ const idxs = [];
+ const verts_pos = [];
+ const verts_tex = [];
+
+ const letterHeight = this.font.letterHeight / this.font.textureHeight;
+ let xPos = 0;
+
+ switch (h_align) {
+ case Align.Begin:
+ break;
+ case Align.End:
+ xPos = -1 * [...text].map(n => this.font.glyphInfos[n] ? this.font.glyphInfos[n].width : this.font.spaceWidth).reduce((a, b) => a + b, 0) / this.font.letterHeight;
+ break;
+ case Align.Middle:
+ xPos = -1 * [...text].map(n => this.font.glyphInfos[n] ? this.font.glyphInfos[n].width : this.font.spaceWidth).reduce((a, b) => a + b, 0) / this.font.letterHeight / 2;
+ break;
+ }
+ let yStart = 0;
+ switch (v_align) {
+ case Align.Begin:
+ break;
+ case Align.End:
+ yStart = 1;
+ break;
+ case Align.Middle:
+ yStart = 0.5;
+ break;
+ }
+
+ let j = 0;
+ for (let i = 0; i < text.length; i++) {
+ const info = this.font.glyphInfos[text[i]];
+ if (info) {
+ const dx = info.width / this.font.letterHeight;
+ const letterWidth = info.width / this.font.textureWidth;
+ const x0 = info.x / this.font.textureWidth;
+ const y0 = info.y / this.font.textureHeight;
+ verts_pos.push(xPos, yStart);
+ verts_pos.push(xPos + dx, yStart);
+ verts_pos.push(xPos, yStart-1);
+ verts_pos.push(xPos + dx, yStart-1);
+
+ verts_tex.push(x0, y0);
+ verts_tex.push(x0 + letterWidth, y0);
+ verts_tex.push(x0, y0 + letterHeight);
+ verts_tex.push(x0 + letterWidth, y0 + letterHeight);
+
+ xPos += dx;
+
+ idxs.push(j+0, j+1, j+2, j+1, j+2, j+3);
+ j += 4;
+ } else {
+ // Just move xPos
+ xPos += this.font.spaceWidth / this.font.letterHeight;
+ }
+ }
+
+ this.inner.updateIndexBuffer(gl, idxs);
+ this.inner.updateVAOBuffer(gl, 0, verts_pos);
+ this.inner.updateVAOBuffer(gl, 1, verts_tex);
+ }
+}
+
+export function defaultLabelFactory(gl: WebGLRenderingContext, shader: Shader): LabelFactory {
+ const fontInfo = {
+ letterHeight: 8,
+ spaceWidth: 8,
+ spacing: -1,
+ textureWidth: 64,
+ textureHeight: 40,
+ glyphInfos: {
+ 'a': { x: 0, y: 0, width: 8, },
+ 'b': { x: 8, y: 0, width: 8, },
+ 'c': { x: 16, y: 0, width: 8, },
+ 'd': { x: 24, y: 0, width: 8, },
+ 'e': { x: 32, y: 0, width: 8, },
+ 'f': { x: 40, y: 0, width: 8, },
+ 'g': { x: 48, y: 0, width: 8, },
+ 'h': { x: 56, y: 0, width: 8, },
+ 'i': { x: 0, y: 8, width: 8, },
+ 'j': { x: 8, y: 8, width: 8, },
+ 'k': { x: 16, y: 8, width: 8, },
+ 'l': { x: 24, y: 8, width: 8, },
+ 'm': { x: 32, y: 8, width: 8, },
+ 'n': { x: 40, y: 8, width: 8, },
+ 'o': { x: 48, y: 8, width: 8, },
+ 'p': { x: 56, y: 8, width: 8, },
+ 'q': { x: 0, y: 16, width: 8, },
+ 'r': { x: 8, y: 16, width: 8, },
+ 's': { x: 16, y: 16, width: 8, },
+ 't': { x: 24, y: 16, width: 8, },
+ 'u': { x: 32, y: 16, width: 8, },
+ 'v': { x: 40, y: 16, width: 8, },
+ 'w': { x: 48, y: 16, width: 8, },
+ 'x': { x: 56, y: 16, width: 8, },
+ 'y': { x: 0, y: 24, width: 8, },
+ 'z': { x: 8, y: 24, width: 8, },
+ '0': { x: 16, y: 24, width: 8, },
+ '1': { x: 24, y: 24, width: 8, },
+ '2': { x: 32, y: 24, width: 8, },
+ '3': { x: 40, y: 24, width: 8, },
+ '4': { x: 48, y: 24, width: 8, },
+ '5': { x: 56, y: 24, width: 8, },
+ '6': { x: 0, y: 32, width: 8, },
+ '7': { x: 8, y: 32, width: 8, },
+ '8': { x: 16, y: 32, width: 8, },
+ '9': { x: 24, y: 32, width: 8, },
+ '-': { x: 32, y: 32, width: 8, },
+ '*': { x: 40, y: 32, width: 8, },
+ '!': { x: 48, y: 32, width: 8, },
+ '?': { x: 56, y: 32, width: 8, },
+ },
+ };
+
+ return new LabelFactory(gl, '/static/res/assets/font.png', fontInfo, shader);
+}