Magnifier.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. import * as THREE from "../../../libs/three.js/build/three.module.js";
  2. import math from '../utils/math.js'
  3. import browser from '../utils/browser.js'
  4. import Viewport from '../viewer/Viewport.js'
  5. const texLoader = new THREE.TextureLoader()
  6. const circleGeo = new THREE.CircleGeometry(1.45,100);
  7. const sphereGeo = new THREE.SphereBufferGeometry(0.018,10,10);
  8. const magDistance_ = 1;//相机离目标位置的距离的分界线,当离得远时要缩小fov以使看到的视野固定(望远镜效果)
  9. /* const radius_ = 0.2; //当相机离目标位置的距离>magDistance_时,希望看到的视野的半径
  10. const maxFov = THREE.Math.radToDeg(Math.atan(radius_ / magDistance_ )) * 2//提前计算出当相机离目标位置的距离<magDistance_时的fov,均使用=magDistance_时的fov。只要保证该fov大于主相机的fov就会有放大效果
  11. */
  12. let w = 200/1.43;
  13. let maxPX = 1366*1024 //ipad pro. 大于这个分辨率的就直接用devicePixelRatio, 如macbook也是
  14. const width2dPX = Math.round(window.devicePixelRatio >= 2 ? ( window.screen.width * window.screen.height >= maxPX ? window.devicePixelRatio/1.2 : window.devicePixelRatio/1.5)*w : w) //触屏或高分辨率的可能要放大些。但在手机上不能太大
  15. //console.log('width2dPX', width2dPX)
  16. export default class Magnifier extends THREE.Object3D {//放大镜or望远镜
  17. constructor (viewer) {
  18. super()
  19. this.width = this.height = width2dPX/* * window.devicePixelRatio */;
  20. this.camera = new THREE.PerspectiveCamera(50, 1, 0.01, 10000); //fov aspect near far
  21. this.camera.up = new THREE.Vector3(0,0,1)
  22. this.viewport = new Viewport( null, this.camera, {
  23. left:0, bottom:0, width:1, height: 1, name:'magnifier' , cameraLayers:['magnifierContent'],
  24. pixelRatio:1
  25. })
  26. this.viewport.setResolution(this.width, this.height,0,0)
  27. {
  28. let density
  29. let sizeType
  30. let colorType
  31. let opacityBefore = new Map()
  32. let visiMap = new Map()
  33. this.viewport.beforeRender = ()=>{
  34. viewer.scene.pointclouds.forEach(e=>{//因为更改pointDensity时会自动变opacity,所以这项最先获取
  35. visiMap.set(e,e.visible)
  36. e.visible = viewer.getObjVisiByReason(e, 'datasetSelection'); //先将隐藏的点云显示
  37. opacityBefore.set(e,e.temp.pointOpacity)
  38. })
  39. //使放大镜里的pointDensity是'magnifier' 最高质量。
  40. density = Potree.settings.pointDensity
  41. Potree.settings.pointDensity = 'magnifier'
  42. viewer.scene.pointclouds.forEach(e=>{//因为全景模式的pointSizeType是fixed所以要还原下
  43. sizeType = e.material.pointSizeType
  44. e.material.pointSizeType = Potree.config.material.pointSizeType
  45. //材质
  46. colorType = e.material.activeAttributeName
  47. e.material.activeAttributeName = 'rgba'
  48. e.changePointOpacity(1)
  49. })
  50. };
  51. this.viewport.afterRender = ()=>{
  52. Potree.settings.pointDensity = density
  53. viewer.scene.pointclouds.forEach(e=>{
  54. e.visible = visiMap.get(e)
  55. e.material.pointSizeType = sizeType
  56. e.material.activeAttributeName = colorType
  57. e.changePointOpacity(opacityBefore.get(e))
  58. })
  59. }
  60. }
  61. this.renderTarget = new THREE.WebGLRenderTarget(this.width,this.height, {
  62. minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter,
  63. format: THREE.RGBAFormat ,
  64. /* type: THREE.FloatType,
  65. minFilter: THREE.NearestFilter,
  66. magFilter: THREE.NearestFilter,
  67. */
  68. } )
  69. this.rtEDL = new THREE.WebGLRenderTarget(this.width, this.height, { //好像没用到? 因为这里不绘制测量线
  70. minFilter: THREE.NearestFilter,
  71. magFilter: THREE.NearestFilter,
  72. format: THREE.RGBAFormat,
  73. type: THREE.FloatType,
  74. depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType)
  75. });
  76. this.mesh = new THREE.Mesh(circleGeo, new THREE.MeshBasicMaterial({
  77. side: THREE.DoubleSide ,
  78. map: this.renderTarget.texture ,
  79. transparent:true,
  80. depthTest: !1,
  81. //depthWrite: !1,
  82. }))
  83. this.overlayMesh = new THREE.Mesh(circleGeo, new THREE.MeshBasicMaterial({
  84. side: THREE.DoubleSide ,
  85. map:texLoader.load(Potree.resourcePath+'/textures/crosshair.png') ,
  86. transparent:true,
  87. depthTest: !1,
  88. //depthWrite: !1,
  89. }))
  90. this.targetPoint = new THREE.Object3D;
  91. this.targetPoint.add(new THREE.Mesh(sphereGeo, new THREE.MeshBasicMaterial({
  92. color:"#ff0000",
  93. transparent:true,
  94. opacity:0.5,
  95. })))
  96. this.targetPoint.add(new THREE.Mesh(sphereGeo, new THREE.MeshBasicMaterial({
  97. color:"#ff0000",
  98. transparent:true,
  99. opacity:0.2,
  100. depthTest:false //被遮挡层
  101. })))
  102. this.targetPoint.name = 'magnifierPointTarget'
  103. viewer.scene.scene.add(this.targetPoint)
  104. viewer.setObjectLayers(this.targetPoint, 'magnifierContent' )
  105. this.add(this.mesh)
  106. this.add(this.overlayMesh)
  107. this.position.set(-1000,-1000,-100000)//令它看不见
  108. this.mesh.renderOrder = 10;
  109. this.overlayMesh.renderOrder = 11;
  110. this.aimPos
  111. viewer.setObjectLayers(this, 'magnifier' )
  112. //viewer.inputHandler.addInputListener(this)
  113. viewer.addEventListener('camera_changed',(e)=>{ // 平移、滚轮时更新
  114. if(e.viewport == viewer.mainViewport) this.update() //不过intersectPoint没更新
  115. })
  116. this.mesh.layers.set(Potree.config.renderLayers.magnifier);
  117. this.overlayMesh.layers.set(Potree.config.renderLayers.magnifier);
  118. //this.layers.set(Potree.config.renderLayers.magnifier);//这句在外层写没用
  119. this.dontRender = false
  120. viewer.addEventListener('global_drag', (e)=>{//拖拽时不渲染。主要是右键平移时渲染延迟了,会闪烁。
  121. this.dontRender = true
  122. })
  123. viewer.addEventListener('global_drop', (e)=>{
  124. this.dontRender = false
  125. })
  126. viewer.addEventListener('global_mouseup', (e)=>{//测量时拖拽场景再mouseup
  127. this.dontRender = false
  128. })
  129. var updateVisi = (e)=>{
  130. if(e.hoverViewport == viewer.mainViewport){
  131. viewer.updateVisible(this,"atViewport", true)
  132. this.update(e.intersect && e.intersect.location)
  133. }else{
  134. viewer.updateVisible(this,"atViewport", false) //小地图不显示
  135. }
  136. }
  137. viewer.addEventListener('global_mousemove', updateVisi)
  138. viewer.addEventListener('global_touchstart', updateVisi)
  139. /* viewer.addEventListener("beginSplitView",()=>{
  140. this.updateVisible("splitView", false)
  141. })
  142. viewer.addEventListener("finishSplitView",()=>{
  143. this.updateVisible("splitView", true)
  144. }) */
  145. this.addEventListener("setEnable",(e)=>{
  146. viewer.updateVisible(this, "enable", e.value) //界面开关
  147. /* if(Potree.settings.displayMode == 'showPanos') && e.value){
  148. Potree.settings.pointDensity = 'magnifier'
  149. }else if() */
  150. })
  151. if(Potree.settings.isOfficial){
  152. viewer.updateVisible(this, "enable", false)
  153. }else{
  154. viewer.updateVisible(this, "measure", false)
  155. viewer.addEventListener("measureMovePoint",()=>{//测量开始
  156. viewer.updateVisible(this, "measure", true)
  157. })
  158. viewer.addEventListener("endMeasureMove",()=>{
  159. viewer.updateVisible(this, "measure", false)
  160. })
  161. }
  162. viewer.scene.view.addEventListener('flyingDone',()=>{
  163. if(!this.visible)return
  164. let pickWindowSize = 100
  165. let intersect = viewer.inputHandler.getIntersect(viewer.mainViewport, viewer.mainViewport.camera, true, pickWindowSize )
  166. this.update(intersect && intersect.location)
  167. })
  168. }
  169. //注意:在鼠标没有移动的时候,无法获取到最新的intersect, 放大镜内的内容可能是错误的。全景模式下更奇怪,原因未知
  170. update(aimPos){//相机靠近 navvis的做法
  171. var dontRender = this.dontRender || !(aimPos instanceof THREE.Vector3) || Potree.settings.displayMode == 'showPanos' && viewer.images360.flying
  172. aimPos = aimPos instanceof THREE.Vector3 ? aimPos : this.aimPos
  173. if(!aimPos || !this.visible)return
  174. var playerCamera = viewer.scene.getActiveCamera()
  175. var playerPos = playerCamera.position;//viewer.scene.view.getPivot()
  176. var dis = playerPos.distanceTo(aimPos);
  177. var dirToCamera = new THREE.Vector3().subVectors(playerPos, aimPos ).normalize()
  178. //相机位置
  179. var finalDisToAim = dis>magDistance_ ? magDistance_ : dis / 2;
  180. this.camera.position.copy(aimPos).add(dirToCamera.multiplyScalar(finalDisToAim))
  181. this.camera.lookAt(aimPos)
  182. this.camera.fov = playerCamera.fov / 2
  183. this.camera.updateProjectionMatrix()
  184. //自身位置
  185. //let pos2d = viewer.inputHandler.pointer.clone(); //跟随鼠标
  186. let pos2d = Potree.Utils.getPos2d(aimPos, playerCamera, viewer.renderArea, viewer.mainViewport).vector //更新目标点的实时二维位置
  187. let margin = 0.4, maxY = 0.4
  188. let screenPos = pos2d.clone().setY(pos2d.y + (pos2d.y>maxY ? -margin : margin ))
  189. let newPos = new THREE.Vector3(screenPos.x,screenPos.y,0.8).unproject(playerCamera); //z:-1朝外
  190. let dir = newPos.clone().sub(playerPos).normalize().multiplyScalar(10);//这个数值要大于playerCamera.near
  191. let s = dis>magDistance_ ? 1 : dis / magDistance_ ;
  192. this.position.copy(playerPos.clone().add(dir))
  193. this.quaternion.copy(playerCamera.quaternion);
  194. this.targetPoint.position.copy(aimPos);
  195. this.targetPoint.scale.set(s,s,s)
  196. this.aimPos = aimPos
  197. var scale = math.getScaleForConstantSize({//
  198. width2d : width2dPX,
  199. camera:viewer.scene.getActiveCamera(), position: this.getWorldPosition(new THREE.Vector3()),
  200. resolution: viewer.mainViewport.resolution2
  201. })
  202. this.scale.set(scale, scale, scale);
  203. if(!dontRender){
  204. this.waitRender = true
  205. }
  206. }
  207. /* update(aimPos){ //仅改fov的版本
  208. aimPos = aimPos instanceof THREE.Vector3 ? aimPos : this.aimPos
  209. if(!aimPos || !this.visible)return
  210. //相机位置
  211. var playerCamera = viewer.scene.getActiveCamera()
  212. var playerPos = playerCamera.position;//viewer.scene.view.getPivot()
  213. var dis = playerPos.distanceTo(aimPos);
  214. if(dis<magDistance_){
  215. this.camera.fov = maxFov
  216. }else{
  217. this.camera.fov = THREE.Math.radToDeg(Math.atan(radius_ / dis )) * 2 //radius_是能看到的范围半径。当dis大于magDistance_时就放大,否则维持fov为maxFov
  218. }
  219. this.camera.updateProjectionMatrix()
  220. this.camera.position.copy(playerPos)
  221. this.camera.lookAt(aimPos)
  222. this.quaternion.copy(playerCamera.quaternion);
  223. let pointer = viewer.inputHandler.pointer.clone();
  224. let margin = 0.4, maxY = 0.4
  225. let screenPos = pointer.clone().setY(pointer.y + (pointer.y>maxY ? -margin : margin ))
  226. let newPos = new THREE.Vector3(screenPos.x,screenPos.y,0.8).unproject(playerCamera); //z:-1朝外
  227. let dir = newPos.clone().sub(playerPos).normalize().multiplyScalar(10);//这个数值要大于playerCamera.near
  228. this.position.copy(playerPos.clone().add(dir))
  229. this.aimPos = aimPos
  230. this.targetPoint.position.copy(aimPos);
  231. var scale = math.getScaleForConstantSize({//
  232. width2d : width2dPX,
  233. camera:viewer.scene.getActiveCamera(), position: this.getWorldPosition(new THREE.Vector3()),
  234. resolution: viewer.mainViewport.resolution2
  235. })
  236. this.scale.set(scale, scale, scale);
  237. if(!this.dontRender){
  238. this.waitRender = true
  239. }
  240. }//位置需要计算,不仅仅是点云,所以需要深度图
  241. */
  242. render(){
  243. if(!this.waitRender)return
  244. //this.visible = false;//防止放大镜里有自己
  245. viewer.render({
  246. target : this.renderTarget,
  247. viewports : [this.viewport],
  248. camera : this.camera,
  249. magnifier : true,
  250. rtEDL: this.rtEDL
  251. /* width :this.renderTarget.width,
  252. height: this.renderTarget.height, */
  253. })
  254. //this.visible = true;
  255. this.waitRender = false
  256. }
  257. }