Skip to content

Commit

Permalink
Globe - basic infrastructure, raster layer adaptation for globe (#3783)
Browse files Browse the repository at this point in the history
* Port changes from main globe branch - basics

Fix minor issues so that it compiles.

* Fix PI redefinitions

* Fix stencil shader

* Port adaptation of raster layer for globe from main globe branch

* Add globe.html example from pheonor's repo

Minor changes (remove terrain, set initial zoom 0, change title and description)

* Better map projection parameter doc comment, warn when using unknown projection

* Mercator projectionData handles negative zoom correctly

* Comment clarification

* Fix spelling of "granularity"

* Add missing docs

* Convert ProjectionBase to an interface

* Do not leak GL object in globe projection error measurement, add a destroy method to projection

* Fix chrome performance warning, refactor error measurement

Warning fixed by changing ring buffer size to 1, making ring buffer pointless, so I removed it.

* Fix granularity capitalization

* Fix capitalization

* Fix typo

* Fix stencil mask triangle index order (this was causing failing render tests)

* Cleanup vertex shader projection interface

* Move projection creation function into its own file

* Remove getProjectionName

* Added comment for deduplicateWrapped

* Remove unused vertex-buffer-related code from image source

* Add globe raster layer render test

* More render tests - test transition to mercator

* Remove pointless test, add test descriptions

* Render test for rendering poles on globe

* SubdivisionGranularitySetting constructor takes an object

* Remove "defines" parameter from useProgram

* Refactor useProgram and Program constructor

* Properly format translatePosMatrix comment

* Refactor globe-specific code outside projection classes, remove stencil-specific granularity settings

* Refactor granularity settings to be more readable

* Minor refactor of ProjectionErrorMeasurement

* Refactor draw_raster.ts

* Move globe utility functions to utils.ts, use easeCubicInOut instead of smoothStep

* Simplify imports in globe.ts

* globe.ts refactor

* Move ProjectionErrorMeasurement to a separate file

* Refactor ProjectionErrorMeasurement

Change parseRGBA8float to a private static function, use isWebGL2 function instead of instanceof

* Refactor draw_raster.ts

* Refactor globe projection error measurement to not use Painter

* Painter.clearStencil creates custom ProjectionData instead of calling getProjectionData(null, null)

* Remove "deduplicateWrapped" functionality from source_cache.ts

* Globe projection no longer requires a map instance

* Painter doesn't pass `this` to `updateGPUdependent`

* isRenderingDirty is now a function

* Rename ProjectionBase to Projection

* Replace globeView property with setGlobeViewAllowed

* Add mercator and globe projection unit tests

* Remove tests that test for exact clipping planes

* Update build test with new bundle size

* isRenderingDirty is now a function
  • Loading branch information
kubapelc authored Mar 15, 2024
1 parent c070aa7 commit 6132078
Show file tree
Hide file tree
Showing 68 changed files with 2,241 additions and 243 deletions.
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
123 changes: 123 additions & 0 deletions src/geo/projection/globe.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {mat4} from 'gl-matrix';
import {GlobeProjection} from './globe';
import {EXTENT} from '../../data/extent';
import {Transform} from '../transform';
import {expectToBeCloseToArray} from './mercator.test';

describe('GlobeProjection', () => {
describe('getProjectionData', () => {
const globe = new GlobeProjection();

test('fallback matrix is set', () => {
const mat = mat4.create();
mat[0] = 1234;
const projectionData = globe.getProjectionData({
x: 0,
y: 0,
z: 0
}, mat);
expect(projectionData.u_projection_fallback_matrix).toEqual(mat);
});
test('mercator tile extents are set', () => {
const mat = mat4.create();
const projectionData = globe.getProjectionData({
x: 1,
y: 0,
z: 1
}, mat);
expectToBeCloseToArray(projectionData.u_projection_tile_mercator_coords, [0.5, 0, 0.5 / EXTENT, 0.5 / EXTENT]);
});
});

describe('clipping plane', () => {
const globe = new GlobeProjection();

describe('general plane properties', () => {
const mat = mat4.create();
const transform = createMockTransform({
pitchDegrees: 0,
});
globe.updateProjection(transform);
const projectionData = globe.getProjectionData({
x: 0,
y: 0,
z: 0
}, mat);

test('plane vector length', () => {
const len = Math.sqrt(
projectionData.u_projection_clipping_plane[0] * projectionData.u_projection_clipping_plane[0] +
projectionData.u_projection_clipping_plane[1] * projectionData.u_projection_clipping_plane[1] +
projectionData.u_projection_clipping_plane[2] * projectionData.u_projection_clipping_plane[2]
);
expect(len).toBeCloseTo(0.25);
});

test('camera is in positive halfspace', () => {
expect(planeDistance((globe as any)._globeCameraPosition, projectionData.u_projection_clipping_plane)).toBeGreaterThan(0);
});

test('coordinates 0E,0N are in positive halfspace', () => {
expect(testPlaneAgainstLngLat(0, 0, projectionData.u_projection_clipping_plane)).toBeGreaterThan(0);
});

test('coordinates 40E,0N are in positive halfspace', () => {
expect(testPlaneAgainstLngLat(40, 0, projectionData.u_projection_clipping_plane)).toBeGreaterThan(0);
});

test('coordinates 0E,90N are in negative halfspace', () => {
expect(testPlaneAgainstLngLat(0, 90, projectionData.u_projection_clipping_plane)).toBeLessThan(0);
});

test('coordinates 90E,0N are in negative halfspace', () => {
expect(testPlaneAgainstLngLat(90, 0, projectionData.u_projection_clipping_plane)).toBeLessThan(0);
});

test('coordinates 180E,0N are in negative halfspace', () => {
expect(testPlaneAgainstLngLat(180, 0, projectionData.u_projection_clipping_plane)).toBeLessThan(0);
});
});
});
});

function testPlaneAgainstLngLat(lngDegrees: number, latDegrees: number, plane: Array<number>) {
const lat = latDegrees / 180.0 * Math.PI;
const lng = lngDegrees / 180.0 * Math.PI;
const len = Math.cos(lat);
const pointOnSphere = [
Math.sin(lng) * len,
Math.sin(lat),
Math.cos(lng) * len
];
return planeDistance(pointOnSphere, plane);
}

function planeDistance(point: Array<number>, plane: Array<number>) {
return point[0] * plane[0] + point[1] * plane[1] + point[2] * plane[2] + plane[3];
}

function createMockTransform(object: {
center?: {
latDegrees: number;
lngDegrees: number;
};
pitchDegrees?: number;
angleDegrees?: number;
}): Transform {
const pitchDegrees = object.pitchDegrees ? object.pitchDegrees : 0;
return {
center: {
lat: object.center ? (object.center.latDegrees / 180.0 * Math.PI) : 0,
lng: object.center ? (object.center.lngDegrees / 180.0 * Math.PI) : 0,
},
worldSize: 10.5 * 512,
_fov: Math.PI / 4.0,
width: 640,
height: 480,
cameraToCenterDistance: 759,
_pitch: pitchDegrees / 180.0 * Math.PI, // in radians
pitch: pitchDegrees, // in degrees
angle: object.angleDegrees ? (object.angleDegrees / 180.0 * Math.PI) : 0,
zoom: 0,
} as Transform;
}
Loading

0 comments on commit 6132078

Please sign in to comment.