@nodecraft/rendermite 1.1.4 → 1.2.1

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 (127) hide show
  1. package/README.md +27 -2
  2. package/assets/builtin/renderchest/models/block/banner_standing.json +225 -0
  3. package/assets/builtin/renderchest/models/block/banner_wall.json +155 -0
  4. package/assets/builtin/renderchest/models/block/bed_foot.json +44 -0
  5. package/assets/builtin/renderchest/models/block/bed_head.json +44 -0
  6. package/assets/builtin/renderchest/models/block/chest_left.json +182 -0
  7. package/assets/builtin/renderchest/models/block/chest_right.json +182 -0
  8. package/assets/builtin/renderchest/models/block/copper_golem_statue.json +709 -0
  9. package/assets/builtin/renderchest/models/block/fluid.json +83 -0
  10. package/assets/builtin/renderchest/models/block/fluid_tinted.json +89 -0
  11. package/assets/builtin/renderchest/models/block/hanging_sign.json +54 -0
  12. package/assets/builtin/renderchest/models/block/standing_sign.json +32 -0
  13. package/assets/builtin/renderchest/models/block/wall_sign.json +19 -0
  14. package/dist/atlas/registry.js +1 -1
  15. package/dist/atlas/sources.js +1 -1
  16. package/dist/atlas-pack-CoOih7By.js +130 -0
  17. package/dist/atlas-pack-DRTrWcSU.d.ts +41 -0
  18. package/dist/block-entities-xv3bQpEX.js +490 -0
  19. package/dist/blocks/atlas-pack.d.ts +2 -0
  20. package/dist/blocks/atlas-pack.js +2 -0
  21. package/dist/blocks/block-entities.d.ts +93 -0
  22. package/dist/blocks/block-entities.js +2 -0
  23. package/dist/blocks/blockstate-eval.d.ts +2 -0
  24. package/dist/blocks/blockstate-eval.js +2 -0
  25. package/dist/blocks/cli.d.ts +9 -0
  26. package/dist/blocks/cli.js +2 -0
  27. package/dist/blocks/export.d.ts +2 -0
  28. package/dist/blocks/export.js +2 -0
  29. package/dist/blocks/obj-mesh.d.ts +2 -0
  30. package/dist/blocks/obj-mesh.js +2 -0
  31. package/dist/blocks/tints.d.ts +2 -0
  32. package/dist/blocks/tints.js +2 -0
  33. package/dist/{blockstate-CdstnW2-.js → blockstate-DVb8Mhxc.js} +1 -1
  34. package/dist/blockstate-eval-BRQ3NWY2.js +264 -0
  35. package/dist/blockstate-eval-CObtPWzJ.d.ts +122 -0
  36. package/dist/cli-CWUKXFbx.js +161 -0
  37. package/dist/cli.js +9 -7
  38. package/dist/{descriptor-DWebtkI1.js → descriptor-BVIoEpPO.js} +3 -3
  39. package/dist/{discovery-B9aqV_Rz.js → discovery-CsZu08re.js} +1 -1
  40. package/dist/dynamic/generators.js +1 -1
  41. package/dist/{entity-fallback-Cu22uEIK.js → entity-fallback-BTkM8j0V.js} +3 -155
  42. package/dist/entity-textures-DFRIVdF1.js +209 -0
  43. package/dist/export-BIpqNU3H.d.ts +44 -0
  44. package/dist/export-DY5sGwt0.js +790 -0
  45. package/dist/{face-cQ-pVHoe.d.ts → face-BKwkeTps.d.ts} +1 -1
  46. package/dist/{fluid-bucket-BzoLofB_.js → fluid-bucket-B-I4ZYHl.js} +1 -1
  47. package/dist/{generators-BlG4KmtP.js → generators-BCZVlWig.js} +1 -1
  48. package/dist/{geo-w9HHIrb5.js → geo-BguldlU-.js} +5 -5
  49. package/dist/{geometry-w0yQbzDZ.d.ts → geometry-Bkur58Vi.d.ts} +1 -1
  50. package/dist/index.d.ts +10 -5
  51. package/dist/index.js +21 -16
  52. package/dist/{item-styles-upgEO1yP.js → item-styles-DjYRdyHi.js} +1 -1
  53. package/dist/items/blockstate.js +1 -1
  54. package/dist/items/discovery.js +1 -1
  55. package/dist/{jar-D9z-X7tA.js → jar-5aLDI0nT.js} +2 -2
  56. package/dist/{legacy-reshape-C05SfAPR.js → legacy-reshape-CcdstKyI.js} +2 -2
  57. package/dist/{legacy-tints-B09J0Rxh.js → legacy-tints-l-541wgI.js} +1 -1
  58. package/dist/{loader-CWsTZgrk.js → loader-C-NuJaEi.js} +119 -9
  59. package/dist/{loader-Lb0ZeILz.js → loader-C18GpurU.js} +1 -1
  60. package/dist/{loader-DD83d80u.d.ts → loader-C_1_amsq.d.ts} +18 -1
  61. package/dist/{locator-ycZzh_6z.js → locator-COMEtEn9.js} +23 -1
  62. package/dist/{manager-CkletR5K.js → manager-CYbAkWuT.js} +2 -2
  63. package/dist/models/loader.d.ts +1 -1
  64. package/dist/models/loader.js +1 -1
  65. package/dist/models/types.d.ts +1 -1
  66. package/dist/{models-BKxGkPS7.js → models-CQwcTZcg.js} +1 -1
  67. package/dist/mods/models.js +1 -1
  68. package/dist/mods/prepare.js +1 -1
  69. package/dist/mods/synthesize.js +1 -1
  70. package/dist/mods/tints.js +1 -1
  71. package/dist/{renderer-DB1Oe7xs.js → obj-bvs3gwex.js} +234 -234
  72. package/dist/obj-mesh-C6Sp4Ysc.d.ts +41 -0
  73. package/dist/obj-mesh-rnYbSbpv.js +97 -0
  74. package/dist/output/item-styles.js +1 -1
  75. package/dist/{pipeline-Bp6amYz_.d.ts → pipeline-BDvXcw20.d.ts} +1 -1
  76. package/dist/{pipeline-CgJaVpoL.js → pipeline-DeGRR0CP.js} +14 -14
  77. package/dist/{prepare-D56Z-9cg.js → prepare-B8oPdGYz.js} +5 -5
  78. package/dist/{prepare-Cc8QZ3j0.js → prepare-C95vjt_o.js} +5 -5
  79. package/dist/{registry-DS5PZ57l.js → registry-DlduyjtM.js} +2 -2
  80. package/dist/render/entity-fallback.d.ts +3 -3
  81. package/dist/render/entity-fallback.js +1 -1
  82. package/dist/render/face.d.ts +1 -1
  83. package/dist/render/face.js +1 -1
  84. package/dist/render/fluid-bucket.js +1 -1
  85. package/dist/render/geo.d.ts +2 -2
  86. package/dist/render/geo.js +1 -1
  87. package/dist/render/geometry.d.ts +1 -1
  88. package/dist/render/legacy-tints.d.ts +1 -1
  89. package/dist/render/legacy-tints.js +1 -1
  90. package/dist/render/obj.d.ts +10 -3
  91. package/dist/render/obj.js +2 -2
  92. package/dist/render/pipeline.d.ts +1 -1
  93. package/dist/render/pipeline.js +1 -1
  94. package/dist/render/renderer.d.ts +1 -1
  95. package/dist/render/renderer.js +1 -1
  96. package/dist/render/tints.d.ts +1 -1
  97. package/dist/render/tints.js +1 -1
  98. package/dist/{renderer-C79EQDSn.d.ts → renderer-U20hnhbi.d.ts} +2 -2
  99. package/dist/resources/descriptor.js +1 -1
  100. package/dist/resources/entity-textures.d.ts +51 -0
  101. package/dist/resources/entity-textures.js +2 -0
  102. package/dist/resources/jar.js +1 -1
  103. package/dist/resources/locator.js +1 -1
  104. package/dist/resources/manager.js +1 -1
  105. package/dist/resources/synthetic.js +1 -1
  106. package/dist/{run-uq8_hUx9.d.ts → run-CvIqiZ6T.d.ts} +1 -1
  107. package/dist/{run-U9pM9wKU.js → run-Diql5y56.js} +7 -7
  108. package/dist/run.d.ts +1 -1
  109. package/dist/run.js +1 -1
  110. package/dist/{sources-Bxk-WOzH.js → sources-BlUriDCs.js} +1 -1
  111. package/dist/{synthesize-3Q0aaOnK.js → synthesize-jTjVjPAt.js} +2 -2
  112. package/dist/{synthetic-ZAUjXT7E.js → synthetic-rc9BPT4P.js} +0 -0
  113. package/dist/textures/loader.js +1 -1
  114. package/dist/tints-2k3ZhkY3.d.ts +41 -0
  115. package/dist/{tints-3VI_2SNy.js → tints-B3WQcPtx.js} +1 -1
  116. package/dist/{tints-Do9Sh3dg.d.ts → tints-DqhgFi8a.d.ts} +1 -1
  117. package/dist/tints-a9FWZLma.js +176 -0
  118. package/dist/{tints-CebmvjmP.js → tints-nxtGrqv5.js} +3 -3
  119. package/dist/{types-DpRSF9Vj.d.ts → types-qlp3I8_-.d.ts} +11 -0
  120. package/dist/versions/download.js +1 -1
  121. package/dist/versions/legacy-reshape.js +1 -1
  122. package/dist/versions/prepare.js +1 -1
  123. package/dist/workers/render-worker.d.ts +1 -1
  124. package/dist/workers/render-worker.js +2 -2
  125. package/package.json +1 -1
  126. package/dist/{download-D9aARBFp.js → download-1wAfrGKx.js} +1 -1
  127. package/dist/{face-CMJ3b3Ae.js → face-CKHboPfh.js} +1 -1
