123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- import * as THREE from "../../../libs/three.js/build/three.module.js";
- import {transitions, easing, lerp} from '../../utils/transitions.js'
- import TileUtils from './tile/TileUtils'
- import { PanoRendererEvents, PanoramaEvents, PanoSizeClass} from '../../defines'
- import math from '../../utils/math'
- import {TextSprite} from '../../objects/TextSprite'
- var texLoader = new THREE.TextureLoader()
- const labelProp = {
- sizeInfo: {minSize : 200 , maxSize : 250, nearBound : 0.8, farBound : 10},
- backgroundColor:{r: 255, g: 255, b: 255, a: 0.4 },
- textColor:{r: 0, g: 0, b: 0, a: 1 },
- borderRadius: 15,
- renderOrder:10
- }
- let standardMarkerMat
- let getMarerMat = function(){
- if(!standardMarkerMat) {
- let map = texLoader.load( Potree.resourcePath+'/textures/marker.png' )
- map.anisotropy = 4 // 各向异性过滤 .防止倾斜模糊
- standardMarkerMat = new THREE.MeshBasicMaterial({opacity:0.7, side: THREE.DoubleSide , map ,transparent:true, depthTest:false})//总是被点云遮住,所以depthTest:false
- }
- return standardMarkerMat.clone()
- }
- //显示全景图时marker没有被遮挡,如果需要,要换成depthBasicMaterial 或者直接把skybox的深度修改(拿到深度贴图后更如此)
- let planeGeo = new THREE.PlaneBufferGeometry(0.2,0.2);
- let sg = new THREE.SphereGeometry(0.1, 8, 8);
- let smHovered = new THREE.MeshBasicMaterial({/* side: THREE.BackSide, */color: 0xff0000});
- let sm = new THREE.MeshBasicMaterial({/* side: THREE.BackSide */});
- var rot90 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), Math.PI/2 ); //使用的是刚好适合全景图的,给cube贴图需要转90°
- //var rot90 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), -Math.PI/2 ); //4dkk->navvis
- //var rot901 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0), -Math.PI/2 ); //整张球幕图要旋转下
- //rot90 = new THREE.Quaternion().multiplyQuaternions( rot901, rot90)
- var old = null;
- /*
- 转成四维看看的axis:
- var a = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), THREE.Math.degToRad(-90)) 因为四维的要绕y转90
- 这里的quaternion.multiply(a);
-
- 先乘再换顺序 w : q.w, x:q.x , y:-q.z, z:q.y
- */
- //暂时直接用4dkkconsole输出的数据
- class Panorama extends THREE.EventDispatcher{
- constructor(o, transform, images360){//file, time, longitude, latitude, altitude, course, pitch, roll
- super()
- this.id = o.id;
- this.images360 = images360
- this.transform = transform
- this.visible = true //for viewer updateVisible
- this.enabled = true//是否可以走
- this.originPosition = new THREE.Vector3().fromArray(o.dataset_location)
- this.originFloorPosition = new THREE.Vector3().fromArray(o.dataset_floor_location)
-
- this.originID = parseInt(o.file_id)//"file_id":"00022"对应是原本的4dkk的id --来自vision.txt
-
-
-
- this.pointcloud = viewer.scene.pointclouds.find(e=>e.dataset_id == o.dataset_id) || viewer.scene.pointclouds[0]
- this.pointcloud.panos.push(this)
- /* this.pointcloud.addEventListener('isVisible',(e)=>{
-
- var visible = viewer.getObjVisiByReason(this.pointcloud, 'datasetSelection')
- console.log('datasetVisi', visible)
- viewer.updateVisible(this, 'pointcloudVisi', visible)
-
- //e.reason == 'datasetSelection' && viewer.updateVisible(this, 'pointcloudVisi', e.visible)
-
- }) */
- this.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)
- this.marker.visible = e.visible
- Potree.settings.showPanoMesh && (this.mesh.visible = e.visible)
- if(e.reason == 'screenshot' || e.visible){
- this.label && (this.label.visible = e.visible)//截图时隐藏下
- }
- })
-
-
- //全景图和Cube的水平采样起始坐标相差90度
-
- /* if(from4dkk){
- var qua = o.dataset_orientation
-
- var quaternion = new THREE.Quaternion().fromArray(qua)
- quaternion = new THREE.Quaternion().multiplyQuaternions(quaternion, rot901);//整张球幕图要旋转下 因为在4dkk里转过,还原。如果是tiles的不用
- this.quaternion = new THREE.Quaternion(quaternion.x, -quaternion.z, quaternion.y, quaternion.w) //转化坐标
-
- }else{ */
-
-
- var qua = o.dataset_orientation
- qua = [qua[1], qua[2], qua[3], qua[0]]
- this.quaternion = new THREE.Quaternion().fromArray(qua)
- this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion
-
- this.quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion, rot90);//全景图和Cube的水平采样起始坐标相差90度,cubeTex转90度
-
- this.rotation4dkk = new THREE.Euler().setFromQuaternion(this.quaternion4dkk)
- this.rotation = new THREE.Euler().setFromQuaternion(this.quaternion)
- //}
-
-
- //this.quaternion1 = Potree.Utils.QuaternionFactory.fromArray(o.dataset_orientation)
- //同quaternion
-
- //let xy = this.transform.forward([this.longitude, this.latitude]);
- this.file = `https://4dkk.4dage.com/images/images${Potree.settings.number}/pan/high/${this.id}.jpg`
- this.build()
- this.transformByPointcloud() //初始化位移
-
-
-
- {//tile
- this.minimumTiledPanoLoaded = !1;
- this.highestPartialTileRenderOpCompleted = 0;
- this.highestFullTileRenderOpCompleted = 0;
- this.shouldRedrawOnBaseLoaded = !1;
- this.resolutionPromise = {}
- this.tiledPanoRenderTarget = null;
- this.zoomed = !1;
-
-
-
-
- images360.panoRenderer.addEventListener(PanoRendererEvents.TileRenderSuccess, this.onTileRendered.bind(this));
- images360.panoRenderer.addEventListener(PanoRendererEvents.PanoRenderComplete, this.onPanoRendered.bind(this));
- images360.panoRenderer.addEventListener(PanoRendererEvents.TileRenderFailure, this.onTileRenderFail.bind(this));
- images360.panoRenderer.addEventListener(PanoRendererEvents.UploadAttemptedForAllTiles, this.onUploadAttemptedForAllTiles.bind(this));
-
- }
-
-
-
- this.addEventListener('hoverOn', (e)=>{//from Map
- if(!e.byMainView){
- this.hoverOn(e)
- }
- })
-
- this.addEventListener('hoverOff', (e)=>{
- if(!e.byMainView){
- this.hoverOff(e)
- }
- })
- }
-
- setEnable(enable){//是否可以走
- viewer.updateVisible(this, 'isEnabled', enable) //令所有marker不可见
- this.enabled = enable
- //如果当前在全景模式且在这个点,需要切换显示吗? 目前用不到
- }
-
-
- build(){
-
- let mesh = new THREE.Mesh(sg, sm);
- mesh.scale.set(1, 1, 1);
- mesh.material.transparent = true;
- mesh.material.opacity = 0.75;
- mesh.pano = this;
- mesh.name = 'panoSphere'
- mesh.addEventListener('mouseover',(e)=>{
- mesh.material = smHovered
- })
- mesh.addEventListener('mouseleave',(e)=>{
- mesh.material = sm
- })
- mesh.addEventListener('click',(e)=>{
- this.images360.focusPano(this)
- })
- { // orientation
- //var {course, pitch, roll} = this;
- //mesh.quaternion.copy(this.quaternion)
-
- //add
- //var quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion, rot901);//改 为球目全
- //quaternion.premultiply(rot90)
- this.panoMatrix = new THREE.Matrix4().makeRotationFromQuaternion(this.quaternion)
- this.oriPanoMatrix = this.panoMatrix.clone()
- //console.log(this.quaternion)
- //this.quaternion = quaternion
- }
- this.mesh = mesh;
- if(!Potree.settings.showPanoMesh) mesh.visible = false
-
- let marker = new THREE.Mesh(planeGeo, getMarerMat() )
- marker.up.set(0,0,1)
- marker.lookAt(marker.up)
- marker.scale.set(2,2,2)
-
-
- this.marker = marker
-
- this.images360.node.add(mesh)
- this.images360.node.add(marker)
- Potree.settings.isTest && this.createTextLabel()
-
- }
-
-
-
-
- transformByPointcloud(){
-
- let position = this.originPosition.clone().applyMatrix4(this.pointcloud.transformMatrix);//也可以用datasetPosTransform算
- let floorPosition = this.originFloorPosition.clone().applyMatrix4(this.pointcloud.transformMatrix);
- this.setPosition(position, floorPosition)
- this.panoMatrix = new THREE.Matrix4().multiplyMatrices(this.pointcloud.rotateMatrix, this.oriPanoMatrix )
- //this.panoMatrix2 = Potree.Utils.datasetRotTransform({fromDataset:true, pointcloud:this.pointcloud, matrix:this.oriPanoMatrix, getMatrix:true}) //和上一行结果一样
- //quaternion也变下
- this.dispatchEvent('rePos')
- }
-
- setPosition(position, floorPosition){
- this.position = position
- this.floorPosition = floorPosition
- this.mesh.position.copy(this.position)
- this.marker.position.copy(this.floorPosition)
- this.marker.position.z+=0.1//会被点云遮住
- if(this.label){
- this.label.position.copy(this.floorPosition), this.label.position.z+=0.2
- this.label.update()
- }
-
- }
-
-
-
-
-
-
-
-
- hoverOn(e={}) {
- //console.log("hoverOn " + this.id )
- transitions.start(lerp.property(this.marker.material, "opacity", 1), 250)
- if(!e.byMap) this.dispatchEvent({type:'hoverOn', byMainView:true})
- }
-
- hoverOff(e={}){
- //console.log("hoverOff " + this.id )
- transitions.start(lerp.property(this.marker.material, "opacity", 0.5), 250)
- if(!e.byMap) this.dispatchEvent({type:'hoverOff', byMainView:true})
- }
-
-
-
- setZoomed(zoomed){
- this.zoomed = zoomed;
- Potree.settings.displayMode == 'showPanos' && this.updateSkyboxForZoomLevel(); //放大后换成zoomTarget贴图
- viewer.dispatchEvent({type:'panoSetZoom', zoomed})
-
- }
-
-
- enter(){
- this.setZoomed(!1),
- viewer.dispatchEvent({type:PanoramaEvents.Enter, oldPano:old, newPano:this } )
- old = this
- //console.log("enter pano "+ this.id)
- }
- exit(){
- /* if(this.tiled)
- { */
- this.clearWaitDeferreds();
- this.minimumTiledPanoLoaded = !1;
- this.tiledPanoRenderTarget = null;
- this.setZoomed(!1);
- this.images360.panoRenderer.deactivateTiledPano(this);
- this.highestPartialTileRenderOpCompleted = 0;
- this.highestFullTileRenderOpCompleted = 0;
- /*}
- else
- {
- this.solidSkybox.dispose();
- this.solidSkybox.loaded = !1;
- this.solidSkybox.version = 0;
- } */
-
- //console.log("exit pano "+ this.id)
-
- viewer.dispatchEvent({type:PanoramaEvents.Exit, pano:this});
- }
-
-
- updateSkyboxForZoomLevel(){
- if(this.minimumTiledPanoLoaded){
- this.images360.updateProjectedPanos();
- }
-
- }
-
- getSkyboxTexture(){
-
- if(this.minimumTiledPanoLoaded)
- {
- if(this.zoomed && this.images360.qualityManager.maxRenderTargetSize > this.images360.qualityManager.maxNavPanoSize)//change 如果放大后和不放大都是2k就不用这个
- {
- return this.images360.panoRenderer.zoomRenderTarget.texture;
- }
- else
- {
-
- this.tiledPanoRenderTarget.texture.mapping = THREE.UVMapping//add
- return this.tiledPanoRenderTarget.texture;
- }
- }
- else
- {
- return null;
- }
-
- }
-
-
-
- isLoaded(e){
- if (e && "string" == typeof e)
- console.error("Wrong panoSize given to Panorama.isLoaded(); a tiled pano uses PanoSizeClass");
- return !!this.minimumTiledPanoLoaded && (!e || this.highestFullTileRenderOpCompleted >= e)//改:原本是:this.highestPartialTileRenderOpCompleted >= e, 希望这代表全部加载完
-
- }
- getWaitDeferred(size){//获取不同size的tile贴图的promiss
- var t = this.resolutionPromise[this.id];
- t || (t = {}, this.resolutionPromise[this.id] = t);
- var i = t[size];
- return i || (i = {
- deferred: $.Deferred(),
- active: !1
- },
- t[size] = i),
- i
- }
-
- clearWaitDeferreds(){
- var e = this.resolutionPromise[this.id];
- e || (e = {},
- this.resolutionPromise[this.id] = e);
- for (var t in e)
- if (e.hasOwnProperty(t)) {
- var i = e[t];
- i.active = !1,
- i.deferred = $.Deferred()
- }
- }
- resetWaitDeferred(e){
- var t = this.getWaitDeferred(e);
- t.active = !1;
- t.deferred = $.Deferred();
- }
- onTileRendered(ev){
- ev.id === this.id && this.dispatchEvent({
- type:PanoramaEvents.TileLoaded,
- size:ev.panoSize, index:ev.tileIndex, count:ev.totalTiles
- });
- }
- onPanoRendered(ev) {
- if(ev.id === this.id)
- {
- this.minimumTiledPanoLoaded = !0;
- this.updateSkyboxForZoomLevel();//更新贴图 setProjected
- ev.panoSize > this.highestPartialTileRenderOpCompleted && (this.highestPartialTileRenderOpCompleted = ev.panoSize);//应该是更新最高获取到的Partial size
- ev.updateFullComplete && ev.panoSize > this.highestFullTileRenderOpCompleted && (this.highestFullTileRenderOpCompleted = ev.panoSize); //应该是更新最高获取到的Full size
- //this.dispatchEvent("load", ev.panoSize);
- viewer.ifAllLoaded( this);
- this.dispatchEvent({type:PanoramaEvents.LoadComplete, size:ev.panoSize, count:ev.totalTiles});
- }
- }
-
- onTileRenderFail(ev) {
- ev.id === this.id && this.dispatchEvent({type:PanoramaEvents.LoadFailed });
- }
- onUploadAttemptedForAllTiles(ev) {
- if (ev.id === this.id) {
- var n = this.images360.qualityManager.getPanoSize(PanoSizeClass.BASE);
- if(ev.panoSize === n && this.shouldRedrawOnBaseLoaded) //shouldRedrawOnBaseLoaded一直是false。在4dkk里只有初始点在quickstart后变为true。
- {
- this.shouldRedrawOnBaseLoaded = !1;
- this.panoRenderer.resetRenderStatus(this.id, !0, !1);
- this.panoRenderer.renderPanoTiles(this.id, null, !0, !0);
- }
- }
- }
-
-
- createTextLabel(){
- this.removeTextLabel()
- this.label = new TextSprite($.extend(
- labelProp, {text: this.id }) //{text: `id:${this.id}, dataset:${this.pointcloud.name}, 4dkkId:${this.originID}`}
- );
-
- this.images360.node.add(this.label);
- this.floorPosition && this.label.position.copy(this.floorPosition)
- }
-
- removeTextLabel(){
- if(this.label){
-
- this.label.parent.remove(this.label);
- }
- }
-
-
-
- };
-
- Panorama.prototype.loadTiledPano = function() {
- //var downloads = [] , t = [];
- var downloaded = {} , eventAdded = {}, latestPartialRequest = {}; //每个pano对应一组这些
-
- return function(size, dirs, fov, o, a, download) {
- var dir = dirs.datasetsLocal.find(e=>e.datasetId == this.pointcloud.dataset_id).direction;
- //var dir = dirs
-
-
- null !== o && void 0 !== o || (o = !0),
- null !== a && void 0 !== a || (a = !0);
- var l = this.getWaitDeferred(size)
- , c = l.deferred
- , h = null
- , u = null;
- fov && ("number" == typeof fov ? h = fov : (h = fov.hFov, u = fov.vFov))
-
- if (!this.isLoaded(size)) {
- //console.log('loadTiledPano', this.id, size, fov)
- if (!l.active) {
- l.active = !0
- let name = this.id + ":" + size
- downloaded[name] = downloaded[name] || []
- /*
- this.downloaded = downloaded
- this.latestPartialRequest = latestPartialRequest
- */
- latestPartialRequest[name] = null
-
- if (fov) {
- let tileArr = []//add
- var d = TileUtils.matchingTilesInDirection(this, size, dir, h, u, tileArr);
-
- latestPartialRequest[name] = tileArr
- downloaded[name].forEach((e)=>{
- let item = latestPartialRequest[name].find(a=>e.faceTileIndex == a.faceTileIndex && e.face == a.face)
- if(item){
- item.loaded = true
- }
- })
- if(!latestPartialRequest[name].some(e=>!e.loaded)){//所需要的全部加载成功
- //let total = TileUtils.getTileCountForSize(size)
- //this.onPanoRendered(this.id, size, total, !0);
- c.resolve(size/* , total */);
- this.resetWaitDeferred(size)
- //console.log('该部分早已经加载好了'+size, this.id)
- latestPartialRequest[name] = null
- }
-
- //console.log("Loading partial pano: " + this.id + " with " + d + " tiles")
- }
- if(!eventAdded[this.id]) {
- eventAdded[this.id] = !0
-
- this.addEventListener(PanoramaEvents.LoadComplete, function(ev/* e, t */) {//本次任务全部加载完毕
-
- //console.warn('点位(可能部分)下载完成 ', 'id:'+this.id, 'size:'+ev.size )
-
- var i = this.getWaitDeferred(ev.size).deferred;//"pending"为还未完成
- i && "pending" === i.state() && this.highestPartialTileRenderOpCompleted >= ev.size && (i.resolve(ev.size, ev.count),
- this.resetWaitDeferred(ev.size))//恢复active为false
- }.bind(this))
-
- this.addEventListener(PanoramaEvents.LoadFailed, function(ev) {
- var t = this.getWaitDeferred(e).deferred;
- t && "pending" === t.state() && this.highestPartialTileRenderOpCompleted >= ev.t && (t.reject(ev.t),
- this.resetWaitDeferred(ev.t))//恢复active为false
- }.bind(this))
-
- this.addEventListener(PanoramaEvents.TileLoaded, function(ev/* t, i, n */) {//每张加载完时
-
- //console.log('tileLoaded', 'id:'+this.id, 'size:'+ev.size, 'tileIndex:'+ev.index )
- let tileIndex = ev.index
- let total = ev.count
- let size = ev.size
- let name = this.id + ":" + size
- downloaded[name] = downloaded[name] || [] //不是所有的加载都是从loadTiledPano获取的所以会有未定义的情况
-
- let {faceTileIndex,face} = TileUtils.getTileLocation(size, tileIndex, {})
- downloaded[name].push({faceTileIndex,face})
- var r = this.getWaitDeferred(size).deferred;
- if (r && "pending" === r.state()) {
- r.notify(size, tileIndex, total);
- if(latestPartialRequest[name]){
- let item = latestPartialRequest[name].find(e=>e.faceTileIndex == faceTileIndex && e.face == face)
- item && (item.loaded = true )
-
- if(!latestPartialRequest[name].some(e=>!e.loaded)){//所需要的局部tiles全部加载成功
- this.onPanoRendered(this.id, size, total, !0); //onPanoRendered还会触发 PanoramaEvents.LoadComplete
- r.resolve(size, total);
- this.resetWaitDeferred(size)
- //console.log('该部分加载好了'+size, this.id)
- latestPartialRequest[name] = null
- }
-
- }
- }
-
-
- /* var r = this.getWaitDeferred(ev.size).deferred;
- if (r && "pending" === r.state()) {
- r.notify(ev.size, ev.index, ev.count);
-
- var o = downloads[this.id + ":" + ev.size];
- if(o){//如果有规定下载哪些tile,只需要下载这些tile则LoadComplete
- o.tileCount++
-
- if(o.tileCount === o.targetTileCount){//达到下载目标数
- this.onPanoRendered(this.id, ev.size, ev.count, !0);
- r.resolve(ev.size, ev.count);
- this.resetWaitDeferred(ev.size)
- }
- }
- } */
- }.bind(this))
- }
- }
- this.images360.tileDownloader.clearForceQueue(),
- this.images360.tileDownloader.forceQueueTilesForPano(this, size, dir, h, u, download)
- this.tiledPanoRenderTarget = this.images360.panoRenderer.activateTiledPano(this, this.images360.qualityManager.getMaxNavPanoSize(), o)
- this.images360.panoRenderer.renderPanoTiles(this.id, dirs, a)
- }else{
- //console.log('早已经全加载好了' +size, this.id)
- c.resolve(size)
- }
- return c.promise()
- }
- }()
- /*
- 经观察发现,navvis的也存在的问题是点云和全景有微小的偏差,导致远处的热点在全景和点云上看位置差别感大,比如一个在路上一个在天空上。
- */
- export default Panorama
|