Skip to content

Commit

Permalink
Merge pull request #5839 from johnhaddon/scopedAdaptors
Browse files Browse the repository at this point in the history
SceneAlgo : Add client/renderer-specific adaptor registrations
  • Loading branch information
johnhaddon authored May 2, 2024
2 parents 8d15199 + 12152d1 commit 3cbfe9d
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 18 deletions.
5 changes: 5 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ Fixes

- Arnold : Fixed rendering of `ai:volume` shaders loaded from USD (#5830).

API
---

- SceneAlgo : Added mechanism for scoping render adaptors to specific clients and/or renderers.

1.4.2.0 (relative to 1.4.1.0)
=======

Expand Down
27 changes: 25 additions & 2 deletions include/GafferScene/SceneAlgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,16 +308,39 @@ GAFFERSCENE_API void validateName( IECore::InternedString name );

/// Render Adaptors
/// ===============
///
/// Adaptors are nodes that are implicitly appended to the node graph
/// before rendering, allowing them to make final modifications
/// to the scene. They can be used to do "delayed resolution" of custom
/// features, enforce pipeline policies or customise output for specific
/// renderers or clients.
///
/// Adaptors are created by clients such as the Render and InteractiveRender
/// nodes and the SceneView which implements Gaffer's 3D Viewer.
///
/// Adaptors are implemented as SceneProcessors with optional `client`
/// and `renderer` input StringPlugs to inform them of the scope in
/// which they are operating. Custom adaptors can be registered for
/// any purpose by calling `registerRenderAdaptor()`. Examples include
/// `startup/GafferScene/renderSetAdaptor.py` which implements features
/// for the RenderPassEditor, and `startup/GafferArnold/ocio.py` which
/// configures a default colour manager for Arnold.

/// Function to return a SceneProcessor used to adapt the
/// scene for rendering.
using RenderAdaptor = std::function<SceneProcessorPtr ()>;
/// Registers an adaptor.
/// Registers an adaptor to be applied when `client` renders using `renderer`. Standard
/// wildcards may be used to match multiple clients and/or renderers.
GAFFERSCENE_API void registerRenderAdaptor( const std::string &name, RenderAdaptor adaptor, const std::string &client, const std::string &renderer );
/// Equivalent to `registerRenderAdaptor( name, adaptor, "*", "*" )`.
/// \todo Remove
GAFFERSCENE_API void registerRenderAdaptor( const std::string &name, RenderAdaptor adaptor );
/// Removes a previously registered adaptor.
GAFFERSCENE_API void deregisterRenderAdaptor( const std::string &name );
/// Returns a SceneProcessor that will apply all the currently
/// registered adaptors.
/// registered adaptors. It is the client's responsibility to set
/// the adaptor's `client` and `renderer` string plugs to appropriate
/// values before use.
GAFFERSCENE_API SceneProcessorPtr createRenderAdaptors();

/// Apply Camera Globals
Expand Down
70 changes: 70 additions & 0 deletions python/GafferSceneTest/SceneAlgoTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2189,6 +2189,76 @@ def a() :
self.assertScenesEqual( defaultAdaptors["out"], defaultAdaptors2["out"] )
self.assertSceneHashesEqual( defaultAdaptors["out"], defaultAdaptors2["out"] )

def testRenderAdaptorScope( self ) :

def adaptor() :

result = GafferScene.CustomOptions()
result["options"].addChild( Gaffer.NameValuePlug( "adapted", True ) )
return result

for clientPattern, rendererPattern, client, renderer, expectAdapted in (
( "*", "*", None, None, True ),
( "*", "*", "Render", "Arnold", True ),
( "*", "Arnold", "Render", "Arnold", True ),
( "*", "Arnold", "Render", "Cycles", False ),
( "Render", "*", "Render", "Arnold", True ),
( "Render", "*", "InteractiveRender", "Arnold", False ),
( "Render InteractiveRender", "*", "Render", "Arnold", True ),
( "Render InteractiveRender", "*", "InteractiveRender", "Arnold", True ),
( "Render InteractiveRender", "*", "SceneView", "Arnold", False ),
( "Render", "Arnold", "SceneView", "Arnold", False ),
( "Render", "Arnold", "Render", "Arnold", True ),
) :
with self.subTest( clientPattern = clientPattern, rendererPattern = rendererPattern, client = client, renderer = renderer ) :

GafferScene.SceneAlgo.registerRenderAdaptor( "Test", adaptor, clientPattern, rendererPattern )
self.addCleanup( GafferScene.SceneAlgo.deregisterRenderAdaptor, "Test" )

adaptors = GafferScene.SceneAlgo.createRenderAdaptors()
if client is not None :
adaptors["client"].setValue( client )
if renderer is not None :
adaptors["renderer"].setValue( renderer )

if expectAdapted :
self.assertIn( "option:adapted", adaptors["out"].globals() )
else :
self.assertNotIn( "option:adapted", adaptors["out"].globals() )

def testRenderAdaptorScopePlugs( self ) :

def adaptor() :

result = GafferScene.CustomOptions()
result["client"] = Gaffer.StringPlug()
result["renderer"] = Gaffer.StringPlug()

result["options"].addChild( Gaffer.NameValuePlug( "theClient", "" ) )
result["options"].addChild( Gaffer.NameValuePlug( "theRenderer", "" ) )

result["options"][0]["value"].setInput( result["client"] )
result["options"][1]["value"].setInput( result["renderer"] )

return result

for client, renderer in [
( "*", "*" ),
( "SceneView", "Arnold" ),
] :

with self.subTest( client = client, renderer = renderer ) :

GafferScene.SceneAlgo.registerRenderAdaptor( "Test", adaptor, client, renderer )
self.addCleanup( GafferScene.SceneAlgo.deregisterRenderAdaptor, "Test" )

adaptors = GafferScene.SceneAlgo.createRenderAdaptors()
adaptors["client"].setValue( "SceneView" )
adaptors["renderer"].setValue( "Arnold" )

self.assertEqual( adaptors["out"].globals()["option:theClient"].value, "SceneView" )
self.assertEqual( adaptors["out"].globals()["option:theRenderer"].value, "Arnold" )

def testNullAdaptor( self ) :

def a() :
Expand Down
2 changes: 2 additions & 0 deletions src/GafferScene/InteractiveRender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ InteractiveRender::InteractiveRender( const IECore::InternedString &rendererType
SceneProcessorPtr adaptors = SceneAlgo::createRenderAdaptors();
setChild( "__adaptors", adaptors );
adaptors->inPlug()->setInput( inPlug() );
adaptors->getChild<StringPlug>( "client" )->setValue( "InteractiveRender" );
adaptors->getChild<StringPlug>( "renderer" )->setInput( resolvedRendererPlug() );
adaptedInPlug()->setInput( adaptors->outPlug() );

outPlug()->setInput( inPlug() );
Expand Down
2 changes: 2 additions & 0 deletions src/GafferScene/Render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ Render::Render( const IECore::InternedString &rendererType, const std::string &n
SceneProcessorPtr adaptors = GafferScene::SceneAlgo::createRenderAdaptors();
setChild( "__adaptors", adaptors );
adaptors->inPlug()->setInput( inPlug() );
adaptors->getChild<StringPlug>( "client" )->setValue( "Render" );
adaptors->getChild<StringPlug>( "renderer" )->setInput( resolvedRendererPlug() );
adaptedInPlug()->setInput( adaptors->outPlug() );

outPlug()->setInput( inPlug() );
Expand Down
110 changes: 97 additions & 13 deletions src/GafferScene/SceneAlgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "Gaffer/Context.h"
#include "Gaffer/Expression.h"
#include "Gaffer/Monitor.h"
#include "Gaffer/NameSwitch.h"
#include "Gaffer/Private/IECorePreview/LRUCache.h"
#include "Gaffer/Process.h"
#include "Gaffer/ScriptNode.h"
Expand Down Expand Up @@ -1327,19 +1328,84 @@ void GafferScene::SceneAlgo::validateName( IECore::InternedString name )
namespace
{

using RenderAdaptors = boost::container::flat_map<string, SceneAlgo::RenderAdaptor>;
struct RenderAdaptorRegistration
{
SceneAlgo::RenderAdaptor creator;
std::string client;
std::string renderer;
};

using RenderAdaptors = boost::container::flat_map<string, RenderAdaptorRegistration>;

RenderAdaptors &renderAdaptors()
{
static RenderAdaptors *a = new RenderAdaptors;
return *a;
}

SceneProcessorPtr createRenderAdaptor( const RenderAdaptorRegistration &registration )
{
SceneProcessorPtr adaptor = registration.creator();
if( !adaptor )
{
return nullptr;
}

if( registration.client == "*" && registration.renderer == "*" )
{
return adaptor;
}

// Wrap using NameSwitches to enable/disable based on client/renderer.

SceneProcessorPtr wrapper = new SceneProcessor;
wrapper->addChild( new StringPlug( "client" ) );
wrapper->addChild( new StringPlug( "renderer" ) );
wrapper->addChild( adaptor );
adaptor->inPlug()->setInput( wrapper->inPlug() );

ScenePlug *out = adaptor->outPlug();

for( const auto &scope : { string( "client" ), string( "renderer" ) } )
{
if( auto p = adaptor->getChild<StringPlug>( scope ) )
{
p->setInput( wrapper->getChild<StringPlug>( scope ) );
}

const std::string &scopeValue = scope == "client" ? registration.client : registration.renderer;
if( scopeValue == "*" )
{
continue;
}

NameSwitchPtr nameSwitch = new NameSwitch( scope + "Switch" );
wrapper->addChild( nameSwitch );
nameSwitch->setup( out );
nameSwitch->inPlugs()->getChild<NameValuePlug>( 0 )->valuePlug()->setInput( wrapper->inPlug() );
nameSwitch->inPlugs()->getChild<NameValuePlug>( 1 )->valuePlug()->setInput( out );
nameSwitch->selectorPlug()->setInput( wrapper->getChild<Plug>( scope ) );
/// \todo Remove "unspecified" - see matching comment in `createRenderAdaptors()`.
nameSwitch->inPlugs()->getChild<NameValuePlug>( 1 )->namePlug()->setValue( scopeValue + " unspecified" );

out = nameSwitch->outPlug()->getChild<ScenePlug>( "value" );
}

wrapper->outPlug()->setInput( out );

return wrapper;
}

} // namespace

void GafferScene::SceneAlgo::registerRenderAdaptor( const std::string &name, RenderAdaptor adaptor, const std::string &client, const std::string &renderer )
{
renderAdaptors()[name] = { adaptor, client, renderer };
}

void GafferScene::SceneAlgo::registerRenderAdaptor( const std::string &name, SceneAlgo::RenderAdaptor adaptor )
{
renderAdaptors()[name] = adaptor;
registerRenderAdaptor( name, adaptor, "*", "*" );
}

void GafferScene::SceneAlgo::deregisterRenderAdaptor( const std::string &name )
Expand All @@ -1350,26 +1416,44 @@ void GafferScene::SceneAlgo::deregisterRenderAdaptor( const std::string &name )
SceneProcessorPtr GafferScene::SceneAlgo::createRenderAdaptors()
{
SceneProcessorPtr result = new SceneProcessor;
StringPlugPtr clientPlug = new StringPlug( "client" );
StringPlugPtr rendererPlug = new StringPlug( "renderer" );
result->addChild( clientPlug );
result->addChild( rendererPlug );

/// \todo Remove, and require all clients to set the plugs themselves.
clientPlug->setValue( "unspecified" );
rendererPlug->setValue( "unspecified" );

ScenePlug *in = result->inPlug();

const RenderAdaptors &a = renderAdaptors();
for( RenderAdaptors::const_iterator it = a.begin(), eIt = a.end(); it != eIt; ++it )
const RenderAdaptors &adaptors = renderAdaptors();
for( const auto &[name, a] : adaptors )
{
SceneProcessorPtr adaptor = it->second();
if( adaptor )
{
result->addChild( adaptor );
adaptor->inPlug()->setInput( in );
in = adaptor->outPlug();
}
else
SceneProcessorPtr adaptor = createRenderAdaptor( a );
if( !adaptor )
{
IECore::msg(
IECore::Msg::Warning, "SceneAlgo::createRenderAdaptors",
fmt::format( "Adaptor \"{}\" returned null", it->first )
fmt::format( "Adaptor \"{}\" returned null", name )
);
continue;
}

adaptor->setName( name );
if( auto s = adaptor->getChild<StringPlug>( "client" ) )
{
s->setInput( clientPlug.get() );
}

if( auto s = adaptor->getChild<StringPlug>( "renderer" ) )
{
s->setInput( rendererPlug.get() );
}

result->addChild( adaptor );
adaptor->inPlug()->setInput( in );
in = adaptor->outPlug();
}

result->outPlug()->setInput( in );
Expand Down
6 changes: 3 additions & 3 deletions src/GafferSceneModule/SceneAlgoBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,9 @@ struct RenderAdaptorWrapper

};

void registerRenderAdaptorWrapper( const std::string &name, object adaptor )
void registerRenderAdaptorWrapper( const std::string &name, object adaptor, const std::string &client, const std::string &renderer )
{
SceneAlgo::registerRenderAdaptor( name, RenderAdaptorWrapper( adaptor ) );
SceneAlgo::registerRenderAdaptor( name, RenderAdaptorWrapper( adaptor ), client, renderer );
}

void applyCameraGlobalsWrapper( IECoreScene::Camera &camera, const IECore::CompoundObject &globals, const ScenePlug &scene )
Expand Down Expand Up @@ -441,7 +441,7 @@ void bindSceneAlgo()

// Render adaptors

def( "registerRenderAdaptor", &registerRenderAdaptorWrapper );
def( "registerRenderAdaptor", &registerRenderAdaptorWrapper, ( arg( "name" ), arg( "adaptor" ), arg( "client" ) = "*", arg( "renderer" ) = "*" ) );
def( "deregisterRenderAdaptor", &SceneAlgo::deregisterRenderAdaptor );
def( "createRenderAdaptors", &SceneAlgo::createRenderAdaptors );

Expand Down
2 changes: 2 additions & 0 deletions src/GafferSceneUI/SceneView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2015,6 +2015,7 @@ SceneView::SceneView( const std::string &name )

SceneProcessorPtr adaptors = SceneAlgo::createRenderAdaptors();
preprocessor->addChild( adaptors );
adaptors->getChild<StringPlug>( "client" )->setValue( "SceneView" );
adaptors->inPlug()->setInput( deleteObject->outPlug() );

// add in the node from the ShadingMode
Expand All @@ -2031,6 +2032,7 @@ SceneView::SceneView( const std::string &name )

preprocessor->addChild( m_renderer->preprocessor() );
m_renderer->preprocessor()->inPlug()->setInput( m_drawingMode->preprocessor()->outPlug() );
adaptors->getChild<StringPlug>( "renderer" )->setInput( getChild<Plug>( "renderer" )->getChild<StringPlug>( "name" ) );

// remove motion blur, because the opengl renderer doesn't support it.

Expand Down

0 comments on commit 3cbfe9d

Please sign in to comment.