Selaa lähdekoodia

Change logic for disposing scene assets. Add iGeometryCommons.dispose and iMaterialCommons.dispose.

master
Palash Bansal 2 vuotta sitten
vanhempi
commit
f2f6312a16
No account linked to committer's email address

+ 19
- 16
src/assetmanager/AssetImporter.ts Näytä tiedosto

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'
// console.log(result) // console.log(result)
if (!options.forceImport && result) { if (!options.forceImport && result) {
const results = await this.processRaw<T>(result, options) // just in case its not processed. Internal check is done to ensure it's not processed twice const results = await this.processRaw<T>(result, options) // just in case its not processed. Internal check is done to ensure it's not processed twice
let isDisposed = false // if any of the objects is disposed
for (const r of results) {
// todo: check if this is still required.
if ((r as RootSceneImportResult)?.userData?.rootSceneModelRoot) { // in case processImported is false we need a special case check here
if (r?.children?.find((c: any) => c.__disposed)) {
isDisposed = true
break
}
}
if (r && !r.__disposed) continue // todo add __disposed to object, material, texture, etc
isDisposed = true
break
}
// let isDisposed = false // if any of the objects is disposed
// for (const r of results) {
// // todo: check if this is still required.
// if ((r as RootSceneImportResult)?.userData?.rootSceneModelRoot) { // in case processImported is false we need a special case check here
// if (r?.children?.find((c: any) => c.__disposed)) {
// isDisposed = true
// break
// }
// }
// if (r && !r.__disposed) continue // todo add __disposed to object, material, texture, etc
// isDisposed = true
// break
// }
// todo: should we check if any of it's children is disposed ? // todo: should we check if any of it's children is disposed ?
if (!isDisposed || options.reimportDisposed === false) return results
// if (!isDisposed || options.reimportDisposed === false)
return results
} }


// todo: add support to get cloned asset? if we want to import multiple times and everytime return a cloned asset // todo: add support to get cloned asset? if we want to import multiple times and everytime return a cloned asset
else arrs.push(result) else arrs.push(result)
} }
// remove preImportedRaw when any of the assets is disposed. This is to prevent memory leaks // remove preImportedRaw when any of the assets is disposed. This is to prevent memory leaks
arrs.forEach(r=>r.addEventListener?.('dispose', () => {
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
})) }))
if (file) { if (file) {
file.__loadedAsset = res file.__loadedAsset = res



// todo: recheck below code after dispose logic change

// Clear the reference __loadedAsset when any one asset is disposed. // Clear the reference __loadedAsset when any one asset is disposed.
// 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[] = []

+ 1
- 4
src/assetmanager/IAssetImporter.ts Näytä tiedosto



userData?: IImportResultUserData userData?: IImportResultUserData


// eslin t-disable-next-line @typescript-eslint/naming-convention
__rootPath?: string __rootPath?: string
// eslin t-disable-next-line @typescript-eslint/naming-convention
__rootBlob?: IFile __rootBlob?: IFile
// eslin t-disable-next-line @typescript-eslint/naming-convention
__disposed?: boolean
// __disposed?: boolean


[key: string]: any [key: string]: any
} }

+ 6
- 9
src/assetmanager/MaterialManager.ts Näytä tiedosto

} from '../core' } from '../core'
import {downloadFile} from 'ts-browser-helpers' import {downloadFile} from 'ts-browser-helpers'
import {MaterialExtension} from '../materials' import {MaterialExtension} from '../materials'
import {generateUUID} from '../three/utils/misc'
import {generateUUID, isInScene} from '../three'




