FirstPersonControls.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /**
  2. * @author mschuetz / http://mschuetz.at
  3. *
  4. * adapted from THREE.OrbitControls by
  5. *
  6. * @author qiao / https://github.com/qiao
  7. * @author mrdoob / http://mrdoob.com
  8. * @author alteredq / http://alteredqualia.com/
  9. * @author WestLangley / http://github.com/WestLangley
  10. * @author erich666 / http://erichaines.com
  11. *
  12. *
  13. *
  14. */
  15. import * as THREE from "../../libs/three.js/build/three.module.js";
  16. import {MOUSE} from "../defines.js";
  17. import {Utils} from "../utils.js";
  18. import {EventDispatcher} from "../EventDispatcher.js";
  19. import cameraLight from "../utils/cameraLight.js";
  20. export class FirstPersonControls extends EventDispatcher {
  21. constructor (viewer, viewport) {
  22. super();
  23. this.viewer = viewer;
  24. this.renderer = viewer.renderer;
  25. this.scene = viewer.scene;
  26. this.rotationSpeed = 200;
  27. this.moveSpeed = 10;
  28. this.setCurrentViewport({hoverViewport:viewport, force:true}) //this.currentViewport = viewport
  29. this.keys = {
  30. FORWARD: ['W'.charCodeAt(0), 38],
  31. BACKWARD: ['S'.charCodeAt(0), 40],
  32. LEFT: ['A'.charCodeAt(0), 37],
  33. RIGHT: ['D'.charCodeAt(0), 39],
  34. UP: ['Q'.charCodeAt(0)],
  35. DOWN: ['E'.charCodeAt(0)],
  36. //SHIFT : [16],
  37. ALT : [18],
  38. Rotate_LEFT : ['L'.charCodeAt(0)],
  39. Rotate_RIGHT : ['J'.charCodeAt(0)],
  40. Rotate_UP : ['K'.charCodeAt(0)],
  41. Rotate_DOWN : ['I'.charCodeAt(0)],
  42. };
  43. this.fadeFactor = 50;
  44. this.yawDelta = 0;
  45. this.pitchDelta = 0;
  46. this.translationDelta = new THREE.Vector3(0, 0, 0);
  47. this.translationWorldDelta = new THREE.Vector3(0, 0, 0);
  48. this.tweens = [];
  49. //this.enableChangePos = true
  50. this.viewer.addEventListener('camera_changed',(e)=>{
  51. this.setFPCMoveSpeed(e.viewport)
  52. })
  53. let drag = (e) => {
  54. if(!this.enabled)return
  55. let viewport = e.drag.dragViewport;
  56. if(!viewport)return
  57. let camera = viewport.camera
  58. let mode = e.drag.mouse === MOUSE.LEFT && (!e.drag.dragViewport || e.drag.dragViewport.name == 'MainView') ? 'rotate' : 'pan'
  59. let moveSpeed = this.currentViewport.getMoveSpeed();
  60. if (e.drag.startHandled === undefined) {///???????
  61. e.drag.startHandled = true;
  62. this.dispatchEvent({type: 'start'});
  63. }
  64. if (mode == 'rotate') {//旋转 (为什么开启调试时旋转很慢?)
  65. //来自panoramaControl updateRotation
  66. let _matrixWorld = camera.matrixWorld
  67. camera.matrixWorld = new THREE.Matrix4;//unproject 前先把相机置于原点
  68. var e1 = new THREE.Vector3(e.drag.pointerDragStart.x,e.drag.pointerDragStart.y,-1).unproject(camera)
  69. , t = new THREE.Vector3(e.drag.pointer.x,e.drag.pointer.y,-1).unproject(camera)
  70. , i = Math.sqrt(e1.x * e1.x + e1.z * e1.z)
  71. , n = Math.sqrt(t.x * t.x + t.z * t.z)
  72. , o = Math.atan2(e1.y, i)
  73. , a = Math.atan2(t.y, n);
  74. this.pitchDelta += o - a //上下旋转
  75. e1.y = 0,
  76. t.y = 0;
  77. var s = Math.acos(e1.dot(t) / e1.length() / t.length());
  78. if(!isNaN(s)){
  79. var yawDelta = s //左右旋转
  80. e.drag.pointerDragStart.x > e.drag.pointer.x && (yawDelta *= -1)
  81. this.yawDelta += yawDelta
  82. }
  83. e.drag.pointerDragStart.copy(e.drag.pointer)
  84. camera.matrixWorld = _matrixWorld ;
  85. } else if (mode == 'pan') {//平移
  86. if(viewport.unableChangePos)return
  87. if(camera.type == "OrthographicCamera"){
  88. /* let ViewWidthPX = viewport.width * viewer.renderer.domElement.clientWidth
  89. let ViewHeightPX = viewport.height * viewer.renderer.domElement.clientHeight
  90. let cameraViewWidth = camera.right * 2
  91. let cameraViewHeight = camera.top * 2;
  92. moveVec.set(-1 * e.drag.mouseDelta.x * cameraViewWidth / ViewWidthPX, e.drag.mouseDelta.y * cameraViewHeight / ViewHeightPX , 0).applyQuaternion(camera.quaternion)
  93. */
  94. let moveVec = Utils.getOrthoCameraMoveVec(e.drag.pointerDelta, camera )//最近一次移动向量
  95. let handleState = window.viewer.modules.Alignment.handleState
  96. if(viewport.alignment && handleState && viewport.alignment[handleState] && e.drag.intersectStart.pointcloud){
  97. this.dispatchEvent({
  98. type : "transformPointcloud",
  99. intersectPoint: e.intersectPoint.orthoIntersect,
  100. intersectStart: e.drag.intersectStart.orthoIntersect,
  101. moveVec,
  102. pointcloud: e.drag.intersectStart.pointcloud,
  103. })
  104. }else{
  105. this.translationWorldDelta.add(moveVec.negate())
  106. }
  107. }else{
  108. if(e.drag.intersectStart){//如果拖拽着点云
  109. if(e.drag.z == void 0){//拖拽开始
  110. let pointerStartPos2d = e.drag.intersectStart.location.clone().project(camera);//识别到的点云点的位置
  111. e.drag.z = pointerStartPos2d.z //记录z,保持拖拽物体到屏幕距离不变,所以z深度不变
  112. e.drag.projectionMatrixInverse = camera.projectionMatrixInverse.clone()
  113. //防止吸附到最近点上(因为鼠标所在位置并非识别到的点云点的位置,需要得到鼠标所在位置的3d坐标。)
  114. let pointerStartPos2dReal = new THREE.Vector3(e.drag.pointerDragStart.x,e.drag.pointerDragStart.y, e.drag.z);
  115. e.drag.translateStartPos = pointerStartPos2dReal.clone().unproject(camera);
  116. /* this.viewer.dispatchEvent({
  117. type: 'dragPanBegin',
  118. projectionMatrixInverse : e.drag.projectionMatrixInverse
  119. }); */
  120. }
  121. //拖拽的过程中将projectionMatrixInverse替换成开始拖拽时的,因为near、far一直在变,会导致unproject计算出的3d坐标改变很大而闪烁。
  122. var _projectionMatrixInverse = camera.projectionMatrixInverse;
  123. camera.projectionMatrixInverse = e.drag.projectionMatrixInverse;
  124. let newPos2d = new THREE.Vector3(e.drag.pointer.x,e.drag.pointer.y, e.drag.z );
  125. let newPos3d = newPos2d.clone().unproject(camera);
  126. let moveVec = newPos3d.clone().sub( e.drag.translateStartPos /* e.drag.intersectStart.location */ );//移动相机,保持鼠标下的位置永远不变,所以用鼠标下的新位置减去鼠标下的原始位置
  127. camera.projectionMatrixInverse = _projectionMatrixInverse
  128. this.translationWorldDelta.copy(moveVec.negate()) //这里没法用add,原因未知,会跳动
  129. }else{ //如果鼠标没有找到和点云的交点,就假设移动整个模型(也可以去扩大范围寻找最近点云)
  130. /* let center = viewer.scene.pointclouds[0].position;
  131. let radius = camera.position.distanceTo(center);
  132. let ratio = radius * Math.tan(THREE.Math.degToRad(camera.fov)/2) / 1000 */
  133. /* let speed = this.currentViewport.getMoveSpeed()
  134. if(FirstPersonControls.boundPlane){
  135. speed = FirstPersonControls.boundPlane.distanceToPoint(this.currentViewport.position)
  136. speed = Math.max(1 , speed)
  137. } */
  138. let lastIntersect = viewport.lastIntersect ? (viewport.lastIntersect.location || viewport.lastIntersect) : viewer.bound.center //该viewport的最近一次鼠标和点云的交点
  139. let speed = camera.position.distanceTo(lastIntersect)
  140. let fov = cameraLight.getHFOVForCamera(camera, camera.aspect, 1)
  141. let ratio = speed * Math.tan(THREE.Math.degToRad(fov)/2)
  142. this.translationDelta.x -= e.drag.pointerDelta.x * ratio
  143. this.translationDelta.z -= e.drag.pointerDelta.y * ratio
  144. }
  145. }
  146. }
  147. //最好按ctrl可以变为dollhouse的那种旋转
  148. };
  149. let drop = e => {
  150. if(!this.enabled)return
  151. this.dispatchEvent({type: 'end'});
  152. };
  153. let scroll = (e) => {
  154. if(!this.enabled)return
  155. this.setCurrentViewport(e)
  156. if(this.currentViewport.unableChangePos)return
  157. let camera = e.hoverViewport.camera
  158. let speed = this.currentViewport.getMoveSpeed() || 1
  159. if(camera.type == "OrthographicCamera"){
  160. let ratio
  161. if(e.delta == 0){//mac
  162. return
  163. }else if (e.delta < 0) {
  164. ratio = 0.9
  165. } else if (e.delta > 0) {
  166. ratio = 1.1
  167. }
  168. let zoom = camera.zoom * ratio
  169. let limit = Potree.config.OrthoCameraLimit.zoom
  170. zoom = THREE.Math.clamp(zoom, limit.min,limit.max )
  171. if(camera.zoom != zoom){
  172. camera.zoom = zoom
  173. camera.updateProjectionMatrix()
  174. }
  175. }else{
  176. var direction = this.currentViewport.view.direction.clone();
  177. var vec = direction.multiplyScalar(speed * 7)
  178. if (e.delta < 0) {
  179. this.translationWorldDelta.copy(vec.negate())
  180. } else if (e.delta > 0) {
  181. this.translationWorldDelta.copy(vec)
  182. }
  183. }
  184. };
  185. let dblclick = (e) => {
  186. if(!this.enabled)return
  187. if(!Potree.settings.dblToFocusPoint)return;//调试时才可双击
  188. if(Potree.settings.displayMode == 'showPointCloud'/* !viewer.images360.isAtPano() */) this.zoomToLocation(e.mouse);
  189. };
  190. this.viewer.addEventListener('global_drag', drag);
  191. this.viewer.addEventListener('global_drop', drop);
  192. this.viewer.addEventListener('global_mousewheel', scroll);
  193. this.viewer.addEventListener('global_dblclick', dblclick);
  194. this.viewer.addEventListener('startDragging', this.setCurrentViewport.bind(this))
  195. /* this.viewer.addEventListener('enableChangePos', (e)=>{
  196. if(!this.enabled)return
  197. this.enableChangePos = e.canLeavePano
  198. }) */
  199. }
  200. setEnable(enabled){
  201. this.enabled = enabled;
  202. }
  203. setFPCMoveSpeed(viewport){
  204. if(viewport.camera.type == 'OrthographicCamera'){
  205. let s = 1 / viewport.camera.zoom
  206. viewport.setMoveSpeed(s)
  207. }else{
  208. if(viewport == viewer.mainViewport && FirstPersonControls.boundPlane){
  209. let s = FirstPersonControls.boundPlane.distanceToPoint(viewer.mainViewport.view.position)
  210. s = Math.sqrt(s) / 10;
  211. s = Math.max(FirstPersonControls.standardSpeed , s)
  212. s *= Potree.config.moveSpeedAdujust;
  213. viewer.setMoveSpeed(s)
  214. }
  215. }
  216. }
  217. setCurrentViewport(o={}){//add
  218. if(!this.enabled && !o.force)return
  219. if(o.hoverViewport && this.currentViewport != o.hoverViewport ){
  220. this.currentViewport = o.hoverViewport
  221. if(this.currentViewport.camera.type == 'OrthographicCamera'){
  222. this.lockElevationOri = true
  223. this.lockRotation = true
  224. }else{
  225. this.lockElevationOri = false
  226. this.lockRotation = false
  227. }
  228. //this.viewer.setMoveSpeed(this.currentViewport.radius/100);
  229. this.setFPCMoveSpeed(this.currentViewport)
  230. }
  231. }
  232. setScene (scene) {
  233. this.scene = scene;
  234. }
  235. stop(){
  236. this.yawDelta = 0;
  237. this.pitchDelta = 0;
  238. this.translationDelta.set(0, 0, 0);
  239. }
  240. zoomToLocation(mouse){
  241. if(!this.enabled)return
  242. let camera = this.scene.getActiveCamera();
  243. /* let I = Utils.getMousePointCloudIntersection(
  244. mouse,
  245. camera,
  246. this.viewer,
  247. this.scene.pointclouds); */
  248. var I = this.viewer.inputHandler.intersectPoint
  249. if (!I) {
  250. return;
  251. }
  252. let targetRadius = 0;
  253. {
  254. let minimumJumpDistance = 0.2;
  255. let domElement = this.renderer.domElement;
  256. let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer, camera);
  257. let {origin, direction} = this.viewer.inputHandler.getMouseDirection()
  258. let raycaster = new THREE.Raycaster();
  259. raycaster.ray.set(origin, direction);
  260. let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray);
  261. let nodes2 = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, raycaster.ray);
  262. let lastNode = nodes[nodes.length - 1];
  263. let radius = lastNode.getBoundingSphere(new THREE.Sphere()).radius;
  264. targetRadius = Math.min(this.scene.view.radius, radius);
  265. targetRadius = Math.max(minimumJumpDistance, targetRadius);
  266. }
  267. let d = this.scene.view.direction.multiplyScalar(-1);
  268. let cameraTargetPosition = new THREE.Vector3().addVectors(I.location, d.multiplyScalar(targetRadius));
  269. // TODO Unused: let controlsTargetPosition = I.location;
  270. let animationDuration = 600;
  271. let easing = TWEEN.Easing.Quartic.Out;
  272. { // animate
  273. let value = {x: 0};
  274. let tween = new TWEEN.Tween(value).to({x: 1}, animationDuration);
  275. tween.easing(easing);
  276. this.tweens.push(tween);
  277. let startPos = this.scene.view.position.clone();
  278. let targetPos = cameraTargetPosition.clone();
  279. let startRadius = this.scene.view.radius;
  280. let targetRadius = cameraTargetPosition.distanceTo(I.location);
  281. tween.onUpdate(() => {
  282. let t = value.x;
  283. this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x;
  284. this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y;
  285. this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z;
  286. this.scene.view.radius = (1 - t) * startRadius + t * targetRadius;
  287. this.viewer.setMoveSpeed(this.scene.view.radius / 2.5);
  288. });
  289. tween.onComplete(() => {
  290. this.tweens = this.tweens.filter(e => e !== tween);
  291. });
  292. tween.start();
  293. }
  294. }
  295. update (delta) {
  296. if(!this.enabled)return
  297. let view = this.currentViewport.view
  298. { // cancel move animations on user input
  299. let changes = [ this.yawDelta,
  300. this.pitchDelta,
  301. this.translationDelta.length(),
  302. this.translationWorldDelta.length() ];
  303. let changeHappens = changes.some(e => Math.abs(e) > 0.001);
  304. if (changeHappens && this.tweens.length > 0) {
  305. this.tweens.forEach(e => e.stop());
  306. this.tweens = [];
  307. }
  308. }
  309. { // accelerate while input is given
  310. let ih = this.viewer.inputHandler;
  311. let moveForward = this.keys.FORWARD.some(e => ih.pressedKeys[e]);
  312. let moveBackward = this.keys.BACKWARD.some(e => ih.pressedKeys[e]);
  313. let moveLeft = this.keys.LEFT.some(e => ih.pressedKeys[e]);
  314. let moveRight = this.keys.RIGHT.some(e => ih.pressedKeys[e]);
  315. let moveUp = this.keys.UP.some(e => ih.pressedKeys[e]);
  316. let moveDown = this.keys.DOWN.some(e => ih.pressedKeys[e]);
  317. let rotateLeft = this.keys.Rotate_LEFT.some(e => ih.pressedKeys[e]);
  318. let rotateRight = this.keys.Rotate_RIGHT.some(e => ih.pressedKeys[e]);
  319. let rotateUp = this.keys.Rotate_UP.some(e => ih.pressedKeys[e]);
  320. let rotateDown = this.keys.Rotate_DOWN.some(e => ih.pressedKeys[e]);
  321. this.lockElevation = this.lockElevationOri || this.keys.ALT.some(e => ih.pressedKeys[e]);
  322. if(!this.lockRotation){
  323. if(rotateLeft){
  324. this.yawDelta -= 0.01
  325. }else if(rotateRight){
  326. this.yawDelta += 0.01
  327. }
  328. if(rotateUp){
  329. this.pitchDelta -= 0.01
  330. }else if(rotateDown){
  331. this.pitchDelta += 0.01
  332. }
  333. }
  334. if(!this.currentViewport.unableChangePos){
  335. if(this.lockElevation){
  336. let dir = view.direction;
  337. dir.z = 0;
  338. dir.normalize();
  339. if (moveForward && moveBackward) {
  340. this.translationWorldDelta.set(0, 0, 0);
  341. } else if (moveForward) {
  342. this.translationWorldDelta.copy(dir.multiplyScalar(this.currentViewport.getMoveSpeed()));
  343. } else if (moveBackward) {
  344. this.translationWorldDelta.copy(dir.multiplyScalar(-this.currentViewport.getMoveSpeed()));
  345. }
  346. }else{
  347. if (moveForward && moveBackward) {
  348. this.translationDelta.y = 0;
  349. } else if (moveForward) {
  350. this.translationDelta.y = this.currentViewport.getMoveSpeed();
  351. } else if (moveBackward) {
  352. this.translationDelta.y = -this.currentViewport.getMoveSpeed();
  353. }
  354. }
  355. if (moveLeft && moveRight) {
  356. this.translationDelta.x = 0;
  357. } else if (moveLeft) {
  358. this.translationDelta.x = -this.currentViewport.getMoveSpeed();
  359. } else if (moveRight) {
  360. this.translationDelta.x = this.currentViewport.getMoveSpeed();
  361. }
  362. if (moveUp && moveDown) {
  363. this.translationWorldDelta.z = 0;
  364. } else if (moveUp) {
  365. this.translationWorldDelta.z = this.currentViewport.getMoveSpeed();
  366. } else if (moveDown) {
  367. this.translationWorldDelta.z = -this.currentViewport.getMoveSpeed();
  368. }
  369. }
  370. }
  371. { // apply rotation
  372. let yaw = view.yaw;
  373. let pitch = view.pitch;
  374. yaw += this.yawDelta /* * delta; */
  375. pitch += this.pitchDelta/* * delta; */
  376. view.yaw = yaw;
  377. view.pitch = pitch;
  378. this.yawDelta = 0
  379. this.pitchDelta = 0
  380. }
  381. if(this.translationWorldDelta.length()>0) {
  382. // console.log('translationDelta')
  383. }
  384. { // apply translation
  385. view.translate(
  386. this.translationDelta.x, /* * delta, */
  387. this.translationDelta.y, /* * delta, */
  388. this.translationDelta.z, /* * delta */
  389. );
  390. this.translationDelta.set(0,0,0)
  391. //if(this.translationWorldDelta.length())console.log(translationWorldDelta)
  392. view.translateWorld(
  393. this.translationWorldDelta.x /* * delta */,
  394. this.translationWorldDelta.y /* * delta */,
  395. this.translationWorldDelta.z /* * delta */
  396. );
  397. this.translationWorldDelta.set(0,0,0)
  398. }
  399. { // set view target according to speed
  400. //view.radius = 1 * this.currentViewport.getMoveSpeed();
  401. /* if(viewer.bound) view.radius = view.position.distanceTo(viewer.bound.center)
  402. let speed = view.radius/100;
  403. this.viewer.setMoveSpeed(speed); */
  404. //this.setMoveSpeed()
  405. }
  406. { // decelerate over time
  407. let attenuation = Math.max(0, 1 - this.fadeFactor * delta);
  408. /* this.yawDelta *= attenuation;
  409. this.pitchDelta *= attenuation;
  410. this.translationDelta.multiplyScalar(attenuation);
  411. this.translationWorldDelta.multiplyScalar(attenuation);*/
  412. }
  413. }
  414. };