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(); } }