minerender 2.1.0-alpha.131 → 2.1.0-alpha.133

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.
package/dist/bundle.js CHANGED
@@ -35632,6 +35632,7 @@ void main() {
35632
35632
  this.entityAnimationCache,
35633
35633
  this.blockEntityIndexCache,
35634
35634
  this.fontCache,
35635
+ this.unihexCache,
35635
35636
  this.bannerPatternCache,
35636
35637
  this.mineRenderDataCache,
35637
35638
  this.usernameUuidCache,
@@ -35685,6 +35686,7 @@ void main() {
35685
35686
  _Caching.entityTextureCache = _Caching.createAssetCache();
35686
35687
  _Caching.entityAnimationCache = _Caching.createAssetCache();
35687
35688
  _Caching.blockEntityIndexCache = _Caching.createAssetCache();
35689
+ _Caching.unihexCache = _Caching.createAssetCache();
35688
35690
  _Caching.fontCache = _Caching.createAssetCache();
35689
35691
  _Caching.bannerPatternCache = _Caching.createAssetCache();
35690
35692
  _Caching.mineRenderDataCache = _Caching.createAssetCache();
@@ -37098,6 +37100,7 @@ void main() {
37098
37100
  AssetParser2["IMAGE"] = "image";
37099
37101
  AssetParser2["LIST"] = "list";
37100
37102
  AssetParser2["JSON"] = "json";
37103
+ AssetParser2["BINARY"] = "binary";
37101
37104
  return AssetParser2;
37102
37105
  })(AssetParser || {});
37103
37106
  }
@@ -73063,7 +73066,7 @@ return ${compiler2.callType("value", tagType)}
73063
73066
  });
73064
73067
 
73065
73068
  // src/assets/source/HostedAssetSource.ts
