minerender 2.1.0-alpha.75 → 2.1.0-alpha.76

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.
@@ -68,6 +68,7 @@ var init_PersistentCache = __esm({
68
68
  /** Bump to invalidate every persisted entry. */
69
69
  this.VERSION = 2;
70
70
  }
71
+ /** Opens a named store in the active environment using the current cache schema version. */
71
72
  static open(name) {
72
73
  return Env.provider.openCache(name, _PersistentCache.VERSION);
73
74
  }
@@ -329,6 +330,7 @@ var init_Caching = __esm({
329
330
  this.fontCache
330
331
  ];
331
332
  }
333
+ /** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
332
334
  static clear() {
333
335
  for (const cache of this.all) {
334
336
  cache.invalidateAll();
@@ -371,6 +373,7 @@ var init_Geometries = __esm({
371
373
  }
372
374
  return geometry;
373
375
  }
376
+ /** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
374
377
  static getBox(key) {
375
378
  const keyStr = serializeBoxGeometryKey(key);
376
379
  return Caching.boxGeometryCache.get(keyStr, (k) => {
@@ -554,12 +557,15 @@ var init_Requests = __esm({
554
557
  static {
555
558
  this.mcAssetRequestQueue = new RequestQueue();
556
559
  }
560
+ /** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
557
561
  static genericRequest(request) {
558
562
  return this.genericQueue.request(request);
559
563
  }
564
+ /** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
560
565
  static mcAssetRequest(request) {
561
566
  return this.mcAssetRequestQueue.request(request);
562
567
  }
568
+ /** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
563
569
  static setMcAssetRoot(root) {
564
570
  this.mcAssetRequestQueue.baseURL = root;
565
571
  }
@@ -612,6 +618,7 @@ var init_WrappedImage = __esm({
612
618
  }
613
619
  return false;
614
620
  }
621
+ /** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
615
622
  get animated() {
616
623
  return this.height > this.width && this.height % this.width === 0;
617
624
  }
@@ -658,6 +665,7 @@ var init_ImageLoader = __esm({
658
665
  static _createImage() {
659
666
  return createImage();
660
667
  }
668
+ /** Resolves when an image URL has decoded, or rejects if decoding fails. */
661
669
  static async loadAsync(src) {
662
670
  return new Promise((resolve, reject) => {
663
671
  const image = this._createImage();
@@ -666,6 +674,7 @@ var init_ImageLoader = __esm({
666
674
  image.src = src;
667
675
  });
668
676
  }
677
+ /** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
669
678
  static loadElement(src, onload, onerr) {
670
679
  const image = this._createImage();
671
680
  if (onload)
@@ -693,6 +702,7 @@ var init_ImageLoader = __esm({
693
702
  throw err;
694
703
  }
695
704
  }
705
+ /** Decodes the supplied bytes into a canvas without fetching the source URL again. */
696
706
  static async infoToCanvasData(info) {
697
707
  const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
698
708
  const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
@@ -709,12 +719,14 @@ var init_ImageLoader = __esm({
709
719
  const image = await this.infoToCanvasData(info);
710
720
  return image.data.getImageData(0, 0, image.width, image.height);
711
721
  }
722
+ /** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
712
723
  static async getData(src) {
713
724
  const keyStr = serializeImageKey({ src });
714
725
  return await Caching.imageDataCache.get(keyStr, (k) => {
715
726
  return _ImageLoader.loadData(src);
716
727
  });
717
728
  }
729
+ /** Returns a cached canvas context and dimensions for an image URL or data URL. */
718
730
  static async getCanvasData(src) {
719
731
  const keyStr = serializeImageKey({ src });
720
732
  return await Caching.canvasImageDataCache.get(keyStr, (k) => {
@@ -814,6 +826,7 @@ var init_TextureLoader = __esm({
814
826
  texture.rotation = rotation;
815
827
  return Textures.initTextureProps(texture);
816
828
  }
829
+ /** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
817
830
  static load(src, format2 = RGBAFormat, rotation = 0) {
818
831
  const texture = new Texture();
819
832
  texture.colorSpace = THREE.SRGBColorSpace;
@@ -862,6 +875,7 @@ var init_Textures = __esm({
862
875
  return this.initTextureProps(texture);
863
876
  });
864
877
  }
878
+ /** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
865
879
  static initTextureProps(texture) {
866
880
  texture.magFilter = THREE2.NearestFilter;
867
881
  texture.minFilter = THREE2.NearestFilter;
@@ -874,11 +888,13 @@ var init_Textures = __esm({
874
888
  static createAsset(key) {
875
889
  return new Texture2();
876
890
  }
891
+ /** Creates an sRGB color texture from a canvas. */
877
892
  static createCanvasTexture(canvas) {
878
893
  const texture = new CanvasTexture(canvas);
879
894
  texture.colorSpace = THREE2.SRGBColorSpace;
880
895
  return this.initTextureProps(texture);
881
896
  }
897
+ /** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
882
898
  static getImage(key) {
883
899
  const keyStr = serializeTextureKey(key);
884
900
  const cached2 = Caching.textureCache.peek(keyStr);
@@ -903,6 +919,7 @@ var init_Materials = __esm({
903
919
  init_TextureLoader();
904
920
  init_Caching();
905
921
  Materials = class _Materials {
922
+ /** Shared checkerboard material, created on first access. */
906
923
  static get MISSING_TEXTURE() {
907
924
  return Caching.materialCache.get("builtin:missing-texture", () => new MeshBasicMaterial({
908
925
  map: Textures.getMissing(),
@@ -1006,6 +1023,7 @@ var init_Materials = __esm({
1006
1023
  if (tint !== void 0) material.color.multiply(new Color(tint));
1007
1024
  return material;
1008
1025
  }
1026
+ /** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
1009
1027
  static createGuiCanvasMaterial(canvas) {
1010
1028
  const material = new MeshBasicMaterial({
1011
1029
  map: Textures.createCanvasTexture(canvas),
@@ -1172,6 +1190,7 @@ var init_Materials = __esm({
1172
1190
  }
1173
1191
  return this.createBasicCanvasMaterial(canvas, transparent, shade);
1174
1192
  }
1193
+ /** Returns a shared image material. Use {@link createImage} for a separate material instance. */
1175
1194
  static getImage(key) {
1176
1195
  const keyStr = serializeMaterialKey(key);
1177
1196
  const map = Caching.materialCache.peek(keyStr)?.map;
@@ -1346,6 +1365,14 @@ var init_AssetKey = __esm({
1346
1365
  }
1347
1366
  };
1348
1367
  AssetKey = class _AssetKey extends BasicAssetKey {
1368
+ /**
1369
+ * Creates a key from separate path components. Use {@link parse} for a combined reference.
1370
+ *
1371
+ * @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
1372
+ * @param type - Subdirectory, such as `block` or `item`.
1373
+ * @param rootType - Top-level directory, usually `assets` or `data`.
1374
+ * @param root - Base URL override for hosted asset sources.
1375
+ */
1349
1376
  constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
1350
1377
  super(namespace, path);
1351
1378
  this.namespace = namespace;
@@ -1357,6 +1384,16 @@ var init_AssetKey = __esm({
1357
1384
  this.root = root;
1358
1385
  this.isAssetKey = true;
1359
1386
  }
1387
+ /**
1388
+ * Parses an asset reference such as `minecraft:block/stone`.
1389
+ * A missing namespace defaults to the origin's namespace or `minecraft`.
1390
+ *
1391
+ * @param assetType - Asset category, such as `models` or `textures`.
1392
+ * @param str - Path with an optional namespace and file extension.
1393
+ * @param origin - Supplies the asset root, default namespace, and non-texture extension.
1394
+ * @returns A key with the matching extension removed from its path. Textures use `.png`;
1395
+ * other assets use the origin's extension or `.json`.
1396
+ */
1360
1397
  static parse(assetType, str, origin) {
1361
1398
  let namespace = origin?.namespace || DEFAULT_NAMESPACE;
1362
1399
  if (str.includes(":")) {
@@ -1388,6 +1425,7 @@ var init_AssetKey = __esm({
1388
1425
  ];
1389
1426
  return a.join("/");
1390
1427
  }
1428
+ /** Joins the type subdirectory and path without adding a namespace or file extension. */
1391
1429
  getFullPath() {
1392
1430
  let p10 = [];
1393
1431
  if (this.type) {
@@ -1399,6 +1437,7 @@ var init_AssetKey = __esm({
1399
1437
  toNamespacedString() {
1400
1438
  return this.namespace + ":" + this.getFullPath();
1401
1439
  }
1440
+ /** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
1402
1441
  serialize() {
1403
1442
  return this.toString();
1404
1443
  }
@@ -1458,6 +1497,7 @@ var init_DisplayTransforms = __esm({
1458
1497
  const scale = new Vector32(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
1459
1498
  return new Matrix4().compose(translation, rotation, scale);
1460
1499
  }
1500
+ /** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
1461
1501
  static apply(geometry, matrix) {
1462
1502
  geometry.applyMatrix4(matrix);
1463
1503
  if (matrix.determinant() < 0) {
@@ -1485,6 +1525,7 @@ var init_ModelMerger = __esm({
1485
1525
  init_AssetKey();
1486
1526
  init_DisplayTransforms();
1487
1527
  ModelMerger = class {
1528
+ /** Loads the parent chain and returns a merged model without modifying the supplied definition. */
1488
1529
  static async mergeWithParents(model) {
1489
1530
  const models = await this.collectAllParents(model);
1490
1531
  let merged = {};
@@ -1552,6 +1593,7 @@ var init_Models = __esm({
1552
1593
  static get PERSISTENT_CACHE() {
1553
1594
  return this._persistentCache ??= PersistentCache.open("minerender-models");
1554
1595
  }
1596
+ /** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
1555
1597
  static async getItemList() {
1556
1598
  const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
1557
1599
  const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
@@ -1622,6 +1664,7 @@ var init_Models = __esm({
1622
1664
  }
1623
1665
  throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
1624
1666
  }
1667
+ /** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
1625
1668
  static async getRaw(key) {
1626
1669
  if (!key.assetType) {
1627
1670
  key.assetType = "models";
@@ -1641,6 +1684,12 @@ var init_Models = __esm({
1641
1684
  return asset;
1642
1685
  });
1643
1686
  }
1687
+ /**
1688
+ * Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
1689
+ * Item keys use GUI context, false conditions, and zero numeric properties.
1690
+ *
1691
+ * @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
1692
+ */
1644
1693
  static async getMerged(key) {
1645
1694
  if (!key.assetType) {
1646
1695
  key.assetType = "models";
@@ -1653,9 +1702,11 @@ var init_Models = __esm({
1653
1702
  return _Models.loadAndMerge(key);
1654
1703
  });
1655
1704
  }
1705
+ /** Alias for {@link getMerged}. */
1656
1706
  static async get(key) {
1657
1707
  return this.getMerged(key);
1658
1708
  }
1709
+ /** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
1659
1710
  static async clearCache() {
1660
1711
  return this.PERSISTENT_CACHE.clear();
1661
1712
  }
@@ -1678,6 +1729,7 @@ var init_BlockStates = __esm({
1678
1729
  return this._persistentCache ??= PersistentCache.open("minerender-blockstates");
1679
1730
  }
1680
1731
  // BlockState names are hardcoded
1732
+ /** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
1681
1733
  static async getList() {
1682
1734
  const key = new AssetKey(
1683
1735
  DEFAULT_NAMESPACE,
@@ -1698,6 +1750,7 @@ var init_BlockStates = __esm({
1698
1750
  return AssetLoader.get(key, "json" /* JSON */);
1699
1751
  });
1700
1752
  }
1753
+ /** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
1701
1754
  static async getDefaultState(key) {
1702
1755
  const defaultStates = await this.getDefaultStates();
1703
1756
  if (!defaultStates) {
@@ -1705,6 +1758,7 @@ var init_BlockStates = __esm({
1705
1758
  }
1706
1759
  return defaultStates["minecraft:" + key.path];
1707
1760
  }
1761
+ /** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
1708
1762
  static async get(key) {
1709
1763
  if (!key.assetType) {
1710
1764
  key.assetType = "blockstates";
@@ -1731,6 +1785,7 @@ var init_BlockStates = __esm({
1731
1785
  }
1732
1786
  return Promise.all(promises);
1733
1787
  }
1788
+ /** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
1734
1789
  static async clearCache() {
1735
1790
  await this.PERSISTENT_CACHE.clear();
1736
1791
  }
@@ -1756,9 +1811,11 @@ var init_Assets = __esm({
1756
1811
  static parseAssetKey(assetType, str, origin) {
1757
1812
  return AssetKey.parse(assetType, str, origin);
1758
1813
  }
1814
+ /** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
1759
1815
  static async getModel(key) {
1760
1816
  return Models.getMerged(key);
1761
1817
  }
1818
+ /** Loads a blockstate definition. See {@link BlockStates.get}. */
1762
1819
  static async getBlockState(key) {
1763
1820
  return BlockStates.get(key);
1764
1821
  }
@@ -1805,6 +1862,10 @@ var BatchedExecutor;
1805
1862
  var init_BatchedExecutor = __esm({
1806
1863
  "src/util/BatchedExecutor.ts"() {
1807
1864
  BatchedExecutor = class {
1865
+ /**
1866
+ * @param interval - Delay between queue runs, in milliseconds.
1867
+ * @param batch - Maximum tasks started per run and maximum tasks active at once.
1868
+ */
1808
1869
  constructor(interval = 1, batch = 30) {
1809
1870
  if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
1810
1871
  if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
@@ -1812,9 +1873,11 @@ var init_BatchedExecutor = __esm({
1812
1873
  this.batch = batch;
1813
1874
  this.queue = new JobQueue2(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
1814
1875
  }
1876
+ /** Queues a task and resolves with its result, or rejects with its error. */
1815
1877
  submit(task) {
1816
1878
  return this.queue.add(() => task());
1817
1879
  }
1880
+ /** Rejects queued and future tasks. Tasks already running are allowed to finish. */
1818
1881
  stop() {
1819
1882
  this.queue.end();
1820
1883
  }
@@ -1956,6 +2019,7 @@ var NBTHelper;
1956
2019
  var init_NBTHelper = __esm({
1957
2020
  "src/nbt/NBTHelper.ts"() {
1958
2021
  NBTHelper = class {
2022
+ /** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
1959
2023
  static async fromBuffer(data, format2) {
1960
2024
  const buffer = Buffer3.from(data instanceof Uint8Array ? data : new Uint8Array(data));
1961
2025
  const prismarineNbt = await import("prismarine-nbt");
@@ -2100,6 +2164,7 @@ var init_HostedAssetSource = __esm({
2100
2164
  ["json" /* JSON */, _HostedAssetSource.JSON]
2101
2165
  ]);
2102
2166
  }
2167
+ /** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
2103
2168
  async loadOrRetryWithDefaults(key, parser) {
2104
2169
  const direct = await this.load(key, parser);
2105
2170
  if (typeof direct !== "undefined") {
@@ -2157,6 +2222,10 @@ var init_HostedAssetSource = __esm({
2157
2222
  assetBasePath(key) {
2158
2223
  return `${key.root ?? this.root ?? AssetLoader.ROOT}/${key.rootType !== void 0 ? key.rootType + "/" : ""}${key.namespace !== void 0 ? key.namespace + "/" : ""}${key.assetType !== void 0 ? key.assetType + "/" : ""}`;
2159
2224
  }
2225
+ /**
2226
+ * Creates a hosted source for {@link AssetLoader.addSource}.
2227
+ * Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
2228
+ */
2160
2229
  constructor(root, options) {
2161
2230
  super();
2162
2231
  this._root = root;
@@ -2188,6 +2257,11 @@ var BrowserArchiveProxy;
2188
2257
  var init_BrowserArchiveProxy = __esm({
2189
2258
  "src/assets/source/archive/BrowserArchiveProxy.ts"() {
2190
2259
  BrowserArchiveProxy = class {
2260
+ /**
2261
+ * Opens a ZIP for asset lookup.
2262
+ * @param id - Cache identity for this content. Change it when the pack changes.
2263
+ * A File supplies an identity from its name, size, and modification time when omitted.
2264
+ */
2191
2265
  constructor(blob, id) {
2192
2266
  this._blob = blob;
2193
2267
  this._reader = new ZipReader(new BlobReader(this._blob));
@@ -2387,6 +2461,7 @@ var init_ArchiveAssetSource = __esm({
2387
2461
  dataPackFormat: copyFormat(options.dataPackFormat)
2388
2462
  };
2389
2463
  }
2464
+ /** Creates a browser ZIP source from a Blob or File. */
2390
2465
  static blob(blob, options) {
2391
2466
  return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
2392
2467
  }
@@ -2519,6 +2594,7 @@ var init_AssetLoader = __esm({
2519
2594
  FALLBACK_ROOT = "https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master";
2520
2595
  AssetLoader = class {
2521
2596
  static {
2597
+ /** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
2522
2598
  this.ROOT = DEFAULT_ROOT;
2523
2599
  }
2524
2600
  static {
@@ -2531,6 +2607,7 @@ var init_AssetLoader = __esm({
2531
2607
  static get version() {
2532
2608
  return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
2533
2609
  }
2610
+ /** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
2534
2611
  static setVersion(version) {
2535
2612
  this.ROOT = `https://assets.mcasset.cloud/${version}`;
2536
2613
  const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
@@ -2564,6 +2641,13 @@ var init_AssetLoader = __esm({
2564
2641
  return scope ? `${scope}
2565
2642
  ${key}` : key;
2566
2643
  }
2644
+ /**
2645
+ * Registers a source at the highest priority.
2646
+ * Call {@link Caching.clear} after changing sources to discard previously loaded assets.
2647
+ *
2648
+ * @param key - Name used to replace or remove this source.
2649
+ * @param override - Removes the first source with this name before adding the new one.
2650
+ */
2567
2651
  static addSource(key, source, override = true) {
2568
2652
  if (override) {
2569
2653
  const existing = this.removeSource(key);
@@ -2574,6 +2658,7 @@ ${key}` : key;
2574
2658
  this._SOURCES.unshift({ key, source });
2575
2659
  console.log(p4, "Added AssetSource", key);
2576
2660
  }
2661
+ /** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
2577
2662
  static removeSource(key) {
2578
2663
  const index = this._SOURCES.findIndex((s) => s.key === key);
2579
2664
  if (index != -1) {
@@ -2588,6 +2673,7 @@ ${key}` : key;
2588
2673
  this.addSource("mcassets-fallback", new HostedAssetSource(FALLBACK_ROOT, { retryDefaults: false }));
2589
2674
  this.addSource("mcassets", new HostedAssetSource(this.ROOT, { retryDefaults: false }));
2590
2675
  }
2676
+ /** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
2591
2677
  static async getAll(key, parser) {
2592
2678
  const sources = [...this._SOURCES];
2593
2679
  const results = [];
@@ -2649,6 +2735,7 @@ var init_ModelTextures = __esm({
2649
2735
  static get PERSISTENT_META_CACHE() {
2650
2736
  return this._persistentMetaCache ??= PersistentCache.open("minerender-texturemeta");
2651
2737
  }
2738
+ /** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
2652
2739
  static async get(key) {
2653
2740
  const keyStr = key.serialize();
2654
2741
  const pending = this.preload(key);
@@ -2666,6 +2753,7 @@ var init_ModelTextures = __esm({
2666
2753
  throw err;
2667
2754
  }
2668
2755
  }
2756
+ /** Fetches and caches encoded texture bytes without decoding the pixels. */
2669
2757
  static async preload(key) {
2670
2758
  const keyStr = key.serialize();
2671
2759
  return Caching.textureAssetCache.get(keyStr, (k) => {
@@ -2676,6 +2764,7 @@ var init_ModelTextures = __esm({
2676
2764
  });
2677
2765
  });
2678
2766
  }
2767
+ /** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
2679
2768
  static async getMeta(key) {
2680
2769
  if (!key.extension || !key.extension.endsWith(".mcmeta")) {
2681
2770
  key = Object.assign(new AssetKey("", ""), key);
@@ -2692,6 +2781,7 @@ var init_ModelTextures = __esm({
2692
2781
  });
2693
2782
  });
2694
2783
  }
2784
+ /** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
2695
2785
  static async clearCache() {
2696
2786
  await this.PERSISTENT_META_CACHE.clear();
2697
2787
  }
@@ -2779,6 +2869,7 @@ var init_Ticker = __esm({
2779
2869
  }
2780
2870
  return handle;
2781
2871
  }
2872
+ /** Starts or resumes the shared tick loop. Repeated calls have no effect. */
2782
2873
  static start() {
2783
2874
  if (this.interval) return;
2784
2875
  this.interval = this.schedule(() => {
@@ -2801,6 +2892,7 @@ var init_Ticker = __esm({
2801
2892
  _Ticker.fiveSecondTicks = 0;
2802
2893
  }, 1e3 * 5);
2803
2894
  }
2895
+ /** Pauses the loop while retaining callbacks for a later {@link start}. */
2804
2896
  static stop() {
2805
2897
  if (this.interval) clearInterval(this.interval);
2806
2898
  if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
@@ -2809,12 +2901,14 @@ var init_Ticker = __esm({
2809
2901
  this.oneSecondTicksTracker = void 0;
2810
2902
  this.fiveSecondTicksTracker = void 0;
2811
2903
  }
2904
+ /** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
2812
2905
  static add(tick) {
2813
2906
  this.start();
2814
2907
  const c = this.counter++;
2815
2908
  this._tickers.set(c, tick);
2816
2909
  return c;
2817
2910
  }
2911
+ /** Removes a callback by ID and stops the timers when no callbacks remain. */
2818
2912
  static remove(c) {
2819
2913
  if (typeof c === "number") this._tickers.delete(c);
2820
2914
  if (!this._tickers.size) this.stop();
@@ -2828,6 +2922,7 @@ var init_Ticker = __esm({
2828
2922
  static get tpsFiveSeconds() {
2829
2923
  return this.fiveSecondTps;
2830
2924
  }
2925
+ /** Stops the loop and removes all registered callbacks. */
2831
2926
  static dispose() {
2832
2927
  this.stop();
2833
2928
  this._tickers.clear();
@@ -2874,6 +2969,7 @@ var init_TextureAtlas = __esm({
2874
2969
  }
2875
2970
  };
2876
2971
  }
2972
+ /** Copies pixels for a texture variable from its current atlas rectangle. */
2877
2973
  getData(texture) {
2878
2974
  const pos = this.positions[texture];
2879
2975
  const size = this.sizes[texture];
@@ -3153,6 +3249,7 @@ var init_UVMapper = __esm({
3153
3249
  }
3154
3250
  uv.needsUpdate = true;
3155
3251
  }
3252
+ /** Returns the shared atlas for a keyed, merged model, creating it on first use. */
3156
3253
  static async getAtlas(model) {
3157
3254
  const keyStr = model.key.serialize();
3158
3255
  return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
@@ -3223,6 +3320,11 @@ var init_UVMapper = __esm({
3223
3320
  }
3224
3321
  return [tl, tr, bl, br];
3225
3322
  }
3323
+ /**
3324
+ * Combines model textures and animation metadata into an atlas.
3325
+ * Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
3326
+ * Returns `undefined` for models without a texture map.
3327
+ */
3226
3328
  static async createAtlas(originalModel) {
3227
3329
  const textureMap = {};
3228
3330
  const metaMap = {};
@@ -3587,6 +3689,7 @@ var init_Entities = __esm({
3587
3689
  init_MineRenderError();
3588
3690
  init_ModelTextures();
3589
3691
  Entities = class {
3692
+ /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
3590
3693
  static async getEntityList() {
3591
3694
  const collect = async (path) => {
3592
3695
  const prefix2 = path ? `${path}/` : "";
@@ -3604,6 +3707,7 @@ var init_Entities = __esm({
3604
3707
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
3605
3708
  return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3606
3709
  }
3710
+ /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
3607
3711
  static async getLayerList(modelKey) {
3608
3712
  return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
3609
3713
  }
@@ -3611,6 +3715,15 @@ var init_Entities = __esm({
3611
3715
  static async getPassList(modelKey) {
3612
3716
  return (await this.getModelFile(modelKey))?.passes ?? [];
3613
3717
  }
3718
+ /**
3719
+ * Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
3720
+ * By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
3721
+ *
3722
+ * @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
3723
+ * @param textureKey - Texture override for the first selected layer.
3724
+ * @param options - Layer selection, conditional passes, and per-layer texture overrides.
3725
+ * @returns The selected model, or `undefined` when its dataset file is missing.
3726
+ */
3614
3727
  static async getEntity(modelKey, textureKey, options) {
3615
3728
  const model = await this.getModelFile(modelKey);
3616
3729
  if (!model) return void 0;
@@ -3656,6 +3769,7 @@ var init_Entities = __esm({
3656
3769
  const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3657
3770
  return file?.animations;
3658
3771
  }
3772
+ /** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
3659
3773
  static async resolveTexture(modelKey) {
3660
3774
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3661
3775
  const root = isAssetKey(modelKey) ? modelKey.root : void 0;
@@ -3816,12 +3930,14 @@ var init_EntityAnimationPlayer = __esm({
3816
3930
  this._animation = void 0;
3817
3931
  this._time = 0;
3818
3932
  }
3933
+ /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
3819
3934
  setTime(seconds) {
3820
3935
  if (!this._animation) return false;
3821
3936
  this._time = seconds;
3822
3937
  this.apply();
3823
3938
  return true;
3824
3939
  }
3940
+ /** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
3825
3941
  advance(deltaSeconds) {
3826
3942
  return this.setTime(this._time + deltaSeconds * this.speed);
3827
3943
  }
@@ -3859,6 +3975,7 @@ var init_InstanceReference = __esm({
3859
3975
  if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
3860
3976
  return this.instanceable;
3861
3977
  }
3978
+ /** Releases this placement's slot for reuse. Repeated calls have no effect. */
3862
3979
  removeFromScene() {
3863
3980
  if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
3864
3981
  }
@@ -3871,9 +3988,11 @@ var init_InstanceReference = __esm({
3871
3988
  setMatrix(matrix) {
3872
3989
  this.activeInstanceable.setMatrixAt(this.index, matrix);
3873
3990
  }
3991
+ /** Hides or restores this placement while preserving its transform and allocated slot. */
3874
3992
  setVisible(visible) {
3875
3993
  this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
3876
3994
  }
3995
+ /** Updates the supplied transform components. Positions use scene units and rotations use radians. */
3877
3996
  setPositionRotationScale(position, rotation, scale) {
3878
3997
  this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
3879
3998
  }
@@ -3950,6 +4069,7 @@ var init_SceneObject = __esm({
3950
4069
  }
3951
4070
  async init() {
3952
4071
  }
4072
+ /** Marks ancestor scenes for redraw and emits a `change` event. */
3953
4073
  notifyDirty() {
3954
4074
  this.traverseAncestors((parent) => {
3955
4075
  const scene = parent;
@@ -3985,15 +4105,17 @@ var init_SceneObject = __esm({
3985
4105
  return obj;
3986
4106
  }
3987
4107
  /**
3988
- * Get a group by its name
4108
+ * Finds a named part, such as `head`, without the internal `group:` prefix.
4109
+ * Returns `undefined` when the group does not exist.
3989
4110
  */
3990
4111
  getGroupByName(name) {
3991
4112
  return this.getObjectByName(`group:${name}`);
3992
4113
  }
3993
4114
  /**
3994
- * Toggle visibility of a group
3995
- * @param name name of the group
3996
- * @param visible set the visibility directly, if not set toggles it
4115
+ * Changes a named group's visibility and requests a redraw.
4116
+ * @param name - Part name without the `group:` prefix.
4117
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4118
+ * @returns The resulting visibility, or `false` if the group does not exist.
3997
4119
  */
3998
4120
  toggleGroupVisibility(name, visible) {
3999
4121
  return this.toggleObjectVisibility(this.getGroupByName(name), visible);
@@ -4044,15 +4166,16 @@ var init_SceneObject = __esm({
4044
4166
  return mesh;
4045
4167
  }
4046
4168
  /**
4047
- * Get a mesh by its name
4169
+ * Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
4048
4170
  */
4049
4171
  getMeshByName(name) {
4050
4172
  return this.getObjectByName(`mesh:${name}`);
4051
4173
  }
4052
4174
  /**
4053
- * Toggle visibility of a mesh
4054
- * @param name name of the mesh
4055
- * @param visible set the visibility directly, if not set toggles it
4175
+ * Changes a named mesh's visibility and requests a redraw.
4176
+ * @param name - Mesh name without the `mesh:` prefix.
4177
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4178
+ * @returns The resulting visibility, or `false` if the mesh does not exist.
4056
4179
  */
4057
4180
  toggleMeshVisibility(name, visible) {
4058
4181
  return this.toggleObjectVisibility(this.getMeshByName(name), visible);
@@ -4098,12 +4221,14 @@ var init_SceneObject = __esm({
4098
4221
  get isInstanced() {
4099
4222
  return this._isInstanced;
4100
4223
  }
4224
+ /** Number of live placements, excluding released instance slots. */
4101
4225
  get instanceCounter() {
4102
4226
  return this._instanceCounter;
4103
4227
  }
4104
4228
  constructInstanceReference(i) {
4105
4229
  return new InstanceReference(this, i);
4106
4230
  }
4231
+ /** Allocates a placement of this instanced object, reusing a released slot when available. */
4107
4232
  nextInstance() {
4108
4233
  const mesh = this.instanceMesh;
4109
4234
  if (!mesh) throw new MineRenderError("Object is not instanced");
@@ -4250,6 +4375,7 @@ var init_SceneObject = __esm({
4250
4375
  vector.setFromMatrixScale(matrix);
4251
4376
  return vector;
4252
4377
  }
4378
+ /** Updates supplied transform components. For a shared model, affects every live instance. */
4253
4379
  setPositionRotationScale(position, rotation, scale) {
4254
4380
  if (this.isInstanced) {
4255
4381
  for (const i of this.activeInstanceIndices()) {
@@ -4268,6 +4394,7 @@ var init_SceneObject = __esm({
4268
4394
  }
4269
4395
  this.notifyDirty();
4270
4396
  }
4397
+ /** Sets position in scene units. For a shared model, updates every live instance. */
4271
4398
  setPosition(position) {
4272
4399
  if (this.isInstanced) {
4273
4400
  for (const i of this.activeInstanceIndices()) {
@@ -4285,6 +4412,7 @@ var init_SceneObject = __esm({
4285
4412
  return this.position;
4286
4413
  }
4287
4414
  }
4415
+ /** Sets rotation in radians. For a shared model, updates every live instance. */
4288
4416
  setRotation(rotation) {
4289
4417
  if (this.isInstanced) {
4290
4418
  for (const i of this.activeInstanceIndices()) {
@@ -4302,6 +4430,7 @@ var init_SceneObject = __esm({
4302
4430
  return this.rotation;
4303
4431
  }
4304
4432
  }
4433
+ /** Sets scale factors. For a shared model, updates every live instance. */
4305
4434
  setScale(scale) {
4306
4435
  if (this.isInstanced) {
4307
4436
  for (const i of this.activeInstanceIndices()) {
@@ -4339,6 +4468,7 @@ var init_SceneObject = __esm({
4339
4468
  this.notifyDirty();
4340
4469
  super.dispose();
4341
4470
  }
4471
+ /** Releases instance slots and removes children, disposing children that implement Disposable. */
4342
4472
  disposeAndRemoveAllChildren() {
4343
4473
  if (this.instanceMesh) {
4344
4474
  if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
@@ -4361,6 +4491,7 @@ var init_SceneObject = __esm({
4361
4491
  }
4362
4492
  this.notifyDirty();
4363
4493
  }
4494
+ /** Detaches the object from its parent and requests a redraw. */
4364
4495
  removeFromScene() {
4365
4496
  this.notifyDirty();
4366
4497
  this.removeFromParent();
@@ -4512,12 +4643,15 @@ var init_EntityObject = __esm({
4512
4643
  if (this.animationPlayers.length) this.notifyDirty();
4513
4644
  }
4514
4645
  //</editor-fold>
4646
+ /** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
4515
4647
  getLayerGroup(name) {
4516
4648
  return super.getGroupByName(`layer:${name}`);
4517
4649
  }
4650
+ /** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
4518
4651
  getGroupByName(name, layerName) {
4519
4652
  return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
4520
4653
  }
4654
+ /** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
4521
4655
  getMeshByName(name, layerName) {
4522
4656
  return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
4523
4657
  }
@@ -5188,6 +5322,7 @@ var init_ModelCulling = __esm({
5188
5322
  "src/model/ModelCulling.ts"() {
5189
5323
  init_CubeFace();
5190
5324
  ModelCulling = class {
5325
+ /** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
5191
5326
  static toLocalMask(worldMask, rotation) {
5192
5327
  let localMask = 0;
5193
5328
  for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
@@ -5198,6 +5333,7 @@ var init_ModelCulling = __esm({
5198
5333
  }
5199
5334
  return localMask;
5200
5335
  }
5336
+ /** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
5201
5337
  static isOpaqueFullCube(atlas) {
5202
5338
  if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
5203
5339
  const element = atlas.model.elements[0];
@@ -5982,6 +6118,7 @@ var Meshes = class _Meshes {
5982
6118
  static createBox(key) {
5983
6119
  return new Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
5984
6120
  }
6121
+ /** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
5985
6122
  static getBox(key) {
5986
6123
  const keyStr = serializeMeshKey(key);
5987
6124
  return Caching.boxMeshCache.get(keyStr, (k) => {
@@ -6094,6 +6231,7 @@ init_AssetParsers();
6094
6231
  init_ModelTextures();
6095
6232
  init_Caching();
6096
6233
  var Fonts = class {
6234
+ /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
6097
6235
  static async get(key = "minecraft:default") {
6098
6236
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
6099
6237
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -9310,6 +9448,13 @@ var GuiHelper = class {
9310
9448
  });
9311
9449
  return layers;
9312
9450
  }
9451
+ /**
9452
+ * Calculates a slot's top-left position in GUI pixels.
9453
+ * @param slot - Zero-based slot index, or `[column, row]`.
9454
+ * @param origin - Position of the first slot.
9455
+ * @param offset - Horizontal and vertical spacing between slot origins, in pixels.
9456
+ * @param rowSize - Slots per row when `slot` is an index.
9457
+ */
9313
9458
  static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
9314
9459
  const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
9315
9460
  return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
@@ -9967,6 +10112,7 @@ var SceneStatsDisplay = class {
9967
10112
  appendTo(el) {
9968
10113
  el.append(this.statsContainer);
9969
10114
  }
10115
+ /** Stops the refresh timer and removes the panel from the page. */
9970
10116
  dispose() {
9971
10117
  clearInterval(this.interval);
9972
10118
  this.interval = void 0;
@@ -9991,6 +10137,7 @@ var InstanceManager = class {
9991
10137
  this.isInstanceManager = true;
9992
10138
  this.instanceCache = {};
9993
10139
  }
10140
+ /** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
9994
10141
  async get(key) {
9995
10142
  if (key in this.instanceCache) {
9996
10143
  console.debug(p9, "key in cache", key);
@@ -9998,6 +10145,7 @@ var InstanceManager = class {
9998
10145
  }
9999
10146
  return void 0;
10000
10147
  }
10148
+ /** Allocates a placement, calling the supplier once when the shared model is first needed. */
10001
10149
  async getOrCreate(key, supplier) {
10002
10150
  if (key in this.instanceCache) {
10003
10151
  return await this.get(key);
@@ -10014,6 +10162,7 @@ var InstanceManager = class {
10014
10162
  throw error;
10015
10163
  }
10016
10164
  }
10165
+ /** Forgets cached models without disposing them or releasing their existing placements. */
10017
10166
  reset() {
10018
10167
  for (let instanceCacheKey in this.instanceCache) {
10019
10168
  delete this.instanceCache[instanceCacheKey];
@@ -10059,6 +10208,7 @@ function matchesCondition(condition, state) {
10059
10208
  });
10060
10209
  }
10061
10210
  var BlockStateResolver = class {
10211
+ /** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
10062
10212
  static async defaults(blockState) {
10063
10213
  const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
10064
10214
  const state = {};
@@ -10078,9 +10228,11 @@ var BlockStateResolver = class {
10078
10228
  }
10079
10229
  return state;
10080
10230
  }
10231
+ /** Selects matching models. Supply `choose` to control selection within each alternatives array. */
10081
10232
  static select(blockState, state, choose = this.choose) {
10082
10233
  return this.matching(blockState, state).map(choose);
10083
10234
  }
10235
+ /** Returns matching model groups before choosing weighted alternatives. */
10084
10236
  static matching(blockState, state) {
10085
10237
  const out = [];
10086
10238
  if (blockState.variants) {
@@ -10104,6 +10256,7 @@ var BlockStateResolver = class {
10104
10256
  }
10105
10257
  return out;
10106
10258
  }
10259
+ /** Selects a random alternative using positive integer weights, each defaulting to 1. */
10107
10260
  static choose(variants) {
10108
10261
  if (!Array.isArray(variants)) return variants;
10109
10262
  if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
@@ -10121,6 +10274,7 @@ var BlockStateResolver = class {
10121
10274
  }
10122
10275
  return variants[variants.length - 1];
10123
10276
  }
10277
+ /** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
10124
10278
  static rotation(variant) {
10125
10279
  const rotation = new Euler8();
10126
10280
  if (typeof variant.x !== "undefined") {
@@ -10407,6 +10561,7 @@ var BlockObject = class _BlockObject extends SceneObject {
10407
10561
  }
10408
10562
  this.notifyDirty();
10409
10563
  }
10564
+ /** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
10410
10565
  get state() {
10411
10566
  return this._state;
10412
10567
  }
@@ -10506,6 +10661,7 @@ var BlockObject = class _BlockObject extends SceneObject {
10506
10661
  obj.setPosition(this.position);
10507
10662
  return obj;
10508
10663
  }
10664
+ /** Clears selected properties and rebuilds models without reapplying property defaults. */
10509
10665
  async resetState() {
10510
10666
  this._previousState = this._state;
10511
10667
  this._state = {};
@@ -12405,6 +12561,11 @@ function compoundList(tags, name) {
12405
12561
  // src/model/multiblock/StructureParser.ts
12406
12562
  init_MineRenderError();
12407
12563
  var StructureParser = class {
12564
+ /**
12565
+ * Parses decoded NBT, omitting air blocks.
12566
+ * @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
12567
+ * @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
12568
+ */
12408
12569
  static async parse(_nbt, paletteIndex) {
12409
12570
  const nbt = _nbt.value;
12410
12571
  let palette;
@@ -13239,6 +13400,7 @@ var SkinImage = class {
13239
13400
  // src/skin/SkinTextures.ts
13240
13401
  init_Textures();
13241
13402
  var SkinTextures = class {
13403
+ /** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
13242
13404
  static async get(src, legacy) {
13243
13405
  const image = await SkinImage.getData(src);
13244
13406
  const scale = image.width / 64;
@@ -13412,6 +13574,14 @@ var SkinObject = class extends SceneObject {
13412
13574
  }
13413
13575
  }
13414
13576
  }
13577
+ /**
13578
+ * Loads a skin texture, detecting slim arms and legacy layouts by default.
13579
+ * To override detection, set `slim` or `legacy` in the options passed to
13580
+ * `new SkinObject(options)` or `scene.addSkin(src, options)`.
13581
+ * Use {@link setSlim} or {@link setLegacy} to change these settings later.
13582
+ *
13583
+ * @param src - URL of the skin texture.
13584
+ */
13415
13585
  async setSkinTexture(src) {
13416
13586
  if (typeof src === "undefined") return;
13417
13587
  this.skinTextureSrc = src;
@@ -13563,6 +13733,7 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
13563
13733
  this.stats.sceneObjectCount += change;
13564
13734
  }
13565
13735
  }
13736
+ /** Initializes each object before attaching it to the scene. */
13566
13737
  async initAndAdd(...object2) {
13567
13738
  this.dirty = true;
13568
13739
  for (let obj of object2) {
@@ -13598,13 +13769,33 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
13598
13769
  return obj;
13599
13770
  }
13600
13771
  }
13772
+ /**
13773
+ * Adds a model loaded with {@link Models.getMerged}.
13774
+ * Set `options.instanceMeshes` to `false` to create a separate ModelObject.
13775
+ *
13776
+ * @param parent - Object to attach the model to. Defaults to this scene.
13777
+ * @returns The model, or a reference to one placement of a shared model.
13778
+ */
13601
13779
  async addModel(model, options, parent = this) {
13602
13780
  options = { ...options, tints: await ItemTints.get(model, options?.tints) };
13603
13781
  return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
13604
13782
  }
13783
+ /**
13784
+ * Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
13785
+ * Set `options.initialState` to choose block properties such as `facing` or `axis`.
13786
+ * Use the returned object's `setPosition` method to move all of its model parts.
13787
+ */
13605
13788
  async addBlock(blockState, options, parent = this) {
13606
13789
  return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
13607
13790
  }
13791
+ /**
13792
+ * Loads a skin and adds an initialized player model.
13793
+ *
13794
+ * @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
13795
+ * Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
13796
+ * @param options - Player settings, including `slim` and `legacy` detection overrides.
13797
+ * @param parent - Object to attach the player to. Defaults to this scene.
13798
+ */
13608
13799
  async addSkin(skin, options, parent = this) {
13609
13800
  this.dirty = true;
13610
13801
  const obj = new SkinObject(options);
@@ -13616,9 +13807,14 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
13616
13807
  parent.add(obj);
13617
13808
  return obj;
13618
13809
  }
13810
+ /** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
13619
13811
  async addEntity(entity, options, parent = this) {
13620
13812
  return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
13621
13813
  }
13814
+ /**
13815
+ * Adds GUI layers in draw order. One GUI pixel equals one scene unit.
13816
+ * Layer positions start at the top left, with positive GUI y pointing down.
13817
+ */
13622
13818
  async addGui(layers, options, parent = this) {
13623
13819
  const obj = new GuiObject(layers, options);
13624
13820
  obj.scene = this;
@@ -14481,6 +14677,7 @@ var Renderer = class _Renderer {
14481
14677
  get element() {
14482
14678
  return this._element;
14483
14679
  }
14680
+ /** Attaches the canvas to an element and sizes it to that element's dimensions. */
14484
14681
  appendTo(element) {
14485
14682
  if (this._disposed) return;
14486
14683
  this._element = element;
@@ -14508,6 +14705,7 @@ var Renderer = class _Renderer {
14508
14705
  events.add(changeEvent2);
14509
14706
  dispatcher.addEventListener(changeEvent2, this._changeListener);
14510
14707
  }
14708
+ /** Updates the camera and canvas size. Width and height are in CSS pixels. */
14511
14709
  resize(width, height) {
14512
14710
  if (this._disposed) return;
14513
14711
  if (isPerspectiveCamera(this.camera)) {
@@ -14546,12 +14744,14 @@ var Renderer = class _Renderer {
14546
14744
  if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
14547
14745
  };
14548
14746
  }
14747
+ /** Starts or resumes the animation loop and registered frame callbacks. */
14549
14748
  start() {
14550
14749
  if (this._disposed) return;
14551
14750
  this.stop();
14552
14751
  this._running = true;
14553
14752
  if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
14554
14753
  }
14754
+ /** Pauses rendering and frame callbacks. Call {@link start} to resume. */
14555
14755
  stop() {
14556
14756
  if (this._disposed) return;
14557
14757
  this._running = false;
@@ -14660,12 +14860,15 @@ var Renderer = class _Renderer {
14660
14860
  const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
14661
14861
  return canvas.toDataURL(mime, quality);
14662
14862
  }
14863
+ /** Exports visible scene meshes as OBJ text, without texture images. */
14663
14864
  toObj() {
14664
14865
  return SceneExporter.toObj(this.scene);
14665
14866
  }
14867
+ /** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
14666
14868
  toPLY(options) {
14667
14869
  return SceneExporter.toPLY(this.scene, options);
14668
14870
  }
14871
+ /** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
14669
14872
  toGLTF(options) {
14670
14873
  return SceneExporter.toGLTF(this.scene, options);
14671
14874
  }
@@ -14708,12 +14911,14 @@ init_request();
14708
14911
  init_Requests();
14709
14912
  var Skins = class {
14710
14913
  //<editor-fold desc="Skins">
14914
+ /** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
14711
14915
  static async fromUuidOrUsername(uuidOrUsername) {
14712
14916
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14713
14917
  return this.fromUuid(uuidOrUsername);
14714
14918
  }
14715
14919
  return this.fromUsername(uuidOrUsername);
14716
14920
  }
14921
+ /** Builds a proxy URL for a UUID without downloading or checking the skin. */
14717
14922
  static async fromUuid(uuid) {
14718
14923
  return this.getMcProxySkinUrl(uuid);
14719
14924
  }
@@ -14725,6 +14930,7 @@ var Skins = class {
14725
14930
  return void 0;
14726
14931
  });
14727
14932
  }
14933
+ /** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
14728
14934
  static async fromMineSkin(mineskinId) {
14729
14935
  return this.getMineSkin(mineskinId).then((mineskin) => {
14730
14936
  if (mineskin) {
@@ -14735,12 +14941,14 @@ var Skins = class {
14735
14941
  }
14736
14942
  //</editor-fold>
14737
14943
  //<editor-fold desc="Capes">
14944
+ /** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
14738
14945
  static async capeFromUuidOrUsername(uuidOrUsername) {
14739
14946
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14740
14947
  return this.capeFromUuid(uuidOrUsername);
14741
14948
  }
14742
14949
  return this.capeFromUsername(uuidOrUsername);
14743
14950
  }
14951
+ /** Builds a proxy URL without checking whether the player has a cape. */
14744
14952
  static async capeFromUuid(uuid) {
14745
14953
  return this.getMcProxyCapeUrl(uuid);
14746
14954
  }
@@ -14895,16 +15103,19 @@ var ChunkData = class _ChunkData {
14895
15103
  this.freeIds = [];
14896
15104
  this.nbt = /* @__PURE__ */ new Map();
14897
15105
  }
15106
+ /** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
14898
15107
  get(index) {
14899
15108
  _ChunkData.validateIndex(index);
14900
15109
  const entry = this.palette[this.indices[index]];
14901
15110
  return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
14902
15111
  }
15112
+ /** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
14903
15113
  snapshot(index) {
14904
15114
  _ChunkData.validateIndex(index);
14905
15115
  const entry = this.palette[this.indices[index]];
14906
15116
  return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
14907
15117
  }
15118
+ /** Copies block data into a cell. An omitted block or an air block clears the cell. */
14908
15119
  set(index, block) {
14909
15120
  _ChunkData.validateIndex(index);
14910
15121
  let state;
@@ -14969,15 +15180,23 @@ var ChunkData = class _ChunkData {
14969
15180
 
14970
15181
  // src/world/AnvilParser.ts
14971
15182
  var AnvilParser = class {
15183
+ /** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
14972
15184
  static getChunkList(data) {
14973
15185
  return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
14974
15186
  }
15187
+ /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
14975
15188
  static async parse(data) {
14976
15189
  const bytes = this.bytes(data);
14977
15190
  const chunks = [];
14978
15191
  for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
14979
15192
  return { chunks };
14980
15193
  }
15194
+ /**
15195
+ * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
15196
+ * @param localX - Chunk x within the region, from 0 to 31.
15197
+ * @param localZ - Chunk z within the region, from 0 to 31.
15198
+ * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
15199
+ */
14981
15200
  static async parseChunk(data, localX, localZ) {
14982
15201
  if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
14983
15202
  throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
@@ -15182,6 +15401,7 @@ var SectionModels = class {
15182
15401
  this.templates = /* @__PURE__ */ new Set();
15183
15402
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
15184
15403
  }
15404
+ /** Returns a cube template, or `undefined` when the block requires an individual render object. */
15185
15405
  async get(blockState, properties = {}) {
15186
15406
  if (!blockState.variants || blockState.multipart) return void 0;
15187
15407
  let states = this.states.get(blockState);
@@ -15235,6 +15455,7 @@ var SectionModels = class {
15235
15455
  }
15236
15456
  return { variants, templates };
15237
15457
  }
15458
+ /** Disposes prepared geometry and clears this world's template caches. */
15238
15459
  clear() {
15239
15460
  for (const template of this.templates) template.geometry.dispose();
15240
15461
  this.templates.clear();
@@ -15285,6 +15506,13 @@ var MineRenderWorld = class {
15285
15506
  this.validatePosBounds(posOrX);
15286
15507
  await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
15287
15508
  }
15509
+ /**
15510
+ * Places a structure's blocks at their stored positions, then refreshes neighboring faces.
15511
+ * Entity NBT is retained by the structure but is not rendered.
15512
+ *
15513
+ * @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
15514
+ * @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
15515
+ */
15288
15516
  async placeMultiBlock(multiblock, useBatches = true, executor) {
15289
15517
  const changes = /* @__PURE__ */ new Map();
15290
15518
  try {
@@ -15381,6 +15609,7 @@ var MineRenderWorld = class {
15381
15609
  });
15382
15610
  }
15383
15611
  }
15612
+ /** Removes all chunks and releases their block placements and section meshes. */
15384
15613
  async clear() {
15385
15614
  await this.culling;
15386
15615
  const chunks = [...this._chunks.values()];
@@ -15466,9 +15695,11 @@ var MineRenderWorld = class {
15466
15695
  }
15467
15696
  return chunk;
15468
15697
  }
15698
+ /** Returns the loaded chunk containing a world block position, without creating a chunk. */
15469
15699
  getChunkAt(pos) {
15470
15700
  return this._chunks.get(this.worldPosToChunkKey(pos));
15471
15701
  }
15702
+ /** Converts block coordinates to scene units by multiplying each component by 16. */
15472
15703
  static worldToScenePosition(pos) {
15473
15704
  return new Vector316(
15474
15705
  pos.x * 16,
@@ -15476,6 +15707,7 @@ var MineRenderWorld = class {
15476
15707
  pos.z * 16
15477
15708
  );
15478
15709
  }
15710
+ /** Converts scene units to block coordinates without rounding to an integer block. */
15479
15711
  static sceneToWorldPosition(pos) {
15480
15712
  return new Vector316(
15481
15713
  pos.x / 16,
@@ -15508,6 +15740,7 @@ var SectionMesh = class _SectionMesh extends Group2 {
15508
15740
  super(...arguments);
15509
15741
  this.ownedMeshes = [];
15510
15742
  }
15743
+ /** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
15511
15744
  static build(entries, maxAtlasSize = 2048) {
15512
15745
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
15513
15746
  const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
@@ -15652,7 +15885,7 @@ var Chunk = class _Chunk {
15652
15885
  return this.setBlockInChunkAt(pos, block, worldPos);
15653
15886
  }
15654
15887
  /**
15655
- * Set a block at integer chunk-local coordinates from 0 to 15.
15888
+ * Places a block at integer chunk-local coordinates from 0 to 15.
15656
15889
  */
15657
15890
  async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
15658
15891
  if (typeof worldPos === "undefined") {
@@ -15701,6 +15934,7 @@ var Chunk = class _Chunk {
15701
15934
  await onBlocksChanged?.([worldPos]);
15702
15935
  }
15703
15936
  }
15937
+ /** Changes visibility at world block coordinates while preserving the block's data. */
15704
15938
  async setBlockVisibleAt(pos, visible) {
15705
15939
  const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
15706
15940
  const block = this.renderedBlocks.get(index);
@@ -15754,6 +15988,7 @@ var Chunk = class _Chunk {
15754
15988
  this.scene.dirty = true;
15755
15989
  this.meshDirty = false;
15756
15990
  }
15991
+ /** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
15757
15992
  async clear(onBlocksChanged = this.onBlocksChanged) {
15758
15993
  const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
15759
15994
  new Vector317(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
@@ -15774,6 +16009,7 @@ var Chunk = class _Chunk {
15774
16009
  static isAir(block) {
15775
16010
  return ChunkData.isAir(block);
15776
16011
  }
16012
+ /** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
15777
16013
  static chunkPosToBlockIndex(pos) {
15778
16014
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
15779
16015
  throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");