@@ -1,239 +1,13 @@
1
- import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
- import { c as normalize, g as transformVector, m as sub, n as displayMatrix, o as identity, r as dot, s as multiply, t as cross } from "./vector-CCxLae_T.js";
1
+ import { n as ResourceLocator } from "./locator-COMEtEn9.js";
3
2
  import { l as toFloatTexture, n as cloneFloatTexture, r as darkenTexture, s as tintTexture, t as blendPixel } from "./blend-KXMclcHk.js";
4
- import { t as combineFaceBuffers } from "./compose-BGsNFR12.js";
3
+ import { c as normalize, g as transformVector, m as sub, n as displayMatrix, o as identity, r as dot, s as multiply, t as cross } from "./vector-CCxLae_T.js";
4
+ import { c as lightsForProfile } from "./lighting--5myudVu.js";
5
5
  import { a as drawTexturedTriangle, i as createFaceBuffer, n as clearFaceBuffer, o as phpRound, t as RowCursor } from "./rasterizer-DcVJzkxz.js";
6
- import { n as faceMaxZ, r as renderFace, t as createShadeCache } from "./face-CMJ3b3Ae.js";
7
- import { t as paintBucketFluid } from "./fluid-bucket-BzoLofB_.js";
6
+ import { t as combineFaceBuffers } from "./compose-BGsNFR12.js";
7
+ import { n as faceMaxZ, r as renderFace, t as createShadeCache } from "./face-CKHboPfh.js";
8
+ import { t as paintBucketFluid } from "./fluid-bucket-B-I4ZYHl.js";
8
9
  import { r as buildElementFaces } from "./geometry-Bi-Bf5rb.js";
