| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- import * as THREE from "../../../../libs/three.js/build/three.module.js";
- import {transitions, easing, lerp} from '../../utils/transitions.js'
-
- import math from '../../utils/math.js'
- import {TextSprite} from '../../objects/TextSprite.js'
- import DepthBasicMaterial from "../../materials/DepthBasicMaterial.js";
- let { PanoramaEvents } = Potree.defines
- var texLoader = new THREE.TextureLoader()
-
- const markerOpacitys ={
- default : 0.5,
- hovered : 1,
- }
- 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,
- useDepth:true,
- clipDistance: 30, maxClipFactor:0.3, occlusionDistance:3,
- }
- const labelProp2 = {
- //sizeInfo: {minSize : 200 , maxSize : 250, nearBound : 0.8, farBound : 10},
- backgroundColor:{r: 255, g: 255, b: 255, a: 0 },
- textColor:{r:255 , g: 255, b: 255, a: 1 },
- textBorderColor:{r:30 , g:30, b: 30, a: 1 },
- textBorderThick:3,
- dontFixOrient:true,
- renderOrder:10,
- fontsize:30,
- }
-
- let markerTex
- //显示全景图时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, images360){//file, time, longitude, latitude, altitude, course, pitch, roll
- super()
- this.id = o.id; //唯一标识
- this.images360 = images360
- this.visible = true //for updateVisible
- this.enabled = true//是否可以走
- this.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)
- //console.log('pano isVisible', this.id, e.visible)
- Potree.Utils.updateVisible(this.marker, 'panoVisi', e.visible)
- Potree.settings.showPanoMesh && (this.mesh.visible = e.visible)
- if(e.reason == 'screenshot' || e.visible){
- this.label && (this.label.visible = e.visible)//截图时隐藏下
- }
- this.label2 && Potree.Utils.updateVisible(this.label2, 'panoVisi', e.visible)
- })
- /*
- 漫游点可见性:旧
- level reason 类型
- 2(最高)buildingChange(不在此楼层) unvisible
- 1 modeIsShowPanos(漫游模式) visible //不记得为什么加这个了,所以重写
- 0 pointcloudVisi(隐藏了数据集) unvisible
- */
-
- /*
- 漫游点可见性:新
- level reason 类型
- 2(最高)buildingChange(不在此楼层) unvisible
- 1 ifShowMarker(marker显示开关) unvisible
- 0 pointcloudVisi(隐藏了数据集) unvisible
- */
-
- this.panosData = o
-
- this.originPosition = new THREE.Vector3().copy(o.pose.translation)
- this.originFloorPosition = new THREE.Vector3().copy(o.puck)
-
- this.originID = parseInt(o.id)// uuid "file_id":"00022"对应是原本的4dkk的id --来自vision.txt
-
- this.pointcloud = viewer.scene.pointclouds[0]
- this.pointcloud.panos.push(this)
-
- //this.sid = this.pointcloud.sceneCode + '|' + this.originID //不会更改的标记
- this.sid = this.originID //不会更改的标记
- //全景图和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{ */
-
- this.quaternion = new THREE.Quaternion().copy(o.pose.rotation)
- this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion
- this.quaternion2 = this.quaternion.clone()
- //this.quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion, rot90);//全景图和Cube的水平采样起始坐标相差90度,cubeTex转90度
-
- this.rotation4dkk = new THREE.Euler().setFromQuaternion(this.quaternion4dkk)
-
- //}
- //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.neighbours = [];
-
-
- this.rotation = new THREE.Euler().setFromQuaternion(this.quaternion)
- this.build()
- this.setPosition(this.originPosition, this.originFloorPosition )//this.transformByPointcloud() //初始化位移
-
-
-
-
- this.addEventListener('hoverOn', (e)=>{//from Map
- if(!e.byMainView){
- this.hoverOn(e)
- }
- })
-
- this.addEventListener('hoverOff', (e)=>{
- if(!e.byMainView){
- this.hoverOff(e)
- }
- })
-
-
- }
-
- get noNeighbour(){//是否绝对到不到的孤立点
-
- for(let i=0,j=this.images360.panos.length; i<j; i++){
- if(this.images360.neighbourMap[this.id][i] !== false ){
- return false
- }
- }
- return true
-
- //return this.neighbours.length == 0
- }
- setEnable(enable){//是否可以走
- Potree.Utils.updateVisible(this, 'isEnabled', enable) //令所有marker不可见
- this.enabled = enable
- //如果当前在全景模式且在这个点,需要切换显示吗? 目前用不到
- }
- waitForLoad(){
- viewer.waitForLoad(this, ()=>{//发送loading
- return this.depthTex && this.skyboxTex
- });
- }
- loadTex(){
- if(this.skyboxTex || this.texLoading)return
- this.texLoading = true
- let src = `${Potree.settings.urls.prefix1}/images/${this.originID}.jpg` //`server\test\SS-t-P1d6CwREny2\${this.id}.jpg` //`${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${this.pointcloud.sceneCode}/data/${this.pointcloud.sceneCode}/depthmap/${this.originID}.png`
- //console.log('开始下载depthImg', this.id)
-
- let startLoad = (src)=>{
- let texture = texLoader.load( src, ()=>{
- this.skyboxTex = texture
- this.dispatchEvent({type:'loadedTex', loaded:true})
- this.depthTexLoading = false
- //viewer.dispatchEvent('content_changed')
- },null,(e)=>{//error
- console.error('loadTex失败, 数据集sceneCode'+ this.pointcloud.sceneCode, this.id )
-
- this.dispatchEvent({type:'loadedTex', })
- });
- texture.wrapS = THREE.RepeatWrapping;
- texture.flipY = false
- texture.magFilter = THREE.LinearFilter
- texture.minFilter = THREE.LinearFilter //防止边缘竖线
- texture.generateMipmaps = false
-
- }
-
- Potree.getRealUrl(src, startLoad)
-
- }
-
-
-
- loadDepthImg(){
- if(!this.pointcloud.hasDepthTex || this.depthTex || this.depthTexLoading)return
- this.depthTexLoading = true
- let src = //Potree.settings.number == 'SS-t-7DUfWAUZ3V' ? `${Potree.scriptPath}/data/${Potree.settings.number}/depthMap/${this.originID}.png` :
- //`${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${this.pointcloud.sceneCode}/data/${this.pointcloud.sceneCode}/depthmap/${this.originID}.png`
- `${Potree.settings.urls.prefix1}/depthmap/${this.originID}.png`
-
- //console.log('开始下载depthImg', this.id)
-
- let startLoad = (src)=>{
-
- let texture = texLoader.load( src, ()=>{
- this.depthTex = texture
- this.dispatchEvent({type:'loadedDepthImg', pano:this, loaded:true})
- this.depthTexLoading = false
- this.images360.updateDepthTex(this)
- //viewer.dispatchEvent('content_changed')
- },null,(e)=>{//error
- console.error('loadDepthImg失败, 数据集sceneCode'+ this.pointcloud.sceneCode, this.id )
- this.pointcloud.hasDepthTex = false
- this.dispatchEvent({type:'loadedDepthImg', pano:this, })
- });
- texture.wrapS = THREE.RepeatWrapping;
- texture.flipY = false
- texture.magFilter = THREE.LinearFilter
- texture.minFilter = THREE.LinearFilter
- texture.generateMipmaps = false
- }
-
- Potree.getRealUrl(src, startLoad)
-
- }
-
-
- build(){
-
- { // orientation
- //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()
-
- //if(this.quaternion2)this.oriPanoMatrix2 = new THREE.Matrix4().makeRotationFromQuaternion(this.quaternion2)
-
-
- //补:全景图下和原来的一样
- this.panoMatrix2 = this.panoMatrix
- this.panoMatrix2Inverse = this.panoMatrix2.clone().invert();
- }
-
- let marker = new THREE.Mesh(planeGeo, this.getMarkerMat() )
- marker.name = 'marker_'+this.id
- marker.up.set(0,0,1)
- marker.lookAt(marker.up)
- marker.scale.set(2,2,2)
- this.addEventListener('changeMarkerTex',(e)=>{
- marker.material.map = markerTex[e.name]
- })
-
- this.marker = marker
-
- this.images360.node.add(marker)
- Potree.settings.isTest && this.addLabel()
- //this.addLabel2()
-
- marker.addEventListener('mouseover', this.hoverOn.bind(this));
- marker.addEventListener('mouseleave', this.hoverOff.bind(this));
-
-
- }
-
-
-
-
- /* 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也变下
- if(this.oriPanoMatrix2){
- this.panoMatrix2 = new THREE.Matrix4().multiplyMatrices(this.pointcloud.rotateMatrix, this.oriPanoMatrix2 )//供DepthImageSampler使用
- this.panoMatrix2Inverse = this.panoMatrix2.clone().invert();
- }
- 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.04//会被点云遮住
- if(this.label){
- if(Potree.settings.editType == 'pano'){
- this.label.position.copy(this.position)
- }else{
- this.label.position.copy(this.floorPosition)
- }
- this.label.position.z+=0.14
- this.label.update()
- }
-
- if(this.label2){
- if(Potree.settings.editType == 'pano'){
- this.label2.position.copy(this.position)
- }else{
- this.label2.position.copy(this.floorPosition)
- }
- this.label2.position.copy(this.marker.position)
- this.label2.update()
- }
-
- }
-
-
-
- getMarkerMat(){
- if(!markerTex) {
- markerTex = {
- default:texLoader.load( Potree.resourcePath+'/textures/marker.png' ),
- ring:texLoader.load( Potree.resourcePath+'/textures/marker2.png' )
- }
- markerTex.default.anisotropy = 4 // 各向异性过滤 .防止倾斜模糊
- markerTex.ring.anisotropy = 4
- //有可能被点云遮住吗。
-
- }
- return new DepthBasicMaterial({opacity: markerOpacitys.default, side: THREE.DoubleSide , map:markerTex.default ,transparent:true,
- clipDistance: 2, occlusionDistance:1, //不能设置太短,因为过渡时深度不准确
- useDepth: !!(Potree.settings.useDepthTex && this.pointcloud.hasDepthTex),
- autoDepthTest:true
- //改为DepthBasicMaterial是因为原Basic的材质在有深度图时过渡会先隐藏后出现。 注:没有深度图时全景模式的marker无法遮挡
- })
- }
-
-
-
-
- hoverOn(e={}) {
- //console.log("hoverOn " + this.id )
- transitions.start(lerp.property(this.marker.material, "opacity", markerOpacitys.hovered,()=>{
- viewer.dispatchEvent('content_changed')
- }), this.marker.visible ? 250 : 0)
- if(!e.byMap) this.dispatchEvent({type:'hoverOn', byMainView:true})
- if(!e.byImages360) this.images360.dispatchEvent({type:'markerHover', hovered:true, pano:this})
- }
-
- hoverOff(e={}){
- //console.log("hoverOff " + this.id )
- transitions.start(lerp.property(this.marker.material, "opacity", markerOpacitys.default,()=>{
- viewer.dispatchEvent('content_changed')
- }), this.marker.visible ? 250 : 0)
- if(!e.byMap) this.dispatchEvent({type:'hoverOff', byMainView:true})
- if(!e.byImages360) this.images360.dispatchEvent({type:'markerHover', hovered:false, pano:this})
- }
-
-
-
-
-
- enter(){
- this.entered = true
- viewer.dispatchEvent({type:PanoramaEvents.Enter, oldPano:old, newPano:this } )
- old = this
- //console.log("enter pano "+ this.id)
- }
- exit(){
- this.skyboxTex && this.skyboxTex.dispose()
- this.depthTex && this.depthTex.dispose() //贴图不使用后先dispose,下次到该点时会自动还原
- this.entered = false //add
-
- viewer.dispatchEvent({type:PanoramaEvents.Exit, pano:this});
- }
-
-
-
-
-
- addLabel(){
- this.removeTextLabel()
- this.label = new TextSprite(Object.assign({},
- labelProp, {text: this.id + "("+this.originID+")"}) //{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);
- }
- }
-
-
-
-
- getCeilHeight(){//天花板高度值 (假设不存在depth为0的点,所有为0的要么是在盲区,要么是无穷远。)
-
- if(this.ceilZ == void 0){
- const depthTiming = Potree.timeCollect.depthSampler.median //pc firefox达到4. chrome为0.01
-
-
- //用三个间隔120度散开,和中心垂直线成一定夹角的三个向量去求 最高高度 (不求平均的原因:万一是0不好算)
-
- let rotMat = new THREE.Matrix4().makeRotationX((Potree.config.depthTexUVyLimit+0.01)*Math.PI)// 角度不能小于天花板中空的半径
-
-
-
- let dirs = [new THREE.Vector3(0,0,1).applyMatrix4(rotMat)];
- if(depthTiming < 1){
- let rotMat1 = new THREE.Matrix4().makeRotationZ(Math.PI*2 / 3);
- dirs.push(dirs[0].clone().applyMatrix4(rotMat1))
- }
- if(depthTiming < 0.3){
- let rotMat2 = new THREE.Matrix4().makeRotationZ(-Math.PI*2 / 3);
- dirs.push(dirs[0].clone().applyMatrix4(rotMat2));
- }
-
- let zs = dirs.map(dir_=>{
- let dir = dir_.clone().applyMatrix4(this.panoMatrix2) //pano不一定是垂直的, 需要把之前的dirInPano先转成真实的dir,防止超出角度限制
- let intersect = viewer.images360.getIntersect(this, dir)
- let z = intersect ? intersect.location.z : Infinity/* this.position.z+skyHeight */ //没有intersect代表可能是天空
- return z
- })
- zs.sort((a,b)=>{return b-a});//得最大值 (不用中位数的原因:在屋檐处,如果仅有一个intersect是天空,因到了室外所以也用天空高度)
-
-
- this.ceilZ = zs[0]
- let min = this.position.z + 1 // 防止意外太低
- this.ceilZ = Math.max(min, this.ceilZ)
- //console.log(this.id, 'ceilZ:', this.ceilZ )
- }
-
- return this.ceilZ
-
-
- }
-
- };
-
-
-
- export default Panorama
|