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

Sky Implementation according to spec #3645

Merged
merged 45 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ae58a36
copy code from old sky branch, but this branch is not ready yet
Jan 19, 2024
5af7c1c
add missing _fogMatrixCache clearance
Jan 30, 2024
1e7f6b1
change long comment from // to /** */ syntax
Jan 31, 2024
7918fba
serialize sky in map.getStyle()
Jan 31, 2024
32ae12b
add sky/fog expect tests (#3649)
acalcutt Feb 1, 2024
84de1de
fix sky2 calculateFogMatrix error and build error (#3651)
acalcutt Feb 2, 2024
7e07a31
Merge branch 'main' into sky2
HarelM Feb 2, 2024
f93c39e
Update test/build/min.test.ts
HarelM Feb 2, 2024
e359524
Merge branch 'main' into sky2
HarelM Feb 14, 2024
edbaa5d
Merge branch 'main' into sky2
HarelM Feb 29, 2024
797f156
Merge branch 'main' into sky2
HarelM Feb 29, 2024
865dbe9
Update diff function to take into consideration sky, updated tests
HarelM Feb 29, 2024
a6857a6
Move gl logic to draw_sky file, added tests to cover serialization.
HarelM Feb 29, 2024
6c084fc
Update docs comment
HarelM Feb 29, 2024
e9b16bb
Merge branch 'main' into sky2
HarelM Feb 29, 2024
6fed1af
Add missing tests to map.test.ts
HarelM Feb 29, 2024
3726ee9
Merge branch 'main' into sky2
HarelM Feb 29, 2024
757bcb5
Remove unneeded member variable
HarelM Mar 7, 2024
363529f
Fix build
HarelM Mar 7, 2024
8c23b36
Add cache to draw sky mesh buffer
HarelM Mar 7, 2024
b5b8793
Use globe's mesh idea, store mesh in sky object, similar to how it is…
HarelM Mar 7, 2024
61ad9c6
Merge branch 'main' into sky2
HarelM Jun 20, 2024
25e7d56
Fix tests
HarelM Jun 20, 2024
fa38f81
Fix names according to new spec
HarelM Jun 20, 2024
b44583d
Fix render and build tests
HarelM Jun 20, 2024
0fb7c88
Changed name to sky-horizon-blend
HarelM Jun 20, 2024
c1fb5c7
Fix lint
HarelM Jun 20, 2024
fe973a5
Add support for blend in fog and horizon
HarelM Jun 21, 2024
e9ec6b9
Terrain fog blending improvements (#4314)
kubapelc Jun 22, 2024
512d73e
Merge branch 'main' into sky2
HarelM Jun 22, 2024
8f1c5aa
Added a debug page, fixed a bug
HarelM Jun 22, 2024
d4fbe6d
Update the example to show the values in the spec.
HarelM Jun 22, 2024
964d177
Added test to reproduce the issue
HarelM Jun 22, 2024
f5bf079
Move sky test to a different folder
HarelM Jun 22, 2024
af0d86d
Fix lint
HarelM Jun 22, 2024
0e20a96
Remove unneeded reference from example
HarelM Jun 22, 2024
eda9122
Always initialize sky, remove ifs, fix tests
HarelM Jun 25, 2024
1d894c7
Update CHANGELOG.md
HarelM Jun 25, 2024
cd78810
Expect build to fail due to missing image in examples
HarelM Jun 26, 2024
6ae936d
Merge branch 'sky2' of https://github.com/maplibre/maplibre-gl-js int…
HarelM Jun 26, 2024
34ab2fc
Fix docs build
HarelM Jun 26, 2024
9ed0f6c
Fix docs build
HarelM Jun 26, 2024
25aa877
Compress image
HarelM Jun 26, 2024
d17bbe1
Update readme
HarelM Jun 26, 2024
592ded2
Merge branch 'main' into sky2
HarelM Jun 26, 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
6 changes: 5 additions & 1 deletion .github/workflows/test-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ jobs:
- run: npm run typecheck
if: '!cancelled()'
- run: npm run generate-docs
- run: docker run --rm -v ${PWD}:/docs squidfunk/mkdocs-material build
- name: Build docs using docker and mkdocs-material
run: |
rm docs/README.md
docker run --rm -v ${PWD}:/docs squidfunk/mkdocs-material build --strict


unit-tests:
name: Unit tests and Coverage
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/docs/API
/docs/examples
/docs/example
/site/
.cache/
*.es.js
*.js.map
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## main

### ✨ Features and improvements
- Add sky implementation according to spec ([#3645](https://github.com/maplibre/maplibre-gl-js/pull/3645))
- _...Add new stuff here..._

### 🐞 Bug fixes
Expand Down
21 changes: 15 additions & 6 deletions build/generate-doc-images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import puppeteer from 'puppeteer';
import packageJson from '../package.json' with { type: 'json' };

const exampleName = process.argv[2];
const useLocalhost = (process.argv.length > 3) && (process.argv[3] === 'serve');
const examplePath = path.resolve('test', 'examples');

const browser = await puppeteer.launch({headless: exampleName === 'all'});
const browser = await puppeteer.launch({headless: true});

const page = await browser.newPage();
// set viewport and double deviceScaleFactor to get a closer shot of the map
Expand All @@ -18,9 +19,13 @@ await page.setViewport({

async function createImage(exampleName) {
// get the example contents
const html = fs.readFileSync(path.resolve(examplePath, `${exampleName}.html`), 'utf-8');

await page.setContent(html.replaceAll('../../dist', `https://unpkg.com/maplibre-gl@${packageJson.version}/dist`));
if (useLocalhost) {
console.log('Using localhost to serve examples.');
await page.goto(`http://localhost:9966/test/examples/${exampleName}.html`);
} else {
const html = fs.readFileSync(path.resolve(examplePath, `${exampleName}.html`), 'utf-8');
await page.setContent(html.replaceAll('../../dist', `https://unpkg.com/maplibre-gl@${packageJson.version}/dist`));
}

// Wait for map to load, then wait two more seconds for images, etc. to load.
try {
Expand Down Expand Up @@ -61,8 +66,12 @@ if (exampleName === 'all') {
} else if (exampleName) {
await createImage(exampleName);
} else {
throw new Error(
'\n Usage: npm run generate-images <file-name|all>\nExample: npm run generate-images 3d-buildings'
throw new Error(`
Usage: npm run generate-images <file-name|all> [serve]
file-name: the name of the example file in test/examples without the .html extension.
all: generate images for all examples.
serve: use localhost to serve examples - use 'npm run start' with this option, otherwise it will use the latest published version in npm.
Example: npm run generate-images 3d-buildings serve`
);
}

Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Examples are written as regular html files in `test/examples`. Each example shou
When you create a new example, you **must** make an accompanying image.

1. Run `npm run generate-images <example-file-name>`. The script will take a screenshot of the map in the example and save it to `docs/assets/examples/`.
2. Optimize the image with [Squoosh](https://squoosh.app/) to reduce the file size. (Optional)
2. Optimize the image with [compresspng](https://compresspng.com/) to reduce the file size. (Optional)
3. Commit the image.

For some examples, `npm run generate-images` does not generate an ideal image. In these cases, you can interact with the map after running the command before the screenshot is taken, or take a screenshot yourself by running the site locally with `npm start`, take a screenshot and save it in the `docs/assets/examples/` folder.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/guides/large-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Once the data is loaded, to ensure a smooth user experience, it's essential to o

One simple approach is to visualise fewer points. If we are using a GeoJSON source (i.e. not vector tiles), we can use 'clustering' to group nearby points together. This approach reduces the number of features displayed on the map, improving rendering performance and maintaining map readability.

To do this, when we add the data, we can adjust the [cluster options](/maplibre-gl-js/docs/API/type-aliases/SetClusterOptions/). For example:
To do this, when we add the data, we can adjust the [cluster options](../API/type-aliases/SetClusterOptions.md). For example:
HarelM marked this conversation as resolved.
Show resolved Hide resolved

```javascript
map.addSource('earthquakes', {
Expand Down
5 changes: 5 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,8 @@ plugins:
- social:
cards_layout_options:
background_color: '#295DAA'
validation:
omitted_files: warn
absolute_links: warn
unrecognized_links: warn
anchors: warn
56 changes: 49 additions & 7 deletions src/geo/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class Transform {
modelViewProjectionMatrix: mat4;
invModelViewProjectionMatrix: mat4;
alignedModelViewProjectionMatrix: mat4;
fogMatrix: mat4;
pixelMatrix: mat4;
pixelMatrix3D: mat4;
pixelMatrixInverse: mat4;
Expand All @@ -58,6 +59,7 @@ export class Transform {
_constraining: boolean;
_posMatrixCache: {[_: string]: mat4};
_alignedPosMatrixCache: {[_: string]: mat4};
_fogMatrixCache: {[_: string]: mat4};

constructor(minZoom?: number, maxZoom?: number, minPitch?: number, maxPitch?: number, renderWorldCopies?: boolean) {
this.tileSize = 512; // constant
Expand All @@ -83,6 +85,7 @@ export class Transform {
this._edgeInsets = new EdgeInsets();
this._posMatrixCache = {};
this._alignedPosMatrixCache = {};
this._fogMatrixCache = {};
this.minElevationForCurrentTile = 0;
}

Expand Down Expand Up @@ -690,6 +693,17 @@ export class Transform {
}
}

calculateTileMatrix(unwrappedTileID: UnwrappedTileID): mat4 {
const canonical = unwrappedTileID.canonical;
const scale = this.worldSize / this.zoomScale(canonical.z);
const unwrappedX = canonical.x + Math.pow(2, canonical.z) * unwrappedTileID.wrap;

const worldMatrix = mat4.identity(new Float64Array(16) as any);
mat4.translate(worldMatrix, worldMatrix, [unwrappedX * scale, canonical.y * scale, 0]);
mat4.scale(worldMatrix, worldMatrix, [scale / EXTENT, scale / EXTENT, 1]);
return worldMatrix;
}

/**
* Calculate the posMatrix that, given a tile coordinate, would be used to display the tile on a map.
* @param unwrappedTileID - the tile ID
Expand All @@ -701,19 +715,32 @@ export class Transform {
return cache[posMatrixKey];
}

const canonical = unwrappedTileID.canonical;
const scale = this.worldSize / this.zoomScale(canonical.z);
const unwrappedX = canonical.x + Math.pow(2, canonical.z) * unwrappedTileID.wrap;

const posMatrix = mat4.identity(new Float64Array(16) as any);
mat4.translate(posMatrix, posMatrix, [unwrappedX * scale, canonical.y * scale, 0]);
mat4.scale(posMatrix, posMatrix, [scale / EXTENT, scale / EXTENT, 1]);
const posMatrix = this.calculateTileMatrix(unwrappedTileID);
mat4.multiply(posMatrix, aligned ? this.alignedModelViewProjectionMatrix : this.modelViewProjectionMatrix, posMatrix);

cache[posMatrixKey] = new Float32Array(posMatrix);
return cache[posMatrixKey];
}

/**
* Calculate the fogMatrix that, given a tile coordinate, would be used to calculate fog on the map.
* @param unwrappedTileID - the tile ID
* @private
*/
calculateFogMatrix(unwrappedTileID: UnwrappedTileID): mat4 {
const posMatrixKey = unwrappedTileID.key;
const cache = this._fogMatrixCache;
if (cache[posMatrixKey]) {
return cache[posMatrixKey];
}

const fogMatrix = this.calculateTileMatrix(unwrappedTileID);
mat4.multiply(fogMatrix, this.fogMatrix, fogMatrix);

cache[posMatrixKey] = new Float32Array(fogMatrix);
return cache[posMatrixKey];
}

customLayerMatrix(): mat4 {
return this.mercatorMatrix.slice() as any;
}
Expand Down Expand Up @@ -910,6 +937,20 @@ export class Transform {
this.modelViewProjectionMatrix = m;
this.invModelViewProjectionMatrix = mat4.invert([] as any, m);

// create a fog matrix, same es proj-matrix but with near clipping-plane in mapcenter
// needed to calculate a correct z-value for fog calculation, because projMatrix z value is not
this.fogMatrix = new Float64Array(16) as any;
mat4.perspective(this.fogMatrix, this._fov, this.width / this.height, cameraToSeaLevelDistance, farZ);
this.fogMatrix[8] = -offset.x * 2 / this.width;
this.fogMatrix[9] = offset.y * 2 / this.height;
mat4.scale(this.fogMatrix, this.fogMatrix, [1, -1, 1]);
mat4.translate(this.fogMatrix, this.fogMatrix, [0, 0, -this.cameraToCenterDistance]);
mat4.rotateX(this.fogMatrix, this.fogMatrix, this._pitch);
mat4.rotateZ(this.fogMatrix, this.fogMatrix, this.angle);
mat4.translate(this.fogMatrix, this.fogMatrix, [-x, -y, 0]);
mat4.scale(this.fogMatrix, this.fogMatrix, [1, 1, this._pixelPerMeter]);
mat4.translate(this.fogMatrix, this.fogMatrix, [0, 0, -this.elevation]); // elevate camera over terrain

// matrix for conversion from world space to screen coordinates in 3D
this.pixelMatrix3D = mat4.multiply(new Float64Array(16) as any, this.labelPlaneMatrix, m);

Expand All @@ -934,6 +975,7 @@ export class Transform {

this._posMatrixCache = {};
this._alignedPosMatrixCache = {};
this._fogMatrixCache = {};
}

maxPitchScaleFactor() {
Expand Down
44 changes: 44 additions & 0 deletions src/render/draw_sky.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {StencilMode} from '../gl/stencil_mode';
import {DepthMode} from '../gl/depth_mode';
import {CullFaceMode} from '../gl/cull_face_mode';
import {PosArray, TriangleIndexArray} from '../data/array_types.g';
import posAttributes from '../data/pos_attributes';
import {SegmentVector} from '../data/segment';
import {skyUniformValues} from './program/sky_program';
import {Sky} from '../style/sky';
import {Mesh} from './mesh';
import type {Painter} from './painter';

export function drawSky(painter: Painter, sky: Sky) {
const context = painter.context;
const gl = context.gl;

const skyUniforms = skyUniformValues(sky, painter.style.map.transform, painter.pixelRatio);

const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
const stencilMode = StencilMode.disabled;
const colorMode = painter.colorModeForRenderPass();
const program = painter.useProgram('sky');

if (!sky.mesh) {
const vertexArray = new PosArray();
vertexArray.emplaceBack(-1, -1);
vertexArray.emplaceBack(1, -1);
vertexArray.emplaceBack(1, 1);
vertexArray.emplaceBack(-1, 1);

const indexArray = new TriangleIndexArray();
indexArray.emplaceBack(0, 1, 2);
indexArray.emplaceBack(0, 2, 3);

sky.mesh = new Mesh(
context.createVertexBuffer(vertexArray, posAttributes.members),
context.createIndexBuffer(indexArray),
SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length)
);
}

program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode,
HarelM marked this conversation as resolved.
Show resolved Hide resolved
CullFaceMode.disabled, skyUniforms, undefined, 'sky', sky.mesh.vertexBuffer,
sky.mesh.indexBuffer, sky.mesh.segments);
}
4 changes: 3 additions & 1 deletion src/render/draw_terrain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ function drawTerrain(painter: Painter, terrain: Terrain, tiles: Array<Tile>) {
context.activeTexture.set(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
const posMatrix = painter.transform.calculatePosMatrix(tile.tileID.toUnwrapped());
const uniformValues = terrainUniformValues(posMatrix, terrain.getMeshFrameDelta(painter.transform.zoom));
const eleDelta = terrain.getMeshFrameDelta(painter.transform.zoom);
const fogMatrix = painter.transform.calculateFogMatrix(tile.tileID.toUnwrapped());
const uniformValues = terrainUniformValues(posMatrix, eleDelta, fogMatrix, painter.style.sky, painter.transform.pitch);
program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, 'terrain', mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
}

Expand Down
25 changes: 25 additions & 0 deletions src/render/mesh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {SegmentVector} from '../data/segment';
import {VertexBuffer} from '../gl/vertex_buffer';
import {IndexBuffer} from '../gl/index_buffer';

export class Mesh {
vertexBuffer: VertexBuffer;
indexBuffer: IndexBuffer;
segments: SegmentVector;

constructor(vertexBuffer: VertexBuffer, indexBuffer: IndexBuffer, segments: SegmentVector) {
this.vertexBuffer = vertexBuffer;
this.indexBuffer = indexBuffer;
this.segments = segments;
}

destroy(): void {
this.vertexBuffer.destroy();
this.indexBuffer.destroy();
this.segments.destroy();

this.vertexBuffer = null;
this.indexBuffer = null;
this.segments = null;
}
}
6 changes: 5 additions & 1 deletion src/render/painter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {drawDebug, drawDebugPadding, selectDebugSource} from './draw_debug';
import {drawCustom} from './draw_custom';
import {drawDepth, drawCoords} from './draw_terrain';
import {OverscaledTileID} from '../source/tile_id';
import {RenderToTexture} from './render_to_texture';
import {drawSky} from './draw_sky';

import type {Transform} from '../geo/transform';
import type {Tile} from '../source/tile';
Expand All @@ -46,7 +48,6 @@ import type {VertexBuffer} from '../gl/vertex_buffer';
import type {IndexBuffer} from '../gl/index_buffer';
import type {DepthRangeType, DepthMaskType, DepthFuncType} from '../gl/types';
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
import {RenderToTexture} from './render_to_texture';

export type RenderPass = 'offscreen' | 'opaque' | 'translucent';

Expand Down Expand Up @@ -407,6 +408,9 @@ export class Painter {
this.context.clear({color: options.showOverdrawInspector ? Color.black : Color.transparent, depth: 1});
this.clearStencil();

// draw sky first to not overwrite symbols
if (this.style.stylesheet.sky) drawSky(this, this.style.sky);

this._showOverdrawInspector = options.showOverdrawInspector;
this.depthRangeFor3D = [0, 1 - ((style._order.length + 2) * this.numSublayers * this.depthEpsilon)];

Expand Down
4 changes: 3 additions & 1 deletion src/render/program/program_uniforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {rasterUniforms} from './raster_program';
import {symbolIconUniforms, symbolSDFUniforms, symbolTextAndIconUniforms} from './symbol_program';
import {backgroundUniforms, backgroundPatternUniforms} from './background_program';
import {terrainUniforms, terrainDepthUniforms, terrainCoordsUniforms} from './terrain_program';
import {skyUniforms} from './sky_program';

export const programUniforms = {
fillExtrusion: fillExtrusionUniforms,
Expand Down Expand Up @@ -40,5 +41,6 @@ export const programUniforms = {
backgroundPattern: backgroundPatternUniforms,
terrain: terrainUniforms,
terrainDepth: terrainDepthUniforms,
terrainCoords: terrainCoordsUniforms
terrainCoords: terrainCoordsUniforms,
sky: skyUniforms
};
28 changes: 28 additions & 0 deletions src/render/program/sky_program.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {UniformColor, Uniform1f} from '../uniform_binding';
import type {Context} from '../../gl/context';
import type {UniformValues, UniformLocations} from '../uniform_binding';
import {Transform} from '../../geo/transform';
import {Sky} from '../../style/sky';

export type SkyUniformsType = {
'u_sky_color': UniformColor;
'u_horizon_color': UniformColor;
'u_horizon': Uniform1f;
'u_sky_horizon_blend': Uniform1f;
};

const skyUniforms = (context: Context, locations: UniformLocations): SkyUniformsType => ({
'u_sky_color': new UniformColor(context, locations.u_sky_color),
'u_horizon_color': new UniformColor(context, locations.u_horizon_color),
'u_horizon': new Uniform1f(context, locations.u_horizon),
'u_sky_horizon_blend': new Uniform1f(context, locations.u_sky_horizon_blend),
});

const skyUniformValues = (sky: Sky, transform: Transform, pixelRatio: number): UniformValues<SkyUniformsType> => ({
'u_sky_color': sky.properties.get('sky-color'),
'u_horizon_color': sky.properties.get('horizon-color'),
'u_horizon': (transform.height / 2 + transform.getHorizon()) * pixelRatio,
'u_sky_horizon_blend': (sky.properties.get('sky-horizon-blend') * transform.height / 2) * pixelRatio,
});

export {skyUniforms, skyUniformValues};
Loading