UNPKG

@allmaps/render

Version:

Render functions for WebGL and image buffers

1,123 lines (1,122 loc) 34.4 kB
import { throttle } from "lodash-es"; import { wrap } from "comlink"; import { mergeOptions, squaredDistance, hexToFractionalRgb, maxOfNumberOrUndefined } from "@allmaps/stdlib"; import { supportedDistortionMeasures } from "@allmaps/transform"; import { red, darkblue, green, yellow, black } from "@allmaps/tailwind"; import { BaseRenderer } from "./BaseRenderer.js"; import { createWebGL2WarpedMapFactory } from "../maps/WebGL2WarpedMap.js"; import { CacheableWorkerImageDataTile } from "../tilecache/CacheableWorkerImageDataTile.js"; import { WarpedMapEvent, WarpedMapEventType } from "../shared/events.js"; import { multiplyHomogeneousTransform, homogeneousTransformToMatrix4, invertHomogeneousTransform } from "../shared/homogeneous-transform.js"; import { createShader, createProgram } from "../shared/webgl2.js"; import { Viewport } from "../viewport/Viewport.js"; import vertex_shader_default from "../shaders/map/vertex-shader.glsl.js"; import fragment_shader_default from "../shaders/map/fragment-shader.glsl.js"; import vertex_shader_default$1 from "../shaders/lines/vertex-shader.glsl.js"; import fragment_shader_default$1 from "../shaders/lines/fragment-shader.glsl.js"; import vertex_shader_default$2 from "../shaders/points/vertex-shader.glsl.js"; import fragment_shader_default$2 from "../shaders/points/fragment-shader.glsl.js"; import WorkerWrapper from "../workers/fetch-and-get-image-data.js"; const THROTTLE_PREPARE_RENDER_WAIT_MS = 200; const THROTTLE_PREPARE_RENDER_OPTIONS = { leading: true, trailing: true }; const THROTTLE_CHANGED_WAIT_MS = 50; const THROTTLE_CHANGED_OPTIONS = { leading: true, trailing: true }; const defaultWebgl2RendererOptions = { debugMaps: false, renderMaps: true, renderLines: true, renderPoints: true }; const DEFAULT_OPACITY = 1; const DEFAULT_SATURATION = 1; const DEFAULT_REMOVE_COLOR_THRESHOLD = 0; const DEFAULT_REMOVE_COLOR_HARDNESS = 0.7; const SIGNIFICANT_VIEWPORT_EPSILON = 100 * Number.EPSILON; const SIGNIFICANT_VIEWPORT_DISTANCE = 5; const ANIMATION_DURATION = 750; class WebGL2Renderer extends BaseRenderer { #worker; gl; partialWebgl2RendererOptions; mapProgram; linesProgram; pointsProgram; previousSignificantViewport; opacity = DEFAULT_OPACITY; saturation = DEFAULT_SATURATION; renderOptions = {}; lastAnimationFrameRequestId; animating = false; transformaterTransitionStart; animationProgress = 0; disableRender = false; throttledPrepareRenderInternal; throttledChanged; /** * Creates an instance of WebGL2Renderer. * * @constructor * @param gl - WebGL 2 rendering context * @param options - options */ constructor(gl, options) { const mapVertexShader = createShader( gl, gl.VERTEX_SHADER, vertex_shader_default ); const mapFragmentShader = createShader( gl, gl.FRAGMENT_SHADER, fragment_shader_default ); const linesVertexShader = createShader( gl, gl.VERTEX_SHADER, vertex_shader_default$1 ); const linesFragmentShader = createShader( gl, gl.FRAGMENT_SHADER, fragment_shader_default$1 ); const pointsVertexShader = createShader( gl, gl.VERTEX_SHADER, vertex_shader_default$2 ); const pointsFragmentShader = createShader( gl, gl.FRAGMENT_SHADER, fragment_shader_default$2 ); const mapProgram = createProgram(gl, mapVertexShader, mapFragmentShader); const linesProgram = createProgram( gl, linesVertexShader, linesFragmentShader ); const pointsProgram = createProgram( gl, pointsVertexShader, pointsFragmentShader ); const worker = new WorkerWrapper(); const wrappedWorker = wrap(worker); super( CacheableWorkerImageDataTile.createFactory(wrappedWorker), createWebGL2WarpedMapFactory(gl, mapProgram, linesProgram, pointsProgram), options ); this.#worker = worker; this.gl = gl; this.partialWebgl2RendererOptions = mergeOptions( defaultWebgl2RendererOptions, options ); this.mapProgram = mapProgram; this.linesProgram = linesProgram; this.pointsProgram = pointsProgram; gl.deleteShader(mapVertexShader); gl.deleteShader(mapFragmentShader); gl.deleteShader(mapVertexShader); gl.deleteShader(mapFragmentShader); gl.deleteShader(mapVertexShader); gl.deleteShader(mapFragmentShader); gl.disable(gl.DEPTH_TEST); this.addEventListeners(); this.throttledPrepareRenderInternal = throttle( this.prepareRenderInternal.bind(this), THROTTLE_PREPARE_RENDER_WAIT_MS, THROTTLE_PREPARE_RENDER_OPTIONS ); this.throttledChanged = throttle( this.changed.bind(this), THROTTLE_CHANGED_WAIT_MS, THROTTLE_CHANGED_OPTIONS ); } initializeWebGL(gl) { const mapVertexShader = createShader( gl, gl.VERTEX_SHADER, vertex_shader_default ); const mapFragmentShader = createShader( gl, gl.FRAGMENT_SHADER, fragment_shader_default ); const linesVertexShader = createShader( gl, gl.VERTEX_SHADER, vertex_shader_default$1 ); const linesFragmentShader = createShader( gl, gl.FRAGMENT_SHADER, fragment_shader_default$1 ); const pointsVertexShader = createShader( gl, gl.VERTEX_SHADER, vertex_shader_default$2 ); const pointsFragmentShader = createShader( gl, gl.FRAGMENT_SHADER, fragment_shader_default$2 ); const mapProgram = createProgram(gl, mapVertexShader, mapFragmentShader); const linesProgram = createProgram( gl, linesVertexShader, linesFragmentShader ); const pointsProgram = createProgram( gl, pointsVertexShader, pointsFragmentShader ); this.gl = gl; this.mapProgram = mapProgram; this.linesProgram = linesProgram; this.pointsProgram = pointsProgram; gl.disable(gl.DEPTH_TEST); for (const webgl2WarpedMap of this.warpedMapList.getWarpedMaps()) { webgl2WarpedMap.initializeWebGL(mapProgram, linesProgram, pointsProgram); } } /** * Get the opacity of the renderer * * @returns */ getOpacity() { return this.opacity; } /** * Set the opacity of the renderer * * @param opacity - opacity to set */ setOpacity(opacity) { this.opacity = opacity; } /** * Reset the opacity of the renderer */ resetOpacity() { this.opacity = DEFAULT_OPACITY; } /** * Get the opacity of a map * * @param mapId - ID of the map * @returns */ getMapOpacity(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { return webgl2WarpedMap.opacity; } } /** * Set the opacity of a map * * @param mapId - ID of the map * @param opacity - opacity to set */ setMapOpacity(mapId, opacity) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.opacity = Math.min(Math.max(opacity, 0), 1); } } /** * Rreset the opacity of a map * * @param mapId - ID of the map */ resetMapOpacity(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.opacity = DEFAULT_OPACITY; } } /** * Get the remove color options of the renderer * * @returns */ getRemoveColorOptions() { return this.renderOptions.removeColorOptions; } /** * Set the remove color options of the renderer * * @param removeColorOptions */ setRemoveColorOptions(removeColorOptions) { this.renderOptions.removeColorOptions = removeColorOptions; } /** * Reset the remove color options of the renderer */ resetRemoveColorOptions() { this.renderOptions.removeColorOptions = void 0; } /** * Get the remove color options of a map * * @param mapId - ID of the map * @returns */ getMapRemoveColorOptions(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { return webgl2WarpedMap.renderOptions.removeColorOptions; } } /** * Set the WebGL2 Renderer options * * @param partialWebgl2RendererOptions - Options */ setOptions(partialWebgl2RendererOptions) { this.partialWebgl2RendererOptions = mergeOptions( this.partialWebgl2RendererOptions, partialWebgl2RendererOptions ); super.setOptions(partialWebgl2RendererOptions); } /** * Set the remove color options of a map * * @param mapId - ID of the map * @param removeColorOptions - the 'remove color options' to set */ setMapRemoveColorOptions(mapId, removeColorOptions) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.renderOptions.removeColorOptions = removeColorOptions; } } /** * Reset the remove color options of a map * * @param mapId - ID of the map */ resetMapRemoveColorOptions(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.renderOptions.removeColorOptions = void 0; } } /** * Get the colorize options of the renderer * * @returns */ getColorizeOptions() { return this.renderOptions.colorizeOptions; } /** * Set the colorize options of the renderer * * @param colorizeOptions - the colorize options to set */ setColorizeOptions(colorizeOptions) { this.renderOptions.colorizeOptions = colorizeOptions; } /** * Reset the colorize options of the renderer */ resetColorizeOptions() { this.renderOptions.colorizeOptions = void 0; } /** * Get the colorize options of a map * * @param mapId - ID of the map * @returns Colorize options */ getMapColorizeOptions(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { return webgl2WarpedMap.renderOptions.colorizeOptions; } } /** * Set the colorize options of a map * * @param mapId - ID of the map * @param colorizeOptions - the colorize options to set */ setMapColorizeOptions(mapId, colorizeOptions) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.renderOptions.colorizeOptions = colorizeOptions; } } /** * Reset the colorize options of a map * * @param mapId - ID of the map */ resetMapColorizeOptions(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.renderOptions.colorizeOptions = void 0; } } /** * Get the grid options of the renderer * * @returns */ getGridOptions() { return this.renderOptions.gridOptions; } /** * Set the grid options of the renderer * * @param gridOptions - the grid options to set */ setGridOptions(gridOptions) { this.renderOptions.gridOptions = gridOptions; } /** * Reset the grid options of the renderer */ resetGridOptions() { this.renderOptions.gridOptions = void 0; } /** * Get the grid options of a map * * @param mapId - ID of the map * @returns */ getMapGridOptions(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { return webgl2WarpedMap.renderOptions.gridOptions; } } /** * Set the grid options of a map * * @param mapId - ID of the map * @param gridOptions - the grid options to set */ setMapGridOptions(mapId, gridOptions) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.renderOptions.gridOptions = gridOptions; } } /** * Reset the grid options of a map * * @param mapId - ID of the map */ resetMapGridOptions(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.renderOptions.gridOptions = void 0; } } /** * Get the saturation of the renderer * * @returns */ getSaturation() { return this.saturation; } /** * Set the saturation of the renderer * * 0 - grayscale, 1 - original colors * * @param saturation - the satuation to set */ setSaturation(saturation) { this.saturation = saturation; } /** * Reset the satuation of the renderer */ resetSaturation() { this.saturation = DEFAULT_SATURATION; } /** * Get the saturation of a map * * @param mapId - ID of the map * @returns */ getMapSaturation(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { return webgl2WarpedMap.saturation; } } /** * Set the saturation of a map * * 0 - grayscale, 1 - original colors * * @param mapId - ID of the map * @param saturation - the saturation to set */ setMapSaturation(mapId, saturation) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.saturation = saturation; } } /** * Reset the saturation of a map * * @param mapId - ID of the map */ resetMapSaturation(mapId) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { webgl2WarpedMap.saturation = DEFAULT_SATURATION; } } /** * Render the map for a given viewport. * * If no viewport is specified the current viewport is rerendered. * If no current viewport is known, a viewport is deduced based on the WarpedMapList and canvas width and hight. * * @param viewport - the current viewport */ render(viewport) { if (this.disableRender) { return; } this.viewport = viewport || this.viewport || Viewport.fromSizeAndMaps( [this.gl.canvas.width, this.gl.canvas.width], this.warpedMapList ); this.loadMissingImageInfosInViewport(); if (this.someImageInfosInViewport()) { this.throttledPrepareRenderInternal(); } this.renderInternal(); } clear() { this.warpedMapList.clear(); this.mapsInViewport = /* @__PURE__ */ new Set(); this.mapsWithRequestedTilesForViewport = /* @__PURE__ */ new Set(); this.gl.clear(this.gl.DEPTH_BUFFER_BIT | this.gl.COLOR_BUFFER_BIT); this.tileCache.clear(); } cancelThrottledFunctions() { this.throttledPrepareRenderInternal.cancel(); this.throttledChanged.cancel(); } destroy() { this.cancelThrottledFunctions(); for (const webgl2WarpedMap of this.warpedMapList.getWarpedMaps()) { this.removeEventListenersFromWebGL2WarpedMap(webgl2WarpedMap); } this.removeEventListeners(); super.destroy(); this.gl.deleteProgram(this.mapProgram); this.gl.deleteProgram(this.linesProgram); this.gl.deleteProgram(this.pointsProgram); this.#worker.terminate(); } updateMapsForViewport(tiles) { const { mapsEnteringViewport, mapsLeavingViewport } = super.updateMapsForViewport(tiles); this.updateVertexBuffers(mapsEnteringViewport); return { mapsEnteringViewport, mapsLeavingViewport }; } resetPrevious(mapIds) { const webgl2WarpedMaps = this.warpedMapList.getWarpedMaps({ mapIds }); for (const webgl2WarpedMap of webgl2WarpedMaps) { webgl2WarpedMap.resetPrevious(); } } updateVertexBuffers(mapIds) { if (!this.viewport) { return; } const webgl2WarpedMaps = this.warpedMapList.getWarpedMaps({ mapIds }); for (const webgl2WarpedMap of webgl2WarpedMaps) { webgl2WarpedMap.updateVertexBuffers( this.viewport.projectedGeoToClipHomogeneousTransform, this.partialWebgl2RendererOptions ); } } prepareRenderInternal() { this.assureProjection(); this.requestFetchableTiles(); } shouldRequestFetchableTiles() { if (!this.viewport) { return false; } if (this.animating) { return false; } if (!this.previousSignificantViewport) { this.previousSignificantViewport = this.viewport; return true; } else { const rectangleSquaredDistances = []; for (let i = 0; i < 4; i++) { rectangleSquaredDistances.push( squaredDistance( this.previousSignificantViewport.projectedGeoRectangle[i], this.viewport.projectedGeoRectangle[i] ) / Math.pow(this.viewport.projectedGeoPerViewportScale, 2) ); } const maxSquaredDistance = Math.max(...rectangleSquaredDistances); if (maxSquaredDistance < SIGNIFICANT_VIEWPORT_EPSILON) { return true; } if (maxSquaredDistance > Math.pow(SIGNIFICANT_VIEWPORT_DISTANCE, 2)) { this.previousSignificantViewport = this.viewport; return true; } else { return false; } } } shouldAnticipateInteraction() { return true; } renderInternal() { if (!this.viewport) { return; } const gl = this.gl; gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); if (this.partialWebgl2RendererOptions.renderMaps) { this.renderMapsInternal(); } if (this.partialWebgl2RendererOptions.renderLines) { this.renderLinesInternal(); } if (this.partialWebgl2RendererOptions.renderPoints) { this.renderPointsInternal(); } } renderMapsInternal() { if (!this.viewport) { return; } this.setMapProgramUniforms(); for (const mapId of this.mapsWithRequestedTilesForViewport) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (!webgl2WarpedMap) { continue; } this.setMapProgramRenderOptionsUniforms( this.renderOptions, webgl2WarpedMap.renderOptions ); this.setMapProgramMapUniforms(webgl2WarpedMap); const count = webgl2WarpedMap.resourceTrianglePoints.length; const primitiveType = this.gl.TRIANGLES; const offset = 0; this.gl.bindVertexArray(webgl2WarpedMap.mapVao); this.gl.drawArrays(primitiveType, offset, count); } } renderLinesInternal() { this.setLinesProgramUniforms(); for (const mapId of this.mapsInViewport) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (!webgl2WarpedMap) { continue; } this.setLinesProgramMapUniforms(webgl2WarpedMap); const count = webgl2WarpedMap.lineLayers.reduce( (accumulator, lineLayer) => accumulator + lineLayer.projectedGeoLines.length, 0 ) * 6; const primitiveType = this.gl.TRIANGLES; const offset = 0; this.gl.bindVertexArray(webgl2WarpedMap.linesVao); this.gl.drawArrays(primitiveType, offset, count); } } renderPointsInternal() { this.setPointsProgramUniforms(); for (const mapId of this.mapsInViewport) { const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (!webgl2WarpedMap) { continue; } this.setPointsProgramMapUniforms(webgl2WarpedMap); const count = webgl2WarpedMap.pointLayers.reduce( (accumulator, pointLayer) => accumulator + pointLayer.projectedGeoPoints.length, 0 ); const primitiveType = this.gl.POINTS; const offset = 0; this.gl.bindVertexArray(webgl2WarpedMap.pointsVao); this.gl.drawArrays(primitiveType, offset, count); } } setMapProgramUniforms() { const program = this.mapProgram; const gl = this.gl; gl.useProgram(program); const debugLocation = gl.getUniformLocation(program, "u_debug"); gl.uniform1f( debugLocation, this.partialWebgl2RendererOptions.debugMaps ? 1 : 0 ); const animationProgressLocation = gl.getUniformLocation( program, "u_animationProgress" ); gl.uniform1f(animationProgressLocation, this.animationProgress); const colorDistortion00 = gl.getUniformLocation( program, "u_colorDistortion00" ); gl.uniform4f(colorDistortion00, ...hexToFractionalRgb(red), 1); const colorDistortion01 = gl.getUniformLocation( program, "u_colorDistortion01" ); gl.uniform4f(colorDistortion01, ...hexToFractionalRgb(darkblue), 1); const colorDistortion1 = gl.getUniformLocation( program, "u_colorDistortion1" ); gl.uniform4f(colorDistortion1, ...hexToFractionalRgb(green), 1); const colorDistortion2 = gl.getUniformLocation( program, "u_colorDistortion2" ); gl.uniform4f(colorDistortion2, ...hexToFractionalRgb(yellow), 1); const colorDistortion3 = gl.getUniformLocation( program, "u_colorDistortion3" ); gl.uniform4f(colorDistortion3, ...hexToFractionalRgb(red), 1); const colorGrid = gl.getUniformLocation(program, "u_colorGrid"); gl.uniform4f(colorGrid, ...hexToFractionalRgb(black), 1); } setMapProgramRenderOptionsUniforms(layerRenderOptions, mapRenderOptions) { const gl = this.gl; const program = this.mapProgram; gl.useProgram(program); const renderOptions = { removeColorOptions: { color: mapRenderOptions.removeColorOptions?.color || layerRenderOptions.removeColorOptions?.color, hardness: maxOfNumberOrUndefined( mapRenderOptions.removeColorOptions?.hardness, layerRenderOptions.removeColorOptions?.hardness ), threshold: maxOfNumberOrUndefined( mapRenderOptions.removeColorOptions?.threshold, layerRenderOptions.removeColorOptions?.threshold ) }, colorizeOptions: { ...layerRenderOptions.colorizeOptions, ...mapRenderOptions.colorizeOptions }, gridOptions: { ...layerRenderOptions.gridOptions, ...mapRenderOptions.gridOptions } }; const removeColorOptionsColor = renderOptions.removeColorOptions?.color; const removeColorLocation = gl.getUniformLocation(program, "u_removeColor"); gl.uniform1f(removeColorLocation, removeColorOptionsColor ? 1 : 0); if (removeColorOptionsColor) { const removeColorOptionsColorLocation = gl.getUniformLocation( program, "u_removeColorOptionsColor" ); gl.uniform3fv(removeColorOptionsColorLocation, removeColorOptionsColor); const removeColorOptionsThresholdLocation = gl.getUniformLocation( program, "u_removeColorOptionsThreshold" ); gl.uniform1f( removeColorOptionsThresholdLocation, renderOptions.removeColorOptions?.threshold || DEFAULT_REMOVE_COLOR_THRESHOLD ); const removeColorOptionsHardnessLocation = gl.getUniformLocation( program, "u_removeColorOptionsHardness" ); gl.uniform1f( removeColorOptionsHardnessLocation, renderOptions.removeColorOptions?.hardness || DEFAULT_REMOVE_COLOR_HARDNESS ); } const colorizeOptionsColor = renderOptions.colorizeOptions?.color; const colorizeLocation = gl.getUniformLocation(program, "u_colorize"); gl.uniform1f(colorizeLocation, colorizeOptionsColor ? 1 : 0); if (colorizeOptionsColor) { const colorizeOptionsColorLocation = gl.getUniformLocation( program, "u_colorizeOptionsColor" ); gl.uniform3fv(colorizeOptionsColorLocation, colorizeOptionsColor); } const gridOptionsGrid = renderOptions.gridOptions?.enabled; const gridLocation = gl.getUniformLocation(program, "u_grid"); gl.uniform1f(gridLocation, gridOptionsGrid ? 1 : 0); } setMapProgramMapUniforms(webgl2WarpedMap) { if (!this.viewport) { return; } const gl = this.gl; const program = this.mapProgram; gl.useProgram(program); const renderHomogeneousTransform = multiplyHomogeneousTransform( this.viewport.projectedGeoToClipHomogeneousTransform, webgl2WarpedMap.invertedRenderHomogeneousTransform ); const renderHomogeneousTransformLocation = gl.getUniformLocation( program, "u_renderHomogeneousTransform" ); gl.uniformMatrix4fv( renderHomogeneousTransformLocation, false, homogeneousTransformToMatrix4(renderHomogeneousTransform) ); const opacityLocation = gl.getUniformLocation(program, "u_opacity"); gl.uniform1f(opacityLocation, this.opacity * webgl2WarpedMap.opacity); const saturationLocation = gl.getUniformLocation(program, "u_saturation"); gl.uniform1f( saturationLocation, this.saturation * webgl2WarpedMap.saturation ); const distortionLocation = gl.getUniformLocation(program, "u_distortion"); gl.uniform1f(distortionLocation, webgl2WarpedMap.distortionMeasure ? 1 : 0); const distortionOptionsDistortionMeasureLocation = gl.getUniformLocation( program, "u_distortionOptionsdistortionMeasure" ); gl.uniform1i( distortionOptionsDistortionMeasureLocation, webgl2WarpedMap.distortionMeasure ? supportedDistortionMeasures.indexOf(webgl2WarpedMap.distortionMeasure) : 0 ); const scaleFactorForViewportLocation = gl.getUniformLocation( program, "u_scaleFactorForViewport" ); const scaleFactorForViewport = webgl2WarpedMap.tileZoomLevelForViewport ? webgl2WarpedMap.tileZoomLevelForViewport.scaleFactor : 1; gl.uniform1i(scaleFactorForViewportLocation, scaleFactorForViewport); const cachedTilesTextureArrayLocation = gl.getUniformLocation( program, "u_cachedTilesTextureArray" ); gl.uniform1i(cachedTilesTextureArrayLocation, 0); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D_ARRAY, webgl2WarpedMap.cachedTilesTextureArray); const cachedTilesResourceOriginPointsAndDimensionsLocation = gl.getUniformLocation( program, "u_cachedTilesResourceOriginPointsAndDimensionsTexture" ); gl.uniform1i(cachedTilesResourceOriginPointsAndDimensionsLocation, 1); gl.activeTexture(gl.TEXTURE1); gl.bindTexture( gl.TEXTURE_2D, webgl2WarpedMap.cachedTilesResourceOriginPointsAndDimensionsTexture ); const cachedTileScaleFactorsTextureLocation = gl.getUniformLocation( program, "u_cachedTilesScaleFactorsTexture" ); gl.uniform1i(cachedTileScaleFactorsTextureLocation, 2); gl.activeTexture(gl.TEXTURE2); gl.bindTexture( gl.TEXTURE_2D, webgl2WarpedMap.cachedTilesScaleFactorsTexture ); } setLinesProgramUniforms() { if (!this.viewport) { return; } const gl = this.gl; const program = this.linesProgram; gl.useProgram(program); const viewportToClipHomogeneousTransformLocation = gl.getUniformLocation( program, "u_viewportToClipHomogeneousTransform" ); gl.uniformMatrix4fv( viewportToClipHomogeneousTransformLocation, false, homogeneousTransformToMatrix4( this.viewport.viewportToClipHomogeneousTransform ) ); const clipToViewportHomogeneousTransformLocation = gl.getUniformLocation( program, "u_clipToViewportHomogeneousTransform" ); gl.uniformMatrix4fv( clipToViewportHomogeneousTransformLocation, false, homogeneousTransformToMatrix4( invertHomogeneousTransform( this.viewport.viewportToClipHomogeneousTransform ) ) ); const animationProgressLocation = gl.getUniformLocation( program, "u_animationProgress" ); gl.uniform1f(animationProgressLocation, this.animationProgress); } setLinesProgramMapUniforms(webgl2WarpedMap) { if (!this.viewport) { return; } const gl = this.gl; const program = this.linesProgram; gl.useProgram(program); const renderHomogeneousTransform = multiplyHomogeneousTransform( this.viewport.projectedGeoToClipHomogeneousTransform, webgl2WarpedMap.invertedRenderHomogeneousTransform ); const renderHomogeneousTransformLocation = gl.getUniformLocation( program, "u_renderHomogeneousTransform" ); gl.uniformMatrix4fv( renderHomogeneousTransformLocation, false, homogeneousTransformToMatrix4(renderHomogeneousTransform) ); } setPointsProgramUniforms() { if (!this.viewport) { return; } const gl = this.gl; const program = this.pointsProgram; gl.useProgram(program); const animationProgressLocation = gl.getUniformLocation( program, "u_animationProgress" ); gl.uniform1f(animationProgressLocation, this.animationProgress); } setPointsProgramMapUniforms(webgl2WarpedMap) { if (!this.viewport) { return; } const gl = this.gl; const program = this.pointsProgram; gl.useProgram(program); const renderHomogeneousTransform = multiplyHomogeneousTransform( this.viewport.projectedGeoToClipHomogeneousTransform, webgl2WarpedMap.invertedRenderHomogeneousTransform ); const renderHomogeneousTransformLocation = gl.getUniformLocation( program, "u_renderHomogeneousTransform" ); gl.uniformMatrix4fv( renderHomogeneousTransformLocation, false, homogeneousTransformToMatrix4(renderHomogeneousTransform) ); } startTransformerTransition(mapIds) { this.updateVertexBuffers(mapIds); if (this.lastAnimationFrameRequestId !== void 0) { cancelAnimationFrame(this.lastAnimationFrameRequestId); } this.animating = true; this.transformaterTransitionStart = void 0; this.lastAnimationFrameRequestId = requestAnimationFrame( ((now) => this.transformerTransitionFrame(now, mapIds)).bind(this) ); } transformerTransitionFrame(now, mapIds) { if (!this.transformaterTransitionStart) { this.transformaterTransitionStart = now; } if (now - this.transformaterTransitionStart < ANIMATION_DURATION) { this.animationProgress = (now - this.transformaterTransitionStart) / ANIMATION_DURATION; this.changed(); this.renderInternal(); this.lastAnimationFrameRequestId = requestAnimationFrame( ((now2) => this.transformerTransitionFrame(now2, mapIds)).bind( this ) ); } else { this.finishTransformerTransition(mapIds); } } finishTransformerTransition(mapIds) { this.resetPrevious(mapIds); this.updateVertexBuffers(mapIds); this.animating = false; this.animationProgress = 0; this.transformaterTransitionStart = void 0; this.changed(); } changed() { this.dispatchEvent(new WarpedMapEvent(WarpedMapEventType.CHANGED)); } imageInfoLoaded(event) { if (event instanceof WarpedMapEvent) { this.dispatchEvent(new WarpedMapEvent(WarpedMapEventType.IMAGEINFOLOADED)); } } clearMap(mapId) { const webGL2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webGL2WarpedMap) { webGL2WarpedMap.clearTextures(); } } mapTileLoaded(event) { if (event instanceof WarpedMapEvent) { const { mapId, tileUrl } = event.data; const tile = this.tileCache.getCacheableTile(tileUrl); if (!tile) { return; } if (!tile.isCachedTile()) { return; } const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (!webgl2WarpedMap) { return; } webgl2WarpedMap.addCachedTileAndUpdateTextures(tile); } } mapTileRemoved(event) { if (event instanceof WarpedMapEvent) { const { mapId, tileUrl } = event.data; const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (!webgl2WarpedMap) { return; } webgl2WarpedMap.removeCachedTileAndUpdateTextures(tileUrl); } } warpedMapAdded(event) { if (event instanceof WarpedMapEvent) { const mapId = event.data; const webgl2WarpedMap = this.warpedMapList.getWarpedMap(mapId); if (webgl2WarpedMap) { this.addEventListenersToWebGL2WarpedMap(webgl2WarpedMap); } } } preChange(event) { if (event instanceof WarpedMapEvent) { const mapIds = event.data; for (const webgl2WarpedMap of this.warpedMapList.getWarpedMaps({ mapIds })) { if (this.animating) { webgl2WarpedMap.mixPreviousAndNew(1 - this.animationProgress); } } } } optionsChanged(event) { if (event instanceof WarpedMapEvent) { const mapIds = event.data; this.finishTransformerTransition(mapIds); } this.changed(); } gcpsChanged(event) { if (event instanceof WarpedMapEvent) { const mapIds = event.data; this.finishTransformerTransition(mapIds); } this.changed(); } resourceMaskChanged(event) { if (event instanceof WarpedMapEvent) { const mapIds = event.data; this.finishTransformerTransition(mapIds); } this.changed(); } transformationChanged(event) { if (event instanceof WarpedMapEvent) { const mapIds = event.data; this.startTransformerTransition(mapIds); } } distortionChanged(event) { if (event instanceof WarpedMapEvent) { const mapIds = event.data; this.startTransformerTransition(mapIds); } } internalProjectionChanged(event) { if (event instanceof WarpedMapEvent) { const mapIds = event.data; this.startTransformerTransition(mapIds); } } projectionChanged(event) { if (event instanceof WarpedMapEvent) { const mapIds = event.data; this.finishTransformerTransition(mapIds); } this.changed(); } addEventListenersToWebGL2WarpedMap(webgl2WarpedMap) { webgl2WarpedMap.addEventListener( WarpedMapEventType.TEXTURESUPDATED, this.throttledChanged.bind(this) ); } removeEventListenersFromWebGL2WarpedMap(webgl2WarpedMap) { webgl2WarpedMap.removeEventListener( WarpedMapEventType.TEXTURESUPDATED, this.throttledChanged.bind(this) ); } contextLost() { this.disableRender = true; this.cancelThrottledFunctions(); for (const webgl2WarpedMap of this.warpedMapList.getWarpedMaps()) { webgl2WarpedMap.cancelThrottledFunctions(); } this.tileCache.clear(); } contextRestored() { this.initializeWebGL(this.gl); this.disableRender = false; } } export { WebGL2Renderer }; //# sourceMappingURL=WebGL2Renderer.js.map