molstar
Version:
A comprehensive macromolecular library.
301 lines (300 loc) • 12.7 kB
JavaScript
"use strict";
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Scene = void 0;
var render_object_1 = require("./render-object");
var object3d_1 = require("./object3d");
var geometry_1 = require("../mol-math/geometry");
var commit_queue_1 = require("./commit-queue");
var now_1 = require("../mol-util/now");
var array_1 = require("../mol-util/array");
var boundary_helper_1 = require("../mol-math/geometry/boundary-helper");
var util_1 = require("../mol-data/util");
var render_item_1 = require("./webgl/render-item");
var interpolate_1 = require("../mol-math/interpolate");
var boundaryHelper = new boundary_helper_1.BoundaryHelper('98');
function calculateBoundingSphere(renderables, boundingSphere, onlyVisible) {
boundaryHelper.reset();
for (var i = 0, il = renderables.length; i < il; ++i) {
if (onlyVisible && !renderables[i].state.visible)
continue;
var boundingSphere_1 = renderables[i].values.boundingSphere.ref.value;
if (!boundingSphere_1.radius)
continue;
boundaryHelper.includeSphere(boundingSphere_1);
}
boundaryHelper.finishedIncludeStep();
for (var i = 0, il = renderables.length; i < il; ++i) {
if (onlyVisible && !renderables[i].state.visible)
continue;
var boundingSphere_2 = renderables[i].values.boundingSphere.ref.value;
if (!boundingSphere_2.radius)
continue;
boundaryHelper.radiusSphere(boundingSphere_2);
}
return boundaryHelper.getSphere(boundingSphere);
}
function renderableSort(a, b) {
var drawProgramIdA = (a.getProgram('colorBlended') || a.getProgram('colorWboit') || a.getProgram('colorDpoit')).id;
var drawProgramIdB = (b.getProgram('colorBlended') || b.getProgram('colorWboit') || b.getProgram('colorDpoit')).id;
var materialIdA = a.materialId;
var materialIdB = b.materialId;
if (drawProgramIdA !== drawProgramIdB) {
// sort by program id to minimize gl state changes
return drawProgramIdA - drawProgramIdB;
}
else if (materialIdA !== materialIdB) {
// sort by material id to minimize gl state changes
return materialIdA - materialIdB;
}
else {
return a.id - b.id;
}
}
var Scene;
(function (Scene) {
function create(ctx, variants) {
if (variants === void 0) { variants = render_item_1.GraphicsRenderVariants; }
var renderableMap = new Map();
var renderables = [];
var boundingSphere = (0, geometry_1.Sphere3D)();
var boundingSphereVisible = (0, geometry_1.Sphere3D)();
var primitives = [];
var volumes = [];
var boundingSphereDirty = true;
var boundingSphereVisibleDirty = true;
var markerAverage = 0;
var opacityAverage = 0;
var hasOpaque = false;
var object3d = object3d_1.Object3D.create();
var view = object3d.view, position = object3d.position, direction = object3d.direction, up = object3d.up;
function add(o) {
if (!renderableMap.has(o)) {
var renderable = (0, render_object_1.createRenderable)(ctx, o, variants);
renderables.push(renderable);
if (o.type === 'direct-volume') {
volumes.push(renderable);
}
else {
primitives.push(renderable);
}
renderableMap.set(o, renderable);
boundingSphereDirty = true;
boundingSphereVisibleDirty = true;
return renderable;
}
else {
console.warn("RenderObject with id '".concat(o.id, "' already present"));
return renderableMap.get(o);
}
}
function remove(o) {
var renderable = renderableMap.get(o);
if (renderable) {
renderable.dispose();
(0, array_1.arraySetRemove)(renderables, renderable);
(0, array_1.arraySetRemove)(primitives, renderable);
(0, array_1.arraySetRemove)(volumes, renderable);
renderableMap.delete(o);
boundingSphereDirty = true;
boundingSphereVisibleDirty = true;
}
}
var commitBulkSize = 100;
function commit(maxTimeMs) {
var start = (0, now_1.now)();
var i = 0;
while (true) {
var o = commitQueue.tryGetRemove();
if (!o)
break;
remove(o);
if (++i % commitBulkSize === 0 && (0, now_1.now)() - start > maxTimeMs)
return false;
}
while (true) {
var o = commitQueue.tryGetAdd();
if (!o)
break;
add(o);
if (++i % commitBulkSize === 0 && (0, now_1.now)() - start > maxTimeMs)
return false;
}
renderables.sort(renderableSort);
markerAverage = calculateMarkerAverage();
opacityAverage = calculateOpacityAverage();
hasOpaque = calculateHasOpaque();
return true;
}
var commitQueue = new commit_queue_1.CommitQueue();
var visibleHash = -1;
function computeVisibleHash() {
var hash = 23;
for (var i = 0, il = renderables.length; i < il; ++i) {
if (!renderables[i].state.visible)
continue;
hash = (31 * hash + renderables[i].id) | 0;
}
hash = (0, util_1.hash1)(hash);
if (hash === -1)
hash = 0;
return hash;
}
function syncVisibility() {
var newVisibleHash = computeVisibleHash();
if (newVisibleHash !== visibleHash) {
boundingSphereVisibleDirty = true;
markerAverage = calculateMarkerAverage();
opacityAverage = calculateOpacityAverage();
hasOpaque = calculateHasOpaque();
visibleHash = newVisibleHash;
return true;
}
else {
return false;
}
}
function calculateMarkerAverage() {
if (primitives.length === 0)
return 0;
var count = 0;
var markerAverage = 0;
for (var i = 0, il = primitives.length; i < il; ++i) {
if (!primitives[i].state.visible)
continue;
markerAverage += primitives[i].values.markerAverage.ref.value;
count += 1;
}
return count > 0 ? markerAverage / count : 0;
}
function calculateOpacityAverage() {
var _a, _b;
if (primitives.length === 0)
return 0;
var count = 0;
var opacityAverage = 0;
for (var i = 0, il = primitives.length; i < il; ++i) {
var p = primitives[i];
if (!p.state.visible)
continue;
// TODO: simplify, handle in renderable.state???
// uAlpha is updated in "render" so we need to recompute it here
var alpha = (0, interpolate_1.clamp)(p.values.alpha.ref.value * p.state.alphaFactor, 0, 1);
var xray = ((_a = p.values.dXrayShaded) === null || _a === void 0 ? void 0 : _a.ref.value) ? 0.5 : 1;
var fuzzy = ((_b = p.values.dPointStyle) === null || _b === void 0 ? void 0 : _b.ref.value) === 'fuzzy' ? 0.5 : 1;
var text = p.values.dGeometryType.ref.value === 'text' ? 0.5 : 1;
opacityAverage += (1 - p.values.transparencyAverage.ref.value) * alpha * xray * fuzzy * text;
count += 1;
}
return count > 0 ? opacityAverage / count : 0;
}
function calculateHasOpaque() {
var _a;
if (primitives.length === 0)
return false;
for (var i = 0, il = primitives.length; i < il; ++i) {
var p = primitives[i];
if (!p.state.visible)
continue;
if (p.state.opaque)
return true;
if (p.state.alphaFactor === 1 && p.values.alpha.ref.value === 1 && p.values.transparencyAverage.ref.value !== 1)
return true;
if (((_a = p.values.dTransparentBackfaces) === null || _a === void 0 ? void 0 : _a.ref.value) === 'opaque')
return true;
}
return false;
}
return {
view: view,
position: position,
direction: direction,
up: up,
renderables: renderables,
primitives: { view: view, position: position, direction: direction, up: up, renderables: primitives },
volumes: { view: view, position: position, direction: direction, up: up, renderables: volumes },
syncVisibility: syncVisibility,
update: function (objects, keepBoundingSphere) {
var _a;
object3d_1.Object3D.update(object3d);
if (objects) {
for (var i = 0, il = objects.length; i < il; ++i) {
(_a = renderableMap.get(objects[i])) === null || _a === void 0 ? void 0 : _a.update();
}
}
else {
for (var i = 0, il = renderables.length; i < il; ++i) {
renderables[i].update();
}
}
if (!keepBoundingSphere) {
boundingSphereDirty = true;
boundingSphereVisibleDirty = true;
}
else {
syncVisibility();
}
markerAverage = calculateMarkerAverage();
opacityAverage = calculateOpacityAverage();
hasOpaque = calculateHasOpaque();
},
add: function (o) { return commitQueue.add(o); },
remove: function (o) { return commitQueue.remove(o); },
commit: function (maxTime) {
if (maxTime === void 0) { maxTime = Number.MAX_VALUE; }
return commit(maxTime);
},
get needsCommit() { return !commitQueue.isEmpty; },
has: function (o) {
return renderableMap.has(o);
},
clear: function () {
for (var i = 0, il = renderables.length; i < il; ++i) {
renderables[i].dispose();
}
renderables.length = 0;
primitives.length = 0;
volumes.length = 0;
renderableMap.clear();
boundingSphereDirty = true;
boundingSphereVisibleDirty = true;
},
forEach: function (callbackFn) {
renderableMap.forEach(callbackFn);
},
get count() {
return renderables.length;
},
get boundingSphere() {
if (boundingSphereDirty) {
calculateBoundingSphere(renderables, boundingSphere, false);
boundingSphereDirty = false;
}
return boundingSphere;
},
get boundingSphereVisible() {
if (boundingSphereVisibleDirty) {
calculateBoundingSphere(renderables, boundingSphereVisible, true);
boundingSphereVisibleDirty = false;
}
return boundingSphereVisible;
},
get markerAverage() {
return markerAverage;
},
get opacityAverage() {
return opacityAverage;
},
get hasOpaque() {
return hasOpaque;
},
};
}
Scene.create = create;
})(Scene || (Scene = {}));
exports.Scene = Scene;