@nodecraft/rendermite 1.0.6 → 1.1.0

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 (171) hide show
  1. package/assets/README.md +6 -2
  2. package/assets/builtin/renderchest/models/item/trident.json +372 -0
  3. package/assets/builtin-legacy/renderchest/models/item/trident.json +372 -0
  4. package/assets/mod-models/README.md +130 -0
  5. package/assets/mod-models/ad_astra/models/item/tier_1_rocket.json +4559 -0
  6. package/assets/mod-models/ad_astra/models/item/tier_1_rover.json +6021 -0
  7. package/assets/mod-models/ad_astra/models/item/tier_2_rocket.json +6832 -0
  8. package/assets/mod-models/ad_astra/models/item/tier_3_rocket.json +8522 -0
  9. package/assets/mod-models/ad_astra/models/item/tier_4_rocket.json +7498 -0
  10. package/assets/mod-models/botania/models/item/avatar.json +574 -0
  11. package/assets/mod-models/botania/models/item/bellows.json +423 -0
  12. package/assets/mod-models/botania/models/item/brewery.json +894 -0
  13. package/assets/mod-models/botania/models/item/corporea_index.json +349 -0
  14. package/assets/mod-models/botania/models/item/gaia_pylon.json +1057 -0
  15. package/assets/mod-models/botania/models/item/hourglass.json +502 -0
  16. package/assets/mod-models/botania/models/item/mana_pylon.json +741 -0
  17. package/assets/mod-models/botania/models/item/natura_pylon.json +1057 -0
  18. package/assets/mod-models/botania/models/item/teru_teru_bozu.json +347 -0
  19. package/assets/mod-models/mekanism/models/item/atomic_disassembler.json +1391 -0
  20. package/assets/mod-models/mekanism/models/item/flamethrower.json +1926 -0
  21. package/assets/mod-models/mekanism/models/item/free_runners.json +595 -0
  22. package/assets/mod-models/mekanism/models/item/jetpack.json +1767 -0
  23. package/assets/mod-models/mekanism/models/item/scuba_mask.json +2305 -0
  24. package/assets/mod-models/mekanism/models/item/scuba_tank.json +833 -0
  25. package/assets/mod-tints/README.md +19 -0
  26. package/assets/mod-tints/mysticalagriculture/items/absorption_i_augment.json +16 -0
  27. package/assets/mod-tints/mysticalagriculture/items/absorption_ii_augment.json +16 -0
  28. package/assets/mod-tints/mysticalagriculture/items/absorption_iii_augment.json +16 -0
  29. package/assets/mod-tints/mysticalagriculture/items/absorption_iv_augment.json +16 -0
  30. package/assets/mod-tints/mysticalagriculture/items/absorption_v_augment.json +16 -0
  31. package/assets/mod-tints/mysticalagriculture/items/attack_aoe_i_augment.json +16 -0
  32. package/assets/mod-tints/mysticalagriculture/items/attack_aoe_ii_augment.json +16 -0
  33. package/assets/mod-tints/mysticalagriculture/items/attack_aoe_iii_augment.json +16 -0
  34. package/assets/mod-tints/mysticalagriculture/items/blindness_resistance_augment.json +16 -0
  35. package/assets/mod-tints/mysticalagriculture/items/fire_resistance_augment.json +16 -0
  36. package/assets/mod-tints/mysticalagriculture/items/flight_augment.json +16 -0
  37. package/assets/mod-tints/mysticalagriculture/items/haste_i_augment.json +16 -0
  38. package/assets/mod-tints/mysticalagriculture/items/haste_ii_augment.json +16 -0
  39. package/assets/mod-tints/mysticalagriculture/items/haste_iii_augment.json +16 -0
  40. package/assets/mod-tints/mysticalagriculture/items/health_boost_i_augment.json +16 -0
  41. package/assets/mod-tints/mysticalagriculture/items/health_boost_ii_augment.json +16 -0
  42. package/assets/mod-tints/mysticalagriculture/items/health_boost_iii_augment.json +16 -0
  43. package/assets/mod-tints/mysticalagriculture/items/health_boost_iv_augment.json +16 -0
  44. package/assets/mod-tints/mysticalagriculture/items/health_boost_v_augment.json +16 -0
  45. package/assets/mod-tints/mysticalagriculture/items/hunger_resistance_augment.json +16 -0
  46. package/assets/mod-tints/mysticalagriculture/items/jump_boost_i_augment.json +16 -0
  47. package/assets/mod-tints/mysticalagriculture/items/jump_boost_ii_augment.json +16 -0
  48. package/assets/mod-tints/mysticalagriculture/items/jump_boost_iii_augment.json +16 -0
  49. package/assets/mod-tints/mysticalagriculture/items/luck_i_augment.json +16 -0
  50. package/assets/mod-tints/mysticalagriculture/items/luck_ii_augment.json +16 -0
  51. package/assets/mod-tints/mysticalagriculture/items/luck_iii_augment.json +16 -0
  52. package/assets/mod-tints/mysticalagriculture/items/mining_aoe_i_augment.json +16 -0
  53. package/assets/mod-tints/mysticalagriculture/items/mining_aoe_ii_augment.json +16 -0
  54. package/assets/mod-tints/mysticalagriculture/items/mining_aoe_iii_augment.json +16 -0
  55. package/assets/mod-tints/mysticalagriculture/items/mining_aoe_iv_augment.json +16 -0
  56. package/assets/mod-tints/mysticalagriculture/items/mining_fatigue_resistance_augment.json +16 -0
  57. package/assets/mod-tints/mysticalagriculture/items/nausea_resistance_augment.json +16 -0
  58. package/assets/mod-tints/mysticalagriculture/items/night_vision_augment.json +16 -0
  59. package/assets/mod-tints/mysticalagriculture/items/pathing_aoe_i_augment.json +16 -0
  60. package/assets/mod-tints/mysticalagriculture/items/pathing_aoe_ii_augment.json +16 -0
  61. package/assets/mod-tints/mysticalagriculture/items/pathing_aoe_iii_augment.json +16 -0
  62. package/assets/mod-tints/mysticalagriculture/items/pathing_aoe_iv_augment.json +16 -0
  63. package/assets/mod-tints/mysticalagriculture/items/poison_resistance_augment.json +16 -0
  64. package/assets/mod-tints/mysticalagriculture/items/slow_falling_augment.json +16 -0
  65. package/assets/mod-tints/mysticalagriculture/items/slowness_resistance_augment.json +16 -0
  66. package/assets/mod-tints/mysticalagriculture/items/speed_i_augment.json +16 -0
  67. package/assets/mod-tints/mysticalagriculture/items/speed_ii_augment.json +16 -0
  68. package/assets/mod-tints/mysticalagriculture/items/speed_iii_augment.json +16 -0
  69. package/assets/mod-tints/mysticalagriculture/items/step_assist_augment.json +16 -0
  70. package/assets/mod-tints/mysticalagriculture/items/strength_i_augment.json +16 -0
  71. package/assets/mod-tints/mysticalagriculture/items/strength_ii_augment.json +16 -0
  72. package/assets/mod-tints/mysticalagriculture/items/strength_iii_augment.json +16 -0
  73. package/assets/mod-tints/mysticalagriculture/items/tilling_aoe_i_augment.json +16 -0
  74. package/assets/mod-tints/mysticalagriculture/items/tilling_aoe_ii_augment.json +16 -0
  75. package/assets/mod-tints/mysticalagriculture/items/tilling_aoe_iii_augment.json +16 -0
  76. package/assets/mod-tints/mysticalagriculture/items/tilling_aoe_iv_augment.json +16 -0
  77. package/assets/mod-tints/mysticalagriculture/items/water_breathing_augment.json +16 -0
  78. package/assets/mod-tints/mysticalagriculture/items/weakness_resistance_augment.json +16 -0
  79. package/assets/mod-tints/mysticalagriculture/items/wither_resistance_augment.json +16 -0
  80. package/dist/atlas/registry.js +1 -1
  81. package/dist/blend-8lnfxtLe.d.ts +79 -0
  82. package/dist/blend-KXMclcHk.js +199 -0
  83. package/dist/blockstate-CdstnW2-.js +104 -0
  84. package/dist/{builtin-caYjESh0.js → builtin-WkWjATeh.js} +5 -1
  85. package/dist/builtin.d.ts +3 -1
  86. package/dist/builtin.js +2 -2
  87. package/dist/cli.js +5 -3
  88. package/dist/{compose-CQCb2LVv.js → compose-BGsNFR12.js} +1 -1
  89. package/dist/{definition-3MBm8ar_.js → definition-ColsvWrH.js} +20 -1
  90. package/dist/{definition-BwX6Xxv0.d.ts → definition-ricL7Jqf.d.ts} +2 -0
  91. package/dist/{discovery-0SM0cTLw.js → discovery-B9aqV_Rz.js} +13 -3
  92. package/dist/{discovery-wiJBGryo.d.ts → discovery-BPSUtdm4.d.ts} +1 -1
  93. package/dist/{encode-DaNQF28M.d.ts → encode-DNcXXvYe.d.ts} +1 -1
  94. package/dist/{encode-D6dQu2dJ.js → encode-LR9wxigC.js} +28 -12
  95. package/dist/{entity-fallback-BRCeGKBT.js → entity-fallback-BnFMSHS6.js} +180 -10
  96. package/dist/{face-DmCiHUd5.js → face-CMJ3b3Ae.js} +44 -18
  97. package/dist/face-cQ-pVHoe.d.ts +44 -0
  98. package/dist/{fluid-bucket-D-GpUQjg.js → fluid-bucket-BzoLofB_.js} +7 -2
  99. package/dist/{geo-Diky41Tw.js → geo-w9HHIrb5.js} +87 -13
  100. package/dist/{geometry-uIDha8S4.js → geometry-Bi-Bf5rb.js} +7 -1
  101. package/dist/{geometry-BSVmKx5b.d.ts → geometry-w0yQbzDZ.d.ts} +5 -1
  102. package/dist/index.d.ts +9 -9
  103. package/dist/index.js +10 -10
  104. package/dist/items/blockstate.d.ts +23 -0
  105. package/dist/items/blockstate.js +2 -0
  106. package/dist/items/definition.d.ts +1 -1
  107. package/dist/items/definition.js +1 -1
  108. package/dist/items/discovery.d.ts +1 -1
  109. package/dist/items/discovery.js +1 -1
  110. package/dist/{loader-D22zZME7.d.ts → loader-DD83d80u.d.ts} +2 -1
  111. package/dist/{loader-B8NJO7pR.js → loader-DwkA9LR5.js} +267 -15
  112. package/dist/{loader-CfXMxcEV.js → loader-Lb0ZeILz.js} +19 -7
  113. package/dist/models/loader.d.ts +1 -1
  114. package/dist/models/loader.js +1 -1
  115. package/dist/models/types.d.ts +1 -1
  116. package/dist/models-BKxGkPS7.js +18 -0
  117. package/dist/mods/models.d.ts +5 -0
  118. package/dist/mods/models.js +2 -0
  119. package/dist/mods/prepare.d.ts +1 -1
  120. package/dist/mods/prepare.js +1 -1
  121. package/dist/mods/tints.js +1 -1
  122. package/dist/output/encode.d.ts +1 -1
  123. package/dist/output/encode.js +1 -1
  124. package/dist/{pipeline-BIk1RRIp.js → pipeline-BVYj7OP0.js} +72 -19
  125. package/dist/{pipeline-zq-yU3Te.d.ts → pipeline-CJNiQG-1.d.ts} +15 -5
  126. package/dist/{prepare-BESRrh6K.js → prepare-D56Z-9cg.js} +7 -2
  127. package/dist/{prepare-CPxO0kKz.d.ts → prepare-Dce7-HAe.d.ts} +8 -0
  128. package/dist/{rasterizer-yqJkicm1.d.ts → rasterizer-BSQHEhJ-.d.ts} +4 -2
  129. package/dist/{rasterizer-76qO8TDh.js → rasterizer-DcVJzkxz.js} +13 -5
  130. package/dist/{registry-B9TcjVbs.js → registry-DS5PZ57l.js} +8 -1
  131. package/dist/render/blend.d.ts +2 -2
  132. package/dist/render/blend.js +2 -2
  133. package/dist/render/compose.d.ts +2 -2
  134. package/dist/render/compose.js +1 -1
  135. package/dist/render/entity-fallback.d.ts +19 -5
  136. package/dist/render/entity-fallback.js +2 -2
  137. package/dist/render/face.d.ts +2 -2
  138. package/dist/render/face.js +2 -2
  139. package/dist/render/fluid-bucket.js +1 -1
  140. package/dist/render/geo.d.ts +3 -3
  141. package/dist/render/geo.js +1 -1
  142. package/dist/render/geometry.d.ts +1 -1
  143. package/dist/render/geometry.js +1 -1
  144. package/dist/render/legacy-tints.d.ts +1 -1
  145. package/dist/render/obj.d.ts +5 -3
  146. package/dist/render/obj.js +1 -1
  147. package/dist/render/pipeline.d.ts +1 -1
  148. package/dist/render/pipeline.js +1 -1
  149. package/dist/render/rasterizer.d.ts +1 -1
  150. package/dist/render/rasterizer.js +1 -1
  151. package/dist/render/renderer.d.ts +1 -1
  152. package/dist/render/renderer.js +1 -1
  153. package/dist/render/tints.d.ts +1 -1
  154. package/dist/render/tints.js +1 -1
  155. package/dist/{renderer-DVsZHAvf.d.ts → renderer-C79EQDSn.d.ts} +3 -3
  156. package/dist/{renderer-CjT19tHd.js → renderer-oi1MSF3Y.js} +36 -19
  157. package/dist/{run-BnICOF4I.js → run-C6AP3Bn8.js} +3 -3
  158. package/dist/{run-CKUtynTy.d.ts → run-uq8_hUx9.d.ts} +1 -1
  159. package/dist/run.d.ts +2 -2
  160. package/dist/run.js +2 -2
  161. package/dist/textures/loader.js +1 -1
  162. package/dist/{tints-BtbHegpI.js → tints-3VI_2SNy.js} +107 -1
  163. package/dist/{tints-B8UJmZMu.js → tints-CebmvjmP.js} +2 -2
  164. package/dist/{tints-DLBExVWN.d.ts → tints-Do9Sh3dg.d.ts} +2 -2
  165. package/dist/{types-COGbtA1t.d.ts → types-DpRSF9Vj.d.ts} +8 -0
  166. package/dist/workers/render-worker.d.ts +1 -1
  167. package/dist/workers/render-worker.js +1 -1
  168. package/package.json +1 -1
  169. package/dist/blend-CHP2-j9U.js +0 -90
  170. package/dist/blend-CyDIxCne.d.ts +0 -36
  171. package/dist/face-BIYg14TE.d.ts +0 -30
