Skip to content

Commit

Permalink
Consume tile engine from three-slippy-map-globe package
Browse files Browse the repository at this point in the history
  • Loading branch information
vasturiano committed Jan 6, 2025
1 parent 32ee28a commit 103342d
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 378 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"kapsule": "^1.16",
"three-conic-polygon-geometry": "2",
"three-geojson-geometry": "2",
"three-slippy-map-globe": "^0.7.1",
"tinycolor2": "1",
"yaot": "^1.1"
},
Expand Down
34 changes: 3 additions & 31 deletions src/globe-kapsule.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ const linkedGlobeLayerProps = Object.assign(...[
'globeImageUrl',
'bumpImageUrl',
'globeTileEngineUrl',
'globeTileEngineImgSize',
'globeTileEngineThresholds',
'globeTileEngineMaxLevel',
'showGlobe',
'showGraticules',
'showAtmosphere',
Expand Down Expand Up @@ -319,35 +318,9 @@ export default Kapsule({
};
}

let cameraDistance = undefined;
let isInView = undefined;
if (state.scene && camera instanceof THREE.Camera) {
const pov = camera.position;
const distToGlobeCenter = pov.distanceTo(state.scene.getWorldPosition(new THREE.Vector3()));
cameraDistance = (distToGlobeCenter - globeRadius) / globeRadius; // in units of globe radius

let frustum;
isInView = pos => {
const wPos = pos.clone().applyMatrix4(state.scene.matrixWorld);

if (!frustum) {
camera.updateMatrix();
camera.updateMatrixWorld();
frustum = new THREE.Frustum();
frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
}

if (!frustum.containsPoint(wPos)) return false; // point out of view

// simplistic way to check if it's behind globe: if it's farther than the center of the globe
return pov.distanceTo(wPos) < distToGlobeCenter;
}
}

// pass pov-related checker fns for layers that need it
state.layersThatNeedUpdatePov.forEach(l => l.updatePov(camera));
state.layersThatNeedBehindGlobeChecker.forEach(l => l.isBehindGlobe(isBehindGlobe));
state.layersThatNeedInViewChecker.forEach(l => l.isInView(isInView));
state.layersThatNeedCameraDistance.forEach(l => l.cameraDistance(cameraDistance));
},
pauseAnimation: function(state) {
if (state.animationFrameRequestId !== null) {
Expand Down Expand Up @@ -399,9 +372,8 @@ export default Kapsule({
return {
tweenGroup,
...layers,
layersThatNeedUpdatePov: Object.values(layers).filter(l => l.hasOwnProperty('updatePov')),
layersThatNeedBehindGlobeChecker: Object.values(layers).filter(l => l.hasOwnProperty('isBehindGlobe')),
layersThatNeedInViewChecker: Object.values(layers).filter(l => l.hasOwnProperty('isInView')),
layersThatNeedCameraDistance: Object.values(layers).filter(l => l.hasOwnProperty('cameraDistance')),
destructableLayers: Object.values(layers).filter(l => l.hasOwnProperty('_destructor')),
pausableLayers: Object.values(layers).filter(l => l.hasOwnProperty('pauseAnimation'))
};
Expand Down
6 changes: 2 additions & 4 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,8 @@ export declare class ThreeGlobeGeneric<ChainableInstance> extends Object3D {
atmosphereAltitude(alt: number): ChainableInstance;
globeTileEngineUrl(): (x: number, y: number, level: number) => string;
globeTileEngineUrl(urlFn: (x: number, y: number, level: number) => string): ChainableInstance;
globeTileEngineImgSize(): number;
globeTileEngineImgSize(size: number): ChainableInstance;
globeTileEngineThresholds(): number[];
globeTileEngineThresholds(thresholds: number[]): ChainableInstance;
globeTileEngineMaxLevel(): number;
globeTileEngineMaxLevel(level: number): ChainableInstance;
globeMaterial(): Material;
globeMaterial(globeMaterial: Material): ChainableInstance;
onGlobeReady(callback: (() => void)): ChainableInstance;
Expand Down
52 changes: 24 additions & 28 deletions src/layers/globe.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Color,
Group,
LineBasicMaterial,
LineSegments,
Mesh,
Expand All @@ -13,6 +14,7 @@ const THREE = window.THREE
? window.THREE // Prefer consumption from global THREE, if exists
: {
Color,
Group,
LineBasicMaterial,
LineSegments,
Mesh,
Expand All @@ -22,13 +24,13 @@ const THREE = window.THREE
TextureLoader
};

import SlippyMap from 'three-slippy-map-globe';
import GeoJsonGeometry from 'three-geojson-geometry';
import GlowMesh from '../utils/GlowMesh.js';

import Kapsule from 'kapsule';
import { geoGraticule10 } from 'd3-geo';

import TileEngine, { convertMercatorUV } from '../utils/tile-engine';
import { emptyObject } from '../utils/gc';
import { GLOBE_RADIUS } from '../constants';

Expand All @@ -38,29 +40,14 @@ export default Kapsule({
props: {
globeImageUrl: {},
bumpImageUrl: {},
showGlobe: { default: true, onChange(showGlobe, state) { state.globeObj.visible = !!showGlobe }, triggerUpdate: false },
showGlobe: { default: true, onChange(showGlobe, state) { state.globeGroup.visible = !!showGlobe }, triggerUpdate: false },
showGraticules: { default: false, onChange(showGraticules, state) { state.graticulesObj.visible = !!showGraticules }, triggerUpdate: false },
showAtmosphere: { default: true, onChange(showAtmosphere, state) { state.atmosphereObj && (state.atmosphereObj.visible = !!showAtmosphere) }, triggerUpdate: false },
atmosphereColor: { default: 'lightskyblue' },
atmosphereAltitude: { default: 0.15 },
globeTileEngineUrl: { onChange(v, state, prevV) {
// Distort or reset globe UVs as tile engine is being enabled/disabled
const uvs = state.globeObj.geometry.attributes.uv;
if (v && !prevV) {
state.linearUVs = uvs.array.slice(); // in case they need to be put back
convertMercatorUV(uvs);
}
if (!v && prevV) {
uvs.array = state.linearUVs;
uvs.needsUpdate = true;
}

state.tileEngine.url(v);
}},
globeTileEngineImgSize: { default: 256, onChange(v, state) { state.tileEngine.imgSize(v) }, triggerUpdate: false },
globeTileEngineThresholds: { default: [5, 2, 3/4, 1/4, 1/8, 1/16], onChange(v, state) { state.tileEngine.thresholds(v) }, triggerUpdate: false },
cameraDistance: { onChange(v, state) { state.tileEngine.cameraDistance(v) }, triggerUpdate: false },
isInView: { onChange(v, state) { state.tileEngine.isInView(v) }, triggerUpdate: false },
globeTileEngineUrl: { onChange(v, state) { state.tileEngine.tileUrl = v }, triggerUpdate: false },
globeTileEngineMaxLevel: { default: 20, onChange(v, state) { state.tileEngine.maxLevel = v }, triggerUpdate: false },
updatePov: { onChange(v, state) { state.tileEngine.updatePov(v) }, triggerUpdate: false },
onReady: { default: () => {}, triggerUpdate: false }
},
methods: {
Expand All @@ -72,8 +59,8 @@ export default Kapsule({
return state.globeObj.material;
},
_destructor: function(state) {
state.tileEngine._destructor();
emptyObject(state.globeObj);
emptyObject(state.tileEngine);
emptyObject(state.graticulesObj);
}
},
Expand All @@ -84,18 +71,24 @@ export default Kapsule({
const defaultGlobeMaterial = new THREE.MeshPhongMaterial({ color: 0x000000 });
const globeObj = new THREE.Mesh(globeGeometry, defaultGlobeMaterial);
globeObj.rotation.y = -Math.PI / 2; // face prime meridian along Z axis
globeObj.__globeObjType = 'globe'; // Add object type

// Create empty tile engine
const tileEngine = new SlippyMap(GLOBE_RADIUS);

// Group including globe and tile engine
const globeGroup = new THREE.Group();
globeGroup.__globeObjType = 'globe'; // Add object type
globeGroup.add(globeObj);
globeGroup.add(tileEngine);

// create graticules
const graticulesObj = new THREE.LineSegments(
new GeoJsonGeometry(geoGraticule10(), GLOBE_RADIUS, 2),
new THREE.LineBasicMaterial({ color: 'lightgrey', transparent: true, opacity: 0.1 })
);

// Bind tile engine to material
const tileEngine = new TileEngine(globeObj.material);

return {
globeGroup,
globeObj,
graticulesObj,
defaultGlobeMaterial,
Expand All @@ -110,7 +103,7 @@ export default Kapsule({
// Main three object to manipulate
state.scene = threeObj;

state.scene.add(state.globeObj); // add globe
state.scene.add(state.globeGroup); // add globe
state.scene.add(state.graticulesObj); // add graticules

state.ready = false;
Expand All @@ -119,9 +112,12 @@ export default Kapsule({
update(state, changedProps) {
const globeMaterial = state.globeObj.material;

if (!state.globeTileEngineUrl && ['globeImageUrl', 'globeTileEngineUrl'].some(p => changedProps.hasOwnProperty(p))) {
// Hide globeObj if it's representing tiles
state.globeObj.visible = !state.globeTileEngineUrl;

if (changedProps.hasOwnProperty('globeImageUrl')) {
if (!state.globeImageUrl) {
// Black globe if no image nor tiles
// Black globe if no image
!globeMaterial.color && (globeMaterial.color = new THREE.Color(0x000000));
} else {
new THREE.TextureLoader().load(state.globeImageUrl, texture => {
Expand Down
163 changes: 0 additions & 163 deletions src/utils/tile-engine.js

This file was deleted.

Loading

0 comments on commit 103342d

Please sign in to comment.