360 Panorama image in spherical object.
// sphere panorama control
window.addEventListener("load", function () {
"use strict";
// prepare screen
var supportOrientation = 'orientation' in screen || 'orientation' in window;
var forceSphericalTexture = false;
var forceGyroscope = false;
var renderer = new THREE.WebGLRenderer();
var view = document.getElementById("sphericalTexture");
var w = view.offsetWidth, h = view.offsetHeight;
renderer.setSize(w, h);
var onPointerDownPointerX = 0,
onPointerDownPointerY = 0,
onPointerDownLon = 0,
onPointerDownLat = 0;
// prepare camera
var camera = new THREE.PerspectiveCamera(75, w / h, 1, 1000);
camera.position.set(0, 0, 0);
camera.up.set(0, 1, 0);
camera.lookAt(new THREE.Vector3(0, 0, -1));
// load texture
// touch devices
view.addEventListener('touchstart', loadSphericalTexture, false);
// load texture
// mouse
view.addEventListener('click', loadSphericalTexture, false);
// load texture function
function loadSphericalTexture() {
if (!forceSphericalTexture) {
view.appendChild(renderer.domElement);
forceSphericalTexture = true;
};
if (supportOrientation && window.DeviceOrientationEvent) {
view.classList.add('gyroscope');
elementEnterFullScreen();
};
};
view.addEventListener("touchstart", function (event) {
onPointerDownPointerX = event.changedTouches[0].pageX;
onPointerDownPointerY = event.changedTouches[0].pageY;
onPointerDownLon = lon;
onPointerDownLat = lat;
view.addEventListener("touchmove", changedTouches, false);
}, false);
view.addEventListener("touchend", function (event) {
view.removeEventListener("touchmove", changedTouches, false);
}, false);
// force gyroscope orientation
if (supportOrientation && window.DeviceOrientationEvent) {
document.getElementById('toggleGyroscope').addEventListener('touchstart', toggleGyroscope, false);
};
view.addEventListener("mousedown", function (event) {
view.addEventListener("mousemove", gyroMouse, false);
}, false);
view.addEventListener("mouseup", function (event) {
view.removeEventListener("mousemove", gyroMouse, false);
}, false);
// resize
window.addEventListener('resize', onWindowResized, false);
function onWindowResized(event) {
renderer.setSize(view.offsetWidth, view.offsetHeight);
}
// enter fullscreen
function elementEnterFullScreen() {
screen.orientation.lock('landscape');
if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) {
if (view.requestFullscreen) {
view.requestFullscreen();
} else if (view.msRequestFullscreen) {
view.msRequestFullscreen();
} else if (view.mozRequestFullScreen) {
view.mozRequestFullScreen();
} else if (view.webkitRequestFullscreen) {
view.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
}
view.classList.add('fullscreen');
}
}
document.getElementById('sphericalFullscreenClose').addEventListener('touchstart', elementExitFullScreen, false);
function elementExitFullScreen(event) {
event.preventDefault();
event.stopPropagation();
screen.orientation.unlock();
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
view.classList.remove('fullscreen');
}
// fullscreenchange
view.addEventListener("mozfullscreenchange", function(event) {
onFullScreenExit();
});
view.addEventListener("webkitfullscreenchange", function(event) {
onFullScreenExit();
});
view.addEventListener("fullscreenchange", function(event) {
onFullScreenExit();
});
function onFullScreenExit() {
if ( (document.fullscreenEnabled || document.mozFullscreenEnabled || document.webkitIsFullScreen) === false ) {
screen.orientation.unlock();
view.classList.remove('fullscreen');
};
}
function toggleGyroscope(event) {
event.preventDefault();
event.stopPropagation();
if (!forceGyroscope) {
forceGyroscope = true;
window.addEventListener("deviceorientation", gyroSensor, false);
} else {
forceGyroscope = false;
window.removeEventListener("deviceorientation", gyroSensor, false);
};
}
// camera rotation by mouse
var lon = 0;
var lat = 0;
var gyroMouse = function (event) {
var mx = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var my = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
lat = Math.min(Math.max(-Math.PI / 2, lat - my * 0.01), Math.PI / 2);
lon = lon - mx * 0.01;
var rotm = new THREE.Quaternion().setFromEuler(new THREE.Euler(lat, lon, 0, "YXZ"));
camera.quaternion.copy(rotm);
};
var onPointerDownLon = lon;
var onPointerDownLat = lat;
var changedTouches = function (event) {
event.preventDefault();
lon = (event.changedTouches[0].pageX - onPointerDownPointerX) * -0.01 + onPointerDownLon;
lat = (event.changedTouches[0].pageY - onPointerDownPointerY) * -0.01 + onPointerDownLat;
var rotm = new THREE.Quaternion().setFromEuler(new THREE.Euler(lat, lon, 0, "YXZ"));
camera.quaternion.copy(rotm);
};
// camera rotation by direct gyro sensor angles on tablets
// see: http://mdn.io/Detecting_device_orientation
// (work on android firefox and iOS Safari)
var eyem = new THREE.Quaternion().setFromEuler(
new THREE.Euler(-Math.PI / 2, 0, 0));
var d2r = Math.PI / 180;
var getOrientation = function () {
// W3C DeviceOrientation Event Specification (Draft)
if (window.screen.orientation) return window.screen.orientation.angle;
// Safari
if (typeof window.orientation === "number") return window.orientation;
// workaround for android firefox
if (window.screen.mozOrientation) return {
"portrait-primary": 0,
"portrait-secondary": 180,
"landscape-primary": 90,
"landscape-secondary": 270,
}[window.screen.mozOrientation];
// otherwise
return 0;
};
var gyroSensor = function (event) {
event.preventDefault();
var angle = getOrientation();
var alpha = event.alpha || 0;
var beta = event.beta || 0;
var gamma = event.gamma || 0;
if (alpha === 0 && beta === 0 && gamma === 0) return;
// on android chrome: bug as beta may become NaN
// device rot axis order Z-X-Y as alpha, beta, gamma
// portrait mode Z=rear->front(screen), X=left->right, Y=near->far(cam)
// => map Z-X-Y to 3D world axes as:
// - portrait => y-x-z
// - landscape => y-z-x
// var rotType = (angle === 0 || angle === 180) ? "YXZ" : "YZX";
var rotType = 'YXZ';
var rotm = new THREE.Quaternion().setFromEuler(new THREE.Euler(beta * d2r, alpha * d2r, -gamma * d2r, rotType));
var devm = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, -angle * d2r, 0));
rotm.multiply(devm).multiply(eyem); //rot = (rot x dev) x eye
camera.quaternion.copy(rotm);
};
// panorama image texture
var img = document.createElement("img");
img.crossOrigin = "anonymous";
var tex = new THREE.Texture(img);
img.addEventListener("load", function () {
tex.needsUpdate = true;
}, false);
var mat = new THREE.MeshBasicMaterial({map: tex});
var geom = new THREE.SphereGeometry(500, 64, 32); // sphere type
geom.applyMatrix(new THREE.Matrix4().makeScale(1, 1, -1)); //surface inside
var obj = new THREE.Mesh(geom, mat);
// create scene
var scene = new THREE.Scene();
scene.add(obj);
var replaceTexture = function () {
var source = view.dataset.image;
var img = document.createElement("img");
img.crossOrigin = "anonymous";
img.src = source;
var tex = new THREE.Texture(img);
mat.map = tex;
img.addEventListener("load", function () {
tex.needsUpdate = true;
mat.map.needsUpdate = true; // important for replacing textures
}, false);
};
replaceTexture();
// play animation
var loop = function loop() {
requestAnimationFrame(loop);
renderer.clear();
renderer.render(scene, camera);
};
loop();
}, false);