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.
@@ -68,6 +68,7 @@ var init_PersistentCache = __esm({
68
68
  /** Bump to invalidate every persisted entry. */
69
69
  this.VERSION = 2;
70
70
  }
71
+ /** Opens a named store in the active environment using the current cache schema version. */
71
72
  static open(name) {
72
73
  return Env.provider.openCache(name, _PersistentCache.VERSION);
73
74
  }
@@ -329,6 +330,7 @@ var init_Caching = __esm({
329
330
  this.fontCache
330
331
  ];
331
332
  }
333
+ /** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
332
334
  static clear() {
333
335
  for (const cache of this.all) {
334
336
  cache.invalidateAll();
@@ -371,6 +373,7 @@ var init_Geometries = __esm({
371
373
  }
372
374
  return geometry;
373
375
  }
376
+ /** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
374
377
  static getBox(key) {
375
378
  const keyStr = serializeBoxGeometryKey(key);
376
379
  return Caching.boxGeometryCache.get(keyStr, (k) => {
@@ -554,12 +557,15 @@ var init_Requests = __esm({
554
557
  static {
555
558
  this.mcAssetRequestQueue = new RequestQueue();
556
559
  }
560
+ /** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
557
561
  static genericRequest(request) {
558
562
  return this.genericQueue.request(request);
559
563
  }
564
+ /** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
560
565
  static mcAssetRequest(request) {
561
566
  return this.mcAssetRequestQueue.request(request);
562
567
  }
568
+ /** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
563
569
  static setMcAssetRoot(root) {
564
570
  this.mcAssetRequestQueue.baseURL = root;
565
571
  }
@@ -612,6 +618,7 @@ var init_WrappedImage = __esm({
612
618
  }
613
619
  return false;
614
620
  }
621
+ /** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
615
622
  get animated() {
616
623
  return this.height > this.width && this.height % this.width === 0;
617
624
  }
@@ -658,6 +665,7 @@ var init_ImageLoader = __esm({
658
665
  static _createImage() {
659
666
  return createImage();
660
667
  }
668
+ /** Resolves when an image URL has decoded, or rejects if decoding fails. */
661
669
  static async loadAsync(src) {
662
670
  return new Promise((resolve, reject) => {
663
671
  const image = this._createImage();
@@ -666,6 +674,7 @@ var init_ImageLoader = __esm({
666
674
  image.src = src;
667
675
  });
668
676
  }
677
+ /** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
669
678
  static loadElement(src, onload, onerr) {
670
679
  const image = this._createImage();
671
680
  if (onload)
@@ -693,6 +702,7 @@ var init_ImageLoader = __esm({
693
702
  throw err;
694
703
  }
695
704
  }
705
+ /** Decodes the supplied bytes into a canvas without fetching the source URL again. */
696
706
  static async infoToCanvasData(info) {
697
707
  const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
698
708
  const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
@@ -709,12 +719,14 @@ var init_ImageLoader = __esm({
709
719
  const image = await this.infoToCanvasData(info);
710
720
  return image.data.getImageData(0, 0, image.width, image.height);
711
721
  }
722
+ /** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
712
723
  static async getData(src) {
713
724
  const keyStr = serializeImageKey({ src });
714
725
  return await Caching.imageDataCache.get(keyStr, (k) => {
715
726
  return _ImageLoader.loadData(src);
716
727
  });
717
728
  }
729
+ /** Returns a cached canvas context and dimensions for an image URL or data URL. */
718
730
  static async getCanvasData(src) {
719
731
  const keyStr = serializeImageKey({ src });
720
732
  return await Caching.canvasImageDataCache.get(keyStr, (k) => {
@@ -814,6 +826,7 @@ var init_TextureLoader = __esm({
814
826
  texture.rotation = rotation;
815
827
  return Textures.initTextureProps(texture);
816
828
  }
829
+ /** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
817
830
  static load(src, format2 = RGBAFormat, rotation = 0) {
818
831
  const texture = new Texture();
819
832
  texture.colorSpace = THREE.SRGBColorSpace;
@@ -850,8 +863,8 @@ var init_Textures = __esm({
850
863
  for (let y = 0; y < size; y++) {
851
864
  for (let x = 0; x < size; x++) {
852
865
  const offset = (y * size + x) * 4;
853
- const color = (x + y) % 2 === 0 ? 255 : 0;
854
- data[offset] = data[offset + 2] = color;
866
+ const color2 = (x + y) % 2 === 0 ? 255 : 0;
867
+ data[offset] = data[offset + 2] = color2;
855
868
  data[offset + 3] = 255;
856
869
  }
857
870
  }
@@ -862,6 +875,7 @@ var init_Textures = __esm({
862
875
  return this.initTextureProps(texture);
863
876
  });
864
877
  }
878
+ /** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
865
879
  static initTextureProps(texture) {
866
880
  texture.magFilter = THREE2.NearestFilter;
867
881
  texture.minFilter = THREE2.NearestFilter;
@@ -874,11 +888,13 @@ var init_Textures = __esm({
874
888
  static createAsset(key) {
875
889
  return new Texture2();
876
890
  }
891
+ /** Creates an sRGB color texture from a canvas. */
877
892
  static createCanvasTexture(canvas) {
878
893
  const texture = new CanvasTexture(canvas);
879
894
  texture.colorSpace = THREE2.SRGBColorSpace;
880
895
  return this.initTextureProps(texture);
881
896
  }
897
+ /** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
882
898
  static getImage(key) {
883
899
  const keyStr = serializeTextureKey(key);
884
900
  const cached2 = Caching.textureCache.peek(keyStr);
@@ -903,6 +919,7 @@ var init_Materials = __esm({
903
919
  init_TextureLoader();
904
920
  init_Caching();
905
921
  Materials = class _Materials {
922
+ /** Shared checkerboard material, created on first access. */
906
923
  static get MISSING_TEXTURE() {
907
924
  return Caching.materialCache.get("builtin:missing-texture", () => new MeshBasicMaterial({
908
925
  map: Textures.getMissing(),
@@ -1006,6 +1023,7 @@ var init_Materials = __esm({
1006
1023
  if (tint !== void 0) material.color.multiply(new Color(tint));
1007
1024
  return material;
1008
1025
  }
1026
+ /** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
1009
1027
  static createGuiCanvasMaterial(canvas) {
1010
1028
  const material = new MeshBasicMaterial({
1011
1029
  map: Textures.createCanvasTexture(canvas),
@@ -1172,6 +1190,7 @@ var init_Materials = __esm({
1172
1190
  }
1173
1191
  return this.createBasicCanvasMaterial(canvas, transparent, shade);
1174
1192
  }
1193
+ /** Returns a shared image material. Use {@link createImage} for a separate material instance. */
1175
1194
  static getImage(key) {
1176
1195
  const keyStr = serializeMaterialKey(key);
1177
1196
  const map = Caching.materialCache.peek(keyStr)?.map;
@@ -1346,6 +1365,14 @@ var init_AssetKey = __esm({
1346
1365
  }
1347
1366
  };
1348
1367
  AssetKey = class _AssetKey extends BasicAssetKey {
1368
+ /**
1369
+ * Creates a key from separate path components. Use {@link parse} for a combined reference.
1370
+ *
1371
+ * @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
1372
+ * @param type - Subdirectory, such as `block` or `item`.
1373
+ * @param rootType - Top-level directory, usually `assets` or `data`.
1374
+ * @param root - Base URL override for hosted asset sources.
1375
+ */
1349
1376
  constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
1350
1377
  super(namespace, path);
1351
1378
  this.namespace = namespace;
@@ -1357,6 +1384,16 @@ var init_AssetKey = __esm({
1357
1384
  this.root = root;
1358
1385
  this.isAssetKey = true;
1359
1386
  }
1387
+ /**
1388
+ * Parses an asset reference such as `minecraft:block/stone`.
1389
+ * A missing namespace defaults to the origin's namespace or `minecraft`.
1390
+ *
1391
+ * @param assetType - Asset category, such as `models` or `textures`.
1392
+ * @param str - Path with an optional namespace and file extension.
1393
+ * @param origin - Supplies the asset root, default namespace, and non-texture extension.
1394
+ * @returns A key with the matching extension removed from its path. Textures use `.png`;
1395
+ * other assets use the origin's extension or `.json`.
1396
+ */
1360
1397
  static parse(assetType, str, origin) {
1361
1398
  let namespace = origin?.namespace || DEFAULT_NAMESPACE;
1362
1399
  if (str.includes(":")) {
@@ -1388,6 +1425,7 @@ var init_AssetKey = __esm({
1388
1425
  ];
1389
1426
  return a.join("/");
1390
1427
  }
1428
+ /** Joins the type subdirectory and path without adding a namespace or file extension. */
1391
1429
  getFullPath() {
1392
1430
  let p10 = [];
1393
1431
  if (this.type) {
@@ -1399,6 +1437,7 @@ var init_AssetKey = __esm({
1399
1437
  toNamespacedString() {
1400
1438
  return this.namespace + ":" + this.getFullPath();
1401
1439
  }
1440
+ /** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
1402
1441
  serialize() {
1403
1442
  return this.toString();
1404
1443
  }
@@ -1410,17 +1449,17 @@ var init_AssetKey = __esm({
1410
1449
  var DisplayPosition, DISPLAY_POSITIONS;
1411
1450
  var init_DisplayPosition = __esm({
1412
1451
  "src/model/DisplayPosition.ts"() {
1413
- DisplayPosition = /* @__PURE__ */ ((DisplayPosition2) => {
1414
- DisplayPosition2["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1415
- DisplayPosition2["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1416
- DisplayPosition2["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1417
- DisplayPosition2["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1418
- DisplayPosition2["GUI"] = "gui";
1419
- DisplayPosition2["HEAD"] = "head";
1420
- DisplayPosition2["GROUND"] = "ground";
1421
- DisplayPosition2["FIXED"] = "fixed";
1422
- DisplayPosition2["ON_SHELF"] = "on_shelf";
1423
- return DisplayPosition2;
1452
+ DisplayPosition = /* @__PURE__ */ ((DisplayPosition3) => {
1453
+ DisplayPosition3["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1454
+ DisplayPosition3["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1455
+ DisplayPosition3["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1456
+ DisplayPosition3["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1457
+ DisplayPosition3["GUI"] = "gui";
1458
+ DisplayPosition3["HEAD"] = "head";
1459
+ DisplayPosition3["GROUND"] = "ground";
1460
+ DisplayPosition3["FIXED"] = "fixed";
1461
+ DisplayPosition3["ON_SHELF"] = "on_shelf";
1462
+ return DisplayPosition3;
1424
1463
  })(DisplayPosition || {});
1425
1464
  DISPLAY_POSITIONS = Object.values(DisplayPosition);
1426
1465
  }
@@ -1458,6 +1497,7 @@ var init_DisplayTransforms = __esm({
1458
1497
  const scale = new Vector32(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
1459
1498
  return new Matrix4().compose(translation, rotation, scale);
1460
1499
  }
1500
+ /** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
1461
1501
  static apply(geometry, matrix) {
1462
1502
  geometry.applyMatrix4(matrix);
1463
1503
  if (matrix.determinant() < 0) {
@@ -1485,6 +1525,7 @@ var init_ModelMerger = __esm({
1485
1525
  init_AssetKey();
1486
1526
  init_DisplayTransforms();
1487
1527
  ModelMerger = class {
1528
+ /** Loads the parent chain and returns a merged model without modifying the supplied definition. */
1488
1529
  static async mergeWithParents(model) {
1489
1530
  const models = await this.collectAllParents(model);
1490
1531
  let merged = {};
@@ -1552,6 +1593,7 @@ var init_Models = __esm({
1552
1593
  static get PERSISTENT_CACHE() {
1553
1594
  return this._persistentCache ??= PersistentCache.open("minerender-models");
1554
1595
  }
1596
+ /** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
1555
1597
  static async getItemList() {
1556
1598
  const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
1557
1599
  const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
@@ -1622,6 +1664,7 @@ var init_Models = __esm({
1622
1664
  }
1623
1665
  throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
1624
1666
  }
1667
+ /** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
1625
1668
  static async getRaw(key) {
1626
1669
  if (!key.assetType) {
1627
1670
  key.assetType = "models";
@@ -1634,13 +1677,19 @@ var init_Models = __esm({
1634
1677
  return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1635
1678
  return AssetLoader.get(key, "model" /* MODEL */);
1636
1679
  });
1637
- }).then((asset) => {
1638
- if (asset) {
1639
- asset.key = key;
1680
+ }).then((asset2) => {
1681
+ if (asset2) {
1682
+ asset2.key = key;
1640
1683
  }
1641
- return asset;
1684
+ return asset2;
1642
1685
  });
1643
1686
  }
1687
+ /**
1688
+ * Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
1689
+ * Item keys use GUI context, false conditions, and zero numeric properties.
1690
+ *
1691
+ * @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
1692
+ */
1644
1693
  static async getMerged(key) {
1645
1694
  if (!key.assetType) {
1646
1695
  key.assetType = "models";
@@ -1653,9 +1702,11 @@ var init_Models = __esm({
1653
1702
  return _Models.loadAndMerge(key);
1654
1703
  });
1655
1704
  }
1705
+ /** Alias for {@link getMerged}. */
1656
1706
  static async get(key) {
1657
1707
  return this.getMerged(key);
1658
1708
  }
1709
+ /** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
1659
1710
  static async clearCache() {
1660
1711
  return this.PERSISTENT_CACHE.clear();
1661
1712
  }
@@ -1678,6 +1729,7 @@ var init_BlockStates = __esm({
1678
1729
  return this._persistentCache ??= PersistentCache.open("minerender-blockstates");
1679
1730
  }
1680
1731
  // BlockState names are hardcoded
1732
+ /** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
1681
1733
  static async getList() {
1682
1734
  const key = new AssetKey(
1683
1735
  DEFAULT_NAMESPACE,
@@ -1698,6 +1750,7 @@ var init_BlockStates = __esm({
1698
1750
  return AssetLoader.get(key, "json" /* JSON */);
1699
1751
  });
1700
1752
  }
1753
+ /** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
1701
1754
  static async getDefaultState(key) {
1702
1755
  const defaultStates = await this.getDefaultStates();
1703
1756
  if (!defaultStates) {
@@ -1705,6 +1758,7 @@ var init_BlockStates = __esm({
1705
1758
  }
1706
1759
  return defaultStates["minecraft:" + key.path];
1707
1760
  }
1761
+ /** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
1708
1762
  static async get(key) {
1709
1763
  if (!key.assetType) {
1710
1764
  key.assetType = "blockstates";
@@ -1717,11 +1771,11 @@ var init_BlockStates = __esm({
1717
1771
  return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1718
1772
  return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
1719
1773
  });
1720
- }).then((asset) => {
1721
- if (asset) {
1722
- asset.key = key;
1774
+ }).then((asset2) => {
1775
+ if (asset2) {
1776
+ asset2.key = key;
1723
1777
  }
1724
- return asset;
1778
+ return asset2;
1725
1779
  });
1726
1780
  }
1727
1781
  static getAll(keys) {
@@ -1731,6 +1785,7 @@ var init_BlockStates = __esm({
1731
1785
  }
1732
1786
  return Promise.all(promises);
1733
1787
  }
1788
+ /** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
1734
1789
  static async clearCache() {
1735
1790
  await this.PERSISTENT_CACHE.clear();
1736
1791
  }
@@ -1756,9 +1811,11 @@ var init_Assets = __esm({
1756
1811
  static parseAssetKey(assetType, str, origin) {
1757
1812
  return AssetKey.parse(assetType, str, origin);
1758
1813
  }
1814
+ /** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
1759
1815
  static async getModel(key) {
1760
1816
  return Models.getMerged(key);
1761
1817
  }
1818
+ /** Loads a blockstate definition. See {@link BlockStates.get}. */
1762
1819
  static async getBlockState(key) {
1763
1820
  return BlockStates.get(key);
1764
1821
  }
@@ -1805,6 +1862,10 @@ var BatchedExecutor;
1805
1862
  var init_BatchedExecutor = __esm({
1806
1863
  "src/util/BatchedExecutor.ts"() {
1807
1864
  BatchedExecutor = class {
1865
+ /**
1866
+ * @param interval - Delay between queue runs, in milliseconds.
1867
+ * @param batch - Maximum tasks started per run and maximum tasks active at once.
1868
+ */
1808
1869
  constructor(interval = 1, batch = 30) {
1809
1870
  if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
1810
1871
  if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
@@ -1812,9 +1873,11 @@ var init_BatchedExecutor = __esm({
1812
1873
  this.batch = batch;
1813
1874
  this.queue = new JobQueue2(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
1814
1875
  }
1876
+ /** Queues a task and resolves with its result, or rejects with its error. */
1815
1877
  submit(task) {
1816
1878
  return this.queue.add(() => task());
1817
1879
  }
1880
+ /** Rejects queued and future tasks. Tasks already running are allowed to finish. */
1818
1881
  stop() {
1819
1882
  this.queue.end();
1820
1883
  }
@@ -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 Vector33();
1867
1930
  box.getSize(size);
1868
1931
  let boxGeo = new BoxGeometry2(size.x, size.y, size.z, 1, 1, 1);
1869
1932
  let wireGeo = new EdgesGeometry(boxGeo);
1870
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
1933
+ let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
1871
1934
  let wireframe = new LineSegments(wireGeo, wireMat);
1872
1935
  obj.add(wireframe);
1873
1936
  let axes = new 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 BoxGeometry2(16, 16, 16, 1, 1, 1);
1878
1941
  let wireGeo = new EdgesGeometry(boxGeo);
1879
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
1942
+ let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
1880
1943
  let wireframe = new LineSegments(wireGeo, wireMat);
1881
1944
  obj.add(wireframe);
1882
1945
  let axes = new 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 EdgesGeometry(geo);
1887
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
1950
+ let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
1888
1951
  let wireframe = new LineSegments(wireGeo, wireMat);
1889
1952
  mesh.add(wireframe);
1890
1953
  let axes = new AxesHelper(1);
@@ -1956,6 +2019,7 @@ var NBTHelper;
1956
2019
  var init_NBTHelper = __esm({
1957
2020
  "src/nbt/NBTHelper.ts"() {
1958
2021
  NBTHelper = class {
2022
+ /** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
1959
2023
  static async fromBuffer(data, format2) {
1960
2024
  const buffer = Buffer3.from(data instanceof Uint8Array ? data : new Uint8Array(data));
1961
2025
  const prismarineNbt = await import("prismarine-nbt");
@@ -2100,6 +2164,7 @@ var init_HostedAssetSource = __esm({
2100
2164
  ["json" /* JSON */, _HostedAssetSource.JSON]
2101
2165
  ]);
2102
2166
  }
2167
+ /** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
2103
2168
  async loadOrRetryWithDefaults(key, parser) {
2104
2169
  const direct = await this.load(key, parser);
2105
2170
  if (typeof direct !== "undefined") {
@@ -2157,6 +2222,10 @@ var init_HostedAssetSource = __esm({
2157
2222
  assetBasePath(key) {
2158
2223
  return `${key.root ?? this.root ?? AssetLoader.ROOT}/${key.rootType !== void 0 ? key.rootType + "/" : ""}${key.namespace !== void 0 ? key.namespace + "/" : ""}${key.assetType !== void 0 ? key.assetType + "/" : ""}`;
2159
2224
  }
2225
+ /**
2226
+ * Creates a hosted source for {@link AssetLoader.addSource}.
2227
+ * Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
2228
+ */
2160
2229
  constructor(root, options) {
2161
2230
  super();
2162
2231
  this._root = root;
@@ -2188,6 +2257,11 @@ var BrowserArchiveProxy;
2188
2257
  var init_BrowserArchiveProxy = __esm({
2189
2258
  "src/assets/source/archive/BrowserArchiveProxy.ts"() {
2190
2259
  BrowserArchiveProxy = class {
2260
+ /**
2261
+ * Opens a ZIP for asset lookup.
2262
+ * @param id - Cache identity for this content. Change it when the pack changes.
2263
+ * A File supplies an identity from its name, size, and modification time when omitted.
2264
+ */
2191
2265
  constructor(blob, id) {
2192
2266
  this._blob = blob;
2193
2267
  this._reader = new ZipReader(new BlobReader(this._blob));
@@ -2320,27 +2394,27 @@ var init_PackFormats = __esm({
2320
2394
  this.versions = /* @__PURE__ */ new Map();
2321
2395
  }
2322
2396
  /** Resolves pack formats from Mojang's client metadata hosted on assets.mcasset.cloud. */
2323
- static async get(version, type) {
2324
- let pending = this.versions.get(version);
2397
+ static async get(version2, type) {
2398
+ let pending = this.versions.get(version2);
2325
2399
  if (!pending || pending.expires <= Date.now()) {
2326
2400
  pending = {
2327
- formats: this.load(version),
2328
- expires: ["latest", "release", "snapshot"].includes(version) ? Date.now() + 3e5 : Infinity
2401
+ formats: this.load(version2),
2402
+ expires: ["latest", "release", "snapshot"].includes(version2) ? Date.now() + 3e5 : Infinity
2329
2403
  };
2330
- this.versions.set(version, pending);
2404
+ this.versions.set(version2, pending);
2331
2405
  }
2332
2406
  try {
2333
2407
  return (await pending.formats)[type];
2334
2408
  } catch (cause) {
2335
- if (this.versions.get(version) === pending) this.versions.delete(version);
2409
+ if (this.versions.get(version2) === pending) this.versions.delete(version2);
2336
2410
  const option = type === "assets" ? "resourcePackFormat" : "dataPackFormat";
2337
2411
  const reason = cause instanceof Error ? cause.message : String(cause);
2338
- throw new Error(`Could not resolve the pack format for Minecraft ${version}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
2412
+ throw new Error(`Could not resolve the pack format for Minecraft ${version2}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
2339
2413
  }
2340
2414
  }
2341
- static async load(version) {
2415
+ static async load(version2) {
2342
2416
  const response = await Requests.genericRequest({
2343
- url: `https://assets.mcasset.cloud/${encodeURIComponent(version)}/game-version.json`,
2417
+ url: `https://assets.mcasset.cloud/${encodeURIComponent(version2)}/game-version.json`,
2344
2418
  responseType: "json"
2345
2419
  });
2346
2420
  const data = response.data;
@@ -2387,6 +2461,7 @@ var init_ArchiveAssetSource = __esm({
2387
2461
  dataPackFormat: copyFormat(options.dataPackFormat)
2388
2462
  };
2389
2463
  }
2464
+ /** Creates a browser ZIP source from a Blob or File. */
2390
2465
  static blob(blob, options) {
2391
2466
  return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
2392
2467
  }
@@ -2427,7 +2502,7 @@ var init_ArchiveAssetSource = __esm({
2427
2502
  }
2428
2503
  async load(key, parser) {
2429
2504
  const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
2430
- const version = AssetLoader.version;
2505
+ const version2 = AssetLoader.version;
2431
2506
  let selectedPath = path;
2432
2507
  let url;
2433
2508
  try {
@@ -2435,7 +2510,7 @@ var init_ArchiveAssetSource = __esm({
2435
2510
  if (key.rootType === "assets" || key.rootType === "data") {
2436
2511
  const metadata = await this.getMetadata(key);
2437
2512
  if (metadata.hasOverlays) {
2438
- const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version, key.rootType);
2513
+ const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version2, key.rootType);
2439
2514
  directories = metadata.overlayDirectories(format2);
2440
2515
  }
2441
2516
  }
@@ -2519,6 +2594,7 @@ var init_AssetLoader = __esm({
2519
2594
  FALLBACK_ROOT = "https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master";
2520
2595
  AssetLoader = class {
2521
2596
  static {
2597
+ /** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
2522
2598
  this.ROOT = DEFAULT_ROOT;
2523
2599
  }
2524
2600
  static {
@@ -2531,8 +2607,9 @@ var init_AssetLoader = __esm({
2531
2607
  static get version() {
2532
2608
  return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
2533
2609
  }
2534
- static setVersion(version) {
2535
- this.ROOT = `https://assets.mcasset.cloud/${version}`;
2610
+ /** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
2611
+ static setVersion(version2) {
2612
+ this.ROOT = `https://assets.mcasset.cloud/${version2}`;
2536
2613
  const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
2537
2614
  const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
2538
2615
  if (index !== -1) {
@@ -2564,6 +2641,13 @@ var init_AssetLoader = __esm({
2564
2641
  return scope ? `${scope}
2565
2642
  ${key}` : key;
2566
2643
  }
2644
+ /**
2645
+ * Registers a source at the highest priority.
2646
+ * Call {@link Caching.clear} after changing sources to discard previously loaded assets.
2647
+ *
2648
+ * @param key - Name used to replace or remove this source.
2649
+ * @param override - Removes the first source with this name before adding the new one.
2650
+ */
2567
2651
  static addSource(key, source, override = true) {
2568
2652
  if (override) {
2569
2653
  const existing = this.removeSource(key);
@@ -2574,6 +2658,7 @@ ${key}` : key;
2574
2658
  this._SOURCES.unshift({ key, source });
2575
2659
  console.log(p4, "Added AssetSource", key);
2576
2660
  }
2661
+ /** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
2577
2662
  static removeSource(key) {
2578
2663
  const index = this._SOURCES.findIndex((s) => s.key === key);
2579
2664
  if (index != -1) {
@@ -2588,12 +2673,13 @@ ${key}` : key;
2588
2673
  this.addSource("mcassets-fallback", new HostedAssetSource(FALLBACK_ROOT, { retryDefaults: false }));
2589
2674
  this.addSource("mcassets", new HostedAssetSource(this.ROOT, { retryDefaults: false }));
2590
2675
  }
2676
+ /** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
2591
2677
  static async getAll(key, parser) {
2592
2678
  const sources = [...this._SOURCES];
2593
2679
  const results = [];
2594
2680
  for (const { source } of sources) {
2595
- const asset = await source.get(key, parser);
2596
- if (asset != void 0) results.push(asset);
2681
+ const asset2 = await source.get(key, parser);
2682
+ if (asset2 != void 0) results.push(asset2);
2597
2683
  if (await source.blocks(key)) break;
2598
2684
  }
2599
2685
  return results;
@@ -2649,14 +2735,15 @@ var init_ModelTextures = __esm({
2649
2735
  static get PERSISTENT_META_CACHE() {
2650
2736
  return this._persistentMetaCache ??= PersistentCache.open("minerender-texturemeta");
2651
2737
  }
2738
+ /** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
2652
2739
  static async get(key) {
2653
2740
  const keyStr = key.serialize();
2654
2741
  const pending = this.preload(key);
2655
2742
  const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
2656
2743
  try {
2657
- const asset = await pending;
2658
- if (asset) {
2659
- return await ImageLoader.infoToCanvasData(asset);
2744
+ const asset2 = await pending;
2745
+ if (asset2) {
2746
+ return await ImageLoader.infoToCanvasData(asset2);
2660
2747
  }
2661
2748
  return void 0;
2662
2749
  } catch (err) {
@@ -2666,16 +2753,18 @@ var init_ModelTextures = __esm({
2666
2753
  throw err;
2667
2754
  }
2668
2755
  }
2756
+ /** Fetches and caches encoded texture bytes without decoding the pixels. */
2669
2757
  static async preload(key) {
2670
2758
  const keyStr = key.serialize();
2671
2759
  return Caching.textureAssetCache.get(keyStr, (k) => {
2672
- return AssetLoader.get(key, "image" /* IMAGE */).then((asset) => {
2673
- if (asset)
2674
- asset.key = key;
2675
- return asset;
2760
+ return AssetLoader.get(key, "image" /* IMAGE */).then((asset2) => {
2761
+ if (asset2)
2762
+ asset2.key = key;
2763
+ return asset2;
2676
2764
  });
2677
2765
  });
2678
2766
  }
2767
+ /** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
2679
2768
  static async getMeta(key) {
2680
2769
  if (!key.extension || !key.extension.endsWith(".mcmeta")) {
2681
2770
  key = Object.assign(new AssetKey("", ""), key);
@@ -2684,14 +2773,15 @@ var init_ModelTextures = __esm({
2684
2773
  const keyStr = key.serialize();
2685
2774
  return Caching.textureMetaCache.get(keyStr, (k) => {
2686
2775
  return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
2687
- return AssetLoader.get(key, "meta" /* META */).then((asset) => {
2688
- if (asset)
2689
- asset.key = key;
2690
- return asset;
2776
+ return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
2777
+ if (asset2)
2778
+ asset2.key = key;
2779
+ return asset2;
2691
2780
  });
2692
2781
  });
2693
2782
  });
2694
2783
  }
2784
+ /** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
2695
2785
  static async clearCache() {
2696
2786
  await this.PERSISTENT_META_CACHE.clear();
2697
2787
  }
@@ -2779,6 +2869,7 @@ var init_Ticker = __esm({
2779
2869
  }
2780
2870
  return handle;
2781
2871
  }
2872
+ /** Starts or resumes the shared tick loop. Repeated calls have no effect. */
2782
2873
  static start() {
2783
2874
  if (this.interval) return;
2784
2875
  this.interval = this.schedule(() => {
@@ -2801,6 +2892,7 @@ var init_Ticker = __esm({
2801
2892
  _Ticker.fiveSecondTicks = 0;
2802
2893
  }, 1e3 * 5);
2803
2894
  }
2895
+ /** Pauses the loop while retaining callbacks for a later {@link start}. */
2804
2896
  static stop() {
2805
2897
  if (this.interval) clearInterval(this.interval);
2806
2898
  if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
@@ -2809,12 +2901,14 @@ var init_Ticker = __esm({
2809
2901
  this.oneSecondTicksTracker = void 0;
2810
2902
  this.fiveSecondTicksTracker = void 0;
2811
2903
  }
2904
+ /** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
2812
2905
  static add(tick) {
2813
2906
  this.start();
2814
2907
  const c = this.counter++;
2815
2908
  this._tickers.set(c, tick);
2816
2909
  return c;
2817
2910
  }
2911
+ /** Removes a callback by ID and stops the timers when no callbacks remain. */
2818
2912
  static remove(c) {
2819
2913
  if (typeof c === "number") this._tickers.delete(c);
2820
2914
  if (!this._tickers.size) this.stop();
@@ -2828,6 +2922,7 @@ var init_Ticker = __esm({
2828
2922
  static get tpsFiveSeconds() {
2829
2923
  return this.fiveSecondTps;
2830
2924
  }
2925
+ /** Stops the loop and removes all registered callbacks. */
2831
2926
  static dispose() {
2832
2927
  this.stop();
2833
2928
  this._tickers.clear();
@@ -2874,6 +2969,7 @@ var init_TextureAtlas = __esm({
2874
2969
  }
2875
2970
  };
2876
2971
  }
2972
+ /** Copies pixels for a texture variable from its current atlas rectangle. */
2877
2973
  getData(texture) {
2878
2974
  const pos = this.positions[texture];
2879
2975
  const size = this.sizes[texture];
@@ -3153,6 +3249,7 @@ var init_UVMapper = __esm({
3153
3249
  }
3154
3250
  uv.needsUpdate = true;
3155
3251
  }
3252
+ /** Returns the shared atlas for a keyed, merged model, creating it on first use. */
3156
3253
  static async getAtlas(model) {
3157
3254
  const keyStr = model.key.serialize();
3158
3255
  return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
@@ -3223,6 +3320,11 @@ var init_UVMapper = __esm({
3223
3320
  }
3224
3321
  return [tl, tr, bl, br];
3225
3322
  }
3323
+ /**
3324
+ * Combines model textures and animation metadata into an atlas.
3325
+ * Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
3326
+ * Returns `undefined` for models without a texture map.
3327
+ */
3226
3328
  static async createAtlas(originalModel) {
3227
3329
  const textureMap = {};
3228
3330
  const metaMap = {};
@@ -3243,12 +3345,12 @@ var init_UVMapper = __esm({
3243
3345
  } else {
3244
3346
  uniqueTextureNames.push(textureKey);
3245
3347
  const assetKey = AssetKey.parse("textures", textureValue, model.key);
3246
- promises.push(ModelTextures.get(assetKey).then((asset) => {
3247
- if (!asset) {
3348
+ promises.push(ModelTextures.get(assetKey).then((asset2) => {
3349
+ if (!asset2) {
3248
3350
  console.warn(p5, "Missing texture", assetKey);
3249
3351
  return;
3250
3352
  }
3251
- textureMap[textureKey] = new WrappedImage(asset);
3353
+ textureMap[textureKey] = new WrappedImage(asset2);
3252
3354
  }));
3253
3355
  promises.push(ModelTextures.getMeta(assetKey).then((meta) => {
3254
3356
  metaMap[textureKey] = meta;
@@ -3587,14 +3689,15 @@ var init_Entities = __esm({
3587
3689
  init_MineRenderError();
3588
3690
  init_ModelTextures();
3589
3691
  Entities = class {
3692
+ /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
3590
3693
  static async getEntityList() {
3591
3694
  const collect = async (path) => {
3592
3695
  const prefix2 = path ? `${path}/` : "";
3593
3696
  const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
3594
- const list = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3595
- if (!list) return [];
3596
- const files = list.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3597
- const children = await Promise.all(list.directories.map((directory) => collect(`${prefix2}${directory}`)));
3697
+ const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3698
+ if (!list2) return [];
3699
+ const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3700
+ const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
3598
3701
  return [...files, ...children.flat()];
3599
3702
  };
3600
3703
  return collect("");
@@ -3604,6 +3707,7 @@ var init_Entities = __esm({
3604
3707
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
3605
3708
  return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3606
3709
  }
3710
+ /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
3607
3711
  static async getLayerList(modelKey) {
3608
3712
  return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
3609
3713
  }
@@ -3611,6 +3715,15 @@ var init_Entities = __esm({
3611
3715
  static async getPassList(modelKey) {
3612
3716
  return (await this.getModelFile(modelKey))?.passes ?? [];
3613
3717
  }
3718
+ /**
3719
+ * Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
3720
+ * By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
3721
+ *
3722
+ * @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
3723
+ * @param textureKey - Texture override for the first selected layer.
3724
+ * @param options - Layer selection, conditional passes, and per-layer texture overrides.
3725
+ * @returns The selected model, or `undefined` when its dataset file is missing.
3726
+ */
3614
3727
  static async getEntity(modelKey, textureKey, options) {
3615
3728
  const model = await this.getModelFile(modelKey);
3616
3729
  if (!model) return void 0;
@@ -3656,6 +3769,7 @@ var init_Entities = __esm({
3656
3769
  const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3657
3770
  return file?.animations;
3658
3771
  }
3772
+ /** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
3659
3773
  static async resolveTexture(modelKey) {
3660
3774
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3661
3775
  const root = isAssetKey(modelKey) ? modelKey.root : void 0;
@@ -3667,8 +3781,8 @@ var init_Entities = __esm({
3667
3781
  if (await ModelTextures.get(texture2)) return texture2;
3668
3782
  }
3669
3783
  const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
3670
- const list = await AssetLoader.get(listKey, "list" /* LIST */);
3671
- const files = list?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3784
+ const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
3785
+ const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3672
3786
  const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
3673
3787
  if (!file) return void 0;
3674
3788
  const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
@@ -3773,27 +3887,27 @@ var init_EntityAnimationPlayer = __esm({
3773
3887
  for (const layer of layers) {
3774
3888
  const objects = [];
3775
3889
  if (poses) {
3776
- layer.traverse((object2) => {
3777
- if (poses.has(object2)) objects.push(object2);
3890
+ layer.traverse((object3) => {
3891
+ if (poses.has(object3)) objects.push(object3);
3778
3892
  });
3779
3893
  } else {
3780
3894
  for (const bone of Object.keys(animation.bones)) {
3781
- const object2 = layer.getObjectByName(`group:${bone}`);
3782
- if (object2) objects.push(object2);
3895
+ const object3 = layer.getObjectByName(`group:${bone}`);
3896
+ if (object3) objects.push(object3);
3783
3897
  }
3784
3898
  }
3785
- for (const object2 of objects) {
3786
- const bone = object2.name.slice("group:".length);
3787
- const pose = poses?.get(object2);
3899
+ for (const object3 of objects) {
3900
+ const bone = object3.name.slice("group:".length);
3901
+ const pose = poses?.get(object3);
3788
3902
  this.parts.push({
3789
3903
  bone,
3790
- object: object2,
3791
- position: object2.position.clone(),
3792
- rotation: object2.rotation.clone(),
3793
- scale: object2.scale.clone(),
3794
- basePosition: pose ? new Vector35(...pose.offset) : object2.position.clone(),
3795
- baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") : object2.rotation.clone(),
3796
- baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) : object2.scale.clone()
3904
+ object: object3,
3905
+ position: object3.position.clone(),
3906
+ rotation: object3.rotation.clone(),
3907
+ scale: object3.scale.clone(),
3908
+ basePosition: pose ? new Vector35(...pose.offset) : object3.position.clone(),
3909
+ baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") : object3.rotation.clone(),
3910
+ baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) : object3.scale.clone()
3797
3911
  });
3798
3912
  }
3799
3913
  }
@@ -3816,12 +3930,14 @@ var init_EntityAnimationPlayer = __esm({
3816
3930
  this._animation = void 0;
3817
3931
  this._time = 0;
3818
3932
  }
3933
+ /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
3819
3934
  setTime(seconds) {
3820
3935
  if (!this._animation) return false;
3821
3936
  this._time = seconds;
3822
3937
  this.apply();
3823
3938
  return true;
3824
3939
  }
3940
+ /** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
3825
3941
  advance(deltaSeconds) {
3826
3942
  return this.setTime(this._time + deltaSeconds * this.speed);
3827
3943
  }
@@ -3859,6 +3975,7 @@ var init_InstanceReference = __esm({
3859
3975
  if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
3860
3976
  return this.instanceable;
3861
3977
  }
3978
+ /** Releases this placement's slot for reuse. Repeated calls have no effect. */
3862
3979
  removeFromScene() {
3863
3980
  if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
3864
3981
  }
@@ -3871,9 +3988,11 @@ var init_InstanceReference = __esm({
3871
3988
  setMatrix(matrix) {
3872
3989
  this.activeInstanceable.setMatrixAt(this.index, matrix);
3873
3990
  }
3991
+ /** Hides or restores this placement while preserving its transform and allocated slot. */
3874
3992
  setVisible(visible) {
3875
3993
  this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
3876
3994
  }
3995
+ /** Updates the supplied transform components. Positions use scene units and rotations use radians. */
3877
3996
  setPositionRotationScale(position, rotation, scale) {
3878
3997
  this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
3879
3998
  }
@@ -3950,6 +4069,7 @@ var init_SceneObject = __esm({
3950
4069
  }
3951
4070
  async init() {
3952
4071
  }
4072
+ /** Marks ancestor scenes for redraw and emits a `change` event. */
3953
4073
  notifyDirty() {
3954
4074
  this.traverseAncestors((parent) => {
3955
4075
  const scene = parent;
@@ -3985,15 +4105,17 @@ var init_SceneObject = __esm({
3985
4105
  return obj;
3986
4106
  }
3987
4107
  /**
3988
- * Get a group by its name
4108
+ * Finds a named part, such as `head`, without the internal `group:` prefix.
4109
+ * Returns `undefined` when the group does not exist.
3989
4110
  */
3990
4111
  getGroupByName(name) {
3991
4112
  return this.getObjectByName(`group:${name}`);
3992
4113
  }
3993
4114
  /**
3994
- * Toggle visibility of a group
3995
- * @param name name of the group
3996
- * @param visible set the visibility directly, if not set toggles it
4115
+ * Changes a named group's visibility and requests a redraw.
4116
+ * @param name - Part name without the `group:` prefix.
4117
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4118
+ * @returns The resulting visibility, or `false` if the group does not exist.
3997
4119
  */
3998
4120
  toggleGroupVisibility(name, visible) {
3999
4121
  return this.toggleObjectVisibility(this.getGroupByName(name), visible);
@@ -4044,15 +4166,16 @@ var init_SceneObject = __esm({
4044
4166
  return mesh;
4045
4167
  }
4046
4168
  /**
4047
- * Get a mesh by its name
4169
+ * Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
4048
4170
  */
4049
4171
  getMeshByName(name) {
4050
4172
  return this.getObjectByName(`mesh:${name}`);
4051
4173
  }
4052
4174
  /**
4053
- * Toggle visibility of a mesh
4054
- * @param name name of the mesh
4055
- * @param visible set the visibility directly, if not set toggles it
4175
+ * Changes a named mesh's visibility and requests a redraw.
4176
+ * @param name - Mesh name without the `mesh:` prefix.
4177
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4178
+ * @returns The resulting visibility, or `false` if the mesh does not exist.
4056
4179
  */
4057
4180
  toggleMeshVisibility(name, visible) {
4058
4181
  return this.toggleObjectVisibility(this.getMeshByName(name), visible);
@@ -4098,12 +4221,14 @@ var init_SceneObject = __esm({
4098
4221
  get isInstanced() {
4099
4222
  return this._isInstanced;
4100
4223
  }
4224
+ /** Number of live placements, excluding released instance slots. */
4101
4225
  get instanceCounter() {
4102
4226
  return this._instanceCounter;
4103
4227
  }
4104
4228
  constructInstanceReference(i) {
4105
4229
  return new InstanceReference(this, i);
4106
4230
  }
4231
+ /** Allocates a placement of this instanced object, reusing a released slot when available. */
4107
4232
  nextInstance() {
4108
4233
  const mesh = this.instanceMesh;
4109
4234
  if (!mesh) throw new MineRenderError("Object is not instanced");
@@ -4148,10 +4273,10 @@ var init_SceneObject = __esm({
4148
4273
  return new InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
4149
4274
  };
4150
4275
  const matrix = grow(mesh.instanceMatrix, 0);
4151
- const color = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
4276
+ const color2 = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
4152
4277
  mesh.dispose();
4153
4278
  mesh.instanceMatrix = matrix;
4154
- mesh.instanceColor = color;
4279
+ mesh.instanceColor = color2;
4155
4280
  }
4156
4281
  *activeInstanceIndices() {
4157
4282
  if (this.instanceMesh) {
@@ -4250,6 +4375,7 @@ var init_SceneObject = __esm({
4250
4375
  vector.setFromMatrixScale(matrix);
4251
4376
  return vector;
4252
4377
  }
4378
+ /** Updates supplied transform components. For a shared model, affects every live instance. */
4253
4379
  setPositionRotationScale(position, rotation, scale) {
4254
4380
  if (this.isInstanced) {
4255
4381
  for (const i of this.activeInstanceIndices()) {
@@ -4268,6 +4394,7 @@ var init_SceneObject = __esm({
4268
4394
  }
4269
4395
  this.notifyDirty();
4270
4396
  }
4397
+ /** Sets position in scene units. For a shared model, updates every live instance. */
4271
4398
  setPosition(position) {
4272
4399
  if (this.isInstanced) {
4273
4400
  for (const i of this.activeInstanceIndices()) {
@@ -4285,6 +4412,7 @@ var init_SceneObject = __esm({
4285
4412
  return this.position;
4286
4413
  }
4287
4414
  }
4415
+ /** Sets rotation in radians. For a shared model, updates every live instance. */
4288
4416
  setRotation(rotation) {
4289
4417
  if (this.isInstanced) {
4290
4418
  for (const i of this.activeInstanceIndices()) {
@@ -4302,6 +4430,7 @@ var init_SceneObject = __esm({
4302
4430
  return this.rotation;
4303
4431
  }
4304
4432
  }
4433
+ /** Sets scale factors. For a shared model, updates every live instance. */
4305
4434
  setScale(scale) {
4306
4435
  if (this.isInstanced) {
4307
4436
  for (const i of this.activeInstanceIndices()) {
@@ -4320,15 +4449,15 @@ var init_SceneObject = __esm({
4320
4449
  }
4321
4450
  }
4322
4451
  //</editor-fold>
4323
- toggleObjectVisibility(object2, visible) {
4324
- if (object2) {
4452
+ toggleObjectVisibility(object3, visible) {
4453
+ if (object3) {
4325
4454
  if (typeof visible !== "undefined") {
4326
- object2.visible = visible;
4455
+ object3.visible = visible;
4327
4456
  } else {
4328
- object2.visible = !object2.visible;
4457
+ object3.visible = !object3.visible;
4329
4458
  }
4330
4459
  this.notifyDirty();
4331
- return object2.visible;
4460
+ return object3.visible;
4332
4461
  }
4333
4462
  this.notifyDirty();
4334
4463
  return false;
@@ -4339,6 +4468,7 @@ var init_SceneObject = __esm({
4339
4468
  this.notifyDirty();
4340
4469
  super.dispose();
4341
4470
  }
4471
+ /** Releases instance slots and removes children, disposing children that implement Disposable. */
4342
4472
  disposeAndRemoveAllChildren() {
4343
4473
  if (this.instanceMesh) {
4344
4474
  if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
@@ -4361,6 +4491,7 @@ var init_SceneObject = __esm({
4361
4491
  }
4362
4492
  this.notifyDirty();
4363
4493
  }
4494
+ /** Detaches the object from its parent and requests a redraw. */
4364
4495
  removeFromScene() {
4365
4496
  this.notifyDirty();
4366
4497
  this.removeFromParent();
@@ -4512,12 +4643,15 @@ var init_EntityObject = __esm({
4512
4643
  if (this.animationPlayers.length) this.notifyDirty();
4513
4644
  }
4514
4645
  //</editor-fold>
4646
+ /** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
4515
4647
  getLayerGroup(name) {
4516
4648
  return super.getGroupByName(`layer:${name}`);
4517
4649
  }
4650
+ /** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
4518
4651
  getGroupByName(name, layerName) {
4519
4652
  return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
4520
4653
  }
4654
+ /** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
4521
4655
  getMeshByName(name, layerName) {
4522
4656
  return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
4523
4657
  }
@@ -4619,8 +4753,8 @@ var init_EntityObject = __esm({
4619
4753
  this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
4620
4754
  }
4621
4755
  }
4622
- this.getLayerGroup(name)?.traverse((object2) => {
4623
- if (isMesh(object2)) object2.material = mat;
4756
+ this.getLayerGroup(name)?.traverse((object3) => {
4757
+ if (isMesh(object3)) object3.material = mat;
4624
4758
  });
4625
4759
  }));
4626
4760
  if (!this.children.length) {
@@ -4959,8 +5093,8 @@ function toTrianglesDrawMode(geometry, drawMode) {
4959
5093
  return geometry;
4960
5094
  }
4961
5095
  }
4962
- function computeMorphedAttributes(object2) {
4963
- if (object2.geometry.isBufferGeometry !== true) {
5096
+ function computeMorphedAttributes(object3) {
5097
+ if (object3.geometry.isBufferGeometry !== true) {
4964
5098
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4965
5099
  return null;
4966
5100
  }
@@ -4973,11 +5107,11 @@ function computeMorphedAttributes(object2) {
4973
5107
  const _morphA = new Vector310();
4974
5108
  const _morphB = new Vector310();
4975
5109
  const _morphC = new Vector310();
4976
- function _calculateMorphedAttributeData(object3, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
5110
+ function _calculateMorphedAttributeData(object4, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4977
5111
  _vA.fromBufferAttribute(attribute, a2);
4978
5112
  _vB.fromBufferAttribute(attribute, b2);
4979
5113
  _vC.fromBufferAttribute(attribute, c2);
4980
- const morphInfluences = object3.morphTargetInfluences;
5114
+ const morphInfluences = object4.morphTargetInfluences;
4981
5115
  if (material2.morphTargets && morphAttribute && morphInfluences) {
4982
5116
  _morphA.set(0, 0, 0);
4983
5117
  _morphB.set(0, 0, 0);
@@ -5003,10 +5137,10 @@ function computeMorphedAttributes(object2) {
5003
5137
  _vB.add(_morphB);
5004
5138
  _vC.add(_morphC);
5005
5139
  }
5006
- if (object3.isSkinnedMesh) {
5007
- object3.boneTransform(a2, _vA);
5008
- object3.boneTransform(b2, _vB);
5009
- object3.boneTransform(c2, _vC);
5140
+ if (object4.isSkinnedMesh) {
5141
+ object4.boneTransform(a2, _vA);
5142
+ object4.boneTransform(b2, _vB);
5143
+ object4.boneTransform(c2, _vC);
5010
5144
  }
5011
5145
  modifiedAttributeArray[a2 * 3 + 0] = _vA.x;
5012
5146
  modifiedAttributeArray[a2 * 3 + 1] = _vA.y;
@@ -5018,8 +5152,8 @@ function computeMorphedAttributes(object2) {
5018
5152
  modifiedAttributeArray[c2 * 3 + 1] = _vC.y;
5019
5153
  modifiedAttributeArray[c2 * 3 + 2] = _vC.z;
5020
5154
  }
5021
- const geometry = object2.geometry;
5022
- const material = object2.material;
5155
+ const geometry = object3.geometry;
5156
+ const material = object3.material;
5023
5157
  let a, b, c;
5024
5158
  const index = geometry.index;
5025
5159
  const positionAttribute = geometry.attributes.position;
@@ -5046,7 +5180,7 @@ function computeMorphedAttributes(object2) {
5046
5180
  b = index.getX(j + 1);
5047
5181
  c = index.getX(j + 2);
5048
5182
  _calculateMorphedAttributeData(
5049
- object2,
5183
+ object3,
5050
5184
  groupMaterial,
5051
5185
  positionAttribute,
5052
5186
  morphPosition,
@@ -5057,7 +5191,7 @@ function computeMorphedAttributes(object2) {
5057
5191
  modifiedPosition
5058
5192
  );
5059
5193
  _calculateMorphedAttributeData(
5060
- object2,
5194
+ object3,
5061
5195
  groupMaterial,
5062
5196
  normalAttribute,
5063
5197
  morphNormal,
@@ -5077,7 +5211,7 @@ function computeMorphedAttributes(object2) {
5077
5211
  b = index.getX(i + 1);
5078
5212
  c = index.getX(i + 2);
5079
5213
  _calculateMorphedAttributeData(
5080
- object2,
5214
+ object3,
5081
5215
  material,
5082
5216
  positionAttribute,
5083
5217
  morphPosition,
@@ -5088,7 +5222,7 @@ function computeMorphedAttributes(object2) {
5088
5222
  modifiedPosition
5089
5223
  );
5090
5224
  _calculateMorphedAttributeData(
5091
- object2,
5225
+ object3,
5092
5226
  material,
5093
5227
  normalAttribute,
5094
5228
  morphNormal,
@@ -5112,7 +5246,7 @@ function computeMorphedAttributes(object2) {
5112
5246
  b = j + 1;
5113
5247
  c = j + 2;
5114
5248
  _calculateMorphedAttributeData(
5115
- object2,
5249
+ object3,
5116
5250
  groupMaterial,
5117
5251
  positionAttribute,
5118
5252
  morphPosition,
@@ -5123,7 +5257,7 @@ function computeMorphedAttributes(object2) {
5123
5257
  modifiedPosition
5124
5258
  );
5125
5259
  _calculateMorphedAttributeData(
5126
- object2,
5260
+ object3,
5127
5261
  groupMaterial,
5128
5262
  normalAttribute,
5129
5263
  morphNormal,
@@ -5143,7 +5277,7 @@ function computeMorphedAttributes(object2) {
5143
5277
  b = i + 1;
5144
5278
  c = i + 2;
5145
5279
  _calculateMorphedAttributeData(
5146
- object2,
5280
+ object3,
5147
5281
  material,
5148
5282
  positionAttribute,
5149
5283
  morphPosition,
@@ -5154,7 +5288,7 @@ function computeMorphedAttributes(object2) {
5154
5288
  modifiedPosition
5155
5289
  );
5156
5290
  _calculateMorphedAttributeData(
5157
- object2,
5291
+ object3,
5158
5292
  material,
5159
5293
  normalAttribute,
5160
5294
  morphNormal,
@@ -5188,6 +5322,7 @@ var init_ModelCulling = __esm({
5188
5322
  "src/model/ModelCulling.ts"() {
5189
5323
  init_CubeFace();
5190
5324
  ModelCulling = class {
5325
+ /** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
5191
5326
  static toLocalMask(worldMask, rotation) {
5192
5327
  let localMask = 0;
5193
5328
  for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
@@ -5198,6 +5333,7 @@ var init_ModelCulling = __esm({
5198
5333
  }
5199
5334
  return localMask;
5200
5335
  }
5336
+ /** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
5201
5337
  static isOpaqueFullCube(atlas) {
5202
5338
  if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
5203
5339
  const element = atlas.model.elements[0];
@@ -5408,9 +5544,9 @@ var init_ItemTints = __esm({
5408
5544
  }));
5409
5545
  return colors;
5410
5546
  }
5411
- static rgb(color) {
5412
- if (!Array.isArray(color)) return color & 16777215;
5413
- const [red, green, blue] = color.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
5547
+ static rgb(color2) {
5548
+ if (!Array.isArray(color2)) return color2 & 16777215;
5549
+ const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
5414
5550
  return red << 16 | green << 8 | blue;
5415
5551
  }
5416
5552
  };
@@ -5466,13 +5602,13 @@ var init_ModelObject = __esm({
5466
5602
  transform.multiply(new Matrix49().makeTranslation(-8, -8, -8));
5467
5603
  try {
5468
5604
  for (const part of parts) {
5469
- const object2 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
5470
- object2.matrix.copy(transform).multiply(part.transform);
5471
- object2.matrixWorldNeedsUpdate = true;
5472
- object2.matrixAutoUpdate = false;
5473
- this.add(object2);
5474
- await object2.init();
5475
- object2.iterateAllMeshes((mesh) => {
5605
+ const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
5606
+ object3.matrix.copy(transform).multiply(part.transform);
5607
+ object3.matrixWorldNeedsUpdate = true;
5608
+ object3.matrixAutoUpdate = false;
5609
+ this.add(object3);
5610
+ await object3.init();
5611
+ object3.iterateAllMeshes((mesh) => {
5476
5612
  const source = mesh.material;
5477
5613
  let material = this.specialMaterials.get(source);
5478
5614
  if (!material) {
@@ -5483,7 +5619,7 @@ var init_ModelObject = __esm({
5483
5619
  mesh.material = material;
5484
5620
  });
5485
5621
  for (const [name, rotation] of Object.entries(part.rotations)) {
5486
- object2.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
5622
+ object3.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
5487
5623
  }
5488
5624
  }
5489
5625
  } catch (error) {
@@ -5536,9 +5672,9 @@ var init_ModelObject = __esm({
5536
5672
  if (tintIndex === void 0 || tintIndex < 0) continue;
5537
5673
  const tint = this.options.tints[tintIndex];
5538
5674
  if (tint === void 0) continue;
5539
- const color = new Color9(tint);
5675
+ const color2 = new Color9(tint);
5540
5676
  for (let vertex = 0; vertex < 4; vertex++) {
5541
- color.toArray(colors, (faceIndex * 4 + vertex) * 3);
5677
+ color2.toArray(colors, (faceIndex * 4 + vertex) * 3);
5542
5678
  }
5543
5679
  }
5544
5680
  elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
@@ -5853,9 +5989,9 @@ var init_FluidModelObject = __esm({
5853
5989
  }
5854
5990
  }
5855
5991
  geometry.clearGroups();
5856
- const color = new Color10(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
5992
+ const color2 = new Color10(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
5857
5993
  const colors = new Float32Array(geometry.getAttribute("position").count * 3);
5858
- for (let i = 0; i < colors.length; i += 3) color.toArray(colors, i);
5994
+ for (let i = 0; i < colors.length; i += 3) color2.toArray(colors, i);
5859
5995
  geometry.setAttribute("color", new BufferAttribute6(colors, 3));
5860
5996
  geometry.computeBoundingBox();
5861
5997
  geometry.computeBoundingSphere();
@@ -5948,9 +6084,9 @@ var NodeEnv = class {
5948
6084
  imageSize(data) {
5949
6085
  return imageSize(Buffer.isBuffer(data) ? data : Buffer.from(data));
5950
6086
  }
5951
- openCache(name, version) {
6087
+ openCache(name, version2) {
5952
6088
  return new NodeCache(NodePersist.create({
5953
- dir: name + version,
6089
+ dir: name + version2,
5954
6090
  encoding: "utf8"
5955
6091
  }));
5956
6092
  }
@@ -5982,6 +6118,7 @@ var Meshes = class _Meshes {
5982
6118
  static createBox(key) {
5983
6119
  return new Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
5984
6120
  }
6121
+ /** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
5985
6122
  static getBox(key) {
5986
6123
  const keyStr = serializeMeshKey(key);
5987
6124
  return Caching.boxMeshCache.get(keyStr, (k) => {
@@ -6094,6 +6231,7 @@ init_AssetParsers();
6094
6231
  init_ModelTextures();
6095
6232
  init_Caching();
6096
6233
  var Fonts = class {
6234
+ /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
6097
6235
  static async get(key = "minecraft:default") {
6098
6236
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
6099
6237
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -6109,8 +6247,8 @@ var Fonts = class {
6109
6247
  const stack = await AssetLoader.getAll(key, "json" /* JSON */);
6110
6248
  if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
6111
6249
  const glyphs = /* @__PURE__ */ new Map();
6112
- for (const asset of stack) {
6113
- for (const provider of asset.providers) {
6250
+ for (const asset2 of stack) {
6251
+ for (const provider of asset2.providers) {
6114
6252
  if (provider.filter?.uniform || provider.filter?.jp) continue;
6115
6253
  let provided;
6116
6254
  switch (provider.type) {
@@ -6256,13 +6394,13 @@ var OBJExporter = class {
6256
6394
  * @param {Object3D} object - The 3D object to export.
6257
6395
  * @return {string} The exported OBJ.
6258
6396
  */
6259
- parse(object2) {
6397
+ parse(object3) {
6260
6398
  let output = "";
6261
6399
  let indexVertex = 0;
6262
6400
  let indexVertexUvs = 0;
6263
6401
  let indexNormals = 0;
6264
6402
  const vertex = new Vector37();
6265
- const color = new Color4();
6403
+ const color2 = new Color4();
6266
6404
  const normal = new Vector37();
6267
6405
  const uv = new Vector22();
6268
6406
  const face = [];
@@ -6361,9 +6499,9 @@ var OBJExporter = class {
6361
6499
  vertex.applyMatrix4(points.matrixWorld);
6362
6500
  output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
6363
6501
  if (colors !== void 0) {
6364
- color.fromBufferAttribute(colors, i);
6365
- ColorManagement.workingToColorSpace(color, SRGBColorSpace3);
6366
- output += " " + color.r + " " + color.g + " " + color.b;
6502
+ color2.fromBufferAttribute(colors, i);
6503
+ ColorManagement.workingToColorSpace(color2, SRGBColorSpace3);
6504
+ output += " " + color2.r + " " + color2.g + " " + color2.b;
6367
6505
  }
6368
6506
  output += "\n";
6369
6507
  }
@@ -6375,7 +6513,7 @@ var OBJExporter = class {
6375
6513
  }
6376
6514
  indexVertex += nbVertex;
6377
6515
  }
6378
- object2.traverse(function(child) {
6516
+ object3.traverse(function(child) {
6379
6517
  if (child.isMesh === true) {
6380
6518
  parseMesh(child);
6381
6519
  }
@@ -6409,9 +6547,9 @@ var PLYExporter = class {
6409
6547
  * @param {PLYExporter~Options} options - The export options.
6410
6548
  * @return {?(string|ArrayBuffer)} The exported PLY.
6411
6549
  */
6412
- parse(object2, onDone, options = {}) {
6550
+ parse(object3, onDone, options = {}) {
6413
6551
  function traverseMeshes(cb) {
6414
- object2.traverse(function(child) {
6552
+ object3.traverse(function(child) {
6415
6553
  if (child.isMesh === true || child.isPoints) {
6416
6554
  const mesh = child;
6417
6555
  const geometry = mesh.geometry;
@@ -6444,7 +6582,7 @@ var PLYExporter = class {
6444
6582
  for (const name of customAttributeNames) customTypes[name] = "float";
6445
6583
  let vertexCount = 0;
6446
6584
  let faceCount = 0;
6447
- object2.traverse(function(child) {
6585
+ object3.traverse(function(child) {
6448
6586
  if (child.isMesh === true) {
6449
6587
  const mesh = child;
6450
6588
  const geometry = mesh.geometry;
@@ -7066,8 +7204,8 @@ function equalArray(array1, array2) {
7066
7204
  return element === array2[index];
7067
7205
  });
7068
7206
  }
7069
- function stringToArrayBuffer(text) {
7070
- return new TextEncoder().encode(text).buffer;
7207
+ function stringToArrayBuffer(text2) {
7208
+ return new TextEncoder().encode(text2).buffer;
7071
7209
  }
7072
7210
  function isIdentityMatrix(matrix) {
7073
7211
  return equalArray(matrix.elements, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
@@ -7260,12 +7398,12 @@ var GLTFWriter = class {
7260
7398
  * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
7261
7399
  * @param {Object} objectDef
7262
7400
  */
7263
- serializeUserData(object2, objectDef) {
7264
- if (Object.keys(object2.userData).length === 0) return;
7401
+ serializeUserData(object3, objectDef) {
7402
+ if (Object.keys(object3.userData).length === 0) return;
7265
7403
  const options = this.options;
7266
7404
  const extensionsUsed = this.extensionsUsed;
7267
7405
  try {
7268
- const json = JSON.parse(JSON.stringify(object2.userData));
7406
+ const json = JSON.parse(JSON.stringify(object3.userData));
7269
7407
  if (options.includeCustomExtensions && json.gltfExtensions) {
7270
7408
  if (objectDef.extensions === void 0) objectDef.extensions = {};
7271
7409
  for (const extensionName in json.gltfExtensions) {
@@ -7276,7 +7414,7 @@ var GLTFWriter = class {
7276
7414
  }
7277
7415
  if (Object.keys(json).length > 0) objectDef.extras = json;
7278
7416
  } catch (error) {
7279
- console.warn("THREE.GLTFExporter: userData of '" + object2.name + "' won't be serialized because of JSON.stringify error - " + error.message);
7417
+ console.warn("THREE.GLTFExporter: userData of '" + object3.name + "' won't be serialized because of JSON.stringify error - " + error.message);
7280
7418
  }
7281
7419
  }
7282
7420
  /**
@@ -7785,9 +7923,9 @@ var GLTFWriter = class {
7785
7923
  if (material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true) {
7786
7924
  console.warn("GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.");
7787
7925
  }
7788
- const color = material.color.toArray().concat([material.opacity]);
7789
- if (!equalArray(color, [1, 1, 1, 1])) {
7790
- materialDef.pbrMetallicRoughness.baseColorFactor = color;
7926
+ const color2 = material.color.toArray().concat([material.opacity]);
7927
+ if (!equalArray(color2, [1, 1, 1, 1])) {
7928
+ materialDef.pbrMetallicRoughness.baseColorFactor = color2;
7791
7929
  }
7792
7930
  if (material.isMeshStandardMaterial) {
7793
7931
  materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
@@ -8202,13 +8340,13 @@ var GLTFWriter = class {
8202
8340
  * @param {THREE.Object3D} object
8203
8341
  * @return {?number}
8204
8342
  */
8205
- processSkin(object2) {
8343
+ processSkin(object3) {
8206
8344
  const json = this.json;
8207
8345
  const nodeMap = this.nodeMap;
8208
- const node = json.nodes[nodeMap.get(object2)];
8209
- const skeleton = object2.skeleton;
8346
+ const node = json.nodes[nodeMap.get(object3)];
8347
+ const skeleton = object3.skeleton;
8210
8348
  if (skeleton === void 0) return null;
8211
- const rootJoint = object2.skeleton.bones[0];
8349
+ const rootJoint = object3.skeleton.bones[0];
8212
8350
  if (rootJoint === void 0) return null;
8213
8351
  const joints = [];
8214
8352
  const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
@@ -8216,7 +8354,7 @@ var GLTFWriter = class {
8216
8354
  for (let i = 0; i < skeleton.bones.length; ++i) {
8217
8355
  joints.push(nodeMap.get(skeleton.bones[i]));
8218
8356
  temporaryBoneInverse.copy(skeleton.boneInverses[i]);
8219
- temporaryBoneInverse.multiply(object2.bindMatrix).toArray(inverseBindMatrices, i * 16);
8357
+ temporaryBoneInverse.multiply(object3.bindMatrix).toArray(inverseBindMatrices, i * 16);
8220
8358
  }
8221
8359
  if (json.skins === void 0) json.skins = [];
8222
8360
  json.skins.push({
@@ -8232,19 +8370,19 @@ var GLTFWriter = class {
8232
8370
  * @param {THREE.Object3D} object Object3D to processNodeAsync
8233
8371
  * @return {Promise<number>} Index of the node in the nodes list
8234
8372
  */
8235
- async processNodeAsync(object2) {
8373
+ async processNodeAsync(object3) {
8236
8374
  const json = this.json;
8237
8375
  const options = this.options;
8238
8376
  const nodeMap = this.nodeMap;
8239
8377
  if (!json.nodes) json.nodes = [];
8240
- if (object2.pivot !== null) {
8241
- return await this._processNodeWithPivotAsync(object2);
8378
+ if (object3.pivot !== null) {
8379
+ return await this._processNodeWithPivotAsync(object3);
8242
8380
  }
8243
8381
  const nodeDef = {};
8244
8382
  if (options.trs) {
8245
- const rotation = object2.quaternion.toArray();
8246
- const position = object2.position.toArray();
8247
- const scale = object2.scale.toArray();
8383
+ const rotation = object3.quaternion.toArray();
8384
+ const position = object3.position.toArray();
8385
+ const scale = object3.scale.toArray();
8248
8386
  if (!equalArray(rotation, [0, 0, 0, 1])) {
8249
8387
  nodeDef.rotation = rotation;
8250
8388
  }
@@ -8255,28 +8393,28 @@ var GLTFWriter = class {
8255
8393
  nodeDef.scale = scale;
8256
8394
  }
8257
8395
  } else {
8258
- if (object2.matrixAutoUpdate) {
8259
- object2.updateMatrix();
8396
+ if (object3.matrixAutoUpdate) {
8397
+ object3.updateMatrix();
8260
8398
  }
8261
- if (isIdentityMatrix(object2.matrix) === false) {
8262
- nodeDef.matrix = object2.matrix.elements;
8399
+ if (isIdentityMatrix(object3.matrix) === false) {
8400
+ nodeDef.matrix = object3.matrix.elements;
8263
8401
  }
8264
8402
  }
8265
- if (object2.name !== "") nodeDef.name = String(object2.name);
8266
- this.serializeUserData(object2, nodeDef);
8267
- if (object2.isMesh || object2.isLine || object2.isPoints) {
8268
- const meshIndex = await this.processMeshAsync(object2);
8403
+ if (object3.name !== "") nodeDef.name = String(object3.name);
8404
+ this.serializeUserData(object3, nodeDef);
8405
+ if (object3.isMesh || object3.isLine || object3.isPoints) {
8406
+ const meshIndex = await this.processMeshAsync(object3);
8269
8407
  if (meshIndex !== null) nodeDef.mesh = meshIndex;
8270
- } else if (object2.isCamera) {
8271
- nodeDef.camera = this.processCamera(object2);
8408
+ } else if (object3.isCamera) {
8409
+ nodeDef.camera = this.processCamera(object3);
8272
8410
  }
8273
- if (object2.isSkinnedMesh) this.skins.push(object2);
8411
+ if (object3.isSkinnedMesh) this.skins.push(object3);
8274
8412
  const nodeIndex = json.nodes.push(nodeDef) - 1;
8275
- nodeMap.set(object2, nodeIndex);
8276
- if (object2.children.length > 0) {
8413
+ nodeMap.set(object3, nodeIndex);
8414
+ if (object3.children.length > 0) {
8277
8415
  const children = [];
8278
- for (let i = 0, l = object2.children.length; i < l; i++) {
8279
- const child = object2.children[i];
8416
+ for (let i = 0, l = object3.children.length; i < l; i++) {
8417
+ const child = object3.children[i];
8280
8418
  if (child.visible || options.onlyVisible === false) {
8281
8419
  const childNodeIndex = await this.processNodeAsync(child);
8282
8420
  if (childNodeIndex !== null) children.push(childNodeIndex);
@@ -8285,7 +8423,7 @@ var GLTFWriter = class {
8285
8423
  if (children.length > 0) nodeDef.children = children;
8286
8424
  }
8287
8425
  await this._invokeAllAsync(function(ext) {
8288
- ext.writeNode && ext.writeNode(object2, nodeDef);
8426
+ ext.writeNode && ext.writeNode(object3, nodeDef);
8289
8427
  });
8290
8428
  return nodeIndex;
8291
8429
  }
@@ -8294,19 +8432,19 @@ var GLTFWriter = class {
8294
8432
  * @param {THREE.Object3D} object Object3D with pivot
8295
8433
  * @return {Promise<number>} Index of the container node
8296
8434
  */
8297
- async _processNodeWithPivotAsync(object2) {
8435
+ async _processNodeWithPivotAsync(object3) {
8298
8436
  const json = this.json;
8299
8437
  const options = this.options;
8300
8438
  const nodeMap = this.nodeMap;
8301
- const pivot = object2.pivot;
8439
+ const pivot = object3.pivot;
8302
8440
  const containerDef = {};
8303
- const rotation = object2.quaternion.toArray();
8441
+ const rotation = object3.quaternion.toArray();
8304
8442
  const position = [
8305
- object2.position.x + pivot.x,
8306
- object2.position.y + pivot.y,
8307
- object2.position.z + pivot.z
8443
+ object3.position.x + pivot.x,
8444
+ object3.position.y + pivot.y,
8445
+ object3.position.z + pivot.z
8308
8446
  ];
8309
- const scale = object2.scale.toArray();
8447
+ const scale = object3.scale.toArray();
8310
8448
  if (!equalArray(rotation, [0, 0, 0, 1])) {
8311
8449
  containerDef.rotation = rotation;
8312
8450
  }
@@ -8317,28 +8455,28 @@ var GLTFWriter = class {
8317
8455
  containerDef.scale = scale;
8318
8456
  }
8319
8457
  containerDef.extras = { pivot: pivot.toArray() };
8320
- if (object2.name !== "") containerDef.name = String(object2.name);
8321
- this.serializeUserData(object2, containerDef);
8458
+ if (object3.name !== "") containerDef.name = String(object3.name);
8459
+ this.serializeUserData(object3, containerDef);
8322
8460
  const containerIndex = json.nodes.push(containerDef) - 1;
8323
- nodeMap.set(object2, containerIndex);
8461
+ nodeMap.set(object3, containerIndex);
8324
8462
  const childDef = {};
8325
8463
  const childPosition = [-pivot.x, -pivot.y, -pivot.z];
8326
8464
  if (!equalArray(childPosition, [0, 0, 0])) {
8327
8465
  childDef.translation = childPosition;
8328
8466
  }
8329
- if (object2.isMesh || object2.isLine || object2.isPoints) {
8330
- const meshIndex = await this.processMeshAsync(object2);
8467
+ if (object3.isMesh || object3.isLine || object3.isPoints) {
8468
+ const meshIndex = await this.processMeshAsync(object3);
8331
8469
  if (meshIndex !== null) childDef.mesh = meshIndex;
8332
- } else if (object2.isCamera) {
8333
- childDef.camera = this.processCamera(object2);
8470
+ } else if (object3.isCamera) {
8471
+ childDef.camera = this.processCamera(object3);
8334
8472
  }
8335
- if (object2.isSkinnedMesh) this.skins.push(object2);
8473
+ if (object3.isSkinnedMesh) this.skins.push(object3);
8336
8474
  const childIndex = json.nodes.push(childDef) - 1;
8337
8475
  const containerChildren = [childIndex];
8338
- if (object2.children.length > 0) {
8476
+ if (object3.children.length > 0) {
8339
8477
  const grandchildren = [];
8340
- for (let i = 0, l = object2.children.length; i < l; i++) {
8341
- const child = object2.children[i];
8478
+ for (let i = 0, l = object3.children.length; i < l; i++) {
8479
+ const child = object3.children[i];
8342
8480
  if (child.visible || options.onlyVisible === false) {
8343
8481
  const childNodeIndex = await this.processNodeAsync(child);
8344
8482
  if (childNodeIndex !== null) grandchildren.push(childNodeIndex);
@@ -8348,7 +8486,7 @@ var GLTFWriter = class {
8348
8486
  }
8349
8487
  containerDef.children = containerChildren;
8350
8488
  await this._invokeAllAsync(function(ext) {
8351
- ext.writeNode && ext.writeNode(object2, containerDef);
8489
+ ext.writeNode && ext.writeNode(object3, containerDef);
8352
8490
  });
8353
8491
  return containerIndex;
8354
8492
  }
@@ -8791,10 +8929,10 @@ var GLTFMeshGpuInstancing = class {
8791
8929
  this.writer = writer;
8792
8930
  this.name = "EXT_mesh_gpu_instancing";
8793
8931
  }
8794
- writeNode(object2, nodeDef) {
8795
- if (!object2.isInstancedMesh) return;
8932
+ writeNode(object3, nodeDef) {
8933
+ if (!object3.isInstancedMesh) return;
8796
8934
  const writer = this.writer;
8797
- const mesh = object2;
8935
+ const mesh = object3;
8798
8936
  const translationAttr = new Float32Array(mesh.count * 3);
8799
8937
  const rotationAttr = new Float32Array(mesh.count * 4);
8800
8938
  const scaleAttr = new Float32Array(mesh.count * 3);
@@ -8984,8 +9122,8 @@ var SceneExporter = class {
8984
9122
  }
8985
9123
  }
8986
9124
  };
8987
- function localMatrix(object2) {
8988
- return object2.matrixAutoUpdate ? new Matrix47().compose(object2.position, object2.quaternion, object2.scale) : object2.matrix.clone();
9125
+ function localMatrix(object3) {
9126
+ return object3.matrixAutoUpdate ? new Matrix47().compose(object3.position, object3.quaternion, object3.scale) : object3.matrix.clone();
8989
9127
  }
8990
9128
  function createSnapshot(root, gltf, bakeColors = false) {
8991
9129
  const scene = new Scene3();
@@ -9089,24 +9227,24 @@ function createSnapshot(root, gltf, bakeColors = false) {
9089
9227
  scene.add(copy);
9090
9228
  }
9091
9229
  };
9092
- const visit = (object2, parentWorld) => {
9093
- if (!object2.visible) return;
9094
- const world = parentWorld.clone().multiply(localMatrix(object2));
9095
- const mesh = object2;
9230
+ const visit = (object3, parentWorld) => {
9231
+ if (!object3.visible) return;
9232
+ const world = parentWorld.clone().multiply(localMatrix(object3));
9233
+ const mesh = object3;
9096
9234
  if (mesh.isMesh) {
9097
9235
  const instanced = mesh;
9098
9236
  if (instanced.isInstancedMesh) {
9099
9237
  for (let i = 0; i < instanced.count; i++) {
9100
9238
  const matrix = new Matrix47();
9101
9239
  instanced.getMatrixAt(i, matrix);
9102
- const color = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
9103
- addMesh(mesh, world.clone().multiply(matrix), color);
9240
+ const color2 = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
9241
+ addMesh(mesh, world.clone().multiply(matrix), color2);
9104
9242
  }
9105
9243
  } else {
9106
9244
  addMesh(mesh, world);
9107
9245
  }
9108
9246
  }
9109
- for (const child of object2.children) visit(child, world);
9247
+ for (const child of object3.children) visit(child, world);
9110
9248
  };
9111
9249
  try {
9112
9250
  const ancestors = [];
@@ -9182,13 +9320,13 @@ init_AssetKey();
9182
9320
 
9183
9321
  // src/gui/GuiText.ts
9184
9322
  import { BufferGeometry as BufferGeometry2, Color as Color8, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
9185
- async function layoutGuiText(text, options = {}) {
9323
+ async function layoutGuiText(text2, options = {}) {
9186
9324
  const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
9187
9325
  if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
9188
9326
  throw new Error("Text wrap width and line height must be positive");
9189
9327
  }
9190
9328
  const font = await Fonts.get(options.font);
9191
- const runs = typeof text === "string" ? [{ text }] : text;
9329
+ const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
9192
9330
  const glyphs = [], lineWidths = [];
9193
9331
  let line = [], width = 0;
9194
9332
  let trailingNewline = false;
@@ -9242,7 +9380,7 @@ function createGuiTextGeometry(layout, shadow = true) {
9242
9380
  batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
9243
9381
  batches.push(batch);
9244
9382
  }
9245
- const color = new Color8(isShadow ? (style.color & 16579836) >> 2 : style.color);
9383
+ const color2 = new Color8(isShadow ? (style.color & 16579836) >> 2 : style.color);
9246
9384
  const offset = isShadow ? 1 : 0;
9247
9385
  const top = glyph ? 7 - glyph.ascent : 0;
9248
9386
  const height = glyph ? glyph.height * glyph.scale : 8;
@@ -9270,7 +9408,7 @@ function createGuiTextGeometry(layout, shadow = true) {
9270
9408
  const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
9271
9409
  const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
9272
9410
  batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
9273
- for (let i = 0; i < 4; i++) batch.colors.push(color.r, color.g, color.b);
9411
+ for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
9274
9412
  batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
9275
9413
  };
9276
9414
  for (const boldOffset of style.bold ? [0, 1] : [0]) {
@@ -9304,12 +9442,19 @@ var GuiHelper = class {
9304
9442
  size: [width + 24, height + 24]
9305
9443
  }));
9306
9444
  let y = position[1];
9307
- lines.forEach((text, index) => {
9308
- layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y] });
9445
+ lines.forEach((text2, index) => {
9446
+ layers.push({ ...textOptions, name: `tooltip-line-${index}`, text: text2, position: [position[0], y] });
9309
9447
  y += layouts[index].height + (index === 0 ? titleGap : 0);
9310
9448
  });
9311
9449
  return layers;
9312
9450
  }
9451
+ /**
9452
+ * Calculates a slot's top-left position in GUI pixels.
9453
+ * @param slot - Zero-based slot index, or `[column, row]`.
9454
+ * @param origin - Position of the first slot.
9455
+ * @param offset - Horizontal and vertical spacing between slot origins, in pixels.
9456
+ * @param rowSize - Slots per row when `slot` is an index.
9457
+ */
9313
9458
  static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
9314
9459
  const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
9315
9460
  return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
@@ -9754,29 +9899,29 @@ var SceneInspector = class {
9754
9899
  this.objectControlsContainer.innerHTML = "";
9755
9900
  this.addControls(targetObject, targetIntersection, instance);
9756
9901
  }
9757
- addControls(object2, intersection, instance) {
9902
+ addControls(object3, intersection, instance) {
9758
9903
  const container = document.createElement("div");
9759
9904
  container.append(this.separator("Select Parent/Child"));
9760
- if (object2.parent) {
9761
- container.append(this.buttonControl("Select Parent " + object2.parent.constructor.name + " " + object2.parent.name, "P", () => {
9762
- this.selectObject(object2.parent, intersection, instance);
9905
+ if (object3.parent) {
9906
+ container.append(this.buttonControl("Select Parent " + object3.parent.constructor.name + " " + object3.parent.name, "P", () => {
9907
+ this.selectObject(object3.parent, intersection, instance);
9763
9908
  }));
9764
9909
  }
9765
- if (object2.children.length > 0) {
9910
+ if (object3.children.length > 0) {
9766
9911
  let i = 1;
9767
- for (let child of object2.children) {
9912
+ for (let child of object3.children) {
9768
9913
  container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
9769
9914
  this.selectObject(child, intersection, instance);
9770
9915
  }));
9771
9916
  }
9772
9917
  }
9773
9918
  container.append(this.separator());
9774
- const selectedInstance = instance && (object2 === instance.instanceable || object2 === intersection?.object) ? instance : void 0;
9919
+ const selectedInstance = instance && (object3 === instance.instanceable || object3 === intersection?.object) ? instance : void 0;
9775
9920
  if (!selectedInstance) {
9776
- container.append(this.toggleControl("Visibility", "V", object2.visible, (v) => object2.visible = v));
9921
+ container.append(this.toggleControl("Visibility", "V", object3.visible, (v) => object3.visible = v));
9777
9922
  }
9778
9923
  container.append(this.separator("Mesh"));
9779
- this.addObjectControls(object2, intersection, container, selectedInstance);
9924
+ this.addObjectControls(object3, intersection, container, selectedInstance);
9780
9925
  container.append(this.separator());
9781
9926
  this.objectControlsContainer.append(container);
9782
9927
  }
@@ -9967,6 +10112,7 @@ var SceneStatsDisplay = class {
9967
10112
  appendTo(el) {
9968
10113
  el.append(this.statsContainer);
9969
10114
  }
10115
+ /** Stops the refresh timer and removes the panel from the page. */
9970
10116
  dispose() {
9971
10117
  clearInterval(this.interval);
9972
10118
  this.interval = void 0;
@@ -9991,6 +10137,7 @@ var InstanceManager = class {
9991
10137
  this.isInstanceManager = true;
9992
10138
  this.instanceCache = {};
9993
10139
  }
10140
+ /** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
9994
10141
  async get(key) {
9995
10142
  if (key in this.instanceCache) {
9996
10143
  console.debug(p9, "key in cache", key);
@@ -9998,6 +10145,7 @@ var InstanceManager = class {
9998
10145
  }
9999
10146
  return void 0;
10000
10147
  }
10148
+ /** Allocates a placement, calling the supplier once when the shared model is first needed. */
10001
10149
  async getOrCreate(key, supplier) {
10002
10150
  if (key in this.instanceCache) {
10003
10151
  return await this.get(key);
@@ -10014,6 +10162,7 @@ var InstanceManager = class {
10014
10162
  throw error;
10015
10163
  }
10016
10164
  }
10165
+ /** Forgets cached models without disposing them or releasing their existing placements. */
10017
10166
  reset() {
10018
10167
  for (let instanceCacheKey in this.instanceCache) {
10019
10168
  delete this.instanceCache[instanceCacheKey];
@@ -10059,6 +10208,7 @@ function matchesCondition(condition, state) {
10059
10208
  });
10060
10209
  }
10061
10210
  var BlockStateResolver = class {
10211
+ /** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
10062
10212
  static async defaults(blockState) {
10063
10213
  const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
10064
10214
  const state = {};
@@ -10078,9 +10228,11 @@ var BlockStateResolver = class {
10078
10228
  }
10079
10229
  return state;
10080
10230
  }
10231
+ /** Selects matching models. Supply `choose` to control selection within each alternatives array. */
10081
10232
  static select(blockState, state, choose = this.choose) {
10082
10233
  return this.matching(blockState, state).map(choose);
10083
10234
  }
10235
+ /** Returns matching model groups before choosing weighted alternatives. */
10084
10236
  static matching(blockState, state) {
10085
10237
  const out = [];
10086
10238
  if (blockState.variants) {
@@ -10104,6 +10256,7 @@ var BlockStateResolver = class {
10104
10256
  }
10105
10257
  return out;
10106
10258
  }
10259
+ /** Selects a random alternative using positive integer weights, each defaulting to 1. */
10107
10260
  static choose(variants) {
10108
10261
  if (!Array.isArray(variants)) return variants;
10109
10262
  if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
@@ -10121,6 +10274,7 @@ var BlockStateResolver = class {
10121
10274
  }
10122
10275
  return variants[variants.length - 1];
10123
10276
  }
10277
+ /** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
10124
10278
  static rotation(variant) {
10125
10279
  const rotation = new Euler8();
10126
10280
  if (typeof variant.x !== "undefined") {
@@ -10183,29 +10337,29 @@ var BlockTints = class {
10183
10337
  }
10184
10338
  }
10185
10339
  if (!indices.size) return tints;
10186
- let color;
10340
+ let color2;
10187
10341
  switch (rule.source) {
10188
10342
  case "constant":
10189
- color = rule.color;
10343
+ color2 = rule.color;
10190
10344
  break;
10191
10345
  case "grass":
10192
- color = await Colormaps.grassColor(key);
10346
+ color2 = await Colormaps.grassColor(key);
10193
10347
  break;
10194
10348
  case "redstone": {
10195
10349
  const power = Number(state.power ?? 0) / 15;
10196
10350
  const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
10197
10351
  const green = Math.max(0, power * power * 0.7 - 0.5);
10198
10352
  const blue = Math.max(0, power * power * 0.6 - 0.7);
10199
- color = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
10353
+ color2 = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
10200
10354
  break;
10201
10355
  }
10202
10356
  case "stem": {
10203
10357
  const age = Number(state.age ?? 0);
10204
- color = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
10358
+ color2 = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
10205
10359
  break;
10206
10360
  }
10207
10361
  }
10208
- return { ...Object.fromEntries([...indices].map((index) => [index, color])), ...tints };
10362
+ return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
10209
10363
  }
10210
10364
  };
10211
10365
 
@@ -10347,8 +10501,8 @@ var BlockObject = class _BlockObject extends SceneObject {
10347
10501
  return false;
10348
10502
  }
10349
10503
  return this._models.some((model) => {
10350
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10351
- return object2.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
10504
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10505
+ return object3.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
10352
10506
  });
10353
10507
  }
10354
10508
  getModelMatrix(model) {
@@ -10357,8 +10511,8 @@ var BlockObject = class _BlockObject extends SceneObject {
10357
10511
  return model.matrix.clone();
10358
10512
  }
10359
10513
  getModelCullMask(model, worldMask) {
10360
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10361
- if (object2.options.displayPosition) return 0;
10514
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10515
+ if (object3.options.displayPosition) return 0;
10362
10516
  const position = new Vector312();
10363
10517
  const rotation = new Quaternion5();
10364
10518
  const scale = new Vector312();
@@ -10375,14 +10529,14 @@ var BlockObject = class _BlockObject extends SceneObject {
10375
10529
  replacements.push(model);
10376
10530
  continue;
10377
10531
  }
10378
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10532
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10379
10533
  const cullMask = this.getModelCullMask(model, worldMask);
10380
- if (cullMask === (object2.options.cullMask ?? 0)) {
10534
+ if (cullMask === (object3.options.cullMask ?? 0)) {
10381
10535
  replacements.push(model);
10382
10536
  continue;
10383
10537
  }
10384
10538
  const matrix = this.getModelMatrix(model);
10385
- const replacement = await this.scene.addModel(object2.originalModel, { ...object2.options, cullMask });
10539
+ const replacement = await this.scene.addModel(object3.originalModel, { ...object3.options, cullMask });
10386
10540
  replacements.push(replacement);
10387
10541
  if (isInstanceReference(replacement)) {
10388
10542
  replacement.setMatrix(matrix);
@@ -10390,8 +10544,8 @@ var BlockObject = class _BlockObject extends SceneObject {
10390
10544
  replacement.matrix.copy(matrix);
10391
10545
  replacement.matrixWorldNeedsUpdate = true;
10392
10546
  matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
10393
- replacement.matrixAutoUpdate = object2.matrixAutoUpdate;
10394
- replacement.visible = object2.visible;
10547
+ replacement.matrixAutoUpdate = object3.matrixAutoUpdate;
10548
+ replacement.visible = object3.visible;
10395
10549
  }
10396
10550
  }
10397
10551
  } catch (error) {
@@ -10407,6 +10561,7 @@ var BlockObject = class _BlockObject extends SceneObject {
10407
10561
  }
10408
10562
  this.notifyDirty();
10409
10563
  }
10564
+ /** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
10410
10565
  get state() {
10411
10566
  return this._state;
10412
10567
  }
@@ -10506,6 +10661,7 @@ var BlockObject = class _BlockObject extends SceneObject {
10506
10661
  obj.setPosition(this.position);
10507
10662
  return obj;
10508
10663
  }
10664
+ /** Clears selected properties and rebuilds models without reapplying property defaults. */
10509
10665
  async resetState() {
10510
10666
  this._previousState = this._state;
10511
10667
  this._state = {};
@@ -12405,6 +12561,11 @@ function compoundList(tags, name) {
12405
12561
  // src/model/multiblock/StructureParser.ts
12406
12562
  init_MineRenderError();
12407
12563
  var StructureParser = class {
12564
+ /**
12565
+ * Parses decoded NBT, omitting air blocks.
12566
+ * @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
12567
+ * @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
12568
+ */
12408
12569
  static async parse(_nbt, paletteIndex) {
12409
12570
  const nbt = _nbt.value;
12410
12571
  let palette;
@@ -13239,6 +13400,7 @@ var SkinImage = class {
13239
13400
  // src/skin/SkinTextures.ts
13240
13401
  init_Textures();
13241
13402
  var SkinTextures = class {
13403
+ /** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
13242
13404
  static async get(src, legacy) {
13243
13405
  const image = await SkinImage.getData(src);
13244
13406
  const scale = image.width / 64;
@@ -13412,6 +13574,14 @@ var SkinObject = class extends SceneObject {
13412
13574
  }
13413
13575
  }
13414
13576
  }
13577
+ /**
13578
+ * Loads a skin texture, detecting slim arms and legacy layouts by default.
13579
+ * To override detection, set `slim` or `legacy` in the options passed to
13580
+ * `new SkinObject(options)` or `scene.addSkin(src, options)`.
13581
+ * Use {@link setSlim} or {@link setLegacy} to change these settings later.
13582
+ *
13583
+ * @param src - URL of the skin texture.
13584
+ */
13415
13585
  async setSkinTexture(src) {
13416
13586
  if (typeof src === "undefined") return;
13417
13587
  this.skinTextureSrc = src;
@@ -13522,59 +13692,60 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
13522
13692
  this.DEFAULT_OPTIONS = (0, import_ts_deepmerge8.default)({}, {});
13523
13693
  }
13524
13694
  add(...objects) {
13525
- for (const object2 of objects) {
13695
+ for (const object3 of objects) {
13526
13696
  this.dirty = true;
13527
13697
  try {
13528
- super.add(object2);
13698
+ super.add(object3);
13529
13699
  } finally {
13530
- if (object2?.isObject3D && object2 !== this) {
13531
- this.updateObjectRegistration(object2, object2.parent === this);
13700
+ if (object3?.isObject3D && object3 !== this) {
13701
+ this.updateObjectRegistration(object3, object3.parent === this);
13532
13702
  }
13533
13703
  }
13534
13704
  }
13535
13705
  return this;
13536
13706
  }
13537
13707
  remove(...objects) {
13538
- for (const object2 of objects) {
13539
- if (!this.children.includes(object2)) continue;
13708
+ for (const object3 of objects) {
13709
+ if (!this.children.includes(object3)) continue;
13540
13710
  this.dirty = true;
13541
- this.updateObjectRegistration(object2, false);
13711
+ this.updateObjectRegistration(object3, false);
13542
13712
  try {
13543
- super.remove(object2);
13713
+ super.remove(object3);
13544
13714
  } finally {
13545
- this.updateObjectRegistration(object2, object2.parent === this);
13715
+ this.updateObjectRegistration(object3, object3.parent === this);
13546
13716
  }
13547
13717
  }
13548
13718
  return this;
13549
13719
  }
13550
- updateObjectRegistration(object2, attached) {
13551
- if (this.observedObjects.has(object2) === attached) return;
13552
- const observable = object2;
13720
+ updateObjectRegistration(object3, attached) {
13721
+ if (this.observedObjects.has(object3) === attached) return;
13722
+ const observable = object3;
13553
13723
  if (attached) {
13554
- this.observedObjects.add(object2);
13724
+ this.observedObjects.add(object3);
13555
13725
  observable.addEventListener("change", this.onObjectChange);
13556
13726
  } else {
13557
- this.observedObjects.delete(object2);
13727
+ this.observedObjects.delete(object3);
13558
13728
  observable.removeEventListener("change", this.onObjectChange);
13559
13729
  }
13560
13730
  const change = attached ? 1 : -1;
13561
13731
  this.stats.objectCount += change;
13562
- if (isSceneObject(object2)) {
13732
+ if (isSceneObject(object3)) {
13563
13733
  this.stats.sceneObjectCount += change;
13564
13734
  }
13565
13735
  }
13566
- async initAndAdd(...object2) {
13736
+ /** Initializes each object before attaching it to the scene. */
13737
+ async initAndAdd(...object3) {
13567
13738
  this.dirty = true;
13568
- for (let obj of object2) {
13739
+ for (let obj of object3) {
13569
13740
  await obj.init();
13570
13741
  }
13571
- return this.add(...object2);
13742
+ return this.add(...object3);
13572
13743
  }
13573
- async addSceneObject(asset, objectSupplier, _options, parent = this) {
13744
+ async addSceneObject(asset2, objectSupplier, _options, parent = this) {
13574
13745
  this.dirty = true;
13575
13746
  const obj = await objectSupplier();
13576
- if (obj?.options?.instanceMeshes && asset.key && asset.key?.assetType === "models") {
13577
- let key = asset.key.serialize();
13747
+ if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
13748
+ let key = asset2.key.serialize();
13578
13749
  if (isModelObject(obj)) {
13579
13750
  const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
13580
13751
  if (displayPosition) key += `|display:${displayPosition}`;
@@ -13598,13 +13769,33 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
13598
13769
  return obj;
13599
13770
  }
13600
13771
  }
13772
+ /**
13773
+ * Adds a model loaded with {@link Models.getMerged}.
13774
+ * Set `options.instanceMeshes` to `false` to create a separate ModelObject.
13775
+ *
13776
+ * @param parent - Object to attach the model to. Defaults to this scene.
13777
+ * @returns The model, or a reference to one placement of a shared model.
13778
+ */
13601
13779
  async addModel(model, options, parent = this) {
13602
13780
  options = { ...options, tints: await ItemTints.get(model, options?.tints) };
13603
13781
  return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
13604
13782
  }
13783
+ /**
13784
+ * Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
13785
+ * Set `options.initialState` to choose block properties such as `facing` or `axis`.
13786
+ * Use the returned object's `setPosition` method to move all of its model parts.
13787
+ */
13605
13788
  async addBlock(blockState, options, parent = this) {
13606
13789
  return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
13607
13790
  }
13791
+ /**
13792
+ * Loads a skin and adds an initialized player model.
13793
+ *
13794
+ * @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
13795
+ * Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
13796
+ * @param options - Player settings, including `slim` and `legacy` detection overrides.
13797
+ * @param parent - Object to attach the player to. Defaults to this scene.
13798
+ */
13608
13799
  async addSkin(skin, options, parent = this) {
13609
13800
  this.dirty = true;
13610
13801
  const obj = new SkinObject(options);
@@ -13616,9 +13807,14 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
13616
13807
  parent.add(obj);
13617
13808
  return obj;
13618
13809
  }
13810
+ /** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
13619
13811
  async addEntity(entity, options, parent = this) {
13620
13812
  return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
13621
13813
  }
13814
+ /**
13815
+ * Adds GUI layers in draw order. One GUI pixel equals one scene unit.
13816
+ * Layer positions start at the top left, with positive GUI y pointing down.
13817
+ */
13622
13818
  async addGui(layers, options, parent = this) {
13623
13819
  const obj = new GuiObject(layers, options);
13624
13820
  obj.scene = this;
@@ -13655,11 +13851,11 @@ var _changeEvent = { type: "change" };
13655
13851
  var _startEvent = { type: "start" };
13656
13852
  var _endEvent = { type: "end" };
13657
13853
  var OrbitControls = class extends EventDispatcher {
13658
- constructor(object2, domElement) {
13854
+ constructor(object3, domElement) {
13659
13855
  super();
13660
13856
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
13661
13857
  if (domElement === document) console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
13662
- this.object = object2;
13858
+ this.object = object3;
13663
13859
  this.domElement = domElement;
13664
13860
  this.domElement.style.touchAction = "none";
13665
13861
  this.enabled = true;
@@ -13716,7 +13912,7 @@ var OrbitControls = class extends EventDispatcher {
13716
13912
  };
13717
13913
  this.update = function() {
13718
13914
  const offset = new Vector313();
13719
- const quat = new Quaternion6().setFromUnitVectors(object2.up, new Vector313(0, 1, 0));
13915
+ const quat = new Quaternion6().setFromUnitVectors(object3.up, new Vector313(0, 1, 0));
13720
13916
  const quatInverse = quat.clone().invert();
13721
13917
  const lastPosition = new Vector313();
13722
13918
  const lastQuaternion = new Quaternion6();
@@ -14281,8 +14477,8 @@ var OrbitControls = class extends EventDispatcher {
14281
14477
  }
14282
14478
  };
14283
14479
  var MapControls = class extends OrbitControls {
14284
- constructor(object2, domElement) {
14285
- super(object2, domElement);
14480
+ constructor(object3, domElement) {
14481
+ super(object3, domElement);
14286
14482
  this.screenSpacePanning = false;
14287
14483
  this.mouseButtons.LEFT = MOUSE.PAN;
14288
14484
  this.mouseButtons.RIGHT = MOUSE.ROTATE;
@@ -14481,6 +14677,7 @@ var Renderer = class _Renderer {
14481
14677
  get element() {
14482
14678
  return this._element;
14483
14679
  }
14680
+ /** Attaches the canvas to an element and sizes it to that element's dimensions. */
14484
14681
  appendTo(element) {
14485
14682
  if (this._disposed) return;
14486
14683
  this._element = element;
@@ -14508,6 +14705,7 @@ var Renderer = class _Renderer {
14508
14705
  events.add(changeEvent2);
14509
14706
  dispatcher.addEventListener(changeEvent2, this._changeListener);
14510
14707
  }
14708
+ /** Updates the camera and canvas size. Width and height are in CSS pixels. */
14511
14709
  resize(width, height) {
14512
14710
  if (this._disposed) return;
14513
14711
  if (isPerspectiveCamera(this.camera)) {
@@ -14546,12 +14744,14 @@ var Renderer = class _Renderer {
14546
14744
  if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
14547
14745
  };
14548
14746
  }
14747
+ /** Starts or resumes the animation loop and registered frame callbacks. */
14549
14748
  start() {
14550
14749
  if (this._disposed) return;
14551
14750
  this.stop();
14552
14751
  this._running = true;
14553
14752
  if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
14554
14753
  }
14754
+ /** Pauses rendering and frame callbacks. Call {@link start} to resume. */
14555
14755
  stop() {
14556
14756
  if (this._disposed) return;
14557
14757
  this._running = false;
@@ -14660,12 +14860,15 @@ var Renderer = class _Renderer {
14660
14860
  const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
14661
14861
  return canvas.toDataURL(mime, quality);
14662
14862
  }
14863
+ /** Exports visible scene meshes as OBJ text, without texture images. */
14663
14864
  toObj() {
14664
14865
  return SceneExporter.toObj(this.scene);
14665
14866
  }
14867
+ /** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
14666
14868
  toPLY(options) {
14667
14869
  return SceneExporter.toPLY(this.scene, options);
14668
14870
  }
14871
+ /** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
14669
14872
  toGLTF(options) {
14670
14873
  return SceneExporter.toGLTF(this.scene, options);
14671
14874
  }
@@ -14704,16 +14907,244 @@ init_SceneObject();
14704
14907
  // src/index.ts
14705
14908
  init_request();
14706
14909
 
14910
+ // src/scene/SceneDocument.ts
14911
+ init_DisplayPosition();
14912
+ function fail(path, message) {
14913
+ throw new Error(`${path}: ${message}`);
14914
+ }
14915
+ function object2(value, path) {
14916
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, "expected a JSON object");
14917
+ for (const key of Object.keys(value)) {
14918
+ if (["__proto__", "prototype", "constructor"].includes(key)) fail(`${path}.${key}`, "unsupported property");
14919
+ }
14920
+ return value;
14921
+ }
14922
+ function fields(value, path, allowed, required = []) {
14923
+ const input = object2(value, path);
14924
+ for (const key of required) if (input[key] === void 0) fail(`${path}.${key}`, "required");
14925
+ for (const [key, entry] of Object.entries(input)) {
14926
+ if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, "unsupported property");
14927
+ if (entry !== void 0) allowed[key](entry, `${path}.${key}`);
14928
+ }
14929
+ return input;
14930
+ }
14931
+ var text = (value, path) => {
14932
+ if (typeof value !== "string" || !value.trim()) fail(path, "expected a nonempty string");
14933
+ };
14934
+ var literalText = (value, path) => {
14935
+ if (typeof value !== "string") fail(path, "expected a string");
14936
+ };
14937
+ var boolean = (value, path) => {
14938
+ if (typeof value !== "boolean") fail(path, "expected a boolean");
14939
+ };
14940
+ var number = (value, path) => {
14941
+ if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number");
14942
+ };
14943
+ var positive = (value, path) => {
14944
+ number(value, path);
14945
+ if (value <= 0) fail(path, "expected a positive number");
14946
+ };
14947
+ var nonnegative = (value, path) => {
14948
+ number(value, path);
14949
+ if (value < 0) fail(path, "expected a nonnegative number");
14950
+ };
14951
+ var color = (value, path) => {
14952
+ number(value, path);
14953
+ if (!Number.isInteger(value) || value < 0 || value > 16777215) {
14954
+ fail(path, "expected an RGB integer from 0 to 16777215");
14955
+ }
14956
+ };
14957
+ var asset = (value, path) => {
14958
+ text(value, path);
14959
+ if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
14960
+ fail(path, "expected a resource ID such as minecraft:stone");
14961
+ }
14962
+ };
14963
+ var version = (value, path) => {
14964
+ text(value, path);
14965
+ if (!/^[a-zA-Z0-9._-]+$/.test(value)) fail(path, "expected a Minecraft version without slashes");
14966
+ };
14967
+ var tuple = (size, item = number) => (value, path) => {
14968
+ if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);
14969
+ for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);
14970
+ };
14971
+ var list = (item) => (value, path) => {
14972
+ if (!Array.isArray(value)) fail(path, "expected an array");
14973
+ for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);
14974
+ };
14975
+ var dictionary = (item, numericKeys = false) => (value, path) => {
14976
+ for (const [key, entry] of Object.entries(object2(value, path))) {
14977
+ if (numericKeys && !/^\d+$/.test(key)) fail(`${path}.${key}`, "expected a nonnegative tint index");
14978
+ item(entry, `${path}.${key}`);
14979
+ }
14980
+ };
14981
+ var enumeration = (values) => (value, path) => {
14982
+ if (!values.includes(value)) fail(path, `expected one of: ${values.join(", ")}`);
14983
+ };
14984
+ var textStyle = { color, bold: boolean, italic: boolean };
14985
+ function guiLayer(value, path) {
14986
+ const layer = object2(value, path);
14987
+ if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
14988
+ fail(path, "expected exactly one of texture, item, or text");
14989
+ }
14990
+ const layout = { name: text, position: tuple(2), size: tuple(2, positive) };
14991
+ if (layer.texture !== void 0) {
14992
+ fields(value, path, { ...layout, texture: asset, crop: (value2, path2) => {
14993
+ tuple(4)(value2, path2);
14994
+ const [x, y, width, height] = value2;
14995
+ nonnegative(x, `${path2}[0]`);
14996
+ nonnegative(y, `${path2}[1]`);
14997
+ positive(width, `${path2}[2]`);
14998
+ positive(height, `${path2}[3]`);
14999
+ } });
15000
+ } else if (layer.item !== void 0) {
15001
+ fields(value, path, { ...layout, item: asset, tints: dictionary(color, true) });
15002
+ } else {
15003
+ fields(value, path, {
15004
+ ...layout,
15005
+ ...textStyle,
15006
+ font: asset,
15007
+ shadow: boolean,
15008
+ maxWidth: positive,
15009
+ lineHeight: positive,
15010
+ text: (value2, path2) => {
15011
+ if (typeof value2 === "string") return;
15012
+ list((run, path3) => {
15013
+ fields(run, path3, { text: literalText, ...textStyle }, ["text"]);
15014
+ })(value2, path2);
15015
+ }
15016
+ });
15017
+ }
15018
+ }
15019
+ function validateObject(value, path) {
15020
+ const input = object2(value, path);
15021
+ const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };
15022
+ const required = ["id", "type"];
15023
+ const renderOptions = { wireframe: boolean };
15024
+ switch (input.type) {
15025
+ case "skin":
15026
+ fields(value, path, {
15027
+ ...common,
15028
+ skin: text,
15029
+ cape: (value2, path2) => {
15030
+ fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
15031
+ },
15032
+ hiddenParts: list(enumeration(SKIN_PARTS)),
15033
+ pose: (value2, path2) => {
15034
+ fields(value2, path2, {
15035
+ head: tuple(3),
15036
+ body: tuple(3),
15037
+ rightArm: tuple(3),
15038
+ leftArm: tuple(3),
15039
+ rightLeg: tuple(3),
15040
+ leftLeg: tuple(3),
15041
+ cape: tuple(3)
15042
+ });
15043
+ },
15044
+ options: (value2, path2) => {
15045
+ fields(value2, path2, { slim: boolean, legacy: boolean });
15046
+ }
15047
+ }, required);
15048
+ break;
15049
+ case "block":
15050
+ fields(value, path, {
15051
+ ...common,
15052
+ asset,
15053
+ state: dictionary(text),
15054
+ options: (value2, path2) => {
15055
+ fields(value2, path2, { ...renderOptions, tints: dictionary(color, true) });
15056
+ }
15057
+ }, [...required, "asset"]);
15058
+ break;
15059
+ case "item":
15060
+ case "model":
15061
+ fields(value, path, {
15062
+ ...common,
15063
+ asset,
15064
+ options: (value2, path2) => {
15065
+ fields(value2, path2, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) });
15066
+ }
15067
+ }, [...required, "asset"]);
15068
+ break;
15069
+ case "entity":
15070
+ fields(value, path, {
15071
+ ...common,
15072
+ asset,
15073
+ texture: asset,
15074
+ layers: list(text),
15075
+ when: list(text),
15076
+ textures: dictionary(asset),
15077
+ options: (value2, path2) => {
15078
+ fields(value2, path2, {
15079
+ ...renderOptions,
15080
+ flip: boolean,
15081
+ tints: dictionary((value3, path3) => {
15082
+ typeof value3 === "string" ? text(value3, path3) : color(value3, path3);
15083
+ })
15084
+ });
15085
+ },
15086
+ animation: (value2, path2) => {
15087
+ fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
15088
+ }
15089
+ }, [...required, "asset"]);
15090
+ if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
15091
+ break;
15092
+ case "gui":
15093
+ fields(value, path, { ...common, layers: list(guiLayer) }, [...required, "layers"]);
15094
+ break;
15095
+ default:
15096
+ fail(`${path}.type`, "expected skin, block, item, entity, model, or gui");
15097
+ }
15098
+ }
15099
+ function parseSceneDocument(value) {
15100
+ if (typeof value === "string") {
15101
+ try {
15102
+ value = JSON.parse(value);
15103
+ } catch {
15104
+ fail("scene", "invalid JSON");
15105
+ }
15106
+ }
15107
+ const input = fields(value, "scene", {
15108
+ format: enumeration(["minerender-scene"]),
15109
+ version: (value2, path) => {
15110
+ if (value2 !== 1) fail(path, "only scene document version 1 is supported");
15111
+ },
15112
+ minecraftVersion: version,
15113
+ camera: (value2, path) => {
15114
+ fields(value2, path, { position: tuple(3), target: tuple(3) }, ["position", "target"]);
15115
+ },
15116
+ objects: list(validateObject)
15117
+ }, ["format", "version", "objects"]);
15118
+ const ids = /* @__PURE__ */ new Set();
15119
+ for (const [index, entry] of input.objects.entries()) {
15120
+ if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID "${entry.id}"`);
15121
+ ids.add(entry.id);
15122
+ }
15123
+ return JSON.parse(JSON.stringify(input));
15124
+ }
15125
+
15126
+ // src/scene/SceneDocumentLoader.ts
15127
+ init_AssetKey();
15128
+ init_AssetLoader();
15129
+ init_BlockStates();
15130
+ init_Entities();
15131
+ init_Models();
15132
+ init_DisplayPosition();
15133
+ import { Group as Group2 } from "three";
15134
+ init_SceneObject();
15135
+
14707
15136
  // src/skin/Skins.ts
14708
15137
  init_Requests();
14709
15138
  var Skins = class {
14710
15139
  //<editor-fold desc="Skins">
15140
+ /** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
14711
15141
  static async fromUuidOrUsername(uuidOrUsername) {
14712
15142
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14713
15143
  return this.fromUuid(uuidOrUsername);
14714
15144
  }
14715
15145
  return this.fromUsername(uuidOrUsername);
14716
15146
  }
15147
+ /** Builds a proxy URL for a UUID without downloading or checking the skin. */
14717
15148
  static async fromUuid(uuid) {
14718
15149
  return this.getMcProxySkinUrl(uuid);
14719
15150
  }
@@ -14725,6 +15156,7 @@ var Skins = class {
14725
15156
  return void 0;
14726
15157
  });
14727
15158
  }
15159
+ /** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
14728
15160
  static async fromMineSkin(mineskinId) {
14729
15161
  return this.getMineSkin(mineskinId).then((mineskin) => {
14730
15162
  if (mineskin) {
@@ -14735,12 +15167,14 @@ var Skins = class {
14735
15167
  }
14736
15168
  //</editor-fold>
14737
15169
  //<editor-fold desc="Capes">
15170
+ /** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
14738
15171
  static async capeFromUuidOrUsername(uuidOrUsername) {
14739
15172
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14740
15173
  return this.capeFromUuid(uuidOrUsername);
14741
15174
  }
14742
15175
  return this.capeFromUsername(uuidOrUsername);
14743
15176
  }
15177
+ /** Builds a proxy URL without checking whether the player has a cape. */
14744
15178
  static async capeFromUuid(uuid) {
14745
15179
  return this.getMcProxyCapeUrl(uuid);
14746
15180
  }
@@ -14869,6 +15303,195 @@ var Skins = class {
14869
15303
  //</editor-fold>
14870
15304
  };
14871
15305
 
15306
+ // src/scene/SceneDocumentLoader.ts
15307
+ var DocumentObjectScene = class extends MineRenderScene {
15308
+ async addSceneObject(_asset, supplier, _options, parent = this) {
15309
+ const object3 = await supplier();
15310
+ return this.initialize(object3, parent);
15311
+ }
15312
+ async initialize(object3, parent = this) {
15313
+ object3.scene = this;
15314
+ try {
15315
+ await object3.init();
15316
+ parent.add(object3);
15317
+ return object3;
15318
+ } catch (error) {
15319
+ object3.dispose();
15320
+ object3.removeFromParent();
15321
+ throw error;
15322
+ }
15323
+ }
15324
+ dispose() {
15325
+ for (const child of [...this.children]) {
15326
+ if (child.parent !== this) continue;
15327
+ if (isSceneObject(child)) child.dispose();
15328
+ child.removeFromParent();
15329
+ }
15330
+ }
15331
+ };
15332
+ async function skinTexture(value, cape = false) {
15333
+ if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;
15334
+ const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));
15335
+ if (!texture) throw new Error(`Could not find ${cape ? "cape" : "skin"} for "${value}"`);
15336
+ return texture;
15337
+ }
15338
+ var SceneDocumentLoader = class {
15339
+ static parse(value) {
15340
+ return parseSceneDocument(value);
15341
+ }
15342
+ /**
15343
+ * Loads objects concurrently and attaches them in document order after every load succeeds.
15344
+ * A failure waits for all loads, disposes staged objects, and reports the first error in document order.
15345
+ */
15346
+ static async load(scene, value, parent = scene) {
15347
+ const definition = this.parse(value);
15348
+ if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
15349
+ throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
15350
+ }
15351
+ const root = new Group2();
15352
+ root.name = "MineRender scene document";
15353
+ const objects = [];
15354
+ let disposed = false;
15355
+ const loaded = {
15356
+ root,
15357
+ objects,
15358
+ definition,
15359
+ advanceAnimations: (delta) => {
15360
+ if (!disposed) for (const object3 of objects) object3.advanceAnimation(delta);
15361
+ },
15362
+ dispose: () => {
15363
+ if (disposed) return;
15364
+ disposed = true;
15365
+ root.removeFromParent();
15366
+ for (const object3 of objects) object3.dispose();
15367
+ root.clear();
15368
+ scene.dirty = true;
15369
+ }
15370
+ };
15371
+ try {
15372
+ const results = await Promise.allSettled(definition.objects.map((object3) => this.loadValidatedObject(scene, object3)));
15373
+ for (const result of results) if (result.status === "fulfilled") objects.push(result.value);
15374
+ const failure = results.find((result) => result.status === "rejected");
15375
+ if (failure) throw failure.reason;
15376
+ for (const object3 of objects) root.add(object3.root);
15377
+ parent.add(root);
15378
+ scene.dirty = true;
15379
+ return loaded;
15380
+ } catch (error) {
15381
+ loaded.dispose();
15382
+ throw error;
15383
+ }
15384
+ }
15385
+ /** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */
15386
+ static async loadObject(scene, value, parent = scene) {
15387
+ const definition = this.parse({ format: "minerender-scene", version: 1, objects: [value] }).objects[0];
15388
+ return this.loadValidatedObject(scene, definition, parent);
15389
+ }
15390
+ static async loadValidatedObject(scene, definition, parent) {
15391
+ const root = new Group2();
15392
+ root.name = definition.name ?? definition.id;
15393
+ root.userData.sceneObjectId = definition.id;
15394
+ root.position.fromArray(definition.position ?? [0, 0, 0]);
15395
+ root.rotation.set(...definition.rotation ?? [0, 0, 0]);
15396
+ root.scale.fromArray(definition.scale ?? [1, 1, 1]);
15397
+ root.visible = definition.visible ?? true;
15398
+ const content = new DocumentObjectScene();
15399
+ root.add(content);
15400
+ let object3;
15401
+ try {
15402
+ switch (definition.type) {
15403
+ case "skin": {
15404
+ const skin = new SkinObject({ ...definition.options, instanceMeshes: false });
15405
+ object3 = skin;
15406
+ const texture = definition.skin ? await skinTexture(definition.skin) : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? "slim/alex" : "wide/steve"}.png`;
15407
+ await skin.setSkinTexture(texture);
15408
+ await content.initialize(skin);
15409
+ if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
15410
+ for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
15411
+ skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
15412
+ }
15413
+ for (const part of definition.hiddenParts ?? []) {
15414
+ const mesh = skin.getMeshByName(part);
15415
+ if (mesh) mesh.visible = false;
15416
+ }
15417
+ break;
15418
+ }
15419
+ case "block": {
15420
+ const state = await BlockStates.get(AssetKey.parse("blockstates", definition.asset));
15421
+ if (!state) throw new Error(`Could not load block ${definition.asset}`);
15422
+ object3 = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false });
15423
+ break;
15424
+ }
15425
+ case "item":
15426
+ case "model": {
15427
+ let key = AssetKey.parse("models", definition.asset);
15428
+ if (definition.type === "item" && key.type !== "item") {
15429
+ key = new AssetKey(key.namespace, key.getFullPath(), "models", "item");
15430
+ }
15431
+ const model = await Models.getMerged(key);
15432
+ if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);
15433
+ object3 = await content.addModel(model, {
15434
+ ...definition.type === "item" ? { displayPosition: "gui" /* GUI */ } : {},
15435
+ ...definition.options,
15436
+ instanceMeshes: false
15437
+ });
15438
+ break;
15439
+ }
15440
+ case "entity": {
15441
+ const key = AssetKey.parse("entity-models", definition.asset);
15442
+ const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse("textures", definition.texture) : void 0, {
15443
+ layers: definition.layers,
15444
+ when: definition.when,
15445
+ textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse("textures", texture)]))
15446
+ });
15447
+ if (!model) throw new Error(`Could not load entity ${definition.asset}`);
15448
+ const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
15449
+ object3 = entity;
15450
+ if (definition.animation) {
15451
+ const animations = await Entities.getAnimations(key);
15452
+ const clip = animations?.[definition.animation.name];
15453
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
15454
+ entity.playAnimation(clip, definition.animation);
15455
+ }
15456
+ break;
15457
+ }
15458
+ case "gui":
15459
+ object3 = await content.initialize(new GuiObject(definition.layers));
15460
+ break;
15461
+ }
15462
+ let disposed = false;
15463
+ const loaded = {
15464
+ root,
15465
+ object: object3,
15466
+ definition,
15467
+ advanceAnimation: (delta) => {
15468
+ if (!disposed && definition.type === "entity" && !definition.animation?.paused) object3.advanceAnimation(delta);
15469
+ },
15470
+ dispose: () => {
15471
+ if (disposed) return;
15472
+ disposed = true;
15473
+ root.removeFromParent();
15474
+ object3.dispose();
15475
+ object3.removeFromParent();
15476
+ content.dispose();
15477
+ root.clear();
15478
+ scene.dirty = true;
15479
+ }
15480
+ };
15481
+ parent?.add(root);
15482
+ scene.dirty = true;
15483
+ return loaded;
15484
+ } catch (error) {
15485
+ object3?.dispose();
15486
+ object3?.removeFromParent();
15487
+ content.dispose();
15488
+ root.removeFromParent();
15489
+ root.clear();
15490
+ throw new Error(`Scene object "${definition.name ?? definition.id}": ${error instanceof Error ? error.message : String(error)}`);
15491
+ }
15492
+ }
15493
+ };
15494
+
14872
15495
  // src/texture/index.ts
14873
15496
  init_Colormaps();
14874
15497
  init_TextureAtlas();
@@ -14895,16 +15518,19 @@ var ChunkData = class _ChunkData {
14895
15518
  this.freeIds = [];
14896
15519
  this.nbt = /* @__PURE__ */ new Map();
14897
15520
  }
15521
+ /** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
14898
15522
  get(index) {
14899
15523
  _ChunkData.validateIndex(index);
14900
15524
  const entry = this.palette[this.indices[index]];
14901
15525
  return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
14902
15526
  }
15527
+ /** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
14903
15528
  snapshot(index) {
14904
15529
  _ChunkData.validateIndex(index);
14905
15530
  const entry = this.palette[this.indices[index]];
14906
15531
  return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
14907
15532
  }
15533
+ /** Copies block data into a cell. An omitted block or an air block clears the cell. */
14908
15534
  set(index, block) {
14909
15535
  _ChunkData.validateIndex(index);
14910
15536
  let state;
@@ -14969,15 +15595,23 @@ var ChunkData = class _ChunkData {
14969
15595
 
14970
15596
  // src/world/AnvilParser.ts
14971
15597
  var AnvilParser = class {
15598
+ /** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
14972
15599
  static getChunkList(data) {
14973
15600
  return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
14974
15601
  }
15602
+ /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
14975
15603
  static async parse(data) {
14976
15604
  const bytes = this.bytes(data);
14977
15605
  const chunks = [];
14978
15606
  for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
14979
15607
  return { chunks };
14980
15608
  }
15609
+ /**
15610
+ * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
15611
+ * @param localX - Chunk x within the region, from 0 to 31.
15612
+ * @param localZ - Chunk z within the region, from 0 to 31.
15613
+ * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
15614
+ */
14981
15615
  static async parseChunk(data, localX, localZ) {
14982
15616
  if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
14983
15617
  throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
@@ -15182,6 +15816,7 @@ var SectionModels = class {
15182
15816
  this.templates = /* @__PURE__ */ new Set();
15183
15817
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
15184
15818
  }
15819
+ /** Returns a cube template, or `undefined` when the block requires an individual render object. */
15185
15820
  async get(blockState, properties = {}) {
15186
15821
  if (!blockState.variants || blockState.multipart) return void 0;
15187
15822
  let states = this.states.get(blockState);
@@ -15210,16 +15845,16 @@ var SectionModels = class {
15210
15845
  if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
15211
15846
  const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
15212
15847
  const template = await cached(models, key, async () => {
15213
- const object2 = new SectionModel(model, {
15848
+ const object3 = new SectionModel(model, {
15214
15849
  instanceMeshes: false,
15215
15850
  mergeMeshes: false,
15216
15851
  tints,
15217
15852
  uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
15218
15853
  });
15219
- await object2.init();
15220
- const geometry = object2.children[0].geometry;
15854
+ await object3.init();
15855
+ const geometry = object3.children[0].geometry;
15221
15856
  geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
15222
- object2.clear();
15857
+ object3.clear();
15223
15858
  const cullFaces = CUBE_FACES.map((face) => {
15224
15859
  const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
15225
15860
  if (local < 0) return 0;
@@ -15235,6 +15870,7 @@ var SectionModels = class {
15235
15870
  }
15236
15871
  return { variants, templates };
15237
15872
  }
15873
+ /** Disposes prepared geometry and clears this world's template caches. */
15238
15874
  clear() {
15239
15875
  for (const template of this.templates) template.geometry.dispose();
15240
15876
  this.templates.clear();
@@ -15285,6 +15921,13 @@ var MineRenderWorld = class {
15285
15921
  this.validatePosBounds(posOrX);
15286
15922
  await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
15287
15923
  }
15924
+ /**
15925
+ * Places a structure's blocks at their stored positions, then refreshes neighboring faces.
15926
+ * Entity NBT is retained by the structure but is not rendered.
15927
+ *
15928
+ * @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
15929
+ * @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
15930
+ */
15288
15931
  async placeMultiBlock(multiblock, useBatches = true, executor) {
15289
15932
  const changes = /* @__PURE__ */ new Map();
15290
15933
  try {
@@ -15381,6 +16024,7 @@ var MineRenderWorld = class {
15381
16024
  });
15382
16025
  }
15383
16026
  }
16027
+ /** Removes all chunks and releases their block placements and section meshes. */
15384
16028
  async clear() {
15385
16029
  await this.culling;
15386
16030
  const chunks = [...this._chunks.values()];
@@ -15426,10 +16070,10 @@ var MineRenderWorld = class {
15426
16070
  await block.object.updateFluid((x, y, z) => {
15427
16071
  const neighbor = pos.clone().add(new Vector316(x, y, z));
15428
16072
  const section = this.getChunkAt(neighbor);
15429
- const object2 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
16073
+ const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
15430
16074
  return {
15431
- fluid: object2?.fluidKind,
15432
- level: object2?.fluidLevel,
16075
+ fluid: object3?.fluidKind,
16076
+ level: object3?.fluidLevel,
15433
16077
  solid: section?.isOccludingAt(neighbor) ?? false
15434
16078
  };
15435
16079
  });
@@ -15466,9 +16110,11 @@ var MineRenderWorld = class {
15466
16110
  }
15467
16111
  return chunk;
15468
16112
  }
16113
+ /** Returns the loaded chunk containing a world block position, without creating a chunk. */
15469
16114
  getChunkAt(pos) {
15470
16115
  return this._chunks.get(this.worldPosToChunkKey(pos));
15471
16116
  }
16117
+ /** Converts block coordinates to scene units by multiplying each component by 16. */
15472
16118
  static worldToScenePosition(pos) {
15473
16119
  return new Vector316(
15474
16120
  pos.x * 16,
@@ -15476,6 +16122,7 @@ var MineRenderWorld = class {
15476
16122
  pos.z * 16
15477
16123
  );
15478
16124
  }
16125
+ /** Converts scene units to block coordinates without rounding to an integer block. */
15479
16126
  static sceneToWorldPosition(pos) {
15480
16127
  return new Vector316(
15481
16128
  pos.x / 16,
@@ -15502,12 +16149,13 @@ init_Model();
15502
16149
  // src/world/SectionMesh.ts
15503
16150
  init_CanvasCompat();
15504
16151
  init_Materials();
15505
- import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group2, Mesh as Mesh9 } from "three";
15506
- var SectionMesh = class _SectionMesh extends Group2 {
16152
+ import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9 } from "three";
16153
+ var SectionMesh = class _SectionMesh extends Group3 {
15507
16154
  constructor() {
15508
16155
  super(...arguments);
15509
16156
  this.ownedMeshes = [];
15510
16157
  }
16158
+ /** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
15511
16159
  static build(entries, maxAtlasSize = 2048) {
15512
16160
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
15513
16161
  const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
@@ -15555,7 +16203,7 @@ var SectionMesh = class _SectionMesh extends Group2 {
15555
16203
  const normal = template.geometry.getAttribute("normal");
15556
16204
  const uv = template.geometry.getAttribute("uv");
15557
16205
  const bounds = template.geometry.getAttribute("uvBounds");
15558
- const color = template.geometry.getAttribute("color");
16206
+ const color2 = template.geometry.getAttribute("color");
15559
16207
  const sourceIndices = template.geometry.getIndex();
15560
16208
  const placement = locations.get(template.atlas).placement;
15561
16209
  const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
@@ -15577,7 +16225,7 @@ var SectionMesh = class _SectionMesh extends Group2 {
15577
16225
  mapU(bounds?.getZ(vertex) ?? 1),
15578
16226
  mapV(bounds?.getW(vertex) ?? 1)
15579
16227
  );
15580
- colors.push(color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1);
16228
+ colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
15581
16229
  }
15582
16230
  for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
15583
16231
  }
@@ -15652,7 +16300,7 @@ var Chunk = class _Chunk {
15652
16300
  return this.setBlockInChunkAt(pos, block, worldPos);
15653
16301
  }
15654
16302
  /**
15655
- * Set a block at integer chunk-local coordinates from 0 to 15.
16303
+ * Places a block at integer chunk-local coordinates from 0 to 15.
15656
16304
  */
15657
16305
  async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
15658
16306
  if (typeof worldPos === "undefined") {
@@ -15665,7 +16313,7 @@ var Chunk = class _Chunk {
15665
16313
  this.renderedBlocks.get(index)?.object?.removeFromScene();
15666
16314
  if (this.sectionBlocks.delete(index)) this.meshDirty = true;
15667
16315
  this.renderedBlocks.delete(index);
15668
- let object2;
16316
+ let object3;
15669
16317
  try {
15670
16318
  if (!readBlock) return void 0;
15671
16319
  const stored = readBlock();
@@ -15680,27 +16328,28 @@ var Chunk = class _Chunk {
15680
16328
  this.sectionBlocks.set(index, { index, template, cullMask: 0 });
15681
16329
  this.meshDirty = true;
15682
16330
  } else {
15683
- object2 = await this.scene.addBlock(blockState, {
16331
+ object3 = await this.scene.addBlock(blockState, {
15684
16332
  mergeMeshes: true,
15685
16333
  instanceMeshes: true,
15686
16334
  maxInstanceCount: 2e3,
15687
16335
  initialState: stored.properties
15688
16336
  });
15689
- object2.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
16337
+ object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
15690
16338
  }
15691
16339
  const info = { get block() {
15692
16340
  return readBlock();
15693
- }, object: object2 };
16341
+ }, object: object3 };
15694
16342
  this.renderedBlocks.set(index, info);
15695
16343
  return info;
15696
16344
  } catch (error) {
15697
16345
  this.data.set(index, void 0);
15698
- object2?.removeFromScene();
16346
+ object3?.removeFromScene();
15699
16347
  throw error;
15700
16348
  } finally {
15701
16349
  await onBlocksChanged?.([worldPos]);
15702
16350
  }
15703
16351
  }
16352
+ /** Changes visibility at world block coordinates while preserving the block's data. */
15704
16353
  async setBlockVisibleAt(pos, visible) {
15705
16354
  const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
15706
16355
  const block = this.renderedBlocks.get(index);
@@ -15754,6 +16403,7 @@ var Chunk = class _Chunk {
15754
16403
  this.scene.dirty = true;
15755
16404
  this.meshDirty = false;
15756
16405
  }
16406
+ /** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
15757
16407
  async clear(onBlocksChanged = this.onBlocksChanged) {
15758
16408
  const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
15759
16409
  new Vector317(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
@@ -15774,6 +16424,7 @@ var Chunk = class _Chunk {
15774
16424
  static isAir(block) {
15775
16425
  return ChunkData.isAir(block);
15776
16426
  }
16427
+ /** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
15777
16428
  static chunkPosToBlockIndex(pos) {
15778
16429
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
15779
16430
  throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
@@ -15875,6 +16526,7 @@ export {
15875
16526
  RequestError,
15876
16527
  Requests,
15877
16528
  SKIN_PARTS,
16529
+ SceneDocumentLoader,
15878
16530
  SceneExporter,
15879
16531
  SceneInspector,
15880
16532
  SceneObject,
@@ -15950,6 +16602,7 @@ export {
15950
16602
  mergeBufferAttributes,
15951
16603
  mergeBufferGeometries,
15952
16604
  mergeVertices,
16605
+ parseSceneDocument,
15953
16606
  prefix,
15954
16607
  registerNodeEnv,
15955
16608
  sampleEntityAnimation,