three-text 0.5.1 → 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.
Files changed (61) hide show
  1. package/LICENSE_THIRD_PARTY +15 -0
  2. package/README.md +80 -50
  3. package/dist/index.cjs +66 -20
  4. package/dist/index.d.ts +8 -0
  5. package/dist/index.js +66 -20
  6. package/dist/index.min.cjs +310 -307
  7. package/dist/index.min.js +265 -262
  8. package/dist/index.umd.js +68 -21
  9. package/dist/index.umd.min.js +268 -265
  10. package/dist/three/index.cjs +2 -1
  11. package/dist/three/index.d.ts +1 -0
  12. package/dist/three/index.js +2 -1
  13. package/dist/three/react.cjs +35 -17
  14. package/dist/three/react.d.ts +8 -0
  15. package/dist/three/react.js +35 -17
  16. package/dist/types/core/Text.d.ts +6 -0
  17. package/dist/types/core/types.d.ts +2 -33
  18. package/dist/types/three/index.d.ts +1 -0
  19. package/dist/types/vector/core/index.d.ts +28 -0
  20. package/dist/types/vector/index.d.ts +17 -12
  21. package/dist/types/vector/react.d.ts +3 -4
  22. package/dist/types/vector/slug/SlugPacker.d.ts +2 -0
  23. package/dist/types/vector/slug/curveUtils.d.ts +6 -0
  24. package/dist/types/vector/slug/index.d.ts +8 -0
  25. package/dist/types/vector/slug/shaderStrings.d.ts +4 -0
  26. package/dist/types/vector/slug/slugGLSL.d.ts +21 -0
  27. package/dist/types/vector/slug/slugTSL.d.ts +13 -0
  28. package/dist/types/vector/slug/types.d.ts +30 -0
  29. package/dist/types/vector/slug/unpackVertices.d.ts +11 -0
  30. package/dist/types/vector/webgl/index.d.ts +7 -3
  31. package/dist/types/vector/webgpu/index.d.ts +4 -4
  32. package/dist/vector/all.cjs +21 -0
  33. package/dist/vector/all.d.ts +134 -0
  34. package/dist/vector/all.js +2 -0
  35. package/dist/vector/core/index.cjs +856 -0
  36. package/dist/vector/core/index.d.ts +63 -0
  37. package/dist/vector/core/index.js +854 -0
  38. package/dist/vector/core.cjs +5489 -0
  39. package/dist/vector/core.d.ts +402 -0
  40. package/dist/vector/core.js +5486 -0
  41. package/dist/vector/index.cjs +5 -1305
  42. package/dist/vector/index.d.ts +41 -67
  43. package/dist/vector/index.js +3 -1306
  44. package/dist/vector/index2.cjs +287 -0
  45. package/dist/vector/index2.js +264 -0
  46. package/dist/vector/loopBlinnTSL.d.ts +69 -0
  47. package/dist/vector/react.cjs +54 -40
  48. package/dist/vector/react.d.ts +11 -2
  49. package/dist/vector/react.js +55 -41
  50. package/dist/vector/slugTSL.cjs +252 -0
  51. package/dist/vector/slugTSL.js +231 -0
  52. package/dist/vector/webgl/index.cjs +131 -201
  53. package/dist/vector/webgl/index.d.ts +19 -44
  54. package/dist/vector/webgl/index.js +131 -201
  55. package/dist/vector/webgpu/index.cjs +100 -283
  56. package/dist/vector/webgpu/index.d.ts +16 -45
  57. package/dist/vector/webgpu/index.js +100 -283
  58. package/package.json +6 -1
  59. package/dist/types/vector/GlyphVectorGeometryBuilder.d.ts +0 -26
  60. package/dist/types/vector/LoopBlinnGeometry.d.ts +0 -68
  61. package/dist/types/vector/loopBlinnTSL.d.ts +0 -11
