UNPKG

molstar

Version:

A comprehensive macromolecular library.

398 lines 15.6 kB
"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