@nodecraft/rendermite 1.0.4 → 1.0.6

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 (121) hide show
  1. package/README.md +5 -0
  2. package/assets/README.md +3 -0
  3. package/assets/mod-tints/README.md +38 -0
  4. package/assets/mod-tints/advanced_ae/items/quantum_boots.json +16 -0
  5. package/assets/mod-tints/advanced_ae/items/quantum_chestplate.json +16 -0
  6. package/assets/mod-tints/advanced_ae/items/quantum_helmet.json +16 -0
  7. package/assets/mod-tints/advanced_ae/items/quantum_leggings.json +16 -0
  8. package/dist/atlas/registry.d.ts +3 -3
  9. package/dist/atlas/sources.d.ts +1 -1
  10. package/dist/{blend-Qvrn6YgK.d.ts → blend-CyDIxCne.d.ts} +1 -1
  11. package/dist/{builtin-C8GZEN40.js → builtin-caYjESh0.js} +5 -1
  12. package/dist/builtin.d.ts +3 -1
  13. package/dist/builtin.js +2 -2
  14. package/dist/cache-Cr2ON3RQ.js +66 -0
  15. package/dist/cli.js +40 -7
  16. package/dist/compose-CQCb2LVv.js +104 -0
  17. package/dist/descriptor-DWebtkI1.js +26 -0
  18. package/dist/{discovery-D89pVVJA.d.ts → discovery-wiJBGryo.d.ts} +1 -1
  19. package/dist/dynamic/generators.d.ts +1 -1
  20. package/dist/dynamic/generators.js +1 -1
  21. package/dist/{encode-3u49i-eD.js → encode-D6dQu2dJ.js} +16 -4
  22. package/dist/{encode-C5kdAm45.d.ts → encode-DaNQF28M.d.ts} +1 -1
  23. package/dist/{entity-fallback-CdCVEG6X.js → entity-fallback-BRCeGKBT.js} +1 -1
  24. package/dist/{face-CpVdnf-w.d.ts → face-BIYg14TE.d.ts} +10 -4
  25. package/dist/{face-D-N1amV4.js → face-DmCiHUd5.js} +23 -10
  26. package/dist/{generators-CcVRG30y.js → generators-BlG4KmtP.js} +1 -1
  27. package/dist/{generators-CCB8h0mP.d.ts → generators-DhRov4RB.d.ts} +1 -1
  28. package/dist/{geo-u8rndjLA.js → geo-Diky41Tw.js} +7 -4
  29. package/dist/index.d.ts +14 -14
  30. package/dist/index.js +13 -13
  31. package/dist/{item-styles-DJ3LWZS2.js → item-styles-upgEO1yP.js} +1 -1
  32. package/dist/items/discovery.d.ts +1 -1
  33. package/dist/items/discovery.js +1 -1
  34. package/dist/{jar-6T3xdgaL.d.ts → jar-CFkA-VgT.d.ts} +2 -1
  35. package/dist/{jar-Bt3i0ls4.js → jar-D9z-X7tA.js} +12 -12
  36. package/dist/{legacy-reshape-CewShfF3.js → legacy-reshape-C05SfAPR.js} +1 -1
  37. package/dist/{loader-A-BZa8J4.js → loader-B8NJO7pR.js} +12 -3
  38. package/dist/{loader-CBzn3Koc.js → loader-CfXMxcEV.js} +37 -7
  39. package/dist/{loader-C-6QN3xv.d.ts → loader-D22zZME7.d.ts} +2 -2
  40. package/dist/{loader-TFFNV4Jl.d.ts → loader-Myy22TDL.d.ts} +8 -2
  41. package/dist/{manager-dfI7b9N4.js → manager-CkletR5K.js} +29 -2
  42. package/dist/{manager-CoZaeQTu.d.ts → manager-Cv1NeFyF.d.ts} +28 -1
  43. package/dist/models/loader.d.ts +1 -1
  44. package/dist/models/loader.js +1 -1
  45. package/dist/mods/prepare.d.ts +1 -1
  46. package/dist/mods/prepare.js +1 -1
  47. package/dist/mods/synthesize.d.ts +2 -2
  48. package/dist/mods/synthesize.js +1 -1
  49. package/dist/mods/tints.d.ts +28 -0
  50. package/dist/mods/tints.js +2 -0
  51. package/dist/output/encode.d.ts +1 -1
  52. package/dist/output/encode.js +1 -1
  53. package/dist/output/item-styles.js +1 -1
  54. package/dist/pipeline-BIk1RRIp.js +221 -0
  55. package/dist/pipeline-zq-yU3Te.d.ts +58 -0
  56. package/dist/pool-CKGp1qup.js +131 -0
  57. package/dist/{prepare-Bvc5ve2m.js → prepare-BESRrh6K.js} +33 -5
  58. package/dist/{prepare-DnnGGv9k.d.ts → prepare-CPxO0kKz.d.ts} +8 -1
  59. package/dist/{prepare-DROxBZp1.js → prepare-Cc8QZ3j0.js} +4 -4
  60. package/dist/{prepare-wfWtaz_X.d.ts → prepare-Cs6QDqie.d.ts} +1 -1
  61. package/dist/{rasterizer-BHWDrmGz.js → rasterizer-76qO8TDh.js} +42 -4
  62. package/dist/{rasterizer-Dh35qXbn.d.ts → rasterizer-yqJkicm1.d.ts} +21 -2
  63. package/dist/render/blend.d.ts +1 -1
  64. package/dist/render/compose.d.ts +2 -2
  65. package/dist/render/compose.js +1 -1
  66. package/dist/render/entity-fallback.d.ts +6 -6
  67. package/dist/render/entity-fallback.js +1 -1
  68. package/dist/render/face.d.ts +2 -2
  69. package/dist/render/face.js +1 -1
  70. package/dist/render/fluid-bucket.d.ts +2 -2
  71. package/dist/render/geo.d.ts +4 -4
  72. package/dist/render/geo.js +1 -1
  73. package/dist/render/legacy-tints.d.ts +2 -2
  74. package/dist/render/obj.d.ts +5 -5
  75. package/dist/render/obj.js +1 -1
  76. package/dist/render/pipeline.d.ts +2 -0
  77. package/dist/render/pipeline.js +2 -0
  78. package/dist/render/rasterizer.d.ts +2 -2
  79. package/dist/render/rasterizer.js +2 -2
  80. package/dist/render/renderer.d.ts +2 -2
  81. package/dist/render/renderer.js +1 -1
  82. package/dist/render/tints.d.ts +1 -1
  83. package/dist/{renderer-BeOinVC8.js → renderer-CjT19tHd.js} +131 -54
  84. package/dist/{renderer-nbMCw1Pl.d.ts → renderer-DVsZHAvf.d.ts} +23 -6
  85. package/dist/resources/cache.d.ts +30 -0
  86. package/dist/resources/cache.js +2 -0
  87. package/dist/resources/descriptor.d.ts +2 -0
  88. package/dist/resources/descriptor.js +2 -0
  89. package/dist/resources/jar.d.ts +1 -1
  90. package/dist/resources/jar.js +1 -1
  91. package/dist/resources/manager.d.ts +1 -1
  92. package/dist/resources/manager.js +1 -1
  93. package/dist/resources/synthetic.d.ts +1 -1
  94. package/dist/resources/synthetic.js +1 -1
  95. package/dist/resources/zip-reader.d.ts +5 -0
  96. package/dist/resources/zip-reader.js +2 -0
  97. package/dist/run-BnICOF4I.js +209 -0
  98. package/dist/{run-BlSZw9Qk.d.ts → run-CKUtynTy.d.ts} +12 -2
  99. package/dist/run.d.ts +1 -1
  100. package/dist/run.js +1 -1
  101. package/dist/{sources-DH9dk3KF.d.ts → sources-B9X9w3qP.d.ts} +1 -1
  102. package/dist/{synthesize-CRgSEmEp.js → synthesize-3Q0aaOnK.js} +1 -1
  103. package/dist/{synthetic-CDsj--li.d.ts → synthetic-Qk-BSUXO.d.ts} +5 -1
  104. package/dist/{synthetic-DC2bSnGe.js → synthetic-ZAUjXT7E.js} +0 -0
  105. package/dist/textures/loader.d.ts +1 -1
  106. package/dist/textures/loader.js +1 -1
  107. package/dist/tints-B8UJmZMu.js +83 -0
  108. package/dist/{tints-txt-8xI5.d.ts → tints-DLBExVWN.d.ts} +2 -2
  109. package/dist/versions/legacy-reshape.d.ts +1 -1
  110. package/dist/versions/legacy-reshape.js +1 -1
  111. package/dist/versions/prepare.d.ts +1 -1
  112. package/dist/versions/prepare.js +1 -1
  113. package/dist/workers/pool.d.ts +36 -5
  114. package/dist/workers/pool.js +2 -67
  115. package/dist/workers/render-worker.d.ts +15 -0
  116. package/dist/workers/render-worker.js +40 -0
  117. package/dist/zip-reader-BSbJKUT1.js +156 -0
  118. package/package.json +4 -2
  119. package/dist/compose-qjhyBvnS.js +0 -41
  120. package/dist/run-DI-cB38y.js +0 -316
  121. package/dist/{discovery-iph0P2F2.js → discovery-0SM0cTLw.js} +1 -1
