jcode/skills/threejs-csg/loaders.md

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.