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
@@ -1,317 +1,134 @@
1
+ var vertWGSL = "// WGSL port of the reference Slug vertex shader\n// Original HLSL by Eric Lengyel, MIT License, Copyright 2017\n\nstruct Uniforms {\n slug_matrix: mat4x4<f32>,\n slug_viewport: vec2<f32>,\n};\n\n@group(0) @binding(0) var<uniform> u: Uniforms;\n\nstruct VertexInput {\n @location(0) a_pos: vec4<f32>,\n @location(1) a_tex: vec4<f32>,\n @location(2) a_jac: vec4<f32>,\n @location(3) a_bnd: vec4<f32>,\n @location(4) a_col: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) color: vec4<f32>,\n @location(1) texcoord: vec2<f32>,\n @location(2) @interpolate(flat) banding: vec4<f32>,\n @location(3) @interpolate(flat) glyph: vec4<i32>,\n};\n\nfn SlugUnpack(tex: vec4<f32>, bnd: vec4<f32>) -> VertexOutput {\n var out: VertexOutput;\n let g = bitcast<vec2<u32>>(tex.zw);\n out.glyph = vec4<i32>(\n i32(g.x & 0xFFFFu),\n i32(g.x >> 16u),\n i32(g.y & 0xFFFFu),\n i32(g.y >> 16u)\n );\n out.banding = bnd;\n return out;\n}\n\nfn SlugDilate(\n pos: vec4<f32>, tex: vec4<f32>, jac: vec4<f32>,\n m0: vec4<f32>, m1: vec4<f32>, m3: vec4<f32>,\n dim: vec2<f32>\n) -> vec4<f32> {\n // Returns vec4(dilated_pos.xy, new_texcoord.xy)\n let n = normalize(pos.zw);\n let s = dot(m3.xy, pos.xy) + m3.w;\n let t = dot(m3.xy, n);\n\n let u_val = (s * dot(m0.xy, n) - t * (dot(m0.xy, pos.xy) + m0.w)) * dim.x;\n let v_val = (s * dot(m1.xy, n) - t * (dot(m1.xy, pos.xy) + m1.w)) * dim.y;\n\n let s2 = s * s;\n let st = s * t;\n let uv = u_val * u_val + v_val * v_val;\n let d = pos.zw * (s2 * (st + sqrt(uv)) / (uv - st * st));\n\n let vpos = pos.xy + d;\n let vtex = vec2<f32>(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\n return vec4<f32>(vpos, vtex);\n}\n\n@vertex\nfn vs_main(in: VertexInput) -> VertexOutput {\n let dilated = SlugDilate(\n in.a_pos, in.a_tex, in.a_jac,\n u.slug_matrix[0], u.slug_matrix[1], u.slug_matrix[3],\n u.slug_viewport\n );\n\n let p = dilated.xy;\n var out = SlugUnpack(in.a_tex, in.a_bnd);\n\n out.position = vec4<f32>(\n p.x * u.slug_matrix[0].x + p.y * u.slug_matrix[0].y + u.slug_matrix[0].w,\n p.x * u.slug_matrix[1].x + p.y * u.slug_matrix[1].y + u.slug_matrix[1].w,\n p.x * u.slug_matrix[2].x + p.y * u.slug_matrix[2].y + u.slug_matrix[2].w,\n p.x * u.slug_matrix[3].x + p.y * u.slug_matrix[3].y + u.slug_matrix[3].w\n );\n\n out.texcoord = dilated.zw;\n out.color = in.a_col;\n return out;\n}\n";
2
+
3
+ var fragWGSL = "// WGSL port of the reference Slug pixel shader\n// by Eric Lengyel, MIT License, Copyright 2017\n\nconst kLogBandTextureWidth: u32 = 12u;\n\n@group(0) @binding(1) var curveTexture: texture_2d<f32>;\n@group(0) @binding(2) var bandTexture: texture_2d<u32>;\n\nstruct FragmentInput {\n @location(0) color: vec4<f32>,\n @location(1) texcoord: vec2<f32>,\n @location(2) @interpolate(flat) banding: vec4<f32>,\n @location(3) @interpolate(flat) glyph: vec4<i32>,\n};\n\nfn CalcRootCode(y1: f32, y2: f32, y3: f32) -> u32 {\n let i1 = bitcast<u32>(y1) >> 31u;\n let i2 = bitcast<u32>(y2) >> 30u;\n let i3 = bitcast<u32>(y3) >> 29u;\n\n var shift = (i2 & 2u) | (i1 & ~2u);\n shift = (i3 & 4u) | (shift & ~4u);\n\n return (0x2E74u >> shift) & 0x0101u;\n}\n\nfn SolveHorizPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\n let a = p12.xy - p12.zw * 2.0 + p3;\n let b = p12.xy - p12.zw;\n let ra = 1.0 / a.y;\n let rb = 0.5 / b.y;\n\n let d = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\n var t1 = (b.y - d) * ra;\n var t2 = (b.y + d) * ra;\n\n if (abs(a.y) < 1.0 / 65536.0) {\n t1 = p12.y * rb;\n t2 = t1;\n }\n\n return vec2<f32>(\n (a.x * t1 - b.x * 2.0) * t1 + p12.x,\n (a.x * t2 - b.x * 2.0) * t2 + p12.x\n );\n}\n\nfn SolveVertPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\n let a = p12.xy - p12.zw * 2.0 + p3;\n let b = p12.xy - p12.zw;\n let ra = 1.0 / a.x;\n let rb = 0.5 / b.x;\n\n let d = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\n var t1 = (b.x - d) * ra;\n var t2 = (b.x + d) * ra;\n\n if (abs(a.x) < 1.0 / 65536.0) {\n t1 = p12.x * rb;\n t2 = t1;\n }\n\n return vec2<f32>(\n (a.y * t1 - b.y * 2.0) * t1 + p12.y,\n (a.y * t2 - b.y * 2.0) * t2 + p12.y\n );\n}\n\nfn CalcBandLoc(glyphLoc: vec2<i32>, offset: u32) -> vec2<i32> {\n var bandLoc = vec2<i32>(glyphLoc.x + i32(offset), glyphLoc.y);\n let bandShift = kLogBandTextureWidth;\n let bandMask = i32((1u << bandShift) - 1u);\n bandLoc.y += bandLoc.x >> bandShift;\n bandLoc.x &= bandMask;\n return bandLoc;\n}\n\nfn CalcCoverage(xcov: f32, ycov: f32, xwgt: f32, ywgt: f32) -> f32 {\n var coverage = max(\n abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0),\n min(abs(xcov), abs(ycov))\n );\n coverage = clamp(coverage, 0.0, 1.0);\n return coverage;\n}\n\nfn SlugRender(renderCoord: vec2<f32>, bandTransform: vec4<f32>, glyphData: vec4<i32>) -> f32 {\n let emsPerPixel = fwidth(renderCoord);\n let pixelsPerEm = 1.0 / emsPerPixel;\n\n var bandMax = glyphData.zw;\n bandMax.y &= 0x00FF;\n\n let bandIndex = clamp(\n vec2<i32>(renderCoord * bandTransform.xy + bandTransform.zw),\n vec2<i32>(0, 0),\n bandMax\n );\n let glyphLoc = glyphData.xy;\n\n var xcov = 0.0;\n var xwgt = 0.0;\n\n let hbandData = textureLoad(bandTexture, vec2<i32>(glyphLoc.x + bandIndex.y, glyphLoc.y), 0).xy;\n let hbandLoc = CalcBandLoc(glyphLoc, hbandData.y);\n\n for (var ci = 0; ci < i32(hbandData.x); ci++) {\n let curveLoc = vec2<i32>(textureLoad(bandTexture, vec2<i32>(hbandLoc.x + ci, hbandLoc.y), 0).xy);\n\n let p12 = textureLoad(curveTexture, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\n let p3 = textureLoad(curveTexture, vec2<i32>(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 let code = CalcRootCode(p12.y, p12.w, p3.y);\n if (code != 0u) {\n let 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 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 var ycov = 0.0;\n var ywgt = 0.0;\n\n let vbandData = textureLoad(bandTexture, vec2<i32>(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0).xy;\n let vbandLoc = CalcBandLoc(glyphLoc, vbandData.y);\n\n for (var ci = 0; ci < i32(vbandData.x); ci++) {\n let curveLoc = vec2<i32>(textureLoad(bandTexture, vec2<i32>(vbandLoc.x + ci, vbandLoc.y), 0).xy);\n let p12 = textureLoad(curveTexture, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\n let p3 = textureLoad(curveTexture, vec2<i32>(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 let code = CalcRootCode(p12.x, p12.z, p3.x);\n if (code != 0u) {\n let 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 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);\n}\n\n@fragment\nfn fs_main(in: FragmentInput) -> @location(0) vec4<f32> {\n let coverage = SlugRender(in.texcoord, in.banding, in.glyph);\n return in.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 vertexShaderWGSL = vertWGSL;
10
+ const fragmentShaderWGSL = fragWGSL;
11
+
1
12
  /// <reference types="@webgpu/types" />
