@loaders.gl/draco 4.3.2 → 4.4.0-alpha.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.
Files changed (41) hide show
  1. package/dist/dist.dev.js +12966 -1012
  2. package/dist/dist.min.js +12 -3
  3. package/dist/draco-arrow-loader.d.ts +30 -0
  4. package/dist/draco-arrow-loader.d.ts.map +1 -0
  5. package/dist/draco-arrow-loader.js +19 -0
  6. package/dist/draco-format.d.ts +13 -0
  7. package/dist/draco-format.d.ts.map +1 -0
  8. package/dist/draco-format.js +15 -0
  9. package/dist/draco-loader.d.ts +28 -1
  10. package/dist/draco-loader.d.ts.map +1 -1
  11. package/dist/draco-loader.js +20 -1
  12. package/dist/draco-worker-node.js +253 -115
  13. package/dist/draco-worker-node.js.map +4 -4
  14. package/dist/draco-worker.js +111 -111
  15. package/dist/draco-worker.js.map +4 -4
  16. package/dist/draco-writer-worker-node.js +145 -6
  17. package/dist/draco-writer-worker-node.js.map +4 -4
  18. package/dist/draco-writer-worker.js +3 -2
  19. package/dist/draco-writer-worker.js.map +2 -2
  20. package/dist/draco-writer.d.ts +16 -0
  21. package/dist/draco-writer.d.ts.map +1 -1
  22. package/dist/draco-writer.js +19 -0
  23. package/dist/index.cjs +818 -802
  24. package/dist/index.cjs.map +4 -4
  25. package/dist/index.d.ts +5 -48
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +6 -38
  28. package/dist/lib/draco-builder.d.ts.map +1 -1
  29. package/dist/lib/draco-parser.d.ts.map +1 -1
  30. package/dist/lib/draco-parser.js +3 -1
  31. package/dist/lib/utils/get-draco-schema.d.ts.map +1 -1
  32. package/dist/lib/utils/get-draco-schema.js +1 -1
  33. package/dist/lib/utils/version.js +1 -1
  34. package/package.json +7 -6
  35. package/src/draco-arrow-loader.ts +25 -0
  36. package/src/draco-format.ts +18 -0
  37. package/src/draco-loader.ts +22 -2
  38. package/src/draco-writer.ts +21 -0
  39. package/src/index.ts +5 -45
  40. package/src/lib/draco-parser.ts +3 -1
  41. package/src/lib/utils/get-draco-schema.ts +2 -1
package/dist/index.cjs CHANGED
@@ -22,975 +22,977 @@ var dist_exports = {};
22
22
  __export(dist_exports, {
23
23
  DRACO_EXTERNAL_LIBRARIES: () => DRACO_EXTERNAL_LIBRARIES,
24
24
  DRACO_EXTERNAL_LIBRARY_URLS: () => DRACO_EXTERNAL_LIBRARY_URLS,
25
- DracoLoader: () => DracoLoader2,
26
- DracoWorkerLoader: () => DracoLoader,
25
+ DracoArrowLoader: () => DracoArrowLoader,
26
+ DracoLoader: () => DracoLoader,
27
+ DracoWorkerLoader: () => DracoWorkerLoader,
27
28
  DracoWriter: () => DracoWriter,
28
29
  DracoWriterWorker: () => DracoWriterWorker
29
30
  });
30
31
  module.exports = __toCommonJS(dist_exports);
31
32
 