@@ -0,0 +1,231 @@
1
+ import * as THREE from 'three';
2
+ import { MeshBasicNodeMaterial } from 'three/webgpu';
3
+ import { Fn, select, uint, float, sqrt, max, If, abs, vec2, ivec2, varying, attribute, uniform, min, int, textureLoad, Loop, vec4, Break, clamp, fwidth } from 'three/tsl';
4
+ import { u as unpackSlugVertices } from './index2.js';
5
+ import './core/index.js';
6
+
7
+ // Slug TSL adapter for Three.js WebGPURenderer (and WebGL via r170+)
8
+ //
9
+ // Creates a single Three.js Mesh with a NodeMaterial that implements
10
+ // the Slug algorithm: per-fragment winding number evaluation via
11
+ // band-accelerated ray-curve intersection
12
+ //
13
+ // Works on both WebGPU and WebGL backends via Three.js TSL
14
+ //
15
+ // Compared to the raw GLSL/WGSL standalone renderers, this adapter
16
+ // trades some features for Three.js integration:
17
+ // - No vertex dilation (may cause sub-pixel edge clipping at extreme zoom)
18
+ // - No adaptive supersampling (single-sample per fragment)
19
+ //
20
+ // Requires peer dependencies: three, three/tsl
21
+ // @ts-ignore - three is a peer dependency
22
+ const LOG_BAND_TEX_W = 12;
23
+ const BAND_TEX_W_MASK = (1 << LOG_BAND_TEX_W) - 1;
24
+ // Determines which quadratic roots to evaluate from the signs of
25
+ // control-point y-coordinates, using direct sign comparisons instead
26
+ // of the original floatBitsToUint encoding.
27
+ // Returns a 16-bit value where bit 0 = evaluate root 1,
28
+ // bit 8 = evaluate root 2 (matching the 0x0101 mask convention)
29
+ const calcRootCode = Fn(([y1, y2, y3]) => {
30
+ const s1 = select(y1.lessThan(0), uint(1), uint(0));
31
+ const s2 = select(y2.lessThan(0), uint(1), uint(0));
32
+ const s3 = select(y3.lessThan(0), uint(1), uint(0));
33
+ const shift = s1.bitOr(s2.shiftLeft(1)).bitOr(s3.shiftLeft(2));
34
+ return uint(0x2E74).shiftRight(shift).bitAnd(uint(0x0101));
35
+ });
36
+ // Solve horizontal quadratic: finds x-intercepts where the curve
37
+ // crosses the fragment's y = 0 line
38
+ const solveHorizPoly = Fn(([p12, p3]) => {
39
+ const ax = p12.x.sub(p12.z.mul(2)).add(p3.x);
40
+ const ay = p12.y.sub(p12.w.mul(2)).add(p3.y);
41
+ const bx = p12.x.sub(p12.z);
42
+ const by = p12.y.sub(p12.w);
43
+ const ra = float(1).div(ay);
44
+ const rb = float(0.5).div(by);
45
+ const d = sqrt(max(by.mul(by).sub(ay.mul(p12.y)), 0));
46
+ const t1 = by.sub(d).mul(ra).toVar();
47
+ const t2 = by.add(d).mul(ra).toVar();
48
+ If(abs(ay).lessThan(float(1.0 / 65536.0)), () => {
49
+ const fb = p12.y.mul(rb);
50
+ t1.assign(fb);
51
+ t2.assign(fb);
52
+ });
53
+ return vec2(ax.mul(t1).sub(bx.mul(2)).mul(t1).add(p12.x), ax.mul(t2).sub(bx.mul(2)).mul(t2).add(p12.x));
54
+ });
55
+ // Solve vertical quadratic: finds y-intercepts where the curve
56
+ // crosses the fragment's x = 0 line
57
+ const solveVertPoly = Fn(([p12, p3]) => {
58
+ const ax = p12.x.sub(p12.z.mul(2)).add(p3.x);
59
+ const ay = p12.y.sub(p12.w.mul(2)).add(p3.y);
60
+ const bx = p12.x.sub(p12.z);
61
+ const by = p12.y.sub(p12.w);
62
+ const ra = float(1).div(ax);
63
+ const rb = float(0.5).div(bx);
64
+ const d = sqrt(max(bx.mul(bx).sub(ax.mul(p12.x)), 0));
65
+ const t1 = bx.sub(d).mul(ra).toVar();
66
+ const t2 = bx.add(d).mul(ra).toVar();
67
+ If(abs(ax).lessThan(float(1.0 / 65536.0)), () => {
68
+ const fb = p12.x.mul(rb);
69
+ t1.assign(fb);
70
+ t2.assign(fb);
71
+ });
72
+ return vec2(ay.mul(t1).sub(by.mul(2)).mul(t1).add(p12.y), ay.mul(t2).sub(by.mul(2)).mul(t2).add(p12.y));
73
+ });
74
+ // Compute a band-texture coordinate with row wrapping
75
+ // Equivalent to CalcBandLoc in the GLSL reference
76
+ const calcBandLoc = Fn(([glyphX, glyphY, offset]) => {
77
+ const bx = glyphX.add(offset).toVar();
78
+ const by = glyphY.add(bx.shiftRight(LOG_BAND_TEX_W)).toVar();
79
+ bx.assign(bx.bitAnd(BAND_TEX_W_MASK));
80
+ return ivec2(bx, by);
81
+ });
82
+ // Create a Three.js Mesh from SlugGPUData using TSL node materials.
83
+ // Returns a single transparent mesh suitable for any Three.js scene.
84
+ // The Slug algorithm evaluates per-fragment coverage analytically,
85
+ // so no stencil buffer or multi-pass rendering is required
86
+ function createSlugTSLMesh(gpuData, color) {
87
+ const attrs = unpackSlugVertices(gpuData);
88
+ const geo = new THREE.BufferGeometry();
89
+ geo.setAttribute('position', new THREE.Float32BufferAttribute(attrs.positions, 3));
90
+ geo.setAttribute('slugTexcoord', new THREE.Float32BufferAttribute(attrs.texcoords, 2));
91
+ geo.setAttribute('slugBanding', new THREE.Float32BufferAttribute(attrs.bandings, 4));
92
+ geo.setAttribute('slugGlyph', new THREE.Float32BufferAttribute(attrs.glyphData, 4));
93
+ geo.setAttribute('slugColor', new THREE.Float32BufferAttribute(attrs.colors, 4));
94
+ geo.setAttribute('glyphCenter', new THREE.Float32BufferAttribute(attrs.glyphCenters, 3));
95
+ geo.setAttribute('glyphIndex', new THREE.Float32BufferAttribute(attrs.glyphIndices, 1));
96
+ geo.setIndex(new THREE.BufferAttribute(gpuData.indices, 1));
97
+ const curveTex = new THREE.DataTexture(gpuData.curveTexture.data, gpuData.curveTexture.width, gpuData.curveTexture.height, THREE.RGBAFormat, THREE.FloatType);
98
+ curveTex.minFilter = THREE.NearestFilter;
99
+ curveTex.magFilter = THREE.NearestFilter;
100
+ curveTex.generateMipmaps = false;
101
+ curveTex.needsUpdate = true;
102
+ // Band texture: convert Uint32 to Float32 (values are small ints, exact in f32)
103
+ const bandFloat = new Float32Array(gpuData.bandTexture.data.length);
104
+ for (let i = 0; i < bandFloat.length; i++) {
105
+ bandFloat[i] = gpuData.bandTexture.data[i];
106
+ }
107
+ const bandTex = new THREE.DataTexture(bandFloat, gpuData.bandTexture.width, gpuData.bandTexture.height, THREE.RGBAFormat, THREE.FloatType);
108
+ bandTex.minFilter = THREE.NearestFilter;
109
+ bandTex.magFilter = THREE.NearestFilter;
110
+ bandTex.generateMipmaps = false;
111
+ bandTex.needsUpdate = true;
112
+ // Varyings: vertex attributes interpolated to fragment stage
113
+ const vTexcoord = varying(attribute('slugTexcoord', 'vec2'), 'v_texcoord');
114
+ const vBanding = varying(attribute('slugBanding', 'vec4'), 'v_banding');
115
+ const vGlyph = varying(attribute('slugGlyph', 'vec4'), 'v_glyph');
116
+ const vColor = varying(attribute('slugColor', 'vec4'), 'v_color');
117
+ // Color uniform (allows dynamic color updates)
118
+ const textColor = uniform(new THREE.Color(color?.r ?? 1, color?.g ?? 1, color?.b ?? 1));
119
+ // Main per-fragment evaluation: SlugRenderSingle ported to TSL
120
+ // Evaluates horizontal and vertical band loops to compute
121
+ // analytic winding-number coverage
122
+ const slugRenderSingle = Fn(([renderCoord, emsPerPixel, bandTransform, glyphData]) => {
123
+ const pixelsPerEm = vec2(float(1).div(emsPerPixel.x), float(1).div(emsPerPixel.y));
124
+ const glyphLocX = glyphData.x.toInt();
125
+ const glyphLocY = glyphData.y.toInt();
126
+ const bandMaxX = glyphData.z.toInt();
127
+ const bandMaxY = glyphData.w.toInt().bitAnd(0xFF);
128
+ const bandIdxX = max(min(renderCoord.x.mul(bandTransform.x).add(bandTransform.z).toInt(), bandMaxX), int(0));
129
+ const bandIdxY = max(min(renderCoord.y.mul(bandTransform.y).add(bandTransform.w).toInt(), bandMaxY), int(0));
130
+ // Horizontal band loop
131
+ const xcov = float(0).toVar();
132
+ const xwgt = float(0).toVar();
133
+ const hbandData = textureLoad(bandTex, ivec2(glyphLocX.add(bandIdxY), glyphLocY));
134
+ const hCurveCount = hbandData.x.toInt();
135
+ const hListOffset = hbandData.y.toInt();
136
+ const hbandLoc = calcBandLoc(glyphLocX, glyphLocY, hListOffset);
137
+ const hIdx = int(0).toVar();
138
+ Loop(hIdx.lessThan(hCurveCount), () => {
139
+ const clEntry = textureLoad(bandTex, ivec2(hbandLoc.x.add(hIdx), hbandLoc.y));
140
+ const cLocX = clEntry.x.toInt();
141
+ const cLocY = clEntry.y.toInt();
142
+ const rawP12 = textureLoad(curveTex, ivec2(cLocX, cLocY));
143
+ const rawP3 = textureLoad(curveTex, ivec2(cLocX.add(1), cLocY));
144
+ const p12 = vec4(rawP12.x.sub(renderCoord.x), rawP12.y.sub(renderCoord.y), rawP12.z.sub(renderCoord.x), rawP12.w.sub(renderCoord.y));
145
+ const p3 = vec2(rawP3.x.sub(renderCoord.x), rawP3.y.sub(renderCoord.y));
146
+ If(max(max(p12.x, p12.z), p3.x).mul(pixelsPerEm.x).lessThan(-0.5), () => {
147
+ Break();
148
+ });
149
+ const code = calcRootCode(p12.y, p12.w, p3.y);
150
+ If(code.notEqual(uint(0)), () => {
151
+ const r = solveHorizPoly(p12, p3).mul(pixelsPerEm.x);
152
+ If(code.bitAnd(uint(1)).notEqual(uint(0)), () => {
153
+ xcov.addAssign(clamp(r.x.add(0.5), 0, 1));
154
+ xwgt.assign(max(xwgt, clamp(float(1).sub(abs(r.x).mul(2)), 0, 1)));
155
+ });
156
+ If(code.greaterThan(uint(1)), () => {
157
+ xcov.subAssign(clamp(r.y.add(0.5), 0, 1));
158
+ xwgt.assign(max(xwgt, clamp(float(1).sub(abs(r.y).mul(2)), 0, 1)));
159
+ });
160
+ });
161
+ hIdx.addAssign(1);
162
+ });
163
+ // Vertical band loop
164
+ const ycov = float(0).toVar();
165
+ const ywgt = float(0).toVar();
166
+ const vbandOffset = bandMaxY.add(1).add(bandIdxX);
167
+ const vbandData = textureLoad(bandTex, ivec2(glyphLocX.add(vbandOffset), glyphLocY));
168
+ const vCurveCount = vbandData.x.toInt();
169
+ const vListOffset = vbandData.y.toInt();
170
+ const vbandLoc = calcBandLoc(glyphLocX, glyphLocY, vListOffset);
171
+ const vIdx = int(0).toVar();
172
+ Loop(vIdx.lessThan(vCurveCount), () => {
173
+ const clEntry = textureLoad(bandTex, ivec2(vbandLoc.x.add(vIdx), vbandLoc.y));
174
+ const cLocX = clEntry.x.toInt();
175
+ const cLocY = clEntry.y.toInt();
176
+ const rawP12 = textureLoad(curveTex, ivec2(cLocX, cLocY));
177
+ const rawP3 = textureLoad(curveTex, ivec2(cLocX.add(1), cLocY));
178
+ const p12 = vec4(rawP12.x.sub(renderCoord.x), rawP12.y.sub(renderCoord.y), rawP12.z.sub(renderCoord.x), rawP12.w.sub(renderCoord.y));
179
+ const p3 = vec2(rawP3.x.sub(renderCoord.x), rawP3.y.sub(renderCoord.y));
180
+ If(max(max(p12.y, p12.w), p3.y).mul(pixelsPerEm.y).lessThan(-0.5), () => {
181
+ Break();
182
+ });
183
+ const code = calcRootCode(p12.x, p12.z, p3.x);
184
+ If(code.notEqual(uint(0)), () => {
185
+ const r = solveVertPoly(p12, p3).mul(pixelsPerEm.y);
186
+ If(code.bitAnd(uint(1)).notEqual(uint(0)), () => {
187
+ ycov.subAssign(clamp(r.x.add(0.5), 0, 1));
188
+ ywgt.assign(max(ywgt, clamp(float(1).sub(abs(r.x).mul(2)), 0, 1)));
189
+ });
190
+ If(code.greaterThan(uint(1)), () => {
191
+ ycov.addAssign(clamp(r.y.add(0.5), 0, 1));
192
+ ywgt.assign(max(ywgt, clamp(float(1).sub(abs(r.y).mul(2)), 0, 1)));
193
+ });
194
+ });
195
+ vIdx.addAssign(1);
196
+ });
197
+ // CalcCoverage (nonzero winding rule)
198
+ const coverage = max(abs(xcov.mul(xwgt).add(ycov.mul(ywgt))).div(max(xwgt.add(ywgt), float(1.0 / 65536.0))), min(abs(xcov), abs(ycov)));
199
+ return clamp(coverage, 0, 1);
200
+ });
201
+ // Top-level fragment node
202
+ const fragmentNode = Fn(() => {
203
+ const emsPerPixel = fwidth(vTexcoord);
204
+ const coverage = slugRenderSingle(vTexcoord, emsPerPixel, vBanding, vGlyph);
205
+ return vec4(textColor.x, textColor.y, textColor.z, vColor.w.mul(coverage));
206
+ })();
207
+ // Material & mesh
208
+ const material = new MeshBasicNodeMaterial();
209
+ material.fragmentNode = fragmentNode;
210
+ material.transparent = true;
211
+ material.depthWrite = false;
212
+ material.side = THREE.DoubleSide;
213
+ const mesh = new THREE.Mesh(geo, material);
214
+ return {
215
+ mesh,
216
+ setOffset(x, y, z = 0) {
217
+ mesh.position.set(x, y, z);
218
+ },
219
+ setColor(r, g, b) {
220
+ textColor.value.setRGB(r, g, b);
221
+ },
222
+ dispose() {
223
+ geo.dispose();
224
+ material.dispose();
225
+ curveTex.dispose();
226
+ bandTex.dispose();
227
+ }
228
+ };
229
+ }
230
+
231
+ export { createSlugTSLMesh };
@@ -1,229 +1,159 @@
1
1
  'use strict';