@@ -1,2 +1,2 @@
1
- import { i as renderFace, n as TintColor, r as faceMaxZ, t as RenderableFace } from "../face-CpVdnf-w.js";
2
- export { RenderableFace, TintColor, faceMaxZ, renderFace };
1
+ import { a as renderFace, i as faceMaxZ, n as RenderableFace, r as TintColor, t as FaceShadeCache } from "../face-BIYg14TE.js";
2
+ export { FaceShadeCache, RenderableFace, TintColor, faceMaxZ, renderFace };
@@ -1,2 +1,2 @@
1
- import { n as renderFace, t as faceMaxZ } from "../face-D-N1amV4.js";
1
+ import { n as renderFace, t as faceMaxZ } from "../face-DmCiHUd5.js";
2
2
  export { faceMaxZ, renderFace };
@@ -1,5 +1,5 @@
1
- import { a as ResourceManager } from "../manager-CoZaeQTu.js";
2
- import { i as TextureLoader, r as Texture } from "../loader-TFFNV4Jl.js";
1
+ import { a as ResourceManager } from "../manager-Cv1NeFyF.js";
2
+ import { i as TextureLoader, r as Texture } from "../loader-Myy22TDL.js";
3
3
  //#region src/render/fluid-bucket.d.ts
4
4
  /**
5
5
  * Paint the fluid into a bucket sprite IN PLACE (pass a copy, never a cached
@@ -1,9 +1,9 @@
1
1
  import { n as ResourceLocator } from "../locator-jDbdUeTd.js";
2
- import { a as ResourceManager } from "../manager-CoZaeQTu.js";
3
- import { i as TextureLoader } from "../loader-TFFNV4Jl.js";
4
- import { t as FloatTexture } from "../blend-Qvrn6YgK.js";
2
+ import { a as ResourceManager } from "../manager-Cv1NeFyF.js";
3
+ import { i as TextureLoader } from "../loader-Myy22TDL.js";
4
+ import { t as FloatTexture } from "../blend-CyDIxCne.js";
5
5
  import { n as FaceGeometry } from "../geometry-BSVmKx5b.js";
6
- import { n as RenderOptions } from "../renderer-nbMCw1Pl.js";
6
+ import { i as RenderOptions } from "../renderer-DVsZHAvf.js";
7
7
  //#region src/render/geo.d.ts
8
8
  interface GeoCube {
9
9
  origin?: number[];
@@ -1,2 +1,2 @@
1
- import { i as renderGeo, n as geoToFaces, r as parseGeo, t as findGeoForItem } from "../geo-u8rndjLA.js";
1
+ import { i as renderGeo, n as geoToFaces, r as parseGeo, t as findGeoForItem } from "../geo-Diky41Tw.js";
2
2
  export { findGeoForItem, geoToFaces, parseGeo, renderGeo };
@@ -1,5 +1,5 @@
1
- import { i as TextureLoader } from "../loader-TFFNV4Jl.js";
2
- import { n as TintColor } from "../face-CpVdnf-w.js";
1
+ import { i as TextureLoader } from "../loader-Myy22TDL.js";
2
+ import { r as TintColor } from "../face-BIYg14TE.js";
3
3
  //#region src/render/legacy-tints.d.ts
4
4
  /**
5
5
  * Tints for a model rendered without an item definition (pre-1.21.4 eras and
@@ -1,9 +1,9 @@
1
- import { a as ResourceManager } from "../manager-CoZaeQTu.js";
2
- import { i as TextureLoader } from "../loader-TFFNV4Jl.js";
3
- import { r as ResolvedModel } from "../loader-C-6QN3xv.js";
4
- import { t as FloatTexture } from "../blend-Qvrn6YgK.js";
1
+ import { a as ResourceManager } from "../manager-Cv1NeFyF.js";
2
+ import { i as TextureLoader } from "../loader-Myy22TDL.js";
3
+ import { r as ResolvedModel } from "../loader-D22zZME7.js";
4
+ import { t as FloatTexture } from "../blend-CyDIxCne.js";
5
5
  import { n as Vec3 } from "../vector-DRKRi4--.js";
6
- import { n as RenderOptions } from "../renderer-nbMCw1Pl.js";
6
+ import { i as RenderOptions } from "../renderer-DVsZHAvf.js";
7
7
  //#region src/render/obj.d.ts
8
8
  interface ObjTriangle {
9
9
  positions: [Vec3, Vec3, Vec3];
@@ -1,2 +1,2 @@
1
- import { c as renderObjModel, o as parseMtl, s as parseObj } from "../renderer-BeOinVC8.js";
1
+ import { c as renderObjModel, o as parseMtl, s as parseObj } from "../renderer-CjT19tHd.js";
2
2
  export { parseMtl, parseObj, renderObjModel };
@@ -0,0 +1,2 @@
1
+ import { i as RenderedFrames, n as ItemRenderOutcome, r as PipelineSettings, t as ItemPipeline } from "../pipeline-zq-yU3Te.js";
2
+ export { ItemPipeline, ItemRenderOutcome, PipelineSettings, RenderedFrames };
@@ -0,0 +1,2 @@
1
+ import { t as ItemPipeline } from "../pipeline-BIk1RRIp.js";
2
+ export { ItemPipeline };
@@ -1,2 +1,2 @@
1
- import { a as createFaceBuffer, i as compactFaceBuffer, n as RasterPoint, o as drawTexturedTriangle, r as RowCursor, s as phpRound, t as FaceBuffer } from "../rasterizer-Dh35qXbn.js";
2
- export { FaceBuffer, RasterPoint, RowCursor, compactFaceBuffer, createFaceBuffer, drawTexturedTriangle, phpRound };
1
+ import { a as clearFaceBuffer, c as drawTexturedTriangle, i as RowCursor, l as phpRound, n as FaceBuffer, o as compactFaceBuffer, r as RasterPoint, s as createFaceBuffer, t as DirtyRect } from "../rasterizer-yqJkicm1.js";
2
+ export { DirtyRect, FaceBuffer, RasterPoint, RowCursor, clearFaceBuffer, compactFaceBuffer, createFaceBuffer, drawTexturedTriangle, phpRound };
@@ -1,2 +1,2 @@
1
- import { a as phpRound, i as drawTexturedTriangle, n as compactFaceBuffer, r as createFaceBuffer, t as RowCursor } from "../rasterizer-BHWDrmGz.js";
2
- export { RowCursor, compactFaceBuffer, createFaceBuffer, drawTexturedTriangle, phpRound };
1
+ import { a as drawTexturedTriangle, i as createFaceBuffer, n as clearFaceBuffer, o as phpRound, r as compactFaceBuffer, t as RowCursor } from "../rasterizer-76qO8TDh.js";
2
+ export { RowCursor, clearFaceBuffer, compactFaceBuffer, createFaceBuffer, drawTexturedTriangle, phpRound };
@@ -1,2 +1,2 @@
1
- import { a as pointResize, i as boxDownscale, n as RenderOptions, o as renderModel, r as RenderedFrames, s as renderModelFrames, t as ItemRenderError } from "../renderer-nbMCw1Pl.js";
2
- export { ItemRenderError, RenderOptions, RenderedFrames, boxDownscale, pointResize, renderModel, renderModelFrames };
1
+ import { a as RenderedFrames, c as renderModel, i as RenderOptions, l as renderModelFrames, n as AnimationLimits, o as boxDownscale, r as ItemRenderError, s as pointResize, t as AnimationDiagnostics } from "../renderer-DVsZHAvf.js";
2
+ export { AnimationDiagnostics, AnimationLimits, ItemRenderError, RenderOptions, RenderedFrames, boxDownscale, pointResize, renderModel, renderModelFrames };
@@ -1,2 +1,2 @@
1
- import { a as renderModelFrames, i as renderModel, n as boxDownscale, r as pointResize, t as ItemRenderError } from "../renderer-BeOinVC8.js";
1
+ import { a as renderModelFrames, i as renderModel, n as boxDownscale, r as pointResize, t as ItemRenderError } from "../renderer-CjT19tHd.js";
2
2
  export { ItemRenderError, boxDownscale, pointResize, renderModel, renderModelFrames };
@@ -1,2 +1,2 @@
1
- import { a as resolveTint, i as parseTintValue, n as InvalidTinterDefinitionError, o as resolveTints, r as TintContext, t as DYE_COLORS } from "../tints-txt-8xI5.js";
1
+ import { a as resolveTint, i as parseTintValue, n as InvalidTinterDefinitionError, o as resolveTints, r as TintContext, t as DYE_COLORS } from "../tints-DLBExVWN.js";
2
2
  export { DYE_COLORS, InvalidTinterDefinitionError, TintContext, parseTintValue, resolveTint, resolveTints };
@@ -1,13 +1,13 @@
1
- import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
1
  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";
3
- import { i as resolveTexture } from "./loader-A-BZa8J4.js";
2
+ import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
4
3
  import { a as tintTexture, n as cloneFloatTexture, r as darkenTexture, s as toFloatTexture, t as blendPixel } from "./blend-CHP2-j9U.js";
5
- import { t as combineFaceBuffers } from "./compose-qjhyBvnS.js";
6
- import { a as phpRound, i as drawTexturedTriangle, r as createFaceBuffer, t as RowCursor } from "./rasterizer-BHWDrmGz.js";
7
- import { n as renderFace, t as faceMaxZ } from "./face-D-N1amV4.js";
4
+ import { t as combineFaceBuffers } from "./compose-CQCb2LVv.js";
5
+ import { a as drawTexturedTriangle, i as createFaceBuffer, n as clearFaceBuffer, o as phpRound, t as RowCursor } from "./rasterizer-76qO8TDh.js";
6
+ import { n as renderFace, t as faceMaxZ } from "./face-DmCiHUd5.js";
8
7
  import { t as paintBucketFluid } from "./fluid-bucket-D-GpUQjg.js";
9
8
  import { r as buildElementFaces } from "./geometry-uIDha8S4.js";
10
9
  import { c as lightsForProfile } from "./lighting--5myudVu.js";
10
+ import { i as resolveTexture } from "./loader-B8NJO7pR.js";
11
11
  //#region src/render/obj.ts
12
12
  /**
13
13
  * Forge/NeoForge OBJ model rendering — the class the old pipeline left
@@ -140,6 +140,7 @@ async function renderObjModel(model, resources, textures, options) {
140
140
  const transformation = multiply(guiMatrix, options.itemTransform ?? identity());
141
141
  const renderSize = options.size * options.quality;
142
142
  const out = createFaceBuffer(renderSize, renderSize);
143
+ const scratch = createFaceBuffer(renderSize, renderSize);
143
144
  const shaded = /* @__PURE__ */ new Map();