@@ -0,0 +1,104 @@
1
+ import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
+ import { _ as translation, d as rotationY, s as multiply, u as rotationX } from "./vector-CCxLae_T.js";
3
+ //#region src/items/blockstate.ts
4
+ /**
5
+ * Block models for items the game draws through a block-entity item renderer.
6
+ *
7
+ * A `builtin/entity` item model carries no geometry, but for a BlockItem the
8
+ * renderer usually still draws the block first — Mekanism's `MekanismISTER`
9
+ * calls `getBlockModelShaper().getBlockModel(block.defaultBlockState())` and
10
+ * only then adds its own extras. That model is stated in the mod's own
11
+ * blockstate file, so resolving it is following the data rather than guessing at
12
+ * a name: `mekanism:basic_energy_cube` names `mekanism:block/energy_cube/basic`,
13
+ * which no same-name lookup would ever find.
14
+ *
15
+ * The one thing assets cannot state is which state is the *default*, since that
16
+ * lives in the block's Java constructor. `scoreVariant` picks the variant the
17
+ * game would almost always start from, and prefers an unrotated one so an icon
18
+ * is never accidentally turned away from the camera.
19
+ */
20
+ /**
21
+ * Property values a block's default state overwhelmingly uses. Blocks declare
22
+ * their own defaults in code, so this is the one inference here — but a facing
23
+ * block defaults to north far more often than not, and a machine defaults to
24
+ * idle rather than running.
25
+ */
26
+ const DEFAULT_VALUES = {
27
+ facing: "north",
28
+ active: "false",
29
+ lit: "false",
30
+ powered: "false",
31
+ open: "false",
32
+ half: "lower",
33
+ axis: "y",
34
+ waterlogged: "false"
35
+ };
36
+ /** Higher is more likely to be the state the game shows for a fresh block. */
37
+ function scoreVariant(key, variant) {
38
+ let score = 0;
39
+ if (key === "") score += 4;
40
+ if (!variant.x && !variant.y) score += 2;
41
+ for (const pair of key.split(",")) {
42
+ const [name, value] = pair.split("=", 2);
43
+ if (name && DEFAULT_VALUES[name] === value) score++;
44
+ }
45
+ return score;
46
+ }
47
+ function firstOf(variant) {
48
+ const entry = Array.isArray(variant) ? variant[0] : variant;
49
+ return entry?.model ? entry : null;
50
+ }
51
+ /**
52
+ * The block model an item's blockstate names for its default state, or null
53
+ * when the item has no blockstate or it states nothing usable.
54
+ */
55
+ async function defaultBlockStateModel(resources, id) {
56
+ const locator = ResourceLocator.tryParse(id);
57
+ if (!locator) return null;
58
+ const raw = await resources.getResource("blockstate", locator);
59
+ if (raw === null) return null;
60
+ let json;
61
+ try {
62
+ json = JSON.parse(raw.toString("utf8"));
63
+ } catch {
64
+ return null;
65
+ }
66
+ let best = null;
67
+ let bestScore = -1;
68
+ for (const [key, value] of Object.entries(json.variants ?? {})) {
69
+ const variant = firstOf(value);
70
+ if (!variant) continue;
71
+ const score = scoreVariant(key, variant);
72
+ if (score > bestScore) {
73
+ best = variant;
74
+ bestScore = score;
75
+ }
76
+ }
77
+ if (!best) {
78
+ for (const part of json.multipart ?? []) if (part.when === void 0) {
79
+ best = firstOf(part.apply);
80
+ break;
81
+ }
82
+ }
83
+ if (!best) return null;
84
+ const model = ResourceLocator.tryParse(best.model.includes(":") ? best.model : `${locator.namespace}:${best.model}`);
85
+ return model === null ? null : {
86
+ model,
87
+ xRotation: best.x ?? 0,
88
+ yRotation: best.y ?? 0
89
+ };
90
+ }
91
+ /**
92
+ * The blockstate's own x/y rotation as an item transform. Vanilla applies these
93
+ * about the block's centre, and the game bakes them into the model before the
94
+ * display transform, so they compose the same way here.
95
+ */
96
+ function blockStateTransform(state) {
97
+ const toRadians = Math.PI / 180;
98
+ let matrix = translation(.5, .5, .5);
99
+ matrix = multiply(matrix, rotationX(-state.xRotation * toRadians));
100
+ matrix = multiply(matrix, rotationY(-state.yRotation * toRadians));
101
+ return multiply(matrix, translation(-.5, -.5, -.5));
102
+ }
103
+ //#endregion
104
+ export { defaultBlockStateModel as n, blockStateTransform as t };
@@ -16,5 +16,9 @@ function bedCompatPath() {
16
16
  function modTintPackPath() {
17
17
  return path.join(assetsRoot, "mod-tints");
18
18
  }
19
+ /** Curated geometry for mod items the game draws in code (builtin/entity). */
20
+ function modModelPackPath() {
21
+ return path.join(assetsRoot, "mod-models");
22
+ }
19
23
  //#endregion
20
- export { builtinPackPath as n, modTintPackPath as r, bedCompatPath as t };
24
+ export { modTintPackPath as i, builtinPackPath as n, modModelPackPath as r, bedCompatPath as t };
package/dist/builtin.d.ts CHANGED
@@ -3,5 +3,7 @@ declare function builtinPackPath(era: 'modern' | 'legacy'): string;
3
3
  declare function bedCompatPath(): string;
4
4
  /** Curated tint overrides for mod items that register their colors in code. */
5
5
  declare function modTintPackPath(): string;
6
+ /** Curated geometry for mod items the game draws in code (builtin/entity). */
7
+ declare function modModelPackPath(): string;
6
8
  //#endregion
7
- export { bedCompatPath, builtinPackPath, modTintPackPath };
9
+ export { bedCompatPath, builtinPackPath, modModelPackPath, modTintPackPath };
package/dist/builtin.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as builtinPackPath, r as modTintPackPath, t as bedCompatPath } from "./builtin-caYjESh0.js";
2
- export { bedCompatPath, builtinPackPath, modTintPackPath };
1
+ import { i as modTintPackPath, n as builtinPackPath, r as modModelPackPath, t as bedCompatPath } from "./builtin-WkWjATeh.js";
2
+ export { bedCompatPath, builtinPackPath, modModelPackPath, modTintPackPath };
package/dist/cli.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { n as builtinPackPath, t as bedCompatPath } from "./builtin-caYjESh0.js";
2
+ import { n as builtinPackPath, t as bedCompatPath } from "./builtin-WkWjATeh.js";
3
3
  import { n as LayeredResourceManager, t as FolderResourceManager } from "./manager-CkletR5K.js";
