minerender 2.1.0-beta.7 → 2.1.0-beta.8

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.
Files changed (73) hide show
  1. package/README.md +10 -1
  2. package/dist/browser/esm/armor/Armor.mjs +84 -0
  3. package/dist/browser/esm/armor/Armor.mjs.map +1 -0
  4. package/dist/browser/esm/armor/index.mjs +3 -0
  5. package/dist/browser/esm/armor/index.mjs.map +1 -0
  6. package/dist/browser/esm/assets/BlockEntities.mjs +2 -0
  7. package/dist/browser/esm/assets/BlockEntities.mjs.map +1 -1
  8. package/dist/browser/esm/assets/Entities.mjs +39 -11
  9. package/dist/browser/esm/assets/Entities.mjs.map +1 -1
  10. package/dist/browser/esm/assets/MineRenderData.mjs +8 -17
  11. package/dist/browser/esm/assets/MineRenderData.mjs.map +1 -1
  12. package/dist/browser/esm/assets/ModelTextures.mjs +13 -7
  13. package/dist/browser/esm/assets/ModelTextures.mjs.map +1 -1
  14. package/dist/browser/esm/assets/_versions/DatasetVersions.mjs +22 -0
  15. package/dist/browser/esm/assets/_versions/DatasetVersions.mjs.map +1 -0
  16. package/dist/browser/esm/cache/Caching.mjs +8 -0
  17. package/dist/browser/esm/cache/Caching.mjs.map +1 -1
  18. package/dist/browser/esm/entity/scene/EntityObject.mjs +31 -5
  19. package/dist/browser/esm/entity/scene/EntityObject.mjs.map +1 -1
  20. package/dist/browser/esm/env/browser/BrowserEnv.mjs +11 -4
  21. package/dist/browser/esm/env/browser/BrowserEnv.mjs.map +1 -1
  22. package/dist/browser/esm/index.browser.d.mts +129 -13
  23. package/dist/browser/esm/index.browser.mjs.map +1 -1
  24. package/dist/browser/esm/index.mjs +1 -0
  25. package/dist/browser/esm/index.mjs.map +1 -1
  26. package/dist/browser/esm/model/ItemGlint.mjs +7 -6
  27. package/dist/browser/esm/model/ItemGlint.mjs.map +1 -1
  28. package/dist/browser/esm/model/block/scene/BlockObject.mjs +16 -5
  29. package/dist/browser/esm/model/block/scene/BlockObject.mjs.map +1 -1
  30. package/dist/browser/esm/scene/SceneDocument.mjs +35 -5
  31. package/dist/browser/esm/scene/SceneDocument.mjs.map +1 -1
  32. package/dist/browser/esm/scene/SceneDocumentLoader.mjs +14 -3
  33. package/dist/browser/esm/scene/SceneDocumentLoader.mjs.map +1 -1
  34. package/dist/browser/esm/skin/SkinGeometries.mjs +4 -1
  35. package/dist/browser/esm/skin/SkinGeometries.mjs.map +1 -1
  36. package/dist/browser/esm/skin/SkinPart.mjs +2 -0
  37. package/dist/browser/esm/skin/SkinPart.mjs.map +1 -1
  38. package/dist/browser/esm/skin/SkinTextureCoordinates.mjs +11 -1
  39. package/dist/browser/esm/skin/SkinTextureCoordinates.mjs.map +1 -1
  40. package/dist/browser/esm/skin/SkinTextures.mjs +1 -1
  41. package/dist/browser/esm/skin/SkinTextures.mjs.map +1 -1
  42. package/dist/browser/esm/skin/scene/SkinObject.mjs +119 -7
  43. package/dist/browser/esm/skin/scene/SkinObject.mjs.map +1 -1
  44. package/dist/browser/esm/texture/PalettedTextures.mjs +46 -0
  45. package/dist/browser/esm/texture/PalettedTextures.mjs.map +1 -0
  46. package/dist/browser/esm/texture/index.mjs +1 -0
  47. package/dist/browser/esm/texture/index.mjs.map +1 -1
  48. package/dist/browser/esm/util/util.mjs +0 -2
  49. package/dist/browser/esm/util/util.mjs.map +1 -1
  50. package/dist/browser/esm/world/Chunk.mjs +62 -33
  51. package/dist/browser/esm/world/Chunk.mjs.map +1 -1
  52. package/dist/browser/esm/world/MineRenderWorld.mjs +98 -13
  53. package/dist/browser/esm/world/MineRenderWorld.mjs.map +1 -1
  54. package/dist/browser/esm/world/SectionMesh.mjs +65 -9
  55. package/dist/browser/esm/world/SectionMesh.mjs.map +1 -1
  56. package/dist/browser/esm/world/SectionModels.mjs +21 -15
  57. package/dist/browser/esm/world/SectionModels.mjs.map +1 -1
  58. package/dist/browser/esm/world/SectionWorker.mjs +8 -2
  59. package/dist/browser/esm/world/SectionWorker.mjs.map +1 -1
  60. package/dist/browser/esm/world/WorldStreamer.mjs +109 -15
  61. package/dist/browser/esm/world/WorldStreamer.mjs.map +1 -1
  62. package/dist/browser/index.d.ts +129 -13
  63. package/dist/browser/index.js +1592 -894
  64. package/dist/browser/index.js.map +1 -1
  65. package/dist/bundle.js +1330 -619
  66. package/dist/bundle.js.map +1 -1
  67. package/dist/node/index.d.mts +117 -12
  68. package/dist/node/index.d.ts +117 -12
  69. package/dist/node/index.js +1505 -793
  70. package/dist/node/index.js.map +1 -1
  71. package/dist/node/index.mjs +1303 -593
  72. package/dist/node/index.mjs.map +1 -1
  73. package/package.json +2 -1
@@ -184,8 +184,6 @@ async function sleep(timeout) {
184
184
  });
185
185
  }
186
186
  function yieldToEventLoop() {
187
- const scheduler = globalThis.scheduler;
188
- if (scheduler?.yield) return scheduler.yield();
189
187
  return new Promise((resolve) => {
190
188
  if (typeof MessageChannel !== "undefined") {
191
189
  const channel = new MessageChannel();
@@ -243,6 +241,10 @@ var init_Caching = __esm({
243
241
  import_loading_cache = require("@inventivetalent/loading-cache");
244
242
  import_time = require("@inventivetalent/time");
245
243
  Caching = class _Caching {
244
+ static {
245
+ /** Cached in place of a missing asset. The caches drop `undefined` results, which would request the asset again. */
246
+ this.MISSING = false;
247
+ }
246
248
  static {
247
249
  this._generation = 0;
248
250
  }
@@ -310,6 +312,9 @@ var init_Caching = __esm({
310
312
  static {
311
313
  this.blockEntityIndexCache = _Caching.createAssetCache();
312
314
  }
315
+ static {
316
+ this.entityDatasetVersionCache = _Caching.createAssetCache();
317
+ }
313
318
  static {
314
319
  this.unihexCache = _Caching.createAssetCache();
315
320
  }
@@ -319,6 +324,12 @@ var init_Caching = __esm({
319
324
  static {
320
325
  this.bannerPatternCache = _Caching.createAssetCache();
321
326
  }
327
+ static {
328
+ this.equipmentCache = _Caching.createAssetCache();
329
+ }
330
+ static {
331
+ this.trimCache = _Caching.createAssetCache();
332
+ }
322
333
  static {
323
334
  this.mineRenderDataCache = _Caching.createAssetCache();
324
335
  }
@@ -354,9 +365,12 @@ var init_Caching = __esm({
354
365
  this.entityTextureCache,
355
366
  this.entityAnimationCache,
356
367
  this.blockEntityIndexCache,
368
+ this.entityDatasetVersionCache,
357
369
  this.fontCache,
358
370
  this.unihexCache,
359
371
  this.bannerPatternCache,
372
+ this.equipmentCache,
373
+ this.trimCache,
360
374
  this.mineRenderDataCache,
361
375
  this.usernameUuidCache,
362
376
  this.biomeCache
@@ -2404,15 +2418,21 @@ var init_ModelTextures = __esm({
2404
2418
  key.extension += ".mcmeta";
2405
2419
  }
2406
2420
  const keyStr = key.serialize();
2407
- return Caching.textureMetaCache.get(keyStr, (k) => {
2408
- return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
2409
- return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
2410
- if (asset2)
2411
- asset2.key = key;
2412
- return asset2;
2413
- });
2421
+ const meta = await Caching.textureMetaCache.get(keyStr, (k) => {
2422
+ return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), async (k1) => {
2423
+ if (AssetLoader.persistentScope === "") {
2424
+ const path = key.getFullPath();
2425
+ const slash = path.lastIndexOf("/");
2426
+ const listKey = new AssetKey(key.namespace, `${path.slice(0, slash + 1)}_list`, "textures", void 0, "assets", ".json", key.root);
2427
+ const list2 = await Caching.listAssetCache.get(listKey.serialize(), () => AssetLoader.get(listKey, "list" /* LIST */)).catch(() => void 0);
2428
+ if (list2 && !list2.files.includes(`${path.slice(slash + 1)}${key.extension}`)) return Caching.MISSING;
2429
+ }
2430
+ const asset2 = await AssetLoader.get(key, "meta" /* META */);
2431
+ if (asset2) asset2.key = key;
2432
+ return asset2 ?? Caching.MISSING;
2414
2433
  });
2415
2434
  });
2435
+ return meta === Caching.MISSING ? void 0 : meta;
2416
2436
  }
2417
2437
  /** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
2418
2438
  static async clearCache() {
@@ -3332,23 +3352,303 @@ var init_MinecraftCubeTexture = __esm({
3332
3352
  }
3333
3353
  });
3334
3354
 
3355
+ // src/assets/_versions/DatasetVersions.ts
3356
+ var parseRelease, compare2, nearestRelease;
3357
+ var init_DatasetVersions = __esm({
3358
+ "src/assets/_versions/DatasetVersions.ts"() {
3359
+ parseRelease = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
3360
+ compare2 = (left, right) => {
3361
+ for (let i = 0; i < 3; i++) {
3362
+ const difference = (left[i] ?? 0) - (right[i] ?? 0);
3363
+ if (difference) return difference;
3364
+ }
3365
+ return 0;
3366
+ };
3367
+ nearestRelease = (requestedVersion, published) => {
3368
+ const requested = parseRelease(requestedVersion);
3369
+ if (!requested) return void 0;
3370
+ const versions = published.map((name) => ({ name, number: parseRelease(name) })).filter((entry) => !!entry.number).sort((left, right) => compare2(left.number, right.number));
3371
+ const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
3372
+ const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare2(left.number, right.number))[0] ?? versions.filter((entry) => compare2(entry.number, requested) <= 0).pop() ?? versions[0];
3373
+ return selected?.name;
3374
+ };
3375
+ }
3376
+ });
3377
+
3378
+ // src/error/MineRenderError.ts
3379
+ var MineRenderError;
3380
+ var init_MineRenderError = __esm({
3381
+ "src/error/MineRenderError.ts"() {
3382
+ MineRenderError = class _MineRenderError extends Error {
3383
+ constructor(msg) {
3384
+ super(msg);
3385
+ Object.setPrototypeOf(this, _MineRenderError.prototype);
3386
+ }
3387
+ get name() {
3388
+ return "MineRenderError";
3389
+ }
3390
+ };
3391
+ }
3392
+ });
3393
+
3394
+ // src/assets/Entities.ts
3395
+ var Entities;
3396
+ var init_Entities = __esm({
3397
+ "src/assets/Entities.ts"() {
3398
+ init_AssetKey();
3399
+ init_DatasetVersions();
3400
+ init_AssetLoader();
3401
+ init_source();
3402
+ init_Caching();
3403
+ init_AssetDefaults();
3404
+ init_MineRenderError();
3405
+ init_ModelTextures();
3406
+ Entities = class {
3407
+ /**
3408
+ * Resolves `root` (default: the active version). Uses the exact dataset when published or when the
3409
+ * version is not a release; otherwise selects the nearest published release, like {@link MineRenderData.resolve}.
3410
+ * Textures keep the requested root.
3411
+ */
3412
+ static async getDatasetRoot(root) {
3413
+ const requestRoot = (root ?? AssetLoader.ROOT).replace(/\/+$/, "");
3414
+ const requestedVersion = requestRoot.slice(requestRoot.lastIndexOf("/") + 1);
3415
+ const base = requestRoot.slice(0, requestRoot.lastIndexOf("/"));
3416
+ const selected = await Caching.entityDatasetVersionCache.get(root ?? `sources:${AssetLoader.ROOT}`, async () => {
3417
+ const key = new AssetKey("entity-models", "version", void 0, void 0, void 0, ".json", root);
3418
+ key.rootType = void 0;
3419
+ if (!parseRelease(requestedVersion) || await AssetLoader.get(key, "json" /* JSON */) !== void 0) return requestedVersion;
3420
+ const indexKey = new AssetKey("entity-models", "versions", void 0, void 0, void 0, ".json", base);
3421
+ indexKey.rootType = void 0;
3422
+ const index = await AssetLoader.get(indexKey, "json" /* JSON */);
3423
+ if (index === void 0) return requestedVersion;
3424
+ if (!index || typeof index !== "object" || Array.isArray(index) || !Array.isArray(index.versions) || !index.versions.every((entry) => entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.name === "string")) {
3425
+ throw new MineRenderError(`Invalid entity model version index at ${base}/entity-models/versions.json`);
3426
+ }
3427
+ return nearestRelease(requestedVersion, index.versions.map((entry) => entry.name)) ?? requestedVersion;
3428
+ });
3429
+ return selected === requestedVersion ? root : `${base}/${selected}`;
3430
+ }
3431
+ /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
3432
+ static async getEntityList() {
3433
+ const root = await this.getDatasetRoot();
3434
+ const collect = async (path) => {
3435
+ const prefix2 = path ? `${path}/` : "";
3436
+ const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json", root);
3437
+ const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3438
+ if (!list2) return [];
3439
+ const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3440
+ const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
3441
+ return [...files, ...children.flat()];
3442
+ };
3443
+ return collect("");
3444
+ }
3445
+ /** @internal */
3446
+ static async getModelFile(modelKey) {
3447
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3448
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", await this.getDatasetRoot(isAssetKey(modelKey) ? modelKey.root : void 0));
3449
+ return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3450
+ }
3451
+ /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
3452
+ static async getLayerList(modelKey) {
3453
+ return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
3454
+ }
3455
+ /** The dataset's extra draws on top of `main`; their `when` labels are the states `EntityModelOptions.when` can enable. */
3456
+ static async getPassList(modelKey) {
3457
+ return (await this.getModelFile(modelKey))?.passes ?? [];
3458
+ }
3459
+ /**
3460
+ * Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
3461
+ * By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
3462
+ *
3463
+ * @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
3464
+ * @param textureKey - Texture override for the first selected layer.
3465
+ * @param options - Layer selection, conditional passes, and per-layer texture overrides.
3466
+ * @returns The selected model, or `undefined` when its dataset file is missing.
3467
+ */
3468
+ static async getEntity(modelKey, textureKey, options) {
3469
+ const model = await this.getModelFile(modelKey);
3470
+ if (!model) return void 0;
3471
+ const passes = options?.layers || options?.layer !== void 0 ? [] : (model.passes ?? []).filter((pass) => pass.when === void 0 || options?.when?.includes(pass.when));
3472
+ const draws = [...new Set(options?.layers ?? [options?.layer ?? "main"])].map((layer) => ({ layer }));
3473
+ if (draws.length === 0) throw new MineRenderError(`Entity ${model.id} requires at least one layer`);
3474
+ draws.push(...passes);
3475
+ const names = [];
3476
+ const selected = draws.map((pass) => {
3477
+ const layer = model.layers[pass.layer];
3478
+ if (!layer) {
3479
+ throw new MineRenderError(`Entity ${model.id} has no layer "${pass.layer}". Available layers: ${Object.keys(model.layers).join(", ")}`);
3480
+ }
3481
+ let name = pass.layer;
3482
+ for (let n = 2; names.includes(name); n++) name = `${pass.layer}#${n}`;
3483
+ names.push(name);
3484
+ return { name, layer, pass };
3485
+ });
3486
+ const layers = Object.fromEntries(await Promise.all(selected.map(async ({ name, layer, pass }, index) => {
3487
+ const override = options?.textures?.[name] ?? (index === 0 ? textureKey : void 0);
3488
+ const textureLocation = pass.textureLocation ?? layer.textureLocation;
3489
+ let texture;
3490
+ if (override) {
3491
+ const path = isAssetKey(override) ? override.getFullPath() : override.path;
3492
+ texture = isAssetKey(override) && override.assetType === "textures" && path.startsWith("entity/") ? override : new AssetKey(override.namespace, path, "textures", "entity", "assets", ".png", isAssetKey(override) ? override.root : void 0);
3493
+ } else if (textureLocation !== void 0) {
3494
+ texture = AssetKey.parse("textures", textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
3495
+ if (isAssetKey(modelKey)) texture.root = modelKey.root;
3496
+ } else {
3497
+ texture = await this.resolveTexture(modelKey);
3498
+ }
3499
+ const render = pass.render ?? layer.render;
3500
+ return [name, { key: override ?? modelKey, texture, layer, ...render && { render }, ...pass.tint && { tint: pass.tint } }];
3501
+ })));
3502
+ return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
3503
+ }
3504
+ /**
3505
+ * Native and sampled vanilla clips by name, or undefined when the model or selected version has none.
3506
+ */
3507
+ static async getAnimations(modelKey) {
3508
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3509
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json", await this.getDatasetRoot(isAssetKey(modelKey) ? modelKey.root : void 0));
3510
+ const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3511
+ return file?.animations;
3512
+ }
3513
+ /** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
3514
+ static async resolveTexture(modelKey) {
3515
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3516
+ const root = isAssetKey(modelKey) ? modelKey.root : void 0;
3517
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", root);
3518
+ const texture = await Caching.entityTextureCache.get(key.serialize(), async () => {
3519
+ const name = path.split("/").pop();
3520
+ for (const candidate of [path, `${path}/${name}`]) {
3521
+ const texture3 = new AssetKey(modelKey.namespace, candidate, "textures", "entity", "assets", ".png", root);
3522
+ if (await ModelTextures.get(texture3)) return texture3;
3523
+ }
3524
+ const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
3525
+ const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
3526
+ const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3527
+ const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
3528
+ if (!file) return Caching.MISSING;
3529
+ const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
3530
+ const variant = await AssetLoader.get(variantKey, "json" /* JSON */);
3531
+ const assets = variant?.assets;
3532
+ const textureId = variant?.asset_id ?? (typeof assets?.wild === "string" ? assets.wild : Object.values(assets ?? {}).find((value) => typeof value === "string"));
3533
+ if (typeof textureId !== "string") return Caching.MISSING;
3534
+ const texture2 = AssetKey.parse("textures", textureId);
3535
+ texture2.root = root;
3536
+ return texture2;
3537
+ });
3538
+ return texture === Caching.MISSING ? void 0 : texture;
3539
+ }
3540
+ /** @deprecated Use getEntityList(); block entities share the entity dataset. */
3541
+ static getBlockList() {
3542
+ return this.getEntityList();
3543
+ }
3544
+ /** @deprecated Use getEntity(); block entities share the entity dataset. */
3545
+ static getBlock(modelKey, textureKey, options) {
3546
+ return this.getEntity(modelKey, textureKey, options);
3547
+ }
3548
+ };
3549
+ }
3550
+ });
3551
+
3552
+ // src/armor/Armor.ts
3553
+ var Armor_exports = {};
3554
+ __export(Armor_exports, {
3555
+ Armor: () => Armor
3556
+ });
3557
+ var ARMOR_TRANSFORM, Armor;
3558
+ var init_Armor = __esm({
3559
+ "src/armor/Armor.ts"() {
3560
+ init_AssetKey();
3561
+ init_AssetLoader();
3562
+ init_Entities();
3563
+ init_AssetParsers();
3564
+ init_Caching();
3565
+ init_MineRenderError();
3566
+ ARMOR_TRANSFORM = [
3567
+ { rotate: [0, Math.PI, 0] },
3568
+ { scale: [-1, -1, 1] },
3569
+ { translate: [0, -24, 0] }
3570
+ ];
3571
+ Armor = class {
3572
+ /** Loads 1.21.4+ armor in SkinObject's frame, or returns undefined when no layer draws. */
3573
+ static async getEntity(armor) {
3574
+ const model = await Entities.getModelFile(new BasicAssetKey("minecraft", "player"));
3575
+ const layers = {};
3576
+ const slots = { chest: "chestplate", legs: "leggings", feet: "boots", head: "helmet" };
3577
+ for (const slot of Object.keys(slots)) {
3578
+ const value = armor[slot];
3579
+ if (value === void 0) continue;
3580
+ const piece = typeof value === "string" ? { asset: value } : value;
3581
+ const name = slots[slot], geometry = model?.layers[name];
3582
+ if (!geometry) throw new MineRenderError(`Missing armor geometry ${name}; armor needs Minecraft 1.21.4 or later`);
3583
+ const id = this.identifier(piece.asset);
3584
+ const key = new AssetKey(id.namespace, id.path, "equipment", void 0, "assets", ".json");
3585
+ const equipment = await Caching.equipmentCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3586
+ if (!equipment) throw new MineRenderError(`Missing equipment asset ${piece.asset}; armor takes equipment asset IDs such as minecraft:diamond and needs Minecraft 1.21.4 or later`);
3587
+ const layerType = slot === "legs" ? "humanoid_leggings" : "humanoid";
3588
+ const equipmentLayers = equipment.layers[layerType];
3589
+ if (!equipmentLayers?.length) continue;
3590
+ let count = 0;
3591
+ const add = (texture, settings) => {
3592
+ layers[++count === 1 ? name : `${name}#${count}`] = { key: texture, texture, layer: geometry, ...settings };
3593
+ };
3594
+ let glint = piece.glint;
3595
+ for (const layer of equipmentLayers) {
3596
+ let color2;
3597
+ if (layer.dyeable) {
3598
+ const undyed = layer.dyeable.color_when_undyed;
3599
+ color2 = piece.color || (Array.isArray(undyed) ? Math.floor(undyed[0] * 255) << 16 | Math.floor(undyed[1] * 255) << 8 | Math.floor(undyed[2] * 255) : undyed === void 0 ? void 0 : undyed & 16777215);
3600
+ if (color2 === void 0) continue;
3601
+ }
3602
+ const texture = this.identifier(layer.texture);
3603
+ add(
3604
+ new AssetKey(texture.namespace, `equipment/${layerType}/${texture.path}`, "textures", "entity", "assets", ".png"),
3605
+ { ...color2 !== void 0 && { color: color2 }, ...glint && { glint: true } }
3606
+ );
3607
+ glint = false;
3608
+ }
3609
+ if (piece.trim) {
3610
+ const patternId = this.identifier(piece.trim.pattern), materialId = this.identifier(piece.trim.material);
3611
+ const patternKey = new AssetKey(patternId.namespace, patternId.path, "trim_pattern", void 0, "data", ".json");
3612
+ const materialKey = new AssetKey(materialId.namespace, materialId.path, "trim_material", void 0, "data", ".json");
3613
+ const pattern2 = await Caching.trimCache.get(patternKey.serialize(), () => AssetLoader.get(patternKey, "json" /* JSON */));
3614
+ if (!pattern2) throw new MineRenderError(`Missing trim pattern ${piece.trim.pattern}`);
3615
+ const material = await Caching.trimCache.get(materialKey.serialize(), () => AssetLoader.get(materialKey, "json" /* JSON */));
3616
+ if (!material) throw new MineRenderError(`Missing trim material ${piece.trim.material}`);
3617
+ const patternAsset = this.identifier(pattern2.asset_id);
3618
+ const suffix = material.override_armor_assets?.[id.toNamespacedString()] ?? material.asset_name;
3619
+ add(new AssetKey(patternAsset.namespace, `entity/${layerType}/${patternAsset.path}`, "textures", "trims", "assets", ".png"), {
3620
+ palette: {
3621
+ key: new AssetKey("minecraft", "color_palettes/trim_palette", "textures", "trims", "assets", ".png"),
3622
+ permutation: new AssetKey("minecraft", `color_palettes/${suffix}`, "textures", "trims", "assets", ".png")
3623
+ },
3624
+ render: pattern2.decal ? "cutout_z_offset" : "cutout"
3625
+ });
3626
+ }
3627
+ }
3628
+ const first = Object.values(layers)[0];
3629
+ return first ? { ...first, id: "minecraft:player", layers, transform: ARMOR_TRANSFORM } : void 0;
3630
+ }
3631
+ static identifier(value) {
3632
+ if (!isResourceLocation(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
3633
+ throw new MineRenderError(`Invalid armor identifier ${value}`);
3634
+ }
3635
+ const [namespace, path] = value.includes(":") ? value.split(":") : ["minecraft", value];
3636
+ return new BasicAssetKey(namespace, path);
3637
+ }
3638
+ };
3639
+ }
3640
+ });
3641
+
3335
3642
  // src/assets/MineRenderData.ts
3336
- var object2, release, compare2, MineRenderData;
3643
+ var object2, MineRenderData;
3337
3644
  var init_MineRenderData = __esm({
3338
3645
  "src/assets/MineRenderData.ts"() {
3646
+ init_DatasetVersions();
3339
3647
  init_AssetKey();
3340
3648
  init_AssetLoader();
3341
3649
  init_AssetParsers();
3342
3650
  init_Caching();
3343
3651
  object2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
3344
- release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
3345
- compare2 = (left, right) => {
3346
- for (let i = 0; i < 3; i++) {
3347
- const difference = (left[i] ?? 0) - (right[i] ?? 0);
3348
- if (difference) return difference;
3349
- }
3350
- return 0;
3351
- };
3352
3652
  MineRenderData = class {
3353
3653
  /** Loads one registry. Missing files and invalid data reject the request. */
3354
3654
  static async get(dataset, root) {
@@ -3378,9 +3678,9 @@ var init_MineRenderData = __esm({
3378
3678
  const scope = AssetLoader.persistentScope;
3379
3679
  return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
3380
3680
  const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
3381
- let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
3681
+ let manifest = await this.manifest(root, requestRoot !== void 0 && parseRelease(requestedVersion) ? requestedVersion : void 0, requestRoot);
3382
3682
  if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
3383
- const requested = release(requestedVersion);
3683
+ const requested = parseRelease(requestedVersion);
3384
3684
  if (!requested) {
3385
3685
  throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
3386
3686
  }
@@ -3392,14 +3692,12 @@ var init_MineRenderData = __esm({
3392
3692
  }
3393
3693
  return value.versions;
3394
3694
  });
3395
- const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare2(left.number, right.number));
3396
- const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
3397
- const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare2(left.number, right.number))[0] ?? versions.filter((entry) => compare2(entry.number, requested) <= 0).pop() ?? versions[0];
3695
+ const selected = nearestRelease(requestedVersion, index.map((entry) => entry.name));
3398
3696
  if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
3399
- const resolvedRoot = `${base}/${selected.name}`;
3400
- manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
3401
- if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
3402
- return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
3697
+ const resolvedRoot = `${base}/${selected}`;
3698
+ manifest = await this.manifest(resolvedRoot, selected, resolvedRoot);
3699
+ if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected}`);
3700
+ return { requestedVersion, version: selected, root: resolvedRoot, manifest };
3403
3701
  });
3404
3702
  }
3405
3703
  static async manifest(root, expectedVersion, requestRoot) {
@@ -3731,22 +4029,6 @@ var init_ItemDefaults = __esm({
3731
4029
  }
3732
4030
  });
3733
4031
 
3734
- // src/error/MineRenderError.ts
3735
- var MineRenderError;
3736
- var init_MineRenderError = __esm({
3737
- "src/error/MineRenderError.ts"() {
3738
- MineRenderError = class _MineRenderError extends Error {
3739
- constructor(msg) {
3740
- super(msg);
3741
- Object.setPrototypeOf(this, _MineRenderError.prototype);
3742
- }
3743
- get name() {
3744
- return "MineRenderError";
3745
- }
3746
- };
3747
- }
3748
- });
3749
-
3750
4032
  // src/texture/Colormaps.ts
3751
4033
  var Colormaps;
3752
4034
  var init_Colormaps = __esm({
@@ -4527,6 +4809,7 @@ var init_BlockEntities = __esm({
4527
4809
  "src/assets/BlockEntities.ts"() {
4528
4810
  import_three9 = require("three");
4529
4811
  init_AssetKey();
4812
+ init_Entities();
4530
4813
  init_AssetLoader();
4531
4814
  init_source();
4532
4815
  init_Caching();
@@ -4543,6 +4826,7 @@ var init_BlockEntities = __esm({
4543
4826
  key.rootType = void 0;
4544
4827
  const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4545
4828
  try {
4829
+ key.root = await Entities.getDatasetRoot(root);
4546
4830
  return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4547
4831
  } catch (error) {
4548
4832
  log9.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
@@ -4628,136 +4912,6 @@ var init_DecoratedPots = __esm({
4628
4912
  }
4629
4913
  });
4630
4914
 
4631
- // src/assets/Entities.ts
4632
- var Entities;
4633
- var init_Entities = __esm({
4634
- "src/assets/Entities.ts"() {
4635
- init_AssetKey();
4636
- init_AssetLoader();
4637
- init_source();
4638
- init_Caching();
4639
- init_AssetDefaults();
4640
- init_MineRenderError();
4641
- init_ModelTextures();
4642
- Entities = class {
4643
- /** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
4644
- static async getEntityList() {
4645
- const collect = async (path) => {
4646
- const prefix2 = path ? `${path}/` : "";
4647
- const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
4648
- const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
4649
- if (!list2) return [];
4650
- const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
4651
- const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
4652
- return [...files, ...children.flat()];
4653
- };
4654
- return collect("");
4655
- }
4656
- static async getModelFile(modelKey) {
4657
- const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4658
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
4659
- return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4660
- }
4661
- /** Returns selectable geometry-layer names, or an empty list when the model is missing. */
4662
- static async getLayerList(modelKey) {
4663
- return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
4664
- }
4665
- /** The dataset's extra draws on top of `main`; their `when` labels are the states `EntityModelOptions.when` can enable. */
4666
- static async getPassList(modelKey) {
4667
- return (await this.getModelFile(modelKey))?.passes ?? [];
4668
- }
4669
- /**
4670
- * Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
4671
- * By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
4672
- *
4673
- * @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
4674
- * @param textureKey - Texture override for the first selected layer.
4675
- * @param options - Layer selection, conditional passes, and per-layer texture overrides.
4676
- * @returns The selected model, or `undefined` when its dataset file is missing.
4677
- */
4678
- static async getEntity(modelKey, textureKey, options) {
4679
- const model = await this.getModelFile(modelKey);
4680
- if (!model) return void 0;
4681
- const passes = options?.layers || options?.layer !== void 0 ? [] : (model.passes ?? []).filter((pass) => pass.when === void 0 || options?.when?.includes(pass.when));
4682
- const draws = [...new Set(options?.layers ?? [options?.layer ?? "main"])].map((layer) => ({ layer }));
4683
- if (draws.length === 0) throw new MineRenderError(`Entity ${model.id} requires at least one layer`);
4684
- draws.push(...passes);
4685
- const names = [];
4686
- const selected = draws.map((pass) => {
4687
- const layer = model.layers[pass.layer];
4688
- if (!layer) {
4689
- throw new MineRenderError(`Entity ${model.id} has no layer "${pass.layer}". Available layers: ${Object.keys(model.layers).join(", ")}`);
4690
- }
4691
- let name = pass.layer;
4692
- for (let n = 2; names.includes(name); n++) name = `${pass.layer}#${n}`;
4693
- names.push(name);
4694
- return { name, layer, pass };
4695
- });
4696
- const layers = Object.fromEntries(await Promise.all(selected.map(async ({ name, layer, pass }, index) => {
4697
- const override = options?.textures?.[name] ?? (index === 0 ? textureKey : void 0);
4698
- const textureLocation = pass.textureLocation ?? layer.textureLocation;
4699
- let texture;
4700
- if (override) {
4701
- const path = isAssetKey(override) ? override.getFullPath() : override.path;
4702
- texture = isAssetKey(override) && override.assetType === "textures" && path.startsWith("entity/") ? override : new AssetKey(override.namespace, path, "textures", "entity", "assets", ".png", isAssetKey(override) ? override.root : void 0);
4703
- } else if (textureLocation !== void 0) {
4704
- texture = AssetKey.parse("textures", textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
4705
- if (isAssetKey(modelKey)) texture.root = modelKey.root;
4706
- } else {
4707
- texture = await this.resolveTexture(modelKey);
4708
- }
4709
- const render = pass.render ?? layer.render;
4710
- return [name, { key: override ?? modelKey, texture, layer, ...render && { render }, ...pass.tint && { tint: pass.tint } }];
4711
- })));
4712
- return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
4713
- }
4714
- /**
4715
- * Native and sampled vanilla clips by name, or undefined when the model or selected version has none.
4716
- */
4717
- static async getAnimations(modelKey) {
4718
- const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4719
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
4720
- const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4721
- return file?.animations;
4722
- }
4723
- /** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
4724
- static async resolveTexture(modelKey) {
4725
- const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4726
- const root = isAssetKey(modelKey) ? modelKey.root : void 0;
4727
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", root);
4728
- return Caching.entityTextureCache.get(key.serialize(), async () => {
4729
- const name = path.split("/").pop();
4730
- for (const candidate of [path, `${path}/${name}`]) {
4731
- const texture2 = new AssetKey(modelKey.namespace, candidate, "textures", "entity", "assets", ".png", root);
4732
- if (await ModelTextures.get(texture2)) return texture2;
4733
- }
4734
- const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
4735
- const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
4736
- const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
4737
- const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
4738
- if (!file) return void 0;
4739
- const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
4740
- const variant = await AssetLoader.get(variantKey, "json" /* JSON */);
4741
- const assets = variant?.assets;
4742
- const textureId = variant?.asset_id ?? (typeof assets?.wild === "string" ? assets.wild : Object.values(assets ?? {}).find((value) => typeof value === "string"));
4743
- if (typeof textureId !== "string") return void 0;
4744
- const texture = AssetKey.parse("textures", textureId);
4745
- texture.root = root;
4746
- return texture;
4747
- });
4748
- }
4749
- /** @deprecated Use getEntityList(); block entities share the entity dataset. */
4750
- static getBlockList() {
4751
- return this.getEntityList();
4752
- }
4753
- /** @deprecated Use getEntity(); block entities share the entity dataset. */
4754
- static getBlock(modelKey, textureKey, options) {
4755
- return this.getEntity(modelKey, textureKey, options);
4756
- }
4757
- };
4758
- }
4759
- });
4760
-
4761
4915
  // src/assets/_fonts/Unihex.ts
