Selaa lähdekoodia

Merge branch 'master' into master

pull/358/head
strarsis 7 vuotta sitten
committed by GitHub
vanhempi
commit
683b86ca43
14 muutettua tiedostoa jossa 1258 lisäystä ja 471 poistoa
  1. +1
    -1
      COPYING
  2. +1
    -1
      VERSION
  3. +92
    -0
      changelog.md
  4. +28
    -2
      doc/events.md
  5. +143
    -60
      doc/json-config-parameters.md
  6. +1
    -1
      package.json
  7. +3
    -1
      src/css/img/sprites.svg
  8. +37
    -9
      src/css/pannellum.css
  9. +92
    -104
      src/js/libpannellum.js
  10. +829
    -269
      src/js/pannellum.js
  11. +13
    -8
      src/standalone/standalone.js
  12. +1
    -1
      utils/build/build.py
  13. +7
    -3
      utils/multires/generate.py
  14. +10
    -11
      utils/video/videojs-pannellum-plugin.js

+ 1
- 1
COPYING Näytä tiedosto

@@ -1,4 +1,4 @@
Copyright (c) 2011-2016 Matthew Petroff
Copyright (c) 2011-2017 Matthew Petroff

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in


+ 1
- 1
VERSION Näytä tiedosto

@@ -1 +1 @@
2.2.1
2.3.2

+ 92
- 0
changelog.md Näytä tiedosto

@@ -2,6 +2,98 @@ Changelog
=========


Changes in Pannellum 2.3.2
--------------------------

Bugfixes:

- Fix Chrome fullscreen regression introduced in 2.3.1


Changes in Pannellum 2.3.1
--------------------------

Bugfixes:

- Removed use of poorly supported ES6 `Math.sign` function
- Fixed fullscreen bug in Internet Explorer
- Fixed framerate issue with device orientation control enabled

Improvements:

- Better handling of view limits when both limits are in view


Changes in Pannellum 2.3.0
--------------------------

New Features:

- Device orientation support for mobile devices
- Event framework for API
- Partial panorama background color can now be set using
`backgroundColor` parameter
- Custom hot spots are now supported as are hot spot click handlers
- Hot spots can now specify target HFOV (`targetHfov` parameter)
- Parameter to hide all controls (`showControls`)
- Parameter to disable mouse zooming (`mouseZoom`)

New API functions:

- Destructor (`destroy`)
- Look at position (`lookAt`)
- Get current scene ID (`getScene`)
- Load scene (`loadScene`)
- Add and remove scenes (`addScene` and `removeScene`)
- Add and remove hot spots (`addHotSpot` and `removeHotSpot`)
- Auto rotate start / stop (`startAutoRotate` and `stopAutoRotate`)
- Retrieve current configuration (`getConfig`)
- Toggle fullscreen (`toggleFullscreen`)
- Get and set north offset (`getNorthOffset` and `setNorthOffset`)

Improvements:

- Pitch and yaw limits are now applied to edge of viewer instead of center
- Panorama extents can now be set using URL parameters
- Individual XMP metadata parameters can now be overridden
- Horizon pitch and roll can now be manually set (was previously only
supported via XMP metadata)
- When auto rotate restarts, the pitch and HFOV now return to their
original values
- API movements can now be animated
- Standalone viewer is more mobile friendly
- Improved touch panning interaction
- Fragments identifiers can now be used for standalone viewer configuration
- Blob URLs are now supported
- Added hot spot debug indicator
- Video.js plugin now accepts a Pannellum configuration

Bugfixes:

- Fixed numerous auto rotate bugs
- Auto rotate speed is now actually in degrees per second
- Long error URLs are now properly wrapped
- Fixed mobile device orientation change bug
- Fixed Safari fullscreen bug
- Fullscreen now works in IE
- Fixed Chrome bug where hot spots appeared above controls
- Scene fades with multires now work properly
- Hot spot target pointing now works when set to zero
- Hot spots without text are now properly handled
- Fixed memory leaks
- Fixed multires tile loading error
- Fixed a few URL handling bugs
- Fixed multires zoom jumping when viewer was resized
- Title and author are now reset when changing scenes
- Mouse handlers now work with Hi-DPI displays
- Minimum and maximum HFOV can now both be set to the same value

Backwards-Incompatible Configuration Parameter Changes:

- The deprecated `tour` parameter was removed; tour JSON configuration files
can be used with the `config` parameter


Changes in Pannellum 2.2.1
--------------------------



+ 28
- 2
doc/events.md Näytä tiedosto

@@ -1,4 +1,4 @@
# Events
# API Events

## `load`

@@ -8,7 +8,13 @@ Fired when a panorama finishes loading.
## `scenechange`

Fired when a scene change is initiated. A `load` event will be fired when the
new scene finishes loading.
new scene finishes loading. Passes scene ID string to handler.


## `scenechangefadedone`

If a scene transition fade interval is specified, this event is fired when the
fading is completed after changing scenes.


## `error`
@@ -20,3 +26,23 @@ event listener.
## `errorcleared`

Fired when an error is cleared.


## `mousedown`

