UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

216 lines (215 loc) 6.36 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 { ADDRESS_CLAMP_TO_EDGE, FILTER_LINEAR, FILTER_NEAREST, PIXELFORMAT_RGB8, PIXELFORMAT_R32F } from "../../platform/graphics/constants.js"; class XrView extends EventHandler { static EVENT_DEPTHRESIZE = "depth:resize"; _manager; _xrView; _positionData = new Float32Array(3); _viewport = new Vec4(); _projMat = new Mat4(); _projViewOffMat = new Mat4(); _viewMat = new Mat4(); _viewOffMat = new Mat4(); _viewMat3 = new Mat3(); _viewInvMat = new Mat4(); _viewInvOffMat = new Mat4(); _xrCamera = null; _textureColor = null; _textureDepth = null; _depthInfo = null; _emptyDepthBuffer = new Uint8Array(32); _depthMatrix = new Mat4(); constructor(manager, xrView, viewsCount) { super(); 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 viewport = this._manager.xrBridge.getViewport(frame, 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; } this._manager.xrBridge?.syncCameraColorTexture(this._xrCamera, this._textureColor); } _updateDepth(frame) { if (!this._manager.views.availableDepth || !this._textureDepth) { return; } const gpu = this._manager.views.depthGpuOptimized; const infoSource = gpu ? this._manager.graphicsBinding : 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) { this._manager.xrBridge?.syncCameraDepthTexture( this._depthInfo, this._textureDepth, this._manager.views.depthPixelFormat ?? PIXELFORMAT_R32F ); } 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._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; } } } export { XrView };