Files
animation/tools/obj_crop.py
T

51 lines
1.9 KiB
Python
Raw Normal View History

"""Crop an OBJ to the faces fully inside a sphere, so clay renders can frame the hand
instead of the whole arm. Keeps vertex order stable across files (same face set in/out)
only when the inputs share topology, so pass --like to reuse a reference file's face mask.
usage: obj_crop.py in.obj out.obj CX CY CZ R (metres)
obj_crop.py in.obj out.obj --mask mask.txt
obj_crop.py in.obj --write-mask mask.txt CX CY CZ R
"""
import sys, math
from pathlib import Path
def load(path):
vs, faces = [], []
for line in Path(path).read_text().splitlines():
if line.startswith("v "):
p = line.split()
vs.append((float(p[1]), float(p[2]), float(p[3])))
elif line.startswith("f "):
faces.append([int(t.split("/")[0]) - 1 for t in line.split()[1:]])
return vs, faces
args = sys.argv[1:]
src = args[0]
vs, faces = load(src)
if "--write-mask" in args:
maskfile = args[args.index("--write-mask") + 1]
cx, cy, cz, r = (float(x) for x in args[-4:])
keep = [i for i, f in enumerate(faces)
if all(math.dist(vs[k], (cx, cy, cz)) <= r for k in f)]
Path(maskfile).write_text("\n".join(map(str, keep)))
print(f"mask {len(keep)}/{len(faces)} faces -> {maskfile}")
sys.exit()
dst = args[1]
if "--mask" in args:
keep = [int(x) for x in Path(args[args.index("--mask") + 1]).read_text().split()]
else:
cx, cy, cz, r = (float(x) for x in args[-4:])
keep = [i for i, f in enumerate(faces)
if all(math.dist(vs[k], (cx, cy, cz)) <= r for k in f)]
used = sorted({k for i in keep for k in faces[i]})
remap = {old: n + 1 for n, old in enumerate(used)}
out = [f"v {vs[o][0]:.6f} {vs[o][1]:.6f} {vs[o][2]:.6f}" for o in used]
out += ["f " + " ".join(str(remap[k]) for k in faces[i]) for i in keep]
Path(dst).write_text("\n".join(out) + "\n")
print(f"{Path(dst).name}: {len(used)} verts, {len(keep)} faces")