9
- import { c as lightsForProfile } from "./lighting--5myudVu.js";
10
- import { i as resolveTexture } from "./loader-CWsTZgrk.js";
11
- //#region src/render/obj.ts
12
- /**
13
- * Forge/NeoForge OBJ model rendering — the class the old pipeline left
14
- * permanently broken ("the geometry is in a different file format"). Parses
15
- * OBJ + MTL out of the asset tree and rasterizes the triangles through the
16
- * same projection/lighting as JSON elements, with a z-tested shared buffer
17
- * (per-triangle painter buffers would be quadratic in triangle count).
18
- */
19
- /** RGBA float channels, 8 bytes each. */
20
- const FLOAT_BYTES_PER_TEXEL = 32;
21
- /** Clone budget for one OBJ render; past it samples are shaded individually. */
22
- const MAX_OBJ_SHADE_BYTES = 67108864;
23
- /** Parse OBJ text into triangles (polygon faces are fan-triangulated). */
24
- function parseObj(text, flipV) {
25
- const positions = [];
26
- const uvs = [];
27
- const triangles = [];
28
- const materialLibs = [];
29
- let material = "";
30
- let object = "";
31
- for (const rawLine of text.split("\n")) {
32
- const line = rawLine.trim();
33
- if (line === "" || line.startsWith("#")) continue;
34
- const parts = line.split(/\s+/);
35
- switch (parts[0]) {
36
- case "v":
37
- positions.push([
38
- Number(parts[1]),
39
- Number(parts[2]),
40
- Number(parts[3])
41
- ]);
42
- break;
43
- case "vt": {
44
- const u = Number(parts[1]);
45
- const v = Number(parts[2] ?? 0);
46
- uvs.push([u, flipV ? 1 - v : v]);
47
- break;
48
- }
49
- case "usemtl":
50
- material = parts.slice(1).join(" ");
51
- break;
52
- case "o":
53
- case "g":
54
- object = parts.slice(1).join(" ");
55
- break;
56
- case "mtllib":
57
- materialLibs.push(parts.slice(1).join(" "));
58
- break;
59
- case "f": {
60
- const corners = parts.slice(1).map((corner) => {
61
- const [vIndex, vtIndex] = corner.split("/", 2);
62
- const position = Number(vIndex);
63
- const uvIndex = vtIndex === void 0 || vtIndex === "" ? 0 : Number(vtIndex);
64
- const uv = uvIndex === 0 ? void 0 : uvs[uvIndex > 0 ? uvIndex - 1 : uvs.length + uvIndex];
65
- return {
66
- position: positions[position > 0 ? position - 1 : positions.length + position],
67
- uv: uv ?? [0, 0]
68
- };
69
- });
70
- for (let i = 2; i < corners.length; i++) {
71
- if (!corners[0]?.position || !corners[i - 1]?.position || !corners[i]?.position) continue;
72
- triangles.push({
73
- positions: [
74
- corners[0].position,
75
- corners[i - 1].position,
76
- corners[i].position
77
- ],
78
- uvs: [
79
- corners[0].uv,
80
- corners[i - 1].uv,
81
- corners[i].uv
82
- ],
83
- material,
84
- object
85
- });
86
- }
87
- break;
88
- }
89
- }
90
- }
91
- return {
92
- triangles,
93
- materialLibs
94
- };
95
- }
96
- /** map_Kd texture per material name out of an MTL library. */
97
- function parseMtl(text) {
98
- const materials = /* @__PURE__ */ new Map();
99
- let current = "";
100
- for (const rawLine of text.split("\n")) {
101
- const parts = rawLine.trim().split(/\s+/);
102
- if (parts[0] === "newmtl") current = parts.slice(1).join(" ");
103
- else if (parts[0] === "map_Kd" && current) materials.set(current, parts.slice(1).join(" "));
104
- }
105
- return materials;
106
- }
107
- /**
108
- * Resolve a material name to a texture locator: `#key` and MTL map_Kd values
109
- * go through the model's texture map (Forge binds materials that way); bare
110
- * names fall back to a direct locator.
111
- */
112
- function materialTexture(model, materials, material) {
113
- const mapped = materials.get(material) ?? material;
114
- if (mapped.startsWith("#")) return resolveTexture(model, mapped);
115
- const viaMap = resolveTexture(model, `#${mapped}`);
116
- if (viaMap) return viaMap;
117
- return ResourceLocator.tryParse(mapped);
118
- }
119
- async function renderObjModel(model, resources, textures, options) {
120
- if (!model.obj) throw new ItemRenderError(`renderObjModel called without obj geometry: ${model.locator}`);
121
- const objLocator = ResourceLocator.parse(model.obj.model);
122
- const objSource = await resources.getResource("raw", objLocator);
123
- if (!objSource) throw new ItemRenderError(`OBJ file not found: ${model.obj.model} for ${model.locator}`);
124
- const parsed = parseObj(objSource.toString("utf8"), model.obj.flipV);
125
- const wanted = model.obj.objects;
126
- if (wanted) {
127
- const allowed = new Set(wanted);
128
- parsed.triangles = parsed.triangles.filter((triangle) => allowed.has(triangle.object));
129
- }
130
- if (parsed.triangles.length === 0) throw new ItemRenderError(`OBJ has no faces: ${model.obj.model}`);
131
- const materials = /* @__PURE__ */ new Map();
132
- const objDirectory = objLocator.path.split("/").slice(0, -1).join("/");
133
- for (const lib of parsed.materialLibs) {
134
- const libPath = lib.includes("/") ? lib : `${objDirectory}/${lib}`;
135
- const libLocator = ResourceLocator.tryParse(`${objLocator.namespace}:${libPath}`);
136
- const source = libLocator ? await resources.getResource("raw", libLocator) : null;
137
- if (source) for (const [name, texture] of parseMtl(source.toString("utf8"))) materials.set(name, texture);
138
- }
139
- const lights = lightsForProfile(options.lightingProfile);
140
- const light = model.elementsGuiLight === "side" ? lights.side : lights.front;
141
- const gui = model.display.gui;
142
- const guiMatrix = displayMatrix(gui?.rotation ?? [
143
- 0,
144
- 0,
145
- 0
146
- ], gui?.translation ?? [
147
- 0,
148
- 0,
149
- 0
150
- ], gui?.scale ?? [
151
- 1,
152
- 1,
153
- 1
154
- ]);
155
- const transformation = multiply(guiMatrix, options.itemTransform ?? identity());
156
- const renderSize = options.size * options.quality;
157
- const out = createFaceBuffer(renderSize, renderSize);
158
- const scratch = createFaceBuffer(renderSize, renderSize);
159
- const shaded = /* @__PURE__ */ new Map();
160
- let shadedBytes = 0;
161
- for (const triangle of parsed.triangles) {
162
- const transformed = triangle.positions.map((position) => transformVector(transformation, position));
163
- const rawNormal = cross(sub(transformed[1], transformed[0]), sub(transformed[2], transformed[0]));
164
- if (rawNormal[0] === 0 && rawNormal[1] === 0 && rawNormal[2] === 0) continue;
165
- const normal = normalize(rawNormal);
166
- if (dot([
167
- 0,
168
- 0,
169
- -1
170
- ], normal) >= -.01) continue;
171
- const level = light.lightLevelForNormal(normal);
172
- const bucket = Math.round(level * 64);
173
- const darkness = 1 - bucket / 64;
174
- const locator = materialTexture(model, materials, triangle.material);
175
- if (!locator) throw new ItemRenderError(`Unresolvable OBJ material ${triangle.material} in ${model.locator}`);
176
- const key = `${locator} ${bucket}`;
177
- let texture = shaded.get(key);
178
- let shading = null;
179
- if (!texture) {
180
- const base = await textures.load(locator) ?? await textures.load(new ResourceLocator(locator.namespace, `item/${locator.path}`));
181
- if (!base) throw new ItemRenderError(`Missing texture ${locator} referenced by ${model.obj.model}`);
182
- const float = toFloatTexture(base);
183
- const bytes = float.width * float.height * FLOAT_BYTES_PER_TEXEL;
184
- if (shadedBytes + bytes <= MAX_OBJ_SHADE_BYTES) {
185
- texture = cloneFloatTexture(float);
186
- darkenTexture(texture, darkness, 0, 0, texture.width, texture.height);
187
- shaded.set(key, texture);
188
- shadedBytes += bytes;
189
- } else {
190
- texture = float;
191
- shading = {
192
- tint: null,
193
- darkness,
194
- alpha: 1,
195
- windowX1: 0,
196
- windowY1: 0,
197
- windowX2: float.width - 1,
198
- windowY2: float.height - 1
199
- };
200
- }
201
- }
202
- clearFaceBuffer(scratch);
203
- const cursor = new RowCursor();
204
- const point = (index) => {
205
- return {
206
- x: phpRound((transformed[index][0] - .5) * renderSize + renderSize / 2),
207
- y: phpRound(renderSize - ((transformed[index][1] - .5) * renderSize + renderSize / 2)),
208
- u: triangle.uvs[index][0],
209
- v: triangle.uvs[index][1],
210
- depth: (transformed[index][2] + 1) / 3
211
- };
212
- };
213
- drawTexturedTriangle(point(0), point(1), point(2), scratch, texture, cursor, shading);
214
- for (let y = Math.max(scratch.dirty.minY, 0); y <= Math.min(scratch.dirty.maxY, renderSize - 1); y++) for (let x = Math.max(scratch.dirty.minX, 0); x <= Math.min(scratch.dirty.maxX, renderSize - 1); x++) {
215
- const pixel = y * renderSize + x;
216
- const offset = pixel * 4;
217
- const alpha = scratch.colors[offset + 3];
218
- if (alpha <= 0) continue;
219
- if (scratch.depths[pixel] >= out.depths[pixel] || out.colors[offset + 3] === 0) {
220
- if (alpha >= 1 || out.colors[offset + 3] === 0) {
221
- out.colors[offset] = scratch.colors[offset];
222
- out.colors[offset + 1] = scratch.colors[offset + 1];
223
- out.colors[offset + 2] = scratch.colors[offset + 2];
224
- out.colors[offset + 3] = alpha;
225
- } else blendPixel(out.colors, offset, scratch.colors[offset], scratch.colors[offset + 1], scratch.colors[offset + 2], alpha);
226
- out.depths[pixel] = Math.max(out.depths[pixel], scratch.depths[pixel]);
227
- }
228
- }
229
- }
230
- return {
231
- width: renderSize,
232
- height: renderSize,
233
- data: out.colors
234
- };
235
- }
236
- //#endregion
10
+ import { i as resolveTexture } from "./loader-C-NuJaEi.js";
237
11
  //#region src/render/renderer.ts
