diff --git a/Changes.md b/Changes.md index 517523172a0..22336ed707f 100644 --- a/Changes.md +++ b/Changes.md @@ -16,6 +16,7 @@ Improvements - LightTool : Changed spot light and quad light edge tool tip locations so that they follow the cone and edge during drag. - Arnold : Improved speed of translation of encapsulated scenes when using many threads. - CollectImages : Added `addLayerPrefix` plug, to allow the layer prefix to be omitted in the case that the input images are already prefixed. +- OSL Expression : Added support for getting an element of an array context variable using `contextElement( variableName, index )` or `contextElement( variableName, index, defaultValue )`. Negative indices can be used to get elements relative to the end of the array. Fixes ----- @@ -31,6 +32,7 @@ Fixes - SceneAlgo : Fixed computation of `ScenePlug.object` in networks with nodes derived from `ObjectProcessor`. These include : `CameraTweaks`, `ClosestPointSampler`, `CollectPrimitiveVariables`, `CopyPrimitiveVariables`, `CurveSampler`, `DeleteCurves`, `DeleteFaces`, `DeletePoints`, `MapOffset`, `MapProjection`, `MeshDistortion`, `MeshNormals`, `MeshSegments`, `MeshTangents`, `MeshToPoints`, `MeshType`, `Orientation`, `PointsType`, `PrimitiveSampler`, `PrimitiveVariables`, `ReverseWinding`, `ShufflePrimitiveVariables` and `UVSampler` (#5406). - Metadata : Fixed redundant copying of metadata when promoting plugs. - OpenColorIO : Fixed hang when opening a script which didn't yet have the `openColorIO.config` plug. +- Context : Fixed bug preventing the retrieval of `V2iVectorData`, `V2fVectorData`, `V3iVectorData` and `V3fVectorData` from a context. API --- diff --git a/doc/source/Reference/ScriptingReference/Expressions/index.md b/doc/source/Reference/ScriptingReference/Expressions/index.md index 1bee9337ab8..f27895d471f 100644 --- a/doc/source/Reference/ScriptingReference/Expressions/index.md +++ b/doc/source/Reference/ScriptingReference/Expressions/index.md @@ -23,7 +23,11 @@ Set value | `parent["NodeName"]["plugName"] = value` | `parent.NodeName Context Variables ----------------- -Operation | Python | OSL -----------------------|-------------------------------------------|---- -Get variable | `context["variableName"]` | `context( "variableName" )` -Get with default | `context.get( "variableName", default )` | `context( "variableName", default )` +Operation | Python | OSL +--------------------------------|------------------------------------------------------------------------------------------|---- +Get variable | `context["variableName"]` | `context( "variableName" )` +Get with default | `context.get( "variableName", default )` | `context( "variableName", default )` +Get array element | `context["variableName"][index]` | `contextElement( "variableName", index )` +Get array element with default | `a = context.get( "variableName", [] )`
`a[index] if len( a ) > index else default` | `contextElement( "variableName", index, default )` +Get array element from end | `context["variableName"][-index]` | `contextElement( "variableName", -index )` + diff --git a/include/Gaffer/Context.inl b/include/Gaffer/Context.inl index 9c473f9fd78..14686d4bbb0 100644 --- a/include/Gaffer/Context.inl +++ b/include/Gaffer/Context.inl @@ -82,6 +82,22 @@ struct DataTraits > }; +template +struct DataTraits > > +{ + + using DataType = IECore::GeometricTypedData>>; + +}; + +template +struct DataTraits > > +{ + + using DataType = IECore::GeometricTypedData>>; + +}; + } // namespace Detail inline Context::Value::Value() diff --git a/python/GafferOSLTest/OSLExpressionEngineTest.py b/python/GafferOSLTest/OSLExpressionEngineTest.py index b096e249eb6..4ade159e2b5 100644 --- a/python/GafferOSLTest/OSLExpressionEngineTest.py +++ b/python/GafferOSLTest/OSLExpressionEngineTest.py @@ -221,6 +221,160 @@ def testContextTypes( self ) : self.assertEqual( s["n"]["user"]["s"].getValue(), "non-default" ) self.assertEqual( s["n"]["user"]["b"].getValue(), True ) + def testContextVectorTypes( self ) : + + s = Gaffer.ScriptNode() + s["n"] = Gaffer.Node() + for i in range( 0, 5 ) : + s["n"]["user"]["f" + str( i )] = Gaffer.FloatPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + s["n"]["user"]["i" + str( i )] = Gaffer.IntPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + s["n"]["user"]["c" + str( i )] = Gaffer.Color3fPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + s["n"]["user"]["v" + str( i )] = Gaffer.V3fPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + s["n"]["user"]["s" + str( i )] = Gaffer.StringPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + s["n"]["user"]["b" + str( i )] = Gaffer.BoolPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + + s["e"] = Gaffer.Expression() + s["e"].setExpression( + inspect.cleandoc( + """ + parent.n.user.f0 = contextElement( "f", 0, 1 ); + parent.n.user.i0 = contextElement( "i", 0, 1 ); + parent.n.user.c0 = contextElement( "c", 0, color( 1, 2, 3 ) ); + parent.n.user.v0 = contextElement( "v", 0 ); + parent.n.user.s0 = contextElement( "s", 0, "default0" ); + + parent.n.user.f1 = contextElement( "f", 1, 2 ); + parent.n.user.i1 = contextElement( "i", 1, 2 ); + parent.n.user.c1 = contextElement( "c", 1, color( 4, 5, 6 ) ); + parent.n.user.v1 = contextElement( "v", 1 ); + parent.n.user.s1 = contextElement( "s", 1, "default1" ); + + parent.n.user.f2 = contextElement( "f", 2, 3 ); + parent.n.user.i2 = contextElement( "i", 2, 3 ); + parent.n.user.c2 = contextElement( "c", 2, color( 7, 8, 9 ) ); + parent.n.user.v2 = contextElement( "v", 2 ); + parent.n.user.s2 = contextElement( "s", 2, "default2" ); + + parent.n.user.f3 = contextElement( "f", -1 ); + parent.n.user.i3 = contextElement( "i", -1 ); + parent.n.user.c3 = contextElement( "c", -1 ); + parent.n.user.v3 = contextElement( "v", -1 ); + parent.n.user.s3 = contextElement( "s", -1 ); + + parent.n.user.f4 = contextElement( "f", -3 ); + parent.n.user.i4 = contextElement( "i", -3 ); + parent.n.user.c4 = contextElement( "c", -3 ); + parent.n.user.v4 = contextElement( "v", -3 ); + parent.n.user.s4 = contextElement( "s", -3 ); + """ + ), + "OSL" + ) + + with Gaffer.Context() as c : + + self.assertEqual( s["n"]["user"]["f0"].getValue(), 1 ) + self.assertEqual( s["n"]["user"]["i0"].getValue(), 1 ) + self.assertEqual( s["n"]["user"]["c0"].getValue(), imath.Color3f( 1, 2, 3 ) ) + self.assertEqual( s["n"]["user"]["v0"].getValue(), imath.V3f( 0 ) ) + self.assertEqual( s["n"]["user"]["s0"].getValue(), "default0" ) + + self.assertEqual( s["n"]["user"]["f1"].getValue(), 2 ) + self.assertEqual( s["n"]["user"]["i1"].getValue(), 2 ) + self.assertEqual( s["n"]["user"]["c1"].getValue(), imath.Color3f( 4, 5, 6 ) ) + self.assertEqual( s["n"]["user"]["v1"].getValue(), imath.V3f( 0 ) ) + self.assertEqual( s["n"]["user"]["s1"].getValue(), "default1" ) + + self.assertEqual( s["n"]["user"]["f2"].getValue(), 3 ) + self.assertEqual( s["n"]["user"]["i2"].getValue(), 3 ) + self.assertEqual( s["n"]["user"]["c2"].getValue(), imath.Color3f( 7, 8, 9 ) ) + self.assertEqual( s["n"]["user"]["v2"].getValue(), imath.V3f( 0 ) ) + self.assertEqual( s["n"]["user"]["s2"].getValue(), "default2" ) + + self.assertEqual( s["n"]["user"]["f3"].getValue(), 0 ) + self.assertEqual( s["n"]["user"]["i3"].getValue(), 0 ) + self.assertEqual( s["n"]["user"]["c3"].getValue(), imath.Color3f( 0 ) ) + self.assertEqual( s["n"]["user"]["v3"].getValue(), imath.V3f( 0 ) ) + self.assertEqual( s["n"]["user"]["s3"].getValue(), "" ) + + self.assertEqual( s["n"]["user"]["f4"].getValue(), 0 ) + self.assertEqual( s["n"]["user"]["i4"].getValue(), 0 ) + self.assertEqual( s["n"]["user"]["c4"].getValue(), imath.Color3f( 0 ) ) + self.assertEqual( s["n"]["user"]["v4"].getValue(), imath.V3f( 0 ) ) + self.assertEqual( s["n"]["user"]["s4"].getValue(), "" ) + + c["f"] = IECore.FloatVectorData( [ 10, 11 ] ) + c["i"] = IECore.IntVectorData( [ 11, 12 ] ) + c["c"] = IECore.Color3fVectorData( [ imath.Color3f( 10, 11, 12 ), imath.Color3f( 13, 14, 15 ) ] ) + c["v"] = IECore.V3fVectorData( [ imath.V3f( 9, 10, 11 ), imath.V3f( 12, 13, 14 ) ] ) + c["s"] = IECore.StringVectorData( [ "non-default0", "non-default1" ] ) + + self.assertEqual( s["n"]["user"]["f0"].getValue(), 10 ) + self.assertEqual( s["n"]["user"]["i0"].getValue(), 11 ) + self.assertEqual( s["n"]["user"]["c0"].getValue(), imath.Color3f( 10, 11, 12 ) ) + self.assertEqual( s["n"]["user"]["v0"].getValue(), imath.V3f( 9, 10, 11 ) ) + self.assertEqual( s["n"]["user"]["s0"].getValue(), "non-default0" ) + + self.assertEqual( s["n"]["user"]["f1"].getValue(), 11 ) + self.assertEqual( s["n"]["user"]["i1"].getValue(), 12 ) + self.assertEqual( s["n"]["user"]["c1"].getValue(), imath.Color3f( 13, 14, 15 ) ) + self.assertEqual( s["n"]["user"]["v1"].getValue(), imath.V3f( 12, 13, 14 ) ) + self.assertEqual( s["n"]["user"]["s1"].getValue(), "non-default1" ) + + self.assertEqual( s["n"]["user"]["f2"].getValue(), 3 ) + self.assertEqual( s["n"]["user"]["i2"].getValue(), 3 ) + self.assertEqual( s["n"]["user"]["c2"].getValue(), imath.Color3f( 7, 8, 9 ) ) + self.assertEqual( s["n"]["user"]["v2"].getValue(), imath.V3f( 0 ) ) + self.assertEqual( s["n"]["user"]["s2"].getValue(), "default2" ) + + self.assertEqual( s["n"]["user"]["f3"].getValue(), 11 ) + self.assertEqual( s["n"]["user"]["i3"].getValue(), 12 ) + self.assertEqual( s["n"]["user"]["c3"].getValue(), imath.Color3f( 13, 14, 15 ) ) + self.assertEqual( s["n"]["user"]["v3"].getValue(), imath.V3f( 12, 13, 14 ) ) + self.assertEqual( s["n"]["user"]["s3"].getValue(), "non-default1" ) + + self.assertEqual( s["n"]["user"]["f4"].getValue(), 0 ) + self.assertEqual( s["n"]["user"]["i4"].getValue(), 0 ) + self.assertEqual( s["n"]["user"]["c4"].getValue(), imath.Color3f( 0 ) ) + self.assertEqual( s["n"]["user"]["v4"].getValue(), imath.V3f( 0 ) ) + self.assertEqual( s["n"]["user"]["s4"].getValue(), "" ) + + def testScenePathContext( self ) : + + s = Gaffer.ScriptNode() + s["n"] = Gaffer.Node() + s["n"]["user"]["p1"] = Gaffer.StringPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + s["n"]["user"]["p2"] = Gaffer.StringPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + s["n"]["user"]["p3"] = Gaffer.StringPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + s["n"]["user"]["p4"] = Gaffer.StringPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + + s["e"] = Gaffer.Expression() + s["e"].setExpression( + inspect.cleandoc( + """ + parent.n.user.p1 = contextElement( "scene:path", 0, "noPath1" ); + parent.n.user.p2 = contextElement( "scene:path", 1, "noPath2" ); + parent.n.user.p3 = contextElement( "scene:path", 2 ); + parent.n.user.p4 = contextElement( "scene:path", 3 ); + """ + ), + "OSL" + ) + + with Gaffer.Context() as c : + + self.assertEqual( s["n"]["user"]["p1"].getValue(), "noPath1" ) + self.assertEqual( s["n"]["user"]["p2"].getValue(), "noPath2" ) + self.assertEqual( s["n"]["user"]["p3"].getValue(), "" ) + self.assertEqual( s["n"]["user"]["p4"].getValue(), "" ) + + c["scene:path"] = IECore.InternedStringVectorData( [ "yellow", "brick", "road" ] ) + + self.assertEqual( s["n"]["user"]["p1"].getValue(), "yellow" ) + self.assertEqual( s["n"]["user"]["p2"].getValue(), "brick" ) + self.assertEqual( s["n"]["user"]["p3"].getValue(), "road" ) + self.assertEqual( s["n"]["user"]["p4"].getValue(), "" ) + def testDefaultExpression( self ) : s = Gaffer.ScriptNode() diff --git a/python/GafferTest/ContextTest.py b/python/GafferTest/ContextTest.py index 78ce2750731..44ee027ea2f 100644 --- a/python/GafferTest/ContextTest.py +++ b/python/GafferTest/ContextTest.py @@ -137,6 +137,15 @@ def testTypes( self ) : self.assertIsInstance( c["v2i"], imath.V2i ) self.assertNotEqual( compare, c.hash() ) + c["v2iv"] = IECore.V2iVectorData( [ imath.V2i( 1, 2 ), imath.V2i( 3, 4 ) ] ) + self.assertEqual( c["v2iv"], IECore.V2iVectorData( [ imath.V2i( 1, 2 ), imath.V2i( 3, 4 ) ] ) ) + self.assertEqual( c.get( "v2iv" ), IECore.V2iVectorData( [ imath.V2i( 1, 2 ), imath.V2i( 3, 4 ) ] ) ) + compare = c.hash() + c.set( "v2iv", IECore.V2iVectorData( [ imath.V2i( 5, 6 ), imath.V2i( 7, 8 ) ] ) ) + self.assertEqual( c["v2iv"], IECore.V2iVectorData( [ imath.V2i( 5, 6 ), imath.V2i( 7, 8 ) ] ) ) + self.assertIsInstance( c["v2iv"], IECore.V2iVectorData ) + self.assertNotEqual( compare, c.hash() ) + c["v3i"] = imath.V3i( 1, 2, 3 ) self.assertEqual( c["v3i"], imath.V3i( 1, 2, 3 ) ) self.assertEqual( c.get( "v3i" ), imath.V3i( 1, 2, 3 ) ) @@ -146,6 +155,15 @@ def testTypes( self ) : self.assertIsInstance( c["v3i"], imath.V3i ) self.assertNotEqual( compare, c.hash() ) + c["v3iv"] = IECore.V3iVectorData( [ imath.V3i( 1, 2, 3 ), imath.V3i( 4, 5, 6 ) ] ) + self.assertEqual( c["v3iv"], IECore.V3iVectorData( [ imath.V3i( 1, 2, 3 ), imath.V3i( 4, 5, 6 ) ] ) ) + self.assertEqual( c.get( "v3iv" ), IECore.V3iVectorData( [ imath.V3i( 1, 2, 3 ), imath.V3i( 4, 5, 6 ) ] ) ) + compare = c.hash() + c.set( "v3iv", IECore.V3iVectorData( [ imath.V3i( 7, 8, 9 ), imath.V3i( 10, 11, 12 ) ] ) ) + self.assertEqual( c["v3iv"], IECore.V3iVectorData( [ imath.V3i( 7, 8, 9 ), imath.V3i( 10, 11, 12 ) ] ) ) + self.assertIsInstance( c["v3iv"], IECore.V3iVectorData ) + self.assertNotEqual( compare, c.hash() ) + c["v2f"] = imath.V2f( 1, 2 ) self.assertEqual( c["v2f"], imath.V2f( 1, 2 ) ) self.assertEqual( c.get( "v2f" ), imath.V2f( 1, 2 ) ) @@ -155,6 +173,15 @@ def testTypes( self ) : self.assertIsInstance( c["v2f"], imath.V2f ) self.assertNotEqual( compare, c.hash() ) + c["v2fv"] = IECore.V2fVectorData( [ imath.V2f( 1, 2 ), imath.V2f( 3, 4 ) ] ) + self.assertEqual( c["v2fv"], IECore.V2fVectorData( [ imath.V2f( 1, 2 ), imath.V2f( 3, 4 ) ] ) ) + self.assertEqual( c.get( "v2fv" ), IECore.V2fVectorData( [ imath.V2f( 1, 2 ), imath.V2f( 3, 4 ) ] ) ) + compare = c.hash() + c.set( "v2fv", IECore.V2fVectorData( [ imath.V2f( 5, 6 ), imath.V2f( 7, 8 ) ] ) ) + self.assertEqual( c["v2fv"], IECore.V2fVectorData( [ imath.V2f( 5, 6 ), imath.V2f( 7, 8 ) ] ) ) + self.assertIsInstance( c["v2fv"], IECore.V2fVectorData ) + self.assertNotEqual( compare, c.hash() ) + c["v3f"] = imath.V3f( 1, 2, 3 ) self.assertEqual( c["v3f"], imath.V3f( 1, 2, 3 ) ) self.assertEqual( c.get( "v3f" ), imath.V3f( 1, 2, 3 ) ) @@ -164,6 +191,15 @@ def testTypes( self ) : self.assertIsInstance( c["v3f"], imath.V3f ) self.assertNotEqual( compare, c.hash() ) + c["v3fv"] = IECore.V3fVectorData( [ imath.V3f( 1, 2, 3 ), imath.V3f( 4, 5, 6 ) ] ) + self.assertEqual( c["v3fv"], IECore.V3fVectorData( [ imath.V3f( 1, 2, 3 ), imath.V3f( 4, 5, 6 ) ] ) ) + self.assertEqual( c.get( "v3fv" ), IECore.V3fVectorData( [ imath.V3f( 1, 2, 3 ), imath.V3f( 4, 5, 6 ) ] ) ) + compare = c.hash() + c.set( "v3fv", IECore.V3fVectorData( [ imath.V3f( 7, 8, 9 ), imath.V3f( 10, 11, 12 ) ] ) ) + self.assertEqual( c["v3fv"], IECore.V3fVectorData( [ imath.V3f( 7, 8, 9 ), imath.V3f( 10, 11, 12 ) ] ) ) + self.assertIsInstance( c["v3fv"], IECore.V3fVectorData ) + self.assertNotEqual( compare, c.hash() ) + def testSwitchTypes( self ) : c = Gaffer.Context() diff --git a/shaders/GafferOSL/Expression.h b/shaders/GafferOSL/Expression.h index dc29bb10315..02bbd71eb55 100644 --- a/shaders/GafferOSL/Expression.h +++ b/shaders/GafferOSL/Expression.h @@ -97,4 +97,66 @@ string context( string name ) return context( name, "" ); } +// Vector context variable queries with index + +int contextElement( string name, int index, int defaultValue ) +{ + int result = defaultValue; + getattribute( "gaffer:context", name, index, result ); + return result; +} + +int contextElement( string name, int index ) +{ + return contextElement( name, index, 0 ); +} + +float contextElement( string name, int index, float defaultValue ) +{ + float result = defaultValue; + getattribute( "gaffer:context", name, index, result ); + return result; +} + +float contextElement( string name, int index ) +{ + return contextElement( name, index, 0.0 ); +} + +color contextElement( string name, int index, color defaultValue ) +{ + color result = defaultValue; + getattribute( "gaffer:context", name, index, result ); + return result; +} + +color contextElement( string name, int index ) +{ + return contextElement( name, index, color( 0.0 ) ); +} + +vector contextElement( string name, int index, vector defaultValue ) +{ + vector result = defaultValue; + getattribute( "gaffer:context", name, index, result ); + return result; +} + +vector contextElement( string name, int index ) +{ + return contextElement( name, index, vector( 0.0 ) ); +} + +string contextElement( string name, int index, string defaultValue ) +{ + string result = defaultValue; + getattribute( "gaffer:context", name, index, result ); + return result; +} + +string contextElement( string name, int index ) +{ + return contextElement( name, index, "" ); +} + #endif // GAFFEROSL_EXPRESSION_H diff --git a/src/GafferOSL/OSLExpressionEngine.cpp b/src/GafferOSL/OSLExpressionEngine.cpp index 5d2fc17ee94..7c9f7b4d033 100644 --- a/src/GafferOSL/OSLExpressionEngine.cpp +++ b/src/GafferOSL/OSLExpressionEngine.cpp @@ -54,8 +54,10 @@ #include "IECoreImage/OpenImageIOAlgo.h" +#include "IECore/DataAlgo.h" #include "IECore/SearchPath.h" #include "IECore/StringAlgo.h" +#include "IECore/TypeTraits.h" #include "OSL/oslcomp.h" #include "OSL/oslexec.h" @@ -102,6 +104,65 @@ struct RenderState // our RenderState. ////////////////////////////////////////////////////////////////////////// + +bool getAttributeInternal( OSL::ShaderGlobals *sg, bool derivatives, ustring object, TypeDesc type, ustring name, std::optional index, void *value ) +{ + const RenderState *renderState = sg ? static_cast( sg->renderstate ) : nullptr; + if( !renderState ) + { + return false; + } + + // TODO - might be nice if there was some way to speed this up by directly querying the type matching + // the TypeDesc, instead of getting as a generic Data? + DataPtr data = renderState->context->getAsData( name.c_str(), nullptr ); + if( !data ) + { + return false; + } + + if( derivatives ) + { + memset( (char*)value + type.size(), 0, 2 * type.size() ); + } + + IECoreImage::OpenImageIOAlgo::DataView dataView( data.get(), /* createUStrings = */ true ); + if( !dataView.data ) + { + if( auto b = runTimeCast( data.get() ) ) + { + // BoolData isn't supported by `DataView` because `OIIO::TypeDesc` doesn't + // have a boolean type. We could work around this in `DataView` by casting to + // `TypeDesc::UCHAR` (along with a `static_assert( sizeof( bool ) == 1`). But that + // wouldn't be round-trippable via `OpenImageIOAlgo::data()`, so it's not clear + // that it would be a good thing in general. Here we don't care about round + // tripping, so we simply perform a conversion ourselves. + const unsigned char c = b->readable(); + return ShadingSystem::convert_value( value, type, &c, TypeDesc::UCHAR ); + } + return false; + } + + int effectiveIndex = 0; + if( index ) + { + effectiveIndex = index.value() < 0 ? dataView.type.arraylen + index.value() : index.value(); + if( effectiveIndex >= dataView.type.arraylen || effectiveIndex < 0 ) + { + return false; + } + dataView.type.arraylen = 0; + } + + return ShadingSystem::convert_value( + value, + type, + (char *)dataView.data + dataView.type.size() * effectiveIndex, + dataView.type + ); +} + + /// \todo Share with OSLRenderer class RendererServices : public OSL::RendererServices @@ -135,47 +196,12 @@ class RendererServices : public OSL::RendererServices bool get_attribute( OSL::ShaderGlobals *sg, bool derivatives, ustring object, TypeDesc type, ustring name, void *value ) override { - const RenderState *renderState = sg ? static_cast( sg->renderstate ) : nullptr; - if( !renderState ) - { - return false; - } - - // TODO - might be nice if there was some way to speed this up by directly querying the type matching - // the TypeDesc, instead of getting as a generic Data? - const DataPtr data = renderState->context->getAsData( name.c_str(), nullptr ); - if( !data ) - { - return false; - } - - if( derivatives ) - { - memset( (char*)value + type.size(), 0, 2 * type.size() ); - } - - IECoreImage::OpenImageIOAlgo::DataView dataView( data.get(), /* createUStrings = */ true ); - if( !dataView.data ) - { - if( auto b = runTimeCast( data.get() ) ) - { - // BoolData isn't supported by `DataView` because `OIIO::TypeDesc` doesn't - // have a boolean type. We could work around this in `DataView` by casting to - // `TypeDesc::UCHAR` (along with a `static_assert( sizeof( bool ) == 1`). But that - // wouldn't be round-trippable via `OpenImageIOAlgo::data()`, so it's not clear - // that it would be a good thing in general. Here we don't care about round - // tripping, so we simply perform a conversion ourselves. - const unsigned char c = b->readable(); - return ShadingSystem::convert_value( value, type, &c, TypeDesc::UCHAR ); - } - return false; - } - return ShadingSystem::convert_value( value, type, dataView.data, dataView.type ); + return getAttributeInternal( sg, derivatives, object, type, name, std::nullopt, value ); } bool get_array_attribute( OSL::ShaderGlobals *sg, bool derivatives, ustring object, TypeDesc type, ustring name, int index, void *value ) override { - return false; + return getAttributeInternal( sg, derivatives, object, type, name, index, value ); } // OSL tries to populate shader parameter values per-object by calling this method.