32
- // dist/lib/utils/version.js
33
- var VERSION = true ? "4.3.1" : "latest";
34
-
35
- // dist/draco-loader.js
36
- var DracoLoader = {
37
- dataType: null,
38
- batchType: null,
39
- name: "Draco",
40
- id: "draco",
41
- module: "draco",
42
- // shapes: ['mesh'],
43
- version: VERSION,
44
- worker: true,
45
- extensions: ["drc"],
46
- mimeTypes: ["application/octet-stream"],
47
- binary: true,
48
- tests: ["DRACO"],
49
- options: {
50
- draco: {
51
- decoderType: typeof WebAssembly === "object" ? "wasm" : "js",
52
- // 'js' for IE11
53
- libraryPath: "libs/",
54
- extraAttributes: {},
55
- attributeNameEntry: void 0
56
- }
57
- }
33
+ // dist/lib/draco-module-loader.js
34
+ var import_worker_utils = require("@loaders.gl/worker-utils");
35
+ var DRACO_DECODER_VERSION = "1.5.6";
36
+ var DRACO_ENCODER_VERSION = "1.4.1";
37
+ var STATIC_DECODER_URL = `https://www.gstatic.com/draco/versioned/decoders/${DRACO_DECODER_VERSION}`;
38
+ var DRACO_EXTERNAL_LIBRARIES = {
39
+ /** The primary Draco3D encoder, javascript wrapper part */
40
+ DECODER: "draco_wasm_wrapper.js",
41
+ /** The primary draco decoder, compiled web assembly part */
42
+ DECODER_WASM: "draco_decoder.wasm",
43
+ /** Fallback decoder for non-webassebly environments. Very big bundle, lower performance */
44
+ FALLBACK_DECODER: "draco_decoder.js",
45
+ /** Draco encoder */
46
+ ENCODER: "draco_encoder.js"
58
47
  };
59
-
60
- // dist/lib/draco-parser.js
61
- var import_schema2 = require("@loaders.gl/schema");
62
-
63
- // dist/lib/utils/get-draco-schema.js
64
- var import_schema = require("@loaders.gl/schema");
65
- function getDracoSchema(attributes, loaderData, indices) {
66
- const metadata = makeMetadata(loaderData.metadata);
67
- const fields = [];
68
- const namedLoaderDataAttributes = transformAttributesLoaderData(loaderData.attributes);
69
- for (const attributeName in attributes) {
70
- const attribute = attributes[attributeName];
71
- const field = getArrowFieldFromAttribute(attributeName, attribute, namedLoaderDataAttributes[attributeName]);
72
- fields.push(field);
73
- }
74
- if (indices) {
75
- const indicesField = getArrowFieldFromAttribute("indices", indices);
76
- fields.push(indicesField);
48
+ var DRACO_EXTERNAL_LIBRARY_URLS = {
49
+ [DRACO_EXTERNAL_LIBRARIES.DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER}`,
50
+ [DRACO_EXTERNAL_LIBRARIES.DECODER_WASM]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER_WASM}`,
51
+ [DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER}`,
52
+ [DRACO_EXTERNAL_LIBRARIES.ENCODER]: `https://raw.githubusercontent.com/google/draco/${DRACO_ENCODER_VERSION}/javascript/${DRACO_EXTERNAL_LIBRARIES.ENCODER}`
53
+ };
54
+ var loadDecoderPromise;
55
+ var loadEncoderPromise;
56
+ async function loadDracoDecoderModule(options) {
57
+ const modules = options.modules || {};
58
+ if (modules.draco3d) {
59
+ loadDecoderPromise ||= modules.draco3d.createDecoderModule({}).then((draco) => {
60
+ return { draco };
61
+ });
62
+ } else {
63
+ loadDecoderPromise ||= loadDracoDecoder(options);
77
64
  }
78
- return { fields, metadata };
65
+ return await loadDecoderPromise;
79
66
  }
80
- function transformAttributesLoaderData(loaderData) {
81
- const result = {};
82
- for (const key in loaderData) {
83
- const dracoAttribute = loaderData[key];
84
- result[dracoAttribute.name || "undefined"] = dracoAttribute;
67
+ async function loadDracoEncoderModule(options) {
68
+ const modules = options.modules || {};
69
+ if (modules.draco3d) {
70
+ loadEncoderPromise ||= modules.draco3d.createEncoderModule({}).then((draco) => {
71
+ return { draco };
72
+ });
73
+ } else {
74
+ loadEncoderPromise ||= loadDracoEncoder(options);
85
75
  }
86
- return result;
76
+ return await loadEncoderPromise;
87
77
  }
88
- function getArrowFieldFromAttribute(attributeName, attribute, loaderData) {
89
- const metadataMap = loaderData ? makeMetadata(loaderData.metadata) : void 0;
90
- const field = (0, import_schema.deduceMeshField)(attributeName, attribute, metadataMap);
91
- return field;
78
+ async function loadDracoDecoder(options) {
79
+ let DracoDecoderModule;
80
+ let wasmBinary;
81
+ switch (options.draco && options.draco.decoderType) {
82
+ case "js":
83
+ DracoDecoderModule = await (0, import_worker_utils.loadLibrary)(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER);
84
+ break;
85
+ case "wasm":
86
+ default:
87
+ [DracoDecoderModule, wasmBinary] = await Promise.all([
88
+ await (0, import_worker_utils.loadLibrary)(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.DECODER),
89
+ await (0, import_worker_utils.loadLibrary)(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM], "draco", options, DRACO_EXTERNAL_LIBRARIES.DECODER_WASM)
90
+ ]);
91
+ }
92
+ DracoDecoderModule = DracoDecoderModule || globalThis.DracoDecoderModule;
93
+ return await initializeDracoDecoder(DracoDecoderModule, wasmBinary);
92
94
  }
93
- function makeMetadata(metadata) {
94
- Object.entries(metadata);
95
- const serializedMetadata = {};
96
- for (const key in metadata) {
97
- serializedMetadata[`${key}.string`] = JSON.stringify(metadata[key]);
95
+ function initializeDracoDecoder(DracoDecoderModule, wasmBinary) {
96
+ const options = {};
97
+ if (wasmBinary) {
98
+ options.wasmBinary = wasmBinary;
98
99
  }
99
- return serializedMetadata;
100
+ return new Promise((resolve) => {
101
+ DracoDecoderModule({
102
+ ...options,
103
+ onModuleLoaded: (draco) => resolve({ draco })
104
+ // Module is Promise-like. Wrap in object to avoid loop.
105
+ });
106
+ });
107
+ }
108
+ async function loadDracoEncoder(options) {
109
+ let DracoEncoderModule = await (0, import_worker_utils.loadLibrary)(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.ENCODER);
110
+ DracoEncoderModule = DracoEncoderModule || globalThis.DracoEncoderModule;
111
+ return new Promise((resolve) => {
112
+ DracoEncoderModule({
113
+ onModuleLoaded: (draco) => resolve({ draco })
114
+ // Module is Promise-like. Wrap in object to avoid loop.
115
+ });
116
+ });
100
117
  }
101
118
 
102
- // dist/lib/draco-parser.js
103
- var DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP = {
119
+ // dist/lib/draco-builder.js
120
+ var GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP = {
104
121
  POSITION: "POSITION",
105
122
  NORMAL: "NORMAL",
106
- COLOR: "COLOR_0",
107
- TEX_COORD: "TEXCOORD_0"
123
+ COLOR_0: "COLOR",
124
+ TEXCOORD_0: "TEX_COORD"
108
125
  };
109
- var DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP = {
110
- 1: Int8Array,
111
- 2: Uint8Array,
112
- 3: Int16Array,
113
- 4: Uint16Array,
114
- 5: Int32Array,
115
- 6: Uint32Array,
116
- // 7: BigInt64Array,
117
- // 8: BigUint64Array,
118
- 9: Float32Array
119
- // 10: Float64Array
120
- // 11: BOOL - What array type do we use for this?
126
+ var noop = () => {
121
127
  };
122
- var INDEX_ITEM_SIZE = 4;
123
- var DracoParser = class {
128
+ var DracoBuilder = class {
124
129
  draco;
125
- decoder;
126
- metadataQuerier;
130
+ dracoEncoder;
131
+ dracoMeshBuilder;
132
+ dracoMetadataBuilder;
133
+ log;
127
134
  // draco - the draco decoder, either import `draco3d` or load dynamically
128
135
  constructor(draco) {
129
136
  this.draco = draco;
130
- this.decoder = new this.draco.Decoder();
131
- this.metadataQuerier = new this.draco.MetadataQuerier();
137
+ this.dracoEncoder = new this.draco.Encoder();
138
+ this.dracoMeshBuilder = new this.draco.MeshBuilder();
139
+ this.dracoMetadataBuilder = new this.draco.MetadataBuilder();
132
140
  }
133
- /**
134
- * Destroy draco resources
135
- */
136
141
  destroy() {
137
- this.draco.destroy(this.decoder);
138
- this.draco.destroy(this.metadataQuerier);
142
+ this.destroyEncodedObject(this.dracoMeshBuilder);
143
+ this.destroyEncodedObject(this.dracoEncoder);
144
+ this.destroyEncodedObject(this.dracoMetadataBuilder);
145
+ this.dracoMeshBuilder = null;
146
+ this.dracoEncoder = null;
147
+ this.draco = null;
139
148
  }
140
- /**
141
- * NOTE: caller must call `destroyGeometry` on the return value after using it
142
- * @param arrayBuffer
143
- * @param options
144
- */
145
- parseSync(arrayBuffer, options = {}) {
146
- const buffer = new this.draco.DecoderBuffer();
147
- buffer.Init(new Int8Array(arrayBuffer), arrayBuffer.byteLength);
148
- this._disableAttributeTransforms(options);
149
- const geometry_type = this.decoder.GetEncodedGeometryType(buffer);
150
- const dracoGeometry = geometry_type === this.draco.TRIANGULAR_MESH ? new this.draco.Mesh() : new this.draco.PointCloud();
151
- try {
152
- let dracoStatus;
153
- switch (geometry_type) {
154
- case this.draco.TRIANGULAR_MESH:
155
- dracoStatus = this.decoder.DecodeBufferToMesh(buffer, dracoGeometry);
156
- break;
157
- case this.draco.POINT_CLOUD:
158
- dracoStatus = this.decoder.DecodeBufferToPointCloud(buffer, dracoGeometry);
159
- break;
160
- default:
161
- throw new Error("DRACO: Unknown geometry type.");
162
- }
163
- if (!dracoStatus.ok() || !dracoGeometry.ptr) {
164
- const message = `DRACO decompression failed: ${dracoStatus.error_msg()}`;
165
- throw new Error(message);
166
- }
167
- const loaderData = this._getDracoLoaderData(dracoGeometry, geometry_type, options);
168
- const geometry = this._getMeshData(dracoGeometry, loaderData, options);
169
- const boundingBox = (0, import_schema2.getMeshBoundingBox)(geometry.attributes);
170
- const schema = getDracoSchema(geometry.attributes, loaderData, geometry.indices);
171
- const data = {
172
- loader: "draco",
173
- loaderData,
174
- header: {
175
- vertexCount: dracoGeometry.num_points(),
176
- boundingBox
177
- },
178
- ...geometry,
179
- schema
180
- };
181
- return data;
182
- } finally {
183
- this.draco.destroy(buffer);
184
- if (dracoGeometry) {
185
- this.draco.destroy(dracoGeometry);
186
- }
149
+ // TBD - when does this need to be called?
150
+ destroyEncodedObject(object) {
151
+ if (object) {
152
+ this.draco.destroy(object);
187
153
  }
188
154
  }
189
- // Draco specific "loader data"
190
155
  /**
191
- * Extract
192
- * @param dracoGeometry
193
- * @param geometry_type
156
+ * Encode mesh or point cloud
157
+ * @param mesh =({})
194
158
  * @param options
195
- * @returns
196
159
  */
197
- _getDracoLoaderData(dracoGeometry, geometry_type, options) {
198
- const metadata = this._getTopLevelMetadata(dracoGeometry);
199
- const attributes = this._getDracoAttributes(dracoGeometry, options);
200
- return {
201
- geometry_type,
202
- num_attributes: dracoGeometry.num_attributes(),
203
- num_points: dracoGeometry.num_points(),
204
- num_faces: dracoGeometry instanceof this.draco.Mesh ? dracoGeometry.num_faces() : 0,
205
- metadata,
206
- attributes
207
- };
160
+ encodeSync(mesh, options = {}) {
161
+ this.log = noop;
162
+ this._setOptions(options);
163
+ return options.pointcloud ? this._encodePointCloud(mesh, options) : this._encodeMesh(mesh, options);
208
164
  }
209
- /**
210
- * Extract all draco provided information and metadata for each attribute
211
- * @param dracoGeometry
212
- * @param options
213
- * @returns
214
- */
215
- _getDracoAttributes(dracoGeometry, options) {
216
- const dracoAttributes = {};
217
- for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) {
218
- const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId);
219
- const metadata = this._getAttributeMetadata(dracoGeometry, attributeId);
220
- dracoAttributes[dracoAttribute.unique_id()] = {
221
- unique_id: dracoAttribute.unique_id(),
222
- attribute_type: dracoAttribute.attribute_type(),
223
- data_type: dracoAttribute.data_type(),
224
- num_components: dracoAttribute.num_components(),
225
- byte_offset: dracoAttribute.byte_offset(),
226
- byte_stride: dracoAttribute.byte_stride(),
227
- normalized: dracoAttribute.normalized(),
228
- attribute_index: attributeId,
229
- metadata
230
- };
231
- const quantization = this._getQuantizationTransform(dracoAttribute, options);
232
- if (quantization) {
233
- dracoAttributes[dracoAttribute.unique_id()].quantization_transform = quantization;
165
+ // PRIVATE
166
+ _getAttributesFromMesh(mesh) {
167
+ const attributes = { ...mesh, ...mesh.attributes };
168
+ if (mesh.indices) {
169
+ attributes.indices = mesh.indices;
170
+ }
171
+ return attributes;
172
+ }
173
+ _encodePointCloud(pointcloud, options) {
174
+ const dracoPointCloud = new this.draco.PointCloud();
175
+ if (options.metadata) {
176
+ this._addGeometryMetadata(dracoPointCloud, options.metadata);
177
+ }
178
+ const attributes = this._getAttributesFromMesh(pointcloud);
179
+ this._createDracoPointCloud(dracoPointCloud, attributes, options);
180
+ const dracoData = new this.draco.DracoInt8Array();
181
+ try {
182
+ const encodedLen = this.dracoEncoder.EncodePointCloudToDracoBuffer(dracoPointCloud, false, dracoData);
183
+ if (!(encodedLen > 0)) {
184
+ throw new Error("Draco encoding failed.");
234
185
  }
235
- const octahedron = this._getOctahedronTransform(dracoAttribute, options);
236
- if (octahedron) {
237
- dracoAttributes[dracoAttribute.unique_id()].octahedron_transform = octahedron;
186
+ this.log(`DRACO encoded ${dracoPointCloud.num_points()} points
187
+ with ${dracoPointCloud.num_attributes()} attributes into ${encodedLen} bytes`);
188
+ return dracoInt8ArrayToArrayBuffer(dracoData);
189
+ } finally {
190
+ this.destroyEncodedObject(dracoData);
191
+ this.destroyEncodedObject(dracoPointCloud);
192
+ }
193
+ }
194
+ _encodeMesh(mesh, options) {
195
+ const dracoMesh = new this.draco.Mesh();
196
+ if (options.metadata) {
197
+ this._addGeometryMetadata(dracoMesh, options.metadata);
198
+ }
199
+ const attributes = this._getAttributesFromMesh(mesh);
200
+ this._createDracoMesh(dracoMesh, attributes, options);
201
+ const dracoData = new this.draco.DracoInt8Array();
202
+ try {
203
+ const encodedLen = this.dracoEncoder.EncodeMeshToDracoBuffer(dracoMesh, dracoData);
204
+ if (encodedLen <= 0) {
205
+ throw new Error("Draco encoding failed.");
238
206
  }
207
+ this.log(`DRACO encoded ${dracoMesh.num_points()} points
208
+ with ${dracoMesh.num_attributes()} attributes into ${encodedLen} bytes`);
209
+ return dracoInt8ArrayToArrayBuffer(dracoData);
210
+ } finally {
211
+ this.destroyEncodedObject(dracoData);
212
+ this.destroyEncodedObject(dracoMesh);
239
213
  }
240
- return dracoAttributes;
241
214
  }
242
215
  /**
243
- * Get standard loaders.gl mesh category data
244
- * Extracts the geometry from draco
245
- * @param dracoGeometry
246
- * @param options
216
+ * Set encoding options.
217
+ * @param {{speed?: any; method?: any; quantization?: any;}} options
247
218
  */
248
- _getMeshData(dracoGeometry, loaderData, options) {
249
- const attributes = this._getMeshAttributes(loaderData, dracoGeometry, options);
250
- const positionAttribute = attributes.POSITION;
251
- if (!positionAttribute) {
252
- throw new Error("DRACO: No position attribute found.");
219
+ _setOptions(options) {
220
+ if ("speed" in options) {
221
+ this.dracoEncoder.SetSpeedOptions(...options.speed);
253
222
  }
254
- if (dracoGeometry instanceof this.draco.Mesh) {
255
- switch (options.topology) {
256
- case "triangle-strip":
257
- return {
258
- topology: "triangle-strip",
259
- mode: 4,
260
- // GL.TRIANGLES
261
- attributes,
262
- indices: {
263
- value: this._getTriangleStripIndices(dracoGeometry),
264
- size: 1
265
- }
266
- };
267
- case "triangle-list":
268
- default:
269
- return {
270
- topology: "triangle-list",
271
- mode: 5,
272
- // GL.TRIANGLE_STRIP
273
- attributes,
274
- indices: {
275
- value: this._getTriangleListIndices(dracoGeometry),
276
- size: 1
277
- }
278
- };
279
- }
223
+ if ("method" in options) {
224
+ const dracoMethod = this.draco[options.method || "MESH_SEQUENTIAL_ENCODING"];
225
+ this.dracoEncoder.SetEncodingMethod(dracoMethod);
280
226
  }
281
- return {
282
- topology: "point-list",
283
- mode: 0,
284
- // GL.POINTS
285
- attributes
286
- };
287
- }
288
- _getMeshAttributes(loaderData, dracoGeometry, options) {
289
- const attributes = {};
290
- for (const loaderAttribute of Object.values(loaderData.attributes)) {
291
- const attributeName = this._deduceAttributeName(loaderAttribute, options);
292
- loaderAttribute.name = attributeName;
293
- const values = this._getAttributeValues(dracoGeometry, loaderAttribute);
294
- if (values) {
295
- const { value, size } = values;
296
- attributes[attributeName] = {
297
- value,
298
- size,
299
- byteOffset: loaderAttribute.byte_offset,
300
- byteStride: loaderAttribute.byte_stride,
301
- normalized: loaderAttribute.normalized
302
- };
227
+ if ("quantization" in options) {
228
+ for (const attribute in options.quantization) {
229
+ const bits = options.quantization[attribute];
230
+ const dracoPosition = this.draco[attribute];
231
+ this.dracoEncoder.SetAttributeQuantization(dracoPosition, bits);
303
232
  }
304
233
  }
305
- return attributes;
306
234
  }
307
- // MESH INDICES EXTRACTION
308
235
  /**
309
- * For meshes, we need indices to define the faces.
310
- * @param dracoGeometry
236
+ * @param {Mesh} dracoMesh
237
+ * @param {object} attributes
238
+ * @returns {Mesh}
311
239
  */
312
- _getTriangleListIndices(dracoGeometry) {
313
- const numFaces = dracoGeometry.num_faces();
314
- const numIndices = numFaces * 3;
315
- const byteLength = numIndices * INDEX_ITEM_SIZE;
316
- const ptr = this.draco._malloc(byteLength);
240
+ _createDracoMesh(dracoMesh, attributes, options) {
241
+ const optionalMetadata = options.attributesMetadata || {};
317
242
  try {
318
- this.decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr);
319
- return new Uint32Array(this.draco.HEAPF32.buffer, ptr, numIndices).slice();
320
- } finally {
321
- this.draco._free(ptr);
243
+ const positions = this._getPositionAttribute(attributes);
244
+ if (!positions) {
245
+ throw new Error("positions");
246
+ }
247
+ const vertexCount = positions.length / 3;
248
+ for (let attributeName in attributes) {
249
+ const attribute = attributes[attributeName];
250
+ attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName;
251
+ const uniqueId = this._addAttributeToMesh(dracoMesh, attributeName, attribute, vertexCount);
252
+ if (uniqueId !== -1) {
253
+ this._addAttributeMetadata(dracoMesh, uniqueId, {
254
+ name: attributeName,
255
+ ...optionalMetadata[attributeName] || {}
256
+ });
257
+ }
258
+ }
259
+ } catch (error) {
260
+ this.destroyEncodedObject(dracoMesh);
261
+ throw error;
322
262
  }
263
+ return dracoMesh;
323
264
  }
324
265
  /**
325
- * For meshes, we need indices to define the faces.
326
- * @param dracoGeometry
266
+ * @param {} dracoPointCloud
267
+ * @param {object} attributes
327
268
  */
328
- _getTriangleStripIndices(dracoGeometry) {
329
- const dracoArray = new this.draco.DracoInt32Array();
269
+ _createDracoPointCloud(dracoPointCloud, attributes, options) {
270
+ const optionalMetadata = options.attributesMetadata || {};
330
271
  try {
331
- this.decoder.GetTriangleStripsFromMesh(dracoGeometry, dracoArray);
332
- return getUint32Array(dracoArray);
333
- } finally {
334
- this.draco.destroy(dracoArray);
272
+ const positions = this._getPositionAttribute(attributes);
273
+ if (!positions) {
274
+ throw new Error("positions");
275
+ }
276
+ const vertexCount = positions.length / 3;
277
+ for (let attributeName in attributes) {
278
+ const attribute = attributes[attributeName];
279
+ attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName;
280
+ const uniqueId = this._addAttributeToMesh(dracoPointCloud, attributeName, attribute, vertexCount);
281
+ if (uniqueId !== -1) {
282
+ this._addAttributeMetadata(dracoPointCloud, uniqueId, {
283
+ name: attributeName,
284
+ ...optionalMetadata[attributeName] || {}
285
+ });
286
+ }
287
+ }
288
+ } catch (error) {
289
+ this.destroyEncodedObject(dracoPointCloud);
290
+ throw error;
335
291
  }
292
+ return dracoPointCloud;
336
293
  }
337
294
  /**
338
- *
339
- * @param dracoGeometry
340
- * @param dracoAttribute
295
+ * @param mesh
341
296
  * @param attributeName
297
+ * @param attribute
298
+ * @param vertexCount
342
299
  */
343
- _getAttributeValues(dracoGeometry, attribute) {
344
- const TypedArrayCtor = DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP[attribute.data_type];
345
- if (!TypedArrayCtor) {
346
- console.warn(`DRACO: Unsupported attribute type ${attribute.data_type}`);
347
- return null;
300
+ _addAttributeToMesh(mesh, attributeName, attribute, vertexCount) {
301
+ if (!ArrayBuffer.isView(attribute)) {
302
+ return -1;
348
303
  }
349
- const numComponents = attribute.num_components;
350
- const numPoints = dracoGeometry.num_points();
351
- const numValues = numPoints * numComponents;
352
- const byteLength = numValues * TypedArrayCtor.BYTES_PER_ELEMENT;
353
- const dataType = getDracoDataType(this.draco, TypedArrayCtor);
354
- let value;
355
- const ptr = this.draco._malloc(byteLength);
356
- try {
357
- const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attribute.attribute_index);
358
- this.decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, dracoAttribute, dataType, byteLength, ptr);
359
- value = new TypedArrayCtor(this.draco.HEAPF32.buffer, ptr, numValues).slice();
360
- } finally {
361
- this.draco._free(ptr);
304
+ const type = this._getDracoAttributeType(attributeName);
305
+ const size = attribute.length / vertexCount;
306
+ if (type === "indices") {
307
+ const numFaces = attribute.length / 3;
308
+ this.log(`Adding attribute ${attributeName}, size ${numFaces}`);
309
+ this.dracoMeshBuilder.AddFacesToMesh(mesh, numFaces, attribute);
310
+ return -1;
362
311
  }
363
- return { value, size: numComponents };
364
- }
365
- // Attribute names
366
- /**
367
- * DRACO does not store attribute names - We need to deduce an attribute name
368
- * for each attribute
369
- _getAttributeNames(
370
- dracoGeometry: Mesh | PointCloud,
371
- options: DracoParseOptions
372
- ): {[unique_id: number]: string} {
373
- const attributeNames: {[unique_id: number]: string} = {};
374
- for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) {
375
- const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId);
376
- const attributeName = this._deduceAttributeName(dracoAttribute, options);
377
- attributeNames[attributeName] = attributeName;
312
+ this.log(`Adding attribute ${attributeName}, size ${size}`);
313
+ const builder = this.dracoMeshBuilder;
314
+ const { buffer } = attribute;
315
+ switch (attribute.constructor) {
316
+ case Int8Array:
317
+ return builder.AddInt8Attribute(mesh, type, vertexCount, size, new Int8Array(buffer));
318
+ case Int16Array:
319
+ return builder.AddInt16Attribute(mesh, type, vertexCount, size, new Int16Array(buffer));
320
+ case Int32Array:
321
+ return builder.AddInt32Attribute(mesh, type, vertexCount, size, new Int32Array(buffer));
322
+ case Uint8Array:
323
+ case Uint8ClampedArray:
324
+ return builder.AddUInt8Attribute(mesh, type, vertexCount, size, new Uint8Array(buffer));
325
+ case Uint16Array:
326
+ return builder.AddUInt16Attribute(mesh, type, vertexCount, size, new Uint16Array(buffer));
327
+ case Uint32Array:
328
+ return builder.AddUInt32Attribute(mesh, type, vertexCount, size, new Uint32Array(buffer));
329
+ case Float32Array:
330
+ return builder.AddFloatAttribute(mesh, type, vertexCount, size, new Float32Array(buffer));
331
+ default:
332
+ console.warn("Unsupported attribute type", attribute);
333
+ return -1;
378
334
  }
379
- return attributeNames;
380
335
  }
381
- */
382
336
  /**
383
- * Deduce an attribute name.
384
- * @note DRACO does not save attribute names, just general type (POSITION, COLOR)
385
- * to help optimize compression. We generate GLTF compatible names for the Draco-recognized
386
- * types
387
- * @param attributeData
337
+ * DRACO can compress attributes of know type better
338
+ * TODO - expose an attribute type map?
339
+ * @param attributeName
388
340
  */
389
- _deduceAttributeName(attribute, options) {
390
- const uniqueId = attribute.unique_id;
391
- for (const [attributeName, attributeUniqueId] of Object.entries(options.extraAttributes || {})) {
392
- if (attributeUniqueId === uniqueId) {
393
- return attributeName;
394
- }
341
+ _getDracoAttributeType(attributeName) {
342
+ switch (attributeName.toLowerCase()) {
343
+ case "indices":
344
+ return "indices";
345
+ case "position":
346
+ case "positions":
347
+ case "vertices":
348
+ return this.draco.POSITION;
349
+ case "normal":
350
+ case "normals":
351
+ return this.draco.NORMAL;
352
+ case "color":
353
+ case "colors":
354
+ return this.draco.COLOR;
355
+ case "texcoord":
356
+ case "texcoords":
357
+ return this.draco.TEX_COORD;
358
+ default:
359
+ return this.draco.GENERIC;
395
360
  }
396
- const thisAttributeType = attribute.attribute_type;
397
- for (const dracoAttributeConstant in DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP) {
398
- const attributeType = this.draco[dracoAttributeConstant];
399
- if (attributeType === thisAttributeType) {
400
- return DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP[dracoAttributeConstant];
361
+ }
362
+ _getPositionAttribute(attributes) {
363
+ for (const attributeName in attributes) {
364
+ const attribute = attributes[attributeName];
365
+ const dracoType = this._getDracoAttributeType(attributeName);
366
+ if (dracoType === this.draco.POSITION) {
367
+ return attribute;
401
368
  }
402
369
  }
403
- const entryName = options.attributeNameEntry || "name";
404
- if (attribute.metadata[entryName]) {
405
- return attribute.metadata[entryName].string;
406
- }
407
- return `CUSTOM_ATTRIBUTE_${uniqueId}`;
408
- }
409
- // METADATA EXTRACTION
410
- /** Get top level metadata */
411
- _getTopLevelMetadata(dracoGeometry) {
412
- const dracoMetadata = this.decoder.GetMetadata(dracoGeometry);
413
- return this._getDracoMetadata(dracoMetadata);
414
- }
415
- /** Get per attribute metadata */
416
- _getAttributeMetadata(dracoGeometry, attributeId) {
417
- const dracoMetadata = this.decoder.GetAttributeMetadata(dracoGeometry, attributeId);
418
- return this._getDracoMetadata(dracoMetadata);
370
+ return null;
419
371
  }
420
372
  /**
421
- * Extract metadata field values
422
- * @param dracoMetadata
423
- * @returns
373
+ * Add metadata for the geometry.
374
+ * @param dracoGeometry - WASM Draco Object
375
+ * @param metadata
424
376
  */
425
- _getDracoMetadata(dracoMetadata) {
426
- if (!dracoMetadata || !dracoMetadata.ptr) {
427
- return {};
428
- }
429
- const result = {};
430
- const numEntries = this.metadataQuerier.NumEntries(dracoMetadata);
431
- for (let entryIndex = 0; entryIndex < numEntries; entryIndex++) {
432
- const entryName = this.metadataQuerier.GetEntryName(dracoMetadata, entryIndex);
433
- result[entryName] = this._getDracoMetadataField(dracoMetadata, entryName);
434
- }
435
- return result;
377
+ _addGeometryMetadata(dracoGeometry, metadata) {
378
+ const dracoMetadata = new this.draco.Metadata();
379
+ this._populateDracoMetadata(dracoMetadata, metadata);
380
+ this.dracoMeshBuilder.AddMetadata(dracoGeometry, dracoMetadata);
436
381
  }
437
382
  /**
438
- * Extracts possible values for one metadata entry by name
439
- * @param dracoMetadata
440
- * @param entryName
383
+ * Add metadata for an attribute to geometry.
384
+ * @param dracoGeometry - WASM Draco Object
385
+ * @param uniqueAttributeId
386
+ * @param metadata
441
387
  */
442
- _getDracoMetadataField(dracoMetadata, entryName) {
443
- const dracoArray = new this.draco.DracoInt32Array();
444
- try {
445
- this.metadataQuerier.GetIntEntryArray(dracoMetadata, entryName, dracoArray);
446
- const intArray = getInt32Array(dracoArray);
447
- return {
448
- int: this.metadataQuerier.GetIntEntry(dracoMetadata, entryName),
449
- string: this.metadataQuerier.GetStringEntry(dracoMetadata, entryName),
450
- double: this.metadataQuerier.GetDoubleEntry(dracoMetadata, entryName),
451
- intArray
452
- };
453
- } finally {
454
- this.draco.destroy(dracoArray);
455
- }
456
- }
457
- // QUANTIZED ATTRIBUTE SUPPORT (NO DECOMPRESSION)
458
- /** Skip transforms for specific attribute types */
459
- _disableAttributeTransforms(options) {
460
- const { quantizedAttributes = [], octahedronAttributes = [] } = options;
461
- const skipAttributes = [...quantizedAttributes, ...octahedronAttributes];
462
- for (const dracoAttributeName of skipAttributes) {
463
- this.decoder.SkipAttributeTransform(this.draco[dracoAttributeName]);
464
- }
388
+ _addAttributeMetadata(dracoGeometry, uniqueAttributeId, metadata) {
389
+ const dracoAttributeMetadata = new this.draco.Metadata();
390
+ this._populateDracoMetadata(dracoAttributeMetadata, metadata);
391
+ this.dracoMeshBuilder.SetMetadataForAttribute(dracoGeometry, uniqueAttributeId, dracoAttributeMetadata);
465
392
  }
466
393
  /**
467
- * Extract (and apply?) Position Transform
468
- * @todo not used
394
+ * Add contents of object or map to a WASM Draco Metadata Object
395
+ * @param dracoMetadata - WASM Draco Object
396
+ * @param metadata
469
397
  */
470
- _getQuantizationTransform(dracoAttribute, options) {
471
- const { quantizedAttributes = [] } = options;
472
- const attribute_type = dracoAttribute.attribute_type();
473
- const skip = quantizedAttributes.map((type) => this.decoder[type]).includes(attribute_type);
474
- if (skip) {
475
- const transform = new this.draco.AttributeQuantizationTransform();
476
- try {
477
- if (transform.InitFromAttribute(dracoAttribute)) {
478
- return {
479
- quantization_bits: transform.quantization_bits(),
480
- range: transform.range(),
481
- min_values: new Float32Array([1, 2, 3]).map((i) => transform.min_value(i))
482
- };
483
- }
484
- } finally {
485
- this.draco.destroy(transform);
486
- }
487
- }
488
- return null;
489
- }
490
- _getOctahedronTransform(dracoAttribute, options) {
491
- const { octahedronAttributes = [] } = options;
492
- const attribute_type = dracoAttribute.attribute_type();
493
- const octahedron = octahedronAttributes.map((type) => this.decoder[type]).includes(attribute_type);
494
- if (octahedron) {
495
- const transform = new this.draco.AttributeQuantizationTransform();
496
- try {
497
- if (transform.InitFromAttribute(dracoAttribute)) {
498
- return {
499
- quantization_bits: transform.quantization_bits()
500
- };
501
- }
502
- } finally {
503
- this.draco.destroy(transform);
398
+ _populateDracoMetadata(dracoMetadata, metadata) {
399
+ for (const [key, value] of getEntries(metadata)) {
400
+ switch (typeof value) {
401
+ case "number":
402
+ if (Math.trunc(value) === value) {
403
+ this.dracoMetadataBuilder.AddIntEntry(dracoMetadata, key, value);
404
+ } else {
405
+ this.dracoMetadataBuilder.AddDoubleEntry(dracoMetadata, key, value);
406
+ }
407
+ break;
408
+ case "object":
409
+ if (value instanceof Int32Array) {
410
+ this.dracoMetadataBuilder.AddIntEntryArray(dracoMetadata, key, value, value.length);
411
+ }
412
+ break;
413
+ case "string":
414
+ default:
415
+ this.dracoMetadataBuilder.AddStringEntry(dracoMetadata, key, value);
504
416
  }
505
417
  }
506
- return null;
507
418
  }
508
419
  };
509
- function getDracoDataType(draco, attributeType) {
510
- switch (attributeType) {
511
- case Float32Array:
512
- return draco.DT_FLOAT32;
513
- case Int8Array:
514
- return draco.DT_INT8;
515
- case Int16Array:
516
- return draco.DT_INT16;
517
- case Int32Array:
518
- return draco.DT_INT32;
519
- case Uint8Array:
520
- return draco.DT_UINT8;
521
- case Uint16Array:
522
- return draco.DT_UINT16;
523
- case Uint32Array:
524
- return draco.DT_UINT32;
525
- default:
526
- return draco.DT_INVALID;
527
- }
528
- }
529
- function getInt32Array(dracoArray) {
530
- const numValues = dracoArray.size();
531
- const intArray = new Int32Array(numValues);
532
- for (let i = 0; i < numValues; i++) {
533
- intArray[i] = dracoArray.GetValue(i);
420
+ function dracoInt8ArrayToArrayBuffer(dracoData) {
421
+ const byteLength = dracoData.size();
422
+ const outputBuffer = new ArrayBuffer(byteLength);
423
+ const outputData = new Int8Array(outputBuffer);
424
+ for (let i = 0; i < byteLength; ++i) {
425
+ outputData[i] = dracoData.GetValue(i);
534
426
  }
535
- return intArray;
427
+ return outputBuffer;
536
428
  }
537
- function getUint32Array(dracoArray) {
538
- const numValues = dracoArray.size();
539
- const intArray = new Int32Array(numValues);
540
- for (let i = 0; i < numValues; i++) {
541
- intArray[i] = dracoArray.GetValue(i);
542
- }
543
- return intArray;
429
+ function getEntries(container) {
430
+ const hasEntriesFunc = container.entries && !container.hasOwnProperty("entries");
431
+ return hasEntriesFunc ? container.entries() : Object.entries(container);
544
432
  }
545
433
 
546
- // dist/lib/draco-module-loader.js
547
- var import_worker_utils = require("@loaders.gl/worker-utils");
548
- var DRACO_DECODER_VERSION = "1.5.6";
549
- var DRACO_ENCODER_VERSION = "1.4.1";
550
- var STATIC_DECODER_URL = `https://www.gstatic.com/draco/versioned/decoders/${DRACO_DECODER_VERSION}`;
551
- var DRACO_EXTERNAL_LIBRARIES = {
552
- /** The primary Draco3D encoder, javascript wrapper part */
553
- DECODER: "draco_wasm_wrapper.js",
554
- /** The primary draco decoder, compiled web assembly part */
555
- DECODER_WASM: "draco_decoder.wasm",
556
- /** Fallback decoder for non-webassebly environments. Very big bundle, lower performance */
557
- FALLBACK_DECODER: "draco_decoder.js",
558
- /** Draco encoder */
559
- ENCODER: "draco_encoder.js"
434
+ // dist/lib/utils/version.js
435
+ var VERSION = true ? "4.4.0-alpha.0" : "latest";
436
+
437
+ // dist/draco-writer.js
438
+ var DEFAULT_DRACO_WRITER_OPTIONS = {
439
+ pointcloud: false,
440
+ // Set to true if pointcloud (mode: 0, no indices)
441
+ attributeNameEntry: "name"
442
+ // Draco Compression Parameters
443
+ // method: 'MESH_EDGEBREAKER_ENCODING', // Use draco defaults
444
+ // speed: [5, 5], // Use draco defaults
445
+ // quantization: { // Use draco defaults
446
+ // POSITION: 10
447
+ // }
560
448
  };
561
- var DRACO_EXTERNAL_LIBRARY_URLS = {
562
- [DRACO_EXTERNAL_LIBRARIES.DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER}`,
563
- [DRACO_EXTERNAL_LIBRARIES.DECODER_WASM]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER_WASM}`,
564
- [DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER}`,
565
- [DRACO_EXTERNAL_LIBRARIES.ENCODER]: `https://raw.githubusercontent.com/google/draco/${DRACO_ENCODER_VERSION}/javascript/${DRACO_EXTERNAL_LIBRARIES.ENCODER}`
449
+ var DracoWriterWorker = {
450
+ id: "draco-writer",
451
+ name: "Draco compressed geometry writer",
452
+ module: "draco",
453
+ version: VERSION,
454
+ worker: true,
455
+ options: {
456
+ draco: {},
457
+ source: null
458
+ }
566
459
  };
567
- var loadDecoderPromise;
568
- var loadEncoderPromise;
569
- async function loadDracoDecoderModule(options) {
570
- const modules = options.modules || {};
571
- if (modules.draco3d) {
572
- loadDecoderPromise ||= modules.draco3d.createDecoderModule({}).then((draco) => {
573
- return { draco };
574
- });
575
- } else {
576
- loadDecoderPromise ||= loadDracoDecoder(options);
460
+ var DracoWriter = {
461
+ name: "DRACO",
462
+ id: "draco",
463
+ module: "draco",
464
+ version: VERSION,
465
+ extensions: ["drc"],
466
+ mimeTypes: ["application/octet-stream"],
467
+ options: {
468
+ draco: DEFAULT_DRACO_WRITER_OPTIONS
469
+ },
470
+ encode
471
+ };
472
+ async function encode(data, options = {}) {
473
+ const { draco } = await loadDracoEncoderModule(options);
474
+ const dracoBuilder = new DracoBuilder(draco);
475
+ try {
476
+ return dracoBuilder.encodeSync(data, options.draco);
477
+ } finally {
478
+ dracoBuilder.destroy();
577
479
  }
578
- return await loadDecoderPromise;
579
480
  }
580
- async function loadDracoEncoderModule(options) {
581
- const modules = options.modules || {};
582
- if (modules.draco3d) {
583
- loadEncoderPromise ||= modules.draco3d.createEncoderModule({}).then((draco) => {
584
- return { draco };
585
- });
586
- } else {
587
- loadEncoderPromise ||= loadDracoEncoder(options);
481
+
482
+ // dist/lib/draco-parser.js
483
+ var import_schema_utils2 = require("@loaders.gl/schema-utils");
484
+
485
+ // dist/lib/utils/get-draco-schema.js
486
+ var import_schema_utils = require("@loaders.gl/schema-utils");
487
+ function getDracoSchema(attributes, loaderData, indices) {
488
+ const metadata = makeMetadata(loaderData.metadata);
489
+ const fields = [];
490
+ const namedLoaderDataAttributes = transformAttributesLoaderData(loaderData.attributes);
491
+ for (const attributeName in attributes) {
492
+ const attribute = attributes[attributeName];
493
+ const field = getArrowFieldFromAttribute(attributeName, attribute, namedLoaderDataAttributes[attributeName]);
494
+ fields.push(field);
588
495
  }
589
- return await loadEncoderPromise;
590
- }
591
- async function loadDracoDecoder(options) {
592
- let DracoDecoderModule;
593
- let wasmBinary;
594
- switch (options.draco && options.draco.decoderType) {
595
- case "js":
596
- DracoDecoderModule = await (0, import_worker_utils.loadLibrary)(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER);
597
- break;
598
- case "wasm":
599
- default:
600
- [DracoDecoderModule, wasmBinary] = await Promise.all([
601
- await (0, import_worker_utils.loadLibrary)(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.DECODER),
602
- await (0, import_worker_utils.loadLibrary)(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM], "draco", options, DRACO_EXTERNAL_LIBRARIES.DECODER_WASM)
603
- ]);
496
+ if (indices) {
497
+ const indicesField = getArrowFieldFromAttribute("indices", indices);
498
+ fields.push(indicesField);
604
499
  }
605
- DracoDecoderModule = DracoDecoderModule || globalThis.DracoDecoderModule;
606
- return await initializeDracoDecoder(DracoDecoderModule, wasmBinary);
500
+ return { fields, metadata };
607
501
  }
608
- function initializeDracoDecoder(DracoDecoderModule, wasmBinary) {
609
- const options = {};
610
- if (wasmBinary) {
611
- options.wasmBinary = wasmBinary;
502
+ function transformAttributesLoaderData(loaderData) {
503
+ const result = {};
504
+ for (const key in loaderData) {
505
+ const dracoAttribute = loaderData[key];
506
+ result[dracoAttribute.name || "undefined"] = dracoAttribute;
612
507
  }
613
- return new Promise((resolve) => {
614
- DracoDecoderModule({
615
- ...options,
616
- onModuleLoaded: (draco) => resolve({ draco })
617
- // Module is Promise-like. Wrap in object to avoid loop.
618
- });
619
- });
508
+ return result;
620
509
  }
621
- async function loadDracoEncoder(options) {
622
- let DracoEncoderModule = await (0, import_worker_utils.loadLibrary)(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.ENCODER);
623
- DracoEncoderModule = DracoEncoderModule || globalThis.DracoEncoderModule;
624
- return new Promise((resolve) => {
625
- DracoEncoderModule({
626
- onModuleLoaded: (draco) => resolve({ draco })
627
- // Module is Promise-like. Wrap in object to avoid loop.
628
- });
629
- });
510
+ function getArrowFieldFromAttribute(attributeName, attribute, loaderData) {
511
+ const metadataMap = loaderData ? makeMetadata(loaderData.metadata) : void 0;
512
+ const field = (0, import_schema_utils.deduceMeshField)(attributeName, attribute, metadataMap);
513
+ return field;
514
+ }
515
+ function makeMetadata(metadata) {
516
+ Object.entries(metadata);
517
+ const serializedMetadata = {};
518
+ for (const key in metadata) {
519
+ serializedMetadata[`${key}.string`] = JSON.stringify(metadata[key]);
520
+ }
521
+ return serializedMetadata;
630
522
  }
631
523
 
632
- // dist/lib/draco-builder.js
633
- var GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP = {
524
+ // dist/lib/draco-parser.js
525
+ var DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP = {
634
526
  POSITION: "POSITION",
635
527
  NORMAL: "NORMAL",
636
- COLOR_0: "COLOR",
637
- TEXCOORD_0: "TEX_COORD"
528
+ COLOR: "COLOR_0",
529
+ TEX_COORD: "TEXCOORD_0"
638
530
  };
639
- var noop = () => {
531
+ var DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP = {
532
+ 1: Int8Array,
533
+ 2: Uint8Array,
534
+ 3: Int16Array,
535
+ 4: Uint16Array,
536
+ 5: Int32Array,
537
+ 6: Uint32Array,
538
+ // 7: BigInt64Array,
539
+ // 8: BigUint64Array,
540
+ 9: Float32Array
541
+ // 10: Float64Array
542
+ // 11: BOOL - What array type do we use for this?
640
543
  };
641
- var DracoBuilder = class {
544
+ var INDEX_ITEM_SIZE = 4;
545
+ var DracoParser = class {
642
546
  draco;
643
- dracoEncoder;
644
- dracoMeshBuilder;
645
- dracoMetadataBuilder;
646
- log;
547
+ decoder;
548
+ metadataQuerier;
647
549
  // draco - the draco decoder, either import `draco3d` or load dynamically
648
550
  constructor(draco) {
649
551
  this.draco = draco;
650
- this.dracoEncoder = new this.draco.Encoder();
651
- this.dracoMeshBuilder = new this.draco.MeshBuilder();
652
- this.dracoMetadataBuilder = new this.draco.MetadataBuilder();
552
+ this.decoder = new this.draco.Decoder();
553
+ this.metadataQuerier = new this.draco.MetadataQuerier();
653
554
  }
555
+ /**
556
+ * Destroy draco resources
557
+ */
654
558
  destroy() {
655
- this.destroyEncodedObject(this.dracoMeshBuilder);
656
- this.destroyEncodedObject(this.dracoEncoder);
657
- this.destroyEncodedObject(this.dracoMetadataBuilder);
658
- this.dracoMeshBuilder = null;
659
- this.dracoEncoder = null;
660
- this.draco = null;
661
- }
662
- // TBD - when does this need to be called?
663
- destroyEncodedObject(object) {
664
- if (object) {
665
- this.draco.destroy(object);
666
- }
559
+ this.draco.destroy(this.decoder);
560
+ this.draco.destroy(this.metadataQuerier);
667
561
  }
668
562
  /**
669
- * Encode mesh or point cloud
670
- * @param mesh =({})
563
+ * NOTE: caller must call `destroyGeometry` on the return value after using it
564
+ * @param arrayBuffer
671
565
  * @param options
672
566
  */
673
- encodeSync(mesh, options = {}) {
674
- this.log = noop;
675
- this._setOptions(options);
676
- return options.pointcloud ? this._encodePointCloud(mesh, options) : this._encodeMesh(mesh, options);
677
- }
678
- // PRIVATE
679
- _getAttributesFromMesh(mesh) {
680
- const attributes = { ...mesh, ...mesh.attributes };
681
- if (mesh.indices) {
682
- attributes.indices = mesh.indices;
683
- }
684
- return attributes;
685
- }
686
- _encodePointCloud(pointcloud, options) {
687
- const dracoPointCloud = new this.draco.PointCloud();
688
- if (options.metadata) {
689
- this._addGeometryMetadata(dracoPointCloud, options.metadata);
690
- }
691
- const attributes = this._getAttributesFromMesh(pointcloud);
692
- this._createDracoPointCloud(dracoPointCloud, attributes, options);
693
- const dracoData = new this.draco.DracoInt8Array();
567
+ parseSync(arrayBuffer, options = {}) {
568
+ const buffer = new this.draco.DecoderBuffer();
569
+ buffer.Init(new Int8Array(arrayBuffer), arrayBuffer.byteLength);
570
+ this._disableAttributeTransforms(options);
571
+ const geometry_type = this.decoder.GetEncodedGeometryType(buffer);
572
+ const dracoGeometry = geometry_type === this.draco.TRIANGULAR_MESH ? new this.draco.Mesh() : new this.draco.PointCloud();
694
573
  try {
695
- const encodedLen = this.dracoEncoder.EncodePointCloudToDracoBuffer(dracoPointCloud, false, dracoData);
696
- if (!(encodedLen > 0)) {
697
- throw new Error("Draco encoding failed.");
574
+ let dracoStatus;
575
+ switch (geometry_type) {
576
+ case this.draco.TRIANGULAR_MESH:
577
+ dracoStatus = this.decoder.DecodeBufferToMesh(buffer, dracoGeometry);
578
+ break;
579
+ case this.draco.POINT_CLOUD:
580
+ dracoStatus = this.decoder.DecodeBufferToPointCloud(buffer, dracoGeometry);
581
+ break;
582
+ default:
583
+ throw new Error("DRACO: Unknown geometry type.");
698
584
  }
699
- this.log(`DRACO encoded ${dracoPointCloud.num_points()} points
700
- with ${dracoPointCloud.num_attributes()} attributes into ${encodedLen} bytes`);
701
- return dracoInt8ArrayToArrayBuffer(dracoData);
585
+ if (!dracoStatus.ok() || !dracoGeometry.ptr) {
586
+ const message = `DRACO decompression failed: ${dracoStatus.error_msg()}`;
587
+ throw new Error(message);
588
+ }
589
+ const loaderData = this._getDracoLoaderData(dracoGeometry, geometry_type, options);
590
+ const geometry = this._getMeshData(dracoGeometry, loaderData, options);
591
+ const boundingBox = (0, import_schema_utils2.getMeshBoundingBox)(geometry.attributes);
592
+ const schema = getDracoSchema(geometry.attributes, loaderData, geometry.indices);
593
+ const data = {
594
+ loader: "draco",
595
+ loaderData,
596
+ header: {
597
+ vertexCount: dracoGeometry.num_points(),
598
+ boundingBox
599
+ },
600
+ ...geometry,
601
+ schema
602
+ };
603
+ return data;
702
604
  } finally {
703
- this.destroyEncodedObject(dracoData);
704
- this.destroyEncodedObject(dracoPointCloud);
605
+ this.draco.destroy(buffer);
606
+ if (dracoGeometry) {
607
+ this.draco.destroy(dracoGeometry);
608
+ }
705
609
  }
706
610
  }
707
- _encodeMesh(mesh, options) {
708
- const dracoMesh = new this.draco.Mesh();
709
- if (options.metadata) {
710
- this._addGeometryMetadata(dracoMesh, options.metadata);
711
- }
712
- const attributes = this._getAttributesFromMesh(mesh);
713
- this._createDracoMesh(dracoMesh, attributes, options);
714
- const dracoData = new this.draco.DracoInt8Array();
715
- try {
716
- const encodedLen = this.dracoEncoder.EncodeMeshToDracoBuffer(dracoMesh, dracoData);
717
- if (encodedLen <= 0) {
718
- throw new Error("Draco encoding failed.");
611
+ // Draco specific "loader data"
612
+ /**
613
+ * Extract
614
+ * @param dracoGeometry
615
+ * @param geometry_type
616
+ * @param options
617
+ * @returns
618
+ */
619
+ _getDracoLoaderData(dracoGeometry, geometry_type, options) {
620
+ const metadata = this._getTopLevelMetadata(dracoGeometry);
621
+ const attributes = this._getDracoAttributes(dracoGeometry, options);
622
+ return {
623
+ geometry_type,
624
+ num_attributes: dracoGeometry.num_attributes(),
625
+ num_points: dracoGeometry.num_points(),
626
+ num_faces: dracoGeometry instanceof this.draco.Mesh ? dracoGeometry.num_faces() : 0,
627
+ metadata,
628
+ attributes
629
+ };
630
+ }
631
+ /**
632
+ * Extract all draco provided information and metadata for each attribute
633
+ * @param dracoGeometry
634
+ * @param options
635
+ * @returns
636
+ */
637
+ _getDracoAttributes(dracoGeometry, options) {
638
+ const dracoAttributes = {};
639
+ for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) {
640
+ const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId);
641
+ const metadata = this._getAttributeMetadata(dracoGeometry, attributeId);
642
+ dracoAttributes[dracoAttribute.unique_id()] = {
643
+ unique_id: dracoAttribute.unique_id(),
644
+ attribute_type: dracoAttribute.attribute_type(),
645
+ data_type: dracoAttribute.data_type(),
646
+ num_components: dracoAttribute.num_components(),
647
+ byte_offset: dracoAttribute.byte_offset(),
648
+ byte_stride: dracoAttribute.byte_stride(),
649
+ normalized: dracoAttribute.normalized(),
650
+ attribute_index: attributeId,
651
+ metadata
652
+ };
653
+ const quantization = this._getQuantizationTransform(dracoAttribute, options);
654
+ if (quantization) {
655
+ dracoAttributes[dracoAttribute.unique_id()].quantization_transform = quantization;
656
+ }
657
+ const octahedron = this._getOctahedronTransform(dracoAttribute, options);
658
+ if (octahedron) {
659
+ dracoAttributes[dracoAttribute.unique_id()].octahedron_transform = octahedron;
719
660
  }
720
- this.log(`DRACO encoded ${dracoMesh.num_points()} points
721
- with ${dracoMesh.num_attributes()} attributes into ${encodedLen} bytes`);
722
- return dracoInt8ArrayToArrayBuffer(dracoData);
723
- } finally {
724
- this.destroyEncodedObject(dracoData);
725
- this.destroyEncodedObject(dracoMesh);
726
661
  }
662
+ return dracoAttributes;
727
663
  }
728
664
  /**
729
- * Set encoding options.
730
- * @param {{speed?: any; method?: any; quantization?: any;}} options
665
+ * Get standard loaders.gl mesh category data
666
+ * Extracts the geometry from draco
667
+ * @param dracoGeometry
668
+ * @param options
731
669
  */
732
- _setOptions(options) {
733
- if ("speed" in options) {
734
- this.dracoEncoder.SetSpeedOptions(...options.speed);
670
+ _getMeshData(dracoGeometry, loaderData, options) {
671
+ const attributes = this._getMeshAttributes(loaderData, dracoGeometry, options);
672
+ const positionAttribute = attributes.POSITION;
673
+ if (!positionAttribute) {
674
+ throw new Error("DRACO: No position attribute found.");
735
675
  }
736
- if ("method" in options) {
737
- const dracoMethod = this.draco[options.method || "MESH_SEQUENTIAL_ENCODING"];
738
- this.dracoEncoder.SetEncodingMethod(dracoMethod);
676
+ if (dracoGeometry instanceof this.draco.Mesh) {
677
+ switch (options.topology) {
678
+ case "triangle-strip":
679
+ return {
680
+ topology: "triangle-strip",
681
+ // TODO - mode is wrong?
682
+ mode: 4,
683
+ // GL.TRIANGLES
684
+ attributes,
685
+ indices: {
686
+ value: this._getTriangleStripIndices(dracoGeometry),
687
+ size: 1
688
+ }
689
+ };
690
+ case "triangle-list":
691
+ default:
692
+ return {
693
+ topology: "triangle-list",
694
+ // TODO - mode is wrong?
695
+ mode: 5,
696
+ // GL.TRIANGLE_STRIP
697
+ attributes,
698
+ indices: {
699
+ value: this._getTriangleListIndices(dracoGeometry),
700
+ size: 1
701
+ }
702
+ };
703
+ }
739
704
  }
740
- if ("quantization" in options) {
741
- for (const attribute in options.quantization) {
742
- const bits = options.quantization[attribute];
743
- const dracoPosition = this.draco[attribute];
744
- this.dracoEncoder.SetAttributeQuantization(dracoPosition, bits);
705
+ return {
706
+ topology: "point-list",
707
+ mode: 0,
708
+ // GL.POINTS
709
+ attributes
710
+ };
711
+ }
712
+ _getMeshAttributes(loaderData, dracoGeometry, options) {
713
+ const attributes = {};
714
+ for (const loaderAttribute of Object.values(loaderData.attributes)) {
715
+ const attributeName = this._deduceAttributeName(loaderAttribute, options);
716
+ loaderAttribute.name = attributeName;
717
+ const values = this._getAttributeValues(dracoGeometry, loaderAttribute);
718
+ if (values) {
719
+ const { value, size } = values;
720
+ attributes[attributeName] = {
721
+ value,
722
+ size,
723
+ byteOffset: loaderAttribute.byte_offset,
724
+ byteStride: loaderAttribute.byte_stride,
725
+ normalized: loaderAttribute.normalized
726
+ };
745
727
  }
746
728
  }
729
+ return attributes;
747
730
  }
731
+ // MESH INDICES EXTRACTION
748
732
  /**
749
- * @param {Mesh} dracoMesh
750
- * @param {object} attributes
751
- * @returns {Mesh}
733
+ * For meshes, we need indices to define the faces.
734
+ * @param dracoGeometry
752
735
  */
753
- _createDracoMesh(dracoMesh, attributes, options) {
754
- const optionalMetadata = options.attributesMetadata || {};
736
+ _getTriangleListIndices(dracoGeometry) {
737
+ const numFaces = dracoGeometry.num_faces();
738
+ const numIndices = numFaces * 3;
739
+ const byteLength = numIndices * INDEX_ITEM_SIZE;
740
+ const ptr = this.draco._malloc(byteLength);
755
741
  try {
756
- const positions = this._getPositionAttribute(attributes);
757
- if (!positions) {
758
- throw new Error("positions");
759
- }
760
- const vertexCount = positions.length / 3;
761
- for (let attributeName in attributes) {
762
- const attribute = attributes[attributeName];
763
- attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName;
764
- const uniqueId = this._addAttributeToMesh(dracoMesh, attributeName, attribute, vertexCount);
765
- if (uniqueId !== -1) {
766
- this._addAttributeMetadata(dracoMesh, uniqueId, {
767
- name: attributeName,
768
- ...optionalMetadata[attributeName] || {}
769
- });
770
- }
771
- }
772
- } catch (error) {
773
- this.destroyEncodedObject(dracoMesh);
774
- throw error;
742
+ this.decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr);
743
+ return new Uint32Array(this.draco.HEAPF32.buffer, ptr, numIndices).slice();
744
+ } finally {
745
+ this.draco._free(ptr);
775
746
  }
776
- return dracoMesh;
777
747
  }
778
748
  /**
779
- * @param {} dracoPointCloud
780
- * @param {object} attributes
749
+ * For meshes, we need indices to define the faces.
750
+ * @param dracoGeometry
781
751
  */
782
- _createDracoPointCloud(dracoPointCloud, attributes, options) {
783
- const optionalMetadata = options.attributesMetadata || {};
752
+ _getTriangleStripIndices(dracoGeometry) {
753
+ const dracoArray = new this.draco.DracoInt32Array();
784
754
  try {
785
- const positions = this._getPositionAttribute(attributes);
786
- if (!positions) {
787
- throw new Error("positions");
788
- }
789
- const vertexCount = positions.length / 3;
790
- for (let attributeName in attributes) {
791
- const attribute = attributes[attributeName];
792
- attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName;
793
- const uniqueId = this._addAttributeToMesh(dracoPointCloud, attributeName, attribute, vertexCount);
794
- if (uniqueId !== -1) {
795
- this._addAttributeMetadata(dracoPointCloud, uniqueId, {
796
- name: attributeName,
797
- ...optionalMetadata[attributeName] || {}
798
- });
799
- }
800
- }
801
- } catch (error) {
802
- this.destroyEncodedObject(dracoPointCloud);
803
- throw error;
755
+ this.decoder.GetTriangleStripsFromMesh(dracoGeometry, dracoArray);
756
+ return getUint32Array(dracoArray);
757
+ } finally {
758
+ this.draco.destroy(dracoArray);
804
759
  }
805
- return dracoPointCloud;
806
760
  }
807
761
  /**
808
- * @param mesh
762
+ *
763
+ * @param dracoGeometry
764
+ * @param dracoAttribute
809
765
  * @param attributeName
810
- * @param attribute
811
- * @param vertexCount
812
766
  */
813
- _addAttributeToMesh(mesh, attributeName, attribute, vertexCount) {
814
- if (!ArrayBuffer.isView(attribute)) {
815
- return -1;
816
- }
817
- const type = this._getDracoAttributeType(attributeName);
818
- const size = attribute.length / vertexCount;
819
- if (type === "indices") {
820
- const numFaces = attribute.length / 3;
821
- this.log(`Adding attribute ${attributeName}, size ${numFaces}`);
822
- this.dracoMeshBuilder.AddFacesToMesh(mesh, numFaces, attribute);
823
- return -1;
767
+ _getAttributeValues(dracoGeometry, attribute) {
768
+ const TypedArrayCtor = DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP[attribute.data_type];
769
+ if (!TypedArrayCtor) {
770
+ console.warn(`DRACO: Unsupported attribute type ${attribute.data_type}`);
771
+ return null;
824
772
  }
825
- this.log(`Adding attribute ${attributeName}, size ${size}`);
826
- const builder = this.dracoMeshBuilder;
827
- const { buffer } = attribute;
828
- switch (attribute.constructor) {
829
- case Int8Array:
830
- return builder.AddInt8Attribute(mesh, type, vertexCount, size, new Int8Array(buffer));
831
- case Int16Array:
832
- return builder.AddInt16Attribute(mesh, type, vertexCount, size, new Int16Array(buffer));
833
- case Int32Array:
834
- return builder.AddInt32Attribute(mesh, type, vertexCount, size, new Int32Array(buffer));
835
- case Uint8Array:
836
- case Uint8ClampedArray:
837
- return builder.AddUInt8Attribute(mesh, type, vertexCount, size, new Uint8Array(buffer));
838
- case Uint16Array:
839
- return builder.AddUInt16Attribute(mesh, type, vertexCount, size, new Uint16Array(buffer));
840
- case Uint32Array:
841
- return builder.AddUInt32Attribute(mesh, type, vertexCount, size, new Uint32Array(buffer));
842
- case Float32Array:
843
- return builder.AddFloatAttribute(mesh, type, vertexCount, size, new Float32Array(buffer));
844
- default:
845
- console.warn("Unsupported attribute type", attribute);
846
- return -1;
773
+ const numComponents = attribute.num_components;
774
+ const numPoints = dracoGeometry.num_points();
775
+ const numValues = numPoints * numComponents;
776
+ const byteLength = numValues * TypedArrayCtor.BYTES_PER_ELEMENT;
777
+ const dataType = getDracoDataType(this.draco, TypedArrayCtor);
778
+ let value;
779
+ const ptr = this.draco._malloc(byteLength);
780
+ try {
781
+ const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attribute.attribute_index);
782
+ this.decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, dracoAttribute, dataType, byteLength, ptr);
783
+ value = new TypedArrayCtor(this.draco.HEAPF32.buffer, ptr, numValues).slice();
784
+ } finally {
785
+ this.draco._free(ptr);
847
786
  }
787
+ return { value, size: numComponents };
848
788
  }
789
+ // Attribute names
849
790
  /**
850
- * DRACO can compress attributes of know type better
851
- * TODO - expose an attribute type map?
852
- * @param attributeName
791
+ * DRACO does not store attribute names - We need to deduce an attribute name
792
+ * for each attribute
793
+ _getAttributeNames(
794
+ dracoGeometry: Mesh | PointCloud,
795
+ options: DracoParseOptions
796
+ ): {[unique_id: number]: string} {
797
+ const attributeNames: {[unique_id: number]: string} = {};
798
+ for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) {
799
+ const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId);
800
+ const attributeName = this._deduceAttributeName(dracoAttribute, options);
801
+ attributeNames[attributeName] = attributeName;
802
+ }
803
+ return attributeNames;
804
+ }
853
805
  */
854
- _getDracoAttributeType(attributeName) {
855
- switch (attributeName.toLowerCase()) {
856
- case "indices":
857
- return "indices";
858
- case "position":
859
- case "positions":
860
- case "vertices":
861
- return this.draco.POSITION;
862
- case "normal":
863
- case "normals":
864
- return this.draco.NORMAL;
865
- case "color":
866
- case "colors":
867
- return this.draco.COLOR;
868
- case "texcoord":
869
- case "texcoords":
870
- return this.draco.TEX_COORD;
871
- default:
872
- return this.draco.GENERIC;
806
+ /**
807
+ * Deduce an attribute name.
808
+ * @note DRACO does not save attribute names, just general type (POSITION, COLOR)
809
+ * to help optimize compression. We generate GLTF compatible names for the Draco-recognized
810
+ * types
811
+ * @param attributeData
812
+ */
813
+ _deduceAttributeName(attribute, options) {
814
+ const uniqueId = attribute.unique_id;
815
+ for (const [attributeName, attributeUniqueId] of Object.entries(options.extraAttributes || {})) {
816
+ if (attributeUniqueId === uniqueId) {
817
+ return attributeName;
818
+ }
873
819
  }
874
- }
875
- _getPositionAttribute(attributes) {
876
- for (const attributeName in attributes) {
877
- const attribute = attributes[attributeName];
878
- const dracoType = this._getDracoAttributeType(attributeName);
879
- if (dracoType === this.draco.POSITION) {
880
- return attribute;
820
+ const thisAttributeType = attribute.attribute_type;
821
+ for (const dracoAttributeConstant in DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP) {
822
+ const attributeType = this.draco[dracoAttributeConstant];
823
+ if (attributeType === thisAttributeType) {
824
+ return DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP[dracoAttributeConstant];
881
825
  }
882
826
  }
883
- return null;
827
+ const entryName = options.attributeNameEntry || "name";
828
+ if (attribute.metadata[entryName]) {
829
+ return attribute.metadata[entryName].string;
830
+ }
831
+ return `CUSTOM_ATTRIBUTE_${uniqueId}`;
832
+ }
833
+ // METADATA EXTRACTION
834
+ /** Get top level metadata */
835
+ _getTopLevelMetadata(dracoGeometry) {
836
+ const dracoMetadata = this.decoder.GetMetadata(dracoGeometry);
837
+ return this._getDracoMetadata(dracoMetadata);
838
+ }
839
+ /** Get per attribute metadata */
840
+ _getAttributeMetadata(dracoGeometry, attributeId) {
841
+ const dracoMetadata = this.decoder.GetAttributeMetadata(dracoGeometry, attributeId);
842
+ return this._getDracoMetadata(dracoMetadata);
884
843
  }
885
844
  /**
886
- * Add metadata for the geometry.
887
- * @param dracoGeometry - WASM Draco Object
888
- * @param metadata
845
+ * Extract metadata field values
846
+ * @param dracoMetadata
847
+ * @returns
889
848
  */
890
- _addGeometryMetadata(dracoGeometry, metadata) {
891
- const dracoMetadata = new this.draco.Metadata();
892
- this._populateDracoMetadata(dracoMetadata, metadata);
893
- this.dracoMeshBuilder.AddMetadata(dracoGeometry, dracoMetadata);
849
+ _getDracoMetadata(dracoMetadata) {
850
+ if (!dracoMetadata || !dracoMetadata.ptr) {
851
+ return {};
852
+ }
853
+ const result = {};
854
+ const numEntries = this.metadataQuerier.NumEntries(dracoMetadata);
855
+ for (let entryIndex = 0; entryIndex < numEntries; entryIndex++) {
856
+ const entryName = this.metadataQuerier.GetEntryName(dracoMetadata, entryIndex);
857
+ result[entryName] = this._getDracoMetadataField(dracoMetadata, entryName);
858
+ }
859
+ return result;
894
860
  }
895
861
  /**
896
- * Add metadata for an attribute to geometry.
897
- * @param dracoGeometry - WASM Draco Object
898
- * @param uniqueAttributeId
899
- * @param metadata
862
+ * Extracts possible values for one metadata entry by name
863
+ * @param dracoMetadata
864
+ * @param entryName
900
865
  */
901
- _addAttributeMetadata(dracoGeometry, uniqueAttributeId, metadata) {
902
- const dracoAttributeMetadata = new this.draco.Metadata();
903
- this._populateDracoMetadata(dracoAttributeMetadata, metadata);
904
- this.dracoMeshBuilder.SetMetadataForAttribute(dracoGeometry, uniqueAttributeId, dracoAttributeMetadata);
866
+ _getDracoMetadataField(dracoMetadata, entryName) {
867
+ const dracoArray = new this.draco.DracoInt32Array();
868
+ try {
869
+ this.metadataQuerier.GetIntEntryArray(dracoMetadata, entryName, dracoArray);
870
+ const intArray = getInt32Array(dracoArray);
871
+ return {
872
+ int: this.metadataQuerier.GetIntEntry(dracoMetadata, entryName),
873
+ string: this.metadataQuerier.GetStringEntry(dracoMetadata, entryName),
874
+ double: this.metadataQuerier.GetDoubleEntry(dracoMetadata, entryName),
875
+ intArray
876
+ };
877
+ } finally {
878
+ this.draco.destroy(dracoArray);
879
+ }
880
+ }
881
+ // QUANTIZED ATTRIBUTE SUPPORT (NO DECOMPRESSION)
882
+ /** Skip transforms for specific attribute types */
883
+ _disableAttributeTransforms(options) {
884
+ const { quantizedAttributes = [], octahedronAttributes = [] } = options;
885
+ const skipAttributes = [...quantizedAttributes, ...octahedronAttributes];
886
+ for (const dracoAttributeName of skipAttributes) {
887
+ this.decoder.SkipAttributeTransform(this.draco[dracoAttributeName]);
888
+ }
905
889
  }
906
890
  /**
907
- * Add contents of object or map to a WASM Draco Metadata Object
908
- * @param dracoMetadata - WASM Draco Object
909
- * @param metadata
891
+ * Extract (and apply?) Position Transform
892
+ * @todo not used
910
893
  */
911
- _populateDracoMetadata(dracoMetadata, metadata) {
912
- for (const [key, value] of getEntries(metadata)) {
913
- switch (typeof value) {
914
- case "number":
915
- if (Math.trunc(value) === value) {
916
- this.dracoMetadataBuilder.AddIntEntry(dracoMetadata, key, value);
917
- } else {
918
- this.dracoMetadataBuilder.AddDoubleEntry(dracoMetadata, key, value);
919
- }
920
- break;
921
- case "object":
922
- if (value instanceof Int32Array) {
923
- this.dracoMetadataBuilder.AddIntEntryArray(dracoMetadata, key, value, value.length);
924
- }
925
- break;
926
- case "string":
927
- default:
928
- this.dracoMetadataBuilder.AddStringEntry(dracoMetadata, key, value);
894
+ _getQuantizationTransform(dracoAttribute, options) {
895
+ const { quantizedAttributes = [] } = options;
896
+ const attribute_type = dracoAttribute.attribute_type();
897
+ const skip = quantizedAttributes.map((type) => this.decoder[type]).includes(attribute_type);
898
+ if (skip) {
899
+ const transform = new this.draco.AttributeQuantizationTransform();
900
+ try {
901
+ if (transform.InitFromAttribute(dracoAttribute)) {
902
+ return {
903
+ quantization_bits: transform.quantization_bits(),
904
+ range: transform.range(),
905
+ min_values: new Float32Array([1, 2, 3]).map((i) => transform.min_value(i))
906
+ };
907
+ }
908
+ } finally {
909
+ this.draco.destroy(transform);
910
+ }
911
+ }
912
+ return null;
913
+ }
914
+ _getOctahedronTransform(dracoAttribute, options) {
915
+ const { octahedronAttributes = [] } = options;
916
+ const attribute_type = dracoAttribute.attribute_type();
917
+ const octahedron = octahedronAttributes.map((type) => this.decoder[type]).includes(attribute_type);
918
+ if (octahedron) {
919
+ const transform = new this.draco.AttributeQuantizationTransform();
920
+ try {
921
+ if (transform.InitFromAttribute(dracoAttribute)) {
922
+ return {
923
+ quantization_bits: transform.quantization_bits()
924
+ };
925
+ }
926
+ } finally {
927
+ this.draco.destroy(transform);
929
928
  }
930
929
  }
930
+ return null;
931
931
  }
932
932
  };
933
- function dracoInt8ArrayToArrayBuffer(dracoData) {
934
- const byteLength = dracoData.size();
935
- const outputBuffer = new ArrayBuffer(byteLength);
936
- const outputData = new Int8Array(outputBuffer);
937
- for (let i = 0; i < byteLength; ++i) {
938
- outputData[i] = dracoData.GetValue(i);
933
+ function getDracoDataType(draco, attributeType) {
934
+ switch (attributeType) {
935
+ case Float32Array:
936
+ return draco.DT_FLOAT32;
937
+ case Int8Array:
938
+ return draco.DT_INT8;
939
+ case Int16Array:
940
+ return draco.DT_INT16;
941
+ case Int32Array:
942
+ return draco.DT_INT32;
943
+ case Uint8Array:
944
+ return draco.DT_UINT8;
945
+ case Uint16Array:
946
+ return draco.DT_UINT16;
947
+ case Uint32Array:
948
+ return draco.DT_UINT32;
949
+ default:
950
+ return draco.DT_INVALID;
939
951
  }
940
- return outputBuffer;
941
952
  }
942
- function getEntries(container) {
943
- const hasEntriesFunc = container.entries && !container.hasOwnProperty("entries");
944
- return hasEntriesFunc ? container.entries() : Object.entries(container);
953
+ function getInt32Array(dracoArray) {
954
+ const numValues = dracoArray.size();
955
+ const intArray = new Int32Array(numValues);
956
+ for (let i = 0; i < numValues; i++) {
957
+ intArray[i] = dracoArray.GetValue(i);
958
+ }
959
+ return intArray;
945
960
  }
946
-
947
- // dist/draco-writer.js
948
- var DEFAULT_DRACO_WRITER_OPTIONS = {
949
- pointcloud: false,
950
- // Set to true if pointcloud (mode: 0, no indices)
951
- attributeNameEntry: "name"
952
- // Draco Compression Parameters
953
- // method: 'MESH_EDGEBREAKER_ENCODING', // Use draco defaults
954
- // speed: [5, 5], // Use draco defaults
955
- // quantization: { // Use draco defaults
956
- // POSITION: 10
957
- // }
958
- };
959
- var DracoWriter = {
960
- name: "DRACO",
961
- id: "draco",
962
- module: "draco",
963
- version: VERSION,
964
- extensions: ["drc"],
965
- options: {
966
- draco: DEFAULT_DRACO_WRITER_OPTIONS
967
- },
968
- encode
969
- };
970
- async function encode(data, options = {}) {
971
- const { draco } = await loadDracoEncoderModule(options);
972
- const dracoBuilder = new DracoBuilder(draco);
973
- try {
974
- return dracoBuilder.encodeSync(data, options.draco);
975
- } finally {
976
- dracoBuilder.destroy();
961
+ function getUint32Array(dracoArray) {
962
+ const numValues = dracoArray.size();
963
+ const intArray = new Int32Array(numValues);
964
+ for (let i = 0; i < numValues; i++) {
965
+ intArray[i] = dracoArray.GetValue(i);
977
966
  }
967
+ return intArray;
978
968
  }
979
969
 
980
- // dist/index.js
981
- var DracoWriterWorker = {
982
- id: "draco-writer",
983
- name: "Draco compressed geometry writer",
970
+ // dist/draco-loader.js
971
+ var DracoWorkerLoader = {
972
+ dataType: null,
973
+ batchType: null,
974
+ name: "Draco",
975
+ id: "draco",
984
976
  module: "draco",
977
+ // shapes: ['mesh'],
985
978
  version: VERSION,
986
979
  worker: true,
980
+ extensions: ["drc"],
981
+ mimeTypes: ["application/octet-stream"],
982
+ binary: true,
983
+ tests: ["DRACO"],
987
984
  options: {
988
- draco: {},
989
- source: null
985
+ draco: {
986
+ decoderType: typeof WebAssembly === "object" ? "wasm" : "js",
987
+ // 'js' for IE11
988
+ libraryPath: "libs/",
989
+ extraAttributes: {},
990
+ attributeNameEntry: void 0
991
+ }
990
992
  }
991
993
  };
992
- var DracoLoader2 = {
993
- ...DracoLoader,
994
+ var DracoLoader = {
995
+ ...DracoWorkerLoader,
994
996
  parse
995
997
  };
996
998
  async function parse(arrayBuffer, options) {
@@ -1002,4 +1004,18 @@ async function parse(arrayBuffer, options) {
1002
1004
  dracoParser.destroy();
1003
1005
  }
1004
1006
  }
1007
+
1008
+ // dist/draco-arrow-loader.js
1009
+ var import_schema_utils3 = require("@loaders.gl/schema-utils");
1010
+ var DracoArrowLoader = {
1011
+ ...DracoLoader,
1012
+ dataType: null,
1013
+ worker: false,
1014
+ parse: parse2
1015
+ };
1016
+ async function parse2(arrayBuffer, options) {
1017
+ const mesh = await DracoLoader.parse(arrayBuffer, options);
1018
+ const arrowTable = (0, import_schema_utils3.convertMeshToTable)(mesh, "arrow-table");
1019
+ return arrowTable;
1020
+ }
1005
1021
  //# sourceMappingURL=index.cjs.map