62 lines
2.6 KiB
Python
62 lines
2.6 KiB
Python
|
|
"""Sum skin weights per bone group (finger vs hand vs rest) for each GLB. usage: weight_audit.py *.glb"""
|
||
|
|
import json, struct, sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
FING = ("thumb", "index", "middle", "ring", "pinky")
|
||
|
|
|
||
|
|
def read_glb(path):
|
||
|
|
data = Path(path).read_bytes()
|
||
|
|
length = struct.unpack_from("<I", data, 8)[0]
|
||
|
|
off = 12; gltf = None; binc = None
|
||
|
|
while off < length:
|
||
|
|
clen, ctype = struct.unpack_from("<II", data, off); off += 8
|
||
|
|
if ctype == 0x4E4F534A: gltf = json.loads(data[off:off+clen])
|
||
|
|
elif ctype == 0x004E4942: binc = data[off:off+clen]
|
||
|
|
off += clen
|
||
|
|
return gltf, binc
|
||
|
|
|
||
|
|
def acc(gltf, binc, idx):
|
||
|
|
a = gltf["accessors"][idx]
|
||
|
|
bv = gltf["bufferViews"][a["bufferView"]]
|
||
|
|
ncomp = {"SCALAR":1, "VEC2":2, "VEC3":3, "VEC4":4}[a["type"]]
|
||
|
|
fmt = {5121:"B", 5123:"H", 5125:"I", 5126:"f"}[a["componentType"]]
|
||
|
|
stride = bv.get("byteStride")
|
||
|
|
size = struct.calcsize(fmt)*ncomp
|
||
|
|
off = bv.get("byteOffset",0) + a.get("byteOffset",0)
|
||
|
|
out = []
|
||
|
|
for i in range(a["count"]):
|
||
|
|
o = off + i*(stride or size)
|
||
|
|
out.append(struct.unpack_from("<%d%s" % (ncomp, fmt), binc, o))
|
||
|
|
return out, a["componentType"]
|
||
|
|
|
||
|
|
for path in sys.argv[1:]:
|
||
|
|
gltf, binc = read_glb(path)
|
||
|
|
names = [nd.get("name", "") for nd in gltf["nodes"]]
|
||
|
|
print(f"\n== {Path(path).name} ==")
|
||
|
|
for mi, mesh in enumerate(gltf.get("meshes", [])):
|
||
|
|
for pi, prim in enumerate(mesh.get("primitives", [])):
|
||
|
|
att = prim["attributes"]
|
||
|
|
if "JOINTS_0" not in att: continue
|
||
|
|
# which skin uses this mesh
|
||
|
|
skin_idx = next((nd.get("skin") for nd in gltf["nodes"]
|
||
|
|
if nd.get("mesh") == mi and "skin" in nd), None)
|
||
|
|
if skin_idx is None: continue
|
||
|
|
joints = gltf["skins"][skin_idx]["joints"]
|
||
|
|
jn = [names[j] for j in joints]
|
||
|
|
J, _ = acc(gltf, binc, att["JOINTS_0"])
|
||
|
|
W, wt = acc(gltf, binc, att["WEIGHTS_0"])
|
||
|
|
wsc = 1.0 if wt == 5126 else (1/255 if wt == 5121 else 1/65535)
|
||
|
|
fing_w = hand_w = 0.0
|
||
|
|
fing_verts = 0
|
||
|
|
for jrow, wrow in zip(J, W):
|
||
|
|
fv = 0
|
||
|
|
for j, w in zip(jrow, wrow):
|
||
|
|
w *= wsc
|
||
|
|
if w <= 0: continue
|
||
|
|
n = jn[j].lower()
|
||
|
|
if any(t in n for t in FING): fing_w += w; fv = 1
|
||
|
|
elif n.startswith("hand"): hand_w += w
|
||
|
|
fing_verts += fv
|
||
|
|
print(f" mesh[{mi}] '{mesh.get('name','')}' prim{pi}: {len(J)} verts | "
|
||
|
|
f"finger-weighted verts: {fing_verts} | total finger W: {fing_w:.0f} | hand W: {hand_w:.0f}")
|