UNPKG

photo-sphere-viewer

Version:

A JavaScript library to display Photo Sphere panoramas

473 lines (395 loc) 14 kB
/*! * Photo Sphere Viewer 4.8.1 * @copyright 2014-2015 Jérémy Heleine * @copyright 2015-2022 Damien "Mistic" Sorel * @licence MIT (https://opensource.org/licenses/MIT) */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('three'), require('photo-sphere-viewer')) : typeof define === 'function' && define.amd ? define(['exports', 'three', 'photo-sphere-viewer'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.PhotoSphereViewer = global.PhotoSphereViewer || {}, global.PhotoSphereViewer.CubemapVideoAdapter = {}), global.THREE, global.PhotoSphereViewer)); })(this, (function (exports, three, photoSphereViewer) { 'use strict'; function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } /** * @typedef {Object} PSV.adapters.AbstractVideoAdapter.Video * @summary Object defining a video * @property {string} source */ /** * @typedef {Object} PSV.adapters.AbstractVideoAdapter.Options * @property {boolean} [autoplay=false] - automatically start the video * @property {boolean} [muted=autoplay] - initially mute the video */ /** * @summary Base video adapters class * @memberof PSV.adapters * @abstract * @private */ var AbstractVideoAdapter = /*#__PURE__*/function (_AbstractAdapter) { _inheritsLoose(AbstractVideoAdapter, _AbstractAdapter); function AbstractVideoAdapter(psv, options) { var _options$autoplay; var _this; _this = _AbstractAdapter.call(this, psv) || this; /** * @member {PSV.adapters.AbstractVideoAdapter.Options} * @private */ _this.config = _extends({ autoplay: false, muted: (_options$autoplay = options == null ? void 0 : options.autoplay) != null ? _options$autoplay : false }, options); /** * @member {HTMLVideoElement} * @private */ _this.video = null; _this.psv.on(photoSphereViewer.CONSTANTS.EVENTS.BEFORE_RENDER, _assertThisInitialized(_this)); return _this; } /** * @override */ var _proto = AbstractVideoAdapter.prototype; _proto.destroy = function destroy() { this.psv.off(photoSphereViewer.CONSTANTS.EVENTS.BEFORE_RENDER, this); this.__removeVideo(); _AbstractAdapter.prototype.destroy.call(this); } /** * @private */ ; _proto.handleEvent = function handleEvent(e) { /* eslint-disable */ switch (e.type) { case photoSphereViewer.CONSTANTS.EVENTS.BEFORE_RENDER: if (this.video) { this.psv.needsUpdate(); } break; } /* eslint-enable */ } /** * @override * @param {PSV.adapters.AbstractVideoAdapter.Video} panorama * @returns {Promise.<PSV.TextureData>} */ ; _proto.loadTexture = function loadTexture(panorama) { if (typeof panorama !== 'object' || !panorama.source) { return Promise.reject(new photoSphereViewer.PSVError('Invalid panorama configuration, are you using the right adapter?')); } if (!this.psv.getPlugin('video')) { return Promise.reject(new photoSphereViewer.PSVError('Video adapters require VideoPlugin to be loaded too.')); } var video = this.__createVideo(panorama.source); return this.__videoLoadPromise(video).then(function () { var texture = new three.VideoTexture(video); return { panorama: panorama, texture: texture }; }); } /** * @override */ ; _proto.__switchVideo = function __switchVideo(texture) { var currentTime; var duration; var paused = !this.config.autoplay; var muted = this.config.muted; var volume = 1; if (this.video) { var _this$video = this.video; currentTime = _this$video.currentTime; duration = _this$video.duration; paused = _this$video.paused; muted = _this$video.muted; volume = _this$video.volume; } this.__removeVideo(); this.video = texture.image; // keep current time when switching resolution if (this.video.duration === duration) { this.video.currentTime = currentTime; } // keep volume this.video.muted = muted; this.video.volume = volume; // play if (!paused) { this.video.play(); } } /** * @override */ ; _proto.disposeTexture = function disposeTexture(textureData) { var _textureData$texture; if (textureData.texture) { var video = textureData.texture.image; video.pause(); this.psv.container.removeChild(video); } (_textureData$texture = textureData.texture) == null ? void 0 : _textureData$texture.dispose(); } /** * @summary Removes the current video element * @private */ ; _proto.__removeVideo = function __removeVideo() { if (this.video) { this.video.pause(); this.psv.container.removeChild(this.video); delete this.video; } } /** * @summary Creates a new video element * @memberOf PSV.adapters * @param {string} src * @return {HTMLVideoElement} * @private */ ; _proto.__createVideo = function __createVideo(src) { var video = document.createElement('video'); video.crossOrigin = this.psv.config.withCredentials ? 'use-credentials' : 'anonymous'; video.loop = true; video.playsInline = true; video.style.display = 'none'; video.muted = this.config.muted; video.src = src; video.preload = 'metadata'; this.psv.container.appendChild(video); return video; } /** * @private */ ; _proto.__videoLoadPromise = function __videoLoadPromise(video) { var self = this; return new Promise(function (resolve, reject) { video.addEventListener('loadedmetadata', function onLoaded() { if (this.video && video.duration === this.video.duration) { resolve(self.__videoBufferPromise(video, this.video.currentTime)); } else { resolve(); } video.removeEventListener('loadedmetadata', onLoaded); }); video.addEventListener('error', function onError(err) { reject(err); video.removeEventListener('error', onError); }); }); } /** * @private */ ; _proto.__videoBufferPromise = function __videoBufferPromise(video, currentTime) { return new Promise(function (resolve) { function onBuffer() { var buffer = video.buffered; for (var i = 0, l = buffer.length; i < l; i++) { if (buffer.start(i) <= video.currentTime && buffer.end(i) >= video.currentTime) { video.pause(); video.removeEventListener('buffer', onBuffer); video.removeEventListener('progress', onBuffer); resolve(); break; } } } // try to reduce the switching time by preloading in advance // FIXME find a better way ? video.currentTime = Math.min(currentTime + 2000, video.duration.currentTime); video.muted = true; video.addEventListener('buffer', onBuffer); video.addEventListener('progress', onBuffer); video.play(); }); }; return AbstractVideoAdapter; }(photoSphereViewer.AbstractAdapter); /** * @typedef {Object} PSV.adapters.CubemapVideoAdapter.Video * @summary Object defining a video * @property {string} source */ /** * @typedef {Object} PSV.adapters.CubemapVideoAdapter.Options * @property {boolean} [autoplay=false] - automatically start the video * @property {boolean} [muted=autoplay] - initially mute the video * @property {number} [equiangular=true] - if the video is an equiangular cubemap (EAC) */ /** * @summary Adapter for cubemap videos * @memberof PSV.adapters * @extends PSV.adapters.AbstractAdapter */ var CubemapVideoAdapter = /*#__PURE__*/function (_AbstractVideoAdapter) { _inheritsLoose(CubemapVideoAdapter, _AbstractVideoAdapter); /** * @param {PSV.Viewer} psv * @param {PSV.adapters.CubemapVideoAdapter.Options} options */ function CubemapVideoAdapter(psv, options) { return _AbstractVideoAdapter.call(this, psv, _extends({ equiangular: true }, options)) || this; } /** * @override * @param {PSV.adapters.CubemapVideoAdapter.Video} panorama * @returns {Promise.<PSV.TextureData>} */ var _proto = CubemapVideoAdapter.prototype; _proto.loadTexture = function loadTexture(panorama) { return _AbstractVideoAdapter.prototype.loadTexture.call(this, panorama); } /** * @override */ ; _proto.createMesh = function createMesh(scale) { if (scale === void 0) { scale = 1; } var cubeSize = photoSphereViewer.CONSTANTS.SPHERE_RADIUS * 2 * scale; var geometry = new three.BoxGeometry(cubeSize, cubeSize, cubeSize).scale(1, 1, -1).toNonIndexed(); geometry.clearGroups(); var uvs = geometry.getAttribute('uv'); /* Structure of a frame 1 +---------+---------+---------+ | | | | | Left | Front | Right | | | | | 1/2 +---------+---------+---------+ | | | | | Bottom | Back | Top | | | | | 0 +---------+---------+---------+ 0 1/3 2/3 1 Bottom, Back and Top are rotated 90° clockwise */ // columns var a = 0; var b = 1 / 3; var c = 2 / 3; var d = 1; // lines var A = 1; var B = 1 / 2; var C = 0; // left uvs.setXY(0, a, A); uvs.setXY(1, a, B); uvs.setXY(2, b, A); uvs.setXY(3, a, B); uvs.setXY(4, b, B); uvs.setXY(5, b, A); // right uvs.setXY(6, c, A); uvs.setXY(7, c, B); uvs.setXY(8, d, A); uvs.setXY(9, c, B); uvs.setXY(10, d, B); uvs.setXY(11, d, A); // top uvs.setXY(12, d, B); uvs.setXY(13, c, B); uvs.setXY(14, d, C); uvs.setXY(15, c, B); uvs.setXY(16, c, C); uvs.setXY(17, d, C); // bottom uvs.setXY(18, b, B); uvs.setXY(19, a, B); uvs.setXY(20, b, C); uvs.setXY(21, a, B); uvs.setXY(22, a, C); uvs.setXY(23, b, C); // back uvs.setXY(24, c, B); uvs.setXY(25, b, B); uvs.setXY(26, c, C); uvs.setXY(27, b, B); uvs.setXY(28, b, C); uvs.setXY(29, c, C); // front uvs.setXY(30, b, A); uvs.setXY(31, b, B); uvs.setXY(32, c, A); uvs.setXY(33, b, B); uvs.setXY(34, c, B); uvs.setXY(35, c, A); // shamelessly copied from https://github.com/videojs/videojs-vr var material = new three.ShaderMaterial({ uniforms: { mapped: { value: null }, contCorrect: { value: 1 }, faceWH: { value: new three.Vector2(1 / 3, 1 / 2) }, vidWH: { value: new three.Vector2(1, 1) } }, vertexShader: "\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);\n}", fragmentShader: "\nvarying vec2 vUv;\nuniform sampler2D mapped;\nuniform vec2 faceWH;\nuniform vec2 vidWH;\nuniform float contCorrect;\n\nconst float PI = 3.1415926535897932384626433832795;\n\nvoid main() {\n vec2 corner = vUv - mod(vUv, faceWH) + vec2(0, contCorrect / vidWH.y);\n vec2 faceWHadj = faceWH - vec2(0, contCorrect * 2. / vidWH.y);\n vec2 p = (vUv - corner) / faceWHadj - .5;\n vec2 q = " + (this.config.equiangular ? '2. / PI * atan(2. * p) + .5' : 'p + .5') + ";\n vec2 eUv = corner + q * faceWHadj;\n gl_FragColor = texture2D(mapped, eUv);\n}" }); return new three.Mesh(geometry, material); } /** * @override */ ; _proto.setTexture = function setTexture(mesh, textureData) { var _mesh$material$unifor; var texture = textureData.texture; (_mesh$material$unifor = mesh.material.uniforms.mapped.value) == null ? void 0 : _mesh$material$unifor.dispose(); mesh.material.uniforms.mapped.value = texture; mesh.material.uniforms.vidWH.value.set(texture.image.videoWidth, texture.image.videoHeight); this.__switchVideo(textureData.texture); }; return CubemapVideoAdapter; }(AbstractVideoAdapter); CubemapVideoAdapter.id = 'cubemap-video'; exports.CubemapVideoAdapter = CubemapVideoAdapter; Object.defineProperty(exports, '__esModule', { value: true }); })); //# sourceMappingURL=cubemap-video.js.map