238
12
  /**
239
13
  * Item rendering entry: flat generated items (2D layer compositing) and 3D
@@ -638,4 +412,230 @@ function clampQuantum(quantum) {
638
412
  return rounded < 0 ? 0 : Math.min(rounded, 65535);
639
413
  }
640
414
  //#endregion
641
- export { renderModelFrames as a, renderObjModel as c, renderModel as i, boxDownscale as n, parseMtl as o, pointResize as r, parseObj as s, ItemRenderError as t };
415
+ //#region src/render/obj.ts
416
+ /**
417
+ * Forge/NeoForge OBJ model rendering — the class the old pipeline left
418
+ * permanently broken ("the geometry is in a different file format"). Parses
419
+ * OBJ + MTL out of the asset tree and rasterizes the triangles through the
420
+ * same projection/lighting as JSON elements, with a z-tested shared buffer
421
+ * (per-triangle painter buffers would be quadratic in triangle count).
422
+ */
423
+ /** RGBA float channels, 8 bytes each. */
424
+ const FLOAT_BYTES_PER_TEXEL = 32;
425
+ /** Clone budget for one OBJ render; past it samples are shaded individually. */
426
+ const MAX_OBJ_SHADE_BYTES = 67108864;
427
+ /** Parse OBJ text into triangles (polygon faces are fan-triangulated). */
428
+ function parseObj(text, flipV) {
429
+ const positions = [];
430
+ const uvs = [];
431
+ const triangles = [];
432
+ const materialLibs = [];
433
+ let material = "";
434
+ let object = "";
435
+ for (const rawLine of text.split("\n")) {
436
+ const line = rawLine.trim();
437
+ if (line === "" || line.startsWith("#")) continue;
438
+ const parts = line.split(/\s+/);
439
+ switch (parts[0]) {
440
+ case "v":
441
+ positions.push([
442
+ Number(parts[1]),
443
+ Number(parts[2]),
444
+ Number(parts[3])
445
+ ]);
446
+ break;
447
+ case "vt": {
448
+ const u = Number(parts[1]);
449
+ const v = Number(parts[2] ?? 0);
450
+ uvs.push([u, flipV ? 1 - v : v]);
451
+ break;
452
+ }
453
+ case "usemtl":
454
+ material = parts.slice(1).join(" ");
455
+ break;
456
+ case "o":
457
+ case "g":
458
+ object = parts.slice(1).join(" ");
459
+ break;
460
+ case "mtllib":
461
+ materialLibs.push(parts.slice(1).join(" "));
462
+ break;
463
+ case "f": {
464
+ const corners = parts.slice(1).map((corner) => {
465
+ const [vIndex, vtIndex] = corner.split("/", 2);
466
+ const position = Number(vIndex);
467
+ const uvIndex = vtIndex === void 0 || vtIndex === "" ? 0 : Number(vtIndex);
468
+ const uv = uvIndex === 0 ? void 0 : uvs[uvIndex > 0 ? uvIndex - 1 : uvs.length + uvIndex];
469
+ return {
470
+ position: positions[position > 0 ? position - 1 : positions.length + position],
471
+ uv: uv ?? [0, 0]
472
+ };
473
+ });
474
+ for (let i = 2; i < corners.length; i++) {
475
+ if (!corners[0]?.position || !corners[i - 1]?.position || !corners[i]?.position) continue;
476
+ triangles.push({
477
+ positions: [
478
+ corners[0].position,
479
+ corners[i - 1].position,
480
+ corners[i].position
481
+ ],
482
+ uvs: [
483
+ corners[0].uv,
484
+ corners[i - 1].uv,
485
+ corners[i].uv
486
+ ],
487
+ material,
488
+ object
489
+ });
490
+ }
491
+ break;
492
+ }
493
+ }
494
+ }
495
+ return {
496
+ triangles,
497
+ materialLibs
498
+ };
499
+ }
500
+ /** map_Kd texture per material name out of an MTL library. */
501
+ function parseMtl(text) {
502
+ const materials = /* @__PURE__ */ new Map();
503
+ let current = "";
504
+ for (const rawLine of text.split("\n")) {
505
+ const parts = rawLine.trim().split(/\s+/);
506
+ if (parts[0] === "newmtl") current = parts.slice(1).join(" ");
507
+ else if (parts[0] === "map_Kd" && current) materials.set(current, parts.slice(1).join(" "));
508
+ }
509
+ return materials;
510
+ }
511
+ /**
512
+ * Resolve a material name to a texture locator: `#key` and MTL map_Kd values
513
+ * go through the model's texture map (Forge binds materials that way); bare
514
+ * names fall back to a direct locator.
515
+ */
516
+ function materialTexture(model, materials, material) {
517
+ const mapped = materials.get(material) ?? material;
518
+ if (mapped.startsWith("#")) return resolveTexture(model, mapped);
519
+ const viaMap = resolveTexture(model, `#${mapped}`);
520
+ if (viaMap) return viaMap;
521
+ return ResourceLocator.tryParse(mapped);
522
+ }
523
+ async function renderObjModel(model, resources, textures, options) {
524
+ if (!model.obj) throw new ItemRenderError(`renderObjModel called without obj geometry: ${model.locator}`);
525
+ const objLocator = ResourceLocator.parse(model.obj.model);
526
+ const objSource = await resources.getResource("raw", objLocator);
527
+ if (!objSource) throw new ItemRenderError(`OBJ file not found: ${model.obj.model} for ${model.locator}`);
528
+ const parsed = parseObj(objSource.toString("utf8"), model.obj.flipV);
529
+ const wanted = model.obj.objects;
530
+ if (wanted) {
531
+ const allowed = new Set(wanted);
532
+ parsed.triangles = parsed.triangles.filter((triangle) => allowed.has(triangle.object));
533
+ }
534
+ if (parsed.triangles.length === 0) throw new ItemRenderError(`OBJ has no faces: ${model.obj.model}`);
535
+ const materials = /* @__PURE__ */ new Map();
536
+ const objDirectory = objLocator.path.split("/").slice(0, -1).join("/");
537
+ for (const lib of parsed.materialLibs) {
538
+ const libPath = lib.includes("/") ? lib : `${objDirectory}/${lib}`;
539
+ const libLocator = ResourceLocator.tryParse(`${objLocator.namespace}:${libPath}`);
540
+ const source = libLocator ? await resources.getResource("raw", libLocator) : null;
541
+ if (source) for (const [name, texture] of parseMtl(source.toString("utf8"))) materials.set(name, texture);
542
+ }
543
+ const lights = lightsForProfile(options.lightingProfile);
544
+ const light = model.elementsGuiLight === "side" ? lights.side : lights.front;
545
+ const gui = model.display.gui;
546
+ const guiMatrix = displayMatrix(gui?.rotation ?? [
547
+ 0,
548
+ 0,
549
+ 0
550
+ ], gui?.translation ?? [
551
+ 0,
552
+ 0,
553
+ 0
554
+ ], gui?.scale ?? [
555
+ 1,
556
+ 1,
557
+ 1
558
+ ]);
559
+ const transformation = multiply(guiMatrix, options.itemTransform ?? identity());
560
+ const renderSize = options.size * options.quality;
561
+ const out = createFaceBuffer(renderSize, renderSize);
562
+ const scratch = createFaceBuffer(renderSize, renderSize);
563
+ const shaded = /* @__PURE__ */ new Map();
564
+ let shadedBytes = 0;
565
+ for (const triangle of parsed.triangles) {
566
+ const transformed = triangle.positions.map((position) => transformVector(transformation, position));
567
+ const rawNormal = cross(sub(transformed[1], transformed[0]), sub(transformed[2], transformed[0]));
568
+ if (rawNormal[0] === 0 && rawNormal[1] === 0 && rawNormal[2] === 0) continue;
569
+ const normal = normalize(rawNormal);
570
+ if (dot([
571
+ 0,
572
+ 0,
573
+ -1
574
+ ], normal) >= -.01) continue;
575
+ const level = light.lightLevelForNormal(normal);
576
+ const bucket = Math.round(level * 64);
577
+ const darkness = 1 - bucket / 64;
578
+ const locator = materialTexture(model, materials, triangle.material);
579
+ if (!locator) throw new ItemRenderError(`Unresolvable OBJ material ${triangle.material} in ${model.locator}`);
580
+ const key = `${locator} ${bucket}`;
581
+ let texture = shaded.get(key);
582
+ let shading = null;
583
+ if (!texture) {
584
+ const base = await textures.load(locator) ?? await textures.load(new ResourceLocator(locator.namespace, `item/${locator.path}`));
585
+ if (!base) throw new ItemRenderError(`Missing texture ${locator} referenced by ${model.obj.model}`);
586
+ const float = toFloatTexture(base);
587
+ const bytes = float.width * float.height * FLOAT_BYTES_PER_TEXEL;
588
+ if (shadedBytes + bytes <= MAX_OBJ_SHADE_BYTES) {
589
+ texture = cloneFloatTexture(float);
590
+ darkenTexture(texture, darkness, 0, 0, texture.width, texture.height);
591
+ shaded.set(key, texture);
592
+ shadedBytes += bytes;
593
+ } else {
594
+ texture = float;
595
+ shading = {
596
+ tint: null,
597
+ darkness,
598
+ alpha: 1,
599
+ windowX1: 0,
600
+ windowY1: 0,
601
+ windowX2: float.width - 1,
602
+ windowY2: float.height - 1
603
+ };
604
+ }
605
+ }
606
+ clearFaceBuffer(scratch);
607
+ const cursor = new RowCursor();
608
+ const point = (index) => {
609
+ return {
610
+ x: phpRound((transformed[index][0] - .5) * renderSize + renderSize / 2),
611
+ y: phpRound(renderSize - ((transformed[index][1] - .5) * renderSize + renderSize / 2)),
612
+ u: triangle.uvs[index][0],
613
+ v: triangle.uvs[index][1],
614
+ depth: (transformed[index][2] + 1) / 3
615
+ };
616
+ };
617
+ drawTexturedTriangle(point(0), point(1), point(2), scratch, texture, cursor, shading);
618
+ for (let y = Math.max(scratch.dirty.minY, 0); y <= Math.min(scratch.dirty.maxY, renderSize - 1); y++) for (let x = Math.max(scratch.dirty.minX, 0); x <= Math.min(scratch.dirty.maxX, renderSize - 1); x++) {
619
+ const pixel = y * renderSize + x;
620
+ const offset = pixel * 4;
621
+ const alpha = scratch.colors[offset + 3];
622
+ if (alpha <= 0) continue;
623
+ if (scratch.depths[pixel] >= out.depths[pixel] || out.colors[offset + 3] === 0) {
624
+ if (alpha >= 1 || out.colors[offset + 3] === 0) {
625
+ out.colors[offset] = scratch.colors[offset];
626
+ out.colors[offset + 1] = scratch.colors[offset + 1];
627
+ out.colors[offset + 2] = scratch.colors[offset + 2];
628
+ out.colors[offset + 3] = alpha;
629
+ } else blendPixel(out.colors, offset, scratch.colors[offset], scratch.colors[offset + 1], scratch.colors[offset + 2], alpha);
630
+ out.depths[pixel] = Math.max(out.depths[pixel], scratch.depths[pixel]);
631
+ }
632
+ }
633
+ }
634
+ return {
635
+ width: renderSize,
636
+ height: renderSize,
637
+ data: out.colors
638
+ };
639
+ }
640
+ //#endregion
641
+ export { ItemRenderError as a, renderModel as c, renderObjModel as i, renderModelFrames as l, parseMtl as n, boxDownscale as o, parseObj as r, pointResize as s, materialTexture as t };
@@ -0,0 +1,41 @@
1
+ import { a as ResourceManager } from "./manager-Cv1NeFyF.js";
2
+ import { r as ResolvedModel } from "./loader-C_1_amsq.js";
3
+ //#region src/blocks/obj-mesh.d.ts
4
+ interface MeshGroup {
5
+ /** Sprite every triangle in this group samples. */
6
+ sprite: string;
7
+ /**
8
+ * The OBJ `o`/`g` name these triangles came from, omitted when the file
9
+ * names none. A loader that packs several states of a block into one OBJ
10
+ * tells them apart by this name, so carrying it lets a consumer filter to
11
+ * the state it wants instead of drawing every state at once.
12
+ */
13
+ object?: string;
14
+ /**
15
+ * xyz per vertex, three vertices per triangle, in the same 0–16 block space
16
+ * as `elements` (OBJ authors in 0–1, and multiblock meshes exceed it).
17
+ */
18
+ positions: number[];
19
+ /** uv per vertex, in 0–16 sprite space, matching a face's `uv`. */
20
+ uv: number[];
21
+ }
22
+ interface ObjMesh {
23
+ groups: MeshGroup[];
24
+ /** Materials whose texture did not resolve, for the failures sidecar. */
25
+ unresolved: string[];
26
+ triangles: number;
27
+ /** Every object name in the file, before any loader restriction. */
28
+ available: string[];
29
+ /** Whether the loader narrowed the file down to a subset of its objects. */
30
+ restricted: boolean;
31
+ /** The loader picks a subset at runtime and the flattener could not name it. */
32
+ conditional: boolean;
33
+ }
34
+ declare class ObjMeshError extends Error {}
35
+ /**
36
+ * Build the mesh a model's OBJ reference describes, or null when the model has
37
+ * no OBJ. `missingSprite` names the sprite unresolved materials fall back to.
38
+ */
39
+ declare function buildObjMesh(model: ResolvedModel, resources: ResourceManager, missingSprite: string): Promise<ObjMesh | null>;
40
+ //#endregion
41
+ export { buildObjMesh as i, ObjMesh as n, ObjMeshError as r, MeshGroup as t };
@@ -0,0 +1,97 @@
1
+ import { n as ResourceLocator } from "./locator-COMEtEn9.js";
2
+ import { n as parseMtl, r as parseObj, t as materialTexture } from "./obj-bvs3gwex.js";
3
+ //#region src/blocks/obj-mesh.ts
4
+ /**
5
+ * OBJ geometry as pack data.
6
+ *
7
+ * Forge/NeoForge let a block model point at an `.obj` instead of stating
8
+ * `elements`, and mods use it for everything a box cannot express: Create's
9
+ * crushing wheels and blaze burners, Immersive Engineering's slopes,
10
+ * connectors and split multiblocks, Mekanism's pipes. Across the mod corpus
11
+ * that is 237 block models — the single largest class of geometry the box-only
12
+ * format could not carry.
13
+ *
14
+ * Triangles are a superset of boxes, and a GPU consumer draws them more easily
15
+ * than it draws a cuboid, so the pack gains a `mesh` alongside `elements`
16
+ * rather than trying to approximate the mesh with boxes.
17
+ */
18
+ var ObjMeshError = class extends Error {};
19
+ /**
20
+ * A mesh past this is not geometry a chunk renderer wants inlined per block —
21
+ * it is a mod shipping a scenery model. Recorded rather than emitted.
22
+ */
23
+ const MAX_TRIANGLES = 2e4;
24
+ /** OBJ authors blocks in 0–1; the pack speaks 0–16 throughout. */
25
+ const BLOCK_UNITS = 16;
26
+ /**
27
+ * Joins the two halves of a mesh group's key. An OBJ object name may hold
28
+ * spaces, so the separator is the one character it cannot: object names are
29
+ * line-delimited in the file.
30
+ */
31
+ const KEY_SEPARATOR = "\n";
32
+ function round(value, places) {
33
+ const factor = 10 ** places;
34
+ return Math.round(value * factor) / factor;
35
+ }
36
+ /**
37
+ * Build the mesh a model's OBJ reference describes, or null when the model has
38
+ * no OBJ. `missingSprite` names the sprite unresolved materials fall back to.
39
+ */
40
+ async function buildObjMesh(model, resources, missingSprite) {
41
+ if (!model.obj) return null;
42
+ const objLocator = ResourceLocator.tryParse(model.obj.model);
43
+ if (objLocator === null) throw new ObjMeshError(`unparseable OBJ reference ${model.obj.model}`);
44
+ const source = await resources.getResource("raw", objLocator);
45
+ if (source === null) throw new ObjMeshError(`OBJ file not found: ${model.obj.model}`);
46
+ const parsed = parseObj(source.toString("utf8"), model.obj.flipV);
47
+ const available = [...new Set(parsed.triangles.map((triangle) => triangle.object))].filter(Boolean);
48
+ const restricted = Boolean(model.obj.objects);
49
+ if (model.obj.objects) {
50
+ const allowed = new Set(model.obj.objects);
51
+ parsed.triangles = parsed.triangles.filter((triangle) => allowed.has(triangle.object));
52
+ }
53
+ if (parsed.triangles.length === 0) throw new ObjMeshError(`OBJ has no faces: ${model.obj.model}`);
54
+ if (parsed.triangles.length > MAX_TRIANGLES) throw new ObjMeshError(`OBJ has ${parsed.triangles.length} triangles, over the ${MAX_TRIANGLES} cap`);
55
+ const materials = /* @__PURE__ */ new Map();
56
+ const directory = objLocator.path.split("/").slice(0, -1).join("/");
57
+ for (const library of parsed.materialLibs) {
58
+ const libraryPath = library.includes("/") ? library : `${directory}/${library}`;
59
+ const libraryLocator = ResourceLocator.tryParse(`${objLocator.namespace}:${libraryPath}`);
60
+ const raw = libraryLocator ? await resources.getResource("raw", libraryLocator) : null;
61
+ if (raw === null) continue;
62
+ for (const [name, texture] of parseMtl(raw.toString("utf8"))) materials.set(name, texture);
63
+ }
64
+ const groups = /* @__PURE__ */ new Map();
65
+ const unresolved = /* @__PURE__ */ new Set();
66
+ for (const triangle of parsed.triangles) {
67
+ const locator = materialTexture(model, materials, triangle.material);
68
+ if (locator === null) unresolved.add(triangle.material);
69
+ const sprite = locator === null ? missingSprite : locator.toString();
70
+ const key = `${triangle.object}${KEY_SEPARATOR}${sprite}`;
71
+ let group = groups.get(key);
72
+ if (!group) {
73
+ group = {
74
+ sprite,
75
+ ...triangle.object && { object: triangle.object },
76
+ positions: [],
77
+ uv: []
78
+ };
79
+ groups.set(key, group);
80
+ }
81
+ for (let corner = 0; corner < 3; corner++) {
82
+ const position = triangle.positions[corner];
83
+ group.positions.push(round(position[0] * BLOCK_UNITS, 3), round(position[1] * BLOCK_UNITS, 3), round(position[2] * BLOCK_UNITS, 3));
84
+ group.uv.push(round(triangle.uvs[corner][0] * BLOCK_UNITS, 4), round(triangle.uvs[corner][1] * BLOCK_UNITS, 4));
85
+ }
86
+ }
87
+ return {
88
+ groups: groups.values().toArray().toSorted((left, right) => (left.object ?? "").localeCompare(right.object ?? "") || left.sprite.localeCompare(right.sprite)),
89
+ unresolved: [...unresolved],
90
+ triangles: parsed.triangles.length,
91
+ available,
92
+ restricted,
93
+ conditional: model.obj.conditional === true
94
+ };
95
+ }
96
+ //#endregion
97
+ export { buildObjMesh as n, ObjMeshError as t };
@@ -1,2 +1,2 @@
1
- import { t as classifyItemStyle } from "../item-styles-upgEO1yP.js";
1
+ import { t as classifyItemStyle } from "../item-styles-DjYRdyHi.js";
2
2
  export { classifyItemStyle };
