Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor globe transfrom #5140

Closed
wants to merge 9 commits into from
10 changes: 5 additions & 5 deletions src/geo/projection/covering_tiles.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {OverscaledTileID} from '../../source/tile_id';
import {vec2, type vec4} from 'gl-matrix';
import {type IReadonlyTransform} from '../transform_interface';
import {MercatorCoordinate} from '../mercator_coordinate';
import {scaleZoom} from '../transform_helper';
import {clamp, degreesToRadians} from '../../util/util';
import {type Terrain} from '../../render/terrain';
import {type Frustum} from '../../util/primitives/frustum';
import {clamp, degreesToRadians, scaleZoom} from '../../util/util';
import {type Aabb, IntersectionResult} from '../../util/primitives/aabb';

import type {IReadonlyTransform} from '../transform_interface';
import type {Terrain} from '../../render/terrain';
import type {Frustum} from '../../util/primitives/frustum';

type CoveringTilesResult = {
tileID: OverscaledTileID;
distanceSq: number;
Expand Down
15 changes: 8 additions & 7 deletions src/geo/projection/globe_camera_helper.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import Point from '@mapbox/point-geometry';
import {type IReadonlyTransform, type ITransform} from '../transform_interface';
import {cameraBoundsWarning, type CameraForBoxAndBearingHandlerResult, type EaseToHandlerResult, type EaseToHandlerOptions, type FlyToHandlerResult, type FlyToHandlerOptions, type ICameraHelper, type MapControlsDeltas, updateRotation, type UpdateRotationArgs} from './camera_helper';
import {type GlobeProjection} from './globe';
import {LngLat, type LngLatLike} from '../lng_lat';
import {MercatorCameraHelper} from './mercator_camera_helper';
import {angularCoordinatesToSurfaceVector, computeGlobePanCenter, getGlobeRadiusPixels, getZoomAdjustment, globeDistanceOfLocationsPixels, interpolateLngLatForGlobe} from './globe_utils';
import {clamp, createVec3f64, differenceOfAnglesDegrees, remapSaturate, rollPitchBearingEqual, warnOnce} from '../../util/util';
import {clamp, createVec3f64, differenceOfAnglesDegrees, MAX_VALID_LATITUDE, remapSaturate, rollPitchBearingEqual, scaleZoom, warnOnce, zoomScale} from '../../util/util';
import {type mat4, vec3} from 'gl-matrix';
import {MAX_VALID_LATITUDE, normalizeCenter, scaleZoom, zoomScale} from '../transform_helper';
import {type CameraForBoundsOptions} from '../../ui/camera';
import {type LngLatBounds} from '../lng_lat_bounds';
import {type PaddingOptions} from '../edge_insets';
import {normalizeCenter} from '../transform_helper';
import {interpolates} from '@maplibre/maplibre-gl-style-spec';

import type {IReadonlyTransform, ITransform} from '../transform_interface';
import type {GlobeProjection} from './globe';
import type {CameraForBoundsOptions} from '../../ui/camera';
import type {LngLatBounds} from '../lng_lat_bounds';
import type {PaddingOptions} from '../edge_insets';

/**
* @internal
*/
Expand Down
239 changes: 123 additions & 116 deletions src/geo/projection/globe_transform.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {type mat2, mat4, vec3, vec4} from 'gl-matrix';
import {MAX_VALID_LATITUDE, TransformHelper} from '../transform_helper';
import {TransformHelper} from '../transform_helper';
import {MercatorTransform} from './mercator_transform';
import {LngLat, type LngLatLike, earthRadius} from '../lng_lat';
import {angleToRotateBetweenVectors2D, clamp, createIdentityMat4f32, createIdentityMat4f64, createMat4f32, createMat4f64, createVec3f64, createVec4f64, differenceOfAnglesDegrees, distanceOfAnglesRadians, easeCubicInOut, lerp, pointPlaneSignedDistance, warnOnce} from '../../util/util';
import {angleToRotateBetweenVectors2D, clamp, createIdentityMat4f32, createIdentityMat4f64, createMat4f32, createMat4f64, createVec3f64, createVec4f64, differenceOfAnglesDegrees, distanceOfAnglesRadians, easeCubicInOut, lerp, MAX_VALID_LATITUDE, pointPlaneSignedDistance, warnOnce} from '../../util/util';
import {UnwrappedTileID, OverscaledTileID, type CanonicalTileID} from '../../source/tile_id';
import Point from '@mapbox/point-geometry';
import {browser} from '../../util/browser';
Expand Down Expand Up @@ -215,7 +215,10 @@
get renderWorldCopies(): boolean {
return this._helper.renderWorldCopies;
}

get cameraToCenterDistance(): number {
// Globe uses the same cameraToCenterDistance as mercator.
return this._helper.cameraToCenterDistance;
}
//
// Implementation of globe transform
//
Expand Down Expand Up @@ -301,7 +304,8 @@

clone(): ITransform {
const clone = new GlobeTransform(null, this._globeProjectionAllowed);
clone._applyGlobeTransform(this);
clone._globeness = this._globeness;
clone._globeLatitudeErrorCorrectionRadians = this._globeLatitudeErrorCorrectionRadians;
clone.apply(this);
return clone;
}
Expand All @@ -311,11 +315,6 @@
this._mercatorTransform.apply(this);
}

