"""本地模型体检 — inspect local GLB/GLTF files for game-engine readiness. tripocli inspect model.glb tripocli inspect ./assets/*.glb """ from .. import glb NAME = "inspect" HELP = "本地模型体检:查看 GLB 的三角形面数、包围盒尺寸、骨骼、动画名与性能告警(0 积分)" def add_arguments(parser): parser.add_argument("files", nargs="+", help="一个或多个本地 .glb/.gltf 文件路径") def _fmt_size(num_bytes): if num_bytes >= 1024 * 1024: return f"{num_bytes / (1024 * 1024):.2f} MB" return f"{num_bytes / 1024:.1f} KB" def run(ctx): results = [] for path in ctx.args.files: results.append(glb.inspect_file(path)) payload = results[0] if len(results) == 1 else {"models": results} def human(): for info in results: comp = "Draco" if info["draco_compressed"] else "未压缩" ctx.out.line(f"📦 {info['name']} ({_fmt_size(info['file_size'])}, {comp})") ctx.out.line( f" 网格面数 : {info['triangles']:,} 三角面 / {info['vertices']:,} 顶点 " f"({info['meshes']} mesh, {info['materials']} 材质, {info['textures']} 贴图" f"{', 含法线/PBR贴图' if info['has_pbr_maps'] else ''})" ) if info["bbox"]: b = info["bbox"] sz = b["size"] ctx.out.line( f" 包围尺寸 : 宽 {sz[0]} × 高 {sz[1]} × 深 {sz[2]} " f"(minY={b['min'][1]}, 底部贴地需偏移 +{b['ground_offset_y']})" ) if info["is_rigged"] or info["animations"]: anims = ", ".join(a["name"] for a in info["animations"]) or "无" ctx.out.line(f" 骨骼动画 : {info['joints']} 根骨骼 | 动作片段 ({len(info['animations'])}): {anims}") for w in info["warnings"]: ctx.out.line(f" ⚠️ {w}") ctx.out.data(payload, human=human) return payload