import * as THREE from "../../../libs/three.js/build/three.module.js"; import Common from '../utils/Common.js' export class ViewerBase extends THREE.EventDispatcher{ constructor(domElement, args = {}){ super() this.name = args.name this.renderArea = domElement this.oldResolution = new THREE.Vector2() this.oldResolution2 = new THREE.Vector2() this.screenSizeInfo = { W:0, H:0, pixelRatio:1 , windowWidth:0, windowHeight:0 } this.initContext(args); this.addEventListener('content_changed', ()=>{//画面改变,需要渲染 this.needRender = true //console.log('needRender') }) } initContext(args){ //console.log(`initializing three.js ${THREE.REVISION}`); let width = this.renderArea.clientWidth; let height = this.renderArea.clientHeight; let contextAttributes = { alpha: true,//支持透明 depth: true, stencil: false, antialias: !!args.antialias, preserveDrawingBuffer: args.preserveDrawingBuffer || false , powerPreference: "high-performance", }; let canvas = document.createElement("canvas"); let context = canvas.getContext('webgl2', contextAttributes ); //不用webgl2是因为有的写法在webgl2不支持 如gl_FragDepthEXT if(context){ Potree.settings.isWebgl2 = true } this.renderer = new THREE.WebGLRenderer({ premultipliedAlpha: false, canvas: canvas, context: context, }); this.renderer.sortObjects = true; //原先false 打开了renderOrder才奏效 //this.renderer.setSize(width, height); this.renderer.autoClear = args.autoClear || false; //args.clearColor = args.clearColor || '#aa0033' args.clearColor && this.renderer.setClearColor(args.clearColor) this.renderArea.appendChild(this.renderer.domElement); this.renderer.domElement.tabIndex = '2222'; this.renderer.domElement.style.position = 'absolute'; this.renderer.domElement.addEventListener('mousedown', () => { this.renderer.domElement.focus(); }); //this.renderer.domElement.focus(); // NOTE: If extension errors occur, pass the string into this.renderer.extensions.get(x) before enabling // enable frag_depth extension for the interpolation shader, if available let gl = this.renderer.getContext(); gl.getExtension('EXT_frag_depth'); gl.getExtension('WEBGL_depth_texture'); gl.getExtension('WEBGL_color_buffer_float'); // Enable explicitly for more portability, EXT_color_buffer_float is the proper name in WebGL 2 if(gl.createVertexArray == null){ let extVAO = gl.getExtension('OES_vertex_array_object'); if(!extVAO){ throw new Error("OES_vertex_array_object extension not supported"); } gl.createVertexArray = extVAO.createVertexArrayOES.bind(extVAO); gl.bindVertexArray = extVAO.bindVertexArrayOES.bind(extVAO); } /* let oldClear = gl.clear; gl.clear = (bits)=>{ console.error('clear') } */ } updateScreenSize(o={}) { //有可能需要让viewport来判断,当窗口大小不变但viewport大小变时 if(this.screenshoting && !o.forceUpdateSize) return //截图时不允许因窗口改变大小而updateScreenSize var render = false, ratio, w, h; //记录应当render的大小 if (o.width != void 0 && o.height != void 0) { w = o.width h = o.height render = true ratio = 1 }else { w = this.renderArea.clientWidth; h = this.renderArea.clientHeight if(w !== this.screenSizeInfo.W || h !== this.screenSizeInfo.H || o.forceUpdateSize || this.screenSizeInfo.pixelRatio != window.devicePixelRatio){ this.screenSizeInfo.W = w this.screenSizeInfo.H = h render = true this.screenSizeInfo.pixelRatio = window.devicePixelRatio //如果player放在小窗口了,也要监测devicePixelRatio,因为缩放时client宽高不会改变 //config.isMobile ? (ratio = Math.min(window.devicePixelRatio, 2)) : (ratio = window.devicePixelRatio) ratio = window.devicePixelRatio } } if (render) { this.setSize(w, h, ratio, o.forTarget ); } } setSize(width, height, devicePixelRatio, onlyForTarget){ //console.log('setSize', width) if(!onlyForTarget){//onlyForTarget表示不更改当前renderer,只是为了rendertarget才要改变viewport this.renderer.setPixelRatio(devicePixelRatio) this.renderer.setSize(width, height ); // resize之后会自动clear(似乎因为setScissor ),所以一定要立刻绘制,所以setSize要在cameraChanged、update之前 } //this.composer && this.composer.setSize(width, height); if(this.viewports){ this.viewports.forEach((view,i)=>{ //if(!view.active)return var width_ = width * view.width var height_ = height * view.height view.setResolution(Math.ceil(width_), Math.ceil(height_), width, height ) if(height_ == 0)return //avoid NAN let aspect = width_ / height_; //camera的参数精确些,不用视口的归整的resolution像素值,否则hasChange无法为true, 导致canvasResize了但map没update从而闪烁 view.camera.aspect = aspect; if(view.camera.type == "OrthographicCamera"){ /* //不改宽度 同4dkk var heightHalf = view.camera.right / aspect view.camera.top = heightHalf view.camera.bottom = -heightHalf */ //高宽都改 使大小不随视口大小改变 navvis (直接和视口大小一致即可,通过zoom来定大小) view.camera.left = -width_/2 view.camera.right = width_/2 view.camera.bottom = -height_/2; view.camera.top = height_/2 }else{ } view.camera.updateProjectionMatrix(); }) } if(!onlyForTarget){//因为onlyForTarget不传递devicePixelRatio所以不发送了 this.dispatchEvent('viewerResize') this.viewports.forEach(e=>{ this.ifEmitResize({viewport:e, deviceRatio:devicePixelRatio}) }) } } ifEmitResize(e){//切换viewport渲染时, 若这些viewport大小不同就发送一次, 通知一些材质更新resolution。 //console.log('ifEmitResize',e.viewport.name,e.viewport.resolution2 ) if(!e.viewport.resolution.equals(this.oldResolution)||!e.viewport.resolution2.equals(this.oldResolution2)){ this.dispatchEvent($.extend(e, {type:'resize'})) this.oldResolution.copy(e.viewport.resolution) this.oldResolution2.copy(e.viewport.resolution2) } } cameraChanged() {//判断相机是否改变 var changed = false; /* if(this.needRender){ this.needRender = false return true } */ for(let i=0,j=this.viewports.length;i