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/README.md +28 -19
- package/dist/index.d.ts +13 -48
- package/dist/index.js +2250 -3456
- package/dist/pool.js +154 -0
- package/dist/three.d.ts +47 -69
- package/dist/three.js +2339 -3665
- package/dist/worker.js +2252 -3464
- package/package.json +1 -1
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
|
-
|
|
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
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
171
|
-
|
|
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
|
-
|
|
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;
|