diff --git a/include/Gaffer/PlugAlgo.h b/include/Gaffer/PlugAlgo.h index 48237bb097b..034ac9d03a6 100644 --- a/include/Gaffer/PlugAlgo.h +++ b/include/Gaffer/PlugAlgo.h @@ -51,6 +51,7 @@ namespace Gaffer IE_CORE_FORWARDDECLARE( Context ) IE_CORE_FORWARDDECLARE( GraphComponent ) IE_CORE_FORWARDDECLARE( ValuePlug ) +IE_CORE_FORWARDDECLARE( ArrayPlug ) namespace PlugAlgo { @@ -97,6 +98,9 @@ GAFFER_API ValuePlugPtr createPlugFromData( const std::string &name, Plug::Direc /// Returns a Data value from a plug. GAFFER_API IECore::DataPtr getValueAsData( const ValuePlug *plug ); +/// Returns a VectorData value from an array plug. +GAFFER_API IECore::DataPtr getArrayAsVectorData( const ArrayPlug *plug ); + /// Sets the value of an existing plug to the specified data. /// Returns `true` on success and `false` on failure. GAFFER_API bool setValueFromData( ValuePlug *plug, const IECore::Data *value ); diff --git a/python/GafferArnoldTest/ArnoldShaderTest.py b/python/GafferArnoldTest/ArnoldShaderTest.py index dbc7e03ff4f..6822f679ee5 100644 --- a/python/GafferArnoldTest/ArnoldShaderTest.py +++ b/python/GafferArnoldTest/ArnoldShaderTest.py @@ -989,6 +989,30 @@ def testUSDRoundTrip( self ) : ignoreBlindData = True ) + def testArrayConnections ( self ): + + ramp = GafferArnold.ArnoldShader("ramp") + ramp.loadShader( "ramp_rgb" ) + ramp['parameters']['position'].resize(2) + ramp['parameters']['color'].resize(2) + ramp['parameters']['interpolation'].resize(2) + + userDataRgb = GafferArnold.ArnoldShader() + userDataRgb.loadShader("user_data_rgb") + + userDataFloat = GafferArnold.ArnoldShader() + userDataFloat.loadShader( "user_data_float" ) + + ramp['parameters']['position'][1].setInput(userDataFloat['out']) + ramp['parameters']['color'][1].setInput(userDataRgb['out']) + + n = ramp.attributes() + + self.assertTrue( ramp["parameters"]["color"][1].acceptsInput( userDataRgb["out"] ) ) + self.assertTrue( ramp["parameters"]["position"][1].acceptsInput( userDataFloat["out"] ) ) + # 3 RGB connections and 1 float connection + self.assertTrue(len(n["ai:surface"].inputConnections("ramp")) == 4) + def testStandardVolumeType( self ) : shader = GafferArnold.ArnoldShader() diff --git a/python/GafferUI/ArrayDataWidget.py b/python/GafferUI/ArrayDataWidget.py new file mode 100644 index 00000000000..2530ef4f14f --- /dev/null +++ b/python/GafferUI/ArrayDataWidget.py @@ -0,0 +1,104 @@ +import Gaffer +import GafferUI +from GafferUI.VectorDataWidget import _Model as _VectorDataWidgetModel +from GafferUI.VectorDataWidget import _Delegate as _VectorDataWidgetDelegate + +from Qt import QtCore +from Qt import QtWidgets +from Qt import QtCompat + + +class ArrayDataWidget( GafferUI.VectorDataWidget ) : + def __init__( self, data=None, **kwargs ) : + self.__rowEditability = None + super( ArrayDataWidget, self ).__init__( data, **kwargs ) + + + def setData( self, data ) : + + if data is not None : + if not isinstance( data, list ) : + data = [ data ] + self._VectorDataWidget__model = _Model( data, self._VectorDataWidget__tableView, self.getEditable(), self._VectorDataWidget__header, self._VectorDataWidget__toolTips, self._VectorDataWidget__columnEditability, self.__rowEditability ) + self._VectorDataWidget__model.dataChanged.connect( Gaffer.WeakMethod( self._VectorDataWidget__modelDataChanged ) ) + self._VectorDataWidget__model.rowsInserted.connect( Gaffer.WeakMethod( self._VectorDataWidget__emitDataChangedSignal ) ) + self._VectorDataWidget__model.rowsRemoved.connect( Gaffer.WeakMethod( self._VectorDataWidget__emitDataChangedSignal ) ) + else : + self._VectorDataWidget__model = None + + self._VectorDataWidget__tableView.setModel( self._VectorDataWidget__model ) + + if self._VectorDataWidget__model : + + columnIndex = 0 + haveResizeableContents = False + for accessor in self._VectorDataWidget__model.vectorDataAccessors() : + for i in range( 0, accessor.numColumns() ) : + delegate = _VectorDataWidgetDelegate.create( accessor.data() ) + delegate.setParent( self._VectorDataWidget__model ) + self._VectorDataWidget__tableView.setItemDelegateForColumn( columnIndex, delegate ) + canStretch = delegate.canStretch() + haveResizeableContents = haveResizeableContents or canStretch + columnIndex += 1 + + QtCompat.setSectionResizeMode( + self._VectorDataWidget__tableView.horizontalHeader(), + QtWidgets.QHeaderView.ResizeToContents if haveResizeableContents else QtWidgets.QHeaderView.Fixed + ) + + self._VectorDataWidget__tableView.horizontalHeader().setStretchLastSection( canStretch ) + horizontalSizePolicy = QtWidgets.QSizePolicy.Expanding + if self._VectorDataWidget__tableView.horizontalScrollMode() == QtCore.Qt.ScrollBarAlwaysOff and not canStretch : + horizontalSizePolicy = QtWidgets.QSizePolicy.Fixed + + self._VectorDataWidget__tableView.setSizePolicy( + QtWidgets.QSizePolicy( + horizontalSizePolicy, + QtWidgets.QSizePolicy.Maximum + ) + ) + + selectionModel = self._VectorDataWidget__tableView.selectionModel() + selectionModel.selectionChanged.connect( Gaffer.WeakMethod( self._VectorDataWidget__selectionChanged ) ) + + self._VectorDataWidget__updateRemoveButtonEnabled() + + self._VectorDataWidget__tableView.verticalHeader().setUpdatesEnabled( True ) + self._VectorDataWidget__tableView.updateGeometry() + + def setRowEditability( self, rowEditability ) : + + self.__rowEditability = rowEditability + + def getRowEditability( self ) : + + return self.__rowEditability + + +class _Model( _VectorDataWidgetModel ) : + + def __init__( self, data, parent=None, editable=True, header=None, toolTips=None, columnEditability=None, rowEditability=None ) : + _VectorDataWidgetModel.__init__( self, data, parent=parent, editable=editable, header=header, toolTips=toolTips, columnEditability=columnEditability ) + + self.__rowEditability = rowEditability + + def flags( self, index ) : + result = ( + QtCore.Qt.ItemIsSelectable | + QtCore.Qt.ItemIsDragEnabled + ) + + if self.__editable : + rowEditable = ( + self.__rowEditability is None or + index.row() >= len( self.__rowEditability ) or + self.__rowEditability[index.row()] + ) + if rowEditable : + result |= QtCore.Qt.ItemIsEnabled + if self.__columnEditability is None or self.__columnEditability[index.column()] : + result |= QtCore.Qt.ItemIsEditable + else : + result |= QtCore.Qt.ItemIsEnabled + + return result \ No newline at end of file diff --git a/python/GafferUI/ArrayPlugValueWidget.py b/python/GafferUI/ArrayPlugValueWidget.py new file mode 100644 index 00000000000..9baabfbae54 --- /dev/null +++ b/python/GafferUI/ArrayPlugValueWidget.py @@ -0,0 +1,97 @@ +import IECore + +import Gaffer +import GafferUI + + +class ArrayPlugValueWidget( GafferUI.PlugValueWidget ) : + + def __init__( self, plug, **kw ) : + + sizeEditable = plug.minSize() != plug.maxSize() + + self.__dataWidget = GafferUI.ArrayDataWidget( + header = True, + sizeEditable = sizeEditable, + ) + + GafferUI.PlugValueWidget.__init__( self, self.__dataWidget, plug, **kw ) + + self.__dataWidget.dataChangedSignal().connect( Gaffer.WeakMethod( self.__dataChanged ) ) + + def setHighlighted( self, highlighted ) : + + GafferUI.PlugValueWidget.setHighlighted( self, highlighted ) + self.__dataWidget.setHighlighted( highlighted ) + + + @staticmethod + def _valuesForUpdate( plugs, auxiliaryPlugs ) : + + assert( len( plugs ) == 1 ) + plug = next( iter( plugs ) ) + children = list( plug ) + return { + "values" : [ c.getValue() for c in children ], + "rowEditable" : [ c.getInput() is None for c in children ], + } + + def _updateFromValues( self, values, exception ) : + + if not values : + return + + vectorDataType = None + + plug = self.getPlug() + if plug is not None : + vectorDataType = self.__vectorDataType( plug ) + + if vectorDataType is not None : + self.__dataWidget.setRowEditability( values["rowEditable"] ) + self.__dataWidget.setData( vectorDataType( values["values"] ) ) + self.__dataWidget.setErrored( exception is not None ) + + def _updateFromEditable( self ) : + + self.__dataWidget.setEditable( self._editable() ) + + def __dataChanged( self, widget ) : + + plug = self.getPlug() + if plug is None : + return + + with self._blockedUpdateFromValues() : + with Gaffer.UndoScope( plug.ancestor( Gaffer.ScriptNode ) ) : + data = self.__dataWidget.getData()[0] + targetSize = max( plug.minSize(), min( plug.maxSize(), len( data ) ) ) + plug.resize( targetSize ) + for i, child in enumerate( plug ) : + if i < len( data ) and child.getInput() is None : + child.setValue( data[i] ) + + self._requestUpdateFromValues() + + @staticmethod + def __vectorDataType( plug ) : + + elementPrototype = plug.elementPrototype() + if elementPrototype is None : + return None + return ArrayPlugValueWidget.__plugTypeToVectorDataType.get( type( elementPrototype ) ) + + __plugTypeToVectorDataType = { + Gaffer.BoolPlug : IECore.BoolVectorData, + Gaffer.IntPlug : IECore.IntVectorData, + Gaffer.FloatPlug : IECore.FloatVectorData, + Gaffer.StringPlug : IECore.StringVectorData, + Gaffer.V2iPlug : IECore.V2iVectorData, + Gaffer.V2fPlug : IECore.V2fVectorData, + Gaffer.V3iPlug : IECore.V3iVectorData, + Gaffer.V3fPlug : IECore.V3fVectorData, + Gaffer.Color3fPlug : IECore.Color3fVectorData, + Gaffer.Color4fPlug : IECore.Color4fVectorData, + } + +GafferUI.PlugValueWidget.registerType( Gaffer.ArrayPlug, ArrayPlugValueWidget ) diff --git a/python/GafferUI/__init__.py b/python/GafferUI/__init__.py index b06e7488c8a..9b7b6ae7310 100644 --- a/python/GafferUI/__init__.py +++ b/python/GafferUI/__init__.py @@ -158,6 +158,7 @@ def __shiboken() : from .ErrorDialogue import ErrorDialogue from ._Variant import _Variant from .VectorDataWidget import VectorDataWidget +from .ArrayDataWidget import ArrayDataWidget from .PathVectorDataWidget import PathVectorDataWidget from .ProgressBar import ProgressBar from .SelectionMenu import SelectionMenu @@ -203,6 +204,7 @@ def __shiboken() : from .PathPlugValueWidget import PathPlugValueWidget from .FileSystemPathPlugValueWidget import FileSystemPathPlugValueWidget from .VectorDataPlugValueWidget import VectorDataPlugValueWidget +from .ArrayPlugValueWidget import ArrayPlugValueWidget from .PathVectorDataPlugValueWidget import PathVectorDataPlugValueWidget from .FileSystemPathVectorDataPlugValueWidget import FileSystemPathVectorDataPlugValueWidget from .PlugWidget import PlugWidget diff --git a/python/GafferUITest/ArrayDataWidgetTest.py b/python/GafferUITest/ArrayDataWidgetTest.py new file mode 100644 index 00000000000..88b8e97f118 --- /dev/null +++ b/python/GafferUITest/ArrayDataWidgetTest.py @@ -0,0 +1,104 @@ +import unittest +import imath + +import IECore + +import GafferTest +import GafferUI +import GafferUITest +from Qt import QtCore + +class ArrayDataWidgetTest( GafferUITest.TestCase ) : + + def testIndexing( self ) : + + data = [ + IECore.FloatVectorData( range( 0, 3 ) ), + IECore.Color3fVectorData( [ imath.Color3f( x ) for x in range( 0, 3 ) ] ), + IECore.StringVectorData( [ str( x ) for x in range( 0, 3 ) ] ), + IECore.IntVectorData( range( 0, 3 ) ), + IECore.V3fVectorData( [ imath.V3f( x ) for x in range( 0, 3 ) ] ), + ] + + w = GafferUI.ArrayDataWidget( data ) + + self.assertEqual( w.columnToDataIndex( 0 ), ( 0, -1 ) ) + self.assertEqual( w.columnToDataIndex( 1 ), ( 1, 0 ) ) + self.assertEqual( w.columnToDataIndex( 2 ), ( 1, 1 ) ) + self.assertEqual( w.columnToDataIndex( 3 ), ( 1, 2 ) ) + self.assertEqual( w.columnToDataIndex( 4 ), ( 1, 3 ) ) + self.assertEqual( w.columnToDataIndex( 5 ), ( 2, -1 ) ) + self.assertEqual( w.columnToDataIndex( 6 ), ( 3, -1 ) ) + self.assertEqual( w.columnToDataIndex( 7 ), ( 4, 0 ) ) + self.assertEqual( w.columnToDataIndex( 8 ), ( 4, 1 ) ) + self.assertEqual( w.columnToDataIndex( 9 ), ( 4, 2 ) ) + + self.assertRaises( IndexError, w.columnToDataIndex, 10 ) + + self.assertEqual( w.dataToColumnIndex( 0, -1 ), 0 ) + self.assertEqual( w.dataToColumnIndex( 1, 0 ), 1 ) + self.assertEqual( w.dataToColumnIndex( 1, 1 ), 2 ) + self.assertEqual( w.dataToColumnIndex( 1, 2 ), 3 ) + self.assertEqual( w.dataToColumnIndex( 1, 3 ), 4 ) + self.assertEqual( w.dataToColumnIndex( 2, -1 ), 5 ) + self.assertEqual( w.dataToColumnIndex( 3, -1 ), 6 ) + self.assertEqual( w.dataToColumnIndex( 4, 0 ), 7 ) + self.assertEqual( w.dataToColumnIndex( 4, 1 ), 8 ) + self.assertEqual( w.dataToColumnIndex( 4, 2 ), 9 ) + + self.assertRaises( IndexError, w.dataToColumnIndex, 6, 0 ) + + def testColumnEditability( self ) : + + data = [ + IECore.FloatVectorData( range( 0, 3 ) ), + IECore.Color3fVectorData( [ imath.Color3f( x ) for x in range( 0, 3 ) ] ), + IECore.StringVectorData( [ str( x ) for x in range( 0, 3 ) ] ), + ] + + w = GafferUI.ArrayDataWidget( data ) + + for i in range( 0, 6 ) : + self.assertEqual( w.getColumnEditable( i ), True ) + + self.assertRaises( IndexError, w.getColumnEditable, 7 ) + self.assertRaises( IndexError, w.getColumnEditable, -1 ) + + w.setColumnEditable( 1, False ) + self.assertEqual( w.getColumnEditable( 1 ), False ) + + data[0][0] += 1.0 + w.setData( data ) + + for i in range( 0, 6 ) : + self.assertEqual( w.getColumnEditable( i ), i != 1 ) + + cs = GafferTest.CapturingSlot( w.dataChangedSignal() ) + self.assertEqual( len( cs ), 0 ) + + w.setColumnEditable( 0, False ) + w.setColumnEditable( 1, True ) + + # changing editability shouldn't emit dataChangedSignal. + self.assertEqual( len( cs ), 0 ) + + def testRowEditability( self ) : + + data = [ IECore.IntVectorData( range( 0, 3 ) ) ] + + w = GafferUI.ArrayDataWidget() + w.setRowEditability( [ False, True, False ] ) + w.setData( data ) + + model = w._VectorDataWidget__model + + index0 = model.index( 0, 0 ) + index1 = model.index( 1, 0 ) + index2 = model.index( 2, 0 ) + + self.assertFalse( bool( model.flags( index0 ) & QtCore.Qt.ItemIsEditable ) ) + self.assertTrue( bool( model.flags( index1 ) & QtCore.Qt.ItemIsEditable ) ) + self.assertFalse( bool( model.flags( index2 ) & QtCore.Qt.ItemIsEditable ) ) + +if __name__ == "__main__": + unittest.main() diff --git a/python/GafferUITest/__init__.py b/python/GafferUITest/__init__.py index 117eeaeed75..1b8c52803ab 100644 --- a/python/GafferUITest/__init__.py +++ b/python/GafferUITest/__init__.py @@ -134,5 +134,6 @@ from .PopupWindowTest import PopupWindowTest from .ColorChooserTest import ColorChooserTest from .ContextTrackerTest import ContextTrackerTest +from .ArrayDataWidgetTest import ArrayDataWidgetTest from .MetadataAlgoTest import MetadataAlgoTest from .BreadCrumbsWidgetTest import BreadCrumbsWidgetTest diff --git a/src/Gaffer/PlugAlgo.cpp b/src/Gaffer/PlugAlgo.cpp index 8ab039c5ba1..edf987ae7d5 100644 --- a/src/Gaffer/PlugAlgo.cpp +++ b/src/Gaffer/PlugAlgo.cpp @@ -53,6 +53,7 @@ #include "Gaffer/TransformPlug.h" #include "Gaffer/TypedObjectPlug.h" #include "Gaffer/ValuePlug.h" +#include "Gaffer/ArrayPlug.h" #include "IECore/DataAlgo.h" #include "IECore/RampData.h" @@ -629,6 +630,125 @@ IECore::DataPtr getValueAsData( const ValuePlug *plug ) } +IECore::DataPtr getArrayAsVectorData( const ArrayPlug *plug ) +{ + size_t size = plug->children().size(); + if ( !size ) + { + return nullptr; + } + auto *childPlug = plug->getChild( 0 ); + int i = 0; + switch(static_cast(childPlug->typeId())) + { + case FloatPlugTypeId : + { + FloatVectorDataPtr data = new FloatVectorData(); + auto &writable = data->writable(); + writable.resize( size ); + for( Plug::InputIterator it( plug ); !it.done(); ++it, ++i ) + { + writable[i] = static_cast( it->get() )->getValue(); + } + return data; + } + case IntPlugTypeId : + { + IntVectorDataPtr data = new IntVectorData(); + auto &writable = data->writable(); + writable.resize( size ); + for( Plug::InputIterator it( plug ); !it.done(); ++it, ++i ) + { + writable[i] = static_cast( it->get() )->getValue(); + } + return data; + } + case Color3fPlugTypeId : + { + Color3fVectorDataPtr data = new Color3fVectorData(); + auto &writable = data->writable(); + writable.resize( size ); + for( Plug::InputIterator it( plug ); !it.done(); ++it, ++i ) + { + writable[i] = static_cast( it->get() )->getValue(); + } + return data; + } + case Color4fPlugTypeId : + { + Color4fVectorDataPtr data = new Color4fVectorData(); + auto &writable = data->writable(); + writable.resize( size ); + for( Plug::InputIterator it( plug ); !it.done(); ++it, ++i ) + { + writable[i] = static_cast( it->get() )->getValue(); + } + return data; + } + case StringPlugTypeId : + { + StringVectorDataPtr data = new StringVectorData(); + auto &writable = data->writable(); + writable.resize( size ); + for( Plug::InputIterator it( plug ); !it.done(); ++it, ++i ) + { + writable[i] = static_cast( it->get() )->getValue(); + } + return data; + } + case BoolPlugTypeId : + { + BoolVectorDataPtr data = new BoolVectorData(); + auto &writable = data->writable(); + writable.resize( size ); + for( Plug::InputIterator it( plug ); !it.done(); ++it, ++i ) + { + writable[i] = static_cast( it->get() )->getValue(); + } + return data; + } + case V2fPlugTypeId : + { + V2fVectorDataPtr data = new V2fVectorData(); + auto &writable = data->writable(); + writable.resize( size ); + for( Plug::InputIterator it( plug ); !it.done(); ++it, ++i ) + { + writable[i] = static_cast( it->get() )->getValue(); + } + return data; + } + case V3fPlugTypeId : + { + V3fVectorDataPtr data = new V3fVectorData(); + auto &writable = data->writable(); + writable.resize( size ); + for( Plug::InputIterator it( plug ); !it.done(); ++it, ++i ) + { + writable[i] = static_cast( it->get() )->getValue(); + } + return data; + } + case M44fPlugTypeId : + { + M44fVectorDataPtr data = new M44fVectorData(); + auto &writable = data->writable(); + writable.resize( size ); + for( Plug::InputIterator it( plug ); !it.done(); ++it, ++i ) + { + writable[i] = static_cast( it->get() )->getValue(); + } + return data; + } + default : + throw IECore::Exception( + fmt::format( "ArrayPlug \"{}\" has unsupported child type \"{}\"", plug->getName().string(), childPlug->typeName() ) + ); + } + +} + + IECore::DataPtr extractDataFromPlug( const ValuePlug *plug ) { return getValueAsData( plug ); diff --git a/src/GafferArnold/ParameterHandler.cpp b/src/GafferArnold/ParameterHandler.cpp index 5c01ca091f3..ce619cd29f9 100644 --- a/src/GafferArnold/ParameterHandler.cpp +++ b/src/GafferArnold/ParameterHandler.cpp @@ -45,6 +45,7 @@ #include "Gaffer/ScriptNode.h" #include "Gaffer/StringPlug.h" #include "Gaffer/TypedPlug.h" +#include "Gaffer/ArrayPlug.h" #include "IECore/MessageHandler.h" @@ -146,6 +147,25 @@ Gaffer::Plug *setupNumericPlug( const AtNodeEntry *node, const AtParamEntry *par return plug.get(); } +Gaffer::Plug *setupArrayPlug( const IECore::InternedString ¶meterName, Gaffer::GraphComponent *plugParent, Gaffer::Plug::Direction direction, int arrayType ) +{ + Plug *existingPlug = plugParent->getChild( parameterName ); + + if( + existingPlug && + existingPlug->direction() == direction + ) + { + existingPlug->setFlags( Gaffer::Plug::Dynamic, false ); + return existingPlug; + } + Plug *arrayTypePlug = ParameterHandler::setupPlug(parameterName, arrayType, plugParent, direction ); + ArrayPlugPtr plug = new ArrayPlug( parameterName, direction, arrayTypePlug, 0, std::numeric_limits::max(), Plug::Flags::Default, false ); + PlugAlgo::replacePlug( plugParent, plug ); + + return plug.get(); +} + Gaffer::Plug *setupPlug( const IECore::InternedString ¶meterName, Gaffer::GraphComponent *plugParent, Gaffer::Plug::Direction direction ) { Plug *existingPlug = plugParent->getChild( parameterName ); @@ -572,6 +592,11 @@ Gaffer::Plug *ParameterHandler::setupPlug( const AtNodeEntry *node, const AtPara ); break; + case AI_TYPE_ARRAY : + int arrayType = AiParamGetSubType(parameter); + plug = setupArrayPlug( AiParamGetName( parameter ).c_str(), plugParent, direction, arrayType); + break; + } if( !plug ) diff --git a/src/GafferScene/Shader.cpp b/src/GafferScene/Shader.cpp index 76ae4d6a180..0563deda4eb 100644 --- a/src/GafferScene/Shader.cpp +++ b/src/GafferScene/Shader.cpp @@ -517,12 +517,7 @@ class Shader::NetworkBuilder } else if( const Gaffer::ArrayPlug *array = IECore::runTimeCast( parameter ) ) { - int i = 0; - for( Plug::InputIterator it( array ); !it.done(); ++it, ++i ) - { - IECore::InternedString childParameterName = parameterName.string() + "[" + std::to_string( i ) + "]"; - addParameter( it->get(), childParameterName, shader, connections ); - } + addParameter( parameter, parameterName, shader, connections ); } else { @@ -631,6 +626,24 @@ class Shader::NetworkBuilder { hashRampParameterComponentConnections< RampfColor4fPlug >( (const RampfColor4fPlug*)parameter, h ); } + else if ( (Gaffer::TypeId)parameter->typeId() == ArrayPlugTypeId ) + { + int i = 0; + for ( Plug::InputIterator it( parameter ); !it.done(); ++it, ++i ) + { + hashParameterComponentConnections( it->get(), h ); + } + } + else + { + OptionalScopedContext parameterContext; + const Gaffer::Plug *effectiveParameter = this->effectiveParameter( parameter, parameterContext ); + if( effectiveParameter && isOutputParameter( effectiveParameter ) ) + { + parameterHashForPlug( effectiveParameter, h ); + h.append( parameter->getName() ); + } + } } template< typename T > @@ -710,6 +723,30 @@ class Shader::NetworkBuilder { addRampParameterComponentConnections< RampfColor4fPlug >( (const RampfColor4fPlug*)parameter, parameterName, connections ); } + else if ( (Gaffer::TypeId)parameter->typeId() == ArrayPlugTypeId ) + { + int i = 0; + for ( Plug::InputIterator it( parameter ); !it.done(); ++it, ++i ) + { + IECore::InternedString inputName = parameterName.string() + "[" + std::to_string( i ) + "]"; + addParameterComponentConnections( it->get(), inputName, connections ); + } + } + else + { + // handling of array children that do not have components + OptionalScopedContext parameterContext; + const Gaffer::Plug *effectiveParameter = this->effectiveParameter( parameter, parameterContext ); + if( effectiveParameter && isOutputParameter( effectiveParameter ) ) + { + IECore::InternedString inputName = parameterName.string(); + + connections.push_back( { + outputParameterForPlug( effectiveParameter ), + { IECore::InternedString(), inputName } + } ); + } + } } template< typename T > @@ -1162,6 +1199,10 @@ IECore::DataPtr Shader::parameterValue( const Gaffer::Plug *parameterPlug ) cons { return Gaffer::PlugAlgo::getValueAsData( valuePlug ); } + else if( auto arrayPlug = IECore::runTimeCast( parameterPlug ) ) + { + return Gaffer::PlugAlgo::getArrayAsVectorData( arrayPlug ); + } return nullptr; }