xzw 2 年之前
父节点
当前提交
67e315458d
共有 3 个文件被更改,包括 422 次插入0 次删除
  1. 376 0
      src/custom/modules/clipping/Clipping.js
  2. 31 0
      src/materials/shaders/skybox.fs
  3. 15 0
      src/materials/shaders/skybox.vs

+ 376 - 0
src/custom/modules/clipping/Clipping.js

@@ -0,0 +1,376 @@
+
+
+
+
+import * as THREE from "../../../../libs/three.js/build/three.module.js";
+ 
+import math from "../../utils/math.js"
+import Common from '../../utils/Common.js'
+import {LineDraw, MeshDraw} from "../../utils/DrawUtil.js";
+import {ExtendView} from "../../../viewer/ExtendView.js";
+import Viewport from "../../viewer/Viewport.js";
+import Sprite from "../../objects/Sprite.js";
+import {transitions, easing, lerp} from '../../utils/transitions.js'
+import {TransformControls} from "../../objects/tool/TransformControls.js";
+import SplitScreen from "../../utils/SplitScreen.js"
+//import History from "../../utils/History.js"
+
+const cameraProps = [ 
+    { 
+        name : 'top',    
+        axis:["x","y"],  
+        direction : new THREE.Vector3(0,0,-1), //镜头朝向
+        openCount:0,
+    } 
+]   
+
+
+
+export class Clipping extends THREE.EventDispatcher{ //实时剪裁
+    
+    constructor(){
+        super()
+        this.views = {}
+        this.cameras = {}
+        this.orthoCamera = new THREE.OrthographicCamera(-100, 100, 100, 100, 0.01, 10000)
+        this.orthoCamera.up.set(0,0,1)
+        
+    }
+    
+    init(){
+        if(this.inited)return
+        this.initViews()
+        this.inited = true
+        this.prepareRecord = true
+        this.activeViewName = 'mainView'
+         
+        this.events = {
+            transfromCallback:(e)=>{//拖拽变化时
+                this.adjustCamHeight()
+                //检测漫游点、回退等
+                /* if(this.prepareRecord){
+                    let box = viewer.transformationTool.selection[0]
+                    this.history.writeIn({box, matrix:box.matrix.clone()})
+                    this.prepareRecord = false
+                } */
+            },
+            /* onTransfromEnd:(e)=>{//拖拽结束、松开
+                this.prepareRecord = true
+            }, */ 
+            selectCallback:(e)=>{
+                this.adjustCamHeight()
+                 
+                let unableNavigate = this.activeViewName != 'mainView' ||  e.selection.length > 0
+                if(Potree.settings.unableNavigate && !unableNavigate){
+                    setTimeout(()=>{
+                        Potree.settings.unableNavigate = this.activeViewName != 'mainView' ||  e.selection.length > 0
+                    },300)//延迟是因为点击时取消选择后可能立即就会触发flyToPano。 而且有的人喜欢点两下
+                }else Potree.settings.unableNavigate = unableNavigate
+                
+                             
+            },
+            onkeydown:(e)=>{ 
+                if(e.keyCode == 8 || e.keyCode == 46){// Backspace or Delete
+                    viewer.inputHandler.selection[0] && viewer.scene.removeVolume(viewer.inputHandler.selection[0]); 
+                } 
+            }  
+             
+        }
+        
+        /* this.history = new History({ //也可以写到全局,但需要加个判断物品是否存在的函数
+            applyData: (data)=>{  
+                if(viewer.scene.volumes.includes(data.box)){
+                    data.matrix.decompose( data.box.position, data.box.quaternion, data.box.scale );
+                }else{
+                    this.history.undo()//找不到就回退下一个。(直接写这?)
+                }
+            } 
+        }) */
+    }
+    initViews(){
+        
+        this.splitScreenTool = new SplitScreen
+        
+         
+        
+        for(let i=0;i<1;i++){
+            let prop = cameraProps[i];
+            let view = new ExtendView()  
+            this.views[prop.name] = view 
+            this.cameras[prop.name] = this.orthoCamera
+             
+            view.direction = prop.direction
+        }
+        this.views.mainView = viewer.mainViewport.view
+        this.cameras.mainView = viewer.mainViewport.camera
+         
+        
+    } 
+    
+    switchView(name){//替换view和camera到mainViewport
+        if(this.activeViewName == name)return
+    
+        let view = this.views[name]
+        let camera = this.cameras[name]
+        let prop = cameraProps.find(e=>e.name == name)
+         
+        let {boundSize, center, boundingBox} = viewer.bound
+        this.lastViewName = this.activeViewName
+        this.activeViewName = name 
+        let lastView = this.views[this.lastViewName]
+        let lastCamera = this.cameras[this.lastViewName]
+        viewer.mainViewport.view = view
+        viewer.mainViewport.camera = camera 
+        if(lastCamera)lastView.zoom = lastCamera.zoom
+         
+        
+        
+        /* if(lastView){//2d->3d 
+            view.copy(lastView)
+        } */
+        if(name == 'mainView'){ 
+            Potree.settings.unableNavigate = false
+            /* viewer.transformationTool.handles['scale.z+'].node.visible = true
+            viewer.transformationTool.handles['scale.z-'].node.visible = true */
+        }else{
+            Potree.settings.unableNavigate = true
+            /* viewer.transformationTool.handles['scale.z+'].node.visible = false
+            viewer.transformationTool.handles['scale.z-'].node.visible = false */
+            if(prop.openCount == 0){//至多执行一次
+                //this.viewportFitBound(name,  boundSize, center)
+                this.orthoMoveFit(center, {bound:boundingBox},  0)
+                this.camHeightOutOfModel = view.position.z  //记录下此刻相机高度。
+            } 
+            prop.openCount++
+            
+            this.adjustCamHeight()
+            
+            /* this.targetPlane.setFromNormalAndCoplanarPoint( view.direction.clone(), center )  
+            this.targetPlane.projectPoint(view.position, this.shiftTarget )  //target转换到过模型中心的平面,以保证镜头一定在模型外
+            view.position.copy(this.splitScreenTool.getPosOutOfModel(viewer.mainViewport)) */
+            
+            if(view.zoom)camera.zoom = view.zoom//恢复上次的zoom
+            
+            
+        }
+        viewer.updateScreenSize({forceUpdateSize:true})//更新camera aspect  left等
+        if(viewer.inputHandler.selection.length){
+            this.focusOnObject(viewer.inputHandler.selection[0])
+        }
+             
+    }
+    
+    
+    
+    focusOnObject(box, duration=0){
+        if(this.activeViewName == 'mainView'){
+            viewer.focusOnObject({boundingBox:box.boundingBox.clone().applyMatrix4(box.matrixWorld)}, 'boundingBox', duration)
+        }else{
+            this.orthoMoveFit(box.position, {bound:box.boundingBox.clone().applyMatrix4(box.matrixWorld)},  duration) 
+        }
+        this.adjustCamHeight()
+    }
+    
+    orthoMoveFit(pos, info, duration){  
+        var margin = {x:viewer.mainViewport.resolution.x*0.4, y:viewer.mainViewport.resolution.y*0.4}      
+        this.splitScreenTool.viewportFitBound(viewer.mainViewport,  info.bound,  pos, duration, margin ) 
+    }
+    
+    adjustCamHeight(){
+        if(this.activeViewName != 'top')return
+        let view = this.views.top
+        let height 
+        
+        if(viewer.inputHandler.selection.length){ //相机高度位于选中的box的顶部
+            let box = viewer.inputHandler.selection[0]
+            height = box.boundingBox.clone().applyMatrix4(box.matrixWorld).max.z;
+            
+        }else{
+            height = this.camHeightOutOfModel   //显示全部点云
+        }
+        view.position.z = height
+        //console.log('adjustCamHeight',height)
+        
+        //缺点:1 会导致缩放很小的时候,transformationTool的轴因放大到了相机背面。(只有scale轴做了处理)
+        //2 无法直接切换 看不到的box,但可以先取消选择
+        //3 但是俯视图中无法切换到被上层盖住的box(不过把俯视图作为辅助,只针对单个box调动的话,问题不大)
+    }
+    
+    enter(){
+        this.init()
+        viewer.transformationTool.setModeEnable(['translation']) 
+        //viewer.transformationTool.handles['rotation.x'].node.visible = false
+        viewer.transformationTool.frame.material.visible = false //不盖住boxVolume的frame
+        this.targetPlane = viewer.mainViewport.targetPlane = new THREE.Plane()
+        this.shiftTarget = viewer.mainViewport.shiftTarget = new THREE.Vector3 //project在targetPlane上的位置  
+
+        this.getAllBoxes().forEach(box=>{
+            viewer.updateVisible(box,'hidden',true)  //显现
+        })
+        
+        viewer.transformationTool.history.clear()
+        
+        viewer.transformationTool.addEventListener('transformed', this.events.transfromCallback)
+        //viewer.transformationTool.addEventListener('stopDrag', this.events.onTransfromEnd)
+        viewer.inputHandler.addEventListener('selection_changed', this.events.selectCallback) 
+        viewer.inputHandler.addEventListener('keydown', this.events.onkeydown)
+          
+         
+        this.setPointLevelAuto()
+        var initialPointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == Potree.settings.originDatasetId)
+        //隐藏 初始数据集以外的数据集 
+        viewer.scene.pointclouds.forEach(e=>{
+            if(e.dataset_id!=Potree.settings.originDatasetId){
+                viewer.updateVisible(e,'enterClipping',false) 
+                //Potree.settings.floorplanEnables[e.dataset_id] = false 
+                e.panos.forEach(pano=>pano.setEnable(false)) //禁止漫游
+                    
+            }else{
+                viewer.updateVisible(e,'enterClipping',true, 1, 'add')
+                //Potree.settings.floorplanEnables[e.dataset_id] = true 
+            }
+        })
+        
+        viewer.flyToDataset({ pointcloud : initialPointcloud, duration:0})
+         
+    }
+    
+    
+    leave(){
+         
+        viewer.transformationTool.setModeEnable(['scale', 'translation', 'rotation'] )
+         
+        viewer.transformationTool.frame.material.visible = true //恢复
+        this.switchView(  'mainView'  ) 
+        
+        
+        this.getAllBoxes().forEach(box=>{
+            viewer.updateVisible(box,'hidden',false)//隐身
+        })
+        viewer.transformationTool.removeEventListener('transformed', this.events.transfromCallback)
+        //viewer.transformationTool.removeEventListener('stopDrag', this.events.onTransfromEnd)
+        viewer.inputHandler.removeEventListener('selection_changed', this.events.selectCallback)
+        //viewer.inputHandler.removeEventListener('keydown', this.events.onkeydown)
+        viewer.transformObject(null)
+        viewer.transformationTool.history.clear()
+        
+        //恢复 初始数据集以外的数据集 
+        viewer.scene.pointclouds.forEach(e=>{
+            if(e.dataset_id!=Potree.settings.originDatasetId){
+                viewer.updateVisible(e,'enterClipping',true) 
+                e.panos.forEach(pano=>pano.setEnable(true))
+            }else{
+                viewer.updateVisible(e,'enterClipping',false, 0, 'cancel')
+            }
+        }) 
+        
+    }
+    
+    setTranMode(mode){//rotate or translate 
+        this.tranMode = mode 
+        
+        viewer.transformationTool.setModeEnable([mode])
+       
+    }
+    
+    
+    //问:是否要显示其他数据集
+    setPointLevelAuto(){
+        /* 
+        
+        let visiCount = viewer.images360.panos.length
+        let maxCount = 200, minCount = 20,  minPer = 0.7, maxPer = 1
+        let percent = maxPer - ( maxPer - minPer) * THREE.Math.clamp((visiCount - minCount)  / (maxCount - minCount),0,1)  
+         
+        Potree.settings.UserDensityPercent = percent        ---还是不限制了,尤其是平面图希望更细致点,毕竟剪裁主要要看清剪裁的部位。
+        
+         */
+        
+        viewer.setPointBudget(5*1000*1000);     //给个中等到高等之间的质量
+        Potree.settings.sizeFitToLevel = true 
+        viewer.setPointLevels()
+             
+    }
+    
+    
+    
+    
+    
+    
+    
+    getAllBoxes(){
+        return viewer.scene.volumes.filter(v=>v.clip && v instanceof Potree.BoxVolume  ) 
+    }
+     
+    
+    getCalcData(){//给后台矩阵数据,以裁剪点云。
+        let Clip = viewer.modules.Clip //裁剪下载模块 
+        
+        let data = {   
+            transformation_matrix: viewer.scene.pointclouds.filter(p=>p.dataset_id == Potree.settings.originDatasetId).map((cloud)=>{
+                let data = {
+                    id: cloud.dataset_id, 
+                    matrix : new THREE.Matrix4().elements,  //参照downloadNoCrop,给默认值,表示没有最外层裁剪
+                    VisiMatrixes: cloud.material.clipBoxes_in.filter(e=>!e.box.isNew).map(e=>Clip.getTransformationMatrix(cloud, e.inverse).elements), 
+                    UnVisiMatrixes: cloud.material.clipBoxes_out.filter(e=>!e.box.isNew).map(e=>Clip.getTransformationMatrix(cloud, e.inverse).elements),
+                    modelMatrix:(new THREE.Matrix4).copy(cloud.transformMatrix).transpose().elements
+                }  
+                return data
+            }) ,
+            aabb: "b-12742000 -12742000 -12742000 12742000 12742000 12742000" //剪裁空间 
+           
+        }
+        
+        return data
+    } 
+    
+    
+    
+    saveClipData(){//输出所有的clip volumeBox
+        let oldState = !viewer.clipUnabled;
+        viewer.setClipState(true)
+        let data = this.getAllBoxes().filter(e=>!e.isNew).map(volume=>{
+            return {  
+                clipTask: volume.clipTask,
+                position: Potree.Utils.datasetPosTransform({position:volume.position, toDataset: true, datasetId: Potree.settings.originDatasetId}).toArray(),
+                rotation: Potree.Utils.datasetRotTransform({rotation:volume.rotation, toDataset: true, datasetId: Potree.settings.originDatasetId, getRotation:true}).toArray().slice(0,3),
+                scale: volume.scale.toArray(),  
+            }
+        })
+        console.log(data)
+        console.log(JSON.stringify(data))
+        viewer.setClipState(oldState)
+        return data
+    }
+    
+    loadFromData(data=[]){
+        data.forEach(v=>{
+            let volume = new Potree.BoxVolume({clip:true, clipTask:v.clipTask});
+            volume.scale.fromArray(v.scale);
+            volume.position.fromArray(v.position);
+            volume.rotation.fromArray(v.rotation);  
+            
+            volume.position.copy(Potree.Utils.datasetPosTransform({position:volume.position, fromDataset: true, datasetId:Potree.settings.originDatasetId}))
+            volume.rotation.copy(Potree.Utils.datasetRotTransform({rotation:volume.rotation, fromDataset: true, datasetId:Potree.settings.originDatasetId, getRotation:true}))
+            
+            viewer.scene.addVolume(volume);
+            viewer.volumeTool.scene.add(volume);
+        })
+    }
+    
+    
+}
+
+
+
+//注意:实时裁剪只对初始数据集有效
+
+
+
+
+
+
+
+
+
+