2
2
 
3
- function assertCreate(value, label) {
4
- if (!value) {
5
- throw new Error(`Failed to create ${label}`);
6
- }
7
- return value;
8
- }
3
+ 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";
4
+
5
+ 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";
6
+
7
+ // Slug shader source re-exports
8
+ // The .glsl/.wgsl files are the single source of truth, imported as strings
9
+ // at build time via the glslPlugin in rollup.config.js
10
+ // @ts-ignore - resolved by rollup glslPlugin
11
+ const vertexShaderGLSL300 = vertGLSL;
12
+ const fragmentShaderGLSL300 = fragGLSL;
13
+
14
+ // Raw WebGL2 Slug vector text renderer
15
+ // Standalone, no dependency on Three.js
9
16
  function compileShader(gl, type, source) {
10
- const shader = assertCreate(gl.createShader(type), 'shader');
17
+ const shader = gl.createShader(type);
18
+ if (!shader)
19
+ throw new Error('Failed to create shader');
11
20
  gl.shaderSource(shader, source);
12
21
  gl.compileShader(shader);
13
22
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
14
- const info = gl.getShaderInfoLog(shader) || 'unknown shader compile error';
23
+ const info = gl.getShaderInfoLog(shader);
15
24
  gl.deleteShader(shader);
16
- throw new Error(info);
25
+ throw new Error(`Shader compile failed: ${info}`);
17
26
  }
