minerender 2.1.0-alpha.75 → 2.1.0-alpha.77

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;
@@ -851,8 +864,8 @@ var init_Textures = __esm({
851
864
  for (let y = 0; y < size; y++) {
852
865
  for (let x = 0; x < size; x++) {
853
866
  const offset = (y * size + x) * 4;
854
- const color = (x + y) % 2 === 0 ? 255 : 0;
855
- data[offset] = data[offset + 2] = color;
867
+ const color2 = (x + y) % 2 === 0 ? 255 : 0;
868
+ data[offset] = data[offset + 2] = color2;
856
869
  data[offset + 3] = 255;
857
870
  }
858
871
  }
@@ -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
  }
@@ -1411,17 +1450,17 @@ var init_AssetKey = __esm({
1411
1450
  var DisplayPosition, DISPLAY_POSITIONS;
1412
1451
  var init_DisplayPosition = __esm({
1413
1452
  "src/model/DisplayPosition.ts"() {
1414
- DisplayPosition = /* @__PURE__ */ ((DisplayPosition2) => {
1415
- DisplayPosition2["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1416
- DisplayPosition2["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1417
- DisplayPosition2["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1418
- DisplayPosition2["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1419
- DisplayPosition2["GUI"] = "gui";
1420
- DisplayPosition2["HEAD"] = "head";
1421
- DisplayPosition2["GROUND"] = "ground";
1422
- DisplayPosition2["FIXED"] = "fixed";
1423
- DisplayPosition2["ON_SHELF"] = "on_shelf";
1424
- return DisplayPosition2;
1453
+ DisplayPosition = /* @__PURE__ */ ((DisplayPosition3) => {
1454
+ DisplayPosition3["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1455
+ DisplayPosition3["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1456
+ DisplayPosition3["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1457
+ DisplayPosition3["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1458
+ DisplayPosition3["GUI"] = "gui";
1459
+ DisplayPosition3["HEAD"] = "head";
1460
+ DisplayPosition3["GROUND"] = "ground";
1461
+ DisplayPosition3["FIXED"] = "fixed";
1462
+ DisplayPosition3["ON_SHELF"] = "on_shelf";
1463
+ return DisplayPosition3;
1425
1464
  })(DisplayPosition || {});
1426
1465
  DISPLAY_POSITIONS = Object.values(DisplayPosition);
1427
1466
  }
@@ -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";
@@ -1635,13 +1678,19 @@ var init_Models = __esm({
1635
1678
  return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1636
1679
  return AssetLoader.get(key, "model" /* MODEL */);
1637
1680
  });
1638
- }).then((asset) => {
1639
- if (asset) {
1640
- asset.key = key;
1681
+ }).then((asset2) => {
1682
+ if (asset2) {
1683
+ asset2.key = key;
1641
1684
  }
1642
- return asset;
1685
+ return asset2;
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";
@@ -1718,11 +1772,11 @@ var init_BlockStates = __esm({
1718
1772
  return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1719
1773
  return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
1720
1774
  });
1721
- }).then((asset) => {
1722
- if (asset) {
1723
- asset.key = key;
1775
+ }).then((asset2) => {
1776
+ if (asset2) {
1777
+ asset2.key = key;
1724
1778
  }
1725
- return asset;
1779
+ return asset2;
1726
1780
  });
1727
1781
  }
1728
1782
  static getAll(keys) {
@@ -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
  }
@@ -1862,29 +1925,29 @@ function applyGenericRotation(axis, rotation, obj) {
1862
1925
  break;
1863
1926
  }
1864
1927
  }
1865
- function addBox3WireframeToObject(box, obj, color = 16777215, w = 2) {
1928
+ function addBox3WireframeToObject(box, obj, color2 = 16777215, w = 2) {
1866
1929
  let size = new import_three8.Vector3();
1867
1930
  box.getSize(size);
1868
1931
  let boxGeo = new import_three8.BoxGeometry(size.x, size.y, size.z, 1, 1, 1);
1869
1932
  let wireGeo = new import_three8.EdgesGeometry(boxGeo);
1870
- let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
1933
+ let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
1871
1934
  let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
1872
1935
  obj.add(wireframe);
1873
1936
  let axes = new import_three8.AxesHelper(1);
1874
1937
  obj.add(axes);
1875
1938
  }
1876
- function addWireframeToObject(obj, color = 16777215, w = 2) {
1939
+ function addWireframeToObject(obj, color2 = 16777215, w = 2) {
1877
1940
  let boxGeo = new import_three8.BoxGeometry(16, 16, 16, 1, 1, 1);
1878
1941
  let wireGeo = new import_three8.EdgesGeometry(boxGeo);
1879
- let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
1942
+ let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
1880
1943
  let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
1881
1944
  obj.add(wireframe);
1882
1945
  let axes = new import_three8.AxesHelper(1);
1883
1946
  obj.add(axes);
1884
1947
  }
1885
- function addWireframeToMesh(geo, mesh, color = 16777215, w = 2) {
1948
+ function addWireframeToMesh(geo, mesh, color2 = 16777215, w = 2) {
1886
1949
  let wireGeo = new import_three8.EdgesGeometry(geo);
1887
- let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
1950
+ let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
1888
1951
  let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
1889
1952
  mesh.add(wireframe);
1890
1953
  let axes = new import_three8.AxesHelper(1);
@@ -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));
@@ -2322,27 +2396,27 @@ var init_PackFormats = __esm({
2322
2396
  this.versions = /* @__PURE__ */ new Map();
2323
2397
  }
2324
2398
  /** Resolves pack formats from Mojang's client metadata hosted on assets.mcasset.cloud. */
2325
- static async get(version, type) {
2326
- let pending = this.versions.get(version);
2399
+ static async get(version2, type) {
2400
+ let pending = this.versions.get(version2);
2327
2401
  if (!pending || pending.expires <= Date.now()) {
2328
2402
  pending = {
2329
- formats: this.load(version),
2330
- expires: ["latest", "release", "snapshot"].includes(version) ? Date.now() + 3e5 : Infinity
2403
+ formats: this.load(version2),
2404
+ expires: ["latest", "release", "snapshot"].includes(version2) ? Date.now() + 3e5 : Infinity
2331
2405
  };
2332
- this.versions.set(version, pending);
2406
+ this.versions.set(version2, pending);
2333
2407
  }
2334
2408
  try {
2335
2409
  return (await pending.formats)[type];
2336
2410
  } catch (cause) {
2337
- if (this.versions.get(version) === pending) this.versions.delete(version);
2411
+ if (this.versions.get(version2) === pending) this.versions.delete(version2);
2338
2412
  const option = type === "assets" ? "resourcePackFormat" : "dataPackFormat";
2339
2413
  const reason = cause instanceof Error ? cause.message : String(cause);
2340
- throw new Error(`Could not resolve the pack format for Minecraft ${version}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
2414
+ throw new Error(`Could not resolve the pack format for Minecraft ${version2}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
2341
2415
  }
2342
2416
  }
2343
- static async load(version) {
2417
+ static async load(version2) {
2344
2418
  const response = await Requests.genericRequest({
2345
- url: `https://assets.mcasset.cloud/${encodeURIComponent(version)}/game-version.json`,
2419
+ url: `https://assets.mcasset.cloud/${encodeURIComponent(version2)}/game-version.json`,
2346
2420
  responseType: "json"
2347
2421
  });
2348
2422
  const data = response.data;
@@ -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
  }
@@ -2429,7 +2504,7 @@ var init_ArchiveAssetSource = __esm({
2429
2504
  }
2430
2505
  async load(key, parser) {
2431
2506
  const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
2432
- const version = AssetLoader.version;
2507
+ const version2 = AssetLoader.version;
2433
2508
  let selectedPath = path;
2434
2509
  let url;
2435
2510
  try {
@@ -2437,7 +2512,7 @@ var init_ArchiveAssetSource = __esm({
2437
2512
  if (key.rootType === "assets" || key.rootType === "data") {
2438
2513
  const metadata = await this.getMetadata(key);
2439
2514
  if (metadata.hasOverlays) {
2440
- const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version, key.rootType);
2515
+ const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version2, key.rootType);
2441
2516
  directories = metadata.overlayDirectories(format2);
2442
2517
  }
2443
2518
  }
@@ -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,8 +2609,9 @@ var init_AssetLoader = __esm({
2533
2609
  static get version() {
2534
2610
  return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
2535
2611
  }
2536
- static setVersion(version) {
2537
- this.ROOT = `https://assets.mcasset.cloud/${version}`;
2612
+ /** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
2613
+ static setVersion(version2) {
2614
+ this.ROOT = `https://assets.mcasset.cloud/${version2}`;
2538
2615
  const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
2539
2616
  const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
2540
2617
  if (index !== -1) {
@@ -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,12 +2675,13 @@ ${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 = [];
2596
2682
  for (const { source } of sources) {
2597
- const asset = await source.get(key, parser);
2598
- if (asset != void 0) results.push(asset);
2683
+ const asset2 = await source.get(key, parser);
2684
+ if (asset2 != void 0) results.push(asset2);
2599
2685
  if (await source.blocks(key)) break;
2600
2686
  }
2601
2687
  return results;
@@ -2651,14 +2737,15 @@ 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);
2657
2744
  const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
2658
2745
  try {
2659
- const asset = await pending;
2660
- if (asset) {
2661
- return await ImageLoader.infoToCanvasData(asset);
2746
+ const asset2 = await pending;
2747
+ if (asset2) {
2748
+ return await ImageLoader.infoToCanvasData(asset2);
2662
2749
  }
2663
2750
  return void 0;
2664
2751
  } catch (err) {
@@ -2668,16 +2755,18 @@ 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) => {
2674
- return AssetLoader.get(key, "image" /* IMAGE */).then((asset) => {
2675
- if (asset)
2676
- asset.key = key;
2677
- return asset;
2762
+ return AssetLoader.get(key, "image" /* IMAGE */).then((asset2) => {
2763
+ if (asset2)
2764
+ asset2.key = key;
2765
+ return asset2;
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);
@@ -2686,14 +2775,15 @@ var init_ModelTextures = __esm({
2686
2775
  const keyStr = key.serialize();
2687
2776
  return Caching.textureMetaCache.get(keyStr, (k) => {
2688
2777
  return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
2689
- return AssetLoader.get(key, "meta" /* META */).then((asset) => {
2690
- if (asset)
2691
- asset.key = key;
2692
- return asset;
2778
+ return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
2779
+ if (asset2)
2780
+ asset2.key = key;
2781
+ return asset2;
2693
2782
  });
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 = {};
@@ -3245,12 +3347,12 @@ var init_UVMapper = __esm({
3245
3347
  } else {
3246
3348
  uniqueTextureNames.push(textureKey);
3247
3349
  const assetKey = AssetKey.parse("textures", textureValue, model.key);
3248
- promises.push(ModelTextures.get(assetKey).then((asset) => {
3249
- if (!asset) {
3350
+ promises.push(ModelTextures.get(assetKey).then((asset2) => {
3351
+ if (!asset2) {
3250
3352
  console.warn(p5, "Missing texture", assetKey);
3251
3353
  return;
3252
3354
  }
3253
- textureMap[textureKey] = new WrappedImage(asset);
3355
+ textureMap[textureKey] = new WrappedImage(asset2);
3254
3356
  }));
3255
3357
  promises.push(ModelTextures.getMeta(assetKey).then((meta) => {
3256
3358
  metaMap[textureKey] = meta;
@@ -3589,14 +3691,15 @@ 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}/` : "";
3595
3698
  const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
3596
- const list = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3597
- if (!list) return [];
3598
- const files = list.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3599
- const children = await Promise.all(list.directories.map((directory) => collect(`${prefix2}${directory}`)));
3699
+ const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3700
+ if (!list2) return [];
3701
+ const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3702
+ const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
3600
3703
  return [...files, ...children.flat()];
3601
3704
  };
3602
3705
  return collect("");
@@ -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;
@@ -3669,8 +3783,8 @@ var init_Entities = __esm({
3669
3783
  if (await ModelTextures.get(texture2)) return texture2;
3670
3784
  }
3671
3785
  const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
3672
- const list = await AssetLoader.get(listKey, "list" /* LIST */);
3673
- const files = list?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3786
+ const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
3787
+ const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3674
3788
  const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
3675
3789
  if (!file) return void 0;
3676
3790
  const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
@@ -3775,27 +3889,27 @@ var init_EntityAnimationPlayer = __esm({
3775
3889
  for (const layer of layers) {
3776
3890
  const objects = [];
3777
3891
  if (poses) {
3778
- layer.traverse((object2) => {
3779
- if (poses.has(object2)) objects.push(object2);
3892
+ layer.traverse((object3) => {
3893
+ if (poses.has(object3)) objects.push(object3);
3780
3894
  });
3781
3895
  } else {
3782
3896
  for (const bone of Object.keys(animation.bones)) {
3783
- const object2 = layer.getObjectByName(`group:${bone}`);
3784
- if (object2) objects.push(object2);
3897
+ const object3 = layer.getObjectByName(`group:${bone}`);
3898
+ if (object3) objects.push(object3);
3785
3899
  }
3786
3900
  }
3787
- for (const object2 of objects) {
3788
- const bone = object2.name.slice("group:".length);
3789
- const pose = poses?.get(object2);
3901
+ for (const object3 of objects) {
3902
+ const bone = object3.name.slice("group:".length);
3903
+ const pose = poses?.get(object3);
3790
3904
  this.parts.push({
3791
3905
  bone,
3792
- object: object2,
3793
- position: object2.position.clone(),
3794
- rotation: object2.rotation.clone(),
3795
- scale: object2.scale.clone(),
3796
- basePosition: pose ? new import_three11.Vector3(...pose.offset) : object2.position.clone(),
3797
- baseRotation: pose ? new import_three11.Euler(...pose.rotation, "ZYX") : object2.rotation.clone(),
3798
- baseScale: pose ? new import_three11.Vector3(...pose.scale ?? [1, 1, 1]) : object2.scale.clone()
3906
+ object: object3,
3907
+ position: object3.position.clone(),
3908
+ rotation: object3.rotation.clone(),
3909
+ scale: object3.scale.clone(),
3910
+ basePosition: pose ? new import_three11.Vector3(...pose.offset) : object3.position.clone(),
3911
+ baseRotation: pose ? new import_three11.Euler(...pose.rotation, "ZYX") : object3.rotation.clone(),
3912
+ baseScale: pose ? new import_three11.Vector3(...pose.scale ?? [1, 1, 1]) : object3.scale.clone()
3799
3913
  });
3800
3914
  }
3801
3915
  }
@@ -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");
@@ -4150,10 +4275,10 @@ var init_SceneObject = __esm({
4150
4275
  return new import_three12.InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
4151
4276
  };
4152
4277
  const matrix = grow(mesh.instanceMatrix, 0);
4153
- const color = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
4278
+ const color2 = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
4154
4279
  mesh.dispose();
4155
4280
  mesh.instanceMatrix = matrix;
4156
- mesh.instanceColor = color;
4281
+ mesh.instanceColor = color2;
4157
4282
  }
4158
4283
  *activeInstanceIndices() {
4159
4284
  if (this.instanceMesh) {
@@ -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()) {
@@ -4322,15 +4451,15 @@ var init_SceneObject = __esm({
4322
4451
  }
4323
4452
  }
4324
4453
  //</editor-fold>
4325
- toggleObjectVisibility(object2, visible) {
4326
- if (object2) {
4454
+ toggleObjectVisibility(object3, visible) {
4455
+ if (object3) {
4327
4456
  if (typeof visible !== "undefined") {
4328
- object2.visible = visible;
4457
+ object3.visible = visible;
4329
4458
  } else {
4330
- object2.visible = !object2.visible;
4459
+ object3.visible = !object3.visible;
4331
4460
  }
4332
4461
  this.notifyDirty();
4333
- return object2.visible;
4462
+ return object3.visible;
4334
4463
  }
4335
4464
  this.notifyDirty();
4336
4465
  return false;
@@ -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
  }
@@ -4621,8 +4755,8 @@ var init_EntityObject = __esm({
4621
4755
  this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
4622
4756
  }
4623
4757
  }
4624
- this.getLayerGroup(name)?.traverse((object2) => {
4625
- if (isMesh(object2)) object2.material = mat;
4758
+ this.getLayerGroup(name)?.traverse((object3) => {
4759
+ if (isMesh(object3)) object3.material = mat;
4626
4760
  });
4627
4761
  }));
4628
4762
  if (!this.children.length) {
@@ -4950,8 +5084,8 @@ function toTrianglesDrawMode(geometry, drawMode) {
4950
5084
  return geometry;
4951
5085
  }
4952
5086
  }
4953
- function computeMorphedAttributes(object2) {
4954
- if (object2.geometry.isBufferGeometry !== true) {
5087
+ function computeMorphedAttributes(object3) {
5088
+ if (object3.geometry.isBufferGeometry !== true) {
4955
5089
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4956
5090
  return null;
4957
5091
  }
@@ -4964,11 +5098,11 @@ function computeMorphedAttributes(object2) {
4964
5098
  const _morphA = new import_three22.Vector3();
4965
5099
  const _morphB = new import_three22.Vector3();
4966
5100
  const _morphC = new import_three22.Vector3();
4967
- function _calculateMorphedAttributeData(object3, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
5101
+ function _calculateMorphedAttributeData(object4, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4968
5102
  _vA.fromBufferAttribute(attribute, a2);
4969
5103
  _vB.fromBufferAttribute(attribute, b2);
4970
5104
  _vC.fromBufferAttribute(attribute, c2);
4971
- const morphInfluences = object3.morphTargetInfluences;
5105
+ const morphInfluences = object4.morphTargetInfluences;
4972
5106
  if (material2.morphTargets && morphAttribute && morphInfluences) {
4973
5107
  _morphA.set(0, 0, 0);
4974
5108
  _morphB.set(0, 0, 0);
@@ -4994,10 +5128,10 @@ function computeMorphedAttributes(object2) {
4994
5128
  _vB.add(_morphB);
4995
5129
  _vC.add(_morphC);
4996
5130
  }
4997
- if (object3.isSkinnedMesh) {
4998
- object3.boneTransform(a2, _vA);
4999
- object3.boneTransform(b2, _vB);
5000
- object3.boneTransform(c2, _vC);
5131
+ if (object4.isSkinnedMesh) {
5132
+ object4.boneTransform(a2, _vA);
5133
+ object4.boneTransform(b2, _vB);
5134
+ object4.boneTransform(c2, _vC);
5001
5135
  }
5002
5136
  modifiedAttributeArray[a2 * 3 + 0] = _vA.x;
5003
5137
  modifiedAttributeArray[a2 * 3 + 1] = _vA.y;
@@ -5009,8 +5143,8 @@ function computeMorphedAttributes(object2) {
5009
5143
  modifiedAttributeArray[c2 * 3 + 1] = _vC.y;
5010
5144
  modifiedAttributeArray[c2 * 3 + 2] = _vC.z;
5011
5145
  }
5012
- const geometry = object2.geometry;
5013
- const material = object2.material;
5146
+ const geometry = object3.geometry;
5147
+ const material = object3.material;
5014
5148
  let a, b, c;
5015
5149
  const index = geometry.index;
5016
5150
  const positionAttribute = geometry.attributes.position;
@@ -5037,7 +5171,7 @@ function computeMorphedAttributes(object2) {
5037
5171
  b = index.getX(j + 1);
5038
5172
  c = index.getX(j + 2);
5039
5173
  _calculateMorphedAttributeData(
5040
- object2,
5174
+ object3,
5041
5175
  groupMaterial,
5042
5176
  positionAttribute,
5043
5177
  morphPosition,
@@ -5048,7 +5182,7 @@ function computeMorphedAttributes(object2) {
5048
5182
  modifiedPosition
5049
5183
  );
5050
5184
  _calculateMorphedAttributeData(
5051
- object2,
5185
+ object3,
5052
5186
  groupMaterial,
5053
5187
  normalAttribute,
5054
5188
  morphNormal,
@@ -5068,7 +5202,7 @@ function computeMorphedAttributes(object2) {
5068
5202
  b = index.getX(i + 1);
5069
5203
  c = index.getX(i + 2);
5070
5204
  _calculateMorphedAttributeData(
5071
- object2,
5205
+ object3,
5072
5206
  material,
5073
5207
  positionAttribute,
5074
5208
  morphPosition,
@@ -5079,7 +5213,7 @@ function computeMorphedAttributes(object2) {
5079
5213
  modifiedPosition
5080
5214
  );
5081
5215
  _calculateMorphedAttributeData(
5082
- object2,
5216
+ object3,
5083
5217
  material,
5084
5218
  normalAttribute,
5085
5219
  morphNormal,
@@ -5103,7 +5237,7 @@ function computeMorphedAttributes(object2) {
5103
5237
  b = j + 1;
5104
5238
  c = j + 2;
5105
5239
  _calculateMorphedAttributeData(
5106
- object2,
5240
+ object3,
5107
5241
  groupMaterial,
5108
5242
  positionAttribute,
5109
5243
  morphPosition,
@@ -5114,7 +5248,7 @@ function computeMorphedAttributes(object2) {
5114
5248
  modifiedPosition
5115
5249
  );
5116
5250
  _calculateMorphedAttributeData(
5117
- object2,
5251
+ object3,
5118
5252
  groupMaterial,
5119
5253
  normalAttribute,
5120
5254
  morphNormal,
@@ -5134,7 +5268,7 @@ function computeMorphedAttributes(object2) {
5134
5268
  b = i + 1;
5135
5269
  c = i + 2;
5136
5270
  _calculateMorphedAttributeData(
5137
- object2,
5271
+ object3,
5138
5272
  material,
5139
5273
  positionAttribute,
5140
5274
  morphPosition,
@@ -5145,7 +5279,7 @@ function computeMorphedAttributes(object2) {
5145
5279
  modifiedPosition
5146
5280
  );
5147
5281
  _calculateMorphedAttributeData(
5148
- object2,
5282
+ object3,
5149
5283
  material,
5150
5284
  normalAttribute,
5151
5285
  morphNormal,
@@ -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];
@@ -5401,9 +5537,9 @@ var init_ItemTints = __esm({
5401
5537
  }));
5402
5538
  return colors;
5403
5539
  }
5404
- static rgb(color) {
5405
- if (!Array.isArray(color)) return color & 16777215;
5406
- const [red, green, blue] = color.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
5540
+ static rgb(color2) {
5541
+ if (!Array.isArray(color2)) return color2 & 16777215;
5542
+ const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
5407
5543
  return red << 16 | green << 8 | blue;
5408
5544
  }
5409
5545
  };
@@ -5459,13 +5595,13 @@ var init_ModelObject = __esm({
5459
5595
  transform.multiply(new import_three25.Matrix4().makeTranslation(-8, -8, -8));
5460
5596
  try {
5461
5597
  for (const part of parts) {
5462
- const object2 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
5463
- object2.matrix.copy(transform).multiply(part.transform);
5464
- object2.matrixWorldNeedsUpdate = true;
5465
- object2.matrixAutoUpdate = false;
5466
- this.add(object2);
5467
- await object2.init();
5468
- object2.iterateAllMeshes((mesh) => {
5598
+ const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
5599
+ object3.matrix.copy(transform).multiply(part.transform);
5600
+ object3.matrixWorldNeedsUpdate = true;
5601
+ object3.matrixAutoUpdate = false;
5602
+ this.add(object3);
5603
+ await object3.init();
5604
+ object3.iterateAllMeshes((mesh) => {
5469
5605
  const source = mesh.material;
5470
5606
  let material = this.specialMaterials.get(source);
5471
5607
  if (!material) {
@@ -5476,7 +5612,7 @@ var init_ModelObject = __esm({
5476
5612
  mesh.material = material;
5477
5613
  });
5478
5614
  for (const [name, rotation] of Object.entries(part.rotations)) {
5479
- object2.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
5615
+ object3.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
5480
5616
  }
5481
5617
  }
5482
5618
  } catch (error) {
@@ -5529,9 +5665,9 @@ var init_ModelObject = __esm({
5529
5665
  if (tintIndex === void 0 || tintIndex < 0) continue;
5530
5666
  const tint = this.options.tints[tintIndex];
5531
5667
  if (tint === void 0) continue;
5532
- const color = new import_three25.Color(tint);
5668
+ const color2 = new import_three25.Color(tint);
5533
5669
  for (let vertex = 0; vertex < 4; vertex++) {
5534
- color.toArray(colors, (faceIndex * 4 + vertex) * 3);
5670
+ color2.toArray(colors, (faceIndex * 4 + vertex) * 3);
5535
5671
  }
5536
5672
  }
5537
5673
  elGeo.setAttribute("color", new import_three25.BufferAttribute(colors, 3));
@@ -5846,9 +5982,9 @@ var init_FluidModelObject = __esm({
5846
5982
  }
5847
5983
  }
5848
5984
  geometry.clearGroups();
5849
- const color = new import_three31.Color(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
5985
+ const color2 = new import_three31.Color(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
5850
5986
  const colors = new Float32Array(geometry.getAttribute("position").count * 3);
5851
- for (let i = 0; i < colors.length; i += 3) color.toArray(colors, i);
5987
+ for (let i = 0; i < colors.length; i += 3) color2.toArray(colors, i);
5852
5988
  geometry.setAttribute("color", new import_three31.BufferAttribute(colors, 3));
5853
5989
  geometry.computeBoundingBox();
5854
5990
  geometry.computeBoundingSphere();
@@ -5959,6 +6095,7 @@ __export(index_node_exports, {
5959
6095
  RequestError: () => RequestError,
5960
6096
  Requests: () => Requests,
5961
6097
  SKIN_PARTS: () => SKIN_PARTS,
6098
+ SceneDocumentLoader: () => SceneDocumentLoader,
5962
6099
  SceneExporter: () => SceneExporter,
5963
6100
  SceneInspector: () => SceneInspector,
5964
6101
  SceneObject: () => SceneObject,
@@ -6034,6 +6171,7 @@ __export(index_node_exports, {
6034
6171
  mergeBufferAttributes: () => mergeBufferAttributes,
6035
6172
  mergeBufferGeometries: () => mergeBufferGeometries,
6036
6173
  mergeVertices: () => mergeVertices,
6174
+ parseSceneDocument: () => parseSceneDocument,
6037
6175
  prefix: () => prefix,
6038
6176
  registerNodeEnv: () => registerNodeEnv,
6039
6177
  sampleEntityAnimation: () => sampleEntityAnimation,
@@ -6124,9 +6262,9 @@ var NodeEnv = class {
6124
6262
  imageSize(data) {
6125
6263
  return (0, import_image_size.default)(Buffer.isBuffer(data) ? data : Buffer.from(data));
6126
6264
  }
6127
- openCache(name, version) {
6265
+ openCache(name, version2) {
6128
6266
  return new NodeCache(import_node_persist.default.create({
6129
- dir: name + version,
6267
+ dir: name + version2,
6130
6268
  encoding: "utf8"
6131
6269
  }));
6132
6270
  }
@@ -6158,6 +6296,7 @@ var Meshes = class _Meshes {
6158
6296
  static createBox(key) {
6159
6297
  return new import_three6.Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
6160
6298
  }
6299
+ /** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
6161
6300
  static getBox(key) {
6162
6301
  const keyStr = serializeMeshKey(key);
6163
6302
  return Caching.boxMeshCache.get(keyStr, (k) => {
@@ -6270,6 +6409,7 @@ init_AssetParsers();
6270
6409
  init_ModelTextures();
6271
6410
  init_Caching();
6272
6411
  var Fonts = class {
6412
+ /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
6273
6413
  static async get(key = "minecraft:default") {
6274
6414
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
6275
6415
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -6285,8 +6425,8 @@ var Fonts = class {
6285
6425
  const stack = await AssetLoader.getAll(key, "json" /* JSON */);
6286
6426
  if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
6287
6427
  const glyphs = /* @__PURE__ */ new Map();
6288
- for (const asset of stack) {
6289
- for (const provider of asset.providers) {
6428
+ for (const asset2 of stack) {
6429
+ for (const provider of asset2.providers) {
6290
6430
  if (provider.filter?.uniform || provider.filter?.jp) continue;
6291
6431
  let provided;
6292
6432
  switch (provider.type) {
@@ -6425,13 +6565,13 @@ var OBJExporter = class {
6425
6565
  * @param {Object3D} object - The 3D object to export.
6426
6566
  * @return {string} The exported OBJ.
6427
6567
  */
6428
- parse(object2) {
6568
+ parse(object3) {
6429
6569
  let output = "";
6430
6570
  let indexVertex = 0;
6431
6571
  let indexVertexUvs = 0;
6432
6572
  let indexNormals = 0;
6433
6573
  const vertex = new import_three16.Vector3();
6434
- const color = new import_three16.Color();
6574
+ const color2 = new import_three16.Color();
6435
6575
  const normal = new import_three16.Vector3();
6436
6576
  const uv = new import_three16.Vector2();
6437
6577
  const face = [];
@@ -6530,9 +6670,9 @@ var OBJExporter = class {
6530
6670
  vertex.applyMatrix4(points.matrixWorld);
6531
6671
  output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
6532
6672
  if (colors !== void 0) {
6533
- color.fromBufferAttribute(colors, i);
6534
- import_three16.ColorManagement.workingToColorSpace(color, import_three16.SRGBColorSpace);
6535
- output += " " + color.r + " " + color.g + " " + color.b;
6673
+ color2.fromBufferAttribute(colors, i);
6674
+ import_three16.ColorManagement.workingToColorSpace(color2, import_three16.SRGBColorSpace);
6675
+ output += " " + color2.r + " " + color2.g + " " + color2.b;
6536
6676
  }
6537
6677
  output += "\n";
6538
6678
  }
@@ -6544,7 +6684,7 @@ var OBJExporter = class {
6544
6684
  }
6545
6685
  indexVertex += nbVertex;
6546
6686
  }
6547
- object2.traverse(function(child) {
6687
+ object3.traverse(function(child) {
6548
6688
  if (child.isMesh === true) {
6549
6689
  parseMesh(child);
6550
6690
  }
@@ -6572,9 +6712,9 @@ var PLYExporter = class {
6572
6712
  * @param {PLYExporter~Options} options - The export options.
6573
6713
  * @return {?(string|ArrayBuffer)} The exported PLY.
6574
6714
  */
6575
- parse(object2, onDone, options = {}) {
6715
+ parse(object3, onDone, options = {}) {
6576
6716
  function traverseMeshes(cb) {
6577
- object2.traverse(function(child) {
6717
+ object3.traverse(function(child) {
6578
6718
  if (child.isMesh === true || child.isPoints) {
6579
6719
  const mesh = child;
6580
6720
  const geometry = mesh.geometry;
@@ -6607,7 +6747,7 @@ var PLYExporter = class {
6607
6747
  for (const name of customAttributeNames) customTypes[name] = "float";
6608
6748
  let vertexCount = 0;
6609
6749
  let faceCount = 0;
6610
- object2.traverse(function(child) {
6750
+ object3.traverse(function(child) {
6611
6751
  if (child.isMesh === true) {
6612
6752
  const mesh = child;
6613
6753
  const geometry = mesh.geometry;
@@ -7201,8 +7341,8 @@ function equalArray(array1, array2) {
7201
7341
  return element === array2[index];
7202
7342
  });
7203
7343
  }
7204
- function stringToArrayBuffer(text) {
7205
- return new TextEncoder().encode(text).buffer;
7344
+ function stringToArrayBuffer(text2) {
7345
+ return new TextEncoder().encode(text2).buffer;
7206
7346
  }
7207
7347
  function isIdentityMatrix(matrix) {
7208
7348
  return equalArray(matrix.elements, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
@@ -7395,12 +7535,12 @@ var GLTFWriter = class {
7395
7535
  * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
7396
7536
  * @param {Object} objectDef
7397
7537
  */
7398
- serializeUserData(object2, objectDef) {
7399
- if (Object.keys(object2.userData).length === 0) return;
7538
+ serializeUserData(object3, objectDef) {
7539
+ if (Object.keys(object3.userData).length === 0) return;
7400
7540
  const options = this.options;
7401
7541
  const extensionsUsed = this.extensionsUsed;
7402
7542
  try {
7403
- const json = JSON.parse(JSON.stringify(object2.userData));
7543
+ const json = JSON.parse(JSON.stringify(object3.userData));
7404
7544
  if (options.includeCustomExtensions && json.gltfExtensions) {
7405
7545
  if (objectDef.extensions === void 0) objectDef.extensions = {};
7406
7546
  for (const extensionName in json.gltfExtensions) {
@@ -7411,7 +7551,7 @@ var GLTFWriter = class {
7411
7551
  }
7412
7552
  if (Object.keys(json).length > 0) objectDef.extras = json;
7413
7553
  } catch (error) {
7414
- console.warn("THREE.GLTFExporter: userData of '" + object2.name + "' won't be serialized because of JSON.stringify error - " + error.message);
7554
+ console.warn("THREE.GLTFExporter: userData of '" + object3.name + "' won't be serialized because of JSON.stringify error - " + error.message);
7415
7555
  }
7416
7556
  }
7417
7557
  /**
@@ -7920,9 +8060,9 @@ var GLTFWriter = class {
7920
8060
  if (material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true) {
7921
8061
  console.warn("GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.");
7922
8062
  }
7923
- const color = material.color.toArray().concat([material.opacity]);
7924
- if (!equalArray(color, [1, 1, 1, 1])) {
7925
- materialDef.pbrMetallicRoughness.baseColorFactor = color;
8063
+ const color2 = material.color.toArray().concat([material.opacity]);
8064
+ if (!equalArray(color2, [1, 1, 1, 1])) {
8065
+ materialDef.pbrMetallicRoughness.baseColorFactor = color2;
7926
8066
  }
7927
8067
  if (material.isMeshStandardMaterial) {
7928
8068
  materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
@@ -8337,13 +8477,13 @@ var GLTFWriter = class {
8337
8477
  * @param {THREE.Object3D} object
8338
8478
  * @return {?number}
8339
8479
  */
8340
- processSkin(object2) {
8480
+ processSkin(object3) {
8341
8481
  const json = this.json;
8342
8482
  const nodeMap = this.nodeMap;
8343
- const node = json.nodes[nodeMap.get(object2)];
8344
- const skeleton = object2.skeleton;
8483
+ const node = json.nodes[nodeMap.get(object3)];
8484
+ const skeleton = object3.skeleton;
8345
8485
  if (skeleton === void 0) return null;
8346
- const rootJoint = object2.skeleton.bones[0];
8486
+ const rootJoint = object3.skeleton.bones[0];
8347
8487
  if (rootJoint === void 0) return null;
8348
8488
  const joints = [];
8349
8489
  const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
@@ -8351,7 +8491,7 @@ var GLTFWriter = class {
8351
8491
  for (let i = 0; i < skeleton.bones.length; ++i) {
8352
8492
  joints.push(nodeMap.get(skeleton.bones[i]));
8353
8493
  temporaryBoneInverse.copy(skeleton.boneInverses[i]);
8354
- temporaryBoneInverse.multiply(object2.bindMatrix).toArray(inverseBindMatrices, i * 16);
8494
+ temporaryBoneInverse.multiply(object3.bindMatrix).toArray(inverseBindMatrices, i * 16);
8355
8495
  }
8356
8496
  if (json.skins === void 0) json.skins = [];
8357
8497
  json.skins.push({
@@ -8367,19 +8507,19 @@ var GLTFWriter = class {
8367
8507
  * @param {THREE.Object3D} object Object3D to processNodeAsync
8368
8508
  * @return {Promise<number>} Index of the node in the nodes list
8369
8509
  */
8370
- async processNodeAsync(object2) {
8510
+ async processNodeAsync(object3) {
8371
8511
  const json = this.json;
8372
8512
  const options = this.options;
8373
8513
  const nodeMap = this.nodeMap;
8374
8514
  if (!json.nodes) json.nodes = [];
8375
- if (object2.pivot !== null) {
8376
- return await this._processNodeWithPivotAsync(object2);
8515
+ if (object3.pivot !== null) {
8516
+ return await this._processNodeWithPivotAsync(object3);
8377
8517
  }
8378
8518
  const nodeDef = {};
8379
8519
  if (options.trs) {
8380
- const rotation = object2.quaternion.toArray();
8381
- const position = object2.position.toArray();
8382
- const scale = object2.scale.toArray();
8520
+ const rotation = object3.quaternion.toArray();
8521
+ const position = object3.position.toArray();
8522
+ const scale = object3.scale.toArray();
8383
8523
  if (!equalArray(rotation, [0, 0, 0, 1])) {
8384
8524
  nodeDef.rotation = rotation;
8385
8525
  }
@@ -8390,28 +8530,28 @@ var GLTFWriter = class {
8390
8530
  nodeDef.scale = scale;
8391
8531
  }
8392
8532
  } else {
8393
- if (object2.matrixAutoUpdate) {
8394
- object2.updateMatrix();
8533
+ if (object3.matrixAutoUpdate) {
8534
+ object3.updateMatrix();
8395
8535
  }
8396
- if (isIdentityMatrix(object2.matrix) === false) {
8397
- nodeDef.matrix = object2.matrix.elements;
8536
+ if (isIdentityMatrix(object3.matrix) === false) {
8537
+ nodeDef.matrix = object3.matrix.elements;
8398
8538
  }
8399
8539
  }
8400
- if (object2.name !== "") nodeDef.name = String(object2.name);
8401
- this.serializeUserData(object2, nodeDef);
8402
- if (object2.isMesh || object2.isLine || object2.isPoints) {
8403
- const meshIndex = await this.processMeshAsync(object2);
8540
+ if (object3.name !== "") nodeDef.name = String(object3.name);
8541
+ this.serializeUserData(object3, nodeDef);
8542
+ if (object3.isMesh || object3.isLine || object3.isPoints) {
8543
+ const meshIndex = await this.processMeshAsync(object3);
8404
8544
  if (meshIndex !== null) nodeDef.mesh = meshIndex;
8405
- } else if (object2.isCamera) {
8406
- nodeDef.camera = this.processCamera(object2);
8545
+ } else if (object3.isCamera) {
8546
+ nodeDef.camera = this.processCamera(object3);
8407
8547
  }
8408
- if (object2.isSkinnedMesh) this.skins.push(object2);
8548
+ if (object3.isSkinnedMesh) this.skins.push(object3);
8409
8549
  const nodeIndex = json.nodes.push(nodeDef) - 1;
8410
- nodeMap.set(object2, nodeIndex);
8411
- if (object2.children.length > 0) {
8550
+ nodeMap.set(object3, nodeIndex);
8551
+ if (object3.children.length > 0) {
8412
8552
  const children = [];
8413
- for (let i = 0, l = object2.children.length; i < l; i++) {
8414
- const child = object2.children[i];
8553
+ for (let i = 0, l = object3.children.length; i < l; i++) {
8554
+ const child = object3.children[i];
8415
8555
  if (child.visible || options.onlyVisible === false) {
8416
8556
  const childNodeIndex = await this.processNodeAsync(child);
8417
8557
  if (childNodeIndex !== null) children.push(childNodeIndex);
@@ -8420,7 +8560,7 @@ var GLTFWriter = class {
8420
8560
  if (children.length > 0) nodeDef.children = children;
8421
8561
  }
8422
8562
  await this._invokeAllAsync(function(ext) {
8423
- ext.writeNode && ext.writeNode(object2, nodeDef);
8563
+ ext.writeNode && ext.writeNode(object3, nodeDef);
8424
8564
  });
8425
8565
  return nodeIndex;
8426
8566
  }
@@ -8429,19 +8569,19 @@ var GLTFWriter = class {
8429
8569
  * @param {THREE.Object3D} object Object3D with pivot
8430
8570
  * @return {Promise<number>} Index of the container node
8431
8571
  */
8432
- async _processNodeWithPivotAsync(object2) {
8572
+ async _processNodeWithPivotAsync(object3) {
8433
8573
  const json = this.json;
8434
8574
  const options = this.options;
8435
8575
  const nodeMap = this.nodeMap;
8436
- const pivot = object2.pivot;
8576
+ const pivot = object3.pivot;
8437
8577
  const containerDef = {};
8438
- const rotation = object2.quaternion.toArray();
8578
+ const rotation = object3.quaternion.toArray();
8439
8579
  const position = [
8440
- object2.position.x + pivot.x,
8441
- object2.position.y + pivot.y,
8442
- object2.position.z + pivot.z
8580
+ object3.position.x + pivot.x,
8581
+ object3.position.y + pivot.y,
8582
+ object3.position.z + pivot.z
8443
8583
  ];
8444
- const scale = object2.scale.toArray();
8584
+ const scale = object3.scale.toArray();
8445
8585
  if (!equalArray(rotation, [0, 0, 0, 1])) {
8446
8586
  containerDef.rotation = rotation;
8447
8587
  }
@@ -8452,28 +8592,28 @@ var GLTFWriter = class {
8452
8592
  containerDef.scale = scale;
8453
8593
  }
8454
8594
  containerDef.extras = { pivot: pivot.toArray() };
8455
- if (object2.name !== "") containerDef.name = String(object2.name);
8456
- this.serializeUserData(object2, containerDef);
8595
+ if (object3.name !== "") containerDef.name = String(object3.name);
8596
+ this.serializeUserData(object3, containerDef);
8457
8597
  const containerIndex = json.nodes.push(containerDef) - 1;
8458
- nodeMap.set(object2, containerIndex);
8598
+ nodeMap.set(object3, containerIndex);
8459
8599
  const childDef = {};
8460
8600
  const childPosition = [-pivot.x, -pivot.y, -pivot.z];
8461
8601
  if (!equalArray(childPosition, [0, 0, 0])) {
8462
8602
  childDef.translation = childPosition;
8463
8603
  }
8464
- if (object2.isMesh || object2.isLine || object2.isPoints) {
8465
- const meshIndex = await this.processMeshAsync(object2);
8604
+ if (object3.isMesh || object3.isLine || object3.isPoints) {
8605
+ const meshIndex = await this.processMeshAsync(object3);
8466
8606
  if (meshIndex !== null) childDef.mesh = meshIndex;
8467
- } else if (object2.isCamera) {
8468
- childDef.camera = this.processCamera(object2);
8607
+ } else if (object3.isCamera) {
8608
+ childDef.camera = this.processCamera(object3);
8469
8609
  }
8470
- if (object2.isSkinnedMesh) this.skins.push(object2);
8610
+ if (object3.isSkinnedMesh) this.skins.push(object3);
8471
8611
  const childIndex = json.nodes.push(childDef) - 1;
8472
8612
  const containerChildren = [childIndex];
8473
- if (object2.children.length > 0) {
8613
+ if (object3.children.length > 0) {
8474
8614
  const grandchildren = [];
8475
- for (let i = 0, l = object2.children.length; i < l; i++) {
8476
- const child = object2.children[i];
8615
+ for (let i = 0, l = object3.children.length; i < l; i++) {
8616
+ const child = object3.children[i];
8477
8617
  if (child.visible || options.onlyVisible === false) {
8478
8618
  const childNodeIndex = await this.processNodeAsync(child);
8479
8619
  if (childNodeIndex !== null) grandchildren.push(childNodeIndex);
@@ -8483,7 +8623,7 @@ var GLTFWriter = class {
8483
8623
  }
8484
8624
  containerDef.children = containerChildren;
8485
8625
  await this._invokeAllAsync(function(ext) {
8486
- ext.writeNode && ext.writeNode(object2, containerDef);
8626
+ ext.writeNode && ext.writeNode(object3, containerDef);
8487
8627
  });
8488
8628
  return containerIndex;
8489
8629
  }
@@ -8926,10 +9066,10 @@ var GLTFMeshGpuInstancing = class {
8926
9066
  this.writer = writer;
8927
9067
  this.name = "EXT_mesh_gpu_instancing";
8928
9068
  }
8929
- writeNode(object2, nodeDef) {
8930
- if (!object2.isInstancedMesh) return;
9069
+ writeNode(object3, nodeDef) {
9070
+ if (!object3.isInstancedMesh) return;
8931
9071
  const writer = this.writer;
8932
- const mesh = object2;
9072
+ const mesh = object3;
8933
9073
  const translationAttr = new Float32Array(mesh.count * 3);
8934
9074
  const rotationAttr = new Float32Array(mesh.count * 4);
8935
9075
  const scaleAttr = new Float32Array(mesh.count * 3);
@@ -9119,8 +9259,8 @@ var SceneExporter = class {
9119
9259
  }
9120
9260
  }
9121
9261
  };
9122
- function localMatrix(object2) {
9123
- return object2.matrixAutoUpdate ? new import_three19.Matrix4().compose(object2.position, object2.quaternion, object2.scale) : object2.matrix.clone();
9262
+ function localMatrix(object3) {
9263
+ return object3.matrixAutoUpdate ? new import_three19.Matrix4().compose(object3.position, object3.quaternion, object3.scale) : object3.matrix.clone();
9124
9264
  }
9125
9265
  function createSnapshot(root, gltf, bakeColors = false) {
9126
9266
  const scene = new import_three19.Scene();
@@ -9224,24 +9364,24 @@ function createSnapshot(root, gltf, bakeColors = false) {
9224
9364
  scene.add(copy);
9225
9365
  }
9226
9366
  };
9227
- const visit = (object2, parentWorld) => {
9228
- if (!object2.visible) return;
9229
- const world = parentWorld.clone().multiply(localMatrix(object2));
9230
- const mesh = object2;
9367
+ const visit = (object3, parentWorld) => {
9368
+ if (!object3.visible) return;
9369
+ const world = parentWorld.clone().multiply(localMatrix(object3));
9370
+ const mesh = object3;
9231
9371
  if (mesh.isMesh) {
9232
9372
  const instanced = mesh;
9233
9373
  if (instanced.isInstancedMesh) {
9234
9374
  for (let i = 0; i < instanced.count; i++) {
9235
9375
  const matrix = new import_three19.Matrix4();
9236
9376
  instanced.getMatrixAt(i, matrix);
9237
- const color = instanced.instanceColor ? new import_three19.Color().fromBufferAttribute(instanced.instanceColor, i) : void 0;
9238
- addMesh(mesh, world.clone().multiply(matrix), color);
9377
+ const color2 = instanced.instanceColor ? new import_three19.Color().fromBufferAttribute(instanced.instanceColor, i) : void 0;
9378
+ addMesh(mesh, world.clone().multiply(matrix), color2);
9239
9379
  }
9240
9380
  } else {
9241
9381
  addMesh(mesh, world);
9242
9382
  }
9243
9383
  }
9244
- for (const child of object2.children) visit(child, world);
9384
+ for (const child of object3.children) visit(child, world);
9245
9385
  };
9246
9386
  try {
9247
9387
  const ancestors = [];
@@ -9317,13 +9457,13 @@ init_AssetKey();
9317
9457
 
9318
9458
  // src/gui/GuiText.ts
9319
9459
  var import_three20 = require("three");
9320
- async function layoutGuiText(text, options = {}) {
9460
+ async function layoutGuiText(text2, options = {}) {
9321
9461
  const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
9322
9462
  if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
9323
9463
  throw new Error("Text wrap width and line height must be positive");
9324
9464
  }
9325
9465
  const font = await Fonts.get(options.font);
9326
- const runs = typeof text === "string" ? [{ text }] : text;
9466
+ const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
9327
9467
  const glyphs = [], lineWidths = [];
9328
9468
  let line = [], width = 0;
9329
9469
  let trailingNewline = false;
@@ -9377,7 +9517,7 @@ function createGuiTextGeometry(layout, shadow = true) {
9377
9517
  batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
9378
9518
  batches.push(batch);
9379
9519
  }
9380
- const color = new import_three20.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
9520
+ const color2 = new import_three20.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
9381
9521
  const offset = isShadow ? 1 : 0;
9382
9522
  const top = glyph ? 7 - glyph.ascent : 0;
9383
9523
  const height = glyph ? glyph.height * glyph.scale : 8;
@@ -9405,7 +9545,7 @@ function createGuiTextGeometry(layout, shadow = true) {
9405
9545
  const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
9406
9546
  const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
9407
9547
  batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
9408
- for (let i = 0; i < 4; i++) batch.colors.push(color.r, color.g, color.b);
9548
+ for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
9409
9549
  batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
9410
9550
  };
9411
9551
  for (const boldOffset of style.bold ? [0, 1] : [0]) {
@@ -9439,12 +9579,19 @@ var GuiHelper = class {
9439
9579
  size: [width + 24, height + 24]
9440
9580
  }));
9441
9581
  let y = position[1];
9442
- lines.forEach((text, index) => {
9443
- layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y] });
9582
+ lines.forEach((text2, index) => {
9583
+ layers.push({ ...textOptions, name: `tooltip-line-${index}`, text: text2, position: [position[0], y] });
9444
9584
  y += layouts[index].height + (index === 0 ? titleGap : 0);
9445
9585
  });
9446
9586
  return layers;
9447
9587
  }
9588
+ /**
9589
+ * Calculates a slot's top-left position in GUI pixels.
9590
+ * @param slot - Zero-based slot index, or `[column, row]`.
9591
+ * @param origin - Position of the first slot.
9592
+ * @param offset - Horizontal and vertical spacing between slot origins, in pixels.
9593
+ * @param rowSize - Slots per row when `slot` is an index.
9594
+ */
9448
9595
  static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
9449
9596
  const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
9450
9597
  return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
@@ -9889,29 +10036,29 @@ var SceneInspector = class {
9889
10036
  this.objectControlsContainer.innerHTML = "";
9890
10037
  this.addControls(targetObject, targetIntersection, instance);
9891
10038
  }
9892
- addControls(object2, intersection, instance) {
10039
+ addControls(object3, intersection, instance) {
9893
10040
  const container = document.createElement("div");
9894
10041
  container.append(this.separator("Select Parent/Child"));
9895
- if (object2.parent) {
9896
- container.append(this.buttonControl("Select Parent " + object2.parent.constructor.name + " " + object2.parent.name, "P", () => {
9897
- this.selectObject(object2.parent, intersection, instance);
10042
+ if (object3.parent) {
10043
+ container.append(this.buttonControl("Select Parent " + object3.parent.constructor.name + " " + object3.parent.name, "P", () => {
10044
+ this.selectObject(object3.parent, intersection, instance);
9898
10045
  }));
9899
10046
  }
9900
- if (object2.children.length > 0) {
10047
+ if (object3.children.length > 0) {
9901
10048
  let i = 1;
9902
- for (let child of object2.children) {
10049
+ for (let child of object3.children) {
9903
10050
  container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
9904
10051
  this.selectObject(child, intersection, instance);
9905
10052
  }));
9906
10053
  }
9907
10054
  }
9908
10055
  container.append(this.separator());
9909
- const selectedInstance = instance && (object2 === instance.instanceable || object2 === intersection?.object) ? instance : void 0;
10056
+ const selectedInstance = instance && (object3 === instance.instanceable || object3 === intersection?.object) ? instance : void 0;
9910
10057
  if (!selectedInstance) {
9911
- container.append(this.toggleControl("Visibility", "V", object2.visible, (v) => object2.visible = v));
10058
+ container.append(this.toggleControl("Visibility", "V", object3.visible, (v) => object3.visible = v));
9912
10059
  }
9913
10060
  container.append(this.separator("Mesh"));
9914
- this.addObjectControls(object2, intersection, container, selectedInstance);
10061
+ this.addObjectControls(object3, intersection, container, selectedInstance);
9915
10062
  container.append(this.separator());
9916
10063
  this.objectControlsContainer.append(container);
9917
10064
  }
@@ -10102,6 +10249,7 @@ var SceneStatsDisplay = class {
10102
10249
  appendTo(el) {
10103
10250
  el.append(this.statsContainer);
10104
10251
  }
10252
+ /** Stops the refresh timer and removes the panel from the page. */
10105
10253
  dispose() {
10106
10254
  clearInterval(this.interval);
10107
10255
  this.interval = void 0;
@@ -10126,6 +10274,7 @@ var InstanceManager = class {
10126
10274
  this.isInstanceManager = true;
10127
10275
  this.instanceCache = {};
10128
10276
  }
10277
+ /** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
10129
10278
  async get(key) {
10130
10279
  if (key in this.instanceCache) {
10131
10280
  console.debug(p9, "key in cache", key);
@@ -10133,6 +10282,7 @@ var InstanceManager = class {
10133
10282
  }
10134
10283
  return void 0;
10135
10284
  }
10285
+ /** Allocates a placement, calling the supplier once when the shared model is first needed. */
10136
10286
  async getOrCreate(key, supplier) {
10137
10287
  if (key in this.instanceCache) {
10138
10288
  return await this.get(key);
@@ -10149,6 +10299,7 @@ var InstanceManager = class {
10149
10299
  throw error;
10150
10300
  }
10151
10301
  }
10302
+ /** Forgets cached models without disposing them or releasing their existing placements. */
10152
10303
  reset() {
10153
10304
  for (let instanceCacheKey in this.instanceCache) {
10154
10305
  delete this.instanceCache[instanceCacheKey];
@@ -10194,6 +10345,7 @@ function matchesCondition(condition, state) {
10194
10345
  });
10195
10346
  }
10196
10347
  var BlockStateResolver = class {
10348
+ /** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
10197
10349
  static async defaults(blockState) {
10198
10350
  const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
10199
10351
  const state = {};
@@ -10213,9 +10365,11 @@ var BlockStateResolver = class {
10213
10365
  }
10214
10366
  return state;
10215
10367
  }
10368
+ /** Selects matching models. Supply `choose` to control selection within each alternatives array. */
10216
10369
  static select(blockState, state, choose = this.choose) {
10217
10370
  return this.matching(blockState, state).map(choose);
10218
10371
  }
10372
+ /** Returns matching model groups before choosing weighted alternatives. */
10219
10373
  static matching(blockState, state) {
10220
10374
  const out = [];
10221
10375
  if (blockState.variants) {
@@ -10239,6 +10393,7 @@ var BlockStateResolver = class {
10239
10393
  }
10240
10394
  return out;
10241
10395
  }
10396
+ /** Selects a random alternative using positive integer weights, each defaulting to 1. */
10242
10397
  static choose(variants) {
10243
10398
  if (!Array.isArray(variants)) return variants;
10244
10399
  if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
@@ -10256,6 +10411,7 @@ var BlockStateResolver = class {
10256
10411
  }
10257
10412
  return variants[variants.length - 1];
10258
10413
  }
10414
+ /** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
10259
10415
  static rotation(variant) {
10260
10416
  const rotation = new import_three29.Euler();
10261
10417
  if (typeof variant.x !== "undefined") {
@@ -10318,29 +10474,29 @@ var BlockTints = class {
10318
10474
  }
10319
10475
  }
10320
10476
  if (!indices.size) return tints;
10321
- let color;
10477
+ let color2;
10322
10478
  switch (rule.source) {
10323
10479
  case "constant":
10324
- color = rule.color;
10480
+ color2 = rule.color;
10325
10481
  break;
10326
10482
  case "grass":
10327
- color = await Colormaps.grassColor(key);
10483
+ color2 = await Colormaps.grassColor(key);
10328
10484
  break;
10329
10485
  case "redstone": {
10330
10486
  const power = Number(state.power ?? 0) / 15;
10331
10487
  const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
10332
10488
  const green = Math.max(0, power * power * 0.7 - 0.5);
10333
10489
  const blue = Math.max(0, power * power * 0.6 - 0.7);
10334
- color = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
10490
+ color2 = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
10335
10491
  break;
10336
10492
  }
10337
10493
  case "stem": {
10338
10494
  const age = Number(state.age ?? 0);
10339
- color = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
10495
+ color2 = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
10340
10496
  break;
10341
10497
  }
10342
10498
  }
10343
- return { ...Object.fromEntries([...indices].map((index) => [index, color])), ...tints };
10499
+ return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
10344
10500
  }
10345
10501
  };
10346
10502
 
@@ -10482,8 +10638,8 @@ var BlockObject = class _BlockObject extends SceneObject {
10482
10638
  return false;
10483
10639
  }
10484
10640
  return this._models.some((model) => {
10485
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10486
- return object2.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
10641
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10642
+ return object3.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
10487
10643
  });
10488
10644
  }
10489
10645
  getModelMatrix(model) {
@@ -10492,8 +10648,8 @@ var BlockObject = class _BlockObject extends SceneObject {
10492
10648
  return model.matrix.clone();
10493
10649
  }
10494
10650
  getModelCullMask(model, worldMask) {
10495
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10496
- if (object2.options.displayPosition) return 0;
10651
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10652
+ if (object3.options.displayPosition) return 0;
10497
10653
  const position = new import_three32.Vector3();
10498
10654
  const rotation = new import_three32.Quaternion();
10499
10655
  const scale = new import_three32.Vector3();
@@ -10510,14 +10666,14 @@ var BlockObject = class _BlockObject extends SceneObject {
10510
10666
  replacements.push(model);
10511
10667
  continue;
10512
10668
  }
10513
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10669
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10514
10670
  const cullMask = this.getModelCullMask(model, worldMask);
10515
- if (cullMask === (object2.options.cullMask ?? 0)) {
10671
+ if (cullMask === (object3.options.cullMask ?? 0)) {
10516
10672
  replacements.push(model);
10517
10673
  continue;
10518
10674
  }
10519
10675
  const matrix = this.getModelMatrix(model);
10520
- const replacement = await this.scene.addModel(object2.originalModel, { ...object2.options, cullMask });
10676
+ const replacement = await this.scene.addModel(object3.originalModel, { ...object3.options, cullMask });
10521
10677
  replacements.push(replacement);
10522
10678
  if (isInstanceReference(replacement)) {
10523
10679
  replacement.setMatrix(matrix);
@@ -10525,8 +10681,8 @@ var BlockObject = class _BlockObject extends SceneObject {
10525
10681
  replacement.matrix.copy(matrix);
10526
10682
  replacement.matrixWorldNeedsUpdate = true;
10527
10683
  matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
10528
- replacement.matrixAutoUpdate = object2.matrixAutoUpdate;
10529
- replacement.visible = object2.visible;
10684
+ replacement.matrixAutoUpdate = object3.matrixAutoUpdate;
10685
+ replacement.visible = object3.visible;
10530
10686
  }
10531
10687
  }
10532
10688
  } catch (error) {
@@ -10542,6 +10698,7 @@ var BlockObject = class _BlockObject extends SceneObject {
10542
10698
  }
10543
10699
  this.notifyDirty();
10544
10700
  }
10701
+ /** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
10545
10702
  get state() {
10546
10703
  return this._state;
10547
10704
  }
@@ -10641,6 +10798,7 @@ var BlockObject = class _BlockObject extends SceneObject {
10641
10798
  obj.setPosition(this.position);
10642
10799
  return obj;
10643
10800
  }
10801
+ /** Clears selected properties and rebuilds models without reapplying property defaults. */
10644
10802
  async resetState() {
10645
10803
  this._previousState = this._state;
10646
10804
  this._state = {};
@@ -12540,6 +12698,11 @@ function compoundList(tags, name) {
12540
12698
  // src/model/multiblock/StructureParser.ts
12541
12699
  init_MineRenderError();
12542
12700
  var StructureParser = class {
12701
+ /**
12702
+ * Parses decoded NBT, omitting air blocks.
12703
+ * @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
12704
+ * @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
12705
+ */
12543
12706
  static async parse(_nbt, paletteIndex) {
12544
12707
  const nbt = _nbt.value;
12545
12708
  let palette;
@@ -13374,6 +13537,7 @@ var SkinImage = class {
13374
13537
  // src/skin/SkinTextures.ts
13375
13538
  init_Textures();
13376
13539
  var SkinTextures = class {
13540
+ /** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
13377
13541
  static async get(src, legacy) {
13378
13542
  const image = await SkinImage.getData(src);
13379
13543
  const scale = image.width / 64;
@@ -13547,6 +13711,14 @@ var SkinObject = class extends SceneObject {
13547
13711
  }
13548
13712
  }
13549
13713
  }
13714
+ /**
13715
+ * Loads a skin texture, detecting slim arms and legacy layouts by default.
13716
+ * To override detection, set `slim` or `legacy` in the options passed to
13717
+ * `new SkinObject(options)` or `scene.addSkin(src, options)`.
13718
+ * Use {@link setSlim} or {@link setLegacy} to change these settings later.
13719
+ *
13720
+ * @param src - URL of the skin texture.
13721
+ */
13550
13722
  async setSkinTexture(src) {
13551
13723
  if (typeof src === "undefined") return;
13552
13724
  this.skinTextureSrc = src;
@@ -13657,59 +13829,60 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
13657
13829
  this.DEFAULT_OPTIONS = (0, import_ts_deepmerge8.default)({}, {});
13658
13830
  }
13659
13831
  add(...objects) {
13660
- for (const object2 of objects) {
13832
+ for (const object3 of objects) {
13661
13833
  this.dirty = true;
13662
13834
  try {
13663
- super.add(object2);
13835
+ super.add(object3);
13664
13836
  } finally {
13665
- if (object2?.isObject3D && object2 !== this) {
13666
- this.updateObjectRegistration(object2, object2.parent === this);
13837
+ if (object3?.isObject3D && object3 !== this) {
13838
+ this.updateObjectRegistration(object3, object3.parent === this);
13667
13839
  }
13668
13840
  }
13669
13841
  }
13670
13842
  return this;
13671
13843
  }
13672
13844
  remove(...objects) {
13673
- for (const object2 of objects) {
13674
- if (!this.children.includes(object2)) continue;
13845
+ for (const object3 of objects) {
13846
+ if (!this.children.includes(object3)) continue;
13675
13847
  this.dirty = true;
13676
- this.updateObjectRegistration(object2, false);
13848
+ this.updateObjectRegistration(object3, false);
13677
13849
  try {
13678
- super.remove(object2);
13850
+ super.remove(object3);
13679
13851
  } finally {
13680
- this.updateObjectRegistration(object2, object2.parent === this);
13852
+ this.updateObjectRegistration(object3, object3.parent === this);
13681
13853
  }
13682
13854
  }
13683
13855
  return this;
13684
13856
  }
13685
- updateObjectRegistration(object2, attached) {
13686
- if (this.observedObjects.has(object2) === attached) return;
13687
- const observable = object2;
13857
+ updateObjectRegistration(object3, attached) {
13858
+ if (this.observedObjects.has(object3) === attached) return;
13859
+ const observable = object3;
13688
13860
  if (attached) {
13689
- this.observedObjects.add(object2);
13861
+ this.observedObjects.add(object3);
13690
13862
  observable.addEventListener("change", this.onObjectChange);
13691
13863
  } else {
13692
- this.observedObjects.delete(object2);
13864
+ this.observedObjects.delete(object3);
13693
13865
  observable.removeEventListener("change", this.onObjectChange);
13694
13866
  }
13695
13867
  const change = attached ? 1 : -1;
13696
13868
  this.stats.objectCount += change;
13697
- if (isSceneObject(object2)) {
13869
+ if (isSceneObject(object3)) {
13698
13870
  this.stats.sceneObjectCount += change;
13699
13871
  }
13700
13872
  }
13701
- async initAndAdd(...object2) {
13873
+ /** Initializes each object before attaching it to the scene. */
13874
+ async initAndAdd(...object3) {
13702
13875
  this.dirty = true;
13703
- for (let obj of object2) {
13876
+ for (let obj of object3) {
13704
13877
  await obj.init();
13705
13878
  }
13706
- return this.add(...object2);
13879
+ return this.add(...object3);
13707
13880
  }
13708
- async addSceneObject(asset, objectSupplier, _options, parent = this) {
13881
+ async addSceneObject(asset2, objectSupplier, _options, parent = this) {
13709
13882
  this.dirty = true;
13710
13883
  const obj = await objectSupplier();
13711
- if (obj?.options?.instanceMeshes && asset.key && asset.key?.assetType === "models") {
13712
- let key = asset.key.serialize();
13884
+ if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
13885
+ let key = asset2.key.serialize();
13713
13886
  if (isModelObject(obj)) {
13714
13887
  const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
13715
13888
  if (displayPosition) key += `|display:${displayPosition}`;
@@ -13733,13 +13906,33 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
13733
13906
  return obj;
13734
13907
  }
13735
13908
  }
13909
+ /**
13910
+ * Adds a model loaded with {@link Models.getMerged}.
13911
+ * Set `options.instanceMeshes` to `false` to create a separate ModelObject.
13912
+ *
13913
+ * @param parent - Object to attach the model to. Defaults to this scene.
13914
+ * @returns The model, or a reference to one placement of a shared model.
13915
+ */
13736
13916
  async addModel(model, options, parent = this) {
13737
13917
  options = { ...options, tints: await ItemTints.get(model, options?.tints) };
13738
13918
  return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
13739
13919
  }
13920
+ /**
13921
+ * Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
13922
+ * Set `options.initialState` to choose block properties such as `facing` or `axis`.
13923
+ * Use the returned object's `setPosition` method to move all of its model parts.
13924
+ */
13740
13925
  async addBlock(blockState, options, parent = this) {
13741
13926
  return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
13742
13927
  }
13928
+ /**
13929
+ * Loads a skin and adds an initialized player model.
13930
+ *
13931
+ * @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
13932
+ * Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
13933
+ * @param options - Player settings, including `slim` and `legacy` detection overrides.
13934
+ * @param parent - Object to attach the player to. Defaults to this scene.
13935
+ */
13743
13936
  async addSkin(skin, options, parent = this) {
13744
13937
  this.dirty = true;
13745
13938
  const obj = new SkinObject(options);
@@ -13751,9 +13944,14 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
13751
13944
  parent.add(obj);
13752
13945
  return obj;
13753
13946
  }
13947
+ /** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
13754
13948
  async addEntity(entity, options, parent = this) {
13755
13949
  return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
13756
13950
  }
13951
+ /**
13952
+ * Adds GUI layers in draw order. One GUI pixel equals one scene unit.
13953
+ * Layer positions start at the top left, with positive GUI y pointing down.
13954
+ */
13757
13955
  async addGui(layers, options, parent = this) {
13758
13956
  const obj = new GuiObject(layers, options);
13759
13957
  obj.scene = this;
@@ -13782,11 +13980,11 @@ var _changeEvent = { type: "change" };
13782
13980
  var _startEvent = { type: "start" };
13783
13981
  var _endEvent = { type: "end" };
13784
13982
  var OrbitControls = class extends import_three35.EventDispatcher {
13785
- constructor(object2, domElement) {
13983
+ constructor(object3, domElement) {
13786
13984
  super();
13787
13985
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
13788
13986
  if (domElement === document) console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
13789
- this.object = object2;
13987
+ this.object = object3;
13790
13988
  this.domElement = domElement;
13791
13989
  this.domElement.style.touchAction = "none";
13792
13990
  this.enabled = true;
@@ -13843,7 +14041,7 @@ var OrbitControls = class extends import_three35.EventDispatcher {
13843
14041
  };
13844
14042
  this.update = function() {
13845
14043
  const offset = new import_three35.Vector3();
13846
- const quat = new import_three35.Quaternion().setFromUnitVectors(object2.up, new import_three35.Vector3(0, 1, 0));
14044
+ const quat = new import_three35.Quaternion().setFromUnitVectors(object3.up, new import_three35.Vector3(0, 1, 0));
13847
14045
  const quatInverse = quat.clone().invert();
13848
14046
  const lastPosition = new import_three35.Vector3();
13849
14047
  const lastQuaternion = new import_three35.Quaternion();
@@ -14408,8 +14606,8 @@ var OrbitControls = class extends import_three35.EventDispatcher {
14408
14606
  }
14409
14607
  };
14410
14608
  var MapControls = class extends OrbitControls {
14411
- constructor(object2, domElement) {
14412
- super(object2, domElement);
14609
+ constructor(object3, domElement) {
14610
+ super(object3, domElement);
14413
14611
  this.screenSpacePanning = false;
14414
14612
  this.mouseButtons.LEFT = import_three35.MOUSE.PAN;
14415
14613
  this.mouseButtons.RIGHT = import_three35.MOUSE.ROTATE;
@@ -14608,6 +14806,7 @@ var Renderer = class _Renderer {
14608
14806
  get element() {
14609
14807
  return this._element;
14610
14808
  }
14809
+ /** Attaches the canvas to an element and sizes it to that element's dimensions. */
14611
14810
  appendTo(element) {
14612
14811
  if (this._disposed) return;
14613
14812
  this._element = element;
@@ -14635,6 +14834,7 @@ var Renderer = class _Renderer {
14635
14834
  events.add(changeEvent2);
14636
14835
  dispatcher.addEventListener(changeEvent2, this._changeListener);
14637
14836
  }
14837
+ /** Updates the camera and canvas size. Width and height are in CSS pixels. */
14638
14838
  resize(width, height) {
14639
14839
  if (this._disposed) return;
14640
14840
  if (isPerspectiveCamera(this.camera)) {
@@ -14673,12 +14873,14 @@ var Renderer = class _Renderer {
14673
14873
  if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
14674
14874
  };
14675
14875
  }
14876
+ /** Starts or resumes the animation loop and registered frame callbacks. */
14676
14877
  start() {
14677
14878
  if (this._disposed) return;
14678
14879
  this.stop();
14679
14880
  this._running = true;
14680
14881
  if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
14681
14882
  }
14883
+ /** Pauses rendering and frame callbacks. Call {@link start} to resume. */
14682
14884
  stop() {
14683
14885
  if (this._disposed) return;
14684
14886
  this._running = false;
@@ -14787,12 +14989,15 @@ var Renderer = class _Renderer {
14787
14989
  const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
14788
14990
  return canvas.toDataURL(mime, quality);
14789
14991
  }
14992
+ /** Exports visible scene meshes as OBJ text, without texture images. */
14790
14993
  toObj() {
14791
14994
  return SceneExporter.toObj(this.scene);
14792
14995
  }
14996
+ /** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
14793
14997
  toPLY(options) {
14794
14998
  return SceneExporter.toPLY(this.scene, options);
14795
14999
  }
15000
+ /** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
14796
15001
  toGLTF(options) {
14797
15002
  return SceneExporter.toGLTF(this.scene, options);
14798
15003
  }
@@ -14831,16 +15036,244 @@ init_SceneObject();
14831
15036
  // src/index.ts
14832
15037
  init_request();
14833
15038
 
15039
+ // src/scene/SceneDocument.ts
15040
+ init_DisplayPosition();
15041
+ function fail(path, message) {
15042
+ throw new Error(`${path}: ${message}`);
15043
+ }
15044
+ function object2(value, path) {
15045
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, "expected a JSON object");
15046
+ for (const key of Object.keys(value)) {
15047
+ if (["__proto__", "prototype", "constructor"].includes(key)) fail(`${path}.${key}`, "unsupported property");
15048
+ }
15049
+ return value;
15050
+ }
15051
+ function fields(value, path, allowed, required = []) {
15052
+ const input = object2(value, path);
15053
+ for (const key of required) if (input[key] === void 0) fail(`${path}.${key}`, "required");
15054
+ for (const [key, entry] of Object.entries(input)) {
15055
+ if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, "unsupported property");
15056
+ if (entry !== void 0) allowed[key](entry, `${path}.${key}`);
15057
+ }
15058
+ return input;
15059
+ }
15060
+ var text = (value, path) => {
15061
+ if (typeof value !== "string" || !value.trim()) fail(path, "expected a nonempty string");
15062
+ };
15063
+ var literalText = (value, path) => {
15064
+ if (typeof value !== "string") fail(path, "expected a string");
15065
+ };
15066
+ var boolean = (value, path) => {
15067
+ if (typeof value !== "boolean") fail(path, "expected a boolean");
15068
+ };
15069
+ var number = (value, path) => {
15070
+ if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number");
15071
+ };
15072
+ var positive = (value, path) => {
15073
+ number(value, path);
15074
+ if (value <= 0) fail(path, "expected a positive number");
15075
+ };
15076
+ var nonnegative = (value, path) => {
15077
+ number(value, path);
15078
+ if (value < 0) fail(path, "expected a nonnegative number");
15079
+ };
15080
+ var color = (value, path) => {
15081
+ number(value, path);
15082
+ if (!Number.isInteger(value) || value < 0 || value > 16777215) {
15083
+ fail(path, "expected an RGB integer from 0 to 16777215");
15084
+ }
15085
+ };
15086
+ var asset = (value, path) => {
15087
+ text(value, path);
15088
+ if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
15089
+ fail(path, "expected a resource ID such as minecraft:stone");
15090
+ }
15091
+ };
15092
+ var version = (value, path) => {
15093
+ text(value, path);
15094
+ if (!/^[a-zA-Z0-9._-]+$/.test(value)) fail(path, "expected a Minecraft version without slashes");
15095
+ };
15096
+ var tuple = (size, item = number) => (value, path) => {
15097
+ if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);
15098
+ for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);
15099
+ };
15100
+ var list = (item) => (value, path) => {
15101
+ if (!Array.isArray(value)) fail(path, "expected an array");
15102
+ for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);
15103
+ };
15104
+ var dictionary = (item, numericKeys = false) => (value, path) => {
15105
+ for (const [key, entry] of Object.entries(object2(value, path))) {
15106
+ if (numericKeys && !/^\d+$/.test(key)) fail(`${path}.${key}`, "expected a nonnegative tint index");
15107
+ item(entry, `${path}.${key}`);
15108
+ }
15109
+ };
15110
+ var enumeration = (values) => (value, path) => {
15111
+ if (!values.includes(value)) fail(path, `expected one of: ${values.join(", ")}`);
15112
+ };
15113
+ var textStyle = { color, bold: boolean, italic: boolean };
15114
+ function guiLayer(value, path) {
15115
+ const layer = object2(value, path);
15116
+ if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
15117
+ fail(path, "expected exactly one of texture, item, or text");
15118
+ }
15119
+ const layout = { name: text, position: tuple(2), size: tuple(2, positive) };
15120
+ if (layer.texture !== void 0) {
15121
+ fields(value, path, { ...layout, texture: asset, crop: (value2, path2) => {
15122
+ tuple(4)(value2, path2);
15123
+ const [x, y, width, height] = value2;
15124
+ nonnegative(x, `${path2}[0]`);
15125
+ nonnegative(y, `${path2}[1]`);
15126
+ positive(width, `${path2}[2]`);
15127
+ positive(height, `${path2}[3]`);
15128
+ } });
15129
+ } else if (layer.item !== void 0) {
15130
+ fields(value, path, { ...layout, item: asset, tints: dictionary(color, true) });
15131
+ } else {
15132
+ fields(value, path, {
15133
+ ...layout,
15134
+ ...textStyle,
15135
+ font: asset,
15136
+ shadow: boolean,
15137
+ maxWidth: positive,
15138
+ lineHeight: positive,
15139
+ text: (value2, path2) => {
15140
+ if (typeof value2 === "string") return;
15141
+ list((run, path3) => {
15142
+ fields(run, path3, { text: literalText, ...textStyle }, ["text"]);
15143
+ })(value2, path2);
15144
+ }
15145
+ });
15146
+ }
15147
+ }
15148
+ function validateObject(value, path) {
15149
+ const input = object2(value, path);
15150
+ const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };
15151
+ const required = ["id", "type"];
15152
+ const renderOptions = { wireframe: boolean };
15153
+ switch (input.type) {
15154
+ case "skin":
15155
+ fields(value, path, {
15156
+ ...common,
15157
+ skin: text,
15158
+ cape: (value2, path2) => {
15159
+ fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
15160
+ },
15161
+ hiddenParts: list(enumeration(SKIN_PARTS)),
15162
+ pose: (value2, path2) => {
15163
+ fields(value2, path2, {
15164
+ head: tuple(3),
15165
+ body: tuple(3),
15166
+ rightArm: tuple(3),
15167
+ leftArm: tuple(3),
15168
+ rightLeg: tuple(3),
15169
+ leftLeg: tuple(3),
15170
+ cape: tuple(3)
15171
+ });
15172
+ },
15173
+ options: (value2, path2) => {
15174
+ fields(value2, path2, { slim: boolean, legacy: boolean });
15175
+ }
15176
+ }, required);
15177
+ break;
15178
+ case "block":
15179
+ fields(value, path, {
15180
+ ...common,
15181
+ asset,
15182
+ state: dictionary(text),
15183
+ options: (value2, path2) => {
15184
+ fields(value2, path2, { ...renderOptions, tints: dictionary(color, true) });
15185
+ }
15186
+ }, [...required, "asset"]);
15187
+ break;
15188
+ case "item":
15189
+ case "model":
15190
+ fields(value, path, {
15191
+ ...common,
15192
+ asset,
15193
+ options: (value2, path2) => {
15194
+ fields(value2, path2, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) });
15195
+ }
15196
+ }, [...required, "asset"]);
15197
+ break;
15198
+ case "entity":
15199
+ fields(value, path, {
15200
+ ...common,
15201
+ asset,
15202
+ texture: asset,
15203
+ layers: list(text),
15204
+ when: list(text),
15205
+ textures: dictionary(asset),
15206
+ options: (value2, path2) => {
15207
+ fields(value2, path2, {
15208
+ ...renderOptions,
15209
+ flip: boolean,
15210
+ tints: dictionary((value3, path3) => {
15211
+ typeof value3 === "string" ? text(value3, path3) : color(value3, path3);
15212
+ })
15213
+ });
15214
+ },
15215
+ animation: (value2, path2) => {
15216
+ fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
15217
+ }
15218
+ }, [...required, "asset"]);
15219
+ if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
15220
+ break;
15221
+ case "gui":
15222
+ fields(value, path, { ...common, layers: list(guiLayer) }, [...required, "layers"]);
15223
+ break;
15224
+ default:
15225
+ fail(`${path}.type`, "expected skin, block, item, entity, model, or gui");
15226
+ }
15227
+ }
15228
+ function parseSceneDocument(value) {
15229
+ if (typeof value === "string") {
15230
+ try {
15231
+ value = JSON.parse(value);
15232
+ } catch {
15233
+ fail("scene", "invalid JSON");
15234
+ }
15235
+ }
15236
+ const input = fields(value, "scene", {
15237
+ format: enumeration(["minerender-scene"]),
15238
+ version: (value2, path) => {
15239
+ if (value2 !== 1) fail(path, "only scene document version 1 is supported");
15240
+ },
15241
+ minecraftVersion: version,
15242
+ camera: (value2, path) => {
15243
+ fields(value2, path, { position: tuple(3), target: tuple(3) }, ["position", "target"]);
15244
+ },
15245
+ objects: list(validateObject)
15246
+ }, ["format", "version", "objects"]);
15247
+ const ids = /* @__PURE__ */ new Set();
15248
+ for (const [index, entry] of input.objects.entries()) {
15249
+ if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID "${entry.id}"`);
15250
+ ids.add(entry.id);
15251
+ }
15252
+ return JSON.parse(JSON.stringify(input));
15253
+ }
15254
+
15255
+ // src/scene/SceneDocumentLoader.ts
15256
+ var import_three38 = require("three");
15257
+ init_AssetKey();
15258
+ init_AssetLoader();
15259
+ init_BlockStates();
15260
+ init_Entities();
15261
+ init_Models();
15262
+ init_DisplayPosition();
15263
+ init_SceneObject();
15264
+
14834
15265
  // src/skin/Skins.ts
14835
15266
  init_Requests();
14836
15267
  var Skins = class {
14837
15268
  //<editor-fold desc="Skins">
15269
+ /** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
14838
15270
  static async fromUuidOrUsername(uuidOrUsername) {
14839
15271
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14840
15272
  return this.fromUuid(uuidOrUsername);
14841
15273
  }
14842
15274
  return this.fromUsername(uuidOrUsername);
14843
15275
  }
15276
+ /** Builds a proxy URL for a UUID without downloading or checking the skin. */
14844
15277
  static async fromUuid(uuid) {
14845
15278
  return this.getMcProxySkinUrl(uuid);
14846
15279
  }
@@ -14852,6 +15285,7 @@ var Skins = class {
14852
15285
  return void 0;
14853
15286
  });
14854
15287
  }
15288
+ /** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
14855
15289
  static async fromMineSkin(mineskinId) {
14856
15290
  return this.getMineSkin(mineskinId).then((mineskin) => {
14857
15291
  if (mineskin) {
@@ -14862,12 +15296,14 @@ var Skins = class {
14862
15296
  }
14863
15297
  //</editor-fold>
14864
15298
  //<editor-fold desc="Capes">
15299
+ /** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
14865
15300
  static async capeFromUuidOrUsername(uuidOrUsername) {
14866
15301
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14867
15302
  return this.capeFromUuid(uuidOrUsername);
14868
15303
  }
14869
15304
  return this.capeFromUsername(uuidOrUsername);
14870
15305
  }
15306
+ /** Builds a proxy URL without checking whether the player has a cape. */
14871
15307
  static async capeFromUuid(uuid) {
14872
15308
  return this.getMcProxyCapeUrl(uuid);
14873
15309
  }
@@ -14996,6 +15432,195 @@ var Skins = class {
14996
15432
  //</editor-fold>
14997
15433
  };
14998
15434
 
15435
+ // src/scene/SceneDocumentLoader.ts
15436
+ var DocumentObjectScene = class extends MineRenderScene {
15437
+ async addSceneObject(_asset, supplier, _options, parent = this) {
15438
+ const object3 = await supplier();
15439
+ return this.initialize(object3, parent);
15440
+ }
15441
+ async initialize(object3, parent = this) {
15442
+ object3.scene = this;
15443
+ try {
15444
+ await object3.init();
15445
+ parent.add(object3);
15446
+ return object3;
15447
+ } catch (error) {
15448
+ object3.dispose();
15449
+ object3.removeFromParent();
15450
+ throw error;
15451
+ }
15452
+ }
15453
+ dispose() {
15454
+ for (const child of [...this.children]) {
15455
+ if (child.parent !== this) continue;
15456
+ if (isSceneObject(child)) child.dispose();
15457
+ child.removeFromParent();
15458
+ }
15459
+ }
15460
+ };
15461
+ async function skinTexture(value, cape = false) {
15462
+ if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;
15463
+ const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));
15464
+ if (!texture) throw new Error(`Could not find ${cape ? "cape" : "skin"} for "${value}"`);
15465
+ return texture;
15466
+ }
15467
+ var SceneDocumentLoader = class {
15468
+ static parse(value) {
15469
+ return parseSceneDocument(value);
15470
+ }
15471
+ /**
15472
+ * Loads objects concurrently and attaches them in document order after every load succeeds.
15473
+ * A failure waits for all loads, disposes staged objects, and reports the first error in document order.
15474
+ */
15475
+ static async load(scene, value, parent = scene) {
15476
+ const definition = this.parse(value);
15477
+ if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
15478
+ throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
15479
+ }
15480
+ const root = new import_three38.Group();
15481
+ root.name = "MineRender scene document";
15482
+ const objects = [];
15483
+ let disposed = false;
15484
+ const loaded = {
15485
+ root,
15486
+ objects,
15487
+ definition,
15488
+ advanceAnimations: (delta) => {
15489
+ if (!disposed) for (const object3 of objects) object3.advanceAnimation(delta);
15490
+ },
15491
+ dispose: () => {
15492
+ if (disposed) return;
15493
+ disposed = true;
15494
+ root.removeFromParent();
15495
+ for (const object3 of objects) object3.dispose();
15496
+ root.clear();
15497
+ scene.dirty = true;
15498
+ }
15499
+ };
15500
+ try {
15501
+ const results = await Promise.allSettled(definition.objects.map((object3) => this.loadValidatedObject(scene, object3)));
15502
+ for (const result of results) if (result.status === "fulfilled") objects.push(result.value);
15503
+ const failure = results.find((result) => result.status === "rejected");
15504
+ if (failure) throw failure.reason;
15505
+ for (const object3 of objects) root.add(object3.root);
15506
+ parent.add(root);
15507
+ scene.dirty = true;
15508
+ return loaded;
15509
+ } catch (error) {
15510
+ loaded.dispose();
15511
+ throw error;
15512
+ }
15513
+ }
15514
+ /** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */
15515
+ static async loadObject(scene, value, parent = scene) {
15516
+ const definition = this.parse({ format: "minerender-scene", version: 1, objects: [value] }).objects[0];
15517
+ return this.loadValidatedObject(scene, definition, parent);
15518
+ }
15519
+ static async loadValidatedObject(scene, definition, parent) {
15520
+ const root = new import_three38.Group();
15521
+ root.name = definition.name ?? definition.id;
15522
+ root.userData.sceneObjectId = definition.id;
15523
+ root.position.fromArray(definition.position ?? [0, 0, 0]);
15524
+ root.rotation.set(...definition.rotation ?? [0, 0, 0]);
15525
+ root.scale.fromArray(definition.scale ?? [1, 1, 1]);
15526
+ root.visible = definition.visible ?? true;
15527
+ const content = new DocumentObjectScene();
15528
+ root.add(content);
15529
+ let object3;
15530
+ try {
15531
+ switch (definition.type) {
15532
+ case "skin": {
15533
+ const skin = new SkinObject({ ...definition.options, instanceMeshes: false });
15534
+ object3 = skin;
15535
+ const texture = definition.skin ? await skinTexture(definition.skin) : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? "slim/alex" : "wide/steve"}.png`;
15536
+ await skin.setSkinTexture(texture);
15537
+ await content.initialize(skin);
15538
+ if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
15539
+ for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
15540
+ skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
15541
+ }
15542
+ for (const part of definition.hiddenParts ?? []) {
15543
+ const mesh = skin.getMeshByName(part);
15544
+ if (mesh) mesh.visible = false;
15545
+ }
15546
+ break;
15547
+ }
15548
+ case "block": {
15549
+ const state = await BlockStates.get(AssetKey.parse("blockstates", definition.asset));
15550
+ if (!state) throw new Error(`Could not load block ${definition.asset}`);
15551
+ object3 = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false });
15552
+ break;
15553
+ }
15554
+ case "item":
15555
+ case "model": {
15556
+ let key = AssetKey.parse("models", definition.asset);
15557
+ if (definition.type === "item" && key.type !== "item") {
15558
+ key = new AssetKey(key.namespace, key.getFullPath(), "models", "item");
15559
+ }
15560
+ const model = await Models.getMerged(key);
15561
+ if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);
15562
+ object3 = await content.addModel(model, {
15563
+ ...definition.type === "item" ? { displayPosition: "gui" /* GUI */ } : {},
15564
+ ...definition.options,
15565
+ instanceMeshes: false
15566
+ });
15567
+ break;
15568
+ }
15569
+ case "entity": {
15570
+ const key = AssetKey.parse("entity-models", definition.asset);
15571
+ const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse("textures", definition.texture) : void 0, {
15572
+ layers: definition.layers,
15573
+ when: definition.when,
15574
+ textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse("textures", texture)]))
15575
+ });
15576
+ if (!model) throw new Error(`Could not load entity ${definition.asset}`);
15577
+ const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
15578
+ object3 = entity;
15579
+ if (definition.animation) {
15580
+ const animations = await Entities.getAnimations(key);
15581
+ const clip = animations?.[definition.animation.name];
15582
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
15583
+ entity.playAnimation(clip, definition.animation);
15584
+ }
15585
+ break;
15586
+ }
15587
+ case "gui":
15588
+ object3 = await content.initialize(new GuiObject(definition.layers));
15589
+ break;
15590
+ }
15591
+ let disposed = false;
15592
+ const loaded = {
15593
+ root,
15594
+ object: object3,
15595
+ definition,
15596
+ advanceAnimation: (delta) => {
15597
+ if (!disposed && definition.type === "entity" && !definition.animation?.paused) object3.advanceAnimation(delta);
15598
+ },
15599
+ dispose: () => {
15600
+ if (disposed) return;
15601
+ disposed = true;
15602
+ root.removeFromParent();
15603
+ object3.dispose();
15604
+ object3.removeFromParent();
15605
+ content.dispose();
15606
+ root.clear();
15607
+ scene.dirty = true;
15608
+ }
15609
+ };
15610
+ parent?.add(root);
15611
+ scene.dirty = true;
15612
+ return loaded;
15613
+ } catch (error) {
15614
+ object3?.dispose();
15615
+ object3?.removeFromParent();
15616
+ content.dispose();
15617
+ root.removeFromParent();
15618
+ root.clear();
15619
+ throw new Error(`Scene object "${definition.name ?? definition.id}": ${error instanceof Error ? error.message : String(error)}`);
15620
+ }
15621
+ }
15622
+ };
15623
+
14999
15624
  // src/texture/index.ts
15000
15625
  init_Colormaps();
15001
15626
  init_TextureAtlas();
@@ -15022,16 +15647,19 @@ var ChunkData = class _ChunkData {
15022
15647
  this.freeIds = [];
15023
15648
  this.nbt = /* @__PURE__ */ new Map();
15024
15649
  }
15650
+ /** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
15025
15651
  get(index) {
15026
15652
  _ChunkData.validateIndex(index);
15027
15653
  const entry = this.palette[this.indices[index]];
15028
15654
  return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
15029
15655
  }
15656
+ /** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
15030
15657
  snapshot(index) {
15031
15658
  _ChunkData.validateIndex(index);
15032
15659
  const entry = this.palette[this.indices[index]];
15033
15660
  return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
15034
15661
  }
15662
+ /** Copies block data into a cell. An omitted block or an air block clears the cell. */
15035
15663
  set(index, block) {
15036
15664
  _ChunkData.validateIndex(index);
15037
15665
  let state;
@@ -15096,15 +15724,23 @@ var ChunkData = class _ChunkData {
15096
15724
 
15097
15725
  // src/world/AnvilParser.ts
15098
15726
  var AnvilParser = class {
15727
+ /** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
15099
15728
  static getChunkList(data) {
15100
15729
  return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
15101
15730
  }
15731
+ /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
15102
15732
  static async parse(data) {
15103
15733
  const bytes = this.bytes(data);
15104
15734
  const chunks = [];
15105
15735
  for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
15106
15736
  return { chunks };
15107
15737
  }
15738
+ /**
15739
+ * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
15740
+ * @param localX - Chunk x within the region, from 0 to 31.
15741
+ * @param localZ - Chunk z within the region, from 0 to 31.
15742
+ * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
15743
+ */
15108
15744
  static async parseChunk(data, localX, localZ) {
15109
15745
  if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
15110
15746
  throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
@@ -15266,12 +15902,12 @@ var AnvilParser = class {
15266
15902
  };
15267
15903
 
15268
15904
  // src/world/Chunk.ts
15269
- var import_three41 = require("three");
15905
+ var import_three42 = require("three");
15270
15906
  init_BlockStates();
15271
15907
  init_AssetKey();
15272
15908
 
15273
15909
  // src/world/MineRenderWorld.ts
15274
- var import_three39 = require("three");
15910
+ var import_three40 = require("three");
15275
15911
  init_Model();
15276
15912
  init_BatchedExecutor();
15277
15913
  init_AssetKey();
@@ -15279,7 +15915,7 @@ init_BlockStates();
15279
15915
  init_CubeFace();
15280
15916
 
15281
15917
  // src/world/SectionModels.ts
15282
- var import_three38 = require("three");
15918
+ var import_three39 = require("three");
15283
15919
  init_AssetKey();
15284
15920
  init_Models();
15285
15921
  init_CubeFace();
@@ -15309,6 +15945,7 @@ var SectionModels = class {
15309
15945
  this.templates = /* @__PURE__ */ new Set();
15310
15946
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
15311
15947
  }
15948
+ /** Returns a cube template, or `undefined` when the block requires an individual render object. */
15312
15949
  async get(blockState, properties = {}) {
15313
15950
  if (!blockState.variants || blockState.multipart) return void 0;
15314
15951
  let states = this.states.get(blockState);
@@ -15337,20 +15974,20 @@ var SectionModels = class {
15337
15974
  if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
15338
15975
  const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
15339
15976
  const template = await cached(models, key, async () => {
15340
- const object2 = new SectionModel(model, {
15977
+ const object3 = new SectionModel(model, {
15341
15978
  instanceMeshes: false,
15342
15979
  mergeMeshes: false,
15343
15980
  tints,
15344
15981
  uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
15345
15982
  });
15346
- await object2.init();
15347
- const geometry = object2.children[0].geometry;
15348
- geometry.applyMatrix4(new import_three38.Matrix4().makeRotationFromEuler(rotation));
15349
- object2.clear();
15983
+ await object3.init();
15984
+ const geometry = object3.children[0].geometry;
15985
+ geometry.applyMatrix4(new import_three39.Matrix4().makeRotationFromEuler(rotation));
15986
+ object3.clear();
15350
15987
  const cullFaces = CUBE_FACES.map((face) => {
15351
15988
  const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
15352
15989
  if (local < 0) return 0;
15353
- const normal = new import_three38.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
15990
+ const normal = new import_three39.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
15354
15991
  const world = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
15355
15992
  return 1 << world;
15356
15993
  });
@@ -15362,6 +15999,7 @@ var SectionModels = class {
15362
15999
  }
15363
16000
  return { variants, templates };
15364
16001
  }
16002
+ /** Disposes prepared geometry and clears this world's template caches. */
15365
16003
  clear() {
15366
16004
  for (const template of this.templates) template.geometry.dispose();
15367
16005
  this.templates.clear();
@@ -15380,20 +16018,20 @@ var MineRenderWorld = class {
15380
16018
  }
15381
16019
  getBlockAt(posOrX, y, z) {
15382
16020
  if (typeof posOrX == "number") {
15383
- return this.getBlockAt(new import_three39.Vector3(posOrX, y, z));
16021
+ return this.getBlockAt(new import_three40.Vector3(posOrX, y, z));
15384
16022
  }
15385
16023
  if (isTripleArray(posOrX)) {
15386
- return this.getBlockAt(new import_three39.Vector3(posOrX[0], posOrX[1], posOrX[2]));
16024
+ return this.getBlockAt(new import_three40.Vector3(posOrX[0], posOrX[1], posOrX[2]));
15387
16025
  }
15388
16026
  this.validatePosBounds(posOrX);
15389
16027
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
15390
16028
  }
15391
16029
  async setBlockAt(posOrX, yOrBlock, z, block) {
15392
16030
  if (typeof posOrX == "number") {
15393
- return this.setBlockAt(new import_three39.Vector3(posOrX, yOrBlock, z), block);
16031
+ return this.setBlockAt(new import_three40.Vector3(posOrX, yOrBlock, z), block);
15394
16032
  }
15395
16033
  if (isTripleArray(posOrX)) {
15396
- return this.setBlockAt(new import_three39.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
16034
+ return this.setBlockAt(new import_three40.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
15397
16035
  }
15398
16036
  return this.placeBlock(posOrX, yOrBlock);
15399
16037
  }
@@ -15404,14 +16042,21 @@ var MineRenderWorld = class {
15404
16042
  }
15405
16043
  async setBlockVisibleAt(posOrX, yOrVisible, z, visible) {
15406
16044
  if (typeof posOrX === "number") {
15407
- return this.setBlockVisibleAt(new import_three39.Vector3(posOrX, yOrVisible, z), visible);
16045
+ return this.setBlockVisibleAt(new import_three40.Vector3(posOrX, yOrVisible, z), visible);
15408
16046
  }
15409
16047
  if (isTripleArray(posOrX)) {
15410
- return this.setBlockVisibleAt(new import_three39.Vector3(...posOrX), yOrVisible);
16048
+ return this.setBlockVisibleAt(new import_three40.Vector3(...posOrX), yOrVisible);
15411
16049
  }
15412
16050
  this.validatePosBounds(posOrX);
15413
16051
  await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
15414
16052
  }
16053
+ /**
16054
+ * Places a structure's blocks at their stored positions, then refreshes neighboring faces.
16055
+ * Entity NBT is retained by the structure but is not rendered.
16056
+ *
16057
+ * @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
16058
+ * @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
16059
+ */
15415
16060
  async placeMultiBlock(multiblock, useBatches = true, executor) {
15416
16061
  const changes = /* @__PURE__ */ new Map();
15417
16062
  try {
@@ -15424,7 +16069,7 @@ var MineRenderWorld = class {
15424
16069
  const collect = async (positions2) => {
15425
16070
  for (const pos of positions2) changes.set(pos.toArray().join(","), pos);
15426
16071
  };
15427
- const place = (block) => this.placeBlock(new import_three39.Vector3(...block.position), block, collect);
16072
+ const place = (block) => this.placeBlock(new import_three40.Vector3(...block.position), block, collect);
15428
16073
  const keys = /* @__PURE__ */ new Map();
15429
16074
  for (const block of multiblock.blocks) {
15430
16075
  if (Chunk.isAir(block)) continue;
@@ -15508,6 +16153,7 @@ var MineRenderWorld = class {
15508
16153
  });
15509
16154
  }
15510
16155
  }
16156
+ /** Removes all chunks and releases their block placements and section meshes. */
15511
16157
  async clear() {
15512
16158
  await this.culling;
15513
16159
  const chunks = [...this._chunks.values()];
@@ -15521,13 +16167,13 @@ var MineRenderWorld = class {
15521
16167
  for (const pos of positions) {
15522
16168
  this.pendingCulling.set(pos.toArray().join(","), pos.clone());
15523
16169
  for (const offset of CUBE_FACE_OFFSETS) {
15524
- const neighbor = pos.clone().add(new import_three39.Vector3(...offset));
16170
+ const neighbor = pos.clone().add(new import_three40.Vector3(...offset));
15525
16171
  this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
15526
16172
  }
15527
16173
  for (let y = -1; y <= 1; y++) {
15528
16174
  for (let z = -1; z <= 1; z++) {
15529
16175
  for (let x = -1; x <= 1; x++) {
15530
- const neighbor = pos.clone().add(new import_three39.Vector3(x, y, z));
16176
+ const neighbor = pos.clone().add(new import_three40.Vector3(x, y, z));
15531
16177
  if (this.getBlockAt(neighbor)?.object?.fluidKind) {
15532
16178
  this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
15533
16179
  }
@@ -15551,19 +16197,19 @@ var MineRenderWorld = class {
15551
16197
  }
15552
16198
  if (block.object?.fluidKind) {
15553
16199
  await block.object.updateFluid((x, y, z) => {
15554
- const neighbor = pos.clone().add(new import_three39.Vector3(x, y, z));
16200
+ const neighbor = pos.clone().add(new import_three40.Vector3(x, y, z));
15555
16201
  const section = this.getChunkAt(neighbor);
15556
- const object2 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
16202
+ const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
15557
16203
  return {
15558
- fluid: object2?.fluidKind,
15559
- level: object2?.fluidLevel,
16204
+ fluid: object3?.fluidKind,
16205
+ level: object3?.fluidLevel,
15560
16206
  solid: section?.isOccludingAt(neighbor) ?? false
15561
16207
  };
15562
16208
  });
15563
16209
  }
15564
16210
  let mask = 0;
15565
16211
  for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
15566
- const neighbor = pos.clone().add(new import_three39.Vector3(...offset));
16212
+ const neighbor = pos.clone().add(new import_three40.Vector3(...offset));
15567
16213
  if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
15568
16214
  mask |= 1 << face;
15569
16215
  }
@@ -15593,18 +16239,21 @@ var MineRenderWorld = class {
15593
16239
  }
15594
16240
  return chunk;
15595
16241
  }
16242
+ /** Returns the loaded chunk containing a world block position, without creating a chunk. */
15596
16243
  getChunkAt(pos) {
15597
16244
  return this._chunks.get(this.worldPosToChunkKey(pos));
15598
16245
  }
16246
+ /** Converts block coordinates to scene units by multiplying each component by 16. */
15599
16247
  static worldToScenePosition(pos) {
15600
- return new import_three39.Vector3(
16248
+ return new import_three40.Vector3(
15601
16249
  pos.x * 16,
15602
16250
  pos.y * 16,
15603
16251
  pos.z * 16
15604
16252
  );
15605
16253
  }
16254
+ /** Converts scene units to block coordinates without rounding to an integer block. */
15606
16255
  static sceneToWorldPosition(pos) {
15607
- return new import_three39.Vector3(
16256
+ return new import_three40.Vector3(
15608
16257
  pos.x / 16,
15609
16258
  pos.y / 16,
15610
16259
  pos.z / 16
@@ -15627,14 +16276,15 @@ var MineRenderWorld = class {
15627
16276
  init_Model();
15628
16277
 
15629
16278
  // src/world/SectionMesh.ts
15630
- var import_three40 = require("three");
16279
+ var import_three41 = require("three");
15631
16280
  init_CanvasCompat();
15632
16281
  init_Materials();
15633
- var SectionMesh = class _SectionMesh extends import_three40.Group {
16282
+ var SectionMesh = class _SectionMesh extends import_three41.Group {
15634
16283
  constructor() {
15635
16284
  super(...arguments);
15636
16285
  this.ownedMeshes = [];
15637
16286
  }
16287
+ /** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
15638
16288
  static build(entries, maxAtlasSize = 2048) {
15639
16289
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
15640
16290
  const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
@@ -15682,7 +16332,7 @@ var SectionMesh = class _SectionMesh extends import_three40.Group {
15682
16332
  const normal = template.geometry.getAttribute("normal");
15683
16333
  const uv = template.geometry.getAttribute("uv");
15684
16334
  const bounds = template.geometry.getAttribute("uvBounds");
15685
- const color = template.geometry.getAttribute("color");
16335
+ const color2 = template.geometry.getAttribute("color");
15686
16336
  const sourceIndices = template.geometry.getIndex();
15687
16337
  const placement = locations.get(template.atlas).placement;
15688
16338
  const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
@@ -15704,24 +16354,24 @@ var SectionMesh = class _SectionMesh extends import_three40.Group {
15704
16354
  mapU(bounds?.getZ(vertex) ?? 1),
15705
16355
  mapV(bounds?.getW(vertex) ?? 1)
15706
16356
  );
15707
- colors.push(color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1);
16357
+ colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
15708
16358
  }
15709
16359
  for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
15710
16360
  }
15711
16361
  }
15712
- const geometry = new import_three40.BufferGeometry();
15713
- geometry.setAttribute("position", new import_three40.Float32BufferAttribute(positions, 3));
15714
- geometry.setAttribute("normal", new import_three40.Float32BufferAttribute(normals, 3));
15715
- geometry.setAttribute("uv", new import_three40.Float32BufferAttribute(uvs, 2));
15716
- geometry.setAttribute("uvBounds", new import_three40.Float32BufferAttribute(uvBounds, 4));
15717
- geometry.setAttribute("color", new import_three40.Float32BufferAttribute(colors, 3));
16362
+ const geometry = new import_three41.BufferGeometry();
16363
+ geometry.setAttribute("position", new import_three41.Float32BufferAttribute(positions, 3));
16364
+ geometry.setAttribute("normal", new import_three41.Float32BufferAttribute(normals, 3));
16365
+ geometry.setAttribute("uv", new import_three41.Float32BufferAttribute(uvs, 2));
16366
+ geometry.setAttribute("uvBounds", new import_three41.Float32BufferAttribute(uvBounds, 4));
16367
+ geometry.setAttribute("color", new import_three41.Float32BufferAttribute(colors, 3));
15718
16368
  geometry.setIndex(indices);
15719
16369
  geometry.computeBoundingBox();
15720
16370
  geometry.computeBoundingSphere();
15721
16371
  const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
15722
16372
  material.vertexColors = true;
15723
16373
  const texture = material.uniforms?.map?.value ?? material.map;
15724
- const mesh = new import_three40.Mesh(geometry, material);
16374
+ const mesh = new import_three41.Mesh(geometry, material);
15725
16375
  section.add(mesh);
15726
16376
  section.ownedMeshes.push({ mesh, texture });
15727
16377
  }
@@ -15757,10 +16407,10 @@ var Chunk = class _Chunk {
15757
16407
  }
15758
16408
  getBlockAt(posOrX, y, z) {
15759
16409
  if (typeof posOrX == "number") {
15760
- return this.getBlockAt(new import_three41.Vector3(posOrX, y, z));
16410
+ return this.getBlockAt(new import_three42.Vector3(posOrX, y, z));
15761
16411
  }
15762
16412
  if (isTripleArray(posOrX)) {
15763
- return this.getBlockAt(new import_three41.Vector3(posOrX[0], posOrX[1], posOrX[2]));
16413
+ return this.getBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]));
15764
16414
  }
15765
16415
  const pos = this.worldPosToChunkPos(posOrX);
15766
16416
  const index = _Chunk.chunkPosToBlockIndex(pos);
@@ -15768,10 +16418,10 @@ var Chunk = class _Chunk {
15768
16418
  }
15769
16419
  async setBlockAt(posOrX, yOrBlock, z, block) {
15770
16420
  if (typeof posOrX == "number") {
15771
- return this.setBlockAt(new import_three41.Vector3(posOrX, yOrBlock, z), block);
16421
+ return this.setBlockAt(new import_three42.Vector3(posOrX, yOrBlock, z), block);
15772
16422
  }
15773
16423
  if (isTripleArray(posOrX)) {
15774
- return this.setBlockAt(new import_three41.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
16424
+ return this.setBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
15775
16425
  }
15776
16426
  const worldPos = posOrX;
15777
16427
  const pos = this.worldPosToChunkPos(worldPos);
@@ -15779,7 +16429,7 @@ var Chunk = class _Chunk {
15779
16429
  return this.setBlockInChunkAt(pos, block, worldPos);
15780
16430
  }
15781
16431
  /**
15782
- * Set a block at integer chunk-local coordinates from 0 to 15.
16432
+ * Places a block at integer chunk-local coordinates from 0 to 15.
15783
16433
  */
15784
16434
  async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
15785
16435
  if (typeof worldPos === "undefined") {
@@ -15792,7 +16442,7 @@ var Chunk = class _Chunk {
15792
16442
  this.renderedBlocks.get(index)?.object?.removeFromScene();
15793
16443
  if (this.sectionBlocks.delete(index)) this.meshDirty = true;
15794
16444
  this.renderedBlocks.delete(index);
15795
- let object2;
16445
+ let object3;
15796
16446
  try {
15797
16447
  if (!readBlock) return void 0;
15798
16448
  const stored = readBlock();
@@ -15807,27 +16457,28 @@ var Chunk = class _Chunk {
15807
16457
  this.sectionBlocks.set(index, { index, template, cullMask: 0 });
15808
16458
  this.meshDirty = true;
15809
16459
  } else {
15810
- object2 = await this.scene.addBlock(blockState, {
16460
+ object3 = await this.scene.addBlock(blockState, {
15811
16461
  mergeMeshes: true,
15812
16462
  instanceMeshes: true,
15813
16463
  maxInstanceCount: 2e3,
15814
16464
  initialState: stored.properties
15815
16465
  });
15816
- object2.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
16466
+ object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
15817
16467
  }
15818
16468
  const info = { get block() {
15819
16469
  return readBlock();
15820
- }, object: object2 };
16470
+ }, object: object3 };
15821
16471
  this.renderedBlocks.set(index, info);
15822
16472
  return info;
15823
16473
  } catch (error) {
15824
16474
  this.data.set(index, void 0);
15825
- object2?.removeFromScene();
16475
+ object3?.removeFromScene();
15826
16476
  throw error;
15827
16477
  } finally {
15828
16478
  await onBlocksChanged?.([worldPos]);
15829
16479
  }
15830
16480
  }
16481
+ /** Changes visibility at world block coordinates while preserving the block's data. */
15831
16482
  async setBlockVisibleAt(pos, visible) {
15832
16483
  const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
15833
16484
  const block = this.renderedBlocks.get(index);
@@ -15881,9 +16532,10 @@ var Chunk = class _Chunk {
15881
16532
  this.scene.dirty = true;
15882
16533
  this.meshDirty = false;
15883
16534
  }
16535
+ /** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
15884
16536
  async clear(onBlocksChanged = this.onBlocksChanged) {
15885
16537
  const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
15886
- new import_three41.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
16538
+ new import_three42.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
15887
16539
  ));
15888
16540
  for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
15889
16541
  this.sectionBlocks.clear();
@@ -15901,6 +16553,7 @@ var Chunk = class _Chunk {
15901
16553
  static isAir(block) {
15902
16554
  return ChunkData.isAir(block);
15903
16555
  }
16556
+ /** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
15904
16557
  static chunkPosToBlockIndex(pos) {
15905
16558
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
15906
16559
  throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
@@ -15908,14 +16561,14 @@ var Chunk = class _Chunk {
15908
16561
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
15909
16562
  }
15910
16563
  worldPosToChunkPos(pos) {
15911
- return new import_three41.Vector3(
16564
+ return new import_three42.Vector3(
15912
16565
  pos.x - this.x * 16,
15913
16566
  pos.y - this.y * 16,
15914
16567
  pos.z - this.z * 16
15915
16568
  );
15916
16569
  }
15917
16570
  chunkPosToWorldPos(pos) {
15918
- return new import_three41.Vector3(
16571
+ return new import_three42.Vector3(
15919
16572
  this.x * 16 + pos.x,
15920
16573
  this.y * 16 + pos.y,
15921
16574
  this.z * 16 + pos.z
@@ -16003,6 +16656,7 @@ var Chunk = class _Chunk {
16003
16656
  RequestError,
16004
16657
  Requests,
16005
16658
  SKIN_PARTS,
16659
+ SceneDocumentLoader,
16006
16660
  SceneExporter,
16007
16661
  SceneInspector,
16008
16662
  SceneObject,
@@ -16078,6 +16732,7 @@ var Chunk = class _Chunk {
16078
16732
  mergeBufferAttributes,
16079
16733
  mergeBufferGeometries,
16080
16734
  mergeVertices,
16735
+ parseSceneDocument,
16081
16736
  prefix,
16082
16737
  registerNodeEnv,
16083
16738
  sampleEntityAnimation,