144
145
  for (const triangle of parsed.triangles) {
145
146
  const transformed = triangle.positions.map((position) => transformVector(transformation, position));
@@ -164,7 +165,7 @@ async function renderObjModel(model, resources, textures, options) {
164
165
  darkenTexture(texture, 1 - bucket / 64, 0, 0, texture.width, texture.height);
165
166
  shaded.set(key, texture);
166
167
  }
167
- const scratch = createFaceBuffer(renderSize, renderSize);
168
+ clearFaceBuffer(scratch);
168
169
  const cursor = new RowCursor();
169
170
  const point = (index) => {
170
171
  return {
@@ -176,7 +177,8 @@ async function renderObjModel(model, resources, textures, options) {
176
177
  };
177
178
  };
178
179
  drawTexturedTriangle(point(0), point(1), point(2), scratch, texture, cursor);
179
- for (let pixel = 0; pixel < renderSize * renderSize; pixel++) {
180
+ 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++) {
181
+ const pixel = y * renderSize + x;
180
182
  const offset = pixel * 4;
181
183
  const alpha = scratch.colors[offset + 3];
182
184
  if (alpha <= 0) continue;
@@ -241,16 +243,19 @@ async function renderModelFrames(model, textures, options) {
241
243
  ticks = ticks / gcd(ticks, texture.tickLength) * texture.tickLength;
242
244
  interpolated ||= texture.interpolated;
243
245
  }
244
- const maxSkippable = interpolated ? Math.ceil(ticks / MAX_ANIMATION_FRAMES) : null;
246
+ const maxFrames = Math.max(1, options.animation?.maxFrames ?? MAX_ANIMATION_FRAMES);
247
+ const exact = interpolated ? null : changeTicks(animated.values().toArray(), ticks, maxFrames);
248
+ const candidates = exact ?? evenlySpacedTicks(ticks, maxFrames);
245
249
  const frames = [];
246
250
  const emitTicks = [];
247
251
  let lastKey = null;
248
- let sinceLast = 0;
249
- for (let tick = 0; tick < ticks; tick++) {
250
- sinceLast++;
251
- const key = animated.values().map((texture) => texture.resolveFrame(tick).frame).toArray().join(",");
252
- if (lastKey !== null && key === lastKey && !interpolated && tick !== ticks - 1) continue;
253
- if (lastKey !== null && interpolated && maxSkippable !== null && sinceLast < maxSkippable && key === lastKey) continue;
252
+ const session = createSession();
253
+ for (const tick of candidates) {
254
+ const key = animated.values().map((texture) => {
255
+ const resolved = texture.resolveFrame(tick);
256
+ return texture.interpolated ? `${resolved.frame}+${resolved.interpolation}` : String(resolved.frame);
257
+ }).toArray().join(",");
258
+ if (key === lastKey) continue;
254
259
  const supplier = async (reference) => {
255
260
  if (skipped.has(reference)) return null;
256
261
  const texture = animated.get(reference);
@@ -258,20 +263,59 @@ async function renderModelFrames(model, textures, options) {
258
263
  return texture.at(tick);
259
264
  };
260
265
  const renderSize = options.size * options.quality;
261
- const full = model.builtinParent === "builtin/generated" ? await renderGeneratedItemWith(model, supplier, renderSize, options.tints) : await renderElementModelWith(model, supplier, renderSize, options);
266
+ const full = model.builtinParent === "builtin/generated" ? await renderGeneratedItemWith(model, supplier, renderSize, options.tints) : await renderElementModelWith(model, supplier, renderSize, options, session);
262
267
  frames.push(options.quality === 1 ? full : boxDownscale(full, options.quality));
263
268
  emitTicks.push(tick);
264
269
  lastKey = key;
265
- sinceLast = 0;
266
270
  }
267
271
  return {
268
272
  frames,
269
273
  delaysMs: emitTicks.map((tick, index) => {
270
274
  const next = index + 1 < emitTicks.length ? emitTicks[index + 1] : ticks;
271
275
  return Math.max(TICK_MS, (next - tick) * TICK_MS);
272
- })
276
+ }),
277
+ animation: {
278
+ ticks,
279
+ candidates: candidates.length,
280
+ sampled: exact === null && ticks > frames.length,
281
+ interpolated
282
+ }
273
283
  };
274
284
  }
285
+ /**
286
+ * Every tick in the combined timeline where some texture switches frame, or
287
+ * null once that exceeds `limit` — past that the caller samples instead, and
288
+ * enumerating a multi-minute timeline in full would be the very cost this
289
+ * bound exists to avoid.
290
+ */
291
+ function changeTicks(textures, ticks, limit) {
292
+ const found = /* @__PURE__ */ new Set([0]);
293
+ for (const texture of textures) {
294
+ if (texture.frameCount <= 1) continue;
295
+ const period = Math.max(1, texture.tickLength);
296
+ const starts = texture.frameStartTicks();
297
+ for (let base = 0; base < ticks; base += period) for (const start of starts) {
298
+ const tick = base + start;
299
+ if (tick < ticks) found.add(tick);
300
+ if (found.size > limit) return null;
301
+ }
302
+ }
303
+ return [...found].toSorted((left, right) => left - right);
304
+ }
305
+ /** `count` tick positions spread evenly across the cycle, always starting at 0. */
306
+ function evenlySpacedTicks(ticks, maxFrames) {
307
+ const count = Math.min(ticks, maxFrames);
308
+ const sampled = [];
309
+ let previous = -1;
310
+ for (let index = 0; index < count; index++) {
311
+ const tick = Math.floor(index * ticks / count);
312
+ if (tick !== previous) {
313
+ sampled.push(tick);
314
+ previous = tick;
315
+ }
316
+ }
317
+ return sampled;
318
+ }
275
319
  async function loadAnimatedTexture(model, textures, reference) {
276
320
  const locator = resolveTexture(model, reference);
277
321
  if (locator) {
@@ -391,11 +435,23 @@ async function renderElementModel(model, textures, renderSize, options) {
391
435
  };
392
436
  return renderElementModelWith(model, supplier, renderSize, options);
393
437
  }
394
- async function renderElementModelWith(model, supplier, renderSize, options) {
438
+ function createSession() {
439
+ return {
440
+ geometry: null,
441
+ order: null,
442
+ transformation: null,
443
+ scratch: null,
444
+ shadeCache: /* @__PURE__ */ new WeakMap(),
445
+ floatTextures: /* @__PURE__ */ new Map(),
446
+ faceBuffers: []
447
+ };
448
+ }
449
+ /** Interpolated frames are new objects every tick; don't grow without bound. */
450
+ const MAX_CACHED_FLOAT_TEXTURES = 32;
451
+ async function renderElementModelWith(model, supplier, renderSize, options, session = createSession()) {
395
452
  const lights = lightsForProfile(options.lightingProfile);
396
453
  const light = model.elementsGuiLight === "side" ? lights.side : lights.front;
397
454
  const loadedTextures = /* @__PURE__ */ new Map();
398
- const floatTextures = /* @__PURE__ */ new Map();
399
455
  const load = (reference) => {
400
456
  let pending = loadedTextures.get(reference);
401
457
  if (!pending) {
@@ -404,47 +460,68 @@ async function renderElementModelWith(model, supplier, renderSize, options) {
404
460
  }
405
461
  return pending;
406
462
  };
407
- const faces = [];
463
+ if (!session.geometry) {
464
+ const geometry = [];
465
+ for (const element of model.elements) for (const face of buildElementFaces(element)) geometry.push({
466
+ geometry: face,
467
+ maxZ: faceMaxZ(face)
468
+ });
469
+ session.geometry = geometry;
470
+ session.order = geometry.map((_, index) => index).toSorted((left, right) => geometry[left].maxZ - geometry[right].maxZ);
471
+ }
472
+ const specs = session.geometry;
473
+ const order = session.order ?? [];
408
474
  let skippedFaces = 0;
409
- for (const element of model.elements) for (const geometry of buildElementFaces(element)) {
410
- const texture = await load(geometry.textureRef);
411
- if (!texture) {
412
- skippedFaces++;
413
- continue;
414
- }
415
- let float = floatTextures.get(texture);
475
+ let drawable = 0;
476
+ for (const spec of specs) if (await load(spec.geometry.textureRef)) drawable++;
477
+ else skippedFaces++;
478
+ if (drawable === 0 && skippedFaces > 0) throw new ItemRenderError(`Every face texture unresolved in ${model.locator} (would render invisible)`);
479
+ if (!session.transformation) {
480
+ const gui = model.display.gui;
481
+ const guiMatrix = displayMatrix(gui?.rotation ?? [
482
+ 0,
483
+ 0,
484
+ 0
485
+ ], gui?.translation ?? [
486
+ 0,
487
+ 0,
488
+ 0
489
+ ], gui?.scale ?? [
490
+ 1,
491
+ 1,
492
+ 1
493
+ ]);
494
+ session.transformation = multiply(guiMatrix, options.itemTransform ?? identity());
495
+ }
496
+ session.scratch ??= createFaceBuffer(renderSize, renderSize);
497
+ if (session.floatTextures.size > MAX_CACHED_FLOAT_TEXTURES) session.floatTextures.clear();
498
+ const buffers = [];
499
+ for (const index of order) {
500
+ const spec = specs[index];
501
+ const texture = await load(spec.geometry.textureRef);
502
+ if (!texture) continue;
503
+ let float = session.floatTextures.get(texture);
416
504
  if (!float) {
417
505
  float = toFloatTexture(texture);
418
- floatTextures.set(texture, float);
506
+ session.floatTextures.set(texture, float);
507
+ }
508
+ const cached = session.faceBuffers[index];
509
+ if (cached?.texture === float) {
510
+ if (cached.buffer) buffers.push(cached.buffer);
511
+ continue;
419
512
  }
420
- faces.push({
421
- geometry,
513
+ const face = {
514
+ geometry: spec.geometry,
422
515
  texture: float,
423
516
  light,
424
- tint: geometry.tintIndex !== null ? options.tints?.[geometry.tintIndex] ?? null : null,
425
- maxZ: faceMaxZ(geometry)
426
- });
427
- }
428
- if (faces.length === 0 && skippedFaces > 0) throw new ItemRenderError(`Every face texture unresolved in ${model.locator} (would render invisible)`);
429
- faces.sort((a, b) => a.maxZ - b.maxZ);
430
- const gui = model.display.gui;
431
- const guiMatrix = displayMatrix(gui?.rotation ?? [
432
- 0,
433
- 0,
434
- 0
435
- ], gui?.translation ?? [
436
- 0,
437
- 0,
438
- 0
439
- ], gui?.scale ?? [
440
- 1,
441
- 1,
442
- 1
443
- ]);
444
- const transformation = multiply(guiMatrix, options.itemTransform ?? identity());
445
- const buffers = [];
446
- for (const face of faces) {
447
- const buffer = renderFace(face, renderSize, renderSize, transformation);
517
+ tint: spec.geometry.tintIndex !== null ? options.tints?.[spec.geometry.tintIndex] ?? null : null,
518
+ maxZ: spec.maxZ
519
+ };
520
+ const buffer = renderFace(face, renderSize, renderSize, session.transformation, session.scratch, session.shadeCache);
521
+ session.faceBuffers[index] = {
522
+ texture: float,
523
+ buffer
524
+ };
448
525
  if (buffer) buffers.push(buffer);
449
526
  }
450
527
  return combineFaceBuffers(renderSize, renderSize, buffers);
@@ -1,15 +1,21 @@
1
- import { a as ResourceManager } from "./manager-CoZaeQTu.js";
2
- import { i as TextureLoader } from "./loader-TFFNV4Jl.js";
3
- import { r as ResolvedModel } from "./loader-C-6QN3xv.js";
4
- import { t as FloatTexture } from "./blend-Qvrn6YgK.js";
1
+ import { a as ResourceManager } from "./manager-Cv1NeFyF.js";
2
+ import { i as TextureLoader } from "./loader-Myy22TDL.js";
3
+ import { r as ResolvedModel } from "./loader-D22zZME7.js";
4
+ import { t as FloatTexture } from "./blend-CyDIxCne.js";
5
5
  import { t as Mat4 } from "./vector-DRKRi4--.js";
6
6
  import { a as LightingProfileName } from "./lighting-3FGRC3Af.js";
7
- import { n as TintColor } from "./face-CpVdnf-w.js";
7
+ import { r as TintColor } from "./face-BIYg14TE.js";
8
8
  //#region src/render/renderer.d.ts
9
+ interface AnimationLimits {
10
+ /** Emitted frames per icon (default 128). */
11
+ maxFrames?: number;
12
+ }
9
13
  interface RenderOptions {
10
14
  size: number;
11
15
  quality: number;
12
16
  lightingProfile: LightingProfileName;
17
+ /** Bounds on animated timelines; defaults apply when omitted. */
18
+ animation?: AnimationLimits;
13
19
  /** Accumulated item-definition transformation (inner matrix). */
14
20
  itemTransform?: Mat4;
15
21
  /** Tint colors by index (face tintindex / generated layer number). */
@@ -22,6 +28,17 @@ interface RenderedFrames {
22
28
  frames: FloatTexture[];
23
29
  /** Display duration per frame in milliseconds (50ms per game tick). */
24
30
  delaysMs: number[];
31
+ /** What the timeline did, so a bounded animation is never a silent one. */
32
+ animation?: AnimationDiagnostics;
33
+ }
34
+ interface AnimationDiagnostics {
35
+ /** Combined cycle length in game ticks (the lcm of the textures' periods). */
36
+ ticks: number;
37
+ /** Ticks considered before identical neighbours were coalesced. */
38
+ candidates: number;
39
+ /** Whether the frame cap forced even sampling instead of exact frames. */
40
+ sampled: boolean;
41
+ interpolated: boolean;
25
42
  }
26
43
  /**
27
44
  * Render every animation frame of a model (a single frame for unanimated
@@ -41,4 +58,4 @@ declare function pointResize(texture: FloatTexture, width: number, height: numbe
41
58
  */
42
59
  declare function boxDownscale(texture: FloatTexture, factor: number): FloatTexture;
43
60
  //#endregion
44
- export { pointResize as a, boxDownscale as i, RenderOptions as n, renderModel as o, RenderedFrames as r, renderModelFrames as s, ItemRenderError as t };
61
+ export { RenderedFrames as a, renderModel as c, RenderOptions as i, renderModelFrames as l, AnimationLimits as n, boxDownscale as o, ItemRenderError as r, pointResize as s, AnimationDiagnostics as t };
@@ -0,0 +1,30 @@
1
+ //#region src/resources/cache.d.ts
2
+ /**
3
+ * Least-recently-used map used by the model and texture loaders. Their caches
4
+ * were unbounded, which is fine for one namespace and not fine for a run that
5
+ * walks thousands of them in a single process — resident memory only ever grew.
6
+ */
7
+ declare class LruCache<K, V> {
8
+ private readonly entries;
9
+ private readonly maxEntries;
10
+ private readonly weigh;
11
+ private readonly maxWeight;
12
+ private readonly weights;
13
+ private weight;
14
+ constructor(maxEntries: number, options?: {
15
+ maxWeight?: number;
16
+ weigh?: (value: V) => number;
17
+ });
18
+ private evict;
19
+ get size(): number;
20
+ get(key: K): V | undefined;
21
+ set(key: K, value: V): void;
22
+ /**
23
+ * Record a value's real cost once it is known. Loaders cache promises, so the
24
+ * weight of a decoded texture only exists after it resolves.
25
+ */
26
+ settle(key: K, value: V): void;
27
+ clear(): void;
28
+ }
29
+ //#endregion
30
+ export { LruCache };
@@ -0,0 +1,2 @@
1
+ import { t as LruCache } from "../cache-Cr2ON3RQ.js";
2
+ export { LruCache };
@@ -0,0 +1,2 @@
1
+ import { c as describeResources, l as restoreResources, o as DescribableResourceManager, s as ResourceDescriptor } from "../manager-Cv1NeFyF.js";
2
+ export { DescribableResourceManager, ResourceDescriptor, describeResources, restoreResources };
@@ -0,0 +1,2 @@
1
+ import { n as restoreResources, t as describeResources } from "../descriptor-DWebtkI1.js";
2
+ export { describeResources, restoreResources };
@@ -1,2 +1,2 @@
1
- import { n as UnsafeJarError, r as isUnsafeEntryPath, t as JarResourceManager } from "../jar-6T3xdgaL.js";
1
+ import { n as UnsafeJarError, r as isUnsafeEntryPath, t as JarResourceManager } from "../jar-CFkA-VgT.js";
2
2
  export { JarResourceManager, UnsafeJarError, isUnsafeEntryPath };
@@ -1,2 +1,2 @@
1
- import { n as UnsafeJarError, r as isUnsafeEntryPath, t as JarResourceManager } from "../jar-Bt3i0ls4.js";
1
+ import { n as UnsafeJarError, r as isUnsafeEntryPath, t as JarResourceManager } from "../jar-D9z-X7tA.js";
2
2
  export { JarResourceManager, UnsafeJarError, isUnsafeEntryPath };
@@ -1,2 +1,2 @@
1
- import { a as ResourceManager, i as ResourceKindName, n as LayeredResourceManager, r as ResourceKind, t as FolderResourceManager } from "../manager-CoZaeQTu.js";
1
+ import { a as ResourceManager, i as ResourceKindName, n as LayeredResourceManager, r as ResourceKind, t as FolderResourceManager } from "../manager-Cv1NeFyF.js";
2
2
  export { FolderResourceManager, LayeredResourceManager, ResourceKind, ResourceKindName, ResourceManager };
@@ -1,2 +1,2 @@
1
- import { n as LayeredResourceManager, r as ResourceKind, t as FolderResourceManager } from "../manager-dfI7b9N4.js";
1
+ import { n as LayeredResourceManager, r as ResourceKind, t as FolderResourceManager } from "../manager-CkletR5K.js";
2
2
  export { FolderResourceManager, LayeredResourceManager, ResourceKind };
@@ -1,2 +1,2 @@
1
- import { n as MapResourceManager, t as KindFilteredResourceManager } from "../synthetic-CDsj--li.js";
1
+ import { n as MapResourceManager, t as KindFilteredResourceManager } from "../synthetic-Qk-BSUXO.js";
2
2
  export { KindFilteredResourceManager, MapResourceManager };
@@ -1,2 +1,2 @@
1
- import { n as MapResourceManager, t as KindFilteredResourceManager } from "../synthetic-DC2bSnGe.js";
1
+ import { n as MapResourceManager, t as KindFilteredResourceManager } from "../synthetic-ZAUjXT7E.js";
2
2
  export { KindFilteredResourceManager, MapResourceManager };
@@ -0,0 +1,5 @@
1
+ import { ZipFile } from "yauzl";
2
+ //#region src/resources/zip-reader.d.ts
3
+ declare function openZip(zipPath: string): Promise<ZipFile>;
4
+ //#endregion
5
+ export { openZip };
@@ -0,0 +1,2 @@
1
+ import { t as openZip } from "../zip-reader-BSbJKUT1.js";
2
+ export { openZip };