minidraco 0.4.0 → 0.6.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/dist/pool.js ADDED
@@ -0,0 +1,154 @@
1
+ // src/three/pool.ts
2
+ var decodeError = (message) => {
3
+ const error = new Error(message);
4
+ error.isDecodeError = true;
5
+ return error;
6
+ };
7
+ var WorkerPool = class {
8
+ // Pool size; the loader keeps it in sync with setWorkerLimit().
9
+ limit;
10
+ Aa;
11
+ D = [];
12
+ nc = 0;
13
+ X = /* @__PURE__ */ new Map();
14
+ // Tasks queued during the current microtask; flushed as one batched message
15
+ // per worker (a 488-primitive scene costs 4 postMessages, not 488).
16
+ Ba = [];
17
+ Pa = false;
18
+ // Set when spawning a worker fails (bundler without module-worker support,
19
+ // file:// pages, …) or a worker errors: the loader then decodes on the main
20
+ // thread.
21
+ broken = false;
22
+ // Same-origin blob bootstrap used when the worker asset lives on a CDN
23
+ // origin (created lazily, revoked on dispose).
24
+ ia = null;
25
+ constructor(limit, workerUrl) {
26
+ this.limit = limit;
27
+ this.Aa = workerUrl;
28
+ }
29
+ // Spawns the whole pool up front: each fresh worker runs a short JIT warmup
30
+ // at startup (see worker.ts), so spawning early lets that overlap the model
31
+ // download instead of the first decode burst.
32
+ spawnAll() {
33
+ while (this.D.length < this.limit && this.Lb() !== null) {
34
+ }
35
+ }
36
+ dispose() {
37
+ for (const entry of this.D) entry.worker.terminate();
38
+ this.D = [];
39
+ const error = decodeError("MinidracoLoader: disposed while decoding");
40
+ for (const task of this.Ba) task.reject(error);
41
+ this.Ba = [];
42
+ this.Pa = false;
43
+ for (const [, task] of this.X) task.reject(error);
44
+ this.X.clear();
45
+ if (this.ia !== null) {
46
+ URL.revokeObjectURL(this.ia);
47
+ this.ia = null;
48
+ }
49
+ }
50
+ decode(buffer, taskConfig) {
51
+ const id = this.nc++;
52
+ return new Promise((resolve, reject) => {
53
+ this.Ba.push({ id, buffer, taskConfig, resolve, reject });
54
+ if (!this.Pa) {
55
+ this.Pa = true;
56
+ queueMicrotask(() => this.oc());
57
+ }
58
+ });
59
+ }
60
+ Lb() {
61
+ if (this.broken) return null;
62
+ if (this.D.length < this.limit) {
63
+ const workerUrl = this.Aa ?? new URL("./worker.js", import.meta.url);
64
+ let worker;
65
+ try {
66
+ worker = new Worker(workerUrl, { type: "module" });
67
+ } catch {
68
+ try {
69
+ if (this.ia === null) {
70
+ const bootstrap = `import ${JSON.stringify(String(workerUrl))};`;
71
+ this.ia = URL.createObjectURL(new Blob([bootstrap], { type: "text/javascript" }));
72
+ }
73
+ worker = new Worker(this.ia, { type: "module" });
74
+ } catch {
75
+ this.broken = true;
76
+ return null;
77
+ }
78
+ }
79
+ const entry = { worker, pending: 0 };
80
+ worker.onmessage = (event) => {
81
+ for (const { id, ok, indices, attributes, error } of event.data.results) {
82
+ const task = this.X.get(id);
83
+ if (!task) continue;
84
+ this.X.delete(id);
85
+ task.entry.pending--;
86
+ if (ok) {
87
+ task.resolve({ indices, attributes });
88
+ } else {
89
+ task.reject(decodeError(error));
90
+ }
91
+ }
92
+ };
93
+ worker.onerror = (event) => {
94
+ this.broken = true;
95
+ for (const [id, task] of this.X) {
96
+ if (task.entry.worker === worker) {
97
+ this.X.delete(id);
98
+ task.reject(new Error(`MinidracoLoader worker failed: ${event.message ?? "unknown error"}`));
99
+ }
100
+ }
101
+ };
102
+ this.D.push(entry);
103
+ return entry;
104
+ }
105
+ let best = this.D[0];
106
+ for (const entry of this.D) if (entry.pending < best.pending) best = entry;
107
+ return best;
108
+ }
109
+ oc() {
110
+ this.Pa = false;
111
+ const batch = this.Ba;
112
+ if (batch.length === 0) return;
113
+ this.Ba = [];
114
+ while (this.D.length < Math.min(this.limit, batch.length) && this.Lb() !== null) {
115
+ }
116
+ if (this.broken || this.D.length === 0) {
117
+ const error = new Error("MinidracoLoader: worker unavailable");
118
+ for (const task of batch) task.reject(error);
119
+ return;
120
+ }
121
+ batch.sort((a, b) => b.buffer.byteLength - a.buffer.byteLength);
122
+ let totalBytes = 0;
123
+ for (const task of batch) totalBytes += task.buffer.byteLength;
124
+ const averageBytes = totalBytes / batch.length;
125
+ const buckets = this.D.map((entry) => ({
126
+ entry,
127
+ tasks: [],
128
+ bytes: entry.pending * averageBytes
129
+ }));
130
+ for (const task of batch) {
131
+ let best = buckets[0];
132
+ for (const bucket of buckets) if (bucket.bytes < best.bytes) best = bucket;
133
+ best.tasks.push(task);
134
+ best.bytes += task.buffer.byteLength;
135
+ }
136
+ for (const { entry, tasks } of buckets) {
137
+ if (tasks.length === 0) continue;
138
+ entry.pending += tasks.length;
139
+ for (const task of tasks) this.X.set(task.id, { resolve: task.resolve, reject: task.reject, entry });
140
+ entry.worker.postMessage({
141
+ tasks: tasks.map((task) => ({
142
+ id: task.id,
143
+ buffer: task.buffer,
144
+ attributeIDs: task.taskConfig.attributeIDs,
145
+ attributeTypes: task.taskConfig.attributeTypes,
146
+ useUniqueIDs: task.taskConfig.useUniqueIDs
147
+ }))
148
+ });
149
+ }
150
+ }
151
+ };
152
+ export {
153
+ WorkerPool
154
+ };
package/dist/three.d.ts CHANGED
@@ -2,13 +2,10 @@ import { Loader, BufferGeometry, LoadingManager, BufferAttribute } from 'three';
2
2
 
