ConvertViews.js 49 KB

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