Skip to content

Commit

Permalink
Merge pull request #6234 from murraystevenson/renderPassEditorRenameA…
Browse files Browse the repository at this point in the history
…ction

RenderPassEditor : Add Rename Render Pass menu item
  • Loading branch information
murraystevenson authored Feb 3, 2025
2 parents 6c74bcf + 590783f commit f66652d
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 2 deletions.
6 changes: 6 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Features
Improvements
------------

- RenderPassEditor : Added ability to rename a render pass within the edit scope it was originally created in. A render pass can be renamed from the "Name" column via the "Rename Selected Render Pass..." menu item, double clicking on a cell or selecting a cell and pressing <kbd>Enter</kbd> or <kbd>Return</kbd>.
- VisualiserTool :
- Changed naming requirements for visualising primitive variables. Values in `dataName` now prefix the primitive variable name with `primitiveVariable:`. Setting `dataName` to `vertex:index` will display vertex indices.
- Added `mode` plug. The available modes are :
Expand All @@ -33,6 +34,11 @@ Fixes
- InteractiveRender : Fixed potential leak of `scene:path` context variable when computing the value for `resolvedRenderer`.
- Dispatch app : Fixed poor UI layout in "Completed" dialogue state (#6244).

API
---

- EditScopeAlgo : Added `renameRenderPass()` and `renameRenderPassNonEditableReason()` functions.

1.5.4.1 (relative to 1.5.4.0)
=======

Expand Down
1 change: 1 addition & 0 deletions doc/source/Interface/ControlsAndShortcuts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ Toggle cell selection | {kbd}`Ctrl` + {{leftClick
Edit selected cells | {kbd}`Return`<br>or<br>{kbd}`Enter`
Disable edit | {kbd}`D`
Toggle a render pass as active | {kbd}`Return` or {{leftClick}} {{leftClick}} a cell within the {{activeRenderPass}} column
Rename a render pass | {kbd}`Return` or {{leftClick}} {{leftClick}} a cell within the "Name" column

## Color Chooser Color Field ##

Expand Down
4 changes: 4 additions & 0 deletions include/GafferScene/EditScopeAlgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ GAFFERSCENE_API void removeRenderPassOptionEdit( Gaffer::EditScope *scope, const
GAFFERSCENE_API const Gaffer::GraphComponent *renderPassOptionEditReadOnlyReason( const Gaffer::EditScope *scope, const std::string &renderPass, const std::string &option );

GAFFERSCENE_API const Gaffer::GraphComponent *renderPassesReadOnlyReason( const Gaffer::EditScope *scope );
/// Renames the render pass `oldName` to `newName` inside `scope`. Returns `true` if renaming occurred.
GAFFERSCENE_API bool renameRenderPass( Gaffer::EditScope *scope, const std::string &oldName, const std::string &newName );
/// Returns the reason why a render pass could not be renamed to `newName`.
GAFFERSCENE_API std::optional<std::string> renameRenderPassNonEditableReason( const Gaffer::EditScope *scope, const std::string &newName );

} // namespace EditScopeAlgo

Expand Down
203 changes: 203 additions & 0 deletions python/GafferSceneTest/EditScopeAlgoTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1726,5 +1726,208 @@ def testRenderPassesSerialisation( self ) :

self.assertEqual( s2["editScope"]["out"]["globals"].getValue().get( "option:renderPass:names" ), IECore.StringVectorData( [ "renderPassA" ] ) )

def testRenameRenderPass( self ) :

editScope = Gaffer.EditScope()
editScope.setup( GafferScene.ScenePlug() )

renderPassNames = IECore.StringVectorData( [ "renderPassA", "renderPassB", "renderPassC" ] )
renderPasses = editScope.acquireProcessor( "RenderPasses", createIfNecessary = True )
renderPasses["names"].setValue( renderPassNames )

self.assertEqual( editScope["out"]["globals"].getValue().get( "option:renderPass:names" ), renderPassNames )

self.assertFalse( GafferScene.EditScopeAlgo.renameRenderPass( editScope, "notAPass", "notARenamedPass" ) )
self.assertEqual( editScope["out"]["globals"].getValue().get( "option:renderPass:names" ), renderPassNames )

self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( editScope, "renamedPassB" ) )
self.assertTrue( GafferScene.EditScopeAlgo.renameRenderPass( editScope, "renderPassB", "renamedPassB" ) )
self.assertEqual( editScope["out"]["globals"].getValue().get( "option:renderPass:names" ), IECore.StringVectorData( [ "renderPassA", "renamedPassB", "renderPassC" ] ) )

self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( editScope, "renderPassB" ) )
self.assertTrue( GafferScene.EditScopeAlgo.renameRenderPass( editScope, "renamedPassB", "renderPassB" ) )
self.assertEqual( editScope["out"]["globals"].getValue().get( "option:renderPass:names" ), renderPassNames )

self.assertIsNotNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( editScope, "renderPassA" ) )
self.assertRaises( RuntimeError, GafferScene.EditScopeAlgo.renameRenderPass, editScope, "renderPassC", "renderPassA" )

