| import {BaseEvent, EventDispatcher} from 'three' | |||||
| import {BaseEvent, EventDispatcher, WebGLRenderTarget} from 'three' | |||||
| import {IMaterial, IObject3D, ITexture} from '../core' | import {IMaterial, IObject3D, ITexture} from '../core' | ||||
| import {AnyOptions} from 'ts-browser-helpers' | import {AnyOptions} from 'ts-browser-helpers' | ||||
| import {BlobExt, ExportFileOptions, IAssetExporter, IExporter, IExportParser} from './IExporter' | import {BlobExt, ExportFileOptions, IAssetExporter, IExporter, IExportParser} from './IExporter' | ||||
| import {SimpleTextExporter} from './export/SimpleTextExporter' | |||||
| import {SimpleJSONExporter} from './export/SimpleJSONExporter' | |||||
| import {EXRExporter2, SimpleJSONExporter, SimpleTextExporter} from './export' | |||||
| import {IRenderTarget} from '../rendering' | |||||
| export class AssetExporter extends EventDispatcher<BaseEvent, 'exporterCreate' | 'exportFile'> implements IAssetExporter { | export class AssetExporter extends EventDispatcher<BaseEvent, 'exporterCreate' | 'exportFile'> implements IAssetExporter { | ||||
| readonly exporters: IExporter[] = [ | readonly exporters: IExporter[] = [ | ||||
| {ctor: ()=>new SimpleJSONExporter(), ext: ['json']}, | {ctor: ()=>new SimpleJSONExporter(), ext: ['json']}, | ||||
| {ctor: ()=>new SimpleTextExporter(), ext: ['txt', 'text']}, | {ctor: ()=>new SimpleTextExporter(), ext: ['txt', 'text']}, | ||||
| {ctor: ()=>new EXRExporter2(), ext: ['exr']}, | |||||
| // {ctor: ()=>new GLTFDracoExporter(), ext: ['gltf', 'glb']}, | // {ctor: ()=>new GLTFDracoExporter(), ext: ['gltf', 'glb']}, | ||||
| ] | ] | ||||
| super() | super() | ||||
| } | } | ||||
| public async exportObject(obj?: IObject3D|IMaterial|ITexture, options: ExportFileOptions = {}): Promise<BlobExt|undefined> { | |||||
| public async exportObject(obj?: IObject3D|IMaterial|ITexture|IRenderTarget, options: ExportFileOptions = {}): Promise<BlobExt|undefined> { | |||||
| if (!obj?.assetType) { | if (!obj?.assetType) { | ||||
| console.error('Object has no asset type') | console.error('Object has no asset type') | ||||
| return undefined | return undefined | ||||
| } | } | ||||
| // export to blob | // export to blob | ||||
| private async _exportFile(obj: IObject3D|IMaterial|ITexture, options: ExportFileOptions = {}): Promise<BlobExt|undefined> { | |||||
| private async _exportFile(obj: IObject3D|IMaterial|ITexture|IRenderTarget, options: ExportFileOptions = {}): Promise<BlobExt|undefined> { | |||||
| // if ((file as any)?.__imported) return (file as any).__imported // todo: cache exports? | // if ((file as any)?.__imported) return (file as any).__imported // todo: cache exports? | ||||
| let res: BlobExt | let res: BlobExt | ||||
| const processed = await this.processBeforeExport(obj, options) | const processed = await this.processBeforeExport(obj, options) | ||||
| const ext = options.exportExt ?? processed?.typeExt ?? processed?.ext | const ext = options.exportExt ?? processed?.typeExt ?? processed?.ext | ||||
| if (!processed || !ext) throw new Error(`Unable to preprocess before export ${ext}`) | if (!processed || !ext) throw new Error(`Unable to preprocess before export ${ext}`) | ||||
| const parser = this._getParser(ext) | |||||
| if (processed.blob) res = processed.blob | |||||
| else { | |||||
| const parser = this._getParser(ext) | |||||
| this.dispatchEvent({type: 'exportFile', obj, state:'exporting'}) | |||||
| const blob = await parser.parseAsync(processed.obj, {exportExt: processed.ext ?? ext, ...options}) as BlobExt | |||||
| blob.ext = processed.ext | |||||
| res = blob | |||||
| this.dispatchEvent({type: 'exportFile', obj, state:'exporting'}) | |||||
| res = await parser.parseAsync(processed.obj, {exportExt: processed.ext ?? ext, ...options}) as BlobExt | |||||
| res.ext = processed.ext | |||||
| } | |||||
| this.dispatchEvent({type: 'exportFile', obj, state: 'done'}) | this.dispatchEvent({type: 'exportFile', obj, state: 'done'}) | ||||
| return this._cachedParsers.find(e => e.ext.includes(ext))?.parser ?? this._createParser(ext) | return this._cachedParsers.find(e => e.ext.includes(ext))?.parser ?? this._createParser(ext) | ||||
| } | } | ||||
| public async processBeforeExport(obj: IObject3D|IMaterial|ITexture, _: AnyOptions = {}): Promise<{obj:any, ext:string, typeExt?:string}|undefined> { | |||||
| public async processBeforeExport(obj: IObject3D|IMaterial|ITexture|IRenderTarget, _: AnyOptions = {}): Promise<{obj:any, ext:string, typeExt?:string, blob?: BlobExt}|undefined> { | |||||
| // if (obj.assetExporterProcessed && !options.forceExporterReprocess) return obj //todo;;; | // if (obj.assetExporterProcessed && !options.forceExporterReprocess) return obj //todo;;; | ||||
| switch (obj.assetType) { | switch (obj.assetType) { | ||||
| return {obj: (obj as IMaterial).toJSON(), ext: (obj as IMaterial).constructor?.TypeSlug || 'json', typeExt: 'json'} | return {obj: (obj as IMaterial).toJSON(), ext: (obj as IMaterial).constructor?.TypeSlug || 'json', typeExt: 'json'} | ||||
| case 'texture': | case 'texture': | ||||
| return {obj: (obj as ITexture).toJSON(), ext: 'json'} | return {obj: (obj as ITexture).toJSON(), ext: 'json'} | ||||
| case 'renderTarget': | |||||
| if (obj.isWebGLMultipleRenderTargets) console.error('AssetExporter: WebGLMultipleRenderTargets export not supported') | |||||
| else if (!obj.renderManager) return {obj, ext: 'exr'} | |||||
| else { | |||||
| const blob = obj.renderManager.exportRenderTarget(obj as WebGLRenderTarget, 'auto') | |||||
| return { | |||||
| obj, ext: blob.ext, blob, | |||||
| } | |||||
| } | |||||
| break | |||||
| default: | default: | ||||
| console.error('AssetExporter: unknown asset type', obj.assetType) | console.error('AssetExporter: unknown asset type', obj.assetType) | ||||
| } | } |
| import { | import { | ||||
| Blending, | Blending, | ||||
| Color, | Color, | ||||
| FloatType, | |||||
| HalfFloatType, | HalfFloatType, | ||||
| IUniform, | IUniform, | ||||
| NoBlending, | NoBlending, | ||||
| Vector4, | Vector4, | ||||
| WebGLRenderer, | WebGLRenderer, | ||||
| WebGLRenderTarget, | WebGLRenderTarget, | ||||
| WebGLRenderTargetOptions, | |||||
| } from 'three' | } from 'three' | ||||
| import {EffectComposer2, IPassID, IPipelinePass, sortPasses} from '../postprocessing' | import {EffectComposer2, IPassID, IPipelinePass, sortPasses} from '../postprocessing' | ||||
| import {IRenderTarget} from './RenderTarget' | import {IRenderTarget} from './RenderTarget' | ||||
| IWebGLRenderer, | IWebGLRenderer, | ||||
| upgradeWebGLRenderer, | upgradeWebGLRenderer, | ||||
| } from '../core' | } from '../core' | ||||
| import {onChange2, serializable, serialize} from 'ts-browser-helpers' | |||||
| import {base64ToArrayBuffer, Class, onChange2, serializable, serialize, ValOrArr} from 'ts-browser-helpers' | |||||
| import {uiConfig, uiFolderContainer, uiMonitor, uiSlider, uiToggle} from 'uiconfig.js' | import {uiConfig, uiFolderContainer, uiMonitor, uiSlider, uiToggle} from 'uiconfig.js' | ||||
| import {generateUUID} from '../three' | |||||
| import {textureDataToImageData} from '../three/utils/texture' | |||||
| import {EXRExporter2} from '../assetmanager/export/EXRExporter2' | |||||
| import {BlobExt} from '../assetmanager' | |||||
| @serializable('RenderManager') | @serializable('RenderManager') | ||||
| @uiFolderContainer('Render Manager') | @uiFolderContainer('Render Manager') | ||||
| // if (material.uniforms.currentFrameCount) material.uniforms.currentFrameCount.value = this.frameCount | // if (material.uniforms.currentFrameCount) material.uniforms.currentFrameCount.value = this.frameCount | ||||
| if (!this.stableNoise) { | if (!this.stableNoise) { | ||||
| if (material.uniforms.frameCount) material.uniforms.frameCount.value = this._totalFrameCount | if (material.uniforms.frameCount) material.uniforms.frameCount.value = this._totalFrameCount | ||||
| else console.warn('BaseRenderer: no uniform: frameCount') | |||||
| else console.warn('RenderManager: no uniform: frameCount') | |||||
| } else { | } else { | ||||
| if (material.uniforms.frameCount) material.uniforms.frameCount.value = this.frameCount | if (material.uniforms.frameCount) material.uniforms.frameCount.value = this.frameCount | ||||
| else console.warn('BaseRenderer: no uniform: frameCount') | |||||
| else console.warn('RenderManager: no uniform: frameCount') | |||||
| } | } | ||||
| return this | return this | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Only to be used for testing. To do it properly, render the target to the main canvas(with proper encoding and type conversion) and call canvas.toDataURL() | |||||
| * @param target | |||||
| * @param mimeType | |||||
| * @param quality | |||||
| */ | |||||
| renderTargetToDataUrl(target: WebGLRenderTarget, mimeType = 'image/png', quality = 90): string { | |||||
| const canvas = document.createElement('canvas') | |||||
| canvas.width = target.width | |||||
| canvas.height = target.height | |||||
| const ctx = canvas.getContext('2d') | |||||
| if (!ctx) throw new Error('Unable to get 2d context') | |||||
| const imageData = ctx.createImageData(target.width, target.height, {colorSpace: ['display-p3', 'srgb'].includes(target.texture.colorSpace) ? <PredefinedColorSpace>target.texture.colorSpace : undefined}) | |||||
| if (target.texture.type === HalfFloatType) { | |||||
| const buffer = new Uint16Array(target.width * target.height * 4) | |||||
| this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, buffer) | |||||
| for (let i = 0; i < buffer.length; i++) { | |||||
| imageData.data[i] = buffer[i] / 15360 * 255 // todo check packing | |||||
| } | |||||
| } else { | |||||
| // todo: handle rgbm to srgb conversion? | |||||
| this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, imageData.data) | |||||
| } | |||||
| ctx.putImageData(imageData, 0, 0) | |||||
| const string = canvas.toDataURL(mimeType, quality) | |||||
| canvas.remove() | |||||
| return string | |||||
| } | |||||
| renderTargetToBuffer(target: WebGLRenderTarget): Uint8Array|Uint16Array { | |||||
| const buffer = target.texture.type === HalfFloatType ? | |||||
| new Uint16Array(target.width * target.height * 4) : | |||||
| new Uint8Array(target.width * target.height * 4) | |||||
| this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, buffer) | |||||
| return buffer | |||||
| } | |||||
| // endregion | // endregion | ||||
| // region Getters and Setters | // region Getters and Setters | ||||
| set pipeline(value: IPassID[]) { | set pipeline(value: IPassID[]) { | ||||
| this._pipeline = value | this._pipeline = value | ||||
| if (this.autoBuildPipeline) { | if (this.autoBuildPipeline) { | ||||
| console.warn('BaseRenderer: pipeline changed, but autoBuildPipeline is true. This will not have any effect.') | |||||
| console.warn('RenderManager: pipeline changed, but autoBuildPipeline is true. This will not have any effect.') | |||||
| } | } | ||||
| this.rebuildPipeline() | this.rebuildPipeline() | ||||
| } | } | ||||
| // endregion | // endregion | ||||
| // region Events Dispatch | |||||
| private _updated(data?: Partial<IRenderManagerUpdateEvent>) { | |||||
| this.dispatchEvent({...data, type: 'update'}) | |||||
| } | |||||
| // endregion | |||||
| // / TODO | |||||
| // region Utils | |||||
| /** | /** | ||||
| * | * | ||||
| } | } | ||||
| /** | |||||
| * Converts a render target to a png/jpeg data url string. | |||||
| * @param target | |||||
| * @param mimeType | |||||
| * @param quality | |||||
| */ | |||||
| renderTargetToDataUrl(target: WebGLRenderTarget, mimeType = 'image/png', quality = 90): string { | |||||
| const canvas = document.createElement('canvas') | |||||
| canvas.width = target.width | |||||
| canvas.height = target.height | |||||
| const ctx = canvas.getContext('2d') | |||||
| if (!ctx) throw new Error('Unable to get 2d context') | |||||
| const imageData = ctx.createImageData(target.width, target.height, {colorSpace: ['display-p3', 'srgb'].includes(target.texture.colorSpace) ? <PredefinedColorSpace>target.texture.colorSpace : undefined}) | |||||
| if (target.texture.type === HalfFloatType || target.texture.type === FloatType) { | |||||
| const buffer = this.renderTargetToBuffer(target) | |||||
| textureDataToImageData({data: buffer, width: target.width, height: target.height}, target.texture.colorSpace, imageData) // this handles converting to srgb | |||||
| } else { | |||||
| // todo: handle rgbm to srgb conversion? | |||||
| this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, imageData.data) | |||||
| } | |||||
| ctx.putImageData(imageData, 0, 0) | |||||
| const string = canvas.toDataURL(mimeType, quality) | |||||
| canvas.remove() | |||||
| return string | |||||
| } | |||||
| renderTargetToBuffer(target: WebGLRenderTarget): Uint8Array|Uint16Array|Float32Array { | |||||
| const buffer = | |||||
| target.texture.type === HalfFloatType ? | |||||
| new Uint16Array(target.width * target.height * 4) : | |||||
| target.texture.type === FloatType ? | |||||
| new Float32Array(target.width * target.height * 4) : | |||||
| new Uint8Array(target.width * target.height * 4) | |||||
| this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, buffer) | |||||
| return buffer | |||||
| } | |||||
| exportRenderTarget(target: WebGLRenderTarget, mimeType = 'auto'): BlobExt { | |||||
| const hdrFormats = ['image/x-exr'] | |||||
| let hdr = target.texture.type === HalfFloatType || target.texture.type === FloatType | |||||
| if (mimeType === 'auto') { | |||||
| mimeType = hdr ? 'image/x-exr' : 'image/png' | |||||
| } | |||||
| if (!hdrFormats.includes(mimeType)) hdr = false | |||||
| let buffer: ArrayBufferLike | |||||
| if (!hdr) { | |||||
| const url = this.renderTargetToDataUrl(target, mimeType === 'auto' ? undefined : mimeType) | |||||
| buffer = base64ToArrayBuffer(url.split(',')[1]) | |||||
| mimeType = url.split(';')[0].split(':')[1] | |||||
| } else { | |||||
| if (mimeType !== 'image/x-exr') { | |||||
| console.warn('RenderManager: mimeType ', mimeType, ' is not supported for HDR. Using EXR instead') | |||||
| mimeType = 'image/x-exr' | |||||
| } | |||||
| const exporter = new EXRExporter2() | |||||
| buffer = exporter.parse(this._renderer, target) | |||||
| } | |||||
| const b = new Blob([buffer], {type: mimeType}) as BlobExt | |||||
| b.ext = mimeType === 'image/x-exr' ? 'exr' : mimeType.split('/')[1] | |||||
| return b | |||||
| } | |||||
| // endregion | |||||
| // region Events Dispatch | |||||
| private _updated(data?: Partial<IRenderManagerUpdateEvent>) { | |||||
| this.dispatchEvent({...data, type: 'update'}) | |||||
| } | |||||
| // endregion | |||||
| protected _createTargetClass(clazz: Class<WebGLRenderTarget>, size: number[], options: WebGLRenderTargetOptions): IRenderTarget { | |||||
| const processNewTarget = this._processNewTarget | |||||
| return new class RenderTarget extends clazz implements IRenderTarget { | |||||
| isTemporary?: boolean | |||||
| sizeMultiplier?: number | |||||
| uuid: string | |||||
| readonly assetType = 'renderTarget' | |||||
| name = 'RenderTarget' | |||||
| // @ts-expect-error because WebGLRenderTarget does not have texture as array | |||||
| texture: ValOrArr<Texture&{_target: IRenderTarget}> | |||||
| constructor(public readonly renderManager: IRenderManager, ...ps: any[]) { | |||||
| super(...ps) | |||||
| this.uuid = generateUUID() | |||||
| const ops = ps[ps.length - 1] as WebGLRenderTargetOptions | |||||
| if (Array.isArray(this.texture)) { | |||||
| this.texture.forEach(t => { | |||||
| if (ops.colorSpace !== undefined) t.colorSpace = ops.colorSpace | |||||
| t._target = this | |||||
| t.toJSON = () => { | |||||
| console.warn('Multiple render target texture.toJSON not supported yet.') | |||||
| return {} | |||||
| } | |||||
| }) | |||||
| } else { | |||||
| this.texture._target = this | |||||
| this.texture.toJSON = () => ({ // todo use readRenderTargetPixels as data url or data buffer. | |||||
| isRenderTargetTexture: true, | |||||
| }) // so that it doesn't get serialized | |||||
| } | |||||
| } | |||||
| setSize(w: number, h: number, depth?: number) { | |||||
| super.setSize(Math.floor(w), Math.floor(h), depth) | |||||
| // console.log('setSize', w, h, depth) | |||||
| return this | |||||
| } | |||||
| clone(trackTarget = true): any { | |||||
| if (this.isTemporary) throw 'Cloning temporary render targets not supported' | |||||
| if (Array.isArray(this.texture)) throw 'Cloning multiple render targets not supported' | |||||
| // Note: todo: webgl render target.clone messes up the texture, by not copying isRenderTargetTexture prop and maybe some other stuff. So its better to just create a new one | |||||
| const cloned = super.clone() as IRenderTarget | |||||
| const tex = cloned.texture | |||||
| if (Array.isArray(tex)) tex.forEach(t => t.isRenderTargetTexture = true) | |||||
| else tex.isRenderTargetTexture = true | |||||
| return processNewTarget(cloned, this.sizeMultiplier || 1, trackTarget) | |||||
| } | |||||
| }(this, ...size, options) | |||||
| } | |||||
| /** | /** | ||||
| * @deprecated use renderScale instead | * @deprecated use renderScale instead | ||||
| */ | */ |
| } from 'three' | } from 'three' | ||||
| import {Vector4} from 'three/src/math/Vector4' | import {Vector4} from 'three/src/math/Vector4' | ||||
| import {DepthTexture} from 'three/src/textures/DepthTexture' | import {DepthTexture} from 'three/src/textures/DepthTexture' | ||||
| import type {IRenderManager} from '../core' | |||||
| import {ValOrArr} from 'ts-browser-helpers' | |||||
| export interface IRenderTarget extends EventDispatcher { | export interface IRenderTarget extends EventDispatcher { | ||||
| isWebGLRenderTarget: boolean | isWebGLRenderTarget: boolean | ||||
| width: number | width: number | ||||
| height: number | height: number | ||||
| depth: number | depth: number | ||||
| assetType?: 'renderTarget' | |||||
| name?: string | |||||
| texture: Texture | Texture[] | |||||
| texture: ValOrArr<Texture&{_target?: IRenderTarget}> | |||||
| uuid?: string | uuid?: string | ||||
| sizeMultiplier?: number | sizeMultiplier?: number | ||||
| isTemporary?: boolean | isTemporary?: boolean | ||||
| isWebGLCubeRenderTarget?: boolean | isWebGLCubeRenderTarget?: boolean | ||||
| isWebGLMultipleRenderTargets?: boolean | isWebGLMultipleRenderTargets?: boolean | ||||
| readonly renderManager?: IRenderManager | |||||
| } | } | ||||
| export interface CreateRenderTargetOptions { | export interface CreateRenderTargetOptions { |
| WebGLRenderTarget, | WebGLRenderTarget, | ||||
| WebGLRenderTargetOptions, | WebGLRenderTargetOptions, | ||||
| } from 'three' | } from 'three' | ||||
| import {generateUUID} from '../three' | |||||
| export abstract class RenderTargetManager<E extends BaseEvent = BaseEvent, ET extends string = string> extends EventDispatcher<E, ET> { | export abstract class RenderTargetManager<E extends BaseEvent = BaseEvent, ET extends string = string> extends EventDispatcher<E, ET> { | ||||
| abstract isWebGL2: boolean | abstract isWebGL2: boolean | ||||
| height, | height, | ||||
| count, | count, | ||||
| }: {width: number, height: number, count?: number}, options: WebGLRenderTargetOptions = {}, clazz?: Class<T>): T { | }: {width: number, height: number, count?: number}, options: WebGLRenderTargetOptions = {}, clazz?: Class<T>): T { | ||||
| const processNewTarget = this._processNewTarget | |||||
| let size = [width, height] | let size = [width, height] | ||||
| if (count && count > 1) size.push(count) | if (count && count > 1) size.push(count) | ||||
| if (width !== height) throw 'Width and height of cube render target must be equal' | if (width !== height) throw 'Width and height of cube render target must be equal' | ||||
| size = [width] | size = [width] | ||||
| } | } | ||||
| const params = [...size, { | |||||
| return this._createTargetClass((clazz as any) ?? WebGLRenderTarget, size, { | |||||
| format: RGBAFormat, | format: RGBAFormat, | ||||
| minFilter: LinearFilter, | minFilter: LinearFilter, | ||||
| magFilter: LinearFilter, | magFilter: LinearFilter, | ||||
| type: UnsignedByteType, | type: UnsignedByteType, | ||||
| colorSpace: NoColorSpace, | colorSpace: NoColorSpace, | ||||
| ...options, | ...options, | ||||
| }] | |||||
| return new class RenderTarget extends ((clazz as any as Class<WebGLRenderTarget>) ?? WebGLRenderTarget) implements IRenderTarget { | |||||
| isTemporary?: boolean | |||||
| sizeMultiplier?: number | |||||
| uuid: string | |||||
| constructor(...ps: any[]) { | |||||
| super(...ps) | |||||
| this.uuid = generateUUID() | |||||
| const ops = ps[ps.length - 1] as WebGLRenderTargetOptions | |||||
| if (Array.isArray(this.texture)) { | |||||
| this.texture.forEach(t => { | |||||
| t.colorSpace = ops.colorSpace | |||||
| t.toJSON = () => { | |||||
| console.warn('Multiple render target texture.toJSON not supported yet.') | |||||
| return {} | |||||
| } | |||||
| }) | |||||
| } else { | |||||
| this.texture.toJSON = () => ({ // todo use readRenderTargetPixels as data url or data buffer. | |||||
| isRenderTargetTexture: true, | |||||
| }) // so that it doesn't get serialized | |||||
| } | |||||
| } | |||||
| setSize(w: number, h: number, depth?: number) { | |||||
| super.setSize(Math.floor(w), Math.floor(h), depth) | |||||
| // console.log('setSize', w, h, depth) | |||||
| return this | |||||
| } | |||||
| clone(trackTarget = true): any { | |||||
| if (this.isTemporary) throw 'Cloning temporary render targets not supported' | |||||
| if (Array.isArray(this.texture)) throw 'Cloning multiple render targets not supported' | |||||
| // Note: todo: webgl render target.clone messes up the texture, by not copying isRenderTargetTexture prop and maybe some other stuff. So its better to just create a new one | |||||
| const cloned = super.clone() as IRenderTarget | |||||
| const tex = cloned.texture | |||||
| if (Array.isArray(tex)) tex.forEach(t => t.isRenderTargetTexture = true) | |||||
| else tex.isRenderTargetTexture = true | |||||
| return processNewTarget(cloned, this.sizeMultiplier || 1, trackTarget) | |||||
| } | |||||
| }(...params) as any as T | |||||
| }) as T | |||||
| } | } | ||||
| protected abstract _createTargetClass(clazz: Class<WebGLRenderTarget>, size: number[], options: WebGLRenderTargetOptions): IRenderTarget | |||||
| dispose() { | dispose() { | ||||
| this._trackedTargets.forEach(t=>t.dispose()) | this._trackedTargets.forEach(t=>t.dispose()) | ||||
| Object.values(this._trackedTempTargets).forEach(t=>t.dispose()) | Object.values(this._trackedTempTargets).forEach(t=>t.dispose()) | ||||
| texture.minFilter = LinearFilter | texture.minFilter = LinearFilter | ||||
| } | } | ||||
| private _processNewTarget(target: IRenderTarget, sizeMultiplier: number | undefined, trackTarget: boolean): IRenderTarget { | |||||
| protected _processNewTarget(target: IRenderTarget, sizeMultiplier: number | undefined, trackTarget: boolean): IRenderTarget { | |||||
| if (sizeMultiplier !== undefined) target.sizeMultiplier = sizeMultiplier | if (sizeMultiplier !== undefined) target.sizeMultiplier = sizeMultiplier | ||||
| if (trackTarget) this.trackTarget(target) | if (trackTarget) this.trackTarget(target) | ||||
| return target | return target |