UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

779 lines (625 loc) 35.1 kB
import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import macro from '../../macro.js'; import { registerViewConstructor } from '../Core/RenderWindow.js'; import vtkForwardPass from './ForwardPass.js'; import vtkViewNodeFactory from './ViewNodeFactory.js'; import vtkRenderPass from '../SceneGraph/RenderPass.js'; import vtkShaderCache from './ShaderCache.js'; import vtkRenderWindowViewNode from '../SceneGraph/RenderWindowViewNode.js'; import vtkTextureUnitManager from './TextureUnitManager.js'; import vtkHardwareSelector from './HardwareSelector.js'; import { VtkDataTypes } from '../../Common/Core/DataArray/Constants.js'; var vtkDebugMacro = macro.vtkDebugMacro, vtkErrorMacro = macro.vtkErrorMacro; var IS_CHROME = navigator.userAgent.indexOf('Chrome') !== -1; var SCREENSHOT_PLACEHOLDER = { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }; function checkRenderTargetSupport(gl, format, type) { // create temporary frame buffer and texture var framebuffer = gl.createFramebuffer(); var texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, format, 2, 2, 0, format, type, null); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); // check frame buffer status var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); // clean up gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.bindTexture(gl.TEXTURE_2D, null); return status === gl.FRAMEBUFFER_COMPLETE; } // ---------------------------------------------------------------------------- // Monitor the usage of GL context across vtkOpenGLRenderWindow instances // ---------------------------------------------------------------------------- var GL_CONTEXT_COUNT = 0; var GL_CONTEXT_LISTENERS = []; function createGLContext() { GL_CONTEXT_COUNT++; GL_CONTEXT_LISTENERS.forEach(function (cb) { return cb(GL_CONTEXT_COUNT); }); } function deleteGLContext() { GL_CONTEXT_COUNT--; GL_CONTEXT_LISTENERS.forEach(function (cb) { return cb(GL_CONTEXT_COUNT); }); } function pushMonitorGLContextCount(cb) { GL_CONTEXT_LISTENERS.push(cb); } function popMonitorGLContextCount(cb) { return GL_CONTEXT_LISTENERS.pop(); } // ---------------------------------------------------------------------------- // vtkOpenGLRenderWindow methods // ---------------------------------------------------------------------------- function vtkOpenGLRenderWindow(publicAPI, model) { // Set our className model.classHierarchy.push('vtkOpenGLRenderWindow'); publicAPI.getViewNodeFactory = function () { return model.myFactory; }; // Auto update style var previousSize = [0, 0]; function updateWindow() { // Canvas size if (model.renderable) { if (model.size[0] !== previousSize[0] || model.size[1] !== previousSize[1]) { previousSize[0] = model.size[0]; previousSize[1] = model.size[1]; model.canvas.setAttribute('width', model.size[0]); model.canvas.setAttribute('height', model.size[1]); } } // ImageStream size if (model.viewStream) { // If same size that's a NoOp model.viewStream.setSize(model.size[0], model.size[1]); } // Offscreen ? model.canvas.style.display = model.useOffScreen ? 'none' : 'block'; // Cursor type if (model.el) { model.el.style.cursor = model.cursorVisibility ? model.cursor : 'none'; } // Invalidate cached DOM container size model.containerSize = null; } publicAPI.onModified(updateWindow); // Builds myself. publicAPI.buildPass = function (prepass) { if (prepass) { if (!model.renderable) { return; } publicAPI.prepareNodes(); publicAPI.addMissingNodes(model.renderable.getRenderersByReference()); publicAPI.removeUnusedNodes(); publicAPI.initialize(); model.children.forEach(function (child) { child.setOpenGLRenderWindow(publicAPI); }); } }; publicAPI.initialize = function () { if (!model.initialized) { model.context = publicAPI.get3DContext(); model.textureUnitManager = vtkTextureUnitManager.newInstance(); model.textureUnitManager.setContext(model.context); model.shaderCache.setContext(model.context); // initialize blending for transparency var gl = model.context; gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); gl.depthFunc(gl.LEQUAL); gl.enable(gl.BLEND); model.initialized = true; } }; publicAPI.makeCurrent = function () { model.context.makeCurrent(); }; publicAPI.setContainer = function (el) { if (model.el && model.el !== el) { if (model.canvas.parentNode !== model.el) { vtkErrorMacro('Error: canvas parent node does not match container'); } // Remove canvas from previous container model.el.removeChild(model.canvas); // If the renderer has previously added // a background image, remove it from the DOM. if (model.el.contains(model.bgImage)) { model.el.removeChild(model.bgImage); } } if (model.el !== el) { model.el = el; if (model.el) { model.el.appendChild(model.canvas); // If the renderer is set to use a background // image, attach it to the DOM. if (model.useBackgroundImage) { model.el.appendChild(model.bgImage); } } // Trigger modified() publicAPI.modified(); } }; publicAPI.getContainer = function () { return model.el; }; publicAPI.getContainerSize = function () { if (!model.containerSize && model.el) { var _model$el$getBounding = model.el.getBoundingClientRect(), width = _model$el$getBounding.width, height = _model$el$getBounding.height; model.containerSize = [width, height]; } return model.containerSize || model.size; }; publicAPI.getFramebufferSize = function () { if (model.activeFramebuffer) { return model.activeFramebuffer.getSize(); } return model.size; }; publicAPI.getPixelData = function (x1, y1, x2, y2) { var pixels = new Uint8Array((x2 - x1 + 1) * (y2 - y1 + 1) * 4); model.context.readPixels(x1, y1, x2 - x1 + 1, y2 - y1 + 1, model.context.RGBA, model.context.UNSIGNED_BYTE, pixels); return pixels; }; publicAPI.get3DContext = function () { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { preserveDrawingBuffer: false, depth: true, alpha: true }; var result = null; var webgl2Supported = typeof WebGL2RenderingContext !== 'undefined'; model.webgl2 = false; if (model.defaultToWebgl2 && webgl2Supported) { result = model.canvas.getContext('webgl2', options); if (result) { model.webgl2 = true; vtkDebugMacro('using webgl2'); } } if (!result) { vtkDebugMacro('using webgl1'); result = model.canvas.getContext('webgl', options) || model.canvas.getContext('experimental-webgl', options); } // Do we have webvr support if (navigator.getVRDisplays) { navigator.getVRDisplays().then(function (displays) { if (displays.length > 0) { // take the first display for now model.vrDisplay = displays[0]; // set the clipping ranges model.vrDisplay.depthNear = 0.01; // meters model.vrDisplay.depthFar = 100.0; // meters publicAPI.invokeHaveVRDisplay(); } }); } // prevent default context lost handler model.canvas.addEventListener('webglcontextlost', function (event) { event.preventDefault(); }, false); model.canvas.addEventListener('webglcontextrestored', publicAPI.restoreContext, false); return result; }; publicAPI.startVR = function () { model.oldCanvasSize = model.size.slice(); if (model.vrDisplay.capabilities.canPresent) { model.vrDisplay.requestPresent([{ source: model.canvas }]).then(function () { if (model.el && model.vrDisplay.capabilities.hasExternalDisplay && model.hideCanvasInVR) { model.el.style.display = 'none'; } if (model.queryVRSize) { var leftEye = model.vrDisplay.getEyeParameters('left'); var rightEye = model.vrDisplay.getEyeParameters('right'); var width = Math.floor(leftEye.renderWidth + rightEye.renderWidth); var height = Math.floor(Math.max(leftEye.renderHeight, rightEye.renderHeight)); publicAPI.setSize(width, height); } else { publicAPI.setSize(model.vrResolution); } var ren = model.renderable.getRenderers()[0]; ren.resetCamera(); model.vrFrameData = new VRFrameData(); model.renderable.getInteractor().switchToVRAnimation(); model.vrSceneFrame = model.vrDisplay.requestAnimationFrame(publicAPI.vrRender); // If Broswer is chrome we need to request animation again to canvas update if (IS_CHROME) { model.vrSceneFrame = model.vrDisplay.requestAnimationFrame(publicAPI.vrRender); } }).catch(function () { console.error('failed to requestPresent'); }); } else { vtkErrorMacro('vrDisplay is not connected'); } }; publicAPI.stopVR = function () { model.renderable.getInteractor().returnFromVRAnimation(); model.vrDisplay.exitPresent(); model.vrDisplay.cancelAnimationFrame(model.vrSceneFrame); publicAPI.setSize.apply(publicAPI, _toConsumableArray(model.oldCanvasSize)); if (model.el && model.vrDisplay.capabilities.hasExternalDisplay) { model.el.style.display = 'block'; } var ren = model.renderable.getRenderers()[0]; ren.getActiveCamera().setProjectionMatrix(null); ren.setViewport(0.0, 0, 1.0, 1.0); publicAPI.traverseAllPasses(); }; publicAPI.vrRender = function () { // If not presenting for any reason, we do not submit frame if (!model.vrDisplay.isPresenting) { return; } model.renderable.getInteractor().updateGamepads(model.vrDisplay.displayId); model.vrSceneFrame = model.vrDisplay.requestAnimationFrame(publicAPI.vrRender); model.vrDisplay.getFrameData(model.vrFrameData); // get the first renderer var ren = model.renderable.getRenderers()[0]; // do the left eye ren.setViewport(0, 0, 0.5, 1.0); ren.getActiveCamera().computeViewParametersFromPhysicalMatrix(model.vrFrameData.leftViewMatrix); ren.getActiveCamera().setProjectionMatrix(model.vrFrameData.leftProjectionMatrix); publicAPI.traverseAllPasses(); ren.setViewport(0.5, 0, 1.0, 1.0); ren.getActiveCamera().computeViewParametersFromPhysicalMatrix(model.vrFrameData.rightViewMatrix); ren.getActiveCamera().setProjectionMatrix(model.vrFrameData.rightProjectionMatrix); publicAPI.traverseAllPasses(); model.vrDisplay.submitFrame(); }; publicAPI.restoreContext = function () { var rp = vtkRenderPass.newInstance(); rp.setCurrentOperation('Release'); rp.traverse(publicAPI, null); }; publicAPI.activateTexture = function (texture) { // Only add if it isn't already there var result = model._textureResourceIds.get(texture); if (result !== undefined) { model.context.activeTexture(model.context.TEXTURE0 + result); return; } var activeUnit = publicAPI.getTextureUnitManager().allocate(); if (activeUnit < 0) { vtkErrorMacro('Hardware does not support the number of textures defined.'); return; } model._textureResourceIds.set(texture, activeUnit); model.context.activeTexture(model.context.TEXTURE0 + activeUnit); }; publicAPI.deactivateTexture = function (texture) { // Only deactivate if it isn't already there var result = model._textureResourceIds.get(texture); if (result !== undefined) { publicAPI.getTextureUnitManager().free(result); delete model._textureResourceIds.delete(texture); } }; publicAPI.getTextureUnitForTexture = function (texture) { var result = model._textureResourceIds.get(texture); if (result !== undefined) { return result; } return -1; }; publicAPI.getDefaultTextureInternalFormat = function (vtktype, numComps, useFloat) { if (model.webgl2) { switch (vtktype) { case VtkDataTypes.UNSIGNED_CHAR: switch (numComps) { case 1: return model.context.R8; case 2: return model.context.RG8; case 3: return model.context.RGB8; case 4: default: return model.context.RGBA8; } default: case VtkDataTypes.FLOAT: switch (numComps) { case 1: return model.context.R16F; case 2: return model.context.RG16F; case 3: return model.context.RGB16F; case 4: default: return model.context.RGBA16F; } } } // webgl1 only supports four types switch (numComps) { case 1: return model.context.LUMINANCE; case 2: return model.context.LUMINANCE_ALPHA; case 3: return model.context.RGB; case 4: default: return model.context.RGBA; } }; publicAPI.setBackgroundImage = function (img) { model.bgImage.src = img.src; }; publicAPI.setUseBackgroundImage = function (value) { model.useBackgroundImage = value; // Add or remove the background image from the // DOM as specified. if (model.useBackgroundImage && !model.el.contains(model.bgImage)) { model.el.appendChild(model.bgImage); } else if (!model.useBackgroundImage && model.el.contains(model.bgImage)) { model.el.removeChild(model.bgImage); } }; function getCanvasDataURL() { var format = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : model.imageFormat; // Copy current canvas to not modify the original var temporaryCanvas = document.createElement('canvas'); var temporaryContext = temporaryCanvas.getContext('2d'); temporaryCanvas.width = model.canvas.width; temporaryCanvas.height = model.canvas.height; temporaryContext.drawImage(model.canvas, 0, 0); // Get current client rect to place canvas var mainBoundingClientRect = model.canvas.getBoundingClientRect(); var renderWindow = model.renderable; var renderers = renderWindow.getRenderers(); renderers.forEach(function (renderer) { var viewProps = renderer.getViewProps(); viewProps.forEach(function (viewProp) { // Check if the prop has a container that should have canvas if (viewProp.getContainer) { var container = viewProp.getContainer(); var canvasList = container.getElementsByTagName('canvas'); // Go throughout all canvas and copy it into temporary main canvas for (var i = 0; i < canvasList.length; i++) { var currentCanvas = canvasList[i]; var boundingClientRect = currentCanvas.getBoundingClientRect(); var newXPosition = boundingClientRect.x - mainBoundingClientRect.x; var newYPosition = boundingClientRect.y - mainBoundingClientRect.y; temporaryContext.drawImage(currentCanvas, newXPosition, newYPosition); } } }); }); var screenshot = temporaryCanvas.toDataURL(format); temporaryCanvas.remove(); publicAPI.invokeImageReady(screenshot); } publicAPI.captureNextImage = function () { var format = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'image/png'; var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref$resetCamera = _ref.resetCamera, resetCamera = _ref$resetCamera === void 0 ? false : _ref$resetCamera, _ref$size = _ref.size, size = _ref$size === void 0 ? null : _ref$size, _ref$scale = _ref.scale, scale = _ref$scale === void 0 ? 1 : _ref$scale; if (model.deleted) { return null; } model.imageFormat = format; var previous = model.notifyStartCaptureImage; model.notifyStartCaptureImage = true; model._screenshot = { size: !!size || scale !== 1 ? size || model.size.map(function (val) { return val * scale; }) : null }; return new Promise(function (resolve, reject) { var subscription = publicAPI.onImageReady(function (imageURL) { if (model._screenshot.size === null) { model.notifyStartCaptureImage = previous; subscription.unsubscribe(); if (model._screenshot.placeHolder) { // resize the main canvas back to its original size and show it model.size = model._screenshot.originalSize; // process the resize publicAPI.modified(); // restore the saved camera parameters, if applicable if (model._screenshot.cameras) { model._screenshot.cameras.forEach(function (_ref2) { var restoreParamsFn = _ref2.restoreParamsFn, arg = _ref2.arg; return restoreParamsFn(arg); }); } // Trigger a render at the original size publicAPI.traverseAllPasses(); // Remove and clean up the placeholder, revealing the original model.el.removeChild(model._screenshot.placeHolder); model._screenshot.placeHolder.remove(); model._screenshot = null; } resolve(imageURL); } else { // Create a placeholder image overlay while we resize and render var tmpImg = document.createElement('img'); tmpImg.style = SCREENSHOT_PLACEHOLDER; tmpImg.src = imageURL; model._screenshot.placeHolder = model.el.appendChild(tmpImg); // hide the main canvas model.canvas.style.display = 'none'; // remember the main canvas original size, then resize it model._screenshot.originalSize = model.size; model.size = model._screenshot.size; model._screenshot.size = null; // process the resize publicAPI.modified(); if (resetCamera) { // If resetCamera was requested, we first save camera parameters // from all the renderers, so we can restore them later model._screenshot.cameras = model.renderable.getRenderers().map(function (renderer) { var camera = renderer.getActiveCamera(); var params = camera.get('focalPoint', 'position', 'parallelScale'); return { resetCameraFn: renderer.resetCamera, restoreParamsFn: camera.set, // "clone" the params so we don't keep refs to properties arg: JSON.parse(JSON.stringify(params)) }; }); // Perform the resetCamera() on each renderer only after capturing // the params from all active cameras, in case there happen to be // linked cameras among the renderers. model._screenshot.cameras.forEach(function (_ref3) { var resetCameraFn = _ref3.resetCameraFn; return resetCameraFn(); }); } // Trigger a render at the custom size publicAPI.traverseAllPasses(); } }); }); }; publicAPI.getGLInformations = function () { var gl = publicAPI.get3DContext(); var glTextureFloat = gl.getExtension('OES_texture_float'); var glTextureHalfFloat = gl.getExtension('OES_texture_half_float'); var glDebugRendererInfo = gl.getExtension('WEBGL_debug_renderer_info'); var glDrawBuffers = gl.getExtension('WEBGL_draw_buffers'); var glAnisotropic = gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); var params = [['Max Vertex Attributes', 'MAX_VERTEX_ATTRIBS', gl.getParameter(gl.MAX_VERTEX_ATTRIBS)], ['Max Varying Vectors', 'MAX_VARYING_VECTORS', gl.getParameter(gl.MAX_VARYING_VECTORS)], ['Max Vertex Uniform Vectors', 'MAX_VERTEX_UNIFORM_VECTORS', gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS)], ['Max Fragment Uniform Vectors', 'MAX_FRAGMENT_UNIFORM_VECTORS', gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS)], ['Max Fragment Texture Image Units', 'MAX_TEXTURE_IMAGE_UNITS', gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)], ['Max Vertex Texture Image Units', 'MAX_VERTEX_TEXTURE_IMAGE_UNITS', gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS)], ['Max Combined Texture Image Units', 'MAX_COMBINED_TEXTURE_IMAGE_UNITS', gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS)], ['Max 2D Texture Size', 'MAX_TEXTURE_SIZE', gl.getParameter(gl.MAX_TEXTURE_SIZE)], ['Max Cube Texture Size', 'MAX_CUBE_MAP_TEXTURE_SIZE', gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE)], ['Max Texture Anisotropy', 'MAX_TEXTURE_MAX_ANISOTROPY_EXT', glAnisotropic && gl.getParameter(glAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT)], ['Point Size Range', 'ALIASED_POINT_SIZE_RANGE', gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE).join(' - ')], ['Line Width Range', 'ALIASED_LINE_WIDTH_RANGE', gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE).join(' - ')], ['Max Viewport Dimensions', 'MAX_VIEWPORT_DIMS', gl.getParameter(gl.MAX_VIEWPORT_DIMS).join(' - ')], ['Max Renderbuffer Size', 'MAX_RENDERBUFFER_SIZE', gl.getParameter(gl.MAX_RENDERBUFFER_SIZE)], ['Framebuffer Red Bits', 'RED_BITS', gl.getParameter(gl.RED_BITS)], ['Framebuffer Green Bits', 'GREEN_BITS', gl.getParameter(gl.GREEN_BITS)], ['Framebuffer Blue Bits', 'BLUE_BITS', gl.getParameter(gl.BLUE_BITS)], ['Framebuffer Alpha Bits', 'ALPHA_BITS', gl.getParameter(gl.ALPHA_BITS)], ['Framebuffer Depth Bits', 'DEPTH_BITS', gl.getParameter(gl.DEPTH_BITS)], ['Framebuffer Stencil Bits', 'STENCIL_BITS', gl.getParameter(gl.STENCIL_BITS)], ['Framebuffer Subpixel Bits', 'SUBPIXEL_BITS', gl.getParameter(gl.SUBPIXEL_BITS)], ['MSAA Samples', 'SAMPLES', gl.getParameter(gl.SAMPLES)], ['MSAA Sample Buffers', 'SAMPLE_BUFFERS', gl.getParameter(gl.SAMPLE_BUFFERS)], ['Supported Formats for UByte Render Targets ', 'UNSIGNED_BYTE RENDER TARGET FORMATS', [glTextureFloat && checkRenderTargetSupport(gl, gl.RGBA, gl.UNSIGNED_BYTE) ? 'RGBA' : '', glTextureFloat && checkRenderTargetSupport(gl, gl.RGB, gl.UNSIGNED_BYTE) ? 'RGB' : '', glTextureFloat && checkRenderTargetSupport(gl, gl.LUMINANCE, gl.UNSIGNED_BYTE) ? 'LUMINANCE' : '', glTextureFloat && checkRenderTargetSupport(gl, gl.ALPHA, gl.UNSIGNED_BYTE) ? 'ALPHA' : '', glTextureFloat && checkRenderTargetSupport(gl, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE) ? 'LUMINANCE_ALPHA' : ''].join(' ')], ['Supported Formats for Half Float Render Targets', 'HALF FLOAT RENDER TARGET FORMATS', [glTextureHalfFloat && checkRenderTargetSupport(gl, gl.RGBA, glTextureHalfFloat.HALF_FLOAT_OES) ? 'RGBA' : '', glTextureHalfFloat && checkRenderTargetSupport(gl, gl.RGB, glTextureHalfFloat.HALF_FLOAT_OES) ? 'RGB' : '', glTextureHalfFloat && checkRenderTargetSupport(gl, gl.LUMINANCE, glTextureHalfFloat.HALF_FLOAT_OES) ? 'LUMINANCE' : '', glTextureHalfFloat && checkRenderTargetSupport(gl, gl.ALPHA, glTextureHalfFloat.HALF_FLOAT_OES) ? 'ALPHA' : '', glTextureHalfFloat && checkRenderTargetSupport(gl, gl.LUMINANCE_ALPHA, glTextureHalfFloat.HALF_FLOAT_OES) ? 'LUMINANCE_ALPHA' : ''].join(' ')], ['Supported Formats for Full Float Render Targets', 'FLOAT RENDER TARGET FORMATS', [glTextureFloat && checkRenderTargetSupport(gl, gl.RGBA, gl.FLOAT) ? 'RGBA' : '', glTextureFloat && checkRenderTargetSupport(gl, gl.RGB, gl.FLOAT) ? 'RGB' : '', glTextureFloat && checkRenderTargetSupport(gl, gl.LUMINANCE, gl.FLOAT) ? 'LUMINANCE' : '', glTextureFloat && checkRenderTargetSupport(gl, gl.ALPHA, gl.FLOAT) ? 'ALPHA' : '', glTextureFloat && checkRenderTargetSupport(gl, gl.LUMINANCE_ALPHA, gl.FLOAT) ? 'LUMINANCE_ALPHA' : ''].join(' ')], ['Max Multiple Render Targets Buffers', 'MAX_DRAW_BUFFERS_WEBGL', glDrawBuffers ? gl.getParameter(glDrawBuffers.MAX_DRAW_BUFFERS_WEBGL) : 0], ['High Float Precision in Vertex Shader', 'HIGH_FLOAT VERTEX_SHADER', [gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).rangeMax, '</sup>)'].join('')], ['Medium Float Precision in Vertex Shader', 'MEDIUM_FLOAT VERTEX_SHADER', [gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).rangeMax, '</sup>)'].join('')], ['Low Float Precision in Vertex Shader', 'LOW_FLOAT VERTEX_SHADER', [gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT).rangeMax, '</sup>)'].join('')], ['High Float Precision in Fragment Shader', 'HIGH_FLOAT FRAGMENT_SHADER', [gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).rangeMax, '</sup>)'].join('')], ['Medium Float Precision in Fragment Shader', 'MEDIUM_FLOAT FRAGMENT_SHADER', [gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).rangeMax, '</sup>)'].join('')], ['Low Float Precision in Fragment Shader', 'LOW_FLOAT FRAGMENT_SHADER', [gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT).rangeMax, '</sup>)'].join('')], ['High Int Precision in Vertex Shader', 'HIGH_INT VERTEX_SHADER', [gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT).rangeMax, '</sup>)'].join('')], ['Medium Int Precision in Vertex Shader', 'MEDIUM_INT VERTEX_SHADER', [gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT).rangeMax, '</sup>)'].join('')], ['Low Int Precision in Vertex Shader', 'LOW_INT VERTEX_SHADER', [gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT).rangeMax, '</sup>)'].join('')], ['High Int Precision in Fragment Shader', 'HIGH_INT FRAGMENT_SHADER', [gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT).rangeMax, '</sup>)'].join('')], ['Medium Int Precision in Fragment Shader', 'MEDIUM_INT FRAGMENT_SHADER', [gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT).rangeMax, '</sup>)'].join('')], ['Low Int Precision in Fragment Shader', 'LOW_INT FRAGMENT_SHADER', [gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT).precision, ' (-2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT).rangeMin, '</sup> - 2<sup>', gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT).rangeMax, '</sup>)'].join('')], ['Supported Extensions', 'EXTENSIONS', gl.getSupportedExtensions().join('<br/>\t\t\t\t\t ')], ['WebGL Renderer', 'RENDERER', gl.getParameter(gl.RENDERER)], ['WebGL Vendor', 'VENDOR', gl.getParameter(gl.VENDOR)], ['WebGL Version', 'VERSION', gl.getParameter(gl.VERSION)], ['Shading Language Version', 'SHADING_LANGUAGE_VERSION', gl.getParameter(gl.SHADING_LANGUAGE_VERSION)], ['Unmasked Renderer', 'UNMASKED_RENDERER', glDebugRendererInfo && gl.getParameter(glDebugRendererInfo.UNMASKED_RENDERER_WEBGL)], ['Unmasked Vendor', 'UNMASKED_VENDOR', glDebugRendererInfo && gl.getParameter(glDebugRendererInfo.UNMASKED_VENDOR_WEBGL)], ['WebGL Version', 'WEBGL_VERSION', model.webgl2 ? 2 : 1]]; var result = {}; while (params.length) { var _params$pop = params.pop(), _params$pop2 = _slicedToArray(_params$pop, 3), label = _params$pop2[0], key = _params$pop2[1], value = _params$pop2[2]; if (key) { result[key] = { label: label, value: value }; } } return result; }; publicAPI.traverseAllPasses = function () { if (model.renderPasses) { for (var index = 0; index < model.renderPasses.length; ++index) { model.renderPasses[index].traverse(publicAPI, null); } } if (model.notifyStartCaptureImage) { getCanvasDataURL(); } }; publicAPI.disableDepthMask = function () { if (model.depthMaskEnabled) { model.context.depthMask(false); model.depthMaskEnabled = false; } }; publicAPI.enableDepthMask = function () { if (!model.depthMaskEnabled) { model.context.depthMask(true); model.depthMaskEnabled = true; } }; publicAPI.disableCullFace = function () { if (model.cullFaceEnabled) { model.context.disable(model.context.CULL_FACE); model.cullFaceEnabled = false; } }; publicAPI.enableCullFace = function () { if (!model.cullFaceEnabled) { model.context.enable(model.context.CULL_FACE); model.cullFaceEnabled = true; } }; publicAPI.setViewStream = function (stream) { if (model.viewStream === stream) { return false; } if (model.subscription) { model.subscription.unsubscribe(); model.subscription = null; } model.viewStream = stream; if (model.viewStream) { // Force background to be transparent + render var mainRenderer = model.renderable.getRenderers()[0]; mainRenderer.getBackgroundByReference()[3] = 0; // Enable display of the background image publicAPI.setUseBackgroundImage(true); // Bind to remote stream model.subscription = model.viewStream.onImageReady(function (e) { return publicAPI.setBackgroundImage(e.image); }); model.viewStream.setSize(model.size[0], model.size[1]); model.viewStream.invalidateCache(); model.viewStream.render(); publicAPI.modified(); } return true; }; publicAPI.delete = macro.chain(publicAPI.delete, publicAPI.setViewStream, deleteGLContext); } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { cullFaceEnabled: false, depthMaskEnabled: true, shaderCache: null, initialized: false, context: null, canvas: null, cursorVisibility: true, cursor: 'pointer', textureUnitManager: null, textureResourceIds: null, containerSize: null, renderPasses: [], notifyStartCaptureImage: false, webgl2: false, defaultToWebgl2: true, // attempt webgl2 on by default vrResolution: [2160, 1200], queryVRSize: false, hideCanvasInVR: true, activeFramebuffer: null, vrDisplay: null, imageFormat: 'image/png', useOffScreen: false, useBackgroundImage: false }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkRenderWindowViewNode.extend(publicAPI, model, initialValues); // Create internal instances model.canvas = document.createElement('canvas'); model.canvas.style.width = '100%'; createGLContext(); if (!model.selector) { model.selector = vtkHardwareSelector.newInstance(); model.selector.setOpenGLRenderWindow(publicAPI); } // Create internal bgImage model.bgImage = new Image(); model.bgImage.style.position = 'absolute'; model.bgImage.style.left = '0'; model.bgImage.style.top = '0'; model.bgImage.style.width = '100%'; model.bgImage.style.height = '100%'; model.bgImage.style.zIndex = '-1'; model._textureResourceIds = new Map(); model.myFactory = vtkViewNodeFactory.newInstance(); /* eslint-disable no-use-before-define */ model.myFactory.registerOverride('vtkRenderWindow', newInstance); /* eslint-enable no-use-before-define */ model.shaderCache = vtkShaderCache.newInstance(); model.shaderCache.setOpenGLRenderWindow(publicAPI); // setup default forward pass rendering model.renderPasses[0] = vtkForwardPass.newInstance(); macro.event(publicAPI, model, 'imageReady'); macro.event(publicAPI, model, 'haveVRDisplay'); // Build VTK API macro.get(publicAPI, model, ['shaderCache', 'textureUnitManager', 'webgl2', 'vrDisplay', 'useBackgroundImage']); macro.setGet(publicAPI, model, ['initialized', 'context', 'canvas', 'renderPasses', 'notifyStartCaptureImage', 'defaultToWebgl2', 'cursor', 'queryVRSize', 'hideCanvasInVR', 'useOffScreen', // might want to make this not call modified as // we change the active framebuffer a lot. Or maybe // only mark modified if the size or depth // of the buffer has changed 'activeFramebuffer']); macro.setGetArray(publicAPI, model, ['size', 'vrResolution'], 2); // Object methods vtkOpenGLRenderWindow(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkOpenGLRenderWindow'); // ---------------------------------------------------------------------------- // Register API specific RenderWindow implementation // ---------------------------------------------------------------------------- registerViewConstructor('WebGL', newInstance); // ---------------------------------------------------------------------------- var vtkRenderWindow = { newInstance: newInstance, extend: extend, pushMonitorGLContextCount: pushMonitorGLContextCount, popMonitorGLContextCount: popMonitorGLContextCount }; export default vtkRenderWindow; export { extend, newInstance, popMonitorGLContextCount, pushMonitorGLContextCount };