# -*- coding: utf-8 -*- """由 box_of_nails 方块贴图(64x64 UV展开图)生成 16x16 的物品图标。 采用等轴测投影组合盒子可见的三个面(顶面 + 正面 + 右侧面), 源贴图中三个面所在的像素区域(由 blockbench 模型 UV 推出): 正面 north px (8, 8) - (18, 13) 10 x 5 右侧 east px (0, 8) - (8, 13) 8 x 5 顶面 up px (8, 13) - (18, 21) 10 x 8 盒子模型尺寸: 宽 10 单位(x) x 高 5 单位(y) x 深 8 单位(z),1 单位 = 1 贴图像素。 """ import math import os from PIL import Image ROOT = r"e:\MCStudioDownload\work\3027246179@qq.com\Cpp\AddOn\0aaaa4f2f16c46e8964025a5ab0ecc02" RES = os.path.join(ROOT, "resource_pack_t7eJjrxY") SRC = os.path.join(RES, "textures", "blocks", "box_of_nails.png") DST = os.path.join(RES, "textures", "items", "box_of_nails.png") OUT = 16 # 最终图标边长 SS = 4 # 超采样倍数 SIZE = OUT * SS # 渲染画布边长 TEX_FRONT = (8, 8, 18, 13) # 正面 north TEX_WEST = (18, 8, 26, 13) # 左侧面 west TEX_TOP = (8, 13, 18, 21) # 顶面 up W, H, D = 10.0, 5.0, 8.0 # 盒子宽/高/深(模型单位) def rasterize(dst, src, origin, uvec, vvec, rect): """把源贴图矩形 rect 以平行四边形(origin + s*uvec + t*vvec)填到 dst。""" x0, y0, x1, y1 = rect ox, oy = origin ux, uy = uvec vx, vy = vvec det = ux * vy - uy * vx if abs(det) < 1e-9: return corners = [origin, (ox + ux, oy + uy), (ox + ux + vx, oy + uy + vy), (ox + vx, oy + vy)] minx = max(0, int(math.floor(min(c[0] for c in corners)))) maxx = min(dst.width - 1, int(math.ceil(max(c[0] for c in corners)))) miny = max(0, int(math.floor(min(c[1] for c in corners)))) maxy = min(dst.height - 1, int(math.ceil(max(c[1] for c in corners)))) px = dst.load() spx = src.load() sw, sh = src.size for py in range(miny, maxy + 1): dy = py + 0.5 - oy for pxx in range(minx, maxx + 1): dx = pxx + 0.5 - ox s = (vy * dx - vx * dy) / det t = (-uy * dx + ux * dy) / det if s < 0.0 or s >= 1.0 or t < 0.0 or t >= 1.0: continue sx = int(x0 + s * (x1 - x0)) sy = int(y0 + t * (y1 - y0)) sx = min(max(sx, 0), sw - 1) sy = min(max(sy, 0), sh - 1) px[pxx, py] = spx[sx, sy] def main(): src = Image.open(SRC).convert("RGBA") dst = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0)) cx = math.cos(math.radians(30.0)) cy = math.sin(math.radians(30.0)) # 画布内每模型单位对应的像素数(保证盒子完整落在画布内) margin = 4 u = min((SIZE - 2 * margin) / ((W + D) * cx), (SIZE - 2 * margin) / ((W + D) * cy + H)) wv = (W * u * cx, -W * u * cy) # 宽度方向 x(右后上) dv = (-D * u * cx, -D * u * cy) # 深度方向 z(左后上) vv = (0.0, H * u) # 竖直向下 # 前上顶点 F(俯视时最靠近观察者的角),整体居中 content_w = (W + D) * u * cx content_h = (W + D) * u * cy + H * u fx = (SIZE - content_w) / 2.0 + D * u * cx fy = (SIZE - content_h) / 2.0 + (W + D) * u * cy f = (fx, fy) rasterize(dst, src, f, dv, vv, TEX_WEST) # 左侧面 rasterize(dst, src, f, wv, vv, TEX_FRONT) # 正面 rasterize(dst, src, f, wv, dv, TEX_TOP) # 顶面 icon = dst.resize((OUT, OUT), Image.BOX) os.makedirs(os.path.dirname(DST), exist_ok=True) icon.save(DST) print("icon ->", DST, icon.size) if __name__ == "__main__": main()