ConvertViews.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  1. import math from './math.js'
  2. let bimViewer
  3. export default class ConvertViews extends THREE.EventDispatcher{
  4. constructor( ) {
  5. super()
  6. this.settings = {
  7. durations : {flyToPano:1000, dolly:20, bimAniOrigin:1000},
  8. checkModeDelay : 1000,
  9. }
  10. }
  11. bindWithSameFakeType(sourceFakeApp, targetApp){//for mobile 实际只有一个场景,未分屏,和上一个场景相比
  12. let sourceApp = {sceneType: sourceFakeApp.sceneType, fakeApp:sourceFakeApp}
  13. this.createTempApp(targetApp, true)
  14. let convertInfo = this.computeAveDiffLon(sourceFakeApp, targetApp.fakeApp)
  15. sourceApp.sceneName = 'sourceApp'
  16. targetApp.sceneName = 'targetApp'
  17. if(sourceApp.sceneType == 'laser'){
  18. let data = this.computeShift({sourceApp,targetApp, convertInfo}) //因为有点云模式自由移动所以需要计算
  19. convertInfo.convertMatrix = data.convertMatrix
  20. convertInfo.convertMatrixInvert = data.convertMatrixInvert
  21. }
  22. this.syncView(sourceApp, targetApp, convertInfo)
  23. if(sourceApp.sceneType == 'kankan' || sourceFakeApp.viewInfo.isAtPano){
  24. this.flyToPano(targetApp, sourceFakeApp.viewInfo.currentPano,{duration:0})
  25. }
  26. if(sourceApp.sceneType == 'laser'){
  27. targetApp.viewer.mainViewport.view.applyToCamera(targetApp.viewer.mainViewport.camera)//使获得的cameraInfo正确
  28. }else if(sourceApp.sceneType == 'kankan'){
  29. targetApp.app.core.get('Player').cameraControls.activeControl.locked = false //怎么刚加载时lock了
  30. targetApp.app.core.get('Player').update()//cameraControls.activeControl.update() //使获得的cameraInfo正确
  31. }
  32. }
  33. bindWithSameType(sourceApp,targetApp, isSwitchScene){
  34. this.sourceApp = sourceApp
  35. this.targetApp = targetApp
  36. this.createTempApp(sourceApp)
  37. this.createTempApp(targetApp)
  38. this.convertInfo = this.computeAveDiffLon(sourceApp.fakeApp, targetApp.fakeApp)
  39. sourceApp.sceneName = 'sourceApp'
  40. targetApp.sceneName = 'targetApp'
  41. if(sourceApp.sceneType == 'laser'){
  42. {
  43. let data = this.computeShift({sourceApp,targetApp, convertInfo:this.convertInfo}) //因为有点云模式自由移动所以需要计算
  44. this.convertInfo.convertMatrix = data.convertMatrix
  45. this.convertInfo.convertMatrixInvert = data.convertMatrixInvert
  46. }
  47. //只监听左边
  48. let displayMode = (e)=>{
  49. targetApp.Potree.settings.displayMode = e.mode
  50. }
  51. sourceApp.viewer.images360.addEventListener('endChangeMode', displayMode)
  52. let dispose = ()=>{
  53. if(!sourceApp.viewer || !sourceApp.viewer.images360)return
  54. sourceApp.viewer.images360.removeEventListener('endChangeMode', displayMode)
  55. this.removeEventListener('clearBind-sameType',dispose)
  56. }
  57. this.addEventListener('clearBind-sameType',dispose)
  58. }
  59. let bind = (master, customer)=>{ //相互都能带动对方
  60. if(sourceApp.sceneType == 'laser'){
  61. var flyToPano = (e)=>{//同步点位
  62. if(master != this.masterApp )return
  63. let pano = customer.viewer.images360.getPano(e.toPano.pano.id)
  64. if(!pano)return console.error('找不到该e.panoId', e.toPano.pano.id)
  65. customer.viewer.images360.flyToPano({pano} )
  66. }
  67. master.viewer.images360.addEventListener('flyToPano',flyToPano)
  68. var cameraMove = (e)=>{
  69. if(master != this.masterApp || !customer.viewer )return
  70. this.addViewInfo(master)
  71. master.fakeApp.viewInfo.quaternionChanged = e.changeInfo && e.changeInfo.quaternionChanged
  72. this.syncView(master, customer)
  73. }
  74. master.viewer.addEventListener('camera_changed',cameraMove)
  75. var dragEnd = (e)=>{
  76. if(customer.viewer.inputHandler.drag){
  77. customer.viewer.inputHandler.onMouseUp(e) //从一侧拖拽到另一侧松开时,需要执行原先一侧的mouseup
  78. }
  79. }
  80. master.addEventListener('mouseup',dragEnd)
  81. }else if(sourceApp.sceneType == 'kankan'){
  82. var player1 = master.app.core.get('Player')
  83. var player2 = customer.app.core.get('Player')
  84. let this_ = this
  85. var flyToPano = (e)=>{//同步点位
  86. if(master != this_.masterApp )return
  87. let pano = player2.model.panos.index[e.panoId]
  88. if(!pano)return console.error('找不到该e.panoId',e.panoId)
  89. player2.flyToPano({pano} )
  90. }
  91. player1.on("flying.started",flyToPano)
  92. var cameraMove = (e)=>{//暂时只有漫游模式
  93. if(!e.hasChanged.cameraChanged || !customer.app || !customer.app.core)return
  94. this.addViewInfo(master)
  95. this.syncView(master, customer)
  96. }
  97. player1.on("update",cameraMove)
  98. }
  99. let changeMaster = ()=>{
  100. this.masterApp = master //主控方。只有主控方能控制被控方。鼠标操作过mousedown mousewheel等才能认定为主控方
  101. }
  102. let dom = sourceApp.sceneType == 'laser' ? master.viewer.inputHandler.domElement : master.app.core.get('Player').domElement
  103. dom.addEventListener('pointerdown',changeMaster )
  104. dom.addEventListener('mousewheel',changeMaster )
  105. let dispose = ()=>{
  106. if(master.sceneType == 'laser'){
  107. if(!master.viewer )return //master已替换,不用处理
  108. master.viewer.images360.removeEventListener('flyToPano',flyToPano)
  109. master.viewer.removeEventListener('camera_changed',cameraMove)
  110. }else if(master.sceneType == 'kankan'){
  111. player1.off("flying.started",flyToPano)
  112. player1.off("update",cameraMove)
  113. }
  114. master.removeEventListener('mousedown',changeMaster)
  115. master.removeEventListener('mousewheel',changeMaster)
  116. master.removeEventListener('mouseup',dragEnd)
  117. this.removeEventListener('clearBind-sameType',dispose)
  118. }
  119. this.addEventListener('clearBind-sameType',dispose)
  120. }
  121. bind(sourceApp, targetApp)
  122. bind(targetApp, sourceApp)
  123. //切换其中一个场景后同步初始漫游点
  124. if(isSwitchScene){
  125. setTimeout(()=>{
  126. let master = isSwitchScene == 'target' ? sourceApp : targetApp
  127. let customer = isSwitchScene == 'target' ? targetApp : sourceApp
  128. this.masterApp = master
  129. this.addViewInfo(master)
  130. this.syncView(master, customer)
  131. if(master.sceneType == 'laser'){
  132. let pano = master.viewer.images360.nextPano || master.viewer.images360.currentPano
  133. pano && this.flyToPano(customer, pano.id, { duration: 0, callback:()=>{
  134. customer.Potree.settings.displayMode = master.Potree.settings.displayMode
  135. }})
  136. //master.viewer.dispatchEvent({type:'camera_changed',changeInfo:{quaternionChanged:true},viewport:master.viewer.mainViewport }) //朝向位置同步
  137. }else{
  138. //master.app.core.get('Player').emit("update",{ hasChanged:{cameraChanged:true} })//朝向同步
  139. let pano = master.app.core.get('Player').nextPano || master.app.core.get('Player').currentPano
  140. pano && this.flyToPano(customer, pano.id, { duration: 0 })
  141. }
  142. },1)//要延迟,否则角度和pano都不成功
  143. }
  144. this.loaded = true
  145. }
  146. bindFakeWithBim(sourceFakeApp, targetApp, panoData ){// bim和其他类型互转(mobile)
  147. if(!panoData)return
  148. let sourceApp = {sceneType: sourceFakeApp.sceneType, fakeApp:sourceFakeApp}
  149. this.createTempApp(targetApp)
  150. let {sourcePano, targetPano} = this.bimGetPanoData(sourceApp, targetApp, panoData)
  151. let convertAxis = sourceApp.sceneType == 'kankan' ? 'YupToZup' : targetApp.sceneType == 'kankan' ? 'ZupToYup' : null
  152. let convertInfo = this.computeShift({sourcePano, targetPano, convertAxis})
  153. console.log('convertInfo', convertInfo, sourcePano, targetPano)
  154. let selectBestPose = ()=>{
  155. let data = this.getTranPosData(sourceFakeApp.viewInfo, convertInfo )
  156. let panos = targetApp.fakeApp.panos;
  157. let panos2 = panos.sort((a,b)=>{
  158. return data.position.distanceToSquared(a.position) - data.position.distanceToSquared(b.position)
  159. })
  160. let dir = new THREE.Vector3().subVectors( data.target, data.position )
  161. console.log('dir', dir)
  162. let prop = { duration:0,}
  163. if(targetApp.sceneType == 'laser'){
  164. targetApp.viewer.mainViewport.view.direction = dir
  165. }else{
  166. let player = targetApp.app.core.get('Player')
  167. console.log('nearest:', panos2[0].id)
  168. prop.aimDuration = 0
  169. prop.lookAtPoint = new THREE.Vector3().addVectors(panos2[0].position, dir)
  170. }
  171. this.flyToPano(targetApp, panos2[0].id, prop)
  172. }
  173. if(targetApp.sceneType == 'bim'){
  174. bimViewer = targetApp.viewer
  175. bimViewer.getViewer().setTransitionAnimationState(false)
  176. targetApp.CLOUD.GlobalData.WalkRotationSpeed = -0.2 //反向一下
  177. }
  178. if(targetApp.sceneType == 'bim' ){
  179. this.syncPosRot(sourceFakeApp.viewInfo, targetApp, convertInfo)
  180. }else if(targetApp.sceneType == 'laser' ){
  181. selectBestPose() //刚好在点位上的话这句设置完就正确了
  182. setTimeout(()=>{ //刚开始总是showPointCloud (且稍后会自动飞到某点)所以需要延时
  183. this.laserCancelFly(targetApp)
  184. if(this.ifCanChangePos(targetApp)){//点云模式的话
  185. this.syncPosRot(sourceFakeApp.viewInfo, targetApp, convertInfo)
  186. }else{
  187. }
  188. },this.settings.checkModeDelay+10)
  189. }else{//bim -> 固定点位
  190. selectBestPose()
  191. }
  192. }
  193. laserCancelFly(app){
  194. app.viewer.images360.cancelFlyToPano()
  195. app.viewer.mainViewport.view.cancelFlying()
  196. }
  197. bindWithBim(sourceApp, targetApp, panoData ) {
  198. //if (!this.player1.model.panos.list.length || !this.player2.model.panos.list.length) return
  199. if(this.loaded || !targetApp ) return
  200. let needBindEvent = !this.targetApp // 若targetApp存在表明targetApp的dom未换掉,事件还存在
  201. this.createTempApp(sourceApp)
  202. this.createTempApp(targetApp)
  203. let {sourcePano, targetPano} = this.bimGetPanoData(sourceApp, targetApp, panoData)
  204. this.sourceApp = sourceApp
  205. this.targetApp = targetApp
  206. let modelSize = new THREE.Vector3
  207. bimViewer = this.bimViewer = targetApp.viewer
  208. let modelBound = bimViewer.getViewer().modelManager.boundingBox
  209. modelBound.getSize(modelSize)
  210. bimViewer.setNavigationMode(targetApp.Glodon.Bimface.Viewer.NavigationMode3D.Walk)
  211. bimViewer.setFlySpeedRate(THREE.MathUtils.clamp( modelSize.length() / 10, 1, 6)) //会被限制
  212. //bimViewer.getViewer().setWalkSpeedRate(2)
  213. this.sourceDom = sourceApp.sceneType == 'laser' ? this.sourceApp.viewer.inputHandler.domElement : this.sourceApp.app.core.get('Player').domElement
  214. if(targetPano){
  215. bimViewer.getViewer().setTransitionAnimationState(false) //setCameraStatus瞬间变化相机 ,or setCameraAnimation?
  216. var convertAxis = sourceApp.sceneType == 'kankan' && targetApp.sceneType == 'bim' && 'YupToZup'// Y朝上需要转换
  217. this.lastCamStatus = bimViewer.getCameraStatus()
  218. this.convertInfo = this.computeShift({sourcePano, targetPano, convertAxis})
  219. bimViewer.addEventListener('Rendered', (e)=>{//反向改变左侧相机
  220. let info = bimViewer.getCameraStatus()
  221. let poseChanged = !math.closeTo(this.lastCamStatus.position, info.position)
  222. || !math.closeTo(this.lastCamStatus.target, info.target)
  223. || !math.closeTo(this.lastCamStatus.fov, info.fov)
  224. if(poseChanged){
  225. if(this.ifCanChangePos(this.sourceApp)){
  226. this.send(info)
  227. this.lastCamStatus = info
  228. }
  229. }
  230. })
  231. if(needBindEvent){
  232. this.bindCamEvent()
  233. }else{//替换的左侧的,需要使左侧和右侧同步, 其实是左侧要和上一个左侧先同步,再让右侧和左侧同步
  234. this.bindWithSameFakeType(this.lastFakeApp, sourceApp)
  235. }
  236. {
  237. let cameraMove
  238. if(sourceApp.sceneType == 'laser'){
  239. cameraMove = e => {
  240. targetApp && this.syncPosRot(this.getCameraData(sourceApp))
  241. }
  242. sourceApp.viewer.addEventListener('camera_changed', cameraMove)
  243. }else if(sourceApp.sceneType == 'kankan'){
  244. var player = this.sourceApp.app.core.get('Player')
  245. //this.sourceDom = player.domElement
  246. cameraMove = (e)=>{//暂时只有漫游模式
  247. if(!e.hasChanged.cameraChanged2)return
  248. //console.log('cameraMove', this.getCameraData(sourceApp))
  249. this.syncPosRot(this.getCameraData(sourceApp))
  250. }
  251. player.on("update",cameraMove)
  252. }
  253. let dispose = ()=>{
  254. if(sourceApp.sceneType == 'laser'){
  255. //if(!sourceApp.viewer || !sourceApp.viewer.images360)return
  256. sourceApp.viewer.removeEventListener('camera_changed', cameraMove)
  257. }else{
  258. //if(!sourceApp.app || !sourceApp.app.core)return
  259. player.off("update",cameraMove)
  260. }
  261. this.removeEventListener('clearBind-sameType',dispose)
  262. }
  263. this.addEventListener('clearBind-sameType',dispose)
  264. }
  265. /* bimViewer.addEventListener(targetApp.Glodon.Bimface.Viewer.Viewer3DEvent.ViewAdded,
  266. ()=>{
  267. this.loaded = true
  268. if(this.firstData){
  269. this.syncPosRot(this.firstData)
  270. }
  271. }
  272. ) */
  273. let data = this.getCameraData(sourceApp)
  274. this.syncPosRot(data)
  275. this.loaded = true
  276. }else{
  277. //分屏 不同步
  278. let data = this.getCameraData(sourceApp)
  279. let camera = bimViewer.getViewer().camera
  280. if(camera.fov != data.fov){
  281. camera.fov = data.fov
  282. camera.updateProjectionMatrix()
  283. }
  284. //将第一人称control补充完:
  285. //scroll
  286. let baseSpeed = THREE.MathUtils.clamp( Math.sqrt(modelSize.length()) / 5, 0.3, 8) //在modelBound中时的速度
  287. //console.log('baseSpeed',baseSpeed)
  288. let dom = bimViewer.getDomElement();
  289. dom.addEventListener('mousewheel', e => { //原版滚轮不能缩放,自己加一个
  290. if(e.wheelDelta == 0)return //mac
  291. let info = bimViewer.getCameraStatus()
  292. let dis = modelBound.distanceToPoint(info.position)
  293. let speed = baseSpeed + dis / 6
  294. //console.log('speed', speed)
  295. this.bimFlyTo({forwardDis: e.wheelDelta > 0 ? speed : -speed, duration:this.settings.durations.dolly , minRadius : baseSpeed})
  296. })
  297. //右键pan
  298. let dragging , pointerDelta = new THREE.Vector2, pointerStart = new THREE.Vector2
  299. dom.addEventListener('mousedown', e => {
  300. if(e.button == 2){//右键
  301. dragging = true
  302. pointerStart.set(e.clientX, e.clientY)
  303. }
  304. })
  305. dom.addEventListener('mousemove', e => {
  306. if(!dragging)return
  307. let pointerEnd = new THREE.Vector2(e.clientX, e.clientY)
  308. pointerDelta.subVectors(pointerEnd, pointerStart)
  309. pointerStart.copy(pointerEnd)
  310. bimViewer.getViewer().cameraControl.pan(pointerDelta.x,pointerDelta.y)
  311. })
  312. let mouseupAt = (target,e)=>{//触发target的mouseup
  313. let event = new MouseEvent('mouseup', {
  314. button : e.button, buttons:e.buttons
  315. })
  316. target.dispatchEvent(event)
  317. }
  318. targetApp.addEventListener('mouseup', e => {
  319. dragging = false
  320. //触发当前sourceDom的mouseup
  321. mouseupAt(this.sourceDom,e)
  322. })
  323. this.sourceDom.addEventListener('mouseup', e => {
  324. dragging = false
  325. //触发当前targetApp的mouseup
  326. mouseupAt(targetApp,e)
  327. })
  328. this.addEventListener('mouseupOutOfWin', e => {
  329. dragging = false
  330. //触发当前targetApp的mouseup
  331. mouseupAt(targetApp,e)
  332. })
  333. targetApp.CLOUD.GlobalData.WalkRotationSpeed = -0.2 //反向一下
  334. //bimViewer.viewer.getViewer().editorManager.userInputEditor.enable = true//这句近似将control切换成orbit
  335. }
  336. }
  337. syncPosRot(data, customer, convertInfo ){//同步 自由位置和朝向(不被漫游点束缚时)
  338. /*
  339. if(!this.loaded){
  340. return this.firstData = data
  341. } */
  342. convertInfo = convertInfo || this.convertInfo
  343. let {position,target} = this.getTranPosData(data, convertInfo )
  344. if(customer && customer.sceneType == 'laser'){
  345. this.laserSyncView(customer, {position,target})
  346. }else{
  347. let msg = {
  348. position,
  349. target,
  350. up: new THREE.Vector3(0,0,1),
  351. //前三个缺一不可
  352. fov: data.fov , //fov 用setCameraStatus 无效
  353. }
  354. bimViewer.setCameraStatus(msg)
  355. this.lastCamStatus = msg //记录下来,防止反向传输
  356. let camera = bimViewer.getViewer().camera
  357. if(camera.fov != data.fov){
  358. camera.fov = data.fov
  359. camera.updateProjectionMatrix()
  360. }
  361. }
  362. }
  363. ifCanChangePos(app){
  364. return app.sceneType == 'laser' && app.Potree.settings.displayMode != 'showPanos' //app.fakeApp.viewInfo.displayMode != 'showPanos'
  365. }
  366. send(info){
  367. //let camera = bimViewer.getViewer().camera
  368. let data = this.getTranPosData(info, this.convertInfo, true )
  369. this.laserSyncView(this.sourceApp, data) //左侧只有laser点云模式才能接收到
  370. }
  371. computeAveDiffLon(sourceFakeApp, targetFakeApp) {
  372. //获取两个场景的lon偏差值
  373. //需要点的个数>1, 且两个场景点一一对应,位置接近且顺序一致
  374. let diffLonAve = 0, length,
  375. diffLons = []
  376. let panoPos1 = sourceFakeApp.panos.map(e=>{
  377. return e.position
  378. })
  379. let panoPos2 = targetFakeApp.panos.map(e=>{
  380. return e.position
  381. })
  382. length = panoPos1.length
  383. //挑选连续的两个点为向量来计算,如有123个漫游点,则选取12 23 31作为向量
  384. let index = 0
  385. while (index < length) {
  386. let pos11 = new THREE.Vector3().copy(panoPos1[index])
  387. let pos12 = new THREE.Vector3().copy(panoPos1[(index + 1) % length])
  388. let pos21 = new THREE.Vector3().copy(panoPos2[index])
  389. let pos22 = new THREE.Vector3().copy(panoPos2[(index + 1) % length])
  390. let vec1 = new THREE.Vector3().subVectors(pos11, pos12).setY(0)
  391. let vec2 = new THREE.Vector3().subVectors(pos21, pos22).setY(0)
  392. let diffLon = math.getAngle(vec1, vec2, 'z')
  393. diffLons.push(diffLon)
  394. diffLonAve += diffLon
  395. index++
  396. }
  397. console.log('diffLons', diffLons)
  398. diffLonAve /= length
  399. console.log('diffLonAve', diffLonAve)
  400. let diffQua = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), diffLonAve)
  401. return {
  402. diffLon : diffLonAve,
  403. diffQua ,
  404. diffQuaInvert : diffQua.clone().invert()
  405. }
  406. }
  407. computeShift(o={}/* sourcePano, targetPano */) { //获取两个场景的旋转和位移偏差值
  408. //需要点的个数>1, 且两个场景点一一对应,位置接近且顺序一致
  409. let panoPos1, panoPos2
  410. if(o.sourceApp && o.targetApp && o.sourceApp.sceneType == o.targetApp.sceneType){
  411. var angle = o.convertInfo.diffLon; //直接使用 更精准
  412. panoPos1 = o.sourceApp.fakeApp.panos.map(e=>{
  413. return e.position
  414. })
  415. panoPos2 = o.targetApp.fakeApp.panos.map(e=>{
  416. return e.position
  417. })
  418. }else{
  419. panoPos1 = o.sourcePano.map(e=>e.position) //pick两个点来计算
  420. panoPos2 = o.targetPano.map(e=>e.position)
  421. if(o.convertAxis){
  422. panoPos1 = panoPos1.map(e=>math.convertVector[o.convertAxis](e))
  423. }
  424. var vec1 = new THREE.Vector3().subVectors(panoPos1[0], panoPos1[1]) //旧的向量
  425. var vec2 = new THREE.Vector3().subVectors(panoPos2[0], panoPos2[1])//新的向量
  426. var angle = math.getAngle(vec1, vec2, 'z')
  427. }
  428. //var scale = vec2.length()/vec1.length()
  429. //var scaleMatrix = new THREE.Matrix4().makeScale(scale,scale,scale) //默认为1, 但由于坐标暂时是自己采集的,所以结果会是第一个点附近比较正确,越远偏差越大
  430. var matrix = new THREE.Matrix4().setPosition(panoPos1[0].clone().negate())//先以点0为基准平移到000
  431. //matrix.premultiply(scaleMatrix)//再缩放
  432. var rotateMatrix = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0,0,1), angle );
  433. matrix.premultiply(rotateMatrix)//和旋转
  434. var moveBackMatrix = new THREE.Matrix4().setPosition(panoPos2[0])
  435. matrix.premultiply(moveBackMatrix)//再移动到realPosition的点0处
  436. return { convertMatrix: matrix, convertMatrixInvert:matrix.clone().invert(), convertAxis:o.convertAxis}
  437. }
  438. /*
  439. laser暂时做成这样: 全景模式时不跟踪pos,跟踪pano变化。点云模式时也跟踪pano变化,但移动时完全跟踪位置变化 ,所以会有左边marker在脚下,右边marker不在脚下的情况。
  440. */
  441. getCameraData(app){
  442. if(app.sceneType == 'laser'){
  443. let camera = app.viewer.mainViewport.camera
  444. return {
  445. position: camera.position.clone(),
  446. quaternion: camera.quaternion.clone(),
  447. fov: camera.fov,
  448. }
  449. }else if(app.sceneType == 'kankan'){
  450. let player = app.app.core.get('Player')
  451. return {
  452. position: player.position.clone(),
  453. quaternion: player.quaternion.clone(),
  454. fov: player.zoomFov,
  455. }
  456. }else{
  457. let bimViewer = app.viewer
  458. let info = bimViewer.getCameraStatus();
  459. return {
  460. position: info.position,
  461. target: info.target,
  462. fov: info.fov,
  463. }
  464. }
  465. }
  466. createTempApp(app, addsubInfo){//for mobile
  467. let fakeApp = {
  468. isFake : true, //标志是虚拟的app。每个真实的app都要带一个这个。在移动端如果大的销毁了还有小的
  469. sceneType : app.sceneType
  470. }
  471. if(app.sceneType != 'bim'){
  472. function getPanos(panos){ // only data
  473. return panos.map(e=>{return {id:e.id, position:e.position, quaternion:e.quaternion}})
  474. }
  475. fakeApp.panos = app.sceneType == 'laser' ? getPanos(app.viewer.images360.panos) : getPanos(app.app.core.get('Player').model.panos.list)
  476. }
  477. app.fakeApp = fakeApp
  478. if(addsubInfo){
  479. this.addViewInfo(app)
  480. }
  481. return fakeApp
  482. }
  483. addViewInfo(app){
  484. let viewInfo
  485. let cameraData = this.getCameraData(app)
  486. if(app.sceneType == 'laser'){
  487. let images360 = app.viewer.images360
  488. viewInfo = {
  489. displayMode : app.Potree.settings.displayMode,
  490. currentPano : images360.currentPano && images360.currentPano.id,
  491. isAtPano : images360.isAtPano(),
  492. quaternionChanged : true,
  493. }
  494. }else if(app.sceneType == 'kankan'){
  495. let player = app.app.core.get('Player')
  496. viewInfo = {
  497. currentPano : player.currentPano.id,
  498. lon : player.cameraControls.activeControl.lon,
  499. lat : player.cameraControls.activeControl.lat,
  500. zoomLevel : player.zoomLevel,
  501. }
  502. }else{
  503. viewInfo = {}
  504. }
  505. for(let i in cameraData){
  506. viewInfo[i] = cameraData[i]
  507. }
  508. app.fakeApp.viewInfo = viewInfo
  509. }
  510. /* getPano(app){
  511. return app.sceneType == 'laser' ? app.viewer.images360.getPano(id) : app.app.core.get('Player').panos.index[id]
  512. } */
  513. syncView(master, customer, convertInfo ){//同类型 相当于moveCamera的函数
  514. let fakeApp = master.fakeApp;
  515. convertInfo = convertInfo || this.convertInfo
  516. if(fakeApp.sceneType == 'laser'){
  517. customer.Potree.settings.displayMode = fakeApp.viewInfo.displayMode
  518. if(fakeApp.viewInfo.isAtPano || fakeApp.viewInfo.displayMode == 'showPanos'){ //不改变漫游点,仅转换朝向
  519. if( fakeApp.viewInfo.quaternionChanged){
  520. let diffQua = customer == this.targetApp ? convertInfo.diffQua : convertInfo.diffQuaInvert
  521. let quaternion = fakeApp.viewInfo.quaternion.clone().premultiply(diffQua)
  522. let rotation = new THREE.Euler().setFromQuaternion(quaternion)
  523. customer.viewer.mainViewport.view.rotation = rotation
  524. //console.log('cameraMove',customer == this.targetApp)
  525. }
  526. if(fakeApp.viewInfo.displayMode == 'showPanos' ){
  527. if(customer.viewer.mainViewport.camera.fov != fakeApp.viewInfo.fov){
  528. customer.viewer.mainViewport.camera.fov = fakeApp.viewInfo.fov
  529. customer.viewer.mainViewport.camera.updateProjectionMatrix()
  530. }
  531. }
  532. }else{//转换朝向和位置
  533. this.syncPosRot(fakeApp.viewInfo, customer , convertInfo)
  534. }
  535. }else if(fakeApp.sceneType == 'kankan'){
  536. let player = customer.app.core.get('Player')
  537. let diffLon = THREE.Math.radToDeg(customer == this.sourceApp ? -convertInfo.diffLon : convertInfo.diffLon)
  538. player.cameraControls.controls.panorama.lon = fakeApp.viewInfo.lon + diffLon
  539. player.cameraControls.controls.panorama.lat = fakeApp.viewInfo.lat
  540. if(player.zoomLevel != fakeApp.viewInfo.zoomLevel){
  541. player.zoomTo(fakeApp.viewInfo.zoomLevel)
  542. }
  543. }
  544. }
  545. bimGetPanoData(sourceApp, targetApp, panoData){
  546. if(panoData){
  547. let sourcePano,targetPano
  548. let pano1 = [{position:new THREE.Vector3().copy(panoData.p1.position)},{position:new THREE.Vector3().copy(panoData.p2.position)}]
  549. let getPano2 = (app)=>{
  550. return [app.fakeApp.panos.find(e=>e.id == panoData.p1.id), app.fakeApp.panos.find(e=>e.id == panoData.p2.id)]
  551. }
  552. if(targetApp.sceneType == 'bim'){
  553. targetPano = pano1
  554. sourcePano = getPano2(sourceApp)
  555. }else{
  556. targetPano = getPano2(targetApp)
  557. sourcePano = pano1
  558. }
  559. if( !sourcePano[0] || !sourcePano[1] || !targetPano[0] || !targetPano[1] ){
  560. console.error('!sourcePano[0] || !sourcePano[1] || !targetPano[0] || !targetPano[1]')
  561. }
  562. return {sourcePano, targetPano}
  563. }else return {}
  564. }
  565. getTranPosData(data, convertInfo, ifRevert ){
  566. let position = new THREE.Vector3, target = new THREE.Vector3
  567. if(data.position){
  568. position = new THREE.Vector3().copy(data.position)
  569. }
  570. if(!data.target){
  571. if(data.quaternion){
  572. let dir = new THREE.Vector3(0, 0, -1).applyQuaternion(data.quaternion)
  573. target.copy(position).add(dir)
  574. }
  575. }else{
  576. target.copy(data.target)
  577. }
  578. if(ifRevert){
  579. position.applyMatrix4(convertInfo.convertMatrixInvert)
  580. target.applyMatrix4(convertInfo.convertMatrixInvert)
  581. if(convertInfo.convertAxis){
  582. position = math.convertVector[convertInfo.convertAxis](position)
  583. target = math.convertVector[convertInfo.convertAxis](target)
  584. }
  585. }else{
  586. if(convertInfo.convertAxis){
  587. position = math.convertVector[convertInfo.convertAxis](position)
  588. target = math.convertVector[convertInfo.convertAxis](target)
  589. }
  590. position.applyMatrix4(convertInfo.convertMatrix)
  591. target.applyMatrix4(convertInfo.convertMatrix)
  592. }
  593. return {position, target}
  594. }
  595. bimFlyTo(data){
  596. let info = bimViewer.getCameraStatus()
  597. let vec = new THREE.Vector3().subVectors(info.target, info.position)
  598. let radius = vec.length() //修改了target到position的距离会影响pan时的速度
  599. let dir = vec.clone().normalize()
  600. let position = data.position
  601. if(!position){
  602. position = new THREE.Vector3().addVectors(info.position, dir.clone().multiplyScalar(data.forwardDis))//forwardDis:前进距离
  603. radius = Math.max(radius-data.forwardDis, data.minRadius || 0.7)
  604. }
  605. if(data.duration != void 0){
  606. bimViewer.getViewer().animator.setDuration(data.duration)//滚轮缩放时长,原先:1000
  607. }
  608. //console.log('radius',radius)
  609. let target = new THREE.Vector3().addVectors(position, dir.clone().multiplyScalar(radius))
  610. let msg = {//不能修改
  611. position,
  612. target,
  613. up: new THREE.Vector3(0,0,1),
  614. //前三个缺一不可
  615. }
  616. bimViewer.setCameraStatus(msg)
  617. }
  618. flyToPano(app, panoId, o={}){
  619. if(app.sceneType == 'laser'){
  620. this.laserCancelFly(app)//app.viewer.images360.cancelFlyToPano()
  621. app.viewer.images360.flyToPano(Object.assign({},{
  622. pano: app.viewer.images360.getPano(panoId)
  623. },o))
  624. }else{
  625. let player = app.app.core.get('Player')
  626. player.flyToPano(Object.assign({},{
  627. pano: player.model.panos.index[panoId]
  628. },o))
  629. }
  630. }
  631. laserSyncView(app,data){
  632. app.viewer.mainViewport.view.position.copy(data.position)
  633. app.viewer.mainViewport.view.lookAt(data.target)
  634. }
  635. lockCamera(locked){//禁止操作改变相机
  636. this.locked = locked
  637. this.updateCtrlEnable()
  638. }
  639. setPanoMode(state){
  640. this.isPanoMode = state
  641. this.updateCtrlEnable()
  642. }
  643. updateCtrlEnable(){
  644. this.bimViewer.camera3D.enableRotate(this.locked ? false : true)
  645. this.bimViewer.enableShortcutKey((this.locked || this.isPanoMode) ? false : true) //键盘移动
  646. }
  647. bindCamEvent(){//传递到另一边的dom
  648. this.lockCamera(true)
  649. /* this.targetApp.addEventListener(this.targetApp.Glodon.Bimface.Viewer.Viewer3DEvent.MouseClicked,(e)=>{
  650. console.log('MouseClicked',e)
  651. }); */
  652. let dom1 = this.bimViewer.getDomElement()
  653. let getEvent = (type, e)=>{
  654. let clientWidth1 = this.sourceDom.clientWidth
  655. let clientHeight1 = this.sourceDom.clientHeight
  656. let clientWidth2 = dom1.clientWidth
  657. let clientHeight2 = dom1.clientHeight
  658. return new MouseEvent(type, {
  659. bubbles: false,//?
  660. cancelable: true,
  661. view: this.sourceApp,
  662. /* clientX: e.clientX,
  663. clientY: e.clientY, */
  664. clientX: clientWidth1 * e.clientX / clientWidth2 , //鼠标在右屏的比例的左屏的相同,针对右屏全屏等左右不对称的情况
  665. clientY: clientHeight1 * e.clientY / clientHeight2,
  666. button: e.button, buttons: e.buttons, which: e.which,
  667. altKey: e.altKey, ctrlKey: e.ctrlKey, shiftKey:e.shiftKey, metaKey: e.metaKey,
  668. detail:e.detail,
  669. //target : dom2
  670. });
  671. }
  672. //let pointerDownPos = new THREE.Vector2
  673. dom1.addEventListener('mousedown',(e)=>{
  674. let event = getEvent('mousedown', e)
  675. this.sourceApp && this.sourceDom.dispatchEvent(event)
  676. //pointerDownPos.set(e.clientX,e.clientY)
  677. })
  678. dom1.addEventListener('mousemove',(e)=>{
  679. let event = getEvent('mousemove', e)
  680. this.sourceApp && this.sourceDom.dispatchEvent(event)
  681. })
  682. dom1.addEventListener('mouseup',(e)=>{
  683. if(!this.sourceApp)return
  684. let event = getEvent('mouseup', e)
  685. event.unableClick = true //最好禁止右侧点击行走。否则和点击效果冲突
  686. if(this.sourceApp.sceneType == 'laser'){
  687. this.sourceApp.dispatchEvent(event) //mouseup 在laser中加在window上的
  688. }else{
  689. let player = this.sourceApp.app.core.get('Player')
  690. player.mouseCouldBeClickToMove = false //dont click
  691. this.sourceDom.dispatchEvent(event)
  692. }
  693. })
  694. dom1.addEventListener('mousewheel',(e)=>{
  695. let event = getEvent('mousewheel', e)
  696. event.wheelDelta = e.wheelDelta //wheelDelta没法在getEvent参数中赋值
  697. this.sourceApp && this.sourceDom.dispatchEvent(event)
  698. })
  699. let stop = (e)=>{ //drag到另一边时停止旋转, 防止转到另一边
  700. let event = getEvent('mouseup', e)
  701. this.sourceApp && this.sourceApp.dispatchEvent(event)
  702. }
  703. dom1.addEventListener('mouseout',stop)
  704. dom1.addEventListener('mouseover',stop)
  705. }
  706. clear(o={}){
  707. this.loaded = false;
  708. if(o.dontClearTarget){
  709. if(this.sourceApp){
  710. this.lastFakeApp = this.sourceApp.fakeApp //记住当前左屏
  711. this.addViewInfo(this.sourceApp)
  712. }
  713. }else{
  714. this.targetApp = null
  715. this.lastFakeApp = null
  716. }
  717. this.sourceApp = null;
  718. this.dispatchEvent({type:'clearBind-sameType'})
  719. window.Log('clear done')
  720. }
  721. }
  722. /*
  723. note:
  724. 旋转只能通过target设置, 不能直接改camera.quaternion
  725. 当且仅当发送方相机属性变化后才传递过来,就不在这里判断是否变化了。
  726. (所以只需要实时检测相机是否改变, hasChanged后发送)
  727. */
  728. /*
  729. 其他代码:
  730. initTagAdd(){
  731. let markerConfig = new Bimface.Plugins.Marker3D.Marker3DContainerConfig();
  732. markerConfig.viewer = this.viewer;
  733. let tags = new Bimface.Plugins.Marker3D.Marker3DContainer(markerConfig);
  734. console.log('tags',tags)
  735. this.addEventListener('addTag',(e)=>{
  736. if(this.targetPano[e.index].tag){
  737. tags.removeItemById(this.targetPano[e.index].tag.id)
  738. }
  739. let position = new THREE.Vector3
  740. if(e.position){
  741. position.copy(e.position)
  742. }else{
  743. let currStatus = this.viewer.getCameraStatus()
  744. position.copy(currStatus.position)
  745. }
  746. let marker3dConfig = new Bimface.Plugins.Marker3D.Marker3DConfig();
  747. marker3dConfig.src = 'images/hotpoint'+ e.index +'.png'//"http://static.bimface.com/resources/3DMarker/warner/warner_red.png";
  748. marker3dConfig.worldPosition = new THREE.Vector3().copy(position)
  749. marker3dConfig.worldPosition.z -= belowHeight
  750. marker3dConfig.tooltip = '此为漫游点'+e.index //三维标签的提示
  751. let tag = new Bimface.Plugins.Marker3D.Marker3D(marker3dConfig);
  752. tags.addItem(tag);
  753. this.viewer.clearSelectedComponents();
  754. this.viewer.render();
  755. this.targetPano[e.index].tag = tag
  756. this.updatePanoMatch(position, e.index )
  757. })
  758. }
  759. this.viewer.addEventListener( Bimface.Viewer.Viewer3DEvent.MouseClicked, (objectData)=>{
  760. let position = objectData.worldPosition.clone().add({x:0,y:0,z:height});
  761. })
  762. addMesh(cameraData){
  763. var mesh = new Bimface.Plugins.Geometry.Plane({
  764. type:'rectangle', points:[{x:-0.1,y:-0.1,z:0},{x:0.1,y:0.1,z:0}]
  765. });
  766. var extObjMng = new Bimface.Plugins.ExternalObject.ExternalObjectManager(viewer2);
  767. extObjMng.loadObject({ name: 'plane', object: mesh});//作为外部构件添加到场景中
  768. //mesh.children[0].position.copy(cameraData.position).setZ(0.5)
  769. mesh.children[0].up.set(0,0,1)
  770. mesh.children[0].rotation.set(0,0,Math.PI/2)
  771. this.plane = mesh
  772. window.extObjMng = extObjMng
  773. }
  774. */