UNPKG

three-slippy-map-globe

Version:

Tiled maps on a globe as a ThreeJS reusable 3D object

608 lines (586 loc) 24.2 kB
import { Camera, Vector3, Group, Mesh, SphereGeometry, MeshBasicMaterial, Frustum, Matrix4, MeshLambertMaterial, TextureLoader, SRGBColorSpace } from 'three'; import { octree } from 'd3-octree'; import { scaleLinear } from 'd3-scale'; import { geoMercatorRaw } from 'd3-geo'; function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } function _classPrivateFieldGet2(s, a) { return s.get(_assertClassBrand(s, a)); } function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); } function _classPrivateFieldSet2(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; } function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), Object.defineProperty(e, "prototype", { writable: false }), e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e; } function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: true, configurable: true } }), Object.defineProperty(t, "prototype", { writable: false }), e && _setPrototypeOf(t, e); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = true, o = false; try { if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = true, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _possibleConstructorReturn(t, e) { if (e && ("object" == typeof e || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return (String )(t); } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } var _materialDispose = function materialDispose(material) { if (material instanceof Array) { material.forEach(_materialDispose); } else { if (material.map) { material.map.dispose(); } material.dispose(); } }; var _deallocate = function deallocate(obj) { if (obj.geometry) { obj.geometry.dispose(); } if (obj.material) { _materialDispose(obj.material); } if (obj.texture) { obj.texture.dispose(); } if (obj.children) { obj.children.forEach(_deallocate); } }; function polar2Cartesian(lat, lng, r) { var phi = (90 - lat) * Math.PI / 180; var theta = (90 - lng) * Math.PI / 180; return { x: r * Math.sin(phi) * Math.cos(theta), y: r * Math.cos(phi), z: r * Math.sin(phi) * Math.sin(theta) }; } function cartesian2Polar(_ref) { var x = _ref.x, y = _ref.y, z = _ref.z; var r = Math.sqrt(x * x + y * y + z * z); var phi = Math.acos(y / r); var theta = Math.atan2(z, x); return { lat: 90 - phi * 180 / Math.PI, lng: 90 - theta * 180 / Math.PI - (theta < -Math.PI / 2 ? 360 : 0), // keep within [-180, 180] boundaries r: r }; } function deg2Rad(deg) { return deg * Math.PI / 180; } var yMercatorScale = function yMercatorScale(y) { return 1 - (geoMercatorRaw(0, (0.5 - y) * Math.PI)[1] / Math.PI + 1) / 2; }; var yMercatorScaleClamped = function yMercatorScaleClamped(y) { return Math.max(0, Math.min(1, yMercatorScale(y))); }; var yMercatorScaleInvert = function yMercatorScaleInvert(y) { return 0.5 - geoMercatorRaw.invert(0, (2 * (1 - y) - 1) * Math.PI)[1] / Math.PI; }; var convertMercatorUV = function convertMercatorUV(uvs) { var y0 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var y1 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; var offsetScale = scaleLinear().domain([1, 0]).range([y0, y1]).clamp(true); var revOffsetScale = scaleLinear().domain([yMercatorScaleClamped(y0), yMercatorScaleClamped(y1)]).range([1, 0]).clamp(true); var scale = function scale(v) { return revOffsetScale(yMercatorScaleClamped(offsetScale(v))); }; var arr = uvs.array; for (var i = 0, len = arr.length; i < len; i += 2) { arr[i + 1] = scale(arr[i + 1]); } uvs.needsUpdate = true; }; var findTileXY = function findTileXY(level, isMercator, lng, lat) { var gridSize = Math.pow(2, level); var x = Math.max(0, Math.min(gridSize - 1, Math.floor((lng + 180) * gridSize / 360))); var relY = (90 - lat) / 180; isMercator && (relY = Math.max(0, Math.min(1, yMercatorScale(relY)))); var y = Math.floor(relY * gridSize); return [x, y]; }; var genTilesCoords = function genTilesCoords(level, isMercator) { var x0 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var y0 = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; var _x1 = arguments.length > 4 ? arguments[4] : undefined; var _y1 = arguments.length > 5 ? arguments[5] : undefined; var tiles = []; var gridSize = Math.pow(2, level); var tileLngLen = 360 / gridSize; var regTileLatLen = 180 / gridSize; var x1 = _x1 === undefined ? gridSize - 1 : _x1; var y1 = _y1 === undefined ? gridSize - 1 : _y1; for (var x = x0, maxX = Math.min(gridSize - 1, x1); x <= maxX; x++) { for (var y = y0, maxY = Math.min(gridSize - 1, y1); y <= maxY; y++) { var reproY = y, tileLatLen = regTileLatLen; if (isMercator) { // lat needs reprojection, but stretch to cover poles reproY = y === 0 ? y : yMercatorScaleInvert(y / gridSize) * gridSize; var reproYEnd = y + 1 === gridSize ? y + 1 : yMercatorScaleInvert((y + 1) / gridSize) * gridSize; tileLatLen = (reproYEnd - reproY) * 180 / gridSize; } // tile centroid coordinates var lng = -180 + (x + 0.5) * tileLngLen; var lat = 90 - (reproY * 180 / gridSize + tileLatLen / 2); var latLen = tileLatLen; // lng is always constant among all tiles tiles.push({ x: x, y: y, lng: lng, lat: lat, latLen: latLen }); } } return tiles; }; var MAX_LEVEL_TO_RENDER_ALL_TILES = 6; // level 6 = 4096 tiles var MAX_LEVEL_TO_BUILD_LOOKUP_OCTREE = 7; // octrees consume too much memory on higher levels, generate tiles on demand for those (based on globe surface distance) as the distortion is negligible var TILE_SEARCH_RADIUS_CAMERA_DISTANCE = 3; // Euclidean distance factor, in units of camera distance to surface var TILE_SEARCH_RADIUS_SURFACE_DISTANCE = 90; // in degrees on the globe surface, relative to camera altitude in globe radius units var _radius = /*#__PURE__*/new WeakMap(); var _isMercator = /*#__PURE__*/new WeakMap(); var _tileUrl = /*#__PURE__*/new WeakMap(); var _level = /*#__PURE__*/new WeakMap(); var _tilesMeta = /*#__PURE__*/new WeakMap(); var _isInView = /*#__PURE__*/new WeakMap(); var _camera = /*#__PURE__*/new WeakMap(); var _innerBackLayer = /*#__PURE__*/new WeakMap(); var _ThreeSlippyMapGlobe_brand = /*#__PURE__*/new WeakSet(); var ThreeSlippyMapGlobe = /*#__PURE__*/function (_Group) { function ThreeSlippyMapGlobe(radius) { var _this; var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, tileUrl = _ref.tileUrl, _ref$minLevel = _ref.minLevel, minLevel = _ref$minLevel === void 0 ? 0 : _ref$minLevel, _ref$maxLevel = _ref.maxLevel, maxLevel = _ref$maxLevel === void 0 ? 17 : _ref$maxLevel, _ref$mercatorProjecti = _ref.mercatorProjection, mercatorProjection = _ref$mercatorProjecti === void 0 ? true : _ref$mercatorProjecti; _classCallCheck(this, ThreeSlippyMapGlobe); _this = _callSuper(this, ThreeSlippyMapGlobe); // Private methods _classPrivateMethodInitSpec(_this, _ThreeSlippyMapGlobe_brand); // Private attributes _classPrivateFieldInitSpec(_this, _radius, void 0); _classPrivateFieldInitSpec(_this, _isMercator, void 0); _classPrivateFieldInitSpec(_this, _tileUrl, void 0); _classPrivateFieldInitSpec(_this, _level, void 0); _classPrivateFieldInitSpec(_this, _tilesMeta, {}); _classPrivateFieldInitSpec(_this, _isInView, void 0); _classPrivateFieldInitSpec(_this, _camera, void 0); _classPrivateFieldInitSpec(_this, _innerBackLayer, void 0); _defineProperty(_this, "minLevel", void 0); _defineProperty(_this, "maxLevel", void 0); _defineProperty(_this, "thresholds", _toConsumableArray(new Array(30)).map(function (_, idx) { return 8 / Math.pow(2, idx); })); // in terms of radius units _defineProperty(_this, "curvatureResolution", 5); // in degrees, affects number of vertices in tiles _defineProperty(_this, "tileMargin", 0); _defineProperty(_this, "clearTiles", function () { Object.values(_classPrivateFieldGet2(_tilesMeta, _this)).forEach(function (l) { l.forEach(function (d) { if (d.obj) { _this.remove(d.obj); _deallocate(d.obj); delete d.obj; } }); }); _classPrivateFieldSet2(_tilesMeta, _this, {}); }); _classPrivateFieldSet2(_radius, _this, radius); _this.tileUrl = tileUrl; _classPrivateFieldSet2(_isMercator, _this, mercatorProjection); _this.minLevel = minLevel; _this.maxLevel = maxLevel; _this.level = 0; // Add protective black sphere just below surface to prevent any depth buffer anomalies _this.add(_classPrivateFieldSet2(_innerBackLayer, _this, new Mesh(new SphereGeometry(_classPrivateFieldGet2(_radius, _this) * 0.99, 180, 90), new MeshBasicMaterial({ color: 0x0 })))); _classPrivateFieldGet2(_innerBackLayer, _this).visible = false; _classPrivateFieldGet2(_innerBackLayer, _this).material.polygonOffset = true; _classPrivateFieldGet2(_innerBackLayer, _this).material.polygonOffsetUnits = 3; _classPrivateFieldGet2(_innerBackLayer, _this).material.polygonOffsetFactor = 1; return _this; } _inherits(ThreeSlippyMapGlobe, _Group); return _createClass(ThreeSlippyMapGlobe, [{ key: "tileUrl", get: // Public attributes function get() { return _classPrivateFieldGet2(_tileUrl, this); }, set: function set(tileUrl) { _classPrivateFieldSet2(_tileUrl, this, tileUrl); this.updatePov(_classPrivateFieldGet2(_camera, this)); // update current view } }, { key: "level", get: function get() { return _classPrivateFieldGet2(_level, this); }, set: function set(level) { var _classPrivateFieldGet2$1, _this2 = this; if (!_classPrivateFieldGet2(_tilesMeta, this)[level]) _assertClassBrand(_ThreeSlippyMapGlobe_brand, this, _buildMetaLevel).call(this, level); var prevLevel = _classPrivateFieldGet2(_level, this); _classPrivateFieldSet2(_level, this, level); if (level === prevLevel || prevLevel === undefined) return; // nothing else to do // Activate back layer for levels > 0, when there's !depthWrite tiles _classPrivateFieldGet2(_innerBackLayer, this).visible = level > 0; // Bring layer to front _classPrivateFieldGet2(_tilesMeta, this)[level].forEach(function (d) { return d.obj && (d.obj.material.depthWrite = true); }); // push lower layers to background prevLevel < level && ((_classPrivateFieldGet2$1 = _classPrivateFieldGet2(_tilesMeta, this)[prevLevel]) === null || _classPrivateFieldGet2$1 === void 0 ? void 0 : _classPrivateFieldGet2$1.forEach(function (d) { return d.obj && (d.obj.material.depthWrite = false); })); // Remove upper layers if (prevLevel > level) { for (var l = level + 1; l <= prevLevel; l++) { _classPrivateFieldGet2(_tilesMeta, this)[l] && _classPrivateFieldGet2(_tilesMeta, this)[l].forEach(function (d) { if (d.obj) { _this2.remove(d.obj); _deallocate(d.obj); delete d.obj; } }); } } _assertClassBrand(_ThreeSlippyMapGlobe_brand, this, _fetchNeededTiles).call(this); } // Public methods }, { key: "updatePov", value: function updatePov(camera) { var _this3 = this; if (!camera || !(camera instanceof Camera)) return; _classPrivateFieldSet2(_camera, this, camera); var frustum; _classPrivateFieldSet2(_isInView, this, function (d) { if (!d.hullPnts) { // cached for next time to improve performance var lngLen = 360 / Math.pow(2, _this3.level); var lng = d.lng, lat = d.lat, latLen = d.latLen; var lng0 = lng - lngLen / 2; var lng1 = lng + lngLen / 2; var lat0 = lat - latLen / 2; var lat1 = lat + latLen / 2; d.hullPnts = [[lat, lng], [lat0, lng0], [lat1, lng0], [lat0, lng1], [lat1, lng1]].map(function (_ref2) { var _ref3 = _slicedToArray(_ref2, 2), lat = _ref3[0], lng = _ref3[1]; return polar2Cartesian(lat, lng, _classPrivateFieldGet2(_radius, _this3)); }).map(function (_ref4) { var x = _ref4.x, y = _ref4.y, z = _ref4.z; return new Vector3(x, y, z); }); } if (!frustum) { frustum = new Frustum(); camera.updateMatrix(); camera.updateMatrixWorld(); frustum.setFromProjectionMatrix(new Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)); } return d.hullPnts.some(function (pos) { return frustum.containsPoint(pos.clone().applyMatrix4(_this3.matrixWorld)); }); }); if (this.tileUrl) { var pov = camera.position.clone(); var distToGlobeCenter = pov.distanceTo(this.getWorldPosition(new Vector3())); var cameraDistance = (distToGlobeCenter - _classPrivateFieldGet2(_radius, this)) / _classPrivateFieldGet2(_radius, this); // in units of globe radius var idx = this.thresholds.findIndex(function (t) { return t && t <= cameraDistance; }); this.level = Math.min(this.maxLevel, Math.max(this.minLevel, idx < 0 ? this.thresholds.length : idx)); _assertClassBrand(_ThreeSlippyMapGlobe_brand, this, _fetchNeededTiles).call(this); } } }]); }(Group); function _buildMetaLevel(level) { var _this4 = this; if (level > MAX_LEVEL_TO_BUILD_LOOKUP_OCTREE) { // Generate meta dynamically _classPrivateFieldGet2(_tilesMeta, this)[level] = []; return; } // Generate distance lookup octree var levelMeta = _classPrivateFieldGet2(_tilesMeta, this)[level] = genTilesCoords(level, _classPrivateFieldGet2(_isMercator, this)); levelMeta.forEach(function (d) { return d.centroid = polar2Cartesian(d.lat, d.lng, _classPrivateFieldGet2(_radius, _this4)); }); levelMeta.octree = octree().x(function (d) { return d.centroid.x; }).y(function (d) { return d.centroid.y; }).z(function (d) { return d.centroid.z; }).addAll(levelMeta); } function _fetchNeededTiles() { var _this5 = this; if (!this.tileUrl || this.level === undefined || !_classPrivateFieldGet2(_tilesMeta, this).hasOwnProperty(this.level)) return; // Safety if can't check in view tiles for higher levels if (!_classPrivateFieldGet2(_isInView, this) && this.level > MAX_LEVEL_TO_RENDER_ALL_TILES) return; var tiles = _classPrivateFieldGet2(_tilesMeta, this)[this.level]; if (_classPrivateFieldGet2(_camera, this)) { // Pre-select tiles close to the camera var povPos = this.worldToLocal(_classPrivateFieldGet2(_camera, this).position.clone()); if (tiles.octree) { var _tiles$octree; // Octree based on 3d positions is more accurate var _povPos = this.worldToLocal(_classPrivateFieldGet2(_camera, this).position.clone()); var searchRadius = (_povPos.length() - _classPrivateFieldGet2(_radius, this)) * TILE_SEARCH_RADIUS_CAMERA_DISTANCE; tiles = (_tiles$octree = tiles.octree).findAllWithinRadius.apply(_tiles$octree, _toConsumableArray(_povPos).concat([searchRadius])); } else { // tiles populated dynamically var povCoords = cartesian2Polar(povPos); var searchRadiusLat = (povCoords.r / _classPrivateFieldGet2(_radius, this) - 1) * TILE_SEARCH_RADIUS_SURFACE_DISTANCE; var searchRadiusLng = searchRadiusLat / Math.cos(deg2Rad(povCoords.lat)); // Distances in longitude degrees shrink towards the poles var lngRange = [povCoords.lng - searchRadiusLng, povCoords.lng + searchRadiusLng]; var latRange = [povCoords.lat + searchRadiusLat, povCoords.lat - searchRadiusLat]; var _findTileXY = findTileXY(this.level, _classPrivateFieldGet2(_isMercator, this), lngRange[0], latRange[0]), _findTileXY2 = _slicedToArray(_findTileXY, 2), x0 = _findTileXY2[0], y0 = _findTileXY2[1]; var _findTileXY3 = findTileXY(this.level, _classPrivateFieldGet2(_isMercator, this), lngRange[1], latRange[1]), _findTileXY4 = _slicedToArray(_findTileXY3, 2), x1 = _findTileXY4[0], y1 = _findTileXY4[1]; !tiles.record && (tiles.record = {}); // Index gen tiles by XY var r = tiles.record; if (!r.hasOwnProperty("".concat(Math.round((x0 + x1) / 2), "_").concat(Math.round((y0 + y1) / 2)))) { // gen all found tiles if middle one is not in record tiles = genTilesCoords(this.level, _classPrivateFieldGet2(_isMercator, this), x0, y0, x1, y1).map(function (d) { var k = "".concat(d.x, "_").concat(d.y); if (r.hasOwnProperty(k)) return r[k]; r[k] = d; tiles.push(d); return d; }); } else { // gen only those missing, one by one var selTiles = []; for (var x = x0; x <= x1; x++) { for (var y = y0; y <= y1; y++) { var k = "".concat(x, "_").concat(y); if (!r.hasOwnProperty(k)) { r[k] = genTilesCoords(this.level, _classPrivateFieldGet2(_isMercator, this), x, y, x, y)[0]; tiles.push(r[k]); } selTiles.push(r[k]); } } tiles = selTiles; } } } /* console.log({ level: this.level, totalObjs: this.children.length, tilesFound: tiles.length, tilesInView: tiles.filter(this.#isInView || (() => true)).length, levelTiles: this.#tilesMeta[this.level].length, fetched: this.#tilesMeta[this.level].filter(d => d.obj).length, loading: this.#tilesMeta[this.level].filter(d => d.loading).length, }); */ tiles.filter(function (d) { return !d.obj; }).filter(_classPrivateFieldGet2(_isInView, this) || function () { return true; }).forEach(function (d) { var x = d.x, y = d.y, lng = d.lng, lat = d.lat, latLen = d.latLen; var lngLen = 360 / Math.pow(2, _this5.level); if (!d.obj) { var width = lngLen * (1 - _this5.tileMargin); var height = latLen * (1 - _this5.tileMargin); var rotLng = deg2Rad(lng); var rotLat = deg2Rad(-lat); var tile = new Mesh(new SphereGeometry(_classPrivateFieldGet2(_radius, _this5), Math.ceil(width / _this5.curvatureResolution), Math.ceil(height / _this5.curvatureResolution), deg2Rad(90 - width / 2) + rotLng, deg2Rad(width), deg2Rad(90 - height / 2) + rotLat, deg2Rad(height)), new MeshLambertMaterial()); if (_classPrivateFieldGet2(_isMercator, _this5)) { var _map = [lat + latLen / 2, lat - latLen / 2].map(function (lat) { return 0.5 - lat / 180; }), _map2 = _slicedToArray(_map, 2), _y = _map2[0], _y2 = _map2[1]; convertMercatorUV(tile.geometry.attributes.uv, _y, _y2); } d.obj = tile; } if (!d.loading) { d.loading = true; // Fetch tile image new TextureLoader().load(_this5.tileUrl(x, y, _this5.level), function (texture) { var tile = d.obj; if (tile) { texture.colorSpace = SRGBColorSpace; tile.material.map = texture; tile.material.color = null; tile.material.needsUpdate = true; _this5.add(tile); } d.loading = false; }); } }); } export { ThreeSlippyMapGlobe as default };