UNPKG

@softrobot/loaders.gl-draco

Version:

Framework-independent loader and writer for Draco compressed meshes and point clouds

377 lines (314 loc) 12.6 kB
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import _typeof from "@babel/runtime/helpers/esm/typeof"; import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck"; import _createClass from "@babel/runtime/helpers/esm/createClass"; import { getMeshBoundingBox } from '@loaders.gl/loader-utils'; var GEOMETRY_TYPE = { TRIANGULAR_MESH: 0, POINT_CLOUD: 1 }; var DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP = { POSITION: 'POSITION', NORMAL: 'NORMAL', COLOR: 'COLOR_0', TEX_COORD: 'TEXCOORD_0' }; var DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP = { 1: Int8Array, 2: Uint8Array, 3: Int16Array, 4: Uint16Array, 5: Int32Array, 6: Uint32Array, 9: Float32Array }; var INDEX_ITEM_SIZE = 4; var DracoParser = function () { function DracoParser(draco) { _classCallCheck(this, DracoParser); this.draco = draco; this.drawMode = 'TRIANGLE'; this.metadataQuerier = {}; } _createClass(DracoParser, [{ key: "destroy", value: function destroy() {} }, { key: "parseSync", value: function parseSync(arrayBuffer) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; this.metadataQuerier = new this.draco.MetadataQuerier(); var buffer = new this.draco.DecoderBuffer(); buffer.Init(new Int8Array(arrayBuffer), arrayBuffer.byteLength); var decoder = new this.draco.Decoder(); var data = {}; var dracoStatus; var dracoGeometry; var header; try { var geometryType = decoder.GetEncodedGeometryType(buffer); switch (geometryType) { case this.draco.TRIANGULAR_MESH: dracoGeometry = new this.draco.Mesh(); dracoStatus = decoder.DecodeBufferToMesh(buffer, dracoGeometry); header = { type: GEOMETRY_TYPE.TRIANGULAR_MESH, faceCount: dracoGeometry.num_faces(), attributeCount: dracoGeometry.num_attributes(), vertexCount: dracoGeometry.num_points() }; break; case this.draco.POINT_CLOUD: dracoGeometry = new this.draco.PointCloud(); dracoStatus = decoder.DecodeBufferToPointCloud(buffer, dracoGeometry); header = { type: GEOMETRY_TYPE.POINT_CLOUD, attributeCount: dracoGeometry.num_attributes(), vertexCount: dracoGeometry.num_points() }; break; default: throw new Error('Unknown DRACO geometry type.'); } if (!dracoStatus.ok() || !dracoGeometry.ptr) { var message = "DRACO decompression failed: ".concat(dracoStatus.error_msg()); if (dracoGeometry) { this.draco.destroy(dracoGeometry); } throw new Error(message); } data.loaderData = { header: header }; this._extractDRACOGeometry(decoder, dracoGeometry, geometryType, data, options); var metadata = this._getGeometryMetadata(decoder, dracoGeometry); data.header = { vertexCount: header.vertexCount, boundingBox: getMeshBoundingBox(data.attributes), metadata: metadata }; } finally { this.draco.destroy(decoder); this.draco.destroy(buffer); this.draco.destroy(dracoGeometry); this.draco.destroy(this.metadataQuerier); } return data; } }, { key: "_extractDRACOGeometry", value: function _extractDRACOGeometry(decoder, dracoGeometry, geometryType, geometry, options) { var attributes = this._getAttributes(decoder, dracoGeometry, options); var positionAttribute = attributes.POSITION; if (!positionAttribute) { throw new Error('DRACO decompressor: No position attribute found.'); } if (geometryType === this.draco.TRIANGULAR_MESH) { attributes.indices = this.drawMode === 'TRIANGLE_STRIP' ? this._getMeshStripIndices(decoder, dracoGeometry) : this._getMeshFaceIndices(decoder, dracoGeometry); geometry.mode = this.drawMode === 'TRIANGLE_STRIP' ? 5 : 4; } else { geometry.mode = 0; } if (attributes.indices) { geometry.indices = { value: attributes.indices, size: 1 }; delete attributes.indices; } geometry.attributes = attributes; return geometry; } }, { key: "getPositionAttributeMetadata", value: function getPositionAttributeMetadata(positionAttribute) { this.metadata = this.metadata || {}; this.metadata.attributes = this.metadata.attributes || {}; var posTransform = new this.draco.AttributeQuantizationTransform(); if (posTransform.InitFromAttribute(positionAttribute)) { this.metadata.attributes.position.isQuantized = true; this.metadata.attributes.position.maxRange = posTransform.range(); this.metadata.attributes.position.numQuantizationBits = posTransform.quantization_bits(); this.metadata.attributes.position.minValues = new Float32Array(3); for (var i = 0; i < 3; ++i) { this.metadata.attributes.position.minValues[i] = posTransform.min_value(i); } } this.draco.destroy(posTransform); } }, { key: "_getAttributes", value: function _getAttributes(decoder, dracoGeometry, options) { var attributes = {}; var numPoints = dracoGeometry.num_points(); for (var attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) { var dracoAttribute = decoder.GetAttribute(dracoGeometry, attributeId); var attributeMetadata = this._getAttributeMetadata(decoder, dracoGeometry, attributeId); var attributeData = { uniqueId: dracoAttribute.unique_id(), attributeType: dracoAttribute.attribute_type(), dataType: DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP[dracoAttribute.data_type()], size: dracoAttribute.size(), numComponents: dracoAttribute.num_components(), byteOffset: dracoAttribute.byte_offset(), byteStride: dracoAttribute.byte_stride(), normalized: dracoAttribute.normalized(), metadata: attributeMetadata }; var attributeName = this._deduceAttributeName(attributeData, options); var _this$_getAttributeTy = this._getAttributeTypedArray(decoder, dracoGeometry, dracoAttribute, attributeName), typedArray = _this$_getAttributeTy.typedArray; attributes[attributeName] = { value: typedArray, size: typedArray.length / numPoints, metadata: attributeMetadata }; } return attributes; } }, { key: "_getMeshFaceIndices", value: function _getMeshFaceIndices(decoder, dracoGeometry) { var numFaces = dracoGeometry.num_faces(); var numIndices = numFaces * 3; var byteLength = numIndices * INDEX_ITEM_SIZE; var ptr = this.draco._malloc(byteLength); decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr); var indices = new Uint32Array(this.draco.HEAPF32.buffer, ptr, numIndices).slice(); this.draco._free(ptr); return indices; } }, { key: "_getMeshStripIndices", value: function _getMeshStripIndices(decoder, dracoGeometry) { var dracoArray = new this.draco.DracoInt32Array(); decoder.GetTriangleStripsFromMesh(dracoGeometry, dracoArray); var indices = new Uint32Array(dracoArray.size()); for (var i = 0; i < dracoArray.size(); ++i) { indices[i] = dracoArray.GetValue(i); } this.draco.destroy(dracoArray); return indices; } }, { key: "_getAttributeTypedArray", value: function _getAttributeTypedArray(decoder, dracoGeometry, dracoAttribute, attributeName) { if (dracoAttribute.ptr === 0) { var message = "DRACO decode bad attribute ".concat(attributeName); throw new Error(message); } var TypedArrayCtor = DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP[dracoAttribute.data_type()]; var numComponents = dracoAttribute.num_components(); var numPoints = dracoGeometry.num_points(); var numValues = numPoints * numComponents; var byteLength = numValues * TypedArrayCtor.BYTES_PER_ELEMENT; var dataType = this._getDracoDataType(TypedArrayCtor); var ptr = this.draco._malloc(byteLength); decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, dracoAttribute, dataType, byteLength, ptr); var typedArray = new TypedArrayCtor(this.draco.HEAPF32.buffer, ptr, numValues).slice(); this.draco._free(ptr); return { typedArray: typedArray, components: numComponents }; } }, { key: "_getDracoDataType", value: function _getDracoDataType(attributeType) { switch (attributeType) { case Float32Array: return this.draco.DT_FLOAT32; case Int8Array: return this.draco.DT_INT8; case Int16Array: return this.draco.DT_INT16; case Int32Array: return this.draco.DT_INT32; case Uint8Array: return this.draco.DT_UINT8; case Uint16Array: return this.draco.DT_UINT16; case Uint32Array: return this.draco.DT_UINT32; default: return this.draco.DT_INVALID; } } }, { key: "_deduceAttributeName", value: function _deduceAttributeName(attributeData, options) { var _options$extraAttribu = options.extraAttributes, extraAttributes = _options$extraAttribu === void 0 ? {} : _options$extraAttribu; if (extraAttributes && _typeof(extraAttributes) === 'object') { for (var _i = 0, _Object$entries = Object.entries(extraAttributes); _i < _Object$entries.length; _i++) { var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attributeName = _Object$entries$_i[0], attributeUniqueId = _Object$entries$_i[1]; if (attributeUniqueId === attributeData.uniqueId) { return attributeName; } } } for (var dracoAttributeConstant in DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP) { var attributeType = this.draco[dracoAttributeConstant]; if (attributeData.attributeType === attributeType) { return DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP[dracoAttributeConstant]; } } if (attributeData.metadata) { var entryName = options.attributeNameEntry || 'name'; if (attributeData.metadata[entryName]) { return attributeData.metadata[entryName].string; } } return "CUSTOM_ATTRIBUTE_".concat(attributeData.uniqueId); } }, { key: "_getGeometryMetadata", value: function _getGeometryMetadata(decoder, dracoGeometry) { var dracoMetadata = decoder.GetMetadata(dracoGeometry); return this._queryDracoMetadata(dracoMetadata); } }, { key: "_getAttributeMetadata", value: function _getAttributeMetadata(decoder, dracoGeometry, attributeId) { var dracoMetadata = decoder.GetAttributeMetadata(dracoGeometry, attributeId); return this._queryDracoMetadata(dracoMetadata); } }, { key: "_queryDracoMetadata", value: function _queryDracoMetadata(dracoMetadata) { if (!dracoMetadata || !dracoMetadata.ptr) { return {}; } var result = {}; var numEntries = this.metadataQuerier.NumEntries(dracoMetadata); var dracoArray = new this.draco.DracoInt32Array(); for (var entryIndex = 0; entryIndex < numEntries; entryIndex++) { var entryName = this.metadataQuerier.GetEntryName(dracoMetadata, entryIndex); this.metadataQuerier.GetIntEntryArray(dracoMetadata, entryName, dracoArray); var numValues = dracoArray.size(); var intArray = new Int32Array(numValues); for (var i = 0; i < numValues; i++) { intArray[i] = dracoArray.GetValue(i); } result[entryName] = { "int": this.metadataQuerier.GetIntEntry(dracoMetadata, entryName), string: this.metadataQuerier.GetStringEntry(dracoMetadata, entryName), "double": this.metadataQuerier.GetDoubleEntry(dracoMetadata, entryName), intArray: intArray }; } this.draco.destroy(dracoArray); return result; } }, { key: "decode", value: function decode(arrayBuffer, options) { return this.parseSync(arrayBuffer, options); } }]); return DracoParser; }(); export { DracoParser as default }; //# sourceMappingURL=draco-parser.js.map