Open sharktacos opened 1 year ago
This is a great request and something that I think would be useful, I will notify that team and bring it to their attention.
wonderful! Thank you!
Here's a quick Python code snippet:
from maya import cmds
import mayaUsd.ufe
from pxr import Usd, UsdShade
def pairwise(iterable):
it = iter(iterable)
return zip(it, it)
def iter_ufe_usd_selection():
for path in cmds.ls(selection=True,
ufeObjects=True,
long=True,
absoluteName=True):
if "," not in path:
continue
node, ufe_path = path.split(",", 1)
if cmds.nodeType(node) != "mayaUsdProxyShape":
continue
yield path
def get_ufe_path(proxy, prim):
prim_path = str(prim.GetPath())
return "{},{}".format(proxy, prim_path)
def convert_ufe_paths_to_bound_materials(
ufe_paths=None,
material_purpose=UsdShade.Tokens.allPurpose,
include_subsets=False
):
"""Convert selection or ufe node paths to bound materials
Arguments:
ufe_paths (Optional[list]): UFE paths to operate on.
If not provided current selection will be used.
material_purpose (UsdShade.Token): Material purpose
to return bounds for. Defaults to all purposes.
include_subsets (bool): Whether to include bound
materials from material bind subsets.
Returns:
list: UsdShadeMaterial UFE paths.
"""
if ufe_paths is None:
ufe_paths = list(iter_ufe_usd_selection())
targets = []
for path in ufe_paths:
proxy, prim_path = path.split(",", 1)
prim = mayaUsd.ufe.ufePathToPrim(path)
if not prim:
continue
search_from = [prim]
if include_subsets:
subsets = UsdShade.MaterialBindingAPI(prim).GetMaterialBindSubsets()
for subset in subsets:
search_from.append(subset.GetPrim())
bounds = UsdShade.MaterialBindingAPI.ComputeBoundMaterials(search_from, material_purpose)
for (material, relationship) in zip(*bounds):
material_prim = material.GetPrim()
if material_prim.IsValid():
material_prim_ufe_path = get_ufe_path(proxy, material_prim)
targets.append(material_prim_ufe_path)
return targets
targets = convert_ufe_paths_to_bound_materials(include_subsets=True)
if targets:
cmds.select(targets, replace=True, noExpand=True)
_The above example does traverse from the prim downstream to its UsdGeomSubsets
for the bound materials on the subsets - there's a flag on the function to disable that.
It would be more optimal to call UsdShade.MaterialBindingAPI.ComputeBoundMaterials(prims)
for all prims at the same time. However I'm not sure how to - from any material prim without further context of proxy shape - find the related UFE path for that particular mayaUsdProxy shape. Because technically the user could've selected prims across multiple USD proxy shapes or have mayaUsdProxyShapes with shared USD files.
The snippet van also here: Convert Maya USD Ufe Geometry selection to bound materials with Python
mayaUsdProxyShape
Just want to cross reference a discussion - this is somewhat related to the Material Relationships & Inheritance UI discussion so do definitely bring your opinions there as well.
I noticed just now that you actually asked for the reverse. Material to bound objects
from maya import cmds
import mayaUsd.ufe
from pxr import Usd, UsdShade
from collections import defaultdict
def pairwise(iterable):
it = iter(iterable)
return zip(it, it)
def iter_ufe_usd_selection():
for path in cmds.ls(selection=True,
ufeObjects=True,
long=True,
absoluteName=True):
if "," not in path:
continue
node, ufe_path = path.split(",", 1)
if cmds.nodeType(node) != "mayaUsdProxyShape":
continue
yield path
def get_ufe_path(proxy, prim):
prim_path = str(prim.GetPath())
return "{},{}".format(proxy, prim_path)
def convert_ufe_paths_to_bound_geo(
ufe_paths=None,
material_purpose=UsdShade.Tokens.allPurpose
):
"""Convert USD material selection or ufe material node paths to bound objects.
Arguments:
ufe_paths (Optional[list]): UFE material paths to operate on.
If not provided current selection will be used.
material_purpose (UsdShade.Token): Material purpose
to return bounds for. Defaults to all purposes.
Returns:
list: UsdPrim UFE paths.
"""
if ufe_paths is None:
ufe_paths = list(iter_ufe_usd_selection())
targets = []
prims_per_proxy = defaultdict(set)
for path in ufe_paths:
prim = mayaUsd.ufe.ufePathToPrim(path)
if not prim:
continue
proxy, _prim_path = path.split(",", 1)
prims_per_proxy[proxy].add(prim)
bindings = defaultdict(set)
for proxy, prims in prims_per_proxy.items():
stage = next(iter(prims)).GetStage()
stage_prims = list(stage.Traverse())
bounds = UsdShade.MaterialBindingAPI.ComputeBoundMaterials(stage_prims, material_purpose)
for stage_prim, material, relationship in zip(stage_prims, bounds[0], bounds[1]):
material_prim = material.GetPrim()
if not material_prim.IsValid():
continue
bindings[material_prim].add(stage_prim)
for prim in prims:
for geo_prim in bindings.get(prim, []):
ufe_path = get_ufe_path(proxy, geo_prim)
targets.append(ufe_path)
return targets
targets = convert_ufe_paths_to_bound_geo()
if targets:
cmds.select(targets, replace=True, noExpand=True)
@BigRoy thanks so much for sharing this. The second one works great. With the first one, I'm getting an error
# Error: NameError: file <maya console> line 37: name 'material_prim_ufe_path' is not defined
Thanks. Updated the first snippet as well.
Issue synced internally to EMSUSD-814
Is your feature request related to a problem? Please describe. Currently, it appears there is no means to select objects bound to a material in Maya USD.
Describe the solution you'd like Currently, you can see the material assigned to a mesh by right-clicking on it and choosing "show in LookdevX" which parallels "graph materials on viewport selection" in the Node Editor.
It would be nice to be able to do the inverse of this: select objects assigned to a material.
I would imagine this to entail right-clicking a material (i.e. its Shading Group) in either the Outliner or LookdevX and choosing "select objects with material" from the context menu, as you can in the Node Editor with Maya materials.
Describe alternatives you've considered None that I know of
Additional context FWIW Omniverse has a similar option called "select bound objects"