three-text 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE_THIRD_PARTY +15 -0
- package/README.md +73 -42
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/three/react.d.ts +2 -0
- package/dist/types/core/types.d.ts +2 -33
- package/dist/types/vector/{core.d.ts → core/index.d.ts} +8 -7
- package/dist/types/vector/index.d.ts +22 -25
- package/dist/types/vector/react.d.ts +4 -3
- package/dist/types/vector/slug/SlugPacker.d.ts +2 -0
- package/dist/types/vector/slug/curveUtils.d.ts +6 -0
- package/dist/types/vector/slug/index.d.ts +8 -0
- package/dist/types/vector/slug/shaderStrings.d.ts +4 -0
- package/dist/types/vector/slug/slugGLSL.d.ts +21 -0
- package/dist/types/vector/slug/slugTSL.d.ts +13 -0
- package/dist/types/vector/slug/types.d.ts +30 -0
- package/dist/types/vector/slug/unpackVertices.d.ts +11 -0
- package/dist/types/vector/webgl/index.d.ts +7 -3
- package/dist/types/vector/webgpu/index.d.ts +4 -4
- package/dist/vector/core/index.cjs +856 -0
- package/dist/vector/core/index.d.ts +63 -0
- package/dist/vector/core/index.js +854 -0
- package/dist/vector/core.cjs +4419 -240
- package/dist/vector/core.d.ts +361 -71
- package/dist/vector/core.js +4406 -226
- package/dist/vector/index.cjs +5 -229
- package/dist/vector/index.d.ts +45 -396
- package/dist/vector/index.js +3 -223
- package/dist/vector/index2.cjs +287 -0
- package/dist/vector/index2.js +264 -0
- package/dist/vector/react.cjs +37 -8
- package/dist/vector/react.d.ts +6 -3
- package/dist/vector/react.js +18 -8
- package/dist/vector/slugTSL.cjs +252 -0
- package/dist/vector/slugTSL.js +231 -0
- package/dist/vector/webgl/index.cjs +131 -201
- package/dist/vector/webgl/index.d.ts +19 -44
- package/dist/vector/webgl/index.js +131 -201
- package/dist/vector/webgpu/index.cjs +100 -283
- package/dist/vector/webgpu/index.d.ts +16 -45
- package/dist/vector/webgpu/index.js +100 -283
- package/package.json +6 -1
- package/dist/types/vector/GlyphVectorGeometryBuilder.d.ts +0 -26
- package/dist/types/vector/LoopBlinnGeometry.d.ts +0 -68
- package/dist/types/vector/loopBlinnTSL.d.ts +0 -22
- package/dist/vector/loopBlinnTSL.cjs +0 -229
- package/dist/vector/loopBlinnTSL.js +0 -207
|
@@ -1,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
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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: [
|
|
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:
|
|
66
|
-
attributes: [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
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:
|
|
186
|
-
entryPoint: '
|
|
187
|
-
buffers: [vertexBufferLayout]
|
|
74
|
+
module: vertModule,
|
|
75
|
+
entryPoint: 'vs_main',
|
|
76
|
+
buffers: [vertexBufferLayout],
|
|
188
77
|
},
|
|
189
78
|
fragment: {
|
|
190
|
-
module:
|
|
191
|
-
entryPoint: '
|
|
192
|
-
targets: [{
|
|
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
|
-
|
|
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 (
|
|
277
|
-
|
|
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,
|
|
282
|
-
if (!
|
|
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 (
|
|
311
|
-
|
|
312
|
-
|
|
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.
|
|
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;
|