import * as THREE from "../../libs/three.js/build/three.module.js"; import math from './utils/math.js' import browser from './utils/browser.js' import './three.shim.js' import "./potree.shim.js" export function start(dom, mapDom, number ){ //t-Zvd3w0m /* { let obj = JSON.parse(localStorage.getItem('setting')) for(let i in obj){ console.log(i + ': ' + obj[i]) } } */ Potree.settings.number = number || 't-o5YMR13'// 't-iksBApb'// 写在viewer前 if(browser.urlHasValue('google'))Potree.settings.mapCompany = 'google' if(browser.urlHasValue('timing'))Potree.measureTimings = 1 let viewer = new Potree.Viewer(dom , mapDom); let Alignment = viewer.modules.Alignment viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); //viewer.loadSettingsFromURL(); if(!Potree.settings.isOfficial){ viewer.loadGUI(() => { viewer.setLanguage('en'); //$("#menu_appearance").next().show(); $("#menu_tools").next().show(); $("#menu_scene").next().show(); $("#siteModel").show(); //$("#alignment").show(); viewer.toggleSidebar(); }); Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 Potree.settings.rotAroundPoint = false if(/* Potree.settings.isTest && */ browser.isMobile()){ changeLog() } } Potree.loadDatasetsCallback = function(data, ifReload){ if(!data || data.length == 0)return console.error('getDataSet加载的数据为空') Potree.datasetData = data viewer.transform = null var datasetLength = data.length var pointcloudLoaded = 0 var panosLoaded = 0 var pointcloudLoadDone = function(){//点云cloud.js加载完毕后 } var panosLoadDone = function(){ viewer.images360.loadDone() viewer.scene.add360Images(viewer.images360); viewer.mapViewer.addListener(viewer.images360) viewer.updateModelBound() //需等pano加载完 let {boundSize, center} = viewer.bound if(!Potree.settings.isOfficial){ Potree.loadMapEntity('all') //加载floorplan } if(!ifReload){ viewer.dispatchEvent({type:'loadPointCloudDone'}) if(!Potree.settings.UserPointDensity){ Potree.settings.UserPointDensity = 'high'//'middle' } Potree.Log('loadPointCloudDone 点云加载完毕', {font:[null, 10]}) } {//初始位置 var panoId = browser.urlHasValue('pano',true); if(panoId !== ''){ var pos var pano = viewer.images360.panos.find(e=>e.id==panoId); if(pano){ viewer.images360.focusPano({ pano, duration:0, callback:()=>{/* Potree.settings.displayMode = 'showPanos' */} }) } }else{//考虑到多数据集距离很远,或者像隧道那种场景,要使视野范围内一定能看到点云,最好初始点设置在漫游点上 let {boundSize, center} = viewer.bound let pano = viewer.images360.findNearestPano(center) pano && viewer.images360.flyToPano({ pano, duration:0, target : viewer.images360.bound.center.setZ(pano.position.z) //平视中心区域(但也不能保证这个方向一定能看到点云密集区,如果在边缘的话) }) } } viewer.addVideo()//addFire() console.log('allLoaded') viewer.dispatchEvent('allLoaded') } var transformPointcloud = (pointcloud, dataset)=>{ var locationLonLat = dataset.location.slice(0,2) //当只有一个dataset时,无论如何transform 点云和漫游点都能对应上。 /* if(window.AMapWith84 && Potree.settings.mapCompany != 'google'){ locationLonLat = AMapWith84.wgs84ToAMap({x:locationLonLat[0], y:locationLonLat[1]}) locationLonLat = [locationLonLat.x,locationLonLat.y] } */ var location = viewer.transform.lonlatToLocal.forward(locationLonLat) //transform.inverse() //初始化位置 /* location[0] = Math.sign(location[0]) * Math.min(7000, Math.abs(location[0])) location[1] = Math.sign(location[1]) * Math.min(7000, Math.abs(location[1])) */ viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud) //dataset.orientation = 0 Alignment.rotate(pointcloud, null, dataset.orientation) Alignment.translate(pointcloud, new THREE.Vector3(location[0], location[1], dataset.location[2])) pointcloud.updateMatrixWorld() Potree.Log(`点云${pointcloud.dataset_id}(${pointcloud.name})旋转值:${pointcloud.orientationUser}, 位置${math.toPrecision(pointcloud.translateUser.toArray(),3)}, 经纬度 ${locationLonLat}, spacing ${pointcloud.material.spacing}`, {font:[null, 13]} ) //------------------- //viewer.mapView.showSources(false); } if(!Potree.settings.originDatasetId)Potree.settings.originDatasetId = data[0].id var originDataset = data.find(e=>e.id == Potree.settings.originDatasetId) {//拿初始数据集作为基准。它的位置是000 var locationLonLat = originDataset.location.slice(0,2) if(window.AMapWith84){//需要转换为高德的 locationLonLat = AMapWith84.wgs84ToAMap({x:locationLonLat[0], y:locationLonLat[1]}) locationLonLat = [locationLonLat.x,locationLonLat.y] } proj4.defs("LOCAL", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); //高德坐标系 proj4.defs("LOCAL_MAP", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); proj4.defs("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); let transform1 = proj4("WGS84", "LOCAL"); //这个ok 是展开的平面投影 LOCAL即NAVVIS:TMERC 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;"); //注:转入后再转出,和原来的有偏差。如果输入是local坐标,数字越大偏差越大,当百万时就明显了。如果是lonlat,很奇怪经度小于50时就乱了。 viewer.transform = { lonlatToLocal : transform1, lonlatTo4550 : transform2 // 转大地坐标EPSG:4550 } if(window.AMapWith84 && Potree.settings.mapCompany != 'google'){//需要转换, 因本地高德用的lonlat和数据里的84不一样. (google地图在国内也用的高德,国外84) let change = (transform)=>{ let forward = transform.forward let inverse = transform.inverse; transform.forward = function(e){ let needTran = e.x == void 0 if(needTran)var a1 = {x:e[0],y:e[1]} else var a1 = e var a = AMapWith84.wgs84ToAMap(a1) if(needTran){ a = [a.x, a.y]; e[2] != void 0 && (a[2] = e[2]) }else{ e.z != void 0 && (a.z = e.z) } return forward(a) } transform.inverse = function(e){ let needTran = e.x == void 0 var a = inverse(e) needTran && (a = {x:a[0],y:a[1]}) a = AMapWith84.aMapToWgs84(a) if(needTran){ a = [a.x,a.y]; e[2] != void 0 && (a[2] = e[2]) }else{ e.z != void 0 && (a.z = e.z) } return a } } for(let f in viewer.transform){ change(viewer.transform[f]) } } viewer.mapViewer && viewer.mapViewer.mapLayer.maps[0].updateProjection() } data.forEach((dataset,index)=>{ if(!ifReload){ var datasetCode = dataset.sceneCode || dataset.name //对应4dkk的场景码 //var cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${datasetCode}/data/${datasetCode}/webcloud/cloud.js` var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 var timeStamp = dataset.updateTime ? dataset.updateTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随updateTime更新一次 //console.warn(dataset.name, 'timeStamp', timeStamp) Potree.loadPointCloud(cloudPath, dataset.name ,datasetCode, timeStamp, e => { let scene = viewer.scene; let pointcloud = e.pointcloud; let config = Potree.config.material let material = pointcloud.material; pointcloud.datasetData = dataset pointcloud.dataset_id = dataset.id;//供漫游点找到属于的dataset点云 pointcloud.hasDepthTex = Potree.settings.useDepthTex && (!!dataset.has_depth || Potree.settings.isLocalhost && Potree.settings.number == 'SS-t-7DUfWAUZ3V') //test material.minSize = config.minSize material.maxSize = config.maxSize material.pointSizeType =/* Potree.settings.isOfficial ? */ config.pointSizeType /* : 'ADAPTIVE' */ //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize(config.realPointSize) //material.size = config.pointSize; pointcloud.changePointOpacity(1) material.shape = Potree.PointShape.SQUARE; pointcloud.color = pointcloud.material.color = dataset.color pointcloud.timeStamp = timeStamp transformPointcloud(pointcloud,dataset) scene.addPointCloud(pointcloud); if(!Potree.settings.isOfficial){ Potree.settings.floorplanEnables[dataset.id] = true Potree.settings.floorplanType[dataset.id] = 'default' } pointcloudLoaded ++; if(pointcloudLoaded == datasetLength)pointcloudLoadDone() Potree.loadPanos(dataset.id, (data) => { //console.log('loadPanos',dataset.sceneCode, dataset.id, data) viewer.images360.addPanoData(data, dataset.id ) panosLoaded ++; if(panosLoaded == datasetLength){ panosLoadDone() } }) }) }else{ let pointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == dataset.id) if(!pointcloud){ Potree.Log('数据集id变了,自动使用第一个', {font:['#500' ]} ) pointcloud = viewer.scene.pointclouds[0] } //先归零 Alignment.translate(pointcloud, pointcloud.translateUser.clone().negate()) Alignment.rotate(pointcloud, null, - pointcloud.orientationUser) transformPointcloud(pointcloud, dataset) } }) if(ifReload){ //loadDone() } } number && Potree.loadDatasets(Potree.loadDatasetsCallback) window.testTransform = function(locationLonLat, location1, location2){ proj4.defs("NAVVIS:test", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); let transform = proj4("WGS84", "NAVVIS:test"); //这个ok navvis里也是这两种转换 见proj4Factory if(location1){//经纬度 return transform.forward(location1) }else{ return transform.inverse(location2) } } window.THREE = THREE window.buttonFunction = function(){ viewer.scene.pointclouds.forEach(e=>e.predictNodeMaxLevel()) /* viewer.startScreenshot({type:'measure', measurement:viewer.scene.measurements[0]}) viewer.modules.RouteGuider.routeStart = new THREE.Vector3(0,0,-1.3) viewer.modules.RouteGuider.routeEnd = new THREE.Vector3(-10,0,-1.3) */ } if(Potree.settings.isLocalhost){ let before = {} viewer.inputHandler.addEventListener('keydown',e=>{ //测试的代码 if(e.event.key == 't'){ viewer.images360.cube.visible = true viewer.images360.cube.material.wireframe = true }else if(e.event.key == 'y'){ viewer.images360.cube.material.wireframe = false viewer.images360.cube.visible = Potree.settings.displayMode == 'showPanos' } }) } //-------------------------------- if(!number){ Potree.settings.boundAddObjs = true Potree.settings.intersectOnObjs = true // Load untextured bunny from ply viewer.loadModel({ fileType:'ply', url:Potree.resourcePath + "/models/indoor.ply", name:'test', }, (object)=>{ object.isModel = true viewer.updateModelBound() } ) } } //======================================================================= /* 漫游点编辑 */ //======================================================================= export function panoEditStart(dom, number, fileServer){ Potree.settings.editType = 'pano' Potree.settings.number = number Potree.settings.unableNavigate = true Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 let viewer = new Potree.Viewer(dom); let Alignment = viewer.modules.Alignment viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); //viewer.loadSettingsFromURL(); let datasetLoaded = 0; if(!Potree.settings.isOfficial){ viewer.loadGUI(() => { viewer.setLanguage('en'); $("#menu_tools").next().show(); $("#panos").show(); $("#alignment").show(); viewer.toggleSidebar(); }); Potree.settings.sizeFitToLevel = true } var pointcloudLoadDone = function( ){//所有点云cloud.js加载完毕后 viewer.scene.pointclouds.forEach(c=>{ transformPointcloud(c) }) viewer.images360.loadDone() viewer.scene.add360Images(viewer.images360); viewer.updateModelBound() let {boundSize, center} = viewer.bound Potree.Log(`中心点: ${math.toPrecision(center.toArray(),2)}, boundSize: ${math.toPrecision(boundSize.toArray(),2)} ` , {font:[null, 12]} ) viewer.scene.view.setView({ position: center.clone().add(new THREE.Vector3(10,5,10)), target: center }) viewer.dispatchEvent({type:'loadPointCloudDone'}) if(!Potree.settings.UserPointDensity){ Potree.settings.UserPointDensity = 'panoEdit'//'middle' } Potree.Log('loadPointCloudDone 点云加载完毕',{font:[null, 10]}) viewer.dispatchEvent('allLoaded'); } var transformPointcloud = (pointcloud )=>{ //初始化位置 viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud) let orientation = pointcloud.panos[0].dataRotation.z + Math.PI let location = pointcloud.panos[0].dataPosition.clone()//.negate() Alignment.rotate(pointcloud, null, orientation ) Alignment.translate(pointcloud, location ) pointcloud.updateMatrixWorld() } let loadPanosDone = Potree.loadPanosDone = (datasetId, panoData )=>{ //一个数据集获取到它的panos后 Potree.settings.datasetsPanos[datasetId] = {panoData, panos:[]} console.log('panoData', datasetId, panoData) let panoCount = panoData.length let pointcloudLoaded = 0 let datasetsCount = Object.keys(Potree.settings.datasetsPanos).length panoData.forEach((pano, index)=>{ //let cloudPath = `${Potree.scriptPath}/data/panoEdit/uuidcloud/${pano.uuid}/cloud.js` let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${Potree.settings.number}/data/bundle_${Potree.settings.number}/building/uuidcloud/${pano.uuid}/cloud.js` let name = datasetId + '-'+pano.uuid let timeStamp = 0 pano.index = index //注意:index不等于uuid,因为有的uuid缺失。但是visibles中存的是下标! Potree.loadPointCloud(cloudPath, name , name, timeStamp, e => { //开始加载点云 let scene = viewer.scene; let pointcloud = e.pointcloud; let config = Potree.config.material let material = pointcloud.material; material.minSize = config.minSize material.maxSize = config.maxSize material.pointSizeType = /* 'ADAPTIVE'// */config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize( 0.1 /* config.realPointSize */ ) //material.size = config.pointSize; pointcloud.changePointOpacity(1) material.shape = Potree.PointShape.SQUARE; pointcloud.color = config.pointColor pointcloud.dataset_id = datasetId; //多个点云指向一个datasetId pointcloud.panoUuid = pano.uuid pointcloud.timeStamp = timeStamp //transformPointcloud(pointcloud, pano) scene.addPointCloud(pointcloud); pointcloudLoaded ++; if(pointcloudLoaded == panoCount ){ datasetLoaded ++ viewer.images360.addPanoData(panoData , datasetId ) if(datasetLoaded == datasetsCount){ pointcloudLoadDone() } } }) }) } if(!Potree.settings.isOfficial){ Potree.settings.datasetsPano = {'testDataset':null} Potree.loadPanosInfo( data=>{loadPanosDone('testDataset', data.sweepLocations)} ) } } export function mergeEditStart(dom){ Potree.settings.editType = 'merge' Potree.settings.intersectOnObjs = true Potree.settings.boundAddObjs = true Potree.settings.unableNavigate = true let viewer = new Potree.Viewer(dom ); let Alignment = viewer.modules.Alignment viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); //viewer.loadSettingsFromURL(); { viewer.mainViewport.view.position.set(30,30,30) viewer.mainViewport.view.lookAt(0,0,0) viewer.updateModelBound()//init //this.bound = new THREE.Box3(new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,1,1)) viewer.transformationTool.setModeEnable(['translation','rotation'] ) viewer.ssaaRenderPass.sampleLevel = 1 // sampleLevel为1 的话,ground就不会变黑 viewer.inputHandler.fixSelection = true //不通过点击屏幕而切换transfrom选中状态 } Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 Potree.loadPointCloudScene = function(url, type, id, title, done, onError){//对应4dkk的场景码 let loadCloud = ({cloudPath, sceneName, sceneCode, timeStamp, color } )=>{ Potree.loadPointCloud(cloudPath, sceneName , sceneCode, timeStamp, e => { let scene = viewer.scene; let pointcloud = e.pointcloud; let config = Potree.config.material let material = pointcloud.material; material.minSize = config.minSize material.maxSize = config.maxSize material.pointSizeType = config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize(config.realPointSize) //material.size = config.pointSize; pointcloud.changePointOpacity(1) material.shape = Potree.PointShape.SQUARE; color && (pointcloud.color = pointcloud.material.color = color) pointcloud.timeStamp = timeStamp //transformPointcloud(pointcloud, originDataset) scene.addPointCloud(pointcloud); { viewer.updateModelBound() let {boundSize, center} = viewer.bound viewer.dispatchEvent({type:'loadPointCloudDone'}) if(!Potree.settings.UserPointDensity){ Potree.settings.UserPointDensity = 'high'//'middle' } Potree.Log('loadPointCloudDone 点云加载完毕', {font:[null,10] }) } /* Potree.loadPanos(dataset.id, (data) => { //暂时不加载panos了,因为没有id //console.log('loadPanos',dataset.sceneCode, dataset.id, data) viewer.images360.addPanoData(data, dataset.id ) viewer.images360.loadDone() viewer.scene.add360Images(viewer.images360); */ viewer.dispatchEvent('allLoaded') done(pointcloud) },onError) } if(type == 'laser'){ let sceneCode = url Potree.loadDatasets((data)=>{ let originDataset = data.find(e=>e.sceneCode == sceneCode);//只加载初始数据集 let timeStamp = originDataset.updateTime ? originDataset.updateTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随updateTime更新一次 //let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${sceneCode}/data/${sceneCode}/webcloud/cloud.js` let cloudPath = `${Potree.settings.urls.prefix1}/${originDataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 loadCloud({cloudPath, sceneName:originDataset.sceneName, sceneCode, timeStamp, color:originDataset.color}) }, sceneCode, onError) }else{//las or ply 直接用url let name = type + '|' + id + '|' + title if(url instanceof Array){ if(url.length == 1){ url = url[0] }else{ console.error('有多个点云?暂时还不支持', url, name)//多个点云要一起移动没想好怎么写 } } let cloudPath = url + '/cloud.js' loadCloud({cloudPath, sceneName:name, sceneCode:name, timeStamp:'' }) } } let setMatrix = (pointcloud)=>{//为了漫游点变换,要算一下 类似setMatrix /* pointcloud.transformMatrix = new THREE.Matrix4().multiplyMatrices(pointcloud.matrix, pointcloud.pos1MatrixInvert)//还原一点位移 pointcloud.transformInvMatrix.copy(pointcloud.transformMatrix).invert() pointcloud.rotateMatrix = new THREE.Matrix4().makeRotationFromEuler(pointcloud.rotation); pointcloud.rotateInvMatrix.copy(pointcloud.rotateMatrix).invert() pointcloud.panos.forEach(e=>e.transformByPointcloud()) */ //pointcloud.updateBound() //pointcloud.getPanosBound() viewer.updateModelBound() } let moveModel = (e)=>{//根据鼠标移动的位置改变位置 let camera = viewer.mainViewport.camera var origin = new THREE.Vector3(e.pointer.x, e.pointer.y, -1).unproject(camera), end = new THREE.Vector3(e.pointer.x, e.pointer.y, 1).unproject(camera) var dir = end.sub(origin) let planeZ = 0; let r = (planeZ - origin.z)/dir.z let x = r * dir.x + origin.x let y = r * dir.y + origin.y //过后改为根据intersect的点来设置底部高度;这样的话,需要发送高度 /*let pos = new THREE.Vector3(x,y, planeZ ) modelEditing.updateMatrixWorld() let boundCenter = modelEditing.boundingBox.getCenter(new THREE.Vector3).applyMatrix4(modelEditing.matrixWorld); */ MergeEditor.moveBoundCenterTo(modelEditing,new THREE.Vector3(x,y, modelEditing.boundCenter.z)) //使模型中心的xy在鼠标所在位置 modelEditing.dispatchEvent("position_changed") } let cancelMove = ()=>{ modelEditing = null viewer.removeEventListener('global_mousemove', moveModel); viewer.removeEventListener('global_click', confirmPos); } let confirmPos = ()=>{ MergeEditor.focusOn(modelEditing) cancelMove() return {stopContinue:true} } let modelType, modelEditing, MergeEditor = viewer.modules.MergeEditor Potree.addModel = function(prop, done, onProgress, onError){ //加载模型 let loadDone = (model)=>{ model.dataset_id = prop.id //唯一标识 if(prop.position){ model.position.copy(prop.position) } if(prop.rotation){ //model.rotation.setFromVector3(prop.rotation) model.rotation.copy(prop.rotation) } if(prop.scale){ model.scale.set(prop.scale,prop.scale,prop.scale) } if(model.isPointcloud){ model.renderOrder = Potree.config.renderOrders.model; //same as glb } if(Potree.settings.maintainBtmZ) {//transform --------维持离地高度和中心点的版本(local ver) let updateBound = ()=>{ model.updateMatrixWorld() viewer.updateModelBound() } let maintainBtmZAndCenter = ()=>{ MergeEditor.maintainBoundXY(model) MergeEditor.setModelBtmHeight(model) updateBound() model.dispatchEvent('transformChanged') } model.addEventListener('position_changed', ()=>{ updateBound() MergeEditor.getBoundCenter(model);//更新boundcenter MergeEditor.computeBtmHeight(model) if(prop.bottomRange && (model.btmHeight > prop.bottomRange.max || model.btmHeight < prop.bottomRange.min)){ model.btmHeight = THREE.Math.clamp(model.btmHeight, prop.bottomRange.min, prop.bottomRange.max) MergeEditor.setModelBtmHeight(model) updateBound() } model.dispatchEvent('transformChanged') }) model.addEventListener("rotation_changed", maintainBtmZAndCenter ) model.addEventListener("scale_changed", maintainBtmZAndCenter ) model.addEventListener('transformChanged', ()=>{ MergeEditor.modelTransformCallback(model) }) //离地高度只是boundingbox在transform后的最低点的高度,而非模型transform后的最低点的高度,所以旋转过后看起来不太准确 } else {//transform --------维持中心点的版本 let updateBound = ()=>{ model.updateMatrixWorld() viewer.updateModelBound() } let maintainCenter = ()=>{ //MergeEditor.maintainBoundXY(model) MergeEditor.maintainBoundCenter(model) updateBound() model.dispatchEvent('transformChanged') } model.addEventListener('position_changed', ()=>{ updateBound() MergeEditor.getBoundCenter(model);//更新boundcenter model.dispatchEvent('transformChanged') }) model.addEventListener("rotation_changed", maintainCenter ) model.addEventListener("scale_changed", maintainCenter ) model.addEventListener('transformChanged', ()=>{ MergeEditor.modelTransformCallback(model) }) } model.updateMatrixWorld() viewer.updateModelBound() MergeEditor.getBoundCenter(model) //初始化 model.lastMatrixWorld = model.matrixWorld.clone() done(model) // 先发送成功,因为2d界面会随机执行changePosition等初始化,然后这边再将模型移到中心地面上 if(prop.isFirstLoad){ MergeEditor.moveBoundCenterTo(model, new THREE.Vector3(0,0,0)) MergeEditor.setModelBtmHeight(model, 0) //初始加载设置离地高度为0 if(prop.mode != 'single'){//如果不是模型展示页,模型会随着鼠标位置移动 modelEditing = model; /* if(model.fileType == '3dTiles'){ setTimeout(()=>{ moveModel({pointer:{x:0,y:0}}) //3dTiles的移动会错乱,先默认放在当前视图中间吧 confirmPos() },1) }else{ */ viewer.addEventListener('global_mousemove', moveModel); viewer.addEventListener('global_click', confirmPos, {importance:3}); //} } model.dispatchEvent("position_changed") }else{ //MergeEditor.setModelBtmHeight(model, prop.bottom || 0) //默认离地高度为0 modelEditing = null } MergeEditor.modelAdded(model) } if(prop.type == 'glb'){ let callback = (object)=>{ //focusOnSelect(object, 1000) object.isModel = true //object.dataset_id = Date.now() //暂时 object.traverse(e=>e.material && (e.material.transparent = true)) /* object.addEventListener('click',(e)=>{ //只是为了能得到hoverElement识别才加这个侦听 }) */ loadDone(object) } let info = { fileType: prop.type, id: prop.id, unlit:true, url : prop.url, name : prop.title, /* transform : { position : prop.position, rotation : new THREE.Euler().setFromVector3(prop.rotation), scale: new THREE.Vector3(prop.scale,prop.scale,prop.scale), } */ } viewer.loadModel(info , callback, onProgress, onError) }else if(prop.type == 'obsg' || prop.type == 'b3dm'){ //3d tiles let callback = (object)=>{ object.isModel = true //透明度怎么办 //object.traverse(e=>e.material && (e.material.transparent = true)) loadDone(object) } viewer.loadModel({ fileType: '3dTiles', id: prop.id, name : prop.title, /* tilesUrl: 'https://4dkk.4dage.com/scene_view_data/SS-Ds19qsmuFA/images/3dtiles/tileset.json', transform : { rotation : [Math.PI/2, 0, 0], position : [0,0,0] } tilesUrl: 'https://testgis.4dage.com/LVBADUI_qp/tileset.json', transform : { rotation : [0, 0, 0], position : [0,0,0] } */ url:prop.url, },callback,onprogress) }else{ //else if(prop.type == 'las' || prop.type == 'ply') Potree.loadPointCloudScene(prop.url, prop.type, prop.modelId, prop.title, (pointcloud)=>{ pointcloud.matrixAutoUpdate = true pointcloud.initialPosition = pointcloud.position.clone() pointcloud.pos1MatrixInvert = new THREE.Matrix4().setPosition(pointcloud.initialPosition).invert() /* let maintainBtmZ = ()=>{ MergeEditor.setModelBtmHeight(pointcloud) updateMatrix() } let updateMatrix = ()=>{ setMatrix(pointcloud) pointcloud.dispatchEvent('transformChanged') } pointcloud.addEventListener('position_changed', updateMatrix ) pointcloud.addEventListener("orientation_changed", maintainBtmZ ) pointcloud.addEventListener("scale_changed", maintainBtmZ ) */ loadDone(pointcloud) /* pointcloud.addEventListener('select',(e)=>{ if(Potree.settings.displayMode == 'showPanos')return console.log('select',e) //viewer.setControls(viewer.orbitControls) MergeEditor.focusOnSelect(pointcloud) viewer.outlinePass.selectedObjects = [pointcloud] return {stopContinue:true} },{importance:1}) pointcloud.addEventListener('deselect',(e)=>{ console.log('deselect',e) //viewer.setControls(viewer.fpControls) viewer.outlinePass.selectedObjects = [] }) */ }, onError) } } return {THREE} } var changeLog = ()=>{ var textarea = document.createElement('textarea'); textarea.id = "consoleLog"; textarea.style.width = '160px'; textarea.style.height = '200px' textarea.style.position = 'fixed' textarea.style.left = 0 textarea.style.bottom = '50px' textarea.style['z-index'] = 9999; textarea.style.color = 'black'; textarea.style.opacity = 0.9; textarea.style['font-size'] = '12px'; textarea.style['backgroundColor'] = '#ffffff' document.getElementsByTagName("body")[0].appendChild(textarea); var list = ["log", "error", "warn", "debug", "info", "time", "timeEnd"] var exchange = function (o) { console["old" + o] = console[o]; console[o] = function () { var args = Array.from(arguments) console["old" + o].apply(this, arguments) var t = document.getElementById("consoleLog").innerHTML; var str = '' args.forEach(a=>{ str += a + ' ' }) document.getElementById("consoleLog").innerHTML = str + "\n\n" + t; } } for (var i = 0; i < list.length; i++) { exchange(list[i]) } } /* 坐标转换问题: 由于控制点可以随便输入,所以本地和地理位置的转换也是可拉伸的。而navvis的转换是等比由中心展开, 所以对比两种转化方式时误差较大。 另外地理注册控制点是有参考数据集的,若参考数据集和我放置在0,0,0的数据集一致,就可直接使用,否则要转换。 --------- lonlat和空间坐标其实并非线性关系,因为lonlat其实是角度。当两个数据集在地球两端时,它们之间的夹角都相差180度了。 所以若要准确展示的话,需要将点云内所有物体,如漫游点,都先获取lonlat再去算local。或者直接将点云整体的transformMatrix考虑上在地球上相对于初始数据集的偏转。 */