1#!/pxrpythonsubst 2# 3# Copyright 2018 Pixar 4# 5# Licensed under the Apache License, Version 2.0 (the "Apache License") 6# with the following modification; you may not use this file except in 7# compliance with the Apache License and the following modification to it: 8# Section 6. Trademarks. is deleted and replaced with: 9# 10# 6. Trademarks. This License does not grant permission to use the trade 11# names, trademarks, service marks, or product names of the Licensor 12# and its affiliates, except as required to comply with Section 4(c) of 13# the License and to reproduce the content of the NOTICE file. 14# 15# You may obtain a copy of the Apache License at 16# 17# http://www.apache.org/licenses/LICENSE-2.0 18# 19# Unless required by applicable law or agreed to in writing, software 20# distributed under the Apache License with the above modification is 21# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22# KIND, either express or implied. See the Apache License for the specific 23# language governing permissions and limitations under the Apache License. 24# 25import argparse 26import os 27import sys 28 29from pxr import Usd 30 31 32METADATA_KEYS_TO_SKIP = ('typeName', 'specifier', 'kind', 'active') 33 34 35def _Msg(msg): 36 sys.stdout.write(msg + '\n') 37 38def _Err(msg): 39 sys.stderr.write(msg + '\n') 40 41 42class USDAccessor(object): 43 @staticmethod 44 def GetChildren(prim): 45 return prim.GetAllChildren() 46 47 @staticmethod 48 def GetProperties(prim): 49 return prim.GetAuthoredProperties() 50 51 @staticmethod 52 def GetPropertyName(prop): 53 return prop.GetName() 54 55 @staticmethod 56 def GetMetadata(prim): 57 return prim.GetAllAuthoredMetadata().keys() 58 59 @staticmethod 60 def GetName(prim): 61 return prim.GetName() 62 63 @staticmethod 64 def GetTypeName(prim): 65 return prim.GetTypeName() 66 67 @staticmethod 68 def GetSpecifier(prim): 69 return prim.GetSpecifier() 70 71 @staticmethod 72 def GetKind(prim): 73 return Usd.ModelAPI(prim).GetKind() 74 75 @staticmethod 76 def HasAuthoredActive(prim): 77 return prim.HasAuthoredActive() 78 79 @staticmethod 80 def IsActive(prim): 81 return prim.IsActive() 82 83 84class SdfAccessor(object): 85 @staticmethod 86 def GetChildren(prim): 87 return prim.nameChildren 88 89 @staticmethod 90 def GetProperties(prim): 91 return prim.properties 92 93 @staticmethod 94 def GetPropertyName(prop): 95 return prop.name 96 97 @staticmethod 98 def GetMetadata(prim): 99 return prim.ListInfoKeys() 100 101 @staticmethod 102 def GetName(prim): 103 return prim.name 104 105 @staticmethod 106 def GetTypeName(prim): 107 return prim.typeName 108 109 @staticmethod 110 def GetSpecifier(prim): 111 return prim.specifier 112 113 @staticmethod 114 def GetKind(prim): 115 return prim.kind 116 117 @staticmethod 118 def HasAuthoredActive(prim): 119 return 'active' in prim.ListInfoKeys() 120 121 @staticmethod 122 def IsActive(prim): 123 return prim.active 124 125 126def GetPrimLabel(acc, prim): 127 spec = acc.GetSpecifier(prim).displayName.lower() 128 typeName = acc.GetTypeName(prim) 129 if typeName: 130 definition = '{} {}'.format(spec, typeName) 131 else: 132 definition = spec 133 label = '{} [{}]'.format(acc.GetName(prim), definition) 134 135 shortMetadata = [] 136 137 if not acc.IsActive(prim): 138 shortMetadata.append('active = false') 139 elif acc.HasAuthoredActive(prim): 140 shortMetadata.append('active = true') 141 142 kind = acc.GetKind(prim) 143 if kind: 144 shortMetadata.append('kind = {}'.format(kind)) 145 146 if shortMetadata: 147 label += ' ({})'.format(', '.join(shortMetadata)) 148 return label 149 150 151def PrintPrim(args, acc, prim, prefix, isLast): 152 if not isLast: 153 lastStep = ' |--' 154 if acc.GetChildren(prim): 155 attrStep = ' | |' 156 else: 157 attrStep = ' | ' 158 else: 159 lastStep = ' `--' 160 if acc.GetChildren(prim): 161 attrStep = ' |' 162 else: 163 attrStep = ' ' 164 165 if args.simple: 166 label = acc.GetName(prim) 167 else: 168 label = GetPrimLabel(acc, prim) 169 170 _Msg('{}{}{}'.format(prefix, lastStep, label)) 171 172 attrs = [] 173 if args.metadata: 174 mdKeys = filter(lambda x: x not in METADATA_KEYS_TO_SKIP, sorted(acc.GetMetadata(prim))) 175 attrs.extend('({})'.format(md) for md in mdKeys) 176 177 if args.attributes: 178 attrs.extend('.{}'.format(acc.GetPropertyName(prop)) for prop in acc.GetProperties(prim)) 179 180 numAttrs = len(attrs) 181 for i, attr in enumerate(attrs): 182 if i < numAttrs - 1: 183 _Msg('{}{} :--{}'.format(prefix, attrStep, attr)) 184 else: 185 _Msg('{}{} `--{}'.format(prefix, attrStep, attr)) 186 187 188def PrintChildren(args, acc, prim, prefix): 189 children = acc.GetChildren(prim) 190 numChildren = len(children) 191 for i, child in enumerate(children): 192 if i < numChildren - 1: 193 PrintPrim(args, acc, child, prefix, isLast=False) 194 PrintChildren(args, acc, child, prefix + ' | ') 195 else: 196 PrintPrim(args, acc, child, prefix, isLast=True) 197 PrintChildren(args, acc, child, prefix + ' ') 198 199 200def PrintStage(args, stage): 201 _Msg('/') 202 PrintChildren(args, USDAccessor, stage.GetPseudoRoot(), '') 203 204 205def PrintLayer(args, layer): 206 _Msg('/') 207 PrintChildren(args, SdfAccessor, layer.pseudoRoot, '') 208 209 210def PrintTree(args, path): 211 if args.flatten: 212 popMask = (None if args.populationMask is None else Usd.StagePopulationMask()) 213 if popMask: 214 for mask in args.populationMask: 215 popMask.Add(mask) 216 if popMask: 217 if args.unloaded: 218 stage = Usd.Stage.OpenMasked(path, popMask, Usd.Stage.LoadNone) 219 else: 220 stage = Usd.Stage.OpenMasked(path, popMask) 221 else: 222 if args.unloaded: 223 stage = Usd.Stage.Open(path, Usd.Stage.LoadNone) 224 else: 225 stage = Usd.Stage.Open(path) 226 PrintStage(args, stage) 227 elif args.flattenLayerStack: 228 from pxr import UsdUtils 229 stage = Usd.Stage.Open(path, Usd.Stage.LoadNone) 230 layer = UsdUtils.FlattenLayerStack(stage) 231 PrintLayer(args, layer) 232 else: 233 from pxr import Sdf 234 layer = Sdf.Layer.FindOrOpen(path) 235 PrintLayer(args, layer) 236 237 238def main(): 239 parser = argparse.ArgumentParser( 240 description='''Writes the tree structure of a USD file. The default is to inspect a single USD file. 241Use the --flatten argument to see the flattened (or composed) Stage tree. 242Special metadata "kind" and "active" are always shown if authored unless --simple is provided.''') 243 244 parser.add_argument('inputPath') 245 parser.add_argument( 246 '--unloaded', action='store_true', 247 dest='unloaded', 248 help='Do not load payloads') 249 parser.add_argument( 250 '--attributes', '-a', action='store_true', 251 dest='attributes', 252 help='Display authored attributes') 253 parser.add_argument( 254 '--metadata', '-m', action='store_true', 255 dest='metadata', 256 help='Display authored metadata (active and kind are part of the label and not shown as individual items)') 257 parser.add_argument( 258 '--simple', '-s', action='store_true', 259 dest='simple', 260 help='Only display prim names: no specifier, kind or active state.') 261 parser.add_argument( 262 '--flatten', '-f', action='store_true', help='Compose the stage with the ' 263 'input file as root layer and write the flattened content.') 264 parser.add_argument( 265 '--flattenLayerStack', action='store_true', 266 help='Flatten the layer stack with the given root layer. ' 267 'Unlike --flatten, this does not flatten composition arcs (such as references).') 268 parser.add_argument('--mask', action='store', 269 dest='populationMask', 270 metavar='PRIMPATH[,PRIMPATH...]', 271 help='Limit stage population to these prims, ' 272 'their descendants and ancestors. To specify ' 273 'multiple paths, either use commas with no spaces ' 274 'or quote the argument and separate paths by ' 275 'commas and/or spaces. Requires --flatten.') 276 277 args = parser.parse_args() 278 279 # split args.populationMask into paths. 280 if args.populationMask: 281 if not args.flatten: 282 # You can only mask a stage, not a layer. 283 _Err("%s: error: --mask requires --flatten" % parser.prog) 284 return 1 285 args.populationMask = args.populationMask.replace(',', ' ').split() 286 287 from pxr import Ar 288 resolver = Ar.GetResolver() 289 290 try: 291 if hasattr(Ar.Resolver, "ConfigureResolverForAsset"): 292 resolver.ConfigureResolverForAsset(args.inputPath) 293 resolverContext = resolver.CreateDefaultContextForAsset(args.inputPath) 294 with Ar.ResolverContextBinder(resolverContext): 295 resolved = resolver.Resolve(args.inputPath) 296 if not resolved: 297 _Err('Cannot resolve inputPath %r'%resolved) 298 return 1 299 PrintTree(args, resolved) 300 except Exception as e: 301 _Err("Failed to process '%s' - %s" % (args.inputPath, e)) 302 return 1 303 304 return 0 305 306 307if __name__ == "__main__": 308 # Restore signal handling defaults to allow output redirection and the like. 309 import platform 310 if platform.system() != 'Windows': 311 import signal 312 signal.signal(signal.SIGPIPE, signal.SIG_DFL) 313 sys.exit(main()) 314