Skip to content

Commit

Permalink
Merge pull request #5587 from murraystevenson/optionInspector
Browse files Browse the repository at this point in the history
GafferSceneUI : Add OptionInspector
  • Loading branch information
johnhaddon authored Dec 19, 2023
2 parents 9fe2e9c + 91c4ec2 commit 6d6ec53
Show file tree
Hide file tree
Showing 11 changed files with 1,731 additions and 4 deletions.
3 changes: 3 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
1.3.x.x (relative to 1.3.9.0)
=======

API
---

- EditScopeAlgo : Added support for editing options for a specific render pass.

1.3.9.0 (relative to 1.3.8.0)
=======
Expand Down
11 changes: 11 additions & 0 deletions include/GafferScene/EditScopeAlgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,17 @@ GAFFERSCENE_API Gaffer::TweakPlug *acquireOptionEdit( Gaffer::EditScope *scope,
GAFFERSCENE_API void removeOptionEdit( Gaffer::EditScope *scope, const std::string &option );
GAFFERSCENE_API const Gaffer::GraphComponent *optionEditReadOnlyReason( const Gaffer::EditScope *scope, const std::string &option );


// Render Pass Options
// ===================
//
// These methods edit scene options for a particular render pass.

GAFFERSCENE_API bool hasRenderPassOptionEdit( const Gaffer::EditScope *scope, const std::string &renderPass, const std::string &option );
GAFFERSCENE_API Gaffer::TweakPlug *acquireRenderPassOptionEdit( Gaffer::EditScope *scope, const std::string &renderPass, const std::string &option, bool createIfNecessary = true );
GAFFERSCENE_API void removeRenderPassOptionEdit( Gaffer::EditScope *scope, const std::string &renderPass, const std::string &option );
GAFFERSCENE_API const Gaffer::GraphComponent *renderPassOptionEditReadOnlyReason( const Gaffer::EditScope *scope, const std::string &renderPass, const std::string &option );

} // namespace EditScopeAlgo

} // namespace GafferScene
84 changes: 84 additions & 0 deletions include/GafferSceneUI/Private/OptionInspector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2023, Cinesite VFX Ltd. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above
// copyright notice, this list of conditions and the following
// disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided with
// the distribution.
//
// * Neither the name of John Haddon nor the names of
// any other contributors to this software may be used to endorse or
// promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#pragma once

#include "GafferSceneUI/Export.h"

#include "GafferSceneUI/Private/Inspector.h"

namespace GafferSceneUI
{

namespace Private
{

class GAFFERSCENEUI_API OptionInspector : public Inspector
{

public :

OptionInspector(
const GafferScene::ScenePlugPtr &scene,
const Gaffer::PlugPtr &editScope,
IECore::InternedString option
);

IE_CORE_DECLAREMEMBERPTR( OptionInspector );

protected :

GafferScene::SceneAlgo::History::ConstPtr history() const override;
IECore::ConstObjectPtr value( const GafferScene::SceneAlgo::History *history ) const override;
Gaffer::ValuePlugPtr source( const GafferScene::SceneAlgo::History *history, std::string &editWarning ) const override;
EditFunctionOrFailure editFunction( Gaffer::EditScope *scope, const GafferScene::SceneAlgo::History *history ) const override;

private :

void plugDirtied( Gaffer::Plug *plug );
void plugMetadataChanged( IECore::InternedString key, const Gaffer::Plug *plug );
void nodeMetadataChanged( IECore::InternedString key, const Gaffer::Node *node );

const GafferScene::ScenePlugPtr m_scene;
const IECore::InternedString m_option;

};

IE_CORE_DECLAREPTR( OptionInspector )

} // namespace Private

} // namespace GafferSceneUI
200 changes: 200 additions & 0 deletions python/GafferSceneTest/EditScopeAlgoTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,206 @@ def testOptionEditSerialisation( self ) :
self.assertEqual( edit["enabled"].getValue(), True )
self.assertEqual( edit["value"].getValue(), 20 )

def testRenderPassOptionEdits( self ) :

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

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

for renderPass, value in [
( "renderPassA", 20 ),
( "renderPassB", 30 ),
] :

self.assertFalse( GafferScene.EditScopeAlgo.hasRenderPassOptionEdit( editScope, renderPass, "test" ) )
self.assertIsNone(
GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit(
editScope, renderPass, "test", createIfNecessary = False
)
)

edit = GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit( editScope, renderPass, "test" )
self.assertIsInstance( edit, Gaffer.TweakPlug )
self.assertIsInstance( edit["value"], Gaffer.IntPlug )
self.assertEqual( edit["mode"].getValue(), Gaffer.TweakPlug.Mode.Create )
self.assertEqual( edit["value"].getValue(), 10 )
self.assertEqual( edit["enabled"].getValue(), False )
self.assertEqual( editScope["RenderPassOptionEdits"]["OptionTweaks"]["enabled"].getInput(), editScope["RenderPassOptionEdits"]["enabled"] )

edit["enabled"].setValue( True )
edit["value"].setValue( value )
self.assertIn( "option:test", editScope["out"].globals() )
with Gaffer.Context() as context :
context["renderPass"] = renderPass
self.assertEqual( editScope["out"].globals()["option:test"].value, value )

self.assertEqual(
GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit( editScope, renderPass, "test" ),
edit
)

self.assertEqual( editScope["out"].globals()["option:test"].value, 10 )

def testRenderPassOptionEditRemoval( self ) :

s = Gaffer.ScriptNode()

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

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

edit1 = GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit( s["scope"], "renderPassA", "test" )

