Browse Source

Merge branch 'master' of https://github.com/mpetroff/pannellum into master-build

pull/952/head
strarsis 5 years ago
parent
commit
ab7b859be1
4 changed files with 176 additions and 69 deletions
  1. +6
    -0
      doc/events.md
  2. +13
    -0
      doc/json-config-parameters.md
  3. +73
    -15
      src/js/libpannellum.js
  4. +84
    -54
      src/js/pannellum.js

+ 6
- 0
doc/events.md View File

@@ -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


+ 13
- 0
doc/json-config-parameters.md View File

@@ -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`.
@@ -439,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


+ 73
- 15
src/js/libpannellum.js View File

@@ -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
@@ -319,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;
@@ -414,8 +425,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
@@ -1306,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;',
@@ -1320,7 +1367,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 +1414,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');



+ 84
- 54
src/js/pannellum.js View File

@@ -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 = {
@@ -91,6 +92,7 @@ var defaultConfig = {
northOffset: 0,
showFullscreenCtrl: true,
dynamic: false,
dynamicUpdate: false,
doubleClickZoom: true,
keyboardZoom: true,
mouseZoom: true,
@@ -107,6 +109,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
@@ -477,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) {
@@ -658,8 +661,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;
}

@@ -1245,19 +1249,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);
}
}

@@ -1300,11 +1304,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;
}
@@ -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');
}

/**
@@ -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 -
@@ -1398,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;

@@ -1438,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 &&
@@ -1783,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 && current.parentNode != renderContainer) {
current = current.parentNode;
}
renderContainer.removeChild(current);
}
renderContainer.removeChild(current);
delete hs[i].div;
}
}
@@ -2104,15 +2107,16 @@ function toggleFullscreen() {
* Event handler for fullscreen changes.
* @private
*/
function onFullScreenChange() {
if (document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement) {
function onFullScreenChange(resize) {
if (document.fullscreenElement || document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement) {
controls.fullscreen.classList.add('pnlm-fullscreen-toggle-button-active');
fullscreenActive = true;
} else {
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);
@@ -2287,6 +2291,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();
}
}

/**
@@ -2386,16 +2397,22 @@ 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;
}
@@ -2447,6 +2464,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) {
@@ -2460,10 +2483,10 @@ 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;
}
@@ -2515,16 +2538,22 @@ 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);
}
@@ -2570,16 +2599,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;
}

@@ -2683,6 +2716,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
@@ -3007,14 +3050,10 @@ 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);
container.removeEventListener('mousewheel', onDocumentMouseWheel, false);
container.removeEventListener('DOMMouseScroll', onDocumentMouseWheel, false);
container.removeEventListener('mozfullscreenchange', onFullScreenChange, false);
container.removeEventListener('webkitfullscreenchange', onFullScreenChange, false);
container.removeEventListener('msfullscreenchange', onFullScreenChange, false);
@@ -3025,18 +3064,9 @@ this.destroy = function() {
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');
}

}


Loading…
Cancel
Save