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

Custom layer immediate and draped rendering on globe and terrain #12182

Merged
merged 25 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8b9071f
WIP: Custom layer draping support
astojilj Jun 15, 2022
9f06ee7
dirty quick way to render a geometry with custom layer on globe
akoylasar Jun 17, 2022
8a604c2
expose necessary API, some clean up
akoylasar Aug 8, 2022
c2ed1b6
use sphere model
akoylasar Aug 8, 2022
1c2368a
WIP combining PRs and changing immediate more rendering demo
akoylasar Aug 12, 2022
bb9cbe0
some so so experiments with rendering cloud
akoylasar Aug 21, 2022
17bba08
satellite vis
akoylasar Aug 22, 2022
bef8bc2
satellite vis complete
akoylasar Aug 22, 2022
bda3020
clean up
akoylasar Aug 23, 2022
d523d98
continued clean up
akoylasar Jan 13, 2023
4833451
add utility func to lnglat class and also render in mercator
akoylasar Jan 16, 2023
589b19d
formatgs
akoylasar Jan 16, 2023
65ace46
rm threejs
akoylasar Jan 16, 2023
cec55a8
rm unused funcs
akoylasar Jan 16, 2023
fd26f28
minor
akoylasar Jan 16, 2023
c490786
render test for draping
akoylasar Jan 16, 2023
a17bc70
render test updated
akoylasar Jan 16, 2023
32af347
minor update to render test
akoylasar Jan 17, 2023
70a557d
update debug page to use pure gl only
akoylasar Jan 17, 2023
1e00366
revert accidentally changed files to main
akoylasar Jan 17, 2023
dbf9173
address review remarks, fix incorrect color, disable stencil
akoylasar Jan 18, 2023
835ea69
disable stencil from gl-js side before invoking renderToTile
akoylasar Jan 18, 2023
edce689
Pass projection and resolve mercatorMatrix during transition
astojilj Jan 23, 2023
ff45cae
Lint and Flow fixes
astojilj Jan 23, 2023
0979259
break circular dependency, final minor fixes
akoylasar Jan 24, 2023
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
67 changes: 67 additions & 0 deletions debug/color-earth-custom-layer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
function getColor(tileId) {
const m = Math.pow(2, tileId.z);
const s = tileId.z + tileId.x * m + tileId.y * m;
const r = (Math.sin(s + 5) * 1924957) % 1;
const g = (Math.sin(s + 7) * 3874133) % 1;
const b = (Math.sin(s + 3) * 7662617) % 1;
return [r, g, b];
};

var coloredEarthLayer = {
id: 'coloredEarth',
type: 'custom',

onAdd: (map, gl) => {
const vertexSource = `
attribute vec2 a_pos;
void main() {
gl_Position = vec4(a_pos, 1.0, 1.0);
}`;

const fragmentSource = `
precision highp float;
uniform vec3 u_color;
void main() {
gl_FragColor = vec4(u_color, 0.5);
}`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);

this.program = gl.createProgram();
gl.attachShader(this.program, vertexShader);
gl.attachShader(this.program, fragmentShader);
gl.linkProgram(this.program);

this.program.aPos = gl.getAttribLocation(this.program, "a_pos");
this.program.uColor = gl.getUniformLocation(this.program, "u_color");

const verts = new Float32Array([1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1]);
this.vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);
},

shouldRerenderTiles: () => {
// return true only when frame content has changed otherwise, all the terrain
// render cache would be invalidated and redrawn causing huge drop in performance.
return true;
},

renderToTile: (gl, tileId) => {
gl.useProgram(this.program);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.enableVertexAttribArray(this.program.aPos);
gl.vertexAttribPointer(this.program.aPos, 2, gl.FLOAT, false, 0, 0);
const color = getColor(tileId);
gl.uniform3f(this.program.uColor, color[0], color[1], color[2]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
},

render: (gl, matrix) => {
}
};
79 changes: 79 additions & 0 deletions debug/custom-layer-globe.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<title>Mapbox GL JS custom layers on globe page</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel='stylesheet' href='../dist/mapbox-gl.css' />
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
#checkboxes {
position: absolute;
top:0;
left:0;
padding:10px;
}
</style>
</head>