+ 31 - 0
src/materials/shaders/skybox.fs

@@ -0,0 +1,31 @@
+varying vec3 vWorldPosition;
+uniform sampler2D tDiffuse; 
+
+#define PI 3.141592653 
+
+/*vec2 getSamplerCoord( vec3 direction ) 
+{
+    direction = normalize(direction);
+    float tx=atan(direction.x,direction.z)/(PI*2.0)+0.5;
+    float ty=acos(direction.y)/PI;
+
+    return vec2(tx,ty);
+}*/
+vec2 getSamplerCoord( vec3 direction ) 
+{
+    direction = normalize(direction);
+    float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;
+    float ty=acos(direction.z)/PI;
+
+    return vec2(tx,ty);
+}  
+void main() 
+{
+    vec2 samplerCoord = getSamplerCoord(vWorldPosition);
+    vec4 color = texture2D(tDiffuse, samplerCoord);
+    //gl_FragColor = RGBEToLinear( color ); //对于hdr必须加这一句,否则效果很奇怪。copy自meshBasicMaterial里的mat_fragment的mapTexelToLinear函数,只要有map就会使用该函数
+    gl_FragColor = color;
+    //#include <tonemapping_fragment>
+    //////#include <encodings_fragment>
+
+}                

+ 15 - 0
src/materials/shaders/skybox.vs

@@ -0,0 +1,15 @@
+uniform mat4 matrix; 
+varying vec3 vWorldPosition;
+
+
+
+void main() 
+{     
+     
+    vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz ;  // - cameraPosition; 
+    vWorldPosition = (vec4(vWorldPosition, 1.0) * matrix).xyz;
+    vWorldPosition.x *= -1.0;
+
+
+    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+}