18
27
  return shader;
19
28
  }
20
- function linkProgram(gl, vertexSource, fragmentSource) {
21
- const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
22
- const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
23
- const program = assertCreate(gl.createProgram(), 'program');
24
- gl.attachShader(program, vertexShader);
25
- gl.attachShader(program, fragmentShader);
26
- gl.linkProgram(program);
27
- gl.deleteShader(vertexShader);
28
- gl.deleteShader(fragmentShader);
29
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
30
- const info = gl.getProgramInfoLog(program) || 'unknown program link error';
31
- gl.deleteProgram(program);
32
- throw new Error(info);
29
+ function createProgram(gl, vsSrc, fsSrc) {
30
+ const vs = compileShader(gl, gl.VERTEX_SHADER, vsSrc);
31
+ const fs = compileShader(gl, gl.FRAGMENT_SHADER, fsSrc);
32
+ const prog = gl.createProgram();
33
+ if (!prog)
34
+ throw new Error('Failed to create program');
35
+ gl.attachShader(prog, vs);
36
+ gl.attachShader(prog, fs);
37
+ gl.linkProgram(prog);
38
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
39
+ const info = gl.getProgramInfoLog(prog);
40
+ gl.deleteProgram(prog);
41
+ throw new Error(`Program link failed: ${info}`);
33
42
  }
34
- return program;
43
+ gl.deleteShader(vs);
44
+ gl.deleteShader(fs);
45
+ return prog;
35
46
  }
