Images360.js 86 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114
  1. import * as THREE from "../../../../libs/three.js/build/three.module.js";
  2. import {TextSprite} from "../../objects/TextSprite.js";
  3. import ModelTextureMaterial from "../../materials/ModelTextureMaterial.js";
  4. import Common from "../../utils/Common.js";
  5. import math from "../../utils/math.js";
  6. import cameraLight from "../../utils/cameraLight.js";
  7. import {MeshDraw } from '../../utils/DrawUtil.js'
  8. import Panorama from "./Panorama.js";
  9. import browser from '../../utils/browser.js'
  10. import {transitions, easing, lerp} from "../../utils/transitions.js";
  11. import DepthImageSampler from './DepthImageSampler.js'
  12. let {PanoSizeClass,Vectors,GLCubeFaces, PanoramaEvents} = Potree.defines
  13. var rot90 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), Math.PI/2 ); //使用的是刚好适合全景图的,给cube贴图需要转90°
  14. let raycaster = new THREE.Raycaster();
  15. //let currentlyHovered = null;
  16. let texLoader = new THREE.TextureLoader()
  17. let tileArr = []
  18. let previousView = {
  19. controls: null,
  20. position: null,
  21. target: null,
  22. };
  23. const HighMapCubeWidth = 1
  24. const directionFactor = 400 //原先10,几乎只往距离近的走了;设置太大容易略过近处漫游点走向下坡,因为鼠标一般在地面,下坡的漫游点更有利
  25. export class Images360 extends THREE.EventDispatcher{
  26. constructor(viewer ){
  27. super();
  28. this.viewer = viewer;
  29. this.panos = [];
  30. this.neighbourMap = {}
  31. this.node = new THREE.Object3D();
  32. this.node.name = 'ImagesNode'
  33. this.cubePanos = [];
  34. {
  35. this.cube = new THREE.Mesh(new THREE.BoxBufferGeometry(1,1,1,1),new ModelTextureMaterial());
  36. Potree.Utils.updateVisible(this.cube,'showSkybox', false )
  37. this.cube.layers.set(Potree.config.renderLayers.skybox)
  38. this.cube.name = 'skyboxCube'
  39. viewer.scene.scene.add(this.cube)
  40. }
  41. if(Potree.settings.testCube){
  42. this.cubeTest = this.cube.clone()
  43. this.cubeTest.material = new THREE.MeshBasicMaterial({
  44. wireframe:true,
  45. color:'#FF3377',
  46. transparent:true,
  47. opacity:0.7,
  48. depthWrite:false,
  49. depthTest:false,
  50. })
  51. viewer.scene.scene.add(this.cubeTest)
  52. this.cubeTest.visible = true
  53. }
  54. this.flying_ = false
  55. this.currentPano = null;
  56. this.mouseLastMoveTime = Date.now()
  57. this.scrollZoomSpeed = 0.06
  58. this.zoomLevel = 1
  59. this.depthSampler = new DepthImageSampler();
  60. viewer.fpControls.addEventListener('dollyStopCauseUnable',(e)=>{
  61. if(/* e.hoverViewport != viewer.mainViewport || */!Potree.settings.zoom.enabled)return
  62. if(e.scale != void 0){//触屏
  63. this.zoomBy(e.scale, e.pointer)
  64. }else{//滚轮
  65. let zoom;
  66. if(e.delta > 0){
  67. zoom = 1 + this.scrollZoomSpeed
  68. }else{
  69. zoom = 1 - this.scrollZoomSpeed
  70. }
  71. e.delta != 0 && this.zoomBy(zoom)
  72. }
  73. })
  74. let click = (e) => {//不用"mouseup" 是因为 mouseup有drag object时也会触发
  75. if(e.clickElement ||
  76. Potree.settings.unableNavigate || this.flying || !e.isTouch && e.button != THREE.MOUSE.LEFT || e.drag && e.drag.object //拖拽结束时不算
  77. || Potree.settings.editType == 'pano' && viewer.modules.PanoEditor.activeViewName != 'mainView'
  78. || Potree.settings.editType == 'merge' && !e.intersectPoint || viewer.inputHandler.hoveredElements[0] && viewer.inputHandler.hoveredElements[0].isModel && e.intersectPoint.distance > viewer.inputHandler.hoveredElements[0].distance
  79. ) return
  80. if(e.hoverViewport != viewer.mainViewport){ //如数据集校准其他viewport
  81. return
  82. }
  83. if(!Potree.settings.dblToFocusPoint/* && this.currentPano */){//双击不会focus点云 或者 已经focusPano了
  84. this.flyToPanoClosestToMouse()
  85. }
  86. }
  87. viewer.addEventListener('global_click' , click)
  88. viewer.addEventListener("global_mousemove", (e) => {
  89. if(!Potree.settings.unableNavigate && Potree.settings.ifShowMarker && e.hoverViewport == viewer.mainViewport){//如果不显示marker,就在点击时再更新
  90. this.updateClosestPano(e.intersect)
  91. }
  92. });
  93. this.addEventListener('markerHover',(e)=>{
  94. this.updateClosestPano(e.pano, e.hovered)
  95. })
  96. if(!Potree.settings.isOfficial){
  97. this.domRoot = viewer.renderer.domElement.parentElement;
  98. let elUnfocus = $("<input type='button' value='unfocus'></input>");
  99. elUnfocus.css({
  100. position : "absolute",
  101. right : '25%',
  102. bottom: '20px',
  103. zIndex: "10000",
  104. fontSize:'1em', color:"black",
  105. display:'none',
  106. background:'rgba(255,255,255,0.8)',
  107. })
  108. elUnfocus.on("click", () => this.unfocus());
  109. this.elUnfocus = elUnfocus;
  110. this.domRoot.appendChild(elUnfocus[0]);
  111. if(Potree.settings.editType != 'merge'){
  112. let elHide = $("<input type='button' value='隐藏点云'></input>")
  113. elHide.css({
  114. position : "absolute",
  115. right : '40%',
  116. bottom: '20px',
  117. zIndex: "10000",
  118. fontSize:'1em' ,color:"black",
  119. width : '100px',
  120. background:'rgba(255,255,255,0.8)',
  121. })
  122. this.domRoot.appendChild(elHide[0]);
  123. elHide.on("click", (e) => {
  124. let visi = Potree.Utils.getObjVisiByReason(viewer.scene.pointclouds[0], 'force')
  125. viewer.scene.pointclouds.forEach(e=>{
  126. Potree.Utils.updateVisible(e, 'force', !visi)
  127. })
  128. elHide.val(!visi ? "隐藏点云" : "显示点云")
  129. });
  130. }
  131. let elDisplayModel = $("<input type='button' value='>>全景'></input>")
  132. elDisplayModel.css({
  133. position : "absolute",
  134. right : '65%',
  135. bottom: '20px',
  136. zIndex: "10000",
  137. fontSize:'1em',color:"black",
  138. width : '100px',
  139. background:'rgba(255,255,255,0.8)',
  140. })
  141. this.domRoot.appendChild(elDisplayModel[0]);
  142. elDisplayModel.on("click", (e) => {
  143. if(Potree.settings.displayMode == 'showPointCloud' && this.panos.length == 0)return
  144. Potree.settings.displayMode = Potree.settings.displayMode == 'showPointCloud' ? 'showPanos' : 'showPointCloud'
  145. });
  146. this.elDisplayModel = elDisplayModel
  147. }
  148. {//切换模式
  149. let displayMode = '';
  150. this.latestRequestMode = '';//因为可能延迟,所以记录下每次的请求模式,延迟后判断这个
  151. Object.defineProperty(Potree.settings , "displayMode",{
  152. get: function() {
  153. return displayMode
  154. },
  155. set: (mode)=> {
  156. this.latestRequestMode = mode
  157. console.warn('Request setMode: ' + mode)
  158. let config2
  159. let config = Potree.config.displayMode[mode]
  160. if(this.isAtPano() && !this.latestToPano){
  161. config2 = config.atPano
  162. }else{
  163. config2 = config.transition
  164. }
  165. let changeTileDownload = ()=>{
  166. if(config2.showSkybox || config2.showPoint && config2.pointUsePanoTex){
  167. //this.tileDownloader.start()
  168. this.currentPano && this.currentPano.enter()
  169. }else{
  170. //this.tileDownloader.stop()
  171. this.currentPano && this.currentPano.exit()
  172. }
  173. }
  174. if(mode != displayMode){
  175. let camera = viewer.scene.getActiveCamera()
  176. if(mode == 'showPanos' && viewer.mainViewport.view.isFlying()){//飞完才能切换全景
  177. let f = ()=>{
  178. if(this.latestRequestMode == mode){//如果ui还是停在这个模式的话
  179. Potree.settings.displayMode = mode
  180. }
  181. viewer.mainViewport.view.removeEventListener('flyingDone', f)
  182. }
  183. viewer.mainViewport.view.addEventListener('flyingDone', f) //once
  184. return
  185. }
  186. if(this.isAtPano() && !this.latestToPano){
  187. config2 = config.atPano
  188. }else{
  189. config2 = config.transition
  190. if(mode == 'showPanos'){//自动飞入一个pano
  191. //要改成飞进最近的。。。
  192. if(this.panos.length == 0)return
  193. //this.modeChanging = true //主要是因为到全景图不会立刻成功
  194. let wait = (e)=>{
  195. this.removeEventListener('flyToPanoDone',wait)
  196. setTimeout(()=>{
  197. if(this.latestRequestMode == mode ){
  198. Potree.settings.displayMode = mode
  199. }
  200. },e.makeIt ? 1 : 50)
  201. }
  202. this.addEventListener('flyToPanoDone',wait) //等待飞行完毕。flyToPano的callback可能不执行所以换这个。但也可能被cancel
  203. this.flyToPano({
  204. pano: this.findNearestPano(),
  205. })
  206. return;
  207. }else{
  208. }
  209. }
  210. changeTileDownload()
  211. if(config2.showSkybox || config2.pointUsePanoTex){
  212. let wait = (e)=> {
  213. viewer.ifAllLoaded()
  214. //if(e.pano && e.pano != this.currentPano)return//loadedDepthImg
  215. setTimeout( ()=>{
  216. if(this.latestRequestMode == mode ){
  217. Potree.settings.displayMode = mode
  218. }
  219. },1)
  220. }
  221. /*
  222. //this.updateDepthTex()
  223. if(this.checkAndWaitForPanoLoad(this.currentPano, this.basePanoSize, wait)){
  224. return
  225. } */
  226. if(!this.currentPano.skyboxTex){
  227. this.currentPano.waitForLoad()
  228. this.currentPano.addEventListener('loadedTex', wait, {once:true})
  229. return this.currentPano.loadTex()
  230. }
  231. }
  232. viewer.scene.pointclouds.forEach(e=>{
  233. Potree.Utils.updateVisible(e, 'displayMode', config2.showPoint, 2 )
  234. })
  235. if(config2.pointUsePanoTex){
  236. viewer.scene.pointclouds.forEach(e=>{
  237. e.material.setProjectedPanos(this.currentPano,this.currentPano, 1)
  238. })
  239. }else{
  240. viewer.scene.pointclouds.forEach(e=>{
  241. e.material.stopProjectedPanos()
  242. })
  243. }
  244. Potree.Utils.updateVisible(this.cube,'showSkybox',config2.showSkybox )// this.cube.visible = config2.showSkybox
  245. //this.cube.visible = config.atPano.showSkybox
  246. if(this.cube.visible){
  247. //this.cube.material.setProjectedPanos(this.currentPano, this.currentPano, 1)
  248. this.setProjectedPanos({
  249. progress:1,
  250. ifSkybox: true,
  251. ifPointcloud : false,
  252. easeInOutRatio : 0,
  253. pano0:this.currentPano,
  254. pano1:this.currentPano
  255. })
  256. }else{
  257. this.smoothZoomTo(1)
  258. }
  259. /* viewer.dispatchEvent({
  260. type: "enableChangePos",
  261. canLeavePano : config.canLeavePano ,
  262. viewport:
  263. }) */
  264. //viewer.mainViewport.unableChangePos = !config.canLeavePano
  265. displayMode = mode
  266. if(mode == 'showPanos'){
  267. camera.far = viewer.farWhenShowPano //修改far
  268. Potree.settings.pointDensity = 'panorama'
  269. if(Potree.config.displayMode.showPanos.transition.pointUsePanoTex){
  270. viewer.scene.pointclouds.forEach(e=>{
  271. e.material.pointSizeType = 'FIXED'
  272. })
  273. }
  274. this.updateCube(this.currentPano)
  275. }else{
  276. if(camera.limitFar) camera.far = Potree.settings.cameraFar;//修改far
  277. Potree.settings.pointDensity = Potree.settings.UserPointDensity
  278. //Potree.sdk && Potree.sdk.scene.changePointOpacity()
  279. if(Potree.config.displayMode.showPanos.transition.pointUsePanoTex){
  280. viewer.scene.pointclouds.forEach(e=>{
  281. e.material.pointSizeType = Potree.config.material.pointSizeType
  282. })
  283. }
  284. }
  285. camera.updateProjectionMatrix()
  286. if(this.elDisplayModel){
  287. this.elDisplayModel.val( mode == 'showPointCloud' ? ">>全景" : '>>点云')
  288. }
  289. /* this.panos.forEach(e=>{
  290. Potree.Utils.updateVisible(e, 'modeIsShowPanos', mode == 'showPanos', 1, mode == 'showPanos' ? 'add':'cancel') //
  291. }) */
  292. this.dispatchEvent({type:'endChangeMode',mode})
  293. console.log('setModeSuccess: ' + mode)
  294. }else{
  295. changeTileDownload()
  296. //this.dispatchEvent({type:'endChangeMode',mode})
  297. }
  298. }
  299. })
  300. Potree.settings.displayMode = 'showPointCloud'
  301. }// 切换模式 end
  302. {//
  303. let currentPano = null;
  304. Object.defineProperty(this , "currentPano",{
  305. get: function() {
  306. return currentPano
  307. },
  308. set: function(e) {
  309. if(e != currentPano){
  310. //console.log('set currentPano ', e.id)
  311. currentPano && currentPano.exit()
  312. e && e.enter()
  313. currentPano = e
  314. }
  315. }
  316. })
  317. }
  318. {//是否显示marker
  319. let ifShowMarker = true;
  320. Object.defineProperty(Potree.settings, "ifShowMarker",{
  321. get: function() {
  322. return ifShowMarker
  323. },
  324. set: (show)=>{
  325. show = !!show
  326. if(show != ifShowMarker){
  327. this.panos.forEach(pano=>{
  328. Potree.Utils.updateVisible(pano, 'ifShowMarker', show, 1 )
  329. })
  330. //this.emit('markersDisplayChange', show)
  331. ifShowMarker = show
  332. viewer.dispatchEvent('showMarkerChanged')
  333. viewer.dispatchEvent('content_changed')
  334. }
  335. }
  336. })
  337. }
  338. viewer.addEventListener("update", () => {
  339. this.update(viewer);
  340. });
  341. //viewer.inputHandler.addInputListener(this);
  342. var keys = {
  343. FORWARD: ['W'.charCodeAt(0), 38],
  344. BACKWARD: ['S'.charCodeAt(0), 40],
  345. LEFT: ['A'.charCodeAt(0), 37],
  346. RIGHT: ['D'.charCodeAt(0), 39],
  347. }
  348. viewer.inputHandler.addEventListener('keydown',(e)=>{
  349. if(Potree.settings.displayMode == 'showPanos'){
  350. for(let i in keys){
  351. if(keys[i].some(a => a == e.keyCode)){
  352. switch(i){
  353. case 'FORWARD':
  354. this.flyLocalDirection(Vectors.FORWARD.clone());
  355. break;
  356. case 'BACKWARD':
  357. this.flyLocalDirection(Vectors.BACK.clone());
  358. break;
  359. case 'LEFT':
  360. this.flyLocalDirection(Vectors.LEFT.clone());
  361. break;
  362. case 'RIGHT':
  363. this.flyLocalDirection(Vectors.RIGHT.clone());
  364. break;
  365. }
  366. break;
  367. }
  368. }
  369. }
  370. })
  371. };
  372. updateDepthTex(pano){
  373. if(this.currentPano != pano || !pano.depthTex)return
  374. //this.depthSampler.changeImg(pano.depthTex.image); //pick sampler要飞到了才能切换图,而skybox贴图是随着全景图切换而切换的
  375. this.cube.material.updateDepthTex(pano) //确保一下
  376. }
  377. findNearestPano(pos){
  378. pos = pos ? new THREE.Vector3().copy(pos) : this.position
  379. let result = Common.sortByScore(this.panos,[Images360.filters.isEnabled()],[e=>-e.position.distanceTo(pos)])
  380. let pano = result[0] && result[0].item
  381. return pano
  382. }
  383. /* set flying(v){//正在飞向pano
  384. this.flying_ = !!v
  385. //console.log('this.flying_ ', !!v )
  386. //this.emit('flying', this.flying_)
  387. let config = Potree.config.displayMode[Potree.settings.displayMode]
  388. viewer.mainViewport.unableChangePos = !config.canLeavePano || !!v
  389. }
  390. get flying(){
  391. return this.flying_
  392. } */
  393. flyLocalDirection(dir) {
  394. var direction = this.getDirection(dir),
  395. option1 = 1 === dir.y ? .4 : .75,
  396. option2 = 1 === Math.abs(dir.x);
  397. return this.flyDirection(direction, option1, option2, true)
  398. }
  399. getDirection(e) {
  400. if(!e){
  401. return viewer.scene.view.direction
  402. }else{
  403. return e = e ? e : (new THREE.Vector3).copy(Vectors.FORWARD),
  404. e.applyQuaternion(viewer.mainViewport.camera.quaternion)
  405. }
  406. }
  407. get position(){
  408. return this.viewer.scene.view.position.clone()
  409. }
  410. isAtPano(precision){//是否在某个漫游点上
  411. if(precision){
  412. return this.currentPano && math.closeTo(viewer.scene.view.position, this.currentPano.position, precision)
  413. }
  414. return this.currentPano && viewer.scene.view.position.equals(this.currentPano.position)
  415. }
  416. updateProjectedPanos(){//更新材质贴图
  417. //console.warn('updateProjectedPanos')
  418. this.projectedPano0 && this.projectedPano1 && this.setProjectedPanos({pano0:this.projectedPano0, pano1:this.projectedPano1})
  419. }
  420. setProjectedPanos(o={}){//设置cube和点云的材质贴图
  421. this.cube.material.setProjectedPanos(o.pano0, o.pano1, o.progress)
  422. if(o.ifPointcloud){
  423. viewer.scene.pointclouds.forEach(e=>{
  424. e.material.setProjectedPanos(o.pano0, o.pano1, o.progress, o.easeInOutRatio)
  425. })
  426. }
  427. //console.warn('setProjectedPanos ', o.pano0.id , o.pano1.id)
  428. this.projectedPano0 = o.pano0
  429. this.projectedPano1 = o.pano1
  430. }
  431. cancelFlyToPano(toPano){//取消当前已有的飞行准备,前提是相机还未移动
  432. if(viewer.mainViewport.view.isFlying() || toPano && this.latestToPano != toPano)return
  433. //Potree.Log('cancelFlyToPano', this.latestToPano && this.latestToPano.pano.id)
  434. this.nextPano = null
  435. this.latestToPano = null
  436. }
  437. flyToPano(toPano) { //飞向漫游点
  438. if(!toPano)return
  439. if(toPano instanceof Panorama){
  440. toPano = {pano: toPano}
  441. }
  442. let done = (makeIt, disturb)=>{
  443. //console.log('flyToPano done ', toPano.pano.id, makeIt, disturb )
  444. if(makeIt || disturb) { // disturb已经开始飞行但中途取消
  445. toPano.callback && toPano.callback(makeIt)
  446. //this.flying = false
  447. this.cancelFlyToPano(toPano)
  448. this.updateClosestPano(this.closestPano,false) //飞行结束后取消点击漫游点时得到的closestPano
  449. }else{
  450. }
  451. this.dispatchEvent({type:'flyToPanoDone', makeIt})
  452. toPano.deferred && toPano.deferred.resolve(makeIt) //测量线截图时发现,resolve需要写在flying=false 后才行。
  453. }
  454. if(!toPano.pano.enabled)return done(false,true);
  455. //Potree.Log('hope flyToPano: '+toPano.pano.id )
  456. if(this.latestToPano && this.latestToPano != toPano && this.latestToPano.pano != this.currentPano){//还在飞//如果旧的toPano只是旋转镜头,就直接取消旧的
  457. return done(false)
  458. }
  459. //Potree.Log('flyToPano: '+toPano.pano.id, this.latestToPano && this.latestToPano.pano.id )
  460. if(this.currentPano == toPano.pano && this.isAtPano() && !toPano.target && !toPano.quaternion ){
  461. this.dispatchEvent({type:'flyToPano', toPano})
  462. return done(true);
  463. }
  464. let target = toPano.target
  465. let config = Potree.config.displayMode[Potree.settings.displayMode]
  466. let pointcloudVisi = config.atPano.showPoint //viewer.scene.pointclouds[0].visible
  467. let pano = toPano.pano
  468. let dis = pano.position.distanceTo(this.position)
  469. this.nextPano = pano
  470. this.latestToPano = toPano
  471. //this.flying = true //防止新的请求
  472. //Potree.Log('flyToPano:'+pano.id + ' , duration:'+toPano.duration, null, 12)
  473. {//不飞的话是否不要执行这段?
  474. let wait = ( )=> {
  475. viewer.ifAllLoaded()
  476. if( this.latestToPano && pano != this.latestToPano.pano)return//loadedDepthImg
  477. if(this.latestToPano != toPano)return /* Potree.Log('已经取消') *///如果取消了
  478. setTimeout(()=>{
  479. if(this.latestToPano != toPano)return
  480. this.flyToPano(toPano)
  481. },1)
  482. }
  483. if(!pano.depthTex && pano.pointcloud.hasDepthTex){ //点云模式也要加载depthTex,因获取neighbour需要用到
  484. //console.log('等待加载depthtex')
  485. pano.addEventListener('loadedDepthImg', wait, {once:true})
  486. pano.waitForLoad()
  487. return pano.loadDepthImg()
  488. }
  489. if(config.atPano.showSkybox || config.atPano.pointUsePanoTex){
  490. let a = this.updateCube(this.currentPano, toPano.pano)
  491. if(a == 'useBound'){
  492. toPano.useBound = true
  493. }
  494. if(!pano.skyboxTex){
  495. pano.waitForLoad()
  496. pano.addEventListener('loadedTex', wait, {once:true})
  497. return pano.loadTex()
  498. }
  499. /* if(this.checkAndWaitForPanoLoad(pano, toPano.basePanoSize || this.basePanoSize, wait )){
  500. return
  501. } */
  502. }
  503. }
  504. Potree.Utils.updateVisible(this.cube,'showSkybox', config.atPano.showSkybox ) // this.cube.visible = config.atPano.showSkybox
  505. //console.log('开始飞1')
  506. if(config.transition.showPoint){
  507. viewer.scene.pointclouds.forEach(e=>{
  508. Potree.Utils.updateVisible(e, 'displayMode', true)
  509. })
  510. }
  511. if(config.transition.showSkybox || config.transition.pointUsePanoTex){
  512. this.setProjectedPanos({
  513. progress:0,
  514. ifSkybox: this.cube.visible,
  515. ifPointcloud : config.transition.pointUsePanoTex,
  516. easeInOutRatio : pointcloudVisi ? 0.3 : 0,
  517. pano0:this.currentPano,
  518. pano1:pano
  519. })
  520. }
  521. const endPosition = pano.position.clone()
  522. let T = Potree.config.transitionsTime
  523. let maxTime = this.isAtPano() ? T.panoToPanoMax : T.flyIn
  524. let duration = toPano.duration == void 0 ? (T.flyMinTime+Math.min(T.flytimeDistanceMultiplier * dis, maxTime)) : toPano.duration
  525. if(toPano.useBound){
  526. duration = Math.min(1500, duration)
  527. toPano.easeName = 'easeInOutQuad'
  528. }else{
  529. if(endPosition.equals(this.position))toPano.easeName = 'easeOutSine'
  530. }
  531. {
  532. toPano.easeName = toPano.easeName || 'linearTween'
  533. toPano.duration = duration
  534. this.beforeFlyToPano(toPano)
  535. }
  536. let onUpdate = (progress)=>{
  537. this.cube.material.uniforms.progress.value = progress
  538. viewer.scene.pointclouds.forEach(e=>{
  539. e.material.uniforms.progress.value = progress
  540. })
  541. }
  542. let fly = ()=>{
  543. let startProgress = toPano.progress = toPano.progress || 0
  544. let loadNextProgress = THREE.MathUtils.clamp(1 - 2.5 / dis, 0.3, 0.8)
  545. //console.log(loadNextProgress, (1-loadNextProgress) * dis )
  546. this.dispatchEvent({type:'flyToPano', toPano})
  547. viewer.scene.view.setView({position:endPosition, target, quaternion:toPano.quaternion , duration:toPano.duration,
  548. onUpdate:(progress_, delta)=>{
  549. let progress = startProgress + progress_ * (1 - startProgress)
  550. let currentSpeed
  551. if (progress_ != 1 && progress_ != 0) { // 1的时候不准,往往偏小, 0的时候速度为0,也不记录
  552. currentSpeed = ((progress - toPano.progress) * dis) / delta //记录下当前速度,当变为匀速时可以过渡到flySpeed
  553. } else {
  554. currentSpeed = toPano.currentSpeed || 0
  555. }
  556. toPano.currentSpeed = currentSpeed
  557. toPano.progress = progress
  558. //console.log('progress_', progress_, 'delta',delta , 'progress', progress/*, 'currentSpeed', currentSpeed, */ )
  559. if (progress > loadNextProgress && toPano.easeName == 'linearTween' && currentSpeed){// 减速. 如果仅旋转就不停止
  560. //console.log('减速', currentSpeed)
  561. toPano.easeName = 'easeOutSine'
  562. let restDis = (1 - progress) * dis
  563. toPano.duration = (Math.PI / 2 * restDis) / currentSpeed // 这样能保证初始速度为currentSpeed
  564. viewer.scene.view.cancelFlying('all',false) //为了防止执行cancelFun先主动cancel
  565. toPano.flyCount = 2
  566. fly(toPano)
  567. }
  568. onUpdate(progress)
  569. },
  570. callback:()=>{
  571. if(!config.atPano.pointUsePanoTex){
  572. viewer.scene.pointclouds.forEach(e=>{
  573. e.material.stopProjectedPanos()
  574. })
  575. }
  576. this.currentPano = pano;
  577. this.nextPano = null;
  578. if(Potree.settings.displayMode == 'showPanos'){
  579. viewer.scene.pointclouds.forEach(e=>{
  580. Potree.Utils.updateVisible(e, 'displayMode',pointcloudVisi)
  581. })
  582. }
  583. done(true);
  584. this.updateDepthTex(this.currentPano)
  585. },
  586. cancelFun:()=>{ done(false, true) },
  587. Easing:toPano.easeName,
  588. ignoreFirstFrame : toPano.flyCount != 2 //变换transition时不停一帧
  589. })
  590. }
  591. if(Potree.settings.displayMode == 'showPanos'){
  592. setTimeout(fly, 40); //更新geo后缓冲
  593. }else{
  594. fly()
  595. }
  596. //console.log('flyToPano:', toPano.pano.id)
  597. }
  598. beforeFlyToPano(toPano){
  599. if(this.currentPano != toPano.pano) {
  600. this.smoothZoomTo(toPano.zoomLevel || 1, toPano.duration / 2);
  601. }
  602. }
  603. isNeighbour(pano0, pano1, dontCompute, onlyUseTex, computeDirFirst){//是否之间没有遮挡(在加载visibles之前,自己算) 最好pano0是currentPano
  604. if(!pano0 || !pano1 )return
  605. let margin = 0.1;
  606. let map0 = this.neighbourMap[pano0.id]; //主
  607. let map1 = this.neighbourMap[pano1.id]; //副
  608. //三个方向 : position0到position1, position0到floorPosition1, position1到floorPosition0。 只要有一个满足ifNeighbour就为true。 不过为了不使sampler总换图,先只考虑从主pano到副pano的方向
  609. //0能看到1不代表1能看到0; 但只要有一方能完全看到另一方,无论是position还是floorPosition都算是neighbor
  610. //也正因此,全部计算完两两之间的neighbour后,neighbourMap也看起来有缺的样子,必须得通过isNeighbour函数来判断是否是邻居
  611. let ifNeighbour
  612. if(map0[pano1.id] != void 0){
  613. ifNeighbour = map0[pano1.id]
  614. }
  615. if(!ifNeighbour && map1[pano0.id] != void 0){
  616. ifNeighbour = map1[pano0.id]
  617. }
  618. if(dontCompute) return ifNeighbour
  619. let getNeighbour = (mainPano, subPano, jumpStep1, simpleJudge)=>{
  620. //暂时只判断到pano,不判断到marker的方向
  621. let dirPoints = []
  622. if(!jumpStep1){//跳过此步骤,因为之前算过不成功(虽然用另一个漫游点算的可能拍摄时间不同所以有概率不一样,如人走动)
  623. dirPoints.push([subPano.position, mainPano.position])
  624. }
  625. if(!simpleJudge){
  626. dirPoints.push([subPano.floorPosition.clone().add(new THREE.Vector3(0,0,0.1)), mainPano.position])
  627. }
  628. for(let i=0; i<dirPoints.length; i++){
  629. let dir = new THREE.Vector3().subVectors(dirPoints[i][0], dirPoints[i][1]).normalize();
  630. let intersectPoint = viewer.images360.depthSampler.sample({dir}, mainPano, true)
  631. if(!intersectPoint || intersectPoint.distance+margin > pano0.position.distanceTo(pano1.position)){
  632. return true
  633. }
  634. }
  635. }
  636. if( map0[pano1.id] == void 0 && !ifNeighbour ) {//主方向为空且不为邻居
  637. let simpleJudge = pano0.position.distanceToSquared(pano1.position) > 300 //在远处去掉对floorPosition的判断
  638. if(pano0.depthTex || pano1.depthTex){
  639. if(map0[pano1.id] == void 0 && pano0.depthTex){
  640. let is = getNeighbour(pano0, pano1, null, simpleJudge)
  641. if(is){
  642. ifNeighbour = true
  643. }else if(simpleJudge){
  644. ifNeighbour = false
  645. }
  646. map0[pano1.id] = !!is
  647. }
  648. if( ifNeighbour == void 0 && /* map1[pano0.id] == void 0 && */ pano1.depthTex){ //若正向为false,反向暂且不算,等到pano0为主时再算
  649. let is = getNeighbour(pano1, pano0, !ifNeighbour, simpleJudge)
  650. if(is){
  651. ifNeighbour = true
  652. }else if(simpleJudge){
  653. ifNeighbour = false
  654. }
  655. map1[pano0.id] = !!is
  656. }
  657. ifNeighbour = !!ifNeighbour
  658. //console.log('isNeighbour', pano0, pano1, ifNeighbour)
  659. /* if(ifNeighbour){ //需要标记成全部true吗,不标记也能get到,但标记了更直观,不标记保留信息更多
  660. map0[pano1.id] = map1[pano0.id] = true
  661. } */
  662. }else if(!onlyUseTex){//使用点云判断(有深度贴图时不会执行到这)
  663. let inDirection = ()=>{
  664. let dir = new THREE.Vector3().subVectors(pano1.position,pano0.position).normalize()
  665. let dis = pano1.position.distanceTo(pano0.position)
  666. let hfov = cameraLight.getHFOVForCamera(viewer.mainViewport.camera , true );
  667. let max = Math.cos(THREE.Math.degToRad(10))
  668. let min = Math.cos(THREE.Math.degToRad(80))
  669. if(this.getDirection().dot(dir) > THREE.Math.clamp(Math.cos(hfov/2 ) * dis / 10, min, max )){//距离越远要求和视线角度越接近
  670. return true
  671. }
  672. }
  673. if(computeDirFirst){//先计算方向,防止重复计算ifBlockedByIntersect
  674. if(inDirection()){
  675. ifNeighbour = !viewer.inputHandler.ifBlockedByIntersect({pos3d:pano1.position, margin, cameraPos:pano0.position})
  676. }
  677. }else{
  678. ifNeighbour = !viewer.inputHandler.ifBlockedByIntersect({pos3d:pano1.position, margin, cameraPos:pano0.position})
  679. if(ifNeighbour && !inDirection()){
  680. ifNeighbour = undefined //不确定
  681. }
  682. }
  683. //}
  684. map0[pano1.id] = map1[pano0.id] = ifNeighbour ? 'byCloud' : ifNeighbour//写简单点
  685. }
  686. }
  687. if(ifNeighbour){
  688. pano0.neighbours.includes(pano1) || pano0.neighbours.push(pano1)
  689. pano1.neighbours.includes(pano0) || pano1.neighbours.push(pano0)
  690. }
  691. return ifNeighbour
  692. }
  693. bump(direction) {//撞墙弹回效果
  694. if (!this.bumping && !this.latestToPano) {
  695. let distance = Potree.settings.displayMode == 'showPanos' ? 0.3 : 0.2;//感觉点云模式比全景模式更明显,所以降低
  696. let currentPos = this.position.clone()
  697. let endPosition = new THREE.Vector3().addVectors(this.position, direction.clone().multiplyScalar(distance))
  698. let duration = 150
  699. viewer.scene.view.setView({position:endPosition, duration,
  700. callback:()=>{
  701. viewer.scene.view.setView({position:currentPos, duration: duration*5,
  702. callback: ()=>{
  703. this.bumping = false
  704. //this.dispatchEvent('cameraMoveDone')
  705. },
  706. Easing:'easeInOutSine',
  707. cancelFun:()=>{this.bumping = false}
  708. })
  709. this.bumping = true
  710. },
  711. cancelFun:()=>{this.bumping = false},
  712. Easing:'easeInOutSine'
  713. })
  714. this.bumping = true
  715. }
  716. //备注:将4dkk中的‘前后方向变化fov、左右方向移动镜头’ 都改为移动镜头。 因为这里无法判断左右离壁距离。
  717. }
  718. flyToPanoClosestToMouse() {
  719. if(!Potree.settings.ifShowMarker){//不显示marker的时候mousemove没更新鼠标最近点所以更新
  720. this.updateClosestPano(viewer.inputHandler.intersect)
  721. }
  722. //console.log('flyToPanoClosestToMouse',this.closestPano)
  723. if (this.closestPano) {
  724. let pano = this.closestPano
  725. return this.flyToPano({
  726. pano, easeName: this.isAtPano() ? 'linearTween' : 'easeInOutQuad'
  727. });
  728. }
  729. var direction = this.viewer.inputHandler.getMouseDirection().direction;
  730. this.flyDirection(direction)
  731. }
  732. flyDirection(direction, option1, option2, byKey) {
  733. if(viewer.mainViewport.view.isFlying()){// closestPanoInDirection函数耗时长,飞行时运行会卡顿(如果以后加无缝过渡再说)
  734. return
  735. }
  736. var deferred = $.Deferred();
  737. //this.history.invalidate();
  738. var panoSet = this.closestPanoInDirection(direction, option1, option2, byKey);
  739. if (panoSet) {
  740. this.flyToPano({
  741. pano: panoSet,
  742. callback: deferred.resolve.bind(deferred, !0)
  743. } );
  744. } else {
  745. this.bump(direction);
  746. deferred.resolve(!1);
  747. }
  748. return deferred.promise();
  749. }
  750. closestPanoInDirection(direction, option1, option2, byKey) {
  751. return this.rankedPanoInDirection(0, direction, option1, option2, byKey)
  752. }
  753. rankedPanoInDirection(t, direction, option1, option2, byKey){
  754. //此direction为mouseDirection,是否需要加上相机角度的权重
  755. //let startTime = Date.now()
  756. var panoSet = {
  757. pano: null,
  758. candidates: [] //缓存顺序--如果需要打印的话
  759. };
  760. t || (t = 0);
  761. option1 = void 0 !== option1 ? option1 : .75;
  762. var o = option2 ? "angle" : "direction";
  763. let disSquareMap = new Map()
  764. this.panos.forEach(pano=>{
  765. let dis2 = pano.position.distanceToSquared(this.position); //距离目标点
  766. disSquareMap.set(pano, dis2)
  767. })
  768. let changeTexCount = 0, maxWaitDur = 300
  769. var request = [//必要条件
  770. Images360.filters.not(this.currentPano),
  771. Images360.filters.isEnabled(),
  772. Images360.filters.inFloorDirection( this.position, direction, option1 ), //原先用inPanoDirection,但容易穿楼层,当mouse较低或较高
  773. Images360.filters.inPanoDirection( this.position, this.getDirection(), option1/* , true */), //垂直方向上再稍微限制一下, 要接近视线方向,避免点击前方时因无路而到下一楼。但不能太高,否则楼梯上稍微朝下点击都到不了上方。之所以使用视线方向是因为镜头方向比鼠标方向目的性更强。
  774. (pano)=>{
  775. let isNeighbour = this.isNeighbour(this.currentPano, pano, false, true); // 注: 不会再changeTex了
  776. if(isNeighbour || pano.noNeighbour && disSquareMap.get(pano) < 200){//在靠近孤立点时可以通行。但是不好把握这个距离,太远的话很多地方都会不小心到孤立点,太近的话可能永远到不了。
  777. return true
  778. }
  779. },
  780. ]
  781. var list = [//决胜项目
  782. (pano)=>{
  783. return -disSquareMap.get(pano)
  784. },
  785. Images360.scoreFunctions[o]( this.position, direction, true),
  786. (pano)=>{
  787. let neighbour = this.isNeighbour(this.currentPano, pano, true, true) //不计算的
  788. return neighbour ? directionFactor : 0;
  789. } ,
  790. ];
  791. if(!byKey && viewer.inputHandler.intersect && this.currentPano ){//方便上下楼, 考虑panos之间的角度差
  792. let pos1 = this.currentPano.floorPosition
  793. let vec1 = new THREE.Vector3().subVectors(viewer.inputHandler.intersect.location, pos1 ).normalize()//应该只有atPano时才会执行到这吧?
  794. list.push( function(pano) {
  795. var pos2 = pano.floorPosition;
  796. var vec2 = pos2.clone().sub(pos1).normalize();
  797. return vec2.dot(vec1) * directionFactor * 4
  798. })
  799. }
  800. this.findRankedByScore(t,request,list,panoSet);
  801. //console.log( 'costTime:',Date.now() - startTime)
  802. return panoSet.pano;
  803. }
  804. findRankedByScore(e, t, i, n) {
  805. n && (n.candidates = null, //candidates 缓存顺序--如果需要打印的话
  806. n.pano = null),
  807. e || (e = 0);
  808. var r = Common.sortByScore(this.panos, t, i);
  809. //console.log('findRankedByScore', r && r.map(u=>u.item.id + '| ' + math.toPrecision(u.score,4) + " | " + math.toPrecision(u.scores,4)))
  810. return !r || 0 === r.length || e >= r.length ? null : (n && (n.candidates = r,
  811. n.pano = r[e].item),
  812. r[e].item)
  813. }
  814. updateClosestPano(intersect, state) {//hover到的pano 大多数时候是null
  815. var pano
  816. if(intersect instanceof Panorama){ //漫游模式
  817. pano = state ? intersect : null
  818. }else{
  819. if(this.isAtPano() || this.bumping){
  820. return
  821. }else{
  822. var filterFuncs = [];
  823. intersect = intersect && intersect.location
  824. if(!intersect)return
  825. let sortFuncs = Potree.settings.editType != 'pano'? [Images360.sortFunctions.floorDisSquaredToPoint(intersect)] : [Images360.sortFunctions.disSquaredToPoint(intersect)]
  826. pano = Common.find(this.panos, filterFuncs, sortFuncs);
  827. }
  828. }
  829. if (pano != this.closestPano) {
  830. pano && (this.isPanoHover = !0);
  831. this.closestPanoChanging(this.closestPano, pano) // 高亮marker
  832. //console.log('closestPano '+ (pano ? pano.id : 'null' ))
  833. this.closestPano = pano;
  834. } else {
  835. this.isPanoHover = !1;
  836. }
  837. }
  838. closestPanoChanging(oldPano, newPano){
  839. if(!Potree.settings.ifShowMarker)return
  840. oldPano && oldPano.hoverOff({byImages360:true})
  841. newPano && newPano.hoverOn({byImages360:true})
  842. }
  843. /*
  844. getTileDirection(){//根据不同dataset的来存储
  845. var vectorForward = viewer.scene.view.direction.clone()
  846. var vectorForwards = viewer.scene.pointclouds.map(e=>{
  847. var inv = new THREE.Matrix4().copy(e.rotateMatrix).invert()//乘上dataset的旋转的反转
  848. var direction = vectorForward.clone().applyMatrix4(inv)
  849. return {
  850. datasetId: e.dataset_id,
  851. direction: math.convertVector.ZupToYup(direction)
  852. }
  853. })
  854. //return vectorForwards[0].direction
  855. return {
  856. datasetsLocal: vectorForwards,
  857. vectorForward
  858. }
  859. }
  860. */
  861. fitPanoTowardPoint(o){ //寻找最适合的点位
  862. var point = o.point, //相机最佳位置
  863. target = o.target || o.point, //实际要看的位置
  864. require = o.require || [],
  865. rank = o.rank || [],
  866. force = o.force,
  867. getAll = o.getAll,
  868. bestDistance = o.bestDistance || 0,
  869. sameFloor = o.sameFloor,
  870. maxDis = o.maxDis,
  871. dir = o.dir
  872. let camera = viewer.scene.getActiveCamera()
  873. if(target && !dir){
  874. dir = new THREE.Vector3().subVectors(target,point).normalize()
  875. }
  876. let atFloor = sameFloor && viewer.modules.SiteModel.pointInWhichEntity(point, 'floor')
  877. //if(o.floor)require.push(Panorama.filters.atFloor(o.floor))
  878. let checkIntersect = o.checkIntersect
  879. let base = Math.max(300, viewer.bound.boundSize.length()*3);
  880. if(o.boundSphere){//只接受boundSphere
  881. let aspect = 1//size.x / size.y
  882. let dis
  883. if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定
  884. dis = /* size.y */o.boundSphere.radius/* / 2 *// THREE.Math.degToRad(camera.fov / 2)
  885. }else{
  886. let hfov = cameraLight.getHFOVForCamera(camera , true );
  887. dis = /* size.x */ o.boundSphere.radius /* / 2 */ / (hfov / 2)
  888. }
  889. bestDistance = dis//*0.8
  890. }
  891. let disSquareMap = new Map()
  892. let bestDisSquared = bestDistance * bestDistance
  893. let maxDisSquared = maxDis && (maxDis * maxDis)
  894. this.panos.forEach(pano=>{
  895. let dis2 = pano.position.distanceToSquared(target); //距离目标点
  896. disSquareMap.set(pano, dis2)
  897. })
  898. let panos = this.panos.sort((p1,p2)=>{return disSquareMap.get(p1)-disSquareMap.get(p2)})
  899. if(maxDisSquared){//热点超过最大距离不可见的
  900. let panos2 = [], pano, i=0
  901. while(pano = panos[i], disSquareMap.get(pano) < maxDisSquared){
  902. panos2.push(pano)
  903. i++;
  904. }
  905. if(panos2.length == 0)return {pano, msg:'tooFar'} //全部都大于maxDis, 就返回最近的
  906. panos = panos2
  907. }
  908. rank.push((pano)=>{
  909. let dis1 = Math.abs(pano.position.distanceToSquared(point) - bestDisSquared); //距离最佳位置
  910. disSquareMap.set(pano, dis1)
  911. if(!target){
  912. return -dis1
  913. }else{
  914. let dis2 = disSquareMap.get(pano)
  915. let vec2 = new THREE.Vector3().subVectors(target,pano.position).normalize()
  916. let cos = dir.dot(vec2)
  917. //let result = (- dis1 - Math.pow(dis2 , 1.5)) / (cos + 2) // cos+2是为了调整到1-3,
  918. let result = (dis1 + dis2*0.3) * ( -1 + cos*0.9 ) //尽量贴近最佳位置的角度, 或贴近相机原来的角度 。尽量靠近最佳观测点,并且优先选择靠近目标点的位置.(注意cos的乘数不能太接近1,否则容易只考虑角度)
  919. //Potree.Log(pano.id, dis1, dis2, cos, result,{font:{toFixed:2,fontSize:10}})
  920. return result
  921. }
  922. //注:热点最好加上法线信息,这样可以多加一个限制,尽量顺着热点像展示的方向。
  923. },
  924. (pano)=>{
  925. let score = 0
  926. if(pano.depthTex && checkIntersect){
  927. let intersect = !!viewer.ifPointBlockedByIntersect(target, pano.id, true) //viewer.inputHandler.ifBlockedByIntersect({pos3d:target, margin:0.1, cameraPos:pano})
  928. if(intersect){
  929. score = 0
  930. }else {
  931. score = base * 2
  932. }
  933. }else{
  934. score = base * 1.5 //没加载好的话,不管了 , 几乎当做无遮挡,否则容易到不了最近点
  935. }
  936. return score
  937. }
  938. )
  939. var g = Common.sortByScore(panos, require, rank);
  940. console.log(g)
  941. let pano = g && g.length > 0 && g[0].item
  942. if(pano && checkIntersect){
  943. let intersect = !!viewer.ifPointBlockedByIntersect(target, pano.id, true)
  944. if(intersect){
  945. return {pano, msg : 'sheltered'}
  946. }
  947. }
  948. //if(getAll)return g;
  949. return pano
  950. //注:深度图有的位置会不准确,以至于会算出错误的遮挡、选择错误的pano,解决办法只能是移动下热点到更好的位置
  951. }
  952. //---------------scroll zoom ------------------------------------------
  953. /* zoomIn = function() { //放大
  954. this.zoomBy(1 + this.zoomSpeed);
  955. }
  956. zoomOut = function() {//缩小
  957. this.zoomBy(1 - this.zoomSpeed);
  958. } */
  959. zoomBy(e, pointer) {//以倍数
  960. this.zoomTo(this.zoomLevel * e, pointer);
  961. }
  962. zoomTo(zoomLevel, pointer) {//缩放到某绝对zoomLevel
  963. let zoom = Potree.settings.zoom
  964. if (zoom.enabled) {
  965. zoomLevel = THREE.Math.clamp(zoomLevel, zoom.min, zoom.max)
  966. //console.log(zoomLevel)
  967. if(zoomLevel == this.zoomLevel) return;
  968. /* if (zoomLevel > this.zoomLevel) {
  969. this.emit(ZoomEvents.ZoomIn);
  970. zoomLevel === settings.zoom.max && this.emit(ZoomEvents.ZoomMax);
  971. } else if (zoomLevel < this.zoomLevel) {
  972. this.emit(ZoomEvents.ZoomOut);
  973. zoomLevel === settings.zoom.min && this.emit(ZoomEvents.ZoomMin);
  974. } */
  975. this.zoomLevel = zoomLevel;
  976. //定点缩放:使当前鼠标所在的位置缩放后不变
  977. let view = viewer.scene.view
  978. let originDir = viewer.scene.view.direction;
  979. let oldPointerDir = viewer.inputHandler.getMouseDirection(pointer).direction
  980. viewer.setFOV(Potree.config.view.fov * (1 / this.zoomLevel))
  981. let newPointerDir = viewer.inputHandler.getMouseDirection(pointer).direction
  982. view.direction = oldPointerDir; //获取一下鼠标所在位置的yaw 和 pitch
  983. let oldPitch = view.pitch, oldYaw = view.yaw;
  984. view.direction = newPointerDir;
  985. let newPitch = view.pitch, newYaw = view.yaw;
  986. view.direction = originDir //还原
  987. viewer.scene.view.pitch -= newPitch - oldPitch
  988. viewer.scene.view.yaw -= newYaw - oldYaw
  989. }
  990. }
  991. zoomFovTo( fov ) { //通过fov来算zoomLevel
  992. let zoomLevel = Potree.config.view.fov /* this.baseFov */ / fov;
  993. this.zoomTo( zoomLevel );
  994. }
  995. smoothZoomTo(aimLevel, dur=0) {
  996. var currentLevel = this.zoomLevel
  997. if(currentLevel == aimLevel)return;
  998. var fun = (progress)=>{
  999. //progress > 1 && (progress = 1)
  1000. let level = currentLevel * (1 - progress) + aimLevel * progress
  1001. this.zoomTo(level, !0)
  1002. }
  1003. transitions.start(fun, dur, null, null, 0 , easing['easeInOutQuad'] )
  1004. }
  1005. getIntersect(pano, dir, origin){
  1006. if(pano && pano.pointcloud.hasDepthTex ){
  1007. return this.depthSampler.sample( {dir }, pano, true )
  1008. }else{
  1009. origin = origin || pano.position
  1010. return viewer.inputHandler.getIntersect(viewer.inputHandler.hoverViewport, true, null, null, true, {
  1011. point: origin.clone().add(dir),
  1012. cameraPos: origin
  1013. })
  1014. }
  1015. }
  1016. addPanoData(data ){//加载漫游点
  1017. //data[0].file_id = '00019'
  1018. data = data.sweepLocations
  1019. if(data.length == 0)console.error( '没有漫游点')
  1020. //data = data.sort(function(a,b){return a.id-b.id})
  1021. data.forEach((info)=>{
  1022. //if(Potree.fileServer){
  1023. info.id = this.panos.length //把info的id的一长串数字改简单点
  1024. //}
  1025. let pano = new Panorama( info, this );
  1026. pano.addEventListener('dispose',(e)=>{
  1027. if(this.closestPano == pano) this.closestPano = null
  1028. })
  1029. this.panos.push(pano);
  1030. })
  1031. }
  1032. loadDone(){
  1033. Potree.Utils.setObjectLayers(this.node, 'sceneObjects')
  1034. this.panos.forEach(e=>{
  1035. this.neighbourMap[e.id] = {}
  1036. e.label && Potree.Utils.setObjectLayers(e.label, 'bothMapAndScene')
  1037. })
  1038. {
  1039. let minSize = new THREE.Vector3(1,1,1)
  1040. this.bound = math.getBoundByPoints(this.panos.map(e=>e.position), minSize)
  1041. viewer.scene.pointclouds.forEach(pointcloud=>pointcloud.getPanosBound())
  1042. }
  1043. if(viewer.scene.pointclouds.some(e=>e.panos.length == 0)){
  1044. //console.warn('存在数据集没有pano');
  1045. viewer.hasNoPanoDataset = true
  1046. }
  1047. }
  1048. getPano(value, typeName='id'){ //默认找的是id,也可以是sid、uuid
  1049. return this.panos.find(p=>p[typeName] == value)
  1050. }
  1051. update(){
  1052. if(!this.currentPano)return
  1053. Potree.Common.intervalTool.isWaiting('filterDepthTex', ()=>{
  1054. var s = [Images360.filters.not(this.currentPano )],
  1055. l = [Images360.scoreFunctions.distanceSquared(this.currentPano ), Images360.scoreFunctions.direction(this.position, this.getDirection())]
  1056. this.nearPanos = Common.sortByScore(this.panos, s, l).map(e=>e.item);
  1057. //下载深度图
  1058. let depTexDlCount = browser.isMobile() ? 1 : 2;
  1059. let loadingCount = this.nearPanos.filter(p=>p.depthTexLoading).length;
  1060. if(loadingCount<depTexDlCount){
  1061. this.nearPanos.filter(p=>!p.depthTex).slice(0, depTexDlCount-loadingCount).forEach(p=>p.loadDepthImg());
  1062. }
  1063. }, 77)
  1064. this.getNeighbours(this.nearPanos)
  1065. }
  1066. };
  1067. Images360.prototype.getNeighbours = function(){ //逐渐自动获取neighbours。 200个点差不多在半分钟内算完
  1068. let lastIndex //标记上次查询到哪,防止重新sortByScore
  1069. return function(nearPanos){
  1070. if(!this.currentPano || viewer.mainViewport.view.isFlying() || viewer.lastFrameChanged || viewer.inputHandler.drag /* interacted */){ //拖拽时不更新,否则移动端卡
  1071. return lastIndex = 0;
  1072. }
  1073. if(!nearPanos)return;
  1074. //let startTime = Date.now()
  1075. let panos = [this.currentPano, ...nearPanos ]
  1076. this.depthSampler.updateNearPanos(panos)
  1077. let maxWaitDur = browser.isMobile() ? 40 : 60
  1078. let changeCount = 0, getCount = 0
  1079. let changeTexCount = ()=>{
  1080. changeCount ++;
  1081. }
  1082. let median = Math.max(10, Potree.timeCollect.depthSampler.median)
  1083. let ifOverTime = ()=>{
  1084. let is = changeCount * median + getCount * 0.01 > maxWaitDur//不换贴图也要一丢丢计算时间
  1085. /* if(is){
  1086. console.log(1)
  1087. } */
  1088. return is
  1089. }
  1090. this.depthSampler.addEventListener('changeImg', changeTexCount)
  1091. outer: for(let i=lastIndex,j=panos.length; i<j; i++){
  1092. let pano = panos[i];
  1093. let others = panos.slice(i+1, j)
  1094. lastIndex = i
  1095. var g = Common.sortByScore(others, [ ], [
  1096. Images360.scoreFunctions.distanceSquared(pano.position)
  1097. ]);
  1098. for(let a=0, b=g.length; a<b; a++){
  1099. let item = g[a]
  1100. if(this.isNeighbour(pano, item.item, true) != void 0) continue
  1101. let byCloud = !pano.pointcloud.hasDepthTex
  1102. let result = this.isNeighbour(pano, item.item, false, !byCloud, true)//计算
  1103. if(result != void 0){//计算了
  1104. //console.log('提前计算neighbor', pano.id, item.item.id)
  1105. this.dispatchEvent({type:'getNeighbourAuto', panos:[pano,item.item]})
  1106. if(byCloud){ //只计算一次
  1107. //console.log('提前计算neighbor byCloud', result, pano.id, item.item.id)
  1108. break outer;
  1109. }
  1110. getCount ++
  1111. if(ifOverTime()){
  1112. break outer
  1113. }
  1114. }
  1115. }
  1116. lastIndex = i+1 //这轮结束
  1117. }
  1118. this.depthSampler.removeEventListener('changeImg', changeTexCount)
  1119. /* let costTime = Date.now() - startTime
  1120. costTime > maxWaitDur && console.log( 'costTime:',costTime) */
  1121. }
  1122. }()
  1123. Images360.filters = {
  1124. inPanoDirection : function(pos, dir, i, log) { //pano在mouse的方向上
  1125. return function(pano) {
  1126. var r = pano.floorPosition.clone().sub(pos).normalize()
  1127. var o = pano.position.clone().sub(pos).normalize()
  1128. log && console.log('dire',pano.id, r.dot(dir), o.dot(dir) )
  1129. return r.dot(dir) > i || o.dot(dir) > i
  1130. }
  1131. },
  1132. inFloorDirection: function(pos, dir, min, log) { //pano在mouse的水平方向上
  1133. return function(pano) {
  1134. var vec = new THREE.Vector2().subVectors(pano.floorPosition, pos).normalize()
  1135. var dir_ = new THREE.Vector2().copy(dir).normalize()
  1136. log && console.log('dire', pano.id, vec.dot(dir_) )
  1137. return vec.dot(dir_) > min
  1138. /* var i = pano.floorPosition.clone().sub(pos).setZ(0).normalize();//改成在xz方向上,否则点击墙面不会移动
  1139. return i.dot(dir.clone().setZ(0)) > min */
  1140. }
  1141. },
  1142. isNotBehindNormal: function(e, t) {
  1143. var i = new THREE.Vector3;
  1144. return t = t.clone(),
  1145. function(n) {
  1146. var r = i.copy(n.position).sub(e).normalize();
  1147. return r.dot(t) > 0
  1148. }
  1149. },
  1150. isCloseEnoughTo: function(e, t) {
  1151. return function(i) {//因为marker可能比地面高,所以识别范围要比marker看起来更近一些。(因为投影到地板的位置比marker更近)
  1152. return e.distanceTo(i.floorPosition) < t //许钟文
  1153. }
  1154. },
  1155. not: function(e) {
  1156. return function(t) {
  1157. return t !== e
  1158. }
  1159. } ,
  1160. isEnabled:function() {
  1161. return function(t) {
  1162. return t.enabled
  1163. }
  1164. },
  1165. isVisible:function() {
  1166. return function(t) {
  1167. return t.visible
  1168. }
  1169. }
  1170. }
  1171. Images360.scoreFunctions = {
  1172. direction: function(curPos, dir, ifLog) {
  1173. return function(pano) {
  1174. var pos1 = /* pano.floorPosition */ pano.position //旧:改为权重放在marker上,这样对有斜坡的更准确,如上楼, 但这样近距离的pano角度就会向下了,以致于走不到
  1175. var n = pos1.clone().sub(curPos).normalize();
  1176. //ifLog && console.log('direction', pano.id, n.dot(dir) * directionFactor )
  1177. return n.dot(dir) * directionFactor
  1178. }
  1179. },
  1180. distance: function(pos1, r=1, ifLog) {
  1181. if(pos1.position)pos1 = pos1.position
  1182. return function(pano) {//许钟文 改
  1183. var pos2 = pano.position.clone()
  1184. //ifLog && console.log('distance', pano.id, pos1.distance(pos2) * -1 )
  1185. return pos1.distanceTo(pos2) * -1 * r;
  1186. }
  1187. },
  1188. distanceSquared: function(pos1, r=1 ) {
  1189. if(pos1.position)pos1 = pos1.position
  1190. return function(pano) {//许钟文 改
  1191. var pos2 = pano.position.clone()
  1192. return pos1.distanceToSquared(pos2) * -1 * r;
  1193. }
  1194. },
  1195. angle: function(e, t) {
  1196. return function(i) {
  1197. var n = i.position.clone().sub(e).normalize();
  1198. return n.angleTo(t) * Potree.config.navigation.angleFactor
  1199. }
  1200. },
  1201. }
  1202. Images360.sortFunctions = {//排序函数,涉及到两个item相减
  1203. floorDisSquaredToPoint: function(e) {
  1204. return function(t, i) {
  1205. return t.floorPosition.distanceToSquared(e) - i.floorPosition.distanceToSquared(e)
  1206. }
  1207. },
  1208. disSquaredToPoint: function(e) {
  1209. return function(t, i) {
  1210. return t.position.distanceToSquared(e) - i.position.distanceToSquared(e)
  1211. }
  1212. },
  1213. }
  1214. Images360.prototype.updateCube = (function(){//增加细分的版本,且垂直方向上取中位数 侧边多条
  1215. const minDis = 0.2; //pano和墙距离不能小于相机的near
  1216. const height = 1 //准确计算的话要分别计算两个pano的地面、天花板之间的俯仰角,和pano之间的还不一样。这里统一假设一个比较小的高度,因高度越大 maxSinAlpha 越大
  1217. let minTanBeta = minDis / height /* (pano0.position.z - pano0.floorPosition.z) */
  1218. let minBeta = Math.atan(minTanBeta)
  1219. const maxSinAlpha = Math.cos(minBeta) // 注:beta = Math/2 - alpha
  1220. const skyHeight = 50
  1221. return function(pano0, pano1){
  1222. if(Potree.settings.displayMode != 'showPanos' || pano0 == pano1
  1223. || this.cubePanos.includes(pano0) && this.cubePanos.includes(pano1)
  1224. ) return
  1225. this.cubePanos = [pano0, pano1]
  1226. viewer.addTimeMark('updateCube','start')
  1227. //console.log('updateCube',pano0.id, pano1&&pano1.id)
  1228. let useBound = (bound, size)=>{
  1229. size = size || bound.getSize(new THREE.Vector3)
  1230. let center = bound.getCenter(new THREE.Vector3)
  1231. size.max(new THREE.Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth))
  1232. this.cube.geometry = new THREE.BoxBufferGeometry(1,1,1,1)
  1233. this.cube.scale.copy(size)
  1234. this.cube.position.copy(center)
  1235. if(Potree.settings.testCube){
  1236. this.cubeTest.geometry = this.cube.geometry
  1237. this.cubeTest.scale.copy(size)
  1238. this.cubeTest.position.copy(center)
  1239. }
  1240. return 'useBound'
  1241. }
  1242. let getPanoBound = (pano)=>{//因漫游点可能在点云外部,如室外平地,所以需要union进漫游点
  1243. let panoBound = new THREE.Box3
  1244. panoBound.expandByPoint(pano.position)
  1245. panoBound.expandByVector(new THREE.Vector3(10,10,10));//give pano a margin
  1246. return pano.pointcloud.bound.clone().union(panoBound)
  1247. }
  1248. this.cube.geometry.dispose();
  1249. if(pano1){//过渡
  1250. let dontAddSides
  1251. let dis = pano0.position.distanceTo(pano1.position)
  1252. let sinAlpha = Math.abs(pano0.position.z - pano1.position.z) / dis //俯仰角的sin,随角度增大而增大 0-1
  1253. let score = (1+sinAlpha*20) * dis //score越大创建的mesh越不适合
  1254. let isNeighbour = this.isNeighbour(pano0, pano1)
  1255. //console.log(pano0.id, pano1.id, maxSinAlpha.toFixed(2), sinAlpha.toFixed(2), score.toFixed(2), isNeighbour)
  1256. //let depthTiming = Potree.timeCollect.depthSampler.median //pc firefox达到4. chrome为0.01
  1257. if(sinAlpha>maxSinAlpha || !pano0.pointcloud.hasDepthTex || !pano1.pointcloud.hasDepthTex || (isNeighbour ? score > 100 : score > 50 ) ){
  1258. let bound = getPanoBound(pano0).union(getPanoBound(pano1))
  1259. let size = bound.getSize(new THREE.Vector3)
  1260. let max = Math.max(size.x, size.y, size.z)
  1261. size.set(max,max,max) //距离太远的数据集,过渡会畸变。所以扩大skybox,且为立方体
  1262. return useBound(bound, size)
  1263. }else if(/* depthTiming > 1 || */ (isNeighbour ? score > 70 : score > 15)){
  1264. dontAddSides = true //pano间有阻挡时得到的side点可能使通道变窄,所以去掉。
  1265. }
  1266. //俯仰角增大时可能不在同一楼层,算出来的mesh不太好,所以更倾向直接使用cube,或去除side。
  1267. let half //6 : (browser.isMobile() ? 2 : 3) //自行输入 (点云计算的慢,还不准)
  1268. {
  1269. let min = 3,max = 6, minTime = 0, maxTime = 3
  1270. /* half = math.linearClamp(depthTiming, minTime,maxTime, max,min)
  1271. half = Math.round(half) */
  1272. half = max
  1273. }
  1274. let count1 = 2*half//偶数个 每个pano向 外dir 个数
  1275. //奇数个的好处:在窄空间内能探测到最远距离,坏处是前方有尖角。偶数个的坏处就是可能检测距离太近。
  1276. //let panoIndex = 0
  1277. let getDir = (angle_, vec)=>{ //旋转获得水平向量
  1278. let rotMat = new THREE.Matrix4().makeRotationZ(angle_)
  1279. return vec.clone().applyMatrix4(rotMat)
  1280. }
  1281. let getFar = (dir, pano, origin, height)=>{//获取在这个方向上和墙体intersect的距离
  1282. //在垂直方向上分出多个方向,取一个最可能的接近真实的距离
  1283. let maxH = 40, minH = 2, minR = 0.5, maxR = 2
  1284. height = height == void 0 ? (Math.min(skyHeight,pano.ceilZ) - pano.floorPosition.z) : height
  1285. //let r = height (maxH - minH)* 0.14 // 高度越小,角度越小
  1286. //let r = minR + ( maxR - minR) * THREE.Math.clamp((height - minH) / (maxH - minH),0,1) //THREE.Math.smoothstep(currentDis, op.nearBound, op.farBound);
  1287. let r = math.linearClamp(height, minH,maxH, minR, maxR)
  1288. let getZ = (deg)=>{
  1289. deg *= r
  1290. deg = THREE.Math.clamp(deg, 1, 80);
  1291. return Math.tan(THREE.Math.degToRad(deg))
  1292. }
  1293. let dirs_ //注意:角度太大会碰到天花板或地板,越远越容易碰到, 在地下停车场就会伸展不开。 户外时需要更多向上的方向,所以上方向多一个
  1294. /* if(depthTiming > 2) dirs_ = [35,0,-5]
  1295. else if(depthTiming > 0.5) dirs_ = [35,10,0,-5]
  1296. else */dirs_ = [35,20,7, 0,-5]
  1297. dirs_ = dirs_.map(deg=> dir.clone().setZ(getZ(deg)).normalize() )
  1298. let max = 50
  1299. let count2 = dirs_.length
  1300. let disArr = dirs_.map((dir_, i) =>{
  1301. let intersect = this.getIntersect(pano, dir_, origin)
  1302. let projectLen = intersect && intersect.distance ? dir_.dot(dir)*intersect.distance : max; //得到project在dir的长度
  1303. return projectLen //得水平距离
  1304. })
  1305. //console.log(pano ? pano.id : 'side','disArr', disArr.slice(0))
  1306. disArr.sort((a,b)=>{return b-a}); //从大到小
  1307. //console.log(pano ? pano.id : 'side','disArr', disArr)
  1308. let dis = disArr[Math.floor(count2/2-0.5)] //对半、取前(中位数)
  1309. return dis
  1310. }
  1311. let sideCount = [0,0]
  1312. let addPos = (pano, vec )=>{//添加这个pano这一侧向外半圆的顶点
  1313. //添加pano位置对应的最高点最低点:
  1314. let minZ, maxZ
  1315. minZ = pano.floorPosition.z
  1316. maxZ = pano.getCeilHeight()
  1317. if(maxZ == Infinity) maxZ = skyHeight; //maxZ = Math.max(skyHeight, maxZ)
  1318. [maxZ, minZ ].forEach(z=>{
  1319. posArr.push(pano.position.clone().setZ(z))
  1320. })
  1321. //在画面上线条从左往右数
  1322. const angle = Math.PI/(count1-1)
  1323. const dirs = []; //平分这半边180度
  1324. for(let i=0;i<count1;i++){
  1325. dirs.push(getDir(Math.PI/2-i*angle, vec))//正的在左边
  1326. }
  1327. let dirs2 = dirs.map((dir)=>{
  1328. return {
  1329. dir,
  1330. dis: getFar(dir, pano)
  1331. }
  1332. })
  1333. //剔除那些突然间离相机很近的dir。有可能是拍摄的人、或者杆子、树
  1334. /* dirs2.forEach((e,i)=>{
  1335. console.log(i, e.dis)
  1336. let smallThanBefore = ()=>{
  1337. return dirs2[i-1].dis / e.dis > maxRatio
  1338. }
  1339. let smallThanAfter = ()=>{
  1340. return dirs2[i+1].dis / e.dis > maxRatio
  1341. }
  1342. if(i>0 && i<count1-1 && smallThanBefore() && smallThanAfter()){//比左右两边都小很多
  1343. e.disB = (dirs2[i-1].dis + dirs2[i+1].dis) / 2 //平均数
  1344. console.log('两者之间',i,e.disB)
  1345. }else if(i==count1-1 && smallThanBefore() ) {//比前者小很多
  1346. e.disB = dirs2[i-1].dis * 0.8
  1347. console.log('smallThanBefore', i, e.disB)
  1348. }else if(i==0 && smallThanAfter() ){//比后者小很多
  1349. e.disB = dirs2[i+1].dis * 0.8
  1350. console.log('smallThanAftere', i, e.disB)
  1351. }
  1352. }) */
  1353. const maxRatio1 = 3 , maxRatio2 = 8 //超过maxRatio1就要加入判断,而最终结果的设限其实是maxRatio2
  1354. const minWidth = 0.5
  1355. let computeWidth = (start,end)=>{
  1356. start+=1 //不包含start和end
  1357. let count = end - start ;
  1358. let dis = 0
  1359. for(let m=start;m<end;m++){//得平均数
  1360. dis += dirs2[m].dis
  1361. }
  1362. dis /= count
  1363. let angle = Math.PI / (count1-1) * count / 2
  1364. let width = dis * Math.tan(angle) //得到block的半宽度
  1365. return width
  1366. }
  1367. let changeDis = (start,end )=>{ //不包含start
  1368. start+=1 //不包含start和end
  1369. for(let m=start;m<end;m++){
  1370. dirs2[m].disB = dirs2[end].dis * 0.8
  1371. //console.log('changeDis', m, dirs2[m].disB)
  1372. }
  1373. }
  1374. let start = -1
  1375. for(let i=0;i<count1;i++){//遍历时将左边dis比之小很多且宽度较小的改大
  1376. //console.log(i, dirs2[i].dis)
  1377. let j = i-1
  1378. let ratios = 0
  1379. while(j>start && dirs2[i].dis / dirs2[j].dis > maxRatio1){
  1380. ratios += dirs2[i].dis / dirs2[j].dis
  1381. j--
  1382. }
  1383. let count = i-j-1
  1384. ratios /= count
  1385. if(count > 0 && computeWidth(j,i)< minWidth * ratios / maxRatio2 ){ //怎么感觉好像改成了视觉宽度小于某个值即可,那直接用count好了?
  1386. changeDis(j,i)
  1387. start = i //在此之前的修改过,之后不用再判断
  1388. }
  1389. /* if(count > 0 && (count == 1 || computeWidth(j,i)<minWidth)){//若只有一个不用判断宽度直接修改
  1390. changeDis(j,i)
  1391. start = i //在此之前的修改过,之后不用再判断
  1392. } */
  1393. }
  1394. dirs2.forEach((e, index)=>{
  1395. let dir = e.dir.clone().multiplyScalar(e.disB || e.dis);
  1396. [maxZ,minZ].forEach(z=>{
  1397. posArr.push(pano.position.clone().setZ(z).add(dir)) //获取到外墙点
  1398. })
  1399. });
  1400. }
  1401. let addSide = ()=>{//两个漫游点间两边各加一些侧线
  1402. //中点处的
  1403. let top0 = pano0.ceilZ == Infinity ? skyHeight : pano0.ceilZ
  1404. let top1 = pano1.ceilZ == Infinity ? skyHeight : pano1.ceilZ
  1405. let midMaxZ = (top0 + top1)/2
  1406. let midMinZ = (pano0.floorPosition.z+pano1.floorPosition.z)/2;
  1407. let mid = new THREE.Vector3().addVectors(pano0.position, pano1.position).multiplyScalar(0.5)
  1408. if(!dontAddSides){
  1409. if( pano0.pointcloud.hasDepthTex && pano0.pointcloud.hasDepthTex){
  1410. let panos = [pano0,pano1]
  1411. let vecs = [vec.clone().negate(), vec]
  1412. let axis = [[-1,1],[1,-1]]
  1413. let dis2d = new THREE.Vector2().subVectors(pano0.position, pano1.position).length()//水平上的距离
  1414. let maxDis = 50, minDis = 0.5, minR = 0.2, maxR = 1.2
  1415. //let r = maxR - ( maxR - minR) * THREE.Math.clamp((dis2d - minDis) / (maxDis - minDis),0,1) //dis2d越大,角度要越小 //THREE.Math.smoothstep(currentDis, op.nearBound, op.farBound);
  1416. let r = math.linearClamp(dis2d, minDis,maxDis, maxR, minR)
  1417. //console.log('dis2d',dis2d,'r',r)
  1418. let angles = (browser.isMobile ? [50] : [35,65]).map(deg=>{ //正的在左边 尽量能够平分中间这段墙体。 (角度为从中心向外)
  1419. let angle = THREE.Math.clamp(deg * r, 5, 80);
  1420. //console.log('angle',angle)
  1421. return THREE.Math.degToRad(angle)
  1422. })
  1423. axis.forEach((axis_, index0)=>{
  1424. let disToSides = []
  1425. let accordingPano = index0 == 0 ? pano0 : pano1; //根据离该点在vec方向上的距离顺序来存顶点
  1426. panos.forEach((pano,index)=>{
  1427. let dirs = angles.map(angle=>getDir(axis_[index]*angle, vecs[index]))//一侧的若干角度
  1428. dirs.forEach((dir_,i)=>{
  1429. let dis1 = getFar(dir_, pano);
  1430. let disToPano2d = dis1 * Math.cos(angles[i])
  1431. if(disToPano2d<dis2d){//超过的话都到另一半pano的半圆了,不计入
  1432. let disToSide = dis1 * Math.sin(angles[i])
  1433. if(pano != accordingPano){
  1434. disToPano2d = dis2d - disToPano2d //反一下
  1435. }
  1436. dir_.multiplyScalar( dis1 );
  1437. disToSides.push({disToSide,disToPano2d, pano, dir_})
  1438. }
  1439. })
  1440. })
  1441. if(disToSides.length){
  1442. //disToSides.sort((a,b)=>{return b-a});//从大到小
  1443. //由距离accordingPano的近到远:
  1444. disToSides.sort((a,b)=>{return a.disToPano2d-b.disToPano2d})
  1445. //console.log('disToSides', index0, disToSides)
  1446. if(disToSides.length == 1 && disToSides[0].disToSide < 0.5){
  1447. disToSides = [] //如果太近直接去除
  1448. }
  1449. disToSides.forEach(e=>{//求z
  1450. let ratio = e.disToPano2d / dis2d
  1451. let r = accordingPano == pano0 ? (1-ratio) : ratio
  1452. let sideMaxZ_ = top0 * r + top1 * (1-r);
  1453. let sideMinZ_ = pano0.floorPosition.z * r + pano1.floorPosition.z * (1-r);
  1454. [sideMaxZ_,sideMinZ_].forEach(z=>{
  1455. posArr.push(e.pano.position.clone().setZ(z).add(e.dir_)) //是直接使用最长dis的那个intersect点好还是mid
  1456. })
  1457. })
  1458. }
  1459. sideCount[index0] = disToSides.length //记录侧边个数
  1460. })
  1461. }else{
  1462. //这段针对点云时,仅测试才会执行到
  1463. sideCount = [1,1]
  1464. let sideDirs = [getDir(Math.PI/2, vec), getDir(-Math.PI/2, vec)]
  1465. sideDirs.forEach((dir_ ,index)=>{
  1466. let dis = getFar(dir_, null, mid, midMaxZ-midMinZ); //直接从中点求两侧的距离
  1467. dir_.multiplyScalar( /* Math.max( */dis/* , sideDis[index]) */ );
  1468. [midMaxZ,midMinZ].forEach(z=>{
  1469. posArr.push(mid.clone().setZ(z).add(dir_))
  1470. })
  1471. })
  1472. }
  1473. }
  1474. //中心:
  1475. [midMaxZ,midMinZ].forEach(z=>{
  1476. posArr.push(mid.clone().setZ(z))
  1477. })
  1478. }
  1479. //positions存放顺序:pano的每边的 zMax和zMin 、count1个dir的点 ;侧边的点 ;连接处顶底的中点
  1480. let addFaces = ()=>{
  1481. let getPI = function(index, posType, panoIndex){//获取顶点序号
  1482. return 2 + (count1*2 + 2 ) * panoIndex + index*2 + (posType == 'top' ? 0 : 1)
  1483. }
  1484. let getSidePI = function(index, posType, panoIndex){//获取侧边顶点序号
  1485. if(panoIndex == 1) index += sideCount[0]
  1486. return getPI(index, posType, 2)-2
  1487. }
  1488. let getPanoPI = function(posType, panoIndex){//获取pano处对应的点序号
  1489. return getPI(-1, posType, panoIndex)
  1490. }
  1491. let topCenter = posArr.length-2; //最后添加的两个中心点
  1492. let btmCenter = posArr.length-1;
  1493. for(let i=0;i<2;i++){
  1494. for(let index=1; index<count1; index++){
  1495. //pano外侧半圆围墙
  1496. faceArr.push([getPI(index,'top',i), getPI(index-1,'btm',i), getPI(index-1,'top',i)],//加入一块四方面
  1497. [getPI(index,'top',i), getPI(index,'btm',i), getPI(index-1,'btm',i)])
  1498. faceArr.push([getPI(index,'top',i), getPI(index-1,'top',i), getPanoPI('top',i)])//天花板扇形
  1499. faceArr.push([getPI(index,'btm',i), getPI(index-1,'btm',i), getPanoPI('btm',i)])//地板扇形
  1500. }
  1501. let j = (i + 1) % 2; //另一个pano
  1502. //侧边
  1503. for(let u=0; u<=sideCount[i]; u++){
  1504. //侧边每个四方的左上右上左下右下四个点
  1505. let pLT = u == 0 ? getPI(0,'top',i) : getSidePI(u-1, 'top',i)
  1506. let pRT = u == sideCount[i] ? getPI(count1-1,'top',j) : getSidePI(u, 'top',i)
  1507. let pLB = u == 0 ? getPI(0,'btm',i) : getSidePI(u-1, 'btm',i)
  1508. let pRB = u == sideCount[i] ? getPI(count1-1,'btm',j) : getSidePI(u, 'btm',i)
  1509. faceArr.push([pLT,pLB,pRB],[pLT,pRB,pRT])//外侧四方面
  1510. faceArr.push([pLT,topCenter,pRT] ,[pLB,btmCenter,pRB] )//顶部和底部到整体中心的扇形(由于点的顺序是根据和两个pano的距离,因此到中心的夹角并没按顺序排列,所以可能会重叠)
  1511. if(i==0){//只需要算一次
  1512. //顶部和底部中心与两个pano边构成的三角形
  1513. if(u == 0){
  1514. faceArr.push([pLT,getPI(count1-1,'top',i),topCenter],
  1515. [pLB,getPI(count1-1,'btm',i),btmCenter],
  1516. )
  1517. }
  1518. //不能用else 因为当length==0时u==0也==sideCount[i],此时就是要两个面
  1519. if(u == sideCount[i]){
  1520. faceArr.push([pRT,getPI(0,'top',j),topCenter],
  1521. [pRB,getPI(0,'btm',j),btmCenter],
  1522. )
  1523. }
  1524. }
  1525. }
  1526. }
  1527. }
  1528. let posArr = [];
  1529. let faceArr = []
  1530. //两点连线的水平向量
  1531. let vec = new THREE.Vector3().subVectors(pano0.position, pano1.position).setZ(0).normalize()
  1532. /* let sideDis0 = */addPos(pano0, vec)
  1533. /* let sideDis1 = */addPos(pano1, vec.clone().negate())
  1534. addSide()
  1535. addFaces()
  1536. //MeshDraw.updateGeometry(this.cube.geometry, posArr, faceArr)
  1537. let geo = MeshDraw.createGeometry(posArr, faceArr)
  1538. this.cube.geometry = geo
  1539. this.cube.scale.set(1,1,1);
  1540. this.cube.position.set(0,0,0)
  1541. if(Potree.settings.testCube){
  1542. this.cubeTest.geometry = geo
  1543. this.cubeTest.scale.set(1,1,1);
  1544. this.cubeTest.position.set(0,0,0)
  1545. }
  1546. }else{
  1547. useBound(getPanoBound(pano0))
  1548. }
  1549. viewer.addTimeMark('updateCube','end')
  1550. return true
  1551. /*
  1552. 关于卡顿:
  1553. 即使使用cube,若scale设置为只容纳两个pano,也会卡顿。但也是有的卡有的不卡。
  1554. 若按照原先的复杂geo,一般在平直的街道上行走流畅,经过拐弯或者复杂区域较卡。减小geo复杂度没有什么作用。
  1555. 注: 修改skybox,若不准的话,会遮住其他mesh,比如marker。尤其在没有深度贴图时。
  1556. 耗时: chrome: 20+ ,但是firefox:500+ 咋回事 ? iphonex safari: 40+
  1557. */
  1558. }
  1559. })()