minidraco 0.1.1 → 0.3.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 +38 -77
- package/dist/index.d.ts +1 -99
- package/dist/index.js +1575 -1496
- package/dist/three.d.ts +23 -7
- package/dist/three.js +1691 -1535
- package/dist/worker.js +1627 -1522
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,39 +1,29 @@
|
|
|
1
|
-
# minidraco
|
|
1
|
+
# 🐲 minidraco
|
|
2
2
|
|
|
3
3
|
A fast, pure-TypeScript [Draco](https://google.github.io/draco/) mesh decoder with a drop-in
|
|
4
|
-
`DRACOLoader`
|
|
5
|
-
|
|
6
|
-
main thread.
|
|
7
|
-
|
|
8
|
-
minidraco started as a TypeScript port of [mrdoob/draco.js](https://github.com/mrdoob/draco.js)
|
|
9
|
-
(MIT), then restructured and optimized (rANS table pooling, seam-list corner tables, allocation
|
|
10
|
-
elimination in the entropy decoders, specialized attribute extraction) to close the gap with the
|
|
11
|
-
official [draco3d](https://www.npmjs.com/package/draco3d) wasm decoder.
|
|
4
|
+
`DRACOLoader` for [Three.js](https://threejs.org/) — no wasm to host or fetch, and a worker pool
|
|
5
|
+
so decoding never blocks the main thread.
|
|
12
6
|
|
|
13
7
|
## Usage
|
|
14
8
|
|
|
15
9
|
```ts
|
|
16
10
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
|
17
|
-
import {
|
|
11
|
+
import { MinidracoLoader } from 'minidraco/three'
|
|
18
12
|
|
|
19
13
|
const gltfLoader = new GLTFLoader()
|
|
20
|
-
gltfLoader.setDRACOLoader(new
|
|
14
|
+
gltfLoader.setDRACOLoader(new MinidracoLoader())
|
|
21
15
|
gltfLoader.load('model.glb', gltf => scene.add(gltf.scene))
|
|
22
16
|
```
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
`0` to force synchronous main-thread decoding). If workers can't be spawned (SSR, exotic
|
|
27
|
-
bundlers), it falls back to synchronous decoding automatically.
|
|
18
|
+
A drop-in for `THREE.DRACOLoader`, no cast needed. Decoding runs in a worker pool by default, with
|
|
19
|
+
a main-thread fallback. Options:
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
it). If even that fails, decoding falls back to the main thread rather than erroring.
|
|
34
|
-
`setWorkerUrl(url)` exists as a manual override for exotic setups.
|
|
21
|
+
```ts
|
|
22
|
+
new MinidracoLoader({ workers: false }) // decode on the main thread
|
|
23
|
+
new MinidracoLoader({ workerLimit: 8 }) // pool size (default 4)
|
|
24
|
+
```
|
|
35
25
|
|
|
36
|
-
Or decode a raw
|
|
26
|
+
Or decode a raw bitstream without Three.js:
|
|
37
27
|
|
|
38
28
|
```ts
|
|
39
29
|
import { decodeDracoMesh } from 'minidraco'
|
|
@@ -41,79 +31,50 @@ import { decodeDracoMesh } from 'minidraco'
|
|
|
41
31
|
const mesh = decodeDracoMesh(new Uint8Array(bytes))
|
|
42
32
|
```
|
|
43
33
|
|
|
44
|
-
##
|
|
34
|
+
## Features
|
|
45
35
|
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- Quantized, integer, and octahedron-normal attributes
|
|
50
|
-
- Custom / generic attributes (glTF `_*` semantics), skinning attributes (`JOINTS_0` /
|
|
51
|
-
`WEIGHTS_0`), vertex colors, multiple UV sets
|
|
52
|
-
- Output is verified bit-identical to the official wasm decoder (≤ 1 ulp on dequantized floats)
|
|
53
|
-
in the test suite, on both real production GLBs and Draco's own test corpus
|
|
54
|
-
- Point clouds are not supported (glTF `KHR_draco_mesh_compression` only ever contains meshes)
|
|
36
|
+
- Every Draco triangle mesh — all encodings, prediction schemes, and attribute types.
|
|
37
|
+
- Bit-identical to the official wasm decoder, verified in the tests.
|
|
38
|
+
- No point clouds (glTF Draco is always meshes).
|
|
55
39
|
|
|
56
40
|
## Performance
|
|
57
41
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
| file (points / faces) | minidraco | draco.js | draco3d wasm |
|
|
62
|
-
| ------------------------------------ | --------- | -------- | ------------ |
|
|
63
|
-
| canine (1.1k / 0.5k) | 0.6 ms | 0.6 ms | 0.4 ms |
|
|
64
|
-
| player (5.1k / 2.5k) | 1.9 ms | 1.7 ms | 1.2 ms |
|
|
65
|
-
| static (291k / 221k, 488 primitives) | **50 ms** | 55 ms | 51 ms |
|
|
42
|
+
Median across a 19-model corpus vs [draco.js](https://github.com/mrdoob/draco.js) and the official
|
|
43
|
+
[draco3d](https://www.npmjs.com/package/draco3d) wasm decoder (full results in
|
|
44
|
+
[BENCH.md](https://github.com/verekia/minidraco/blob/main/BENCH.md)):
|
|
66
45
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
46
|
+
| benchmark | vs draco.js | vs draco3d wasm |
|
|
47
|
+
| --------------------------------------------- | --------------- | --------------- |
|
|
48
|
+
| single-threaded decode — bun (JSC) | ⚪ even | 🟢 1.13× faster |
|
|
49
|
+
| single-threaded decode — Chrome (V8) | ⚪ even | 🟢 1.50× faster |
|
|
50
|
+
| `GLTFLoader.parse`, worker pool — Chrome (V8) | 🟢 1.23× faster | 🔴 1.10× slower |
|
|
70
51
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
| minidraco (4 workers) | ~51 ms | free |
|
|
74
|
-
| draco.js (main thread) | ~72 ms | blocked |
|
|
75
|
-
| draco3d wasm (4 workers) | ~23 ms | free |
|
|
52
|
+
On par with draco.js, faster than the wasm decoder single-threaded, and competitive in a real
|
|
53
|
+
`GLTFLoader.parse` with the main thread left free.
|
|
76
54
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
option for the first model on screen.
|
|
55
|
+
🟢 **Cold start is the real win, and no warm benchmark shows it:** there's no `draco_decoder.wasm`
|
|
56
|
+
to fetch and compile before the first decode, so the first model on screen appears sooner.
|
|
80
57
|
|
|
81
58
|
## Download size
|
|
82
59
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
| ----------------------------------------------------- | ------ | ------- | ------- |
|
|
88
|
-
| minidraco — `minidraco/three` in the app bundle | 102 KB | 26.6 KB | 23.1 KB |
|
|
89
|
-
| minidraco — `worker.js` chunk (fetched on 1st decode) | 99 KB | 25.5 KB | 22.1 KB |
|
|
90
|
-
| draco.js — loader + decoder in the app bundle | 96 KB | 24.8 KB | 21.5 KB |
|
|
91
|
-
| draco3d — `draco_wasm_wrapper.js` (runtime fetch) | 78 KB | 13.1 KB | 11.0 KB |
|
|
92
|
-
| draco3d — `draco_decoder.wasm` (runtime fetch) | 279 KB | 86.1 KB | 64.6 KB |
|
|
93
|
-
|
|
94
|
-
Totals over the wire (brotli): **minidraco ~45 KB** (23 KB in the app bundle + 22 KB worker
|
|
95
|
-
chunk, or just 23 KB with `setWorkerLimit(0)`), **draco.js ~22 KB**, **draco3d wasm ~76 KB** —
|
|
96
|
-
and the wasm files are separate runtime fetches you must host, on the critical path of the first
|
|
97
|
-
decode, while the JS decoders ship inside your existing bundle chunks.
|
|
60
|
+
Over the wire (brotli): **~23 KB** ships in your app bundle. With the worker pool on (default) the
|
|
61
|
+
browser also fetches a **~22 KB** worker chunk on the first decode — **~45 KB** total; `workers: false`
|
|
62
|
+
skips that fetch. **draco.js ~22 KB**, **draco3d wasm ~76 KB** — the wasm is a separate file you
|
|
63
|
+
must host, while the JS decoders ship inside your bundle.
|
|
98
64
|
|
|
99
65
|
## Monorepo
|
|
100
66
|
|
|
101
67
|
- `library/` — the `minidraco` package
|
|
102
|
-
- `example/` — Next.js + React Three Fiber demo
|
|
103
|
-
animation playback, in-browser benchmark at `/bench`)
|
|
68
|
+
- `example/` — Next.js + React Three Fiber demo with an in-browser benchmark at `/bench`
|
|
104
69
|
|
|
105
70
|
```sh
|
|
106
71
|
bun install
|
|
107
|
-
bun dev #
|
|
108
|
-
bun run all # format
|
|
109
|
-
bun run bench # decoder
|
|
72
|
+
bun dev # watch build + demo
|
|
73
|
+
bun run all # format, lint, typecheck, tests
|
|
74
|
+
bun run bench # cross-decoder benchmark
|
|
110
75
|
```
|
|
111
76
|
|
|
112
|
-
The test suite decodes every Draco primitive of the bundle GLBs with minidraco and the official
|
|
113
|
-
draco3d wasm decoder and compares indices and every attribute value, plus 13 raw `.drc` fixtures
|
|
114
|
-
from Draco's test corpus covering the encodings the bundles don't hit.
|
|
115
|
-
|
|
116
77
|
## License
|
|
117
78
|
|
|
118
|
-
MIT —
|
|
119
|
-
|
|
79
|
+
MIT — derived from [mrdoob/draco.js](https://github.com/mrdoob/draco.js) (MIT), implementing
|
|
80
|
+
Google's [Draco](https://github.com/google/draco) bitstream (Apache-2.0).
|
package/dist/index.d.ts
CHANGED
|
@@ -1,45 +1,3 @@
|
|
|
1
|
-
declare class BitDecoder {
|
|
2
|
-
_bitBuffer: Uint8Array | null;
|
|
3
|
-
_bitOffset: number;
|
|
4
|
-
_byteLength: number;
|
|
5
|
-
constructor();
|
|
6
|
-
reset(uint8Array: Uint8Array, byteLength: number): void;
|
|
7
|
-
bitsDecoded(): number;
|
|
8
|
-
getBits(nbits: number): number | undefined;
|
|
9
|
-
}
|
|
10
|
-
declare class DecoderBuffer {
|
|
11
|
-
_data: Uint8Array | null;
|
|
12
|
-
_dataView: DataView | null;
|
|
13
|
-
_dataSize: number;
|
|
14
|
-
_pos: number;
|
|
15
|
-
_bitDecoder: BitDecoder;
|
|
16
|
-
_bitMode: boolean;
|
|
17
|
-
_bitstreamVersion: number;
|
|
18
|
-
constructor();
|
|
19
|
-
init(data: ArrayBuffer | Uint8Array | ArrayLike<number>, dataSize?: number, version?: number): void;
|
|
20
|
-
decodeUint8(): number | undefined;
|
|
21
|
-
decodeInt8(): number | undefined;
|
|
22
|
-
decodeUint16(): number | undefined;
|
|
23
|
-
decodeUint32(): number | undefined;
|
|
24
|
-
decodeInt32(): number | undefined;
|
|
25
|
-
decodeFloat32(): number | undefined;
|
|
26
|
-
decodeUint64(): number | undefined;
|
|
27
|
-
decodeBytes(size: number): Uint8Array | undefined;
|
|
28
|
-
startBitDecoding(decodeSize: boolean): number | undefined;
|
|
29
|
-
endBitDecoding(): void;
|
|
30
|
-
decodeLeastSignificantBits32(nbits: number): number | undefined;
|
|
31
|
-
decodeVarintUint32(): number | undefined;
|
|
32
|
-
decodeVarintUint64(): number | undefined;
|
|
33
|
-
advance(bytes: number): void;
|
|
34
|
-
get bitstreamVersion(): number;
|
|
35
|
-
set bitstreamVersion(v: number);
|
|
36
|
-
get data(): Uint8Array;
|
|
37
|
-
get dataHead(): Uint8Array;
|
|
38
|
-
get remainingSize(): number;
|
|
39
|
-
get decodedSize(): number;
|
|
40
|
-
get bitDecoderActive(): boolean;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
1
|
declare class DataBuffer {
|
|
44
2
|
_data: Uint8Array;
|
|
45
3
|
constructor();
|
|
@@ -162,68 +120,12 @@ declare class Mesh extends PointCloud {
|
|
|
162
120
|
elementType: number;
|
|
163
121
|
}[];
|
|
164
122
|
constructor();
|
|
165
|
-
_ensureFaceCapacity(numFaces: number): void;
|
|
166
|
-
addFace(face: ArrayLike<number>): void;
|
|
167
123
|
setNumFaces(numFaces: number): void;
|
|
168
124
|
numFaces(): number;
|
|
169
125
|
face(faceId: number): number[];
|
|
170
126
|
setAttribute(attId: number, pa: PointAttribute): void;
|
|
171
127
|
}
|
|
172
128
|
|
|
173
|
-
declare class DracoOptions {
|
|
174
|
-
_globalOptions: Map<string, unknown>;
|
|
175
|
-
_attributeOptions: Map<number, Map<string, unknown>>;
|
|
176
|
-
constructor();
|
|
177
|
-
getGlobalBool(name: string, defaultVal: boolean): boolean;
|
|
178
|
-
findAttributeOptions(attKey: number): Map<string, unknown> | null;
|
|
179
|
-
getAttributeBool(attKey: number, name: string, defaultVal: boolean): boolean;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
declare class DecoderOptions extends DracoOptions {
|
|
183
|
-
constructor();
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
declare class Decoder {
|
|
187
|
-
options_: DecoderOptions;
|
|
188
|
-
constructor();
|
|
189
|
-
static getEncodedGeometryType(inBuffer: DecoderBuffer): number;
|
|
190
|
-
decodeMeshFromBuffer(inBuffer: DecoderBuffer): {
|
|
191
|
-
mesh: Mesh | null;
|
|
192
|
-
ok: boolean;
|
|
193
|
-
message: string;
|
|
194
|
-
};
|
|
195
|
-
decodeBufferToMesh(inBuffer: DecoderBuffer, outGeometry: Mesh): {
|
|
196
|
-
ok: boolean;
|
|
197
|
-
message: string;
|
|
198
|
-
};
|
|
199
|
-
options(): DecoderOptions;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
declare const DataType: {
|
|
203
|
-
readonly INVALID: 0;
|
|
204
|
-
readonly INT8: 1;
|
|
205
|
-
readonly UINT8: 2;
|
|
206
|
-
readonly INT16: 3;
|
|
207
|
-
readonly UINT16: 4;
|
|
208
|
-
readonly INT32: 5;
|
|
209
|
-
readonly UINT32: 6;
|
|
210
|
-
readonly INT64: 7;
|
|
211
|
-
readonly UINT64: 8;
|
|
212
|
-
readonly FLOAT32: 9;
|
|
213
|
-
readonly FLOAT64: 10;
|
|
214
|
-
readonly BOOL: 11;
|
|
215
|
-
readonly TYPES_COUNT: 12;
|
|
216
|
-
};
|
|
217
|
-
type DataType = (typeof DataType)[keyof typeof DataType];
|
|
218
|
-
|
|
219
|
-
declare const EncodedGeometryType: {
|
|
220
|
-
readonly INVALID_GEOMETRY_TYPE: -1;
|
|
221
|
-
readonly POINT_CLOUD: 0;
|
|
222
|
-
readonly TRIANGULAR_MESH: 1;
|
|
223
|
-
readonly NUM_ENCODED_GEOMETRY_TYPES: 2;
|
|
224
|
-
};
|
|
225
|
-
type EncodedGeometryType = (typeof EncodedGeometryType)[keyof typeof EncodedGeometryType];
|
|
226
|
-
|
|
227
129
|
declare const decodeDracoMesh: (data: Uint8Array) => Mesh;
|
|
228
130
|
|
|
229
|
-
export {
|
|
131
|
+
export { Type as GeometryAttributeType, Mesh, PointAttribute, decodeDracoMesh };
|