minerender 2.1.0-alpha.60 → 2.1.0-alpha.62

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
@@ -33790,7 +33790,7 @@ var MineRender = (() => {
33790
33790
  }
33791
33791
  static async getItemModel(key) {
33792
33792
  const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
33793
- const model = await this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(itemKey.serialize()), async () => {
33793
+ const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v2:${AssetLoader.persistentKey(itemKey.serialize())}`, async () => {
33794
33794
  const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
33795
33795
  if (!result) return void 0;
33796
33796
  if (result.key.assetType !== "items") return { ...result.asset, key };
@@ -33799,7 +33799,7 @@ var MineRender = (() => {
33799
33799
  modelKey.root = key.root;
33800
33800
  const model2 = await this.getRaw(modelKey);
33801
33801
  if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
33802
- return selected.special ? { ...model2, special: selected.special } : model2;
33802
+ return { ...model2, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
33803
33803
  });
33804
33804
  return model ? { ...model, key: Object.assign(new AssetKey("", ""), model.key) } : void 0;
33805
33805
  }
@@ -33810,7 +33810,7 @@ var MineRender = (() => {
33810
33810
  }
33811
33811
  switch (node.type.replace(/^minecraft:/, "")) {
33812
33812
  case "model":
33813
- if (typeof node.model === "string" && node.model) return { model: node.model };
33813
+ if (typeof node.model === "string" && node.model) return { model: node.model, tints: node.tints };
33814
33814
  break;
33815
33815
  case "special": {
33816
33816
  if (!node.base || !node.model || typeof node.model !== "object") break;
@@ -78923,11 +78923,11 @@ ${key}` : key;
78923
78923
  return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
78924
78924
  }
78925
78925
  /**
78926
- * Vanilla's keyframe animations of a model by name, or undefined when the model or the selected version has none.
78926
+ * Native and sampled vanilla clips by name, or undefined when the model or selected version has none.
78927
78927
  */
78928
78928
  static async getAnimations(modelKey) {
78929
78929
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
78930
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json");
78930
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
78931
78931
  const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
78932
78932
  return file?.animations;
78933
78933
  }
@@ -78977,7 +78977,7 @@ ${key}` : key;
78977
78977
  let low = 0, high = keyframes.length;
78978
78978
  while (low < high) {
78979
78979
  const middle = low + high >>> 1;
78980
- if (time <= keyframes[middle].time) high = middle;
78980
+ if (time < keyframes[middle].time) high = middle;
78981
78981
  else low = middle + 1;
78982
78982
  }
78983
78983
  const a4 = Math.max(0, low - 1);
@@ -79025,6 +79025,7 @@ ${key}` : key;
79025
79025
  init_dirname();
79026
79026
  init_buffer2();
79027
79027
  init_process2();
79028
+ init_three_module();
79028
79029
  init_EntityAnimation();
79029
79030
  ZERO = [0, 0, 0];
79030
79031
  EntityAnimationPlayer = class {
@@ -79041,25 +79042,45 @@ ${key}` : key;
79041
79042
  return this._time;
79042
79043
  }
79043
79044
  /**
79044
- * Starts an animation on the named parts below each layer root. The current pose of a part becomes its default;
79045
- * bones without a matching part are skipped.
79045
+ * Starts an animation on matching named parts. Supplied baked poses are the baseline for offsets;
79046
+ * without them, the current pose is used. Stopping restores the pose from before playback.
79046
79047
  */
79047
- play(layers, animation, options = {}) {
79048
+ play(layers, animation, options = {}, poses) {
79048
79049
  this.stop();
79049
79050
  this._animation = animation;
79050
79051
  this._time = options.time ?? 0;
79051
79052
  this.loop = options.loop ?? animation.loop;
79052
79053
  this.speed = options.speed ?? 1;
79053
79054
  for (const layer of layers) {
79054
- for (const bone of Object.keys(animation.bones)) {
79055
- const object = layer.getObjectByName(`group:${bone}`);
79056
- if (!object) continue;
79057
- this.parts.push({ bone, object, position: object.position.clone(), rotation: object.rotation.clone(), scale: object.scale.clone() });
79055
+ const objects = [];
79056
+ if (poses) {
79057
+ layer.traverse((object) => {
79058
+ if (poses.has(object)) objects.push(object);
79059
+ });
79060
+ } else {
79061
+ for (const bone of Object.keys(animation.bones)) {
79062
+ const object = layer.getObjectByName(`group:${bone}`);
79063
+ if (object) objects.push(object);
79064
+ }
79065
+ }
79066
+ for (const object of objects) {
79067
+ const bone = object.name.slice("group:".length);
79068
+ const pose = poses?.get(object);
79069
+ this.parts.push({
79070
+ bone,
79071
+ object,
79072
+ position: object.position.clone(),
79073
+ rotation: object.rotation.clone(),
79074
+ scale: object.scale.clone(),
79075
+ basePosition: pose ? new Vector3(...pose.offset) : object.position.clone(),
79076
+ baseRotation: pose ? new Euler(...pose.rotation, "ZYX") : object.rotation.clone(),
79077
+ baseScale: pose ? new Vector3(...pose.scale ?? [1, 1, 1]) : object.scale.clone()
79078
+ });
79058
79079
  }
79059
79080
  }
79060
79081
  this.apply();
79061
79082
  }
79062
- /** Restores the default poses. Returns whether an animation was active. */
79083
+ /** Restores the poses from before playback. Returns whether an animation was active. */
79063
79084
  stop() {
79064
79085
  if (!this._animation) return false;
79065
79086
  for (const part of this.parts) {
@@ -79088,10 +79109,10 @@ ${key}` : key;
79088
79109
  apply() {
79089
79110
  const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
79090
79111
  for (const part of this.parts) {
79091
- const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone];
79092
- part.object.position.set(part.position.x + position[0], part.position.y + position[1], part.position.z + position[2]);
79093
- part.object.rotation.set(part.rotation.x + rotation[0], part.rotation.y + rotation[1], part.rotation.z + rotation[2], part.rotation.order);
79094
- part.object.scale.set(part.scale.x + scale[0], part.scale.y + scale[1], part.scale.z + scale[2]);
79112
+ const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone] ?? {};
79113
+ part.object.position.set(part.basePosition.x + position[0], part.basePosition.y + position[1], part.basePosition.z + position[2]);
79114
+ part.object.rotation.set(part.baseRotation.x + rotation[0], part.baseRotation.y + rotation[1], part.baseRotation.z + rotation[2], part.baseRotation.order);
79115
+ part.object.scale.set(part.baseScale.x + scale[0], part.baseScale.y + scale[1], part.baseScale.z + scale[2]);
79095
79116
  }
79096
79117
  }
79097
79118
  };
