@softrobot/loaders.gl-draco
Version:
Framework-independent loader and writer for Draco compressed meshes and point clouds
389 lines (320 loc) • 13 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _loaderUtils = require("@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) {
(0, _classCallCheck2["default"])(this, DracoParser);
this.draco = draco;
this.drawMode = 'TRIANGLE';
this.metadataQuerier = {};
}
(0, _createClass2["default"])(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: (0, _loaderUtils.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 && (0, _typeof2["default"])(extraAttributes) === 'object') {
for (var _i = 0, _Object$entries = Object.entries(extraAttributes); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = (0, _slicedToArray2["default"])(_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;
}();
exports["default"] = DracoParser;
//# sourceMappingURL=draco-parser.js.map