export class MaterialManager<T = ''> extends EventDispatcher<BaseEvent, T> { export class MaterialManager<T = ''> extends EventDispatcher<BaseEvent, T> {
const mat = e.target const mat = e.target
if (!mat || mat.assetType !== 'material') return if (!mat || mat.assetType !== 'material') return
mat.setDirty() mat.setDirty()
const maps = this._getMapsForMaterial(mat)
maps.forEach(map=>{
const mats = map.userData.__appliedMaterials!
mats?.delete(mat)
if (!mats || map.userData.disposeOnIdle === false) return
if (mats.size === 0) map.dispose()
})
this.unregisterMaterial(mat)
this._getMapsForMaterial(mat)
.forEach(map=>
!map.isRenderTargetTexture && map.userData.disposeOnIdle !== false &&
map.dispose && !isInScene(map) && map.dispose())
// this.unregisterMaterial(mat) // todo
} }


private _materialMaps = new Map<string, Set<ITexture>>() private _materialMaps = new Map<string, Set<ITexture>>()

+ 12
- 1
src/core/IGeometry.ts Näytä tiedosto

import {IImportResultUserData} from '../assetmanager' import {IImportResultUserData} from '../assetmanager'


export interface IGeometryUserData extends IImportResultUserData{ export interface IGeometryUserData extends IImportResultUserData{
disposeOnIdle?: boolean // default: true
/**
* Automatically dispose geometry when not used by any object in the scene
* @default true
*/
disposeOnIdle?: boolean
// [key: string]: any // commented for noe // [key: string]: any // commented for noe
} }
export interface IGeometry<Attributes extends NormalOrGLBufferAttributes = NormalBufferAttributes> extends BufferGeometry<Attributes, IGeometryEvent, IGeometryEventTypes>, IUiConfigContainer { export interface IGeometry<Attributes extends NormalOrGLBufferAttributes = NormalBufferAttributes> extends BufferGeometry<Attributes, IGeometryEvent, IGeometryEventTypes>, IUiConfigContainer {
// Note: for userData: add _ in front of for private use, which is preserved while cloning but not serialisation, and __ for private use, which is not preserved while cloning and serialisation // Note: for userData: add _ in front of for private use, which is preserved while cloning but not serialisation, and __ for private use, which is not preserved while cloning and serialisation
userData: IGeometryUserData userData: IGeometryUserData


/**
* Disposes the geometry from the GPU.
* Set force to false if not sure the geometry is used by any object in the scene.
* // todo add check for visible in scene also? or is that overkill
* @param force - when true, same as three.js dispose. when false, only disposes if disposeOnIdle not false and not used by any object in the scene. default: true
*/
dispose(force?: boolean): void


// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
_uiConfig?: UiObjectConfig _uiConfig?: UiObjectConfig

+ 14
- 3
src/core/IMaterial.ts Näytä tiedosto

} }
export interface IMaterialUserData extends IImportResultUserData{ export interface IMaterialUserData extends IImportResultUserData{
uuid?: string // adding to userdata also, so that its saved in gltf uuid?: string // adding to userdata also, so that its saved in gltf

disposeOnIdle?: boolean // default: true
/**
* Automatically dispose material when not used by any object in the scene
* @default true
*/
disposeOnIdle?: boolean


renderToGBuffer?: boolean renderToGBuffer?: boolean
/** /**


inverseAlphaMap?: boolean // only for physical material right now inverseAlphaMap?: boolean // only for physical material right now


// [key: string]: any // commented for noe
[key: string]: any // commented for noe




// legacy, to be removed // legacy, to be removed
// Note: for userData: add _ in front of for private use, which is preserved while cloning but not serialisation, and __ for private use, which is not preserved while cloning and serialisation // Note: for userData: add _ in front of for private use, which is preserved while cloning but not serialisation, and __ for private use, which is not preserved while cloning and serialisation
userData: IMaterialUserData userData: IMaterialUserData


/**
* Disposes the material from the GPU.
* Set force to false if not sure the material is used by any object in the scene.
* // todo add check for visible in scene also? or is that overkill
* @param force - when true, same as three.js dispose. when false, only disposes if disposeOnIdle not false and not used by any object in the scene. default: true
*/
dispose(force?: boolean): void

// optional from subclasses, added here for autocomplete // optional from subclasses, added here for autocomplete
flatShading?: boolean flatShading?: boolean
map?: ITexture | null map?: ITexture | null

+ 15
- 2
src/core/IObject.ts Näytä tiedosto

* @deprecated * @deprecated
*/ */
dispose?: any dispose?: any
/**
* @deprecated
*/
setMaterial?: any setMaterial?: any
/**
* @deprecated
*/
setGeometry?: any setGeometry?: any
/**
* @deprecated
*/
setDirty?: any setDirty?: any


/** /**


objectProcessor?: IObjectProcessor objectProcessor?: IObjectProcessor


// eslint-disable-next-line @typescript-eslint/naming-convention
__disposed?: boolean
// __disposed?: boolean
/**
*
* @param removeFromParent - remove from parent. Default true
*/
dispose(removeFromParent?: boolean): void;


// region inherited type fixes // region inherited type fixes



+ 7
- 1
src/core/ITexture.ts Näytä tiedosto

import {IMaterial} from './IMaterial' import {IMaterial} from './IMaterial'
import {Event, Texture} from 'three' import {Event, Texture} from 'three'
import {ChangeEvent} from 'uiconfig.js' import {ChangeEvent} from 'uiconfig.js'
import {IRenderTarget} from '../rendering'


export interface ITextureUserData{ export interface ITextureUserData{
mimeType?: string mimeType?: string
embedUrlImagePreviews?: boolean embedUrlImagePreviews?: boolean
disposeOnIdle?: boolean // automatically dispose when added to a material and then not used in any material
/**
* Automatically dispose texture when not used by any material that's applied to some object in the scene.
* Works only after it's applied to a material once.
*/
disposeOnIdle?: boolean
__appliedMaterials?: Set<IMaterial> __appliedMaterials?: Set<IMaterial>
} }
export type ITextureEventTypes = 'dispose' | 'update' export type ITextureEventTypes = 'dispose' | 'update'


setDirty?(): void setDirty?(): void


_target?: IRenderTarget // for internal use only. refers to the render target that this texture is attached to
} }