Fired when the mouse button is pressed. Passes `MouseEvent` to handler.


## `mouseup`

Fired when the mouse button is released. Passes `MouseEvent` to handler.


## `touchstart`

Fired when a touch starts. Passes `TouchEvent` to handler.


## `touchend`

Fired when a touch ends. Passes `TouchEvent` to handler.

+ 143
- 60
doc/json-config-parameters.md Näytä tiedosto

@@ -5,190 +5,271 @@
## General options


### `type`
### `type` (string)

This specifies the panorama type. Can be `equirectangular`, `cubemap`, or
`multires`. Defaults to `equirectangular`.


### `title`
### `title` (string)

If set, the value is displayed as the panorama's title. If no title is desired,
don't set this parameter.


### `author`
### `author` (string)

If set, the value is displayed as the panorama's author. If no author is
desired, don't set this parameter.


### `basePath`
### `basePath` (string)

This specifies a base path to load the images from.


### `autoLoad`
### `autoLoad` (boolean)

When set to `true`, the panorama will automatically load. When `false`, the
user needs to click on the load button to load the panorama. Defaults to
`false`.


### `autoRotate`
### `autoRotate` (number)

Setting this parameter causes the panorama to automatically rotate when loaded.
The value specifies the rotation speed in degrees per second. Positive is
counter-clockwise, and negative is clockwise.


### `autoRotateInactivityDelay`
### `autoRotateInactivityDelay` (number)

Sets the delay, in milliseconds, to start automatically rotating the panorama
after user activity ceases. This parameter only has an effect if the
`autoRotate` parameter is set.


### `autoRotateStopDelay`
### `autoRotateStopDelay` (number)

Sets the delay, in milliseconds, to stop automatically rotating the panorama
after it is loaded. This parameter only has an effect if the `autoRotate`
parameter is set.


### `fallback`
### `fallback` (string)

If set, the value is used as a URL for a fallback viewer in case Pannellum is
not supported by the user's device. The user will be given the option to click
a link and visit this URL if Pannellum fails to work.


### `showZoomCtrl`
### `orientationOnByDefault` (boolean)

If set to `true`, device orientation control will be used when the panorama is
loaded, if the device supports it. If false, device orientation control needs
to be activated by pressing a button. Defaults to `false`.


### `showZoomCtrl` (boolean)

If set to `false`, the zoom controls will not be displayed. Defaults to `true`.


### `keyboardZoom`
### `keyboardZoom` (boolean)

If set to `false`, zooming with keyboard will be disabled. Defaults to `true`.

### `mouseZoom`

### `mouseZoom` (boolean or string)

If set to `false`, zooming with mouse wheel will be disabled. Defaults to `true`.
Can also be set to `fullscreenonly`, in which case it is only enabled when the
viewer is fullscreen.


### `draggable` (boolean)

### `showFullscreenCtrl`
If set to `false`, mouse and touch dragging is disabled. Defaults to `true`.


### `showFullscreenCtrl` (boolean)

If set to `false`, the fullscreen control will not be displayed. Defaults to
`true`. The fullscreen button will only be displayed if the browser supports
the fullscreen API.


### `yaw`
### `showControls` (boolean)

If set to `false`, no controls are displayed. Defaults to `true`.


### `yaw` (number)

Sets the panorama's starting yaw position in degrees. Defaults to `0`.


### `pitch`
### `pitch` (number)

Sets the panorama's starting pitch position in degrees. Defaults to `0`.


### `hfov`
### `hfov` (number)

Sets the panorama's starting horizontal field of view in degrees. Defaults to
`100`.


### `minYaw` and `maxYaw`
### `minYaw` and `maxYaw` (number)

Sets the minimum / maximum yaw the viewer can be centered at, in degrees.
Defaults to `-360` / `360`, i.e. no limit.
Sets the minimum / maximum yaw the viewer edge can be at, in degrees.
Defaults to `-180` / `180`, i.e. no limit.


### `minPitch` and `maxPitch`
### `minPitch` and `maxPitch` (number)

Sets the minimum / maximum pitch the viewer can be centered at, in degrees.
Defaults to `-85` / `85`.
Sets the minimum / maximum pitch the viewer edge can be at, in degrees.
Defaults to `undefined`, so the viewer center can reach `-90` / `90`.


### `minHfov` and `maxHfov`
### `minHfov` and `maxHfov` (number)

Sets the minimum / maximum horizontal field of view, in degrees, that the
viewer can be set to. Defaults to `50` / `120`.


### `compass`
### `compass` (boolean)

If `true`, a compass is displayed. Defaults to `false`.
If `true`, a compass is displayed. Normally defaults to `false`; defaults to
`true` if heading information is present in Photo Sphere XMP metadata.


### `northOffset`
### `northOffset` (number)

Set the offset, in degrees, of the center of the panorama from North. As this
affects the compass, it only has an effect if `compass` is set to `true`.


### `preview`
### `preview` (string)

Specifies a URL for a preview image to display before the panorama is loaded.


### `hotSpots`
### `previewTitle` (string)

Specifies the title to be displayed while the load button is displayed.


### `previewAuthor` (string)

