| name | inspect-asset |
| description | Quick USD stage inspection — reports stage info, prim counts, mesh statistics, materials, animation, and scene scale. Use before optimizing. |
| version | 1.0.0 |
| allowed-tools | Shell, Read |
| metadata | {"author":"NVIDIA Corporation","tags":["usd","inspection","analysis"]} |
Inspect Asset
Quick, non-destructive inspection of a USD stage. Produces a structured
summary useful before running operations, validators, or tuning
parameters.
What this skill covers
- Usage — arguments.
- Step 1 — resolve a Python with
pxr.
- Step 2 — run the inspection script.
- Step 3 — present the report.
Companion skills: run-validators (validate the asset),
run-operations (optimize the asset), tune-parameters (uses
inspection data to pick starting configs), compare-stages (compare
two stages).
Usage
| Argument | Meaning |
|---|
<path/to/asset.usd> | Required. .usd / .usda / .usdc / .usdz. |
--detailed | Include per-mesh vertex counts and top-level prim tree. |
Workflow:
- Pass the asset path as a positional argument.
- Probe for the
pxr Python bindings (Step 1).
- Run the inspection script with the resolved Python (Step 2).
- Use the captured JSON to render the structured report (Step 3).
- Pass
--detailed to also emit per-mesh and top-level-prim sections.
If no path is provided, ask:
"Which USD file should I inspect? Please provide the full path."
Step 1 — Resolve a Python with pxr
Prefer a built repo's bindings (export the build env, mirroring
tools/validators/run.sh:20-21 / run.bat:25-26); else use whatever pxr the
interpreter already has (wheel/Kit/conda). Don't bare-probe first — a stray
PYTHONPATH can shadow the build and fail with a confusing ABI error.
CONFIG="${USD_OPTIMIZE_CONFIG:-release}"; PLATFORM="${USD_OPTIMIZE_PLATFORM:-linux-x86_64}"
BUILD_DIR="_build/$PLATFORM/$CONFIG"; USD_DIR="_build/target-deps/usd/$CONFIG"
if [ -d "$BUILD_DIR" ]; then
export LD_LIBRARY_PATH="$BUILD_DIR/lib:$BUILD_DIR/extraLibs:$USD_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export PYTHONPATH="$BUILD_DIR/python:$USD_DIR/lib/python${PYTHONPATH:+:$PYTHONPATH}"
PYBIN="_build/target-deps/python/bin/python3.12"
else PYBIN="python3"; fi
"$PYBIN" -c "from pxr import Usd; print('ok', Usd.GetVersion())"
# Windows (PowerShell)
$Config = if ($env:USD_OPTIMIZE_CONFIG) { $env:USD_OPTIMIZE_CONFIG } else { "release" }
$Platform = if ($env:USD_OPTIMIZE_PLATFORM) { $env:USD_OPTIMIZE_PLATFORM } else { "windows-x86_64" }
$BuildDir = "_build\$Platform\$Config"; $UsdDir = "_build\target-deps\usd\$Config"
if (Test-Path $BuildDir) {
$env:PATH = "$BuildDir\bin;$BuildDir\lib;$BuildDir\extraLibs;$UsdDir\bin;$UsdDir\lib;$env:PATH"
$env:PYTHONPATH = "$BuildDir\python;$UsdDir\lib\python;$env:PYTHONPATH"
$PyBin = "_build\target-deps\python\python.exe"
} else { $PyBin = "python" }
& $PyBin -c "from pxr import Usd; print('ok', Usd.GetVersion())"
Reuse the resolved interpreter ($PYBIN / $PyBin, env exported) for Step 2. If
it still fails (no build, no pxr): build the repo, or pip install usd-core==25.11 (match the USD version pinned in deps/usd_flavors.json /
deps/usd-lib-deps.json; a bare pip install usd-core may not match the
build's USD).
Step 2 — Run the inspection script
Write a temp script and run it with the Step 1 interpreter ($PYBIN /
$PyBin, build env exported). The script outputs a single JSON object.
import json, os, sys
from pxr import Usd, UsdGeom, UsdShade, UsdSkel
path = sys.argv[1]
detailed = "--detailed" in sys.argv
stage = Usd.Stage.Open(path)
if not stage:
print(json.dumps({"error": f"Failed to open: {path}"}))
sys.exit(1)
mpu = UsdGeom.GetStageMetersPerUnit(stage)
up = UsdGeom.GetStageUpAxis(stage)
default_prim = stage.GetDefaultPrim()
all_prims = list(stage.TraverseAll())
type_counts = {}
for p in all_prims:
t = p.GetTypeName() or "(untyped)"
type_counts[t] = type_counts.get(t, 0) + 1
meshes = [p for p in all_prims if p.IsA(UsdGeom.Mesh)]
materials = [p for p in all_prims if p.IsA(UsdShade.Material)]
instances = [p for p in all_prims if p.IsInstance()]
skel_roots = [p for p in all_prims if p.IsA(UsdSkel.Root)]
total_verts = 0
total_faces = 0
per_mesh = []
for p in meshes:
mesh = UsdGeom.Mesh(p)
pts = mesh.GetPointsAttr().Get()
fvc = mesh.GetFaceVertexCountsAttr().Get()
nv = len(pts) if pts else 0
nf = len(fvc) if fvc else 0
total_verts += nv
total_faces += nf
if detailed:
per_mesh.append({: (p.GetPath()), : nv, : nf})
has_animation =
p all_prims[:]:
attr p.GetAttributes():
attr.GetNumTimeSamples() > :
has_animation =
has_animation:
bcache = UsdGeom.BBoxCache(Usd.TimeCode.Default(),
[UsdGeom.Tokens.default_, UsdGeom.Tokens.render])
root_bb = bcache.ComputeWorldBound(stage.GetPseudoRoot()).ComputeAlignedBox()
root_bb.IsEmpty():
bb_size = root_bb.GetMax() - root_bb.GetMin()
bb_diag = bb_size.GetLength()
bbox = {: (root_bb.GetMin()), : (root_bb.GetMax()),
: (bb_size), : bb_diag}
:
bbox =
top_prims = []
detailed:
p stage.GetPseudoRoot().GetChildren():
top_prims.append({: (p.GetPath()), : p.GetTypeName()})
:
file_size = os.path.getsize(path) os.path.isfile(path)
(FileNotFoundError, OSError):
file_size =
result = {
: path,
: file_size,
: mpu,
: (up),
: (default_prim.GetPath()) default_prim ,
: (all_prims),
: ((type_counts.items(), key= x: -x[])),
: (meshes),
: total_verts,
: total_faces,
: (materials),
: (instances),
: (skel_roots),
: has_animation,
: bbox,
}
detailed:
result[] = top_prims
result[] = (per_mesh, key= x: -x[])[:]
(json.dumps(result, indent=))
Step 3 — Present the report
Parse the JSON and present a structured summary:
Asset: <basename>
Path: <full path>
Size: <human-readable>
Stage metadata:
metersPerUnit: <value> (<unit name>)
upAxis: <value>
defaultPrim: <path or "(none)")>
Geometry:
Prims: <total>
Meshes: <count> (<vertices> vertices, <faces> faces)
Materials: <count>
Instances: <count>
SkelRoots: <count>
Animation: <yes/no>
Bounding box (stage units):
Size: <X> × <Y> × <Z>
Diagonal: <D>
Prim types:
Mesh: <count>
Xform: <count>
Material: <count>
Shader: <count>
...
Unit interpretation
Map metersPerUnit to a human-readable name:
| Value | Unit |
|---|
| 0.001 | millimeters |
| 0.01 | centimeters |
| 0.0254 | inches |
| 0.3048 | feet |
| 1.0 | meters |
Flags and warnings
- No default prim: warn that some operations (e.g.
deduplicateHierarchies) require a default prim.
- No meshes: note that mesh-only operations and validators find
nothing to do on this stage (references-only / materials-library /
layout stages are the common case).
- Zero-area bounding box: the stage may contain only non-renderable
prims.
- Animation detected: note that time-sampled attributes are present;
optimizeTimeSamples may be relevant.
- High instance count: the stage already uses instancing; warn before
running
deduplicateGeometry (which adds more instances) or merge
(which can't merge instanced prims without deinstancing first).
Detailed mode
If --detailed was requested, also show:
Top-level prims:
/World (Xform)
/World/Asset (Xform)
/World/Lights (Xform)
Largest meshes (top 10 by vertex count):
| Prim path | Vertices | Faces |
|----------------------------|----------|--------|
| /World/Asset/Body/Mesh | 245,000 | 163,000|
| /World/Asset/Wheels/Mesh | 82,000 | 54,000|
| ... | | |
See also
.agents/skills/run-validators/SKILL.md — validate the asset.
.agents/skills/run-operations/SKILL.md — optimize the asset.
.agents/skills/compare-stages/SKILL.md — compare before/after.
.agents/skills/tune-parameters/SKILL.md — Step 3 uses similar
inspection data to pick starting configs.
Purpose
Produce a quick, non-destructive structured summary of a USD stage —
metadata, prim/mesh/material counts, animation flag, bounding box,
scene scale — so the agent (and the user) have a baseline before any
optimization, validation, or tuning workflow. Useful as the very first
step when the user opens a new asset.
Prerequisites
- A USD asset path (
.usd / .usda / .usdc / .usdz).
- A Python interpreter with the
pxr bindings — a built repo
(_build/target-deps/, env exported per Step 1), an existing pxr
(wheel/Kit/conda), or a pinned pip install usd-core==25.11.
Limitations
- This skill is read-only — it never edits the input.
- Animation detection is a sample-only probe (caps at 500 prims and
stops at the first time-sampled attribute) to keep large stages
fast. The flag is a hint, not an exhaustive scan.
- Bounding box is computed for
default + render purposes; assets
whose visual content lives under proxy or guide purposes will
report a degenerate bbox.
- This skill does not edit, optimize, or rewrite stages — for
those, use
run-operations or the operation-specific skills.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|
pxr import fails | Not built, no pxr installed, or a stray PYTHONPATH shadows the build (ABI error). | Build the repo and re-run Step 1 (it exports the build env), or pip install usd-core==25.11 (matched pin). |
Failed to open: <path> in the JSON output | Path is wrong, layer is corrupt, or it's a non-USD file with a .usd extension. | Verify the path; try usdcat <path> or open in usdview to confirm the file is a valid USD layer. |
total_prims = 0 | Asset is essentially empty, or the open call hit a payload that didn't load. | Check whether the stage uses payloads — Stage.OpenMasked or Usd.Stage.Open(path, Usd.Stage.LoadAll) may surface content. |
bbox = null | All visible prims are non-renderable, or the stage uses non-default/render purpose. | Inspect with --detailed and check purpose on top-level prims. |
has_animation: false on an obviously animated stage | Animated attributes live deeper than 500 prims. | Re-run with the cap raised (edit the script's [:500] slice). |