diff --git a/Changes.md b/Changes.md index 19fe4d9feb4..68bd522666a 100644 --- a/Changes.md +++ b/Changes.md @@ -6,6 +6,10 @@ Improvements - CameraTweaks : Added `ignoreMissing` plug to align behaviour with the other Tweaks nodes. - AttributeTweaks : The `{source}` substitution for `linkedLights` now expands to `defaultLights` if the attribute doesn't exist yet. This makes tweaks such as `({source}) - unwantedLights` reliable even if no light links have been authored yet. +- 3Delight : + - Added translation of Gaffer render:* sets to NSI sets. + - Added NSI lightset connections driven by Gaffer lightgroup output parameter supporting space separated light objects and render sets. + - Updated 3Delight output presets to include a lightgroup parameter (empty by default). Breaking Changes ---------------- @@ -13,6 +17,7 @@ Breaking Changes - CameraTweaks : `Replace` mode now errors if the input parameter does not exist. Use `Create` mode or the new `ignoreMissing` plug instead. - TweakPlug : Remove deprecated `MissingMode::IgnoreOrReplace`. - AttributeTweaks : `Replace` mode no longer errors if the `linkedLights` attribute doesn't exist. +- 3Delight : Switched NSI outputs creation from after global options to after scene objects both for batch and interactive rendering. 1.4.x.x (relative to 1.4.4.0) ======= diff --git a/python/IECoreDelightTest/RendererTest.py b/python/IECoreDelightTest/RendererTest.py index 0d35338c23a..a1bc9c44269 100644 --- a/python/IECoreDelightTest/RendererTest.py +++ b/python/IECoreDelightTest/RendererTest.py @@ -632,6 +632,54 @@ def testObjectInstancing( self ) : self.assertEqual( nsi.count( '"transform"' ), 2 ) self.assertEqual( nsi.count( '"mesh"' ), 1 ) + def testRenderSets( self ) : + + r = GafferScene.Private.IECoreScenePreview.Renderer.create( + "3Delight", + GafferScene.Private.IECoreScenePreview.Renderer.RenderType.SceneDescription, + str( self.temporaryDirectory() / "test.nsia" ), + ) + + m = IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( -1 ), imath.V2f( 1 ) ) ) + a = IECore.CompoundObject() + a["sets"] = IECore.InternedStringVectorData( [ "testSet" ] ) + + r.object( "testPlane1", m, r.attributes( a ) ) + r.object( "testPlane2", m, r.attributes( a ) ) + + r.render() + del r + + nsi = self.__parseDict( self.temporaryDirectory() / "test.nsia" ) + self.assertIn( "render:testSet", nsi ) + self.assertEqual( nsi["render:testSet"]["members"], ['', ''] ) + + def testLightgroups( self ) : + + r = GafferScene.Private.IECoreScenePreview.Renderer.create( + "3Delight", + GafferScene.Private.IECoreScenePreview.Renderer.RenderType.SceneDescription, + str( self.temporaryDirectory() / "test.nsia" ) + ) + + r.output( + "test", + IECoreScene.Output( + "beauty.exr", + "exr", + "rgba", + { + "lightgroup" : "render:testSet", + } + ) + ) + + r.render() + del r + + nsi = self.__parseDict( self.temporaryDirectory() / "test.nsia" ) + self.assertEqual( nsi["outputLayer:test"]["lightset"], [""] ) + def testTransform( self ) : r = GafferScene.Private.IECoreScenePreview.Renderer.create( diff --git a/src/GafferScene/Render.cpp b/src/GafferScene/Render.cpp index bd50011ea62..9b988771c37 100644 --- a/src/GafferScene/Render.cpp +++ b/src/GafferScene/Render.cpp @@ -51,6 +51,8 @@ #include "IECore/ObjectPool.h" +#include "boost/algorithm/string.hpp" + #include #include @@ -348,7 +350,10 @@ void Render::executeInternal( bool flushCaches ) const Monitor::Scope performanceMonitorScope( performanceMonitor ); GafferScene::Private::RendererAlgo::outputOptions( renderOptions.globals.get(), renderer.get() ); - GafferScene::Private::RendererAlgo::outputOutputs( inPlug(), renderOptions.globals.get(), renderer.get() ); + if( !( boost::starts_with( rendererType, "3Delight" ) ) ) + { + GafferScene::Private::RendererAlgo::outputOutputs( inPlug(), renderOptions.globals.get(), renderer.get() ); + } { // Using nested scope so that we free the memory used by `renderSets` @@ -363,6 +368,11 @@ void Render::executeInternal( bool flushCaches ) const GafferScene::Private::RendererAlgo::outputObjects( adaptedInPlug(), renderOptions, renderSets, &lightLinks, renderer.get() ); } + if( boost::starts_with( rendererType, "3Delight" ) ) + { + GafferScene::Private::RendererAlgo::outputOutputs( inPlug(), renderOptions.globals.get(), renderer.get() ); + } + if( renderScope.sceneTranslationOnly() ) { return; diff --git a/src/GafferScene/RenderController.cpp b/src/GafferScene/RenderController.cpp index a5748f76dfd..6baae373f80 100644 --- a/src/GafferScene/RenderController.cpp +++ b/src/GafferScene/RenderController.cpp @@ -1620,12 +1620,20 @@ void RenderController::updateInternal( const ProgressCallback &callback, const I { try { + RenderOptions dl_renderOptions; // Update globals if( m_dirtyGlobalComponents & GlobalsGlobalComponent ) { RenderOptions renderOptions( m_scene.get() ); Private::RendererAlgo::outputOptions( renderOptions.globals.get(), m_renderOptions.globals.get(), m_renderer.get() ); - Private::RendererAlgo::outputOutputs( m_scene.get(), renderOptions.globals.get(), m_renderOptions.globals.get(), m_renderer.get() ); + if( !( boost::starts_with( m_renderer->name().string(), "3Delight" ) ) ) + { + Private::RendererAlgo::outputOutputs( m_scene.get(), renderOptions.globals.get(), m_renderOptions.globals.get(), m_renderer.get() ); + } + else + { + dl_renderOptions = m_renderOptions; + } if( *renderOptions.globals != *m_renderOptions.globals ) { m_changedGlobalComponents |= GlobalsGlobalComponent; @@ -1717,6 +1725,11 @@ void RenderController::updateInternal( const ProgressCallback &callback, const I updateDefaultCamera(); } + if( m_changedGlobalComponents && boost::starts_with( m_renderer->name().string(), "3Delight" ) ) + { + Private::RendererAlgo::outputOutputs( m_scene.get(), m_renderOptions.globals.get(), dl_renderOptions.globals.get(), m_renderer.get() ); + } + if( !pathsToUpdate ) { // Only clear `m_changedGlobalComponents` when we diff --git a/src/IECoreDelight/Renderer.cpp b/src/IECoreDelight/Renderer.cpp index bc5cb596c96..307669c9879 100644 --- a/src/IECoreDelight/Renderer.cpp +++ b/src/IECoreDelight/Renderer.cpp @@ -269,7 +269,14 @@ class DelightOutput : public IECore::RefCounted ParameterList driverParams; for( const auto &[parameterName, parameterValue] : output->parameters() ) { - if( parameterName != "filter" && parameterName != "filterwidth" && parameterName != "scalarformat" && parameterName != "colorprofile" && parameterName != "layername" && parameterName != "layerName" && parameterName != "withalpha" ) + if( parameterName != "filter" + && parameterName != "filterwidth" + && parameterName != "scalarformat" + && parameterName != "colorprofile" + && parameterName != "lightgroup" + && parameterName != "layername" + && parameterName != "layerName" + && parameterName != "withalpha" ) { driverParams.add( parameterName.c_str(), parameterValue.get() ); } @@ -404,9 +411,21 @@ class DelightOutput : public IECore::RefCounted layerParams.add( { "filterwidth", &filterWidth, NSITypeDouble, 0, 1, 0 } ); } + const string lightGroup = parameter( output->parameters(), "lightgroup", "" ); + vector lightGroupTokens; + if( lightGroup != "" ) + { + IECore::StringAlgo::tokenize( lightGroup, ' ', lightGroupTokens ); + } + for( const auto &[parameterName, parameterValue] : output->parameters() ) { - if( parameterName != "filter" && parameterName != "filterwidth" && parameterName != "scalarformat" && parameterName != "colorprofile" && parameterName != "layerName" ) + if( parameterName != "filter" + && parameterName != "filterwidth" + && parameterName != "scalarformat" + && parameterName != "colorprofile" + && parameterName != "lightgroup" + && parameterName != "layerName" ) { layerParams.add( parameterName.c_str(), parameterValue.get() ); } @@ -420,6 +439,20 @@ class DelightOutput : public IECore::RefCounted m_layerHandle.name(), "outputdrivers", 0, nullptr ); + + if( lightGroup != "" ) + { + for( string lightGroupToken : lightGroupTokens ) + { + NSIConnect( + m_context, + lightGroupToken.c_str(), "", + m_layerHandle.name(), "lightset", + 0, nullptr + ); + } + } + } const DelightHandle &layerHandle() const @@ -721,7 +754,10 @@ class DelightAttributes : public IECoreScenePreview::Renderer::AttributesInterfa { if( d->readable().size() ) { - msg( Msg::Warning, "DelightRenderer", "Attribute \"sets\" not supported" ); + for( auto &setName : d->readable() ) + { + m_sets.push_back(setName); + } } } } @@ -798,6 +834,11 @@ class DelightAttributes : public IECoreScenePreview::Renderer::AttributesInterfa return m_handle; } + const vector &sets() const + { + return m_sets; + } + private : static ConstDelightShaderPtr shader( const IECore::InternedString &name, const IECore::CompoundObject *attributes, ShaderCache *shaderCache ) @@ -818,6 +859,7 @@ class DelightAttributes : public IECoreScenePreview::Renderer::AttributesInterfa ConstDelightShaderPtr m_displacementShader; ConstShaderNetworkPtr m_usdLightShader; + vector m_sets; }; @@ -1093,6 +1135,15 @@ class DelightObject: public IECoreScenePreview::Renderer::ObjectInterface m_attributes->handle().name(), "", m_transformHandle.name(), "shaderattributes" ); + for ( auto setName : m_attributes->sets() ) + { + setName = "render:" + setName; + NSIDisconnect( + m_transformHandle.context(), + m_transformHandle.name(), "", + setName.c_str(), "members" + ); + } } m_attributes = static_cast( attributes ); @@ -1111,6 +1162,17 @@ class DelightObject: public IECoreScenePreview::Renderer::ObjectInterface ); + for ( auto setName : m_attributes->sets() ) + { + setName = "render:" + setName; + NSICreate( m_transformHandle.context(), setName.c_str(), "set", 0, nullptr ); + NSIConnect( + m_transformHandle.context(), + m_transformHandle.name(), "", + setName.c_str(), "members", + 0, nullptr + ); + } return true; } diff --git a/startup/gui/outputs.py b/startup/gui/outputs.py index 9f10e6ee823..580ecc57f0b 100644 --- a/startup/gui/outputs.py +++ b/startup/gui/outputs.py @@ -250,6 +250,7 @@ "colorprofile" : "linear", "filter" : "blackman-harris", "filterwidth" : 3.0, + "lightgroup" : "", } ) ) @@ -265,6 +266,7 @@ "colorprofile" : "linear", "filter" : "blackman-harris", "filterwidth" : 3.0, + "lightgroup" : "", } ) )