import * as THREE from "../../../../libs/three.js/build/three.module.js"; import cameraLight from '../../utils/cameraLight.js' import math from "../../utils/math.js" import Common from '../../utils/Common.js' import {LineDraw, MeshDraw} from "../../utils/DrawUtil.js"; import {transitions, easing, lerp} from '../../utils/transitions.js' import SplitScreen from "../../utils/SplitScreen.js"; import InfiniteGridHelper from '../../objects/InfiniteGridHelper.js' import Compass from "../../objects/tool/Compass.js"; import {TransformControls} from "../../objects/tool/TransformControls.js"; import History from "../../utils/History.js" const texLoader = new THREE.TextureLoader() texLoader.crossOrigin = "anonymous" const edgeStrengths = { pointcloud: 4, glb: 100 } const viewportProps = [{ left:0, bottom:0, width: 0.5,height:1, name : 'top', axis:["x","y"], direction : new THREE.Vector3(0,0,-1), //镜头朝向 active: true, //相机位置在z轴正向 limitBound: new THREE.Box3(new THREE.Vector3(-Infinity,-Infinity, 1),new THREE.Vector3(Infinity,Infinity,5000)), //在地面以上 margin:{x:50, y:150} , }, { left:0.5, bottom:0, width: 0.5,height:1, name : 'right', axis:["y","z"], direction : new THREE.Vector3(1,0,0), active: true, //相机位置在x轴负向 右下角屏 viewContainsPoints:[new THREE.Vector3(0,0,0)], margin:{x:300, y:250} , } ] let cylinderSkyGeo, oldSkyGeo let MergeEditor = { bus:new THREE.EventDispatcher(), SplitScreen : new SplitScreen(), init(){ { let ground = this.ground = new InfiniteGridHelper(1, 10000, new THREE.Color('#fff'), 10000, 0.2, 0.3) viewer.scene.scene.add(ground) //再加两条线否则在正侧边看不到 let line1 = LineDraw.createLine([new THREE.Vector3(-10000, 0, 0),new THREE.Vector3(10000, 0, 0) ], {color:'#666', }) let line2 = LineDraw.createLine([new THREE.Vector3(0, -10000, 0),new THREE.Vector3(0, 10000, 0) ], {mat:line1.material}) ground.renderOrder = Potree.config.renderOrders.model + 1//line1.renderOrder + 1 //要比模型低,否则模型透明时效果不对 ground.add(line1) ground.add(line2) ground.material.polygonOffset = true //多边形偏移(视觉上没有移动模型位置),防止闪烁 ground.material.polygonOffsetFactor = 100 //多边形偏移因子 ground.material.polygonOffsetUnits = 10 //多边形偏移单位 ground.material.depthWrite = false //ground.material.depthTest = false line1.material.polygonOffset = true line1.material.polygonOffsetFactor = 130 line1.material.polygonOffsetUnits = 10 line1.material.depthWrite = false //见笔记:透明物体的材质设置 } let oriEdgeStrength = viewer.outlinePass.edgeStrength { this.transformControls = new TransformControls(viewer.mainViewport.camera, viewer.renderArea,{ dontHideWhenFaceCamera: true, }); //this.transformControls.space = 'local'//为了在当前方向上平移 this.transformControls.setSize(1.5) viewer.scene.scene.add(this.transformControls) this.transformControls._gizmo.hideAxis = {rotate:['e']} this.transformControls.setRotateMethod(2) //右屏 this.transformControls2 = new TransformControls(viewer.mainViewport.camera, viewer.renderArea,{ dontHideWhenFaceCamera: true, }); this.transformControls.setSize(1.5) viewer.scene.scene.add(this.transformControls2) Potree.Utils.setObjectLayers(this.transformControls2, 'layer2' ) let mouseDown = (e)=>{ viewer.outlinePass.edgeStrength = 0//暂时消失线 } let mouseUp = (e)=>{ //this.updateEdgeStrength() viewer.outlinePass.edgeStrength = oriEdgeStrength } this.transformControls.addEventListener('mouseDown',mouseDown) this.transformControls2.addEventListener('mouseDown',mouseDown) this.transformControls.addEventListener('mouseUp',mouseUp) this.transformControls2.addEventListener('mouseUp',mouseUp) } { this.secondCompass = new Compass(null) } viewer.setControls(viewer.orbitControls) //viewer.mainViewport.view.fixZWhenPan = true viewer.orbitControls.constantlyForward = true viewer.addEventListener('global_single_click',(e)=>{ if( this.noNeedSelection //如模型查看页 || viewer.scene.cameraAnimations.some(c=>c.onUpdate) //正在播放 || e.drag && e.drag.notPressMouse //在加测量线 || viewer.mainViewport.view.isFlying() //有其他校准 || this.split //分屏中 || e.clickElement //触发别的点击事件,如测量时click marker /* && e.clickElement != e.intersect.object */ ){ return } if(e.intersect){ let object = e.intersect.object || e.intersect.pointcloud let objects = this.getAllObjects() if(objects.includes(object)){ this.selectModel(object) }else{ //if(!viewer.inputHandler.selection[0]){//正在平移和旋转,不允许取消 this.selectModel(null) //} } }else{ //if(!viewer.inputHandler.selection[0]){ this.selectModel(null) //} } }) viewer.inputHandler.addEventListener('keydown', (e)=>{ if((e.event.key).toLowerCase() == "h" ){ this.fadeOutlineAuto = !this.fadeOutlineAuto this.showModelOutline(this.selected,!!this.selected) } }) viewer.ssaaRenderPass.enabled = false viewer.outlinePass.enabled = true //Potree.settings.intersectWhenHover = false //Potree.Utils.updateVisible(viewer.reticule, 'force', false) viewer.mainViewport.camera.near = 0.05; // too small will result in z-fighting viewer.addEventListener('updateModelBound', (e)=>{ if(this.split){ this.SplitScreen.updateCameraOutOfModel(/* this.selected && [this.selected] */) } }) {//校准页面拖拽 //左右屏都可以拖拽模型,旋转只能左屏 let dragInfo let drag = (e)=>{ if(this.split && this.selected && this.transformState && (e.dragViewport.name == 'top' || this.transformState == 'translate') ){ if(e.type == 'global_mousedown' ){ //开始 //if((e.intersect.object || e.intersect.pointcloud) == this.selected){ if(e.intersect.pointclouds.includes(this.selected) || e.intersect.allElements.some(e=>e.object == this.selected)){ dragInfo = {} //if(this.selected.isPointcloud){ viewer.outlinePass.edgeStrength = 0//暂时消失线 //} } } if(e.type == 'global_drag' && dragInfo ){ if(this.transformState == 'translate'){ let moveVec = Potree.Utils.getOrthoCameraMoveVec(e.drag.pointerDelta, e.dragViewport.camera )//最近一次移动向量 this.selected.position.add(moveVec) this.selected.dispatchEvent("position_changed") }else if(this.transformState == 'rotate'){ let vec = new THREE.Vector3().subVectors(e.intersect.orthoIntersect || e.intersect.location, this.selected.boundCenter).setZ(0) if(dragInfo.lastVec == void 0){//global_mousedown dragInfo.lastVec = vec return } let angle = math.getAngle(dragInfo.lastVec, vec, 'z') dragInfo.lastVec = vec //this.selected.rotation.z += angle //局部 /* object.quaternion.copy( .setFromAxisAngle( new THREE.Vector3(0,0,1), angle ) ); object.quaternion.multiply( quaternionStart ).normalize(); */ let diffQua = new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3(0,0,1), angle ) this.selected.quaternion.premultiply(diffQua) //世界 this.selected.dispatchEvent("rotation_changed") } return {stopContinue:true} } } } viewer.addEventListener('global_mousedown', drag) viewer.addEventListener('global_drag', drag, 10) viewer.addEventListener('global_mousemove', (e)=>{ if(this.split && this.transformState && !e.drag && (e.hoverViewport.name == 'top' || this.transformState == 'translate')){ /* if(this.lastHoverViewport != e.hoverViewport){ this.lastHoverViewport = e.hoverViewport this.transformControls.view = e.hoverViewport.view this.transformControls.camera = e.hoverViewport.camera this.transformControls.hideAxis( this.transformState, e.hoverViewport.name == 'top' ? [z] : [x,y]); } */ let mouseover = e.intersect.pointclouds.includes(this.selected) || e.intersect.allElements.some(e=>e.object == this.selected) //let mouseover = (e.intersect.object || e.intersect.pointcloud) == this.selected if(mouseover){ if(this.transformState == 'translate'){ viewer.dispatchEvent({ type : "CursorChange", action : "add", name:"movePointcloud" }) }else{ viewer.dispatchEvent({ type : "CursorChange", action : "add", name:"rotatePointcloud" }) } }else{ this.clearTranCursor() } } }) viewer.addEventListener('global_drop', (e)=>{ dragInfo = null this.clearTranCursor() //this.updateEdgeStrength() viewer.outlinePass.edgeStrength = oriEdgeStrength }) } /* viewer.addEventListener('background_changed',()=>{ }) */ }, clearTranCursor(){ viewer.dispatchEvent({ type : "CursorChange", action : "remove", name:"movePointcloud" }) viewer.dispatchEvent({ type : "CursorChange", action : "remove", name:"rotatePointcloud" }) }, enterSplit(){ this.split = true if(this.selected) this.SplitScreen.focusCenter = this.selected.boundCenter //旋转中心。注意 boundCenter不能直接赋值,否则改变后focusCenter也要改 else this.SplitScreen.focusCenter = null this.SplitScreen.splitStart(viewportProps) this.beforeSplit = { pointDensity: Potree.settings.pointDensity, } Potree.settings.pointDensity = 'fourViewports' //强制降低点云质量 viewer.setControls(viewer.fpControls) let rightViewport = viewer.viewports.find(e=>e.name == 'right') let topViewport = viewer.viewports.find(e=>e.name == 'top') topViewport.alignment = true rightViewport.rotateSide = true rightViewport.skyboxFixPos = true rightViewport.skyboxMinZoom = 10 rightViewport.skyboxRenderFun = ()=>{// 使cube的一面永远正向镜头。 因侧视图的camera是ortho类型,需要平视mesh才不会拉伸 viewer.skybox.scene.children[0].rotation.copy(rightViewport.camera.rotation) } topViewport.skyboxRenderFun = ()=>{ viewer.skybox.scene.children[0].rotation.set(0,0,0) } viewer.viewports[1].layersAdd('layer2') viewer.viewports[0].layersAdd('layer1') Potree.Utils.setObjectLayers(this.transformControls, 'layer1' ) this.transformControls.view = viewer.viewports[0].view this.transformControls.camera = viewer.viewports[0].camera this.transformControls._gizmo.hideAxis = {translate:['z'], rotate:['x','y','z'] } this.transformControls2.view = viewer.viewports[1].view this.transformControls2.camera = viewer.viewports[1].camera this.transformControls2._gizmo.hideAxis = {translate:['x','y'], rotate:['x','y','z'] } this.secondCompass.changeViewport(viewer.viewports[0]) this.secondCompass.setDomPos() this.secondCompass.setDisplay(true) viewer.compass.changeViewport(viewer.viewports[1]) viewer.compass.setDomPos() //this.changeSkyboxGeo(true) }, leaveSplit(){ this.split = false this.SplitScreen.unSplit() viewer.setControls(viewer.orbitControls) Potree.settings.pointDensity = this.beforeSplit.pointDensity /* if(this.selected && this.selected.isPointcloud){ this.showModelOutline(this.selected, true) this.selected.material.activeAttributeName = "rgba" } */ this.transformControls.camera = viewer.viewports[0].camera this.transformControls.view = viewer.viewports[0].view this.transformControls._gizmo.hideAxis = {rotate:['e']} Potree.Utils.setObjectLayers(this.transformControls, 'sceneObjects' ) //恢复 viewer.compass.changeViewport(viewer.viewports[0]) //恢复 viewer.compass.setDomPos() this.secondCompass.setDisplay(false) }, rotateSideCamera(angle){ this.SplitScreen.rotateSideCamera(viewer.viewports.find(e=>e.name == 'right'), angle) }, setTransformState(state){//校准时 this.transformState = state this.clearTranCursor() }, //--------------------------- /* writeToHistory(content){ if(!this.prepareRecord)return; this.prepareRecord = false this.history.push(content) }, */ //--------------------------- getAllObjects(){ return viewer.objs.children.concat(viewer.scene.pointclouds) }, getModel(id){ let models = this.getAllObjects() return models.find(e=>e.dataset_id == id) }, removeModel(model){ if(this.selected == model) this.selectModel(null) let dispose = (e)=>{ e.geometry && e.geometry.dispose() e.material && e.material.dispose() } if(model.isPointcloud){ dispose(model) viewer.scene.removePointCloud(model) }else{ model.traverse(e=>{ dispose(e) }) viewer.objs.remove(model) } }, selectModel(model, state=true, fitBound, by2d){ if(!model) { model = this.selected state = false } if(state){ if(this.selected){ if(this.selected == model) return else{ let transToolAttached = !!this.transformControls.object this.selectModel(this.selected, false, fitBound, by2d) transToolAttached && this.transformControls.attach(model) } } this.selected = model MergeEditor.focusOn(model, 500, !!fitBound) //通过在场景里点击模型的话,不focus this.showModelOutline(model) //this.updateEdgeStrength() //console.log('selectModel', model) }else{ if(this.selected != model)return //model本来就没选中,不需要处理(防止2d先选中新的再取消旧的) this.showModelOutline(model, false) this.selected = null this.transformControls.detach() //viewer.transformObject(null); //console.log('selectModel', null) } if(!by2d && model){ model.dispatchEvent({type:'changeSelect', selected : state}) } }, showModelOutline(model, state){ if(this.fadeOutlineAuto){ if(state === false){ viewer.outlinePass.selectedObjects = [] clearTimeout(this.timer) return } viewer.outlinePass.selectedObjects = [model] if(this.timer){ clearTimeout(this.timer) } this.timer = setTimeout(()=>{ viewer.outlinePass.selectedObjects = [] }, 1000) }else{ if(state === false){ viewer.outlinePass.selectedObjects = [] }else{ viewer.outlinePass.selectedObjects = [model] } } }, /*updateEdgeStrength(){ if(!this.selected)return if(this.selected.isPointcloud){ viewer.outlinePass.edgeStrength = edgeStrengths.pointcloud// / this.selected.material.opacity }else{ viewer.outlinePass.edgeStrength = edgeStrengths.glb } },*/ focusOn(objects, duration = 400, fitBound=true, dontLookUp){ if(!(objects instanceof Array)){ objects = [objects] } let boundingBox = new THREE.Box3 objects.forEach(object=>{ boundingBox.union(object.boundingBox.clone().applyMatrix4(object.matrixWorld)) }) if(fitBound){ viewer.focusOnObject({boundingBox}, 'boundingBox', duration, {dontLookUp, dontChangeCamDir:true}) }else{ /* let position = viewer.inputHandler.intersect ? viewer.inputHandler.intersect.location : boundingBox.getCenter(new THREE.Vector3) position && viewer.focusOnObject({position}, 'point', duration, {dontChangePos: true}) */ let position = viewer.inputHandler.intersect ? viewer.inputHandler.intersect.location : boundingBox.getCenter(new THREE.Vector3) if(!position)return /* let targetOld = viewer.mainViewport.view.getPivot() let projected1 = targetOld.clone().project(viewer.mainViewport.camera); let projected2 = position.clone().project(viewer.mainViewport.camera); //使用其z let targetNew = projected1.clone().setZ(projected2.z).unproject(viewer.mainViewport.camera); viewer.mainViewport.view.lookAt(targetNew) */ viewer.mainViewport.view.radius = viewer.mainViewport.camera.position.distanceTo(position) //为了不改画面,不调节方向了,只能调调radius,一定程度将target靠近model } }, moveBoundCenterTo(model,pos){ //使boundCenter在所要的位置 let diff = new THREE.Vector3().subVectors(pos, model.boundCenter) model.position.add(diff); }, getBoundCenter(model){ if(!model.boundCenter) model.boundCenter = new THREE.Vector3 model.boundingBox.getCenter(model.boundCenter).applyMatrix4(model.matrixWorld) }, setModelBtmHeight(model, z ){ //无论模型怎么缩放、旋转,都使最低点为z if(z == void 0) z = model.btmHeight; //维持离地高度 else model.btmHeight = z; model.updateMatrixWorld() let boundingBox2 = model.boundingBox.clone().applyMatrix4(model.matrixWorld) let size = boundingBox2.getSize(new THREE.Vector3); let center = boundingBox2.getCenter(new THREE.Vector3); let hopeZ = z + size.z / 2 //model.position.z = z + size.z / 2 - center.z model.position.z += (hopeZ - center.z) }, computeBtmHeight(model){ //位移之后重新计算btmHeight model.updateMatrixWorld() let boundingBox2 = model.boundingBox.clone().applyMatrix4(model.matrixWorld) let size = boundingBox2.getSize(new THREE.Vector3); let center = boundingBox2.getCenter(new THREE.Vector3); model.btmHeight = center.z - size.z / 2 }, maintainBoundXY(model){ //在旋转和缩放后,立即执行这个函数,使boundCenter保持原位 model.updateMatrixWorld() let center1 = model.boundCenter.clone();//还未更新的 this.getBoundCenter(model)//更新 let center2 = model.boundCenter.clone(); let diff = new THREE.Vector2().subVectors(center1,center2); model.position.x += diff.x; model.position.y += diff.y; model.boundCenter.copy(center1) }, maintainBoundCenter(model){ model.updateMatrixWorld() let center1 = model.boundCenter.clone();//还未更新的 this.getBoundCenter(model)//更新 let center2 = model.boundCenter.clone(); let diff = new THREE.Vector3().subVectors(center1,center2); model.position.add(diff) model.boundCenter.copy(center1) }, modelTransformCallback(model){ model.updateMatrixWorld() if(model.matrixWorld.equals(model.lastMatrixWorld))return viewer.scene.measurements.forEach(measure=>{ let changed measure.points_datasets.forEach((dataset_id,i)=>{ if(dataset_id == model.dataset_id){ changed = true measure.points[i] = Potree.Utils.datasetPosTransform({fromDataset:true,datasetId:dataset_id, position:measure.dataset_points[i].clone()}) measure.updateMarker(measure.markers[i], measure.points[i]) } }) if(changed){//仿transformByPointcloud measure.getPoint2dInfo(measure.points) measure.update() measure.setSelected(false)//隐藏edgelabel } }) model.lastMatrixWorld = model.matrixWorld.clone() }, changeOpacity(model, opacity){ let isRoot = model.dataset_id != void 0 //是否是最外层 if(model.isPointcloud){ model.changePointOpacity(opacity) //MergeEditor.updateEdgeStrength() }else{ //model.traverse(e=>e.material && setOp(e, opacity)) model.traverse(mesh=>{ if(mesh.material){ mesh.material.opacity = opacity if(opacity<1){ mesh.material.transparent = true if(model.isPointcloud){ mesh.changePointOpacity(opacity) }else{ mesh.material.opacity = opacity } mesh.renderOrder = Potree.config.renderOrders.model+1 mesh.material.depthWrite = false }else{ mesh.material.transparent = false mesh.renderOrder = Potree.config.renderOrders.model mesh.material.depthWrite = true } } }) } isRoot && (model.opacity = opacity)//记录在最外层 } } export default MergeEditor /* note: 要注意getHoveredElements只在getIntersect时才使interactables包含加载的model, 也就是model上不能有使之成为interactables的事件,否则在鼠标hover到模型上开始转动的一瞬间很卡。 */