2
- function createBufferWithData(device, data, usage) {
3
- const byteLength = Math.max(4, data.byteLength);
4
- const buffer = device.createBuffer({
5
- size: byteLength,
6
- usage,
7
- mappedAtCreation: true
8
- });
9
- if (data.byteLength > 0) {
10
- const mapped = new Uint8Array(buffer.getMappedRange());
11
- mapped.set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
12
- }
13
- buffer.unmap();
14
- return buffer;
15
- }
16
- function createGeometryResources(device, data) {
17
- const interiorPositionBuffer = createBufferWithData(device, data.interiorPositions, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST);
18
- const interiorIndexBuffer = createBufferWithData(device, data.interiorIndices, GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST);
19
- const curvePositionBuffer = createBufferWithData(device, data.curvePositions, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST);
20
- const fillPositionBuffer = createBufferWithData(device, data.fillPositions, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST);
21
- const fillIndexBuffer = createBufferWithData(device, data.fillIndices, GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST);
22
- return {
23
- interiorPositionBuffer,
24
- interiorIndexBuffer,
25
- interiorIndexCount: data.interiorIndices.length,
26
- interiorIndexFormat: data.interiorIndices instanceof Uint16Array ? 'uint16' : 'uint32',
27
- curvePositionBuffer,
28
- curveVertexCount: data.curvePositions.length / 3,
29
- fillPositionBuffer,
30
- fillIndexBuffer,
31
- fillIndexCount: data.fillIndices.length
32
- };
33
- }
34
- function destroyGeometryResources(resources) {
35
- resources.interiorPositionBuffer.destroy();
36
- resources.interiorIndexBuffer.destroy();
37
- resources.curvePositionBuffer.destroy();
38
- resources.fillPositionBuffer.destroy();
39
- resources.fillIndexBuffer.destroy();
40
- }
41
- function createWebGPUVectorRenderer(device, format, options = {}) {
42
- const depthStencilFormat = options.depthStencilFormat ?? 'depth24plus-stencil8';
43
- const sampleCount = options.sampleCount ?? 4;
13
+ function createResources(device, gpuData, format, sampleCount) {
44
14
  const uniformBuffer = device.createBuffer({
45
15
  size: 80,
46
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
16
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
17
+ });
18
+ const curveTexture = device.createTexture({
19
+ size: [gpuData.curveTexture.width, gpuData.curveTexture.height],
20
+ format: 'rgba32float',
21
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
47
22
  });
23
+ device.queue.writeTexture({ texture: curveTexture }, gpuData.curveTexture.data.buffer, { bytesPerRow: gpuData.curveTexture.width * 16 }, [gpuData.curveTexture.width, gpuData.curveTexture.height]);
24
+ const bandTexture = device.createTexture({
25
+ size: [gpuData.bandTexture.width, gpuData.bandTexture.height],
26
+ format: 'rgba32uint',
27
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
28
+ });
29
+ device.queue.writeTexture({ texture: bandTexture }, gpuData.bandTexture.data.buffer, { bytesPerRow: gpuData.bandTexture.width * 16 }, [gpuData.bandTexture.width, gpuData.bandTexture.height]);
30
+ const vertexBuffer = device.createBuffer({
31
+ size: gpuData.vertices.byteLength,
32
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
33
+ });
34
+ device.queue.writeBuffer(vertexBuffer, 0, gpuData.vertices.buffer);
35
+ const indexBuffer = device.createBuffer({
36
+ size: gpuData.indices.byteLength,
37
+ usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
38
+ });
39
+ device.queue.writeBuffer(indexBuffer, 0, gpuData.indices.buffer);
40
+ const vertModule = device.createShaderModule({ code: vertexShaderWGSL });
41
+ const fragModule = device.createShaderModule({ code: fragmentShaderWGSL });
48
42
  const bindGroupLayout = device.createBindGroupLayout({
49
43
  entries: [
50
- {
51
- binding: 0,
52
- visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
53
- buffer: { type: 'uniform' }
54
- }
55
- ]
56
- });
57
- const pipelineLayout = device.createPipelineLayout({
58
- bindGroupLayouts: [bindGroupLayout]
44
+ { binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: 'uniform' } },
45
+ { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'unfilterable-float' } },
46
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'uint' } },
47
+ ],
59
48
  });
