index.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. import mitt from 'mitt'
  2. import axios from 'axios' //{ axios } from '@/api'
  3. export const enter = (dom, isLocal) => {
  4. Potree.settings.isOfficial = true //标记为正式、非测试版本
  5. //Potree.fileServer = axios
  6. Potree.settings.libsUrl = window.location.href.includes('http://localhost:5173/') ? '../lib/':'./lib/' //在深时本地版访问这个时是'./lib/',但本地调试是前者
  7. Potree.settings.notAdditiveBlending = true
  8. const tagLimitDis = 8;
  9. Potree.settings.showCompass = true
  10. Potree.settings.compassDom = dom.querySelector('#direction')
  11. Potree.settings.maintainBtmZ = true //有离地高度,需要维持不变
  12. let {THREE} = Potree.mergeEditStart(dom)
  13. let MergeEditor = viewer.modules.MergeEditor
  14. let sceneBus = mitt()
  15. MergeEditor.transformControls.pivotOnBottom = !!isLocal //中心点居于模型bound底部,因固定离地高度,旋转时旋转中心在地面上就不会变位置
  16. viewer.addEventListener('camera_changed', e => {
  17. var camera = e.viewport.camera
  18. var pos = camera.position
  19. if (e.viewport.name == 'MainView') {
  20. sceneBus.emit('cameraChange', { x: pos.x, y: pos.y, z: pos.z, rotate: camera.rotation })
  21. }
  22. })
  23. viewer.addEventListener('webglError', e => {
  24. console.error('viewer webglError: ' + e)
  25. sceneBus.emit('webglError', { msg: e.msg })
  26. })
  27. window.THREE = THREE
  28. //isLocal = false
  29. let autoLoads = []
  30. let readyToAddModel
  31. let maxLoadingCount = /* isLocal ? 1 : */2; //正在加载模型的最大数目
  32. let sdk = {
  33. sceneBus,
  34. getPositionByScreen(pos2d, hopeModelId ){//通过屏幕坐标获取真实坐标 . hopeModelId: 如果指定了模型,优先返回hopeModelId上的intersect
  35. console.log('getPositionByScreen',hopeModelId)
  36. hopeModelId = null
  37. let worldPos, localPos, modelId, intersect
  38. let Handler = viewer.inputHandler
  39. let reGet = ()=>{//不使用当前鼠标所在位置的intersect,单独算
  40. pos2d.clientX = pos2d.x
  41. pos2d.clientY = pos2d.y
  42. pos2d.onlyGetIntersect = true
  43. pos2d.whichPointcloud = true
  44. if(hopeModelId != void 0){//隐藏其他的模型
  45. let models = MergeEditor.getAllObjects()
  46. models.forEach(model=>{
  47. Potree.Utils.updateVisible(model, 'forPick', model.dataset_id == hopeModelId)
  48. })
  49. }
  50. let intersect2 = Handler.onMouseMove(pos2d)
  51. if(hopeModelId != void 0){//恢复
  52. let models = MergeEditor.getAllObjects()
  53. models.forEach(model=>{
  54. Potree.Utils.updateVisible(model, 'forPick', true)
  55. })
  56. }
  57. if(intersect2 && intersect2.location){
  58. intersect = intersect2
  59. }
  60. }
  61. if (pos2d && pos2d.inDrag) {
  62. reGet()
  63. } else {
  64. intersect = Handler.intersect
  65. if(intersect){
  66. modelId = intersect.pointcloud ? intersect.pointcloud.dataset_id : intersect.object.dataset_id
  67. if(hopeModelId != void 0 && modelId != hopeModelId){
  68. reGet()
  69. }
  70. }
  71. }
  72. if (intersect && intersect.location) {
  73. modelId = intersect.pointcloud ? intersect.pointcloud.dataset_id : intersect.object.dataset_id
  74. /* if(hopeModelId != void 0 && modelId != hopeModelId){
  75. return null
  76. } */
  77. worldPos = intersect.location.clone()
  78. localPos = Potree.Utils.datasetPosTransform({ toDataset: true, datasetId:modelId, position:worldPos })
  79. } else return null
  80. return { worldPos, modelId, localPos }
  81. },
  82. getScreenByPosition(pos3d, modelId, canShelter/* , disToCameraLimit */){//通过模型局部坐标获取屏幕坐标
  83. let isLocal = modelId != void 0
  84. pos3d = new THREE.Vector3().copy(pos3d)
  85. let worldPos = isLocal ? Potree.Utils.datasetPosTransform({ fromDataset: true, datasetId: modelId, position:pos3d}) : pos3d
  86. if(!worldPos)return
  87. if(canShelter){
  88. if(viewer.inputHandler.ifBlockedByIntersect(worldPos, 0.1, true)) return {trueSide:false};
  89. }
  90. var viewport = viewer.mainViewport
  91. var camera = viewport.camera
  92. var dom = viewer.renderArea
  93. if(tagLimitDis != void 0){
  94. if(camera.position.distanceToSquared(worldPos) > Math.pow(tagLimitDis,2))return false
  95. }
  96. //console.log('getScreenByPoint ' + pos3d.toArray())
  97. return Potree.Utils.getPos2d(worldPos, camera, dom, viewport)
  98. },
  99. screenshot: (width, height) => {
  100. //截图
  101. var {getImagePromise, finishPromise} = viewer.startScreenshot({ type: 'default',useRenderTarget:true}, width, height)
  102. var deferred = $.Deferred();
  103. finishPromise.done(({dataUrl}) => {
  104. deferred.resolve(dataUrl)
  105. })
  106. return deferred.promise()
  107. },
  108. getPose() {//获取当前点位和朝向
  109. const camera = viewer.scene.getActiveCamera()
  110. const target = viewer.scene.view.getPivot().clone()
  111. const position = viewer.scene.view.position.clone()
  112. //console.log('getPose',position, target)
  113. return { position, target }
  114. },
  115. comeTo(o = {}) {
  116. //console.log('comeTo',o.position, o.target)
  117. //飞到某个点
  118. if(o.modelId){
  119. ['position','target'].forEach(e=>{
  120. if(o[e]){
  121. o[e] = Potree.Utils.datasetPosTransform({ fromDataset: true, datasetId: o.modelId, position:o[e]})
  122. }
  123. })
  124. }
  125. if(o.distance){
  126. let position = o.target || o.position
  127. return viewer.focusOnObject({ position}, 'tag', null,{distance:o.distance} ).promise
  128. }
  129. let deferred = $.Deferred()
  130. viewer.scene.view.setView($.extend({},o, {
  131. duration: o.dur,
  132. callback:()=>{
  133. o.callback && o.callback()
  134. deferred.resolve(true)
  135. }
  136. }))
  137. return deferred.promise()
  138. },
  139. /* getPose(o={}) {
  140. //获取相对于第一个数据集的初始画面。(当数据集校准后,如果初始画面设置在被修改的数据集上,且该数据集非初始数据集的话,还是会偏移的)
  141. var deferred = o.deferred || $.Deferred();
  142. console.log('getPose')
  143. if(viewer.mainViewport.view.isFlying()){
  144. let f = ()=>{
  145. this.getPose(o)
  146. viewer.mainViewport.view.removeEventListener('flyingDone', f)
  147. }
  148. viewer.mainViewport.view.addEventListener('flyingDone', f) //once
  149. o.deferred = deferred
  150. return deferred.promise()
  151. }
  152. var camera = viewer.scene.getActiveCamera()
  153. var rotation = camera.rotation
  154. var pos_In_dataset = Potree.Utils.datasetPosTransform({ toDataset: true, position: camera.position.clone(), datasetId: Potree.settings.originDatasetId })
  155. var rot_In_dataset = Potree.Utils.datasetRotTransform({ toDataset: true, rotation, getRotation: true, datasetId: Potree.settings.originDatasetId }) //拿第一个数据集
  156. var view = viewer.scene.view.clone()
  157. view.rotation = rot_In_dataset //获取yaw pitch
  158. var pose = {
  159. //displayMode: Potree.settings.displayMode,
  160. position: pos_In_dataset,
  161. yaw: view.yaw,
  162. pitch: view.pitch,
  163. displayMode : Potree.settings.displayMode,
  164. panoSid: viewer.images360.currentPano.sid
  165. }
  166. //return pose
  167. setTimeout(()=>{
  168. deferred.resolve(pose)
  169. console.log('getPose resolve',pose)
  170. },1)
  171. return deferred.promise()
  172. },
  173. setPose(o = {}, duration=0) {
  174. //设置相机位置和朝向
  175. var deferred = o.deferred || $.Deferred();
  176. console.warn('setPose 初始画面', o)
  177. var quaternion
  178. let view = viewer.scene.view.clone()
  179. if(viewer.mainViewport.view.isFlying()){
  180. let f = ()=>{
  181. this.setPose(o, duration)
  182. viewer.mainViewport.view.removeEventListener('flyingDone', f)
  183. }
  184. viewer.mainViewport.view.addEventListener('flyingDone', f) //once
  185. o.deferred = deferred
  186. return deferred.promise()
  187. }
  188. var getQuaternion = ()=>{
  189. view.pitch = o.pitch
  190. view.yaw = o.yaw
  191. quaternion = Potree.Utils.datasetRotTransform({ fromDataset: true, rotation: view.rotation, getQuaternion: true, datasetId: Potree.settings.originDatasetId }) //拿第一个数据集
  192. }
  193. viewer.images360.cancelFlyToPano()//防止旧的在之后继续执行
  194. let pano
  195. if(o.panoSid != void 0){//好像都不存这个
  196. pano = viewer.images360.panos.find(e=>e.sid == o.panoSid)
  197. if(pano == void 0)return deferred.reject('没有找到该panoSid').promise()
  198. getQuaternion()
  199. viewer.images360.flyToPano({pano, duration, quaternion},()=>{
  200. deferred.resolve()
  201. })
  202. }else{
  203. if(Potree.settings.displayMode == 'showPanos'){
  204. return deferred.reject('全景模式下不允许设置位置').promise()
  205. }
  206. let position = Potree.Utils.datasetPosTransform({ fromDataset: true, position: o.position, datasetId: Potree.settings.originDatasetId })
  207. //view.position.copy(position)
  208. getQuaternion()
  209. pano = viewer.images360.panos.find(e=>Potree.math.closeTo(e.position, position))
  210. if(pano){//如果原来在某pano上最好也使currentPano为此pano,否则isAtPano会返回false
  211. viewer.images360.flyToPano({pano, duration, quaternion},()=>{
  212. deferred.resolve()
  213. })
  214. }else{
  215. viewer.scene.view.setView({position,quaternion,duration, callback:()=>{
  216. //setTimeout(()=>{
  217. deferred.resolve()
  218. console.log('setPose resolve')
  219. //},1)
  220. } })
  221. viewer.mapViewer.moveTo(position, null, duration) //初始位置在地图居中
  222. }
  223. }
  224. return deferred.promise()
  225. },
  226. */
  227. enterSceneGuide(pathArr){//导览 (不需要修改参数)
  228. let editor = viewer.modules.CamAniEditor
  229. console.log('pathArr',pathArr)
  230. /* type SceneGuidec = {
  231. position: {x,y,z}
  232. target: {x,y,z}
  233. time: number
  234. speed: number //没用到
  235. }
  236. */
  237. //console.log('enterSceneGuide',pathArr)
  238. let data = {
  239. duration: pathArr.slice(0,pathArr.length-1).reduce(function(total, currentValue ){return total+currentValue.time}, 0), //总时长(要去掉最后一个,因为已到终点,该点time无意义)
  240. points: pathArr,
  241. useDurSlice:true
  242. }
  243. let animation = editor.createAnimation(data)
  244. //注:最多只存在一条导览
  245. let bus = mitt()
  246. //播放完成
  247. animation.addEventListener('playDone', () => {
  248. bus.emit('playComplete')
  249. })
  250. //切换点
  251. animation.addEventListener('updateCurrentIndex', e => {
  252. bus.emit('changePoint', e.currentIndex + 1)
  253. })
  254. return {
  255. bus,
  256. play() {
  257. MergeEditor.selectModel(null)
  258. animation.play()
  259. },
  260. pause() {
  261. animation.pause()
  262. },
  263. clear() {
  264. //删除
  265. editor.removeAnimation(animation)
  266. },
  267. }
  268. },
  269. //[path1, paht2], { time, speed }
  270. calcPathInfo(paths, info){ //传入的time, speed仅有一个。返回完整的 time, speed
  271. //这一版的control似乎无法在某个位置上改变角度,位置和角度一般都是一起变的,所以先不增加单位更换功能。
  272. let pos1 = new THREE.Vector3().copy(paths[0].position)
  273. let pos2 = new THREE.Vector3().copy(paths[1].position)
  274. let dis = pos1.distanceTo(pos2)
  275. if(info.time != void 0){
  276. info.speed = dis / info.time
  277. }else{
  278. info.time = dis / info.speed
  279. }
  280. return info
  281. },
  282. //scaleRange: { min, max }, opacityRange: { min, max }, bottomRange: { min, max } })
  283. addModel(props){
  284. //props.url = Potree.resourcePath+'/models/1.glb'
  285. let bus = mitt()
  286. //console.log('addModel',props)
  287. props.isFirstLoad = props.bottom == void 0 //在编辑时用户添加的
  288. if(props.opacity == void 0) props.opacity = 1
  289. if(props.type == 'obj') props.type = 'glb'
  290. props.scale /= 100
  291. if(props.rotation){
  292. if(props.rotation._x == void 0 && props.rotation.x != void 0){
  293. props.rotation = new THREE.Euler().setFromVector3(props.rotation)
  294. }
  295. }
  296. if(!props.isFirstLoad){
  297. if(autoLoads.length == 0){ //首次加载
  298. setTimeout(()=>{
  299. let sizes = autoLoads.map(e=>e.size)
  300. console.log('需要请求加载的模型大小为', sizes, '总大小', sizes.reduce(function(total, currentValue ){
  301. let current = parseFloat(currentValue)
  302. return total + ((typeof currentValue == 'number' || currentValue.includes('M')) ? current : current / 1024)
  303. }, 0))
  304. readyToAddModel = true //准备开始加载
  305. startLoad(autoLoads[0])
  306. },30)
  307. }
  308. autoLoads.push(props)
  309. readyToAddModel = false
  310. }else{
  311. readyToAddModel = true
  312. }
  313. let startLoad = (prop)=>{
  314. //if(autoLoads.filter(e=>e.loaded).length>1)return console.log('取消加载', prop), prop.onError()
  315. //return prop.onError()
  316. Potree.addModel(prop, prop.done , prop.progressFun, prop.onError)
  317. prop.loading = true
  318. console.log('-------开始加载 id:', prop.id, 'title:', prop.title, ', filename:', getName(prop.url), prop )
  319. }
  320. let spliceFromArr = (model,loaded)=>{
  321. //let autoLoads.find()
  322. props.loadFinish = true
  323. props.loading = false
  324. if(loaded){
  325. props.loaded = true
  326. props.model = model
  327. }else{
  328. props.error = true
  329. }
  330. let haventLoad = autoLoads.filter(e=>!e.loading && !e.loadFinish);
  331. if( haventLoad[0]){
  332. startLoad(haventLoad[0])
  333. //this.addModel(autoLoads[0])
  334. }else if(autoLoads.filter(e=>!e.loadFinish).length == 0 && autoLoads.filter(e=>e.loaded).length>0 && !props.isFirstLoad){//设置相机位置:当自动开始加载第一个模型时(其余的也跟着自动加载),等这批加载完后;
  335. let autoLoadsDone = autoLoads.filter(e=>e.loaded).map(e=>e.model)
  336. console.
  337. log('所有模型加载完毕')
  338. autoLoads.filter(e=>e.loaded && e.show).forEach(e=>e.model.visible = true)
  339. MergeEditor.focusOn(autoLoadsDone, 1000, true, true)
  340. autoLoads.length = 0
  341. }
  342. }
  343. let model
  344. let done = (model_)=>{
  345. model = model_
  346. if(!props.isFirstLoad){
  347. model.visible = false//先不显示,防止卡顿
  348. }
  349. props.opacity < 100 && result.changeOpacity(props.opacity)
  350. model.addEventListener('changeSelect',(e)=>{
  351. bus.emit('changeSelect',e.selected)
  352. })
  353. let lastState={ }
  354. model.addEventListener('transformChanged',(e)=>{
  355. //console.log('transformChanged',model.position,model.scale.x, model.rotation,model.btmHeight)
  356. let msg = {}
  357. if(!lastState.position || !model.position.equals(lastState.position)){
  358. lastState.position = msg.position = model.position.clone()
  359. }
  360. if(!lastState.rotation || !model.rotation.equals(lastState.rotation)){
  361. lastState.rotation = msg.rotation = model.rotation.clone()
  362. }
  363. if(lastState.scale == void 0 || model.scale.x * 100 != lastState.scale){
  364. lastState.scale = msg.scale = model.scale.x * 100
  365. }
  366. if(lastState.bottom == void 0 || model.btmHeight != lastState.bottom){
  367. lastState.bottom = msg.bottom = model.btmHeight
  368. }
  369. msg = Potree.Common.CloneObject(msg)
  370. console.log(msg)
  371. bus.emit('transformChanged', msg )
  372. })
  373. spliceFromArr(model,true)
  374. bus.emit('loadDone')
  375. //console.log('loadDone' )
  376. }
  377. let progressFun = (progress)=>{
  378. bus.emit('loadProgress',progress)
  379. }
  380. let onError = function ( xhr ) {
  381. bus.emit('loadError', xhr)
  382. console.log('loadError!!!!!!!!!', getName(props.url), props.size, xhr)
  383. spliceFromArr(model,false)
  384. }
  385. props.done = done; props.progressFun = progressFun; props.onError = onError
  386. if(readyToAddModel){
  387. if(autoLoads.filter(e=>e.loading).length<maxLoadingCount ){
  388. startLoad(props)
  389. }
  390. }
  391. let result = {
  392. bus,
  393. changeShow(show){
  394. props.show = show //for autoLoads show model
  395. if(model){
  396. Potree.Utils.updateVisible(model, 'changeShow', show)
  397. viewer.dispatchEvent('content_changed')
  398. }
  399. },
  400. changeSelect(state){
  401. if(model){
  402. MergeEditor.selectModel(model, state, true, true)
  403. if(state && viewer.inputHandler.selection[0]){
  404. MergeEditor.transformControls.attach(model) //viewer.transformObject(model); //交换
  405. }
  406. //console.log('changeSelect', props.id, state)
  407. }
  408. },
  409. changeScale(s){
  410. if(model){
  411. s /= 100
  412. model.scale.set(s,s,s)
  413. model.isPointcloud && model.changePointSize(Potree.config.material.realPointSize * s)
  414. model.dispatchEvent("scale_changed")
  415. }
  416. },
  417. changeOpacity(opacity){ //见笔记:透明物体的材质设置
  418. if(opacity == void 0)opacity = 100
  419. opacity/=100
  420. if(model){
  421. MergeEditor.changeOpacity(model,opacity)
  422. }
  423. },
  424. changeBottom(z){
  425. //console.log('changeBottom',z)
  426. model && MergeEditor.setModelBtmHeight(model,z)
  427. model.dispatchEvent('transformChanged') //改了position */
  428. },
  429. changePosition(pos){//校准取消时执行
  430. //console.log('changePosition',pos.x, pos.y, pos.z)
  431. model && model.position.copy(pos)
  432. model.dispatchEvent({type:'position_changed'})
  433. },
  434. changeRotation(rot){//校准取消时执行
  435. //console.log('changeRotation',rot.x, rot.y, rot.z)
  436. model && model.rotation.setFromVector3(rot)
  437. model.dispatchEvent({type:'rotation_changed'})
  438. },
  439. enterRotateMode(){
  440. if(model){
  441. MergeEditor.transformControls.attach(model)
  442. MergeEditor.transformControls.mode = 'rotate'
  443. /* viewer.transformObject(model);
  444. viewer.transformationTool.setModeEnable('rotation',true)
  445. viewer.transformationTool.setModeEnable('translation',false) */
  446. }
  447. },
  448. enterMoveMode(){
  449. if(model){
  450. MergeEditor.transformControls.attach(model)
  451. MergeEditor.transformControls.mode = 'translate'
  452. /* viewer.transformObject(model);
  453. viewer.transformationTool.setModeEnable('rotation',false)
  454. viewer.transformationTool.setModeEnable('translation',true) */
  455. }
  456. },
  457. leaveTransform(){
  458. //viewer.transformObject(null);
  459. MergeEditor.transformControls.detach()
  460. },
  461. destroy(){
  462. model && MergeEditor.removeModel(model)
  463. viewer.dispatchEvent('content_changed')
  464. }
  465. }
  466. return result
  467. },
  468. }
  469. function getName(url){
  470. return url.split('/').pop()
  471. }
  472. console.log('版本: 2023.3.24-0')
  473. return sdk
  474. }
  475. export default enter