From 6fb82490eb31c97d666631d0d825d1b758e7f3f2 Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Mon, 9 Dec 2024 19:52:19 -0800 Subject: [PATCH] RenderPassEditor : Display adaptor disabled render passes Pipelines can register render adaptors to `client = "RenderPassWedge"` that disable render passes. We want to display the state of these to the user and distinguish them from regular user-disabled render passes. --- Changes.md | 1 + python/GafferSceneUI/RenderPassEditor.py | 42 ++++++++++++++++++- .../GafferSceneUITest/RenderPassEditorTest.py | 41 ++++++++++++++++++ resources/graphics.py | 1 + resources/graphics.svg | 34 +++++++++++++++ .../RenderPassEditorBinding.cpp | 34 +++++++++++++-- 6 files changed, 148 insertions(+), 5 deletions(-) diff --git a/Changes.md b/Changes.md index 6457525e7b8..65035a91f1b 100644 --- a/Changes.md +++ b/Changes.md @@ -21,6 +21,7 @@ Improvements - PlugLayout : - A warning widget is now displayed when an invalid custom widget is registered. - `layout:customWidget::width` and `layout:customWidget::minimumWidth` metadata registrations are now supported for custom widgets. +- RenderPassEditor : Render passes disabled by render adaptors registered to `client = "RenderPassWedge"` are now shown as disabled. To differentiate these from user disabled render passes, an orange dot is shown in the corner of the disabled icon and the tooltip describes them as automatically disabled. Fixes ----- diff --git a/python/GafferSceneUI/RenderPassEditor.py b/python/GafferSceneUI/RenderPassEditor.py index c2af53d1e2b..bcf8e314a5c 100644 --- a/python/GafferSceneUI/RenderPassEditor.py +++ b/python/GafferSceneUI/RenderPassEditor.py @@ -67,6 +67,12 @@ def __init__( self ) : self["editScope"] = Gaffer.Plug() self["displayGrouped"] = Gaffer.BoolPlug() + self["__adaptors"] = GafferSceneUI.RenderPassEditor._createRenderAdaptors() + self["__adaptors"]["in"].setInput( self["in"] ) + + self["__adaptedIn"] = GafferScene.ScenePlug() + self["__adaptedIn"].setInput( self["__adaptors"]["out"] ) + IECore.registerRunTimeTyped( Settings, typeName = "GafferSceneUI::RenderPassEditor::Settings" ) def __init__( self, scriptNode, **kw ) : @@ -236,7 +242,19 @@ def _createRenderAdaptors() : adaptors["__renderAdaptors"]["client"].setValue( "RenderPassWedge" ) adaptors["__renderAdaptors"]["in"].setInput( adaptors["in"] ) - adaptors["out"].setInput( adaptors["__renderAdaptors"]["out"] ) + adaptors["__adaptorSwitch"] = Gaffer.Switch() + adaptors["__adaptorSwitch"].setup( GafferScene.ScenePlug() ) + adaptors["__adaptorSwitch"]["in"]["in0"].setInput( adaptors["__renderAdaptors"]["out"] ) + adaptors["__adaptorSwitch"]["in"]["in1"].setInput( adaptors["in"] ) + + adaptors["__contextQuery"] = Gaffer.ContextQuery() + adaptors["__contextQuery"].addQuery( Gaffer.BoolPlug( "disableAdaptors", defaultValue = False ) ) + adaptors["__contextQuery"]["queries"][0]["name"].setValue( "renderPassEditor:disableAdaptors" ) + + adaptors["__adaptorSwitch"]["index"].setInput( adaptors["__contextQuery"]["out"][0]["value"] ) + adaptors["__adaptorSwitch"]["deleteContextVariables"].setValue( "renderPassEditor:disableAdaptors" ) + + adaptors["out"].setInput( adaptors["__adaptorSwitch"]["out"] ) return adaptors @@ -281,7 +299,7 @@ def __setPathListingPath( self ) : # control of updates ourselves in _updateFromContext(), using LazyMethod to defer the calls to this # function until we are visible and playback has stopped. contextCopy = Gaffer.Context( self.context() ) - self.__pathListing.setPath( _GafferSceneUI._RenderPassEditor.RenderPassPath( self.settings()["in"], contextCopy, "/", filter = self.__filter, grouped = self.settings()["displayGrouped"].getValue() ) ) + self.__pathListing.setPath( _GafferSceneUI._RenderPassEditor.RenderPassPath( self.settings()["__adaptedIn"], contextCopy, "/", filter = self.__filter, grouped = self.settings()["displayGrouped"].getValue() ) ) def __displayGroupedChanged( self ) : @@ -1015,8 +1033,13 @@ def _valuesForUpdate( plugs, auxiliaryPlugs ) : for renderPass in globalsPlug.getValue().get( "option:renderPass:names", IECore.StringVectorData() ) : renderPasses.setdefault( "all", [] ).append( renderPass ) context["renderPass"] = renderPass + context["renderPassEditor:disableAdaptors"] = False if globalsPlug.getValue().get( "option:renderPass:enabled", IECore.BoolData( True ) ).value : renderPasses.setdefault( "enabled", [] ).append( renderPass ) + else : + context["renderPassEditor:disableAdaptors"] = True + if globalsPlug.getValue().get( "option:renderPass:enabled", IECore.BoolData( True ) ).value : + renderPasses.setdefault( "adaptorDisabled", [] ).append( renderPass ) result.append( { "value" : plug.getValue(), @@ -1079,6 +1102,7 @@ def __menuDefinition( self ) : { "command" : functools.partial( Gaffer.WeakMethod( self.__setCurrentRenderPass ), name ), "icon" : self.__renderPassIcon( name, activeIndicator = True ), + "description" : self.__renderPassDescription( name ) } ) @@ -1123,6 +1147,18 @@ def __setCurrentRenderPass( self, renderPass, *unused ) : for plug in self.getPlugs() : plug.setValue( renderPass ) + def __renderPassDescription( self, renderPass ) : + + if renderPass == "" : + return "" + + if renderPass in self.__renderPasses.get( "adaptorDisabled", [] ) : + return "{} has been automatically disabled by a render adaptor.".format( renderPass ) + elif renderPass not in self.__renderPasses.get( "enabled", [] ) : + return "{} has been disabled.".format( renderPass ) + + return "" + def __renderPassIcon( self, renderPass, activeIndicator = False ) : if renderPass == "" : @@ -1134,6 +1170,8 @@ def __renderPassIcon( self, renderPass, activeIndicator = False ) : return "warningSmall.png" elif renderPass in self.__renderPasses.get( "enabled", [] ) : return "renderPass.png" + elif renderPass in self.__renderPasses.get( "adaptorDisabled", [] ) : + return "adaptorDisabledRenderPass.png" else : return "disabledRenderPass.png" diff --git a/python/GafferSceneUITest/RenderPassEditorTest.py b/python/GafferSceneUITest/RenderPassEditorTest.py index 430b2956bf6..c72f7981bdf 100644 --- a/python/GafferSceneUITest/RenderPassEditorTest.py +++ b/python/GafferSceneUITest/RenderPassEditorTest.py @@ -167,6 +167,47 @@ def testFn( name ) : else : self.assertIsNone( inspectionContext ) + def testRenderPassPathAdaptorDisablingPasses( self ) : + + def createAdaptor() : + + node = GafferScene.SceneProcessor() + node["options"] = GafferScene.CustomOptions() + node["options"]["in"].setInput( node["in"] ) + node["options"]["options"].addChild( Gaffer.NameValuePlug( "renderPass:enabled", False ) ) + + node["switch"] = Gaffer.NameSwitch() + node["switch"].setup( node["options"]["out"] ) + node["switch"]["in"][0]["value"].setInput( node["in"] ) + node["switch"]["in"][1]["value"].setInput( node["options"]["out"] ) + node["switch"]["in"][1]["name"].setValue( "B C" ) + node["switch"]["selector"].setValue( "${renderPass}" ) + + node["out"].setInput( node["switch"]["out"]["value"] ) + + return node + + GafferScene.SceneAlgo.registerRenderAdaptor( "RenderPassEditorTest", createAdaptor, client = "RenderPassWedge" ) + self.addCleanup( GafferScene.SceneAlgo.deregisterRenderAdaptor, "RenderPassEditorTest" ) + + renderPasses = GafferScene.RenderPasses() + renderPasses["names"].setValue( IECore.StringVectorData( [ "A", "B", "C", "D" ] ) ) + + adaptors = GafferSceneUI.RenderPassEditor._createRenderAdaptors() + adaptors["in"].setInput( renderPasses["out"] ) + + context = Gaffer.Context() + path = _GafferSceneUI._RenderPassEditor.RenderPassPath( adaptors["out"], context, "/" ) + + self.assertEqual( [ str( c ) for c in path.children() ], [ "/A", "/B", "/C", "/D" ] ) + + pathCopy = path.copy() + + for p in [ "/A", "/B", "/C", "/D" ] : + pathCopy.setFromString( p ) + self.assertEqual( pathCopy.property( "renderPassPath:enabled" ), p in ( "/A", "/D" ) ) + self.assertTrue( pathCopy.property( "renderPassPath:enabledWithoutAdaptors" ) ) + def testSearchFilter( self ) : renderPasses = GafferScene.RenderPasses() diff --git a/resources/graphics.py b/resources/graphics.py index 0b7ace2f53c..451924ee0e0 100644 --- a/resources/graphics.py +++ b/resources/graphics.py @@ -471,6 +471,7 @@ "ids" : [ "renderPass", "disabledRenderPass", + "adaptorDisabledRenderPass", "renderPassFolder", "activeRenderPass", "activeRenderPassFadedHighlighted", diff --git a/resources/graphics.svg b/resources/graphics.svg index 48d4a282b42..85fddf966e1 100644 --- a/resources/graphics.svg +++ b/resources/graphics.svg @@ -3592,6 +3592,14 @@ width="14" style="display:inline;opacity:0.1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.597614;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.10173;stroke-opacity:1;paint-order:markers stroke fill" inkscape:label="activeRenderPassFadedHighlightedIcon" /> + + + + + + ( g_disableAdaptorsContextName, &disableAdaptors ); + } if( canceller ) { scopedContext.setCanceller( canceller ); @@ -452,9 +460,12 @@ RenderPassPath::Ptr constructor2( ScenePlug &scene, Context &context, const std: // RenderPassNameColumn ////////////////////////////////////////////////////////////////////////// +ConstStringDataPtr g_adaptorDisabledRenderPassIcon = new StringData( "adaptorDisabledRenderPass.png" ); ConstStringDataPtr g_disabledRenderPassIcon = new StringData( "disabledRenderPass.png" ); ConstStringDataPtr g_renderPassIcon = new StringData( "renderPass.png" ); ConstStringDataPtr g_renderPassFolderIcon = new StringData( "renderPassFolder.png" ); +ConstStringDataPtr g_disabledToolTip = new StringData( "Disabled." ); +ConstStringDataPtr g_adaptorDisabledToolTip = new StringData( "Automatically disabled by a render adaptor."); const Color4fDataPtr g_dimmedForegroundColor = new Color4fData( Imath::Color4f( 152, 152, 152, 255 ) / 255.0f ); class RenderPassNameColumn : public StandardPathColumn @@ -482,8 +493,25 @@ class RenderPassNameColumn : public StandardPathColumn { if( const auto renderPassEnabled = runTimeCast( path.property( g_renderPassEnabledPropertyName, canceller ) ) ) { - result.icon = renderPassEnabled->readable() ? g_renderPassIcon : g_disabledRenderPassIcon; - result.foreground = renderPassEnabled->readable() ? nullptr : g_dimmedForegroundColor; + if( renderPassEnabled->readable() ) + { + result.icon = g_renderPassIcon; + } + else + { + result.foreground = g_dimmedForegroundColor; + const auto renderPassEnabledWithoutAdaptors = runTimeCast( path.property( g_renderPassEnabledWithoutAdaptorsPropertyName, canceller ) ); + if( !renderPassEnabledWithoutAdaptors || !renderPassEnabledWithoutAdaptors->readable() ) + { + result.icon = g_disabledRenderPassIcon; + result.toolTip = g_disabledToolTip; + } + else + { + result.icon = g_adaptorDisabledRenderPassIcon; + result.toolTip = g_adaptorDisabledToolTip; + } + } } else {