export function upgradeTexture(this: ITexture) { export function upgradeTexture(this: ITexture) {

+ 14
- 3
src/core/geometry/iGeometryCommons.ts Näytä tiedosto

import {UiObjectConfig} from 'uiconfig.js' import {UiObjectConfig} from 'uiconfig.js'
import {IGeometry, IGeometrySetDirtyOptions} from '../IGeometry' import {IGeometry, IGeometrySetDirtyOptions} from '../IGeometry'
import {toIndexedGeometry} from '../../three'
import {isInScene, toIndexedGeometry} from '../../three'
import {BufferGeometry} from 'three'


export const iGeometryCommons = { export const iGeometryCommons = {
setDirty: function(this: IGeometry, options?: IGeometrySetDirtyOptions): void { setDirty: function(this: IGeometry, options?: IGeometrySetDirtyOptions): void {
refreshUi: function(this: IGeometry) { refreshUi: function(this: IGeometry) {
this.uiConfig?.uiRefresh?.(true, 'postFrame', 1) this.uiConfig?.uiRefresh?.(true, 'postFrame', 1)
}, },
dispose: (superDispose: BufferGeometry['dispose']): IGeometry['dispose'] =>
function(this: IGeometry, force = true): void {
if (!force && (this.userData.disposeOnIdle === false || isInScene(this))) return
superDispose.call(this)
},
upgradeGeometry: upgradeGeometry, upgradeGeometry: upgradeGeometry,
makeUiConfig: function(this: IGeometry): UiObjectConfig { makeUiConfig: function(this: IGeometry): UiObjectConfig {
if (this.uiConfig) return this.uiConfig if (this.uiConfig) return this.uiConfig
}, },
} }


export function upgradeGeometry(this: IGeometry) {
function upgradeGeometry(this: IGeometry) {
if (this.assetType === 'geometry') return // already upgraded if (this.assetType === 'geometry') return // already upgraded
if (!this.isBufferGeometry) { if (!this.isBufferGeometry) {
console.error('Material is not a this', this)
console.error('Geometry is not a this', this)
return return
} }
this.assetType = 'geometry' this.assetType = 'geometry'

this.dispose = iGeometryCommons.dispose(this.dispose)

if (!this.setDirty) this.setDirty = iGeometryCommons.setDirty if (!this.setDirty) this.setDirty = iGeometryCommons.setDirty
if (!this.refreshUi) this.refreshUi = iGeometryCommons.refreshUi if (!this.refreshUi) this.refreshUi = iGeometryCommons.refreshUi

if (!this.appliedMeshes) this.appliedMeshes = new Set() if (!this.appliedMeshes) this.appliedMeshes = new Set()
if (!this.userData) this.userData = {} if (!this.userData) this.userData = {}
this.uiConfig = iGeometryCommons.makeUiConfig.call(this) this.uiConfig = iGeometryCommons.makeUiConfig.call(this)

// todo: dispose uiconfig on geometry dispose // todo: dispose uiconfig on geometry dispose


// todo: add serialization? // todo: add serialization?

+ 1
- 0
src/core/material/PhysicalMaterial.ts Näytä tiedosto



readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)} clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
dispatchEvent(event: IMaterialEvent): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)} dispatchEvent(event: IMaterialEvent): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}



+ 3
- 0
src/core/material/ShaderMaterial2.ts Näytä tiedosto



readonly appliedMeshes: Set<any> = new Set() readonly appliedMeshes: Set<any> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
dispatchEvent(event: IMaterialEvent): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}


