Improve warning in AssetManager, Create MaterialManager.copyMaterialProps, fix camera aspect when canvas has height 0, add undo for some buttons, changes/fixes for ui, handle InteractionPromptPlugin in CameraViewPlugin, support async onStop and onComplete in PopmotionPlugin, allow resetting geometry in BaseGroundPlugin, add pluginListeners, forPlugin in ThreeViewer, add SwitchNodeBasePlugin.snapIcons, fixes in InteractionPromptPlugin, add isEditor, LS_DEFAULT_LOGO in LoadingScreenPlugin, add isEditor in FrameFadePlugin.
1 год назад Improve warning in AssetManager, Create MaterialManager.copyMaterialProps, fix camera aspect when canvas has height 0, add undo for some buttons, changes/fixes for ui, handle InteractionPromptPlugin in CameraViewPlugin, support async onStop and onComplete in PopmotionPlugin, allow resetting geometry in BaseGroundPlugin, add pluginListeners, forPlugin in ThreeViewer, add SwitchNodeBasePlugin.snapIcons, fixes in InteractionPromptPlugin, add isEditor, LS_DEFAULT_LOGO in LoadingScreenPlugin, add isEditor in FrameFadePlugin.
1 год назад Improve warning in AssetManager, Create MaterialManager.copyMaterialProps, fix camera aspect when canvas has height 0, add undo for some buttons, changes/fixes for ui, handle InteractionPromptPlugin in CameraViewPlugin, support async onStop and onComplete in PopmotionPlugin, allow resetting geometry in BaseGroundPlugin, add pluginListeners, forPlugin in ThreeViewer, add SwitchNodeBasePlugin.snapIcons, fixes in InteractionPromptPlugin, add isEditor, LS_DEFAULT_LOGO in LoadingScreenPlugin, add isEditor in FrameFadePlugin.
1 год назад Improve warning in AssetManager, Create MaterialManager.copyMaterialProps, fix camera aspect when canvas has height 0, add undo for some buttons, changes/fixes for ui, handle InteractionPromptPlugin in CameraViewPlugin, support async onStop and onComplete in PopmotionPlugin, allow resetting geometry in BaseGroundPlugin, add pluginListeners, forPlugin in ThreeViewer, add SwitchNodeBasePlugin.snapIcons, fixes in InteractionPromptPlugin, add isEditor, LS_DEFAULT_LOGO in LoadingScreenPlugin, add isEditor in FrameFadePlugin.
1 год назад Improve warning in AssetManager, Create MaterialManager.copyMaterialProps, fix camera aspect when canvas has height 0, add undo for some buttons, changes/fixes for ui, handle InteractionPromptPlugin in CameraViewPlugin, support async onStop and onComplete in PopmotionPlugin, allow resetting geometry in BaseGroundPlugin, add pluginListeners, forPlugin in ThreeViewer, add SwitchNodeBasePlugin.snapIcons, fixes in InteractionPromptPlugin, add isEditor, LS_DEFAULT_LOGO in LoadingScreenPlugin, add isEditor in FrameFadePlugin.
1 год назад |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- import {LinearFilter, WebGLRenderTarget} from 'three'
- import {IPassID, IPipelinePass} from '../../postprocessing'
- import {ThreeViewer} from '../../viewer'
- import {PipelinePassPlugin} from '../base/PipelinePassPlugin'
- import {uiFolderContainer, uiToggle} from 'uiconfig.js'
- import {ITexture, IWebGLRenderer} from '../../core'
- import {AddBlendTexturePass} from '../../postprocessing/AddBlendTexturePass'
- import {now, serialize, timeout, ValOrFunc} from 'ts-browser-helpers'
- import {ProgressivePlugin} from './ProgressivePlugin'
- import {IRenderTarget} from '../../rendering'
-
- /**
- * FrameFade Plugin
- *
- * Adds a post-render pass to smoothly fade to a new rendered frame over time.
- * This is useful for example when changing the camera position, material, object properties, etc to avoid a sudden jump.
- * @category Plugins
- */
- @uiFolderContainer('FrameFade Plugin')
- export class FrameFadePlugin
- extends PipelinePassPlugin<FrameFadeBlendPass, 'frameFade'> {
-
- readonly passId = 'frameFade'
- public static readonly PluginType = 'FrameFadePlugin'
-
- dependencies = [ProgressivePlugin]
-
- // disables fadeOn... options but not serialized
- isEditor = false
-
- @serialize() @uiToggle() fadeOnActiveCameraChange = true
- @serialize() @uiToggle() fadeOnMaterialUpdate = true
- @serialize() @uiToggle() fadeOnSceneUpdate = true
-
- protected _pointerEnabled = true
- protected _target?: IRenderTarget
-
- constructor(
- enabled = true,
- ) {
- super()
- this.enabled = enabled
- this.startTransition = this.startTransition.bind(this)
- this.stopTransition = this.stopTransition.bind(this)
- this._fadeCam = this._fadeCam.bind(this)
- this._fadeMat = this._fadeMat.bind(this)
- this.isDisabled = ((sup)=>()=>!this._pointerEnabled || sup())(this.isDisabled)
- }
-
- saveFrameTimeThreshold = 500 // ms
-
- /**
- * Start a frame fade transition.
- * Note that the current frame data will only be used if the last running transition is ended or near the end. To do it anyway, call {@link stopTransition} first
- * @param duration
- */
- public async startTransition(duration: number) { // duration in ms
- if (!this._viewer || !this._pass || this.isDisabled()) return
- if (!this._target)
- this._target = this._viewer.renderManager.getTempTarget({
- sizeMultiplier: 1.,
- minFilter: LinearFilter,
- magFilter: LinearFilter,
- colorSpace: (this._viewer.renderManager.composerTarget.texture as ITexture).colorSpace,
- })
-
- if (this._pass.fadeTimeState < this.saveFrameTimeThreshold) // only save if very near the end
- this._pass.toSaveFrame = true
-
- this._pass.fadeTimeState = Math.max(duration, this._pass.fadeTimeState)
- this._pass.fadeTime = this._pass.fadeTimeState
- // this._pass.enabled = true
- this.setDirty()
- await timeout(duration)
- }
-
- /**
- * Stop a frame fade transition if running. Note that it will be stopped next frame.
- */
- public stopTransition() {
- if (!this._pass) return
- this._pass.fadeTimeState = 0. // will be stopped in update on next frame
- }
-
- onAdded(viewer: ThreeViewer) {
- super.onAdded(viewer)
- viewer.scene.addEventListener('mainCameraUpdate', this.stopTransition)
- viewer.scene.addEventListener('mainCameraChange', this._fadeCam)
- viewer.scene.addEventListener('materialUpdate', this._fadeMat)
- viewer.scene.addEventListener('sceneUpdate', this._fadeScene)
- viewer.scene.addEventListener('objectUpdate', this._fadeObjectUpdate)
- window.addEventListener('pointermove', this._onPointerMove) // has to be on window
- }
-
- onRemove(viewer: ThreeViewer) {
- viewer.scene.removeEventListener('mainCameraUpdate', this.stopTransition)
- viewer.scene.removeEventListener('mainCameraChange', this._fadeCam)
- viewer.scene.removeEventListener('materialUpdate', this._fadeMat)
- viewer.scene.removeEventListener('sceneUpdate', this._fadeScene)
- viewer.scene.removeEventListener('objectUpdate', this._fadeObjectUpdate)
- window.removeEventListener('pointermove', this._onPointerMove)
- super.onRemove(viewer)
- }
-
- private _fadeCam = async(ev: any)=>
- ev.frameFade !== false && !this.isEditor && this.fadeOnActiveCameraChange && this.startTransition(ev.fadeDuration || 1000)
- private _fadeMat = async(ev: any)=>
- ev.frameFade !== false && !this.isEditor && this.fadeOnMaterialUpdate && this.startTransition(ev.fadeDuration || 200)
- private _fadeScene = async(ev: any)=>
- ev.frameFade !== false && !this.isEditor && this.fadeOnSceneUpdate && this.startTransition(ev.fadeDuration || 500)
- private _fadeObjectUpdate = async(ev: any)=>
- ev.frameFade && !this.isEditor && this.startTransition(ev.fadeDuration || 500)
-
- private _onPointerMove = (ev: PointerEvent)=> {
- const canvas = this._viewer?.canvas
- if (!canvas) {
- this._pointerEnabled = false
- return
- }
-
- // no button is pressed
- if (!ev.buttons || ev.target !== canvas) {
- this._pointerEnabled = true
- return
- }
-
- // check if pointer is over canvas
- const rect = canvas.getBoundingClientRect()
- const x = (ev.clientX - rect.left) / rect.width
- const y = (ev.clientY - rect.top) / rect.height
- this._pointerEnabled = x < 0 || x > 1 || y < 0 || y > 1
- }
-
- setDirty() {
- super.setDirty()
- if (this.isDisabled()) return
- this._viewer?.setDirty()
- }
-
- get dirty() {
- return !this.isDisabled() && !!this._pass && this._pass.fadeTimeState > 0
- }
-
- set dirty(_: boolean) {
- console.error('FrameFadePlugin.dirty is readonly')
- }
-
- protected _createPass() {
- return new FrameFadeBlendPass(this.passId, this, this._viewer?.renderManager.maxHDRIntensity)
- }
-
- get canFrameFade() {
- return this._target && this._pointerEnabled &&
- this.dirty && this._pass &&
- this._pass.fadeTimeState > 0.001 &&
- this._viewer && this._viewer.scene.renderCamera === this._viewer.scene.mainCamera
- }
-
- get lastFrame() {
- return this._viewer?.getPlugin(ProgressivePlugin)?.texture
- }
-
- get target() {
- return this._target
- }
-
- protected _beforeRender(): boolean {
- if (!super._beforeRender() || !this._pass) return false
-
- if (this.isDisabled()) this.stopTransition()
-
- if (this._pass.fadeTimeState < 0.001) {
- this._pass.toSaveFrame = false
- if (this._target && this._viewer) {
- this._viewer.renderManager.releaseTempTarget(this._target)
- this._target = undefined
- }
- }
- return true
- }
-
- }
-
- export class FrameFadeBlendPass extends AddBlendTexturePass implements IPipelinePass {
- before = ['progressive', 'taa']
- after = ['render']
- required = ['render', 'progressive']
- dirty: ValOrFunc<boolean> = () => false
-
- fadeTime = 0 // ms
- fadeTimeState = 0
- toSaveFrame = false
-
- private _lastTime = 0
-
- constructor(public readonly passId: IPassID, public plugin: FrameFadePlugin, maxIntensity = 120) {
- super(undefined, maxIntensity)
- }
-
- render(renderer: IWebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, deltaTime: number, maskActive: boolean) {
- this.needsSwap = false
- const target = this.plugin.target
-
- if (!this.plugin.canFrameFade || !target) return
- const lastFrame = this.plugin.lastFrame
- if (this.toSaveFrame && lastFrame) {
- renderer.renderManager.blit(target, {source: lastFrame, respectColorSpace: false})
- this._lastTime = 0
- this.toSaveFrame = false
- }
-
- this.uniforms.tDiffuse2.value = target.texture
-
- const weight = this.fadeTimeState / this.fadeTime
- this.uniforms.weight2.value.setScalar(weight)
- this.uniforms.weight2.value.w = 1
- this.uniforms.weight.value.setScalar(1. - weight)
- this.uniforms.weight.value.w = 1
- super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
- this.needsSwap = true
-
- const time = now()
- if (this._lastTime < 10) this._lastTime = time - 10 // ms
- const dt = time - this._lastTime
- this._lastTime = time
-
- this.fadeTimeState -= dt
- }
-
- }
-
- declare module '../../core/IObject'{
- export interface IObjectSetDirtyOptions{
- frameFade?: boolean
- }
- }
|