minerender 2.1.0-beta.5 → 2.1.0-beta.6

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 (43) hide show
  1. package/dist/browser/esm/Materials.mjs +3 -2
  2. package/dist/browser/esm/Materials.mjs.map +1 -1
  3. package/dist/browser/esm/assets/Fonts.mjs +31 -4
  4. package/dist/browser/esm/assets/Fonts.mjs.map +1 -1
  5. package/dist/browser/esm/assets/_fonts/Unihex.mjs +166 -0
  6. package/dist/browser/esm/assets/_fonts/Unihex.mjs.map +1 -0
  7. package/dist/browser/esm/assets/source/HostedAssetSource.mjs +12 -1
  8. package/dist/browser/esm/assets/source/HostedAssetSource.mjs.map +1 -1
  9. package/dist/browser/esm/assets/source/parser/AssetParsers.mjs +1 -0
  10. package/dist/browser/esm/assets/source/parser/AssetParsers.mjs.map +1 -1
  11. package/dist/browser/esm/cache/Caching.mjs +2 -0
  12. package/dist/browser/esm/cache/Caching.mjs.map +1 -1
  13. package/dist/browser/esm/entity/scene/EntityAnimationPlayer.mjs +62 -11
  14. package/dist/browser/esm/entity/scene/EntityAnimationPlayer.mjs.map +1 -1
  15. package/dist/browser/esm/entity/scene/EntityObject.mjs +53 -20
  16. package/dist/browser/esm/entity/scene/EntityObject.mjs.map +1 -1
  17. package/dist/browser/esm/env/browser/section.worker.mjs +5 -3
  18. package/dist/browser/esm/env/browser/section.worker.mjs.map +1 -1
  19. package/dist/browser/esm/gui/GuiText.mjs +4 -3
  20. package/dist/browser/esm/gui/GuiText.mjs.map +1 -1
  21. package/dist/browser/esm/index.browser.d.mts +52 -10
  22. package/dist/browser/esm/model/scene/ModelObject.mjs +4 -0
  23. package/dist/browser/esm/model/scene/ModelObject.mjs.map +1 -1
  24. package/dist/browser/esm/util/model.mjs +7 -7
  25. package/dist/browser/esm/util/model.mjs.map +1 -1
  26. package/dist/browser/esm/world/SectionGeometry.mjs +5 -3
  27. package/dist/browser/esm/world/SectionGeometry.mjs.map +1 -1
  28. package/dist/browser/esm/world/SectionMesh.mjs +4 -0
  29. package/dist/browser/esm/world/SectionMesh.mjs.map +1 -1
  30. package/dist/browser/esm/world/SectionModels.mjs +2 -2
  31. package/dist/browser/esm/world/SectionModels.mjs.map +1 -1
  32. package/dist/browser/index.d.ts +52 -10
  33. package/dist/browser/index.js +382 -73
  34. package/dist/browser/index.js.map +1 -1
  35. package/dist/bundle.js +425 -73
  36. package/dist/bundle.js.map +1 -1
  37. package/dist/node/index.d.mts +52 -10
  38. package/dist/node/index.d.ts +52 -10
  39. package/dist/node/index.js +387 -73
  40. package/dist/node/index.js.map +1 -1
  41. package/dist/node/index.mjs +386 -73
  42. package/dist/node/index.mjs.map +1 -1
  43. package/package.json +1 -1
