Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.38.10/source/MaterialXGenOsl/OslShaderGenerator.cpp
2025-05-18 13:04:45 +08:00

674 lines
30 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXGenOsl/OslShaderGenerator.h>
#include <MaterialXGenOsl/OslSyntax.h>
#include <MaterialXGenShader/GenContext.h>
#include <MaterialXGenShader/Shader.h>
#include <MaterialXGenShader/ShaderStage.h>
#include <MaterialXGenShader/Nodes/SwizzleNode.h>
#include <MaterialXGenShader/Nodes/ConvertNode.h>
#include <MaterialXGenShader/Nodes/CombineNode.h>
#include <MaterialXGenShader/Nodes/SwitchNode.h>
#include <MaterialXGenShader/Nodes/SourceCodeNode.h>
#include <MaterialXGenShader/Nodes/ClosureAddNode.h>
#include <MaterialXGenShader/Nodes/ClosureMixNode.h>
#include <MaterialXGenShader/Nodes/ClosureMultiplyNode.h>
#include <MaterialXGenOsl/Nodes/BlurNodeOsl.h>
#include <MaterialXGenOsl/Nodes/SurfaceNodeOsl.h>
#include <MaterialXGenOsl/Nodes/ClosureLayerNodeOsl.h>
#include <MaterialXGenOsl/Nodes/MaterialNodeOsl.h>
MATERIALX_NAMESPACE_BEGIN
const string OslShaderGenerator::TARGET = "genosl";
//
// OslShaderGenerator methods
//
OslShaderGenerator::OslShaderGenerator() :
ShaderGenerator(OslSyntax::create())
{
// Register build-in implementations
// <!-- <switch> -->
// <!-- 'which' type : float -->
registerImplementation("IM_switch_float_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_color3_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_color4_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_vector2_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_vector3_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_vector4_" + OslShaderGenerator::TARGET, SwitchNode::create);
// <!-- 'which' type : integer -->
registerImplementation("IM_switch_floatI_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_color3I_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_color4I_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_vector2I_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_vector3I_" + OslShaderGenerator::TARGET, SwitchNode::create);
registerImplementation("IM_switch_vector4I_" + OslShaderGenerator::TARGET, SwitchNode::create);
// <!-- <swizzle> -->
// <!-- from type : float -->
registerImplementation("IM_swizzle_float_color3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_float_color4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_float_vector2_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_float_vector3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_float_vector4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
// <!-- from type : color3 -->
registerImplementation("IM_swizzle_color3_float_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color3_color3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color3_color4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color3_vector2_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color3_vector3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color3_vector4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
// <!-- from type : color4 -->
registerImplementation("IM_swizzle_color4_float_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color4_color3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color4_color4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color4_vector2_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color4_vector3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_color4_vector4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
// <!-- from type : vector2 -->
registerImplementation("IM_swizzle_vector2_float_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector2_color3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector2_color4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector2_vector2_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector2_vector3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector2_vector4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
// <!-- from type : vector3 -->
registerImplementation("IM_swizzle_vector3_float_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector3_color3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector3_color4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector3_vector2_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector3_vector3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector3_vector4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
// <!-- from type : vector4 -->
registerImplementation("IM_swizzle_vector4_float_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector4_color3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector4_color4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector4_vector2_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector4_vector3_" + OslShaderGenerator::TARGET, SwizzleNode::create);
registerImplementation("IM_swizzle_vector4_vector4_" + OslShaderGenerator::TARGET, SwizzleNode::create);
// <!-- <convert> -->
registerImplementation("IM_convert_float_color3_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_float_color4_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_float_vector2_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_float_vector3_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_float_vector4_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_vector2_vector3_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_vector3_vector2_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_vector3_color3_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_vector3_vector4_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_vector4_vector3_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_vector4_color4_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_color3_vector3_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_color4_vector4_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_color3_color4_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_color4_color3_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_boolean_float_" + OslShaderGenerator::TARGET, ConvertNode::create);
registerImplementation("IM_convert_integer_float_" + OslShaderGenerator::TARGET, ConvertNode::create);
// <!-- <combine> -->
registerImplementation("IM_combine2_vector2_" + OslShaderGenerator::TARGET, CombineNode::create);
registerImplementation("IM_combine2_color4CF_" + OslShaderGenerator::TARGET, CombineNode::create);
registerImplementation("IM_combine2_vector4VF_" + OslShaderGenerator::TARGET, CombineNode::create);
registerImplementation("IM_combine2_vector4VV_" + OslShaderGenerator::TARGET, CombineNode::create);
registerImplementation("IM_combine3_color3_" + OslShaderGenerator::TARGET, CombineNode::create);
registerImplementation("IM_combine3_vector3_" + OslShaderGenerator::TARGET, CombineNode::create);
registerImplementation("IM_combine4_color4_" + OslShaderGenerator::TARGET, CombineNode::create);
registerImplementation("IM_combine4_vector4_" + OslShaderGenerator::TARGET, CombineNode::create);
// <!-- <blur> -->
registerImplementation("IM_blur_float_" + OslShaderGenerator::TARGET, BlurNodeOsl::create);
registerImplementation("IM_blur_color3_" + OslShaderGenerator::TARGET, BlurNodeOsl::create);
registerImplementation("IM_blur_color4_" + OslShaderGenerator::TARGET, BlurNodeOsl::create);
registerImplementation("IM_blur_vector2_" + OslShaderGenerator::TARGET, BlurNodeOsl::create);
registerImplementation("IM_blur_vector3_" + OslShaderGenerator::TARGET, BlurNodeOsl::create);
registerImplementation("IM_blur_vector4_" + OslShaderGenerator::TARGET, BlurNodeOsl::create);
// <!-- <layer> -->
registerImplementation("IM_layer_bsdf_" + OslShaderGenerator::TARGET, ClosureLayerNodeOsl::create);
registerImplementation("IM_layer_vdf_" + OslShaderGenerator::TARGET, ClosureLayerNodeOsl::create);
#ifdef MATERIALX_OSL_LEGACY_CLOSURES
// <!-- <mix> -->
registerImplementation("IM_mix_bsdf_" + OslShaderGenerator::TARGET, ClosureMixNode::create);
registerImplementation("IM_mix_edf_" + OslShaderGenerator::TARGET, ClosureMixNode::create);
// <!-- <add> -->
registerImplementation("IM_add_bsdf_" + OslShaderGenerator::TARGET, ClosureAddNode::create);
registerImplementation("IM_add_edf_" + OslShaderGenerator::TARGET, ClosureAddNode::create);
// <!-- <multiply> -->
registerImplementation("IM_multiply_bsdfC_" + OslShaderGenerator::TARGET, ClosureMultiplyNode::create);
registerImplementation("IM_multiply_bsdfF_" + OslShaderGenerator::TARGET, ClosureMultiplyNode::create);
registerImplementation("IM_multiply_edfC_" + OslShaderGenerator::TARGET, ClosureMultiplyNode::create);
registerImplementation("IM_multiply_edfF_" + OslShaderGenerator::TARGET, ClosureMultiplyNode::create);
#endif // MATERIALX_OSL_LEGACY_CLOSURES
// <!-- <thin_film> -->
registerImplementation("IM_thin_film_bsdf_" + OslShaderGenerator::TARGET, NopNode::create);
// <!-- <surface> -->
registerImplementation("IM_surface_" + OslShaderGenerator::TARGET, SurfaceNodeOsl::create);
// <!-- <surfacematerial> -->
registerImplementation("IM_surfacematerial_" + OslShaderGenerator::TARGET, MaterialNodeOsl::create);
}
ShaderPtr OslShaderGenerator::generate(const string& name, ElementPtr element, GenContext& context) const
{
ShaderPtr shader = createShader(name, element, context);
// Request fixed floating-point notation for consistency across targets.
ScopedFloatFormatting fmt(Value::FloatFormatFixed);
ShaderGraph& graph = shader->getGraph();
ShaderStage& stage = shader->getStage(Stage::PIXEL);
emitLibraryIncludes(stage, context);
// Add global constants and type definitions
emitTypeDefinitions(context, stage);
emitLine("#define M_FLOAT_EPS 1e-8", stage, false);
emitLineBreak(stage);
// Set the include file to use for uv transformations,
// depending on the vertical flip flag.
if (context.getOptions().fileTextureVerticalFlip)
{
_tokenSubstitutions[ShaderGenerator::T_FILE_TRANSFORM_UV] = "mx_transform_uv_vflip.osl";
}
else
{
_tokenSubstitutions[ShaderGenerator::T_FILE_TRANSFORM_UV] = "mx_transform_uv.osl";
}
// Emit function definitions for all nodes
emitFunctionDefinitions(graph, context, stage);
// Emit shader type, determined from the first
// output if there are multiple outputs.
const ShaderGraphOutputSocket* outputSocket0 = graph.getOutputSocket(0);
if (*outputSocket0->getType() == *Type::SURFACESHADER)
{
emitString("surface ", stage);
}
else if (*outputSocket0->getType() == *Type::VOLUMESHADER)
{
emitString("volume ", stage);
}
else
{
emitString("shader ", stage);
}
// Begin shader signature. Note that makeIdentifier() will sanitize the name.
string functionName = shader->getName();
_syntax->makeIdentifier(functionName, graph.getIdentifierMap());
setFunctionName(functionName, stage);
emitLine(functionName, stage, false);
const ShaderMetadataVecPtr& metadata = graph.getMetadata();
bool haveShaderMetaData = metadata && metadata->size();
// Always emit node information
emitScopeBegin(stage, Syntax::DOUBLE_SQUARE_BRACKETS);
emitLine("string mtlx_category = \"" + element->getCategory() + "\"" + Syntax::COMMA, stage, false);
emitLine("string mtlx_name = \"" + element->getQualifiedName(element->getName()) + "\"" +
(haveShaderMetaData ? Syntax::COMMA : EMPTY_STRING),
stage, false);
// Add any metadata if set on the graph.
if (haveShaderMetaData)
{
for (size_t j = 0; j < metadata->size(); ++j)
{
const ShaderMetadata& data = metadata->at(j);
const string& delim = (j == metadata->size() - 1) ? EMPTY_STRING : Syntax::COMMA;
const string& dataType = _syntax->getTypeName(data.type);
const string dataValue = _syntax->getValue(data.type, *data.value, true);
emitLine(dataType + " " + data.name + " = " + dataValue + delim, stage, false);
}
}
emitScopeEnd(stage, false, false);
emitLineEnd(stage, false);
emitScopeBegin(stage, Syntax::PARENTHESES);
// Emit shader inputs
emitShaderInputs(stage.getInputBlock(OSL::INPUTS), stage);
emitShaderInputs(stage.getUniformBlock(OSL::UNIFORMS), stage);
// Emit shader output
const VariableBlock& outputs = stage.getOutputBlock(OSL::OUTPUTS);
const ShaderPort* singleOutput = outputs.size() == 1 ? outputs[0] : NULL;
const bool isSurfaceShaderOutput = singleOutput && *singleOutput->getType() == *Type::SURFACESHADER;
#ifdef MATERIALX_OSL_LEGACY_CLOSURES
const bool isBsdfOutput = singleOutput && *singleOutput->getType() == *Type::BSDF;
#endif
if (isSurfaceShaderOutput)
{
// Special case for having 'surfaceshader' as final output type.
// This type is a struct internally (BSDF, EDF, opacity) so we must
// declare this as a single closure color type in order for renderers
// to understand this output.
emitLine("output closure color " + singleOutput->getVariable() + " = 0", stage, false);
}
#ifdef MATERIALX_OSL_LEGACY_CLOSURES
else if (isBsdfOutput)
{
// Special case for having 'BSDF' as final output type.
// For legacy closures this type is a struct internally (response, throughput, thickness, ior)
// so we must declare this as a single closure color type in order for renderers
// to understand this output.
emitLine("output closure color " + singleOutput->getVariable() + " = 0", stage, false);
}
#endif
else
{
// Just emit all outputs the way they are declared.
emitShaderOutputs(outputs, stage);
}
// End shader signature
emitScopeEnd(stage);
// Begin shader body
emitFunctionBodyBegin(graph, context, stage);
// Emit constants
const VariableBlock& constants = stage.getConstantBlock();
if (constants.size())
{
emitVariableDeclarations(constants, _syntax->getConstantQualifier(), Syntax::SEMICOLON, context, stage);
emitLineBreak(stage);
}
// Inputs of type 'filename' has been generated into two shader inputs.
// So here we construct a single 'textureresource' from these inputs,
// to be used further downstream. See emitShaderInputs() for details.
VariableBlock& inputs = stage.getUniformBlock(OSL::UNIFORMS);
for (size_t i = 0; i < inputs.size(); ++i)
{
ShaderPort* input = inputs[i];
if (*input->getType() == *Type::FILENAME)
{
// Construct the textureresource variable.
const string newVariableName = input->getVariable() + "_";
const string& type = _syntax->getTypeName(input->getType());
emitLine(type + newVariableName + " = {" + input->getVariable() + ", " + input->getVariable() + "_colorspace}", stage);
// Update the variable name to be used downstream.
input->setVariable(newVariableName);
}
}
// Emit all texturing nodes. These are inputs to any
// closure/shader nodes and need to be emitted first.
emitFunctionCalls(graph, context, stage, ShaderNode::Classification::TEXTURE);
// Emit function calls for "root" closure/shader nodes.
// These will internally emit function calls for any dependent closure nodes upstream.
for (ShaderGraphOutputSocket* socket : graph.getOutputSockets())
{
if (socket->getConnection())
{
const ShaderNode* upstream = socket->getConnection()->getNode();
if (upstream->getParent() == &graph &&
(upstream->hasClassification(ShaderNode::Classification::CLOSURE) ||
upstream->hasClassification(ShaderNode::Classification::SHADER)))
{
emitFunctionCall(*upstream, context, stage);
}
}
}
// Emit final outputs
if (isSurfaceShaderOutput)
{
// Special case for having 'surfaceshader' as final output type.
// This type is a struct internally (BSDF, EDF, opacity) so we must
// comvert this to a single closure color type in order for renderers
// to understand this output.
const ShaderGraphOutputSocket* socket = graph.getOutputSocket(0);
const string result = getUpstreamResult(socket, context);
emitScopeBegin(stage);
emitLine("float opacity_weight = clamp(" + result + ".opacity, 0.0, 1.0)", stage);
emitLine(singleOutput->getVariable() + " = (" + result + ".bsdf + " + result + ".edf) * opacity_weight + transparent() * (1.0 - opacity_weight)", stage);
emitScopeEnd(stage);
}
#ifdef MATERIALX_OSL_LEGACY_CLOSURES
else if (isBsdfOutput)
{
// Special case for having 'BSDF' as final output type.
// For legacy closures this type is a struct internally (response, throughput, thickness, ior)
// so we must declare this as a single closure color type in order for renderers
// to understand this output.
const ShaderGraphOutputSocket* socket = graph.getOutputSocket(0);
const string result = getUpstreamResult(socket, context);
emitLine(singleOutput->getVariable() + " = " + result + ".response", stage);
}
#endif
else
{
// Assign results to final outputs.
for (size_t i = 0; i < outputs.size(); ++i)
{
const ShaderGraphOutputSocket* outputSocket = graph.getOutputSocket(i);
const string result = getUpstreamResult(outputSocket, context);
emitLine(outputSocket->getVariable() + " = " + result, stage);
}
}
// End shader body
emitFunctionBodyEnd(graph, context, stage);
// Perform token substitution
replaceTokens(_tokenSubstitutions, stage);
return shader;
}
void OslShaderGenerator::registerShaderMetadata(const DocumentPtr& doc, GenContext& context) const
{
// Register all standard metadata.
ShaderGenerator::registerShaderMetadata(doc, context);
ShaderMetadataRegistryPtr registry = context.getUserData<ShaderMetadataRegistry>(ShaderMetadataRegistry::USER_DATA_NAME);
if (!registry)
{
throw ExceptionShaderGenError("Registration of metadata faild");
}
// Rename the standard metadata names to corresponding OSL metadata names.
const StringMap nameRemapping =
{
{ ValueElement::UI_NAME_ATTRIBUTE, "label" },
{ ValueElement::UI_FOLDER_ATTRIBUTE, "page" },
{ ValueElement::UI_MIN_ATTRIBUTE, "min" },
{ ValueElement::UI_MAX_ATTRIBUTE, "max" },
{ ValueElement::UI_SOFT_MIN_ATTRIBUTE, "slidermin" },
{ ValueElement::UI_SOFT_MAX_ATTRIBUTE, "slidermax" },
{ ValueElement::UI_STEP_ATTRIBUTE, "sensitivity" },
{ ValueElement::DOC_ATTRIBUTE, "help" }
};
for (auto it : nameRemapping)
{
ShaderMetadata* data = registry->findMetadata(it.first);
if (data)
{
data->name = it.second;
}
}
}
ShaderPtr OslShaderGenerator::createShader(const string& name, ElementPtr element, GenContext& context) const
{
// Create the root shader graph
ShaderGraphPtr graph = ShaderGraph::create(nullptr, name, element, context);
ShaderPtr shader = std::make_shared<Shader>(name, graph);
// Create our stage.
ShaderStagePtr stage = createStage(Stage::PIXEL, *shader);
stage->createUniformBlock(OSL::UNIFORMS);
stage->createInputBlock(OSL::INPUTS);
stage->createOutputBlock(OSL::OUTPUTS);
// Create shader variables for all nodes that need this.
createVariables(graph, context, *shader);
// Create uniforms for the published graph interface.
VariableBlock& uniforms = stage->getUniformBlock(OSL::UNIFORMS);
for (ShaderGraphInputSocket* inputSocket : graph->getInputSockets())
{
// Only for inputs that are connected/used internally,
// and are editable by users.
if (inputSocket->getConnections().size() && graph->isEditable(*inputSocket))
{
uniforms.add(inputSocket->getSelf());
}
}
// Create outputs from the graph interface.
VariableBlock& outputs = stage->getOutputBlock(OSL::OUTPUTS);
for (ShaderGraphOutputSocket* outputSocket : graph->getOutputSockets())
{
outputs.add(outputSocket->getSelf());
}
return shader;
}
void OslShaderGenerator::emitFunctionCalls(const ShaderGraph& graph, GenContext& context, ShaderStage& stage, uint32_t classification) const
{
// Special handling for closures functions.
if ((classification & ShaderNode::Classification::CLOSURE) != 0)
{
// Emit function calls for closures connected to the outputs.
// These will internally emit other closure function calls
// for upstream nodes if needed.
for (ShaderGraphOutputSocket* outputSocket : graph.getOutputSockets())
{
const ShaderNode* upstream = outputSocket->getConnection() ? outputSocket->getConnection()->getNode() : nullptr;
if (upstream && upstream->hasClassification(classification))
{
emitFunctionCall(*upstream, context, stage);
}
}
}
else
{
// Not a closures graph so just generate all
// function calls in order.
ShaderGenerator::emitFunctionCalls(graph, context, stage, classification);
}
}
void OslShaderGenerator::emitFunctionBodyBegin(const ShaderNode& node, GenContext&, ShaderStage& stage, Syntax::Punctuation punc) const
{
emitScopeBegin(stage, punc);
if (node.hasClassification(ShaderNode::Classification::SHADER) || node.hasClassification(ShaderNode::Classification::CLOSURE))
{
emitLine("closure color null_closure = 0", stage);
}
}
void OslShaderGenerator::emitLibraryIncludes(ShaderStage& stage, GenContext& context) const
{
static const string INCLUDE_PREFIX = "#include \"";
static const string INCLUDE_SUFFIX = "\"";
static const StringVec INCLUDE_FILES =
{
"mx_funcs.h"
};
for (const string& file : INCLUDE_FILES)
{
FilePath path = context.resolveSourceFile(file, FilePath());
// Force path to use slash since backslash even if escaped
// gives problems when saving the source code to file.
string pathStr = path.asString();
std::replace(pathStr.begin(), pathStr.end(), '\\', '/');
emitLine(INCLUDE_PREFIX + pathStr + INCLUDE_SUFFIX, stage, false);
}
emitLineBreak(stage);
}
void OslShaderGenerator::emitShaderInputs(const VariableBlock& inputs, ShaderStage& stage) const
{
static const std::unordered_map<string, string> GEOMPROP_DEFINITIONS =
{
{ "Pobject", "transform(\"object\", P)" },
{ "Pworld", "P" },
{ "Nobject", "transform(\"object\", N)" },
{ "Nworld", "N" },
{ "Tobject", "transform(\"object\", dPdu)" },
{ "Tworld", "dPdu" },
{ "Bobject", "transform(\"object\", dPdv)" },
{ "Bworld", "dPdv" },
{ "UV0", "{u,v}" },
{ "Vworld", "I" }
};
for (size_t i = 0; i < inputs.size(); ++i)
{
const ShaderPort* input = inputs[i];
const string& type = _syntax->getTypeName(input->getType());
if (*input->getType() == *Type::FILENAME)
{
// Shader inputs of type 'filename' (textures) need special handling.
// In OSL codegen a 'filename' is translated to the custom type 'textureresource',
// which is a struct containing a file string and a colorspace string.
// For the published shader interface we here split this into two separate inputs,
// which gives a nicer shader interface with widget metadata on each input.
ValuePtr value = input->getValue();
const string valueStr = value ? value->getValueString() : EMPTY_STRING;
// Add the file string input
emitLineBegin(stage);
emitString("string " + input->getVariable() + " = \"" + valueStr + "\"", stage);
emitMetadata(input, stage);
emitString(",", stage);
emitLineEnd(stage, false);
// Add the colorspace string input
emitLineBegin(stage);
emitString("string " + input->getVariable() + "_colorspace = \"" + input->getColorSpace() + "\"", stage);
emitLineEnd(stage, false);
emitScopeBegin(stage, Syntax::DOUBLE_SQUARE_BRACKETS);
emitLine("string widget = \"colorspace\"", stage, false);
emitScopeEnd(stage, false, false);
}
else
{
emitLineBegin(stage);
emitString(type + " " + input->getVariable(), stage);
string value = _syntax->getValue(input, true);
const string& geomprop = input->getGeomProp();
if (!geomprop.empty())
{
auto it = GEOMPROP_DEFINITIONS.find(geomprop);
if (it != GEOMPROP_DEFINITIONS.end())
{
value = it->second;
}
}
if (value.empty())
{
value = _syntax->getDefaultValue(input->getType());
}
emitString(" = " + value, stage);
emitMetadata(input, stage);
}
if (i < inputs.size())
{
emitString(",", stage);
}
emitLineEnd(stage, false);
}
}
void OslShaderGenerator::emitShaderOutputs(const VariableBlock& outputs, ShaderStage& stage) const
{
for (size_t i = 0; i < outputs.size(); ++i)
{
const ShaderPort* output = outputs[i];
const TypeDesc* outputType = output->getType();
const string type = _syntax->getOutputTypeName(outputType);
const string value = _syntax->getDefaultValue(outputType, true);
const string& delim = (i == outputs.size() - 1) ? EMPTY_STRING : Syntax::COMMA;
emitLine(type + " " + output->getVariable() + " = " + value + delim, stage, false);
}
}
void OslShaderGenerator::emitMetadata(const ShaderPort* port, ShaderStage& stage) const
{
static const std::unordered_map<const TypeDesc*, ShaderMetadata> UI_WIDGET_METADATA =
{
{ Type::FLOAT, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("number", Type::STRING->getName())) },
{ Type::INTEGER, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("number", Type::STRING->getName())) },
{ Type::FILENAME, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("filename", Type::STRING->getName())) },
{ Type::BOOLEAN, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("checkBox", Type::STRING->getName())) }
};
static const std::set<const TypeDesc*> METADATA_TYPE_BLACKLIST =
{
Type::VECTOR2, // Custom struct types doesn't support metadata declarations.
Type::VECTOR4, //
Type::COLOR4, //
Type::FILENAME, //
Type::BSDF //
};
auto widgetMetadataIt = UI_WIDGET_METADATA.find(port->getType());
const ShaderMetadata* widgetMetadata = widgetMetadataIt != UI_WIDGET_METADATA.end() ? &widgetMetadataIt->second : nullptr;
const ShaderMetadataVecPtr& metadata = port->getMetadata();
if (widgetMetadata || (metadata && metadata->size()))
{
StringVec metadataLines;
if (metadata)
{
for (size_t j = 0; j < metadata->size(); ++j)
{
const ShaderMetadata& data = metadata->at(j);
if (METADATA_TYPE_BLACKLIST.count(data.type) == 0)
{
const string& delim = (widgetMetadata || j < metadata->size() - 1) ? Syntax::COMMA : EMPTY_STRING;
const string& dataType = _syntax->getTypeName(data.type);
const string dataValue = _syntax->getValue(data.type, *data.value, true);
metadataLines.push_back(dataType + " " + data.name + " = " + dataValue + delim);
}
}
}
if (widgetMetadata)
{
const string& dataType = _syntax->getTypeName(widgetMetadata->type);
const string dataValue = _syntax->getValue(widgetMetadata->type, *widgetMetadata->value, true);
metadataLines.push_back(dataType + " " + widgetMetadata->name + " = " + dataValue);
}
if (metadataLines.size())
{
emitLineEnd(stage, false);
emitScopeBegin(stage, Syntax::DOUBLE_SQUARE_BRACKETS);
for (const auto& line : metadataLines)
{
emitLine(line, stage, false);
}
emitScopeEnd(stage, false, false);
}
}
}
namespace OSL
{
// Identifiers for OSL variable blocks
const string UNIFORMS = "u";
const string INPUTS = "i";
const string OUTPUTS = "o";
} // namespace OSL
MATERIALX_NAMESPACE_END