three-text 0.4.11 → 0.4.12
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/README.md +257 -38
- package/dist/index.cjs +3420 -3436
- package/dist/index.d.ts +163 -9
- package/dist/index.js +3416 -3437
- package/dist/index.min.cjs +667 -666
- package/dist/index.min.js +670 -669
- package/dist/index.umd.js +3557 -3565
- package/dist/index.umd.min.js +752 -746
- package/dist/p5/index.cjs +2738 -5
- package/dist/p5/index.js +2738 -5
- package/dist/slug/index.cjs +380 -0
- package/dist/slug/index.d.ts +62 -0
- package/dist/slug/index.js +374 -0
- package/dist/three/index.cjs +50 -35
- package/dist/three/index.js +50 -35
- package/dist/three/react.cjs +5 -2
- package/dist/three/react.d.ts +66 -120
- package/dist/three/react.js +6 -3
- package/dist/types/core/Text.d.ts +3 -10
- package/dist/types/core/cache/sharedCaches.d.ts +2 -1
- package/dist/types/core/shaping/DrawCallbacks.d.ts +11 -3
- package/dist/types/core/shaping/TextShaper.d.ts +1 -5
- package/dist/types/core/types.d.ts +84 -0
- package/dist/types/index.d.ts +7 -3
- package/dist/types/{core/cache → mesh}/GlyphContourCollector.d.ts +4 -4
- package/dist/types/{core/cache → mesh}/GlyphGeometryBuilder.d.ts +5 -5
- package/dist/types/mesh/MeshGeometryBuilder.d.ts +18 -0
- package/dist/types/{core → mesh}/geometry/BoundaryClusterer.d.ts +1 -1
- package/dist/types/{core → mesh}/geometry/Extruder.d.ts +1 -1
- package/dist/types/{core → mesh}/geometry/PathOptimizer.d.ts +1 -1
- package/dist/types/{core → mesh}/geometry/Polygonizer.d.ts +1 -1
- package/dist/types/{core → mesh}/geometry/Tessellator.d.ts +1 -1
- package/dist/types/react/utils.d.ts +2 -0
- package/dist/types/vector/GlyphOutlineCollector.d.ts +25 -0
- package/dist/types/vector/GlyphVectorGeometryBuilder.d.ts +26 -0
- package/dist/types/vector/LoopBlinnGeometry.d.ts +68 -0
- package/dist/types/vector/index.d.ts +29 -0
- package/dist/types/vector/loopBlinnTSL.d.ts +11 -0
- package/dist/types/vector/react.d.ts +24 -0
- package/dist/types/vector/webgl/index.d.ts +7 -0
- package/dist/types/vector/webgpu/index.d.ts +11 -0
- package/dist/vector/index.cjs +1458 -0
- package/dist/vector/index.d.ts +122 -0
- package/dist/vector/index.js +1434 -0
- package/dist/vector/react.cjs +153 -0
- package/dist/vector/react.d.ts +317 -0
- package/dist/vector/react.js +132 -0
- package/dist/vector/types/slug-lib/src/SlugPacker.d.ts +17 -0
- package/dist/vector/types/slug-lib/src/WebGL2Renderer.d.ts +21 -0
- package/dist/vector/types/slug-lib/src/WebGPURenderer.d.ts +16 -0
- package/dist/vector/types/slug-lib/src/index.d.ts +15 -0
- package/dist/vector/types/slug-lib/src/shaderStrings.d.ts +9 -0
- package/dist/vector/types/slug-lib/src/types.d.ts +34 -0
- package/dist/vector/types/src/core/types.d.ts +381 -0
- package/dist/vector/types/src/hyphenation/HyphenationPatternLoader.d.ts +2 -0
- package/dist/vector/types/src/hyphenation/index.d.ts +7 -0
- package/dist/vector/types/src/hyphenation/types.d.ts +6 -0
- package/dist/vector/types/src/utils/Cache.d.ts +14 -0
- package/dist/vector/types/src/utils/vectors.d.ts +75 -0
- package/dist/vector/types/src/vector/VectorDataBuilder.d.ts +30 -0
- package/dist/vector/types/src/vector/VectorThreeAdapter.d.ts +27 -0
- package/dist/vector/types/src/vector/index.d.ts +15 -0
- package/dist/vector/webgl/index.cjs +229 -0
- package/dist/vector/webgl/index.d.ts +53 -0
- package/dist/vector/webgl/index.js +227 -0
- package/dist/vector/webgpu/index.cjs +321 -0
- package/dist/vector/webgpu/index.d.ts +57 -0
- package/dist/vector/webgpu/index.js +319 -0
- package/dist/webgl-vector/index.cjs +243 -0
- package/dist/webgl-vector/index.d.ts +34 -0
- package/dist/webgl-vector/index.js +241 -0
- package/dist/webgpu-vector/index.cjs +336 -0
- package/dist/webgpu-vector/index.d.ts +38 -0
- package/dist/webgpu-vector/index.js +334 -0
- package/package.json +48 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.typescriptlang.org/)
|
|
5
5
|
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
6
6
|
|
|
7
|
-
High fidelity 3D
|
|
7
|
+
High fidelity 3D text rendering and layout for the web
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
@@ -13,22 +13,21 @@ High fidelity 3D mesh font geometry and text layout engine for the web
|
|
|
13
13
|
## Overview
|
|
14
14
|
|
|
15
15
|
> [!CAUTION]
|
|
16
|
-
> three-text is
|
|
16
|
+
> three-text is in alpha release and the API may break rapidly. This warning will likely last until summer of 2026. If API stability is important to you, consider pinning your version. Community feedback is encouraged; please open an issue if you have any suggestions or feedback, thank you
|
|
17
17
|
|
|
18
|
-
**three-text** is a 3D
|
|
18
|
+
**three-text** is a 3D font rendering and text layout library for the web. It supports TTF, OTF, WOFF, and WOFF2 font files and uses [TeX](https://en.wikipedia.org/wiki/TeX)-based parameters for layout, with support for CJK and RTL scripts. Two rendering pipelines share the same core: **mesh** (extruded, lit, deformable geometry) and **vector** (resolution-independent Loop-Blinn outlines). Contours and geometries are cached per glyph for low CPU overhead in text-heavy scenes. Variable fonts are supported
|
|
19
19
|
|
|
20
|
-
The library has a framework-agnostic core
|
|
20
|
+
The library has a framework-agnostic core with lightweight adapters for [Three.js](https://threejs.org), [React Three Fiber](https://docs.pmnd.rs/react-three-fiber), [p5.js](https://p5js.org), [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API), and [WebGPU](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API)
|
|
21
21
|
|
|
22
|
-
Under the hood, three-text relies on [harfbuzzjs](https://github.com/harfbuzz/harfbuzzjs) (based on [HarfBuzz](https://github.com/harfbuzz/harfbuzz) by Behdad Esfahbod et al) for text shaping, [Knuth-Plass](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf) line breaking (with [SILE](https://github.com/sile-typesetter/sile/blob/master/core/break.lua) being the
|
|
22
|
+
Under the hood, three-text relies on a core of [harfbuzzjs](https://github.com/harfbuzz/harfbuzzjs) (based on [HarfBuzz](https://github.com/harfbuzz/harfbuzz) by Behdad Esfahbod et al) for text shaping, [Knuth-Plass](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf) line breaking (with [SILE](https://github.com/sile-typesetter/sile/blob/master/core/break.lua) and LuaTex being the closest modern references), [Liang](https://tug.org/docs/liang/liang-thesis.pdf) hyphenation and the [TeX hyphenation patterns](https://github.com/hyphenation/tex-hyphen), and [woff-lib](https://github.com/countertype/woff-lib) for optional WOFF2 support. The mesh text pipeline uses [libtess-ts](https://github.com/countertype/libtess-ts) (a port of the [GLU tessellator](https://www.songho.ca/opengl/gl_tessellation.html) by Eric Veach) for removing overlaps and triangulation, adaptive curve polygonization from Maxim Shemanarev's [Anti-Grain Geometry](https://web.archive.org/web/20060128212843/http://www.antigrain.com/research/adaptive_bezier/index.html), and [Visvalingam-Whyatt](https://hull-repository.worktribe.com/preview/376364/000870493786962263.pdf) [line simplification](https://bost.ocks.org/mike/simplify/). The vector pipeline uses [Loop-Blinn](https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf) resolution-independent curve rendering with [Kokojima et al.](https://dl.acm.org/doi/10.1145/1179849.1179997) stencil filling
|
|
23
23
|
|
|
24
24
|
## Table of contents
|
|
25
25
|
|
|
26
26
|
- [Overview](#overview)
|
|
27
27
|
- [Getting started](#getting-started)
|
|
28
28
|
- [Architecture](#architecture)
|
|
29
|
-
- [
|
|
30
|
-
- [
|
|
31
|
-
- [p5.js](#p5js)
|
|
29
|
+
- [Mesh vs vector](#mesh-vs-vector)
|
|
30
|
+
- [Basic Usage](#basic-usage)
|
|
32
31
|
- [Coordinate systems](#coordinate-systems)
|
|
33
32
|
- [Development and examples](#development-and-examples)
|
|
34
33
|
- [Why three-text?](#why-three-text)
|
|
@@ -63,22 +62,44 @@ npm install three
|
|
|
63
62
|
|
|
64
63
|
three-text has a framework-agnostic core that processes fonts and generates geometry data. Lightweight adapters convert this data to framework-specific formats:
|
|
65
64
|
|
|
66
|
-
- **`three-text`** - Three.js adapter (default export, returns BufferGeometry)
|
|
67
|
-
- **`three-text/
|
|
68
|
-
- **`three-text/
|
|
65
|
+
- **`three-text`** - Three.js adapter (default export, returns `BufferGeometry`)
|
|
66
|
+
- **`three-text/mesh`** - Same as above (explicit alias)
|
|
67
|
+
- **`three-text/mesh/react`** - React Three Fiber component for extruded mesh text
|
|
68
|
+
- **`three-text/three`** - Deprecated, use `three-text/mesh`
|
|
69
|
+
- **`three-text/three/react`** - Deprecated, use `three-text/mesh/react`
|
|
70
|
+
- **`three-text/mesh/webgl`** - WebGL mesh buffer utility
|
|
71
|
+
- **`three-text/mesh/webgpu`** - WebGPU mesh buffer utility
|
|
72
|
+
- **`three-text/mesh/p5`** - p5.js adapter
|
|
69
73
|
- **`three-text/core`** - Framework-agnostic core (returns raw arrays)
|
|
70
|
-
- **`three-text/
|
|
71
|
-
- **`three-text/
|
|
72
|
-
- **`three-text/
|
|
74
|
+
- **`three-text/vector`** - Vector rendering (Loop-Blinn and Kokojima stencil fill, resolution-independent)
|
|
75
|
+
- **`three-text/vector/react`** - React Three Fiber component for vector text
|
|
76
|
+
- **`three-text/vector/webgl`** - WebGL vector renderer
|
|
77
|
+
- **`three-text/vector/webgpu`** - WebGPU vector renderer
|
|
78
|
+
- **`three-text/webgl`** - Deprecated, use `three-text/mesh/webgl`
|
|
79
|
+
- **`three-text/webgpu`** - Deprecated, use `three-text/mesh/webgpu`
|
|
80
|
+
- **`three-text/p5`** - Deprecated, use `three-text/mesh/p5`
|
|
73
81
|
|
|
74
|
-
Most users will just `import { Text } from 'three-text'` for Three.js projects
|
|
82
|
+
Most users will just `import { Text } from 'three-text'` for Three.js projects with mesh, or `import { Text } from 'three-text/vector'` for vector text
|
|
83
|
+
|
|
84
|
+
### Mesh vs vector
|
|
85
|
+
|
|
86
|
+
The library offers two rendering modes that share the same core (HarfBuzz shaping, Knuth-Plass justification, glyph caching):
|
|
87
|
+
|
|
88
|
+
- **Mesh** (`three-text` (default) / `three-text/mesh`): triangulated geometry you can extrude, light, and shade. Use for 3D text, text in a scene graph, or anywhere you need depth
|
|
89
|
+
- **Vector** (`three-text/vector`): resolution-independent rendering on the GPU without tessellation. Use for text that needs to stay sharp at arbitrary zoom
|
|
90
|
+
|
|
91
|
+
Both can be used in the same project from separate entry points
|
|
92
|
+
|
|
93
|
+
**React Three Fiber:** both `three-text/mesh/react` and `three-text/vector/react` export `Text`
|
|
75
94
|
|
|
76
95
|
### Basic Usage
|
|
77
96
|
|
|
78
|
-
#### Three.js
|
|
97
|
+
#### Mesh (Three.js)
|
|
98
|
+
|
|
99
|
+
Extruded `BufferGeometry` — light, shade, and deform as a normal mesh:
|
|
79
100
|
|
|
80
101
|
```javascript
|
|
81
|
-
import { Text } from 'three-text
|
|
102
|
+
import { Text } from 'three-text';
|
|
82
103
|
import { woff2Decode } from 'woff-lib/woff2/decode';
|
|
83
104
|
import * as THREE from 'three';
|
|
84
105
|
|
|
@@ -94,11 +115,58 @@ const mesh = new THREE.Mesh(result.geometry, material);
|
|
|
94
115
|
scene.add(mesh);
|
|
95
116
|
```
|
|
96
117
|
|
|
97
|
-
####
|
|
118
|
+
#### Vector (Three.js)
|
|
119
|
+
|
|
120
|
+
Resolution-independent outlines via Loop-Blinn stencil passes (see [Vector rendering](#vector-rendering)):
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
import { Text } from 'three-text/vector';
|
|
124
|
+
import { woff2Decode } from 'woff-lib/woff2/decode';
|
|
125
|
+
|
|
126
|
+
Text.setHarfBuzzPath('/hb/hb.wasm');
|
|
127
|
+
Text.enableWoff2(woff2Decode);
|
|
128
|
+
const result = await Text.create({
|
|
129
|
+
text: 'Hello Vector',
|
|
130
|
+
font: '/fonts/Font.woff2',
|
|
131
|
+
size: 72
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const vectorData = result.geometryData;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Use `createVectorMeshes(vectorData)` from `three-text/vector` for TSL / `WebGPURenderer`, or build your own stencil materials (see [Vector rendering](#vector-rendering))
|
|
138
|
+
|
|
139
|
+
#### Mesh + vector in one scene
|
|
140
|
+
|
|
141
|
+
Alias one import to avoid the name collision between `Text` components. The entry points share a core (shaping, layout, font cache) so you can mix them freely — fonts load once regardless of which entry point requests them:
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
import { Text as MeshText } from 'three-text';
|
|
145
|
+
import { Text as VectorText, createVectorMeshes } from 'three-text/vector';
|
|
146
|
+
|
|
147
|
+
MeshText.setHarfBuzzPath('/hb/hb.wasm');
|
|
148
|
+
|
|
149
|
+
const heading = await MeshText.create({
|
|
150
|
+
text: 'Heading',
|
|
151
|
+
font: '/fonts/Font.woff2',
|
|
152
|
+
size: 72, depth: 10
|
|
153
|
+
});
|
|
154
|
+
scene.add(new THREE.Mesh(heading.geometry, material));
|
|
155
|
+
|
|
156
|
+
const caption = await VectorText.create({
|
|
157
|
+
text: 'Caption text',
|
|
158
|
+
font: '/fonts/Font.woff2',
|
|
159
|
+
size: 24
|
|
160
|
+
});
|
|
161
|
+
const { interiorMesh, curveMesh, fillMesh } = createVectorMeshes(caption.geometryData);
|
|
162
|
+
scene.add(interiorMesh, curveMesh, fillMesh);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### React Three Fiber — mesh
|
|
98
166
|
|
|
99
167
|
```jsx
|
|
100
168
|
import { Canvas } from '@react-three/fiber';
|
|
101
|
-
import { Text } from 'three-text/
|
|
169
|
+
import { Text } from 'three-text/mesh/react';
|
|
102
170
|
|
|
103
171
|
Text.setHarfBuzzPath('/hb/hb.wasm');
|
|
104
172
|
|
|
@@ -114,6 +182,72 @@ function App() {
|
|
|
114
182
|
}
|
|
115
183
|
```
|
|
116
184
|
|
|
185
|
+
#### React Three Fiber — vector
|
|
186
|
+
|
|
187
|
+
The vector `Text` builds three internal meshes (interior / curve / fill) with TSL stencil materials. Requires a renderer that supports `MeshBasicNodeMaterial` (Three.js r170+). With WebGPU, pass a `WebGPURenderer` with `stencil: true` and await `init()` (see the [three.js WebGPU examples](https://threejs.org/examples/?q=webgpu)):
|
|
188
|
+
|
|
189
|
+
```jsx
|
|
190
|
+
import { Canvas } from '@react-three/fiber';
|
|
191
|
+
import * as THREE from 'three/webgpu';
|
|
192
|
+
import { Text } from 'three-text/vector/react';
|
|
193
|
+
|
|
194
|
+
Text.setHarfBuzzPath('/hb/hb.wasm');
|
|
195
|
+
|
|
196
|
+
function App() {
|
|
197
|
+
return (
|
|
198
|
+
<Canvas
|
|
199
|
+
gl={async (props) => {
|
|
200
|
+
const renderer = new THREE.WebGPURenderer({
|
|
201
|
+
canvas: props.canvas,
|
|
202
|
+
stencil: true
|
|
203
|
+
});
|
|
204
|
+
await renderer.init();
|
|
205
|
+
return renderer;
|
|
206
|
+
}}
|
|
207
|
+
>
|
|
208
|
+
<Text font="/fonts/Font.woff" size={72} fillColor="#ffffff">
|
|
209
|
+
Sharp vector text
|
|
210
|
+
</Text>
|
|
211
|
+
</Canvas>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### React Three Fiber — both in one app
|
|
217
|
+
|
|
218
|
+
Since both adapters export `Text`, alias one at the import site:
|
|
219
|
+
|
|
220
|
+
```jsx
|
|
221
|
+
import { Canvas } from '@react-three/fiber';
|
|
222
|
+
import * as THREE from 'three/webgpu';
|
|
223
|
+
import { Text as MeshText } from 'three-text/mesh/react';
|
|
224
|
+
import { Text as VectorText } from 'three-text/vector/react';
|
|
225
|
+
|
|
226
|
+
MeshText.setHarfBuzzPath('/hb/hb.wasm');
|
|
227
|
+
|
|
228
|
+
function App() {
|
|
229
|
+
return (
|
|
230
|
+
<Canvas
|
|
231
|
+
gl={async (props) => {
|
|
232
|
+
const renderer = new THREE.WebGPURenderer({
|
|
233
|
+
canvas: props.canvas,
|
|
234
|
+
stencil: true
|
|
235
|
+
});
|
|
236
|
+
await renderer.init();
|
|
237
|
+
return renderer;
|
|
238
|
+
}}
|
|
239
|
+
>
|
|
240
|
+
<MeshText font="/fonts/Font.woff" size={72} depth={10}>
|
|
241
|
+
Extruded heading
|
|
242
|
+
</MeshText>
|
|
243
|
+
<VectorText font="/fonts/Font.woff" size={24} fillColor="#cccccc">
|
|
244
|
+
Sharp caption
|
|
245
|
+
</VectorText>
|
|
246
|
+
</Canvas>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
117
251
|
#### p5.js
|
|
118
252
|
|
|
119
253
|
```javascript
|
|
@@ -147,6 +281,54 @@ function draw() {
|
|
|
147
281
|
|
|
148
282
|
`createThreeTextGeometry()` accepts all the same options as Three.js (`layout`, `fontVariations`, `depth`, etc.) and returns `{ geometry, planeBounds, glyphs }`. Use `planeBounds` to center the text
|
|
149
283
|
|
|
284
|
+
#### Vector rendering
|
|
285
|
+
|
|
286
|
+
**Raw WebGL2 (via `three-text/vector/webgl`):**
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
import { Text } from 'three-text/vector';
|
|
290
|
+
import { createWebGLVectorRenderer } from 'three-text/vector/webgl';
|
|
291
|
+
import { woff2Decode } from 'woff-lib/woff2/decode';
|
|
292
|
+
|
|
293
|
+
Text.setHarfBuzzPath('/hb/hb.wasm');
|
|
294
|
+
Text.enableWoff2(woff2Decode);
|
|
295
|
+
|
|
296
|
+
const gl = canvas.getContext('webgl2', { antialias: true, stencil: true });
|
|
297
|
+
const renderer = createWebGLVectorRenderer(gl);
|
|
298
|
+
|
|
299
|
+
const result = await Text.create({ text: 'Hello', font: '/fonts/Font.woff2', size: 72 });
|
|
300
|
+
const vectorData = result.geometryData;
|
|
301
|
+
renderer.setGeometry(vectorData);
|
|
302
|
+
|
|
303
|
+
// In render loop:
|
|
304
|
+
renderer.render(mvpMatrix, new Float32Array([1, 1, 1, 1]));
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Raw WebGPU (via `three-text/vector/webgpu`):**
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
import { Text } from 'three-text/vector';
|
|
311
|
+
import { createWebGPUVectorRenderer } from 'three-text/vector/webgpu';
|
|
312
|
+
import { woff2Decode } from 'woff-lib/woff2/decode';
|
|
313
|
+
|
|
314
|
+
Text.setHarfBuzzPath('/hb/hb.wasm');
|
|
315
|
+
Text.enableWoff2(woff2Decode);
|
|
316
|
+
|
|
317
|
+
const renderer = createWebGPUVectorRenderer(device, format, {
|
|
318
|
+
depthStencilFormat: 'depth24plus-stencil8',
|
|
319
|
+
sampleCount: 4
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const result = await Text.create({ text: 'Hello', font: '/fonts/Font.woff2', size: 72 });
|
|
323
|
+
const vectorData = result.geometryData;
|
|
324
|
+
renderer.setGeometry(vectorData);
|
|
325
|
+
|
|
326
|
+
// In render pass:
|
|
327
|
+
renderer.render(passEncoder, mvpMatrix, new Float32Array([1, 1, 1, 1]));
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
See `examples/webgl-vector.html` and `examples/webgpu-vector.html` for raw WebGL/WebGPU demos. The main interactive demo (`examples/index.html`) uses `WebGPURenderer` with TSL node materials for both mesh and Loop-Blinn vector paths. A `WebGLRenderer` variant is available at `examples/index-webgl.html`
|
|
331
|
+
|
|
150
332
|
### Coordinate systems
|
|
151
333
|
|
|
152
334
|
The core library uses a right-handed coordinate system with +Y down. Text extrudes from z=0 toward positive Z
|
|
@@ -253,15 +435,24 @@ Then navigate to `http://localhost:3000`
|
|
|
253
435
|
|
|
254
436
|
## Why three-text?
|
|
255
437
|
|
|
256
|
-
three-text
|
|
438
|
+
three-text renders text from real font files (TTF, OTF, WOFF, WOFF2) with two pipelines:
|
|
439
|
+
|
|
440
|
+
- **Mesh** — tessellated 3D geometry that can be extruded, lit, and shaded like any model
|
|
441
|
+
- **Vector** — resolution-independent outlines rendered directly from curve data on the GPU, sharp at any zoom or angle
|
|
442
|
+
|
|
443
|
+
Both share the same layout engine (HarfBuzz shaping, Knuth-Plass line breaking) and glyph cache, so a paragraph of 1000 words might only require 50 unique glyphs to be processed
|
|
257
444
|
|
|
258
445
|
Existing solutions take different approaches:
|
|
259
446
|
|
|
260
|
-
- **Three.js native TextGeometry**
|
|
261
|
-
- **three-bmfont-text** renders from pre-generated SDF atlas textures
|
|
262
|
-
- **troika-three-text** generates SDF glyphs at runtime
|
|
447
|
+
- **Three.js native TextGeometry** extrudes 2D outlines from facetype.js JSON. True 3D geometry with depth, but no support for real fonts or OpenType features needed for many of the world's scripts
|
|
448
|
+
- **three-bmfont-text** renders from pre-generated SDF atlas textures built offline at fixed sizes
|
|
449
|
+
- **troika-three-text** generates SDF glyphs at runtime via HarfBuzz. More flexible than bmfont, but still an image-space technique with artifacts up close
|
|
450
|
+
|
|
451
|
+
three-text produces actual geometry from font files, sharper at close distances than bitmap approaches, with control over typesetting and paragraph justification via TeX-based parameters
|
|
452
|
+
|
|
453
|
+
### Why Loop-Blinn
|
|
263
454
|
|
|
264
|
-
|
|
455
|
+
The vector path uses Loop-Blinn curve rendering, where each quadratic Bezier segment becomes a triangle whose fragment shader evaluates the implicit equation `u² - v = 0` to resolve inside/outside analytically, while interior regions are filled separately. We also evaluated Eric Lengyel's [Slug](https://github.com/EricLengyel/Slug) algorithm, which renders glyphs by casting rays against banded curve data over a dilated bounding polygon, and tested several antialiasing configurations including adaptive supersampling and alpha-to-coverage. Loop-Blinn produced cleaner results under strong perspective and oblique viewing angles, so that is what we use
|
|
265
456
|
|
|
266
457
|
## Library structure
|
|
267
458
|
|
|
@@ -269,20 +460,32 @@ three-text generates true 3D geometry from font files via HarfBuzz. It is sharpe
|
|
|
269
460
|
three-text/
|
|
270
461
|
├── src/
|
|
271
462
|
│ ├── core/ # Framework-agnostic text engine
|
|
272
|
-
│ │ ├── Text.ts # Core API
|
|
463
|
+
│ │ ├── Text.ts # Core API, font loading, shaping, layout
|
|
273
464
|
│ │ ├── vectors.ts # Vec2, Vec3, Box3Core
|
|
274
465
|
│ │ ├── types.ts # TypeScript interfaces
|
|
275
466
|
│ │ ├── cache/ # Glyph caching system
|
|
276
467
|
│ │ ├── font/ # Font loading and metrics
|
|
277
468
|
│ │ ├── shaping/ # HarfBuzz text shaping
|
|
278
|
-
│ │
|
|
279
|
-
│
|
|
469
|
+
│ │ └── layout/ # Line breaking and text layout
|
|
470
|
+
│ ├── mesh/ # Mesh geometry pipeline
|
|
471
|
+
│ │ ├── MeshGeometryBuilder.ts # Orchestrates mesh output from layout
|
|
472
|
+
│ │ ├── GlyphGeometryBuilder.ts # Instanced geometry from glyph contours
|
|
473
|
+
│ │ ├── GlyphContourCollector.ts # Collects draw callbacks for mesh path
|
|
474
|
+
│ │ └── geometry/ # Tessellation, extrusion, optimization
|
|
280
475
|
│ ├── three/ # Three.js adapter
|
|
281
476
|
│ │ ├── index.ts # BufferGeometry wrapper
|
|
282
477
|
│ │ ├── react.tsx # React component export
|
|
283
478
|
│ │ └── ThreeText.tsx # React Three Fiber component
|
|
284
|
-
│ ├──
|
|
285
|
-
│ ├──
|
|
479
|
+
│ ├── vector/ # Vector rendering (Loop-Blinn)
|
|
480
|
+
│ │ ├── index.ts # Vector entry point and TSL re-exports
|
|
481
|
+
│ │ ├── loopBlinnTSL.ts # TSL adapter for Three.js WebGPURenderer
|
|
482
|
+
│ │ ├── LoopBlinnGeometry.ts # Fan triangulation + curve extraction
|
|
483
|
+
│ │ ├── GlyphVectorGeometryBuilder.ts # Outline collection and geometry packing
|
|
484
|
+
│ │ ├── GlyphOutlineCollector.ts # Collects draw callbacks for vector path
|
|
485
|
+
│ │ ├── webgl/ # WebGL2 stencil-based renderer
|
|
486
|
+
│ │ └── webgpu/ # WebGPU stencil-based renderer
|
|
487
|
+
│ ├── webgl/ # WebGL mesh buffer utility
|
|
488
|
+
│ ├── webgpu/ # WebGPU mesh buffer utility
|
|
286
489
|
│ ├── p5/ # p5.js adapter
|
|
287
490
|
│ ├── hyphenation/ # Language-specific hyphenation patterns
|
|
288
491
|
│ └── utils/ # Performance logging, data structures
|
|
@@ -316,7 +519,7 @@ Hyphenation uses patterns derived from the Tex hyphenation project, converted in
|
|
|
316
519
|
|
|
317
520
|
### Geometry generation and optimization
|
|
318
521
|
|
|
319
|
-
|
|
522
|
+
By default, three-text runs in mesh mode, generating triangulated geometry from glyph outlines that you can extrude, light, or deform. The mesh pipeline runs once per unique glyph (or glyph cluster), with intermediate results cached to avoid redundant work:
|
|
320
523
|
|
|
321
524
|
1. **Path collection**: HarfBuzz callbacks provide low level drawing operations
|
|
322
525
|
2. **Curve polygonization**: Flattens bezier curves into line segments, placing more points where curves are tight
|
|
@@ -328,6 +531,14 @@ The geometry pipeline runs once per unique glyph (or glyph cluster), with interm
|
|
|
328
531
|
|
|
329
532
|
The multi-stage geometry approach (curve polygonization followed by cleanup, then triangulation) reduces triangle counts and removes overlaps in variable fonts
|
|
330
533
|
|
|
534
|
+
### Vector rendering
|
|
535
|
+
|
|
536
|
+
The vector pipeline (`three-text/vector`) renders glyphs directly from their mathematical outlines without tessellation or curve flattening. Text stays sharp at any zoom level and the geometry footprint is small -- just the control points of each curve
|
|
537
|
+
|
|
538
|
+
Curves use the [Loop-Blinn](https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf) technique: each quadratic curve is rendered as a triangle whose fragment shader evaluates `u² - v` to resolve inside/outside, with screen-space derivatives producing a signed distance that feeds alpha-to-coverage for smooth MSAA edges. Glyph interiors use [Kokojima et al.](https://dl.acm.org/doi/10.1145/1179849.1179997) stencil filling: fan-triangulate, stencil XOR, fill where nonzero
|
|
539
|
+
|
|
540
|
+
An alternative for this sort of resolution-independent rendering is [Slug](https://github.com/EricLengyel/Slug) by Eric Lengyel, which casts rays against all curves per fragment to compute winding numbers. Loop-Blinn was chosen here because it integrates with hardware MSAA and alpha-to-coverage directly, without the overhead of adaptive supersampling that Slug requires for comparable antialiasing
|
|
541
|
+
|
|
331
542
|
#### Glyph caching
|
|
332
543
|
|
|
333
544
|
The library uses a hybrid caching strategy to maximize performance while ensuring visual correctness
|
|
@@ -339,7 +550,7 @@ For text with tight tracking, connected scripts, or complex kerning pairs, indiv
|
|
|
339
550
|
|
|
340
551
|
#### Flat geometry mode
|
|
341
552
|
|
|
342
|
-
When `depth` is 0, the library generates single-sided geometry, reducing triangles by approximately 50%
|
|
553
|
+
When `depth` is 0 in mesh mode, the library generates single-sided geometry, reducing triangles by approximately 50%
|
|
343
554
|
|
|
344
555
|
- Use `THREE.DoubleSide` for flat text so it remains visible from both sides
|
|
345
556
|
- For extruded text, `THREE.FrontSide` is typical since front and back faces are separate geometry
|
|
@@ -369,11 +580,11 @@ const text = await Text.create({
|
|
|
369
580
|
},
|
|
370
581
|
});
|
|
371
582
|
|
|
372
|
-
// Fixed-step:
|
|
583
|
+
// Fixed-step: 32 segments per curve
|
|
373
584
|
const text = await Text.create({
|
|
374
585
|
text: 'Sample',
|
|
375
586
|
font: '/fonts/Font.ttf',
|
|
376
|
-
curveSteps:
|
|
587
|
+
curveSteps: 32,
|
|
377
588
|
});
|
|
378
589
|
```
|
|
379
590
|
|
|
@@ -385,6 +596,8 @@ After curve polygonization, the library applies Visvalingam-Whyatt simplificatio
|
|
|
385
596
|
const text = await Text.create({
|
|
386
597
|
text: 'Sample text',
|
|
387
598
|
font: '/fonts/Font.ttf',
|
|
599
|
+
// Fixed-step: 32 segments per curve
|
|
600
|
+
curveSteps: 32,
|
|
388
601
|
geometryOptimization: {
|
|
389
602
|
areaThreshold: 1.0, // remove triangles < 1 font unit²
|
|
390
603
|
},
|
|
@@ -533,7 +746,7 @@ Common tags include [`liga`](https://learn.microsoft.com/en-us/typography/openty
|
|
|
533
746
|
|
|
534
747
|
### Per-glyph attributes
|
|
535
748
|
|
|
536
|
-
For shader-based animations and interactive effects, the library can generate per-vertex attributes that identify which glyph each vertex belongs to:
|
|
749
|
+
For shader-based animations and interactive effects, the library can generate per-vertex attributes that identify which glyph each vertex belongs to. Both mesh and vector entry points support this: pass `perGlyphAttributes: true` to `Text.create()`
|
|
537
750
|
|
|
538
751
|
```javascript
|
|
539
752
|
const text = await Text.create({
|
|
@@ -550,7 +763,9 @@ const text = await Text.create({
|
|
|
550
763
|
// - glyphBaselineY (float): Y coordinate of glyph baseline
|
|
551
764
|
```
|
|
552
765
|
|
|
553
|
-
|
|
766
|
+
**Mesh:** attributes live on the extruded `geometry`. **Vector:** the same attributes are emitted on interior, curve, and fill buffer geometries. When you need per-glyph draw ranges (for example stencil passes that must not XOR across overlapping glyphs), use `geometryData.glyphRanges`: each entry lists index/vertex ranges for that glyph’s interior, curve, and fill quads
|
|
767
|
+
|
|
768
|
+
This option bypasses overlap-based clustering and adds vertex attributes suitable for per-character manipulation in vertex shaders (or TSL `positionNode` displacements). Each unique glyph is still tessellated only once and cached for reuse. The tradeoff is potential visual artifacts where glyphs actually overlap (tight kerning, cursive scripts)
|
|
554
769
|
|
|
555
770
|
## Querying text content
|
|
556
771
|
|
|
@@ -945,7 +1160,7 @@ While `three-text` runs on all modern browsers, performance varies significantly
|
|
|
945
1160
|
|
|
946
1161
|
**Safari** for macOS shows reduced performance, which is likely due to the platform's conservative resource management; 120FPS is not acheivable
|
|
947
1162
|
|
|
948
|
-
The library was also tested on a Brightsign 223HD, which took a long time to generate the initial geometry but
|
|
1163
|
+
The library was also tested on a Brightsign 223HD, which took a very long time to generate the initial geometry but ran fine after that. We did not push our luck with further testing
|
|
949
1164
|
|
|
950
1165
|
## Testing
|
|
951
1166
|
|
|
@@ -1023,8 +1238,12 @@ The build generates multiple module formats for core and all adapters:
|
|
|
1023
1238
|
**Adapters:**
|
|
1024
1239
|
- `dist/three/` - Three.js adapter
|
|
1025
1240
|
- `dist/three/react.js` - React component
|
|
1026
|
-
- `dist/
|
|
1027
|
-
- `dist/
|
|
1241
|
+
- `dist/vector/` - Vector rendering (Loop-Blinn, Three.js adapter)
|
|
1242
|
+
- `dist/vector/react.js` - React Three Fiber vector component
|
|
1243
|
+
- `dist/webgl/` - WebGL mesh buffer utility
|
|
1244
|
+
- `dist/vector/webgl/` - WebGL vector renderer
|
|
1245
|
+
- `dist/webgpu/` - WebGPU mesh buffer utility
|
|
1246
|
+
- `dist/vector/webgpu/` - WebGPU vector renderer
|
|
1028
1247
|
- `dist/p5/` - p5.js adapter
|
|
1029
1248
|
|
|
1030
1249
|
**Patterns:**
|