UNPKG

molstar

Version:

A comprehensive macromolecular library.

301 lines 16.2 kB
"use strict"; /** * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Sukolsak Sakshuwong <sukolsak@stanford.edu> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.GlbExporter = void 0; var tslib_1 = require("tslib"); var ascii_1 = require("../../mol-io/common/ascii"); var binary_1 = require("../../mol-io/common/binary"); var linear_algebra_1 = require("../../mol-math/linear-algebra"); var version_1 = require("../../mol-plugin/version"); var color_1 = require("../../mol-util/color/color"); var array_1 = require("../../mol-util/array"); var mesh_exporter_1 = require("./mesh-exporter"); // avoiding namespace lookup improved performance in Chrome (Aug 2020) var v3fromArray = linear_algebra_1.Vec3.fromArray; var v3normalize = linear_algebra_1.Vec3.normalize; var v3toArray = linear_algebra_1.Vec3.toArray; // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0 var UNSIGNED_BYTE = 5121; var UNSIGNED_INT = 5125; var FLOAT = 5126; var ARRAY_BUFFER = 34962; var ELEMENT_ARRAY_BUFFER = 34963; var GlbExporter = /** @class */ (function (_super) { (0, tslib_1.__extends)(GlbExporter, _super); function GlbExporter(style, boundingBox) { var _this = _super.call(this) || this; _this.style = style; _this.fileExtension = 'glb'; _this.nodes = []; _this.meshes = []; _this.accessors = []; _this.bufferViews = []; _this.binaryBuffer = []; _this.byteOffset = 0; var tmpV = (0, linear_algebra_1.Vec3)(); linear_algebra_1.Vec3.add(tmpV, boundingBox.min, boundingBox.max); linear_algebra_1.Vec3.scale(tmpV, tmpV, -0.5); _this.centerTransform = linear_algebra_1.Mat4.fromTranslation((0, linear_algebra_1.Mat4)(), tmpV); return _this; } GlbExporter.vec3MinMax = function (a) { var min = [Infinity, Infinity, Infinity]; var max = [-Infinity, -Infinity, -Infinity]; for (var i = 0, il = a.length; i < il; i += 3) { for (var j = 0; j < 3; ++j) { min[j] = Math.min(a[i + j], min[j]); max[j] = Math.max(a[i + j], max[j]); } } return [min, max]; }; GlbExporter.prototype.addBuffer = function (buffer, componentType, type, count, target, min, max, normalized) { this.binaryBuffer.push(buffer); var bufferViewOffset = this.bufferViews.length; this.bufferViews.push({ buffer: 0, byteOffset: this.byteOffset, byteLength: buffer.byteLength, target: target }); this.byteOffset += buffer.byteLength; var accessorOffset = this.accessors.length; this.accessors.push({ bufferView: bufferViewOffset, byteOffset: 0, componentType: componentType, count: count, type: type, min: min, max: max, normalized: normalized }); return accessorOffset; }; GlbExporter.prototype.addGeometryBuffers = function (vertices, normals, indices, vertexCount, drawCount, isGeoTexture) { var tmpV = (0, linear_algebra_1.Vec3)(); var stride = isGeoTexture ? 4 : 3; var vertexArray = new Float32Array(vertexCount * 3); var normalArray = new Float32Array(vertexCount * 3); var indexArray; // position for (var i = 0; i < vertexCount; ++i) { v3fromArray(tmpV, vertices, i * stride); v3toArray(tmpV, vertexArray, i * 3); } // normal for (var i = 0; i < vertexCount; ++i) { v3fromArray(tmpV, normals, i * stride); v3normalize(tmpV, tmpV); v3toArray(tmpV, normalArray, i * 3); } // face if (!isGeoTexture) { indexArray = indices.slice(0, drawCount); } var _a = GlbExporter.vec3MinMax(vertexArray), vertexMin = _a[0], vertexMax = _a[1]; var vertexBuffer = vertexArray.buffer; var normalBuffer = normalArray.buffer; var indexBuffer = isGeoTexture ? undefined : indexArray.buffer; if (!binary_1.IsNativeEndianLittle) { vertexBuffer = (0, binary_1.flipByteOrder)(new Uint8Array(vertexBuffer), 4); normalBuffer = (0, binary_1.flipByteOrder)(new Uint8Array(normalBuffer), 4); if (!isGeoTexture) indexBuffer = (0, binary_1.flipByteOrder)(new Uint8Array(indexBuffer), 4); } return { vertexAccessorIndex: this.addBuffer(vertexBuffer, FLOAT, 'VEC3', vertexCount, ARRAY_BUFFER, vertexMin, vertexMax), normalAccessorIndex: this.addBuffer(normalBuffer, FLOAT, 'VEC3', vertexCount, ARRAY_BUFFER), indexAccessorIndex: isGeoTexture ? undefined : this.addBuffer(indexBuffer, UNSIGNED_INT, 'SCALAR', drawCount, ELEMENT_ARRAY_BUFFER) }; }; GlbExporter.prototype.addColorBuffer = function (values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors) { var groupCount = values.uGroupCount.ref.value; var uAlpha = values.uAlpha.ref.value; var dTransparency = values.dTransparency.ref.value; var tTransparency = values.tTransparency.ref.value; var colorArray = new Uint8Array(vertexCount * 4); for (var i = 0; i < vertexCount; ++i) { var color = GlbExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, i); var alpha = uAlpha; if (dTransparency) { var group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i]; var transparency = tTransparency.array[instanceIndex * groupCount + group] / 255; alpha *= 1 - transparency; } color = color_1.Color.sRGBToLinear(color); color_1.Color.toArray(color, colorArray, i * 4); colorArray[i * 4 + 3] = Math.round(alpha * 255); } var colorBuffer = colorArray.buffer; if (!binary_1.IsNativeEndianLittle) { colorBuffer = (0, binary_1.flipByteOrder)(new Uint8Array(colorBuffer), 4); } return this.addBuffer(colorBuffer, UNSIGNED_BYTE, 'VEC4', vertexCount, ARRAY_BUFFER, undefined, undefined, true); }; GlbExporter.prototype.addMeshWithColors = function (input) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var mesh, values, isGeoTexture, webgl, ctx, t, colorType, dTransparency, aTransform, instanceCount, interpolatedColors, stride, sameGeometryBuffers, sameColorBuffer, vertexAccessorIndex, normalAccessorIndex, indexAccessorIndex, colorAccessorIndex, meshIndex, instanceIndex, _a, vertices, normals, indices, groups, vertexCount, drawCount, accessorIndices, node; return (0, tslib_1.__generator)(this, function (_b) { switch (_b.label) { case 0: mesh = input.mesh, values = input.values, isGeoTexture = input.isGeoTexture, webgl = input.webgl, ctx = input.ctx; t = (0, linear_algebra_1.Mat4)(); colorType = values.dColorType.ref.value; dTransparency = values.dTransparency.ref.value; aTransform = values.aTransform.ref.value; instanceCount = values.uInstanceCount.ref.value; if (colorType === 'volume' || colorType === 'volumeInstance') { stride = isGeoTexture ? 4 : 3; interpolatedColors = GlbExporter.getInterpolatedColors(mesh.vertices, mesh.vertexCount, values, stride, colorType, webgl); } sameGeometryBuffers = mesh !== undefined; sameColorBuffer = sameGeometryBuffers && colorType !== 'instance' && !colorType.endsWith('Instance') && !dTransparency; return [4 /*yield*/, ctx.update({ isIndeterminate: false, current: 0, max: instanceCount })]; case 1: _b.sent(); instanceIndex = 0; _b.label = 2; case 2: if (!(instanceIndex < instanceCount)) return [3 /*break*/, 6]; if (!ctx.shouldUpdate) return [3 /*break*/, 4]; return [4 /*yield*/, ctx.update({ current: instanceIndex + 1 })]; case 3: _b.sent(); _b.label = 4; case 4: // create a glTF mesh if needed if (instanceIndex === 0 || !sameGeometryBuffers || !sameColorBuffer) { _a = GlbExporter.getInstance(input, instanceIndex), vertices = _a.vertices, normals = _a.normals, indices = _a.indices, groups = _a.groups, vertexCount = _a.vertexCount, drawCount = _a.drawCount; // create geometry buffers if needed if (instanceIndex === 0 || !sameGeometryBuffers) { accessorIndices = this.addGeometryBuffers(vertices, normals, indices, vertexCount, drawCount, isGeoTexture); vertexAccessorIndex = accessorIndices.vertexAccessorIndex; normalAccessorIndex = accessorIndices.normalAccessorIndex; indexAccessorIndex = accessorIndices.indexAccessorIndex; } // create a color buffer if needed if (instanceIndex === 0 || !sameColorBuffer) { colorAccessorIndex = this.addColorBuffer(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors); } // glTF mesh meshIndex = this.meshes.length; this.meshes.push({ primitives: [{ attributes: { POSITION: vertexAccessorIndex, NORMAL: normalAccessorIndex, COLOR_0: colorAccessorIndex }, indices: indexAccessorIndex, material: 0 }] }); } // node linear_algebra_1.Mat4.fromArray(t, aTransform, instanceIndex * 16); linear_algebra_1.Mat4.mul(t, this.centerTransform, t); node = { mesh: meshIndex, matrix: t.slice() }; this.nodes.push(node); _b.label = 5; case 5: ++instanceIndex; return [3 /*break*/, 2]; case 6: return [2 /*return*/]; } }); }); }; GlbExporter.prototype.getData = function () { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var binaryBufferLength, gltf, createChunk, jsonString, jsonBuffer, _a, jsonChunk, jsonChunkLength, _b, binaryChunk, binaryChunkLength, glbBufferLength, header, headerDataView, glbBuffer, glb, offset, _i, glbBuffer_1, buffer; return (0, tslib_1.__generator)(this, function (_c) { binaryBufferLength = this.byteOffset; gltf = { asset: { version: '2.0', generator: "Mol* " + version_1.PLUGIN_VERSION }, scenes: [{ nodes: (0, array_1.fillSerial)(new Array(this.nodes.length)) }], nodes: this.nodes, meshes: this.meshes, buffers: [{ byteLength: binaryBufferLength, }], bufferViews: this.bufferViews, accessors: this.accessors, materials: [{ pbrMetallicRoughness: { baseColorFactor: [1, 1, 1, 1], metallicFactor: this.style.metalness, roughnessFactor: this.style.roughness } }] }; createChunk = function (chunkType, data, byteLength, padChar) { var padding = null; if (byteLength % 4 !== 0) { var pad = 4 - (byteLength % 4); byteLength += pad; padding = new Uint8Array(pad); padding.fill(padChar); } var preamble = new ArrayBuffer(8); var preambleDataView = new DataView(preamble); preambleDataView.setUint32(0, byteLength, true); preambleDataView.setUint32(4, chunkType, true); var chunk = (0, tslib_1.__spreadArray)([preamble], data, true); if (padding) { chunk.push(padding.buffer); } return [chunk, 8 + byteLength]; }; jsonString = JSON.stringify(gltf); jsonBuffer = new Uint8Array(jsonString.length); (0, ascii_1.asciiWrite)(jsonBuffer, jsonString); _a = createChunk(0x4E4F534A, [jsonBuffer.buffer], jsonBuffer.length, 0x20), jsonChunk = _a[0], jsonChunkLength = _a[1]; _b = createChunk(0x004E4942, this.binaryBuffer, binaryBufferLength, 0x00), binaryChunk = _b[0], binaryChunkLength = _b[1]; glbBufferLength = 12 + jsonChunkLength + binaryChunkLength; header = new ArrayBuffer(12); headerDataView = new DataView(header); headerDataView.setUint32(0, 0x46546C67, true); // magic number "glTF" headerDataView.setUint32(4, 2, true); // version headerDataView.setUint32(8, glbBufferLength, true); // length glbBuffer = (0, tslib_1.__spreadArray)((0, tslib_1.__spreadArray)([header], jsonChunk, true), binaryChunk, true); glb = new Uint8Array(glbBufferLength); offset = 0; for (_i = 0, glbBuffer_1 = glbBuffer; _i < glbBuffer_1.length; _i++) { buffer = glbBuffer_1[_i]; glb.set(new Uint8Array(buffer), offset); offset += buffer.byteLength; } return [2 /*return*/, { glb: glb }]; }); }); }; GlbExporter.prototype.getBlob = function (ctx) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var _a; return (0, tslib_1.__generator)(this, function (_b) { switch (_b.label) { case 0: _a = Blob.bind; return [4 /*yield*/, this.getData()]; case 1: return [2 /*return*/, new (_a.apply(Blob, [void 0, [(_b.sent()).glb], { type: 'model/gltf-binary' }]))()]; } }); }); }; return GlbExporter; }(mesh_exporter_1.MeshExporter)); exports.GlbExporter = GlbExporter; //# sourceMappingURL=glb-exporter.js.map