<body>
<div id='map'></div>
<div id='checkboxes'>
<label><input id='immediate-checkbox' type='checkbox' checked> add a custom layer with immediate rendering</label><br />
<label><input id='draped-checkbox' type='checkbox'> add a custom layer with draped rendering</label><br />
<label><input id='repaint-checkbox' type='checkbox'> repaint. FPS: </label><label id='fps'>0</label><br />
</div>

<script src="//unpkg.com/satellite.js/dist/satellite.min.js"></script>
<script src="satellites-custom-layer.js"></script>

<script src="color-earth-custom-layer.js"></script>

<script src='../dist/mapbox-gl-dev.js'></script>
<script src='../debug/access_token_generated.js'></script>

<script>
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/satellite-v9',
center: [0, 0],
zoom: 10,
antialias: true, // create the gl context with MSAA antialiasing, so custom layers are antialiased
hash:true,
projection: 'globe'
});

map.on('style.load', () => {
map.addLayer(satellitesLayer); // eslint-disable-line no-undef
map.addLayer(coloredEarthLayer); // eslint-disable-line no-undef

document.getElementById('immediate-checkbox').onclick();
document.getElementById('draped-checkbox').onclick();
});

document.getElementById('immediate-checkbox').onclick = function() {
map.setLayoutProperty('satellites', 'visibility', this.checked ? 'visible' : 'none');
};

document.getElementById('draped-checkbox').onclick = function() {
map.setLayoutProperty('coloredEarth', 'visibility', this.checked ? 'visible' : 'none');
};

document.getElementById('repaint-checkbox').onclick = function() {
map.repaint = !!this.checked;
if (this.checked) {
this['frameCounter'] = map.painter.frameCounter;
this['fpsTimer'] = window.setInterval(() => {
document.getElementById('fps').innerHTML = `${(map.painter.frameCounter - this.frameCounter) / 2}`;
this.frameCounter = map.painter.frameCounter;
}, 2000);
} else {
window.clearInterval(this.fpsTimer);
document.getElementById('fps').innerHTML = `0`;
}
};

</script>
</body>
</html>
169 changes: 169 additions & 0 deletions debug/satellites-custom-layer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
const EARTH_RADIUS_METERS = 6371008.8;
const EARTH_CIRCUMFERENCE_METERS = 2 * Math.PI * EARTH_RADIUS_METERS;
const GLOBE_CIRCUMFERENCE_ECEF = 8192;
const METERS_TO_ECEF = GLOBE_CIRCUMFERENCE_ECEF / EARTH_CIRCUMFERENCE_METERS;

const KM_TO_M = 1000;
const TIME_STEP = 3 * 1000;

const globeVertCode = `
attribute vec3 a_pos_ecef;
attribute vec3 a_pos_merc;

uniform mat4 u_projection;
uniform mat4 u_globeToMercMatrix;
uniform float u_globeToMercatorTransition;

void main() {
vec4 p = u_projection * u_globeToMercMatrix * vec4(a_pos_ecef, 1.);
p /= p.w;
if (u_globeToMercatorTransition > 0.) {
vec4 merc = u_projection * vec4(a_pos_merc, 1.);
merc /= merc.w;
p = mix(p, merc, u_globeToMercatorTransition);
}
gl_PointSize = 30.;
gl_Position = p;
}
`;

const mercVertCode = `
precision highp float;
attribute vec3 a_pos_merc;
uniform mat4 u_projection;

void main() {
gl_PointSize = 30.;
gl_Position = u_projection * vec4(a_pos_merc, 1.);
}
`;

const fragCode = `
precision highp float;
uniform vec4 u_color;

void main() {
gl_FragColor = vec4(1., 0., 0., 1.);
}
`;

let time = new Date();

function createShader(gl, src, type) {
var shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
const message = gl.getShaderInfoLog(shader);
if (message.length > 0) {
console.error(message);
}
return shader;
};

