@nodecraft/rendermite 1.1.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -2
- package/assets/builtin/renderchest/models/block/banner_standing.json +225 -0
- package/assets/builtin/renderchest/models/block/banner_wall.json +155 -0
- package/assets/builtin/renderchest/models/block/bed_foot.json +44 -0
- package/assets/builtin/renderchest/models/block/bed_head.json +44 -0
- package/assets/builtin/renderchest/models/block/chest_left.json +182 -0
- package/assets/builtin/renderchest/models/block/chest_right.json +182 -0
- package/assets/builtin/renderchest/models/block/copper_golem_statue.json +709 -0
- package/assets/builtin/renderchest/models/block/fluid.json +83 -0
- package/assets/builtin/renderchest/models/block/fluid_tinted.json +89 -0
- package/assets/builtin/renderchest/models/block/hanging_sign.json +54 -0
- package/assets/builtin/renderchest/models/block/standing_sign.json +32 -0
- package/assets/builtin/renderchest/models/block/wall_sign.json +19 -0
- package/dist/atlas/registry.js +1 -1
- package/dist/atlas/sources.js +1 -1
- package/dist/atlas-pack-CoOih7By.js +130 -0
- package/dist/atlas-pack-DRTrWcSU.d.ts +41 -0
- package/dist/block-entities-BbS1kv0m.js +481 -0
- package/dist/blocks/atlas-pack.d.ts +2 -0
- package/dist/blocks/atlas-pack.js +2 -0
- package/dist/blocks/block-entities.d.ts +93 -0
- package/dist/blocks/block-entities.js +2 -0
- package/dist/blocks/blockstate-eval.d.ts +2 -0
- package/dist/blocks/blockstate-eval.js +2 -0
- package/dist/blocks/cli.d.ts +9 -0
- package/dist/blocks/cli.js +2 -0
- package/dist/blocks/export.d.ts +2 -0
- package/dist/blocks/export.js +2 -0
- package/dist/blocks/obj-mesh.d.ts +2 -0
- package/dist/blocks/obj-mesh.js +2 -0
- package/dist/blocks/tints.d.ts +2 -0
- package/dist/blocks/tints.js +2 -0
- package/dist/{blockstate-CdstnW2-.js → blockstate-DVb8Mhxc.js} +1 -1
- package/dist/blockstate-eval-Ckjlyxn4.js +231 -0
- package/dist/blockstate-eval-DDArZs1P.d.ts +107 -0
- package/dist/cli-D7_oUuPT.js +161 -0
- package/dist/cli.js +9 -7
- package/dist/{descriptor-DWebtkI1.js → descriptor-BVIoEpPO.js} +3 -3
- package/dist/{discovery-B9aqV_Rz.js → discovery-CsZu08re.js} +1 -1
- package/dist/dynamic/generators.js +1 -1
- package/dist/{entity-fallback-Cu22uEIK.js → entity-fallback-DNO0Ta9C.js} +3 -155
- package/dist/entity-textures-DFRIVdF1.js +209 -0
- package/dist/export-BIpqNU3H.d.ts +44 -0
- package/dist/export-DxREy3qA.js +701 -0
- package/dist/{face-cQ-pVHoe.d.ts → face-BKwkeTps.d.ts} +1 -1
- package/dist/{fluid-bucket-BzoLofB_.js → fluid-bucket-B-I4ZYHl.js} +1 -1
- package/dist/{generators-BlG4KmtP.js → generators-BCZVlWig.js} +1 -1
- package/dist/{geo-w9HHIrb5.js → geo-BguldlU-.js} +5 -5
- package/dist/{geometry-w0yQbzDZ.d.ts → geometry-Bkur58Vi.d.ts} +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +21 -16
- package/dist/{item-styles-upgEO1yP.js → item-styles-DjYRdyHi.js} +1 -1
- package/dist/items/blockstate.js +1 -1
- package/dist/items/discovery.js +1 -1
- package/dist/{jar-D9z-X7tA.js → jar-5aLDI0nT.js} +2 -2
- package/dist/{legacy-reshape-C05SfAPR.js → legacy-reshape-CcdstKyI.js} +2 -2
- package/dist/{legacy-tints-B09J0Rxh.js → legacy-tints-l-541wgI.js} +1 -1
- package/dist/{loader-CWsTZgrk.js → loader-BvLcevlN.js} +100 -9
- package/dist/{loader-Lb0ZeILz.js → loader-C18GpurU.js} +1 -1
- package/dist/{loader-DD83d80u.d.ts → loader-C_1_amsq.d.ts} +18 -1
- package/dist/{locator-ycZzh_6z.js → locator-COMEtEn9.js} +23 -1
- package/dist/{manager-CkletR5K.js → manager-CYbAkWuT.js} +2 -2
- package/dist/models/loader.d.ts +1 -1
- package/dist/models/loader.js +1 -1
- package/dist/models/types.d.ts +1 -1
- package/dist/{models-BKxGkPS7.js → models-CQwcTZcg.js} +1 -1
- package/dist/mods/models.js +1 -1
- package/dist/mods/prepare.js +1 -1
- package/dist/mods/synthesize.js +1 -1
- package/dist/mods/tints.js +1 -1
- package/dist/{renderer-DB1Oe7xs.js → obj-BEjq3_Ao.js} +234 -234
- package/dist/obj-mesh-C6Sp4Ysc.d.ts +41 -0
- package/dist/obj-mesh-CiHiqrlm.js +97 -0
- package/dist/output/item-styles.js +1 -1
- package/dist/{pipeline-Bp6amYz_.d.ts → pipeline-BDvXcw20.d.ts} +1 -1
- package/dist/{pipeline-CgJaVpoL.js → pipeline-DRMsBiod.js} +14 -14
- package/dist/{prepare-D56Z-9cg.js → prepare-B8oPdGYz.js} +5 -5
- package/dist/{prepare-Cc8QZ3j0.js → prepare-C95vjt_o.js} +5 -5
- package/dist/{registry-DS5PZ57l.js → registry-DlduyjtM.js} +2 -2
- package/dist/render/entity-fallback.d.ts +3 -3
- package/dist/render/entity-fallback.js +1 -1
- package/dist/render/face.d.ts +1 -1
- package/dist/render/face.js +1 -1
- package/dist/render/fluid-bucket.js +1 -1
- package/dist/render/geo.d.ts +2 -2
- package/dist/render/geo.js +1 -1
- package/dist/render/geometry.d.ts +1 -1
- package/dist/render/legacy-tints.d.ts +1 -1
- package/dist/render/legacy-tints.js +1 -1
- package/dist/render/obj.d.ts +10 -3
- package/dist/render/obj.js +2 -2
- package/dist/render/pipeline.d.ts +1 -1
- package/dist/render/pipeline.js +1 -1
- package/dist/render/renderer.d.ts +1 -1
- package/dist/render/renderer.js +1 -1
- package/dist/render/tints.d.ts +1 -1
- package/dist/render/tints.js +1 -1
- package/dist/{renderer-C79EQDSn.d.ts → renderer-U20hnhbi.d.ts} +2 -2
- package/dist/resources/descriptor.js +1 -1
- package/dist/resources/entity-textures.d.ts +51 -0
- package/dist/resources/entity-textures.js +2 -0
- package/dist/resources/jar.js +1 -1
- package/dist/resources/locator.js +1 -1
- package/dist/resources/manager.js +1 -1
- package/dist/resources/synthetic.js +1 -1
- package/dist/{run-uq8_hUx9.d.ts → run-CvIqiZ6T.d.ts} +1 -1
- package/dist/{run-BPfpBZyZ.js → run-D2S_W5As.js} +10 -7
- package/dist/run.d.ts +1 -1
- package/dist/run.js +1 -1
- package/dist/{sources-Bxk-WOzH.js → sources-BlUriDCs.js} +1 -1
- package/dist/{synthesize-3Q0aaOnK.js → synthesize-jTjVjPAt.js} +2 -2
- package/dist/{synthetic-ZAUjXT7E.js → synthetic-rc9BPT4P.js} +0 -0
- package/dist/textures/loader.js +1 -1
- package/dist/tints-2k3ZhkY3.d.ts +41 -0
- package/dist/{tints-3VI_2SNy.js → tints-B3WQcPtx.js} +1 -1
- package/dist/{tints-Do9Sh3dg.d.ts → tints-DqhgFi8a.d.ts} +1 -1
- package/dist/tints-a9FWZLma.js +176 -0
- package/dist/{tints-CebmvjmP.js → tints-nxtGrqv5.js} +3 -3
- package/dist/{types-DpRSF9Vj.d.ts → types-qlp3I8_-.d.ts} +11 -0
- package/dist/versions/download.js +1 -1
- package/dist/versions/legacy-reshape.js +1 -1
- package/dist/versions/prepare.js +1 -1
- package/dist/workers/render-worker.d.ts +1 -1
- package/dist/workers/render-worker.js +2 -2
- package/package.json +1 -1
- package/dist/{download-D9aARBFp.js → download-1wAfrGKx.js} +1 -1
- package/dist/{face-CMJ3b3Ae.js → face-CKHboPfh.js} +1 -1
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import { n as ResourceLocator } from "./locator-COMEtEn9.js";
|
|
2
|
+
import { a as findChestTexture, c as findFluidStill, i as findBedTexture, l as findMobTexture, n as HEAD_LAYOUTS, o as findEntityTexture, r as coversSheet, s as findFluidSibling, t as DYE_COLORS } from "./entity-textures-DFRIVdF1.js";
|
|
3
|
+
//#region src/blocks/block-entities.ts
|
|
4
|
+
/**
|
|
5
|
+
* Geometry for blocks the game draws from Java rather than from a model.
|
|
6
|
+
*
|
|
7
|
+
* A chest's blockstate is `{"variants":{"":{"model":"block/chest"}}}` and that
|
|
8
|
+
* model has no elements: the shape, the facing, and the pairing of a double
|
|
9
|
+
* chest all live in a block entity renderer. Same for beds, banners, shulker
|
|
10
|
+
* boxes, skulls and signs — 137 of vanilla's 1,107 blocks. A world viewer
|
|
11
|
+
* reading only the assets would put a hole where every chest is.
|
|
12
|
+
*
|
|
13
|
+
* rendermite already vendors box geometry for most of these, because the icon
|
|
14
|
+
* pipeline has the same problem and solved it years ago (`src/builtin.ts`,
|
|
15
|
+
* `assets/builtin/`). This maps it onto blocks and synthesises the orientation
|
|
16
|
+
* variants vanilla's blockstate file does not carry — the consumer knows the
|
|
17
|
+
* block's real property map from the chunk, so it can pick.
|
|
18
|
+
*
|
|
19
|
+
* Everything here is a curated approximation, and the pack says so: each
|
|
20
|
+
* substituted block lands in the failures sidecar, and the emitted model is
|
|
21
|
+
* named `rendermite:block/…` so it is never mistaken for something a jar
|
|
22
|
+
* shipped.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Blocks that export empty because the game genuinely draws nothing for them,
|
|
26
|
+
* or draws something no model format can express. Kept apart from the blocks
|
|
27
|
+
* that came out empty by accident, so a consumer can tell a deliberate hole
|
|
28
|
+
* from a gap in this exporter.
|
|
29
|
+
*/
|
|
30
|
+
const INVISIBLE_BLOCKS = {
|
|
31
|
+
"minecraft:air": "nothing to draw",
|
|
32
|
+
"minecraft:cave_air": "nothing to draw",
|
|
33
|
+
"minecraft:void_air": "nothing to draw",
|
|
34
|
+
"minecraft:barrier": "only drawn while the player holds one",
|
|
35
|
+
"minecraft:light": "only drawn while the player holds one",
|
|
36
|
+
"minecraft:structure_void": "only drawn inside the structure block UI",
|
|
37
|
+
"minecraft:moving_piston": "a placeholder; the moving block is drawn by the piston",
|
|
38
|
+
"minecraft:end_portal": "a shader effect, not geometry",
|
|
39
|
+
"minecraft:end_gateway": "a shader effect, not geometry"
|
|
40
|
+
};
|
|
41
|
+
/** Clockwise from north, matching how a blockstate's `y` rotates a model. */
|
|
42
|
+
const FACING_DEGREES = {
|
|
43
|
+
north: 0,
|
|
44
|
+
east: 90,
|
|
45
|
+
south: 180,
|
|
46
|
+
west: 270
|
|
47
|
+
};
|
|
48
|
+
/** `facing6` for a shulker box: `up` is the shape as authored. */
|
|
49
|
+
const FACING6_ROTATION = {
|
|
50
|
+
up: {
|
|
51
|
+
x: 0,
|
|
52
|
+
y: 0
|
|
53
|
+
},
|
|
54
|
+
down: {
|
|
55
|
+
x: 180,
|
|
56
|
+
y: 0
|
|
57
|
+
},
|
|
58
|
+
north: {
|
|
59
|
+
x: 270,
|
|
60
|
+
y: 0
|
|
61
|
+
},
|
|
62
|
+
south: {
|
|
63
|
+
x: 90,
|
|
64
|
+
y: 0
|
|
65
|
+
},
|
|
66
|
+
east: {
|
|
67
|
+
x: 90,
|
|
68
|
+
y: 90
|
|
69
|
+
},
|
|
70
|
+
west: {
|
|
71
|
+
x: 90,
|
|
72
|
+
y: 270
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* The blockstate cases to emit for a substituted block. One per orientation the
|
|
77
|
+
* block can be placed in, so a consumer holding the real property map from the
|
|
78
|
+
* chunk finds the case the game would have used.
|
|
79
|
+
*/
|
|
80
|
+
function synthesizeVariants(orient) {
|
|
81
|
+
switch (orient.kind) {
|
|
82
|
+
case "fixed": return [{
|
|
83
|
+
when: {},
|
|
84
|
+
x: 0,
|
|
85
|
+
y: 0
|
|
86
|
+
}];
|
|
87
|
+
case "facing": {
|
|
88
|
+
const authored = FACING_DEGREES[orient.authored];
|
|
89
|
+
return Object.entries(FACING_DEGREES).map(([facing, degrees]) => {
|
|
90
|
+
return {
|
|
91
|
+
when: { facing },
|
|
92
|
+
x: 0,
|
|
93
|
+
y: (degrees - authored + 360) % 360
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
case "facing6": return Object.entries(FACING6_ROTATION).map(([facing, rotation]) => {
|
|
98
|
+
return {
|
|
99
|
+
when: { facing },
|
|
100
|
+
x: rotation.x,
|
|
101
|
+
y: rotation.y
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
case "rotation16": return Array.from({ length: 16 }, (_, step) => {
|
|
105
|
+
return {
|
|
106
|
+
when: { rotation: String(step) },
|
|
107
|
+
x: 0,
|
|
108
|
+
y: Math.round(step * 22.5)
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const CHEST_VARIANTS = {
|
|
114
|
+
"minecraft:chest": "chest",
|
|
115
|
+
"minecraft:trapped_chest": "trapped_chest",
|
|
116
|
+
"minecraft:ender_chest": "ender_chest",
|
|
117
|
+
"minecraft:copper_chest": "copper_chest",
|
|
118
|
+
"minecraft:exposed_copper_chest": "exposed_copper_chest",
|
|
119
|
+
"minecraft:weathered_copper_chest": "weathered_copper_chest",
|
|
120
|
+
"minecraft:oxidized_copper_chest": "oxidized_copper_chest",
|
|
121
|
+
"minecraft:waxed_copper_chest": "copper_chest",
|
|
122
|
+
"minecraft:waxed_exposed_copper_chest": "exposed_copper_chest",
|
|
123
|
+
"minecraft:waxed_weathered_copper_chest": "weathered_copper_chest",
|
|
124
|
+
"minecraft:waxed_oxidized_copper_chest": "oxidized_copper_chest"
|
|
125
|
+
};
|
|
126
|
+
/** Base entity sheet per chest variant, which the double halves suffix. */
|
|
127
|
+
const CHEST_SHEETS = {
|
|
128
|
+
chest: "minecraft:entity/chest/normal",
|
|
129
|
+
trapped_chest: "minecraft:entity/chest/trapped",
|
|
130
|
+
ender_chest: "minecraft:entity/chest/ender",
|
|
131
|
+
copper_chest: "minecraft:entity/chest/copper",
|
|
132
|
+
exposed_copper_chest: "minecraft:entity/chest/copper_exposed",
|
|
133
|
+
weathered_copper_chest: "minecraft:entity/chest/copper_weathered",
|
|
134
|
+
oxidized_copper_chest: "minecraft:entity/chest/copper_oxidized"
|
|
135
|
+
};
|
|
136
|
+
/** Where a skull's block name does not match the mobheads model's. */
|
|
137
|
+
const SKULL_MOBS = { dragon: "ender_dragon" };
|
|
138
|
+
const FLUID_SPRITES = {
|
|
139
|
+
"minecraft:water": {
|
|
140
|
+
sprite: "minecraft:block/water_still",
|
|
141
|
+
tinted: true
|
|
142
|
+
},
|
|
143
|
+
"minecraft:bubble_column": {
|
|
144
|
+
sprite: "minecraft:block/water_still",
|
|
145
|
+
tinted: true
|
|
146
|
+
},
|
|
147
|
+
"minecraft:lava": {
|
|
148
|
+
sprite: "minecraft:block/lava_still",
|
|
149
|
+
tinted: false
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const SHULKER_COLOURS = new Set(Object.keys(DYE_COLORS));
|
|
153
|
+
/**
|
|
154
|
+
* The vendored chest is authored with its latch on the south face, so a
|
|
155
|
+
* `facing=south` chest needs no rotation. Verified by rendering, not by
|
|
156
|
+
* reading the model.
|
|
157
|
+
*/
|
|
158
|
+
const CHEST_AUTHORED = {
|
|
159
|
+
kind: "facing",
|
|
160
|
+
authored: "south"
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Two chests placed side by side become one double chest: each keeps its own
|
|
164
|
+
* block but takes a `type` of `left` or `right`, and the game swaps in a wider
|
|
165
|
+
* box on a wider sheet so the pair reads as a single container with one lock on
|
|
166
|
+
* the seam. Drawing two singles instead leaves a visible join and two lids.
|
|
167
|
+
*
|
|
168
|
+
* The halves are only offered where the sheets exist — an ender chest never
|
|
169
|
+
* doubles and ships none — and the exporter drops a shape it cannot bind.
|
|
170
|
+
*/
|
|
171
|
+
async function chestGeometry(blockId, context) {
|
|
172
|
+
const [namespace, name] = blockId.split(":", 2);
|
|
173
|
+
const variant = CHEST_VARIANTS[blockId];
|
|
174
|
+
const searchable = /chest|crate/.test(name) && !name.includes("chestplate");
|
|
175
|
+
const found = variant === void 0 && searchable ? await findChestTexture(context.resources, namespace, name) : null;
|
|
176
|
+
const sheet = variant === void 0 ? found?.toString() : CHEST_SHEETS[variant];
|
|
177
|
+
if (sheet === void 0) return null;
|
|
178
|
+
const single = variant === void 0 ? {
|
|
179
|
+
model: "renderchest:item/chest",
|
|
180
|
+
textures: { texture: sheet }
|
|
181
|
+
} : { model: `renderchest:item/chest/${variant}` };
|
|
182
|
+
const [left, right] = await Promise.all([halfSheet(sheet, "left", context), halfSheet(sheet, "right", context)]);
|
|
183
|
+
if (left === null || right === null) return {
|
|
184
|
+
shapes: [single],
|
|
185
|
+
orient: CHEST_AUTHORED
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
shapes: [
|
|
189
|
+
{
|
|
190
|
+
...single,
|
|
191
|
+
when: { type: "single" }
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
model: "renderchest:block/chest_left",
|
|
195
|
+
when: { type: "left" },
|
|
196
|
+
textures: { texture: left }
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
model: "renderchest:block/chest_right",
|
|
200
|
+
when: { type: "right" },
|
|
201
|
+
textures: { texture: right }
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
orient: CHEST_AUTHORED
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* The wider sheet for one half of a double chest. Vanilla suffixes the base
|
|
209
|
+
* name (`normal` → `normal_left`); quark files the halves beside it instead
|
|
210
|
+
* (`acacia/normal` → `acacia/left`). Both are common enough to try.
|
|
211
|
+
*/
|
|
212
|
+
async function halfSheet(sheet, side, context) {
|
|
213
|
+
const base = ResourceLocator.parse(sheet);
|
|
214
|
+
const sibling = base.path.includes("/") ? `${base.path.slice(0, base.path.lastIndexOf("/"))}/${side}` : side;
|
|
215
|
+
for (const path of [`${base.path}_${side}`, sibling]) {
|
|
216
|
+
const candidate = new ResourceLocator(base.namespace, path);
|
|
217
|
+
if (await context.resources.hasResource("texture", candidate)) return candidate.toString();
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* A banner's colour is a tint over one greyscale sheet, not 16 textures, so the
|
|
223
|
+
* two forms share a model each and the tint table carries the dye.
|
|
224
|
+
*/
|
|
225
|
+
async function bannerGeometry(blockId, context) {
|
|
226
|
+
const [namespace, name] = blockId.split(":", 2);
|
|
227
|
+
const wall = name.endsWith("_wall_banner");
|
|
228
|
+
if (!wall && !name.endsWith("_banner")) return null;
|
|
229
|
+
const own = new ResourceLocator(namespace, "entity/banner/base");
|
|
230
|
+
const texture = namespace !== "minecraft" && await context.resources.hasResource("texture", own) ? own.toString() : "minecraft:entity/banner/base";
|
|
231
|
+
return {
|
|
232
|
+
shapes: [{ model: `renderchest:block/banner_${wall ? "wall" : "standing"}` }],
|
|
233
|
+
textures: { texture },
|
|
234
|
+
orient: wall ? {
|
|
235
|
+
kind: "facing",
|
|
236
|
+
authored: "south"
|
|
237
|
+
} : { kind: "rotation16" }
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* The vendored heads were authored against current vanilla, which files an
|
|
242
|
+
* entity sheet under its own mob folder (`entity/bat/bat`). Older versions keep
|
|
243
|
+
* them flat (`entity/bat`), so a binding that does not resolve is looked up
|
|
244
|
+
* again by name rather than left to export as magenta. The variable it is bound
|
|
245
|
+
* to is whatever the model called it, which is why the map is read rather than
|
|
246
|
+
* a name assumed.
|
|
247
|
+
*/
|
|
248
|
+
async function rebindMissingSheets(bindings, context, mob) {
|
|
249
|
+
const fixes = {};
|
|
250
|
+
for (const [name, value] of Object.entries(bindings)) {
|
|
251
|
+
const locator = ResourceLocator.tryParse(value);
|
|
252
|
+
if (locator === null || await context.resources.hasResource("texture", locator)) continue;
|
|
253
|
+
const leaf = locator.path.split("/").pop() ?? "";
|
|
254
|
+
const found = await findMobTexture(context.resources, locator.namespace, leaf) ?? await findMobTexture(context.resources, locator.namespace, mob);
|
|
255
|
+
if (found !== null) fixes[name] = found.toString();
|
|
256
|
+
}
|
|
257
|
+
return Object.keys(fixes).length > 0 ? fixes : void 0;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Skulls reuse the mobheads geometry the icon pipeline already vendors, which
|
|
261
|
+
* is centred in the block for an item's hand. A block wants it on the floor or
|
|
262
|
+
* against a wall, which is a translation rather than 90 more models.
|
|
263
|
+
*/
|
|
264
|
+
async function skullGeometry(blockId, context) {
|
|
265
|
+
const [namespace, name] = blockId.split(":", 2);
|
|
266
|
+
const match = /^(.+?)_(?:wall_)?(?:skull|head)$/.exec(name);
|
|
267
|
+
if (match === null) return null;
|
|
268
|
+
const mob = SKULL_MOBS[match[1]] ?? match[1];
|
|
269
|
+
const wall = name.includes("_wall_");
|
|
270
|
+
const place = (model, textures) => {
|
|
271
|
+
return {
|
|
272
|
+
shapes: [{
|
|
273
|
+
model,
|
|
274
|
+
textures,
|
|
275
|
+
translate: wall ? [
|
|
276
|
+
0,
|
|
277
|
+
0,
|
|
278
|
+
4
|
|
279
|
+
] : [
|
|
280
|
+
0,
|
|
281
|
+
-4,
|
|
282
|
+
0
|
|
283
|
+
]
|
|
284
|
+
}],
|
|
285
|
+
orient: wall ? {
|
|
286
|
+
kind: "facing",
|
|
287
|
+
authored: "north"
|
|
288
|
+
} : { kind: "rotation16" }
|
|
289
|
+
};
|
|
290
|
+
};
|
|
291
|
+
const parts = mob.split("_");
|
|
292
|
+
for (let keep = parts.length; keep >= 1 && keep >= parts.length - 2; keep--) {
|
|
293
|
+
const model = `mobheads:item/${parts.slice(0, keep).join("_")}`;
|
|
294
|
+
const resolved = await context.models.load(model).catch(() => null);
|
|
295
|
+
if (resolved !== null) return place(model, await rebindMissingSheets(resolved.textures, context, mob));
|
|
296
|
+
}
|
|
297
|
+
const sheet = await findMobTexture(context.resources, namespace, mob);
|
|
298
|
+
const skin = sheet === null ? null : await context.textures.load(sheet).catch(() => null);
|
|
299
|
+
const base = skin === null ? void 0 : HEAD_LAYOUTS[`${skin.width}x${skin.height}`];
|
|
300
|
+
return sheet !== null && base !== void 0 ? place(`mobheads:item/base/${base}`, { texture: sheet.toString() }) : null;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* 26.2's copper golem statue, which the game draws entirely from Java: an empty
|
|
304
|
+
* variant on a model with only a particle texture, and no entity geometry data
|
|
305
|
+
* in the jar either. `renderchest:block/copper_golem_statue` was authored from
|
|
306
|
+
* the sheet's own unwrap for it.
|
|
307
|
+
*
|
|
308
|
+
* `copper_golem_pose` picks between standing, sitting, running and star, which
|
|
309
|
+
* differ by limb rotations nothing in the assets records. Keying only on
|
|
310
|
+
* `facing` means every pose draws the standing statue rather than a hole.
|
|
311
|
+
*/
|
|
312
|
+
async function copperGolemGeometry(blockId, context) {
|
|
313
|
+
const name = blockId.split(":", 2)[1];
|
|
314
|
+
const match = /^(?:waxed_)?(exposed_|weathered_|oxidized_)?copper_golem_statue$/.exec(name);
|
|
315
|
+
if (match === null) return null;
|
|
316
|
+
const stage = match[1] === void 0 ? "" : `_${match[1].slice(0, -1)}`;
|
|
317
|
+
const sheet = new ResourceLocator("minecraft", `entity/copper_golem/copper_golem${stage}`);
|
|
318
|
+
if (!await context.resources.hasResource("texture", sheet)) return null;
|
|
319
|
+
return {
|
|
320
|
+
shapes: [{ model: "renderchest:block/copper_golem_statue" }],
|
|
321
|
+
textures: {
|
|
322
|
+
texture: sheet.toString(),
|
|
323
|
+
eyes: `minecraft:entity/copper_golem/copper_golem_eyes${stage}`
|
|
324
|
+
},
|
|
325
|
+
orient: {
|
|
326
|
+
kind: "facing",
|
|
327
|
+
authored: "north"
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* A fluid's surface height and flow direction come from its neighbours, which
|
|
333
|
+
* no pack can carry, so the block is a plain culled cube: right wherever the
|
|
334
|
+
* fluid is enclosed, two pixels tall at an open surface. The still and flow
|
|
335
|
+
* sprites are in the atlas for a consumer that meshes fluids properly.
|
|
336
|
+
*/
|
|
337
|
+
async function fluidGeometry(blockId, context) {
|
|
338
|
+
const [namespace, name] = blockId.split(":", 2);
|
|
339
|
+
const known = FLUID_SPRITES[blockId];
|
|
340
|
+
const found = known === void 0 ? await findFluidStill(context.resources, namespace, name) : null;
|
|
341
|
+
const still = known === void 0 ? found : ResourceLocator.parse(known.sprite);
|
|
342
|
+
if (still === null) return null;
|
|
343
|
+
const flow = await findFluidSibling(context.resources, still, ["_flow", "_flowing"]);
|
|
344
|
+
const overlay = await findFluidSibling(context.resources, still, ["_overlay"]);
|
|
345
|
+
return {
|
|
346
|
+
shapes: [{ model: `renderchest:block/fluid${known?.tinted ? "_tinted" : ""}` }],
|
|
347
|
+
textures: {
|
|
348
|
+
still: still.toString(),
|
|
349
|
+
flow: (flow ?? still).toString()
|
|
350
|
+
},
|
|
351
|
+
orient: { kind: "fixed" },
|
|
352
|
+
fluid: overlay === null ? {} : { overlay: overlay.toString() }
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
async function shulkerGeometry(blockId, context) {
|
|
356
|
+
const [namespace, name] = blockId.split(":", 2);
|
|
357
|
+
if (!name.includes("shulker") || name.includes("shell")) return null;
|
|
358
|
+
if (name === "shulker_box") return {
|
|
359
|
+
shapes: [{ model: "renderchest:item/shulker_box" }],
|
|
360
|
+
textures: { texture: "minecraft:entity/shulker/shulker" },
|
|
361
|
+
orient: { kind: "facing6" }
|
|
362
|
+
};
|
|
363
|
+
const colour = name.endsWith("_shulker_box") ? name.slice(0, -12) : null;
|
|
364
|
+
if (colour !== null && SHULKER_COLOURS.has(colour) && namespace === "minecraft") return {
|
|
365
|
+
shapes: [{ model: `renderchest:item/shulker_box/${name}` }],
|
|
366
|
+
orient: { kind: "facing6" }
|
|
367
|
+
};
|
|
368
|
+
const candidate = await findEntityTexture(context.resources, namespace, name, "shulker");
|
|
369
|
+
return {
|
|
370
|
+
shapes: [{
|
|
371
|
+
model: "renderchest:item/shulker_box",
|
|
372
|
+
textures: { texture: candidate !== null && await coversSheet(context.textures, candidate) ? candidate.toString() : "minecraft:entity/shulker/shulker" }
|
|
373
|
+
}],
|
|
374
|
+
orient: { kind: "facing6" }
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Signs are the largest family the game draws in code — 48 blocks in vanilla —
|
|
379
|
+
* and nothing was vendored for them, so `block/standing_sign` and
|
|
380
|
+
* `block/wall_sign` were authored against the entity sheet's own box unwrap.
|
|
381
|
+
*
|
|
382
|
+
* The wood is read off the block name rather than listed, so a mod that follows
|
|
383
|
+
* the same convention gets its signs for free; the exporter only substitutes
|
|
384
|
+
* once it has checked the texture is really there.
|
|
385
|
+
*/
|
|
386
|
+
async function signGeometry(blockId, context) {
|
|
387
|
+
const [namespace, name] = blockId.split(":", 2);
|
|
388
|
+
if (name !== "sign" && !name.endsWith("_sign")) return null;
|
|
389
|
+
let wood = name === "sign" ? "" : name.slice(0, -5);
|
|
390
|
+
const peel = (token) => {
|
|
391
|
+
if (wood !== token && !wood.endsWith(`_${token}`)) return false;
|
|
392
|
+
wood = wood.slice(0, Math.max(0, wood.length - token.length - 1));
|
|
393
|
+
return true;
|
|
394
|
+
};
|
|
395
|
+
const hanging = peel("hanging");
|
|
396
|
+
const wall = peel("wall");
|
|
397
|
+
const model = hanging ? "hanging_sign" : wall ? "wall_sign" : "standing_sign";
|
|
398
|
+
const conventional = new ResourceLocator(namespace, wood.length === 0 ? "entity/sign" : `entity/signs/${hanging ? "hanging/" : ""}${wood}`);
|
|
399
|
+
const found = await context.resources.hasResource("texture", conventional) ? conventional : await findEntityTexture(context.resources, namespace, `${wood}_${hanging ? "hanging_" : ""}sign`, "sign");
|
|
400
|
+
if (found === null) return null;
|
|
401
|
+
return {
|
|
402
|
+
shapes: [{ model: `renderchest:block/${model}` }],
|
|
403
|
+
textures: { texture: found.toString() },
|
|
404
|
+
orient: wall ? {
|
|
405
|
+
kind: "facing",
|
|
406
|
+
authored: "north"
|
|
407
|
+
} : { kind: "rotation16" }
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/** Every dye a bed comes in; the sheet is named for it. */
|
|
411
|
+
const BED_COLOURS = SHULKER_COLOURS;
|
|
412
|
+
/**
|
|
413
|
+
* A bed is two blocks with a model each, and `BedRenderer` draws both halves
|
|
414
|
+
* from one entity model spanning them. `part` picks the half and `facing`
|
|
415
|
+
* orients it, so the two multiply into eight cases.
|
|
416
|
+
*/
|
|
417
|
+
async function bedGeometry(blockId, context) {
|
|
418
|
+
const [namespace, name] = blockId.split(":", 2);
|
|
419
|
+
if (name !== "bed" && !name.endsWith("_bed")) return null;
|
|
420
|
+
const colour = name === "bed" ? "" : name.slice(0, -4);
|
|
421
|
+
const conventional = new ResourceLocator(namespace, `entity/bed/${colour}`);
|
|
422
|
+
const found = BED_COLOURS.has(colour) && await context.resources.hasResource("texture", conventional) ? conventional : await findBedTexture(context.resources, namespace, name);
|
|
423
|
+
if (found === null) return null;
|
|
424
|
+
return {
|
|
425
|
+
shapes: [{
|
|
426
|
+
when: { part: "head" },
|
|
427
|
+
model: "renderchest:block/bed_head"
|
|
428
|
+
}, {
|
|
429
|
+
when: { part: "foot" },
|
|
430
|
+
model: "renderchest:block/bed_foot"
|
|
431
|
+
}],
|
|
432
|
+
textures: { texture: found.toString() },
|
|
433
|
+
orient: {
|
|
434
|
+
kind: "facing",
|
|
435
|
+
authored: "north"
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
/** Blocks with one shape and nothing to orient by. */
|
|
440
|
+
const FIXED = {
|
|
441
|
+
"minecraft:conduit": {
|
|
442
|
+
shapes: [{ model: "renderchest:item/conduit" }],
|
|
443
|
+
orient: { kind: "fixed" }
|
|
444
|
+
},
|
|
445
|
+
"minecraft:decorated_pot": {
|
|
446
|
+
shapes: [{ model: "renderchest:item/decorated_pot" }],
|
|
447
|
+
textures: {
|
|
448
|
+
base: "minecraft:entity/decorated_pot/decorated_pot_base",
|
|
449
|
+
north: "minecraft:entity/decorated_pot/decorated_pot_side",
|
|
450
|
+
south: "minecraft:entity/decorated_pot/decorated_pot_side",
|
|
451
|
+
west: "minecraft:entity/decorated_pot/decorated_pot_side",
|
|
452
|
+
east: "minecraft:entity/decorated_pot/decorated_pot_side"
|
|
453
|
+
},
|
|
454
|
+
orient: { kind: "fixed" }
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
/**
|
|
458
|
+
* The vendored geometry for a block the game draws in code, or null when there
|
|
459
|
+
* is none and the block stays empty.
|
|
460
|
+
*/
|
|
461
|
+
async function blockEntityGeometry(blockId, context) {
|
|
462
|
+
const fixed = FIXED[blockId];
|
|
463
|
+
if (fixed !== void 0) return fixed;
|
|
464
|
+
const families = [
|
|
465
|
+
chestGeometry,
|
|
466
|
+
shulkerGeometry,
|
|
467
|
+
signGeometry,
|
|
468
|
+
bedGeometry,
|
|
469
|
+
bannerGeometry,
|
|
470
|
+
copperGolemGeometry,
|
|
471
|
+
skullGeometry,
|
|
472
|
+
fluidGeometry
|
|
473
|
+
];
|
|
474
|
+
for (const family of families) {
|
|
475
|
+
const geometry = await family(blockId, context);
|
|
476
|
+
if (geometry !== null) return geometry;
|
|
477
|
+
}
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
//#endregion
|
|
481
|
+
export { blockEntityGeometry as n, synthesizeVariants as r, INVISIBLE_BLOCKS as t };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { a as ResourceManager } from "../manager-Cv1NeFyF.js";
|
|
2
|
+
import { i as TextureLoader } from "../loader-Myy22TDL.js";
|
|
3
|
+
import { t as ModelLoader } from "../loader-C_1_amsq.js";
|
|
4
|
+
//#region src/blocks/block-entities.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* What the table is allowed to look at. The vanilla families are a lookup, but
|
|
7
|
+
* a mod's chest or mob head is only findable by searching its own assets, so
|
|
8
|
+
* the table needs the pack in hand rather than just the block's name.
|
|
9
|
+
*/
|
|
10
|
+
interface GeometryContext {
|
|
11
|
+
resources: ResourceManager;
|
|
12
|
+
textures: TextureLoader;
|
|
13
|
+
models: ModelLoader;
|
|
14
|
+
}
|
|
15
|
+
/** How a block's own properties turn into a rotation of the vendored model. */
|
|
16
|
+
type Orientation =
|
|
17
|
+
/** One shape, no property to read. */
|
|
18
|
+
{
|
|
19
|
+
kind: 'fixed';
|
|
20
|
+
} |
|
|
21
|
+
/**
|
|
22
|
+
* `facing` around the vertical axis. `authored` is the direction the
|
|
23
|
+
* vendored model already points, so the rest are offsets from it.
|
|
24
|
+
*/
|
|
25
|
+
{
|
|
26
|
+
kind: 'facing';
|
|
27
|
+
authored: 'north' | 'south' | 'east' | 'west';
|
|
28
|
+
} |
|
|
29
|
+
/** `facing` in all six directions, as a shulker box has. */
|
|
30
|
+
{
|
|
31
|
+
kind: 'facing6';
|
|
32
|
+
} |
|
|
33
|
+
/** `rotation` 0–15 in 22.5 degree steps, as a standing banner or skull has. */
|
|
34
|
+
{
|
|
35
|
+
kind: 'rotation16';
|
|
36
|
+
};
|
|
37
|
+
interface BlockEntityShape {
|
|
38
|
+
/** Vendored model drawn in place of the empty one. */
|
|
39
|
+
model: string;
|
|
40
|
+
/**
|
|
41
|
+
* A further property split, multiplied with the orientation. A bed is two
|
|
42
|
+
* blocks with a model each, told apart by `part`.
|
|
43
|
+
*/
|
|
44
|
+
when?: Record<string, string>;
|
|
45
|
+
/** Bindings for this shape alone, merged over the geometry's own. */
|
|
46
|
+
textures?: Record<string, string>;
|
|
47
|
+
/**
|
|
48
|
+
* Offset applied to the model's elements, in model units. Lets a skull reuse
|
|
49
|
+
* the mobheads geometry, which is centred for an item's hand rather than
|
|
50
|
+
* sat on the floor or hung on a wall.
|
|
51
|
+
*/
|
|
52
|
+
translate?: [number, number, number];
|
|
53
|
+
}
|
|
54
|
+
interface BlockEntityGeometry {
|
|
55
|
+
/** Usually one; more where a property picks between shapes. */
|
|
56
|
+
shapes: BlockEntityShape[];
|
|
57
|
+
/** Texture variables to bind where the vendored model leaves them open. */
|
|
58
|
+
textures?: Record<string, string>;
|
|
59
|
+
orient: Orientation;
|
|
60
|
+
/**
|
|
61
|
+
* Marks the substitute as a stand-in for a fluid, whose real surface the
|
|
62
|
+
* consumer has to mesh from neighbours. The emitted model says so, and names
|
|
63
|
+
* the sheets to mesh it with.
|
|
64
|
+
*/
|
|
65
|
+
fluid?: {
|
|
66
|
+
overlay?: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Blocks that export empty because the game genuinely draws nothing for them,
|
|
71
|
+
* or draws something no model format can express. Kept apart from the blocks
|
|
72
|
+
* that came out empty by accident, so a consumer can tell a deliberate hole
|
|
73
|
+
* from a gap in this exporter.
|
|
74
|
+
*/
|
|
75
|
+
declare const INVISIBLE_BLOCKS: Record<string, string>;
|
|
76
|
+
interface SynthesizedVariant {
|
|
77
|
+
when: Record<string, string>;
|
|
78
|
+
x: number;
|
|
79
|
+
y: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The blockstate cases to emit for a substituted block. One per orientation the
|
|
83
|
+
* block can be placed in, so a consumer holding the real property map from the
|
|
84
|
+
* chunk finds the case the game would have used.
|
|
85
|
+
*/
|
|
86
|
+
declare function synthesizeVariants(orient: Orientation): SynthesizedVariant[];
|
|
87
|
+
/**
|
|
88
|
+
* The vendored geometry for a block the game draws in code, or null when there
|
|
89
|
+
* is none and the block stays empty.
|
|
90
|
+
*/
|
|
91
|
+
declare function blockEntityGeometry(blockId: string, context: GeometryContext): Promise<BlockEntityGeometry | null>;
|
|
92
|
+
//#endregion
|
|
93
|
+
export { BlockEntityGeometry, BlockEntityShape, GeometryContext, INVISIBLE_BLOCKS, Orientation, SynthesizedVariant, blockEntityGeometry, synthesizeVariants };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as BlockStateMultipartCase, c as ParseBlockStateOptions, d as matchesCondition, f as parseBlockState, i as BlockStateError, l as collectModels, n as BlockProperties, o as BlockStateVariantCase, p as weightedVariantIndex, r as BlockStateDefinition, s as MultipartCondition, t as BlockModelVariant, u as evaluateBlockState } from "../blockstate-eval-DDArZs1P.js";
|
|
2
|
+
export { BlockModelVariant, BlockProperties, BlockStateDefinition, BlockStateError, BlockStateMultipartCase, BlockStateVariantCase, MultipartCondition, ParseBlockStateOptions, collectModels, evaluateBlockState, matchesCondition, parseBlockState, weightedVariantIndex };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as parseBlockState, i as matchesCondition, n as collectModels, o as weightedVariantIndex, r as evaluateBlockState, t as BlockStateError } from "../blockstate-eval-Ckjlyxn4.js";
|
|
2
|
+
export { BlockStateError, collectModels, evaluateBlockState, matchesCondition, parseBlockState, weightedVariantIndex };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/blocks/cli.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* `rendermite blocks` — the block pack export, mirroring the icon CLI's input
|
|
4
|
+
* handling (download a version's jar, or read asset folders, plus mod jars) but
|
|
5
|
+
* emitting geometry and an atlas instead of icons.
|
|
6
|
+
*/
|
|
7
|
+
declare function runBlocksCli(argv: string[]): Promise<number>;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { runBlocksCli };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as ResourceLocator } from "./locator-
|
|
1
|
+
import { n as ResourceLocator } from "./locator-COMEtEn9.js";
|
|
2
2
|
import { _ as translation, d as rotationY, s as multiply, u as rotationX } from "./vector-CCxLae_T.js";
|
|
3
3
|
//#region src/items/blockstate.ts
|
|
4
4
|
/**
|