From 36b02e6ca59663974a62d65ca2c99af6cc61a97c Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Wed, 15 Jan 2025 15:21:54 +0200 Subject: [PATCH 1/3] feat(viewer-lib): Updates to the PassReader extension along with underlying viewer library updates Implemented reading framebuffer contents for framebuffers with multiple attachements. The current version of three.js that we are using does not support this. Because we still need to drag along WebGL 1.0 support, only attachement 0 can be read for now, which does not bother us. DepthNormalPass now specifies it's MRT output target as the outputTarget PassReader's read function is now overloaded and it can take a pass name as a string or a GPass | GPass[] Had to add a small type augmentation since the current version of types-three library does a poor job when it comes to WebGLMultipleRenderTargets Updated PassReader extension in frontend and updated the call to read in order to make sure depth reading works in other view modes that write depth. The only view mode that does not draw to depth is Shaded mode --- .../lib/viewer/extensions/PassReader.ts | 52 ++- .../src/Extensions/PassReader.ts | 60 ++- packages/viewer-sandbox/src/Sandbox.ts | 17 +- packages/viewer-sandbox/src/main.ts | 6 +- .../modules/objects/SpeckleWebGLRenderer.ts | 442 +++++++++++++++++- .../pipeline/Passes/DepthNormalPass.ts | 7 +- .../viewer/src/type-augmentations/three.d.ts | 5 + 7 files changed, 545 insertions(+), 44 deletions(-) diff --git a/packages/frontend-2/lib/viewer/extensions/PassReader.ts b/packages/frontend-2/lib/viewer/extensions/PassReader.ts index 2fe49767bb..e9b186a905 100644 --- a/packages/frontend-2/lib/viewer/extensions/PassReader.ts +++ b/packages/frontend-2/lib/viewer/extensions/PassReader.ts @@ -1,4 +1,4 @@ -import type { SpeckleRenderer } from '@speckle/viewer' +import type { GPass, SpeckleRenderer } from '@speckle/viewer' import { Extension } from '@speckle/viewer' import type { WebGLRenderTarget } from 'three' import { Vector3, Vector4 } from 'three' @@ -11,21 +11,34 @@ export class PassReader extends Extension { | ((arg: [Uint8ClampedArray, number, number]) => void) | null = null - public async read(passName: string): Promise<[Uint8ClampedArray, number, number]> { + public async read(pass: string): Promise<[Uint8ClampedArray, number, number]> + public async read(pass: GPass | GPass[]): Promise<[Uint8ClampedArray, number, number]> + + public async read( + pass: string | GPass | GPass[] + ): Promise<[Uint8ClampedArray, number, number]> { return new Promise<[Uint8ClampedArray, number, number]>((resolve, reject) => { const renderer: SpeckleRenderer = this.viewer.getRenderer() + let passes: GPass[] + if (typeof pass === 'string') passes = renderer.pipeline.getPass(pass) + else if (Array.isArray(pass)) passes = pass + else passes = [pass] - const depthPass = renderer.pipeline.getPass(passName)[0] + if (!passes || !passes.length) { + reject(`Could not read from pass`) + return + } + const validPass = passes.find((pass: GPass) => this.hasFramebuffer(pass)) - if (!depthPass) { - reject(`Pipeline does not have a ${passName} pass`) + if (!validPass) { + reject(`Requested pass does not have a valid framebuffer`) return } - this.renderTarget = depthPass.outputTarget + this.renderTarget = validPass.outputTarget if (!this.renderTarget) { - reject('Pass does not have a render target assigned') + reject('Requested Pass does not have a render target assigned') return } @@ -38,19 +51,24 @@ export class PassReader extends Extension { }) } + protected hasFramebuffer(pass: GPass) { + const renderer = this.viewer.getRenderer().renderer + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + return renderer.properties.get(pass.outputTarget).__webglFramebuffer !== undefined + } + public onRender(): void { if (!this.needsRead || !this.renderTarget) return - this.viewer - .getRenderer() - .renderer.readRenderTargetPixels( - this.renderTarget, - 0, - 0, - this.renderTarget.width, - this.renderTarget.height, - this.outputBuffer - ) + const renderer = this.viewer.getRenderer().renderer + renderer.readRenderTargetPixels( + this.renderTarget, + 0, + 0, + this.renderTarget.width, + this.renderTarget.height, + this.outputBuffer + ) if (this.readbackExecutor) this.readbackExecutor([ diff --git a/packages/viewer-sandbox/src/Extensions/PassReader.ts b/packages/viewer-sandbox/src/Extensions/PassReader.ts index 5ae2cf59cb..e9b186a905 100644 --- a/packages/viewer-sandbox/src/Extensions/PassReader.ts +++ b/packages/viewer-sandbox/src/Extensions/PassReader.ts @@ -1,4 +1,4 @@ -import type { SpeckleRenderer } from '@speckle/viewer' +import type { GPass, SpeckleRenderer } from '@speckle/viewer' import { Extension } from '@speckle/viewer' import type { WebGLRenderTarget } from 'three' import { Vector3, Vector4 } from 'three' @@ -11,21 +11,34 @@ export class PassReader extends Extension { | ((arg: [Uint8ClampedArray, number, number]) => void) | null = null - public async read(passName: string): Promise<[Uint8ClampedArray, number, number]> { + public async read(pass: string): Promise<[Uint8ClampedArray, number, number]> + public async read(pass: GPass | GPass[]): Promise<[Uint8ClampedArray, number, number]> + + public async read( + pass: string | GPass | GPass[] + ): Promise<[Uint8ClampedArray, number, number]> { return new Promise<[Uint8ClampedArray, number, number]>((resolve, reject) => { const renderer: SpeckleRenderer = this.viewer.getRenderer() + let passes: GPass[] + if (typeof pass === 'string') passes = renderer.pipeline.getPass(pass) + else if (Array.isArray(pass)) passes = pass + else passes = [pass] - const depthPass = renderer.pipeline.getPass(passName)[0] + if (!passes || !passes.length) { + reject(`Could not read from pass`) + return + } + const validPass = passes.find((pass: GPass) => this.hasFramebuffer(pass)) - if (!depthPass) { - reject(`Pipeline does not have a ${passName} pass`) + if (!validPass) { + reject(`Requested pass does not have a valid framebuffer`) return } - this.renderTarget = depthPass.outputTarget + this.renderTarget = validPass.outputTarget if (!this.renderTarget) { - reject('Pass does not have a render target assigned') + reject('Requested Pass does not have a render target assigned') return } @@ -38,19 +51,24 @@ export class PassReader extends Extension { }) } + protected hasFramebuffer(pass: GPass) { + const renderer = this.viewer.getRenderer().renderer + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + return renderer.properties.get(pass.outputTarget).__webglFramebuffer !== undefined + } + public onRender(): void { if (!this.needsRead || !this.renderTarget) return - this.viewer - .getRenderer() - .renderer.readRenderTargetPixels( - this.renderTarget, - 0, - 0, - this.renderTarget.width, - this.renderTarget.height, - this.outputBuffer - ) + const renderer = this.viewer.getRenderer().renderer + renderer.readRenderTargetPixels( + this.renderTarget, + 0, + 0, + this.renderTarget.width, + this.renderTarget.height, + this.outputBuffer + ) if (this.readbackExecutor) this.readbackExecutor([ @@ -89,10 +107,14 @@ export class PassReader extends Extension { return buffer } - public static toBase64(buffer: Uint8ClampedArray, width: number, height: number) { + public static toBase64( + buffer: Uint8ClampedArray, + width: number, + height: number + ): string { const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') - if (!ctx) return + if (!ctx) return '' canvas.width = width canvas.height = height diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index c75c8de386..84cb21a4fd 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -57,6 +57,7 @@ import Bright from '../assets/hdri/Bright.png' import { Euler, Vector3, Box3, Color, LinearFilter } from 'three' import { GeometryType } from '@speckle/viewer' import { MeshBatch } from '@speckle/viewer' +import { PassReader } from './Extensions/PassReader' export default class Sandbox { private viewer: Viewer @@ -500,10 +501,18 @@ export default class Sandbox { title: 'Screenshot' }) screenshot.on('click', async () => { - // console.warn(await this.viewer.screenshot()) - this.viewer - .getExtension(FilteringExtension) - .hideObjects(['1facfaaf1d3682707edd9ac20ef34e62']) + console.warn(await this.viewer.screenshot()) + + /** Read depth */ + // const pass = [ + // ...this.viewer.getRenderer().pipeline.getPass('DEPTH'), + // ...this.viewer.getRenderer().pipeline.getPass('DEPTH-NORMAL') + // ] + // const [depthData, width, height] = await this.viewer + // .getExtension(PassReader) + // .read(pass) + + // console.log(PassReader.toBase64(PassReader.decodeDepth(depthData), width, height)) }) const rotate = this.tabs.pages[0].addButton({ diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index 164e09f3e1..b63965434e 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -21,6 +21,7 @@ import { SectionTool } from '@speckle/viewer' import { SectionOutlines } from '@speckle/viewer' import { ViewModesKeys } from './Extensions/ViewModesKeys' import { BoxSelection } from './Extensions/BoxSelection' +import { PassReader } from './Extensions/PassReader' const createViewer = async (containerName: string, _stream: string) => { const container = document.querySelector(containerName) @@ -56,6 +57,7 @@ const createViewer = async (containerName: string, _stream: string) => { viewer.createExtension(ViewModesKeys) const boxSelect = viewer.createExtension(BoxSelection) boxSelect.realtimeSelection = false + viewer.createExtension(PassReader) // const rotateCamera = viewer.createExtension(RotateCamera) cameraController // use it selection // use it @@ -111,7 +113,7 @@ const getStream = () => { // prettier-ignore // 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D' // Revit sample house (good for bim-like stuff with many display meshes) - // 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8' + 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8' // 'https://latest.speckle.systems/streams/c1faab5c62/commits/ab1a1ab2b6' // 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8' // 'https://latest.speckle.systems/streams/58b5648c4d/commits/60371ecb2d' @@ -466,7 +468,7 @@ const getStream = () => { // Instance toilets // 'https://app.speckle.systems/projects/e89b61b65c/models/2a0995f124' - 'https://latest.speckle.systems/projects/3fe1880c36/models/65bb4287a8' + // 'https://latest.speckle.systems/projects/3fe1880c36/models/65bb4287a8' ) } diff --git a/packages/viewer/src/modules/objects/SpeckleWebGLRenderer.ts b/packages/viewer/src/modules/objects/SpeckleWebGLRenderer.ts index bc71a0552c..30ae1c3cdc 100644 --- a/packages/viewer/src/modules/objects/SpeckleWebGLRenderer.ts +++ b/packages/viewer/src/modules/objects/SpeckleWebGLRenderer.ts @@ -1,5 +1,66 @@ -import { Camera, Matrix4, Vector3, WebGLRenderer } from 'three' +/* eslint-disable camelcase */ +import { + _SRGBAFormat, + AlphaFormat, + ByteType, + Camera, + DepthFormat, + DepthStencilFormat, + FloatType, + HalfFloatType, + IntType, + LuminanceAlphaFormat, + LuminanceFormat, + Matrix4, + RedFormat, + RedIntegerFormat, + RGB_ETC1_Format, + RGB_ETC2_Format, + RGB_PVRTC_2BPPV1_Format, + RGB_PVRTC_4BPPV1_Format, + RGB_S3TC_DXT1_Format, + RGBA_ASTC_10x10_Format, + RGBA_ASTC_10x5_Format, + RGBA_ASTC_10x6_Format, + RGBA_ASTC_10x8_Format, + RGBA_ASTC_12x10_Format, + RGBA_ASTC_12x12_Format, + RGBA_ASTC_4x4_Format, + RGBA_ASTC_5x4_Format, + RGBA_ASTC_5x5_Format, + RGBA_ASTC_6x5_Format, + RGBA_ASTC_6x6_Format, + RGBA_ASTC_8x5_Format, + RGBA_ASTC_8x6_Format, + RGBA_ASTC_8x8_Format, + RGBA_BPTC_Format, + RGBA_ETC2_EAC_Format, + RGBA_PVRTC_2BPPV1_Format, + RGBA_PVRTC_4BPPV1_Format, + RGBA_S3TC_DXT1_Format, + RGBA_S3TC_DXT3_Format, + RGBA_S3TC_DXT5_Format, + RGBAFormat, + RGBAIntegerFormat, + RGBFormat, + RGFormat, + RGIntegerFormat, + ShortType, + sRGBEncoding, + UnsignedByteType, + UnsignedInt248Type, + UnsignedIntType, + UnsignedShort4444Type, + UnsignedShort5551Type, + UnsignedShortType, + Vector3, + WebGLCubeRenderTarget, + WebGLMultipleRenderTargets, + WebGLRenderer, + WebGLRenderTarget +} from 'three' import { Geometry } from '../converter/Geometry.js' +import { TypedArray } from 'type-fest' export class RTEBuffers { private _cache: RTEBuffers | undefined @@ -59,4 +120,383 @@ export class SpeckleWebGLRenderer extends WebGLRenderer { this.RTEBuffers.viewerHigh ) } + + public readRenderTargetPixels = ( + renderTarget: + | WebGLRenderTarget + | WebGLCubeRenderTarget + | WebGLMultipleRenderTargets, + x: number, + y: number, + width: number, + height: number, + buffer: TypedArray, + activeCubeFaceIndex: number = 0 + ) => { + if ( + !( + renderTarget && + (renderTarget instanceof WebGLRenderTarget || + renderTarget instanceof WebGLMultipleRenderTargets) + ) + ) { + console.error( + 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' + ) + return + } + + let framebuffer = this.properties.get(renderTarget).__webglFramebuffer + + if ( + renderTarget instanceof WebGLCubeRenderTarget && + activeCubeFaceIndex !== undefined + ) { + framebuffer = framebuffer[activeCubeFaceIndex] + } + + if (framebuffer) { + this.state.bindFramebuffer(36160, framebuffer) + + try { + const texture = Array.isArray(renderTarget.texture) + ? renderTarget.texture[0] + : renderTarget.texture + const textureFormat = texture.format + const textureType = texture.type + + if ( + textureFormat !== RGBAFormat && + this.convert(textureFormat) !== this.context.getParameter(35739) + ) { + console.error( + 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' + ) + return + } + + const halfFloatSupportedByExt = + textureType === HalfFloatType && + (this.extensions.has('EXT_color_buffer_half_float') || + (this.capabilities.isWebGL2 && + this.extensions.has('EXT_color_buffer_float'))) + + if ( + textureType !== UnsignedByteType && + this.convert(textureType) !== this.context.getParameter(35738) && // Edge and Chrome Mac < 52 (#9513) + !( + textureType === FloatType && + (this.capabilities.isWebGL2 || + this.extensions.has('OES_texture_float') || + this.extensions.has('WEBGL_color_buffer_float')) + ) && // Chrome Mac >= 52 and Firefox + !halfFloatSupportedByExt + ) { + console.error( + 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' + ) + return + } + + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + + if ( + x >= 0 && + x <= renderTarget.width - width && + y >= 0 && + y <= renderTarget.height - height + ) { + this.context.readPixels( + x, + y, + width, + height, + this.convert(textureFormat), + this.convert(textureType), + buffer + ) + } + } finally { + // restore framebuffer of current render target if necessary + + const framebuffer = + this.getRenderTarget() !== null + ? this.properties.get(this.getRenderTarget()).__webglFramebuffer + : null + this.state.bindFramebuffer(36160, framebuffer) + } + } + } + + /** Taken from three since they hide it, thank you very much */ + private convert(p: number, encoding = null) { + let extension + + if (p === UnsignedByteType) return 5121 + if (p === UnsignedShort4444Type) return 32819 + if (p === UnsignedShort5551Type) return 32820 + + if (p === ByteType) return 5120 + if (p === ShortType) return 5122 + if (p === UnsignedShortType) return 5123 + if (p === IntType) return 5124 + if (p === UnsignedIntType) return 5125 + if (p === FloatType) return 5126 + + if (p === HalfFloatType) { + if (this.capabilities.isWebGL2) return 5131 + + extension = this.extensions.get('OES_texture_half_float') + + if (extension !== null) { + return extension.HALF_FLOAT_OES + } else { + return null + } + } + + if (p === AlphaFormat) return 6406 + if (p === RGBAFormat) return 6408 + if (p === LuminanceFormat) return 6409 + if (p === LuminanceAlphaFormat) return 6410 + if (p === DepthFormat) return 6402 + if (p === DepthStencilFormat) return 34041 + if (p === RedFormat) return 6403 + + if (p === RGBFormat) { + console.warn( + 'THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228' + ) + return 6408 + } + + // WebGL 1 sRGB fallback + + if (p === _SRGBAFormat) { + extension = this.extensions.get('EXT_sRGB') + + if (extension !== null) { + return extension.SRGB_ALPHA_EXT + } else { + return null + } + } + + // WebGL2 formats. + + if (p === RedIntegerFormat) return 36244 + if (p === RGFormat) return 33319 + if (p === RGIntegerFormat) return 33320 + if (p === RGBAIntegerFormat) return 36249 + + // S3TC + + if ( + p === RGB_S3TC_DXT1_Format || + p === RGBA_S3TC_DXT1_Format || + p === RGBA_S3TC_DXT3_Format || + p === RGBA_S3TC_DXT5_Format + ) { + if (encoding === sRGBEncoding) { + extension = this.extensions.get('WEBGL_compressed_texture_s3tc_srgb') + + if (extension !== null) { + if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT + if (p === RGBA_S3TC_DXT1_Format) + return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT + if (p === RGBA_S3TC_DXT3_Format) + return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT + if (p === RGBA_S3TC_DXT5_Format) + return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT + } else { + return null + } + } else { + extension = this.extensions.get('WEBGL_compressed_texture_s3tc') + + if (extension !== null) { + if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT + if (p === RGBA_S3TC_DXT1_Format) + return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT + if (p === RGBA_S3TC_DXT3_Format) + return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT + if (p === RGBA_S3TC_DXT5_Format) + return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT + } else { + return null + } + } + } + + // PVRTC + + if ( + p === RGB_PVRTC_4BPPV1_Format || + p === RGB_PVRTC_2BPPV1_Format || + p === RGBA_PVRTC_4BPPV1_Format || + p === RGBA_PVRTC_2BPPV1_Format + ) { + extension = this.extensions.get('WEBGL_compressed_texture_pvrtc') + + if (extension !== null) { + if (p === RGB_PVRTC_4BPPV1_Format) + return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG + if (p === RGB_PVRTC_2BPPV1_Format) + return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG + if (p === RGBA_PVRTC_4BPPV1_Format) + return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + if (p === RGBA_PVRTC_2BPPV1_Format) + return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG + } else { + return null + } + } + + // ETC1 + + if (p === RGB_ETC1_Format) { + extension = this.extensions.get('WEBGL_compressed_texture_etc1') + + if (extension !== null) { + return extension.COMPRESSED_RGB_ETC1_WEBGL + } else { + return null + } + } + + // ETC2 + + if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) { + extension = this.extensions.get('WEBGL_compressed_texture_etc') + + if (extension !== null) { + if (p === RGB_ETC2_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ETC2 + : extension.COMPRESSED_RGB8_ETC2 + if (p === RGBA_ETC2_EAC_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + : extension.COMPRESSED_RGBA8_ETC2_EAC + } else { + return null + } + } + + // ASTC + + if ( + p === RGBA_ASTC_4x4_Format || + p === RGBA_ASTC_5x4_Format || + p === RGBA_ASTC_5x5_Format || + p === RGBA_ASTC_6x5_Format || + p === RGBA_ASTC_6x6_Format || + p === RGBA_ASTC_8x5_Format || + p === RGBA_ASTC_8x6_Format || + p === RGBA_ASTC_8x8_Format || + p === RGBA_ASTC_10x5_Format || + p === RGBA_ASTC_10x6_Format || + p === RGBA_ASTC_10x8_Format || + p === RGBA_ASTC_10x10_Format || + p === RGBA_ASTC_12x10_Format || + p === RGBA_ASTC_12x12_Format + ) { + extension = this.extensions.get('WEBGL_compressed_texture_astc') + + if (extension !== null) { + if (p === RGBA_ASTC_4x4_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR + : extension.COMPRESSED_RGBA_ASTC_4x4_KHR + if (p === RGBA_ASTC_5x4_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR + : extension.COMPRESSED_RGBA_ASTC_5x4_KHR + if (p === RGBA_ASTC_5x5_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR + : extension.COMPRESSED_RGBA_ASTC_5x5_KHR + if (p === RGBA_ASTC_6x5_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR + : extension.COMPRESSED_RGBA_ASTC_6x5_KHR + if (p === RGBA_ASTC_6x6_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR + : extension.COMPRESSED_RGBA_ASTC_6x6_KHR + if (p === RGBA_ASTC_8x5_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR + : extension.COMPRESSED_RGBA_ASTC_8x5_KHR + if (p === RGBA_ASTC_8x6_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR + : extension.COMPRESSED_RGBA_ASTC_8x6_KHR + if (p === RGBA_ASTC_8x8_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR + : extension.COMPRESSED_RGBA_ASTC_8x8_KHR + if (p === RGBA_ASTC_10x5_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR + : extension.COMPRESSED_RGBA_ASTC_10x5_KHR + if (p === RGBA_ASTC_10x6_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR + : extension.COMPRESSED_RGBA_ASTC_10x6_KHR + if (p === RGBA_ASTC_10x8_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR + : extension.COMPRESSED_RGBA_ASTC_10x8_KHR + if (p === RGBA_ASTC_10x10_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR + : extension.COMPRESSED_RGBA_ASTC_10x10_KHR + if (p === RGBA_ASTC_12x10_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR + : extension.COMPRESSED_RGBA_ASTC_12x10_KHR + if (p === RGBA_ASTC_12x12_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR + : extension.COMPRESSED_RGBA_ASTC_12x12_KHR + } else { + return null + } + } + + // BPTC + + if (p === RGBA_BPTC_Format) { + extension = this.extensions.get('EXT_texture_compression_bptc') + + if (extension !== null) { + if (p === RGBA_BPTC_Format) + return encoding === sRGBEncoding + ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT + : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT + } else { + return null + } + } + + // + + if (p === UnsignedInt248Type) { + if (this.capabilities.isWebGL2) return 34042 + + extension = this.extensions.get('WEBGL_depth_texture') + + if (extension !== null) { + return extension.UNSIGNED_INT_24_8_WEBGL + } else { + return null + } + } + + // if "p" can't be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + return this.context[p] !== undefined ? this.context[p] : null + } } diff --git a/packages/viewer/src/modules/pipeline/Passes/DepthNormalPass.ts b/packages/viewer/src/modules/pipeline/Passes/DepthNormalPass.ts index c339bf9fc2..ee95d5bbcf 100644 --- a/packages/viewer/src/modules/pipeline/Passes/DepthNormalPass.ts +++ b/packages/viewer/src/modules/pipeline/Passes/DepthNormalPass.ts @@ -10,7 +10,8 @@ import { Scene, Texture, WebGLMultipleRenderTargets, - WebGLRenderer + WebGLRenderer, + WebGLRenderTarget } from 'three' import { BaseGPass } from './GPass.js' import { Pipeline } from '../Pipelines/Pipeline.js' @@ -48,6 +49,10 @@ export class DepthNormalPass extends BaseGPass { return this.mrt.texture[1] } + get outputTarget(): WebGLRenderTarget | null { + return this.mrt as unknown as WebGLRenderTarget + } + public set options(value: DepthNormalPassOptions) { super.options = value this.depthType = this._options.depthType diff --git a/packages/viewer/src/type-augmentations/three.d.ts b/packages/viewer/src/type-augmentations/three.d.ts index f6917c1660..eb8544b139 100644 --- a/packages/viewer/src/type-augmentations/three.d.ts +++ b/packages/viewer/src/type-augmentations/three.d.ts @@ -7,5 +7,10 @@ declare module 'three' { interface BufferGeometry { boundsTree: MeshBVH } + + interface WebGLMultipleRenderTargets { + width: number + height: number + } } export {} From 8a8fffd8fef429714215202d05bab91a95794383 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Wed, 15 Jan 2025 20:24:05 +0200 Subject: [PATCH 2/3] fix(viewer-lib): Fixed the classic sandbox compile error --- packages/viewer-sandbox/src/Sandbox.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index 84cb21a4fd..8d9bfde237 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -57,7 +57,6 @@ import Bright from '../assets/hdri/Bright.png' import { Euler, Vector3, Box3, Color, LinearFilter } from 'three' import { GeometryType } from '@speckle/viewer' import { MeshBatch } from '@speckle/viewer' -import { PassReader } from './Extensions/PassReader' export default class Sandbox { private viewer: Viewer From 8e63e37a38cb1eb4c46adb2c76469dea0daa91d4 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Thu, 16 Jan 2025 00:22:43 +0200 Subject: [PATCH 3/3] fix(frontend-2): Updated depth reading to work with MRT depth from our view mode pipelines --- packages/frontend-2/components/viewer/gendo/Panel.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/frontend-2/components/viewer/gendo/Panel.vue b/packages/frontend-2/components/viewer/gendo/Panel.vue index a19c388f5b..7b0a52c41e 100644 --- a/packages/frontend-2/components/viewer/gendo/Panel.vue +++ b/packages/frontend-2/components/viewer/gendo/Panel.vue @@ -158,9 +158,13 @@ const formattedResetDate = computed(() => { const enqueMagic = async () => { isLoading.value = true + const pass = [ + ...viewerInstance.getRenderer().pipeline.getPass('DEPTH'), + ...viewerInstance.getRenderer().pipeline.getPass('DEPTH-NORMAL') + ] const [depthData, width, height] = await viewerInstance .getExtension(PassReader) - .read('DEPTH') + .read(pass) const screenshot = PassReader.toBase64( PassReader.decodeDepth(depthData), width,