36
- function getUniform(gl, program, name) {
37
- const location = gl.getUniformLocation(program, name);
38
- if (!location) {
39
- throw new Error(`Missing uniform "${name}"`);
40
- }
41
- return location;
47
+ function createRGBA32FTexture(gl, data, width, height) {
48
+ const tex = gl.createTexture();
49
+ if (!tex)
50
+ throw new Error('Failed to create texture');
51
+ gl.bindTexture(gl.TEXTURE_2D, tex);
52
+ gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
53
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
54
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
55
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
56
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
57
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, width, height, 0, gl.RGBA, gl.FLOAT, data);
58
+ gl.bindTexture(gl.TEXTURE_2D, null);
59
+ return tex;
42
60
  }
43
- function createProgramWithMvp(gl, vertexSource, fragmentSource) {
44
- const program = linkProgram(gl, vertexSource, fragmentSource);
45
- return {
46
- program,
47
- mvp: getUniform(gl, program, 'u_mvp')
48
- };
61
+ function createRGBA32UITexture(gl, data, width, height) {
62
+ const tex = gl.createTexture();
63
+ if (!tex)
64
+ throw new Error('Failed to create texture');
65
+ gl.bindTexture(gl.TEXTURE_2D, tex);
66
+ gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
67
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
68
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
69
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
70
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
71
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32UI, width, height, 0, gl.RGBA_INTEGER, gl.UNSIGNED_INT, data);
72
+ gl.bindTexture(gl.TEXTURE_2D, null);
73
+ return tex;
49
74
  }