readonly isRawShaderMaterial: boolean readonly isRawShaderMaterial: boolean



+ 1
- 0
src/core/material/UnlitMaterial.ts Näytä tiedosto



readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)} clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
dispatchEvent(event: IMaterialEvent): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)} dispatchEvent(event: IMaterialEvent): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}



+ 7
- 0
src/core/material/iMaterialCommons.ts Näytä tiedosto

import {MaterialExtender, MaterialExtension} from '../../materials' import {MaterialExtender, MaterialExtension} from '../../materials'
import {IScene} from '../IScene' import {IScene} from '../IScene'
import {IMaterial, IMaterialEvent, IMaterialSetDirtyOptions} from '../IMaterial' import {IMaterial, IMaterialEvent, IMaterialSetDirtyOptions} from '../IMaterial'
import {isInScene} from '../../three'


/** /**
* Map of all material properties and their default values in three.js - Material.js * Map of all material properties and their default values in three.js - Material.js
this.setDirty?.() this.setDirty?.()
return this return this
}, },
dispose: (superDispose: Material<any, any>['dispose']): IMaterial['dispose'] =>
function(this: IMaterial, force = true): void {
if (!force && (this.userData.disposeOnIdle === false || isInScene(this))) return
superDispose.call(this)
},
clone: (superClone: Material<any, any>['clone']): IMaterial['clone'] => clone: (superClone: Material<any, any>['clone']): IMaterial['clone'] =>
function(this: IMaterial): IMaterial { function(this: IMaterial): IMaterial {
if (!this.userData.cloneId) { if (!this.userData.cloneId) {
if (this.assetType === 'material') return this // already upgraded if (this.assetType === 'material') return this // already upgraded
this.assetType = 'material' this.assetType = 'material'
this.setValues = iMaterialCommons.setValues(this.setValues) this.setValues = iMaterialCommons.setValues(this.setValues)
this.dispose = iMaterialCommons.dispose(this.dispose)
this.clone = iMaterialCommons.clone(this.clone) this.clone = iMaterialCommons.clone(this.clone)
this.dispatchEvent = iMaterialCommons.dispatchEvent(this.dispatchEvent) this.dispatchEvent = iMaterialCommons.dispatchEvent(this.dispatchEvent)



+ 69
- 70
src/core/object/iObjectCommons.ts Näytä tiedosto

import {IGeometry, IGeometryEvent} from '../IGeometry' import {IGeometry, IGeometryEvent} from '../IGeometry'
import {Box3B} from '../../three' import {Box3B} from '../../three'
import {makeIObject3DUiConfig} from './IObjectUi' import {makeIObject3DUiConfig} from './IObjectUi'
import {upgradeGeometry} from '../geometry/iGeometryCommons'
import {iGeometryCommons} from '../geometry/iGeometryCommons'
import {iMaterialCommons} from '../material/iMaterialCommons' import {iMaterialCommons} from '../material/iMaterialCommons'


export const iObjectCommons = { export const iObjectCommons = {
if (!mat) continue if (!mat) continue
if (mat.appliedMeshes) { if (mat.appliedMeshes) {
mat.appliedMeshes.delete(this) mat.appliedMeshes.delete(this)
if (mat.userData && mat.appliedMeshes?.size === 0 && mat.userData.disposeOnIdle !== false)
mat.dispose() // this will dispose textures(if they are idle) if the material is registered in the material manager
// if (mat.userData && mat.appliedMeshes?.size === 0 && mat.userData.disposeOnIdle !== false)
mat.dispose(false) // this will dispose textures(if they are idle) if the material is registered in the material manager
} }
} }


this._onGeometryUpdate && geom.removeEventListener('geometryUpdate', this._onGeometryUpdate) this._onGeometryUpdate && geom.removeEventListener('geometryUpdate', this._onGeometryUpdate)
if (geom.appliedMeshes) { if (geom.appliedMeshes) {
geom.appliedMeshes.delete(this) geom.appliedMeshes.delete(this)
if (geom.userData && geom.appliedMeshes.size === 0 && geom.userData.disposeOnIdle !== false) geom.dispose()
geom.dispose(false)
} }
} }
if (geometry) { if (geometry) {
if (!geometry.assetType) { if (!geometry.assetType) {
// console.error('Geometry not upgraded') // console.error('Geometry not upgraded')
upgradeGeometry.call(geometry)
iGeometryCommons.upgradeGeometry.call(geometry)
} }
} }
this._currentGeometry = geometry || null this._currentGeometry = geometry || null
return superAdd.call(this, ...args) return superAdd.call(this, ...args)
}, },
dispose: (superDispose?: IObject3D['dispose']) => dispose: (superDispose?: IObject3D['dispose']) =>
function(this: IObject3D): void {
this.dispatchEvent({type: 'dispose', bubbleToParent: false})

if (this.__disposed) {
console.warn('Object already disposed', this)
return
function(this: IObject3D, removeFromParent = true): void {
if (removeFromParent && this.parent) {
this.removeFromParent()
delete this.parentRoot
} }
this.__disposed = true


// this is first so that the leaf children are removed from parent first, removed event will be fired depth first
for (const c of [...this.children]) c?.dispose?.()
this.children = []
if (this.parent) this.removeFromParent()
this.dispatchEvent({type: 'dispose', bubbleToParent: false})


// if (this.__disposed) {
// console.warn('Object already disposed', this)
// return
// }
// this.__disposed = true


delete this.parentRoot
// safeSetProperty(this, 'modelObject', undefined, true) // in-case modelObject is just a getter.
this.userData = {} // todo: clear only our userdata and maybe any private variables?
for (const c of [...this.children]) c?.dispose && c.dispose(false) // not removing the children from parent to preserve hierarchy
// this.children = []


// this.uiConfig?.dispose?.() // todo: make uiConfig.dispose // this.uiConfig?.dispose?.() // todo: make uiConfig.dispose
this.uiConfig = undefined


superDispose?.call(this) superDispose?.call(this)
}, },
function upgradeObject3D(this: IObject3D, parent?: IObject3D|undefined, objectProcessor?: IObjectProcessor): void { // parent is the root Object3DModel. function upgradeObject3D(this: IObject3D, parent?: IObject3D|undefined, objectProcessor?: IObjectProcessor): void { // parent is the root Object3DModel.
if (!this) return if (!this) return
// console.log('upgradeObject3D', this, parent, objectProcessor) // console.log('upgradeObject3D', this, parent, objectProcessor)
if (this.__disposed) {
console.warn('re-init/re-add disposed object, things might not work as intended', this)
delete this.__disposed
}
// if (this.__disposed) {
// console.warn('re-init/re-add disposed object, things might not work as intended', this)
// delete this.__disposed
// }
if (!this.userData) this.userData = {} if (!this.userData) this.userData = {}
this.userData.uuid = this.uuid this.userData.uuid = this.uuid




if (parent) this.parentRoot = parent if (parent) this.parentRoot = parent


const oldFunctions = {
dispatchEvent: this.dispatchEvent,
clone: this.clone,
copy: this.copy,
add: this.add,
dispose: this.dispose,
}
this.addEventListener('dispose', () => Object.assign(this, oldFunctions)) // todo: is this required?
// const oldFunctions = {
// dispatchEvent: this.dispatchEvent,
// clone: this.clone,
// copy: this.copy,
// add: this.add,
// dispose: this.dispose,
// }
// this.addEventListener('dispose', () => Object.assign(this, oldFunctions)) // todo: is this required?


// typed because of type-checking // typed because of type-checking
this.dispatchEvent = iObjectCommons.dispatchEvent(this.dispatchEvent) this.dispatchEvent = iObjectCommons.dispatchEvent(this.dispatchEvent)
this.addEventListener('added', iObjectCommons.eventCallbacks.onAddedToParent) this.addEventListener('added', iObjectCommons.eventCallbacks.onAddedToParent)
this.addEventListener('removed', iObjectCommons.eventCallbacks.onRemovedFromParent) this.addEventListener('removed', iObjectCommons.eventCallbacks.onRemovedFromParent)


this.addEventListener('dispose', ()=>{
this.removeEventListener('added', iObjectCommons.eventCallbacks.onAddedToParent)
this.removeEventListener('removed', iObjectCommons.eventCallbacks.onRemovedFromParent)
})
// this.addEventListener('dispose', ()=>{
// this.removeEventListener('added', iObjectCommons.eventCallbacks.onAddedToParent)
// this.removeEventListener('removed', iObjectCommons.eventCallbacks.onRemovedFromParent)
// })


if ((this.isMesh || this.isLine) && !this.userData.__meshSetup) { if ((this.isMesh || this.isLine) && !this.userData.__meshSetup) {
this.userData.__meshSetup = true this.userData.__meshSetup = true
} }


this.addEventListener('dispose', ()=>{ this.addEventListener('dispose', ()=>{
if (this.material) {
// const oldMats = Array.isArray(this.material) ? [...(this.material as IMaterial[])] : [this.material!]
this.material = undefined // this will dispose material if not used by other meshes
// delete this.material
// for (const oldMat of oldMats) {
// if (oldMat && oldMat.userData && oldMat.appliedMeshes?.size === 0 && oldMat.userData.disposeOnIdle !== false) oldMat.dispose()
// }
}
if (this.geometry) {
// const oldGeom = this.geometry
this.geometry = undefined // this will dispose geometry if not used by other meshes
// delete this.geometry
// if (oldGeom && oldGeom.userData && oldGeom.appliedMeshes?.size === 0 && oldGeom.userData.disposeOnIdle !== false) oldGeom.dispose()
}


delete this._onGeometryUpdate
(this.materials || [<IMaterial> this.material]).forEach(m => m?.dispose(false))
this.geometry?.dispose(false)

// if (this.material) {
// // const oldMats = Array.isArray(this.material) ? [...(this.material as IMaterial[])] : [this.material!]
// this.material = undefined // this will dispose material if not used by other meshes
// // delete this.material
// // for (const oldMat of oldMats) {
// // if (oldMat && oldMat.userData && oldMat.appliedMeshes?.size === 0 && oldMat.userData.disposeOnIdle !== false) oldMat.dispose()
// // }
// }
// if (this.geometry) {
// // const oldGeom = this.geometry
// this.geometry = undefined // this will dispose geometry if not used by other meshes
// // delete this.geometry
// // if (oldGeom && oldGeom.userData && oldGeom.appliedMeshes?.size === 0 && oldGeom.userData.disposeOnIdle !== false) oldGeom.dispose()
// }
//
// delete this._onGeometryUpdate
}) })


} }
for (const c of children) upgradeObject3D.call(c, this) for (const c of children) upgradeObject3D.call(c, this)


// region Legacy // region Legacy
if (this.userData.dispose) console.warn('userData.dispose already defined')
this.userData.dispose = () => {

// eslint-disable-next-line deprecation/deprecation
!this.userData.dispose && (this.userData.dispose = () => {
console.warn('userData.dispose is deprecated, use dispose directly') console.warn('userData.dispose is deprecated, use dispose directly')
this.dispose?.()
}
if (!this.modelObject) {
Object.defineProperty(this, 'modelObject', {
get: ()=>{
console.error('modelObject is deprecated, use object directly')
return this
},
})
}
if (!this.userData.setDirty)
this.userData.setDirty = (e: any)=>{
console.error('object.userData.setDirty is deprecated, use object.setDirty directly')
this.setDirty?.(e)
}
this.dispose && this.dispose()
})
// eslint-disable-next-line deprecation/deprecation
!this.modelObject && Object.defineProperty(this, 'modelObject', {
get: ()=>{
console.error('modelObject is deprecated, use object directly')
return this
},
})
// eslint-disable-next-line deprecation/deprecation
!this.userData.setDirty && (this.userData.setDirty = (e: any)=>{
console.error('object.userData.setDirty is deprecated, use object.setDirty directly')
this.setDirty?.(e)
})


// endregion // endregion


// if (!this.objectProcessor) console.warn('objectProcessor not set for', this)
// else
this.objectProcessor?.processObject(this) this.objectProcessor?.processObject(this)


} }

+ 22
- 0
src/three/utils/misc.ts Näytä tiedosto

import {BufferGeometry, MathUtils} from 'three' import {BufferGeometry, MathUtils} from 'three'
import {mergeVertices} from 'three/examples/jsm/utils/BufferGeometryUtils.js' import {mergeVertices} from 'three/examples/jsm/utils/BufferGeometryUtils.js'
import {IGeometry, IMaterial, IObject3D, IScene, ITexture} from '../../core'


/** /**
* Convert geometry to BufferGeometry with indexed attributes. * Convert geometry to BufferGeometry with indexed attributes.
export function generateUUID() { export function generateUUID() {
return MathUtils.generateUUID() return MathUtils.generateUUID()
} }

/**
* Check if a single or multiple object/geometry/material/texture is in the scene.
* This is used internally to check if a material is used by any object in the scene, and if not, it can be disposed.
* @param sceneObj
*/
export function isInScene(...sceneObj: (IGeometry|IMaterial|IObject3D|ITexture)[]): boolean {
if (sceneObj.length > 1) return sceneObj.some((a)=>isInScene(a))
const o = sceneObj[0]
if ((<ITexture>o).isTexture) return Array.from((<ITexture>o).userData.__appliedMaterials || []).some((m) => isInScene(m)) ?? false

const objects =
(<IObject3D>o).isObject3D ? [<IObject3D>o] :
(<IGeometry|IMaterial>o).appliedMeshes
for (const obj of objects) {
let inScene = false
obj.traverseAncestors((ob: IObject3D) => (<IScene>ob).isScene && (inScene = true))
if (inScene) return true
}
return false
}

Loading…
Peruuta
Tallenna