@softrobot/loaders.gl-draco
Version:
Framework-independent loader and writer for Draco compressed meshes and point clouds
405 lines (319 loc) • 15.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
var GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP = {
POSITION: 'POSITION',
NORMAL: 'NORMAL',
COLOR_0: 'COLOR',
TEXCOORD_0: 'TEX_COORD'
};
function noop() {}
var DracoBuilder = function () {
function DracoBuilder(draco) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
(0, _classCallCheck2["default"])(this, DracoBuilder);
this.draco = draco;
this.dracoEncoder = new this.draco.Encoder();
this.dracoMeshBuilder = new this.draco.MeshBuilder();
this.dracoMetadataBuilder = new this.draco.MetadataBuilder();
this.log = options.log || noop;
}
(0, _createClass2["default"])(DracoBuilder, [{
key: "destroy",
value: function destroy() {
this.destroyEncodedObject(this.dracoMeshBuilder);
this.destroyEncodedObject(this.dracoEncoder);
this.destroyEncodedObject(this.dracoMetadataBuilder);
this.dracoMeshBuilder = null;
this.dracoEncoder = null;
this.draco = null;
}
}, {
key: "destroyEncodedObject",
value: function destroyEncodedObject(object) {
if (object) {
this.draco.destroy(object);
}
}
}, {
key: "encodeSync",
value: function encodeSync(mesh) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
this._setOptions(options);
return options.pointcloud ? this._encodePointCloud(mesh, options) : this._encodeMesh(mesh, options);
}
}, {
key: "_getAttributesFromMesh",
value: function _getAttributesFromMesh(mesh) {
var attributes = _objectSpread(_objectSpread({}, mesh), mesh.attributes);
if (mesh.indices) {
attributes.indices = mesh.indices;
}
return attributes;
}
}, {
key: "_encodePointCloud",
value: function _encodePointCloud(pointcloud, options) {
var dracoPointCloud = new this.draco.PointCloud();
if (options.metadata) {
this._addGeometryMetadata(dracoPointCloud, options.metadata);
}
var attributes = this._getAttributesFromMesh(pointcloud);
this._createDracoPointCloud(dracoPointCloud, attributes, options);
var dracoData = new this.draco.DracoInt8Array();
try {
var encodedLen = this.dracoEncoder.EncodePointCloudToDracoBuffer(dracoPointCloud, false, dracoData);
if (!(encodedLen > 0)) {
throw new Error('Draco encoding failed.');
}
this.log("DRACO encoded ".concat(dracoPointCloud.num_points(), " points\n with ").concat(dracoPointCloud.num_attributes(), " attributes into ").concat(encodedLen, " bytes"));
return dracoInt8ArrayToArrayBuffer(dracoData);
} finally {
this.destroyEncodedObject(dracoData);
this.destroyEncodedObject(dracoPointCloud);
}
}
}, {
key: "_encodeMesh",
value: function _encodeMesh(mesh, options) {
var dracoMesh = new this.draco.Mesh();
if (options.metadata) {
this._addGeometryMetadata(dracoMesh, options.metadata);
}
var attributes = this._getAttributesFromMesh(mesh);
this._createDracoMesh(dracoMesh, attributes, options);
var dracoData = new this.draco.DracoInt8Array();
try {
var encodedLen = this.dracoEncoder.EncodeMeshToDracoBuffer(dracoMesh, dracoData);
if (encodedLen <= 0) {
throw new Error('Draco encoding failed.');
}
this.log("DRACO encoded ".concat(dracoMesh.num_points(), " points\n with ").concat(dracoMesh.num_attributes(), " attributes into ").concat(encodedLen, " bytes"));
return dracoInt8ArrayToArrayBuffer(dracoData);
} finally {
this.destroyEncodedObject(dracoData);
this.destroyEncodedObject(dracoMesh);
}
}
}, {
key: "_setOptions",
value: function _setOptions(options) {
if ('speed' in options) {
var _this$dracoEncoder;
(_this$dracoEncoder = this.dracoEncoder).SetSpeedOptions.apply(_this$dracoEncoder, (0, _toConsumableArray2["default"])(options.speed));
}
if ('method' in options) {
var dracoMethod = this.draco[options.method];
this.dracoEncoder.SetEncodingMethod(dracoMethod);
}
if ('quantization' in options) {
for (var attribute in options.quantization) {
var bits = options.quantization[attribute];
var dracoPosition = this.draco[attribute];
this.dracoEncoder.SetAttributeQuantization(dracoPosition, bits);
}
}
}
}, {
key: "_createDracoMesh",
value: function _createDracoMesh(dracoMesh, attributes, options) {
var optionalMetadata = options.attributesMetadata || {};
try {
var positions = this._getPositionAttribute(attributes);
if (!positions) {
throw new Error('positions');
}
var vertexCount = positions.length / 3;
for (var attributeName in attributes) {
var attribute = attributes[attributeName];
attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName;
var uniqueId = this._addAttributeToMesh(dracoMesh, attributeName, attribute, vertexCount);
if (uniqueId !== -1) {
this._addAttributeMetadata(dracoMesh, uniqueId, _objectSpread({
name: attributeName
}, optionalMetadata[attributeName] || {}));
}
}
} catch (error) {
this.destroyEncodedObject(dracoMesh);
throw error;
}
return dracoMesh;
}
}, {
key: "_createDracoPointCloud",
value: function _createDracoPointCloud(dracoPointCloud, attributes, options) {
var optionalMetadata = options.attributesMetadata || {};
try {
var positions = this._getPositionAttribute(attributes);
if (!positions) {
throw new Error('positions');
}
var vertexCount = positions.length / 3;
for (var attributeName in attributes) {
var attribute = attributes[attributeName];
attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName;
var uniqueId = this._addAttributeToMesh(dracoPointCloud, attributeName, attribute, vertexCount);
if (uniqueId !== -1) {
this._addAttributeMetadata(dracoPointCloud, uniqueId, _objectSpread({
name: attributeName
}, optionalMetadata[attributeName] || {}));
}
}
} catch (error) {
this.destroyEncodedObject(dracoPointCloud);
throw error;
}
return dracoPointCloud;
}
}, {
key: "_addAttributeToMesh",
value: function _addAttributeToMesh(mesh, attributeName, attribute, vertexCount) {
if (!ArrayBuffer.isView(attribute)) {
return -1;
}
var type = this._getDracoAttributeType(attributeName);
var size = attribute.length / vertexCount;
if (type === 'indices') {
var numFaces = attribute.length / 3;
this.log("Adding attribute ".concat(attributeName, ", size ").concat(numFaces));
this.dracoMeshBuilder.AddFacesToMesh(mesh, numFaces, attribute);
return -1;
}
this.log("Adding attribute ".concat(attributeName, ", size ").concat(size));
var builder = this.dracoMeshBuilder;
var buffer = attribute.buffer;
switch (attribute.constructor) {
case Int8Array:
return builder.AddInt8Attribute(mesh, type, vertexCount, size, new Int8Array(buffer));
case Int16Array:
return builder.AddInt16Attribute(mesh, type, vertexCount, size, new Int16Array(buffer));
case Int32Array:
return builder.AddInt32Attribute(mesh, type, vertexCount, size, new Int32Array(buffer));
case Uint8Array:
case Uint8ClampedArray:
return builder.AddUInt8Attribute(mesh, type, vertexCount, size, new Uint8Array(buffer));
case Uint16Array:
return builder.AddUInt16Attribute(mesh, type, vertexCount, size, new Uint16Array(buffer));
case Uint32Array:
return builder.AddUInt32Attribute(mesh, type, vertexCount, size, new Uint32Array(buffer));
case Float32Array:
default:
return builder.AddFloatAttribute(mesh, type, vertexCount, size, new Float32Array(buffer));
}
}
}, {
key: "_getDracoAttributeType",
value: function _getDracoAttributeType(attributeName) {
switch (attributeName.toLowerCase()) {
case 'indices':
return 'indices';
case 'position':
case 'positions':
case 'vertices':
return this.draco.POSITION;
case 'normal':
case 'normals':
return this.draco.NORMAL;
case 'color':
case 'colors':
return this.draco.COLOR;
case 'texcoord':
case 'texcoords':
return this.draco.TEX_COORD;
default:
return this.draco.GENERIC;
}
}
}, {
key: "_getPositionAttribute",
value: function _getPositionAttribute(attributes) {
for (var attributeName in attributes) {
var attribute = attributes[attributeName];
var dracoType = this._getDracoAttributeType(attributeName);
if (dracoType === this.draco.POSITION) {
return attribute;
}
}
return null;
}
}, {
key: "_addGeometryMetadata",
value: function _addGeometryMetadata(dracoGeometry, metadata) {
var dracoMetadata = new this.draco.Metadata();
this._populateDracoMetadata(dracoMetadata, metadata);
this.dracoMeshBuilder.AddMetadata(dracoGeometry, dracoMetadata);
}
}, {
key: "_addAttributeMetadata",
value: function _addAttributeMetadata(dracoGeometry, uniqueAttributeId, metadata) {
var dracoAttributeMetadata = new this.draco.Metadata();
this._populateDracoMetadata(dracoAttributeMetadata, metadata);
this.dracoMeshBuilder.SetMetadataForAttribute(dracoGeometry, uniqueAttributeId, dracoAttributeMetadata);
}
}, {
key: "_populateDracoMetadata",
value: function _populateDracoMetadata(dracoMetadata, metadata) {
var _iterator = _createForOfIteratorHelper(getEntries(metadata)),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _step$value = (0, _slicedToArray2["default"])(_step.value, 2),
key = _step$value[0],
value = _step$value[1];
switch ((0, _typeof2["default"])(value)) {
case 'number':
if (Math.trunc(value) === value) {
this.dracoMetadataBuilder.AddIntEntry(dracoMetadata, key, value);
} else {
this.dracoMetadataBuilder.AddDoubleEntry(dracoMetadata, key, value);
}
break;
case 'object':
if (value instanceof Int32Array) {
this.dracoMetadataBuilder.AddIntEntryArray(dracoMetadata, key, value, value.length);
}
break;
case 'string':
default:
this.dracoMetadataBuilder.AddStringEntry(dracoMetadata, key, value);
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}
}]);
return DracoBuilder;
}();
exports["default"] = DracoBuilder;
function dracoInt8ArrayToArrayBuffer(dracoData) {
var byteLength = dracoData.size();
var outputBuffer = new ArrayBuffer(byteLength);
var outputData = new Int8Array(outputBuffer);
for (var i = 0; i < byteLength; ++i) {
outputData[i] = dracoData.GetValue(i);
}
return outputBuffer;
}
function getEntries(container) {
var hasEntriesFunc = container.entries && !container.hasOwnProperty('entries');
return hasEntriesFunc ? container.entries() : Object.entries(container);
}
//# sourceMappingURL=draco-builder.js.map