FirstPersonControlsNew.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  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 {Utils} from "../utils.js";
  17. import cameraLight from "../custom/utils/cameraLight.js";
  18. import Common from "../custom/utils/Common.js";
  19. import math from "../custom/utils/math.js";
  20. let Buttons = Potree.defines.Buttons
  21. export class FirstPersonControls extends THREE.EventDispatcher {
  22. constructor (viewer, viewport) {
  23. super();
  24. this.viewer = viewer;
  25. this.renderer = viewer.renderer;
  26. this.scene = viewer.scene;
  27. this.rotationSpeed = 200;
  28. this.moveSpeed = 10;
  29. this.setCurrentViewport({hoverViewport:viewport, force:true}) //this.currentViewport = viewport
  30. this.keys = {
  31. FORWARD: ['W'.charCodeAt(0), 38],
  32. BACKWARD: ['S'.charCodeAt(0), 40],
  33. LEFT: ['A'.charCodeAt(0), 37],
  34. RIGHT: ['D'.charCodeAt(0), 39],
  35. UP: ['Q'.charCodeAt(0)],
  36. DOWN: ['E'.charCodeAt(0)],
  37. //SHIFT : [16],
  38. ALT : [18],
  39. Rotate_LEFT : ['L'.charCodeAt(0)],
  40. Rotate_RIGHT : ['J'.charCodeAt(0)],
  41. Rotate_UP : ['K'.charCodeAt(0)],
  42. Rotate_DOWN : ['I'.charCodeAt(0)],
  43. };
  44. this.fadeFactor = 20;
  45. this.yawDelta = 0;
  46. this.pitchDelta = 0;
  47. this.translationDelta = new THREE.Vector3(0, 0, 0);
  48. this.translationWorldDelta = new THREE.Vector3(0, 0, 0);
  49. this.tweens = [];
  50. this.dollyStart = new THREE.Vector2
  51. this.dollyEnd = new THREE.Vector2
  52. //this.enableChangePos = true
  53. this.viewer.addEventListener('camera_changed',(e)=>{
  54. if(this.viewer.name == 'mapViewer' || e.changeInfo && e.changeInfo.positionChanged && !viewer.mainViewport.view.isFlying() ){
  55. this.setFPCMoveSpeed(e.viewport)
  56. }
  57. })
  58. let drag = (e) => {
  59. if(!this.enabled)return
  60. let viewport = e.dragViewport;
  61. if(!viewport)return
  62. let camera = viewport.camera
  63. let mode
  64. if(e.isTouch){
  65. if(e.touches.length == 1){
  66. mode = (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'rotate' : 'pan'
  67. }else if(e.touches.length == 2){
  68. mode = Potree.settings.displayMode == 'showPanos' ? 'scale' : 'pan-scale'
  69. }else{
  70. mode = (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'pan' : 'scale'
  71. }
  72. }else{
  73. //mode = e.buttons === Buttons.LEFT && (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'rotate' : 'pan'
  74. mode = e.buttons === Buttons.LEFT && camera.type != 'OrthographicCamera' ? 'rotate' : 'pan'
  75. }
  76. //console.log('mode ', mode )
  77. let moveSpeed = this.currentViewport.getMoveSpeed();
  78. if (e.drag.startHandled === undefined) {///???????
  79. e.drag.startHandled = true;
  80. this.dispatchEvent({type: 'start'});
  81. }
  82. if (mode.includes('rotate')) {//旋转
  83. //来自panoramaControl updateRotation
  84. if(!this.pointerDragStart){
  85. return this.pointerDragStart = e.pointer.clone()
  86. }
  87. let view = this.scene.view;
  88. if(this.rotateStartInfo.rotAroundPoint){//定点旋转: 以当前intersect的点为target旋转,不改点在屏幕中的位置
  89. let distance = camera.position.distanceTo(this.rotateStartInfo.rotCenter/* this.intersectStart.location */) //不按下ctrl的话
  90. if(this.rotateStartInfo.rotCenter2d.z>1)distance *= -1 //在背面
  91. //按照orbitControl的方式旋转:
  92. let rotationSpeed = 2;
  93. this.yawDelta -= e.drag.pointerDelta.x * rotationSpeed;
  94. this.pitchDelta += e.drag.pointerDelta.y * rotationSpeed;
  95. /* //旋转方向和偏移量尽量和在漫游点处旋转方式的一样
  96. this.yawDelta += e.drag.pointerDelta.x / 500 * viewport.resolution.x
  97. this.pitchDelta -= e.drag.pointerDelta.y / 500 * viewport.resolution.y */
  98. //先更新一下相机:
  99. this.update()
  100. view.applyToCamera(camera)
  101. //然后得到新的相机角度下,原先点在屏幕中的位置所对应的3d点现在的坐标。只需要平移一下新旧坐标差值即可。//感觉貌似也可以用project和unproject后的差值的方式,还不用判断z背面
  102. let newPointerDir = viewer.inputHandler.getMouseDirection(this.rotateStartInfo.rotCenter2d).direction.clone().multiplyScalar(distance)
  103. let pivot = new THREE.Vector3().addVectors(camera.position, newPointerDir) //新的3d点
  104. let moveVec = new THREE.Vector3().subVectors(pivot, this.rotateStartInfo.rotCenter)
  105. this.translationWorldDelta.copy(moveVec.negate())
  106. //立即更新下,防止因update和此drag频率不同而打滑。
  107. this.update()
  108. view.applyToCamera(camera)
  109. }else{
  110. let _matrixWorld = camera.matrixWorld
  111. camera.matrixWorld = new THREE.Matrix4;//unproject 前先把相机置于原点
  112. var e1 = new THREE.Vector3(this.pointerDragStart.x,this.pointerDragStart.y,-1).unproject(camera)
  113. , t = new THREE.Vector3(e.pointer.x,e.pointer.y,-1).unproject(camera)
  114. , i = Math.sqrt(e1.x * e1.x + e1.z * e1.z)
  115. , n = Math.sqrt(t.x * t.x + t.z * t.z)
  116. , o = Math.atan2(e1.y, i)
  117. , a = Math.atan2(t.y, n);
  118. this.pitchDelta += o - a //上下旋转
  119. e1.y = 0,
  120. t.y = 0;
  121. var s = Math.acos(e1.dot(t) / e1.length() / t.length());
  122. if(!isNaN(s)){
  123. var yawDelta = s //左右旋转
  124. this.pointerDragStart.x > e.pointer.x && (yawDelta *= -1)
  125. this.yawDelta += yawDelta
  126. }
  127. //console.log('rotate:', this.pitchDelta, e.pointer.toArray(), this.pointerDragStart.toArray())
  128. this.pointerDragStart.copy(e.pointer)
  129. camera.matrixWorld = _matrixWorld ;
  130. }
  131. }
  132. if (mode.includes('pan')) {//平移
  133. if(!this.canMovePos(viewport)){
  134. return
  135. }
  136. if(camera.type == "OrthographicCamera"){
  137. //console.log(e.drag.pointerDelta, e.pointer, e.drag.end)
  138. let moveVec = Utils.getOrthoCameraMoveVec(e.drag.pointerDelta, camera )//最近一次移动向量
  139. let pointclouds;
  140. let Alignment = window.viewer.modules.Alignment
  141. let MergeEditor = window.viewer.modules.MergeEditor
  142. let handleState = Alignment.handleState
  143. //右键平移视图、左键操作点云
  144. let a = e.buttons === Buttons.LEFT && viewport.alignment && handleState && viewport.alignment[handleState]
  145. if(Potree.settings.editType == 'pano'){
  146. let PanoEditor = window.viewer.modules.PanoEditor
  147. if(a && PanoEditor.selectedPano){
  148. if(/* !PanoEditor.selectedGroup || */!PanoEditor.checkIfAllLinked({group:PanoEditor.selectedGroup}) ){
  149. if(handleState == 'translate' && ( e.drag.intersectStart.pointclouds && Common.getMixedSet(PanoEditor.selectedClouds, e.drag.intersectStart.pointclouds).length || PanoEditor.selectedPano.hovered)//平移时 拖拽到点云上 或 circle。(其中点云只需要intersect的点云中包含选择的点云中之一即可)
  150. || handleState == 'rotate' ) //旋转模式不需要intersect
  151. {
  152. pointclouds = PanoEditor.selectedClouds
  153. }
  154. }else{
  155. PanoEditor.dispatchEvent('needToDisConnect')
  156. console.warn('选中的漫游点连通了整个数据集,不允许移动')
  157. }
  158. }
  159. if(!pointclouds && e.buttons === Buttons.LEFT && viewport.rotateSide){//侧视图 (有时候会卡顿,是mousemove执行延迟了,一般发生在突然加载很多点云时)
  160. //console.log('rotateSide', -e.drag.pointerDelta.x )
  161. return PanoEditor.rotateSideCamera(-e.drag.pointerDelta.x)
  162. }
  163. }else if(Potree.settings.editType == 'merge'){
  164. if(e.buttons === Buttons.LEFT && viewport.rotateSide){
  165. return MergeEditor.rotateSideCamera(-e.drag.pointerDelta.x)
  166. }
  167. }else{
  168. /* if(Alignment.selectedClouds && Alignment.selectedClouds.length){//多个点云
  169. pointclouds = a && e.drag.intersectStart.pointclouds && Common.getMixedSet(Alignment.selectedClouds, e.drag.intersectStart.pointclouds).length && Alignment.selectedClouds
  170. }else{ */
  171. //pointclouds = a && e.drag.intersectStart.pointcloud && [e.drag.intersectStart.pointcloud]
  172. //}
  173. if(a && e.drag.intersectStart.pointcloud){
  174. pointclouds = [e.drag.intersectStart.pointcloud]
  175. if(e.drag.intersectStart.pointcloud.dataset_id == Potree.settings.originDatasetId){
  176. let p = e.drag.intersectStart.pointclouds.find(p=>p.dataset_id != Potree.settings.originDatasetId)
  177. if(p) pointclouds = [p]
  178. }
  179. }
  180. }
  181. if(pointclouds){
  182. if(handleState == 'translate' && viewport.alignment.translateVec){//只能沿某个方向移动
  183. moveVec.projectOnVector(viewport.alignment.translateVec)
  184. }
  185. this.dispatchEvent({
  186. type : "transformPointcloud",
  187. intersect: e.intersect.orthoIntersect,
  188. intersectStart: e.drag.intersectStart.orthoIntersect,
  189. moveVec,
  190. pointclouds,
  191. camera
  192. })
  193. }else{
  194. this.translationWorldDelta.add(moveVec.negate())
  195. }
  196. }else{ //perspectiveCamera:
  197. if(e.drag.intersectStart){//如果拖拽着点云
  198. let ifInit = e.drag.z == void 0
  199. let pointerStartPos2d = e.drag.intersectStart.location.clone().project(camera);//识别到的点云点的位置
  200. e.drag.z = pointerStartPos2d.z //记录z,保持拖拽物体到屏幕距离不变,所以z深度不变(如果拖拽过程中没有缩放,这个z其实不变)
  201. if(ifInit){//拖拽开始
  202. e.drag.projectionMatrixInverse = camera.projectionMatrixInverse.clone()
  203. //防止吸附到最近点上(因为鼠标所在位置并非识别到的点云点的位置,需要得到鼠标所在位置的3d坐标。)
  204. let pointerStartPos2dReal = new THREE.Vector3(this.pointerDragStart.x,this.pointerDragStart.y, e.drag.z);
  205. e.drag.translateStartPos = pointerStartPos2dReal.clone().unproject(camera);
  206. //console.log('开始拖拽', e.pointer.clone())
  207. }
  208. //拖拽的过程中将projectionMatrixInverse替换成开始拖拽时的,因为near、far一直在变,会导致unproject计算出的3d坐标改变很大而闪烁。
  209. var _projectionMatrixInverse = camera.projectionMatrixInverse;
  210. camera.projectionMatrixInverse = e.drag.projectionMatrixInverse;
  211. let newPos2d = new THREE.Vector3(e.pointer.x,e.pointer.y, e.drag.z );
  212. let newPos3d = newPos2d.clone().unproject(camera);
  213. let moveVec = newPos3d.clone().sub( e.drag.translateStartPos /* e.drag.intersectStart.location */ );//移动相机,保持鼠标下的位置永远不变,所以用鼠标下的新位置减去鼠标下的原始位置
  214. camera.projectionMatrixInverse = _projectionMatrixInverse
  215. this.translationWorldDelta.copy(moveVec.negate()) //这里没法用add,原因未知,打开console时会跳动
  216. //console.log('pan 1', this.translationWorldDelta.clone())
  217. //四指松开剩三指时会偏移一下,暂不知道哪里的问题,或许跟开头防止点云吸附有关?
  218. }else{ //如果鼠标没有找到和点云的交点,就假设移动整个模型(也可以去扩大范围寻找最近点云)
  219. /* let center = viewer.scene.pointclouds[0].position;
  220. let radius = camera.position.distanceTo(center);
  221. let ratio = radius * Math.tan(THREE.Math.degToRad(camera.fov)/2) / 1000 */
  222. /* let speed = this.currentViewport.getMoveSpeed()
  223. if(FirstPersonControls.boundPlane){
  224. speed = FirstPersonControls.boundPlane.distanceToPoint(this.currentViewport.position)
  225. speed = Math.max(1 , speed)
  226. } */
  227. let lastIntersect = this.target || viewport.lastIntersect && (viewport.lastIntersect.location || viewport.lastIntersect)//该viewport的最近一次鼠标和点云的交点
  228. if(!lastIntersect || !(lastIntersect instanceof THREE.Vector3))lastIntersect = viewer.bound.center
  229. let speed = camera.position.distanceTo(lastIntersect)
  230. let fov = cameraLight.getHFOVForCamera(camera, true)
  231. let ratio = speed * Math.tan(fov/2)
  232. this.translationDelta.x -= e.drag.pointerDelta.x * ratio
  233. this.translationDelta.z -= e.drag.pointerDelta.y * ratio
  234. //console.log('pan2', e.drag.pointerDelta)
  235. }
  236. }
  237. this.useAttenuation = false
  238. }
  239. if(mode.includes('scale')){//触屏缩放
  240. this.dollyEnd.subVectors(e.touches[0].pointer, e.touches[1].pointer);
  241. //if(!this.dollyStart)return
  242. var scale = this.dollyEnd.length() / this.dollyStart.length()
  243. let pointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
  244. dolly({
  245. pointer,
  246. scale, camera, drag:e.drag
  247. })
  248. this.dollyStart.copy(this.dollyEnd);
  249. }
  250. //最好按ctrl可以变为dollhouse的那种旋转
  251. };
  252. let drop = e => {
  253. if(!this.enabled)return
  254. this.dispatchEvent({type: 'end'});
  255. };
  256. let dolly = (e={})=>{
  257. if(Potree.settings.displayMode == 'showPanos' && this.currentViewport == viewer.mainViewport/* this.currentViewport.unableChangePos */){//全景时
  258. this.dispatchEvent({type:'dollyStopCauseUnable',delta:e.delta, scale:e.scale})
  259. return
  260. }
  261. let camera = e.camera
  262. if(camera.type == "OrthographicCamera"){
  263. let ratio
  264. if(e.delta != void 0){//滚轮缩放
  265. if(e.delta == 0){//mac
  266. return
  267. }else if (e.delta < 0) {
  268. ratio = 0.9
  269. } else if (e.delta > 0) {
  270. ratio = 1.12 //增加的需要比减少的多一些,否则缩放相同次数下,r0 * ([1+s)(1-s)]^n = r0 * (1-s^2)^n < r0
  271. }
  272. }else{
  273. ratio = e.scale //触屏缩放
  274. }
  275. let zoom = camera.zoom * ratio
  276. let limit = camera.zoomLimit
  277. if(limit) zoom = THREE.Math.clamp(zoom, limit.min,limit.max )
  278. let pointerPos = new THREE.Vector3(e.pointer.x, e.pointer.y,0.5);
  279. let oldPos = pointerPos.clone().unproject(camera);
  280. if(camera.zoom != zoom){
  281. camera.zoom = zoom
  282. camera.updateProjectionMatrix()
  283. }
  284. let newPos = pointerPos.clone().unproject(camera);
  285. //定点缩放, 恢复一下鼠标所在位置的位置改变量
  286. let moveVec = new THREE.Vector3().subVectors(newPos,oldPos)
  287. this.translationWorldDelta.add(moveVec.negate())
  288. this.useAttenuation = false
  289. }else{
  290. let speed = this.currentViewport.getMoveSpeed() * 15, direction
  291. if(e.delta != void 0){//滚轮缩放
  292. if(e.delta == 0)return //mac
  293. /*if(this.target && !e.intersect){//如果没有intersect点云且有target的话,就朝target的方向. 但无限靠近时有问题,且到背面时前进却是后退
  294. direction = new THREE.Vector3().subVectors(this.target, camera.position).normalize()
  295. }else{ */
  296. direction = this.viewer.inputHandler.getMouseDirection().direction //定点缩放
  297. if(e.intersect && e.intersect.location){//和intersect的墙越接近,速度越慢,便于focus细节
  298. let dis = camera.position.distanceTo(e.intersect.location);
  299. speed = THREE.Math.clamp(dis * 0.1, 0.3, speed)
  300. }
  301. if (e.delta < 0) {
  302. speed *= -1
  303. }
  304. this.useAttenuation = true
  305. }else{//触屏缩放
  306. direction = this.viewer.inputHandler.getMouseDirection(e.pointer).direction //定点缩放
  307. if(e.drag.intersectStart){//和intersect的墙越接近,速度越慢,便于focus细节
  308. let dis = camera.position.distanceTo(e.drag.intersectStart.location);
  309. let r = 1-1/e.scale
  310. let closeMin = 0.1, standardMin = 0.001, disBound1 = 2, disBound2 = 5
  311. if(math.closeTo(e.scale,1,0.03)){//如果偏差小于0.01,就不限制最小值,因为平移容易正负抖动,近距离有最小值的话抖动明显
  312. closeMin = 0 //所以若缩放不明显(双指滑动慢),就不设置最低值。(这时候穿越障碍物会比较困难。)
  313. }
  314. //console.log('closeMin',closeMin)
  315. let min = math.linearClamp(dis, disBound1, disBound2, closeMin, standardMin) //触屏和滚轮不一样,触发较为连续,所以最小值设低一点。若要保持双指相对点云位置不变,理想最小值是0,但那样就无法穿越点云(最小值太小的话穿越密集点云如树丛很困难;太大会打滑)所以当离点云近时增大最小值
  316. speed = Math.sign(r) * THREE.Math.clamp(dis * Math.abs(r), min, speed)
  317. //console.log(speed, dis, e.scale)
  318. }else{
  319. this.useAttenuation = true
  320. let accelerate = 80;
  321. if(math.closeTo(e.scale,1,0.02)){//缩放小的时候很可能是双指平移时,容易抖动,所以降低移动速度
  322. accelerate *= Math.min(40*Math.abs(e.scale-1), 0.8)
  323. }
  324. // console.log('accelerate',accelerate)
  325. const constantDis = this.currentViewport.getMoveSpeed() * accelerate //constantDis = 10;//常量系数,当放大一倍时前进的距离。可以调整
  326. speed = (e.scale-1)*constantDis
  327. }
  328. }
  329. var vec = direction.multiplyScalar(speed )
  330. //this.translationWorldDelta.copy(vec)
  331. this.translationWorldDelta.add(vec)
  332. //console.log(direction.toArray(), speed, e.scale)
  333. }
  334. return true
  335. }
  336. let scroll = (e) => {
  337. if(!this.enabled || !e.hoverViewport)return
  338. this.setCurrentViewport(e)
  339. e.camera = e.hoverViewport.camera
  340. dolly(e)
  341. };
  342. let dblclick = (e) => {
  343. if(!this.enabled)return
  344. if(!Potree.settings.dblToFocusPoint)return;//调试时才可双击
  345. if(Potree.settings.displayMode == 'showPointCloud'/* !viewer.images360.isAtPano() */) this.zoomToLocation(e.mouse);
  346. };
  347. this.viewer.addEventListener('global_drag', drag);
  348. /* this.viewer.addEventListener('global_touchmove', (e)=>{
  349. if(!this.enabled)return
  350. if(e.touches.length>1){//单指的就触发上一句
  351. //console.log('global_touchmove' )
  352. drag(e)
  353. }
  354. }); */
  355. this.viewer.addEventListener('global_drop', drop);
  356. this.viewer.addEventListener('global_mousewheel', scroll);
  357. this.viewer.addEventListener('global_dblclick', dblclick);
  358. let prepareScale = (e)=>{//触屏的scale
  359. this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer);
  360. e.drag.camDisToPointStart = null
  361. }
  362. let prepareRotate = (e)=>{
  363. this.pointerDragStart = e.pointer.clone()
  364. if(e.viewer.name != 'mainViewer' )return
  365. let intersect = e.intersect || e.dragViewport.lastIntersect
  366. //在数据集外部时绕中心点旋转,在数据集内部时绕intersect点旋转(其他数据集的点也可以) 或者 原地旋转镜头
  367. let rotAroundPoint = Potree.settings.rotAroundPoint && e.dragViewport.camera.type != 'OrthographicCamera' && (viewer.atDatasets.length == 0 || intersect) && this.canMovePos(viewport) && !viewer.images360.isAtPano() && !this.viewer.inputHandler.pressedKeys[32]
  368. let rotCenter2d, rotCenter
  369. if(rotAroundPoint){
  370. let pivotType = this.target ? 'target' : viewer.atDatasets.length > 0 ? 'intersect' : viewer.inputHandler.selection.length ? 'selection' : this.target2 ? 'target2' : 'boundCenter'
  371. rotCenter = pivotType == 'target'? this.target :pivotType == 'intersect' ? intersect.location : pivotType == 'selection' ? viewer.inputHandler.selection[0].position : pivotType == 'target2' ? this.target2 :viewer.bound.center
  372. if(rotCenter){
  373. rotCenter2d = rotCenter.clone().project(e.dragViewport.camera) //点在屏幕中的位置
  374. }else{
  375. rotAroundPoint = false
  376. }
  377. }
  378. this.rotateStartInfo = {
  379. rotAroundPoint, //定点旋转
  380. rotCenter,
  381. rotCenter2d
  382. }
  383. //缺点:多数据集绕中心点转很难操作,感觉可以也改为绕lastIntersect
  384. //console.log('prepareRotate' )
  385. }
  386. let preparePan = (e)=>{//触屏的pan点云 还是会偏移
  387. this.pointerDragStart = e.pointer.clone()
  388. e.drag.z = void 0 //清空
  389. drag(e) //触屏点击时更新的pointer直接用一次drag
  390. //console.log('preparePan ' )
  391. }
  392. this.viewer.addEventListener('global_mousedown'/* 'startDragging' */, (e)=>{
  393. if(!this.enabled)return
  394. this.setCurrentViewport(e)
  395. prepareRotate(e)
  396. })
  397. //注意,每次增减指头都会修改pointer,需要更新下状态
  398. this.viewer.addEventListener('global_touchstart', (e)=>{
  399. if(!this.enabled)return
  400. if(e.touches.length==2){//只监听开头两个指头
  401. prepareScale(e)
  402. preparePan(e)
  403. }else if(e.touches.length>=3){
  404. preparePan(e)
  405. }
  406. })
  407. this.viewer.addEventListener('global_touchend', (e)=>{//e.touches是剩余的指头
  408. if(!this.enabled)return
  409. if(e.touches.length==2){//停止平移,开始scale
  410. prepareScale(e)
  411. preparePan(e)
  412. }else if(e.touches.length==1){//停止scale,开始rotate
  413. prepareRotate(e)
  414. }else if(e.touches.length>=3){//重新准备下平移(因为抬起的指头可能包含平移使用的数据),否则抬起时漂移
  415. preparePan(e)
  416. }
  417. })
  418. /* this.viewer.addEventListener('enableChangePos', (e)=>{
  419. if(!this.enabled)return
  420. this.enableChangePos = e.canLeavePano
  421. }) */
  422. }
  423. canMovePos(viewport){
  424. if(viewport == viewer.mainViewport && (Potree.settings.displayMode == 'showPanos'
  425. || viewer.images360.bumping || viewer.images360.latestToPano))return false
  426. else return true
  427. }
  428. setEnable(enabled){
  429. this.enabled = enabled;
  430. }
  431. setTarget(target, index){//绕该点旋转,类似orbitControl
  432. if(index == 2)this.target2 = target
  433. else this.target = target
  434. }
  435. setFPCMoveSpeed(viewport){
  436. if(viewport.camera.type == 'OrthographicCamera'){
  437. let s = 1 / viewport.camera.zoom
  438. viewport.setMoveSpeed(s)
  439. }else{
  440. //根据和漫游点的最短距离算moveSpeed。缺点:对于导入的无漫游点的数据集没有意义。
  441. if(viewport == viewer.mainViewport && viewer.images360){
  442. let position = viewer.mainViewport.view.position
  443. let speed
  444. let pano = viewer.images360.findNearestPano()
  445. if(!pano){
  446. if(!viewer.bound)return
  447. let boundFloor = viewer.bound.boundingBox.clone();
  448. boundFloor.max.z = boundFloor.min.z
  449. speed = boundFloor.distanceToPoint(viewer.mainViewport.view.position)
  450. speed = Math.sqrt(speed) / 50;
  451. }else{
  452. let dis = pano.position.distanceTo(position)
  453. let minSpeed = 0.05, minDis = 3, multiplier = 0.005;
  454. speed = dis <= minDis ? minSpeed : minSpeed + (dis-minDis) * multiplier
  455. //console.log('dis', dis, 'speed', speed, pano.id )
  456. }
  457. viewer.setMoveSpeed(speed)
  458. }
  459. //调试场景t-FhDWmV5xur 两个数据集,大的数据集没有漫游点。
  460. }
  461. }
  462. setCurrentViewport(o={}){//add
  463. if(!this.enabled && !o.force )return
  464. if(o.hoverViewport && this.currentViewport != o.hoverViewport ){
  465. this.currentViewport = o.hoverViewport
  466. //this.viewer.setMoveSpeed(this.currentViewport.radius/100);
  467. this.setFPCMoveSpeed(this.currentViewport)
  468. }
  469. if(this.currentViewport.camera.type == 'OrthographicCamera'){
  470. this.lockElevationOri = true
  471. this.lockRotation = true
  472. }else{
  473. this.lockElevationOri = false
  474. this.lockRotation = false
  475. }
  476. }
  477. setScene (scene) {
  478. this.scene = scene;
  479. }
  480. stop(){
  481. this.yawDelta = 0;
  482. this.pitchDelta = 0;
  483. this.translationDelta.set(0, 0, 0);
  484. }
  485. zoomToLocation(mouse){
  486. if(!this.enabled)return
  487. let camera = this.scene.getActiveCamera();
  488. /* let I = Utils.getMousePointCloudIntersection(
  489. mouse,
  490. camera,
  491. this.viewer,
  492. this.scene.pointclouds); */
  493. var I = this.viewer.inputHandler.intersect
  494. if (!I) {
  495. return;
  496. }
  497. let targetRadius = 0;
  498. {
  499. let minimumJumpDistance = 0.2;
  500. let domElement = this.renderer.domElement;
  501. let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer, camera);
  502. let {origin, direction} = this.viewer.inputHandler.getMouseDirection()
  503. let raycaster = new THREE.Raycaster();
  504. raycaster.ray.set(origin, direction);
  505. let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray);
  506. let nodes2 = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, raycaster.ray);
  507. let lastNode = nodes[nodes.length - 1];
  508. let radius = lastNode.getBoundingSphere(new THREE.Sphere()).radius;
  509. targetRadius = Math.min(this.scene.view.radius, radius);
  510. targetRadius = Math.max(minimumJumpDistance, targetRadius);
  511. }
  512. let d = this.scene.view.direction.multiplyScalar(-1);
  513. let cameraTargetPosition = new THREE.Vector3().addVectors(I.location, d.multiplyScalar(targetRadius));
  514. // TODO Unused: let controlsTargetPosition = I.location;
  515. let animationDuration = 600;
  516. let easing = TWEEN.Easing.Quartic.Out;
  517. { // animate
  518. let value = {x: 0};
  519. let tween = new TWEEN.Tween(value).to({x: 1}, animationDuration);
  520. tween.easing(easing);
  521. this.tweens.push(tween);
  522. let startPos = this.scene.view.position.clone();
  523. let targetPos = cameraTargetPosition.clone();
  524. let startRadius = this.scene.view.radius;
  525. let targetRadius = cameraTargetPosition.distanceTo(I.location);
  526. tween.onUpdate(() => {
  527. let t = value.x;
  528. this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x;
  529. this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y;
  530. this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z;
  531. this.scene.view.radius = (1 - t) * startRadius + t * targetRadius;
  532. this.viewer.setMoveSpeed(this.scene.view.radius / 2.5);
  533. });
  534. tween.onComplete(() => {
  535. this.tweens = this.tweens.filter(e => e !== tween);
  536. });
  537. tween.start();
  538. }
  539. }
  540. update (delta=1) {
  541. if(!this.enabled)return
  542. //console.log('update')
  543. let view = this.currentViewport.view
  544. { // cancel move animations on user input
  545. let changes = [ this.yawDelta,
  546. this.pitchDelta,
  547. this.translationDelta.length(),
  548. this.translationWorldDelta.length() ];
  549. let changeHappens = changes.some(e => Math.abs(e) > 0.001);
  550. if (changeHappens && this.tweens.length > 0) {
  551. this.tweens.forEach(e => e.stop());
  552. this.tweens = [];
  553. }
  554. }
  555. { // accelerate while input is given
  556. let ih = this.viewer.inputHandler;
  557. let moveForward = this.keys.FORWARD.some(e => ih.pressedKeys[e]);
  558. let moveBackward = this.keys.BACKWARD.some(e => ih.pressedKeys[e]);
  559. let moveLeft = this.keys.LEFT.some(e => ih.pressedKeys[e]);
  560. let moveRight = this.keys.RIGHT.some(e => ih.pressedKeys[e]);
  561. let moveUp = this.keys.UP.some(e => ih.pressedKeys[e]);
  562. let moveDown = this.keys.DOWN.some(e => ih.pressedKeys[e]);
  563. let rotateLeft = this.keys.Rotate_LEFT.some(e => ih.pressedKeys[e]);
  564. let rotateRight = this.keys.Rotate_RIGHT.some(e => ih.pressedKeys[e]);
  565. let rotateUp = this.keys.Rotate_UP.some(e => ih.pressedKeys[e]);
  566. let rotateDown = this.keys.Rotate_DOWN.some(e => ih.pressedKeys[e]);
  567. this.lockElevation = this.lockElevationOri || this.keys.ALT.some(e => ih.pressedKeys[e]);
  568. if(!this.lockRotation){
  569. if(rotateLeft){
  570. this.yawDelta -= 0.01
  571. }else if(rotateRight){
  572. this.yawDelta += 0.01
  573. }
  574. if(rotateUp){
  575. this.pitchDelta -= 0.01
  576. }else if(rotateDown){
  577. this.pitchDelta += 0.01
  578. }
  579. }
  580. if(this.canMovePos(this.currentViewport) && !this.lockKey){
  581. if(this.lockElevation){
  582. let dir = view.direction;
  583. dir.z = 0;
  584. dir.normalize();
  585. if (moveForward && moveBackward) {
  586. this.translationWorldDelta.set(0, 0, 0);
  587. } else if (moveForward) {
  588. this.translationWorldDelta.copy(dir.multiplyScalar(this.currentViewport.getMoveSpeed()));
  589. } else if (moveBackward) {
  590. this.translationWorldDelta.copy(dir.multiplyScalar(-this.currentViewport.getMoveSpeed()));
  591. }
  592. }else{
  593. if (moveForward && moveBackward) {
  594. this.translationDelta.y = 0;
  595. } else if (moveForward) {
  596. this.translationDelta.y = this.currentViewport.getMoveSpeed();
  597. } else if (moveBackward) {
  598. this.translationDelta.y = -this.currentViewport.getMoveSpeed();
  599. }
  600. }
  601. if (moveLeft && moveRight) {
  602. this.translationDelta.x = 0;
  603. } else if (moveLeft) {
  604. this.translationDelta.x = -this.currentViewport.getMoveSpeed();
  605. } else if (moveRight) {
  606. this.translationDelta.x = this.currentViewport.getMoveSpeed();
  607. }
  608. if (moveUp && moveDown) {
  609. this.translationWorldDelta.z = 0;
  610. } else if (moveUp) {
  611. this.translationWorldDelta.z = this.currentViewport.getMoveSpeed();
  612. } else if (moveDown) {
  613. this.translationWorldDelta.z = -this.currentViewport.getMoveSpeed();
  614. }
  615. if(moveUp || moveDown || moveForward || moveBackward){
  616. this.useAttenuation = false
  617. }
  618. }
  619. }
  620. { // apply rotation
  621. let yaw = view.yaw;
  622. let pitch = view.pitch;
  623. yaw += this.yawDelta /* * delta; */
  624. pitch += this.pitchDelta/* * delta; */
  625. view.yaw = yaw;
  626. view.pitch = pitch;
  627. if(this.yawDelta || this.pitchDelta){
  628. view.cancelFlying('rotate')
  629. }
  630. this.yawDelta = 0
  631. this.pitchDelta = 0
  632. }
  633. /* if(this.translationWorldDelta.length()>0) {
  634. // console.log('translationDelta')
  635. } */
  636. { // apply translation
  637. view.translate(
  638. this.translationDelta.x, /* * delta, */
  639. this.translationDelta.y, /* * delta, */
  640. this.translationDelta.z, /* * delta */
  641. );
  642. this.translationDelta.set(0,0,0)
  643. //if(this.translationWorldDelta.length())console.log(translationWorldDelta)
  644. view.translateWorld(
  645. this.translationWorldDelta.x /* * delta */,
  646. this.translationWorldDelta.y /* * delta */,
  647. this.translationWorldDelta.z /* * delta */
  648. );
  649. //this.translationWorldDelta.set(0,0,0)
  650. }
  651. { // set view target according to speed
  652. //view.radius = 1 * this.currentViewport.getMoveSpeed();
  653. /* if(viewer.bound) view.radius = view.position.distanceTo(viewer.bound.center)
  654. let speed = view.radius/100;
  655. this.viewer.setMoveSpeed(speed); */
  656. //this.setMoveSpeed()
  657. }
  658. if(this.useAttenuation){ //只有滚轮缩放时开启
  659. let attenuation = Math.max(0, 1 - this.fadeFactor * delta);
  660. /*this.yawDelta *= attenuation;
  661. this.pitchDelta *= attenuation;
  662. this.translationDelta.multiplyScalar(attenuation);*/
  663. this.translationWorldDelta.multiplyScalar(attenuation);
  664. }else{
  665. this.translationWorldDelta.set(0,0,0)
  666. }
  667. }
  668. };