minerender 2.1.0-alpha.79 → 2.1.0-alpha.80

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.
@@ -523,12 +523,14 @@ interface ItemModel extends Model {
523
523
  gui_light?: GuiLight;
524
524
  special?: SpecialItemRenderer;
525
525
  tints?: ItemTintSource[];
526
+ /** Stack-component snapshot with namespaced IDs, used to resolve this item's tint sources. */
527
+ components?: Record<string, unknown>;
526
528
  /** Ordered composite children, each with its own textures, display pose, and tint sources. */
527
529
  parts?: ItemModel[];
528
530
  }
529
531
  /** An sRGB packed `0xRRGGBB` value or an RGB triple with components from 0 to 1. */
530
532
  type ItemTintColor = number | TripleArray;
531
- /** A vanilla item color source. Gameplay-dependent sources use their declared default color. */
533
+ /** A vanilla item color source. Missing component values and unsupported gameplay state use the declared default. */
532
534
  type ItemTintSource = {
533
535
  type: "constant" | "minecraft:constant";
534
536
  value: ItemTintColor;
@@ -1242,10 +1244,13 @@ declare class DisplayTransforms {
1242
1244
  static apply(geometry: BufferGeometry, matrix: Matrix4): void;
1243
1245
  }
1244
1246
 
1245
- /** Resolves static item-preview colors from vanilla tint definitions and caller overrides. */
1247
+ /** Resolves item-preview colors from vanilla tint definitions, stack components, and caller overrides. */
1246
1248
  declare class ItemTints {
1247
1249
  /** Resolves preview colors from item definitions; explicit per-index colors take precedence. */
1248
1250
  static get(model: Model, tints?: Record<number, number>): Promise<Record<number, number> | undefined>;
1251
+ private static componentField;
1252
+ private static colorOrDefault;
1253
+ private static componentColor;
1249
1254
  private static rgb;
1250
1255
  }
1251
1256
 
@@ -1662,6 +1667,8 @@ interface GuiTextureLayer extends GuiLayerLayout {
1662
1667
  interface GuiItemLayer extends GuiLayerLayout {
1663
1668
  /** Model key, such as minecraft:item/stone; uses the model's GUI display pose. */
1664
1669
  item: AssetKey | string;
1670
+ /** Supplied item state. GUI layers always select models in the GUI display context. */
1671
+ context?: Omit<ItemModelContext, "displayContext">;
1665
1672
  /** sRGB 0xRRGGBB colors by face tint index, overriding the item's automatic preview colors. */
1666
1673
  tints?: Record<number, number>;
1667
1674
  }
@@ -523,12 +523,14 @@ interface ItemModel extends Model {
523
523
  gui_light?: GuiLight;
524
524
  special?: SpecialItemRenderer;
525
525
  tints?: ItemTintSource[];
526
+ /** Stack-component snapshot with namespaced IDs, used to resolve this item's tint sources. */
527
+ components?: Record<string, unknown>;
526
528
  /** Ordered composite children, each with its own textures, display pose, and tint sources. */
527
529
  parts?: ItemModel[];
528
530
  }
529
531
  /** An sRGB packed `0xRRGGBB` value or an RGB triple with components from 0 to 1. */
530
532
  type ItemTintColor = number | TripleArray;
531
- /** A vanilla item color source. Gameplay-dependent sources use their declared default color. */
533
+ /** A vanilla item color source. Missing component values and unsupported gameplay state use the declared default. */
532
534
  type ItemTintSource = {
533
535
  type: "constant" | "minecraft:constant";
534
536
  value: ItemTintColor;
@@ -1242,10 +1244,13 @@ declare class DisplayTransforms {
1242
1244
  static apply(geometry: BufferGeometry, matrix: Matrix4): void;
1243
1245
  }
1244
1246
 
1245
- /** Resolves static item-preview colors from vanilla tint definitions and caller overrides. */
1247
+ /** Resolves item-preview colors from vanilla tint definitions, stack components, and caller overrides. */
1246
1248
  declare class ItemTints {
1247
1249
  /** Resolves preview colors from item definitions; explicit per-index colors take precedence. */
1248
1250
  static get(model: Model, tints?: Record<number, number>): Promise<Record<number, number> | undefined>;
1251
+ private static componentField;
1252
+ private static colorOrDefault;
1253
+ private static componentColor;
1249
1254
  private static rgb;
1250
1255
  }
1251
1256
 
@@ -1662,6 +1667,8 @@ interface GuiTextureLayer extends GuiLayerLayout {
1662
1667
  interface GuiItemLayer extends GuiLayerLayout {
1663
1668
  /** Model key, such as minecraft:item/stone; uses the model's GUI display pose. */
1664
1669
  item: AssetKey | string;
1670
+ /** Supplied item state. GUI layers always select models in the GUI display context. */
1671
+ context?: Omit<ItemModelContext, "displayContext">;
1665
1672
  /** sRGB 0xRRGGBB colors by face tint index, overriding the item's automatic preview colors. */
1666
1673
  tints?: Record<number, number>;
1667
1674
  }
@@ -1491,6 +1491,7 @@ var init_ModelMerger = __esm({
1491
1491
  }
1492
1492
  merged.hierarchy = models.map((m) => m.parent).filter((p10) => `${p10}`);
1493
1493
  merged.hierarchy.push(`${merged.parent}`);
1494
+ if (model.components) merged.components = model.components;
1494
1495
  return merged;
1495
1496
  }
1496
1497
  static async collectAllParents(model) {
@@ -1566,10 +1567,10 @@ var init_Models = __esm({
1566
1567
  const preview = this.snapshotContext(key, context);
1567
1568
  const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
1568
1569
  const cacheKey = itemKey.serialize() + this.contextKey(preview);
1569
- const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v3:${AssetLoader.persistentKey(cacheKey)}`, async () => {
1570
+ const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v4:${AssetLoader.persistentKey(cacheKey)}`, async () => {
1570
1571
  const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
1571
1572
  if (!result) return void 0;
1572
- if (result.key.assetType !== "items") return { ...result.asset, key };
1573
+ if (result.key.assetType !== "items") return { ...result.asset, key, components: preview.components };
1573
1574
  const load = async (selected) => {
1574
1575
  if ("parts" in selected) {
1575
1576
  return { key, parts: await Promise.all(selected.parts.map(load)) };
@@ -1583,7 +1584,7 @@ var init_Models = __esm({
1583
1584
  modelKey.root = key.root;
1584
1585
  const model2 = await this.getRaw(modelKey);
1585
1586
  if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
1586
- return { ...model2, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
1587
+ return { ...model2, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
1587
1588
  };
1588
1589
  return load(this.selectItemModel(result.asset.model, key, preview));
1589
1590
  });
@@ -1641,7 +1642,7 @@ var init_Models = __esm({
1641
1642
  }
1642
1643
  static contextKey(context) {
1643
1644
  const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
1644
- return `|item-v3:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
1645
+ return `|item-v4:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
1645
1646
  }
1646
1647
  static snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
1647
1648
  if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
@@ -5572,6 +5573,7 @@ var init_ItemTints = __esm({
5572
5573
  static async get(model, tints) {
5573
5574
  const sources = model.tints;
5574
5575
  if (!sources?.length) return tints;
5576
+ const components = model.components ?? {};
5575
5577
  const colors = { ...tints };
5576
5578
  await Promise.all(sources.map(async (source, index) => {
5577
5579
  if (colors[index] !== void 0) return;
@@ -5586,27 +5588,71 @@ var init_ItemTints = __esm({
5586
5588
  break;
5587
5589
  case "dye":
5588
5590
  case "minecraft:dye":
5591
+ colors[index] = this.colorOrDefault(components["minecraft:dyed_color"], source.default, true);
5592
+ break;
5589
5593
  case "potion":
5590
- case "minecraft:potion":
5594
+ case "minecraft:potion": {
5595
+ const value = typeof components["minecraft:potion_contents"] === "string" ? void 0 : this.componentField(components, "potion_contents", "custom_color");
5596
+ colors[index] = this.colorOrDefault(value, source.default);
5597
+ break;
5598
+ }
5591
5599
  case "map_color":
5592
5600
  case "minecraft:map_color":
5601
+ colors[index] = this.colorOrDefault(components["minecraft:map_color"], source.default);
5602
+ break;
5593
5603
  case "firework":
5594
- case "minecraft:firework":
5604
+ case "minecraft:firework": {
5605
+ const values = this.componentField(components, "firework_explosion", "colors");
5606
+ if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint firework_explosion.colors must be an array");
5607
+ if (!values?.length) {
5608
+ colors[index] = this.rgb(source.default);
5609
+ break;
5610
+ }
5611
+ const rgb = values.map((value) => this.componentColor(value));
5612
+ const red = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 16 & 255), 0) / rgb.length);
5613
+ const green = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 8 & 255), 0) / rgb.length);
5614
+ const blue = Math.floor(rgb.reduce((sum, color2) => sum + (color2 & 255), 0) / rgb.length);
5615
+ colors[index] = red << 16 | green << 8 | blue;
5616
+ break;
5617
+ }
5595
5618
  case "team":
5596
5619
  case "minecraft:team":
5597
- case "custom_model_data":
5598
- case "minecraft:custom_model_data":
5599
5620
  colors[index] = this.rgb(source.default);
5600
5621
  break;
5622
+ case "custom_model_data":
5623
+ case "minecraft:custom_model_data": {
5624
+ const colorIndex = source.index ?? 0;
5625
+ if (!Number.isSafeInteger(colorIndex) || colorIndex < 0) throw new MineRenderError("Item tint custom_model_data index must be a nonnegative integer");
5626
+ const values = this.componentField(components, "custom_model_data", "colors");
5627
+ if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint custom_model_data.colors must be an array");
5628
+ colors[index] = this.colorOrDefault(values?.[colorIndex], source.default, true);
5629
+ break;
5630
+ }
5601
5631
  default:
5602
5632
  throw new MineRenderError(`Unsupported item tint source ${source.type}`);
5603
5633
  }
5604
5634
  }));
5605
5635
  return colors;
5606
5636
  }
5637
+ static componentField(components, id, field) {
5638
+ const value = components[`minecraft:${id}`];
5639
+ if (value === void 0) return void 0;
5640
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new MineRenderError(`Item tint ${id} must be an object`);
5641
+ return value[field];
5642
+ }
5643
+ static colorOrDefault(value, fallback, allowTriple = false) {
5644
+ return value === void 0 ? this.rgb(fallback) : this.componentColor(value, allowTriple);
5645
+ }
5646
+ static componentColor(value, allowTriple = false) {
5647
+ if (typeof value === "number" && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647) return this.rgb(value);
5648
+ if (allowTriple && Array.isArray(value) && value.length === 3 && value.every((channel) => typeof channel === "number" && Number.isFinite(channel))) {
5649
+ return this.rgb(value) & 16777215;
5650
+ }
5651
+ throw new MineRenderError(`Item tint component color must be a packed integer${allowTriple ? " or RGB triple" : ""}`);
5652
+ }
5607
5653
  static rgb(color2) {
5608
5654
  if (!Array.isArray(color2)) return color2 & 16777215;
5609
- const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
5655
+ const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)) & 255);
5610
5656
  return red << 16 | green << 8 | blue;
5611
5657
  }
5612
5658
  };
@@ -10116,7 +10162,7 @@ var GuiObject = class extends SceneObject {
10116
10162
  }
10117
10163
  async createItem(layer, index) {
10118
10164
  const key = typeof layer.item === "string" ? AssetKey.parse("models", layer.item) : layer.item;
10119
- const model = await Models.getMerged(key);
10165
+ const model = await Models.getMerged(key, { ...layer.context, displayContext: "gui" /* GUI */ });
10120
10166
  if (!model) throw new Error(`Could not load GUI item ${key.toNamespacedString()}`);
10121
10167
  const item = new ModelObject(model, {
10122
10168
  displayPosition: "gui" /* GUI */,
@@ -14145,6 +14191,7 @@ var _MineRenderScene = class _MineRenderScene extends import_three34.Scene {
14145
14191
  async addSceneObject(asset2, objectSupplier, _options, parent = this) {
14146
14192
  this.dirty = true;
14147
14193
  const obj = await objectSupplier();
14194
+ if (isModelObject(obj)) obj.options.tints = await ItemTints.get(obj.originalModel, obj.options.tints);
14148
14195
  if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
14149
14196
  let key = asset2.key.serialize();
14150
14197
  if (isModelObject(obj)) {
@@ -14178,7 +14225,6 @@ var _MineRenderScene = class _MineRenderScene extends import_three34.Scene {
14178
14225
  * @returns The model, or a reference to one placement of a shared model.
14179
14226
  */
14180
14227
  async addModel(model, options, parent = this) {
14181
- options = { ...options, tints: await ItemTints.get(model, options?.tints) };
14182
14228
  return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
14183
14229
  }
14184
14230
  /**