private _applyGlobeTransform(that: GlobeTransform): void {
this._globeness = that._globeness;
this._globeLatitudeErrorCorrectionRadians = that._globeLatitudeErrorCorrectionRadians;
}

public get projectionMatrix(): mat4 { return this.isGlobeRendering ? this._projectionMatrix : this._mercatorTransform.projectionMatrix; }

public get modelViewProjectionMatrix(): mat4 { return this.isGlobeRendering ? this._globeViewProjMatrixNoCorrection : this._mercatorTransform.modelViewProjectionMatrix; }
Expand All @@ -331,11 +330,6 @@
return copy;
}

get cameraToCenterDistance(): number {
// Globe uses the same cameraToCenterDistance as mercator.
return this._mercatorTransform.cameraToCenterDistance;
}

public get nearZ(): number { return this._nearZ; }
public get farZ(): number { return this._farZ; }

Expand Down Expand Up @@ -455,18 +449,16 @@
}

getProjectionData(params: ProjectionDataParams): ProjectionData {
const {overscaledTileID, aligned, applyTerrainMatrix, applyGlobeMatrix} = params;
const data = this._mercatorTransform.getProjectionData({overscaledTileID, aligned, applyTerrainMatrix});

// Set 'projectionMatrix' to actual globe transform
if (this.isGlobeRendering) {
data.mainMatrix = this._globeViewProjMatrix32f;
}

data.clippingPlane = this._cachedClippingPlane as [number, number, number, number];
data.projectionTransition = applyGlobeMatrix ? this._globeness : 0;
const {overscaledTileID, applyGlobeMatrix} = params;
const mercatorProjectionData = this._mercatorTransform.getProjectionData(params)

return data;
return {
mainMatrix: this.isGlobeRendering ? this._globeViewProjMatrix32f : mercatorProjectionData.mainMatrix,
tileMercatorCoords: this._helper.getTileMercatorCoordinates(overscaledTileID),
clippingPlane: this._cachedClippingPlane as [number, number, number, number],
projectionTransition: applyGlobeMatrix ? this._globeness : 0,
fallbackMatrix: mercatorProjectionData.mainMatrix
};
}

private _computeClippingPlane(globeRadiusPixels: number): vec4 {
Expand Down Expand Up @@ -717,15 +709,15 @@
}

getCameraPoint(): Point {
return this._mercatorTransform.getCameraPoint();
return this._helper.getCameraPoint();
}

getCameraAltitude(): number {
return this._mercatorTransform.getCameraAltitude();
return this._helper.getCameraAltitude();
}

getCameraLngLat(): LngLat {
return this._mercatorTransform.getCameraLngLat();
return this._helper.getCameraLngLat();
}

