UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

252 lines (249 loc) 9.14 kB
import { EventHandler } from '../../core/event-handler.js'; import { Texture } from '../../platform/graphics/texture.js'; import { Vec4 } from '../../core/math/vec4.js'; import { Mat3 } from '../../core/math/mat3.js'; import { Mat4 } from '../../core/math/mat4.js'; import { FILTER_LINEAR, ADDRESS_CLAMP_TO_EDGE, PIXELFORMAT_RGB8, FILTER_NEAREST, PIXELFORMAT_DEPTH, PIXELFORMAT_R32F } from '../../platform/graphics/constants.js'; class XrView extends EventHandler { static{ this.EVENT_DEPTHRESIZE = 'depth:resize'; } constructor(manager, xrView, viewsCount){ super(), this._positionData = new Float32Array(3), this._viewport = new Vec4(), this._projMat = new Mat4(), this._projViewOffMat = new Mat4(), this._viewMat = new Mat4(), this._viewOffMat = new Mat4(), this._viewMat3 = new Mat3(), this._viewInvMat = new Mat4(), this._viewInvOffMat = new Mat4(), this._xrCamera = null, this._textureColor = null, this._textureDepth = null, this._depthInfo = null, this._emptyDepthBuffer = new Uint8Array(32), this._depthMatrix = new Mat4(); this._manager = manager; this._xrView = xrView; const device = this._manager.app.graphicsDevice; if (this._manager.views.supportedColor) { this._xrCamera = this._xrView.camera; if (this._manager.views.availableColor && this._xrCamera) { this._textureColor = new Texture(device, { format: PIXELFORMAT_RGB8, mipmaps: false, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, minFilter: FILTER_LINEAR, magFilter: FILTER_LINEAR, width: this._xrCamera.width, height: this._xrCamera.height, name: `XrView-${this._xrView.eye}-Color` }); } } if (this._manager.views.supportedDepth && this._manager.views.availableDepth) { const filtering = this._manager.views.depthGpuOptimized ? FILTER_NEAREST : FILTER_LINEAR; this._textureDepth = new Texture(device, { format: this._manager.views.depthPixelFormat, arrayLength: viewsCount === 1 ? 0 : viewsCount, mipmaps: false, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, minFilter: filtering, magFilter: filtering, width: 4, height: 4, name: `XrView-${this._xrView.eye}-Depth` }); for(let i = 0; i < this._textureDepth._levels.length; i++){ this._textureDepth._levels[i] = this._emptyDepthBuffer; } this._textureDepth.upload(); } if (this._textureColor || this._textureDepth) { device.on('devicelost', this._onDeviceLost, this); } } get textureColor() { return this._textureColor; } get textureDepth() { return this._textureDepth; } get depthUvMatrix() { return this._depthMatrix; } get depthValueToMeters() { return this._depthInfo?.rawValueToMeters || 0; } get eye() { return this._xrView.eye; } get viewport() { return this._viewport; } get projMat() { return this._projMat; } get projViewOffMat() { return this._projViewOffMat; } get viewOffMat() { return this._viewOffMat; } get viewInvOffMat() { return this._viewInvOffMat; } get viewMat3() { return this._viewMat3; } get positionData() { return this._positionData; } update(frame, xrView) { this._xrView = xrView; if (this._manager.views.availableColor) { this._xrCamera = this._xrView.camera; } const layer = frame.session.renderState.baseLayer; const viewport = layer.getViewport(this._xrView); this._viewport.x = viewport.x; this._viewport.y = viewport.y; this._viewport.z = viewport.width; this._viewport.w = viewport.height; this._projMat.set(this._xrView.projectionMatrix); this._viewMat.set(this._xrView.transform.inverse.matrix); this._viewInvMat.set(this._xrView.transform.matrix); this._updateTextureColor(); this._updateDepth(frame); } _updateTextureColor() { if (!this._manager.views.availableColor || !this._xrCamera || !this._textureColor) { return; } const binding = this._manager.webglBinding; if (!binding) { return; } const texture = binding.getCameraImage(this._xrCamera); if (!texture) { return; } const device = this._manager.app.graphicsDevice; const gl = device.gl; if (!this._frameBufferSource) { this._frameBufferSource = gl.createFramebuffer(); this._frameBuffer = gl.createFramebuffer(); } else { const attachmentBaseConstant = gl.COLOR_ATTACHMENT0; const width = this._xrCamera.width; const height = this._xrCamera.height; device.setFramebuffer(this._frameBufferSource); gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentBaseConstant, gl.TEXTURE_2D, texture, 0); device.setFramebuffer(this._frameBuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentBaseConstant, gl.TEXTURE_2D, this._textureColor.impl._glTexture, 0); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._frameBufferSource); gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._frameBuffer); gl.blitFramebuffer(0, height, width, 0, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST); } } _updateDepth(frame) { if (!this._manager.views.availableDepth || !this._textureDepth) { return; } const gpu = this._manager.views.depthGpuOptimized; const infoSource = gpu ? this._manager.webglBinding : frame; if (!infoSource) { this._depthInfo = null; return; } const depthInfo = infoSource.getDepthInformation(this._xrView); if (!depthInfo) { this._depthInfo = null; return; } let matrixDirty = !this._depthInfo !== !depthInfo; this._depthInfo = depthInfo; const width = this._depthInfo?.width || 4; const height = this._depthInfo?.height || 4; let resized = false; if (this._textureDepth.width !== width || this._textureDepth.height !== height) { this._textureDepth._width = width; this._textureDepth._height = height; matrixDirty = true; resized = true; } if (matrixDirty) { if (this._depthInfo) { this._depthMatrix.data.set(this._depthInfo.normDepthBufferFromNormView.matrix); } else { this._depthMatrix.setIdentity(); } } if (this._depthInfo) { if (gpu) { if (this._depthInfo.texture) { const gl = this._manager.app.graphicsDevice.gl; this._textureDepth.impl._glTexture = this._depthInfo.texture; if (this._depthInfo.textureType === 'texture-array') { this._textureDepth.impl._glTarget = gl.TEXTURE_2D_ARRAY; } else { this._textureDepth.impl._glTarget = gl.TEXTURE_2D; } switch(this._manager.views.depthPixelFormat){ case PIXELFORMAT_R32F: this._textureDepth.impl._glInternalFormat = gl.R32F; this._textureDepth.impl._glPixelType = gl.FLOAT; this._textureDepth.impl._glFormat = gl.RED; break; case PIXELFORMAT_DEPTH: this._textureDepth.impl._glInternalFormat = gl.DEPTH_COMPONENT16; this._textureDepth.impl._glPixelType = gl.UNSIGNED_SHORT; this._textureDepth.impl._glFormat = gl.DEPTH_COMPONENT; break; } this._textureDepth.impl._glCreated = true; } } else { this._textureDepth._levels[0] = new Uint8Array(this._depthInfo.data); this._textureDepth.upload(); } } else { this._textureDepth._levels[0] = this._emptyDepthBuffer; this._textureDepth.upload(); } if (resized) this.fire('depth:resize', width, height); } updateTransforms(transform) { if (transform) { this._viewInvOffMat.mul2(transform, this._viewInvMat); this.viewOffMat.copy(this._viewInvOffMat).invert(); } else { this._viewInvOffMat.copy(this._viewInvMat); this.viewOffMat.copy(this._viewMat); } this._viewMat3.setFromMat4(this._viewOffMat); this._projViewOffMat.mul2(this._projMat, this._viewOffMat); this._positionData[0] = this._viewInvOffMat.data[12]; this._positionData[1] = this._viewInvOffMat.data[13]; this._positionData[2] = this._viewInvOffMat.data[14]; } _onDeviceLost() { this._frameBufferSource = null; this._frameBuffer = null; this._depthInfo = null; } getDepth(u, v) { if (this._manager.views.depthGpuOptimized) { return null; } return this._depthInfo?.getDepthInMeters(u, v) ?? null; } destroy() { this._depthInfo = null; if (this._textureColor) { this._textureColor.destroy(); this._textureColor = null; } if (this._textureDepth) { this._textureDepth.destroy(); this._textureDepth = null; } if (this._frameBufferSource) { const gl = this._manager.app.graphicsDevice.gl; gl.deleteFramebuffer(this._frameBufferSource); this._frameBufferSource = null; gl.deleteFramebuffer(this._frameBuffer); this._frameBuffer = null; } } } export { XrView };