self.assertEqual( len( s["scope"]["RenderPassOptionEdits"]["edits"].children() ), 2 )
self.assertEqual( s["scope"]["RenderPassOptionEdits"]["edits"][1]["cells"]["test"]["value"], edit1 )
edit1["enabled"].setValue( True )

GafferScene.EditScopeAlgo.removeRenderPassOptionEdit( s["scope"], "renderPassA", "test" )

# Removing an edit will result in it being disabled, as it could be sharing a spreadsheet
# row or column with other edits.
self.assertEqual( s["scope"]["RenderPassOptionEdits"]["edits"][1]["cells"]["test"]["value"], edit1 )
self.assertFalse( edit1["enabled"].getValue() )

def testRenderPassOptionEditReadOnlyReason( self ) :

s = Gaffer.ScriptNode()

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

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

s["box"] = Gaffer.Box.create( s, Gaffer.StandardSet( [ s["options"], s["scope"] ] ) )

self.assertIsNone(
GafferScene.EditScopeAlgo.renderPassOptionEditReadOnlyReason( s["box"]["scope"], "renderPassA", "test" )
)

for component in ( s["box"], s["box"]["scope"] ) :
Gaffer.MetadataAlgo.setReadOnly( component, True )
self.assertEqual(
GafferScene.EditScopeAlgo.renderPassOptionEditReadOnlyReason( s["box"]["scope"], "renderPassA", "test" ),
s["box"]
)

Gaffer.MetadataAlgo.setReadOnly( s["box"], False )
self.assertEqual(
GafferScene.EditScopeAlgo.renderPassOptionEditReadOnlyReason( s["box"]["scope"], "renderPassA", "test" ),
s["box"]["scope"]
)

Gaffer.MetadataAlgo.setReadOnly( s["box"]["scope"], False )
GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit( s["box"]["scope"], "renderPassA", "test" )

self.assertIsNone(
GafferScene.EditScopeAlgo.renderPassOptionEditReadOnlyReason( s["box"]["scope"], "renderPassA", "test" )
)

candidateComponents = (
s["box"]["scope"]["RenderPassOptionEdits"]["edits"][1]["cells"]["test"]["value"]["value"],
s["box"]["scope"]["RenderPassOptionEdits"]["edits"][1]["cells"]["test"]["value"]["mode"],
s["box"]["scope"]["RenderPassOptionEdits"]["edits"][1]["cells"]["test"]["value"]["enabled"],
s["box"]["scope"]["RenderPassOptionEdits"]["edits"][1]["cells"]["test"]["value"]["name"],
s["box"]["scope"]["RenderPassOptionEdits"]["edits"][1]["cells"]["test"]["value"],
s["box"]["scope"]["RenderPassOptionEdits"]["edits"][1]["cells"]["test"],
s["box"]["scope"]["RenderPassOptionEdits"]["edits"][1]["cells"],
s["box"]["scope"]["RenderPassOptionEdits"]["edits"][1],
s["box"]["scope"]["RenderPassOptionEdits"]["edits"],
s["box"]["scope"]["RenderPassOptionEdits"],
s["box"]["scope"],
s["box"]
)

for component in candidateComponents :
Gaffer.MetadataAlgo.setReadOnly( component, True )
self.assertEqual(
GafferScene.EditScopeAlgo.renderPassOptionEditReadOnlyReason(
s["box"]["scope"],
"renderPassA",
"test"
),
component
)

for component in candidateComponents :
Gaffer.MetadataAlgo.setReadOnly( component, False )

# We can't remove option edits, they're just disabled (as the row is shared with
# other options), so we try to create one for another option instead.
s["box"]["options"]["options"]["NameValuePlug"]["name"].setValue( "test2" )

self.assertIsNone(
GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit(
s["box"]["scope"],
"renderPassA",
"test2",
createIfNecessary = False
)
)

for component in (
s["box"]["scope"]["RenderPassOptionEdits"]["edits"],
s["box"]["scope"]["RenderPassOptionEdits"]
) :
Gaffer.MetadataAlgo.setReadOnly( component, True )
self.assertEqual(
GafferScene.EditScopeAlgo.renderPassOptionEditReadOnlyReason(
s["box"]["scope"],
"renderPassA",
"test"
),
component
)

def testRenderPassOptionEditExceptions( self ) :

options = GafferScene.Options()
editScope = Gaffer.EditScope()
editScope.setup( options["out"] )
editScope["in"].setInput( options["out"] )
emptyKeys = editScope.keys()

with self.assertRaisesRegex( RuntimeError, 'Option "bogus" does not exist' ) :
GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit( editScope, "renderPassA", "bogus" )
self.assertEqual( editScope.keys(), emptyKeys )

def testRenderPassOptionEditSerialisation( self ) :

script = Gaffer.ScriptNode()

script["options"] = GafferScene.CustomOptions()
script["options"]["options"].addChild( Gaffer.NameValuePlug( "test", IECore.IntData( 10 ) ) )

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

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

script2 = Gaffer.ScriptNode()
script2.execute( script.serialise() )

with Gaffer.Context() as context :
context["renderPass"] = "renderPassA"
self.assertScenesEqual( script2["editScope"]["out"], script["editScope"]["out"] )

edit = GafferScene.EditScopeAlgo.acquireRenderPassOptionEdit(
script2["editScope"], "renderPassA", "test", createIfNecessary = False
)
self.assertIsNotNone( edit )
self.assertEqual( edit["enabled"].getValue(), True )
self.assertEqual( edit["value"].getValue(), 20 )

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

0 comments on commit 6d6ec53

Please sign in to comment.