Specifies the author to be displayed while the load button is displayed.


### `loadButtonLabel` (string)

Label to display on load button. Defaults to `Click to Load Panorama`.


### `horizonPitch` and `horizonRoll` (number)

Specifies pitch / roll of image horizon, in degrees (for correcting
non-leveled panoramas).


### `animationTimingFunction` (function) [API only]

This specifies a timing function to be used for animating movements such as
when the `lookAt` method is called. The default timing function is
`easeInOutQuad`. If a custom function is specified, it should take a number
[0, 1] as its only argument and return a number [0, 1].


### `escapeHTML` (boolean)

When true, HTML is escaped from configuration strings to help mitigate possible
DOM XSS attacks. This is always `true` when using the standalone viewer since
the configuration is provided via the URL; it defaults to `false` but can be
set to `true` when using the API.


### `hotSpots` (array)

This specifies an array of hot spots that can be links to other scenes,
information, or external links. Each array element has the following properties.


#### `pitch`
#### `pitch` (number)

Specifies the pitch portion of the hot spot's location.
Specifies the pitch portion of the hot spot's location, in degrees.


#### `yaw`
#### `yaw` (number)

Specifies the yaw portion of the hot spot's location.
Specifies the yaw portion of the hot spot's location, in degrees.


#### `type`
#### `type` (string)

Specifies the type of the hot spot. Can be `scene` for scene links or `info`
for information hot spots. A tour configuration file is required for `scene`
hot spots.

#### `text`
#### `text` (string)

This specifies the text that is displayed when the user hovers over the hot
spot.

#### `URL`
#### `URL` (string)

If specified for an `info` hot spot, the hot spot links to the specified URL.
Not applicable for `scene` hot spots.

#### `sceneId`
#### `sceneId` (string)

Specifies the ID of the scene to link to for `scene` hot spots. Not applicable
for `info` hot spots.

#### `targetPitch`
#### `targetPitch` (number)

Specifies the pitch of the target scene, in degrees. Can also be set to `same`,
which uses the current pitch of the current scene as the initial pitch of the
target scene.

#### `targetYaw` (number)

Specifies the pitch of the target scene.
Specifies the yaw of the target scene, in degrees. Can also be set to `same` or
`sameAzimuth`. These settings use the current yaw of the current scene as the
initial yaw of the target scene; `same` uses the current yaw directly, while
`sameAzimuth` takes into account the `northOffset` values of both scenes to
maintain the same direction with regard to north.

#### `targetYaw`
#### `targetHfov` (number)

Specifies the yaw of the target scene.
Specifies the HFOV of the target scene, in degrees.

#### `targetHfov`
#### `cssClass` (string)

Specifies the HFOV of the target scene.
If specified, string is used as the CSS class for the hot spot instead of the
default CSS classes.

### `hotSpotDebug`
#### `createTooltipFunc` (function) and `createTooltipArgs` (object)

If `createTooltipFunc` is specified, this function is used to create the hot
spot tooltip DOM instead of the default function. The contents of
`createTooltipArgs` are passed to the function as arguments.

#### `clickHandlerFunc` (function) and `clickHandlerArgs` (object)

If `clickHandlerFunc` is specified, this function is added as an event handler
for the hot spot's `click` event. The event object and the contents of
`clickHandlerArgs` are passed to the function as arguments.

### `hotSpotDebug` (boolean)

When `true`, the mouse pointer's pitch and yaw are logged to the console when
the mouse button is clicked. Defaults to `false`.

### `sceneFadeDuration`
### `sceneFadeDuration` (number)

Specifies the fade duration, in milliseconds, when transitioning between
scenes. Not defined by default. Only applicable for tours. Only works with
@@ -198,38 +279,40 @@ WebGL renderer.

## `equirectangular` specific options

### `panorama`
### `panorama` (string)

Sets the URL to the equirectangular panorama image. This is relative to
`basePath` if it is set, else it is relative to the location of
`pannellum.htm`. An absolute URL can also be used.


### `haov`
### `haov` (number)

Sets the panorama's horizontal angle of view, in degrees. Defaults to `360`.
This is used if the equirectangular image does not cover a full 360 degrees in
the horizontal.


### `vaov`
### `vaov` (number)

Sets the panorama's vertical angle of view, in degrees. Defaults to `180`. This
is used if the equirectangular image does not cover a full 180 degrees in the
vertical.


### `vOffset`
### `vOffset` (number)

Sets the vertical offset of the center of the equirectangular image from the
horizon, in degrees. Defaults to `0`. This is used if `vaov` is less than `180`
and the equirectangular image is not cropped symmetrically.

### `ignoreGPanoXMP`
### `ignoreGPanoXMP` (boolean)

If set to `true`, any embedded Photo Sphere XMP data will be ignored; else,
said data will override any existing settings. Defaults to `false`.

### `backgroundColor` ([number, number, number])

Specifies an array containing RGB values [0, 1] that sets the background color
shown past the edges of a partial panorama. Defaults to `[0, 0, 0]` (black).



## `cubemap` specific options
@@ -250,14 +333,14 @@ used.
This contains information about the multiresolution panorama in sub-keys.


