minerender 2.1.0-alpha.74 → 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.
@@ -69,6 +69,7 @@ var init_PersistentCache = __esm({
69
69
  /** Bump to invalidate every persisted entry. */
70
70
  this.VERSION = 2;
71
71
  }
72
+ /** Opens a named store in the active environment using the current cache schema version. */
72
73
  static open(name) {
73
74
  return Env.provider.openCache(name, _PersistentCache.VERSION);
74
75
  }
@@ -330,6 +331,7 @@ var init_Caching = __esm({
330
331
  this.fontCache
331
332
  ];
332
333
  }
334
+ /** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
333
335
  static clear() {
334
336
  for (const cache of this.all) {
335
337
  cache.invalidateAll();
@@ -372,6 +374,7 @@ var init_Geometries = __esm({
372
374
  }
373
375
  return geometry;
374
376
  }
377
+ /** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
375
378
  static getBox(key) {
376
379
  const keyStr = serializeBoxGeometryKey(key);
377
380
  return Caching.boxGeometryCache.get(keyStr, (k) => {
@@ -555,12 +558,15 @@ var init_Requests = __esm({
555
558
  static {
556
559
  this.mcAssetRequestQueue = new RequestQueue();
557
560
  }
561
+ /** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
558
562
  static genericRequest(request) {
559
563
  return this.genericQueue.request(request);
560
564
  }
565
+ /** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
561
566
  static mcAssetRequest(request) {
562
567
  return this.mcAssetRequestQueue.request(request);
563
568
  }
569
+ /** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
564
570
  static setMcAssetRoot(root) {
565
571
  this.mcAssetRequestQueue.baseURL = root;
566
572
  }
@@ -613,6 +619,7 @@ var init_WrappedImage = __esm({
613
619
  }
614
620
  return false;
615
621
  }
622
+ /** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
616
623
  get animated() {
617
624
  return this.height > this.width && this.height % this.width === 0;
618
625
  }
@@ -659,6 +666,7 @@ var init_ImageLoader = __esm({
659
666
  static _createImage() {
660
667
  return createImage();
661
668
  }
669
+ /** Resolves when an image URL has decoded, or rejects if decoding fails. */
662
670
  static async loadAsync(src) {
663
671
  return new Promise((resolve, reject) => {
664
672
  const image = this._createImage();
@@ -667,6 +675,7 @@ var init_ImageLoader = __esm({
667
675
  image.src = src;
668
676
  });
669
677
  }
678
+ /** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
670
679
  static loadElement(src, onload, onerr) {
671
680
  const image = this._createImage();
672
681
  if (onload)
@@ -694,6 +703,7 @@ var init_ImageLoader = __esm({
694
703
  throw err;
695
704
  }
696
705
  }
706
+ /** Decodes the supplied bytes into a canvas without fetching the source URL again. */
697
707
  static async infoToCanvasData(info) {
698
708
  const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
699
709
  const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
@@ -710,12 +720,14 @@ var init_ImageLoader = __esm({
710
720
  const image = await this.infoToCanvasData(info);
711
721
  return image.data.getImageData(0, 0, image.width, image.height);
712
722
  }
723
+ /** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
713
724
  static async getData(src) {
714
725
  const keyStr = serializeImageKey({ src });
715
726
  return await Caching.imageDataCache.get(keyStr, (k) => {
716
727
  return _ImageLoader.loadData(src);
717
728
  });
718
729
  }
730
+ /** Returns a cached canvas context and dimensions for an image URL or data URL. */
719
731
  static async getCanvasData(src) {
720
732
  const keyStr = serializeImageKey({ src });
721
733
  return await Caching.canvasImageDataCache.get(keyStr, (k) => {
@@ -815,6 +827,7 @@ var init_TextureLoader = __esm({
815
827
  texture.rotation = rotation;
816
828
  return Textures.initTextureProps(texture);
817
829
  }
830
+ /** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
818
831
  static load(src, format2 = import_three3.RGBAFormat, rotation = 0) {
819
832
  const texture = new import_three3.Texture();
820
833
  texture.colorSpace = THREE.SRGBColorSpace;
@@ -863,6 +876,7 @@ var init_Textures = __esm({
863
876
  return this.initTextureProps(texture);
864
877
  });
865
878
  }
879
+ /** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
866
880
  static initTextureProps(texture) {
867
881
  texture.magFilter = THREE2.NearestFilter;
868
882
  texture.minFilter = THREE2.NearestFilter;
@@ -875,11 +889,13 @@ var init_Textures = __esm({
875
889
  static createAsset(key) {
876
890
  return new import_three4.Texture();
877
891
  }
892
+ /** Creates an sRGB color texture from a canvas. */
878
893
  static createCanvasTexture(canvas) {
879
894
  const texture = new import_three4.CanvasTexture(canvas);
880
895
  texture.colorSpace = THREE2.SRGBColorSpace;
881
896
  return this.initTextureProps(texture);
882
897
  }
898
+ /** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
883
899
  static getImage(key) {
884
900
  const keyStr = serializeTextureKey(key);
885
901
  const cached2 = Caching.textureCache.peek(keyStr);
@@ -904,6 +920,7 @@ var init_Materials = __esm({
904
920
  init_TextureLoader();
905
921
  init_Caching();
906
922
  Materials = class _Materials {
923
+ /** Shared checkerboard material, created on first access. */
907
924
  static get MISSING_TEXTURE() {
908
925
  return Caching.materialCache.get("builtin:missing-texture", () => new import_three5.MeshBasicMaterial({
909
926
  map: Textures.getMissing(),
@@ -1007,6 +1024,7 @@ var init_Materials = __esm({
1007
1024
  if (tint !== void 0) material.color.multiply(new import_three5.Color(tint));
1008
1025
  return material;
1009
1026
  }
1027
+ /** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
1010
1028
  static createGuiCanvasMaterial(canvas) {
1011
1029
  const material = new import_three5.MeshBasicMaterial({
1012
1030
  map: Textures.createCanvasTexture(canvas),
@@ -1173,6 +1191,7 @@ var init_Materials = __esm({
1173
1191
  }
1174
1192
  return this.createBasicCanvasMaterial(canvas, transparent, shade);
1175
1193
  }
1194
+ /** Returns a shared image material. Use {@link createImage} for a separate material instance. */
1176
1195
  static getImage(key) {
1177
1196
  const keyStr = serializeMaterialKey(key);
1178
1197
  const map = Caching.materialCache.peek(keyStr)?.map;
@@ -1347,6 +1366,14 @@ var init_AssetKey = __esm({
1347
1366
  }
1348
1367
  };
1349
1368
  AssetKey = class _AssetKey extends BasicAssetKey {
1369
+ /**
1370
+ * Creates a key from separate path components. Use {@link parse} for a combined reference.
1371
+ *
1372
+ * @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
1373
+ * @param type - Subdirectory, such as `block` or `item`.
1374
+ * @param rootType - Top-level directory, usually `assets` or `data`.
1375
+ * @param root - Base URL override for hosted asset sources.
1376
+ */
1350
1377
  constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
1351
1378
  super(namespace, path);
1352
1379
  this.namespace = namespace;
@@ -1358,6 +1385,16 @@ var init_AssetKey = __esm({
1358
1385
  this.root = root;
1359
1386
  this.isAssetKey = true;
1360
1387
  }
1388
+ /**
1389
+ * Parses an asset reference such as `minecraft:block/stone`.
1390
+ * A missing namespace defaults to the origin's namespace or `minecraft`.
1391
+ *
1392
+ * @param assetType - Asset category, such as `models` or `textures`.
1393
+ * @param str - Path with an optional namespace and file extension.
1394
+ * @param origin - Supplies the asset root, default namespace, and non-texture extension.
1395
+ * @returns A key with the matching extension removed from its path. Textures use `.png`;
1396
+ * other assets use the origin's extension or `.json`.
1397
+ */
1361
1398
  static parse(assetType, str, origin) {
1362
1399
  let namespace = origin?.namespace || DEFAULT_NAMESPACE;
1363
1400
  if (str.includes(":")) {
@@ -1389,6 +1426,7 @@ var init_AssetKey = __esm({
1389
1426
  ];
1390
1427
  return a.join("/");
1391
1428
  }
1429
+ /** Joins the type subdirectory and path without adding a namespace or file extension. */
1392
1430
  getFullPath() {
1393
1431
  let p10 = [];
1394
1432
  if (this.type) {
@@ -1400,6 +1438,7 @@ var init_AssetKey = __esm({
1400
1438
  toNamespacedString() {
1401
1439
  return this.namespace + ":" + this.getFullPath();
1402
1440
  }
1441
+ /** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
1403
1442
  serialize() {
1404
1443
  return this.toString();
1405
1444
  }
@@ -1459,6 +1498,7 @@ var init_DisplayTransforms = __esm({
1459
1498
  const scale = new import_three7.Vector3(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
1460
1499
  return new import_three7.Matrix4().compose(translation, rotation, scale);
1461
1500
  }
1501
+ /** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
1462
1502
  static apply(geometry, matrix) {
1463
1503
  geometry.applyMatrix4(matrix);
1464
1504
  if (matrix.determinant() < 0) {
@@ -1486,6 +1526,7 @@ var init_ModelMerger = __esm({
1486
1526
  init_AssetKey();
1487
1527
  init_DisplayTransforms();
1488
1528
  ModelMerger = class {
1529
+ /** Loads the parent chain and returns a merged model without modifying the supplied definition. */
1489
1530
  static async mergeWithParents(model) {
1490
1531
  const models = await this.collectAllParents(model);
1491
1532
  let merged = {};
@@ -1553,6 +1594,7 @@ var init_Models = __esm({
1553
1594
  static get PERSISTENT_CACHE() {
1554
1595
  return this._persistentCache ??= PersistentCache.open("minerender-models");
1555
1596
  }
1597
+ /** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
1556
1598
  static async getItemList() {
1557
1599
  const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
1558
1600
  const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
@@ -1623,6 +1665,7 @@ var init_Models = __esm({
1623
1665
  }
1624
1666
  throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
1625
1667
  }
1668
+ /** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
1626
1669
  static async getRaw(key) {
1627
1670
  if (!key.assetType) {
1628
1671
  key.assetType = "models";
@@ -1642,6 +1685,12 @@ var init_Models = __esm({
1642
1685
  return asset;
1643
1686
  });
1644
1687
  }
1688
+ /**
1689
+ * Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
1690
+ * Item keys use GUI context, false conditions, and zero numeric properties.
1691
+ *
1692
+ * @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
1693
+ */
1645
1694
  static async getMerged(key) {
1646
1695
  if (!key.assetType) {
1647
1696
  key.assetType = "models";
@@ -1654,9 +1703,11 @@ var init_Models = __esm({
1654
1703
  return _Models.loadAndMerge(key);
1655
1704
  });
1656
1705
  }
1706
+ /** Alias for {@link getMerged}. */
1657
1707
  static async get(key) {
1658
1708
  return this.getMerged(key);
1659
1709
  }
1710
+ /** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
1660
1711
  static async clearCache() {
1661
1712
  return this.PERSISTENT_CACHE.clear();
1662
1713
  }
@@ -1679,6 +1730,7 @@ var init_BlockStates = __esm({
1679
1730
  return this._persistentCache ??= PersistentCache.open("minerender-blockstates");
1680
1731
  }
1681
1732
  // BlockState names are hardcoded
1733
+ /** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
1682
1734
  static async getList() {
1683
1735
  const key = new AssetKey(
1684
1736
  DEFAULT_NAMESPACE,
@@ -1699,6 +1751,7 @@ var init_BlockStates = __esm({
1699
1751
  return AssetLoader.get(key, "json" /* JSON */);
1700
1752
  });
1701
1753
  }
1754
+ /** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
1702
1755
  static async getDefaultState(key) {
1703
1756
  const defaultStates = await this.getDefaultStates();
1704
1757
  if (!defaultStates) {
@@ -1706,6 +1759,7 @@ var init_BlockStates = __esm({
1706
1759
  }
1707
1760
  return defaultStates["minecraft:" + key.path];
1708
1761
  }
1762
+ /** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
1709
1763
  static async get(key) {
1710
1764
  if (!key.assetType) {
1711
1765
  key.assetType = "blockstates";
@@ -1732,6 +1786,7 @@ var init_BlockStates = __esm({
1732
1786
  }
1733
1787
  return Promise.all(promises);
1734
1788
  }
1789
+ /** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
1735
1790
  static async clearCache() {
1736
1791
  await this.PERSISTENT_CACHE.clear();
1737
1792
  }
@@ -1757,9 +1812,11 @@ var init_Assets = __esm({
1757
1812
  static parseAssetKey(assetType, str, origin) {
1758
1813
  return AssetKey.parse(assetType, str, origin);
1759
1814
  }
1815
+ /** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
1760
1816
  static async getModel(key) {
1761
1817
  return Models.getMerged(key);
1762
1818
  }
1819
+ /** Loads a blockstate definition. See {@link BlockStates.get}. */
1763
1820
  static async getBlockState(key) {
1764
1821
  return BlockStates.get(key);
1765
1822
  }
@@ -1806,6 +1863,10 @@ var init_BatchedExecutor = __esm({
1806
1863
  "src/util/BatchedExecutor.ts"() {
1807
1864
  import_jobqu2 = require("jobqu");
1808
1865
  BatchedExecutor = class {
1866
+ /**
1867
+ * @param interval - Delay between queue runs, in milliseconds.
1868
+ * @param batch - Maximum tasks started per run and maximum tasks active at once.
1869
+ */
1809
1870
  constructor(interval = 1, batch = 30) {
1810
1871
  if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
1811
1872
  if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
@@ -1813,9 +1874,11 @@ var init_BatchedExecutor = __esm({
1813
1874
  this.batch = batch;
1814
1875
  this.queue = new import_jobqu2.JobQueue(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
1815
1876
  }
1877
+ /** Queues a task and resolves with its result, or rejects with its error. */
1816
1878
  submit(task) {
1817
1879
  return this.queue.add(() => task());
1818
1880
  }
1881
+ /** Rejects queued and future tasks. Tasks already running are allowed to finish. */
1819
1882
  stop() {
1820
1883
  this.queue.end();
1821
1884
  }
@@ -1958,6 +2021,7 @@ var init_NBTHelper = __esm({
1958
2021
  "src/nbt/NBTHelper.ts"() {
1959
2022
  import_buffer2 = require("buffer");
1960
2023
  NBTHelper = class {
2024
+ /** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
1961
2025
  static async fromBuffer(data, format2) {
1962
2026
  const buffer = import_buffer2.Buffer.from(data instanceof Uint8Array ? data : new Uint8Array(data));
1963
2027
  const prismarineNbt = await import("prismarine-nbt");
@@ -2102,6 +2166,7 @@ var init_HostedAssetSource = __esm({
2102
2166
  ["json" /* JSON */, _HostedAssetSource.JSON]
2103
2167
  ]);
2104
2168
  }
2169
+ /** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
2105
2170
  async loadOrRetryWithDefaults(key, parser) {
2106
2171
  const direct = await this.load(key, parser);
2107
2172
  if (typeof direct !== "undefined") {
@@ -2159,6 +2224,10 @@ var init_HostedAssetSource = __esm({
2159
2224
  assetBasePath(key) {
2160
2225
  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 + "/" : ""}`;
2161
2226
  }
2227
+ /**
2228
+ * Creates a hosted source for {@link AssetLoader.addSource}.
2229
+ * Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
2230
+ */
2162
2231
  constructor(root, options) {
2163
2232
  super();
2164
2233
  this._root = root;
@@ -2190,6 +2259,11 @@ var init_BrowserArchiveProxy = __esm({
2190
2259
  "src/assets/source/archive/BrowserArchiveProxy.ts"() {
2191
2260
  import_zip = require("@zip.js/zip.js");
2192
2261
  BrowserArchiveProxy = class {
2262
+ /**
2263
+ * Opens a ZIP for asset lookup.
2264
+ * @param id - Cache identity for this content. Change it when the pack changes.
2265
+ * A File supplies an identity from its name, size, and modification time when omitted.
2266
+ */
2193
2267
  constructor(blob, id) {
2194
2268
  this._blob = blob;
2195
2269
  this._reader = new import_zip.ZipReader(new import_zip.BlobReader(this._blob));
@@ -2389,6 +2463,7 @@ var init_ArchiveAssetSource = __esm({
2389
2463
  dataPackFormat: copyFormat(options.dataPackFormat)
2390
2464
  };
2391
2465
  }
2466
+ /** Creates a browser ZIP source from a Blob or File. */
2392
2467
  static blob(blob, options) {
2393
2468
  return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
2394
2469
  }
@@ -2521,6 +2596,7 @@ var init_AssetLoader = __esm({
2521
2596
  FALLBACK_ROOT = "https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master";
2522
2597
  AssetLoader = class {
2523
2598
  static {
2599
+ /** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
2524
2600
  this.ROOT = DEFAULT_ROOT;
2525
2601
  }
2526
2602
  static {
@@ -2533,6 +2609,7 @@ var init_AssetLoader = __esm({
2533
2609
  static get version() {
2534
2610
  return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
2535
2611
  }
2612
+ /** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
2536
2613
  static setVersion(version) {
2537
2614
  this.ROOT = `https://assets.mcasset.cloud/${version}`;
2538
2615
  const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
@@ -2566,6 +2643,13 @@ var init_AssetLoader = __esm({
2566
2643
  return scope ? `${scope}
2567
2644
  ${key}` : key;
2568
2645
  }
2646
+ /**
2647
+ * Registers a source at the highest priority.
2648
+ * Call {@link Caching.clear} after changing sources to discard previously loaded assets.
2649
+ *
2650
+ * @param key - Name used to replace or remove this source.
2651
+ * @param override - Removes the first source with this name before adding the new one.
2652
+ */
2569
2653
  static addSource(key, source, override = true) {
2570
2654
  if (override) {
2571
2655
  const existing = this.removeSource(key);
@@ -2576,6 +2660,7 @@ ${key}` : key;
2576
2660
  this._SOURCES.unshift({ key, source });
2577
2661
  console.log(p4, "Added AssetSource", key);
2578
2662
  }
2663
+ /** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
2579
2664
  static removeSource(key) {
2580
2665
  const index = this._SOURCES.findIndex((s) => s.key === key);
2581
2666
  if (index != -1) {
@@ -2590,6 +2675,7 @@ ${key}` : key;
2590
2675
  this.addSource("mcassets-fallback", new HostedAssetSource(FALLBACK_ROOT, { retryDefaults: false }));
2591
2676
  this.addSource("mcassets", new HostedAssetSource(this.ROOT, { retryDefaults: false }));
2592
2677
  }
2678
+ /** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
2593
2679
  static async getAll(key, parser) {
2594
2680
  const sources = [...this._SOURCES];
2595
2681
  const results = [];
@@ -2651,6 +2737,7 @@ var init_ModelTextures = __esm({
2651
2737
  static get PERSISTENT_META_CACHE() {
2652
2738
  return this._persistentMetaCache ??= PersistentCache.open("minerender-texturemeta");
2653
2739
  }
2740
+ /** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
2654
2741
  static async get(key) {
2655
2742
  const keyStr = key.serialize();
2656
2743
  const pending = this.preload(key);
@@ -2668,6 +2755,7 @@ var init_ModelTextures = __esm({
2668
2755
  throw err;
2669
2756
  }
2670
2757
  }
2758
+ /** Fetches and caches encoded texture bytes without decoding the pixels. */
2671
2759
  static async preload(key) {
2672
2760
  const keyStr = key.serialize();
2673
2761
  return Caching.textureAssetCache.get(keyStr, (k) => {
@@ -2678,6 +2766,7 @@ var init_ModelTextures = __esm({
2678
2766
  });
2679
2767
  });
2680
2768
  }
2769
+ /** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
2681
2770
  static async getMeta(key) {
2682
2771
  if (!key.extension || !key.extension.endsWith(".mcmeta")) {
2683
2772
  key = Object.assign(new AssetKey("", ""), key);
@@ -2694,6 +2783,7 @@ var init_ModelTextures = __esm({
2694
2783
  });
2695
2784
  });
2696
2785
  }
2786
+ /** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
2697
2787
  static async clearCache() {
2698
2788
  await this.PERSISTENT_META_CACHE.clear();
2699
2789
  }
@@ -2781,6 +2871,7 @@ var init_Ticker = __esm({
2781
2871
  }
2782
2872
  return handle;
2783
2873
  }
2874
+ /** Starts or resumes the shared tick loop. Repeated calls have no effect. */
2784
2875
  static start() {
2785
2876
  if (this.interval) return;
2786
2877
  this.interval = this.schedule(() => {
@@ -2803,6 +2894,7 @@ var init_Ticker = __esm({
2803
2894
  _Ticker.fiveSecondTicks = 0;
2804
2895
  }, 1e3 * 5);
2805
2896
  }
2897
+ /** Pauses the loop while retaining callbacks for a later {@link start}. */
2806
2898
  static stop() {
2807
2899
  if (this.interval) clearInterval(this.interval);
2808
2900
  if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
@@ -2811,12 +2903,14 @@ var init_Ticker = __esm({
2811
2903
  this.oneSecondTicksTracker = void 0;
2812
2904
  this.fiveSecondTicksTracker = void 0;
2813
2905
  }
2906
+ /** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
2814
2907
  static add(tick) {
2815
2908
  this.start();
2816
2909
  const c = this.counter++;
2817
2910
  this._tickers.set(c, tick);
2818
2911
  return c;
2819
2912
  }
2913
+ /** Removes a callback by ID and stops the timers when no callbacks remain. */
2820
2914
  static remove(c) {
2821
2915
  if (typeof c === "number") this._tickers.delete(c);
2822
2916
  if (!this._tickers.size) this.stop();
@@ -2830,6 +2924,7 @@ var init_Ticker = __esm({
2830
2924
  static get tpsFiveSeconds() {
2831
2925
  return this.fiveSecondTps;
2832
2926
  }
2927
+ /** Stops the loop and removes all registered callbacks. */
2833
2928
  static dispose() {
2834
2929
  this.stop();
2835
2930
  this._tickers.clear();
@@ -2876,6 +2971,7 @@ var init_TextureAtlas = __esm({
2876
2971
  }
2877
2972
  };
2878
2973
  }
2974
+ /** Copies pixels for a texture variable from its current atlas rectangle. */
2879
2975
  getData(texture) {
2880
2976
  const pos = this.positions[texture];
2881
2977
  const size = this.sizes[texture];
@@ -3155,6 +3251,7 @@ var init_UVMapper = __esm({
3155
3251
  }
3156
3252
  uv.needsUpdate = true;
3157
3253
  }
3254
+ /** Returns the shared atlas for a keyed, merged model, creating it on first use. */
3158
3255
  static async getAtlas(model) {
3159
3256
  const keyStr = model.key.serialize();
3160
3257
  return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
@@ -3225,6 +3322,11 @@ var init_UVMapper = __esm({
3225
3322
  }
3226
3323
  return [tl, tr, bl, br];
3227
3324
  }
3325
+ /**
3326
+ * Combines model textures and animation metadata into an atlas.
3327
+ * Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
3328
+ * Returns `undefined` for models without a texture map.
3329
+ */
3228
3330
  static async createAtlas(originalModel) {
3229
3331
  const textureMap = {};
3230
3332
  const metaMap = {};
@@ -3589,6 +3691,7 @@ var init_Entities = __esm({
3589
3691
  init_MineRenderError();
3590
3692
  init_ModelTextures();
3591
3693
  Entities = class {
3694
+ /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
3592
3695
  static async getEntityList() {
3593
3696
  const collect = async (path) => {
3594
3697
  const prefix2 = path ? `${path}/` : "";
@@ -3606,6 +3709,7 @@ var init_Entities = __esm({
3606
3709
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
3607
3710
  return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3608
3711
  }
3712
+ /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
3609
3713
  static async getLayerList(modelKey) {
3610
3714
  return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
3611
3715
  }
@@ -3613,6 +3717,15 @@ var init_Entities = __esm({
3613
3717
  static async getPassList(modelKey) {
3614
3718
  return (await this.getModelFile(modelKey))?.passes ?? [];
3615
3719
  }
3720
+ /**
3721
+ * Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
3722
+ * By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
3723
+ *
3724
+ * @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
3725
+ * @param textureKey - Texture override for the first selected layer.
3726
+ * @param options - Layer selection, conditional passes, and per-layer texture overrides.
3727
+ * @returns The selected model, or `undefined` when its dataset file is missing.
3728
+ */
3616
3729
  static async getEntity(modelKey, textureKey, options) {
3617
3730
  const model = await this.getModelFile(modelKey);
3618
3731
  if (!model) return void 0;
@@ -3658,6 +3771,7 @@ var init_Entities = __esm({
3658
3771
  const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3659
3772
  return file?.animations;
3660
3773
  }
3774
+ /** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
3661
3775
  static async resolveTexture(modelKey) {
3662
3776
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3663
3777
  const root = isAssetKey(modelKey) ? modelKey.root : void 0;
@@ -3818,12 +3932,14 @@ var init_EntityAnimationPlayer = __esm({
3818
3932
  this._animation = void 0;
3819
3933
  this._time = 0;
3820
3934
  }
3935
+ /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
3821
3936
  setTime(seconds) {
3822
3937
  if (!this._animation) return false;
3823
3938
  this._time = seconds;
3824
3939
  this.apply();
3825
3940
  return true;
3826
3941
  }
3942
+ /** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
3827
3943
  advance(deltaSeconds) {
3828
3944
  return this.setTime(this._time + deltaSeconds * this.speed);
3829
3945
  }
@@ -3861,6 +3977,7 @@ var init_InstanceReference = __esm({
3861
3977
  if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
3862
3978
  return this.instanceable;
3863
3979
  }
3980
+ /** Releases this placement's slot for reuse. Repeated calls have no effect. */
3864
3981
  removeFromScene() {
3865
3982
  if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
3866
3983
  }
@@ -3873,9 +3990,11 @@ var init_InstanceReference = __esm({
3873
3990
  setMatrix(matrix) {
3874
3991
  this.activeInstanceable.setMatrixAt(this.index, matrix);
3875
3992
  }
3993
+ /** Hides or restores this placement while preserving its transform and allocated slot. */
3876
3994
  setVisible(visible) {
3877
3995
  this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
3878
3996
  }
3997
+ /** Updates the supplied transform components. Positions use scene units and rotations use radians. */
3879
3998
  setPositionRotationScale(position, rotation, scale) {
3880
3999
  this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
3881
4000
  }
@@ -3952,6 +4071,7 @@ var init_SceneObject = __esm({
3952
4071
  }
3953
4072
  async init() {
3954
4073
  }
4074
+ /** Marks ancestor scenes for redraw and emits a `change` event. */
3955
4075
  notifyDirty() {
3956
4076
  this.traverseAncestors((parent) => {
3957
4077
  const scene = parent;
@@ -3987,15 +4107,17 @@ var init_SceneObject = __esm({
3987
4107
  return obj;
3988
4108
  }
3989
4109
  /**
3990
- * Get a group by its name
4110
+ * Finds a named part, such as `head`, without the internal `group:` prefix.
4111
+ * Returns `undefined` when the group does not exist.
3991
4112
  */
3992
4113
  getGroupByName(name) {
3993
4114
  return this.getObjectByName(`group:${name}`);
3994
4115
  }
3995
4116
  /**
3996
- * Toggle visibility of a group
3997
- * @param name name of the group
3998
- * @param visible set the visibility directly, if not set toggles it
4117
+ * Changes a named group's visibility and requests a redraw.
4118
+ * @param name - Part name without the `group:` prefix.
4119
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4120
+ * @returns The resulting visibility, or `false` if the group does not exist.
3999
4121
  */
4000
4122
  toggleGroupVisibility(name, visible) {
4001
4123
  return this.toggleObjectVisibility(this.getGroupByName(name), visible);
@@ -4046,15 +4168,16 @@ var init_SceneObject = __esm({
4046
4168
  return mesh;
4047
4169
  }
4048
4170
  /**
4049
- * Get a mesh by its name
4171
+ * Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
4050
4172
  */
4051
4173
  getMeshByName(name) {
4052
4174
  return this.getObjectByName(`mesh:${name}`);
4053
4175
  }
4054
4176
  /**
4055
- * Toggle visibility of a mesh
4056
- * @param name name of the mesh
4057
- * @param visible set the visibility directly, if not set toggles it
4177
+ * Changes a named mesh's visibility and requests a redraw.
4178
+ * @param name - Mesh name without the `mesh:` prefix.
4179
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4180
+ * @returns The resulting visibility, or `false` if the mesh does not exist.
4058
4181
  */
4059
4182
  toggleMeshVisibility(name, visible) {
4060
4183
  return this.toggleObjectVisibility(this.getMeshByName(name), visible);
@@ -4100,12 +4223,14 @@ var init_SceneObject = __esm({
4100
4223
  get isInstanced() {
4101
4224
  return this._isInstanced;
4102
4225
  }
4226
+ /** Number of live placements, excluding released instance slots. */
4103
4227
  get instanceCounter() {
4104
4228
  return this._instanceCounter;
4105
4229
  }
4106
4230
  constructInstanceReference(i) {
4107
4231
  return new InstanceReference(this, i);
4108
4232
  }
4233
+ /** Allocates a placement of this instanced object, reusing a released slot when available. */
4109
4234
  nextInstance() {
4110
4235
  const mesh = this.instanceMesh;
4111
4236
  if (!mesh) throw new MineRenderError("Object is not instanced");
@@ -4252,6 +4377,7 @@ var init_SceneObject = __esm({
4252
4377
  vector.setFromMatrixScale(matrix);
4253
4378
  return vector;
4254
4379
  }
4380
+ /** Updates supplied transform components. For a shared model, affects every live instance. */
4255
4381
  setPositionRotationScale(position, rotation, scale) {
4256
4382
  if (this.isInstanced) {
4257
4383
  for (const i of this.activeInstanceIndices()) {
@@ -4270,6 +4396,7 @@ var init_SceneObject = __esm({
4270
4396
  }
4271
4397
  this.notifyDirty();
4272
4398
  }
4399
+ /** Sets position in scene units. For a shared model, updates every live instance. */
4273
4400
  setPosition(position) {
4274
4401
  if (this.isInstanced) {
4275
4402
  for (const i of this.activeInstanceIndices()) {
@@ -4287,6 +4414,7 @@ var init_SceneObject = __esm({
4287
4414
  return this.position;
4288
4415
  }
4289
4416
  }
4417
+ /** Sets rotation in radians. For a shared model, updates every live instance. */
4290
4418
  setRotation(rotation) {
4291
4419
  if (this.isInstanced) {
4292
4420
  for (const i of this.activeInstanceIndices()) {
@@ -4304,6 +4432,7 @@ var init_SceneObject = __esm({
4304
4432
  return this.rotation;
4305
4433
  }
4306
4434
  }
4435
+ /** Sets scale factors. For a shared model, updates every live instance. */
4307
4436
  setScale(scale) {
4308
4437
  if (this.isInstanced) {
4309
4438
  for (const i of this.activeInstanceIndices()) {
@@ -4341,6 +4470,7 @@ var init_SceneObject = __esm({
4341
4470
  this.notifyDirty();
4342
4471
  super.dispose();
4343
4472
  }
4473
+ /** Releases instance slots and removes children, disposing children that implement Disposable. */
4344
4474
  disposeAndRemoveAllChildren() {
4345
4475
  if (this.instanceMesh) {
4346
4476
  if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
@@ -4363,6 +4493,7 @@ var init_SceneObject = __esm({
4363
4493
  }
4364
4494
  this.notifyDirty();
4365
4495
  }
4496
+ /** Detaches the object from its parent and requests a redraw. */
4366
4497
  removeFromScene() {
4367
4498
  this.notifyDirty();
4368
4499
  this.removeFromParent();
@@ -4514,12 +4645,15 @@ var init_EntityObject = __esm({
4514
4645
  if (this.animationPlayers.length) this.notifyDirty();
4515
4646
  }
4516
4647
  //</editor-fold>
4648
+ /** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
4517
4649
  getLayerGroup(name) {
4518
4650
  return super.getGroupByName(`layer:${name}`);
4519
4651
  }
4652
+ /** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
4520
4653
  getGroupByName(name, layerName) {
4521
4654
  return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
4522
4655
  }
4656
+ /** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
4523
4657
  getMeshByName(name, layerName) {
4524
4658
  return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
4525
4659
  }
@@ -5181,6 +5315,7 @@ var init_ModelCulling = __esm({
5181
5315
  import_three23 = require("three");
5182
5316
  init_CubeFace();
5183
5317
  ModelCulling = class {
5318
+ /** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
5184
5319
  static toLocalMask(worldMask, rotation) {
5185
5320
  let localMask = 0;
5186
5321
  for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
@@ -5191,6 +5326,7 @@ var init_ModelCulling = __esm({
5191
5326
  }
5192
5327
  return localMask;
5193
5328
  }
5329
+ /** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
5194
5330
  static isOpaqueFullCube(atlas) {
5195
5331
  if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
5196
5332
  const element = atlas.model.elements[0];
@@ -6158,6 +6294,7 @@ var Meshes = class _Meshes {
6158
6294
  static createBox(key) {
6159
6295
  return new import_three6.Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
6160
6296
  }
6297
+ /** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
6161
6298
  static getBox(key) {
6162
6299
  const keyStr = serializeMeshKey(key);
6163
6300
  return Caching.boxMeshCache.get(keyStr, (k) => {
@@ -6270,6 +6407,7 @@ init_AssetParsers();
6270
6407
  init_ModelTextures();
6271
6408
  init_Caching();
6272
6409
  var Fonts = class {
6410
+ /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
6273
6411
  static async get(key = "minecraft:default") {
6274
6412
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
6275
6413
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -9445,6 +9583,13 @@ var GuiHelper = class {
9445
9583
  });
9446
9584
  return layers;
9447
9585
  }
9586
+ /**
9587
+ * Calculates a slot's top-left position in GUI pixels.
9588
+ * @param slot - Zero-based slot index, or `[column, row]`.
9589
+ * @param origin - Position of the first slot.
9590
+ * @param offset - Horizontal and vertical spacing between slot origins, in pixels.
9591
+ * @param rowSize - Slots per row when `slot` is an index.
9592
+ */
9448
9593
  static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
9449
9594
  const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
9450
9595
  return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
@@ -10102,6 +10247,7 @@ var SceneStatsDisplay = class {
10102
10247
  appendTo(el) {
10103
10248
  el.append(this.statsContainer);
10104
10249
  }
10250
+ /** Stops the refresh timer and removes the panel from the page. */
10105
10251
  dispose() {
10106
10252
  clearInterval(this.interval);
10107
10253
  this.interval = void 0;
@@ -10126,6 +10272,7 @@ var InstanceManager = class {
10126
10272
  this.isInstanceManager = true;
10127
10273
  this.instanceCache = {};
10128
10274
  }
10275
+ /** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
10129
10276
  async get(key) {
10130
10277
  if (key in this.instanceCache) {
10131
10278
  console.debug(p9, "key in cache", key);
@@ -10133,6 +10280,7 @@ var InstanceManager = class {
10133
10280
  }
10134
10281
  return void 0;
10135
10282
  }
10283
+ /** Allocates a placement, calling the supplier once when the shared model is first needed. */
10136
10284
  async getOrCreate(key, supplier) {
10137
10285
  if (key in this.instanceCache) {
10138
10286
  return await this.get(key);
@@ -10149,6 +10297,7 @@ var InstanceManager = class {
10149
10297
  throw error;
10150
10298
  }
10151
10299
  }
10300
+ /** Forgets cached models without disposing them or releasing their existing placements. */
10152
10301
  reset() {
10153
10302
  for (let instanceCacheKey in this.instanceCache) {
10154
10303
  delete this.instanceCache[instanceCacheKey];
@@ -10194,6 +10343,7 @@ function matchesCondition(condition, state) {
10194
10343
  });
10195
10344
  }
10196
10345
  var BlockStateResolver = class {
10346
+ /** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
10197
10347
  static async defaults(blockState) {
10198
10348
  const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
10199
10349
  const state = {};
@@ -10213,9 +10363,11 @@ var BlockStateResolver = class {
10213
10363
  }
10214
10364
  return state;
10215
10365
  }
10366
+ /** Selects matching models. Supply `choose` to control selection within each alternatives array. */
10216
10367
  static select(blockState, state, choose = this.choose) {
10217
10368
  return this.matching(blockState, state).map(choose);
10218
10369
  }
10370
+ /** Returns matching model groups before choosing weighted alternatives. */
10219
10371
  static matching(blockState, state) {
10220
10372
  const out = [];
10221
10373
  if (blockState.variants) {
@@ -10239,6 +10391,7 @@ var BlockStateResolver = class {
10239
10391
  }
10240
10392
  return out;
10241
10393
  }
10394
+ /** Selects a random alternative using positive integer weights, each defaulting to 1. */
10242
10395
  static choose(variants) {
10243
10396
  if (!Array.isArray(variants)) return variants;
10244
10397
  if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
@@ -10256,6 +10409,7 @@ var BlockStateResolver = class {
10256
10409
  }
10257
10410
  return variants[variants.length - 1];
10258
10411
  }
10412
+ /** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
10259
10413
  static rotation(variant) {
10260
10414
  const rotation = new import_three29.Euler();
10261
10415
  if (typeof variant.x !== "undefined") {
@@ -10542,6 +10696,7 @@ var BlockObject = class _BlockObject extends SceneObject {
10542
10696
  }
10543
10697
  this.notifyDirty();
10544
10698
  }
10699
+ /** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
10545
10700
  get state() {
10546
10701
  return this._state;
10547
10702
  }
@@ -10641,6 +10796,7 @@ var BlockObject = class _BlockObject extends SceneObject {
10641
10796
  obj.setPosition(this.position);
10642
10797
  return obj;
10643
10798
  }
10799
+ /** Clears selected properties and rebuilds models without reapplying property defaults. */
10644
10800
  async resetState() {
10645
10801
  this._previousState = this._state;
10646
10802
  this._state = {};
@@ -12540,6 +12696,11 @@ function compoundList(tags, name) {
12540
12696
  // src/model/multiblock/StructureParser.ts
12541
12697
  init_MineRenderError();
12542
12698
  var StructureParser = class {
12699
+ /**
12700
+ * Parses decoded NBT, omitting air blocks.
12701
+ * @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
12702
+ * @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
12703
+ */
12543
12704
  static async parse(_nbt, paletteIndex) {
12544
12705
  const nbt = _nbt.value;
12545
12706
  let palette;
@@ -13374,6 +13535,7 @@ var SkinImage = class {
13374
13535
  // src/skin/SkinTextures.ts
13375
13536
  init_Textures();
13376
13537
  var SkinTextures = class {
13538
+ /** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
13377
13539
  static async get(src, legacy) {
13378
13540
  const image = await SkinImage.getData(src);
13379
13541
  const scale = image.width / 64;
@@ -13547,6 +13709,14 @@ var SkinObject = class extends SceneObject {
13547
13709
  }
13548
13710
  }
13549
13711
  }
13712
+ /**
13713
+ * Loads a skin texture, detecting slim arms and legacy layouts by default.
13714
+ * To override detection, set `slim` or `legacy` in the options passed to
13715
+ * `new SkinObject(options)` or `scene.addSkin(src, options)`.
13716
+ * Use {@link setSlim} or {@link setLegacy} to change these settings later.
13717
+ *
13718
+ * @param src - URL of the skin texture.
13719
+ */
13550
13720
  async setSkinTexture(src) {
13551
13721
  if (typeof src === "undefined") return;
13552
13722
  this.skinTextureSrc = src;
@@ -13698,6 +13868,7 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
13698
13868
  this.stats.sceneObjectCount += change;
13699
13869
  }
13700
13870
  }
13871
+ /** Initializes each object before attaching it to the scene. */
13701
13872
  async initAndAdd(...object2) {
13702
13873
  this.dirty = true;
13703
13874
  for (let obj of object2) {
@@ -13733,13 +13904,33 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
13733
13904
  return obj;
13734
13905
  }
13735
13906
  }
13907
+ /**
13908
+ * Adds a model loaded with {@link Models.getMerged}.
13909
+ * Set `options.instanceMeshes` to `false` to create a separate ModelObject.
13910
+ *
13911
+ * @param parent - Object to attach the model to. Defaults to this scene.
13912
+ * @returns The model, or a reference to one placement of a shared model.
13913
+ */
13736
13914
  async addModel(model, options, parent = this) {
13737
13915
  options = { ...options, tints: await ItemTints.get(model, options?.tints) };
13738
13916
  return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
13739
13917
  }
13918
+ /**
13919
+ * Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
13920
+ * Set `options.initialState` to choose block properties such as `facing` or `axis`.
13921
+ * Use the returned object's `setPosition` method to move all of its model parts.
13922
+ */
13740
13923
  async addBlock(blockState, options, parent = this) {
13741
13924
  return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
13742
13925
  }
13926
+ /**
13927
+ * Loads a skin and adds an initialized player model.
13928
+ *
13929
+ * @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
13930
+ * Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
13931
+ * @param options - Player settings, including `slim` and `legacy` detection overrides.
13932
+ * @param parent - Object to attach the player to. Defaults to this scene.
13933
+ */
13743
13934
  async addSkin(skin, options, parent = this) {
13744
13935
  this.dirty = true;
13745
13936
  const obj = new SkinObject(options);
@@ -13751,9 +13942,14 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
13751
13942
  parent.add(obj);
13752
13943
  return obj;
13753
13944
  }
13945
+ /** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
13754
13946
  async addEntity(entity, options, parent = this) {
13755
13947
  return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
13756
13948
  }
13949
+ /**
13950
+ * Adds GUI layers in draw order. One GUI pixel equals one scene unit.
13951
+ * Layer positions start at the top left, with positive GUI y pointing down.
13952
+ */
13757
13953
  async addGui(layers, options, parent = this) {
13758
13954
  const obj = new GuiObject(layers, options);
13759
13955
  obj.scene = this;
@@ -14608,6 +14804,7 @@ var Renderer = class _Renderer {
14608
14804
  get element() {
14609
14805
  return this._element;
14610
14806
  }
14807
+ /** Attaches the canvas to an element and sizes it to that element's dimensions. */
14611
14808
  appendTo(element) {
14612
14809
  if (this._disposed) return;
14613
14810
  this._element = element;
@@ -14635,6 +14832,7 @@ var Renderer = class _Renderer {
14635
14832
  events.add(changeEvent2);
14636
14833
  dispatcher.addEventListener(changeEvent2, this._changeListener);
14637
14834
  }
14835
+ /** Updates the camera and canvas size. Width and height are in CSS pixels. */
14638
14836
  resize(width, height) {
14639
14837
  if (this._disposed) return;
14640
14838
  if (isPerspectiveCamera(this.camera)) {
@@ -14673,12 +14871,14 @@ var Renderer = class _Renderer {
14673
14871
  if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
14674
14872
  };
14675
14873
  }
14874
+ /** Starts or resumes the animation loop and registered frame callbacks. */
14676
14875
  start() {
14677
14876
  if (this._disposed) return;
14678
14877
  this.stop();
14679
14878
  this._running = true;
14680
14879
  if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
14681
14880
  }
14881
+ /** Pauses rendering and frame callbacks. Call {@link start} to resume. */
14682
14882
  stop() {
14683
14883
  if (this._disposed) return;
14684
14884
  this._running = false;
@@ -14787,12 +14987,15 @@ var Renderer = class _Renderer {
14787
14987
  const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
14788
14988
  return canvas.toDataURL(mime, quality);
14789
14989
  }
14990
+ /** Exports visible scene meshes as OBJ text, without texture images. */
14790
14991
  toObj() {
14791
14992
  return SceneExporter.toObj(this.scene);
14792
14993
  }
14994
+ /** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
14793
14995
  toPLY(options) {
14794
14996
  return SceneExporter.toPLY(this.scene, options);
14795
14997
  }
14998
+ /** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
14796
14999
  toGLTF(options) {
14797
15000
  return SceneExporter.toGLTF(this.scene, options);
14798
15001
  }
@@ -14835,12 +15038,14 @@ init_request();
14835
15038
  init_Requests();
14836
15039
  var Skins = class {
14837
15040
  //<editor-fold desc="Skins">
15041
+ /** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
14838
15042
  static async fromUuidOrUsername(uuidOrUsername) {
14839
15043
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14840
15044
  return this.fromUuid(uuidOrUsername);
14841
15045
  }
14842
15046
  return this.fromUsername(uuidOrUsername);
14843
15047
  }
15048
+ /** Builds a proxy URL for a UUID without downloading or checking the skin. */
14844
15049
  static async fromUuid(uuid) {
14845
15050
  return this.getMcProxySkinUrl(uuid);
14846
15051
  }
@@ -14852,6 +15057,7 @@ var Skins = class {
14852
15057
  return void 0;
14853
15058
  });
14854
15059
  }
15060
+ /** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
14855
15061
  static async fromMineSkin(mineskinId) {
14856
15062
  return this.getMineSkin(mineskinId).then((mineskin) => {
14857
15063
  if (mineskin) {
@@ -14862,12 +15068,14 @@ var Skins = class {
14862
15068
  }
14863
15069
  //</editor-fold>
14864
15070
  //<editor-fold desc="Capes">
15071
+ /** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
14865
15072
  static async capeFromUuidOrUsername(uuidOrUsername) {
14866
15073
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14867
15074
  return this.capeFromUuid(uuidOrUsername);
14868
15075
  }
14869
15076
  return this.capeFromUsername(uuidOrUsername);
14870
15077
  }
15078
+ /** Builds a proxy URL without checking whether the player has a cape. */
14871
15079
  static async capeFromUuid(uuid) {
14872
15080
  return this.getMcProxyCapeUrl(uuid);
14873
15081
  }
@@ -15022,16 +15230,19 @@ var ChunkData = class _ChunkData {
15022
15230
  this.freeIds = [];
15023
15231
  this.nbt = /* @__PURE__ */ new Map();
15024
15232
  }
15233
+ /** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
15025
15234
  get(index) {
15026
15235
  _ChunkData.validateIndex(index);
15027
15236
  const entry = this.palette[this.indices[index]];
15028
15237
  return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
15029
15238
  }
15239
+ /** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
15030
15240
  snapshot(index) {
15031
15241
  _ChunkData.validateIndex(index);
15032
15242
  const entry = this.palette[this.indices[index]];
15033
15243
  return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
15034
15244
  }
15245
+ /** Copies block data into a cell. An omitted block or an air block clears the cell. */
15035
15246
  set(index, block) {
15036
15247
  _ChunkData.validateIndex(index);
15037
15248
  let state;
@@ -15096,15 +15307,23 @@ var ChunkData = class _ChunkData {
15096
15307
 
15097
15308
  // src/world/AnvilParser.ts
15098
15309
  var AnvilParser = class {
15310
+ /** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
15099
15311
  static getChunkList(data) {
15100
15312
  return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
15101
15313
  }
15314
+ /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
15102
15315
  static async parse(data) {
15103
15316
  const bytes = this.bytes(data);
15104
15317
  const chunks = [];
15105
15318
  for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
15106
15319
  return { chunks };
15107
15320
  }
15321
+ /**
15322
+ * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
15323
+ * @param localX - Chunk x within the region, from 0 to 31.
15324
+ * @param localZ - Chunk z within the region, from 0 to 31.
15325
+ * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
15326
+ */
15108
15327
  static async parseChunk(data, localX, localZ) {
15109
15328
  if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
15110
15329
  throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
@@ -15309,6 +15528,7 @@ var SectionModels = class {
15309
15528
  this.templates = /* @__PURE__ */ new Set();
15310
15529
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
15311
15530
  }
15531
+ /** Returns a cube template, or `undefined` when the block requires an individual render object. */
15312
15532
  async get(blockState, properties = {}) {
15313
15533
  if (!blockState.variants || blockState.multipart) return void 0;
15314
15534
  let states = this.states.get(blockState);
@@ -15362,6 +15582,7 @@ var SectionModels = class {
15362
15582
  }
15363
15583
  return { variants, templates };
15364
15584
  }
15585
+ /** Disposes prepared geometry and clears this world's template caches. */
15365
15586
  clear() {
15366
15587
  for (const template of this.templates) template.geometry.dispose();
15367
15588
  this.templates.clear();
@@ -15412,6 +15633,13 @@ var MineRenderWorld = class {
15412
15633
  this.validatePosBounds(posOrX);
15413
15634
  await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
15414
15635
  }
15636
+ /**
15637
+ * Places a structure's blocks at their stored positions, then refreshes neighboring faces.
15638
+ * Entity NBT is retained by the structure but is not rendered.
15639
+ *
15640
+ * @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
15641
+ * @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
15642
+ */
15415
15643
  async placeMultiBlock(multiblock, useBatches = true, executor) {
15416
15644
  const changes = /* @__PURE__ */ new Map();
15417
15645
  try {
@@ -15463,13 +15691,7 @@ var MineRenderWorld = class {
15463
15691
  async placeChunk(chunk, executor) {
15464
15692
  const changes = /* @__PURE__ */ new Map();
15465
15693
  try {
15466
- const previous = [...this._chunks.entries()].filter(([, section]) => section.x === chunk.x && section.z === chunk.z);
15467
- for (const [key, section] of previous) {
15468
- this._chunks.delete(key);
15469
- await section.clear(async (positions) => {
15470
- for (const pos of positions) changes.set(pos.toArray().join(","), pos);
15471
- });
15472
- }
15694
+ await this.clearChunkColumn(chunk.x, chunk.z, changes);
15473
15695
  for (const section of chunk.sections) {
15474
15696
  const blocks = [];
15475
15697
  for (let index = 0; index < 4096; index++) {
@@ -15489,6 +15711,32 @@ var MineRenderWorld = class {
15489
15711
  await this.updateCulling([...changes.values()]);
15490
15712
  }
15491
15713
  }
15714
+ /**
15715
+ * Removes all sections at integer chunk-column coordinates, then refreshes neighboring faces and fluids.
15716
+ * Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
15717
+ */
15718
+ async unloadChunkColumn(x, z) {
15719
+ if (!Number.isInteger(x) || !Number.isInteger(z)) {
15720
+ throw new RangeError("Chunk column coordinates must be integers");
15721
+ }
15722
+ await this.culling;
15723
+ const changes = /* @__PURE__ */ new Map();
15724
+ try {
15725
+ await this.clearChunkColumn(x, z, changes);
15726
+ } finally {
15727
+ await this.updateCulling([...changes.values()]);
15728
+ }
15729
+ }
15730
+ async clearChunkColumn(x, z, changes) {
15731
+ const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
15732
+ for (const [key, section] of previous) {
15733
+ this._chunks.delete(key);
15734
+ await section.clear(async (positions) => {
15735
+ for (const pos of positions) changes.set(pos.toArray().join(","), pos);
15736
+ });
15737
+ }
15738
+ }
15739
+ /** Removes all chunks and releases their block placements and section meshes. */
15492
15740
  async clear() {
15493
15741
  await this.culling;
15494
15742
  const chunks = [...this._chunks.values()];
@@ -15574,9 +15822,11 @@ var MineRenderWorld = class {
15574
15822
  }
15575
15823
  return chunk;
15576
15824
  }
15825
+ /** Returns the loaded chunk containing a world block position, without creating a chunk. */
15577
15826
  getChunkAt(pos) {
15578
15827
  return this._chunks.get(this.worldPosToChunkKey(pos));
15579
15828
  }
15829
+ /** Converts block coordinates to scene units by multiplying each component by 16. */
15580
15830
  static worldToScenePosition(pos) {
15581
15831
  return new import_three39.Vector3(
15582
15832
  pos.x * 16,
@@ -15584,6 +15834,7 @@ var MineRenderWorld = class {
15584
15834
  pos.z * 16
15585
15835
  );
15586
15836
  }
15837
+ /** Converts scene units to block coordinates without rounding to an integer block. */
15587
15838
  static sceneToWorldPosition(pos) {
15588
15839
  return new import_three39.Vector3(
15589
15840
  pos.x / 16,
@@ -15616,6 +15867,7 @@ var SectionMesh = class _SectionMesh extends import_three40.Group {
15616
15867
  super(...arguments);
15617
15868
  this.ownedMeshes = [];
15618
15869
  }
15870
+ /** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
15619
15871
  static build(entries, maxAtlasSize = 2048) {
15620
15872
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
15621
15873
  const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
@@ -15760,7 +16012,7 @@ var Chunk = class _Chunk {
15760
16012
  return this.setBlockInChunkAt(pos, block, worldPos);
15761
16013
  }
15762
16014
  /**
15763
- * Set a block at integer chunk-local coordinates from 0 to 15.
16015
+ * Places a block at integer chunk-local coordinates from 0 to 15.
15764
16016
  */
15765
16017
  async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
15766
16018
  if (typeof worldPos === "undefined") {
@@ -15809,6 +16061,7 @@ var Chunk = class _Chunk {
15809
16061
  await onBlocksChanged?.([worldPos]);
15810
16062
  }
15811
16063
  }
16064
+ /** Changes visibility at world block coordinates while preserving the block's data. */
15812
16065
  async setBlockVisibleAt(pos, visible) {
15813
16066
  const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
15814
16067
  const block = this.renderedBlocks.get(index);
@@ -15862,6 +16115,7 @@ var Chunk = class _Chunk {
15862
16115
  this.scene.dirty = true;
15863
16116
  this.meshDirty = false;
15864
16117
  }
16118
+ /** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
15865
16119
  async clear(onBlocksChanged = this.onBlocksChanged) {
15866
16120
  const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
15867
16121
  new import_three41.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
@@ -15882,6 +16136,7 @@ var Chunk = class _Chunk {
15882
16136
  static isAir(block) {
15883
16137
  return ChunkData.isAir(block);
15884
16138
  }
16139
+ /** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
15885
16140
  static chunkPosToBlockIndex(pos) {
15886
16141
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
15887
16142
  throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");