utils/VectorTileCreator.tstypescript
import { doubleToTwoFloats2 } from "../math/coder";
import { Framebuffer } from "../webgl/Framebuffer";
import { Handler } from "../webgl/Handler";
import { Material } from "../layer/Material";
import { ShaderProgram } from "../webgl/ShaderProgram";
import { Planet } from "../scene/Planet";
import { NOTRENDERING } from "../quadTree/quadTree";
import { Vector } from "../layer/Vector";
let tempArr = new Float32Array(2);
const MAX_FRAME_TIME = 50.0;
export class VectorTileCreator {
protected _width: number;
protected _height: number;
protected _planet: Planet;
protected _framebuffer: Framebuffer | null;
protected _queue: Material[];
protected _handler: Handler | null;
constructor(planet: Planet, width: number = 256, height: number = 256) {
this._width = width;
this._height = height;
this._planet = planet;
this._framebuffer = null;
this._queue = [];
this._handler = null;
}
public init() {
this._handler = this._planet.renderer!.handler;
//Line
if (!this._handler.programs.vectorTileLineRasterization) {
this._handler.addProgram(
new ShaderProgram("vectorTileLineRasterization", {
uniforms: {
viewport: "vec2",
thicknessOutline: "float",
alpha: "float",
extentParamsHigh: "vec4",
extentParamsLow: "vec4"
},
attributes: {
prevHigh: "vec2",
currentHigh: "vec2",
nextHigh: "vec2",
prevLow: "vec2",
currentLow: "vec2",
nextLow: "vec2",
order: "float",
color: "vec4",
thickness: "float"
},
vertexShader: `attribute vec2 prevHigh;
attribute vec2 currentHigh;
attribute vec2 nextHigh;
attribute vec2 prevLow;
attribute vec2 currentLow;
attribute vec2 nextLow;
attribute float order;
attribute float thickness;
attribute vec4 color;
uniform float thicknessOutline;
uniform vec2 viewport;
uniform vec4 extentParamsHigh;
uniform vec4 extentParamsLow;
varying vec4 vColor;
vec2 proj(vec2 coordHigh, vec2 coordLow) {
vec2 highDiff = coordHigh - extentParamsHigh.xy;
vec2 lowDiff = coordLow - extentParamsLow.xy;
return vec2(-1.0 + (highDiff * step(1.0, length(highDiff)) + lowDiff) * extentParamsHigh.zw) * vec2(1.0, -1.0);
}
vec2 getIntersection(vec2 start1, vec2 end1, vec2 start2, vec2 end2, float eps) {
vec2 dir = end2 - start2;
vec2 perp = vec2(-dir.y, dir.x);
float d2 = dot(perp, start2);
float seg = dot(perp, start1) - d2;
float prl = seg - dot(perp, end1) + d2;
if (prl > -eps && prl < eps) {
return start1;
}
float u = seg / prl;
return start1 + u * (end1 - start1);
}
vec2 safeNormalize(vec2 v, vec2 fallback, float eps) {
float len2 = dot(v, v);
if (len2 > eps * eps) {
return v * inversesqrt(len2);
}
return fallback;
}
void main(){
vColor = color;
float eps = 2.0 / max(viewport.x, viewport.y);
vec2 vNext = proj(nextHigh, nextLow),
vCurrent = proj(currentHigh, currentLow),
vPrev = proj(prevHigh, prevLow);
vec2 _next = vNext;
vec2 _prev = vPrev;
vec2 _current = vCurrent;
if(distance(_prev, _current) < eps){
if(distance(_next, _current) < eps){
_next = _current + vec2(1.0, 0.0);
_prev = _current - vec2(1.0, 0.0);
}else{
_prev = _current + safeNormalize(_current - _next, vec2(1.0, 0.0), eps);
}
}
if(distance(_next, _current) < eps){
_next = _current + safeNormalize(_current - _prev, vec2(-1.0, 0.0), eps);
}
vec2 sNext = _next;
vec2 sCurrent = _current;
vec2 sPrev = _prev;
vec2 dirNext = safeNormalize(sNext - sCurrent, vec2(1.0, 0.0), eps);
vec2 dirPrev = safeNormalize(sPrev - sCurrent, -dirNext, eps);
float dotNP = clamp(dot(dirNext, dirPrev), -1.0, 1.0);
vec2 normalNext = safeNormalize(vec2(-dirNext.y, dirNext.x), vec2(0.0, 1.0), eps);
vec2 normalPrev = safeNormalize(vec2(dirPrev.y, -dirPrev.x), vec2(0.0, 1.0), eps);
vec2 sideNormal = abs(order) == 1.0 ? normalPrev : normalNext;
float side = sign(order);
vec2 d = (thickness + thicknessOutline) * 0.5 * side / viewport;
vec2 m;
float lenNext = distance(sCurrent, sNext);
float lenPrev = distance(sCurrent, sPrev);
if(lenNext < eps || lenPrev < eps){
m = sCurrent + sideNormal * d;
}else if(dotNP >= 0.99991){
m = sCurrent - normalPrev * d;
}else{
m = getIntersection(sCurrent + normalPrev * d, sPrev + normalPrev * d,
sCurrent + normalNext * d, sNext + normalNext * d, eps);
if( dotNP > 0.5 && dot(dirNext + dirPrev, m - sCurrent) < 0.0 ){
float occw = order * sign(dirNext.x * dirPrev.y - dirNext.y * dirPrev.x);
if(occw == -1.0){
m = sCurrent + normalPrev * d;
}else if(occw == 1.0){
m = sCurrent + normalNext * d;
}else if(occw == -2.0){
m = sCurrent + normalNext * d;
}else if(occw == 2.0){
m = sCurrent + normalPrev * d;
}
}else if(distance(sCurrent, m) > min(distance(sCurrent, sNext), distance(sCurrent, sPrev))){
m = sCurrent + sideNormal * d;
}
}
float sameSide = dot(m - sCurrent, sideNormal) * side;
float wrongSide = 1.0 - step(0.0, sameSide);
m = mix(m, sCurrent + sideNormal * d, wrongSide);
gl_Position = vec4(m.x, m.y, 0.0, 1.0);
}`,
fragmentShader: `precision highp float;
uniform float alpha;
varying vec4 vColor;
void main() {
gl_FragColor = vec4(vColor.rgb, alpha * vColor.a);
}`
})
);
}
//Polygon
if (!this._handler.programs.vectorTilePolygonRasterization) {
this._handler.addProgram(
new ShaderProgram("vectorTilePolygonRasterization", {
uniforms: {
extentParamsHigh: "vec4",
extentParamsLow: "vec4"
},
attributes: {
coordinatesHigh: "vec2",
coordinatesLow: "vec2",
colors: "vec4"
},
vertexShader: `attribute vec2 coordinatesHigh;
attribute vec2 coordinatesLow;
attribute vec4 colors;
uniform vec4 extentParamsHigh;
uniform vec4 extentParamsLow;
varying vec4 color;
vec2 proj(vec2 coordHigh, vec2 coordLow) {
vec2 highDiff = coordHigh - extentParamsHigh.xy;
vec2 lowDiff = coordLow - extentParamsLow.xy;
return vec2(-1.0 + (highDiff * step(1.0, length(highDiff)) + lowDiff) * extentParamsHigh.zw) * vec2(1.0, -1.0);
}
void main() {
color = colors;
gl_Position = vec4(proj(coordinatesHigh, coordinatesLow), 0.0, 1.0);
}`,
fragmentShader: `precision highp float;
varying vec4 color;
void main () {
gl_FragColor = color;
}`
})
);
}
this._framebuffer = new Framebuffer(this._handler, {
width: this._width,
height: this._height,
useDepth: false
});
this._framebuffer.init();
}
/**
* Returns true when there is nothing left to rasterize.
* @public
* @returns {boolean}
*/
public get isIdle(): boolean {
return this._queue.length === 0;
}
public frame() {
if (this._planet.layerLock.isFree() && this._queue.length) {
let h = this._handler!,
gl = h.gl!;
gl.disable(gl.CULL_FACE);
gl.disable(gl.DEPTH_TEST);
let hLine = h.programs.vectorTileLineRasterization,
hPoly = h.programs.vectorTilePolygonRasterization;
let _w = this._width,
_h = this._height,
width = _w,
height = _h,
_w2 = width << 1,
_h2 = height << 1;
// var prevLayerId = -1;
let extentParamsHigh = new Float32Array(4);
let extentParamsLow = new Float32Array(4);
let f = this._framebuffer!.activate();
let deltaTime = 0,
startTime = window.performance.now();
while (this._queue.length && deltaTime < MAX_FRAME_TIME) {
let material = this._queue.shift()!;
if (material.isLoading && material.segment.node.getState() !== NOTRENDERING) {
let pickingEnabled = material.layer._pickingEnabled;
if (material.segment.tileZoom < 4) {
width = _w2;
height = _h2;
} else {
width = _w;
height = _h;
}
let texture = material._updateTexture || h.createEmptyTexture_l(width, height);
let pickingMask = pickingEnabled
? material._updatePickingMask || h.createEmptyTexture_n(width, height)
: null;
material.applyTexture(texture, pickingMask);
f.setSize(width, height);
f.bindOutputTexture(texture!);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
let extent = material.segment.getExtentMerc();
doubleToTwoFloats2(extent.southWest.lon, tempArr);
extentParamsHigh[0] = tempArr[0];
extentParamsLow[0] = tempArr[1];
doubleToTwoFloats2(extent.southWest.lat, tempArr);
extentParamsHigh[1] = tempArr[0];
extentParamsLow[1] = tempArr[1];
extentParamsHigh[2] = 2.0 / extent.getWidth();
extentParamsHigh[3] = 2.0 / extent.getHeight();
hPoly.activate();
let sh = hPoly;
let sha = sh.attributes,
shu = sh.uniforms;
let geomHandler = (material.layer as Vector)._geometryHandler;
//=========================================
//Polygon rendering
//=========================================
gl.uniform4fv(shu.extentParamsHigh, extentParamsHigh);
gl.uniform4fv(shu.extentParamsLow, extentParamsLow);
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._polyVerticesHighBufferMerc!);
gl.vertexAttribPointer(
sha.coordinatesHigh,
geomHandler._polyVerticesHighBufferMerc!.itemSize,
gl.FLOAT,
false,
0,
0
);
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._polyVerticesLowBufferMerc!);
gl.vertexAttribPointer(
sha.coordinatesLow,
geomHandler._polyVerticesLowBufferMerc!.itemSize,
gl.FLOAT,
false,
0,
0
);
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._polyColorsBuffer!);
gl.vertexAttribPointer(sha.colors, geomHandler._polyColorsBuffer!.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geomHandler._polyIndexesBuffer!);
gl.drawElements(gl.TRIANGLES, geomHandler._polyIndexesBuffer!.numItems, gl.UNSIGNED_INT, 0);
//Polygon picking PASS
if (pickingEnabled) {
f.bindOutputTexture(pickingMask!);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._polyPickingColorsBuffer!);
gl.vertexAttribPointer(
sha.colors,
geomHandler._polyPickingColorsBuffer!.itemSize,
gl.FLOAT,
false,
0,
0
);
gl.drawElements(gl.TRIANGLES, geomHandler._polyIndexesBuffer!.numItems, gl.UNSIGNED_INT, 0);
}
//=========================================
//Strokes and linestrings rendering
//=========================================
f.bindOutputTexture(texture!);
hLine.activate();
sh = hLine;
sha = sh.attributes;
shu = sh.uniforms;
gl.uniform2fv(shu.viewport, [width, height]);
gl.uniform4fv(shu.extentParamsHigh, extentParamsHigh);
gl.uniform4fv(shu.extentParamsLow, extentParamsLow);
//vertex
let mb = geomHandler._lineVerticesHighBufferMerc!;
gl.bindBuffer(gl.ARRAY_BUFFER, mb);
gl.vertexAttribPointer(sha.prevHigh, mb.itemSize, gl.FLOAT, false, 8, 0);
gl.vertexAttribPointer(sha.currentHigh, mb.itemSize, gl.FLOAT, false, 8, 32);
gl.vertexAttribPointer(sha.nextHigh, mb.itemSize, gl.FLOAT, false, 8, 64);
mb = geomHandler._lineVerticesLowBufferMerc!;
gl.bindBuffer(gl.ARRAY_BUFFER, mb);
gl.vertexAttribPointer(sha.prevLow, mb.itemSize, gl.FLOAT, false, 8, 0);
gl.vertexAttribPointer(sha.currentLow, mb.itemSize, gl.FLOAT, false, 8, 32);
gl.vertexAttribPointer(sha.nextLow, mb.itemSize, gl.FLOAT, false, 8, 64);
//order
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineOrdersBuffer!);
gl.vertexAttribPointer(sha.order, geomHandler._lineOrdersBuffer!.itemSize, gl.FLOAT, false, 4, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geomHandler._lineIndexesBuffer!);
//PASS - stroke
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineStrokesBuffer!);
gl.vertexAttribPointer(
sha.thickness,
geomHandler._lineStrokesBuffer!.itemSize,
gl.FLOAT,
false,
0,
0
);
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineStrokeColorsBuffer!);
gl.vertexAttribPointer(
sha.color,
geomHandler._lineStrokeColorsBuffer!.itemSize,
gl.FLOAT,
false,
0,
0
);
//Antialias pass
gl.uniform1f(shu.thicknessOutline, 2);
gl.uniform1f(shu.alpha, 0.54);
gl.drawElements(gl.TRIANGLE_STRIP, geomHandler._lineIndexesBuffer!.numItems, gl.UNSIGNED_INT, 0);
//
//Aliased pass
gl.uniform1f(shu.thicknessOutline, 1);
gl.uniform1f(shu.alpha, 1.0);
gl.drawElements(gl.TRIANGLE_STRIP, geomHandler._lineIndexesBuffer!.numItems, gl.UNSIGNED_INT, 0);
//PASS - inside line
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineThicknessBuffer!);
gl.vertexAttribPointer(
sha.thickness,
geomHandler._lineThicknessBuffer!.itemSize,
gl.FLOAT,
false,
0,
0
);
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineColorsBuffer!);
gl.vertexAttribPointer(sha.color, geomHandler._lineColorsBuffer!.itemSize, gl.FLOAT, false, 0, 0);
//Antialias pass
gl.uniform1f(shu.thicknessOutline, 2);
gl.uniform1f(shu.alpha, 0.54);
gl.drawElements(gl.TRIANGLE_STRIP, geomHandler._lineIndexesBuffer!.numItems, gl.UNSIGNED_INT, 0);
//
//Aliased pass
gl.uniform1f(shu.thicknessOutline, 1);
gl.uniform1f(shu.alpha, 1.0);
gl.drawElements(gl.TRIANGLE_STRIP, geomHandler._lineIndexesBuffer!.numItems, gl.UNSIGNED_INT, 0);
if (pickingEnabled) {
f.bindOutputTexture(pickingMask!);
gl.uniform1f(shu.thicknessOutline, 8);
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._linePickingColorsBuffer!);
gl.vertexAttribPointer(
sha.color,
geomHandler._linePickingColorsBuffer!.itemSize,
gl.FLOAT,
false,
0,
0
);
gl.drawElements(
gl.TRIANGLE_STRIP,
geomHandler._lineIndexesBuffer!.numItems,
gl.UNSIGNED_INT,
0
);
}
} else {
material.isLoading = false;
}
deltaTime = window.performance.now() - startTime;
}
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
f.deactivate();
}
}
public add(material: Material) {
this._queue.push(material);
}
public remove(material: Material) {
//...
}
public get queueSize(): number {
return this._queue.length;
}
}