import * as THREE from "../../libs/three.js/build/three.module.js"; import Common from '../utils/Common' export class ViewerBase extends THREE.EventDispatcher{ constructor(domElement, args = {}){ super() this.name = args.name this.renderArea = domElement this.oldResolution = 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 }) } 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: true, preserveDrawingBuffer: true, powerPreference: "high-performance", }; let canvas = document.createElement("canvas"); let context = canvas.getContext('webgl', contextAttributes ); this.renderer = new THREE.WebGLRenderer({ alpha: true, //支持透明 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大小变时 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); } } /* updateScreenSize(o={}) { //容易出错。。 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 let refreshWin = ()=>{ this.screenSizeInfo.windowWidth = window.innerWidth this.screenSizeInfo.windowHeight = window.innerHeight } let prepareToRender = ()=>{ w = this.renderArea.clientWidth; h = this.renderArea.clientHeight this.screenSizeInfo.W = w this.screenSizeInfo.H = h refreshWin() //render后才refreshWin render = true this.screenSizeInfo.pixelRatio = window.devicePixelRatio //如果player放在小窗口了,也要监测devicePixelRatio,因为缩放时client宽高不会改变 //config.isMobile ? (ratio = Math.min(window.devicePixelRatio, 2)) : (ratio = window.devicePixelRatio) ratio = window.devicePixelRatio //console.log('setSize11', w) o.forceUpdateSize = false ;//防止再次render } let ifNeedUpdate = ()=>{ w = this.renderArea.clientWidth; h = this.renderArea.clientHeight return o.forceUpdateSize || w !== this.screenSizeInfo.W || h !== this.screenSizeInfo.H || this.screenSizeInfo.pixelRatio != window.devicePixelRatio } if(ifNeedUpdate()){ //当只有一个有效viewport时,且因为改变整个窗口大小而触发的话,延时 let canInterval = !o.forceUpdateSize && this.viewports.filter(e=>e.active).length==1 && (this.screenSizeInfo.windowWidth != window.innerWidth || this.screenSizeInfo.windowHeight != window.innerHeight ) if(canInterval){ Common.intervalTool.isWaiting('updateScreenSize', ()=>{ //延时update,防止崩溃 , 未到时间就拦截(第一次直接执行) if(ifNeedUpdate()){ prepareToRender() return true } }, 500) }else{ //console.log('soon', window.innerWidth, this.screenSizeInfo.windowWidth) prepareToRender() } } } if (render) { this.setSize(w, h, ratio); } } */ setSize(width, height, devicePixelRatio, onlyForTarget){ //console.log('setSize', width) if(!onlyForTarget){//onlyForTarget表示不更改当前renderer,只是为了rendertarget才要改变viewport this.renderer.setSize(width, height, null, devicePixelRatio); // 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 if(height_ == 0)return //avoid NAN view.setResolution(Math.ceil(width_), Math.ceil(height_), width, height ) //本来应该是floor,但是这样奇数时会少一个像素,导致向左移一个像素且宽度少1。现在则多绘制1个像素,超出的1个像素应该不会绘制出来(但不知道其他地方是否有偏差,比如pick时) 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.emitResizeMsg({viewport:this.viewports[0], deviceRatio:devicePixelRatio}) } } emitResizeMsg(e){//切换viewport渲染时就发送一次, 通知一些材质更新resolution。 if(!e.viewport.resolution.equals(this.oldResolution)){ this.dispatchEvent($.extend(e, {type:'resize'})) this.oldResolution.copy(e.viewport.resolution) } } cameraChanged() {//判断相机是否改变 var changed = false; /* if(this.needRender){ this.needRender = false return true } */ for(let i=0,j=this.viewports.length;i