@draco-rust/decoder 0.1.1 → 0.2.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/three.js ADDED
@@ -0,0 +1,82 @@
1
+ // three.js's DRACOLoader, decoding on this entry's worker pool. Everything a
2
+ // caller sees -- load, parse, decodeDracoFile, GLTFLoader's use of it, the
3
+ // geometry it builds -- is three's own class; only the decode is replaced, so
4
+ // the result follows whichever three the application has.
5
+ import { REVISION } from 'three';
6
+ import { DRACOLoader as ThreeDRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
7
+ import { createDecoderPool } from './pool.js';
8
+
9
+ // From r181 DRACOLoader pads an attribute whose items are not a multiple of
10
+ // four bytes and builds it interleaved, from the stride its worker reports.
11
+ const PADS = Number.parseInt(REVISION, 10) >= 181;
12
+
13
+ function padded(array, itemSize) {
14
+ const bytes = array.BYTES_PER_ELEMENT;
15
+ const stride = (Math.ceil((itemSize * bytes) / 4) * 4) / bytes;
16
+ if (!PADS || stride === itemSize) return { array, stride: itemSize };
17
+ const count = array.length / itemSize;
18
+ const out = new array.constructor(count * stride);
19
+ for (let i = 0; i < count; i += 1) {
20
+ out.set(array.subarray(i * itemSize, (i + 1) * itemSize), i * stride);
21
+ }
22
+ return { array: out, stride };
23
+ }
24
+
25
+ export class DRACOLoader extends ThreeDRACOLoader {
26
+ #tasks = new WeakMap();
27
+
28
+ constructor(manager, options = {}) {
29
+ super(manager);
30
+ this.pool = options.pool ?? createDecoderPool(options);
31
+ }
32
+
33
+ // The buffer goes to a worker by transfer, which leaves nothing here to
34
+ // decode again; a second request for it gets the first answer, as three's
35
+ // own task cache gives it.
36
+ decodeGeometry(buffer, taskConfig) {
37
+ const key = JSON.stringify(taskConfig);
38
+ const cached = this.#tasks.get(buffer);
39
+ if (cached?.key === key) return cached.promise;
40
+ if (cached && buffer.byteLength === 0) {
41
+ return Promise.reject(new Error('THREE.DRACOLoader: Unable to re-decode a buffer with different settings. Buffer has already been transferred.'));
42
+ }
43
+ const { attributeIDs, attributeTypes, useUniqueIDs, vertexColorSpace } = taskConfig;
44
+ const requests = Object.keys(attributeIDs).map((name) => ({
45
+ name,
46
+ ...(useUniqueIDs ? { id: attributeIDs[name] } : { semantic: attributeIDs[name] }),
47
+ type: attributeTypes[name],
48
+ }));
49
+ const promise = this.pool.decode(buffer, requests, { transfer: buffer instanceof ArrayBuffer }).then((result) => {
50
+ if (!result.success) throw new Error(`THREE.DRACOLoader: Decoding failed: ${result.error}`);
51
+ return this._createGeometry({
52
+ index: result.index ? { array: result.index, itemSize: 1 } : null,
53
+ attributes: result.attributes.map(({ name, array, itemSize }) => ({
54
+ name,
55
+ itemSize,
56
+ ...padded(array, itemSize),
57
+ ...(name === 'color' ? { vertexColorSpace } : {}),
58
+ })),
59
+ });
60
+ });
61
+ if (buffer instanceof ArrayBuffer) this.#tasks.set(buffer, { key, promise });
62
+ return promise;
63
+ }
64
+
65
+ setWorkerLimit(workerLimit) {
66
+ super.setWorkerLimit(workerLimit);
67
+ this.pool.setWorkerLimit(workerLimit);
68
+ return this;
69
+ }
70
+
71
+ preload() {
72
+ // A failure shows again on the first decode, which is where it is handled.
73
+ this.pool.preload().catch(() => {});
74
+ return this;
75
+ }
76
+
77
+ dispose() {
78
+ super.dispose();
79
+ this.pool.dispose();
80
+ return this;
81
+ }
82
+ }
package/worker.js ADDED
@@ -0,0 +1,28 @@
1
+ // A decoder worker. The pool sends it the compiled module once, then streams
2
+ // to decode; it answers each with its result, handing the arrays back by
3
+ // transfer. Runs as a Web Worker and as a Node worker thread.
4
+ import init, { decode_draco } from './index.js';
5
+
6
+ const web = typeof self !== 'undefined' && typeof self.postMessage === 'function';
7
+ const port = web ? self : process.getBuiltinModule('node:worker_threads').parentPort;
8
+ const listen = (handler) =>
9
+ web ? self.addEventListener('message', (event) => handler(event.data)) : port.on('message', handler);
10
+
11
+ let ready = null;
12
+
13
+ listen(async (message) => {
14
+ if (message.type === 'init') {
15
+ ready = init({ module_or_path: message.module });
16
+ return;
17
+ }
18
+ const { id, data, attributes } = message;
19
+ try {
20
+ await ready;
21
+ const result = decode_draco(data, attributes);
22
+ const transfer = result.attributes.map((attribute) => attribute.array.buffer);
23
+ if (result.index) transfer.push(result.index.buffer);
24
+ port.postMessage({ id, result }, transfer);
25
+ } catch (error) {
26
+ port.postMessage({ id, error: String(error?.message ?? error) });
27
+ }
28
+ });