83 lines
3.5 KiB
Markdown
83 lines
3.5 KiB
Markdown
# Loaders, exporters and text/SVG shapes
|
|
|
|
All of these live under `three/addons/`, which the import map of template.html
|
|
already provides. Import from the addon file, NOT from the `THREE` namespace
|
|
(`THREE.STLExporter` is undefined):
|
|
```js
|
|
import { STLExporter } from 'three/addons/exporters/STLExporter.js';
|
|
import { STLLoader } from 'three/addons/loaders/STLLoader.js';
|
|
import { SVGLoader } from 'three/addons/loaders/SVGLoader.js';
|
|
import { FontLoader } from 'three/addons/loaders/FontLoader.js';
|
|
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
|
|
```
|
|
|
|
## Export STL
|
|
`STLExporter.parse(object3d)` traverses `object3d` and exports every `Mesh` in
|
|
WORLD coordinates. Give it a throw-away `Group` of plain meshes - never the
|
|
scene (grid, lights, helpers would be included) and never Brush objects with
|
|
stale matrices:
|
|
```js
|
|
function toSTL(objs, binary = false) {
|
|
const g = new THREE.Group();
|
|
for (const o of objs) { // o.mesh: Mesh or Brush (result of CSG)
|
|
const m = new THREE.Mesh(o.mesh.geometry); // plain Mesh, geometry is shared
|
|
m.matrix.copy(o.mesh.matrixWorld); m.matrixAutoUpdate = false;
|
|
g.add(m);
|
|
}
|
|
g.updateMatrixWorld(true);
|
|
return new STLExporter().parse(g, { binary }); // string (ASCII) or DataView (binary)
|
|
}
|
|
```
|
|
Call `obj.updateMatrixWorld(true)` after changing position/rotation and before
|
|
exporting or evaluating CSG. Holes must already be subtracted (export the group
|
|
results, not the hole brushes).
|
|
|
|
## Import STL
|
|
```js
|
|
pickFile('.stl', false, buf => { // ArrayBuffer (see single-file-webapp skill)
|
|
const geo = new STLLoader().parse(buf); // handles ASCII and binary
|
|
geo.computeVertexNormals(); geo.center(); // optional
|
|
addObject({ type: 'stl', geometry: geo });
|
|
});
|
|
```
|
|
|
|
## Extrude an SVG
|
|
```js
|
|
pickFile('.svg', true, text => {
|
|
const data = new SVGLoader().parse(text);
|
|
const shapes = data.paths.flatMap(p => SVGLoader.createShapes(p));
|
|
const geo = new THREE.ExtrudeGeometry(shapes, { depth: 2, bevelEnabled: false });
|
|
geo.rotateX(Math.PI); // SVG y points down
|
|
geo.center();
|
|
addObject({ type: 'svg', geometry: geo });
|
|
});
|
|
```
|
|
SVG units are px; scale with `geo.scale(s, s, 1)` (e.g. `s = 0.1` for mm-ish sizes).
|
|
|
|
## Solid text
|
|
```js
|
|
const FONT_URL = 'https://cdn.jsdelivr.net/npm/three@0.160.0/examples/fonts/helvetiker_regular.typeface.json';
|
|
const font = await new FontLoader().loadAsync(FONT_URL); // load once, cache
|
|
const geo = new TextGeometry('Hi', { font, size: 10, height: 2, curveSegments: 6 });
|
|
geo.center();
|
|
```
|
|
(`height` is the extrusion depth; in three >= r163 the option is called `depth`.)
|
|
|
|
## Group with holes = CSG result
|
|
```js
|
|
function buildGroup(members) { // members: [{mesh, hole}]
|
|
const ev = new Evaluator(); ev.useGroups = false;
|
|
const solids = members.filter(m => !m.hole), holes = members.filter(m => m.hole);
|
|
let result = null;
|
|
for (const s of solids) { s.mesh.updateMatrixWorld(true);
|
|
result = result ? ev.evaluate(result, s.mesh, ADDITION) : s.mesh; }
|
|
for (const h of holes) { h.mesh.updateMatrixWorld(true);
|
|
result = ev.evaluate(result, h.mesh, SUBTRACTION); }
|
|
result.material = solids[0].mesh.material;
|
|
return result; // a Brush; keep `members` for ungroup
|
|
}
|
|
```
|
|
Operands must be `Brush` instances (create every object as a Brush from the
|
|
start, so any object can take part in CSG later). Render holes with a
|
|
transparent material (`transparent: true, opacity: 0.35`) until grouped.
|