123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- /**
- * @author mschuetz / http://mschuetz.at
- *
- * adapted from THREE.OrbitControls by
- *
- * @author qiao / https://github.com/qiao
- * @author mrdoob / http://mrdoob.com
- * @author alteredq / http://alteredqualia.com/
- * @author WestLangley / http://github.com/WestLangley
- * @author erich666 / http://erichaines.com
- *
- *
- *
- */
- import * as THREE from "../../libs/three.js/build/three.module.js";
- import {MOUSE} from "../defines.js";
- import {Utils} from "../utils.js";
- import {EventDispatcher} from "../EventDispatcher.js";
- import cameraLight from "../utils/cameraLight.js";
-
- export class FirstPersonControls extends EventDispatcher {
- constructor (viewer, viewport) {
- super();
-
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.scene = viewer.scene;
-
- this.rotationSpeed = 200;
- this.moveSpeed = 10;
-
-
- this.setCurrentViewport({hoverViewport:viewport, force:true}) //this.currentViewport = viewport
-
-
-
- this.keys = {
- FORWARD: ['W'.charCodeAt(0), 38],
- BACKWARD: ['S'.charCodeAt(0), 40],
- LEFT: ['A'.charCodeAt(0), 37],
- RIGHT: ['D'.charCodeAt(0), 39],
- UP: ['Q'.charCodeAt(0)],
- DOWN: ['E'.charCodeAt(0)],
-
- //SHIFT : [16],
- ALT : [18],
- Rotate_LEFT : ['L'.charCodeAt(0)],
- Rotate_RIGHT : ['J'.charCodeAt(0)],
- Rotate_UP : ['K'.charCodeAt(0)],
- Rotate_DOWN : ['I'.charCodeAt(0)],
- };
- this.fadeFactor = 50;
- this.yawDelta = 0;
- this.pitchDelta = 0;
- this.translationDelta = new THREE.Vector3(0, 0, 0);
- this.translationWorldDelta = new THREE.Vector3(0, 0, 0);
- this.tweens = [];
- //this.enableChangePos = true
-
- this.viewer.addEventListener('camera_changed',(e)=>{
- this.setFPCMoveSpeed(e.viewport)
- })
-
- let drag = (e) => {
- if(!this.enabled)return
- let viewport = e.drag.dragViewport;
- if(!viewport)return
- let camera = viewport.camera
- let mode = e.drag.mouse === MOUSE.LEFT && (!e.drag.dragViewport || e.drag.dragViewport.name == 'MainView') ? 'rotate' : 'pan'
- let moveSpeed = this.currentViewport.getMoveSpeed();
- if (e.drag.startHandled === undefined) {///???????
- e.drag.startHandled = true;
- this.dispatchEvent({type: 'start'});
- }
-
- if (mode == 'rotate') {//旋转 (为什么开启调试时旋转很慢?)
-
- //来自panoramaControl updateRotation
-
- let _matrixWorld = camera.matrixWorld
- camera.matrixWorld = new THREE.Matrix4;//unproject 前先把相机置于原点
-
- var e1 = new THREE.Vector3(e.drag.pointerDragStart.x,e.drag.pointerDragStart.y,-1).unproject(camera)
- , t = new THREE.Vector3(e.drag.pointer.x,e.drag.pointer.y,-1).unproject(camera)
- , i = Math.sqrt(e1.x * e1.x + e1.z * e1.z)
- , n = Math.sqrt(t.x * t.x + t.z * t.z)
- , o = Math.atan2(e1.y, i)
- , a = Math.atan2(t.y, n);
-
- this.pitchDelta += o - a //上下旋转
- e1.y = 0,
- t.y = 0;
-
- var s = Math.acos(e1.dot(t) / e1.length() / t.length());
-
- if(!isNaN(s)){
- var yawDelta = s //左右旋转
- e.drag.pointerDragStart.x > e.drag.pointer.x && (yawDelta *= -1)
- this.yawDelta += yawDelta
- }
- e.drag.pointerDragStart.copy(e.drag.pointer)
-
- camera.matrixWorld = _matrixWorld ;
-
- } else if (mode == 'pan') {//平移
- if(viewport.unableChangePos)return
- if(camera.type == "OrthographicCamera"){
-
-
- /* let ViewWidthPX = viewport.width * viewer.renderer.domElement.clientWidth
- let ViewHeightPX = viewport.height * viewer.renderer.domElement.clientHeight
- let cameraViewWidth = camera.right * 2
- let cameraViewHeight = camera.top * 2;
-
- moveVec.set(-1 * e.drag.mouseDelta.x * cameraViewWidth / ViewWidthPX, e.drag.mouseDelta.y * cameraViewHeight / ViewHeightPX , 0).applyQuaternion(camera.quaternion)
- */
-
- let moveVec = Utils.getOrthoCameraMoveVec(e.drag.pointerDelta, camera )//最近一次移动向量
-
- let handleState = window.viewer.modules.Alignment.handleState
- if(viewport.alignment && handleState && viewport.alignment[handleState] && e.drag.intersectStart.pointcloud){
- this.dispatchEvent({
- type : "transformPointcloud",
- intersectPoint: e.intersectPoint.orthoIntersect,
- intersectStart: e.drag.intersectStart.orthoIntersect,
- moveVec,
- pointcloud: e.drag.intersectStart.pointcloud,
- })
- }else{
-
- this.translationWorldDelta.add(moveVec.negate())
-
- }
-
-
-
- }else{
- if(e.drag.intersectStart){//如果拖拽着点云
-
- if(e.drag.z == void 0){//拖拽开始
- let pointerStartPos2d = e.drag.intersectStart.location.clone().project(camera);//识别到的点云点的位置
- e.drag.z = pointerStartPos2d.z //记录z,保持拖拽物体到屏幕距离不变,所以z深度不变
- e.drag.projectionMatrixInverse = camera.projectionMatrixInverse.clone()
- //防止吸附到最近点上(因为鼠标所在位置并非识别到的点云点的位置,需要得到鼠标所在位置的3d坐标。)
- let pointerStartPos2dReal = new THREE.Vector3(e.drag.pointerDragStart.x,e.drag.pointerDragStart.y, e.drag.z);
- e.drag.translateStartPos = pointerStartPos2dReal.clone().unproject(camera);
- /* this.viewer.dispatchEvent({
- type: 'dragPanBegin',
- projectionMatrixInverse : e.drag.projectionMatrixInverse
- }); */
- }
- //拖拽的过程中将projectionMatrixInverse替换成开始拖拽时的,因为near、far一直在变,会导致unproject计算出的3d坐标改变很大而闪烁。
- var _projectionMatrixInverse = camera.projectionMatrixInverse;
- camera.projectionMatrixInverse = e.drag.projectionMatrixInverse;
-
-
- let newPos2d = new THREE.Vector3(e.drag.pointer.x,e.drag.pointer.y, e.drag.z );
- let newPos3d = newPos2d.clone().unproject(camera);
- let moveVec = newPos3d.clone().sub( e.drag.translateStartPos /* e.drag.intersectStart.location */ );//移动相机,保持鼠标下的位置永远不变,所以用鼠标下的新位置减去鼠标下的原始位置
-
-
- camera.projectionMatrixInverse = _projectionMatrixInverse
- this.translationWorldDelta.copy(moveVec.negate()) //这里没法用add,原因未知,会跳动
-
- }else{ //如果鼠标没有找到和点云的交点,就假设移动整个模型(也可以去扩大范围寻找最近点云)
-
- /* let center = viewer.scene.pointclouds[0].position;
- let radius = camera.position.distanceTo(center);
- let ratio = radius * Math.tan(THREE.Math.degToRad(camera.fov)/2) / 1000 */
-
-
- /* let speed = this.currentViewport.getMoveSpeed()
- if(FirstPersonControls.boundPlane){
- speed = FirstPersonControls.boundPlane.distanceToPoint(this.currentViewport.position)
- speed = Math.max(1 , speed)
- } */
- let lastIntersect = viewport.lastIntersect ? (viewport.lastIntersect.location || viewport.lastIntersect) : viewer.bound.center //该viewport的最近一次鼠标和点云的交点
- let speed = camera.position.distanceTo(lastIntersect)
- let fov = cameraLight.getHFOVForCamera(camera, camera.aspect, 1)
- let ratio = speed * Math.tan(THREE.Math.degToRad(fov)/2)
- this.translationDelta.x -= e.drag.pointerDelta.x * ratio
- this.translationDelta.z -= e.drag.pointerDelta.y * ratio
-
-
-
- }
- }
-
- }
- //最好按ctrl可以变为dollhouse的那种旋转
- };
- let drop = e => {
- if(!this.enabled)return
- this.dispatchEvent({type: 'end'});
- };
- let scroll = (e) => {
- if(!this.enabled)return
- this.setCurrentViewport(e)
- if(this.currentViewport.unableChangePos)return
- let camera = e.hoverViewport.camera
- let speed = this.currentViewport.getMoveSpeed() || 1
- if(camera.type == "OrthographicCamera"){
- let ratio
- if(e.delta == 0){//mac
- return
- }else if (e.delta < 0) {
- ratio = 0.9
- } else if (e.delta > 0) {
- ratio = 1.1
- }
- let zoom = camera.zoom * ratio
- let limit = Potree.config.OrthoCameraLimit.zoom
- zoom = THREE.Math.clamp(zoom, limit.min,limit.max )
-
- if(camera.zoom != zoom){
- camera.zoom = zoom
- camera.updateProjectionMatrix()
- }
-
-
-
- }else{
- var direction = this.currentViewport.view.direction.clone();
-
- var vec = direction.multiplyScalar(speed * 7)
- if (e.delta < 0) {
- this.translationWorldDelta.copy(vec.negate())
- } else if (e.delta > 0) {
- this.translationWorldDelta.copy(vec)
- }
- }
-
-
- };
- let dblclick = (e) => {
- if(!this.enabled)return
-
- if(!Potree.settings.dblToFocusPoint)return;//调试时才可双击
-
- if(Potree.settings.displayMode == 'showPointCloud'/* !viewer.images360.isAtPano() */) this.zoomToLocation(e.mouse);
- };
- this.viewer.addEventListener('global_drag', drag);
- this.viewer.addEventListener('global_drop', drop);
- this.viewer.addEventListener('global_mousewheel', scroll);
- this.viewer.addEventListener('global_dblclick', dblclick);
-
- this.viewer.addEventListener('startDragging', this.setCurrentViewport.bind(this))
- /* this.viewer.addEventListener('enableChangePos', (e)=>{
- if(!this.enabled)return
- this.enableChangePos = e.canLeavePano
- }) */
-
- }
-
- setEnable(enabled){
- this.enabled = enabled;
-
- }
- setFPCMoveSpeed(viewport){
- if(viewport.camera.type == 'OrthographicCamera'){
- let s = 1 / viewport.camera.zoom
- viewport.setMoveSpeed(s)
-
- }else{
- if(viewport == viewer.mainViewport && FirstPersonControls.boundPlane){
- let s = FirstPersonControls.boundPlane.distanceToPoint(viewer.mainViewport.view.position)
- s = Math.sqrt(s) / 10;
- s = Math.max(FirstPersonControls.standardSpeed , s)
- s *= Potree.config.moveSpeedAdujust;
- viewer.setMoveSpeed(s)
- }
-
- }
-
- }
- setCurrentViewport(o={}){//add
- if(!this.enabled && !o.force)return
- if(o.hoverViewport && this.currentViewport != o.hoverViewport ){
- this.currentViewport = o.hoverViewport
-
- if(this.currentViewport.camera.type == 'OrthographicCamera'){
- this.lockElevationOri = true
- this.lockRotation = true
- }else{
- this.lockElevationOri = false
- this.lockRotation = false
- }
-
-
-
- //this.viewer.setMoveSpeed(this.currentViewport.radius/100);
- this.setFPCMoveSpeed(this.currentViewport)
- }
- }
-
- setScene (scene) {
- this.scene = scene;
-
- }
- stop(){
- this.yawDelta = 0;
- this.pitchDelta = 0;
- this.translationDelta.set(0, 0, 0);
- }
-
-
-
- zoomToLocation(mouse){
- if(!this.enabled)return
- let camera = this.scene.getActiveCamera();
-
- /* let I = Utils.getMousePointCloudIntersection(
- mouse,
- camera,
- this.viewer,
- this.scene.pointclouds); */
- var I = this.viewer.inputHandler.intersectPoint
- if (!I) {
- return;
- }
- let targetRadius = 0;
- {
- let minimumJumpDistance = 0.2;
- let domElement = this.renderer.domElement;
-
- let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer, camera);
- let {origin, direction} = this.viewer.inputHandler.getMouseDirection()
- let raycaster = new THREE.Raycaster();
- raycaster.ray.set(origin, direction);
-
- let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray);
-
- let nodes2 = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, raycaster.ray);
-
-
- let lastNode = nodes[nodes.length - 1];
- let radius = lastNode.getBoundingSphere(new THREE.Sphere()).radius;
- targetRadius = Math.min(this.scene.view.radius, radius);
- targetRadius = Math.max(minimumJumpDistance, targetRadius);
- }
- let d = this.scene.view.direction.multiplyScalar(-1);
- let cameraTargetPosition = new THREE.Vector3().addVectors(I.location, d.multiplyScalar(targetRadius));
- // TODO Unused: let controlsTargetPosition = I.location;
- let animationDuration = 600;
- let easing = TWEEN.Easing.Quartic.Out;
- { // animate
- let value = {x: 0};
- let tween = new TWEEN.Tween(value).to({x: 1}, animationDuration);
- tween.easing(easing);
- this.tweens.push(tween);
- let startPos = this.scene.view.position.clone();
- let targetPos = cameraTargetPosition.clone();
- let startRadius = this.scene.view.radius;
- let targetRadius = cameraTargetPosition.distanceTo(I.location);
- tween.onUpdate(() => {
- let t = value.x;
- this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x;
- this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y;
- this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z;
- this.scene.view.radius = (1 - t) * startRadius + t * targetRadius;
- this.viewer.setMoveSpeed(this.scene.view.radius / 2.5);
- });
- tween.onComplete(() => {
- this.tweens = this.tweens.filter(e => e !== tween);
- });
- tween.start();
- }
- }
- update (delta) {
- if(!this.enabled)return
-
-
- let view = this.currentViewport.view
- { // cancel move animations on user input
- let changes = [ this.yawDelta,
- this.pitchDelta,
- this.translationDelta.length(),
- this.translationWorldDelta.length() ];
- let changeHappens = changes.some(e => Math.abs(e) > 0.001);
- if (changeHappens && this.tweens.length > 0) {
- this.tweens.forEach(e => e.stop());
- this.tweens = [];
- }
- }
- { // accelerate while input is given
- let ih = this.viewer.inputHandler;
- let moveForward = this.keys.FORWARD.some(e => ih.pressedKeys[e]);
- let moveBackward = this.keys.BACKWARD.some(e => ih.pressedKeys[e]);
- let moveLeft = this.keys.LEFT.some(e => ih.pressedKeys[e]);
- let moveRight = this.keys.RIGHT.some(e => ih.pressedKeys[e]);
- let moveUp = this.keys.UP.some(e => ih.pressedKeys[e]);
- let moveDown = this.keys.DOWN.some(e => ih.pressedKeys[e]);
-
- let rotateLeft = this.keys.Rotate_LEFT.some(e => ih.pressedKeys[e]);
- let rotateRight = this.keys.Rotate_RIGHT.some(e => ih.pressedKeys[e]);
- let rotateUp = this.keys.Rotate_UP.some(e => ih.pressedKeys[e]);
- let rotateDown = this.keys.Rotate_DOWN.some(e => ih.pressedKeys[e]);
-
- this.lockElevation = this.lockElevationOri || this.keys.ALT.some(e => ih.pressedKeys[e]);
-
-
-
- if(!this.lockRotation){
- if(rotateLeft){
- this.yawDelta -= 0.01
- }else if(rotateRight){
- this.yawDelta += 0.01
- }
- if(rotateUp){
- this.pitchDelta -= 0.01
- }else if(rotateDown){
- this.pitchDelta += 0.01
- }
- }
-
- if(!this.currentViewport.unableChangePos){
- if(this.lockElevation){
- let dir = view.direction;
- dir.z = 0;
- dir.normalize();
- if (moveForward && moveBackward) {
- this.translationWorldDelta.set(0, 0, 0);
- } else if (moveForward) {
- this.translationWorldDelta.copy(dir.multiplyScalar(this.currentViewport.getMoveSpeed()));
- } else if (moveBackward) {
- this.translationWorldDelta.copy(dir.multiplyScalar(-this.currentViewport.getMoveSpeed()));
- }
- }else{
- if (moveForward && moveBackward) {
- this.translationDelta.y = 0;
- } else if (moveForward) {
- this.translationDelta.y = this.currentViewport.getMoveSpeed();
- } else if (moveBackward) {
- this.translationDelta.y = -this.currentViewport.getMoveSpeed();
- }
- }
- if (moveLeft && moveRight) {
- this.translationDelta.x = 0;
- } else if (moveLeft) {
- this.translationDelta.x = -this.currentViewport.getMoveSpeed();
- } else if (moveRight) {
- this.translationDelta.x = this.currentViewport.getMoveSpeed();
- }
- if (moveUp && moveDown) {
- this.translationWorldDelta.z = 0;
- } else if (moveUp) {
- this.translationWorldDelta.z = this.currentViewport.getMoveSpeed();
- } else if (moveDown) {
- this.translationWorldDelta.z = -this.currentViewport.getMoveSpeed();
- }
- }
- }
- { // apply rotation
- let yaw = view.yaw;
- let pitch = view.pitch;
- yaw += this.yawDelta /* * delta; */
- pitch += this.pitchDelta/* * delta; */
- view.yaw = yaw;
- view.pitch = pitch;
-
-
- this.yawDelta = 0
- this.pitchDelta = 0
- }
- if(this.translationWorldDelta.length()>0) {
- // console.log('translationDelta')
- }
- { // apply translation
- view.translate(
- this.translationDelta.x, /* * delta, */
- this.translationDelta.y, /* * delta, */
- this.translationDelta.z, /* * delta */
- );
- this.translationDelta.set(0,0,0)
- //if(this.translationWorldDelta.length())console.log(translationWorldDelta)
-
- view.translateWorld(
- this.translationWorldDelta.x /* * delta */,
- this.translationWorldDelta.y /* * delta */,
- this.translationWorldDelta.z /* * delta */
- );
- this.translationWorldDelta.set(0,0,0)
- }
- { // set view target according to speed
- //view.radius = 1 * this.currentViewport.getMoveSpeed();
-
- /* if(viewer.bound) view.radius = view.position.distanceTo(viewer.bound.center)
- let speed = view.radius/100;
- this.viewer.setMoveSpeed(speed); */
- //this.setMoveSpeed()
-
-
- }
-
- { // decelerate over time
- let attenuation = Math.max(0, 1 - this.fadeFactor * delta);
- /* this.yawDelta *= attenuation;
- this.pitchDelta *= attenuation;
- this.translationDelta.multiplyScalar(attenuation);
- this.translationWorldDelta.multiplyScalar(attenuation);*/
- }
- }
- };
|