@@ -1,7 +1,7 @@
1
1
  import { a as ResourceManager } from "./manager-Cv1NeFyF.js";
2
2
  import { n as FloatTexture } from "./blend-8lnfxtLe.js";
3
3
  import { a as LightingProfileName } from "./lighting-3FGRC3Af.js";
4
- import { n as AnimationLimits } from "./renderer-C79EQDSn.js";
4
+ import { n as AnimationLimits } from "./renderer-U20hnhbi.js";
5
5
  //#region src/render/pipeline.d.ts
6
6
  interface PipelineSettings {
7
7
  builtin?: string;
@@ -1,19 +1,19 @@
1
- import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
- import { n as LayeredResourceManager, t as FolderResourceManager } from "./manager-CkletR5K.js";
3
- import { a as defaultProperties } from "./definition-ColsvWrH.js";
4
- import { r as resolveItem } from "./discovery-B9aqV_Rz.js";
1
+ import { n as ResourceLocator } from "./locator-COMEtEn9.js";
5
2
  import { l as toFloatTexture, t as blendPixel } from "./blend-KXMclcHk.js";
3
+ import { t as ModelLoader } from "./loader-C-NuJaEi.js";
4
+ import { c as renderModel, l as renderModelFrames, o as boxDownscale, s as pointResize } from "./obj-bvs3gwex.js";
5
+ import { t as loadAtlasResolver } from "./registry-DlduyjtM.js";
6
+ import { n as LayeredResourceManager, t as FolderResourceManager } from "./manager-CYbAkWuT.js";
7
+ import { n as TextureLoader } from "./loader-C18GpurU.js";
8
+ import { a as defaultProperties } from "./definition-ColsvWrH.js";
9
+ import { r as resolveItem } from "./discovery-CsZu08re.js";
6
10
  import { t as encodeAnimatedIcon } from "./encode-LR9wxigC.js";
7
- import { a as builtinDynamicResources } from "./generators-BlG4KmtP.js";
8
- import { t as ModelLoader } from "./loader-CWsTZgrk.js";
9
- import { a as renderModelFrames, i as renderModel, n as boxDownscale, r as pointResize } from "./renderer-DB1Oe7xs.js";
10
- import { a as spawnEggTints, i as renderEntityFallback, n as dyedVariantTint, r as renderDyedVariantFallback, t as dyeVariantName } from "./entity-fallback-Cu22uEIK.js";
11
- import { i as renderGeo, t as findGeoForItem } from "./geo-w9HHIrb5.js";
12
- import { t as legacyTintsForChain } from "./legacy-tints-B09J0Rxh.js";
13
- import { a as resolveTints } from "./tints-3VI_2SNy.js";
14
- import { t as loadAtlasResolver } from "./registry-DS5PZ57l.js";
15
- import { n as defaultBlockStateModel, t as blockStateTransform } from "./blockstate-CdstnW2-.js";
16
- import { n as TextureLoader } from "./loader-Lb0ZeILz.js";
11
+ import { a as builtinDynamicResources } from "./generators-BCZVlWig.js";
12
+ import { a as spawnEggTints, i as renderEntityFallback, n as dyedVariantTint, r as renderDyedVariantFallback, t as dyeVariantName } from "./entity-fallback-BTkM8j0V.js";
13
+ import { i as renderGeo, t as findGeoForItem } from "./geo-BguldlU-.js";
14
+ import { t as legacyTintsForChain } from "./legacy-tints-l-541wgI.js";
15
+ import { a as resolveTints } from "./tints-B3WQcPtx.js";
16
+ import { n as defaultBlockStateModel, t as blockStateTransform } from "./blockstate-DVb8Mhxc.js";
17
17
  import path from "node:path";
18
18
  import { mkdir, writeFile } from "node:fs/promises";
19
19
  //#region src/render/pipeline.ts
@@ -1,8 +1,8 @@
1
- import { n as LayeredResourceManager } from "./manager-CkletR5K.js";
2
- import { t as curatedModelOverrides } from "./models-BKxGkPS7.js";
3
- import { n as harvestTintOverrides, t as curatedTintOverrides } from "./tints-CebmvjmP.js";
4
- import { n as UnsafeJarError, t as JarResourceManager } from "./jar-D9z-X7tA.js";
5
- import { t as synthesizeItemModels } from "./synthesize-3Q0aaOnK.js";
1
+ import { n as LayeredResourceManager } from "./manager-CYbAkWuT.js";
2
+ import { t as curatedModelOverrides } from "./models-CQwcTZcg.js";
3
+ import { t as synthesizeItemModels } from "./synthesize-jTjVjPAt.js";
4
+ import { n as harvestTintOverrides, t as curatedTintOverrides } from "./tints-nxtGrqv5.js";
5
+ import { n as UnsafeJarError, t as JarResourceManager } from "./jar-5aLDI0nT.js";
6
6
  //#region src/mods/prepare.ts
7
7
  /**
8
8
  * Mod asset composition: mod jars layered under the vanilla base (vanilla wins