dev-docs/RFCs/v5.2/view-class-rfc.md
References:
This RFC proposed a new View class that takes over the role of "viewport descriptor" from the current Viewport class. This reduces the heavy overload of Viewport and provides a highly extensible API for the future.
deck.gl v5.0 implements powerful multi-view feature however the API is still "experimental" due to a consensus that API is not final and needs further discussion and revision.
This RFC proposes a new View class API for specifying multi-viewport configurations that solves a number of issues with the existing API. It is intended to represent a big step towards an official multi-view(port) API in deck.gl.
Issues related to using Viewports as "view descriptors"
Viewport property overload - Separation of concerns: jamming all new description options into the base Viewport class, adds a lot of complexity to an already quite complex class.Viewport classes are static/immutable - Viewports are designed for efficient projection/unprojection. This makes them somewhat unsuitable to be descriptors for "views" as they have to be recreated every time the deck.gl container size (canvas size) changes.Issues related to the Viewport class hierarchy being the main API for selecting type of view:
Viewport class hierarcy - Settling on a consistent, simple to explain Viewport class hierarchy has been difficult since info vis and hybrid geospatial apps value different aspects (e.g. type of projection matrix vs type of view matrix).Issues related to future support for multiple (per-view) Controllers:
controller field to the current viewport seems like unacceptable coupling between two complex class hierarchies.The detailed multi controller proposal is in a separate RFC, however the View class becomes a natural anchor point to specify what Controller works with what View.
Primary Requirements
Secondary Requirements:
View class could be a natural place to specify any necessary parameters.Proposed: New View ES6 Class
Viewport Autosizing: Relative Positions and Dimensions - Ability to specify viewport x, y, width, height as relative values (percentages) in addition to numbers.
View ClassA descriptor for one view (window or viewport) into your date. Contains id, type (WebMercatorViewport etc), relative size (x, y, width, height).
A new views property lets the DeckGL component to render multiple views of your based on a list of View "descriptors".
flattenArray, it will allow a single View or nested View array to be passed to views.DeckGL.layerFilter propertyChange signature layerFilter({layer, view, isPicking}) instead of layerFilter({layer, viewport, isPicking})
We could still provide the derived viewport instance, but it may no longer have the id property the app most likely is looking for.
| Old Method | New Method | Comment |
|---|---|---|
viewports | views | viewports was an experimental prop |
viewport | views |
| Old Method | New Method | Comment |
|---|---|---|
id | N/A | Removed |
x | N/A | TBD - keep and use as offset in projections? |
y | N/A | TBD - keep and use as offset in projections? |
Note:
Viewclass instances use the "CSS" (top-left, window, non-device-pixel) coordinate system to interpresetx,y,widthandheightproperties and automatically convert to WebGL (gl.viewport) coordinates under the hood.
type (Viewport subclass, default Viewport) - Can be any existing Viewport class type...id - (String) - for filtering and child component layoutx - (Number|String, default 0)y - (Number|String, default 0)width - (Number|String, default 100%)height - (Number|String, default 100%)layerFilter({layer, view, isPicking}) - Similar to the layerFilter on the main DeckGL class, this filter allows control of which layers are rendered in this viewport.onBeforeRender({gl, view, ...}) - called just before this view is renderedonAfterRender({gl, view, ...}) - called just before this view is renderedPicking already renders all viewports, so an object can be picked in any viewport. But pick info is not fully updated.
In this discussion, view parameter refer to a controller/viewport specific set of parameters that describe the "current viewpoint" for a specific controller/view pair. It can be lng/lat/zoom for a web mercator viewport, or rotationX/rotationY for an orbit controller.
While we can absolutely add view parameters to the View class, it is desirable to decouple "view parameters" from "view descriptors" since all other properties of view descriptors change very rarely, if at all.
| Area | Impact |
|---|---|
| updateTriggers | No impact identified |
| transitions | No impact identified |
Note: The purpose of this section is to show that in contrast to
Viewport, theViewclass can support considerable functional API extensions in a natural way. The actual features are not considered part of this RFC and may need to be approved in a separate RFC before implementation.
Specify per viewport controller (see separate RFC).
Viewports and multi-view functionality is likely to keep evolving and an important aspect is that this proposal should allow for future feature growth in a natural way.
Therefore this section lists ideas of new functionality that could be elegantly "absorbed" by the View descriptor class.
Ideas:
color - Background coloronClear({gl, view, viewport, ...}) - Customize what is cleared (color, depth, stencil etc)framebuffer - makes this view render into a framebufferparameters: {blendMode, ...}We could address use cases beyond simply rendering multiple views:
A viewport descriptor could specify additional information like:
context object with parameters that get passed to property animation functions.More ambitous extensions:
These are API examples to show how the new class would be used in applications
Views can be laid out top-to-bottom as in this example. Note how the application controls both the height and the y position of the two viewports.
<DeckGL views=[
new View({type: FirstPersonViewport, viewprops, height: '50%'}),
new View({type: WebMercatorViewport, viewprops, y: '50%', height: '50%'}),
...
]/>
Side-by-side Views is of course essential for stereoscopic rendering (and conveniently, View's can directly accept view and projection matrices from the WebVR API):
<DeckGL views=[
// left eye
new View({ // type defaults to `Viewport`
width: '50%',
viewMatrix: leftViewMatrix, // from WebVR API
projectionMatrix: leftProjectionMatrix // from WebVR API
}),
// right eye
new View({ // type defaults to `Viewport`
x: '50%',
width: '50%',
viewMatrix: rightViewMatrix, // from WebVR API
projectionMatrix: rightProjectionMatrix // from WebVR API
}),
...
]/>
View's render in order, so they can also partially overlap, (e.g. having a small overview map in the bottom middle of the screen overlaid over the main view).
const {width, height} = viewportProps;
...
<DeckGL views=[
new View({
type: FirstPersonViewport,
...viewprops
}),
// Render a small map viewport over the main first person viewport
new WebMercatorViewport({
...viewprops,
height: height / 8,
width: width / 8,
x: width * 7 / 16,
y: height * 13 / 16
})
]/>
And of course Views can also fully overlap.
TODO - in this case the application will need to control how background is cleared, likely clearing depth and stencil buffers but not color buffer for the second view.
const {width, height} = viewportProps;
...
<DeckGL views=[
new View({
type: FirstPersonViewport,
...viewprops
}),
// Render a small map viewport over the main first person viewport
new View({
type: WebMercatorViewport({
...viewprops
})
]/>
render() {
<DeckGL
width={viewportProps.width}
height={viewportProps.height}
layers={this._renderLayers()}
views={[
new View({type: FirstPersonViewport, ...}),
new View({type: WebMercatorViewport, id: 'basemap', ...})
]}
>
<StaticMap
viewId='basemap'
{...viewportProps}/>
</DeckGL>
}
In the React API, views could be created once and just passed in on each render.
const views = [...];
render() {
<DeckGL
views={views}
viewStates={{firstPerson: viewState1, map: viewState2}}
}
In the JS API, the views could be set just once.
const deck = new Deck();
deck.setViews(views);
onViewStateChanged(...) {
deck.setViewStates(viewStates);
}