4762
4916
  var Unihex_exports = {};
4763
4917
  __export(Unihex_exports, {
@@ -5898,6 +6052,266 @@ var init_model = __esm({
5898
6052
  }
5899
6053
  });
5900
6054
 
6055
+ // src/model/ItemGlint.ts
6056
+ var ItemGlint_exports = {};
6057
+ __export(ItemGlint_exports, {
6058
+ ItemGlint: () => ItemGlint
6059
+ });
6060
+ var import_three14, ItemGlint;
6061
+ var init_ItemGlint = __esm({
6062
+ "src/model/ItemGlint.ts"() {
6063
+ import_three14 = require("three");
6064
+ init_AssetKey();
6065
+ init_Models();
6066
+ init_ModelTextures();
6067
+ init_Caching();
6068
+ init_Textures();
6069
+ init_Ticker();
6070
+ init_CubeFace();
6071
+ init_DisplayPosition();
6072
+ ItemGlint = class _ItemGlint {
6073
+ constructor(owner, material) {
6074
+ this.owner = owner;
6075
+ this.material = material;
6076
+ this.updateTime();
6077
+ }
6078
+ /** Enables glint for enchantments or a lodestone compass unless a boolean component overrides them. */
6079
+ static enabled(components = {}, itemId) {
6080
+ const override = Models.componentValue(components, "enchantment_glint_override");
6081
+ if (override !== void 0) {
6082
+ if (typeof override !== "boolean") throw new Error("Item enchantment_glint_override must be a boolean");
6083
+ return override;
6084
+ }
6085
+ if (itemId === "minecraft:compass") {
6086
+ Models.componentValue(components, "lodestone_tracker");
6087
+ if (Object.prototype.hasOwnProperty.call(components, "lodestone_tracker") || Object.prototype.hasOwnProperty.call(components, "minecraft:lodestone_tracker")) return true;
6088
+ }
6089
+ const enchantments = Models.componentValue(components, "enchantments");
6090
+ if (enchantments === void 0) return false;
6091
+ if (!enchantments || typeof enchantments !== "object" || Array.isArray(enchantments)) throw new Error("Item enchantments must be an object");
6092
+ return Object.keys(enchantments).length > 0;
6093
+ }
6094
+ /** @internal */
6095
+ static async create(owner, baseMap, root, entityMeshes, kind = "item") {
6096
+ const key = new AssetKey("minecraft", `enchanted_glint_${kind}`, "textures", "misc", "assets", ".png", root);
6097
+ const assetKey = key.serialize(), textureKey = `item-glint:${assetKey}`;
6098
+ let texture = Caching.textureCache.getIfPresent(textureKey);
6099
+ if (!texture) {
6100
+ const pending = ModelTextures.get(key);
6101
+ const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKey);
6102
+ const [image, metadata] = await Promise.all([pending, ModelTextures.getMeta(key)]);
6103
+ if (!image) throw new Error(`Missing ${kind} glint texture ${key.toNamespacedString()}`);
6104
+ if (cachedAsset && Caching.textureAssetCache.getIfPresent(assetKey) !== cachedAsset) return this.create(owner, baseMap, root, entityMeshes, kind);
6105
+ texture = Caching.textureCache.get(textureKey, () => {
6106
+ const texture2 = Textures.createCanvasTexture(image.data.canvas);
6107
+ texture2.wrapS = texture2.wrapT = metadata?.texture?.clamp ? import_three14.ClampToEdgeWrapping : import_three14.RepeatWrapping;
6108
+ if (metadata?.texture?.blur) texture2.magFilter = texture2.minFilter = import_three14.LinearFilter;
6109
+ return texture2;
6110
+ });
6111
+ }
6112
+ const material = new import_three14.ShaderMaterial({
6113
+ name: "item-glint",
6114
+ transparent: true,
6115
+ depthWrite: false,
6116
+ depthFunc: import_three14.EqualDepth,
6117
+ side: import_three14.DoubleSide,
6118
+ forceSinglePass: true,
6119
+ blending: import_three14.CustomBlending,
6120
+ blendSrc: import_three14.SrcColorFactor,
6121
+ blendDst: import_three14.OneFactor,
6122
+ blendSrcAlpha: import_three14.ZeroFactor,
6123
+ blendDstAlpha: import_three14.OneFactor,
6124
+ toneMapped: false,
6125
+ // TextureTransform.ARMOR_ENTITY_GLINT_TEXTURING uses a smaller UV scale than item glint.
6126
+ defines: entityMeshes ? { ENTITY_GLINT: true, ENTITY_GLINT_SCALE: kind === "armor" ? "0.16" : "0.5" } : {},
6127
+ uniforms: {
6128
+ ...!entityMeshes && { baseMap: { value: baseMap } },
6129
+ glintMap: { value: texture },
6130
+ glintOffset: { value: new import_three14.Vector2() },
6131
+ glintAlpha: { value: 0.75 }
6132
+ },
6133
+ vertexShader: `
6134
+ uniform vec2 glintOffset;
6135
+ #ifndef ENTITY_GLINT
6136
+ attribute vec4 uvBounds;
6137
+ attribute vec2 glintUv;
6138
+ flat out vec4 vUvBounds;
6139
+ centroid out vec2 vUv;
6140
+ #endif
6141
+ varying vec2 vGlintUv;
6142
+ void main() {
6143
+ #ifdef ENTITY_GLINT
6144
+ vec2 p = vec2(uv.x, 1.0 - uv.y) * ENTITY_GLINT_SCALE;
6145
+ #else
6146
+ vUv = uv;
6147
+ vUvBounds = uvBounds;
6148
+ vec2 p = glintUv * 8.0;
6149
+ #endif
6150
+ float angle = radians(10.0);
6151
+ vGlintUv = vec2(cos(angle) * p.x - sin(angle) * p.y, sin(angle) * p.x + cos(angle) * p.y) + glintOffset;
6152
+ vec4 mvPosition = vec4(position, 1.0);
6153
+ mvPosition = modelViewMatrix * mvPosition;
6154
+ gl_Position = projectionMatrix * mvPosition;
6155
+ }
6156
+ `,
6157
+ fragmentShader: `
6158
+ #ifndef ENTITY_GLINT
6159
+ uniform sampler2D baseMap;
6160
+ flat in vec4 vUvBounds;
6161
+ centroid in vec2 vUv;
6162
+ #endif
6163
+ uniform sampler2D glintMap;
6164
+ uniform float glintAlpha;
6165
+ varying vec2 vGlintUv;
6166
+ void main() {
6167
+ #ifndef ENTITY_GLINT
6168
+ vec2 mapUv = clamp(vUv, vUvBounds.xy, vUvBounds.zw);
6169
+ if (texture2D(baseMap, mapUv).a < 0.01) discard;
6170
+ #endif
6171
+ vec4 color = texture2D(glintMap, vec2(vGlintUv.x, 1.0 - vGlintUv.y));
6172
+ if (color.a < 0.1) discard;
6173
+ gl_FragColor = color;
6174
+ #include <colorspace_fragment>
6175
+ gl_FragColor.rgb *= glintAlpha;
6176
+ }
6177
+ `
6178
+ });
6179
+ if (!entityMeshes) Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
6180
+ const targets = entityMeshes ? [...entityMeshes] : [];
6181
+ if (!entityMeshes) owner.iterateAllMeshes((mesh) => targets.push({ mesh }));
6182
+ for (const { mesh: base, parent = base } of targets) {
6183
+ const pass = new import_three14.Mesh(base.geometry, material);
6184
+ pass.name = `${base.name}:glint`;
6185
+ pass.userData.minerenderItemGlint = true;
6186
+ pass.raycast = () => {
6187
+ };
6188
+ pass.renderOrder = parent.renderOrder + 0.5;
6189
+ parent.add(pass);
6190
+ }
6191
+ return new _ItemGlint(owner, material);
6192
+ }
6193
+ /**
6194
+ * Maps glint before model centering and display transforms. Ordinary sprites approximate Minecraft's shared atlas density.
6195
+ * @internal
6196
+ */
6197
+ static mapUvs(geometry, faces, atlas, itemId, display) {
6198
+ const uv = geometry.getAttribute("uv");
6199
+ const mapped = new Float32Array(uv.count * 2);
6200
+ if (itemId === "minecraft:compass" || itemId === "minecraft:recovery_compass" || itemId === "minecraft:clock") {
6201
+ const positions = geometry.getAttribute("position"), normals = geometry.getAttribute("normal");
6202
+ const scale = display === "gui" /* GUI */ ? 0.5 : display === "firstperson_lefthand" /* FIRSTPERSON_LEFTHAND */ || display === "firstperson_righthand" /* FIRSTPERSON_RIGHTHAND */ ? 0.75 : 1;
6203
+ const factor = 1 / (16 * 128 * scale);
6204
+ for (let index = 0; index < positions.count; index++) {
6205
+ const x = positions.getX(index), y = positions.getY(index), z = positions.getZ(index);
6206
+ const nx = normals.getX(index), ny = normals.getY(index), nz = normals.getZ(index);
6207
+ const ax = Math.abs(nx), ay = Math.abs(ny), az = Math.abs(nz);
6208
+ let u, v;
6209
+ if (ay >= az && ay >= ax) {
6210
+ u = x;
6211
+ v = ny < 0 ? -z : z;
6212
+ } else if (az >= ax) {
6213
+ u = nz < 0 ? -x : x;
6214
+ v = -y;
6215
+ } else {
6216
+ u = nx < 0 ? -z : z;
6217
+ v = -y;
6218
+ }
6219
+ mapped[index * 2] = u * factor;
6220
+ mapped[index * 2 + 1] = v * factor;
6221
+ }
6222
+ geometry.setAttribute("glintUv", new import_three14.Float32BufferAttribute(mapped, 2));
6223
+ return;
6224
+ }
6225
+ for (const [faceIndex, face] of CUBE_FACES.entries()) {
6226
+ const texture = faces[face]?.texture?.substring(1);
6227
+ const position = texture && atlas.positions[texture] || [0, 0];
6228
+ const size = texture && atlas.sizes[texture] || [atlas.image.width, atlas.image.height];
6229
+ for (let corner = 0; corner < 4; corner++) {
6230
+ const index = faceIndex * 4 + corner;
6231
+ mapped[index * 2] = (uv.getX(index) * atlas.image.width - position[0]) / size[0] / 32;
6232
+ mapped[index * 2 + 1] = ((1 - uv.getY(index)) * atlas.image.height - position[1]) / size[1] / 32;
6233
+ }
6234
+ }
6235
+ geometry.setAttribute("glintUv", new import_three14.Float32BufferAttribute(mapped, 2));
6236
+ }
6237
+ updateTime() {
6238
+ const time = Math.trunc(Date.now() * 4);
6239
+ this.material.uniforms.glintOffset.value.set(-(time % 11e4) / 11e4, time % 3e4 / 3e4);
6240
+ }
6241
+ /** @internal */
6242
+ updateSubscription(active) {
6243
+ if (active && this.ticker === void 0) {
6244
+ this.updateTime();
6245
+ this.ticker = Ticker.add(() => {
6246
+ this.updateTime();
6247
+ this.owner.notifyDirty();
6248
+ });
6249
+ } else if (!active) {
6250
+ Ticker.remove(this.ticker);
6251
+ this.ticker = void 0;
6252
+ }
6253
+ }
6254
+ dispose() {
6255
+ Ticker.remove(this.ticker);
6256
+ this.ticker = void 0;
6257
+ this.material.dispose();
6258
+ }
6259
+ };
6260
+ }
6261
+ });
6262
+
6263
+ // src/texture/PalettedTextures.ts
6264
+ var PalettedTextures_exports = {};
6265
+ __export(PalettedTextures_exports, {
6266
+ PalettedTextures: () => PalettedTextures
6267
+ });
6268
+ var PalettedTextures;
6269
+ var init_PalettedTextures = __esm({
6270
+ "src/texture/PalettedTextures.ts"() {
6271
+ init_ModelTextures();
6272
+ init_Caching();
6273
+ init_CanvasImage();
6274
+ init_MineRenderError();
6275
+ PalettedTextures = class {
6276
+ /** Loads a texture with a palette permutation, sharing the resulting canvas between callers. */
6277
+ static async get(texture, key, permutation2) {
6278
+ return Caching.canvasImageDataCache.get(`palette:${texture.serialize()}:${key.serialize()}:${permutation2.serialize()}`, async () => {
6279
+ const source = await ModelTextures.get(texture);
6280
+ if (!source) return void 0;
6281
+ const [palette, replacement] = await Promise.all([ModelTextures.get(key), ModelTextures.get(permutation2)]);
6282
+ if (!palette) throw new MineRenderError(`Missing palette ${key.toNamespacedString()}`);
6283
+ if (!replacement) throw new MineRenderError(`Missing palette ${permutation2.toNamespacedString()}`);
6284
+ const pixels = source.data.getImageData(0, 0, source.width, source.height);
6285
+ const colors2 = palette.data.getImageData(0, 0, palette.width, palette.height).data;
6286
+ const replacements = replacement.data.getImageData(0, 0, replacement.width, replacement.height).data;
6287
+ const image = new CanvasImage(source.width, source.height);
6288
+ const output = image.context.createImageData(source.width, source.height);
6289
+ output.data.set(this.permute(pixels.data, colors2, replacements));
6290
+ image.putData(output);
6291
+ return { data: image.context, width: image.width, height: image.height };
6292
+ });
6293
+ }
6294
+ /** @internal */
6295
+ static permute(pixels, key, permutation2) {
6296
+ if (key.length !== permutation2.length) throw new MineRenderError("Palette images must have the same pixel count");
6297
+ const mapping = /* @__PURE__ */ new Map();
6298
+ for (let i = 0; i < key.length; i += 4) {
6299
+ if (key[i + 3] !== 0) mapping.set(key[i] << 16 | key[i + 1] << 8 | key[i + 2], i);
6300
+ }
6301
+ const output = new Uint8ClampedArray(pixels);
6302
+ for (let i = 0; i < pixels.length; i += 4) {
6303
+ if (pixels[i + 3] === 0) continue;
6304
+ const mapped = mapping.get(pixels[i] << 16 | pixels[i + 1] << 8 | pixels[i + 2]);
6305
+ if (mapped === void 0) continue;
6306
+ output.set(permutation2.subarray(mapped, mapped + 3), i);
6307
+ output[i + 3] = Math.floor(pixels[i + 3] * permutation2[mapped + 3] / 255);
6308
+ }
6309
+ return output;
6310
+ }
6311
+ };
6312
+ }
6313
+ });
6314
+
5901
6315
  // src/entity/scene/EntityObject.ts
5902
6316
  var EntityObject_exports = {};
