s2maps-gpu
Version:
S2 Maps GPU - An open source, high-performance, and GPU-accelerated map engine for rendering large-scale, interactive maps.
131 lines (130 loc) • 4.41 kB
JavaScript
import Workflow, { Feature } from './workflow.js';
import frag1 from '../shaders/shade1.fragment.glsl';
import vert1 from '../shaders/shade1.vertex.glsl';
import frag2 from '../shaders/shade2.fragment.glsl';
import vert2 from '../shaders/shade2.vertex.glsl';
/** Shape Feature is a standalone shade render storage unit that can be drawn to the GPU */
export class ShadeFeature extends Feature {
layerGuide;
workflow;
source;
featureCode;
tile;
type = 'shade';
maskLayer = true;
/**
* @param layerGuide - layer guide for this feature
* @param workflow - the shade workflow
* @param source - the input mask source
* @param featureCode - the encoded feature code that tells the GPU how to compute it's properties
* @param tile - the tile that the feature is drawn on
*/
constructor(layerGuide, workflow, source, featureCode, tile) {
super(workflow, tile, layerGuide, featureCode);
this.layerGuide = layerGuide;
this.workflow = workflow;
this.source = source;
this.featureCode = featureCode;
this.tile = tile;
}
/** Draw this feature to the GPU */
draw() {
super.draw();
this.workflow.draw(this);
}
/**
* Duplicate this feature
* @param tile - the tile that the feature is drawn on
* @returns the duplicated feature
*/
duplicate(tile) {
const { layerGuide, workflow, source, featureCode } = this;
return new ShadeFeature(layerGuide, workflow, source, featureCode, tile);
}
}
/** Shade Workflow */
export default class ShadeWorkflow extends Workflow {
label = 'shade';
/** @param context - The WebGL(1|2) context */
constructor(context) {
// get gl from context
const { type, devicePixelRatio } = context;
// inject Program
super(context);
// build shaders
if (type === 1)
this.buildShaders(vert1, frag1, { aPos: 0 });
else
this.buildShaders(vert2, frag2);
// activate so we can setup devicePixelRatio
this.use();
// set pixel ratio
this.setDevicePixelRatio(devicePixelRatio);
}
/**
* Build a layer definition for this workflow given the user input layer
* @param layerBase - the common layer attributes
* @param layer - the user defined layer attributes
* @returns a built layer definition that's ready to describe how to render a feature
*/
buildLayerDefinition(layerBase, layer) {
let { color } = layer;
color = color ?? 'rgb(0.6, 0.6, 0.6)';
return {
...layerBase,
type: 'shade',
color,
};
}
/**
* given a set of layerIndexes that use Masks and the tile of interest, build a mask feature
* @param layerDefinition - layer definition
* @param tile - the tile that needs a mask
*/
buildMaskFeature(layerDefinition, tile) {
const { mask, zoom } = tile;
const { minzoom, maxzoom } = layerDefinition;
// not in the zoom range, ignore
if (zoom < minzoom || zoom > maxzoom)
return;
// set the layer guide
const layerGuide = {
...layerDefinition,
sourceName: 'mask',
layerCode: [],
interactive: false,
opaque: false,
};
tile.addFeatures([new ShadeFeature(layerGuide, this, mask, [0], tile)]);
}
/** Use this workflow as the current shaders for the GPU */
use() {
super.use();
// grab context & prep
const { context } = this;
context.enableCullFace();
context.enableDepthTest();
context.disableStencilTest();
context.shadeBlend();
context.lessDepth();
}
/** Set the layer code uniforms for this workflow */
setLayerCode() {
// noop
}
/**
* Draw a shade feature
* @param feature - the feature
*/
draw(feature) {
const { gl, context } = this;
const { source, layerGuide: { layerIndex, visible }, } = feature;
const { count, offset, vao } = source;
if (!visible)
return;
// bind vao & draw
context.setDepthRange(layerIndex);
gl.bindVertexArray(vao);
gl.drawElements(gl.TRIANGLE_STRIP, count, gl.UNSIGNED_INT, offset * 4);
}
}