diff --git a/examples/examplecube.htm b/examples/examplecube.htm
new file mode 100644
index 0000000..5e3fb20
--- /dev/null
+++ b/examples/examplecube.htm
@@ -0,0 +1,10 @@
+
+
+
+ pannellum embed example
+
+
+
+
+
+
diff --git a/examples/examplepanocubeXn.jpg b/examples/examplepanocubeXn.jpg
new file mode 100644
index 0000000..f7c6098
Binary files /dev/null and b/examples/examplepanocubeXn.jpg differ
diff --git a/examples/examplepanocubeXp.jpg b/examples/examplepanocubeXp.jpg
new file mode 100644
index 0000000..80db892
Binary files /dev/null and b/examples/examplepanocubeXp.jpg differ
diff --git a/examples/examplepanocubeYn.jpg b/examples/examplepanocubeYn.jpg
new file mode 100644
index 0000000..058e48b
Binary files /dev/null and b/examples/examplepanocubeYn.jpg differ
diff --git a/examples/examplepanocubeYp.jpg b/examples/examplepanocubeYp.jpg
new file mode 100644
index 0000000..8aed1e5
Binary files /dev/null and b/examples/examplepanocubeYp.jpg differ
diff --git a/examples/examplepanocubeZn.jpg b/examples/examplepanocubeZn.jpg
new file mode 100644
index 0000000..2e2281e
Binary files /dev/null and b/examples/examplepanocubeZn.jpg differ
diff --git a/examples/examplepanocubeZp.jpg b/examples/examplepanocubeZp.jpg
new file mode 100644
index 0000000..b00d5c1
Binary files /dev/null and b/examples/examplepanocubeZp.jpg differ
diff --git a/src/js/libpannellum.js b/src/js/libpannellum.js
index e11fd27..bb394a9 100644
--- a/src/js/libpannellum.js
+++ b/src/js/libpannellum.js
@@ -22,21 +22,33 @@
*/
window.libpannellum = (function(window, document, undefined) {
-function Renderer(canvas, image) {
+/* Image Type argument can be that of "equirectangular", "cubemap"
+ * If "cubemap" is used, the image argument should be an array of images
+ * instead of a single image. They should be the order of:
+ * +x -x +y -y +z -z
+ */
+function Renderer(canvas, image, imageType) {
this.canvas = canvas;
this.image = image;
-
+
+ //Default argument for image type
+ this.imageType = "equirectangular";
+ if( typeof imageType != "undefined" ){
+ this.imageType = imageType;
+ }
+
var program, gl;
-
+
this.init = function(haov, vaov, voffset) {
// Enable WebGL on canvas
gl = this.canvas.getContext('experimental-webgl');
-
+ var glBindType = gl.TEXTURE_2D;
+
// Create viewport for entire canvas and clear canvas
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
-
+
// Create vertex shader
var vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, v);
@@ -44,7 +56,12 @@ function Renderer(canvas, image) {
// Create fragment shader
var fs = gl.createShader(gl.FRAGMENT_SHADER);
- gl.shaderSource(fs, f);
+ var fragmentSrc = fragEquirectangular;
+ if( this.imageType == "cubemap"){
+ glBindType = gl.TEXTURE_CUBE_MAP
+ fragmentSrc = fragCube;
+ }
+ gl.shaderSource(fs, fragmentSrc);
gl.compileShader(fs);
// Link WebGL program
@@ -90,21 +107,32 @@ function Renderer(canvas, image) {
gl.uniform1f(program.h, haov / (Math.PI * 2.0));
gl.uniform1f(program.v, vaov / Math.PI);
gl.uniform1f(program.vo, voffset / Math.PI);
-
+
// Create texture
program.texture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, program.texture);
-
+ gl.bindTexture(glBindType, program.texture);
+
+ //upload images to texture depending on type
+ if( this.imageType == "cubemap" ){
+ //Load all 6 sides of the cube map
+ gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image[0]);
+ gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image[1]);
+ gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image[2]);
+ gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image[3]);
+ gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image[4]);
+ gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image[5]);
+ }else{
+ // Upload image to the texture
+ gl.texImage2D(glBindType, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image);
+ }
+
// Set parameters for rendering any size
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
-
- // Upload image to texture
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image);
+ 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);
}
-
+
this.render = function(pitch, yaw, hfov) {
// Calculate focal length from horizontal angle of view
var focal = 1 / Math.tan(hfov / 2);
@@ -117,7 +145,7 @@ function Renderer(canvas, image) {
// Draw using current buffer
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
-
+
this.setImage = function(image) {
this.image = image;
this.init();
@@ -144,7 +172,52 @@ var v = [
].join('');
// Fragment shader
-var f = [
+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;',
+
+'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;',
+ 'vec3 viewVector = planePos - 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;',
+
+ //lookup the color
+ 'gl_FragColor = textureCube(u_image, viewVectorPsi);',
+'}'
+].join('\n');
+
+// Fragment shader
+var fragEquirectangular = [
'precision mediump float;',
'uniform float u_aspectRatio;',
@@ -173,7 +246,7 @@ var f = [
'float root = sqrt(x * x + a * a);',
'float lambda = atan(x / root, a / root) + u_psi;',
'float phi = atan((y * costheta + u_f * sintheta) / root);',
-
+
// Wrap image
'if(lambda > PI)',
'lambda = lambda - PI * 2.0;',
@@ -193,8 +266,8 @@ var f = [
].join('\n');
return {
- renderer: function(canvas, image) {
- return new Renderer(canvas, image);
+ renderer: function(canvas, image, imagetype) {
+ return new Renderer(canvas, image, imagetype);
}
}
diff --git a/src/js/pannellum.js b/src/js/pannellum.js
index eb765ec..1637dba 100644
--- a/src/js/pannellum.js
+++ b/src/js/pannellum.js
@@ -148,10 +148,20 @@ if(getURLParameter('autorotate') == 'ccw') {
}
function init() {
- var panoimage = new Image()
- panoimage.onload = function() {
+ var panotype = getURLParameter('panotype');
+ if( panotype == '' ) panotype = "equirectangular";
+ var panoimage;
+
+ if( panotype != "cubemap" ){
+ panoimage = new Image();
+ }else{
+ panoimage = new Array();
+ for( var i=0; i < 6; i++ )panoimage.push( new Image() );
+ }
+
+ function finishloadimage() {
try {
- renderer = new libpannellum.renderer(canvas, panoimage);
+ renderer = new libpannellum.renderer(canvas, panoimage, panotype);
} catch (event) {
// show error message if WebGL is not supported
anError();
@@ -180,11 +190,29 @@ function init() {
renderInit();
var t=setTimeout('isTimedOut = true',500);
-
+
setInterval('keyRepeat()',10);
- };
- panoimage.src = getURLParameter('panorama');
+ }
+ //set event handlers
+ if( panotype != "cubemap" ){
+ panoimage.onload = finishloadimage;
+ panoimage.src = getURLParameter('panorama');
+ }else{
+ //quick loading counter for syncronous loading
+ var itemstoload = 6;
+ function loadCounter(){
+ itemstoload --;
+ if( itemstoload == 0 ){
+ finishloadimage();
+ }
+ }
+ //set the onload and src
+ for(var i=0; i < panoimage.length; i++){
+ panoimage[i].onload = loadCounter;
+ panoimage[i].src = getURLParameter('panorama' + i.toString());
+ }
+ }
document.getElementById('page').className = 'grab';
}
@@ -227,9 +255,10 @@ function onDocumentMouseDown(event) {
}
function onDocumentMouseMove(event) {
- if (isUserInteracting) {
- yaw = (onPointerDownPointerX - event.clientX) * 0.1 + onPointerDownYaw;
- pitch = (event.clientY - onPointerDownPointerY) * 0.1 + onPointerDownPitch;
+ if (isUserInteracting) {
+ //TODO: This should not only be FOV scaled but scaled to canvas size
+ yaw = (onPointerDownPointerX - event.clientX) * 0.0029 * hfov + onPointerDownYaw;
+ pitch = (event.clientY - onPointerDownPointerY) * 0.0029 * hfov + onPointerDownPitch;
animate();
}
}