def testRenameRenderPassDoesNotCreateUnnecessaryProcessors( self ) :

editScope = Gaffer.EditScope()
editScope.setup( GafferScene.ScenePlug() )

self.assertFalse( GafferScene.EditScopeAlgo.renameRenderPass( editScope, "notAPass", "notARenamedPass" ) )

self.assertIsNone( editScope.acquireProcessor( "RenderPasses", createIfNecessary = False ) )
self.assertIsNone( editScope.acquireProcessor( "RenderPassOptionEdits", createIfNecessary = False ) )

renderPasses = editScope.acquireProcessor( "RenderPasses", createIfNecessary = True )
renderPasses["names"].setValue( IECore.StringVectorData( [ "renderPassA", "renderPassB", "renderPassC" ] ) )
self.assertTrue( GafferScene.EditScopeAlgo.renameRenderPass( editScope, "renderPassA", "renamedPassA" ) )

self.assertIsNone( editScope.acquireProcessor( "RenderPassOptionEdits", createIfNecessary = False ) )

def testRenameRenderPassWithOptionEdits( self ) :

s = Gaffer.ScriptNode()

s["options"] = GafferScene.CustomOptions()
s["options"]["options"].addChild( Gaffer.NameValuePlug( "test", IECore.StringData( "" ) ) )

s["editScope"] = Gaffer.EditScope()
s["editScope"].setup( s["options"]["out"] )
s["editScope"]["in"].setInput( s["options"]["out"] )

renderPassNames = IECore.StringVectorData( [ "renderPassA", "renderPassB", "renderPassC" ] )
renderPasses = s["editScope"].acquireProcessor( "RenderPasses", createIfNecessary = True )
renderPasses["names"].setValue( renderPassNames )

for name in renderPassNames :
edit = GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit( s["editScope"], name, "test" )
edit["enabled"].setValue( True )
edit["value"].setValue( name )

with Gaffer.Context() as context :
context["renderPass"] = name
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, name )

self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renamedPassA" ) )
self.assertTrue( GafferScene.EditScopeAlgo.renameRenderPass( s["editScope"], "renderPassA", "renamedPassA" ) )
self.assertEqual( s["editScope"]["out"]["globals"].getValue().get( "option:renderPass:names" ), IECore.StringVectorData( [ "renamedPassA", "renderPassB", "renderPassC" ] ) )

with Gaffer.Context() as context :
context["renderPass"] = "renamedPassA"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "renderPassA" )
for name in renderPassNames :
context["renderPass"] = name
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, name if name != "renderPassA" else "" )

# Renaming renderPassB to a previously used name is permitted.
self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassA" ) )
self.assertTrue( GafferScene.EditScopeAlgo.renameRenderPass( s["editScope"], "renderPassB", "renderPassA" ) )
self.assertEqual( s["editScope"]["out"]["globals"].getValue().get( "option:renderPass:names" ), IECore.StringVectorData( [ "renamedPassA", "renderPassA", "renderPassC" ] ) )

with Gaffer.Context() as context :
context["renderPass"] = "renderPassB"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "" )

context["renderPass"] = "renderPassA"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "renderPassB" )

context["renderPass"] = "renamedPassA"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "renderPassA" )

context["renderPass"] = "renderPassC"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "renderPassC" )

# Renaming renderPassC to a currently existing name should error and not change anything.
self.assertIsNotNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassA" ) )
self.assertRaises( RuntimeError, GafferScene.EditScopeAlgo.renameRenderPass, s["editScope"], "renderPassC", "renderPassA" )
self.assertEqual( s["editScope"]["out"]["globals"].getValue().get( "option:renderPass:names" ), IECore.StringVectorData( [ "renamedPassA", "renderPassA", "renderPassC" ] ) )

with Gaffer.Context() as context :
context["renderPass"] = "renderPassB"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "" )

context["renderPass"] = "renderPassA"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "renderPassB" )

context["renderPass"] = "renamedPassA"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "renderPassA" )

context["renderPass"] = "renderPassC"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "renderPassC" )

