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.
Files changed (52) hide show
  1. package/LICENSE_THIRD_PARTY +15 -0
  2. package/README.md +73 -42
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +1 -1
  6. package/dist/index.min.cjs +1 -1
  7. package/dist/index.min.js +1 -1
  8. package/dist/index.umd.js +1 -1
  9. package/dist/index.umd.min.js +1 -1
  10. package/dist/three/react.d.ts +2 -0
  11. package/dist/types/core/types.d.ts +2 -33
  12. package/dist/types/vector/{core.d.ts → core/index.d.ts} +8 -7
  13. package/dist/types/vector/index.d.ts +22 -25
  14. package/dist/types/vector/react.d.ts +4 -3
  15. package/dist/types/vector/slug/SlugPacker.d.ts +2 -0
  16. package/dist/types/vector/slug/curveUtils.d.ts +6 -0
  17. package/dist/types/vector/slug/index.d.ts +8 -0
  18. package/dist/types/vector/slug/shaderStrings.d.ts +4 -0
  19. package/dist/types/vector/slug/slugGLSL.d.ts +21 -0
  20. package/dist/types/vector/slug/slugTSL.d.ts +13 -0
  21. package/dist/types/vector/slug/types.d.ts +30 -0
  22. package/dist/types/vector/slug/unpackVertices.d.ts +11 -0
  23. package/dist/types/vector/webgl/index.d.ts +7 -3
  24. package/dist/types/vector/webgpu/index.d.ts +4 -4
  25. package/dist/vector/core/index.cjs +856 -0
  26. package/dist/vector/core/index.d.ts +63 -0
  27. package/dist/vector/core/index.js +854 -0
  28. package/dist/vector/core.cjs +4419 -240
  29. package/dist/vector/core.d.ts +361 -71
  30. package/dist/vector/core.js +4406 -226
  31. package/dist/vector/index.cjs +5 -229
  32. package/dist/vector/index.d.ts +45 -396
  33. package/dist/vector/index.js +3 -223
  34. package/dist/vector/index2.cjs +287 -0
  35. package/dist/vector/index2.js +264 -0
  36. package/dist/vector/react.cjs +37 -8
  37. package/dist/vector/react.d.ts +6 -3
  38. package/dist/vector/react.js +18 -8
  39. package/dist/vector/slugTSL.cjs +252 -0
  40. package/dist/vector/slugTSL.js +231 -0
  41. package/dist/vector/webgl/index.cjs +131 -201
  42. package/dist/vector/webgl/index.d.ts +19 -44
  43. package/dist/vector/webgl/index.js +131 -201
  44. package/dist/vector/webgpu/index.cjs +100 -283
  45. package/dist/vector/webgpu/index.d.ts +16 -45
  46. package/dist/vector/webgpu/index.js +100 -283
  47. package/package.json +6 -1
  48. package/dist/types/vector/GlyphVectorGeometryBuilder.d.ts +0 -26
  49. package/dist/types/vector/LoopBlinnGeometry.d.ts +0 -68
  50. package/dist/types/vector/loopBlinnTSL.d.ts +0 -22
  51. package/dist/vector/loopBlinnTSL.cjs +0 -229
  52. package/dist/vector/loopBlinnTSL.js +0 -207
