123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
-
- 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"
- import {ExtendEventDispatcher} from "../../custom/ExtendEventDispatcher.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 MergeEditor = {
- bus:new ExtendEventDispatcher(),
-
-
- 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', dontAlwaysSeen:true})
- 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
- //见笔记:透明物体的材质设置
- }
-
-
- {
-
- 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.transformControls2 = new TransformControls(viewer.mainViewport.camera, viewer.renderArea,{
- dontHideWhenFaceCamera: true,
- });
- this.transformControls.setSize(1.5)
- viewer.scene.scene.add(this.transformControls2)
- viewer.setObjectLayers(this.transformControls2, 'layer2' )
-
- let mouseDown = (e)=>{
-
- viewer.outlinePass.edgeStrength = 0//暂时消失线
-
- }
- let mouseUp = (e)=>{
-
- this.updateEdgeStrength()
-
- }
- 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
- //viewer.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()
-
-
- })
-
- }
- },
-
-
- 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.scene.pointclouds.forEach(e=>{
- e.material.activeAttributeName = "color"
- e.material.useFilterByNormal = true
- }) */
- //取消outline,改点云颜色为和outline一样的颜色
- /* if(this.selected && this.selected.isPointcloud){
- this.showModelOutline(this.selected, false)
- this.selected.material.activeAttributeName = "color"
- this.selected.material.color = viewer.outlinePass.visibleEdgeColor
- } */
-
-
- viewer.setControls(viewer.fpControls)
- viewer.viewports.find(e=>e.name == 'right').rotateSide = true
- viewer.viewports.find(e=>e.name == 'top').alignment = true
-
-
- viewer.viewports[1].layersAdd('layer2')
- viewer.viewports[0].layersAdd('layer1')
- viewer.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()
-
- },
-
- 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 = {}
- viewer.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()
- }
- }
-
-
-
-
-
-
- export default MergeEditor
-
-
- /*
- note:
- 要注意getHoveredElements只在getIntersect时才使interactables包含加载的model, 也就是model上不能有使之成为interactables的事件,否则在鼠标hover到模型上开始转动的一瞬间很卡。
- */
|