ExtendView.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import {transitions, easing, lerp} from '../custom/utils/transitions.js'
  3. import math from '../custom/utils/math.js'
  4. import Common from '../custom/utils/Common.js'
  5. import {View} from './View.js'
  6. let sid = 0
  7. class ExtendView extends View {
  8. constructor () {
  9. super()
  10. this.yaw = 0 //Math.PI / 4; // = 4dkk lon + 90
  11. this._pitch = 0 //-Math.PI / 4; //上下旋转 = 4dkk lat
  12. this.sid = sid++
  13. this.LookTransition = 'LookTransition'+this.sid
  14. this.FlyTransition = 'FlyTransition'+this.sid
  15. }
  16. //add------
  17. applyToCamera(camera){
  18. camera.position.copy(this.position);
  19. camera.rotation.copy(this.rotation)
  20. camera.updateMatrix();
  21. camera.updateMatrixWorld();
  22. //camera.matrixWorldInverse.copy(camera.matrixWorld).invert();
  23. }
  24. get rotation(){
  25. var rotation = new THREE.Euler;
  26. rotation.order = "ZXY";
  27. rotation.x = Math.PI / 2 + this.pitch;
  28. rotation.z = this.yaw;
  29. return rotation
  30. }
  31. set rotation(rotation){//这个在数字很小(垂直向上看)的时候水平旋转精度可能损失,导致突变到另一个角度去了,用 set quaternion比较好
  32. //因为 rotation的y不一定是0 , 所以不能直接逆着写。
  33. this.direction = new THREE.Vector3(0,0,-1).applyEuler(rotation)
  34. }
  35. get quaternion(){
  36. return new THREE.Quaternion().setFromEuler(this.rotation)
  37. }
  38. set quaternion(q){
  39. this.direction = new THREE.Vector3(0,0,-1).applyQuaternion(q) //注意如果得到的dir.x==dir.y==0, yaw不会变为0, 导致算的quaternion和q不一致
  40. }
  41. copy(a){
  42. Common.CopyClassObject(this, a, {ignoreList: ['_listeners']})
  43. }
  44. clone () {
  45. return Common.CloneClassObject(this, {ignoreList: ['_listeners']})
  46. }
  47. //----------
  48. setCubeView(dir) {
  49. switch(dir) {
  50. case "front":
  51. this.yaw = 0;
  52. this.pitch = 0;
  53. break;
  54. case "back":
  55. this.yaw = Math.PI;
  56. this.pitch = 0;
  57. break;
  58. case "left":
  59. this.yaw = -Math.PI / 2;
  60. this.pitch = 0;
  61. break;
  62. case "right":
  63. this.yaw = Math.PI / 2;
  64. this.pitch = 0;
  65. break;
  66. case "top":
  67. this.yaw = 0;
  68. this.pitch = -Math.PI / 2;
  69. break;
  70. case "bottom":
  71. this.yaw = -Math.PI;
  72. this.pitch = Math.PI / 2;
  73. break;
  74. }
  75. }
  76. /* pan (x, y) {
  77. let dir = new THREE.Vector3(0, 1, 0);
  78. dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), this.pitch);
  79. dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
  80. // let side = new THREE.Vector3(1, 0, 0);
  81. // side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
  82. let side = this.getSide();
  83. let up = side.clone().cross(dir);
  84. let pan = side.multiplyScalar(x).add(up.multiplyScalar(y));
  85. this.position = this.position.add(pan);
  86. // this.target = this.target.add(pan);
  87. } */
  88. pan (x, y) { //发现pan其实就是translate
  89. this.translate(x, 0, y)
  90. }
  91. translate (x, y, z, forceHorizon ) {
  92. //相机方向
  93. let dir = new THREE.Vector3(0, 1, 0);
  94. dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), forceHorizon ? 0 : this.pitch); //上下角度
  95. dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);//水平角度
  96. let side = new THREE.Vector3(1, 0, 0);
  97. side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw); //垂直于相机当前水平朝向的 左右方向
  98. let up = side.clone().cross(dir); //垂直于相机当前水平朝向的 向上方向
  99. let t = side.multiplyScalar(x) //x影响 左右分量
  100. .add(dir.multiplyScalar(y)) //y影响 前后分量
  101. .add(up.multiplyScalar(z)); //z影响 上下分量
  102. this.position = this.position.add(t);
  103. if((!math.closeTo(x, 0, 1e-4) || !math.closeTo(y, 0, 1e-4) || !math.closeTo(z, 0, 1e-4)) && Potree.settings.displayMode != 'showPanos'){
  104. this.cancelFlying('pos')
  105. }
  106. this.restrictPos()
  107. }
  108. translateWorld (x, y, z) {
  109. super.translateWorld(x, y, z)
  110. if((!math.closeTo(x, 0, 1e-4) || !math.closeTo(y, 0, 1e-4) || !math.closeTo(z, 0, 1e-4)) && Potree.settings.displayMode != 'showPanos'){
  111. this.cancelFlying('pos')
  112. }
  113. this.restrictPos()
  114. }
  115. restrictPos(position){//add
  116. if(this.limitBound){
  117. (position || this.position).clamp(this.limitBound.min, this.limitBound.max)
  118. }
  119. }
  120. isFlying(type='all'){
  121. let a = transitions.getById(this.FlyTransition).length > 0
  122. let b = transitions.getById(this.LookTransition).length > 0
  123. return type == 'pos' ? a : type == 'rotate' ? b : (a || b)
  124. }
  125. cancelFlying(type='all', dealCancel=true){//外界只能通过这个来cancel
  126. type == 'pos' ? transitions.cancelById(this.FlyTransition, dealCancel )
  127. : type == 'rotate' ? transitions.cancelById(this.LookTransition, dealCancel )
  128. : (transitions.cancelById(this.FlyTransition, dealCancel ), transitions.cancelById(this.LookTransition, dealCancel ))
  129. //console.log('cancelFlying ' , this.sid, type)
  130. }
  131. setView( info = {}){
  132. // position, target, duration = 0, callback = null, onUpdate = null, Easing='', cancelFun
  133. this.cancelFlying()
  134. let posWaitDone, rotWaitDone , dir
  135. let posDone = ()=>{
  136. rotWaitDone || done()
  137. posWaitDone = false
  138. }
  139. let rotDone = ()=>{
  140. if(endTarget){
  141. this.lookAt(endTarget); //compute radius for orbitcontrol
  142. }else if(endQuaternion){
  143. this.rotation = new THREE.Euler().setFromQuaternion(endQuaternion)
  144. }else if(endYaw != void 0){
  145. this.yaw = endYaw, this.pitch = endPitch
  146. }
  147. //if(dir.x == 0 && dir.y == 0)this.yaw = 0 //统一一下 朝上的话是正的。朝下的一般不是0,会保留一个接近0的小数所以不用管
  148. posWaitDone || done()
  149. rotWaitDone = false
  150. }
  151. let done = ()=>{ //一定要旋转和位移都结束了才能执行
  152. let f = ()=>{
  153. this.position.copy(endPosition) //因为延时1后control的update会导致位置改变
  154. info.callback && info.callback()
  155. this.dispatchEvent('flyingDone')
  156. }
  157. if(info.duration){
  158. setTimeout(f,1)//延迟是为了使isFlying先为false
  159. }else{
  160. f() //有的需要迅速执行回调
  161. }
  162. }
  163. let endPosition = new THREE.Vector3().copy(info.position)
  164. let startPosition = this.position.clone();
  165. let startQuaternion, endQuaternion, endTarget = null,
  166. endYaw, startYaw, endPitch, startPitch ;
  167. this.restrictPos(endPosition)
  168. if(info.endYaw != void 0) {
  169. startPitch = this.pitch
  170. endPitch = info.endPitch;
  171. startYaw = this.yaw
  172. endYaw = info.endYaw
  173. if(Math.abs(startYaw - endYaw)>Math.PI){//如果差距大于半个圆,就要反个方向转(把大的那个数字减去360度)
  174. startYaw > endYaw ? (startYaw -= Math.PI*2) : (endYaw -= Math.PI*2)
  175. }
  176. }else if(info.target ){
  177. endTarget = new THREE.Vector3().copy(info.target)
  178. endQuaternion = math.getQuaFromPosAim(endPosition,endTarget) //若为垂直,会自动偏向x负的方向
  179. dir = new THREE.Vector3().subVectors(endTarget, endPosition).normalize()
  180. //console.log(dir, this.direction)
  181. }else if(info.quaternion){
  182. endQuaternion = info.quaternion.clone()
  183. }
  184. if(endQuaternion){
  185. startQuaternion = this.quaternion
  186. }
  187. if(!info.duration){
  188. this.position.copy(endPosition);
  189. this.restrictPos()
  190. posWaitDone = true, rotWaitDone = true
  191. info.onUpdate && info.onUpdate(1)
  192. posDone()
  193. rotDone()
  194. }else{
  195. info.onUpdate && info.onUpdate(0) //初始化progress
  196. let posChange = !this.position.equals(endPosition)
  197. if(posChange){
  198. posWaitDone = true
  199. transitions.start(lerp.vector(this.position, endPosition, (pos, progress, delta)=>{
  200. info.onUpdate && info.onUpdate(progress, delta)
  201. }), info.duration, posDone , 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine ,null, this.FlyTransition, ()=>{
  202. //中途取消
  203. if(endTarget ){
  204. /* endPosition = new THREE.Vector3().copy(this.position)//更改旋转的endQuaternion
  205. endQuaternion = math.getQuaFromPosAim(endPosition,endTarget) */
  206. //直接改变endQuaternion会突变,所以还是cancel吧
  207. this.cancelFlying('rotate')
  208. }
  209. posWaitDone = false
  210. info.cancelFun && info.cancelFun()
  211. }, info.ignoreFirstFrame);
  212. }
  213. if(endQuaternion || endYaw != void 0){
  214. rotWaitDone = true
  215. transitions.start( (progress, delta )=>{
  216. if(endYaw != void 0){
  217. this.yaw = startYaw * (1-progress) + endYaw * progress
  218. this.pitch = startPitch * (1-progress) + endPitch * progress
  219. }else{
  220. let quaternion = (new THREE.Quaternion()).copy(startQuaternion)
  221. lerp.quaternion(quaternion, endQuaternion)(progress) //在垂直的视角下的角度突变的厉害,这时候可能渐变yaw比较好
  222. //console.log(quaternion)
  223. //this.rotation = new THREE.Euler().setFromQuaternion(quaternion)
  224. this.quaternion = quaternion
  225. }
  226. posChange || info.onUpdate && info.onUpdate(progress, delta)
  227. }, info.duration, rotDone , 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine ,null, this.LookTransition, ()=>{
  228. //中途取消
  229. rotWaitDone = false
  230. info.cancelFun && info.cancelFun()
  231. }, info.ignoreFirstFrame);
  232. }
  233. /* transitions.start(lerp.vector(this.position, endPosition, (pos, progress)=>{
  234. let t = progress
  235. if(endQuaternion){
  236. let quaternion = (new THREE.Quaternion()).copy(startQuaternion)
  237. lerp.quaternion(quaternion, endQuaternion)(progress),
  238. this.rotation = new THREE.Euler().setFromQuaternion(quaternion)
  239. }
  240. this.restrictPos()
  241. //console.log('setView flying')
  242. info.onUpdate && info.onUpdate(t)//add
  243. }), info.duration, done, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine ,null, this.LookTransition, info.cancelFun); //easeInOutQuad
  244. */
  245. }
  246. }
  247. //平移Ortho相机
  248. moveOrthoCamera(viewport, info, duration, easeName){//boundSize优先于endZoom。
  249. let camera = info.camera || viewport.camera
  250. let startZoom = camera.zoom
  251. let endPosition = info.endPosition
  252. let boundSize = info.boundSize
  253. let endZoom = info.endZoom
  254. let margin = info.margin || {x:0,y:0}/* 200 */ //像素
  255. let onUpdate = info.onUpdate
  256. if(info.bound){//需要修改boundSize以适应相机的旋转,当相机不在xy水平面上朝向z时
  257. endPosition = endPosition || info.bound.getCenter(new THREE.Vector3())
  258. let matrixRot = new THREE.Matrix4().makeRotationFromEuler(this.rotation).invert()
  259. let boundingBox = info.bound.clone().applyMatrix4(matrixRot)
  260. boundSize = boundingBox.getSize(new THREE.Vector3())
  261. }
  262. if(boundSize && boundSize.x == 0 && boundSize.y == 0){
  263. boundSize.set(1,1) //避免infinity
  264. }
  265. this.setView( Object.assign(info, { position:endPosition, duration,
  266. onUpdate:(progress, delta)=>{
  267. if(boundSize || endZoom){
  268. if(boundSize){
  269. let aspect = boundSize.x / boundSize.y
  270. let w, h;
  271. if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定
  272. h = boundSize.y
  273. endZoom = (viewport.resolution.y - margin.y) / h //注意,要在resolution不为0时执行
  274. }else{
  275. w = boundSize.x;
  276. endZoom = (viewport.resolution.x - margin.x) / w
  277. }
  278. //onUpdate时更新endzoom是因为画布大小可能更改
  279. }
  280. camera.zoom = endZoom * progress + startZoom * (1 - progress)
  281. camera.updateProjectionMatrix()
  282. onUpdate && onUpdate(progress, delta)
  283. }
  284. },
  285. Easing:easeName
  286. }))
  287. }
  288. zoomOrthoCamera(camera, endZoom, pointer, duration, onProgress){//定点缩放
  289. let startZoom = camera.zoom
  290. let pointerPos = new THREE.Vector3(pointer.x, pointer.y,0.5);
  291. transitions.start(( progress)=>{
  292. let oldPos = pointerPos.clone().unproject(camera);
  293. camera.zoom = endZoom * progress + startZoom * (1 - progress)
  294. camera.updateProjectionMatrix()
  295. let newPos = pointerPos.clone().unproject(camera);
  296. //定点缩放, 恢复一下鼠标所在位置的位置改变量
  297. let moveVec = new THREE.Vector3().subVectors(newPos, oldPos)
  298. camera.position.sub(moveVec)
  299. this.position.copy(camera.position)
  300. onProgress && onProgress()
  301. } , duration, null/* done */, 0, easing.easeInOutSine, null, "zoomInView"/* , info.cancelFun */);
  302. }
  303. tranCamera(viewport, info, duration, easeName){
  304. viewport.camera = info.midCamera
  305. //viewport.camera.matrixWorld = info.endCamera.matrixWorld
  306. //viewer.setCameraMode(CameraMode.ORTHOGRAPHIC)
  307. info.midCamera.projectionMatrix.copy(info.startCamera.projectionMatrix)
  308. let onUpdate = info.onUpdate
  309. info.onUpdate = (progress, delta)=>{
  310. lerp.matrix4(info.midCamera.projectionMatrix, info.endCamera.projectionMatrix)(progress)
  311. onUpdate && onUpdate(progress, delta)
  312. }
  313. let callback = info.callback
  314. info.callback = ()=>{
  315. viewport.camera = info.endCamera
  316. viewer.scene.measurements.forEach((e)=>{
  317. Potree.Utils.updateVisible(e, 'tranCamera', true)
  318. })
  319. this.applyToCamera(viewport.camera)
  320. viewer.dispatchEvent({type:'camera_changed', viewport:viewer.mainViewport, changeInfo:{}})//update sprite
  321. callback && callback()
  322. }
  323. //info.forbitCancel = true
  324. info.camera = info.endCamera
  325. if(info.camera.type == "OrthographicCamera"){
  326. this.moveOrthoCamera(viewport, info, duration, easeName)
  327. }else{
  328. this.setView( Object.assign(info, { duration}) )
  329. }
  330. }
  331. };
  332. //Object.assign(ExtendView.prototype, THREE.EventDispatcher.prototype)
  333. export {ExtendView}