@@ -309,6 +309,9 @@ var init_Caching = __esm({
309
309
  static {
310
310
  this.blockEntityIndexCache = _Caching.createAssetCache();
311
311
  }
312
+ static {
313
+ this.unihexCache = _Caching.createAssetCache();
314
+ }
312
315
  static {
313
316
  this.fontCache = _Caching.createAssetCache();
314
317
  }
@@ -351,6 +354,7 @@ var init_Caching = __esm({
351
354
  this.entityAnimationCache,
352
355
  this.blockEntityIndexCache,
353
356
  this.fontCache,
357
+ this.unihexCache,
354
358
  this.bannerPatternCache,
355
359
  this.mineRenderDataCache,
356
360
  this.usernameUuidCache,
@@ -1112,6 +1116,7 @@ var init_Materials = __esm({
1112
1116
  static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
1113
1117
  const vertShader = `
1114
1118
  uniform bool SHADE;
1119
+ attribute float shade;
1115
1120
 
1116
1121
  // Keep MSAA edge pixels from sampling outside the face's atlas region.
1117
1122
  #if __VERSION__ >= 300
@@ -1134,7 +1139,7 @@ var init_Materials = __esm({
1134
1139
  void main()
1135
1140
  {
1136
1141
 
1137
- if (SHADE) {
1142
+ if (SHADE && shade > 0.5) {
1138
1143
 
1139
1144
  #ifdef USE_INSTANCING
1140
1145
  vec3 N = vec3( modelMatrix * instanceMatrix * vec4(normal, 0.0) );
@@ -1243,7 +1248,7 @@ var init_Materials = __esm({
1243
1248
  side: transparent ? DoubleSide : FrontSide,
1244
1249
  alphaTest: 0.5
1245
1250
  });
1246
- Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
1251
+ Object.assign(material.defaultAttributeValues, { shade: [1], uvBounds: [0, 0, 1, 1] });
1247
1252
  return material;
1248
1253
  } catch (e) {
1249
1254
  console.warn(e);
@@ -1408,6 +1413,7 @@ var init_AssetParsers = __esm({
1408
1413
  AssetParser2["IMAGE"] = "image";
1409
1414
  AssetParser2["LIST"] = "list";
1410
1415
  AssetParser2["JSON"] = "json";
1416
+ AssetParser2["BINARY"] = "binary";
1411
1417
  return AssetParser2;
1412
1418
  })(AssetParser || {});
1413
1419
  }
@@ -2691,13 +2697,13 @@ function applyElementRotation(rotation, geometry) {
2691
2697
  break;
2692
2698
  }
2693
2699
  if (rotation.rescale) {
2694
- if (Math.abs(rotation.angle) === 22.5) {
2695
- const s = 1 / Math.cos(0.39269909262657166);
2696
- geometry.scale(s, 1, s);
2697
- } else {
2698
- const s = 1 / Math.cos(0.7853981852531433);
2699
- geometry.scale(s, 1, s);
2700
- }
2700
+ const angle = toRadians(rotation.angle);
2701
+ const s = 1 / Math.max(Math.abs(Math.cos(angle)), Math.abs(Math.sin(angle)));
2702
+ geometry.scale(
2703
+ rotation.axis === "x" /* X */ ? 1 : s,
2704
+ rotation.axis === "y" /* Y */ ? 1 : s,
2705
+ rotation.axis === "z" /* Z */ ? 1 : s
2706
+ );
2701
2707
  }
2702
2708
  geometry.translate(origin.x, origin.y, origin.z);
2703
2709
  }
@@ -2878,7 +2884,7 @@ var init_parser = __esm({
2878
2884
  });
2879
2885
 
2880
2886
  // src/assets/source/HostedAssetSource.ts
2881
- var p3, DEFAULT_OPTIONS, HostedAssetSource;
2887
+ var BINARY_TIMEOUT, p3, DEFAULT_OPTIONS, HostedAssetSource;
2882
2888
  var init_HostedAssetSource = __esm({
2883
2889
  "src/assets/source/HostedAssetSource.ts"() {
2884
2890
  init_AssetSource();
@@ -2891,6 +2897,7 @@ var init_HostedAssetSource = __esm({
2891
2897
  init_AssetLoader();
2892
2898
  init_parser();
2893
2899
  init_merge();
2900
+ BINARY_TIMEOUT = 6e4;
2894
2901
  p3 = prefix("HostedAssetSource");
2895
2902
  DEFAULT_OPTIONS = {
2896
2903
  retryDefaults: true
@@ -2961,6 +2968,17 @@ var init_HostedAssetSource = __esm({
2961
2968
  }
2962
2969
  };
2963
2970
  }
2971
+ static {
2972
+ this.BINARY = {
2973
+ config(request) {
2974
+ request.timeout = BINARY_TIMEOUT;
2975
+ request.responseType = "arraybuffer";
2976
+ },
2977
+ parse(response) {
2978
+ return { data: new Uint8Array(response.data) };
2979
+ }
2980
+ };
2981
+ }
2964
2982
  static {
2965
2983
  this.PARSER_MAP = /* @__PURE__ */ new Map([
2966
2984
  ["model" /* MODEL */, _HostedAssetSource.MODEL],
@@ -2969,7 +2987,8 @@ var init_HostedAssetSource = __esm({
2969
2987
  ["nbt" /* NBT */, _HostedAssetSource.NBT],
2970
2988
  ["image" /* IMAGE */, _HostedAssetSource.IMAGE],
2971
2989
  ["list" /* LIST */, _HostedAssetSource.LIST],
2972
- ["json" /* JSON */, _HostedAssetSource.JSON]
2990
+ ["json" /* JSON */, _HostedAssetSource.JSON],
2991
+ ["binary" /* BINARY */, _HostedAssetSource.BINARY]
2973
2992
  ]);
2974
2993
  }
2975
2994
  /** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
@@ -4855,6 +4874,178 @@ var init_Entities = __esm({
4855
4874
  }
4856
4875
  });
4857
4876
 
4877
+ // src/assets/_fonts/Unihex.ts
4878
+ var Unihex_exports = {};
4879
+ __export(Unihex_exports, {
4880
+ loadUnihex: () => loadUnihex
4881
+ });
4882
+ import { TextWriter, Uint8ArrayReader, ZipReader as ZipReader2 } from "@zip.js/zip.js";
4883
+ function readHex(text2, filename, glyphs) {
4884
+ const lines = text2.split("\n");
4885
+ if (lines[lines.length - 1] === "") lines.pop();
4886
+ lines.forEach((line, index) => {
4887
+ const match = /^([0-9A-F]{4,6}):([0-9A-F]+)$/.exec(line);
4888
+ if (!match || ![32, 64, 96, 128].includes(match[2].length)) {
4889
+ throw new Error(`Invalid Unihex record in ${filename} at line ${index + 1}`);
4890
+ }
4891
+ const codepoint = parseInt(match[1], 16);
4892
+ if (codepoint > 1114111) throw new Error(`Invalid Unihex codepoint in ${filename} at line ${index + 1}`);
4893
+ const bitWidth = match[2].length / 4;
4894
+ const rowLength = bitWidth / 4;
4895
+ const rows = new Uint32Array(GLYPH_HEIGHT);
4896
+ let mask = 0;
4897
+ for (let y = 0; y < GLYPH_HEIGHT; y++) {
4898
+ rows[y] = parseInt(match[2].slice(y * rowLength, (y + 1) * rowLength), 16);
4899
+ mask |= rows[y];
4900
+ }
4901
+ let left = 0, right = bitWidth;
4902
+ if (mask) {
4903
+ while (!(mask >>> bitWidth - left - 1 & 1)) left++;
4904
+ right = bitWidth - 1;
4905
+ while (!(mask >>> bitWidth - right - 1 & 1)) right--;
4906
+ }
4907
+ glyphs.set(codepoint, { rows, bitWidth, left, right });
4908
+ });
4909
+ }
4910
+ async function parseUnihex(key) {
4911
+ try {
4912
+ const asset2 = await AssetLoader.get(key, "binary" /* BINARY */);
4913
+ if (!asset2) throw new Error(`Could not load Unihex archive ${key.toNamespacedString()}`);
4914
+ const records = /* @__PURE__ */ new Map();
4915
+ const reader = new ZipReader2(new Uint8ArrayReader(asset2.data), { useWebWorkers: false, useCompressionStream: false });
4916
+ try {
4917
+ for (const entry of await reader.getEntries()) {
4918
+ if (!entry.directory && entry.filename.endsWith(".hex")) {
4919
+ readHex(await entry.getData(new TextWriter(), { checkSignature: true }), entry.filename, records);
4920
+ }
4921
+ }
4922
+ } finally {
4923
+ await reader.close();
4924
+ }
4925
+ const indices = new Int32Array(1114112).fill(-1);
4926
+ const rows = new Uint32Array(records.size * GLYPH_HEIGHT), metrics = new Uint8Array(records.size * 3);
4927
+ let index = 0;
4928
+ for (const [codepoint, record] of records) {
4929
+ indices[codepoint] = index;
4930
+ rows.set(record.rows, index * GLYPH_HEIGHT);
4931
+ metrics.set([record.bitWidth, record.left, record.right], index * 3);
4932
+ index++;
4933
+ }
4934
+ return { indices, rows, metrics };
4935
+ } catch (error) {
4936
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error);
4937
+ return void 0;
4938
+ }
4939
+ }
4940
+ async function loadUnihex(provider, fontKey, characters) {
4941
+ const origin = new AssetKey(DEFAULT_NAMESPACE, "", void 0, void 0, "assets", "", fontKey.root);
4942
+ const key = AssetKey.parse("font", provider.hex_file, origin);
4943
+ key.assetType = void 0;
4944
+ const glyphs = /* @__PURE__ */ new Map();
4945
+ let overrides;
4946
+ try {
4947
+ overrides = (provider.size_overrides ?? []).map((override) => {
4948
+ const from = override.from.codePointAt(0), to = override.to.codePointAt(0);
4949
+ if ([...override.from].length !== 1 || [...override.to].length !== 1 || from >= to || !Number.isInteger(override.left) || !Number.isInteger(override.right) || override.right < override.left) {
4950
+ throw new Error(`Invalid Unihex size override in font ${fontKey.toNamespacedString()}`);
4951
+ }
4952
+ return { ...override, from, to };
4953
+ });
4954
+ } catch (error) {
4955
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error);
4956
+ return glyphs;
4957
+ }
4958
+ const file = await Caching.unihexCache.get(key.serialize(), () => parseUnihex(key));
4959
+ if (!file) return glyphs;
4960
+ let atlas = atlases.get(provider);
4961
+ if (!atlas) {
4962
+ atlas = new UnihexAtlas();
4963
+ atlases.set(provider, atlas);
4964
+ }
4965
+ for (const character of characters) {
4966
+ const codepoint = character.codePointAt(0), index = file.indices[codepoint];
4967
+ if (index < 0) continue;
4968
+ const record = {
4969
+ rows: file.rows.subarray(index * GLYPH_HEIGHT, (index + 1) * GLYPH_HEIGHT),
4970
+ bitWidth: file.metrics[index * 3],
4971
+ left: file.metrics[index * 3 + 1],
4972
+ right: file.metrics[index * 3 + 2]
4973
+ };
4974
+ const override = overrides.find((range) => codepoint >= range.from && codepoint <= range.to);
4975
+ if (override) {
4976
+ record.left = override.left;
4977
+ record.right = override.right;
4978
+ }
4979
+ const width = record.right - record.left + 1;
4980
+ let rendered;
4981
+ const render = () => rendered ??= atlas.draw(record);
4982
+ glyphs.set(character, {
4983
+ get image() {
4984
+ return render().image;
4985
+ },
4986
+ get x() {
4987
+ return render().x;
4988
+ },
4989
+ get y() {
4990
+ return render().y;
4991
+ },
4992
+ width,
4993
+ height: GLYPH_HEIGHT,
4994
+ scale: 0.5,
4995
+ ascent: 7,
4996
+ advance: Math.floor(width / 2) + 1,
4997
+ boldOffset: 0.5,
4998
+ shadowOffset: 0.5
4999
+ });
5000
+ }
5001
+ return glyphs;
5002
+ }
5003
+ var ATLAS_SIZE, GLYPH_HEIGHT, UnihexAtlas, atlases;
5004
+ var init_Unihex = __esm({
5005
+ "src/assets/_fonts/Unihex.ts"() {
5006
+ init_AssetKey();
5007
+ init_AssetLoader();
5008
+ init_Assets();
5009
+ init_Caching();
5010
+ init_AssetParsers();
5011
+ init_CanvasCompat();
5012
+ ATLAS_SIZE = 512;
5013
+ GLYPH_HEIGHT = 16;
5014
+ UnihexAtlas = class {
5015
+ constructor() {
5016
+ this.x = 0;
5017
+ this.y = 0;
5018
+ }
5019
+ draw(glyph) {
5020
+ const width = glyph.right - glyph.left + 1;
5021
+ if (this.image && this.x + width > this.image.width) {
5022
+ this.x = 0;
5023
+ this.y += GLYPH_HEIGHT;
5024
+ }
5025
+ if (!this.image || this.y + GLYPH_HEIGHT > this.image.height || width > this.image.width) {
5026
+ this.image = createCanvas2(Math.max(ATLAS_SIZE, width), ATLAS_SIZE);
5027
+ this.x = 0;
5028
+ this.y = 0;
5029
+ }
5030
+ const context = this.image.getContext("2d");
5031
+ const pixels = context.createImageData(width, GLYPH_HEIGHT);
5032
+ for (let y = 0; y < GLYPH_HEIGHT; y++) {
5033
+ for (let x = 0; x < width; x++) {
5034
+ const column = glyph.left + x;
5035
+ if (column < 0 || column >= glyph.bitWidth || !(glyph.rows[y] >>> glyph.bitWidth - column - 1 & 1)) continue;
5036
+ pixels.data.fill(255, (y * width + x) * 4, (y * width + x + 1) * 4);
5037
+ }
5038
+ }
5039
+ const result = { image: this.image, x: this.x, y: this.y };
5040
+ context.putImageData(pixels, this.x, this.y);
5041
+ this.x += width;
5042
+ return result;
5043
+ }
5044
+ };
5045
+ atlases = /* @__PURE__ */ new WeakMap();
5046
+ }
5047
+ });
5048
+
4858
5049
  // src/assets/Fonts.ts
4859
5050
  var Fonts;
4860
5051
  var init_Fonts = __esm({
@@ -4865,7 +5056,7 @@ var init_Fonts = __esm({
4865
5056
  init_ModelTextures();
4866
5057
  init_Caching();
4867
5058
  Fonts = class {
4868
- /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
5059
+ /** Combines font definitions in source-priority order. TrueType providers are skipped. */
4869
5060
  static async get(key = "minecraft:default") {
4870
5061
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
4871
5062
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -4881,6 +5072,7 @@ var init_Fonts = __esm({
4881
5072
  const stack = await AssetLoader.getAll(key, "json" /* JSON */);
4882
5073
  if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
4883
5074
  const glyphs = /* @__PURE__ */ new Map();
5075
+ const providers = [];
4884
5076
  for (const asset2 of stack) {
4885
5077
  for (const provider of asset2.providers) {
4886
5078
  if (provider.filter?.uniform || provider.filter?.jp) continue;
@@ -4899,17 +5091,43 @@ var init_Fonts = __esm({
4899
5091
  break;
4900
5092
  case "reference":
4901
5093
  case "minecraft:reference":
4902
- provided = (await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized])).glyphs;
5094
+ const reference = await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized]);
5095
+ if (reference.resolve) {
5096
+ providers.push(async (characters) => {
5097
+ await reference.resolve(characters);
5098
+ return reference.glyphs;
5099
+ });
5100
+ continue;
5101
+ }
5102
+ provided = reference.glyphs;
4903
5103
  break;
5104
+ case "unihex":
5105
+ case "minecraft:unihex":
5106
+ providers.push(async (characters) => (await Promise.resolve().then(() => (init_Unihex(), Unihex_exports))).loadUnihex(provider, key, characters));
5107
+ continue;
4904
5108
  default:
4905
5109
  continue;
4906
5110
  }
4907
- for (const [character, glyph] of provided) {
5111
+ if (providers.every((provider2) => provider2 instanceof Map)) for (const [character, glyph] of provided) {
4908
5112
  if (character !== "\0" && !glyphs.has(character)) glyphs.set(character, glyph);
4909
5113
  }
5114
+ providers.push(provided);
5115
+ }
5116
+ }
5117
+ return { glyphs, resolve: providers.every((provider) => provider instanceof Map) ? void 0 : async (characters) => {
5118
+ const remaining = new Set([...characters].filter((character) => character !== "\0" && character !== "\n" && character !== "\r" && !glyphs.has(character)));
5119
+ for (const provider of providers) {
5120
+ if (!remaining.size) break;
5121
+ const provided = provider instanceof Map ? provider : await provider([...remaining].join(""));
5122
+ for (const character of remaining) {
5123
+ const glyph = provided.get(character);
5124
+ if (glyph) {
5125
+ glyphs.set(character, glyph);
5126
+ remaining.delete(character);
5127
+ }
5128
+ }
4910
5129
  }
4911
- }
4912
- return { glyphs };
5130
+ } };
4913
5131
  }
4914
5132
  static async bitmap(provider, fontKey) {
4915
5133
  const rows = provider.chars.map((row) => [...row]);
@@ -5003,6 +5221,13 @@ var init_EntityAnimation = __esm({
5003
5221
 
5004
5222
  // src/entity/scene/EntityAnimationPlayer.ts
5005
5223
  import { Euler as Euler3, Vector3 as Vector35 } from "three";
5224
+ function validateAnimationWeights(weights, count) {
5225
+ const values = Array.from(weights);
5226
+ if (values.length !== count || values.some((weight) => !Number.isFinite(weight) || weight < 0)) {
5227
+ throw new Error("Animation weights must match the clip count and contain only finite nonnegative numbers");
5228
+ }
5229
+ return values;
5230
+ }
5006
5231
  var ZERO, EntityAnimationPlayer;
5007
5232
  var init_EntityAnimationPlayer = __esm({
5008
5233
  "src/entity/scene/EntityAnimationPlayer.ts"() {
@@ -5011,12 +5236,20 @@ var init_EntityAnimationPlayer = __esm({
5011
5236
  EntityAnimationPlayer = class {
5012
5237
  constructor() {
5013
5238
  this.parts = [];
5239
+ this._animations = [];
5240
+ this._weights = [];
5241
+ this.loops = [];
5014
5242
  this._time = 0;
5015
- this.loop = false;
5016
5243
  this.speed = 1;
5017
5244
  }
5018
5245
  get animation() {
5019
- return this._animation;
5246
+ return this._animations[0];
5247
+ }
5248
+ get animations() {
5249
+ return this._animations.slice();
5250
+ }
5251
+ get weights() {
5252
+ return this._weights.slice();
5020
5253
  }
5021
5254
  get time() {
5022
5255
  return this._time;
@@ -5026,10 +5259,20 @@ var init_EntityAnimationPlayer = __esm({
5026
5259
  * without them, the current pose is used. Stopping restores the pose from before playback.
5027
5260
  */
5028
5261
  play(layers, animation, options = {}, poses) {
5262
+ this.playAnimations(layers, [animation], options, poses);
5263
+ }
5264
+ /**
5265
+ * Starts clips on matching parts with a shared clock. Weighted offsets add to the supplied baked poses
5266
+ * or, without poses, to each animated part's pose when playback starts.
5267
+ */
5268
+ playAnimations(layers, animations, options = {}, poses) {
5269
+ const weights = validateAnimationWeights(options.weights ?? animations.map(() => 1), animations.length);
5029
5270
  this.stop();
5030
- this._animation = animation;
5271
+ this._animations = animations.slice();
5272
+ this._weights = weights;
5273
+ if (!animations.length) return;
5031
5274
  this._time = options.time ?? 0;
5032
- this.loop = options.loop ?? animation.loop;
5275
+ this.loops = animations.map((animation) => options.loop ?? animation.loop);
5033
5276
  this.speed = options.speed ?? 1;
5034
5277
  for (const layer of layers) {
5035
5278
  const objects = [];
@@ -5038,7 +5281,8 @@ var init_EntityAnimationPlayer = __esm({
5038
5281
  if (poses.has(object4)) objects.push(object4);
5039
5282
  });
5040
5283
  } else {
5041
- for (const bone of Object.keys(animation.bones)) {
5284
+ const bones = new Set(animations.flatMap((animation) => Object.keys(animation.bones)));
5285
+ for (const bone of bones) {
5042
5286
  const object4 = layer.getObjectByName(`group:${bone}`);
5043
5287
  if (object4) objects.push(object4);
5044
5288
  }
@@ -5062,7 +5306,7 @@ var init_EntityAnimationPlayer = __esm({
5062
5306
  }
5063
5307
  /** Restores the poses from before playback. Returns whether an animation was active. */
5064
5308
  stop() {
5065
- if (!this._animation) return false;
5309
+ if (!this._animations.length) return false;
5066
5310
  for (const part of this.parts) {
5067
5311
  part.object.position.copy(part.position);
5068
5312
  part.object.rotation.copy(part.rotation);
@@ -5074,12 +5318,14 @@ var init_EntityAnimationPlayer = __esm({
5074
5318
  /** Forgets the animation without touching the parts, for parts that no longer exist. */
5075
5319
  clear() {
5076
5320
  this.parts = [];
5077
- this._animation = void 0;
5321
+ this._animations = [];
5322
+ this._weights = [];
5323
+ this.loops = [];
5078
5324
  this._time = 0;
5079
5325
  }
5080
5326
  /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
5081
5327
  setTime(seconds) {
5082
- if (!this._animation) return false;
5328
+ if (!this._animations.length) return false;
5083
5329
  this._time = seconds;
5084
5330
  this.apply();
5085
5331
  return true;
@@ -5088,8 +5334,30 @@ var init_EntityAnimationPlayer = __esm({
5088
5334
  advance(deltaSeconds) {
5089
5335
  return this.setTime(this._time + deltaSeconds * this.speed);
5090
5336
  }
5337
+ /** Changes weights in clip order and reapplies the current time without restarting playback. */
5338
+ setWeights(weights) {
5339
+ if (!this._animations.length) return false;
5340
+ const validated = validateAnimationWeights(weights, this._animations.length);
5341
+ this._weights = validated;
5342
+ this.apply();
5343
+ return true;
5344
+ }
5091
5345
  apply() {
5092
- const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
5346
+ const pose = /* @__PURE__ */ Object.create(null);
5347
+ for (let i = 0; i < this._animations.length; i++) {
5348
+ const weight = this._weights[i];
5349
+ if (weight === 0) continue;
5350
+ const sampled = sampleEntityAnimation(this._animations[i], this._time, this.loops[i]);
5351
+ for (const [bone, channels] of Object.entries(sampled)) {
5352
+ const blended = pose[bone] ??= {};
5353
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
5354
+ const value = channels[channel];
5355
+ if (!value) continue;
5356
+ const target = blended[channel] ??= [0, 0, 0];
5357
+ for (let axis = 0; axis < 3; axis++) target[axis] += value[axis] * weight;
5358
+ }
5359
+ }
5360
+ }
5093
5361
  for (const part of this.parts) {
5094
5362
  const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone] ?? {};
5095
5363
  part.object.position.set(part.basePosition.x + position[0], part.basePosition.y + position[1], part.basePosition.z + position[2]);
@@ -5713,7 +5981,7 @@ var init_EntityObject = __esm({
5713
5981
  for (const geometry of this.geometries) geometry.dispose();
5714
5982
  this.geometries.clear();
5715
5983
  for (const dispose of this.disposeWireframes.splice(0)) dispose();
5716
- for (const player of this.animationPlayers.splice(0)) player.clear();
5984
+ for (const { player } of this.animationPlayers.splice(0)) player.clear();
5717
5985
  this.partPoses.clear();
5718
5986
  super.disposeAndRemoveAllChildren();
5719
5987
  }
@@ -5748,15 +6016,31 @@ var init_EntityObject = __esm({
5748
6016
  //<editor-fold desc="ANIMATION">
5749
6017
  /** The first active clip, until playback is stopped or the object is disposed. */
5750
6018
  get animation() {
5751
- return this.animationPlayers[0]?.animation;
6019
+ return this.animationPlayers[0]?.player.animation;
5752
6020
  }
5753
- /** Clips playing together, each targeting a separate geometry layer. */
6021
+ /** Clips playing together, in the order passed to {@link playAnimations}. */
5754
6022
  get activeAnimations() {
5755
- return this.animationPlayers.map((player) => player.animation);
6023
+ const animations = [];
6024
+ for (const { player, indices } of this.animationPlayers) {
6025
+ player.animations.forEach((animation, i) => {
6026
+ animations[indices[i]] = animation;
6027
+ });
6028
+ }
6029
+ return animations;
6030
+ }
6031
+ /** Weights of the active clips, in the same order as {@link activeAnimations}. */
6032
+ get animationWeights() {
6033
+ const weights = [];
6034
+ for (const { player, indices } of this.animationPlayers) {
6035
+ player.weights.forEach((weight, i) => {
6036
+ weights[indices[i]] = weight;
6037
+ });
6038
+ }
6039
+ return weights;
5756
6040
  }
5757
6041
  /** Seconds since the animation started, before looping. */
5758
6042
  get animationTime() {
5759
- return this.animationPlayers[0]?.time ?? 0;
6043
+ return this.animationPlayers[0]?.player.time ?? 0;
5760
6044
  }
5761
6045
  /**
5762
6046
  * Plays a clip from {@link Entities.getAnimations}, replacing all active clips.
@@ -5767,42 +6051,59 @@ var init_EntityObject = __esm({
5767
6051
  playAnimation(animation, options) {
5768
6052
  this.playAnimations([animation], options);
5769
6053
  }
5770
- /** Plays clips with a shared clock and no blending. Selected layers without their own clip follow `main`. */
6054
+ /**
6055
+ * Plays clips with a shared clock, adding their weighted offsets within each geometry layer.
6056
+ * Weights default to 1 and are not normalized. Selected layers without their own clip follow `main`.
6057
+ */
5771
6058
  playAnimations(animations, options) {
5772
- const targets = /* @__PURE__ */ new Set();
5773
- const selections = animations.map((animation) => {
6059
+ const weights = validateAnimationWeights(options?.weights ?? animations.map(() => 1), animations.length);
6060
+ const selections = /* @__PURE__ */ new Map();
6061
+ animations.forEach((animation, index) => {
5774
6062
  const layer = animation.layer ?? "main";
5775
- if (targets.has(layer)) throw new Error(`Multiple animations target layer "${layer}"`);
5776
- targets.add(layer);
5777
6063
  const names = Object.keys(this.entityLayers).filter((name) => name.split("#")[0] === layer);
5778
6064
  if (!names.length) throw new Error(`Entity ${this.entity.id} has no selected animation layer "${layer}"`);
5779
- return { animation, names };
6065
+ let selection = selections.get(layer);
6066
+ if (!selection) selections.set(layer, selection = { animations: [], names, indices: [] });
6067
+ selection.animations.push(animation);
6068
+ selection.indices.push(index);
5780
6069
  });
5781
- const main = selections.find(({ animation }) => (animation.layer ?? "main") === "main");
5782
- if (main) main.names.push(...Object.keys(this.entityLayers).filter((name) => !targets.has(name.split("#")[0])));
6070
+ const main = selections.get("main");
6071
+ if (main) main.names.push(...Object.keys(this.entityLayers).filter((name) => !selections.has(name.split("#")[0])));
5783
6072
  this.createMeshes();
5784
6073
  this.stopAnimation();
5785
- for (const { animation, names } of selections) {
6074
+ for (const { animations: clips, names, indices } of selections.values()) {
5786
6075
  const player = new EntityAnimationPlayer();
5787
- player.play(names.map((name) => this.getLayerGroup(name)), animation, options, this.partPoses);
5788
- this.animationPlayers.push(player);
6076
+ player.playAnimations(
6077
+ names.map((name) => this.getLayerGroup(name)),
6078
+ clips,
6079
+ { ...options, weights: indices.map((index) => weights[index]) },
6080
+ this.partPoses
6081
+ );
6082
+ this.animationPlayers.push({ player, indices });
5789
6083
  }
5790
6084
  this.notifyDirty();
5791
6085
  }
6086
+ /** Changes weights in active clip order, notifying the scene without restarting the shared clock. Has no effect without an animation. */
6087
+ setAnimationWeights(weights) {
6088
+ if (!this.animationPlayers.length) return;
6089
+ const values = validateAnimationWeights(weights, this.activeAnimations.length);
6090
+ for (const { player, indices } of this.animationPlayers) player.setWeights(indices.map((index) => values[index]));
6091
+ this.notifyDirty();
6092
+ }
5792
6093
  /** Stops all clips and restores the poses from before playback. */
5793
6094
  stopAnimation() {
5794
6095
  const players = this.animationPlayers.splice(0);
5795
- for (const player of players) player.stop();
6096
+ for (const { player } of players) player.stop();
5796
6097
  if (players.length) this.notifyDirty();
5797
6098
  }
5798
6099
  /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
5799
6100
  setAnimationTime(seconds) {
5800
- for (const player of this.animationPlayers) player.setTime(seconds);
6101
+ for (const { player } of this.animationPlayers) player.setTime(seconds);
5801
6102
  if (this.animationPlayers.length) this.notifyDirty();
5802
6103
  }
5803
6104
  /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
5804
6105
  advanceAnimation(deltaSeconds) {
5805
- for (const player of this.animationPlayers) player.advance(deltaSeconds);
6106
+ for (const { player } of this.animationPlayers) player.advance(deltaSeconds);
5806
6107
  if (this.animationPlayers.length) this.notifyDirty();
5807
6108
  }
5808
6109
  //</editor-fold>
@@ -8064,6 +8365,7 @@ async function layoutGuiText(text2, options = {}) {
8064
8365
  }
8065
8366
  const font = await Fonts.get(options.font);
8066
8367
  const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
8368
+ await font.resolve?.(runs.map((run) => run.text).join(""));
8067
8369
  const glyphs = [], lineWidths = [];
8068
8370
  let line = [], width = 0;
8069
8371
  let trailingNewline = false;
@@ -8090,7 +8392,7 @@ async function layoutGuiText(text2, options = {}) {
8090
8392
  continue;
8091
8393
  }
8092
8394
  const glyph = font.glyphs.get(character);
8093
- const advance = (glyph?.advance ?? 6) + (style.bold ? 1 : 0);
8395
+ const advance = (glyph?.advance ?? 6) + (style.bold ? glyph?.boldOffset ?? 1 : 0);
8094
8396
  line.push({ character, glyph, advance, style, x: 0, y: 0 });
8095
8397
  width += advance;
8096
8398
  while (width > maxWidth && line.length > 1) {
@@ -8118,7 +8420,7 @@ function createGuiTextGeometry(layout, shadow = true) {
8118
8420
  batches.push(batch);
8119
8421
  }
8120
8422
  const color2 = new Color8(isShadow ? (style.color & 16579836) >> 2 : style.color);
8121
- const offset = isShadow ? 1 : 0;
8423
+ const offset = isShadow ? glyph?.shadowOffset ?? 1 : 0;
8122
8424
  const top = glyph ? 7 - glyph.ascent : 0;
8123
8425
  const height = glyph ? glyph.height * glyph.scale : 8;
8124
8426
  const width = glyph ? glyph.width * glyph.scale : 5;
@@ -8148,7 +8450,7 @@ function createGuiTextGeometry(layout, shadow = true) {
8148
8450
  for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
8149
8451
  batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
8150
8452
  };
8151
- for (const boldOffset of style.bold ? [0, 1] : [0]) {
8453
+ for (const boldOffset of style.bold ? [0, glyph?.boldOffset ?? 1] : [0]) {
8152
8454
  if (glyph) quad(0, top, width, top + height, boldOffset);
8153
8455
  else {
8154
8456
  quad(0, 0, 5, 1, boldOffset);
@@ -9928,6 +10230,10 @@ var init_ModelObject = __esm({
9928
10230
  this.atlas.model.elements?.forEach((el) => {
9929
10231
  const elGeo = this._getBoxGeometryFromElement(el).clone();
9930
10232
  this.geometries.add(elGeo);
10233
+ elGeo.setAttribute("shade", new BufferAttribute5(
10234
+ new Float32Array(elGeo.getAttribute("position").count).fill(!this.originalModel.itemId && el.shade === false ? 0 : 1),
10235
+ 1
10236
+ ));
9931
10237
  if (this.options.cullMask) {
9932
10238
  const indices = Array.from(elGeo.getIndex().array);
9933
10239
  elGeo.setIndex(indices.filter((_, index) => {
@@ -17521,9 +17827,9 @@ function cached(map, key, load) {
17521
17827
  function compactQuads(geometry, keep) {
17522
17828
  const quads = keep.flatMap((kept, index) => kept ? [index] : []);
17523
17829
  const compact = new BufferGeometry8();
17524
- for (const [name, size] of Object.entries({ position: 3, normal: 3, uv: 2, uvBounds: 4, color: 3 })) {
17830
+ for (const [name, size] of Object.entries({ position: 3, normal: 3, uv: 2, uvBounds: 4, color: 3, shade: 1 })) {
17525
17831
  const source2 = geometry.getAttribute(name);
17526
- const defaults = name === "uvBounds" ? [0, 0, 1, 1] : name === "color" ? [1, 1, 1] : void 0;
17832
+ const defaults = name === "uvBounds" ? [0, 0, 1, 1] : name === "color" ? [1, 1, 1] : name === "shade" ? [1] : void 0;
17527
17833
  const values = new Float32Array(quads.length * 4 * size);
17528
17834
  for (let q = 0; q < quads.length; q++) {
17529
17835
  for (let vertex = 0; vertex < 4; vertex++) {
@@ -18771,7 +19077,7 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
18771
19077
 
18772
19078
  // src/world/SectionGeometry.ts
18773
19079
  function buildSectionGeometry(input) {
18774
- const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
19080
+ const { count, indices, templates, cullMasks, templateData, atlases: atlases2, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
18775
19081
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
18776
19082
  if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
18777
19083
  if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
@@ -18789,7 +19095,7 @@ function buildSectionGeometry(input) {
18789
19095
  const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
18790
19096
  for (const template of templateData) {
18791
19097
  const { quads } = template;
18792
- if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads || template.biomeTint && template.biomeTint.length !== quads) {
19098
+ if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads || template.shade && template.shade.length !== quads * 4 || template.biomeTint && template.biomeTint.length !== quads) {
18793
19099
  throw new RangeError("Section template arrays do not match its quad count");
18794
19100
  }
18795
19101
  }
@@ -18806,8 +19112,8 @@ function buildSectionGeometry(input) {
18806
19112
  const locations = /* @__PURE__ */ new Map();
18807
19113
  for (const [layer, layerAtlases] of [...visibleAtlases].sort(([a], [b]) => a - b)) {
18808
19114
  const firstPage = pages.length;
18809
- for (const atlas of [...layerAtlases].sort((a, b) => Number(atlases[a].animated) - Number(atlases[b].animated) || (atlases[a].animated ? a - b : atlases[b].height - atlases[a].height))) {
18810
- const { width, height, animated } = atlases[atlas];
19115
+ for (const atlas of [...layerAtlases].sort((a, b) => Number(atlases2[a].animated) - Number(atlases2[b].animated) || (atlases2[a].animated ? a - b : atlases2[b].height - atlases2[a].height))) {
19116
+ const { width, height, animated } = atlases2[atlas];
18811
19117
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
18812
19118
  let selected = -1;
18813
19119
  for (let i = firstPage; !animated && i < pages.length; i++) {
@@ -18850,7 +19156,7 @@ function buildSectionGeometry(input) {
18850
19156
  });
18851
19157
  if (!quads.sprites.length) continue;
18852
19158
  if (!fluidPages.has(fluid)) {
18853
- const { width, height } = atlases[data.atlas];
19159
+ const { width, height } = atlases2[data.atlas];
18854
19160
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
18855
19161
  fluidPages.set(fluid, {
18856
19162
  layer: 2,
@@ -18868,7 +19174,7 @@ function buildSectionGeometry(input) {
18868
19174
  const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
18869
19175
  fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
18870
19176
  }
18871
- pages.push(...[...fluidPages.values()].sort((a, b) => Number(atlases[a.placements[0].atlas].animated) - Number(atlases[b.placements[0].atlas].animated) || a.placements[0].atlas - b.placements[0].atlas));
19177
+ pages.push(...[...fluidPages.values()].sort((a, b) => Number(atlases2[a.placements[0].atlas].animated) - Number(atlases2[b.placements[0].atlas].animated) || a.placements[0].atlas - b.placements[0].atlas));
18872
19178
  }
18873
19179
  return pages.map((page) => {
18874
19180
  let faces = page.fluids.reduce((sum, fluid) => sum + fluid.quads.sprites.length, 0);
@@ -18880,12 +19186,13 @@ function buildSectionGeometry(input) {
18880
19186
  }
18881
19187
  const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
18882
19188
  const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
19189
+ const shade = new Float32Array(faces * 4).fill(1);
18883
19190
  const outputIndices = new Uint32Array(faces * 6);
18884
19191
  const blockIndices = new Uint16Array(faces);
18885
19192
  let vertex = 0, indexOffset = 0;
18886
19193
  for (const entry of page.entries) {
18887
19194
  const template = templateData[templates[entry]];
18888
- const atlas = atlases[template.atlas];
19195
+ const atlas = atlases2[template.atlas];
18889
19196
  const placement = locations.get(template.atlas);
18890
19197
  const mapU = (u) => (placement.x + u * atlas.width) / page.width;
18891
19198
  const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
@@ -18909,6 +19216,7 @@ function buildSectionGeometry(input) {
18909
19216
  normals[vertex * 3] = template.normals[source * 3];
18910
19217
  normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
18911
19218
  normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
19219
+ shade[vertex] = template.shade?.[source] ?? 1;
18912
19220
  uvs[vertex * 2] = mapU(template.uvs[source * 2]);
18913
19221
  uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
18914
19222
  uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
@@ -18924,7 +19232,7 @@ function buildSectionGeometry(input) {
18924
19232
  }
18925
19233
  }
18926
19234
  for (const { quads, x, y, z, data, tint } of page.fluids) {
18927
- const atlas = atlases[data.atlas];
19235
+ const atlas = atlases2[data.atlas];
18928
19236
  const placement = page.placements[0];
18929
19237
  const mapU = (u) => (placement.x + u * atlas.width) / page.width;
18930
19238
  const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
@@ -18961,11 +19269,11 @@ function buildSectionGeometry(input) {
18961
19269
  indexOffset += 6;
18962
19270
  }
18963
19271
  }
18964
- return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices, blockIndices };
19272
+ return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, shade, indices: outputIndices, blockIndices };
18965
19273
  });
18966
19274
  }
18967
19275
  function sectionGeometryTransferables(pages) {
18968
- return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer, page.blockIndices.buffer]);
19276
+ return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.shade.buffer, page.indices.buffer, page.blockIndices.buffer]);
18969
19277
  }
18970
19278
 
18971
19279
  // src/world/SectionWorker.ts
@@ -19026,7 +19334,7 @@ var templateCache = /* @__PURE__ */ new WeakMap();
19026
19334
  var WATER_TINT = new Color12(4159204).toArray();
19027
19335
  function toInput(entries, maxAtlasSize, fluids, lighting) {
19028
19336
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
19029
- const atlases = [];
19337
+ const atlases2 = [];
19030
19338
  const atlasIds = /* @__PURE__ */ new Map();
19031
19339
  const templateIds = /* @__PURE__ */ new Map();
19032
19340
  const input = {
@@ -19049,9 +19357,9 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19049
19357
  if (id === void 0) {
19050
19358
  let atlas = atlasIds.get(template.atlas);
19051
19359
  if (atlas === void 0) {
19052
- atlas = atlases.length;
19360
+ atlas = atlases2.length;
19053
19361
  atlasIds.set(template.atlas, atlas);
19054
- atlases.push(template.atlas);
19362
+ atlases2.push(template.atlas);
19055
19363
  input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height, animated: template.atlas.hasAnimation });
19056
19364
  }
19057
19365
  let data = templateCache.get(template);
@@ -19069,6 +19377,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19069
19377
  uvs: new Float32Array(quads * 8),
19070
19378
  uvBounds: new Float32Array(quads * 16),
19071
19379
  colors: new Float32Array(quads * 12),
19380
+ shade: new Float32Array(quads * 4),
19072
19381
  indices: new Uint16Array(quads * 6),
19073
19382
  cullFaces: new Uint8Array(template.cullFaces),
19074
19383
  biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
@@ -19080,6 +19389,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19080
19389
  const uv = template.geometry.getAttribute("uv");
19081
19390
  const bounds = template.geometry.getAttribute("uvBounds");
19082
19391
  const color2 = template.geometry.getAttribute("color");
19392
+ const shade = template.geometry.getAttribute("shade");
19083
19393
  for (let vertex = 0; vertex < quads * 4; vertex++) {
19084
19394
  data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
19085
19395
  data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
@@ -19091,6 +19401,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19091
19401
  bounds?.getW(vertex) ?? 1
19092
19402
  ], vertex * 4);
19093
19403
  data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
19404
+ data.shade[vertex] = shade?.getX(vertex) ?? 1;
19094
19405
  }
19095
19406
  for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - 4 * Math.floor(i2 / 6);
19096
19407
  templateCache.set(template, data);
@@ -19110,9 +19421,9 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19110
19421
  if (!atlas) continue;
19111
19422
  let id = atlasIds.get(atlas);
19112
19423
  if (id === void 0) {
19113
- id = atlases.length;
19424
+ id = atlases2.length;
19114
19425
  atlasIds.set(atlas, id);
19115
- atlases.push(atlas);
19426
+ atlases2.push(atlas);
19116
19427
  input.atlases.push({ width: atlas.image.width, height: atlas.image.height, animated: atlas.hasAnimation });
19117
19428
  }
19118
19429
  input.fluids[kind] = {
@@ -19123,7 +19434,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19123
19434
  };
19124
19435
  }
19125
19436
  }
19126
- return { input, atlases };
19437
+ return { input, atlases: atlases2 };
19127
19438
  }
19128
19439
  var SectionMesh = class _SectionMesh extends Group3 {
19129
19440
  constructor() {
@@ -19138,23 +19449,23 @@ var SectionMesh = class _SectionMesh extends Group3 {
19138
19449
  }
19139
19450
  /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
19140
19451
  static build(entries, maxAtlasSize = 2048, fluids, lighting) {
19141
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
19142
- return this.fromPages(buildSectionGeometry(input), atlases);
19452
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
19453
+ return this.fromPages(buildSectionGeometry(input), atlases2);
19143
19454
  }
19144
19455
  /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
19145
19456
  static async buildAsync(entries, maxAtlasSize = 2048, fluids, lighting) {
19146
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
19457
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
19147
19458
  const worker = SectionWorker.shared();
19148
19459
  const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
19149
- return this.fromPages(pages, atlases);
19460
+ return this.fromPages(pages, atlases2);
19150
19461
  }
19151
- static fromPages(pages, atlases) {
19462
+ static fromPages(pages, atlases2) {
19152
19463
  const section = new _SectionMesh();
19153
19464
  for (const page of pages) {
19154
19465
  const canvas = createCanvas2(page.width, page.height);
19155
19466
  const context = canvas.getContext("2d");
19156
19467
  for (const { atlas, x, y } of page.placements) {
19157
- context.drawImage(atlases[atlas].image.canvas, x, y);
19468
+ context.drawImage(atlases2[atlas].image.canvas, x, y);
19158
19469
  }
19159
19470
  const geometry = new BufferGeometry9();
19160
19471
  geometry.setAttribute("position", new Float32BufferAttribute10(page.positions.buffer, 3));
@@ -19162,10 +19473,11 @@ var SectionMesh = class _SectionMesh extends Group3 {
19162
19473
  geometry.setAttribute("uv", new Float32BufferAttribute10(page.uvs.buffer, 2));
19163
19474
  geometry.setAttribute("uvBounds", new Float32BufferAttribute10(page.uvBounds.buffer, 4));
19164
19475
  geometry.setAttribute("color", new Float32BufferAttribute10(page.colors.buffer, 3));
19476
+ geometry.setAttribute("shade", new Float32BufferAttribute10(page.shade.buffer, 1));
19165
19477
  geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));
19166
19478
  geometry.computeBoundingBox();
19167
19479
  geometry.computeBoundingSphere();
19168
- const transparent = page.layer === 2 ? atlases[page.placements[0].atlas].hasTranslucency : page.layer === 1;
19480
+ const transparent = page.layer === 2 ? atlases2[page.placements[0].atlas].hasTranslucency : page.layer === 1;
19169
19481
  const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
19170
19482
  material.side = FrontSide4;
19171
19483
  if (page.layer === 2) {
@@ -19180,7 +19492,7 @@ var SectionMesh = class _SectionMesh extends Group3 {
19180
19492
  section.blockIndices.set(mesh, page.blockIndices);
19181
19493
  const unsubscribe = [];
19182
19494
  for (const { atlas: id, x, y } of page.placements) {
19183
- const atlas = atlases[id];
19495
+ const atlas = atlases2[id];
19184
19496
  if (!atlas.hasAnimation) continue;
19185
19497
  unsubscribe.push(atlas.subscribe(() => {
19186
19498
  context.clearRect(x, y, atlas.image.width, atlas.image.height);
@@ -20523,6 +20835,7 @@ export {
20523
20835
  toRadians,
20524
20836
  toTrianglesDrawMode,
20525
20837
  trimCanvas,
20838
+ validateAnimationWeights,
20526
20839
  yieldToEventLoop
20527
20840
  };
20528
20841
  //# sourceMappingURL=index.mjs.map