4
- import { n as harvestTintOverrides, t as curatedTintOverrides } from "./tints-B8UJmZMu.js";
4
+ import { t as curatedModelOverrides } from "./models-BKxGkPS7.js";
5
+ import { n as harvestTintOverrides, t as curatedTintOverrides } from "./tints-CebmvjmP.js";
5
6
  import { t as JarResourceManager } from "./jar-D9z-X7tA.js";
6
- import { t as run } from "./run-BnICOF4I.js";
7
+ import { t as run } from "./run-C6AP3Bn8.js";
7
8
  import { t as downloadClientJar } from "./download-D9aARBFp.js";
8
9
  import { a as prepareVersionResources, i as eraForVersion, n as GEOMETRY_DONOR_VERSIONS } from "./prepare-Cc8QZ3j0.js";
9
10
  import path from "node:path";
@@ -166,6 +167,7 @@ if (values.mod?.length) {
166
167
  const base = new LayeredResourceManager([
167
168
  resources,
168
169
  curatedTintOverrides(),
170
+ curatedModelOverrides(),
169
171
  ...values.mod.map((jar) => new JarResourceManager(jar))
170
172
  ]);
171
173
  resources = base;
@@ -1,4 +1,4 @@
1
- import { t as blendPixel } from "./blend-CHP2-j9U.js";
1
+ import { t as blendPixel } from "./blend-KXMclcHk.js";
2
2
  //#region src/render/compose.ts
3
3
  /**
4
4
  * Per-pixel painter's compositing, mirroring upstream Model::combineFaceImages:
@@ -108,9 +108,28 @@ function evaluateItemModel(node, properties = defaultProperties(), parentTransfo
108
108
  }];
109
109
  }
110
110
  return [];
111
+ case "neoforge:fluid_container":
112
+ case "forge:fluid_container": {
113
+ const fluid = typeof item.fluid === "string" ? item.fluid : void 0;
114
+ const base = item.textures?.base;
115
+ return [{
116
+ model: typeof base === "string" && base.includes("item/") ? base : "minecraft:item/bucket",
117
+ tints: [],
118
+ transform,
119
+ ...fluid && !fluid.endsWith(":empty") && { fluid }
120
+ }];
121
+ }
111
122
  case "minecraft:empty":
112
123
  case "minecraft:bundle/selected_item": return [];
113
- default: throw new InvalidItemDefinitionError(`Unknown item model type: ${String(item.type)}`);
124
+ default: {
125
+ const named = typeof item.model === "string" ? item.model : item.base_model;
126
+ if (typeof named === "string") return [{
127
+ model: named,
128
+ tints: Array.isArray(item.tints) ? item.tints : [],
129
+ transform
130
+ }];
131
+ throw new InvalidItemDefinitionError(`Unknown item model type: ${String(item.type)}`);
132
+ }
114
133
  }
115
134
  }
116
135
  //#endregion
@@ -6,6 +6,8 @@ interface ItemRenderInstruction {
6
6
  model: string;
7
7
  tints: TintDefinition[];
8
8
  transform: Mat4;
9
+ /** Fluid to paint into the bucket, from a fluid_container item model. */
10
+ fluid?: string;
9
11
  }
