1
$\begingroup$

I found the blender Asset browser API really difficult to work with, i wonder if any of you have a better solution than the one i found, posted below.

The goal would be to:

  • for each selected assets in the user open asset browser
    • get the asset type (object? collection? material? node?)
    • get the asset data name (object.name, material.name, node.name)
    • get the filepath of this asset .blend file
$\endgroup$
2

1 Answer 1

1
$\begingroup$

In the Geo-Scatter plugin, this is how we proceed, the method is FAR from ideal. Collection type could be implemented that way as well, however, note that we rely on the context asset library being selected, the new "all" category for example render this method useless.

One of the constraints we are facing is the asset.relative_path property, which is utterly useless, as we still lack the most important information, which is the context library directory


class AssetItem():
    
    def __init__(self, asset=None, directory="",):
        
        self.asset = asset
        self.name = asset.name
        self.type = asset.id_type

        if (bool(directory)):
            blend, inner = asset.relative_path.split(".blend") #this line won't work if the name is "dude.blend123.blend" for example
            self.path = os.path.join(directory, f"{blend}.blend")
            self.full_path = os.path.join(directory, asset.relative_path)
            assert os.path.exists(self.path)
        else:
            self.path = "current_file"
            self.full_path = "current_file" 
    
    def __repr__(self):

        return f"<AssetItem {self.type} '{self.name}' at {self.full_path}>"


#globals needed for workaround
is_current_file = None
import_type = None
selected_asset_items = None


class SCATTER5_OT_get_browser_items(bpy.types.Operator):
    """workaround, use an operator context override to get the correct context. using areas[x].selected_asset_files simply refuses to work on blender 3.3LTS"""

    bl_idname      = "scatter5.get_browser_items"
    bl_label       = ""
    bl_description = ""

    def execute(self, context):
        """gather asset browset items from operator context"""

        #import globals and reset
        global is_current_file , import_type , selected_asset_items
        is_current_file = import_type = selected_asset_items = None

        #no headless support
        if (not context.window):
            print("SCATTER5_OT_get_browser_items->not context.window, using blender headlessly?")
            return {'FINISHED'}
        
        #set global context
        window, area = None, None

        #is context area already correct?
        if (context.area.ui_type=="ASSETS"):
            window, area = context.window, context.area

        #else try to find AB area in context window first
        if (area is None):
            for a in context.window.screen.areas:
                if (a.ui_type=="ASSETS"):
                    window, area = context.window, a
                    break

        #else try other windows?
        if (area is None):
            for w in context.window_manager.windows:
                for a in w.screen.areas:
                    if (a.ui_type=="ASSETS"):
                        window, area = w, a
                        break

        #else raise error..
        if (area is None):
            print("SCATTER5_OT_get_browser_items->No match for windows/areas")
            return {'FINISHED'}

        #override if needed, to do so, will need to quit and relaunch this operator
        if ((context.area!=area) or (context.window!=window)):
            override = bpy.context.copy()
            override.update(area=area)
            override.update(window=window)
            bpy.ops.scatter5.get_browser_items(override)
            return {'FINISHED'}

        space = area.spaces[0]
        directory = space.params.directory.decode("utf-8")

        #Better way to support assets? get main directory path easily from API? don't looks like it
        
        #write globals
        is_current_file = not bool(directory)
        import_type = space.params.import_type
        selected_asset_items = [ AssetItem(asset=ass, directory=directory) for ass in context.selected_asset_files ]
        #TODO supports "COLLECTION", will need to import collection and do object instance ourself if cannot find collection instance object
        selected_asset_items = [ Ass for Ass in selected_asset_items if (Ass.type=="OBJECT") ] 

        return {'FINISHED'}


def import_selected_assets(link=False, link_coll="Geo-Scatter Import",):
    """import selected object type assets from browser, link/append depends on technique"""

    objects_found = []

    bpy.ops.scatter5.get_browser_items()
    global is_current_file , import_type , selected_asset_items

    ...
    # proceed to the import code..
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .