litematica-render 0.1.0__py3-none-any.whl
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.
- litematic/__init__.py +34 -0
- litematic/parse/__init__.py +1 -0
- litematic/parse/errors.py +13 -0
- litematic/parse/model.py +225 -0
- litematic/parse/nbt.py +167 -0
- litematic/parse/parser.py +317 -0
- litematic/render/__init__.py +134 -0
- litematic/render/errors.py +9 -0
- litematic/render/pack/__init__.py +7 -0
- litematic/render/pack/blockstate.py +111 -0
- litematic/render/pack/compile.py +423 -0
- litematic/render/pack/elements.py +421 -0
- litematic/render/pack/entities.py +181 -0
- litematic/render/pack/extract.py +243 -0
- litematic/render/pack/loader.py +186 -0
- litematic/render/pack/resolve.py +344 -0
- litematic/render/pack/stack.py +83 -0
- litematic/render/pack/texture.py +347 -0
- litematic/render/png.py +54 -0
- litematic/render/quads.py +106 -0
- litematic/render/scene/__init__.py +6 -0
- litematic/render/scene/isometric.py +579 -0
- litematic/render/scene/painter.py +395 -0
- litematic/render/shapes/__init__.py +6 -0
- litematic/render/shapes/boxes.py +290 -0
- litematic/render/shapes/families.py +357 -0
- litematica_render-0.1.0.dist-info/METADATA +52 -0
- litematica_render-0.1.0.dist-info/RECORD +30 -0
- litematica_render-0.1.0.dist-info/WHEEL +4 -0
- litematica_render-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"""Builtin shape table: box families plus multi-box and cross plants."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Mapping
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from litematic.render.errors import RenderError
|
|
8
|
+
from litematic.render.quads import (
|
|
9
|
+
CULL_NONE,
|
|
10
|
+
QUAD_DTYPE,
|
|
11
|
+
TIER_EAST,
|
|
12
|
+
TIER_NORTH,
|
|
13
|
+
TIER_SOUTH,
|
|
14
|
+
TIER_UP,
|
|
15
|
+
TIER_WEST,
|
|
16
|
+
_redstone_tint,
|
|
17
|
+
)
|
|
18
|
+
from litematic.render.shapes.boxes import (
|
|
19
|
+
Box,
|
|
20
|
+
_banner,
|
|
21
|
+
_barrel,
|
|
22
|
+
_button,
|
|
23
|
+
_cactus,
|
|
24
|
+
_carpet,
|
|
25
|
+
_chest,
|
|
26
|
+
_combine,
|
|
27
|
+
_composter,
|
|
28
|
+
_conduit,
|
|
29
|
+
_door,
|
|
30
|
+
_facing,
|
|
31
|
+
_flag,
|
|
32
|
+
_fluid,
|
|
33
|
+
_head,
|
|
34
|
+
_ladder,
|
|
35
|
+
_lectern,
|
|
36
|
+
_lily_pad,
|
|
37
|
+
_pressure_plate,
|
|
38
|
+
_rail,
|
|
39
|
+
_repeater,
|
|
40
|
+
_shulker_box,
|
|
41
|
+
_sign,
|
|
42
|
+
_slab,
|
|
43
|
+
_snow,
|
|
44
|
+
_stairs,
|
|
45
|
+
_text,
|
|
46
|
+
_torch_standing,
|
|
47
|
+
_torch_wall,
|
|
48
|
+
_trapdoor,
|
|
49
|
+
box_quads,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
__all__ = ["builtin_quads", "box_quads"]
|
|
53
|
+
|
|
54
|
+
Builder = Callable[[Mapping[str, str]], np.ndarray]
|
|
55
|
+
_SIDES = ("north", "south", "west", "east")
|
|
56
|
+
|
|
57
|
+
# Connection geometry per family (Blockscope shape.rs taxonomy): fences
|
|
58
|
+
# carry two rails per connected side at y 6..9 and 12..15, walls one full
|
|
59
|
+
# arm with the post height driven by "up" (low and tall arms share the
|
|
60
|
+
# same 0..14 box, so tall arms' cross-block reach is approximated), panes
|
|
61
|
+
# and iron bars one full arm per side plus a full bar when unconnected.
|
|
62
|
+
_FENCE_POST: Box = (6, 0, 6, 10, 16, 10)
|
|
63
|
+
_FENCE_ARMS: dict[str, tuple[Box, Box]] = {
|
|
64
|
+
"north": ((6, 6, 0, 10, 9, 6), (6, 12, 0, 10, 15, 6)),
|
|
65
|
+
"south": ((6, 6, 10, 10, 9, 16), (6, 12, 10, 10, 15, 16)),
|
|
66
|
+
"west": ((0, 6, 6, 6, 9, 10), (0, 12, 6, 6, 15, 10)),
|
|
67
|
+
"east": ((10, 6, 6, 16, 9, 10), (10, 12, 6, 16, 15, 10)),
|
|
68
|
+
}
|
|
69
|
+
_GATE_POST: Box = (6, 0, 6, 10, 16, 10)
|
|
70
|
+
# Open gate bars stay centered over the full block depth per Blockscope,
|
|
71
|
+
# coplanarly overlapping the post side faces; both faces share the same
|
|
72
|
+
# auto-UV texel mapping, so the overlap does not change rendered pixels.
|
|
73
|
+
_GATE_BAR_X: Box = (0, 6, 6, 16, 15, 10)
|
|
74
|
+
_GATE_BAR_Z: Box = (6, 6, 0, 10, 15, 16)
|
|
75
|
+
_WALL_ARMS: dict[str, Box] = {
|
|
76
|
+
"north": (4, 0, 0, 12, 14, 4),
|
|
77
|
+
"south": (4, 0, 12, 12, 14, 16),
|
|
78
|
+
"west": (0, 0, 4, 4, 14, 12),
|
|
79
|
+
"east": (12, 0, 4, 16, 14, 12),
|
|
80
|
+
}
|
|
81
|
+
_PANE_POST: Box = (7, 0, 7, 9, 16, 9)
|
|
82
|
+
_PANE_ARMS: dict[str, Box] = {
|
|
83
|
+
"north": (7, 0, 0, 9, 16, 7),
|
|
84
|
+
"south": (7, 0, 9, 9, 16, 16),
|
|
85
|
+
"west": (0, 0, 7, 7, 16, 9),
|
|
86
|
+
"east": (9, 0, 7, 16, 16, 9),
|
|
87
|
+
}
|
|
88
|
+
_PANE_BAR: Box = (7, 0, 0, 9, 16, 16)
|
|
89
|
+
|
|
90
|
+
# Cross plants: two diagonal planes spanning the full block height,
|
|
91
|
+
# texture v running bottom (v=16) to top (v=0); both quads are double-
|
|
92
|
+
# sided and carry the up tier like the vanilla cross model.
|
|
93
|
+
_CROSS_VTX = np.array(
|
|
94
|
+
[
|
|
95
|
+
[(0, 0, 0), (16, 0, 16), (16, 16, 16), (0, 16, 0)],
|
|
96
|
+
[(16, 0, 0), (0, 0, 16), (0, 16, 16), (16, 16, 0)],
|
|
97
|
+
],
|
|
98
|
+
dtype="f4",
|
|
99
|
+
)
|
|
100
|
+
_CROSS_UV = np.tile(
|
|
101
|
+
np.array([(0, 16), (16, 16), (16, 0), (0, 0)], dtype="f4"), (2, 1, 1)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Cross-plant block names rendered as two diagonal planes (Blockscope
|
|
105
|
+
# taxonomy plus the sapling/propagule suffix families).
|
|
106
|
+
_CROSS_PLANTS = frozenset(
|
|
107
|
+
{
|
|
108
|
+
"short_grass",
|
|
109
|
+
"tall_grass",
|
|
110
|
+
"fern",
|
|
111
|
+
"large_fern",
|
|
112
|
+
"dandelion",
|
|
113
|
+
"poppy",
|
|
114
|
+
"blue_orchid",
|
|
115
|
+
"allium",
|
|
116
|
+
"azure_bluet",
|
|
117
|
+
"oxeye_daisy",
|
|
118
|
+
"cornflower",
|
|
119
|
+
"lily_of_the_valley",
|
|
120
|
+
"red_tulip",
|
|
121
|
+
"orange_tulip",
|
|
122
|
+
"white_tulip",
|
|
123
|
+
"pink_tulip",
|
|
124
|
+
"sunflower",
|
|
125
|
+
"lilac",
|
|
126
|
+
"rose_bush",
|
|
127
|
+
"peony",
|
|
128
|
+
"vine",
|
|
129
|
+
"cobweb",
|
|
130
|
+
"bamboo",
|
|
131
|
+
"dead_bush",
|
|
132
|
+
"seagrass",
|
|
133
|
+
"kelp",
|
|
134
|
+
"kelp_plant",
|
|
135
|
+
"sugar_cane",
|
|
136
|
+
"wheat",
|
|
137
|
+
"carrots",
|
|
138
|
+
"potatoes",
|
|
139
|
+
"beetroots",
|
|
140
|
+
"torchflower",
|
|
141
|
+
"pitcher_plant",
|
|
142
|
+
"bush",
|
|
143
|
+
"sweet_berry_bush",
|
|
144
|
+
"tall_seagrass",
|
|
145
|
+
"red_mushroom",
|
|
146
|
+
"brown_mushroom",
|
|
147
|
+
"crimson_roots",
|
|
148
|
+
"warped_roots",
|
|
149
|
+
"nether_sprouts",
|
|
150
|
+
"crimson_fungus",
|
|
151
|
+
"warped_fungus",
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _fence(properties: Mapping[str, str]) -> np.ndarray:
|
|
157
|
+
"""Fence: center post plus two rails on every connected side."""
|
|
158
|
+
boxes: list[Box] = [_FENCE_POST]
|
|
159
|
+
for side in _SIDES:
|
|
160
|
+
if _flag(properties, side):
|
|
161
|
+
boxes.extend(_FENCE_ARMS[side])
|
|
162
|
+
return _combine(boxes)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _fence_gate(properties: Mapping[str, str]) -> np.ndarray:
|
|
166
|
+
"""Fence gate: center post plus a bar swung aside when open."""
|
|
167
|
+
along_x = _facing(properties) in ("north", "south")
|
|
168
|
+
bar = _GATE_BAR_X if along_x else _GATE_BAR_Z
|
|
169
|
+
if _flag(properties, "open"):
|
|
170
|
+
bar = _GATE_BAR_Z if along_x else _GATE_BAR_X
|
|
171
|
+
return _combine([_GATE_POST, bar])
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _side(properties: Mapping[str, str], key: str) -> bool:
|
|
175
|
+
"""True when a wall side property is present and not "none"."""
|
|
176
|
+
return _text(properties, key, "none") != "none"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _wall(properties: Mapping[str, str]) -> np.ndarray:
|
|
180
|
+
"""Wall: center column plus one arm per connected side."""
|
|
181
|
+
top = 16.0 if _flag(properties, "up") else 14.0
|
|
182
|
+
boxes: list[Box] = [(4, 0, 4, 12, top, 12)]
|
|
183
|
+
for side in _SIDES:
|
|
184
|
+
if _side(properties, side):
|
|
185
|
+
boxes.append(_WALL_ARMS[side])
|
|
186
|
+
return _combine(boxes)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _pane(properties: Mapping[str, str]) -> np.ndarray:
|
|
190
|
+
"""Pane and iron bars: center post plus arms, full bar when alone."""
|
|
191
|
+
boxes: list[Box] = [_PANE_POST]
|
|
192
|
+
connected = False
|
|
193
|
+
for side in _SIDES:
|
|
194
|
+
if _flag(properties, side):
|
|
195
|
+
boxes.append(_PANE_ARMS[side])
|
|
196
|
+
connected = True
|
|
197
|
+
if not connected:
|
|
198
|
+
boxes.append(_PANE_BAR)
|
|
199
|
+
return _combine(boxes)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _cross(properties: Mapping[str, str]) -> np.ndarray:
|
|
203
|
+
"""Cross plant: two diagonal double-sided planes at full height."""
|
|
204
|
+
quads = np.empty(2, dtype=QUAD_DTYPE)
|
|
205
|
+
quads["vtx"] = _CROSS_VTX
|
|
206
|
+
quads["uv"] = _CROSS_UV
|
|
207
|
+
quads["texkey"] = ""
|
|
208
|
+
quads["shade"] = TIER_UP
|
|
209
|
+
quads["dbl"] = True
|
|
210
|
+
quads["cull"] = CULL_NONE
|
|
211
|
+
return quads
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _named(*names: str) -> Callable[[str], bool]:
|
|
215
|
+
"""Match one of the exact block names."""
|
|
216
|
+
return lambda stem: stem in names
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _suffixed(suffix: str) -> Callable[[str], bool]:
|
|
220
|
+
"""Match any block name ending with the suffix."""
|
|
221
|
+
return lambda stem: stem.endswith(suffix)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _is_shulker(stem: str) -> bool:
|
|
225
|
+
"""Match the shulker box family including the base block."""
|
|
226
|
+
return stem == "shulker_box" or stem.endswith("_shulker_box")
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _is_head(stem: str) -> bool:
|
|
230
|
+
"""Match heads and skulls."""
|
|
231
|
+
return stem.endswith("_head") or stem.endswith("skull")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _is_wall_torch(stem: str) -> bool:
|
|
235
|
+
"""Match wall-mounted torches."""
|
|
236
|
+
return stem == "wall_torch" or stem.endswith("_wall_torch")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _is_torch(stem: str) -> bool:
|
|
240
|
+
"""Match standing torches (checked after wall torches)."""
|
|
241
|
+
return stem == "torch" or stem.endswith("_torch")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _is_pane(stem: str) -> bool:
|
|
245
|
+
"""Match glass panes and the iron bars block."""
|
|
246
|
+
return stem == "iron_bars" or stem.endswith("_pane")
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _is_cross(stem: str) -> bool:
|
|
250
|
+
"""Match cross-plant names and sapling/propagule suffixes."""
|
|
251
|
+
return stem in _CROSS_PLANTS or stem.endswith(("_sapling", "_propagule"))
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _ground_plane(transposed: bool, texkey: str) -> np.ndarray:
|
|
255
|
+
"""One full-width dust plane on the block floor; transposed uv runs E-W."""
|
|
256
|
+
quad = box_quads(0, 0, 0, 16, 0, 16)[[TIER_UP]]
|
|
257
|
+
if transposed:
|
|
258
|
+
quad["uv"][0] = quad["vtx"][0][:, [2, 0]]
|
|
259
|
+
quad["texkey"][0] = texkey
|
|
260
|
+
return quad
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _wall_strip(tier: int, texkey: str) -> np.ndarray:
|
|
264
|
+
"""One full-height dust strip on a block face, visible from both sides."""
|
|
265
|
+
bounds = {
|
|
266
|
+
TIER_NORTH: (0, 0, 0, 16, 16, 0),
|
|
267
|
+
TIER_SOUTH: (0, 0, 16, 16, 16, 16),
|
|
268
|
+
TIER_WEST: (0, 0, 0, 0, 16, 16),
|
|
269
|
+
TIER_EAST: (16, 0, 0, 16, 16, 16),
|
|
270
|
+
}[tier]
|
|
271
|
+
quad = box_quads(*bounds)[[tier]]
|
|
272
|
+
quad["texkey"][0] = texkey
|
|
273
|
+
quad["dbl"][0] = True
|
|
274
|
+
quad["shade"][0] = TIER_UP
|
|
275
|
+
return quad
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _wire(properties: Mapping[str, str]) -> np.ndarray:
|
|
279
|
+
"""Redstone dust: ground planes per connected axis, wall strips for up."""
|
|
280
|
+
try:
|
|
281
|
+
power = max(0, min(15, int(properties.get("power", "0"))))
|
|
282
|
+
except (TypeError, ValueError):
|
|
283
|
+
power = 0
|
|
284
|
+
r, g, b = _redstone_tint(power)
|
|
285
|
+
line = f"block/redstone_dust_line0@{r},{g},{b}"
|
|
286
|
+
dot = f"block/redstone_dust_dot@{r},{g},{b}"
|
|
287
|
+
connected = [
|
|
288
|
+
properties.get(direction, "none") in ("side", "up")
|
|
289
|
+
for direction in _SIDES
|
|
290
|
+
]
|
|
291
|
+
quads: list[np.ndarray] = []
|
|
292
|
+
if not any(connected):
|
|
293
|
+
quads.append(_ground_plane(False, dot))
|
|
294
|
+
else:
|
|
295
|
+
# North-south planes sample the texture column along x; east-west
|
|
296
|
+
# planes transpose the uv so the same column runs along z.
|
|
297
|
+
if connected[0] or connected[1]:
|
|
298
|
+
quads.append(_ground_plane(False, line))
|
|
299
|
+
if connected[2] or connected[3]:
|
|
300
|
+
quads.append(_ground_plane(True, line))
|
|
301
|
+
for direction, tier in zip(_SIDES, (TIER_NORTH, TIER_SOUTH, TIER_WEST, TIER_EAST)):
|
|
302
|
+
if properties.get(direction, "none") == "up":
|
|
303
|
+
quads.append(_wall_strip(tier, line))
|
|
304
|
+
return np.concatenate(quads)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# Ordered dispatch, earlier rows win: wall torches must precede standing
|
|
308
|
+
# torches because "*_wall_torch" also ends with "_torch". The other rows
|
|
309
|
+
# are pairwise disjoint and keep Blockscope's precedence for reference.
|
|
310
|
+
_FAMILIES: tuple[tuple[Callable[[str], bool], Builder], ...] = (
|
|
311
|
+
(_suffixed("_fence_gate"), _fence_gate),
|
|
312
|
+
(_suffixed("_fence"), _fence),
|
|
313
|
+
(_suffixed("_wall"), _wall),
|
|
314
|
+
(_is_pane, _pane),
|
|
315
|
+
(_is_cross, _cross),
|
|
316
|
+
(_named("water", "lava", "bubble_column"), _fluid),
|
|
317
|
+
(_named("snow"), _snow),
|
|
318
|
+
(_named("redstone_wire"), _wire),
|
|
319
|
+
(_named("composter"), _composter),
|
|
320
|
+
(_suffixed("_slab"), _slab),
|
|
321
|
+
(_suffixed("_stairs"), _stairs),
|
|
322
|
+
(_suffixed("_carpet"), _carpet),
|
|
323
|
+
(_suffixed("rail"), _rail),
|
|
324
|
+
(_suffixed("_pressure_plate"), _pressure_plate),
|
|
325
|
+
(_named("repeater", "comparator"), _repeater),
|
|
326
|
+
(_suffixed("_trapdoor"), _trapdoor),
|
|
327
|
+
(_suffixed("_door"), _door),
|
|
328
|
+
(_named("ladder"), _ladder),
|
|
329
|
+
(_suffixed("_button"), _button),
|
|
330
|
+
(_suffixed("sign"), _sign),
|
|
331
|
+
(_suffixed("banner"), _banner),
|
|
332
|
+
(_named("chest", "trapped_chest", "ender_chest"), _chest),
|
|
333
|
+
(_is_shulker, _shulker_box),
|
|
334
|
+
(_named("barrel"), _barrel),
|
|
335
|
+
(_is_head, _head),
|
|
336
|
+
(_named("cactus"), _cactus),
|
|
337
|
+
(_named("lily_pad"), _lily_pad),
|
|
338
|
+
(_named("conduit"), _conduit),
|
|
339
|
+
(_named("lectern"), _lectern),
|
|
340
|
+
(_is_wall_torch, _torch_wall),
|
|
341
|
+
(_is_torch, _torch_standing),
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def builtin_quads(
|
|
346
|
+
state_name: str, properties: Mapping[str, str]
|
|
347
|
+
) -> np.ndarray | None:
|
|
348
|
+
"""Build geometry-only quads for a known family, else None."""
|
|
349
|
+
if not isinstance(state_name, str):
|
|
350
|
+
raise RenderError("shapes: block state name must be a string")
|
|
351
|
+
if not isinstance(properties, Mapping):
|
|
352
|
+
raise RenderError("shapes: block properties must be a mapping")
|
|
353
|
+
stem = state_name.partition(":")[2] or state_name
|
|
354
|
+
for matches, build in _FAMILIES:
|
|
355
|
+
if matches(stem):
|
|
356
|
+
return build(properties)
|
|
357
|
+
return None
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: litematica-render
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Parse Minecraft Litematica schematics and render them to isometric PNGs
|
|
5
|
+
Project-URL: Homepage, https://github.com/late-maple/Litematica-Render
|
|
6
|
+
Author: late-maple
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Topic :: Games/Entertainment
|
|
11
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Requires-Dist: numpy
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
16
|
+
Requires-Dist: pillow>=9.1; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
18
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
19
|
+
Provides-Extra: pack
|
|
20
|
+
Requires-Dist: pillow>=9.1; extra == 'pack'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Litematica-Render
|
|
24
|
+
|
|
25
|
+
Offline isometric rendering of Minecraft Litematica `.litematic` schematics.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- Parse `.litematic` files: streaming NBT, lazy vectorized region unpacking
|
|
30
|
+
- Render isometric PNGs from a resource pack's real blockstates, models, and textures
|
|
31
|
+
- Pack stack: layer add-on packs (e.g. Faithful 32x) over a vanilla base, MC-style first-hit lookup
|
|
32
|
+
|
|
33
|
+
## Example
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from litematic import load
|
|
37
|
+
from litematic.render import isometric_png, unresolved_colors
|
|
38
|
+
|
|
39
|
+
schem = load("schematic.litematic")
|
|
40
|
+
|
|
41
|
+
png = isometric_png(
|
|
42
|
+
schem,
|
|
43
|
+
textures_path="vanilla-assets.zip", # base pack, required
|
|
44
|
+
extra_packs=["Faithful 32x.zip"], # add-ons, earlier = higher priority
|
|
45
|
+
max_size=2048,
|
|
46
|
+
)
|
|
47
|
+
with open("render.png", "wb") as f:
|
|
48
|
+
f.write(png)
|
|
49
|
+
|
|
50
|
+
# block names the pack stack cannot cover; those render magenta
|
|
51
|
+
missing = unresolved_colors(schem, textures_path="vanilla-assets.zip")
|
|
52
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
litematic/__init__.py,sha256=E2jYR3b82jQXR81PqeT4A01P6J1gvrVRu8RRZ0BcGfg,715
|
|
2
|
+
litematic/parse/__init__.py,sha256=l4mrsKjEn7snnwkUCX9olPWXjRcJGW0PX6s4RG3HfPE,70
|
|
3
|
+
litematic/parse/errors.py,sha256=Kx5GCUdpRJcHqjF8J-znSlGGCb4NHDbR2cQULzp-9Z8,390
|
|
4
|
+
litematic/parse/model.py,sha256=Tt4-atRpqYEGHRIQgDJ8oWABmqmN4vbKSpJen0i-8Q8,7212
|
|
5
|
+
litematic/parse/nbt.py,sha256=PkpyfGraPDpHQexXmdAMADi_7DHCDcOZr5upTihFTNU,5137
|
|
6
|
+
litematic/parse/parser.py,sha256=rHDgIlgiaRGF9ZlX3dmAuDH-b84ot5rtOOTUV1X0ZCE,11765
|
|
7
|
+
litematic/render/__init__.py,sha256=enttGyJ4q7To8tJUx9Liw6R92hqNQ8n_o1oL-gq3-qA,4038
|
|
8
|
+
litematic/render/errors.py,sha256=EY1MDCoRCXZ0YMOj1IodOUsNq56RJ3rpp4wBjszI-Ys,221
|
|
9
|
+
litematic/render/png.py,sha256=t9E28uSm_FysBCPmK7Tng927EP-pctPRsPR7NlfK6Rw,1451
|
|
10
|
+
litematic/render/quads.py,sha256=keEFJgM1JJFXSueenyQKQUepI6mK80dT4oAIEHWHWQM,3202
|
|
11
|
+
litematic/render/pack/__init__.py,sha256=P-4XDsFVJ2kVdr6o89n55x5tUhaWUEbHNPDR8LYm9Po,320
|
|
12
|
+
litematic/render/pack/blockstate.py,sha256=wozrHtq1SOWQ1BfWd0IUjbVl1GftL5b1TIVevCY3QG4,4021
|
|
13
|
+
litematic/render/pack/compile.py,sha256=_DR7SA4tk_jk0ZsnX3kGZN-jwGcysdGVzMu4sX-vAAs,12448
|
|
14
|
+
litematic/render/pack/elements.py,sha256=lMoZztRGGEKO1u8-eUiHctRnsAegAdgojsJwdJOd6zI,13645
|
|
15
|
+
litematic/render/pack/entities.py,sha256=SRDUJqlDmQPi88qSu9j5SP4lYgyD2sLDXaLtVNDdN8Y,6027
|
|
16
|
+
litematic/render/pack/extract.py,sha256=09UTQLSWZo2eX7zM1iy8x3r6n6QS_GWIkDtsd8UuEQA,8192
|
|
17
|
+
litematic/render/pack/loader.py,sha256=48Nw4RnurJQBg4V9Lzc_MiFEdRiLVm4K7pUgZ-twluI,6768
|
|
18
|
+
litematic/render/pack/resolve.py,sha256=BoyWX9ZQ7aqusYoLFHRYajrKhbKkJNKfZjkLYhA7p1o,11025
|
|
19
|
+
litematic/render/pack/stack.py,sha256=0sBboFXIYij9TZ3Of1dEGux72Eh1KBwHh0NcJCRx_ks,2876
|
|
20
|
+
litematic/render/pack/texture.py,sha256=yrvYRfhLaVlAoFBY6ruKTolHwXwb6dFfGhysWPSl-fs,10386
|
|
21
|
+
litematic/render/scene/__init__.py,sha256=DXSoN_mQMVw12MkWfY5RJF6bqXmqoc5b7J2gX1h5FuE,266
|
|
22
|
+
litematic/render/scene/isometric.py,sha256=Gs9MA8KxBB4jvzuOrKzvOLWFxFQWoAv4VkYcj7dnZPk,19832
|
|
23
|
+
litematic/render/scene/painter.py,sha256=b3pI4CgjSoXsfj6e5Si46UeShcliBIy45_bRoUgrlBE,14184
|
|
24
|
+
litematic/render/shapes/__init__.py,sha256=nbLcM_HO2l6U4uUMR7_5liMMuvexYdbz3ZHIxoTJlUw,232
|
|
25
|
+
litematic/render/shapes/boxes.py,sha256=v8GkWeo7xrr9bbWV2t-m1XM6IZpKfS3zQgXuLlRrRb4,9603
|
|
26
|
+
litematic/render/shapes/families.py,sha256=yzfN5GnUHSJC8sB3qmrhLVOgd2mGYfIjWd05Cx0ZGkY,11130
|
|
27
|
+
litematica_render-0.1.0.dist-info/METADATA,sha256=C-od2iPBWy4oASOpmn539uSv8_RHImIsB3vb-rwckfU,1648
|
|
28
|
+
litematica_render-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
29
|
+
litematica_render-0.1.0.dist-info/licenses/LICENSE,sha256=FeHjRX52Zw1w0SHoK2lW2xttM_HUYk_bsdFcoKPPTrk,1067
|
|
30
|
+
litematica_render-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 late_maple
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|