60
49
  const bindGroup = device.createBindGroup({
61
50
  layout: bindGroupLayout,
62
- entries: [{ binding: 0, resource: { buffer: uniformBuffer } }]
51
+ entries: [
52
+ { binding: 0, resource: { buffer: uniformBuffer } },
53
+ { binding: 1, resource: curveTexture.createView() },
54
+ { binding: 2, resource: bandTexture.createView() },
55
+ ],
56
+ });
57
+ const pipelineLayout = device.createPipelineLayout({
58
+ bindGroupLayouts: [bindGroupLayout],
63
59
  });
60
+ const stride = 20 * 4;
64
61
  const vertexBufferLayout = {
65
- arrayStride: 12,
66
- attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x3' }]
67
- };
68
- const baseVertexShader = `
69
- struct Uniforms {
70
- mvp: mat4x4<f32>,
71
- color: vec4<f32>,
72
- }
73
-
74
- @group(0) @binding(0) var<uniform> uniforms: Uniforms;
75
-
76
- struct VertexInput {
77
- @location(0) position: vec3<f32>,
78
- }
79
-
80
- struct VertexOutput {
81
- @builtin(position) position: vec4<f32>,
82
- }
83
-
84
- @vertex
85
- fn main(input: VertexInput) -> VertexOutput {
86
- var output: VertexOutput;
87
- output.position = uniforms.mvp * vec4<f32>(input.position, 1.0);
88
- return output;
89
- }`;
90
- const curveVertexShader = `
91
- struct Uniforms {
92
- mvp: mat4x4<f32>,
93
- color: vec4<f32>,
94
- }
95
-
96
- @group(0) @binding(0) var<uniform> uniforms: Uniforms;
97
-
98
- struct VertexInput {
99
- @location(0) position: vec3<f32>,
100
- }
101
-
102
- struct VertexOutput {
103
- @builtin(position) position: vec4<f32>,
104
- @location(0) uv: vec2<f32>,
105
- }
106
-
107
- @vertex
108
- fn main(input: VertexInput, @builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
109
- let localVertex = vertexIndex % 3u;
110
- let u = f32(localVertex) * 0.5;
111
- var output: VertexOutput;
112
- output.uv = vec2<f32>(u, floor(u));
113
- output.position = uniforms.mvp * vec4<f32>(input.position, 1.0);
114
- return output;
115
- }`;
116
- const stencilFragmentShader = `
117
- @fragment
118
- fn main() -> @location(0) vec4<f32> {
119
- return vec4<f32>(1.0);
120
- }`;
121
- const curveFragmentShader = `
122
- struct FragmentInput {
123
- @location(0) uv: vec2<f32>,
124
- }
125
-
126
- @fragment
127
- fn main(input: FragmentInput) -> @location(0) vec4<f32> {
128
- let px = dpdx(input.uv);
129
- let py = dpdy(input.uv);
130
- let fx = 2.0 * input.uv.x * px.x - px.y;
131
- let fy = 2.0 * input.uv.x * py.x - py.y;
132
- let denom = sqrt(fx * fx + fy * fy);
133
-
134
- if (denom < 1e-6) {
135
- discard;
136
- }
137
-
138
- let sd = (input.uv.x * input.uv.x - input.uv.y) / denom;
139
- let alpha = clamp(0.5 - sd, 0.0, 1.0);
140
- if (alpha <= 0.0) {
141
- discard;
142
- }
143
-
144
- return vec4<f32>(1.0, 1.0, 1.0, alpha);
145
- }`;
146
- const colorFragmentShader = `
147
- struct Uniforms {
148
- mvp: mat4x4<f32>,
149
- color: vec4<f32>,
150
- }
151
-
152
- @group(0) @binding(0) var<uniform> uniforms: Uniforms;
153
-
154
- @fragment
155
- fn main() -> @location(0) vec4<f32> {
156
- return uniforms.color;
157
- }`;
158
- const baseStencilStateFront = {
159
- compare: 'always',
160
- failOp: 'keep',
161
- depthFailOp: 'keep',
162
- passOp: 'invert'
163
- };
164
- const baseStencilStateBack = {
165
- compare: 'always',
166
- failOp: 'keep',
167
- depthFailOp: 'keep',
168
- passOp: 'invert'
62
+ arrayStride: stride,
63
+ attributes: [
64
+ { shaderLocation: 0, offset: 0, format: 'float32x4' },
65
+ { shaderLocation: 1, offset: 16, format: 'float32x4' },
66
+ { shaderLocation: 2, offset: 32, format: 'float32x4' },
67
+ { shaderLocation: 3, offset: 48, format: 'float32x4' },
68
+ { shaderLocation: 4, offset: 64, format: 'float32x4' },
69
+ ],
169
70
  };