@@ -79634,7 +79655,8 @@ ${key}` : key;
79634
79655
  this.scrollMaterials = [];
79635
79656
  /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */
79636
79657
  this.age = 0;
79637
- this.animationPlayer = new EntityAnimationPlayer();
79658
+ this.animationPlayers = [];
79659
+ this.partPoses = /* @__PURE__ */ new Map();
79638
79660
  this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
79639
79661
  this.addEventListener("added", () => this.updateScrollSubscription());
79640
79662
  this.addEventListener("removed", () => this.updateScrollSubscription());
@@ -79651,7 +79673,8 @@ ${key}` : key;
79651
79673
  for (const geometry of this.geometries) geometry.dispose();
79652
79674
  this.geometries.clear();
79653
79675
  for (const dispose of this.disposeWireframes.splice(0)) dispose();
79654
- this.animationPlayer.clear();
79676
+ for (const player of this.animationPlayers.splice(0)) player.clear();
79677
+ this.partPoses.clear();
79655
79678
  super.disposeAndRemoveAllChildren();
79656
79679
  }
79657
79680
  clearScrollMaterials() {
@@ -79683,36 +79706,61 @@ ${key}` : key;
79683
79706
  this.notifyDirty();
79684
79707
  }
79685
79708
  //<editor-fold desc="ANIMATION">
79686
- /** The animation started by {@link playAnimation}, until {@link stopAnimation} or disposal. */
79709
+ /** The first active clip, until playback is stopped or the object is disposed. */
79687
79710
  get animation() {
79688
- return this.animationPlayer.animation;
79711
+ return this.animationPlayers[0]?.animation;
79712
+ }
79713
+ /** Clips playing together, each targeting a separate geometry layer. */
79714
+ get activeAnimations() {
79715
+ return this.animationPlayers.map((player) => player.animation);
79689
79716
  }
79690
79717
  /** Seconds since the animation started, before looping. */
79691
79718
  get animationTime() {
79692
- return this.animationPlayer.time;
79719
+ return this.animationPlayers[0]?.time ?? 0;
79693
79720
  }
79694
79721
  /**
79695
- * Poses the object from a keyframe animation of {@link Entities.getAnimations}, replacing the current one.
79696
- * Parts of every layer are matched by name and their current pose is the default the offsets add to.
79722
+ * Plays a clip from {@link Entities.getAnimations}, replacing all active clips.
79723
+ * Offsets apply to the baked pose in the clip's layer (`main` by default), including repeated draws of that layer.
79697
79724
  * The object owns no clock: call {@link advanceAnimation} per frame, or {@link setAnimationTime}.
79698
79725
  */
79699
79726
  playAnimation(animation, options) {
79727
+ this.playAnimations([animation], options);
79728
+ }
79729
+ /** Plays one clip per selected geometry layer, with a shared clock and no blending between clips. */
79730
+ playAnimations(animations, options) {
79731
+ const targets = /* @__PURE__ */ new Set();
79732
+ const selections = animations.map((animation) => {
79733
+ const layer = animation.layer ?? "main";
79734
+ if (targets.has(layer)) throw new Error(`Multiple animations target layer "${layer}"`);
79735
+ targets.add(layer);
79736
+ const names = Object.keys(this.entityLayers).filter((name2) => name2.split("#")[0] === layer);
79737
+ if (!names.length) throw new Error(`Entity ${this.entity.id} has no selected animation layer "${layer}"`);
79738
+ return { animation, names };
79739
+ });
79700
79740
  this.createMeshes();
79701
- const layers = Object.keys(this.entityLayers).map((name2) => this.getLayerGroup(name2)).filter((layer) => !!layer);
79702
- this.animationPlayer.play(layers, animation, options);
79741
+ this.stopAnimation();
79742
+ for (const { animation, names } of selections) {
79743
+ const player = new EntityAnimationPlayer();
79744
+ player.play(names.map((name2) => this.getLayerGroup(name2)), animation, options, this.partPoses);
79745
+ this.animationPlayers.push(player);
79746
+ }
79703
79747
  this.notifyDirty();
79704
79748
  }
79705
- /** Stops the animation and restores the default pose. */
79749
+ /** Stops all clips and restores the poses from before playback. */
79706
79750
  stopAnimation() {
79707
- if (this.animationPlayer.stop()) this.notifyDirty();
79751
+ const players = this.animationPlayers.splice(0);
79752
+ for (const player of players) player.stop();
79753
+ if (players.length) this.notifyDirty();
79708
79754
  }
79709
79755
  /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
79710
79756
  setAnimationTime(seconds) {
79711
- if (this.animationPlayer.setTime(seconds)) this.notifyDirty();
79757
+ for (const player of this.animationPlayers) player.setTime(seconds);
79758
+ if (this.animationPlayers.length) this.notifyDirty();
79712
79759
  }
79713
79760
  /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
79714
79761
  advanceAnimation(deltaSeconds) {
79715
- if (this.animationPlayer.advance(deltaSeconds)) this.notifyDirty();
79762
+ for (const player of this.animationPlayers) player.advance(deltaSeconds);
79763
+ if (this.animationPlayers.length) this.notifyDirty();
79716
79764
  }
79717
79765
  //</editor-fold>
79718
79766
  getLayerGroup(name2) {
@@ -79772,6 +79820,7 @@ ${key}` : key;
79772
79820
  anchor.position.fromArray(part.pose.offset);
79773
79821
  anchor.rotation.set(...part.pose.rotation, "ZYX");
79774
79822
  anchor.scale.fromArray(part.pose.scale ?? [1, 1, 1]);
79823
+ this.partPoses.set(anchor, part.pose);
79775
79824
  parent.add(anchor);
79776
79825
  const size = part.texture ?? textureSize;
79777
79826
  for (const cube of part.cubes) {
@@ -80541,6 +80590,93 @@ ${shader.fragmentShader}`.replace(
80541
80590
  }
80542
80591
  });
80543
80592
 
80593
+ // src/texture/Colormaps.ts
80594
+ var Colormaps;
80595
+ var init_Colormaps = __esm({
80596
+ "src/texture/Colormaps.ts"() {
80597
+ init_dirname();
80598
+ init_buffer2();
80599
+ init_process2();
80600
+ init_AssetKey();
80601
+ init_ModelTextures();
80602
+ init_Caching();
80603
+ init_MineRenderError();
80604
+ Colormaps = class {
80605
+ /** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
80606
+ static async grassColor(origin, temperature = 0.5, downfall = 1) {
80607
+ const key = AssetKey.parse("textures", "minecraft:colormap/grass", origin);
80608
+ const temp = Math.fround(temperature);
80609
+ const rain = Math.fround(downfall) * temp;
80610
+ const x4 = Math.floor((1 - temp) * 255), y5 = Math.floor((1 - rain) * 255);
80611
+ return await Caching.blockTintCache.get(`${key.serialize()}|${x4},${y5}`, async () => {
80612
+ const image = await ModelTextures.get(key);
80613
+ if (!image) throw new MineRenderError(`Missing grass colormap: ${key.toNamespacedString()}`);
80614
+ if (image.width !== 256 || image.height !== 256) {
80615
+ throw new MineRenderError("Grass colormaps must be 256 \xD7 256 pixels");
80616
+ }
80617
+ const [red, green, blue] = image.data.getImageData(x4, y5, 1, 1).data;
80618
+ return red << 16 | green << 8 | blue;
80619
+ });
80620
+ }
80621
+ };
80622
+ }
80623
+ });
80624
+
80625
+ // src/model/ItemTints.ts
80626
+ var ItemTints;
80627
+ var init_ItemTints = __esm({
80628
+ "src/model/ItemTints.ts"() {
80629
+ init_dirname();
80630
+ init_buffer2();
80631
+ init_process2();
80632
+ init_MineRenderError();
80633
+ init_Colormaps();
80634
+ ItemTints = class {
80635
+ /** Resolves preview colors from item definitions; explicit per-index colors take precedence. */
80636
+ static async get(model, tints) {
80637
+ const sources = model.tints;
80638
+ if (!sources?.length) return tints;
80639
+ const colors = { ...tints };
80640
+ await Promise.all(sources.map(async (source, index) => {
80641
+ if (colors[index] !== void 0) return;
80642
+ switch (source.type) {
80643
+ case "constant":
80644
+ case "minecraft:constant":
80645
+ colors[index] = this.rgb(source.value);
80646
+ break;
80647
+ case "grass":
80648
+ case "minecraft:grass":
80649
+ colors[index] = await Colormaps.grassColor(model.key, source.temperature, source.downfall);
80650
+ break;
80651
+ case "dye":
80652
+ case "minecraft:dye":
80653
+ case "potion":
80654
+ case "minecraft:potion":
80655
+ case "map_color":
80656
+ case "minecraft:map_color":
80657
+ case "firework":
80658
+ case "minecraft:firework":
80659
+ case "team":
80660
+ case "minecraft:team":
80661
+ case "custom_model_data":
80662
+ case "minecraft:custom_model_data":
80663
+ colors[index] = this.rgb(source.default);
80664
+ break;
80665
+ default:
80666
+ throw new MineRenderError(`Unsupported item tint source ${source.type}`);
80667
+ }
80668
+ }));
80669
+ return colors;
80670
+ }
80671
+ static rgb(color2) {
80672
+ if (!Array.isArray(color2)) return color2 & 16777215;
80673
+ const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
80674
+ return red << 16 | green << 8 | blue;
80675
+ }
80676
+ };
80677
+ }
80678
+ });
80679
+
80544
80680
  // src/model/scene/ModelObject.ts
80545
80681
  function isModelObject(obj) {
80546
80682
  return obj.isModelObject;
@@ -80566,6 +80702,7 @@ ${shader.fragmentShader}`.replace(
80566
80702
  init_SpecialItems();
80567
80703
  init_EntityObject();
80568
80704
  init_GuiLight();
80705
+ init_ItemTints();
80569
80706
  p10 = prefix("ModelObject");
80570
80707
  _ModelObject = class _ModelObject extends SceneObject {
80571
80708
  constructor(originalModel, options) {
@@ -80581,6 +80718,7 @@ ${shader.fragmentShader}`.replace(
80581
80718
  this.addEventListener("removed", () => this.updateAnimationSubscription());
80582
80719
  }
80583
80720
  async init() {
80721
+ this.options.tints = await ItemTints.get(this.originalModel, this.options.tints);
80584
80722
  const special = this.originalModel.special;
80585
80723
  if (special) {
80586
80724
  const parts = await SpecialItems.getParts(special, this.originalModel.key?.root);
@@ -83855,6 +83993,7 @@ ${shader.fragmentShader}`.replace(
83855
83993
  CanvasImage: () => CanvasImage,
83856
83994
  Chunk: () => Chunk,
83857
83995
  ChunkData: () => ChunkData,
83996
+ Colormaps: () => Colormaps,
83858
83997
  CubeFace: () => CubeFace,
83859
83998
  CubeFaceIndex: () => CubeFaceIndex,
83860
83999
  DEFAULT_ELEMENTS: () => DEFAULT_ELEMENTS,
@@ -83880,6 +84019,7 @@ ${shader.fragmentShader}`.replace(
83880
84019
  ImageLoader: () => ImageLoader,
83881
84020
  InstanceManager: () => InstanceManager,
83882
84021
  InstanceReference: () => InstanceReference,
84022
+ ItemTints: () => ItemTints,
83883
84023
  MapControls: () => MapControls,
83884
84024
  Materials: () => Materials,
83885
84025
  Meshes: () => Meshes,
@@ -87614,6 +87754,7 @@ property list uchar int vertex_index
87614
87754
 
87615
87755
  // src/model/index.ts
87616
87756
  init_GuiLight();
87757
+ init_ItemTints();
87617
87758
  init_Model();
87618
87759
  init_ModelCulling();
87619
87760
 
@@ -87744,10 +87885,7 @@ property list uchar int vertex_index
87744
87885
  init_dirname();
87745
87886
  init_buffer2();
87746
87887
  init_process2();
87747
- init_AssetKey();
87748
- init_ModelTextures();
87749
- init_Caching();
87750
- init_MineRenderError();
87888
+ init_Colormaps();
87751
87889
 
87752
87890
  // src/model/block/blockTints.json
87753
87891
  var blockTints_default = {
@@ -87802,7 +87940,7 @@ property list uchar int vertex_index
87802
87940
  color2 = rule.color;
87803
87941
  break;
87804
87942
  case "grass":
87805
- color2 = await this.grassColor(key);
87943
+ color2 = await Colormaps.grassColor(key);
87806
87944
  break;
87807
87945
  case "redstone": {
87808
87946
  const power = Number(state.power ?? 0) / 15;
@@ -87820,18 +87958,6 @@ property list uchar int vertex_index
87820
87958
  }
87821
87959
  return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
87822
87960
  }
87823
- static async grassColor(origin) {
87824
- const key = AssetKey.parse("textures", "minecraft:colormap/grass", origin);
87825
- return await Caching.blockTintCache.get(key.serialize(), async () => {
87826
- const image = await ModelTextures.get(key);
87827
- if (!image) throw new MineRenderError(`Missing grass colormap: ${key.toNamespacedString()}`);
87828
- if (image.width !== 256 || image.height !== 256) {
87829
- throw new MineRenderError("Grass colormaps must be 256 \xD7 256 pixels");
87830
- }
87831
- const [red, green, blue] = image.data.getImageData(127, 127, 1, 1).data;
87832
- return red << 16 | green << 8 | blue;
87833
- });
87834
- }
87835
87961
  };
87836
87962
 
87837
87963
  // src/model/block/scene/index.ts
@@ -91145,6 +91271,7 @@ property list uchar int vertex_index
91145
91271
 
91146
91272
  // src/renderer/MineRenderScene.ts
91147
91273
  init_EntityObject();
91274
+ init_ItemTints();
91148
91275
  var _MineRenderScene = class _MineRenderScene extends Scene {
91149
91276
  constructor(options) {
91150
91277
  super();
@@ -91237,6 +91364,7 @@ property list uchar int vertex_index
91237
91364
  }
91238
91365
  }
91239
91366
  async addModel(model, options, parent = this) {
91367
+ options = { ...options, tints: await ItemTints.get(model, options?.tints) };
91240
91368
  return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
91241
91369
  }
91242
91370
  async addBlock(blockState, options, parent = this) {
@@ -94298,6 +94426,7 @@ uniform float opacity;varying vec2 vUv;void main(){vec4 texel=texture2D(inputBuf
94298
94426
  init_dirname();
94299
94427
  init_buffer2();
94300
94428
  init_process2();
94429
+ init_Colormaps();
94301
94430
  init_TextureAtlas();
94302
94431
  init_TextureLoader();
94303
94432
  init_Textures();