function createProgram(gl, vert, frag) {
var vertShader = this.createShader(gl, vert, gl.VERTEX_SHADER);
var fragShader = this.createShader(gl, frag, gl.FRAGMENT_SHADER);

var program = gl.createProgram();
gl.attachShader(program, vertShader);
gl.attachShader(program, fragShader);
gl.linkProgram(program);
gl.validateProgram(program);

if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
const info = gl.getProgramInfoLog(program);
console.error(`Could not compile WebGL program. \n\n${info}`);
}

return program;
};

function updateVboAndActivateAttrib(gl, prog, vbo, data, attribName) {
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.DYNAMIC_DRAW);
const attribLoc = gl.getAttribLocation(prog, attribName);
gl.vertexAttribPointer(attribLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(attribLoc);
}

const satellitesLayer = {
id: 'satellites',
type: 'custom',
onAdd (map, gl) {
this.map = map;

this.posEcef = [];
this.posMerc = [];

this.posEcefVbo = gl.createBuffer();
this.posMercVbo = gl.createBuffer();

this.globeProgram = createProgram(gl, globeVertCode, fragCode);
this.mercProgram = createProgram(gl, mercVertCode, fragCode);

fetch('space-track-leo.txt').then(r => r.text()).then(rawData => {
const tleData = rawData.replace(/\r/g, '')
.split(/\n(?=[^12])/)
.filter(d => d)
.map(tle => tle.split('\n'));
this.satData = tleData.map(([name, ...tle]) => ({
satrec: satellite.twoline2satrec(...tle),
name: name.trim().replace(/^0 /, '')
}))
// exclude those that can't be propagated
.filter(d => !!satellite.propagate(d.satrec, new Date()).position)
.slice(0, 10);

this.updateBuffers();
});
},

updateBuffers() {
time = new Date(+time + TIME_STEP);
const gmst = satellite.gstime(time);
this.posEcef = [];
this.posMerc = [];
for (let i = 0; i < this.satData.length; ++i) {
const satrec = this.satData[i].satrec;
const eci = satellite.propagate(satrec, time);
if (eci.position) {
const geodetic = satellite.eciToGeodetic(eci.position, gmst);

const lngLat = [satellite.degreesLong(geodetic.longitude), satellite.degreesLat(geodetic.latitude)];
const altitude = geodetic.height * KM_TO_M;

const merc = mapboxgl.MercatorCoordinate.fromLngLat(lngLat, altitude);
const ecef = mapboxgl.LngLat.convert(lngLat).toEcef(altitude);

this.posEcef.push(...ecef);
this.posMerc.push(...[merc.x, merc.y, merc.z]);
}
}
},

render (gl, projectionMatrix, projection, globeToMercMatrix, transition) {
if (this.satData) {
this.updateBuffers();

const primitiveCount = this.posEcef.length / 3;
gl.enable(gl.DEPTH_TEST);
if (projection && projection.name === 'globe') { // globe projection and globe to mercator transition
gl.useProgram(this.globeProgram);

updateVboAndActivateAttrib(gl, this.globeProgram, this.posEcefVbo, this.posEcef, "a_pos_ecef");
updateVboAndActivateAttrib(gl, this.globeProgram, this.posMercVbo, this.posMerc, "a_pos_merc");

gl.uniformMatrix4fv(gl.getUniformLocation(this.globeProgram, "u_projection"), false, projectionMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(this.globeProgram, "u_globeToMercMatrix"), false, globeToMercMatrix);
gl.uniform1f(gl.getUniformLocation(this.globeProgram, "u_globeToMercatorTransition"), transition);

gl.drawArrays(gl.POINTS, 0, primitiveCount);
} else { // mercator projection
gl.useProgram(this.mercProgram);
updateVboAndActivateAttrib(gl, this.mercProgram, this.posMercVbo, this.posMerc, "a_pos_merc");
gl.uniformMatrix4fv(gl.getUniformLocation(this.mercProgram, "u_projection"), false, projectionMatrix);
gl.drawArrays(gl.POINTS, 0, primitiveCount);
}
}
}
};
Loading