spriteManager.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. import { Observable } from "../Misc/observable.js";
  2. import { Vector3, TmpVectors, Matrix } from "../Maths/math.vector.js";
  3. import { Sprite } from "./sprite.js";
  4. import { SpriteSceneComponent } from "./spriteSceneComponent.js";
  5. import { PickingInfo } from "../Collisions/pickingInfo.js";
  6. import { Texture } from "../Materials/Textures/texture.js";
  7. import { SceneComponentConstants } from "../sceneComponent.js";
  8. import { Logger } from "../Misc/logger.js";
  9. import { Tools } from "../Misc/tools.js";
  10. import { WebRequest } from "../Misc/webRequest.js";
  11. import { SpriteRenderer } from "./spriteRenderer.js";
  12. import { EngineStore } from "../Engines/engineStore.js";
  13. /**
  14. * Class used to manage multiple sprites on the same spritesheet
  15. * @see https://doc.babylonjs.com/features/featuresDeepDive/sprites
  16. */
  17. export class SpriteManager {
  18. /**
  19. * Callback called when the manager is disposed
  20. */
  21. set onDispose(callback) {
  22. if (this._onDisposeObserver) {
  23. this.onDisposeObservable.remove(this._onDisposeObserver);
  24. }
  25. this._onDisposeObserver = this.onDisposeObservable.add(callback);
  26. }
  27. /**
  28. * Gets the array of sprites
  29. */
  30. get children() {
  31. return this.sprites;
  32. }
  33. /**
  34. * Gets the hosting scene
  35. */
  36. get scene() {
  37. return this._scene;
  38. }
  39. /**
  40. * Gets the capacity of the manager
  41. */
  42. get capacity() {
  43. return this._spriteRenderer.capacity;
  44. }
  45. /**
  46. * Gets or sets the spritesheet texture
  47. */
  48. get texture() {
  49. return this._spriteRenderer.texture;
  50. }
  51. set texture(value) {
  52. value.wrapU = Texture.CLAMP_ADDRESSMODE;
  53. value.wrapV = Texture.CLAMP_ADDRESSMODE;
  54. this._spriteRenderer.texture = value;
  55. this._textureContent = null;
  56. }
  57. /** Defines the default width of a cell in the spritesheet */
  58. get cellWidth() {
  59. return this._spriteRenderer.cellWidth;
  60. }
  61. set cellWidth(value) {
  62. this._spriteRenderer.cellWidth = value;
  63. }
  64. /** Defines the default height of a cell in the spritesheet */
  65. get cellHeight() {
  66. return this._spriteRenderer.cellHeight;
  67. }
  68. set cellHeight(value) {
  69. this._spriteRenderer.cellHeight = value;
  70. }
  71. /** Gets or sets a boolean indicating if the manager must consider scene fog when rendering */
  72. get fogEnabled() {
  73. return this._spriteRenderer.fogEnabled;
  74. }
  75. set fogEnabled(value) {
  76. this._spriteRenderer.fogEnabled = value;
  77. }
  78. /**
  79. * Blend mode use to render the particle, it can be any of
  80. * the static undefined properties provided in this class.
  81. * Default value is 2
  82. */
  83. get blendMode() {
  84. return this._spriteRenderer.blendMode;
  85. }
  86. set blendMode(blendMode) {
  87. this._spriteRenderer.blendMode = blendMode;
  88. }
  89. /** Disables writing to the depth buffer when rendering the sprites.
  90. * It can be handy to disable depth writing when using textures without alpha channel
  91. * and setting some specific blend modes.
  92. */
  93. get disableDepthWrite() {
  94. return this._disableDepthWrite;
  95. }
  96. set disableDepthWrite(value) {
  97. this._disableDepthWrite = value;
  98. this._spriteRenderer.disableDepthWrite = value;
  99. }
  100. /**
  101. * Gets or sets a boolean indicating if the renderer must render sprites with pixel perfect rendering
  102. * In this mode, sprites are rendered as "pixel art", which means that they appear as pixelated but remain stable when moving or when rotated or scaled.
  103. * Note that for this mode to work as expected, the sprite texture must use the BILINEAR sampling mode, not NEAREST!
  104. */
  105. get pixelPerfect() {
  106. return this._spriteRenderer.pixelPerfect;
  107. }
  108. set pixelPerfect(value) {
  109. this._spriteRenderer.pixelPerfect = value;
  110. if (value && this.texture.samplingMode !== 3) {
  111. this.texture.updateSamplingMode(3);
  112. }
  113. }
  114. /**
  115. * Creates a new sprite manager
  116. * @param name defines the manager's name
  117. * @param imgUrl defines the sprite sheet url
  118. * @param capacity defines the maximum allowed number of sprites
  119. * @param cellSize defines the size of a sprite cell
  120. * @param scene defines the hosting scene
  121. * @param epsilon defines the epsilon value to align texture (0.01 by default)
  122. * @param samplingMode defines the sampling mode to use with spritesheet
  123. * @param fromPacked set to false; do not alter
  124. * @param spriteJSON null otherwise a JSON object defining sprite sheet data; do not alter
  125. */
  126. constructor(
  127. /** defines the manager's name */
  128. name, imgUrl, capacity, cellSize, scene, epsilon = 0.01, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, fromPacked = false, spriteJSON = null) {
  129. this.name = name;
  130. /** Gets the list of sprites */
  131. this.sprites = [];
  132. /** Gets or sets the rendering group id (0 by default) */
  133. this.renderingGroupId = 0;
  134. /** Gets or sets camera layer mask */
  135. this.layerMask = 0x0fffffff;
  136. /** Gets or sets a boolean indicating if the sprites are pickable */
  137. this.isPickable = false;
  138. /**
  139. * Gets or sets an object used to store user defined information for the sprite manager
  140. */
  141. this.metadata = null;
  142. /** @internal */
  143. this._wasDispatched = false;
  144. /**
  145. * An event triggered when the manager is disposed.
  146. */
  147. this.onDisposeObservable = new Observable();
  148. this._disableDepthWrite = false;
  149. /** True when packed cell data from JSON file is ready*/
  150. this._packedAndReady = false;
  151. this._customUpdate = (sprite, baseSize) => {
  152. if (!sprite.cellRef) {
  153. sprite.cellIndex = 0;
  154. }
  155. const num = sprite.cellIndex;
  156. if (typeof num === "number" && isFinite(num) && Math.floor(num) === num) {
  157. sprite.cellRef = this._spriteMap[sprite.cellIndex];
  158. }
  159. sprite._xOffset = this._cellData[sprite.cellRef].frame.x / baseSize.width;
  160. sprite._yOffset = this._cellData[sprite.cellRef].frame.y / baseSize.height;
  161. sprite._xSize = this._cellData[sprite.cellRef].frame.w;
  162. sprite._ySize = this._cellData[sprite.cellRef].frame.h;
  163. };
  164. if (!scene) {
  165. scene = EngineStore.LastCreatedScene;
  166. }
  167. if (!scene._getComponent(SceneComponentConstants.NAME_SPRITE)) {
  168. scene._addComponent(new SpriteSceneComponent(scene));
  169. }
  170. this._fromPacked = fromPacked;
  171. this._scene = scene;
  172. const engine = this._scene.getEngine();
  173. this._spriteRenderer = new SpriteRenderer(engine, capacity, epsilon, scene);
  174. if (cellSize.width && cellSize.height) {
  175. this.cellWidth = cellSize.width;
  176. this.cellHeight = cellSize.height;
  177. }
  178. else if (cellSize !== undefined) {
  179. this.cellWidth = cellSize;
  180. this.cellHeight = cellSize;
  181. }
  182. else {
  183. this._spriteRenderer = null;
  184. return;
  185. }
  186. this._scene.spriteManagers && this._scene.spriteManagers.push(this);
  187. this.uniqueId = this.scene.getUniqueId();
  188. if (imgUrl) {
  189. this.texture = new Texture(imgUrl, scene, true, false, samplingMode);
  190. }
  191. if (this._fromPacked) {
  192. this._makePacked(imgUrl, spriteJSON);
  193. }
  194. }
  195. /**
  196. * Returns the string "SpriteManager"
  197. * @returns "SpriteManager"
  198. */
  199. getClassName() {
  200. return "SpriteManager";
  201. }
  202. _makePacked(imgUrl, spriteJSON) {
  203. if (spriteJSON !== null) {
  204. try {
  205. //Get the JSON and Check its structure. If its an array parse it if its a JSON string etc...
  206. let celldata;
  207. if (typeof spriteJSON === "string") {
  208. celldata = JSON.parse(spriteJSON);
  209. }
  210. else {
  211. celldata = spriteJSON;
  212. }
  213. if (celldata.frames.length) {
  214. const frametemp = {};
  215. for (let i = 0; i < celldata.frames.length; i++) {
  216. const _f = celldata.frames[i];
  217. if (typeof Object.keys(_f)[0] !== "string") {
  218. throw new Error("Invalid JSON Format. Check the frame values and make sure the name is the first parameter.");
  219. }
  220. const name = _f[Object.keys(_f)[0]];
  221. frametemp[name] = _f;
  222. }
  223. celldata.frames = frametemp;
  224. }
  225. const spritemap = Reflect.ownKeys(celldata.frames);
  226. this._spriteMap = spritemap;
  227. this._packedAndReady = true;
  228. this._cellData = celldata.frames;
  229. }
  230. catch (e) {
  231. this._fromPacked = false;
  232. this._packedAndReady = false;
  233. throw new Error("Invalid JSON from string. Spritesheet managed with constant cell size.");
  234. }
  235. }
  236. else {
  237. const re = /\./g;
  238. let li;
  239. do {
  240. li = re.lastIndex;
  241. re.test(imgUrl);
  242. } while (re.lastIndex > 0);
  243. const jsonUrl = imgUrl.substring(0, li - 1) + ".json";
  244. const onerror = () => {
  245. Logger.Error("JSON ERROR: Unable to load JSON file.");
  246. this._fromPacked = false;
  247. this._packedAndReady = false;
  248. };
  249. const onload = (data) => {
  250. try {
  251. const celldata = JSON.parse(data);
  252. const spritemap = Reflect.ownKeys(celldata.frames);
  253. this._spriteMap = spritemap;
  254. this._packedAndReady = true;
  255. this._cellData = celldata.frames;
  256. }
  257. catch (e) {
  258. this._fromPacked = false;
  259. this._packedAndReady = false;
  260. throw new Error("Invalid JSON format. Please check documentation for format specifications.");
  261. }
  262. };
  263. Tools.LoadFile(jsonUrl, onload, undefined, undefined, false, onerror);
  264. }
  265. }
  266. _checkTextureAlpha(sprite, ray, distance, min, max) {
  267. if (!sprite.useAlphaForPicking || !this.texture) {
  268. return true;
  269. }
  270. const textureSize = this.texture.getSize();
  271. if (!this._textureContent) {
  272. this._textureContent = new Uint8Array(textureSize.width * textureSize.height * 4);
  273. this.texture.readPixels(0, 0, this._textureContent);
  274. }
  275. const contactPoint = TmpVectors.Vector3[0];
  276. contactPoint.copyFrom(ray.direction);
  277. contactPoint.normalize();
  278. contactPoint.scaleInPlace(distance);
  279. contactPoint.addInPlace(ray.origin);
  280. const contactPointU = (contactPoint.x - min.x) / (max.x - min.x);
  281. const contactPointV = 1.0 - (contactPoint.y - min.y) / (max.y - min.y);
  282. const u = (sprite._xOffset * textureSize.width + contactPointU * sprite._xSize) | 0;
  283. const v = (sprite._yOffset * textureSize.height + contactPointV * sprite._ySize) | 0;
  284. const alpha = this._textureContent[(u + v * textureSize.width) * 4 + 3];
  285. return alpha > 0.5;
  286. }
  287. /**
  288. * Intersects the sprites with a ray
  289. * @param ray defines the ray to intersect with
  290. * @param camera defines the current active camera
  291. * @param predicate defines a predicate used to select candidate sprites
  292. * @param fastCheck defines if a fast check only must be done (the first potential sprite is will be used and not the closer)
  293. * @returns null if no hit or a PickingInfo
  294. */
  295. intersects(ray, camera, predicate, fastCheck) {
  296. const count = Math.min(this.capacity, this.sprites.length);
  297. const min = Vector3.Zero();
  298. const max = Vector3.Zero();
  299. let distance = Number.MAX_VALUE;
  300. let currentSprite = null;
  301. const pickedPoint = TmpVectors.Vector3[0];
  302. const cameraSpacePosition = TmpVectors.Vector3[1];
  303. const cameraView = camera.getViewMatrix();
  304. let activeRay = ray;
  305. let pickedRay = ray;
  306. for (let index = 0; index < count; index++) {
  307. const sprite = this.sprites[index];
  308. if (!sprite) {
  309. continue;
  310. }
  311. if (predicate) {
  312. if (!predicate(sprite)) {
  313. continue;
  314. }
  315. }
  316. else if (!sprite.isPickable) {
  317. continue;
  318. }
  319. Vector3.TransformCoordinatesToRef(sprite.position, cameraView, cameraSpacePosition);
  320. if (sprite.angle) {
  321. // Create a rotation matrix to rotate the ray to the sprite's rotation
  322. Matrix.TranslationToRef(-cameraSpacePosition.x, -cameraSpacePosition.y, 0, TmpVectors.Matrix[1]);
  323. Matrix.TranslationToRef(cameraSpacePosition.x, cameraSpacePosition.y, 0, TmpVectors.Matrix[2]);
  324. Matrix.RotationZToRef(-sprite.angle, TmpVectors.Matrix[3]);
  325. // inv translation x rotation x translation
  326. TmpVectors.Matrix[1].multiplyToRef(TmpVectors.Matrix[3], TmpVectors.Matrix[4]);
  327. TmpVectors.Matrix[4].multiplyToRef(TmpVectors.Matrix[2], TmpVectors.Matrix[0]);
  328. activeRay = ray.clone();
  329. Vector3.TransformCoordinatesToRef(ray.origin, TmpVectors.Matrix[0], activeRay.origin);
  330. Vector3.TransformNormalToRef(ray.direction, TmpVectors.Matrix[0], activeRay.direction);
  331. }
  332. else {
  333. activeRay = ray;
  334. }
  335. min.copyFromFloats(cameraSpacePosition.x - sprite.width / 2, cameraSpacePosition.y - sprite.height / 2, cameraSpacePosition.z);
  336. max.copyFromFloats(cameraSpacePosition.x + sprite.width / 2, cameraSpacePosition.y + sprite.height / 2, cameraSpacePosition.z);
  337. if (activeRay.intersectsBoxMinMax(min, max)) {
  338. const currentDistance = Vector3.Distance(cameraSpacePosition, activeRay.origin);
  339. if (distance > currentDistance) {
  340. if (!this._checkTextureAlpha(sprite, activeRay, currentDistance, min, max)) {
  341. continue;
  342. }
  343. pickedRay = activeRay;
  344. distance = currentDistance;
  345. currentSprite = sprite;
  346. if (fastCheck) {
  347. break;
  348. }
  349. }
  350. }
  351. }
  352. if (currentSprite) {
  353. const result = new PickingInfo();
  354. cameraView.invertToRef(TmpVectors.Matrix[0]);
  355. result.hit = true;
  356. result.pickedSprite = currentSprite;
  357. result.distance = distance;
  358. // Get picked point
  359. const direction = TmpVectors.Vector3[2];
  360. direction.copyFrom(pickedRay.direction);
  361. direction.normalize();
  362. direction.scaleInPlace(distance);
  363. pickedRay.origin.addToRef(direction, pickedPoint);
  364. result.pickedPoint = Vector3.TransformCoordinates(pickedPoint, TmpVectors.Matrix[0]);
  365. return result;
  366. }
  367. return null;
  368. }
  369. /**
  370. * Intersects the sprites with a ray
  371. * @param ray defines the ray to intersect with
  372. * @param camera defines the current active camera
  373. * @param predicate defines a predicate used to select candidate sprites
  374. * @returns null if no hit or a PickingInfo array
  375. */
  376. multiIntersects(ray, camera, predicate) {
  377. const count = Math.min(this.capacity, this.sprites.length);
  378. const min = Vector3.Zero();
  379. const max = Vector3.Zero();
  380. let distance;
  381. const results = [];
  382. const pickedPoint = TmpVectors.Vector3[0].copyFromFloats(0, 0, 0);
  383. const cameraSpacePosition = TmpVectors.Vector3[1].copyFromFloats(0, 0, 0);
  384. const cameraView = camera.getViewMatrix();
  385. for (let index = 0; index < count; index++) {
  386. const sprite = this.sprites[index];
  387. if (!sprite) {
  388. continue;
  389. }
  390. if (predicate) {
  391. if (!predicate(sprite)) {
  392. continue;
  393. }
  394. }
  395. else if (!sprite.isPickable) {
  396. continue;
  397. }
  398. Vector3.TransformCoordinatesToRef(sprite.position, cameraView, cameraSpacePosition);
  399. min.copyFromFloats(cameraSpacePosition.x - sprite.width / 2, cameraSpacePosition.y - sprite.height / 2, cameraSpacePosition.z);
  400. max.copyFromFloats(cameraSpacePosition.x + sprite.width / 2, cameraSpacePosition.y + sprite.height / 2, cameraSpacePosition.z);
  401. if (ray.intersectsBoxMinMax(min, max)) {
  402. distance = Vector3.Distance(cameraSpacePosition, ray.origin);
  403. if (!this._checkTextureAlpha(sprite, ray, distance, min, max)) {
  404. continue;
  405. }
  406. const result = new PickingInfo();
  407. results.push(result);
  408. cameraView.invertToRef(TmpVectors.Matrix[0]);
  409. result.hit = true;
  410. result.pickedSprite = sprite;
  411. result.distance = distance;
  412. // Get picked point
  413. const direction = TmpVectors.Vector3[2];
  414. direction.copyFrom(ray.direction);
  415. direction.normalize();
  416. direction.scaleInPlace(distance);
  417. ray.origin.addToRef(direction, pickedPoint);
  418. result.pickedPoint = Vector3.TransformCoordinates(pickedPoint, TmpVectors.Matrix[0]);
  419. }
  420. }
  421. return results;
  422. }
  423. /**
  424. * Render all child sprites
  425. */
  426. render() {
  427. // Check
  428. if (this._fromPacked && (!this._packedAndReady || !this._spriteMap || !this._cellData)) {
  429. return;
  430. }
  431. const engine = this._scene.getEngine();
  432. const deltaTime = engine.getDeltaTime();
  433. if (this._packedAndReady) {
  434. this._spriteRenderer.render(this.sprites, deltaTime, this._scene.getViewMatrix(), this._scene.getProjectionMatrix(), this._customUpdate);
  435. }
  436. else {
  437. this._spriteRenderer.render(this.sprites, deltaTime, this._scene.getViewMatrix(), this._scene.getProjectionMatrix());
  438. }
  439. }
  440. /**
  441. * Rebuilds the manager (after a context lost, for eg)
  442. */
  443. rebuild() {
  444. this._spriteRenderer?.rebuild();
  445. }
  446. /**
  447. * Release associated resources
  448. */
  449. dispose() {
  450. if (this._spriteRenderer) {
  451. this._spriteRenderer.dispose();
  452. this._spriteRenderer = null;
  453. }
  454. this._textureContent = null;
  455. // Remove from scene
  456. if (this._scene.spriteManagers) {
  457. const index = this._scene.spriteManagers.indexOf(this);
  458. this._scene.spriteManagers.splice(index, 1);
  459. }
  460. // Callback
  461. this.onDisposeObservable.notifyObservers(this);
  462. this.onDisposeObservable.clear();
  463. this.metadata = null;
  464. }
  465. /**
  466. * Serializes the sprite manager to a JSON object
  467. * @param serializeTexture defines if the texture must be serialized as well
  468. * @returns the JSON object
  469. */
  470. serialize(serializeTexture = false) {
  471. const serializationObject = {};
  472. serializationObject.name = this.name;
  473. serializationObject.capacity = this.capacity;
  474. serializationObject.cellWidth = this.cellWidth;
  475. serializationObject.cellHeight = this.cellHeight;
  476. serializationObject.fogEnabled = this.fogEnabled;
  477. serializationObject.blendMode = this.blendMode;
  478. serializationObject.disableDepthWrite = this.disableDepthWrite;
  479. serializationObject.pixelPerfect = this.pixelPerfect;
  480. if (this.texture) {
  481. if (serializeTexture) {
  482. serializationObject.texture = this.texture.serialize();
  483. }
  484. else {
  485. serializationObject.textureUrl = this.texture.name;
  486. serializationObject.invertY = this.texture._invertY;
  487. }
  488. }
  489. serializationObject.sprites = [];
  490. for (const sprite of this.sprites) {
  491. serializationObject.sprites.push(sprite.serialize());
  492. }
  493. serializationObject.metadata = this.metadata;
  494. return serializationObject;
  495. }
  496. /**
  497. * Parses a JSON object to create a new sprite manager.
  498. * @param parsedManager The JSON object to parse
  499. * @param scene The scene to create the sprite manager
  500. * @param rootUrl The root url to use to load external dependencies like texture
  501. * @returns the new sprite manager
  502. */
  503. static Parse(parsedManager, scene, rootUrl) {
  504. const manager = new SpriteManager(parsedManager.name, "", parsedManager.capacity, {
  505. width: parsedManager.cellWidth,
  506. height: parsedManager.cellHeight,
  507. }, scene);
  508. if (parsedManager.fogEnabled !== undefined) {
  509. manager.fogEnabled = parsedManager.fogEnabled;
  510. }
  511. if (parsedManager.blendMode !== undefined) {
  512. manager.blendMode = parsedManager.blendMode;
  513. }
  514. if (parsedManager.disableDepthWrite !== undefined) {
  515. manager.disableDepthWrite = parsedManager.disableDepthWrite;
  516. }
  517. if (parsedManager.pixelPerfect !== undefined) {
  518. manager.pixelPerfect = parsedManager.pixelPerfect;
  519. }
  520. if (parsedManager.metadata !== undefined) {
  521. manager.metadata = parsedManager.metadata;
  522. }
  523. if (parsedManager.texture) {
  524. manager.texture = Texture.Parse(parsedManager.texture, scene, rootUrl);
  525. }
  526. else if (parsedManager.textureName) {
  527. manager.texture = new Texture(rootUrl + parsedManager.textureUrl, scene, false, parsedManager.invertY !== undefined ? parsedManager.invertY : true);
  528. }
  529. for (const parsedSprite of parsedManager.sprites) {
  530. Sprite.Parse(parsedSprite, manager);
  531. }
  532. return manager;
  533. }
  534. /**
  535. * Creates a sprite manager from a snippet saved in a remote file
  536. * @param name defines the name of the sprite manager to create (can be null or empty to use the one from the json data)
  537. * @param url defines the url to load from
  538. * @param scene defines the hosting scene
  539. * @param rootUrl defines the root URL to use to load textures and relative dependencies
  540. * @returns a promise that will resolve to the new sprite manager
  541. */
  542. static ParseFromFileAsync(name, url, scene, rootUrl = "") {
  543. return new Promise((resolve, reject) => {
  544. const request = new WebRequest();
  545. request.addEventListener("readystatechange", () => {
  546. if (request.readyState == 4) {
  547. if (request.status == 200) {
  548. const serializationObject = JSON.parse(request.responseText);
  549. const output = SpriteManager.Parse(serializationObject, scene || EngineStore.LastCreatedScene, rootUrl);
  550. if (name) {
  551. output.name = name;
  552. }
  553. resolve(output);
  554. }
  555. else {
  556. reject("Unable to load the sprite manager");
  557. }
  558. }
  559. });
  560. request.open("GET", url);
  561. request.send();
  562. });
  563. }
  564. /**
  565. * Creates a sprite manager from a snippet saved by the sprite editor
  566. * @param snippetId defines the snippet to load (can be set to _BLANK to create a default one)
  567. * @param scene defines the hosting scene
  568. * @param rootUrl defines the root URL to use to load textures and relative dependencies
  569. * @returns a promise that will resolve to the new sprite manager
  570. */
  571. static ParseFromSnippetAsync(snippetId, scene, rootUrl = "") {
  572. if (snippetId === "_BLANK") {
  573. return Promise.resolve(new SpriteManager("Default sprite manager", "//playground.babylonjs.com/textures/player.png", 500, 64, scene));
  574. }
  575. return new Promise((resolve, reject) => {
  576. const request = new WebRequest();
  577. request.addEventListener("readystatechange", () => {
  578. if (request.readyState == 4) {
  579. if (request.status == 200) {
  580. const snippet = JSON.parse(JSON.parse(request.responseText).jsonPayload);
  581. const serializationObject = JSON.parse(snippet.spriteManager);
  582. const output = SpriteManager.Parse(serializationObject, scene || EngineStore.LastCreatedScene, rootUrl);
  583. output.snippetId = snippetId;
  584. resolve(output);
  585. }
  586. else {
  587. reject("Unable to load the snippet " + snippetId);
  588. }
  589. }
  590. });
  591. request.open("GET", this.SnippetUrl + "/" + snippetId.replace(/#/g, "/"));
  592. request.send();
  593. });
  594. }
  595. }
  596. /** Define the Url to load snippets */
  597. SpriteManager.SnippetUrl = `https://snippet.babylonjs.com`;
  598. /**
  599. * Creates a sprite manager from a snippet saved by the sprite editor
  600. * @deprecated Please use ParseFromSnippetAsync instead
  601. * @param snippetId defines the snippet to load (can be set to _BLANK to create a default one)
  602. * @param scene defines the hosting scene
  603. * @param rootUrl defines the root URL to use to load textures and relative dependencies
  604. * @returns a promise that will resolve to the new sprite manager
  605. */
  606. SpriteManager.CreateFromSnippetAsync = SpriteManager.ParseFromSnippetAsync;
  607. //# sourceMappingURL=spriteManager.js.map