-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathimg2stru.py
More file actions
154 lines (129 loc) · 5.77 KB
/
img2stru.py
File metadata and controls
154 lines (129 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import os
import json
import re
import random
import numpy as np
from PIL import Image
from scipy.spatial import KDTree
from tqdm import tqdm # 导入进度条库
import mcstructure
from mcstructure import Block, Structure
# 1. 强制破解库的尺寸限制
mcstructure.STRUCTURE_MAX_SIZE = (99999, 99999, 99999)
# 2. 排除关键词
EXCLUDE_KEYWORDS = [
"carpet", "slab", "door", "dragon_egg", "anvil", "snow_layer",
"stairs", "button", "pressure_plate", "fence", "wall", "sign",
"daylight", "frame", "invisibleBedrock", "chorus", "trapdoor",
"lantern", "torch", "reeds", "wheat", "stem", "rail", "vine", "ladder"
]
def get_texture_by_priority(tex_field, priority_list):
if isinstance(tex_field, str): return tex_field
if isinstance(tex_field, dict):
for key in priority_list:
if key in tex_field: return tex_field[key]
vals = list(tex_field.values())
return vals[0] if vals else None
return None
def is_texture_solid_16(img_path):
if not os.path.exists(img_path): return False
try:
with Image.open(img_path).convert("RGBA") as img:
if img.size != (16, 16): return False
img_np = np.array(img)
return not np.any(img_np[:, :, 3] < 255)
except:
return False
def load_palette(json_path, pack_root, is_3d_mode=False):
if not os.path.exists(json_path):
print(f"错误: 找不到 blocks.json: {json_path}")
return None, None
with open(json_path, 'r', encoding='utf-8') as f:
content = re.sub(r'//.*', '', f.read())
data = json.loads(content)
palette_rgbs = []
palette_meta = []
items = list(data.items())
print(f"--- 正在分析方块贴图 ({'3D模式' if is_3d_mode else '2D模式'}) ---")
# 进度条 1: 加载色板
for block_id, info in tqdm(items, desc="构建色板", unit="方块"):
if block_id == "format_version" or not isinstance(info, dict): continue
if any(k in block_id for k in EXCLUDE_KEYWORDS): continue
tex_field = info.get("textures")
if not tex_field: continue
p_list = ["up", "all"] if is_3d_mode else ["side", "all", "up"]
target_tex = get_texture_by_priority(tex_field, p_list)
if not target_tex: continue
path = os.path.join(pack_root, "textures", "blocks", target_tex + ".png")
if not is_texture_solid_16(path): continue
try:
with Image.open(path).convert("RGB") as img:
base_rgb = np.array(img).mean(axis=(0, 1))
if is_3d_mode:
palette_rgbs.append(np.clip(base_rgb * 1.0, 0, 255))
palette_meta.append((f"minecraft:{block_id}", 1))
palette_rgbs.append(np.clip(base_rgb * 0.86, 0, 255))
palette_meta.append((f"minecraft:{block_id}", 0))
palette_rgbs.append(np.clip(base_rgb * 0.71, 0, 255))
palette_meta.append((f"minecraft:{block_id}", -1))
else:
palette_rgbs.append(base_rgb)
palette_meta.append((f"minecraft:{block_id}", 0))
except: continue
return np.array(palette_rgbs), palette_meta
def convert(img_path, json_path, pack_root, width, mode):
is_3d = (mode == "7")
rgbs, meta = load_palette(json_path, pack_root, is_3d)
if rgbs is None or len(rgbs) == 0: return
tree = KDTree(rgbs)
with Image.open(img_path).convert("RGB") as img:
W = width
H = int(img.height * (W / img.width))
img = img.resize((W, H), Image.Resampling.LANCZOS)
img_np = np.array(img)
print(f"--- 正在生成 Minecraft 结构 ({W}x{H}) ---")
if is_3d:
struct = Structure((W, 64, H))
curr_y = np.full(W, 32, dtype=int)
# 进度条 2: 3D 模式处理
for z in tqdm(range(H), desc="生成3D层级", unit="行"):
for x in range(W):
_, idx = tree.query(img_np[z, x])
block_id, y_step = meta[idx]
if z > 0: curr_y[x] += y_step
curr_y[x] = np.clip(curr_y[x], 0, 63)
struct.set_block((x, int(curr_y[x]), z), Block(block_id))
mode_str = "3DMap64"
else:
orient_map = {"1":(W,1,H), "2":(W,1,H), "3":(W,H,1), "4":(W,H,1), "5":(1,H,W), "6":(1,H,W)}
dim = orient_map.get(mode, (W,H,1))
struct = Structure(dim)
flat_pixels = img_np.reshape(-1, 3)
_, indices = tree.query(flat_pixels)
# 进度条 2: 2D 模式处理
for i, idx in enumerate(tqdm(indices, desc="填充方块", unit="像素")):
ix, iy = i % W, i // W
block_id, _ = meta[idx]
if mode == "1": pos = (ix, 0, iy)
elif mode == "2": pos = (ix, 0, H-1-iy)
elif mode == "3": pos = (W-1-ix, H-1-iy, 0)
elif mode == "5": pos = (0, H-1-iy, W-1-ix)
elif mode == "6": pos = (0, H-1-iy, ix)
else: pos = (ix, H-1-iy, 0)
struct.set_block(pos, Block(block_id))
mode_str = "2D"
# 保存
rand = random.randint(1000, 9999)
out_name = f"art_{mode_str}_{rand}.mcstructure"
with open(out_name, "wb") as f:
struct.dump(f)
print(f"\n✅ 转换完成!\n文件名: {out_name}\n总像素点: {W * H}")
if __name__ == "__main__":
root = os.path.dirname(os.path.abspath(__file__))
# 简单的交互
p = input("🖼️ 图片路径: ").replace("\"", "").strip()
w = int(input("📏 目标宽度 (方块数, 默认128): ") or 128)
print("\n🗺️ 模式选择:")
print("1.地面 2.天花板 3.北墙 4.南墙 5.东墙 6.西墙 7.【3D阶梯地图画(限高64,失真严重)】")
m = input("请输入 (1-7): ") or "4"
convert(p, os.path.join(root, "blocks.json"), root, w, m)