@@ -0,0 +1,287 @@
1
+ 'use strict';
2
+
3
+ var core = require('./core/index.cjs');
4
+ var THREE = require('three');
5
+
6
+ function _interopNamespaceDefault(e) {
7
+ var n = Object.create(null);
8
+ if (e) {
9
+ Object.keys(e).forEach(function (k) {
10
+ if (k !== 'default') {
11
+ var d = Object.getOwnPropertyDescriptor(e, k);
12
+ Object.defineProperty(n, k, d.get ? d : {
13
+ enumerable: true,
14
+ get: function () { return e[k]; }
15
+ });
16
+ }
17
+ });
18
+ }
19
+ n.default = e;
20
+ return Object.freeze(n);
21
+ }
22
+
23
+ var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
24
+
25
+ // Restructures the tightly-packed vertex buffer from SlugPacker into
26
+ // separate per-attribute arrays for GPU attribute binding
27
+ const FLOATS_PER_VERT = 20;
28
+ function unpackSlugVertices(gpuData) {
29
+ const vertCount = gpuData.shapeCount * 4;
30
+ const positions = new Float32Array(vertCount * 3);
31
+ const texcoords = new Float32Array(vertCount * 2);
32
+ const bandings = new Float32Array(vertCount * 4);
33
+ const glyphData = new Float32Array(vertCount * 4);
34
+ const colors = new Float32Array(vertCount * 4);
35
+ const srcF = gpuData.vertices;
36
+ const srcU = new Uint32Array(srcF.buffer, srcF.byteOffset, srcF.length);
37
+ for (let i = 0; i < vertCount; i++) {
38
+ const s = i * FLOATS_PER_VERT;
39
+ positions[i * 3] = srcF[s];
40
+ positions[i * 3 + 1] = srcF[s + 1];
41
+ positions[i * 3 + 2] = 0;
42
+ texcoords[i * 2] = srcF[s + 4];
43
+ texcoords[i * 2 + 1] = srcF[s + 5];
44
+ bandings[i * 4] = srcF[s + 12];
45
+ bandings[i * 4 + 1] = srcF[s + 13];
46
+ bandings[i * 4 + 2] = srcF[s + 14];
47
+ bandings[i * 4 + 3] = srcF[s + 15];
48
+ // Unpack glyph location and band metadata from bit-packed fields
49
+ const g0 = srcU[s + 6];
50
+ const g1 = srcU[s + 7];
51
+ glyphData[i * 4] = g0 & 0xFFFF;
52
+ glyphData[i * 4 + 1] = (g0 >>> 16) & 0xFFFF;
53
+ glyphData[i * 4 + 2] = g1 & 0xFFFF;
54
+ glyphData[i * 4 + 3] = (g1 >>> 16) & 0xFFFF;
55
+ colors[i * 4] = srcF[s + 16];
56
+ colors[i * 4 + 1] = srcF[s + 17];
57
+ colors[i * 4 + 2] = srcF[s + 18];
58
+ colors[i * 4 + 3] = srcF[s + 19];
59
+ }
60
+ // Per-glyph center: average of quad corner positions
61
+ const glyphCenters = new Float32Array(vertCount * 3);
62
+ const glyphIndices = new Float32Array(vertCount);
63
+ for (let g = 0; g < gpuData.shapeCount; g++) {
64
+ const base = g * 4;
65
+ let cx = 0, cy = 0;
66
+ for (let v = 0; v < 4; v++) {
67
+ cx += positions[(base + v) * 3];
68
+ cy += positions[(base + v) * 3 + 1];
69
+ }
70
+ cx *= 0.25;
71
+ cy *= 0.25;
72
+ for (let v = 0; v < 4; v++) {
73
+ const idx = base + v;
74
+ glyphCenters[idx * 3] = cx;
75
+ glyphCenters[idx * 3 + 1] = cy;
76
+ glyphCenters[idx * 3 + 2] = 0;
77
+ glyphIndices[idx] = g;
78
+ }
79
+ }
80
+ return { positions, texcoords, bandings, glyphData, colors, glyphCenters, glyphIndices };
81
+ }
82
+
83
+ 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";
84
+
85
+ // Slug shader source re-exports
86
+ // The .glsl/.wgsl files are the single source of truth, imported as strings
87
+ // at build time via the glslPlugin in rollup.config.js
88
+ // @ts-ignore - resolved by rollup glslPlugin
89
+ const fragmentShaderGLSL300 = fragGLSL;
90
+
91
+ // Slug GLSL adapter for Three.js, using RawShaderMaterial with the
92
+ // reference GLSL shaders. Works with both WebGLRenderer and
93
+ // WebGPURenderer (via GLSL-to-WGSL transpilation)
94
+ //
95
+ // Compared to the TSL adapter (slugTSL.ts):
96
+ // - Works with any Three.js renderer (no node-material dependency)
97
+ // - Uses native Uint32 band texture (no float conversion)
98
+ // - Supports GLSL animation injection via animationDeclarations/animationBody
99
+ // - Same tradeoff: no vertex dilation (may cause sub-pixel edge clipping at extreme zoom)
100
+ //
101
+ // Requires peer dependency: three
102
+ // @ts-ignore - three is a peer dependency
103
+ // Three.js GLSL3 mode prepends #version 300 es, so strip it from the raw shader
104
+ const fragShader = fragmentShaderGLSL300.replace(/^#version\s+300\s+es\s*\n/, '');
105
+ function buildVertexShader(options) {
106
+ const decl = options?.animationDeclarations ?? '';
107
+ const body = options?.animationBody ?? 'vec3 outPos = position;';
108
+ return `precision highp float;
109
+ precision highp int;
110
+
111
+ in vec3 position;
112
+ in vec2 slugTexcoord;
113
+ in vec4 slugBanding;
114
+ in vec4 slugGlyph;
115
+ in vec4 slugColor;
116
+ in vec3 glyphCenter;
117
+ in float glyphIndex;
118
+
119
+ uniform mat4 modelViewMatrix;
120
+ uniform mat4 projectionMatrix;
121
+ uniform float time;
122
+ ${decl}
123
+
124
+ out vec2 v_texcoord;
125
+ flat out vec4 v_banding;
126
+ flat out ivec4 v_glyph;
127
+ out vec4 v_color;
128
+
129
+ void main() {
130
+ v_texcoord = slugTexcoord;
131
+ v_banding = slugBanding;
132
+ v_glyph = ivec4(slugGlyph);
133
+ v_color = slugColor;
134
+
135
+ ${body}
136
+
137
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(outPos, 1.0);
138
+ }
139
+ `;
140
+ }
141
+ function createSlugGLSLMesh(gpuData, options) {
142
+ const attrs = unpackSlugVertices(gpuData);
143
+ const geo = new THREE__namespace.BufferGeometry();
144
+ geo.setAttribute('position', new THREE__namespace.Float32BufferAttribute(attrs.positions, 3));
145
+ geo.setAttribute('slugTexcoord', new THREE__namespace.Float32BufferAttribute(attrs.texcoords, 2));
146
+ geo.setAttribute('slugBanding', new THREE__namespace.Float32BufferAttribute(attrs.bandings, 4));
147
+ geo.setAttribute('slugGlyph', new THREE__namespace.Float32BufferAttribute(attrs.glyphData, 4));
148
+ geo.setAttribute('slugColor', new THREE__namespace.Float32BufferAttribute(attrs.colors, 4));
149
+ geo.setAttribute('glyphCenter', new THREE__namespace.Float32BufferAttribute(attrs.glyphCenters, 3));
150
+ geo.setAttribute('glyphIndex', new THREE__namespace.Float32BufferAttribute(attrs.glyphIndices, 1));
151
+ geo.setIndex(new THREE__namespace.BufferAttribute(gpuData.indices, 1));
152
+ // Curve texture: RGBA32F
153
+ const curveTex = new THREE__namespace.DataTexture(gpuData.curveTexture.data, gpuData.curveTexture.width, gpuData.curveTexture.height, THREE__namespace.RGBAFormat, THREE__namespace.FloatType);
154
+ curveTex.minFilter = THREE__namespace.NearestFilter;
155
+ curveTex.magFilter = THREE__namespace.NearestFilter;
156
+ curveTex.generateMipmaps = false;
157
+ curveTex.needsUpdate = true;
158
+ // Band texture: native RGBA32UI (no float conversion needed)
159
+ const bandTex = new THREE__namespace.DataTexture(gpuData.bandTexture.data, gpuData.bandTexture.width, gpuData.bandTexture.height);
160
+ bandTex.format = THREE__namespace.RGBAIntegerFormat;
161
+ bandTex.type = THREE__namespace.UnsignedIntType;
162
+ bandTex.internalFormat = 'RGBA32UI';
163
+ bandTex.minFilter = THREE__namespace.NearestFilter;
164
+ bandTex.magFilter = THREE__namespace.NearestFilter;
165
+ bandTex.generateMipmaps = false;
166
+ bandTex.needsUpdate = true;
167
+ const col = options?.color ?? { r: 1, g: 1, b: 1 };
168
+ const colorUniform = new THREE__namespace.Vector4(col.r, col.g, col.b, 1.0);
169
+ const uniforms = {
170
+ curveTexture: { value: curveTex },
171
+ bandTexture: { value: bandTex },
172
+ time: { value: 0 },
173
+ u_color: { value: colorUniform },
174
+ ...options?.uniforms,
175
+ };
176
+ const frag = options?.adaptiveSupersampling
177
+ ? '#define SLUG_ADAPTIVE_SUPERSAMPLE\n' + fragShader
178
+ : fragShader;
179
+ const material = new THREE__namespace.RawShaderMaterial({
180
+ glslVersion: THREE__namespace.GLSL3,
181
+ uniforms,
182
+ vertexShader: buildVertexShader(options),
183
+ fragmentShader: frag,
184
+ transparent: true,
185
+ depthWrite: false,
186
+ side: THREE__namespace.DoubleSide,
187
+ });
188
+ const mesh = new THREE__namespace.Mesh(geo, material);
189
+ return {
190
+ mesh,
191
+ uniforms,
192
+ setOffset(x, y, z = 0) {
193
+ mesh.position.set(x, y, z);
194
+ },
195
+ setColor(r, g, b) {
196
+ colorUniform.set(r, g, b, 1.0);
197
+ },
198
+ dispose() {
199
+ geo.dispose();
200
+ material.dispose();
201
+ curveTex.dispose();
202
+ bandTex.dispose();
203
+ }
204
+ };
205
+ }
206
+
207
+ function parseColor(color) {
208
+ if (!color)
209
+ return { r: 1, g: 1, b: 1 };
210
+ if (Array.isArray(color))
211
+ return { r: color[0], g: color[1], b: color[2] };
212
+ if (color.default)
213
+ return { r: color.default[0], g: color.default[1], b: color.default[2] };
214
+ return { r: 1, g: 1, b: 1 };
215
+ }
216
+ async function createMesh(gpuData, color, adaptiveSupersampling) {
217
+ try {
218
+ const { createSlugTSLMesh } = await Promise.resolve().then(function () { return require('./slugTSL.cjs'); });
219
+ return createSlugTSLMesh(gpuData, color);
220
+ }
221
+ catch {
222
+ return createSlugGLSLMesh(gpuData, { color, adaptiveSupersampling });
223
+ }
224
+ }
225
+ async function wrapCoreResult(coreResult, options) {
226
+ const color = parseColor(options.color);
227
+ const slugMesh = await createMesh(coreResult.gpuData, color, options.adaptiveSupersampling);
228
+ const geo = slugMesh.mesh.geometry;
229
+ geo.computeBoundingBox();
230
+ const center = new THREE__namespace.Vector3();
231
+ geo.boundingBox.getCenter(center);
232
+ geo.translate(-center.x, -center.y, -center.z);
233
+ const gcAttr = geo.getAttribute('glyphCenter');
234
+ if (gcAttr) {
235
+ const arr = gcAttr.array;
236
+ for (let i = 0; i < arr.length; i += 3) {
237
+ arr[i] -= center.x;
238
+ arr[i + 1] -= center.y;
239
+ arr[i + 2] -= center.z;
240
+ }
241
+ gcAttr.needsUpdate = true;
242
+ }
243
+ const group = new THREE__namespace.Group();
244
+ group.add(slugMesh.mesh);
245
+ return {
246
+ group,
247
+ mesh: slugMesh.mesh,
248
+ gpuData: coreResult.gpuData,
249
+ glyphs: coreResult.glyphs,
250
+ planeBounds: coreResult.planeBounds,
251
+ query: (queryOptions) => coreResult.query(queryOptions),
252
+ getLoadedFont: () => coreResult.getLoadedFont(),
253
+ measureTextWidth: (text, letterSpacing) => coreResult.measureTextWidth(text, letterSpacing),
254
+ update: async (newOptions) => {
255
+ const mergedOptions = { ...options };
256
+ for (const key in newOptions) {
257
+ const value = newOptions[key];
258
+ if (value !== undefined) {
259
+ mergedOptions[key] = value;
260
+ }
261
+ }
262
+ const newCore = await coreResult.update(newOptions);
263
+ return wrapCoreResult(newCore, mergedOptions);
264
+ },
265
+ dispose: () => {
266
+ slugMesh.dispose();
267
+ coreResult.dispose();
268
+ }
269
+ };
270
+ }
271
+ class Text {
272
+ static { this.setHarfBuzzPath = core.Text.setHarfBuzzPath; }
273
+ static { this.setHarfBuzzBuffer = core.Text.setHarfBuzzBuffer; }
274
+ static { this.init = core.Text.init; }
275
+ static { this.registerPattern = core.Text.registerPattern; }
276
+ static { this.preloadPatterns = core.Text.preloadPatterns; }
277
+ static { this.setMaxFontCacheMemoryMB = core.Text.setMaxFontCacheMemoryMB; }
278
+ static { this.enableWoff2 = core.Text.enableWoff2; }
279
+ static async create(options) {
280
+ const coreResult = await core.Text.create(options);
281
+ return wrapCoreResult(coreResult, options);
282
+ }
283
+ }
284
+
285
+ exports.Text = Text;
286
+ exports.createSlugGLSLMesh = createSlugGLSLMesh;
287
+ exports.unpackSlugVertices = unpackSlugVertices;
@@ -0,0 +1,264 @@
1
+ import { Text as Text$1 } from './core/index.js';
2
+ import * as THREE from 'three';
3
+
4
+ // Restructures the tightly-packed vertex buffer from SlugPacker into
5
+ // separate per-attribute arrays for GPU attribute binding
6
+ const FLOATS_PER_VERT = 20;
7
+ function unpackSlugVertices(gpuData) {
8
+ const vertCount = gpuData.shapeCount * 4;
9
+ const positions = new Float32Array(vertCount * 3);
10
+ const texcoords = new Float32Array(vertCount * 2);
11
+ const bandings = new Float32Array(vertCount * 4);
12
+ const glyphData = new Float32Array(vertCount * 4);
13
+ const colors = new Float32Array(vertCount * 4);
14
+ const srcF = gpuData.vertices;
15
+ const srcU = new Uint32Array(srcF.buffer, srcF.byteOffset, srcF.length);
16
+ for (let i = 0; i < vertCount; i++) {
17
+ const s = i * FLOATS_PER_VERT;
18
+ positions[i * 3] = srcF[s];
19
+ positions[i * 3 + 1] = srcF[s + 1];
20
+ positions[i * 3 + 2] = 0;
21
+ texcoords[i * 2] = srcF[s + 4];
22
+ texcoords[i * 2 + 1] = srcF[s + 5];
23
+ bandings[i * 4] = srcF[s + 12];
24
+ bandings[i * 4 + 1] = srcF[s + 13];
25
+ bandings[i * 4 + 2] = srcF[s + 14];
26
+ bandings[i * 4 + 3] = srcF[s + 15];
27
+ // Unpack glyph location and band metadata from bit-packed fields
28
+ const g0 = srcU[s + 6];
29
+ const g1 = srcU[s + 7];
30
+ glyphData[i * 4] = g0 & 0xFFFF;
31
+ glyphData[i * 4 + 1] = (g0 >>> 16) & 0xFFFF;
32
+ glyphData[i * 4 + 2] = g1 & 0xFFFF;
33
+ glyphData[i * 4 + 3] = (g1 >>> 16) & 0xFFFF;
34
+ colors[i * 4] = srcF[s + 16];
35
+ colors[i * 4 + 1] = srcF[s + 17];
36
+ colors[i * 4 + 2] = srcF[s + 18];
37
+ colors[i * 4 + 3] = srcF[s + 19];
38
+ }
39
+ // Per-glyph center: average of quad corner positions
40
+ const glyphCenters = new Float32Array(vertCount * 3);
41
+ const glyphIndices = new Float32Array(vertCount);
42
+ for (let g = 0; g < gpuData.shapeCount; g++) {
43
+ const base = g * 4;
44
+ let cx = 0, cy = 0;
45
+ for (let v = 0; v < 4; v++) {
46
+ cx += positions[(base + v) * 3];
47
+ cy += positions[(base + v) * 3 + 1];
48
+ }
49
+ cx *= 0.25;
50
+ cy *= 0.25;
51
+ for (let v = 0; v < 4; v++) {
52
+ const idx = base + v;
53
+ glyphCenters[idx * 3] = cx;
54
+ glyphCenters[idx * 3 + 1] = cy;
55
+ glyphCenters[idx * 3 + 2] = 0;
56
+ glyphIndices[idx] = g;
57
+ }
58
+ }
59
+ return { positions, texcoords, bandings, glyphData, colors, glyphCenters, glyphIndices };
60
+ }
61
+
62
+ 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";
63
+
64
+ // Slug shader source re-exports
65
+ // The .glsl/.wgsl files are the single source of truth, imported as strings
66
+ // at build time via the glslPlugin in rollup.config.js
67
+ // @ts-ignore - resolved by rollup glslPlugin
68
+ const fragmentShaderGLSL300 = fragGLSL;
69
+
70
+ // Slug GLSL adapter for Three.js, using RawShaderMaterial with the
71
+ // reference GLSL shaders. Works with both WebGLRenderer and
72
+ // WebGPURenderer (via GLSL-to-WGSL transpilation)
73
+ //
74
+ // Compared to the TSL adapter (slugTSL.ts):
75
+ // - Works with any Three.js renderer (no node-material dependency)
76
+ // - Uses native Uint32 band texture (no float conversion)
77
+ // - Supports GLSL animation injection via animationDeclarations/animationBody
78
+ // - Same tradeoff: no vertex dilation (may cause sub-pixel edge clipping at extreme zoom)
79
+ //
80
+ // Requires peer dependency: three
81
+ // @ts-ignore - three is a peer dependency
82
+ // Three.js GLSL3 mode prepends #version 300 es, so strip it from the raw shader
83
+ const fragShader = fragmentShaderGLSL300.replace(/^#version\s+300\s+es\s*\n/, '');
84
+ function buildVertexShader(options) {
85
+ const decl = options?.animationDeclarations ?? '';
86
+ const body = options?.animationBody ?? 'vec3 outPos = position;';
87
+ return `precision highp float;
88
+ precision highp int;
89
+
90
+ in vec3 position;
91
+ in vec2 slugTexcoord;
92
+ in vec4 slugBanding;
93
+ in vec4 slugGlyph;
94
+ in vec4 slugColor;
95
+ in vec3 glyphCenter;
96
+ in float glyphIndex;
97
+
98
+ uniform mat4 modelViewMatrix;
99
+ uniform mat4 projectionMatrix;
100
+ uniform float time;
101
+ ${decl}
102
+
103
+ out vec2 v_texcoord;
104
+ flat out vec4 v_banding;
105
+ flat out ivec4 v_glyph;
106
+ out vec4 v_color;
107
+
108
+ void main() {
109
+ v_texcoord = slugTexcoord;
110
+ v_banding = slugBanding;
111
+ v_glyph = ivec4(slugGlyph);
112
+ v_color = slugColor;
113
+
114
+ ${body}
115
+
116
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(outPos, 1.0);
117
+ }
118
+ `;
119
+ }
120
+ function createSlugGLSLMesh(gpuData, options) {
121
+ const attrs = unpackSlugVertices(gpuData);
122
+ const geo = new THREE.BufferGeometry();
123
+ geo.setAttribute('position', new THREE.Float32BufferAttribute(attrs.positions, 3));
124
+ geo.setAttribute('slugTexcoord', new THREE.Float32BufferAttribute(attrs.texcoords, 2));
125
+ geo.setAttribute('slugBanding', new THREE.Float32BufferAttribute(attrs.bandings, 4));
126
+ geo.setAttribute('slugGlyph', new THREE.Float32BufferAttribute(attrs.glyphData, 4));
127
+ geo.setAttribute('slugColor', new THREE.Float32BufferAttribute(attrs.colors, 4));
128
+ geo.setAttribute('glyphCenter', new THREE.Float32BufferAttribute(attrs.glyphCenters, 3));
129
+ geo.setAttribute('glyphIndex', new THREE.Float32BufferAttribute(attrs.glyphIndices, 1));
130
+ geo.setIndex(new THREE.BufferAttribute(gpuData.indices, 1));
131
+ // Curve texture: RGBA32F
132
+ const curveTex = new THREE.DataTexture(gpuData.curveTexture.data, gpuData.curveTexture.width, gpuData.curveTexture.height, THREE.RGBAFormat, THREE.FloatType);
133
+ curveTex.minFilter = THREE.NearestFilter;
134
+ curveTex.magFilter = THREE.NearestFilter;
135
+ curveTex.generateMipmaps = false;
136
+ curveTex.needsUpdate = true;
137
+ // Band texture: native RGBA32UI (no float conversion needed)
138
+ const bandTex = new THREE.DataTexture(gpuData.bandTexture.data, gpuData.bandTexture.width, gpuData.bandTexture.height);
139
+ bandTex.format = THREE.RGBAIntegerFormat;
140
+ bandTex.type = THREE.UnsignedIntType;
141
+ bandTex.internalFormat = 'RGBA32UI';
142
+ bandTex.minFilter = THREE.NearestFilter;
143
+ bandTex.magFilter = THREE.NearestFilter;
144
+ bandTex.generateMipmaps = false;
145
+ bandTex.needsUpdate = true;
146
+ const col = options?.color ?? { r: 1, g: 1, b: 1 };
147
+ const colorUniform = new THREE.Vector4(col.r, col.g, col.b, 1.0);
148
+ const uniforms = {
149
+ curveTexture: { value: curveTex },
150
+ bandTexture: { value: bandTex },
151
+ time: { value: 0 },
152
+ u_color: { value: colorUniform },
153
+ ...options?.uniforms,
154
+ };
155
+ const frag = options?.adaptiveSupersampling
156
+ ? '#define SLUG_ADAPTIVE_SUPERSAMPLE\n' + fragShader
157
+ : fragShader;
158
+ const material = new THREE.RawShaderMaterial({
159
+ glslVersion: THREE.GLSL3,
160
+ uniforms,
161
+ vertexShader: buildVertexShader(options),
162
+ fragmentShader: frag,
163
+ transparent: true,
164
+ depthWrite: false,
165
+ side: THREE.DoubleSide,
166
+ });
167
+ const mesh = new THREE.Mesh(geo, material);
168
+ return {
169
+ mesh,
170
+ uniforms,
171
+ setOffset(x, y, z = 0) {
172
+ mesh.position.set(x, y, z);
173
+ },
174
+ setColor(r, g, b) {
175
+ colorUniform.set(r, g, b, 1.0);
176
+ },
177
+ dispose() {
178
+ geo.dispose();
179
+ material.dispose();
180
+ curveTex.dispose();
181
+ bandTex.dispose();
182
+ }
183
+ };
184
+ }
185
+
186
+ function parseColor(color) {
187
+ if (!color)
188
+ return { r: 1, g: 1, b: 1 };
189
+ if (Array.isArray(color))
190
+ return { r: color[0], g: color[1], b: color[2] };
191
+ if (color.default)
192
+ return { r: color.default[0], g: color.default[1], b: color.default[2] };
193
+ return { r: 1, g: 1, b: 1 };
194
+ }
195
+ async function createMesh(gpuData, color, adaptiveSupersampling) {
196
+ try {
197
+ const { createSlugTSLMesh } = await import('./slugTSL.js');
198
+ return createSlugTSLMesh(gpuData, color);
199
+ }
200
+ catch {
201
+ return createSlugGLSLMesh(gpuData, { color, adaptiveSupersampling });
202
+ }
203
+ }
204
+ async function wrapCoreResult(coreResult, options) {
205
+ const color = parseColor(options.color);
206
+ const slugMesh = await createMesh(coreResult.gpuData, color, options.adaptiveSupersampling);
207
+ const geo = slugMesh.mesh.geometry;
208
+ geo.computeBoundingBox();
209
+ const center = new THREE.Vector3();
210
+ geo.boundingBox.getCenter(center);
211
+ geo.translate(-center.x, -center.y, -center.z);
212
+ const gcAttr = geo.getAttribute('glyphCenter');
213
+ if (gcAttr) {
214
+ const arr = gcAttr.array;
215
+ for (let i = 0; i < arr.length; i += 3) {
216
+ arr[i] -= center.x;
217
+ arr[i + 1] -= center.y;
218
+ arr[i + 2] -= center.z;
219
+ }
220
+ gcAttr.needsUpdate = true;
221
+ }
222
+ const group = new THREE.Group();
223
+ group.add(slugMesh.mesh);
224
+ return {
225
+ group,
226
+ mesh: slugMesh.mesh,
227
+ gpuData: coreResult.gpuData,
228
+ glyphs: coreResult.glyphs,
229
+ planeBounds: coreResult.planeBounds,
230
+ query: (queryOptions) => coreResult.query(queryOptions),
231
+ getLoadedFont: () => coreResult.getLoadedFont(),
232
+ measureTextWidth: (text, letterSpacing) => coreResult.measureTextWidth(text, letterSpacing),
233
+ update: async (newOptions) => {
234
+ const mergedOptions = { ...options };
235
+ for (const key in newOptions) {
236
+ const value = newOptions[key];
237
+ if (value !== undefined) {
238
+ mergedOptions[key] = value;
239
+ }
240
+ }
241
+ const newCore = await coreResult.update(newOptions);
242
+ return wrapCoreResult(newCore, mergedOptions);
243
+ },
244
+ dispose: () => {
245
+ slugMesh.dispose();
246
+ coreResult.dispose();
247
+ }
248
+ };
249
+ }
250
+ class Text {
251
+ static { this.setHarfBuzzPath = Text$1.setHarfBuzzPath; }
252
+ static { this.setHarfBuzzBuffer = Text$1.setHarfBuzzBuffer; }
253
+ static { this.init = Text$1.init; }
254
+ static { this.registerPattern = Text$1.registerPattern; }
255
+ static { this.preloadPatterns = Text$1.preloadPatterns; }
256
+ static { this.setMaxFontCacheMemoryMB = Text$1.setMaxFontCacheMemoryMB; }
257
+ static { this.enableWoff2 = Text$1.enableWoff2; }
258
+ static async create(options) {
259
+ const coreResult = await Text$1.create(options);
260
+ return wrapCoreResult(coreResult, options);
261
+ }
262
+ }
263
+
264
+ export { Text as T, createSlugGLSLMesh as c, unpackSlugVertices as u };
@@ -2,8 +2,28 @@
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
+ var THREE = require('three');
5
6
  var index = require('./index.cjs');
6
7
 
8
+ function _interopNamespaceDefault(e) {
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+
25
+ var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
26
+
7
27
  function deepEqual(a, b) {
8
28
  if (a === b)
9
29
  return true;
@@ -46,7 +66,7 @@ function useDeepCompareMemo(value) {
46
66
  }
47
67
 
48
68
  const TextInner = react.forwardRef(function TextInner(props, ref) {
49
- const { children, font, fillColor = '#ffffff', positionNode, colorNode, position = [0, 0, 0], rotation = [0, 0, 0], scale = [1, 1, 1], onLoad, onError, ...restOptions } = props;
69
+ const { children, font, fillColor = '#ffffff', position = [0, 0, 0], rotation = [0, 0, 0], scale = [1, 1, 1], positionNode, onLoad, onError, ...restOptions } = props;
50
70
  const memoizedTextOptions = useDeepCompareMemo(restOptions);
51
71
  const [group, setGroup] = react.useState(null);
52
72
  const [error, setError] = react.useState(null);
@@ -61,24 +81,22 @@ const TextInner = react.forwardRef(function TextInner(props, ref) {
61
81
  async function setup() {
62
82
  try {
63
83
  setError(null);
84
+ const color = new THREE__namespace.Color(fillColor);
64
85
  const resultPromise = opRef.current.catch(() => null).then(() => {
65
86
  if (cancelled)
66
87
  return null;
88
+ const colorArr = [color.r, color.g, color.b];
67
89
  return resultRef.current
68
90
  ? resultRef.current.update({
69
91
  text: children,
70
92
  font,
71
- color: fillColor,
72
- positionNode,
73
- colorNode,
93
+ color: colorArr,
74
94
  ...memoizedTextOptions
75
95
  })
76
96
  : index.Text.create({
77
97
  text: children,
78
98
  font,
79
- color: fillColor,
80
- positionNode,
81
- colorNode,
99
+ color: colorArr,
82
100
  ...memoizedTextOptions
83
101
  });
84
102
  });
@@ -90,6 +108,10 @@ const TextInner = react.forwardRef(function TextInner(props, ref) {
90
108
  result.dispose();
91
109
  return;
92
110
  }
111
+ if (positionNode && result.mesh?.material) {
112
+ result.mesh.material.positionNode = positionNode;
113
+ result.mesh.material.needsUpdate = true;
114
+ }
93
115
  const prev = resultRef.current;
94
116
  resultRef.current = result;
95
117
  setGroup(result.group);
@@ -112,7 +134,14 @@ const TextInner = react.forwardRef(function TextInner(props, ref) {
112
134
  return () => {
113
135
  cancelled = true;
114
136
  };
115
- }, [children, font, memoizedTextOptions, fillColor, positionNode, colorNode]);
137
+ }, [children, font, memoizedTextOptions, fillColor]);
138
+ react.useEffect(() => {
139
+ const result = resultRef.current;
140
+ if (result?.mesh?.material) {
141
+ result.mesh.material.positionNode = positionNode ?? null;
142
+ result.mesh.material.needsUpdate = true;
143
+ }
144
+ }, [positionNode]);
116
145
  react.useEffect(() => {
117
146
  return () => {
118
147
  resultRef.current?.dispose();