@nodecraft/rendermite 1.0.5 → 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.
- package/README.md +5 -0
- package/dist/atlas/registry.d.ts +3 -3
- package/dist/atlas/sources.d.ts +1 -1
- package/dist/{blend-Qvrn6YgK.d.ts → blend-CyDIxCne.d.ts} +1 -1
- package/dist/cache-Cr2ON3RQ.js +66 -0
- package/dist/cli.js +12 -6
- package/dist/compose-CQCb2LVv.js +104 -0
- package/dist/descriptor-DWebtkI1.js +26 -0
- package/dist/{discovery-D89pVVJA.d.ts → discovery-wiJBGryo.d.ts} +1 -1
- package/dist/dynamic/generators.d.ts +1 -1
- package/dist/dynamic/generators.js +1 -1
- package/dist/{encode-3u49i-eD.js → encode-D6dQu2dJ.js} +16 -4
- package/dist/{encode-C5kdAm45.d.ts → encode-DaNQF28M.d.ts} +1 -1
- package/dist/{entity-fallback-DfL0_eyf.js → entity-fallback-BRCeGKBT.js} +1 -1
- package/dist/{face-CpVdnf-w.d.ts → face-BIYg14TE.d.ts} +10 -4
- package/dist/{face-D-N1amV4.js → face-DmCiHUd5.js} +23 -10
- package/dist/{generators-CcVRG30y.js → generators-BlG4KmtP.js} +1 -1
- package/dist/{generators-CCB8h0mP.d.ts → generators-DhRov4RB.d.ts} +1 -1
- package/dist/{geo-Dq2zve09.js → geo-Diky41Tw.js} +6 -3
- package/dist/index.d.ts +14 -14
- package/dist/index.js +11 -11
- package/dist/{item-styles-DJ3LWZS2.js → item-styles-upgEO1yP.js} +1 -1
- package/dist/items/discovery.d.ts +1 -1
- package/dist/{jar-6T3xdgaL.d.ts → jar-CFkA-VgT.d.ts} +2 -1
- package/dist/{jar-Bt3i0ls4.js → jar-D9z-X7tA.js} +12 -12
- package/dist/{legacy-reshape-CewShfF3.js → legacy-reshape-C05SfAPR.js} +1 -1
- package/dist/{loader-A-BZa8J4.js → loader-B8NJO7pR.js} +12 -3
- package/dist/{loader-CBzn3Koc.js → loader-CfXMxcEV.js} +37 -7
- package/dist/{loader-C-6QN3xv.d.ts → loader-D22zZME7.d.ts} +2 -2
- package/dist/{loader-TFFNV4Jl.d.ts → loader-Myy22TDL.d.ts} +8 -2
- package/dist/{manager-dfI7b9N4.js → manager-CkletR5K.js} +29 -2
- package/dist/{manager-CoZaeQTu.d.ts → manager-Cv1NeFyF.d.ts} +28 -1
- package/dist/models/loader.d.ts +1 -1
- package/dist/models/loader.js +1 -1
- package/dist/mods/prepare.d.ts +1 -1
- package/dist/mods/prepare.js +1 -1
- package/dist/mods/synthesize.d.ts +2 -2
- package/dist/mods/synthesize.js +1 -1
- package/dist/mods/tints.d.ts +2 -2
- package/dist/mods/tints.js +1 -1
- package/dist/output/encode.d.ts +1 -1
- package/dist/output/encode.js +1 -1
- package/dist/output/item-styles.js +1 -1
- package/dist/pipeline-BIk1RRIp.js +221 -0
- package/dist/pipeline-zq-yU3Te.d.ts +58 -0
- package/dist/pool-CKGp1qup.js +131 -0
- package/dist/{prepare-BeUpXwaP.js → prepare-BESRrh6K.js} +4 -4
- package/dist/{prepare-gfR1aIjt.d.ts → prepare-CPxO0kKz.d.ts} +1 -1
- package/dist/{prepare-C8wRjAXP.js → prepare-Cc8QZ3j0.js} +4 -4
- package/dist/{prepare-wfWtaz_X.d.ts → prepare-Cs6QDqie.d.ts} +1 -1
- package/dist/{rasterizer-BHWDrmGz.js → rasterizer-76qO8TDh.js} +42 -4
- package/dist/{rasterizer-Dh35qXbn.d.ts → rasterizer-yqJkicm1.d.ts} +21 -2
- package/dist/render/blend.d.ts +1 -1
- package/dist/render/compose.d.ts +2 -2
- package/dist/render/compose.js +1 -1
- package/dist/render/entity-fallback.d.ts +6 -6
- package/dist/render/entity-fallback.js +1 -1
- package/dist/render/face.d.ts +2 -2
- package/dist/render/face.js +1 -1
- package/dist/render/fluid-bucket.d.ts +2 -2
- package/dist/render/geo.d.ts +4 -4
- package/dist/render/geo.js +1 -1
- package/dist/render/legacy-tints.d.ts +2 -2
- package/dist/render/obj.d.ts +5 -5
- package/dist/render/obj.js +1 -1
- package/dist/render/pipeline.d.ts +2 -0
- package/dist/render/pipeline.js +2 -0
- package/dist/render/rasterizer.d.ts +2 -2
- package/dist/render/rasterizer.js +2 -2
- package/dist/render/renderer.d.ts +2 -2
- package/dist/render/renderer.js +1 -1
- package/dist/render/tints.d.ts +1 -1
- package/dist/{renderer-LDhBNHcl.js → renderer-CjT19tHd.js} +130 -53
- package/dist/{renderer-nbMCw1Pl.d.ts → renderer-DVsZHAvf.d.ts} +23 -6
- package/dist/resources/cache.d.ts +30 -0
- package/dist/resources/cache.js +2 -0
- package/dist/resources/descriptor.d.ts +2 -0
- package/dist/resources/descriptor.js +2 -0
- package/dist/resources/jar.d.ts +1 -1
- package/dist/resources/jar.js +1 -1
- package/dist/resources/manager.d.ts +1 -1
- package/dist/resources/manager.js +1 -1
- package/dist/resources/synthetic.d.ts +1 -1
- package/dist/resources/synthetic.js +1 -1
- package/dist/resources/zip-reader.d.ts +5 -0
- package/dist/resources/zip-reader.js +2 -0
- package/dist/run-BnICOF4I.js +209 -0
- package/dist/{run-BlSZw9Qk.d.ts → run-CKUtynTy.d.ts} +12 -2
- package/dist/run.d.ts +1 -1
- package/dist/run.js +1 -1
- package/dist/{sources-DH9dk3KF.d.ts → sources-B9X9w3qP.d.ts} +1 -1
- package/dist/{synthesize-CRgSEmEp.js → synthesize-3Q0aaOnK.js} +1 -1
- package/dist/{synthetic-CDsj--li.d.ts → synthetic-Qk-BSUXO.d.ts} +5 -1
- package/dist/{synthetic-DC2bSnGe.js → synthetic-ZAUjXT7E.js} +0 -0
- package/dist/textures/loader.d.ts +1 -1
- package/dist/textures/loader.js +1 -1
- package/dist/{tints-CuG6GwQJ.js → tints-B8UJmZMu.js} +2 -2
- package/dist/{tints-txt-8xI5.d.ts → tints-DLBExVWN.d.ts} +2 -2
- package/dist/versions/legacy-reshape.d.ts +1 -1
- package/dist/versions/legacy-reshape.js +1 -1
- package/dist/versions/prepare.d.ts +1 -1
- package/dist/versions/prepare.js +1 -1
- package/dist/workers/pool.d.ts +36 -5
- package/dist/workers/pool.js +2 -67
- package/dist/workers/render-worker.d.ts +15 -0
- package/dist/workers/render-worker.js +40 -0
- package/dist/zip-reader-BSbJKUT1.js +156 -0
- package/package.json +4 -2
- package/dist/compose-qjhyBvnS.js +0 -41
- package/dist/run-CfnWIRY4.js +0 -316
|
@@ -1,13 +1,13 @@
|
|
|
1
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";
|
|
2
2
|
import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
|
|
3
|
-
import { i as resolveTexture } from "./loader-A-BZa8J4.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-
|
|
6
|
-
import { a as
|
|
7
|
-
import { n as renderFace, t as faceMaxZ } from "./face-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
249
|
-
for (
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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);
|
|
419
507
|
}
|
|
420
|
-
|
|
421
|
-
|
|
508
|
+
const cached = session.faceBuffers[index];
|
|
509
|
+
if (cached?.texture === float) {
|
|
510
|
+
if (cached.buffer) buffers.push(cached.buffer);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
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:
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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-
|
|
2
|
-
import { i as TextureLoader } from "./loader-
|
|
3
|
-
import { r as ResolvedModel } from "./loader-
|
|
4
|
-
import { t as FloatTexture } from "./blend-
|
|
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 {
|
|
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 {
|
|
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 };
|
package/dist/resources/jar.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as UnsafeJarError, r as isUnsafeEntryPath, t as JarResourceManager } from "../jar-
|
|
1
|
+
import { n as UnsafeJarError, r as isUnsafeEntryPath, t as JarResourceManager } from "../jar-CFkA-VgT.js";
|
|
2
2
|
export { JarResourceManager, UnsafeJarError, isUnsafeEntryPath };
|
package/dist/resources/jar.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as UnsafeJarError, r as isUnsafeEntryPath, t as JarResourceManager } from "../jar-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
1
|
+
import { n as MapResourceManager, t as KindFilteredResourceManager } from "../synthetic-ZAUjXT7E.js";
|
|
2
2
|
export { KindFilteredResourceManager, MapResourceManager };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
|
|
2
|
+
import { n as discoverItems } from "./discovery-0SM0cTLw.js";
|
|
3
|
+
import { r as encodeRasterAsset } from "./encode-D6dQu2dJ.js";
|
|
4
|
+
import { t as classifyItemStyle } from "./item-styles-upgEO1yP.js";
|
|
5
|
+
import { t as minifyStylesheet } from "./minify-bjMRmr5J.js";
|
|
6
|
+
import { t as generateStylesheet } from "./stylesheet-CovT3B2S.js";
|
|
7
|
+
import { t as ItemPipeline } from "./pipeline-BIk1RRIp.js";
|
|
8
|
+
import { t as describeResources } from "./descriptor-DWebtkI1.js";
|
|
9
|
+
import { t as WorkerRenderPool } from "./pool-CKGp1qup.js";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import os from "node:os";
|
|
12
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
//#region src/run.ts
|
|
15
|
+
/**
|
|
16
|
+
* Render-run orchestration: discover items, evaluate definitions, render,
|
|
17
|
+
* encode, write images + stylesheet + item list. Unlike upstream, failures are
|
|
18
|
+
* per-item, collected, and reported — a run summary counts files, never log lines.
|
|
19
|
+
*
|
|
20
|
+
* Items render in worker threads when the resource tree can describe itself
|
|
21
|
+
* (see resources/descriptor.ts); otherwise the same pipeline runs in-process.
|
|
22
|
+
*/
|
|
23
|
+
async function run(options) {
|
|
24
|
+
const namespaces = options.namespaces ?? ["minecraft"];
|
|
25
|
+
const size = options.size ?? 64;
|
|
26
|
+
const quality = options.quality ?? 2;
|
|
27
|
+
const prefix = options.prefix ?? "rc-";
|
|
28
|
+
const format = options.format ?? "webp";
|
|
29
|
+
const fallback = options.fallback ?? true;
|
|
30
|
+
const lightingProfile = options.lightingProfile ?? "modern";
|
|
31
|
+
const settings = {
|
|
32
|
+
builtin: options.builtin,
|
|
33
|
+
output: options.output,
|
|
34
|
+
size,
|
|
35
|
+
quality,
|
|
36
|
+
format,
|
|
37
|
+
gif: options.gif,
|
|
38
|
+
avif: options.avif,
|
|
39
|
+
lightingProfile,
|
|
40
|
+
animation: options.animation
|
|
41
|
+
};
|
|
42
|
+
const concurrency = Math.max(1, options.concurrency ?? 8);
|
|
43
|
+
const requestedWorkers = resolveWorkerCount(options.workers);
|
|
44
|
+
const descriptor = requestedWorkers > 1 ? describeResources(options.resources) : null;
|
|
45
|
+
const pool = descriptor ? new WorkerRenderPool({
|
|
46
|
+
workerUrl: workerUrl(),
|
|
47
|
+
workerData: {
|
|
48
|
+
resources: descriptor,
|
|
49
|
+
settings,
|
|
50
|
+
sharpConcurrency: Math.max(1, Math.round(os.availableParallelism() / requestedWorkers))
|
|
51
|
+
},
|
|
52
|
+
workers: requestedWorkers,
|
|
53
|
+
inFlight: concurrency
|
|
54
|
+
}) : null;
|
|
55
|
+
const usedWorkers = pool ? requestedWorkers : 1;
|
|
56
|
+
const pipeline = await ItemPipeline.create(options.resources, settings).catch(async (err) => {
|
|
57
|
+
await pool?.close();
|
|
58
|
+
throw err;
|
|
59
|
+
});
|
|
60
|
+
const { resources } = pipeline;
|
|
61
|
+
const glint = await resolveEnchantmentGlint(resources);
|
|
62
|
+
if (!glint) throw new Error("Missing enchantment glint texture (minecraft:misc/enchanted_glint_item or enchanted_item_glint)");
|
|
63
|
+
const encodedGlint = await encodeRasterAsset(glint, fallback ? ["png", format] : [format]);
|
|
64
|
+
const ids = [];
|
|
65
|
+
const seen = /* @__PURE__ */ new Set();
|
|
66
|
+
if (options.builtin) for (const placeholder of ["renderchest:unknown", "renderchest:empty"]) {
|
|
67
|
+
seen.add(placeholder);
|
|
68
|
+
ids.push(placeholder);
|
|
69
|
+
}
|
|
70
|
+
for (const namespace of namespaces) for (const item of await discoverItems(resources, namespace, { filterUntranslatedModels: options.filterUntranslatedModels })) {
|
|
71
|
+
if (seen.has(item.id)) continue;
|
|
72
|
+
if (options.builtin && !await itemExistsIn(options.resources, item.id)) continue;
|
|
73
|
+
seen.add(item.id);
|
|
74
|
+
ids.push(item.id);
|
|
75
|
+
}
|
|
76
|
+
if (pipeline.dynamicItems && namespaces.includes("minecraft")) {
|
|
77
|
+
const hasTrims = await resources.hasResource("texture", ResourceLocator.parse("minecraft:trims/color_palettes/trim_palette"));
|
|
78
|
+
const groupAvailable = {
|
|
79
|
+
_armor_trims: hasTrims,
|
|
80
|
+
_leather_armor: hasTrims,
|
|
81
|
+
_decorated_pot: await resources.hasResource("texture", ResourceLocator.parse("minecraft:entity/decorated_pot/decorated_pot_base")),
|
|
82
|
+
_crossbow: await resources.hasResource("model", ResourceLocator.parse("minecraft:item/crossbow_arrow"))
|
|
83
|
+
};
|
|
84
|
+
for (const id of pipeline.dynamicItems) if ((groupAvailable[id.split(":", 1)[0]] ?? true) && !seen.has(id)) {
|
|
85
|
+
seen.add(id);
|
|
86
|
+
ids.push(id);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const rendered = [];
|
|
90
|
+
const failed = [];
|
|
91
|
+
const spriteFallbacks = [];
|
|
92
|
+
const skippedTemplates = [];
|
|
93
|
+
const styles = [];
|
|
94
|
+
let writtenFiles = 0;
|
|
95
|
+
await mkdir(options.output, { recursive: true });
|
|
96
|
+
for (const [extension, buffer] of encodedGlint) {
|
|
97
|
+
await writeFile(path.join(options.output, `${prefix}enchantment.${extension}`), buffer);
|
|
98
|
+
writtenFiles++;
|
|
99
|
+
}
|
|
100
|
+
let templateParents = null;
|
|
101
|
+
const loadTemplateParents = async () => {
|
|
102
|
+
const parents = /* @__PURE__ */ new Set();
|
|
103
|
+
for (const namespace of namespaces) for (const modelLocator of await resources.listResources("model", namespace)) {
|
|
104
|
+
if (!modelLocator.path.startsWith("item/")) continue;
|
|
105
|
+
const raw = await resources.getResource("model", modelLocator);
|
|
106
|
+
if (!raw) continue;
|
|
107
|
+
try {
|
|
108
|
+
const parent = JSON.parse(raw.toString("utf8")).parent;
|
|
109
|
+
if (typeof parent === "string") parents.add(ResourceLocator.parse(parent).toString());
|
|
110
|
+
} catch {}
|
|
111
|
+
}
|
|
112
|
+
return parents;
|
|
113
|
+
};
|
|
114
|
+
const isTemplateModel = async (id) => {
|
|
115
|
+
const locator = ResourceLocator.parse(id);
|
|
116
|
+
if (await resources.hasResource("itemDefinition", locator)) return false;
|
|
117
|
+
templateParents ??= loadTemplateParents();
|
|
118
|
+
return (await templateParents).has(new ResourceLocator(locator.namespace, `item/${locator.path}`).toString());
|
|
119
|
+
};
|
|
120
|
+
let completed = 0;
|
|
121
|
+
const record = async (id, outcome, error) => {
|
|
122
|
+
if (outcome) {
|
|
123
|
+
writtenFiles += outcome.written;
|
|
124
|
+
rendered.push(id);
|
|
125
|
+
if (outcome.usedFallback) spriteFallbacks.push(id);
|
|
126
|
+
} else if (await isTemplateModel(id).catch(() => false)) skippedTemplates.push(id);
|
|
127
|
+
else failed.push({
|
|
128
|
+
id,
|
|
129
|
+
error: error ?? "Unknown render failure"
|
|
130
|
+
});
|
|
131
|
+
completed++;
|
|
132
|
+
options.onProgress?.(completed, ids.length, id, outcome ? void 0 : error);
|
|
133
|
+
};
|
|
134
|
+
if (pool) {
|
|
135
|
+
const pending = [];
|
|
136
|
+
await pool.run(ids.map((id) => {
|
|
137
|
+
return { id };
|
|
138
|
+
}), (message) => {
|
|
139
|
+
pending.push(record(message.id, message.result ?? null, message.error));
|
|
140
|
+
});
|
|
141
|
+
await Promise.all(pending);
|
|
142
|
+
} else {
|
|
143
|
+
let nextIndex = 0;
|
|
144
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, ids.length) }, async () => {
|
|
145
|
+
while (nextIndex < ids.length) {
|
|
146
|
+
const id = ids[nextIndex++];
|
|
147
|
+
try {
|
|
148
|
+
await record(id, await pipeline.renderItem(id));
|
|
149
|
+
} catch (err) {
|
|
150
|
+
await record(id, null, err.message);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
rendered.sort((left, right) => left.localeCompare(right));
|
|
156
|
+
const renderedSet = new Set(rendered);
|
|
157
|
+
for (const id of rendered) {
|
|
158
|
+
const style = classifyItemStyle(id, renderedSet);
|
|
159
|
+
if (style) styles.push(style);
|
|
160
|
+
}
|
|
161
|
+
const stylesheet = generateStylesheet(styles, {
|
|
162
|
+
prefix,
|
|
163
|
+
format,
|
|
164
|
+
fallback
|
|
165
|
+
});
|
|
166
|
+
const finalStylesheet = options.minify === false ? stylesheet : minifyStylesheet(stylesheet, (message) => options.onProgress?.(0, 0, "stylesheet", message));
|
|
167
|
+
await writeFile(path.join(options.output, `${prefix}${namespaces[0]}-${size}.css`), finalStylesheet);
|
|
168
|
+
if (options.itemList !== false) {
|
|
169
|
+
await writeFile(path.join(options.output, `${prefix}${namespaces[0]}.json`), JSON.stringify(rendered));
|
|
170
|
+
await writeFile(path.join(options.output, `${prefix}${namespaces[0]}-failures.json`), JSON.stringify(failed, null, " "));
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
rendered,
|
|
174
|
+
failed,
|
|
175
|
+
spriteFallbacks,
|
|
176
|
+
skippedTemplates,
|
|
177
|
+
writtenFiles,
|
|
178
|
+
workers: usedWorkers
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* One thread per core less the orchestrator, capped at 8: measured throughput
|
|
183
|
+
* on a 16-thread host peaks around 4-8 workers and falls off above that, where
|
|
184
|
+
* per-worker asset caches and libvips threads start competing for the same cores.
|
|
185
|
+
*/
|
|
186
|
+
function resolveWorkerCount(requested) {
|
|
187
|
+
if (requested !== void 0) return Math.max(1, Math.floor(requested));
|
|
188
|
+
return Math.min(8, Math.max(1, os.availableParallelism() - 1));
|
|
189
|
+
}
|
|
190
|
+
/** The worker's own file, in whichever form this module was loaded from. */
|
|
191
|
+
function workerUrl() {
|
|
192
|
+
const extension = path.extname(fileURLToPath(import.meta.url)) || ".js";
|
|
193
|
+
return new URL(`./workers/render-worker${extension}`, import.meta.url);
|
|
194
|
+
}
|
|
195
|
+
async function itemExistsIn(resources, id) {
|
|
196
|
+
const locator = ResourceLocator.parse(id);
|
|
197
|
+
if (await resources.hasResource("itemDefinition", locator)) return true;
|
|
198
|
+
return resources.hasResource("model", new ResourceLocator(locator.namespace, `item/${locator.path}`));
|
|
199
|
+
}
|
|
200
|
+
/** Resolve both names used on either side of Minecraft 1.19.3. */
|
|
201
|
+
async function resolveEnchantmentGlint(resources) {
|
|
202
|
+
for (const path of ["misc/enchanted_glint_item", "misc/enchanted_item_glint"]) {
|
|
203
|
+
const texture = await resources.getResource("texture", new ResourceLocator("minecraft", path));
|
|
204
|
+
if (texture) return texture;
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
export { run as t };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { a as ResourceManager } from "./manager-
|
|
1
|
+
import { a as ResourceManager } from "./manager-Cv1NeFyF.js";
|
|
2
2
|
import { a as LightingProfileName } from "./lighting-3FGRC3Af.js";
|
|
3
|
+
import { n as AnimationLimits } from "./renderer-DVsZHAvf.js";
|
|
3
4
|
//#region src/run.d.ts
|
|
4
5
|
interface RunOptions {
|
|
5
6
|
resources: ResourceManager;
|
|
@@ -22,8 +23,15 @@ interface RunOptions {
|
|
|
22
23
|
avif?: boolean;
|
|
23
24
|
/** Minify the stylesheet via lightningcss (default true). */
|
|
24
25
|
minify?: boolean;
|
|
25
|
-
/** In-flight items (encode overlaps on libvips' threadpool; default 8). */
|
|
26
|
+
/** In-flight items per thread (encode overlaps on libvips' threadpool; default 8). */
|
|
26
27
|
concurrency?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Render threads. Defaults to one per core (minus the main thread). 1 renders
|
|
30
|
+
* in-process; a resource tree that cannot be described falls back to that too.
|
|
31
|
+
*/
|
|
32
|
+
workers?: number;
|
|
33
|
+
/** Bounds on animated timelines (frame count). */
|
|
34
|
+
animation?: AnimationLimits;
|
|
27
35
|
/**
|
|
28
36
|
* Drop classic item models with no matching translation (default true).
|
|
29
37
|
* Disable for a prepared asset tree whose own preprocessing has already
|
|
@@ -54,6 +62,8 @@ interface RunResult {
|
|
|
54
62
|
skippedTemplates: string[];
|
|
55
63
|
/** Image files actually written, the only trustworthy success signal. */
|
|
56
64
|
writtenFiles: number;
|
|
65
|
+
/** Worker threads used; 1 means everything rendered in-process. */
|
|
66
|
+
workers: number;
|
|
57
67
|
}
|
|
58
68
|
declare function run(options: RunOptions): Promise<RunResult>;
|
|
59
69
|
//#endregion
|
package/dist/run.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { l as evaluateItemModel } from "./definition-BwX6Xxv0.js";
|
|
2
|
-
import { i as run, n as RunOptions, r as RunResult, t as ItemFailure } from "./run-
|
|
2
|
+
import { i as run, n as RunOptions, r as RunResult, t as ItemFailure } from "./run-CKUtynTy.js";
|
|
3
3
|
export { ItemFailure, RunOptions, RunResult, evaluateItemModel, run };
|
package/dist/run.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as ResourceLocator } from "./locator-jDbdUeTd.js";
|
|
2
|
-
import { r as Texture } from "./loader-
|
|
2
|
+
import { r as Texture } from "./loader-Myy22TDL.js";
|
|
3
3
|
//#region src/atlas/sources.d.ts
|
|
4
4
|
declare class AtlasSourceError extends Error {}
|
|
5
5
|
interface AtlasSource {
|