| @@ -62,7 +62,7 @@ export class DepthBufferPlugin | |||
| // @onChange2(DepthBufferPlugin.prototype._createTarget) | |||
| // @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig) | |||
| readonly bufferType: TextureDataType // cannot be changed after creation (for now) | |||
| readonly bufferType: TextureDataType // cannot be changed after creation (for now) todo line 139: unregisterMaterialExtensions, maybe because the priority is not set so its added at the end? | |||
| // @uiToggle() | |||
| // @onChange2(DepthBufferPlugin.prototype._createTarget) | |||
| @@ -80,9 +80,11 @@ export class DepthBufferPlugin | |||
| unpackExtension: MaterialExtension = { | |||
| shaderExtender: (shader)=>{ | |||
| const includes = ['depth_buffer_unpack', 'gbuffer_unpack', 'packing'] as const | |||
| const include = includes.find(i=>shader.fragmentShader.includes(`#include <${i}>`)) | |||
| shader.fragmentShader = shaderReplaceString(shader.fragmentShader, | |||
| '#include <packing>', | |||
| '\n' + DepthBufferUnpack + '\n', {append: true}) | |||
| `#include <${include}>`, | |||
| '\n' + DepthBufferUnpack + '\n', {append: include === 'packing'}) | |||
| }, | |||
| extraUniforms: { | |||
| tDepthBuffer: ()=>({value: this.target?.texture}), | |||
| @@ -120,6 +122,7 @@ export class DepthBufferPlugin | |||
| if (this.isPrimaryGBuffer) { | |||
| this._viewer.renderManager.gbufferTarget = this.target | |||
| this._viewer.renderManager.gbufferUnpackExtension = this.unpackExtension | |||
| this._viewer.renderManager.screenPass.material.registerMaterialExtensions([this.unpackExtension]) | |||
| this._isPrimaryGBufferSet = true | |||
| } | |||
| @@ -134,7 +137,8 @@ export class DepthBufferPlugin | |||
| this.texture = undefined | |||
| if (this._isPrimaryGBufferSet) { // using a separate flag as when isPrimaryGBuffer is changed, we cannot check it. | |||
| this._viewer.renderManager.gbufferTarget = undefined | |||
| // this._viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this.unpackExtension]) // todo | |||
| this._viewer.renderManager.gbufferUnpackExtension = undefined | |||
| // this._viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this.unpackExtension]) // todo this has an issue | |||
| this._isPrimaryGBufferSet = false | |||
| } | |||
| } | |||
| @@ -57,7 +57,8 @@ export class FrameFadePlugin | |||
| }) | |||
| this._pass.fadeTimeState = Math.max(duration, this._pass.fadeTimeState) | |||
| this._pass.fadeTime = this._pass.fadeTimeState | |||
| this._pass.toSaveFrame = true | |||
| if (this._pass.fadeTimeState < 500) // only save if very near the end | |||
| this._pass.toSaveFrame = true | |||
| // this._pass.passObject.enabled = true | |||
| this.setDirty() | |||
| await timeout(duration) | |||
| @@ -119,9 +119,11 @@ export class GBufferPlugin | |||
| unpackExtension: MaterialExtension = { | |||
| shaderExtender: (shader)=>{ | |||
| const includes = ['gbuffer_unpack', 'packing'] as const | |||
| const include = includes.find(i=>shader.fragmentShader.includes(`#include <${i}>`)) | |||
| shader.fragmentShader = shaderReplaceString(shader.fragmentShader, | |||
| '#include <packing>', | |||
| '\n' + GBufferUnpack + '\n', {append: true}) | |||
| `#include <${include}>`, | |||
| '\n' + GBufferUnpack + '\n', {append: include === 'packing'}) | |||
| }, | |||
| extraUniforms: { | |||
| tNormalDepth: ()=>({value: this.normalDepthTexture}), | |||
| @@ -183,6 +185,7 @@ export class GBufferPlugin | |||
| if (this.isPrimaryGBuffer) { | |||
| this._viewer.renderManager.gbufferTarget = this.target | |||
| this._viewer.renderManager.gbufferUnpackExtension = this.unpackExtension | |||
| this._viewer.renderManager.screenPass.material.registerMaterialExtensions([this.unpackExtension]) | |||
| this._isPrimaryGBufferSet = true | |||
| } | |||
| @@ -197,6 +200,7 @@ export class GBufferPlugin | |||
| this.textures = [] | |||
| if (this._isPrimaryGBufferSet) { // using a separate flag as when isPrimaryGBuffer is changed, we cannot check it. | |||
| this._viewer.renderManager.gbufferTarget = undefined | |||
| this._viewer.renderManager.gbufferUnpackExtension = undefined | |||
| // this._viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this.unpackExtension]) // todo | |||
| this._isPrimaryGBufferSet = false | |||
| } | |||
| @@ -25,6 +25,11 @@ export class ProgressivePlugin | |||
| readonly passId = 'progressive' | |||
| public static readonly PluginType = 'ProgressivePlugin' | |||
| /** | |||
| * Different targets for different render cameras. | |||
| * Need to save them all here since we need them in the next frame. | |||
| * @protected | |||
| */ | |||
| protected _targets = new Map<string, ProgressivePluginTarget>() | |||
| @serialize() @uiInput('Frame count') maxFrameCount: number | |||
| @@ -15,12 +15,13 @@ import { | |||
| import {glsl, onChange, serialize} from 'ts-browser-helpers' | |||
| import {IMaterial} from '../../core' | |||
| import {updateBit} from '../../utils' | |||
| import {matDefine, uniform} from '../../three' | |||
| import {uniform} from '../../three' | |||
| import Uncharted2ToneMappingShader from './shaders/Uncharted2ToneMapping.glsl' | |||
| import TonemapShader from './shaders/TonemapPlugin.pars.glsl' | |||
| import TonemapShaderPatch from './shaders/TonemapPlugin.patch.glsl' | |||
| import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin' | |||
| import {GBufferUpdaterContext} from '../pipeline/GBufferPlugin' | |||
| import {matDefineBool} from '../../three/utils/decorators' | |||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||
| export const Uncharted2Tonemapping: ToneMapping = CustomToneMapping | |||
| @@ -63,7 +64,7 @@ export class TonemapPlugin extends AScreenPassExtensionPlugin<''> { | |||
| @serialize() toneMapping: ToneMapping = ACESFilmicToneMapping | |||
| @uiToggle('Tonemap Background', (t: TonemapPlugin)=>({hidden: ()=>!t._viewer?.renderManager.gbufferTarget})) | |||
| @matDefine('TONEMAP_BACKGROUND', undefined, true, TonemapPlugin.prototype.setDirty, (v)=>v ? '1' : '0', (v) => v !== '0') | |||
| @matDefineBool('TONEMAP_BACKGROUND', undefined, true, TonemapPlugin.prototype.setDirty) | |||
| @serialize() tonemapBackground = true | |||
| // todo handle legacy deserialize | |||
| @@ -132,7 +133,7 @@ export class TonemapPlugin extends AScreenPassExtensionPlugin<''> { | |||
| return super.fromJSON(data, meta) | |||
| } | |||
| // TODO: add gBufferData or just tonemapEnabled to the scene material UI with an extension | |||
| // TODO: add gBufferData or just tonemapEnabled to the scene material UI with an extension like bloom | |||
| updateGBufferFlags(data: Vector4, c: GBufferUpdaterContext): void { | |||
| const x = (c.material.userData.gBufferData?.tonemapEnabled ?? c.material?.userData.postTonemap) === false ? 0 : 1 | |||
| data.w = updateBit(data.w, 1, x) // 2nd Bit | |||
| @@ -1,4 +1,5 @@ | |||
| import { | |||
| BaseEvent, | |||
| Color, | |||
| FloatType, | |||
| HalfFloatType, | |||
| @@ -47,7 +48,7 @@ import {RendererBlitOptions} from '../core/IRenderer' | |||
| @serializable('RenderManager') | |||
| @uiFolderContainer('Render Manager') | |||
| export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRenderManagerEventTypes> implements IShaderPropertiesUpdater, IRenderManager { | |||
| export class RenderManager<TEvent extends BaseEvent = IRenderManagerEvent, TEventTypes extends string = IRenderManagerEventTypes> extends RenderTargetManager<IRenderManagerEvent|TEvent, IRenderManagerEventTypes|TEventTypes> implements IShaderPropertiesUpdater, IRenderManager { | |||
| private readonly _isWebGL2: boolean | |||
| private readonly _composer: EffectComposer2 | |||
| private readonly _context: WebGLRenderingContext | |||
| @@ -51,10 +51,14 @@ function callOnChange(this: any, onChange: (...args: any[]) => any, params: any[ | |||
| } | |||
| /** | |||
| * | |||
| * @param customDefines - object for setting define value (like ShaderMaterial.defines), otherwise this.material.defines is taken | |||
| * Decorator to create a three.js style define in this or this.material and bind to a property. | |||
| * see also - {@link matDefineBool} | |||
| * @param key - define name | |||
| * @param customDefines - object for setting define value (like ShaderMaterial.defines), otherwise this.material.defines is taken | |||
| * @param thisMat - access this.defines instead of this.material.defines | |||
| * @param onChange - function to call when the value changes. The function is called with the following parameters: [key, newVal]. Note: needsUpdate is set to true for this/material if onChange is not provided. | |||
| * @param processVal - function that processes the value before setting it. | |||
| * @param invProcessVal - function that processes the value before returning it. | |||
| */ | |||
| export function matDefine(key?: string|symbol, customDefines?: any, thisMat = false, onChange?: (...args: any[]) => any, processVal?: (newVal: any)=>any, invProcessVal?: (val:any)=>any): PropertyDecorator { | |||
| // backing up properties as values are different when called again, no idea why. | |||
| @@ -77,6 +81,10 @@ export function matDefine(key?: string|symbol, customDefines?: any, thisMat = fa | |||
| set(newVal: any) { | |||
| const {t, p} = getTarget(thisMat ? this : this.material) | |||
| if (processVal) newVal = processVal(newVal) | |||
| else if (typeof newVal === 'boolean') { // just in case | |||
| console.error('Boolean values are not supported for defines. Use @matDefineBool instead.') | |||
| newVal = newVal ? '1' : '0' | |||
| } | |||
| safeSetProperty(t, p, newVal, true) | |||
| if (newVal === undefined) delete t[p] | |||
| if (onChange && typeof onChange === 'function') { | |||
| @@ -91,6 +99,18 @@ export function matDefine(key?: string|symbol, customDefines?: any, thisMat = fa | |||
| } | |||
| } | |||
| /** | |||
| * Same as {@link matDefine} but for boolean values. It sets the value to '1' or '0'/undefined. | |||
| * @param key - define name | |||
| * @param customDefines - object for setting define value (like ShaderMaterial.defines), otherwise this.material.defines is taken | |||
| * @param thisMat - access this.defines instead of this.material.defines | |||
| * @param onChange - function to call when the value changes. If a string, it is used as a property name in `this` and called. If a function, it is called. The function is called with the following parameters: key, newVal | |||
| * @param deleteOnFalse - sets to undefined instead of '0' when false | |||
| */ | |||
| export function matDefineBool(key?: string|symbol, customDefines?: any, thisMat = false, onChange?: (...args: any[]) => any, deleteOnFalse = false): PropertyDecorator { | |||
| return matDefine(key, customDefines, thisMat, onChange, (v: any)=>v ? '1' : deleteOnFalse ? undefined : '0', (v: any)=>v && v !== '0') | |||
| } | |||
| /** | |||
| * Binds a property to a value in an object. If the object is a string, it is used as a property name in `this`. | |||
| * @param obj - object to bind to. If a string, it is used as a property name in `this`. If a function, it is called and the result is used as the object/string. | |||
| @@ -1,8 +1,10 @@ | |||
| import {IRenderTarget, RenderManager} from '../rendering' | |||
| import {HalfFloatType, LinearMipMapLinearFilter, NoColorSpace, RGBM16ColorSpace, UnsignedByteType} from 'three' | |||
| import {IRenderManagerOptions} from '../core' | |||
| import {IRenderManagerEvent, IRenderManagerOptions} from '../core' | |||
| import {ExtendedRenderPass, ScreenPass, TViewerScreenShader} from '../postprocessing' | |||
| import {uiFolderContainer} from 'uiconfig.js' | |||
| import {MaterialExtension} from '../materials' | |||
| import {onChange3} from 'ts-browser-helpers' | |||
| export interface ViewerRenderManagerOptions extends IRenderManagerOptions { | |||
| rgbm?: boolean, | |||
| @@ -13,7 +15,7 @@ export interface ViewerRenderManagerOptions extends IRenderManagerOptions { | |||
| } | |||
| @uiFolderContainer('Render Manager') | |||
| export class ViewerRenderManager extends RenderManager { | |||
| export class ViewerRenderManager extends RenderManager<IRenderManagerEvent, 'gbufferUnpackExtensionChanged'> { | |||
| readonly rgbm: boolean | |||
| readonly msaa: boolean | number | |||
| readonly depthBuffer: boolean | |||
| @@ -55,5 +57,15 @@ export class ViewerRenderManager extends RenderManager { | |||
| * Reference to the gbuffer target, if it exists. This can be set by plugins like {@link DepthBufferPlugin}, {@link GBufferPlugin} | |||
| */ | |||
| gbufferTarget: IRenderTarget | undefined | |||
| /** | |||
| * The extension that can be used to upload and unpack the values in gbuffer target(s), if it exists. This can be set by plugins like {@link DepthBufferPlugin}, {@link GBufferPlugin} | |||
| * Note: this should not be changed after set by some plugin. | |||
| */ | |||
| @onChange3(ViewerRenderManager.prototype._gbufferUnpackExtensionChanged) | |||
| gbufferUnpackExtension: MaterialExtension | undefined | |||
| private _gbufferUnpackExtensionChanged(params: any) { | |||
| this.dispatchEvent({type: 'gbufferUnpackExtensionChanged', ...params}) | |||
| } | |||
| } | |||