#### `basePath`
#### `basePath` (string)

This is the base path of the URLs for the multiresolution tiles. It is relative
to the regular `basePath` option if it is defined, else it is relative to the
location of `pannellum.htm`. An absolute URL can also be used.


#### `path`
#### `path` (string)

This is a format string for the location of the multiresolution tiles, relative
to `multiRes.basePath`, which is relative to `basePath`. Format parameters are
@@ -265,7 +348,7 @@ to `multiRes.basePath`, which is relative to `basePath`. Format parameters are
`%y` for the y index. For each tile, `.extension` is appended.


#### `fallbackPath`
#### `fallbackPath` (string)

This is a format string for the location of the fallback tiles for the CSS 3D
transform-based renderer if the WebGL renderer is not supported, relative
@@ -273,22 +356,22 @@ to `multiRes.basePath`, which is relative to `basePath`. The only format
parameter is `%s`, for the cube face. For each face, `.extension` is appended.


#### `extension`
#### `extension` (string)

Specifies the tiles' file extension. Do not include the `.`.


#### `tileResolution`
#### `tileResolution` (number)

This specifies the size in pixels of each image tile.


#### `maxLevel`
#### `maxLevel` (number)

This specifies the maximum zoom level.


#### `cubeResolution`
#### `cubeResolution` (number)

This specifies the size in pixels of the full resolution cube faces the image
tiles were created from.
@@ -299,7 +382,7 @@ tiles were created from.

Currently, only equirectangular dynamic content is supported.

### `dynamic`
### `dynamic` (boolean)

The panorama source is considered dynamic when this is set to `true`. Defaults
to `false`. This should be set to `true` for video.


+ 1
- 1
package.json Näytä tiedosto

@@ -1,7 +1,7 @@
{
"name": "pannellum",
"description": "Pannellum is a lightweight, free, and open source panorama viewer for the web.",
"version": "2.2.1",
"version": "2.3.2",
"bugs": {
"url": "https://github.com/mpetroff/pannellum/issues"
},


+ 3
- 1
src/css/img/sprites.svg Näytä tiedosto

@@ -1,7 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="182">
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="208">
<circle fill-opacity=".78" cy="117" cx="13" r="11" fill="#fff"/>
<circle fill-opacity=".78" cy="143" cx="13" r="11" fill="#fff"/>
<circle cy="169" cx="13" r="7" fill="none" stroke="#000" stroke-width="2"/>
<circle cy="195" cx="13" r="7" fill="none" stroke="#000" stroke-width="2"/>
<circle cx="13" cy="195" r="2.5"/>
<path d="m5 83v6h2v-4h4v-2zm10 0v2h4v4h2v-6zm-5 5v6h6v-6zm-5 5v6h6v-2h-4v-4zm14 0v4h-4v2h6v-6z"/>
<path d="m13 110a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm-1 3h2v2h-2zm0 3h2v5h-2z"/>
<path d="m5 57v6h2v-4h4v-2zm10 0v2h4v4h2v-6zm-10 10v6h6v-2h-4v-4zm14 0v4h-4v2h6v-6z"/>


+ 37
- 9
src/css/pannellum.css Näytä tiedosto

@@ -16,6 +16,7 @@
user-select: none;
outline: 0;
line-height: 1.4;
contain: content;
}

.pnlm-container * {
@@ -23,10 +24,12 @@
}

.pnlm-grab {
cursor: grab;
cursor: url('img/grab.svg') 12 8, default;
}

.pnlm-grabbing {
cursor: grabbing;
cursor: url('img/grabbing.svg') 12 8, default;
}

@@ -42,6 +45,10 @@
height: 100% !important;
width: 100% !important;
}
.pnlm-container:-ms-fullscreen {
height: 100% !important;
width: 100% !important;
}
.pnlm-container:fullscreen {
height: 100% !important;
width: 100% !important;
@@ -104,12 +111,31 @@
border-radius: 0 0 3px 3px;
}

.pnlm-fullscreen-toggle-button, .pnlm-orientation-button {
.pnlm-fullscreen-toggle-button, .pnlm-orientation-button, .pnlm-hot-spot-debug-indicator {
width: 26px;
height: 26px;
}

.pnlm-hot-spot-debug-indicator {
position: absolute;
top: 50%;
left: 50%;
width: 26px;
height: 26px;
margin: -13px 0 0 -13px;
background-color: rgba(255, 255, 255, 0.5);
border-radius: 13px;
display: none;
}

.pnlm-orientation-button-inactive {
background-position: 0 -156px;
}

.pnlm-orientation-button-active {
background-position: 0 -182px;
}

.pnlm-fullscreen-toggle-button-inactive {
background-position: 0 -52px;
}
@@ -293,17 +319,19 @@
color: #eee;
}

