86 lines
2.9 KiB
Python
86 lines
2.9 KiB
Python
"""Post-process pixel UV maps: rebuild left-limb maps from right-limb maps."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from PIL import Image
|
|
|
|
from skin_figurine.regions import REGIONS
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
SKINS = ROOT / "tests" / "fixtures" / "skins"
|
|
MAPS = ROOT / "skin_figurine" / "data" / "pixel_uv_maps.json"
|
|
|
|
|
|
def remap_info(info: dict, src_regions: dict, dst_regions: dict) -> dict:
|
|
face = info["face"]
|
|
i, j = info["i"], info["j"]
|
|
if face not in dst_regions:
|
|
return dict(info)
|
|
x0, y0, w, h = dst_regions[face]
|
|
# clamp
|
|
i = min(max(i, 0), w - 1)
|
|
j = min(max(j, 0), h - 1)
|
|
out = dict(info)
|
|
out["i"] = i
|
|
out["j"] = j
|
|
out["u"] = x0 + i
|
|
out["v"] = y0 + j
|
|
out["remapped_from_right"] = True
|
|
return out
|
|
|
|
|
|
def rebuild_left(style: str, data: dict) -> None:
|
|
pairs = [("arm_r", "arm_l"), ("leg_r", "leg_l")]
|
|
regions = REGIONS[style]
|
|
for right_key, left_key in pairs:
|
|
right_map = data[style][right_key]
|
|
# Index right map by (face,i,j) — primary; also keep list for zip fallback
|
|
by_fij = {}
|
|
for k, info in right_map.items():
|
|
by_fij[(info["face"], info["i"], info["j"])] = info
|
|
|
|
left_map = data[style][left_key]
|
|
# Rebuild: for every left key, if we can find same face/i/j in right, remap UV.
|
|
# Left keys that only exist on left: zip-sorted with right keys.
|
|
new_left = {}
|
|
right_sorted = sorted(right_map.items(), key=lambda kv: int(kv[0]) if kv[0].isdigit() else kv[0])
|
|
left_keys = sorted(left_map.keys(), key=lambda k: int(k) if k.isdigit() else k)
|
|
|
|
# Prefer 1:1 by sorted key order (same Blender stack order on mirrored parts)
|
|
for idx, lk in enumerate(left_keys):
|
|
if idx < len(right_sorted):
|
|
_rk, rinfo = right_sorted[idx]
|
|
new_left[lk] = remap_info(rinfo, regions[right_key], regions[left_key])
|
|
elif lk in left_map:
|
|
# keep old if any
|
|
info = left_map[lk]
|
|
new_left[lk] = remap_info(info, regions[left_key], regions[left_key])
|
|
|
|
# Also ensure every right (face,i,j) has a left counterpart via key order already
|
|
data[style][left_key] = new_left
|
|
print(f"{style}.{left_key}: rebuilt {len(new_left)} entries from {right_key}")
|
|
|
|
|
|
def main():
|
|
data = json.loads(MAPS.read_text(encoding="utf-8"))
|
|
rebuild_left("classic", data)
|
|
rebuild_left("slim", data)
|
|
MAPS.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
print(f"Updated {MAPS}")
|
|
|
|
# Quick validate against jay left leg opaque count
|
|
from PIL import Image
|
|
|
|
skin = Image.open(SKINS / "jay.png").convert("RGBA")
|
|
pred = 0
|
|
for info in data["classic"]["leg_l"].values():
|
|
if skin.getpixel((info["u"], info["v"]))[3] > 10:
|
|
pred += 1
|
|
print(f"jay left leg predicted opaque mappings: {pred}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|