I was attempting to get the resolvedPath
for an SdfAssetPath
attribute/input with the default resolver by doing this:
from pxr import Ar
relativeAssetPath = "./textures/Fieldstone/Fieldstone_BaseColor.png"
resolvedRelativePath = Ar.GetResolver().Resolve(relativeAssetPath)
print(f"resolvedRelativePath: {resolvedRelativePath}")
resolvedRelativePath
will be empty. I then assumed that since there was no “context” that it I must provide search paths, as the default resolver documentation suggests:
from pxr import Ar
relativeAssetPath = "./textures/Fieldstone/Fieldstone_BaseColor.png"
with Ar.ResolverContextBinder(usdviewApi.stage.GetPathResolverContext()):
resolvedRelativePath = Ar.GetResolver().Resolve(relativeAssetPath)
print(f"resolvedRelativePath: {resolvedRelativePath}")
This also leaves resolvedRelativePath
empty.
I then debugged the code to find that ArDefaultResolver::_Resolve() will only use the search paths provided in the context if the input assetPath
is a “search path”. It looks to me like a “search path” is a relative path without ./
or ../
.
According to the Default Resolver Details, it will resolve relative paths by using the search paths as anchors, but in practice it only uses CWD() as an anchor.
In order to resolve assets specified by relative paths, this resolver implements a simple “search path” scheme. The resolver will anchor the relative path to a series of directories and return the first absolute path where the asset exists.
Maybe this Default Resolver Details section could be rephrased to indicate the difference between a “search” relative path and an “anchored” relative path in the context of the default resolver so others aren’t tripped up by this behavior:
- search relative path:
directory/file.ext
,file.ext
- search paths are also relative paths and resolved by the default resolver using the resolver’s context paths
- anchored relative path:
./directory/file.ext
,../file.ext
,./file.ext
- these paths are “bypassed”… do we call these “anchored” relative paths?
I did a lot of digging and find a few instances where it is noted that the default resolver doesn’t resolve ./
relative paths:
- “asset identifiers prefixed with
./
are incorrect” in this issue - “anchored paths, those that start with “./” like
@./anchored/layer.usd@
won’t work properly” in this post
My final solution was uncovered when I found this post with this enlightening text:
if the authored path is an “anchored path” like
@./udimsfolder/udim_texture_.jpg@
, the returnedSdfAssetPath
that theUsdAttribute
gives you will have already performed the anchoring to the layer’s location for you, so you will get back a different and more complete string than the one that was authored
I found that the following code does the asset resolving that we require, but I wonder why “anchored” relative paths are treated differently by the default resolver:
from pxr import Sdf, Usd, UsdShade
matPrim = UsdShade.Material(usdviewApi.stage.GetPrimAtPath("/World/Looks/cubePbr"))
textureAssetPath = matPrim.GetInput("DiffuseTexture").Get()
print(f"assetPath: {textureAssetPath.path}")
print(f"resolvedAssetPath: {textureAssetPath.resolvedPath}")
Finally, resolvedAssetPath
contains a resolved path that I can use
All of this is an attempt to avoid any examination on the “logical” asset path string and use the “resolved” asset path.
I have uploaded an example stage zip that can be opened in USDView and all of this code can be run within the Python Interpreter window.