import { AbstractAssetTask , AssetsManager , IAssetsProgressEvent , TextureAssetTask , } from '@babylonjs/core/Misc/assetsManager.js' ; import { EventState } from '@babylonjs/core/Misc/observable.js' ; import { Scene } from '@babylonjs/core/scene.js' ; import { Nullable } from '@babylonjs/core/types.js' ; import React , { useContext , useState } from 'react' ; import { useScene } from '../scene' ; export enum TaskType { Binary = 'Binary' , Mesh = 'Mesh' , Texture = 'Texture' } export type BinaryTask = { taskType : TaskType . Binary ; name : string ; url : string ; } ; export type MeshTask = { taskType : TaskType . Mesh ; name : string ; meshesNames ? : any ; rootUrl : string ; sceneFilename : string ; } ; export type TextureTask = { taskType : TaskType . Texture ; name : string ; url : string ; noMipmap ? : boolean ; invertY ? : boolean ; samplingMode ? : number ; } ; export type Task = BinaryTask | MeshTask | TextureTask ; export type AssetManagerContextType = { updateProgress : ( progress : AssetManagerProgressType ) => void ; lastProgress ? : AssetManagerProgressType ; } | undefined ; export const AssetManagerContext = React . createContext < AssetManagerContextType > ( undefined ) ; export type AssetManagerProgressType = { eventData : IAssetsProgressEvent ; eventState : EventState ; } | undefined ; export type AssetManagerContextProviderProps = { startProgress ? : AssetManagerProgressType ; children : React . ReactNode ; } ; export const AssetManagerContextProvider = ( props ) => { const [ progress , setProgress ] = useState < AssetManagerProgressType > ( undefined ) ; return ( < AssetManagerContext . Provider value = { { lastProgress : progress , updateProgress : setProgress } } > { props . children } < /AssetManagerContext.Provider>); } ; export type AssetManagerOptions = { useDefaultLoadingScreen ? : boolean ; reportProgress ? : boolean ; scene ? : Scene ; } ; const getTaskKey = ( task ) : string | undefined => { switch ( task . taskType ) { case TaskType . Binary : return `binary:${ task . url }` ; case TaskType . Mesh : return `mesh:${ task . rootUrl }/${ task . sceneFilename }` ; case TaskType . Texture : return `texture:${ task . url }` ; default : throw new Error ( `Unknown task type: ${ JSON . stringify ( task ) }` ) ; } } ; type AssetManagerResult = { tasks : AbstractAssetTask [ ] ; taskNameMap : Record < string , AbstractAssetTask > ; } ; /**
 * This has limited functionality and only works for limited asset types.
 *
 * This is an experimental API and *WILL* change.
 * TODO: function signature is not any.
 */ const useAssetManagerWithCache = ( ) : ( ( tasks : Task [ ] , options ? : AssetManagerOptions ) => AssetManagerResult ) => { let suspenseCache = { } ; let suspenseScene = null ; let tasksCompletedCache = { } ; return ( tasks , options ? ) : AssetManagerResult => { const hookScene = useScene ( ) ; const opts = options || { useDefaultLoadingScreen : false , } ; if ( opts . scene === undefined && hookScene === null ) { throw new Error ( 'useAssetManager can only be used inside a Scene component (or pass scene as an option)' ) ; } const assetManagerContext = useContext < AssetManagerContextType > ( AssetManagerContext ) ; const scene = opts . scene || hookScene ! ; if ( suspenseScene == null ) { suspenseScene = scene ; } else { if ( suspenseScene !== scene ) { suspenseCache = { } ; tasksCompletedCache = { } ; suspenseScene = scene ; } } const createGetAssets = ( tasks ) : ( ( ) => AssetManagerResult ) => { if ( ! Array . isArray ( tasks ) ) { throw new Error ( 'Asset Manager tasks must be an array' ) ; } const newRequests = new Map < AbstractAssetTask , Task > ( ) ; const assetManager = new AssetsManager ( scene ) ; const cachedTasks = [ ] ; tasks . forEach ( ( task ) => { const key = getTaskKey ( task ) ; if ( key !== undefined && suspenseCache [ key ] ) { cachedTasks . push ( suspenseCache [ key ] ) ; } else { switch ( task . taskType ) { case TaskType . Binary : const binaryTask = assetManager . addBinaryFileTask ( task . name , task . url ) ; newRequests . set ( binaryTask , task ) ; break ; case TaskType . Mesh : const meshTask = assetManager . addMeshTask ( task . name , task . meshesNames , task . rootUrl , task . sceneFilename ) ; newRequests . set ( meshTask , task ) ; break ; case TaskType . Texture : const textureTask = assetManager . addTextureTask ( task . name , task . url , task . noMipmap , task . invertY , task . samplingMode ) ; newRequests . set ( textureTask , task ) ; break ; default : throw new Error ( `Only binary/mesh supported currently.  'taskType' found on ${ JSON . stringify ( task ) }` ) ; } } } ) ; const createResultFromTasks = ( tasks ) : AssetManagerResult => { const taskNameMap = tasks . reduce < Record < string , AbstractAssetTask > > ( ( prev , cur ) => { prev [ cur . name ] = cur ; return prev ; } , { } ) ; return { tasks , taskNameMap } ; } ; const taskPromise = tasks . length === cachedTasks . length ? new Promise < AssetManagerResult > ( ( resolve ) => resolve ( createResultFromTasks ( cachedTasks ) ) ) : new Promise < AssetManagerResult > ( ( resolve , reject ) => { let failed = false ; assetManager . useDefaultLoadingScreen = opts . useDefaultLoadingScreen ?? false ; assetManager . onFinish = ( tasks ) => { tasks . forEach ( ( task ) => { if ( newRequests . has ( task ) ) { const originalTask = newRequests . get ( task ) ! ; const key = getTaskKey ( originalTask ) ! ; tasksCompletedCache [ key ] = task ; } } ) ; if ( failed === false ) { const result = createResultFromTasks ( tasks . concat ( cachedTasks ) ) ; resolve ( result ) ; } } ; if ( opts . reportProgress !== false && assetManagerContext !== undefined ) { assetManager . onProgressObservable . add ( ( eventData , eventState ) => { assetManagerContext ! . updateProgress ( { eventData , eventState } ) ; } ) ; } assetManager . onTaskError = ( task ) => { failed = true ; reject ( `Failed task ${ task . errorObject ? task . errorObject . message ?? `no error message on '${ task . name }'` : task . name }` ) ; } ; assetManager . load ( ) ; } ) ; let result ; let error = null ; let suspender = ( async ( ) => { try { result = await taskPromise ; } catch ( e ) { error = e as Error ; } finally { suspender = null ; } } ) ( ) ; const getAssets = ( ) => { if ( suspender ) { throw suspender ; } if ( error !== null ) { throw error ; } return result ; } ; return getAssets ; } ; const key = JSON . stringify ( tasks ) ; if ( suspenseCache [ key ] === undefined ) { suspenseCache [ key ] = createGetAssets ( tasks ) ; } return suspenseCache [ key ] ( ) ; } ; } ; export const useAssetManager = useAssetManagerWithCache ( ) ;