Przeglądaj źródła

Add sendArgs: false wherever required, CameraViewPlugin improvements, add clear option to ThreeViewer.dispose and RenderTargetManager.dispose, minor fixes.

master
Palash Bansal 1 rok temu
rodzic
commit
b237f88069
No account linked to committer's email address

+ 1
- 0
package.json Wyświetl plik

@@ -48,6 +48,7 @@
"files": [
"dist",
"src",
"lib",
"examples",
"plugins/*/dist",
"plugins/*/src",

+ 2
- 2
src/core/IRenderer.ts Wyświetl plik

@@ -1,4 +1,4 @@
import {IDisposable, PartialRecord} from 'ts-browser-helpers'
import {PartialRecord} from 'ts-browser-helpers'
import {
Blending,
Clock,
@@ -36,7 +36,7 @@ export type IRenderManagerEvent = Partial<IAnimationLoopEvent>&Partial<IRenderMa
}
export type IRenderManagerEventTypes = 'animationLoop'|'update'|'resize'|'contextLost'|'contextRestored'
export interface RendererBlitOptions {source?: Texture, viewport?: Vector4, material?: ShaderMaterial, clear?: boolean, respectColorSpace?: boolean, blending?: Blending, transparent?: boolean}
export interface IRenderManager<E extends IRenderManagerEvent = IRenderManagerEvent, ET extends string = IRenderManagerEventTypes> extends RenderTargetManager<E, ET>, IDisposable, IShaderPropertiesUpdater{
export interface IRenderManager<E extends IRenderManagerEvent = IRenderManagerEvent, ET extends string = IRenderManagerEventTypes> extends RenderTargetManager<E, ET>, IShaderPropertiesUpdater{
readonly renderer: IWebGLRenderer
readonly needsRender: boolean
rebuildPipeline(setDirty?: boolean): void

+ 21
- 11
src/core/object/RootScene.ts Wyświetl plik

@@ -262,7 +262,7 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I
this.setDirty({refreshScene: true})
}

@uiButton()
@uiButton(undefined, {sendArgs: false})
centerAllGeometries(keepPosition = true, obj?: IObject3D) {
const geoms = new Set<IGeometry>()
;(obj ?? this.modelRoot).traverse((o) => o.geometry && geoms.add(o.geometry))
@@ -276,10 +276,14 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I
setDirty && this.setDirty({refreshScene: true})
}

disposeSceneModels(setDirty = true) {
[...this.modelRoot.children].forEach(child => child.dispose ? child.dispose() : child.removeFromParent())
this.modelRoot.clear()
if (setDirty) this.setDirty({refreshScene: true})
disposeSceneModels(setDirty = true, clear = true) {
if (clear) {
[...this.modelRoot.children].forEach(child => child.dispose ? child.dispose() : child.removeFromParent())
this.modelRoot.clear()
if (setDirty) this.setDirty({refreshScene: true})
} else {
this.modelRoot.children.forEach(child => child.dispose && child.dispose())
}
}

private _onEnvironmentChange() {
@@ -372,16 +376,22 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I
* Dispose the scene and clear all resources.
* @warn Not fully implemented yet, just clears the scene.
*/
dispose(): void {
this.disposeSceneModels();
[...this.children].forEach(child => child.dispose ? child.dispose() : child.removeFromParent())
this.clear()
dispose(clear = true): void {
this.disposeSceneModels(false, clear)

if (clear) {
[...this.children].forEach(child => child.dispose ? child.dispose() : child.removeFromParent())
this.clear()
}

// todo: dispose more stuff?
this.environment?.dispose()
if ((this.background as ITexture)?.isTexture) (this.background as ITexture)?.dispose?.()
this.environment = null
this.background = null

if (clear) {
this.environment = null
this.background = null
}
return
}


+ 18
- 7
src/plugins/animation/CameraViewPlugin.ts Wyświetl plik

@@ -118,7 +118,7 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC
return super.onRemove(viewer)
}

@uiButton('Reset To First View')
@uiButton('Reset To First View', {sendArgs: false})
public async resetToFirstView(duration = 100) {
if (this.isDisabled()) return
this._currentView = undefined
@@ -138,7 +138,7 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC
}

addView(view: CameraView) {
this._cameraViews.push(view)
if (!this._cameraViews.includes(view)) this._cameraViews.push(view)
view.addEventListener('setView', this._viewSetView as any)
view.addEventListener('updateView', this._viewUpdateView as any)
view.addEventListener('deleteView', this._viewDeleteView as any)
@@ -185,6 +185,10 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC
const i = this._cameraViews.indexOf(view)
if (i >= 0)
this._cameraViews.splice(i, 1)
view.removeEventListener('setView', this._viewSetView as any)
view.removeEventListener('updateView', this._viewUpdateView as any)
view.removeEventListener('deleteView', this._viewDeleteView as any)
view.removeEventListener('animateView', this._viewAnimateView as any)
this.uiConfig.uiRefresh?.()
this.dispatchEvent({type: 'viewDelete', view})
}
@@ -203,7 +207,7 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC

private _currentView: CameraView | undefined

@uiButton('Focus Next') focusNext = (wrap = true)=>{
@uiButton('Focus Next', {sendArgs: false}) focusNext = (wrap = true)=>{
if (this._animating) return
if (this._cameraViews.length < 2) return
let index = this._cameraViews.findIndex(v=>v === this._currentView)
@@ -213,7 +217,7 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC
else index = index % this._cameraViews.length
this.animateToView(index)
}
@uiButton('Focus Previous') focusPrevious = (wrap = true)=> {
@uiButton('Focus Previous', {sendArgs: false}) focusPrevious = (wrap = true)=> {
if (this._animating) return
if (this._cameraViews.length < 2 || !this._currentView) return
let index = this._cameraViews.findIndex(v=>v === this._currentView)
@@ -226,7 +230,7 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC

private _popAnimations: AnimationResult[] = []

async animateToView(_view: CameraView|number, duration?: number, easing?: Easing|EasingFunctionType, camera?: ICamera, throwOnStop = false) {
async animateToView(_view: CameraView|number|string, duration?: number, easing?: Easing|EasingFunctionType, camera?: ICamera, throwOnStop = false) {
camera = camera || this._viewer?.scene.mainCamera
if (!camera) return
// if (this._currentView === view) return // todo: also check if the camera is at the correct position and orientation, till then use resetToFirstView to reset current view
@@ -245,7 +249,13 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC
return
}
}
const view = typeof _view === 'number' ? this._cameraViews[_view] : _view
const view = typeof _view === 'number' ? this._cameraViews[_view] :
typeof _view === 'string' ? this._cameraViews.find(v=>v.name === _view) :
_view
if (!view) {
this._viewer?.console.warn('Invalid view', _view)
return
}

this._currentView = view
this._animating = true
@@ -303,6 +313,7 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC
fromJSON(data: any, meta?: any): this | null {
this._cameraViews.forEach(v=>this.deleteView(v)) // deserialize pushes to the existing array
if (super.fromJSON(data, meta)) {
this._cameraViews.forEach(v=>this.addView(v))
this.uiConfig.uiRefresh?.()
return this
}
@@ -311,7 +322,7 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC

public async animateToObject(selected?: Object3D, distanceMultiplier = 4, duration?: number, ease?: Easing|EasingFunctionType, distanceBounds = {min: 0.5, max: 5.0}) {
if (!this._viewer) return
const bbox = new Box3B().expandByObject(selected || this._viewer.scene.modelRoot.modelObject, false, true)
const bbox = new Box3B().expandByObject(selected || this._viewer.scene.modelRoot, false, true)
const center = bbox.getCenter(new Vector3())
const size = bbox.getSize(new Vector3())
const radius = size.length() / 2

+ 2
- 2
src/plugins/animation/GLTFAnimationPlugin.ts Wyświetl plik

@@ -340,7 +340,7 @@ export class GLTFAnimationPlugin extends AViewerPluginSync<'checkpointEnd'|'chec
this._viewer?.setDirty()
}

@uiButton('Stop')
@uiButton('Stop', {sendArgs: false})
stopAnimation(reset = false) {
this._animationState = 'stopped'
// safeSetProperty(this._viewer?.getPlugin<PickingPlugin>('Picking'), 'enabled', true)
@@ -355,7 +355,7 @@ export class GLTFAnimationPlugin extends AViewerPluginSync<'checkpointEnd'|'chec

}

@uiButton('Reset')
@uiButton('Reset', {sendArgs: false})
resetAnimation() {
if (this._animationState !== 'stopped' && this._animationState !== 'none') {
this.stopAnimation(true) // reset and stop

+ 6
- 1
src/plugins/export/CanvasSnapshotPlugin.ts Wyświetl plik

@@ -89,7 +89,7 @@ export class CanvasSnapshotPlugin extends AViewerPluginSync<''> {
quality: 0.9,
}

@uiButton('Download .png')
// @uiButton('Download .png', {sendArgs: false})
async downloadSnapshot(filename?: string, options: CanvasSnapshotOptions&{waitForProgressive?: boolean} = {waitForProgressive: true}): Promise<void> {
if (!this._viewer) return
if (!options.mimeType && !filename) this.filename = this.filename.split('.').slice(0, -1).join('.') + '.png'
@@ -97,6 +97,11 @@ export class CanvasSnapshotPlugin extends AViewerPluginSync<''> {
if (file) await this._viewer.exportBlob(file, file.name)
}

@uiButton('Download .png')
protected async _downloadPng(): Promise<void> {
this.filename = this.filename.split('.').slice(0, -1).join('.') + '.png'
return this.downloadSnapshot(undefined, {mimeType: 'image/png'})
}
@uiButton('Download .jpeg')
protected async _downloadJpeg(): Promise<void> {
this.filename = this.filename.split('.').slice(0, -1).join('.') + '.jpeg'

+ 2
- 2
src/plugins/extras/ContactShadowGroundPlugin.ts Wyświetl plik

@@ -17,11 +17,11 @@ import {BaseGroundPlugin} from '../base/BaseGroundPlugin'
import {GBufferRenderPass} from '../../postprocessing'
import {ThreeViewer} from '../../viewer'
import {IRenderTarget} from '../../rendering'
import {uiFolderContainer, uiSlider, uiToggle} from 'uiconfig.js'
import {uiPanelContainer, uiSlider, uiToggle} from 'uiconfig.js'
import {HVBlurHelper} from '../../three/utils/HVBlurHelper'
import {shaderReplaceString} from '../../utils'

@uiFolderContainer('Contact Shadow Ground')
@uiPanelContainer('Contact Shadow Ground')
export class ContactShadowGroundPlugin extends BaseGroundPlugin {
static readonly PluginType = 'ContactShadowGroundPlugin'


+ 1
- 1
src/plugins/extras/Object3DGeneratorPlugin.ts Wyświetl plik

@@ -27,7 +27,7 @@ export class Object3DGeneratorPlugin extends AViewerPluginSync<''> {
}))
protected _selectedType = ''

@uiButton('Generate')
@uiButton('Generate', {sendArgs: false})
generate(type?: string, params?: any, addToScene = true) {
if (!this._viewer) throw new Error('No viewer')
const obj = this.generators[type ?? this._selectedType]?.(params)

+ 1
- 1
src/plugins/extras/SimplifyModifierPlugin.ts Wyświetl plik

@@ -149,7 +149,7 @@ export abstract class SimplifyModifierPlugin extends AViewerPluginSync<''> {
*/
protected abstract _simplify(geometry: IGeometry, count: number): IGeometry

@uiButton('Simplify All')
@uiButton('Simplify All', {sendArgs: false})
async simplifyAll(root?: IObject3D, options?: SimplifyOptions) {
if (!root && this._viewer) root = this._viewer.scene.modelRoot
if (!root) {

+ 3
- 3
src/plugins/interaction/FullScreenPlugin.ts Wyświetl plik

@@ -51,7 +51,7 @@ export class FullScreenPlugin extends AViewerPluginSync<'enter'|'exit'> {
}
}

@uiButton('Enter FullScreen')
@uiButton('Enter FullScreen', {sendArgs: false})
async enter(element?: HTMLElement): Promise<void> {
if (this.isFullScreen()) return

@@ -82,7 +82,7 @@ export class FullScreenPlugin extends AViewerPluginSync<'enter'|'exit'> {
return elem.msRequestFullscreen()
}
}
@uiButton('Exit FullScreen')
@uiButton('Exit FullScreen', {sendArgs: false})
async exit(): Promise<void> {
if (document.exitFullscreen) {
return document.exitFullscreen()
@@ -94,7 +94,7 @@ export class FullScreenPlugin extends AViewerPluginSync<'enter'|'exit'> {
return (document as any).msExitFullscreen()
}
}
@uiButton('Toggle FullScreen')
@uiButton('Toggle FullScreen', {sendArgs: false})
async toggle(element?: HTMLElement): Promise<void> {
if (this.isFullScreen()) {
return this.exit()

+ 1
- 1
src/plugins/material/ParallaxMappingPlugin.ts Wyświetl plik

@@ -13,7 +13,7 @@ import ParallaxMappingPluginReliefShader from './shaders/ParallaxMappingPlugin.r
* This is a port of Relief Parallax Mapping from [Rabbid76/graphics-snippets](https://github.com/Rabbid76/graphics-snippets/blob/master/html/technique/parallax_005_parallax_relief_mapping_derivative_tbn.html)
* @category Plugins
*/
@uiFolderContainer('Parallax Mapping')
@uiFolderContainer('Parallax Bump Mapping (MatExt)')
export class ParallaxMappingPlugin extends AViewerPluginSync<''> {
public static PluginType = 'ReliefParallaxMapping'


+ 2
- 1
src/postprocessing/GBufferRenderPass.ts Wyświetl plik

@@ -25,8 +25,9 @@ export class GBufferRenderPass<TP extends IPassID=IPassID, T extends WebGLMultip

preprocessMaterial = (material: IMaterial, renderToGBuffer?: boolean) => {
renderToGBuffer = renderToGBuffer ?? material.userData.renderToGBuffer
if (material.userData.pluginsDisabled) renderToGBuffer = false
if (
material.transparent && renderToGBuffer || // transparent and render to gbuffer
material.transparent && (renderToGBuffer || material.opacity > 0.99) || // transparent and render to gbuffer
!material.transparent && !material.transmission && renderToGBuffer === false // opaque and dont render to gbuffer
) {
this._transparentMats.add(material)

+ 6
- 6
src/rendering/RenderManager.ts Wyświetl plik

@@ -96,7 +96,7 @@ export class RenderManager<TEvent extends BaseEvent = IRenderManagerEvent, TEven
@onChange2(RenderManager.prototype.rebuildPipeline)
public autoBuildPipeline = true

@uiButton('Rebuild Pipeline')
@uiButton('Rebuild Pipeline', {sendArgs: false})
rebuildPipeline(setDirty = true): void {
this._passesNeedsUpdate = true
if (setDirty) this._updated({change: 'rebuild'})
@@ -275,8 +275,8 @@ export class RenderManager<TEvent extends BaseEvent = IRenderManagerEvent, TEven
this._updated({change: 'passRefresh'})
}

dispose(): void {
super.dispose()
dispose(clear = true): void {
super.dispose(clear)
this._renderer.dispose()
}

@@ -521,7 +521,7 @@ export class RenderManager<TEvent extends BaseEvent = IRenderManagerEvent, TEven
* @param quality
* @param textureIndex - index of the texture to use in the render target (only in case of multiple render target)
*/
renderTargetToDataUrl(target: WebGLMultipleRenderTargets|WebGLRenderTarget, mimeType = 'image/png', quality = 90, textureIndex = 0): string {
renderTargetToDataUrl(target: WebGLMultipleRenderTargets|WebGLRenderTarget|IRenderTarget, mimeType = 'image/png', quality = 90, textureIndex = 0): string {
const canvas = document.createElement('canvas')
canvas.width = target.width
canvas.height = target.height
@@ -530,11 +530,11 @@ export class RenderManager<TEvent extends BaseEvent = IRenderManagerEvent, TEven
const texture = Array.isArray(target.texture) ? target.texture[textureIndex] : target.texture
const imageData = ctx.createImageData(target.width, target.height, {colorSpace: ['display-p3', 'srgb'].includes(texture.colorSpace) ? <PredefinedColorSpace>texture.colorSpace : undefined})
if (texture.type === HalfFloatType || texture.type === FloatType) {
const buffer = this.renderTargetToBuffer(target, textureIndex)
const buffer = this.renderTargetToBuffer(target as any, textureIndex)
textureDataToImageData({data: buffer, width: target.width, height: target.height}, 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, undefined, textureIndex)
this._renderer.readRenderTargetPixels(target as any, 0, 0, target.width, target.height, imageData.data, undefined, textureIndex)
}

ctx.putImageData(imageData, 0, 0)

+ 6
- 4
src/rendering/RenderTargetManager.ts Wyświetl plik

@@ -150,12 +150,14 @@ export abstract class RenderTargetManager<E extends BaseEvent = BaseEvent, ET ex

protected abstract _createTargetClass(clazz: Class<WebGLRenderTarget>, size: number[], options: WebGLRenderTargetOptions): IRenderTarget

dispose() {
dispose(clear = true) {
this._trackedTargets.forEach(t=>t.dispose())
Object.values(this._trackedTempTargets).forEach(t=>t.dispose())
this._trackedTargets = []
this._releasedTempTargets = {}
this._trackedTempTargets = []
if (clear) {
this._trackedTargets = []
this._releasedTempTargets = {}
this._trackedTempTargets = []
}
}

/**

+ 16
- 12
src/viewer/ThreeViewer.ts Wyświetl plik

@@ -570,27 +570,31 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
/**
* Disposes the viewer and frees up all resource and events. Do not use the viewer after calling dispose.
* @note - If you want to reuse the viewer, set viewer.enabled to false instead, then set it to true again when required. To dispose all the objects, materials in the scene use `viewer.scene.disposeSceneModels()`
* This function is not fully implemented yet. There might be some memory leaks.
* This function is not fully implemented yet. There might be some leaks.
* @todo - return promise?
*/
public dispose(): void {
public dispose(clear = true): void {
// todo: dispose stuff from constructor etc
for (const plugin of [...Object.values(this.plugins)]) {
this.removePlugin(plugin, true)
if (clear) {
for (const plugin of [...Object.values(this.plugins)]) {
this.removePlugin(plugin, true)
}
}

this._scene.dispose()
this.renderManager.dispose()
this._scene.dispose(clear)
this.renderManager.dispose(clear)

this._canvas.removeEventListener('webglcontextrestored', this._onContextRestore, false)
this._canvas.removeEventListener('webglcontextlost', this._onContextLost, false)
if (clear) {
this._canvas.removeEventListener('webglcontextrestored', this._onContextRestore, false)
this._canvas.removeEventListener('webglcontextlost', this._onContextLost, false)

;(window as any).threeViewers?.splice((window as any).threeViewers.indexOf(this), 1)
;(window as any).threeViewers?.splice((window as any).threeViewers.indexOf(this), 1)

if (this.resizeObserver) this.resizeObserver.unobserve(this._canvas)
window.removeEventListener('resize', this.resize)
if (this.resizeObserver) this.resizeObserver.unobserve(this._canvas)
window.removeEventListener('resize', this.resize)
}

this.dispatchEvent({type: 'dispose'})
this.dispatchEvent({type: 'dispose', clear})
}

/**

+ 1
- 1
src/viewer/version.ts Wyświetl plik

@@ -1 +1 @@
export const VERSION = '0.0.31'
export const VERSION = '0.0.32'

Ładowanie…
Anuluj
Zapisz