@loaders.gl/draco 4.3.4 → 4.4.0-alpha.10

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