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

Globe - basic infrastructure, raster layer adaptation for globe #3783

Merged
merged 53 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
18765bb
Port changes from main globe branch - basics
kubapelc Feb 29, 2024
864fc05
Fix PI redefinitions
kubapelc Mar 1, 2024
9c443ac
Fix stencil shader
kubapelc Mar 1, 2024
d318ae0
Port adaptation of raster layer for globe from main globe branch
kubapelc Mar 1, 2024
9efa2df
Add globe.html example from pheonor's repo
kubapelc Mar 1, 2024
6062c88
Better map projection parameter doc comment, warn when using unknown …
kubapelc Mar 1, 2024
d03d9f8
Mercator projectionData handles negative zoom correctly
kubapelc Mar 4, 2024
6a4f6df
Comment clarification
kubapelc Mar 4, 2024
8e7b42e
Fix spelling of "granularity"
kubapelc Mar 4, 2024
b18607e
Merge tag 'v4.1.0' into kubapelc/globe-pr
kubapelc Mar 4, 2024
9d2d8f1
Add missing docs
kubapelc Mar 4, 2024
ba1798a
Convert ProjectionBase to an interface
kubapelc Mar 5, 2024
925eff6
Do not leak GL object in globe projection error measurement, add a de…
kubapelc Mar 5, 2024
e20c02f
Fix chrome performance warning, refactor error measurement
kubapelc Mar 5, 2024
cb30ec2
Fix granularity capitalization
kubapelc Mar 5, 2024
c3d6973
Fix capitalization
kubapelc Mar 5, 2024
6428456
Fix typo
kubapelc Mar 5, 2024
abf9dfb
Fix stencil mask triangle index order (this was causing failing rende…
kubapelc Mar 5, 2024
3cc71ca
Cleanup vertex shader projection interface
kubapelc Mar 6, 2024
cf797bc
Move projection creation function into its own file
kubapelc Mar 9, 2024
d4b861e
Remove getProjectionName
kubapelc Mar 9, 2024
532180d
Added comment for deduplicateWrapped
kubapelc Mar 9, 2024
cdb98ec
Remove unused vertex-buffer-related code from image source
kubapelc Mar 11, 2024
e36c1e9
Add globe raster layer render test
kubapelc Mar 12, 2024
3612661
More render tests - test transition to mercator
kubapelc Mar 12, 2024
667d654
Remove pointless test, add test descriptions
kubapelc Mar 12, 2024
01e84e3
Render test for rendering poles on globe
kubapelc Mar 12, 2024
7d6fdb0
SubdivisionGranularitySetting constructor takes an object
kubapelc Mar 13, 2024
89b568c
Remove "defines" parameter from useProgram
kubapelc Mar 13, 2024
1c9ebb1
Refactor useProgram and Program constructor
kubapelc Mar 13, 2024
23533e8
Properly format translatePosMatrix comment
kubapelc Mar 13, 2024
d1d4960
Refactor globe-specific code outside projection classes, remove stenc…
kubapelc Mar 13, 2024
ff58e89
Refactor granularity settings to be more readable
kubapelc Mar 13, 2024
5f0f31f
Minor refactor of ProjectionErrorMeasurement
kubapelc Mar 13, 2024
d6136a7
Refactor draw_raster.ts
kubapelc Mar 13, 2024
31edcdd
Move globe utility functions to utils.ts, use easeCubicInOut instead …
kubapelc Mar 13, 2024
d2048b4
Simplify imports in globe.ts
kubapelc Mar 13, 2024
a0d8e50
globe.ts refactor
kubapelc Mar 13, 2024
c798bc8
Move ProjectionErrorMeasurement to a separate file
kubapelc Mar 13, 2024
3b392fe
Refactor ProjectionErrorMeasurement
kubapelc Mar 14, 2024
f12d996
Refactor draw_raster.ts
kubapelc Mar 14, 2024
80b4dd3
Refactor globe projection error measurement to not use Painter
kubapelc Mar 14, 2024
fbc654e
Painter.clearStencil creates custom ProjectionData instead of calling…
kubapelc Mar 14, 2024
01e55b1
Remove "deduplicateWrapped" functionality from source_cache.ts
kubapelc Mar 14, 2024
20cdfee
Globe projection no longer requires a map instance
kubapelc Mar 14, 2024
7363ed1
Painter doesn't pass `this` to `updateGPUdependent`
kubapelc Mar 14, 2024
560dd34
isRenderingDirty is now a function
kubapelc Mar 14, 2024
7494c02
Rename ProjectionBase to Projection
kubapelc Mar 14, 2024
79bacd0
Replace globeView property with setGlobeViewAllowed
kubapelc Mar 14, 2024
5054de0
Add mercator and globe projection unit tests
kubapelc Mar 14, 2024
8915b67
Remove tests that test for exact clipping planes
kubapelc Mar 14, 2024
d98921d
Update build test with new bundle size
kubapelc Mar 14, 2024
4526c16
isRenderingDirty is now a function
kubapelc Mar 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/data/load_geometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function loadGeometry(feature: VectorTileFeature): Array<Array<Point>> {
for (let p = 0; p < ring.length; p++) {
const point = ring[p];
// round here because mapbox-gl-native uses integers to represent
// points and we need to do the same to avoid renering differences.
// points and we need to do the same to avoid rendering differences.
const x = Math.round(point.x * scale);
const y = Math.round(point.y * scale);

Expand Down
7 changes: 7 additions & 0 deletions src/geo/lng_lat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,14 @@ export type LngLatLike = LngLat | {
* @see [Create a timeline animation](https://maplibre.org/maplibre-gl-js/docs/examples/timeline-animation/)
*/
export class LngLat {
/**
* Longitude, measured in degrees.
*/
lng: number;

/**
* Latitude, measured in degrees.
*/
lat: number;

/**
Expand Down
766 changes: 766 additions & 0 deletions src/geo/projection/globe.ts

Large diffs are not rendered by default.

162 changes: 162 additions & 0 deletions src/geo/projection/mercator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import {mat4} from 'gl-matrix';
import {Painter} from '../../render/painter';
import {Transform} from '../transform';
import {ProjectionBase} from './projection_base';
import {UnwrappedTileID} from '../../source/tile_id';
import Point from '@mapbox/point-geometry';
import {Tile} from '../../source/tile';
import {ProjectionData} from '../../render/program/projection_program';
import {pixelsToTileUnits} from '../../source/pixels_to_tile_units';
import {EXTENT} from '../../data/extent';
import {PreparedShader, shaders} from '../../shaders/shaders';

export const MercatorShaderDefine = '#define PROJECTION_MERCATOR';
export const MercatorShaderVariantKey = 'mercator';

export class MercatorProjection implements ProjectionBase {
get name(): string {
return 'mercator';
}

get useSpecialProjectionForSymbols(): boolean {
return false;
}

get isRenderingDirty(): boolean {
// Mercator projection does no animations of its own, so rendering is never dirty from its perspective.
return false;
}

get drawWrappedTiles(): boolean {
// Mercator always needs to draw wrapped/duplicated tiles.
return true;
}

get shaderVariantName(): string {
return MercatorShaderVariantKey;
}

get shaderDefine(): string {
return MercatorShaderDefine;
}

get shaderPreludeCode(): PreparedShader {
return shaders.projectionMercator;
}

get vertexShaderPreludeCode(): string {
return shaders.projectionMercator.vertexSource;
}

destroy(): void {
// Do nothing.
}

updateGPUdependent(_: Painter): void {
// Do nothing.
}

updateProjection(_: Transform): void {
// Do nothing.
}

getProjectionData(canonicalTileCoords: {x: number; y: number; z: number}, tilePosMatrix: mat4): ProjectionData {
let tileOffsetSize: [number, number, number, number];

if (canonicalTileCoords) {
const scale = (canonicalTileCoords.z >= 0) ? (1 << canonicalTileCoords.z) : Math.pow(2.0, canonicalTileCoords.z);
tileOffsetSize = [
canonicalTileCoords.x / scale,
canonicalTileCoords.y / scale,
1.0 / scale / EXTENT,
1.0 / scale / EXTENT
];
} else {
tileOffsetSize = [0, 0, 1, 1];
}
const mainMatrix = tilePosMatrix ? tilePosMatrix : mat4.create();

const data: ProjectionData = {
'u_projection_matrix': mainMatrix, // Might be set to a custom matrix by different projections
'u_projection_tile_mercator_coords': tileOffsetSize,
'u_projection_clipping_plane': [0, 0, 0, 0],
'u_projection_transition': 0.0,
'u_projection_fallback_matrix': mainMatrix,
};

return data;
}

isOccluded(_: number, __: number, ___: UnwrappedTileID): boolean {
return false;
}

project(_x: number, _y: number, _unwrappedTileID: UnwrappedTileID): {
point: Point;
signedDistanceFromCamera: number;
isOccluded: boolean;
} {
// This function should only be used when useSpecialProjectionForSymbols is set to true.
throw new Error('Not implemented.');
}

getPixelScale(_: Transform): number {
return 1.0;
}

translatePosition(transform: Transform, tile: Tile, translate: [number, number], translateAnchor: 'map' | 'viewport'): [number, number] {
return translatePosition(transform, tile, translate, translateAnchor);
}
}

/**
* Transform a matrix to incorporate the *-translate and *-translate-anchor properties into it.
* @param inViewportPixelUnitsUnits - True when the units accepted by the matrix are in viewport pixels instead of tile units.
* @returns matrix
*/
export function translatePosMatrix(
transform: Transform,
tile: Tile,
matrix: mat4,
translate: [number, number],
translateAnchor: 'map' | 'viewport',
inViewportPixelUnitsUnits: boolean = false
): mat4 {
if (!translate[0] && !translate[1]) return matrix;

const translation = translatePosition(transform, tile, translate, translateAnchor, inViewportPixelUnitsUnits);
const translatedMatrix = new Float32Array(16);
mat4.translate(translatedMatrix, matrix, [translation[0], translation[1], 0]);
return translatedMatrix;
}

/**
* Returns a translation in tile units that correctly incorporates the view angle and the *-translate and *-translate-anchor properties.
* @param inViewportPixelUnitsUnits - True when the units accepted by the matrix are in viewport pixels instead of tile units.
*/
export function translatePosition(
transform: Transform,
tile: Tile,
translate: [number, number],
translateAnchor: 'map' | 'viewport',
inViewportPixelUnitsUnits: boolean = false
): [number, number] {
if (!translate[0] && !translate[1]) return [0, 0];

const angle = inViewportPixelUnitsUnits ?
(translateAnchor === 'map' ? transform.angle : 0) :
(translateAnchor === 'viewport' ? -transform.angle : 0);

if (angle) {
const sinA = Math.sin(angle);
const cosA = Math.cos(angle);
translate = [
translate[0] * cosA - translate[1] * sinA,
translate[0] * sinA + translate[1] * cosA
];
}

return [
inViewportPixelUnitsUnits ? translate[0] : pixelsToTileUnits(tile, translate[0], transform.zoom),
inViewportPixelUnitsUnits ? translate[1] : pixelsToTileUnits(tile, translate[1], transform.zoom)];
}
120 changes: 120 additions & 0 deletions src/geo/projection/projection_base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {mat4} from 'gl-matrix';
import {Painter} from '../../render/painter';
import {Tile} from '../../source/tile';
import {UnwrappedTileID} from '../../source/tile_id';
import {Transform} from '../transform';
import Point from '@mapbox/point-geometry';
import {ProjectionData} from '../../render/program/projection_program';
import {PreparedShader} from '../../shaders/shaders';

/**
* An abstract class the specializations of which are used internally by MapLibre to handle different projections.
*/
export interface ProjectionBase {
/**
* @internal
* A short, descriptive name of this projection, such as 'mercator' or 'globe'.
*/
get name(): string;
HarelM marked this conversation as resolved.
Show resolved Hide resolved

/**
* @internal
* True if symbols should use the `project` method of the current ProjectionBase class
* instead of the default (and fast) mercator projection path.
*/
get useSpecialProjectionForSymbols(): boolean;

/**
* @internal
* True when an animation handled by the projection is in progress,
* requiring MapLibre to keep rendering new frames.
*/
get isRenderingDirty(): boolean;

/**
* @internal
* True if this projection required wrapped copies of the world to be drawn.
*/
get drawWrappedTiles(): boolean;

/**
* Name of the shader projection variant that should be used for this projection.
* Note that this value may change dynamically, for example when globe projection internally transitions to mercator.
* Then globe projection might start reporting the mercator shader variant name to make MapLibre use faster mercator shaders.
*/
get shaderVariantName(): string;

/**
* A `#define` macro that is injected into every MapLibre shader that uses this projection.
* @example
* `const define = projection.shaderDefine; // '#define GLOBE'`
*/
get shaderDefine(): string;

/**
* @internal
* A preprocessed prelude code for both vertex and fragment shaders.
*/
get shaderPreludeCode(): PreparedShader;

/**
* Vertex shader code that is injected into every MapLibre vertex shader that uses this projection.
*/
get vertexShaderPreludeCode(): string;

/**
* @internal
* Cleans up any resources the projection created, especially GPU buffers.
*/
destroy(): void;

/**
* @internal
* Runs any GPU-side tasks this projection required. Called at the beginning of every frame.
*/
updateGPUdependent(painter: Painter): void;

/**
* @internal
* Updates the projection for current transform, such as recomputing internal matrices.
* May change the value of `isRenderingDirty`.
*/
updateProjection(transform: Transform): void;

/**
* @internal
* Generates a `ProjectionData` instance to be used while rendering the supplied tile.
*/
getProjectionData(canonicalTileCoords: {x: number; y: number; z: number}, tilePosMatrix: mat4): ProjectionData;

/**
* @internal
* Returns whether the supplied location is occluded in this projection.
* For example during globe rendering a location on the backfacing side of the globe is occluded.
* @param x - Tile space coordinate in range 0..EXTENT.
* @param y - Tile space coordinate in range 0..EXTENT.
* @param unwrappedTileID - TileID of the tile the supplied coordinates belong to.
*/
isOccluded(x: number, y: number, unwrappedTileID: UnwrappedTileID): boolean;

/**
* @internal
* Projects a point in tile coordinates. Used in symbol rendering.
*/
project(x: number, y: number, unwrappedTileID: UnwrappedTileID): {
point: Point;
signedDistanceFromCamera: number;
isOccluded: boolean;
};

/**
* @internal
*/
getPixelScale(transform: Transform): number;

/**
* @internal
* Returns a translation in tile units that correctly incorporates the view angle and the *-translate and *-translate-anchor properties.
*/
translatePosition(transform: Transform, tile: Tile, translate: [number, number], translateAnchor: 'map' | 'viewport'): [number, number];
}
15 changes: 15 additions & 0 deletions src/geo/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,19 @@ export class Transform {
scale: number;
width: number;
height: number;

/**
* This transform's bearing in radians.
*/
angle: number;
rotationMatrix: mat2;
pixelsToGLUnits: [number, number];

/**
* Distance from camera origin to view plane, in pixels.
* Calculated using vertical fov and viewport height.
* Center is considered to be in the middle of the viewport.
*/
cameraToCenterDistance: number;
mercatorMatrix: mat4;
projMatrix: mat4;
Expand All @@ -41,7 +51,12 @@ export class Transform {
glCoordMatrix: mat4;
labelPlaneMatrix: mat4;
minElevationForCurrentTile: number;

/**
* Vertical field of view in radians.
*/
_fov: number;

_pitch: number;
_zoom: number;
_unmodified: boolean;
Expand Down
5 changes: 5 additions & 0 deletions src/gl/cull_face_mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export class CullFaceMode {
}

static disabled: Readonly<CullFaceMode>;

/**
* The standard GL cull mode. Culls backfacing triangles when counterclockwise vertex order is used.
* Use for 3D geometry such as terrain.
*/
static backCCW: Readonly<CullFaceMode>;
}

Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function drawBackground(painter: Painter, sourceCache: SourceCache, layer
const terrainData = painter.style.map.terrain && painter.style.map.terrain.getTerrainData(tileID);

program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
uniformValues, terrainData, layer.id, painter.tileExtentBuffer,
uniformValues, terrainData, null, layer.id, painter.tileExtentBuffer,
painter.quadTriangleIndexBuffer, painter.tileExtentSegments);
}
}
2 changes: 1 addition & 1 deletion src/render/draw_circle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function drawCircles(painter: Painter, sourceCache: SourceCache, layer: C
const segments = segmentsState.segments;

program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
uniformValues, terrainData, layer.id,
uniformValues, terrainData, null, layer.id,
layoutVertexBuffer, indexBuffer, segments,
layer.paint, painter.transform.zoom, programConfiguration);
}
Expand Down
3 changes: 2 additions & 1 deletion src/render/draw_collision_debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, l
posMatrix,
painter.transform,
tile),
painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord),
painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord), null,
layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer,
buffers.segments, null, painter.transform.zoom, null, null,
buffers.collisionVertexBuffer);
Expand Down Expand Up @@ -134,6 +134,7 @@ export function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, l
CullFaceMode.disabled,
uniforms,
painter.style.map.terrain && painter.style.map.terrain.getTerrainData(batch.coord),
null,
layer.id,
vertexBuffer,
indexBuffer,
Expand Down
Loading
Loading