5903
6317
  __export(EntityObject_exports, {
@@ -5907,13 +6321,13 @@ __export(EntityObject_exports, {
5907
6321
  function isEntityObject(obj) {
5908
6322
  return obj.isEntityObject;
5909
6323
  }
5910
- var import_three14, EntityObject;
6324
+ var import_three15, ItemGlintClass, PalettedTexturesClass, EntityObject;
5911
6325
  var init_EntityObject = __esm({
5912
6326
  "src/entity/scene/EntityObject.ts"() {
5913
6327
  init_SceneObject();
5914
6328
  init_Caching();
5915
6329
  init_merge();
5916
- import_three14 = require("three");
6330
+ import_three15 = require("three");
5917
6331
  init_Ticker();
5918
6332
  init_model();
5919
6333
  init_ModelTextures();
@@ -5938,8 +6352,14 @@ var init_EntityObject = __esm({
5938
6352
  this.animationPlayers = [];
5939
6353
  this.partPoses = /* @__PURE__ */ new Map();
5940
6354
  this.options = (0, import_ts_deepmerge.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
5941
- this.addEventListener("added", () => this.updateScrollSubscription());
5942
- this.addEventListener("removed", () => this.updateScrollSubscription());
6355
+ this.addEventListener("added", () => {
6356
+ this.updateScrollSubscription();
6357
+ this.glint?.updateSubscription(!!this.parent);
6358
+ });
6359
+ this.addEventListener("removed", () => {
6360
+ this.updateScrollSubscription();
6361
+ this.glint?.updateSubscription(!!this.parent);
6362
+ });
5943
6363
  }
5944
6364
  static {
5945
6365
  this.DEFAULT_OPTIONS = (0, import_ts_deepmerge.default)({}, SceneObject.DEFAULT_OPTIONS);
@@ -5953,12 +6373,26 @@ var init_EntityObject = __esm({
5953
6373
  mesh.material = material;
5954
6374
  });
5955
6375
  this.notifyDirty();
5956
- } else await this.applyTextures();
6376
+ } else {
6377
+ await this.applyTextures();
6378
+ const layers = Object.entries(this.entityLayers).filter(([, layer]) => layer.glint);
6379
+ const meshes = [];
6380
+ for (const [name] of layers) this.getLayerGroup(name)?.traverse((object4) => {
6381
+ if (isMesh(object4)) meshes.push(object4);
6382
+ });
6383
+ if (meshes.length) {
6384
+ const ItemGlint2 = ItemGlintClass ??= (await Promise.resolve().then(() => (init_ItemGlint(), ItemGlint_exports))).ItemGlint;
6385
+ this.glint = await ItemGlint2.create(this, void 0, this.getTextureKey(layers[0][1]).root, meshes.map((mesh) => ({ mesh })), "armor");
6386
+ this.glint.updateSubscription(!!this.parent);
6387
+ }
6388
+ }
5957
6389
  }
5958
6390
  dispose() {
5959
6391
  super.dispose();
5960
6392
  }
5961
6393
  disposeAndRemoveAllChildren() {
6394
+ this.glint?.dispose();
6395
+ this.glint = void 0;
5962
6396
  this.clearScrollMaterials();
5963
6397
  for (const geometry of this.geometries) geometry.dispose();
5964
6398
  this.geometries.clear();
@@ -6117,17 +6551,21 @@ var init_EntityObject = __esm({
6117
6551
  );
6118
6552
  }
6119
6553
  async loadTextures(layer = this.entity) {
6554
+ if (layer.palette) {
6555
+ const PalettedTextures2 = PalettedTexturesClass ??= (await Promise.resolve().then(() => (init_PalettedTextures(), PalettedTextures_exports))).PalettedTextures;
6556
+ return PalettedTextures2.get(this.getTextureKey(layer), layer.palette.key, layer.palette.permutation);
6557
+ }
6120
6558
  return ModelTextures.get(this.getTextureKey(layer));
6121
6559
  }
6122
6560
  createMeshes(force = false) {
6123
6561
  if (this.meshesCreated && !force) return;
6124
- const modelRoot = new import_three14.Object3D();
6562
+ const modelRoot = new import_three15.Object3D();
6125
6563
  let transformNode = modelRoot;
6126
6564
  const transform = this.options.flip === void 0 ? this.entity.transform : void 0;
6127
6565
  if (transform) {
6128
6566
  for (const [index, op] of transform.entries()) {
6129
6567
  if (index > 0) {
6130
- const child = new import_three14.Object3D();
6568
+ const child = new import_three15.Object3D();
6131
6569
  transformNode.add(child);
6132
6570
  transformNode = child;
6133
6571
  }
@@ -6189,10 +6627,10 @@ var init_EntityObject = __esm({
6189
6627
  this.clearScrollMaterials();
6190
6628
  const results = await Promise.allSettled(Object.entries(this.entityLayers).map(async ([name, layer]) => {
6191
6629
  const mode = layer.render ?? layer.layer.render ?? "cutout";
6192
- const tint = layer.tint === void 0 ? void 0 : this.options.tints?.[layer.tint];
6630
+ const tint = (layer.tint === void 0 ? void 0 : this.options.tints?.[layer.tint]) ?? layer.color;
6193
6631
  const scroll = Materials.entityModeScroll(mode);
6194
6632
  const assetKeyStr = this.getTextureKey(layer).serialize();
6195
- const keyStr = `entity:${mode}:${tint === void 0 ? "" : new import_three14.Color(tint).getHexString()}:${assetKeyStr}`;
6633
+ const keyStr = `entity:${mode}:${tint === void 0 ? "" : new import_three15.Color(tint).getHexString()}:${assetKeyStr}` + (layer.palette ? `:${layer.palette.key.serialize()}:${layer.palette.permutation.serialize()}` : "");
6196
6634
  let mat = scroll ? void 0 : Caching.materialCache.getIfPresent(keyStr);
6197
6635
  if (!mat) {
6198
6636
  const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);
@@ -6255,7 +6693,7 @@ function getMinMax(attribute, start, count) {
6255
6693
  else if (a === 2) value = attribute.getZ(i);
6256
6694
  else if (a === 3) value = attribute.getW(i);
6257
6695
  if (attribute.normalized === true) {
6258
- value = import_three18.MathUtils.normalize(value, attribute.array);
6696
+ value = import_three19.MathUtils.normalize(value, attribute.array);
6259
6697
  }
6260
6698
  }
6261
6699
  output.min[a] = Math.min(output.min[a], value);
@@ -6303,10 +6741,10 @@ function getToBlobPromise(canvas, mimeType) {
6303
6741
  return new Promise((resolve) => canvas.toBlob(resolve, mimeType));
6304
6742
  }
6305
6743
  }
6306
- var import_three18, KHR_mesh_quantization_ExtraAttrTypes, GLTFExporter, WEBGL_CONSTANTS, KHR_MESH_QUANTIZATION, THREE_TO_WEBGL, PATH_PROPERTIES, DEFAULT_SPECULAR_COLOR, GLB_HEADER_BYTES, GLB_HEADER_MAGIC, GLB_VERSION, GLB_CHUNK_PREFIX_BYTES, GLB_CHUNK_TYPE_JSON, GLB_CHUNK_TYPE_BIN, GLTFWriter, GLTFLightExtension, GLTFMaterialsUnlitExtension, GLTFMaterialsClearcoatExtension, GLTFMaterialsDispersionExtension, GLTFMaterialsIridescenceExtension, GLTFMaterialsTransmissionExtension, GLTFMaterialsVolumeExtension, GLTFMaterialsIorExtension, GLTFMaterialsSpecularExtension, GLTFMaterialsSheenExtension, GLTFMaterialsAnisotropyExtension, GLTFMaterialsEmissiveStrengthExtension, GLTFMaterialsBumpExtension, GLTFMeshGpuInstancing;
6744
+ var import_three19, KHR_mesh_quantization_ExtraAttrTypes, GLTFExporter, WEBGL_CONSTANTS, KHR_MESH_QUANTIZATION, THREE_TO_WEBGL, PATH_PROPERTIES, DEFAULT_SPECULAR_COLOR, GLB_HEADER_BYTES, GLB_HEADER_MAGIC, GLB_VERSION, GLB_CHUNK_PREFIX_BYTES, GLB_CHUNK_TYPE_JSON, GLB_CHUNK_TYPE_BIN, GLTFWriter, GLTFLightExtension, GLTFMaterialsUnlitExtension, GLTFMaterialsClearcoatExtension, GLTFMaterialsDispersionExtension, GLTFMaterialsIridescenceExtension, GLTFMaterialsTransmissionExtension, GLTFMaterialsVolumeExtension, GLTFMaterialsIorExtension, GLTFMaterialsSpecularExtension, GLTFMaterialsSheenExtension, GLTFMaterialsAnisotropyExtension, GLTFMaterialsEmissiveStrengthExtension, GLTFMaterialsBumpExtension, GLTFMeshGpuInstancing;
6307
6745
  var init_GLTFExporter = __esm({
6308
6746
  "../../node_modules/three/examples/jsm/exporters/GLTFExporter.js"() {
6309
- import_three18 = require("three");
6747
+ import_three19 = require("three");
6310
6748
  KHR_mesh_quantization_ExtraAttrTypes = {
6311
6749
  POSITION: [
6312
6750
  "byte",
@@ -6485,22 +6923,22 @@ var init_GLTFExporter = __esm({
6485
6923
  };
6486
6924
  KHR_MESH_QUANTIZATION = "KHR_mesh_quantization";
6487
6925
  THREE_TO_WEBGL = {};
6488
- THREE_TO_WEBGL[import_three18.NearestFilter] = WEBGL_CONSTANTS.NEAREST;
6489
- THREE_TO_WEBGL[import_three18.NearestMipmapNearestFilter] = WEBGL_CONSTANTS.NEAREST_MIPMAP_NEAREST;
6490
- THREE_TO_WEBGL[import_three18.NearestMipmapLinearFilter] = WEBGL_CONSTANTS.NEAREST_MIPMAP_LINEAR;
6491
- THREE_TO_WEBGL[import_three18.LinearFilter] = WEBGL_CONSTANTS.LINEAR;
6492
- THREE_TO_WEBGL[import_three18.LinearMipmapNearestFilter] = WEBGL_CONSTANTS.LINEAR_MIPMAP_NEAREST;
6493
- THREE_TO_WEBGL[import_three18.LinearMipmapLinearFilter] = WEBGL_CONSTANTS.LINEAR_MIPMAP_LINEAR;
6494
- THREE_TO_WEBGL[import_three18.ClampToEdgeWrapping] = WEBGL_CONSTANTS.CLAMP_TO_EDGE;
6495
- THREE_TO_WEBGL[import_three18.RepeatWrapping] = WEBGL_CONSTANTS.REPEAT;
6496
- THREE_TO_WEBGL[import_three18.MirroredRepeatWrapping] = WEBGL_CONSTANTS.MIRRORED_REPEAT;
6926
+ THREE_TO_WEBGL[import_three19.NearestFilter] = WEBGL_CONSTANTS.NEAREST;
6927
+ THREE_TO_WEBGL[import_three19.NearestMipmapNearestFilter] = WEBGL_CONSTANTS.NEAREST_MIPMAP_NEAREST;
6928
+ THREE_TO_WEBGL[import_three19.NearestMipmapLinearFilter] = WEBGL_CONSTANTS.NEAREST_MIPMAP_LINEAR;
6929
+ THREE_TO_WEBGL[import_three19.LinearFilter] = WEBGL_CONSTANTS.LINEAR;
6930
+ THREE_TO_WEBGL[import_three19.LinearMipmapNearestFilter] = WEBGL_CONSTANTS.LINEAR_MIPMAP_NEAREST;
6931
+ THREE_TO_WEBGL[import_three19.LinearMipmapLinearFilter] = WEBGL_CONSTANTS.LINEAR_MIPMAP_LINEAR;
6932
+ THREE_TO_WEBGL[import_three19.ClampToEdgeWrapping] = WEBGL_CONSTANTS.CLAMP_TO_EDGE;
6933
+ THREE_TO_WEBGL[import_three19.RepeatWrapping] = WEBGL_CONSTANTS.REPEAT;
6934
+ THREE_TO_WEBGL[import_three19.MirroredRepeatWrapping] = WEBGL_CONSTANTS.MIRRORED_REPEAT;
6497
6935
  PATH_PROPERTIES = {
6498
6936
  scale: "scale",
6499
6937
  position: "translation",
6500
6938
  quaternion: "rotation",
6501
6939
  morphTargetInfluences: "weights"
6502
6940
  };
6503
- DEFAULT_SPECULAR_COLOR = new import_three18.Color();
6941
+ DEFAULT_SPECULAR_COLOR = new import_three19.Color();
6504
6942
  GLB_HEADER_BYTES = 12;
6505
6943
  GLB_HEADER_MAGIC = 1179937895;
6506
6944
  GLB_VERSION = 2;
@@ -6524,7 +6962,7 @@ var init_GLTFExporter = __esm({
6524
6962
  this.json = {
6525
6963
  asset: {
6526
6964
  version: "2.0",
6527
- generator: "THREE.GLTFExporter r" + import_three18.REVISION
6965
+ generator: "THREE.GLTFExporter r" + import_three19.REVISION
6528
6966
  }
6529
6967
  };
6530
6968
  this.cache = {
@@ -6676,7 +7114,7 @@ var init_GLTFExporter = __esm({
6676
7114
  isNormalizedNormalAttribute(normal) {
6677
7115
  const cache = this.cache;
6678
7116
  if (cache.attributesNormalized.has(normal)) return false;
6679
- const v = new import_three18.Vector3();
7117
+ const v = new import_three19.Vector3();
6680
7118
  for (let i = 0, il = normal.count; i < il; i++) {
6681
7119
  if (Math.abs(v.fromBufferAttribute(normal, i).length() - 1) > 5e-4) return false;
6682
7120
  }
@@ -6693,7 +7131,7 @@ var init_GLTFExporter = __esm({
6693
7131
  const cache = this.cache;
6694
7132
  if (cache.attributesNormalized.has(normal)) return cache.attributesNormalized.get(normal);
6695
7133
  const attribute = normal.clone();
6696
- const v = new import_three18.Vector3();
7134
+ const v = new import_three19.Vector3();
6697
7135
  for (let i = 0, il = attribute.count; i < il; i++) {
6698
7136
  v.fromBufferAttribute(attribute, i);
6699
7137
  if (v.x === 0 && v.y === 0 && v.z === 0) {
@@ -6737,7 +7175,7 @@ var init_GLTFExporter = __esm({
6737
7175
  async buildMetalRoughTextureAsync(metalnessMap, roughnessMap) {
6738
7176
  if (metalnessMap === roughnessMap) return metalnessMap;
6739
7177
  function getEncodingConversion(map) {
6740
- if (map.colorSpace === import_three18.SRGBColorSpace) {
7178
+ if (map.colorSpace === import_three19.SRGBColorSpace) {
6741
7179
  return function SRGBToLinear(c) {
6742
7180
  return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4);
6743
7181
  };
@@ -6746,10 +7184,10 @@ var init_GLTFExporter = __esm({
6746
7184
  return c;
6747
7185
  };
6748
7186
  }
6749
- if (metalnessMap instanceof import_three18.CompressedTexture) {
7187
+ if (metalnessMap instanceof import_three19.CompressedTexture) {
6750
7188
  metalnessMap = await this.decompressTextureAsync(metalnessMap);
6751
7189
  }
6752
- if (roughnessMap instanceof import_three18.CompressedTexture) {
7190
+ if (roughnessMap instanceof import_three19.CompressedTexture) {
6753
7191
  roughnessMap = await this.decompressTextureAsync(roughnessMap);
6754
7192
  }
6755
7193
  const metalness = metalnessMap ? metalnessMap.image : null;
@@ -6784,8 +7222,8 @@ var init_GLTFExporter = __esm({
6784
7222
  context.putImageData(composite, 0, 0);
6785
7223
  const reference = metalnessMap || roughnessMap;
6786
7224
  const texture = reference.clone();
6787
- texture.source = new import_three18.TextureSource(canvas);
6788
- texture.colorSpace = import_three18.NoColorSpace;
7225
+ texture.source = new import_three19.TextureSource(canvas);
7226
+ texture.colorSpace = import_three19.NoColorSpace;
6789
7227
  texture.channel = (metalnessMap || roughnessMap).channel;
6790
7228
  if (metalnessMap && roughnessMap && metalnessMap.channel !== roughnessMap.channel) {
6791
7229
  console.warn("THREE.GLTFExporter: UV channels for metalnessMap and roughnessMap textures must match.");
@@ -6806,7 +7244,7 @@ var init_GLTFExporter = __esm({
6806
7244
  * @return {Promise<THREE.Texture>} The derived normal map texture.
6807
7245
  */
6808
7246
  async buildNormalMapTextureAsync(normalMap, flipX, flipY) {
6809
- if (normalMap instanceof import_three18.CompressedTexture) {
7247
+ if (normalMap instanceof import_three19.CompressedTexture) {
6810
7248
  normalMap = await this.decompressTextureAsync(normalMap);
6811
7249
  }
6812
7250
  const image = normalMap.image;
@@ -6825,7 +7263,7 @@ var init_GLTFExporter = __esm({
6825
7263
  }
6826
7264
  context.putImageData(imageData, 0, 0);
6827
7265
  const texture = normalMap.clone();
6828
- texture.source = new import_three18.TextureSource(canvas);
7266
+ texture.source = new import_three19.TextureSource(canvas);
6829
7267
  return texture;
6830
7268
  }
6831
7269
  async decompressTextureAsync(texture, maxTextureSize = Infinity) {
@@ -6889,7 +7327,7 @@ var init_GLTFExporter = __esm({
6889
7327
  else if (a === 2) value = attribute.getZ(i);
6890
7328
  else if (a === 3) value = attribute.getW(i);
6891
7329
  if (attribute.normalized === true) {
6892
- value = import_three18.MathUtils.normalize(value, attribute.array);
7330
+ value = import_three19.MathUtils.normalize(value, attribute.array);
6893
7331
  }
6894
7332
  }
6895
7333
  if (componentType === WEBGL_CONSTANTS.FLOAT) {
@@ -7044,7 +7482,7 @@ var init_GLTFExporter = __esm({
7044
7482
  ctx.scale(1, -1);
7045
7483
  }
7046
7484
  if (image.data !== void 0) {
7047
- if (format2 !== import_three18.RGBAFormat) {
7485
+ if (format2 !== import_three19.RGBAFormat) {
7048
7486
  console.error("GLTFExporter: Only RGBAFormat is supported.", format2);
7049
7487
  }
7050
7488
  if (image.width > options.maxTextureSize || image.height > options.maxTextureSize) {
@@ -7072,7 +7510,7 @@ var init_GLTFExporter = __esm({
7072
7510
  })
7073
7511
  );
7074
7512
  } else {
7075
- imageDef.uri = import_three18.ImageUtils.getDataURL(canvas, mimeType);
7513
+ imageDef.uri = import_three19.ImageUtils.getDataURL(canvas, mimeType);
7076
7514
  }
7077
7515
  const index = json.images.push(imageDef) - 1;
7078
7516
  cachedImages[key] = index;
@@ -7109,7 +7547,7 @@ var init_GLTFExporter = __esm({
7109
7547
  const json = this.json;
7110
7548
  if (cache.textures.has(map)) return cache.textures.get(map);
7111
7549
  if (!json.textures) json.textures = [];
7112
- if (map instanceof import_three18.CompressedTexture) {
7550
+ if (map instanceof import_three19.CompressedTexture) {
7113
7551
  map = await this.decompressTextureAsync(map, options.maxTextureSize);
7114
7552
  }
7115
7553
  const mimeType = map.userData.mimeType;
@@ -7242,7 +7680,7 @@ var init_GLTFExporter = __esm({
7242
7680
  materialDef.alphaCutoff = material.alphaTest;
7243
7681
  }
7244
7682
  }
7245
- if (material.side === import_three18.DoubleSide) materialDef.doubleSided = true;
7683
+ if (material.side === import_three19.DoubleSide) materialDef.doubleSided = true;
7246
7684
  if (material.name !== "") materialDef.name = material.name;
7247
7685
  this.serializeUserData(material, materialDef);
7248
7686
  await this._invokeAllAsync(async function(ext) {
@@ -7491,7 +7929,7 @@ var init_GLTFExporter = __esm({
7491
7929
  } else {
7492
7930
  cameraDef.perspective = {
7493
7931
  aspectRatio: camera.aspect,
7494
- yfov: import_three18.MathUtils.degToRad(camera.fov),
7932
+ yfov: import_three19.MathUtils.degToRad(camera.fov),
7495
7933
  zfar: camera.far <= 0 ? 1e-3 : camera.far,
7496
7934
  znear: camera.near < 0 ? 0 : camera.near
7497
7935
  };
@@ -7519,8 +7957,8 @@ var init_GLTFExporter = __esm({
7519
7957
  const samplers = [];
7520
7958
  for (let i = 0; i < tracks.length; ++i) {
7521
7959
  const track = tracks[i];
7522
- const trackBinding = import_three18.PropertyBinding.parseTrackName(track.name);
7523
- let trackNode = import_three18.PropertyBinding.findNode(root, trackBinding.nodeName);
7960
+ const trackBinding = import_three19.PropertyBinding.parseTrackName(track.name);
7961
+ let trackNode = import_three19.PropertyBinding.findNode(root, trackBinding.nodeName);
7524
7962
  const trackProperty = PATH_PROPERTIES[trackBinding.propertyName];
7525
7963
  if (trackBinding.objectName === "bones") {
7526
7964
  if (trackNode.isSkinnedMesh === true) {
@@ -7542,14 +7980,14 @@ var init_GLTFExporter = __esm({
7542
7980
  if (track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline === true) {
7543
7981
  interpolation = "CUBICSPLINE";
7544
7982
  outputItemSize /= 3;
7545
- } else if (track.getInterpolation() === import_three18.InterpolateDiscrete) {
7983
+ } else if (track.getInterpolation() === import_three19.InterpolateDiscrete) {
7546
7984
  interpolation = "STEP";
7547
7985
  } else {
7548
7986
  interpolation = "LINEAR";
7549
7987
  }
7550
7988
  samplers.push({
7551
- input: this.processAccessor(new import_three18.BufferAttribute(track.times, inputItemSize)),
7552
- output: this.processAccessor(new import_three18.BufferAttribute(track.values, outputItemSize)),
7989
+ input: this.processAccessor(new import_three19.BufferAttribute(track.times, inputItemSize)),
7990
+ output: this.processAccessor(new import_three19.BufferAttribute(track.values, outputItemSize)),
7553
7991
  interpolation
7554
7992
  });
7555
7993
  channels.push({
@@ -7583,7 +8021,7 @@ var init_GLTFExporter = __esm({
7583
8021
  if (rootJoint === void 0) return null;
7584
8022
  const joints = [];
7585
8023
  const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
7586
- const temporaryBoneInverse = new import_three18.Matrix4();
8024
+ const temporaryBoneInverse = new import_three19.Matrix4();
7587
8025
  for (let i = 0; i < skeleton.bones.length; ++i) {
7588
8026
  joints.push(nodeMap.get(skeleton.bones[i]));
7589
8027
  temporaryBoneInverse.copy(skeleton.boneInverses[i]);
@@ -7591,7 +8029,7 @@ var init_GLTFExporter = __esm({
7591
8029
  }
7592
8030
  if (json.skins === void 0) json.skins = [];
7593
8031
  json.skins.push({
7594
- inverseBindMatrices: this.processAccessor(new import_three18.BufferAttribute(inverseBindMatrices, 16)),
8032
+ inverseBindMatrices: this.processAccessor(new import_three19.BufferAttribute(inverseBindMatrices, 16)),
7595
8033
  joints,
7596
8034
  skeleton: nodeMap.get(rootJoint)
7597
8035
  });
@@ -7753,7 +8191,7 @@ var init_GLTFExporter = __esm({
7753
8191
  * @param {Array<THREE.Object3D>} objects List of objects to process
7754
8192
  */
7755
8193
  async processObjectsAsync(objects) {
7756
- const scene = new import_three18.Scene();
8194
+ const scene = new import_three19.Scene();
7757
8195
  scene.name = "AuxScene";
7758
8196
  for (let i = 0; i < objects.length; i++) {
7759
8197
  scene.children.push(objects[i]);
@@ -7771,7 +8209,7 @@ var init_GLTFExporter = __esm({
7771
8209
  });
7772
8210
  const objectsWithoutScene = [];
7773
8211
  for (let i = 0; i < input.length; i++) {
7774
- if (input[i] instanceof import_three18.Scene) {
8212
+ if (input[i] instanceof import_three19.Scene) {
7775
8213
  await this.processSceneAsync(input[i]);
7776
8214
  } else {
7777
8215
  objectsWithoutScene.push(input[i]);
@@ -8169,10 +8607,10 @@ var init_GLTFExporter = __esm({
8169
8607
  const translationAttr = new Float32Array(mesh.count * 3);
8170
8608
  const rotationAttr = new Float32Array(mesh.count * 4);
8171
8609
  const scaleAttr = new Float32Array(mesh.count * 3);
8172
- const matrix = new import_three18.Matrix4();
8173
- const position = new import_three18.Vector3();
8174
- const quaternion = new import_three18.Quaternion();
8175
- const scale = new import_three18.Vector3();
8610
+ const matrix = new import_three19.Matrix4();
8611
+ const position = new import_three19.Vector3();
8612
+ const quaternion = new import_three19.Quaternion();
8613
+ const scale = new import_three19.Vector3();
8176
8614
  for (let i = 0; i < mesh.count; i++) {
8177
8615
  mesh.getMatrixAt(i, matrix);
8178
8616
  matrix.decompose(position, quaternion, scale);
@@ -8181,9 +8619,9 @@ var init_GLTFExporter = __esm({
8181
8619
  scale.toArray(scaleAttr, i * 3);
8182
8620
  }
8183
8621
  const attributes = {
8184
- TRANSLATION: writer.processAccessor(new import_three18.BufferAttribute(translationAttr, 3)),
8185
- ROTATION: writer.processAccessor(new import_three18.BufferAttribute(rotationAttr, 4)),
8186
- SCALE: writer.processAccessor(new import_three18.BufferAttribute(scaleAttr, 3))
8622
+ TRANSLATION: writer.processAccessor(new import_three19.BufferAttribute(translationAttr, 3)),
8623
+ ROTATION: writer.processAccessor(new import_three19.BufferAttribute(rotationAttr, 4)),
8624
+ SCALE: writer.processAccessor(new import_three19.BufferAttribute(scaleAttr, 3))
8187
8625
  };
8188
8626
  if (mesh.instanceColor)
8189
8627
  attributes._COLOR_0 = writer.processAccessor(mesh.instanceColor);
@@ -8248,8 +8686,8 @@ var init_GLTFExporter = __esm({
8248
8686
  const sourceTracks = clip.tracks;
8249
8687
  for (let i = 0; i < sourceTracks.length; ++i) {
8250
8688
  let sourceTrack = sourceTracks[i];
8251
- const sourceTrackBinding = import_three18.PropertyBinding.parseTrackName(sourceTrack.name);
8252
- const sourceTrackNode = import_three18.PropertyBinding.findNode(root, sourceTrackBinding.nodeName);
8689
+ const sourceTrackBinding = import_three19.PropertyBinding.parseTrackName(sourceTrack.name);
8690
+ const sourceTrackNode = import_three19.PropertyBinding.findNode(root, sourceTrackBinding.nodeName);
8253
8691
  if (sourceTrackBinding.propertyName !== "morphTargetInfluences" || sourceTrackBinding.propertyIndex === void 0) {
8254
8692
  tracks.push(sourceTrack);
8255
8693
  continue;
@@ -8260,7 +8698,7 @@ var init_GLTFExporter = __esm({
8260
8698
  }
8261
8699
  console.warn("THREE.GLTFExporter: Morph target interpolation mode not yet supported. Using LINEAR instead.");
8262
8700
  sourceTrack = sourceTrack.clone();
8263
- sourceTrack.setInterpolation(import_three18.InterpolateLinear);
8701
+ sourceTrack.setInterpolation(import_three19.InterpolateLinear);
8264
8702
  }
8265
8703
  const targetCount = sourceTrackNode.morphTargetInfluences.length;
8266
8704
  const targetIndex = sourceTrackNode.morphTargetDictionary[sourceTrackBinding.propertyIndex];
@@ -8294,7 +8732,7 @@ var init_GLTFExporter = __esm({
8294
8732
  return clip;
8295
8733
  },
8296
8734
  toTypedBufferAttribute: function(srcAttribute, TypedArray) {
8297
- const dstAttribute = new import_three18.BufferAttribute(new TypedArray(srcAttribute.count * srcAttribute.itemSize), srcAttribute.itemSize, false);
8735
+ const dstAttribute = new import_three19.BufferAttribute(new TypedArray(srcAttribute.count * srcAttribute.itemSize), srcAttribute.itemSize, false);
8298
8736
  if (!srcAttribute.normalized && !srcAttribute.isInterleavedBufferAttribute) {
8299
8737
  dstAttribute.array.set(srcAttribute.array);
8300
8738
  return dstAttribute;
@@ -8372,7 +8810,7 @@ function createGuiTextGeometry(layout, shadow = true) {
8372
8810
  batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
8373
8811
  batches.push(batch);
8374
8812
  }
8375
- const color2 = new import_three20.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
8813
+ const color2 = new import_three21.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
8376
8814
  const offset = isShadow ? glyph?.shadowOffset ?? 1 : 0;
8377
8815
  const top = glyph ? 7 - glyph.ascent : 0;
8378
8816
  const height = glyph ? glyph.height * glyph.scale : 8;
@@ -8414,12 +8852,12 @@ function createGuiTextGeometry(layout, shadow = true) {
8414
8852
  }
8415
8853
  }
8416
8854
  }
8417
- return batches.map(({ image, positions, uvs, colors: colors2, indices }) => ({ image, geometry: new import_three20.BufferGeometry().setAttribute("position", new import_three20.Float32BufferAttribute(positions, 3)).setAttribute("uv", new import_three20.Float32BufferAttribute(uvs, 2)).setAttribute("color", new import_three20.Float32BufferAttribute(colors2, 3)).setIndex(indices) }));
8855
+ return batches.map(({ image, positions, uvs, colors: colors2, indices }) => ({ image, geometry: new import_three21.BufferGeometry().setAttribute("position", new import_three21.Float32BufferAttribute(positions, 3)).setAttribute("uv", new import_three21.Float32BufferAttribute(uvs, 2)).setAttribute("color", new import_three21.Float32BufferAttribute(colors2, 3)).setIndex(indices) }));
8418
8856
  }
8419
- var import_three20;
8857
+ var import_three21;
8420
8858
  var init_GuiText = __esm({
8421
8859
  "src/gui/GuiText.ts"() {
8422
- import_three20 = require("three");
8860
+ import_three21 = require("three");
8423
8861
  init_Fonts();
8424
8862
  }
8425
8863
  });
@@ -8428,7 +8866,7 @@ var init_GuiText = __esm({
8428
8866
  function createGuiTextureGeometry(width, height, imageWidth, imageHeight, crop, scaling) {
8429
8867
  if (crop || !scaling || scaling.type === "stretch") {
8430
8868
  const [x, y, w, h] = crop ?? [0, 0, imageWidth, imageHeight];
8431
- const geometry = new import_three21.PlaneGeometry(width, height);
8869
+ const geometry = new import_three22.PlaneGeometry(width, height);
8432
8870
  const uv = geometry.getAttribute("uv");
8433
8871
  for (let i = 0; i < uv.count; i++) {
8434
8872
  uv.setXY(
@@ -8499,12 +8937,12 @@ function createGuiTextureGeometry(width, height, imageWidth, imageHeight, crop,
8499
8937
  }
8500
8938
  }
8501
8939
  }
8502
- return new import_three21.BufferGeometry().setAttribute("position", new import_three21.Float32BufferAttribute(positions, 3)).setAttribute("normal", new import_three21.Float32BufferAttribute(normals, 3)).setAttribute("uv", new import_three21.Float32BufferAttribute(uvs, 2)).setIndex(indices);
8940
+ return new import_three22.BufferGeometry().setAttribute("position", new import_three22.Float32BufferAttribute(positions, 3)).setAttribute("normal", new import_three22.Float32BufferAttribute(normals, 3)).setAttribute("uv", new import_three22.Float32BufferAttribute(uvs, 2)).setIndex(indices);
8503
8941
  }
8504
- var import_three21;
8942
+ var import_three22;
8505
8943
  var init_GuiTextureGeometry = __esm({
8506
8944
  "src/gui/GuiTextureGeometry.ts"() {
8507
- import_three21 = require("three");
8945
+ import_three22 = require("three");
8508
8946
  }
8509
8947
  });
8510
8948
 
@@ -8520,7 +8958,7 @@ function mergeBufferGeometries(geometries, useGroups = false) {
8520
8958
  const attributes = {};
8521
8959
  const morphAttributes = {};
8522
8960
  const morphTargetsRelative = geometries[0].morphTargetsRelative;
8523
- const mergedGeometry = new import_three22.BufferGeometry();
8961
+ const mergedGeometry = new import_three23.BufferGeometry();
8524
8962
  let offset = 0;
8525
8963
  for (let i = 0; i < geometries.length; ++i) {
8526
8964
  const geometry = geometries[i];
@@ -8644,7 +9082,7 @@ function mergeBufferAttributes(attributes) {
8644
9082
  array.set(attributes[i].array, offset);
8645
9083
  offset += attributes[i].array.length;
8646
9084
  }
8647
- return new import_three22.BufferAttribute(array, itemSize, normalized);
9085
+ return new import_three23.BufferAttribute(array, itemSize, normalized);
8648
9086
  }
8649
9087
  function interleaveAttributes(attributes) {
8650
9088
  let TypedArray;
@@ -8660,7 +9098,7 @@ function interleaveAttributes(attributes) {
8660
9098
  arrayLength += attribute.array.length;
8661
9099
  stride += attribute.itemSize;
8662
9100
  }
8663
- const interleavedBuffer = new import_three22.InterleavedBuffer(new TypedArray(arrayLength), stride);
9101
+ const interleavedBuffer = new import_three23.InterleavedBuffer(new TypedArray(arrayLength), stride);
8664
9102
  let offset = 0;
8665
9103
  const res = [];
8666
9104
  const getters = ["getX", "getY", "getZ", "getW"];
@@ -8669,7 +9107,7 @@ function interleaveAttributes(attributes) {
8669
9107
  const attribute = attributes[j];
8670
9108
  const itemSize = attribute.itemSize;
8671
9109
  const count = attribute.count;
8672
- const iba = new import_three22.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
9110
+ const iba = new import_three23.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
8673
9111
  res.push(iba);
8674
9112
  offset += itemSize;
8675
9113
  for (let c = 0; c < count; c++) {
@@ -8753,13 +9191,13 @@ function mergeVertices(geometry, tolerance = 1e-4) {
8753
9191
  const name = attributeNames[i];
8754
9192
  const oldAttribute = geometry.getAttribute(name);
8755
9193
  const buffer = new oldAttribute.array.constructor(attrArrays[name]);
8756
- const attribute = new import_three22.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
9194
+ const attribute = new import_three23.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
8757
9195
  result.setAttribute(name, attribute);
8758
9196
  if (name in morphAttrsArrays) {
8759
9197
  for (let j = 0; j < morphAttrsArrays[name].length; j++) {
8760
9198
  const oldMorphAttribute = geometry.morphAttributes[name][j];
8761
9199
  const buffer2 = new oldMorphAttribute.array.constructor(morphAttrsArrays[name][j]);
8762
- const morphAttribute = new import_three22.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
9200
+ const morphAttribute = new import_three23.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
8763
9201
  result.morphAttributes[name][j] = morphAttribute;
8764
9202
  }
8765
9203
  }
@@ -8768,11 +9206,11 @@ function mergeVertices(geometry, tolerance = 1e-4) {
8768
9206
  return result;
8769
9207
  }
8770
9208
  function toTrianglesDrawMode(geometry, drawMode) {
8771
- if (drawMode === import_three22.TrianglesDrawMode) {
9209
+ if (drawMode === import_three23.TrianglesDrawMode) {
8772
9210
  console.warn("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.");
8773
9211
  return geometry;
8774
9212
  }
8775
- if (drawMode === import_three22.TriangleFanDrawMode || drawMode === import_three22.TriangleStripDrawMode) {
9213
+ if (drawMode === import_three23.TriangleFanDrawMode || drawMode === import_three23.TriangleStripDrawMode) {
8776
9214
  let index = geometry.getIndex();
8777
9215
  if (index === null) {
8778
9216
  const indices = [];
@@ -8790,7 +9228,7 @@ function toTrianglesDrawMode(geometry, drawMode) {
8790
9228
  }
8791
9229
  const numberOfTriangles = index.count - 2;
8792
9230
  const newIndices = [];
8793
- if (drawMode === import_three22.TriangleFanDrawMode) {
9231
+ if (drawMode === import_three23.TriangleFanDrawMode) {
8794
9232
  for (let i = 1; i <= numberOfTriangles; i++) {
8795
9233
  newIndices.push(index.getX(0));
8796
9234
  newIndices.push(index.getX(i));
@@ -8826,15 +9264,15 @@ function computeMorphedAttributes(object4) {
8826
9264
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
8827
9265
  return null;
8828
9266
  }
8829
- const _vA = new import_three22.Vector3();
8830
- const _vB = new import_three22.Vector3();
8831
- const _vC = new import_three22.Vector3();
8832
- const _tempA = new import_three22.Vector3();
8833
- const _tempB = new import_three22.Vector3();
8834
- const _tempC = new import_three22.Vector3();
8835
- const _morphA = new import_three22.Vector3();
8836
- const _morphB = new import_three22.Vector3();
8837
- const _morphC = new import_three22.Vector3();
9267
+ const _vA = new import_three23.Vector3();
9268
+ const _vB = new import_three23.Vector3();
9269
+ const _vC = new import_three23.Vector3();
9270
+ const _tempA = new import_three23.Vector3();
9271
+ const _tempB = new import_three23.Vector3();
9272
+ const _tempC = new import_three23.Vector3();
9273
+ const _morphA = new import_three23.Vector3();
9274
+ const _morphB = new import_three23.Vector3();
9275
+ const _morphC = new import_three23.Vector3();
8838
9276
  function _calculateMorphedAttributeData(object5, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
8839
9277
  _vA.fromBufferAttribute(attribute, a2);
8840
9278
  _vB.fromBufferAttribute(attribute, b2);
@@ -9029,8 +9467,8 @@ function computeMorphedAttributes(object4) {
9029
9467
  }
9030
9468
  }
9031
9469
  }
9032
- const morphedPositionAttribute = new import_three22.Float32BufferAttribute(modifiedPosition, 3);
9033
- const morphedNormalAttribute = new import_three22.Float32BufferAttribute(modifiedNormal, 3);
9470
+ const morphedPositionAttribute = new import_three23.Float32BufferAttribute(modifiedPosition, 3);
9471
+ const morphedNormalAttribute = new import_three23.Float32BufferAttribute(modifiedNormal, 3);
9034
9472
  return {
9035
9473
  positionAttribute,
9036
9474
  normalAttribute,
@@ -9038,25 +9476,25 @@ function computeMorphedAttributes(object4) {
9038
9476
  morphedNormalAttribute
9039
9477
  };
9040
9478
  }
9041
- var import_three22;
9479
+ var import_three23;
9042
9480
  var init_BufferGeometryUtils = __esm({
9043
9481
  "src/three/BufferGeometryUtils.js"() {
9044
- import_three22 = require("three");
9482
+ import_three23 = require("three");
9045
9483
  }
9046
9484
  });
9047
9485
 
9048
9486
  // src/model/ModelCulling.ts
9049
- var import_three23, ModelCulling;
9487
+ var import_three24, ModelCulling;
9050
9488
  var init_ModelCulling = __esm({
9051
9489
  "src/model/ModelCulling.ts"() {
9052
- import_three23 = require("three");
9490
+ import_three24 = require("three");
9053
9491
  init_CubeFace();
9054
9492
  ModelCulling = class {
9055
9493
  /** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
9056
9494
  static toLocalMask(worldMask, rotation) {
9057
9495
  let localMask = 0;
9058
9496
  for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
9059
- const normal = new import_three23.Vector3(...offset).applyEuler(rotation);
9497
+ const normal = new import_three24.Vector3(...offset).applyEuler(rotation);
9060
9498
  const worldFace = 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);
9061
9499
  if (worldFace < 0) return 0;
9062
9500
  if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
@@ -9319,10 +9757,10 @@ var init_SkinImage = __esm({
9319
9757
  });
9320
9758
 
9321
9759
  // src/skin/SkinTextures.ts
9322
- var import_three24, SkinTextures;
9760
+ var import_three25, SkinTextures;
9323
9761
  var init_SkinTextures = __esm({
9324
9762
  "src/skin/SkinTextures.ts"() {
9325
- import_three24 = require("three");
9763
+ import_three25 = require("three");
9326
9764
  init_Caching();
9327
9765
  init_CacheKey();
9328
9766
  init_MineRenderError();
@@ -9360,11 +9798,11 @@ var init_SkinTextures = __esm({
9360
9798
  });
9361
9799
  }
9362
9800
  static createMaterial(pixels, width, height, transparent = true) {
9363
- const texture = Textures.initTextureProps(new import_three24.DataTexture(pixels, width, height));
9364
- texture.colorSpace = import_three24.SRGBColorSpace;
9801
+ const texture = Textures.initTextureProps(new import_three25.DataTexture(pixels, width, height));
9802
+ texture.colorSpace = import_three25.SRGBColorSpace;
9365
9803
  texture.flipY = true;
9366
9804
  texture.needsUpdate = true;
9367
- return new import_three24.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three24.DoubleSide : import_three24.FrontSide, alphaTest: transparent ? 0.1 : 0 });
9805
+ return new import_three25.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three25.DoubleSide : import_three25.FrontSide, alphaTest: transparent ? 0.1 : 0 });
9368
9806
  }
9369
9807
  static isSlim(image, scale) {
9370
9808
  for (const [x, y] of [[46, 52], [54, 20]]) {
@@ -9417,7 +9855,7 @@ var init_SkinTextures = __esm({
9417
9855
  return pixels;
9418
9856
  }
9419
9857
  static makeBaseOpaque(pixels, width, scale) {
9420
- for (const [x0, y0, x1, y1] of [[0, 0, 32, 16], [0, 16, 64, 32], [16, 48, 48, 64]]) {
9858
+ for (const [x0, y0, x1, y1] of [[0, 0, 32, 16], [0, 16, 64, 32], [16, 48, 48, 64], [32, 0, 38, 7]]) {
9421
9859
  for (let y = y0 * scale; y < y1 * scale; y++) {
9422
9860
  for (let x = x0 * scale; x < x1 * scale; x++) pixels[(y * width + x) * 4 + 3] = 255;
9423
9861
  }
@@ -9428,6 +9866,10 @@ var init_SkinTextures = __esm({
9428
9866
  });
9429
9867
 
9430
9868
  // src/skin/PlayerHeadTextures.ts
9869
+ var PlayerHeadTextures_exports = {};
9870
+ __export(PlayerHeadTextures_exports, {
9871
+ PlayerHeadTextures: () => PlayerHeadTextures
9872
+ });
9431
9873
  var PlayerHeadTextures;
9432
9874
  var init_PlayerHeadTextures = __esm({
9433
9875
  "src/skin/PlayerHeadTextures.ts"() {
@@ -9548,10 +9990,10 @@ var init_PlayerHeadTextures = __esm({
9548
9990
  });
9549
9991
 
9550
9992
  // src/model/SpecialItems.ts
9551
- var import_three25, SpecialItems;
9993
+ var import_three26, SpecialItems;
9552
9994
  var init_SpecialItems = __esm({
9553
9995
  "src/model/SpecialItems.ts"() {
9554
- import_three25 = require("three");
9996
+ import_three26 = require("three");
9555
9997
  init_AssetKey();
9556
9998
  init_Entities();
9557
9999
  init_BannerPatterns();
@@ -9589,8 +10031,8 @@ var init_SpecialItems = __esm({
9589
10031
  const sideTextures = DecoratedPots.getSideTextures(components["minecraft:pot_decorations"], root);
9590
10032
  const baseTexture = texture("decorated_pot_base", "decorated_pot");
9591
10033
  const [base, sides] = await Promise.all([
9592
- load("decorated_pot_base", baseTexture, new import_three25.Matrix4()),
9593
- load("decorated_pot_sides", texture("decorated_pot_side", "decorated_pot"), new import_three25.Matrix4())
10034
+ load("decorated_pot_base", baseTexture, new import_three26.Matrix4()),
10035
+ load("decorated_pot_sides", texture("decorated_pot_side", "decorated_pot"), new import_three26.Matrix4())
9594
10036
  ]);
9595
10037
  const layers = {
9596
10038
  base: { key: base.model.key, texture: baseTexture, layer: base.model.layer, render: "solid" }
@@ -9618,7 +10060,7 @@ var init_SpecialItems = __esm({
9618
10060
  const part = await load(
9619
10061
  "player_head",
9620
10062
  skin.texture,
9621
- new import_three25.Matrix4().makeTranslation(8, 0, 8).multiply(new import_three25.Matrix4().makeScale(-1, -1, 1)),
10063
+ new import_three26.Matrix4().makeTranslation(8, 0, 8).multiply(new import_three26.Matrix4().makeScale(-1, -1, 1)),
9622
10064
  { head: [0, Math.PI, 0] }
9623
10065
  );
9624
10066
  const main = { ...part.model, texture: skin.texture, render: "translucent" };
@@ -9635,7 +10077,7 @@ var init_SpecialItems = __esm({
9635
10077
  const statue = await load(
9636
10078
  special.pose === "standing" ? "copper_golem" : `copper_golem_${special.pose}`,
9637
10079
  key,
9638
- new import_three25.Matrix4().makeTranslation(8, 24, 8).multiply(new import_three25.Matrix4().makeScale(-1, -1, 1))
10080
+ new import_three26.Matrix4().makeTranslation(8, 24, 8).multiply(new import_three26.Matrix4().makeScale(-1, -1, 1))
9639
10081
  );
9640
10082
  const pose = statue.model.layer.root.pose;
9641
10083
  statue.rotations.root = [pose.rotation[0], Math.PI, Math.PI];
@@ -9651,7 +10093,7 @@ var init_SpecialItems = __esm({
9651
10093
  const part = await load(
9652
10094
  trident ? "trident" : "conduit",
9653
10095
  texture(trident ? "trident" : "conduit/base", ""),
9654
- trident ? new import_three25.Matrix4().makeScale(1, -1, -1) : new import_three25.Matrix4().makeTranslation(8, 8, 8),
10096
+ trident ? new import_three26.Matrix4().makeScale(1, -1, -1) : new import_three26.Matrix4().makeTranslation(8, 8, 8),
9655
10097
  {},
9656
10098
  trident ? ["main"] : ["shell"]
9657
10099
  );
@@ -9672,7 +10114,7 @@ var init_SpecialItems = __esm({
9672
10114
  const patterns = await BannerPatterns.getLayers(components["minecraft:banner_patterns"], banner ? "banner" : "shield", root);
9673
10115
  const patterned = banner || color2 !== void 0 || patterns.length > 0;
9674
10116
  const baseTexture = texture(banner ? "banner_base" : patterned ? "shield_base" : "shield_base_nopattern", "");
9675
- const transform = banner ? new import_three25.Matrix4().makeTranslation(8, 0, 8).multiply(new import_three25.Matrix4().makeScale(2 / 3, -2 / 3, -2 / 3)) : new import_three25.Matrix4().makeScale(1, -1, -1);
10117
+ const transform = banner ? new import_three26.Matrix4().makeTranslation(8, 0, 8).multiply(new import_three26.Matrix4().makeScale(2 / 3, -2 / 3, -2 / 3)) : new import_three26.Matrix4().makeScale(1, -1, -1);
9676
10118
  const part = await load(banner ? "standing_banner" : "shield", baseTexture, transform, {}, banner ? ["main", "flag"] : ["main"]);
9677
10119
  const layers = Object.fromEntries(Object.entries(part.model.layers ?? { main: part.model }).map(([name, layer]) => [name, { ...layer, render: "solid" }]));
9678
10120
  let overlay2;
@@ -9710,7 +10152,7 @@ var init_SpecialItems = __esm({
9710
10152
  case "minecraft:chest":
9711
10153
  case "chest": {
9712
10154
  const angle = -(special.openness ?? 0) * Math.PI / 2;
9713
- const chest = await load("chest", texture(special.texture, "chest"), new import_three25.Matrix4(), {
10155
+ const chest = await load("chest", texture(special.texture, "chest"), new import_three26.Matrix4(), {
9714
10156
  lid: [angle, 0, 0],
9715
10157
  lock: [angle, 0, 0]
9716
10158
  });
@@ -9732,18 +10174,18 @@ var init_SpecialItems = __esm({
9732
10174
  if (!Object.prototype.hasOwnProperty.call(directions, orientation)) throw new Error(`Unsupported shulker-box orientation ${orientation}`);
9733
10175
  if (typeof openness !== "number" || !Number.isFinite(openness)) throw new Error("Shulker-box openness must be finite");
9734
10176
  if (typeof special.texture !== "string" || !special.texture) throw new Error("Shulker-box texture is required");
9735
- const transform = new import_three25.Matrix4().makeTranslation(8, 8, 8).multiply(new import_three25.Matrix4().makeScale(0.9995, 0.9995, 0.9995)).multiply(new import_three25.Matrix4().makeRotationFromEuler(new import_three25.Euler(...directions[orientation], "XYZ"))).multiply(new import_three25.Matrix4().makeScale(1, -1, -1)).multiply(new import_three25.Matrix4().makeTranslation(0, -16, 0));
10177
+ const transform = new import_three26.Matrix4().makeTranslation(8, 8, 8).multiply(new import_three26.Matrix4().makeScale(0.9995, 0.9995, 0.9995)).multiply(new import_three26.Matrix4().makeRotationFromEuler(new import_three26.Euler(...directions[orientation], "XYZ"))).multiply(new import_three26.Matrix4().makeScale(1, -1, -1)).multiply(new import_three26.Matrix4().makeTranslation(0, -16, 0));
9736
10178
  const box = await load("shulker_box", texture(special.texture, "shulker"), transform, { lid: [0, openness * Math.PI * 1.5, 0] });
9737
10179
  box.positions = { lid: [0, 24 - openness * 8, 0] };
9738
10180
  return [box];
9739
10181
  }
9740
10182
  case "minecraft:bed":
9741
10183
  case "bed": {
9742
- const transform = new import_three25.Matrix4().makeTranslation(0, 9, 0).multiply(new import_three25.Matrix4().makeRotationX(Math.PI / 2)).multiply(new import_three25.Matrix4().makeTranslation(8, 8, 8)).multiply(new import_three25.Matrix4().makeRotationZ(Math.PI)).multiply(new import_three25.Matrix4().makeTranslation(-8, -8, -8));
10184
+ const transform = new import_three26.Matrix4().makeTranslation(0, 9, 0).multiply(new import_three26.Matrix4().makeRotationX(Math.PI / 2)).multiply(new import_three26.Matrix4().makeTranslation(8, 8, 8)).multiply(new import_three26.Matrix4().makeRotationZ(Math.PI)).multiply(new import_three26.Matrix4().makeTranslation(-8, -8, -8));
9743
10185
  const key = texture(special.texture, "bed");
9744
10186
  return Promise.all([
9745
10187
  load("bed_head", key, transform),
9746
- load("bed_foot", key, new import_three25.Matrix4().makeTranslation(0, 0, -16).multiply(transform))
10188
+ load("bed_foot", key, new import_three26.Matrix4().makeTranslation(0, 0, -16).multiply(transform))
9747
10189
  ]);
9748
10190
  }
9749
10191
  case "minecraft:head":
@@ -9755,265 +10197,62 @@ var init_SpecialItems = __esm({
9755
10197
  creeper: "creeper_head",
9756
10198
  dragon: "dragon_skull",
9757
10199
  piglin: "piglin_head"
9758
- };
9759
- const id = Object.prototype.hasOwnProperty.call(models, special.kind) ? models[special.kind] : void 0;
9760
- if (!id) throw new Error(`Unsupported special item head kind ${special.kind}`);
9761
- const animation = (special.animation ?? 0) * Math.PI * 0.2;
9762
- const rotations = { head: [0, Math.PI, 0] };
9763
- if (special.kind === "dragon") rotations.jaw = [(Math.sin(animation) + 1) * 0.2, 0, 0];
9764
- if (special.kind === "piglin") {
9765
- rotations.left_ear = [0, 0, -(Math.cos(animation * 1.2) + 2.5) * 0.2];
9766
- rotations.right_ear = [0, 0, (Math.cos(animation) + 2.5) * 0.2];
9767
- }
9768
- return [await load(
9769
- id,
9770
- special.texture ? texture(special.texture, "") : void 0,
9771
- new import_three25.Matrix4().makeTranslation(8, 0, 8).multiply(new import_three25.Matrix4().makeScale(-1, -1, 1)),
9772
- rotations
9773
- )];
9774
- }
9775
- }
9776
- }
9777
- /** An owned material with model-preview shading; its entity texture remains shared. */
9778
- static createMaterial(source, shade) {
9779
- const material = source.clone();
9780
- if (shade) {
9781
- material.onBeforeCompile = (shader) => {
9782
- shader.vertexShader = `varying float itemLight;
9783
- ${shader.vertexShader}`.replace("#include <begin_vertex>", `
9784
- #include <begin_vertex>
9785
- vec3 N = inverseTransformDirection(normalMatrix * normal, viewMatrix);
9786
- itemLight = (1.0 + N.y) * 0.25 - N.x * N.x * 0.15 + N.z * N.z * 0.05 + 0.5;
9787
- `);
9788
- shader.fragmentShader = `varying float itemLight;
9789
- ${shader.fragmentShader}`.replace(
9790
- "#include <color_fragment>",
9791
- "#include <color_fragment>\ndiffuseColor.rgb *= itemLight;"
9792
- );
9793
- };
9794
- material.customProgramCacheKey = () => "special-item-side";
9795
- }
9796
- return material;
9797
- }
9798
- };
9799
- }
9800
- });
9801
-
9802
- // src/model/GuiLight.ts
9803
- var GuiLight;
9804
- var init_GuiLight = __esm({
9805
- "src/model/GuiLight.ts"() {
9806
- GuiLight = /* @__PURE__ */ ((GuiLight2) => {
9807
- GuiLight2["FRONT"] = "front";
9808
- GuiLight2["SIDE"] = "side";
9809
- return GuiLight2;
9810
- })(GuiLight || {});
9811
- }
9812
- });
9813
-
9814
- // src/model/ItemGlint.ts
9815
- var import_three26, ItemGlint;
9816
- var init_ItemGlint = __esm({
9817
- "src/model/ItemGlint.ts"() {
9818
- import_three26 = require("three");
9819
- init_AssetKey();
9820
- init_Models();
9821
- init_ModelTextures();
9822
- init_Caching();
9823
- init_Textures();
9824
- init_Ticker();
9825
- init_CubeFace();
9826
- init_DisplayPosition();
9827
- ItemGlint = class _ItemGlint {
9828
- constructor(owner, material) {
9829
- this.owner = owner;
9830
- this.material = material;
9831
- this.updateTime();
9832
- }
9833
- /** Enables glint for enchantments or a lodestone compass unless a boolean component overrides them. */
9834
- static enabled(components = {}, itemId) {
9835
- const override = Models.componentValue(components, "enchantment_glint_override");
9836
- if (override !== void 0) {
9837
- if (typeof override !== "boolean") throw new Error("Item enchantment_glint_override must be a boolean");
9838
- return override;
9839
- }
9840
- if (itemId === "minecraft:compass") {
9841
- Models.componentValue(components, "lodestone_tracker");
9842
- if (Object.prototype.hasOwnProperty.call(components, "lodestone_tracker") || Object.prototype.hasOwnProperty.call(components, "minecraft:lodestone_tracker")) return true;
9843
- }
9844
- const enchantments = Models.componentValue(components, "enchantments");
9845
- if (enchantments === void 0) return false;
9846
- if (!enchantments || typeof enchantments !== "object" || Array.isArray(enchantments)) throw new Error("Item enchantments must be an object");
9847
- return Object.keys(enchantments).length > 0;
9848
- }
9849
- /** @internal */
9850
- static async create(owner, baseMap, root, entityMeshes) {
9851
- const key = new AssetKey("minecraft", "enchanted_glint_item", "textures", "misc", "assets", ".png", root);
9852
- const assetKey = key.serialize(), textureKey = `item-glint:${assetKey}`;
9853
- let texture = Caching.textureCache.getIfPresent(textureKey);
9854
- if (!texture) {
9855
- const pending = ModelTextures.get(key);
9856
- const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKey);
9857
- const [image, metadata] = await Promise.all([pending, ModelTextures.getMeta(key)]);
9858
- if (!image) throw new Error(`Missing item glint texture ${key.toNamespacedString()}`);
9859
- if (cachedAsset && Caching.textureAssetCache.getIfPresent(assetKey) !== cachedAsset) return this.create(owner, baseMap, root, entityMeshes);
9860
- texture = Caching.textureCache.get(textureKey, () => {
9861
- const texture2 = Textures.createCanvasTexture(image.data.canvas);
9862
- texture2.wrapS = texture2.wrapT = metadata?.texture?.clamp ? import_three26.ClampToEdgeWrapping : import_three26.RepeatWrapping;
9863
- if (metadata?.texture?.blur) texture2.magFilter = texture2.minFilter = import_three26.LinearFilter;
9864
- return texture2;
9865
- });
9866
- }
9867
- const material = new import_three26.ShaderMaterial({
9868
- name: "item-glint",
9869
- transparent: true,
9870
- depthWrite: false,
9871
- depthFunc: import_three26.EqualDepth,
9872
- side: import_three26.DoubleSide,
9873
- forceSinglePass: true,
9874
- blending: import_three26.CustomBlending,
9875
- blendSrc: import_three26.SrcColorFactor,
9876
- blendDst: import_three26.OneFactor,
9877
- blendSrcAlpha: import_three26.ZeroFactor,
9878
- blendDstAlpha: import_three26.OneFactor,
9879
- toneMapped: false,
9880
- defines: entityMeshes ? { ENTITY_GLINT: true } : {},
9881
- uniforms: {
9882
- ...!entityMeshes && { baseMap: { value: baseMap } },
9883
- glintMap: { value: texture },
9884
- glintOffset: { value: new import_three26.Vector2() },
9885
- glintAlpha: { value: 0.75 }
9886
- },
9887
- vertexShader: `
9888
- uniform vec2 glintOffset;
9889
- #ifndef ENTITY_GLINT
9890
- attribute vec4 uvBounds;
9891
- attribute vec2 glintUv;
9892
- flat out vec4 vUvBounds;
9893
- centroid out vec2 vUv;
9894
- #endif
9895
- varying vec2 vGlintUv;
9896
- void main() {
9897
- #ifdef ENTITY_GLINT
9898
- vec2 p = vec2(uv.x, 1.0 - uv.y) * 0.5;
9899
- #else
9900
- vUv = uv;
9901
- vUvBounds = uvBounds;
9902
- vec2 p = glintUv * 8.0;
9903
- #endif
9904
- float angle = radians(10.0);
9905
- vGlintUv = vec2(cos(angle) * p.x - sin(angle) * p.y, sin(angle) * p.x + cos(angle) * p.y) + glintOffset;
9906
- vec4 mvPosition = vec4(position, 1.0);
9907
- mvPosition = modelViewMatrix * mvPosition;
9908
- gl_Position = projectionMatrix * mvPosition;
9909
- }
9910
- `,
9911
- fragmentShader: `
9912
- #ifndef ENTITY_GLINT
9913
- uniform sampler2D baseMap;
9914
- flat in vec4 vUvBounds;
9915
- centroid in vec2 vUv;
9916
- #endif
9917
- uniform sampler2D glintMap;
9918
- uniform float glintAlpha;
9919
- varying vec2 vGlintUv;
9920
- void main() {
9921
- #ifndef ENTITY_GLINT
9922
- vec2 mapUv = clamp(vUv, vUvBounds.xy, vUvBounds.zw);
9923
- if (texture2D(baseMap, mapUv).a < 0.01) discard;
9924
- #endif
9925
- vec4 color = texture2D(glintMap, vec2(vGlintUv.x, 1.0 - vGlintUv.y));
9926
- if (color.a < 0.1) discard;
9927
- gl_FragColor = color;
9928
- #include <colorspace_fragment>
9929
- gl_FragColor.rgb *= glintAlpha;
9930
- }
9931
- `
9932
- });
9933
- if (!entityMeshes) Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
9934
- const targets = entityMeshes ? [...entityMeshes] : [];
9935
- if (!entityMeshes) owner.iterateAllMeshes((mesh) => targets.push({ mesh }));
9936
- for (const { mesh: base, parent = base } of targets) {
9937
- const pass = new import_three26.Mesh(base.geometry, material);
9938
- pass.name = `${base.name}:glint`;
9939
- pass.userData.minerenderItemGlint = true;
9940
- pass.raycast = () => {
9941
- };
9942
- pass.renderOrder = parent.renderOrder + 0.5;
9943
- parent.add(pass);
9944
- }
9945
- return new _ItemGlint(owner, material);
9946
- }
9947
- /**
9948
- * Maps glint before model centering and display transforms. Ordinary sprites approximate Minecraft's shared atlas density.
9949
- * @internal
9950
- */
9951
- static mapUvs(geometry, faces, atlas, itemId, display) {
9952
- const uv = geometry.getAttribute("uv");
9953
- const mapped = new Float32Array(uv.count * 2);
9954
- if (itemId === "minecraft:compass" || itemId === "minecraft:recovery_compass" || itemId === "minecraft:clock") {
9955
- const positions = geometry.getAttribute("position"), normals = geometry.getAttribute("normal");
9956
- const scale = display === "gui" /* GUI */ ? 0.5 : display === "firstperson_lefthand" /* FIRSTPERSON_LEFTHAND */ || display === "firstperson_righthand" /* FIRSTPERSON_RIGHTHAND */ ? 0.75 : 1;
9957
- const factor = 1 / (16 * 128 * scale);
9958
- for (let index = 0; index < positions.count; index++) {
9959
- const x = positions.getX(index), y = positions.getY(index), z = positions.getZ(index);
9960
- const nx = normals.getX(index), ny = normals.getY(index), nz = normals.getZ(index);
9961
- const ax = Math.abs(nx), ay = Math.abs(ny), az = Math.abs(nz);
9962
- let u, v;
9963
- if (ay >= az && ay >= ax) {
9964
- u = x;
9965
- v = ny < 0 ? -z : z;
9966
- } else if (az >= ax) {
9967
- u = nz < 0 ? -x : x;
9968
- v = -y;
9969
- } else {
9970
- u = nx < 0 ? -z : z;
9971
- v = -y;
9972
- }
9973
- mapped[index * 2] = u * factor;
9974
- mapped[index * 2 + 1] = v * factor;
9975
- }
9976
- geometry.setAttribute("glintUv", new import_three26.Float32BufferAttribute(mapped, 2));
9977
- return;
9978
- }
9979
- for (const [faceIndex, face] of CUBE_FACES.entries()) {
9980
- const texture = faces[face]?.texture?.substring(1);
9981
- const position = texture && atlas.positions[texture] || [0, 0];
9982
- const size = texture && atlas.sizes[texture] || [atlas.image.width, atlas.image.height];
9983
- for (let corner = 0; corner < 4; corner++) {
9984
- const index = faceIndex * 4 + corner;
9985
- mapped[index * 2] = (uv.getX(index) * atlas.image.width - position[0]) / size[0] / 32;
9986
- mapped[index * 2 + 1] = ((1 - uv.getY(index)) * atlas.image.height - position[1]) / size[1] / 32;
10200
+ };
10201
+ const id = Object.prototype.hasOwnProperty.call(models, special.kind) ? models[special.kind] : void 0;
10202
+ if (!id) throw new Error(`Unsupported special item head kind ${special.kind}`);
10203
+ const animation = (special.animation ?? 0) * Math.PI * 0.2;
10204
+ const rotations = { head: [0, Math.PI, 0] };
10205
+ if (special.kind === "dragon") rotations.jaw = [(Math.sin(animation) + 1) * 0.2, 0, 0];
10206
+ if (special.kind === "piglin") {
10207
+ rotations.left_ear = [0, 0, -(Math.cos(animation * 1.2) + 2.5) * 0.2];
10208
+ rotations.right_ear = [0, 0, (Math.cos(animation) + 2.5) * 0.2];
10209
+ }
10210
+ return [await load(
10211
+ id,
10212
+ special.texture ? texture(special.texture, "") : void 0,
10213
+ new import_three26.Matrix4().makeTranslation(8, 0, 8).multiply(new import_three26.Matrix4().makeScale(-1, -1, 1)),
10214
+ rotations
10215
+ )];
9987
10216
  }
9988
10217
  }
9989
- geometry.setAttribute("glintUv", new import_three26.Float32BufferAttribute(mapped, 2));
9990
- }
9991
- updateTime() {
9992
- const time = Math.trunc(Date.now() * 4);
9993
- this.material.uniforms.glintOffset.value.set(-(time % 11e4) / 11e4, time % 3e4 / 3e4);
9994
10218
  }
9995
- /** @internal */
9996
- updateSubscription(active) {
9997
- if (active && this.ticker === void 0) {
9998
- this.updateTime();
9999
- this.ticker = Ticker.add(() => {
10000
- this.updateTime();
10001
- this.owner.notifyDirty();
10002
- });
10003
- } else if (!active) {
10004
- Ticker.remove(this.ticker);
10005
- this.ticker = void 0;
10219
+ /** An owned material with model-preview shading; its entity texture remains shared. */
10220
+ static createMaterial(source, shade) {
10221
+ const material = source.clone();
10222
+ if (shade) {
10223
+ material.onBeforeCompile = (shader) => {
10224
+ shader.vertexShader = `varying float itemLight;
10225
+ ${shader.vertexShader}`.replace("#include <begin_vertex>", `
10226
+ #include <begin_vertex>
10227
+ vec3 N = inverseTransformDirection(normalMatrix * normal, viewMatrix);
10228
+ itemLight = (1.0 + N.y) * 0.25 - N.x * N.x * 0.15 + N.z * N.z * 0.05 + 0.5;
10229
+ `);
10230
+ shader.fragmentShader = `varying float itemLight;
10231
+ ${shader.fragmentShader}`.replace(
10232
+ "#include <color_fragment>",
10233
+ "#include <color_fragment>\ndiffuseColor.rgb *= itemLight;"
10234
+ );
10235
+ };
10236
+ material.customProgramCacheKey = () => "special-item-side";
10006
10237
  }
10007
- }
10008
- dispose() {
10009
- Ticker.remove(this.ticker);
10010
- this.ticker = void 0;
10011
- this.material.dispose();
10238
+ return material;
10012
10239
  }
10013
10240
  };
10014
10241
  }
10015
10242
  });
10016
10243
 
10244
+ // src/model/GuiLight.ts
10245
+ var GuiLight;
10246
+ var init_GuiLight = __esm({
10247
+ "src/model/GuiLight.ts"() {
10248
+ GuiLight = /* @__PURE__ */ ((GuiLight2) => {
10249
+ GuiLight2["FRONT"] = "front";
10250
+ GuiLight2["SIDE"] = "side";
10251
+ return GuiLight2;
10252
+ })(GuiLight || {});
10253
+ }
10254
+ });
10255
+
10017
10256
  // src/model/scene/ModelObject.ts
10018
10257
  var ModelObject_exports = {};
10019
10258
  __export(ModelObject_exports, {
@@ -11159,17 +11398,25 @@ var init_BlockObject = __esm({
11159
11398
  if (!key || this.options.displayPosition) return false;
11160
11399
  const generation = this._entityGeneration;
11161
11400
  const created = [];
11401
+ const placement = new import_three34.Matrix4();
11162
11402
  let resolved;
11163
11403
  try {
11164
11404
  resolved = BlockEntities.resolve(await BlockEntities.getIndex(key.root), key.toNamespacedString(), this.state);
11165
11405
  if (!resolved) return false;
11406
+ BlockEntities.matrix(resolved, placement);
11166
11407
  for (const part of resolved.parts) {
11167
11408
  const [namespace, path] = part.model.includes(":") ? part.model.split(":") : [key.namespace, part.model];
11168
- const texture = part.textureLocation === void 0 ? void 0 : AssetKey.parse("textures", part.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
11409
+ let texture = part.textureLocation === void 0 ? void 0 : AssetKey.parse("textures", part.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
11410
+ if (part.textureLocation === void 0 && namespace === "minecraft" && path === "player_head") {
11411
+ const { PlayerHeadTextures: PlayerHeadTextures2 } = await Promise.resolve().then(() => (init_PlayerHeadTextures(), PlayerHeadTextures_exports));
11412
+ texture = (await PlayerHeadTextures2.get(void 0, key.root)).texture;
11413
+ }
11169
11414
  if (texture) texture.root = key.root;
11170
11415
  const entity = await Entities.getEntity(new BasicAssetKey(namespace, path), texture, { layer: part.layer });
11171
11416
  if (!entity) throw new MineRenderError(`Missing entity model ${part.model}`);
11172
- created.push(await this.scene.addEntity(entity, { wireframe: this.options.wireframe }));
11417
+ const object4 = await this.scene.addEntity(entity, { wireframe: this.options.wireframe });
11418
+ this.placeEntity(object4, placement);
11419
+ created.push(object4);
11173
11420
  }
11174
11421
  } catch (error) {
11175
11422
  log15.warn(`Could not draw block entity ${key.toNamespacedString()}, using its block model`, error);
@@ -11180,18 +11427,21 @@ var init_BlockObject = __esm({
11180
11427
  this.removeEntities(created);
11181
11428
  return true;
11182
11429
  }
11183
- BlockEntities.matrix(resolved, this._entityPlacement);
11430
+ this._entityPlacement.copy(placement);
11184
11431
  this._entities = created;
11185
11432
  this.placeEntities();
11186
11433
  this.setVisible(this.visible);
11187
11434
  return true;
11188
11435
  }
11189
11436
  placeEntities() {
11190
- const matrix = new import_three34.Matrix4().makeTranslation(this.position.x - 8, this.position.y - 8, this.position.z - 8).multiply(this._entityPlacement);
11191
11437
  for (const entity of this._entities) {
11192
- matrix.decompose(entity.position, entity.quaternion, entity.scale);
11438
+ this.placeEntity(entity, this._entityPlacement);
11193
11439
  }
11194
11440
  }
11441
+ placeEntity(entity, placement) {
11442
+ const matrix = new import_three34.Matrix4().makeTranslation(this.position.x - 8, this.position.y - 8, this.position.z - 8).multiply(placement);
11443
+ matrix.decompose(entity.position, entity.quaternion, entity.scale);
11444
+ }
11195
11445
  get isOccluding() {
11196
11446
  if (!this.visible) {
11197
11447
  return false;
@@ -11481,6 +11731,7 @@ __export(index_node_exports, {
11481
11731
  AnvilParser: () => AnvilParser,
11482
11732
  AnvilWorldSource: () => AnvilWorldSource,
11483
11733
  ArchiveAssetSource: () => ArchiveAssetSource,
11734
+ Armor: () => Armor,
11484
11735
  AssetKey: () => AssetKey,
11485
11736
  AssetLoadError: () => AssetLoadError,
11486
11737
  AssetLoader: () => AssetLoader,
@@ -11564,6 +11815,7 @@ __export(index_node_exports, {
11564
11815
  OrbitControls: () => OrbitControls,
11565
11816
  PackFormats: () => PackFormats,
11566
11817
  PackMetadata: () => PackMetadata,
11818
+ PalettedTextures: () => PalettedTextures,
11567
11819
  PersistentCache: () => PersistentCache,
11568
11820
  PlayerHeadTextures: () => PlayerHeadTextures,
11569
11821
  Renames: () => Renames,
@@ -11706,6 +11958,7 @@ init_PersistentCache();
11706
11958
  var NodeCache = class extends PersistentCache {
11707
11959
  constructor(backing) {
11708
11960
  super(backing);
11961
+ this.pendingWrites = /* @__PURE__ */ new Map();
11709
11962
  }
11710
11963
  ready() {
11711
11964
  return this.initialized ??= this.backing.init();
@@ -11716,7 +11969,14 @@ var NodeCache = class extends PersistentCache {
11716
11969
  }
11717
11970
  async put(key, value) {
11718
11971
  await this.ready();
11719
- return this.backing.setItem(key, value).then((res) => res.content);
11972
+ const previous = this.pendingWrites.get(key);
11973
+ const write = (previous ? previous.catch(() => void 0) : Promise.resolve()).then(() => this.backing.setItem(key, value)).then(() => value);
11974
+ this.pendingWrites.set(key, write);
11975
+ try {
11976
+ return await write;
11977
+ } finally {
11978
+ if (this.pendingWrites.get(key) === write) this.pendingWrites.delete(key);
11979
+ }
11720
11980
  }
11721
11981
  async delete(key) {
11722
11982
  await this.ready();
@@ -11762,7 +12022,10 @@ var NodeEnv = class {
11762
12022
  openCache(name, version2) {
11763
12023
  return new NodeCache(import_node_persist.default.create({
11764
12024
  dir: name + version2,
11765
- encoding: "utf8"
12025
+ encoding: "utf8",
12026
+ // node-persist's unref'd one-second write queue delays loads and lets Node exit before writes finish.
12027
+ // NodeCache serializes writes to each key while allowing different files to write concurrently.
12028
+ writeQueue: false
11766
12029
  }));
11767
12030
  }
11768
12031
  };
@@ -11839,6 +12102,9 @@ function shutdown() {
11839
12102
  Caching.end();
11840
12103
  }
11841
12104
 
12105
+ // src/armor/index.ts
12106
+ init_Armor();
12107
+
11842
12108
  // src/assets/index.ts
11843
12109
  init_AssetDefaults();
11844
12110
  init_AssetKey();
@@ -11930,10 +12196,10 @@ init_EntityObject();
11930
12196
  init_MineRenderError();
11931
12197
 
11932
12198
  // src/export/SceneExporter.ts
11933
- var import_three19 = require("three");
12199
+ var import_three20 = require("three");
11934
12200
 
11935
12201
  // ../../node_modules/three/examples/jsm/exporters/OBJExporter.js
11936
- var import_three16 = require("three");
12202
+ var import_three17 = require("three");
11937
12203
  var OBJExporter = class {
11938
12204
  /**
11939
12205
  * Parses the given 3D object and generates the OBJ output.
@@ -11948,17 +12214,17 @@ var OBJExporter = class {
11948
12214
  let indexVertex = 0;
11949
12215
  let indexVertexUvs = 0;
11950
12216
  let indexNormals = 0;
11951
- const vertex = new import_three16.Vector3();
11952
- const color2 = new import_three16.Color();
11953
- const normal = new import_three16.Vector3();
11954
- const uv = new import_three16.Vector2();
12217
+ const vertex = new import_three17.Vector3();
12218
+ const color2 = new import_three17.Color();
12219
+ const normal = new import_three17.Vector3();
12220
+ const uv = new import_three17.Vector2();
11955
12221
  const face = [];
11956
12222
  function parseMesh(mesh) {
11957
12223
  let nbVertex = 0;
11958
12224
  let nbNormals = 0;
11959
12225
  let nbVertexUvs = 0;
11960
12226
  const geometry = mesh.geometry;
11961
- const normalMatrixWorld = new import_three16.Matrix3();
12227
+ const normalMatrixWorld = new import_three17.Matrix3();
11962
12228
  const vertices = geometry.getAttribute("position");
11963
12229
  const normals = geometry.getAttribute("normal");
11964
12230
  const uvs = geometry.getAttribute("uv");
@@ -12049,7 +12315,7 @@ var OBJExporter = class {
12049
12315
  output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
12050
12316
  if (colors2 !== void 0) {
12051
12317
  color2.fromBufferAttribute(colors2, i);
12052
- import_three16.ColorManagement.workingToColorSpace(color2, import_three16.SRGBColorSpace);
12318
+ import_three17.ColorManagement.workingToColorSpace(color2, import_three17.SRGBColorSpace);
12053
12319
  output += " " + color2.r + " " + color2.g + " " + color2.b;
12054
12320
  }
12055
12321
  output += "\n";
@@ -12078,7 +12344,7 @@ var OBJExporter = class {
12078
12344
  };
12079
12345
 
12080
12346
  // ../../node_modules/three/examples/jsm/exporters/PLYExporter.js
12081
- var import_three17 = require("three");
12347
+ var import_three18 = require("three");
12082
12348
  var PLYExporter = class {
12083
12349
  /**
12084
12350
  * Parses the given 3D object and generates the PLY output.
@@ -12179,7 +12445,7 @@ var PLYExporter = class {
12179
12445
  includeIndices = false;
12180
12446
  }
12181
12447
  });
12182
- const tempColor = new import_three17.Color();
12448
+ const tempColor = new import_three18.Color();
12183
12449
  includeIndices = includeIndices && excludeAttributes.indexOf("index") === -1;
12184
12450
  includeNormals = includeNormals && excludeAttributes.indexOf("normal") === -1;
12185
12451
  includeColors = includeColors && excludeAttributes.indexOf("color") === -1;
@@ -12228,8 +12494,8 @@ property list uchar int vertex_index
12228
12494
  `;
12229
12495
  }
12230
12496
  header += "end_header\n";
12231
- const vertex = new import_three17.Vector3();
12232
- const normalMatrixWorld = new import_three17.Matrix3();
12497
+ const vertex = new import_three18.Vector3();
12498
+ const normalMatrixWorld = new import_three18.Matrix3();
12233
12499
  let result = null;
12234
12500
  if (options.binary === true) {
12235
12501
  const headerBin = new TextEncoder().encode(header);
@@ -12307,7 +12573,7 @@ property list uchar int vertex_index
12307
12573
  if (includeColors === true) {
12308
12574
  if (colors2 != null) {
12309
12575
  tempColor.fromBufferAttribute(colors2, i);
12310
- import_three17.ColorManagement.workingToColorSpace(tempColor, import_three17.SRGBColorSpace);
12576
+ import_three18.ColorManagement.workingToColorSpace(tempColor, import_three18.SRGBColorSpace);
12311
12577
  const r = colorIsFloat ? tempColor.r : Math.round(tempColor.r * colorScale);
12312
12578
  const g = colorIsFloat ? tempColor.g : Math.round(tempColor.g * colorScale);
12313
12579
  const b = colorIsFloat ? tempColor.b : Math.round(tempColor.b * colorScale);
@@ -12409,7 +12675,7 @@ property list uchar int vertex_index
12409
12675
  if (includeColors === true) {
12410
12676
  if (colors2 != null) {
12411
12677
  tempColor.fromBufferAttribute(colors2, i);
12412
- import_three17.ColorManagement.workingToColorSpace(tempColor, import_three17.SRGBColorSpace);
12678
+ import_three18.ColorManagement.workingToColorSpace(tempColor, import_three18.SRGBColorSpace);
12413
12679
  const r = colorIsFloat ? tempColor.r : Math.round(tempColor.r * colorScale);
12414
12680
  const g = colorIsFloat ? tempColor.g : Math.round(tempColor.g * colorScale);
12415
12681
  const b = colorIsFloat ? tempColor.b : Math.round(tempColor.b * colorScale);
@@ -12561,10 +12827,10 @@ var SceneExporter = class {
12561
12827
  }
12562
12828
  };
12563
12829
  function localMatrix(object4) {
12564
- return object4.matrixAutoUpdate ? new import_three19.Matrix4().compose(object4.position, object4.quaternion, object4.scale) : object4.matrix.clone();
12830
+ return object4.matrixAutoUpdate ? new import_three20.Matrix4().compose(object4.position, object4.quaternion, object4.scale) : object4.matrix.clone();
12565
12831
  }
12566
12832
  function createSnapshot(root, gltf, bakeColors = false) {
12567
- const scene = new import_three19.Scene();
12833
+ const scene = new import_three20.Scene();
12568
12834
  const geometries = [];
12569
12835
  const materials = [];
12570
12836
  const textures = [];
@@ -12582,7 +12848,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
12582
12848
  if (!uniforms.map?.value?.isTexture || !uniforms.SHADE || !uniforms.BRIGHTNESS || !uniforms.EMISSIVE) {
12583
12849
  throw new MineRenderError("glTF export does not support this custom ShaderMaterial");
12584
12850
  }
12585
- material = new import_three19.MeshBasicMaterial({
12851
+ material = new import_three20.MeshBasicMaterial({
12586
12852
  map: uniforms.map.value,
12587
12853
  vertexColors: source.vertexColors,
12588
12854
  transparent: source.transparent,
@@ -12592,7 +12858,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
12592
12858
  });
12593
12859
  material.name = source.name;
12594
12860
  } else if (shader.isShaderMaterial) {
12595
- material = new import_three19.MeshBasicMaterial({ vertexColors: source.vertexColors });
12861
+ material = new import_three20.MeshBasicMaterial({ vertexColors: source.vertexColors });
12596
12862
  material.name = source.name;
12597
12863
  } else {
12598
12864
  const copySource = Object.create(source);
@@ -12646,11 +12912,11 @@ function createSnapshot(root, gltf, bakeColors = false) {
12646
12912
  const material = copyMaterial(sourceMaterial, geometry);
12647
12913
  if (!sourceMaterial.vertexColors) geometry.deleteAttribute("color");
12648
12914
  if (bakeColors || instanceColor) {
12649
- const tint = (material.color ?? new import_three19.Color()).clone();
12915
+ const tint = (material.color ?? new import_three20.Color()).clone();
12650
12916
  if (instanceColor) tint.multiply(instanceColor);
12651
12917
  if (bakeColors) {
12652
12918
  const original = geometry.getAttribute("color");
12653
- const colors2 = new import_three19.Float32BufferAttribute(new Float32Array(geometry.getAttribute("position").count * 3), 3);
12919
+ const colors2 = new import_three20.Float32BufferAttribute(new Float32Array(geometry.getAttribute("position").count * 3), 3);
12654
12920
  for (let i = 0; i < colors2.count; i++) {
12655
12921
  colors2.setXYZ(i, (original?.getX(i) ?? 1) * tint.r, (original?.getY(i) ?? 1) * tint.g, (original?.getZ(i) ?? 1) * tint.b);
12656
12922
  }
@@ -12659,7 +12925,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
12659
12925
  material.color.copy(tint);
12660
12926
  }
12661
12927
  }
12662
- const copy = new import_three19.Mesh(geometry, material);
12928
+ const copy = new import_three20.Mesh(geometry, material);
12663
12929
  copy.name = mesh.name;
12664
12930
  copy.renderOrder = mesh.renderOrder;
12665
12931
  scene.add(copy);
@@ -12673,9 +12939,9 @@ function createSnapshot(root, gltf, bakeColors = false) {
12673
12939
  const instanced = mesh;
12674
12940
  if (instanced.isInstancedMesh) {
12675
12941
  for (let i = 0; i < instanced.count; i++) {
12676
- const matrix = new import_three19.Matrix4();
12942
+ const matrix = new import_three20.Matrix4();
12677
12943
  instanced.getMatrixAt(i, matrix);
12678
- const color2 = instanced.instanceColor ? new import_three19.Color().fromBufferAttribute(instanced.instanceColor, i) : void 0;
12944
+ const color2 = instanced.instanceColor ? new import_three20.Color().fromBufferAttribute(instanced.instanceColor, i) : void 0;
12679
12945
  addMesh(mesh, world.clone().multiply(matrix), color2);
12680
12946
  }
12681
12947
  } else {
@@ -12687,7 +12953,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
12687
12953
  try {
12688
12954
  const ancestors = [];
12689
12955
  for (let parent = root.parent; parent; parent = parent.parent) ancestors.unshift(parent);
12690
- const world = new import_three19.Matrix4();
12956
+ const world = new import_three20.Matrix4();
12691
12957
  for (const ancestor of ancestors) {
12692
12958
  if (!ancestor.visible) return { scene, dispose };
12693
12959
  world.multiply(localMatrix(ancestor));
@@ -12701,7 +12967,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
12701
12967
  }
12702
12968
  function selectAlphaMode(material, geometry) {
12703
12969
  const texture = material.map;
12704
- if (material.type !== "MeshBasicMaterial" || !material.transparent || !material.depthWrite || material.opacity !== 1 || material.alphaMap || !texture?.isDataTexture || texture.format !== import_three19.RGBAFormat || texture.type !== import_three19.UnsignedByteType || texture.wrapS !== import_three19.ClampToEdgeWrapping || texture.wrapT !== import_three19.ClampToEdgeWrapping || texture.magFilter !== import_three19.NearestFilter || texture.minFilter !== import_three19.NearestFilter || texture.anisotropy > 1 || texture.offset.x !== 0 || texture.offset.y !== 0 || texture.repeat.x !== 1 || texture.repeat.y !== 1 || texture.rotation !== 0 || !texture.matrixAutoUpdate && !texture.matrix.elements.every((value, i) => value === (i % 4 === 0 ? 1 : 0))) return;
12970
+ if (material.type !== "MeshBasicMaterial" || !material.transparent || !material.depthWrite || material.opacity !== 1 || material.alphaMap || !texture?.isDataTexture || texture.format !== import_three20.RGBAFormat || texture.type !== import_three20.UnsignedByteType || texture.wrapS !== import_three20.ClampToEdgeWrapping || texture.wrapT !== import_three20.ClampToEdgeWrapping || texture.magFilter !== import_three20.NearestFilter || texture.minFilter !== import_three20.NearestFilter || texture.anisotropy > 1 || texture.offset.x !== 0 || texture.offset.y !== 0 || texture.repeat.x !== 1 || texture.repeat.y !== 1 || texture.rotation !== 0 || !texture.matrixAutoUpdate && !texture.matrix.elements.every((value, i) => value === (i % 4 === 0 ? 1 : 0))) return;
12705
12971
  const uv = geometry.getAttribute(texture.channel === 0 ? "uv" : `uv${texture.channel}`);
12706
12972
  const colors2 = geometry.getAttribute("color");
12707
12973
  if (!uv || material.vertexColors && colors2?.itemSize === 4) return;
@@ -12738,7 +13004,7 @@ function selectAlphaMode(material, geometry) {
12738
13004
  }
12739
13005
  function copyDataTexture(source) {
12740
13006
  const image = source.image;
12741
- if (!image.data || source.format !== import_three19.RGBAFormat || source.type !== import_three19.UnsignedByteType) {
13007
+ if (!image.data || source.format !== import_three20.RGBAFormat || source.type !== import_three20.UnsignedByteType) {
12742
13008
  throw new MineRenderError("glTF export requires RGBA unsigned-byte data textures");
12743
13009
  }
12744
13010
  const canvas = createCanvas2(image.width, image.height);
@@ -12749,8 +13015,8 @@ function copyDataTexture(source) {
12749
13015
  context.putImageData(pixels, 0, 0);
12750
13016
  const copySource = Object.create(source);
12751
13017
  copySource.userData = {};
12752
- copySource.source = new import_three19.TextureSource(canvas);
12753
- return new import_three19.Texture().copy(copySource);
13018
+ copySource.source = new import_three20.TextureSource(canvas);
13019
+ return new import_three20.Texture().copy(copySource);
12754
13020
  }
12755
13021
 
12756
13022
  // src/export/VideoExporter.ts
@@ -14360,12 +14626,13 @@ function isFlyControls(obj) {
14360
14626
  }
14361
14627
 
14362
14628
  // src/renderer/MineRenderScene.ts
14363
- var import_three36 = require("three");
14629
+ var import_three37 = require("three");
14364
14630
  init_SceneObject();
14365
14631
  init_merge();
14366
14632
 
14367
14633
  // src/skin/scene/SkinObject.ts
14368
14634
  init_SceneObject();
14635
+ var import_three36 = require("three");
14369
14636
 
14370
14637
  // src/skin/SkinPart.ts
14371
14638
  var SkinPart = /* @__PURE__ */ ((SkinPart2) => {
@@ -14382,11 +14649,21 @@ var SkinPart = /* @__PURE__ */ ((SkinPart2) => {
14382
14649
  SkinPart2["RIGHT_TROUSERS"] = "rightTrousers";
14383
14650
  SkinPart2["LEFT_TROUSERS"] = "leftTrousers";
14384
14651
  SkinPart2["CAPE"] = "cape";
14652
+ SkinPart2["LEFT_EAR"] = "leftEar";
14653
+ SkinPart2["RIGHT_EAR"] = "rightEar";
14385
14654
  return SkinPart2;
14386
14655
  })(SkinPart || {});
14387
14656
  var SKIN_PARTS = Object.values(SkinPart);
14388
14657
 
14389
14658
  // src/skin/SkinTextureCoordinates.ts
14659
+ var earCoordinates = {
14660
+ west: { uv: [24, 1, 25, 7] },
14661
+ east: { uv: [31, 1, 32, 7] },
14662
+ south: { uv: [25, 1, 31, 7] },
14663
+ north: { uv: [32, 1, 38, 7] },
14664
+ up: { uv: [25, 0, 31, 1] },
14665
+ down: { uv: [31, 1, 37, 0] }
14666
+ };
14390
14667
  var baseSkinCoordinates = {
14391
14668
  head: {
14392
14669
  west: {
@@ -15007,7 +15284,9 @@ var baseSkinCoordinates = {
15007
15284
  south: { uv: [12, 1, 22, 17] },
15008
15285
  up: { uv: [11, 1, 1, 0] },
15009
15286
  down: { uv: [21, 0, 11, 1] }
15010
- }
15287
+ },
15288
+ leftEar: earCoordinates,
15289
+ rightEar: earCoordinates
15011
15290
  };
15012
15291
  var classicSkinTextureCoordinates = {
15013
15292
  ...baseSkinCoordinates
@@ -15058,6 +15337,7 @@ var box4_12_4outer = [4.5, 12.5, 4.5];
15058
15337
  var box3_12_4inner = [3, 12, 4];
15059
15338
  var box3_12_4outer = [3.5, 12.5, 4.5];
15060
15339
  var box10_16_1 = [10, 16, 1];
15340
+ var box8_8_3ear = [8, 8, 3];
15061
15341
  var baseSkinGeometries = {
15062
15342
  head: box8inner,
15063
15343
  hat: box8outer,
@@ -15071,7 +15351,9 @@ var baseSkinGeometries = {
15071
15351
  leftTrousers: box4_12_4outer,
15072
15352
  rightLeg: box4_12_4inner,
15073
15353
  rightTrousers: box4_12_4outer,
15074
- cape: box10_16_1
15354
+ cape: box10_16_1,
15355
+ leftEar: box8_8_3ear,
15356
+ rightEar: box8_8_3ear
15075
15357
  };
15076
15358
  var classicSkinGeometries = { ...{}, ...baseSkinGeometries };
15077
15359
  var slimSkinGeometries = {
@@ -15100,6 +15382,12 @@ var capeTextureSizes = {
15100
15382
  };
15101
15383
 
15102
15384
  // src/skin/scene/SkinObject.ts
15385
+ var ArmorClass;
15386
+ var EntityObjectClass;
15387
+ function hasCubes(part) {
15388
+ return part.cubes.length > 0 || Object.values(part.children).some(hasCubes);
15389
+ }
15390
+ var UPSIDE_DOWN_OFFSET = (1.8 + 0.1) * 16 / 0.9375;
15103
15391
  var SkinObject = class extends SceneObject {
15104
15392
  constructor(options) {
15105
15393
  super();
@@ -15107,27 +15395,42 @@ var SkinObject = class extends SceneObject {
15107
15395
  this.detectedSlim = false;
15108
15396
  this.skinLoad = 0;
15109
15397
  this.capeLoad = 0;
15398
+ this.armorLoad = 0;
15399
+ this.armorObjects = [];
15110
15400
  this.options = (0, import_ts_deepmerge.default)({}, SceneObject.DEFAULT_OPTIONS, options ?? {});
15111
15401
  this.slim = this.options.slim ?? false;
15112
15402
  }
15113
15403
  async init() {
15114
15404
  const cape = this.getGroupByName("cape" /* CAPE */);
15405
+ const capeOnBody = cape?.parent === this.getGroupByName("body" /* BODY */);
15115
15406
  this.createMeshes();
15116
- if (cape) this.getGroupByName("body" /* BODY */).attach(cape);
15407
+ if (cape) {
15408
+ const body = this.getGroupByName("body" /* BODY */);
15409
+ if (capeOnBody) body.add(cape);
15410
+ else body.attach(cape);
15411
+ }
15412
+ for (const { part, object: object4 } of this.armorObjects) this.getGroupByName(part).add(object4);
15413
+ this.updateEars();
15414
+ this.updateUpsideDown();
15117
15415
  }
15118
15416
  removeMeshes() {
15119
15417
  }
15120
15418
  createMeshes() {
15121
15419
  const mat = this.skinMaterial ?? Materials.MISSING_TEXTURE;
15420
+ this.model?.removeFromParent();
15421
+ const model = this.model = new import_three36.Object3D();
15422
+ this.add(model);
15122
15423
  {
15123
- const headGroup = this.createAndAddGroup("head", 0, 28, 0, "y" /* Y */, -4);
15424
+ const headGroup = this.createGroup("head", 0, 28, 0, "y" /* Y */, -4);
15425
+ model.add(headGroup);
15124
15426
  const headGeo = this.getBoxGeometry("head" /* HEAD */);
15125
15427
  const head = this.createAndAddMesh("head", headGroup, headGeo, mat, "y" /* Y */, 4);
15126
15428
  const hatGeo = this.getBoxGeometry("hat" /* HAT */);
15127
15429
  const hat = this.createAndAddMesh("hat", headGroup, hatGeo, mat, "y" /* Y */, 4);
15128
15430
  }
15129
15431
  {
15130
- const bodyGroup = this.createAndAddGroup("body", 0, 24, 0);
15432
+ const bodyGroup = this.createGroup("body", 0, 24, 0);
15433
+ model.add(bodyGroup);
15131
15434
  const bodyGeo = this.getBoxGeometry("body" /* BODY */);
15132
15435
  const body = this.createAndAddMesh("body", bodyGroup, bodyGeo, mat, "y" /* Y */, -6);
15133
15436
  const jacketGeo = this.getBoxGeometry("jacket" /* JACKET */);
@@ -15135,7 +15438,8 @@ var SkinObject = class extends SceneObject {
15135
15438
  }
15136
15439
  {
15137
15440
  {
15138
- const leftArmGroup = this.createAndAddGroup("leftArm", 5, 22, 0);
15441
+ const leftArmGroup = this.createGroup("leftArm", 5, 22, 0);
15442
+ model.add(leftArmGroup);
15139
15443
  const leftArmGeo = this.getBoxGeometry("leftArm" /* LEFT_ARM */);
15140
15444
  const leftArm = this.createAndAddMesh("leftArm", leftArmGroup, leftArmGeo, mat, "y" /* Y */, -4);
15141
15445
  leftArm.position.x = this.slim ? 0.5 : 1;
@@ -15144,7 +15448,8 @@ var SkinObject = class extends SceneObject {
15144
15448
  leftSleeve.position.x = this.slim ? 0.5 : 1;
15145
15449
  }
15146
15450
  {
15147
- const rightArmGroup = this.createAndAddGroup("rightArm", -5, 22, 0);
15451
+ const rightArmGroup = this.createGroup("rightArm", -5, 22, 0);
15452
+ model.add(rightArmGroup);
15148
15453
  const rightArmGeo = this.getBoxGeometry("rightArm" /* RIGHT_ARM */);
15149
15454
  const rightArm = this.createAndAddMesh("rightArm", rightArmGroup, rightArmGeo, mat, "y" /* Y */, -4);
15150
15455
  rightArm.position.x = this.slim ? -0.5 : -1;
@@ -15155,14 +15460,16 @@ var SkinObject = class extends SceneObject {
15155
15460
  }
15156
15461
  {
15157
15462
  {
15158
- const leftLegGroup = this.createAndAddGroup("leftLeg", 1.9, 12, 0);
15463
+ const leftLegGroup = this.createGroup("leftLeg", 1.9, 12, 0);
15464
+ model.add(leftLegGroup);
15159
15465
  const leftLegGeo = this.getBoxGeometry("leftLeg" /* LEFT_LEG */);
15160
15466
  const leftLeg = this.createAndAddMesh("leftLeg", leftLegGroup, leftLegGeo, mat, "y" /* Y */, -6);
15161
15467
  const leftTrousersGeo = this.getBoxGeometry("leftTrousers" /* LEFT_TROUSERS */);
15162
15468
  const leftTrousers = this.createAndAddMesh("leftTrousers", leftLegGroup, leftTrousersGeo, mat, "y" /* Y */, -6);
15163
15469
  }
15164
15470
  {
15165
- const rightLegGroup = this.createAndAddGroup("rightLeg", -1.9, 12, 0);
15471
+ const rightLegGroup = this.createGroup("rightLeg", -1.9, 12, 0);
15472
+ model.add(rightLegGroup);
15166
15473
  const rightLegGeo = this.getBoxGeometry("rightLeg" /* RIGHT_LEG */);
15167
15474
  const rightLeg = this.createAndAddMesh("rightLeg", rightLegGroup, rightLegGeo, mat, "y" /* Y */, -6);
15168
15475
  const rightTrousersGeo = this.getBoxGeometry("rightTrousers" /* RIGHT_TROUSERS */);
@@ -15229,6 +15536,54 @@ var SkinObject = class extends SceneObject {
15229
15536
  }
15230
15537
  this.notifyDirty();
15231
15538
  }
15539
+ /**
15540
+ * Loads armor from 1.21.4+ equipment assets, or pass undefined to remove it.
15541
+ * Each piece moves with the skin part it covers.
15542
+ */
15543
+ async setArmor(armor) {
15544
+ const load = ++this.armorLoad;
15545
+ const objects = [];
15546
+ if (armor) {
15547
+ const Armor2 = ArmorClass ??= (await Promise.resolve().then(() => (init_Armor(), Armor_exports))).Armor;
15548
+ const EntityObject2 = EntityObjectClass ??= (await Promise.resolve().then(() => (init_EntityObject(), EntityObject_exports))).EntityObject;
15549
+ const model = await Armor2.getEntity(armor);
15550
+ if (model) {
15551
+ const parts = { head: "head", body: "body", left_arm: "leftArm", right_arm: "rightArm", left_leg: "leftLeg", right_leg: "rightLeg" };
15552
+ for (const [name, part] of Object.entries(parts)) {
15553
+ const layers = {};
15554
+ for (const [draw, layer] of Object.entries(model.layers)) {
15555
+ const child = layer.layer.root.children[name];
15556
+ if (!child || !hasCubes(child)) continue;
15557
+ const pose = { offset: [0, 0, 0], rotation: [0, 0, 0] };
15558
+ layers[draw] = { ...layer, layer: { ...layer.layer, root: {
15559
+ pose,
15560
+ cubes: [],
15561
+ children: { [name]: { ...child, pose } }
15562
+ } } };
15563
+ }
15564
+ const first = Object.values(layers)[0];
15565
+ if (!first) continue;
15566
+ const object4 = new EntityObject2({ ...first, id: model.id, layers }, { flip: false });
15567
+ object4.rotation.x = Math.PI;
15568
+ objects.push({ part, object: object4 });
15569
+ }
15570
+ }
15571
+ }
15572
+ const results = await Promise.allSettled(objects.map(({ object: object4 }) => object4.init()));
15573
+ const failure = results.find((result) => result.status === "rejected");
15574
+ if (load !== this.armorLoad || failure) {
15575
+ for (const { object: object4 } of objects) object4.dispose();
15576
+ if (load === this.armorLoad && failure?.status === "rejected") throw failure.reason;
15577
+ return;
15578
+ }
15579
+ for (const { object: object4 } of this.armorObjects) {
15580
+ object4.dispose();
15581
+ object4.removeFromParent();
15582
+ }
15583
+ this.armorObjects = objects;
15584
+ for (const { part, object: object4 } of objects) this.getGroupByName(part)?.add(object4);
15585
+ this.notifyDirty();
15586
+ }
15232
15587
  /** Select the arm model, or pass undefined to use texture detection. */
15233
15588
  setSlim(slim) {
15234
15589
  this.options.slim = slim;
@@ -15247,6 +15602,39 @@ var SkinObject = class extends SceneObject {
15247
15602
  }
15248
15603
  this.notifyDirty();
15249
15604
  }
15605
+ /** Shows or hides deadmau5's ears, which vanilla draws from the skin texture at 24,0 for that player only. */
15606
+ setEars(ears) {
15607
+ if (ears === (this.options.ears ?? false)) return;
15608
+ this.options.ears = ears;
15609
+ this.updateEars();
15610
+ }
15611
+ updateEars() {
15612
+ const head = this.getGroupByName("head" /* HEAD */);
15613
+ if (!head) return;
15614
+ for (const [part, x] of [["leftEar" /* LEFT_EAR */, 6], ["rightEar" /* RIGHT_EAR */, -6]]) {
15615
+ const ear = this.getMeshByName(part);
15616
+ if (!this.options.ears) {
15617
+ ear?.removeFromParent();
15618
+ } else if (!ear) {
15619
+ const mesh = this.createAndAddMesh(part, head, this.getBoxGeometry(part), this.skinMaterial ?? Materials.MISSING_TEXTURE);
15620
+ mesh.position.set(x, 9, 0.5);
15621
+ }
15622
+ }
15623
+ this.notifyDirty();
15624
+ }
15625
+ /** Turns the player upside down, as vanilla does for players named Dinnerbone or Grumm. */
15626
+ setUpsideDown(upsideDown) {
15627
+ if (upsideDown === (this.options.upsideDown ?? false)) return;
15628
+ this.options.upsideDown = upsideDown;
15629
+ this.updateUpsideDown();
15630
+ }
15631
+ updateUpsideDown() {
15632
+ if (!this.model) return;
15633
+ const upsideDown = this.options.upsideDown ?? false;
15634
+ this.model.position.y = upsideDown ? UPSIDE_DOWN_OFFSET : 0;
15635
+ this.model.rotation.z = upsideDown ? Math.PI : 0;
15636
+ this.notifyDirty();
15637
+ }
15250
15638
  /** Select the texture layout, or pass undefined to detect it from image dimensions. */
15251
15639
  async setLegacy(legacy) {
15252
15640
  if (legacy === this.options.legacy) return;
@@ -15256,6 +15644,11 @@ var SkinObject = class extends SceneObject {
15256
15644
  dispose() {
15257
15645
  this.skinLoad++;
15258
15646
  this.capeLoad++;
15647
+ this.armorLoad++;
15648
+ for (const { object: object4 } of this.armorObjects.splice(0)) {
15649
+ object4.dispose();
15650
+ object4.removeFromParent();
15651
+ }
15259
15652
  super.dispose();
15260
15653
  }
15261
15654
  //TODO: layer toggles
@@ -15275,10 +15668,10 @@ function isBlockObject2(obj) {
15275
15668
  }
15276
15669
  var ModelObjectClass;
15277
15670
  var BlockObjectClass;
15278
- var EntityObjectClass;
15671
+ var EntityObjectClass2;
15279
15672
  var GuiObjectClass;
15280
15673
  var ItemTintsClass;
15281
- var MineRenderScene = class _MineRenderScene extends import_three36.Scene {
15674
+ var MineRenderScene = class _MineRenderScene extends import_three37.Scene {
15282
15675
  constructor(options) {
15283
15676
  super();
15284
15677
  this.isMineRenderScene = true;
@@ -15419,7 +15812,7 @@ var MineRenderScene = class _MineRenderScene extends import_three36.Scene {
15419
15812
  }
15420
15813
  /** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
15421
15814
  async addEntity(entity, options, parent = this) {
15422
- const EntityObject2 = EntityObjectClass ??= (await Promise.resolve().then(() => (init_EntityObject(), EntityObject_exports))).EntityObject;
15815
+ const EntityObject2 = EntityObjectClass2 ??= (await Promise.resolve().then(() => (init_EntityObject(), EntityObject_exports))).EntityObject;
15423
15816
  return this.addSceneObject(entity, () => new EntityObject2(entity, options), options, parent);
15424
15817
  }
15425
15818
  /**
@@ -15441,7 +15834,7 @@ function isMineRenderScene(obj) {
15441
15834
  }
15442
15835
 
15443
15836
  // src/renderer/Renderer.ts
15444
- var import_three38 = require("three");
15837
+ var import_three39 = require("three");
15445
15838
  init_merge();
15446
15839
  var import_stats = __toESM(require("stats.js"));
15447
15840
  var import_postprocessing = require("postprocessing");
@@ -15450,11 +15843,11 @@ init_Model();
15450
15843
  init_three();
15451
15844
 
15452
15845
  // src/three/OrbitControls.js
15453
- var import_three37 = require("three");
15846
+ var import_three38 = require("three");
15454
15847
  var _changeEvent = { type: "change" };
15455
15848
  var _startEvent = { type: "start" };
15456
15849
  var _endEvent = { type: "end" };
15457
- var OrbitControls = class extends import_three37.EventDispatcher {
15850
+ var OrbitControls = class extends import_three38.EventDispatcher {
15458
15851
  constructor(object4, domElement) {
15459
15852
  super();
15460
15853
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
@@ -15463,7 +15856,7 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15463
15856
  this.domElement = domElement;
15464
15857
  this.domElement.style.touchAction = "none";
15465
15858
  this.enabled = true;
15466
- this.target = new import_three37.Vector3();
15859
+ this.target = new import_three38.Vector3();
15467
15860
  this.minDistance = 0;
15468
15861
  this.maxDistance = Infinity;
15469
15862
  this.minZoom = 0;
@@ -15485,8 +15878,8 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15485
15878
  this.autoRotate = false;
15486
15879
  this.autoRotateSpeed = 2;
15487
15880
  this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" };
15488
- this.mouseButtons = { LEFT: import_three37.MOUSE.ROTATE, MIDDLE: import_three37.MOUSE.DOLLY, RIGHT: import_three37.MOUSE.PAN };
15489
- this.touches = { ONE: import_three37.TOUCH.ROTATE, TWO: import_three37.TOUCH.DOLLY_PAN };
15881
+ this.mouseButtons = { LEFT: import_three38.MOUSE.ROTATE, MIDDLE: import_three38.MOUSE.DOLLY, RIGHT: import_three38.MOUSE.PAN };
15882
+ this.touches = { ONE: import_three38.TOUCH.ROTATE, TWO: import_three38.TOUCH.DOLLY_PAN };
15490
15883
  this.target0 = this.target.clone();
15491
15884
  this.position0 = this.object.position.clone();
15492
15885
  this.zoom0 = this.object.zoom;
@@ -15515,11 +15908,11 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15515
15908
  state = STATE.NONE;
15516
15909
  };
15517
15910
  this.update = function() {
15518
- const offset = new import_three37.Vector3();
15519
- const quat = new import_three37.Quaternion().setFromUnitVectors(object4.up, new import_three37.Vector3(0, 1, 0));
15911
+ const offset = new import_three38.Vector3();
15912
+ const quat = new import_three38.Quaternion().setFromUnitVectors(object4.up, new import_three38.Vector3(0, 1, 0));
15520
15913
  const quatInverse = quat.clone().invert();
15521
- const lastPosition = new import_three37.Vector3();
15522
- const lastQuaternion = new import_three37.Quaternion();
15914
+ const lastPosition = new import_three38.Vector3();
15915
+ const lastQuaternion = new import_three38.Quaternion();
15523
15916
  const twoPI = 2 * Math.PI;
15524
15917
  return function update() {
15525
15918
  const position = scope.object.position;
@@ -15605,20 +15998,20 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15605
15998
  };
15606
15999
  let state = STATE.NONE;
15607
16000
  const EPS = 1e-6;
15608
- const spherical = new import_three37.Spherical();
15609
- const sphericalDelta = new import_three37.Spherical();
16001
+ const spherical = new import_three38.Spherical();
16002
+ const sphericalDelta = new import_three38.Spherical();
15610
16003
  let scale = 1;
15611
- const panOffset = new import_three37.Vector3();
16004
+ const panOffset = new import_three38.Vector3();
15612
16005
  let zoomChanged = false;
15613
- const rotateStart = new import_three37.Vector2();
15614
- const rotateEnd = new import_three37.Vector2();
15615
- const rotateDelta = new import_three37.Vector2();
15616
- const panStart = new import_three37.Vector2();
15617
- const panEnd = new import_three37.Vector2();
15618
- const panDelta = new import_three37.Vector2();
15619
- const dollyStart = new import_three37.Vector2();
15620
- const dollyEnd = new import_three37.Vector2();
15621
- const dollyDelta = new import_three37.Vector2();
16006
+ const rotateStart = new import_three38.Vector2();
16007
+ const rotateEnd = new import_three38.Vector2();
16008
+ const rotateDelta = new import_three38.Vector2();
16009
+ const panStart = new import_three38.Vector2();
16010
+ const panEnd = new import_three38.Vector2();
16011
+ const panDelta = new import_three38.Vector2();
16012
+ const dollyStart = new import_three38.Vector2();
16013
+ const dollyEnd = new import_three38.Vector2();
16014
+ const dollyDelta = new import_three38.Vector2();
15622
16015
  const pointers = [];
15623
16016
  const pointerPositions = {};
15624
16017
  function getAutoRotationAngle() {
@@ -15634,7 +16027,7 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15634
16027
  sphericalDelta.phi -= angle;
15635
16028
  }
15636
16029
  const panLeft = function() {
15637
- const v = new import_three37.Vector3();
16030
+ const v = new import_three38.Vector3();
15638
16031
  return function panLeft2(distance, objectMatrix) {
15639
16032
  v.setFromMatrixColumn(objectMatrix, 0);
15640
16033
  v.multiplyScalar(-distance);
@@ -15642,7 +16035,7 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15642
16035
  };
15643
16036
  }();
15644
16037
  const panUp = function() {
15645
- const v = new import_three37.Vector3();
16038
+ const v = new import_three38.Vector3();
15646
16039
  return function panUp2(distance, objectMatrix) {
15647
16040
  if (scope.screenSpacePanning === true) {
15648
16041
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -15655,7 +16048,7 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15655
16048
  };
15656
16049
  }();
15657
16050
  const pan = function() {
15658
- const offset = new import_three37.Vector3();
16051
+ const offset = new import_three38.Vector3();
15659
16052
  return function pan2(deltaX, deltaY) {
15660
16053
  const element = scope.domElement;
15661
16054
  if (scope.object.isPerspectiveCamera) {
@@ -15904,12 +16297,12 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15904
16297
  mouseAction = -1;
15905
16298
  }
15906
16299
  switch (mouseAction) {
15907
- case import_three37.MOUSE.DOLLY:
16300
+ case import_three38.MOUSE.DOLLY:
15908
16301
  if (scope.enableZoom === false) return;
15909
16302
  handleMouseDownDolly(event);
15910
16303
  state = STATE.DOLLY;
15911
16304
  break;
15912
- case import_three37.MOUSE.ROTATE:
16305
+ case import_three38.MOUSE.ROTATE:
15913
16306
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
15914
16307
  if (scope.enablePan === false) return;
15915
16308
  handleMouseDownPan(event);
@@ -15920,7 +16313,7 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15920
16313
  state = STATE.ROTATE;
15921
16314
  }
15922
16315
  break;
15923
- case import_three37.MOUSE.PAN:
16316
+ case import_three38.MOUSE.PAN:
15924
16317
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
15925
16318
  if (scope.enableRotate === false) return;
15926
16319
  handleMouseDownRotate(event);
@@ -15976,12 +16369,12 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15976
16369
  switch (pointers.length) {
15977
16370
  case 1:
15978
16371
  switch (scope.touches.ONE) {
15979
- case import_three37.TOUCH.ROTATE:
16372
+ case import_three38.TOUCH.ROTATE:
15980
16373
  if (scope.enableRotate === false) return;
15981
16374
  handleTouchStartRotate();
15982
16375
  state = STATE.TOUCH_ROTATE;
15983
16376
  break;
15984
- case import_three37.TOUCH.PAN:
16377
+ case import_three38.TOUCH.PAN:
15985
16378
  if (scope.enablePan === false) return;
15986
16379
  handleTouchStartPan();
15987
16380
  state = STATE.TOUCH_PAN;
@@ -15992,12 +16385,12 @@ var OrbitControls = class extends import_three37.EventDispatcher {
15992
16385
  break;
15993
16386
  case 2:
15994
16387
  switch (scope.touches.TWO) {
15995
- case import_three37.TOUCH.DOLLY_PAN:
16388
+ case import_three38.TOUCH.DOLLY_PAN:
15996
16389
  if (scope.enableZoom === false && scope.enablePan === false) return;
15997
16390
  handleTouchStartDollyPan();
15998
16391
  state = STATE.TOUCH_DOLLY_PAN;
15999
16392
  break;
16000
- case import_three37.TOUCH.DOLLY_ROTATE:
16393
+ case import_three38.TOUCH.DOLLY_ROTATE:
16001
16394
  if (scope.enableZoom === false && scope.enableRotate === false) return;
16002
16395
  handleTouchStartDollyRotate();
16003
16396
  state = STATE.TOUCH_DOLLY_ROTATE;
@@ -16064,7 +16457,7 @@ var OrbitControls = class extends import_three37.EventDispatcher {
16064
16457
  function trackPointer(event) {
16065
16458
  let position = pointerPositions[event.pointerId];
16066
16459
  if (position === void 0) {
16067
- position = new import_three37.Vector2();
16460
+ position = new import_three38.Vector2();
16068
16461
  pointerPositions[event.pointerId] = position;
16069
16462
  }
16070
16463
  position.set(event.pageX, event.pageY);
@@ -16084,10 +16477,10 @@ var MapControls = class extends OrbitControls {
16084
16477
  constructor(object4, domElement) {
16085
16478
  super(object4, domElement);
16086
16479
  this.screenSpacePanning = false;
16087
- this.mouseButtons.LEFT = import_three37.MOUSE.PAN;
16088
- this.mouseButtons.RIGHT = import_three37.MOUSE.ROTATE;
16089
- this.touches.ONE = import_three37.TOUCH.PAN;
16090
- this.touches.TWO = import_three37.TOUCH.DOLLY_ROTATE;
16480
+ this.mouseButtons.LEFT = import_three38.MOUSE.PAN;
16481
+ this.mouseButtons.RIGHT = import_three38.MOUSE.ROTATE;
16482
+ this.touches.ONE = import_three38.TOUCH.PAN;
16483
+ this.touches.TWO = import_three38.TOUCH.DOLLY_ROTATE;
16091
16484
  }
16092
16485
  };
16093
16486
 
@@ -16168,8 +16561,8 @@ var Renderer = class _Renderer {
16168
16561
  top: void 0,
16169
16562
  bottom: void 0
16170
16563
  },
16171
- position: new import_three38.Vector3(50, 50, 50),
16172
- lookingAt: new import_three38.Vector3(0, 0, 0)
16564
+ position: new import_three39.Vector3(50, 50, 50),
16565
+ lookingAt: new import_three39.Vector3(0, 0, 0)
16173
16566
  },
16174
16567
  render: {
16175
16568
  fpsLimit: 60,
@@ -16201,14 +16594,14 @@ var Renderer = class _Renderer {
16201
16594
  switch (this.options.camera.type) {
16202
16595
  case "perspective":
16203
16596
  default:
16204
- return new import_three38.PerspectiveCamera(
16597
+ return new import_three39.PerspectiveCamera(
16205
16598
  this.options.camera.perspective.fov,
16206
16599
  this.options.camera.perspective.aspect ?? this.viewWidth / this.viewHeight,
16207
16600
  this.options.camera.near,
16208
16601
  this.options.camera.far
16209
16602
  );
16210
16603
  case "orthographic":
16211
- return new import_three38.OrthographicCamera(
16604
+ return new import_three39.OrthographicCamera(
16212
16605
  this.options.camera.orthographic.left ?? this.viewWidth / -2,
16213
16606
  this.options.camera.orthographic.right ?? this.viewWidth / 2,
16214
16607
  this.options.camera.orthographic.top ?? this.viewHeight / 2,
@@ -16224,7 +16617,7 @@ var Renderer = class _Renderer {
16224
16617
  }
16225
16618
  const pixelRatio = this.options.render.pixelRatio ?? 1;
16226
16619
  this._surface?.resize?.(Math.floor(this.viewWidth * pixelRatio), Math.floor(this.viewHeight * pixelRatio));
16227
- const renderer = new import_three38.WebGLRenderer({
16620
+ const renderer = new import_three39.WebGLRenderer({
16228
16621
  canvas: this._surface?.canvas,
16229
16622
  context: this._surface?.context,
16230
16623
  antialias: this.options.render.antialias,
@@ -16234,8 +16627,8 @@ var Renderer = class _Renderer {
16234
16627
  });
16235
16628
  renderer.setClearColor(0, 0);
16236
16629
  renderer.shadowMap.enabled = true;
16237
- renderer.shadowMap.type = import_three38.PCFShadowMap;
16238
- renderer.outputColorSpace = import_three38.SRGBColorSpace;
16630
+ renderer.shadowMap.type = import_three39.PCFShadowMap;
16631
+ renderer.outputColorSpace = import_three39.SRGBColorSpace;
16239
16632
  if (this._surface) {
16240
16633
  renderer.setDrawingBufferSize(this.viewWidth, this.viewHeight, pixelRatio);
16241
16634
  } else {
@@ -16258,8 +16651,8 @@ var Renderer = class _Renderer {
16258
16651
  initialTarget() {
16259
16652
  const target = this.options.camera.lookingAt;
16260
16653
  if (isVector3(target)) return target.clone();
16261
- if (isTripleArray(target)) return new import_three38.Vector3(target[0], target[1], target[2]);
16262
- return new import_three38.Vector3();
16654
+ if (isTripleArray(target)) return new import_three39.Vector3(target[0], target[1], target[2]);
16655
+ return new import_three39.Vector3();
16263
16656
  }
16264
16657
  createControlsFor(mode, target) {
16265
16658
  let controls;
@@ -16292,7 +16685,7 @@ var Renderer = class _Renderer {
16292
16685
  let target;
16293
16686
  if (isFlyControls(current)) {
16294
16687
  const distance = this._orbitDistance ?? (this.camera.position.distanceTo(this.initialTarget()) || 1);
16295
- target = new import_three38.Vector3(0, 0, -distance).applyQuaternion(this.camera.quaternion).add(this.camera.position);
16688
+ target = new import_three39.Vector3(0, 0, -distance).applyQuaternion(this.camera.quaternion).add(this.camera.position);
16296
16689
  } else {
16297
16690
  target = current.target.clone();
16298
16691
  this._orbitDistance = this.camera.position.distanceTo(target);
@@ -16332,20 +16725,20 @@ var Renderer = class _Renderer {
16332
16725
  window["__THREE_DEVTOOLS__"].dispatchEvent(new CustomEvent("observe", { detail: this.scene }));
16333
16726
  }
16334
16727
  if (this.options.debug.grid) {
16335
- const gridHelper = new import_three38.GridHelper(128, 16);
16728
+ const gridHelper = new import_three39.GridHelper(128, 16);
16336
16729
  this._debugHelpers.push(gridHelper);
16337
16730
  this.scene.add(gridHelper);
16338
- const gridHelper2 = new import_three38.GridHelper(128, 16);
16731
+ const gridHelper2 = new import_three39.GridHelper(128, 16);
16339
16732
  this._debugHelpers.push(gridHelper2);
16340
16733
  gridHelper2.rotation.x = 90 * (Math.PI / 180);
16341
16734
  this.scene.add(gridHelper2);
16342
- const gridHelper3 = new import_three38.GridHelper(128, 16);
16735
+ const gridHelper3 = new import_three39.GridHelper(128, 16);
16343
16736
  this._debugHelpers.push(gridHelper3);
16344
16737
  gridHelper3.rotation.z = 90 * (Math.PI / 180);
16345
16738
  this.scene.add(gridHelper3);
16346
16739
  }
16347
16740
  if (this.options.debug.axes) {
16348
- const axesHelper = new import_three38.AxesHelper(64);
16741
+ const axesHelper = new import_three39.AxesHelper(64);
16349
16742
  this._debugHelpers.push(axesHelper);
16350
16743
  this.scene.add(axesHelper);
16351
16744
  }
@@ -16766,6 +17159,23 @@ var itemContext = (withDisplayContext) => (value, path) => {
16766
17159
  }
16767
17160
  });
16768
17161
  };
17162
+ function entityAnimation(value, path) {
17163
+ const animation = object3(value, path);
17164
+ if (["name", "clips"].filter((key) => animation[key] !== void 0).length !== 1) {
17165
+ fail(path, "expected exactly one of name or clips");
17166
+ }
17167
+ fields(value, path, { name: text, clips: (value2, path2) => {
17168
+ list((value3, path3) => {
17169
+ fields(value3, path3, { name: text, weight: nonnegative }, ["name"]);
17170
+ })(value2, path2);
17171
+ if (!value2.length) fail(path2, "expected at least one clip");
17172
+ const names = /* @__PURE__ */ new Set();
17173
+ for (const [index, clip] of value2.entries()) {
17174
+ if (names.has(clip.name)) fail(`${path2}[${index}].name`, `duplicate clip "${clip.name}"`);
17175
+ names.add(clip.name);
17176
+ }
17177
+ }, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean });
17178
+ }
16769
17179
  function guiLayer(value, path) {
16770
17180
  const layer = object3(value, path);
16771
17181
  if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
@@ -16806,13 +17216,27 @@ function validateObject(value, path) {
16806
17216
  const required = ["id", "type"];
16807
17217
  const renderOptions = { wireframe: boolean };
16808
17218
  switch (input.type) {
16809
- case "skin":
17219
+ case "skin": {
17220
+ const piece = (value2, path2) => {
17221
+ if (typeof value2 === "string") asset(value2, path2);
17222
+ else fields(value2, path2, {
17223
+ asset,
17224
+ color,
17225
+ glint: boolean,
17226
+ trim: (value3, path3) => {
17227
+ fields(value3, path3, { pattern: asset, material: asset }, ["pattern", "material"]);
17228
+ }
17229
+ }, ["asset"]);
17230
+ };
16810
17231
  fields(value, path, {
16811
17232
  ...common,
16812
17233
  skin: text,
16813
17234
  cape: (value2, path2) => {
16814
17235
  fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
16815
17236
  },
17237
+ armor: (value2, path2) => {
17238
+ fields(value2, path2, { head: piece, chest: piece, legs: piece, feet: piece });
17239
+ },
16816
17240
  hiddenParts: list(enumeration(SKIN_PARTS)),
16817
17241
  pose: (value2, path2) => {
16818
17242
  fields(value2, path2, {
@@ -16826,10 +17250,11 @@ function validateObject(value, path) {
16826
17250
  });
16827
17251
  },
16828
17252
  options: (value2, path2) => {
16829
- fields(value2, path2, { slim: boolean, legacy: boolean });
17253
+ fields(value2, path2, { slim: boolean, legacy: boolean, ears: boolean, upsideDown: boolean });
16830
17254
  }
16831
17255
  }, required);
16832
17256
  break;
17257
+ }
16833
17258
  case "block":
16834
17259
  fields(value, path, {
16835
17260
  ...common,
@@ -16868,9 +17293,7 @@ function validateObject(value, path) {
16868
17293
  })
16869
17294
  });
16870
17295
  },
16871
- animation: (value2, path2) => {
16872
- fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
16873
- }
17296
+ animation: entityAnimation
16874
17297
  }, [...required, "asset"]);
16875
17298
  if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
16876
17299
  break;
@@ -16978,7 +17401,7 @@ async function decodeSceneDocument(value) {
16978
17401
  }
16979
17402
 
16980
17403
  // src/scene/SceneDocumentLoader.ts
16981
- var import_three40 = require("three");
17404
+ var import_three41 = require("three");
16982
17405
  init_AssetKey();
16983
17406
  init_AssetLoader();
16984
17407
  init_BlockStates();
@@ -17040,7 +17463,7 @@ var SceneDocumentLoader = class {
17040
17463
  if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
17041
17464
  throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
17042
17465
  }
17043
- const root = new import_three40.Group();
17466
+ const root = new import_three41.Group();
17044
17467
  root.name = "MineRender scene document";
17045
17468
  const objects = [];
17046
17469
  let disposed = false;
@@ -17080,7 +17503,7 @@ var SceneDocumentLoader = class {
17080
17503
  return this.loadValidatedObject(scene, definition, parent);
17081
17504
  }
17082
17505
  static async loadValidatedObject(scene, definition, parent) {
17083
- const root = new import_three40.Group();
17506
+ const root = new import_three41.Group();
17084
17507
  root.name = definition.name ?? definition.id;
17085
17508
  root.userData.sceneObjectId = definition.id;
17086
17509
  root.position.fromArray(definition.position ?? [0, 0, 0]);
@@ -17099,6 +17522,7 @@ var SceneDocumentLoader = class {
17099
17522
  await skin.setSkinTexture(texture);
17100
17523
  await content.initialize(skin);
17101
17524
  if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
17525
+ if (definition.armor) await skin.setArmor(definition.armor);
17102
17526
  for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
17103
17527
  skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
17104
17528
  }
@@ -17137,10 +17561,20 @@ var SceneDocumentLoader = class {
17137
17561
  const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
17138
17562
  object4 = entity;
17139
17563
  if (definition.animation) {
17564
+ const animation = definition.animation;
17140
17565
  const animations = await Entities.getAnimations(key);
17141
- const clip = animations?.[definition.animation.name];
17142
- if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
17143
- entity.playAnimation(clip, definition.animation);
17566
+ if (animation.name !== void 0) {
17567
+ const clip = animations && Object.prototype.hasOwnProperty.call(animations, animation.name) ? animations[animation.name] : void 0;
17568
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${animation.name}"`);
17569
+ entity.playAnimation(clip, animation);
17570
+ } else {
17571
+ const clips = animation.clips.map(({ name }) => {
17572
+ const clip = animations && Object.prototype.hasOwnProperty.call(animations, name) ? animations[name] : void 0;
17573
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${name}"`);
17574
+ return clip;
17575
+ });
17576
+ entity.playAnimations(clips, { ...animation, weights: animation.clips.map((clip) => clip.weight ?? 1) });
17577
+ }
17144
17578
  }
17145
17579
  break;
17146
17580
  }
@@ -17189,6 +17623,7 @@ init_Skins();
17189
17623
 
17190
17624
  // src/texture/index.ts
17191
17625
  init_Colormaps();
17626
+ init_PalettedTextures();
17192
17627
  init_TextureAtlas();
17193
17628
  init_TextureLoader();
17194
17629
  init_Textures();
@@ -17978,7 +18413,7 @@ var AnvilWorldSource = class {
17978
18413
  read.waiters++;
17979
18414
  return new Promise((resolve, reject) => {
17980
18415
  let finished = false;
17981
- const release2 = () => {
18416
+ const release = () => {
17982
18417
  if (finished) return false;
17983
18418
  finished = true;
17984
18419
  signal?.removeEventListener("abort", abort);
@@ -17986,7 +18421,7 @@ var AnvilWorldSource = class {
17986
18421
  return true;
17987
18422
  };
17988
18423
  const abort = () => {
17989
- if (!release2()) return;
18424
+ if (!release()) return;
17990
18425
  if (!read.waiters && !read.settled) {
17991
18426
  if (this.pending.get(key) === read) this.pending.delete(key);
17992
18427
  read.controller.abort(signal.reason);
@@ -17996,10 +18431,10 @@ var AnvilWorldSource = class {
17996
18431
  signal?.addEventListener("abort", abort, { once: true });
17997
18432
  read.promise.then(
17998
18433
  (data) => {
17999
- if (release2()) resolve(data);
18434
+ if (release()) resolve(data);
18000
18435
  },
18001
18436
  (error) => {
18002
- if (release2()) reject(error);
18437
+ if (release()) reject(error);
18003
18438
  }
18004
18439
  );
18005
18440
  if (signal?.aborted) abort();
@@ -18021,20 +18456,22 @@ var AnvilWorldSource = class {
18021
18456
  };
18022
18457
 
18023
18458
  // src/world/Chunk.ts
18024
- var import_three47 = require("three");
18459
+ init_BlockObject();
18460
+ var import_three48 = require("three");
18025
18461
  init_BlockStates();
18026
18462
  init_AssetKey();
18027
18463
 
18028
18464
  // src/world/MineRenderWorld.ts
18029
- var import_three45 = require("three");
18465
+ var import_three46 = require("three");
18030
18466
  init_Model();
18031
18467
  init_util();
18032
18468
  init_AssetKey();
18033
18469
  init_BlockStates();
18470
+ init_BlockEntities();
18034
18471
  init_CubeFace();
18035
18472
 
18036
18473
  // src/world/SectionModels.ts
18037
- var import_three41 = require("three");
18474
+ var import_three42 = require("three");
18038
18475
  init_AssetKey();
18039
18476
  init_Models();
18040
18477
  init_CubeFace();
@@ -18060,7 +18497,7 @@ function cached(map, key, load) {
18060
18497
  }
18061
18498
  function compactQuads(geometry, keep) {
18062
18499
  const quads = keep.flatMap((kept, index) => kept ? [index] : []);
18063
- const compact = new import_three41.BufferGeometry();
18500
+ const compact = new import_three42.BufferGeometry();
18064
18501
  for (const [name, size] of Object.entries({ position: 3, normal: 3, uv: 2, uvBounds: 4, color: 3, shade: 1 })) {
18065
18502
  const source2 = geometry.getAttribute(name);
18066
18503
  const defaults = name === "uvBounds" ? [0, 0, 1, 1] : name === "color" ? [1, 1, 1] : name === "shade" ? [1] : void 0;
@@ -18072,7 +18509,7 @@ function compactQuads(geometry, keep) {
18072
18509
  }
18073
18510
  }
18074
18511
  }
18075
- compact.setAttribute(name, new import_three41.BufferAttribute(values, size));
18512
+ compact.setAttribute(name, new import_three42.BufferAttribute(values, size));
18076
18513
  }
18077
18514
  const source = geometry.getIndex();
18078
18515
  const indices = [];
@@ -18128,6 +18565,10 @@ var SectionModels = class {
18128
18565
  biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
18129
18566
  };
18130
18567
  }
18568
+ /** Starts loading the state's models and atlases so a later `prepareState` with the same properties finds them loaded. */
18569
+ async preload(blockState3, properties = {}) {
18570
+ await this.state(blockState3, properties);
18571
+ }
18131
18572
  async state(blockState3, properties) {
18132
18573
  let states = this.states.get(blockState3);
18133
18574
  if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
@@ -18150,22 +18591,24 @@ var SectionModels = class {
18150
18591
  async prepare(blockState3, properties) {
18151
18592
  const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
18152
18593
  const groups = BlockStateResolver.matching(blockState3, state);
18153
- if (!groups.length) return void 0;
18594
+ if (!groups.length || groups.some((group) => Array.isArray(group) && !group.length)) return void 0;
18595
+ const variants = groups.flatMap((group) => Array.isArray(group) ? group : [group]);
18596
+ if (variants.some((variant) => !variant.model)) return void 0;
18597
+ const settled = await Promise.allSettled(variants.map(async (variant) => {
18598
+ const modelKey = AssetKey.parse("models", variant.model);
18599
+ modelKey.root = blockState3.key?.root;
18600
+ const model = await Models.getMerged(modelKey);
18601
+ if (!model?.elements?.length) return void 0;
18602
+ const atlas = await UVMapper.getAtlas(model);
18603
+ const rotation = BlockStateResolver.rotation(variant);
18604
+ if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
18605
+ return { model, atlas, rotation };
18606
+ }));
18154
18607
  const prepared = /* @__PURE__ */ new Map();
18155
- for (const group of groups) {
18156
- const choices = Array.isArray(group) ? group : [group];
18157
- if (!choices.length) return void 0;
18158
- for (const variant of choices) {
18159
- if (!variant.model) return void 0;
18160
- const modelKey = AssetKey.parse("models", variant.model);
18161
- modelKey.root = blockState3.key?.root;
18162
- const model = await Models.getMerged(modelKey);
18163
- if (!model?.elements?.length) return void 0;
18164
- const atlas = await UVMapper.getAtlas(model);
18165
- const rotation = BlockStateResolver.rotation(variant);
18166
- if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
18167
- prepared.set(variant, { model, atlas, rotation });
18168
- }
18608
+ for (const [i, result] of settled.entries()) {
18609
+ if (result.status === "rejected") throw result.reason;
18610
+ if (!result.value) return void 0;
18611
+ prepared.set(variants[i], result.value);
18169
18612
  }
18170
18613
  return { state, groups, choices: prepared };
18171
18614
  }
@@ -18182,7 +18625,7 @@ var SectionModels = class {
18182
18625
  });
18183
18626
  await object4.init();
18184
18627
  let geometry = object4.children[0].geometry;
18185
- geometry.applyMatrix4(new import_three41.Matrix4().makeRotationFromEuler(rotation));
18628
+ geometry.applyMatrix4(new import_three42.Matrix4().makeRotationFromEuler(rotation));
18186
18629
  object4.clear();
18187
18630
  const keep = [];
18188
18631
  const cullFaces = [];
@@ -18198,7 +18641,7 @@ var SectionModels = class {
18198
18641
  cullFaces.push(0);
18199
18642
  continue;
18200
18643
  }
18201
- const normal = new import_three41.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
18644
+ const normal = new import_three42.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
18202
18645
  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);
18203
18646
  cullFaces.push(world < 0 ? 0 : 1 << world);
18204
18647
  }
@@ -18302,10 +18745,10 @@ function resolveSavedEntity(entity) {
18302
18745
 
18303
18746
  // src/world/_entities/WorldEntities.ts
18304
18747
  init_BannerPatterns();
18305
- var import_three44 = require("three");
18748
+ var import_three45 = require("three");
18306
18749
 
18307
18750
  // src/world/_picking/Picking.ts
18308
- var import_three42 = require("three");
18751
+ var import_three43 = require("three");
18309
18752
  init_three();
18310
18753
  function pickableMeshes(root, scene, raycaster) {
18311
18754
  const result = [];
@@ -18333,11 +18776,11 @@ function hitNormal(hit) {
18333
18776
  const mesh = hit.object;
18334
18777
  const matrix = mesh.matrixWorld.clone();
18335
18778
  if (isInstancedMesh(mesh) && hit.instanceId !== void 0) {
18336
- const instance = new import_three42.Matrix4();
18779
+ const instance = new import_three43.Matrix4();
18337
18780
  mesh.getMatrixAt(hit.instanceId, instance);
18338
18781
  matrix.multiply(instance);
18339
18782
  }
18340
- return hit.face.normal.clone().applyNormalMatrix(new import_three42.Matrix3().getNormalMatrix(matrix));
18783
+ return hit.face.normal.clone().applyNormalMatrix(new import_three43.Matrix3().getNormalMatrix(matrix));
18341
18784
  }
18342
18785
 
18343
18786
  // src/world/_entities/WorldEntities.ts
@@ -18363,7 +18806,7 @@ var WorldEntities = class {
18363
18806
  const owner = owners.get(mesh);
18364
18807
  return {
18365
18808
  type: owner.type,
18366
- position: new import_three44.Vector3(...owner.position),
18809
+ position: new import_three45.Vector3(...owner.position),
18367
18810
  object: owner.object,
18368
18811
  point: hit.point.clone(),
18369
18812
  normal: hitNormal(hit),
@@ -18625,6 +19068,7 @@ function skipsBlockFace(data, rule, block, neighbor, face) {
18625
19068
  // src/world/MineRenderWorld.ts
18626
19069
  init_log();
18627
19070
  var log18 = logger("MineRenderWorld");
19071
+ var YIELD_INTERVAL_MS = 8;
18628
19072
  var MineRenderWorld = class {
18629
19073
  constructor(scene, options = {}) {
18630
19074
  this._chunks = /* @__PURE__ */ new Map();
@@ -18738,7 +19182,7 @@ var MineRenderWorld = class {
18738
19182
  failures.set(chunk, error);
18739
19183
  if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
18740
19184
  }
18741
- if (performance.now() - sliceStart > 8) {
19185
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
18742
19186
  await yieldToEventLoop();
18743
19187
  sliceStart = performance.now();
18744
19188
  }
@@ -18751,25 +19195,25 @@ var MineRenderWorld = class {
18751
19195
  }
18752
19196
  getBlockAt(posOrX, y, z) {
18753
19197
  if (typeof posOrX == "number") {
18754
- return this.getBlockAt(new import_three45.Vector3(posOrX, y, z));
19198
+ return this.getBlockAt(new import_three46.Vector3(posOrX, y, z));
18755
19199
  }
18756
19200
  if (isTripleArray(posOrX)) {
18757
- return this.getBlockAt(new import_three45.Vector3(posOrX[0], posOrX[1], posOrX[2]));
19201
+ return this.getBlockAt(new import_three46.Vector3(posOrX[0], posOrX[1], posOrX[2]));
18758
19202
  }
18759
19203
  this.validatePosBounds(posOrX);
18760
19204
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
18761
19205
  }
18762
19206
  getBiomeAt(posOrX, y, z) {
18763
- if (typeof posOrX === "number") return this.getBiomeAt(new import_three45.Vector3(posOrX, y, z));
18764
- if (isTripleArray(posOrX)) return this.getBiomeAt(new import_three45.Vector3(...posOrX));
19207
+ if (typeof posOrX === "number") return this.getBiomeAt(new import_three46.Vector3(posOrX, y, z));
19208
+ if (isTripleArray(posOrX)) return this.getBiomeAt(new import_three46.Vector3(...posOrX));
18765
19209
  this.validatePosBounds(posOrX);
18766
19210
  const cx = Math.floor(posOrX.x / 16), cy = Math.floor(posOrX.y / 16), cz = Math.floor(posOrX.z / 16);
18767
19211
  const lx = posOrX.x - cx * 16, ly = posOrX.y - cy * 16, lz = posOrX.z - cz * 16;
18768
19212
  return this.biomes.get(`${cx}_${cz}`)?.get(cy)?.[Math.floor(lx / 4) + Math.floor(lz / 4) * 4 + Math.floor(ly / 4) * 16];
18769
19213
  }
18770
19214
  getLightAt(posOrX, y, z) {
18771
- if (typeof posOrX === "number") return this.getLightAt(new import_three45.Vector3(posOrX, y, z));
18772
- if (isTripleArray(posOrX)) return this.getLightAt(new import_three45.Vector3(...posOrX));
19215
+ if (typeof posOrX === "number") return this.getLightAt(new import_three46.Vector3(posOrX, y, z));
19216
+ if (isTripleArray(posOrX)) return this.getLightAt(new import_three46.Vector3(...posOrX));
18773
19217
  this.validatePosBounds(posOrX);
18774
19218
  const { x, y: py, z: pz } = posOrX;
18775
19219
  const cx = Math.floor(x / 16), cy = Math.floor(py / 16), cz = Math.floor(pz / 16);
@@ -18787,10 +19231,10 @@ var MineRenderWorld = class {
18787
19231
  }
18788
19232
  async setBlockAt(posOrX, yOrBlock, z, block) {
18789
19233
  if (typeof posOrX == "number") {
18790
- return this.setBlockAt(new import_three45.Vector3(posOrX, yOrBlock, z), block);
19234
+ return this.setBlockAt(new import_three46.Vector3(posOrX, yOrBlock, z), block);
18791
19235
  }
18792
19236
  if (isTripleArray(posOrX)) {
18793
- return this.setBlockAt(new import_three45.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
19237
+ return this.setBlockAt(new import_three46.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
18794
19238
  }
18795
19239
  return this.placeBlock(posOrX, yOrBlock);
18796
19240
  }
@@ -18801,10 +19245,10 @@ var MineRenderWorld = class {
18801
19245
  }
18802
19246
  async setBlockVisibleAt(posOrX, yOrVisible, z, visible) {
18803
19247
  if (typeof posOrX === "number") {
18804
- return this.setBlockVisibleAt(new import_three45.Vector3(posOrX, yOrVisible, z), visible);
19248
+ return this.setBlockVisibleAt(new import_three46.Vector3(posOrX, yOrVisible, z), visible);
18805
19249
  }
18806
19250
  if (isTripleArray(posOrX)) {
18807
- return this.setBlockVisibleAt(new import_three45.Vector3(...posOrX), yOrVisible);
19251
+ return this.setBlockVisibleAt(new import_three46.Vector3(...posOrX), yOrVisible);
18808
19252
  }
18809
19253
  this.validatePosBounds(posOrX);
18810
19254
  await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
@@ -18830,7 +19274,7 @@ var MineRenderWorld = class {
18830
19274
  const collect = async (positions2) => {
18831
19275
  for (const pos of positions2) changes.set(pos.toArray().join(","), pos);
18832
19276
  };
18833
- const place = (block) => this.placeBlock(new import_three45.Vector3(...block.position), block, collect);
19277
+ const place = (block) => this.placeBlock(new import_three46.Vector3(...block.position), block, collect);
18834
19278
  const keys = /* @__PURE__ */ new Map();
18835
19279
  const types = /* @__PURE__ */ new Set();
18836
19280
  for (const block of multiblock.blocks) {
@@ -18839,15 +19283,18 @@ var MineRenderWorld = class {
18839
19283
  const key = AssetKey.parse("blockstates", block.type);
18840
19284
  keys.set(key.serialize(), key);
18841
19285
  }
18842
- await BlockStates.getAll(keys.values());
19286
+ const loaded = await BlockStates.getAll(keys.values());
19287
+ const states = new Map([...keys.keys()].map((key, i) => [key, loaded[i]]));
18843
19288
  if (!useBatches) {
18844
19289
  for (const block of multiblock.blocks) await place(block);
18845
19290
  return;
18846
19291
  }
18847
19292
  if (!executor) {
18848
19293
  const groups2 = /* @__PURE__ */ new Map();
18849
- const position = new import_three45.Vector3();
19294
+ const distinct = /* @__PURE__ */ new Map();
19295
+ const position = new import_three46.Vector3();
18850
19296
  for (const block of multiblock.blocks) {
19297
+ if (this.sectionModels && !Chunk.isAir(block)) distinct.set(ChunkData.paletteKey(block), block);
18851
19298
  const [x, y, z] = block.position;
18852
19299
  this.validatePosBounds(position.set(x, y, z));
18853
19300
  const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
@@ -18858,6 +19305,16 @@ var MineRenderWorld = class {
18858
19305
  if (!group) groups2.set(chunk, group = []);
18859
19306
  group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
18860
19307
  }
19308
+ if (this.sectionModels) {
19309
+ for (const block of distinct.values()) {
19310
+ const state = states.get(AssetKey.parse("blockstates", block.type).serialize());
19311
+ if (!state) continue;
19312
+ void (async () => {
19313
+ if (state.key && BlockEntities.entry(await BlockEntities.getIndex(state.key.root), state.key.toNamespacedString())) return;
19314
+ await this.sectionModels.preload(state, block.properties);
19315
+ })().catch(() => void 0);
19316
+ }
19317
+ }
18861
19318
  await this.placeChunkGroups(groups2, changes);
18862
19319
  return;
18863
19320
  }
@@ -18886,14 +19343,14 @@ var MineRenderWorld = class {
18886
19343
  try {
18887
19344
  positions = await chunk.placeBlocks(blocks);
18888
19345
  } finally {
18889
- positions ??= blocks.map(({ index }) => new import_three45.Vector3(
19346
+ positions ??= blocks.map(({ index }) => new import_three46.Vector3(
18890
19347
  chunk.x * 16 + index % 16,
18891
19348
  chunk.y * 16 + Math.floor(index / 256),
18892
19349
  chunk.z * 16 + Math.floor(index / 16) % 16
18893
19350
  ));
18894
19351
  for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
18895
19352
  }
18896
- if (performance.now() - sliceStart > 8) {
19353
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
18897
19354
  await yieldToEventLoop();
18898
19355
  sliceStart = performance.now();
18899
19356
  }
@@ -18901,6 +19358,62 @@ var MineRenderWorld = class {
18901
19358
  }
18902
19359
  /** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
18903
19360
  async placeChunk(chunk, executor) {
19361
+ return this.placeColumn(chunk, executor);
19362
+ }
19363
+ /**
19364
+ * Replaces parsed columns in input order, then refreshes neighboring geometry once.
19365
+ * Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
19366
+ */
19367
+ async placeChunks(chunks) {
19368
+ const columns = [];
19369
+ for (const chunk of chunks) {
19370
+ const { x, z } = chunk ?? {};
19371
+ if (![x, z].every(Number.isSafeInteger)) {
19372
+ throw new RangeError("Chunk column coordinates must be safe integers");
19373
+ }
19374
+ const sections = Array.from(chunk.sections, (section) => {
19375
+ const { y } = section ?? {};
19376
+ if (!Number.isSafeInteger(y)) throw new RangeError("Chunk section coordinates must be safe integers");
19377
+ return { ...section, y };
19378
+ });
19379
+ columns.push({ ...chunk, x, z, sections });
19380
+ }
19381
+ if (!columns.length) return;
19382
+ await this.culling;
19383
+ let failed = false, failure, sliceStart = performance.now();
19384
+ try {
19385
+ for (const column of columns) {
19386
+ await this.placeColumn(column, void 0, true);
19387
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
19388
+ await yieldToEventLoop();
19389
+ sliceStart = performance.now();
19390
+ }
19391
+ }
19392
+ } catch (error) {
19393
+ failed = true;
19394
+ failure = error;
19395
+ } finally {
19396
+ try {
19397
+ const failures = await this.refreshBiomes(true);
19398
+ if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
19399
+ } catch (error) {
19400
+ if (!failed) {
19401
+ failed = true;
19402
+ failure = error;
19403
+ }
19404
+ }
19405
+ try {
19406
+ await this.updateCulling([]);
19407
+ } catch (error) {
19408
+ if (!failed) {
19409
+ failed = true;
19410
+ failure = error;
19411
+ }
19412
+ }
19413
+ }
19414
+ if (failed) throw failure;
19415
+ }
19416
+ async placeColumn(chunk, executor, deferCulling = false) {
18904
19417
  if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
18905
19418
  throw new RangeError("Chunk column coordinates must be safe integers");
18906
19419
  }
@@ -18938,7 +19451,7 @@ var MineRenderWorld = class {
18938
19451
  }
18939
19452
  for (const section of chunk.sections) {
18940
19453
  if (section.biomes) {
18941
- const position = new import_three45.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
19454
+ const position = new import_three46.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
18942
19455
  this.validatePosBounds(position);
18943
19456
  const copy = [...section.biomes];
18944
19457
  if (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0)) {
@@ -18954,7 +19467,7 @@ var MineRenderWorld = class {
18954
19467
  for (const section of chunk.sections) {
18955
19468
  if (!executor) {
18956
19469
  if (!occupiedSections.has(section.y)) continue;
18957
- const position = new import_three45.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
19470
+ const position = new import_three46.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
18958
19471
  this.validatePosBounds(position);
18959
19472
  const target = this.getOrCreateChunkAt(position);
18960
19473
  try {
@@ -18962,7 +19475,7 @@ var MineRenderWorld = class {
18962
19475
  } finally {
18963
19476
  this.markSectionChanged(target);
18964
19477
  }
18965
- if (performance.now() - sliceStart > 8) {
19478
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
18966
19479
  await yieldToEventLoop();
18967
19480
  sliceStart = performance.now();
18968
19481
  }
@@ -18985,9 +19498,12 @@ var MineRenderWorld = class {
18985
19498
  await placeEntities?.();
18986
19499
  } finally {
18987
19500
  this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
18988
- const failures = await this.refreshBiomes();
18989
- if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
18990
- await this.updateCulling([...changes.values()]);
19501
+ if (deferCulling) await this.updateCulling([...changes.values()], true);
19502
+ else {
19503
+ const failures = await this.refreshBiomes();
19504
+ if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
19505
+ await this.updateCulling([...changes.values()]);
19506
+ }
18991
19507
  }
18992
19508
  }
18993
19509
  /**
@@ -19017,7 +19533,7 @@ var MineRenderWorld = class {
19017
19533
  for (const { x, z } of columns.values()) {
19018
19534
  this.entities?.clearColumn(x, z);
19019
19535
  await this.clearChunkColumn(x, z);
19020
- if (performance.now() - sliceStart > 8) {
19536
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
19021
19537
  await yieldToEventLoop();
19022
19538
  sliceStart = performance.now();
19023
19539
  }
@@ -19087,7 +19603,7 @@ var MineRenderWorld = class {
19087
19603
  this.sectionModels?.clear();
19088
19604
  }
19089
19605
  updateCulling(positions, defer = false) {
19090
- const neighbor = new import_three45.Vector3();
19606
+ const neighbor = new import_three46.Vector3();
19091
19607
  for (const pos of positions) {
19092
19608
  const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
19093
19609
  const chunk = this._chunks.get(`${cx}_${cy}_${cz}`);
@@ -19156,15 +19672,25 @@ var MineRenderWorld = class {
19156
19672
  for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
19157
19673
  await chunk.refreshLight(indices === "all" ? void 0 : indices);
19158
19674
  chunk.refreshAmbientOcclusion(this._ambientOcclusion);
19159
- if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
19675
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
19160
19676
  await yieldToEventLoop();
19161
19677
  sliceStart = performance.now();
19162
19678
  }
19163
19679
  }
19164
- const builds = await Promise.allSettled([...batch.keys()].map(async (chunk) => {
19165
- await chunk.rebuildSectionMesh();
19166
- batch.delete(chunk);
19167
- }));
19680
+ const pending = [];
19681
+ for (const chunk of [...batch.keys()]) {
19682
+ const build = (async () => {
19683
+ await chunk.rebuildSectionMesh();
19684
+ batch.delete(chunk);
19685
+ })();
19686
+ void build.catch(() => void 0);
19687
+ pending.push(build);
19688
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
19689
+ await yieldToEventLoop();
19690
+ sliceStart = performance.now();
19691
+ }
19692
+ }
19693
+ const builds = await Promise.allSettled(pending);
19168
19694
  const failure = builds.find((result) => result.status === "rejected");
19169
19695
  if (failure?.status === "rejected") throw failure.reason;
19170
19696
  } catch (error) {
@@ -19247,7 +19773,7 @@ var MineRenderWorld = class {
19247
19773
  }
19248
19774
  /** Converts block coordinates to scene units by multiplying each component by 16. */
19249
19775
  static worldToScenePosition(pos) {
19250
- return new import_three45.Vector3(
19776
+ return new import_three46.Vector3(
19251
19777
  pos.x * 16,
19252
19778
  pos.y * 16,
19253
19779
  pos.z * 16
@@ -19255,7 +19781,7 @@ var MineRenderWorld = class {
19255
19781
  }
19256
19782
  /** Converts scene units to block coordinates without rounding to an integer block. */
19257
19783
  static sceneToWorldPosition(pos) {
19258
- return new import_three45.Vector3(
19784
+ return new import_three46.Vector3(
19259
19785
  pos.x / 16,
19260
19786
  pos.y / 16,
19261
19787
  pos.z / 16
@@ -19279,7 +19805,7 @@ init_Model();
19279
19805
  init_BlockEntities();
19280
19806
 
19281
19807
  // src/world/SectionMesh.ts
19282
- var import_three46 = require("three");
19808
+ var import_three47 = require("three");
19283
19809
  init_CanvasCompat();
19284
19810
  init_Materials();
19285
19811
 
@@ -19556,6 +20082,8 @@ function sectionGeometryTransferables(pages) {
19556
20082
 
19557
20083
  // src/world/SectionWorker.ts
19558
20084
  init_Env();
20085
+ init_log();
20086
+ var log19 = logger("SectionWorker");
19559
20087
  var SectionWorker = class _SectionWorker {
19560
20088
  constructor(worker) {
19561
20089
  this.worker = worker;
@@ -19569,8 +20097,12 @@ var SectionWorker = class _SectionWorker {
19569
20097
  if (reply.type === "pages") pending.resolve(reply.pages);
19570
20098
  else pending.reject(new Error(reply.message));
19571
20099
  });
19572
- worker.addEventListener("error", (event) => this.fail(event.message || "Section worker failed"));
19573
- worker.addEventListener("messageerror", () => this.fail("Section worker message could not be deserialized"));
20100
+ const fallBack = (message) => {
20101
+ log19.warn(`${message}, so sections build on the main thread`);
20102
+ this.fail(message);
20103
+ };
20104
+ worker.addEventListener("error", (event) => fallBack(event.message || "Section worker failed"));
20105
+ worker.addEventListener("messageerror", () => fallBack("Section worker message could not be deserialized"));
19574
20106
  }
19575
20107
  static {
19576
20108
  this.initialized = false;
@@ -19608,8 +20140,11 @@ var SectionWorker = class _SectionWorker {
19608
20140
  };
19609
20141
 
19610
20142
  // src/world/SectionMesh.ts
20143
+ var BLOCK_SIZE = 16;
20144
+ var QUAD_VERTICES = 4;
20145
+ var QUAD_INDICES = 6;
19611
20146
  var templateCache = /* @__PURE__ */ new WeakMap();
19612
- var WATER_TINT = new import_three46.Color(4159204).toArray();
20147
+ var WATER_TINT = new import_three47.Color(4159204).toArray();
19613
20148
  function toInput(entries, maxAtlasSize, fluids, lighting) {
19614
20149
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
19615
20150
  const atlases2 = [];
@@ -19644,8 +20179,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19644
20179
  if (!data) {
19645
20180
  const indices = template.geometry.getIndex();
19646
20181
  const position = template.geometry.getAttribute("position");
19647
- const quads = indices.count / 6;
19648
- if (indices.count % 6 !== 0 || position.count !== quads * 4) {
20182
+ const quads = indices.count / QUAD_INDICES;
20183
+ if (indices.count % QUAD_INDICES !== 0 || position.count !== quads * QUAD_VERTICES) {
19649
20184
  throw new RangeError("Section template geometry must hold four vertices and six indices per quad");
19650
20185
  }
19651
20186
  data = {
@@ -19655,8 +20190,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19655
20190
  uvs: new Float32Array(quads * 8),
19656
20191
  uvBounds: new Float32Array(quads * 16),
19657
20192
  colors: new Float32Array(quads * 12),
19658
- shade: new Float32Array(quads * 4),
19659
- indices: new Uint16Array(quads * 6),
20193
+ shade: new Float32Array(quads * QUAD_VERTICES),
20194
+ indices: new Uint16Array(quads * QUAD_INDICES),
19660
20195
  cullFaces: new Uint8Array(template.cullFaces),
19661
20196
  biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
19662
20197
  atlas: 0,
@@ -19668,7 +20203,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19668
20203
  const bounds = template.geometry.getAttribute("uvBounds");
19669
20204
  const color2 = template.geometry.getAttribute("color");
19670
20205
  const shade = template.geometry.getAttribute("shade");
19671
- for (let vertex = 0; vertex < quads * 4; vertex++) {
20206
+ for (let vertex = 0; vertex < quads * QUAD_VERTICES; vertex++) {
19672
20207
  data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
19673
20208
  data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
19674
20209
  data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
@@ -19681,7 +20216,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19681
20216
  data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
19682
20217
  data.shade[vertex] = shade?.getX(vertex) ?? 1;
19683
20218
  }
19684
- for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - 4 * Math.floor(i2 / 6);
20219
+ for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - QUAD_VERTICES * Math.floor(i2 / QUAD_INDICES);
19685
20220
  templateCache.set(template, data);
19686
20221
  }
19687
20222
  id = input.templateData.length;
@@ -19714,7 +20249,55 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19714
20249
  }
19715
20250
  return { input, atlases: atlases2 };
19716
20251
  }
19717
- var SectionMesh = class _SectionMesh extends import_three46.Group {
20252
+ function sortTransparentQuads(mesh) {
20253
+ const index = mesh.geometry.getIndex();
20254
+ if (index.count <= QUAD_INDICES) return;
20255
+ index.setUsage(import_three47.DynamicDrawUsage);
20256
+ const count = index.count / QUAD_INDICES;
20257
+ let source, centers, depths, order;
20258
+ const view = new import_three47.Matrix4(), localCamera = new import_three47.Vector3(), key = new import_three47.Vector3(), previous = new import_three47.Vector3();
20259
+ let previousOrthographic;
20260
+ mesh.onBeforeRender = (_renderer, _scene, camera) => {
20261
+ localCamera.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(view.copy(mesh.matrixWorld).invert());
20262
+ const orthographic = !!camera.isOrthographicCamera;
20263
+ if (orthographic) {
20264
+ view.multiplyMatrices(camera.matrixWorldInverse, mesh.matrixWorld);
20265
+ key.set(view.elements[2], view.elements[6], view.elements[10]);
20266
+ } else {
20267
+ key.copy(localCamera).divideScalar(BLOCK_SIZE).floor();
20268
+ }
20269
+ if (orthographic === previousOrthographic && key.equals(previous)) return;
20270
+ previous.copy(key);
20271
+ previousOrthographic = orthographic;
20272
+ if (!centers) {
20273
+ source = new Uint32Array(index.array);
20274
+ centers = new Float32Array(count * 3);
20275
+ depths = new Float32Array(count);
20276
+ order = Uint32Array.from({ length: count }, (_, quad) => quad);
20277
+ const positions = mesh.geometry.getAttribute("position");
20278
+ for (let quad = 0; quad < count; quad++) {
20279
+ for (let axis = 0; axis < 3; axis++) {
20280
+ let center = 0;
20281
+ for (let corner = 0; corner < QUAD_VERTICES; corner++) center += positions.getComponent(quad * QUAD_VERTICES + corner, axis);
20282
+ centers[quad * 3 + axis] = center / QUAD_VERTICES;
20283
+ }
20284
+ }
20285
+ }
20286
+ for (let quad = 0; quad < count; quad++) {
20287
+ const x = centers[quad * 3], y = centers[quad * 3 + 1], z = centers[quad * 3 + 2];
20288
+ depths[quad] = orthographic ? key.x * x + key.y * y + key.z * z : -((x - localCamera.x) ** 2 + (y - localCamera.y) ** 2 + (z - localCamera.z) ** 2);
20289
+ }
20290
+ order.sort((a, b) => depths[a] - depths[b] || a - b);
20291
+ if (!order.some((quad, offset) => index.array[offset * QUAD_INDICES] !== source[quad * QUAD_INDICES])) return;
20292
+ for (let offset = 0; offset < count; offset++) {
20293
+ for (let indexOffset = 0; indexOffset < QUAD_INDICES; indexOffset++) {
20294
+ index.array[offset * QUAD_INDICES + indexOffset] = source[order[offset] * QUAD_INDICES + indexOffset];
20295
+ }
20296
+ }
20297
+ index.needsUpdate = true;
20298
+ };
20299
+ }
20300
+ var SectionMesh = class _SectionMesh extends import_three47.Group {
19718
20301
  constructor() {
19719
20302
  super(...arguments);
19720
20303
  this.ownedMeshes = [];
@@ -19723,7 +20306,11 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19723
20306
  /** Returns the owning section cell for a triangle hit on this mesh's merged geometry. */
19724
20307
  getBlockIndex(hit) {
19725
20308
  if (hit.faceIndex === void 0 || hit.faceIndex === null) return void 0;
19726
- return this.blockIndices.get(hit.object)?.[Math.floor(hit.faceIndex / 2)];
20309
+ const mesh = hit.object;
20310
+ const owners = this.blockIndices.get(mesh);
20311
+ if (!owners) return void 0;
20312
+ const vertex = mesh.geometry.getIndex().getX(hit.faceIndex * 3);
20313
+ return owners[Math.floor(vertex / QUAD_VERTICES)];
19727
20314
  }
19728
20315
  /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
19729
20316
  static build(entries, maxAtlasSize = 2048, fluids, lighting) {
@@ -19745,27 +20332,28 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19745
20332
  for (const { atlas, x, y } of page.placements) {
19746
20333
  context.drawImage(atlases2[atlas].image.canvas, x, y);
19747
20334
  }
19748
- const geometry = new import_three46.BufferGeometry();
19749
- geometry.setAttribute("position", new import_three46.Float32BufferAttribute(page.positions.buffer, 3));
19750
- geometry.setAttribute("normal", new import_three46.Float32BufferAttribute(page.normals.buffer, 3));
19751
- geometry.setAttribute("uv", new import_three46.Float32BufferAttribute(page.uvs.buffer, 2));
19752
- geometry.setAttribute("uvBounds", new import_three46.Float32BufferAttribute(page.uvBounds.buffer, 4));
19753
- geometry.setAttribute("color", new import_three46.Float32BufferAttribute(page.colors.buffer, 3));
19754
- geometry.setAttribute("shade", new import_three46.Float32BufferAttribute(page.shade.buffer, 1));
19755
- geometry.setIndex(new import_three46.Uint32BufferAttribute(page.indices.buffer, 1));
20335
+ const geometry = new import_three47.BufferGeometry();
20336
+ geometry.setAttribute("position", new import_three47.Float32BufferAttribute(page.positions.buffer, 3));
20337
+ geometry.setAttribute("normal", new import_three47.Float32BufferAttribute(page.normals.buffer, 3));
20338
+ geometry.setAttribute("uv", new import_three47.Float32BufferAttribute(page.uvs.buffer, 2));
20339
+ geometry.setAttribute("uvBounds", new import_three47.Float32BufferAttribute(page.uvBounds.buffer, 4));
20340
+ geometry.setAttribute("color", new import_three47.Float32BufferAttribute(page.colors.buffer, 3));
20341
+ geometry.setAttribute("shade", new import_three47.Float32BufferAttribute(page.shade.buffer, 1));
20342
+ geometry.setIndex(new import_three47.Uint32BufferAttribute(page.indices.buffer, 1));
19756
20343
  geometry.computeBoundingBox();
19757
20344
  geometry.computeBoundingSphere();
19758
20345
  const transparent = page.layer === 2 ? atlases2[page.placements[0].atlas].hasTranslucency : page.layer === 1;
19759
20346
  const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
19760
- material.side = import_three46.FrontSide;
20347
+ material.side = import_three47.FrontSide;
19761
20348
  if (page.layer === 2) {
19762
- material.side = import_three46.DoubleSide;
19763
- material.forceSinglePass = true;
19764
20349
  material.depthWrite = !material.transparent;
20350
+ material.side = import_three47.DoubleSide;
20351
+ material.forceSinglePass = true;
19765
20352
  }
19766
20353
  material.vertexColors = true;
19767
20354
  const texture = material.uniforms?.map?.value ?? material.map;
19768
- const mesh = new import_three46.Mesh(geometry, material);
20355
+ const mesh = new import_three47.Mesh(geometry, material);
20356
+ if (transparent) sortTransparentQuads(mesh);
19769
20357
  section.add(mesh);
19770
20358
  section.blockIndices.set(mesh, page.blockIndices);
19771
20359
  const unsubscribe = [];
@@ -19806,7 +20394,7 @@ init_CubeFace();
19806
20394
  init_BlockTints();
19807
20395
  init_Biomes();
19808
20396
  init_InstanceReference();
19809
- var WATER_TINT2 = new import_three47.Color(4159204).toArray();
20397
+ var WATER_TINT2 = new import_three48.Color(4159204).toArray();
19810
20398
  var Chunk = class _Chunk {
19811
20399
  constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0, biomeBlend) {
19812
20400
  this.onBlocksChanged = onBlocksChanged;
@@ -19824,7 +20412,7 @@ var Chunk = class _Chunk {
19824
20412
  this.sectionBlocks = /* @__PURE__ */ new Map();
19825
20413
  this.hiddenBlocks = /* @__PURE__ */ new Set();
19826
20414
  this.fluidCells = new Uint8Array(4096);
19827
- this.blockPosition = new import_three47.Vector3();
20415
+ this.blockPosition = new import_three48.Vector3();
19828
20416
  this.meshDirty = false;
19829
20417
  this.meshGeneration = 0;
19830
20418
  this.blockChanges = /* @__PURE__ */ new Set();
@@ -19884,7 +20472,7 @@ var Chunk = class _Chunk {
19884
20472
  const info = owner.chunk.renderedBlocks.get(owner.index);
19885
20473
  if (!info) continue;
19886
20474
  return {
19887
- position: new import_three47.Vector3(
20475
+ position: new import_three48.Vector3(
19888
20476
  owner.chunk.x * 16 + owner.index % 16,
19889
20477
  owner.chunk.y * 16 + Math.floor(owner.index / 256),
19890
20478
  owner.chunk.z * 16 + Math.floor(owner.index / 16) % 16
@@ -19899,18 +20487,18 @@ var Chunk = class _Chunk {
19899
20487
  }
19900
20488
  getBlockAt(posOrX, y, z) {
19901
20489
  if (typeof posOrX == "number") {
19902
- return this.getBlockAt(new import_three47.Vector3(posOrX, y, z));
20490
+ return this.getBlockAt(new import_three48.Vector3(posOrX, y, z));
19903
20491
  }
19904
20492
  if (isTripleArray(posOrX)) {
19905
- return this.getBlockAt(new import_three47.Vector3(posOrX[0], posOrX[1], posOrX[2]));
20493
+ return this.getBlockAt(new import_three48.Vector3(posOrX[0], posOrX[1], posOrX[2]));
19906
20494
  }
19907
20495
  const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
19908
20496
  const index = _Chunk.chunkPosToBlockIndex(pos);
19909
20497
  return this.renderedBlocks.get(index);
19910
20498
  }
19911
20499
  getBiomeAt(posOrX, y, z) {
19912
- if (typeof posOrX === "number") return this.getBiomeAt(new import_three47.Vector3(posOrX, y, z));
19913
- if (isTripleArray(posOrX)) return this.getBiomeAt(new import_three47.Vector3(...posOrX));
20500
+ if (typeof posOrX === "number") return this.getBiomeAt(new import_three48.Vector3(posOrX, y, z));
20501
+ if (isTripleArray(posOrX)) return this.getBiomeAt(new import_three48.Vector3(...posOrX));
19914
20502
  const pos = this.worldPosToChunkPos(posOrX);
19915
20503
  _Chunk.chunkPosToBlockIndex(pos);
19916
20504
  return this.biomeAt ? this.biomeAt(posOrX.x, posOrX.y, posOrX.z) : this.biomes?.[Math.floor(pos.x / 4) + Math.floor(pos.z / 4) * 4 + Math.floor(pos.y / 4) * 16];
@@ -19988,17 +20576,17 @@ var Chunk = class _Chunk {
19988
20576
  this.hiddenBlocks.add(index);
19989
20577
  this.renderedBlocks.get(index)?.object?.setVisible(false);
19990
20578
  }
19991
- const positions = blocks.map(({ index }) => new import_three47.Vector3(...this.positionAt(index)));
20579
+ const positions = blocks.map(({ index }) => new import_three48.Vector3(...this.positionAt(index)));
19992
20580
  if (this.onBlocksChanged) await this.onBlocksChanged(positions, deferCulling);
19993
20581
  else await this.rebuildSectionMesh();
19994
20582
  }
19995
20583
  }
19996
20584
  async setBlockAt(posOrX, yOrBlock, z, block) {
19997
20585
  if (typeof posOrX == "number") {
19998
- return this.setBlockAt(new import_three47.Vector3(posOrX, yOrBlock, z), block);
20586
+ return this.setBlockAt(new import_three48.Vector3(posOrX, yOrBlock, z), block);
19999
20587
  }
20000
20588
  if (isTripleArray(posOrX)) {
20001
- return this.setBlockAt(new import_three47.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
20589
+ return this.setBlockAt(new import_three48.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
20002
20590
  }
20003
20591
  const worldPos = posOrX;
20004
20592
  const pos = this.worldPosToChunkPos(worldPos);
@@ -20060,8 +20648,10 @@ var Chunk = class _Chunk {
20060
20648
  if (rejected?.status === "rejected") throw rejected.reason;
20061
20649
  }
20062
20650
  const positions = [];
20063
- let failed = false, failure;
20064
- for (const { index, key, block } of placements) {
20651
+ const jobs = /* @__PURE__ */ new Map();
20652
+ let failureOrder = Infinity, failure;
20653
+ for (const [order, { index, key, block }] of placements.entries()) {
20654
+ jobs.delete(index);
20065
20655
  this.hiddenBlocks.delete(index);
20066
20656
  this.data.assign(index, this.data.intern(block), block?.nbt);
20067
20657
  const readBlock = this.data.snapshot(index);
@@ -20071,13 +20661,12 @@ var Chunk = class _Chunk {
20071
20661
  this.fluidCells[index] = 0;
20072
20662
  this.renderedBlocks.delete(index);
20073
20663
  this.objectBlocks.delete(index);
20074
- const worldPos = new import_three47.Vector3(
20664
+ const worldPos = new import_three48.Vector3(
20075
20665
  this.x * 16 + index % 16,
20076
20666
  this.y * 16 + Math.floor(index / 256),
20077
20667
  this.z * 16 + Math.floor(index / 16) % 16
20078
20668
  );
20079
20669
  positions.push(worldPos);
20080
- let object4;
20081
20670
  try {
20082
20671
  if (!readBlock) continue;
20083
20672
  const resolved = resolutions.get(key);
@@ -20099,7 +20688,7 @@ var Chunk = class _Chunk {
20099
20688
  this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
20100
20689
  this.meshDirty = true;
20101
20690
  } else {
20102
- object4 = await this.scene.addBlock(blockState3, {
20691
+ jobs.set(index, { order, readBlock, blockState: blockState3, position: MineRenderWorld.worldToScenePosition(worldPos), options: {
20103
20692
  mergeMeshes: true,
20104
20693
  instanceMeshes: true,
20105
20694
  maxInstanceCount: 2e3,
@@ -20107,13 +20696,12 @@ var Chunk = class _Chunk {
20107
20696
  variantPosition,
20108
20697
  biome: context?.biome,
20109
20698
  biomePosition: this.biomePosition(index, blockState3, stored)
20110
- });
20111
- object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
20699
+ } });
20700
+ continue;
20112
20701
  }
20113
20702
  this.renderedBlocks.set(index, { get block() {
20114
20703
  return readBlock();
20115
- }, object: object4 });
20116
- if (object4) this.objectBlocks.add(index);
20704
+ }, object: void 0 });
20117
20705
  } catch (error) {
20118
20706
  if (preserveDataOnFailure && readBlock) {
20119
20707
  this.renderedBlocks.set(index, { get block() {
@@ -20121,12 +20709,35 @@ var Chunk = class _Chunk {
20121
20709
  }, object: void 0 });
20122
20710
  } else this.data.assign(index, 0);
20123
20711
  this.fluidCells[index] = 0;
20124
- object4?.removeFromScene();
20125
- if (!failed) failure = error;
20126
- failed = true;
20712
+ if (order < failureOrder) {
20713
+ failureOrder = order;
20714
+ failure = error;
20715
+ }
20127
20716
  }
20128
20717
  }
20129
- if (failed) throw failure;
20718
+ const pending = [...jobs];
20719
+ const objects = await Promise.allSettled(pending.map(([, job]) => this.createBlockObject(job.blockState, job.position, job.options)));
20720
+ for (const [i, [index, { order, readBlock }]] of pending.entries()) {
20721
+ const result = objects[i];
20722
+ if (result.status === "fulfilled") {
20723
+ this.renderedBlocks.set(index, { get block() {
20724
+ return readBlock();
20725
+ }, object: result.value });
20726
+ this.objectBlocks.add(index);
20727
+ } else {
20728
+ if (preserveDataOnFailure) {
20729
+ this.renderedBlocks.set(index, { get block() {
20730
+ return readBlock();
20731
+ }, object: void 0 });
20732
+ } else this.data.assign(index, 0);
20733
+ this.fluidCells[index] = 0;
20734
+ if (order < failureOrder) {
20735
+ failureOrder = order;
20736
+ failure = result.reason;
20737
+ }
20738
+ }
20739
+ }
20740
+ if (failureOrder < Infinity) throw failure;
20130
20741
  return positions;
20131
20742
  }
20132
20743
  /** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
@@ -20197,37 +20808,42 @@ var Chunk = class _Chunk {
20197
20808
  failed = true;
20198
20809
  }
20199
20810
  }
20200
- for (const { index, blockState: blockState3, biome } of perBlocks) {
20201
- let object4;
20202
- try {
20203
- object4 = await this.scene.addBlock(blockState3, {
20204
- mergeMeshes: true,
20205
- instanceMeshes: true,
20206
- maxInstanceCount: 2e3,
20207
- initialState: this.data.stateAt(this.data.idAt(index)).properties,
20208
- variantPosition: this.positionAt(index),
20209
- biome,
20210
- biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
20211
- });
20212
- object4.setPosition(new import_three47.Vector3(
20213
- this.x * 256 + index % 16 * 16,
20214
- this.y * 256 + Math.floor(index / 256) * 16,
20215
- this.z * 256 + Math.floor(index / 16) % 16 * 16
20216
- ));
20217
- this.renderedBlocks.get(index).object = object4;
20811
+ const objects = await Promise.allSettled(perBlocks.map(({ index, blockState: blockState3, biome }) => this.createBlockObject(blockState3, new import_three48.Vector3(
20812
+ this.x * 256 + index % 16 * 16,
20813
+ this.y * 256 + Math.floor(index / 256) * 16,
20814
+ this.z * 256 + Math.floor(index / 16) % 16 * 16
20815
+ ), {
20816
+ mergeMeshes: true,
20817
+ instanceMeshes: true,
20818
+ maxInstanceCount: 2e3,
20819
+ initialState: this.data.stateAt(this.data.idAt(index)).properties,
20820
+ variantPosition: this.positionAt(index),
20821
+ biome,
20822
+ biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
20823
+ })));
20824
+ for (const [i, { index }] of perBlocks.entries()) {
20825
+ const result = objects[i];
20826
+ if (result.status === "fulfilled") {
20827
+ this.renderedBlocks.get(index).object = result.value;
20218
20828
  this.objectBlocks.add(index);
20219
- } catch (error) {
20829
+ } else {
20220
20830
  this.data.assign(index, 0);
20221
20831
  this.fluidCells[index] = 0;
20222
20832
  this.renderedBlocks.delete(index);
20223
20833
  this.objectBlocks.delete(index);
20224
- object4?.removeFromScene();
20225
- if (!failed) failure = error;
20834
+ if (!failed) failure = result.reason;
20226
20835
  failed = true;
20227
20836
  }
20228
20837
  }
20229
20838
  if (failed) throw failure;
20230
20839
  }
20840
+ createBlockObject(blockState3, position, options) {
20841
+ return this.scene.addSceneObject(blockState3, () => {
20842
+ const object4 = new BlockObject(blockState3, options);
20843
+ object4.position.copy(position);
20844
+ return object4;
20845
+ }, options);
20846
+ }
20231
20847
  positionAt(index) {
20232
20848
  return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
20233
20849
  }
@@ -20282,11 +20898,11 @@ var Chunk = class _Chunk {
20282
20898
  const context = {};
20283
20899
  if (source && prepared2?.biomeTint) {
20284
20900
  const color2 = await biomeColor(source);
20285
- if (color2 !== void 0) context.tint = new import_three47.Color(color2).toArray();
20901
+ if (color2 !== void 0) context.tint = new import_three48.Color(color2).toArray();
20286
20902
  }
20287
20903
  if (fluid?.kind === "water") {
20288
20904
  const color2 = await biomeColor("water");
20289
- if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three47.Color(color2).toArray();
20905
+ if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three48.Color(color2).toArray();
20290
20906
  }
20291
20907
  contexts.set(index, context);
20292
20908
  }));
@@ -20324,7 +20940,7 @@ var Chunk = class _Chunk {
20324
20940
  context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
20325
20941
  if (fluid?.kind === "water" && await available()) {
20326
20942
  const color2 = await Biomes.getColor(biome, "water", blockState3?.key);
20327
- if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three47.Color(color2).toArray();
20943
+ if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three48.Color(color2).toArray();
20328
20944
  }
20329
20945
  }
20330
20946
  for (const index of indices2) contexts.set(index, context);
@@ -20516,7 +21132,7 @@ var Chunk = class _Chunk {
20516
21132
  async clear(notify = true) {
20517
21133
  if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
20518
21134
  const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
20519
- new import_three47.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
21135
+ new import_three48.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
20520
21136
  )) : [];
20521
21137
  for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
20522
21138
  this.sectionBlocks.clear();
@@ -20552,14 +21168,14 @@ var Chunk = class _Chunk {
20552
21168
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
20553
21169
  }
20554
21170
  worldPosToChunkPos(pos) {
20555
- return new import_three47.Vector3(
21171
+ return new import_three48.Vector3(
20556
21172
  pos.x - this.x * 16,
20557
21173
  pos.y - this.y * 16,
20558
21174
  pos.z - this.z * 16
20559
21175
  );
20560
21176
  }
20561
21177
  chunkPosToWorldPos(pos) {
20562
- return new import_three47.Vector3(
21178
+ return new import_three48.Vector3(
20563
21179
  this.x * 16 + pos.x,
20564
21180
  this.y * 16 + pos.y,
20565
21181
  this.z * 16 + pos.z
@@ -20568,6 +21184,7 @@ var Chunk = class _Chunk {
20568
21184
  };
20569
21185
 
20570
21186
  // src/world/WorldStreamer.ts
21187
+ var MAX_PREFETCHED_COLUMNS = 4;
20571
21188
  var WorldStreamer = class {
20572
21189
  constructor(world, source, options = {}) {
20573
21190
  this.world = world;
@@ -20577,6 +21194,7 @@ var WorldStreamer = class {
20577
21194
  this.missing = /* @__PURE__ */ new Map();
20578
21195
  this.failed = /* @__PURE__ */ new Map();
20579
21196
  this.desired = [];
21197
+ this.reads = /* @__PURE__ */ new Map();
20580
21198
  this.disposed = false;
20581
21199
  this.loadRadius = options.loadRadius ?? 2;
20582
21200
  this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);
@@ -20621,6 +21239,12 @@ var WorldStreamer = class {
20621
21239
  if (this.loading && !this.within(this.loading.position, this.loadRadius)) {
20622
21240
  this.loading.controller.abort();
20623
21241
  }
21242
+ for (const [key, read] of this.reads) {
21243
+ if (!this.within(read.position, this.loadRadius)) {
21244
+ read.controller.abort();
21245
+ if (read.ready) this.reads.delete(key);
21246
+ }
21247
+ }
20624
21248
  }
20625
21249
  return this.running ??= Promise.resolve().then(() => this.drain());
20626
21250
  }
@@ -20641,8 +21265,11 @@ var WorldStreamer = class {
20641
21265
  this.desired = [];
20642
21266
  this.center = void 0;
20643
21267
  this.loading?.controller.abort();
21268
+ for (const read of this.reads.values()) read.controller.abort();
20644
21269
  return this.disposal ??= Promise.resolve().then(async () => {
20645
21270
  await this.running?.catch(() => void 0);
21271
+ await Promise.all([...this.reads.values()].map((read) => read.promise));
21272
+ this.reads.clear();
20646
21273
  try {
20647
21274
  await this.unloadMany([...this.owned.values()]);
20648
21275
  } finally {
@@ -20668,6 +21295,10 @@ var WorldStreamer = class {
20668
21295
  await this.unloadMany(obsolete);
20669
21296
  continue;
20670
21297
  }
21298
+ if (this.world.placeChunks) {
21299
+ if (await this.placeReadyChunks()) continue;
21300
+ return;
21301
+ }
20671
21302
  const position = this.desired.find((position2) => this.needsLoad(position2));
20672
21303
  if (!position) return;
20673
21304
  let chunk;
@@ -20690,26 +21321,105 @@ var WorldStreamer = class {
20690
21321
  continue;
20691
21322
  }
20692
21323
  this.owned.set(this.key(position), position);
21324
+ await this.placeOwnedColumns([position], () => this.world.placeChunk(chunk));
21325
+ }
21326
+ } finally {
21327
+ for (const [key, read] of this.reads) {
21328
+ read.controller.abort();
21329
+ if (read.ready) this.reads.delete(key);
21330
+ }
21331
+ this.running = void 0;
21332
+ }
21333
+ }
21334
+ fillReadSlots() {
21335
+ while (!this.disposed && this.reads.size < MAX_PREFETCHED_COLUMNS) {
21336
+ const position = this.desired.find((position2) => this.needsLoad(position2) && !this.reads.has(this.key(position2)));
21337
+ if (!position) return;
21338
+ const key = this.key(position);
21339
+ const read = {
21340
+ position,
21341
+ controller: new AbortController(),
21342
+ promise: Promise.resolve(),
21343
+ ready: false,
21344
+ failed: false
21345
+ };
21346
+ this.reads.set(key, read);
21347
+ read.promise = (async () => {
20693
21348
  try {
20694
- await this.world.placeChunk(chunk);
20695
- this.loaded.set(this.key(position), position);
21349
+ read.chunk = await this.source.getChunk(position.x, position.z, read.controller.signal);
21350
+ if (read.chunk) this.validateChunk(read.chunk, position);
20696
21351
  } catch (error) {
20697
- try {
20698
- await this.unloadMany([position]);
20699
- } catch (cleanupError) {
20700
- const failure = error instanceof Error ? error : new Error(String(error));
20701
- try {
20702
- Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
20703
- } catch {
20704
- throw Object.assign(new Error(failure.message), { cause: cleanupError });
20705
- }
20706
- throw failure;
21352
+ read.failed = true;
21353
+ read.error = error;
21354
+ } finally {
21355
+ read.ready = true;
21356
+ if (read.controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) {
21357
+ this.reads.delete(key);
20707
21358
  }
20708
- throw error;
21359
+ }
21360
+ })();
21361
+ }
21362
+ }
21363
+ /** Places only the ready prefix, so later source reads cannot delay the nearest ready column. */
21364
+ async placeReadyChunks() {
21365
+ this.fillReadSlots();
21366
+ const position = this.desired.find((position2) => this.needsLoad(position2));
21367
+ if (!position) {
21368
+ if (!this.reads.size) return false;
21369
+ await Promise.all([...this.reads.values()].map((read) => read.promise));
21370
+ return true;
21371
+ }
21372
+ const head = this.reads.get(this.key(position));
21373
+ if (!head) {
21374
+ for (let index = this.desired.length - 1; index >= 0; index--) {
21375
+ const key = this.key(this.desired[index]);
21376
+ if (this.reads.get(key)?.ready) {
21377
+ this.reads.delete(key);
21378
+ return true;
20709
21379
  }
20710
21380
  }
20711
- } finally {
20712
- this.running = void 0;
21381
+ await Promise.race([...this.reads.values()].map((read) => read.promise));
21382
+ return true;
21383
+ }
21384
+ if (!head.ready) {
21385
+ await head.promise;
21386
+ return true;
21387
+ }
21388
+ const chunks = [], positions = [];
21389
+ for (const desired of this.desired) {
21390
+ if (!this.needsLoad(desired)) continue;
21391
+ const key = this.key(desired), read = this.reads.get(key);
21392
+ if (!read?.ready) break;
21393
+ this.reads.delete(key);
21394
+ if (read.failed) this.failed.set(key, { ...desired, error: read.error });
21395
+ else if (!read.chunk) this.missing.set(key, desired);
21396
+ else {
21397
+ chunks.push(read.chunk);
21398
+ positions.push(desired);
21399
+ }
21400
+ }
21401
+ if (!chunks.length) return true;
21402
+ for (const position2 of positions) this.owned.set(this.key(position2), position2);
21403
+ await this.placeOwnedColumns(positions, () => this.world.placeChunks(chunks));
21404
+ return true;
21405
+ }
21406
+ async placeOwnedColumns(positions, place) {
21407
+ try {
21408
+ await place();
21409
+ for (const position of positions) this.loaded.set(this.key(position), position);
21410
+ } catch (error) {
21411
+ try {
21412
+ await this.unloadMany(positions);
21413
+ } catch (cleanupError) {
21414
+ const failure = error instanceof Error ? error : new Error(String(error));
21415
+ try {
21416
+ Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
21417
+ } catch {
21418
+ throw Object.assign(new Error(failure.message), { cause: cleanupError });
21419
+ }
21420
+ throw failure;
21421
+ }
21422
+ throw error;
20713
21423
  }
20714
21424
  }
20715
21425
  needsLoad(position) {
@@ -20911,6 +21621,7 @@ var NodeRenderer = class _NodeRenderer extends Renderer {
20911
21621
  AnvilParser,
20912
21622
  AnvilWorldSource,
20913
21623
  ArchiveAssetSource,
21624
+ Armor,
20914
21625
  AssetKey,
20915
21626
  AssetLoadError,
20916
21627
  AssetLoader,
@@ -20994,6 +21705,7 @@ var NodeRenderer = class _NodeRenderer extends Renderer {
20994
21705
  OrbitControls,
20995
21706
  PackFormats,
20996
21707
  PackMetadata,
21708
+ PalettedTextures,
20997
21709
  PersistentCache,
20998
21710
  PlayerHeadTextures,
20999
21711
  Renames,