50
- function createColorProgram(gl, vertexSource, fragmentSource) {
51
- const program = linkProgram(gl, vertexSource, fragmentSource);
52
- return {
53
- program,
54
- mvp: getUniform(gl, program, 'u_mvp'),
55
- color: getUniform(gl, program, 'u_color')
75
+ function createResources(gl, gpuData, fragSrc) {
76
+ gl.getExtension('EXT_color_buffer_float');
77
+ const program = createProgram(gl, vertexShaderGLSL300, fragSrc);
78
+ const uniforms = {
79
+ slug_matrix: gl.getUniformLocation(program, 'slug_matrix'),
80
+ slug_viewport: gl.getUniformLocation(program, 'slug_viewport'),
81
+ curveTexture: gl.getUniformLocation(program, 'curveTexture'),
82
+ bandTexture: gl.getUniformLocation(program, 'bandTexture'),
56
83
  };
57
- }
58
- function createGeometryResources(gl, data) {
59
- const interiorVAO = assertCreate(gl.createVertexArray(), 'interior VAO');
60
- const interiorPositionBuffer = assertCreate(gl.createBuffer(), 'interior position buffer');
61
- const interiorIndexBuffer = assertCreate(gl.createBuffer(), 'interior index buffer');
62
- gl.bindVertexArray(interiorVAO);
63
- gl.bindBuffer(gl.ARRAY_BUFFER, interiorPositionBuffer);
64
- gl.bufferData(gl.ARRAY_BUFFER, data.interiorPositions, gl.STATIC_DRAW);
65
- gl.enableVertexAttribArray(0);
66
- gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
67
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, interiorIndexBuffer);
68
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data.interiorIndices, gl.STATIC_DRAW);
69
- const curveVAO = assertCreate(gl.createVertexArray(), 'curve VAO');
70
- const curvePositionBuffer = assertCreate(gl.createBuffer(), 'curve position buffer');
71
- gl.bindVertexArray(curveVAO);
72
- gl.bindBuffer(gl.ARRAY_BUFFER, curvePositionBuffer);
73
- gl.bufferData(gl.ARRAY_BUFFER, data.curvePositions, gl.STATIC_DRAW);
74
- gl.enableVertexAttribArray(0);
75
- gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
76
- const fillVAO = assertCreate(gl.createVertexArray(), 'fill VAO');
77
- const fillPositionBuffer = assertCreate(gl.createBuffer(), 'fill position buffer');
78
- const fillIndexBuffer = assertCreate(gl.createBuffer(), 'fill index buffer');
79
- gl.bindVertexArray(fillVAO);
80
- gl.bindBuffer(gl.ARRAY_BUFFER, fillPositionBuffer);
81
- gl.bufferData(gl.ARRAY_BUFFER, data.fillPositions, gl.STATIC_DRAW);
84
+ const vao = gl.createVertexArray();
85
+ if (!vao)
86
+ throw new Error('Failed to create VAO');
87
+ gl.bindVertexArray(vao);
88
+ const vbo = gl.createBuffer();
89
+ if (!vbo)
90
+ throw new Error('Failed to create VBO');
91
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
92
+ gl.bufferData(gl.ARRAY_BUFFER, gpuData.vertices, gl.STATIC_DRAW);
93
+ const stride = 20 * 4;
82
94
  gl.enableVertexAttribArray(0);
83
- gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
84
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, fillIndexBuffer);
85
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data.fillIndices, gl.STATIC_DRAW);
95
+ gl.vertexAttribPointer(0, 4, gl.FLOAT, false, stride, 0);
96
+ gl.enableVertexAttribArray(1);
97
+ gl.vertexAttribPointer(1, 4, gl.FLOAT, false, stride, 4 * 4);
98
+ gl.enableVertexAttribArray(2);
99
+ gl.vertexAttribPointer(2, 4, gl.FLOAT, false, stride, 8 * 4);
100
+ gl.enableVertexAttribArray(3);
101
+ gl.vertexAttribPointer(3, 4, gl.FLOAT, false, stride, 12 * 4);
102
+ gl.enableVertexAttribArray(4);
103
+ gl.vertexAttribPointer(4, 4, gl.FLOAT, false, stride, 16 * 4);
104
+ const ibo = gl.createBuffer();
105
+ if (!ibo)
106
+ throw new Error('Failed to create IBO');
107
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
108
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, gpuData.indices, gl.STATIC_DRAW);
86
109
  gl.bindVertexArray(null);
