Ver código fonte

Dispose fix for scene model root import

master
Palash Bansal 11 meses atrás
pai
commit
596d6ea570
Nenhuma conta vinculada ao e-mail do autor do commit

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

ImportFilesOptions, ImportFilesOptions,
ImportResult, ImportResult,
LoadFileOptions, LoadFileOptions,
ProcessRawOptions,
ProcessRawOptions, RootSceneImportResult,
} from './IAssetImporter' } from './IAssetImporter'
import {IAsset, IFile} from './IAsset' import {IAsset, IFile} from './IAsset'
import {IImporter, ILoader} from './IImporter' import {IImporter, ILoader} from './IImporter'
if (result) result = await this.processRaw(result, options, path) if (result) result = await this.processRaw(result, options, path)
if (result) { if (result) {
if (options.processRaw !== false) asset.preImported = result if (options.processRaw !== false) asset.preImported = result

// asset.preImportedRaw = undefined
// asset.preImported = undefined
const arrs: any[] = [] 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 arrs.forEach(r=>r.addEventListener?.('dispose', () => { // todo: recheck after dispose logic change
if (asset?.preImportedRaw) asset.preImportedRaw = undefined if (asset?.preImportedRaw) asset.preImportedRaw = undefined
if (asset?.preImported) asset.preImported = undefined if (asset?.preImported) asset.preImported = undefined
// it's a bit hacky to do this here, but it works for now. todo: move to a better place // it's a bit hacky to do this here, but it works for now. todo: move to a better place
let ress: any[] = [] let ress: any[] = []
if (Array.isArray(res)) ress = res.flat(2) 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) else ress.push(res)
for (const r of ress) r?.addEventListener?.('dispose', () => file.__loadedAsset = undefined) for (const r of ress) r?.addEventListener?.('dispose', () => file.__loadedAsset = undefined)




if (Array.isArray(res)) { if (Array.isArray(res)) {
const r: any[] = [] 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)) r.push(...await this.processRaw(re, options, path))
} }
return r return r
} }
} }
} }
if ((res as RootSceneImportResult).userData.rootSceneModelRoot) {
res._childrenCopy = [...res.children]
}


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

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

gltfAsset?: GLTF['asset'] gltfAsset?: GLTF['asset']
[key: string]: any [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 export type ImportResultObject = IObject3D | ITexture | ICamera | ISerializedConfig | ISerializedViewerConfig | RootSceneImportResult | IMaterial

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

this.clearSceneModels(options.disposeSceneObjects) this.clearSceneModels(options.disposeSceneObjects)
} }
if (!obj.userData?.rootSceneModelRoot) { 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) { if (obj.userData) {
// todo deep merge all userdata? // todo deep merge all userdata?
this.modelRoot.animations.push(animation) 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 { private _addObject3D(model: IObject3D|null, {autoCenter = false, centerGeometries = false, centerGeometriesKeepPosition = true, autoScale = false, autoScaleRadius = 2., addToRoot = false, license}: AddObjectOptions = {}): void {


disposeSceneModels(setDirty = true, clear = true) { disposeSceneModels(setDirty = true, clear = true) {
if (clear) { 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() this.modelRoot.clear()
if (setDirty) this.setDirty({refreshScene: true}) if (setDirty) this.setDirty({refreshScene: true})
} else { } 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 arquivo

Vector2, Vector2,
Vector3, Vector3,
} from 'three' } 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 {TViewerScreenShader} from '../postprocessing'
import { import {
AddObjectOptions, AddObjectOptions,
} }


deleteImportedViewerConfigOnLoad = true deleteImportedViewerConfigOnLoad = true
deleteImportedViewerConfigOnLoadWait = 2000 // ms


/** /**
* Add an object to the scene model root. * Add an object to the scene model root.
if (imported.userData?.rootSceneModelRoot) { if (imported.userData?.rootSceneModelRoot) {
const obj = <RootSceneImportResult>imported const obj = <RootSceneImportResult>imported
this._scene.loadModelRoot(obj, options) 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) { 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 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 return this._scene.modelRoot as T
} }

Carregando…
Cancelar
Salvar