73066
- var p7, DEFAULT_OPTIONS2, _HostedAssetSource, HostedAssetSource;
73069
+ var BINARY_TIMEOUT, p7, DEFAULT_OPTIONS2, _HostedAssetSource, HostedAssetSource;
73067
73070
  var init_HostedAssetSource = __esm({
73068
73071
  "src/assets/source/HostedAssetSource.ts"() {
73069
73072
  init_dirname();
@@ -73079,6 +73082,7 @@ return ${compiler2.callType("value", tagType)}
73079
73082
  init_AssetLoader();
73080
73083
  init_parser();
73081
73084
  init_merge();
73085
+ BINARY_TIMEOUT = 6e4;
73082
73086
  p7 = prefix("HostedAssetSource");
73083
73087
  DEFAULT_OPTIONS2 = {
73084
73088
  retryDefaults: true
@@ -73219,6 +73223,15 @@ return ${compiler2.callType("value", tagType)}
73219
73223
  return response.data;
73220
73224
  }
73221
73225
  };
73226
+ _HostedAssetSource.BINARY = {
73227
+ config(request) {
73228
+ request.timeout = BINARY_TIMEOUT;
73229
+ request.responseType = "arraybuffer";
73230
+ },
73231
+ parse(response) {
73232
+ return { data: new Uint8Array(response.data) };
73233
+ }
73234
+ };
73222
73235
  _HostedAssetSource.PARSER_MAP = /* @__PURE__ */ new Map([
73223
73236
  ["model" /* MODEL */, _HostedAssetSource.MODEL],
73224
73237
  ["blockstate" /* BLOCKSTATE */, _HostedAssetSource.BLOCKSTATE],
@@ -73226,7 +73239,8 @@ return ${compiler2.callType("value", tagType)}
73226
73239
  ["nbt" /* NBT */, _HostedAssetSource.NBT],
73227
73240
  ["image" /* IMAGE */, _HostedAssetSource.IMAGE],
73228
73241
  ["list" /* LIST */, _HostedAssetSource.LIST],
73229
- ["json" /* JSON */, _HostedAssetSource.JSON]
73242
+ ["json" /* JSON */, _HostedAssetSource.JSON],
73243
+ ["binary" /* BINARY */, _HostedAssetSource.BINARY]
73230
73244
  ]);
73231
73245
  HostedAssetSource = _HostedAssetSource;
73232
73246
  }
@@ -81841,7 +81855,7 @@ return ${compiler2.callType("value", tagType)}
81841
81855
  function readUint8Array(reader, offset, size, diskNumber) {
81842
81856
  return reader.readUint8Array(offset, size, diskNumber);
81843
81857
  }
81844
- var ERR_ITERATOR_COMPLETED_TOO_SOON, HTTP_HEADER_CONTENT_TYPE, DEFAULT_CHUNK_SIZE, PROPERTY_NAME_WRITABLE, Stream, Reader, BlobReader, BlobWriter, SplitDataReader, SplitDataWriter;
81858
+ var ERR_ITERATOR_COMPLETED_TOO_SOON, HTTP_HEADER_CONTENT_TYPE, DEFAULT_CHUNK_SIZE, PROPERTY_NAME_WRITABLE, Stream, Reader, BlobReader, BlobWriter, TextWriter, Uint8ArrayReader, SplitDataReader, SplitDataWriter;
81845
81859
  var init_io = __esm({
81846
81860
  "../../node_modules/@zip.js/zip.js/lib/core/io.js"() {
81847
81861
  init_dirname();
@@ -81924,6 +81938,46 @@ return ${compiler2.callType("value", tagType)}
81924
81938
  return this.blob;
81925
81939
  }
81926
81940
  };
81941
+ TextWriter = class extends BlobWriter {
81942
+ constructor(encoding) {
81943
+ super(encoding);
81944
+ Object.assign(this, {
81945
+ encoding,
81946
+ utf8: !encoding || encoding.toLowerCase() == "utf-8"
81947
+ });
81948
+ }
81949
+ async getData() {
81950
+ const {
81951
+ encoding,
81952
+ utf8
81953
+ } = this;
81954
+ const blob = await super.getData();
81955
+ if (blob.text && utf8) {
81956
+ return blob.text();
81957
+ } else {
81958
+ const reader = new FileReader();
81959
+ return new Promise((resolve, reject) => {
81960
+ Object.assign(reader, {
81961
+ onload: ({ target }) => resolve(target.result),
81962
+ onerror: () => reject(reader.error)
81963
+ });
81964
+ reader.readAsText(blob, encoding);
81965
+ });
81966
+ }
81967
+ }
81968
+ };
81969
+ Uint8ArrayReader = class extends Reader {
81970
+ constructor(array) {
81971
+ super();
81972
+ Object.assign(this, {
81973
+ array,
81974
+ size: array.length
81975
+ });
81976
+ }
81977
+ readUint8Array(index, length) {
81978
+ return this.array.slice(index, index + length);
81979
+ }
81980
+ };
81927
81981
  SplitDataReader = class extends Reader {
81928
81982
  constructor(readers) {
81929
81983
  super();
@@ -84682,6 +84736,181 @@ ${key}` : key;
84682
84736
  }
84683
84737
  });
84684
84738
 
84739
+ // src/assets/_fonts/Unihex.ts
84740
+ var Unihex_exports = {};
84741
+ __export(Unihex_exports, {
84742
+ loadUnihex: () => loadUnihex
84743
+ });
84744
+ function readHex(text2, filename, glyphs) {
84745
+ const lines = text2.split("\n");
84746
+ if (lines[lines.length - 1] === "") lines.pop();
84747
+ lines.forEach((line, index) => {
84748
+ const match = /^([0-9A-F]{4,6}):([0-9A-F]+)$/.exec(line);
84749
+ if (!match || ![32, 64, 96, 128].includes(match[2].length)) {
84750
+ throw new Error(`Invalid Unihex record in ${filename} at line ${index + 1}`);
84751
+ }
84752
+ const codepoint = parseInt(match[1], 16);
84753
+ if (codepoint > 1114111) throw new Error(`Invalid Unihex codepoint in ${filename} at line ${index + 1}`);
84754
+ const bitWidth = match[2].length / 4;
84755
+ const rowLength = bitWidth / 4;
84756
+ const rows = new Uint32Array(GLYPH_HEIGHT);
84757
+ let mask = 0;
84758
+ for (let y5 = 0; y5 < GLYPH_HEIGHT; y5++) {
84759
+ rows[y5] = parseInt(match[2].slice(y5 * rowLength, (y5 + 1) * rowLength), 16);
84760
+ mask |= rows[y5];
84761
+ }
84762
+ let left = 0, right = bitWidth;
84763
+ if (mask) {
84764
+ while (!(mask >>> bitWidth - left - 1 & 1)) left++;
84765
+ right = bitWidth - 1;
84766
+ while (!(mask >>> bitWidth - right - 1 & 1)) right--;
84767
+ }
84768
+ glyphs.set(codepoint, { rows, bitWidth, left, right });
84769
+ });
84770
+ }
84771
+ async function parseUnihex(key) {
84772
+ try {
84773
+ const asset2 = await AssetLoader.get(key, "binary" /* BINARY */);
84774
+ if (!asset2) throw new Error(`Could not load Unihex archive ${key.toNamespacedString()}`);
84775
+ const records = /* @__PURE__ */ new Map();
84776
+ const reader = new ZipReader(new Uint8ArrayReader(asset2.data), { useWebWorkers: false, useCompressionStream: false });
84777
+ try {
84778
+ for (const entry of await reader.getEntries()) {
84779
+ if (!entry.directory && entry.filename.endsWith(".hex")) {
84780
+ readHex(await entry.getData(new TextWriter(), { checkSignature: true }), entry.filename, records);
84781
+ }
84782
+ }
84783
+ } finally {
84784
+ await reader.close();
84785
+ }
84786
+ const indices = new Int32Array(1114112).fill(-1);
84787
+ const rows = new Uint32Array(records.size * GLYPH_HEIGHT), metrics = new Uint8Array(records.size * 3);
84788
+ let index = 0;
84789
+ for (const [codepoint, record] of records) {
84790
+ indices[codepoint] = index;
84791
+ rows.set(record.rows, index * GLYPH_HEIGHT);
84792
+ metrics.set([record.bitWidth, record.left, record.right], index * 3);
84793
+ index++;
84794
+ }
84795
+ return { indices, rows, metrics };
84796
+ } catch (error2) {
84797
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error2);
84798
+ return void 0;
84799
+ }
84800
+ }
84801
+ async function loadUnihex(provider, fontKey, characters) {
84802
+ const origin = new AssetKey(DEFAULT_NAMESPACE, "", void 0, void 0, "assets", "", fontKey.root);
84803
+ const key = AssetKey.parse("font", provider.hex_file, origin);
84804
+ key.assetType = void 0;
84805
+ const glyphs = /* @__PURE__ */ new Map();
84806
+ let overrides;
84807
+ try {
84808
+ overrides = (provider.size_overrides ?? []).map((override) => {
84809
+ const from = override.from.codePointAt(0), to = override.to.codePointAt(0);
84810
+ if ([...override.from].length !== 1 || [...override.to].length !== 1 || from >= to || !Number.isInteger(override.left) || !Number.isInteger(override.right) || override.right < override.left) {
84811
+ throw new Error(`Invalid Unihex size override in font ${fontKey.toNamespacedString()}`);
84812
+ }
84813
+ return { ...override, from, to };
84814
+ });
84815
+ } catch (error2) {
84816
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error2);
84817
+ return glyphs;
84818
+ }
84819
+ const file = await Caching.unihexCache.get(key.serialize(), () => parseUnihex(key));
84820
+ if (!file) return glyphs;
84821
+ let atlas = atlases.get(provider);
84822
+ if (!atlas) {
84823
+ atlas = new UnihexAtlas();
84824
+ atlases.set(provider, atlas);
84825
+ }
84826
+ for (const character of characters) {
84827
+ const codepoint = character.codePointAt(0), index = file.indices[codepoint];
84828
+ if (index < 0) continue;
84829
+ const record = {
84830
+ rows: file.rows.subarray(index * GLYPH_HEIGHT, (index + 1) * GLYPH_HEIGHT),
84831
+ bitWidth: file.metrics[index * 3],
84832
+ left: file.metrics[index * 3 + 1],
84833
+ right: file.metrics[index * 3 + 2]
84834
+ };
84835
+ const override = overrides.find((range) => codepoint >= range.from && codepoint <= range.to);
84836
+ if (override) {
84837
+ record.left = override.left;
84838
+ record.right = override.right;
84839
+ }
84840
+ const width = record.right - record.left + 1;
84841
+ let rendered;
84842
+ const render = () => rendered ?? (rendered = atlas.draw(record));
84843
+ glyphs.set(character, {
84844
+ get image() {
84845
+ return render().image;
84846
+ },
84847
+ get x() {
84848
+ return render().x;
84849
+ },
84850
+ get y() {
84851
+ return render().y;
84852
+ },
84853
+ width,
84854
+ height: GLYPH_HEIGHT,
84855
+ scale: 0.5,
84856
+ ascent: 7,
84857
+ advance: Math.floor(width / 2) + 1,
84858
+ boldOffset: 0.5,
84859
+ shadowOffset: 0.5
84860
+ });
84861
+ }
84862
+ return glyphs;
84863
+ }
84864
+ var ATLAS_SIZE, GLYPH_HEIGHT, UnihexAtlas, atlases;
84865
+ var init_Unihex = __esm({
84866
+ "src/assets/_fonts/Unihex.ts"() {
84867
+ init_dirname();
84868
+ init_buffer2();
84869
+ init_process2();
84870
+ init_zip();
84871
+ init_AssetKey();
84872
+ init_AssetLoader();
84873
+ init_Assets();
84874
+ init_Caching();
84875
+ init_AssetParsers();
84876
+ init_CanvasCompat();
84877
+ ATLAS_SIZE = 512;
84878
+ GLYPH_HEIGHT = 16;
84879
+ UnihexAtlas = class {
84880
+ constructor() {
84881
+ this.x = 0;
84882
+ this.y = 0;
84883
+ }
84884
+ draw(glyph) {
84885
+ const width = glyph.right - glyph.left + 1;
84886
+ if (this.image && this.x + width > this.image.width) {
84887
+ this.x = 0;
84888
+ this.y += GLYPH_HEIGHT;
84889
+ }
84890
+ if (!this.image || this.y + GLYPH_HEIGHT > this.image.height || width > this.image.width) {
84891
+ this.image = createCanvas(Math.max(ATLAS_SIZE, width), ATLAS_SIZE);
84892
+ this.x = 0;
84893
+ this.y = 0;
84894
+ }
84895
+ const context = this.image.getContext("2d");
84896
+ const pixels = context.createImageData(width, GLYPH_HEIGHT);
84897
+ for (let y5 = 0; y5 < GLYPH_HEIGHT; y5++) {
84898
+ for (let x4 = 0; x4 < width; x4++) {
84899
+ const column = glyph.left + x4;
84900
+ if (column < 0 || column >= glyph.bitWidth || !(glyph.rows[y5] >>> glyph.bitWidth - column - 1 & 1)) continue;
84901
+ pixels.data.fill(255, (y5 * width + x4) * 4, (y5 * width + x4 + 1) * 4);
84902
+ }
84903
+ }
84904
+ const result = { image: this.image, x: this.x, y: this.y };
84905
+ context.putImageData(pixels, this.x, this.y);
84906
+ this.x += width;
84907
+ return result;
84908
+ }
84909
+ };
84910
+ atlases = /* @__PURE__ */ new WeakMap();
84911
+ }
84912
+ });
84913
+
84685
84914
  // src/assets/Fonts.ts
84686
84915
  var Fonts;
84687
84916
  var init_Fonts = __esm({
@@ -84695,7 +84924,7 @@ ${key}` : key;
84695
84924
  init_ModelTextures();
84696
84925
  init_Caching();
84697
84926
  Fonts = class {
84698
- /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
84927
+ /** Combines font definitions in source-priority order. TrueType providers are skipped. */
84699
84928
  static async get(key = "minecraft:default") {
84700
84929
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
84701
84930
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -84711,6 +84940,7 @@ ${key}` : key;
84711
84940
  const stack = await AssetLoader.getAll(key, "json" /* JSON */);
84712
84941
  if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
84713
84942
  const glyphs = /* @__PURE__ */ new Map();
84943
+ const providers = [];
84714
84944
  for (const asset2 of stack) {
84715
84945
  for (const provider of asset2.providers) {
84716
84946
  if (provider.filter?.uniform || provider.filter?.jp) continue;
@@ -84729,17 +84959,43 @@ ${key}` : key;
84729
84959
  break;
84730
84960
  case "reference":
84731
84961
  case "minecraft:reference":
84732
- provided = (await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized])).glyphs;
84962
+ const reference = await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized]);
84963
+ if (reference.resolve) {
84964
+ providers.push(async (characters) => {
84965
+ await reference.resolve(characters);
84966
+ return reference.glyphs;
84967
+ });
84968
+ continue;
84969
+ }
84970
+ provided = reference.glyphs;
84733
84971
  break;
84972
+ case "unihex":
84973
+ case "minecraft:unihex":
84974
+ providers.push(async (characters) => (await Promise.resolve().then(() => (init_Unihex(), Unihex_exports))).loadUnihex(provider, key, characters));
84975
+ continue;
84734
84976
  default:
84735
84977
  continue;
84736
84978
  }
84737
- for (const [character, glyph] of provided) {
84979
+ if (providers.every((provider2) => provider2 instanceof Map)) for (const [character, glyph] of provided) {
84738
84980
  if (character !== "\0" && !glyphs.has(character)) glyphs.set(character, glyph);
84739
84981
  }
84982
+ providers.push(provided);
84740
84983
  }
84741
84984
  }
