Explorar el Código

Dispose fix for scene model root import

master
Palash Bansal hace 11 meses
padre
commit
596d6ea570
No account linked to committer's email address

+ 14
- 9
src/assetmanager/AssetImporter.ts Ver fichero

@@ -6,7 +6,7 @@ import {
ImportFilesOptions,
ImportResult,
LoadFileOptions,
ProcessRawOptions,
ProcessRawOptions, RootSceneImportResult,
} from './IAssetImporter'
import {IAsset, IFile} from './IAsset'
import {IImporter, ILoader} from './IImporter'
@@ -199,14 +199,16 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEventMap> imple
if (result) result = await this.processRaw(result, options, path)
if (result) {
if (options.processRaw !== false) asset.preImported = result

// asset.preImportedRaw = undefined
// asset.preImported = undefined
const arrs: any[] = []
if (Array.isArray(result)) arrs.push(...result)
else {
if (result.userData?.rootSceneModelRoot) arrs.push(...result.children)
else arrs.push(result)
const push = (r: typeof result)=>{
if (r.userData?.rootSceneModelRoot) arrs.push(...r.children)
else arrs.push(r)
}
// remove preImportedRaw when any of the assets is disposed. This is to prevent memory leaks
if (Array.isArray(result)) result.map(push)
else push(result)
// remove preImportedRaw when any one of the assets is disposed. todo maybe do when ALL are dispoed?
arrs.forEach(r=>r.addEventListener?.('dispose', () => { // todo: recheck after dispose logic change
if (asset?.preImportedRaw) asset.preImportedRaw = undefined
if (asset?.preImported) asset.preImported = undefined
@@ -351,7 +353,7 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEventMap> imple
// it's a bit hacky to do this here, but it works for now. todo: move to a better place
let ress: any[] = []
if (Array.isArray(res)) ress = res.flat(2)
else if ((<IObject3D>res)?.userData?.rootSceneModelRoot) ress.push(...(<IObject3D>res).children)
else if ((<RootSceneImportResult>res)?.userData?.rootSceneModelRoot) ress.push(...(<IObject3D>res).children)
else ress.push(res)
for (const r of ress) r?.addEventListener?.('dispose', () => file.__loadedAsset = undefined)

@@ -425,7 +427,7 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEventMap> imple

if (Array.isArray(res)) {
const r: any[] = []
for (const re of res) { // todo: can we parallelize?
for (const re of res) { // todo: should we parallelize?
r.push(...await this.processRaw(re, options, path))
}
return r
@@ -457,6 +459,9 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEventMap> imple
}
}
}
if ((res as RootSceneImportResult).userData.rootSceneModelRoot) {
res._childrenCopy = [...res.children]
}

// if (res.assetType) // todo: why if?
res.assetImporterProcessed = true // this should not be put in userData

+ 6
- 0
src/assetmanager/IAssetImporter.ts Ver fichero

@@ -16,6 +16,12 @@ export interface RootSceneImportResult extends Object3D {
gltfAsset?: GLTF['asset']
[key: string]: any
}

/**
* copy of children to use after import, set in processRaw
* @internal
*/
_childrenCopy?: Object3D[]
}

export type ImportResultObject = IObject3D | ITexture | ICamera | ISerializedConfig | ISerializedViewerConfig | RootSceneImportResult | IMaterial

+ 9
- 5
src/core/object/RootScene.ts Ver fichero

@@ -224,7 +224,7 @@ export class RootScene<TE extends ISceneEventMap = ISceneEventMap> extends Scene
this.clearSceneModels(options.disposeSceneObjects)
}
if (!obj.userData?.rootSceneModelRoot) {
console.error('Invalid model root scene object. Trying to add anyway.', obj)
console.error('RootScene: Invalid model root scene object. Trying to add anyway.', obj)
}
if (obj.userData) {
// todo deep merge all userdata?
@@ -253,8 +253,8 @@ export class RootScene<TE extends ISceneEventMap = ISceneEventMap> extends Scene
this.modelRoot.animations.push(animation)
}
}
return [...obj.children] // need to clone
.map(c=>this.addObject(c, {...options, clearSceneObjects: false, disposeSceneObjects: false}))
const children = obj._childrenCopy || [...obj.children]
return children.map(c=>this.addObject(c, {...options, clearSceneObjects: false, disposeSceneObjects: false}))
}

private _addObject3D(model: IObject3D|null, {autoCenter = false, centerGeometries = false, centerGeometriesKeepPosition = true, autoScale = false, autoScaleRadius = 2., addToRoot = false, license}: AddObjectOptions = {}): void {
@@ -311,11 +311,15 @@ export class RootScene<TE extends ISceneEventMap = ISceneEventMap> extends Scene

disposeSceneModels(setDirty = true, clear = true) {
if (clear) {
[...this.modelRoot.children].forEach(child => child.dispose ? child.dispose() : child.removeFromParent())
for (const child of [...this.modelRoot.children]) {
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())
for (const child of this.modelRoot.children) {
child.dispose && child.dispose(false)
}
}
}


+ 16
- 4
src/viewer/ThreeViewer.ts Ver fichero

@@ -12,7 +12,7 @@ import {
Vector2,
Vector3,
} from 'three'
import {Class, createCanvasElement, downloadBlob, onChange, serialize, timeout, ValOrArr} from 'ts-browser-helpers'
import {Class, createCanvasElement, downloadBlob, onChange, serialize, ValOrArr} from 'ts-browser-helpers'
import {TViewerScreenShader} from '../postprocessing'
import {
AddObjectOptions,
@@ -1003,6 +1003,7 @@ export class ThreeViewer extends EventDispatcher<Record<IViewerEventTypes, IView
}

deleteImportedViewerConfigOnLoad = true
deleteImportedViewerConfigOnLoadWait = 2000 // ms

/**
* Add an object to the scene model root.
@@ -1014,12 +1015,23 @@ export class ThreeViewer extends EventDispatcher<Record<IViewerEventTypes, IView
if (imported.userData?.rootSceneModelRoot) {
const obj = <RootSceneImportResult>imported
this._scene.loadModelRoot(obj, options)
if (obj.importedViewerConfig && options?.importConfig !== false) await this.importConfig(obj.importedViewerConfig)
if (options?.importConfig !== false) {
if (obj.importedViewerConfig) {
await this.importConfig(obj.importedViewerConfig)
// @ts-expect-error no type for this
if (obj._deletedImportedViewerConfig) delete obj._deletedImportedViewerConfig
// @ts-expect-error no type for this
} else if (obj._deletedImportedViewerConfig)
this.console.error('ThreeViewer - Imported viewer config was deleted, cannot import it again. Set `viewer.deleteImportedViewerConfigOnLoad` to `false` to keep it in the object for reuse workflows.')
}

if (this.deleteImportedViewerConfigOnLoad && obj.importedViewerConfig) {
timeout(2000).then(()=>{ // todo timeout time?
setTimeout(()=>{
if (!obj.importedViewerConfig) return
delete obj.importedViewerConfig // any useful data in the config should be loaded into userData.__importData by then
})
// @ts-expect-error no type for this
obj._deletedImportedViewerConfig = true // for console warning above
}, this.deleteImportedViewerConfigOnLoadWait)
}
return this._scene.modelRoot as T
}

Cargando…
Cancelar
Guardar