10
12
  interface TintDefinition {
11
13
  type: string;
@@ -1,6 +1,6 @@
1
- import { o as identity } from "./vector-CCxLae_T.js";
2
- import { o as evaluateItemModel } from "./definition-3MBm8ar_.js";
3
1
  import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
+ import { o as identity } from "./vector-CCxLae_T.js";
3
+ import { n as InvalidItemDefinitionError, o as evaluateItemModel } from "./definition-ColsvWrH.js";
4
4
  //#region src/items/discovery.ts
5
5
  /**
6
6
  * Era-adaptive item discovery — the piece no single renderchest tag has:
@@ -78,7 +78,17 @@ async function resolveItem(resources, id, properties) {
78
78
  throw new ItemResolutionError(`Item definition is not valid JSON: ${id}`);
79
79
  }
80
80
  if (parsed.model === void 0) throw new ItemResolutionError(`Item definition has no model node: ${id}`);
81
- return evaluateItemModel(parsed.model, properties);
81
+ try {
82
+ return evaluateItemModel(parsed.model, properties);
83
+ } catch (err) {
84
+ const conventional = new ResourceLocator(locator.namespace, `item/${locator.path}`);
85
+ if (err instanceof InvalidItemDefinitionError && await resources.hasResource("model", conventional)) return [{
86
+ model: conventional.toString(),
87
+ tints: [],
88
+ transform: identity()
89
+ }];
90
+ throw err;
91
+ }
82
92
  }
83
93
  const modelLocator = new ResourceLocator(locator.namespace, `item/${locator.path}`);
84
94
  if (await resources.hasResource("model", modelLocator)) return [{
@@ -1,5 +1,5 @@
1
1
  import { a as ResourceManager } from "./manager-Cv1NeFyF.js";
2
- import { i as ItemRenderInstruction, r as ItemProperties } from "./definition-BwX6Xxv0.js";
2
+ import { i as ItemRenderInstruction, r as ItemProperties } from "./definition-ricL7Jqf.js";
3
3
  //#region src/items/discovery.d.ts
4
4
  declare class ItemResolutionError extends Error {}
5
5
  interface DiscoveredItem {
@@ -1,4 +1,4 @@
1
- import { t as FloatTexture } from "./blend-CyDIxCne.js";
1
+ import { n as FloatTexture } from "./blend-8lnfxtLe.js";
2
2
  //#region src/output/encode.d.ts
3
3
  interface EncodedIcon {
4
4
  png: Buffer;
@@ -1,4 +1,4 @@
1
- import { o as toByteTexture } from "./blend-CHP2-j9U.js";
1
+ import { c as toByteTexture } from "./blend-KXMclcHk.js";
2
2
  import sharp from "sharp";
3
3
  //#region src/output/encode.ts
4
4
  /**
@@ -58,17 +58,25 @@ async function encodeAnimatedIcon(frames, delaysMs, options = {}) {
58
58
  } }).png({ compressionLevel: 0 }).toBuffer();
59
59
  })));
60
60
  const animated = () => sharp(pages, { join: { animated: true } });
61
- const result = { png: (await encodeIcon(frames[0], { webp: false })).png };
62
- if (options.webp !== false) result.webp = await animated().webp({
61
+ const webpPromise = options.webp !== false ? animated().webp({
63
62
  lossless: true,
64
63
  effort: ANIMATED_WEBP_EFFORT,
65
64
  loop: 0,
66
65
  delay: delaysMs
67
- }).toBuffer();
68
- if (options.gif) result.gif = await animated().gif({
66
+ }).toBuffer() : Promise.resolve(void 0);
67
+ const firstFramePromise = encodeIcon(frames[0], { webp: false });
68
+ const gifPromise = options.gif ? animated().gif({
69
69
  loop: 0,
70
70
  delay: delaysMs
71
- }).toBuffer();
71
+ }).toBuffer() : Promise.resolve(void 0);
72
+ const [firstFrame, webp, gif] = await Promise.all([
73
+ firstFramePromise,
74
+ webpPromise,
75
+ gifPromise
76
+ ]);
77
+ const result = { png: firstFrame.png };
78
+ if (webp) result.webp = webp;
79
+ if (gif) result.gif = gif;
72
80
  if (options.avif) try {
73
81
  result.avif = await animated().avif({
74
82
  lossless: true,
@@ -101,19 +109,27 @@ async function encodeIcon(texture, options = {}) {
101
109
  channels: 4
102
110
  } });
103
111
  const paletteSafe = countColors(bytes.data, 256) <= 256;
104
- const result = { png: await raw.clone().png({
112
+ const pngPromise = raw.clone().png({
105
113
  compressionLevel: 9,
106
114
  effort: 10,
107
115
  palette: paletteSafe
108
- }).toBuffer() };
109
- if (options.webp !== false) result.webp = await raw.clone().webp({
116
+ }).toBuffer();
117
+ const webpPromise = options.webp !== false ? raw.clone().webp({
110
118
  lossless: true,
111
119
  effort: STATIC_WEBP_EFFORT
112
- }).toBuffer();
113
- if (options.avif) result.avif = await raw.clone().avif({
120
+ }).toBuffer() : Promise.resolve(void 0);
121
+ const avifPromise = options.avif ? raw.clone().avif({
114
122
  lossless: true,
115
123
  effort: 6
116
- }).toBuffer();
124
+ }).toBuffer() : Promise.resolve(void 0);
125
+ const [png, webp, avif] = await Promise.all([
126
+ pngPromise,
127
+ webpPromise,
128
+ avifPromise
129
+ ]);
130
+ const result = { png };
131
+ if (webp) result.webp = webp;
132
+ if (avif) result.avif = avif;
117
133
  return result;
118
134
  }
119
135
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
- import { i as renderModel } from "./renderer-CjT19tHd.js";
2
+ import { i as renderModel } from "./renderer-oi1MSF3Y.js";
3
3
  //#region src/render/entity-fallback.ts
4
4
  /**
5
5
  * Heuristic rendering for mod items that parent `builtin/entity` — chests,
@@ -64,6 +64,22 @@ async function findChestTexture(resources, namespace, itemName) {
64
64
  }
65
65
  return best;
66
66
  }
67
+ /**
68
+ * An item's English display name, slugified, or null. Mods do not always put the
69
+ * kind in the id — lootr's Loot Chest is `lootr_inventory` — and the lang file is
70
+ * a statement of what the thing is rather than a guess from its name.
71
+ */
72
+ async function displayName(resources, locator) {
73
+ const raw = await resources.getResource("lang", new ResourceLocator(locator.namespace, "en_us")).catch(() => null);
74
+ if (!raw) return null;
75
+ try {
76
+ const lang = JSON.parse(raw.toString("utf8"));
77
+ const value = lang[`item.${locator.namespace}.${locator.path}`] ?? lang[`block.${locator.namespace}.${locator.path}`];
78
+ return typeof value === "string" ? value.toLowerCase().replaceAll(/[^a-z\d]+/g, "_") : null;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
67
83
  /** Clone a resolved model with one texture variable rebound. */
68
84
  function withTexture(model, name, locator) {
69
85
  return {
@@ -99,6 +115,33 @@ const DYE_COLORS = {
99
115
  black: 1908001
100
116
  };
101
117
  const DYE_PREFIX = new RegExp(`^(${Object.keys(DYE_COLORS).join("|")})_(.+)$`);
118
+ /** Skin sheet sizes the mobheads pack has base geometry for. */
119
+ const HEAD_LAYOUTS = {
120
+ "64x32": "head_64_32",
121
+ "64x64": "head_64_64",
122
+ "32x32": "head_32_32"
123
+ };
124
+ /**
125
+ * The entity sheet for a mob, preferring a file named exactly after it. Sheets
126
+ * live at entity/<mob>.png or entity/<mob>/<mob>.png depending on the mod, and a
127
+ * head only ever wants the base skin — never a blink or armour overlay.
128
+ */
129
+ async function findMobTexture(resources, namespace, mob) {
130
+ for (const source of [namespace, "minecraft"]) {
131
+ for (const candidate of [`entity/${mob}`, `entity/${mob}/${mob}`]) {
132
+ const locator = new ResourceLocator(source, candidate);
133
+ if (await resources.hasResource("texture", locator)) return locator;
134
+ }
135
+ let best = null;
136
+ for (const locator of await resources.listResources("texture", source)) {
137
+ const leaf = locator.path.split("/").pop();
138
+ if (!locator.path.startsWith("entity/") || leaf !== mob) continue;
139
+ if (best === null || locator.path.length < best.path.length) best = locator;
140
+ }
141
+ if (best) return best;
142
+ }
143
+ return null;
144
+ }
102
145
  /** Find a full modded bed entity sheet, preferring every distinguishing item-name token. */
103
146
  async function findBedTexture(resources, namespace, itemName) {
104
147
  const wanted = tokens(itemName).filter((token) => token !== "bed");
@@ -120,6 +163,37 @@ async function findBedTexture(resources, namespace, itemName) {
120
163
  }
121
164
  return best;
122
165
  }
166
+ /** The dye name a `<dye>_<thing>` mod item is prefixed with, or null. */
167
+ function dyeVariantName(id) {
168
+ const locator = ResourceLocator.tryParse(id);
169
+ if (!locator || locator.namespace === "minecraft") return null;
170
+ const name = locator.path.split("/").pop() ?? locator.path;
171
+ return DYE_PREFIX.exec(name)?.[1] ?? null;
172
+ }
173
+ /**
174
+ * The dye color a `<dye>_<thing>` item name implies, or null. Mods that name
175
+ * variants after dyes sometimes register the matching DyeColor as the tint in
176
+ * client code, where no asset states it (waystones' sharestones call
177
+ * `DyeColor.getTextColor()`), so the name is the only record of it left.
178
+ *
179
+ * Only ever right for variants that SHARE one uncoloured sprite. A mod that
180
+ * draws each colour its own texture is already the right colour, and tinting it
181
+ * again multiplies the dye in — chipped's blue_paper_lantern came out near
182
+ * black. Callers must check the model's textures; see `dyeVariantName`.
183
+ */
184
+ function dyedVariantTint(id) {
185
+ const locator = ResourceLocator.tryParse(id);
186
+ if (!locator || locator.namespace === "minecraft") return null;
187
+ const name = locator.path.split("/").pop() ?? locator.path;
188
+ const match = DYE_PREFIX.exec(name);
189
+ if (!match) return null;
190
+ const color = DYE_COLORS[match[1]];
191
+ return {
192
+ r: (color >>> 16 & 255) / 255,
193
+ g: (color >>> 8 & 255) / 255,
194
+ b: (color & 255) / 255
195
+ };
196
+ }
123
197
  /**
124
198
  * Dyed variants of vanilla items whose models mods generate at runtime (quark's
125
199
  * `black_frame_empty` parents a block model that exists nowhere on disk): render
@@ -242,19 +316,28 @@ async function spawnEggTints(id, resources, textures) {
242
316
  async function renderEntityFallback(id, models, textures, resources, options) {
243
317
  const locator = ResourceLocator.parse(id);
244
318
  const name = locator.path.split("/").pop() ?? locator.path;
319
+ const searchName = `${name}_${await displayName(resources, locator) ?? ""}`;
245
320
  const headMatch = /^(.+?)_(?:head|skull)$/.exec(name);
246
321
  if (headMatch) {
247
- const parts = headMatch[1].replace(/^(?:carved|wall|floor)_/, "").split("_");
322
+ const mob = headMatch[1].replace(/^(?:carved|wall|floor)_/, "");
323
+ const parts = mob.split("_");
248
324
  for (let keep = parts.length; keep >= 1 && keep >= parts.length - 2; keep--) try {
249
325
  const model = await models.load(`mobheads:item/${parts.slice(0, keep).join("_")}`);
250
326
  return await renderModel(model, textures, options);
251
327
  } catch {
252
328
  continue;
253
329
  }
330
+ const sheet = await findMobTexture(resources, locator.namespace, mob);
331
+ const skin = sheet ? await textures.load(sheet).catch(() => null) : null;
332
+ const base = skin ? HEAD_LAYOUTS[`${skin.width}x${skin.height}`] : void 0;
333
+ if (sheet && base) {
334
+ const geometry = await models.load(`mobheads:item/base/${base}`).catch(() => null);
335
+ if (geometry) return renderModel(withTexture(geometry, "texture", sheet), textures, options);
336
+ }
254
337
  return null;
255
338
  }
256
- if (name.includes("chest") && !name.includes("chestplate") || name.endsWith("_crate")) {
257
- const variant = name.includes("ender") ? "ender" : name.includes("trapped") ? "trapped" : "normal";
339
+ if (searchName.includes("chest") && !searchName.includes("chestplate") || name.endsWith("_crate")) {
340
+ const variant = searchName.includes("ender") ? "ender" : searchName.includes("trapped") ? "trapped" : "normal";
258
341
  const candidates = [[`renderchest:item/chest/${variant === "normal" ? "chest" : `${variant}_chest`}`, null], ["renderchest:item/chest", `minecraft:entity/chest/${variant}`]];
259
342
  const modTexture = await findChestTexture(resources, locator.namespace, name);
260
343
  for (const [modelPath, defaultTexture] of candidates) try {
@@ -277,11 +360,12 @@ async function renderEntityFallback(id, models, textures, resources, options) {
277
360
  return null;
278
361
  }
279
362
  }
280
- if (name.includes("shulker_box")) try {
281
- const model = await models.load("renderchest:item/shulker_box");
282
- return await renderModel(model, textures, options);
283
- } catch {
284
- return null;
363
+ if (searchName.includes("shulker") && !searchName.includes("shell")) {
364
+ const model = await models.load("renderchest:item/shulker_box").catch(() => null);
365
+ if (!model) return null;
366
+ const candidate = await findEntityTexture(resources, locator.namespace, name, "shulker");
367
+ const sheet = candidate && await coversSheet(textures, candidate) ? candidate : ResourceLocator.parse("minecraft:entity/shulker/shulker");
368
+ return renderModel(withTexture(model, "texture", sheet), textures, options).catch(() => null);
285
369
  }
286
370
  if (name.endsWith("banner")) try {
287
371
  const model = await models.load("renderchest:item/banner");
@@ -292,7 +376,93 @@ async function renderEntityFallback(id, models, textures, resources, options) {
292
376
  } catch {
293
377
  return null;
294
378
  }
379
+ if (name.includes("shield")) {
380
+ const model = await models.load("renderchest:item/shield").catch(() => null);
381
+ if (!model) return null;
382
+ const sheet = await findShieldTexture(resources, locator.namespace, name);
383
+ return renderModel(sheet ? withTexture(model, "texture", sheet) : model, textures, options);
384
+ }
385
+ if (name === "trident" || name.endsWith("_trident")) {
386
+ const model = await models.load("renderchest:item/trident").catch(() => null);
387
+ if (!model) return null;
388
+ const sheet = await findTridentTexture(resources, locator.namespace, name) ?? ResourceLocator.parse("minecraft:entity/trident");
389
+ return renderModel(withTexture(model, "texture", sheet), textures, {
390
+ ...options,
391
+ quality: Math.max(options.quality, 4)
392
+ }).catch(() => null);
393
+ }
394
+ return null;
395
+ }
396
+ /**
397
+ * Whether a sheet is a whole skin rather than an overlay. Vanilla's shulker
398
+ * covers 44% of its sheet; a tier trim covers under 5%.
399
+ */
400
+ async function coversSheet(textures, locator) {
401
+ const texture = await textures.load(locator).catch(() => null);
402
+ if (!texture) return false;
403
+ let opaque = 0;
404
+ for (let offset = 3; offset < texture.data.length; offset += 4) if (texture.data[offset] > 16) opaque++;
405
+ return opaque / (texture.width * texture.height) > .2;
406
+ }
407
+ /**
408
+ * A mod's entity sheet for a kind, preferring the one its item name points at.
409
+ * Not restricted to `entity/`: lootr files its whole-skin sheets straight under
410
+ * `textures/` (`lootr:shulker`), and the caller gates on coverage anyway.
411
+ */
412
+ async function findEntityTexture(resources, namespace, itemName, kind) {
413
+ const wanted = tokens(itemName).filter((token) => token !== kind && token !== "box");
414
+ let best = null;
415
+ let bestScore = -1;
416
+ for (const locator of await resources.listResources("texture", namespace)) {
417
+ if (!locator.path.includes(kind)) continue;
418
+ const candidate = tokens(locator.path);
419
+ let score = locator.path.startsWith("entity/") ? 1 : 0;
420
+ for (const token of wanted) if (candidate.includes(token)) score += 2;
421
+ if (score > bestScore) {
422
+ best = locator;
423
+ bestScore = score;
424
+ }
425
+ }
426
+ return best;
427
+ }
428
+ /**
429
+ * A trident's entity sheet. Vanilla's is `entity/trident` (32×32) and mods
430
+ * that reuse the vanilla model ship the same path under their own namespace;
431
+ * a mod that names its sheet after the item instead (an `iron_trident` with
432
+ * `entity/iron_trident`) is covered by the item-name fallback. Searches the
433
+ * item's namespace first, then vanilla.
434
+ */
435
+ async function findTridentTexture(resources, namespace, itemName) {
436
+ for (const source of [namespace, "minecraft"]) for (const candidate of [
437
+ "entity/trident",
438
+ "entity/trident/trident",
439
+ `entity/${itemName}`
440
+ ]) {
441
+ const locator = new ResourceLocator(source, candidate);
442
+ if (await resources.hasResource("texture", locator)) return locator;
443
+ }
295
444
  return null;
296
445
  }
446
+ /**
447
+ * A mod's shield face, scored by how much of the item name it accounts for.
448
+ * `_blocking` is the raised pose rather than a different shield, and a
449
+ * `_nopattern` sheet is the plain one a banner would otherwise decorate.
450
+ */
451
+ async function findShieldTexture(resources, namespace, itemName) {
452
+ const wanted = tokens(itemName).filter((token) => token !== "shield" && token !== "blocking");
453
+ let best = null;
454
+ let bestScore = 0;
455
+ for (const locator of await resources.listResources("texture", namespace)) {
456
+ if (!locator.path.startsWith("entity/") || !locator.path.includes("shield")) continue;
457
+ const candidate = tokens(locator.path);
458
+ let score = candidate.includes("nopattern") ? 1 : 0;
459
+ for (const token of wanted) if (candidate.includes(token)) score += 2;
460
+ if (score > bestScore) {
461
+ best = locator;
462
+ bestScore = score;
463
+ }
464
+ }
465
+ return best;
466
+ }
297
467
  //#endregion
298
- export { renderEntityFallback as n, spawnEggTints as r, renderDyedVariantFallback as t };
468
+ export { spawnEggTints as a, renderEntityFallback as i, dyedVariantTint as n, renderDyedVariantFallback as r, dyeVariantName as t };
@@ -1,11 +1,20 @@
1
1
  import { c as normalize, g as transformVector, h as transformDirection, m as sub, r as dot, t as cross } from "./vector-CCxLae_T.js";
2
- import { a as tintTexture, n as cloneFloatTexture, r as darkenTexture } from "./blend-CHP2-j9U.js";
3
- import { a as drawTexturedTriangle, i as createFaceBuffer, n as clearFaceBuffer, o as phpRound, r as compactFaceBuffer, t as RowCursor } from "./rasterizer-76qO8TDh.js";
2
+ import { a as scaleTextureAlpha, n as cloneFloatTexture, r as darkenTexture, s as tintTexture, u as uvWindow } from "./blend-KXMclcHk.js";
3
+ import { a as drawTexturedTriangle, i as createFaceBuffer, n as clearFaceBuffer, o as phpRound, r as compactFaceBuffer, t as RowCursor } from "./rasterizer-DcVJzkxz.js";
4
4
  //#region src/render/face.ts
5
5
  /**
6
6
  * Face rendering: cull, transform, project, tint, darken, rasterize — a faithful
7
7
  * port of upstream Face::render (docs/research/renderchest-spec.md §4).
8
8
  */
9
+ function createShadeCache() {
10
+ return {
11
+ byTexture: /* @__PURE__ */ new WeakMap(),
12
+ bytes: 0
13
+ };
14
+ }
15
+ /** Roughly a small sheet reused freely, or one large sheet and no more. */
16
+ const MAX_SHADE_CACHE_BYTES = 67108864;
17
+ const FLOAT_BYTES_PER_TEXEL = 32;
9
18
  function faceMaxZ(geometry) {
10
19
  return Math.max(geometry.vertices[0][2], geometry.vertices[1][2], geometry.vertices[2][2], geometry.vertices[3][2]);
11
20
  }
@@ -54,19 +63,36 @@ function renderFace(face, width, height, transformation, scratch, shadeCache) {
54
63
  const absU2 = geometry.uv2[0] * face.texture.width;
55
64
  const absV2 = geometry.uv2[1] * face.texture.height;
56
65
  const tint = geometry.tintIndex !== null ? face.tint : null;
57
- const darkness = 1 - face.light.lightLevelForNormal(shadeDirection);
58
- const shadeKey = `${tint ? `${tint.r},${tint.g},${tint.b}` : "-"}|${darkness}|${absU1},${absV1},${absU2},${absV2}`;
59
- let byKey = shadeCache?.get(face.texture);
60
- let texture = byKey?.get(shadeKey);
61
- if (!texture) {
62
- texture = cloneFloatTexture(face.texture);
63
- if (tint) tintTexture(texture, tint.r, tint.g, tint.b);
64
- darkenTexture(texture, darkness, absU1, absV1, absU2, absV2);
65
- if (shadeCache) {
66
- byKey ??= /* @__PURE__ */ new Map();
67
- byKey.set(shadeKey, texture);
68
- shadeCache.set(face.texture, byKey);
69
- }
66
+ const darkness = geometry.emissive ? 0 : 1 - face.light.lightLevelForNormal(shadeDirection);
67
+ const [windowX1, windowY1, windowX2, windowY2] = uvWindow(face.texture, absU1, absV1, absU2, absV2);
68
+ let shading = {
69
+ tint,
70
+ darkness,
71
+ alpha: geometry.alpha,
72
+ windowX1,
73
+ windowY1,
74
+ windowX2,
75
+ windowY2
76
+ };
77
+ let texture = face.texture;
78
+ const shadeKey = `${tint ? `${tint.r},${tint.g},${tint.b}` : "-"}|${darkness}|${geometry.alpha}|${absU1},${absV1},${absU2},${absV2}`;
79
+ const byKey = shadeCache?.byTexture.get(face.texture);
80
+ const cached = byKey?.get(shadeKey);
81
+ const cloneBytes = face.texture.width * face.texture.height * FLOAT_BYTES_PER_TEXEL;
82
+ if (cached) {
83
+ texture = cached;
84
+ shading = null;
85
+ } else if (shadeCache && shadeCache.bytes + cloneBytes <= MAX_SHADE_CACHE_BYTES) {
86
+ const shaded = cloneFloatTexture(face.texture);
87
+ if (tint) tintTexture(shaded, tint.r, tint.g, tint.b);
88
+ darkenTexture(shaded, darkness, absU1, absV1, absU2, absV2);
89
+ if (geometry.alpha < 1) scaleTextureAlpha(shaded, geometry.alpha, absU1, absV1, absU2, absV2);
90
+ const store = byKey ?? /* @__PURE__ */ new Map();
91
+ store.set(shadeKey, shaded);
92
+ shadeCache.byTexture.set(face.texture, store);
93
+ shadeCache.bytes += cloneBytes;
94
+ texture = shaded;
95
+ shading = null;
70
96
  }
71
97
  const uvs = [
72
98
  [geometry.uv1[0], geometry.uv1[1]],
@@ -97,9 +123,9 @@ function renderFace(face, width, height, transformation, scratch, shadeCache) {
97
123
  };
98
124
  };
99
125
  const cursor = new RowCursor();
100
- drawTexturedTriangle(point(0), point(2), point(3), buffer, texture, cursor);
101
- drawTexturedTriangle(point(0), point(1), point(2), buffer, texture, cursor);
126
+ drawTexturedTriangle(point(0), point(2), point(3), buffer, texture, cursor, shading);
127
+ drawTexturedTriangle(point(0), point(1), point(2), buffer, texture, cursor, shading);
102
128
  return compactFaceBuffer(buffer);
103
129
  }
104
130
  //#endregion
105
- export { renderFace as n, faceMaxZ as t };
131
+ export { faceMaxZ as n, renderFace as r, createShadeCache as t };