lngLatToCameraDepth(lngLat: LngLat, elevation: number): number {
Expand All @@ -750,76 +742,7 @@
if (!this.isGlobeRendering) {
return this._mercatorTransform.getBounds();
}

const xMid = this.width * 0.5;
const yMid = this.height * 0.5;

// LngLat extremes will probably tend to be in screen corners or in middle of screen edges.
// These test points should result in a pretty good approximation.
const testPoints = [
new Point(0, 0),
new Point(xMid, 0),
new Point(this.width, 0),
new Point(this.width, yMid),
new Point(this.width, this.height),
new Point(xMid, this.height),
new Point(0, this.height),
new Point(0, yMid),
];

const projectedPoints = [];
for (const p of testPoints) {
projectedPoints.push(this.unprojectScreenPoint(p));
}

// We can't construct a simple min/max aabb, since points might lie on either side of the antimeridian.
// We will instead compute the furthest points relative to map center.
// We also take advantage of the fact that `unprojectScreenPoint` will snap pixels
// outside the planet to the closest point on the planet's horizon.
let mostEast = 0, mostWest = 0, mostNorth = 0, mostSouth = 0; // We will store these values signed.
const center = this.center;
for (const p of projectedPoints) {
const dLng = differenceOfAnglesDegrees(center.lng, p.lng);
const dLat = differenceOfAnglesDegrees(center.lat, p.lat);
if (dLng < mostWest) {
mostWest = dLng;
}
if (dLng > mostEast) {
mostEast = dLng;
}
if (dLat < mostSouth) {
mostSouth = dLat;
}
if (dLat > mostNorth) {
mostNorth = dLat;
}
}

const boundsArray: [number, number, number, number] = [
center.lng + mostWest, // west
center.lat + mostSouth, // south
center.lng + mostEast, // east
center.lat + mostNorth // north
];

// Sometimes the poles might end up not being on the horizon,
// thus not being detected as the northernmost/southernmost points.
// We fix that here.
if (this.isSurfacePointOnScreen([0, 1, 0])) {
// North pole is visible
// This also means that the entire longitude range must be visible
boundsArray[3] = 90;
boundsArray[0] = -180;
boundsArray[2] = 180;
}
if (this.isSurfacePointOnScreen([0, -1, 0])) {
// South pole is visible
boundsArray[1] = -90;
boundsArray[0] = -180;
boundsArray[2] = 180;
}

return new LngLatBounds(boundsArray);
return this.vp_getBounds();
}

getConstrained(lngLat: LngLat, zoom: number): { center: LngLat; zoom: number } {
Expand All @@ -837,7 +760,7 @@
}

calculateCenterFromCameraLngLatAlt(lngLat: LngLatLike, alt: number, bearing?: number, pitch?: number): {center: LngLat; elevation: number; zoom: number} {
return this._mercatorTransform.calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch);
return this._helper.calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch);

Check warning on line 763 in src/geo/projection/globe_transform.ts

View check run for this annotation

Codecov / codecov/patch

src/geo/projection/globe_transform.ts#L763

Added line #L763 was not covered by tests
}

