viewerBase.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import Common from '../utils/Common'
  3. export class ViewerBase extends THREE.EventDispatcher{
  4. constructor(domElement, args = {}){
  5. super()
  6. this.name = args.name
  7. this.renderArea = domElement
  8. this.oldResolution = new THREE.Vector2()
  9. this.screenSizeInfo = {
  10. W:0, H:0, pixelRatio:1 , windowWidth:0, windowHeight:0
  11. }
  12. this.initContext(args);
  13. this.addEventListener('content_changed', ()=>{//画面改变,需要渲染
  14. this.needRender = true
  15. })
  16. }
  17. initContext(args){
  18. //console.log(`initializing three.js ${THREE.REVISION}`);
  19. let width = this.renderArea.clientWidth;
  20. let height = this.renderArea.clientHeight;
  21. let contextAttributes = {
  22. alpha: true,
  23. depth: true,
  24. stencil: false,
  25. antialias: true,
  26. preserveDrawingBuffer: true,
  27. powerPreference: "high-performance",
  28. };
  29. let canvas = document.createElement("canvas");
  30. let context = canvas.getContext('webgl', contextAttributes );
  31. this.renderer = new THREE.WebGLRenderer({
  32. alpha: true, //支持透明
  33. premultipliedAlpha: false,
  34. canvas: canvas,
  35. context: context,
  36. });
  37. this.renderer.sortObjects = true; //原先false 打开了renderOrder才奏效
  38. //this.renderer.setSize(width, height);
  39. this.renderer.autoClear = args.autoClear || false;
  40. //args.clearColor = args.clearColor || '#aa0033'
  41. args.clearColor && this.renderer.setClearColor(args.clearColor)
  42. this.renderArea.appendChild(this.renderer.domElement);
  43. this.renderer.domElement.tabIndex = '2222';
  44. this.renderer.domElement.style.position = 'absolute';
  45. this.renderer.domElement.addEventListener('mousedown', () => {
  46. this.renderer.domElement.focus();
  47. });
  48. //this.renderer.domElement.focus();
  49. // NOTE: If extension errors occur, pass the string into this.renderer.extensions.get(x) before enabling
  50. // enable frag_depth extension for the interpolation shader, if available
  51. let gl = this.renderer.getContext();
  52. gl.getExtension('EXT_frag_depth');
  53. gl.getExtension('WEBGL_depth_texture');
  54. gl.getExtension('WEBGL_color_buffer_float'); // Enable explicitly for more portability, EXT_color_buffer_float is the proper name in WebGL 2
  55. if(gl.createVertexArray == null){
  56. let extVAO = gl.getExtension('OES_vertex_array_object');
  57. if(!extVAO){
  58. throw new Error("OES_vertex_array_object extension not supported");
  59. }
  60. gl.createVertexArray = extVAO.createVertexArrayOES.bind(extVAO);
  61. gl.bindVertexArray = extVAO.bindVertexArrayOES.bind(extVAO);
  62. }
  63. /* let oldClear = gl.clear;
  64. gl.clear = (bits)=>{
  65. console.error('clear')
  66. }
  67. */
  68. }
  69. updateScreenSize(o={}) { //有可能需要让viewport来判断,当窗口大小不变但viewport大小变时
  70. var render = false, ratio, w, h;
  71. //记录应当render的大小
  72. if (o.width != void 0 && o.height != void 0) {
  73. w = o.width
  74. h = o.height
  75. render = true
  76. ratio = 1
  77. }else {
  78. w = this.renderArea.clientWidth;
  79. h = this.renderArea.clientHeight
  80. if(w !== this.screenSizeInfo.W || h !== this.screenSizeInfo.H || o.forceUpdateSize || this.screenSizeInfo.pixelRatio != window.devicePixelRatio){
  81. this.screenSizeInfo.W = w
  82. this.screenSizeInfo.H = h
  83. render = true
  84. this.screenSizeInfo.pixelRatio = window.devicePixelRatio //如果player放在小窗口了,也要监测devicePixelRatio,因为缩放时client宽高不会改变
  85. //config.isMobile ? (ratio = Math.min(window.devicePixelRatio, 2)) : (ratio = window.devicePixelRatio)
  86. ratio = window.devicePixelRatio
  87. }
  88. }
  89. if (render) {
  90. this.setSize(w, h, ratio);
  91. }
  92. }
  93. /* updateScreenSize(o={}) { //容易出错。。
  94. var render = false, ratio, w, h;
  95. //记录应当render的大小
  96. if (o.width != void 0 && o.height != void 0) {
  97. w = o.width
  98. h = o.height
  99. render = true
  100. ratio = 1
  101. }else {
  102. w = this.renderArea.clientWidth;
  103. h = this.renderArea.clientHeight
  104. let refreshWin = ()=>{
  105. this.screenSizeInfo.windowWidth = window.innerWidth
  106. this.screenSizeInfo.windowHeight = window.innerHeight
  107. }
  108. let prepareToRender = ()=>{
  109. w = this.renderArea.clientWidth;
  110. h = this.renderArea.clientHeight
  111. this.screenSizeInfo.W = w
  112. this.screenSizeInfo.H = h
  113. refreshWin() //render后才refreshWin
  114. render = true
  115. this.screenSizeInfo.pixelRatio = window.devicePixelRatio //如果player放在小窗口了,也要监测devicePixelRatio,因为缩放时client宽高不会改变
  116. //config.isMobile ? (ratio = Math.min(window.devicePixelRatio, 2)) : (ratio = window.devicePixelRatio)
  117. ratio = window.devicePixelRatio
  118. //console.log('setSize11', w)
  119. o.forceUpdateSize = false ;//防止再次render
  120. }
  121. let ifNeedUpdate = ()=>{
  122. w = this.renderArea.clientWidth;
  123. h = this.renderArea.clientHeight
  124. return o.forceUpdateSize || w !== this.screenSizeInfo.W || h !== this.screenSizeInfo.H || this.screenSizeInfo.pixelRatio != window.devicePixelRatio
  125. }
  126. if(ifNeedUpdate()){
  127. //当只有一个有效viewport时,且因为改变整个窗口大小而触发的话,延时
  128. let canInterval = !o.forceUpdateSize && this.viewports.filter(e=>e.active).length==1 && (this.screenSizeInfo.windowWidth != window.innerWidth || this.screenSizeInfo.windowHeight != window.innerHeight )
  129. if(canInterval){
  130. Common.intervalTool.isWaiting('updateScreenSize', ()=>{ //延时update,防止崩溃 , 未到时间就拦截(第一次直接执行)
  131. if(ifNeedUpdate()){
  132. prepareToRender()
  133. return true
  134. }
  135. }, 500)
  136. }else{
  137. //console.log('soon', window.innerWidth, this.screenSizeInfo.windowWidth)
  138. prepareToRender()
  139. }
  140. }
  141. }
  142. if (render) {
  143. this.setSize(w, h, ratio);
  144. }
  145. } */
  146. setSize(width, height, devicePixelRatio, onlyForTarget){
  147. //console.log('setSize', width)
  148. if(!onlyForTarget){//onlyForTarget表示不更改当前renderer,只是为了rendertarget才要改变viewport
  149. this.renderer.setSize(width, height, null, devicePixelRatio); // resize之后会自动clear(似乎因为setScissor ),所以一定要立刻绘制,所以setSize要在cameraChanged、update之前
  150. }
  151. this.composer && this.composer.setSize(width, height);
  152. if(this.viewports){
  153. this.viewports.forEach((view,i)=>{
  154. if(!view.active)return
  155. var width_ = width * view.width
  156. var height_ = height * view.height
  157. if(height_ == 0)return //avoid NAN
  158. view.setResolution(Math.ceil(width_), Math.ceil(height_), width, height ) //本来应该是floor,但是这样奇数时会少一个像素,导致向左移一个像素且宽度少1。现在则多绘制1个像素,超出的1个像素应该不会绘制出来(但不知道其他地方是否有偏差,比如pick时)
  159. let aspect = width_ / height_; //camera的参数精确些,不用视口的归整的resolution像素值,否则hasChange无法为true, 导致canvasResize了但map没update从而闪烁
  160. view.camera.aspect = aspect;
  161. if(view.camera.type == "OrthographicCamera"){
  162. /* //不改宽度 同4dkk
  163. var heightHalf = view.camera.right / aspect
  164. view.camera.top = heightHalf
  165. view.camera.bottom = -heightHalf */
  166. //高宽都改 使大小不随视口大小改变 navvis (直接和视口大小一致即可,通过zoom来定大小)
  167. view.camera.left = -width_/2
  168. view.camera.right = width_/2
  169. view.camera.bottom = -height_/2;
  170. view.camera.top = height_/2
  171. }else{
  172. }
  173. view.camera.updateProjectionMatrix();
  174. })
  175. }
  176. if(!onlyForTarget){//因为onlyForTarget不传递devicePixelRatio所以不发送了
  177. this.emitResizeMsg({viewport:this.viewports[0], deviceRatio:devicePixelRatio})
  178. }
  179. }
  180. emitResizeMsg(e){//切换viewport渲染时就发送一次, 通知一些材质更新resolution。
  181. if(!e.viewport.resolution.equals(this.oldResolution)){
  182. this.dispatchEvent($.extend(e, {type:'resize'}))
  183. this.oldResolution.copy(e.viewport.resolution)
  184. }
  185. }
  186. cameraChanged() {//判断相机是否改变
  187. var changed = false;
  188. /* if(this.needRender){
  189. this.needRender = false
  190. return true
  191. } */
  192. for(let i=0,j=this.viewports.length;i<j;i++){
  193. let changeInfo = this.viewports[i].cameraChanged()
  194. if(changeInfo.changed){
  195. changed = true
  196. //if(!this.changeTime ||this.changeTime<100){
  197. this.dispatchEvent({
  198. type: "camera_changed",
  199. camera: this.viewports[i].camera,
  200. viewport : this.viewports[i],
  201. changeInfo
  202. })
  203. //this.changeTime = (this.changeTime || 0) +1
  204. //}
  205. }
  206. }
  207. return changed
  208. }
  209. makeScreenshot( size, viewports, compressRatio){//暂时不要指定viewports渲染,但也可以
  210. let {width, height} = size;
  211. /* let oldBudget = Potree.pointBudget;
  212. Potree.pointBudget = Math.max(10 * 1000 * 1000, 2 * oldBudget);
  213. let result = Potree.updatePointClouds(this.scene.pointclouds, camera, size );
  214. Potree.pointBudget = oldBudget;
  215. this.dispatchEvent({ //resize everything such as lines targets
  216. type: 'resize',
  217. resolution: new THREE.Vector2(width,height),
  218. });*/
  219. let target = new THREE.WebGLRenderTarget(width, height, {
  220. format: THREE.RGBAFormat,
  221. });
  222. this.setSize(width, height,1,true)
  223. this.render({
  224. target ,
  225. //camera ,
  226. viewports: viewports || this.viewports,
  227. screenshot : true,
  228. width ,
  229. height,
  230. resize :true //需要resize
  231. });
  232. let dataUrl = Potree.Utils.renderTargetToDataUrl(target, width, height, this.renderer, compressRatio)
  233. /* let pixelCount = width * height;
  234. let buffer = new Uint8Array(4 * pixelCount);
  235. this.renderer.readRenderTargetPixels(target, 0, 0, width, height, buffer);
  236. let dataUrl = Potree.Utils.pixelsArrayToDataUrl(buffer, width, height, compressRatio) */
  237. target.dispose();
  238. //resize back
  239. //this.updateScreenSize({forceUpdateSize:true})
  240. return {
  241. width,
  242. height,
  243. dataUrl
  244. };
  245. }
  246. }