@nodecraft/rendermite 1.0.5 → 1.0.7
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-CEHoJ7tM.d.ts} +3 -2
- package/dist/{blend-CHP2-j9U.js → blend-D0XWgVQr.js} +20 -8
- package/dist/cache-Cr2ON3RQ.js +66 -0
- package/dist/cli.js +12 -6
- package/dist/compose-lWPrB7wh.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-C5kdAm45.d.ts → encode-CaSK8-mi.d.ts} +1 -1
- package/dist/{encode-3u49i-eD.js → encode-Df_tWzkl.js} +44 -16
- package/dist/{entity-fallback-DfL0_eyf.js → entity-fallback-D9QTGU9X.js} +1 -1
- package/dist/{face-CpVdnf-w.d.ts → face-C49L66sQ.d.ts} +10 -4
- package/dist/{face-D-N1amV4.js → face-Cl1O4m2i.js} +24 -11
- 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-DHiWBa-C.js} +7 -4
- package/dist/index.d.ts +14 -14
- package/dist/index.js +12 -12
- 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-Da2Kavba.js +221 -0
- package/dist/pipeline-HiA3DC71.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-B9ZGJI3-.d.ts} +21 -2
- package/dist/render/blend.d.ts +1 -1
- package/dist/render/blend.js +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-nbMCw1Pl.d.ts → renderer-B20ssjRm.d.ts} +23 -6
- package/dist/{renderer-LDhBNHcl.js → renderer-gQW_I7YG.js} +146 -63
- 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-B0qPw3zf.js +209 -0
- package/dist/{run-BlSZw9Qk.d.ts → run-CT3VtFqg.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-BH9l0ND0.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
package/README.md
CHANGED
|
@@ -75,6 +75,11 @@ Benchmarked against PHP renderchest v5.1.0 on the same machine, rendering the sa
|
|
|
75
75
|
| renderchest (docker, 8 workers) | 210s | 1,573 | 33 | ~449/min |
|
|
76
76
|
| rendermite (one Node process, pool 8) | 22.9s | 2,065 | 0 | ~5,410/min |
|
|
77
77
|
|
|
78
|
+
That comparison is a fixed point in time; rendermite has since roughly halved its own wall
|
|
79
|
+
time again (worker threads, a faster compositor, bounded animations). Run `npm run bench` for
|
|
80
|
+
current numbers on your machine — CI runs the same suite and comments the delta on pull
|
|
81
|
+
requests.
|
|
82
|
+
|
|
78
83
|
## Testing
|
|
79
84
|
|
|
80
85
|
```sh
|
package/dist/atlas/registry.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as ResourceLocator } from "../locator-jDbdUeTd.js";
|
|
2
|
-
import { a as ResourceManager } from "../manager-
|
|
3
|
-
import { r as Texture } from "../loader-
|
|
4
|
-
import { t as AtlasResolver } from "../sources-
|
|
2
|
+
import { a as ResourceManager } from "../manager-Cv1NeFyF.js";
|
|
3
|
+
import { r as Texture } from "../loader-Myy22TDL.js";
|
|
4
|
+
import { t as AtlasResolver } from "../sources-B9X9w3qP.js";
|
|
5
5
|
//#region src/atlas/registry.d.ts
|
|
6
6
|
declare function loadAtlasResolver(resources: ResourceManager, loadTexture: (locator: ResourceLocator) => Promise<Texture | null>, onWarning?: (message: string) => void): Promise<AtlasResolver>;
|
|
7
7
|
//#endregion
|
package/dist/atlas/sources.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as HasTexture, c as SingleAtlasSource, d as parseAtlasSources, i as DirectoryAtlasSource, l as UnstitchAtlasSource, n as AtlasSource, o as PalettedPermutationsSource, r as AtlasSourceError, s as ResolvedSprite, t as AtlasResolver, u as createAtlasResolver } from "../sources-
|
|
1
|
+
import { a as HasTexture, c as SingleAtlasSource, d as parseAtlasSources, i as DirectoryAtlasSource, l as UnstitchAtlasSource, n as AtlasSource, o as PalettedPermutationsSource, r as AtlasSourceError, s as ResolvedSprite, t as AtlasResolver, u as createAtlasResolver } from "../sources-B9X9w3qP.js";
|
|
2
2
|
export { AtlasResolver, AtlasSource, AtlasSourceError, DirectoryAtlasSource, HasTexture, PalettedPermutationsSource, ResolvedSprite, SingleAtlasSource, UnstitchAtlasSource, createAtlasResolver, parseAtlasSources };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as Texture } from "./loader-
|
|
1
|
+
import { r as Texture } from "./loader-Myy22TDL.js";
|
|
2
2
|
//#region src/render/blend.d.ts
|
|
3
3
|
/** RGBA float pixels for render-time work; converted back to 8-bit at encode. */
|
|
4
4
|
interface FloatTexture {
|
|
@@ -19,7 +19,8 @@ declare function q16(value: number): number;
|
|
|
19
19
|
/**
|
|
20
20
|
* Source-over with straight alpha, in place on `dst` at `offset`:
|
|
21
21
|
* ra = 1-(1-sa)(1-da); rc = (sc*sa + dc*da*(1-sa)) / ra; rgb = 0 when ra = 0.
|
|
22
|
-
* Results are Q16-quantized like every Imagick pixel write.
|
|
22
|
+
* Results are Q16-quantized like every Imagick pixel write. q16 is inlined
|
|
23
|
+
* here because blendPixel is the single hottest function in the pipeline.
|
|
23
24
|
*/
|
|
24
25
|
declare function blendPixel(dst: Float64Array, offset: number, sr: number, sg: number, sb: number, sa: number): void;
|
|
25
26
|
/**
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
//#region src/render/blend.ts
|
|
2
|
+
/** Lookup table for Q16 → 8-bit conversion (ImageMagick's floor(q/257)). */
|
|
3
|
+
const BYTE_LUT = /* @__PURE__ */ new Uint8Array(65536);
|
|
2
4
|
function toFloatTexture(texture) {
|
|
3
5
|
const data = new Float64Array(texture.data.length);
|
|
4
6
|
for (let i = 0; i < texture.data.length; i++) data[i] = texture.data[i] / 255;
|
|
@@ -10,9 +12,11 @@ function toFloatTexture(texture) {
|
|
|
10
12
|
}
|
|
11
13
|
function toByteTexture(texture) {
|
|
12
14
|
const data = new Uint8ClampedArray(texture.data.length);
|
|
13
|
-
for (let
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
if (BYTE_LUT[65535] === 0) for (let q = 0; q < 65536; q++) BYTE_LUT[q] = Math.floor(q / 257);
|
|
16
|
+
const src = texture.data;
|
|
17
|
+
for (let i = 0; i < src.length; i++) {
|
|
18
|
+
const value = src[i];
|
|
19
|
+
data[i] = BYTE_LUT[Math.round((value < 0 ? 0 : value > 1 ? 1 : value) * 65535)];
|
|
16
20
|
}
|
|
17
21
|
return {
|
|
18
22
|
width: texture.width,
|
|
@@ -38,7 +42,8 @@ function q16(value) {
|
|
|
38
42
|
/**
|
|
39
43
|
* Source-over with straight alpha, in place on `dst` at `offset`:
|
|
40
44
|
* ra = 1-(1-sa)(1-da); rc = (sc*sa + dc*da*(1-sa)) / ra; rgb = 0 when ra = 0.
|
|
41
|
-
* Results are Q16-quantized like every Imagick pixel write.
|
|
45
|
+
* Results are Q16-quantized like every Imagick pixel write. q16 is inlined
|
|
46
|
+
* here because blendPixel is the single hottest function in the pipeline.
|
|
42
47
|
*/
|
|
43
48
|
function blendPixel(dst, offset, sr, sg, sb, sa) {
|
|
44
49
|
const da = dst[offset + 3];
|
|
@@ -51,10 +56,17 @@ function blendPixel(dst, offset, sr, sg, sb, sa) {
|
|
|
51
56
|
return;
|
|
52
57
|
}
|
|
53
58
|
const dstWeight = da * (1 - sa);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
dst[offset
|
|
57
|
-
dst[offset +
|
|
59
|
+
let v = (sr * sa + dst[offset] * dstWeight) / ra;
|
|
60
|
+
v = v < 0 ? 0 : v > 1 ? 1 : v;
|
|
61
|
+
dst[offset] = Math.round(v * 65535) / 65535;
|
|
62
|
+
v = (sg * sa + dst[offset + 1] * dstWeight) / ra;
|
|
63
|
+
v = v < 0 ? 0 : v > 1 ? 1 : v;
|
|
64
|
+
dst[offset + 1] = Math.round(v * 65535) / 65535;
|
|
65
|
+
v = (sb * sa + dst[offset + 2] * dstWeight) / ra;
|
|
66
|
+
v = v < 0 ? 0 : v > 1 ? 1 : v;
|
|
67
|
+
dst[offset + 2] = Math.round(v * 65535) / 65535;
|
|
68
|
+
v = ra < 0 ? 0 : ra > 1 ? 1 : ra;
|
|
69
|
+
dst[offset + 3] = Math.round(v * 65535) / 65535;
|
|
58
70
|
}
|
|
59
71
|
/**
|
|
60
72
|
* Upstream tintImage: uses ONLY the red channel as luminance — per pixel,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/resources/cache.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
|
+
var LruCache = class {
|
|
8
|
+
entries = /* @__PURE__ */ new Map();
|
|
9
|
+
maxEntries;
|
|
10
|
+
weigh;
|
|
11
|
+
maxWeight;
|
|
12
|
+
weights = /* @__PURE__ */ new Map();
|
|
13
|
+
weight = 0;
|
|
14
|
+
constructor(maxEntries, options = {}) {
|
|
15
|
+
this.maxEntries = Math.max(1, maxEntries);
|
|
16
|
+
this.maxWeight = options.maxWeight ?? Infinity;
|
|
17
|
+
this.weigh = options.weigh ?? (() => 0);
|
|
18
|
+
}
|
|
19
|
+
evict() {
|
|
20
|
+
while (this.entries.size > this.maxEntries || this.weight > this.maxWeight && this.entries.size > 1) {
|
|
21
|
+
const oldest = this.entries.keys().next();
|
|
22
|
+
if (oldest.done) return;
|
|
23
|
+
this.weight -= this.weights.get(oldest.value) ?? 0;
|
|
24
|
+
this.weights.delete(oldest.value);
|
|
25
|
+
this.entries.delete(oldest.value);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
get size() {
|
|
29
|
+
return this.entries.size;
|
|
30
|
+
}
|
|
31
|
+
get(key) {
|
|
32
|
+
const value = this.entries.get(key);
|
|
33
|
+
if (value !== void 0) {
|
|
34
|
+
this.entries.delete(key);
|
|
35
|
+
this.entries.set(key, value);
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
set(key, value) {
|
|
40
|
+
if (this.entries.has(key)) {
|
|
41
|
+
this.weight -= this.weights.get(key) ?? 0;
|
|
42
|
+
this.entries.delete(key);
|
|
43
|
+
}
|
|
44
|
+
this.entries.set(key, value);
|
|
45
|
+
this.weights.set(key, 0);
|
|
46
|
+
this.evict();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Record a value's real cost once it is known. Loaders cache promises, so the
|
|
50
|
+
* weight of a decoded texture only exists after it resolves.
|
|
51
|
+
*/
|
|
52
|
+
settle(key, value) {
|
|
53
|
+
if (!this.entries.has(key)) return;
|
|
54
|
+
const size = this.weigh(value);
|
|
55
|
+
this.weight += size - (this.weights.get(key) ?? 0);
|
|
56
|
+
this.weights.set(key, size);
|
|
57
|
+
this.evict();
|
|
58
|
+
}
|
|
59
|
+
clear() {
|
|
60
|
+
this.entries.clear();
|
|
61
|
+
this.weights.clear();
|
|
62
|
+
this.weight = 0;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
//#endregion
|
|
66
|
+
export { LruCache as t };
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { n as builtinPackPath, t as bedCompatPath } from "./builtin-caYjESh0.js";
|
|
3
|
-
import { n as LayeredResourceManager, t as FolderResourceManager } from "./manager-
|
|
4
|
-
import { n as harvestTintOverrides, t as curatedTintOverrides } from "./tints-
|
|
5
|
-
import { t as JarResourceManager } from "./jar-
|
|
6
|
-
import { t as run } from "./run-
|
|
3
|
+
import { n as LayeredResourceManager, t as FolderResourceManager } from "./manager-CkletR5K.js";
|
|
4
|
+
import { n as harvestTintOverrides, t as curatedTintOverrides } from "./tints-B8UJmZMu.js";
|
|
5
|
+
import { t as JarResourceManager } from "./jar-D9z-X7tA.js";
|
|
6
|
+
import { t as run } from "./run-B0qPw3zf.js";
|
|
7
7
|
import { t as downloadClientJar } from "./download-D9aARBFp.js";
|
|
8
|
-
import { a as prepareVersionResources, i as eraForVersion, n as GEOMETRY_DONOR_VERSIONS } from "./prepare-
|
|
8
|
+
import { a as prepareVersionResources, i as eraForVersion, n as GEOMETRY_DONOR_VERSIONS } from "./prepare-Cc8QZ3j0.js";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import { parseArgs } from "node:util";
|
|
@@ -88,6 +88,10 @@ const { values } = parseArgs({ options: {
|
|
|
88
88
|
"builtin": { type: "string" },
|
|
89
89
|
"cache-dir": { type: "string" },
|
|
90
90
|
"concurrency": { type: "string" },
|
|
91
|
+
"workers": {
|
|
92
|
+
type: "string",
|
|
93
|
+
short: "w"
|
|
94
|
+
},
|
|
91
95
|
"help": {
|
|
92
96
|
type: "boolean",
|
|
93
97
|
short: "h",
|
|
@@ -122,7 +126,8 @@ Options:
|
|
|
122
126
|
--lighting-profile modern (default) | legacy (matches published corpus)
|
|
123
127
|
--builtin <dir> override the builtin pack (defaults per era)
|
|
124
128
|
--cache-dir <dir> jar download cache (default ~/.rendermite)
|
|
125
|
-
--concurrency <n> in-flight items (default 8)
|
|
129
|
+
--concurrency <n> in-flight items per thread (default 8)
|
|
130
|
+
-w, --workers <n> render threads (default: one per core, less one)
|
|
126
131
|
`);
|
|
127
132
|
process.exit(values.help ? 0 : 1);
|
|
128
133
|
}
|
|
@@ -188,6 +193,7 @@ const result = await run({
|
|
|
188
193
|
gif: values.gif,
|
|
189
194
|
avif: values.avif,
|
|
190
195
|
concurrency: values.concurrency ? Number(values.concurrency) : void 0,
|
|
196
|
+
workers: values.workers ? Number(values.workers) : void 0,
|
|
191
197
|
lightingProfile: values["lighting-profile"] === "legacy" ? "legacy" : "modern",
|
|
192
198
|
itemList: values["item-list"],
|
|
193
199
|
onProgress: (done, total, id, error) => {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { t as blendPixel } from "./blend-D0XWgVQr.js";
|
|
2
|
+
//#region src/render/compose.ts
|
|
3
|
+
/**
|
|
4
|
+
* Per-pixel painter's compositing, mirroring upstream Model::combineFaceImages:
|
|
5
|
+
* at each pixel, faces sorted ascending by that pixel's depth value (0 where the
|
|
6
|
+
* rasterizer never wrote depth — the overdraw band relies on sorting beneath),
|
|
7
|
+
* then source-over blended in that order when alpha > 0.
|
|
8
|
+
*
|
|
9
|
+
* Upstream re-sorts one persistent array, so each pixel's sort is really a
|
|
10
|
+
* stable partition: faces the pixel misses keep their order, faces with depth
|
|
11
|
+
* move to the end in depth order. That reordering is invariant under taking
|
|
12
|
+
* subsets, so instead of touching every face at every pixel we keep a rank per
|
|
13
|
+
* face (a moved face gets a fresh, higher rank) and visit only the faces whose
|
|
14
|
+
* bounding box contains the pixel. Same order, same pixels, sublinear in faces.
|
|
15
|
+
*/
|
|
16
|
+
function combineFaceBuffers(width, height, faces) {
|
|
17
|
+
const out = new Float64Array(width * height * 4);
|
|
18
|
+
const count = faces.length;
|
|
19
|
+
if (count === 0) return {
|
|
20
|
+
width,
|
|
21
|
+
height,
|
|
22
|
+
data: out
|
|
23
|
+
};
|
|
24
|
+
const rowCounts = new Int32Array(height + 1);
|
|
25
|
+
for (const face of faces) {
|
|
26
|
+
const endY = Math.min(face.y + face.height, height);
|
|
27
|
+
for (let y = Math.max(face.y, 0); y < endY; y++) rowCounts[y]++;
|
|
28
|
+
}
|
|
29
|
+
const rowStart = new Int32Array(height + 1);
|
|
30
|
+
for (let y = 0; y < height; y++) rowStart[y + 1] = rowStart[y] + rowCounts[y];
|
|
31
|
+
const cursor = [...rowStart];
|
|
32
|
+
const rowFaces = new Int32Array(rowStart[height]);
|
|
33
|
+
for (let index = 0; index < count; index++) {
|
|
34
|
+
const face = faces[index];
|
|
35
|
+
const endY = Math.min(face.y + face.height, height);
|
|
36
|
+
for (let y = Math.max(face.y, 0); y < endY; y++) rowFaces[cursor[y]++] = index;
|
|
37
|
+
}
|
|
38
|
+
const rank = new Float64Array(count);
|
|
39
|
+
for (let index = 0; index < count; index++) rank[index] = index;
|
|
40
|
+
let nextRank = count;
|
|
41
|
+
const flat = new Int32Array(count);
|
|
42
|
+
const flatRank = new Float64Array(count);
|
|
43
|
+
const deep = new Int32Array(count);
|
|
44
|
+
const deepDepth = new Float64Array(count);
|
|
45
|
+
const deepRank = new Float64Array(count);
|
|
46
|
+
for (let y = 0; y < height; y++) {
|
|
47
|
+
const start = rowStart[y];
|
|
48
|
+
const end = rowStart[y + 1];
|
|
49
|
+
if (start === end) continue;
|
|
50
|
+
for (let x = 0; x < width; x++) {
|
|
51
|
+
let flatCount = 0;
|
|
52
|
+
let deepCount = 0;
|
|
53
|
+
for (let slot = start; slot < end; slot++) {
|
|
54
|
+
const index = rowFaces[slot];
|
|
55
|
+
const face = faces[index];
|
|
56
|
+
const localX = x - face.x;
|
|
57
|
+
if (localX < 0 || localX >= face.width) continue;
|
|
58
|
+
const localPixel = (y - face.y) * face.width + localX;
|
|
59
|
+
const depth = face.depths[localPixel];
|
|
60
|
+
if (depth > 0) {
|
|
61
|
+
let slotIndex = deepCount++;
|
|
62
|
+
while (slotIndex > 0 && (deepDepth[slotIndex - 1] > depth || deepDepth[slotIndex - 1] === depth && deepRank[slotIndex - 1] > rank[index])) {
|
|
63
|
+
deep[slotIndex] = deep[slotIndex - 1];
|
|
64
|
+
deepDepth[slotIndex] = deepDepth[slotIndex - 1];
|
|
65
|
+
deepRank[slotIndex] = deepRank[slotIndex - 1];
|
|
66
|
+
slotIndex--;
|
|
67
|
+
}
|
|
68
|
+
deep[slotIndex] = index;
|
|
69
|
+
deepDepth[slotIndex] = depth;
|
|
70
|
+
deepRank[slotIndex] = rank[index];
|
|
71
|
+
} else {
|
|
72
|
+
let slotIndex = flatCount++;
|
|
73
|
+
while (slotIndex > 0 && flatRank[slotIndex - 1] > rank[index]) {
|
|
74
|
+
flat[slotIndex] = flat[slotIndex - 1];
|
|
75
|
+
flatRank[slotIndex] = flatRank[slotIndex - 1];
|
|
76
|
+
slotIndex--;
|
|
77
|
+
}
|
|
78
|
+
flat[slotIndex] = index;
|
|
79
|
+
flatRank[slotIndex] = rank[index];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (flatCount === 0 && deepCount === 0) continue;
|
|
83
|
+
const offset = (y * width + x) * 4;
|
|
84
|
+
for (let i = 0; i < flatCount; i++) blendFace(out, offset, faces[flat[i]], x, y);
|
|
85
|
+
for (let i = 0; i < deepCount; i++) {
|
|
86
|
+
const index = deep[i];
|
|
87
|
+
blendFace(out, offset, faces[index], x, y);
|
|
88
|
+
rank[index] = nextRank++;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
width,
|
|
94
|
+
height,
|
|
95
|
+
data: out
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function blendFace(out, offset, face, x, y) {
|
|
99
|
+
const faceOffset = ((y - face.y) * face.width + (x - face.x)) * 4;
|
|
100
|
+
const alpha = face.colors[faceOffset + 3];
|
|
101
|
+
if (alpha > 0) blendPixel(out, offset, face.colors[faceOffset], face.colors[faceOffset + 1], face.colors[faceOffset + 2], alpha);
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { combineFaceBuffers as t };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { n as LayeredResourceManager, t as FolderResourceManager } from "./manager-CkletR5K.js";
|
|
2
|
+
import { n as MapResourceManager, t as KindFilteredResourceManager } from "./synthetic-ZAUjXT7E.js";
|
|
3
|
+
import { t as JarResourceManager } from "./jar-D9z-X7tA.js";
|
|
4
|
+
//#region src/resources/descriptor.ts
|
|
5
|
+
/**
|
|
6
|
+
* Serializable descriptions of a resource tree, so the same layered tree can be
|
|
7
|
+
* rebuilt inside a worker thread. Managers describe themselves; a tree
|
|
8
|
+
* containing anything that cannot (a consumer's own ResourceManager) describes
|
|
9
|
+
* as null, and the caller renders in-process rather than silently dropping
|
|
10
|
+
* layers.
|
|
11
|
+
*/
|
|
12
|
+
function describeResources(manager) {
|
|
13
|
+
const describable = manager;
|
|
14
|
+
return typeof describable.describe === "function" ? describable.describe() : null;
|
|
15
|
+
}
|
|
16
|
+
function restoreResources(descriptor) {
|
|
17
|
+
switch (descriptor.kind) {
|
|
18
|
+
case "folder": return new FolderResourceManager(descriptor.root);
|
|
19
|
+
case "jar": return new JarResourceManager(descriptor.path);
|
|
20
|
+
case "map": return MapResourceManager.fromEntries(descriptor.entries);
|
|
21
|
+
case "layered": return new LayeredResourceManager(descriptor.layers.map((layer) => restoreResources(layer)));
|
|
22
|
+
case "kindFiltered": return new KindFilteredResourceManager(restoreResources(descriptor.inner), descriptor.kinds);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { restoreResources as n, describeResources as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ResourceManager } from "./manager-
|
|
1
|
+
import { a as ResourceManager } from "./manager-Cv1NeFyF.js";
|
|
2
2
|
import { i as ItemRenderInstruction, r as ItemProperties } from "./definition-BwX6Xxv0.js";
|
|
3
3
|
//#region src/items/discovery.d.ts
|
|
4
4
|
declare class ItemResolutionError extends Error {}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as TRIM_DARKER_MATERIALS, i as POTTERY_SHERDS, n as DynamicItem, o as TRIM_MATERIALS, r as DynamicResources, s as builtinDynamicResources, t as ARMOR_ITEM_TYPES } from "../generators-
|
|
1
|
+
import { a as TRIM_DARKER_MATERIALS, i as POTTERY_SHERDS, n as DynamicItem, o as TRIM_MATERIALS, r as DynamicResources, s as builtinDynamicResources, t as ARMOR_ITEM_TYPES } from "../generators-DhRov4RB.js";
|
|
2
2
|
export { ARMOR_ITEM_TYPES, DynamicItem, DynamicResources, POTTERY_SHERDS, TRIM_DARKER_MATERIALS, TRIM_MATERIALS, builtinDynamicResources };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as builtinDynamicResources, i as TRIM_MATERIALS, n as POTTERY_SHERDS, r as TRIM_DARKER_MATERIALS, t as ARMOR_ITEM_TYPES } from "../generators-
|
|
1
|
+
import { a as builtinDynamicResources, i as TRIM_MATERIALS, n as POTTERY_SHERDS, r as TRIM_DARKER_MATERIALS, t as ARMOR_ITEM_TYPES } from "../generators-BlG4KmtP.js";
|
|
2
2
|
export { ARMOR_ITEM_TYPES, POTTERY_SHERDS, TRIM_DARKER_MATERIALS, TRIM_MATERIALS, builtinDynamicResources };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as toByteTexture } from "./blend-
|
|
1
|
+
import { o as toByteTexture } from "./blend-D0XWgVQr.js";
|
|
2
2
|
import sharp from "sharp";
|
|
3
3
|
//#region src/output/encode.ts
|
|
4
4
|
/**
|
|
@@ -6,6 +6,17 @@ import sharp from "sharp";
|
|
|
6
6
|
* separate optimization step is needed: lossless everywhere, strictly-smaller
|
|
7
7
|
* candidates only.
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* Lossless WebP effort for single-frame icons. Measured across 1.12.2–1.21.8
|
|
11
|
+
* vanilla, effort 2 is 0.1–1.9% SMALLER than effort 6 for static icons at 2.8×
|
|
12
|
+
* the speed — libvips' higher efforts search predictors that rarely pay off on
|
|
13
|
+
* 64px sprites. Animations are the opposite (up to 21% larger on one item), so
|
|
14
|
+
* they keep effort 6 below.
|
|
15
|
+
*/
|
|
16
|
+
const STATIC_WEBP_EFFORT = 2;
|
|
17
|
+
const ANIMATED_WEBP_EFFORT = 6;
|
|
18
|
+
/** Frames encoded concurrently when assembling an animation's pages. */
|
|
19
|
+
const PAGE_ENCODE_BATCH = 8;
|
|
9
20
|
/** Losslessly transcode an existing PNG resource into the formats referenced by generated CSS. */
|
|
10
21
|
async function encodeRasterAsset(source, formats) {
|
|
11
22
|
const image = sharp(source);
|
|
@@ -37,26 +48,35 @@ async function encodeRasterAsset(source, formats) {
|
|
|
37
48
|
async function encodeAnimatedIcon(frames, delaysMs, options = {}) {
|
|
38
49
|
if (frames.length === 1) return encodeIcon(frames[0], options);
|
|
39
50
|
const { width, height } = frames[0];
|
|
40
|
-
const pages =
|
|
51
|
+
const pages = [];
|
|
52
|
+
for (let start = 0; start < frames.length; start += PAGE_ENCODE_BATCH) pages.push(...await Promise.all(frames.slice(start, start + PAGE_ENCODE_BATCH).map((frame) => {
|
|
41
53
|
const bytes = toByteTexture(frame);
|
|
42
54
|
return sharp(Buffer.from(bytes.data.buffer, bytes.data.byteOffset, bytes.data.length), { raw: {
|
|
43
55
|
width,
|
|
44
56
|
height,
|
|
45
57
|
channels: 4
|
|
46
58
|
} }).png({ compressionLevel: 0 }).toBuffer();
|
|
47
|
-
}));
|
|
59
|
+
})));
|
|
48
60
|
const animated = () => sharp(pages, { join: { animated: true } });
|
|
49
|
-
const
|
|
50
|
-
if (options.webp !== false) result.webp = await animated().webp({
|
|
61
|
+
const webpPromise = options.webp !== false ? animated().webp({
|
|
51
62
|
lossless: true,
|
|
52
|
-
effort:
|
|
63
|
+
effort: ANIMATED_WEBP_EFFORT,
|
|
53
64
|
loop: 0,
|
|
54
65
|
delay: delaysMs
|
|
55
|
-
}).toBuffer();
|
|
56
|
-
|
|
66
|
+
}).toBuffer() : Promise.resolve(void 0);
|
|
67
|
+
const firstFramePromise = encodeIcon(frames[0], { webp: false });
|
|
68
|
+
const gifPromise = options.gif ? animated().gif({
|
|
57
69
|
loop: 0,
|
|
58
70
|
delay: delaysMs
|
|
59
|
-
}).toBuffer();
|
|
71
|
+
}).toBuffer() : Promise.resolve(void 0);
|
|
72
|
+
const [firstFrame, webp, gif] = await Promise.all([
|
|
73
|
+
firstFramePromise,
|
|
74
|
+
webpPromise,
|
|
75
|
+
gifPromise
|
|
76
|
+
]);
|
|
77
|
+
const result = { png: firstFrame.png };
|
|
78
|
+
if (webp) result.webp = webp;
|
|
79
|
+
if (gif) result.gif = gif;
|
|
60
80
|
if (options.avif) try {
|
|
61
81
|
result.avif = await animated().avif({
|
|
62
82
|
lossless: true,
|
|
@@ -89,19 +109,27 @@ async function encodeIcon(texture, options = {}) {
|
|
|
89
109
|
channels: 4
|
|
90
110
|
} });
|
|
91
111
|
const paletteSafe = countColors(bytes.data, 256) <= 256;
|
|
92
|
-
const
|
|
112
|
+
const pngPromise = raw.clone().png({
|
|
93
113
|
compressionLevel: 9,
|
|
94
114
|
effort: 10,
|
|
95
115
|
palette: paletteSafe
|
|
96
|
-
}).toBuffer() };
|
|
97
|
-
if (options.webp !== false) result.webp = await raw.clone().webp({
|
|
98
|
-
lossless: true,
|
|
99
|
-
effort: 6
|
|
100
116
|
}).toBuffer();
|
|
101
|
-
|
|
117
|
+
const webpPromise = options.webp !== false ? raw.clone().webp({
|
|
118
|
+
lossless: true,
|
|
119
|
+
effort: STATIC_WEBP_EFFORT
|
|
120
|
+
}).toBuffer() : Promise.resolve(void 0);
|
|
121
|
+
const avifPromise = options.avif ? raw.clone().avif({
|
|
102
122
|
lossless: true,
|
|
103
123
|
effort: 6
|
|
104
|
-
}).toBuffer();
|
|
124
|
+
}).toBuffer() : Promise.resolve(void 0);
|
|
125
|
+
const [png, webp, avif] = await Promise.all([
|
|
126
|
+
pngPromise,
|
|
127
|
+
webpPromise,
|
|
128
|
+
avifPromise
|
|
129
|
+
]);
|
|
130
|
+
const result = { png };
|
|
131
|
+
if (webp) result.webp = webp;
|
|
132
|
+
if (avif) result.avif = avif;
|
|
105
133
|
return result;
|
|
106
134
|
}
|
|
107
135
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
|
|
2
|
-
import { i as renderModel } from "./renderer-
|
|
2
|
+
import { i as renderModel } from "./renderer-gQW_I7YG.js";
|
|
3
3
|
//#region src/render/entity-fallback.ts
|
|
4
4
|
/**
|
|
5
5
|
* Heuristic rendering for mod items that parent `builtin/entity` — chests,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as FloatTexture } from "./blend-
|
|
1
|
+
import { t as FloatTexture } from "./blend-CEHoJ7tM.js";
|
|
2
2
|
import { t as Mat4 } from "./vector-DRKRi4--.js";
|
|
3
3
|
import { n as FaceGeometry } from "./geometry-BSVmKx5b.js";
|
|
4
4
|
import { r as LightSource } from "./lighting-3FGRC3Af.js";
|
|
5
|
-
import {
|
|
5
|
+
import { n as FaceBuffer } from "./rasterizer-B9ZGJI3-.js";
|
|
6
6
|
//#region src/render/face.d.ts
|
|
7
7
|
interface TintColor {
|
|
8
8
|
r: number;
|
|
@@ -17,8 +17,14 @@ interface RenderableFace {
|
|
|
17
17
|
/** Untransformed model-space max Z — upstream's stable pre-sort key. */
|
|
18
18
|
maxZ: number;
|
|
19
19
|
}
|
|
20
|
+
/** Tinted + darkened textures keyed by source texture, then by shading params. */
|
|
21
|
+
type FaceShadeCache = WeakMap<FloatTexture, Map<string, FloatTexture>>;
|
|
20
22
|
declare function faceMaxZ(geometry: FaceGeometry): number;
|
|
21
23
|
/** Returns null when the face is culled, degenerate, or has a zero-area UV. */
|
|
22
|
-
declare function renderFace(face: RenderableFace, width: number, height: number, transformation: Mat4
|
|
24
|
+
declare function renderFace(face: RenderableFace, width: number, height: number, transformation: Mat4,
|
|
25
|
+
/** Reused across a model's faces; cleared here, compacted before return. */
|
|
26
|
+
scratch?: FaceBuffer,
|
|
27
|
+
/** Shared tint/shade results for one model render. */
|
|
28
|
+
shadeCache?: FaceShadeCache): FaceBuffer | null;
|
|
23
29
|
//#endregion
|
|
24
|
-
export { renderFace as i,
|
|
30
|
+
export { renderFace as a, faceMaxZ as i, RenderableFace as n, TintColor as r, FaceShadeCache as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as normalize, g as transformVector, h as transformDirection, m as sub, r as dot, t as cross } from "./vector-CCxLae_T.js";
|
|
2
|
-
import { a as tintTexture, n as cloneFloatTexture, r as darkenTexture } from "./blend-
|
|
3
|
-
import { a as
|
|
2
|
+
import { a as tintTexture, n as cloneFloatTexture, r as darkenTexture } from "./blend-D0XWgVQr.js";
|
|
3
|
+
import { a as drawTexturedTriangle, i as createFaceBuffer, n as clearFaceBuffer, o as phpRound, r as compactFaceBuffer, t as RowCursor } from "./rasterizer-76qO8TDh.js";
|
|
4
4
|
//#region src/render/face.ts
|
|
5
5
|
/**
|
|
6
6
|
* Face rendering: cull, transform, project, tint, darken, rasterize — a faithful
|
|
@@ -18,7 +18,7 @@ function faceNormal(v0, v1, v2) {
|
|
|
18
18
|
return normalize(normal);
|
|
19
19
|
}
|
|
20
20
|
/** Returns null when the face is culled, degenerate, or has a zero-area UV. */
|
|
21
|
-
function renderFace(face, width, height, transformation) {
|
|
21
|
+
function renderFace(face, width, height, transformation, scratch, shadeCache) {
|
|
22
22
|
const { geometry } = face;
|
|
23
23
|
const uvWidth = geometry.uv2[0] - geometry.uv1[0];
|
|
24
24
|
const uvHeight = geometry.uv2[1] - geometry.uv1[1];
|
|
@@ -49,13 +49,25 @@ function renderFace(face, width, height, transformation) {
|
|
|
49
49
|
y: phpRound(height - ((v[1] - .5) * height + height / 2))
|
|
50
50
|
};
|
|
51
51
|
});
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
const absU1 = geometry.uv1[0] * face.texture.width;
|
|
53
|
+
const absV1 = geometry.uv1[1] * face.texture.height;
|
|
54
|
+
const absU2 = geometry.uv2[0] * face.texture.width;
|
|
55
|
+
const absV2 = geometry.uv2[1] * face.texture.height;
|
|
56
|
+
const tint = geometry.tintIndex !== null ? face.tint : null;
|
|
57
|
+
const darkness = 1 - face.light.lightLevelForNormal(shadeDirection);
|
|
58
|
+
const shadeKey = `${tint ? `${tint.r},${tint.g},${tint.b}` : "-"}|${darkness}|${absU1},${absV1},${absU2},${absV2}`;
|
|
59
|
+
let byKey = shadeCache?.get(face.texture);
|
|
60
|
+
let texture = byKey?.get(shadeKey);
|
|
61
|
+
if (!texture) {
|
|
62
|
+
texture = cloneFloatTexture(face.texture);
|
|
63
|
+
if (tint) tintTexture(texture, tint.r, tint.g, tint.b);
|
|
64
|
+
darkenTexture(texture, darkness, absU1, absV1, absU2, absV2);
|
|
65
|
+
if (shadeCache) {
|
|
66
|
+
byKey ??= /* @__PURE__ */ new Map();
|
|
67
|
+
byKey.set(shadeKey, texture);
|
|
68
|
+
shadeCache.set(face.texture, byKey);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
59
71
|
const uvs = [
|
|
60
72
|
[geometry.uv1[0], geometry.uv1[1]],
|
|
61
73
|
[geometry.uv1[0] + uvWidth, geometry.uv1[1]],
|
|
@@ -67,7 +79,8 @@ function renderFace(face, width, height, transformation) {
|
|
|
67
79
|
const last = uvs.pop();
|
|
68
80
|
if (last) uvs.unshift(last);
|
|
69
81
|
}
|
|
70
|
-
const buffer = createFaceBuffer(width, height);
|
|
82
|
+
const buffer = scratch ?? createFaceBuffer(width, height);
|
|
83
|
+
clearFaceBuffer(buffer);
|
|
71
84
|
const depths = [
|
|
72
85
|
linearDepth(v0[2]),
|
|
73
86
|
linearDepth(v1[2]),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as MapResourceManager } from "./synthetic-
|
|
1
|
+
import { n as MapResourceManager } from "./synthetic-Qk-BSUXO.js";
|
|
2
2
|
//#region src/dynamic/generators.d.ts
|
|
3
3
|
declare const TRIM_MATERIALS: readonly ["quartz", "iron", "gold", "diamond", "netherite", "redstone", "copper", "emerald", "lapis", "amethyst", "resin"];
|
|
4
4
|
/** Materials with a darker variant used when the trim matches the armor metal. */
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { n as displayMatrix } from "./vector-CCxLae_T.js";
|
|
2
2
|
import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
|
|
3
|
-
import { s as toFloatTexture } from "./blend-
|
|
4
|
-
import { t as combineFaceBuffers } from "./compose-
|
|
5
|
-
import {
|
|
3
|
+
import { s as toFloatTexture } from "./blend-D0XWgVQr.js";
|
|
4
|
+
import { t as combineFaceBuffers } from "./compose-lWPrB7wh.js";
|
|
5
|
+
import { i as createFaceBuffer } from "./rasterizer-76qO8TDh.js";
|
|
6
|
+
import { n as renderFace, t as faceMaxZ } from "./face-Cl1O4m2i.js";
|
|
6
7
|
import { c as lightsForProfile } from "./lighting--5myudVu.js";
|
|
7
8
|
//#region src/render/geo.ts
|
|
8
9
|
/**
|
|
@@ -322,8 +323,10 @@ async function renderGeo(geo, texture, textures, options) {
|
|
|
322
323
|
});
|
|
323
324
|
renderable.sort((a, b) => a.maxZ - b.maxZ);
|
|
324
325
|
const buffers = [];
|
|
326
|
+
const scratch = createFaceBuffer(renderSize, renderSize);
|
|
327
|
+
const shadeCache = /* @__PURE__ */ new WeakMap();
|
|
325
328
|
for (const face of renderable) {
|
|
326
|
-
const buffer = renderFace(face, renderSize, renderSize, transformation);
|
|
329
|
+
const buffer = renderFace(face, renderSize, renderSize, transformation, scratch, shadeCache);
|
|
327
330
|
if (buffer) buffers.push(buffer);
|
|
328
331
|
}
|
|
329
332
|
if (buffers.length === 0) return null;
|