Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select objects bound to material #3328

Open
sharktacos opened this issue Sep 14, 2023 · 8 comments
Open

Select objects bound to material #3328

sharktacos opened this issue Sep 14, 2023 · 8 comments
Labels
enhancement New feature or request

Comments

@sharktacos
Copy link

sharktacos commented Sep 14, 2023

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"
image

@sharktacos sharktacos added the enhancement New feature or request label Sep 14, 2023
@santosd
Copy link
Collaborator

santosd commented Sep 20, 2023

This is a great request and something that I think would be useful, I will notify that team and bring it to their attention.

@sharktacos
Copy link
Author

wonderful! Thank you!

@BigRoy
Copy link
Contributor

BigRoy commented Oct 10, 2023

Here's a quick Python code snippet:

Maya USD Ufe Selection convert selection to bound materials (gist)

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

How to use?

  • Select the geometry in outliner under the mayaUsdProxyShape
  • Then run this script in Python (in e.g. Maya Script Editor)

@BigRoy
Copy link
Contributor

BigRoy commented Oct 10, 2023

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.

@BigRoy
Copy link
Contributor

BigRoy commented Oct 10, 2023

I noticed just now that you actually asked for the reverse. Material to bound objects

Maya USD Ufe Material Selection convert to bound objects (gist)

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)

@sharktacos
Copy link
Author

@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

@BigRoy
Copy link
Contributor

BigRoy commented Oct 12, 2023

Thanks. Updated the first snippet as well.

@neilh-adsk neilh-adsk moved this to Needs triage in maya-usd Nov 6, 2023
@maya-usd-git-sync
Copy link

Issue synced internally to EMSUSD-814

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Status: Needs triage
Status: Needs triage
Development

No branches or pull requests

3 participants