From d65e9bfe30aa159338a5dde675db02dfdccd3db0 Mon Sep 17 00:00:00 2001 From: Xiaoji Chen Date: Mon, 13 Jan 2020 12:05:30 -0800 Subject: [PATCH 1/2] support preprojected positions --- .../src/shaderlib/project/project.glsl.js | 4 +- .../shaderlib/project/viewport-uniforms.js | 110 +++++++++++++----- test/apps/mapbox-tile/app.js | 2 + test/apps/mapbox-tile/utils/decode.js | 19 ++- .../project/viewport-uniforms.spec.js | 32 +++++ 5 files changed, 123 insertions(+), 44 deletions(-) diff --git a/modules/core/src/shaderlib/project/project.glsl.js b/modules/core/src/shaderlib/project/project.glsl.js index ad516c8223f..110c6794b9e 100644 --- a/modules/core/src/shaderlib/project/project.glsl.js +++ b/modules/core/src/shaderlib/project/project.glsl.js @@ -121,7 +121,9 @@ vec4 project_position(vec4 position, vec3 position64Low) { vec4 position_world = project_uModelMatrix * position; - if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT && project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET) { + if (project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET && + (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT || + project_uCoordinateSystem == COORDINATE_SYSTEM_CARTESIAN)) { // Subtract high part of 64 bit value. Convert remainder to float32, preserving precision. position_world.xyz -= project_uCoordinateOrigin; position_world.xyz += position64Low; diff --git a/modules/core/src/shaderlib/project/viewport-uniforms.js b/modules/core/src/shaderlib/project/viewport-uniforms.js index 30c1a809a74..172e14bdc91 100644 --- a/modules/core/src/shaderlib/project/viewport-uniforms.js +++ b/modules/core/src/shaderlib/project/viewport-uniforms.js @@ -17,6 +17,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +/* eslint-disable complexity */ import * as mat4 from 'gl-matrix/mat4'; import * as vec4 from 'gl-matrix/vec4'; @@ -42,23 +43,58 @@ const getMemoizedViewportUniforms = memoize(calculateViewportUniforms); // The code that utilizes Matrix4 does the same calculation as their mat4 counterparts, // has lower performance but provides error checking. // Uncomment when debugging -function calculateMatrixAndOffset({viewport, coordinateSystem, coordinateOrigin}) { +function calculateMatrixAndOffset(viewport, coordinateSystem, coordinateOrigin) { const {viewMatrixUncentered, projectionMatrix, projectionMode} = viewport; let {viewMatrix, viewProjectionMatrix} = viewport; let projectionCenter = ZERO_VECTOR; let cameraPosCommon = viewport.cameraPosition; let shaderCoordinateOrigin = coordinateOrigin; + let geospatialOrigin; let offsetMode = true; - if (coordinateSystem === COORDINATE_SYSTEM.LNGLAT) { - if (projectionMode === PROJECTION_MODE.WEB_MERCATOR) { + if ( + coordinateSystem === COORDINATE_SYSTEM.LNGLAT_OFFSETS || + coordinateSystem === COORDINATE_SYSTEM.METER_OFFSETS + ) { + geospatialOrigin = coordinateOrigin; + } else { + geospatialOrigin = viewport.isGeospatial + ? [Math.fround(viewport.longitude), Math.fround(viewport.latitude), 0] + : null; + } + + switch (projectionMode) { + case PROJECTION_MODE.WEB_MERCATOR: + if ( + coordinateSystem === COORDINATE_SYSTEM.LNGLAT || + coordinateSystem === COORDINATE_SYSTEM.CARTESIAN + ) { + offsetMode = false; + } + break; + + case PROJECTION_MODE.WEB_MERCATOR_AUTO_OFFSET: + if (coordinateSystem === COORDINATE_SYSTEM.LNGLAT) { + // viewport center in world space + 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 + ]; + } + break; + + case PROJECTION_MODE.IDENTITY: + shaderCoordinateOrigin = viewport.position.map(Math.fround); + break; + + default: + // Unknown projection mode offsetMode = false; - } else { - shaderCoordinateOrigin = [Math.fround(viewport.longitude), Math.fround(viewport.latitude), 0]; - } - } else if (projectionMode === PROJECTION_MODE.IDENTITY) { - shaderCoordinateOrigin = viewport.position.map(Math.fround); } shaderCoordinateOrigin[2] = shaderCoordinateOrigin[2] || 0; @@ -67,7 +103,9 @@ function calculateMatrixAndOffset({viewport, coordinateSystem, coordinateOrigin} // 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) - const positionCommonSpace = viewport.projectPosition(shaderCoordinateOrigin); + const positionCommonSpace = viewport.projectPosition( + geospatialOrigin || shaderCoordinateOrigin + ); cameraPosCommon = [ cameraPosCommon[0] - positionCommonSpace[0], @@ -96,7 +134,8 @@ function calculateMatrixAndOffset({viewport, coordinateSystem, coordinateOrigin} viewProjectionMatrix, projectionCenter, cameraPosCommon, - shaderCoordinateOrigin + shaderCoordinateOrigin, + geospatialOrigin }; } @@ -152,12 +191,9 @@ function calculateViewportUniforms({ projectionCenter, viewProjectionMatrix, cameraPosCommon, - shaderCoordinateOrigin - } = calculateMatrixAndOffset({ - coordinateSystem, - coordinateOrigin, - viewport - }); + shaderCoordinateOrigin, + geospatialOrigin + } = calculateMatrixAndOffset(viewport, coordinateSystem, coordinateOrigin); // Calculate projection pixels per unit const distanceScales = viewport.getDistanceScales(); @@ -189,21 +225,33 @@ function calculateViewportUniforms({ project_uCameraPosition: cameraPosCommon }; - const distanceScalesAtOrigin = viewport.getDistanceScales(shaderCoordinateOrigin); - switch (coordinateSystem) { - case COORDINATE_SYSTEM.METER_OFFSETS: - uniforms.project_uCommonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerMeter; - uniforms.project_uCommonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerMeter2; - break; - - case COORDINATE_SYSTEM.LNGLAT: - case COORDINATE_SYSTEM.LNGLAT_OFFSETS: - uniforms.project_uCommonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerDegree; - uniforms.project_uCommonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerDegree2; - break; - - default: - break; + if (geospatialOrigin) { + const distanceScalesAtOrigin = viewport.getDistanceScales(geospatialOrigin); + switch (coordinateSystem) { + case COORDINATE_SYSTEM.METER_OFFSETS: + uniforms.project_uCommonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerMeter; + uniforms.project_uCommonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerMeter2; + break; + + case COORDINATE_SYSTEM.LNGLAT: + case COORDINATE_SYSTEM.LNGLAT_OFFSETS: + uniforms.project_uCommonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerDegree; + uniforms.project_uCommonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerDegree2; + break; + + // a.k.a "preprojected" positions + case COORDINATE_SYSTEM.CARTESIAN: + uniforms.project_uCommonUnitsPerWorldUnit = [1, 1, distanceScalesAtOrigin.unitsPerMeter[2]]; + uniforms.project_uCommonUnitsPerWorldUnit2 = [ + 0, + 0, + distanceScalesAtOrigin.unitsPerMeter2[2] + ]; + break; + + default: + break; + } } return uniforms; diff --git a/test/apps/mapbox-tile/app.js b/test/apps/mapbox-tile/app.js index b5ca45bde87..9f56980dcdd 100644 --- a/test/apps/mapbox-tile/app.js +++ b/test/apps/mapbox-tile/app.js @@ -4,6 +4,7 @@ import React, {PureComponent} from 'react'; import {render} from 'react-dom'; import DeckGL from '@deck.gl/react'; import {TileLayer} from '@deck.gl/geo-layers'; +import {COORDINATE_SYSTEM} from '@deck.gl/core'; import {decodeTile} from './utils/decode'; @@ -95,6 +96,7 @@ class Root extends PureComponent { layers={[ new TileLayer({ ...MAP_LAYER_STYLES, + coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, getPolygonOffset: null, pickable: true, getTileData diff --git a/test/apps/mapbox-tile/utils/decode.js b/test/apps/mapbox-tile/utils/decode.js index aff98aa40ab..e0eed1e5cff 100644 --- a/test/apps/mapbox-tile/utils/decode.js +++ b/test/apps/mapbox-tile/utils/decode.js @@ -2,9 +2,7 @@ import Protobuf from 'pbf'; import {VectorTile} from '@mapbox/vector-tile'; import {vectorTileFeatureToGeoJSON} from './feature'; -const PI = Math.PI; -const PI_4 = PI / 4; -const RADIANS_TO_DEGREES_2 = 360 / PI; +const TILE_SIZE = 512; export function decodeTile(x, y, z, arrayBuffer) { const tile = new VectorTile(new Protobuf(arrayBuffer)); @@ -12,8 +10,8 @@ export function decodeTile(x, y, z, arrayBuffer) { const result = []; const scale = Math.pow(2, z); - const projX = x / scale; - const projY = y / scale; + const projX = (x * TILE_SIZE) / scale; + const projY = (y * TILE_SIZE) / scale; const projectFunc = project.bind(null, projX, projY, scale); @@ -32,15 +30,12 @@ export function decodeTile(x, y, z, arrayBuffer) { } function project(x, y, scale, line, extent) { - const pixelToCommon = 1 / extent / scale; + const pixelToCommon = TILE_SIZE / extent / scale; for (let i = 0; i < line.length; i++) { const p = line[i]; - // common space - const cx = x + p[0] * pixelToCommon; - const cy = y + p[1] * pixelToCommon; - // LNGLAT - p[0] = cx * 360 - 180; - p[1] = (Math.atan(Math.exp(PI - cy * 2 * PI)) - PI_4) * RADIANS_TO_DEGREES_2; + // convert to deck.gl common space + p[0] = x + p[0] * pixelToCommon; + p[1] = TILE_SIZE - y - p[1] * pixelToCommon; } } diff --git a/test/modules/core/shaderlib/project/viewport-uniforms.spec.js b/test/modules/core/shaderlib/project/viewport-uniforms.spec.js index f47162d0979..f3541dd148e 100644 --- a/test/modules/core/shaderlib/project/viewport-uniforms.spec.js +++ b/test/modules/core/shaderlib/project/viewport-uniforms.spec.js @@ -87,6 +87,8 @@ const UNIFORMS_64 = { project64_uScale: Number }; +const EPSILON = 1e-4; + function getUniformsError(uniforms, formats) { for (const name in UNIFORMS) { const value = uniforms[name]; @@ -106,8 +108,21 @@ test('project#getUniforms', t => { t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated'); t.deepEqual(uniforms.project_uCenter, [0, 0, 0, 0], 'Returned zero projection center'); + uniforms = project.getUniforms({ + viewport: TEST_VIEWPORTS.map, + coordinateSystem: COORDINATE_SYSTEM.CARTESIAN + }); + t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated'); + t.deepEqual(uniforms.project_uCenter, [0, 0, 0, 0], 'Returned zero projection center'); + uniforms = project.getUniforms({viewport: TEST_VIEWPORTS.mapHighZoom}); t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated'); + t.ok(uniforms.project_uCenter.some(x => x), 'Returned non-trivial projection center'); + t.ok( + Math.abs(uniforms.project_uCenter[0]) < EPSILON && + Math.abs(uniforms.project_uCenter[1]) < EPSILON, + 'project center at center of clipspace' + ); t.deepEqual( uniforms.project_uCoordinateOrigin, [-122.42694091796875, 37.75153732299805, 0], @@ -123,6 +138,23 @@ test('project#getUniforms', t => { t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated'); t.ok(uniforms.project_uCenter.some(x => x), 'Returned non-trivial projection center'); + uniforms = project.getUniforms({ + viewport: TEST_VIEWPORTS.mapHighZoom, + coordinateSystem: COORDINATE_SYSTEM.CARTESIAN + }); + t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated'); + t.ok(uniforms.project_uCenter.some(x => x), 'Returned non-trivial projection center'); + t.ok( + Math.abs(uniforms.project_uCenter[0]) < EPSILON && + Math.abs(uniforms.project_uCenter[1]) < EPSILON, + 'project center at center of clipspace' + ); + t.ok( + uniforms.project_uCommonUnitsPerWorldUnit[0] === 1 && + uniforms.project_uCommonUnitsPerWorldUnit[1] === 1, + 'Returned correct distanceScales' + ); + uniforms = project.getUniforms({ viewport: TEST_VIEWPORTS.infoVis, coordinateSystem: COORDINATE_SYSTEM.CARTESIAN From 5146fc654eeb40561eb11df199216911a2260a9a Mon Sep 17 00:00:00 2001 From: Xiaoji Chen Date: Wed, 15 Jan 2020 13:49:57 -0800 Subject: [PATCH 2/2] fix mercator modelmatrix --- modules/core/src/shaderlib/project/project.glsl.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/core/src/shaderlib/project/project.glsl.js b/modules/core/src/shaderlib/project/project.glsl.js index 110c6794b9e..def40543142 100644 --- a/modules/core/src/shaderlib/project/project.glsl.js +++ b/modules/core/src/shaderlib/project/project.glsl.js @@ -111,16 +111,15 @@ vec2 project_mercator_(vec2 lnglat) { // Projects positions (defined by project_uCoordinateSystem) to common space (defined by project_uProjectionMode) // vec4 project_position(vec4 position, vec3 position64Low) { + vec4 position_world = project_uModelMatrix * position; + if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT && project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR) { - return project_uModelMatrix * vec4( - project_mercator_(position.xy) * WORLD_SCALE, - project_size(position.z), - position.w + return vec4( + project_mercator_(position_world.xy) * WORLD_SCALE, + project_size(position_world.z), + position_world.w ); } - - vec4 position_world = project_uModelMatrix * position; - if (project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET && (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT || project_uCoordinateSystem == COORDINATE_SYSTEM_CARTESIAN)) {