@es3d/es3d 1.0.0 → 1.0.1
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 +201 -201
- package/README.md +86 -86
- package/docs/SPEC.md +417 -417
- package/package.json +1 -1
- package/scripts/es3d-cli.mjs +377 -377
- package/src/cache/geometry-store.js +392 -392
- package/src/cache/mesh-bundle.js +123 -123
- package/src/cache/sha256.js +73 -73
- package/src/format/ast.js +238 -238
- package/src/format/colors.js +93 -93
- package/src/format/compact-parser.js +171 -171
- package/src/format/compact-tokenizer.js +165 -165
- package/src/format/errors.js +247 -247
- package/src/format/json.js +55 -55
- package/src/format/limits.js +14 -14
- package/src/format/parse.js +92 -92
- package/src/format/schema.js +617 -617
- package/src/format/serializer.js +333 -333
- package/src/format/validator.js +330 -330
- package/src/format/value-error.js +12 -12
- package/src/format/value-format.js +314 -314
- package/src/format/values.js +459 -459
- package/src/format/xml-parser.js +136 -136
- package/src/format/xml-tokenizer.js +237 -237
- package/src/geometry/contour.js +86 -86
- package/src/geometry/extrude.js +175 -175
- package/src/geometry/gear.js +85 -85
- package/src/geometry/geometry-cache.js +159 -159
- package/src/geometry/lod-error.js +178 -178
- package/src/geometry/mesh.js +458 -458
- package/src/geometry/primitives.js +299 -299
- package/src/geometry/registry.js +645 -645
- package/src/geometry/revolve.js +136 -136
- package/src/geometry/sweep.js +441 -441
- package/src/index.js +47 -47
- package/src/io/compression.js +67 -67
- package/src/io/convert.js +236 -236
- package/src/io/download.js +13 -13
- package/src/io/external-scene.js +127 -127
- package/src/io/file-loader.js +28 -28
- package/src/io/gltf.js +630 -630
- package/src/io/mesh-import.js +104 -104
- package/src/io/obj.js +352 -352
- package/src/io/step-brep.js +439 -439
- package/src/io/step-exact.js +1149 -1149
- package/src/io/step.js +1418 -1418
- package/src/io/stl.js +193 -193
- package/src/io/svg-import.js +787 -787
- package/src/material/materials.js +154 -154
- package/src/math/mat4.js +475 -475
- package/src/math/random.js +30 -30
- package/src/math/trig.js +65 -65
- package/src/math/vec3.js +81 -81
- package/src/optimize/optimizer.js +579 -579
- package/src/path/path-flatten.js +322 -322
- package/src/path/path-parser.js +332 -332
- package/src/path/shapes.js +129 -129
- package/src/path/triangulate.js +517 -517
- package/src/pipeline.js +74 -74
- package/src/render/camera.js +168 -168
- package/src/render/frame-builder.js +362 -362
- package/src/render/geometry-pool.js +149 -149
- package/src/render/lighting.js +149 -149
- package/src/render/orbit-controls.js +237 -237
- package/src/render/renderer.js +258 -249
- package/src/render/scroll-timeline.js +104 -104
- package/src/render/shaders/glsl.js +285 -285
- package/src/render/shaders/wgsl.js +252 -252
- package/src/render/stage.js +364 -352
- package/src/render/texture-cache.js +40 -40
- package/src/render/textures.js +223 -223
- package/src/render/webgl2/webgl2-backend.js +586 -532
- package/src/render/webgpu/webgpu-backend.js +482 -480
- package/src/scene/flatten.js +754 -754
- package/src/scene/picking.js +161 -161
- package/src/scene/scene-graph.js +320 -320
- package/src/transform/transform.js +211 -211
- package/src/wasm/kernels-binary.js +3 -3
- package/src/wasm/kernels.js +385 -385
|
@@ -1,159 +1,159 @@
|
|
|
1
|
-
// Geometry cache: deduplicated, lazily generated meshes.
|
|
2
|
-
//
|
|
3
|
-
// Geometry is identified by its descriptor key, so identical shapes anywhere
|
|
4
|
-
// in a document (and across live edits of the document) share one entry.
|
|
5
|
-
// Meshes are generated on first use and per level of detail; an entry that is
|
|
6
|
-
// never drawn is never tessellated. Entries unused by the latest scene build
|
|
7
|
-
// are evicted.
|
|
8
|
-
|
|
9
|
-
import { Mesh } from './mesh.js';
|
|
10
|
-
|
|
11
|
-
let nextEntryId = 1;
|
|
12
|
-
|
|
13
|
-
export class GeometryEntry {
|
|
14
|
-
constructor(desc, owner) {
|
|
15
|
-
this.id = nextEntryId++;
|
|
16
|
-
this.key = desc.key;
|
|
17
|
-
// name of the shape in the persistent cache (src/cache/), null if none
|
|
18
|
-
this.identity = desc.identity || null;
|
|
19
|
-
this.desc = desc;
|
|
20
|
-
this.kind = desc.kind;
|
|
21
|
-
this.lods = desc.lods;
|
|
22
|
-
this.doubleSided = desc.doubleSided;
|
|
23
|
-
this.bounds = desc.bounds;
|
|
24
|
-
this.lodDeviation = desc.lodDeviation || [0];
|
|
25
|
-
this.meshes = new Array(desc.lods).fill(null);
|
|
26
|
-
// diagnostics reported while generating each level, kept so that a mesh
|
|
27
|
-
// restored from the persistent cache reports them again
|
|
28
|
-
this.reports = new Array(desc.lods).fill(null);
|
|
29
|
-
this.owner = owner; // first element using this geometry (diagnostics)
|
|
30
|
-
this.epoch = 0;
|
|
31
|
-
this.useCount = 0;
|
|
32
|
-
this.failed = false;
|
|
33
|
-
this.failedLods = 0; // bit per level whose generation failed (never persisted)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export class GeometryCache {
|
|
38
|
-
constructor() {
|
|
39
|
-
this.entries = new Map();
|
|
40
|
-
this.quality = 1;
|
|
41
|
-
this.epoch = 0;
|
|
42
|
-
this.version = 0; // bumps when meshes are invalidated
|
|
43
|
-
this.stats = { generated: 0, generateMs: 0, triangles: 0, restored: 0 };
|
|
44
|
-
this.diagnostics = [];
|
|
45
|
-
this.onDiagnostic = null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
beginBuild() {
|
|
49
|
-
this.epoch++;
|
|
50
|
-
for (const e of this.entries.values()) e.useCount = 0;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Get or create the entry for a descriptor. */
|
|
54
|
-
acquire(desc, owner) {
|
|
55
|
-
let e = this.entries.get(desc.key);
|
|
56
|
-
if (!e) {
|
|
57
|
-
e = new GeometryEntry(desc, owner);
|
|
58
|
-
this.entries.set(desc.key, e);
|
|
59
|
-
} else if (e.epoch !== this.epoch) {
|
|
60
|
-
// the descriptor closure may reference a newer AST; keep it current
|
|
61
|
-
e.desc = desc;
|
|
62
|
-
e.identity = desc.identity || null;
|
|
63
|
-
e.owner = owner;
|
|
64
|
-
e.bounds = desc.bounds;
|
|
65
|
-
e.doubleSided = desc.doubleSided;
|
|
66
|
-
}
|
|
67
|
-
e.epoch = this.epoch;
|
|
68
|
-
e.useCount++;
|
|
69
|
-
return e;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Drop entries that the latest build did not use. Returns the evicted entries. */
|
|
73
|
-
endBuild() {
|
|
74
|
-
const evicted = [];
|
|
75
|
-
for (const [k, e] of this.entries) {
|
|
76
|
-
if (e.epoch !== this.epoch) {
|
|
77
|
-
this.entries.delete(k);
|
|
78
|
-
evicted.push(e);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return evicted;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
setQuality(q) {
|
|
85
|
-
if (q === this.quality) return;
|
|
86
|
-
this.quality = q;
|
|
87
|
-
for (const e of this.entries.values()) {
|
|
88
|
-
e.meshes.fill(null);
|
|
89
|
-
e.reports.fill(null);
|
|
90
|
-
e.failedLods = 0;
|
|
91
|
-
}
|
|
92
|
-
this.version++;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** Mesh of an entry at a level of detail, generated on demand. */
|
|
96
|
-
mesh(entry, lod = 0) {
|
|
97
|
-
const l = Math.min(Math.max(lod, 0), entry.lods - 1);
|
|
98
|
-
let m = entry.meshes[l];
|
|
99
|
-
if (m) return m;
|
|
100
|
-
const t0 = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
101
|
-
const reports = [];
|
|
102
|
-
const report = (severity, code, message) => {
|
|
103
|
-
reports.push({ severity, code, message });
|
|
104
|
-
this.emit(entry, severity, code, message);
|
|
105
|
-
};
|
|
106
|
-
try {
|
|
107
|
-
m = entry.desc.generate(l, this.quality, report);
|
|
108
|
-
} catch (err) {
|
|
109
|
-
report('error', 'E_GEOMETRY', `Geometry generation failed: ${err && err.message ? err.message : err}`);
|
|
110
|
-
m = null;
|
|
111
|
-
}
|
|
112
|
-
if (!m) {
|
|
113
|
-
entry.failed = true;
|
|
114
|
-
entry.failedLods |= 1 << l;
|
|
115
|
-
m = emptyMesh(entry.kind);
|
|
116
|
-
}
|
|
117
|
-
const t1 = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
118
|
-
this.stats.generated++;
|
|
119
|
-
this.stats.generateMs += t1 - t0;
|
|
120
|
-
entry.meshes[l] = m;
|
|
121
|
-
entry.reports[l] = reports.length ? reports : null;
|
|
122
|
-
return m;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
emit(entry, severity, code, message) {
|
|
126
|
-
const d = { severity, code, message, element: entry.owner, phase: 'geometry' };
|
|
127
|
-
this.diagnostics.push(d);
|
|
128
|
-
if (this.onDiagnostic) this.onDiagnostic(d);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Take a mesh made earlier (the persistent cache) as level `lod` of an entry,
|
|
133
|
-
* with the diagnostics its generation reported. A level that exists stays.
|
|
134
|
-
* @returns {boolean} whether the mesh was taken
|
|
135
|
-
*/
|
|
136
|
-
adopt(entry, lod, mesh, reports = null) {
|
|
137
|
-
if (lod < 0 || lod >= entry.lods || entry.meshes[lod]) return false;
|
|
138
|
-
entry.meshes[lod] = mesh;
|
|
139
|
-
entry.reports[lod] = reports && reports.length ? reports : null;
|
|
140
|
-
if (reports) for (const r of reports) this.emit(entry, r.severity, r.code, r.message);
|
|
141
|
-
this.stats.restored++;
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Generate every level of detail of every entry (benchmarks, exports). */
|
|
146
|
-
generateAll(maxLod = 0) {
|
|
147
|
-
for (const e of this.entries.values()) for (let l = 0; l <= Math.min(maxLod, e.lods - 1); l++) this.mesh(e, l);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
takeDiagnostics() {
|
|
151
|
-
const d = this.diagnostics;
|
|
152
|
-
this.diagnostics = [];
|
|
153
|
-
return d;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function emptyMesh(kind) {
|
|
158
|
-
return new Mesh({ positions: new Float32Array(0), indices: new Uint32Array(0), kind });
|
|
159
|
-
}
|
|
1
|
+
// Geometry cache: deduplicated, lazily generated meshes.
|
|
2
|
+
//
|
|
3
|
+
// Geometry is identified by its descriptor key, so identical shapes anywhere
|
|
4
|
+
// in a document (and across live edits of the document) share one entry.
|
|
5
|
+
// Meshes are generated on first use and per level of detail; an entry that is
|
|
6
|
+
// never drawn is never tessellated. Entries unused by the latest scene build
|
|
7
|
+
// are evicted.
|
|
8
|
+
|
|
9
|
+
import { Mesh } from './mesh.js';
|
|
10
|
+
|
|
11
|
+
let nextEntryId = 1;
|
|
12
|
+
|
|
13
|
+
export class GeometryEntry {
|
|
14
|
+
constructor(desc, owner) {
|
|
15
|
+
this.id = nextEntryId++;
|
|
16
|
+
this.key = desc.key;
|
|
17
|
+
// name of the shape in the persistent cache (src/cache/), null if none
|
|
18
|
+
this.identity = desc.identity || null;
|
|
19
|
+
this.desc = desc;
|
|
20
|
+
this.kind = desc.kind;
|
|
21
|
+
this.lods = desc.lods;
|
|
22
|
+
this.doubleSided = desc.doubleSided;
|
|
23
|
+
this.bounds = desc.bounds;
|
|
24
|
+
this.lodDeviation = desc.lodDeviation || [0];
|
|
25
|
+
this.meshes = new Array(desc.lods).fill(null);
|
|
26
|
+
// diagnostics reported while generating each level, kept so that a mesh
|
|
27
|
+
// restored from the persistent cache reports them again
|
|
28
|
+
this.reports = new Array(desc.lods).fill(null);
|
|
29
|
+
this.owner = owner; // first element using this geometry (diagnostics)
|
|
30
|
+
this.epoch = 0;
|
|
31
|
+
this.useCount = 0;
|
|
32
|
+
this.failed = false;
|
|
33
|
+
this.failedLods = 0; // bit per level whose generation failed (never persisted)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class GeometryCache {
|
|
38
|
+
constructor() {
|
|
39
|
+
this.entries = new Map();
|
|
40
|
+
this.quality = 1;
|
|
41
|
+
this.epoch = 0;
|
|
42
|
+
this.version = 0; // bumps when meshes are invalidated
|
|
43
|
+
this.stats = { generated: 0, generateMs: 0, triangles: 0, restored: 0 };
|
|
44
|
+
this.diagnostics = [];
|
|
45
|
+
this.onDiagnostic = null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
beginBuild() {
|
|
49
|
+
this.epoch++;
|
|
50
|
+
for (const e of this.entries.values()) e.useCount = 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Get or create the entry for a descriptor. */
|
|
54
|
+
acquire(desc, owner) {
|
|
55
|
+
let e = this.entries.get(desc.key);
|
|
56
|
+
if (!e) {
|
|
57
|
+
e = new GeometryEntry(desc, owner);
|
|
58
|
+
this.entries.set(desc.key, e);
|
|
59
|
+
} else if (e.epoch !== this.epoch) {
|
|
60
|
+
// the descriptor closure may reference a newer AST; keep it current
|
|
61
|
+
e.desc = desc;
|
|
62
|
+
e.identity = desc.identity || null;
|
|
63
|
+
e.owner = owner;
|
|
64
|
+
e.bounds = desc.bounds;
|
|
65
|
+
e.doubleSided = desc.doubleSided;
|
|
66
|
+
}
|
|
67
|
+
e.epoch = this.epoch;
|
|
68
|
+
e.useCount++;
|
|
69
|
+
return e;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Drop entries that the latest build did not use. Returns the evicted entries. */
|
|
73
|
+
endBuild() {
|
|
74
|
+
const evicted = [];
|
|
75
|
+
for (const [k, e] of this.entries) {
|
|
76
|
+
if (e.epoch !== this.epoch) {
|
|
77
|
+
this.entries.delete(k);
|
|
78
|
+
evicted.push(e);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return evicted;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setQuality(q) {
|
|
85
|
+
if (q === this.quality) return;
|
|
86
|
+
this.quality = q;
|
|
87
|
+
for (const e of this.entries.values()) {
|
|
88
|
+
e.meshes.fill(null);
|
|
89
|
+
e.reports.fill(null);
|
|
90
|
+
e.failedLods = 0;
|
|
91
|
+
}
|
|
92
|
+
this.version++;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Mesh of an entry at a level of detail, generated on demand. */
|
|
96
|
+
mesh(entry, lod = 0) {
|
|
97
|
+
const l = Math.min(Math.max(lod, 0), entry.lods - 1);
|
|
98
|
+
let m = entry.meshes[l];
|
|
99
|
+
if (m) return m;
|
|
100
|
+
const t0 = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
101
|
+
const reports = [];
|
|
102
|
+
const report = (severity, code, message) => {
|
|
103
|
+
reports.push({ severity, code, message });
|
|
104
|
+
this.emit(entry, severity, code, message);
|
|
105
|
+
};
|
|
106
|
+
try {
|
|
107
|
+
m = entry.desc.generate(l, this.quality, report);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
report('error', 'E_GEOMETRY', `Geometry generation failed: ${err && err.message ? err.message : err}`);
|
|
110
|
+
m = null;
|
|
111
|
+
}
|
|
112
|
+
if (!m) {
|
|
113
|
+
entry.failed = true;
|
|
114
|
+
entry.failedLods |= 1 << l;
|
|
115
|
+
m = emptyMesh(entry.kind);
|
|
116
|
+
}
|
|
117
|
+
const t1 = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
118
|
+
this.stats.generated++;
|
|
119
|
+
this.stats.generateMs += t1 - t0;
|
|
120
|
+
entry.meshes[l] = m;
|
|
121
|
+
entry.reports[l] = reports.length ? reports : null;
|
|
122
|
+
return m;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
emit(entry, severity, code, message) {
|
|
126
|
+
const d = { severity, code, message, element: entry.owner, phase: 'geometry' };
|
|
127
|
+
this.diagnostics.push(d);
|
|
128
|
+
if (this.onDiagnostic) this.onDiagnostic(d);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Take a mesh made earlier (the persistent cache) as level `lod` of an entry,
|
|
133
|
+
* with the diagnostics its generation reported. A level that exists stays.
|
|
134
|
+
* @returns {boolean} whether the mesh was taken
|
|
135
|
+
*/
|
|
136
|
+
adopt(entry, lod, mesh, reports = null) {
|
|
137
|
+
if (lod < 0 || lod >= entry.lods || entry.meshes[lod]) return false;
|
|
138
|
+
entry.meshes[lod] = mesh;
|
|
139
|
+
entry.reports[lod] = reports && reports.length ? reports : null;
|
|
140
|
+
if (reports) for (const r of reports) this.emit(entry, r.severity, r.code, r.message);
|
|
141
|
+
this.stats.restored++;
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Generate every level of detail of every entry (benchmarks, exports). */
|
|
146
|
+
generateAll(maxLod = 0) {
|
|
147
|
+
for (const e of this.entries.values()) for (let l = 0; l <= Math.min(maxLod, e.lods - 1); l++) this.mesh(e, l);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
takeDiagnostics() {
|
|
151
|
+
const d = this.diagnostics;
|
|
152
|
+
this.diagnostics = [];
|
|
153
|
+
return d;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function emptyMesh(kind) {
|
|
158
|
+
return new Mesh({ positions: new Float32Array(0), indices: new Uint32Array(0), kind });
|
|
159
|
+
}
|