UNPKG

@babylonjs/viewer

Version:

The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.

1,110 lines (1,108 loc) 218 kB
import { h as RenderTargetTexture, C as Constants, T as Texture, P as PostProcess, O as Observable, M as Matrix, i as Vector4, L as Logger, a as EffectRenderer, E as EffectWrapper, j as Color4, k as Engine, V as Vector3, S as ShaderStore, l as MaterialFlags, m as VertexBuffer, n as PrepareDefinesAndAttributesForMorphTargets, o as PushAttributesForInstances, p as PrepareStringDefinesForClipPlanes, q as BindSceneUniformBuffer, r as BindClipPlane, s as BindMorphTargetParameters, t as Material, A as AddClipPlaneUniforms, _ as _WarnImport, b as Tools, u as __decorate, v as serialize, w as MaterialPluginBase, x as PBRBaseMaterial, y as MaterialDefines, z as serializeAsTexture, D as expandToProperty, R as RegisterClass, F as Scene, G as SceneComponentConstants, H as EngineStore, Q as Quaternion } from './index-FzOfPXLV.esm.js'; import { ShaderMaterial } from './shaderMaterial-Bz73fOLU.esm.js'; import './engine.multiRender-D0bO0nxA.esm.js'; import { P as ProceduralTexture, I as IblCdfGenerator } from './iblCdfGenerator-BDPVKn79.esm.js'; import './clipPlaneFragment-OFOZXtyS.esm.js'; import './bumpFragment-PSTyPyLQ.esm.js'; import './helperFunctions-CLFdU2UC.esm.js'; import './bakedVertexAnimation-D5Wmzm8F.esm.js'; import './morphTargetsVertex-Bimm08Fe.esm.js'; import './instancesDeclaration-D6f54GuO.esm.js'; import './sceneUboDeclaration-CeGVxetF.esm.js'; import './clipPlaneVertex-wC8bw87F.esm.js'; import './bumpVertex-CQnDcsrx.esm.js'; import { R as RawTexture } from './rawTexture-B2DimmQ5.esm.js'; import { S as StandardMaterial } from './standardMaterial-DpmQ1Io2.esm.js'; /** * A multi render target, like a render target provides the ability to render to a texture. * Unlike the render target, it can render to several draw buffers (render textures) in one draw. * This is specially interesting in deferred rendering or for any effects requiring more than * just one color from a single pass. */ class MultiRenderTarget extends RenderTargetTexture { /** * Get if draw buffers (render textures) are currently supported by the used hardware and browser. */ get isSupported() { return this._engine?.getCaps().drawBuffersExtension ?? false; } /** * Get the list of textures generated by the multi render target. */ get textures() { return this._textures; } /** * Gets the number of textures in this MRT. This number can be different from `_textures.length` in case a depth texture is generated. */ get count() { return this._count; } /** * Get the depth texture generated by the multi render target if options.generateDepthTexture has been set */ get depthTexture() { return this._textures[this._textures.length - 1]; } /** * Set the wrapping mode on U of all the textures we are rendering to. * Can be any of the Texture. (CLAMP_ADDRESSMODE, MIRROR_ADDRESSMODE or WRAP_ADDRESSMODE) */ set wrapU(wrap) { if (this._textures) { for (let i = 0; i < this._textures.length; i++) { this._textures[i].wrapU = wrap; } } } /** * Set the wrapping mode on V of all the textures we are rendering to. * Can be any of the Texture. (CLAMP_ADDRESSMODE, MIRROR_ADDRESSMODE or WRAP_ADDRESSMODE) */ set wrapV(wrap) { if (this._textures) { for (let i = 0; i < this._textures.length; i++) { this._textures[i].wrapV = wrap; } } } /** * Instantiate a new multi render target texture. * A multi render target, like a render target provides the ability to render to a texture. * Unlike the render target, it can render to several draw buffers (render textures) in one draw. * This is specially interesting in deferred rendering or for any effects requiring more than * just one color from a single pass. * @param name Define the name of the texture * @param size Define the size of the buffers to render to * @param count Define the number of target we are rendering into * @param scene Define the scene the texture belongs to * @param options Define the options used to create the multi render target * @param textureNames Define the names to set to the textures (if count \> 0 - optional) */ constructor(name, size, count, scene, options, textureNames) { const generateMipMaps = options && options.generateMipMaps ? options.generateMipMaps : false; const generateDepthTexture = options && options.generateDepthTexture ? options.generateDepthTexture : false; const depthTextureFormat = options && options.depthTextureFormat ? options.depthTextureFormat : Constants.TEXTUREFORMAT_DEPTH16; const doNotChangeAspectRatio = !options || options.doNotChangeAspectRatio === undefined ? true : options.doNotChangeAspectRatio; const drawOnlyOnFirstAttachmentByDefault = options && options.drawOnlyOnFirstAttachmentByDefault ? options.drawOnlyOnFirstAttachmentByDefault : false; super(name, size, scene, generateMipMaps, doNotChangeAspectRatio, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true); if (!this.isSupported) { this.dispose(); return; } this._textureNames = textureNames; const types = []; const samplingModes = []; const useSRGBBuffers = []; const formats = []; const targetTypes = []; const faceIndex = []; const layerIndex = []; const layerCounts = []; this._initTypes(count, types, samplingModes, useSRGBBuffers, formats, targetTypes, faceIndex, layerIndex, layerCounts, options); const generateDepthBuffer = !options || options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer; const generateStencilBuffer = !options || options.generateStencilBuffer === undefined ? false : options.generateStencilBuffer; const samples = options && options.samples ? options.samples : 1; this._multiRenderTargetOptions = { samplingModes: samplingModes, generateMipMaps: generateMipMaps, generateDepthBuffer: generateDepthBuffer, generateStencilBuffer: generateStencilBuffer, generateDepthTexture: generateDepthTexture, depthTextureFormat: depthTextureFormat, types: types, textureCount: count, useSRGBBuffers: useSRGBBuffers, samples, formats: formats, targetTypes: targetTypes, faceIndex: faceIndex, layerIndex: layerIndex, layerCounts: layerCounts, labels: textureNames, label: name, }; this._count = count; this._drawOnlyOnFirstAttachmentByDefault = drawOnlyOnFirstAttachmentByDefault; if (count > 0) { this._createInternalTextures(); this._createTextures(textureNames); } } _initTypes(count, types, samplingModes, useSRGBBuffers, formats, targets, faceIndex, layerIndex, layerCounts, options) { for (let i = 0; i < count; i++) { if (options && options.types && options.types[i] !== undefined) { types.push(options.types[i]); } else { types.push(options && options.defaultType ? options.defaultType : Constants.TEXTURETYPE_UNSIGNED_BYTE); } if (options && options.samplingModes && options.samplingModes[i] !== undefined) { samplingModes.push(options.samplingModes[i]); } else { samplingModes.push(Texture.BILINEAR_SAMPLINGMODE); } if (options && options.useSRGBBuffers && options.useSRGBBuffers[i] !== undefined) { useSRGBBuffers.push(options.useSRGBBuffers[i]); } else { useSRGBBuffers.push(false); } if (options && options.formats && options.formats[i] !== undefined) { formats.push(options.formats[i]); } else { formats.push(Constants.TEXTUREFORMAT_RGBA); } if (options && options.targetTypes && options.targetTypes[i] !== undefined) { targets.push(options.targetTypes[i]); } else { targets.push(Constants.TEXTURE_2D); } if (options && options.faceIndex && options.faceIndex[i] !== undefined) { faceIndex.push(options.faceIndex[i]); } else { faceIndex.push(0); } if (options && options.layerIndex && options.layerIndex[i] !== undefined) { layerIndex.push(options.layerIndex[i]); } else { layerIndex.push(0); } if (options && options.layerCounts && options.layerCounts[i] !== undefined) { layerCounts.push(options.layerCounts[i]); } else { layerCounts.push(1); } } } _createInternaTextureIndexMapping() { const mapMainInternalTexture2Index = {}; const mapInternalTexture2MainIndex = []; if (!this._renderTarget) { return mapInternalTexture2MainIndex; } const internalTextures = this._renderTarget.textures; for (let i = 0; i < internalTextures.length; i++) { const texture = internalTextures[i]; if (!texture) { continue; } const mainIndex = mapMainInternalTexture2Index[texture.uniqueId]; if (mainIndex !== undefined) { mapInternalTexture2MainIndex[i] = mainIndex; } else { mapMainInternalTexture2Index[texture.uniqueId] = i; } } return mapInternalTexture2MainIndex; } /** * @internal */ _rebuild(fromContextLost = false, forceFullRebuild = false, textureNames) { if (this._count < 1 || fromContextLost) { return; } const mapInternalTexture2MainIndex = this._createInternaTextureIndexMapping(); this.releaseInternalTextures(); this._createInternalTextures(); if (forceFullRebuild) { this._releaseTextures(); this._createTextures(textureNames); } const internalTextures = this._renderTarget.textures; for (let i = 0; i < internalTextures.length; i++) { const texture = this._textures[i]; if (mapInternalTexture2MainIndex[i] !== undefined) { this._renderTarget.setTexture(internalTextures[mapInternalTexture2MainIndex[i]], i); } texture._texture = internalTextures[i]; if (texture._texture) { texture._noMipmap = !texture._texture.useMipMaps; texture._useSRGBBuffer = texture._texture._useSRGBBuffer; } } if (this.samples !== 1) { this._renderTarget.setSamples(this.samples, !this._drawOnlyOnFirstAttachmentByDefault, true); } } _createInternalTextures() { this._renderTarget = this._getEngine().createMultipleRenderTarget(this._size, this._multiRenderTargetOptions, !this._drawOnlyOnFirstAttachmentByDefault); this._texture = this._renderTarget.texture; } _releaseTextures() { if (this._textures) { for (let i = 0; i < this._textures.length; i++) { this._textures[i]._texture = null; // internal textures are released by a call to releaseInternalTextures() this._textures[i].dispose(); } } } _createTextures(textureNames) { const internalTextures = this._renderTarget.textures; this._textures = []; for (let i = 0; i < internalTextures.length; i++) { const texture = new Texture(null, this.getScene()); if (textureNames?.[i]) { texture.name = textureNames[i]; } texture._texture = internalTextures[i]; if (texture._texture) { texture._noMipmap = !texture._texture.useMipMaps; texture._useSRGBBuffer = texture._texture._useSRGBBuffer; } this._textures.push(texture); } } /** * Replaces an internal texture within the MRT. Useful to share textures between MultiRenderTarget. * @param texture The new texture to set in the MRT * @param index The index of the texture to replace * @param disposePrevious Set to true if the previous internal texture should be disposed */ setInternalTexture(texture, index, disposePrevious = true) { if (!this.renderTarget) { return; } if (index === 0) { this._texture = texture; } this.renderTarget.setTexture(texture, index, disposePrevious); if (!this.textures[index]) { this.textures[index] = new Texture(null, this.getScene()); this.textures[index].name = this._textureNames?.[index] ?? this.textures[index].name; } this.textures[index]._texture = texture; this.textures[index]._noMipmap = !texture.useMipMaps; this.textures[index]._useSRGBBuffer = texture._useSRGBBuffer; this._count = this.renderTarget.textures ? this.renderTarget.textures.length : 0; if (this._multiRenderTargetOptions.types) { this._multiRenderTargetOptions.types[index] = texture.type; } if (this._multiRenderTargetOptions.samplingModes) { this._multiRenderTargetOptions.samplingModes[index] = texture.samplingMode; } if (this._multiRenderTargetOptions.useSRGBBuffers) { this._multiRenderTargetOptions.useSRGBBuffers[index] = texture._useSRGBBuffer; } if (this._multiRenderTargetOptions.targetTypes && this._multiRenderTargetOptions.targetTypes[index] !== -1) { let target = 0; if (texture.is2DArray) { target = Constants.TEXTURE_2D_ARRAY; } else if (texture.isCube) { target = Constants.TEXTURE_CUBE_MAP; } /*else if (texture.isCubeArray) { target = Constants.TEXTURE_CUBE_MAP_ARRAY; }*/ else if (texture.is3D) { target = Constants.TEXTURE_3D; } else { target = Constants.TEXTURE_2D; } this._multiRenderTargetOptions.targetTypes[index] = target; } } /** * Changes an attached texture's face index or layer. * @param index The index of the texture to modify the attachment of * @param layerIndex The layer index of the texture to be attached to the framebuffer * @param faceIndex The face index of the texture to be attached to the framebuffer */ setLayerAndFaceIndex(index, layerIndex = -1, faceIndex = -1) { if (!this.textures[index] || !this.renderTarget) { return; } if (this._multiRenderTargetOptions.layerIndex) { this._multiRenderTargetOptions.layerIndex[index] = layerIndex; } if (this._multiRenderTargetOptions.faceIndex) { this._multiRenderTargetOptions.faceIndex[index] = faceIndex; } this.renderTarget.setLayerAndFaceIndex(index, layerIndex, faceIndex); } /** * Changes every attached texture's face index or layer. * @param layerIndices The layer indices of the texture to be attached to the framebuffer * @param faceIndices The face indices of the texture to be attached to the framebuffer */ setLayerAndFaceIndices(layerIndices, faceIndices) { if (!this.renderTarget) { return; } this._multiRenderTargetOptions.layerIndex = layerIndices; this._multiRenderTargetOptions.faceIndex = faceIndices; this.renderTarget.setLayerAndFaceIndices(layerIndices, faceIndices); } /** * Define the number of samples used if MSAA is enabled. */ get samples() { return this._samples; } set samples(value) { if (this._renderTarget) { this._samples = this._renderTarget.setSamples(value); } else { // In case samples are set with 0 textures created, we must save the desired samples value this._samples = value; } } /** * Resize all the textures in the multi render target. * Be careful as it will recreate all the data in the new texture. * @param size Define the new size */ resize(size) { this._processSizeParameter(size); this._rebuild(false, undefined, this._textureNames); } /** * Changes the number of render targets in this MRT * Be careful as it will recreate all the data in the new texture. * @param count new texture count * @param options Specifies texture types and sampling modes for new textures * @param textureNames Specifies the names of the textures (optional) */ updateCount(count, options, textureNames) { this._multiRenderTargetOptions.textureCount = count; this._count = count; const types = []; const samplingModes = []; const useSRGBBuffers = []; const formats = []; const targetTypes = []; const faceIndex = []; const layerIndex = []; const layerCounts = []; this._textureNames = textureNames; this._initTypes(count, types, samplingModes, useSRGBBuffers, formats, targetTypes, faceIndex, layerIndex, layerCounts, options); this._multiRenderTargetOptions.types = types; this._multiRenderTargetOptions.samplingModes = samplingModes; this._multiRenderTargetOptions.useSRGBBuffers = useSRGBBuffers; this._multiRenderTargetOptions.formats = formats; this._multiRenderTargetOptions.targetTypes = targetTypes; this._multiRenderTargetOptions.faceIndex = faceIndex; this._multiRenderTargetOptions.layerIndex = layerIndex; this._multiRenderTargetOptions.layerCounts = layerCounts; this._multiRenderTargetOptions.labels = textureNames; this._rebuild(false, true, textureNames); } _unbindFrameBuffer(engine, faceIndex) { if (this._renderTarget) { engine.unBindMultiColorAttachmentFramebuffer(this._renderTarget, this.isCube, () => { this.onAfterRenderObservable.notifyObservers(faceIndex); }); } } /** * Dispose the render targets and their associated resources * @param doNotDisposeInternalTextures if set to true, internal textures won't be disposed (default: false). */ dispose(doNotDisposeInternalTextures = false) { this._releaseTextures(); if (!doNotDisposeInternalTextures) { this.releaseInternalTextures(); } else { // Prevent internal texture dispose in super.dispose this._texture = null; } super.dispose(); } /** * Release all the underlying texture used as draw buffers (render textures). */ releaseInternalTextures() { const internalTextures = this._renderTarget?.textures; if (!internalTextures) { return; } for (let i = internalTextures.length - 1; i >= 0; i--) { this._textures[i]._texture = null; } this._renderTarget?.dispose(); this._renderTarget = null; } } /** * Voxel-based shadow rendering for IBL's. * This should not be instanciated directly, as it is part of a scene component * @internal * @see https://playground.babylonjs.com/#8R5SSE#222 */ class _IblShadowsVoxelRenderer { /** * Return the voxel grid texture. * @returns The voxel grid texture. */ getVoxelGrid() { if (this._triPlanarVoxelization) { return this._voxelGridRT; } else { return this._voxelGridZaxis; } } /** * The debug pass post process * @returns The debug pass post process */ getDebugPassPP() { if (!this._voxelDebugPass) { this._createDebugPass(); } return this._voxelDebugPass; } /** * Whether to use tri-planar voxelization. More expensive, but can help with artifacts. */ get triPlanarVoxelization() { return this._triPlanarVoxelization; } /** * Whether to use tri-planar voxelization. More expensive, but can help with artifacts. */ set triPlanarVoxelization(enabled) { if (this._triPlanarVoxelization === enabled) { return; } this._triPlanarVoxelization = enabled; this._disposeVoxelTextures(); this._createTextures(); } /** * Set the matrix to use for scaling the world space to voxel space * @param matrix The matrix to use for scaling the world space to voxel space */ setWorldScaleMatrix(matrix) { this._invWorldScaleMatrix = matrix; } /** * @returns Whether voxelization is currently happening. */ isVoxelizationInProgress() { return this._voxelizationInProgress; } /** * Resolution of the voxel grid. The final resolution will be 2^resolutionExp. */ get voxelResolutionExp() { return this._voxelResolutionExp; } /** * Resolution of the voxel grid. The final resolution will be 2^resolutionExp. */ set voxelResolutionExp(resolutionExp) { if (this._voxelResolutionExp === resolutionExp && this._voxelGridZaxis) { return; } this._voxelResolutionExp = Math.round(Math.min(Math.max(resolutionExp, 3), 9)); this._voxelResolution = Math.pow(2.0, this._voxelResolutionExp); this._disposeVoxelTextures(); this._createTextures(); } /** * Shows only the voxels that were rendered along a particular axis (while using triPlanarVoxelization). * If not set, the combined voxel grid will be shown. * Note: This only works when the debugMipNumber is set to 0 because we don't generate mips for each axis. * @param axis The axis to show (0 = x, 1 = y, 2 = z) */ set voxelDebugAxis(axis) { this._voxelDebugAxis = axis; } get voxelDebugAxis() { return this._voxelDebugAxis; } /** * Sets params that control the position and scaling of the debug display on the screen. * @param x Screen X offset of the debug display (0-1) * @param y Screen Y offset of the debug display (0-1) * @param widthScale X scale of the debug display (0-1) * @param heightScale Y scale of the debug display (0-1) */ setDebugDisplayParams(x, y, widthScale, heightScale) { this._debugSizeParams.set(x, y, widthScale, heightScale); } /** * The mip level to show in the debug display * @param mipNum The mip level to show in the debug display */ setDebugMipNumber(mipNum) { this._debugMipNumber = mipNum; } /** * Sets the name of the debug pass */ get debugPassName() { return this._debugPassName; } /** * Enable or disable the debug view for this pass */ get voxelDebugEnabled() { return this._voxelDebugEnabled; } set voxelDebugEnabled(enabled) { if (this._voxelDebugEnabled === enabled) { return; } this._voxelDebugEnabled = enabled; if (enabled) { this._voxelSlabDebugRT = new RenderTargetTexture("voxelSlabDebug", { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, this._scene, { generateDepthBuffer: true, generateMipMaps: false, type: Constants.TEXTURETYPE_UNSIGNED_BYTE, format: Constants.TEXTUREFORMAT_RGBA, samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, }); this._voxelSlabDebugRT.noPrePassRenderer = true; } if (this._voxelSlabDebugRT) { this._removeVoxelRTs([this._voxelSlabDebugRT]); } // Add the slab debug RT if needed. if (this._voxelDebugEnabled) { this._addRTsForRender([this._voxelSlabDebugRT], this._includedMeshes, this._voxelDebugAxis, 1, true); this._setDebugBindingsBound = this._setDebugBindings.bind(this); this._scene.onBeforeRenderObservable.add(this._setDebugBindingsBound); } else { this._scene.onBeforeRenderObservable.removeCallback(this._setDebugBindingsBound); } } /** * Creates the debug post process effect for this pass */ _createDebugPass() { const isWebGPU = this._engine.isWebGPU; if (!this._voxelDebugPass) { const debugOptions = { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight(), textureFormat: Constants.TEXTUREFORMAT_RGBA, textureType: Constants.TEXTURETYPE_UNSIGNED_BYTE, samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, uniforms: ["sizeParams", "mipNumber"], samplers: ["voxelTexture", "voxelSlabTexture"], engine: this._engine, reusable: false, shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializations: (useWebGPU, list) => { if (this._isVoxelGrid3D) { if (useWebGPU) { list.push(import('./iblVoxelGrid3dDebug.fragment-BVx-tQk5.esm.js')); } else { list.push(import('./iblVoxelGrid3dDebug.fragment-D1RADqwQ.esm.js')); } return; } if (useWebGPU) { list.push(import('./iblVoxelGrid2dArrayDebug.fragment-B7bP3JYg.esm.js')); } else { list.push(import('./iblVoxelGrid2dArrayDebug.fragment-DqJ6_ih8.esm.js')); } }, }; this._voxelDebugPass = new PostProcess(this.debugPassName, this._isVoxelGrid3D ? "iblVoxelGrid3dDebug" : "iblVoxelGrid2dArrayDebug", debugOptions); this._voxelDebugPass.onApplyObservable.add((effect) => { if (this._voxelDebugAxis === 0) { effect.setTexture("voxelTexture", this._voxelGridXaxis); } else if (this._voxelDebugAxis === 1) { effect.setTexture("voxelTexture", this._voxelGridYaxis); } else if (this._voxelDebugAxis === 2) { effect.setTexture("voxelTexture", this._voxelGridZaxis); } else { effect.setTexture("voxelTexture", this.getVoxelGrid()); } effect.setTexture("voxelSlabTexture", this._voxelSlabDebugRT); effect.setVector4("sizeParams", this._debugSizeParams); effect.setFloat("mipNumber", this._debugMipNumber); }); } } /** * Instanciates the voxel renderer * @param scene Scene to attach to * @param iblShadowsRenderPipeline The render pipeline this pass is associated with * @param resolutionExp Resolution of the voxel grid. The final resolution will be 2^resolutionExp. * @param triPlanarVoxelization Whether to use tri-planar voxelization. More expensive, but can help with artifacts. * @returns The voxel renderer */ constructor(scene, iblShadowsRenderPipeline, resolutionExp = 6, triPlanarVoxelization = true) { this._voxelMrtsXaxis = []; this._voxelMrtsYaxis = []; this._voxelMrtsZaxis = []; this._isVoxelGrid3D = true; /** * Observable that triggers when the voxelization is complete */ this.onVoxelizationCompleteObservable = new Observable(); this._renderTargets = []; this._triPlanarVoxelization = true; this._voxelizationInProgress = false; this._invWorldScaleMatrix = Matrix.Identity(); this._voxelResolution = 64; this._voxelResolutionExp = 6; this._mipArray = []; this._voxelDebugEnabled = false; this._voxelDebugAxis = -1; this._debugSizeParams = new Vector4(0.0, 0.0, 0.0, 0.0); this._includedMeshes = []; this._debugMipNumber = 0; this._debugPassName = "Voxelization Debug Pass"; this._scene = scene; this._engine = scene.getEngine(); this._triPlanarVoxelization = triPlanarVoxelization; if (!this._engine.getCaps().drawBuffersExtension) { Logger.Error("Can't do voxel rendering without the draw buffers extension."); } const isWebGPU = this._engine.isWebGPU; this._maxDrawBuffers = this._engine.getCaps().maxDrawBuffers || 0; this._copyMipEffectRenderer = new EffectRenderer(this._engine); this._copyMipEffectWrapper = new EffectWrapper({ engine: this._engine, fragmentShader: "copyTexture3DLayerToTexture", useShaderStore: true, uniformNames: ["layerNum"], samplerNames: ["textureSampler"], shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializationsAsync: async () => { if (isWebGPU) { await import('./copyTexture3DLayerToTexture.fragment-CXt1seoX.esm.js'); } else { await import('./copyTexture3DLayerToTexture.fragment-C7LNUal1.esm.js'); } }, }); this.voxelResolutionExp = resolutionExp; } _generateMipMaps() { const iterations = Math.ceil(Math.log2(this._voxelResolution)); for (let i = 1; i < iterations + 1; i++) { this._generateMipMap(i); } } _generateMipMap(lodLevel) { // Generate a mip map for the given level by triggering the render of the procedural mip texture. const mipTarget = this._mipArray[lodLevel - 1]; if (!mipTarget) { return; } mipTarget.setTexture("srcMip", lodLevel === 1 ? this.getVoxelGrid() : this._mipArray[lodLevel - 2]); mipTarget.render(); } _copyMipMaps() { const iterations = Math.ceil(Math.log2(this._voxelResolution)); for (let i = 1; i < iterations + 1; i++) { this._copyMipMap(i); } } _copyMipMap(lodLevel) { // Now, copy this mip into the mip chain of the voxel grid. // TODO - this currently isn't working. "textureSampler" isn't being properly set to mipTarget. const mipTarget = this._mipArray[lodLevel - 1]; if (!mipTarget) { return; } const voxelGrid = this.getVoxelGrid(); let rt; if (voxelGrid instanceof RenderTargetTexture && voxelGrid.renderTarget) { rt = voxelGrid.renderTarget; } else { rt = voxelGrid._rtWrapper; } if (rt) { this._copyMipEffectRenderer.saveStates(); const bindSize = mipTarget.getSize().width; // Render to each layer of the voxel grid. for (let layer = 0; layer < bindSize; layer++) { this._engine.bindFramebuffer(rt, 0, bindSize, bindSize, true, lodLevel, layer); this._copyMipEffectRenderer.applyEffectWrapper(this._copyMipEffectWrapper); this._copyMipEffectWrapper.effect.setTexture("textureSampler", mipTarget); this._copyMipEffectWrapper.effect.setInt("layerNum", layer); this._copyMipEffectRenderer.draw(); this._engine.unBindFramebuffer(rt, true); } this._copyMipEffectRenderer.restoreStates(); } } _computeNumberOfSlabs() { return Math.ceil(this._voxelResolution / this._maxDrawBuffers); } _createTextures() { const isWebGPU = this._engine.isWebGPU; const size = { width: this._voxelResolution, height: this._voxelResolution, layers: this._isVoxelGrid3D ? undefined : this._voxelResolution, depth: this._isVoxelGrid3D ? this._voxelResolution : undefined, }; const voxelAxisOptions = { generateDepthBuffer: false, generateMipMaps: false, type: Constants.TEXTURETYPE_UNSIGNED_BYTE, format: Constants.TEXTUREFORMAT_R, samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, }; // We can render up to maxDrawBuffers voxel slices of the grid per render. // We call this a slab. const numSlabs = this._computeNumberOfSlabs(); const voxelCombinedOptions = { generateDepthBuffer: false, generateMipMaps: true, type: Constants.TEXTURETYPE_UNSIGNED_BYTE, format: Constants.TEXTUREFORMAT_R, samplingMode: Constants.TEXTURE_NEAREST_NEAREST_MIPNEAREST, shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializationsAsync: async () => { if (isWebGPU) { await import('./iblCombineVoxelGrids.fragment-mTCcwBrp.esm.js'); } else { await import('./iblCombineVoxelGrids.fragment-DLzGXvyG.esm.js'); } }, }; if (this._triPlanarVoxelization) { this._voxelGridXaxis = new RenderTargetTexture("voxelGridXaxis", size, this._scene, voxelAxisOptions); this._voxelGridYaxis = new RenderTargetTexture("voxelGridYaxis", size, this._scene, voxelAxisOptions); this._voxelGridZaxis = new RenderTargetTexture("voxelGridZaxis", size, this._scene, voxelAxisOptions); this._voxelMrtsXaxis = this._createVoxelMRTs("x_axis_", this._voxelGridXaxis, numSlabs); this._voxelMrtsYaxis = this._createVoxelMRTs("y_axis_", this._voxelGridYaxis, numSlabs); this._voxelMrtsZaxis = this._createVoxelMRTs("z_axis_", this._voxelGridZaxis, numSlabs); this._voxelGridRT = new ProceduralTexture("combinedVoxelGrid", size, "iblCombineVoxelGrids", this._scene, voxelCombinedOptions, false); this._scene.proceduralTextures.splice(this._scene.proceduralTextures.indexOf(this._voxelGridRT), 1); this._voxelGridRT.setFloat("layer", 0.0); this._voxelGridRT.setTexture("voxelXaxisSampler", this._voxelGridXaxis); this._voxelGridRT.setTexture("voxelYaxisSampler", this._voxelGridYaxis); this._voxelGridRT.setTexture("voxelZaxisSampler", this._voxelGridZaxis); // We will render this only after voxelization is completed for the 3 axes. this._voxelGridRT.autoClear = false; this._voxelGridRT.wrapU = Texture.CLAMP_ADDRESSMODE; this._voxelGridRT.wrapV = Texture.CLAMP_ADDRESSMODE; } else { this._voxelGridZaxis = new RenderTargetTexture("voxelGridZaxis", size, this._scene, voxelCombinedOptions); this._voxelMrtsZaxis = this._createVoxelMRTs("z_axis_", this._voxelGridZaxis, numSlabs); } const generateVoxelMipOptions = { generateDepthBuffer: false, generateMipMaps: false, type: Constants.TEXTURETYPE_UNSIGNED_BYTE, format: Constants.TEXTUREFORMAT_R, samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializationsAsync: async () => { if (isWebGPU) { await import('./iblGenerateVoxelMip.fragment-BpQfiWGP.esm.js'); } else { await import('./iblGenerateVoxelMip.fragment-cq-mKZHq.esm.js'); } }, }; this._mipArray = new Array(Math.ceil(Math.log2(this._voxelResolution))); for (let mipIdx = 1; mipIdx <= this._mipArray.length; mipIdx++) { const mipDim = this._voxelResolution >> mipIdx; const mipSize = { width: mipDim, height: mipDim, depth: mipDim }; this._mipArray[mipIdx - 1] = new ProceduralTexture("voxelMip" + mipIdx, mipSize, "iblGenerateVoxelMip", this._scene, generateVoxelMipOptions, false); this._scene.proceduralTextures.splice(this._scene.proceduralTextures.indexOf(this._mipArray[mipIdx - 1]), 1); const mipTarget = this._mipArray[mipIdx - 1]; mipTarget.autoClear = false; mipTarget.wrapU = Texture.CLAMP_ADDRESSMODE; mipTarget.wrapV = Texture.CLAMP_ADDRESSMODE; mipTarget.setTexture("srcMip", mipIdx > 1 ? this._mipArray[mipIdx - 2] : this.getVoxelGrid()); mipTarget.setInt("layerNum", 0); } this._createVoxelMaterials(); } _createVoxelMRTs(name, voxelRT, numSlabs) { voxelRT.wrapU = Texture.CLAMP_ADDRESSMODE; voxelRT.wrapV = Texture.CLAMP_ADDRESSMODE; voxelRT.noPrePassRenderer = true; const mrtArray = []; const targetTypes = new Array(this._maxDrawBuffers).fill(this._isVoxelGrid3D ? Constants.TEXTURE_3D : Constants.TEXTURE_2D_ARRAY); for (let mrtIndex = 0; mrtIndex < numSlabs; mrtIndex++) { let layerIndices = new Array(this._maxDrawBuffers).fill(0); layerIndices = layerIndices.map((value, index) => mrtIndex * this._maxDrawBuffers + index); let textureNames = new Array(this._maxDrawBuffers).fill(""); textureNames = textureNames.map((value, index) => "voxel_grid_" + name + (mrtIndex * this._maxDrawBuffers + index)); const mrt = new MultiRenderTarget("mrt_" + name + mrtIndex, { width: this._voxelResolution, height: this._voxelResolution, depth: this._isVoxelGrid3D ? this._voxelResolution : undefined }, this._maxDrawBuffers, // number of draw buffers this._scene, { types: new Array(this._maxDrawBuffers).fill(Constants.TEXTURETYPE_UNSIGNED_BYTE), samplingModes: new Array(this._maxDrawBuffers).fill(Constants.TEXTURE_TRILINEAR_SAMPLINGMODE), generateMipMaps: false, targetTypes, formats: new Array(this._maxDrawBuffers).fill(Constants.TEXTUREFORMAT_R), faceIndex: new Array(this._maxDrawBuffers).fill(0), layerIndex: layerIndices, layerCounts: new Array(this._maxDrawBuffers).fill(this._voxelResolution), generateDepthBuffer: false, generateStencilBuffer: false, }, textureNames); mrt.clearColor = new Color4(0, 0, 0, 1); mrt.noPrePassRenderer = true; for (let i = 0; i < this._maxDrawBuffers; i++) { mrt.setInternalTexture(voxelRT.getInternalTexture(), i); } mrtArray.push(mrt); } return mrtArray; } _disposeVoxelTextures() { this._stopVoxelization(); for (let i = 0; i < this._voxelMrtsZaxis.length; i++) { if (this._triPlanarVoxelization) { this._voxelMrtsXaxis[i].dispose(true); this._voxelMrtsYaxis[i].dispose(true); } this._voxelMrtsZaxis[i].dispose(true); } if (this._triPlanarVoxelization) { this._voxelGridXaxis?.dispose(); this._voxelGridYaxis?.dispose(); this._voxelGridRT?.dispose(); } this._voxelGridZaxis?.dispose(); for (const mip of this._mipArray) { mip.dispose(); } this._voxelMaterial?.dispose(); this._voxelSlabDebugMaterial?.dispose(); this._mipArray = []; this._voxelMrtsXaxis = []; this._voxelMrtsYaxis = []; this._voxelMrtsZaxis = []; } _createVoxelMaterials() { const isWebGPU = this._engine.isWebGPU; this._voxelMaterial = new ShaderMaterial("voxelization", this._scene, "iblVoxelGrid", { uniforms: ["world", "viewMatrix", "invWorldScale", "nearPlane", "farPlane", "stepSize"], defines: ["MAX_DRAW_BUFFERS " + this._maxDrawBuffers], shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializationsAsync: async () => { if (isWebGPU) { await Promise.all([import('./iblVoxelGrid.fragment-B8PkAbQL.esm.js'), import('./iblVoxelGrid.vertex-BJktRmGp.esm.js')]); } else { await Promise.all([import('./iblVoxelGrid.fragment-6jQ-b5NZ.esm.js'), import('./iblVoxelGrid.vertex-DKwAHkCJ.esm.js')]); } }, }); this._voxelMaterial.cullBackFaces = false; this._voxelMaterial.backFaceCulling = false; this._voxelMaterial.depthFunction = Engine.ALWAYS; this._voxelSlabDebugMaterial = new ShaderMaterial("voxelSlabDebug", this._scene, "iblVoxelSlabDebug", { uniforms: ["world", "viewMatrix", "cameraViewMatrix", "projection", "invWorldScale", "nearPlane", "farPlane", "stepSize"], defines: ["MAX_DRAW_BUFFERS " + this._maxDrawBuffers], shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializationsAsync: async () => { if (isWebGPU) { await Promise.all([import('./iblVoxelSlabDebug.fragment-CaEe7_dA.esm.js'), import('./iblVoxelSlabDebug.vertex-CfVVUMNf.esm.js')]); } else { await Promise.all([import('./iblVoxelSlabDebug.fragment-DAe6_iT0.esm.js'), import('./iblVoxelSlabDebug.vertex-cmrQi3Vb.esm.js')]); } }, }); } _setDebugBindings() { this._voxelSlabDebugMaterial.setMatrix("projection", this._scene.activeCamera.getProjectionMatrix()); this._voxelSlabDebugMaterial.setMatrix("cameraViewMatrix", this._scene.activeCamera.getViewMatrix()); } /** * Checks if the voxel renderer is ready to voxelize scene * @returns true if the voxel renderer is ready to voxelize scene */ isReady() { let allReady = this.getVoxelGrid().isReady(); for (let i = 0; i < this._mipArray.length; i++) { const mipReady = this._mipArray[i].isReady(); allReady &&= mipReady; } if (!allReady || this._voxelizationInProgress) { return false; } return true; } /** * If the MRT's are already in the list of render targets, this will * remove them so that they don't get rendered again. */ _stopVoxelization() { // If the MRT's are already in the list of render targets, remove them. this._removeVoxelRTs(this._voxelMrtsXaxis); this._removeVoxelRTs(this._voxelMrtsYaxis); this._removeVoxelRTs(this._voxelMrtsZaxis); } _removeVoxelRTs(rts) { // const currentRTs = this._scene.customRenderTargets; const rtIdx = this._renderTargets.findIndex((rt) => { if (rt === rts[0]) { return true; } return false; }); if (rtIdx >= 0) { this._renderTargets.splice(rtIdx, rts.length); } else { const rtIdx = this._scene.customRenderTargets.findIndex((rt) => { if (rt === rts[0]) { return true; } return false; }); if (rtIdx >= 0) { this._scene.customRenderTargets.splice(rtIdx, rts.length); } } } /** * Renders voxel grid of scene for IBL shadows * @param includedMeshes */ updateVoxelGrid(includedMeshes) { this._stopVoxelization(); this._includedMeshes = includedMeshes; this._voxelizationInProgress = true; if (this._triPlanarVoxelization) { this._addRTsForRender(this._voxelMrtsXaxis, includedMeshes, 0); this._addRTsForRender(this._voxelMrtsYaxis, includedMeshes, 1); this._addRTsForRender(this._voxelMrtsZaxis, includedMeshes, 2); } else { this._addRTsForRender(this._voxelMrtsZaxis, includedMeshes, 2); } if (this._voxelDebugEnabled) { this._addRTsForRender([this._voxelSlabDebugRT], includedMeshes, this._voxelDebugAxis, 1, true); } this._renderVoxelGridBound = this._renderVoxelGrid.bind(this); this._scene.onAfterRenderObservable.add(this._renderVoxelGridBound); } _renderVoxelGrid() { if (this._voxelizationInProgress) { let allReady = this.getVoxelGrid().isReady(); for (let i = 0; i < this._mipArray.length; i++) { const mipReady = this._mipArray[i].isReady(); allReady &&= mipReady; } for (let i = 0; i < this._renderTargets.length; i++) { const rttReady = this._renderTargets[i].isReadyForRendering(); allReady &&= rttReady; } if (allReady) { for (const rt of this._renderTargets) { rt.render(); } this._stopVoxelization(); if (this._triPlanarVoxelization) { this._voxelGridRT.render(); } this._generateMipMaps(); // eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then this._copyMipEffectWrapper.effect.whenCompiledAsync().then(() => { this._copyMipMaps(); this._scene.onAfterRenderObservable.removeCallback(this._renderVoxelGridBound); this._voxelizationInProgress = false; this.onVoxelizationCompleteObservable.notifyObservers(); }); } } } _addRTsForRender(mrts, includedMeshes, axis, shaderType = 0, continuousRender = false) { const slabSize = 1.0 / this._computeNumberOfSlabs(); let voxelMaterial; if (shaderType === 0) { voxelMaterial = this._voxelMaterial; } else { voxelMaterial = this._voxelSlabDebugMaterial; } // We need to update the world scale uniform for every mesh being rendered to the voxel grid. for (let mrtIndex = 0; mrtIndex < mrts.length; mrtIndex++) { const mrt = mrts[mrtIndex]; mrt.renderList = []; const nearPlane = mrtIndex * slabSize; const farPlane = (mrtIndex + 1) * slabSize; const stepSize = slabSize / this._maxDrawBuffers; const cameraPosition = new Vector3(0, 0, 0); let targetPosition = new Vector3(0, 0, 1); if (axis === 0) { targetPosition = new Vector3(1, 0, 0); } else if (axis === 1) { targetPosition = new Vector3(0, 1, 0); } let upDirection = new Vector3(0, 1, 0); if (axis === 1) { upDirection = new Vector3(1, 0, 0); } mrt.onBeforeRenderObservable.add(() => { voxelMaterial.setMatrix("viewMatrix", Matrix.LookAtLH(cameraPosition, targetPosition, upDirection)); voxelMaterial.setMatrix("invWorldScale", this._invWorldScaleMatrix); voxelMaterial.setFloat("nearPlane", nearPlane); voxelMaterial.setFloat("farPlane", farPlane); voxelMaterial.setFloat("stepSize", stepSize); }); // Set this material on every mesh in the scene (for this RT) if (includedMeshes.length === 0) { return; } for (const mesh of includedMeshes) { if (mesh) { if (mesh.subMeshes && mesh.subMeshes.length > 0) { mrt.renderList?.push(mesh); mrt.setMaterialForRendering(mesh, voxelMaterial); } const meshes = mesh.getChildMeshes(); for (const childMesh of meshes) { if (childMesh.subMeshes && childMesh.subMeshes.length > 0) { mrt.renderList?.push(childMesh); mrt.setMaterialForRendering(childMesh, voxelMaterial); } } } } } // Add the MRT's to render. if (continuousRender) { for (const mrt of mrts) { if (this._scene.customRenderTargets.indexOf(mrt) === -1) { this._scene.customRenderTargets.push(mrt); } } } else { this._renderTargets = this._renderTargets.concat(mrts); } } /** * Called by the pipeline to resize resources. */ resize() { this._voxelSlabDebugRT?.resize({ width: this._scene.getEngine().getRenderWidth(), height: this._scene.getEngine().getRenderHeight() }); } /** * Disposes the voxel renderer and associated resources */ dispose() { this._disposeVoxelTextures(); if