maplibre-gl 6.0.0 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/readme.md +0 -2
- package/dist/maplibre-gl-dev.mjs +433 -573
- package/dist/maplibre-gl-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared-dev.mjs +984 -850
- package/dist/maplibre-gl-shared-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared.mjs +2 -2
- package/dist/maplibre-gl-shared.mjs.map +1 -1
- package/dist/maplibre-gl-worker-dev.mjs +14 -12
- package/dist/maplibre-gl-worker-dev.mjs.map +1 -1
- package/dist/maplibre-gl-worker.mjs +2 -2
- package/dist/maplibre-gl-worker.mjs.map +1 -1
- package/dist/maplibre-gl.css +1 -1
- package/dist/maplibre-gl.d.ts +119 -42
- package/dist/maplibre-gl.mjs +22 -22
- package/dist/maplibre-gl.mjs.map +1 -1
- package/package.json +20 -22
- package/src/css/maplibre-gl.css +4 -0
- package/src/data/bucket/circle_bucket.ts +3 -2
- package/src/data/bucket/fill_bucket.ts +3 -2
- package/src/data/bucket/fill_extrusion_bucket.test.ts +22 -0
- package/src/data/bucket/fill_extrusion_bucket.ts +17 -4
- package/src/data/bucket/line_bucket.test.ts +5 -5
- package/src/data/bucket/line_bucket.ts +3 -2
- package/src/data/bucket/round_polygon_corners.test.ts +159 -0
- package/src/data/bucket/round_polygon_corners.ts +121 -0
- package/src/data/bucket/symbol_bucket.test.ts +7 -7
- package/src/data/dem_data.test.ts +23 -23
- package/src/data/feature_position_map.test.ts +1 -1
- package/src/data/segment.test.ts +14 -14
- package/src/geo/bounds.test.ts +4 -4
- package/src/geo/lng_lat_bounds.test.ts +1 -1
- package/src/geo/projection/covering_tiles.bench.ts +39 -0
- package/src/geo/projection/covering_tiles.test.ts +3 -7
- package/src/geo/projection/covering_tiles.ts +37 -2
- package/src/geo/projection/globe_covering_tiles_details_provider.ts +4 -4
- package/src/geo/projection/globe_projection.test.ts +2 -2
- package/src/geo/projection/globe_projection.ts +6 -15
- package/src/geo/projection/globe_transform.test.ts +3 -6
- package/src/geo/projection/globe_transform.ts +4 -7
- package/src/geo/projection/mercator_covering_tiles_details_provider.ts +4 -4
- package/src/geo/projection/mercator_projection.ts +1 -13
- package/src/geo/projection/mercator_transform.test.ts +1 -1
- package/src/geo/projection/mercator_transform.ts +2 -1
- package/src/geo/projection/mercator_utils.test.ts +8 -8
- package/src/geo/projection/projection.ts +0 -27
- package/src/geo/projection/projection_data.ts +8 -0
- package/src/geo/projection/projection_factory.ts +3 -3
- package/src/geo/projection/vertical_perspective_projection.ts +3 -60
- package/src/geo/projection/vertical_perspective_transform.ts +16 -23
- package/src/geo/transform_interface.ts +1 -2
- package/src/index.test.ts +7 -12
- package/src/index.ts +4 -2
- package/src/render/glyph_manager.test.ts +7 -22
- package/src/render/painter.test.ts +2 -0
- package/src/render/painter.ts +8 -11
- package/src/render/subdivision.bench.ts +39 -0
- package/src/render/subdivision.test.ts +22 -23
- package/src/render/subdivision.ts +46 -0
- package/src/render/terrain.test.ts +68 -34
- package/src/render/terrain.ts +69 -26
- package/src/shaders/glsl/_projection_globe.fragment.glsl +12 -0
- package/src/shaders/glsl/_projection_globe.fragment.glsl.g.ts +2 -0
- package/src/shaders/glsl/_projection_globe.vertex.glsl +29 -10
- package/src/shaders/glsl/_projection_globe.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/_projection_mercator.fragment.glsl +4 -0
- package/src/shaders/glsl/_projection_mercator.fragment.glsl.g.ts +2 -0
- package/src/shaders/glsl/line.fragment.glsl +2 -0
- package/src/shaders/glsl/line.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_gradient.fragment.glsl +2 -0
- package/src/shaders/glsl/line_gradient.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_gradient_sdf.fragment.glsl +2 -0
- package/src/shaders/glsl/line_gradient_sdf.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_pattern.fragment.glsl +2 -0
- package/src/shaders/glsl/line_pattern.fragment.glsl.g.ts +1 -1
- package/src/shaders/glsl/line_sdf.fragment.glsl +2 -0
- package/src/shaders/glsl/line_sdf.fragment.glsl.g.ts +1 -1
- package/src/shaders/shaders.ts +4 -6
- package/src/source/canvas_source.test.ts +1 -1
- package/src/source/geojson_source.test.ts +32 -4
- package/src/source/geojson_source.ts +41 -0
- package/src/source/geojson_source_diff.test.ts +2 -2
- package/src/source/geojson_worker_source.test.ts +9 -5
- package/src/source/image_source.test.ts +137 -62
- package/src/source/image_source.ts +49 -12
- package/src/source/raster_dem_tile_source.test.ts +2 -2
- package/src/source/raster_tile_source.test.ts +64 -9
- package/src/source/raster_tile_source.ts +2 -1
- package/src/source/rtl_text_plugin_main_thread.test.ts +14 -14
- package/src/source/source.test.ts +8 -8
- package/src/source/vector_tile_source.test.ts +1 -1
- package/src/source/vector_tile_worker_source.test.ts +7 -5
- package/src/source/video_source.test.ts +1 -1
- package/src/source/worker.test.ts +1 -1
- package/src/source/worker_tile.test.ts +10 -8
- package/src/style/light.test.ts +9 -9
- package/src/style/light.ts +3 -3
- package/src/style/load_glyph_range.test.ts +8 -8
- package/src/style/load_sprite.test.ts +1 -1
- package/src/style/properties.test.ts +94 -2
- package/src/style/properties.ts +30 -1
- package/src/style/sky.test.ts +9 -9
- package/src/style/sky.ts +2 -2
- package/src/style/style.test.ts +38 -36
- package/src/style/style.ts +3 -3
- package/src/style/style_layer/color_relief_style_layer.test.ts +1 -1
- package/src/style/style_layer/fill_extrusion_style_layer.ts +6 -3
- package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +14 -1
- package/src/style/style_layer/raster_style_layer.test.ts +2 -2
- package/src/style/style_layer.test.ts +5 -51
- package/src/style/validate_style.test.ts +1 -1
- package/src/symbol/collision_index.test.ts +1 -1
- package/src/symbol/grid_index.test.ts +1 -1
- package/src/tile/terrain_tile_manager.test.ts +2 -2
- package/src/tile/tile_id.test.ts +5 -5
- package/src/tile/tile_manager.test.ts +113 -72
- package/src/tile/tile_manager.ts +2 -2
- package/src/tile/tile_manager_in_view_tiles.test.ts +2 -2
- package/src/ui/camera.test.ts +138 -135
- package/src/ui/control/geolocate_control.test.ts +7 -30
- package/src/ui/handler/dblclick_zoom.test.ts +2 -2
- package/src/ui/handler/mouse.ts +15 -7
- package/src/ui/handler/mouse_handler_interface.test.ts +21 -1
- package/src/ui/handler/scroll_zoom.test.ts +2 -0
- package/src/ui/handler_manager.test.ts +7 -7
- package/src/ui/hash.test.ts +1 -0
- package/src/ui/map.ts +45 -20
- package/src/ui/map_tests/map_basic.test.ts +11 -23
- package/src/ui/map_tests/map_calculate_camera_options.test.ts +6 -6
- package/src/ui/map_tests/map_events.test.ts +5 -7
- package/src/ui/map_tests/map_global_state.test.ts +3 -3
- package/src/ui/map_tests/map_images.test.ts +4 -3
- package/src/ui/map_tests/map_is_moving.test.ts +1 -1
- package/src/ui/map_tests/map_is_rotating.test.ts +1 -1
- package/src/ui/map_tests/map_layer.test.ts +2 -1
- package/src/ui/map_tests/map_options.test.ts +1 -5
- package/src/ui/map_tests/map_pitch.test.ts +6 -6
- package/src/ui/map_tests/map_refresh_tiles.test.ts +2 -2
- package/src/ui/map_tests/map_render.test.ts +1 -1
- package/src/ui/map_tests/map_set_lod_params.test.ts +1 -1
- package/src/ui/map_tests/map_style.test.ts +9 -1
- package/src/ui/map_tests/map_terrain.test.ts +41 -1
- package/src/ui/map_tests/map_zoom.test.ts +15 -15
- package/src/ui/marker.test.ts +71 -14
- package/src/ui/marker.ts +30 -6
- package/src/ui/popup.test.ts +3 -3
- package/src/util/actor.test.ts +6 -6
- package/src/util/ajax.test.ts +27 -39
- package/src/util/browser.test.ts +7 -9
- package/src/util/create_tile_mesh.test.ts +2 -2
- package/src/util/dispatcher.test.ts +1 -1
- package/src/util/evented.test.ts +4 -1
- package/src/util/fast_maths.test.ts +4 -4
- package/src/util/image_request.test.ts +6 -4
- package/src/util/primitives/bounding_volume_cache.ts +3 -1
- package/src/util/struct_array.test.ts +2 -2
- package/src/util/task_queue.test.ts +1 -1
- package/src/util/test/util.ts +1 -0
- package/src/util/time_control.test.ts +2 -2
- package/src/util/transferable_grid_index.test.ts +1 -1
- package/src/util/util.test.ts +27 -13
- package/src/util/verticalize_punctuation.test.ts +4 -4
- package/src/util/web_worker_transfer.test.ts +5 -5
- package/src/webgl/draw/draw_custom.test.ts +13 -13
- package/src/webgl/draw/draw_debug.test.ts +1 -1
- package/src/webgl/draw/draw_fill.test.ts +13 -12
- package/src/webgl/draw/draw_symbol.test.ts +17 -16
- package/src/webgl/program/program_uniforms.ts +0 -3
- package/src/webgl/program/projection_program.ts +12 -10
- package/src/webgl/program/terrain_program.test.ts +2 -2
- package/src/webgl/program.ts +6 -4
- package/src/webgl/render_to_texture.test.ts +5 -5
- package/src/webgl/state.test.ts +1 -1
- package/src/webgl/texture.test.ts +3 -3
- package/src/webgl/vertex_buffer.test.ts +1 -1
- package/build/rolldown_plugins.ts +0 -18
- package/src/geo/projection/globe_projection_error_measurement.ts +0 -229
- package/src/shaders/glsl/projection_error_measurement.fragment.glsl +0 -5
- package/src/shaders/glsl/projection_error_measurement.fragment.glsl.g.ts +0 -2
- package/src/shaders/glsl/projection_error_measurement.vertex.glsl +0 -22
- package/src/shaders/glsl/projection_error_measurement.vertex.glsl.g.ts +0 -2
- package/src/webgl/program/projection_error_measurement_program.ts +0 -23
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {bench} from 'vitest';
|
|
2
|
+
import Point from '@mapbox/point-geometry';
|
|
3
|
+
import {CanonicalTileID} from '../tile/tile_id.ts';
|
|
4
|
+
import {EXTENT} from '../data/extent.ts';
|
|
5
|
+
import {subdividePolygon} from './subdivision.ts';
|
|
6
|
+
|
|
7
|
+
function generateRing(cx: number, cy: number, radius: number, vertexCount: number): Point[] {
|
|
8
|
+
const ring = [];
|
|
9
|
+
|
|
10
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
11
|
+
const angle = i / vertexCount * 2.0 * Math.PI;
|
|
12
|
+
ring.push(new Point(
|
|
13
|
+
Math.round(cx + Math.cos(angle) * radius),
|
|
14
|
+
Math.round(cy + Math.sin(angle) * radius)
|
|
15
|
+
));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return ring;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const vertexCountMultiplier = 11;
|
|
22
|
+
const granularity = 64;
|
|
23
|
+
const tileID = new CanonicalTileID(2, 1, 1);
|
|
24
|
+
|
|
25
|
+
const polygon: Point[][] = [generateRing(EXTENT / 2, EXTENT / 2, EXTENT * 1.1 / 2, 81 * vertexCountMultiplier)];
|
|
26
|
+
|
|
27
|
+
function generateHole(cx: number, cy: number, r: number, vertexCount: number) {
|
|
28
|
+
polygon.push(generateRing(cx * EXTENT, cy * EXTENT, r * EXTENT, vertexCount));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
generateHole(0.25, 0.5, 0.15, 16 * vertexCountMultiplier);
|
|
32
|
+
generateHole(0.75, 0.5, 0.15, 2 * vertexCountMultiplier);
|
|
33
|
+
generateHole(0.5, 0.1, 0.05, 4 * vertexCountMultiplier);
|
|
34
|
+
|
|
35
|
+
bench('subdividePolygon', () => {
|
|
36
|
+
for (let i = 0; i < 10; i++) {
|
|
37
|
+
subdividePolygon(polygon, tileID, granularity, true);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
@@ -224,7 +224,7 @@ describe('Fill subdivision', () => {
|
|
|
224
224
|
3, 0
|
|
225
225
|
]
|
|
226
226
|
]);
|
|
227
|
-
|
|
227
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
228
228
|
});
|
|
229
229
|
|
|
230
230
|
test('Polygon is unchanged when granularity=1, but winding order is corrected.', () => {
|
|
@@ -259,7 +259,7 @@ describe('Fill subdivision', () => {
|
|
|
259
259
|
3, 0
|
|
260
260
|
]
|
|
261
261
|
]);
|
|
262
|
-
|
|
262
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
263
263
|
});
|
|
264
264
|
|
|
265
265
|
test('Polygon inside cell is unchanged', () => {
|
|
@@ -294,7 +294,7 @@ describe('Fill subdivision', () => {
|
|
|
294
294
|
3, 0
|
|
295
295
|
]
|
|
296
296
|
]);
|
|
297
|
-
|
|
297
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
298
298
|
});
|
|
299
299
|
|
|
300
300
|
test('Subdivide a polygon', () => {
|
|
@@ -349,7 +349,6 @@ describe('Fill subdivision', () => {
|
|
|
349
349
|
// 7:
|
|
350
350
|
//
|
|
351
351
|
// 8: 2
|
|
352
|
-
console.log(result.indicesTriangles);
|
|
353
352
|
expect(result.indicesTriangles).toEqual([
|
|
354
353
|
3, 0, 6,
|
|
355
354
|
7, 0, 3,
|
|
@@ -405,7 +404,7 @@ describe('Fill subdivision', () => {
|
|
|
405
404
|
6, 3
|
|
406
405
|
]
|
|
407
406
|
]);
|
|
408
|
-
|
|
407
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
409
408
|
});
|
|
410
409
|
|
|
411
410
|
describe('Polygon outline line list is correct', () => {
|
|
@@ -420,7 +419,7 @@ describe('Fill subdivision', () => {
|
|
|
420
419
|
expect(hasDuplicateVertices(result.verticesFlattened)).toBe(false);
|
|
421
420
|
testMeshIntegrity(result.indicesTriangles);
|
|
422
421
|
testPolygonOutlineMatches(result.indicesTriangles, result.indicesLineList);
|
|
423
|
-
|
|
422
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
424
423
|
});
|
|
425
424
|
|
|
426
425
|
test('Small polygon', () => {
|
|
@@ -434,7 +433,7 @@ describe('Fill subdivision', () => {
|
|
|
434
433
|
expect(hasDuplicateVertices(result.verticesFlattened)).toBe(false);
|
|
435
434
|
testMeshIntegrity(result.indicesTriangles);
|
|
436
435
|
testPolygonOutlineMatches(result.indicesTriangles, result.indicesLineList);
|
|
437
|
-
|
|
436
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
438
437
|
});
|
|
439
438
|
|
|
440
439
|
test('Medium polygon', () => {
|
|
@@ -448,7 +447,7 @@ describe('Fill subdivision', () => {
|
|
|
448
447
|
expect(hasDuplicateVertices(result.verticesFlattened)).toBe(false);
|
|
449
448
|
testMeshIntegrity(result.indicesTriangles);
|
|
450
449
|
testPolygonOutlineMatches(result.indicesTriangles, result.indicesLineList);
|
|
451
|
-
|
|
450
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
452
451
|
});
|
|
453
452
|
|
|
454
453
|
test('Large polygon', () => {
|
|
@@ -462,7 +461,7 @@ describe('Fill subdivision', () => {
|
|
|
462
461
|
expect(hasDuplicateVertices(result.verticesFlattened)).toBe(false);
|
|
463
462
|
testMeshIntegrity(result.indicesTriangles);
|
|
464
463
|
testPolygonOutlineMatches(result.indicesTriangles, result.indicesLineList);
|
|
465
|
-
|
|
464
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
466
465
|
});
|
|
467
466
|
|
|
468
467
|
test('Large polygon with hole', () => {
|
|
@@ -481,7 +480,7 @@ describe('Fill subdivision', () => {
|
|
|
481
480
|
expect(hasDuplicateVertices(result.verticesFlattened)).toBe(false);
|
|
482
481
|
testMeshIntegrity(result.indicesTriangles);
|
|
483
482
|
testPolygonOutlineMatches(result.indicesTriangles, result.indicesLineList);
|
|
484
|
-
|
|
483
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
485
484
|
});
|
|
486
485
|
|
|
487
486
|
test('Large polygon with hole, granularity=0', () => {
|
|
@@ -500,7 +499,7 @@ describe('Fill subdivision', () => {
|
|
|
500
499
|
expect(hasDuplicateVertices(result.verticesFlattened)).toBe(false);
|
|
501
500
|
testMeshIntegrity(result.indicesTriangles);
|
|
502
501
|
testPolygonOutlineMatches(result.indicesTriangles, result.indicesLineList);
|
|
503
|
-
|
|
502
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
504
503
|
});
|
|
505
504
|
|
|
506
505
|
test('Large polygon with hole, finer granularity', () => {
|
|
@@ -518,7 +517,7 @@ describe('Fill subdivision', () => {
|
|
|
518
517
|
], canonicalDefault, EXTENT / 8);
|
|
519
518
|
|
|
520
519
|
expect(hasDuplicateVertices(result.verticesFlattened)).toBe(false);
|
|
521
|
-
|
|
520
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
522
521
|
|
|
523
522
|
// This polygon subdivision results in at least one edge that is shared among more than 2 triangles.
|
|
524
523
|
// This is not ideal, but it is also an edge case of a weird triangle getting subdivided by a very fine grid.
|
|
@@ -587,7 +586,7 @@ describe('Fill subdivision', () => {
|
|
|
587
586
|
5, 3
|
|
588
587
|
]
|
|
589
588
|
]);
|
|
590
|
-
|
|
589
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
591
590
|
});
|
|
592
591
|
|
|
593
592
|
test('Polygon with duplicate vertex with hole inside cell', () => {
|
|
@@ -641,7 +640,7 @@ describe('Fill subdivision', () => {
|
|
|
641
640
|
4, 0
|
|
642
641
|
]
|
|
643
642
|
]);
|
|
644
|
-
|
|
643
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
645
644
|
});
|
|
646
645
|
|
|
647
646
|
test('Polygon with duplicate edge inside cell', () => {
|
|
@@ -701,7 +700,7 @@ describe('Fill subdivision', () => {
|
|
|
701
700
|
3, 0
|
|
702
701
|
]
|
|
703
702
|
]);
|
|
704
|
-
|
|
703
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
705
704
|
});
|
|
706
705
|
});
|
|
707
706
|
|
|
@@ -778,7 +777,7 @@ describe('Fill subdivision', () => {
|
|
|
778
777
|
4, 0
|
|
779
778
|
]
|
|
780
779
|
]);
|
|
781
|
-
|
|
780
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
782
781
|
});
|
|
783
782
|
|
|
784
783
|
test('Generates pole geometry for north pole only (geometry not bordering other pole)', () => {
|
|
@@ -814,7 +813,7 @@ describe('Fill subdivision', () => {
|
|
|
814
813
|
2, 3, 3, 0
|
|
815
814
|
]
|
|
816
815
|
]);
|
|
817
|
-
|
|
816
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
818
817
|
});
|
|
819
818
|
|
|
820
819
|
test('Generates pole geometry for south pole only (geometry not bordering other pole)', () => {
|
|
@@ -850,7 +849,7 @@ describe('Fill subdivision', () => {
|
|
|
850
849
|
2, 3, 3, 0
|
|
851
850
|
]
|
|
852
851
|
]);
|
|
853
|
-
|
|
852
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
854
853
|
});
|
|
855
854
|
|
|
856
855
|
test('Generates pole geometry for north pole only (tile not bordering other pole)', () => {
|
|
@@ -886,7 +885,7 @@ describe('Fill subdivision', () => {
|
|
|
886
885
|
2, 3, 3, 0
|
|
887
886
|
]
|
|
888
887
|
]);
|
|
889
|
-
|
|
888
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
890
889
|
});
|
|
891
890
|
|
|
892
891
|
test('Generates pole geometry for south pole only (tile not bordering other pole)', () => {
|
|
@@ -922,7 +921,7 @@ describe('Fill subdivision', () => {
|
|
|
922
921
|
2, 3, 3, 0
|
|
923
922
|
]
|
|
924
923
|
]);
|
|
925
|
-
|
|
924
|
+
expectWindingOrder(result.verticesFlattened, result.indicesTriangles);
|
|
926
925
|
});
|
|
927
926
|
|
|
928
927
|
test('Scanline subdivision ring generation case 1', () => {
|
|
@@ -952,7 +951,7 @@ describe('Fill subdivision', () => {
|
|
|
952
951
|
const ring = [0, 1, 2, 3, 4, 5, 6, 7];
|
|
953
952
|
const finalIndices = [];
|
|
954
953
|
scanlineTriangulateVertexRing(vertices, ring, finalIndices);
|
|
955
|
-
|
|
954
|
+
expectWindingOrder(vertices, finalIndices);
|
|
956
955
|
});
|
|
957
956
|
|
|
958
957
|
test('Scanline subdivision ring generation case 2', () => {
|
|
@@ -961,7 +960,7 @@ describe('Fill subdivision', () => {
|
|
|
961
960
|
const ring = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
962
961
|
const finalIndices = [];
|
|
963
962
|
scanlineTriangulateVertexRing(vertices, ring, finalIndices);
|
|
964
|
-
|
|
963
|
+
expectWindingOrder(vertices, finalIndices);
|
|
965
964
|
});
|
|
966
965
|
});
|
|
967
966
|
|
|
@@ -1076,7 +1075,7 @@ function hasDuplicateVertices(flattened: number[]): boolean {
|
|
|
1076
1075
|
/**
|
|
1077
1076
|
* Passes if all triangles have the correct winding order, otherwise throws.
|
|
1078
1077
|
*/
|
|
1079
|
-
function
|
|
1078
|
+
function expectWindingOrder(flattened: number[], indices: number[]): void {
|
|
1080
1079
|
for (let i = 0; i < indices.length; i += 3) {
|
|
1081
1080
|
const i0 = indices[i];
|
|
1082
1081
|
const i1 = indices[i + 1];
|
|
@@ -612,6 +612,11 @@ class Subdivider {
|
|
|
612
612
|
// Add pole geometry if needed
|
|
613
613
|
this._handlePoles(subdividedTriangles);
|
|
614
614
|
|
|
615
|
+
if (this._granularity >= 2 && this._canonical?.z === 0) {
|
|
616
|
+
subdividedTriangles = this._removeTrianglesOutsideTileX(subdividedTriangles);
|
|
617
|
+
subdividedLines = subdividedLines.map(lines => this._removeLinesOutsideTileX(lines));
|
|
618
|
+
}
|
|
619
|
+
|
|
615
620
|
return {
|
|
616
621
|
verticesFlattened: this._vertexBuffer,
|
|
617
622
|
indicesTriangles: subdividedTriangles,
|
|
@@ -619,6 +624,47 @@ class Subdivider {
|
|
|
619
624
|
};
|
|
620
625
|
}
|
|
621
626
|
|
|
627
|
+
private _vertexOutsideTileX(index: number): boolean {
|
|
628
|
+
const x = this._vertexBuffer[index * 2];
|
|
629
|
+
return x < 0 || x > EXTENT;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Drops all triangles that reach beyond the tile's X extent.
|
|
634
|
+
*
|
|
635
|
+
* On globe the z0 tile's buffer wraps around the planet onto the tile itself, drawing buffered geometry twice.
|
|
636
|
+
* Only globe uses subdivision (`granularity >= 2`), so mercator is never affected.
|
|
637
|
+
* @param indices - Triangle indices into `this._vertexBuffer`.
|
|
638
|
+
* @returns The indices with every triangle that has a vertex outside the tile's X extent removed.
|
|
639
|
+
*/
|
|
640
|
+
private _removeTrianglesOutsideTileX(indices: number[]): number[] {
|
|
641
|
+
const filtered: number[] = [];
|
|
642
|
+
for (let i = 0; i < indices.length; i += 3) {
|
|
643
|
+
if (this._vertexOutsideTileX(indices[i]) || this._vertexOutsideTileX(indices[i + 1]) || this._vertexOutsideTileX(indices[i + 2])) {
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
filtered.push(indices[i], indices[i + 1], indices[i + 2]);
|
|
647
|
+
}
|
|
648
|
+
return filtered;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Drops all outline line segments that reach beyond the tile's X extent,
|
|
653
|
+
* for the same reason as {@link Subdivider._removeTrianglesOutsideTileX}.
|
|
654
|
+
* @param indices - Line segment indices into `this._vertexBuffer`.
|
|
655
|
+
* @returns The indices with every segment that has a vertex outside the tile's X extent removed.
|
|
656
|
+
*/
|
|
657
|
+
private _removeLinesOutsideTileX(indices: number[]): number[] {
|
|
658
|
+
const filtered: number[] = [];
|
|
659
|
+
for (let i = 0; i < indices.length; i += 2) {
|
|
660
|
+
if (this._vertexOutsideTileX(indices[i]) || this._vertexOutsideTileX(indices[i + 1])) {
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
filtered.push(indices[i], indices[i + 1]);
|
|
664
|
+
}
|
|
665
|
+
return filtered;
|
|
666
|
+
}
|
|
667
|
+
|
|
622
668
|
/**
|
|
623
669
|
* Sometimes the supplies vertex and index array has duplicate vertices - same coordinates that are referenced by multiple different indices.
|
|
624
670
|
* That is not allowed for purposes of subdivision, duplicates are removed in `this.initializeVertices`.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {describe, beforeEach, afterEach, test, expect, vi} from 'vitest';
|
|
2
2
|
import Point from '@mapbox/point-geometry';
|
|
3
|
-
import {mat4} from 'gl-matrix';
|
|
4
3
|
import {Terrain} from './terrain.ts';
|
|
5
4
|
import {Context} from '../webgl/context.ts';
|
|
6
5
|
import {RGBAImage} from '../util/image.ts';
|
|
@@ -248,31 +247,66 @@ describe('Terrain', () => {
|
|
|
248
247
|
expect(actualVertexArray).toStrictEqual([0, 0, 0, 2048, 0, 0, 4096, 0, 0, 6144, 0, 0, 8192, 0, 0, 0, 2048, 0, 2048, 2048, 0, 4096, 2048, 0, 6144, 2048, 0, 8192, 2048, 0, 0, 4096, 0, 2048, 4096, 0, 4096, 4096, 0, 6144, 4096, 0, 8192, 4096, 0, 0, 6144, 0, 2048, 6144, 0, 4096, 6144, 0, 6144, 6144, 0, 8192, 6144, 0, 0, 8192, 0, 2048, 8192, 0, 4096, 8192, 0, 6144, 8192, 0, 8192, 8192, 0, 0, 0, 1, 2048, 0, 1, 4096, 0, 1, 6144, 0, 1, 8192, 0, 1, 0, 8192, 1, 2048, 8192, 1, 4096, 8192, 1, 6144, 8192, 1, 8192, 8192, 1, 0, 0, 0, 0, 0, 1, 0, 2048, 0, 0, 2048, 1, 0, 4096, 0, 0, 4096, 1, 0, 6144, 0, 0, 6144, 1, 0, 8192, 0, 0, 8192, 1, 8192, 0, 0, 8192, 0, 1, 8192, 2048, 0, 8192, 2048, 1, 8192, 4096, 0, 8192, 4096, 1, 8192, 6144, 0, 8192, 6144, 1, 8192, 8192, 0, 8192, 8192, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
|
249
248
|
});
|
|
250
249
|
|
|
251
|
-
test('
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
dim: 1,
|
|
260
|
-
sampleBilinear(x: number, y: number) {
|
|
261
|
-
return 100 * x + 10 * y;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
};
|
|
250
|
+
test('getElevation interpolates and reuses DEM sampling setup until reset', () => {
|
|
251
|
+
const terrain = new Terrain(null, {_source: {tileSize: 512}} as any, {exaggeration: 2} as any);
|
|
252
|
+
const tileID = new OverscaledTileID(1, 0, 1, 0, 0);
|
|
253
|
+
const sourceTile = {
|
|
254
|
+
tileID,
|
|
255
|
+
dem: {
|
|
256
|
+
dim: 1,
|
|
257
|
+
sampleBilinear: (x: number, y: number) => 100 * x + 10 * y
|
|
266
258
|
}
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
expect(
|
|
272
|
-
expect(
|
|
273
|
-
expect(
|
|
274
|
-
expect(
|
|
275
|
-
|
|
259
|
+
} as any as Tile;
|
|
260
|
+
terrain.tileManager.getSourceTile = vi.fn(() => sourceTile);
|
|
261
|
+
terrain.tileManager.getSource = vi.fn(() => ({minzoom: 0, maxzoom: 22}) as any);
|
|
262
|
+
|
|
263
|
+
expect(terrain.getDEMElevation(tileID, EXTENT * 0.4, EXTENT * 0.2)).toBeCloseTo(42);
|
|
264
|
+
expect(terrain.getElevation(tileID, EXTENT / 2, EXTENT / 2)).toBeCloseTo(110);
|
|
265
|
+
expect(terrain.getElevation(tileID, EXTENT / 2, EXTENT / 2)).toBeCloseTo(110);
|
|
266
|
+
expect(terrain.tileManager.getSourceTile).toHaveBeenCalledTimes(1);
|
|
267
|
+
|
|
268
|
+
terrain.resetElevationCache();
|
|
269
|
+
expect(terrain.getElevation(tileID, EXTENT / 2, EXTENT / 2)).toBeCloseTo(110);
|
|
270
|
+
|
|
271
|
+
expect(terrain.tileManager.getSourceTile).toHaveBeenCalledTimes(2);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test('getDEMElevation samples the correct part of a parent DEM tile', () => {
|
|
275
|
+
const terrain = new Terrain(null, {_source: {tileSize: 512}} as any, {} as any);
|
|
276
|
+
const childTileID = new OverscaledTileID(2, 0, 2, 3, 3);
|
|
277
|
+
const parentTileID = new OverscaledTileID(1, 0, 1, 1, 1);
|
|
278
|
+
const sampleBilinear = vi.fn(() => 42);
|
|
279
|
+
|
|
280
|
+
terrain.tileManager.getSourceTile = vi.fn(() => ({
|
|
281
|
+
tileID: parentTileID,
|
|
282
|
+
dem: {dim: 4, sampleBilinear}
|
|
283
|
+
}) as any as Tile);
|
|
284
|
+
terrain.tileManager.getSource = vi.fn(() => ({maxzoom: 22}) as any);
|
|
285
|
+
|
|
286
|
+
expect(terrain.getDEMElevation(childTileID, EXTENT / 2, EXTENT / 2)).toBe(42);
|
|
287
|
+
// The center of the bottom-right child is at 75% of both parent axes.
|
|
288
|
+
expect(sampleBilinear).toHaveBeenCalledWith(3, 3);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('getElevation retries sampling setup when DEM data becomes available', () => {
|
|
292
|
+
const terrain = new Terrain(null, {_source: {tileSize: 512}} as any, {exaggeration: 1} as any);
|
|
293
|
+
const tileID = new OverscaledTileID(1, 0, 1, 0, 0);
|
|
294
|
+
let tileHasDem = false;
|
|
295
|
+
|
|
296
|
+
terrain.tileManager.getSourceTile = vi.fn(() => ({
|
|
297
|
+
tileID,
|
|
298
|
+
dem: tileHasDem ? {
|
|
299
|
+
dim: 1,
|
|
300
|
+
sampleBilinear: (x: number, y: number) => 100 * x + 10 * y
|
|
301
|
+
} : undefined
|
|
302
|
+
}) as any as Tile);
|
|
303
|
+
terrain.tileManager.getSource = vi.fn(() => ({minzoom: 0, maxzoom: 22}) as any);
|
|
304
|
+
|
|
305
|
+
expect(terrain.getElevation(tileID, EXTENT / 2, EXTENT / 2)).toBe(0);
|
|
306
|
+
|
|
307
|
+
tileHasDem = true;
|
|
308
|
+
expect(terrain.getElevation(tileID, EXTENT / 2, EXTENT / 2)).toBeCloseTo(55);
|
|
309
|
+
expect(terrain.tileManager.getSourceTile).toHaveBeenCalledTimes(2);
|
|
276
310
|
});
|
|
277
311
|
|
|
278
312
|
test('getElevationForLngLat uses covering tiles to get the right zoom', () => {
|
|
@@ -323,15 +357,15 @@ describe('Terrain', () => {
|
|
|
323
357
|
|
|
324
358
|
test('getDEMElevation normalizes out-of-bounds coordinates to neighbor tile', () => {
|
|
325
359
|
const terrain = new Terrain(null, {_source: {tileSize: 512}} as any, {} as any);
|
|
326
|
-
const
|
|
327
|
-
terrain.
|
|
360
|
+
const getSourceTile = vi.fn();
|
|
361
|
+
terrain.tileManager.getSourceTile = getSourceTile;
|
|
328
362
|
|
|
329
363
|
// tile (0,0,1) with x beyond EXTENT should normalize to tile (1,0,1)
|
|
330
364
|
const tileID = new OverscaledTileID(1, 0, 1, 0, 0);
|
|
331
365
|
terrain.getDEMElevation(tileID, EXTENT + 100, 50);
|
|
332
366
|
|
|
333
|
-
expect(
|
|
334
|
-
const [calledTileID] =
|
|
367
|
+
expect(getSourceTile).toHaveBeenCalledTimes(1);
|
|
368
|
+
const [calledTileID] = getSourceTile.mock.calls[0];
|
|
335
369
|
expect(calledTileID.canonical.x).toBe(1);
|
|
336
370
|
expect(calledTileID.canonical.y).toBe(0);
|
|
337
371
|
expect(calledTileID.canonical.z).toBe(1);
|
|
@@ -339,15 +373,15 @@ describe('Terrain', () => {
|
|
|
339
373
|
|
|
340
374
|
test('getDEMElevation returns 0 for coordinates beyond tile grid', () => {
|
|
341
375
|
const terrain = new Terrain(null, {_source: {tileSize: 512}} as any, {} as any);
|
|
342
|
-
const
|
|
343
|
-
terrain.
|
|
376
|
+
const getSourceTile = vi.fn();
|
|
377
|
+
terrain.tileManager.getSourceTile = getSourceTile;
|
|
344
378
|
|
|
345
379
|
// tile (0,0,0) with y beyond EXTENT — no tile exists below at z=0
|
|
346
380
|
const tileID = new OverscaledTileID(0, 0, 0, 0, 0);
|
|
347
381
|
const result = terrain.getDEMElevation(tileID, 100, EXTENT + 100);
|
|
348
382
|
|
|
349
383
|
expect(result).toBe(0);
|
|
350
|
-
expect(
|
|
384
|
+
expect(getSourceTile).not.toHaveBeenCalled();
|
|
351
385
|
});
|
|
352
386
|
|
|
353
387
|
describe('getElevationForLngLatZoom returns 0 for out of bounds', () => {
|
|
@@ -361,11 +395,11 @@ describe('Terrain', () => {
|
|
|
361
395
|
expect(terrain.getElevationForLngLatZoom(new LngLat(0, 88), 0)).toBe(0);
|
|
362
396
|
});
|
|
363
397
|
|
|
364
|
-
test('zoom', () => {
|
|
398
|
+
test('zoom below the minimum', () => {
|
|
365
399
|
expect(terrain.getElevationForLngLatZoom(new LngLat(0, 0), MIN_TILE_ZOOM - 1)).toBe(0);
|
|
366
400
|
});
|
|
367
401
|
|
|
368
|
-
test('zoom', () => {
|
|
402
|
+
test('zoom above the maximum', () => {
|
|
369
403
|
expect(terrain.getElevationForLngLatZoom(new LngLat(0, 0), MAX_TILE_ZOOM + 1)).toBe(0);
|
|
370
404
|
});
|
|
371
405
|
});
|
package/src/render/terrain.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {mat4
|
|
1
|
+
import {mat4} from 'gl-matrix';
|
|
2
2
|
import {OverscaledTileID} from '../tile/tile_id.ts';
|
|
3
3
|
import {RGBAImage} from '../util/image.ts';
|
|
4
4
|
import {warnOnce} from '../util/util.ts';
|
|
@@ -38,6 +38,8 @@ export type TerrainData = {
|
|
|
38
38
|
tile: Tile;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
type TerrainElevationSampler = (x: number, y: number, extent: number) => number;
|
|
42
|
+
|
|
41
43
|
/**
|
|
42
44
|
* @internal
|
|
43
45
|
* This is the main class which handles most of the 3D Terrain logic. It has the following topics:
|
|
@@ -135,7 +137,13 @@ export class Terrain {
|
|
|
135
137
|
* as of overzooming of raster-dem tiles in high zoomlevels, this cache contains
|
|
136
138
|
* matrices to transform from vector-tile coords to raster-dem-tile coords.
|
|
137
139
|
*/
|
|
138
|
-
_demMatrixCache:
|
|
140
|
+
_demMatrixCache: Map<string, mat4>;
|
|
141
|
+
/**
|
|
142
|
+
* Per-render cache of resolved CPU elevation samplers. It is cleared after
|
|
143
|
+
* terrain tile selection updates and whenever the terrain source changes.
|
|
144
|
+
* Missing DEM data is deliberately not cached so a later sample can retry.
|
|
145
|
+
*/
|
|
146
|
+
_elevationSamplerCache: Map<string, TerrainElevationSampler>;
|
|
139
147
|
/**
|
|
140
148
|
* Controls how terrain skirt length is calculated.
|
|
141
149
|
* @see {@link MapOptions.terrainSkirtLength}
|
|
@@ -149,7 +157,8 @@ export class Terrain {
|
|
|
149
157
|
this._terrainSkirtLength = terrainSkirtLength;
|
|
150
158
|
this.qualityFactor = 2;
|
|
151
159
|
this.meshSize = 128;
|
|
152
|
-
this._demMatrixCache =
|
|
160
|
+
this._demMatrixCache = new Map();
|
|
161
|
+
this._elevationSamplerCache = new Map();
|
|
153
162
|
this.coordsIndex = [];
|
|
154
163
|
this._coordsTextureSize = 1024;
|
|
155
164
|
}
|
|
@@ -200,13 +209,8 @@ export class Terrain {
|
|
|
200
209
|
const normalized = tileID.normalizeCoordinates(x, y, extent);
|
|
201
210
|
if (!normalized) return 0;
|
|
202
211
|
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
if (!dem) return 0;
|
|
206
|
-
|
|
207
|
-
const pos = vec2.transformMat4([], [normalized.x / extent * EXTENT, normalized.y / extent * EXTENT], terrain.u_terrain_matrix);
|
|
208
|
-
const coord = [pos[0] * dem.dim, pos[1] * dem.dim];
|
|
209
|
-
return dem.sampleBilinear(coord[0], coord[1]);
|
|
212
|
+
const sampler = this._getElevationSampler(normalized.tileID);
|
|
213
|
+
return sampler ? sampler(normalized.x, normalized.y, extent) : 0;
|
|
210
214
|
}
|
|
211
215
|
|
|
212
216
|
/**
|
|
@@ -250,6 +254,59 @@ export class Terrain {
|
|
|
250
254
|
return this.getDEMElevation(tileID, x, y, extent) * this.exaggeration;
|
|
251
255
|
}
|
|
252
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Clear CPU elevation samplers that may retain a previously selected DEM tile.
|
|
259
|
+
* @internal
|
|
260
|
+
*/
|
|
261
|
+
resetElevationCache(): void {
|
|
262
|
+
this._elevationSamplerCache.clear();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
_getElevationSampler(tileID: OverscaledTileID): TerrainElevationSampler | null {
|
|
266
|
+
const key = tileID.key;
|
|
267
|
+
const cachedSampler = this._elevationSamplerCache.get(key);
|
|
268
|
+
if (cachedSampler) return cachedSampler;
|
|
269
|
+
|
|
270
|
+
const sourceTile = this.tileManager.getSourceTile(tileID, true);
|
|
271
|
+
const dem = sourceTile?.dem;
|
|
272
|
+
if (!sourceTile || !dem) return null;
|
|
273
|
+
|
|
274
|
+
const matrix = this._getDEMTileMatrix(tileID, sourceTile);
|
|
275
|
+
// Store the vector-tile to DEM-pixel transform once for the hot sampling loop.
|
|
276
|
+
const demPixelScaleX = matrix[0] * dem.dim;
|
|
277
|
+
const demPixelScaleY = matrix[5] * dem.dim;
|
|
278
|
+
const demPixelOffsetX = matrix[12] * dem.dim;
|
|
279
|
+
const demPixelOffsetY = matrix[13] * dem.dim;
|
|
280
|
+
const sampler = (x: number, y: number, extent: number): number => {
|
|
281
|
+
const extentScale = extent === EXTENT ? 1 : EXTENT / extent;
|
|
282
|
+
return dem.sampleBilinear(
|
|
283
|
+
x * extentScale * demPixelScaleX + demPixelOffsetX,
|
|
284
|
+
y * extentScale * demPixelScaleY + demPixelOffsetY
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
this._elevationSamplerCache.set(key, sampler);
|
|
288
|
+
return sampler;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
_getDEMTileMatrix(tileID: OverscaledTileID, sourceTile: Tile): mat4 {
|
|
292
|
+
const matrixKey = `${sourceTile.tileID.key}/${tileID.key}`;
|
|
293
|
+
const cachedMatrix = this._demMatrixCache.get(matrixKey);
|
|
294
|
+
if (cachedMatrix) return cachedMatrix;
|
|
295
|
+
|
|
296
|
+
const maxzoom = this.tileManager.getSource().maxzoom;
|
|
297
|
+
let dz = tileID.canonical.z - sourceTile.tileID.canonical.z;
|
|
298
|
+
if (tileID.overscaledZ > tileID.canonical.z) {
|
|
299
|
+
if (tileID.canonical.z >= maxzoom) dz = tileID.canonical.z - maxzoom;
|
|
300
|
+
else warnOnce('cannot calculate elevation if elevation maxzoom > source.maxzoom');
|
|
301
|
+
}
|
|
302
|
+
const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
|
|
303
|
+
const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
|
|
304
|
+
const demMatrix = mat4.fromScaling(new Float64Array(16), [1 / (EXTENT << dz), 1 / (EXTENT << dz), 0]);
|
|
305
|
+
mat4.translate(demMatrix, demMatrix, [dx * EXTENT, dy * EXTENT, 0]);
|
|
306
|
+
this._demMatrixCache.set(matrixKey, demMatrix);
|
|
307
|
+
return demMatrix;
|
|
308
|
+
}
|
|
309
|
+
|
|
253
310
|
/**
|
|
254
311
|
* returns a Terrain Object for a tile. Unless the tile corresponds to data (e.g. tile is loading), return a flat dem object
|
|
255
312
|
* @param tileID - the tile to get the terrain for
|
|
@@ -277,27 +334,13 @@ export class Terrain {
|
|
|
277
334
|
sourceTile.demTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
|
|
278
335
|
sourceTile.needsTerrainPrepare = false;
|
|
279
336
|
}
|
|
280
|
-
|
|
281
|
-
const matrixKey = sourceTile && sourceTile.toString() + sourceTile.tileID.key + tileID.key;
|
|
282
|
-
if (matrixKey && !this._demMatrixCache[matrixKey]) {
|
|
283
|
-
const maxzoom = this.tileManager.getSource().maxzoom;
|
|
284
|
-
let dz = tileID.canonical.z - sourceTile.tileID.canonical.z;
|
|
285
|
-
if (tileID.overscaledZ > tileID.canonical.z) {
|
|
286
|
-
if (tileID.canonical.z >= maxzoom) dz = tileID.canonical.z - maxzoom;
|
|
287
|
-
else warnOnce('cannot calculate elevation if elevation maxzoom > source.maxzoom');
|
|
288
|
-
}
|
|
289
|
-
const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
|
|
290
|
-
const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
|
|
291
|
-
const demMatrix = mat4.fromScaling(new Float64Array(16), [1 / (EXTENT << dz), 1 / (EXTENT << dz), 0]);
|
|
292
|
-
mat4.translate(demMatrix, demMatrix, [dx * EXTENT, dy * EXTENT, 0]);
|
|
293
|
-
this._demMatrixCache[matrixKey] = {matrix: demMatrix, coord: tileID};
|
|
294
|
-
}
|
|
337
|
+
const terrainMatrix = sourceTile ? this._getDEMTileMatrix(tileID, sourceTile) : this._emptyDemMatrix;
|
|
295
338
|
// return uniform values & textures
|
|
296
339
|
return {
|
|
297
340
|
'u_depth': 2,
|
|
298
341
|
'u_terrain': 3,
|
|
299
342
|
'u_terrain_dim': sourceTile?.dem?.dim || 1,
|
|
300
|
-
'u_terrain_matrix':
|
|
343
|
+
'u_terrain_matrix': terrainMatrix,
|
|
301
344
|
'u_terrain_unpack': sourceTile?.dem?.getUnpackVector() || this._emptyDemUnpack,
|
|
302
345
|
'u_terrain_exaggeration': this.exaggeration,
|
|
303
346
|
texture: (sourceTile?.demTexture || this._emptyDemTexture).texture,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
uniform bool u_projection_clip_antimeridian;
|
|
2
|
+
|
|
3
|
+
in highp float v_projection_tile_x;
|
|
4
|
+
|
|
5
|
+
// On globe the z0 tile's buffer wraps around the planet onto the tile itself, drawing geometry near the antimeridian twice.
|
|
6
|
+
// To avoid this, we discard fragments beyond the tile's X extent (0..8192) when enabled for the tile.
|
|
7
|
+
// The range test is half-open so that both sides of the antimeridian tile the seam exactly, without overlap.
|
|
8
|
+
void clipAntimeridian() {
|
|
9
|
+
if (u_projection_clip_antimeridian && (v_projection_tile_x < 0.0 || v_projection_tile_x >= 8192.0)) {
|
|
10
|
+
discard;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is generated. Edit build/generate-shaders.ts, then run `npm run codegen`.
|
|
2
|
+
export default 'uniform bool u_projection_clip_antimeridian;in highp float v_projection_tile_x;void clipAntimeridian() {if (u_projection_clip_antimeridian && (v_projection_tile_x < 0.0 || v_projection_tile_x >=8192.0)) {discard;}}';
|