Example #1
0
IECore::ConstCompoundObjectPtr Grid::computeAttributes( const SceneNode::ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	if( path.size() == 1 )
	{
		CompoundObjectPtr result = new CompoundObject;

		result->members()["gl:curvesPrimitive:useGLLines"] = new BoolData( true );
		result->members()["gl:smoothing:lines"] = new BoolData( true );

		ShaderPtr shader = new Shader( "Constant", "gl:surface" );
		shader->parameters()["Cs"] = new Color3fData( Color3f( 1 ) );
		result->members()["gl:surface"] = shader;

		return result;
	}
	else if( path.size() == 2 )
	{
		float pixelWidth = 1.0f;
		if( path.back() == g_gridLinesName )
		{
			pixelWidth = gridPixelWidthPlug()->getValue();
		}
		else if( path.back() == g_centerLinesName )
		{
			pixelWidth = centerPixelWidthPlug()->getValue();
		}
		else if( path.back() == g_borderLinesName )
		{
			pixelWidth = borderPixelWidthPlug()->getValue();
		}

		CompoundObjectPtr result = new CompoundObject;
		result->members()["gl:curvesPrimitive:glLineWidth"] = new FloatData( pixelWidth );

		return result;
	}
	return outPlug()->attributesPlug()->defaultValue();
}
IECore::ConstCompoundObjectPtr AppleseedShaderAdaptor::computeAttributes( const ScenePath &path, const Gaffer::Context *context, const GafferScene::ScenePlug *parent ) const
{
	ConstCompoundObjectPtr inputAttributes = inPlug()->attributesPlug()->getValue();

	/// \todo Drop support for "osl:shader" assignments. They were never desirable, and the ShaderAssignment node
	/// no longer makes them.
	const ShaderNetwork *shaderNetwork = inputAttributes->member<const ShaderNetwork>( g_oslShaderAttributeName );
	if( !shaderNetwork )
	{
		shaderNetwork = inputAttributes->member<const ShaderNetwork>( g_oslSurfaceAttributeName );
	}
	if( !shaderNetwork )
	{
		return inputAttributes;
	}

	const Shader *outputShader = shaderNetwork->outputShader();
	if( !outputShader )
	{
		return inputAttributes;
	}

	OSLQuery::Parameter *firstOutput = firstOutputParameter( outputShader->getName() );

	// Build an adaptor network

	ShaderNetworkPtr adaptedNetwork;
	if( firstOutput && firstOutput->isclosure )
	{
		adaptedNetwork = shaderNetwork->copy();
		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { adaptedNetwork->getOutput().shader, firstOutput->name.string() }, { materialHandle, g_bsdfParameterName } }
		);
		adaptedNetwork->setOutput( materialHandle );

	}
	else if( firstOutput && firstOutput->type == TypeDesc::TypeColor )
	{
		adaptedNetwork = shaderNetwork->copy();
		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		InternedString emissionHandle = adaptedNetwork->addShader( "emission", std::move( emission ) );
		adaptedNetwork->addConnection(
			{ { adaptedNetwork->getOutput().shader, firstOutput->name.string() }, { emissionHandle, "Color" } }
		);

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { emissionHandle, "BSDF" }, { materialHandle, "BSDF" } }
		);

		adaptedNetwork->setOutput( materialHandle );
	}
	else if( firstOutput && ( firstOutput->type == TypeDesc::TypeFloat || firstOutput->type == TypeDesc::TypeInt ) )
	{
		adaptedNetwork = shaderNetwork->copy();
		ShaderPtr colorBuild = new Shader( "color/as_color_build", "osl:shader" );
		InternedString colorBuildHandle = adaptedNetwork->addShader( "colorBuild", std::move( colorBuild ) );
		for( const auto &channel : { "R", "G", "B" } )
		{
			adaptedNetwork->addConnection(
				{ { adaptedNetwork->getOutput().shader, firstOutput->name.string() }, { colorBuildHandle, channel } }
			);
		}

		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		InternedString emissionHandle = adaptedNetwork->addShader( "emission", std::move( emission ) );
		adaptedNetwork->addConnection(
			{ { colorBuildHandle, "ColorOut" }, { emissionHandle, "Color" } }
		);

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { emissionHandle, "BSDF" }, { materialHandle, "BSDF" } }
		);
		adaptedNetwork->setOutput( materialHandle );
	}
	else if( firstOutput && firstOutput->type == TypeDesc::TypeVector )
	{
		adaptedNetwork = shaderNetwork->copy();
		ShaderPtr vectorSplit = new Shader( "vector/as_vector_split", "osl:shader" );
		InternedString vectorSplitHandle = adaptedNetwork->addShader( "vectorSplit", std::move( vectorSplit ) );
		adaptedNetwork->addConnection(
			{ { adaptedNetwork->getOutput().shader, firstOutput->name.string() }, { vectorSplitHandle, "Vector" } }
		);

		ShaderPtr colorBuild = new Shader( "color/as_color_build", "osl:shader" );
		InternedString colorBuildHandle = adaptedNetwork->addShader( "colorBuild", std::move( colorBuild ) );
		for( const auto &c : { make_pair( "X", "R" ), make_pair( "Y", "G" ), make_pair( "Z", "B" ) } )
		{
			adaptedNetwork->addConnection(
				{ { vectorSplitHandle, c.first }, { colorBuildHandle, c.second } }
			);
		}

		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		InternedString emissionHandle = adaptedNetwork->addShader( "emission", std::move( emission ) );
		adaptedNetwork->addConnection(
			{ { colorBuildHandle, "ColorOut" }, { emissionHandle, "Color" } }
		);

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { emissionHandle, "BSDF" }, { materialHandle, "BSDF" } }
		);
		adaptedNetwork->setOutput( materialHandle );
	}
	else
	{
		// Shader has no output, or an output we can't map sensibly.
		// Make an "error" shader.
		adaptedNetwork = new ShaderNetwork;
		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new Color3fData( Imath::Color3f( 1, 0, 0 ) );
		InternedString emissionHandle = adaptedNetwork->addShader( "emission", std::move( emission ) );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { emissionHandle, "BSDF" }, { materialHandle, "BSDF" } }
		);
		adaptedNetwork->setOutput( materialHandle );
	}

	// Place the new network into the "osl:surface" attribute
	// and remove the "osl:shader" attribute.

	CompoundObjectPtr outputAttributes = new CompoundObject;
	outputAttributes->members() = inputAttributes->members(); // Shallow copy for speed - do not modify in place!
	outputAttributes->members()[g_oslSurfaceAttributeName] = adaptedNetwork;
	outputAttributes->members().erase( g_oslShaderAttributeName );

	return outputAttributes;
}
Example #3
0
ObjectPtr SLOReader::doOperation( const CompoundObject * operands )
{
	tbb::mutex::scoped_lock lock( g_mutex );
	
	if( Slo_SetShader( (char *)fileName().c_str() ) )
	{
		throw Exception( boost::str( boost::format( "Unable to set shader to \"%s\"" ) % fileName() ) );
	}

	string name = Slo_GetName();
	string type = Slo_TypetoStr( Slo_GetType() );
	ShaderPtr result = new Shader( name, type );

	CompoundDataPtr typeHints = new CompoundData;
	result->blindData()->writable().insert( pair<string, DataPtr>( "ri:parameterTypeHints", typeHints ) );

	// we lose the ordering of parameter names when we put them in result->parameters(),
	// so we stick the correct order in the blind data as a workaround for anyone interested
	// in the true ordering.
	StringVectorDataPtr orderedParameterNames = new StringVectorData;
	result->blindData()->writable().insert( pair<string, DataPtr>( "ri:orderedParameterNames", orderedParameterNames ) );	

	// we don't have a way of communicating which parameters are outputs in the Shader::parametersData(),
	// so we work around that using the blind data too.
	StringVectorDataPtr outputParameterNames = new StringVectorData;
	result->blindData()->writable().insert( pair<string, DataPtr>( "ri:outputParameterNames", outputParameterNames ) );	

	int numArgs = Slo_GetNArgs();
	for( int i=1; i<=numArgs; i++ )
	{
		DataPtr data = 0;

		SLO_VISSYMDEF *arg = Slo_GetArgById( i );
		switch( arg->svd_type )
		{
			case SLO_TYPE_POINT :
			case SLO_TYPE_VECTOR :
			case SLO_TYPE_NORMAL :
				{
					if( arg->svd_arraylen==0 )
					{
						const SLO_POINT *p = arg->svd_default.pointval;
						if( p )
						{
							data = new V3fData( V3f( p->xval, p->yval, p->zval ) );
						}
						else
						{
							// 0 length and null value signifies a variable length array
							data = new V3fVectorData();
						}
					}
					else
					{
						V3fVectorDataPtr vData = new V3fVectorData();
						data = vData;
						for( int j=0; j<arg->svd_arraylen; j++ )
						{
							SLO_VISSYMDEF *a = Slo_GetArrayArgElement( arg, j );
							const SLO_POINT *p = a->svd_default.pointval;
							vData->writable().push_back( V3f( p->xval, p->yval, p->zval ) );
						}
					}
					typeHints->writable().insert( pair<string, DataPtr>( arg->svd_name, new StringData( Slo_TypetoStr( arg->svd_type ) ) ) );
					break;
				}

			case SLO_TYPE_COLOR :
				{
					if( arg->svd_arraylen==0 )
					{
						const SLO_POINT *p = arg->svd_default.pointval;
						if( p )
						{
							data = new Color3fData( Color3f( p->xval, p->yval, p->zval ) );
						}
						else
						{
							// 0 length and null value signifies a variable length array
							data = new Color3fVectorData();
						}
					}
					else
					{
						Color3fVectorDataPtr vData = new Color3fVectorData();
						data = vData;
						for( int j=0; j<arg->svd_arraylen; j++ )
						{
							SLO_VISSYMDEF *a = Slo_GetArrayArgElement( arg, j );
							const SLO_POINT *p = a->svd_default.pointval;
							vData->writable().push_back( Color3f( p->xval, p->yval, p->zval ) );
						}
					}
				}
				break;

			case SLO_TYPE_SCALAR :
				{
					if( arg->svd_arraylen==0 )
					{
						const float *value = arg->svd_default.scalarval;
						if( value )
						{
							data = new FloatData( *value );
						}
						else
						{
							// 0 length and null value signifies a variable length array
							data = new FloatVectorData();
						}
					}
					else
					{
						FloatVectorDataPtr vData = new FloatVectorData();
						data = vData;
						for( int j=0; j<arg->svd_arraylen; j++ )
						{
							SLO_VISSYMDEF *a = Slo_GetArrayArgElement( arg, j );
							vData->writable().push_back( *(a->svd_default.scalarval) );
						}
						if( arg->svd_arraylen==3 )
						{
							// allow V3fData and V3fVectorData to be mapped to float[3] parameters.
							typeHints->writable().insert( pair<string, DataPtr>( arg->svd_name, new StringData( "float[3]" ) ) );
						}
					}
				}
				break;

			case SLO_TYPE_STRING :
				{
					if( arg->svd_arraylen==0 )
					{
						const char *defaultValue = arg->svd_default.stringval;
						if( defaultValue )
						{
							data = new StringData( defaultValue );
						}
						else
						{
							// 0 length and null value signifies a variable length array
							data = new StringVectorData();
						}
					}
					else
					{
						StringVectorDataPtr vData = new StringVectorData();
						data = vData;
						for( int j=0; j<arg->svd_arraylen; j++ )
						{
							SLO_VISSYMDEF *a = Slo_GetArrayArgElement( arg, j );
							// sometimes the default value for an element of a string array can be a null pointer.
							// i'm not sure what the meaning of this is. the 3delight shaderinfo utility reports such values
							// as "(null)", so that's what we do too.
							const char *defaultValue = a->svd_default.stringval;
							vData->writable().push_back( defaultValue ? defaultValue : "(null)" );
						}
					}
				}
				break;

			case SLO_TYPE_MATRIX :
				{
					if( arg->svd_arraylen==0 )
					{
						const float *m = arg->svd_default.matrixval;
						if( m )
						{
							M44f mm(	m[0], m[1], m[2], m[3],
										m[4], m[5], m[6], m[7],
										m[8], m[9], m[10], m[11],
										m[12], m[13], m[14], m[15] 	);
							data = new M44fData( mm );
						}
						else
						{
							// 0 length and null value signifies a variable length array
							data = new M44fVectorData();
						}
					}
					else
					{
						M44fVectorDataPtr vData = new M44fVectorData();
						data = vData;
						for( int j=0; j<arg->svd_arraylen; j++ )
						{
							SLO_VISSYMDEF *a = Slo_GetArrayArgElement( arg, j );
							const float *m = a->svd_default.matrixval;
							M44f mm(	m[0], m[1], m[2], m[3],
										m[4], m[5], m[6], m[7],
										m[8], m[9], m[10], m[11],
										m[12], m[13], m[14], m[15] 	);
							vData->writable().push_back( mm );
						}
					}
				}
				break;
				
			case SLO_TYPE_SHADER :
				{
					if( arg->svd_arraylen==0 )
					{
						if( !arg->svd_valisvalid )
						{
							// variable length array
							data = new StringVectorData();
						}
						else
						{
							data = new StringData();
						}
					}
					else
					{
						StringVectorDataPtr sData = new StringVectorData();
						data = sData;
						sData->writable().resize( arg->svd_arraylen );
					}
					typeHints->writable().insert( pair<string, DataPtr>( arg->svd_name, new StringData( Slo_TypetoStr( arg->svd_type ) ) ) );
				}
				break;

			default :

				msg( Msg::Warning, "SLOReader::read", format( "Parameter \"%s\" has unsupported type." ) % arg->svd_name );
		}

		if( data )
		{
			orderedParameterNames->writable().push_back( arg->svd_name );
			result->parameters().insert( CompoundDataMap::value_type( arg->svd_name, data ) );
			if( arg->svd_storage == SLO_STOR_OUTPUTPARAMETER )
			{
				outputParameterNames->writable().push_back( arg->svd_name );
			}
		}

	}

	// shader annotations
	
	CompoundDataPtr annotations = new CompoundData;
	result->blindData()->writable().insert( pair<string, DataPtr>( "ri:annotations", annotations ) );
	
#ifndef PRMANEXPORT
	for( int i=1, n=Slo_GetNAnnotations(); i <= n; i++ )
	{
		const char *key = Slo_GetAnnotationKeyById( i );
		annotations->writable()[key] = new StringData( Slo_GetAnnotationByKey( key ) );
	}
#endif

	Slo_EndShader();
	return result;
}
IECore::ConstCompoundObjectPtr AppleseedShaderAdaptor::computeAttributes( const ScenePath &path, const Gaffer::Context *context, const GafferScene::ScenePlug *parent ) const
{
	ConstCompoundObjectPtr inputAttributes = inPlug()->attributesPlug()->getValue();

	const ObjectVector *shaderNetwork = inputAttributes->member<const ObjectVector>( g_oslShaderAttributeName );
	if( !shaderNetwork || shaderNetwork->members().empty() )
	{
		return inputAttributes;
	}

	const Shader *rootShader = runTimeCast<const Shader>( shaderNetwork->members().back().get() );
	if( !rootShader )
	{
		return inputAttributes;
	}

	OSLQuery::Parameter *firstOutput = firstOutputParameter( rootShader->getName() );

	// Build an adapter network if we can.

	bool prependInputNetwork = true;
	vector<ShaderPtr> adapters;
	if( firstOutput && firstOutput->isclosure )
	{
		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterInputHandle." + firstOutput->name.string() );
		adapters.push_back( material );
	}
	else if( firstOutput && firstOutput->type == TypeDesc::TypeColor )
	{
		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new StringData( "link:adapterInputHandle." + firstOutput->name.string() );
		emission->parameters()[g_handleParameterName] = new StringData( "adapterEmissionHandle" );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterEmissionHandle.BSDF" );

		adapters.push_back( emission );
		adapters.push_back( material );
	}
	else if( firstOutput && ( firstOutput->type == TypeDesc::TypeFloat || firstOutput->type == TypeDesc::TypeInt ) )
	{
		ShaderPtr colorBuild = new Shader( "color/as_color_build", "osl:shader" );
		StringDataPtr colorLink = new StringData( "link:adapterInputHandle." + firstOutput->name.string() );
		colorBuild->parameters()["R"] = colorLink;
		colorBuild->parameters()["G"] = colorLink;
		colorBuild->parameters()["B"] = colorLink;
		colorBuild->parameters()[g_handleParameterName] = new StringData( "adapterColorBuildHandle" );

		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new StringData( "link:adapterColorBuildHandle.ColorOut" );
		emission->parameters()[g_handleParameterName] = new StringData( "adapterEmissionHandle" );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterEmissionHandle.BSDF" );

		adapters.push_back( colorBuild );
		adapters.push_back( emission );
		adapters.push_back( material );
	}
	else if( firstOutput && firstOutput->type == TypeDesc::TypeVector )
	{
		ShaderPtr vectorSplit = new Shader( "vector/as_vector_split", "osl:shader" );
		vectorSplit->parameters()["Vector"] = new StringData( "link:adapterInputHandle." + firstOutput->name.string() );
		vectorSplit->parameters()[g_handleParameterName] = new StringData( "adapterVectorSplit" );

		ShaderPtr colorBuild = new Shader( "color/as_color_build", "osl:shader" );
		colorBuild->parameters()["R"] = new StringData( "link:adapterVectorSplit.X" );
		colorBuild->parameters()["G"] = new StringData( "link:adapterVectorSplit.Y" );
		colorBuild->parameters()["B"] = new StringData( "link:adapterVectorSplit.Z" );
		colorBuild->parameters()[g_handleParameterName] = new StringData( "adapterColorBuildHandle" );

		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new StringData( "link:adapterColorBuildHandle.ColorOut" );
		emission->parameters()[g_handleParameterName] = new StringData( "adapterEmissionHandle" );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterEmissionHandle.BSDF" );

		adapters.push_back( vectorSplit );
		adapters.push_back( colorBuild );
		adapters.push_back( emission );
		adapters.push_back( material );
	}
	else
	{
		// Shader has no output, or an output we can't map sensibly.
		// Make an "error" shader.
		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new Color3fData( Imath::Color3f( 1, 0, 0 ) );
		emission->parameters()[g_handleParameterName] = new StringData( "adapterEmissionHandle" );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterEmissionHandle.BSDF" );

		adapters.push_back( emission );
		adapters.push_back( material );

		prependInputNetwork = false; // We don't need the original shaders at all
	}

	// Make a new network with the adapter network
	// appended onto the input network

	ObjectVectorPtr adaptedNetwork = new ObjectVector();

	if( prependInputNetwork )
	{
		adaptedNetwork->members() = shaderNetwork->members(); // Shallow copy for speed - do not modify in place!

		ShaderPtr rootShaderCopy = rootShader->copy();
		rootShaderCopy->parameters()[g_handleParameterName] = new StringData( "adapterInputHandle" );;
		adaptedNetwork->members().back() = rootShaderCopy;
	}

	std::copy( adapters.begin(), adapters.end(), back_inserter( adaptedNetwork->members() ) );

	// Place the new network into the "osl:surface" attribute
	// and remove the "osl:shader" attribute.

	CompoundObjectPtr outputAttributes = new CompoundObject;
	outputAttributes->members() = inputAttributes->members(); // Shallow copy for speed - do not modify in place!
	outputAttributes->members()[g_oslSurfaceAttributeName] = adaptedNetwork;
	outputAttributes->members().erase( g_oslShaderAttributeName );

	return outputAttributes;
}
IECore::ConstCompoundObjectPtr AttributeVisualiser::computeProcessedAttributes( const ScenePath &path, const Gaffer::Context *context, IECore::ConstCompoundObjectPtr inputAttributes ) const
{
	const std::string attributeName = attributeNamePlug()->getValue();
	if( !attributeName.size() )
	{
		return inputAttributes;
	}

	const std::string shaderType = shaderTypePlug()->getValue();
	if( !shaderType.size() )
	{
		return inputAttributes;
	}

	const Object *attribute = inputAttributes->member<Object>( attributeName );
	if( !attribute )
	{
		if( !inputAttributes->member<Object>( shaderType ) )
		{
			return inputAttributes;
		}
	}

	CompoundObjectPtr result = new CompoundObject;
	// Since we're not going to modify any existing members (only add a new one),
	// and our result becomes const on returning it, we can directly reference
	// the input members in our result without copying. Be careful not to modify
	// them though!
	result->members() = inputAttributes->members();

	if( !attribute )
	{
		result->members().erase( shaderType );
		return result;
	}

	// Compute our colour.

	Color3f color( 0.0f );
	const Mode mode = (Mode)modePlug()->getValue();
	if( mode == Random )
	{
		Rand32 r( tbb_hasher( attribute->hash() ) );
		for( int i = 0; i < 3; ++i )
		{
			color[i] = r.nextf();
		}
	}
	else if( mode == ShaderNodeColor )
	{
		const Shader *shader = runTimeCast<const Shader>( attribute );
		if( !shader )
		{
			if( const ShaderNetwork *network = runTimeCast<const ShaderNetwork>( attribute ) )
			{
				shader = network->outputShader();
			}
		}
		if( shader )
		{
			const Color3fData *colorData = shader->blindData()->member<const Color3fData>( "gaffer:nodeColor" );
			if( colorData )
			{
				color = colorData->readable();
			}
		}
	}
	else
	{
		// Color or FalseColor
		switch( attribute->typeId() )
		{
			case FloatDataTypeId :
				color = Color3f( static_cast<const FloatData *>( attribute )->readable() );
				break;
			case DoubleDataTypeId :
				color = Color3f( static_cast<const DoubleData *>( attribute )->readable() );
				break;
			case IntDataTypeId :
				color = Color3f( static_cast<const IntData *>( attribute )->readable() );
				break;
			case BoolDataTypeId :
				color = Color3f( static_cast<const BoolData *>( attribute )->readable() );
				break;
			case Color3fDataTypeId :
				color = static_cast<const Color3fData *>( attribute )->readable();
				break;
			default :
				throw IECore::Exception( boost::str(
					boost::format( "Unsupported attribute data type \"%s\"" ) % attribute->typeName()
				) );
		}
		const Color3f min( minPlug()->getValue() );
		const Color3f max( maxPlug()->getValue() );
		color = ( color - min ) / ( max - min );
		if( mode == FalseColor )
		{
			const SplinefColor3f ramp = rampPlug()->getValue().spline();
			color = ramp( color[0] );
		}
	}

	// Apply the colour using a shader.

	ShaderPtr shader = new Shader( shaderNamePlug()->getValue(), shaderType );
	shader->parameters()[shaderParameterPlug()->getValue()] = new Color3fData( color );
	ShaderNetworkPtr shaderNetwork = new ShaderNetwork;
	const InternedString handle = shaderNetwork->addShader( "surface", std::move( shader ) );
	shaderNetwork->setOutput( handle );

	result->members()[shaderType] = shaderNetwork;

	return result;
}