/** * @author mschuetz / http://mschuetz.at * * */ import * as THREE from "../../libs/three.js/build/three.module.js"; import {KeyCodes} from "../KeyCodes.js"; import {Utils} from "../utils.js"; import Common from "../custom/utils/Common.js"; import DepthBasicMaterial from '../custom/materials/DepthBasicMaterial.js' let {Buttons} = Potree.defines export class InputHandler extends THREE.EventDispatcher { constructor (viewer,scene) { super(); this.viewer = viewer; this.renderer = viewer.renderer; this.domElement = this.renderer.domElement; this.enabled = true; this.scene = scene; this.interactiveScenes = []; this.interactiveObjects = new Set(); this.inputListeners = []; this.blacklist = new Set(); this.drag = null; this.mouse = new THREE.Vector2(0, 0); //add: this.pointer = new THREE.Vector2(0, 0); //交互点的屏幕坐标,有别于DOM坐标,在此存放NDC坐标。(NDC,三维常用坐标系,二维坐标,整个屏幕映射范围(-1,1),屏幕中心为原点,+Y朝上,+X朝右) this.mouseDownMouse = new THREE.Vector2(0, 0); this.selection = []; this.hoveredElements = []; this.pressedKeys = {}; this.wheelDelta = 0; this.speed = 1; this.logMessages = false; if (this.domElement.tabIndex === -1) { this.domElement.tabIndex = 2222; } this.lastPointerUpTime = 0 this.touches = [] this.interactHistory = {move:0} //add this.hoverViewport = viewer.viewports[0] this.domElement.addEventListener('contextmenu', (event) => { event.preventDefault(); }, false); this.domElement.addEventListener('click', this.onMouseClick.bind(this), false); this.domElement.addEventListener('mousedown', this.onMouseDown.bind(this), false); window.addEventListener('mouseup', this.onMouseUp.bind(this), false); if(Potree.isIframeChild){//子页面的话在父页面也要加侦听(应该不会有多层吧?否则要一直加到最外层) //window.parent.addEventListener('mouseup', this.onMouseUp.bind(this), false); //可能跨域 //window.parent.postMessage('listenMouseup', '*'); window.addEventListener('mouseout', this.onMouseUp.bind(this), false); //cancel drag } this.domElement.addEventListener('mousemove', this.onMouseMove.bind(this), false); //add /* this.domElement.addEventListener("pointerout", this.onMouseUp.bind(this)), this.domElement.addEventListener("pointercancel", this.onMouseUp.bind(this)), */ this.domElement.addEventListener('mousewheel', this.onMouseWheel.bind(this), false); this.domElement.addEventListener('DOMMouseScroll', this.onMouseWheel.bind(this), false); // Firefox //this.domElement.addEventListener('dblclick', this.onDoubleClick.bind(this)); //因为双击时间间隔是跟随系统的所以不好判断 this.domElement.addEventListener('keydown', this.onKeyDown.bind(this)); window.addEventListener('keyup', this.onKeyUp.bind(this)); window.addEventListener('blur',this.onKeyUp.bind(this)); //add this.domElement.addEventListener('touchstart', this.onTouchStart.bind(this)); this.domElement.addEventListener('touchend', this.onTouchEnd.bind(this)); this.domElement.addEventListener('touchmove', this.onTouchMove.bind(this)); { this.addEventListener('isMeasuring',(e)=>{ //console.log('isMeasuring',e.v,e.cause) this.isMeasuring = e.v }) } window.viewer.addEventListener('loopStart',()=>{ this.interactHistory = {} //清空 }) } /* addInputListener (listener) { this.inputListeners.push(listener); } removeInputListener (listener) { this.inputListeners = this.inputListeners.filter(e => e !== listener); } getSortedListeners(){ return this.inputListeners.sort( (a, b) => { let ia = (a.importance !== undefined) ? a.importance : 0; let ib = (b.importance !== undefined) ? b.importance : 0; return ib - ia; }); } */ //统一跟第一个触碰的viewport相同 updateTouchesInfo(e){ var viewport, pointer, camera let oldTouches = this.touches let changedTouches = Array.from(e.changedTouches) let touches = Array.from(e.touches) this.touches = touches.map(touch=>{ let touch_ = oldTouches.find(a=>a.touch.identifier == touch.identifier) let pointer = touch_ && touch_.pointer //复制原先的值 return { touch, pointer, } }) if(e.touches.length > 0){ let newTouches = touches.filter(e=>! oldTouches.some(a=>a.touch.identifier == e.identifier) && !changedTouches.some(a=>a.identifier == e.identifier) ) //从按钮处划过时e.touches中会出现this.touches和changedTouches中都没有的identifier if(newTouches.length>0){ console.warn('has new',newTouches.map(e=>e.identifier)) } newTouches.concat(changedTouches).forEach(touch=>{ //修改changedTouches的 let touch_ = this.touches.find(a=>a.touch.identifier == touch.identifier) if(touch_){ let a = this.getPointerInViewport(touch.pageX, touch.pageY, this.dragViewport||viewport, new THREE.Vector2) touch_.pointer = a.pointer.clone() viewport = a.viewport; camera = a.camera } }) //使用当前touches的平均 if(e.touches.length > 1){ let pageX = Common.average(e.touches, "pageX") let pageY = Common.average(e.touches, "pageY") let a = this.getPointerInViewport(pageX, pageY, viewport, new THREE.Vector2) this.pointer.copy(a.pointer) //console.log('updateTouchesInfo', this.pointer.clone()) }else{ this.pointer = this.touches[0].pointer.clone() //更新,使用当前touches中的第一个 } /* if(this.touches.find(e=>!e.pointer)){ console.error(' touches has no pointer', oldTouches.map(e=>e.touch.identifier), Array.from(e.touches).map(e=>e.identifier), Array.from(e.changedTouches).map(e=>e.identifier) ) } */ //console.log(this.touches) //console.log('更新pointer1',this.pointer.toArray()) return {viewport, camera/* , pointer:this.pointer */} } } onTouchStart (e) { if (this.logMessages) console.log(this.constructor.name + ': onTouchStart'); e.preventDefault(); /* if (e.touches.length === 1 || !this.drag) { //!this.drag代表一次性下了两个指头 let rect = this.domElement.getBoundingClientRect(); let x = e.touches[0].pageX let y = e.touches[0].pageY this.dealPointerDown(x,y,e,true) }else{ this.updateTouchesInfo(e) this.drag.end.copy(this.pointer) } */ this.dealPointerDown(e,true) this.viewer.dispatchEvent($.extend( this.getEventDesc(e,true), { type: 'global_' + e.type, changedTouches: e.changedTouches } )); /* console.log('targetTouches :', Array.from(e.targetTouches).map(e=>'| identifier: '+ e.identifier), 'changedTouches :', Array.from(e.changedTouches).map(e=>'| identifier: '+ e.identifier) ) */ //console.log('') } onTouchMove (e) { if (this.logMessages) console.log(this.constructor.name + ': onTouchMove'); e.preventDefault(); /* if (e.touches.length === 1) { let rect = this.domElement.getBoundingClientRect(); let x = e.touches[0].pageX; let y = e.touches[0].pageY; }else{ this.updateTouchesInfo(e) this.drag.pointerDelta.subVectors(this.pointer, this.drag.end) this.drag.end.copy(this.pointer) } */ this.dealPointerMove(e, true) this.viewer.dispatchEvent($.extend( this.getEventDesc(e,true), { type: 'global_' + e.type, changedTouches: e.changedTouches } )); /* console.log('targetTouches :', Array.from(e.targetTouches).map(e=>'| identifier: '+ e.identifier), 'changedTouches :', Array.from(e.changedTouches).map(e=>'| identifier: '+ e.identifier) ) */ } onTouchEnd (e) { if (this.logMessages) console.log(this.constructor.name + ': onTouchEnd'); e.preventDefault(); //console.log('onTouchEnd') this.updateTouchesInfo(e) /* if (e.touches.length === 0) { let rect = this.domElement.getBoundingClientRect(); let x = e.changedTouches[0].pageX //万一一次松开两个指头的怎么办 let y = e.changedTouches[0].pageY this.dealPointerUp(x,y,e,true) }else { this.drag.end.copy(this.pointer) } */ this.dealPointerUp(e,true) this.viewer.dispatchEvent($.extend( this.getEventDesc(e,true), { type: 'global_' + e.type, } )); //console.log('touchend length '+e.touches.length, this.touches.length) } onKeyDown (e) { if (this.logMessages) console.log(this.constructor.name + ': onKeyDown'); // DELETE /* if (e.keyCode === KeyCodes.DELETE && this.selection.length > 0) { this.dispatchEvent({ type: 'delete', selection: this.selection }); this.deselectAll(); } */ this.dispatchEvent({ type: 'keydown', keyCode: e.keyCode, event: e }); // for(let l of this.getSortedListeners()){ // l.dispatchEvent({ // type: "keydown", // keyCode: e.keyCode, // event: e // }); // } this.pressedKeys[e.keyCode] = true; // e.preventDefault(); } onKeyUp (e) { if (this.logMessages) console.log(this.constructor.name + ': onKeyUp'); if(e.keyCode != void 0){ delete this.pressedKeys[e.keyCode]; }else{ this.pressedKeys = {} } e.preventDefault(); } onDoubleClick (e) { if (this.logMessages) console.log(this.constructor.name + ': onDoubleClick'); let consumed = false; for (let hovered of this.hoveredElements) { if (hovered._listeners && hovered._listeners['dblclick']) { hovered.object.dispatchEvent({ type: 'dblclick', mouse: this.mouse, object: hovered.object }); consumed = true; break; } } if (!consumed) { /* for (let inputListener of this.getSortedListeners()) { inputListener. */this.viewer.dispatchEvent({ type: 'global_dblclick', mouse: this.mouse, object: null }); //} } this.needSingleClick = false//add e.preventDefault(); } onMouseClick (e) { if (this.logMessages) console.log(this.constructor.name + ': onMouseClick'); e.preventDefault(); } dealPointerDown(e,isTouch){ e.preventDefault(); //重新获取一下pointer, 因点击了浏览器的按钮展开列表时 move回来不会触发onmousemove,所以pointer是旧的 if(isTouch){ var { camera, viewport } = this.updateTouchesInfo(e) if(this.drag){ //因为触屏在按下前缺少pointermove所以要更新下 this.drag.end = this.pointer.clone() } }else{ var { camera, viewport } = this.getPointerInViewport(e.clientX, e.clientY ) } this.dragViewport = this.hoverViewport = viewport //if(isTouch || !Potree.settings.intersectWhenHover ){ if(isTouch || !this.dragViewport.view.isFlying() && Potree.settings.intersectWhenHover && Potree.settings.editType != 'pano' ){//漫游点编辑如果拖拽前getIntersect旋转会延迟 //isTouch必须更新 否则是旧的 this.hoveredElements = this.getHoveredElements(); let dontIntersect = false this.intersect = this.getIntersect(viewport,null,null,dontIntersect) //更新intersect,避免在没有mousemove但flyToPano后intersect未更新。 //this.intersect = this.getWholeIntersect() } if(!viewport)return //why add this? if (!this.drag) { let target = (isTouch||e.button == THREE.MOUSE.LEFT) && this.hoveredElements.find(el => (//只有左键能拖拽 el.object._listeners && el.object._listeners['drag'] && el.object._listeners['drag'].length > 0)); if (target) { this.startDragging(target.object, {location: target.point}); } else { this.startDragging(null); } } this.drag.intersectStart = this.intersect; if(!isTouch || e.touches.length == 1){ let consumed = false; let consume = () => { return consumed = true; }; //if (this.hoveredElements.length === 0) { this.viewer.dispatchEvent($.extend( this.getEventDesc(e,isTouch), { type: 'global_mousedown' } )); for(let hovered of this.hoveredElements){ let object = hovered.object; object.dispatchEvent({ type: 'mousedown', viewer: this.viewer, consume: consume }); if(consumed){ break; } } } this.mouseDownMouse = this.mouse.clone() this.pointerDownTime = Date.now() } onMouseDown (e) { if (this.logMessages) console.log(this.constructor.name + ': onMouseDown'); this.dealPointerDown(e) } /* getWholeIntersect(hoveredElements, intersectPoint){//add hoveredElements = hoveredElements || this.hoveredElements intersectPoint = intersectPoint || this.intersectPoint if(Potree.settings.intersectOnObjs && hoveredElements[0] && hoveredElements[0].object.isModel){ return {//模拟点云的intersectPoint的结构写法 hoveredElement : hoveredElements[0] , location: hoveredElements[0].point, point: {normal: hoveredElements[0].face.normal }, distance: hoveredElements[0].distance, object: hoveredElements[0].object } }else return intersectPoint } */ getEventDesc(e,isTouch){//搜集dispatchEvent要给的一般数据 let o = { viewer: this.viewer, mouse: this.mouse, pointer:this.pointer, drag :this.drag, isTouch, dragViewport : this.dragViewport, hoverViewport: this.hoverViewport, // button: isTouch ? 0 : e.button, //intersectPoint:this.intersectPoint, hoveredElement: this.hoveredElements[0], intersect: this.intersect//this.getWholeIntersect() , //可能包含mesh上的,针对融合页面 } if(e){ o.isAtDomElement = e.target == this.domElement } if(isTouch){ o.touches = this.touches }else if(e){ o.button = e.button o.buttons = e.buttons } return o; } dealPointerUp(e,isTouch){ if(!this.drag){// 在canvas外mousedown return } this.drag.end.copy(this.pointer) if(isTouch && e.touches.length >= 1){ return } let now = Date.now() if (this.logMessages) console.log(this.constructor.name + ': onMouseUp'); e.preventDefault(); let pressDistance = this.mouseDownMouse.distanceTo(this.mouse); let pressTime = now - this.pointerDownTime; let noMovement = this.drag.pointerDelta.length() == 0//this.getNormalizedDrag().length() === 0; let consumed = false; let consume = () => { return consumed = true; }; //if (this.hoveredElements.length === 0) { /* for (let inputListener of this.getSortedListeners()) { inputListener */this.viewer.dispatchEvent($.extend( this.getEventDesc(e,isTouch), { type: 'global_mouseup', pressDistance, consume, } )); /* if(consumed){//?? break; } */ //} //} if (this.hoveredElements.length > 0) { let hovered = this.hoveredElements .map(e => e.object) .find(e => (e._listeners && e._listeners['mouseup'])); if(hovered){ hovered.dispatchEvent({ type: 'mouseup', viewer: this.viewer, consume: consume }); } } if (this.drag) { //拖拽结束 if (this.drag.object/* && e.button == THREE.MOUSE.LEFT */) {//add LEFT if (this.logMessages) console.log(`${this.constructor.name}: drop ${this.drag.object.name}`); this.drag.object.dispatchEvent($.extend( this.getEventDesc(e,isTouch), { type: 'drop', pressDistance, } )); } else { this.viewer.dispatchEvent($.extend( this.getEventDesc(e,isTouch), { type: 'global_drop', pressDistance } )); } // check for a click if(pressDistance < Potree.config.clickMaxDragDis && pressTimee.object._listeners['click']) if(clickElement){ //console.log('clickElement',clickElement) if (this.logMessages) console.log(`${this.constructor.name}: click ${clickElement.name}`); clickElement.object.dispatchEvent($.extend( this.getEventDesc(e,isTouch), { type: 'click', pressDistance } )); } } let selectable if(/* !consumed && */ !this.fixSelection){ if (e.button === THREE.MOUSE.LEFT) { if (noMovement) { selectable = this.hoveredElements.find(el => el.object._listeners && el.object._listeners['select']); if (selectable) { selectable = selectable.object; if (this.isSelected(selectable)) { this.deselectAll(); } else { this.deselectAll(); this.toggleSelection(selectable); } consumed = true //add } else { if(this.selection.length>0)consumed = true //add 取消选择后,阻断后续 this.deselectAll(); } } } else if ((e.button === THREE.MOUSE.RIGHT) && noMovement) { this.deselectAll(); } } let consume = () => { return consumed = true; }; let desc = this.getEventDesc(e,isTouch) if(!consumed){ this.viewer.dispatchEvent($.extend( desc, { type: 'global_click', pressDistance, clickElement:clickElement/* || selectable */, consume } )); } //增加 单击: this.needSingleClick = true; consumed || setTimeout(()=>{ if(this.needSingleClick){ this.viewer.dispatchEvent($.extend( desc, { type: 'global_single_click', pressDistance, clickElement } )); } }, Potree.config.doubleClickTime+1) //自行执行双击: if(now - this.lastClickTime < Potree.config.doubleClickTime){ this.onDoubleClick(e) } this.lastClickTime = now } this.drag = null; } this.dragViewport = null } onMouseUp (e) { this.dealPointerUp( e ) } getPointerInViewport(clientX, clientY, viewForceAt, pointer ){ let rect = this.domElement.getBoundingClientRect(); let x = clientX - rect.left; let y = clientY - rect.top; let camera let viewport pointer = pointer ||this.pointer //if(this.viewer.viewports || viewForceAt){ var getDimension = (view)=>{ var left = Math.ceil(this.domElement.clientWidth * view.left) , bottom = Math.ceil(this.domElement.clientHeight * view.bottom) , width = Math.ceil(this.domElement.clientWidth * view.width) , height = Math.ceil(this.domElement.clientHeight * view.height) , top = this.domElement.clientHeight - bottom - height return {left, bottom, width, height, top} } var getView = (view, left, bottom, width, height, top)=>{ this.mouse.set(x-left, y - top ) Utils.convertScreenPositionToNDC(pointer, this.mouse, width, height); //console.log('更新pointer2',this.pointer.toArray()) camera = view.camera; viewport = view } if(viewForceAt){ let {left, bottom, width, height, top} = getDimension(viewForceAt) getView(viewForceAt, left, bottom, width, height, top) }else{ var length = this.viewer.viewports.length; //var getif = false for(var i=0;i= left && x <= left + width && y >= top && y <= top + height){ getView(view, left, bottom, width, height, top) //getif = true break; } } } return { camera, viewport, pointer } } ifBlockedByIntersect({pos3d, margin=0, cameraPos, pickWindowSize, pano, useDepthTex}={}){//某点是否被遮挡(不允许camera修改位置, 因为depthTex不好置换) let intersect = this.getIntersect(this.hoverViewport, true, pickWindowSize, null, null, useDepthTex, {pos3d, cameraPos, pano}) let cameraPos_ = (!cameraPos && pano) ? pano.position : (cameraPos||this.hoverViewport.view.position) if(intersect && intersect.distance+margin <= pos3d.distanceTo(cameraPos_)){ return intersect //被遮挡 } //点云模式,对没加载出的点云不准确。 尤其是需要修改相机位置时,因临时修改并不能使点云加载。 } getIntersect(viewport, onlyGetIntersect, pickWindowSize, dontIntersect, usePointcloud, useDepthTex, prop={}){// usePointcloud:必须使用点云 let intersectPoint let camera = viewport.camera let raycaster viewer.addTimeMark('getIntersect','start') let getByDepthTex = ()=>{ let intersect if(prop.pos3d){ let cameraPos = prop.pano ? prop.pano.position : camera.position let dir = new THREE.Vector3().subVectors(prop.pos3d, cameraPos).normalize(); intersect = {dir} }else{ intersect = Utils.getIntersect(camera, [viewer.images360.cube], this.pointer, raycaster) } intersectPoint = viewer.images360.depthSampler.sample(intersect, prop.pano, !!prop.pos3d) //可能不准确, 因pano可能未加载depthTex if(intersectPoint && Potree.settings.depTexLocBindDataset){ intersectPoint.pointcloud = (prop.pano || viewer.images360.currentPano).pointcloud //在全景模式下,虽然深度图上的点可能对应别的pointcloud,但因为是在当前全景图处得到的,所以即使将原本对应的点云移走,该点也不移动是有道理的。它可以永远跟着该全景图。 } } let getByCloud = ()=>{ if(prop.pos3d){//指定了目标点,而非只是用pointer所在位置 prop.cameraPos && camera.position.copy(prop.cameraPos) camera.lookAt(prop.pos3d) camera.updateMatrixWorld() prop.pointer = this.pointer.clone() prop.mouse = this.mouse.clone() this.pointer.set(0,0) //画布中心 this.mouse.set(Math.round(viewport.resolution.x/2), Math.round(viewport.resolution.y/2)) } intersectPoint = (viewport.noPointcloud || dontIntersect)? null : Utils.getMousePointCloudIntersection( viewport, this.mouse, this.pointer, camera, this.viewer, this.viewer.scene.pointclouds, {pickClipped: true, isMeasuring: this.isMeasuring, pickWindowSize, cameraChanged: !!prop.pos3d } ); //恢复 if(prop.pos3d){ viewport.view.applyToCamera(camera) this.pointer.copy(prop.pointer) this.mouse.copy(prop.mouse) } } let canUseDepthTex = (Potree.settings.displayMode == 'showPanos' || useDepthTex) && viewer.images360.currentPano.pointcloud.hasDepthTex && viewport == viewer.mainViewport && !usePointcloud if(canUseDepthTex)getByDepthTex() else getByCloud() /* if(canUseDepthTex && !this.isMeasuring){ getByDepthTex() }else{ getByCloud() if(!intersectPoint && canUseDepthTex ){ //若在测量,先尝试点云,再用全景 //后来发现有深度图的点云全景visibleNode为空,pick不到的 getByDepthTex() } } */ //console.log(viewport.name , intersectPoint && intersectPoint.location ) let intersect let intersectOnModel, allElements if(Potree.settings.intersectOnObjs && !dontIntersect){ if(prop.point){ raycaster = new THREE.Raycaster() var dir = new THREE.Vector3().subVectors(prop.point, camera.position).normalize() raycaster.set(camera.position, dir) //var origin = new THREE.Vector3(pointer.x, pointer.y, -1).unproject(camera), } allElements = this.getHoveredElements(viewer.objs.children, true, raycaster) if(allElements[0]){ intersectOnModel = {//模拟点云的intersectPoint的结构写法 hoveredElement : allElements[0] , location: allElements[0].point, //point: {normal: allElements[0].face.normal }, normal: allElements[0].face && allElements[0].face.normal, distance: allElements[0].distance, object: allElements[0].object } } } if(intersectPoint && intersectOnModel){ if(intersectPoint.distance < intersectOnModel.distance){ intersect = intersectPoint }else{ intersect = intersectOnModel } }else{ intersect = intersectOnModel || intersectPoint } if(viewport.camera.type == 'OrthographicCamera'/* == 'mapViewport' */){ let pos3d = new THREE.Vector3(this.pointer.x,this.pointer.y,-1).unproject(viewport.camera); //z:-1朝外 if(!intersect){ intersect = {} } intersect.orthoIntersect = pos3d.clone() } //记录全部hover到的: if(intersect){ intersect.allElements = allElements intersect.pointclouds = intersectPoint ? intersectPoint.pointclouds : [] } viewer.addTimeMark('getIntersect','end') //点云费时:2-15ms //深度图费时: 0.1-0.2ms /* intersect && intersect.location && intersect.location.applyMatrix4(viewer.scene.scene.matrix)//add */ if(onlyGetIntersect){ return intersect } if (intersect) { if(viewer.showCoordType){ //显示坐标位置时 let pos = intersect.point.position.toArray() if(viewer.showCoordType == "local"){ }else if(viewer.showCoordType == "lonlat"){ pos = viewer.transform.lonlatToLocal.inverse(pos) }else{ pos = viewer.transform.lonlatToLocal.inverse(pos) pos = viewer.transform.lonlatTo4550.forward(pos) } viewer.dispatchEvent({ type : "coordinateChange", pos }) } } //console.log('getIntersect', !!intersectPoint) this.intersect = intersect intersect && (this.hoverViewport.lastIntersect = intersect) return intersect } onMouseMove (e) { return this.dealPointerMove( e ) } dealPointerMove(e, isTouch){ if(this.interactHistory.move) return //一帧只触发一次 this.interactHistory.move = 1 if(isTouch){ var { camera, viewport } = this.updateTouchesInfo(e) }else { var { camera, viewport } = this.getPointerInViewport(e.clientX, e.clientY, this.dragViewport) } this.hoverViewport = viewport if(!viewport)return//刚变化viewport时会找不到 let isFlying = this.viewer.viewports.some(e=>e.view.isFlying()) || viewer.scene.cameraAnimations.some(c=>c.onUpdate) let intersect if(e.onlyGetIntersect || (!this.drag || this.drag.object || viewport.alignment ) ){ //没有拖拽物体,但按下鼠标了的话,不intersect。触屏的就能直接避免intersect let dontIntersect = this.drag && viewport.alignment || isFlying || !Potree.settings.intersectWhenHover // flying 时可能卡顿 //console.log('dontIntersectPointcloud',dontIntersectPointcloud) /* if(e.onlyGetIntersect ) */intersect = this.getIntersect(viewport, e.onlyGetIntersect, e.pickWindowSize, !!dontIntersect, e.whichPointcloud) //数据集多的时候卡顿 /* else Potree.Common.intervalTool.isWaiting('getIntersect', ()=>{ this.intersect = this.getIntersect(viewport, e.onlyGetIntersect, e.pickWindowSize, !!dontIntersect, e.whichPointcloud) //数据集多的时候卡顿 }, 156); */ //console.log('intersect', intersect) } if(e.onlyGetIntersect){ /* if(Potree.settings.intersectOnObjs){ let hoveredElements = this.getHoveredElements() //应该不用发送mouseover事件吧 let intersect = this.getWholeIntersect(hoveredElements, intersectPoint) return intersect } return intersectPoint */ return intersect } e.preventDefault(); if (this.drag) {//有拖拽(不一定拖拽了物体, 也不一定按下了鼠标) this.drag.mouse = isTouch ? 1 : e.buttons; //add: //this.drag.pointer = this.pointer.clone(); //this.drag.hoverViewport = this.hoverViewport this.drag.pointerDelta.subVectors(this.pointer, this.drag.end) this.drag.end.copy(this.pointer) if (this.drag.object && (e.buttons == Buttons.NONE || !this.drag.notPressMouse )){//如果是本不需要按鼠标的拖拽,但按下了鼠标,就不执行这段(改为拖拽场景,如添加测量时突然拖拽画面) if (this.logMessages) console.log(this.constructor.name + ': drag: ' + this.drag.object.name); this.drag.object.dispatchEvent($.extend( this.getEventDesc(e,isTouch), { type: 'drag', //拖拽物体 } )) viewer.dispatchEvent('content_changed') } else { if (this.logMessages) console.log(this.constructor.name + ': drag: '); let dragConsumed = false; this.viewer.dispatchEvent($.extend( this.getEventDesc(e,isTouch), { type: 'global_drag', //拖拽画面 consume: () => {dragConsumed = true;} } )) } } if(!isTouch || e.touches.length == 1){ if((!this.drag || this.drag.notPressMouse || Potree.settings.intersectOnObjs && this.drag.object) && !isFlying){ /* let blacklist = this.drag && this.drag */ let hoveredElements = this.getHoveredElements( ); if(hoveredElements.length > 0){ let names = hoveredElements.map(h => h.object.name).join(", "); if (this.logMessages) console.log(`${this.constructor.name}: onMouseMove; hovered: '${names}'`); } let curr = hoveredElements.map(a => a.object).find(a => true);//只取第一个 let prev = this.lastMouseoverElement //this.hoveredElements.map(a => a.object).find(a => true); if(curr !== prev){ if(curr){ if (this.logMessages) console.log(`${this.constructor.name}: mouseover: ${curr.name}`); curr.dispatchEvent({ type: 'mouseover', object: curr, }); } if(prev){ if (this.logMessages) console.log(`${this.constructor.name}: mouseleave: ${prev.name}`); prev.dispatchEvent({ type: 'mouseleave', object: prev, }); } this.lastMouseoverElement = curr viewer.dispatchEvent('content_changed') } if(hoveredElements.length > 0){ let object = hoveredElements .map(e => e.object) .find(e => (e._listeners && e._listeners['mousemove'])); if(object){ object.dispatchEvent({ type: 'mousemove', object: object }); } } this.hoveredElements = hoveredElements } //this.intersect = this.getWholeIntersect() this.viewer.dispatchEvent($.extend( this.getEventDesc(e,isTouch), { type: 'global_mousemove', } )) } } onMouseWheel(e){ if(!this.enabled) return; if(this.logMessages) console.log(this.constructor.name + ": onMouseWheel"); e.preventDefault(); let delta = 0; if (e.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9 delta = e.wheelDelta; } else if (e.detail !== undefined) { // Firefox delta = -e.detail; } let ndelta = Math.sign(delta); // this.wheelDelta += Math.sign(delta); if(!this.hoverViewport){//调试手机版时会无 var { viewport } = this.getPointerInViewport(e.clientX, e.clientY ) this.hoverViewport = viewport } if (this.hoveredElement) { this.hoveredElement.object.dispatchEvent($.extend( this.getEventDesc(e,isTouch), { type: 'mousewheel', delta: ndelta, object: this.hoveredElement.object } )); } else { this.viewer.dispatchEvent($.extend( this.getEventDesc(e), { type: 'global_mousewheel', delta: ndelta, } )); } setTimeout(()=>{ this.dealPointerMove(e )//add 在更新完view后重新获取intersect 和 drag },1)//只延迟1会崩溃吗 } startDragging (object, args = null) { let name = object ? object.name : "no name"; if (this.logMessages) console.log(`${this.constructor.name}: startDragging: '${name}'`); this.drag = { start: this.pointer.clone(), end: this.pointer.clone(), pointerDelta: new THREE.Vector2(0, 0), object: object, hoverViewport: this.hoverViewport, //会变化 dragViewport: this.hoverViewport, //不变 }; if (args) { for (let key of Object.keys(args)) { this.drag[key] = args[key]; } } if(object){ object.dispatchEvent($.extend( this.getEventDesc(), { type: 'startDragging' } )); } } /* getMousePointCloudIntersection (mouse) { return Utils.getMousePointCloudIntersection( this.mouse, this.scene.getActiveCamera(), this.viewer, this.scene.pointclouds); } */ toggleSelection (object) { let oldSelection = this.selection; let index = this.selection.indexOf(object); if (index === -1) { this.selection.push(object); object.dispatchEvent({ type: 'select' }); } else { this.selection.splice(index, 1); object.dispatchEvent({ type: 'deselect' }); } this.dispatchEvent({ type: 'selection_changed', oldSelection: oldSelection, selection: this.selection }); viewer.dispatchEvent('content_changed') } deselect(object){ let oldSelection = this.selection; let index = this.selection.indexOf(object); if(index >= 0){ this.selection.splice(index, 1); object.dispatchEvent({ type: 'deselect' }); this.dispatchEvent({ type: 'selection_changed', oldSelection: oldSelection, selection: this.selection }); } viewer.dispatchEvent('content_changed') } deselectAll () { for (let object of this.selection) { object.dispatchEvent({ type: 'deselect' }); } let oldSelection = this.selection; if (this.selection.length > 0) { this.selection = []; this.dispatchEvent({ type: 'selection_changed', oldSelection: oldSelection, selection: this.selection }); } viewer.dispatchEvent('content_changed') } isSelected (object) { let index = this.selection.indexOf(object); return index !== -1; } registerInteractiveObject(object){ this.interactiveObjects.add(object); } removeInteractiveObject(object){ this.interactiveObjects.delete(object); } registerInteractiveScene (scene) { let index = this.interactiveScenes.indexOf(scene); if (index === -1) { this.interactiveScenes.push(scene); } } unregisterInteractiveScene (scene) { let index = this.interactiveScenes.indexOf(scene); if (index > -1) { this.interactiveScenes.splice(index, 1); } } getHoveredElement () { let hoveredElements = this.getHoveredElements(); if (hoveredElements.length > 0) { return hoveredElements[0]; } else { return null; } } getHoveredElements (interactables, dontCheckDis, raycaster) { if(!interactables){ let scenes = this.hoverViewport.interactiveScenes || this.interactiveScenes.concat(this.scene); let interactableListeners = ['mouseup', 'mousemove', 'mouseover', 'mouseleave', 'drag', 'drop', 'click', 'select', 'deselect']; interactables = [] for (let scene of scenes) { scene.traverseVisible(node => {//检测加了侦听的object if (node._listeners && node.visible && !this.blacklist.has(node) ) { let hasInteractableListener = interactableListeners.filter((e) => { return node._listeners[e] !== undefined; }).length > 0; if (hasInteractableListener) { interactables.push(node); } } }); } } let camera = this.hoverViewport.camera if(!raycaster){ let ray = Utils.mouseToRay(this.pointer, camera ); raycaster = new THREE.Raycaster(); raycaster.ray.set(ray.origin, ray.direction); raycaster.camera = camera //add } if(camera.type == "OrthographicCamera"){//使无论多远,threshold区域都是一样宽的 raycaster.params.Line.threshold = 20/camera.zoom }else{ raycaster.params.Line.threshold = 0.04; //相对长度 } raycaster.params.Line2 = {threshold :20 } //拓宽的lineWidth //raycaster.layers.enableAll()//add Potree.Utils.setCameraLayers(raycaster, //设置能识别到的layers(如空间模型里只有mapViewer能识别到marker) ['sceneObjects','mapObjects','measure', 'transformationTool', 'model'], this.hoverViewport && this.hoverViewport.extraEnableLayers ) //this.hoverViewport.beforeRender && this.hoverViewport.beforeRender() viewer.dispatchEvent( {type:'raycaster', viewport: this.hoverViewport})//add let intersections = raycaster.intersectObjects(interactables.filter(o => o.visible), true, null, true); //原本是false 检测不到children let intersectionsCopy = intersections.slice() if(this.intersect && this.intersect.distance != void 0 && !dontCheckDis){//add intersections = intersections.filter(e=>{ let material = e.object.material return e.object.pickDontCheckDis || ( material.depthTest == false || material.depthWrite == false) && !material.realUseDepth //!material.depthTestWhenPick || ( material.useDepth ? e.distance < this.intersect.distance + material.uniforms.occlusionDistance.value : e.distance < this.intersect.distance ) }) } intersections = intersections.map(e=>{//add 转化为interactables var object = e.object; do{ if(interactables.includes(object)) { e.oriObject = e.object; e.object = object; break } object = object.parent }while(object) return e }) //add for测量线,在检测到sphere时优先选中sphere而非线 //intersections = intersections.sort(function(a,b){return b.object.renderOrder-a.object.renderOrder}) // 降序 intersections = intersections.sort(function(a,b){ let order2 = b.object.pickOrder || 0 let order1 = a.object.pickOrder || 0 return order2-order1 }) // 降序 //console.log('getHoveredElement ', intersections) return intersections; } /* setScene (scene) { this.deselectAll(); this.scene = scene; } */ update (delta) { } /*getNormalizedDrag () { if (!this.drag) { return new THREE.Vector2(0, 0); } let diff = new THREE.Vector2().subVectors(this.drag.end, this.drag.start); diff.x = diff.x / this.domElement.clientWidth; diff.y = diff.y / this.domElement.clientHeight; return diff; } getNormalizedLastDrag () { if (!this.drag) { return new THREE.Vector2(0, 0); } let mouseDelta = this.drag.mouseDelta.clone(); mouseDelta.x = mouseDelta.x / this.domElement.clientWidth; mouseDelta.y = mouseDelta.y / this.domElement.clientHeight; return mouseDelta; } */ getMouseDirection(pointer) {//add pointer = pointer || this.pointer let camera = this.hoverViewport.camera var t = new THREE.Vector3(pointer.x, pointer.y, -1).unproject(camera), i = new THREE.Vector3(pointer.x, pointer.y, 1).unproject(camera); return {origin: t, direction:i.clone().sub(t).normalize() } } }