import { Texture } from "../../Materials/Textures/texture.js"; import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture.js"; import "../../Engines/Extensions/engine.multiRender.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. */ export 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 : 15; 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; this._multiRenderTargetOptions = { samplingModes: samplingModes, generateMipMaps: generateMipMaps, generateDepthBuffer: generateDepthBuffer, generateStencilBuffer: generateStencilBuffer, generateDepthTexture: generateDepthTexture, depthTextureFormat: depthTextureFormat, types: types, textureCount: count, useSRGBBuffers: useSRGBBuffers, 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 : 0); } 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(5); } if (options && options.targetTypes && options.targetTypes[i] !== undefined) { targets.push(options.targetTypes[i]); } else { targets.push(3553); } 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 = 35866; } else if (texture.isCube) { target = 34067; } /*else if (texture.isCubeArray) { target = 3735928559; }*/ else if (texture.is3D) { target = 32879; } else { target = 3553; } 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, false); 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; } } //# sourceMappingURL=multiRenderTarget.js.map