photo-sphere-viewer
Version:
A JavaScript library to display Photo Sphere panoramas
165 lines (133 loc) • 3.58 kB
JavaScript
import { Euler, MathUtils, Matrix4, Mesh, PlaneBufferGeometry, ShaderMaterial, Texture } from 'three';
import { CONSTANTS, DEFAULTS, EquirectangularAdapter } from '../..';
DEFAULTS.defaultLat = -Math.PI / 2;
const euler = new Euler();
/**
* @summary Adapter for equirectangular panoramas displayed with little planet effect
* @memberof PSV.adapters
* @extends PSV.adapters.AbstractAdapter
*/
export class LittlePlanetAdapter extends EquirectangularAdapter {
static id = 'little-planet';
static supportsOverlay = false;
/**
* @param {PSV.Viewer} psv
*/
constructor(psv) {
super(psv);
this.psv.prop.littlePlanet = true;
this.psv.on(CONSTANTS.EVENTS.SIZE_UPDATED, this);
this.psv.on(CONSTANTS.EVENTS.ZOOM_UPDATED, this);
this.psv.on(CONSTANTS.EVENTS.POSITION_UPDATED, this);
}
/**
* @override
*/
supportsTransition() {
return false;
}
/**
* @override
*/
supportsPreload() {
return true;
}
/**
* @private
*/
handleEvent(e) {
/* eslint-disable */
switch (e.type) {
case CONSTANTS.EVENTS.SIZE_UPDATED:
this.__setResolution(e.args[0]);
break;
case CONSTANTS.EVENTS.ZOOM_UPDATED:
this.__setZoom();
break;
case CONSTANTS.EVENTS.POSITION_UPDATED:
this.__setPosition(e.args[0]);
break;
}
/* eslint-enable */
}
/**
* @param {PSV.Size} size
* @private
*/
__setResolution(size) {
this.uniforms.resolution.value = size.width / size.height;
}
/**
* @private
*/
__setZoom() {
// mapping values are empirical
this.uniforms.zoom.value = Math.max(0.1, MathUtils.mapLinear(this.psv.prop.vFov, 90, 30, 50, 2));
}
/**
* @param {PSV.Position} position
* @private
*/
__setPosition(position) {
euler.set(
Math.PI / 2 + position.latitude,
0,
-Math.PI / 2 - position.longitude,
'ZYX'
);
this.uniforms.transform.value.makeRotationFromEuler(euler);
}
/**
* @override
*/
createMesh() {
const geometry = new PlaneBufferGeometry(20, 10)
.translate(0, 0, -1);
// this one was copied from https://github.com/pchen66/panolens.js
const material = new ShaderMaterial({
uniforms: {
panorama : { value: new Texture() },
resolution: { value: 2.0 },
transform : { value: new Matrix4() },
zoom : { value: 10.0 },
opacity : { value: 1.0 },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4( position, 1.0 );
}`,
fragmentShader: `
uniform sampler2D panorama;
uniform float resolution;
uniform mat4 transform;
uniform float zoom;
uniform float opacity;
varying vec2 vUv;
const float PI = 3.1415926535897932384626433832795;
void main() {
vec2 position = -1.0 + 2.0 * vUv;
position *= vec2( zoom * resolution, zoom * 0.5 );
float x2y2 = position.x * position.x + position.y * position.y;
vec3 sphere_pnt = vec3( 2. * position, x2y2 - 1. ) / ( x2y2 + 1. );
sphere_pnt = vec3( transform * vec4( sphere_pnt, 1.0 ) );
vec2 sampleUV = vec2(
1.0 - (atan(sphere_pnt.y, sphere_pnt.x) / PI + 1.0) * 0.5,
(asin(sphere_pnt.z) / PI + 0.5)
);
gl_FragColor = texture2D( panorama, sampleUV );
gl_FragColor.a *= opacity;
}`,
});
this.uniforms = material.uniforms;
return new Mesh(geometry, material);
}
/**
* @override
*/
setTexture(mesh, textureData) {
mesh.material.uniforms.panorama.value.dispose();
mesh.material.uniforms.panorama.value = textureData.texture;
}
}