diff options
author | Ilion Beyst <ilion.beyst@gmail.com> | 2022-11-06 17:43:00 +0100 |
---|---|---|
committer | Ilion Beyst <ilion.beyst@gmail.com> | 2022-11-06 18:07:16 +0100 |
commit | fdc2ab9421c6a38c6bbd9b621c4da4f2c147a773 (patch) | |
tree | 50466d9b751f1e1eb3ee8a47479a4d28b5050aa6 /web/pw-visualizer/src/webgl/msdf_text.ts | |
parent | d3845eb85fc8f75562a6e121c5e437763ffeea63 (diff) | |
download | planetwars.dev-fdc2ab9421c6a38c6bbd9b621c4da4f2c147a773.tar.xz planetwars.dev-fdc2ab9421c6a38c6bbd9b621c4da4f2c147a773.zip |
msdf font renderen
Diffstat (limited to 'web/pw-visualizer/src/webgl/msdf_text.ts')
-rw-r--r-- | web/pw-visualizer/src/webgl/msdf_text.ts | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/web/pw-visualizer/src/webgl/msdf_text.ts b/web/pw-visualizer/src/webgl/msdf_text.ts new file mode 100644 index 0000000..5bbac20 --- /dev/null +++ b/web/pw-visualizer/src/webgl/msdf_text.ts @@ -0,0 +1,181 @@ +import { Shader, Uniform1f, Uniform4f, UniformMatrix3fv } from "./shader"; +import { Texture } from "./texture"; +import { DefaultRenderable } from "./renderer"; +import { IndexBuffer, VertexBuffer } from "./buffer"; +import { VertexBufferLayout, VertexArray } from "./vertexBufferLayout"; +import { robotoMsdfJson } from "../assets"; +import { GlypInfo } from "./text"; + + +export enum Align { + Begin, + End, + Middle, +} + +export type FontAtlas = { + atlas: AtlasMeta, + metrics: Metrics, + glyphs: Glyph[], +} + +export type AtlasMeta = { + type: string, + distanceRange: number, + size: number, + width: number, + height: number, + yOrigin: string, +} + +export type Metrics = { + emSize: number, + lineHeight: number, + ascender: number, + descender: number, + underlineY: number, + underlineThickness: number, +} + + +export type Glyph = { + unicode: number, + advance: number, + planeBounds?: Bounds, + atlasBounds?: Bounds, +} + +export type Bounds = { + left: number, + bottom: number, + right: number, + top: number, +} + + +export class MsdfLabelFactory { + texture: Texture; + font: FontAtlas; + shader: Shader; + + constructor(gl: WebGLRenderingContext, fontTexture: Texture, font: FontAtlas, shader: Shader) { + this.texture = fontTexture; + 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: FontAtlas; + charAtlas: {[unicodeNumber: number]: Glyph}; + + constructor(gl: WebGLRenderingContext, shader: Shader, tex: Texture, font: FontAtlas, transform: UniformMatrix3fv) { + this.font = font; + this.charAtlas = {} + this.font.glyphs.forEach((glyph) => { + this.charAtlas[glyph.unicode] = glyph; + }); + + const uniforms = { + "u_trans": transform, + "u_trans_next": transform, + "u_fgColor": new Uniform4f([1.0, 1.0, 1.0, 1.0]), + "u_bgColor": new Uniform4f([0.0, 0.0, 0.0, 1.0]), + "u_distanceRange": new Uniform1f(font.atlas.distanceRange), + "u_glyphSize": new Uniform1f(font.atlas.size), + }; + 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 = []; + + let xPos = 0; + let yPos = 0; + switch (v_align) { + case Align.Begin: + yPos = -1; + break; + case Align.End: + yPos = 0; + break; + case Align.Middle: + yPos = -0.5; + break; + } + + // track position in the index buffer + let bufPos = 0; + for (let charIndex = 0; charIndex < text.length; charIndex++) { + let char = this.charAtlas[text.charCodeAt(charIndex)] + if (char.atlasBounds && char.planeBounds) { + verts_pos.push(xPos + char.planeBounds.left, yPos-char.planeBounds.top); + verts_pos.push(xPos + char.planeBounds.right, yPos-char.planeBounds.top); + verts_pos.push(xPos + char.planeBounds.left, yPos-char.planeBounds.bottom); + verts_pos.push(xPos + char.planeBounds.right, yPos-char.planeBounds.bottom); + + const atlasWidth = this.font.atlas.width; + const atlasHeight = this.font.atlas.height; + + verts_tex.push(char.atlasBounds.left / atlasWidth, char.atlasBounds.top / atlasHeight); + verts_tex.push(char.atlasBounds.right / atlasWidth, char.atlasBounds.top / atlasHeight); + verts_tex.push(char.atlasBounds.left / atlasWidth, char.atlasBounds.bottom / atlasHeight); + verts_tex.push(char.atlasBounds.right / atlasWidth, char.atlasBounds.bottom / atlasHeight); + + idxs.push(bufPos+0, bufPos+1, bufPos+2); + idxs.push(bufPos+1, bufPos+2, bufPos+3); + bufPos += 4; + } + xPos += char.advance; + } + + let shift = 0; + switch (h_align) { + case Align.End: + shift = xPos; + break; + case Align.Middle: + shift = xPos / 2; + break; + } + + for (let i = 0; i < verts_pos.length; i += 2) { + verts_pos[i] -= shift; + } + + + this.inner.updateIndexBuffer(gl, idxs); + this.inner.updateVAOBuffer(gl, 0, verts_pos); + this.inner.updateVAOBuffer(gl, 1, verts_tex); + } +} + +export function defaultMsdfLabelFactory(gl: WebGLRenderingContext, fontTexture: Texture, shader: Shader): MsdfLabelFactory { + return new MsdfLabelFactory(gl, fontTexture, robotoMsdfJson, shader); +} |