87
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
88
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
89
- return {
90
- interiorVAO,
91
- interiorPositionBuffer,
92
- interiorIndexBuffer,
93
- interiorIndexCount: data.interiorIndices.length,
94
- curveVAO,
95
- curvePositionBuffer,
96
- curveVertexCount: data.curvePositions.length / 3,
97
- fillVAO,
98
- fillPositionBuffer,
99
- fillIndexBuffer,
100
- fillIndexCount: data.fillIndices.length
101
- };
110
+ const curveTexture = createRGBA32FTexture(gl, gpuData.curveTexture.data, gpuData.curveTexture.width, gpuData.curveTexture.height);
111
+ const bandTexture = createRGBA32UITexture(gl, gpuData.bandTexture.data, gpuData.bandTexture.width, gpuData.bandTexture.height);
112
+ return { program, vao, vbo, ibo, curveTexture, bandTexture, uniforms, indexCount: gpuData.indices.length };
102
113
  }
103
- function destroyGeometryResources(gl, resources) {
104
- gl.deleteVertexArray(resources.interiorVAO);
105
- gl.deleteBuffer(resources.interiorPositionBuffer);
106
- gl.deleteBuffer(resources.interiorIndexBuffer);
107
- gl.deleteVertexArray(resources.curveVAO);
108
- gl.deleteBuffer(resources.curvePositionBuffer);
109
- gl.deleteVertexArray(resources.fillVAO);
110
- gl.deleteBuffer(resources.fillPositionBuffer);
111
- gl.deleteBuffer(resources.fillIndexBuffer);
114
+ function draw(gl, res, mvpMatrix, viewportWidth, viewportHeight) {
115
+ gl.useProgram(res.program);
116
+ gl.uniformMatrix4fv(res.uniforms.slug_matrix, false, mvpMatrix);
117
+ gl.uniform2f(res.uniforms.slug_viewport, viewportWidth, viewportHeight);
118
+ gl.activeTexture(gl.TEXTURE0);
119
+ gl.bindTexture(gl.TEXTURE_2D, res.curveTexture);
120
+ gl.uniform1i(res.uniforms.curveTexture, 0);
121
+ gl.activeTexture(gl.TEXTURE1);
122
+ gl.bindTexture(gl.TEXTURE_2D, res.bandTexture);
123
+ gl.uniform1i(res.uniforms.bandTexture, 1);
124
+ gl.bindVertexArray(res.vao);
125
+ gl.enable(gl.BLEND);
126
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
127
+ gl.drawElements(gl.TRIANGLES, res.indexCount, gl.UNSIGNED_SHORT, 0);
128
+ gl.bindVertexArray(null);
112
129
  }
113
- function createWebGLVectorRenderer(gl) {
114
- const interiorVertexShader = `#version 300 es
115
- layout(location = 0) in vec3 a_position;
116
- uniform mat4 u_mvp;
117
- void main() {
118
- gl_Position = u_mvp * vec4(a_position, 1.0);
119
- }`;
120
- const interiorFragmentShader = `#version 300 es
121
- precision highp float;
122
- out vec4 outColor;
123
- void main() {
124
- outColor = vec4(1.0);
125
- }`;
126
- const curveVertexShader = `#version 300 es
127
- layout(location = 0) in vec3 a_position;
128
- uniform mat4 u_mvp;
129
- out vec2 v_uv;
130
- void main() {
131
- int localVertex = gl_VertexID % 3;
132
- float u = float(localVertex) * 0.5;
133
- v_uv = vec2(u, floor(u));
134
- gl_Position = u_mvp * vec4(a_position, 1.0);
135
- }`;
136
- const curveFragmentShader = `#version 300 es
137
- precision highp float;
138
- in vec2 v_uv;
139
- out vec4 outColor;
140
- void main() {
141
- vec2 px = dFdx(v_uv);
142
- vec2 py = dFdy(v_uv);
143
- float fx = 2.0 * v_uv.x * px.x - px.y;
144
- float fy = 2.0 * v_uv.x * py.x - py.y;
145
- float denom = sqrt(fx * fx + fy * fy);
146
- if (denom < 1e-6) {
147
- discard;
148
- }
149
- float sd = (v_uv.x * v_uv.x - v_uv.y) / denom;
150
- float alpha = clamp(0.5 - sd, 0.0, 1.0);
151
- if (alpha <= 0.0) {
152
- discard;
153
- }
154
- outColor = vec4(1.0, 1.0, 1.0, alpha);
155
- }`;
156
- const colorVertexShader = interiorVertexShader;
157
- const colorFragmentShader = `#version 300 es
158
- precision highp float;
159
- uniform vec4 u_color;
160
- out vec4 outColor;
161
- void main() {
162
- outColor = u_color;
163
- }`;
164
- const interiorProgram = createProgramWithMvp(gl, interiorVertexShader, interiorFragmentShader);
165
- const curveProgram = createProgramWithMvp(gl, curveVertexShader, curveFragmentShader);
166
- const colorProgram = createColorProgram(gl, colorVertexShader, colorFragmentShader);
167
- let geometryResources = null;
130
+ function createWebGLVectorRenderer(gl, options) {
131
+ let resources = null;
132
+ const fragSrc = options?.adaptiveSupersampling
133
+ ? '#define SLUG_ADAPTIVE_SUPERSAMPLE\n' + fragmentShaderGLSL300
134
+ : fragmentShaderGLSL300;
168
135
  return {
169
136
  setGeometry(data) {
170
- if (geometryResources) {
171
- destroyGeometryResources(gl, geometryResources);
172
- }
173
- geometryResources = createGeometryResources(gl, data);
137
+ if (resources)
138
+ this.dispose();
139
+ resources = createResources(gl, data, fragSrc);
174
140
  },
175
- render(mvp, color) {
176
- if (!geometryResources) {
141
+ render(mvp, _color) {
142
+ if (!resources)
177
143
  return;
178
- }
179
- gl.disable(gl.CULL_FACE);
180
- gl.disable(gl.DEPTH_TEST);
181
- gl.depthMask(false);
182
- // No stencil clear needed - the fill pass resets stencil to 0
183
- // via passOp ZERO wherever stencil was non-zero, and per-glyph
184
- // fill quads cover all stencil writes from interior/curve passes
185
- gl.enable(gl.STENCIL_TEST);
186
- gl.stencilMask(0xff);
187
- gl.stencilFunc(gl.ALWAYS, 0, 0xff);
188
- // Nonzero winding: front faces increment, back faces decrement
189
- gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP);
190
- gl.stencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP);
191
- gl.colorMask(false, false, false, false);
192
- if (geometryResources.interiorIndexCount > 0) {
193
- gl.useProgram(interiorProgram.program);
194
- gl.uniformMatrix4fv(interiorProgram.mvp, false, mvp);
195
- gl.bindVertexArray(geometryResources.interiorVAO);
196
- gl.drawElements(gl.TRIANGLES, geometryResources.interiorIndexCount, gl.UNSIGNED_INT, 0);
197
- }
198
- if (geometryResources.curveVertexCount > 0) {
199
- gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
200
- gl.useProgram(curveProgram.program);
201
- gl.uniformMatrix4fv(curveProgram.mvp, false, mvp);
202
- gl.bindVertexArray(geometryResources.curveVAO);
203
- gl.drawArrays(gl.TRIANGLES, 0, geometryResources.curveVertexCount);
204
- gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
205
- }
206
- gl.stencilFunc(gl.NOTEQUAL, 0, 0xff);
207
- gl.stencilOp(gl.KEEP, gl.KEEP, gl.ZERO);
208
- gl.colorMask(true, true, true, true);
209
- gl.useProgram(colorProgram.program);
210
- gl.uniformMatrix4fv(colorProgram.mvp, false, mvp);
211
- gl.uniform4fv(colorProgram.color, color);
212
- gl.bindVertexArray(geometryResources.fillVAO);
213
- gl.drawElements(gl.TRIANGLES, geometryResources.fillIndexCount, gl.UNSIGNED_INT, 0);
214
- gl.bindVertexArray(null);
215
- gl.useProgram(null);
216
- gl.disable(gl.STENCIL_TEST);
217
- gl.depthMask(true);
144
+ draw(gl, resources, mvp, gl.drawingBufferWidth, gl.drawingBufferHeight);
218
145
  },
219
146
  dispose() {
220
- if (geometryResources) {
221
- destroyGeometryResources(gl, geometryResources);
222
- geometryResources = null;
223
- }
224
- gl.deleteProgram(interiorProgram.program);
225
- gl.deleteProgram(curveProgram.program);
226
- gl.deleteProgram(colorProgram.program);
147
+ if (!resources)
148
+ return;
149
+ const gl2 = gl;
150
+ gl2.deleteTexture(resources.curveTexture);
151
+ gl2.deleteTexture(resources.bandTexture);
152
+ gl2.deleteBuffer(resources.vbo);
153
+ gl2.deleteBuffer(resources.ibo);
154
+ gl2.deleteVertexArray(resources.vao);
155
+ gl2.deleteProgram(resources.program);
156
+ resources = null;
227
157
  }
228
158
  };
229
159
  }