viewerBase.js 11 KB

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