MapViewer.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. import * as THREE from "../../../libs/three.js/build/three.module.js";
  2. import {MapLayer} from './Map'
  3. import {FirstPersonControls} from '../../navigation/FirstPersonControls'
  4. import {View} from "../View.js";
  5. import Viewport from "../Viewport.js";
  6. import {InputHandler} from "../../navigation/InputHandler.js";
  7. import {ViewerBase} from "../viewerBase.js"
  8. import math from "../../utils/math.js";
  9. //import CursorDeal from '../utils/CursorDeal'
  10. import {Images360} from '../../modules/Images360/Images360'
  11. import Common from '../../utils/Common'
  12. import {transitions, easing, lerp} from "../../utils/transitions.js";
  13. import {config } from "../../settings.js";
  14. /* var centerCross = new THREE.Mesh(new THREE.SphereGeometry(1, 4, 4),new THREE.MeshBasicMaterial({
  15. transparent:true, color:"#ff0000", opacity:0.5
  16. })); */
  17. /* const mapHeight = -1000;//要比点云低。最低
  18. const cameraHeight = 1000; //最高 */
  19. const panosHeight = config.map.mapHeight + 100 //要比点云低 (marker)
  20. const cursorHeight = 0;//比地图高就行
  21. const routeLayerHeight = config.map.mapHeight + 105
  22. const texLoader = new THREE.TextureLoader()
  23. const planeGeo = new THREE.PlaneBufferGeometry(1,1)
  24. const markerSize = 1;
  25. var initCameraFeildWidth = 50
  26. var panoMarkerMats
  27. export class MapViewer extends ViewerBase{
  28. constructor(dom){
  29. super(dom, {
  30. clearColor: Potree.config.mapBG,
  31. name: 'mapViewer'
  32. })
  33. this.visible = true
  34. this.initScene()
  35. this.mapLayer = new MapLayer(this, this.viewports[0])
  36. this.scene.add(this.mapLayer.sceneGroup)
  37. this.mapLayer.sceneGroup.position.setZ(Potree.config.map.mapHeight)
  38. this.mapRatio = 0.5
  39. this.splitDir = 'leftRight'
  40. //this.scene.add(centerCross)
  41. viewer.addEventListener("camera_changed", (e)=>{
  42. if(e.viewport == viewer.mainViewport) this.updateCursor()
  43. else this.updateWhenAtViewer()
  44. })
  45. //viewer.addEventListener("global_mousemove", this.updateWhenAtViewer.bind(this)) //鼠标移动时reticule也动,所以直接就needRender
  46. viewer.reticule.addEventListener('update',(e)=>{
  47. if(this.attachedToViewer)this.needRender = true
  48. })
  49. viewer.scene.addEventListener("360_images_added", this.addPanos.bind(this))
  50. viewer.addEventListener("loadPointCloudDone", this.initProjection.bind(this))
  51. this.addEventListener('global_click',(e)=>{
  52. if(!e.isTouch && e.button != THREE.MOUSE.LEFT)return
  53. this.updateClosestPano(e.intersectPoint)
  54. })
  55. this.addEventListener('add',(e)=>{//添加其他mesh
  56. this.scene.add(e.object)
  57. if(e.name == 'route'){
  58. e.object.position.z = routeLayerHeight
  59. }
  60. viewer.setObjectLayers(e.object, 'mapObjects' )
  61. })
  62. if(!Potree.settings.isOfficial){
  63. let domRoot = viewer.renderer.domElement.parentElement;
  64. let elAttach = $("<input type='button' value='map绑定'></input>");
  65. elAttach.css({
  66. position : "absolute",
  67. right : '80%',
  68. bottom: '20px',
  69. zIndex: "10000",
  70. fontSize:'1em', color:"black",
  71. background:'rgba(255,255,255,0.8)',
  72. })
  73. let state = false
  74. elAttach.on("click", () => {
  75. state = !state
  76. this.attachToMainViewer(state, 'measure')
  77. elAttach.val(state ? 'map分离':'map绑定')
  78. });
  79. domRoot.appendChild(elAttach[0]);
  80. }
  81. }
  82. waitLoadDone(callback){//确保加载完后执行
  83. if(this.mapLayer.loadingInProgress == 0){
  84. callback()
  85. }else{
  86. var f = ()=>{
  87. callback()
  88. this.mapLayer.removeEventListener('loadDone', f)
  89. }
  90. this.mapLayer.addEventListener('loadDone', f)
  91. }
  92. }
  93. addListener(images360){
  94. images360.addEventListener('flyToPano',e=>{
  95. let toPano = e.toPano
  96. if(toPano.dontMoveMap) return
  97. /* transitions.start(lerp.vector(this.view.position, toPano.pano.position.clone().setZ(cameraHeight),
  98. (pos, progress)=>{
  99. }), toPano.duration, null, 0, easing[toPano.easeName] ); */
  100. let boundSize// = new THREE.Vector2(10,10)
  101. this.moveTo(toPano.pano.position.clone().setZ(Potree.config.map.cameraHeight), boundSize, toPano.duration, toPano.easeName)
  102. })
  103. }
  104. initProjection(){
  105. this.started = true
  106. this.mapLayer.initProjection()
  107. }
  108. initScene(){
  109. let w = initCameraFeildWidth
  110. let width = this.renderArea.clientWidth;
  111. let height = this.renderArea.clientHeight;
  112. //let aspect = width / height
  113. this.camera = new THREE.OrthographicCamera(-width/2,width/2,height/2,-height/2/* -w/2, w/2, w/2/aspect, -w/2/aspect */, 0.01, 10000);
  114. this.camera.zoom = width / w //zoom越大视野越小
  115. //this.camera.position.set(0,0,10);
  116. this.camera.up.set(0,0,1)
  117. //this.camera.lookAt(new THREE.Vector3())
  118. //this.camera.updateMatrixWorld()
  119. this.view = new View();
  120. this.view.position.set(0,0,Potree.config.map.cameraHeight);
  121. this.view.lookAt(0,0,0)
  122. this.setViewLimit('standard')
  123. let viewport = new Viewport( this.view, this.camera, {
  124. left:0, bottom:0, width:1, height: 1, name:'mapViewport'
  125. })
  126. viewport.axis = ["x","y"]
  127. viewport.axisSign = [1,1]
  128. viewport.noPointcloud = true; //不要渲染点云
  129. viewport.render = this.render.bind(this)//标志给mainView渲染
  130. //viewport.unableDepth = true //depthBasicMaterial等在此viewport中不开启depth
  131. this.viewports = [viewport]
  132. this.controls = new FirstPersonControls(this, this.viewports[0]);
  133. this.controls.setEnable(true)
  134. this.scene = new THREE.Scene();
  135. this.inputHandler = new InputHandler(this, this.scene);
  136. this.inputHandler.name = 'mapInputHandler'
  137. //this.inputHandler.addInputListener(this.controls);
  138. this.inputHandler.registerInteractiveScene(this.scene);//interactiveScenes
  139. this.viewports[0].interactiveScenes = this.inputHandler.interactiveScenes;//供viewer的inputHandler使用
  140. var cursor = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({
  141. transparent:true,
  142. opacity:0.9,
  143. depthTest : false, //防止透明冲突
  144. map: texLoader.load(Potree.resourcePath+'/textures/pic_location128.png' )
  145. }))
  146. cursor.position.set(0,0,cursorHeight);
  147. this.cursor = cursor
  148. this.scene.add(cursor)
  149. viewer.setObjectLayers(this.scene, 'mapObjects' )
  150. }
  151. setViewLimit(state){//设置边界,当编辑空间模型等时要解禁
  152. let setting = Potree.config.OrthoCameraLimit[state]
  153. if(setting){
  154. this.view.limitBound = new THREE.Box3().copy(setting.posBound)
  155. this.camera.zoomLimit = $.extend({},setting.zoom);
  156. }else{
  157. this.view.limitBound = null
  158. this.camera.zoomLimit = null
  159. }
  160. }
  161. updateCursor(){
  162. var scale = math.getScaleForConstantSize( {//规定下最小最大像素
  163. minSize : 80, maxSize : 200, nearBound : initCameraFeildWidth*0.1 , farBound : initCameraFeildWidth*2,
  164. camera:this.camera , position: this.cursor.getWorldPosition(new THREE.Vector3()),
  165. resolution: this.viewports[0].resolution//2
  166. })
  167. this.cursor.scale.set(scale, scale, scale);//当地图缩放时
  168. this.cursor.position.copy(viewer.mainViewport.camera.position).setZ(cursorHeight)//当场景镜头旋转移动时
  169. this.cursor.rotation.z = viewer.mainViewport.view.yaw
  170. this.needRender = true
  171. }
  172. addPanos(e){
  173. var panosGroup = new THREE.Object3D;
  174. panoMarkerMats = {
  175. default: new THREE.MeshBasicMaterial({
  176. transparent:true,
  177. opacity: 0.5,
  178. map: texLoader.load(Potree.resourcePath+'/textures/map_marker.png' ),
  179. }),
  180. selected: new THREE.MeshBasicMaterial({
  181. transparent:true,
  182. opacity: 1,
  183. map: texLoader.load(Potree.resourcePath+'/textures/map_marker.png' ),
  184. })
  185. }
  186. e.images.panos.forEach(pano=>{
  187. pano.mapMarker = new THREE.Mesh(planeGeo, panoMarkerMats.default);
  188. pano.mapMarker.position.copy(pano.position).setZ(0)
  189. pano.mapMarker.scale.set(markerSize,markerSize,markerSize)
  190. pano.mapMarker.name = 'mapMarker'
  191. panosGroup.add(pano.mapMarker);
  192. let mouseover = (e)=>{
  193. if(!e.byMap){
  194. pano.mapMarker.material = panoMarkerMats.selected
  195. if(!e.byMainView) pano.dispatchEvent({type: "hoverOn", byMap:true})
  196. this.needRender = true
  197. }
  198. }
  199. let mouseleave = (e)=>{
  200. if(!e.byMap){
  201. pano.mapMarker.material = panoMarkerMats.default
  202. if(!e.byMainView) pano.dispatchEvent({type: "hoverOff", byMap:true})
  203. this.needRender = true
  204. }
  205. }
  206. pano.mapMarker.addEventListener('mouseover', mouseover);
  207. pano.mapMarker.addEventListener('mouseleave', mouseleave);
  208. pano.addEventListener('hoverOn', mouseover)
  209. pano.addEventListener('hoverOff', mouseleave)
  210. let onclick = (e)=>{
  211. viewer.images360.flyToPano(pano)
  212. }
  213. pano.mapMarker.addEventListener('click', onclick);
  214. pano.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)
  215. //console.log('panoMarker isVisible', pano.id, e.visible)
  216. viewer.updateVisible(pano.mapMarker, 'panoVisible', e.visible )
  217. this.needRender = true
  218. })
  219. pano.addEventListener('rePos',(e)=>{
  220. pano.mapMarker.position.copy(pano.position).setZ(0)
  221. })
  222. })
  223. this.scene.add(panosGroup)
  224. panosGroup.position.z = panosHeight
  225. this.panosGroup = panosGroup
  226. viewer.setObjectLayers(panosGroup, 'mapObjects' )
  227. /* e.images.on('markersDisplayChange', (show)=>{
  228. panosGroup.visible = show
  229. this.needRender = true
  230. }) */
  231. //-------
  232. //this.fitPanosToViewport()
  233. this.initFitView()
  234. }
  235. updateClosestPano(intersect){
  236. if(viewer.images360.flying)return;
  237. intersect = intersect && intersect.orthoIntersect
  238. if(!intersect)return
  239. intersect = intersect.clone().setZ(0)
  240. const minDis = 20 //距离鼠标不能太远
  241. var filterFuncs = [
  242. (pano)=>{
  243. return pano.position.clone().setZ(0).distanceTo(intersect) < minDis
  244. },
  245. Images360.filters.isEnabled(),
  246. Images360.filters.isVisible(),//只走显示的点,否则会走到别的层
  247. ];
  248. var pano = Common.find(viewer.images360.panos, filterFuncs, [Images360.sortFunctions.floorDisSquaredToPoint(intersect)]);
  249. if (pano && pano != viewer.images360.currentPano ) {
  250. viewer.images360.flyToPano(pano)
  251. }
  252. }
  253. fitPanosToViewport(){//使所有漫游点占满viewport
  254. //var w = viewer.bound.boundSize.x;
  255. var boundSize = viewer.images360.bound.size.clone().multiplyScalar(1.1);
  256. boundSize.max(new THREE.Vector3(4,4,4))
  257. let endPosition = viewer.images360.bound.center.clone()
  258. this.moveTo(endPosition, boundSize, 0)
  259. }
  260. fitToPointcloud(pointcloud, duration=400){
  261. var boundSize = pointcloud.bound.getSize(new THREE.Vector3)/* .multiplyScalar(1.1); */
  262. boundSize.max(new THREE.Vector3(4,4,4))
  263. let endPosition = pointcloud.bound.getCenter(new THREE.Vector3)
  264. this.moveTo(endPosition, boundSize, duration) //给点duration去变化 否则地图放大后所占的还是很小
  265. }
  266. initFitView(){
  267. let dis = 5 , px = 70 //地图上px像素长度代表的距离为dis //px是手动缩放到5m后发现是这个长度
  268. let zoom = px / dis;
  269. this.camera.zoom = zoom
  270. this.moveTo(viewer.images360.position/* viewer.images360.bound.center */)
  271. this.camera.updateProjectionMatrix()
  272. }
  273. fitToDatasets(datasets){
  274. let bound = new THREE.Box3;
  275. datasets.forEach(e=>bound.union(e.bound))
  276. let center = bound.getCenter(new THREE.Vector3)
  277. let size = bound.getSize(new THREE.Vector3)
  278. this.moveTo(center, size, 200 ) //给duration是为了顺应视口大小改变,缓冲
  279. }
  280. moveTo(endPosition, boundSize, duration=0, easeName){//前两个参数有xy即可
  281. endPosition = new THREE.Vector3(endPosition.x,endPosition.y,Potree.config.map.cameraHeight)
  282. this.view.moveOrthoCamera(this.viewports[0], {endPosition, boundSize }, duration, easeName)
  283. /* let endZoom, startZoom = this.camera.zoom
  284. //修改相机为bound中心,这样能看到全部(宽度范围内)
  285. this.view.setView({ position:endPosition, duration,
  286. callback:()=>{//done
  287. },
  288. onUpdate:(progress)=>{
  289. if(boundSize){
  290. let aspect = boundSize.x / boundSize.y
  291. let w, h;
  292. if(this.camera.aspect > aspect){//视野更宽则用bound的纵向来决定
  293. h = boundSize.y
  294. //w = h * this.camera.aspect
  295. endZoom = this.viewports[0].resolution.y / h
  296. }else{
  297. w = boundSize.x;
  298. //h = w / this.camera.aspect
  299. endZoom = this.viewports[0].resolution.x / w
  300. }
  301. //onUpdate时更新endzoom是因为画布大小可能更改
  302. this.camera.zoom = endZoom * progress + startZoom * (1 - progress)
  303. this.camera.updateProjectionMatrix()
  304. }
  305. },
  306. Easing:easeName
  307. }) */
  308. }
  309. updateWhenAtViewer(e){
  310. if(this.attachedToViewer){
  311. if(this.started) this.mapLayer.update()
  312. this.updateCursor()
  313. this.needRender = true
  314. }
  315. }
  316. update(delta, areaSize ){
  317. if(!this.visible && !this.attachedToViewer )return
  318. if(this.attachedToViewer){
  319. if(this.mapLayer.needUpdate){//必须更新。(较少触发)
  320. this.updateWhenAtViewer()
  321. }
  322. return
  323. }
  324. this.updateScreenSize()
  325. this.controls.update(delta);
  326. this.view.applyToCamera(this.camera)
  327. let changed = this.cameraChanged()
  328. if(this.started && (changed || this.mapLayer.needUpdate))this.mapLayer.update()
  329. if(changed /*|| || this.needRender */){
  330. this.dispatchEvent({
  331. type: "camera_changed",
  332. camera: this.camera,
  333. viewport : this.viewports[0]
  334. })
  335. this.needRender = true
  336. this.updateCursor()//更改大小
  337. }
  338. this.render()
  339. }
  340. attachToMainViewer(state, desc, mapRatio=0.5, options={}){//转移到viewer中。测量时展示or截图需要
  341. if(!Potree.settings.isOfficial)this.renderArea.style.display = state ? 'none':'block'
  342. if(state){
  343. this.enabledOld = this.enabled
  344. this.enabled = true
  345. if(mapRatio != 'dontSet'){
  346. this.changeSplitScreenDir(options.dir, mapRatio)
  347. if(this.attachedToViewer){
  348. //this.fitPanosToViewport()
  349. viewer.updateScreenSize({forceUpdateSize:true})
  350. return
  351. }
  352. viewer.viewports = [viewer.mainViewport, viewer.mapViewer.viewports[0] ];//因为mainViewer的相机变化会触发map的变化,所以先渲染mainViewer
  353. }
  354. if(desc == 'measure') this.inputHandler.registerInteractiveScene(viewer.measuringTool.scene);//虽然用的是viewer的inputHandler,但借用了this.inputHandler的interactiveScenes
  355. else if(desc == 'split4Screens') {
  356. this.inputHandler.registerInteractiveScene(viewer.scene.scene);
  357. }
  358. }else{
  359. if(!this.attachedToViewer)return
  360. viewer.mainViewport.left = 0;
  361. viewer.mainViewport.bottom = 0;
  362. viewer.mainViewport.width = 1;
  363. viewer.mainViewport.height = 1;
  364. this.viewports[0].width = 1;
  365. this.viewports[0].bottom = 0;
  366. this.viewports[0].height = 1;
  367. this.viewports[0].left = 0;
  368. this.inputHandler.unregisterInteractiveScene(viewer.measuringTool.scene);
  369. this.inputHandler.unregisterInteractiveScene(viewer.scene.scene);
  370. viewer.viewports = [viewer.mainViewport]
  371. this.updateScreenSize({forceUpdateSize:true}) //更新相机projectionMatrix
  372. }
  373. //if(desc == 'measure')this.renderMeasure = state
  374. this.attachedToViewer = state
  375. viewer.updateScreenSize({forceUpdateSize:true})
  376. //mapRatio != 'dontSet' && !options.dontFit && this.moveTo(...)//要写在updateScreenSize后面,因为要根据视图大小来fit
  377. if(options.moveToCurrentPos){
  378. let boundSize = new THREE.Vector2(10,10)
  379. let duration = 1000
  380. this.moveTo(viewer.images360.position.clone(), boundSize, duration)
  381. }
  382. this.needRender = true
  383. }
  384. changeSplitScreenDir(dir, mapRatio){//左右 | 上下
  385. //if(!dir || dir == this.dir)return
  386. if(dir )this.splitDir = dir
  387. this.updateSplitSize(mapRatio)
  388. /* if(this.attachedToViewer){ //如果已经分屏了,中途修改方向的话……
  389. this.updateSplitSize()
  390. } */
  391. }
  392. updateSplitSize(mapRatio){
  393. if(mapRatio != void 0) this.mapRatio = mapRatio
  394. //console.log(this.mapRatio)
  395. if(this.splitDir == 'leftRight'){//地图在左方
  396. viewer.mainViewport.left = this.mapRatio
  397. viewer.mainViewport.width = 1-this.mapRatio
  398. this.viewports[0].width = this.mapRatio;
  399. viewer.mainViewport.bottom = this.viewports[0].bottom = 0
  400. viewer.mainViewport.height = this.viewports[0].height = 1
  401. }else if(this.splitDir == 'upDown'){ //地图在下方
  402. viewer.mainViewport.bottom = this.mapRatio
  403. viewer.mainViewport.height = 1-this.mapRatio
  404. this.viewports[0].height = this.mapRatio;
  405. viewer.mainViewport.left = this.viewports[0].left = 0
  406. viewer.mainViewport.width = this.viewports[0].width = 1
  407. }
  408. if(this.attachedToViewer){
  409. viewer.updateScreenSize({forceUpdateSize:true})
  410. }
  411. }
  412. render(params={}){
  413. if(!this.visible && !this.attachedToViewer || !this.needRender && !params.force)return
  414. let renderer = params.renderer || this.renderer
  415. if(params.target){
  416. renderer.setRenderTarget(params.target)
  417. }
  418. /* if(params.resize){
  419. this.emitResizeMsg(new THREE.Vector2(params.width,params.height, viewport:params.viewport))
  420. } */
  421. params.clear ? params.clear() : renderer.clear();
  422. if(!this.attachedToViewer){
  423. viewer.dispatchEvent({type: "render.begin", viewer: this, viewport:this.viewports[0], params });
  424. }
  425. viewer.setCameraLayers(this.camera, ['map','mapObjects' , 'bothMapAndScene' ])
  426. if(this.mapGradientBG){//渲染背景
  427. viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg);
  428. renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
  429. }
  430. renderer.render(this.scene, this.camera);
  431. renderer.render(viewer.scene.scene, this.camera);
  432. //测量线等
  433. //params.renderOverlay && params.renderOverlay({camera:this.camera, isMap:true, viewport: this.viewports[0] })//其余如reticule 等场景层
  434. params.renderOverlay && params.renderOverlay( $.extend({}, params, { isMap:true }))
  435. renderer.setRenderTarget(null)
  436. this.needRender = false
  437. }
  438. /* render(){
  439. let camera = viewer.scene.getActiveCamera();
  440. viewer.scene.view.position.x += 0.01
  441. viewer.setCameraLayers(camera, ['sceneObjects','marker','reticule','skybox' ])
  442. this.renderer.render(viewer.scene.scene, camera);
  443. } */
  444. }
  445. //本地调试地图白屏是因为代码自动更新了 但没刷新