assetsManager.ts 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
  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. * Defines if texture must be inverted on Y axis (default is false)
  432. */
  433. public invertY: boolean = false; // note that Texture defaults to true when using its constructor
  434. /**
  435. * Callback called when the task is successful
  436. */
  437. public onSuccess: (task: TextureAssetTask) => void;
  438. /**
  439. * Callback called when the task is successful
  440. */
  441. public onError: (task: TextureAssetTask, message?: string, exception?: any) => void;
  442. /**
  443. * Creates a new TextureAssetTask object
  444. * @param name defines the name of the task
  445. * @param url defines the location of the file to load
  446. * @param noMipmap defines if mipmap should not be generated (default is false)
  447. * @param invertY defines if texture must be inverted on Y axis (default is false)
  448. * @param samplingMode defines the sampling mode to use (default is Texture.TRILINEAR_SAMPLINGMODE)
  449. */
  450. constructor(
  451. /**
  452. * Defines the name of the task
  453. */
  454. public name: string,
  455. /**
  456. * Defines the location of the file to load
  457. */
  458. public url: string,
  459. /**
  460. * Defines if mipmap should not be generated (default is false)
  461. */
  462. public noMipmap?: boolean,
  463. /**
  464. * Defines if texture must be inverted on Y axis (default is false)
  465. */
  466. invertY: boolean = false,
  467. /**
  468. * Defines the sampling mode to use (default is Texture.TRILINEAR_SAMPLINGMODE)
  469. */
  470. public samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
  471. super(name);
  472. }
  473. /**
  474. * Execute the current task
  475. * @param scene defines the scene where you want your assets to be loaded
  476. * @param onSuccess is a callback called when the task is successfully executed
  477. * @param onError is a callback called if an error occurs
  478. */
  479. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  480. var onload = () => {
  481. onSuccess();
  482. };
  483. var onerror = (message?: string, exception?: any) => {
  484. onError(message, exception);
  485. };
  486. this.texture = new Texture(this.url, scene, this.noMipmap, this.invertY, this.samplingMode, onload, onerror);
  487. }
  488. }
  489. /**
  490. * Define a task used by AssetsManager to load cube textures
  491. */
  492. export class CubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<CubeTexture> {
  493. /**
  494. * Gets the loaded texture
  495. */
  496. public texture: CubeTexture;
  497. /**
  498. * Callback called when the task is successful
  499. */
  500. public onSuccess: (task: CubeTextureAssetTask) => void;
  501. /**
  502. * Callback called when the task is successful
  503. */
  504. public onError: (task: CubeTextureAssetTask, message?: string, exception?: any) => void;
  505. /**
  506. * Creates a new CubeTextureAssetTask
  507. * @param name defines the name of the task
  508. * @param url defines the location of the files to load (You have to specify the folder where the files are + filename with no extension)
  509. * @param extensions defines the extensions to use to load files (["_px", "_py", "_pz", "_nx", "_ny", "_nz"] by default)
  510. * @param noMipmap defines if mipmaps should not be generated (default is false)
  511. * @param files defines the explicit list of files (undefined by default)
  512. */
  513. constructor(
  514. /**
  515. * Defines the name of the task
  516. */
  517. public name: string,
  518. /**
  519. * Defines the location of the files to load (You have to specify the folder where the files are + filename with no extension)
  520. */
  521. public url: string,
  522. /**
  523. * Defines the extensions to use to load files (["_px", "_py", "_pz", "_nx", "_ny", "_nz"] by default)
  524. */
  525. public extensions?: string[],
  526. /**
  527. * Defines if mipmaps should not be generated (default is false)
  528. */
  529. public noMipmap?: boolean,
  530. /**
  531. * Defines the explicit list of files (undefined by default)
  532. */
  533. public files?: string[]) {
  534. super(name);
  535. }
  536. /**
  537. * Execute the current task
  538. * @param scene defines the scene where you want your assets to be loaded
  539. * @param onSuccess is a callback called when the task is successfully executed
  540. * @param onError is a callback called if an error occurs
  541. */
  542. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  543. var onload = () => {
  544. onSuccess();
  545. };
  546. var onerror = (message?: string, exception?: any) => {
  547. onError(message, exception);
  548. };
  549. this.texture = new CubeTexture(this.url, scene, this.extensions, this.noMipmap, this.files, onload, onerror);
  550. }
  551. }
  552. /**
  553. * Define a task used by AssetsManager to load HDR cube textures
  554. */
  555. export class HDRCubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<HDRCubeTexture> {
  556. /**
  557. * Gets the loaded texture
  558. */
  559. public texture: HDRCubeTexture;
  560. /**
  561. * Callback called when the task is successful
  562. */
  563. public onSuccess: (task: HDRCubeTextureAssetTask) => void;
  564. /**
  565. * Callback called when the task is successful
  566. */
  567. public onError: (task: HDRCubeTextureAssetTask, message?: string, exception?: any) => void;
  568. /**
  569. * Creates a new HDRCubeTextureAssetTask object
  570. * @param name defines the name of the task
  571. * @param url defines the location of the file to load
  572. * @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.
  573. * @param noMipmap defines if mipmaps should not be generated (default is false)
  574. * @param generateHarmonics specifies whether you want to extract the polynomial harmonics during the generation process (default is true)
  575. * @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)
  576. * @param reserved Internal use only
  577. */
  578. constructor(
  579. /**
  580. * Defines the name of the task
  581. */
  582. public name: string,
  583. /**
  584. * Defines the location of the file to load
  585. */
  586. public url: string,
  587. /**
  588. * Defines the desired size (the more it increases the longer the generation will be)
  589. */
  590. public size: number,
  591. /**
  592. * Defines if mipmaps should not be generated (default is false)
  593. */
  594. public noMipmap = false,
  595. /**
  596. * Specifies whether you want to extract the polynomial harmonics during the generation process (default is true)
  597. */
  598. public generateHarmonics = true,
  599. /**
  600. * 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)
  601. */
  602. public gammaSpace = false,
  603. /**
  604. * Internal Use Only
  605. */
  606. public reserved = false) {
  607. super(name);
  608. }
  609. /**
  610. * Execute the current task
  611. * @param scene defines the scene where you want your assets to be loaded
  612. * @param onSuccess is a callback called when the task is successfully executed
  613. * @param onError is a callback called if an error occurs
  614. */
  615. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
  616. var onload = () => {
  617. onSuccess();
  618. };
  619. var onerror = (message?: string, exception?: any) => {
  620. onError(message, exception);
  621. };
  622. this.texture = new HDRCubeTexture(this.url, scene, this.size, this.noMipmap, this.generateHarmonics, this.gammaSpace, this.reserved, onload, onerror);
  623. }
  624. }
  625. /**
  626. * Define a task used by AssetsManager to load Equirectangular cube textures
  627. */
  628. export class EquiRectangularCubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<EquiRectangularCubeTexture> {
  629. /**
  630. * Gets the loaded texture
  631. */
  632. public texture: EquiRectangularCubeTexture;
  633. /**
  634. * Callback called when the task is successful
  635. */
  636. public onSuccess: (task: EquiRectangularCubeTextureAssetTask) => void;
  637. /**
  638. * Callback called when the task is successful
  639. */
  640. public onError: (task: EquiRectangularCubeTextureAssetTask, message?: string, exception?: any) => void;
  641. /**
  642. * Creates a new EquiRectangularCubeTextureAssetTask object
  643. * @param name defines the name of the task
  644. * @param url defines the location of the file to load
  645. * @param size defines the desired size (the more it increases the longer the generation will be)
  646. * If the size is omitted this implies you are using a preprocessed cubemap.
  647. * @param noMipmap defines if mipmaps should not be generated (default is false)
  648. * @param gammaSpace specifies if the texture will be used in gamma or linear space
  649. * (the PBR material requires those texture in linear space, but the standard material would require them in Gamma space)
  650. * (default is true)
  651. */
  652. constructor(
  653. /**
  654. * Defines the name of the task
  655. */
  656. public name: string,
  657. /**
  658. * Defines the location of the file to load
  659. */
  660. public url: string,
  661. /**
  662. * Defines the desired size (the more it increases the longer the generation will be)
  663. */
  664. public size: number,
  665. /**
  666. * Defines if mipmaps should not be generated (default is false)
  667. */
  668. public noMipmap: boolean = false,
  669. /**
  670. * Specifies if the texture will be use in gamma or linear space (the PBR material requires those texture in linear space,
  671. * but the standard material would require them in Gamma space) (default is true)
  672. */
  673. public gammaSpace: boolean = true) {
  674. super(name);
  675. }
  676. /**
  677. * Execute the current task
  678. * @param scene defines the scene where you want your assets to be loaded
  679. * @param onSuccess is a callback called when the task is successfully executed
  680. * @param onError is a callback called if an error occurs
  681. */
  682. public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void): void {
  683. const onload = () => {
  684. onSuccess();
  685. };
  686. const onerror = (message?: string, exception?: any) => {
  687. onError(message, exception);
  688. };
  689. this.texture = new EquiRectangularCubeTexture(this.url, scene, this.size, this.noMipmap, this.gammaSpace, onload, onerror);
  690. }
  691. }
  692. /**
  693. * This class can be used to easily import assets into a scene
  694. * @see http://doc.babylonjs.com/how_to/how_to_use_assetsmanager
  695. */
  696. export class AssetsManager {
  697. private _scene: Scene;
  698. private _isLoading = false;
  699. protected _tasks = new Array<AbstractAssetTask>();
  700. protected _waitingTasksCount = 0;
  701. protected _totalTasksCount = 0;
  702. /**
  703. * Callback called when all tasks are processed
  704. */
  705. public onFinish: (tasks: AbstractAssetTask[]) => void;
  706. /**
  707. * Callback called when a task is successful
  708. */
  709. public onTaskSuccess: (task: AbstractAssetTask) => void;
  710. /**
  711. * Callback called when a task had an error
  712. */
  713. public onTaskError: (task: AbstractAssetTask) => void;
  714. /**
  715. * Callback called when a task is done (whatever the result is)
  716. */
  717. public onProgress: (remainingCount: number, totalCount: number, task: AbstractAssetTask) => void;
  718. /**
  719. * Observable called when all tasks are processed
  720. */
  721. public onTaskSuccessObservable = new Observable<AbstractAssetTask>();
  722. /**
  723. * Observable called when a task had an error
  724. */
  725. public onTaskErrorObservable = new Observable<AbstractAssetTask>();
  726. /**
  727. * Observable called when all tasks were executed
  728. */
  729. public onTasksDoneObservable = new Observable<AbstractAssetTask[]>();
  730. /**
  731. * Observable called when a task is done (whatever the result is)
  732. */
  733. public onProgressObservable = new Observable<IAssetsProgressEvent>();
  734. /**
  735. * Gets or sets a boolean defining if the AssetsManager should use the default loading screen
  736. * @see http://doc.babylonjs.com/how_to/creating_a_custom_loading_screen
  737. */
  738. public useDefaultLoadingScreen = true;
  739. /**
  740. * Gets or sets a boolean defining if the AssetsManager should automatically hide the loading screen
  741. * when all assets have been downloaded.
  742. * If set to false, you need to manually call in hideLoadingUI() once your scene is ready.
  743. */
  744. public autoHideLoadingUI = true;
  745. /**
  746. * Creates a new AssetsManager
  747. * @param scene defines the scene to work on
  748. */
  749. constructor(scene: Scene) {
  750. this._scene = scene;
  751. }
  752. /**
  753. * Add a MeshAssetTask to the list of active tasks
  754. * @param taskName defines the name of the new task
  755. * @param meshesNames defines the name of meshes to load
  756. * @param rootUrl defines the root url to use to locate files
  757. * @param sceneFilename defines the filename of the scene file
  758. * @returns a new MeshAssetTask object
  759. */
  760. public addMeshTask(taskName: string, meshesNames: any, rootUrl: string, sceneFilename: string): MeshAssetTask {
  761. var task = new MeshAssetTask(taskName, meshesNames, rootUrl, sceneFilename);
  762. this._tasks.push(task);
  763. return task;
  764. }
  765. /**
  766. * Add a TextFileAssetTask to the list of active tasks
  767. * @param taskName defines the name of the new task
  768. * @param url defines the url of the file to load
  769. * @returns a new TextFileAssetTask object
  770. */
  771. public addTextFileTask(taskName: string, url: string): TextFileAssetTask {
  772. var task = new TextFileAssetTask(taskName, url);
  773. this._tasks.push(task);
  774. return task;
  775. }
  776. /**
  777. * Add a BinaryFileAssetTask to the list of active tasks
  778. * @param taskName defines the name of the new task
  779. * @param url defines the url of the file to load
  780. * @returns a new BinaryFileAssetTask object
  781. */
  782. public addBinaryFileTask(taskName: string, url: string): BinaryFileAssetTask {
  783. var task = new BinaryFileAssetTask(taskName, url);
  784. this._tasks.push(task);
  785. return task;
  786. }
  787. /**
  788. * Add a ImageAssetTask to the list of active tasks
  789. * @param taskName defines the name of the new task
  790. * @param url defines the url of the file to load
  791. * @returns a new ImageAssetTask object
  792. */
  793. public addImageTask(taskName: string, url: string): ImageAssetTask {
  794. var task = new ImageAssetTask(taskName, url);
  795. this._tasks.push(task);
  796. return task;
  797. }
  798. /**
  799. * Add a TextureAssetTask to the list of active tasks
  800. * @param taskName defines the name of the new task
  801. * @param url defines the url of the file to load
  802. * @param noMipmap defines if the texture must not receive mipmaps (false by default)
  803. * @param invertY defines if you want to invert Y axis of the loaded texture (false by default)
  804. * @param samplingMode defines the sampling mode to use (Texture.TRILINEAR_SAMPLINGMODE by default)
  805. * @returns a new TextureAssetTask object
  806. */
  807. public addTextureTask(taskName: string, url: string, noMipmap?: boolean, invertY?: boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE): TextureAssetTask {
  808. var task = new TextureAssetTask(taskName, url, noMipmap, invertY, samplingMode);
  809. this._tasks.push(task);
  810. return task;
  811. }
  812. /**
  813. * Add a CubeTextureAssetTask to the list of active tasks
  814. * @param taskName defines the name of the new task
  815. * @param url defines the url of the file to load
  816. * @param extensions defines the extension to use to load the cube map (can be null)
  817. * @param noMipmap defines if the texture must not receive mipmaps (false by default)
  818. * @param files defines the list of files to load (can be null)
  819. * @returns a new CubeTextureAssetTask object
  820. */
  821. public addCubeTextureTask(taskName: string, url: string, extensions?: string[], noMipmap?: boolean, files?: string[]): CubeTextureAssetTask {
  822. var task = new CubeTextureAssetTask(taskName, url, extensions, noMipmap, files);
  823. this._tasks.push(task);
  824. return task;
  825. }
  826. /**
  827. *
  828. * Add a HDRCubeTextureAssetTask to the list of active tasks
  829. * @param taskName defines the name of the new task
  830. * @param url defines the url of the file to load
  831. * @param size defines the size you want for the cubemap (can be null)
  832. * @param noMipmap defines if the texture must not receive mipmaps (false by default)
  833. * @param generateHarmonics defines if you want to automatically generate (true by default)
  834. * @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)
  835. * @param reserved Internal use only
  836. * @returns a new HDRCubeTextureAssetTask object
  837. */
  838. public addHDRCubeTextureTask(taskName: string, url: string, size: number, noMipmap = false, generateHarmonics = true, gammaSpace = false, reserved = false): HDRCubeTextureAssetTask {
  839. var task = new HDRCubeTextureAssetTask(taskName, url, size, noMipmap, generateHarmonics, gammaSpace, reserved);
  840. this._tasks.push(task);
  841. return task;
  842. }
  843. /**
  844. *
  845. * Add a EquiRectangularCubeTextureAssetTask to the list of active tasks
  846. * @param taskName defines the name of the new task
  847. * @param url defines the url of the file to load
  848. * @param size defines the size you want for the cubemap (can be null)
  849. * @param noMipmap defines if the texture must not receive mipmaps (false by default)
  850. * @param gammaSpace Specifies if the texture will be used in gamma or linear space
  851. * (the PBR material requires those textures in linear space, but the standard material would require them in Gamma space)
  852. * @returns a new EquiRectangularCubeTextureAssetTask object
  853. */
  854. public addEquiRectangularCubeTextureAssetTask(taskName: string, url: string, size: number, noMipmap = false, gammaSpace = true): EquiRectangularCubeTextureAssetTask {
  855. const task = new EquiRectangularCubeTextureAssetTask(taskName, url, size, noMipmap, gammaSpace);
  856. this._tasks.push(task);
  857. return task;
  858. }
  859. /**
  860. * Remove a task from the assets manager.
  861. * @param task the task to remove
  862. */
  863. public removeTask(task: AbstractAssetTask) {
  864. let index = this._tasks.indexOf(task);
  865. if (index > -1) {
  866. this._tasks.splice(index, 1);
  867. }
  868. }
  869. private _decreaseWaitingTasksCount(task: AbstractAssetTask): void {
  870. this._waitingTasksCount--;
  871. try {
  872. if (this.onProgress) {
  873. this.onProgress(
  874. this._waitingTasksCount,
  875. this._totalTasksCount,
  876. task
  877. );
  878. }
  879. this.onProgressObservable.notifyObservers(
  880. new AssetsProgressEvent(
  881. this._waitingTasksCount,
  882. this._totalTasksCount,
  883. task
  884. )
  885. );
  886. } catch (e) {
  887. Logger.Error("Error running progress callbacks.");
  888. console.log(e);
  889. }
  890. if (this._waitingTasksCount === 0) {
  891. try {
  892. var currentTasks = this._tasks.slice();
  893. if (this.onFinish) {
  894. // Calling onFinish with immutable array of tasks
  895. this.onFinish(currentTasks);
  896. }
  897. // Let's remove successfull tasks
  898. for (var task of currentTasks) {
  899. if (task.taskState === AssetTaskState.DONE) {
  900. let index = this._tasks.indexOf(task);
  901. if (index > -1) {
  902. this._tasks.splice(index, 1);
  903. }
  904. }
  905. }
  906. this.onTasksDoneObservable.notifyObservers(this._tasks);
  907. } catch (e) {
  908. Logger.Error("Error running tasks-done callbacks.");
  909. console.log(e);
  910. }
  911. this._isLoading = false;
  912. if (this.autoHideLoadingUI) {
  913. this._scene.getEngine().hideLoadingUI();
  914. }
  915. }
  916. }
  917. private _runTask(task: AbstractAssetTask): void {
  918. let done = () => {
  919. try {
  920. if (this.onTaskSuccess) {
  921. this.onTaskSuccess(task);
  922. }
  923. this.onTaskSuccessObservable.notifyObservers(task);
  924. this._decreaseWaitingTasksCount(task);
  925. } catch (e) {
  926. error("Error executing task success callbacks", e);
  927. }
  928. };
  929. let error = (message?: string, exception?: any) => {
  930. task._setErrorObject(message, exception);
  931. if (this.onTaskError) {
  932. this.onTaskError(task);
  933. }
  934. this.onTaskErrorObservable.notifyObservers(task);
  935. this._decreaseWaitingTasksCount(task);
  936. };
  937. task.run(this._scene, done, error);
  938. }
  939. /**
  940. * Reset the AssetsManager and remove all tasks
  941. * @return the current instance of the AssetsManager
  942. */
  943. public reset(): AssetsManager {
  944. this._isLoading = false;
  945. this._tasks = new Array<AbstractAssetTask>();
  946. return this;
  947. }
  948. /**
  949. * Start the loading process
  950. * @return the current instance of the AssetsManager
  951. */
  952. public load(): AssetsManager {
  953. if (this._isLoading) {
  954. return this;
  955. }
  956. this._isLoading = true;
  957. this._waitingTasksCount = this._tasks.length;
  958. this._totalTasksCount = this._tasks.length;
  959. if (this._waitingTasksCount === 0) {
  960. this._isLoading = false;
  961. if (this.onFinish) {
  962. this.onFinish(this._tasks);
  963. }
  964. this.onTasksDoneObservable.notifyObservers(this._tasks);
  965. return this;
  966. }
  967. if (this.useDefaultLoadingScreen) {
  968. this._scene.getEngine().displayLoadingUI();
  969. }
  970. for (var index = 0; index < this._tasks.length; index++) {
  971. var task = this._tasks[index];
  972. if (task.taskState === AssetTaskState.INIT) {
  973. this._runTask(task);
  974. }
  975. }
  976. return this;
  977. }
  978. /**
  979. * Start the loading process as an async operation
  980. * @return a promise returning the list of failed tasks
  981. */
  982. public loadAsync(): Promise<void> {
  983. return new Promise((resolve, reject) => {
  984. if (this._isLoading) {
  985. resolve();
  986. return;
  987. }
  988. this.onTasksDoneObservable.addOnce((remainingTasks) => {
  989. if (remainingTasks && remainingTasks.length) {
  990. reject(remainingTasks);
  991. } else {
  992. resolve();
  993. }
  994. });
  995. this.load();
  996. });
  997. }
  998. }