utils/GeoImageCreator.tstypescript
import * as utils from "../utils/shared";
import { BaseGeoImage } from "../layer/BaseGeoImage";
import { Framebuffer } from "../webgl/Framebuffer";
import { LonLat } from "../LonLat";
import { ShaderProgram } from "../webgl/ShaderProgram";
import { Planet } from "../scene/Planet";
import { doubleToTwoFloats2 } from "../math/coder";
import type { WebGLBufferExt, WebGLTextureExt } from "../webgl/Handler";
export class GeoImageCreator {
public MAX_FRAMES: number;
protected _gridSize: number;
protected _planet: Planet;
public _framebuffer: Framebuffer | null;
protected _framebufferMercProj: Framebuffer | null;
public _texCoordsBuffer: WebGLBufferExt | null;
public _indexBuffer: WebGLBufferExt | null;
protected _currentFrame: number;
protected _queue: BaseGeoImage[];
protected _animate: BaseGeoImage[];
protected _quadTexCoordsBuffer: WebGLBufferExt | null;
protected _quadVertexBuffer: WebGLTextureExt | null;
constructor(planet: Planet, maxFrames: number = 5) {
this.MAX_FRAMES = maxFrames;
this._gridSize = 64;
this._planet = planet;
this._framebuffer = null;
this._framebufferMercProj = null;
this._texCoordsBuffer = null;
this._indexBuffer = null;
this._currentFrame = 0;
this._queue = [];
this._animate = [];
this._quadTexCoordsBuffer = null;
this._quadVertexBuffer = null;
}
public init() {
this._initShaders();
this._initBuffers();
}
/**
* Creates geoImage corners coordinates grid buffer.
* @public
* @param{Array.<LonLat>} c - GeoImage corners coordinates.
* @param{boolean} [toMerc=false] - Transform to web mercator.
* @return{WebGLBuffer} Grid coordinates buffer.
*/
public createGridBuffer(c: LonLat[], toMerc: boolean = false): [WebGLBufferExt, WebGLBufferExt] {
let gs = this._gridSize;
let v03 = new LonLat((c[3].lon - c[0].lon) / gs, (c[3].lat - c[0].lat) / gs),
v12 = new LonLat((c[2].lon - c[1].lon) / gs, (c[2].lat - c[1].lat) / gs),
v01 = new LonLat((c[1].lon - c[0].lon) / gs, (c[1].lat - c[0].lat) / gs),
v32 = new LonLat((c[2].lon - c[3].lon) / gs, (c[2].lat - c[3].lat) / gs);
const size = (gs + 1) * (gs + 1) * 2,
halfSize = size / 2;
let gridHigh = new Float32Array(size),
gridLow = new Float32Array(size);
let lonLatArr = new Array(halfSize);
let kh = 0,
kl = 0,
k = 0;
let tempArr = new Float32Array(2);
for (let i = 0; i <= gs; i++) {
let P03i = new LonLat(c[0].lon + i * v03.lon, c[0].lat + i * v03.lat),
P12i = new LonLat(c[1].lon + i * v12.lon, c[1].lat + i * v12.lat);
for (let j = 0; j <= gs; j++) {
let P01j = new LonLat(c[0].lon + j * v01.lon, c[0].lat + j * v01.lat),
P32j = new LonLat(c[3].lon + j * v32.lon, c[3].lat + j * v32.lat);
let xx = utils.getLinesIntersectionLonLat(P03i, P12i, P01j, P32j)!;
doubleToTwoFloats2(xx.lon, tempArr);
gridHigh[kh++] = tempArr[0];
gridLow[kl++] = tempArr[1];
doubleToTwoFloats2(xx.lat, tempArr);
gridHigh[kh++] = tempArr[0];
gridLow[kl++] = tempArr[1];
lonLatArr[k++] = xx;
}
}
if (toMerc) {
for (let i = 0; i < halfSize; i++) {
let c = lonLatArr[i].forwardMercator();
doubleToTwoFloats2(c.lon, tempArr);
gridHigh[i * 2] = tempArr[0];
gridLow[i * 2] = tempArr[1];
doubleToTwoFloats2(c.lat, tempArr);
gridHigh[i * 2 + 1] = tempArr[0];
gridLow[i * 2 + 1] = tempArr[1];
}
}
return [
this._planet.renderer!.handler.createArrayBuffer(gridHigh, 2, halfSize),
this._planet.renderer!.handler.createArrayBuffer(gridLow, 2, halfSize)
];
}
/**
* Returns true when there is nothing left to rasterize and no animated
* geo images(i.e. GeoVideo) are being played.
* @public
* @returns {boolean}
*/
public get isIdle(): boolean {
if (this._queue.length !== 0) {
return false;
}
for (let i = 0; i < this._animate.length; i++) {
if (this._animate[i].getVisibility()) {
return false;
}
}
return true;
}
public frame() {
let i = this.MAX_FRAMES;
while (i-- && this._queue.length) {
const q = this._queue.shift()!;
q._isRendering = false;
q.rendering();
q.events.dispatch(q.events.loadend);
}
i = this._animate.length;
while (i--) {
let ai = this._animate[i];
if (ai.getVisibility()) {
ai.rendering();
}
}
}
public add(geoImage: BaseGeoImage) {
if (!geoImage._isRendering) {
geoImage._isRendering = true;
if (geoImage._animate) {
this._animate.push(geoImage);
} else {
this._queue.push(geoImage);
}
this._planet.renderer?.requestRedraw();
}
}
public remove(geoImage: BaseGeoImage) {
geoImage._creationProceeding = false;
geoImage._isRendering = false;
for (let i = 0; i < this._queue.length; i++) {
if (this._queue[i].isEqual(geoImage)) {
this._queue.splice(i, 1);
break;
}
}
for (let i = 0; i < this._animate.length; i++) {
if (this._animate[i].isEqual(geoImage)) {
this._animate.splice(i, 1);
break;
}
}
}
protected _initBuffers() {
let h = this._planet.renderer!.handler!;
this._framebuffer = new Framebuffer(h, { width: 2, height: 2, useDepth: false });
this._framebuffer.init();
this._framebufferMercProj = new Framebuffer(h, { width: 2, height: 2, useDepth: false });
this._framebufferMercProj.init();
let gs = Math.log2(this._gridSize);
this._texCoordsBuffer = this._planet._textureCoordsBufferCache[gs];
this._indexBuffer = this._planet._indexesCache[gs][gs][gs][gs][gs].buffer;
this._quadTexCoordsBuffer = h.createArrayBuffer(new Float32Array([0, 1, 1, 1, 0, 0, 1, 0]), 2, 4);
this._quadVertexBuffer = h.createArrayBuffer(new Float32Array([-1, 1, 1, 1, -1, -1, 1, -1]), 2, 4);
}
protected _initShaders() {
this._planet.renderer!.handler.addProgram(
new ShaderProgram("geoImageTransform", {
uniforms: {
sourceTexture: "sampler2d",
extentParamsHigh: "vec4",
extentParamsLow: "vec4",
isFullExtent: "bool",
decodeSourceSRGB: "bool"
},
attributes: {
cornersHigh: "vec2",
cornersLow: "vec2",
texCoords: "vec2"
},
vertexShader: `attribute vec2 cornersHigh;
attribute vec2 cornersLow;
attribute vec2 texCoords;
uniform vec4 extentParamsHigh;
uniform vec4 extentParamsLow;
varying vec2 v_texCoords;
void main() {
v_texCoords = texCoords;
vec2 highDiff = cornersHigh - extentParamsHigh.xy;
vec2 lowDiff = cornersLow - extentParamsLow.xy;
gl_Position = vec4((-1.0 + (highDiff * step(1.0, length(highDiff)) + lowDiff) * extentParamsHigh.zw) * vec2(1.0, -1.0), 0.0, 1.0);
}`,
fragmentShader: `precision highp float;
uniform sampler2D sourceTexture;
uniform bool isFullExtent;
uniform bool decodeSourceSRGB;
varying vec2 v_texCoords;
void main () {
if(!isFullExtent && (v_texCoords.x <= 0.001 || v_texCoords.x >= 0.999 ||
v_texCoords.y <= 0.001 || v_texCoords.y >= 0.999)) {
discard;
}
vec4 color = texture2D(sourceTexture, v_texCoords);
if (decodeSourceSRGB) {
color.rgb = pow(color.rgb, vec3(2.2));
}
gl_FragColor = color;
}`
})
);
}
}