UNPKG

@deck.gl/core

Version:

deck.gl core library

211 lines 10.2 kB
// 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