84742
- return { glyphs };
84985
+ return { glyphs, resolve: providers.every((provider) => provider instanceof Map) ? void 0 : async (characters) => {
84986
+ const remaining = new Set([...characters].filter((character) => character !== "\0" && character !== "\n" && character !== "\r" && !glyphs.has(character)));
84987
+ for (const provider of providers) {
84988
+ if (!remaining.size) break;
84989
+ const provided = provider instanceof Map ? provider : await provider([...remaining].join(""));
84990
+ for (const character of remaining) {
84991
+ const glyph = provided.get(character);
84992
+ if (glyph) {
84993
+ glyphs.set(character, glyph);
84994
+ remaining.delete(character);
84995
+ }
84996
+ }
84997
+ }
84998
+ } };
84743
84999
  }
84744
85000
  static async bitmap(provider, fontKey) {
84745
85001
  const rows = provider.chars.map((row) => [...row]);
@@ -84835,6 +85091,13 @@ ${key}` : key;
84835
85091
  });
84836
85092
 
84837
85093
  // src/entity/scene/EntityAnimationPlayer.ts
85094
+ function validateAnimationWeights(weights, count) {
85095
+ const values = Array.from(weights);
85096
+ if (values.length !== count || values.some((weight) => !Number.isFinite(weight) || weight < 0)) {
85097
+ throw new Error("Animation weights must match the clip count and contain only finite nonnegative numbers");
85098
+ }
85099
+ return values;
85100
+ }
84838
85101
  var ZERO, EntityAnimationPlayer;
84839
85102
  var init_EntityAnimationPlayer = __esm({
84840
85103
  "src/entity/scene/EntityAnimationPlayer.ts"() {
@@ -84847,12 +85110,20 @@ ${key}` : key;
84847
85110
  EntityAnimationPlayer = class {
84848
85111
  constructor() {
84849
85112
  this.parts = [];
85113
+ this._animations = [];
85114
+ this._weights = [];
85115
+ this.loops = [];
84850
85116
  this._time = 0;
84851
- this.loop = false;
84852
85117
  this.speed = 1;
84853
85118
  }
84854
85119
  get animation() {
84855
- return this._animation;
85120
+ return this._animations[0];
85121
+ }
85122
+ get animations() {
85123
+ return this._animations.slice();
85124
+ }
85125
+ get weights() {
85126
+ return this._weights.slice();
84856
85127
  }
84857
85128
  get time() {
84858
85129
  return this._time;
@@ -84862,10 +85133,20 @@ ${key}` : key;
84862
85133
  * without them, the current pose is used. Stopping restores the pose from before playback.
84863
85134
  */
84864
85135
  play(layers, animation, options = {}, poses) {
85136
+ this.playAnimations(layers, [animation], options, poses);
85137
+ }
85138
+ /**
85139
+ * Starts clips on matching parts with a shared clock. Weighted offsets add to the supplied baked poses
85140
+ * or, without poses, to each animated part's pose when playback starts.
85141
+ */
85142
+ playAnimations(layers, animations, options = {}, poses) {
85143
+ const weights = validateAnimationWeights(options.weights ?? animations.map(() => 1), animations.length);
84865
85144
  this.stop();
84866
- this._animation = animation;
85145
+ this._animations = animations.slice();
85146
+ this._weights = weights;
85147
+ if (!animations.length) return;
84867
85148
  this._time = options.time ?? 0;
84868
- this.loop = options.loop ?? animation.loop;
85149
+ this.loops = animations.map((animation) => options.loop ?? animation.loop);
84869
85150
  this.speed = options.speed ?? 1;
84870
85151
  for (const layer of layers) {
84871
85152
  const objects = [];
@@ -84874,7 +85155,8 @@ ${key}` : key;
84874
85155
  if (poses.has(object4)) objects.push(object4);
84875
85156
  });
84876
85157
  } else {
84877
- for (const bone of Object.keys(animation.bones)) {
85158
+ const bones = new Set(animations.flatMap((animation) => Object.keys(animation.bones)));
85159
+ for (const bone of bones) {
84878
85160
  const object4 = layer.getObjectByName(`group:${bone}`);
84879
85161
  if (object4) objects.push(object4);
84880
85162
  }
@@ -84898,7 +85180,7 @@ ${key}` : key;
84898
85180
  }
84899
85181
  /** Restores the poses from before playback. Returns whether an animation was active. */
84900
85182
  stop() {
84901
- if (!this._animation) return false;
85183
+ if (!this._animations.length) return false;
84902
85184
  for (const part of this.parts) {
84903
85185
  part.object.position.copy(part.position);
84904
85186
  part.object.rotation.copy(part.rotation);
@@ -84910,12 +85192,14 @@ ${key}` : key;
84910
85192
  /** Forgets the animation without touching the parts, for parts that no longer exist. */
84911
85193
  clear() {
84912
85194
  this.parts = [];
84913
- this._animation = void 0;
85195
+ this._animations = [];
85196
+ this._weights = [];
85197
+ this.loops = [];
84914
85198
  this._time = 0;
84915
85199
  }
84916
85200
  /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
84917
85201
  setTime(seconds) {
84918
- if (!this._animation) return false;
85202
+ if (!this._animations.length) return false;
84919
85203
  this._time = seconds;
84920
85204
  this.apply();
84921
85205
  return true;
@@ -84924,8 +85208,30 @@ ${key}` : key;
84924
85208
  advance(deltaSeconds) {
84925
85209
  return this.setTime(this._time + deltaSeconds * this.speed);
84926
85210
  }
85211
+ /** Changes weights in clip order and reapplies the current time without restarting playback. */
85212
+ setWeights(weights) {
85213
+ if (!this._animations.length) return false;
85214
+ const validated = validateAnimationWeights(weights, this._animations.length);
85215
+ this._weights = validated;
85216
+ this.apply();
85217
+ return true;
85218
+ }
84927
85219
  apply() {
84928
- const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
85220
+ const pose = /* @__PURE__ */ Object.create(null);
85221
+ for (let i4 = 0; i4 < this._animations.length; i4++) {
85222
+ const weight = this._weights[i4];
85223
+ if (weight === 0) continue;
85224
+ const sampled = sampleEntityAnimation(this._animations[i4], this._time, this.loops[i4]);
85225
+ for (const [bone, channels] of Object.entries(sampled)) {
85226
+ const blended = pose[bone] ?? (pose[bone] = {});
85227
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
85228
+ const value = channels[channel];
85229
+ if (!value) continue;
85230
+ const target = blended[channel] ?? (blended[channel] = [0, 0, 0]);
85231
+ for (let axis = 0; axis < 3; axis++) target[axis] += value[axis] * weight;
85232
+ }
85233
+ }
85234
+ }
84929
85235
  for (const part of this.parts) {
84930
85236
  const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone] ?? {};
84931
85237
  part.object.position.set(part.basePosition.x + position[0], part.basePosition.y + position[1], part.basePosition.z + position[2]);
@@ -85554,7 +85860,7 @@ ${key}` : key;
85554
85860
  for (const geometry of this.geometries) geometry.dispose();
85555
85861
  this.geometries.clear();
85556
85862
  for (const dispose of this.disposeWireframes.splice(0)) dispose();
85557
- for (const player of this.animationPlayers.splice(0)) player.clear();
85863
+ for (const { player } of this.animationPlayers.splice(0)) player.clear();
85558
85864
  this.partPoses.clear();
85559
85865
  super.disposeAndRemoveAllChildren();
85560
85866
  }
@@ -85589,15 +85895,31 @@ ${key}` : key;
85589
85895
  //<editor-fold desc="ANIMATION">
85590
85896
  /** The first active clip, until playback is stopped or the object is disposed. */
85591
85897
  get animation() {
85592
- return this.animationPlayers[0]?.animation;
85898
+ return this.animationPlayers[0]?.player.animation;
85593
85899
  }
85594
- /** Clips playing together, each targeting a separate geometry layer. */
85900
+ /** Clips playing together, in the order passed to {@link playAnimations}. */
85595
85901
  get activeAnimations() {
85596
- return this.animationPlayers.map((player) => player.animation);
85902
+ const animations = [];
85903
+ for (const { player, indices } of this.animationPlayers) {
85904
+ player.animations.forEach((animation, i4) => {
85905
+ animations[indices[i4]] = animation;
85906
+ });
85907
+ }
85908
+ return animations;
85909
+ }
85910
+ /** Weights of the active clips, in the same order as {@link activeAnimations}. */
85911
+ get animationWeights() {
85912
+ const weights = [];
85913
+ for (const { player, indices } of this.animationPlayers) {
85914
+ player.weights.forEach((weight, i4) => {
85915
+ weights[indices[i4]] = weight;
85916
+ });
85917
+ }
85918
+ return weights;
85597
85919
  }
85598
85920
  /** Seconds since the animation started, before looping. */
85599
85921
  get animationTime() {
85600
- return this.animationPlayers[0]?.time ?? 0;
85922
+ return this.animationPlayers[0]?.player.time ?? 0;
85601
85923
  }
85602
85924
  /**
85603
85925
  * Plays a clip from {@link Entities.getAnimations}, replacing all active clips.
@@ -85608,42 +85930,59 @@ ${key}` : key;
85608
85930
  playAnimation(animation, options) {
85609
85931
  this.playAnimations([animation], options);
85610
85932
  }
85611
- /** Plays clips with a shared clock and no blending. Selected layers without their own clip follow `main`. */
85933
+ /**
85934
+ * Plays clips with a shared clock, adding their weighted offsets within each geometry layer.
85935
+ * Weights default to 1 and are not normalized. Selected layers without their own clip follow `main`.
85936
+ */
85612
85937
  playAnimations(animations, options) {
85613
- const targets = /* @__PURE__ */ new Set();
85614
- const selections = animations.map((animation) => {
85938
+ const weights = validateAnimationWeights(options?.weights ?? animations.map(() => 1), animations.length);
85939
+ const selections = /* @__PURE__ */ new Map();
85940
+ animations.forEach((animation, index) => {
85615
85941
  const layer = animation.layer ?? "main";
85616
- if (targets.has(layer)) throw new Error(`Multiple animations target layer "${layer}"`);
85617
- targets.add(layer);
85618
85942
  const names = Object.keys(this.entityLayers).filter((name2) => name2.split("#")[0] === layer);
85619
85943
  if (!names.length) throw new Error(`Entity ${this.entity.id} has no selected animation layer "${layer}"`);
85620
- return { animation, names };
85944
+ let selection = selections.get(layer);
85945
+ if (!selection) selections.set(layer, selection = { animations: [], names, indices: [] });
85946
+ selection.animations.push(animation);
85947
+ selection.indices.push(index);
85621
85948
  });
85622
- const main = selections.find(({ animation }) => (animation.layer ?? "main") === "main");
85623
- if (main) main.names.push(...Object.keys(this.entityLayers).filter((name2) => !targets.has(name2.split("#")[0])));
85949
+ const main = selections.get("main");
85950
+ if (main) main.names.push(...Object.keys(this.entityLayers).filter((name2) => !selections.has(name2.split("#")[0])));
85624
85951
  this.createMeshes();
85625
85952
  this.stopAnimation();
85626
- for (const { animation, names } of selections) {
85953
+ for (const { animations: clips, names, indices } of selections.values()) {
85627
85954
  const player = new EntityAnimationPlayer();
85628
- player.play(names.map((name2) => this.getLayerGroup(name2)), animation, options, this.partPoses);
85629
- this.animationPlayers.push(player);
85955
+ player.playAnimations(
85956
+ names.map((name2) => this.getLayerGroup(name2)),
85957
+ clips,
85958
+ { ...options, weights: indices.map((index) => weights[index]) },
85959
+ this.partPoses
85960
+ );
85961
+ this.animationPlayers.push({ player, indices });
85630
85962
  }
85631
85963
  this.notifyDirty();
85632
85964
  }
85965
+ /** Changes weights in active clip order, notifying the scene without restarting the shared clock. Has no effect without an animation. */
85966
+ setAnimationWeights(weights) {
85967
+ if (!this.animationPlayers.length) return;
85968
+ const values = validateAnimationWeights(weights, this.activeAnimations.length);
85969
+ for (const { player, indices } of this.animationPlayers) player.setWeights(indices.map((index) => values[index]));
85970
+ this.notifyDirty();
85971
+ }
85633
85972
  /** Stops all clips and restores the poses from before playback. */
85634
85973
  stopAnimation() {
85635
85974
  const players = this.animationPlayers.splice(0);
85636
- for (const player of players) player.stop();
85975
+ for (const { player } of players) player.stop();
85637
85976
  if (players.length) this.notifyDirty();
85638
85977
  }
85639
85978
  /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
85640
85979
  setAnimationTime(seconds) {
85641
- for (const player of this.animationPlayers) player.setTime(seconds);
85980
+ for (const { player } of this.animationPlayers) player.setTime(seconds);
85642
85981
  if (this.animationPlayers.length) this.notifyDirty();
85643
85982
  }
85644
85983
  /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
85645
85984
  advanceAnimation(deltaSeconds) {
85646
- for (const player of this.animationPlayers) player.advance(deltaSeconds);
85985
+ for (const { player } of this.animationPlayers) player.advance(deltaSeconds);
85647
85986
  if (this.animationPlayers.length) this.notifyDirty();
85648
85987
  }
85649
85988
  //</editor-fold>
@@ -87881,6 +88220,7 @@ ${key}` : key;
87881
88220
  }
87882
88221
  const font = await Fonts.get(options.font);
87883
88222
  const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
88223
+ await font.resolve?.(runs.map((run) => run.text).join(""));
87884
88224
  const glyphs = [], lineWidths = [];
87885
88225
  let line = [], width = 0;
87886
88226
  let trailingNewline = false;
@@ -87907,7 +88247,7 @@ ${key}` : key;
87907
88247
  continue;
87908
88248
  }
87909
88249
  const glyph = font.glyphs.get(character);
87910
- const advance = (glyph?.advance ?? 6) + (style.bold ? 1 : 0);
88250
+ const advance = (glyph?.advance ?? 6) + (style.bold ? glyph?.boldOffset ?? 1 : 0);
87911
88251
  line.push({ character, glyph, advance, style, x: 0, y: 0 });
87912
88252
  width += advance;
87913
88253
  while (width > maxWidth && line.length > 1) {
@@ -87935,7 +88275,7 @@ ${key}` : key;
87935
88275
  batches.push(batch);
87936
88276
  }
87937
88277
  const color3 = new Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
87938
- const offset = isShadow ? 1 : 0;
88278
+ const offset = isShadow ? glyph?.shadowOffset ?? 1 : 0;
87939
88279
  const top = glyph ? 7 - glyph.ascent : 0;
87940
88280
  const height = glyph ? glyph.height * glyph.scale : 8;
87941
88281
  const width = glyph ? glyph.width * glyph.scale : 5;
@@ -87965,7 +88305,7 @@ ${key}` : key;
87965
88305
  for (let i4 = 0; i4 < 4; i4++) batch.colors.push(color3.r, color3.g, color3.b);
87966
88306
  batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
87967
88307
  };
87968
- for (const boldOffset of style.bold ? [0, 1] : [0]) {
88308
+ for (const boldOffset of style.bold ? [0, glyph?.boldOffset ?? 1] : [0]) {
87969
88309
  if (glyph) quad(0, top, width, top + height, boldOffset);
87970
88310
  else {
87971
88311
  quad(0, 0, 5, 1, boldOffset);
@@ -94105,6 +94445,7 @@ ${shader.fragmentShader}`.replace(
94105
94445
  toRadians: () => toRadians,
94106
94446
  toTrianglesDrawMode: () => toTrianglesDrawMode,
94107
94447
  trimCanvas: () => trimCanvas,
94448
+ validateAnimationWeights: () => validateAnimationWeights,
94108
94449
  yieldToEventLoop: () => yieldToEventLoop
94109
94450
  });
94110
94451
  init_dirname();
@@ -104074,7 +104415,7 @@ gl_FragDepth=readDepth(vUv);
104074
104415
 
104075
104416
  // src/world/SectionGeometry.ts
104076
104417
  function buildSectionGeometry(input) {
104077
- const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
104418
+ const { count, indices, templates, cullMasks, templateData, atlases: atlases2, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
104078
104419
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
104079
104420
  if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
104080
104421
  if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
@@ -104109,8 +104450,8 @@ gl_FragDepth=readDepth(vUv);
104109
104450
  const locations = /* @__PURE__ */ new Map();
104110
104451
  for (const [layer, layerAtlases] of [...visibleAtlases].sort(([a4], [b4]) => a4 - b4)) {
104111
104452
  const firstPage = pages.length;
104112
- for (const atlas of [...layerAtlases].sort((a4, b4) => Number(atlases[a4].animated) - Number(atlases[b4].animated) || (atlases[a4].animated ? a4 - b4 : atlases[b4].height - atlases[a4].height))) {
104113
- const { width, height, animated } = atlases[atlas];
104453
+ for (const atlas of [...layerAtlases].sort((a4, b4) => Number(atlases2[a4].animated) - Number(atlases2[b4].animated) || (atlases2[a4].animated ? a4 - b4 : atlases2[b4].height - atlases2[a4].height))) {
104454
+ const { width, height, animated } = atlases2[atlas];
104114
104455
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
104115
104456
  let selected = -1;
104116
104457
  for (let i4 = firstPage; !animated && i4 < pages.length; i4++) {
@@ -104153,7 +104494,7 @@ gl_FragDepth=readDepth(vUv);
104153
104494
  });
104154
104495
  if (!quads.sprites.length) continue;
104155
104496
  if (!fluidPages.has(fluid)) {
104156
- const { width, height } = atlases[data.atlas];
104497
+ const { width, height } = atlases2[data.atlas];
104157
104498
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
104158
104499
  fluidPages.set(fluid, {
104159
104500
  layer: 2,
@@ -104171,7 +104512,7 @@ gl_FragDepth=readDepth(vUv);
104171
104512
  const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
104172
104513
  fluidPages.get(fluid).fluids.push({ quads, x: x4, y: y5, z: z4, data, tint });
104173
104514
  }
104174
- pages.push(...[...fluidPages.values()].sort((a4, b4) => Number(atlases[a4.placements[0].atlas].animated) - Number(atlases[b4.placements[0].atlas].animated) || a4.placements[0].atlas - b4.placements[0].atlas));
104515
+ pages.push(...[...fluidPages.values()].sort((a4, b4) => Number(atlases2[a4.placements[0].atlas].animated) - Number(atlases2[b4.placements[0].atlas].animated) || a4.placements[0].atlas - b4.placements[0].atlas));
104175
104516
  }
104176
104517
  return pages.map((page) => {
104177
104518
  let faces = page.fluids.reduce((sum, fluid) => sum + fluid.quads.sprites.length, 0);
@@ -104188,7 +104529,7 @@ gl_FragDepth=readDepth(vUv);
104188
104529
  let vertex2 = 0, indexOffset = 0;
104189
104530
  for (const entry of page.entries) {
104190
104531
  const template = templateData[templates[entry]];
104191
- const atlas = atlases[template.atlas];
104532
+ const atlas = atlases2[template.atlas];
104192
104533
  const placement = locations.get(template.atlas);
104193
104534
  const mapU = (u4) => (placement.x + u4 * atlas.width) / page.width;
104194
104535
  const mapV = (v5) => 1 - (placement.y + (1 - v5) * atlas.height) / page.height;
@@ -104227,7 +104568,7 @@ gl_FragDepth=readDepth(vUv);
104227
104568
  }
104228
104569
  }
104229
104570
  for (const { quads, x: x4, y: y5, z: z4, data, tint } of page.fluids) {
104230
- const atlas = atlases[data.atlas];
104571
+ const atlas = atlases2[data.atlas];
104231
104572
  const placement = page.placements[0];
104232
104573
  const mapU = (u4) => (placement.x + u4 * atlas.width) / page.width;
104233
104574
  const mapV = (v5) => 1 - (placement.y + (1 - v5) * atlas.height) / page.height;
@@ -104331,7 +104672,7 @@ gl_FragDepth=readDepth(vUv);
104331
104672
  var WATER_TINT = new Color(4159204).toArray();
104332
104673
  function toInput(entries, maxAtlasSize, fluids, lighting) {
104333
104674
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
104334
- const atlases = [];
104675
+ const atlases2 = [];
104335
104676
  const atlasIds = /* @__PURE__ */ new Map();
104336
104677
  const templateIds = /* @__PURE__ */ new Map();
104337
104678
  const input = {
@@ -104354,9 +104695,9 @@ gl_FragDepth=readDepth(vUv);
104354
104695
  if (id === void 0) {
104355
104696
  let atlas = atlasIds.get(template.atlas);
104356
104697
  if (atlas === void 0) {
104357
- atlas = atlases.length;
104698
+ atlas = atlases2.length;
104358
104699
  atlasIds.set(template.atlas, atlas);
104359
- atlases.push(template.atlas);
104700
+ atlases2.push(template.atlas);
104360
104701
  input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height, animated: template.atlas.hasAnimation });
104361
104702
  }
104362
104703
  let data = templateCache.get(template);
@@ -104415,9 +104756,9 @@ gl_FragDepth=readDepth(vUv);
104415
104756
  if (!atlas) continue;
104416
104757
  let id = atlasIds.get(atlas);
104417
104758
  if (id === void 0) {
104418
- id = atlases.length;
104759
+ id = atlases2.length;
104419
104760
  atlasIds.set(atlas, id);
104420
- atlases.push(atlas);
104761
+ atlases2.push(atlas);
104421
104762
  input.atlases.push({ width: atlas.image.width, height: atlas.image.height, animated: atlas.hasAnimation });
104422
104763
  }
104423
104764
  input.fluids[kind] = {
@@ -104428,7 +104769,7 @@ gl_FragDepth=readDepth(vUv);
104428
104769
  };
104429
104770
  }
104430
104771
  }
104431
- return { input, atlases };
104772
+ return { input, atlases: atlases2 };
104432
104773
  }
104433
104774
  var SectionMesh = class _SectionMesh extends Group {
104434
104775
  constructor() {
@@ -104443,23 +104784,23 @@ gl_FragDepth=readDepth(vUv);
104443
104784
  }
104444
104785
  /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
104445
104786
  static build(entries, maxAtlasSize = 2048, fluids, lighting) {
104446
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
104447
- return this.fromPages(buildSectionGeometry(input), atlases);
104787
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
104788
+ return this.fromPages(buildSectionGeometry(input), atlases2);
104448
104789
  }
104449
104790
  /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
104450
104791
  static async buildAsync(entries, maxAtlasSize = 2048, fluids, lighting) {
104451
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
104792
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
104452
104793
  const worker = SectionWorker.shared();
104453
104794
  const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
104454
- return this.fromPages(pages, atlases);
104795
+ return this.fromPages(pages, atlases2);
104455
104796
  }
104456
- static fromPages(pages, atlases) {
104797
+ static fromPages(pages, atlases2) {
104457
104798
  const section = new _SectionMesh();
104458
104799
  for (const page of pages) {
104459
104800
  const canvas = createCanvas(page.width, page.height);
104460
104801
  const context = canvas.getContext("2d");
104461
104802
  for (const { atlas, x: x4, y: y5 } of page.placements) {
104462
- context.drawImage(atlases[atlas].image.canvas, x4, y5);
104803
+ context.drawImage(atlases2[atlas].image.canvas, x4, y5);
104463
104804
  }
104464
104805
  const geometry = new BufferGeometry();
104465
104806
  geometry.setAttribute("position", new Float32BufferAttribute(page.positions.buffer, 3));
@@ -104470,7 +104811,7 @@ gl_FragDepth=readDepth(vUv);
104470
104811
  geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));
104471
104812
  geometry.computeBoundingBox();
104472
104813
  geometry.computeBoundingSphere();
104473
- const transparent = page.layer === 2 ? atlases[page.placements[0].atlas].hasTranslucency : page.layer === 1;
104814
+ const transparent = page.layer === 2 ? atlases2[page.placements[0].atlas].hasTranslucency : page.layer === 1;
104474
104815
  const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
104475
104816
  material.side = FrontSide;
104476
104817
  if (page.layer === 2) {
@@ -104485,7 +104826,7 @@ gl_FragDepth=readDepth(vUv);
104485
104826
  section.blockIndices.set(mesh, page.blockIndices);
104486
104827
  const unsubscribe = [];
104487
104828
  for (const { atlas: id, x: x4, y: y5 } of page.placements) {
104488
- const atlas = atlases[id];
104829
+ const atlas = atlases2[id];
104489
104830
  if (!atlas.hasAnimation) continue;
104490
104831
  unsubscribe.push(atlas.subscribe(() => {
104491
104832
  context.clearRect(x4, y5, atlas.image.width, atlas.image.height);