UNPKG

avansel

Version:

Free OpenSource ThreeJS Javascript Virtual Tours viewer

247 lines (222 loc) 8.54 kB
import { Group, Mesh, Vector2, Raycaster, PerspectiveCamera, Vector3 } from 'three' import { createSide, updateSide, deleteSide } from './common/Side.js' import { tilesFor } from '../utils' import { createCube } from './common/cube' import { pano } from '../../config.json' import Controls from '../../systems/Controls.js' class Multires { levels: Array<any> source: Function | string controls: Controls camera: PerspectiveCamera instance: Group pos: Object fov: number visible: any pixelsMin: number pixelsMax: number cube: Mesh constructor(levels: Array<any>, source: Function | string, controls: Controls) { this.pixelsMin = 0.5 this.pixelsMax = 5 this.levels = levels this.source = source this.controls = controls this.camera = controls.camera.get() this.visible = { pixels: [], maxLevel: 0, sides: {}, meshes: [] } this.cube = createCube() this.controls.canvas.addEventListener( 'cameraMove', this.onCameraMove.bind(this) ) this.controls.canvas.addEventListener( 'fovChanged', this.onFovChanged.bind(this) ) } get(): Group{ return this.instance } createPano(){ this.instance = new Group(); this.instance.renderOrder = 2 this.instance.name = 'multires-pano' return this.instance } pixelsBySize(size: number, fov: number){ const height = this.controls.canvas.parentElement.clientHeight; const number = height / (0.9 * fov * size / 100) return { number, visible: number >= this.pixelsMin && number <= this.pixelsMax } } minFov(size: number, max: number){ const height = this.controls.canvas.parentElement.clientHeight; return (height * 100) / (size * 0.9 * max) } pointSideXY(point: Vector3){ const size = ( pano.maxLevels + pano.tileBase + 2 ) const mul = 1000000 const hs = size / 2 const hsMul = (hs * mul) const x = Math.round(point.x * mul) const y = Math.round(point.y * mul) const z = Math.round(point.z * mul) if(z === hsMul) return { side: 'f', x: 1 - (point.x + hs) / size, y: 1 - (point.y + hs) / size } if(z === -hsMul) return { side: 'b', x: (point.x + hs) / size, y: 1 - (point.y + hs) / size } if(x === hsMul) return { side: 'l', x: (point.z + hs) / size, y: 1- (point.y + hs) / size } if(x === -hsMul) return { side: 'r', x: (hs - point.z) / size, y: 1 - (point.y + hs) / size } if(y === hsMul) return { side: 'u', x: 1- (point.x + hs) / size, y: (point.z + hs) / size } if(y === -hsMul) return { side: 'd', x: 1 - (point.x + hs) / size, y: 1 - (point.z + hs) / size } } sidesBounds(){ const sides = {} for( var i = 0; i < this.visible.points.length; i ++ ){ let point = this.visible.points[i] const sideXY = this.pointSideXY(point) if(!sides[sideXY.side]) sides[sideXY.side] = { points: [], bounds: {x: {}, y: {}} } sides[sideXY.side].points.push({ x: sideXY.x, y: sideXY.y }) } for(var side in sides){ for(var i = 0; i < sides[side].points.length; i ++ ){ let point = sides[side].points[i] if(!sides[side].bounds.x.min || sides[side].bounds.x.min > point.x) sides[side].bounds.x.min = point.x if(!sides[side].bounds.x.max || sides[side].bounds.x.max < point.x) sides[side].bounds.x.max = point.x if(!sides[side].bounds.y.min || sides[side].bounds.y.min > point.y) sides[side].bounds.y.min = point.y if(!sides[side].bounds.y.max || sides[side].bounds.y.max < point.y) sides[side].bounds.y.max = point.y } } return sides } screenPoints(max: number, size: number ){ let screenPoints = [] let points = [] const min = -max const step = (max - min) / (size - 1) for(var x = min; x <= max; x += step){ for(var y = min; y <= max; y += step){ screenPoints.push(new Vector2( x, y )) } } const raycaster = new Raycaster() for(var i in screenPoints){ raycaster.setFromCamera( screenPoints[i], this.camera ); points.push(raycaster.intersectObject( this.cube )[0].point) } return points } sidesVisibleTiles(){ this.visible.meshes = [] const sides = ['f', 'b', 'u', 'd', 'l', 'r'] for(var level in this.visible.pixels){ const levelInt = parseInt(level) if(this.visible.pixels[level].visible){ for(var side in this.visible.sides){ if(!this.visible.sides[side].tiles){ this.visible.sides[side].tiles = {} } this.visible.sides[side].tiles[level] = tilesFor( levelInt, this.levels[level], this.levels[level].fallback ? { x: { min: -1.1, max: 1.1 }, y: { min: -1.1, max: 1.1 } } : this.visible.sides[side].bounds ) this.visible.meshes.push(level + '-' + side) this.visible.meshes = [...this.visible.meshes, ...this.visible.sides[side].tiles[level].map(item => level + '-' + side + '-' + item.x + '-' + item.y)] } if(this.levels[level].fallback){ for(var i in sides){ const side = sides[i] if(!this.visible.sides[side]){ this.visible.sides[side] = { tiles: {} } this.visible.sides[side].tiles[level] = tilesFor( levelInt, this.levels[level], {x: { min: -1.1, max: 1.1 }, y: { min: -1.1, max: 1.1 } } ) this.visible.meshes.push( level + '-' + side ) this.visible.meshes = [...this.visible.meshes, ...this.visible.sides[side].tiles[level].map(item => level + '-' + side + '-' + item.x + '-' + item.y)] } } } } } } onCameraMove(e: Event){ this.updatePosition() } onFovChanged(e: Event){ this.updatePosition() } updatePosition(){ const pos = this.controls.position() this.onPosFovChanged( pos ) this.addUpdateVisible() if(this.camera.fov != pos.fov){ this.camera.fov = pos.fov this.camera.updateProjectionMatrix() } } onPosFovChanged(pos: any){ this.calcVisibleData(pos) } calcVisibleData(pos: any){ const levels = this.levels.length let hasVisible = false let maxLevel = 0 for(var i = 0; i < levels; i++){ const item = this.pixelsBySize(this.levels[i].size, pos.fov) if(this.levels[i].fallback) item.visible = true if(item.visible && !hasVisible) hasVisible = true if(item.visible && i > maxLevel) maxLevel = i this.visible.pixels[i] = item } const lastLevel = this.visible.pixels.length - 1 if( lastLevel && !this.visible.pixels[lastLevel].visible && this.visible.pixels[lastLevel].number > this.pixelsMax ){ this.visible.pixels[lastLevel].visible = true } this.visible.maxLevel = maxLevel if(!hasVisible){ if(this.visible.pixels[0].number < this.pixelsMin) this.visible.pixels[0].visible = true if(this.visible.pixels[levels - 1].number > this.pixelsMax) this.visible.pixels[levels - 1].visible = true } this.visible.points = this.screenPoints(1.1, 5) this.visible.sides = this.sidesBounds() this.controls.fovMin = this.minFov(this.levels[lastLevel].size, pano.pixelZoom) this.sidesVisibleTiles() } addUpdateVisible(){ for(var side in this.visible.sides){ for(var level in this.visible.sides[side].tiles){ const tiles = this.visible.sides[side].tiles[level] const name = level + '-' + side const group = this.instance.getObjectByName(name) as Group if(group){ updateSide(group, side, parseInt(level), tiles, this.source, this.visible.meshes) }else{ this.instance.add(createSide(side, parseInt(level), tiles, this.source)) } } } const groups = this.instance.children.map(item => item.name) for(var i = groups.length - 1; i >= 0; i --){ const name = groups[i] const [ level ] = name.split('-') if( !this.visible.meshes.includes(name) && !this.levels[level].fallback ){ const group = this.instance.getObjectByName(name) if(group){ deleteSide(group) this.instance.remove(group) } } } return this.instance } } export { Multires }