287 lines
9.2 KiB
JavaScript
287 lines
9.2 KiB
JavaScript
/**
|
|
* @module ol/renderer/webgl/Layer
|
|
*/
|
|
import LayerRenderer from '../Layer.js';
|
|
import RenderEvent from '../../render/Event.js';
|
|
import RenderEventType from '../../render/EventType.js';
|
|
import WebGLHelper from '../../webgl/Helper.js';
|
|
import {
|
|
compose as composeTransform,
|
|
create as createTransform,
|
|
} from '../../transform.js';
|
|
|
|
/**
|
|
* @enum {string}
|
|
*/
|
|
export const WebGLWorkerMessageType = {
|
|
GENERATE_BUFFERS: 'GENERATE_BUFFERS',
|
|
};
|
|
|
|
/**
|
|
* @typedef {Object} WebGLWorkerGenerateBuffersMessage
|
|
* This message will trigger the generation of a vertex and an index buffer based on the given render instructions.
|
|
* When the buffers are generated, the worked will send a message of the same type to the main thread, with
|
|
* the generated buffers in it.
|
|
* Note that any addition properties present in the message *will* be sent back to the main thread.
|
|
* @property {WebGLWorkerMessageType} type Message type
|
|
* @property {ArrayBuffer} renderInstructions Render instructions raw binary buffer.
|
|
* @property {ArrayBuffer} [vertexBuffer] Vertices array raw binary buffer (sent by the worker).
|
|
* @property {ArrayBuffer} [indexBuffer] Indices array raw binary buffer (sent by the worker).
|
|
* @property {number} [customAttributesCount] Amount of custom attributes count in the render instructions.
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} PostProcessesOptions
|
|
* @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than
|
|
* the main canvas that will then be sampled up (useful for saving resource on blur steps).
|
|
* @property {string} [vertexShader] Vertex shader source
|
|
* @property {string} [fragmentShader] Fragment shader source
|
|
* @property {Object<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process step
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} Options
|
|
* @property {string} [className='ol-layer'] A CSS class name to set to the canvas element.
|
|
* @property {Object<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process steps
|
|
* @property {Array<PostProcessesOptions>} [postProcesses] Post-processes definitions
|
|
*/
|
|
|
|
/**
|
|
* @classdesc
|
|
* Base WebGL renderer class.
|
|
* Holds all logic related to data manipulation & some common rendering logic
|
|
* @template {import("../../layer/Layer.js").default} LayerType
|
|
*/
|
|
class WebGLLayerRenderer extends LayerRenderer {
|
|
/**
|
|
* @param {LayerType} layer Layer.
|
|
* @param {Options} [opt_options] Options.
|
|
*/
|
|
constructor(layer, opt_options) {
|
|
super(layer);
|
|
|
|
const options = opt_options || {};
|
|
|
|
/**
|
|
* The transform for viewport CSS pixels to rendered pixels. This transform is only
|
|
* set before dispatching rendering events.
|
|
* @private
|
|
* @type {import("../../transform.js").Transform}
|
|
*/
|
|
this.inversePixelTransform_ = createTransform();
|
|
|
|
/**
|
|
* @type {WebGLHelper}
|
|
* @protected
|
|
*/
|
|
this.helper = new WebGLHelper({
|
|
postProcesses: options.postProcesses,
|
|
uniforms: options.uniforms,
|
|
});
|
|
|
|
if (options.className !== undefined) {
|
|
this.helper.getCanvas().className = options.className;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clean up.
|
|
*/
|
|
disposeInternal() {
|
|
this.helper.dispose();
|
|
delete this.helper;
|
|
|
|
super.disposeInternal();
|
|
}
|
|
|
|
/**
|
|
* @param {import("../../render/EventType.js").default} type Event type.
|
|
* @param {WebGLRenderingContext} context The rendering context.
|
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
|
* @private
|
|
*/
|
|
dispatchRenderEvent_(type, context, frameState) {
|
|
const layer = this.getLayer();
|
|
if (layer.hasListener(type)) {
|
|
composeTransform(
|
|
this.inversePixelTransform_,
|
|
0,
|
|
0,
|
|
frameState.pixelRatio,
|
|
-frameState.pixelRatio,
|
|
0,
|
|
0,
|
|
-frameState.size[1]
|
|
);
|
|
|
|
const event = new RenderEvent(
|
|
type,
|
|
this.inversePixelTransform_,
|
|
frameState,
|
|
context
|
|
);
|
|
layer.dispatchEvent(event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {WebGLRenderingContext} context The rendering context.
|
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
|
* @protected
|
|
*/
|
|
preRender(context, frameState) {
|
|
this.dispatchRenderEvent_(RenderEventType.PRERENDER, context, frameState);
|
|
}
|
|
|
|
/**
|
|
* @param {WebGLRenderingContext} context The rendering context.
|
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
|
* @protected
|
|
*/
|
|
postRender(context, frameState) {
|
|
this.dispatchRenderEvent_(RenderEventType.POSTRENDER, context, frameState);
|
|
}
|
|
}
|
|
|
|
const tmpArray_ = [];
|
|
const bufferPositions_ = {vertexPosition: 0, indexPosition: 0};
|
|
|
|
function writePointVertex(buffer, pos, x, y, index) {
|
|
buffer[pos + 0] = x;
|
|
buffer[pos + 1] = y;
|
|
buffer[pos + 2] = index;
|
|
}
|
|
|
|
/**
|
|
* An object holding positions both in an index and a vertex buffer.
|
|
* @typedef {Object} BufferPositions
|
|
* @property {number} vertexPosition Position in the vertex buffer
|
|
* @property {number} indexPosition Position in the index buffer
|
|
*/
|
|
|
|
/**
|
|
* Pushes a quad (two triangles) based on a point geometry
|
|
* @param {Float32Array} instructions Array of render instructions for points.
|
|
* @param {number} elementIndex Index from which render instructions will be read.
|
|
* @param {Float32Array} vertexBuffer Buffer in the form of a typed array.
|
|
* @param {Uint32Array} indexBuffer Buffer in the form of a typed array.
|
|
* @param {number} customAttributesCount Amount of custom attributes for each element.
|
|
* @param {BufferPositions} [bufferPositions] Buffer write positions; if not specified, positions will be set at 0.
|
|
* @return {BufferPositions} New buffer positions where to write next
|
|
* @property {number} vertexPosition New position in the vertex buffer where future writes should start.
|
|
* @property {number} indexPosition New position in the index buffer where future writes should start.
|
|
* @private
|
|
*/
|
|
export function writePointFeatureToBuffers(
|
|
instructions,
|
|
elementIndex,
|
|
vertexBuffer,
|
|
indexBuffer,
|
|
customAttributesCount,
|
|
bufferPositions
|
|
) {
|
|
// This is for x, y and index
|
|
const baseVertexAttrsCount = 3;
|
|
const baseInstructionsCount = 2;
|
|
const stride = baseVertexAttrsCount + customAttributesCount;
|
|
|
|
const x = instructions[elementIndex + 0];
|
|
const y = instructions[elementIndex + 1];
|
|
|
|
// read custom numerical attributes on the feature
|
|
const customAttrs = tmpArray_;
|
|
customAttrs.length = customAttributesCount;
|
|
for (let i = 0; i < customAttrs.length; i++) {
|
|
customAttrs[i] = instructions[elementIndex + baseInstructionsCount + i];
|
|
}
|
|
|
|
let vPos = bufferPositions ? bufferPositions.vertexPosition : 0;
|
|
let iPos = bufferPositions ? bufferPositions.indexPosition : 0;
|
|
const baseIndex = vPos / stride;
|
|
|
|
// push vertices for each of the four quad corners (first standard then custom attributes)
|
|
writePointVertex(vertexBuffer, vPos, x, y, 0);
|
|
customAttrs.length &&
|
|
vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
|
|
vPos += stride;
|
|
|
|
writePointVertex(vertexBuffer, vPos, x, y, 1);
|
|
customAttrs.length &&
|
|
vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
|
|
vPos += stride;
|
|
|
|
writePointVertex(vertexBuffer, vPos, x, y, 2);
|
|
customAttrs.length &&
|
|
vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
|
|
vPos += stride;
|
|
|
|
writePointVertex(vertexBuffer, vPos, x, y, 3);
|
|
customAttrs.length &&
|
|
vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
|
|
vPos += stride;
|
|
|
|
indexBuffer[iPos++] = baseIndex;
|
|
indexBuffer[iPos++] = baseIndex + 1;
|
|
indexBuffer[iPos++] = baseIndex + 3;
|
|
indexBuffer[iPos++] = baseIndex + 1;
|
|
indexBuffer[iPos++] = baseIndex + 2;
|
|
indexBuffer[iPos++] = baseIndex + 3;
|
|
|
|
bufferPositions_.vertexPosition = vPos;
|
|
bufferPositions_.indexPosition = iPos;
|
|
|
|
return bufferPositions_;
|
|
}
|
|
|
|
/**
|
|
* Returns a texture of 1x1 pixel, white
|
|
* @private
|
|
* @return {ImageData} Image data.
|
|
*/
|
|
export function getBlankImageData() {
|
|
const canvas = document.createElement('canvas');
|
|
const image = canvas.getContext('2d').createImageData(1, 1);
|
|
image.data[0] = 255;
|
|
image.data[1] = 255;
|
|
image.data[2] = 255;
|
|
image.data[3] = 255;
|
|
return image;
|
|
}
|
|
|
|
/**
|
|
* Generates a color array based on a numerical id
|
|
* Note: the range for each component is 0 to 1 with 256 steps
|
|
* @param {number} id Id
|
|
* @param {Array<number>} [opt_array] Reusable array
|
|
* @return {Array<number>} Color array containing the encoded id
|
|
*/
|
|
export function colorEncodeId(id, opt_array) {
|
|
const array = opt_array || [];
|
|
const radix = 256;
|
|
const divide = radix - 1;
|
|
array[0] = Math.floor(id / radix / radix / radix) / divide;
|
|
array[1] = (Math.floor(id / radix / radix) % radix) / divide;
|
|
array[2] = (Math.floor(id / radix) % radix) / divide;
|
|
array[3] = (id % radix) / divide;
|
|
return array;
|
|
}
|
|
|
|
/**
|
|
* Reads an id from a color-encoded array
|
|
* Note: the expected range for each component is 0 to 1 with 256 steps.
|
|
* @param {Array<number>} color Color array containing the encoded id
|
|
* @return {number} Decoded id
|
|
*/
|
|
export function colorDecodeId(color) {
|
|
let id = 0;
|
|
const radix = 256;
|
|
const mult = radix - 1;
|
|
id += Math.round(color[0] * radix * radix * radix * mult);
|
|
id += Math.round(color[1] * radix * radix * mult);
|
|
id += Math.round(color[2] * radix * mult);
|
|
id += Math.round(color[3] * mult);
|
|
return id;
|
|
}
|
|
|
|
export default WebGLLayerRenderer;
|