@deck.gl/core
Version:
deck.gl core library
211 lines • 10.2 kB
JavaScript
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
/* eslint-disable complexity, camelcase */
import { mat4, vec4 } from '@math.gl/core';
import { COORDINATE_SYSTEM, PROJECTION_MODE } from "../../lib/constants.js";
import memoize from "../../utils/memoize.js";
// To quickly set a vector to zero
const ZERO_VECTOR = [0, 0, 0, 0];
// 4x4 matrix that drops 4th component of vector
const VECTOR_TO_POINT_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0];
const IDENTITY_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
const DEFAULT_PIXELS_PER_UNIT2 = [0, 0, 0];
const DEFAULT_COORDINATE_ORIGIN = [0, 0, 0];
const getMemoizedViewportUniforms = memoize(calculateViewportUniforms);
export function getOffsetOrigin(viewport, coordinateSystem, coordinateOrigin = DEFAULT_COORDINATE_ORIGIN) {
if (coordinateOrigin.length < 3) {
coordinateOrigin = [coordinateOrigin[0], coordinateOrigin[1], 0];
}
let shaderCoordinateOrigin = coordinateOrigin;
let geospatialOrigin;
let offsetMode = true;
if (coordinateSystem === COORDINATE_SYSTEM.LNGLAT_OFFSETS ||
coordinateSystem === COORDINATE_SYSTEM.METER_OFFSETS) {
geospatialOrigin = coordinateOrigin;
}
else {
geospatialOrigin = viewport.isGeospatial
? // @ts-expect-error longitude and latitude are not defined on the base Viewport, but is expected on geospatial viewports
[Math.fround(viewport.longitude), Math.fround(viewport.latitude), 0]
: null;
}
switch (viewport.projectionMode) {
case PROJECTION_MODE.WEB_MERCATOR:
if (coordinateSystem === COORDINATE_SYSTEM.LNGLAT ||
coordinateSystem === COORDINATE_SYSTEM.CARTESIAN) {
geospatialOrigin = [0, 0, 0];
offsetMode = false;
}
break;
case PROJECTION_MODE.WEB_MERCATOR_AUTO_OFFSET:
if (coordinateSystem === COORDINATE_SYSTEM.LNGLAT) {
// viewport center in world space
// @ts-expect-error when using LNGLAT coordinates, we expect the viewport to be geospatial, in which case geospatialOrigin is defined
shaderCoordinateOrigin = geospatialOrigin;
}
else if (coordinateSystem === COORDINATE_SYSTEM.CARTESIAN) {
// viewport center in common space
shaderCoordinateOrigin = [
Math.fround(viewport.center[0]),
Math.fround(viewport.center[1]),
0
];
// Geospatial origin (wgs84) must match shaderCoordinateOrigin (common)
geospatialOrigin = viewport.unprojectPosition(shaderCoordinateOrigin);
shaderCoordinateOrigin[0] -= coordinateOrigin[0];
shaderCoordinateOrigin[1] -= coordinateOrigin[1];
shaderCoordinateOrigin[2] -= coordinateOrigin[2];
}
break;
case PROJECTION_MODE.IDENTITY:
shaderCoordinateOrigin = viewport.position.map(Math.fround);
shaderCoordinateOrigin[2] = shaderCoordinateOrigin[2] || 0;
break;
case PROJECTION_MODE.GLOBE:
offsetMode = false;
geospatialOrigin = null;
break;
default:
// Unknown projection mode
offsetMode = false;
}
return { geospatialOrigin, shaderCoordinateOrigin, offsetMode };
}
// The code that utilizes Matrix4 does the same calculation as their mat4 counterparts,
// has lower performance but provides error checking.
function calculateMatrixAndOffset(viewport, coordinateSystem, coordinateOrigin) {
const { viewMatrixUncentered, projectionMatrix } = viewport;
let { viewMatrix, viewProjectionMatrix } = viewport;
let projectionCenter = ZERO_VECTOR;
let originCommon = ZERO_VECTOR;
let cameraPosCommon = viewport.cameraPosition;
const { geospatialOrigin, shaderCoordinateOrigin, offsetMode } = getOffsetOrigin(viewport, coordinateSystem, coordinateOrigin);
if (offsetMode) {
// Calculate transformed projectionCenter (using 64 bit precision JS)
// This is the key to offset mode precision
// (avoids doing this addition in 32 bit precision in GLSL)
// @ts-expect-error the 4th component is assigned below
originCommon = viewport.projectPosition(geospatialOrigin || shaderCoordinateOrigin);
cameraPosCommon = [
cameraPosCommon[0] - originCommon[0],
cameraPosCommon[1] - originCommon[1],
cameraPosCommon[2] - originCommon[2]
];
originCommon[3] = 1;
// projectionCenter = new Matrix4(viewProjectionMatrix)
// .transformVector([positionPixels[0], positionPixels[1], 0.0, 1.0]);
projectionCenter = vec4.transformMat4([], originCommon, viewProjectionMatrix);
// Always apply uncentered projection matrix if available (shader adds center)
viewMatrix = viewMatrixUncentered || viewMatrix;
// Zero out 4th coordinate ("after" model matrix) - avoids further translations
// viewMatrix = new Matrix4(viewMatrixUncentered || viewMatrix)
// .multiplyRight(VECTOR_TO_POINT_MATRIX);
viewProjectionMatrix = mat4.multiply([], projectionMatrix, viewMatrix);
viewProjectionMatrix = mat4.multiply([], viewProjectionMatrix, VECTOR_TO_POINT_MATRIX);
}
return {
viewMatrix: viewMatrix,
viewProjectionMatrix: viewProjectionMatrix,
projectionCenter,
originCommon,
cameraPosCommon,
shaderCoordinateOrigin,
geospatialOrigin
};
}
/**
* Returns uniforms for shaders based on current projection
* includes: projection matrix suitable for shaders
*
* TODO - Ensure this works with any viewport, not just WebMercatorViewports
*
* @param {WebMercatorViewport} viewport -
* @return {Float32Array} - 4x4 projection matrix that can be used in shaders
*/
export function getUniformsFromViewport({ viewport, devicePixelRatio = 1, modelMatrix = null,
// Match Layer.defaultProps
coordinateSystem = COORDINATE_SYSTEM.DEFAULT, coordinateOrigin = DEFAULT_COORDINATE_ORIGIN, autoWrapLongitude = false }) {
if (coordinateSystem === COORDINATE_SYSTEM.DEFAULT) {
coordinateSystem = viewport.isGeospatial
? COORDINATE_SYSTEM.LNGLAT
: COORDINATE_SYSTEM.CARTESIAN;
}
const uniforms = getMemoizedViewportUniforms({
viewport,
devicePixelRatio,
coordinateSystem,
coordinateOrigin
});
uniforms.wrapLongitude = autoWrapLongitude;
uniforms.modelMatrix = modelMatrix || IDENTITY_MATRIX;
return uniforms;
}
function calculateViewportUniforms({ viewport, devicePixelRatio, coordinateSystem, coordinateOrigin }) {
const { projectionCenter, viewProjectionMatrix, originCommon, cameraPosCommon, shaderCoordinateOrigin, geospatialOrigin } = calculateMatrixAndOffset(viewport, coordinateSystem, coordinateOrigin);
// Calculate projection pixels per unit
const distanceScales = viewport.getDistanceScales();
const viewportSize = [
viewport.width * devicePixelRatio,
viewport.height * devicePixelRatio
];
// Distance at which screen pixels are projected.
// Used to scale sizes in clipspace to match screen pixels.
// When using Viewport class's default projection matrix, this yields 1 for orthographic
// and `viewport.focalDistance` for perspective views
const focalDistance = vec4.transformMat4([], [0, 0, -viewport.focalDistance, 1], viewport.projectionMatrix)[3] || 1;
const uniforms = {
// Projection mode values
coordinateSystem,
projectionMode: viewport.projectionMode,
coordinateOrigin: shaderCoordinateOrigin,
commonOrigin: originCommon.slice(0, 3),
center: projectionCenter,
// Backward compatibility
// TODO: remove in v9
// @ts-expect-error _pseudoMeters is only defined on WebMercator viewport
pseudoMeters: Boolean(viewport._pseudoMeters),
// Screen size
viewportSize,
devicePixelRatio,
focalDistance,
commonUnitsPerMeter: distanceScales.unitsPerMeter,
commonUnitsPerWorldUnit: distanceScales.unitsPerMeter,
commonUnitsPerWorldUnit2: DEFAULT_PIXELS_PER_UNIT2,
scale: viewport.scale, // This is the mercator scale (2 ** zoom)
wrapLongitude: false,
viewProjectionMatrix,
modelMatrix: IDENTITY_MATRIX,
// This is for lighting calculations
cameraPosition: cameraPosCommon
};
if (geospatialOrigin) {
// Get high-precision DistanceScales from geospatial viewport
// TODO: stricter types in Viewport classes
const distanceScalesAtOrigin = viewport.getDistanceScales(geospatialOrigin);
switch (coordinateSystem) {
case COORDINATE_SYSTEM.METER_OFFSETS:
uniforms.commonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerMeter;
uniforms.commonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerMeter2;
break;
case COORDINATE_SYSTEM.LNGLAT:
case COORDINATE_SYSTEM.LNGLAT_OFFSETS:
// @ts-expect-error _pseudoMeters only exists on WebMercatorView
if (!viewport._pseudoMeters) {
uniforms.commonUnitsPerMeter = distanceScalesAtOrigin.unitsPerMeter;
}
uniforms.commonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerDegree;
uniforms.commonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerDegree2;
break;
// a.k.a "preprojected" positions
case COORDINATE_SYSTEM.CARTESIAN:
uniforms.commonUnitsPerWorldUnit = [1, 1, distanceScalesAtOrigin.unitsPerMeter[2]];
uniforms.commonUnitsPerWorldUnit2 = [0, 0, distanceScalesAtOrigin.unitsPerMeter2[2]];
break;
default:
break;
}
}
return uniforms;
}
//# sourceMappingURL=viewport-uniforms.js.map