viewer/legacy/reconstruction.html (1,270 lines of code) (raw):

<!DOCTYPE html> <html lang="en"> <head> <title>Reconstruction Viewer</title> <meta charset="utf-8"> <style> body { margin: 0px; } #load_button { position: absolute; top: 50%; left: 50%; width: 300px; height: 120px; margin-top: -60px; /* Half the height */ margin-left: -150px; /* Half the width */ } #loading { position: absolute; top: 50%; left: 50%; width: 245px; height: 148px; margin-top: -74px; /* Half the height */ margin-left: -122px; /* Half the width */ display: none; } #info { position: absolute; top: 0px; left: 10px; background-color: #000; visibility: hidden; } #image { width: 160px; height: 120px; } #text { text-align: center; font-family: sans-serif; font-size: 10px; color: #FFF; } #reconstruction { text-align: center; font-family: sans-serif; font-size: 10px; color: #FFF; } #navigation { position: absolute; top: 0px; left: 10px; display: none; font-family: sans-serif; font-size: 30px; } </style> </head> <body> <div id="ThreeJS" target="_blank"></div> <input id='load_button' type="file" value="open reconstruction" /> <div id="loading"> <img src="images/preloader.gif"> </div> <div id="info"> <img id="image"> <div id="text"></div> <div id="reconstruction"></div> </div> <div id="navigation"> <table> <tr> <td id="nav-turn-left"> <button onclick="walkOneStep('TURN_LEFT')"> &#8624; </button> </td> <td id="nav-forward"> <button onclick="walkOneStep('STEP_FORWARD')"> &#8593; </button> </td> <td id="nav-turn-right"> <button onclick="walkOneStep('TURN_RIGHT')"> &#8625; </button> </td> </tr> <tr> <td id="nav-left"> <button onclick="walkOneStep('STEP_LEFT')"> &#8592; </button> </td> <td id="nav-u-turn"> <button onclick="walkOneStep('TURN_U')"> &#8630; </button> </td> <td id="nav-right"> <button onclick="walkOneStep('STEP_RIGHT')"> &#8594; </button> </td> </tr> <tr> <td> </td> <td id="nav-backward"> <button onclick="walkOneStep('STEP_BACKWARD')"> &#8595; </button> </td> <td> <button onclick="setMovingMode('orbit')"> fly </button> </td> </tr> </table> </div> <script src="js/jquery.js"></script> <script src="js/three.js"></script> <script src="js/OrbitControls.js"></script> <script src="js/TrackballControls.js"></script> <script src="js/dat.gui.js"></script> <!-- Shaders --> <script type="x-shader/x-vertex" id="vertexshader"> // switch on high precision floats #ifdef GL_ES precision highp float; #endif varying vec4 vRstq; uniform mat4 projectorMat; void main() { vRstq = projectorMat * vec4(position, 1.0); gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); } </script> <script type="x-shader/x-fragment" id="fragmentshader"> #ifdef GL_ES precision highp float; #endif varying vec4 vRstq; uniform sampler2D projectorTex; uniform float opacity; uniform float focal; uniform float k1; uniform float k2; uniform float scale_x; uniform float scale_y; void main() { float x = vRstq.x / vRstq.z; float y = vRstq.y / vRstq.z; float r2 = x * x + y * y; float d = 1.0 + k1 * r2 + k2 * r2 * r2; float u = scale_x * focal * d * x + 0.5; float v = - scale_y * focal * d * y + 0.5; vec4 baseColor = texture2D(projectorTex, vec2(u, v)); baseColor.a = opacity; gl_FragColor = baseColor; } </script> <script type="x-shader/x-fragment" id="fragmentshader_fisheye"> #ifdef GL_ES precision highp float; #endif varying vec4 vRstq; uniform sampler2D projectorTex; uniform float opacity; uniform float focal; uniform float k1; uniform float k2; uniform float scale_x; uniform float scale_y; void main() { float x = vRstq.x; float y = vRstq.y; float z = vRstq.z; float a = x / z; float b = y / z; float r = sqrt(a * a + b * b); float theta = atan(r); float theta2 = theta * theta; float theta_d = theta * (1.0 + theta2 * (k1 + theta2 * k2)); float inv_r = r > 1e-8 ? 1.0 / r : 1.0; float cdist = r > 1e-8 ? theta_d * inv_r : 1.0; float x_p = cdist * a; float y_p = cdist * b; float u = scale_x * focal * x_p + 0.5; float v = -scale_y * focal * y_p + 0.5; vec4 baseColor = texture2D(projectorTex, vec2(u, v)); baseColor.a = opacity; gl_FragColor = baseColor; } </script> <script type="x-shader/x-fragment" id="fragmentshader_fisheye_opencv"> #ifdef GL_ES precision highp float; #endif varying vec4 vRstq; uniform sampler2D projectorTex; uniform float opacity; uniform float focal_x; uniform float focal_y; uniform float c_x; uniform float c_y; uniform float k1; uniform float k2; uniform float k3; uniform float k4; uniform float scale_x; uniform float scale_y; void main() { float x = vRstq.x; float y = vRstq.y; float z = vRstq.z; float a = x / z; float b = y / z; float r = sqrt(a * a + b * b); float theta = atan(r); float theta2 = theta * theta; float theta_d = theta * (1.0 + theta2 * (k1 + theta2 * (k2 + theta2 * (k3 + theta2 * k4)))); float inv_r = r > 1e-8 ? 1.0 / r : 1.0; float cdist = r > 1e-8 ? theta_d * inv_r : 1.0; float x_p = cdist * a; float y_p = cdist * b; float u = scale_x * focal_x * x_p + c_x + 0.5; float v = -scale_y * focal_y * y_p + c_y + 0.5; vec4 baseColor = texture2D(projectorTex, vec2(u, v)); baseColor.a = opacity; gl_FragColor = baseColor; } </script> <script type="x-shader/x-fragment" id="fragmentshader_equirectangular"> #ifdef GL_ES precision highp float; #endif #define tau 6.28318530718 varying vec4 vRstq; uniform sampler2D projectorTex; uniform float opacity; void main() { vec3 b = normalize(vRstq.xyz); float lat = -asin(b.y); float lon = atan(b.x, b.z); float x = lon / tau + 0.5; float y = lat / tau * 2.0 + 0.5; vec4 baseColor = texture2D(projectorTex, vec2(x, y)); baseColor.a = opacity; gl_FragColor = baseColor; } </script> <script> var urlParams; var f1; var container, camera, controls, scene, renderer; var mouse = new THREE.Vector2(); var hoverCamera, raycaster, parentTransform; var selectedCamera; var imagePlane, imagePlaneCamera; var imagePlaneOld, imagePlaneCameraOld; var scene_group, grid_group; var pointCloudMaterial; var reconstructions; var reconstruction_visibles = []; var reconstruction_groups = []; var point_clouds = []; var camera_lines = []; var gps_lines = []; var imagePlanes = []; var imagePlaneCameras = []; var imageMaterials = []; var num_preview_plane = 5; var moveSpeed = 0.2; var turnSpeed = 0.1; var previousShot = undefined; var validMoves; var movingMode = 'orbit'; var savedOptions = { cameraSize: 0, pointSize: 0, showThumbnail: false, showImagePlane: true, drawGrid: false, drawGPS: false }; var options = { cameraSize: 0.9, pointSize: 0.7, imagePlaneSize: 50, showThumbnail: true, showImagePlane: false, drawGrid: true, drawGPS: false, animationSpeed: 0.1, imagePlaneOpacity: 1, cameraColor: new THREE.Color(0xFFFFFF), hoverCameraColor: new THREE.Color(0xFF8888), selectedCameraColor: new THREE.Color(0xFFFF88), reconstruction_visibles: {}, resolution: 'original', allNone: function () { var someone = false; for (var r = 0; r < reconstructions.length; ++r) { if (options.reconstruction_visibles[r]) { someone = true; break; } } for (var r = 0; r < reconstructions.length; ++r) { options.reconstruction_visibles[r] = !someone; reconstruction_groups[r].traverse(function (object) { object.visible = !someone; }); } render(); } }; document.getElementById('load_button').addEventListener('change', onReconstructionFileSelected, false); processUrlParams(); function addDatGui(){ var gui = new dat.GUI(); f1 = gui.addFolder('Options'); f1.add(options, 'pointSize', 0, 2) .listen() .onChange(setPointSize); f1.add(options, 'cameraSize', 0, 2) .listen() .onChange(setCameraSize); f1.add(options, 'imagePlaneSize', 1, 400) .onChange(function(value) { options.imagePlaneSize *= 1.5; imagePlane.geometry = imagePlaneGeo(imagePlaneCameraOld.reconstruction, imagePlaneCameraOld.shot_id); options.imagePlaneSize /= 1.5; imagePlane.geometry = imagePlaneGeo(imagePlaneCamera.reconstruction, imagePlaneCamera.shot_id); render(); }); f1.add(options, 'animationSpeed', 0, 0.2) .onChange(function(value) { controls.animationSpeed = value; }); f1.add(options, 'resolution', [ '320', '640', 'original' ] ); f1.add(options, 'showThumbnail') .listen() .onChange(setShowThumbnail); f1.add(options, 'drawGrid') .listen() .onChange(setDrawGrid); f1.add(options, 'showImagePlane') .listen() .onChange(setShowImagePlane); f1.add(options, 'drawGPS') .listen() .onChange(setDrawGPS); f1.open(); var f3 = gui.addFolder('Reconstructions') f3.add(options, 'allNone'); options.reconstruction_visibles = []; for (var r = 0; r < reconstructions.length; ++r) { options.reconstruction_visibles[r] = true; f3.add(options.reconstruction_visibles, r, true) .onChange( (function(rr) { return function (value) { reconstruction_groups[rr].traverse( function (object) { object.visible = value; } ); render(); } })(r) ).listen(); } f3.close(); gui.close(); } function setPointSize(value) { options.pointSize = value; pointCloudMaterial.size = value; for (var i = 0; i < point_clouds.length; ++i) { point_clouds[i].visible = (value > 0); } render(); } function setCameraSize(value) { options.cameraSize = value; for (var r = 0; r < reconstructions.length; ++r) { updateCameraLines(reconstructions[r]); } render(); } function setShowThumbnail(value) { options.showThumbnail = value; $('#info').css('visibility', value ? 'visible' : 'hidden'); } function setShowImagePlane(value) { options.showImagePlane = value; imagePlane.visible = value; if (movingMode === 'walk') { imagePlaneOld.visible = value; } else { imagePlaneOld.visible = false; } render(); } function setDrawGrid(value) { options.drawGrid = value; grid_group.visible = value; render(); } function setDrawGPS(value) { options.drawGPS = value; for (var i = 0; i < gps_lines.length; ++i) { gps_lines[i].visible = value; } render(); } function setMovingMode(mode) { if (mode != movingMode) { movingMode = mode; if (mode == 'orbit') { resetWalkMode(); swapOptions(); controls.noRotate = false; controls.noLookAround = false; controls.noPan = false; controls.noZoom = false; controls.noKeys = false; controls.animationPosition.z += 10; controls.dollyOut(4); imagePlane.material.depthWrite = true; imagePlaneOld.material.depthWrite = true; $('#navigation').hide(); } else if (mode == 'walk') { swapOptions(); // controls.noRotate = true; // controls.noLookAround = true; // controls.noPan = true; // controls.noZoom = true; // controls.noKeys = true; imagePlane.material.depthWrite = false; imagePlaneOld.material.depthWrite = false; $('#navigation').show(); } } } function resetWalkMode() { previousShot = undefined; } function swapOptions() { var tmpOptions = { pointSize: savedOptions.pointSize, cameraSize: savedOptions.cameraSize, showThumbnail: savedOptions.showThumbnail, showImagePlane: savedOptions.showImagePlane, drawGrid: savedOptions.drawGrid, drawGPS: savedOptions.drawGPS }; savedOptions.pointSize = options.pointSize; savedOptions.cameraSize = options.cameraSize; savedOptions.showThumbnail = options.showThumbnail; savedOptions.showImagePlane = options.showImagePlane; savedOptions.drawGrid = options.drawGrid; savedOptions.drawGPS = options.drawGPS; setPointSize(tmpOptions.pointSize); setCameraSize(tmpOptions.cameraSize); setShowThumbnail(tmpOptions.showThumbnail); setShowImagePlane(tmpOptions.showImagePlane); setDrawGrid(tmpOptions.drawGrid); setDrawGPS(tmpOptions.drawGPS); } function imageURL(shot_id) { var url = urlParams.file; if (url === undefined) return; var slash = url.lastIndexOf('/'); var imagePath = '/images' + options.resolution.replace('original', '') return url.substring(0, slash) + imagePath +'/' + shot_id; } function parseUrl() { var match, pl = /\+/g, // Regex for replacing addition symbol with a space search = /([^&=]+)=?([^&]*)/g, decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, hash = window.location.hash.substring(1); urlParams = {}; while (match = search.exec(hash)) urlParams[decode(match[1])] = decode(match[2]); } function processUrlParams() { parseUrl(); if ('res' in urlParams) options.resolution = urlParams.res; if (urlParams.file !== undefined) { loadReconstructionFromURL(urlParams.file); } } function onReconstructionFileSelected(evt) { let file = evt.target.files[0]; console.log(file); $('#load_button').hide(); $('#loading').show(); var reader = new FileReader(); reader.onload = function(e) { data = JSON.parse(e.target.result); setReconstructionData(data); }; reader.readAsText(file); } function loadReconstructionFromURL(file_path) { $('#load_button').hide(); $('#loading').show(); jQuery.getJSON(file_path, setReconstructionData); } function setReconstructionData(data) { if ('cameras' in data) { reconstructions = [data]; } else { reconstructions = data; } $('#loading').hide(); init(); animate(); } function rotate(vector, angleaxis) { var v = new THREE.Vector3(vector[0], vector[1], vector[2]); var axis = new THREE.Vector3(angleaxis[0], angleaxis[1], angleaxis[2]); var angle = axis.length(); axis.normalize(); var matrix = new THREE.Matrix4().makeRotationAxis(axis, angle); v.applyMatrix4(matrix); return v; } function opticalCenter(shot) { var angleaxis = [-shot.rotation[0], -shot.rotation[1], -shot.rotation[2]]; var Rt = rotate(shot.translation, angleaxis); Rt.negate(); return Rt; } function viewingDirection(shot) { var angleaxis = [-shot.rotation[0], -shot.rotation[1], -shot.rotation[2]]; return rotate([0,0,1], angleaxis); } function pixelToVertex(cam, shot, u, v, scale) { // Projection model: // xc = R * x + t // u = focal * xc / zc // v = focal * yc / zc var focal = cam.focal || 0.3; var zc = scale; var xc = u / focal * zc; var yc = v / focal * zc; var xct = [xc - shot.translation[0], yc - shot.translation[1], zc - shot.translation[2]]; var angleaxis = [-shot.rotation[0], -shot.rotation[1], -shot.rotation[2]]; return rotate(xct, angleaxis); } function initCameraLines(reconstruction) { var lines = [] for (var shot_id in reconstruction.shots) { if (reconstruction.shots.hasOwnProperty(shot_id)) { var lineMaterial = new THREE.LineBasicMaterial({size: 0.1 }) var rid = reconstruction_id_of_shot(reconstructions, shot_id); lineMaterial.color = reconstruction_color(rid); var linegeo = cameraLineGeo(reconstruction, shot_id); var line = new THREE.Line(linegeo, lineMaterial, THREE.LinePieces); line.reconstruction = reconstruction; line.shot_id = shot_id; lines.push(line); } } return lines; } function updateCameraLines() { for (var i = 0; i < camera_lines.length; ++i) { var linegeo = cameraLineGeo(camera_lines[i].reconstruction, camera_lines[i].shot_id); camera_lines[i].geometry.vertices = linegeo.vertices; camera_lines[i].geometry.verticesNeedUpdate = true; } } function cameraLineGeo(reconstruction, shot_id) { var shot = reconstruction.shots[shot_id]; var cam = reconstruction.cameras[shot.camera]; var ocenter = opticalCenter(shot); var dx = cam.width / 2.0 / Math.max(cam.width, cam.height); var dy = cam.height / 2.0 / Math.max(cam.width, cam.height); var top_left = pixelToVertex(cam, shot, -dx, -dy, options.cameraSize); var top_right = pixelToVertex(cam, shot, dx, -dy, options.cameraSize); var bottom_right = pixelToVertex(cam, shot, dx, dy, options.cameraSize); var bottom_left = pixelToVertex(cam, shot, -dx, dy, options.cameraSize); var linegeo = new THREE.Geometry(); linegeo.vertices.push(ocenter); linegeo.vertices.push(top_left); linegeo.vertices.push(ocenter); linegeo.vertices.push(top_right); linegeo.vertices.push(ocenter); linegeo.vertices.push(bottom_right); linegeo.vertices.push(ocenter); linegeo.vertices.push(bottom_left); linegeo.vertices.push(top_left); linegeo.vertices.push(top_right); linegeo.vertices.push(top_right); linegeo.vertices.push(bottom_right); // linegeo.vertices.push(bottom_right); // linegeo.vertices.push(bottom_left); linegeo.vertices.push(bottom_left); linegeo.vertices.push(top_left); return linegeo; } function imagePlaneGeo(reconstruction, shot_id) { var shot = reconstruction.shots[shot_id]; var cam = reconstruction.cameras[shot.camera]; if ('vertices' in shot && shot['vertices'].length > 0) { var geometry = new THREE.Geometry(); for (var i = 0; i < shot['vertices'].length; ++i) { geometry.vertices.push( new THREE.Vector3( shot['vertices'][i][0], shot['vertices'][i][1], shot['vertices'][i][2] ) ); } for (var i = 0; i < shot['faces'].length; ++i) { var v0 = shot['faces'][i][0]; var v1 = shot['faces'][i][1]; var v2 = shot['faces'][i][2]; geometry.faces.push(new THREE.Face3(v0, v1, v2)); } return geometry; } else { if (cam.projection_type == "spherical" || cam.projection_type == "equirectangular") { return imageSphereGeoFlat(cam, shot); } else { return imagePlaneGeoFlat(cam, shot); } } } function imagePlaneGeoFlat(cam, shot) { var geometry = new THREE.Geometry(); var dx = cam.width / 2.0 / Math.max(cam.width, cam.height); var dy = cam.height / 2.0 / Math.max(cam.width, cam.height); var top_left = pixelToVertex(cam, shot, -dx, -dy, options.imagePlaneSize); var top_right = pixelToVertex(cam, shot, dx, -dy, options.imagePlaneSize); var bottom_right = pixelToVertex(cam, shot, dx, dy, options.imagePlaneSize); var bottom_left = pixelToVertex(cam, shot, -dx, dy, options.imagePlaneSize); geometry.vertices.push( top_left, bottom_left, bottom_right, top_right ); geometry.faces.push( new THREE.Face3(0, 1, 3), new THREE.Face3(1, 2, 3) ); return geometry; } function imageSphereGeoFlat(cam, shot) { geometry = new THREE.SphereGeometry( options.imagePlaneSize, 20, 40 ); var center = pixelToVertex(cam, shot, 0, 0, 0); geometry.applyMatrix(new THREE.Matrix4().makeTranslation(center.x, center.y, center.z)); return geometry; } function createImagePlaneMaterial(cam, shot, shot_id) { var imageTexture = THREE.ImageUtils.loadTexture(imageURL(shot_id)); imageTexture.minFilter = THREE.LinearFilter; var material = new THREE.ShaderMaterial({ side: THREE.DoubleSide, transparent: true, depthWrite: true, uniforms: { projectorMat: { type: 'm4', value: projectorCameraMatrix(cam, shot) }, projectorTex: { type: 't', value: imageTexture }, opacity: { type: 'f', value: options.imagePlaneOpacity }, focal: { type: 'f', value: cam.focal }, focal_x: { type: 'f', value: cam.focal_x }, focal_y: { type: 'f', value: cam.focal_y }, c_x: { type: 'f', value: cam.c_x }, c_y: { type: 'f', value: cam.c_y }, k1: { type: 'f', value: cam.k1 }, k2: { type: 'f', value: cam.k2 }, k3: { type: 'f', value: cam.k3 }, k4: { type: 'f', value: cam.k4 }, scale_x: { type: 'f', value: Math.max(cam.width, cam.height) / cam.width }, scale_y: { type: 'f', value: Math.max(cam.width, cam.height) / cam.height } }, vertexShader: imageVertexShader(cam), fragmentShader: imageFragmentShader(cam) }); return material; } function imageVertexShader(cam) { return $('#vertexshader').text(); } function imageFragmentShader(cam) { if (cam.projection_type == 'equirectangular' || cam.projection_type == 'spherical') return $('#fragmentshader_equirectangular').text(); else if (cam.projection_type == 'fisheye') return $('#fragmentshader_fisheye').text(); else if (cam.projection_type == 'fisheye_opencv') return $('#fragmentshader_fisheye_opencv').text(); else return $('#fragmentshader').text(); } function projectorCameraMatrix(cam, shot) { var angleaxis = shot.rotation; var axis = new THREE.Vector3(angleaxis[0], angleaxis[1], angleaxis[2]); var angle = axis.length(); axis.normalize(); var rotation = new THREE.Matrix4().makeRotationAxis(axis, angle); var t = shot.translation; var translation = new THREE.Vector3(t[0], t[1], t[2]); rotation.setPosition(translation); return rotation; } function reconstruction_color(id){ var s = 1 if(id !== 0){ s = Math.sin(id) * 10000; s -= Math.floor(s); } var color = new THREE.Color(); color.setHex( s * 0xffffff ); return color; } function init() { raycaster = new THREE.Raycaster(); raycaster.precision = 0.01; renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor( 0x202020, 0.0); renderer.sortObjects = false; container = document.getElementById( 'ThreeJS' ); container.appendChild(renderer.domElement); camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.03, 10000); camera.position.x = 50; camera.position.y = 50; camera.position.z = 50; camera.up = new THREE.Vector3(0,0,1); controls = new THREE.OrbitControls(camera, renderer.domElement); controls.addEventListener('change', render); window.addEventListener('resize', onWindowResize, false); renderer.domElement.addEventListener('mousemove', onDocumentMouseMove, false); renderer.domElement.addEventListener('mousedown', onDocumentMouseDown, false); window.addEventListener( 'keydown', onKeyDown, false ); scene_group = new THREE.Object3D(); pointCloudMaterial = new THREE.PointCloudMaterial({ size: options.pointSize, vertexColors: true, }); for (var r = 0; r < reconstructions.length; ++r) { var reconstruction = reconstructions[r]; reconstruction_groups[r] = new THREE.Object3D(); var group = reconstruction_groups[r]; // Points. var points = new THREE.Geometry(); for (var point_id in reconstruction.points) { if (reconstruction.points.hasOwnProperty(point_id)) { var p = reconstruction.points[point_id].coordinates; var c = reconstruction.points[point_id].color; var color = new THREE.Color(); color.setRGB(c[0] / 255., c[1] / 255., c[2] / 255.) points.vertices.push(new THREE.Vector3(p[0], p[1], p[2])); points.colors.push(color); } } var point_cloud = new THREE.PointCloud(points, pointCloudMaterial); point_clouds.push(point_cloud); group.add(point_cloud); // Cameras. var lines = initCameraLines(reconstruction); for (var i = 0; i < lines.length; ++i) { group.add(lines[i]); camera_lines.push(lines[i]); } // GPS positions for (var shot_id in reconstruction.shots) { if (reconstruction.shots.hasOwnProperty(shot_id)) { var shot = reconstruction.shots[shot_id]; var ocenter = opticalCenter(shot); var gps = shot.gps_position; if (gps){ var linegeo = new THREE.Geometry(); linegeo.vertices.push( ocenter, new THREE.Vector3(gps[0], gps[1], gps[2]) ); var lineMaterial = new THREE.LineBasicMaterial({ color: 0xff00ff }); var line = new THREE.Line(linegeo, lineMaterial, THREE.LinePieces); line.visible = options.drawGPS; group.add(line); gps_lines.push(line); } } } scene_group.add(group); } // Image plane imagePlaneCamera = camera_lines[0]; var shot = imagePlaneCamera.reconstruction.shots[imagePlaneCamera.shot_id]; var cam = imagePlaneCamera.reconstruction.cameras[shot.camera]; imagePlane = new THREE.Mesh(imagePlaneGeo(imagePlaneCamera.reconstruction, imagePlaneCamera.shot_id), createImagePlaneMaterial(cam, shot, imagePlaneCamera.shot_id)); imagePlane.visible = options.showImagePlane; imagePlaneCameraOld = camera_lines[0]; imagePlaneOld = new THREE.Mesh(imagePlaneGeo(imagePlaneCameraOld.reconstruction, imagePlaneCameraOld.shot_id), createImagePlaneMaterial(cam, shot, imagePlaneCameraOld.shot_id)); imagePlaneOld.visible = options.showImagePlane; scene_group.add(imagePlane); scene_group.add(imagePlaneOld); // Axis grid_group = new THREE.Object3D(); var linegeo = new THREE.Geometry(); linegeo.vertices = [ new THREE.Vector3(0, 0, 0), new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 1) ]; linegeo.colors = [ new THREE.Color( 0xff0000 ), new THREE.Color( 0xff0000 ), new THREE.Color( 0x00ff00 ), new THREE.Color( 0x00ff00 ), new THREE.Color( 0x0000ff ), new THREE.Color( 0x0000ff ) ]; var lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, vertexColors: THREE.VertexColors }); var line = new THREE.Line(linegeo, lineMaterial, THREE.LinePieces); grid_group.add(line); // Ground grid { var linegeo = new THREE.Geometry(); var N = 20; var scale = 2; for (var i = 0; i <= 2 * N; ++i) { linegeo.vertices.push( new THREE.Vector3(scale * (i - N), scale * (-N), 0), new THREE.Vector3(scale * (i - N), scale * ( N), 0), new THREE.Vector3(scale * (-N), scale * (i - N), 0), new THREE.Vector3(scale * ( N), scale * (i - N), 0) ); } var lineMaterial = new THREE.LineBasicMaterial({color: 0x555555}); var line = new THREE.Line(linegeo, lineMaterial, THREE.LinePieces); grid_group.add(line); } scene_group.add(grid_group); scene = new THREE.Scene(); scene.add(scene_group); addDatGui(); setShowThumbnail(true); if ('img' in urlParams) { for (var i = 0; i < camera_lines.length; ++i) { if (camera_lines[i].shot_id.indexOf(urlParams.img) > -1) { var initialCamera = camera_lines[i]; setMovingMode('walk'); setImagePlaneCamera(initialCamera); navigateToShot(initialCamera); break; } } } if (camera_lines.length < 50) { preloadAllImages(); } render(); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); render(); } function onDocumentMouseMove(event) { event.preventDefault(); mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; render(); } function reconstruction_of_shot(reconstructions, shot_id) { for (var r = 0; r < reconstructions.length; ++r) { if (shot_id in reconstructions[r]['shots']) { return reconstructions[r]; } } return undefined; } function reconstruction_id_of_shot(reconstructions, shot_id) { for (var r = 0; r < reconstructions.length; ++r) { if (shot_id in reconstructions[r]['shots']) { return r; } } return undefined; } function setSelectedCamera(cameraObject) { var r = cameraObject.reconstruction; var shot_id = cameraObject.shot_id; var shot = r['shots'][shot_id]; var image_url = imageURL(shot_id); var rid = reconstruction_id_of_shot(reconstructions, shot_id); if (selectedCamera !== undefined) { selectedCamera.material.linewidth = 1; selectedCamera.material.color = reconstruction_color(rid); } selectedCamera = cameraObject; selectedCamera.material.linewidth = 5; selectedCamera.material.color = options.selectedCameraColor; var image_tag = document.getElementById('image'); image_tag.src = image_url; var text = document.getElementById('text'); text.innerHTML = shot_id; var reconstruction = document.getElementById('reconstruction'); reconstruction.innerHTML = rid; } function setImagePlaneCamera(cameraObject) { var r = cameraObject.reconstruction; var shot_id = cameraObject.shot_id; var shot = r['shots'][shot_id]; var cam = r['cameras'][shot['camera']]; if (previousShot !== cameraObject.shot_id) { previousShot = cameraObject.shot_id var image_url = imageURL(shot_id); if (selectedCamera !== cameraObject) { setSelectedCamera(cameraObject); } if (imagePlaneCamera !== undefined) { if (imagePlaneCameraOld === undefined || imagePlaneCamera.shot_id !== cameraObject.shot_id) { imagePlaneCameraOld = imagePlaneCamera; imagePlaneOld.material.uniforms.projectorTex.value = imagePlane.material.uniforms.projectorTex.value; imagePlaneOld.material.uniforms.projectorMat.value = imagePlane.material.uniforms.projectorMat.value; imagePlane.material.uniforms.focal.value = imagePlane.material.uniforms.focal.value; imagePlane.material.uniforms.focal_x.value = imagePlane.material.uniforms.focal_x.value; imagePlane.material.uniforms.focal_y.value = imagePlane.material.uniforms.focal_y.value; imagePlane.material.uniforms.c_x.value = imagePlane.material.uniforms.c_x.value; imagePlane.material.uniforms.c_y.value = imagePlane.material.uniforms.c_y.value; imagePlane.material.uniforms.k1.value = imagePlane.material.uniforms.k1.value; imagePlane.material.uniforms.k2.value = imagePlane.material.uniforms.k2.value; imagePlane.material.uniforms.k3.value = imagePlane.material.uniforms.k3.value; imagePlane.material.uniforms.k4.value = imagePlane.material.uniforms.k4.value; imagePlane.material.uniforms.scale_x.value = imagePlane.material.uniforms.scale_x.value; imagePlane.material.uniforms.scale_y.value = imagePlane.material.uniforms.scale_y.value; imagePlaneOld.material.vertexShader = imagePlane.material.vertexShader; imagePlaneOld.material.fragmentShader = imagePlane.material.fragmentShader; imagePlaneOld.material.needsUpdate = true; imagePlaneOld.geometry.dispose(); imagePlaneOld.geometry = imagePlaneGeo(imagePlaneCameraOld.reconstruction, imagePlaneCameraOld.shot_id); } if (movingMode === 'walk') { options.imagePlaneOpacity = 1; } } imagePlaneCamera = cameraObject; imagePlane.material.dispose(); imagePlane.geometry.dispose(); imagePlane.material = createImagePlaneMaterial(cam, shot, shot_id); imagePlane.geometry = imagePlaneGeo(r, shot_id); } } function setImagePlaneCameraList(cameraObject, id) { var r = cameraObject.reconstruction; var shot_id = cameraObject.shot_id; var shot = r['shots'][shot_id]; var cam = r['cameras'][shot['camera']]; var image_url = imageURL(shot_id); imagePlaneCameras[id] = cameraObject; imageMaterials[id].map = THREE.ImageUtils.loadTexture(image_url, null, render); imageMaterials[id].map.minFilter = THREE.LinearFilter; imagePlanes[id].geometry = imagePlaneGeo(r, shot_id); imagePlanes[id].visible = true; } function onDocumentMouseDown(event) { window.focus(); if (hoverCamera !== undefined) { if (movingMode !== 'walk') { if (selectedCamera !== hoverCamera) { setSelectedCamera(hoverCamera); setImagePlaneCamera(hoverCamera); } else { setMovingMode('walk'); setImagePlaneCamera(selectedCamera); navigateToShot(selectedCamera); } } render(); } } function navigateToShot(camera) { var reconstruction = camera.reconstruction; var shot = reconstruction['shots'][camera.shot_id]; var cam = reconstruction['cameras'][shot['camera']]; controls.goto_shot(cam, shot); } function hideImagePlanesList(){ for (var i =0; i < num_preview_plane; ++i) { imagePlanes[i].visible = false; } } function angleBetweenVector2(x1, y1, x2, y2) { var a = Math.atan2(y2, x2) - Math.atan2(y1, x1); if (a > Math.PI) return a - 2 * Math.PI; else if (a < -Math.PI) return a + 2 * Math.PI; else return a; } function computeValidMoves() { var currentPosition = controls.animationPosition; var currentTarget = controls.animationTarget; var currentDir = currentTarget.clone().sub(currentPosition); var turnAngle = undefined; var wantedMotionDirs = { STEP_LEFT: new THREE.Vector3(-currentDir.y, currentDir.x, 0), STEP_RIGHT: new THREE.Vector3(currentDir.y, -currentDir.x, 0), STEP_FORWARD: new THREE.Vector3(currentDir.x, currentDir.y, 0), STEP_BACKWARD: new THREE.Vector3(-currentDir.x, -currentDir.y, 0), TURN_LEFT: new THREE.Vector3(0, 0, 0), TURN_RIGHT: new THREE.Vector3(0, 0, 0), TURN_U: new THREE.Vector3(0, 0, 0) } var wantedDirs = { STEP_LEFT: new THREE.Vector3(currentDir.x, currentDir.y, 0), STEP_RIGHT: new THREE.Vector3(currentDir.x, currentDir.y, 0), STEP_FORWARD: new THREE.Vector3(currentDir.x, currentDir.y, 0), STEP_BACKWARD: new THREE.Vector3(currentDir.x, currentDir.y, 0), TURN_LEFT: new THREE.Vector3(-currentDir.y, currentDir.x, 0), TURN_RIGHT: new THREE.Vector3(currentDir.y, -currentDir.x, 0), TURN_U: new THREE.Vector3(-currentDir.x, -currentDir.y, 0) } var min_d = {}; var closest_line = {}; var turn_threshold; for (var k in wantedMotionDirs) { if (wantedMotionDirs.hasOwnProperty(k)) { min_d[k] = 999999999999; closest_line[k] = undefined; } } for (var i = 0; i < camera_lines.length; ++i) { var line = camera_lines[i]; var r = line.reconstruction; var shot_id = line.shot_id; var shot = r['shots'][shot_id]; var cam = r['cameras'][shot['camera']]; var oc = opticalCenter(shot); var dir = (cam.projection_type == 'spherical' || cam.projection_type == 'equirectangular') ? currentDir : viewingDirection(shot); var motion = oc.clone().sub(currentPosition); var d = currentPosition.distanceTo(oc); var rid = reconstruction_id_of_shot(reconstructions, shot_id); var visible = options.reconstruction_visibles[rid]; if (!visible) continue; for (var k in wantedMotionDirs) { if (wantedMotionDirs.hasOwnProperty(k)) { var turn = angleBetweenVector2(wantedDirs[k].x, wantedDirs[k].y, dir.x, dir.y); var driftAB = angleBetweenVector2(wantedMotionDirs[k].x, wantedMotionDirs[k].y, motion.x, motion.y); var driftBA = driftAB - turn; var drift = Math.max(driftAB, driftBA); if (k.lastIndexOf('STEP', 0) === 0) { turn_threshold = 0.5 if (Math.abs(turn) < turn_threshold && Math.abs(drift) < 0.5 && d > 0.01 && d < 20) { if (d < min_d[k]) { min_d[k] = d; closest_line[k] = line; } } } else if (k.lastIndexOf('TURN', 0) === 0) { if (Math.abs(turn) < 0.7 && d < 15) { if (d < min_d[k]) { min_d[k] = d; closest_line[k] = line; } } } } } } return closest_line; } function walkOneStep(motion_type) { var line = validMoves[motion_type]; if (line !== undefined) { setImagePlaneCamera(line); navigateToShot(line); } } function onKeyDown(event) { if (movingMode == 'walk') { var validKey = true; switch (event.keyCode) { case 37: // left arrow if (event.shiftKey) { walkOneStep('TURN_LEFT'); } else { walkOneStep('STEP_LEFT'); } break; case 38: // up arrow walkOneStep('STEP_FORWARD'); break; case 39: // right arrow if (event.shiftKey) { walkOneStep('TURN_RIGHT'); } else { walkOneStep('STEP_RIGHT'); } break; case 40: // down arrow if (event.shiftKey) { walkOneStep('TURN_U'); } else { walkOneStep('STEP_BACKWARD'); } break; case 27: // ESC setMovingMode('orbit'); break; default: validKey = false; break; } if (validKey) { event.preventDefault(); } } } function preloadAllImages() { for (var i = 0; i < camera_lines.length; ++i) { var shot_id = camera_lines[i].shot_id; var image_url = imageURL(shot_id); var temp_img = new Image(); temp_img.src = image_url; } } function preloadValidMoves() { for (var k in validMoves) { if (validMoves.hasOwnProperty(k)) { var line = validMoves[k]; if (line !== undefined) { var shot_id = line.shot_id; var image_url = imageURL(shot_id); var temp_img = new Image(); temp_img.src = image_url; } } } } function updateValidMovesWidget() { $('#nav-left').css('visibility', (validMoves.STEP_LEFT === undefined) ? 'hidden':'visible'); $('#nav-right').css('visibility', (validMoves.STEP_RIGHT === undefined) ? 'hidden':'visible'); $('#nav-forward').css('visibility', (validMoves.STEP_FORWARD === undefined) ? 'hidden':'visible'); $('#nav-backward').css('visibility', (validMoves.STEP_BACKWARD === undefined) ? 'hidden':'visible'); $('#nav-turn-left').css('visibility', (validMoves.TURN_LEFT === undefined) ? 'hidden':'visible'); $('#nav-turn-right').css('visibility', (validMoves.TURN_RIGHT === undefined) ? 'hidden':'visible'); $('#nav-u-turn').css('visibility', (validMoves.TURN_U === undefined) ? 'hidden':'visible'); } function animate() { requestAnimationFrame(animate); imagePlane.material.uniforms.opacity.value = 1 - options.imagePlaneOpacity; if (imagePlaneOld !== undefined) { imagePlaneOld.material.uniforms.opacity.value = 1; } options.imagePlaneOpacity *= 1 - options.animationSpeed; controls.update(); } function render() { validMoves = computeValidMoves(); updateValidMovesWidget(); preloadValidMoves(); // Handle camera selection. if (hoverCamera !== undefined && hoverCamera !== selectedCamera) { hoverCamera.material.linewidth = 1; var rid = reconstruction_id_of_shot(reconstructions, hoverCamera.shot_id); hoverCamera.material.color = reconstruction_color(rid); } var vector = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera); raycaster.set(camera.position, vector.sub(camera.position).normalize()); var intersects = raycaster.intersectObjects(camera_lines, true); hoverCamera = undefined; for (var i = 0; i < intersects.length; ++i) { if (intersects[i].distance > 1.5 * options.cameraSize && intersects[i].object.visible) { hoverCamera = intersects[i].object; if (hoverCamera !== selectedCamera) { hoverCamera.material.linewidth = 2; hoverCamera.material.color = options.hoverCameraColor; } break; } } // Render. renderer.render(scene, camera); } </script> </body> </html>