// // Copyright Contributors to the MaterialX Project // SPDX-License-Identifier: Apache-2.0 // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MATERIALX_NAMESPACE_BEGIN namespace { const vector DEFAULT_IMPORTS = { "import ::df::*", "import ::base::*", "import ::math::*", "import ::state::*", "import ::anno::*", "import ::tex::*", "using ::materialx::core import *", "using ::materialx::sampling import *", }; const vector DEFAULT_VERSIONED_IMPORTS = { "using ::materialx::stdlib_", "using ::materialx::pbrlib_", }; const string IMPORT_ALL = " import *"; const string MDL_VERSION_1_6 = "1.6"; const string MDL_VERSION_1_7 = "1.7"; const string MDL_VERSION_1_8 = "1.8"; const string MDL_VERSION_1_9 = "1.9"; const string MDL_VERSION_1_10 = "1.10"; const string MDL_VERSION_SUFFIX_1_6 = "1_6"; const string MDL_VERSION_SUFFIX_1_7 = "1_7"; const string MDL_VERSION_SUFFIX_1_8 = "1_8"; const string MDL_VERSION_SUFFIX_1_9 = "1_9"; const string MDL_VERSION_SUFFIX_1_10 = "1_10"; } // anonymous namespace const string MdlShaderGenerator::TARGET = "genmdl"; const string GenMdlOptions::GEN_CONTEXT_USER_DATA_KEY = "genmdloptions"; const std::unordered_map MdlShaderGenerator::GEOMPROP_DEFINITIONS = { { "Pobject", "state::transform_point(state::coordinate_internal, state::coordinate_object, state::position())" }, { "Pworld", "state::transform_point(state::coordinate_internal, state::coordinate_world, state::position())" }, { "Nobject", "state::transform_normal(state::coordinate_internal, state::coordinate_object, state::normal())" }, { "Nworld", "state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal())" }, { "Tobject", "state::transform_vector(state::coordinate_internal, state::coordinate_object, state::texture_tangent_u(0))" }, { "Tworld", "state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))" }, { "Bobject", "state::transform_vector(state::coordinate_internal, state::coordinate_object, state::texture_tangent_v(0))" }, { "Bworld", "state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_v(0))" }, { "UV0", "float2(state::texture_coordinate(0).x, state::texture_coordinate(0).y)" }, { "Vworld", "state::direction()" } }; // // MdlShaderGenerator methods // MdlShaderGenerator::MdlShaderGenerator(TypeSystemPtr typeSystem) : ShaderGenerator(typeSystem, MdlSyntax::create(typeSystem)) { // Register custom types to handle enumeration output _typeSystem->registerType(Type::MDL_COORDINATESPACE); _typeSystem->registerType(Type::MDL_ADDRESSMODE); _typeSystem->registerType(Type::MDL_FILTERLOOKUPMODE); _typeSystem->registerType(Type::MDL_FILTERTYPE); _typeSystem->registerType(Type::MDL_DISTRIBUTIONTYPE); _typeSystem->registerType(Type::MDL_SCATTER_MODE); // Register build-in implementations // registerImplementation("IM_surfacematerial_" + MdlShaderGenerator::TARGET, MaterialNodeMdl::create); // registerImplementation("IM_surface_" + MdlShaderGenerator::TARGET, SurfaceNodeMdl::create); // registerImplementation("IM_blur_float_" + MdlShaderGenerator::TARGET, BlurNodeMdl::create); registerImplementation("IM_blur_color3_" + MdlShaderGenerator::TARGET, BlurNodeMdl::create); registerImplementation("IM_blur_color4_" + MdlShaderGenerator::TARGET, BlurNodeMdl::create); registerImplementation("IM_blur_vector2_" + MdlShaderGenerator::TARGET, BlurNodeMdl::create); registerImplementation("IM_blur_vector3_" + MdlShaderGenerator::TARGET, BlurNodeMdl::create); registerImplementation("IM_blur_vector4_" + MdlShaderGenerator::TARGET, BlurNodeMdl::create); // registerImplementation("IM_heighttonormal_vector3_" + MdlShaderGenerator::TARGET, HeightToNormalNodeMdl::create); // registerImplementation("IM_layer_bsdf_" + MdlShaderGenerator::TARGET, ClosureLayerNodeMdl::create); registerImplementation("IM_layer_vdf_" + MdlShaderGenerator::TARGET, ClosureLayerNodeMdl::create); // registerImplementation("IM_dielectric_bsdf_" + MdlShaderGenerator::TARGET, LayerableNodeMdl::create); // registerImplementation("IM_generalized_schlick_bsdf_" + MdlShaderGenerator::TARGET, LayerableNodeMdl::create); // registerImplementation("IM_sheen_bsdf_" + MdlShaderGenerator::TARGET, LayerableNodeMdl::create); // registerImplementation("IM_image_float_" + MdlShaderGenerator::TARGET, ImageNodeMdl::create); registerImplementation("IM_image_color3_" + MdlShaderGenerator::TARGET, ImageNodeMdl::create); registerImplementation("IM_image_color4_" + MdlShaderGenerator::TARGET, ImageNodeMdl::create); registerImplementation("IM_image_vector2_" + MdlShaderGenerator::TARGET, ImageNodeMdl::create); registerImplementation("IM_image_vector3_" + MdlShaderGenerator::TARGET, ImageNodeMdl::create); registerImplementation("IM_image_vector4_" + MdlShaderGenerator::TARGET, ImageNodeMdl::create); } ShaderPtr MdlShaderGenerator::generate(const string& name, ElementPtr element, GenContext& context) const { // For MDL we cannot cache node implementations between generation calls, // because this generator needs to do edits to subgraphs implementations // depending on the context in which a node is used. context.clearNodeImplementations(); 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); // Emit version emitMdlVersionNumber(context, stage); emitLineBreak(stage); // Emit module imports for (const string& module : DEFAULT_IMPORTS) { emitLine(module, stage); } for (const string& module : DEFAULT_VERSIONED_IMPORTS) { emitString(module, stage); emitMdlVersionFilenameSuffix(context, stage); emitString(IMPORT_ALL, stage); emitLineEnd(stage, true); } // Emit custom node imports for nodes in the graph for (ShaderNode* node : graph.getNodes()) { const ShaderNodeImpl& impl = node->getImplementation(); const CustomCodeNodeMdl* customNode = dynamic_cast(&impl); if (customNode) { const string& importName = customNode->getQualifiedModuleName(); if (!importName.empty()) { emitString("import ", stage); emitString(importName, stage); emitString("::*", stage); emitLineEnd(stage, true); } } } // Add global constants and type definitions emitTypeDefinitions(context, stage); // 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* outputSocket = graph.getOutputSocket(0); emitString("export material ", 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); emitScopeBegin(stage, Syntax::PARENTHESES); // Emit shader inputs emitShaderInputs(element->getDocument(), stage.getInputBlock(MDL::INPUTS), stage); // End shader signature emitScopeEnd(stage); // Begin shader body emitLine("= let", stage, false); emitScopeBegin(stage); // Emit constants const VariableBlock& constants = stage.getConstantBlock(); if (constants.size()) { emitVariableDeclarations(constants, _syntax->getConstantQualifier(), Syntax::SEMICOLON, context, stage); emitLineBreak(stage); } // Emit shader inputs that have been filtered during printing of the public interface const string uniformPrefix = _syntax->getUniformQualifier() + " "; for (ShaderGraphInputSocket* inputSocket : graph.getInputSockets()) { if (inputSocket->getConnections().size() && (inputSocket->getType().getSemantic() == TypeDesc::SEMANTIC_SHADER || inputSocket->getType().getSemantic() == TypeDesc::SEMANTIC_CLOSURE || inputSocket->getType().getSemantic() == TypeDesc::SEMANTIC_MATERIAL)) { const string& qualifier = inputSocket->isUniform() || inputSocket->getType() == Type::FILENAME ? uniformPrefix : EMPTY_STRING; const string& type = _syntax->getTypeName(inputSocket->getType()); emitLineBegin(stage); emitString(qualifier + type + " " + inputSocket->getVariable() + " = ", stage); emitString(_syntax->getDefaultValue(inputSocket->getType(), true), stage); emitLineEnd(stage, true); } } // 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); } } } // Get final result const string result = getUpstreamResult(outputSocket, context); const TypeDesc outputType = outputSocket->getType(); // Try to return some meaningful color in case the output is not a material if (graph.hasClassification(ShaderNode::Classification::TEXTURE)) { if (outputType == Type::DISPLACEMENTSHADER) { emitLine("float3 displacement__ = " + result + ".geometry.displacement", stage); emitLine("color finalOutput__ = mk_color3(" "r: math::dot(displacement__, state::texture_tangent_u(0))," "g: math::dot(displacement__, state::texture_tangent_v(0))," "b: math::dot(displacement__, state::normal()))", stage); } else { emitLine("float3 displacement__ = float3(0.0)", stage); std::string finalOutput = "mk_color3(0.0)"; if (outputType == Type::BOOLEAN) finalOutput = result + " ? mk_color3(0.0, 1.0, 0.0) : mk_color3(1.0, 0.0, 0.0)"; else if (outputType == Type::INTEGER) finalOutput = "mk_color3(" + result + " / 100)"; // arbitrary else if (outputType == Type::FLOAT) finalOutput = "mk_color3(" + result + ")"; else if (outputType == Type::VECTOR2) finalOutput = "mk_color3(" + result + ".x, " + result + ".y, 0.0)"; else if (outputType == Type::VECTOR3) finalOutput = "mk_color3(" + result + ")"; else if (outputType == Type::VECTOR4) finalOutput = "mk_color3(" + result + ".x, " + result + ".y, " + result + ".z)"; else if (outputType == Type::COLOR3) finalOutput = result; else if (outputType == Type::COLOR4) finalOutput = result + ".rgb"; else if (outputType == Type::MATRIX33 || outputType == Type::MATRIX44) finalOutput = "mk_color3(" + result + "[0][0], " + result + "[1][1], " + result + "[2][2])"; emitLine("color finalOutput__ = " + finalOutput, stage); } // End shader body emitScopeEnd(stage); static const string textureMaterial = "in material\n" "(\n" " surface: material_surface(\n" " emission : material_emission(\n" " emission : df::diffuse_edf(),\n" " intensity : finalOutput__ * math::PI,\n" " mode : intensity_radiant_exitance\n" " )\n" " ),\n" " geometry: material_geometry(\n" " displacement : displacement__\n" " )\n" ");"; emitBlock(textureMaterial, FilePath(), context, stage); } else { emitLine(_syntax->getTypeSyntax(outputType).getName() + " finalOutput__ = " + result, stage); // End shader body emitScopeEnd(stage); static const string shaderMaterial = "in material(finalOutput__);"; emitBlock(shaderMaterial, FilePath(), context, stage); } // Perform token substitution replaceTokens(_tokenSubstitutions, stage); return shader; } ShaderNodeImplPtr MdlShaderGenerator::getImplementation(const NodeDef& nodedef, GenContext& context) const { InterfaceElementPtr implElement = nodedef.getImplementation(getTarget()); if (!implElement) { return nullptr; } const string& name = implElement->getName(); // Check if it's created and cached already. ShaderNodeImplPtr impl = context.findNodeImplementation(name); if (impl) { return impl; } vector outputs = nodedef.getActiveOutputs(); if (outputs.empty()) { throw ExceptionShaderGenError("NodeDef '" + nodedef.getName() + "' has no outputs defined"); } const TypeDesc outputType = _typeSystem->getType(outputs[0]->getType()); if (implElement->isA()) { // Use a compound implementation. if (outputType.isClosure()) { impl = ClosureCompoundNodeMdl::create(); } else { impl = CompoundNodeMdl::create(); } } else if (implElement->isA()) { // Try creating a new in the factory. impl = _implFactory.create(name); if (!impl) { // When `file` and `function` are provided we consider this node a user node const string file = implElement->getTypedAttribute("file"); const string function = implElement->getTypedAttribute("function"); // Or, if `sourcecode` is provided we consider this node a user node with inline implementation // inline implementations are not supposed to have replacement markers const string sourcecode = implElement->getTypedAttribute("sourcecode"); if ((!file.empty() && !function.empty()) || (!sourcecode.empty() && sourcecode.find("{{") == string::npos)) { impl = CustomCodeNodeMdl::create(); } else if (file.empty() && sourcecode.empty()) { throw ExceptionShaderGenError("No valid MDL implementation found for '" + name + "'"); } else { // Fall back to source code implementation. if (outputType.isClosure()) { impl = ClosureSourceCodeNodeMdl::create(); } else { impl = SourceCodeNodeMdl::create(); } } } } if (!impl) { return nullptr; } impl->initialize(*implElement, context); // Cache it. context.addNodeImplementation(name, impl); return impl; } string MdlShaderGenerator::getUpstreamResult(const ShaderInput* input, GenContext& context) const { const ShaderOutput* upstreamOutput = input->getConnection(); if (!upstreamOutput || upstreamOutput->getNode()->isAGraph()) { return ShaderGenerator::getUpstreamResult(input, context); } const MdlSyntax& mdlSyntax = static_cast(getSyntax()); string variable; const ShaderNode* upstreamNode = upstreamOutput->getNode(); if (!upstreamNode->isAGraph() && upstreamNode->numOutputs() > 1) { const CompoundNodeMdl* upstreamNodeMdl = dynamic_cast(&upstreamNode->getImplementation()); if (upstreamNodeMdl && upstreamNodeMdl->unrollReturnStructMembers()) { variable = upstreamNode->getName() + "__" + upstreamOutput->getName(); } else { const string& fieldName = upstreamOutput->getName(); const CustomCodeNodeMdl* upstreamCustomNodeMdl = dynamic_cast(&upstreamNode->getImplementation()); if (upstreamCustomNodeMdl) { // Prefix the port name depending on the CustomCodeNode variable = upstreamNode->getName() + "_result." + upstreamCustomNodeMdl->modifyPortName(fieldName, mdlSyntax); } else { // Existing implementations and none user defined structs will keep the prefix always to not break existing content variable = upstreamNode->getName() + "_result." + mdlSyntax.modifyPortName(upstreamOutput->getName()); } } } else { variable = upstreamOutput->getVariable(); } // Look for any additional suffix to append string suffix; context.getInputSuffix(input, suffix); if (!suffix.empty()) { variable += suffix; } return variable; } namespace { // [TODO] // Here we assume this bit of the port flags is unused. // Change this to a more general and safe solution. class ShaderPortFlagMdl { public: static const uint32_t TRANSMISSION_IOR_DEPENDENCY = 1u << 31; }; // Check if a graph has inputs with dependencies on transmission IOR on the inside. // Track all subgraphs found that has such a dependency, as well as subgraphs that are // found to have a varying connection to transmission IOR. // Returns true if uniform ior dependencies are found. bool checkTransmissionIorDependencies(ShaderGraph* g, std::set& graphsWithIorDependency, std::set& graphsWithIorVarying) { bool result = false; for (ShaderNode* node : g->getNodes()) { ShaderGraph* subgraph = node->getImplementation().getGraph(); if (subgraph) { // Check recursively if this subgraph has IOR dependencies. if (checkTransmissionIorDependencies(subgraph, graphsWithIorDependency, graphsWithIorVarying)) { for (ShaderOutput* socket : subgraph->getInputSockets()) { if (socket->getFlag(ShaderPortFlagMdl::TRANSMISSION_IOR_DEPENDENCY)) { ShaderInput* input = node->getInput(socket->getName()); ShaderOutput* source = input ? input->getConnection() : nullptr; if (source) { // Check if this is a graph interface connection. if (source->getNode() == g) { graphsWithIorDependency.insert(g); source->setFlag(ShaderPortFlagMdl::TRANSMISSION_IOR_DEPENDENCY, true); result = true; } else if (source->getNode()->hasClassification(ShaderNode::Classification::CONSTANT)) { // If the connection is to a constant node we can // handled that here since it's just a uniform value. ShaderInput* value = source->getNode()->getInput(ValueElement::VALUE_ATTRIBUTE); if (value && value->getValue()) { input->setValue(value->getValue()); } input->breakConnection(); } else { // If we get here we have to assume this is a varying connection. // Save the graph as a varying graph so we later can break its // internal connections to transmission IOR. graphsWithIorVarying.insert(subgraph); return false; // no need to continue with this subgraph } } } } } } else { // Check for transmission BSDF node. if (node->hasClassification(ShaderNode::Classification::BSDF_T)) { // Check if IOR is connected. ShaderInput* ior = node->getInput("ior"); ShaderOutput* source = ior ? ior->getConnection() : nullptr; if (source) { // Check if this is a graph interface connection. if (source->getNode() == g) { graphsWithIorDependency.insert(g); source->setFlag(ShaderPortFlagMdl::TRANSMISSION_IOR_DEPENDENCY, true); result = true; } else if (source->getNode()->hasClassification(ShaderNode::Classification::CONSTANT)) { // If the connection is to a constant node we can // handled that here since it's just a uniform value. ShaderInput* value = source->getNode()->getInput(ValueElement::VALUE_ATTRIBUTE); if (value && value->getValue()) { ior->setValue(value->getValue()); } ior->breakConnection(); } else { // If we get here we have to assume this is a varying connection // and we can break it immediately here. ior->breakConnection(); } } } } } return result; } // Disconnect any incoming connections to transmission IOR // inside a graph. void disconnectTransmissionIor(ShaderGraph* g) { for (ShaderNode* node : g->getNodes()) { ShaderGraph* subgraph = node->getImplementation().getGraph(); if (subgraph && (subgraph->hasClassification(ShaderNode::Classification::SHADER) || subgraph->hasClassification(ShaderNode::Classification::CLOSURE))) { disconnectTransmissionIor(subgraph); } else if (node->hasClassification(ShaderNode::Classification::BSDF_T)) { ShaderInput* ior = node->getInput("ior"); if (ior) { ior->breakConnection(); } } } } } // anonymous namespace ShaderPtr MdlShaderGenerator::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(name, graph); // Create our stage. ShaderStagePtr stage = createStage(Stage::PIXEL, *shader); VariableBlockPtr inputs = stage->createInputBlock(MDL::INPUTS); VariableBlockPtr outputs = stage->createOutputBlock(MDL::OUTPUTS); // Create shader variables for all nodes that need this. createVariables(graph, context, *shader); // Create inputs for the published graph interface. 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)) { if (inputSocket->getType().getSemantic() == TypeDesc::SEMANTIC_SHADER || inputSocket->getType().getSemantic() == TypeDesc::SEMANTIC_CLOSURE || inputSocket->getType().getSemantic() == TypeDesc::SEMANTIC_MATERIAL) { continue; } inputs->add(inputSocket->getSelf()); } } // Create outputs from the graph interface. for (ShaderGraphOutputSocket* outputSocket : graph->getOutputSockets()) { outputs->add(outputSocket->getSelf()); } // MDL does not allow varying data connected to transmission IOR until MDL 1.9. // We must find all uses of transmission IOR and make sure we don't // have a varying connection to it. If a varying connection is found // we break that connection and revert to using default value on that // instance of IOR, so that other uses of the same varying input still // works in other places. // As a result if a varying connections is set on transmission IOR // it just reverts to default value. Varying data on transmission IOR // is very rare so this is normally not a problem in practice. // One use-case where this fix is important is for shading models with // a single IOR input, that gets connected to both reflection and // transmission IOR inside the shading model graph. For such cases // this fix will disconnect the transmission IOR on the inside, but // still support the connection to reflection IOR. // GenMdlOptions::MdlVersion version = getMdlVersion(context); bool uniformIorRequired = version == GenMdlOptions::MdlVersion::MDL_1_6 || version == GenMdlOptions::MdlVersion::MDL_1_7 || version == GenMdlOptions::MdlVersion::MDL_1_8; if (uniformIorRequired && ( graph->hasClassification(ShaderNode::Classification::SHADER) || graph->hasClassification(ShaderNode::Classification::CLOSURE))) { // Find dependencies on transmission IOR. std::set graphsWithIorDependency; std::set graphsWithIorVarying; checkTransmissionIorDependencies(graph.get(), graphsWithIorDependency, graphsWithIorVarying); // For any graphs found that has a varying connection // to transmission IOR we need to break that connection. for (ShaderGraph* g : graphsWithIorVarying) { disconnectTransmissionIor(g); graphsWithIorDependency.erase(g); } // For graphs that has a dependency with transmission IOR on the inside, // we can declare the corresponding inputs as being uniform and preserve // the internal connection to transmission IOR. for (ShaderGraph* g : graphsWithIorDependency) { for (ShaderOutput* socket : g->getInputSockets()) { if (socket->getFlag(ShaderPortFlagMdl::TRANSMISSION_IOR_DEPENDENCY)) { socket->setUniform(); } } } } return shader; } namespace { void emitInputAnnotations(const MdlShaderGenerator& _this, ConstDocumentPtr doc, const ShaderPort* variable, ShaderStage& stage) { // allows to relate between MaterialX and MDL parameters when looking at the MDL code. const std::string mtlxParameterPathAnno = "materialx::core::origin(\"" + variable->getPath() + "\")"; _this.emitLineEnd(stage, false); _this.emitLine("[[", stage, false); _this.emitLine("\t" + mtlxParameterPathAnno, stage, false); _this.emitLineBegin(stage); _this.emitString("]]", stage); // line ending follows by caller } } // anonymous namespace void MdlShaderGenerator::emitShaderInputs(ConstDocumentPtr doc, const VariableBlock& inputs, ShaderStage& stage) const { const string uniformPrefix = _syntax->getUniformQualifier() + " "; for (size_t i = 0; i < inputs.size(); ++i) { const ShaderPort* input = inputs[i]; const string& qualifier = input->isUniform() || input->getType() == Type::FILENAME ? uniformPrefix : EMPTY_STRING; const string& type = _syntax->getTypeName(input->getType()); string value = input->getValue() ? _syntax->getValue(input->getType(), *input->getValue(), true) : EMPTY_STRING; 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(), true); } emitLineBegin(stage); emitString(qualifier + type + " " + input->getVariable() + " = " + value, stage); emitInputAnnotations(*this, doc, input, stage); if (i < inputs.size() - 1) { emitString(",", stage); } emitLineEnd(stage, false); } } GenMdlOptions::MdlVersion MdlShaderGenerator::getMdlVersion(GenContext& context) const { GenMdlOptionsPtr options = context.getUserData(GenMdlOptions::GEN_CONTEXT_USER_DATA_KEY); return options ? options->targetVersion : GenMdlOptions::MdlVersion::MDL_LATEST; } void MdlShaderGenerator::emitMdlVersionNumber(GenContext& context, ShaderStage& stage) const { GenMdlOptions::MdlVersion version = getMdlVersion(context); emitLineBegin(stage); emitString("mdl ", stage); switch (version) { case GenMdlOptions::MdlVersion::MDL_1_6: emitString(MDL_VERSION_1_6, stage); break; case GenMdlOptions::MdlVersion::MDL_1_7: emitString(MDL_VERSION_1_7, stage); break; case GenMdlOptions::MdlVersion::MDL_1_8: emitString(MDL_VERSION_1_8, stage); break; case GenMdlOptions::MdlVersion::MDL_1_9: emitString(MDL_VERSION_1_9, stage); break; default: // GenMdlOptions::MdlVersion::MDL_1_10 // GenMdlOptions::MdlVersion::MDL_LATEST emitString(MDL_VERSION_1_10, stage); break; } emitLineEnd(stage, true); } const string& MdlShaderGenerator::getMdlVersionFilenameSuffix(GenContext& context) const { GenMdlOptions::MdlVersion version = getMdlVersion(context); switch (version) { case GenMdlOptions::MdlVersion::MDL_1_6: return MDL_VERSION_SUFFIX_1_6; case GenMdlOptions::MdlVersion::MDL_1_7: return MDL_VERSION_SUFFIX_1_7; case GenMdlOptions::MdlVersion::MDL_1_8: return MDL_VERSION_SUFFIX_1_8; case GenMdlOptions::MdlVersion::MDL_1_9: return MDL_VERSION_SUFFIX_1_9; default: // GenMdlOptions::MdlVersion::MDL_1_10 // GenMdlOptions::MdlVersion::MDL_LATEST return MDL_VERSION_SUFFIX_1_10; } } void MdlShaderGenerator::emitMdlVersionFilenameSuffix(GenContext& context, ShaderStage& stage) const { emitString(getMdlVersionFilenameSuffix(context), stage); } namespace MDL { // Identifiers for MDL variable blocks const string INPUTS = "i"; const string OUTPUTS = "o"; } // namespace MDL MATERIALX_NAMESPACE_END