import type Extent from "../../../geometry/Extent.js"; import type Multipoint from "../../../geometry/Multipoint.js"; import type Point from "../../../geometry/Point.js"; import type Polygon from "../../../geometry/Polygon.js"; import type Polyline from "../../../geometry/Polyline.js"; import type Layer from "../../../layers/Layer.js"; import type MapView from "../../MapView.js"; import type ViewState from "../ViewState.js"; import type LayerView from "../../layers/LayerView.js"; import type { ScreenPoint } from "../../../core/types.js"; import type { ViewHit } from "../../types.js"; import type { TessellatedMesh } from "../types.js"; import type { LayerViewProperties } from "../../layers/LayerView.js"; export interface BaseLayerViewGL2DProperties extends LayerViewProperties, Partial> {} /** * A rectangle in screen-space. * * An instance of `Rect` is used to specify the screen-space geometry of the resulting marker when calling * [tessellatePoint()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#tessellatePoint) or [tessellateMultipoint()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#tessellateMultipoint). * The exact interpretation of _"screen-space"_ is ultimately implemented through a custom vertex shader; * a common convention is to interpret the values in the rect as being expressed in pixels or points. * See [TessellatedMesh](https://developers.arcgis.com/javascript/latest/references/core/views/2d/types/#TessellatedMesh) for more details. * * ![tessellation-helpers-uv](https://developers.arcgis.com/javascript/latest/assets/references/core/layers/tessellation-helpers-rect.png) */ export interface Rect { /** The `x`-coordinate of the upper-left corner of the rectangle, relative to the anchor of the marker. */ x: number; /** The `y`-coordinate of the upper-left corner of the rectangle, relative to the anchor of the marker. */ y: number; /** Width of the rectangle. */ width: number; /** Height of the rectangle. */ height: number; } /** * The destination to which [render()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#render) should direct its output. * * Rendering output is saved into a [framebuffer](https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer), * possibly but not necessarily the default one, at a location * defined by the [viewport](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/viewport). * * An instance of this class is mantained internally by `BaseLayerViewGL2D` and can be accessed by a call to * [getRenderTarget()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#getRenderTarget). It is the same instance that is restored to the WebGL context by * calling [bindRenderTarget()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#bindRenderTarget). * * When control is handed over to [render()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#render), the WebGL context is guaranteed to be in a default state * except for the currently bound framebuffer and the configured viewport. Implementations of [render()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#render) * are free to change these parameters using `gl.bindFramebuffer()` and `gl.viewport()`, but they must send the * output of the rendering process to the render target. */ export interface RenderTarget { /** The framebuffer where the [render()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#render) method should direct its output. */ framebuffer: WebGLFramebuffer | null; /** A viewport that fully covers `framebuffer`. */ viewport: [ number, number, number, number ]; } export interface RenderParameters { /** * The * WebGL or WebGL 2 context. Its concrete type depends on system configuration. * Every time that `render()` is called, the API automatically resets WebGL to a conventional * state which is _almost_ the default one; the only two things that may be non-default are * the bound framebuffer and the viewport, which is set to match the entire framebuffer. * _The body of `render()` **must not** change these settings_. */ context: WebGL2RenderingContext; /** The object that describes view state. */ state: ViewState; /** The stationary state of the `MapView`. */ stationary: boolean; } /** Represents a tile reference. */ export interface Tile { /** The tile string identifier in the format `level/row/col/world` */ id: string; /** The level identifier of the [LOD](https://developers.arcgis.com/javascript/latest/references/core/layers/support/LOD/) to which the tile belongs */ level: number; /** The row identifier. */ row: number; /** The column identifier. */ col: number; /** When the projection allows world wrapping (e.g. Web Mercator), identifies the instance of the world this tile's `level`/`row`/`col`. */ world: number; /** The number of map units per pixel in the tile. */ resolution: number; /** The map scale at the tile's level. */ scale: number; /** The coordinates of the top-left corner of the tile as an array of two numbers. The coordinates are in un-normalized map units. */ coords: [ number, number ]; /** The bounds of the tile as an array of four numbers that be readily converted to an [Extent](https://developers.arcgis.com/javascript/latest/references/core/geometry/Extent/) object. */ bounds: [ number, number, number, number ]; } /** * Represents the [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) of a [Layer](https://developers.arcgis.com/javascript/latest/references/core/layers/Layer/) * after it has been added to a [Map](https://developers.arcgis.com/javascript/latest/references/core/Map/) with a [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/). In contrast to the * related class [BaseLayerView2D](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerView2D/), this one exposes [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext) * rendering capabilities. * * > [!WARNING] * > * > ### Important notes * > * > **This interface is experimental**. Please read the following information carefully before using it in a product. * > * > Due to the nature of WebGL it is not possible to fully sandbox user-supplied code, and its malfunctions can affect the * > performance, visual quality and even stability of the entire [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/). For this reason, Esri * > does not provide any support for issues related to WebGL rendering in custom rendering code, or for issues that arise in * > [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/) rendering while using custom rendering code. * * This class may be extended to create a custom [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) for a Layer. * A [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) is created on demand by the [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/) when a layer is * added the to list of layers of its map. * * The subclass can implement multiple functions of the LayerView lifecycle. * * * First, the [attach()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#attach) method is called when the LayerView is about to start drawing the layer's content; it * is usually responsible for resource allocation. * * Then during the life of the LayerView the [render()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#render) method is called once per frame; it must complete * drawing before returning. * * Finally the [detach()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#detach) method is called after the layer is removed from the map; it releases all allocated * resources and stops on-going requests. * * Each of these functions has access to the [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/)'s WebGL context through the instance property * [this.context](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#context). The developer must provide own shaders, meshes * and textures, and is responsible for setting the required GL states on the context. * * As a convenience for the developer, starting with version 4.14 of the API, `BaseLayerViewGL2D` includes tessellation * helper methods; the developer can supply [Point](https://developers.arcgis.com/javascript/latest/references/core/geometry/Point/), [Multipoint](https://developers.arcgis.com/javascript/latest/references/core/geometry/Multipoint/), * [Polyline](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polyline/), [Extent](https://developers.arcgis.com/javascript/latest/references/core/geometry/Extent/), or [Polygon](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/) * geometries and have them converted to abstract descriptions of triangle meshes that can be easily fed to the GPU as * vertex and index buffers. The [SDK sample](https://developers.arcgis.com/javascript/latest/sample-code/custom-gl-tessellation-helpers/) * explains in detail how to use the tessellation helpers and how to write compatible shaders. * * @since 4.11 * @see [Sample - Tutorial: animated markers](https://developers.arcgis.com/javascript/latest/sample-code/custom-gl-visuals/) * @see [Sample - Tessellation helpers](https://developers.arcgis.com/javascript/latest/sample-code/custom-gl-tessellation-helpers/) * @see [Sample - Animated lines](https://developers.arcgis.com/javascript/latest/sample-code/custom-gl-animated-lines/) * @see [Sample - Tiling support](https://developers.arcgis.com/javascript/latest/sample-code/custom-gl-tiles/) * @see [Sample - Using deck.gl](https://developers.arcgis.com/javascript/latest/sample-code/custom-lv-deckgl/) * @example * let CustomLayerView2D = BaseLayerViewGL2D.createSubclass({ * render(renderParameters) { * const gl = this.context; * * ... * } * * attach() { * const gl = this.context; * * ... * } * * detach() { * const gl = this.context; * * ... * } * }); * * let CustomTileLayer = Layer.createSubclass({ * tileInfo: TileInfo.create({ spatialReference: { wkid: 3857 }}), * * createLayerView(view) { * if (view.type === "2d") { * return new CustomLayerView2D({ * view: view, * layer: this * }); * } * } * }); */ export default abstract class BaseLayerViewGL2D extends LayerView { constructor(properties?: BaseLayerViewGL2DProperties); /** The WebGL rendering context associated to this layer view. */ get context(): WebGL2RenderingContext; /** References the layer this [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) represents. */ get layer(): Layer; /** * The array of [Tile](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#Tile) objects computed to cover the MapView's visible area. * This array is updated when the view is animating or the user is interacting with it. Then if tiles have been added or removed, * [tilesChanged()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#tilesChanged) is called. * * @see [tilesChanged()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#tilesChanged) */ tiles: Tile[]; /** * References the [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/) this [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) belongs to. * * @since 4.28 * @example * // Check for the first time layerView.updating becomes false. Then query for * // features that are visible within the view associated with the layer view. * await reactiveUtils.whenOnce(() => !layerView.updating); * const query = layerView.createQuery(); * query.geometry = layerView.view.extent; * const result = layerView.queryFeatures(query); */ get view(): MapView; /** * Method called after the [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) is created and right before it starts drawing the layer's content. * Typically this method is implemented to start watching property changes on the layer and to initialize WebGL objects such as * shaders. * * @see [detach()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#detach) * @example * // Create a shader program and a property watcher * attach() { * let gl = this.context; * * this._shaderProgram = gl.createProgram(); * ... * * this._propertyHandle = reactiveUtils.watch( * () => this.layer.opacity, * () => this.requestRender() * ); * } */ attach(): void; /** * Bind the designated rendering output surface and restore the correct viewport. * * This method can be used after the WebGL state has been altered by a call to * `gl.bindFramebuffer()` to restore the framebuffer that contains the final, * composited frame, i.e. the one that is guaranteed to be bound right before * control is handed over to [render()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#render). Note that this *may or may not be the default framebuffer*; * [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/) can use various surfaces for frame compositing and there is no * guarantee that when [render()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#render) is called, the bound framebuffer is * the default one. * * Together with the framebuffer, also a matching full-size viewport is restored. * * @example * render() { * let gl = this.context; * * ... * * // Bind a temporary offscreen surface * gl.bindFramebuffer(gl.FRAMEBUFFER, this.myOffscreenSurface); * * ... * * // Render to the offscreen surface * * ... * * // Bind the original render surface so that the image stored * // into the temporary one can be blitted/composited with the * // actual frame data * this.bindRenderTarget(); * * ... * * // Your own frame composition logic * * ... * } */ bindRenderTarget(): void; /** * Method called after the layer is removed and the [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) is about to be removed. * Typically, this method is implemented to free resources like watchers and destroy WebGL objects such as shader programs. * * @see [attach()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#attach) * @example * // Remove the watchers and destroy the shader program created in attach() * detach() { * this._propertyHandle.remove(); * this._propertyHandle = null; * * const gl = this.context; * * gl.deleteProgram(this._shaderProgram); * this._shaderProgram = null; * } */ detach(): void; /** * Get the designated rendering output surface and corresponding viewport configuration. * * The returned object is the same render target that is restored by a call to [bindRenderTarget()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#bindRenderTarget). */ getRenderTarget(): RenderTarget; /** * Method to implement that is responsible for providing objects hit at the specified screen coordinates. * This method is called internally by the [MapView](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/) each time * its [MapView.hitTest()](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/#hitTest) method is called. * * @param mapPoint - The point in map units. * @param screenPoint - The point in screen coordinates. * @returns A Promise that resolves to an array of graphics. */ hitTest(mapPoint: Point, screenPoint: ScreenPoint): Promise; /** * The method to implement that is responsible of drawing the content of the layer. * This method is called every time the MapView's state changes, or if [requestRender()](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#requestRender) has been called. * * @param renderParameters * @example * // Example of a render implementation that draws using a custom shader program * render(renderParameters) { * const gl = this.context; * gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); * gl.vertexAttribPointer(0, 2, gl.SHORT, false, 10, 0); * gl.vertexAttribPointer(1, 3, gl.SHORT, true, 10, 4); * gl.bindBuffer(gl.ARRAY_BUFFER, null); * gl.enableVertexAttribArray(0); * gl.enableVertexAttribArray(1); * ... * // Update uniforms as needed by calling gl.uniform... * ... * gl.useProgram(this._shaderProgram); * gl.drawArrays(gl.TRIANGLES, 0, this._vertexCount); * gl.disableVertexAttribArray(0); * gl.disableVertexAttribArray(1); * gl.useProgram(null); * } */ abstract render(renderParameters: RenderParameters): void; /** * The [LayerView](https://developers.arcgis.com/javascript/latest/references/core/views/layers/LayerView/) can call this method to ask the MapView to schedule a new rendering frame. * * @example * // Call requestRender whenever the layer opacity has changed * attach() { * this._propertyHandle = reactiveUtils.watch( * () => this.layer.opacity, * () => this.requestRender() * ); * } */ requestRender(): void; /** * Tessellate an [Extent](https://developers.arcgis.com/javascript/latest/references/core/geometry/Extent/) into a rectangle. * * @param extent - The input geometry. * @returns A promise to a [TessellatedMesh](https://developers.arcgis.com/javascript/latest/references/core/views/2d/types/#TessellatedMesh). The output mesh is composed of two triangles. * @since 4.14 * @example * this.tessellateExtent(g.geometry).then(function (mesh) { * // do something with mesh * }); */ tessellateExtent(extent: Extent): Promise; /** * Tessellate a [Multipoint](https://developers.arcgis.com/javascript/latest/references/core/geometry/Multipoint/) into quads (markers). * * @param multipoint - The input geometry. These are the geographic points where each marker will me anchored. * @param footprint - The rectangle that describes the geometry of each marker. Coordinates x and y * can be thought as being in screen-space, relative to the screen-space projection of the geographic point. * @returns A promise to a [TessellatedMesh](https://developers.arcgis.com/javascript/latest/references/core/views/2d/types/#TessellatedMesh). Each marker is represented by two triangles. * @since 4.14 * @example * this.tessellateMultipoint(g.geometry, {x: 0, : -12, width: 34, height: 10}).then(function (mesh) { * // do something with mesh * }); */ tessellateMultipoint(multipoint: Multipoint, footprint: Rect): Promise; /** * Tessellate a [Point](https://developers.arcgis.com/javascript/latest/references/core/geometry/Point/) into a quad (marker). * * @param point - The input geometry. This is the geographic point where the marker will me anchored. * @param footprint - The rectangle that describes the geometry of the marker. * Coordinates `x` and `y` are the * position of the upper-left corner of the marker, and can be thought as being in screen-space, relative to the screen-space * projection of the geographic point; `width` and `height` are in pixels. See [Rect](https://developers.arcgis.com/javascript/latest/references/core/views/2d/layers/BaseLayerViewGL2D/#Rect) * for a visual explanation of marker geometry. * @returns A promise to a [TessellatedMesh](https://developers.arcgis.com/javascript/latest/references/core/views/2d/types/#TessellatedMesh). The output mesh is composed of two triangles. * @since 4.14 * @example * this.tessellatePoint(g.geometry, {x: 0, : -12, width: 34, height: 10}).then(function (mesh) { * // do something with mesh * }); */ tessellatePoint(point: Point, footprint: Rect): Promise; /** * Tessellate a [Polygon](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/) into triangles. * * @param polygon - The input geometry. *The geometry must be simple*; * if the input geometry is not simple, you must first create a simplified version of it using * [simplifyOperator](https://developers.arcgis.com/javascript/latest/references/core/geometry/operators/simplifyOperator/), and pass the simplified geometry to `tessellatePolygon`. * @returns A promise to a [TessellatedMesh](https://developers.arcgis.com/javascript/latest/references/core/views/2d/types/#TessellatedMesh). * @since 4.14 * @example * this.tessellatePolygon(g.geometry).then(function (mesh) { * // do something with mesh * }); */ tessellatePolygon(polygon: Polygon): Promise; /** * Tessellate a [Polyline](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polyline/) into triangles. * * @param polyline - The input geometry. *The geometry must be simple*; * if the input geometry is not simple, you must first create a simplified version of it using * [simplifyOperator](https://developers.arcgis.com/javascript/latest/references/core/geometry/operators/simplifyOperator/), and pass the simplified geometry to `tessellatePolyline`. * @param width - The width of the line; this will be used to scale xOffset and yOffset. * @returns A promise to a [TessellatedMesh](https://developers.arcgis.com/javascript/latest/references/core/views/2d/types/#TessellatedMesh). * @since 4.14 * @example * this.tessellatePolyline(g.geometry, 10).then(function (mesh) { * // do something with mesh * }); */ tessellatePolyline(polyline: Polyline, width: number): Promise; /** * Method to implement, which notifies of tiles being added or removed for the current view state. * This function can be implemented to start and stop fetching new data, or allocate and dispose resources. * * @param added - The tile objects added for the current view viewport. * @param removed - The tile objects removed from the view viewport. */ tilesChanged(added: Tile[], removed: Tile[]): void; }