@bloopjs/toodle 0.0.102 → 0.0.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mod.js CHANGED
@@ -3611,7 +3611,7 @@ class SceneNode {
3611
3611
  #transform;
3612
3612
  #matrix = mat3.identity();
3613
3613
  #renderComponent = null;
3614
- #idealSize = null;
3614
+ #size = null;
3615
3615
  #positionProxy;
3616
3616
  #scaleProxy;
3617
3617
  #cache = null;
@@ -3623,7 +3623,7 @@ class SceneNode {
3623
3623
  this.#transform = {
3624
3624
  position: opts?.position ?? { x: 0, y: 0 },
3625
3625
  scale: { x: 1, y: 1 },
3626
- size: opts?.idealSize ?? { width: 1, height: 1 },
3626
+ size: opts?.size ?? { width: 1, height: 1 },
3627
3627
  rotation: opts?.rotationRadians ?? 0
3628
3628
  };
3629
3629
  if (opts?.scale)
@@ -3635,7 +3635,7 @@ class SceneNode {
3635
3635
  this.#layer = opts?.layer ?? null;
3636
3636
  this.#isActive = opts?.isActive ?? true;
3637
3637
  this.label = opts?.label ?? undefined;
3638
- this.#idealSize = opts?.idealSize ?? null;
3638
+ this.#size = opts?.size ?? null;
3639
3639
  this.#key = opts?.key ?? null;
3640
3640
  for (const kid of opts?.kids ?? []) {
3641
3641
  this.add(kid);
@@ -3745,19 +3745,19 @@ class SceneNode {
3745
3745
  }
3746
3746
  this.setDirty();
3747
3747
  }
3748
- set idealSize(value) {
3749
- this.#idealSize = value;
3748
+ set size(value) {
3749
+ this.#size = value;
3750
3750
  this.setDirty();
3751
3751
  }
3752
3752
  get size() {
3753
- return this.#idealSize;
3753
+ return this.#size;
3754
3754
  }
3755
3755
  get aspectRatio() {
3756
- if (!this.#idealSize) {
3756
+ if (!this.#size) {
3757
3757
  console.warn("Attempted to get aspect ratio of a node with no ideal size");
3758
3758
  return 1;
3759
3759
  }
3760
- return this.#idealSize.width / this.#idealSize.height;
3760
+ return this.#size.width / this.#size.height;
3761
3761
  }
3762
3762
  get isActive() {
3763
3763
  if (!this.#cache?.isActive) {
@@ -3964,7 +3964,7 @@ class QuadNode extends SceneNode {
3964
3964
  #writeInstance;
3965
3965
  constructor(options, matrixPool) {
3966
3966
  assert(options.shader, "QuadNode requires a shader to be explicitly provided");
3967
- assert(options.idealSize, "QuadNode requires an ideal size to be explicitly provided");
3967
+ assert(options.size, "QuadNode requires a size to be explicitly provided");
3968
3968
  assert(options.atlasCoords, "QuadNode requires atlas coords to be explicitly provided");
3969
3969
  options.render ??= {
3970
3970
  shader: options.shader,
@@ -17570,8 +17570,8 @@ class TextNode extends SceneNode {
17570
17570
  throw new Error(`Text: ${text} exceeds ${shader.maxCharCount} characters. Try using fewer characters or increase the limit in Toodle.attach.`);
17571
17571
  }
17572
17572
  const em2px = shader.font.lineHeight / (opts.fontSize ?? DEFAULT_FONT_SIZE);
17573
- if (!opts.shrinkToFit && !opts.idealSize) {
17574
- opts.idealSize = { width: width / em2px, height: height / em2px };
17573
+ if (!opts.shrinkToFit && !opts.size) {
17574
+ opts.size = { width: width / em2px, height: height / em2px };
17575
17575
  }
17576
17576
  super({
17577
17577
  ...opts,
@@ -18088,6 +18088,276 @@ class Pool {
18088
18088
  this.#index = 0;
18089
18089
  }
18090
18090
  }
18091
+ // src/textures/Bundles.ts
18092
+ class Bundles {
18093
+ #bundles = new Map;
18094
+ #textures = new Map;
18095
+ #atlasSize;
18096
+ constructor(options = {}) {
18097
+ this.#atlasSize = options.atlasSize ?? 4096;
18098
+ }
18099
+ async registerAtlasBundle(bundleId, opts) {
18100
+ const atlases = [];
18101
+ for (const atlas of opts.atlases) {
18102
+ const jsonUrl = atlas.json ?? new URL(atlas.png.toString().replace(".png", ".json"), atlas.png.origin);
18103
+ const pngUrl = atlas.png ?? new URL(atlas.json.toString().replace(".json", ".png"), atlas.json.origin);
18104
+ const atlasDef = await (await fetch(jsonUrl)).json();
18105
+ const bitmap = !opts.rg8 ? await this.#getBitmapFromUrl(pngUrl) : await createImageBitmap(new ImageData(1, 1));
18106
+ let rg8Bytes;
18107
+ if (opts.rg8) {
18108
+ const rg8url = new URL(pngUrl.toString().replace(".png", ".rg8.gz"), pngUrl.origin);
18109
+ rg8Bytes = await this.#fetchRg8Bytes(rg8url);
18110
+ }
18111
+ const cpuTextureAtlas = {
18112
+ texture: bitmap,
18113
+ rg8Bytes,
18114
+ textureRegions: new Map,
18115
+ width: opts.rg8 ? this.#atlasSize : bitmap.width,
18116
+ height: opts.rg8 ? this.#atlasSize : bitmap.height
18117
+ };
18118
+ for (const [assetId, frame] of Object.entries(atlasDef.frames)) {
18119
+ const textureRegion = this.#parsePixiFrame(frame, cpuTextureAtlas.width, cpuTextureAtlas.height);
18120
+ cpuTextureAtlas.textureRegions.set(assetId, textureRegion);
18121
+ }
18122
+ atlases.push(cpuTextureAtlas);
18123
+ }
18124
+ this.#bundles.set(bundleId, {
18125
+ atlases,
18126
+ atlasIndices: [],
18127
+ isLoaded: false
18128
+ });
18129
+ return bundleId;
18130
+ }
18131
+ registerDynamicBundle(bundleId, atlases) {
18132
+ this.#bundles.set(bundleId, {
18133
+ atlases,
18134
+ atlasIndices: [],
18135
+ isLoaded: false
18136
+ });
18137
+ }
18138
+ hasBundle(bundleId) {
18139
+ return this.#bundles.has(bundleId);
18140
+ }
18141
+ isBundleLoaded(bundleId) {
18142
+ const bundle = this.#bundles.get(bundleId);
18143
+ return bundle?.isLoaded ?? false;
18144
+ }
18145
+ getBundleAtlasIndices(bundleId) {
18146
+ const bundle = this.#bundles.get(bundleId);
18147
+ return bundle?.atlasIndices ?? [];
18148
+ }
18149
+ setBundleLoaded(bundleId, atlasIndices) {
18150
+ const bundle = this.#bundles.get(bundleId);
18151
+ if (!bundle) {
18152
+ throw new Error(`Bundle ${bundleId} not found`);
18153
+ }
18154
+ bundle.atlasIndices = atlasIndices;
18155
+ bundle.isLoaded = true;
18156
+ }
18157
+ markBundleLoaded(bundleId, atlasIndices) {
18158
+ const bundle = this.#bundles.get(bundleId);
18159
+ if (!bundle) {
18160
+ throw new Error(`Bundle ${bundleId} not found`);
18161
+ }
18162
+ if (bundle.isLoaded) {
18163
+ console.warn(`Bundle ${bundleId} is already loaded.`);
18164
+ return;
18165
+ }
18166
+ const indices = atlasIndices ?? bundle.atlases.map(() => this.#getNextAtlasIndex());
18167
+ if (indices.length !== bundle.atlases.length) {
18168
+ throw new Error(`Expected ${bundle.atlases.length} atlas indices, got ${indices.length}`);
18169
+ }
18170
+ for (let i3 = 0;i3 < bundle.atlases.length; i3++) {
18171
+ const atlas = bundle.atlases[i3];
18172
+ const atlasIndex = indices[i3];
18173
+ bundle.atlasIndices.push(atlasIndex);
18174
+ for (const [id, region] of atlas.textureRegions) {
18175
+ const coords = { ...region, atlasIndex };
18176
+ const existing = this.#textures.get(id);
18177
+ if (existing) {
18178
+ existing.push(coords);
18179
+ } else {
18180
+ this.#textures.set(id, [coords]);
18181
+ }
18182
+ }
18183
+ }
18184
+ bundle.isLoaded = true;
18185
+ }
18186
+ unloadBundle(bundleId) {
18187
+ const bundle = this.#bundles.get(bundleId);
18188
+ if (!bundle) {
18189
+ throw new Error(`Bundle ${bundleId} not found`);
18190
+ }
18191
+ if (!bundle.isLoaded) {
18192
+ console.warn(`Bundle ${bundleId} is not loaded.`);
18193
+ return;
18194
+ }
18195
+ for (const atlasIndex of bundle.atlasIndices) {
18196
+ for (const [id, coords] of this.#textures.entries()) {
18197
+ const indexToRemove = coords.findIndex((coord) => coord.atlasIndex === atlasIndex);
18198
+ if (indexToRemove !== -1) {
18199
+ coords.splice(indexToRemove, 1);
18200
+ }
18201
+ if (!coords.length) {
18202
+ this.#textures.delete(id);
18203
+ }
18204
+ }
18205
+ }
18206
+ bundle.isLoaded = false;
18207
+ bundle.atlasIndices = [];
18208
+ }
18209
+ get textures() {
18210
+ return this.#textures;
18211
+ }
18212
+ get textureIds() {
18213
+ return Array.from(this.#textures.keys());
18214
+ }
18215
+ getAtlasCoords(id) {
18216
+ const coords = this.#textures.get(id);
18217
+ if (!coords) {
18218
+ throw new Error(`Texture ${id} not found. Have you registered and loaded a bundle containing this texture?`);
18219
+ }
18220
+ return coords;
18221
+ }
18222
+ setAtlasCoords(id, coords) {
18223
+ const oldCoords = this.#textures.get(id);
18224
+ if (!oldCoords)
18225
+ return;
18226
+ const indexToModify = oldCoords.findIndex((coord) => coord.atlasIndex === coords.atlasIndex);
18227
+ if (indexToModify === -1)
18228
+ return;
18229
+ oldCoords[indexToModify] = coords;
18230
+ this.#textures.set(id, oldCoords);
18231
+ }
18232
+ addTextureEntry(id, coords) {
18233
+ const existing = this.#textures.get(id);
18234
+ if (existing) {
18235
+ existing.push(coords);
18236
+ } else {
18237
+ this.#textures.set(id, [coords]);
18238
+ }
18239
+ }
18240
+ removeTextureEntriesForAtlas(atlasIndex) {
18241
+ for (const [id, coords] of this.#textures.entries()) {
18242
+ const indexToRemove = coords.findIndex((coord) => coord.atlasIndex === atlasIndex);
18243
+ if (indexToRemove !== -1) {
18244
+ coords.splice(indexToRemove, 1);
18245
+ }
18246
+ if (!coords.length) {
18247
+ this.#textures.delete(id);
18248
+ }
18249
+ }
18250
+ }
18251
+ getTextureRegion(id) {
18252
+ const coords = this.#textures.get(id);
18253
+ if (!coords || coords.length === 0)
18254
+ return;
18255
+ const { atlasIndex: _3, ...region } = coords[0];
18256
+ return region;
18257
+ }
18258
+ getTextureOffset(id) {
18259
+ const coords = this.#textures.get(id);
18260
+ if (!coords) {
18261
+ throw new Error(`Texture ${id} not found. Have you registered and loaded a bundle containing this texture?`);
18262
+ }
18263
+ return coords[0].cropOffset;
18264
+ }
18265
+ getSize(id) {
18266
+ const coords = this.getAtlasCoords(id);
18267
+ const uvScale = coords[0].uvScale;
18268
+ return {
18269
+ width: uvScale.width * this.#atlasSize,
18270
+ height: uvScale.height * this.#atlasSize
18271
+ };
18272
+ }
18273
+ getCroppedSize(id) {
18274
+ const coords = this.getAtlasCoords(id);
18275
+ const uvScaleCropped = coords[0].uvScaleCropped;
18276
+ if (uvScaleCropped) {
18277
+ return {
18278
+ width: uvScaleCropped.width * this.#atlasSize,
18279
+ height: uvScaleCropped.height * this.#atlasSize
18280
+ };
18281
+ }
18282
+ return this.getSize(id);
18283
+ }
18284
+ hasTexture(id) {
18285
+ return this.#textures.has(id);
18286
+ }
18287
+ getRegisteredBundleIds() {
18288
+ return Array.from(this.#bundles.keys());
18289
+ }
18290
+ getLoadedBundleIds() {
18291
+ return Array.from(this.#bundles.entries()).filter(([, bundle]) => bundle.isLoaded).map(([id]) => id);
18292
+ }
18293
+ getBundleAtlases(bundleId) {
18294
+ const bundle = this.#bundles.get(bundleId);
18295
+ if (!bundle) {
18296
+ throw new Error(`Bundle ${bundleId} not found`);
18297
+ }
18298
+ return bundle.atlases;
18299
+ }
18300
+ get atlasSize() {
18301
+ return this.#atlasSize;
18302
+ }
18303
+ #parsePixiFrame(frame, atlasWidth, atlasHeight) {
18304
+ const leftCrop = frame.spriteSourceSize.x;
18305
+ const rightCrop = frame.sourceSize.w - frame.spriteSourceSize.x - frame.spriteSourceSize.w;
18306
+ const topCrop = frame.spriteSourceSize.y;
18307
+ const bottomCrop = frame.sourceSize.h - frame.spriteSourceSize.y - frame.spriteSourceSize.h;
18308
+ return {
18309
+ cropOffset: {
18310
+ x: leftCrop - rightCrop,
18311
+ y: bottomCrop - topCrop
18312
+ },
18313
+ originalSize: {
18314
+ width: frame.sourceSize.w,
18315
+ height: frame.sourceSize.h
18316
+ },
18317
+ uvOffset: {
18318
+ x: frame.frame.x / atlasWidth,
18319
+ y: frame.frame.y / atlasHeight
18320
+ },
18321
+ uvScale: {
18322
+ width: frame.sourceSize.w / atlasWidth,
18323
+ height: frame.sourceSize.h / atlasHeight
18324
+ },
18325
+ uvScaleCropped: {
18326
+ width: frame.frame.w / atlasWidth,
18327
+ height: frame.frame.h / atlasHeight
18328
+ }
18329
+ };
18330
+ }
18331
+ async#getBitmapFromUrl(url) {
18332
+ const response = await fetch(url);
18333
+ const blob = await response.blob();
18334
+ return createImageBitmap(blob);
18335
+ }
18336
+ async#fetchRg8Bytes(url) {
18337
+ const response = await fetch(url);
18338
+ const enc = (response.headers.get("content-encoding") || "").toLowerCase();
18339
+ if (enc.includes("gzip") || enc.includes("br") || enc.includes("deflate")) {
18340
+ return new Uint8Array(await response.arrayBuffer());
18341
+ }
18342
+ if (!response.body) {
18343
+ throw new Error("Response body of rg8 file is null");
18344
+ }
18345
+ const ds = new DecompressionStream("gzip");
18346
+ const ab = await new Response(response.body.pipeThrough(ds)).arrayBuffer();
18347
+ return new Uint8Array(ab);
18348
+ }
18349
+ #getNextAtlasIndex() {
18350
+ let maxIndex = -1;
18351
+ for (const bundle of this.#bundles.values()) {
18352
+ for (const idx of bundle.atlasIndices) {
18353
+ if (idx > maxIndex)
18354
+ maxIndex = idx;
18355
+ }
18356
+ }
18357
+ return maxIndex + 1;
18358
+ }
18359
+ }
18360
+
18091
18361
  // src/textures/pixel-scraping.wgsl.ts
18092
18362
  var pixel_scraping_wgsl_default = `
18093
18363
  // ==============================
@@ -18650,18 +18920,19 @@ async function textureToBitmap(device, texture, width, height) {
18650
18920
  // src/textures/AssetManager.ts
18651
18921
  class AssetManager {
18652
18922
  textureAtlas;
18923
+ bundles;
18653
18924
  #device;
18654
18925
  #presentationFormat;
18655
- #bundles = new Map;
18656
- #textures = new Map;
18657
18926
  #fonts = new Map;
18658
18927
  #cropComputeShader;
18659
18928
  #limits;
18660
18929
  #availableIndices = new Set;
18661
- constructor(device, presentationFormat, limits, format = "rgba8unorm") {
18930
+ constructor(device, presentationFormat, limits, options = {}) {
18662
18931
  this.#device = device;
18663
18932
  this.#presentationFormat = presentationFormat;
18664
18933
  this.#limits = limits;
18934
+ this.bundles = options.bundles ?? new Bundles({ atlasSize: limits.textureSize });
18935
+ const format = options.format ?? "rgba8unorm";
18665
18936
  this.textureAtlas = device.createTexture({
18666
18937
  label: "Asset Manager Atlas Texture",
18667
18938
  size: [
@@ -18694,16 +18965,16 @@ class AssetManager {
18694
18965
  return this.getSize(id);
18695
18966
  }
18696
18967
  isCropped(id) {
18697
- if (!this.#textures.has(id)) {
18968
+ if (!this.bundles.hasTexture(id)) {
18698
18969
  throw new Error(`Texture ${id} not found in atlas. Have you called toodle.loadTextures with this id or toodle.loadBundle with a bundle that contains it?`);
18699
18970
  }
18700
- return this.#textures.get(id)[0].uvScaleCropped === undefined;
18971
+ return this.bundles.getAtlasCoords(id)[0].uvScaleCropped === undefined;
18701
18972
  }
18702
18973
  get textures() {
18703
- return this.#textures;
18974
+ return this.bundles.textures;
18704
18975
  }
18705
18976
  get textureIds() {
18706
- return Array.from(this.#textures.keys());
18977
+ return this.bundles.textureIds;
18707
18978
  }
18708
18979
  async loadTextures(opts) {
18709
18980
  await Promise.all(Object.entries(opts).map(([id, url]) => this.loadTexture(id, url, opts)));
@@ -18730,7 +19001,7 @@ class AssetManager {
18730
19001
  },
18731
19002
  atlasIndex
18732
19003
  };
18733
- this.#textures.set(id, [coords]);
19004
+ this.bundles.addTextureEntry(id, coords);
18734
19005
  this.#availableIndices.delete(atlasIndex);
18735
19006
  textureWrapper.texture.destroy();
18736
19007
  return { id, coords };
@@ -18741,38 +19012,39 @@ class AssetManager {
18741
19012
  } else {
18742
19013
  await this.#registerBundleFromAtlases(bundleId, opts);
18743
19014
  }
18744
- if (opts.autoLoad) {
19015
+ const autoLoad = opts.autoLoad ?? true;
19016
+ if (autoLoad) {
18745
19017
  await this.loadBundle(bundleId);
18746
19018
  }
18747
19019
  return bundleId;
18748
19020
  }
18749
19021
  async loadBundle(bundleId) {
18750
- const bundle = this.#bundles.get(bundleId);
18751
- if (!bundle) {
19022
+ if (!this.bundles.hasBundle(bundleId)) {
18752
19023
  throw new Error(`Bundle ${bundleId} not found`);
18753
19024
  }
18754
- if (bundle.isLoaded) {
19025
+ if (this.bundles.isBundleLoaded(bundleId)) {
18755
19026
  console.warn(`Bundle ${bundleId} is already loaded.`);
18756
19027
  return;
18757
19028
  }
18758
- for (const atlas of bundle.atlases) {
19029
+ const atlases = this.bundles.getBundleAtlases(bundleId);
19030
+ const atlasIndices = [];
19031
+ for (const atlas of atlases) {
18759
19032
  const atlasIndex = await this.extra.loadAtlas(atlas);
18760
- bundle.atlasIndices.push(atlasIndex);
19033
+ atlasIndices.push(atlasIndex);
18761
19034
  }
18762
- bundle.isLoaded = true;
19035
+ this.bundles.setBundleLoaded(bundleId, atlasIndices);
18763
19036
  }
18764
19037
  async unloadBundle(bundleId) {
18765
- const bundle = this.#bundles.get(bundleId);
18766
- if (!bundle) {
19038
+ if (!this.bundles.hasBundle(bundleId)) {
18767
19039
  throw new Error(`Bundle ${bundleId} not found`);
18768
19040
  }
18769
- if (!bundle.isLoaded) {
19041
+ if (!this.bundles.isBundleLoaded(bundleId)) {
18770
19042
  console.warn(`Bundle ${bundleId} is not loaded.`);
18771
19043
  return;
18772
19044
  }
18773
- await Promise.all(bundle.atlasIndices.map((atlasIndex) => this.extra.unloadAtlas(atlasIndex)));
18774
- bundle.isLoaded = false;
18775
- bundle.atlasIndices = [];
19045
+ const atlasIndices = this.bundles.getBundleAtlasIndices(bundleId);
19046
+ await Promise.all(atlasIndices.map((atlasIndex) => this.extra.unloadAtlas(atlasIndex)));
19047
+ this.bundles.unloadBundle(bundleId);
18776
19048
  }
18777
19049
  async loadFont(id, url, fallbackCharacter = "_") {
18778
19050
  const font = await MsdfFont.create(id, url);
@@ -18791,22 +19063,14 @@ class AssetManager {
18791
19063
  validateTextureReference(node) {
18792
19064
  if (!(node instanceof QuadNode) || node.isPrimitive || node instanceof JumboQuadNode)
18793
19065
  return;
18794
- const coords = this.#textures.get(node.textureId);
18795
- if (!coords || !coords.length) {
19066
+ if (!this.bundles.hasTexture(node.textureId)) {
18796
19067
  throw new Error(`Node ${node.id} references an invalid texture ${node.textureId}.`);
18797
19068
  }
19069
+ const coords = this.bundles.getAtlasCoords(node.textureId);
18798
19070
  if (coords.find((coord) => coord.atlasIndex === node.atlasCoords.atlasIndex))
18799
19071
  return;
18800
19072
  node.extra.setAtlasCoords(coords[0]);
18801
19073
  }
18802
- #addTexture(id, textureRegion, atlasIndex) {
18803
- this.#textures.set(id, [
18804
- {
18805
- ...textureRegion,
18806
- atlasIndex
18807
- }
18808
- ]);
18809
- }
18810
19074
  #createTextureFromImageBitmap(bitmap, name) {
18811
19075
  const texture = this.#device.createTexture({
18812
19076
  label: `${name} Intermediary Texture`,
@@ -18835,106 +19099,26 @@ class AssetManager {
18835
19099
  images.set(id, textureWrapper);
18836
19100
  }));
18837
19101
  const atlases = await packBitmapsToAtlas(images, this.#limits.textureSize, this.#device);
18838
- this.#bundles.set(bundleId, {
18839
- atlases,
18840
- atlasIndices: [],
18841
- isLoaded: false
18842
- });
19102
+ this.bundles.registerDynamicBundle(bundleId, atlases);
18843
19103
  }
18844
19104
  async#registerBundleFromAtlases(bundleId, opts) {
18845
- const atlases = [];
18846
- for (const atlas of opts.atlases) {
18847
- const jsonUrl = atlas.json ?? new URL(atlas.png.toString().replace(".png", ".json"), atlas.png.origin);
18848
- const pngUrl = atlas.png ?? new URL(atlas.json.toString().replace(".json", ".png"), atlas.json.origin);
18849
- const atlasDef = await (await fetch(jsonUrl)).json();
18850
- const bitmap = !opts.rg8 ? await getBitmapFromUrl(pngUrl) : await createImageBitmap(new ImageData(1, 1));
18851
- let rg8Bytes;
18852
- if (opts.rg8) {
18853
- const rg8url = new URL(pngUrl.toString().replace(".png", ".rg8.gz"), pngUrl.origin);
18854
- const rgBytes = await fetch(rg8url).then(async (r3) => {
18855
- const enc = (r3.headers.get("content-encoding") || "").toLowerCase();
18856
- if (enc.includes("gzip") || enc.includes("br") || enc.includes("deflate")) {
18857
- return new Uint8Array(await r3.arrayBuffer());
18858
- }
18859
- assert(r3.body, "Response body of rg8 file is null");
18860
- const ds = new DecompressionStream("gzip");
18861
- const ab = await new Response(r3.body.pipeThrough(ds)).arrayBuffer();
18862
- return new Uint8Array(ab);
18863
- });
18864
- rg8Bytes = rgBytes;
18865
- }
18866
- const cpuTextureAtlas = {
18867
- texture: bitmap,
18868
- rg8Bytes,
18869
- textureRegions: new Map,
18870
- width: opts.rg8 ? this.#limits.textureSize : bitmap.width,
18871
- height: opts.rg8 ? this.#limits.textureSize : bitmap.height
18872
- };
18873
- for (const [assetId, frame] of Object.entries(atlasDef.frames)) {
18874
- const leftCrop = frame.spriteSourceSize.x;
18875
- const rightCrop = frame.sourceSize.w - frame.spriteSourceSize.x - frame.spriteSourceSize.w;
18876
- const topCrop = frame.spriteSourceSize.y;
18877
- const bottomCrop = frame.sourceSize.h - frame.spriteSourceSize.y - frame.spriteSourceSize.h;
18878
- cpuTextureAtlas.textureRegions.set(assetId, {
18879
- cropOffset: {
18880
- x: leftCrop - rightCrop,
18881
- y: bottomCrop - topCrop
18882
- },
18883
- originalSize: {
18884
- width: frame.sourceSize.w,
18885
- height: frame.sourceSize.h
18886
- },
18887
- uvOffset: {
18888
- x: frame.frame.x / cpuTextureAtlas.width,
18889
- y: frame.frame.y / cpuTextureAtlas.height
18890
- },
18891
- uvScale: {
18892
- width: frame.sourceSize.w / cpuTextureAtlas.width,
18893
- height: frame.sourceSize.h / cpuTextureAtlas.height
18894
- },
18895
- uvScaleCropped: {
18896
- width: frame.frame.w / cpuTextureAtlas.width,
18897
- height: frame.frame.h / cpuTextureAtlas.height
18898
- }
18899
- });
18900
- }
18901
- atlases.push(cpuTextureAtlas);
18902
- }
18903
- this.#bundles.set(bundleId, {
18904
- atlases,
18905
- atlasIndices: [],
18906
- isLoaded: false
18907
- });
19105
+ await this.bundles.registerAtlasBundle(bundleId, opts);
18908
19106
  }
18909
19107
  extra = {
18910
19108
  getRegisteredBundleIds: () => {
18911
- return this.#bundles ? Array.from(this.#bundles.keys()) : [];
19109
+ return this.bundles.getRegisteredBundleIds();
18912
19110
  },
18913
19111
  getLoadedBundleIds: () => {
18914
- return Array.from(this.#bundles.entries()).filter(([, value]) => value.isLoaded).map(([key]) => key);
19112
+ return this.bundles.getLoadedBundleIds();
18915
19113
  },
18916
19114
  setAtlasCoords: (id, coords) => {
18917
- const oldCoords = this.#textures.get(id);
18918
- if (!oldCoords)
18919
- return;
18920
- const indexToModify = oldCoords.findIndex((coord) => coord.atlasIndex === coords.atlasIndex);
18921
- if (indexToModify === -1)
18922
- return;
18923
- oldCoords[indexToModify] = coords;
18924
- this.#textures.set(id, oldCoords);
19115
+ this.bundles.setAtlasCoords(id, coords);
18925
19116
  },
18926
19117
  getAtlasCoords: (id) => {
18927
- if (!this.#textures.has(id)) {
18928
- throw new Error(`Texture ${id} not found in atlas. Have you called toodle.loadBundle with a bundle that contains this id (or toodle.loadTextures with this id as a key)?`);
18929
- }
18930
- return this.#textures.get(id) ?? [];
19118
+ return this.bundles.getAtlasCoords(id);
18931
19119
  },
18932
19120
  getTextureOffset: (id) => {
18933
- const texture = this.#textures.get(id);
18934
- if (!texture) {
18935
- throw new Error(`Texture ${id} not found in atlas. Have you called toodle.loadTextures with this id or toodle.loadBundle with a bundle that contains it?`);
18936
- }
18937
- return texture[0].cropOffset;
19121
+ return this.bundles.getTextureOffset(id);
18938
19122
  },
18939
19123
  getAtlasUsage: () => {
18940
19124
  return {
@@ -18971,25 +19155,13 @@ class AssetManager {
18971
19155
  }, [atlas.texture.width, atlas.texture.height, 1]);
18972
19156
  }
18973
19157
  for (const [id, region] of atlas.textureRegions) {
18974
- const existing = this.#textures.get(id);
18975
- if (existing) {
18976
- existing.push({ ...region, atlasIndex });
18977
- } else
18978
- this.#addTexture(id, region, atlasIndex);
19158
+ this.bundles.addTextureEntry(id, { ...region, atlasIndex });
18979
19159
  }
18980
19160
  return atlasIndex;
18981
19161
  },
18982
19162
  unloadAtlas: async (atlasIndex) => {
18983
19163
  this.#availableIndices.add(atlasIndex);
18984
- for (const [id, coords] of this.#textures.entries()) {
18985
- const indexToModify = coords.findIndex((coord) => coord.atlasIndex === atlasIndex);
18986
- if (indexToModify !== -1) {
18987
- coords.splice(indexToModify, 1);
18988
- }
18989
- if (!coords.length) {
18990
- this.#textures.delete(id);
18991
- }
18992
- }
19164
+ this.bundles.removeTextureEntriesForAtlas(atlasIndex);
18993
19165
  }
18994
19166
  };
18995
19167
  #wrapBitmapToTexture(bitmap, name = "Unknown") {
@@ -19230,7 +19402,7 @@ class Toodle {
19230
19402
  }
19231
19403
  Quad(assetId, options = {}) {
19232
19404
  const assetManager = options.assetManager ?? this.assets;
19233
- options.idealSize ??= assetManager.getSize(assetId);
19405
+ options.size ??= assetManager.getSize(assetId);
19234
19406
  options.shader ??= this.#defaultQuadShader();
19235
19407
  options.atlasCoords ??= assetManager.extra.getAtlasCoords(assetId)[0];
19236
19408
  options.textureId ??= assetId;
@@ -19279,7 +19451,7 @@ class Toodle {
19279
19451
  width: originalSize.width,
19280
19452
  height: originalSize.height
19281
19453
  };
19282
- options.idealSize ??= {
19454
+ options.size ??= {
19283
19455
  width: originalSize.width,
19284
19456
  height: originalSize.height
19285
19457
  };
@@ -19296,7 +19468,7 @@ class Toodle {
19296
19468
  }
19297
19469
  shapes = {
19298
19470
  Rect: (options = {}) => {
19299
- options.idealSize ??= { width: 1, height: 1 };
19471
+ options.size ??= { width: 1, height: 1 };
19300
19472
  options.shader ??= this.#defaultQuadShader();
19301
19473
  options.atlasCoords ??= {
19302
19474
  atlasIndex: 1000,
@@ -19318,18 +19490,23 @@ class Toodle {
19318
19490
  }
19319
19491
  return quad;
19320
19492
  },
19321
- Circle: (options = {}) => {
19322
- options.idealSize ??= { width: 1, height: 1 };
19323
- options.shader ??= this.#defaultQuadShader();
19324
- options.atlasCoords ??= {
19325
- atlasIndex: 1001,
19326
- uvOffset: { x: 0, y: 0 },
19327
- uvScale: { width: 0, height: 0 },
19328
- cropOffset: { x: 0, y: 0 },
19329
- originalSize: { width: 1, height: 1 }
19493
+ Circle: (options = { radius: 50 }) => {
19494
+ const radius = options.radius ?? 50;
19495
+ const diameter = radius * 2;
19496
+ const quadOptions = {
19497
+ ...options,
19498
+ size: { width: diameter, height: diameter },
19499
+ shader: options.shader ?? this.#defaultQuadShader(),
19500
+ atlasCoords: options.atlasCoords ?? {
19501
+ atlasIndex: 1001,
19502
+ uvOffset: { x: 0, y: 0 },
19503
+ uvScale: { width: 0, height: 0 },
19504
+ cropOffset: { x: 0, y: 0 },
19505
+ originalSize: { width: 1, height: 1 }
19506
+ },
19507
+ assetManager: this.assets
19330
19508
  };
19331
- options.assetManager = this.assets;
19332
- const quad = new QuadNode(options, this.#matrixPool);
19509
+ const quad = new QuadNode(quadOptions, this.#matrixPool);
19333
19510
  if (options?.position) {
19334
19511
  quad.position = options.position;
19335
19512
  }
@@ -19358,7 +19535,7 @@ class Toodle {
19358
19535
  originalSize: { width: 1, height: 1 }
19359
19536
  },
19360
19537
  shader: options.shader ?? this.#defaultQuadShader(),
19361
- idealSize: { width: 1, height: 1 },
19538
+ size: { width: 1, height: 1 },
19362
19539
  layer: options.layer,
19363
19540
  key: options.key,
19364
19541
  rotationRadians: angle,
@@ -19647,6 +19824,9 @@ __export(exports_mod7, {
19647
19824
  });
19648
19825
  // src/textures/mod.ts
19649
19826
  var exports_mod8 = {};
19827
+ __export(exports_mod8, {
19828
+ Bundles: () => Bundles
19829
+ });
19650
19830
  export {
19651
19831
  exports_mod as Utils,
19652
19832
  Toodle,
@@ -19658,8 +19838,9 @@ export {
19658
19838
  exports_mod3 as GfxMath,
19659
19839
  DEFAULT_LIMITS,
19660
19840
  exports_mod2 as Colors,
19841
+ Bundles,
19661
19842
  AssetManager
19662
19843
  };
19663
19844
 
19664
- //# debugId=E4D2A9C0603A997464756E2164756E21
19845
+ //# debugId=70898D8E4A9F02C164756E2164756E21
19665
19846
  //# sourceMappingURL=mod.js.map