Custom USD Layers

Custom USD Layers#

This example plugin creates USD departmentlayers and sublayers for newly created shots.
It also defines the default content of each layer.
When creating custom departmentlayers and sublayers, you can use the Layer Order setting to define the order of these layers.
See the section for Single File Plugins on how to load this example.
# CustomUsdLayers.py

name = "CustomUsdLayers"
classname = "CustomUsdLayers"


class CustomUsdLayers:
    def __init__(self, core):
        self.core = core
        self.version = "v1.0.0"

        self.core.registerCallback("onShotCreated", self.onShotCreated, plugin=self)

        # check if USD plugin is loaded
        usdPlugin = self.core.getPlugin("USD")
        if usdPlugin:
            # if yes, patch the function
            self.applyPatch(usdPlugin)

        # register callback in case the USD plugin will be loaded later on
        # this is important if the plugin gets loaded later on during the startup or manually by the user
        self.core.registerCallback(
            "pluginLoaded", self.onPluginLoaded, plugin=self
        )

    def onPluginLoaded(self, plugin):
        # check if the loaded plugin is the USD plugin and if yes apply the patch
        if plugin.pluginName == "USD":
            self.applyPatch(plugin)

    def applyPatch(self, plugin):
        # apply the monkeypatches to the functions of the USD plugin
        # This makes sure that Prism will not create the default USD layers when a shot gets created
        self.core.plugins.monkeyPatch(plugin.api.createDftEntityContainer, lambda: False, self, force=True)
        self.core.plugins.monkeyPatch(plugin.api.createLayersAutomatically, lambda: False, self, force=True)

    def onShotCreated(self, origin, entity):
        # function gets called every time a shot gets created

        # check if the USD plugin is loaded
        usdPlug = self.core.plugins.getPlugin("USD")
        if not usdPlug:
            return usdPlug

        global Usd
        from pxr import Usd
        usdApi = usdPlug.api

        # create the shot USD container
        comment = "my custom USD"
        path = usdApi.getNewEntityUsdPath(entity, comment=comment)
        stage = usdApi.generateStageWithDefaultContent(entity)
        result = usdApi.saveStage(stage, path)
        if result is False:
            return False

        # create the versioninfo.json file for the shot container
        details = entity.copy()
        details["product"] = usdApi.getShotUsdName()
        hVersion = self.core.products.getVersionFromFilepath(path)
        details["version"] = hVersion
        details["comment"] = comment

        infoPath = self.core.products.getVersionInfoPathFromProductFilepath(
            path
        )
        self.core.saveVersionInfo(
            filepath=infoPath,
            details=details
        )

        # update the master version of the shot container
        useMaster = self.core.products.getUseMaster()
        if useMaster:
            self.core.products.updateMasterVersion(path)

        # define which departmentlayers will be created
        deps = ["Layout", "Animation", "Lighting", "Custom"]
        for dep in deps:
            # create department layers
            usdApi.createDepartmentLayerForEntity(entity, dep)
            deplayerPath = usdApi.getLatestDepartmentLayerPath(entity, dep, includeMaster=False)
            stage = self.getStageForDepartment(dep)
            framerange = self.core.entities.getShotRange(entity)
            if framerange:
                start = framerange[0]
                end = framerange[1]
            else:
                start = end = None

            usdApi.setFrameRangeInStage(stage, start, end)
            usdApi.saveStage(stage, deplayerPath)

            # create sublayers for department layers
            if dep == "Layout":
                sublayer = "camera"
                usdApi.createSublayerLayerForDepartment(entity, dep, sublayer)
                sublayerPath = usdApi.getLatestSublayerPath(entity, dep, sublayer, includeMaster=False)
                stage = self.getCameraStage()
                usdApi.setFrameRangeInStage(stage, start, end)
                usdApi.saveStage(stage, sublayerPath)
            elif dep == "Custom":
                sublayer = "customSublayer1"
                usdApi.createSublayerLayerForDepartment(entity, dep, sublayer)
                sublayerPath = usdApi.getLatestSublayerPath(entity, dep, sublayer, includeMaster=False)
                stage = self.getCustom1Stage()
                usdApi.setFrameRangeInStage(stage, start, end)
                usdApi.saveStage(stage, sublayerPath)

                sublayer = "customSublayer2"
                usdApi.createSublayerLayerForDepartment(entity, dep, sublayer)
                sublayerPath = usdApi.getLatestSublayerPath(entity, dep, sublayer, includeMaster=False)
                stage = self.getCustom2Stage()
                usdApi.setFrameRangeInStage(stage, start, end)
                usdApi.saveStage(stage, sublayerPath)

    def getStageForDepartment(self, department):
        # create a default stage with prims in memory for a specific department
        stage = Usd.Stage.CreateInMemory()
        if department == "Layout":
            stage.DefinePrim("/assets", "Scope")
        elif department == "Lighting":
            stage.DefinePrim("/lights", "Xform")

        return stage

    def getCameraStage(self):
        # create the stage for the camera sublayer of the Layout departmentlayer
        stage = Usd.Stage.CreateInMemory()
        stage.DefinePrim("/cameras/shotcam", "Camera")
        return stage

    def getCustom1Stage(self):
        # create a custom stage for the sublayer of the Custom departmentlayer
        stage = Usd.Stage.CreateInMemory()
        stage.DefinePrim("/custom1", "Scope")
        return stage

    def getCustom2Stage(self):
        # create another custom stage for another sublayer of the Custom departmentlayer
        stage = Usd.Stage.CreateInMemory()
        stage.DefinePrim("/custom2", "Scope")
        return stage