.pnlm-hotspot {
.pnlm-hotspot-base {
position: absolute;
height: 26px;
width: 26px;
visibility: hidden;
cursor: default;
border-radius: 13px;
vertical-align: middle;
top: 0;
z-index: 1;
}
.pnlm-hotspot {
height: 26px;
width: 26px;
border-radius: 13px;
}
.pnlm-hotspot:hover {
background-color: rgba(255,255,255,0.2);
}
@@ -323,14 +351,13 @@ div.pnlm-tooltip span {
text-align: center;
max-width: 200px;
padding: 5px 10px;
margin-top: -41px;
margin-left: -220px;
cursor: default;
}
div.pnlm-tooltip:hover span{
visibility: visible;
}
div.pnlm-tooltip:hover:after {
div.pnlm-tooltip:hover span:after {
content: '';
position: absolute;
width: 0;
@@ -338,8 +365,9 @@ div.pnlm-tooltip:hover:after {
border-width: 10px;
border-style: solid;
border-color: rgba(0,0,0,0.7) transparent transparent transparent;
top: -12px;
left: 3px;
bottom: -20px;
left: -10px;
margin: 0 50%;
}

.pnlm-compass {


+ 92
- 104
src/js/libpannellum.js Näytä tiedosto

@@ -1,6 +1,6 @@
/*
* libpannellum - A WebGL and CSS 3D transform based Panorama Renderer
* Copyright (c) 2012-2016 Matthew Petroff
* Copyright (c) 2012-2017 Matthew Petroff
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -59,11 +59,19 @@ function Renderer(container) {
* @param {number} vaov - Initial vertical angle of view.
* @param {number} voffset - Initial vertical offset angle.
* @param {function} callback - Load callback function.
* @param {Object} [params] - Other configuration parameters (`horizonPitch`, `horizonRoll`, `backgroundColor`).
*/
this.init = function(_image, _imageType, _dynamic, haov, vaov, voffset, callback) {
this.init = function(_image, _imageType, _dynamic, haov, vaov, voffset, callback, params) {
// Default argument for image type
if (typeof _imageType === undefined)
_imageType = 'equirectangular';

if (_imageType != 'equirectangular' && _imageType != 'cubemap' &&
_imageType != 'multires') {
console.log('Error: invalid image type specified!');
throw {type: 'config error'};
}

imageType = _imageType;
image = _image;
dynamic = _dynamic;
@@ -88,6 +96,7 @@ function Renderer(container) {
gl.deleteProgram(program);
program = undefined;
}
pose = undefined;

var s;
@@ -248,9 +257,9 @@ function Renderer(container) {
}

// Store horizon pitch and roll if applicable
if (image.horizonPitch !== undefined && image.horizonRoll !== undefined) {
pose = [image.horizonPitch, image.horizonRoll];
}
if (params !== undefined && (params.horizonPitch !== undefined || params.horizonRoll !== undefined))
pose = [params.horizonPitch == undefined ? 0 : params.horizonPitch,
params.horizonRoll == undefined ? 0 : params.horizonRoll];

// Set 2d texture binding
var glBindType = gl.TEXTURE_2D;
@@ -328,6 +337,13 @@ function Renderer(container) {
gl.uniform1f(program.v, vaov / Math.PI);
gl.uniform1f(program.vo, voffset / Math.PI * 2);

// Set background color
if (imageType == 'equirectangular') {
program.backgroundColor = gl.getUniformLocation(program, 'u_backgroundColor');
var color = params.backgroundColor ? params.backgroundColor : [0, 0, 0];
gl.uniform4fv(program.backgroundColor, color.concat([1]));
}

// Create texture
program.texture = gl.createTexture();
gl.bindTexture(glBindType, program.texture);
@@ -408,6 +424,13 @@ function Renderer(container) {
container.removeChild(world);
}
}
if (gl) {
// The spec says this is only supposed to simulate losing the WebGL
// context, but in practice it tends to actually free the memory.
var extension = gl.getExtension('WEBGL_lose_context');
if (extension)
extension.loseContext();
}
};

/**
@@ -446,7 +469,41 @@ function Renderer(container) {
params = {};
if (params.roll)
roll = params.roll;

// Apply pitch and roll transformation if applicable
if (pose !== undefined) {
var horizonPitch = pose[0],
horizonRoll = pose[1];

// Calculate new pitch and yaw
var orig_pitch = pitch,
orig_yaw = yaw,
x = Math.cos(horizonRoll) * Math.sin(pitch) * Math.sin(horizonPitch) +
Math.cos(pitch) * (Math.cos(horizonPitch) * Math.cos(yaw) +
Math.sin(horizonRoll) * Math.sin(horizonPitch) * Math.sin(yaw)),
y = -Math.sin(pitch) * Math.sin(horizonRoll) +
Math.cos(pitch) * Math.cos(horizonRoll) * Math.sin(yaw),
z = Math.cos(horizonRoll) * Math.cos(horizonPitch) * Math.sin(pitch) +
Math.cos(pitch) * (-Math.cos(yaw) * Math.sin(horizonPitch) +
Math.cos(horizonPitch) * Math.sin(horizonRoll) * Math.sin(yaw));
pitch = Math.asin(Math.max(Math.min(z, 1), -1));
yaw = Math.atan2(y, x);

// Calculate roll
var v = [Math.cos(orig_pitch) * (Math.sin(horizonRoll) * Math.sin(horizonPitch) * Math.cos(orig_yaw) -
Math.cos(horizonPitch) * Math.sin(orig_yaw)),
Math.cos(orig_pitch) * Math.cos(horizonRoll) * Math.cos(orig_yaw),
Math.cos(orig_pitch) * (Math.cos(horizonPitch) * Math.sin(horizonRoll) * Math.cos(orig_yaw) +
Math.sin(orig_yaw) * Math.sin(horizonPitch))],
w = [-Math.cos(pitch) * Math.sin(yaw), Math.cos(pitch) * Math.cos(yaw)];
var roll_adj = Math.acos(Math.max(Math.min((v[0]*w[0] + v[1]*w[1]) /
(Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]) *
Math.sqrt(w[0]*w[0]+w[1]*w[1])), 1), -1));
if (v[2] < 0)
roll_adj = 2 * Math.PI - roll_adj;
roll += roll_adj;
}

// If no WebGL
if (!gl && (imageType == 'multires' || imageType == 'cubemap')) {
// Determine face transforms
@@ -479,40 +536,6 @@ function Renderer(container) {
var vfov = 2 * Math.atan(Math.tan(hfov * 0.5) / (canvas.width / canvas.height));
focal = 1 / Math.tan(vfov * 0.5);

// Apply pitch and roll transformation if applicable
if (imageType == 'equirectangular' && pose !== undefined) {
var horizonPitch = pose[0],
horizonRoll = pose[1];

// Calculate new pitch and yaw
var orig_pitch = pitch,
orig_yaw = yaw,
x = Math.cos(horizonRoll) * Math.sin(pitch) * Math.sin(horizonPitch) +
Math.cos(pitch) * (Math.cos(horizonPitch) * Math.cos(yaw) +
Math.sin(horizonRoll) * Math.sin(horizonPitch) * Math.sin(yaw)),
y = -Math.sin(pitch) * Math.sin(horizonRoll) +
Math.cos(pitch) * Math.cos(horizonRoll) * Math.sin(yaw),
z = Math.cos(horizonRoll) * Math.cos(horizonPitch) * Math.sin(pitch) +
Math.cos(pitch) * (-Math.cos(yaw) * Math.sin(horizonPitch) +
Math.cos(horizonPitch) * Math.sin(horizonRoll) * Math.sin(yaw));
pitch = Math.asin(Math.max(Math.min(z, 1), -1));
yaw = Math.atan2(y, x);

// Calculate roll
var v = [Math.cos(orig_pitch) * (Math.sin(horizonRoll) * Math.sin(horizonPitch) * Math.cos(orig_yaw) -
Math.cos(horizonPitch) * Math.sin(orig_yaw)),
Math.cos(orig_pitch) * Math.cos(horizonRoll) * Math.cos(orig_yaw),
Math.cos(orig_pitch) * (Math.cos(horizonPitch) * Math.sin(horizonRoll) * Math.cos(orig_yaw) +
Math.sin(orig_yaw) * Math.sin(horizonPitch))],
w = [-Math.cos(pitch) * Math.sin(yaw), Math.cos(pitch) * Math.cos(yaw)];
var roll_adj = Math.acos(Math.max(Math.min((v[0]*w[0] + v[1]*w[1]) /
(Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]) *
Math.sqrt(w[0]*w[0]+w[1]*w[1])), 1), -1));
if (v[2] < 0)
roll_adj = 2 * Math.PI - roll_adj;
roll += roll_adj;
}

// Pass psi, theta, roll, and focal length
gl.uniform1f(program.psi, yaw);
gl.uniform1f(program.theta, pitch);
@@ -782,7 +805,7 @@ function Renderer(container) {
}
}
// Handle small tiles that have fewer than four children
if (doubleTileSize < image.tileResolution) {
if (doubleTileSize <= image.tileResolution) {
if (node.x == numTiles) {
f1 = 0;
i1 = 1;
@@ -808,7 +831,7 @@ function Renderer(container) {
];
ntmp = new MultiresNode(vtmp, node.side, node.level + 1, node.x*2, node.y*2, image.fullpath);
children.push(ntmp);
if (!(node.x == numTiles && doubleTileSize < image.tileResolution)) {
if (!(node.x == numTiles && doubleTileSize <= image.tileResolution)) {
vtmp = [v[0]*f1+v[3]*i1, v[1]*f+v[4]*i, v[2]*f3+v[5]*i3,
v[3], v[4], v[5],
v[3]*f+v[6]*i, v[4]*f2+v[7]*i2, v[5]*f3+v[8]*i3,
@@ -817,8 +840,8 @@ function Renderer(container) {
ntmp = new MultiresNode(vtmp, node.side, node.level + 1, node.x*2+1, node.y*2, image.fullpath);
children.push(ntmp);
}
if (!(node.x == numTiles && doubleTileSize < image.tileResolution) &&
!(node.y == numTiles && doubleTileSize < image.tileResolution)) {
if (!(node.x == numTiles && doubleTileSize <= image.tileResolution) &&
!(node.y == numTiles && doubleTileSize <= image.tileResolution)) {
vtmp = [v[0]*f1+v[6]*i1, v[1]*f2+v[7]*i2, v[2]*f3+v[8]*i3,
v[3]*f+v[6]*i, v[4]*f2+v[7]*i2, v[5]*f3+v[8]*i3,
v[6], v[7], v[8],
@@ -827,7 +850,7 @@ function Renderer(container) {
ntmp = new MultiresNode(vtmp, node.side, node.level + 1, node.x*2+1, node.y*2+1, image.fullpath);
children.push(ntmp);
}
if (!(node.y == numTiles && doubleTileSize < image.tileResolution)) {
if (!(node.y == numTiles && doubleTileSize <= image.tileResolution)) {
vtmp = [ v[0]*f+v[9]*i, v[1]*f2+v[10]*i2, v[2]*f3+v[11]*i3,
v[0]*f1+v[6]*i1, v[1]*f2+v[7]*i2, v[2]*f3+v[8]*i3,
v[9]*f1+v[6]*i1, v[10]*f+v[7]*i, v[11]*f3+v[8]*i3,
@@ -981,10 +1004,11 @@ function Renderer(container) {
var self = this;
this.texture = this.callback = null;
this.image = new Image();
this.image.crossOrigin = 'anonymous';
this.image.addEventListener('load', function() {
processLoadedTexture(self.image, self.texture);
releaseTextureImageLoader(self);
self.callback(self.texture);
releaseTextureImageLoader(self);
});
};

@@ -1027,7 +1051,8 @@ function Renderer(container) {
* @param {MultiresNode} node - Input node.
*/
function processNextTile(node) {
if (!node.texture) {
if (!node.textureLoad) {
node.textureLoad = true;
loadTexture(encodeURI(node.path + '.' + image.extension), function(texture) {
node.texture = texture;
node.textureLoaded = true;
@@ -1173,56 +1198,7 @@ var vMulti = [
].join('');

// Fragment shader
var fragCube = [
'precision mediump float;',

'uniform float u_aspectRatio;',
'uniform float u_psi;',
'uniform float u_theta;',
'uniform float u_f;',
'uniform float u_h;',
'uniform float u_v;',
'uniform float u_vo;',
'uniform float u_rot;',

'const float PI = 3.14159265358979323846264;',

// Texture
'uniform samplerCube u_image;',

// Coordinates passed in from vertex shader
'varying vec2 v_texCoord;',

'void main() {',
// Find the vector of focal point to view plane
'vec3 planePos = vec3(v_texCoord.xy, 0.0);',
'planePos.x *= u_aspectRatio;',
'float sinrot = sin(u_rot);',
'float cosrot = cos(u_rot);',
'vec3 rotPos = vec3(planePos.x * cosrot - planePos.y * sinrot, planePos.x * sinrot + planePos.y * cosrot, 0.0);',
'vec3 viewVector = rotPos - vec3(0.0, 0.0, -u_f);',

// Rotate vector for psi (yaw) and theta (pitch)
'float sinpsi = sin(-u_psi);',
'float cospsi = cos(-u_psi);',
'float sintheta = sin(u_theta);',
'float costheta = cos(u_theta);',
// Now apply the rotations
'vec3 viewVectorTheta = viewVector;',
'viewVectorTheta.z = viewVector.z * costheta - viewVector.y * sintheta;',
'viewVectorTheta.y = viewVector.z * sintheta + viewVector.y * costheta;',
'vec3 viewVectorPsi = viewVectorTheta;',
'viewVectorPsi.x = viewVectorTheta.x * cospsi - viewVectorTheta.z * sinpsi;',
'viewVectorPsi.z = viewVectorTheta.x * sinpsi + viewVectorTheta.z * cospsi;',

// Look up color from texture
'gl_FragColor = textureCube(u_image, viewVectorPsi);',
'}'
].join('\n');

// Fragment shader
var fragEquirectangular = [
var fragEquiCubeBase = [
'precision mediump float;',

'uniform float u_aspectRatio;',
@@ -1238,10 +1214,14 @@ var fragEquirectangular = [

// Texture
'uniform sampler2D u_image;',
'uniform samplerCube u_imageCube;',

// Coordinates passed in from vertex shader
'varying vec2 v_texCoord;',

// Background color (display for partial panoramas)
'uniform vec4 u_backgroundColor;',

'void main() {',
// Map canvas/camera to sphere
'float x = v_texCoord.x * u_aspectRatio;',
@@ -1256,20 +1236,28 @@ var fragEquirectangular = [
'float root = sqrt(rot_x * rot_x + a * a);',
'float lambda = atan(rot_x / root, a / root) + u_psi;',
'float phi = atan((rot_y * costheta + u_f * sintheta) / root);',
].join('\n');

// Fragment shader
var fragCube = fragEquiCubeBase + [
// Look up color from texture
'float cosphi = cos(phi);',
'gl_FragColor = textureCube(u_imageCube, vec3(cosphi*sin(lambda), sin(phi), cosphi*cos(lambda)));',
'}'
].join('\n');

// Fragment shader
var fragEquirectangular = fragEquiCubeBase + [
// Wrap image
'if(lambda > PI)',
'lambda = lambda - PI * 2.0;',
'if(lambda < -PI)',
'lambda = lambda + PI * 2.0;',
'lambda = mod(lambda + PI, PI * 2.0) - PI;',

// Map texture to sphere
'vec2 coord = vec2(lambda / PI, phi / (PI / 2.0));',

// Look up color from texture
// 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 = vec4(0, 0, 0, 1.0);',
'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)));',
'}'


+ 829
- 269
src/js/pannellum.js
File diff suppressed because it is too large
Näytä tiedosto


+ 13
- 8
src/standalone/standalone.js Näytä tiedosto

@@ -7,8 +7,14 @@ function anError(error) {

var viewer;
function parseURLParameters() {
var URL = decodeURI(window.location.href).split('?');
URL.shift();
var URL;
if (window.location.hash.length > 0) {
// Prefered method since parameters aren't sent to server
URL = [window.location.hash.slice(1)];
} else {
URL = decodeURI(window.location.href).split('?');
URL.shift();
}
if (URL.length < 1) {
// Display error if no configuration parameters are specified
anError('No configuration options were specified.');
@@ -30,23 +36,19 @@ function parseURLParameters() {
case 'autoLoad': case 'ignoreGPanoXMP':
configFromURL[option] = JSON.parse(value);
break;
case 'tour':
console.log('The `tour` parameter is deprecated and will be removed. Use the `config` parameter instead.')
case 'author': case 'title': case 'firstScene': case 'fallback':
case 'preview': case 'panorama': case 'config':
configFromURL[option] = decodeURIComponent(value);
break;
default:
anError('An invalid configuration parameter was specified: ' + option);
return;
}
}

var request;

// Check for JSON configuration file
if (configFromURL.tour) {
configFromURL.config = configFromURL.tour;
}
if (configFromURL.config) {
// Get JSON configuration file
request = new XMLHttpRequest();
@@ -63,7 +65,8 @@ function parseURLParameters() {
var responseMap = JSON.parse(request.responseText);

// Set JSON file location
responseMap.basePath = configFromURL.config.substring(0, configFromURL.config.lastIndexOf('/')+1);
if (responseMap.basePath === undefined)
responseMap.basePath = configFromURL.config.substring(0, configFromURL.config.lastIndexOf('/')+1);

// Merge options
for (var key in responseMap) {
@@ -78,6 +81,7 @@ function parseURLParameters() {
document.title = configFromURL.title;

// Create viewer
configFromURL.escapeHTML = true;
viewer = pannellum.viewer('container', configFromURL);
};
request.open('GET', configFromURL.config);
@@ -90,6 +94,7 @@ function parseURLParameters() {
document.title = configFromURL.title;

// Create viewer
configFromURL.escapeHTML = true;
pannellum.viewer('container', configFromURL);
}



+ 1
- 1
utils/build/build.py Näytä tiedosto

@@ -98,7 +98,7 @@ def build(files, css, html, filename, release=False):
js = merge(files)
if release:
version = read('../VERSION')
version = read('../VERSION').strip()
else:
version = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode('utf-8').strip()
js = js.replace('"_blank">Pannellum</a>','"_blank">Pannellum</a> ' + version)


+ 7
- 3
utils/multires/generate.py Näytä tiedosto

@@ -4,7 +4,7 @@
# and nona (from Hugin)

# generate.py - A multires tile set generator for Pannellum
# Copyright (c) 2014-2016 Matthew Petroff
# Copyright (c) 2014-2017 Matthew Petroff
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -34,8 +34,12 @@ import math
from distutils.spawn import find_executable
import subprocess

# find external programs
nona = find_executable('nona')
# Find external programs
try:
nona = find_executable('nona')
except KeyError:
# Handle case of PATH not being set
nona = None

# Parse input
parser = argparse.ArgumentParser(description='Generate a Pannellum multires tile set from an full equirectangular panorama.',


+ 10
- 11
utils/video/videojs-pannellum-plugin.js Näytä tiedosto

@@ -1,26 +1,25 @@
/*
* Video.js plugin for Pannellum
* Copyright (c) 2015-2016 Matthew Petroff
* Copyright (c) 2015-2017 Matthew Petroff
* MIT License
*/

(function(document, videojs, pannellum) {
'use strict';

videojs.plugin('pannellum', function() {
videojs.plugin('pannellum', function(config) {
// Create Pannellum instance
var player = this;
var container = player.el();
var vid = container.getElementsByTagName('video')[0],
pnlmContainer = document.createElement('div'),
config = {
'type': 'equirectangular',
'dynamic': true,
'showZoomCtrl': false,
'showFullscreenCtrl': false,
'autoLoad': true,
'panorama': vid
};
pnlmContainer = document.createElement('div');
config = config || {};
config.type = 'equirectangular';
config.dynamic = true;
config.showZoomCtrl = false;
config.showFullscreenCtrl = false;
config.autoLoad = true;
config.panorama = vid;
pnlmContainer.style.visibility = 'hidden';
var pnlm = pannellum.viewer(pnlmContainer, config);
container.insertBefore(pnlmContainer, container.firstChild);


Ladataan…
Peruuta
Tallenna