# Reversing our rename steps should return things to the original state.
self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassB" ) )
self.assertTrue( GafferScene.EditScopeAlgo.renameRenderPass( s["editScope"], "renderPassA", "renderPassB" ) )
self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassA" ) )
self.assertTrue( GafferScene.EditScopeAlgo.renameRenderPass( s["editScope"], "renamedPassA", "renderPassA" ) )

self.assertEqual( s["editScope"]["out"]["globals"].getValue().get( "option:renderPass:names" ), renderPassNames )
with Gaffer.Context() as context :
context["renderPass"] = "renamedPassA"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "" )
for name in renderPassNames :
context["renderPass"] = name
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, name )

def testRenameRenderPassWithOnlyOptionEdits( self ) :

s = Gaffer.ScriptNode()

s["options"] = GafferScene.CustomOptions()
s["options"]["options"].addChild( Gaffer.NameValuePlug( "test", IECore.StringData( "" ) ) )

s["editScope"] = Gaffer.EditScope()
s["editScope"].setup( s["options"]["out"] )
s["editScope"]["in"].setInput( s["options"]["out"] )

for name in [ "renderPassA", "renderPassB", "renderPassC" ] :
edit = GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit( s["editScope"], name, "test" )
edit["enabled"].setValue( True )
edit["value"].setValue( name )

self.assertIsNone( s["editScope"].acquireProcessor( "RenderPasses", createIfNecessary = False ) )

self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renamedPassA" ) )
self.assertTrue( GafferScene.EditScopeAlgo.renameRenderPass( s["editScope"], "renderPassA", "renamedPassA" ) )
self.assertIsNone( s["editScope"].acquireProcessor( "RenderPasses", createIfNecessary = False ) )

with Gaffer.Context() as context :
context["renderPass"] = "renderPassA"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "" )

for name in [ "renamedPassA", "renderPassB", "renderPassC" ] :
context["renderPass"] = name
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "renderPassA" if name == "renamedPassA" else name )

self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassA" ) )
self.assertTrue( GafferScene.EditScopeAlgo.renameRenderPass( s["editScope"], "renamedPassA", "renderPassA" ) )

with Gaffer.Context() as context :
context["renderPass"] = "renamedPassA"
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, "" )

for name in [ "renderPassA", "renderPassB", "renderPassC" ] :
context["renderPass"] = name
self.assertEqual( s["editScope"]["out"].globals()["option:test"].value, name )

def testRenameRenderPassNonEditableReason( self ) :

s = Gaffer.ScriptNode()

s["options"] = GafferScene.CustomOptions()
s["options"]["options"].addChild( Gaffer.NameValuePlug( "test", IECore.StringData( "" ) ) )

s["editScope"] = Gaffer.EditScope()
s["editScope"].setup( s["options"]["out"] )
s["editScope"]["in"].setInput( s["options"]["out"] )

self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassA" ) )

edit = GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit( s["editScope"], "renderPassA", "test" )
edit["enabled"].setValue( True )
edit["value"].setValue( "renderPassA" )

self.assertEqual( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassA" ), "Edits already exist for render pass \"renderPassA\" in editScope.RenderPassOptionEdits" )
self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassB" ) )

renderPasses = s["editScope"].acquireProcessor( "RenderPasses", createIfNecessary = True )
renderPasses["names"].setValue( IECore.StringVectorData( [ "renderPassA" ] ) )
self.assertEqual( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassA" ), "A render pass named \"renderPassA\" already exists in editScope.RenderPasses" )
self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassB" ) )

renderPasses["names"].setValue( IECore.StringVectorData( [ "renderPassB" ] ) )
self.assertEqual( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassA" ), "Edits already exist for render pass \"renderPassA\" in editScope.RenderPassOptionEdits" )
self.assertEqual( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassB" ), "A render pass named \"renderPassB\" already exists in editScope.RenderPasses" )
self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassC" ) )

renderPasses["names"].setValue( IECore.StringVectorData( [ "renderPassB", "renderPassC" ] ) )
self.assertEqual( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassA" ), "Edits already exist for render pass \"renderPassA\" in editScope.RenderPassOptionEdits" )
self.assertEqual( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassB" ), "A render pass named \"renderPassB\" already exists in editScope.RenderPasses" )
self.assertEqual( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassC" ), "A render pass named \"renderPassC\" already exists in editScope.RenderPasses" )
self.assertIsNone( GafferScene.EditScopeAlgo.renameRenderPassNonEditableReason( s["editScope"], "renderPassD" ) )

if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit f66652d

Please sign in to comment.