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

Render Pass Menu #6177

Open
wants to merge 10 commits into
base: 1.5_maintenance
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Improvements
- PlugLayout :
- A warning widget is now displayed when an invalid custom widget is registered.
- `layout:customWidget:<name>:width` and `layout:customWidget:<name>:minimumWidth` metadata registrations are now supported for custom widgets.
- RenderPassEditor / RenderPassChooserWidget : 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we don't need to mention the RenderPassChooserWidget here, since it is in Features?


Fixes
-----
Expand Down
56 changes: 55 additions & 1 deletion python/GafferSceneUI/RenderPassEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) :
Expand Down Expand Up @@ -224,6 +230,34 @@ def pathGroupingFunction() :

return _GafferSceneUI._RenderPassEditor.RenderPassPath.pathGroupingFunction()

@staticmethod
def _createRenderAdaptors() :

adaptors = GafferScene.SceneProcessor()

adaptors["__renderAdaptors"] = GafferScene.SceneAlgo.createRenderAdaptors()
## \todo We currently masquerade as the RenderPassWedge in order to include
# adaptors that disable render passes. We may want to find a more general
# client name for this usage...
adaptors["__renderAdaptors"]["client"].setValue( "RenderPassWedge" )
adaptors["__renderAdaptors"]["in"].setInput( adaptors["in"] )

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

def __repr__( self ) :

return "GafferSceneUI.RenderPassEditor( scriptNode )"
Expand Down Expand Up @@ -265,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 ) :

Expand Down Expand Up @@ -1005,8 +1039,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(),
Expand Down Expand Up @@ -1074,6 +1113,7 @@ def __menuDefinition( self ) :
{
"command" : functools.partial( Gaffer.WeakMethod( self.__setCurrentRenderPass ), name ),
"icon" : self.__renderPassIcon( name, activeIndicator = True ),
"description" : self.__renderPassDescription( name )
}
)

Expand Down Expand Up @@ -1118,6 +1158,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 == "" :
Expand All @@ -1129,6 +1181,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"

Expand Down
41 changes: 41 additions & 0 deletions python/GafferSceneUITest/RenderPassEditorTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
1 change: 1 addition & 0 deletions resources/graphics.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@
"ids" : [
"renderPass",
"disabledRenderPass",
"adaptorDisabledRenderPass",
"renderPassFolder",
"activeRenderPass",
"activeRenderPassFadedHighlighted",
Expand Down
34 changes: 34 additions & 0 deletions resources/graphics.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 31 additions & 3 deletions src/GafferSceneUIModule/RenderPassEditorBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,10 @@ using PathMatcherCache = IECorePreview::LRUCache<IECore::MurmurHash, IECore::Pat
PathMatcherCache g_pathMatcherCache( pathMatcherCacheGetter, 25 );

const InternedString g_renderPassContextName( "renderPass" );
const InternedString g_disableAdaptorsContextName( "renderPassEditor:disableAdaptors" );
const InternedString g_renderPassNamePropertyName( "renderPassPath:name" );
const InternedString g_renderPassEnabledPropertyName( "renderPassPath:enabled" );
const InternedString g_renderPassEnabledWithoutAdaptorsPropertyName( "renderPassPath:enabledWithoutAdaptors" );
const InternedString g_renderPassNamesOption( "option:renderPass:names" );
const InternedString g_renderPassEnabledOption( "option:renderPass:enabled" );

Expand Down Expand Up @@ -303,6 +305,7 @@ class RenderPassPath : public Gaffer::Path
Path::propertyNames( names, canceller );
names.push_back( g_renderPassNamePropertyName );
names.push_back( g_renderPassEnabledPropertyName );
names.push_back( g_renderPassEnabledWithoutAdaptorsPropertyName );
}

IECore::ConstRunTimeTypedPtr property( const IECore::InternedString &name, const IECore::Canceller *canceller = nullptr ) const override
Expand All @@ -315,12 +318,17 @@ class RenderPassPath : public Gaffer::Path
return new StringData( names().back().string() );
}
}
else if( name == g_renderPassEnabledPropertyName )
else if( name == g_renderPassEnabledPropertyName || name == g_renderPassEnabledWithoutAdaptorsPropertyName )
{
const PathMatcher p = pathMatcher( canceller );
if( p.match( names() ) & PathMatcher::ExactMatch )
{
Context::EditableScope scopedContext( getContext() );
if( name == g_renderPassEnabledWithoutAdaptorsPropertyName )
{
const bool disableAdaptors = true;
scopedContext.set<bool>( g_disableAdaptorsContextName, &disableAdaptors );
Comment on lines +338 to +339
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could I make a case for having an enableAdaptors context variable instead?

  • We generally try to avoid inverted "true means off" semantics.
  • I think adaptors being off by default when using RenderPassPath::context() directly probably makes the most sense - because RenderPasses provide the "recipe" for adaptors, we want to see that recipe rather than the end result in the RenderPassEditor. If I made an adaptor that monkied with renderPass:inclusions, I don't think that should be reflected in the RenderPassEditor, but I think it would be currently.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wonder if the context monkeying might be better off in the RenderPassNameColumn, so RenderPassPath is a bit conceptually purer, and the adaptor juggling is in higher-level parts of the UI. I suppose RenderPassPath is already a bit UI-focussed what with the grouping options, but it still feels like maybe it'd be nice to keep it closer to just a raw Path-based view onto a single scene.

}
if( canceller )
{
scopedContext.setCanceller( canceller );
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -482,8 +493,25 @@ class RenderPassNameColumn : public StandardPathColumn
{
if( const auto renderPassEnabled = runTimeCast<const IECore::BoolData>( 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<const IECore::BoolData>( 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
{
Expand Down
6 changes: 1 addition & 5 deletions startup/gui/renderPassEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,8 @@ def __compoundEditorCreated( editor ) :
settingsNode.addChild( GafferScene.ScenePlug( "__renderPassMenuInputScene" ) )
settingsNode.addChild( GafferScene.ScenePlug( "__renderPassMenuAdaptedScene" ) )

settingsNode["__renderPassMenuAdaptors"] = GafferScene.SceneAlgo.createRenderAdaptors()
settingsNode["__renderPassMenuAdaptors"] = GafferSceneUI.RenderPassEditor._createRenderAdaptors()
settingsNode["__renderPassMenuAdaptors"]["in"].setInput( settingsNode["__renderPassMenuInputScene"] )
## \todo We currently masquerade as the RenderPassWedge in order to include
# adaptors that disable render passes. We may want to find a more general
# client name for this usage...
settingsNode["__renderPassMenuAdaptors"]["client"].setValue( "RenderPassWedge" )
settingsNode["__renderPassMenuAdaptedScene"].setInput( settingsNode["__renderPassMenuAdaptors"]["out"] )

GafferUI.CompoundEditor.instanceCreatedSignal().connect( __compoundEditorCreated )
Expand Down