/**
Expand Down Expand Up @@ -998,13 +921,7 @@
if (!this.isGlobeRendering) {
return this._mercatorTransform.isPointOnMapSurface(p, terrain);
}

const rayOrigin = this._cameraPosition;
const rayDirection = this.getRayDirectionFromPixel(p);

const intersection = this.rayPlanetIntersection(rayOrigin, rayDirection);

return !!intersection;
return this.vp_isPointOnMapSurface(p, terrain);
}

/**
Expand Down Expand Up @@ -1173,16 +1090,7 @@
if (!this.isGlobeRendering) {
return this._mercatorTransform.getMatrixForModel(location, altitude);
}
const lnglat = LngLat.convert(location);
const scale = 1.0 / earthRadius;

const m = createIdentityMat4f64();
mat4.rotateY(m, m, lnglat.lng / 180.0 * Math.PI);
mat4.rotateX(m, m, -lnglat.lat / 180.0 * Math.PI);
mat4.translate(m, m, [0, 0, 1 + altitude / earthRadius]);
mat4.rotateX(m, m, Math.PI * 0.5);
mat4.scale(m, m, [scale, scale, scale]);
return m;
return this.vp_getMatrixForModel(location, altitude);

Check warning on line 1093 in src/geo/projection/globe_transform.ts

View check run for this annotation

Codecov / codecov/patch

src/geo/projection/globe_transform.ts#L1093

Added line #L1093 was not covered by tests
}

getProjectionDataForCustomLayer(applyGlobeMatrix: boolean = true): ProjectionData {
Expand All @@ -1208,4 +1116,103 @@
}
return undefined;
}

//
// Vertical perspective area for refactoring... //
//

private vp_getMatrixForModel(location: LngLatLike, altitude?: number): mat4 {
const lnglat = LngLat.convert(location);
const scale = 1.0 / earthRadius;

Check warning on line 1126 in src/geo/projection/globe_transform.ts

View check run for this annotation

Codecov / codecov/patch

src/geo/projection/globe_transform.ts#L1125-L1126

Added lines #L1125 - L1126 were not covered by tests

const m = createIdentityMat4f64();
mat4.rotateY(m, m, lnglat.lng / 180.0 * Math.PI);
mat4.rotateX(m, m, -lnglat.lat / 180.0 * Math.PI);
mat4.translate(m, m, [0, 0, 1 + altitude / earthRadius]);
mat4.rotateX(m, m, Math.PI * 0.5);
mat4.scale(m, m, [scale, scale, scale]);
return m;

Check warning on line 1134 in src/geo/projection/globe_transform.ts

View check run for this annotation

Codecov / codecov/patch

src/geo/projection/globe_transform.ts#L1128-L1134

Added lines #L1128 - L1134 were not covered by tests
}

vp_isPointOnMapSurface(p: Point, _terrain?: Terrain): boolean {
const rayOrigin = this._cameraPosition;
const rayDirection = this.getRayDirectionFromPixel(p);

const intersection = this.rayPlanetIntersection(rayOrigin, rayDirection);

return !!intersection;
}

vp_getBounds(): LngLatBounds {
const xMid = this.width * 0.5;
const yMid = this.height * 0.5;

// LngLat extremes will probably tend to be in screen corners or in middle of screen edges.
// These test points should result in a pretty good approximation.
const testPoints = [
new Point(0, 0),
new Point(xMid, 0),
new Point(this.width, 0),
new Point(this.width, yMid),
new Point(this.width, this.height),
new Point(xMid, this.height),
new Point(0, this.height),
new Point(0, yMid),
];

const projectedPoints = [];
for (const p of testPoints) {
projectedPoints.push(this.unprojectScreenPoint(p));
}

// We can't construct a simple min/max aabb, since points might lie on either side of the antimeridian.
// We will instead compute the furthest points relative to map center.
// We also take advantage of the fact that `unprojectScreenPoint` will snap pixels
// outside the planet to the closest point on the planet's horizon.
let mostEast = 0, mostWest = 0, mostNorth = 0, mostSouth = 0; // We will store these values signed.
const center = this.center;
for (const p of projectedPoints) {
const dLng = differenceOfAnglesDegrees(center.lng, p.lng);
const dLat = differenceOfAnglesDegrees(center.lat, p.lat);
if (dLng < mostWest) {
mostWest = dLng;
}
if (dLng > mostEast) {
mostEast = dLng;
}
if (dLat < mostSouth) {
mostSouth = dLat;
}
if (dLat > mostNorth) {
mostNorth = dLat;
}
}

const boundsArray: [number, number, number, number] = [
center.lng + mostWest, // west
center.lat + mostSouth, // south
center.lng + mostEast, // east
center.lat + mostNorth // north
];

// Sometimes the poles might end up not being on the horizon,
// thus not being detected as the northernmost/southernmost points.
// We fix that here.
if (this.isSurfacePointOnScreen([0, 1, 0])) {
// North pole is visible
// This also means that the entire longitude range must be visible
boundsArray[3] = 90;
boundsArray[0] = -180;
boundsArray[2] = 180;
}

Check warning on line 1207 in src/geo/projection/globe_transform.ts

View check run for this annotation

Codecov / codecov/patch

src/geo/projection/globe_transform.ts#L1204-L1207

Added lines #L1204 - L1207 were not covered by tests
if (this.isSurfacePointOnScreen([0, -1, 0])) {
// South pole is visible
boundsArray[1] = -90;
boundsArray[0] = -180;
boundsArray[2] = 180;
}

return new LngLatBounds(boundsArray);
}

}
5 changes: 2 additions & 3 deletions src/geo/projection/globe_utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {vec3} from 'gl-matrix';
import {clamp, lerp, mod, remapSaturate, wrap} from '../../util/util';
import {clamp, lerp, MAX_VALID_LATITUDE, mod, remapSaturate, scaleZoom, wrap} from '../../util/util';
import {LngLat} from '../lng_lat';
import {MAX_VALID_LATITUDE, scaleZoom} from '../transform_helper';
import type Point from '@mapbox/point-geometry';
import {EXTENT} from '../../data/extent';
import type Point from '@mapbox/point-geometry';

export function getGlobeCircumferencePixels(transform: {worldSize: number; center: {lat: number}}): number {
const radius = getGlobeRadiusPixels(transform.worldSize, transform.center.lat);
Expand Down
Loading
Loading