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.
@@ -64,6 +64,7 @@ var init_PersistentCache = __esm({
64
64
  constructor(backing) {
65
65
  this.backing = backing;
66
66
  }
67
+ /** Opens a named store in the active environment using the current cache schema version. */
67
68
  static open(name) {
68
69
  return Env.provider.openCache(name, _PersistentCache.VERSION);
69
70
  }
@@ -262,6 +263,7 @@ var init_Caching = __esm({
262
263
  this.fontCache
263
264
  ];
264
265
  }
266
+ /** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
265
267
  static clear() {
266
268
  for (const cache of this.all) {
267
269
  cache.invalidateAll();
@@ -327,6 +329,7 @@ var init_Geometries = __esm({
327
329
  }
328
330
  return geometry;
329
331
  }
332
+ /** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
330
333
  static getBox(key) {
331
334
  const keyStr = serializeBoxGeometryKey(key);
332
335
  return Caching.boxGeometryCache.get(keyStr, (k) => {
@@ -504,12 +507,15 @@ var init_Requests = __esm({
504
507
  }
505
508
  };
506
509
  Requests = class {
510
+ /** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
507
511
  static genericRequest(request) {
508
512
  return this.genericQueue.request(request);
509
513
  }
514
+ /** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
510
515
  static mcAssetRequest(request) {
511
516
  return this.mcAssetRequestQueue.request(request);
512
517
  }
518
+ /** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
513
519
  static setMcAssetRoot(root) {
514
520
  this.mcAssetRequestQueue.baseURL = root;
515
521
  }
@@ -564,6 +570,7 @@ var init_WrappedImage = __esm({
564
570
  }
565
571
  return false;
566
572
  }
573
+ /** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
567
574
  get animated() {
568
575
  return this.height > this.width && this.height % this.width === 0;
569
576
  }
@@ -610,6 +617,7 @@ var init_ImageLoader = __esm({
610
617
  static _createImage() {
611
618
  return createImage();
612
619
  }
620
+ /** Resolves when an image URL has decoded, or rejects if decoding fails. */
613
621
  static async loadAsync(src) {
614
622
  return new Promise((resolve, reject) => {
615
623
  const image = this._createImage();
@@ -618,6 +626,7 @@ var init_ImageLoader = __esm({
618
626
  image.src = src;
619
627
  });
620
628
  }
629
+ /** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
621
630
  static loadElement(src, onload, onerr) {
622
631
  const image = this._createImage();
623
632
  if (onload)
@@ -645,6 +654,7 @@ var init_ImageLoader = __esm({
645
654
  throw err;
646
655
  }
647
656
  }
657
+ /** Decodes the supplied bytes into a canvas without fetching the source URL again. */
648
658
  static async infoToCanvasData(info) {
649
659
  const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
650
660
  const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
@@ -661,12 +671,14 @@ var init_ImageLoader = __esm({
661
671
  const image = await this.infoToCanvasData(info);
662
672
  return image.data.getImageData(0, 0, image.width, image.height);
663
673
  }
674
+ /** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
664
675
  static async getData(src) {
665
676
  const keyStr = serializeImageKey({ src });
666
677
  return await Caching.imageDataCache.get(keyStr, (k) => {
667
678
  return _ImageLoader.loadData(src);
668
679
  });
669
680
  }
681
+ /** Returns a cached canvas context and dimensions for an image URL or data URL. */
670
682
  static async getCanvasData(src) {
671
683
  const keyStr = serializeImageKey({ src });
672
684
  return await Caching.canvasImageDataCache.get(keyStr, (k) => {
@@ -763,6 +775,7 @@ var init_TextureLoader = __esm({
763
775
  texture.rotation = rotation;
764
776
  return Textures.initTextureProps(texture);
765
777
  }
778
+ /** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
766
779
  static load(src, format2 = RGBAFormat, rotation = 0) {
767
780
  const texture = new Texture();
768
781
  texture.colorSpace = THREE.SRGBColorSpace;
@@ -800,8 +813,8 @@ var init_Textures = __esm({
800
813
  for (let y = 0; y < size; y++) {
801
814
  for (let x = 0; x < size; x++) {
802
815
  const offset = (y * size + x) * 4;
803
- const color = (x + y) % 2 === 0 ? 255 : 0;
804
- data[offset] = data[offset + 2] = color;
816
+ const color2 = (x + y) % 2 === 0 ? 255 : 0;
817
+ data[offset] = data[offset + 2] = color2;
805
818
  data[offset + 3] = 255;
806
819
  }
807
820
  }
@@ -812,6 +825,7 @@ var init_Textures = __esm({
812
825
  return this.initTextureProps(texture);
813
826
  });
814
827
  }
828
+ /** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
815
829
  static initTextureProps(texture) {
816
830
  texture.magFilter = THREE2.NearestFilter;
817
831
  texture.minFilter = THREE2.NearestFilter;
@@ -824,11 +838,13 @@ var init_Textures = __esm({
824
838
  static createAsset(key) {
825
839
  return new Texture2();
826
840
  }
841
+ /** Creates an sRGB color texture from a canvas. */
827
842
  static createCanvasTexture(canvas) {
828
843
  const texture = new CanvasTexture(canvas);
829
844
  texture.colorSpace = THREE2.SRGBColorSpace;
830
845
  return this.initTextureProps(texture);
831
846
  }
847
+ /** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
832
848
  static getImage(key) {
833
849
  const keyStr = serializeTextureKey(key);
834
850
  const cached2 = Caching.textureCache.peek(keyStr);
@@ -853,6 +869,7 @@ var init_Materials = __esm({
853
869
  init_TextureLoader();
854
870
  init_Caching();
855
871
  Materials = class _Materials {
872
+ /** Shared checkerboard material, created on first access. */
856
873
  static get MISSING_TEXTURE() {
857
874
  return Caching.materialCache.get("builtin:missing-texture", () => new MeshBasicMaterial({
858
875
  map: Textures.getMissing(),
@@ -956,6 +973,7 @@ var init_Materials = __esm({
956
973
  if (tint !== void 0) material.color.multiply(new Color(tint));
957
974
  return material;
958
975
  }
976
+ /** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
959
977
  static createGuiCanvasMaterial(canvas) {
960
978
  const material = new MeshBasicMaterial({
961
979
  map: Textures.createCanvasTexture(canvas),
@@ -1122,6 +1140,7 @@ var init_Materials = __esm({
1122
1140
  }
1123
1141
  return this.createBasicCanvasMaterial(canvas, transparent, shade);
1124
1142
  }
1143
+ /** Returns a shared image material. Use {@link createImage} for a separate material instance. */
1125
1144
  static getImage(key) {
1126
1145
  const keyStr = serializeMaterialKey(key);
1127
1146
  const map = Caching.materialCache.peek(keyStr)?.map;
@@ -1296,6 +1315,14 @@ var init_AssetKey = __esm({
1296
1315
  }
1297
1316
  };
1298
1317
  AssetKey = class _AssetKey extends BasicAssetKey {
1318
+ /**
1319
+ * Creates a key from separate path components. Use {@link parse} for a combined reference.
1320
+ *
1321
+ * @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
1322
+ * @param type - Subdirectory, such as `block` or `item`.
1323
+ * @param rootType - Top-level directory, usually `assets` or `data`.
1324
+ * @param root - Base URL override for hosted asset sources.
1325
+ */
1299
1326
  constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
1300
1327
  super(namespace, path);
1301
1328
  this.namespace = namespace;
@@ -1307,6 +1334,16 @@ var init_AssetKey = __esm({
1307
1334
  this.root = root;
1308
1335
  this.isAssetKey = true;
1309
1336
  }
1337
+ /**
1338
+ * Parses an asset reference such as `minecraft:block/stone`.
1339
+ * A missing namespace defaults to the origin's namespace or `minecraft`.
1340
+ *
1341
+ * @param assetType - Asset category, such as `models` or `textures`.
1342
+ * @param str - Path with an optional namespace and file extension.
1343
+ * @param origin - Supplies the asset root, default namespace, and non-texture extension.
1344
+ * @returns A key with the matching extension removed from its path. Textures use `.png`;
1345
+ * other assets use the origin's extension or `.json`.
1346
+ */
1310
1347
  static parse(assetType, str, origin) {
1311
1348
  let namespace = origin?.namespace || DEFAULT_NAMESPACE;
1312
1349
  if (str.includes(":")) {
@@ -1338,6 +1375,7 @@ var init_AssetKey = __esm({
1338
1375
  ];
1339
1376
  return a.join("/");
1340
1377
  }
1378
+ /** Joins the type subdirectory and path without adding a namespace or file extension. */
1341
1379
  getFullPath() {
1342
1380
  let p10 = [];
1343
1381
  if (this.type) {
@@ -1349,6 +1387,7 @@ var init_AssetKey = __esm({
1349
1387
  toNamespacedString() {
1350
1388
  return this.namespace + ":" + this.getFullPath();
1351
1389
  }
1390
+ /** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
1352
1391
  serialize() {
1353
1392
  return this.toString();
1354
1393
  }
@@ -1360,17 +1399,17 @@ var init_AssetKey = __esm({
1360
1399
  var DisplayPosition, DISPLAY_POSITIONS;
1361
1400
  var init_DisplayPosition = __esm({
1362
1401
  "src/model/DisplayPosition.ts"() {
1363
- DisplayPosition = /* @__PURE__ */ ((DisplayPosition2) => {
1364
- DisplayPosition2["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1365
- DisplayPosition2["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1366
- DisplayPosition2["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1367
- DisplayPosition2["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1368
- DisplayPosition2["GUI"] = "gui";
1369
- DisplayPosition2["HEAD"] = "head";
1370
- DisplayPosition2["GROUND"] = "ground";
1371
- DisplayPosition2["FIXED"] = "fixed";
1372
- DisplayPosition2["ON_SHELF"] = "on_shelf";
1373
- return DisplayPosition2;
1402
+ DisplayPosition = /* @__PURE__ */ ((DisplayPosition3) => {
1403
+ DisplayPosition3["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1404
+ DisplayPosition3["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1405
+ DisplayPosition3["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1406
+ DisplayPosition3["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1407
+ DisplayPosition3["GUI"] = "gui";
1408
+ DisplayPosition3["HEAD"] = "head";
1409
+ DisplayPosition3["GROUND"] = "ground";
1410
+ DisplayPosition3["FIXED"] = "fixed";
1411
+ DisplayPosition3["ON_SHELF"] = "on_shelf";
1412
+ return DisplayPosition3;
1374
1413
  })(DisplayPosition || {});
1375
1414
  DISPLAY_POSITIONS = Object.values(DisplayPosition);
1376
1415
  }
@@ -1408,6 +1447,7 @@ var init_DisplayTransforms = __esm({
1408
1447
  const scale = new Vector32(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
1409
1448
  return new Matrix4().compose(translation, rotation, scale);
1410
1449
  }
1450
+ /** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
1411
1451
  static apply(geometry, matrix) {
1412
1452
  geometry.applyMatrix4(matrix);
1413
1453
  if (matrix.determinant() < 0) {
@@ -1435,6 +1475,7 @@ var init_ModelMerger = __esm({
1435
1475
  init_AssetKey();
1436
1476
  init_DisplayTransforms();
1437
1477
  ModelMerger = class {
1478
+ /** Loads the parent chain and returns a merged model without modifying the supplied definition. */
1438
1479
  static async mergeWithParents(model) {
1439
1480
  const models = await this.collectAllParents(model);
1440
1481
  let merged = {};
@@ -1502,6 +1543,7 @@ var init_Models = __esm({
1502
1543
  static get PERSISTENT_CACHE() {
1503
1544
  return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-models"));
1504
1545
  }
1546
+ /** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
1505
1547
  static async getItemList() {
1506
1548
  const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
1507
1549
  const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
@@ -1572,6 +1614,7 @@ var init_Models = __esm({
1572
1614
  }
1573
1615
  throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
1574
1616
  }
1617
+ /** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
1575
1618
  static async getRaw(key) {
1576
1619
  if (!key.assetType) {
1577
1620
  key.assetType = "models";
@@ -1584,13 +1627,19 @@ var init_Models = __esm({
1584
1627
  return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1585
1628
  return AssetLoader.get(key, "model" /* MODEL */);
1586
1629
  });
1587
- }).then((asset) => {
1588
- if (asset) {
1589
- asset.key = key;
1630
+ }).then((asset2) => {
1631
+ if (asset2) {
1632
+ asset2.key = key;
1590
1633
  }
1591
- return asset;
1634
+ return asset2;
1592
1635
  });
1593
1636
  }
1637
+ /**
1638
+ * Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
1639
+ * Item keys use GUI context, false conditions, and zero numeric properties.
1640
+ *
1641
+ * @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
1642
+ */
1594
1643
  static async getMerged(key) {
1595
1644
  if (!key.assetType) {
1596
1645
  key.assetType = "models";
@@ -1603,9 +1652,11 @@ var init_Models = __esm({
1603
1652
  return _Models.loadAndMerge(key);
1604
1653
  });
1605
1654
  }
1655
+ /** Alias for {@link getMerged}. */
1606
1656
  static async get(key) {
1607
1657
  return this.getMerged(key);
1608
1658
  }
1659
+ /** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
1609
1660
  static async clearCache() {
1610
1661
  return this.PERSISTENT_CACHE.clear();
1611
1662
  }
@@ -1628,6 +1679,7 @@ var init_BlockStates = __esm({
1628
1679
  return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-blockstates"));
1629
1680
  }
1630
1681
  // BlockState names are hardcoded
1682
+ /** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
1631
1683
  static async getList() {
1632
1684
  const key = new AssetKey(
1633
1685
  DEFAULT_NAMESPACE,
@@ -1648,6 +1700,7 @@ var init_BlockStates = __esm({
1648
1700
  return AssetLoader.get(key, "json" /* JSON */);
1649
1701
  });
1650
1702
  }
1703
+ /** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
1651
1704
  static async getDefaultState(key) {
1652
1705
  const defaultStates = await this.getDefaultStates();
1653
1706
  if (!defaultStates) {
@@ -1655,6 +1708,7 @@ var init_BlockStates = __esm({
1655
1708
  }
1656
1709
  return defaultStates["minecraft:" + key.path];
1657
1710
  }
1711
+ /** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
1658
1712
  static async get(key) {
1659
1713
  if (!key.assetType) {
1660
1714
  key.assetType = "blockstates";
@@ -1667,11 +1721,11 @@ var init_BlockStates = __esm({
1667
1721
  return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1668
1722
  return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
1669
1723
  });
1670
- }).then((asset) => {
1671
- if (asset) {
1672
- asset.key = key;
1724
+ }).then((asset2) => {
1725
+ if (asset2) {
1726
+ asset2.key = key;
1673
1727
  }
1674
- return asset;
1728
+ return asset2;
1675
1729
  });
1676
1730
  }
1677
1731
  static getAll(keys) {
@@ -1681,6 +1735,7 @@ var init_BlockStates = __esm({
1681
1735
  }
1682
1736
  return Promise.all(promises);
1683
1737
  }
1738
+ /** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
1684
1739
  static async clearCache() {
1685
1740
  await this.PERSISTENT_CACHE.clear();
1686
1741
  }
@@ -1706,9 +1761,11 @@ var init_Assets = __esm({
1706
1761
  static parseAssetKey(assetType, str, origin) {
1707
1762
  return AssetKey.parse(assetType, str, origin);
1708
1763
  }
1764
+ /** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
1709
1765
  static async getModel(key) {
1710
1766
  return Models.getMerged(key);
1711
1767
  }
1768
+ /** Loads a blockstate definition. See {@link BlockStates.get}. */
1712
1769
  static async getBlockState(key) {
1713
1770
  return BlockStates.get(key);
1714
1771
  }
@@ -1755,6 +1812,10 @@ var BatchedExecutor;
1755
1812
  var init_BatchedExecutor = __esm({
1756
1813
  "src/util/BatchedExecutor.ts"() {
1757
1814
  BatchedExecutor = class {
1815
+ /**
1816
+ * @param interval - Delay between queue runs, in milliseconds.
1817
+ * @param batch - Maximum tasks started per run and maximum tasks active at once.
1818
+ */
1758
1819
  constructor(interval = 1, batch = 30) {
1759
1820
  if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
1760
1821
  if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
@@ -1762,9 +1823,11 @@ var init_BatchedExecutor = __esm({
1762
1823
  this.batch = batch;
1763
1824
  this.queue = new JobQueue2(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
1764
1825
  }
1826
+ /** Queues a task and resolves with its result, or rejects with its error. */
1765
1827
  submit(task) {
1766
1828
  return this.queue.add(() => task());
1767
1829
  }
1830
+ /** Rejects queued and future tasks. Tasks already running are allowed to finish. */
1768
1831
  stop() {
1769
1832
  this.queue.end();
1770
1833
  }
@@ -1812,29 +1875,29 @@ function applyGenericRotation(axis, rotation, obj) {
1812
1875
  break;
1813
1876
  }
1814
1877
  }
1815
- function addBox3WireframeToObject(box, obj, color = 16777215, w = 2) {
1878
+ function addBox3WireframeToObject(box, obj, color2 = 16777215, w = 2) {
1816
1879
  let size = new Vector33();
1817
1880
  box.getSize(size);
1818
1881
  let boxGeo = new BoxGeometry2(size.x, size.y, size.z, 1, 1, 1);
1819
1882
  let wireGeo = new EdgesGeometry(boxGeo);
1820
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
1883
+ let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
1821
1884
  let wireframe = new LineSegments(wireGeo, wireMat);
1822
1885
  obj.add(wireframe);
1823
1886
  let axes = new AxesHelper(1);
1824
1887
  obj.add(axes);
1825
1888
  }
1826
- function addWireframeToObject(obj, color = 16777215, w = 2) {
1889
+ function addWireframeToObject(obj, color2 = 16777215, w = 2) {
1827
1890
  let boxGeo = new BoxGeometry2(16, 16, 16, 1, 1, 1);
1828
1891
  let wireGeo = new EdgesGeometry(boxGeo);
1829
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
1892
+ let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
1830
1893
  let wireframe = new LineSegments(wireGeo, wireMat);
1831
1894
  obj.add(wireframe);
1832
1895
  let axes = new AxesHelper(1);
1833
1896
  obj.add(axes);
1834
1897
  }
1835
- function addWireframeToMesh(geo, mesh, color = 16777215, w = 2) {
1898
+ function addWireframeToMesh(geo, mesh, color2 = 16777215, w = 2) {
1836
1899
  let wireGeo = new EdgesGeometry(geo);
1837
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
1900
+ let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
1838
1901
  let wireframe = new LineSegments(wireGeo, wireMat);
1839
1902
  mesh.add(wireframe);
1840
1903
  let axes = new AxesHelper(1);
@@ -1906,6 +1969,7 @@ var NBTHelper;
1906
1969
  var init_NBTHelper = __esm({
1907
1970
  "src/nbt/NBTHelper.ts"() {
1908
1971
  NBTHelper = class {
1972
+ /** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
1909
1973
  static async fromBuffer(data, format2) {
1910
1974
  const buffer = Buffer3.from(data instanceof Uint8Array ? data : new Uint8Array(data));
1911
1975
  const prismarineNbt = await import("prismarine-nbt");
@@ -1974,6 +2038,7 @@ var init_HostedAssetSource = __esm({
1974
2038
  retryDefaults: true
1975
2039
  };
1976
2040
  _HostedAssetSource = class _HostedAssetSource extends AssetSource {
2041
+ /** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
1977
2042
  async loadOrRetryWithDefaults(key, parser) {
1978
2043
  const direct = await this.load(key, parser);
1979
2044
  if (typeof direct !== "undefined") {
@@ -2031,6 +2096,10 @@ var init_HostedAssetSource = __esm({
2031
2096
  assetBasePath(key) {
2032
2097
  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 + "/" : ""}`;
2033
2098
  }
2099
+ /**
2100
+ * Creates a hosted source for {@link AssetLoader.addSource}.
2101
+ * Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
2102
+ */
2034
2103
  constructor(root, options) {
2035
2104
  super();
2036
2105
  this._root = root;
@@ -2123,6 +2192,11 @@ var BrowserArchiveProxy;
2123
2192
  var init_BrowserArchiveProxy = __esm({
2124
2193
  "src/assets/source/archive/BrowserArchiveProxy.ts"() {
2125
2194
  BrowserArchiveProxy = class {
2195
+ /**
2196
+ * Opens a ZIP for asset lookup.
2197
+ * @param id - Cache identity for this content. Change it when the pack changes.
2198
+ * A File supplies an identity from its name, size, and modification time when omitted.
2199
+ */
2126
2200
  constructor(blob, id) {
2127
2201
  this._blob = blob;
2128
2202
  this._reader = new ZipReader(new BlobReader(this._blob));
@@ -2252,27 +2326,27 @@ var init_PackFormats = __esm({
2252
2326
  init_Requests();
2253
2327
  PackFormats = class {
2254
2328
  /** Resolves pack formats from Mojang's client metadata hosted on assets.mcasset.cloud. */
2255
- static async get(version, type) {
2256
- let pending = this.versions.get(version);
2329
+ static async get(version2, type) {
2330
+ let pending = this.versions.get(version2);
2257
2331
  if (!pending || pending.expires <= Date.now()) {
2258
2332
  pending = {
2259
- formats: this.load(version),
2260
- expires: ["latest", "release", "snapshot"].includes(version) ? Date.now() + 3e5 : Infinity
2333
+ formats: this.load(version2),
2334
+ expires: ["latest", "release", "snapshot"].includes(version2) ? Date.now() + 3e5 : Infinity
2261
2335
  };
2262
- this.versions.set(version, pending);
2336
+ this.versions.set(version2, pending);
2263
2337
  }
2264
2338
  try {
2265
2339
  return (await pending.formats)[type];
2266
2340
  } catch (cause) {
2267
- if (this.versions.get(version) === pending) this.versions.delete(version);
2341
+ if (this.versions.get(version2) === pending) this.versions.delete(version2);
2268
2342
  const option = type === "assets" ? "resourcePackFormat" : "dataPackFormat";
2269
2343
  const reason = cause instanceof Error ? cause.message : String(cause);
2270
- throw new Error(`Could not resolve the pack format for Minecraft ${version}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
2344
+ throw new Error(`Could not resolve the pack format for Minecraft ${version2}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
2271
2345
  }
2272
2346
  }
2273
- static async load(version) {
2347
+ static async load(version2) {
2274
2348
  const response = await Requests.genericRequest({
2275
- url: `https://assets.mcasset.cloud/${encodeURIComponent(version)}/game-version.json`,
2349
+ url: `https://assets.mcasset.cloud/${encodeURIComponent(version2)}/game-version.json`,
2276
2350
  responseType: "json"
2277
2351
  });
2278
2352
  const data = response.data;
@@ -2320,6 +2394,7 @@ var init_ArchiveAssetSource = __esm({
2320
2394
  dataPackFormat: copyFormat(options.dataPackFormat)
2321
2395
  };
2322
2396
  }
2397
+ /** Creates a browser ZIP source from a Blob or File. */
2323
2398
  static blob(blob, options) {
2324
2399
  return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
2325
2400
  }
@@ -2360,7 +2435,7 @@ var init_ArchiveAssetSource = __esm({
2360
2435
  }
2361
2436
  async load(key, parser) {
2362
2437
  const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
2363
- const version = AssetLoader.version;
2438
+ const version2 = AssetLoader.version;
2364
2439
  let selectedPath = path;
2365
2440
  let url;
2366
2441
  try {
@@ -2368,7 +2443,7 @@ var init_ArchiveAssetSource = __esm({
2368
2443
  if (key.rootType === "assets" || key.rootType === "data") {
2369
2444
  const metadata = await this.getMetadata(key);
2370
2445
  if (metadata.hasOverlays) {
2371
- const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version, key.rootType);
2446
+ const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version2, key.rootType);
2372
2447
  directories = metadata.overlayDirectories(format2);
2373
2448
  }
2374
2449
  }
@@ -2454,8 +2529,9 @@ var init_AssetLoader = __esm({
2454
2529
  static get version() {
2455
2530
  return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
2456
2531
  }
2457
- static setVersion(version) {
2458
- this.ROOT = `https://assets.mcasset.cloud/${version}`;
2532
+ /** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
2533
+ static setVersion(version2) {
2534
+ this.ROOT = `https://assets.mcasset.cloud/${version2}`;
2459
2535
  const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
2460
2536
  const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
2461
2537
  if (index !== -1) {
@@ -2487,6 +2563,13 @@ var init_AssetLoader = __esm({
2487
2563
  return scope ? `${scope}
2488
2564
  ${key}` : key;
2489
2565
  }
2566
+ /**
2567
+ * Registers a source at the highest priority.
2568
+ * Call {@link Caching.clear} after changing sources to discard previously loaded assets.
2569
+ *
2570
+ * @param key - Name used to replace or remove this source.
2571
+ * @param override - Removes the first source with this name before adding the new one.
2572
+ */
2490
2573
  static addSource(key, source, override = true) {
2491
2574
  if (override) {
2492
2575
  const existing = this.removeSource(key);
@@ -2497,6 +2580,7 @@ ${key}` : key;
2497
2580
  this._SOURCES.unshift({ key, source });
2498
2581
  console.log(p4, "Added AssetSource", key);
2499
2582
  }
2583
+ /** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
2500
2584
  static removeSource(key) {
2501
2585
  const index = this._SOURCES.findIndex((s) => s.key === key);
2502
2586
  if (index != -1) {
@@ -2507,12 +2591,13 @@ ${key}` : key;
2507
2591
  }
2508
2592
  return void 0;
2509
2593
  }
2594
+ /** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
2510
2595
  static async getAll(key, parser) {
2511
2596
  const sources = [...this._SOURCES];
2512
2597
  const results = [];
2513
2598
  for (const { source } of sources) {
2514
- const asset = await source.get(key, parser);
2515
- if (asset != void 0) results.push(asset);
2599
+ const asset2 = await source.get(key, parser);
2600
+ if (asset2 != void 0) results.push(asset2);
2516
2601
  if (await source.blocks(key)) break;
2517
2602
  }
2518
2603
  return results;
@@ -2551,6 +2636,7 @@ ${key}` : key;
2551
2636
  return void 0;
2552
2637
  }
2553
2638
  };
2639
+ /** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
2554
2640
  _AssetLoader.ROOT = DEFAULT_ROOT;
2555
2641
  _AssetLoader._SOURCES = [];
2556
2642
  /** Used when a registered source cannot identify its content, so nothing persists past this session. */
@@ -2575,14 +2661,15 @@ var init_ModelTextures = __esm({
2575
2661
  static get PERSISTENT_META_CACHE() {
2576
2662
  return this._persistentMetaCache ?? (this._persistentMetaCache = PersistentCache.open("minerender-texturemeta"));
2577
2663
  }
2664
+ /** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
2578
2665
  static async get(key) {
2579
2666
  const keyStr = key.serialize();
2580
2667
  const pending = this.preload(key);
2581
2668
  const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
2582
2669
  try {
2583
- const asset = await pending;
2584
- if (asset) {
2585
- return await ImageLoader.infoToCanvasData(asset);
2670
+ const asset2 = await pending;
2671
+ if (asset2) {
2672
+ return await ImageLoader.infoToCanvasData(asset2);
2586
2673
  }
2587
2674
  return void 0;
2588
2675
  } catch (err) {
@@ -2592,16 +2679,18 @@ var init_ModelTextures = __esm({
2592
2679
  throw err;
2593
2680
  }
2594
2681
  }
2682
+ /** Fetches and caches encoded texture bytes without decoding the pixels. */
2595
2683
  static async preload(key) {
2596
2684
  const keyStr = key.serialize();
2597
2685
  return Caching.textureAssetCache.get(keyStr, (k) => {
2598
- return AssetLoader.get(key, "image" /* IMAGE */).then((asset) => {
2599
- if (asset)
2600
- asset.key = key;
2601
- return asset;
2686
+ return AssetLoader.get(key, "image" /* IMAGE */).then((asset2) => {
2687
+ if (asset2)
2688
+ asset2.key = key;
2689
+ return asset2;
2602
2690
  });
2603
2691
  });
2604
2692
  }
2693
+ /** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
2605
2694
  static async getMeta(key) {
2606
2695
  if (!key.extension || !key.extension.endsWith(".mcmeta")) {
2607
2696
  key = Object.assign(new AssetKey("", ""), key);
@@ -2610,14 +2699,15 @@ var init_ModelTextures = __esm({
2610
2699
  const keyStr = key.serialize();
2611
2700
  return Caching.textureMetaCache.get(keyStr, (k) => {
2612
2701
  return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
2613
- return AssetLoader.get(key, "meta" /* META */).then((asset) => {
2614
- if (asset)
2615
- asset.key = key;
2616
- return asset;
2702
+ return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
2703
+ if (asset2)
2704
+ asset2.key = key;
2705
+ return asset2;
2617
2706
  });
2618
2707
  });
2619
2708
  });
2620
2709
  }
2710
+ /** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
2621
2711
  static async clearCache() {
2622
2712
  await this.PERSISTENT_META_CACHE.clear();
2623
2713
  }
@@ -2684,6 +2774,7 @@ var init_Ticker = __esm({
2684
2774
  }
2685
2775
  return handle;
2686
2776
  }
2777
+ /** Starts or resumes the shared tick loop. Repeated calls have no effect. */
2687
2778
  static start() {
2688
2779
  if (this.interval) return;
2689
2780
  this.interval = this.schedule(() => {
@@ -2706,6 +2797,7 @@ var init_Ticker = __esm({
2706
2797
  _Ticker.fiveSecondTicks = 0;
2707
2798
  }, 1e3 * 5);
2708
2799
  }
2800
+ /** Pauses the loop while retaining callbacks for a later {@link start}. */
2709
2801
  static stop() {
2710
2802
  if (this.interval) clearInterval(this.interval);
2711
2803
  if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
@@ -2714,12 +2806,14 @@ var init_Ticker = __esm({
2714
2806
  this.oneSecondTicksTracker = void 0;
2715
2807
  this.fiveSecondTicksTracker = void 0;
2716
2808
  }
2809
+ /** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
2717
2810
  static add(tick) {
2718
2811
  this.start();
2719
2812
  const c = this.counter++;
2720
2813
  this._tickers.set(c, tick);
2721
2814
  return c;
2722
2815
  }
2816
+ /** Removes a callback by ID and stops the timers when no callbacks remain. */
2723
2817
  static remove(c) {
2724
2818
  if (typeof c === "number") this._tickers.delete(c);
2725
2819
  if (!this._tickers.size) this.stop();
@@ -2733,6 +2827,7 @@ var init_Ticker = __esm({
2733
2827
  static get tpsFiveSeconds() {
2734
2828
  return this.fiveSecondTps;
2735
2829
  }
2830
+ /** Stops the loop and removes all registered callbacks. */
2736
2831
  static dispose() {
2737
2832
  this.stop();
2738
2833
  this._tickers.clear();
@@ -2787,6 +2882,7 @@ var init_TextureAtlas = __esm({
2787
2882
  }
2788
2883
  };
2789
2884
  }
2885
+ /** Copies pixels for a texture variable from its current atlas rectangle. */
2790
2886
  getData(texture) {
2791
2887
  const pos = this.positions[texture];
2792
2888
  const size = this.sizes[texture];
@@ -3064,6 +3160,7 @@ var init_UVMapper = __esm({
3064
3160
  }
3065
3161
  uv.needsUpdate = true;
3066
3162
  }
3163
+ /** Returns the shared atlas for a keyed, merged model, creating it on first use. */
3067
3164
  static async getAtlas(model) {
3068
3165
  const keyStr = model.key.serialize();
3069
3166
  return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
@@ -3134,6 +3231,11 @@ var init_UVMapper = __esm({
3134
3231
  }
3135
3232
  return [tl, tr, bl, br];
3136
3233
  }
3234
+ /**
3235
+ * Combines model textures and animation metadata into an atlas.
3236
+ * Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
3237
+ * Returns `undefined` for models without a texture map.
3238
+ */
3137
3239
  static async createAtlas(originalModel) {
3138
3240
  const textureMap = {};
3139
3241
  const metaMap = {};
@@ -3154,12 +3256,12 @@ var init_UVMapper = __esm({
3154
3256
  } else {
3155
3257
  uniqueTextureNames.push(textureKey);
3156
3258
  const assetKey = AssetKey.parse("textures", textureValue, model.key);
3157
- promises.push(ModelTextures.get(assetKey).then((asset) => {
3158
- if (!asset) {
3259
+ promises.push(ModelTextures.get(assetKey).then((asset2) => {
3260
+ if (!asset2) {
3159
3261
  console.warn(p5, "Missing texture", assetKey);
3160
3262
  return;
3161
3263
  }
3162
- textureMap[textureKey] = new WrappedImage(asset);
3264
+ textureMap[textureKey] = new WrappedImage(asset2);
3163
3265
  }));
3164
3266
  promises.push(ModelTextures.getMeta(assetKey).then((meta) => {
3165
3267
  metaMap[textureKey] = meta;
@@ -3498,14 +3600,15 @@ var init_Entities = __esm({
3498
3600
  init_MineRenderError();
3499
3601
  init_ModelTextures();
3500
3602
  Entities = class {
3603
+ /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
3501
3604
  static async getEntityList() {
3502
3605
  const collect = async (path) => {
3503
3606
  const prefix2 = path ? `${path}/` : "";
3504
3607
  const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
3505
- const list = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3506
- if (!list) return [];
3507
- const files = list.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3508
- const children = await Promise.all(list.directories.map((directory) => collect(`${prefix2}${directory}`)));
3608
+ const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3609
+ if (!list2) return [];
3610
+ const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3611
+ const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
3509
3612
  return [...files, ...children.flat()];
3510
3613
  };
3511
3614
  return collect("");
@@ -3515,6 +3618,7 @@ var init_Entities = __esm({
3515
3618
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
3516
3619
  return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3517
3620
  }
3621
+ /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
3518
3622
  static async getLayerList(modelKey) {
3519
3623
  return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
3520
3624
  }
@@ -3522,6 +3626,15 @@ var init_Entities = __esm({
3522
3626
  static async getPassList(modelKey) {
3523
3627
  return (await this.getModelFile(modelKey))?.passes ?? [];
3524
3628
  }
3629
+ /**
3630
+ * Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
3631
+ * By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
3632
+ *
3633
+ * @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
3634
+ * @param textureKey - Texture override for the first selected layer.
3635
+ * @param options - Layer selection, conditional passes, and per-layer texture overrides.
3636
+ * @returns The selected model, or `undefined` when its dataset file is missing.
3637
+ */
3525
3638
  static async getEntity(modelKey, textureKey, options) {
3526
3639
  const model = await this.getModelFile(modelKey);
3527
3640
  if (!model) return void 0;
@@ -3567,6 +3680,7 @@ var init_Entities = __esm({
3567
3680
  const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3568
3681
  return file?.animations;
3569
3682
  }
3683
+ /** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
3570
3684
  static async resolveTexture(modelKey) {
3571
3685
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3572
3686
  const root = isAssetKey(modelKey) ? modelKey.root : void 0;
@@ -3578,8 +3692,8 @@ var init_Entities = __esm({
3578
3692
  if (await ModelTextures.get(texture2)) return texture2;
3579
3693
  }
3580
3694
  const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
3581
- const list = await AssetLoader.get(listKey, "list" /* LIST */);
3582
- const files = list?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3695
+ const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
3696
+ const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3583
3697
  const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
3584
3698
  if (!file) return void 0;
3585
3699
  const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
@@ -3684,27 +3798,27 @@ var init_EntityAnimationPlayer = __esm({
3684
3798
  for (const layer of layers) {
3685
3799
  const objects = [];
3686
3800
  if (poses) {
3687
- layer.traverse((object2) => {
3688
- if (poses.has(object2)) objects.push(object2);
3801
+ layer.traverse((object3) => {
3802
+ if (poses.has(object3)) objects.push(object3);
3689
3803
  });
3690
3804
  } else {
3691
3805
  for (const bone of Object.keys(animation.bones)) {
3692
- const object2 = layer.getObjectByName(`group:${bone}`);
3693
- if (object2) objects.push(object2);
3806
+ const object3 = layer.getObjectByName(`group:${bone}`);
3807
+ if (object3) objects.push(object3);
3694
3808
  }
3695
3809
  }
3696
- for (const object2 of objects) {
3697
- const bone = object2.name.slice("group:".length);
3698
- const pose = poses?.get(object2);
3810
+ for (const object3 of objects) {
3811
+ const bone = object3.name.slice("group:".length);
3812
+ const pose = poses?.get(object3);
3699
3813
  this.parts.push({
3700
3814
  bone,
3701
- object: object2,
3702
- position: object2.position.clone(),
3703
- rotation: object2.rotation.clone(),
3704
- scale: object2.scale.clone(),
3705
- basePosition: pose ? new Vector35(...pose.offset) : object2.position.clone(),
3706
- baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") : object2.rotation.clone(),
3707
- baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) : object2.scale.clone()
3815
+ object: object3,
3816
+ position: object3.position.clone(),
3817
+ rotation: object3.rotation.clone(),
3818
+ scale: object3.scale.clone(),
3819
+ basePosition: pose ? new Vector35(...pose.offset) : object3.position.clone(),
3820
+ baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") : object3.rotation.clone(),
3821
+ baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) : object3.scale.clone()
3708
3822
  });
3709
3823
  }
3710
3824
  }
@@ -3727,12 +3841,14 @@ var init_EntityAnimationPlayer = __esm({
3727
3841
  this._animation = void 0;
3728
3842
  this._time = 0;
3729
3843
  }
3844
+ /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
3730
3845
  setTime(seconds) {
3731
3846
  if (!this._animation) return false;
3732
3847
  this._time = seconds;
3733
3848
  this.apply();
3734
3849
  return true;
3735
3850
  }
3851
+ /** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
3736
3852
  advance(deltaSeconds) {
3737
3853
  return this.setTime(this._time + deltaSeconds * this.speed);
3738
3854
  }
@@ -3770,6 +3886,7 @@ var init_InstanceReference = __esm({
3770
3886
  if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
3771
3887
  return this.instanceable;
3772
3888
  }
3889
+ /** Releases this placement's slot for reuse. Repeated calls have no effect. */
3773
3890
  removeFromScene() {
3774
3891
  if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
3775
3892
  }
@@ -3782,9 +3899,11 @@ var init_InstanceReference = __esm({
3782
3899
  setMatrix(matrix) {
3783
3900
  this.activeInstanceable.setMatrixAt(this.index, matrix);
3784
3901
  }
3902
+ /** Hides or restores this placement while preserving its transform and allocated slot. */
3785
3903
  setVisible(visible) {
3786
3904
  this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
3787
3905
  }
3906
+ /** Updates the supplied transform components. Positions use scene units and rotations use radians. */
3788
3907
  setPositionRotationScale(position, rotation, scale) {
3789
3908
  this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
3790
3909
  }
@@ -3853,6 +3972,7 @@ var init_SceneObject = __esm({
3853
3972
  }
3854
3973
  async init() {
3855
3974
  }
3975
+ /** Marks ancestor scenes for redraw and emits a `change` event. */
3856
3976
  notifyDirty() {
3857
3977
  this.traverseAncestors((parent) => {
3858
3978
  const scene = parent;
@@ -3888,15 +4008,17 @@ var init_SceneObject = __esm({
3888
4008
  return obj;
3889
4009
  }
3890
4010
  /**
3891
- * Get a group by its name
4011
+ * Finds a named part, such as `head`, without the internal `group:` prefix.
4012
+ * Returns `undefined` when the group does not exist.
3892
4013
  */
3893
4014
  getGroupByName(name) {
3894
4015
  return this.getObjectByName(`group:${name}`);
3895
4016
  }
3896
4017
  /**
3897
- * Toggle visibility of a group
3898
- * @param name name of the group
3899
- * @param visible set the visibility directly, if not set toggles it
4018
+ * Changes a named group's visibility and requests a redraw.
4019
+ * @param name - Part name without the `group:` prefix.
4020
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4021
+ * @returns The resulting visibility, or `false` if the group does not exist.
3900
4022
  */
3901
4023
  toggleGroupVisibility(name, visible) {
3902
4024
  return this.toggleObjectVisibility(this.getGroupByName(name), visible);
@@ -3947,15 +4069,16 @@ var init_SceneObject = __esm({
3947
4069
  return mesh;
3948
4070
  }
3949
4071
  /**
3950
- * Get a mesh by its name
4072
+ * Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
3951
4073
  */
3952
4074
  getMeshByName(name) {
3953
4075
  return this.getObjectByName(`mesh:${name}`);
3954
4076
  }
3955
4077
  /**
3956
- * Toggle visibility of a mesh
3957
- * @param name name of the mesh
3958
- * @param visible set the visibility directly, if not set toggles it
4078
+ * Changes a named mesh's visibility and requests a redraw.
4079
+ * @param name - Mesh name without the `mesh:` prefix.
4080
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4081
+ * @returns The resulting visibility, or `false` if the mesh does not exist.
3959
4082
  */
3960
4083
  toggleMeshVisibility(name, visible) {
3961
4084
  return this.toggleObjectVisibility(this.getMeshByName(name), visible);
@@ -4001,12 +4124,14 @@ var init_SceneObject = __esm({
4001
4124
  get isInstanced() {
4002
4125
  return this._isInstanced;
4003
4126
  }
4127
+ /** Number of live placements, excluding released instance slots. */
4004
4128
  get instanceCounter() {
4005
4129
  return this._instanceCounter;
4006
4130
  }
4007
4131
  constructInstanceReference(i) {
4008
4132
  return new InstanceReference(this, i);
4009
4133
  }
4134
+ /** Allocates a placement of this instanced object, reusing a released slot when available. */
4010
4135
  nextInstance() {
4011
4136
  const mesh = this.instanceMesh;
4012
4137
  if (!mesh) throw new MineRenderError("Object is not instanced");
@@ -4051,10 +4176,10 @@ var init_SceneObject = __esm({
4051
4176
  return new InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
4052
4177
  };
4053
4178
  const matrix = grow(mesh.instanceMatrix, 0);
4054
- const color = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
4179
+ const color2 = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
4055
4180
  mesh.dispose();
4056
4181
  mesh.instanceMatrix = matrix;
4057
- mesh.instanceColor = color;
4182
+ mesh.instanceColor = color2;
4058
4183
  }
4059
4184
  *activeInstanceIndices() {
4060
4185
  if (this.instanceMesh) {
@@ -4153,6 +4278,7 @@ var init_SceneObject = __esm({
4153
4278
  vector.setFromMatrixScale(matrix);
4154
4279
  return vector;
4155
4280
  }
4281
+ /** Updates supplied transform components. For a shared model, affects every live instance. */
4156
4282
  setPositionRotationScale(position, rotation, scale) {
4157
4283
  if (this.isInstanced) {
4158
4284
  for (const i of this.activeInstanceIndices()) {
@@ -4171,6 +4297,7 @@ var init_SceneObject = __esm({
4171
4297
  }
4172
4298
  this.notifyDirty();
4173
4299
  }
4300
+ /** Sets position in scene units. For a shared model, updates every live instance. */
4174
4301
  setPosition(position) {
4175
4302
  if (this.isInstanced) {
4176
4303
  for (const i of this.activeInstanceIndices()) {
@@ -4188,6 +4315,7 @@ var init_SceneObject = __esm({
4188
4315
  return this.position;
4189
4316
  }
4190
4317
  }
4318
+ /** Sets rotation in radians. For a shared model, updates every live instance. */
4191
4319
  setRotation(rotation) {
4192
4320
  if (this.isInstanced) {
4193
4321
  for (const i of this.activeInstanceIndices()) {
@@ -4205,6 +4333,7 @@ var init_SceneObject = __esm({
4205
4333
  return this.rotation;
4206
4334
  }
4207
4335
  }
4336
+ /** Sets scale factors. For a shared model, updates every live instance. */
4208
4337
  setScale(scale) {
4209
4338
  if (this.isInstanced) {
4210
4339
  for (const i of this.activeInstanceIndices()) {
@@ -4223,15 +4352,15 @@ var init_SceneObject = __esm({
4223
4352
  }
4224
4353
  }
4225
4354
  //</editor-fold>
4226
- toggleObjectVisibility(object2, visible) {
4227
- if (object2) {
4355
+ toggleObjectVisibility(object3, visible) {
4356
+ if (object3) {
4228
4357
  if (typeof visible !== "undefined") {
4229
- object2.visible = visible;
4358
+ object3.visible = visible;
4230
4359
  } else {
4231
- object2.visible = !object2.visible;
4360
+ object3.visible = !object3.visible;
4232
4361
  }
4233
4362
  this.notifyDirty();
4234
- return object2.visible;
4363
+ return object3.visible;
4235
4364
  }
4236
4365
  this.notifyDirty();
4237
4366
  return false;
@@ -4242,6 +4371,7 @@ var init_SceneObject = __esm({
4242
4371
  this.notifyDirty();
4243
4372
  super.dispose();
4244
4373
  }
4374
+ /** Releases instance slots and removes children, disposing children that implement Disposable. */
4245
4375
  disposeAndRemoveAllChildren() {
4246
4376
  if (this.instanceMesh) {
4247
4377
  if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
@@ -4264,6 +4394,7 @@ var init_SceneObject = __esm({
4264
4394
  }
4265
4395
  this.notifyDirty();
4266
4396
  }
4397
+ /** Detaches the object from its parent and requests a redraw. */
4267
4398
  removeFromScene() {
4268
4399
  this.notifyDirty();
4269
4400
  this.removeFromParent();
@@ -4419,12 +4550,15 @@ var init_EntityObject = __esm({
4419
4550
  if (this.animationPlayers.length) this.notifyDirty();
4420
4551
  }
4421
4552
  //</editor-fold>
4553
+ /** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
4422
4554
  getLayerGroup(name) {
4423
4555
  return super.getGroupByName(`layer:${name}`);
4424
4556
  }
4557
+ /** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
4425
4558
  getGroupByName(name, layerName) {
4426
4559
  return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
4427
4560
  }
4561
+ /** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
4428
4562
  getMeshByName(name, layerName) {
4429
4563
  return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
4430
4564
  }
@@ -4526,8 +4660,8 @@ var init_EntityObject = __esm({
4526
4660
  this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
4527
4661
  }
4528
4662
  }
4529
- this.getLayerGroup(name)?.traverse((object2) => {
4530
- if (isMesh(object2)) object2.material = mat;
4663
+ this.getLayerGroup(name)?.traverse((object3) => {
4664
+ if (isMesh(object3)) object3.material = mat;
4531
4665
  });
4532
4666
  }));
4533
4667
  if (!this.children.length) {
@@ -4868,8 +5002,8 @@ function toTrianglesDrawMode(geometry, drawMode) {
4868
5002
  return geometry;
4869
5003
  }
4870
5004
  }
4871
- function computeMorphedAttributes(object2) {
4872
- if (object2.geometry.isBufferGeometry !== true) {
5005
+ function computeMorphedAttributes(object3) {
5006
+ if (object3.geometry.isBufferGeometry !== true) {
4873
5007
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4874
5008
  return null;
4875
5009
  }
@@ -4882,11 +5016,11 @@ function computeMorphedAttributes(object2) {
4882
5016
  const _morphA = new Vector310();
4883
5017
  const _morphB = new Vector310();
4884
5018
  const _morphC = new Vector310();
4885
- function _calculateMorphedAttributeData(object3, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
5019
+ function _calculateMorphedAttributeData(object4, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4886
5020
  _vA.fromBufferAttribute(attribute, a2);
4887
5021
  _vB.fromBufferAttribute(attribute, b2);
4888
5022
  _vC.fromBufferAttribute(attribute, c2);
4889
- const morphInfluences = object3.morphTargetInfluences;
5023
+ const morphInfluences = object4.morphTargetInfluences;
4890
5024
  if (material2.morphTargets && morphAttribute && morphInfluences) {
4891
5025
  _morphA.set(0, 0, 0);
4892
5026
  _morphB.set(0, 0, 0);
@@ -4912,10 +5046,10 @@ function computeMorphedAttributes(object2) {
4912
5046
  _vB.add(_morphB);
4913
5047
  _vC.add(_morphC);
4914
5048
  }
4915
- if (object3.isSkinnedMesh) {
4916
- object3.boneTransform(a2, _vA);
4917
- object3.boneTransform(b2, _vB);
4918
- object3.boneTransform(c2, _vC);
5049
+ if (object4.isSkinnedMesh) {
5050
+ object4.boneTransform(a2, _vA);
5051
+ object4.boneTransform(b2, _vB);
5052
+ object4.boneTransform(c2, _vC);
4919
5053
  }
4920
5054
  modifiedAttributeArray[a2 * 3 + 0] = _vA.x;
4921
5055
  modifiedAttributeArray[a2 * 3 + 1] = _vA.y;
@@ -4927,8 +5061,8 @@ function computeMorphedAttributes(object2) {
4927
5061
  modifiedAttributeArray[c2 * 3 + 1] = _vC.y;
4928
5062
  modifiedAttributeArray[c2 * 3 + 2] = _vC.z;
4929
5063
  }
4930
- const geometry = object2.geometry;
4931
- const material = object2.material;
5064
+ const geometry = object3.geometry;
5065
+ const material = object3.material;
4932
5066
  let a, b, c;
4933
5067
  const index = geometry.index;
4934
5068
  const positionAttribute = geometry.attributes.position;
@@ -4955,7 +5089,7 @@ function computeMorphedAttributes(object2) {
4955
5089
  b = index.getX(j + 1);
4956
5090
  c = index.getX(j + 2);
4957
5091
  _calculateMorphedAttributeData(
4958
- object2,
5092
+ object3,
4959
5093
  groupMaterial,
4960
5094
  positionAttribute,
4961
5095
  morphPosition,
@@ -4966,7 +5100,7 @@ function computeMorphedAttributes(object2) {
4966
5100
  modifiedPosition
4967
5101
  );
4968
5102
  _calculateMorphedAttributeData(
4969
- object2,
5103
+ object3,
4970
5104
  groupMaterial,
4971
5105
  normalAttribute,
4972
5106
  morphNormal,
@@ -4986,7 +5120,7 @@ function computeMorphedAttributes(object2) {
4986
5120
  b = index.getX(i + 1);
4987
5121
  c = index.getX(i + 2);
4988
5122
  _calculateMorphedAttributeData(
4989
- object2,
5123
+ object3,
4990
5124
  material,
4991
5125
  positionAttribute,
4992
5126
  morphPosition,
@@ -4997,7 +5131,7 @@ function computeMorphedAttributes(object2) {
4997
5131
  modifiedPosition
4998
5132
  );
4999
5133
  _calculateMorphedAttributeData(
5000
- object2,
5134
+ object3,
5001
5135
  material,
5002
5136
  normalAttribute,
5003
5137
  morphNormal,
@@ -5021,7 +5155,7 @@ function computeMorphedAttributes(object2) {
5021
5155
  b = j + 1;
5022
5156
  c = j + 2;
5023
5157
  _calculateMorphedAttributeData(
5024
- object2,
5158
+ object3,
5025
5159
  groupMaterial,
5026
5160
  positionAttribute,
5027
5161
  morphPosition,
@@ -5032,7 +5166,7 @@ function computeMorphedAttributes(object2) {
5032
5166
  modifiedPosition
5033
5167
  );
5034
5168
  _calculateMorphedAttributeData(
5035
- object2,
5169
+ object3,
5036
5170
  groupMaterial,
5037
5171
  normalAttribute,
5038
5172
  morphNormal,
@@ -5052,7 +5186,7 @@ function computeMorphedAttributes(object2) {
5052
5186
  b = i + 1;
5053
5187
  c = i + 2;
5054
5188
  _calculateMorphedAttributeData(
5055
- object2,
5189
+ object3,
5056
5190
  material,
5057
5191
  positionAttribute,
5058
5192
  morphPosition,
@@ -5063,7 +5197,7 @@ function computeMorphedAttributes(object2) {
5063
5197
  modifiedPosition
5064
5198
  );
5065
5199
  _calculateMorphedAttributeData(
5066
- object2,
5200
+ object3,
5067
5201
  material,
5068
5202
  normalAttribute,
5069
5203
  morphNormal,
@@ -5097,6 +5231,7 @@ var init_ModelCulling = __esm({
5097
5231
  "src/model/ModelCulling.ts"() {
5098
5232
  init_CubeFace();
5099
5233
  ModelCulling = class {
5234
+ /** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
5100
5235
  static toLocalMask(worldMask, rotation) {
5101
5236
  let localMask = 0;
5102
5237
  for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
@@ -5107,6 +5242,7 @@ var init_ModelCulling = __esm({
5107
5242
  }
5108
5243
  return localMask;
5109
5244
  }
5245
+ /** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
5110
5246
  static isOpaqueFullCube(atlas) {
5111
5247
  if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
5112
5248
  const element = atlas.model.elements[0];
@@ -5317,9 +5453,9 @@ var init_ItemTints = __esm({
5317
5453
  }));
5318
5454
  return colors;
5319
5455
  }
5320
- static rgb(color) {
5321
- if (!Array.isArray(color)) return color & 16777215;
5322
- const [red, green, blue] = color.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
5456
+ static rgb(color2) {
5457
+ if (!Array.isArray(color2)) return color2 & 16777215;
5458
+ const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
5323
5459
  return red << 16 | green << 8 | blue;
5324
5460
  }
5325
5461
  };
@@ -5372,13 +5508,13 @@ var init_ModelObject = __esm({
5372
5508
  transform.multiply(new Matrix49().makeTranslation(-8, -8, -8));
5373
5509
  try {
5374
5510
  for (const part of parts) {
5375
- const object2 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
5376
- object2.matrix.copy(transform).multiply(part.transform);
5377
- object2.matrixWorldNeedsUpdate = true;
5378
- object2.matrixAutoUpdate = false;
5379
- this.add(object2);
5380
- await object2.init();
5381
- object2.iterateAllMeshes((mesh) => {
5511
+ const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
5512
+ object3.matrix.copy(transform).multiply(part.transform);
5513
+ object3.matrixWorldNeedsUpdate = true;
5514
+ object3.matrixAutoUpdate = false;
5515
+ this.add(object3);
5516
+ await object3.init();
5517
+ object3.iterateAllMeshes((mesh) => {
5382
5518
  const source = mesh.material;
5383
5519
  let material = this.specialMaterials.get(source);
5384
5520
  if (!material) {
@@ -5389,7 +5525,7 @@ var init_ModelObject = __esm({
5389
5525
  mesh.material = material;
5390
5526
  });
5391
5527
  for (const [name, rotation] of Object.entries(part.rotations)) {
5392
- object2.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
5528
+ object3.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
5393
5529
  }
5394
5530
  }
5395
5531
  } catch (error) {
@@ -5442,9 +5578,9 @@ var init_ModelObject = __esm({
5442
5578
  if (tintIndex === void 0 || tintIndex < 0) continue;
5443
5579
  const tint = this.options.tints[tintIndex];
5444
5580
  if (tint === void 0) continue;
5445
- const color = new Color9(tint);
5581
+ const color2 = new Color9(tint);
5446
5582
  for (let vertex = 0; vertex < 4; vertex++) {
5447
- color.toArray(colors, (faceIndex * 4 + vertex) * 3);
5583
+ color2.toArray(colors, (faceIndex * 4 + vertex) * 3);
5448
5584
  }
5449
5585
  }
5450
5586
  elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
@@ -5761,9 +5897,9 @@ var init_FluidModelObject = __esm({
5761
5897
  }
5762
5898
  }
5763
5899
  geometry.clearGroups();
5764
- const color = new Color10(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
5900
+ const color2 = new Color10(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
5765
5901
  const colors = new Float32Array(geometry.getAttribute("position").count * 3);
5766
- for (let i = 0; i < colors.length; i += 3) color.toArray(colors, i);
5902
+ for (let i = 0; i < colors.length; i += 3) color2.toArray(colors, i);
5767
5903
  geometry.setAttribute("color", new BufferAttribute6(colors, 3));
5768
5904
  geometry.computeBoundingBox();
5769
5905
  geometry.computeBoundingSphere();
@@ -5893,11 +6029,11 @@ var BrowserEnv = class {
5893
6029
  imageSize(data) {
5894
6030
  return probeImageSize(data);
5895
6031
  }
5896
- openCache(name, version) {
6032
+ openCache(name, version2) {
5897
6033
  return new BrowserCache(localforage.createInstance({
5898
6034
  // IndexedDB schema upgrades retain records; each cache generation needs its own database.
5899
- name: name + version,
5900
- version
6035
+ name: name + version2,
6036
+ version: version2
5901
6037
  }));
5902
6038
  }
5903
6039
  };
@@ -5928,6 +6064,7 @@ var Meshes = class _Meshes {
5928
6064
  static createBox(key) {
5929
6065
  return new Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
5930
6066
  }
6067
+ /** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
5931
6068
  static getBox(key) {
5932
6069
  const keyStr = serializeMeshKey(key);
5933
6070
  return Caching.boxMeshCache.get(keyStr, (k) => {
@@ -6040,6 +6177,7 @@ init_AssetParsers();
6040
6177
  init_ModelTextures();
6041
6178
  init_Caching();
6042
6179
  var Fonts = class {
6180
+ /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
6043
6181
  static async get(key = "minecraft:default") {
6044
6182
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
6045
6183
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -6055,8 +6193,8 @@ var Fonts = class {
6055
6193
  const stack = await AssetLoader.getAll(key, "json" /* JSON */);
6056
6194
  if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
6057
6195
  const glyphs = /* @__PURE__ */ new Map();
6058
- for (const asset of stack) {
6059
- for (const provider of asset.providers) {
6196
+ for (const asset2 of stack) {
6197
+ for (const provider of asset2.providers) {
6060
6198
  if (provider.filter?.uniform || provider.filter?.jp) continue;
6061
6199
  let provided;
6062
6200
  switch (provider.type) {
@@ -6202,13 +6340,13 @@ var OBJExporter = class {
6202
6340
  * @param {Object3D} object - The 3D object to export.
6203
6341
  * @return {string} The exported OBJ.
6204
6342
  */
6205
- parse(object2) {
6343
+ parse(object3) {
6206
6344
  let output = "";
6207
6345
  let indexVertex = 0;
6208
6346
  let indexVertexUvs = 0;
6209
6347
  let indexNormals = 0;
6210
6348
  const vertex = new Vector37();
6211
- const color = new Color4();
6349
+ const color2 = new Color4();
6212
6350
  const normal = new Vector37();
6213
6351
  const uv = new Vector22();
6214
6352
  const face = [];
@@ -6307,9 +6445,9 @@ var OBJExporter = class {
6307
6445
  vertex.applyMatrix4(points.matrixWorld);
6308
6446
  output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
6309
6447
  if (colors !== void 0) {
6310
- color.fromBufferAttribute(colors, i);
6311
- ColorManagement.workingToColorSpace(color, SRGBColorSpace3);
6312
- output += " " + color.r + " " + color.g + " " + color.b;
6448
+ color2.fromBufferAttribute(colors, i);
6449
+ ColorManagement.workingToColorSpace(color2, SRGBColorSpace3);
6450
+ output += " " + color2.r + " " + color2.g + " " + color2.b;
6313
6451
  }
6314
6452
  output += "\n";
6315
6453
  }
@@ -6321,7 +6459,7 @@ var OBJExporter = class {
6321
6459
  }
6322
6460
  indexVertex += nbVertex;
6323
6461
  }
6324
- object2.traverse(function(child) {
6462
+ object3.traverse(function(child) {
6325
6463
  if (child.isMesh === true) {
6326
6464
  parseMesh(child);
6327
6465
  }
@@ -6355,9 +6493,9 @@ var PLYExporter = class {
6355
6493
  * @param {PLYExporter~Options} options - The export options.
6356
6494
  * @return {?(string|ArrayBuffer)} The exported PLY.
6357
6495
  */
6358
- parse(object2, onDone, options = {}) {
6496
+ parse(object3, onDone, options = {}) {
6359
6497
  function traverseMeshes(cb) {
6360
- object2.traverse(function(child) {
6498
+ object3.traverse(function(child) {
6361
6499
  if (child.isMesh === true || child.isPoints) {
6362
6500
  const mesh = child;
6363
6501
  const geometry = mesh.geometry;
@@ -6390,7 +6528,7 @@ var PLYExporter = class {
6390
6528
  for (const name of customAttributeNames) customTypes[name] = "float";
6391
6529
  let vertexCount = 0;
6392
6530
  let faceCount = 0;
6393
- object2.traverse(function(child) {
6531
+ object3.traverse(function(child) {
6394
6532
  if (child.isMesh === true) {
6395
6533
  const mesh = child;
6396
6534
  const geometry = mesh.geometry;
@@ -7012,8 +7150,8 @@ function equalArray(array1, array2) {
7012
7150
  return element === array2[index];
7013
7151
  });
7014
7152
  }
7015
- function stringToArrayBuffer(text) {
7016
- return new TextEncoder().encode(text).buffer;
7153
+ function stringToArrayBuffer(text2) {
7154
+ return new TextEncoder().encode(text2).buffer;
7017
7155
  }
7018
7156
  function isIdentityMatrix(matrix) {
7019
7157
  return equalArray(matrix.elements, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
@@ -7206,12 +7344,12 @@ var GLTFWriter = class {
7206
7344
  * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
7207
7345
  * @param {Object} objectDef
7208
7346
  */
7209
- serializeUserData(object2, objectDef) {
7210
- if (Object.keys(object2.userData).length === 0) return;
7347
+ serializeUserData(object3, objectDef) {
7348
+ if (Object.keys(object3.userData).length === 0) return;
7211
7349
  const options = this.options;
7212
7350
  const extensionsUsed = this.extensionsUsed;
7213
7351
  try {
7214
- const json = JSON.parse(JSON.stringify(object2.userData));
7352
+ const json = JSON.parse(JSON.stringify(object3.userData));
7215
7353
  if (options.includeCustomExtensions && json.gltfExtensions) {
7216
7354
  if (objectDef.extensions === void 0) objectDef.extensions = {};
7217
7355
  for (const extensionName in json.gltfExtensions) {
@@ -7222,7 +7360,7 @@ var GLTFWriter = class {
7222
7360
  }
7223
7361
  if (Object.keys(json).length > 0) objectDef.extras = json;
7224
7362
  } catch (error) {
7225
- console.warn("THREE.GLTFExporter: userData of '" + object2.name + "' won't be serialized because of JSON.stringify error - " + error.message);
7363
+ console.warn("THREE.GLTFExporter: userData of '" + object3.name + "' won't be serialized because of JSON.stringify error - " + error.message);
7226
7364
  }
7227
7365
  }
7228
7366
  /**
@@ -7731,9 +7869,9 @@ var GLTFWriter = class {
7731
7869
  if (material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true) {
7732
7870
  console.warn("GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.");
7733
7871
  }
7734
- const color = material.color.toArray().concat([material.opacity]);
7735
- if (!equalArray(color, [1, 1, 1, 1])) {
7736
- materialDef.pbrMetallicRoughness.baseColorFactor = color;
7872
+ const color2 = material.color.toArray().concat([material.opacity]);
7873
+ if (!equalArray(color2, [1, 1, 1, 1])) {
7874
+ materialDef.pbrMetallicRoughness.baseColorFactor = color2;
7737
7875
  }
7738
7876
  if (material.isMeshStandardMaterial) {
7739
7877
  materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
@@ -8148,13 +8286,13 @@ var GLTFWriter = class {
8148
8286
  * @param {THREE.Object3D} object
8149
8287
  * @return {?number}
8150
8288
  */
8151
- processSkin(object2) {
8289
+ processSkin(object3) {
8152
8290
  const json = this.json;
8153
8291
  const nodeMap = this.nodeMap;
8154
- const node = json.nodes[nodeMap.get(object2)];
8155
- const skeleton = object2.skeleton;
8292
+ const node = json.nodes[nodeMap.get(object3)];
8293
+ const skeleton = object3.skeleton;
8156
8294
  if (skeleton === void 0) return null;
8157
- const rootJoint = object2.skeleton.bones[0];
8295
+ const rootJoint = object3.skeleton.bones[0];
8158
8296
  if (rootJoint === void 0) return null;
8159
8297
  const joints = [];
8160
8298
  const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
@@ -8162,7 +8300,7 @@ var GLTFWriter = class {
8162
8300
  for (let i = 0; i < skeleton.bones.length; ++i) {
8163
8301
  joints.push(nodeMap.get(skeleton.bones[i]));
8164
8302
  temporaryBoneInverse.copy(skeleton.boneInverses[i]);
8165
- temporaryBoneInverse.multiply(object2.bindMatrix).toArray(inverseBindMatrices, i * 16);
8303
+ temporaryBoneInverse.multiply(object3.bindMatrix).toArray(inverseBindMatrices, i * 16);
8166
8304
  }
8167
8305
  if (json.skins === void 0) json.skins = [];
8168
8306
  json.skins.push({
@@ -8178,19 +8316,19 @@ var GLTFWriter = class {
8178
8316
  * @param {THREE.Object3D} object Object3D to processNodeAsync
8179
8317
  * @return {Promise<number>} Index of the node in the nodes list
8180
8318
  */
8181
- async processNodeAsync(object2) {
8319
+ async processNodeAsync(object3) {
8182
8320
  const json = this.json;
8183
8321
  const options = this.options;
8184
8322
  const nodeMap = this.nodeMap;
8185
8323
  if (!json.nodes) json.nodes = [];
8186
- if (object2.pivot !== null) {
8187
- return await this._processNodeWithPivotAsync(object2);
8324
+ if (object3.pivot !== null) {
8325
+ return await this._processNodeWithPivotAsync(object3);
8188
8326
  }
8189
8327
  const nodeDef = {};
8190
8328
  if (options.trs) {
8191
- const rotation = object2.quaternion.toArray();
8192
- const position = object2.position.toArray();
8193
- const scale = object2.scale.toArray();
8329
+ const rotation = object3.quaternion.toArray();
8330
+ const position = object3.position.toArray();
8331
+ const scale = object3.scale.toArray();
8194
8332
  if (!equalArray(rotation, [0, 0, 0, 1])) {
8195
8333
  nodeDef.rotation = rotation;
8196
8334
  }
@@ -8201,28 +8339,28 @@ var GLTFWriter = class {
8201
8339
  nodeDef.scale = scale;
8202
8340
  }
8203
8341
  } else {
8204
- if (object2.matrixAutoUpdate) {
8205
- object2.updateMatrix();
8342
+ if (object3.matrixAutoUpdate) {
8343
+ object3.updateMatrix();
8206
8344
  }
8207
- if (isIdentityMatrix(object2.matrix) === false) {
8208
- nodeDef.matrix = object2.matrix.elements;
8345
+ if (isIdentityMatrix(object3.matrix) === false) {
8346
+ nodeDef.matrix = object3.matrix.elements;
8209
8347
  }
8210
8348
  }
8211
- if (object2.name !== "") nodeDef.name = String(object2.name);
8212
- this.serializeUserData(object2, nodeDef);
8213
- if (object2.isMesh || object2.isLine || object2.isPoints) {
8214
- const meshIndex = await this.processMeshAsync(object2);
8349
+ if (object3.name !== "") nodeDef.name = String(object3.name);
8350
+ this.serializeUserData(object3, nodeDef);
8351
+ if (object3.isMesh || object3.isLine || object3.isPoints) {
8352
+ const meshIndex = await this.processMeshAsync(object3);
8215
8353
  if (meshIndex !== null) nodeDef.mesh = meshIndex;
8216
- } else if (object2.isCamera) {
8217
- nodeDef.camera = this.processCamera(object2);
8354
+ } else if (object3.isCamera) {
8355
+ nodeDef.camera = this.processCamera(object3);
8218
8356
  }
8219
- if (object2.isSkinnedMesh) this.skins.push(object2);
8357
+ if (object3.isSkinnedMesh) this.skins.push(object3);
8220
8358
  const nodeIndex = json.nodes.push(nodeDef) - 1;
8221
- nodeMap.set(object2, nodeIndex);
8222
- if (object2.children.length > 0) {
8359
+ nodeMap.set(object3, nodeIndex);
8360
+ if (object3.children.length > 0) {
8223
8361
  const children = [];
8224
- for (let i = 0, l = object2.children.length; i < l; i++) {
8225
- const child = object2.children[i];
8362
+ for (let i = 0, l = object3.children.length; i < l; i++) {
8363
+ const child = object3.children[i];
8226
8364
  if (child.visible || options.onlyVisible === false) {
8227
8365
  const childNodeIndex = await this.processNodeAsync(child);
8228
8366
  if (childNodeIndex !== null) children.push(childNodeIndex);
@@ -8231,7 +8369,7 @@ var GLTFWriter = class {
8231
8369
  if (children.length > 0) nodeDef.children = children;
8232
8370
  }
8233
8371
  await this._invokeAllAsync(function(ext) {
8234
- ext.writeNode && ext.writeNode(object2, nodeDef);
8372
+ ext.writeNode && ext.writeNode(object3, nodeDef);
8235
8373
  });
8236
8374
  return nodeIndex;
8237
8375
  }
@@ -8240,19 +8378,19 @@ var GLTFWriter = class {
8240
8378
  * @param {THREE.Object3D} object Object3D with pivot
8241
8379
  * @return {Promise<number>} Index of the container node
8242
8380
  */
8243
- async _processNodeWithPivotAsync(object2) {
8381
+ async _processNodeWithPivotAsync(object3) {
8244
8382
  const json = this.json;
8245
8383
  const options = this.options;
8246
8384
  const nodeMap = this.nodeMap;
8247
- const pivot = object2.pivot;
8385
+ const pivot = object3.pivot;
8248
8386
  const containerDef = {};
8249
- const rotation = object2.quaternion.toArray();
8387
+ const rotation = object3.quaternion.toArray();
8250
8388
  const position = [
8251
- object2.position.x + pivot.x,
8252
- object2.position.y + pivot.y,
8253
- object2.position.z + pivot.z
8389
+ object3.position.x + pivot.x,
8390
+ object3.position.y + pivot.y,
8391
+ object3.position.z + pivot.z
8254
8392
  ];
8255
- const scale = object2.scale.toArray();
8393
+ const scale = object3.scale.toArray();
8256
8394
  if (!equalArray(rotation, [0, 0, 0, 1])) {
8257
8395
  containerDef.rotation = rotation;
8258
8396
  }
@@ -8263,28 +8401,28 @@ var GLTFWriter = class {
8263
8401
  containerDef.scale = scale;
8264
8402
  }
8265
8403
  containerDef.extras = { pivot: pivot.toArray() };
8266
- if (object2.name !== "") containerDef.name = String(object2.name);
8267
- this.serializeUserData(object2, containerDef);
8404
+ if (object3.name !== "") containerDef.name = String(object3.name);
8405
+ this.serializeUserData(object3, containerDef);
8268
8406
  const containerIndex = json.nodes.push(containerDef) - 1;
8269
- nodeMap.set(object2, containerIndex);
8407
+ nodeMap.set(object3, containerIndex);
8270
8408
  const childDef = {};
8271
8409
  const childPosition = [-pivot.x, -pivot.y, -pivot.z];
8272
8410
  if (!equalArray(childPosition, [0, 0, 0])) {
8273
8411
  childDef.translation = childPosition;
8274
8412
  }
8275
- if (object2.isMesh || object2.isLine || object2.isPoints) {
8276
- const meshIndex = await this.processMeshAsync(object2);
8413
+ if (object3.isMesh || object3.isLine || object3.isPoints) {
8414
+ const meshIndex = await this.processMeshAsync(object3);
8277
8415
  if (meshIndex !== null) childDef.mesh = meshIndex;
8278
- } else if (object2.isCamera) {
8279
- childDef.camera = this.processCamera(object2);
8416
+ } else if (object3.isCamera) {
8417
+ childDef.camera = this.processCamera(object3);
8280
8418
  }
8281
- if (object2.isSkinnedMesh) this.skins.push(object2);
8419
+ if (object3.isSkinnedMesh) this.skins.push(object3);
8282
8420
  const childIndex = json.nodes.push(childDef) - 1;
8283
8421
  const containerChildren = [childIndex];
8284
- if (object2.children.length > 0) {
8422
+ if (object3.children.length > 0) {
8285
8423
  const grandchildren = [];
8286
- for (let i = 0, l = object2.children.length; i < l; i++) {
8287
- const child = object2.children[i];
8424
+ for (let i = 0, l = object3.children.length; i < l; i++) {
8425
+ const child = object3.children[i];
8288
8426
  if (child.visible || options.onlyVisible === false) {
8289
8427
  const childNodeIndex = await this.processNodeAsync(child);
8290
8428
  if (childNodeIndex !== null) grandchildren.push(childNodeIndex);
@@ -8294,7 +8432,7 @@ var GLTFWriter = class {
8294
8432
  }
8295
8433
  containerDef.children = containerChildren;
8296
8434
  await this._invokeAllAsync(function(ext) {
8297
- ext.writeNode && ext.writeNode(object2, containerDef);
8435
+ ext.writeNode && ext.writeNode(object3, containerDef);
8298
8436
  });
8299
8437
  return containerIndex;
8300
8438
  }
@@ -8737,10 +8875,10 @@ var GLTFMeshGpuInstancing = class {
8737
8875
  this.writer = writer;
8738
8876
  this.name = "EXT_mesh_gpu_instancing";
8739
8877
  }
8740
- writeNode(object2, nodeDef) {
8741
- if (!object2.isInstancedMesh) return;
8878
+ writeNode(object3, nodeDef) {
8879
+ if (!object3.isInstancedMesh) return;
8742
8880
  const writer = this.writer;
8743
- const mesh = object2;
8881
+ const mesh = object3;
8744
8882
  const translationAttr = new Float32Array(mesh.count * 3);
8745
8883
  const rotationAttr = new Float32Array(mesh.count * 4);
8746
8884
  const scaleAttr = new Float32Array(mesh.count * 3);
@@ -8930,8 +9068,8 @@ var SceneExporter = class {
8930
9068
  }
8931
9069
  }
8932
9070
  };
8933
- function localMatrix(object2) {
8934
- return object2.matrixAutoUpdate ? new Matrix47().compose(object2.position, object2.quaternion, object2.scale) : object2.matrix.clone();
9071
+ function localMatrix(object3) {
9072
+ return object3.matrixAutoUpdate ? new Matrix47().compose(object3.position, object3.quaternion, object3.scale) : object3.matrix.clone();
8935
9073
  }
8936
9074
  function createSnapshot(root, gltf, bakeColors = false) {
8937
9075
  const scene = new Scene3();
@@ -9035,24 +9173,24 @@ function createSnapshot(root, gltf, bakeColors = false) {
9035
9173
  scene.add(copy);
9036
9174
  }
9037
9175
  };
9038
- const visit = (object2, parentWorld) => {
9039
- if (!object2.visible) return;
9040
- const world = parentWorld.clone().multiply(localMatrix(object2));
9041
- const mesh = object2;
9176
+ const visit = (object3, parentWorld) => {
9177
+ if (!object3.visible) return;
9178
+ const world = parentWorld.clone().multiply(localMatrix(object3));
9179
+ const mesh = object3;
9042
9180
  if (mesh.isMesh) {
9043
9181
  const instanced = mesh;
9044
9182
  if (instanced.isInstancedMesh) {
9045
9183
  for (let i = 0; i < instanced.count; i++) {
9046
9184
  const matrix = new Matrix47();
9047
9185
  instanced.getMatrixAt(i, matrix);
9048
- const color = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
9049
- addMesh(mesh, world.clone().multiply(matrix), color);
9186
+ const color2 = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
9187
+ addMesh(mesh, world.clone().multiply(matrix), color2);
9050
9188
  }
9051
9189
  } else {
9052
9190
  addMesh(mesh, world);
9053
9191
  }
9054
9192
  }
9055
- for (const child of object2.children) visit(child, world);
9193
+ for (const child of object3.children) visit(child, world);
9056
9194
  };
9057
9195
  try {
9058
9196
  const ancestors = [];
@@ -9128,13 +9266,13 @@ init_AssetKey();
9128
9266
 
9129
9267
  // src/gui/GuiText.ts
9130
9268
  import { BufferGeometry as BufferGeometry2, Color as Color8, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
9131
- async function layoutGuiText(text, options = {}) {
9269
+ async function layoutGuiText(text2, options = {}) {
9132
9270
  const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
9133
9271
  if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
9134
9272
  throw new Error("Text wrap width and line height must be positive");
9135
9273
  }
9136
9274
  const font = await Fonts.get(options.font);
9137
- const runs = typeof text === "string" ? [{ text }] : text;
9275
+ const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
9138
9276
  const glyphs = [], lineWidths = [];
9139
9277
  let line = [], width = 0;
9140
9278
  let trailingNewline = false;
@@ -9188,7 +9326,7 @@ function createGuiTextGeometry(layout, shadow = true) {
9188
9326
  batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
9189
9327
  batches.push(batch);
9190
9328
  }
9191
- const color = new Color8(isShadow ? (style.color & 16579836) >> 2 : style.color);
9329
+ const color2 = new Color8(isShadow ? (style.color & 16579836) >> 2 : style.color);
9192
9330
  const offset = isShadow ? 1 : 0;
9193
9331
  const top = glyph ? 7 - glyph.ascent : 0;
9194
9332
  const height = glyph ? glyph.height * glyph.scale : 8;
@@ -9216,7 +9354,7 @@ function createGuiTextGeometry(layout, shadow = true) {
9216
9354
  const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
9217
9355
  const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
9218
9356
  batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
9219
- for (let i = 0; i < 4; i++) batch.colors.push(color.r, color.g, color.b);
9357
+ for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
9220
9358
  batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
9221
9359
  };
9222
9360
  for (const boldOffset of style.bold ? [0, 1] : [0]) {
@@ -9250,12 +9388,19 @@ var GuiHelper = class {
9250
9388
  size: [width + 24, height + 24]
9251
9389
  }));
9252
9390
  let y = position[1];
9253
- lines.forEach((text, index) => {
9254
- layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y] });
9391
+ lines.forEach((text2, index) => {
9392
+ layers.push({ ...textOptions, name: `tooltip-line-${index}`, text: text2, position: [position[0], y] });
9255
9393
  y += layouts[index].height + (index === 0 ? titleGap : 0);
9256
9394
  });
9257
9395
  return layers;
9258
9396
  }
9397
+ /**
9398
+ * Calculates a slot's top-left position in GUI pixels.
9399
+ * @param slot - Zero-based slot index, or `[column, row]`.
9400
+ * @param origin - Position of the first slot.
9401
+ * @param offset - Horizontal and vertical spacing between slot origins, in pixels.
9402
+ * @param rowSize - Slots per row when `slot` is an index.
9403
+ */
9259
9404
  static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
9260
9405
  const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
9261
9406
  return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
@@ -9700,29 +9845,29 @@ var SceneInspector = class {
9700
9845
  this.objectControlsContainer.innerHTML = "";
9701
9846
  this.addControls(targetObject, targetIntersection, instance);
9702
9847
  }
9703
- addControls(object2, intersection, instance) {
9848
+ addControls(object3, intersection, instance) {
9704
9849
  const container = document.createElement("div");
9705
9850
  container.append(this.separator("Select Parent/Child"));
9706
- if (object2.parent) {
9707
- container.append(this.buttonControl("Select Parent " + object2.parent.constructor.name + " " + object2.parent.name, "P", () => {
9708
- this.selectObject(object2.parent, intersection, instance);
9851
+ if (object3.parent) {
9852
+ container.append(this.buttonControl("Select Parent " + object3.parent.constructor.name + " " + object3.parent.name, "P", () => {
9853
+ this.selectObject(object3.parent, intersection, instance);
9709
9854
  }));
9710
9855
  }
9711
- if (object2.children.length > 0) {
9856
+ if (object3.children.length > 0) {
9712
9857
  let i = 1;
9713
- for (let child of object2.children) {
9858
+ for (let child of object3.children) {
9714
9859
  container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
9715
9860
  this.selectObject(child, intersection, instance);
9716
9861
  }));
9717
9862
  }
9718
9863
  }
9719
9864
  container.append(this.separator());
9720
- const selectedInstance = instance && (object2 === instance.instanceable || object2 === intersection?.object) ? instance : void 0;
9865
+ const selectedInstance = instance && (object3 === instance.instanceable || object3 === intersection?.object) ? instance : void 0;
9721
9866
  if (!selectedInstance) {
9722
- container.append(this.toggleControl("Visibility", "V", object2.visible, (v) => object2.visible = v));
9867
+ container.append(this.toggleControl("Visibility", "V", object3.visible, (v) => object3.visible = v));
9723
9868
  }
9724
9869
  container.append(this.separator("Mesh"));
9725
- this.addObjectControls(object2, intersection, container, selectedInstance);
9870
+ this.addObjectControls(object3, intersection, container, selectedInstance);
9726
9871
  container.append(this.separator());
9727
9872
  this.objectControlsContainer.append(container);
9728
9873
  }
@@ -9913,6 +10058,7 @@ var SceneStatsDisplay = class {
9913
10058
  appendTo(el) {
9914
10059
  el.append(this.statsContainer);
9915
10060
  }
10061
+ /** Stops the refresh timer and removes the panel from the page. */
9916
10062
  dispose() {
9917
10063
  clearInterval(this.interval);
9918
10064
  this.interval = void 0;
@@ -9937,6 +10083,7 @@ var InstanceManager = class {
9937
10083
  this.isInstanceManager = true;
9938
10084
  this.instanceCache = {};
9939
10085
  }
10086
+ /** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
9940
10087
  async get(key) {
9941
10088
  if (key in this.instanceCache) {
9942
10089
  console.debug(p9, "key in cache", key);
@@ -9944,6 +10091,7 @@ var InstanceManager = class {
9944
10091
  }
9945
10092
  return void 0;
9946
10093
  }
10094
+ /** Allocates a placement, calling the supplier once when the shared model is first needed. */
9947
10095
  async getOrCreate(key, supplier) {
9948
10096
  if (key in this.instanceCache) {
9949
10097
  return await this.get(key);
@@ -9960,6 +10108,7 @@ var InstanceManager = class {
9960
10108
  throw error;
9961
10109
  }
9962
10110
  }
10111
+ /** Forgets cached models without disposing them or releasing their existing placements. */
9963
10112
  reset() {
9964
10113
  for (let instanceCacheKey in this.instanceCache) {
9965
10114
  delete this.instanceCache[instanceCacheKey];
@@ -10005,6 +10154,7 @@ function matchesCondition(condition, state) {
10005
10154
  });
10006
10155
  }
10007
10156
  var BlockStateResolver = class {
10157
+ /** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
10008
10158
  static async defaults(blockState) {
10009
10159
  const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
10010
10160
  const state = {};
@@ -10024,9 +10174,11 @@ var BlockStateResolver = class {
10024
10174
  }
10025
10175
  return state;
10026
10176
  }
10177
+ /** Selects matching models. Supply `choose` to control selection within each alternatives array. */
10027
10178
  static select(blockState, state, choose = this.choose) {
10028
10179
  return this.matching(blockState, state).map(choose);
10029
10180
  }
10181
+ /** Returns matching model groups before choosing weighted alternatives. */
10030
10182
  static matching(blockState, state) {
10031
10183
  const out = [];
10032
10184
  if (blockState.variants) {
@@ -10050,6 +10202,7 @@ var BlockStateResolver = class {
10050
10202
  }
10051
10203
  return out;
10052
10204
  }
10205
+ /** Selects a random alternative using positive integer weights, each defaulting to 1. */
10053
10206
  static choose(variants) {
10054
10207
  if (!Array.isArray(variants)) return variants;
10055
10208
  if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
@@ -10067,6 +10220,7 @@ var BlockStateResolver = class {
10067
10220
  }
10068
10221
  return variants[variants.length - 1];
10069
10222
  }
10223
+ /** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
10070
10224
  static rotation(variant) {
10071
10225
  const rotation = new Euler8();
10072
10226
  if (typeof variant.x !== "undefined") {
@@ -10129,29 +10283,29 @@ var BlockTints = class {
10129
10283
  }
10130
10284
  }
10131
10285
  if (!indices.size) return tints;
10132
- let color;
10286
+ let color2;
10133
10287
  switch (rule.source) {
10134
10288
  case "constant":
10135
- color = rule.color;
10289
+ color2 = rule.color;
10136
10290
  break;
10137
10291
  case "grass":
10138
- color = await Colormaps.grassColor(key);
10292
+ color2 = await Colormaps.grassColor(key);
10139
10293
  break;
10140
10294
  case "redstone": {
10141
10295
  const power = Number(state.power ?? 0) / 15;
10142
10296
  const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
10143
10297
  const green = Math.max(0, power * power * 0.7 - 0.5);
10144
10298
  const blue = Math.max(0, power * power * 0.6 - 0.7);
10145
- color = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
10299
+ color2 = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
10146
10300
  break;
10147
10301
  }
10148
10302
  case "stem": {
10149
10303
  const age = Number(state.age ?? 0);
10150
- color = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
10304
+ color2 = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
10151
10305
  break;
10152
10306
  }
10153
10307
  }
10154
- return { ...Object.fromEntries([...indices].map((index) => [index, color])), ...tints };
10308
+ return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
10155
10309
  }
10156
10310
  };
10157
10311
 
@@ -10288,8 +10442,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
10288
10442
  return false;
10289
10443
  }
10290
10444
  return this._models.some((model) => {
10291
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10292
- return object2.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
10445
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10446
+ return object3.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
10293
10447
  });
10294
10448
  }
10295
10449
  getModelMatrix(model) {
@@ -10298,8 +10452,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
10298
10452
  return model.matrix.clone();
10299
10453
  }
10300
10454
  getModelCullMask(model, worldMask) {
10301
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10302
- if (object2.options.displayPosition) return 0;
10455
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10456
+ if (object3.options.displayPosition) return 0;
10303
10457
  const position = new Vector312();
10304
10458
  const rotation = new Quaternion5();
10305
10459
  const scale = new Vector312();
@@ -10316,14 +10470,14 @@ var _BlockObject = class _BlockObject extends SceneObject {
10316
10470
  replacements.push(model);
10317
10471
  continue;
10318
10472
  }
10319
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10473
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10320
10474
  const cullMask = this.getModelCullMask(model, worldMask);
10321
- if (cullMask === (object2.options.cullMask ?? 0)) {
10475
+ if (cullMask === (object3.options.cullMask ?? 0)) {
10322
10476
  replacements.push(model);
10323
10477
  continue;
10324
10478
  }
10325
10479
  const matrix = this.getModelMatrix(model);
10326
- const replacement = await this.scene.addModel(object2.originalModel, { ...object2.options, cullMask });
10480
+ const replacement = await this.scene.addModel(object3.originalModel, { ...object3.options, cullMask });
10327
10481
  replacements.push(replacement);
10328
10482
  if (isInstanceReference(replacement)) {
10329
10483
  replacement.setMatrix(matrix);
@@ -10331,8 +10485,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
10331
10485
  replacement.matrix.copy(matrix);
10332
10486
  replacement.matrixWorldNeedsUpdate = true;
10333
10487
  matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
10334
- replacement.matrixAutoUpdate = object2.matrixAutoUpdate;
10335
- replacement.visible = object2.visible;
10488
+ replacement.matrixAutoUpdate = object3.matrixAutoUpdate;
10489
+ replacement.visible = object3.visible;
10336
10490
  }
10337
10491
  }
10338
10492
  } catch (error) {
@@ -10348,6 +10502,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
10348
10502
  }
10349
10503
  this.notifyDirty();
10350
10504
  }
10505
+ /** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
10351
10506
  get state() {
10352
10507
  return this._state;
10353
10508
  }
@@ -10447,6 +10602,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
10447
10602
  obj.setPosition(this.position);
10448
10603
  return obj;
10449
10604
  }
10605
+ /** Clears selected properties and rebuilds models without reapplying property defaults. */
10450
10606
  async resetState() {
10451
10607
  this._previousState = this._state;
10452
10608
  this._state = {};
@@ -12350,6 +12506,11 @@ function compoundList(tags, name) {
12350
12506
  // src/model/multiblock/StructureParser.ts
12351
12507
  init_MineRenderError();
12352
12508
  var StructureParser = class {
12509
+ /**
12510
+ * Parses decoded NBT, omitting air blocks.
12511
+ * @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
12512
+ * @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
12513
+ */
12353
12514
  static async parse(_nbt, paletteIndex) {
12354
12515
  const nbt = _nbt.value;
12355
12516
  let palette;
@@ -13184,6 +13345,7 @@ var SkinImage = class {
13184
13345
  // src/skin/SkinTextures.ts
13185
13346
  init_Textures();
13186
13347
  var SkinTextures = class {
13348
+ /** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
13187
13349
  static async get(src, legacy) {
13188
13350
  const image = await SkinImage.getData(src);
13189
13351
  const scale = image.width / 64;
@@ -13357,6 +13519,14 @@ var SkinObject = class extends SceneObject {
13357
13519
  }
13358
13520
  }
13359
13521
  }
13522
+ /**
13523
+ * Loads a skin texture, detecting slim arms and legacy layouts by default.
13524
+ * To override detection, set `slim` or `legacy` in the options passed to
13525
+ * `new SkinObject(options)` or `scene.addSkin(src, options)`.
13526
+ * Use {@link setSlim} or {@link setLegacy} to change these settings later.
13527
+ *
13528
+ * @param src - URL of the skin texture.
13529
+ */
13360
13530
  async setSkinTexture(src) {
13361
13531
  if (typeof src === "undefined") return;
13362
13532
  this.skinTextureSrc = src;
@@ -13464,59 +13634,60 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
13464
13634
  this.options = (0, import_ts_deepmerge8.default)({}, _MineRenderScene.DEFAULT_OPTIONS, options ?? {});
13465
13635
  }
13466
13636
  add(...objects) {
13467
- for (const object2 of objects) {
13637
+ for (const object3 of objects) {
13468
13638
  this.dirty = true;
13469
13639
  try {
13470
- super.add(object2);
13640
+ super.add(object3);
13471
13641
  } finally {
13472
- if (object2?.isObject3D && object2 !== this) {
13473
- this.updateObjectRegistration(object2, object2.parent === this);
13642
+ if (object3?.isObject3D && object3 !== this) {
13643
+ this.updateObjectRegistration(object3, object3.parent === this);
13474
13644
  }
13475
13645
  }
13476
13646
  }
13477
13647
  return this;
13478
13648
  }
13479
13649
  remove(...objects) {
13480
- for (const object2 of objects) {
13481
- if (!this.children.includes(object2)) continue;
13650
+ for (const object3 of objects) {
13651
+ if (!this.children.includes(object3)) continue;
13482
13652
  this.dirty = true;
13483
- this.updateObjectRegistration(object2, false);
13653
+ this.updateObjectRegistration(object3, false);
13484
13654
  try {
13485
- super.remove(object2);
13655
+ super.remove(object3);
13486
13656
  } finally {
13487
- this.updateObjectRegistration(object2, object2.parent === this);
13657
+ this.updateObjectRegistration(object3, object3.parent === this);
13488
13658
  }
13489
13659
  }
13490
13660
  return this;
13491
13661
  }
13492
- updateObjectRegistration(object2, attached) {
13493
- if (this.observedObjects.has(object2) === attached) return;
13494
- const observable = object2;
13662
+ updateObjectRegistration(object3, attached) {
13663
+ if (this.observedObjects.has(object3) === attached) return;
13664
+ const observable = object3;
13495
13665
  if (attached) {
13496
- this.observedObjects.add(object2);
13666
+ this.observedObjects.add(object3);
13497
13667
  observable.addEventListener("change", this.onObjectChange);
13498
13668
  } else {
13499
- this.observedObjects.delete(object2);
13669
+ this.observedObjects.delete(object3);
13500
13670
  observable.removeEventListener("change", this.onObjectChange);
13501
13671
  }
13502
13672
  const change = attached ? 1 : -1;
13503
13673
  this.stats.objectCount += change;
13504
- if (isSceneObject(object2)) {
13674
+ if (isSceneObject(object3)) {
13505
13675
  this.stats.sceneObjectCount += change;
13506
13676
  }
13507
13677
  }
13508
- async initAndAdd(...object2) {
13678
+ /** Initializes each object before attaching it to the scene. */
13679
+ async initAndAdd(...object3) {
13509
13680
  this.dirty = true;
13510
- for (let obj of object2) {
13681
+ for (let obj of object3) {
13511
13682
  await obj.init();
13512
13683
  }
13513
- return this.add(...object2);
13684
+ return this.add(...object3);
13514
13685
  }
13515
- async addSceneObject(asset, objectSupplier, _options, parent = this) {
13686
+ async addSceneObject(asset2, objectSupplier, _options, parent = this) {
13516
13687
  this.dirty = true;
13517
13688
  const obj = await objectSupplier();
13518
- if (obj?.options?.instanceMeshes && asset.key && asset.key?.assetType === "models") {
13519
- let key = asset.key.serialize();
13689
+ if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
13690
+ let key = asset2.key.serialize();
13520
13691
  if (isModelObject(obj)) {
13521
13692
  const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
13522
13693
  if (displayPosition) key += `|display:${displayPosition}`;
@@ -13540,13 +13711,33 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
13540
13711
  return obj;
13541
13712
  }
13542
13713
  }
13714
+ /**
13715
+ * Adds a model loaded with {@link Models.getMerged}.
13716
+ * Set `options.instanceMeshes` to `false` to create a separate ModelObject.
13717
+ *
13718
+ * @param parent - Object to attach the model to. Defaults to this scene.
13719
+ * @returns The model, or a reference to one placement of a shared model.
13720
+ */
13543
13721
  async addModel(model, options, parent = this) {
13544
13722
  options = { ...options, tints: await ItemTints.get(model, options?.tints) };
13545
13723
  return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
13546
13724
  }
13725
+ /**
13726
+ * Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
13727
+ * Set `options.initialState` to choose block properties such as `facing` or `axis`.
13728
+ * Use the returned object's `setPosition` method to move all of its model parts.
13729
+ */
13547
13730
  async addBlock(blockState, options, parent = this) {
13548
13731
  return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
13549
13732
  }
13733
+ /**
13734
+ * Loads a skin and adds an initialized player model.
13735
+ *
13736
+ * @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
13737
+ * Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
13738
+ * @param options - Player settings, including `slim` and `legacy` detection overrides.
13739
+ * @param parent - Object to attach the player to. Defaults to this scene.
13740
+ */
13550
13741
  async addSkin(skin, options, parent = this) {
13551
13742
  this.dirty = true;
13552
13743
  const obj = new SkinObject(options);
@@ -13558,9 +13749,14 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
13558
13749
  parent.add(obj);
13559
13750
  return obj;
13560
13751
  }
13752
+ /** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
13561
13753
  async addEntity(entity, options, parent = this) {
13562
13754
  return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
13563
13755
  }
13756
+ /**
13757
+ * Adds GUI layers in draw order. One GUI pixel equals one scene unit.
13758
+ * Layer positions start at the top left, with positive GUI y pointing down.
13759
+ */
13564
13760
  async addGui(layers, options, parent = this) {
13565
13761
  const obj = new GuiObject(layers, options);
13566
13762
  obj.scene = this;
@@ -13599,11 +13795,11 @@ var _changeEvent = { type: "change" };
13599
13795
  var _startEvent = { type: "start" };
13600
13796
  var _endEvent = { type: "end" };
13601
13797
  var OrbitControls = class extends EventDispatcher {
13602
- constructor(object2, domElement) {
13798
+ constructor(object3, domElement) {
13603
13799
  super();
13604
13800
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
13605
13801
  if (domElement === document) console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
13606
- this.object = object2;
13802
+ this.object = object3;
13607
13803
  this.domElement = domElement;
13608
13804
  this.domElement.style.touchAction = "none";
13609
13805
  this.enabled = true;
@@ -13660,7 +13856,7 @@ var OrbitControls = class extends EventDispatcher {
13660
13856
  };
13661
13857
  this.update = function() {
13662
13858
  const offset = new Vector313();
13663
- const quat = new Quaternion6().setFromUnitVectors(object2.up, new Vector313(0, 1, 0));
13859
+ const quat = new Quaternion6().setFromUnitVectors(object3.up, new Vector313(0, 1, 0));
13664
13860
  const quatInverse = quat.clone().invert();
13665
13861
  const lastPosition = new Vector313();
13666
13862
  const lastQuaternion = new Quaternion6();
@@ -14225,8 +14421,8 @@ var OrbitControls = class extends EventDispatcher {
14225
14421
  }
14226
14422
  };
14227
14423
  var MapControls = class extends OrbitControls {
14228
- constructor(object2, domElement) {
14229
- super(object2, domElement);
14424
+ constructor(object3, domElement) {
14425
+ super(object3, domElement);
14230
14426
  this.screenSpacePanning = false;
14231
14427
  this.mouseButtons.LEFT = MOUSE.PAN;
14232
14428
  this.mouseButtons.RIGHT = MOUSE.ROTATE;
@@ -14385,6 +14581,7 @@ var _Renderer = class _Renderer {
14385
14581
  get element() {
14386
14582
  return this._element;
14387
14583
  }
14584
+ /** Attaches the canvas to an element and sizes it to that element's dimensions. */
14388
14585
  appendTo(element) {
14389
14586
  if (this._disposed) return;
14390
14587
  this._element = element;
@@ -14412,6 +14609,7 @@ var _Renderer = class _Renderer {
14412
14609
  events.add(changeEvent2);
14413
14610
  dispatcher.addEventListener(changeEvent2, this._changeListener);
14414
14611
  }
14612
+ /** Updates the camera and canvas size. Width and height are in CSS pixels. */
14415
14613
  resize(width, height) {
14416
14614
  if (this._disposed) return;
14417
14615
  if (isPerspectiveCamera(this.camera)) {
@@ -14450,12 +14648,14 @@ var _Renderer = class _Renderer {
14450
14648
  if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
14451
14649
  };
14452
14650
  }
14651
+ /** Starts or resumes the animation loop and registered frame callbacks. */
14453
14652
  start() {
14454
14653
  if (this._disposed) return;
14455
14654
  this.stop();
14456
14655
  this._running = true;
14457
14656
  if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
14458
14657
  }
14658
+ /** Pauses rendering and frame callbacks. Call {@link start} to resume. */
14459
14659
  stop() {
14460
14660
  if (this._disposed) return;
14461
14661
  this._running = false;
@@ -14564,12 +14764,15 @@ var _Renderer = class _Renderer {
14564
14764
  const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
14565
14765
  return canvas.toDataURL(mime, quality);
14566
14766
  }
14767
+ /** Exports visible scene meshes as OBJ text, without texture images. */
14567
14768
  toObj() {
14568
14769
  return SceneExporter.toObj(this.scene);
14569
14770
  }
14771
+ /** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
14570
14772
  toPLY(options) {
14571
14773
  return SceneExporter.toPLY(this.scene, options);
14572
14774
  }
14775
+ /** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
14573
14776
  toGLTF(options) {
14574
14777
  return SceneExporter.toGLTF(this.scene, options);
14575
14778
  }
@@ -14647,16 +14850,244 @@ init_SceneObject();
14647
14850
  // src/index.ts
14648
14851
  init_request();
14649
14852
 
14853
+ // src/scene/SceneDocument.ts
14854
+ init_DisplayPosition();
14855
+ function fail(path, message) {
14856
+ throw new Error(`${path}: ${message}`);
14857
+ }
14858
+ function object2(value, path) {
14859
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, "expected a JSON object");
14860
+ for (const key of Object.keys(value)) {
14861
+ if (["__proto__", "prototype", "constructor"].includes(key)) fail(`${path}.${key}`, "unsupported property");
14862
+ }
14863
+ return value;
14864
+ }
14865
+ function fields(value, path, allowed, required = []) {
14866
+ const input = object2(value, path);
14867
+ for (const key of required) if (input[key] === void 0) fail(`${path}.${key}`, "required");
14868
+ for (const [key, entry] of Object.entries(input)) {
14869
+ if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, "unsupported property");
14870
+ if (entry !== void 0) allowed[key](entry, `${path}.${key}`);
14871
+ }
14872
+ return input;
14873
+ }
14874
+ var text = (value, path) => {
14875
+ if (typeof value !== "string" || !value.trim()) fail(path, "expected a nonempty string");
14876
+ };
14877
+ var literalText = (value, path) => {
14878
+ if (typeof value !== "string") fail(path, "expected a string");
14879
+ };
14880
+ var boolean = (value, path) => {
14881
+ if (typeof value !== "boolean") fail(path, "expected a boolean");
14882
+ };
14883
+ var number = (value, path) => {
14884
+ if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number");
14885
+ };
14886
+ var positive = (value, path) => {
14887
+ number(value, path);
14888
+ if (value <= 0) fail(path, "expected a positive number");
14889
+ };
14890
+ var nonnegative = (value, path) => {
14891
+ number(value, path);
14892
+ if (value < 0) fail(path, "expected a nonnegative number");
14893
+ };
14894
+ var color = (value, path) => {
14895
+ number(value, path);
14896
+ if (!Number.isInteger(value) || value < 0 || value > 16777215) {
14897
+ fail(path, "expected an RGB integer from 0 to 16777215");
14898
+ }
14899
+ };
14900
+ var asset = (value, path) => {
14901
+ text(value, path);
14902
+ if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
14903
+ fail(path, "expected a resource ID such as minecraft:stone");
14904
+ }
14905
+ };
14906
+ var version = (value, path) => {
14907
+ text(value, path);
14908
+ if (!/^[a-zA-Z0-9._-]+$/.test(value)) fail(path, "expected a Minecraft version without slashes");
14909
+ };
14910
+ var tuple = (size, item = number) => (value, path) => {
14911
+ if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);
14912
+ for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);
14913
+ };
14914
+ var list = (item) => (value, path) => {
14915
+ if (!Array.isArray(value)) fail(path, "expected an array");
14916
+ for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);
14917
+ };
14918
+ var dictionary = (item, numericKeys = false) => (value, path) => {
14919
+ for (const [key, entry] of Object.entries(object2(value, path))) {
14920
+ if (numericKeys && !/^\d+$/.test(key)) fail(`${path}.${key}`, "expected a nonnegative tint index");
14921
+ item(entry, `${path}.${key}`);
14922
+ }
14923
+ };
14924
+ var enumeration = (values) => (value, path) => {
14925
+ if (!values.includes(value)) fail(path, `expected one of: ${values.join(", ")}`);
14926
+ };
14927
+ var textStyle = { color, bold: boolean, italic: boolean };
14928
+ function guiLayer(value, path) {
14929
+ const layer = object2(value, path);
14930
+ if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
14931
+ fail(path, "expected exactly one of texture, item, or text");
14932
+ }
14933
+ const layout = { name: text, position: tuple(2), size: tuple(2, positive) };
14934
+ if (layer.texture !== void 0) {
14935
+ fields(value, path, { ...layout, texture: asset, crop: (value2, path2) => {
14936
+ tuple(4)(value2, path2);
14937
+ const [x, y, width, height] = value2;
14938
+ nonnegative(x, `${path2}[0]`);
14939
+ nonnegative(y, `${path2}[1]`);
14940
+ positive(width, `${path2}[2]`);
14941
+ positive(height, `${path2}[3]`);
14942
+ } });
14943
+ } else if (layer.item !== void 0) {
14944
+ fields(value, path, { ...layout, item: asset, tints: dictionary(color, true) });
14945
+ } else {
14946
+ fields(value, path, {
14947
+ ...layout,
14948
+ ...textStyle,
14949
+ font: asset,
14950
+ shadow: boolean,
14951
+ maxWidth: positive,
14952
+ lineHeight: positive,
14953
+ text: (value2, path2) => {
14954
+ if (typeof value2 === "string") return;
14955
+ list((run, path3) => {
14956
+ fields(run, path3, { text: literalText, ...textStyle }, ["text"]);
14957
+ })(value2, path2);
14958
+ }
14959
+ });
14960
+ }
14961
+ }
14962
+ function validateObject(value, path) {
14963
+ const input = object2(value, path);
14964
+ const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };
14965
+ const required = ["id", "type"];
14966
+ const renderOptions = { wireframe: boolean };
14967
+ switch (input.type) {
14968
+ case "skin":
14969
+ fields(value, path, {
14970
+ ...common,
14971
+ skin: text,
14972
+ cape: (value2, path2) => {
14973
+ fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
14974
+ },
14975
+ hiddenParts: list(enumeration(SKIN_PARTS)),
14976
+ pose: (value2, path2) => {
14977
+ fields(value2, path2, {
14978
+ head: tuple(3),
14979
+ body: tuple(3),
14980
+ rightArm: tuple(3),
14981
+ leftArm: tuple(3),
14982
+ rightLeg: tuple(3),
14983
+ leftLeg: tuple(3),
14984
+ cape: tuple(3)
14985
+ });
14986
+ },
14987
+ options: (value2, path2) => {
14988
+ fields(value2, path2, { slim: boolean, legacy: boolean });
14989
+ }
14990
+ }, required);
14991
+ break;
14992
+ case "block":
14993
+ fields(value, path, {
14994
+ ...common,
14995
+ asset,
14996
+ state: dictionary(text),
14997
+ options: (value2, path2) => {
14998
+ fields(value2, path2, { ...renderOptions, tints: dictionary(color, true) });
14999
+ }
15000
+ }, [...required, "asset"]);
15001
+ break;
15002
+ case "item":
15003
+ case "model":
15004
+ fields(value, path, {
15005
+ ...common,
15006
+ asset,
15007
+ options: (value2, path2) => {
15008
+ fields(value2, path2, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) });
15009
+ }
15010
+ }, [...required, "asset"]);
15011
+ break;
15012
+ case "entity":
15013
+ fields(value, path, {
15014
+ ...common,
15015
+ asset,
15016
+ texture: asset,
15017
+ layers: list(text),
15018
+ when: list(text),
15019
+ textures: dictionary(asset),
15020
+ options: (value2, path2) => {
15021
+ fields(value2, path2, {
15022
+ ...renderOptions,
15023
+ flip: boolean,
15024
+ tints: dictionary((value3, path3) => {
15025
+ typeof value3 === "string" ? text(value3, path3) : color(value3, path3);
15026
+ })
15027
+ });
15028
+ },
15029
+ animation: (value2, path2) => {
15030
+ fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
15031
+ }
15032
+ }, [...required, "asset"]);
15033
+ if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
15034
+ break;
15035
+ case "gui":
15036
+ fields(value, path, { ...common, layers: list(guiLayer) }, [...required, "layers"]);
15037
+ break;
15038
+ default:
15039
+ fail(`${path}.type`, "expected skin, block, item, entity, model, or gui");
15040
+ }
15041
+ }
15042
+ function parseSceneDocument(value) {
15043
+ if (typeof value === "string") {
15044
+ try {
15045
+ value = JSON.parse(value);
15046
+ } catch {
15047
+ fail("scene", "invalid JSON");
15048
+ }
15049
+ }
15050
+ const input = fields(value, "scene", {
15051
+ format: enumeration(["minerender-scene"]),
15052
+ version: (value2, path) => {
15053
+ if (value2 !== 1) fail(path, "only scene document version 1 is supported");
15054
+ },
15055
+ minecraftVersion: version,
15056
+ camera: (value2, path) => {
15057
+ fields(value2, path, { position: tuple(3), target: tuple(3) }, ["position", "target"]);
15058
+ },
15059
+ objects: list(validateObject)
15060
+ }, ["format", "version", "objects"]);
15061
+ const ids = /* @__PURE__ */ new Set();
15062
+ for (const [index, entry] of input.objects.entries()) {
15063
+ if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID "${entry.id}"`);
15064
+ ids.add(entry.id);
15065
+ }
15066
+ return JSON.parse(JSON.stringify(input));
15067
+ }
15068
+
15069
+ // src/scene/SceneDocumentLoader.ts
15070
+ init_AssetKey();
15071
+ init_AssetLoader();
15072
+ init_BlockStates();
15073
+ init_Entities();
15074
+ init_Models();
15075
+ init_DisplayPosition();
15076
+ import { Group as Group2 } from "three";
15077
+ init_SceneObject();
15078
+
14650
15079
  // src/skin/Skins.ts
14651
15080
  init_Requests();
14652
15081
  var Skins = class {
14653
15082
  //<editor-fold desc="Skins">
15083
+ /** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
14654
15084
  static async fromUuidOrUsername(uuidOrUsername) {
14655
15085
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14656
15086
  return this.fromUuid(uuidOrUsername);
14657
15087
  }
14658
15088
  return this.fromUsername(uuidOrUsername);
14659
15089
  }
15090
+ /** Builds a proxy URL for a UUID without downloading or checking the skin. */
14660
15091
  static async fromUuid(uuid) {
14661
15092
  return this.getMcProxySkinUrl(uuid);
14662
15093
  }
@@ -14668,6 +15099,7 @@ var Skins = class {
14668
15099
  return void 0;
14669
15100
  });
14670
15101
  }
15102
+ /** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
14671
15103
  static async fromMineSkin(mineskinId) {
14672
15104
  return this.getMineSkin(mineskinId).then((mineskin) => {
14673
15105
  if (mineskin) {
@@ -14678,12 +15110,14 @@ var Skins = class {
14678
15110
  }
14679
15111
  //</editor-fold>
14680
15112
  //<editor-fold desc="Capes">
15113
+ /** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
14681
15114
  static async capeFromUuidOrUsername(uuidOrUsername) {
14682
15115
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14683
15116
  return this.capeFromUuid(uuidOrUsername);
14684
15117
  }
14685
15118
  return this.capeFromUsername(uuidOrUsername);
14686
15119
  }
15120
+ /** Builds a proxy URL without checking whether the player has a cape. */
14687
15121
  static async capeFromUuid(uuid) {
14688
15122
  return this.getMcProxyCapeUrl(uuid);
14689
15123
  }
@@ -14812,6 +15246,195 @@ var Skins = class {
14812
15246
  //</editor-fold>
14813
15247
  };
14814
15248
 
15249
+ // src/scene/SceneDocumentLoader.ts
15250
+ var DocumentObjectScene = class extends MineRenderScene {
15251
+ async addSceneObject(_asset, supplier, _options, parent = this) {
15252
+ const object3 = await supplier();
15253
+ return this.initialize(object3, parent);
15254
+ }
15255
+ async initialize(object3, parent = this) {
15256
+ object3.scene = this;
15257
+ try {
15258
+ await object3.init();
15259
+ parent.add(object3);
15260
+ return object3;
15261
+ } catch (error) {
15262
+ object3.dispose();
15263
+ object3.removeFromParent();
15264
+ throw error;
15265
+ }
15266
+ }
15267
+ dispose() {
15268
+ for (const child of [...this.children]) {
15269
+ if (child.parent !== this) continue;
15270
+ if (isSceneObject(child)) child.dispose();
15271
+ child.removeFromParent();
15272
+ }
15273
+ }
15274
+ };
15275
+ async function skinTexture(value, cape = false) {
15276
+ if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;
15277
+ const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));
15278
+ if (!texture) throw new Error(`Could not find ${cape ? "cape" : "skin"} for "${value}"`);
15279
+ return texture;
15280
+ }
15281
+ var SceneDocumentLoader = class {
15282
+ static parse(value) {
15283
+ return parseSceneDocument(value);
15284
+ }
15285
+ /**
15286
+ * Loads objects concurrently and attaches them in document order after every load succeeds.
15287
+ * A failure waits for all loads, disposes staged objects, and reports the first error in document order.
15288
+ */
15289
+ static async load(scene, value, parent = scene) {
15290
+ const definition = this.parse(value);
15291
+ if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
15292
+ throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
15293
+ }
15294
+ const root = new Group2();
15295
+ root.name = "MineRender scene document";
15296
+ const objects = [];
15297
+ let disposed = false;
15298
+ const loaded = {
15299
+ root,
15300
+ objects,
15301
+ definition,
15302
+ advanceAnimations: (delta) => {
15303
+ if (!disposed) for (const object3 of objects) object3.advanceAnimation(delta);
15304
+ },
15305
+ dispose: () => {
15306
+ if (disposed) return;
15307
+ disposed = true;
15308
+ root.removeFromParent();
15309
+ for (const object3 of objects) object3.dispose();
15310
+ root.clear();
15311
+ scene.dirty = true;
15312
+ }
15313
+ };
15314
+ try {
15315
+ const results = await Promise.allSettled(definition.objects.map((object3) => this.loadValidatedObject(scene, object3)));
15316
+ for (const result of results) if (result.status === "fulfilled") objects.push(result.value);
15317
+ const failure = results.find((result) => result.status === "rejected");
15318
+ if (failure) throw failure.reason;
15319
+ for (const object3 of objects) root.add(object3.root);
15320
+ parent.add(root);
15321
+ scene.dirty = true;
15322
+ return loaded;
15323
+ } catch (error) {
15324
+ loaded.dispose();
15325
+ throw error;
15326
+ }
15327
+ }
15328
+ /** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */
15329
+ static async loadObject(scene, value, parent = scene) {
15330
+ const definition = this.parse({ format: "minerender-scene", version: 1, objects: [value] }).objects[0];
15331
+ return this.loadValidatedObject(scene, definition, parent);
15332
+ }
15333
+ static async loadValidatedObject(scene, definition, parent) {
15334
+ const root = new Group2();
15335
+ root.name = definition.name ?? definition.id;
15336
+ root.userData.sceneObjectId = definition.id;
15337
+ root.position.fromArray(definition.position ?? [0, 0, 0]);
15338
+ root.rotation.set(...definition.rotation ?? [0, 0, 0]);
15339
+ root.scale.fromArray(definition.scale ?? [1, 1, 1]);
15340
+ root.visible = definition.visible ?? true;
15341
+ const content = new DocumentObjectScene();
15342
+ root.add(content);
15343
+ let object3;
15344
+ try {
15345
+ switch (definition.type) {
15346
+ case "skin": {
15347
+ const skin = new SkinObject({ ...definition.options, instanceMeshes: false });
15348
+ object3 = skin;
15349
+ const texture = definition.skin ? await skinTexture(definition.skin) : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? "slim/alex" : "wide/steve"}.png`;
15350
+ await skin.setSkinTexture(texture);
15351
+ await content.initialize(skin);
15352
+ if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
15353
+ for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
15354
+ skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
15355
+ }
15356
+ for (const part of definition.hiddenParts ?? []) {
15357
+ const mesh = skin.getMeshByName(part);
15358
+ if (mesh) mesh.visible = false;
15359
+ }
15360
+ break;
15361
+ }
15362
+ case "block": {
15363
+ const state = await BlockStates.get(AssetKey.parse("blockstates", definition.asset));
15364
+ if (!state) throw new Error(`Could not load block ${definition.asset}`);
15365
+ object3 = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false });
15366
+ break;
15367
+ }
15368
+ case "item":
15369
+ case "model": {
15370
+ let key = AssetKey.parse("models", definition.asset);
15371
+ if (definition.type === "item" && key.type !== "item") {
15372
+ key = new AssetKey(key.namespace, key.getFullPath(), "models", "item");
15373
+ }
15374
+ const model = await Models.getMerged(key);
15375
+ if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);
15376
+ object3 = await content.addModel(model, {
15377
+ ...definition.type === "item" ? { displayPosition: "gui" /* GUI */ } : {},
15378
+ ...definition.options,
15379
+ instanceMeshes: false
15380
+ });
15381
+ break;
15382
+ }
15383
+ case "entity": {
15384
+ const key = AssetKey.parse("entity-models", definition.asset);
15385
+ const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse("textures", definition.texture) : void 0, {
15386
+ layers: definition.layers,
15387
+ when: definition.when,
15388
+ textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse("textures", texture)]))
15389
+ });
15390
+ if (!model) throw new Error(`Could not load entity ${definition.asset}`);
15391
+ const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
15392
+ object3 = entity;
15393
+ if (definition.animation) {
15394
+ const animations = await Entities.getAnimations(key);
15395
+ const clip = animations?.[definition.animation.name];
15396
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
15397
+ entity.playAnimation(clip, definition.animation);
15398
+ }
15399
+ break;
15400
+ }
15401
+ case "gui":
15402
+ object3 = await content.initialize(new GuiObject(definition.layers));
15403
+ break;
15404
+ }
15405
+ let disposed = false;
15406
+ const loaded = {
15407
+ root,
15408
+ object: object3,
15409
+ definition,
15410
+ advanceAnimation: (delta) => {
15411
+ if (!disposed && definition.type === "entity" && !definition.animation?.paused) object3.advanceAnimation(delta);
15412
+ },
15413
+ dispose: () => {
15414
+ if (disposed) return;
15415
+ disposed = true;
15416
+ root.removeFromParent();
15417
+ object3.dispose();
15418
+ object3.removeFromParent();
15419
+ content.dispose();
15420
+ root.clear();
15421
+ scene.dirty = true;
15422
+ }
15423
+ };
15424
+ parent?.add(root);
15425
+ scene.dirty = true;
15426
+ return loaded;
15427
+ } catch (error) {
15428
+ object3?.dispose();
15429
+ object3?.removeFromParent();
15430
+ content.dispose();
15431
+ root.removeFromParent();
15432
+ root.clear();
15433
+ throw new Error(`Scene object "${definition.name ?? definition.id}": ${error instanceof Error ? error.message : String(error)}`);
15434
+ }
15435
+ }
15436
+ };
15437
+
14815
15438
  // src/texture/index.ts
14816
15439
  init_Colormaps();
14817
15440
  init_TextureAtlas();
@@ -14838,16 +15461,19 @@ var ChunkData = class _ChunkData {
14838
15461
  this.freeIds = [];
14839
15462
  this.nbt = /* @__PURE__ */ new Map();
14840
15463
  }
15464
+ /** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
14841
15465
  get(index) {
14842
15466
  _ChunkData.validateIndex(index);
14843
15467
  const entry = this.palette[this.indices[index]];
14844
15468
  return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
14845
15469
  }
15470
+ /** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
14846
15471
  snapshot(index) {
14847
15472
  _ChunkData.validateIndex(index);
14848
15473
  const entry = this.palette[this.indices[index]];
14849
15474
  return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
14850
15475
  }
15476
+ /** Copies block data into a cell. An omitted block or an air block clears the cell. */
14851
15477
  set(index, block) {
14852
15478
  _ChunkData.validateIndex(index);
14853
15479
  let state;
@@ -14912,15 +15538,23 @@ var ChunkData = class _ChunkData {
14912
15538
 
14913
15539
  // src/world/AnvilParser.ts
14914
15540
  var AnvilParser = class {
15541
+ /** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
14915
15542
  static getChunkList(data) {
14916
15543
  return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
14917
15544
  }
15545
+ /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
14918
15546
  static async parse(data) {
14919
15547
  const bytes = this.bytes(data);
14920
15548
  const chunks = [];
14921
15549
  for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
14922
15550
  return { chunks };
14923
15551
  }
15552
+ /**
15553
+ * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
15554
+ * @param localX - Chunk x within the region, from 0 to 31.
15555
+ * @param localZ - Chunk z within the region, from 0 to 31.
15556
+ * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
15557
+ */
14924
15558
  static async parseChunk(data, localX, localZ) {
14925
15559
  if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
14926
15560
  throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
@@ -15125,6 +15759,7 @@ var SectionModels = class {
15125
15759
  this.templates = /* @__PURE__ */ new Set();
15126
15760
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
15127
15761
  }
15762
+ /** Returns a cube template, or `undefined` when the block requires an individual render object. */
15128
15763
  async get(blockState, properties = {}) {
15129
15764
  if (!blockState.variants || blockState.multipart) return void 0;
15130
15765
  let states = this.states.get(blockState);
@@ -15153,16 +15788,16 @@ var SectionModels = class {
15153
15788
  if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
15154
15789
  const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
15155
15790
  const template = await cached(models, key, async () => {
15156
- const object2 = new SectionModel(model, {
15791
+ const object3 = new SectionModel(model, {
15157
15792
  instanceMeshes: false,
15158
15793
  mergeMeshes: false,
15159
15794
  tints,
15160
15795
  uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
15161
15796
  });
15162
- await object2.init();
15163
- const geometry = object2.children[0].geometry;
15797
+ await object3.init();
15798
+ const geometry = object3.children[0].geometry;
15164
15799
  geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
15165
- object2.clear();
15800
+ object3.clear();
15166
15801
  const cullFaces = CUBE_FACES.map((face) => {
15167
15802
  const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
15168
15803
  if (local < 0) return 0;
@@ -15178,6 +15813,7 @@ var SectionModels = class {
15178
15813
  }
15179
15814
  return { variants, templates };
15180
15815
  }
15816
+ /** Disposes prepared geometry and clears this world's template caches. */
15181
15817
  clear() {
15182
15818
  for (const template of this.templates) template.geometry.dispose();
15183
15819
  this.templates.clear();
@@ -15228,6 +15864,13 @@ var MineRenderWorld = class {
15228
15864
  this.validatePosBounds(posOrX);
15229
15865
  await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
15230
15866
  }
15867
+ /**
15868
+ * Places a structure's blocks at their stored positions, then refreshes neighboring faces.
15869
+ * Entity NBT is retained by the structure but is not rendered.
15870
+ *
15871
+ * @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
15872
+ * @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
15873
+ */
15231
15874
  async placeMultiBlock(multiblock, useBatches = true, executor) {
15232
15875
  const changes = /* @__PURE__ */ new Map();
15233
15876
  try {
@@ -15324,6 +15967,7 @@ var MineRenderWorld = class {
15324
15967
  });
15325
15968
  }
15326
15969
  }
15970
+ /** Removes all chunks and releases their block placements and section meshes. */
15327
15971
  async clear() {
15328
15972
  await this.culling;
15329
15973
  const chunks = [...this._chunks.values()];
@@ -15369,10 +16013,10 @@ var MineRenderWorld = class {
15369
16013
  await block.object.updateFluid((x, y, z) => {
15370
16014
  const neighbor = pos.clone().add(new Vector316(x, y, z));
15371
16015
  const section = this.getChunkAt(neighbor);
15372
- const object2 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
16016
+ const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
15373
16017
  return {
15374
- fluid: object2?.fluidKind,
15375
- level: object2?.fluidLevel,
16018
+ fluid: object3?.fluidKind,
16019
+ level: object3?.fluidLevel,
15376
16020
  solid: section?.isOccludingAt(neighbor) ?? false
15377
16021
  };
15378
16022
  });
@@ -15409,9 +16053,11 @@ var MineRenderWorld = class {
15409
16053
  }
15410
16054
  return chunk;
15411
16055
  }
16056
+ /** Returns the loaded chunk containing a world block position, without creating a chunk. */
15412
16057
  getChunkAt(pos) {
15413
16058
  return this._chunks.get(this.worldPosToChunkKey(pos));
15414
16059
  }
16060
+ /** Converts block coordinates to scene units by multiplying each component by 16. */
15415
16061
  static worldToScenePosition(pos) {
15416
16062
  return new Vector316(
15417
16063
  pos.x * 16,
@@ -15419,6 +16065,7 @@ var MineRenderWorld = class {
15419
16065
  pos.z * 16
15420
16066
  );
15421
16067
  }
16068
+ /** Converts scene units to block coordinates without rounding to an integer block. */
15422
16069
  static sceneToWorldPosition(pos) {
15423
16070
  return new Vector316(
15424
16071
  pos.x / 16,
@@ -15445,12 +16092,13 @@ init_Model();
15445
16092
  // src/world/SectionMesh.ts
15446
16093
  init_CanvasCompat();
15447
16094
  init_Materials();
15448
- import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group2, Mesh as Mesh9 } from "three";
15449
- var SectionMesh = class _SectionMesh extends Group2 {
16095
+ import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9 } from "three";
16096
+ var SectionMesh = class _SectionMesh extends Group3 {
15450
16097
  constructor() {
15451
16098
  super(...arguments);
15452
16099
  this.ownedMeshes = [];
15453
16100
  }
16101
+ /** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
15454
16102
  static build(entries, maxAtlasSize = 2048) {
15455
16103
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
15456
16104
  const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
@@ -15498,7 +16146,7 @@ var SectionMesh = class _SectionMesh extends Group2 {
15498
16146
  const normal = template.geometry.getAttribute("normal");
15499
16147
  const uv = template.geometry.getAttribute("uv");
15500
16148
  const bounds = template.geometry.getAttribute("uvBounds");
15501
- const color = template.geometry.getAttribute("color");
16149
+ const color2 = template.geometry.getAttribute("color");
15502
16150
  const sourceIndices = template.geometry.getIndex();
15503
16151
  const placement = locations.get(template.atlas).placement;
15504
16152
  const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
@@ -15520,7 +16168,7 @@ var SectionMesh = class _SectionMesh extends Group2 {
15520
16168
  mapU(bounds?.getZ(vertex) ?? 1),
15521
16169
  mapV(bounds?.getW(vertex) ?? 1)
15522
16170
  );
15523
- colors.push(color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1);
16171
+ colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
15524
16172
  }
15525
16173
  for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
15526
16174
  }
@@ -15595,7 +16243,7 @@ var Chunk = class _Chunk {
15595
16243
  return this.setBlockInChunkAt(pos, block, worldPos);
15596
16244
  }
15597
16245
  /**
15598
- * Set a block at integer chunk-local coordinates from 0 to 15.
16246
+ * Places a block at integer chunk-local coordinates from 0 to 15.
15599
16247
  */
15600
16248
  async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
15601
16249
  if (typeof worldPos === "undefined") {
@@ -15608,7 +16256,7 @@ var Chunk = class _Chunk {
15608
16256
  this.renderedBlocks.get(index)?.object?.removeFromScene();
15609
16257
  if (this.sectionBlocks.delete(index)) this.meshDirty = true;
15610
16258
  this.renderedBlocks.delete(index);
15611
- let object2;
16259
+ let object3;
15612
16260
  try {
15613
16261
  if (!readBlock) return void 0;
15614
16262
  const stored = readBlock();
@@ -15623,27 +16271,28 @@ var Chunk = class _Chunk {
15623
16271
  this.sectionBlocks.set(index, { index, template, cullMask: 0 });
15624
16272
  this.meshDirty = true;
15625
16273
  } else {
15626
- object2 = await this.scene.addBlock(blockState, {
16274
+ object3 = await this.scene.addBlock(blockState, {
15627
16275
  mergeMeshes: true,
15628
16276
  instanceMeshes: true,
15629
16277
  maxInstanceCount: 2e3,
15630
16278
  initialState: stored.properties
15631
16279
  });
15632
- object2.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
16280
+ object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
15633
16281
  }
15634
16282
  const info = { get block() {
15635
16283
  return readBlock();
15636
- }, object: object2 };
16284
+ }, object: object3 };
15637
16285
  this.renderedBlocks.set(index, info);
15638
16286
  return info;
15639
16287
  } catch (error) {
15640
16288
  this.data.set(index, void 0);
15641
- object2?.removeFromScene();
16289
+ object3?.removeFromScene();
15642
16290
  throw error;
15643
16291
  } finally {
15644
16292
  await onBlocksChanged?.([worldPos]);
15645
16293
  }
15646
16294
  }
16295
+ /** Changes visibility at world block coordinates while preserving the block's data. */
15647
16296
  async setBlockVisibleAt(pos, visible) {
15648
16297
  const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
15649
16298
  const block = this.renderedBlocks.get(index);
@@ -15697,6 +16346,7 @@ var Chunk = class _Chunk {
15697
16346
  this.scene.dirty = true;
15698
16347
  this.meshDirty = false;
15699
16348
  }
16349
+ /** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
15700
16350
  async clear(onBlocksChanged = this.onBlocksChanged) {
15701
16351
  const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
15702
16352
  new Vector317(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
@@ -15717,6 +16367,7 @@ var Chunk = class _Chunk {
15717
16367
  static isAir(block) {
15718
16368
  return ChunkData.isAir(block);
15719
16369
  }
16370
+ /** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
15720
16371
  static chunkPosToBlockIndex(pos) {
15721
16372
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
15722
16373
  throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
@@ -15818,6 +16469,7 @@ export {
15818
16469
  RequestError,
15819
16470
  Requests,
15820
16471
  SKIN_PARTS,
16472
+ SceneDocumentLoader,
15821
16473
  SceneExporter,
15822
16474
  SceneInspector,
15823
16475
  SceneObject,
@@ -15893,6 +16545,7 @@ export {
15893
16545
  mergeBufferAttributes,
15894
16546
  mergeBufferGeometries,
15895
16547
  mergeVertices,
16548
+ parseSceneDocument,
15896
16549
  prefix,
15897
16550
  probeImageSize,
15898
16551
  registerBrowserEnv,