import { AnimationGroup } from '@babylonjs/core/Animations/animationGroup.js' ; import { Skeleton } from '@babylonjs/core/Bones/skeleton.js' ; import { ISceneLoaderPlugin , ISceneLoaderPluginAsync , ISceneLoaderProgressEvent , SceneLoader , } from '@babylonjs/core/Loading/sceneLoader.js' ; import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh.js' ; import { Mesh } from '@babylonjs/core/Meshes/mesh.js' ; import { IParticleSystem } from '@babylonjs/core/Particles/IParticleSystem.js' ; import { Scene } from '@babylonjs/core/scene.js' ; import { Nullable } from '@babylonjs/core/types.js' ; import React , { useContext , useState } from 'react' ; import { CreatedInstance } from '../../CreatedInstance' ; import { FiberMesh } from '../../generatedCode' ; import { FiberMeshProps } from '../../generatedProps' ; import { PropertyUpdate , PropsHandler } from '../../PropsHandler' ; import { applyUpdateToInstance } from '../../UpdateInstance' ; import { useScene } from '../scene' ; import { ILoadedModel , LoadedModel , LoaderStatus } from './loadedModel' ; export type SceneLoaderContextType = { updateProgress : ( progress : ISceneLoaderProgressEvent ) => void ; lastProgress ? : Nullable < ISceneLoaderProgressEvent > ; } | undefined ; export const SceneLoaderContext = React . createContext < SceneLoaderContextType > ( undefined ) ; export type SceneLoaderContextProviderProps = { startProgress ? : ISceneLoaderProgressEvent ; children : React . ReactNode ; } ; export const SceneLoaderContextProvider = ( props ) => { const [ progress , setProgress ] = useState < Nullable < ISceneLoaderProgressEvent > > ( null ) ; return ( < SceneLoaderContext . Provider value = { { lastProgress : progress , updateProgress : setProgress } } > { props . children } < /SceneLoaderContext.Provider>); } ; export type SceneLoaderOptions = { /**
     * An array of mesh names, a single mesh name, or empty string for all meshes that filter what meshes are imported
     */ meshNames ? : any ; /**
     * set that all meshes receive shadows.
     * Defaults to false.
     */ receiveShadows ? : boolean ; /**
     * Scale entire model within these square bounds
     * Defaults to no scaling.
     */ scaleToDimension ? : number ; /**
     * Always select root mesh as active.
     * Defaults to false.
     */ alwaysSelectAsActiveMesh ? : boolean ; /**
     * SceneLoader progress events are set on context provider (when available).
     * Defaults to false.
     */ reportProgress ? : boolean ; /**
     * Not needed if you are within a SceneContext.
     */ scene ? : Scene ; /**
     * Access to loaded model as soon as it is loaded, so it provides a way to hide or scale the meshes before the first render.
     */ onModelLoaded ? : ( loadedModel : ILoadedModel ) => void ; /**
     * Raw progress event for SceneLoader
     */ onLoadProgress ? : ( event : ISceneLoaderProgressEvent ) => void ; /**
     * Called if SceneLoader returns an error.
     */ onModelError ? : ( model : ILoadedModel ) => void ; } ; /**
 * A cached version of SceneLoader with a Suspense cache.
 */ const useSceneLoaderWithCache = ( ) : ( ( rootUrl : string , sceneFilename : string , pluginExtension ? : string , options ? : SceneLoaderOptions , initialProps ? : FiberMeshProps ) => LoadedModel ) => { let suspenseCache = { } ; let suspenseScene = null ; return ( rootUrl , sceneFilename , pluginExtension ? , options ? , initialProps ? ) : LoadedModel => { const opts = options || { } ; const hookScene = useScene ( ) ; if ( opts . scene === undefined && hookScene === null ) { throw new Error ( 'useSceneLoader can only be used inside a Scene component (or pass scene as an option)' ) ; } const scene = opts . scene || hookScene ! ; if ( suspenseScene == null ) { suspenseScene = scene ; } else { if ( suspenseScene !== scene ) { suspenseCache = { } ; suspenseScene = scene ; } } const suspenseKey = `${ rootUrl }/${ sceneFilename }` ; const sceneLoaderContext = useContext < SceneLoaderContextType > ( SceneLoaderContext ) ; const createSceneLoader = ( ) : ( ( ) => LoadedModel ) => { const taskPromise = new Promise < LoadedModel > ( ( resolve , reject ) => { let loadedModel = new LoadedModel ( ) ; loadedModel . status = LoaderStatus . Loading ; let loader = SceneLoader . ImportMesh ( opts . meshNames , rootUrl , sceneFilename , scene , ( meshes , particleSystems , skeletons , animationGroups ) : void => { loadedModel . rootMesh = new AbstractMesh ( sceneFilename + '-root-model' , scene ) ; if ( opts . alwaysSelectAsActiveMesh === true ) { loadedModel . rootMesh . alwaysSelectAsActiveMesh = true ; } loadedModel . meshes = [ ] ; meshes . forEach ( ( mesh ) => { loadedModel . meshes ! . push ( mesh ) ; if ( ! mesh . parent ) { mesh . parent = loadedModel . rootMesh ! ; } if ( opts . receiveShadows === true ) { mesh . receiveShadows = true ; } } ) ; loadedModel . particleSystems = particleSystems ; loadedModel . skeletons = skeletons ; loadedModel . animationGroups = animationGroups ; loadedModel . status = LoaderStatus . Loaded ; if ( opts . scaleToDimension ) { loadedModel . scaleTo ( opts . scaleToDimension ) ; } if ( initialProps ) { const initPayload = [ ] ; new FiberMesh ( ) . getPropsHandlers ( ) . forEach ( ( propHandler ) => { const handlerUpdates = propHandler . getPropertyUpdates ( { } , initialProps ) ; if ( handlerUpdates !== null ) { initPayload . push ( ... handlerUpdates ) ; } } ) ; if ( initPayload . length > 0 ) { initPayload . forEach ( ( update ) => { applyUpdateToInstance ( { hostInstance : loadedModel . rootMesh } as CreatedInstance < Mesh > , update ) ; } ) ; } } if ( options ?. onModelLoaded ) { options . onModelLoaded ( loadedModel ) ; } const originalDispose = loadedModel . dispose ; loadedModel . dispose = ( ) => { suspenseCache [ suspenseKey ] = undefined ; originalDispose . call ( loadedModel ) ; } ; resolve ( loadedModel ) ; } , ( event ) : void => { if ( opts . reportProgress === true && sceneLoaderContext !== undefined ) { sceneLoaderContext ! . updateProgress ( event ) ; } if ( opts . onLoadProgress ) { opts . onLoadProgress ( event ) ; } } , ( _ , message , exception ? ) : void => { if ( opts . onModelError ) { opts . onModelError ( loadedModel ) ; } reject ( exception ?? message ) ; } , pluginExtension ) ; if ( loader ) { loadedModel . loaderName = loader . name ; } else { loadedModel . loaderName = 'no loader found' ; } } ) ; 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 ; } ; if ( suspenseCache [ suspenseKey ] === undefined ) { suspenseCache [ suspenseKey ] = createSceneLoader ( ) ; } return suspenseCache [ suspenseKey ] ! ( ) ; } ; } ; export const useSceneLoader = useSceneLoaderWithCache ( ) ;