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.
@@ -65,6 +65,7 @@ var init_PersistentCache = __esm({
65
65
  constructor(backing) {
66
66
  this.backing = backing;
67
67
  }
68
+ /** Opens a named store in the active environment using the current cache schema version. */
68
69
  static open(name) {
69
70
  return Env.provider.openCache(name, _PersistentCache.VERSION);
70
71
  }
@@ -263,6 +264,7 @@ var init_Caching = __esm({
263
264
  this.fontCache
264
265
  ];
265
266
  }
267
+ /** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
266
268
  static clear() {
267
269
  for (const cache of this.all) {
268
270
  cache.invalidateAll();
@@ -328,6 +330,7 @@ var init_Geometries = __esm({
328
330
  }
329
331
  return geometry;
330
332
  }
333
+ /** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
331
334
  static getBox(key) {
332
335
  const keyStr = serializeBoxGeometryKey(key);
333
336
  return Caching.boxGeometryCache.get(keyStr, (k) => {
@@ -505,12 +508,15 @@ var init_Requests = __esm({
505
508
  }
506
509
  };
507
510
  Requests = class {
511
+ /** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
508
512
  static genericRequest(request) {
509
513
  return this.genericQueue.request(request);
510
514
  }
515
+ /** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
511
516
  static mcAssetRequest(request) {
512
517
  return this.mcAssetRequestQueue.request(request);
513
518
  }
519
+ /** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
514
520
  static setMcAssetRoot(root) {
515
521
  this.mcAssetRequestQueue.baseURL = root;
516
522
  }
@@ -565,6 +571,7 @@ var init_WrappedImage = __esm({
565
571
  }
566
572
  return false;
567
573
  }
574
+ /** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
568
575
  get animated() {
569
576
  return this.height > this.width && this.height % this.width === 0;
570
577
  }
@@ -611,6 +618,7 @@ var init_ImageLoader = __esm({
611
618
  static _createImage() {
612
619
  return createImage();
613
620
  }
621
+ /** Resolves when an image URL has decoded, or rejects if decoding fails. */
614
622
  static async loadAsync(src) {
615
623
  return new Promise((resolve, reject) => {
616
624
  const image = this._createImage();
@@ -619,6 +627,7 @@ var init_ImageLoader = __esm({
619
627
  image.src = src;
620
628
  });
621
629
  }
630
+ /** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
622
631
  static loadElement(src, onload, onerr) {
623
632
  const image = this._createImage();
624
633
  if (onload)
@@ -646,6 +655,7 @@ var init_ImageLoader = __esm({
646
655
  throw err;
647
656
  }
648
657
  }
658
+ /** Decodes the supplied bytes into a canvas without fetching the source URL again. */
649
659
  static async infoToCanvasData(info) {
650
660
  const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
651
661
  const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
@@ -662,12 +672,14 @@ var init_ImageLoader = __esm({
662
672
  const image = await this.infoToCanvasData(info);
663
673
  return image.data.getImageData(0, 0, image.width, image.height);
664
674
  }
675
+ /** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
665
676
  static async getData(src) {
666
677
  const keyStr = serializeImageKey({ src });
667
678
  return await Caching.imageDataCache.get(keyStr, (k) => {
668
679
  return _ImageLoader.loadData(src);
669
680
  });
670
681
  }
682
+ /** Returns a cached canvas context and dimensions for an image URL or data URL. */
671
683
  static async getCanvasData(src) {
672
684
  const keyStr = serializeImageKey({ src });
673
685
  return await Caching.canvasImageDataCache.get(keyStr, (k) => {
@@ -764,6 +776,7 @@ var init_TextureLoader = __esm({
764
776
  texture.rotation = rotation;
765
777
  return Textures.initTextureProps(texture);
766
778
  }
779
+ /** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
767
780
  static load(src, format2 = import_three3.RGBAFormat, rotation = 0) {
768
781
  const texture = new import_three3.Texture();
769
782
  texture.colorSpace = THREE.SRGBColorSpace;
@@ -801,8 +814,8 @@ var init_Textures = __esm({
801
814
  for (let y = 0; y < size; y++) {
802
815
  for (let x = 0; x < size; x++) {
803
816
  const offset = (y * size + x) * 4;
804
- const color = (x + y) % 2 === 0 ? 255 : 0;
805
- data[offset] = data[offset + 2] = color;
817
+ const color2 = (x + y) % 2 === 0 ? 255 : 0;
818
+ data[offset] = data[offset + 2] = color2;
806
819
  data[offset + 3] = 255;
807
820
  }
808
821
  }
@@ -813,6 +826,7 @@ var init_Textures = __esm({
813
826
  return this.initTextureProps(texture);
814
827
  });
815
828
  }
829
+ /** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
816
830
  static initTextureProps(texture) {
817
831
  texture.magFilter = THREE2.NearestFilter;
818
832
  texture.minFilter = THREE2.NearestFilter;
@@ -825,11 +839,13 @@ var init_Textures = __esm({
825
839
  static createAsset(key) {
826
840
  return new import_three4.Texture();
827
841
  }
842
+ /** Creates an sRGB color texture from a canvas. */
828
843
  static createCanvasTexture(canvas) {
829
844
  const texture = new import_three4.CanvasTexture(canvas);
830
845
  texture.colorSpace = THREE2.SRGBColorSpace;
831
846
  return this.initTextureProps(texture);
832
847
  }
848
+ /** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
833
849
  static getImage(key) {
834
850
  const keyStr = serializeTextureKey(key);
835
851
  const cached2 = Caching.textureCache.peek(keyStr);
@@ -854,6 +870,7 @@ var init_Materials = __esm({
854
870
  init_TextureLoader();
855
871
  init_Caching();
856
872
  Materials = class _Materials {
873
+ /** Shared checkerboard material, created on first access. */
857
874
  static get MISSING_TEXTURE() {
858
875
  return Caching.materialCache.get("builtin:missing-texture", () => new import_three5.MeshBasicMaterial({
859
876
  map: Textures.getMissing(),
@@ -957,6 +974,7 @@ var init_Materials = __esm({
957
974
  if (tint !== void 0) material.color.multiply(new import_three5.Color(tint));
958
975
  return material;
959
976
  }
977
+ /** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
960
978
  static createGuiCanvasMaterial(canvas) {
961
979
  const material = new import_three5.MeshBasicMaterial({
962
980
  map: Textures.createCanvasTexture(canvas),
@@ -1123,6 +1141,7 @@ var init_Materials = __esm({
1123
1141
  }
1124
1142
  return this.createBasicCanvasMaterial(canvas, transparent, shade);
1125
1143
  }
1144
+ /** Returns a shared image material. Use {@link createImage} for a separate material instance. */
1126
1145
  static getImage(key) {
1127
1146
  const keyStr = serializeMaterialKey(key);
1128
1147
  const map = Caching.materialCache.peek(keyStr)?.map;
@@ -1297,6 +1316,14 @@ var init_AssetKey = __esm({
1297
1316
  }
1298
1317
  };
1299
1318
  AssetKey = class _AssetKey extends BasicAssetKey {
1319
+ /**
1320
+ * Creates a key from separate path components. Use {@link parse} for a combined reference.
1321
+ *
1322
+ * @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
1323
+ * @param type - Subdirectory, such as `block` or `item`.
1324
+ * @param rootType - Top-level directory, usually `assets` or `data`.
1325
+ * @param root - Base URL override for hosted asset sources.
1326
+ */
1300
1327
  constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
1301
1328
  super(namespace, path);
1302
1329
  this.namespace = namespace;
@@ -1308,6 +1335,16 @@ var init_AssetKey = __esm({
1308
1335
  this.root = root;
1309
1336
  this.isAssetKey = true;
1310
1337
  }
1338
+ /**
1339
+ * Parses an asset reference such as `minecraft:block/stone`.
1340
+ * A missing namespace defaults to the origin's namespace or `minecraft`.
1341
+ *
1342
+ * @param assetType - Asset category, such as `models` or `textures`.
1343
+ * @param str - Path with an optional namespace and file extension.
1344
+ * @param origin - Supplies the asset root, default namespace, and non-texture extension.
1345
+ * @returns A key with the matching extension removed from its path. Textures use `.png`;
1346
+ * other assets use the origin's extension or `.json`.
1347
+ */
1311
1348
  static parse(assetType, str, origin) {
1312
1349
  let namespace = origin?.namespace || DEFAULT_NAMESPACE;
1313
1350
  if (str.includes(":")) {
@@ -1339,6 +1376,7 @@ var init_AssetKey = __esm({
1339
1376
  ];
1340
1377
  return a.join("/");
1341
1378
  }
1379
+ /** Joins the type subdirectory and path without adding a namespace or file extension. */
1342
1380
  getFullPath() {
1343
1381
  let p10 = [];
1344
1382
  if (this.type) {
@@ -1350,6 +1388,7 @@ var init_AssetKey = __esm({
1350
1388
  toNamespacedString() {
1351
1389
  return this.namespace + ":" + this.getFullPath();
1352
1390
  }
1391
+ /** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
1353
1392
  serialize() {
1354
1393
  return this.toString();
1355
1394
  }
@@ -1361,17 +1400,17 @@ var init_AssetKey = __esm({
1361
1400
  var DisplayPosition, DISPLAY_POSITIONS;
1362
1401
  var init_DisplayPosition = __esm({
1363
1402
  "src/model/DisplayPosition.ts"() {
1364
- DisplayPosition = /* @__PURE__ */ ((DisplayPosition2) => {
1365
- DisplayPosition2["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1366
- DisplayPosition2["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1367
- DisplayPosition2["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1368
- DisplayPosition2["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1369
- DisplayPosition2["GUI"] = "gui";
1370
- DisplayPosition2["HEAD"] = "head";
1371
- DisplayPosition2["GROUND"] = "ground";
1372
- DisplayPosition2["FIXED"] = "fixed";
1373
- DisplayPosition2["ON_SHELF"] = "on_shelf";
1374
- return DisplayPosition2;
1403
+ DisplayPosition = /* @__PURE__ */ ((DisplayPosition3) => {
1404
+ DisplayPosition3["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1405
+ DisplayPosition3["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1406
+ DisplayPosition3["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1407
+ DisplayPosition3["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1408
+ DisplayPosition3["GUI"] = "gui";
1409
+ DisplayPosition3["HEAD"] = "head";
1410
+ DisplayPosition3["GROUND"] = "ground";
1411
+ DisplayPosition3["FIXED"] = "fixed";
1412
+ DisplayPosition3["ON_SHELF"] = "on_shelf";
1413
+ return DisplayPosition3;
1375
1414
  })(DisplayPosition || {});
1376
1415
  DISPLAY_POSITIONS = Object.values(DisplayPosition);
1377
1416
  }
@@ -1409,6 +1448,7 @@ var init_DisplayTransforms = __esm({
1409
1448
  const scale = new import_three7.Vector3(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
1410
1449
  return new import_three7.Matrix4().compose(translation, rotation, scale);
1411
1450
  }
1451
+ /** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
1412
1452
  static apply(geometry, matrix) {
1413
1453
  geometry.applyMatrix4(matrix);
1414
1454
  if (matrix.determinant() < 0) {
@@ -1436,6 +1476,7 @@ var init_ModelMerger = __esm({
1436
1476
  init_AssetKey();
1437
1477
  init_DisplayTransforms();
1438
1478
  ModelMerger = class {
1479
+ /** Loads the parent chain and returns a merged model without modifying the supplied definition. */
1439
1480
  static async mergeWithParents(model) {
1440
1481
  const models = await this.collectAllParents(model);
1441
1482
  let merged = {};
@@ -1503,6 +1544,7 @@ var init_Models = __esm({
1503
1544
  static get PERSISTENT_CACHE() {
1504
1545
  return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-models"));
1505
1546
  }
1547
+ /** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
1506
1548
  static async getItemList() {
1507
1549
  const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
1508
1550
  const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
@@ -1573,6 +1615,7 @@ var init_Models = __esm({
1573
1615
  }
1574
1616
  throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
1575
1617
  }
1618
+ /** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
1576
1619
  static async getRaw(key) {
1577
1620
  if (!key.assetType) {
1578
1621
  key.assetType = "models";
@@ -1585,13 +1628,19 @@ var init_Models = __esm({
1585
1628
  return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1586
1629
  return AssetLoader.get(key, "model" /* MODEL */);
1587
1630
  });
1588
- }).then((asset) => {
1589
- if (asset) {
1590
- asset.key = key;
1631
+ }).then((asset2) => {
1632
+ if (asset2) {
1633
+ asset2.key = key;
1591
1634
  }
1592
- return asset;
1635
+ return asset2;
1593
1636
  });
1594
1637
  }
1638
+ /**
1639
+ * Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
1640
+ * Item keys use GUI context, false conditions, and zero numeric properties.
1641
+ *
1642
+ * @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
1643
+ */
1595
1644
  static async getMerged(key) {
1596
1645
  if (!key.assetType) {
1597
1646
  key.assetType = "models";
@@ -1604,9 +1653,11 @@ var init_Models = __esm({
1604
1653
  return _Models.loadAndMerge(key);
1605
1654
  });
1606
1655
  }
1656
+ /** Alias for {@link getMerged}. */
1607
1657
  static async get(key) {
1608
1658
  return this.getMerged(key);
1609
1659
  }
1660
+ /** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
1610
1661
  static async clearCache() {
1611
1662
  return this.PERSISTENT_CACHE.clear();
1612
1663
  }
@@ -1629,6 +1680,7 @@ var init_BlockStates = __esm({
1629
1680
  return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-blockstates"));
1630
1681
  }
1631
1682
  // BlockState names are hardcoded
1683
+ /** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
1632
1684
  static async getList() {
1633
1685
  const key = new AssetKey(
1634
1686
  DEFAULT_NAMESPACE,
@@ -1649,6 +1701,7 @@ var init_BlockStates = __esm({
1649
1701
  return AssetLoader.get(key, "json" /* JSON */);
1650
1702
  });
1651
1703
  }
1704
+ /** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
1652
1705
  static async getDefaultState(key) {
1653
1706
  const defaultStates = await this.getDefaultStates();
1654
1707
  if (!defaultStates) {
@@ -1656,6 +1709,7 @@ var init_BlockStates = __esm({
1656
1709
  }
1657
1710
  return defaultStates["minecraft:" + key.path];
1658
1711
  }
1712
+ /** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
1659
1713
  static async get(key) {
1660
1714
  if (!key.assetType) {
1661
1715
  key.assetType = "blockstates";
@@ -1668,11 +1722,11 @@ var init_BlockStates = __esm({
1668
1722
  return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1669
1723
  return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
1670
1724
  });
1671
- }).then((asset) => {
1672
- if (asset) {
1673
- asset.key = key;
1725
+ }).then((asset2) => {
1726
+ if (asset2) {
1727
+ asset2.key = key;
1674
1728
  }
1675
- return asset;
1729
+ return asset2;
1676
1730
  });
1677
1731
  }
1678
1732
  static getAll(keys) {
@@ -1682,6 +1736,7 @@ var init_BlockStates = __esm({
1682
1736
  }
1683
1737
  return Promise.all(promises);
1684
1738
  }
1739
+ /** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
1685
1740
  static async clearCache() {
1686
1741
  await this.PERSISTENT_CACHE.clear();
1687
1742
  }
@@ -1707,9 +1762,11 @@ var init_Assets = __esm({
1707
1762
  static parseAssetKey(assetType, str, origin) {
1708
1763
  return AssetKey.parse(assetType, str, origin);
1709
1764
  }
1765
+ /** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
1710
1766
  static async getModel(key) {
1711
1767
  return Models.getMerged(key);
1712
1768
  }
1769
+ /** Loads a blockstate definition. See {@link BlockStates.get}. */
1713
1770
  static async getBlockState(key) {
1714
1771
  return BlockStates.get(key);
1715
1772
  }
@@ -1756,6 +1813,10 @@ var init_BatchedExecutor = __esm({
1756
1813
  "src/util/BatchedExecutor.ts"() {
1757
1814
  import_jobqu2 = require("jobqu");
1758
1815
  BatchedExecutor = class {
1816
+ /**
1817
+ * @param interval - Delay between queue runs, in milliseconds.
1818
+ * @param batch - Maximum tasks started per run and maximum tasks active at once.
1819
+ */
1759
1820
  constructor(interval = 1, batch = 30) {
1760
1821
  if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
1761
1822
  if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
@@ -1763,9 +1824,11 @@ var init_BatchedExecutor = __esm({
1763
1824
  this.batch = batch;
1764
1825
  this.queue = new import_jobqu2.JobQueue(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
1765
1826
  }
1827
+ /** Queues a task and resolves with its result, or rejects with its error. */
1766
1828
  submit(task) {
1767
1829
  return this.queue.add(() => task());
1768
1830
  }
1831
+ /** Rejects queued and future tasks. Tasks already running are allowed to finish. */
1769
1832
  stop() {
1770
1833
  this.queue.end();
1771
1834
  }
@@ -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 import_three8.Vector3();
1817
1880
  box.getSize(size);
1818
1881
  let boxGeo = new import_three8.BoxGeometry(size.x, size.y, size.z, 1, 1, 1);
1819
1882
  let wireGeo = new import_three8.EdgesGeometry(boxGeo);
1820
- let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
1883
+ let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
1821
1884
  let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
1822
1885
  obj.add(wireframe);
1823
1886
  let axes = new import_three8.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 import_three8.BoxGeometry(16, 16, 16, 1, 1, 1);
1828
1891
  let wireGeo = new import_three8.EdgesGeometry(boxGeo);
1829
- let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
1892
+ let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
1830
1893
  let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
1831
1894
  obj.add(wireframe);
1832
1895
  let axes = new import_three8.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 import_three8.EdgesGeometry(geo);
1837
- let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
1900
+ let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
1838
1901
  let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
1839
1902
  mesh.add(wireframe);
1840
1903
  let axes = new import_three8.AxesHelper(1);
@@ -1908,6 +1971,7 @@ var init_NBTHelper = __esm({
1908
1971
  "src/nbt/NBTHelper.ts"() {
1909
1972
  import_buffer2 = require("buffer");
1910
1973
  NBTHelper = class {
1974
+ /** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
1911
1975
  static async fromBuffer(data, format2) {
1912
1976
  const buffer = import_buffer2.Buffer.from(data instanceof Uint8Array ? data : new Uint8Array(data));
1913
1977
  const prismarineNbt = await import("prismarine-nbt");
@@ -1976,6 +2040,7 @@ var init_HostedAssetSource = __esm({
1976
2040
  retryDefaults: true
1977
2041
  };
1978
2042
  _HostedAssetSource = class _HostedAssetSource extends AssetSource {
2043
+ /** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
1979
2044
  async loadOrRetryWithDefaults(key, parser) {
1980
2045
  const direct = await this.load(key, parser);
1981
2046
  if (typeof direct !== "undefined") {
@@ -2033,6 +2098,10 @@ var init_HostedAssetSource = __esm({
2033
2098
  assetBasePath(key) {
2034
2099
  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 + "/" : ""}`;
2035
2100
  }
2101
+ /**
2102
+ * Creates a hosted source for {@link AssetLoader.addSource}.
2103
+ * Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
2104
+ */
2036
2105
  constructor(root, options) {
2037
2106
  super();
2038
2107
  this._root = root;
@@ -2125,6 +2194,11 @@ var init_BrowserArchiveProxy = __esm({
2125
2194
  "src/assets/source/archive/BrowserArchiveProxy.ts"() {
2126
2195
  import_zip = require("@zip.js/zip.js");
2127
2196
  BrowserArchiveProxy = class {
2197
+ /**
2198
+ * Opens a ZIP for asset lookup.
2199
+ * @param id - Cache identity for this content. Change it when the pack changes.
2200
+ * A File supplies an identity from its name, size, and modification time when omitted.
2201
+ */
2128
2202
  constructor(blob, id) {
2129
2203
  this._blob = blob;
2130
2204
  this._reader = new import_zip.ZipReader(new import_zip.BlobReader(this._blob));
@@ -2254,27 +2328,27 @@ var init_PackFormats = __esm({
2254
2328
  init_Requests();
2255
2329
  PackFormats = class {
2256
2330
  /** Resolves pack formats from Mojang's client metadata hosted on assets.mcasset.cloud. */
2257
- static async get(version, type) {
2258
- let pending = this.versions.get(version);
2331
+ static async get(version2, type) {
2332
+ let pending = this.versions.get(version2);
2259
2333
  if (!pending || pending.expires <= Date.now()) {
2260
2334
  pending = {
2261
- formats: this.load(version),
2262
- expires: ["latest", "release", "snapshot"].includes(version) ? Date.now() + 3e5 : Infinity
2335
+ formats: this.load(version2),
2336
+ expires: ["latest", "release", "snapshot"].includes(version2) ? Date.now() + 3e5 : Infinity
2263
2337
  };
2264
- this.versions.set(version, pending);
2338
+ this.versions.set(version2, pending);
2265
2339
  }
2266
2340
  try {
2267
2341
  return (await pending.formats)[type];
2268
2342
  } catch (cause) {
2269
- if (this.versions.get(version) === pending) this.versions.delete(version);
2343
+ if (this.versions.get(version2) === pending) this.versions.delete(version2);
2270
2344
  const option = type === "assets" ? "resourcePackFormat" : "dataPackFormat";
2271
2345
  const reason = cause instanceof Error ? cause.message : String(cause);
2272
- throw new Error(`Could not resolve the pack format for Minecraft ${version}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
2346
+ throw new Error(`Could not resolve the pack format for Minecraft ${version2}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
2273
2347
  }
2274
2348
  }
2275
- static async load(version) {
2349
+ static async load(version2) {
2276
2350
  const response = await Requests.genericRequest({
2277
- url: `https://assets.mcasset.cloud/${encodeURIComponent(version)}/game-version.json`,
2351
+ url: `https://assets.mcasset.cloud/${encodeURIComponent(version2)}/game-version.json`,
2278
2352
  responseType: "json"
2279
2353
  });
2280
2354
  const data = response.data;
@@ -2322,6 +2396,7 @@ var init_ArchiveAssetSource = __esm({
2322
2396
  dataPackFormat: copyFormat(options.dataPackFormat)
2323
2397
  };
2324
2398
  }
2399
+ /** Creates a browser ZIP source from a Blob or File. */
2325
2400
  static blob(blob, options) {
2326
2401
  return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
2327
2402
  }
@@ -2362,7 +2437,7 @@ var init_ArchiveAssetSource = __esm({
2362
2437
  }
2363
2438
  async load(key, parser) {
2364
2439
  const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
2365
- const version = AssetLoader.version;
2440
+ const version2 = AssetLoader.version;
2366
2441
  let selectedPath = path;
2367
2442
  let url;
2368
2443
  try {
@@ -2370,7 +2445,7 @@ var init_ArchiveAssetSource = __esm({
2370
2445
  if (key.rootType === "assets" || key.rootType === "data") {
2371
2446
  const metadata = await this.getMetadata(key);
2372
2447
  if (metadata.hasOverlays) {
2373
- const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version, key.rootType);
2448
+ const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version2, key.rootType);
2374
2449
  directories = metadata.overlayDirectories(format2);
2375
2450
  }
2376
2451
  }
@@ -2456,8 +2531,9 @@ var init_AssetLoader = __esm({
2456
2531
  static get version() {
2457
2532
  return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
2458
2533
  }
2459
- static setVersion(version) {
2460
- this.ROOT = `https://assets.mcasset.cloud/${version}`;
2534
+ /** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
2535
+ static setVersion(version2) {
2536
+ this.ROOT = `https://assets.mcasset.cloud/${version2}`;
2461
2537
  const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
2462
2538
  const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
2463
2539
  if (index !== -1) {
@@ -2489,6 +2565,13 @@ var init_AssetLoader = __esm({
2489
2565
  return scope ? `${scope}
2490
2566
  ${key}` : key;
2491
2567
  }
2568
+ /**
2569
+ * Registers a source at the highest priority.
2570
+ * Call {@link Caching.clear} after changing sources to discard previously loaded assets.
2571
+ *
2572
+ * @param key - Name used to replace or remove this source.
2573
+ * @param override - Removes the first source with this name before adding the new one.
2574
+ */
2492
2575
  static addSource(key, source, override = true) {
2493
2576
  if (override) {
2494
2577
  const existing = this.removeSource(key);
@@ -2499,6 +2582,7 @@ ${key}` : key;
2499
2582
  this._SOURCES.unshift({ key, source });
2500
2583
  console.log(p4, "Added AssetSource", key);
2501
2584
  }
2585
+ /** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
2502
2586
  static removeSource(key) {
2503
2587
  const index = this._SOURCES.findIndex((s) => s.key === key);
2504
2588
  if (index != -1) {
@@ -2509,12 +2593,13 @@ ${key}` : key;
2509
2593
  }
2510
2594
  return void 0;
2511
2595
  }
2596
+ /** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
2512
2597
  static async getAll(key, parser) {
2513
2598
  const sources = [...this._SOURCES];
2514
2599
  const results = [];
2515
2600
  for (const { source } of sources) {
2516
- const asset = await source.get(key, parser);
2517
- if (asset != void 0) results.push(asset);
2601
+ const asset2 = await source.get(key, parser);
2602
+ if (asset2 != void 0) results.push(asset2);
2518
2603
  if (await source.blocks(key)) break;
2519
2604
  }
2520
2605
  return results;
@@ -2553,6 +2638,7 @@ ${key}` : key;
2553
2638
  return void 0;
2554
2639
  }
2555
2640
  };
2641
+ /** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
2556
2642
  _AssetLoader.ROOT = DEFAULT_ROOT;
2557
2643
  _AssetLoader._SOURCES = [];
2558
2644
  /** Used when a registered source cannot identify its content, so nothing persists past this session. */
@@ -2577,14 +2663,15 @@ var init_ModelTextures = __esm({
2577
2663
  static get PERSISTENT_META_CACHE() {
2578
2664
  return this._persistentMetaCache ?? (this._persistentMetaCache = PersistentCache.open("minerender-texturemeta"));
2579
2665
  }
2666
+ /** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
2580
2667
  static async get(key) {
2581
2668
  const keyStr = key.serialize();
2582
2669
  const pending = this.preload(key);
2583
2670
  const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
2584
2671
  try {
2585
- const asset = await pending;
2586
- if (asset) {
2587
- return await ImageLoader.infoToCanvasData(asset);
2672
+ const asset2 = await pending;
2673
+ if (asset2) {
2674
+ return await ImageLoader.infoToCanvasData(asset2);
2588
2675
  }
2589
2676
  return void 0;
2590
2677
  } catch (err) {
@@ -2594,16 +2681,18 @@ var init_ModelTextures = __esm({
2594
2681
  throw err;
2595
2682
  }
2596
2683
  }
2684
+ /** Fetches and caches encoded texture bytes without decoding the pixels. */
2597
2685
  static async preload(key) {
2598
2686
  const keyStr = key.serialize();
2599
2687
  return Caching.textureAssetCache.get(keyStr, (k) => {
2600
- return AssetLoader.get(key, "image" /* IMAGE */).then((asset) => {
2601
- if (asset)
2602
- asset.key = key;
2603
- return asset;
2688
+ return AssetLoader.get(key, "image" /* IMAGE */).then((asset2) => {
2689
+ if (asset2)
2690
+ asset2.key = key;
2691
+ return asset2;
2604
2692
  });
2605
2693
  });
2606
2694
  }
2695
+ /** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
2607
2696
  static async getMeta(key) {
2608
2697
  if (!key.extension || !key.extension.endsWith(".mcmeta")) {
2609
2698
  key = Object.assign(new AssetKey("", ""), key);
@@ -2612,14 +2701,15 @@ var init_ModelTextures = __esm({
2612
2701
  const keyStr = key.serialize();
2613
2702
  return Caching.textureMetaCache.get(keyStr, (k) => {
2614
2703
  return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
2615
- return AssetLoader.get(key, "meta" /* META */).then((asset) => {
2616
- if (asset)
2617
- asset.key = key;
2618
- return asset;
2704
+ return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
2705
+ if (asset2)
2706
+ asset2.key = key;
2707
+ return asset2;
2619
2708
  });
2620
2709
  });
2621
2710
  });
2622
2711
  }
2712
+ /** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
2623
2713
  static async clearCache() {
2624
2714
  await this.PERSISTENT_META_CACHE.clear();
2625
2715
  }
@@ -2686,6 +2776,7 @@ var init_Ticker = __esm({
2686
2776
  }
2687
2777
  return handle;
2688
2778
  }
2779
+ /** Starts or resumes the shared tick loop. Repeated calls have no effect. */
2689
2780
  static start() {
2690
2781
  if (this.interval) return;
2691
2782
  this.interval = this.schedule(() => {
@@ -2708,6 +2799,7 @@ var init_Ticker = __esm({
2708
2799
  _Ticker.fiveSecondTicks = 0;
2709
2800
  }, 1e3 * 5);
2710
2801
  }
2802
+ /** Pauses the loop while retaining callbacks for a later {@link start}. */
2711
2803
  static stop() {
2712
2804
  if (this.interval) clearInterval(this.interval);
2713
2805
  if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
@@ -2716,12 +2808,14 @@ var init_Ticker = __esm({
2716
2808
  this.oneSecondTicksTracker = void 0;
2717
2809
  this.fiveSecondTicksTracker = void 0;
2718
2810
  }
2811
+ /** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
2719
2812
  static add(tick) {
2720
2813
  this.start();
2721
2814
  const c = this.counter++;
2722
2815
  this._tickers.set(c, tick);
2723
2816
  return c;
2724
2817
  }
2818
+ /** Removes a callback by ID and stops the timers when no callbacks remain. */
2725
2819
  static remove(c) {
2726
2820
  if (typeof c === "number") this._tickers.delete(c);
2727
2821
  if (!this._tickers.size) this.stop();
@@ -2735,6 +2829,7 @@ var init_Ticker = __esm({
2735
2829
  static get tpsFiveSeconds() {
2736
2830
  return this.fiveSecondTps;
2737
2831
  }
2832
+ /** Stops the loop and removes all registered callbacks. */
2738
2833
  static dispose() {
2739
2834
  this.stop();
2740
2835
  this._tickers.clear();
@@ -2789,6 +2884,7 @@ var init_TextureAtlas = __esm({
2789
2884
  }
2790
2885
  };
2791
2886
  }
2887
+ /** Copies pixels for a texture variable from its current atlas rectangle. */
2792
2888
  getData(texture) {
2793
2889
  const pos = this.positions[texture];
2794
2890
  const size = this.sizes[texture];
@@ -3066,6 +3162,7 @@ var init_UVMapper = __esm({
3066
3162
  }
3067
3163
  uv.needsUpdate = true;
3068
3164
  }
3165
+ /** Returns the shared atlas for a keyed, merged model, creating it on first use. */
3069
3166
  static async getAtlas(model) {
3070
3167
  const keyStr = model.key.serialize();
3071
3168
  return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
@@ -3136,6 +3233,11 @@ var init_UVMapper = __esm({
3136
3233
  }
3137
3234
  return [tl, tr, bl, br];
3138
3235
  }
3236
+ /**
3237
+ * Combines model textures and animation metadata into an atlas.
3238
+ * Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
3239
+ * Returns `undefined` for models without a texture map.
3240
+ */
3139
3241
  static async createAtlas(originalModel) {
3140
3242
  const textureMap = {};
3141
3243
  const metaMap = {};
@@ -3156,12 +3258,12 @@ var init_UVMapper = __esm({
3156
3258
  } else {
3157
3259
  uniqueTextureNames.push(textureKey);
3158
3260
  const assetKey = AssetKey.parse("textures", textureValue, model.key);
3159
- promises.push(ModelTextures.get(assetKey).then((asset) => {
3160
- if (!asset) {
3261
+ promises.push(ModelTextures.get(assetKey).then((asset2) => {
3262
+ if (!asset2) {
3161
3263
  console.warn(p5, "Missing texture", assetKey);
3162
3264
  return;
3163
3265
  }
3164
- textureMap[textureKey] = new WrappedImage(asset);
3266
+ textureMap[textureKey] = new WrappedImage(asset2);
3165
3267
  }));
3166
3268
  promises.push(ModelTextures.getMeta(assetKey).then((meta) => {
3167
3269
  metaMap[textureKey] = meta;
@@ -3500,14 +3602,15 @@ var init_Entities = __esm({
3500
3602
  init_MineRenderError();
3501
3603
  init_ModelTextures();
3502
3604
  Entities = class {
3605
+ /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
3503
3606
  static async getEntityList() {
3504
3607
  const collect = async (path) => {
3505
3608
  const prefix2 = path ? `${path}/` : "";
3506
3609
  const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
3507
- const list = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3508
- if (!list) return [];
3509
- const files = list.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3510
- const children = await Promise.all(list.directories.map((directory) => collect(`${prefix2}${directory}`)));
3610
+ const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3611
+ if (!list2) return [];
3612
+ const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3613
+ const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
3511
3614
  return [...files, ...children.flat()];
3512
3615
  };
3513
3616
  return collect("");
@@ -3517,6 +3620,7 @@ var init_Entities = __esm({
3517
3620
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
3518
3621
  return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3519
3622
  }
3623
+ /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
3520
3624
  static async getLayerList(modelKey) {
3521
3625
  return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
3522
3626
  }
@@ -3524,6 +3628,15 @@ var init_Entities = __esm({
3524
3628
  static async getPassList(modelKey) {
3525
3629
  return (await this.getModelFile(modelKey))?.passes ?? [];
3526
3630
  }
3631
+ /**
3632
+ * Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
3633
+ * By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
3634
+ *
3635
+ * @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
3636
+ * @param textureKey - Texture override for the first selected layer.
3637
+ * @param options - Layer selection, conditional passes, and per-layer texture overrides.
3638
+ * @returns The selected model, or `undefined` when its dataset file is missing.
3639
+ */
3527
3640
  static async getEntity(modelKey, textureKey, options) {
3528
3641
  const model = await this.getModelFile(modelKey);
3529
3642
  if (!model) return void 0;
@@ -3569,6 +3682,7 @@ var init_Entities = __esm({
3569
3682
  const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3570
3683
  return file?.animations;
3571
3684
  }
3685
+ /** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
3572
3686
  static async resolveTexture(modelKey) {
3573
3687
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3574
3688
  const root = isAssetKey(modelKey) ? modelKey.root : void 0;
@@ -3580,8 +3694,8 @@ var init_Entities = __esm({
3580
3694
  if (await ModelTextures.get(texture2)) return texture2;
3581
3695
  }
3582
3696
  const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
3583
- const list = await AssetLoader.get(listKey, "list" /* LIST */);
3584
- const files = list?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3697
+ const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
3698
+ const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3585
3699
  const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
3586
3700
  if (!file) return void 0;
3587
3701
  const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
@@ -3686,27 +3800,27 @@ var init_EntityAnimationPlayer = __esm({
3686
3800
  for (const layer of layers) {
3687
3801
  const objects = [];
3688
3802
  if (poses) {
3689
- layer.traverse((object2) => {
3690
- if (poses.has(object2)) objects.push(object2);
3803
+ layer.traverse((object3) => {
3804
+ if (poses.has(object3)) objects.push(object3);
3691
3805
  });
3692
3806
  } else {
3693
3807
  for (const bone of Object.keys(animation.bones)) {
3694
- const object2 = layer.getObjectByName(`group:${bone}`);
3695
- if (object2) objects.push(object2);
3808
+ const object3 = layer.getObjectByName(`group:${bone}`);
3809
+ if (object3) objects.push(object3);
3696
3810
  }
3697
3811
  }
3698
- for (const object2 of objects) {
3699
- const bone = object2.name.slice("group:".length);
3700
- const pose = poses?.get(object2);
3812
+ for (const object3 of objects) {
3813
+ const bone = object3.name.slice("group:".length);
3814
+ const pose = poses?.get(object3);
3701
3815
  this.parts.push({
3702
3816
  bone,
3703
- object: object2,
3704
- position: object2.position.clone(),
3705
- rotation: object2.rotation.clone(),
3706
- scale: object2.scale.clone(),
3707
- basePosition: pose ? new import_three11.Vector3(...pose.offset) : object2.position.clone(),
3708
- baseRotation: pose ? new import_three11.Euler(...pose.rotation, "ZYX") : object2.rotation.clone(),
3709
- baseScale: pose ? new import_three11.Vector3(...pose.scale ?? [1, 1, 1]) : object2.scale.clone()
3817
+ object: object3,
3818
+ position: object3.position.clone(),
3819
+ rotation: object3.rotation.clone(),
3820
+ scale: object3.scale.clone(),
3821
+ basePosition: pose ? new import_three11.Vector3(...pose.offset) : object3.position.clone(),
3822
+ baseRotation: pose ? new import_three11.Euler(...pose.rotation, "ZYX") : object3.rotation.clone(),
3823
+ baseScale: pose ? new import_three11.Vector3(...pose.scale ?? [1, 1, 1]) : object3.scale.clone()
3710
3824
  });
3711
3825
  }
3712
3826
  }
@@ -3729,12 +3843,14 @@ var init_EntityAnimationPlayer = __esm({
3729
3843
  this._animation = void 0;
3730
3844
  this._time = 0;
3731
3845
  }
3846
+ /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
3732
3847
  setTime(seconds) {
3733
3848
  if (!this._animation) return false;
3734
3849
  this._time = seconds;
3735
3850
  this.apply();
3736
3851
  return true;
3737
3852
  }
3853
+ /** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
3738
3854
  advance(deltaSeconds) {
3739
3855
  return this.setTime(this._time + deltaSeconds * this.speed);
3740
3856
  }
@@ -3772,6 +3888,7 @@ var init_InstanceReference = __esm({
3772
3888
  if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
3773
3889
  return this.instanceable;
3774
3890
  }
3891
+ /** Releases this placement's slot for reuse. Repeated calls have no effect. */
3775
3892
  removeFromScene() {
3776
3893
  if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
3777
3894
  }
@@ -3784,9 +3901,11 @@ var init_InstanceReference = __esm({
3784
3901
  setMatrix(matrix) {
3785
3902
  this.activeInstanceable.setMatrixAt(this.index, matrix);
3786
3903
  }
3904
+ /** Hides or restores this placement while preserving its transform and allocated slot. */
3787
3905
  setVisible(visible) {
3788
3906
  this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
3789
3907
  }
3908
+ /** Updates the supplied transform components. Positions use scene units and rotations use radians. */
3790
3909
  setPositionRotationScale(position, rotation, scale) {
3791
3910
  this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
3792
3911
  }
@@ -3855,6 +3974,7 @@ var init_SceneObject = __esm({
3855
3974
  }
3856
3975
  async init() {
3857
3976
  }
3977
+ /** Marks ancestor scenes for redraw and emits a `change` event. */
3858
3978
  notifyDirty() {
3859
3979
  this.traverseAncestors((parent) => {
3860
3980
  const scene = parent;
@@ -3890,15 +4010,17 @@ var init_SceneObject = __esm({
3890
4010
  return obj;
3891
4011
  }
3892
4012
  /**
3893
- * Get a group by its name
4013
+ * Finds a named part, such as `head`, without the internal `group:` prefix.
4014
+ * Returns `undefined` when the group does not exist.
3894
4015
  */
3895
4016
  getGroupByName(name) {
3896
4017
  return this.getObjectByName(`group:${name}`);
3897
4018
  }
3898
4019
  /**
3899
- * Toggle visibility of a group
3900
- * @param name name of the group
3901
- * @param visible set the visibility directly, if not set toggles it
4020
+ * Changes a named group's visibility and requests a redraw.
4021
+ * @param name - Part name without the `group:` prefix.
4022
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4023
+ * @returns The resulting visibility, or `false` if the group does not exist.
3902
4024
  */
3903
4025
  toggleGroupVisibility(name, visible) {
3904
4026
  return this.toggleObjectVisibility(this.getGroupByName(name), visible);
@@ -3949,15 +4071,16 @@ var init_SceneObject = __esm({
3949
4071
  return mesh;
3950
4072
  }
3951
4073
  /**
3952
- * Get a mesh by its name
4074
+ * Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
3953
4075
  */
3954
4076
  getMeshByName(name) {
3955
4077
  return this.getObjectByName(`mesh:${name}`);
3956
4078
  }
3957
4079
  /**
3958
- * Toggle visibility of a mesh
3959
- * @param name name of the mesh
3960
- * @param visible set the visibility directly, if not set toggles it
4080
+ * Changes a named mesh's visibility and requests a redraw.
4081
+ * @param name - Mesh name without the `mesh:` prefix.
4082
+ * @param visible - Visibility to assign. Omit it to toggle the current value.
4083
+ * @returns The resulting visibility, or `false` if the mesh does not exist.
3961
4084
  */
3962
4085
  toggleMeshVisibility(name, visible) {
3963
4086
  return this.toggleObjectVisibility(this.getMeshByName(name), visible);
@@ -4003,12 +4126,14 @@ var init_SceneObject = __esm({
4003
4126
  get isInstanced() {
4004
4127
  return this._isInstanced;
4005
4128
  }
4129
+ /** Number of live placements, excluding released instance slots. */
4006
4130
  get instanceCounter() {
4007
4131
  return this._instanceCounter;
4008
4132
  }
4009
4133
  constructInstanceReference(i) {
4010
4134
  return new InstanceReference(this, i);
4011
4135
  }
4136
+ /** Allocates a placement of this instanced object, reusing a released slot when available. */
4012
4137
  nextInstance() {
4013
4138
  const mesh = this.instanceMesh;
4014
4139
  if (!mesh) throw new MineRenderError("Object is not instanced");
@@ -4053,10 +4178,10 @@ var init_SceneObject = __esm({
4053
4178
  return new import_three12.InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
4054
4179
  };
4055
4180
  const matrix = grow(mesh.instanceMatrix, 0);
4056
- const color = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
4181
+ const color2 = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
4057
4182
  mesh.dispose();
4058
4183
  mesh.instanceMatrix = matrix;
4059
- mesh.instanceColor = color;
4184
+ mesh.instanceColor = color2;
4060
4185
  }
4061
4186
  *activeInstanceIndices() {
4062
4187
  if (this.instanceMesh) {
@@ -4155,6 +4280,7 @@ var init_SceneObject = __esm({
4155
4280
  vector.setFromMatrixScale(matrix);
4156
4281
  return vector;
4157
4282
  }
4283
+ /** Updates supplied transform components. For a shared model, affects every live instance. */
4158
4284
  setPositionRotationScale(position, rotation, scale) {
4159
4285
  if (this.isInstanced) {
4160
4286
  for (const i of this.activeInstanceIndices()) {
@@ -4173,6 +4299,7 @@ var init_SceneObject = __esm({
4173
4299
  }
4174
4300
  this.notifyDirty();
4175
4301
  }
4302
+ /** Sets position in scene units. For a shared model, updates every live instance. */
4176
4303
  setPosition(position) {
4177
4304
  if (this.isInstanced) {
4178
4305
  for (const i of this.activeInstanceIndices()) {
@@ -4190,6 +4317,7 @@ var init_SceneObject = __esm({
4190
4317
  return this.position;
4191
4318
  }
4192
4319
  }
4320
+ /** Sets rotation in radians. For a shared model, updates every live instance. */
4193
4321
  setRotation(rotation) {
4194
4322
  if (this.isInstanced) {
4195
4323
  for (const i of this.activeInstanceIndices()) {
@@ -4207,6 +4335,7 @@ var init_SceneObject = __esm({
4207
4335
  return this.rotation;
4208
4336
  }
4209
4337
  }
4338
+ /** Sets scale factors. For a shared model, updates every live instance. */
4210
4339
  setScale(scale) {
4211
4340
  if (this.isInstanced) {
4212
4341
  for (const i of this.activeInstanceIndices()) {
@@ -4225,15 +4354,15 @@ var init_SceneObject = __esm({
4225
4354
  }
4226
4355
  }
4227
4356
  //</editor-fold>
4228
- toggleObjectVisibility(object2, visible) {
4229
- if (object2) {
4357
+ toggleObjectVisibility(object3, visible) {
4358
+ if (object3) {
4230
4359
  if (typeof visible !== "undefined") {
4231
- object2.visible = visible;
4360
+ object3.visible = visible;
4232
4361
  } else {
4233
- object2.visible = !object2.visible;
4362
+ object3.visible = !object3.visible;
4234
4363
  }
4235
4364
  this.notifyDirty();
4236
- return object2.visible;
4365
+ return object3.visible;
4237
4366
  }
4238
4367
  this.notifyDirty();
4239
4368
  return false;
@@ -4244,6 +4373,7 @@ var init_SceneObject = __esm({
4244
4373
  this.notifyDirty();
4245
4374
  super.dispose();
4246
4375
  }
4376
+ /** Releases instance slots and removes children, disposing children that implement Disposable. */
4247
4377
  disposeAndRemoveAllChildren() {
4248
4378
  if (this.instanceMesh) {
4249
4379
  if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
@@ -4266,6 +4396,7 @@ var init_SceneObject = __esm({
4266
4396
  }
4267
4397
  this.notifyDirty();
4268
4398
  }
4399
+ /** Detaches the object from its parent and requests a redraw. */
4269
4400
  removeFromScene() {
4270
4401
  this.notifyDirty();
4271
4402
  this.removeFromParent();
@@ -4421,12 +4552,15 @@ var init_EntityObject = __esm({
4421
4552
  if (this.animationPlayers.length) this.notifyDirty();
4422
4553
  }
4423
4554
  //</editor-fold>
4555
+ /** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
4424
4556
  getLayerGroup(name) {
4425
4557
  return super.getGroupByName(`layer:${name}`);
4426
4558
  }
4559
+ /** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
4427
4560
  getGroupByName(name, layerName) {
4428
4561
  return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
4429
4562
  }
4563
+ /** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
4430
4564
  getMeshByName(name, layerName) {
4431
4565
  return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
4432
4566
  }
@@ -4528,8 +4662,8 @@ var init_EntityObject = __esm({
4528
4662
  this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
4529
4663
  }
4530
4664
  }
4531
- this.getLayerGroup(name)?.traverse((object2) => {
4532
- if (isMesh(object2)) object2.material = mat;
4665
+ this.getLayerGroup(name)?.traverse((object3) => {
4666
+ if (isMesh(object3)) object3.material = mat;
4533
4667
  });
4534
4668
  }));
4535
4669
  if (!this.children.length) {
@@ -4859,8 +4993,8 @@ function toTrianglesDrawMode(geometry, drawMode) {
4859
4993
  return geometry;
4860
4994
  }
4861
4995
  }
4862
- function computeMorphedAttributes(object2) {
4863
- if (object2.geometry.isBufferGeometry !== true) {
4996
+ function computeMorphedAttributes(object3) {
4997
+ if (object3.geometry.isBufferGeometry !== true) {
4864
4998
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4865
4999
  return null;
4866
5000
  }
@@ -4873,11 +5007,11 @@ function computeMorphedAttributes(object2) {
4873
5007
  const _morphA = new import_three22.Vector3();
4874
5008
  const _morphB = new import_three22.Vector3();
4875
5009
  const _morphC = new import_three22.Vector3();
4876
- function _calculateMorphedAttributeData(object3, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
5010
+ function _calculateMorphedAttributeData(object4, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4877
5011
  _vA.fromBufferAttribute(attribute, a2);
4878
5012
  _vB.fromBufferAttribute(attribute, b2);
4879
5013
  _vC.fromBufferAttribute(attribute, c2);
4880
- const morphInfluences = object3.morphTargetInfluences;
5014
+ const morphInfluences = object4.morphTargetInfluences;
4881
5015
  if (material2.morphTargets && morphAttribute && morphInfluences) {
4882
5016
  _morphA.set(0, 0, 0);
4883
5017
  _morphB.set(0, 0, 0);
@@ -4903,10 +5037,10 @@ function computeMorphedAttributes(object2) {
4903
5037
  _vB.add(_morphB);
4904
5038
  _vC.add(_morphC);
4905
5039
  }
4906
- if (object3.isSkinnedMesh) {
4907
- object3.boneTransform(a2, _vA);
4908
- object3.boneTransform(b2, _vB);
4909
- object3.boneTransform(c2, _vC);
5040
+ if (object4.isSkinnedMesh) {
5041
+ object4.boneTransform(a2, _vA);
5042
+ object4.boneTransform(b2, _vB);
5043
+ object4.boneTransform(c2, _vC);
4910
5044
  }
4911
5045
  modifiedAttributeArray[a2 * 3 + 0] = _vA.x;
4912
5046
  modifiedAttributeArray[a2 * 3 + 1] = _vA.y;
@@ -4918,8 +5052,8 @@ function computeMorphedAttributes(object2) {
4918
5052
  modifiedAttributeArray[c2 * 3 + 1] = _vC.y;
4919
5053
  modifiedAttributeArray[c2 * 3 + 2] = _vC.z;
4920
5054
  }
4921
- const geometry = object2.geometry;
4922
- const material = object2.material;
5055
+ const geometry = object3.geometry;
5056
+ const material = object3.material;
4923
5057
  let a, b, c;
4924
5058
  const index = geometry.index;
4925
5059
  const positionAttribute = geometry.attributes.position;
@@ -4946,7 +5080,7 @@ function computeMorphedAttributes(object2) {
4946
5080
  b = index.getX(j + 1);
4947
5081
  c = index.getX(j + 2);
4948
5082
  _calculateMorphedAttributeData(
4949
- object2,
5083
+ object3,
4950
5084
  groupMaterial,
4951
5085
  positionAttribute,
4952
5086
  morphPosition,
@@ -4957,7 +5091,7 @@ function computeMorphedAttributes(object2) {
4957
5091
  modifiedPosition
4958
5092
  );
4959
5093
  _calculateMorphedAttributeData(
4960
- object2,
5094
+ object3,
4961
5095
  groupMaterial,
4962
5096
  normalAttribute,
4963
5097
  morphNormal,
@@ -4977,7 +5111,7 @@ function computeMorphedAttributes(object2) {
4977
5111
  b = index.getX(i + 1);
4978
5112
  c = index.getX(i + 2);
4979
5113
  _calculateMorphedAttributeData(
4980
- object2,
5114
+ object3,
4981
5115
  material,
4982
5116
  positionAttribute,
4983
5117
  morphPosition,
@@ -4988,7 +5122,7 @@ function computeMorphedAttributes(object2) {
4988
5122
  modifiedPosition
4989
5123
  );
4990
5124
  _calculateMorphedAttributeData(
4991
- object2,
5125
+ object3,
4992
5126
  material,
4993
5127
  normalAttribute,
4994
5128
  morphNormal,
@@ -5012,7 +5146,7 @@ function computeMorphedAttributes(object2) {
5012
5146
  b = j + 1;
5013
5147
  c = j + 2;
5014
5148
  _calculateMorphedAttributeData(
5015
- object2,
5149
+ object3,
5016
5150
  groupMaterial,
5017
5151
  positionAttribute,
5018
5152
  morphPosition,
@@ -5023,7 +5157,7 @@ function computeMorphedAttributes(object2) {
5023
5157
  modifiedPosition
5024
5158
  );
5025
5159
  _calculateMorphedAttributeData(
5026
- object2,
5160
+ object3,
5027
5161
  groupMaterial,
5028
5162
  normalAttribute,
5029
5163
  morphNormal,
@@ -5043,7 +5177,7 @@ function computeMorphedAttributes(object2) {
5043
5177
  b = i + 1;
5044
5178
  c = i + 2;
5045
5179
  _calculateMorphedAttributeData(
5046
- object2,
5180
+ object3,
5047
5181
  material,
5048
5182
  positionAttribute,
5049
5183
  morphPosition,
@@ -5054,7 +5188,7 @@ function computeMorphedAttributes(object2) {
5054
5188
  modifiedPosition
5055
5189
  );
5056
5190
  _calculateMorphedAttributeData(
5057
- object2,
5191
+ object3,
5058
5192
  material,
5059
5193
  normalAttribute,
5060
5194
  morphNormal,
@@ -5090,6 +5224,7 @@ var init_ModelCulling = __esm({
5090
5224
  import_three23 = require("three");
5091
5225
  init_CubeFace();
5092
5226
  ModelCulling = class {
5227
+ /** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
5093
5228
  static toLocalMask(worldMask, rotation) {
5094
5229
  let localMask = 0;
5095
5230
  for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
@@ -5100,6 +5235,7 @@ var init_ModelCulling = __esm({
5100
5235
  }
5101
5236
  return localMask;
5102
5237
  }
5238
+ /** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
5103
5239
  static isOpaqueFullCube(atlas) {
5104
5240
  if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
5105
5241
  const element = atlas.model.elements[0];
@@ -5310,9 +5446,9 @@ var init_ItemTints = __esm({
5310
5446
  }));
5311
5447
  return colors;
5312
5448
  }
5313
- static rgb(color) {
5314
- if (!Array.isArray(color)) return color & 16777215;
5315
- const [red, green, blue] = color.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
5449
+ static rgb(color2) {
5450
+ if (!Array.isArray(color2)) return color2 & 16777215;
5451
+ const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
5316
5452
  return red << 16 | green << 8 | blue;
5317
5453
  }
5318
5454
  };
@@ -5365,13 +5501,13 @@ var init_ModelObject = __esm({
5365
5501
  transform.multiply(new import_three25.Matrix4().makeTranslation(-8, -8, -8));
5366
5502
  try {
5367
5503
  for (const part of parts) {
5368
- const object2 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
5369
- object2.matrix.copy(transform).multiply(part.transform);
5370
- object2.matrixWorldNeedsUpdate = true;
5371
- object2.matrixAutoUpdate = false;
5372
- this.add(object2);
5373
- await object2.init();
5374
- object2.iterateAllMeshes((mesh) => {
5504
+ const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
5505
+ object3.matrix.copy(transform).multiply(part.transform);
5506
+ object3.matrixWorldNeedsUpdate = true;
5507
+ object3.matrixAutoUpdate = false;
5508
+ this.add(object3);
5509
+ await object3.init();
5510
+ object3.iterateAllMeshes((mesh) => {
5375
5511
  const source = mesh.material;
5376
5512
  let material = this.specialMaterials.get(source);
5377
5513
  if (!material) {
@@ -5382,7 +5518,7 @@ var init_ModelObject = __esm({
5382
5518
  mesh.material = material;
5383
5519
  });
5384
5520
  for (const [name, rotation] of Object.entries(part.rotations)) {
5385
- object2.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
5521
+ object3.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
5386
5522
  }
5387
5523
  }
5388
5524
  } catch (error) {
@@ -5435,9 +5571,9 @@ var init_ModelObject = __esm({
5435
5571
  if (tintIndex === void 0 || tintIndex < 0) continue;
5436
5572
  const tint = this.options.tints[tintIndex];
5437
5573
  if (tint === void 0) continue;
5438
- const color = new import_three25.Color(tint);
5574
+ const color2 = new import_three25.Color(tint);
5439
5575
  for (let vertex = 0; vertex < 4; vertex++) {
5440
- color.toArray(colors, (faceIndex * 4 + vertex) * 3);
5576
+ color2.toArray(colors, (faceIndex * 4 + vertex) * 3);
5441
5577
  }
5442
5578
  }
5443
5579
  elGeo.setAttribute("color", new import_three25.BufferAttribute(colors, 3));
@@ -5754,9 +5890,9 @@ var init_FluidModelObject = __esm({
5754
5890
  }
5755
5891
  }
5756
5892
  geometry.clearGroups();
5757
- const color = new import_three31.Color(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
5893
+ const color2 = new import_three31.Color(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
5758
5894
  const colors = new Float32Array(geometry.getAttribute("position").count * 3);
5759
- for (let i = 0; i < colors.length; i += 3) color.toArray(colors, i);
5895
+ for (let i = 0; i < colors.length; i += 3) color2.toArray(colors, i);
5760
5896
  geometry.setAttribute("color", new import_three31.BufferAttribute(colors, 3));
5761
5897
  geometry.computeBoundingBox();
5762
5898
  geometry.computeBoundingSphere();
@@ -5867,6 +6003,7 @@ __export(index_browser_exports, {
5867
6003
  RequestError: () => RequestError,
5868
6004
  Requests: () => Requests,
5869
6005
  SKIN_PARTS: () => SKIN_PARTS,
6006
+ SceneDocumentLoader: () => SceneDocumentLoader,
5870
6007
  SceneExporter: () => SceneExporter,
5871
6008
  SceneInspector: () => SceneInspector,
5872
6009
  SceneObject: () => SceneObject,
@@ -5942,6 +6079,7 @@ __export(index_browser_exports, {
5942
6079
  mergeBufferAttributes: () => mergeBufferAttributes,
5943
6080
  mergeBufferGeometries: () => mergeBufferGeometries,
5944
6081
  mergeVertices: () => mergeVertices,
6082
+ parseSceneDocument: () => parseSceneDocument,
5945
6083
  prefix: () => prefix,
5946
6084
  probeImageSize: () => probeImageSize,
5947
6085
  registerBrowserEnv: () => registerBrowserEnv,
@@ -6070,11 +6208,11 @@ var BrowserEnv = class {
6070
6208
  imageSize(data) {
6071
6209
  return probeImageSize(data);
6072
6210
  }
6073
- openCache(name, version) {
6211
+ openCache(name, version2) {
6074
6212
  return new BrowserCache(import_localforage.default.createInstance({
6075
6213
  // IndexedDB schema upgrades retain records; each cache generation needs its own database.
6076
- name: name + version,
6077
- version
6214
+ name: name + version2,
6215
+ version: version2
6078
6216
  }));
6079
6217
  }
6080
6218
  };
@@ -6105,6 +6243,7 @@ var Meshes = class _Meshes {
6105
6243
  static createBox(key) {
6106
6244
  return new import_three6.Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
6107
6245
  }
6246
+ /** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
6108
6247
  static getBox(key) {
6109
6248
  const keyStr = serializeMeshKey(key);
6110
6249
  return Caching.boxMeshCache.get(keyStr, (k) => {
@@ -6217,6 +6356,7 @@ init_AssetParsers();
6217
6356
  init_ModelTextures();
6218
6357
  init_Caching();
6219
6358
  var Fonts = class {
6359
+ /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
6220
6360
  static async get(key = "minecraft:default") {
6221
6361
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
6222
6362
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -6232,8 +6372,8 @@ var Fonts = class {
6232
6372
  const stack = await AssetLoader.getAll(key, "json" /* JSON */);
6233
6373
  if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
6234
6374
  const glyphs = /* @__PURE__ */ new Map();
6235
- for (const asset of stack) {
6236
- for (const provider of asset.providers) {
6375
+ for (const asset2 of stack) {
6376
+ for (const provider of asset2.providers) {
6237
6377
  if (provider.filter?.uniform || provider.filter?.jp) continue;
6238
6378
  let provided;
6239
6379
  switch (provider.type) {
@@ -6372,13 +6512,13 @@ var OBJExporter = class {
6372
6512
  * @param {Object3D} object - The 3D object to export.
6373
6513
  * @return {string} The exported OBJ.
6374
6514
  */
6375
- parse(object2) {
6515
+ parse(object3) {
6376
6516
  let output = "";
6377
6517
  let indexVertex = 0;
6378
6518
  let indexVertexUvs = 0;
6379
6519
  let indexNormals = 0;
6380
6520
  const vertex = new import_three16.Vector3();
6381
- const color = new import_three16.Color();
6521
+ const color2 = new import_three16.Color();
6382
6522
  const normal = new import_three16.Vector3();
6383
6523
  const uv = new import_three16.Vector2();
6384
6524
  const face = [];
@@ -6477,9 +6617,9 @@ var OBJExporter = class {
6477
6617
  vertex.applyMatrix4(points.matrixWorld);
6478
6618
  output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
6479
6619
  if (colors !== void 0) {
6480
- color.fromBufferAttribute(colors, i);
6481
- import_three16.ColorManagement.workingToColorSpace(color, import_three16.SRGBColorSpace);
6482
- output += " " + color.r + " " + color.g + " " + color.b;
6620
+ color2.fromBufferAttribute(colors, i);
6621
+ import_three16.ColorManagement.workingToColorSpace(color2, import_three16.SRGBColorSpace);
6622
+ output += " " + color2.r + " " + color2.g + " " + color2.b;
6483
6623
  }
6484
6624
  output += "\n";
6485
6625
  }
@@ -6491,7 +6631,7 @@ var OBJExporter = class {
6491
6631
  }
6492
6632
  indexVertex += nbVertex;
6493
6633
  }
6494
- object2.traverse(function(child) {
6634
+ object3.traverse(function(child) {
6495
6635
  if (child.isMesh === true) {
6496
6636
  parseMesh(child);
6497
6637
  }
@@ -6519,9 +6659,9 @@ var PLYExporter = class {
6519
6659
  * @param {PLYExporter~Options} options - The export options.
6520
6660
  * @return {?(string|ArrayBuffer)} The exported PLY.
6521
6661
  */
6522
- parse(object2, onDone, options = {}) {
6662
+ parse(object3, onDone, options = {}) {
6523
6663
  function traverseMeshes(cb) {
6524
- object2.traverse(function(child) {
6664
+ object3.traverse(function(child) {
6525
6665
  if (child.isMesh === true || child.isPoints) {
6526
6666
  const mesh = child;
6527
6667
  const geometry = mesh.geometry;
@@ -6554,7 +6694,7 @@ var PLYExporter = class {
6554
6694
  for (const name of customAttributeNames) customTypes[name] = "float";
6555
6695
  let vertexCount = 0;
6556
6696
  let faceCount = 0;
6557
- object2.traverse(function(child) {
6697
+ object3.traverse(function(child) {
6558
6698
  if (child.isMesh === true) {
6559
6699
  const mesh = child;
6560
6700
  const geometry = mesh.geometry;
@@ -7148,8 +7288,8 @@ function equalArray(array1, array2) {
7148
7288
  return element === array2[index];
7149
7289
  });
7150
7290
  }
7151
- function stringToArrayBuffer(text) {
7152
- return new TextEncoder().encode(text).buffer;
7291
+ function stringToArrayBuffer(text2) {
7292
+ return new TextEncoder().encode(text2).buffer;
7153
7293
  }
7154
7294
  function isIdentityMatrix(matrix) {
7155
7295
  return equalArray(matrix.elements, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
@@ -7342,12 +7482,12 @@ var GLTFWriter = class {
7342
7482
  * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
7343
7483
  * @param {Object} objectDef
7344
7484
  */
7345
- serializeUserData(object2, objectDef) {
7346
- if (Object.keys(object2.userData).length === 0) return;
7485
+ serializeUserData(object3, objectDef) {
7486
+ if (Object.keys(object3.userData).length === 0) return;
7347
7487
  const options = this.options;
7348
7488
  const extensionsUsed = this.extensionsUsed;
7349
7489
  try {
7350
- const json = JSON.parse(JSON.stringify(object2.userData));
7490
+ const json = JSON.parse(JSON.stringify(object3.userData));
7351
7491
  if (options.includeCustomExtensions && json.gltfExtensions) {
7352
7492
  if (objectDef.extensions === void 0) objectDef.extensions = {};
7353
7493
  for (const extensionName in json.gltfExtensions) {
@@ -7358,7 +7498,7 @@ var GLTFWriter = class {
7358
7498
  }
7359
7499
  if (Object.keys(json).length > 0) objectDef.extras = json;
7360
7500
  } catch (error) {
7361
- console.warn("THREE.GLTFExporter: userData of '" + object2.name + "' won't be serialized because of JSON.stringify error - " + error.message);
7501
+ console.warn("THREE.GLTFExporter: userData of '" + object3.name + "' won't be serialized because of JSON.stringify error - " + error.message);
7362
7502
  }
7363
7503
  }
7364
7504
  /**
@@ -7867,9 +8007,9 @@ var GLTFWriter = class {
7867
8007
  if (material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true) {
7868
8008
  console.warn("GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.");
7869
8009
  }
7870
- const color = material.color.toArray().concat([material.opacity]);
7871
- if (!equalArray(color, [1, 1, 1, 1])) {
7872
- materialDef.pbrMetallicRoughness.baseColorFactor = color;
8010
+ const color2 = material.color.toArray().concat([material.opacity]);
8011
+ if (!equalArray(color2, [1, 1, 1, 1])) {
8012
+ materialDef.pbrMetallicRoughness.baseColorFactor = color2;
7873
8013
  }
7874
8014
  if (material.isMeshStandardMaterial) {
7875
8015
  materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
@@ -8284,13 +8424,13 @@ var GLTFWriter = class {
8284
8424
  * @param {THREE.Object3D} object
8285
8425
  * @return {?number}
8286
8426
  */
8287
- processSkin(object2) {
8427
+ processSkin(object3) {
8288
8428
  const json = this.json;
8289
8429
  const nodeMap = this.nodeMap;
8290
- const node = json.nodes[nodeMap.get(object2)];
8291
- const skeleton = object2.skeleton;
8430
+ const node = json.nodes[nodeMap.get(object3)];
8431
+ const skeleton = object3.skeleton;
8292
8432
  if (skeleton === void 0) return null;
8293
- const rootJoint = object2.skeleton.bones[0];
8433
+ const rootJoint = object3.skeleton.bones[0];
8294
8434
  if (rootJoint === void 0) return null;
8295
8435
  const joints = [];
8296
8436
  const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
@@ -8298,7 +8438,7 @@ var GLTFWriter = class {
8298
8438
  for (let i = 0; i < skeleton.bones.length; ++i) {
8299
8439
  joints.push(nodeMap.get(skeleton.bones[i]));
8300
8440
  temporaryBoneInverse.copy(skeleton.boneInverses[i]);
8301
- temporaryBoneInverse.multiply(object2.bindMatrix).toArray(inverseBindMatrices, i * 16);
8441
+ temporaryBoneInverse.multiply(object3.bindMatrix).toArray(inverseBindMatrices, i * 16);
8302
8442
  }
8303
8443
  if (json.skins === void 0) json.skins = [];
8304
8444
  json.skins.push({
@@ -8314,19 +8454,19 @@ var GLTFWriter = class {
8314
8454
  * @param {THREE.Object3D} object Object3D to processNodeAsync
8315
8455
  * @return {Promise<number>} Index of the node in the nodes list
8316
8456
  */
8317
- async processNodeAsync(object2) {
8457
+ async processNodeAsync(object3) {
8318
8458
  const json = this.json;
8319
8459
  const options = this.options;
8320
8460
  const nodeMap = this.nodeMap;
8321
8461
  if (!json.nodes) json.nodes = [];
8322
- if (object2.pivot !== null) {
8323
- return await this._processNodeWithPivotAsync(object2);
8462
+ if (object3.pivot !== null) {
8463
+ return await this._processNodeWithPivotAsync(object3);
8324
8464
  }
8325
8465
  const nodeDef = {};
8326
8466
  if (options.trs) {
8327
- const rotation = object2.quaternion.toArray();
8328
- const position = object2.position.toArray();
8329
- const scale = object2.scale.toArray();
8467
+ const rotation = object3.quaternion.toArray();
8468
+ const position = object3.position.toArray();
8469
+ const scale = object3.scale.toArray();
8330
8470
  if (!equalArray(rotation, [0, 0, 0, 1])) {
8331
8471
  nodeDef.rotation = rotation;
8332
8472
  }
@@ -8337,28 +8477,28 @@ var GLTFWriter = class {
8337
8477
  nodeDef.scale = scale;
8338
8478
  }
8339
8479
  } else {
8340
- if (object2.matrixAutoUpdate) {
8341
- object2.updateMatrix();
8480
+ if (object3.matrixAutoUpdate) {
8481
+ object3.updateMatrix();
8342
8482
  }
8343
- if (isIdentityMatrix(object2.matrix) === false) {
8344
- nodeDef.matrix = object2.matrix.elements;
8483
+ if (isIdentityMatrix(object3.matrix) === false) {
8484
+ nodeDef.matrix = object3.matrix.elements;
8345
8485
  }
8346
8486
  }
8347
- if (object2.name !== "") nodeDef.name = String(object2.name);
8348
- this.serializeUserData(object2, nodeDef);
8349
- if (object2.isMesh || object2.isLine || object2.isPoints) {
8350
- const meshIndex = await this.processMeshAsync(object2);
8487
+ if (object3.name !== "") nodeDef.name = String(object3.name);
8488
+ this.serializeUserData(object3, nodeDef);
8489
+ if (object3.isMesh || object3.isLine || object3.isPoints) {
8490
+ const meshIndex = await this.processMeshAsync(object3);
8351
8491
  if (meshIndex !== null) nodeDef.mesh = meshIndex;
8352
- } else if (object2.isCamera) {
8353
- nodeDef.camera = this.processCamera(object2);
8492
+ } else if (object3.isCamera) {
8493
+ nodeDef.camera = this.processCamera(object3);
8354
8494
  }
8355
- if (object2.isSkinnedMesh) this.skins.push(object2);
8495
+ if (object3.isSkinnedMesh) this.skins.push(object3);
8356
8496
  const nodeIndex = json.nodes.push(nodeDef) - 1;
8357
- nodeMap.set(object2, nodeIndex);
8358
- if (object2.children.length > 0) {
8497
+ nodeMap.set(object3, nodeIndex);
8498
+ if (object3.children.length > 0) {
8359
8499
  const children = [];
8360
- for (let i = 0, l = object2.children.length; i < l; i++) {
8361
- const child = object2.children[i];
8500
+ for (let i = 0, l = object3.children.length; i < l; i++) {
8501
+ const child = object3.children[i];
8362
8502
  if (child.visible || options.onlyVisible === false) {
8363
8503
  const childNodeIndex = await this.processNodeAsync(child);
8364
8504
  if (childNodeIndex !== null) children.push(childNodeIndex);
@@ -8367,7 +8507,7 @@ var GLTFWriter = class {
8367
8507
  if (children.length > 0) nodeDef.children = children;
8368
8508
  }
8369
8509
  await this._invokeAllAsync(function(ext) {
8370
- ext.writeNode && ext.writeNode(object2, nodeDef);
8510
+ ext.writeNode && ext.writeNode(object3, nodeDef);
8371
8511
  });
8372
8512
  return nodeIndex;
8373
8513
  }
@@ -8376,19 +8516,19 @@ var GLTFWriter = class {
8376
8516
  * @param {THREE.Object3D} object Object3D with pivot
8377
8517
  * @return {Promise<number>} Index of the container node
8378
8518
  */
8379
- async _processNodeWithPivotAsync(object2) {
8519
+ async _processNodeWithPivotAsync(object3) {
8380
8520
  const json = this.json;
8381
8521
  const options = this.options;
8382
8522
  const nodeMap = this.nodeMap;
8383
- const pivot = object2.pivot;
8523
+ const pivot = object3.pivot;
8384
8524
  const containerDef = {};
8385
- const rotation = object2.quaternion.toArray();
8525
+ const rotation = object3.quaternion.toArray();
8386
8526
  const position = [
8387
- object2.position.x + pivot.x,
8388
- object2.position.y + pivot.y,
8389
- object2.position.z + pivot.z
8527
+ object3.position.x + pivot.x,
8528
+ object3.position.y + pivot.y,
8529
+ object3.position.z + pivot.z
8390
8530
  ];
8391
- const scale = object2.scale.toArray();
8531
+ const scale = object3.scale.toArray();
8392
8532
  if (!equalArray(rotation, [0, 0, 0, 1])) {
8393
8533
  containerDef.rotation = rotation;
8394
8534
  }
@@ -8399,28 +8539,28 @@ var GLTFWriter = class {
8399
8539
  containerDef.scale = scale;
8400
8540
  }
8401
8541
  containerDef.extras = { pivot: pivot.toArray() };
8402
- if (object2.name !== "") containerDef.name = String(object2.name);
8403
- this.serializeUserData(object2, containerDef);
8542
+ if (object3.name !== "") containerDef.name = String(object3.name);
8543
+ this.serializeUserData(object3, containerDef);
8404
8544
  const containerIndex = json.nodes.push(containerDef) - 1;
8405
- nodeMap.set(object2, containerIndex);
8545
+ nodeMap.set(object3, containerIndex);
8406
8546
  const childDef = {};
8407
8547
  const childPosition = [-pivot.x, -pivot.y, -pivot.z];
8408
8548
  if (!equalArray(childPosition, [0, 0, 0])) {
8409
8549
  childDef.translation = childPosition;
8410
8550
  }
8411
- if (object2.isMesh || object2.isLine || object2.isPoints) {
8412
- const meshIndex = await this.processMeshAsync(object2);
8551
+ if (object3.isMesh || object3.isLine || object3.isPoints) {
8552
+ const meshIndex = await this.processMeshAsync(object3);
8413
8553
  if (meshIndex !== null) childDef.mesh = meshIndex;
8414
- } else if (object2.isCamera) {
8415
- childDef.camera = this.processCamera(object2);
8554
+ } else if (object3.isCamera) {
8555
+ childDef.camera = this.processCamera(object3);
8416
8556
  }
8417
- if (object2.isSkinnedMesh) this.skins.push(object2);
8557
+ if (object3.isSkinnedMesh) this.skins.push(object3);
8418
8558
  const childIndex = json.nodes.push(childDef) - 1;
8419
8559
  const containerChildren = [childIndex];
8420
- if (object2.children.length > 0) {
8560
+ if (object3.children.length > 0) {
8421
8561
  const grandchildren = [];
8422
- for (let i = 0, l = object2.children.length; i < l; i++) {
8423
- const child = object2.children[i];
8562
+ for (let i = 0, l = object3.children.length; i < l; i++) {
8563
+ const child = object3.children[i];
8424
8564
  if (child.visible || options.onlyVisible === false) {
8425
8565
  const childNodeIndex = await this.processNodeAsync(child);
8426
8566
  if (childNodeIndex !== null) grandchildren.push(childNodeIndex);
@@ -8430,7 +8570,7 @@ var GLTFWriter = class {
8430
8570
  }
8431
8571
  containerDef.children = containerChildren;
8432
8572
  await this._invokeAllAsync(function(ext) {
8433
- ext.writeNode && ext.writeNode(object2, containerDef);
8573
+ ext.writeNode && ext.writeNode(object3, containerDef);
8434
8574
  });
8435
8575
  return containerIndex;
8436
8576
  }
@@ -8873,10 +9013,10 @@ var GLTFMeshGpuInstancing = class {
8873
9013
  this.writer = writer;
8874
9014
  this.name = "EXT_mesh_gpu_instancing";
8875
9015
  }
8876
- writeNode(object2, nodeDef) {
8877
- if (!object2.isInstancedMesh) return;
9016
+ writeNode(object3, nodeDef) {
9017
+ if (!object3.isInstancedMesh) return;
8878
9018
  const writer = this.writer;
8879
- const mesh = object2;
9019
+ const mesh = object3;
8880
9020
  const translationAttr = new Float32Array(mesh.count * 3);
8881
9021
  const rotationAttr = new Float32Array(mesh.count * 4);
8882
9022
  const scaleAttr = new Float32Array(mesh.count * 3);
@@ -9066,8 +9206,8 @@ var SceneExporter = class {
9066
9206
  }
9067
9207
  }
9068
9208
  };
9069
- function localMatrix(object2) {
9070
- return object2.matrixAutoUpdate ? new import_three19.Matrix4().compose(object2.position, object2.quaternion, object2.scale) : object2.matrix.clone();
9209
+ function localMatrix(object3) {
9210
+ return object3.matrixAutoUpdate ? new import_three19.Matrix4().compose(object3.position, object3.quaternion, object3.scale) : object3.matrix.clone();
9071
9211
  }
9072
9212
  function createSnapshot(root, gltf, bakeColors = false) {
9073
9213
  const scene = new import_three19.Scene();
@@ -9171,24 +9311,24 @@ function createSnapshot(root, gltf, bakeColors = false) {
9171
9311
  scene.add(copy);
9172
9312
  }
9173
9313
  };
9174
- const visit = (object2, parentWorld) => {
9175
- if (!object2.visible) return;
9176
- const world = parentWorld.clone().multiply(localMatrix(object2));
9177
- const mesh = object2;
9314
+ const visit = (object3, parentWorld) => {
9315
+ if (!object3.visible) return;
9316
+ const world = parentWorld.clone().multiply(localMatrix(object3));
9317
+ const mesh = object3;
9178
9318
  if (mesh.isMesh) {
9179
9319
  const instanced = mesh;
9180
9320
  if (instanced.isInstancedMesh) {
9181
9321
  for (let i = 0; i < instanced.count; i++) {
9182
9322
  const matrix = new import_three19.Matrix4();
9183
9323
  instanced.getMatrixAt(i, matrix);
9184
- const color = instanced.instanceColor ? new import_three19.Color().fromBufferAttribute(instanced.instanceColor, i) : void 0;
9185
- addMesh(mesh, world.clone().multiply(matrix), color);
9324
+ const color2 = instanced.instanceColor ? new import_three19.Color().fromBufferAttribute(instanced.instanceColor, i) : void 0;
9325
+ addMesh(mesh, world.clone().multiply(matrix), color2);
9186
9326
  }
9187
9327
  } else {
9188
9328
  addMesh(mesh, world);
9189
9329
  }
9190
9330
  }
9191
- for (const child of object2.children) visit(child, world);
9331
+ for (const child of object3.children) visit(child, world);
9192
9332
  };
9193
9333
  try {
9194
9334
  const ancestors = [];
@@ -9264,13 +9404,13 @@ init_AssetKey();
9264
9404
 
9265
9405
  // src/gui/GuiText.ts
9266
9406
  var import_three20 = require("three");
9267
- async function layoutGuiText(text, options = {}) {
9407
+ async function layoutGuiText(text2, options = {}) {
9268
9408
  const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
9269
9409
  if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
9270
9410
  throw new Error("Text wrap width and line height must be positive");
9271
9411
  }
9272
9412
  const font = await Fonts.get(options.font);
9273
- const runs = typeof text === "string" ? [{ text }] : text;
9413
+ const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
9274
9414
  const glyphs = [], lineWidths = [];
9275
9415
  let line = [], width = 0;
9276
9416
  let trailingNewline = false;
@@ -9324,7 +9464,7 @@ function createGuiTextGeometry(layout, shadow = true) {
9324
9464
  batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
9325
9465
  batches.push(batch);
9326
9466
  }
9327
- const color = new import_three20.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
9467
+ const color2 = new import_three20.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
9328
9468
  const offset = isShadow ? 1 : 0;
9329
9469
  const top = glyph ? 7 - glyph.ascent : 0;
9330
9470
  const height = glyph ? glyph.height * glyph.scale : 8;
@@ -9352,7 +9492,7 @@ function createGuiTextGeometry(layout, shadow = true) {
9352
9492
  const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
9353
9493
  const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
9354
9494
  batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
9355
- for (let i = 0; i < 4; i++) batch.colors.push(color.r, color.g, color.b);
9495
+ for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
9356
9496
  batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
9357
9497
  };
9358
9498
  for (const boldOffset of style.bold ? [0, 1] : [0]) {
@@ -9386,12 +9526,19 @@ var GuiHelper = class {
9386
9526
  size: [width + 24, height + 24]
9387
9527
  }));
9388
9528
  let y = position[1];
9389
- lines.forEach((text, index) => {
9390
- layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y] });
9529
+ lines.forEach((text2, index) => {
9530
+ layers.push({ ...textOptions, name: `tooltip-line-${index}`, text: text2, position: [position[0], y] });
9391
9531
  y += layouts[index].height + (index === 0 ? titleGap : 0);
9392
9532
  });
9393
9533
  return layers;
9394
9534
  }
9535
+ /**
9536
+ * Calculates a slot's top-left position in GUI pixels.
9537
+ * @param slot - Zero-based slot index, or `[column, row]`.
9538
+ * @param origin - Position of the first slot.
9539
+ * @param offset - Horizontal and vertical spacing between slot origins, in pixels.
9540
+ * @param rowSize - Slots per row when `slot` is an index.
9541
+ */
9395
9542
  static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
9396
9543
  const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
9397
9544
  return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
@@ -9836,29 +9983,29 @@ var SceneInspector = class {
9836
9983
  this.objectControlsContainer.innerHTML = "";
9837
9984
  this.addControls(targetObject, targetIntersection, instance);
9838
9985
  }
9839
- addControls(object2, intersection, instance) {
9986
+ addControls(object3, intersection, instance) {
9840
9987
  const container = document.createElement("div");
9841
9988
  container.append(this.separator("Select Parent/Child"));
9842
- if (object2.parent) {
9843
- container.append(this.buttonControl("Select Parent " + object2.parent.constructor.name + " " + object2.parent.name, "P", () => {
9844
- this.selectObject(object2.parent, intersection, instance);
9989
+ if (object3.parent) {
9990
+ container.append(this.buttonControl("Select Parent " + object3.parent.constructor.name + " " + object3.parent.name, "P", () => {
9991
+ this.selectObject(object3.parent, intersection, instance);
9845
9992
  }));
9846
9993
  }
9847
- if (object2.children.length > 0) {
9994
+ if (object3.children.length > 0) {
9848
9995
  let i = 1;
9849
- for (let child of object2.children) {
9996
+ for (let child of object3.children) {
9850
9997
  container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
9851
9998
  this.selectObject(child, intersection, instance);
9852
9999
  }));
9853
10000
  }
9854
10001
  }
9855
10002
  container.append(this.separator());
9856
- const selectedInstance = instance && (object2 === instance.instanceable || object2 === intersection?.object) ? instance : void 0;
10003
+ const selectedInstance = instance && (object3 === instance.instanceable || object3 === intersection?.object) ? instance : void 0;
9857
10004
  if (!selectedInstance) {
9858
- container.append(this.toggleControl("Visibility", "V", object2.visible, (v) => object2.visible = v));
10005
+ container.append(this.toggleControl("Visibility", "V", object3.visible, (v) => object3.visible = v));
9859
10006
  }
9860
10007
  container.append(this.separator("Mesh"));
9861
- this.addObjectControls(object2, intersection, container, selectedInstance);
10008
+ this.addObjectControls(object3, intersection, container, selectedInstance);
9862
10009
  container.append(this.separator());
9863
10010
  this.objectControlsContainer.append(container);
9864
10011
  }
@@ -10049,6 +10196,7 @@ var SceneStatsDisplay = class {
10049
10196
  appendTo(el) {
10050
10197
  el.append(this.statsContainer);
10051
10198
  }
10199
+ /** Stops the refresh timer and removes the panel from the page. */
10052
10200
  dispose() {
10053
10201
  clearInterval(this.interval);
10054
10202
  this.interval = void 0;
@@ -10073,6 +10221,7 @@ var InstanceManager = class {
10073
10221
  this.isInstanceManager = true;
10074
10222
  this.instanceCache = {};
10075
10223
  }
10224
+ /** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
10076
10225
  async get(key) {
10077
10226
  if (key in this.instanceCache) {
10078
10227
  console.debug(p9, "key in cache", key);
@@ -10080,6 +10229,7 @@ var InstanceManager = class {
10080
10229
  }
10081
10230
  return void 0;
10082
10231
  }
10232
+ /** Allocates a placement, calling the supplier once when the shared model is first needed. */
10083
10233
  async getOrCreate(key, supplier) {
10084
10234
  if (key in this.instanceCache) {
10085
10235
  return await this.get(key);
@@ -10096,6 +10246,7 @@ var InstanceManager = class {
10096
10246
  throw error;
10097
10247
  }
10098
10248
  }
10249
+ /** Forgets cached models without disposing them or releasing their existing placements. */
10099
10250
  reset() {
10100
10251
  for (let instanceCacheKey in this.instanceCache) {
10101
10252
  delete this.instanceCache[instanceCacheKey];
@@ -10141,6 +10292,7 @@ function matchesCondition(condition, state) {
10141
10292
  });
10142
10293
  }
10143
10294
  var BlockStateResolver = class {
10295
+ /** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
10144
10296
  static async defaults(blockState) {
10145
10297
  const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
10146
10298
  const state = {};
@@ -10160,9 +10312,11 @@ var BlockStateResolver = class {
10160
10312
  }
10161
10313
  return state;
10162
10314
  }
10315
+ /** Selects matching models. Supply `choose` to control selection within each alternatives array. */
10163
10316
  static select(blockState, state, choose = this.choose) {
10164
10317
  return this.matching(blockState, state).map(choose);
10165
10318
  }
10319
+ /** Returns matching model groups before choosing weighted alternatives. */
10166
10320
  static matching(blockState, state) {
10167
10321
  const out = [];
10168
10322
  if (blockState.variants) {
@@ -10186,6 +10340,7 @@ var BlockStateResolver = class {
10186
10340
  }
10187
10341
  return out;
10188
10342
  }
10343
+ /** Selects a random alternative using positive integer weights, each defaulting to 1. */
10189
10344
  static choose(variants) {
10190
10345
  if (!Array.isArray(variants)) return variants;
10191
10346
  if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
@@ -10203,6 +10358,7 @@ var BlockStateResolver = class {
10203
10358
  }
10204
10359
  return variants[variants.length - 1];
10205
10360
  }
10361
+ /** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
10206
10362
  static rotation(variant) {
10207
10363
  const rotation = new import_three29.Euler();
10208
10364
  if (typeof variant.x !== "undefined") {
@@ -10265,29 +10421,29 @@ var BlockTints = class {
10265
10421
  }
10266
10422
  }
10267
10423
  if (!indices.size) return tints;
10268
- let color;
10424
+ let color2;
10269
10425
  switch (rule.source) {
10270
10426
  case "constant":
10271
- color = rule.color;
10427
+ color2 = rule.color;
10272
10428
  break;
10273
10429
  case "grass":
10274
- color = await Colormaps.grassColor(key);
10430
+ color2 = await Colormaps.grassColor(key);
10275
10431
  break;
10276
10432
  case "redstone": {
10277
10433
  const power = Number(state.power ?? 0) / 15;
10278
10434
  const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
10279
10435
  const green = Math.max(0, power * power * 0.7 - 0.5);
10280
10436
  const blue = Math.max(0, power * power * 0.6 - 0.7);
10281
- color = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
10437
+ color2 = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
10282
10438
  break;
10283
10439
  }
10284
10440
  case "stem": {
10285
10441
  const age = Number(state.age ?? 0);
10286
- color = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
10442
+ color2 = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
10287
10443
  break;
10288
10444
  }
10289
10445
  }
10290
- return { ...Object.fromEntries([...indices].map((index) => [index, color])), ...tints };
10446
+ return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
10291
10447
  }
10292
10448
  };
10293
10449
 
@@ -10424,8 +10580,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
10424
10580
  return false;
10425
10581
  }
10426
10582
  return this._models.some((model) => {
10427
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10428
- return object2.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
10583
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10584
+ return object3.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
10429
10585
  });
10430
10586
  }
10431
10587
  getModelMatrix(model) {
@@ -10434,8 +10590,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
10434
10590
  return model.matrix.clone();
10435
10591
  }
10436
10592
  getModelCullMask(model, worldMask) {
10437
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10438
- if (object2.options.displayPosition) return 0;
10593
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10594
+ if (object3.options.displayPosition) return 0;
10439
10595
  const position = new import_three32.Vector3();
10440
10596
  const rotation = new import_three32.Quaternion();
10441
10597
  const scale = new import_three32.Vector3();
@@ -10452,14 +10608,14 @@ var _BlockObject = class _BlockObject extends SceneObject {
10452
10608
  replacements.push(model);
10453
10609
  continue;
10454
10610
  }
10455
- const object2 = isInstanceReference(model) ? model.instanceable : model;
10611
+ const object3 = isInstanceReference(model) ? model.instanceable : model;
10456
10612
  const cullMask = this.getModelCullMask(model, worldMask);
10457
- if (cullMask === (object2.options.cullMask ?? 0)) {
10613
+ if (cullMask === (object3.options.cullMask ?? 0)) {
10458
10614
  replacements.push(model);
10459
10615
  continue;
10460
10616
  }
10461
10617
  const matrix = this.getModelMatrix(model);
10462
- const replacement = await this.scene.addModel(object2.originalModel, { ...object2.options, cullMask });
10618
+ const replacement = await this.scene.addModel(object3.originalModel, { ...object3.options, cullMask });
10463
10619
  replacements.push(replacement);
10464
10620
  if (isInstanceReference(replacement)) {
10465
10621
  replacement.setMatrix(matrix);
@@ -10467,8 +10623,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
10467
10623
  replacement.matrix.copy(matrix);
10468
10624
  replacement.matrixWorldNeedsUpdate = true;
10469
10625
  matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
10470
- replacement.matrixAutoUpdate = object2.matrixAutoUpdate;
10471
- replacement.visible = object2.visible;
10626
+ replacement.matrixAutoUpdate = object3.matrixAutoUpdate;
10627
+ replacement.visible = object3.visible;
10472
10628
  }
10473
10629
  }
10474
10630
  } catch (error) {
@@ -10484,6 +10640,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
10484
10640
  }
10485
10641
  this.notifyDirty();
10486
10642
  }
10643
+ /** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
10487
10644
  get state() {
10488
10645
  return this._state;
10489
10646
  }
@@ -10583,6 +10740,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
10583
10740
  obj.setPosition(this.position);
10584
10741
  return obj;
10585
10742
  }
10743
+ /** Clears selected properties and rebuilds models without reapplying property defaults. */
10586
10744
  async resetState() {
10587
10745
  this._previousState = this._state;
10588
10746
  this._state = {};
@@ -12486,6 +12644,11 @@ function compoundList(tags, name) {
12486
12644
  // src/model/multiblock/StructureParser.ts
12487
12645
  init_MineRenderError();
12488
12646
  var StructureParser = class {
12647
+ /**
12648
+ * Parses decoded NBT, omitting air blocks.
12649
+ * @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
12650
+ * @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
12651
+ */
12489
12652
  static async parse(_nbt, paletteIndex) {
12490
12653
  const nbt = _nbt.value;
12491
12654
  let palette;
@@ -13320,6 +13483,7 @@ var SkinImage = class {
13320
13483
  // src/skin/SkinTextures.ts
13321
13484
  init_Textures();
13322
13485
  var SkinTextures = class {
13486
+ /** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
13323
13487
  static async get(src, legacy) {
13324
13488
  const image = await SkinImage.getData(src);
13325
13489
  const scale = image.width / 64;
@@ -13493,6 +13657,14 @@ var SkinObject = class extends SceneObject {
13493
13657
  }
13494
13658
  }
13495
13659
  }
13660
+ /**
13661
+ * Loads a skin texture, detecting slim arms and legacy layouts by default.
13662
+ * To override detection, set `slim` or `legacy` in the options passed to
13663
+ * `new SkinObject(options)` or `scene.addSkin(src, options)`.
13664
+ * Use {@link setSlim} or {@link setLegacy} to change these settings later.
13665
+ *
13666
+ * @param src - URL of the skin texture.
13667
+ */
13496
13668
  async setSkinTexture(src) {
13497
13669
  if (typeof src === "undefined") return;
13498
13670
  this.skinTextureSrc = src;
@@ -13600,59 +13772,60 @@ var _MineRenderScene = class _MineRenderScene extends import_three34.Scene {
13600
13772
  this.options = (0, import_ts_deepmerge8.default)({}, _MineRenderScene.DEFAULT_OPTIONS, options ?? {});
13601
13773
  }
13602
13774
  add(...objects) {
13603
- for (const object2 of objects) {
13775
+ for (const object3 of objects) {
13604
13776
  this.dirty = true;
13605
13777
  try {
13606
- super.add(object2);
13778
+ super.add(object3);
13607
13779
  } finally {
13608
- if (object2?.isObject3D && object2 !== this) {
13609
- this.updateObjectRegistration(object2, object2.parent === this);
13780
+ if (object3?.isObject3D && object3 !== this) {
13781
+ this.updateObjectRegistration(object3, object3.parent === this);
13610
13782
  }
13611
13783
  }
13612
13784
  }
13613
13785
  return this;
13614
13786
  }
13615
13787
  remove(...objects) {
13616
- for (const object2 of objects) {
13617
- if (!this.children.includes(object2)) continue;
13788
+ for (const object3 of objects) {
13789
+ if (!this.children.includes(object3)) continue;
13618
13790
  this.dirty = true;
13619
- this.updateObjectRegistration(object2, false);
13791
+ this.updateObjectRegistration(object3, false);
13620
13792
  try {
13621
- super.remove(object2);
13793
+ super.remove(object3);
13622
13794
  } finally {
13623
- this.updateObjectRegistration(object2, object2.parent === this);
13795
+ this.updateObjectRegistration(object3, object3.parent === this);
13624
13796
  }
13625
13797
  }
13626
13798
  return this;
13627
13799
  }
13628
- updateObjectRegistration(object2, attached) {
13629
- if (this.observedObjects.has(object2) === attached) return;
13630
- const observable = object2;
13800
+ updateObjectRegistration(object3, attached) {
13801
+ if (this.observedObjects.has(object3) === attached) return;
13802
+ const observable = object3;
13631
13803
  if (attached) {
13632
- this.observedObjects.add(object2);
13804
+ this.observedObjects.add(object3);
13633
13805
  observable.addEventListener("change", this.onObjectChange);
13634
13806
  } else {
13635
- this.observedObjects.delete(object2);
13807
+ this.observedObjects.delete(object3);
13636
13808
  observable.removeEventListener("change", this.onObjectChange);
13637
13809
  }
13638
13810
  const change = attached ? 1 : -1;
13639
13811
  this.stats.objectCount += change;
13640
- if (isSceneObject(object2)) {
13812
+ if (isSceneObject(object3)) {
13641
13813
  this.stats.sceneObjectCount += change;
13642
13814
  }
13643
13815
  }
13644
- async initAndAdd(...object2) {
13816
+ /** Initializes each object before attaching it to the scene. */
13817
+ async initAndAdd(...object3) {
13645
13818
  this.dirty = true;
13646
- for (let obj of object2) {
13819
+ for (let obj of object3) {
13647
13820
  await obj.init();
13648
13821
  }
13649
- return this.add(...object2);
13822
+ return this.add(...object3);
13650
13823
  }
13651
- async addSceneObject(asset, objectSupplier, _options, parent = this) {
13824
+ async addSceneObject(asset2, objectSupplier, _options, parent = this) {
13652
13825
  this.dirty = true;
13653
13826
  const obj = await objectSupplier();
13654
- if (obj?.options?.instanceMeshes && asset.key && asset.key?.assetType === "models") {
13655
- let key = asset.key.serialize();
13827
+ if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
13828
+ let key = asset2.key.serialize();
13656
13829
  if (isModelObject(obj)) {
13657
13830
  const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
13658
13831
  if (displayPosition) key += `|display:${displayPosition}`;
@@ -13676,13 +13849,33 @@ var _MineRenderScene = class _MineRenderScene extends import_three34.Scene {
13676
13849
  return obj;
13677
13850
  }
13678
13851
  }
13852
+ /**
13853
+ * Adds a model loaded with {@link Models.getMerged}.
13854
+ * Set `options.instanceMeshes` to `false` to create a separate ModelObject.
13855
+ *
13856
+ * @param parent - Object to attach the model to. Defaults to this scene.
13857
+ * @returns The model, or a reference to one placement of a shared model.
13858
+ */
13679
13859
  async addModel(model, options, parent = this) {
13680
13860
  options = { ...options, tints: await ItemTints.get(model, options?.tints) };
13681
13861
  return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
13682
13862
  }
13863
+ /**
13864
+ * Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
13865
+ * Set `options.initialState` to choose block properties such as `facing` or `axis`.
13866
+ * Use the returned object's `setPosition` method to move all of its model parts.
13867
+ */
13683
13868
  async addBlock(blockState, options, parent = this) {
13684
13869
  return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
13685
13870
  }
13871
+ /**
13872
+ * Loads a skin and adds an initialized player model.
13873
+ *
13874
+ * @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
13875
+ * Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
13876
+ * @param options - Player settings, including `slim` and `legacy` detection overrides.
13877
+ * @param parent - Object to attach the player to. Defaults to this scene.
13878
+ */
13686
13879
  async addSkin(skin, options, parent = this) {
13687
13880
  this.dirty = true;
13688
13881
  const obj = new SkinObject(options);
@@ -13694,9 +13887,14 @@ var _MineRenderScene = class _MineRenderScene extends import_three34.Scene {
13694
13887
  parent.add(obj);
13695
13888
  return obj;
13696
13889
  }
13890
+ /** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
13697
13891
  async addEntity(entity, options, parent = this) {
13698
13892
  return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
13699
13893
  }
13894
+ /**
13895
+ * Adds GUI layers in draw order. One GUI pixel equals one scene unit.
13896
+ * Layer positions start at the top left, with positive GUI y pointing down.
13897
+ */
13700
13898
  async addGui(layers, options, parent = this) {
13701
13899
  const obj = new GuiObject(layers, options);
13702
13900
  obj.scene = this;
@@ -13727,11 +13925,11 @@ var _changeEvent = { type: "change" };
13727
13925
  var _startEvent = { type: "start" };
13728
13926
  var _endEvent = { type: "end" };
13729
13927
  var OrbitControls = class extends import_three35.EventDispatcher {
13730
- constructor(object2, domElement) {
13928
+ constructor(object3, domElement) {
13731
13929
  super();
13732
13930
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
13733
13931
  if (domElement === document) console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
13734
- this.object = object2;
13932
+ this.object = object3;
13735
13933
  this.domElement = domElement;
13736
13934
  this.domElement.style.touchAction = "none";
13737
13935
  this.enabled = true;
@@ -13788,7 +13986,7 @@ var OrbitControls = class extends import_three35.EventDispatcher {
13788
13986
  };
13789
13987
  this.update = function() {
13790
13988
  const offset = new import_three35.Vector3();
13791
- const quat = new import_three35.Quaternion().setFromUnitVectors(object2.up, new import_three35.Vector3(0, 1, 0));
13989
+ const quat = new import_three35.Quaternion().setFromUnitVectors(object3.up, new import_three35.Vector3(0, 1, 0));
13792
13990
  const quatInverse = quat.clone().invert();
13793
13991
  const lastPosition = new import_three35.Vector3();
13794
13992
  const lastQuaternion = new import_three35.Quaternion();
@@ -14353,8 +14551,8 @@ var OrbitControls = class extends import_three35.EventDispatcher {
14353
14551
  }
14354
14552
  };
14355
14553
  var MapControls = class extends OrbitControls {
14356
- constructor(object2, domElement) {
14357
- super(object2, domElement);
14554
+ constructor(object3, domElement) {
14555
+ super(object3, domElement);
14358
14556
  this.screenSpacePanning = false;
14359
14557
  this.mouseButtons.LEFT = import_three35.MOUSE.PAN;
14360
14558
  this.mouseButtons.RIGHT = import_three35.MOUSE.ROTATE;
@@ -14513,6 +14711,7 @@ var _Renderer = class _Renderer {
14513
14711
  get element() {
14514
14712
  return this._element;
14515
14713
  }
14714
+ /** Attaches the canvas to an element and sizes it to that element's dimensions. */
14516
14715
  appendTo(element) {
14517
14716
  if (this._disposed) return;
14518
14717
  this._element = element;
@@ -14540,6 +14739,7 @@ var _Renderer = class _Renderer {
14540
14739
  events.add(changeEvent2);
14541
14740
  dispatcher.addEventListener(changeEvent2, this._changeListener);
14542
14741
  }
14742
+ /** Updates the camera and canvas size. Width and height are in CSS pixels. */
14543
14743
  resize(width, height) {
14544
14744
  if (this._disposed) return;
14545
14745
  if (isPerspectiveCamera(this.camera)) {
@@ -14578,12 +14778,14 @@ var _Renderer = class _Renderer {
14578
14778
  if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
14579
14779
  };
14580
14780
  }
14781
+ /** Starts or resumes the animation loop and registered frame callbacks. */
14581
14782
  start() {
14582
14783
  if (this._disposed) return;
14583
14784
  this.stop();
14584
14785
  this._running = true;
14585
14786
  if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
14586
14787
  }
14788
+ /** Pauses rendering and frame callbacks. Call {@link start} to resume. */
14587
14789
  stop() {
14588
14790
  if (this._disposed) return;
14589
14791
  this._running = false;
@@ -14692,12 +14894,15 @@ var _Renderer = class _Renderer {
14692
14894
  const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
14693
14895
  return canvas.toDataURL(mime, quality);
14694
14896
  }
14897
+ /** Exports visible scene meshes as OBJ text, without texture images. */
14695
14898
  toObj() {
14696
14899
  return SceneExporter.toObj(this.scene);
14697
14900
  }
14901
+ /** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
14698
14902
  toPLY(options) {
14699
14903
  return SceneExporter.toPLY(this.scene, options);
14700
14904
  }
14905
+ /** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
14701
14906
  toGLTF(options) {
14702
14907
  return SceneExporter.toGLTF(this.scene, options);
14703
14908
  }
@@ -14775,16 +14980,244 @@ init_SceneObject();
14775
14980
  // src/index.ts
14776
14981
  init_request();
14777
14982
 
14983
+ // src/scene/SceneDocument.ts
14984
+ init_DisplayPosition();
14985
+ function fail(path, message) {
14986
+ throw new Error(`${path}: ${message}`);
14987
+ }
14988
+ function object2(value, path) {
14989
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, "expected a JSON object");
14990
+ for (const key of Object.keys(value)) {
14991
+ if (["__proto__", "prototype", "constructor"].includes(key)) fail(`${path}.${key}`, "unsupported property");
14992
+ }
14993
+ return value;
14994
+ }
14995
+ function fields(value, path, allowed, required = []) {
14996
+ const input = object2(value, path);
14997
+ for (const key of required) if (input[key] === void 0) fail(`${path}.${key}`, "required");
14998
+ for (const [key, entry] of Object.entries(input)) {
14999
+ if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, "unsupported property");
15000
+ if (entry !== void 0) allowed[key](entry, `${path}.${key}`);
15001
+ }
15002
+ return input;
15003
+ }
15004
+ var text = (value, path) => {
15005
+ if (typeof value !== "string" || !value.trim()) fail(path, "expected a nonempty string");
15006
+ };
15007
+ var literalText = (value, path) => {
15008
+ if (typeof value !== "string") fail(path, "expected a string");
15009
+ };
15010
+ var boolean = (value, path) => {
15011
+ if (typeof value !== "boolean") fail(path, "expected a boolean");
15012
+ };
15013
+ var number = (value, path) => {
15014
+ if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number");
15015
+ };
15016
+ var positive = (value, path) => {
15017
+ number(value, path);
15018
+ if (value <= 0) fail(path, "expected a positive number");
15019
+ };
15020
+ var nonnegative = (value, path) => {
15021
+ number(value, path);
15022
+ if (value < 0) fail(path, "expected a nonnegative number");
15023
+ };
15024
+ var color = (value, path) => {
15025
+ number(value, path);
15026
+ if (!Number.isInteger(value) || value < 0 || value > 16777215) {
15027
+ fail(path, "expected an RGB integer from 0 to 16777215");
15028
+ }
15029
+ };
15030
+ var asset = (value, path) => {
15031
+ text(value, path);
15032
+ if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
15033
+ fail(path, "expected a resource ID such as minecraft:stone");
15034
+ }
15035
+ };
15036
+ var version = (value, path) => {
15037
+ text(value, path);
15038
+ if (!/^[a-zA-Z0-9._-]+$/.test(value)) fail(path, "expected a Minecraft version without slashes");
15039
+ };
15040
+ var tuple = (size, item = number) => (value, path) => {
15041
+ if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);
15042
+ for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);
15043
+ };
15044
+ var list = (item) => (value, path) => {
15045
+ if (!Array.isArray(value)) fail(path, "expected an array");
15046
+ for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);
15047
+ };
15048
+ var dictionary = (item, numericKeys = false) => (value, path) => {
15049
+ for (const [key, entry] of Object.entries(object2(value, path))) {
15050
+ if (numericKeys && !/^\d+$/.test(key)) fail(`${path}.${key}`, "expected a nonnegative tint index");
15051
+ item(entry, `${path}.${key}`);
15052
+ }
15053
+ };
15054
+ var enumeration = (values) => (value, path) => {
15055
+ if (!values.includes(value)) fail(path, `expected one of: ${values.join(", ")}`);
15056
+ };
15057
+ var textStyle = { color, bold: boolean, italic: boolean };
15058
+ function guiLayer(value, path) {
15059
+ const layer = object2(value, path);
15060
+ if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
15061
+ fail(path, "expected exactly one of texture, item, or text");
15062
+ }
15063
+ const layout = { name: text, position: tuple(2), size: tuple(2, positive) };
15064
+ if (layer.texture !== void 0) {
15065
+ fields(value, path, { ...layout, texture: asset, crop: (value2, path2) => {
15066
+ tuple(4)(value2, path2);
15067
+ const [x, y, width, height] = value2;
15068
+ nonnegative(x, `${path2}[0]`);
15069
+ nonnegative(y, `${path2}[1]`);
15070
+ positive(width, `${path2}[2]`);
15071
+ positive(height, `${path2}[3]`);
15072
+ } });
15073
+ } else if (layer.item !== void 0) {
15074
+ fields(value, path, { ...layout, item: asset, tints: dictionary(color, true) });
15075
+ } else {
15076
+ fields(value, path, {
15077
+ ...layout,
15078
+ ...textStyle,
15079
+ font: asset,
15080
+ shadow: boolean,
15081
+ maxWidth: positive,
15082
+ lineHeight: positive,
15083
+ text: (value2, path2) => {
15084
+ if (typeof value2 === "string") return;
15085
+ list((run, path3) => {
15086
+ fields(run, path3, { text: literalText, ...textStyle }, ["text"]);
15087
+ })(value2, path2);
15088
+ }
15089
+ });
15090
+ }
15091
+ }
15092
+ function validateObject(value, path) {
15093
+ const input = object2(value, path);
15094
+ const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };
15095
+ const required = ["id", "type"];
15096
+ const renderOptions = { wireframe: boolean };
15097
+ switch (input.type) {
15098
+ case "skin":
15099
+ fields(value, path, {
15100
+ ...common,
15101
+ skin: text,
15102
+ cape: (value2, path2) => {
15103
+ fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
15104
+ },
15105
+ hiddenParts: list(enumeration(SKIN_PARTS)),
15106
+ pose: (value2, path2) => {
15107
+ fields(value2, path2, {
15108
+ head: tuple(3),
15109
+ body: tuple(3),
15110
+ rightArm: tuple(3),
15111
+ leftArm: tuple(3),
15112
+ rightLeg: tuple(3),
15113
+ leftLeg: tuple(3),
15114
+ cape: tuple(3)
15115
+ });
15116
+ },
15117
+ options: (value2, path2) => {
15118
+ fields(value2, path2, { slim: boolean, legacy: boolean });
15119
+ }
15120
+ }, required);
15121
+ break;
15122
+ case "block":
15123
+ fields(value, path, {
15124
+ ...common,
15125
+ asset,
15126
+ state: dictionary(text),
15127
+ options: (value2, path2) => {
15128
+ fields(value2, path2, { ...renderOptions, tints: dictionary(color, true) });
15129
+ }
15130
+ }, [...required, "asset"]);
15131
+ break;
15132
+ case "item":
15133
+ case "model":
15134
+ fields(value, path, {
15135
+ ...common,
15136
+ asset,
15137
+ options: (value2, path2) => {
15138
+ fields(value2, path2, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) });
15139
+ }
15140
+ }, [...required, "asset"]);
15141
+ break;
15142
+ case "entity":
15143
+ fields(value, path, {
15144
+ ...common,
15145
+ asset,
15146
+ texture: asset,
15147
+ layers: list(text),
15148
+ when: list(text),
15149
+ textures: dictionary(asset),
15150
+ options: (value2, path2) => {
15151
+ fields(value2, path2, {
15152
+ ...renderOptions,
15153
+ flip: boolean,
15154
+ tints: dictionary((value3, path3) => {
15155
+ typeof value3 === "string" ? text(value3, path3) : color(value3, path3);
15156
+ })
15157
+ });
15158
+ },
15159
+ animation: (value2, path2) => {
15160
+ fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
15161
+ }
15162
+ }, [...required, "asset"]);
15163
+ if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
15164
+ break;
15165
+ case "gui":
15166
+ fields(value, path, { ...common, layers: list(guiLayer) }, [...required, "layers"]);
15167
+ break;
15168
+ default:
15169
+ fail(`${path}.type`, "expected skin, block, item, entity, model, or gui");
15170
+ }
15171
+ }
15172
+ function parseSceneDocument(value) {
15173
+ if (typeof value === "string") {
15174
+ try {
15175
+ value = JSON.parse(value);
15176
+ } catch {
15177
+ fail("scene", "invalid JSON");
15178
+ }
15179
+ }
15180
+ const input = fields(value, "scene", {
15181
+ format: enumeration(["minerender-scene"]),
15182
+ version: (value2, path) => {
15183
+ if (value2 !== 1) fail(path, "only scene document version 1 is supported");
15184
+ },
15185
+ minecraftVersion: version,
15186
+ camera: (value2, path) => {
15187
+ fields(value2, path, { position: tuple(3), target: tuple(3) }, ["position", "target"]);
15188
+ },
15189
+ objects: list(validateObject)
15190
+ }, ["format", "version", "objects"]);
15191
+ const ids = /* @__PURE__ */ new Set();
15192
+ for (const [index, entry] of input.objects.entries()) {
15193
+ if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID "${entry.id}"`);
15194
+ ids.add(entry.id);
15195
+ }
15196
+ return JSON.parse(JSON.stringify(input));
15197
+ }
15198
+
15199
+ // src/scene/SceneDocumentLoader.ts
15200
+ var import_three38 = require("three");
15201
+ init_AssetKey();
15202
+ init_AssetLoader();
15203
+ init_BlockStates();
15204
+ init_Entities();
15205
+ init_Models();
15206
+ init_DisplayPosition();
15207
+ init_SceneObject();
15208
+
14778
15209
  // src/skin/Skins.ts
14779
15210
  init_Requests();
14780
15211
  var Skins = class {
14781
15212
  //<editor-fold desc="Skins">
15213
+ /** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
14782
15214
  static async fromUuidOrUsername(uuidOrUsername) {
14783
15215
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14784
15216
  return this.fromUuid(uuidOrUsername);
14785
15217
  }
14786
15218
  return this.fromUsername(uuidOrUsername);
14787
15219
  }
15220
+ /** Builds a proxy URL for a UUID without downloading or checking the skin. */
14788
15221
  static async fromUuid(uuid) {
14789
15222
  return this.getMcProxySkinUrl(uuid);
14790
15223
  }
@@ -14796,6 +15229,7 @@ var Skins = class {
14796
15229
  return void 0;
14797
15230
  });
14798
15231
  }
15232
+ /** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
14799
15233
  static async fromMineSkin(mineskinId) {
14800
15234
  return this.getMineSkin(mineskinId).then((mineskin) => {
14801
15235
  if (mineskin) {
@@ -14806,12 +15240,14 @@ var Skins = class {
14806
15240
  }
14807
15241
  //</editor-fold>
14808
15242
  //<editor-fold desc="Capes">
15243
+ /** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
14809
15244
  static async capeFromUuidOrUsername(uuidOrUsername) {
14810
15245
  if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
14811
15246
  return this.capeFromUuid(uuidOrUsername);
14812
15247
  }
14813
15248
  return this.capeFromUsername(uuidOrUsername);
14814
15249
  }
15250
+ /** Builds a proxy URL without checking whether the player has a cape. */
14815
15251
  static async capeFromUuid(uuid) {
14816
15252
  return this.getMcProxyCapeUrl(uuid);
14817
15253
  }
@@ -14940,6 +15376,195 @@ var Skins = class {
14940
15376
  //</editor-fold>
14941
15377
  };
14942
15378
 
15379
+ // src/scene/SceneDocumentLoader.ts
15380
+ var DocumentObjectScene = class extends MineRenderScene {
15381
+ async addSceneObject(_asset, supplier, _options, parent = this) {
15382
+ const object3 = await supplier();
15383
+ return this.initialize(object3, parent);
15384
+ }
15385
+ async initialize(object3, parent = this) {
15386
+ object3.scene = this;
15387
+ try {
15388
+ await object3.init();
15389
+ parent.add(object3);
15390
+ return object3;
15391
+ } catch (error) {
15392
+ object3.dispose();
15393
+ object3.removeFromParent();
15394
+ throw error;
15395
+ }
15396
+ }
15397
+ dispose() {
15398
+ for (const child of [...this.children]) {
15399
+ if (child.parent !== this) continue;
15400
+ if (isSceneObject(child)) child.dispose();
15401
+ child.removeFromParent();
15402
+ }
15403
+ }
15404
+ };
15405
+ async function skinTexture(value, cape = false) {
15406
+ if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;
15407
+ const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));
15408
+ if (!texture) throw new Error(`Could not find ${cape ? "cape" : "skin"} for "${value}"`);
15409
+ return texture;
15410
+ }
15411
+ var SceneDocumentLoader = class {
15412
+ static parse(value) {
15413
+ return parseSceneDocument(value);
15414
+ }
15415
+ /**
15416
+ * Loads objects concurrently and attaches them in document order after every load succeeds.
15417
+ * A failure waits for all loads, disposes staged objects, and reports the first error in document order.
15418
+ */
15419
+ static async load(scene, value, parent = scene) {
15420
+ const definition = this.parse(value);
15421
+ if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
15422
+ throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
15423
+ }
15424
+ const root = new import_three38.Group();
15425
+ root.name = "MineRender scene document";
15426
+ const objects = [];
15427
+ let disposed = false;
15428
+ const loaded = {
15429
+ root,
15430
+ objects,
15431
+ definition,
15432
+ advanceAnimations: (delta) => {
15433
+ if (!disposed) for (const object3 of objects) object3.advanceAnimation(delta);
15434
+ },
15435
+ dispose: () => {
15436
+ if (disposed) return;
15437
+ disposed = true;
15438
+ root.removeFromParent();
15439
+ for (const object3 of objects) object3.dispose();
15440
+ root.clear();
15441
+ scene.dirty = true;
15442
+ }
15443
+ };
15444
+ try {
15445
+ const results = await Promise.allSettled(definition.objects.map((object3) => this.loadValidatedObject(scene, object3)));
15446
+ for (const result of results) if (result.status === "fulfilled") objects.push(result.value);
15447
+ const failure = results.find((result) => result.status === "rejected");
15448
+ if (failure) throw failure.reason;
15449
+ for (const object3 of objects) root.add(object3.root);
15450
+ parent.add(root);
15451
+ scene.dirty = true;
15452
+ return loaded;
15453
+ } catch (error) {
15454
+ loaded.dispose();
15455
+ throw error;
15456
+ }
15457
+ }
15458
+ /** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */
15459
+ static async loadObject(scene, value, parent = scene) {
15460
+ const definition = this.parse({ format: "minerender-scene", version: 1, objects: [value] }).objects[0];
15461
+ return this.loadValidatedObject(scene, definition, parent);
15462
+ }
15463
+ static async loadValidatedObject(scene, definition, parent) {
15464
+ const root = new import_three38.Group();
15465
+ root.name = definition.name ?? definition.id;
15466
+ root.userData.sceneObjectId = definition.id;
15467
+ root.position.fromArray(definition.position ?? [0, 0, 0]);
15468
+ root.rotation.set(...definition.rotation ?? [0, 0, 0]);
15469
+ root.scale.fromArray(definition.scale ?? [1, 1, 1]);
15470
+ root.visible = definition.visible ?? true;
15471
+ const content = new DocumentObjectScene();
15472
+ root.add(content);
15473
+ let object3;
15474
+ try {
15475
+ switch (definition.type) {
15476
+ case "skin": {
15477
+ const skin = new SkinObject({ ...definition.options, instanceMeshes: false });
15478
+ object3 = skin;
15479
+ const texture = definition.skin ? await skinTexture(definition.skin) : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? "slim/alex" : "wide/steve"}.png`;
15480
+ await skin.setSkinTexture(texture);
15481
+ await content.initialize(skin);
15482
+ if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
15483
+ for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
15484
+ skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
15485
+ }
15486
+ for (const part of definition.hiddenParts ?? []) {
15487
+ const mesh = skin.getMeshByName(part);
15488
+ if (mesh) mesh.visible = false;
15489
+ }
15490
+ break;
15491
+ }
15492
+ case "block": {
15493
+ const state = await BlockStates.get(AssetKey.parse("blockstates", definition.asset));
15494
+ if (!state) throw new Error(`Could not load block ${definition.asset}`);
15495
+ object3 = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false });
15496
+ break;
15497
+ }
15498
+ case "item":
15499
+ case "model": {
15500
+ let key = AssetKey.parse("models", definition.asset);
15501
+ if (definition.type === "item" && key.type !== "item") {
15502
+ key = new AssetKey(key.namespace, key.getFullPath(), "models", "item");
15503
+ }
15504
+ const model = await Models.getMerged(key);
15505
+ if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);
15506
+ object3 = await content.addModel(model, {
15507
+ ...definition.type === "item" ? { displayPosition: "gui" /* GUI */ } : {},
15508
+ ...definition.options,
15509
+ instanceMeshes: false
15510
+ });
15511
+ break;
15512
+ }
15513
+ case "entity": {
15514
+ const key = AssetKey.parse("entity-models", definition.asset);
15515
+ const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse("textures", definition.texture) : void 0, {
15516
+ layers: definition.layers,
15517
+ when: definition.when,
15518
+ textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse("textures", texture)]))
15519
+ });
15520
+ if (!model) throw new Error(`Could not load entity ${definition.asset}`);
15521
+ const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
15522
+ object3 = entity;
15523
+ if (definition.animation) {
15524
+ const animations = await Entities.getAnimations(key);
15525
+ const clip = animations?.[definition.animation.name];
15526
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
15527
+ entity.playAnimation(clip, definition.animation);
15528
+ }
15529
+ break;
15530
+ }
15531
+ case "gui":
15532
+ object3 = await content.initialize(new GuiObject(definition.layers));
15533
+ break;
15534
+ }
15535
+ let disposed = false;
15536
+ const loaded = {
15537
+ root,
15538
+ object: object3,
15539
+ definition,
15540
+ advanceAnimation: (delta) => {
15541
+ if (!disposed && definition.type === "entity" && !definition.animation?.paused) object3.advanceAnimation(delta);
15542
+ },
15543
+ dispose: () => {
15544
+ if (disposed) return;
15545
+ disposed = true;
15546
+ root.removeFromParent();
15547
+ object3.dispose();
15548
+ object3.removeFromParent();
15549
+ content.dispose();
15550
+ root.clear();
15551
+ scene.dirty = true;
15552
+ }
15553
+ };
15554
+ parent?.add(root);
15555
+ scene.dirty = true;
15556
+ return loaded;
15557
+ } catch (error) {
15558
+ object3?.dispose();
15559
+ object3?.removeFromParent();
15560
+ content.dispose();
15561
+ root.removeFromParent();
15562
+ root.clear();
15563
+ throw new Error(`Scene object "${definition.name ?? definition.id}": ${error instanceof Error ? error.message : String(error)}`);
15564
+ }
15565
+ }
15566
+ };
15567
+
14943
15568
  // src/texture/index.ts
14944
15569
  init_Colormaps();
14945
15570
  init_TextureAtlas();
@@ -14966,16 +15591,19 @@ var ChunkData = class _ChunkData {
14966
15591
  this.freeIds = [];
14967
15592
  this.nbt = /* @__PURE__ */ new Map();
14968
15593
  }
15594
+ /** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
14969
15595
  get(index) {
14970
15596
  _ChunkData.validateIndex(index);
14971
15597
  const entry = this.palette[this.indices[index]];
14972
15598
  return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
14973
15599
  }
15600
+ /** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
14974
15601
  snapshot(index) {
14975
15602
  _ChunkData.validateIndex(index);
14976
15603
  const entry = this.palette[this.indices[index]];
14977
15604
  return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
14978
15605
  }
15606
+ /** Copies block data into a cell. An omitted block or an air block clears the cell. */
14979
15607
  set(index, block) {
14980
15608
  _ChunkData.validateIndex(index);
14981
15609
  let state;
@@ -15040,15 +15668,23 @@ var ChunkData = class _ChunkData {
15040
15668
 
15041
15669
  // src/world/AnvilParser.ts
15042
15670
  var AnvilParser = class {
15671
+ /** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
15043
15672
  static getChunkList(data) {
15044
15673
  return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
15045
15674
  }
15675
+ /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
15046
15676
  static async parse(data) {
15047
15677
  const bytes = this.bytes(data);
15048
15678
  const chunks = [];
15049
15679
  for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
15050
15680
  return { chunks };
15051
15681
  }
15682
+ /**
15683
+ * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
15684
+ * @param localX - Chunk x within the region, from 0 to 31.
15685
+ * @param localZ - Chunk z within the region, from 0 to 31.
15686
+ * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
15687
+ */
15052
15688
  static async parseChunk(data, localX, localZ) {
15053
15689
  if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
15054
15690
  throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
@@ -15210,12 +15846,12 @@ var AnvilParser = class {
15210
15846
  };
15211
15847
 
15212
15848
  // src/world/Chunk.ts
15213
- var import_three41 = require("three");
15849
+ var import_three42 = require("three");
15214
15850
  init_BlockStates();
15215
15851
  init_AssetKey();
15216
15852
 
15217
15853
  // src/world/MineRenderWorld.ts
15218
- var import_three39 = require("three");
15854
+ var import_three40 = require("three");
15219
15855
  init_Model();
15220
15856
  init_BatchedExecutor();
15221
15857
  init_AssetKey();
@@ -15223,7 +15859,7 @@ init_BlockStates();
15223
15859
  init_CubeFace();
15224
15860
 
15225
15861
  // src/world/SectionModels.ts
15226
- var import_three38 = require("three");
15862
+ var import_three39 = require("three");
15227
15863
  init_AssetKey();
15228
15864
  init_Models();
15229
15865
  init_CubeFace();
@@ -15253,6 +15889,7 @@ var SectionModels = class {
15253
15889
  this.templates = /* @__PURE__ */ new Set();
15254
15890
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
15255
15891
  }
15892
+ /** Returns a cube template, or `undefined` when the block requires an individual render object. */
15256
15893
  async get(blockState, properties = {}) {
15257
15894
  if (!blockState.variants || blockState.multipart) return void 0;
15258
15895
  let states = this.states.get(blockState);
@@ -15281,20 +15918,20 @@ var SectionModels = class {
15281
15918
  if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
15282
15919
  const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
15283
15920
  const template = await cached(models, key, async () => {
15284
- const object2 = new SectionModel(model, {
15921
+ const object3 = new SectionModel(model, {
15285
15922
  instanceMeshes: false,
15286
15923
  mergeMeshes: false,
15287
15924
  tints,
15288
15925
  uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
15289
15926
  });
15290
- await object2.init();
15291
- const geometry = object2.children[0].geometry;
15292
- geometry.applyMatrix4(new import_three38.Matrix4().makeRotationFromEuler(rotation));
15293
- object2.clear();
15927
+ await object3.init();
15928
+ const geometry = object3.children[0].geometry;
15929
+ geometry.applyMatrix4(new import_three39.Matrix4().makeRotationFromEuler(rotation));
15930
+ object3.clear();
15294
15931
  const cullFaces = CUBE_FACES.map((face) => {
15295
15932
  const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
15296
15933
  if (local < 0) return 0;
15297
- const normal = new import_three38.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
15934
+ const normal = new import_three39.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
15298
15935
  const world = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
15299
15936
  return 1 << world;
15300
15937
  });
@@ -15306,6 +15943,7 @@ var SectionModels = class {
15306
15943
  }
15307
15944
  return { variants, templates };
15308
15945
  }
15946
+ /** Disposes prepared geometry and clears this world's template caches. */
15309
15947
  clear() {
15310
15948
  for (const template of this.templates) template.geometry.dispose();
15311
15949
  this.templates.clear();
@@ -15324,20 +15962,20 @@ var MineRenderWorld = class {
15324
15962
  }
15325
15963
  getBlockAt(posOrX, y, z) {
15326
15964
  if (typeof posOrX == "number") {
15327
- return this.getBlockAt(new import_three39.Vector3(posOrX, y, z));
15965
+ return this.getBlockAt(new import_three40.Vector3(posOrX, y, z));
15328
15966
  }
15329
15967
  if (isTripleArray(posOrX)) {
15330
- return this.getBlockAt(new import_three39.Vector3(posOrX[0], posOrX[1], posOrX[2]));
15968
+ return this.getBlockAt(new import_three40.Vector3(posOrX[0], posOrX[1], posOrX[2]));
15331
15969
  }
15332
15970
  this.validatePosBounds(posOrX);
15333
15971
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
15334
15972
  }
15335
15973
  async setBlockAt(posOrX, yOrBlock, z, block) {
15336
15974
  if (typeof posOrX == "number") {
15337
- return this.setBlockAt(new import_three39.Vector3(posOrX, yOrBlock, z), block);
15975
+ return this.setBlockAt(new import_three40.Vector3(posOrX, yOrBlock, z), block);
15338
15976
  }
15339
15977
  if (isTripleArray(posOrX)) {
15340
- return this.setBlockAt(new import_three39.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
15978
+ return this.setBlockAt(new import_three40.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
15341
15979
  }
15342
15980
  return this.placeBlock(posOrX, yOrBlock);
15343
15981
  }
@@ -15348,14 +15986,21 @@ var MineRenderWorld = class {
15348
15986
  }
15349
15987
  async setBlockVisibleAt(posOrX, yOrVisible, z, visible) {
15350
15988
  if (typeof posOrX === "number") {
15351
- return this.setBlockVisibleAt(new import_three39.Vector3(posOrX, yOrVisible, z), visible);
15989
+ return this.setBlockVisibleAt(new import_three40.Vector3(posOrX, yOrVisible, z), visible);
15352
15990
  }
15353
15991
  if (isTripleArray(posOrX)) {
15354
- return this.setBlockVisibleAt(new import_three39.Vector3(...posOrX), yOrVisible);
15992
+ return this.setBlockVisibleAt(new import_three40.Vector3(...posOrX), yOrVisible);
15355
15993
  }
15356
15994
  this.validatePosBounds(posOrX);
15357
15995
  await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
15358
15996
  }
15997
+ /**
15998
+ * Places a structure's blocks at their stored positions, then refreshes neighboring faces.
15999
+ * Entity NBT is retained by the structure but is not rendered.
16000
+ *
16001
+ * @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
16002
+ * @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
16003
+ */
15359
16004
  async placeMultiBlock(multiblock, useBatches = true, executor) {
15360
16005
  const changes = /* @__PURE__ */ new Map();
15361
16006
  try {
@@ -15368,7 +16013,7 @@ var MineRenderWorld = class {
15368
16013
  const collect = async (positions2) => {
15369
16014
  for (const pos of positions2) changes.set(pos.toArray().join(","), pos);
15370
16015
  };
15371
- const place = (block) => this.placeBlock(new import_three39.Vector3(...block.position), block, collect);
16016
+ const place = (block) => this.placeBlock(new import_three40.Vector3(...block.position), block, collect);
15372
16017
  const keys = /* @__PURE__ */ new Map();
15373
16018
  for (const block of multiblock.blocks) {
15374
16019
  if (Chunk.isAir(block)) continue;
@@ -15452,6 +16097,7 @@ var MineRenderWorld = class {
15452
16097
  });
15453
16098
  }
15454
16099
  }
16100
+ /** Removes all chunks and releases their block placements and section meshes. */
15455
16101
  async clear() {
15456
16102
  await this.culling;
15457
16103
  const chunks = [...this._chunks.values()];
@@ -15465,13 +16111,13 @@ var MineRenderWorld = class {
15465
16111
  for (const pos of positions) {
15466
16112
  this.pendingCulling.set(pos.toArray().join(","), pos.clone());
15467
16113
  for (const offset of CUBE_FACE_OFFSETS) {
15468
- const neighbor = pos.clone().add(new import_three39.Vector3(...offset));
16114
+ const neighbor = pos.clone().add(new import_three40.Vector3(...offset));
15469
16115
  this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
15470
16116
  }
15471
16117
  for (let y = -1; y <= 1; y++) {
15472
16118
  for (let z = -1; z <= 1; z++) {
15473
16119
  for (let x = -1; x <= 1; x++) {
15474
- const neighbor = pos.clone().add(new import_three39.Vector3(x, y, z));
16120
+ const neighbor = pos.clone().add(new import_three40.Vector3(x, y, z));
15475
16121
  if (this.getBlockAt(neighbor)?.object?.fluidKind) {
15476
16122
  this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
15477
16123
  }
@@ -15495,19 +16141,19 @@ var MineRenderWorld = class {
15495
16141
  }
15496
16142
  if (block.object?.fluidKind) {
15497
16143
  await block.object.updateFluid((x, y, z) => {
15498
- const neighbor = pos.clone().add(new import_three39.Vector3(x, y, z));
16144
+ const neighbor = pos.clone().add(new import_three40.Vector3(x, y, z));
15499
16145
  const section = this.getChunkAt(neighbor);
15500
- const object2 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
16146
+ const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
15501
16147
  return {
15502
- fluid: object2?.fluidKind,
15503
- level: object2?.fluidLevel,
16148
+ fluid: object3?.fluidKind,
16149
+ level: object3?.fluidLevel,
15504
16150
  solid: section?.isOccludingAt(neighbor) ?? false
15505
16151
  };
15506
16152
  });
15507
16153
  }
15508
16154
  let mask = 0;
15509
16155
  for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
15510
- const neighbor = pos.clone().add(new import_three39.Vector3(...offset));
16156
+ const neighbor = pos.clone().add(new import_three40.Vector3(...offset));
15511
16157
  if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
15512
16158
  mask |= 1 << face;
15513
16159
  }
@@ -15537,18 +16183,21 @@ var MineRenderWorld = class {
15537
16183
  }
15538
16184
  return chunk;
15539
16185
  }
16186
+ /** Returns the loaded chunk containing a world block position, without creating a chunk. */
15540
16187
  getChunkAt(pos) {
15541
16188
  return this._chunks.get(this.worldPosToChunkKey(pos));
15542
16189
  }
16190
+ /** Converts block coordinates to scene units by multiplying each component by 16. */
15543
16191
  static worldToScenePosition(pos) {
15544
- return new import_three39.Vector3(
16192
+ return new import_three40.Vector3(
15545
16193
  pos.x * 16,
15546
16194
  pos.y * 16,
15547
16195
  pos.z * 16
15548
16196
  );
15549
16197
  }
16198
+ /** Converts scene units to block coordinates without rounding to an integer block. */
15550
16199
  static sceneToWorldPosition(pos) {
15551
- return new import_three39.Vector3(
16200
+ return new import_three40.Vector3(
15552
16201
  pos.x / 16,
15553
16202
  pos.y / 16,
15554
16203
  pos.z / 16
@@ -15571,14 +16220,15 @@ var MineRenderWorld = class {
15571
16220
  init_Model();
15572
16221
 
15573
16222
  // src/world/SectionMesh.ts
15574
- var import_three40 = require("three");
16223
+ var import_three41 = require("three");
15575
16224
  init_CanvasCompat();
15576
16225
  init_Materials();
15577
- var SectionMesh = class _SectionMesh extends import_three40.Group {
16226
+ var SectionMesh = class _SectionMesh extends import_three41.Group {
15578
16227
  constructor() {
15579
16228
  super(...arguments);
15580
16229
  this.ownedMeshes = [];
15581
16230
  }
16231
+ /** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
15582
16232
  static build(entries, maxAtlasSize = 2048) {
15583
16233
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
15584
16234
  const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
@@ -15626,7 +16276,7 @@ var SectionMesh = class _SectionMesh extends import_three40.Group {
15626
16276
  const normal = template.geometry.getAttribute("normal");
15627
16277
  const uv = template.geometry.getAttribute("uv");
15628
16278
  const bounds = template.geometry.getAttribute("uvBounds");
15629
- const color = template.geometry.getAttribute("color");
16279
+ const color2 = template.geometry.getAttribute("color");
15630
16280
  const sourceIndices = template.geometry.getIndex();
15631
16281
  const placement = locations.get(template.atlas).placement;
15632
16282
  const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
@@ -15648,24 +16298,24 @@ var SectionMesh = class _SectionMesh extends import_three40.Group {
15648
16298
  mapU(bounds?.getZ(vertex) ?? 1),
15649
16299
  mapV(bounds?.getW(vertex) ?? 1)
15650
16300
  );
15651
- colors.push(color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1);
16301
+ colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
15652
16302
  }
15653
16303
  for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
15654
16304
  }
15655
16305
  }
15656
- const geometry = new import_three40.BufferGeometry();
15657
- geometry.setAttribute("position", new import_three40.Float32BufferAttribute(positions, 3));
15658
- geometry.setAttribute("normal", new import_three40.Float32BufferAttribute(normals, 3));
15659
- geometry.setAttribute("uv", new import_three40.Float32BufferAttribute(uvs, 2));
15660
- geometry.setAttribute("uvBounds", new import_three40.Float32BufferAttribute(uvBounds, 4));
15661
- geometry.setAttribute("color", new import_three40.Float32BufferAttribute(colors, 3));
16306
+ const geometry = new import_three41.BufferGeometry();
16307
+ geometry.setAttribute("position", new import_three41.Float32BufferAttribute(positions, 3));
16308
+ geometry.setAttribute("normal", new import_three41.Float32BufferAttribute(normals, 3));
16309
+ geometry.setAttribute("uv", new import_three41.Float32BufferAttribute(uvs, 2));
16310
+ geometry.setAttribute("uvBounds", new import_three41.Float32BufferAttribute(uvBounds, 4));
16311
+ geometry.setAttribute("color", new import_three41.Float32BufferAttribute(colors, 3));
15662
16312
  geometry.setIndex(indices);
15663
16313
  geometry.computeBoundingBox();
15664
16314
  geometry.computeBoundingSphere();
15665
16315
  const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
15666
16316
  material.vertexColors = true;
15667
16317
  const texture = material.uniforms?.map?.value ?? material.map;
15668
- const mesh = new import_three40.Mesh(geometry, material);
16318
+ const mesh = new import_three41.Mesh(geometry, material);
15669
16319
  section.add(mesh);
15670
16320
  section.ownedMeshes.push({ mesh, texture });
15671
16321
  }
@@ -15701,10 +16351,10 @@ var Chunk = class _Chunk {
15701
16351
  }
15702
16352
  getBlockAt(posOrX, y, z) {
15703
16353
  if (typeof posOrX == "number") {
15704
- return this.getBlockAt(new import_three41.Vector3(posOrX, y, z));
16354
+ return this.getBlockAt(new import_three42.Vector3(posOrX, y, z));
15705
16355
  }
15706
16356
  if (isTripleArray(posOrX)) {
15707
- return this.getBlockAt(new import_three41.Vector3(posOrX[0], posOrX[1], posOrX[2]));
16357
+ return this.getBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]));
15708
16358
  }
15709
16359
  const pos = this.worldPosToChunkPos(posOrX);
15710
16360
  const index = _Chunk.chunkPosToBlockIndex(pos);
@@ -15712,10 +16362,10 @@ var Chunk = class _Chunk {
15712
16362
  }
15713
16363
  async setBlockAt(posOrX, yOrBlock, z, block) {
15714
16364
  if (typeof posOrX == "number") {
15715
- return this.setBlockAt(new import_three41.Vector3(posOrX, yOrBlock, z), block);
16365
+ return this.setBlockAt(new import_three42.Vector3(posOrX, yOrBlock, z), block);
15716
16366
  }
15717
16367
  if (isTripleArray(posOrX)) {
15718
- return this.setBlockAt(new import_three41.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
16368
+ return this.setBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
15719
16369
  }
15720
16370
  const worldPos = posOrX;
15721
16371
  const pos = this.worldPosToChunkPos(worldPos);
@@ -15723,7 +16373,7 @@ var Chunk = class _Chunk {
15723
16373
  return this.setBlockInChunkAt(pos, block, worldPos);
15724
16374
  }
15725
16375
  /**
15726
- * Set a block at integer chunk-local coordinates from 0 to 15.
16376
+ * Places a block at integer chunk-local coordinates from 0 to 15.
15727
16377
  */
15728
16378
  async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
15729
16379
  if (typeof worldPos === "undefined") {
@@ -15736,7 +16386,7 @@ var Chunk = class _Chunk {
15736
16386
  this.renderedBlocks.get(index)?.object?.removeFromScene();
15737
16387
  if (this.sectionBlocks.delete(index)) this.meshDirty = true;
15738
16388
  this.renderedBlocks.delete(index);
15739
- let object2;
16389
+ let object3;
15740
16390
  try {
15741
16391
  if (!readBlock) return void 0;
15742
16392
  const stored = readBlock();
@@ -15751,27 +16401,28 @@ var Chunk = class _Chunk {
15751
16401
  this.sectionBlocks.set(index, { index, template, cullMask: 0 });
15752
16402
  this.meshDirty = true;
15753
16403
  } else {
15754
- object2 = await this.scene.addBlock(blockState, {
16404
+ object3 = await this.scene.addBlock(blockState, {
15755
16405
  mergeMeshes: true,
15756
16406
  instanceMeshes: true,
15757
16407
  maxInstanceCount: 2e3,
15758
16408
  initialState: stored.properties
15759
16409
  });
15760
- object2.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
16410
+ object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
15761
16411
  }
15762
16412
  const info = { get block() {
15763
16413
  return readBlock();
15764
- }, object: object2 };
16414
+ }, object: object3 };
15765
16415
  this.renderedBlocks.set(index, info);
15766
16416
  return info;
15767
16417
  } catch (error) {
15768
16418
  this.data.set(index, void 0);
15769
- object2?.removeFromScene();
16419
+ object3?.removeFromScene();
15770
16420
  throw error;
15771
16421
  } finally {
15772
16422
  await onBlocksChanged?.([worldPos]);
15773
16423
  }
15774
16424
  }
16425
+ /** Changes visibility at world block coordinates while preserving the block's data. */
15775
16426
  async setBlockVisibleAt(pos, visible) {
15776
16427
  const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
15777
16428
  const block = this.renderedBlocks.get(index);
@@ -15825,9 +16476,10 @@ var Chunk = class _Chunk {
15825
16476
  this.scene.dirty = true;
15826
16477
  this.meshDirty = false;
15827
16478
  }
16479
+ /** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
15828
16480
  async clear(onBlocksChanged = this.onBlocksChanged) {
15829
16481
  const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
15830
- new import_three41.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
16482
+ new import_three42.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
15831
16483
  ));
15832
16484
  for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
15833
16485
  this.sectionBlocks.clear();
@@ -15845,6 +16497,7 @@ var Chunk = class _Chunk {
15845
16497
  static isAir(block) {
15846
16498
  return ChunkData.isAir(block);
15847
16499
  }
16500
+ /** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
15848
16501
  static chunkPosToBlockIndex(pos) {
15849
16502
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
15850
16503
  throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
@@ -15852,14 +16505,14 @@ var Chunk = class _Chunk {
15852
16505
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
15853
16506
  }
15854
16507
  worldPosToChunkPos(pos) {
15855
- return new import_three41.Vector3(
16508
+ return new import_three42.Vector3(
15856
16509
  pos.x - this.x * 16,
15857
16510
  pos.y - this.y * 16,
15858
16511
  pos.z - this.z * 16
15859
16512
  );
15860
16513
  }
15861
16514
  chunkPosToWorldPos(pos) {
15862
- return new import_three41.Vector3(
16515
+ return new import_three42.Vector3(
15863
16516
  this.x * 16 + pos.x,
15864
16517
  this.y * 16 + pos.y,
15865
16518
  this.z * 16 + pos.z