Skip to content

Commit

Permalink
support preprojected positions
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoji Chen committed Jan 13, 2020
1 parent 853d972 commit 299d638
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 44 deletions.
4 changes: 3 additions & 1 deletion modules/core/src/shaderlib/project/project.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
110 changes: 79 additions & 31 deletions modules/core/src/shaderlib/project/viewport-uniforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand All @@ -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],
Expand Down Expand Up @@ -96,7 +134,8 @@ function calculateMatrixAndOffset({viewport, coordinateSystem, coordinateOrigin}
viewProjectionMatrix,
projectionCenter,
cameraPosCommon,
shaderCoordinateOrigin
shaderCoordinateOrigin,
geospatialOrigin
};
}

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions test/apps/mapbox-tile/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -95,6 +96,7 @@ class Root extends PureComponent {
layers={[
new TileLayer({
...MAP_LAYER_STYLES,
coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
getPolygonOffset: null,
pickable: true,
getTileData
Expand Down
19 changes: 7 additions & 12 deletions test/apps/mapbox-tile/utils/decode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ 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));

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);

Expand All @@ -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;
}
}
32 changes: 32 additions & 0 deletions test/modules/core/shaderlib/project/viewport-uniforms.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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],
Expand All @@ -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
Expand Down

0 comments on commit 299d638

Please sign in to comment.