#!/usr/bin/env python3 """从 GLB 中删除指定节点,并清理不再使用的网格/accessor,输出更小的文件。 支持三种定位方式(可组合): --index N 节点下标(0 起,对应文件里 nodes 数组的顺序) --label NAME 查看器里显示的标签(同名部件按 three.js 规则: 第一个保留原名,其后依次 _1、_2…) --name NAME 原始名称精确匹配,删除所有同名节点 用法: python3 viewer/tools/remove_nodes.py --input a.glb --inplace --label 三维立体 python3 viewer/tools/remove_nodes.py --input a.glb --output b.glb --index 0 12 """ import argparse, os, sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from split_glb import read_glb, BandBuilder, world_matrices def viewer_labels(gltf): """按 three.js GLTFLoader.createUniqueName 的规则,为每个节点算出显示标签。""" used = {} labels = {} for i, n in enumerate(gltf["nodes"]): nm = n.get("name", "") if not nm: continue if nm in used: used[nm] += 1 labels[i] = f"{nm}_{used[nm]}" else: used[nm] = 0 labels[i] = nm return labels def main(): ap = argparse.ArgumentParser() ap.add_argument("--input", required=True) ap.add_argument("--output") ap.add_argument("--inplace", action="store_true") ap.add_argument("--index", type=int, nargs="*", default=[]) ap.add_argument("--label", nargs="*", default=[]) ap.add_argument("--name", nargs="*", default=[]) args = ap.parse_args() src, src_bin = read_glb(args.input) labels = viewer_labels(src) drop = set(args.index) for i, lb in labels.items(): if lb in args.label: drop.add(i) for i, n in enumerate(src["nodes"]): if n.get("name", "") in args.name: drop.add(i) if not drop: print("没有匹配到要删除的节点,未做改动。") return print(f"将删除 {len(drop)} 个节点:") for i in sorted(drop): n = src["nodes"][i] print(f" node{i}: name={n.get('name','')!r} label={labels.get(i,'')!r} mesh={n.get('mesh')}") world = world_matrices(src) b = BandBuilder(src) b.src_bin = src_bin kept = 0 for i, n in enumerate(src["nodes"]): if i in drop: continue if "mesh" not in n: continue # 节点在原文件里是多层嵌套的,这里把世界变换烘焙进节点后挂到根上, # 避免仅复制局部变换导致部件错位。 b.add_verbatim_node(i, world[i]) kept += 1 out = args.output or (args.input if args.inplace else args.input + ".removed.glb") if args.inplace: tmp = out + ".tmp" b.finish(tmp) os.replace(tmp, out) else: b.finish(out) print(f"保留 {kept} 个节点 -> {out} ({os.path.getsize(out)/1048576:.1f} MB)") if __name__ == "__main__": main()