UNPKG

itowns

Version:

A JS/WebGL framework for 3D geospatial data visualization

325 lines (267 loc) 12.5 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var THREE = _interopRequireWildcard(require("three")); var _GeometryLayer2 = _interopRequireDefault(require("./GeometryLayer")); var _OrientedImageMaterial = _interopRequireDefault(require("../Renderer/OrientedImageMaterial")); var _GeoJsonParser = _interopRequireDefault(require("../Parser/GeoJsonParser")); var _CameraCalibrationParser = _interopRequireDefault(require("../Parser/CameraCalibrationParser")); var _Coordinates = _interopRequireDefault(require("../Core/Geographic/Coordinates")); var _OrientationUtils = _interopRequireDefault(require("../Utils/OrientationUtils")); var coord = new _Coordinates["default"]('EPSG:4978', 0, 0, 0); var commandCancellation = function (cmd) { return cmd.requester.id !== cmd.layer.currentPano.id; }; function updatePano(context, camera, layer) { var newPano = layer.mostNearPano(camera.position); // detection of oriented image change var currentId = layer.currentPano ? layer.currentPano.id : undefined; if (newPano && currentId != newPano.id) { layer.currentPano = newPano; // callback to indicate current pano has changed layer.onPanoChanged({ previousPanoPosition: layer.getPreviousPano() ? layer.getPreviousPano().position : undefined, currentPanoPosition: layer.getCurrentPano().position, nextPanoPosition: layer.getNextPano().position }); // prepare informations about the needed textures var panoCameras = newPano.geometry[0].properties.idSensors; var imagesInfo = layer.cameras.map(function (cam) { return { cameraId: cam.name, panoId: newPano.id, as: function as() {}, toString: function toString() { var separator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; return "".concat(cam.name).concat(separator).concat(newPano.id); } }; }).filter(function (info) { return !panoCameras || panoCameras.includes(info.cameraId); }); var command = { layer: layer, // put informations about image URL as extent to be used by generic DataSourceProvider, OrientedImageSource will use that. extentsSource: imagesInfo, view: context.view, threejsLayer: layer.threejsLayer, requester: newPano, earlyDropFunction: commandCancellation }; // async call to scheduler to get textures context.scheduler.execute(command).then(function (textures) { if (newPano.id === layer.currentPano.id) { layer.material.setTextures(textures, newPano, layer.getCamerasNameFromFeature(newPano)); layer.material.updateUniforms(context.camera.camera3D); context.view.notifyChange(layer, true); } }, function () {}); } } function updateBackground(layer) { if (layer.background && layer.currentPano) { layer.background.position.copy(layer.currentPano.position); layer.background.updateMatrixWorld(); layer.background.material = layer.material || layer.background.material; } } function createBackground(radius) { if (!radius || radius <= 0) { return undefined; } var geometry = new THREE.SphereGeometry(radius, 32, 32); var material = new THREE.MeshPhongMaterial({ color: 0x7777ff, side: THREE.DoubleSide, transparent: true, opacity: 0.5, wireframe: true }); var sphere = new THREE.Mesh(geometry, material); sphere.visible = true; sphere.name = 'OrientedImageBackground'; return sphere; } /** * @classdesc OrientedImageLayer loads oriented images, and project these textures on the scene. * It is design to create an immersive view. </br> * It loads a set of panoramic position and orientation, * a set of camera calibration file (it's the same set of camera for each panoramic), * and a set of texture (each image for each camera for each panoramic), all organised in an {@link OrientedImageSource}. </br> * It creates an {@link OrientedImageMaterial} used to do projective texture mapping on the scene. * @extends GeometryLayer */ var OrientedImageLayer = /*#__PURE__*/ function (_GeometryLayer) { (0, _inherits2["default"])(OrientedImageLayer, _GeometryLayer); /** * @constructor * @param { string } id - The id of the layer, a unique name. * @param { Object } config - configuration of the layer * @param { number } config.backgroundDistance - Radius in meter of the sphere used as a background * @param { function } config.onPanoChanged - callback fired when current panoramic changes * @param { string } config.projection - projection of the view * @param { string } config.orientation - Json object, using GeoJSon format to represent points, * it's a set of panoramic position and orientation. * @param { string } config.calibrations - Json object, representing a set of camera. see [CameraCalibrationParser]{@link module:CameraCalibrationParser} * @param { OrientedImageSource } config.source - Source used to build url of texture for each oriented image, * a tecture is need for each camera, for each panoramic. */ function OrientedImageLayer(id) { var _this; var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; (0, _classCallCheck2["default"])(this, OrientedImageLayer); _this = (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(OrientedImageLayer).call(this, id, new THREE.Group(), config)); _this.background = config.background || createBackground(config.backgroundDistance); _this.isOrientedImageLayer = true; if (_this.background) { _this.object3d.add(_this.background); } // currentPano is the current point, means it's the closest from the camera _this.currentPano = undefined; // store a callback to fire event when current panoramic change _this.onPanoChanged = config.onPanoChanged || function () {}; // function to get cameras name from panoramic feature _this.getCamerasNameFromFeature = config.getCamerasNameFromFeature || function () {}; // panos is an array of feature point, representing many panoramics. // for each point, there is a position and a quaternion attribute. var p1 = _GeoJsonParser["default"].parse(config.orientation, { mergeFeatures: false, crsOut: config.projection }).then(function (res) { _this.panos = res.features; var crsIn = res.optionsFeature.crsIn; var crsOut = config.projection; var crs2crs = _OrientationUtils["default"].quaternionFromCRSToCRS(crsIn, crsOut); var quat = new THREE.Quaternion(); // add position and quaternion attributes from point feature var i = 0; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = _this.panos[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var pano = _step.value; // set position coord.crs = pano.crs; coord.setFromArray(pano.vertices); pano.position = coord.toVector3(); // set quaternion crs2crs(coord, quat); pano.quaternion = _OrientationUtils["default"].quaternionFromAttitude(pano.geometry[0].properties).premultiply(quat); // TODO clean DataSourceProvider, so that we don't have this hack to do pano.material = {}; pano.id = pano.geometry[0].properties.id; pano.index = i++; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator["return"] != null) { _iterator["return"](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } }); // array of cameras, represent the projective texture configuration for each panoramic. var p2 = _CameraCalibrationParser["default"].parse(config.calibration, config).then(function (c) { _this.cameras = c; // create the material _this.material = new _OrientedImageMaterial["default"](_this.cameras, config); }); // wait for the twos promises to tell layer is ready. _this.whenReady = Promise.all([p1, p2]).then(function () { _this.ready = true; return (0, _assertThisInitialized2["default"])(_this); }); return _this; } // eslint-disable-next-line (0, _createClass2["default"])(OrientedImageLayer, [{ key: "update", value: function update() {} }, { key: "preUpdate", value: function preUpdate(context) { updatePano(context, context.camera.camera3D, this); this.material.updateUniforms(context.camera.camera3D); updateBackground(this); } }, { key: "getNextPano", value: function getNextPano() { var index = (this.currentPano.index + 1) % this.panos.length; return this.panos[index]; } }, { key: "getCurrentPano", value: function getCurrentPano() { return this.currentPano; } }, { key: "getPreviousPano", value: function getPreviousPano() { var index = (this.currentPano.index - 1) % this.panos.length; return this.panos[index]; } /** * Delete background, but doesn't delete OrientedImageLayer.material. For the moment, this material visibility is set to false. * You need to replace OrientedImageLayer.material applied on each object, if you want to continue displaying them. * This issue (see #1018 {@link https://github.com/iTowns/itowns/issues/1018}) will be fixed when OrientedImageLayer will be a ColorLayer. */ }, { key: "delete", value: function _delete() { (0, _get2["default"])((0, _getPrototypeOf2["default"])(OrientedImageLayer.prototype), "delete", this).call(this); this.material.visible = false; console.warn('You need to replace OrientedImageLayer.material applied on each object. This issue will be fixed when OrientedImageLayer will be a ColorLayer. the material visibility is set to false. To follow issue see https://github.com/iTowns/itowns/issues/1018'); } }, { key: "mostNearPano", value: function mostNearPano(position) { var minDistance = Infinity; var nearPano; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = this.panos[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var pano = _step2.value; var distance = position.distanceTo(pano.position); if (distance < minDistance) { minDistance = distance; nearPano = pano; } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) { _iterator2["return"](); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return nearPano; } }, { key: "boostLight", set: function set(value) { this.material.uniforms.boostLight.value = value; }, get: function get() { return this.material.uniforms.boostLight.value; } }]); return OrientedImageLayer; }(_GeometryLayer2["default"]); var _default = OrientedImageLayer; exports["default"] = _default;