3
3
  declare class DataBuffer {
4
4
  _data: Uint8Array;
5
- constructor();
6
- update(data: Uint8Array | ArrayBufferView | ArrayBuffer | null | undefined, size: number, offset?: number): boolean;
7
- resize(newSize: number): void;
5
+ update(data: Uint8Array | null, size: number): void;
8
6
  adoptScratch(size: number): void;
9
- write(bytePos: number, inArray: Uint8Array | ArrayBufferView | ArrayBuffer, dataSize: number): void;
7
+ write(bytePos: number, inArray: Uint8Array, dataSize: number): void;
10
8
  get data(): Uint8Array;
11
- get dataSize(): number;
12
9
  _resize(newSize: number): void;
13
10
  }
14
11
 
@@ -21,10 +18,7 @@ declare class GeometryAttribute {
21
18
  _byteOffset: number;
22
19
  _attributeType: number;
23
20
  _uniqueId: number;
24
- constructor();
25
21
  init(attributeType: number, buffer: DataBuffer | null, numComponents: number, dataType: number, normalized: boolean, byteStride: number, byteOffset: number): void;
26
- getAddress(attIndex: number): Uint8Array;
27
- copyFrom(srcAtt: GeometryAttribute): boolean;
28
22
  resetBuffer(buffer: DataBuffer, byteStride: number, byteOffset: number): void;
29
23
  get attributeType(): number;
30
24
  get dataType(): number;
@@ -37,46 +31,25 @@ declare class GeometryAttribute {
37
31
  }
38
32
 
39
33
  declare class AttributeTransformData {
40
- _transformType: number;
41
- _bytes: Uint8Array;
42
- _view: DataView;
43
- _size: number;
44
- constructor();
45
- get transformType(): number;
46
- set transformType(type: number);
47
- get dataSize(): number;
48
- get data(): Uint8Array;
49
- _reserve(sizeNeeded: number): void;
50
- setParameterValue(byteOffset: number, value: number, type: string): void;
34
+ transformType: number;
35
+ _values: number[];
36
+ _types: string[];
51
37
  appendParameterValue(value: number, type: string): void;
52
- _typeSize(type: string): number;
53
38
  }
54
39
 
55
40
  type ExtractTypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
41
+ type ViewCtor = (new (buffer: ArrayBufferLike) => ExtractTypedArray) & {
42
+ BYTES_PER_ELEMENT: number;
43
+ };
56
44
  declare class PointAttribute extends GeometryAttribute {
57
45
  _identityMapping: boolean;
58
46
  _numUniqueEntries: number;
59
47
  _indicesMap: number[] | Uint32Array;
60
48
  _attributeBuffer: DataBuffer | null;
61
49
  _attributeTransformData: AttributeTransformData | null;
62
- _cachedFloat32View?: Float32Array;
63
- _cachedFloat32Buffer?: ArrayBufferLike;
64
- _cachedInt32View?: Int32Array;
65
- _cachedInt32Buffer?: ArrayBufferLike;
66
- _cachedUint32View?: Uint32Array;
67
- _cachedUint32Buffer?: ArrayBufferLike;
68
- _cachedUint16View?: Uint16Array;
69
- _cachedUint16Buffer?: ArrayBufferLike;
70
- _cachedInt16View?: Int16Array;
71
- _cachedInt16Buffer?: ArrayBufferLike;
72
- _cachedUint8View?: Uint8Array;
73
- _cachedUint8Buffer?: ArrayBufferLike;
74
- _cachedInt8View?: Int8Array;
75
- _cachedInt8Buffer?: ArrayBufferLike;
76
- _cachedFloat64View?: Float64Array;
77
- _cachedFloat64Buffer?: ArrayBufferLike;
78
- _cachedDataView?: DataView;
79
- _cachedDVBuffer?: ArrayBufferLike;
50
+ _cachedViewCtor: ViewCtor | null;
51
+ _cachedViewBuffer: ArrayBufferLike | null;
52
+ _cachedView: ExtractTypedArray | null;
80
53
  constructor(geometryAttribute?: GeometryAttribute);
81
54
  init(attributeType: number, numComponents: number, dataType: number, normalized: boolean, numAttributeValues: number): void;
82
55
  reset(numAttributeValues: number): boolean;
@@ -87,21 +60,19 @@ declare class PointAttribute extends GeometryAttribute {
87
60
  get indicesMapSize(): number;
88
61
  get indicesMap(): number[] | Uint32Array;
89
62
  setIdentityMapping(): void;
90
- setExplicitMapping(numPoints: number): void;
91
63
  setExplicitMappingUnfilled(numPoints: number): void;
92
64
  setExplicitMappingShared(map: Uint32Array): void;
93
65
  setExplicitMappingScratch(numPoints: number): void;
94
66
  setAttributeTransformData(transformData: AttributeTransformData): void;
67
+ _view(Ctor: ViewCtor, bufData: Uint8Array): ExtractTypedArray;
95
68
  convertValue(attIndex: number, outVal: number[] | Int32Array | Uint32Array | Float32Array | Float64Array): void;
96
69
  extractTo<C extends new (length: number) => ExtractTypedArray>(OutputTypedArray: C, numPoints: number): InstanceType<C>;
97
- copyFrom(srcAtt: PointAttribute): void;
98
70
  }
99
71
 
100
72
  declare class PointCloud {
101
73
  num_points_: number;
102
74
  attributes_: (PointAttribute | null)[];
103
75
  named_attribute_index_: number[][];
104
- constructor();
105
76
  numNamedAttributes(type: number): number;
106
77
  getNamedAttributeId(type: number, i?: number): number;
107
78
  getNamedAttribute(type: number, i?: number): PointAttribute | null;
@@ -116,26 +87,12 @@ declare class PointCloud {
116
87
  }
117
88
 
118
89
  declare class Mesh extends PointCloud {
119
- faces_: Int32Array;
90
+ faces_: Int32Array<ArrayBuffer>;
120
91
  numFaces_: number;
121
- attribute_data_: {
122
- elementType: number;
123
- }[];
124
- constructor();
125
92
  setNumFaces(numFaces: number): void;
126
93
  numFaces(): number;
127
- face(faceId: number): number[];
128
- setAttribute(attId: number, pa: PointAttribute): void;
129
94
  }
130
95
 
131
- type AttributeIDs = Record<string, number | string>;
132
- type AttributeTypes = Record<string, string>;
133
- interface MinidracoLoaderOptions {
134
- manager?: LoadingManager;
135
- workers?: boolean;
136
- workerLimit?: number;
137
- mainThreadByteThreshold?: number;
138
- }
139
96
  interface TaskConfig {
140
97
  attributeIDs: AttributeIDs;
141
98
  attributeTypes: AttributeTypes;
@@ -162,23 +119,46 @@ interface QueuedTask {
162
119
  resolve: (raw: RawGeometry) => void;
163
120
  reject: (error: unknown) => void;
164
121
  }
122
+ interface PendingTask {
123
+ resolve: (raw: RawGeometry) => void;
124
+ reject: (error: unknown) => void;
125
+ entry: WorkerEntry;
126
+ }
127
+ declare class WorkerPool {
128
+ limit: number;
129
+ _workerUrl: string | URL | null;
130
+ _workers: WorkerEntry[];
131
+ _taskId: number;
132
+ _tasks: Map<number, PendingTask>;
133
+ _batch: QueuedTask[];
134
+ _batchScheduled: boolean;
135
+ broken: boolean;
136
+ _blobUrl: string | null;
137
+ constructor(limit: number, workerUrl: string | URL | null);
138
+ spawnAll(): void;
139
+ dispose(): void;
140
+ decode(buffer: ArrayBuffer, taskConfig: TaskConfig): Promise<RawGeometry>;
141
+ _getWorker(): WorkerEntry | null;
142
+ _flushBatch(): void;
143
+ }
144
+
145
+ type AttributeIDs = Record<string, number | string>;
146
+ type AttributeTypes = Record<string, string>;
147
+ interface MinidracoLoaderOptions {
148
+ manager?: LoadingManager;
149
+ workers?: boolean;
150
+ workerLimit?: number;
151
+ mainThreadByteThreshold?: number;
152
+ }
165
153
  declare class MinidracoLoader extends Loader<BufferGeometry> {
166
154
  defaultAttributeIDs: AttributeIDs;
167
155
  defaultAttributeTypes: AttributeTypes;
168
156
  workerLimit: number;
169
157
  mainThreadByteThreshold: number;
170
- _workers: WorkerEntry[];
171
- _taskId: number;
172
- _tasks: Map<number, {
173
- resolve: (raw: RawGeometry) => void;
174
- reject: (error: unknown) => void;
175
- entry: WorkerEntry;
176
- }>;
177
- _batch: QueuedTask[];
178
- _batchScheduled: boolean;
158
+ _pool: WorkerPool | null;
159
+ _poolPromise: Promise<WorkerPool | null> | null;
179
160
  _workersBroken: boolean;
180
161
  _workerUrl: string | URL | null;
181
- _workerBlobUrl: string | null;
182
162
  constructor(managerOrOptions?: LoadingManager | MinidracoLoaderOptions);
183
163
  setWorkerUrl(url: string | URL | null): this;
184
164
  setDecoderPath(_path?: unknown): this;
@@ -193,9 +173,7 @@ declare class MinidracoLoader extends Loader<BufferGeometry> {
193
173
  decodeGeometry(buffer: ArrayBuffer, taskConfig: TaskConfig): Promise<BufferGeometry>;
194
174
  _runTask(buffer: ArrayBuffer, taskConfig: TaskConfig): Promise<BufferGeometry>;
195
175
  _workersAvailable(): boolean;
196
- _getWorker(): WorkerEntry | null;
197
- _decodeInWorker(buffer: ArrayBuffer, taskConfig: TaskConfig): Promise<RawGeometry>;
198
- _flushBatch(): void;
176
+ _getPool(): Promise<WorkerPool | null>;
199
177
  _buildGeometryFromRaw(raw: RawGeometry, taskConfig: TaskConfig): BufferGeometry;
200
178
  _decodeBuffer(buffer: ArrayBuffer, taskConfig: TaskConfig): BufferGeometry;
201
179
  _buildGeometry(dracoGeometry: Mesh, taskConfig: TaskConfig): BufferGeometry;