start.js 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import math from './utils/math.js'
  3. import browser from './utils/browser.js'
  4. import './three.shim.js'
  5. import "./potree.shim.js"
  6. export function start(dom, mapDom, number ){ //t-Zvd3w0m
  7. /* {
  8. let obj = JSON.parse(localStorage.getItem('setting'))
  9. for(let i in obj){
  10. console.log(i + ': ' + obj[i])
  11. }
  12. }
  13. */
  14. Potree.settings.number = number || 't-o5YMR13'// 't-iksBApb'// 写在viewer前
  15. if(browser.urlHasValue('google'))Potree.settings.mapCompany = 'google'
  16. if(browser.urlHasValue('timing'))Potree.measureTimings = 1
  17. let viewer = new Potree.Viewer(dom , mapDom);
  18. let Alignment = viewer.modules.Alignment
  19. viewer.setEDLEnabled(false);
  20. viewer.setFOV(Potree.config.view.fov);
  21. //viewer.loadSettingsFromURL();
  22. if(!Potree.settings.isOfficial){
  23. viewer.loadGUI(() => {
  24. viewer.setLanguage('en');
  25. //$("#menu_appearance").next().show();
  26. $("#menu_tools").next().show();
  27. $("#menu_scene").next().show();
  28. $("#siteModel").show();
  29. //$("#alignment").show();
  30. viewer.toggleSidebar();
  31. });
  32. Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
  33. Potree.settings.rotAroundPoint = false
  34. if(/* Potree.settings.isTest && */ browser.isMobile()){
  35. changeLog()
  36. }
  37. }
  38. Potree.loadDatasetsCallback = function(data, ifReload){
  39. if(!data || data.length == 0)return console.error('getDataSet加载的数据为空')
  40. Potree.datasetData = data
  41. viewer.transform = null
  42. var datasetLength = data.length
  43. var pointcloudLoaded = 0
  44. var panosLoaded = 0
  45. var pointcloudLoadDone = function(){//点云cloud.js加载完毕后
  46. }
  47. var panosLoadDone = function(){
  48. viewer.images360.loadDone()
  49. viewer.scene.add360Images(viewer.images360);
  50. viewer.mapViewer.addListener(viewer.images360)
  51. viewer.updateModelBound() //需等pano加载完
  52. let {boundSize, center} = viewer.bound
  53. if(!Potree.settings.isOfficial){
  54. Potree.loadMapEntity('all') //加载floorplan
  55. }
  56. if(!ifReload){
  57. viewer.dispatchEvent({type:'loadPointCloudDone'})
  58. if(!Potree.settings.UserPointDensity){
  59. Potree.settings.UserPointDensity = 'high'//'middle'
  60. }
  61. Potree.Log('loadPointCloudDone 点云加载完毕', {font:[null, 10]})
  62. }
  63. {//初始位置
  64. var panoId = browser.urlHasValue('pano',true);
  65. if(panoId !== ''){
  66. var pos
  67. var pano = viewer.images360.panos.find(e=>e.id==panoId);
  68. if(pano){
  69. viewer.images360.focusPano({
  70. pano,
  71. duration:0,
  72. callback:()=>{/* Potree.settings.displayMode = 'showPanos' */}
  73. })
  74. }
  75. }else{//考虑到多数据集距离很远,或者像隧道那种场景,要使视野范围内一定能看到点云,最好初始点设置在漫游点上
  76. let {boundSize, center} = viewer.bound
  77. let pano = viewer.images360.findNearestPano(center)
  78. pano && viewer.images360.flyToPano({
  79. pano, duration:0,
  80. target : viewer.images360.bound.center.setZ(pano.position.z) //平视中心区域(但也不能保证这个方向一定能看到点云密集区,如果在边缘的话)
  81. })
  82. }
  83. }
  84. viewer.addVideo()//addFire()
  85. console.log('allLoaded')
  86. viewer.dispatchEvent('allLoaded')
  87. }
  88. var transformPointcloud = (pointcloud, dataset)=>{
  89. var locationLonLat = dataset.location.slice(0,2)
  90. //当只有一个dataset时,无论如何transform 点云和漫游点都能对应上。
  91. /* if(window.AMapWith84 && Potree.settings.mapCompany != 'google'){
  92. locationLonLat = AMapWith84.wgs84ToAMap({x:locationLonLat[0], y:locationLonLat[1]})
  93. locationLonLat = [locationLonLat.x,locationLonLat.y]
  94. } */
  95. var location = viewer.transform.lonlatToLocal.forward(locationLonLat) //transform.inverse()
  96. //初始化位置
  97. /*
  98. location[0] = Math.sign(location[0]) * Math.min(7000, Math.abs(location[0]))
  99. location[1] = Math.sign(location[1]) * Math.min(7000, Math.abs(location[1]))
  100. */
  101. viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud)
  102. //dataset.orientation = 0
  103. Alignment.rotate(pointcloud, null, dataset.orientation)
  104. Alignment.translate(pointcloud, new THREE.Vector3(location[0], location[1], dataset.location[2]))
  105. pointcloud.updateMatrixWorld()
  106. Potree.Log(`点云${pointcloud.dataset_id}(${pointcloud.name})旋转值:${pointcloud.orientationUser}, 位置${math.toPrecision(pointcloud.translateUser.toArray(),3)}, 经纬度 ${locationLonLat}, spacing ${pointcloud.material.spacing}`, {font:[null, 13]} )
  107. //-------------------
  108. //viewer.mapView.showSources(false);
  109. }
  110. if(!Potree.settings.originDatasetId)Potree.settings.originDatasetId = data[0].id
  111. var originDataset = data.find(e=>e.id == Potree.settings.originDatasetId)
  112. {//拿初始数据集作为基准。它的位置是000
  113. var locationLonLat = originDataset.location.slice(0,2)
  114. if(window.AMapWith84){//需要转换为高德的
  115. locationLonLat = AMapWith84.wgs84ToAMap({x:locationLonLat[0], y:locationLonLat[1]})
  116. locationLonLat = [locationLonLat.x,locationLonLat.y]
  117. }
  118. proj4.defs("LOCAL", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); //高德坐标系
  119. proj4.defs("LOCAL_MAP", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15));
  120. proj4.defs("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
  121. let transform1 = proj4("WGS84", "LOCAL"); //这个ok 是展开的平面投影 LOCAL即NAVVIS:TMERC
  122. let transform2 = proj4("+proj=tmerc +lat_0=0 +lon_0=123 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs;");
  123. //注:转入后再转出,和原来的有偏差。如果输入是local坐标,数字越大偏差越大,当百万时就明显了。如果是lonlat,很奇怪经度小于50时就乱了。
  124. viewer.transform = {
  125. lonlatToLocal : transform1,
  126. lonlatTo4550 : transform2 // 转大地坐标EPSG:4550
  127. }
  128. if(window.AMapWith84 && Potree.settings.mapCompany != 'google'){//需要转换, 因本地高德用的lonlat和数据里的84不一样. (google地图在国内也用的高德,国外84)
  129. let change = (transform)=>{
  130. let forward = transform.forward
  131. let inverse = transform.inverse;
  132. transform.forward = function(e){
  133. let needTran = e.x == void 0
  134. if(needTran)var a1 = {x:e[0],y:e[1]}
  135. else var a1 = e
  136. var a = AMapWith84.wgs84ToAMap(a1)
  137. if(needTran){
  138. a = [a.x, a.y]; e[2] != void 0 && (a[2] = e[2])
  139. }else{
  140. e.z != void 0 && (a.z = e.z)
  141. }
  142. return forward(a)
  143. }
  144. transform.inverse = function(e){
  145. let needTran = e.x == void 0
  146. var a = inverse(e)
  147. needTran && (a = {x:a[0],y:a[1]})
  148. a = AMapWith84.aMapToWgs84(a)
  149. if(needTran){
  150. a = [a.x,a.y]; e[2] != void 0 && (a[2] = e[2])
  151. }else{
  152. e.z != void 0 && (a.z = e.z)
  153. }
  154. return a
  155. }
  156. }
  157. for(let f in viewer.transform){
  158. change(viewer.transform[f])
  159. }
  160. }
  161. viewer.mapViewer && viewer.mapViewer.mapLayer.maps[0].updateProjection()
  162. }
  163. data.forEach((dataset,index)=>{
  164. if(!ifReload){
  165. var datasetCode = dataset.sceneCode || dataset.name //对应4dkk的场景码
  166. //var cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${datasetCode}/data/${datasetCode}/webcloud/cloud.js`
  167. var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢
  168. var timeStamp = dataset.updateTime ? dataset.updateTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随updateTime更新一次
  169. //console.warn(dataset.name, 'timeStamp', timeStamp)
  170. Potree.loadPointCloud(cloudPath, dataset.name ,datasetCode, timeStamp, e => {
  171. let scene = viewer.scene;
  172. let pointcloud = e.pointcloud;
  173. let config = Potree.config.material
  174. let material = pointcloud.material;
  175. pointcloud.datasetData = dataset
  176. pointcloud.dataset_id = dataset.id;//供漫游点找到属于的dataset点云
  177. pointcloud.hasDepthTex = Potree.settings.useDepthTex && (!!dataset.has_depth || Potree.settings.isLocalhost && Potree.settings.number == 'SS-t-7DUfWAUZ3V') //test
  178. material.minSize = config.minSize
  179. material.maxSize = config.maxSize
  180. material.pointSizeType =/* Potree.settings.isOfficial ? */ config.pointSizeType /* : 'ADAPTIVE' */ //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
  181. pointcloud.changePointSize(config.realPointSize) //material.size = config.pointSize;
  182. pointcloud.changePointOpacity(1)
  183. material.shape = Potree.PointShape.SQUARE;
  184. pointcloud.color = pointcloud.material.color = dataset.color
  185. pointcloud.timeStamp = timeStamp
  186. transformPointcloud(pointcloud,dataset)
  187. scene.addPointCloud(pointcloud);
  188. if(!Potree.settings.isOfficial){
  189. Potree.settings.floorplanEnables[dataset.id] = true
  190. Potree.settings.floorplanType[dataset.id] = 'default'
  191. }
  192. pointcloudLoaded ++;
  193. if(pointcloudLoaded == datasetLength)pointcloudLoadDone()
  194. Potree.loadPanos(dataset.id, (data) => {
  195. //console.log('loadPanos',dataset.sceneCode, dataset.id, data)
  196. viewer.images360.addPanoData(data, dataset.id )
  197. panosLoaded ++;
  198. if(panosLoaded == datasetLength){
  199. panosLoadDone()
  200. }
  201. })
  202. })
  203. }else{
  204. let pointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == dataset.id)
  205. if(!pointcloud){
  206. Potree.Log('数据集id变了,自动使用第一个', {font:['#500' ]} )
  207. pointcloud = viewer.scene.pointclouds[0]
  208. }
  209. //先归零
  210. Alignment.translate(pointcloud, pointcloud.translateUser.clone().negate())
  211. Alignment.rotate(pointcloud, null, - pointcloud.orientationUser)
  212. transformPointcloud(pointcloud, dataset)
  213. }
  214. })
  215. if(ifReload){
  216. //loadDone()
  217. }
  218. }
  219. number && Potree.loadDatasets(Potree.loadDatasetsCallback)
  220. window.testTransform = function(locationLonLat, location1, location2){
  221. proj4.defs("NAVVIS:test", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15));
  222. let transform = proj4("WGS84", "NAVVIS:test"); //这个ok navvis里也是这两种转换 见proj4Factory
  223. if(location1){//经纬度
  224. return transform.forward(location1)
  225. }else{
  226. return transform.inverse(location2)
  227. }
  228. }
  229. window.THREE = THREE
  230. window.buttonFunction = function(){
  231. viewer.scene.pointclouds.forEach(e=>e.predictNodeMaxLevel())
  232. /*
  233. viewer.startScreenshot({type:'measure', measurement:viewer.scene.measurements[0]})
  234. viewer.modules.RouteGuider.routeStart = new THREE.Vector3(0,0,-1.3)
  235. viewer.modules.RouteGuider.routeEnd = new THREE.Vector3(-10,0,-1.3)
  236. */
  237. }
  238. if(Potree.settings.isLocalhost){
  239. let before = {}
  240. viewer.inputHandler.addEventListener('keydown',e=>{ //测试的代码
  241. if(e.event.key == 't'){
  242. viewer.images360.cube.visible = true
  243. viewer.images360.cube.material.wireframe = true
  244. }else if(e.event.key == 'y'){
  245. viewer.images360.cube.material.wireframe = false
  246. viewer.images360.cube.visible = Potree.settings.displayMode == 'showPanos'
  247. }
  248. })
  249. }
  250. //--------------------------------
  251. if(!number){
  252. Potree.settings.boundAddObjs = true
  253. Potree.settings.intersectOnObjs = true
  254. // Load untextured bunny from ply
  255. viewer.loadModel({
  256. fileType:'ply',
  257. url:Potree.resourcePath + "/models/indoor.ply",
  258. name:'test',
  259. },
  260. (object)=>{
  261. object.isModel = true
  262. viewer.updateModelBound()
  263. }
  264. )
  265. }
  266. }
  267. //=======================================================================
  268. /*
  269. 漫游点编辑
  270. */
  271. //=======================================================================
  272. export function panoEditStart(dom, number, fileServer){
  273. Potree.settings.editType = 'pano'
  274. Potree.settings.number = number
  275. Potree.settings.unableNavigate = true
  276. Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
  277. let viewer = new Potree.Viewer(dom);
  278. let Alignment = viewer.modules.Alignment
  279. viewer.setEDLEnabled(false);
  280. viewer.setFOV(Potree.config.view.fov);
  281. //viewer.loadSettingsFromURL();
  282. let datasetLoaded = 0;
  283. if(!Potree.settings.isOfficial){
  284. viewer.loadGUI(() => {
  285. viewer.setLanguage('en');
  286. $("#menu_tools").next().show();
  287. $("#panos").show();
  288. $("#alignment").show();
  289. viewer.toggleSidebar();
  290. });
  291. Potree.settings.sizeFitToLevel = true
  292. }
  293. var pointcloudLoadDone = function( ){//所有点云cloud.js加载完毕后
  294. viewer.scene.pointclouds.forEach(c=>{
  295. transformPointcloud(c)
  296. })
  297. viewer.images360.loadDone()
  298. viewer.scene.add360Images(viewer.images360);
  299. viewer.updateModelBound()
  300. let {boundSize, center} = viewer.bound
  301. Potree.Log(`中心点: ${math.toPrecision(center.toArray(),2)}, boundSize: ${math.toPrecision(boundSize.toArray(),2)} ` , {font:[null, 12]} )
  302. viewer.scene.view.setView({
  303. position: center.clone().add(new THREE.Vector3(10,5,10)),
  304. target: center
  305. })
  306. viewer.dispatchEvent({type:'loadPointCloudDone'})
  307. if(!Potree.settings.UserPointDensity){
  308. Potree.settings.UserPointDensity = 'panoEdit'//'middle'
  309. }
  310. Potree.Log('loadPointCloudDone 点云加载完毕',{font:[null, 10]})
  311. viewer.dispatchEvent('allLoaded');
  312. }
  313. var transformPointcloud = (pointcloud )=>{ //初始化位置
  314. viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud)
  315. let orientation = pointcloud.panos[0].dataRotation.z + Math.PI
  316. let location = pointcloud.panos[0].dataPosition.clone()//.negate()
  317. Alignment.rotate(pointcloud, null, orientation )
  318. Alignment.translate(pointcloud, location )
  319. pointcloud.updateMatrixWorld()
  320. }
  321. let loadPanosDone = Potree.loadPanosDone = (datasetId, panoData )=>{ //一个数据集获取到它的panos后
  322. Potree.settings.datasetsPanos[datasetId] = {panoData, panos:[]}
  323. console.log('panoData', datasetId, panoData)
  324. let panoCount = panoData.length
  325. let pointcloudLoaded = 0
  326. let datasetsCount = Object.keys(Potree.settings.datasetsPanos).length
  327. panoData.forEach((pano, index)=>{
  328. //let cloudPath = `${Potree.scriptPath}/data/panoEdit/uuidcloud/${pano.uuid}/cloud.js`
  329. let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${Potree.settings.number}/data/bundle_${Potree.settings.number}/building/uuidcloud/${pano.uuid}/cloud.js`
  330. let name = datasetId + '-'+pano.uuid
  331. let timeStamp = 0
  332. pano.index = index //注意:index不等于uuid,因为有的uuid缺失。但是visibles中存的是下标!
  333. Potree.loadPointCloud(cloudPath, name , name, timeStamp, e => { //开始加载点云
  334. let scene = viewer.scene;
  335. let pointcloud = e.pointcloud;
  336. let config = Potree.config.material
  337. let material = pointcloud.material;
  338. material.minSize = config.minSize
  339. material.maxSize = config.maxSize
  340. material.pointSizeType = /* 'ADAPTIVE'// */config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
  341. pointcloud.changePointSize( 0.1 /* config.realPointSize */ ) //material.size = config.pointSize;
  342. pointcloud.changePointOpacity(1)
  343. material.shape = Potree.PointShape.SQUARE;
  344. pointcloud.color = config.pointColor
  345. pointcloud.dataset_id = datasetId; //多个点云指向一个datasetId
  346. pointcloud.panoUuid = pano.uuid
  347. pointcloud.timeStamp = timeStamp
  348. //transformPointcloud(pointcloud, pano)
  349. scene.addPointCloud(pointcloud);
  350. pointcloudLoaded ++;
  351. if(pointcloudLoaded == panoCount ){
  352. datasetLoaded ++
  353. viewer.images360.addPanoData(panoData , datasetId )
  354. if(datasetLoaded == datasetsCount){
  355. pointcloudLoadDone()
  356. }
  357. }
  358. })
  359. })
  360. }
  361. if(!Potree.settings.isOfficial){
  362. Potree.settings.datasetsPano = {'testDataset':null}
  363. Potree.loadPanosInfo( data=>{loadPanosDone('testDataset', data.sweepLocations)} )
  364. }
  365. }
  366. export function mergeEditStart(dom){
  367. Potree.settings.editType = 'merge'
  368. Potree.settings.intersectOnObjs = true
  369. Potree.settings.boundAddObjs = true
  370. Potree.settings.unableNavigate = true
  371. let viewer = new Potree.Viewer(dom );
  372. let Alignment = viewer.modules.Alignment
  373. viewer.setEDLEnabled(false);
  374. viewer.setFOV(Potree.config.view.fov);
  375. //viewer.loadSettingsFromURL();
  376. {
  377. viewer.mainViewport.view.position.set(30,30,30)
  378. viewer.mainViewport.view.lookAt(0,0,0)
  379. viewer.updateModelBound()//init
  380. //this.bound = new THREE.Box3(new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,1,1))
  381. viewer.transformationTool.setModeEnable(['translation','rotation'] )
  382. viewer.ssaaRenderPass.sampleLevel = 1 // sampleLevel为1 的话,ground就不会变黑
  383. viewer.inputHandler.fixSelection = true //不通过点击屏幕而切换transfrom选中状态
  384. }
  385. Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
  386. Potree.loadPointCloudScene = function(url, type, id, title, done, onError){//对应4dkk的场景码
  387. let loadCloud = ({cloudPath, sceneName, sceneCode, timeStamp, color } )=>{
  388. Potree.loadPointCloud(cloudPath, sceneName , sceneCode, timeStamp, e => {
  389. let scene = viewer.scene;
  390. let pointcloud = e.pointcloud;
  391. let config = Potree.config.material
  392. let material = pointcloud.material;
  393. material.minSize = config.minSize
  394. material.maxSize = config.maxSize
  395. material.pointSizeType = config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
  396. pointcloud.changePointSize(config.realPointSize) //material.size = config.pointSize;
  397. pointcloud.changePointOpacity(1)
  398. material.shape = Potree.PointShape.SQUARE;
  399. color && (pointcloud.color = pointcloud.material.color = color)
  400. pointcloud.timeStamp = timeStamp
  401. //transformPointcloud(pointcloud, originDataset)
  402. scene.addPointCloud(pointcloud);
  403. {
  404. viewer.updateModelBound()
  405. let {boundSize, center} = viewer.bound
  406. viewer.dispatchEvent({type:'loadPointCloudDone'})
  407. if(!Potree.settings.UserPointDensity){
  408. Potree.settings.UserPointDensity = 'high'//'middle'
  409. }
  410. Potree.Log('loadPointCloudDone 点云加载完毕', {font:[null,10] })
  411. }
  412. /* Potree.loadPanos(dataset.id, (data) => { //暂时不加载panos了,因为没有id
  413. //console.log('loadPanos',dataset.sceneCode, dataset.id, data)
  414. viewer.images360.addPanoData(data, dataset.id )
  415. viewer.images360.loadDone()
  416. viewer.scene.add360Images(viewer.images360); */
  417. viewer.dispatchEvent('allLoaded')
  418. done(pointcloud)
  419. },onError)
  420. }
  421. if(type == 'laser'){
  422. let sceneCode = url
  423. Potree.loadDatasets((data)=>{
  424. let originDataset = data.find(e=>e.sceneCode == sceneCode);//只加载初始数据集
  425. let timeStamp = originDataset.updateTime ? originDataset.updateTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随updateTime更新一次
  426. //let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${sceneCode}/data/${sceneCode}/webcloud/cloud.js`
  427. let cloudPath = `${Potree.settings.urls.prefix1}/${originDataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢
  428. loadCloud({cloudPath, sceneName:originDataset.sceneName, sceneCode, timeStamp, color:originDataset.color})
  429. }, sceneCode, onError)
  430. }else{//las or ply 直接用url
  431. let name = type + '|' + id + '|' + title
  432. if(url instanceof Array){
  433. if(url.length == 1){
  434. url = url[0]
  435. }else{
  436. console.error('有多个点云?暂时还不支持', url, name)//多个点云要一起移动没想好怎么写
  437. }
  438. }
  439. let cloudPath = url + '/cloud.js'
  440. loadCloud({cloudPath, sceneName:name, sceneCode:name, timeStamp:'' })
  441. }
  442. }
  443. let setMatrix = (pointcloud)=>{//为了漫游点变换,要算一下 类似setMatrix
  444. /* pointcloud.transformMatrix = new THREE.Matrix4().multiplyMatrices(pointcloud.matrix, pointcloud.pos1MatrixInvert)//还原一点位移
  445. pointcloud.transformInvMatrix.copy(pointcloud.transformMatrix).invert()
  446. pointcloud.rotateMatrix = new THREE.Matrix4().makeRotationFromEuler(pointcloud.rotation);
  447. pointcloud.rotateInvMatrix.copy(pointcloud.rotateMatrix).invert()
  448. pointcloud.panos.forEach(e=>e.transformByPointcloud()) */
  449. //pointcloud.updateBound()
  450. //pointcloud.getPanosBound()
  451. viewer.updateModelBound()
  452. }
  453. let moveModel = (e)=>{//根据鼠标移动的位置改变位置
  454. let camera = viewer.mainViewport.camera
  455. var origin = new THREE.Vector3(e.pointer.x, e.pointer.y, -1).unproject(camera),
  456. end = new THREE.Vector3(e.pointer.x, e.pointer.y, 1).unproject(camera)
  457. var dir = end.sub(origin)
  458. let planeZ = 0;
  459. let r = (planeZ - origin.z)/dir.z
  460. let x = r * dir.x + origin.x
  461. let y = r * dir.y + origin.y
  462. //过后改为根据intersect的点来设置底部高度;这样的话,需要发送高度
  463. /*let pos = new THREE.Vector3(x,y, planeZ )
  464. modelEditing.updateMatrixWorld()
  465. let boundCenter = modelEditing.boundingBox.getCenter(new THREE.Vector3).applyMatrix4(modelEditing.matrixWorld);
  466. */
  467. MergeEditor.moveBoundCenterTo(modelEditing,new THREE.Vector3(x,y, modelEditing.boundCenter.z)) //使模型中心的xy在鼠标所在位置
  468. modelEditing.dispatchEvent("position_changed")
  469. }
  470. let cancelMove = ()=>{
  471. modelEditing = null
  472. viewer.removeEventListener('global_mousemove', moveModel);
  473. viewer.removeEventListener('global_click', confirmPos);
  474. }
  475. let confirmPos = ()=>{
  476. MergeEditor.focusOn(modelEditing)
  477. cancelMove()
  478. return {stopContinue:true}
  479. }
  480. let modelType, modelEditing, MergeEditor = viewer.modules.MergeEditor
  481. Potree.addModel = function(prop, done, onProgress, onError){ //加载模型
  482. let loadDone = (model)=>{
  483. model.dataset_id = prop.id //唯一标识
  484. if(prop.position){
  485. model.position.copy(prop.position)
  486. }
  487. if(prop.rotation){
  488. //model.rotation.setFromVector3(prop.rotation)
  489. model.rotation.copy(prop.rotation)
  490. }
  491. if(prop.scale){
  492. model.scale.set(prop.scale,prop.scale,prop.scale)
  493. }
  494. if(model.isPointcloud){
  495. model.renderOrder = Potree.config.renderOrders.model; //same as glb
  496. }
  497. if(Potree.settings.maintainBtmZ)
  498. {//transform --------维持离地高度和中心点的版本(local ver)
  499. let updateBound = ()=>{
  500. model.updateMatrixWorld()
  501. viewer.updateModelBound()
  502. }
  503. let maintainBtmZAndCenter = ()=>{
  504. MergeEditor.maintainBoundXY(model)
  505. MergeEditor.setModelBtmHeight(model)
  506. updateBound()
  507. model.dispatchEvent('transformChanged')
  508. }
  509. model.addEventListener('position_changed', ()=>{
  510. updateBound()
  511. MergeEditor.getBoundCenter(model);//更新boundcenter
  512. MergeEditor.computeBtmHeight(model)
  513. if(prop.bottomRange && (model.btmHeight > prop.bottomRange.max || model.btmHeight < prop.bottomRange.min)){
  514. model.btmHeight = THREE.Math.clamp(model.btmHeight, prop.bottomRange.min, prop.bottomRange.max)
  515. MergeEditor.setModelBtmHeight(model)
  516. updateBound()
  517. }
  518. model.dispatchEvent('transformChanged')
  519. })
  520. model.addEventListener("rotation_changed", maintainBtmZAndCenter )
  521. model.addEventListener("scale_changed", maintainBtmZAndCenter )
  522. model.addEventListener('transformChanged', ()=>{
  523. MergeEditor.modelTransformCallback(model)
  524. })
  525. //离地高度只是boundingbox在transform后的最低点的高度,而非模型transform后的最低点的高度,所以旋转过后看起来不太准确
  526. } else
  527. {//transform --------维持中心点的版本
  528. let updateBound = ()=>{
  529. model.updateMatrixWorld()
  530. viewer.updateModelBound()
  531. }
  532. let maintainCenter = ()=>{
  533. //MergeEditor.maintainBoundXY(model)
  534. MergeEditor.maintainBoundCenter(model)
  535. updateBound()
  536. model.dispatchEvent('transformChanged')
  537. }
  538. model.addEventListener('position_changed', ()=>{
  539. updateBound()
  540. MergeEditor.getBoundCenter(model);//更新boundcenter
  541. model.dispatchEvent('transformChanged')
  542. })
  543. model.addEventListener("rotation_changed", maintainCenter )
  544. model.addEventListener("scale_changed", maintainCenter )
  545. model.addEventListener('transformChanged', ()=>{
  546. MergeEditor.modelTransformCallback(model)
  547. })
  548. }
  549. model.updateMatrixWorld()
  550. viewer.updateModelBound()
  551. MergeEditor.getBoundCenter(model) //初始化
  552. model.lastMatrixWorld = model.matrixWorld.clone()
  553. done(model) // 先发送成功,因为2d界面会随机执行changePosition等初始化,然后这边再将模型移到中心地面上
  554. if(prop.isFirstLoad){
  555. MergeEditor.moveBoundCenterTo(model, new THREE.Vector3(0,0,0))
  556. MergeEditor.setModelBtmHeight(model, 0) //初始加载设置离地高度为0
  557. if(prop.mode != 'single'){//如果不是模型展示页,模型会随着鼠标位置移动
  558. modelEditing = model;
  559. /* if(model.fileType == '3dTiles'){
  560. setTimeout(()=>{
  561. moveModel({pointer:{x:0,y:0}}) //3dTiles的移动会错乱,先默认放在当前视图中间吧
  562. confirmPos()
  563. },1)
  564. }else{ */
  565. viewer.addEventListener('global_mousemove', moveModel);
  566. viewer.addEventListener('global_click', confirmPos, {importance:3});
  567. //}
  568. }
  569. model.dispatchEvent("position_changed")
  570. }else{
  571. //MergeEditor.setModelBtmHeight(model, prop.bottom || 0) //默认离地高度为0
  572. modelEditing = null
  573. }
  574. MergeEditor.modelAdded(model)
  575. }
  576. if(prop.type == 'glb'){
  577. let callback = (object)=>{
  578. //focusOnSelect(object, 1000)
  579. object.isModel = true
  580. //object.dataset_id = Date.now() //暂时
  581. object.traverse(e=>e.material && (e.material.transparent = true))
  582. /* object.addEventListener('click',(e)=>{
  583. //只是为了能得到hoverElement识别才加这个侦听
  584. }) */
  585. loadDone(object)
  586. }
  587. let info = {
  588. fileType: prop.type,
  589. id: prop.id,
  590. unlit:true,
  591. url : prop.url,
  592. name : prop.title,
  593. /* transform : {
  594. position : prop.position,
  595. rotation : new THREE.Euler().setFromVector3(prop.rotation),
  596. scale: new THREE.Vector3(prop.scale,prop.scale,prop.scale),
  597. } */
  598. }
  599. viewer.loadModel(info , callback, onProgress, onError)
  600. }else if(prop.type == 'obsg' || prop.type == 'b3dm'){ //3d tiles
  601. let callback = (object)=>{
  602. object.isModel = true
  603. //透明度怎么办
  604. //object.traverse(e=>e.material && (e.material.transparent = true))
  605. loadDone(object)
  606. }
  607. viewer.loadModel({
  608. fileType: '3dTiles',
  609. id: prop.id,
  610. name : prop.title,
  611. /* tilesUrl: 'https://4dkk.4dage.com/scene_view_data/SS-Ds19qsmuFA/images/3dtiles/tileset.json',
  612. transform : {
  613. rotation : [Math.PI/2, 0, 0],
  614. position : [0,0,0]
  615. }
  616. tilesUrl: 'https://testgis.4dage.com/LVBADUI_qp/tileset.json',
  617. transform : {
  618. rotation : [0, 0, 0],
  619. position : [0,0,0]
  620. } */
  621. url:prop.url,
  622. },callback,onprogress)
  623. }else{
  624. //else if(prop.type == 'las' || prop.type == 'ply')
  625. Potree.loadPointCloudScene(prop.url, prop.type, prop.modelId, prop.title, (pointcloud)=>{
  626. pointcloud.matrixAutoUpdate = true
  627. pointcloud.initialPosition = pointcloud.position.clone()
  628. pointcloud.pos1MatrixInvert = new THREE.Matrix4().setPosition(pointcloud.initialPosition).invert()
  629. /* let maintainBtmZ = ()=>{
  630. MergeEditor.setModelBtmHeight(pointcloud)
  631. updateMatrix()
  632. }
  633. let updateMatrix = ()=>{
  634. setMatrix(pointcloud)
  635. pointcloud.dispatchEvent('transformChanged')
  636. }
  637. pointcloud.addEventListener('position_changed', updateMatrix )
  638. pointcloud.addEventListener("orientation_changed", maintainBtmZ )
  639. pointcloud.addEventListener("scale_changed", maintainBtmZ ) */
  640. loadDone(pointcloud)
  641. /* pointcloud.addEventListener('select',(e)=>{
  642. if(Potree.settings.displayMode == 'showPanos')return
  643. console.log('select',e)
  644. //viewer.setControls(viewer.orbitControls)
  645. MergeEditor.focusOnSelect(pointcloud)
  646. viewer.outlinePass.selectedObjects = [pointcloud]
  647. return {stopContinue:true}
  648. },{importance:1})
  649. pointcloud.addEventListener('deselect',(e)=>{
  650. console.log('deselect',e)
  651. //viewer.setControls(viewer.fpControls)
  652. viewer.outlinePass.selectedObjects = []
  653. }) */
  654. }, onError)
  655. }
  656. }
  657. return {THREE}
  658. }
  659. var changeLog = ()=>{
  660. var textarea = document.createElement('textarea');
  661. textarea.id = "consoleLog";
  662. textarea.style.width = '160px';
  663. textarea.style.height = '200px'
  664. textarea.style.position = 'fixed'
  665. textarea.style.left = 0
  666. textarea.style.bottom = '50px'
  667. textarea.style['z-index'] = 9999;
  668. textarea.style.color = 'black';
  669. textarea.style.opacity = 0.9;
  670. textarea.style['font-size'] = '12px';
  671. textarea.style['backgroundColor'] = '#ffffff'
  672. document.getElementsByTagName("body")[0].appendChild(textarea);
  673. var list = ["log", "error", "warn", "debug", "info", "time", "timeEnd"]
  674. var exchange = function (o) {
  675. console["old" + o] = console[o];
  676. console[o] = function () {
  677. var args = Array.from(arguments)
  678. console["old" + o].apply(this, arguments)
  679. var t = document.getElementById("consoleLog").innerHTML;
  680. var str = ''
  681. args.forEach(a=>{
  682. str += a + ' '
  683. })
  684. document.getElementById("consoleLog").innerHTML = str + "\n\n" + t;
  685. }
  686. }
  687. for (var i = 0; i < list.length; i++) {
  688. exchange(list[i])
  689. }
  690. }
  691. /*
  692. 坐标转换问题:
  693. 由于控制点可以随便输入,所以本地和地理位置的转换也是可拉伸的。而navvis的转换是等比由中心展开,
  694. 所以对比两种转化方式时误差较大。
  695. 另外地理注册控制点是有参考数据集的,若参考数据集和我放置在0,0,0的数据集一致,就可直接使用,否则要转换。
  696. ---------
  697. lonlat和空间坐标其实并非线性关系,因为lonlat其实是角度。当两个数据集在地球两端时,它们之间的夹角都相差180度了。
  698. 所以若要准确展示的话,需要将点云内所有物体,如漫游点,都先获取lonlat再去算local。或者直接将点云整体的transformMatrix考虑上在地球上相对于初始数据集的偏转。
  699. */