import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants'; import macro from 'vtk.js/Sources/macros'; import { registerViewConstructor } from 'vtk.js/Sources/Rendering/Core/RenderWindow'; import vtkForwardPass from 'vtk.js/Sources/Rendering/OpenGL/ForwardPass'; import vtkOpenGLHardwareSelector from 'vtk.js/Sources/Rendering/OpenGL/HardwareSelector'; import vtkShaderCache from 'vtk.js/Sources/Rendering/OpenGL/ShaderCache'; import vtkOpenGLTextureUnitManager from 'vtk.js/Sources/Rendering/OpenGL/TextureUnitManager'; import vtkOpenGLViewNodeFactory from 'vtk.js/Sources/Rendering/OpenGL/ViewNodeFactory'; import vtkRenderPass from 'vtk.js/Sources/Rendering/SceneGraph/RenderPass'; import vtkRenderWindowViewNode from 'vtk.js/Sources/Rendering/SceneGraph/RenderWindowViewNode'; import Constants from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow/Constants'; import { createContextProxyHandler, GET_UNDERLYING_CONTEXT, } from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow/ContextProxy';
const { vtkDebugMacro, vtkErrorMacro } = macro; const { XrSessionTypes } = Constants;
const SCREENSHOT_PLACEHOLDER = { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', };
const DEFAULT_RESET_FACTORS = { rescaleFactor: 0.25, translateZ: -1.5, };
function checkRenderTargetSupport(gl, format, type) { const framebuffer = gl.createFramebuffer(); const 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 );
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.bindTexture(gl.TEXTURE_2D, null);
return status === gl.FRAMEBUFFER_COMPLETE; }
let GL_CONTEXT_COUNT = 0; const GL_CONTEXT_LISTENERS = [];
function createGLContext() { GL_CONTEXT_COUNT++; GL_CONTEXT_LISTENERS.forEach((cb) => cb(GL_CONTEXT_COUNT)); }
function deleteGLContext() { GL_CONTEXT_COUNT--; GL_CONTEXT_LISTENERS.forEach((cb) => cb(GL_CONTEXT_COUNT)); }
export function pushMonitorGLContextCount(cb) { GL_CONTEXT_LISTENERS.push(cb); }
export function popMonitorGLContextCount(cb) { return GL_CONTEXT_LISTENERS.pop(); }
function _preventDefault(e) { e.preventDefault(); }
function vtkOpenGLRenderWindow(publicAPI, model) { model.classHierarchy.push('vtkOpenGLRenderWindow');
const cachingContextHandler = createContextProxyHandler();
publicAPI.getViewNodeFactory = () => model.myFactory;
model.canvas.addEventListener('webglcontextlost', _preventDefault, false);
model.canvas.addEventListener( 'webglcontextrestored', publicAPI.restoreContext, false );
const isImmersiveVrSupported = navigator.xr !== undefined && navigator.xr.isSessionSupported('immersive-vr');
const previousSize = [0, 0]; function updateWindow() { 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]); } }
if (model.viewStream) { model.viewStream.setSize(model.size[0], model.size[1]); }
model.canvas.style.display = model.useOffScreen ? 'none' : 'block';
if (model.el) { model.el.style.cursor = model.cursorVisibility ? model.cursor : 'none'; }
model.containerSize = null; } publicAPI.onModified(updateWindow);
publicAPI.buildPass = (prepass) => { if (prepass) { if (!model.renderable) { return; }
publicAPI.prepareNodes(); publicAPI.addMissingNodes(model.renderable.getRenderersByReference()); publicAPI.removeUnusedNodes();
publicAPI.initialize(); model.children.forEach((child) => { child.setOpenGLRenderWindow(publicAPI); }); } };
publicAPI.initialize = () => { if (!model.initialized) { model.context = publicAPI.get3DContext(); model.textureUnitManager = vtkOpenGLTextureUnitManager.newInstance(); model.textureUnitManager.setContext(model.context); model.shaderCache.setContext(model.context); const 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 = () => { model.context.makeCurrent(); };
publicAPI.setContainer = (el) => { if (model.el && model.el !== el) { if (model.canvas.parentNode !== model.el) { vtkErrorMacro('Error: canvas parent node does not match container'); }
model.el.removeChild(model.canvas);
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 (model.useBackgroundImage) { model.el.appendChild(model.bgImage); } }
publicAPI.modified(); } };
publicAPI.getContainer = () => model.el;
publicAPI.getContainerSize = () => { if (!model.containerSize && model.el) { const { width, height } = model.el.getBoundingClientRect(); model.containerSize = [width, height]; } return model.containerSize || model.size; };
publicAPI.getFramebufferSize = () => { if (model.activeFramebuffer) { return model.activeFramebuffer.getSize(); } return model.size; };
publicAPI.getPixelData = (x1, y1, x2, y2) => { const 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 = ( options = { preserveDrawingBuffer: false, depth: true, alpha: true, powerPreference: 'high-performance', } ) => { let result = null;
if (isImmersiveVrSupported) { publicAPI.invokeHaveVRDisplay(); }
const 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); }
return new Proxy(result, cachingContextHandler); };
publicAPI.startXR = (xrSessionType) => { if (navigator.xr === undefined) { throw new Error('WebXR is not available'); }
model.xrSessionType = xrSessionType !== undefined ? xrSessionType : XrSessionTypes.HmdVR; const isXrSessionAR = [ XrSessionTypes.HmdAR, XrSessionTypes.MobileAR, ].includes(model.xrSessionType); const sessionType = isXrSessionAR ? 'immersive-ar' : 'immersive-vr'; if (!navigator.xr.isSessionSupported(sessionType)) { if (isXrSessionAR) { throw new Error('Device does not support AR session'); } else { throw new Error('VR display is not available'); } } if (model.xrSession === null) { navigator.xr.requestSession(sessionType).then(publicAPI.enterXR, () => { throw new Error('Failed to create XR session!'); }); } else { throw new Error('XR Session already exists!'); } };
publicAPI.enterXR = async (xrSession) => { model.xrSession = xrSession; model.oldCanvasSize = model.size.slice();
if (model.xrSession !== null) { const gl = publicAPI.get3DContext(); await gl.makeXRCompatible();
const glLayer = new global.XRWebGLLayer( model.xrSession, gl[GET_UNDERLYING_CONTEXT]() ); publicAPI.setSize(glLayer.framebufferWidth, glLayer.framebufferHeight);
model.xrSession.updateRenderState({ baseLayer: glLayer, });
model.xrSession.requestReferenceSpace('local').then((refSpace) => { model.xrReferenceSpace = refSpace; });
const isXrSessionAR = [ XrSessionTypes.HmdAR, XrSessionTypes.MobileAR, ].includes(model.xrSessionType); if (isXrSessionAR) { const ren = model.renderable.getRenderers()[0]; model.preXrSessionBackground = ren.getBackground(); ren.setBackground([0, 0, 0, 0]); }
publicAPI.resetXRScene();
model.renderable.getInteractor().switchToXRAnimation(); model.xrSceneFrame = model.xrSession.requestAnimationFrame( publicAPI.xrRender ); } else { throw new Error('Failed to enter XR with a null xrSession.'); } };
publicAPI.resetXRScene = ( rescaleFactor = DEFAULT_RESET_FACTORS.rescaleFactor, translateZ = DEFAULT_RESET_FACTORS.translateZ ) => {
const ren = model.renderable.getRenderers()[0]; ren.resetCamera();
const camera = ren.getActiveCamera(); let physicalScale = camera.getPhysicalScale(); const physicalTranslation = camera.getPhysicalTranslation();
const rescaledTranslateZ = translateZ * physicalScale; physicalScale /= rescaleFactor; physicalTranslation[2] += rescaledTranslateZ;
camera.setPhysicalScale(physicalScale); camera.setPhysicalTranslation(physicalTranslation); camera.setClippingRange(0.1 * physicalScale, 100.0 * physicalScale); };
publicAPI.stopXR = async () => { if (navigator.xr === undefined) { return; }
if (model.xrSession !== null) { model.xrSession.cancelAnimationFrame(model.xrSceneFrame); model.renderable.getInteractor().returnFromXRAnimation(); const gl = publicAPI.get3DContext(); gl.bindFramebuffer(gl.FRAMEBUFFER, null);
await model.xrSession.end().catch((error) => { if (!(error instanceof DOMException)) { throw error; } }); model.xrSession = null; }
if (model.oldCanvasSize !== undefined) { publicAPI.setSize(...model.oldCanvasSize); }
const ren = model.renderable.getRenderers()[0];
if (model.preXrSessionBackground != null) { ren.setBackground(model.preXrSessionBackground); model.preXrSessionBackground = null; }
ren.getActiveCamera().setProjectionMatrix(null); ren.resetCamera();
ren.setViewport(0.0, 0, 1.0, 1.0); publicAPI.traverseAllPasses(); };
publicAPI.xrRender = async (t, frame) => { const xrSession = frame.session; const isXrSessionHMD = [ XrSessionTypes.HmdVR, XrSessionTypes.HmdAR, ].includes(model.xrSessionType);
model.renderable .getInteractor() .updateXRGamepads(xrSession, frame, model.xrReferenceSpace);
model.xrSceneFrame = model.xrSession.requestAnimationFrame( publicAPI.xrRender );
const xrPose = frame.getViewerPose(model.xrReferenceSpace);
if (xrPose) { const gl = publicAPI.get3DContext();
if ( model.xrSessionType === XrSessionTypes.MobileAR && model.oldCanvasSize !== undefined ) { gl.canvas.width = model.oldCanvasSize[0]; gl.canvas.height = model.oldCanvasSize[1]; }
const glLayer = xrSession.renderState.baseLayer; gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer); gl.clear(gl.COLOR_BUFFER_BIT); gl.clear(gl.DEPTH_BUFFER_BIT); publicAPI.setSize(glLayer.framebufferWidth, glLayer.framebufferHeight);
const ren = model.renderable.getRenderers()[0];
xrPose.views.forEach((view, index) => { const viewport = glLayer.getViewport(view);
if (isXrSessionHMD) { if (view.eye === 'left') { ren.setViewport(0, 0, 0.5, 1.0); } else if (view.eye === 'right') { ren.setViewport(0.5, 0, 1.0, 1.0); } else { return; } } else if (model.xrSessionType === XrSessionTypes.LookingGlassVR) { const startX = viewport.x / glLayer.framebufferWidth; const startY = viewport.y / glLayer.framebufferHeight; const endX = (viewport.x + viewport.width) / glLayer.framebufferWidth; const endY = (viewport.y + viewport.height) / glLayer.framebufferHeight; ren.setViewport(startX, startY, endX, endY); } else { ren.setViewport(0, 0, 1, 1); }
ren .getActiveCamera() .computeViewParametersFromPhysicalMatrix( view.transform.inverse.matrix ); ren.getActiveCamera().setProjectionMatrix(view.projectionMatrix);
publicAPI.traverseAllPasses(); });
gl.scissor(0, 0, glLayer.framebufferWidth, glLayer.framebufferHeight); gl.disable(gl.SCISSOR_TEST); } };
publicAPI.restoreContext = () => { const rp = vtkRenderPass.newInstance(); rp.setCurrentOperation('Release'); rp.traverse(publicAPI, null); };
publicAPI.activateTexture = (texture) => { const result = model._textureResourceIds.get(texture); if (result !== undefined) { model.context.activeTexture(model.context.TEXTURE0 + result); return; }
const 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 = (texture) => { const result = model._textureResourceIds.get(texture); if (result !== undefined) { publicAPI.getTextureUnitManager().free(result); model._textureResourceIds.delete(texture); } };
publicAPI.getTextureUnitForTexture = (texture) => { const result = model._textureResourceIds.get(texture); if (result !== undefined) { return result; } return -1; };
publicAPI.getDefaultTextureInternalFormat = ( vtktype, numComps, oglNorm16Ext = null, useHalfFloat = false ) => { 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; } case oglNorm16Ext && !useHalfFloat && VtkDataTypes.UNSIGNED_SHORT: switch (numComps) { case 1: return oglNorm16Ext.R16_EXT; case 2: return oglNorm16Ext.RG16_EXT; case 3: return oglNorm16Ext.RGB16_EXT; case 4: default: return oglNorm16Ext.RGBA16_EXT; } case oglNorm16Ext && !useHalfFloat && VtkDataTypes.SHORT: switch (numComps) { case 1: return oglNorm16Ext.R16_SNORM_EXT; case 2: return oglNorm16Ext.RG16_SNORM_EXT; case 3: return oglNorm16Ext.RGB16_SNORM_EXT; case 4: default: return oglNorm16Ext.RGBA16_SNORM_EXT; } case VtkDataTypes.FLOAT: default: 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; } } }
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 = (img) => { model.bgImage.src = img.src; };
publicAPI.setUseBackgroundImage = (value) => { model.useBackgroundImage = value;
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(format = model.imageFormat) { const temporaryCanvas = document.createElement('canvas'); const temporaryContext = temporaryCanvas.getContext('2d'); temporaryCanvas.width = model.canvas.width; temporaryCanvas.height = model.canvas.height; temporaryContext.drawImage(model.canvas, 0, 0);
const mainBoundingClientRect = model.canvas.getBoundingClientRect();
const renderWindow = model.renderable; const renderers = renderWindow.getRenderers(); renderers.forEach((renderer) => { const viewProps = renderer.getViewProps(); viewProps.forEach((viewProp) => { if (viewProp.getContainer) { const container = viewProp.getContainer(); const canvasList = container.getElementsByTagName('canvas'); for (let i = 0; i < canvasList.length; i++) { const currentCanvas = canvasList[i]; const boundingClientRect = currentCanvas.getBoundingClientRect(); const newXPosition = boundingClientRect.x - mainBoundingClientRect.x; const newYPosition = boundingClientRect.y - mainBoundingClientRect.y; temporaryContext.drawImage( currentCanvas, newXPosition, newYPosition ); } } }); });
const screenshot = temporaryCanvas.toDataURL(format); temporaryCanvas.remove(); publicAPI.invokeImageReady(screenshot); }
publicAPI.captureNextImage = ( format = 'image/png', { resetCamera = false, size = null, scale = 1 } = {} ) => { if (model.deleted) { return null; } model.imageFormat = format; const previous = model.notifyStartCaptureImage; model.notifyStartCaptureImage = true;
model._screenshot = { size: !!size || scale !== 1 ? size || model.size.map((val) => val * scale) : null, };
return new Promise((resolve, reject) => { const subscription = publicAPI.onImageReady((imageURL) => { if (model._screenshot.size === null) { model.notifyStartCaptureImage = previous; subscription.unsubscribe(); if (model._screenshot.placeHolder) { model.size = model._screenshot.originalSize;
publicAPI.modified();
if (model._screenshot.cameras) { model._screenshot.cameras.forEach(({ restoreParamsFn, arg }) => restoreParamsFn(arg) ); }
publicAPI.traverseAllPasses();
model.el.removeChild(model._screenshot.placeHolder); model._screenshot.placeHolder.remove(); model._screenshot = null; } resolve(imageURL); } else { const tmpImg = document.createElement('img'); tmpImg.style = SCREENSHOT_PLACEHOLDER; tmpImg.src = imageURL; model._screenshot.placeHolder = model.el.appendChild(tmpImg);
model.canvas.style.display = 'none';
model._screenshot.originalSize = model.size; model.size = model._screenshot.size; model._screenshot.size = null;
publicAPI.modified();
if (resetCamera) { const isUserResetCamera = resetCamera !== true;
model._screenshot.cameras = model.renderable .getRenderers() .map((renderer) => { const camera = renderer.getActiveCamera(); const params = camera.get( 'focalPoint', 'position', 'parallelScale' );
return { resetCameraArgs: isUserResetCamera ? { renderer } : undefined, resetCameraFn: isUserResetCamera ? resetCamera : renderer.resetCamera, restoreParamsFn: camera.set, arg: JSON.parse(JSON.stringify(params)), }; });
model._screenshot.cameras.forEach( ({ resetCameraFn, resetCameraArgs }) => resetCameraFn(resetCameraArgs) ); }
publicAPI.traverseAllPasses(); } }); }); };
let hardwareMaximumLineWidth; publicAPI.getHardwareMaximumLineWidth = () => { if (hardwareMaximumLineWidth != null) { return hardwareMaximumLineWidth; }
const gl = publicAPI.get3DContext(); const lineWidthRange = gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE);
hardwareMaximumLineWidth = lineWidthRange[1];
return lineWidthRange[1]; };
publicAPI.getGLInformations = () => { const gl = publicAPI.get3DContext();
const glTextureFloat = gl.getExtension('OES_texture_float'); const glTextureHalfFloat = gl.getExtension('OES_texture_half_float'); const glDebugRendererInfo = gl.getExtension('WEBGL_debug_renderer_info'); const glDrawBuffers = gl.getExtension('WEBGL_draw_buffers'); const glAnisotropic = gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic');
const 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], ];
const result = {}; while (params.length) { const [label, key, value] = params.pop(); if (key) { result[key] = { label, value }; } } return result; };
publicAPI.traverseAllPasses = () => { if (model.renderPasses) { for (let index = 0; index < model.renderPasses.length; ++index) { model.renderPasses[index].traverse(publicAPI, null); } } if (model.notifyStartCaptureImage) { getCanvasDataURL(); } };
publicAPI.disableCullFace = () => { if (model.cullFaceEnabled) { model.context.disable(model.context.CULL_FACE); model.cullFaceEnabled = false; } };
publicAPI.enableCullFace = () => { if (!model.cullFaceEnabled) { model.context.enable(model.context.CULL_FACE); model.cullFaceEnabled = true; } };
publicAPI.setViewStream = (stream) => { if (model.viewStream === stream) { return false; } if (model.subscription) { model.subscription.unsubscribe(); model.subscription = null; } model.viewStream = stream; if (model.viewStream) { const mainRenderer = model.renderable.getRenderers()[0]; mainRenderer.getBackgroundByReference()[3] = 0;
publicAPI.setUseBackgroundImage(true);
model.subscription = model.viewStream.onImageReady((e) => publicAPI.setBackgroundImage(e.image) ); model.viewStream.setSize(model.size[0], model.size[1]); model.viewStream.invalidateCache(); model.viewStream.render();
publicAPI.modified(); } return true; };
publicAPI.createSelector = () => { const ret = vtkOpenGLHardwareSelector.newInstance(); ret.setOpenGLRenderWindow(publicAPI); return ret; };
function clearEvents() { model.canvas.removeEventListener('webglcontextlost', _preventDefault); model.canvas.removeEventListener( 'webglcontextrestored', publicAPI.restoreContext ); }
publicAPI.delete = macro.chain( clearEvents, publicAPI.delete, publicAPI.setViewStream, deleteGLContext );
publicAPI.setActiveFramebuffer = (newActiveFramebuffer) => { model.activeFramebuffer = newActiveFramebuffer; };
const superSetSize = publicAPI.setSize; publicAPI.setSize = (width, height) => { const modified = superSetSize(width, height); if (modified) { publicAPI.invokeWindowResizeEvent({ width, height }); } return modified; }; }
const DEFAULT_VALUES = { cullFaceEnabled: false, 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, activeFramebuffer: null, xrSession: null, xrReferenceSpace: null, xrSupported: true, imageFormat: 'image/png', useOffScreen: false, useBackgroundImage: false, };
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues);
vtkRenderWindowViewNode.extend(publicAPI, model, initialValues);
model.canvas = document.createElement('canvas'); model.canvas.style.width = '100%'; createGLContext();
if (!model.selector) { model.selector = vtkOpenGLHardwareSelector.newInstance(); model.selector.setOpenGLRenderWindow(publicAPI); }
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 = vtkOpenGLViewNodeFactory.newInstance(); model.myFactory.registerOverride('vtkRenderWindow', newInstance);
model.shaderCache = vtkShaderCache.newInstance(); model.shaderCache.setOpenGLRenderWindow(publicAPI);
model.renderPasses[0] = vtkForwardPass.newInstance();
macro.event(publicAPI, model, 'imageReady'); macro.event(publicAPI, model, 'haveVRDisplay');
macro.get(publicAPI, model, [ 'shaderCache', 'textureUnitManager', 'webgl2', 'vrDisplay', 'useBackgroundImage', 'xrSupported', 'activeFramebuffer', ]);
macro.setGet(publicAPI, model, [ 'initialized', 'context', 'canvas', 'renderPasses', 'notifyStartCaptureImage', 'defaultToWebgl2', 'cursor', 'useOffScreen', ]);
macro.setGetArray(publicAPI, model, ['size'], 2); macro.event(publicAPI, model, 'windowResizeEvent');
vtkOpenGLRenderWindow(publicAPI, model); }
export const newInstance = macro.newInstance(extend, 'vtkOpenGLRenderWindow');
registerViewConstructor('WebGL', newInstance);
export default { newInstance, extend, pushMonitorGLContextCount, popMonitorGLContextCount, };
|