UNPKG

molstar

Version:

A comprehensive macromolecular library.

219 lines (218 loc) 15.8 kB
/** * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Sukolsak Sakshuwong <sukolsak@stanford.edu> * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { __awaiter, __extends, __generator, __spreadArray } from "tslib"; import { asciiWrite } from '../../mol-io/common/ascii'; import { Vec3, Mat3, Mat4 } from '../../mol-math/linear-algebra'; import { PLUGIN_VERSION } from '../../mol-plugin/version'; import { StringBuilder } from '../../mol-util'; import { Color } from '../../mol-util/color/color'; import { zip } from '../../mol-util/zip/zip'; import { MeshExporter } from './mesh-exporter'; // avoiding namespace lookup improved performance in Chrome (Aug 2020) var v3fromArray = Vec3.fromArray; var v3transformMat4 = Vec3.transformMat4; var v3transformMat3 = Vec3.transformMat3; var mat3directionTransform = Mat3.directionTransform; var UsdzExporter = /** @class */ (function (_super) { __extends(UsdzExporter, _super); function UsdzExporter(boundingBox, radius) { var _this = _super.call(this) || this; _this.fileExtension = 'usdz'; _this.meshes = []; _this.materials = []; _this.materialMap = new Map(); var t = Mat4(); // scale the model so that it fits within 1 meter Mat4.fromUniformScaling(t, Math.min(1 / (radius * 2), 1)); // translate the model so that it sits on the ground plane (y = 0) Mat4.translate(t, t, Vec3.create(-(boundingBox.min[0] + boundingBox.max[0]) / 2, -boundingBox.min[1], -(boundingBox.min[2] + boundingBox.max[2]) / 2)); _this.centerTransform = t; return _this; } UsdzExporter.prototype.addMaterial = function (color, alpha, metalness, roughness) { var hash = "".concat(color, "|").concat(alpha, "|").concat(metalness, "|").concat(roughness); if (this.materialMap.has(hash)) return this.materialMap.get(hash); var materialKey = this.materialMap.size; this.materialMap.set(hash, materialKey); var _a = Color.toRgbNormalized(color).map(function (v) { return Math.round(v * 1000) / 1000; }), r = _a[0], g = _a[1], b = _a[2]; this.materials.push("\ndef Material \"material".concat(materialKey, "\"\n{\n token outputs:surface.connect = </material").concat(materialKey, "/shader.outputs:surface>\n def Shader \"shader\"\n {\n uniform token info:id = \"UsdPreviewSurface\"\n color3f inputs:diffuseColor = (").concat(r, ",").concat(g, ",").concat(b, ")\n float inputs:opacity = ").concat(alpha, "\n float inputs:metallic = ").concat(metalness, "\n float inputs:roughness = ").concat(roughness, "\n token outputs:surface\n }\n}\n")); return materialKey; }; UsdzExporter.prototype.addMeshWithColors = function (input) { return __awaiter(this, void 0, void 0, function () { var mesh, values, isGeoTexture, webgl, ctx, t, n, tmpV, stride, colorType, overpaintType, transparencyType, uAlpha, aTransform, instanceCount, metalness, roughness, interpolatedColors, interpolatedOverpaint, stride_1, interpolatedTransparency, stride_2, _loop_1, this_1, instanceIndex; return __generator(this, function (_a) { switch (_a.label) { case 0: mesh = input.mesh, values = input.values, isGeoTexture = input.isGeoTexture, webgl = input.webgl, ctx = input.ctx; t = Mat4(); n = Mat3(); tmpV = Vec3(); stride = isGeoTexture ? 4 : 3; colorType = values.dColorType.ref.value; overpaintType = values.dOverpaintType.ref.value; transparencyType = values.dTransparencyType.ref.value; uAlpha = values.uAlpha.ref.value; aTransform = values.aTransform.ref.value; instanceCount = values.uInstanceCount.ref.value; metalness = values.uMetalness.ref.value; roughness = values.uRoughness.ref.value; if (colorType === 'volume' || colorType === 'volumeInstance') { interpolatedColors = UsdzExporter.getInterpolatedColors(webgl, { vertices: mesh.vertices, vertexCount: mesh.vertexCount, values: values, stride: stride, colorType: colorType }); } if (overpaintType === 'volumeInstance') { stride_1 = isGeoTexture ? 4 : 3; interpolatedOverpaint = UsdzExporter.getInterpolatedOverpaint(webgl, { vertices: mesh.vertices, vertexCount: mesh.vertexCount, values: values, stride: stride_1, colorType: overpaintType }); } if (transparencyType === 'volumeInstance') { stride_2 = isGeoTexture ? 4 : 3; interpolatedTransparency = UsdzExporter.getInterpolatedTransparency(webgl, { vertices: mesh.vertices, vertexCount: mesh.vertexCount, values: values, stride: stride_2, colorType: transparencyType }); } return [4 /*yield*/, ctx.update({ isIndeterminate: false, current: 0, max: instanceCount })]; case 1: _a.sent(); _loop_1 = function (instanceIndex) { var _b, vertices, normals, indices, groups, vertexCount, drawCount, vertexBuilder, normalBuilder, indexBuilder, i, i, geoData, i, v, quantizedColors, i, v, color, faceIndicesByMaterial, i, color, transparency, alpha, materialKey, faceIndices, materialBinding, materialKey, geomSubsets_1; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!ctx.shouldUpdate) return [3 /*break*/, 2]; return [4 /*yield*/, ctx.update({ current: instanceIndex + 1 })]; case 1: _c.sent(); _c.label = 2; case 2: _b = UsdzExporter.getInstance(input, instanceIndex), vertices = _b.vertices, normals = _b.normals, indices = _b.indices, groups = _b.groups, vertexCount = _b.vertexCount, drawCount = _b.drawCount; Mat4.fromArray(t, aTransform, instanceIndex * 16); Mat4.mul(t, this_1.centerTransform, t); mat3directionTransform(n, t); vertexBuilder = StringBuilder.create(); normalBuilder = StringBuilder.create(); indexBuilder = StringBuilder.create(); // position for (i = 0; i < vertexCount; ++i) { v3transformMat4(tmpV, v3fromArray(tmpV, vertices, i * stride), t); StringBuilder.writeSafe(vertexBuilder, (i === 0) ? '(' : ',('); StringBuilder.writeFloat(vertexBuilder, tmpV[0], 10000); StringBuilder.writeSafe(vertexBuilder, ','); StringBuilder.writeFloat(vertexBuilder, tmpV[1], 10000); StringBuilder.writeSafe(vertexBuilder, ','); StringBuilder.writeFloat(vertexBuilder, tmpV[2], 10000); StringBuilder.writeSafe(vertexBuilder, ')'); } // normal for (i = 0; i < vertexCount; ++i) { v3transformMat3(tmpV, v3fromArray(tmpV, normals, i * stride), n); StringBuilder.writeSafe(normalBuilder, (i === 0) ? '(' : ',('); StringBuilder.writeFloat(normalBuilder, tmpV[0], 100); StringBuilder.writeSafe(normalBuilder, ','); StringBuilder.writeFloat(normalBuilder, tmpV[1], 100); StringBuilder.writeSafe(normalBuilder, ','); StringBuilder.writeFloat(normalBuilder, tmpV[2], 100); StringBuilder.writeSafe(normalBuilder, ')'); } geoData = { values: values, groups: groups, vertexCount: vertexCount, instanceIndex: instanceIndex, isGeoTexture: isGeoTexture }; // face for (i = 0; i < drawCount; ++i) { v = isGeoTexture ? i : indices[i]; if (i > 0) StringBuilder.writeSafe(indexBuilder, ','); StringBuilder.writeInteger(indexBuilder, v); } quantizedColors = new Uint8Array(drawCount * 3); for (i = 0; i < drawCount; i += 3) { v = isGeoTexture ? i : indices[i]; color = UsdzExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint); Color.toArray(color, quantizedColors, i); } UsdzExporter.quantizeColors(quantizedColors, vertexCount); faceIndicesByMaterial = new Map(); for (i = 0; i < drawCount; i += 3) { color = Color.fromArray(quantizedColors, i); transparency = UsdzExporter.getTransparency(i, geoData, interpolatedTransparency); alpha = Math.round(uAlpha * (1 - transparency) * 10) / 10; materialKey = this_1.addMaterial(color, alpha, metalness, roughness); faceIndices = faceIndicesByMaterial.get(materialKey); if (faceIndices === undefined) { faceIndices = []; faceIndicesByMaterial.set(materialKey, faceIndices); } faceIndices.push(i / 3); } materialBinding = void 0; if (faceIndicesByMaterial.size === 1) { materialKey = faceIndicesByMaterial.keys().next().value; materialBinding = "rel material:binding = </material".concat(materialKey, ">"); } else { geomSubsets_1 = []; faceIndicesByMaterial.forEach(function (faceIndices, materialKey) { geomSubsets_1.push("\n def GeomSubset \"g".concat(materialKey, "\"\n {\n uniform token elementType = \"face\"\n uniform token familyName = \"materialBind\"\n int[] indices = [").concat(faceIndices.join(','), "]\n rel material:binding = </material").concat(materialKey, ">\n }\n")); }); materialBinding = geomSubsets_1.join(''); } this_1.meshes.push("\ndef Mesh \"mesh".concat(this_1.meshes.length, "\"\n{\n int[] faceVertexCounts = [").concat(new Array(drawCount / 3).fill(3).join(','), "]\n int[] faceVertexIndices = [").concat(StringBuilder.getString(indexBuilder), "]\n point3f[] points = [").concat(StringBuilder.getString(vertexBuilder), "]\n normal3f[] primvars:normals = [").concat(StringBuilder.getString(normalBuilder), "] (\n interpolation = \"vertex\"\n )\n uniform token subdivisionScheme = \"none\"\n ").concat(materialBinding, "\n}\n")); return [2 /*return*/]; } }); }; this_1 = this; instanceIndex = 0; _a.label = 2; case 2: if (!(instanceIndex < instanceCount)) return [3 /*break*/, 5]; return [5 /*yield**/, _loop_1(instanceIndex)]; case 3: _a.sent(); _a.label = 4; case 4: ++instanceIndex; return [3 /*break*/, 2]; case 5: return [2 /*return*/]; } }); }); }; UsdzExporter.prototype.getData = function (ctx) { return __awaiter(this, void 0, void 0, function () { var header, usda, usdaData, zipDataObj; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: header = "#usda 1.0\n(\n customLayerData = {\n string creator = \"Mol* ".concat(PLUGIN_VERSION, "\"\n }\n metersPerUnit = 1\n)\n"); usda = __spreadArray(__spreadArray([header], this.materials, true), this.meshes, true).join(''); usdaData = new Uint8Array(usda.length); asciiWrite(usdaData, usda); zipDataObj = (_a = {}, _a['model.usda'] = usdaData, _a); _b = {}; return [4 /*yield*/, zip(ctx, zipDataObj, true)]; case 1: return [2 /*return*/, (_b.usdz = _c.sent(), _b)]; } }); }); }; UsdzExporter.prototype.getBlob = function (ctx) { return __awaiter(this, void 0, void 0, function () { var usdz; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.getData(ctx)]; case 1: usdz = (_a.sent()).usdz; return [2 /*return*/, new Blob([usdz], { type: 'model/vnd.usdz+zip' })]; } }); }); }; return UsdzExporter; }(MeshExporter)); export { UsdzExporter };