molstar
Version:
A comprehensive macromolecular library.
398 lines • 15.6 kB
JavaScript
"use strict";
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNullTexture = exports.loadImageTexture = exports.createTextures = exports.createTexture = exports.getAttachment = exports.getFilter = exports.getType = exports.getInternalFormat = exports.getFormat = exports.getTarget = void 0;
var mol_util_1 = require("../../mol-util");
var id_factory_1 = require("../../mol-util/id-factory");
var compat_1 = require("./compat");
var getNextTextureId = (0, id_factory_1.idFactory)();
function getTarget(gl, kind) {
switch (kind) {
case 'image-uint8': return gl.TEXTURE_2D;
case 'image-float32': return gl.TEXTURE_2D;
case 'image-float16': return gl.TEXTURE_2D;
case 'image-depth': return gl.TEXTURE_2D;
}
if ((0, compat_1.isWebGL2)(gl)) {
switch (kind) {
case 'image-int32': return gl.TEXTURE_2D;
case 'volume-uint8': return gl.TEXTURE_3D;
case 'volume-float32': return gl.TEXTURE_3D;
case 'volume-float16': return gl.TEXTURE_3D;
}
}
throw new Error("unknown texture kind '" + kind + "'");
}
exports.getTarget = getTarget;
function getFormat(gl, format, type) {
switch (format) {
case 'alpha':
if ((0, compat_1.isWebGL2)(gl) && type === 'float')
return gl.RED;
else if ((0, compat_1.isWebGL2)(gl) && type === 'int')
return gl.RED_INTEGER;
else
return gl.ALPHA;
case 'rgb':
if ((0, compat_1.isWebGL2)(gl) && type === 'int')
return gl.RGB_INTEGER;
return gl.RGB;
case 'rgba':
if ((0, compat_1.isWebGL2)(gl) && type === 'int')
return gl.RGBA_INTEGER;
return gl.RGBA;
case 'depth': return gl.DEPTH_COMPONENT;
}
}
exports.getFormat = getFormat;
function getInternalFormat(gl, format, type) {
if ((0, compat_1.isWebGL2)(gl)) {
switch (format) {
case 'alpha':
switch (type) {
case 'ubyte': return gl.ALPHA;
case 'float': return gl.R32F;
case 'fp16': return gl.R16F;
case 'int': return gl.R32I;
}
case 'rgb':
switch (type) {
case 'ubyte': return gl.RGB;
case 'float': return gl.RGB32F;
case 'fp16': return gl.RGB16F;
case 'int': return gl.RGB32I;
}
case 'rgba':
switch (type) {
case 'ubyte': return gl.RGBA;
case 'float': return gl.RGBA32F;
case 'fp16': return gl.RGBA16F;
case 'int': return gl.RGBA32I;
}
case 'depth':
switch (type) {
case 'ushort': return gl.DEPTH_COMPONENT16;
case 'float': return gl.DEPTH_COMPONENT32F;
}
}
}
return getFormat(gl, format, type);
}
exports.getInternalFormat = getInternalFormat;
function getByteCount(format, type, width, height, depth) {
var bpe = getFormatSize(format) * getTypeSize(type);
return bpe * width * height * (depth || 1);
}
function getFormatSize(format) {
switch (format) {
case 'alpha': return 1;
case 'rgb': return 3;
case 'rgba': return 4;
case 'depth': return 4;
}
}
function getTypeSize(type) {
switch (type) {
case 'ubyte': return 1;
case 'ushort': return 2;
case 'float': return 4;
case 'fp16': return 2;
case 'int': return 4;
}
}
function getType(gl, extensions, type) {
switch (type) {
case 'ubyte': return gl.UNSIGNED_BYTE;
case 'ushort': return gl.UNSIGNED_SHORT;
case 'float': return gl.FLOAT;
case 'fp16':
if (extensions.textureHalfFloat)
return extensions.textureHalfFloat.HALF_FLOAT;
else
throw new Error('extension "texture_half_float" unavailable');
case 'int':
if ((0, compat_1.isWebGL2)(gl))
return gl.INT;
else
throw new Error('texture type "int" requires webgl2');
}
}
exports.getType = getType;
function getFilter(gl, type) {
switch (type) {
case 'nearest': return gl.NEAREST;
case 'linear': return gl.LINEAR;
}
}
exports.getFilter = getFilter;
function getAttachment(gl, extensions, attachment) {
switch (attachment) {
case 'depth': return gl.DEPTH_ATTACHMENT;
case 'stencil': return gl.STENCIL_ATTACHMENT;
case 'color0':
case 0: return gl.COLOR_ATTACHMENT0;
}
if (extensions.drawBuffers) {
switch (attachment) {
case 'color1':
case 1: return extensions.drawBuffers.COLOR_ATTACHMENT1;
case 'color2':
case 2: return extensions.drawBuffers.COLOR_ATTACHMENT2;
case 'color3':
case 3: return extensions.drawBuffers.COLOR_ATTACHMENT3;
case 'color4':
case 4: return extensions.drawBuffers.COLOR_ATTACHMENT4;
case 'color5':
case 5: return extensions.drawBuffers.COLOR_ATTACHMENT5;
case 'color6':
case 6: return extensions.drawBuffers.COLOR_ATTACHMENT6;
case 'color7':
case 7: return extensions.drawBuffers.COLOR_ATTACHMENT7;
}
}
throw new Error('unknown texture attachment');
}
exports.getAttachment = getAttachment;
function isImage(x) {
return typeof HTMLImageElement !== 'undefined' && (x instanceof HTMLImageElement);
}
function isTexture2d(x, target, gl) {
return target === gl.TEXTURE_2D;
}
function isTexture3d(x, target, gl) {
return target === gl.TEXTURE_3D;
}
function getTexture(gl) {
var texture = gl.createTexture();
if (texture === null) {
throw new Error('Could not create WebGL texture');
}
return texture;
}
function createTexture(gl, extensions, kind, _format, _type, _filter) {
var id = getNextTextureId();
var texture = getTexture(gl);
// check texture kind and type compatability
if ((kind.endsWith('float32') && _type !== 'float') ||
(kind.endsWith('float16') && _type !== 'fp16') ||
(kind.endsWith('uint8') && _type !== 'ubyte') ||
(kind.endsWith('int32') && _type !== 'int') ||
(kind.endsWith('depth') && _type !== 'ushort' && _type !== 'float')) {
throw new Error("texture kind '" + kind + "' and type '" + _type + "' are incompatible");
}
var target = getTarget(gl, kind);
var filter = getFilter(gl, _filter);
var format = getFormat(gl, _format, _type);
var internalFormat = getInternalFormat(gl, _format, _type);
var type = getType(gl, extensions, _type);
function init() {
gl.bindTexture(target, texture);
gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filter);
gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filter);
// clamp-to-edge needed for non-power-of-two textures in webgl
gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.bindTexture(target, null);
}
init();
var width = 0, height = 0, depth = 0;
var loadedData;
var destroyed = false;
function define(_width, _height, _depth) {
if (width === _width && height === _height && depth === (_depth || 0))
return;
width = _width, height = _height, depth = _depth || 0;
gl.bindTexture(target, texture);
if (target === gl.TEXTURE_2D) {
gl.texImage2D(target, 0, internalFormat, width, height, 0, format, type, null);
}
else if ((0, compat_1.isWebGL2)(gl) && target === gl.TEXTURE_3D && depth !== undefined) {
gl.texImage3D(target, 0, internalFormat, width, height, depth, 0, format, type, null);
}
else {
throw new Error('unknown texture target');
}
}
function load(data, sub) {
if (sub === void 0) { sub = false; }
gl.bindTexture(target, texture);
// unpack alignment of 1 since we use textures only for data
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
if (isImage(data)) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, type, data);
}
else if (isTexture2d(data, target, gl)) {
var _filter_1 = data.filter ? getFilter(gl, data.filter) : filter;
gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, _filter_1);
gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, _filter_1);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY);
if (sub) {
gl.texSubImage2D(target, 0, 0, 0, data.width, data.height, format, type, data.array);
}
else {
width = data.width, height = data.height;
gl.texImage2D(target, 0, internalFormat, width, height, 0, format, type, data.array);
}
}
else if ((0, compat_1.isWebGL2)(gl) && isTexture3d(data, target, gl)) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
if (sub) {
gl.texSubImage3D(target, 0, 0, 0, 0, data.width, data.height, data.depth, format, type, data.array);
}
else {
width = data.width, height = data.height, depth = data.depth;
gl.texImage3D(target, 0, internalFormat, width, height, depth, 0, format, type, data.array);
}
}
else {
throw new Error('unknown texture target');
}
gl.bindTexture(target, null);
loadedData = data;
}
function attachFramebuffer(framebuffer, attachment, layer) {
framebuffer.bind();
if (target === gl.TEXTURE_2D) {
gl.framebufferTexture2D(gl.FRAMEBUFFER, getAttachment(gl, extensions, attachment), gl.TEXTURE_2D, texture, 0);
}
else if ((0, compat_1.isWebGL2)(gl) && target === gl.TEXTURE_3D) {
if (layer === undefined)
throw new Error('need `layer` to attach 3D texture');
gl.framebufferTextureLayer(gl.FRAMEBUFFER, getAttachment(gl, extensions, attachment), texture, 0, layer);
}
else {
throw new Error('unknown/unsupported texture target');
}
}
return {
id: id,
target: target,
format: format,
internalFormat: internalFormat,
type: type,
filter: filter,
getWidth: function () { return width; },
getHeight: function () { return height; },
getDepth: function () { return depth; },
getByteCount: function () { return getByteCount(_format, _type, width, height, depth); },
define: define,
load: load,
bind: function (id) {
gl.activeTexture(gl.TEXTURE0 + id);
gl.bindTexture(target, texture);
},
unbind: function (id) {
gl.activeTexture(gl.TEXTURE0 + id);
gl.bindTexture(target, null);
},
attachFramebuffer: attachFramebuffer,
detachFramebuffer: function (framebuffer, attachment) {
framebuffer.bind();
if (target === gl.TEXTURE_2D) {
gl.framebufferTexture2D(gl.FRAMEBUFFER, getAttachment(gl, extensions, attachment), gl.TEXTURE_2D, null, 0);
}
else if ((0, compat_1.isWebGL2)(gl) && target === gl.TEXTURE_3D) {
gl.framebufferTextureLayer(gl.FRAMEBUFFER, getAttachment(gl, extensions, attachment), null, 0, 0);
}
else {
throw new Error('unknown texture target');
}
},
reset: function () {
texture = getTexture(gl);
init();
var _a = [width, height, depth], _width = _a[0], _height = _a[1], _depth = _a[2];
width = 0, height = 0, depth = 0; // set to zero to trigger resize
define(_width, _height, _depth);
if (loadedData)
load(loadedData);
},
destroy: function () {
if (destroyed)
return;
gl.deleteTexture(texture);
destroyed = true;
}
};
}
exports.createTexture = createTexture;
function createTextures(ctx, schema, values) {
var resources = ctx.resources;
var textures = [];
Object.keys(schema).forEach(function (k) {
var spec = schema[k];
if (spec.type === 'texture') {
var value = values[k];
if (value) {
if (spec.kind === 'texture') {
textures[textures.length] = [k, value.ref.value];
}
else {
var texture = resources.texture(spec.kind, spec.format, spec.dataType, spec.filter);
texture.load(value.ref.value);
textures[textures.length] = [k, texture];
}
}
}
});
return textures;
}
exports.createTextures = createTextures;
/**
* Loads an image from a url to a textures and triggers update asynchronously.
* This will not work on node.js without a polyfill for `HTMLImageElement`.
*/
function loadImageTexture(src, cell, texture) {
var img = new Image();
img.onload = function () {
texture.load(img);
mol_util_1.ValueCell.update(cell, texture);
};
img.src = src;
}
exports.loadImageTexture = loadImageTexture;
//
function createNullTexture(gl) {
var _a;
var target = (_a = gl === null || gl === void 0 ? void 0 : gl.TEXTURE_2D) !== null && _a !== void 0 ? _a : 3553;
return {
id: getNextTextureId(),
target: target,
format: 0,
internalFormat: 0,
type: 0,
filter: 0,
getWidth: function () { return 0; },
getHeight: function () { return 0; },
getDepth: function () { return 0; },
getByteCount: function () { return 0; },
define: function () { },
load: function () { },
bind: function (id) {
if (gl) {
gl.activeTexture(gl.TEXTURE0 + id);
gl.bindTexture(target, null);
}
},
unbind: function (id) {
if (gl) {
gl.activeTexture(gl.TEXTURE0 + id);
gl.bindTexture(target, null);
}
},
attachFramebuffer: function () { },
detachFramebuffer: function () { },
reset: function () { },
destroy: function () { },
};
}
exports.createNullTexture = createNullTexture;
//# sourceMappingURL=texture.js.map