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
@@ -310,6 +310,9 @@ var init_Caching = __esm({
310
310
  static {
311
311
  this.blockEntityIndexCache = _Caching.createAssetCache();
312
312
  }
313
+ static {
314
+ this.unihexCache = _Caching.createAssetCache();
315
+ }
313
316
  static {
314
317
  this.fontCache = _Caching.createAssetCache();
315
318
  }
@@ -352,6 +355,7 @@ var init_Caching = __esm({
352
355
  this.entityAnimationCache,
353
356
  this.blockEntityIndexCache,
354
357
  this.fontCache,
358
+ this.unihexCache,
355
359
  this.bannerPatternCache,
356
360
  this.mineRenderDataCache,
357
361
  this.usernameUuidCache,
@@ -1113,6 +1117,7 @@ var init_Materials = __esm({
1113
1117
  static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
1114
1118
  const vertShader = `
1115
1119
  uniform bool SHADE;
1120
+ attribute float shade;
1116
1121
 
1117
1122
  // Keep MSAA edge pixels from sampling outside the face's atlas region.
1118
1123
  #if __VERSION__ >= 300
@@ -1135,7 +1140,7 @@ var init_Materials = __esm({
1135
1140
  void main()
1136
1141
  {
1137
1142
 
1138
- if (SHADE) {
1143
+ if (SHADE && shade > 0.5) {
1139
1144
 
1140
1145
  #ifdef USE_INSTANCING
1141
1146
  vec3 N = vec3( modelMatrix * instanceMatrix * vec4(normal, 0.0) );
@@ -1244,7 +1249,7 @@ var init_Materials = __esm({
1244
1249
  side: transparent ? import_three5.DoubleSide : import_three5.FrontSide,
1245
1250
  alphaTest: 0.5
1246
1251
  });
1247
- Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
1252
+ Object.assign(material.defaultAttributeValues, { shade: [1], uvBounds: [0, 0, 1, 1] });
1248
1253
  return material;
1249
1254
  } catch (e) {
1250
1255
  console.warn(e);
@@ -1409,6 +1414,7 @@ var init_AssetParsers = __esm({
1409
1414
  AssetParser2["IMAGE"] = "image";
1410
1415
  AssetParser2["LIST"] = "list";
1411
1416
  AssetParser2["JSON"] = "json";
1417
+ AssetParser2["BINARY"] = "binary";
1412
1418
  return AssetParser2;
1413
1419
  })(AssetParser || {});
1414
1420
  }
@@ -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
  }
@@ -2881,7 +2887,7 @@ var init_parser = __esm({
2881
2887
  });
2882
2888
 
2883
2889
  // src/assets/source/HostedAssetSource.ts
2884
- var p3, DEFAULT_OPTIONS, HostedAssetSource;
2890
+ var BINARY_TIMEOUT, p3, DEFAULT_OPTIONS, HostedAssetSource;
2885
2891
  var init_HostedAssetSource = __esm({
2886
2892
  "src/assets/source/HostedAssetSource.ts"() {
2887
2893
  init_AssetSource();
@@ -2894,6 +2900,7 @@ var init_HostedAssetSource = __esm({
2894
2900
  init_AssetLoader();
2895
2901
  init_parser();
2896
2902
  init_merge();
2903
+ BINARY_TIMEOUT = 6e4;
2897
2904
  p3 = prefix("HostedAssetSource");
2898
2905
  DEFAULT_OPTIONS = {
2899
2906
  retryDefaults: true
@@ -2964,6 +2971,17 @@ var init_HostedAssetSource = __esm({
2964
2971
  }
2965
2972
  };
2966
2973
  }
2974
+ static {
2975
+ this.BINARY = {
2976
+ config(request) {
2977
+ request.timeout = BINARY_TIMEOUT;
2978
+ request.responseType = "arraybuffer";
2979
+ },
2980
+ parse(response) {
2981
+ return { data: new Uint8Array(response.data) };
2982
+ }
2983
+ };
2984
+ }
2967
2985
  static {
2968
2986
  this.PARSER_MAP = /* @__PURE__ */ new Map([
2969
2987
  ["model" /* MODEL */, _HostedAssetSource.MODEL],
@@ -2972,7 +2990,8 @@ var init_HostedAssetSource = __esm({
2972
2990
  ["nbt" /* NBT */, _HostedAssetSource.NBT],
2973
2991
  ["image" /* IMAGE */, _HostedAssetSource.IMAGE],
2974
2992
  ["list" /* LIST */, _HostedAssetSource.LIST],
2975
- ["json" /* JSON */, _HostedAssetSource.JSON]
2993
+ ["json" /* JSON */, _HostedAssetSource.JSON],
2994
+ ["binary" /* BINARY */, _HostedAssetSource.BINARY]
2976
2995
  ]);
2977
2996
  }
2978
2997
  /** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
@@ -4858,6 +4877,178 @@ var init_Entities = __esm({
4858
4877
  }
4859
4878
  });
4860
4879
 
4880
+ // src/assets/_fonts/Unihex.ts
4881
+ var Unihex_exports = {};
4882
+ __export(Unihex_exports, {
4883
+ loadUnihex: () => loadUnihex
4884
+ });
4885
+ function readHex(text2, filename, glyphs) {
4886
+ const lines = text2.split("\n");
4887
+ if (lines[lines.length - 1] === "") lines.pop();
4888
+ lines.forEach((line, index) => {
4889
+ const match = /^([0-9A-F]{4,6}):([0-9A-F]+)$/.exec(line);
4890
+ if (!match || ![32, 64, 96, 128].includes(match[2].length)) {
4891
+ throw new Error(`Invalid Unihex record in ${filename} at line ${index + 1}`);
4892
+ }
4893
+ const codepoint = parseInt(match[1], 16);
4894
+ if (codepoint > 1114111) throw new Error(`Invalid Unihex codepoint in ${filename} at line ${index + 1}`);
4895
+ const bitWidth = match[2].length / 4;
4896
+ const rowLength = bitWidth / 4;
4897
+ const rows = new Uint32Array(GLYPH_HEIGHT);
4898
+ let mask = 0;
4899
+ for (let y = 0; y < GLYPH_HEIGHT; y++) {
4900
+ rows[y] = parseInt(match[2].slice(y * rowLength, (y + 1) * rowLength), 16);
4901
+ mask |= rows[y];
4902
+ }
4903
+ let left = 0, right = bitWidth;
4904
+ if (mask) {
4905
+ while (!(mask >>> bitWidth - left - 1 & 1)) left++;
4906
+ right = bitWidth - 1;
4907
+ while (!(mask >>> bitWidth - right - 1 & 1)) right--;
4908
+ }
4909
+ glyphs.set(codepoint, { rows, bitWidth, left, right });
4910
+ });
4911
+ }
4912
+ async function parseUnihex(key) {
4913
+ try {
4914
+ const asset2 = await AssetLoader.get(key, "binary" /* BINARY */);
4915
+ if (!asset2) throw new Error(`Could not load Unihex archive ${key.toNamespacedString()}`);
4916
+ const records = /* @__PURE__ */ new Map();
4917
+ const reader = new import_zip2.ZipReader(new import_zip2.Uint8ArrayReader(asset2.data), { useWebWorkers: false, useCompressionStream: false });
4918
+ try {
4919
+ for (const entry of await reader.getEntries()) {
4920
+ if (!entry.directory && entry.filename.endsWith(".hex")) {
4921
+ readHex(await entry.getData(new import_zip2.TextWriter(), { checkSignature: true }), entry.filename, records);
4922
+ }
4923
+ }
4924
+ } finally {
4925
+ await reader.close();
4926
+ }
4927
+ const indices = new Int32Array(1114112).fill(-1);
4928
+ const rows = new Uint32Array(records.size * GLYPH_HEIGHT), metrics = new Uint8Array(records.size * 3);
4929
+ let index = 0;
4930
+ for (const [codepoint, record] of records) {
4931
+ indices[codepoint] = index;
4932
+ rows.set(record.rows, index * GLYPH_HEIGHT);
4933
+ metrics.set([record.bitWidth, record.left, record.right], index * 3);
4934
+ index++;
4935
+ }
4936
+ return { indices, rows, metrics };
4937
+ } catch (error) {
4938
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error);
4939
+ return void 0;
4940
+ }
4941
+ }
4942
+ async function loadUnihex(provider, fontKey, characters) {
4943
+ const origin = new AssetKey(DEFAULT_NAMESPACE, "", void 0, void 0, "assets", "", fontKey.root);
4944
+ const key = AssetKey.parse("font", provider.hex_file, origin);
4945
+ key.assetType = void 0;
4946
+ const glyphs = /* @__PURE__ */ new Map();
4947
+ let overrides;
4948
+ try {
4949
+ overrides = (provider.size_overrides ?? []).map((override) => {
4950
+ const from = override.from.codePointAt(0), to = override.to.codePointAt(0);
4951
+ if ([...override.from].length !== 1 || [...override.to].length !== 1 || from >= to || !Number.isInteger(override.left) || !Number.isInteger(override.right) || override.right < override.left) {
4952
+ throw new Error(`Invalid Unihex size override in font ${fontKey.toNamespacedString()}`);
4953
+ }
4954
+ return { ...override, from, to };
4955
+ });
4956
+ } catch (error) {
4957
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error);
4958
+ return glyphs;
4959
+ }
4960
+ const file = await Caching.unihexCache.get(key.serialize(), () => parseUnihex(key));
4961
+ if (!file) return glyphs;
4962
+ let atlas = atlases.get(provider);
4963
+ if (!atlas) {
4964
+ atlas = new UnihexAtlas();
4965
+ atlases.set(provider, atlas);
4966
+ }
4967
+ for (const character of characters) {
4968
+ const codepoint = character.codePointAt(0), index = file.indices[codepoint];
4969
+ if (index < 0) continue;
4970
+ const record = {
4971
+ rows: file.rows.subarray(index * GLYPH_HEIGHT, (index + 1) * GLYPH_HEIGHT),
4972
+ bitWidth: file.metrics[index * 3],
4973
+ left: file.metrics[index * 3 + 1],
4974
+ right: file.metrics[index * 3 + 2]
4975
+ };
4976
+ const override = overrides.find((range) => codepoint >= range.from && codepoint <= range.to);
4977
+ if (override) {
4978
+ record.left = override.left;
4979
+ record.right = override.right;
4980
+ }
4981
+ const width = record.right - record.left + 1;
4982
+ let rendered;
4983
+ const render = () => rendered ??= atlas.draw(record);
4984
+ glyphs.set(character, {
4985
+ get image() {
4986
+ return render().image;
4987
+ },
4988
+ get x() {
4989
+ return render().x;
4990
+ },
4991
+ get y() {
4992
+ return render().y;
4993
+ },
4994
+ width,
4995
+ height: GLYPH_HEIGHT,
4996
+ scale: 0.5,
4997
+ ascent: 7,
4998
+ advance: Math.floor(width / 2) + 1,
4999
+ boldOffset: 0.5,
5000
+ shadowOffset: 0.5
5001
+ });
5002
+ }
5003
+ return glyphs;
5004
+ }
5005
+ var import_zip2, ATLAS_SIZE, GLYPH_HEIGHT, UnihexAtlas, atlases;
5006
+ var init_Unihex = __esm({
5007
+ "src/assets/_fonts/Unihex.ts"() {
5008
+ import_zip2 = require("@zip.js/zip.js");
5009
+ init_AssetKey();
5010
+ init_AssetLoader();
5011
+ init_Assets();
5012
+ init_Caching();
5013
+ init_AssetParsers();
5014
+ init_CanvasCompat();
5015
+ ATLAS_SIZE = 512;
5016
+ GLYPH_HEIGHT = 16;
5017
+ UnihexAtlas = class {
5018
+ constructor() {
5019
+ this.x = 0;
5020
+ this.y = 0;
5021
+ }
5022
+ draw(glyph) {
5023
+ const width = glyph.right - glyph.left + 1;
5024
+ if (this.image && this.x + width > this.image.width) {
5025
+ this.x = 0;
5026
+ this.y += GLYPH_HEIGHT;
5027
+ }
5028
+ if (!this.image || this.y + GLYPH_HEIGHT > this.image.height || width > this.image.width) {
5029
+ this.image = createCanvas2(Math.max(ATLAS_SIZE, width), ATLAS_SIZE);
5030
+ this.x = 0;
5031
+ this.y = 0;
5032
+ }
5033
+ const context = this.image.getContext("2d");
5034
+ const pixels = context.createImageData(width, GLYPH_HEIGHT);
5035
+ for (let y = 0; y < GLYPH_HEIGHT; y++) {
5036
+ for (let x = 0; x < width; x++) {
5037
+ const column = glyph.left + x;
5038
+ if (column < 0 || column >= glyph.bitWidth || !(glyph.rows[y] >>> glyph.bitWidth - column - 1 & 1)) continue;
5039
+ pixels.data.fill(255, (y * width + x) * 4, (y * width + x + 1) * 4);
5040
+ }
5041
+ }
5042
+ const result = { image: this.image, x: this.x, y: this.y };
5043
+ context.putImageData(pixels, this.x, this.y);
5044
+ this.x += width;
5045
+ return result;
5046
+ }
5047
+ };
5048
+ atlases = /* @__PURE__ */ new WeakMap();
5049
+ }
5050
+ });
5051
+
4861
5052
  // src/assets/Fonts.ts
4862
5053
  var Fonts;
4863
5054
  var init_Fonts = __esm({
@@ -4868,7 +5059,7 @@ var init_Fonts = __esm({
4868
5059
  init_ModelTextures();
4869
5060
  init_Caching();
4870
5061
  Fonts = class {
4871
- /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
5062
+ /** Combines font definitions in source-priority order. TrueType providers are skipped. */
4872
5063
  static async get(key = "minecraft:default") {
4873
5064
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
4874
5065
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -4884,6 +5075,7 @@ var init_Fonts = __esm({
4884
5075
  const stack = await AssetLoader.getAll(key, "json" /* JSON */);
4885
5076
  if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
4886
5077
  const glyphs = /* @__PURE__ */ new Map();
5078
+ const providers = [];
4887
5079
  for (const asset2 of stack) {
4888
5080
  for (const provider of asset2.providers) {
4889
5081
  if (provider.filter?.uniform || provider.filter?.jp) continue;
@@ -4902,17 +5094,43 @@ var init_Fonts = __esm({
4902
5094
  break;
4903
5095
  case "reference":
4904
5096
  case "minecraft:reference":
4905
- provided = (await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized])).glyphs;
5097
+ const reference = await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized]);
5098
+ if (reference.resolve) {
5099
+ providers.push(async (characters) => {
5100
+ await reference.resolve(characters);
5101
+ return reference.glyphs;
5102
+ });
5103
+ continue;
5104
+ }
5105
+ provided = reference.glyphs;
4906
5106
  break;
5107
+ case "unihex":
5108
+ case "minecraft:unihex":
5109
+ providers.push(async (characters) => (await Promise.resolve().then(() => (init_Unihex(), Unihex_exports))).loadUnihex(provider, key, characters));
5110
+ continue;
4907
5111
  default:
4908
5112
  continue;
4909
5113
  }
4910
- for (const [character, glyph] of provided) {
5114
+ if (providers.every((provider2) => provider2 instanceof Map)) for (const [character, glyph] of provided) {
4911
5115
  if (character !== "\0" && !glyphs.has(character)) glyphs.set(character, glyph);
4912
5116
  }
5117
+ providers.push(provided);
5118
+ }
5119
+ }
5120
+ return { glyphs, resolve: providers.every((provider) => provider instanceof Map) ? void 0 : async (characters) => {
5121
+ const remaining = new Set([...characters].filter((character) => character !== "\0" && character !== "\n" && character !== "\r" && !glyphs.has(character)));
5122
+ for (const provider of providers) {
5123
+ if (!remaining.size) break;
5124
+ const provided = provider instanceof Map ? provider : await provider([...remaining].join(""));
5125
+ for (const character of remaining) {
5126
+ const glyph = provided.get(character);
5127
+ if (glyph) {
5128
+ glyphs.set(character, glyph);
5129
+ remaining.delete(character);
5130
+ }
5131
+ }
4913
5132
  }
4914
- }
4915
- return { glyphs };
5133
+ } };
4916
5134
  }
4917
5135
  static async bitmap(provider, fontKey) {
4918
5136
  const rows = provider.chars.map((row) => [...row]);
@@ -5005,6 +5223,13 @@ var init_EntityAnimation = __esm({
5005
5223
  });
5006
5224
 
5007
5225
  // src/entity/scene/EntityAnimationPlayer.ts
5226
+ function validateAnimationWeights(weights, count) {
5227
+ const values = Array.from(weights);
5228
+ if (values.length !== count || values.some((weight) => !Number.isFinite(weight) || weight < 0)) {
5229
+ throw new Error("Animation weights must match the clip count and contain only finite nonnegative numbers");
5230
+ }
5231
+ return values;
5232
+ }
5008
5233
  var import_three11, ZERO, EntityAnimationPlayer;
5009
5234
  var init_EntityAnimationPlayer = __esm({
5010
5235
  "src/entity/scene/EntityAnimationPlayer.ts"() {
@@ -5014,12 +5239,20 @@ var init_EntityAnimationPlayer = __esm({
5014
5239
  EntityAnimationPlayer = class {
5015
5240
  constructor() {
5016
5241
  this.parts = [];
5242
+ this._animations = [];
5243
+ this._weights = [];
5244
+ this.loops = [];
5017
5245
  this._time = 0;
5018
- this.loop = false;
5019
5246
  this.speed = 1;
5020
5247
  }
5021
5248
  get animation() {
5022
- return this._animation;
5249
+ return this._animations[0];
5250
+ }
5251
+ get animations() {
5252
+ return this._animations.slice();
5253
+ }
5254
+ get weights() {
5255
+ return this._weights.slice();
5023
5256
  }
5024
5257
  get time() {
5025
5258
  return this._time;
@@ -5029,10 +5262,20 @@ var init_EntityAnimationPlayer = __esm({
5029
5262
  * without them, the current pose is used. Stopping restores the pose from before playback.
5030
5263
  */
5031
5264
  play(layers, animation, options = {}, poses) {
5265
+ this.playAnimations(layers, [animation], options, poses);
5266
+ }
5267
+ /**
5268
+ * Starts clips on matching parts with a shared clock. Weighted offsets add to the supplied baked poses
5269
+ * or, without poses, to each animated part's pose when playback starts.
5270
+ */
5271
+ playAnimations(layers, animations, options = {}, poses) {
5272
+ const weights = validateAnimationWeights(options.weights ?? animations.map(() => 1), animations.length);
5032
5273
  this.stop();
5033
- this._animation = animation;
5274
+ this._animations = animations.slice();
5275
+ this._weights = weights;
5276
+ if (!animations.length) return;
5034
5277
  this._time = options.time ?? 0;
5035
- this.loop = options.loop ?? animation.loop;
5278
+ this.loops = animations.map((animation) => options.loop ?? animation.loop);
5036
5279
  this.speed = options.speed ?? 1;
5037
5280
  for (const layer of layers) {
5038
5281
  const objects = [];
@@ -5041,7 +5284,8 @@ var init_EntityAnimationPlayer = __esm({
5041
5284
  if (poses.has(object4)) objects.push(object4);
5042
5285
  });
5043
5286
  } else {
5044
- for (const bone of Object.keys(animation.bones)) {
5287
+ const bones = new Set(animations.flatMap((animation) => Object.keys(animation.bones)));
5288
+ for (const bone of bones) {
5045
5289
  const object4 = layer.getObjectByName(`group:${bone}`);
5046
5290
  if (object4) objects.push(object4);
5047
5291
  }
@@ -5065,7 +5309,7 @@ var init_EntityAnimationPlayer = __esm({
5065
5309
  }
5066
5310
  /** Restores the poses from before playback. Returns whether an animation was active. */
5067
5311
  stop() {
5068
- if (!this._animation) return false;
5312
+ if (!this._animations.length) return false;
5069
5313
  for (const part of this.parts) {
5070
5314
  part.object.position.copy(part.position);
5071
5315
  part.object.rotation.copy(part.rotation);
@@ -5077,12 +5321,14 @@ var init_EntityAnimationPlayer = __esm({
5077
5321
  /** Forgets the animation without touching the parts, for parts that no longer exist. */
5078
5322
  clear() {
5079
5323
  this.parts = [];
5080
- this._animation = void 0;
5324
+ this._animations = [];
5325
+ this._weights = [];
5326
+ this.loops = [];
5081
5327
  this._time = 0;
5082
5328
  }
5083
5329
  /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
5084
5330
  setTime(seconds) {
5085
- if (!this._animation) return false;
5331
+ if (!this._animations.length) return false;
5086
5332
  this._time = seconds;
5087
5333
  this.apply();
5088
5334
  return true;
@@ -5091,8 +5337,30 @@ var init_EntityAnimationPlayer = __esm({
5091
5337
  advance(deltaSeconds) {
5092
5338
  return this.setTime(this._time + deltaSeconds * this.speed);
5093
5339
  }
5340
+ /** Changes weights in clip order and reapplies the current time without restarting playback. */
5341
+ setWeights(weights) {
5342
+ if (!this._animations.length) return false;
5343
+ const validated = validateAnimationWeights(weights, this._animations.length);
5344
+ this._weights = validated;
5345
+ this.apply();
5346
+ return true;
5347
+ }
5094
5348
  apply() {
5095
- const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
5349
+ const pose = /* @__PURE__ */ Object.create(null);
5350
+ for (let i = 0; i < this._animations.length; i++) {
5351
+ const weight = this._weights[i];
5352
+ if (weight === 0) continue;
5353
+ const sampled = sampleEntityAnimation(this._animations[i], this._time, this.loops[i]);
5354
+ for (const [bone, channels] of Object.entries(sampled)) {
5355
+ const blended = pose[bone] ??= {};
5356
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
5357
+ const value = channels[channel];
5358
+ if (!value) continue;
5359
+ const target = blended[channel] ??= [0, 0, 0];
5360
+ for (let axis = 0; axis < 3; axis++) target[axis] += value[axis] * weight;
5361
+ }
5362
+ }
5363
+ }
5096
5364
  for (const part of this.parts) {
5097
5365
  const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone] ?? {};
5098
5366
  part.object.position.set(part.basePosition.x + position[0], part.basePosition.y + position[1], part.basePosition.z + position[2]);
@@ -5716,7 +5984,7 @@ var init_EntityObject = __esm({
5716
5984
  for (const geometry of this.geometries) geometry.dispose();
5717
5985
  this.geometries.clear();
5718
5986
  for (const dispose of this.disposeWireframes.splice(0)) dispose();
5719
- for (const player of this.animationPlayers.splice(0)) player.clear();
5987
+ for (const { player } of this.animationPlayers.splice(0)) player.clear();
5720
5988
  this.partPoses.clear();
5721
5989
  super.disposeAndRemoveAllChildren();
5722
5990
  }
@@ -5751,15 +6019,31 @@ var init_EntityObject = __esm({
5751
6019
  //<editor-fold desc="ANIMATION">
5752
6020
  /** The first active clip, until playback is stopped or the object is disposed. */
5753
6021
  get animation() {
5754
- return this.animationPlayers[0]?.animation;
6022
+ return this.animationPlayers[0]?.player.animation;
5755
6023
  }
5756
- /** Clips playing together, each targeting a separate geometry layer. */
6024
+ /** Clips playing together, in the order passed to {@link playAnimations}. */
5757
6025
  get activeAnimations() {
5758
- return this.animationPlayers.map((player) => player.animation);
6026
+ const animations = [];
6027
+ for (const { player, indices } of this.animationPlayers) {
6028
+ player.animations.forEach((animation, i) => {
6029
+ animations[indices[i]] = animation;
6030
+ });
6031
+ }
6032
+ return animations;
6033
+ }
6034
+ /** Weights of the active clips, in the same order as {@link activeAnimations}. */
6035
+ get animationWeights() {
6036
+ const weights = [];
6037
+ for (const { player, indices } of this.animationPlayers) {
6038
+ player.weights.forEach((weight, i) => {
6039
+ weights[indices[i]] = weight;
6040
+ });
6041
+ }
6042
+ return weights;
5759
6043
  }
5760
6044
  /** Seconds since the animation started, before looping. */
5761
6045
  get animationTime() {
5762
- return this.animationPlayers[0]?.time ?? 0;
6046
+ return this.animationPlayers[0]?.player.time ?? 0;
5763
6047
  }
5764
6048
  /**
5765
6049
  * Plays a clip from {@link Entities.getAnimations}, replacing all active clips.
@@ -5770,42 +6054,59 @@ var init_EntityObject = __esm({
5770
6054
  playAnimation(animation, options) {
5771
6055
  this.playAnimations([animation], options);
5772
6056
  }
5773
- /** Plays clips with a shared clock and no blending. Selected layers without their own clip follow `main`. */
6057
+ /**
6058
+ * Plays clips with a shared clock, adding their weighted offsets within each geometry layer.
6059
+ * Weights default to 1 and are not normalized. Selected layers without their own clip follow `main`.
6060
+ */
5774
6061
  playAnimations(animations, options) {
5775
- const targets = /* @__PURE__ */ new Set();
5776
- const selections = animations.map((animation) => {
6062
+ const weights = validateAnimationWeights(options?.weights ?? animations.map(() => 1), animations.length);
6063
+ const selections = /* @__PURE__ */ new Map();
6064
+ animations.forEach((animation, index) => {
5777
6065
  const layer = animation.layer ?? "main";
5778
- if (targets.has(layer)) throw new Error(`Multiple animations target layer "${layer}"`);
5779
- targets.add(layer);
5780
6066
  const names = Object.keys(this.entityLayers).filter((name) => name.split("#")[0] === layer);
5781
6067
  if (!names.length) throw new Error(`Entity ${this.entity.id} has no selected animation layer "${layer}"`);
5782
- return { animation, names };
6068
+ let selection = selections.get(layer);
6069
+ if (!selection) selections.set(layer, selection = { animations: [], names, indices: [] });
6070
+ selection.animations.push(animation);
6071
+ selection.indices.push(index);
5783
6072
  });
5784
- const main = selections.find(({ animation }) => (animation.layer ?? "main") === "main");
5785
- if (main) main.names.push(...Object.keys(this.entityLayers).filter((name) => !targets.has(name.split("#")[0])));
6073
+ const main = selections.get("main");
6074
+ if (main) main.names.push(...Object.keys(this.entityLayers).filter((name) => !selections.has(name.split("#")[0])));
5786
6075
  this.createMeshes();
5787
6076
  this.stopAnimation();
5788
- for (const { animation, names } of selections) {
6077
+ for (const { animations: clips, names, indices } of selections.values()) {
5789
6078
  const player = new EntityAnimationPlayer();
5790
- player.play(names.map((name) => this.getLayerGroup(name)), animation, options, this.partPoses);
5791
- this.animationPlayers.push(player);
6079
+ player.playAnimations(
6080
+ names.map((name) => this.getLayerGroup(name)),
6081
+ clips,
6082
+ { ...options, weights: indices.map((index) => weights[index]) },
6083
+ this.partPoses
6084
+ );
6085
+ this.animationPlayers.push({ player, indices });
5792
6086
  }
5793
6087
  this.notifyDirty();
5794
6088
  }
6089
+ /** Changes weights in active clip order, notifying the scene without restarting the shared clock. Has no effect without an animation. */
6090
+ setAnimationWeights(weights) {
6091
+ if (!this.animationPlayers.length) return;
6092
+ const values = validateAnimationWeights(weights, this.activeAnimations.length);
6093
+ for (const { player, indices } of this.animationPlayers) player.setWeights(indices.map((index) => values[index]));
6094
+ this.notifyDirty();
6095
+ }
5795
6096
  /** Stops all clips and restores the poses from before playback. */
5796
6097
  stopAnimation() {
5797
6098
  const players = this.animationPlayers.splice(0);
5798
- for (const player of players) player.stop();
6099
+ for (const { player } of players) player.stop();
5799
6100
  if (players.length) this.notifyDirty();
5800
6101
  }
5801
6102
  /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
5802
6103
  setAnimationTime(seconds) {
5803
- for (const player of this.animationPlayers) player.setTime(seconds);
6104
+ for (const { player } of this.animationPlayers) player.setTime(seconds);
5804
6105
  if (this.animationPlayers.length) this.notifyDirty();
5805
6106
  }
5806
6107
  /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
5807
6108
  advanceAnimation(deltaSeconds) {
5808
- for (const player of this.animationPlayers) player.advance(deltaSeconds);
6109
+ for (const { player } of this.animationPlayers) player.advance(deltaSeconds);
5809
6110
  if (this.animationPlayers.length) this.notifyDirty();
5810
6111
  }
5811
6112
  //</editor-fold>
@@ -8038,6 +8339,7 @@ async function layoutGuiText(text2, options = {}) {
8038
8339
  }
8039
8340
  const font = await Fonts.get(options.font);
8040
8341
  const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
8342
+ await font.resolve?.(runs.map((run) => run.text).join(""));
8041
8343
  const glyphs = [], lineWidths = [];
8042
8344
  let line = [], width = 0;
8043
8345
  let trailingNewline = false;
@@ -8064,7 +8366,7 @@ async function layoutGuiText(text2, options = {}) {
8064
8366
  continue;
8065
8367
  }
8066
8368
  const glyph = font.glyphs.get(character);
8067
- const advance = (glyph?.advance ?? 6) + (style.bold ? 1 : 0);
8369
+ const advance = (glyph?.advance ?? 6) + (style.bold ? glyph?.boldOffset ?? 1 : 0);
8068
8370
  line.push({ character, glyph, advance, style, x: 0, y: 0 });
8069
8371
  width += advance;
8070
8372
  while (width > maxWidth && line.length > 1) {
@@ -8092,7 +8394,7 @@ function createGuiTextGeometry(layout, shadow = true) {
8092
8394
  batches.push(batch);
8093
8395
  }
8094
8396
  const color2 = new import_three20.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
8095
- const offset = isShadow ? 1 : 0;
8397
+ const offset = isShadow ? glyph?.shadowOffset ?? 1 : 0;
8096
8398
  const top = glyph ? 7 - glyph.ascent : 0;
8097
8399
  const height = glyph ? glyph.height * glyph.scale : 8;
8098
8400
  const width = glyph ? glyph.width * glyph.scale : 5;
@@ -8122,7 +8424,7 @@ function createGuiTextGeometry(layout, shadow = true) {
8122
8424
  for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
8123
8425
  batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
8124
8426
  };
8125
- for (const boldOffset of style.bold ? [0, 1] : [0]) {
8427
+ for (const boldOffset of style.bold ? [0, glyph?.boldOffset ?? 1] : [0]) {
8126
8428
  if (glyph) quad(0, top, width, top + height, boldOffset);
8127
8429
  else {
8128
8430
  quad(0, 0, 5, 1, boldOffset);
@@ -9896,6 +10198,10 @@ var init_ModelObject = __esm({
9896
10198
  this.atlas.model.elements?.forEach((el) => {
9897
10199
  const elGeo = this._getBoxGeometryFromElement(el).clone();
9898
10200
  this.geometries.add(elGeo);
10201
+ elGeo.setAttribute("shade", new import_three27.BufferAttribute(
10202
+ new Float32Array(elGeo.getAttribute("position").count).fill(!this.originalModel.itemId && el.shade === false ? 0 : 1),
10203
+ 1
10204
+ ));
9899
10205
  if (this.options.cullMask) {
9900
10206
  const indices = Array.from(elGeo.getIndex().array);
9901
10207
  elGeo.setIndex(indices.filter((_, index) => {
@@ -11399,6 +11705,7 @@ __export(index_node_exports, {
11399
11705
  toRadians: () => toRadians,
11400
11706
  toTrianglesDrawMode: () => toTrianglesDrawMode,
11401
11707
  trimCanvas: () => trimCanvas,
11708
+ validateAnimationWeights: () => validateAnimationWeights,
11402
11709
  yieldToEventLoop: () => yieldToEventLoop
11403
11710
  });
11404
11711
  module.exports = __toCommonJS(index_node_exports);
@@ -17687,9 +17994,9 @@ function cached(map, key, load) {
17687
17994
  function compactQuads(geometry, keep) {
17688
17995
  const quads = keep.flatMap((kept, index) => kept ? [index] : []);
17689
17996
  const compact = new import_three41.BufferGeometry();
17690
- for (const [name, size] of Object.entries({ position: 3, normal: 3, uv: 2, uvBounds: 4, color: 3 })) {
17997
+ for (const [name, size] of Object.entries({ position: 3, normal: 3, uv: 2, uvBounds: 4, color: 3, shade: 1 })) {
17691
17998
  const source2 = geometry.getAttribute(name);
17692
- const defaults = name === "uvBounds" ? [0, 0, 1, 1] : name === "color" ? [1, 1, 1] : void 0;
17999
+ const defaults = name === "uvBounds" ? [0, 0, 1, 1] : name === "color" ? [1, 1, 1] : name === "shade" ? [1] : void 0;
17693
18000
  const values = new Float32Array(quads.length * 4 * size);
17694
18001
  for (let q = 0; q < quads.length; q++) {
17695
18002
  for (let vertex = 0; vertex < 4; vertex++) {
@@ -18937,7 +19244,7 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
18937
19244
 
18938
19245
  // src/world/SectionGeometry.ts
18939
19246
  function buildSectionGeometry(input) {
18940
- const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
19247
+ const { count, indices, templates, cullMasks, templateData, atlases: atlases2, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
18941
19248
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
18942
19249
  if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
18943
19250
  if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
@@ -18955,7 +19262,7 @@ function buildSectionGeometry(input) {
18955
19262
  const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
18956
19263
  for (const template of templateData) {
18957
19264
  const { quads } = template;
18958
- 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) {
19265
+ 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) {
18959
19266
  throw new RangeError("Section template arrays do not match its quad count");
18960
19267
  }
18961
19268
  }
@@ -18972,8 +19279,8 @@ function buildSectionGeometry(input) {
18972
19279
  const locations = /* @__PURE__ */ new Map();
18973
19280
  for (const [layer, layerAtlases] of [...visibleAtlases].sort(([a], [b]) => a - b)) {
18974
19281
  const firstPage = pages.length;
18975
- 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))) {
18976
- const { width, height, animated } = atlases[atlas];
19282
+ 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))) {
19283
+ const { width, height, animated } = atlases2[atlas];
18977
19284
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
18978
19285
  let selected = -1;
18979
19286
  for (let i = firstPage; !animated && i < pages.length; i++) {
@@ -19016,7 +19323,7 @@ function buildSectionGeometry(input) {
19016
19323
  });
19017
19324
  if (!quads.sprites.length) continue;
19018
19325
  if (!fluidPages.has(fluid)) {
19019
- const { width, height } = atlases[data.atlas];
19326
+ const { width, height } = atlases2[data.atlas];
19020
19327
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
19021
19328
  fluidPages.set(fluid, {
19022
19329
  layer: 2,
@@ -19034,7 +19341,7 @@ function buildSectionGeometry(input) {
19034
19341
  const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
19035
19342
  fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
19036
19343
  }
19037
- 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));
19344
+ 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));
19038
19345
  }
19039
19346
  return pages.map((page) => {
19040
19347
  let faces = page.fluids.reduce((sum, fluid) => sum + fluid.quads.sprites.length, 0);
@@ -19046,12 +19353,13 @@ function buildSectionGeometry(input) {
19046
19353
  }
19047
19354
  const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
19048
19355
  const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
19356
+ const shade = new Float32Array(faces * 4).fill(1);
19049
19357
  const outputIndices = new Uint32Array(faces * 6);
19050
19358
  const blockIndices = new Uint16Array(faces);
19051
19359
  let vertex = 0, indexOffset = 0;
19052
19360
  for (const entry of page.entries) {
19053
19361
  const template = templateData[templates[entry]];
19054
- const atlas = atlases[template.atlas];
19362
+ const atlas = atlases2[template.atlas];
19055
19363
  const placement = locations.get(template.atlas);
19056
19364
  const mapU = (u) => (placement.x + u * atlas.width) / page.width;
19057
19365
  const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
@@ -19075,6 +19383,7 @@ function buildSectionGeometry(input) {
19075
19383
  normals[vertex * 3] = template.normals[source * 3];
19076
19384
  normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
19077
19385
  normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
19386
+ shade[vertex] = template.shade?.[source] ?? 1;
19078
19387
  uvs[vertex * 2] = mapU(template.uvs[source * 2]);
19079
19388
  uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
19080
19389
  uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
@@ -19090,7 +19399,7 @@ function buildSectionGeometry(input) {
19090
19399
  }
19091
19400
  }
19092
19401
  for (const { quads, x, y, z, data, tint } of page.fluids) {
19093
- const atlas = atlases[data.atlas];
19402
+ const atlas = atlases2[data.atlas];
19094
19403
  const placement = page.placements[0];
19095
19404
  const mapU = (u) => (placement.x + u * atlas.width) / page.width;
19096
19405
  const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
@@ -19127,11 +19436,11 @@ function buildSectionGeometry(input) {
19127
19436
  indexOffset += 6;
19128
19437
  }
19129
19438
  }
19130
- return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices, blockIndices };
19439
+ return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, shade, indices: outputIndices, blockIndices };
19131
19440
  });
19132
19441
  }
19133
19442
  function sectionGeometryTransferables(pages) {
19134
- 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]);
19443
+ 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]);
19135
19444
  }
19136
19445
 
19137
19446
  // src/world/SectionWorker.ts
@@ -19192,7 +19501,7 @@ var templateCache = /* @__PURE__ */ new WeakMap();
19192
19501
  var WATER_TINT = new import_three46.Color(4159204).toArray();
19193
19502
  function toInput(entries, maxAtlasSize, fluids, lighting) {
19194
19503
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
19195
- const atlases = [];
19504
+ const atlases2 = [];
19196
19505
  const atlasIds = /* @__PURE__ */ new Map();
19197
19506
  const templateIds = /* @__PURE__ */ new Map();
19198
19507
  const input = {
@@ -19215,9 +19524,9 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19215
19524
  if (id === void 0) {
19216
19525
  let atlas = atlasIds.get(template.atlas);
19217
19526
  if (atlas === void 0) {
19218
- atlas = atlases.length;
19527
+ atlas = atlases2.length;
19219
19528
  atlasIds.set(template.atlas, atlas);
19220
- atlases.push(template.atlas);
19529
+ atlases2.push(template.atlas);
19221
19530
  input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height, animated: template.atlas.hasAnimation });
19222
19531
  }
19223
19532
  let data = templateCache.get(template);
@@ -19235,6 +19544,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19235
19544
  uvs: new Float32Array(quads * 8),
19236
19545
  uvBounds: new Float32Array(quads * 16),
19237
19546
  colors: new Float32Array(quads * 12),
19547
+ shade: new Float32Array(quads * 4),
19238
19548
  indices: new Uint16Array(quads * 6),
19239
19549
  cullFaces: new Uint8Array(template.cullFaces),
19240
19550
  biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
@@ -19246,6 +19556,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19246
19556
  const uv = template.geometry.getAttribute("uv");
19247
19557
  const bounds = template.geometry.getAttribute("uvBounds");
19248
19558
  const color2 = template.geometry.getAttribute("color");
19559
+ const shade = template.geometry.getAttribute("shade");
19249
19560
  for (let vertex = 0; vertex < quads * 4; vertex++) {
19250
19561
  data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
19251
19562
  data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
@@ -19257,6 +19568,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19257
19568
  bounds?.getW(vertex) ?? 1
19258
19569
  ], vertex * 4);
19259
19570
  data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
19571
+ data.shade[vertex] = shade?.getX(vertex) ?? 1;
19260
19572
  }
19261
19573
  for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - 4 * Math.floor(i2 / 6);
19262
19574
  templateCache.set(template, data);
@@ -19276,9 +19588,9 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19276
19588
  if (!atlas) continue;
19277
19589
  let id = atlasIds.get(atlas);
19278
19590
  if (id === void 0) {
19279
- id = atlases.length;
19591
+ id = atlases2.length;
19280
19592
  atlasIds.set(atlas, id);
19281
- atlases.push(atlas);
19593
+ atlases2.push(atlas);
19282
19594
  input.atlases.push({ width: atlas.image.width, height: atlas.image.height, animated: atlas.hasAnimation });
19283
19595
  }
19284
19596
  input.fluids[kind] = {
@@ -19289,7 +19601,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19289
19601
  };
19290
19602
  }
19291
19603
  }
19292
- return { input, atlases };
19604
+ return { input, atlases: atlases2 };
19293
19605
  }
19294
19606
  var SectionMesh = class _SectionMesh extends import_three46.Group {
19295
19607
  constructor() {
@@ -19304,23 +19616,23 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19304
19616
  }
19305
19617
  /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
19306
19618
  static build(entries, maxAtlasSize = 2048, fluids, lighting) {
19307
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
19308
- return this.fromPages(buildSectionGeometry(input), atlases);
19619
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
19620
+ return this.fromPages(buildSectionGeometry(input), atlases2);
19309
19621
  }
19310
19622
  /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
19311
19623
  static async buildAsync(entries, maxAtlasSize = 2048, fluids, lighting) {
19312
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
19624
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
19313
19625
  const worker = SectionWorker.shared();
19314
19626
  const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
19315
- return this.fromPages(pages, atlases);
19627
+ return this.fromPages(pages, atlases2);
19316
19628
  }
19317
- static fromPages(pages, atlases) {
19629
+ static fromPages(pages, atlases2) {
19318
19630
  const section = new _SectionMesh();
19319
19631
  for (const page of pages) {
19320
19632
  const canvas = createCanvas2(page.width, page.height);
19321
19633
  const context = canvas.getContext("2d");
19322
19634
  for (const { atlas, x, y } of page.placements) {
19323
- context.drawImage(atlases[atlas].image.canvas, x, y);
19635
+ context.drawImage(atlases2[atlas].image.canvas, x, y);
19324
19636
  }
19325
19637
  const geometry = new import_three46.BufferGeometry();
19326
19638
  geometry.setAttribute("position", new import_three46.Float32BufferAttribute(page.positions.buffer, 3));
@@ -19328,10 +19640,11 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19328
19640
  geometry.setAttribute("uv", new import_three46.Float32BufferAttribute(page.uvs.buffer, 2));
19329
19641
  geometry.setAttribute("uvBounds", new import_three46.Float32BufferAttribute(page.uvBounds.buffer, 4));
19330
19642
  geometry.setAttribute("color", new import_three46.Float32BufferAttribute(page.colors.buffer, 3));
19643
+ geometry.setAttribute("shade", new import_three46.Float32BufferAttribute(page.shade.buffer, 1));
19331
19644
  geometry.setIndex(new import_three46.Uint32BufferAttribute(page.indices.buffer, 1));
19332
19645
  geometry.computeBoundingBox();
19333
19646
  geometry.computeBoundingSphere();
19334
- const transparent = page.layer === 2 ? atlases[page.placements[0].atlas].hasTranslucency : page.layer === 1;
19647
+ const transparent = page.layer === 2 ? atlases2[page.placements[0].atlas].hasTranslucency : page.layer === 1;
19335
19648
  const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
19336
19649
  material.side = import_three46.FrontSide;
19337
19650
  if (page.layer === 2) {
@@ -19346,7 +19659,7 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19346
19659
  section.blockIndices.set(mesh, page.blockIndices);
19347
19660
  const unsubscribe = [];
19348
19661
  for (const { atlas: id, x, y } of page.placements) {
19349
- const atlas = atlases[id];
19662
+ const atlas = atlases2[id];
19350
19663
  if (!atlas.hasAnimation) continue;
19351
19664
  unsubscribe.push(atlas.subscribe(() => {
19352
19665
  context.clearRect(x, y, atlas.image.width, atlas.image.height);
@@ -20690,6 +21003,7 @@ var NodeRenderer = class _NodeRenderer extends Renderer {
20690
21003
  toRadians,
20691
21004
  toTrianglesDrawMode,
20692
21005
  trimCanvas,
21006
+ validateAnimationWeights,
20693
21007
  yieldToEventLoop
20694
21008
  });
20695
21009
  //# sourceMappingURL=index.js.map