assetsManager.ts 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  1. import { Scene } from "../scene";
  2. import { AbstractMesh } from "../Meshes/abstractMesh";
  3. import { IParticleSystem } from "../Particles/IParticleSystem";
  4. import { Skeleton } from "../Bones/skeleton";
  5. import { SceneLoader } from "../Loading/sceneLoader";
  6. import { Tools } from "./tools";
  7. import { Observable } from "./observable";
  8. import { BaseTexture } from "../Materials/Textures/baseTexture";
  9. import { Texture } from "../Materials/Textures/texture";
  10. import { CubeTexture } from "../Materials/Textures/cubeTexture";
  11. import { HDRCubeTexture } from "../Materials/Textures/hdrCubeTexture";
  12. import { EquiRectangularCubeTexture } from "../Materials/Textures/equiRectangularCubeTexture";
  13. import { Logger } from "../Misc/logger";
  14. import { AnimationGroup } from '../Animations/animationGroup';
  15. /**
  16. * Defines the list of states available for a task inside a AssetsManager
  17. */
  18. export enum AssetTaskState {
  19. /**
  20. * Initialization
  21. */
  22. INIT,
  23. /**
  24. * Running
  25. */
  26. RUNNING,
  27. /**
  28. * Done
  29. */
  30. DONE,
  31. /**
  32. * Error
  33. */
  34. ERROR
  35. }
  36. /**
  37. * Define an abstract asset task used with a AssetsManager class to load assets into a scene
  38. */
  39. export abstract class AbstractAssetTask {
  40. /**
  41. * Callback called when the task is successful
  42. */
  43. public onSuccess: (task: any) => void;
  44. /**
  45. * Callback called when the task is not successful
  46. */
  47. public onError: (task: any, message?: string, exception?: any) => void;
  48. /**
  49. * Creates a new AssetsManager
  50. * @param name defines the name of the task
  51. */
  52. constructor(
  53. /**
  54. * Task name
  55. */public name: string) {
  56. }
  57. private _isCompleted = false;
  58. private _taskState = AssetTaskState.INIT;
  59. private _errorObject: { message?: string; exception?: any; };
  60. /**
  61. * Get if the task is completed
  62. */
  63. public get isCompleted(): boolean {
  64. return this._isCompleted;
  65. }
  66. /**
  67. * Gets the current state of the task
  68. */
  69. public get taskState(): AssetTaskState {
  70. return this._taskState;
  71. }
  72. /**
  73. * Gets the current error object (if task is in error)
  74. */
  75. public get errorObject(): { message?: string; exception?: any; } {
  76. return this._errorObject;
  77. }
  78. /**
  79. * Internal only
  80. * @hidden
  81. */
  82. public _setErrorObject(message?: string, exception?: any) {
  83. if (this._errorObject) {
  84. return;
  85. }
  86. this._errorObject = {
  87. message: message,
  88. exception: exception
  89. };
  90. }
  91. /**
  92. * Execute the current task
  93. * @param scene defines the scene where you want your assets to be loaded
  94. * @param onSuccess is a callback called when the task is successfully executed
  95. * @param onError is a callback called if an error occurs
  96. */
  97. public run(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  98. this._taskState = AssetTaskState.RUNNING;
  99. this.runTask(scene, () => {
  100. this.onDoneCallback(onSuccess, onError);
  101. }, (msg, exception) => {
  102. this.onErrorCallback(onError, msg, exception);
  103. });
  104. }
  105. /**
  106. * Execute the current task
  107. * @param scene defines the scene where you want your assets to be loaded
  108. * @param onSuccess is a callback called when the task is successfully executed
  109. * @param onError is a callback called if an error occurs
  110. */
  111. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  112. throw new Error("runTask is not implemented");
  113. }
  114. /**
  115. * Reset will set the task state back to INIT, so the next load call of the assets manager will execute this task again.
  116. * This can be used with failed tasks that have the reason for failure fixed.
  117. */
  118. public reset() {
  119. this._taskState = AssetTaskState.INIT;
  120. }
  121. private onErrorCallback(onError: (message?: string, exception?: any) => void, message?: string, exception?: any) {
  122. this._taskState = AssetTaskState.ERROR;
  123. this._errorObject = {
  124. message: message,
  125. exception: exception
  126. };
  127. if (this.onError) {
  128. this.onError(this, message, exception);
  129. }
  130. onError();
  131. }
  132. private onDoneCallback(onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  133. try {
  134. this._taskState = AssetTaskState.DONE;
  135. this._isCompleted = true;
  136. if (this.onSuccess) {
  137. this.onSuccess(this);
  138. }
  139. onSuccess();
  140. } catch (e) {
  141. this.onErrorCallback(onError, "Task is done, error executing success callback(s)", e);
  142. }
  143. }
  144. }
  145. /**
  146. * Define the interface used by progress events raised during assets loading
  147. */
  148. export interface IAssetsProgressEvent {
  149. /**
  150. * Defines the number of remaining tasks to process
  151. */
  152. remainingCount: number;
  153. /**
  154. * Defines the total number of tasks
  155. */
  156. totalCount: number;
  157. /**
  158. * Defines the task that was just processed
  159. */
  160. task: AbstractAssetTask;
  161. }
  162. /**
  163. * Class used to share progress information about assets loading
  164. */
  165. export class AssetsProgressEvent implements IAssetsProgressEvent {
  166. /**
  167. * Defines the number of remaining tasks to process
  168. */
  169. public remainingCount: number;
  170. /**
  171. * Defines the total number of tasks
  172. */
  173. public totalCount: number;
  174. /**
  175. * Defines the task that was just processed
  176. */
  177. public task: AbstractAssetTask;
  178. /**
  179. * Creates a AssetsProgressEvent
  180. * @param remainingCount defines the number of remaining tasks to process
  181. * @param totalCount defines the total number of tasks
  182. * @param task defines the task that was just processed
  183. */
  184. constructor(remainingCount: number, totalCount: number, task: AbstractAssetTask) {
  185. this.remainingCount = remainingCount;
  186. this.totalCount = totalCount;
  187. this.task = task;
  188. }
  189. }
  190. /**
  191. * Define a task used by AssetsManager to load meshes
  192. */
  193. export class MeshAssetTask extends AbstractAssetTask {
  194. /**
  195. * Gets the list of loaded meshes
  196. */
  197. public loadedMeshes: Array<AbstractMesh>;
  198. /**
  199. * Gets the list of loaded particle systems
  200. */
  201. public loadedParticleSystems: Array<IParticleSystem>;
  202. /**
  203. * Gets the list of loaded skeletons
  204. */
  205. public loadedSkeletons: Array<Skeleton>;
  206. /**
  207. * Gets the list of loaded animation groups
  208. */
  209. public loadedAnimationGroups: Array<AnimationGroup>;
  210. /**
  211. * Callback called when the task is successful
  212. */
  213. public onSuccess: (task: MeshAssetTask) => void;
  214. /**
  215. * Callback called when the task is successful
  216. */
  217. public onError: (task: MeshAssetTask, message?: string, exception?: any) => void;
  218. /**
  219. * Creates a new MeshAssetTask
  220. * @param name defines the name of the task
  221. * @param meshesNames defines the list of mesh's names you want to load
  222. * @param rootUrl defines the root url to use as a base to load your meshes and associated resources
  223. * @param sceneFilename defines the filename of the scene to load from
  224. */
  225. constructor(
  226. /**
  227. * Defines the name of the task
  228. */
  229. public name: string,
  230. /**
  231. * Defines the list of mesh's names you want to load
  232. */
  233. public meshesNames: any,
  234. /**
  235. * Defines the root url to use as a base to load your meshes and associated resources
  236. */
  237. public rootUrl: string,
  238. /**
  239. * Defines the filename of the scene to load from
  240. */
  241. public sceneFilename: string) {
  242. super(name);
  243. }
  244. /**
  245. * Execute the current task
  246. * @param scene defines the scene where you want your assets to be loaded
  247. * @param onSuccess is a callback called when the task is successfully executed
  248. * @param onError is a callback called if an error occurs
  249. */
  250. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  251. SceneLoader.ImportMesh(this.meshesNames, this.rootUrl, this.sceneFilename, scene,
  252. (meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[]) => {
  253. this.loadedMeshes = meshes;
  254. this.loadedParticleSystems = particleSystems;
  255. this.loadedSkeletons = skeletons;
  256. this.loadedAnimationGroups = animationGroups;
  257. onSuccess();
  258. }, null, (scene, message, exception) => {
  259. onError(message, exception);
  260. }
  261. );
  262. }
  263. }
  264. /**
  265. * Define a task used by AssetsManager to load text content
  266. */
  267. export class TextFileAssetTask extends AbstractAssetTask {
  268. /**
  269. * Gets the loaded text string
  270. */
  271. public text: string;
  272. /**
  273. * Callback called when the task is successful
  274. */
  275. public onSuccess: (task: TextFileAssetTask) => void;
  276. /**
  277. * Callback called when the task is successful
  278. */
  279. public onError: (task: TextFileAssetTask, message?: string, exception?: any) => void;
  280. /**
  281. * Creates a new TextFileAssetTask object
  282. * @param name defines the name of the task
  283. * @param url defines the location of the file to load
  284. */
  285. constructor(
  286. /**
  287. * Defines the name of the task
  288. */
  289. public name: string,
  290. /**
  291. * Defines the location of the file to load
  292. */
  293. public url: string) {
  294. super(name);
  295. }
  296. /**
  297. * Execute the current task
  298. * @param scene defines the scene where you want your assets to be loaded
  299. * @param onSuccess is a callback called when the task is successfully executed
  300. * @param onError is a callback called if an error occurs
  301. */
  302. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  303. scene._loadFile(this.url, (data) => {
  304. this.text = data as string;
  305. onSuccess();
  306. }, undefined, false, false, (request, exception) => {
  307. if (request) {
  308. onError(request.status + " " + request.statusText, exception);
  309. }
  310. });
  311. }
  312. }
  313. /**
  314. * Define a task used by AssetsManager to load binary data
  315. */
  316. export class BinaryFileAssetTask extends AbstractAssetTask {
  317. /**
  318. * Gets the lodaded data (as an array buffer)
  319. */
  320. public data: ArrayBuffer;
  321. /**
  322. * Callback called when the task is successful
  323. */
  324. public onSuccess: (task: BinaryFileAssetTask) => void;
  325. /**
  326. * Callback called when the task is successful
  327. */
  328. public onError: (task: BinaryFileAssetTask, message?: string, exception?: any) => void;
  329. /**
  330. * Creates a new BinaryFileAssetTask object
  331. * @param name defines the name of the new task
  332. * @param url defines the location of the file to load
  333. */
  334. constructor(
  335. /**
  336. * Defines the name of the task
  337. */
  338. public name: string,
  339. /**
  340. * Defines the location of the file to load
  341. */
  342. public url: string) {
  343. super(name);
  344. }
  345. /**
  346. * Execute the current task
  347. * @param scene defines the scene where you want your assets to be loaded
  348. * @param onSuccess is a callback called when the task is successfully executed
  349. * @param onError is a callback called if an error occurs
  350. */
  351. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  352. scene._loadFile(this.url, (data) => {
  353. this.data = data as ArrayBuffer;
  354. onSuccess();
  355. }, undefined, true, true, (request, exception) => {
  356. if (request) {
  357. onError(request.status + " " + request.statusText, exception);
  358. }
  359. });
  360. }
  361. }
  362. /**
  363. * Define a task used by AssetsManager to load images
  364. */
  365. export class ImageAssetTask extends AbstractAssetTask {
  366. /**
  367. * Gets the loaded images
  368. */
  369. public image: HTMLImageElement;
  370. /**
  371. * Callback called when the task is successful
  372. */
  373. public onSuccess: (task: ImageAssetTask) => void;
  374. /**
  375. * Callback called when the task is successful
  376. */
  377. public onError: (task: ImageAssetTask, message?: string, exception?: any) => void;
  378. /**
  379. * Creates a new ImageAssetTask
  380. * @param name defines the name of the task
  381. * @param url defines the location of the image to load
  382. */
  383. constructor(
  384. /**
  385. * Defines the name of the task
  386. */
  387. public name: string,
  388. /**
  389. * Defines the location of the image to load
  390. */
  391. public url: string) {
  392. super(name);
  393. }
  394. /**
  395. * Execute the current task
  396. * @param scene defines the scene where you want your assets to be loaded
  397. * @param onSuccess is a callback called when the task is successfully executed
  398. * @param onError is a callback called if an error occurs
  399. */
  400. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  401. var img = new Image();
  402. Tools.SetCorsBehavior(this.url, img);
  403. img.onload = () => {
  404. this.image = img;
  405. onSuccess();
  406. };
  407. img.onerror = (err: string | Event): any => {
  408. onError("Error loading image", err);
  409. };
  410. img.src = this.url;
  411. }
  412. }
  413. /**
  414. * Defines the interface used by texture loading tasks
  415. */
  416. export interface ITextureAssetTask<TEX extends BaseTexture> {
  417. /**
  418. * Gets the loaded texture
  419. */
  420. texture: TEX;
  421. }
  422. /**
  423. * Define a task used by AssetsManager to load 2D textures
  424. */
  425. export class TextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<Texture> {
  426. /**
  427. * Gets the loaded texture
  428. */
  429. public texture: Texture;
  430. /**
  431. * Callback called when the task is successful
  432. */
  433. public onSuccess: (task: TextureAssetTask) => void;
  434. /**
  435. * Callback called when the task is successful
  436. */
  437. public onError: (task: TextureAssetTask, message?: string, exception?: any) => void;
  438. /**
  439. * Creates a new TextureAssetTask object
  440. * @param name defines the name of the task
  441. * @param url defines the location of the file to load
  442. * @param noMipmap defines if mipmap should not be generated (default is false)
  443. * @param invertY defines if texture must be inverted on Y axis (default is false)
  444. * @param samplingMode defines the sampling mode to use (default is Texture.TRILINEAR_SAMPLINGMODE)
  445. */
  446. constructor(
  447. /**
  448. * Defines the name of the task
  449. */
  450. public name: string,
  451. /**
  452. * Defines the location of the file to load
  453. */
  454. public url: string,
  455. /**
  456. * Defines if mipmap should not be generated (default is false)
  457. */
  458. public noMipmap?: boolean,
  459. /**
  460. * Defines if texture must be inverted on Y axis (default is false)
  461. */
  462. public invertY?: boolean,
  463. /**
  464. * Defines the sampling mode to use (default is Texture.TRILINEAR_SAMPLINGMODE)
  465. */
  466. public samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
  467. super(name);
  468. }
  469. /**
  470. * Execute the current task
  471. * @param scene defines the scene where you want your assets to be loaded
  472. * @param onSuccess is a callback called when the task is successfully executed
  473. * @param onError is a callback called if an error occurs
  474. */
  475. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  476. var onload = () => {
  477. onSuccess();
  478. };
  479. var onerror = (message?: string, exception?: any) => {
  480. onError(message, exception);
  481. };
  482. this.texture = new Texture(this.url, scene, this.noMipmap, this.invertY, this.samplingMode, onload, onerror);
  483. }
  484. }
  485. /**
  486. * Define a task used by AssetsManager to load cube textures
  487. */
  488. export class CubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<CubeTexture> {
  489. /**
  490. * Gets the loaded texture
  491. */
  492. public texture: CubeTexture;
  493. /**
  494. * Callback called when the task is successful
  495. */
  496. public onSuccess: (task: CubeTextureAssetTask) => void;
  497. /**
  498. * Callback called when the task is successful
  499. */
  500. public onError: (task: CubeTextureAssetTask, message?: string, exception?: any) => void;
  501. /**
  502. * Creates a new CubeTextureAssetTask
  503. * @param name defines the name of the task
  504. * @param url defines the location of the files to load (You have to specify the folder where the files are + filename with no extension)
  505. * @param extensions defines the extensions to use to load files (["_px", "_py", "_pz", "_nx", "_ny", "_nz"] by default)
  506. * @param noMipmap defines if mipmaps should not be generated (default is false)
  507. * @param files defines the explicit list of files (undefined by default)
  508. */
  509. constructor(
  510. /**
  511. * Defines the name of the task
  512. */
  513. public name: string,
  514. /**
  515. * Defines the location of the files to load (You have to specify the folder where the files are + filename with no extension)
  516. */
  517. public url: string,
  518. /**
  519. * Defines the extensions to use to load files (["_px", "_py", "_pz", "_nx", "_ny", "_nz"] by default)
  520. */
  521. public extensions?: string[],
  522. /**
  523. * Defines if mipmaps should not be generated (default is false)
  524. */
  525. public noMipmap?: boolean,
  526. /**
  527. * Defines the explicit list of files (undefined by default)
  528. */
  529. public files?: string[]) {
  530. super(name);
  531. }
  532. /**
  533. * Execute the current task
  534. * @param scene defines the scene where you want your assets to be loaded
  535. * @param onSuccess is a callback called when the task is successfully executed
  536. * @param onError is a callback called if an error occurs
  537. */
  538. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  539. var onload = () => {
  540. onSuccess();
  541. };
  542. var onerror = (message?: string, exception?: any) => {
  543. onError(message, exception);
  544. };
  545. this.texture = new CubeTexture(this.url, scene, this.extensions, this.noMipmap, this.files, onload, onerror);
  546. }
  547. }
  548. /**
  549. * Define a task used by AssetsManager to load HDR cube textures
  550. */
  551. export class HDRCubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<HDRCubeTexture> {
  552. /**
  553. * Gets the loaded texture
  554. */
  555. public texture: HDRCubeTexture;
  556. /**
  557. * Callback called when the task is successful
  558. */
  559. public onSuccess: (task: HDRCubeTextureAssetTask) => void;
  560. /**
  561. * Callback called when the task is successful
  562. */
  563. public onError: (task: HDRCubeTextureAssetTask, message?: string, exception?: any) => void;
  564. /**
  565. * Creates a new HDRCubeTextureAssetTask object
  566. * @param name defines the name of the task
  567. * @param url defines the location of the file to load
  568. * @param size defines the desired size (the more it increases the longer the generation will be) If the size is omitted this implies you are using a preprocessed cubemap.
  569. * @param noMipmap defines if mipmaps should not be generated (default is false)
  570. * @param generateHarmonics specifies whether you want to extract the polynomial harmonics during the generation process (default is true)
  571. * @param gammaSpace specifies if the texture will be use in gamma or linear space (the PBR material requires those texture in linear space, but the standard material would require them in Gamma space) (default is false)
  572. * @param reserved Internal use only
  573. */
  574. constructor(
  575. /**
  576. * Defines the name of the task
  577. */
  578. public name: string,
  579. /**
  580. * Defines the location of the file to load
  581. */
  582. public url: string,
  583. /**
  584. * Defines the desired size (the more it increases the longer the generation will be)
  585. */
  586. public size: number,
  587. /**
  588. * Defines if mipmaps should not be generated (default is false)
  589. */
  590. public noMipmap = false,
  591. /**
  592. * Specifies whether you want to extract the polynomial harmonics during the generation process (default is true)
  593. */
  594. public generateHarmonics = true,
  595. /**
  596. * Specifies if the texture will be use in gamma or linear space (the PBR material requires those texture in linear space, but the standard material would require them in Gamma space) (default is false)
  597. */
  598. public gammaSpace = false,
  599. /**
  600. * Internal Use Only
  601. */
  602. public reserved = false) {
  603. super(name);
  604. }
  605. /**
  606. * Execute the current task
  607. * @param scene defines the scene where you want your assets to be loaded
  608. * @param onSuccess is a callback called when the task is successfully executed
  609. * @param onError is a callback called if an error occurs
  610. */
  611. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  612. var onload = () => {
  613. onSuccess();
  614. };
  615. var onerror = (message?: string, exception?: any) => {
  616. onError(message, exception);
  617. };
  618. this.texture = new HDRCubeTexture(this.url, scene, this.size, this.noMipmap, this.generateHarmonics, this.gammaSpace, this.reserved, onload, onerror);
  619. }
  620. }
  621. /**
  622. * Define a task used by AssetsManager to load Equirectangular cube textures
  623. */
  624. export class EquiRectangularCubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<EquiRectangularCubeTexture> {
  625. /**
  626. * Gets the loaded texture
  627. */
  628. public texture: EquiRectangularCubeTexture;
  629. /**
  630. * Callback called when the task is successful
  631. */
  632. public onSuccess: (task: EquiRectangularCubeTextureAssetTask) => void;
  633. /**
  634. * Callback called when the task is successful
  635. */
  636. public onError: (task: EquiRectangularCubeTextureAssetTask, message?: string, exception?: any) => void;
  637. /**
  638. * Creates a new EquiRectangularCubeTextureAssetTask object
  639. * @param name defines the name of the task
  640. * @param url defines the location of the file to load
  641. * @param size defines the desired size (the more it increases the longer the generation will be)
  642. * If the size is omitted this implies you are using a preprocessed cubemap.
  643. * @param noMipmap defines if mipmaps should not be generated (default is false)
  644. * @param gammaSpace specifies if the texture will be used in gamma or linear space
  645. * (the PBR material requires those texture in linear space, but the standard material would require them in Gamma space)
  646. * (default is true)
  647. */
  648. constructor(
  649. /**
  650. * Defines the name of the task
  651. */
  652. public name: string,
  653. /**
  654. * Defines the location of the file to load
  655. */
  656. public url: string,
  657. /**
  658. * Defines the desired size (the more it increases the longer the generation will be)
  659. */
  660. public size: number,
  661. /**
  662. * Defines if mipmaps should not be generated (default is false)
  663. */
  664. public noMipmap: boolean = false,
  665. /**
  666. * Specifies if the texture will be use in gamma or linear space (the PBR material requires those texture in linear space,
  667. * but the standard material would require them in Gamma space) (default is true)
  668. */
  669. public gammaSpace: boolean = true) {
  670. super(name);
  671. }
  672. /**
  673. * Execute the current task
  674. * @param scene defines the scene where you want your assets to be loaded
  675. * @param onSuccess is a callback called when the task is successfully executed
  676. * @param onError is a callback called if an error occurs
  677. */
  678. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void): void {
  679. const onload = () => {
  680. onSuccess();
  681. };
  682. const onerror = (message?: string, exception?: any) => {
  683. onError(message, exception);
  684. };
  685. this.texture = new EquiRectangularCubeTexture(this.url, scene, this.size, this.noMipmap, this.gammaSpace, onload, onerror);
  686. }
  687. }
  688. /**
  689. * This class can be used to easily import assets into a scene
  690. * @see http://doc.babylonjs.com/how_to/how_to_use_assetsmanager
  691. */
  692. export class AssetsManager {
  693. private _scene: Scene;
  694. private _isLoading = false;
  695. protected _tasks = new Array<AbstractAssetTask>();
  696. protected _waitingTasksCount = 0;
  697. protected _totalTasksCount = 0;
  698. /**
  699. * Callback called when all tasks are processed
  700. */
  701. public onFinish: (tasks: AbstractAssetTask[]) => void;
  702. /**
  703. * Callback called when a task is successful
  704. */
  705. public onTaskSuccess: (task: AbstractAssetTask) => void;
  706. /**
  707. * Callback called when a task had an error
  708. */
  709. public onTaskError: (task: AbstractAssetTask) => void;
  710. /**
  711. * Callback called when a task is done (whatever the result is)
  712. */
  713. public onProgress: (remainingCount: number, totalCount: number, task: AbstractAssetTask) => void;
  714. /**
  715. * Observable called when all tasks are processed
  716. */
  717. public onTaskSuccessObservable = new Observable<AbstractAssetTask>();
  718. /**
  719. * Observable called when a task had an error
  720. */
  721. public onTaskErrorObservable = new Observable<AbstractAssetTask>();
  722. /**
  723. * Observable called when all tasks were executed
  724. */
  725. public onTasksDoneObservable = new Observable<AbstractAssetTask[]>();
  726. /**
  727. * Observable called when a task is done (whatever the result is)
  728. */
  729. public onProgressObservable = new Observable<IAssetsProgressEvent>();
  730. /**
  731. * Gets or sets a boolean defining if the AssetsManager should use the default loading screen
  732. * @see http://doc.babylonjs.com/how_to/creating_a_custom_loading_screen
  733. */
  734. public useDefaultLoadingScreen = true;
  735. /**
  736. * Gets or sets a boolean defining if the AssetsManager should automatically hide the loading screen
  737. * when all assets have been downloaded.
  738. * If set to false, you need to manually call in hideLoadingUI() once your scene is ready.
  739. */
  740. public autoHideLoadingUI = true;
  741. /**
  742. * Creates a new AssetsManager
  743. * @param scene defines the scene to work on
  744. */
  745. constructor(scene: Scene) {
  746. this._scene = scene;
  747. }
  748. /**
  749. * Add a MeshAssetTask to the list of active tasks
  750. * @param taskName defines the name of the new task
  751. * @param meshesNames defines the name of meshes to load
  752. * @param rootUrl defines the root url to use to locate files
  753. * @param sceneFilename defines the filename of the scene file
  754. * @returns a new MeshAssetTask object
  755. */
  756. public addMeshTask(taskName: string, meshesNames: any, rootUrl: string, sceneFilename: string): MeshAssetTask {
  757. var task = new MeshAssetTask(taskName, meshesNames, rootUrl, sceneFilename);
  758. this._tasks.push(task);
  759. return task;
  760. }
  761. /**
  762. * Add a TextFileAssetTask to the list of active tasks
  763. * @param taskName defines the name of the new task
  764. * @param url defines the url of the file to load
  765. * @returns a new TextFileAssetTask object
  766. */
  767. public addTextFileTask(taskName: string, url: string): TextFileAssetTask {
  768. var task = new TextFileAssetTask(taskName, url);
  769. this._tasks.push(task);
  770. return task;
  771. }
  772. /**
  773. * Add a BinaryFileAssetTask to the list of active tasks
  774. * @param taskName defines the name of the new task
  775. * @param url defines the url of the file to load
  776. * @returns a new BinaryFileAssetTask object
  777. */
  778. public addBinaryFileTask(taskName: string, url: string): BinaryFileAssetTask {
  779. var task = new BinaryFileAssetTask(taskName, url);
  780. this._tasks.push(task);
  781. return task;
  782. }
  783. /**
  784. * Add a ImageAssetTask to the list of active tasks
  785. * @param taskName defines the name of the new task
  786. * @param url defines the url of the file to load
  787. * @returns a new ImageAssetTask object
  788. */
  789. public addImageTask(taskName: string, url: string): ImageAssetTask {
  790. var task = new ImageAssetTask(taskName, url);
  791. this._tasks.push(task);
  792. return task;
  793. }
  794. /**
  795. * Add a TextureAssetTask to the list of active tasks
  796. * @param taskName defines the name of the new task
  797. * @param url defines the url of the file to load
  798. * @param noMipmap defines if the texture must not receive mipmaps (false by default)
  799. * @param invertY defines if you want to invert Y axis of the loaded texture (false by default)
  800. * @param samplingMode defines the sampling mode to use (Texture.TRILINEAR_SAMPLINGMODE by default)
  801. * @returns a new TextureAssetTask object
  802. */
  803. public addTextureTask(taskName: string, url: string, noMipmap?: boolean, invertY?: boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE): TextureAssetTask {
  804. var task = new TextureAssetTask(taskName, url, noMipmap, invertY, samplingMode);
  805. this._tasks.push(task);
  806. return task;
  807. }
  808. /**
  809. * Add a CubeTextureAssetTask to the list of active tasks
  810. * @param taskName defines the name of the new task
  811. * @param url defines the url of the file to load
  812. * @param extensions defines the extension to use to load the cube map (can be null)
  813. * @param noMipmap defines if the texture must not receive mipmaps (false by default)
  814. * @param files defines the list of files to load (can be null)
  815. * @returns a new CubeTextureAssetTask object
  816. */
  817. public addCubeTextureTask(taskName: string, url: string, extensions?: string[], noMipmap?: boolean, files?: string[]): CubeTextureAssetTask {
  818. var task = new CubeTextureAssetTask(taskName, url, extensions, noMipmap, files);
  819. this._tasks.push(task);
  820. return task;
  821. }
  822. /**
  823. *
  824. * Add a HDRCubeTextureAssetTask to the list of active tasks
  825. * @param taskName defines the name of the new task
  826. * @param url defines the url of the file to load
  827. * @param size defines the size you want for the cubemap (can be null)
  828. * @param noMipmap defines if the texture must not receive mipmaps (false by default)
  829. * @param generateHarmonics defines if you want to automatically generate (true by default)
  830. * @param gammaSpace specifies if the texture will be use in gamma or linear space (the PBR material requires those texture in linear space, but the standard material would require them in Gamma space) (default is false)
  831. * @param reserved Internal use only
  832. * @returns a new HDRCubeTextureAssetTask object
  833. */
  834. public addHDRCubeTextureTask(taskName: string, url: string, size: number, noMipmap = false, generateHarmonics = true, gammaSpace = false, reserved = false): HDRCubeTextureAssetTask {
  835. var task = new HDRCubeTextureAssetTask(taskName, url, size, noMipmap, generateHarmonics, gammaSpace, reserved);
  836. this._tasks.push(task);
  837. return task;
  838. }
  839. /**
  840. *
  841. * Add a EquiRectangularCubeTextureAssetTask to the list of active tasks
  842. * @param taskName defines the name of the new task
  843. * @param url defines the url of the file to load
  844. * @param size defines the size you want for the cubemap (can be null)
  845. * @param noMipmap defines if the texture must not receive mipmaps (false by default)
  846. * @param gammaSpace Specifies if the texture will be used in gamma or linear space
  847. * (the PBR material requires those textures in linear space, but the standard material would require them in Gamma space)
  848. * @returns a new EquiRectangularCubeTextureAssetTask object
  849. */
  850. public addEquiRectangularCubeTextureAssetTask(taskName: string, url: string, size: number, noMipmap = false, gammaSpace = true): EquiRectangularCubeTextureAssetTask {
  851. const task = new EquiRectangularCubeTextureAssetTask(taskName, url, size, noMipmap, gammaSpace);
  852. this._tasks.push(task);
  853. return task;
  854. }
  855. /**
  856. * Remove a task from the assets manager.
  857. * @param task the task to remove
  858. */
  859. public removeTask(task: AbstractAssetTask) {
  860. let index = this._tasks.indexOf(task);
  861. if (index > -1) {
  862. this._tasks.splice(index, 1);
  863. }
  864. }
  865. private _decreaseWaitingTasksCount(task: AbstractAssetTask): void {
  866. this._waitingTasksCount--;
  867. try {
  868. if (this.onProgress) {
  869. this.onProgress(
  870. this._waitingTasksCount,
  871. this._totalTasksCount,
  872. task
  873. );
  874. }
  875. this.onProgressObservable.notifyObservers(
  876. new AssetsProgressEvent(
  877. this._waitingTasksCount,
  878. this._totalTasksCount,
  879. task
  880. )
  881. );
  882. } catch (e) {
  883. Logger.Error("Error running progress callbacks.");
  884. console.log(e);
  885. }
  886. if (this._waitingTasksCount === 0) {
  887. try {
  888. if (this.onFinish) {
  889. this.onFinish(this._tasks);
  890. }
  891. // Let's remove successfull tasks
  892. var currentTasks = this._tasks.slice();
  893. for (var task of currentTasks) {
  894. if (task.taskState === AssetTaskState.DONE) {
  895. let index = this._tasks.indexOf(task);
  896. if (index > -1) {
  897. this._tasks.splice(index, 1);
  898. }
  899. }
  900. }
  901. this.onTasksDoneObservable.notifyObservers(this._tasks);
  902. } catch (e) {
  903. Logger.Error("Error running tasks-done callbacks.");
  904. console.log(e);
  905. }
  906. this._isLoading = false;
  907. if (this.autoHideLoadingUI) {
  908. this._scene.getEngine().hideLoadingUI();
  909. }
  910. }
  911. }
  912. private _runTask(task: AbstractAssetTask): void {
  913. let done = () => {
  914. try {
  915. if (this.onTaskSuccess) {
  916. this.onTaskSuccess(task);
  917. }
  918. this.onTaskSuccessObservable.notifyObservers(task);
  919. this._decreaseWaitingTasksCount(task);
  920. } catch (e) {
  921. error("Error executing task success callbacks", e);
  922. }
  923. };
  924. let error = (message?: string, exception?: any) => {
  925. task._setErrorObject(message, exception);
  926. if (this.onTaskError) {
  927. this.onTaskError(task);
  928. }
  929. this.onTaskErrorObservable.notifyObservers(task);
  930. this._decreaseWaitingTasksCount(task);
  931. };
  932. task.run(this._scene, done, error);
  933. }
  934. /**
  935. * Reset the AssetsManager and remove all tasks
  936. * @return the current instance of the AssetsManager
  937. */
  938. public reset(): AssetsManager {
  939. this._isLoading = false;
  940. this._tasks = new Array<AbstractAssetTask>();
  941. return this;
  942. }
  943. /**
  944. * Start the loading process
  945. * @return the current instance of the AssetsManager
  946. */
  947. public load(): AssetsManager {
  948. if (this._isLoading) {
  949. return this;
  950. }
  951. this._isLoading = true;
  952. this._waitingTasksCount = this._tasks.length;
  953. this._totalTasksCount = this._tasks.length;
  954. if (this._waitingTasksCount === 0) {
  955. this._isLoading = false;
  956. if (this.onFinish) {
  957. this.onFinish(this._tasks);
  958. }
  959. this.onTasksDoneObservable.notifyObservers(this._tasks);
  960. return this;
  961. }
  962. if (this.useDefaultLoadingScreen) {
  963. this._scene.getEngine().displayLoadingUI();
  964. }
  965. for (var index = 0; index < this._tasks.length; index++) {
  966. var task = this._tasks[index];
  967. if (task.taskState === AssetTaskState.INIT) {
  968. this._runTask(task);
  969. }
  970. }
  971. return this;
  972. }
  973. /**
  974. * Start the loading process as an async operation
  975. * @return a promise returning the list of failed tasks
  976. */
  977. public loadAsync(): Promise<void> {
  978. return new Promise((resolve, reject) => {
  979. if (this._isLoading) {
  980. resolve();
  981. return;
  982. }
  983. this.onTasksDoneObservable.addOnce((remainingTasks) => {
  984. if (remainingTasks && remainingTasks.length) {
  985. reject(remainingTasks);
  986. } else {
  987. resolve();
  988. }
  989. });
  990. this.load();
  991. });
  992. }
  993. }