170
- const colorStencilStateFront = {
171
- compare: 'not-equal',
172
- failOp: 'keep',
173
- depthFailOp: 'keep',
174
- passOp: 'zero'
175
- };
176
- const colorStencilStateBack = {
177
- compare: 'not-equal',
178
- failOp: 'keep',
179
- depthFailOp: 'keep',
180
- passOp: 'zero'
181
- };
182
- const interiorPipeline = device.createRenderPipeline({
71
+ const pipeline = device.createRenderPipeline({
183
72
  layout: pipelineLayout,
184
73
  vertex: {
185
- module: device.createShaderModule({ code: baseVertexShader }),
186
- entryPoint: 'main',
187
- buffers: [vertexBufferLayout]
74
+ module: vertModule,
75
+ entryPoint: 'vs_main',
76
+ buffers: [vertexBufferLayout],
188
77
  },
189
78
  fragment: {
190
- module: device.createShaderModule({ code: stencilFragmentShader }),
191
- entryPoint: 'main',
192
- targets: [{ format, writeMask: 0 }]
79
+ module: fragModule,
80
+ entryPoint: 'fs_main',
81
+ targets: [{
82
+ format,
83
+ blend: {
84
+ color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
85
+ alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
86
+ },
87
+ }],
193
88
  },
194
89
  primitive: {
195
90
  topology: 'triangle-list',
196
- cullMode: 'none'
197
- },
198
- depthStencil: {
199
- format: depthStencilFormat,
200
- depthWriteEnabled: false,
201
- depthCompare: 'always',
202
- stencilFront: baseStencilStateFront,
203
- stencilBack: baseStencilStateBack,
204
- stencilReadMask: 0xff,
205
- stencilWriteMask: 0xff
91
+ cullMode: 'none',
206
92
  },
207
93
  multisample: {
208
94
  count: sampleCount,
209
- alphaToCoverageEnabled: false
210
- }
211
- });
212
- const curvePipeline = device.createRenderPipeline({
213
- layout: pipelineLayout,
214
- vertex: {
215
- module: device.createShaderModule({ code: curveVertexShader }),
216
- entryPoint: 'main',
217
- buffers: [vertexBufferLayout]
218
95
  },
219
- fragment: {
220
- module: device.createShaderModule({ code: curveFragmentShader }),
221
- entryPoint: 'main',
222
- targets: [{ format, writeMask: 0 }]
223
- },
224
- primitive: {
225
- topology: 'triangle-list',
226
- cullMode: 'none'
227
- },
228
- depthStencil: {
229
- format: depthStencilFormat,
230
- depthWriteEnabled: false,
231
- depthCompare: 'always',
232
- stencilFront: baseStencilStateFront,
233
- stencilBack: baseStencilStateBack,
234
- stencilReadMask: 0xff,
235
- stencilWriteMask: 0xff
236
- },
237
- multisample: {
238
- count: sampleCount,
239
- alphaToCoverageEnabled: true
240
- }
241
- });
242
- const colorPipeline = device.createRenderPipeline({
243
- layout: pipelineLayout,
244
- vertex: {
245
- module: device.createShaderModule({ code: baseVertexShader }),
246
- entryPoint: 'main',
247
- buffers: [vertexBufferLayout]
248
- },
249
- fragment: {
250
- module: device.createShaderModule({ code: colorFragmentShader }),
251
- entryPoint: 'main',
252
- targets: [{ format, writeMask: GPUColorWrite.ALL }]
253
- },
254
- primitive: {
255
- topology: 'triangle-list',
256
- cullMode: 'none'
257
- },
258
- depthStencil: {
259
- format: depthStencilFormat,
260
- depthWriteEnabled: false,
261
- depthCompare: 'always',
262
- stencilFront: colorStencilStateFront,
263
- stencilBack: colorStencilStateBack,
264
- stencilReadMask: 0xff,
265
- stencilWriteMask: 0xff
266
- },
267
- multisample: {
268
- count: sampleCount,
269
- alphaToCoverageEnabled: false
270
- }
271
96
  });
97
+ return { pipeline, bindGroup, vertexBuffer, indexBuffer, uniformBuffer, indexCount: gpuData.indices.length };
98
+ }
99
+ function draw(device, pass, res, mvpMatrix, viewportWidth, viewportHeight) {
272
100
  const uniformData = new Float32Array(20);
273
- let geometryResources = null;
101
+ uniformData.set(mvpMatrix, 0);
102
+ uniformData[16] = viewportWidth;
103
+ uniformData[17] = viewportHeight;
104
+ device.queue.writeBuffer(res.uniformBuffer, 0, uniformData);
105
+ pass.setPipeline(res.pipeline);
106
+ pass.setBindGroup(0, res.bindGroup);
107
+ pass.setVertexBuffer(0, res.vertexBuffer);
108
+ pass.setIndexBuffer(res.indexBuffer, 'uint16');
109
+ pass.drawIndexed(res.indexCount);
110
+ }
111
+ function createWebGPUVectorRenderer(device, format, options) {
112
+ let resources = null;
113
+ const sampleCount = options?.sampleCount ?? 1;
274
114
  return {
275
115
  setGeometry(data) {
276
- if (geometryResources) {
277
- destroyGeometryResources(geometryResources);
278
- }
279
- geometryResources = createGeometryResources(device, data);
116
+ if (resources)
117
+ this.dispose();
118
+ resources = createResources(device, data, format, sampleCount);
280
119
  },
281
- render(passEncoder, mvp, color) {
282
- if (!geometryResources) {
120
+ render(passEncoder, mvp, _color, viewportWidth, viewportHeight) {
121
+ if (!resources)
283
122
  return;
284
- }
285
- uniformData.set(mvp, 0);
286
- uniformData[16] = color[0];
287
- uniformData[17] = color[1];
288
- uniformData[18] = color[2];
289
- uniformData[19] = color[3];
290
- device.queue.writeBuffer(uniformBuffer, 0, uniformData);
291
- passEncoder.setBindGroup(0, bindGroup);
292
- passEncoder.setStencilReference(0);
293
- if (geometryResources.interiorIndexCount > 0) {
294
- passEncoder.setPipeline(interiorPipeline);
295
- passEncoder.setVertexBuffer(0, geometryResources.interiorPositionBuffer);
296
- passEncoder.setIndexBuffer(geometryResources.interiorIndexBuffer, geometryResources.interiorIndexFormat);
297
- passEncoder.drawIndexed(geometryResources.interiorIndexCount);
298
- }
299
- if (geometryResources.curveVertexCount > 0) {
300
- passEncoder.setPipeline(curvePipeline);
301
- passEncoder.setVertexBuffer(0, geometryResources.curvePositionBuffer);
302
- passEncoder.draw(geometryResources.curveVertexCount);
303
- }
304
- passEncoder.setPipeline(colorPipeline);
305
- passEncoder.setVertexBuffer(0, geometryResources.fillPositionBuffer);
306
- passEncoder.setIndexBuffer(geometryResources.fillIndexBuffer, 'uint32');
307
- passEncoder.drawIndexed(geometryResources.fillIndexCount);
123
+ draw(device, passEncoder, resources, mvp, viewportWidth ?? 1, viewportHeight ?? 1);
308
124
  },
309
125
  dispose() {
310
- if (geometryResources) {
311
- destroyGeometryResources(geometryResources);
312
- geometryResources = null;
313
- }
314
- uniformBuffer.destroy();
126
+ if (!resources)
127
+ return;
128
+ resources.vertexBuffer.destroy();
129
+ resources.indexBuffer.destroy();
130
+ resources.uniformBuffer.destroy();
131
+ resources = null;
315
132
  }
316
133
  };
317
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-text",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "3D mesh font geometry and text layout engine for the web",
5
5
  "main": "dist/three/index.cjs",
6
6
  "module": "dist/three/index.js",
@@ -58,6 +58,11 @@
58
58
  "import": "./dist/webgpu/index.js",
59
59
  "require": "./dist/webgpu/index.cjs"
60
60
  },
61
+ "./vector/core": {
62
+ "types": "./dist/vector/core/index.d.ts",
63
+ "import": "./dist/vector/core/index.js",
64
+ "require": "./dist/vector/core/index.cjs"
65
+ },
61
66
  "./vector/webgl": {
62
67
  "types": "./dist/vector/webgl/index.d.ts",
63
68
  "import": "./dist/vector/webgl/index.js",
@@ -1,26 +0,0 @@
1
- import type { GlyphCluster, LoadedFont, GlyphOutline, VectorGlyphInfo, VectorTextGeometryInfo } from '../core/types';
2
- import { Cache } from '../utils/Cache';
3
- import type { LoopBlinnInput } from './LoopBlinnGeometry';
4
- export declare class GlyphVectorGeometryBuilder {
5
- private outlineCache;
6
- private drawCallbacks;
7
- private collector;
8
- private loadedFont;
9
- private fontId;
10
- private cacheKeyPrefix;
11
- private emptyGlyphs;
12
- constructor(loadedFont: LoadedFont, cache?: Cache<string, GlyphOutline>);
13
- setFontId(fontId: string): void;
14
- clearCache(): void;
15
- getCacheStats(): import("../utils/Cache").CacheStats;
16
- private collectUniqueGlyphIds;
17
- private computePlaneBounds;
18
- buildForLoopBlinn(clustersByLine: GlyphCluster[][], scale: number): {
19
- loopBlinnInput: LoopBlinnInput;
20
- glyphs: VectorGlyphInfo[];
21
- };
22
- private getOutlineForGlyph;
23
- private writeSegmentToTexture;
24
- private segmentAABB;
25
- buildVectorGeometry(clustersByLine: GlyphCluster[][], scale: number, segmentTextureWidth?: number, bandCount?: number, tileCountX?: number, tileCountY?: number): Omit<VectorTextGeometryInfo, 'query'>;
26
- }
@@ -1,68 +0,0 @@
1
- import type { BoundingBox } from '../utils/vectors';
2
- export interface QuadraticSegment {
3
- p0x: number;
4
- p0y: number;
5
- p1x: number;
6
- p1y: number;
7
- p2x: number;
8
- p2y: number;
9
- }
10
- interface ContourVertex {
11
- x: number;
12
- y: number;
13
- }
14
- export interface VectorContour {
15
- vertices: ContourVertex[];
16
- segments: QuadraticSegment[];
17
- }
18
- export interface VectorGlyphAttributes {
19
- glyphCenter: Float32Array;
20
- glyphIndex: Float32Array;
21
- glyphProgress: Float32Array;
22
- glyphLineIndex: Float32Array;
23
- glyphBaselineY: Float32Array;
24
- }
25
- export interface GlyphRange {
26
- interiorIndexStart: number;
27
- interiorIndexCount: number;
28
- curveVertexStart: number;
29
- curveVertexCount: number;
30
- }
31
- export interface VectorGeometryData {
32
- interiorPositions: Float32Array;
33
- interiorIndices: Uint32Array;
34
- curvePositions: Float32Array;
35
- fillPositions: Float32Array;
36
- fillIndices: Uint32Array;
37
- glyphRanges: GlyphRange[];
38
- interiorGlyphAttrs?: VectorGlyphAttributes;
39
- curveGlyphAttrs?: VectorGlyphAttributes;
40
- fillGlyphAttrs?: VectorGlyphAttributes;
41
- planeBounds: BoundingBox;
42
- stats: {
43
- glyphCount: number;
44
- contourCount: number;
45
- interiorTriangleCount: number;
46
- curveTriangleCount: number;
47
- };
48
- }
49
- export interface LoopBlinnGlyphInput {
50
- offsetX: number;
51
- offsetY: number;
52
- segments: QuadraticSegment[];
53
- bounds: {
54
- minX: number;
55
- minY: number;
56
- maxX: number;
57
- maxY: number;
58
- };
59
- lineIndex: number;
60
- baselineY: number;
61
- }
62
- export interface LoopBlinnInput {
63
- glyphs: LoopBlinnGlyphInput[];
64
- planeBounds: BoundingBox;
65
- }
66
- export declare function extractContours(segments: QuadraticSegment[]): VectorContour[];
67
- export declare function buildVectorGeometry(input: LoopBlinnInput): VectorGeometryData;
68
- export {};
@@ -1,22 +0,0 @@
1
- import * as THREE from 'three';
2
- import type { VectorGeometryData } from './LoopBlinnGeometry';
3
- export interface VectorMeshOptions {
4
- color?: THREE.ColorRepresentation;
5
- positionNode?: any;
6
- colorNode?: any;
7
- center?: boolean;
8
- }
9
- export interface VectorMeshes {
10
- group: THREE.Group;
11
- interiorMesh: THREE.Object3D;
12
- curveMesh: THREE.Object3D;
13
- fillMesh: THREE.Mesh;
14
- interiorGeometry: THREE.BufferGeometry;
15
- curveGeometry: THREE.BufferGeometry;
16
- fillGeometry: THREE.BufferGeometry;
17
- setOffset(x: number, y: number, z?: number): void;
18
- updateMaterials(options?: VectorMeshOptions): void;
19
- dispose(): void;
20
- }
21
- export declare const loopBlinnFragment: any;
22
- export declare function createVectorMeshes(data: VectorGeometryData, options?: VectorMeshOptions | THREE.ColorRepresentation): VectorMeshes;