From 8b5e36e9b008fd058aab568bfa9cc9ae89dd49da Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Wed, 3 Oct 2018 20:23:53 -0400 Subject: [PATCH 01/19] When setting view via API, reset timer for auto rotate (fixes #664). --- src/js/pannellum.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index f233249..895a9da 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -2399,6 +2399,7 @@ this.setPitch = function(pitch, animated, callback, callbackArgs) { } else { config.pitch = pitch; } + latestInteraction = Date.now(); animateInit(); return this; }; @@ -2467,6 +2468,7 @@ this.setYaw = function(yaw, animated, callback, callbackArgs) { } else { config.yaw = yaw; } + latestInteraction = Date.now(); animateInit(); return this; }; @@ -2528,6 +2530,7 @@ this.setHfov = function(hfov, animated, callback, callbackArgs) { } else { setHfov(hfov); } + latestInteraction = Date.now(); animateInit(); return this; }; From a40b906ebdbc6aec89041658f206d5b8a0228917 Mon Sep 17 00:00:00 2001 From: Will Calderbank Date: Thu, 27 Sep 2018 15:10:44 +0100 Subject: [PATCH 02/19] Add animateFinished event fired on animations finishing --- src/js/pannellum.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index 895a9da..08cd810 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -1372,6 +1372,7 @@ function animate() { } else if (renderer && (renderer.isLoading() || (config.dynamic === true && update))) { requestAnimationFrame(animate); } else { + fireEvent('animateFinished', {pitch: _this.getPitch(), yaw: _this.getYaw(), hfov: _this.getHfov()}); animating = false; prevTime = undefined; var autoRotateStartTime = config.autoRotateInactivityDelay - From 6d8a19991d6a8bcd33656fafa9bd7a1448423c58 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Wed, 3 Oct 2018 21:00:30 -0400 Subject: [PATCH 03/19] Add documentation for new `animatefinished` event. --- doc/events.md | 6 ++++++ src/js/pannellum.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/events.md b/doc/events.md index 6b70488..940c02a 100644 --- a/doc/events.md +++ b/doc/events.md @@ -27,6 +27,12 @@ If a scene transition fade interval is specified, this event is fired when the fading is completed after changing scenes. +## `animatefinished` + +Fired when any movements / animations finish, i.e. when the renderer stops +rendering new frames. Passes final pitch, yaw, and HFOV values to handler. + + ## `error` Fired when an error occured. The error message string is passed to the diff --git a/src/js/pannellum.js b/src/js/pannellum.js index 08cd810..52918e3 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -1372,7 +1372,7 @@ function animate() { } else if (renderer && (renderer.isLoading() || (config.dynamic === true && update))) { requestAnimationFrame(animate); } else { - fireEvent('animateFinished', {pitch: _this.getPitch(), yaw: _this.getYaw(), hfov: _this.getHfov()}); + fireEvent('animatefinished', {pitch: _this.getPitch(), yaw: _this.getYaw(), hfov: _this.getHfov()}); animating = false; prevTime = undefined; var autoRotateStartTime = config.autoRotateInactivityDelay - From 3151cafb1c0c6d9ab997e660e3b36ab5950de7cb Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Tue, 9 Oct 2018 10:38:03 -0400 Subject: [PATCH 04/19] Split equirectangular images into two textures if too big. This allows for equirectangular images on a given device to be twice as big as they were previously, so (almost) all devices now support equirectangular images up to 8192px across. This technique could be extended to double the maximum size again (using eight textures), but images bigger than 8192px should be converted to the multi-resolution format anyway, so there's little point in implementing this extension. --- src/js/libpannellum.js | 77 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/src/js/libpannellum.js b/src/js/libpannellum.js index 662fb3c..128073a 100644 --- a/src/js/libpannellum.js +++ b/src/js/libpannellum.js @@ -294,18 +294,20 @@ function Renderer(container) { } // Make sure image isn't too big - var width = 0, maxWidth = 0; + var maxWidth = 0; if (imageType == 'equirectangular') { - width = Math.max(image.width, image.height); maxWidth = gl.getParameter(gl.MAX_TEXTURE_SIZE); + if (Math.max(image.width / 2, image.height) > maxWidth) { + console.log('Error: The image is too big; it\'s ' + image.width + 'px wide, '+ + 'but this device\'s maximum supported size is ' + (maxWidth * 2) + 'px.'); + throw {type: 'webgl size error', width: image.width, maxWidth: maxWidth * 2}; + } } else if (imageType == 'cubemap') { - width = cubeImgWidth; - maxWidth = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); - } - if (width > maxWidth) { - console.log('Error: The image is too big; it\'s ' + width + 'px wide, '+ - 'but this device\'s maximum supported size is ' + maxWidth + 'px.'); - throw {type: 'webgl size error', width: width, maxWidth: maxWidth}; + if (cubeImgWidth > gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE)) { + console.log('Error: The image is too big; it\'s ' + width + 'px wide, '+ + 'but this device\'s maximum supported size is ' + maxWidth + 'px.'); + throw {type: 'webgl size error', width: width, maxWidth: maxWidth}; + } } // Store horizon pitch and roll if applicable @@ -414,8 +416,44 @@ function Renderer(container) { gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image[0]); gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image[2]); } else { - // Upload image to the texture - gl.texImage2D(glBindType, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); + if (image.width <= maxWidth) { + gl.uniform1i(gl.getUniformLocation(program, 'u_splitImage'), 0); + // Upload image to the texture + gl.texImage2D(glBindType, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); + } else { + // Image needs to be split into two parts due to texture size limits + gl.uniform1i(gl.getUniformLocation(program, 'u_splitImage'), 1); + + // Draw image on canvas + var cropCanvas = document.createElement('canvas'); + cropCanvas.width = image.width; + cropCanvas.height = image.height; + var cropContext = cropCanvas.getContext('2d'); + cropContext.drawImage(image, 0, 0); + + // Upload first half of image to the texture + var cropImage = cropContext.getImageData(0, 0, image.width / 2, image.height); + gl.texImage2D(glBindType, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, cropImage); + + // Create and bind texture for second half of image + program.texture2 = gl.createTexture(); + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(glBindType, program.texture2); + gl.uniform1i(gl.getUniformLocation(program, 'u_image1'), 1); + + // Upload second half of image to the texture + cropImage = cropContext.getImageData(image.width / 2, 0, image.width / 2, image.height); + gl.texImage2D(glBindType, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, cropImage); + + // Set parameters for rendering any size + gl.texParameteri(glBindType, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(glBindType, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(glBindType, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(glBindType, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + + // Reactive first texture unit + gl.activeTexture(gl.TEXTURE0); + } } // Set parameters for rendering any size @@ -1320,7 +1358,9 @@ var fragEquiCubeBase = [ 'const float PI = 3.14159265358979323846264;', // Texture -'uniform sampler2D u_image;', +'uniform sampler2D u_image0;', +'uniform sampler2D u_image1;', +'uniform bool u_splitImage;', 'uniform samplerCube u_imageCube;', // Coordinates passed in from vertex shader @@ -1365,8 +1405,17 @@ var fragEquirectangular = fragEquiCubeBase + [ // Map from [-1,1] to [0,1] and flip y-axis 'if(coord.x < -u_h || coord.x > u_h || coord.y < -u_v + u_vo || coord.y > u_v + u_vo)', 'gl_FragColor = u_backgroundColor;', - 'else', - 'gl_FragColor = texture2D(u_image, vec2((coord.x + u_h) / (u_h * 2.0), (-coord.y + u_v + u_vo) / (u_v * 2.0)));', + 'else {', + 'if(u_splitImage) {', + // Image was split into two textures to work around texture size limits + 'if(coord.x < 0.0)', + 'gl_FragColor = texture2D(u_image0, vec2((coord.x + u_h) / u_h, (-coord.y + u_v + u_vo) / (u_v * 2.0)));', + 'else', + 'gl_FragColor = texture2D(u_image1, vec2((coord.x + u_h) / u_h - 1.0, (-coord.y + u_v + u_vo) / (u_v * 2.0)));', + '} else {', + 'gl_FragColor = texture2D(u_image0, vec2((coord.x + u_h) / (u_h * 2.0), (-coord.y + u_v + u_vo) / (u_v * 2.0)));', + '}', + '}', '}' ].join('\n'); From d12baa08d4f15f4f09dd2d78b193b8f47a9697d2 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Wed, 10 Oct 2018 12:23:37 -0400 Subject: [PATCH 05/19] Fix mouse position for iOS (fixes #668). --- src/js/pannellum.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index 52918e3..c9958c8 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -658,8 +658,9 @@ function aboutMessage(event) { function mousePosition(event) { var bounds = container.getBoundingClientRect(); var pos = {}; - pos.x = event.clientX - bounds.left; - pos.y = event.clientY - bounds.top; + // pageX / pageY needed for iOS + pos.x = (event.clientX || event.pageX) - bounds.left; + pos.y = (event.clientY || event.pageY) - bounds.top; return pos; } From 5c36101744431e87c5ad548cb63ef7dd89690610 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Wed, 10 Oct 2018 18:10:30 -0400 Subject: [PATCH 06/19] Restructure how animated move callbacks are handled to fix possible race condition (see #658). --- src/js/pannellum.js | 54 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index c9958c8..f1e669b 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -67,6 +67,7 @@ var config, externalEventListeners = {}, specifiedPhotoSphereExcludes = [], update = false, // Should we update when still to render dynamic content + eps = 1e-6, hotspotsCreated = false; var defaultConfig = { @@ -1301,11 +1302,7 @@ function animateMove(axis) { t.endPosition === t.startPosition) { result = t.endPosition; speed[axis] = 0; - var callback = animatedMove[axis].callback, - callbackArgs = animatedMove[axis].callbackArgs; delete animatedMove[axis]; - if (typeof callback == 'function') - callback(callbackArgs); } config[axis] = result; } @@ -2388,20 +2385,25 @@ this.getPitch = function() { * @returns {Viewer} `this` */ this.setPitch = function(pitch, animated, callback, callbackArgs) { + latestInteraction = Date.now(); + if (Math.abs(pitch - config.pitch) <= eps) { + if (typeof callback == 'function') + callback(callbackArgs); + return this; + } animated = animated == undefined ? 1000: Number(animated); if (animated) { animatedMove.pitch = { 'startTime': Date.now(), 'startPosition': config.pitch, 'endPosition': pitch, - 'duration': animated, - 'callback': callback, - 'callbackArgs': callbackArgs + 'duration': animated } + if (typeof callback == 'function') + setTimeout(function(){callback(callbackArgs)}, animated); } else { config.pitch = pitch; } - latestInteraction = Date.now(); animateInit(); return this; }; @@ -2450,6 +2452,12 @@ this.getYaw = function() { * @returns {Viewer} `this` */ this.setYaw = function(yaw, animated, callback, callbackArgs) { + latestInteraction = Date.now(); + if (Math.abs(yaw - config.yaw) <= eps) { + if (typeof callback == 'function') + callback(callbackArgs); + return this; + } animated = animated == undefined ? 1000: Number(animated); yaw = ((yaw + 180) % 360) - 180 // Keep in bounds if (animated) { @@ -2463,14 +2471,13 @@ this.setYaw = function(yaw, animated, callback, callbackArgs) { 'startTime': Date.now(), 'startPosition': config.yaw, 'endPosition': yaw, - 'duration': animated, - 'callback': callback, - 'callbackArgs': callbackArgs + 'duration': animated } + if (typeof callback == 'function') + setTimeout(function(){callback(callbackArgs)}, animated); } else { config.yaw = yaw; } - latestInteraction = Date.now(); animateInit(); return this; }; @@ -2519,20 +2526,25 @@ this.getHfov = function() { * @returns {Viewer} `this` */ this.setHfov = function(hfov, animated, callback, callbackArgs) { + latestInteraction = Date.now(); + if (Math.abs(hfov - config.hfov) <= eps) { + if (typeof callback == 'function') + callback(callbackArgs); + return this; + } animated = animated == undefined ? 1000: Number(animated); if (animated) { animatedMove.hfov = { 'startTime': Date.now(), 'startPosition': config.hfov, 'endPosition': constrainHfov(hfov), - 'duration': animated, - 'callback': callback, - 'callbackArgs': callbackArgs + 'duration': animated } + if (typeof callback == 'function') + setTimeout(function(){callback(callbackArgs)}, animated); } else { setHfov(hfov); } - latestInteraction = Date.now(); animateInit(); return this; }; @@ -2575,16 +2587,20 @@ this.setHfovBounds = function(bounds) { */ this.lookAt = function(pitch, yaw, hfov, animated, callback, callbackArgs) { animated = animated == undefined ? 1000: Number(animated); - if (pitch !== undefined) { + if (pitch !== undefined && Math.abs(pitch - config.pitch) > eps) { this.setPitch(pitch, animated, callback, callbackArgs); callback = undefined; } - if (yaw !== undefined) { + if (yaw !== undefined && Math.abs(yaw - config.yaw) > eps) { this.setYaw(yaw, animated, callback, callbackArgs); callback = undefined; } - if (hfov !== undefined) + if (hfov !== undefined && Math.abs(hfov - config.hfov) > eps) { this.setHfov(hfov, animated, callback, callbackArgs); + callback = undefined; + } + if (typeof callback == 'function') + callback(callbackArgs); return this; } From 2d7ccb5fada646cf1ba07dfb446c3066f28bb05f Mon Sep 17 00:00:00 2001 From: Will Calderbank Date: Thu, 18 Oct 2018 16:54:09 +0100 Subject: [PATCH 07/19] Allow friction value to be set in options --- src/js/pannellum.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index f1e669b..f040a6e 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -108,6 +108,7 @@ var defaultConfig = { crossOrigin: 'anonymous', touchPanSpeedCoeffFactor: 1, capturedKeyNumbers: [16, 17, 27, 37, 38, 39, 40, 61, 65, 68, 83, 87, 107, 109, 173, 187, 189], + friction: 0.15 }; // Translatable / configurable strings @@ -1247,19 +1248,19 @@ function keyRepeat() { // "Inertia" if (diff > 0 && !config.autoRotate) { // "Friction" - var friction = 0.85; - + var slowDownFactor = 1 - config.friction; + // Yaw if (!keysDown[4] && !keysDown[5] && !keysDown[8] && !keysDown[9] && !animatedMove.yaw) { - config.yaw += speed.yaw * diff * friction; + config.yaw += speed.yaw * diff * slowDownFactor; } // Pitch if (!keysDown[2] && !keysDown[3] && !keysDown[6] && !keysDown[7] && !animatedMove.pitch) { - config.pitch += speed.pitch * diff * friction; + config.pitch += speed.pitch * diff * slowDownFactor; } // Zoom if (!keysDown[0] && !keysDown[1] && !animatedMove.hfov) { - setHfov(config.hfov + speed.hfov * diff * friction); + setHfov(config.hfov + speed.hfov * diff * slowDownFactor); } } From 42a7a3a08d1300906bbd432ada11b5294da0bf32 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Fri, 19 Oct 2018 10:37:37 -0300 Subject: [PATCH 08/19] Add documentation for new `friction` configuration parameter. --- doc/json-config-parameters.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/json-config-parameters.md b/doc/json-config-parameters.md index 7d86bfb..9cddd9d 100644 --- a/doc/json-config-parameters.md +++ b/doc/json-config-parameters.md @@ -98,6 +98,13 @@ viewer is fullscreen. If set to `false`, mouse and touch dragging is disabled. Defaults to `true`. +### `friction` (number) + +Controls the "friction" that slows down the viewer motion after it is dragged +and released. Higher values mean the motion stops faster. Should be set +(0.0, 1.0]; defaults to 0.15. + + ### `disableKeyboardCtrl` (boolean) If set to `true`, keyboard controls are disabled. Defaults to `false`. From a05721198901cf2554e560f5029184ee897b4fc1 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Tue, 13 Nov 2018 21:55:22 -0500 Subject: [PATCH 09/19] Allow switching to dynamic scenes (fixes #686). --- doc/json-config-parameters.md | 6 ++++++ src/js/pannellum.js | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/doc/json-config-parameters.md b/doc/json-config-parameters.md index 9cddd9d..7f872ea 100644 --- a/doc/json-config-parameters.md +++ b/doc/json-config-parameters.md @@ -446,6 +446,12 @@ Currently, only equirectangular dynamic content is supported. The panorama source is considered dynamic when this is set to `true`. Defaults to `false`. This should be set to `true` for video. +### `dynamicUpdate` (boolean) + +For dynamic content, viewer will start automatically updating when set to +`true`. Defaults to `false`. If the updates are controlled via the `setUpdate` +method, as with the Video.js plugin, this should be set to `false`. + ## Additional information for tour configuration files diff --git a/src/js/pannellum.js b/src/js/pannellum.js index f040a6e..077fa57 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -92,6 +92,7 @@ var defaultConfig = { northOffset: 0, showFullscreenCtrl: true, dynamic: false, + dynamicUpdate: false, doubleClickZoom: true, keyboardZoom: true, mouseZoom: true, @@ -2287,6 +2288,13 @@ function loadScene(sceneId, targetPitch, targetYaw, targetHfov, fadeDone) { } fireEvent('scenechange', sceneId); load(); + + // Properly handle switching to dynamic scenes + update = config.dynamicUpdate === true; + if (config.dynamic) { + panoImage = config.panorama; + onImageLoad(); + } } /** From 4242e7c53aec68780d298456f92ea88b02575b14 Mon Sep 17 00:00:00 2001 From: Will Calderbank Date: Wed, 5 Dec 2018 23:15:26 +0000 Subject: [PATCH 10/19] Add stopMovement to stop all movement (#690) Add `stopMovement` to stop all movement --- src/js/pannellum.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index 077fa57..51feb0d 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -2713,6 +2713,16 @@ this.stopAutoRotate = function() { }; /** + * Stops all movement. + * @memberof Viewer + * @instance + */ +this.stopMovement = function() { + stopAnimation(); + speed = {'yaw': 0, 'pitch': 0, 'hfov': 0}; +} + +/** * Returns the panorama renderer. * @memberof Viewer * @instance From f0af9d07bfe46200ebe4b1bf81eb5658a0653cf5 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Fri, 21 Dec 2018 16:23:31 -0500 Subject: [PATCH 11/19] Fix interaction between F11 and API fullscreens in Chrome (fixes #696). --- src/js/pannellum.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index 51feb0d..f5ec471 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -480,10 +480,10 @@ function onImageLoad() { if (config.doubleClickZoom) { dragFix.addEventListener('dblclick', onDocumentDoubleClick, false); } - uiContainer.addEventListener('mozfullscreenchange', onFullScreenChange, false); - uiContainer.addEventListener('webkitfullscreenchange', onFullScreenChange, false); - uiContainer.addEventListener('msfullscreenchange', onFullScreenChange, false); - uiContainer.addEventListener('fullscreenchange', onFullScreenChange, false); + container.addEventListener('mozfullscreenchange', onFullScreenChange, false); + container.addEventListener('webkitfullscreenchange', onFullScreenChange, false); + container.addEventListener('msfullscreenchange', onFullScreenChange, false); + container.addEventListener('fullscreenchange', onFullScreenChange, false); window.addEventListener('resize', onDocumentResize, false); window.addEventListener('orientationchange', onDocumentResize, false); if (!config.disableKeyboardCtrl) { From f05615045e976d069f972869f1988ff61f83fda0 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Fri, 21 Dec 2018 16:25:33 -0500 Subject: [PATCH 12/19] Fix event listener removal regression introduced in 64c337beff511a0eaa862179bc4cd919ba87e5ae. --- src/js/pannellum.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index f5ec471..149b10c 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -3053,17 +3053,17 @@ this.destroy = function() { dragFix.removeEventListener('dblclick', onDocumentDoubleClick, false); document.removeEventListener('mousemove', onDocumentMouseMove, false); document.removeEventListener('mouseup', onDocumentMouseUp, false); - container.removeEventListener('mousewheel', onDocumentMouseWheel, false); - container.removeEventListener('DOMMouseScroll', onDocumentMouseWheel, false); + uiContainer.removeEventListener('mousewheel', onDocumentMouseWheel, false); + uiContainer.removeEventListener('DOMMouseScroll', onDocumentMouseWheel, false); container.removeEventListener('mozfullscreenchange', onFullScreenChange, false); container.removeEventListener('webkitfullscreenchange', onFullScreenChange, false); container.removeEventListener('msfullscreenchange', onFullScreenChange, false); container.removeEventListener('fullscreenchange', onFullScreenChange, false); window.removeEventListener('resize', onDocumentResize, false); window.removeEventListener('orientationchange', onDocumentResize, false); - container.removeEventListener('keydown', onDocumentKeyPress, false); - container.removeEventListener('keyup', onDocumentKeyUp, false); - container.removeEventListener('blur', clearKeys, false); + uiContainer.removeEventListener('keydown', onDocumentKeyPress, false); + uiContainer.removeEventListener('keyup', onDocumentKeyUp, false); + uiContainer.removeEventListener('blur', clearKeys, false); document.removeEventListener('mouseleave', onDocumentMouseUp, false); dragFix.removeEventListener('touchstart', onDocumentTouchStart, false); dragFix.removeEventListener('touchmove', onDocumentTouchMove, false); From bcebff27d115e527652e270d62f48d69867a1104 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Fri, 21 Dec 2018 16:32:31 -0500 Subject: [PATCH 13/19] Don't fire `fullscreenchange` event on resize. --- src/js/pannellum.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index 149b10c..c6a904e 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -1329,7 +1329,7 @@ function onDocumentResize() { //animateInit(); // Kludge to deal with WebKit regression: https://bugs.webkit.org/show_bug.cgi?id=93525 - onFullScreenChange(); + onFullScreenChange('resize'); } /** @@ -2105,7 +2105,7 @@ function toggleFullscreen() { * Event handler for fullscreen changes. * @private */ -function onFullScreenChange() { +function onFullScreenChange(resize) { if (document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement) { controls.fullscreen.classList.add('pnlm-fullscreen-toggle-button-active'); fullscreenActive = true; @@ -2113,7 +2113,8 @@ function onFullScreenChange() { controls.fullscreen.classList.remove('pnlm-fullscreen-toggle-button-active'); fullscreenActive = false; } - fireEvent('fullscreenchange', fullscreenActive); + if (resize !== 'resize') + fireEvent('fullscreenchange', fullscreenActive); // Resize renderer (deal with browser quirks and fixes #155) renderer.resize(); setHfov(config.hfov); From 03eb7fa13151dd2b6854b59e0fc76564192a8ffe Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Fri, 21 Dec 2018 16:40:02 -0500 Subject: [PATCH 14/19] Add check for `document.fullscreenElement` since `document.fullscreen` is deprecated. --- src/js/pannellum.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index c6a904e..ba6b90b 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -2106,7 +2106,7 @@ function toggleFullscreen() { * @private */ function onFullScreenChange(resize) { - if (document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement) { + if (document.fullscreenElement || document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement) { controls.fullscreen.classList.add('pnlm-fullscreen-toggle-button-active'); fullscreenActive = true; } else { From 12516e69b90e8ab4c6f42b64560aef92ae539bd0 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Fri, 21 Dec 2018 17:16:14 -0500 Subject: [PATCH 15/19] Fix issue with mouse dragging causing jump around yaw limits (fixes #698) --- src/js/pannellum.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index ba6b90b..3d1fb33 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -1399,12 +1399,6 @@ function render() { var tmpyaw; if (loaded) { - if (config.yaw > 180) { - config.yaw -= 360; - } else if (config.yaw < -180) { - config.yaw += 360; - } - // Keep a tmp value of yaw for autoRotate comparison later tmpyaw = config.yaw; @@ -1439,6 +1433,12 @@ function render() { config.yaw = Math.max(minYaw, Math.min(maxYaw, config.yaw)); } + if (config.yaw > 180) { + config.yaw -= 360; + } else if (config.yaw < -180) { + config.yaw += 360; + } + // Check if we autoRotate in a limited by min and max yaw // If so reverse direction if (config.autoRotate !== false && tmpyaw != config.yaw && From 19b020dbc72b9aeb5d6e3faca95bb55ce5418bb5 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Mon, 31 Dec 2018 14:36:39 -0500 Subject: [PATCH 16/19] Check to make sure hot spot div exists before deleting it (#702). --- src/js/pannellum.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index 3d1fb33..fb5bebb 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -1784,10 +1784,12 @@ function destroyHotSpots() { if (hs) { for (var i = 0; i < hs.length; i++) { var current = hs[i].div; - while(current.parentNode != renderContainer) { - current = current.parentNode; + if (current) { + while(current.parentNode != renderContainer) { + current = current.parentNode; + } + renderContainer.removeChild(current); } - renderContainer.removeChild(current); delete hs[i].div; } } From 34cb1a409ef169167536bef199a2a95d2642ab19 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Mon, 31 Dec 2018 14:41:05 -0500 Subject: [PATCH 17/19] Another check to make sure hot spot div exists before deleting it (#702). --- src/js/pannellum.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index fb5bebb..4df169b 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -1785,7 +1785,7 @@ function destroyHotSpots() { for (var i = 0; i < hs.length; i++) { var current = hs[i].div; if (current) { - while(current.parentNode != renderContainer) { + while (current.parentNode && current.parentNode != renderContainer) { current = current.parentNode; } renderContainer.removeChild(current); From 71bf642860f2cc34c7120435ed635b893d92e5c5 Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Wed, 30 Jan 2019 20:17:34 -0500 Subject: [PATCH 18/19] Properly remove event listeners from container element (fixes #710). For three event listeners added to the container elements weren't being removed properly, since `removeEventListener` was mistakenly being called on `uiContainer` instead. This was preventing garbage collection. --- src/js/pannellum.js | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/js/pannellum.js b/src/js/pannellum.js index 4df169b..fa7f947 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -3050,36 +3050,23 @@ function fireEvent(type) { */ this.destroy = function() { if (renderer) - renderer.destroy() + renderer.destroy(); if (listenersAdded) { - dragFix.removeEventListener('mousedown', onDocumentMouseDown, false); - dragFix.removeEventListener('dblclick', onDocumentDoubleClick, false); document.removeEventListener('mousemove', onDocumentMouseMove, false); document.removeEventListener('mouseup', onDocumentMouseUp, false); - uiContainer.removeEventListener('mousewheel', onDocumentMouseWheel, false); - uiContainer.removeEventListener('DOMMouseScroll', onDocumentMouseWheel, false); container.removeEventListener('mozfullscreenchange', onFullScreenChange, false); container.removeEventListener('webkitfullscreenchange', onFullScreenChange, false); container.removeEventListener('msfullscreenchange', onFullScreenChange, false); container.removeEventListener('fullscreenchange', onFullScreenChange, false); window.removeEventListener('resize', onDocumentResize, false); window.removeEventListener('orientationchange', onDocumentResize, false); - uiContainer.removeEventListener('keydown', onDocumentKeyPress, false); - uiContainer.removeEventListener('keyup', onDocumentKeyUp, false); - uiContainer.removeEventListener('blur', clearKeys, false); + container.removeEventListener('keydown', onDocumentKeyPress, false); + container.removeEventListener('keyup', onDocumentKeyUp, false); + container.removeEventListener('blur', clearKeys, false); document.removeEventListener('mouseleave', onDocumentMouseUp, false); - dragFix.removeEventListener('touchstart', onDocumentTouchStart, false); - dragFix.removeEventListener('touchmove', onDocumentTouchMove, false); - dragFix.removeEventListener('touchend', onDocumentTouchEnd, false); - dragFix.removeEventListener('pointerdown', onDocumentPointerDown, false); - dragFix.removeEventListener('pointermove', onDocumentPointerMove, false); - dragFix.removeEventListener('pointerup', onDocumentPointerUp, false); - dragFix.removeEventListener('pointerleave', onDocumentPointerUp, false); } container.innerHTML = ''; container.classList.remove('pnlm-container'); - uiContainer.classList.remove('pnlm-grab'); - uiContainer.classList.remove('pnlm-grabbing'); } } From 59893bcea4629be059ad3e59c3fa5bde438d292b Mon Sep 17 00:00:00 2001 From: Matthew Petroff Date: Fri, 12 Apr 2019 19:57:24 -0400 Subject: [PATCH 19/19] Switch to `highp` precision for equirectangular fragment shader. This resolves pixelation issues on some mobile devices (fixes #725). It also may be related to issues #301, #496, #683, and #708. --- src/js/libpannellum.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/js/libpannellum.js b/src/js/libpannellum.js index 128073a..805fe2e 100644 --- a/src/js/libpannellum.js +++ b/src/js/libpannellum.js @@ -321,6 +321,15 @@ function Renderer(container) { // Create viewport for entire canvas gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + // Check precision support + if (gl.getShaderPrecisionFormat) { + var precision = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT); + if (precision && precision.precision < 1) { + // `highp` precision not supported; https://stackoverflow.com/a/33308927 + fragEquiCubeBase = fragEquiCubeBase.replace('highp', 'mediump'); + } + } + // Create vertex shader vs = gl.createShader(gl.VERTEX_SHADER); var vertexSrc = v; @@ -1344,7 +1353,7 @@ var vMulti = [ // Fragment shader var fragEquiCubeBase = [ -'precision mediump float;', +'precision highp float;', // mediump looks bad on some mobile devices 'uniform float u_aspectRatio;', 'uniform float u_psi;',