three-text 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE_THIRD_PARTY +15 -0
- package/README.md +73 -42
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/three/react.d.ts +2 -0
- package/dist/types/core/types.d.ts +2 -33
- package/dist/types/vector/{core.d.ts → core/index.d.ts} +8 -7
- package/dist/types/vector/index.d.ts +22 -25
- package/dist/types/vector/react.d.ts +4 -3
- package/dist/types/vector/slug/SlugPacker.d.ts +2 -0
- package/dist/types/vector/slug/curveUtils.d.ts +6 -0
- package/dist/types/vector/slug/index.d.ts +8 -0
- package/dist/types/vector/slug/shaderStrings.d.ts +4 -0
- package/dist/types/vector/slug/slugGLSL.d.ts +21 -0
- package/dist/types/vector/slug/slugTSL.d.ts +13 -0
- package/dist/types/vector/slug/types.d.ts +30 -0
- package/dist/types/vector/slug/unpackVertices.d.ts +11 -0
- package/dist/types/vector/webgl/index.d.ts +7 -3
- package/dist/types/vector/webgpu/index.d.ts +4 -4
- package/dist/vector/core/index.cjs +856 -0
- package/dist/vector/core/index.d.ts +63 -0
- package/dist/vector/core/index.js +854 -0
- package/dist/vector/core.cjs +4419 -240
- package/dist/vector/core.d.ts +361 -71
- package/dist/vector/core.js +4406 -226
- package/dist/vector/index.cjs +5 -229
- package/dist/vector/index.d.ts +45 -396
- package/dist/vector/index.js +3 -223
- package/dist/vector/index2.cjs +287 -0
- package/dist/vector/index2.js +264 -0
- package/dist/vector/react.cjs +37 -8
- package/dist/vector/react.d.ts +6 -3
- package/dist/vector/react.js +18 -8
- package/dist/vector/slugTSL.cjs +252 -0
- package/dist/vector/slugTSL.js +231 -0
- package/dist/vector/webgl/index.cjs +131 -201
- package/dist/vector/webgl/index.d.ts +19 -44
- package/dist/vector/webgl/index.js +131 -201
- package/dist/vector/webgpu/index.cjs +100 -283
- package/dist/vector/webgpu/index.d.ts +16 -45
- package/dist/vector/webgpu/index.js +100 -283
- package/package.json +6 -1
- package/dist/types/vector/GlyphVectorGeometryBuilder.d.ts +0 -26
- package/dist/types/vector/LoopBlinnGeometry.d.ts +0 -68
- package/dist/types/vector/loopBlinnTSL.d.ts +0 -22
- package/dist/vector/loopBlinnTSL.cjs +0 -229
- package/dist/vector/loopBlinnTSL.js +0 -207
|
@@ -1,227 +1,157 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
var vertGLSL = "#version 300 es\n// GLSL 300 es port of the Slug vertex shader\n// Eric Lengyel, MIT License, 2017\n\nprecision highp float;\nprecision highp int;\n\n// Per-vertex attributes (5 x vec4, matching Slug reference layout)\nlayout(location = 0) in vec4 a_pos; // .xy = object-space position, .zw = outward normal\nlayout(location = 1) in vec4 a_tex; // .xy = em-space sample coords, .z = packed glyph loc, .w = packed band max + flags\nlayout(location = 2) in vec4 a_jac; // inverse Jacobian (2x2): (j00, j01, j10, j11)\nlayout(location = 3) in vec4 a_bnd; // (bandScaleX, bandScaleY, bandOffsetX, bandOffsetY)\nlayout(location = 4) in vec4 a_col; // vertex color RGBA\n\nuniform mat4 slug_matrix; // MVP matrix (rows as vec4s)\nuniform vec2 slug_viewport; // viewport dimensions in pixels\n\nout vec4 v_color;\nout vec2 v_texcoord;\nflat out vec4 v_banding;\nflat out ivec4 v_glyph;\n\nvoid SlugUnpack(vec4 tex, vec4 bnd, out vec4 vbnd, out ivec4 vgly) {\n uvec2 g = floatBitsToUint(tex.zw);\n vgly = ivec4(g.x & 0xFFFFu, g.x >> 16u, g.y & 0xFFFFu, g.y >> 16u);\n vbnd = bnd;\n}\n\nvec2 SlugDilate(vec4 pos, vec4 tex, vec4 jac, vec4 m0, vec4 m1, vec4 m3, vec2 dim, out vec2 vpos) {\n vec2 n = normalize(pos.zw);\n float s = dot(m3.xy, pos.xy) + m3.w;\n float t = dot(m3.xy, n);\n\n float u = (s * dot(m0.xy, n) - t * (dot(m0.xy, pos.xy) + m0.w)) * dim.x;\n float v = (s * dot(m1.xy, n) - t * (dot(m1.xy, pos.xy) + m1.w)) * dim.y;\n\n float s2 = s * s;\n float st = s * t;\n float uv = u * u + v * v;\n vec2 d = pos.zw * (s2 * (st + sqrt(uv)) / (uv - st * st));\n\n vpos = pos.xy + d;\n return vec2(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\n}\n\nvoid main() {\n vec2 p;\n\n // Dynamic dilation: expand quad by a pixel to prevent edge clipping.\n v_texcoord = SlugDilate(a_pos, a_tex, a_jac,\n slug_matrix[0], slug_matrix[1], slug_matrix[3],\n slug_viewport, p);\n\n // MVP transform on dilated position.\n gl_Position.x = p.x * slug_matrix[0].x + p.y * slug_matrix[0].y + slug_matrix[0].w;\n gl_Position.y = p.x * slug_matrix[1].x + p.y * slug_matrix[1].y + slug_matrix[1].w;\n gl_Position.z = p.x * slug_matrix[2].x + p.y * slug_matrix[2].y + slug_matrix[2].w;\n gl_Position.w = p.x * slug_matrix[3].x + p.y * slug_matrix[3].y + slug_matrix[3].w;\n\n SlugUnpack(a_tex, a_bnd, v_banding, v_glyph);\n v_color = a_col;\n}\n";
|
|
2
|
+
|
|
3
|
+
var fragGLSL = "#version 300 es\n// GLSL 300 es port of the Slug fragment shader\n// Eric Lengyel, MIT License, 2017\n\nprecision highp float;\nprecision highp int;\n\n#define kLogBandTextureWidth 12\n\nin vec4 v_color;\nin vec2 v_texcoord;\nflat in vec4 v_banding;\nflat in ivec4 v_glyph;\n\nuniform sampler2D curveTexture; // RGBA32F control points\nuniform highp usampler2D bandTexture; // RGBA32UI band data\n\nlayout(location = 0) out vec4 outColor;\n\nuint CalcRootCode(float y1, float y2, float y3) {\n uint i1 = floatBitsToUint(y1) >> 31u;\n uint i2 = floatBitsToUint(y2) >> 30u;\n uint i3 = floatBitsToUint(y3) >> 29u;\n\n uint shift = (i2 & 2u) | (i1 & ~2u);\n shift = (i3 & 4u) | (shift & ~4u);\n\n return (0x2E74u >> shift) & 0x0101u;\n}\n\nvec2 SolveHorizPoly(vec4 p12, vec2 p3) {\n vec2 a = p12.xy - p12.zw * 2.0 + p3;\n vec2 b = p12.xy - p12.zw;\n float ra = 1.0 / a.y;\n float rb = 0.5 / b.y;\n\n float d = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\n float t1 = (b.y - d) * ra;\n float t2 = (b.y + d) * ra;\n\n if (abs(a.y) < 1.0 / 65536.0) t1 = t2 = p12.y * rb;\n\n return vec2((a.x * t1 - b.x * 2.0) * t1 + p12.x, (a.x * t2 - b.x * 2.0) * t2 + p12.x);\n}\n\nvec2 SolveVertPoly(vec4 p12, vec2 p3) {\n vec2 a = p12.xy - p12.zw * 2.0 + p3;\n vec2 b = p12.xy - p12.zw;\n float ra = 1.0 / a.x;\n float rb = 0.5 / b.x;\n\n float d = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\n float t1 = (b.x - d) * ra;\n float t2 = (b.x + d) * ra;\n\n if (abs(a.x) < 1.0 / 65536.0) t1 = t2 = p12.x * rb;\n\n return vec2((a.y * t1 - b.y * 2.0) * t1 + p12.y, (a.y * t2 - b.y * 2.0) * t2 + p12.y);\n}\n\nivec2 CalcBandLoc(ivec2 glyphLoc, uint offset) {\n ivec2 bandLoc = ivec2(glyphLoc.x + int(offset), glyphLoc.y);\n bandLoc.y += bandLoc.x >> kLogBandTextureWidth;\n bandLoc.x &= (1 << kLogBandTextureWidth) - 1;\n return bandLoc;\n}\n\nfloat CalcCoverage(float xcov, float ycov, float xwgt, float ywgt, int flags) {\n float coverage = max(abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0), min(abs(xcov), abs(ycov)));\n\n#if defined(SLUG_EVENODD)\n if ((flags & 0x1000) == 0) {\n#endif\n coverage = clamp(coverage, 0.0, 1.0);\n#if defined(SLUG_EVENODD)\n } else {\n coverage = 1.0 - abs(1.0 - fract(coverage * 0.5) * 2.0);\n }\n#endif\n\n#if defined(SLUG_WEIGHT)\n coverage = sqrt(coverage);\n#endif\n\n return coverage;\n}\n\nfloat SlugRenderSingle(vec2 renderCoord, vec2 emsPerPixel, vec4 bandTransform, ivec4 glyphData) {\n int curveIndex;\n\n vec2 pixelsPerEm = 1.0 / emsPerPixel;\n\n ivec2 bandMax = glyphData.zw;\n bandMax.y &= 0x00FF;\n\n ivec2 bandIndex = clamp(ivec2(renderCoord * bandTransform.xy + bandTransform.zw), ivec2(0, 0), bandMax);\n ivec2 glyphLoc = glyphData.xy;\n\n float xcov = 0.0;\n float xwgt = 0.0;\n\n uvec2 hbandData = texelFetch(bandTexture, ivec2(glyphLoc.x + bandIndex.y, glyphLoc.y), 0).xy;\n ivec2 hbandLoc = CalcBandLoc(glyphLoc, hbandData.y);\n\n for (curveIndex = 0; curveIndex < int(hbandData.x); curveIndex++) {\n ivec2 curveLoc = ivec2(texelFetch(bandTexture, ivec2(hbandLoc.x + curveIndex, hbandLoc.y), 0).xy);\n\n vec4 p12 = texelFetch(curveTexture, curveLoc, 0) - vec4(renderCoord, renderCoord);\n vec2 p3 = texelFetch(curveTexture, ivec2(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) break;\n\n uint code = CalcRootCode(p12.y, p12.w, p3.y);\n if (code != 0u) {\n vec2 r = SolveHorizPoly(p12, p3) * pixelsPerEm.x;\n\n if ((code & 1u) != 0u) {\n xcov += clamp(r.x + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n\n if (code > 1u) {\n xcov -= clamp(r.y + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n float ycov = 0.0;\n float ywgt = 0.0;\n\n uvec2 vbandData = texelFetch(bandTexture, ivec2(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0).xy;\n ivec2 vbandLoc = CalcBandLoc(glyphLoc, vbandData.y);\n\n for (curveIndex = 0; curveIndex < int(vbandData.x); curveIndex++) {\n ivec2 curveLoc = ivec2(texelFetch(bandTexture, ivec2(vbandLoc.x + curveIndex, vbandLoc.y), 0).xy);\n vec4 p12 = texelFetch(curveTexture, curveLoc, 0) - vec4(renderCoord, renderCoord);\n vec2 p3 = texelFetch(curveTexture, ivec2(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) break;\n\n uint code = CalcRootCode(p12.x, p12.z, p3.x);\n if (code != 0u) {\n vec2 r = SolveVertPoly(p12, p3) * pixelsPerEm.y;\n\n if ((code & 1u) != 0u) {\n ycov -= clamp(r.x + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n\n if (code > 1u) {\n ycov += clamp(r.y + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n return CalcCoverage(xcov, ycov, xwgt, ywgt, glyphData.w);\n}\n\nfloat SlugRender(vec2 renderCoord, vec4 bandTransform, ivec4 glyphData) {\n vec2 emsPerPixel = fwidth(renderCoord);\n\n#if defined(SLUG_ADAPTIVE_SUPERSAMPLE)\n // Per-pixel rotated RGSS-4. The base RGSS offsets are rotated by a\n // unique angle per fragment (interleaved gradient noise). This converts\n // structured aliasing shimmer into uncorrelated grain that the eye\n // naturally filters out, much closer to how hardware MSAA on many small\n // triangles behaves perceptually.\n float noise = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));\n float angle = noise * 6.2831853;\n float ca = cos(angle), sa = sin(angle);\n\n // Base RGSS offsets rotated by per-pixel angle\n vec2 o0 = vec2(ca * -0.375 - sa * 0.125, sa * -0.375 + ca * 0.125) * emsPerPixel;\n vec2 o1 = vec2(ca * 0.125 - sa * 0.375, sa * 0.125 + ca * 0.375) * emsPerPixel;\n vec2 o2 = vec2(ca * 0.375 - sa * -0.125, sa * 0.375 + ca * -0.125) * emsPerPixel;\n vec2 o3 = vec2(ca * -0.125 - sa * -0.375, sa * -0.125 + ca * -0.375) * emsPerPixel;\n\n float coverage =\n SlugRenderSingle(renderCoord + o0, emsPerPixel, bandTransform, glyphData) +\n SlugRenderSingle(renderCoord + o1, emsPerPixel, bandTransform, glyphData) +\n SlugRenderSingle(renderCoord + o2, emsPerPixel, bandTransform, glyphData) +\n SlugRenderSingle(renderCoord + o3, emsPerPixel, bandTransform, glyphData);\n return coverage * 0.25;\n#else\n return SlugRenderSingle(renderCoord, emsPerPixel, bandTransform, glyphData);\n#endif\n}\n\nvoid main() {\n float coverage = SlugRender(v_texcoord, v_banding, v_glyph);\n outColor = v_color * coverage;\n}\n";
|
|
4
|
+
|
|
5
|
+
// Slug shader source re-exports
|
|
6
|
+
// The .glsl/.wgsl files are the single source of truth, imported as strings
|
|
7
|
+
// at build time via the glslPlugin in rollup.config.js
|
|
8
|
+
// @ts-ignore - resolved by rollup glslPlugin
|
|
9
|
+
const vertexShaderGLSL300 = vertGLSL;
|
|
10
|
+
const fragmentShaderGLSL300 = fragGLSL;
|
|
11
|
+
|
|
12
|
+
// Raw WebGL2 Slug vector text renderer
|
|
13
|
+
// Standalone, no dependency on Three.js
|
|
7
14
|
function compileShader(gl, type, source) {
|
|
8
|
-
const shader =
|
|
15
|
+
const shader = gl.createShader(type);
|
|
16
|
+
if (!shader)
|
|
17
|
+
throw new Error('Failed to create shader');
|
|
9
18
|
gl.shaderSource(shader, source);
|
|
10
19
|
gl.compileShader(shader);
|
|
11
20
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
12
|
-
const info = gl.getShaderInfoLog(shader)
|
|
21
|
+
const info = gl.getShaderInfoLog(shader);
|
|
13
22
|
gl.deleteShader(shader);
|
|
14
|
-
throw new Error(info);
|
|
23
|
+
throw new Error(`Shader compile failed: ${info}`);
|
|
15
24
|
}
|
|
16
25
|
return shader;
|
|
17
26
|
}
|
|
18
|
-
function
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
gl.
|
|
25
|
-
gl.
|
|
26
|
-
gl.
|
|
27
|
-
if (!gl.getProgramParameter(
|
|
28
|
-
const info = gl.getProgramInfoLog(
|
|
29
|
-
gl.deleteProgram(
|
|
30
|
-
throw new Error(info);
|
|
27
|
+
function createProgram(gl, vsSrc, fsSrc) {
|
|
28
|
+
const vs = compileShader(gl, gl.VERTEX_SHADER, vsSrc);
|
|
29
|
+
const fs = compileShader(gl, gl.FRAGMENT_SHADER, fsSrc);
|
|
30
|
+
const prog = gl.createProgram();
|
|
31
|
+
if (!prog)
|
|
32
|
+
throw new Error('Failed to create program');
|
|
33
|
+
gl.attachShader(prog, vs);
|
|
34
|
+
gl.attachShader(prog, fs);
|
|
35
|
+
gl.linkProgram(prog);
|
|
36
|
+
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
|
37
|
+
const info = gl.getProgramInfoLog(prog);
|
|
38
|
+
gl.deleteProgram(prog);
|
|
39
|
+
throw new Error(`Program link failed: ${info}`);
|
|
31
40
|
}
|
|
32
|
-
|
|
41
|
+
gl.deleteShader(vs);
|
|
42
|
+
gl.deleteShader(fs);
|
|
43
|
+
return prog;
|
|
33
44
|
}
|
|
34
|
-
function
|
|
35
|
-
const
|
|
36
|
-
if (!
|
|
37
|
-
throw new Error(
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
function createRGBA32FTexture(gl, data, width, height) {
|
|
46
|
+
const tex = gl.createTexture();
|
|
47
|
+
if (!tex)
|
|
48
|
+
throw new Error('Failed to create texture');
|
|
49
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
50
|
+
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
|
51
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
52
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
53
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
54
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
55
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, width, height, 0, gl.RGBA, gl.FLOAT, data);
|
|
56
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
57
|
+
return tex;
|
|
40
58
|
}
|
|
41
|
-
function
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
function createRGBA32UITexture(gl, data, width, height) {
|
|
60
|
+
const tex = gl.createTexture();
|
|
61
|
+
if (!tex)
|
|
62
|
+
throw new Error('Failed to create texture');
|
|
63
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
64
|
+
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
|
65
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
66
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
67
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
68
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
69
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32UI, width, height, 0, gl.RGBA_INTEGER, gl.UNSIGNED_INT, data);
|
|
70
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
71
|
+
return tex;
|
|
47
72
|
}
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
73
|
+
function createResources(gl, gpuData, fragSrc) {
|
|
74
|
+
gl.getExtension('EXT_color_buffer_float');
|
|
75
|
+
const program = createProgram(gl, vertexShaderGLSL300, fragSrc);
|
|
76
|
+
const uniforms = {
|
|
77
|
+
slug_matrix: gl.getUniformLocation(program, 'slug_matrix'),
|
|
78
|
+
slug_viewport: gl.getUniformLocation(program, 'slug_viewport'),
|
|
79
|
+
curveTexture: gl.getUniformLocation(program, 'curveTexture'),
|
|
80
|
+
bandTexture: gl.getUniformLocation(program, 'bandTexture'),
|
|
54
81
|
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
gl.
|
|
63
|
-
gl.
|
|
64
|
-
|
|
65
|
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, interiorIndexBuffer);
|
|
66
|
-
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data.interiorIndices, gl.STATIC_DRAW);
|
|
67
|
-
const curveVAO = assertCreate(gl.createVertexArray(), 'curve VAO');
|
|
68
|
-
const curvePositionBuffer = assertCreate(gl.createBuffer(), 'curve position buffer');
|
|
69
|
-
gl.bindVertexArray(curveVAO);
|
|
70
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, curvePositionBuffer);
|
|
71
|
-
gl.bufferData(gl.ARRAY_BUFFER, data.curvePositions, gl.STATIC_DRAW);
|
|
72
|
-
gl.enableVertexAttribArray(0);
|
|
73
|
-
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
|
|
74
|
-
const fillVAO = assertCreate(gl.createVertexArray(), 'fill VAO');
|
|
75
|
-
const fillPositionBuffer = assertCreate(gl.createBuffer(), 'fill position buffer');
|
|
76
|
-
const fillIndexBuffer = assertCreate(gl.createBuffer(), 'fill index buffer');
|
|
77
|
-
gl.bindVertexArray(fillVAO);
|
|
78
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, fillPositionBuffer);
|
|
79
|
-
gl.bufferData(gl.ARRAY_BUFFER, data.fillPositions, gl.STATIC_DRAW);
|
|
82
|
+
const vao = gl.createVertexArray();
|
|
83
|
+
if (!vao)
|
|
84
|
+
throw new Error('Failed to create VAO');
|
|
85
|
+
gl.bindVertexArray(vao);
|
|
86
|
+
const vbo = gl.createBuffer();
|
|
87
|
+
if (!vbo)
|
|
88
|
+
throw new Error('Failed to create VBO');
|
|
89
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
90
|
+
gl.bufferData(gl.ARRAY_BUFFER, gpuData.vertices, gl.STATIC_DRAW);
|
|
91
|
+
const stride = 20 * 4;
|
|
80
92
|
gl.enableVertexAttribArray(0);
|
|
81
|
-
gl.vertexAttribPointer(0,
|
|
82
|
-
gl.
|
|
83
|
-
gl.
|
|
93
|
+
gl.vertexAttribPointer(0, 4, gl.FLOAT, false, stride, 0);
|
|
94
|
+
gl.enableVertexAttribArray(1);
|
|
95
|
+
gl.vertexAttribPointer(1, 4, gl.FLOAT, false, stride, 4 * 4);
|
|
96
|
+
gl.enableVertexAttribArray(2);
|
|
97
|
+
gl.vertexAttribPointer(2, 4, gl.FLOAT, false, stride, 8 * 4);
|
|
98
|
+
gl.enableVertexAttribArray(3);
|
|
99
|
+
gl.vertexAttribPointer(3, 4, gl.FLOAT, false, stride, 12 * 4);
|
|
100
|
+
gl.enableVertexAttribArray(4);
|
|
101
|
+
gl.vertexAttribPointer(4, 4, gl.FLOAT, false, stride, 16 * 4);
|
|
102
|
+
const ibo = gl.createBuffer();
|
|
103
|
+
if (!ibo)
|
|
104
|
+
throw new Error('Failed to create IBO');
|
|
105
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
106
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, gpuData.indices, gl.STATIC_DRAW);
|
|
84
107
|
gl.bindVertexArray(null);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
interiorVAO,
|
|
89
|
-
interiorPositionBuffer,
|
|
90
|
-
interiorIndexBuffer,
|
|
91
|
-
interiorIndexCount: data.interiorIndices.length,
|
|
92
|
-
curveVAO,
|
|
93
|
-
curvePositionBuffer,
|
|
94
|
-
curveVertexCount: data.curvePositions.length / 3,
|
|
95
|
-
fillVAO,
|
|
96
|
-
fillPositionBuffer,
|
|
97
|
-
fillIndexBuffer,
|
|
98
|
-
fillIndexCount: data.fillIndices.length
|
|
99
|
-
};
|
|
108
|
+
const curveTexture = createRGBA32FTexture(gl, gpuData.curveTexture.data, gpuData.curveTexture.width, gpuData.curveTexture.height);
|
|
109
|
+
const bandTexture = createRGBA32UITexture(gl, gpuData.bandTexture.data, gpuData.bandTexture.width, gpuData.bandTexture.height);
|
|
110
|
+
return { program, vao, vbo, ibo, curveTexture, bandTexture, uniforms, indexCount: gpuData.indices.length };
|
|
100
111
|
}
|
|
101
|
-
function
|
|
102
|
-
gl.
|
|
103
|
-
gl.
|
|
104
|
-
gl.
|
|
105
|
-
gl.
|
|
106
|
-
gl.
|
|
107
|
-
gl.
|
|
108
|
-
gl.
|
|
109
|
-
gl.
|
|
112
|
+
function draw(gl, res, mvpMatrix, viewportWidth, viewportHeight) {
|
|
113
|
+
gl.useProgram(res.program);
|
|
114
|
+
gl.uniformMatrix4fv(res.uniforms.slug_matrix, false, mvpMatrix);
|
|
115
|
+
gl.uniform2f(res.uniforms.slug_viewport, viewportWidth, viewportHeight);
|
|
116
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
117
|
+
gl.bindTexture(gl.TEXTURE_2D, res.curveTexture);
|
|
118
|
+
gl.uniform1i(res.uniforms.curveTexture, 0);
|
|
119
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
120
|
+
gl.bindTexture(gl.TEXTURE_2D, res.bandTexture);
|
|
121
|
+
gl.uniform1i(res.uniforms.bandTexture, 1);
|
|
122
|
+
gl.bindVertexArray(res.vao);
|
|
123
|
+
gl.enable(gl.BLEND);
|
|
124
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
125
|
+
gl.drawElements(gl.TRIANGLES, res.indexCount, gl.UNSIGNED_SHORT, 0);
|
|
126
|
+
gl.bindVertexArray(null);
|
|
110
127
|
}
|
|
111
|
-
function createWebGLVectorRenderer(gl) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
gl_Position = u_mvp * vec4(a_position, 1.0);
|
|
117
|
-
}`;
|
|
118
|
-
const interiorFragmentShader = `#version 300 es
|
|
119
|
-
precision highp float;
|
|
120
|
-
out vec4 outColor;
|
|
121
|
-
void main() {
|
|
122
|
-
outColor = vec4(1.0);
|
|
123
|
-
}`;
|
|
124
|
-
const curveVertexShader = `#version 300 es
|
|
125
|
-
layout(location = 0) in vec3 a_position;
|
|
126
|
-
uniform mat4 u_mvp;
|
|
127
|
-
out vec2 v_uv;
|
|
128
|
-
void main() {
|
|
129
|
-
int localVertex = gl_VertexID % 3;
|
|
130
|
-
float u = float(localVertex) * 0.5;
|
|
131
|
-
v_uv = vec2(u, floor(u));
|
|
132
|
-
gl_Position = u_mvp * vec4(a_position, 1.0);
|
|
133
|
-
}`;
|
|
134
|
-
const curveFragmentShader = `#version 300 es
|
|
135
|
-
precision highp float;
|
|
136
|
-
in vec2 v_uv;
|
|
137
|
-
out vec4 outColor;
|
|
138
|
-
void main() {
|
|
139
|
-
vec2 px = dFdx(v_uv);
|
|
140
|
-
vec2 py = dFdy(v_uv);
|
|
141
|
-
float fx = 2.0 * v_uv.x * px.x - px.y;
|
|
142
|
-
float fy = 2.0 * v_uv.x * py.x - py.y;
|
|
143
|
-
float denom = sqrt(fx * fx + fy * fy);
|
|
144
|
-
if (denom < 1e-6) {
|
|
145
|
-
discard;
|
|
146
|
-
}
|
|
147
|
-
float sd = (v_uv.x * v_uv.x - v_uv.y) / denom;
|
|
148
|
-
float alpha = clamp(0.5 - sd, 0.0, 1.0);
|
|
149
|
-
if (alpha <= 0.0) {
|
|
150
|
-
discard;
|
|
151
|
-
}
|
|
152
|
-
outColor = vec4(1.0, 1.0, 1.0, alpha);
|
|
153
|
-
}`;
|
|
154
|
-
const colorVertexShader = interiorVertexShader;
|
|
155
|
-
const colorFragmentShader = `#version 300 es
|
|
156
|
-
precision highp float;
|
|
157
|
-
uniform vec4 u_color;
|
|
158
|
-
out vec4 outColor;
|
|
159
|
-
void main() {
|
|
160
|
-
outColor = u_color;
|
|
161
|
-
}`;
|
|
162
|
-
const interiorProgram = createProgramWithMvp(gl, interiorVertexShader, interiorFragmentShader);
|
|
163
|
-
const curveProgram = createProgramWithMvp(gl, curveVertexShader, curveFragmentShader);
|
|
164
|
-
const colorProgram = createColorProgram(gl, colorVertexShader, colorFragmentShader);
|
|
165
|
-
let geometryResources = null;
|
|
128
|
+
function createWebGLVectorRenderer(gl, options) {
|
|
129
|
+
let resources = null;
|
|
130
|
+
const fragSrc = options?.adaptiveSupersampling
|
|
131
|
+
? '#define SLUG_ADAPTIVE_SUPERSAMPLE\n' + fragmentShaderGLSL300
|
|
132
|
+
: fragmentShaderGLSL300;
|
|
166
133
|
return {
|
|
167
134
|
setGeometry(data) {
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
geometryResources = createGeometryResources(gl, data);
|
|
135
|
+
if (resources)
|
|
136
|
+
this.dispose();
|
|
137
|
+
resources = createResources(gl, data, fragSrc);
|
|
172
138
|
},
|
|
173
|
-
render(mvp,
|
|
174
|
-
if (!
|
|
139
|
+
render(mvp, _color) {
|
|
140
|
+
if (!resources)
|
|
175
141
|
return;
|
|
176
|
-
|
|
177
|
-
gl.disable(gl.CULL_FACE);
|
|
178
|
-
gl.disable(gl.DEPTH_TEST);
|
|
179
|
-
gl.depthMask(false);
|
|
180
|
-
// No stencil clear needed - the fill pass resets stencil to 0
|
|
181
|
-
// via passOp ZERO wherever stencil was non-zero, and per-glyph
|
|
182
|
-
// fill quads cover all stencil writes from interior/curve passes
|
|
183
|
-
gl.enable(gl.STENCIL_TEST);
|
|
184
|
-
gl.stencilMask(0xff);
|
|
185
|
-
gl.stencilFunc(gl.ALWAYS, 0, 0xff);
|
|
186
|
-
// Nonzero winding: front faces increment, back faces decrement
|
|
187
|
-
gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP);
|
|
188
|
-
gl.stencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP);
|
|
189
|
-
gl.colorMask(false, false, false, false);
|
|
190
|
-
if (geometryResources.interiorIndexCount > 0) {
|
|
191
|
-
gl.useProgram(interiorProgram.program);
|
|
192
|
-
gl.uniformMatrix4fv(interiorProgram.mvp, false, mvp);
|
|
193
|
-
gl.bindVertexArray(geometryResources.interiorVAO);
|
|
194
|
-
gl.drawElements(gl.TRIANGLES, geometryResources.interiorIndexCount, gl.UNSIGNED_INT, 0);
|
|
195
|
-
}
|
|
196
|
-
if (geometryResources.curveVertexCount > 0) {
|
|
197
|
-
gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
198
|
-
gl.useProgram(curveProgram.program);
|
|
199
|
-
gl.uniformMatrix4fv(curveProgram.mvp, false, mvp);
|
|
200
|
-
gl.bindVertexArray(geometryResources.curveVAO);
|
|
201
|
-
gl.drawArrays(gl.TRIANGLES, 0, geometryResources.curveVertexCount);
|
|
202
|
-
gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
203
|
-
}
|
|
204
|
-
gl.stencilFunc(gl.NOTEQUAL, 0, 0xff);
|
|
205
|
-
gl.stencilOp(gl.KEEP, gl.KEEP, gl.ZERO);
|
|
206
|
-
gl.colorMask(true, true, true, true);
|
|
207
|
-
gl.useProgram(colorProgram.program);
|
|
208
|
-
gl.uniformMatrix4fv(colorProgram.mvp, false, mvp);
|
|
209
|
-
gl.uniform4fv(colorProgram.color, color);
|
|
210
|
-
gl.bindVertexArray(geometryResources.fillVAO);
|
|
211
|
-
gl.drawElements(gl.TRIANGLES, geometryResources.fillIndexCount, gl.UNSIGNED_INT, 0);
|
|
212
|
-
gl.bindVertexArray(null);
|
|
213
|
-
gl.useProgram(null);
|
|
214
|
-
gl.disable(gl.STENCIL_TEST);
|
|
215
|
-
gl.depthMask(true);
|
|
142
|
+
draw(gl, resources, mvp, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
|
216
143
|
},
|
|
217
144
|
dispose() {
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
145
|
+
if (!resources)
|
|
146
|
+
return;
|
|
147
|
+
const gl2 = gl;
|
|
148
|
+
gl2.deleteTexture(resources.curveTexture);
|
|
149
|
+
gl2.deleteTexture(resources.bandTexture);
|
|
150
|
+
gl2.deleteBuffer(resources.vbo);
|
|
151
|
+
gl2.deleteBuffer(resources.ibo);
|
|
152
|
+
gl2.deleteVertexArray(resources.vao);
|
|
153
|
+
gl2.deleteProgram(resources.program);
|
|
154
|
+
resources = null;
|
|
225
155
|
}
|
|
226
156
|
};
|
|
227
157
|
}
|