@allmaps/render
Version:
Render functions for WebGL and image buffers
874 lines (873 loc) • 29.3 kB
JavaScript
import { throttle } from "lodash-es";
import { mergeOptions, pointsAndPointsToLines, lineStringToLines, hexToFractionalOpaqueRgba, subSetArray } from "@allmaps/stdlib";
import { red, yellow, green, darkblue, black, pink, blue, gray, white } from "@allmaps/tailwind";
import { TriangulatedWarpedMap } from "./TriangulatedWarpedMap.js";
import { WarpedMapEvent, WarpedMapEventType } from "../shared/events.js";
import { createHomogeneousTransform, invertHomogeneousTransform, applyHomogeneousTransform } from "../shared/homogeneous-transform.js";
import { createBuffer } from "../shared/webgl2.js";
import { getTilesAtOtherScaleFactors, tileKey } from "../shared/tiles.js";
const THROTTLE_UPDATE_TEXTURES_WAIT_MS = 200;
const THROTTLE_UPDATE_TEXTURES_OPTIONS = {
leading: true,
trailing: true
};
const DEFAULT_RENDER_LINE_GROUP_OPTIONS = {
viewportSize: 6,
color: black,
viewportBorderSize: 0,
borderColor: white
};
const DEFAULT_RENDER_POINT_GROUP_OPTION = {
viewportSize: 16,
color: black,
viewportBorderSize: 1,
borderColor: white
};
const DEFAULT_SPECIFIC_WEBGL2_WARPED_MAP_OPTIONS = {
renderGcps: false,
renderGcpsColor: blue,
renderTransformedGcps: false,
renderTransformedGcpsColor: pink,
renderVectors: false,
renderVectorsSize: 6,
renderVectorsColor: black,
renderFullMask: false,
renderFullMaskSize: 8,
renderFullMaskColor: green,
renderAppliableMask: false,
renderAppliableMaskSize: 8,
renderAppliableMaskColor: pink,
renderMask: false,
renderMaskSize: 8,
renderMaskColor: pink,
opacity: 1,
saturation: 1,
removeColor: false,
removeColorColor: black,
removeColorThreshold: 0,
removeColorHardness: 0.7,
colorize: false,
colorizeColor: pink,
renderGrid: false,
renderGridColor: black,
distortionColor00: red,
distortionColor01: darkblue,
distortionColor1: green,
distortionColor2: yellow,
distortionColor3: red,
debugTiles: false,
debugTriangles: false,
debugTriangulation: false
};
const TEXTURES_MAX_HIGHER_LOG2_SCALE_FACTOR_DIFF = 5;
const TEXTURES_MAX_LOWER_LOG2_SCALE_FACTOR_DIFF = 1;
function createWebGL2WarpedMapFactory(gl, mapProgram, linesProgram, pointsProgram) {
return (mapId, georeferencedMap, listOptions, mapOptions) => new WebGL2WarpedMap(
mapId,
georeferencedMap,
gl,
mapProgram,
linesProgram,
pointsProgram,
listOptions,
mapOptions
);
}
class WebGL2WarpedMap extends TriangulatedWarpedMap {
gl;
mapProgram;
linesProgram;
pointsProgram;
mapVao = null;
linesVao = null;
pointsVao = null;
lineGroups = [];
pointGroups = [];
// Consider to store cachedTilesByTileKey as a quadtree for faster lookups
cachedTilesByTileKey;
cachedTilesByTileUrl;
cachedTilesForTexture = [];
previousCachedTilesForTexture = [];
cachedTilesTextureArray = null;
cachedTilesResourceOriginPointsAndSizesTexture = null;
cachedTilesScaleFactorsTexture = null;
// About renderHomogeneousTransform and InvertedRenderHomogeneousTransform:
// renderHomogeneousTransform is the product of:
// - the viewport's projectedGeoToClipTransform (projected geo coordinates -> clip coordinates)
// - the saved invertedRenderHomogeneousTransform (projected clip coordinates -> geo coordinates)
// since updateVertexBuffers ('where to draw triangles') run with possibly a different Viewport then renderInternal ('drawing the triangles'), a difference caused by throttling, there needs to be an adjustment.
// this adjustment is minimal: indeed, since invertedRenderHomogeneousTransform is set as the inverse of the viewport's projectedGeoToClipTransform in updateVertexBuffers()
// this renderHomogeneousTransform is almost the identity transform [1, 0, 0, 1, 0, 0].
invertedRenderHomogeneousTransform;
throttledUpdateTextures;
/**
* Creates an instance of WebGL2WarpedMap.
*
* @constructor
* @param mapId - ID of the map
* @param georeferencedMap - Georeferenced map used to construct the WarpedMap
* @param gl - WebGL rendering context
* @param mapProgram - WebGL program for map
* @param options - WarpedMapOptions
*/
constructor(mapId, georeferencedMap, gl, mapProgram, linesProgram, pointsProgram, listOptions, mapOptions) {
super(mapId, georeferencedMap, listOptions, mapOptions);
this.cachedTilesByTileKey = /* @__PURE__ */ new Map();
this.cachedTilesByTileUrl = /* @__PURE__ */ new Map();
this.gl = gl;
this.initializeWebGL(mapProgram, linesProgram, pointsProgram);
this.invertedRenderHomogeneousTransform = createHomogeneousTransform();
this.throttledUpdateTextures = throttle(
this.updateTextures.bind(this),
THROTTLE_UPDATE_TEXTURES_WAIT_MS,
THROTTLE_UPDATE_TEXTURES_OPTIONS
);
}
initializeWebGL(mapProgram, linesProgram, pointsProgram) {
this.mapProgram = mapProgram;
this.linesProgram = linesProgram;
this.pointsProgram = pointsProgram;
this.mapVao = this.gl.createVertexArray();
this.linesVao = this.gl.createVertexArray();
this.pointsVao = this.gl.createVertexArray();
this.cachedTilesTextureArray = this.gl.createTexture();
this.cachedTilesScaleFactorsTexture = this.gl.createTexture();
this.cachedTilesResourceOriginPointsAndSizesTexture = this.gl.createTexture();
}
/**
* Get default options
*/
static getDefaultOptions() {
return mergeOptions(
DEFAULT_SPECIFIC_WEBGL2_WARPED_MAP_OPTIONS,
super.getDefaultOptions()
);
}
/** Set default options */
setDefaultOptions() {
this.defaultOptions = WebGL2WarpedMap.getDefaultOptions();
}
applyOptions(animationOptions) {
const changedOptions = super.applyOptions(animationOptions);
this.options.opacity = (this.listOptions?.opacity ?? this.defaultOptions.opacity) * (this.mapOptions?.opacity ?? 1);
this.options.saturation = (this.listOptions?.saturation ?? this.defaultOptions.saturation) * (this.mapOptions?.saturation ?? 1);
return changedOptions;
}
shouldRenderMap() {
return super.shouldRenderMap() && this.options.renderMaps !== false && this.options.opacity !== 0;
}
shouldRenderLines() {
return super.shouldRenderLines() && this.options.renderLines !== false && (this.options.renderFullMask || this.options.renderAppliableMask || this.options.renderMask || this.options.renderVectors);
}
shouldRenderPoints() {
return super.shouldRenderPoints() && this.options.renderPoints !== false && (this.options.renderGcps || this.options.renderTransformedGcps || this.options.debugTriangulation);
}
/**
* Update the vertex buffers of this warped map
*
* @param projectedGeoToClipHomogeneousTransform - Transform from projected geo coordinates to webgl2 coordinates in the [-1, 1] range. Equivalent to OpenLayers' projectionTransform.
*/
updateVertexBuffers(projectedGeoToClipHomogeneousTransform) {
this.invertedRenderHomogeneousTransform = invertHomogeneousTransform(
projectedGeoToClipHomogeneousTransform
);
if (this.shouldRenderMap()) {
this.updateVertexBuffersMap(projectedGeoToClipHomogeneousTransform);
}
if (this.shouldRenderLines()) {
this.updateVertexBuffersLines(projectedGeoToClipHomogeneousTransform);
}
if (this.shouldRenderPoints()) {
this.updateVertexBuffersPoints(projectedGeoToClipHomogeneousTransform);
}
}
/**
* Clear textures for this map
*/
clearTextures() {
}
/**
* Add cached tile to the textures of this map and update textures
*
* @param cachedTile
*/
addCachedTileAndUpdateTextures(cachedTile) {
this.cachedTilesByTileKey.set(cachedTile.fetchableTile.tileKey, cachedTile);
this.cachedTilesByTileUrl.set(cachedTile.fetchableTile.tileUrl, cachedTile);
this.throttledUpdateTextures();
}
/**
* Remove cached tile from the textures of this map and update textures
*
* @param tileUrl
*/
removeCachedTileAndUpdateTextures(tileUrl) {
const cachedTile = this.cachedTilesByTileUrl.get(tileUrl);
if (!cachedTile) {
return;
}
this.cachedTilesByTileKey.delete(cachedTile.fetchableTile.tileKey);
this.cachedTilesByTileUrl.delete(tileUrl);
this.throttledUpdateTextures();
}
cancelThrottledFunctions() {
this.throttledUpdateTextures.cancel();
}
destroy() {
this.gl.deleteVertexArray(this.mapVao);
this.gl.deleteVertexArray(this.linesVao);
this.gl.deleteVertexArray(this.pointsVao);
this.gl.deleteTexture(this.cachedTilesTextureArray);
this.gl.deleteTexture(this.cachedTilesScaleFactorsTexture);
this.gl.deleteTexture(this.cachedTilesResourceOriginPointsAndSizesTexture);
this.cancelThrottledFunctions();
super.destroy();
}
setLineGroups() {
this.lineGroups = [];
if (this.options.renderVectors) {
this.lineGroups.push({
projectedGeoLines: pointsAndPointsToLines(
this.projectedGeoPoints,
this.projectedGeoTransformedResourcePoints
),
projectedGeoPreviousLines: pointsAndPointsToLines(
this.projectedGeoPoints,
this.projectedGeoPreviousTransformedResourcePoints
),
viewportSize: this.options.renderVectorsSize,
color: this.options.renderVectorsColor,
viewportBorderSize: this.options.renderVectorsBorderSize,
borderColor: this.options.renderVectorsBorderColor
});
}
if (this.options.renderFullMask) {
this.lineGroups.push({
projectedGeoLines: lineStringToLines(this.projectedGeoFullMask),
viewportSize: this.options.renderFullMaskSize,
color: this.options.renderFullMaskColor,
viewportBorderSize: this.options.renderFullMaskBorderSize,
borderColor: this.options.renderFullMaskBorderColor
});
}
if (this.options.renderAppliableMask) {
this.lineGroups.push({
projectedGeoLines: lineStringToLines(
this.projectedGeoTriangulationAppliableMask
),
projectedGeoPreviousLines: lineStringToLines(
this.projectedGeoPreviousTriangulationAppliableMask
),
viewportSize: this.options.renderAppliableMaskSize,
color: this.options.renderAppliableMaskColor,
viewportBorderSize: this.options.renderAppliableMaskBorderSize,
borderColor: this.options.renderAppliableMaskBorderColor
});
}
if (this.options.renderMask) {
this.lineGroups.push({
projectedGeoLines: lineStringToLines(
this.projectedGeoTriangulationMask
),
projectedGeoPreviousLines: lineStringToLines(
this.projectedGeoPreviousTriangulationMask
),
viewportSize: this.options.renderMaskSize,
color: this.options.renderMaskColor,
viewportBorderSize: this.options.renderMaskBorderSize,
borderColor: this.options.renderMaskBorderColor
});
}
}
setPointGroups() {
this.pointGroups = [];
if (this.options.renderGcps) {
this.pointGroups.push({
projectedGeoPoints: this.projectedGeoPoints,
viewportSize: this.options.renderGcpsSize,
color: this.options.renderGcpsColor,
viewportBorderSize: this.options.renderGcpsBorderSize,
borderColor: this.options.renderGcpsBorderColor
});
}
if (this.options.renderTransformedGcps) {
this.pointGroups.push({
projectedGeoPoints: this.projectedGeoTransformedResourcePoints,
projectedGeoPreviousPoints: this.projectedGeoPreviousTransformedResourcePoints,
viewportSize: this.options.renderTransformedGcpsSize,
color: this.options.renderTransformedGcpsColor,
viewportBorderSize: this.options.renderTransformedGcpsBorderSize,
borderColor: this.options.renderTransformedGcpsBorderColor
});
}
if (this.options.debugTriangulation) {
this.pointGroups.push({
projectedGeoPoints: this.projectedGeoPreviousTrianglePoints,
color: gray
});
this.pointGroups.push({
projectedGeoPoints: this.projectedGeoTrianglePoints,
color: yellow
});
}
}
updateVertexBuffersMap(projectedGeoToClipHomogeneousTransform) {
if (!this.mapVao) {
return;
}
const gl = this.gl;
const program = this.mapProgram;
gl.bindVertexArray(this.mapVao);
createBuffer(
gl,
program,
new Float32Array(this.resourceTrianglePoints.flat()),
2,
"a_resourceTrianglePoint"
);
const clipPreviousTrianglePoints = this.projectedGeoPreviousTrianglePoints.map(
(point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point)
);
createBuffer(
gl,
program,
new Float32Array(clipPreviousTrianglePoints.flat()),
2,
"a_clipPreviousTrianglePoint"
);
const clipTrianglePoints = this.projectedGeoTrianglePoints.map(
(point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point)
);
createBuffer(
gl,
program,
new Float32Array(clipTrianglePoints.flat()),
2,
"a_clipTrianglePoint"
);
createBuffer(
gl,
program,
new Float32Array(this.previousTrianglePointsDistortion),
1,
"a_previousTrianglePointDistortion"
);
createBuffer(
gl,
program,
new Float32Array(this.trianglePointsDistortion),
1,
"a_trianglePointDistortion"
);
const trianglePointsTriangleIndex = new Float32Array(
this.resourceTrianglePoints.length
).map((_v, i) => {
return i;
});
createBuffer(
gl,
program,
trianglePointsTriangleIndex,
1,
"a_trianglePointIndex"
);
}
updateVertexBuffersLines(projectedGeoToClipHomogeneousTransform) {
if (!this.linesVao) {
return;
}
const gl = this.gl;
const program = this.linesProgram;
gl.bindVertexArray(this.linesVao);
this.setLineGroups();
const clipSixPoints = this.lineGroups.reduce(
(accumulator, lineGroup) => accumulator.concat(lineGroup.projectedGeoLines),
[]
).map((projectedGeoLine) => [
projectedGeoLine[0],
projectedGeoLine[0],
projectedGeoLine[0],
projectedGeoLine[1],
projectedGeoLine[1],
projectedGeoLine[1]
]).flat().map(
(point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point)
);
createBuffer(
gl,
program,
new Float32Array(clipSixPoints.flat()),
2,
"a_clipPoint"
);
const clipSixOtherPoints = this.lineGroups.reduce(
(accumulator, lineGroup) => accumulator.concat(lineGroup.projectedGeoLines),
[]
).map((projectedGeoLine) => [
projectedGeoLine[1],
projectedGeoLine[1],
projectedGeoLine[1],
projectedGeoLine[0],
projectedGeoLine[0],
projectedGeoLine[0]
]).flat().map(
(point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point)
);
createBuffer(
gl,
program,
new Float32Array(clipSixOtherPoints.flat()),
2,
"a_clipOtherPoint"
);
const clipSixPreviousPoints = this.lineGroups.reduce(
(accumulator, lineGroup) => accumulator.concat(
lineGroup.projectedGeoPreviousLines || lineGroup.projectedGeoLines
),
[]
).map((projectedGeoLine) => [
projectedGeoLine[0],
projectedGeoLine[0],
projectedGeoLine[0],
projectedGeoLine[1],
projectedGeoLine[1],
projectedGeoLine[1]
]).flat().map(
(point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point)
);
createBuffer(
gl,
program,
new Float32Array(clipSixPreviousPoints.flat()),
2,
"a_clipPreviousPoint"
);
const clipSixPreviousOtherPoints = this.lineGroups.reduce(
(accumulator, lineGroup) => accumulator.concat(
lineGroup.projectedGeoPreviousLines || lineGroup.projectedGeoLines
),
[]
).map((projectedGeoLine) => [
projectedGeoLine[1],
projectedGeoLine[1],
projectedGeoLine[1],
projectedGeoLine[0],
projectedGeoLine[0],
projectedGeoLine[0]
]).flat().map(
(point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point)
);
createBuffer(
gl,
program,
new Float32Array(clipSixPreviousOtherPoints.flat()),
2,
"a_clipPreviousOtherPoint"
);
const sixIsOtherPoints = this.lineGroups.reduce(
(accumulator, lineGroup) => accumulator.concat(
lineGroup.projectedGeoLines.flatMap((_projectedGeoLine) => [
0,
0,
1,
0,
0,
1
])
),
[]
);
createBuffer(
gl,
program,
new Float32Array(sixIsOtherPoints),
1,
"a_isOtherPoint"
);
const sixNormalSigns = this.lineGroups.reduce(
(accumulator, lineGroup) => accumulator.concat(
lineGroup.projectedGeoLines.flatMap((_projectedGeoLine) => [
1,
-1,
1,
1,
-1,
1
])
),
[]
);
createBuffer(
gl,
program,
new Float32Array(sixNormalSigns),
1,
"a_normalSign"
);
const viewportSizes = this.lineGroups.reduce(
(accumulator, lineGroup) => accumulator.concat(
lineGroup.projectedGeoLines.flatMap(
(_projectedGeoLine) => Array(6).fill(
lineGroup.viewportSize ?? DEFAULT_RENDER_LINE_GROUP_OPTIONS.viewportSize
)
)
),
[]
);
createBuffer(
gl,
program,
new Float32Array(viewportSizes),
1,
"a_viewportSize"
);
const colors = this.lineGroups.reduce(
(accumulator, lineGroup) => {
const color = hexToFractionalOpaqueRgba(
lineGroup.color ?? DEFAULT_RENDER_LINE_GROUP_OPTIONS.color
);
return accumulator.concat(
lineGroup.projectedGeoLines.flatMap(
(_projectedGeoLine) => Array(6).fill(color)
)
);
},
[]
);
createBuffer(gl, program, new Float32Array(colors.flat()), 4, "a_color");
const viewportBorderSizes = this.lineGroups.reduce(
(accumulator, lineGroup) => accumulator.concat(
lineGroup.projectedGeoLines.flatMap(
(_projectedGeoLine) => Array(6).fill(
lineGroup.viewportBorderSize ?? DEFAULT_RENDER_LINE_GROUP_OPTIONS.viewportBorderSize
)
)
),
[]
);
createBuffer(
gl,
program,
new Float32Array(viewportBorderSizes),
1,
"a_viewportBorderSize"
);
const borderColors = this.lineGroups.reduce(
(accumulator, lineGroup) => {
const color = hexToFractionalOpaqueRgba(
lineGroup.borderColor ?? DEFAULT_RENDER_LINE_GROUP_OPTIONS.borderColor
);
return accumulator.concat(
lineGroup.projectedGeoLines.flatMap(
(_projectedGeoLine) => Array(6).fill(color)
)
);
},
[]
);
createBuffer(
gl,
program,
new Float32Array(borderColors.flat()),
4,
"a_borderColor"
);
}
updateVertexBuffersPoints(projectedGeoToClipHomogeneousTransform) {
if (!this.pointsVao) {
return;
}
const gl = this.gl;
const program = this.pointsProgram;
gl.bindVertexArray(this.pointsVao);
this.setPointGroups();
const clipPoints = this.pointGroups.reduce(
(accumulator, pointGroup) => accumulator.concat(pointGroup.projectedGeoPoints),
[]
).map(
(point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point)
);
createBuffer(
gl,
program,
new Float32Array(clipPoints.flat()),
2,
"a_clipPoint"
);
const clipPreviousPoints = this.pointGroups.reduce(
(accumulator, pointGroup) => accumulator.concat(
pointGroup.projectedGeoPreviousPoints || pointGroup.projectedGeoPoints
),
[]
).map(
(point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point)
);
createBuffer(
gl,
program,
new Float32Array(clipPreviousPoints.flat()),
2,
"a_clipPreviousPoint"
);
const viewportSizes = this.pointGroups.reduce(
(accumulator, pointGroup) => accumulator.concat(
pointGroup.projectedGeoPoints.map(
(_projectedGeoPoint) => pointGroup.viewportSize ?? DEFAULT_RENDER_POINT_GROUP_OPTION.viewportSize
)
),
[]
);
createBuffer(
gl,
program,
new Float32Array(viewportSizes),
1,
"a_viewportSize"
);
const colors = this.pointGroups.reduce(
(accumulator, pointGroup) => {
const color = hexToFractionalOpaqueRgba(
pointGroup.color ?? DEFAULT_RENDER_POINT_GROUP_OPTION.color
);
return accumulator.concat(
pointGroup.projectedGeoPoints.map((_projectedGeoPoint) => color)
);
},
[]
);
createBuffer(gl, program, new Float32Array(colors.flat()), 4, "a_color");
const viewportBorderSizes = this.pointGroups.reduce(
(accumulator, pointGroup) => accumulator.concat(
pointGroup.projectedGeoPoints.map(
(_projectedGeoPoint) => pointGroup.viewportBorderSize ?? DEFAULT_RENDER_POINT_GROUP_OPTION.viewportBorderSize
)
),
[]
);
createBuffer(
gl,
program,
new Float32Array(viewportBorderSizes),
1,
"a_viewportBorderSize"
);
const borderColors = this.pointGroups.reduce(
(accumulator, pointGroup) => {
const color = hexToFractionalOpaqueRgba(
pointGroup.borderColor ?? DEFAULT_RENDER_POINT_GROUP_OPTION.borderColor
);
return accumulator.concat(
pointGroup.projectedGeoPoints.map((_projectedGeoPoint) => color)
);
},
[]
);
createBuffer(
gl,
program,
new Float32Array(borderColors.flat()),
4,
"a_borderColor"
);
}
async updateTextures() {
const gl = this.gl;
this.updateCachedTilesForTextures();
if (this.cachedTilesForTexture.length == 0 || this.cachedTilesForTexture.length !== 0 && subSetArray(
this.previousCachedTilesForTexture.map(
(textureTile) => textureTile.fetchableTile.tileUrl
),
this.cachedTilesForTexture.map(
(textureTile) => textureTile.fetchableTile.tileUrl
)
)) {
return;
}
if (!this.image) {
return;
}
const requiredTextureWidth = this.tileSize[0];
const requiredTextureHeigt = this.tileSize[1];
const requiredTextureDepth = this.cachedTilesForTexture.length;
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.cachedTilesTextureArray);
gl.texImage3D(
gl.TEXTURE_2D_ARRAY,
0,
gl.RGBA,
requiredTextureWidth,
requiredTextureHeigt,
requiredTextureDepth,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null
);
for (let i = 0; i < this.cachedTilesForTexture.length; i++) {
const imageData = this.cachedTilesForTexture[i].data;
if (imageData.width !== requiredTextureWidth || imageData.width !== requiredTextureWidth) {
throw new Error("Cached tile doesn't fit in texture");
}
const pbo = gl.createBuffer();
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo);
gl.bufferData(gl.PIXEL_UNPACK_BUFFER, imageData.data, gl.STATIC_DRAW);
gl.texSubImage3D(
gl.TEXTURE_2D_ARRAY,
0,
0,
0,
i,
imageData.width,
imageData.height,
1,
gl.RGBA,
gl.UNSIGNED_BYTE,
0
);
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null);
}
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
const cachedTilesResourceOriginPointsAndSizes = this.cachedTilesForTexture.map((textureTile) => {
if (textureTile && textureTile.fetchableTile && textureTile.fetchableTile.options && textureTile.fetchableTile.options.imageRequest && textureTile.fetchableTile.options.imageRequest.region) {
return [
textureTile.fetchableTile.options.imageRequest.region.x,
textureTile.fetchableTile.options.imageRequest.region.y,
textureTile.fetchableTile.options.imageRequest.region.width,
textureTile.fetchableTile.options.imageRequest.region.height
];
} else {
throw new Error("Missing resource origin points and sizes");
}
});
gl.bindTexture(
gl.TEXTURE_2D,
this.cachedTilesResourceOriginPointsAndSizesTexture
);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.R32I,
1,
this.cachedTilesForTexture.length * 4,
0,
gl.RED_INTEGER,
gl.INT,
new Int32Array(cachedTilesResourceOriginPointsAndSizes.flat())
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
const cachedTilesScaleFactors = this.cachedTilesForTexture.map(
(textureTile) => textureTile.fetchableTile.tile.tileZoomLevel.scaleFactor
);
gl.bindTexture(gl.TEXTURE_2D, this.cachedTilesScaleFactorsTexture);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.R32I,
1,
this.cachedTilesForTexture.length,
0,
gl.RED_INTEGER,
gl.INT,
new Int32Array(cachedTilesScaleFactors)
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
this.dispatchEvent(new WarpedMapEvent(WarpedMapEventType.TEXTURESUPDATED));
}
updateCachedTilesForTextures() {
const cachedTiles = [];
const cachedTilesAtOtherScaleFactors = [];
const overviewCachedTiles = [];
const spriteCachedTiles = [];
for (const fetchableTile of this.fetchableTilesForViewport) {
const cachedTile = this.cachedTilesByTileUrl.get(fetchableTile.tileUrl);
if (cachedTile) {
cachedTiles.push(cachedTile);
} else {
for (const cachedTile2 of this.getCachedTilesAtOtherScaleFactors(
fetchableTile.tile
)) {
cachedTilesAtOtherScaleFactors.push(cachedTile2);
}
}
}
spriteCachedTiles.push(
...Array.from(this.cachedTilesByTileUrl.values()).filter(
(cachedTile) => cachedTile.isTileFromSprites()
)
);
for (const fetchableTile of this.overviewFetchableTilesForViewport) {
const cachedTile = this.cachedTilesByTileUrl.get(fetchableTile.tileUrl);
if (cachedTile) {
const tileZoolLevelTilesCount = this.tileZoomLevelForViewport ? this.tileZoomLevelForViewport.rows * this.tileZoomLevelForViewport.columns : void 0;
if (cachedTiles.length === 0 || tileZoolLevelTilesCount && cachedTiles.length < tileZoolLevelTilesCount) {
overviewCachedTiles.push(cachedTile);
}
}
}
let cachedTilesForTextures = [
...cachedTiles,
...cachedTilesAtOtherScaleFactors,
...spriteCachedTiles,
...overviewCachedTiles
];
const cachedTilesForTexturesByTileUrl = /* @__PURE__ */ new Map();
cachedTilesForTextures.forEach(
(cachedTile) => cachedTilesForTexturesByTileUrl.set(
cachedTile.fetchableTile.tileUrl,
cachedTile
)
);
cachedTilesForTextures = [...cachedTilesForTexturesByTileUrl.values()];
this.previousCachedTilesForTexture = this.cachedTilesForTexture;
this.cachedTilesForTexture = cachedTilesForTextures;
return;
}
getCachedTilesAtOtherScaleFactors(tile) {
if (this.cachedTilesByTileUrl.size === 0) {
return [];
}
if (!this.tileZoomLevelForViewport) {
return [];
}
const cachedTiles = [];
for (tile of getTilesAtOtherScaleFactors(
tile,
this.image,
this.tileZoomLevelForViewport.scaleFactor,
TEXTURES_MAX_LOWER_LOG2_SCALE_FACTOR_DIFF,
TEXTURES_MAX_HIGHER_LOG2_SCALE_FACTOR_DIFF,
this.tileInCachedTiles.bind(this)
// Only consider tiles in cache,
)) {
const cachedTile = this.tileToCachedTile(tile);
if (cachedTile) {
cachedTiles.push(cachedTile);
} else {
throw new Error("Tile supposed to be in cache isn't.");
}
}
return cachedTiles;
}
// Lookup by tileKey (zoomlevel, row, column) instead of tileUrl
// Because computing the tileUrl for every tile is expensive
tileToCachedTile(tile) {
return this.cachedTilesByTileKey.get(tileKey(tile));
}
tileInCachedTiles(tile) {
return this.cachedTilesByTileKey.has(tileKey(tile));
}
}
export {
WebGL2WarpedMap,
createWebGL2WarpedMapFactory
};
//# sourceMappingURL=WebGL2WarpedMap.js.map