96 lines
3.6 KiB
Python
96 lines
3.6 KiB
Python
# -*- 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() |