index.js 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773
  1. import mitt from 'mitt'
  2. import axios from 'axios' //{ axios } from '@/api'
  3. let requestLoadCount = 0
  4. let maxLoadingCount = 2; //正在加载模型的最大数目
  5. //0看看,1看见,2深时,3用户上传三维模型,4深时mesh,5深光点云,6深光mesh
  6. const ModelTypes = {
  7. 0 : {name:'看看(八目)', panos4dkk:true},
  8. 1 : {name:'看见(双目转台)', panos4dkk:true, rot90:true},
  9. 2 : {name:'深时', },
  10. 3 : {name:'用户上传三维模型'},
  11. 4 : {name:'深时mesh(激光转台)',panos4dkk:true, rot90:true},//3dtiles
  12. 5 : {name:'深光点云' },
  13. 6 : {name:'深光mesh',panos4dkk:true, rot90:true},//3dtiles
  14. }
  15. let cesAspect
  16. export const enter = ({ dom, mapDom, isLocal, lonlat, scenes }) => {
  17. console.warn('新的页面')
  18. Potree.settings.isOfficial = true //标记为正式、非测试版本
  19. //Potree.fileServer = axios
  20. Potree.settings.libsUrl = './lib/'
  21. let loadStartTime = Date.now()
  22. //正式环境(本地调试会打不开)
  23. if (location.host === 'mix3d.4dkankan.com') {
  24. Potree.settings.urls.prefix = Potree.settings.urls.prefix6
  25. Potree.settings.webSite = 'datav1'
  26. } else if (location.host === 'xfhd.4dkankan.com') {
  27. Potree.settings.urls.prefix = Potree.settings.urls.prefix7
  28. Potree.settings.webSite = 'datav1'
  29. }
  30. const mapBus = mitt(), sceneBus = mitt()
  31. Potree.settings.showCompass = true
  32. Potree.settings.compassDom = dom.querySelector('#direction')
  33. Potree.settings.mergeType2 = true //标识新版
  34. Potree.settings.modelSkybox = true //是否将全景图贴在模型上(会导致卡顿)。若不显示模型将不显示Reticule
  35. Potree.settings.tiles3DMaxMemory = 300 //稍微增加点
  36. Potree.settings.mergeTransCtlOnClick = true
  37. Potree.settings.canWalkThroughModel = true
  38. let { THREE } = Potree.mergeEditStart(dom, mapDom)
  39. let MergeEditor = viewer.modules.MergeEditor
  40. Potree.settings.unableNavigate = true
  41. /* Potree.settings.showCesium = !!lonlat
  42. Potree.settings.showCesium && buildMap()
  43. */
  44. //因为getPose里用的是target,俯视的yaw不准,所以限制一下不要完全俯视
  45. viewer.mainViewport.view.maxPitch-=0.001
  46. viewer.mainViewport.view.minPitch+=0.001
  47. viewer.addEventListener('camera_changed', e => {
  48. var camera = e.viewport.camera
  49. var pos = camera.position
  50. if (e.viewport.name == 'MainView') {
  51. sceneBus.emit('cameraChange', { x: pos.x, y: pos.y, z: pos.z, rotate: camera.rotation })
  52. updateMap()
  53. Potree.Common.intervalTool.isWaiting('updateCamNear', ()=>{
  54. updateCamNear()
  55. }, 1000)
  56. //viewer.scene.tags.children.forEach(tag=>tag.functions.updateVisiFar())
  57. }
  58. })
  59. //-------------------------------------
  60. let modelAinB = (A,B)=>{ //B的expand(5m) bound完全包含A
  61. let boundB = B.boundingBox.clone().expandByVector(new THREE.Vector3(5,5,5)).applyMatrix4(B.matrixWorld)
  62. let boundA = A.boundingBox.clone().applyMatrix4(A.matrixWorld)
  63. return boundB.containsBox(boundA)
  64. }
  65. let changeMeshVisi = (object, show) => {
  66. if(show == void 0) show = Potree.settings.displayMode == 'showPointCloud' || object == viewer.images360.currentPano.pointcloud && Potree.settings.modelSkybox || object.showInPano //showInPano: 装饰物,一直显示
  67. || !object.panos && modelAinB(object, viewer.images360.currentPano.pointcloud) //装饰物
  68. Potree.Utils.updateVisible(object, 'showPanos', show)
  69. }
  70. if(Potree.settings.canWalkThroughModel){
  71. let lastModel
  72. viewer.images360.addEventListener('flyToPano',(e)=>{//开始漫游 漫游到另一个模型就要选中这个模型?
  73. let model = e.toPano.pano.pointcloud
  74. if(lastModel != model){
  75. changeMeshVisi(model, true)
  76. //MergeEditor.selectModel(model)
  77. //model.result_.flyInPano(e.toPano.pano, {dontFly:true}) //切换模型显示,因为flyInPano有事件怕乱所以统一用这个函数
  78. updateCamNear()
  79. }
  80. })
  81. viewer.images360.addEventListener('flyToPanoDone',(e)=>{
  82. if(!e.makeIt)return
  83. let model = viewer.images360.currentPano.pointcloud
  84. if(lastModel != model){
  85. lastModel?.isModel && changeMeshVisi(lastModel, false)
  86. sceneBus.emit('panoModelChange', model.result_ )
  87. }
  88. lastModel = model
  89. })
  90. }
  91. viewer.images360.addEventListener('endChangeMode',(e)=>{
  92. sceneBus.emit('modeChange', {mode: e.mode == 'showPanos' ? 'pano' : 'fuse', model : e.mode == 'showPanos' && viewer.images360.currentPano.pointcloud.result_} )
  93. Potree.Utils.updateVisible(MergeEditor.transformControls, 'showPanos', e.mode == 'showPointCloud')
  94. Potree.Utils.updateVisible(MergeEditor.boxHelper, 'showPanos', e.mode == 'showPointCloud')
  95. if(e.mode == 'showPanos'){
  96. viewer.setControls( viewer.fpControls )
  97. viewer.removeEventListener('camera_changed', camera_changed)
  98. }else{
  99. viewer.addEventListener('camera_changed', camera_changed)
  100. }
  101. viewer.objs.children.forEach((e)=>{changeMeshVisi(e)})
  102. Potree.settings.canWalkThroughModel || viewer.images360.panos.forEach(pano => {
  103. pano.setEnable(e.mode == 'showPanos' ? pano.pointcloud == viewer.images360.currentPano.model : true)
  104. })
  105. Potree.settings.unableNavigate = e.mode == 'showPointCloud'
  106. updateCamNear()
  107. })
  108. let camera_changed = (e) => {
  109. if (e.viewport.name == 'MainView' && e.changeInfo.positionChanged) {
  110. //viewer.mainViewport.camera.position
  111. viewer.mainViewport.view.radius = 0.1 //使pivot在面前一丢丢距离
  112. viewer.setControls(viewer.orbitControls)
  113. viewer.removeEventListener('camera_changed', camera_changed)
  114. }
  115. }
  116. let requestInPano = false
  117. //-------------------------------------
  118. /* viewer.inputHandler.addEventListener('keydown', (e)=>{
  119. if(e.event.key == "e" ){
  120. MergeEditor.transformControls.mode = 'rotate'
  121. }else if(e.event.key == "w"){
  122. MergeEditor.transformControls.mode = 'translate'
  123. }else if(e.event.key == "s"){
  124. MergeEditor.transformControls.mode = 'scale'
  125. }
  126. }) */
  127. viewer.addEventListener('webglError', e => {
  128. console.error('viewer webglError: ' + e)
  129. let memory = '. \n jsHeapSizeLimit:'+ performance.memory.jsHeapSizeLimit/ 1e6 + ', usedJSHeapSize: '+performance.memory.usedJSHeapSize/ 1e6 + '(M)'
  130. sceneBus.emit('webglError', { msg: e.msg + memory })
  131. })
  132. viewer.compass.setAutoDisplay(true)
  133. /* mapBus.on('visible', v => {
  134. //console.log('mapBus visible', v)
  135. viewer.mapViewer.visible = v
  136. if (v) {
  137. viewer.mapViewer.mapLayer.needUpdate = true
  138. }
  139. viewer.mapViewer.dispatchEvent({type:'forceVisible',visible:v})
  140. }) */
  141. {
  142. let index = 1;
  143. //let setDisplay()
  144. if (!Potree.isIframeChild) {
  145. /* viewer.addEventListener('createIframe',(e)=>{//创建了子页面
  146. }) */
  147. window.winIndex = 0;
  148. window.iframeCreated = function (iframe) {
  149. let child = iframe.contentWindow
  150. child.winIndex = index++
  151. //案件里视图提取页面子页面覆盖了父级页面,父级的模型可以隐藏以释放内存
  152. console.error('createdIframe', child.winIndex, child.location.href)
  153. viewer.setDisplay(false)
  154. child.beforeDestroy = function () { //注:在前端仍会找不到beforeDestroy,可能contentWindow变更??所以手动调用setDisplay
  155. console.warn('beforeDestroy', child.winIndex)
  156. child.viewer && child.viewer.setDisplay(false)
  157. //如果是四维看看的场景,先不管了,页面被销毁应该就没了吧
  158. viewer.setDisplay(true)//恢复主页的模型显示
  159. if (!child.viewer) {
  160. try {
  161. let player = child.__sdk.core.get('Player')
  162. /* let runtime = player.model._3dTilesRuntime
  163. let tileset = runtime.getTileset()
  164. tileset._cache.trim(); //使下一次update时dispose所有不可见的tiles
  165. let sceneRenderer = child.__sdk.core.get('SceneRenderer')
  166. player.model.visible = false
  167. runtime.update(16, sceneRenderer.renderer, sceneRenderer.camera, true) //没用,为何_trimTiles的while无法进入
  168. */
  169. player.model.traverse(e => {
  170. e.geometry && e.geometry.dispose()
  171. if (e.material) {
  172. e.material.map && e.material.map.dispose()
  173. if (e.material.uniforms && e.material.uniforms.map && e.material.uniforms.map.value) {
  174. e.material.uniforms.map.value.dispose()
  175. }
  176. }
  177. }) //效果甚微
  178. /* let sceneRenderer = child.__sdk.core.get('SceneRenderer')
  179. sceneRenderer.renderer.render(sceneRenderer.scene, sceneRenderer.camera)
  180. */
  181. } catch (e) {
  182. console.log(e)
  183. }
  184. }
  185. }
  186. }
  187. //不知道删除iframe时是否那些模型还在内存里,需要释放吗? 如果要需要加一个事件
  188. } else {
  189. }
  190. }
  191. window.THREE = THREE
  192. //isLocal = false
  193. let autoLoads = /* window.autoLoads = */ []
  194. let readyToAddModel
  195. let mainBackground = viewer.background
  196. const units = { 1: 'metric', 2: 'imperial' }
  197. let getMeasureType = function (type, unit = 1) {
  198. let info
  199. switch (type) {
  200. case 'free':
  201. info = { measureType: 'Distance' }
  202. break
  203. case 'area':
  204. info = { measureType: 'Area' }
  205. break
  206. case 'vertical':
  207. info = { measureType: 'Ver Distance' }
  208. break
  209. default:
  210. console.error('无此 measure type')
  211. }
  212. info.unit = units[unit]
  213. return info
  214. }
  215. let getMeasureFunction = function (measure, bus) {
  216. measure.addEventListener('highlight', (e) => {
  217. //console.log('3d->2d highlight',e.state)
  218. bus.emit('highlight', e.state)
  219. })
  220. measure.addEventListener('marker_dropped', (e) => {//拖拽结束后发送changeCallBack
  221. if (measure.parent) {
  222. //未被删除
  223. bus.emit('update', [
  224. measure.dataset_points.map(p => p.clone()),
  225. measure.points_datasets
  226. ])
  227. }
  228. })
  229. return {
  230. /* quit: () => {
  231. Potree.Log('quit结束且删除: ' + measure.id, '#00c7b2')
  232. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure })
  233. }, //触发结束。退出测量模式,清除之前操作 */
  234. destroy: () => {
  235. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure })
  236. viewer.scene.removeMeasurement(measure)
  237. },
  238. /* getPoints: () => {
  239. return measure.points
  240. },
  241. getDatasetLocations: () => {
  242. return measure.dataset_points
  243. },
  244. getDatasets: () => {
  245. return measure.points_datasets
  246. },
  247. getDatasetId: () => {
  248. return measure.datasetId
  249. }, */
  250. getArea: () => {
  251. return measure.area //{value:area, string:..}
  252. },
  253. getDistance: () => {
  254. if (measure.points.length < 2) return 0
  255. var value = measure.points[0].distanceTo(measure.points[1])
  256. return {
  257. value, //米
  258. string: measure.getConvertString(value, 'distance')
  259. }
  260. },
  261. //手动开启或关闭:
  262. show: () => {
  263. Potree.Utils.updateVisible(measure, 'inListByUser', true)
  264. },
  265. hide: () => {
  266. Potree.Utils.updateVisible(measure, 'inListByUser', false)
  267. },
  268. fly() {
  269. let result = viewer.focusOnObject(measure, 'measure', 1200)
  270. return result.msg ? result.msg : result.promise
  271. //返回值 1 deferred 表示即将位移 2 'posNoChange' 表示已在最佳位置 3 'tooFar' 表示距离最佳位置太远
  272. },
  273. changeSelect(isHight) {
  274. console.log('2d->3d isHight ', isHight)
  275. measure.setSelected(isHight, 'byList')
  276. },
  277. }
  278. }
  279. let sdk = {
  280. sceneBus, mapBus,
  281. canTurnToPanoMode(pos) {
  282. pos = pos ? new THREE.Vector3().copy(pos) : viewer.images360.position
  283. let pano = viewer.images360.findNearestPano(pos)
  284. if (pano && pano.position.distanceTo(pos) < Potree.config.panoFieldRadius * pano.pointcloud.scale.x) {
  285. return {model:pano.pointcloud.result_}
  286. }
  287. //poschange后会调用这个,如果返回false会变为点云模式,且不会自动变回原先的模式
  288. },
  289. getPositionByScreen(pos2d, hopeModelId) {//通过屏幕坐标获取真实坐标 . hopeModelId: 如果指定了模型,优先返回hopeModelId上的intersect
  290. //console.log('getPositionByScreen',hopeModelId)
  291. hopeModelId = null
  292. let worldPos, localPos, modelId, intersect, normal, localNormal
  293. let Handler = viewer.inputHandler
  294. let reGet = () => {//不使用当前鼠标所在位置的intersect,单独算
  295. pos2d.clientX = pos2d.x
  296. pos2d.clientY = pos2d.y
  297. pos2d.onlyGetIntersect = true
  298. pos2d.whichPointcloud = true
  299. if (hopeModelId != void 0) {//隐藏其他的模型
  300. let models = MergeEditor.getAllObjects()
  301. models.forEach(model => {
  302. Potree.Utils.updateVisible(model, 'forPick', model.dataset_id == hopeModelId)
  303. })
  304. }
  305. let intersect2 = Handler.onMouseMove(pos2d)
  306. if (hopeModelId != void 0) {//恢复
  307. let models = MergeEditor.getAllObjects()
  308. models.forEach(model => {
  309. Potree.Utils.updateVisible(model, 'forPick', true)
  310. })
  311. }
  312. if (intersect2 && intersect2.location) {
  313. intersect = intersect2
  314. }
  315. }
  316. if (pos2d && pos2d.inDrag) {
  317. reGet()
  318. } else {
  319. intersect = Handler.intersect
  320. if (intersect) {
  321. modelId = intersect.pointcloud ? intersect.pointcloud.dataset_id : intersect.object.dataset_id
  322. if (hopeModelId != void 0 && modelId != hopeModelId) {
  323. reGet()
  324. }
  325. }
  326. }
  327. if (intersect && intersect.location) {
  328. modelId = intersect.pointcloud ? intersect.pointcloud.dataset_id : intersect.object.dataset_id
  329. /* if(hopeModelId != void 0 && modelId != hopeModelId){
  330. return null
  331. } */
  332. worldPos = intersect.location.clone()
  333. localPos = Potree.Utils.datasetPosTransform({ toDataset: true, datasetId: modelId, position: worldPos })
  334. normal = intersect.normal
  335. localNormal = intersect.localNormal
  336. } else return null
  337. return { worldPos, modelId, normal, localPos, localNormal }
  338. },
  339. getScreenByPosition(pos3d, modelId, canShelter/* , disToCameraLimit */) {//通过模型局部坐标获取屏幕坐标
  340. //console.log('getScreenByPoint ')
  341. let isLocal = modelId != void 0
  342. pos3d = new THREE.Vector3().copy(pos3d)
  343. let worldPos = isLocal ? Potree.Utils.datasetPosTransform({ fromDataset: true, datasetId: modelId, position: pos3d }) : pos3d
  344. if (!worldPos) return
  345. if (canShelter) {
  346. if (viewer.inputHandler.ifBlockedByIntersect(worldPos, 0.1, true)) return { trueSide: false };
  347. }
  348. var viewport = viewer.mainViewport
  349. var camera = viewport.camera
  350. var dom = viewer.renderArea
  351. /* if (tagLimitDis != void 0) {
  352. if (camera.position.distanceToSquared(worldPos) > Math.pow(tagLimitDis, 2)) return false
  353. } */
  354. //console.log('getScreenByPoint ' + pos3d.toArray())
  355. return Potree.Utils.getPos2d(worldPos, viewport, dom)
  356. },
  357. setCameraFov(fov) {
  358. viewer.setFOV(fov)
  359. },
  360. screenshot: (width, height/* , bgOpacity=0 */ ) => {//
  361. //截图
  362. let bgOpacity = Potree.settings.showCesium ? 0 : 1 /* viewer.background == 'skybox' */ //因为要画map底图所以上层只能透明。之后需要的话再改
  363. console.log('bgOpacity', bgOpacity)
  364. Potree.Utils.updateVisible(MergeEditor.boxHelper, 'screenshot', false)
  365. var { getImagePromise, finishPromise } = viewer.startScreenshot({ type: 'default', /* useRenderTarget:true, */bgOpacity }, width, height)
  366. var deferred = $.Deferred();
  367. finishPromise.done(({ dataUrl }) => {
  368. if(Potree.settings.displayMode != 'showPanos' && Potree.settings.showCesium){//need map background
  369. Potree.cesScreenshot(width, height).done((mapBGurl)=>{
  370. let img = new Image(); img.src = dataUrl
  371. let imgBG = new Image(); imgBG.src = mapBGurl
  372. let loadCount = 0
  373. img.onload = imgBG.onload = ()=>{
  374. loadCount++;
  375. if(loadCount == 2){
  376. let url = Potree.Common.imgAddLabel(imgBG,img,{leftRatioToImg:0,topRatioToImg:0})
  377. deferred.resolve(url)
  378. }
  379. }
  380. })
  381. }else{
  382. deferred.resolve(dataUrl)
  383. }
  384. Potree.Utils.updateVisible(MergeEditor.boxHelper, 'screenshot', true)
  385. })
  386. return deferred.promise()
  387. },
  388. getPose() {//获取当前点位和朝向
  389. const camera = viewer.scene.getActiveCamera()
  390. const target = viewer.scene.view.getPivot().clone()
  391. const position = viewer.scene.view.position.clone()
  392. const pose = { position, target, displayMode:Potree.settings.displayMode }
  393. if(Potree.settings.displayMode == 'showPanos'){
  394. let model = viewer.images360.currentPano.pointcloud
  395. pose.panoId = viewer.images360.currentPano.originID
  396. pose.model = model.result_
  397. pose.posInModel = Potree.Utils.datasetPosTransform({ toDataset: true, position: camera.position.clone(), object:model })
  398. pose.rotInModel = Potree.Utils.datasetRotTransform({ toDataset: true, quaternion: camera.quaternion.clone(), getQuaternion: true, pointcloud:model }).toArray() //拿第一个数据集
  399. }
  400. //console.log('getPose',position, target)
  401. return pose
  402. },
  403. comeTo(o = {}) {
  404. //console.log('comeTo',o.position, o.target)
  405. //飞到某个点
  406. let deferred = $.Deferred()
  407. if(o.panoId != void 0){
  408. let model = o.model.model
  409. let pano = model.panos.find(a=>a.originID == o.panoId)
  410. if(pano){
  411. o.rotInModel = new THREE.Quaternion().fromArray(o.rotInModel)
  412. let quaternion = Potree.Utils.datasetRotTransform({ fromDataset: true, quaternion: o.rotInModel, getQuaternion: true, object:model})
  413. o.model.flyInPano(pano, {quaternion, duration:0, callback(){
  414. o.callback && o.callback()
  415. deferred.resolve(true)
  416. }})
  417. return deferred.promise()
  418. }else{
  419. console.warn('没有找到漫游点',o)
  420. }
  421. }else if(requestInPano){
  422. requestInPano.result_.flyOutPano()
  423. }else{
  424. if (o.modelId != void 0) {
  425. ['position', 'target'].forEach(e => {
  426. if (o[e]) {
  427. o[e] = Potree.Utils.datasetPosTransform({ fromDataset: true, datasetId: o.modelId, position: o[e] })
  428. }
  429. })
  430. }
  431. }
  432. if (o.distance) {
  433. let position = o.target || o.position
  434. return viewer.focusOnObject({ position }, 'tag', null, { distance: o.distance }).promise
  435. }
  436. viewer.scene.view.setView($.extend({}, o, {
  437. duration: o.dur,
  438. callback: () => {
  439. o.callback && o.callback()
  440. deferred.resolve(true)
  441. }
  442. }))
  443. return deferred.promise()
  444. },
  445. setBackdrop(sky, type, { scale, rotate }) {//天空盒背景
  446. //console.log('天空盒背景', sky,type)
  447. let setGroundAndText = (color) => {
  448. MergeEditor.secondCompass.dom.find(".dirText").css({ 'color': color })
  449. viewer.compass.dom.find(".dirText").css({ 'color': color })
  450. MergeEditor.ground.material.uniforms.uColor.value.set(color)
  451. //MergeEditor.ground.children[0].material.color.set(color)
  452. }
  453. viewer.dispatchEvent('content_changed')
  454. if(type == 'map'){
  455. MergeEditor.setGroundPlaneImg(null)
  456. viewer.setBackground(mainBackground)
  457. Potree.settings.showCesium = true
  458. buildMap()
  459. viewer.backgroundOpacity = 0
  460. return
  461. }else{
  462. Potree.settings.showCesium = false
  463. }
  464. if (type == 'bimg') {//地面图
  465. MergeEditor.setGroundPlaneImg(sky, scale, rotate)
  466. setGroundAndText('#e0e0e0')
  467. viewer.setBackground(mainBackground)
  468. } else {
  469. MergeEditor.setGroundPlaneImg(null)
  470. if (sky == 'none') {
  471. viewer.setBackground(mainBackground)
  472. setGroundAndText('#eee')
  473. } else if (sky[0] == '#') {
  474. viewer.setBackground(new THREE.Color(sky))
  475. let color = sky == '#fff' ? '#666' : sky == '#333' ? '#eee' : '#bbb' //反相
  476. setGroundAndText(color)
  477. } else if (type == 'image-map' || type == 'vector-map') {//影像|矢量 地图
  478. } else {//环境
  479. viewer.setBackground('skybox', sky)
  480. setGroundAndText('#e0e0e0')
  481. }
  482. }
  483. },
  484. switchMapType(type) {
  485. let map = viewer.mapViewer.mapLayer.maps.find(e => e.name == 'map')
  486. map.switchStyle(type/* map.style == 'satellite' ? 'standard' : 'satellite' */)
  487. },
  488. enableMap(mapArea, latlng) {
  489. if (!viewer.mapViewer) {
  490. //--------------------------------
  491. viewer.mapViewer = new Potree.MapViewer(mapArea)
  492. viewer.mapViewer.initProjection()
  493. //focus
  494. let boundSize = new THREE.Vector3(200, 150, 1).max(viewer.bound.boundSize)
  495. viewer.mapViewer.addEventListener('viewerResize', () => {
  496. viewer.mapViewer.moveTo(viewer.bound.center, boundSize, 0)
  497. }, { once: true })
  498. }
  499. },
  500. enterSceneGuide(pathArr) {//导览 (不需要修改参数)
  501. let editor = viewer.modules.CamAniEditor
  502. console.log('pathArr', pathArr)
  503. //console.log('enterSceneGuide',pathArr)
  504. pathArr.forEach(e=>{
  505. if(e.panoId != void 0){
  506. e.model = e.model.model
  507. }
  508. })
  509. let data = {
  510. //duration: pathArr.slice(0, pathArr.length - 1).reduce(function (total, currentValue) { return total + currentValue.time }, 0), //总时长(要去掉最后一个,因为已到终点,该点time无意义)
  511. points: pathArr,
  512. useDurSlice: true
  513. }
  514. let ani = editor.createMulAnimation(data)
  515. //注:最多只存在一条导览
  516. let bus = mitt()
  517. //播放完成
  518. ani.event_.addEventListener('playDone', () => {
  519. bus.emit('playComplete')
  520. viewer.images360.panos.forEach(e=>e.marker && Potree.Utils.updateVisible(e, 'playAni', true))
  521. })
  522. //切换点
  523. ani.event_.addEventListener('updateCurrentIndex', e => {
  524. bus.emit('changePoint', e.currentIndex + 1)
  525. })
  526. return {
  527. bus,
  528. play() {
  529. MergeEditor.selectModel(null)
  530. viewer.images360.panos.forEach(e=>e.marker && Potree.Utils.updateVisible(e, 'playAni', false))
  531. ani.play()
  532. },
  533. pause() {
  534. viewer.images360.panos.forEach(e=>e.marker && Potree.Utils.updateVisible(e, 'playAni', true))
  535. ani.stop()
  536. },
  537. clear() {
  538. ani.remove()
  539. },
  540. }
  541. },
  542. //[path1, paht2], { time, speed }
  543. calcPathInfo(paths, info) { //传入的time, speed仅有一个。返回完整的 time, speed
  544. //这一版的control似乎无法在某个位置上改变角度,位置和角度一般都是一起变的,所以先不增加单位更换功能。
  545. let pos1 = new THREE.Vector3().copy(paths[0].position)
  546. let pos2 = new THREE.Vector3().copy(paths[1].position)
  547. let dis = pos1.distanceTo(pos2)
  548. if (info.time != void 0) {
  549. info.speed = dis / info.time
  550. } else {
  551. info.time = dis / info.speed
  552. }
  553. return info
  554. },
  555. addModel(props) {
  556. let bus = props.bus = mitt()
  557. //console.log('addModel',props)
  558. props.isFirstLoad = isLocal ? props.bottom == void 0 : (props.isDynamicAdded || props.mode == 'single') // 在编辑时用户添加的 或 展示单个模型 (props.mode='single'模型展示页, props.mode='many'融合页)
  559. if (props.opacity == void 0) props.opacity = 1
  560. if (props.type == 'obj') props.type = 'glb'
  561. props.scale /= 100
  562. if (props.rotation) {
  563. if (props.rotation._x == void 0 && props.rotation.x != void 0) {
  564. props.rotation = new THREE.Euler().setFromVector3(props.rotation)
  565. }
  566. }
  567. let getDefaultRotation = () => {
  568. if(ModelTypes[props.fromType]?.rot90 && props.type != 'glb'){
  569. return new THREE.Euler(Math.PI / 2, 0, 0)
  570. } else return new THREE.Euler(0, 0, 0)
  571. }
  572. if (!props.isFirstLoad) {
  573. if (autoLoads.length == 0) { //首次加载
  574. setTimeout(() => {
  575. let sizes = autoLoads.map(e => e.size || 0)
  576. console.log('需要请求加载的模型大小为', sizes, '总大小', sizes.reduce(function (total, currentValue) {
  577. let current = parseFloat(currentValue)
  578. return total + ((typeof currentValue == 'number' || currentValue.includes('M')) ? current : current / 1024)
  579. }, 0))
  580. readyToAddModel = true //准备开始加载
  581. loadNext()//startLoad(autoLoads[0])
  582. }, 30)
  583. }
  584. autoLoads.push(props)
  585. readyToAddModel = false
  586. } else {
  587. readyToAddModel = true
  588. props.rotation = getDefaultRotation()
  589. }
  590. let model
  591. let done = (model_) => {
  592. model = model_
  593. model.result_ = result
  594. model.props = props
  595. result.model = model
  596. model.fromType = ModelTypes[props.fromType].name
  597. if (!props.isFirstLoad) {
  598. model.visible = false//先不显示,防止卡顿
  599. }
  600. model.showInPano = props.raw.showInPano
  601. props.opacity < 100 && result.changeOpacity(props.opacity)
  602. model.addEventListener('changeSelect', (e) => {
  603. bus.emit('changeSelect', e.selected)
  604. })
  605. let lastState = {}
  606. model.addEventListener('transformChanged', (e) => {
  607. let msg = {}
  608. if (!lastState.position || !model.position.equals(lastState.position)) {
  609. lastState.position = msg.position = model.position.clone()
  610. }
  611. if (!lastState.rotation || !model.rotation.equals(lastState.rotation)) {
  612. lastState.rotation = msg.rotation = model.rotation.clone()
  613. }
  614. if (lastState.scale == void 0 || model.scale.x * 100 != lastState.scale) {
  615. lastState.scale = msg.scale = model.scale.x * 100
  616. }
  617. msg = Potree.Common.CloneObject(msg)
  618. //console.log(msg)
  619. bus.emit('transformChanged', msg)
  620. })
  621. spliceFromArr(model, props, true)
  622. model.addEventListener('changeSelect', (e) => {
  623. MergeEditor.transformControls.visible && e.selected && MergeEditor.transformControls.attach(model, e.clickPos) //: MergeEditor.transformControls.detach()
  624. })
  625. MergeEditor.modelAdded(model)
  626. if (props.mode == 'single') {//模型查看页
  627. MergeEditor.noNeedSelection = true
  628. setTimeout(() => {
  629. MergeEditor.focusOn([model], 1000, true, true)
  630. }, 1)
  631. }
  632. if(ModelTypes[props.fromType].panos4dkk){
  633. Potree.load4dkkPanos(props.raw.num, model, getDefaultRotation(), () => {
  634. bus.emit('loadDone')
  635. }, props.fromType == 0 ? '2k' : '4k' ) //看看场景是2k
  636. } else {
  637. bus.emit('loadDone')
  638. }
  639. //console.log('loadDone' )
  640. }
  641. let progressFun = (progress) => {
  642. bus.emit('loadProgress', progress)
  643. }
  644. let onError = function (xhr) {
  645. bus.emit('loadError', xhr)
  646. console.log('loadError!!!!!!!!!', Potree.Common.getNameFromURL(props.url), props.size, xhr)
  647. spliceFromArr(model, props, false)
  648. }
  649. try {
  650. props.url = JSON.parse(props.url) //去掉 '\'
  651. } catch (e) { }
  652. props.done = done; props.progressFun = progressFun; props.onError = onError
  653. if (readyToAddModel) {
  654. if (autoLoads.filter(e => e.loading).length < maxLoadingCount) {
  655. startLoad(props)
  656. }
  657. }
  658. let scaleMeasure
  659. let result = {
  660. bus,
  661. model,
  662. getDefaultRotation,
  663. supportPano() { //是否支持全景图
  664. return model?.panos?.length > 0
  665. },
  666. flyInPano(pano, {dontFly, quaternion, duration}={}) {// 飞入全景图
  667. requestInPano = model
  668. pano = pano || viewer.images360.findNearestPano(null, model.panos)
  669. if (pano) {
  670. dontFly || viewer.images360.flyToPano({ pano, canCancelLast: true, quaternion, duration})
  671. Potree.settings.displayMode = 'showPanos'
  672. }
  673. },
  674. flyOutPano() {// 飞出全景图(就是切换到正常融合视角)
  675. requestInPano = false
  676. Potree.settings.displayMode = 'showPointCloud'
  677. /* setTimeout(() => {//在下一帧再变,因为3dtiles需要更新一下才会显示tiles
  678. if (!requestInPano) {
  679. Potree.settings.displayMode = 'showPointCloud'
  680. Potree.Utils.updateVisible(MergeEditor.boxHelper, 'showPanos', true)
  681. }
  682. }, 50) */
  683. },
  684. changeShow(show) {
  685. props.show = show //for autoLoads show model
  686. if (model) {
  687. Potree.Utils.updateVisible(model, 'datasetSelection', show)
  688. if (model.panos) {
  689. model.panos.forEach(e => e.setEnable(show))
  690. }
  691. viewer.dispatchEvent('content_changed')
  692. }
  693. },
  694. changeSelect(state) {
  695. console.error('select', state)
  696. if (model) {
  697. let fly = viewer.images360.latestRequestMode != 'showPanos'
  698. MergeEditor.selectModel(model, state, fly, true)
  699. updateCamNear()
  700. //console.log('changeSelect', props.id, state)
  701. }
  702. },
  703. changeScale(s) {
  704. if (model) {
  705. s /= 100
  706. if (model.scale.x == s) return
  707. //MergeEditor.history.beforeChange(model)//但不知道什么时候结束拖拽
  708. model.scale.set(s, s, s)
  709. model.isPointcloud && model.changePointSize(/* Potree.config.material.realPointSize * s */)
  710. model.dispatchEvent("scale_changed")
  711. }
  712. },
  713. changeOpacity(opacity) { //见笔记:透明物体的材质设置
  714. if (opacity == void 0) opacity = 100
  715. opacity /= 100
  716. MergeEditor.changeOpacity(model, opacity)
  717. },
  718. changeBottom(z) {
  719. /* model && MergeEditor.setModelBtmHeight(model,z)
  720. model.dispatchEvent('transformChanged') //改了position */
  721. },
  722. changePosition(pos) {//校准取消时执行
  723. console.log('changePosition', pos.x, pos.y, pos.z)
  724. model && model.position.copy(pos)
  725. model.dispatchEvent({ type: 'position_changed' })
  726. },
  727. changeRotation(rot) {//校准取消时执行
  728. console.log('changeRotation', rot.x, rot.y, rot.z)
  729. model && model.rotation.setFromVector3(rot)
  730. model.dispatchEvent({ type: 'rotation_changed' })
  731. },
  732. enterRotateMode() {
  733. if (model) {
  734. if (MergeEditor.split) {//分屏校准
  735. MergeEditor.setTransformState('rotate')
  736. MergeEditor.transformControls2.attach(model)
  737. MergeEditor.transformControls2.mode = 'rotate'
  738. }
  739. MergeEditor.transformControls.attach(model)
  740. MergeEditor.transformControls.mode = 'rotate'
  741. }
  742. },
  743. enterMoveMode() {
  744. console.log('enterMoveMode')
  745. if (model) {
  746. if (MergeEditor.split) {//分屏校准
  747. MergeEditor.setTransformState('translate')
  748. MergeEditor.transformControls2.attach(model)
  749. MergeEditor.transformControls2.mode = 'translate'
  750. }
  751. MergeEditor.transformControls.attach(model)
  752. MergeEditor.transformControls.mode = 'translate'
  753. }
  754. },
  755. leaveTransform() {
  756. console.log('leaveTransform')
  757. if (MergeEditor.split) {//分屏校准
  758. MergeEditor.setTransformState(null)
  759. } else {
  760. MergeEditor.transformControls.detach()
  761. MergeEditor.transformControls2.detach()
  762. }
  763. MergeEditor.history.clear()
  764. },
  765. enterAlignment() {//开始校准
  766. result.leaveTransform()
  767. MergeEditor.enterSplit()
  768. //console.log('enterAlignment',model.position, model.rotation)
  769. let bus = new mitt()
  770. /* MergeEditor.transformControls.attach(model)
  771. MergeEditor.transformControls.mode = 'translate' */
  772. return {
  773. bus
  774. }
  775. },
  776. leaveAlignment() {
  777. //console.log('leaveAlignment',model.position, model.rotation)
  778. MergeEditor.leaveSplit()
  779. MergeEditor.transformControls.detach()
  780. MergeEditor.transformControls2.detach()
  781. },
  782. enterScaleSet() {//设置比例
  783. let bus = new mitt()
  784. let length, measureBuilded;
  785. //viewer.outlinePass.selectedObjects = []
  786. if (!Potree.Utils.isInsideFrustum(model.boundingBox.clone().applyMatrix4(model.matrixWorld), viewer.scene.getActiveCamera())) {
  787. MergeEditor.focusOn(model, 600)
  788. }
  789. MergeEditor.getAllObjects().forEach(m => {//隐藏其他的模型
  790. if (m != model) Potree.Utils.updateVisible(m, 'enterScaleSet', false)
  791. })
  792. let setScale = () => {
  793. if (length == void 0 || !measureBuilded) return
  794. let vec = new THREE.Vector3().subVectors(viewer.mainViewport.camera.position, scaleMeasure.points[1])
  795. let s = length / (scaleMeasure.points[0].distanceTo(scaleMeasure.points[1]))
  796. result.changeScale(model.scale.x * s * 100)
  797. /* setTimeout(()=>{
  798. viewer.focusOnObject(scaleMeasure , 'measure', 500)
  799. },1) */
  800. let newCamPos = new THREE.Vector3().addVectors(scaleMeasure.points[1], vec.multiplyScalar(s))
  801. viewer.scene.view.setView({
  802. position: newCamPos, target: scaleMeasure.getCenter(), duration: 0, callback: () => {
  803. //更改target到measure中心的好处就是可以让相机绕measure中心转,坏处是每次更改都会变一下画面
  804. }
  805. })
  806. }
  807. return {
  808. bus,
  809. setLength(v) {
  810. if (!v) return
  811. length = v
  812. setScale()
  813. },
  814. startMeasure() {
  815. if (scaleMeasure) {
  816. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure: scaleMeasure })
  817. viewer.scene.removeMeasurement(scaleMeasure)
  818. }
  819. measureBuilded = false
  820. scaleMeasure = viewer.measuringTool.startInsertion(
  821. { measureType: "Distance", unit: "metric" },
  822. () => {
  823. //done:
  824. //bus.emit('end' ) //完成
  825. measureBuilded = true
  826. setScale()
  827. },
  828. () => {
  829. //cancel
  830. //bus.emit('quit') //删除
  831. }
  832. )
  833. scaleMeasure.addEventListener('marker_dropped', (e) => {//拖拽结束后发送changeCallBack
  834. if (scaleMeasure.parent) {
  835. //未被删除
  836. measureBuilded && setScale()
  837. }
  838. })
  839. }
  840. }
  841. },
  842. leaveScaleSet() {
  843. if (scaleMeasure) {
  844. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure: scaleMeasure })
  845. viewer.scene.removeMeasurement(scaleMeasure)
  846. scaleMeasure = null
  847. }
  848. //viewer.outlinePass.selectedObjects = [model];
  849. MergeEditor.getAllObjects().forEach(m => {//恢复其他的模型
  850. if (m != model) Potree.Utils.updateVisible(m, 'enterScaleSet', true)
  851. })
  852. },
  853. destroy() {
  854. model && MergeEditor.removeModel(model)
  855. result.changeSelect(false)
  856. viewer.dispatchEvent('content_changed')
  857. }
  858. }
  859. return result
  860. },
  861. //测量线的点都附着于各个模型,当模型变化时,点跟着变化。
  862. // 新的测量创建方法,传入type 返回新测量对象
  863. startMeasure(type) {
  864. // 寻创建的测量对象有上面绘画测量对象的所有方法
  865. const bus = mitt()
  866. let info = getMeasureType(type)
  867. let measure = viewer.measuringTool.startInsertion(
  868. info,
  869. () => {
  870. //done:
  871. /* bus.emit('submit', {
  872. dataset_points: measure.dataset_points.map(p=>p.clone()) ,
  873. points_datasets: measure.points_datasets
  874. } ) //完成 */
  875. bus.emit('submit')
  876. bus.emit('update', [
  877. measure.dataset_points.map(p => p.clone()),
  878. measure.points_datasets
  879. ])
  880. },
  881. () => {
  882. //cancel
  883. bus.emit('cancel'/* , ret */) //删除
  884. }
  885. )
  886. Potree.Log('startMeasure: ' + measure.id, '#00c7b2')
  887. /* let cancel = ()=>{
  888. Potree.Log('clear删除: ' + measure.id, '#00c7b2')
  889. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure })
  890. viewer.scene.removeMeasurement(measure)
  891. } */
  892. let result = {
  893. bus,
  894. ...getMeasureFunction(measure, bus),
  895. }
  896. /* StartMeasure = Measure & {
  897. // 多了cancel 取消测量的事件,没有参数
  898. // 多了invalidPoint 当用户测量了无效点时的事件,抛出无效原因
  899. bus: Emitter<{ cancel: void; invalidPoint: string }>
  900. } */
  901. return result
  902. },
  903. // 绘画测量线(非新增使用)
  904. // type = 'free' (自由) || 'vertical' (垂直) || 'area' (面积)
  905. // positions 点数组 构成如下 [{ point: {x,y,z}, modelId: 1 }]
  906. drawMeasure(type, dataset_points, points_datasets) {
  907. // 返回测量对象有如下
  908. const bus = mitt()
  909. let info = getMeasureType(type /* , unit */)
  910. //info.points = positions
  911. info.dataset_points = dataset_points
  912. info.points_datasets = points_datasets
  913. //info.sid = sid
  914. info.bus = bus
  915. let measure = viewer.measuringTool.createMeasureFromData(info)
  916. if (!measure) return { bus }
  917. Potree.Log('drawMeasure由数据新建: ' + measure.id, '#00c7b2')
  918. let result = {
  919. bus,
  920. setPositions(dataset_points, points_datasets) {//用于恢复measure的点,不会修改点的个数
  921. measure.dataset_points = dataset_points.map(e => {
  922. return e && new THREE.Vector3().copy(e)
  923. })
  924. measure.points_datasets = points_datasets
  925. measure.points = measure.dataset_points.map((p, i) => {
  926. return Potree.Utils.datasetPosTransform({ fromDataset: true, datasetId: measure.points_datasets[i], position: p })
  927. })
  928. measure.getPoint2dInfo(measure.points)
  929. measure.update({ ifUpdateMarkers: true })
  930. measure.setSelected(false)//隐藏edgelabel
  931. },
  932. ...getMeasureFunction(measure, bus),
  933. }
  934. return result
  935. },
  936. createPath(props){//路线
  937. /* export type Path = {
  938. bus: Emitter<{
  939. // 标注点击事件
  940. click: void;
  941. // 鼠标移入标注事件
  942. enter: void;
  943. // 鼠标移出标注事件
  944. leave: void;
  945. // 顶部标注中心点像素位置更改事件, 传出像素位置
  946. lineTopPositionChange: SceneLocalPos
  947. // 路径点位置变更
  948. changePoints: PathProps['points']
  949. // 距离相机位置变更
  950. toCameraDistanceChange: number
  951. }>;
  952. // 是否可编辑
  953. changeCanEdit: (canMove: boolean) => void
  954. // 标注可见性
  955. visibility: (visibility: boolean) => void
  956. // 更改标题气泡属性
  957. changeLine: (props: Partial<PathProps['line']>) => void
  958. // 顶标注中心像素位置
  959. lineTopPosition: ScreenLocalPos
  960. // 距离相机位置
  961. toCameraDistance: number
  962. // 线段销毁
  963. destory: () => void */
  964. },
  965. /* createTagging: (props: Tagging3DProps) => Tagging3D
  966. export type Tagging3DProps = {
  967. // 标注类型 2d | 3d
  968. type: TaggingPositionType,
  969. // 模型id
  970. modelId: string,
  971. // 贴地射线获取的位置
  972. position: SceneLocalPos
  973. // 是否可以移动
  974. canMove: boolean,
  975. // 贴地图片url
  976. image: string
  977. // 离地高度
  978. altitudeAboveGround: number,
  979. // 贴地图片的变换
  980. mat: {
  981. scale: SceneLocalPos,
  982. rotation: Rotation,
  983. }
  984. }
  985. export type Tagging3D = {
  986. bus: Emitter<{
  987. // 标注点击事件
  988. click: void;
  989. // 鼠标移入标注事件
  990. enter: void;
  991. // 鼠标移出标注事件
  992. leave: void;
  993. // 顶部标注中心点像素位置更改事件, 传出像素位置
  994. topPositionChange: SceneLocalPos
  995. // 位置变更
  996. changePosition: {pos: SceneLocalPos, modelId: string}
  997. // 距离相机位置变更
  998. toCameraDistanceChange: number
  999. }>;
  1000. changeCanMove: (canMove: boolean) => void
  1001. // 更改图标
  1002. changeImage: (url: string) => void
  1003. // 标注可见性
  1004. visibility: (visibility: boolean) => void
  1005. // 标注图片变换,传入变换
  1006. changeMat: (props: Tagging3DProps['mat']) => void
  1007. // 更改离地高度
  1008. changeAltitudeAboveGround: (val: number) => void
  1009. // 顶标注中心像素位置 (2d要加上图片大小)
  1010. topPosition: SceneLocalPos
  1011. // 距离相机位置
  1012. toCameraDistance: number
  1013. // 标注销毁
  1014. destory: () => void
  1015. }
  1016. */
  1017. createTagging(props){
  1018. let bus = mitt()
  1019. let root = viewer.scene.pointclouds.concat(viewer.objs.children).find(e=>e.dataset_id == props.modelId)
  1020. if(!root){
  1021. return console.error('热点没有找到该modelId,模型是否已经删除?')
  1022. }
  1023. let info = {
  1024. position: new THREE.Vector3().copy(props.position), //局部坐标
  1025. normal: new THREE.Vector3().copy(props.normal),
  1026. root, lineLength: props.altitudeAboveGround,
  1027. title: props.title, fontsize: props.fontSize
  1028. }
  1029. let tag = viewer.tagTool.createTagFromData(info)
  1030. tag.addEventListener('mouseover',()=>{
  1031. bus.emit('enter')
  1032. })
  1033. tag.addEventListener('mouseleave',()=>{
  1034. bus.emit('leave')
  1035. })
  1036. tag.addEventListener('click',()=>{
  1037. bus.emit('click')
  1038. })
  1039. tag.addEventListener('posChanged',(e)=>{
  1040. bus.emit('changePosition', {
  1041. modelId: tag.root.dataset_id,
  1042. normal: tag.normal.clone(),
  1043. pos: tag.position.clone()
  1044. })
  1045. })
  1046. tag.functions = {
  1047. bus,
  1048. changeType(type){
  1049. let onMesh = type == '3d'
  1050. if(tag.onMesh != onMesh){
  1051. tag.changeOnMesh(onMesh)
  1052. }
  1053. },
  1054. visibility(v){// 标注可见性
  1055. Potree.Utils.updateVisible(tag,'user', v)
  1056. },
  1057. visibilityTitle(v){
  1058. tag.setTitleVisi(v, 'user')
  1059. },
  1060. changeImage(url){
  1061. tag.changeMap(url)
  1062. },
  1063. changeTitle(title){
  1064. tag.setTitle(title)
  1065. },
  1066. changeMat({scale,rotation}){//大小旋转 贴墙时
  1067. tag.setFaceAngle(rotation)
  1068. tag.changeSpotScale(scale)
  1069. },
  1070. changeFontSize(fontsize){
  1071. tag.setFontSize(fontsize)
  1072. },
  1073. // 更改离地高度
  1074. changeLineHeight(height){//线长
  1075. tag.changeLineLen(height)
  1076. },
  1077. changeCanMove(canMove){
  1078. tag.dragEnable = canMove
  1079. },
  1080. getImageCenter(){ //热点在模型的本地坐标
  1081. /* tag.titleLabel.parent.updateMatrix()
  1082. //tag.titleLabel.parent.updateMatrixWorld()
  1083. let pos = tag.titleLabel.parent.getWorldPosition(new THREE.Vector3)
  1084. console.log(pos) */
  1085. return tag.onMesh ? tag.position : new THREE.Vector3().addVectors(tag.position, tag.titleLabel.parent.position)
  1086. },
  1087. /* toCameraDistance(far){//多远会消失
  1088. tag.farSquared = far * far
  1089. this.updateVisiFar(dis)
  1090. },
  1091. updateVisiFar(){//我自己调用
  1092. if(tag.farSquared){
  1093. let v = viewer.mainViewport.camera.position.distanceToSquared(tag.position) < tag.farSquared
  1094. Potree.Utils.updateVisible(tag,'updateVisiFar',v)
  1095. }
  1096. }, */
  1097. getCameraDisSquared(){//距离intersect的位置
  1098. return viewer.mainViewport.camera.position.distanceToSquared(tag.getWorldPosition()) /* < tag.farSquared */
  1099. },
  1100. destory(){
  1101. tag.dispose()
  1102. },
  1103. }
  1104. /*
  1105. tag.functions.changeType(props.type)
  1106. tag.functions.changeImage(props.image)
  1107. */
  1108. return tag.functions
  1109. },
  1110. /*
  1111. addTag(info) {//加标签
  1112. let bus = mitt()
  1113. let tag
  1114. let done = () => {
  1115. bus.emit('added')
  1116. bus.emit('update', { position: tag.position.clone(), normal: o.normal.clone(), modelId: tag.root.dataset_id })
  1117. tag = tag_
  1118. tag.spot.addEventListener('mouseover', () => {
  1119. bus.emit('hoverState', true)
  1120. })
  1121. tag.spot.addEventListener('mouseout', () => {
  1122. bus.emit('hoverState', false)
  1123. })
  1124. }
  1125. if (!info.position) {
  1126. viewer.tagTool.startInsertion().done(tag_ => {
  1127. done()
  1128. })
  1129. } else {
  1130. info.root = MergeEditor.getAllObjects().find(e => e.dataset_id == info.modelId)
  1131. if (!info.root) {
  1132. console.error('没有找到该modelId')
  1133. }
  1134. tag = viewer.tagTool.createTagFromData(info)
  1135. done()
  1136. }
  1137. let result = {
  1138. bus,
  1139. getScreenPos() {
  1140. let pos3d = new THREE.Vector3().setFromMatrixPosition(tag.matrixWorld)
  1141. return sdk.getScreenByPosition(pos3d)
  1142. },
  1143. show() {
  1144. Potree.Utils.updateVisible(tag, 'byList', true)
  1145. },
  1146. hide() {
  1147. Potree.Utils.updateVisible(tag, 'byList', false)
  1148. },
  1149. destroy() {
  1150. if (tag) {
  1151. tag.dispose()
  1152. }
  1153. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true })
  1154. },
  1155. changeTitle(title) {
  1156. tag.changeTitle(title)
  1157. }
  1158. }
  1159. return result
  1160. }, */
  1161. /* export type PathProps = {
  1162. line: {
  1163. width: number,
  1164. color: string,
  1165. altitudeAboveGround: number
  1166. position: SceneLocalPos,
  1167. modelId: string
  1168. },
  1169. points: {
  1170. position: SceneLocalPos,
  1171. modelId: string,
  1172. }[]
  1173. } */
  1174. showGrid() {
  1175. Potree.Utils.updateVisible(viewer.modules.MergeEditor.ground, 'hideGrid', true)
  1176. viewer.dispatchEvent('content_changed')
  1177. },
  1178. hideGrid() {
  1179. Potree.Utils.updateVisible(viewer.modules.MergeEditor.ground, 'hideGrid', false)
  1180. viewer.dispatchEvent('content_changed')
  1181. }
  1182. }
  1183. function spliceFromArr(model, props, loaded){
  1184. //let autoLoads.find()
  1185. props.loadFinish = true
  1186. props.loading = false
  1187. if (loaded) {
  1188. props.loaded = true
  1189. props.model = model
  1190. } else {
  1191. props.error = true
  1192. }
  1193. /* let haventLoad = autoLoads.filter(e=>!e.loading && !e.loadFinish);
  1194. if( haventLoad[0]){
  1195. startLoad(haventLoad[0])
  1196. */
  1197. if (!loadNext()) {
  1198. if (autoLoads.filter(e => !e.loadFinish).length == 0 && autoLoads.filter(e => e.loaded).length > 0 && !props.isFirstLoad) {//设置相机位置:当自动开始加载第一个模型时(其余的也跟着自动加载),等这批加载完后;
  1199. let autoLoadsDone = autoLoads.filter(e => e.loaded).map(e => e.model)
  1200. let loadTimeCost = Date.now() - loadStartTime
  1201. console.log('所有模型加载完毕, 耗时', parseInt(loadTimeCost) )
  1202. autoLoads.filter(e => e.loaded && e.show).forEach(e => e.model.visible = true)
  1203. MergeEditor.focusOn(autoLoadsDone, 1000, true, true)
  1204. autoLoads.length = 0
  1205. }
  1206. }
  1207. }
  1208. function loadNext(){
  1209. let haventLoad = autoLoads.filter(e => !e.loading && !e.loadFinish);
  1210. let loading = autoLoads.filter(e => e.loading);
  1211. let needLoad = haventLoad.slice(0, maxLoadingCount - loading.length)
  1212. needLoad.forEach(e => startLoad(e))
  1213. return haventLoad.length > 0
  1214. }
  1215. function startLoad(prop){
  1216. /* if(prop.raw.visible !== 1){//用于临时隐藏
  1217. setTimeout(()=>{
  1218. spliceFromArr(null, prop, false)
  1219. prop.bus.emit('loadError' )
  1220. },1)
  1221. return
  1222. } */
  1223. if(prop.loading || prop.loadFinish)return
  1224. Potree.Log(`--开始加载--`, { font: { color: '#f68' } });
  1225. console.log('id:', prop.id, ', title:', prop.title, ', filename:', Potree.Common.getNameFromURL(prop.url), ', type:', prop.type, prop)
  1226. prop.unlit = prop.renderType != 'normal'
  1227. prop.maximumScreenSpaceError = 70
  1228. prop.prefix = prop.raw.prefix
  1229. Potree.addModel(prop, prop.done, prop.progressFun, prop.onError)
  1230. prop.loading = true
  1231. }
  1232. function buildMap(){
  1233. if (Potree.settings.showCesium && !window.cesiumViewer) {
  1234. viewer.backgroundOpacity = 0
  1235. Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2ZGM2YzY0ZC1kNWE0LTRiYTgtYTkwNS1kYmJiODRjMWUwMmQiLCJpZCI6MjMzMTQ1LCJpYXQiOjE3MjI5OTUwNTB9.niqpkl6xOkQ2KeJjelyDDDydmSGqKXKb5cX2NyxSNAw'
  1236. window.cesiumViewer = new Cesium.Viewer('app', {
  1237. useDefaultRenderLoop: true,
  1238. requestRenderMode: true, //add 只有需要render时才会render,如tile加载完后、镜头移动后
  1239. animation: false,
  1240. baseLayerPicker: false,
  1241. fullscreenButton: false,
  1242. geocoder: false,
  1243. homeButton: false,
  1244. infoBox: false,
  1245. sceneModePicker: false,
  1246. selectionIndicator: false,
  1247. timeline: false,
  1248. navigationHelpButton: false,
  1249. //imageryProvider : Cesium.createOpenStreetMapImageryProvider({url : 'https://a.tile.openstreetmap.org/'}),
  1250. imageryProvider: Cesium.UrlTemplateImageryProvider({ //直接用84坐标,不用转高德
  1251. //"https://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&style=7&yrs=m&x=${x}&y=${y}&z=${z}" //
  1252. //url : 'https://webst0{0-7}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}&token=YOUR_API_KEY',
  1253. url: 'https://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}&token=YOUR_API_KEY',
  1254. minimumLevel: 0,
  1255. maximumLevel: 19
  1256. }),
  1257. //高德秘钥版 imageryProvider: new Cesium.AmapImageryProvider({key, mapStyle: 'normal'})
  1258. //报错 401 (Unauthorized) 的方法 https://blog.csdn.net/LBY_XK/article/details/121992641
  1259. terrainShadows: Cesium.ShadowMode.DISABLED, //terrain地形
  1260. });
  1261. //lonlat = [113.595236803415,22.3665168584444]//[113.600356,22.364093]
  1262. Potree.setLonlat(lonlat[0], lonlat[1])
  1263. Potree.cesScreenshot = (w,h)=>{
  1264. console.log('cesScreenshot',w,h)
  1265. cesiumViewer.scene.canvas.style.width = w+'px'
  1266. cesiumViewer.scene.canvas.style.height = h+'px'
  1267. cesiumViewer.scene.canvas.style.visibility = 'hidden'
  1268. cesiumViewer.resize()
  1269. cesAspect = w/h
  1270. let deferred = $.Deferred();
  1271. updateMap(w/h)//hfov可能改变了需要update。
  1272. setTimeout(()=>{ //延迟是似乎还要做别的处理,否则立即截图的话可能得到绿色底图(俯视状态容易触发)
  1273. let oldMode = window.cesiumViewer._cesiumWidget._scene.requestRenderMode
  1274. window.cesiumViewer._cesiumWidget._scene.requestRenderMode = 0 //强制render,否则会黑屏
  1275. cesiumViewer.render();
  1276. let dataUrl = window.cesiumViewer.scene.canvas.toDataURL('image/png')
  1277. window.cesiumViewer._cesiumWidget._scene.requestRenderMode = oldMode
  1278. //Potree.Common.downloadFile(dataUrl, 'screenshot.png')
  1279. cesAspect = null
  1280. cesiumViewer.scene.canvas.style.width = ''
  1281. cesiumViewer.scene.canvas.style.height = ''
  1282. cesiumViewer.scene.canvas.style.visibility = ''
  1283. deferred.resolve(dataUrl)
  1284. },200) //时间短了容易黑屏
  1285. return deferred.promise()
  1286. }
  1287. }
  1288. updateMap()
  1289. }
  1290. function updateCamNear(type){// 有的漫游场景模型缩放的很小(0.1%),需要缩小near才能看见, 但会造成z-fighting, 离远了看大模型会闪烁
  1291. const min = 0.0001, max = 0.1
  1292. let near , bigScale = 0.2
  1293. if(Potree.settings.displayMode == 'showPanos'/* && type != 'cameraMove' */){
  1294. let currentModel = viewer.images360.currentPano.pointcloud
  1295. near = Potree.math.linearClamp(currentModel.scale.x, [0, 1], [min, max])
  1296. }else/* if(type == 'cameraMove') */{
  1297. //没有完美的解决方式,优先考虑选中的模型。否则优先考虑离得近的。 另外尽量用大的,因为缩很小的情况很少。
  1298. //虽然案例说应该看model.bound.size,但如果非场景模型,缩很小的话也不需要凑近看。
  1299. let allModels = viewer.objs.children.concat(viewer.scene.pointclouds)
  1300. if(allModels.length == 0)return
  1301. allModels.sort((a,b)=>{return a.scale.x - b.scale.x})
  1302. let minS = allModels[0].scale.x, maxS = allModels.pop().scale.x
  1303. let considerModel
  1304. if(minS>bigScale) near = max
  1305. else{
  1306. if(MergeEditor.selected){
  1307. considerModel = MergeEditor.selected
  1308. near = Potree.math.linearClamp(considerModel.scale.x, [0, bigScale], [min, max])
  1309. }else{
  1310. //allModels = allModels.filter() //写不下去了好难,就算了吧, 折中
  1311. near = Potree.math.linearClamp(minS, [0, bigScale], [max/4, max])
  1312. }
  1313. }
  1314. }
  1315. if(near != viewer.mainViewport.camera.near){
  1316. console.log('updateNear',near)
  1317. viewer.mainViewport.camera.near = near
  1318. viewer.mainViewport.camera.updateProjectionMatrix()
  1319. viewer.dispatchEvent('content_changed')
  1320. }
  1321. }
  1322. function updateMap( ){
  1323. if (Potree.settings.showCesium && Potree.settings.displayMode == 'showPointCloud') {
  1324. let camera = viewer.mainViewport.camera
  1325. let pPos = new THREE.Vector3(0, 0, 0).applyMatrix4(camera.matrixWorld);
  1326. let pRight = new THREE.Vector3(600, 0, 0).applyMatrix4(camera.matrixWorld);
  1327. let pUp = new THREE.Vector3(0, 600, 0).applyMatrix4(camera.matrixWorld);
  1328. let pTarget = viewer.scene.view.getPivot();
  1329. let toCes = (pos) => {
  1330. let xy = [pos.x, pos.y];
  1331. let height = pos.z;
  1332. let deg = viewer.transform.lonlatToLocal.inverse(xy) // toMap.forward(xy);
  1333. let cPos = Cesium.Cartesian3.fromDegrees(...deg, height);
  1334. return cPos;
  1335. };
  1336. let cPos = toCes(pPos);
  1337. let cUpTarget = toCes(pUp);
  1338. let cTarget = toCes(pTarget);
  1339. let cDir = Cesium.Cartesian3.subtract(cTarget, cPos, new Cesium.Cartesian3());
  1340. let cUp = Cesium.Cartesian3.subtract(cUpTarget, cPos, new Cesium.Cartesian3());
  1341. cDir = Cesium.Cartesian3.normalize(cDir, new Cesium.Cartesian3());
  1342. cUp = Cesium.Cartesian3.normalize(cUp, new Cesium.Cartesian3());
  1343. cesiumViewer.camera.setView({
  1344. destination: cPos,
  1345. orientation: {
  1346. direction: cDir,
  1347. up: cUp
  1348. }
  1349. });
  1350. let aspect = cesAspect || viewer.scene.getActiveCamera().aspect;
  1351. //console.log('updateMap', aspect)
  1352. if (aspect < 1) {
  1353. let fovy = Math.PI * (viewer.scene.getActiveCamera().fov / 180);
  1354. cesiumViewer.camera.frustum.fov = fovy;
  1355. } else {
  1356. let fovy = Math.PI * (viewer.scene.getActiveCamera().fov / 180);
  1357. let fovx = Math.atan(Math.tan(0.5 * fovy) * aspect) * 2
  1358. cesiumViewer.camera.frustum.fov = fovx;
  1359. }
  1360. cesiumViewer.render(); //立即render,否则会和点云render不同步而错位
  1361. }
  1362. }
  1363. return sdk
  1364. }
  1365. /* class path = */
  1366. /*
  1367. 暂定不同场景间的漫游点不能互通。虽然它们可能是摆放正确的,如果是组成一整个场景的话还是要打通……
  1368. 不互通的方法是设置pano.enable
  1369. 现在需要互通了。但是还需要设置neibgbours, 有点麻烦,暂时没写。
  1370. */
  1371. export default enter