// // 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 #include #include #include #include #include #include #include #include MATERIALX_NAMESPACE_BEGIN const string GlslShaderGenerator::TARGET = "genglsl"; const string GlslShaderGenerator::VERSION = "400"; const string GlslSamplingIncludeFilename = "stdlib/genglsl/lib/mx_sampling.glsl"; // // GlslShaderGenerator methods // GlslShaderGenerator::GlslShaderGenerator(TypeSystemPtr typeSystem) : HwShaderGenerator(typeSystem, GlslSyntax::create(typeSystem)) { // // Register all custom node implementation classes // StringVec elementNames; // registerImplementation("IM_position_vector3_" + GlslShaderGenerator::TARGET, HwPositionNode::create); // registerImplementation("IM_normal_vector3_" + GlslShaderGenerator::TARGET, HwNormalNode::create); // registerImplementation("IM_tangent_vector3_" + GlslShaderGenerator::TARGET, HwTangentNode::create); // registerImplementation("IM_bitangent_vector3_" + GlslShaderGenerator::TARGET, HwBitangentNode::create); // registerImplementation("IM_texcoord_vector2_" + GlslShaderGenerator::TARGET, HwTexCoordNode::create); registerImplementation("IM_texcoord_vector3_" + GlslShaderGenerator::TARGET, HwTexCoordNode::create); // registerImplementation("IM_geomcolor_float_" + GlslShaderGenerator::TARGET, HwGeomColorNode::create); registerImplementation("IM_geomcolor_color3_" + GlslShaderGenerator::TARGET, HwGeomColorNode::create); registerImplementation("IM_geomcolor_color4_" + GlslShaderGenerator::TARGET, HwGeomColorNode::create); // elementNames = { "IM_geompropvalue_integer_" + GlslShaderGenerator::TARGET, "IM_geompropvalue_float_" + GlslShaderGenerator::TARGET, "IM_geompropvalue_color3_" + GlslShaderGenerator::TARGET, "IM_geompropvalue_color4_" + GlslShaderGenerator::TARGET, "IM_geompropvalue_vector2_" + GlslShaderGenerator::TARGET, "IM_geompropvalue_vector3_" + GlslShaderGenerator::TARGET, "IM_geompropvalue_vector4_" + GlslShaderGenerator::TARGET, }; registerImplementation(elementNames, HwGeomPropValueNode::create); registerImplementation("IM_geompropvalue_boolean_" + GlslShaderGenerator::TARGET, HwGeomPropValueNodeAsUniform::create); registerImplementation("IM_geompropvalue_string_" + GlslShaderGenerator::TARGET, HwGeomPropValueNodeAsUniform::create); registerImplementation("IM_geompropvalue_filename_" + GlslShaderGenerator::TARGET, HwGeomPropValueNodeAsUniform::create); // registerImplementation("IM_frame_float_" + GlslShaderGenerator::TARGET, HwFrameNode::create); // registerImplementation("IM_viewdirection_vector3_" + GlslShaderGenerator::TARGET, HwViewDirectionNode::create); // registerImplementation("IM_surface_" + GlslShaderGenerator::TARGET, SurfaceNodeGlsl::create); registerImplementation("IM_surface_unlit_" + GlslShaderGenerator::TARGET, UnlitSurfaceNodeGlsl::create); // registerImplementation("IM_light_" + GlslShaderGenerator::TARGET, LightNodeGlsl::create); // registerImplementation("IM_point_light_" + GlslShaderGenerator::TARGET, LightShaderNodeGlsl::create); // registerImplementation("IM_directional_light_" + GlslShaderGenerator::TARGET, LightShaderNodeGlsl::create); // registerImplementation("IM_spot_light_" + GlslShaderGenerator::TARGET, LightShaderNodeGlsl::create); // registerImplementation("IM_heighttonormal_vector3_" + GlslShaderGenerator::TARGET, []() -> ShaderNodeImplPtr { return HwHeightToNormalNode::create(GlslSamplingIncludeFilename);}); // elementNames = { "IM_blur_float_" + GlslShaderGenerator::TARGET, "IM_blur_color3_" + GlslShaderGenerator::TARGET, "IM_blur_color4_" + GlslShaderGenerator::TARGET, "IM_blur_vector2_" + GlslShaderGenerator::TARGET, "IM_blur_vector3_" + GlslShaderGenerator::TARGET, "IM_blur_vector4_" + GlslShaderGenerator::TARGET, }; registerImplementation(elementNames, []() -> ShaderNodeImplPtr { return HwBlurNode::create(GlslSamplingIncludeFilename);}); // elementNames = { "IM_image_float_" + GlslShaderGenerator::TARGET, "IM_image_color3_" + GlslShaderGenerator::TARGET, "IM_image_color4_" + GlslShaderGenerator::TARGET, "IM_image_vector2_" + GlslShaderGenerator::TARGET, "IM_image_vector3_" + GlslShaderGenerator::TARGET, "IM_image_vector4_" + GlslShaderGenerator::TARGET, }; registerImplementation(elementNames, HwImageNode::create); // registerImplementation("IM_surfacematerial_" + GlslShaderGenerator::TARGET, MaterialNode::create); _lightSamplingNodes.push_back(ShaderNode::create(nullptr, "numActiveLightSources", NumLightsNodeGlsl::create())); _lightSamplingNodes.push_back(ShaderNode::create(nullptr, "sampleLightSource", LightSamplerNodeGlsl::create())); } ShaderPtr GlslShaderGenerator::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); // Make sure we initialize/reset the binding context before generation. HwResourceBindingContextPtr resourceBindingCtx = getResourceBindingContext(context); if (resourceBindingCtx) { resourceBindingCtx->initialize(); } // Emit code for vertex shader stage ShaderStage& vs = shader->getStage(Stage::VERTEX); emitVertexStage(shader->getGraph(), context, vs); replaceTokens(_tokenSubstitutions, vs); // Emit code for pixel shader stage ShaderStage& ps = shader->getStage(Stage::PIXEL); emitPixelStage(shader->getGraph(), context, ps); replaceTokens(_tokenSubstitutions, ps); return shader; } void GlslShaderGenerator::emitVertexStage(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const { HwResourceBindingContextPtr resourceBindingCtx = getResourceBindingContext(context); emitDirectives(context, stage); if (resourceBindingCtx) { resourceBindingCtx->emitDirectives(context, stage); } emitLineBreak(stage); // Add all constants emitConstants(context, stage); // Add all uniforms emitUniforms(context, stage); // Add vertex inputs emitInputs(context, stage); // Add vertex data outputs block emitOutputs(context, stage); emitFunctionDefinitions(graph, context, stage); // Add main function setFunctionName("main", stage); emitLine("void main()", stage, false); emitFunctionBodyBegin(graph, context, stage); emitLine("vec4 hPositionWorld = " + HW::T_WORLD_MATRIX + " * vec4(" + HW::T_IN_POSITION + ", 1.0)", stage); emitLine("gl_Position = " + HW::T_VIEW_PROJECTION_MATRIX + " * hPositionWorld", stage); // Emit all function calls in order for (const ShaderNode* node : graph.getNodes()) { emitFunctionCall(*node, context, stage); } emitFunctionBodyEnd(graph, context, stage); } void GlslShaderGenerator::emitSpecularEnvironment(GenContext& context, ShaderStage& stage) const { int specularMethod = context.getOptions().hwSpecularEnvironmentMethod; if (specularMethod == SPECULAR_ENVIRONMENT_FIS) { emitLibraryInclude("pbrlib/genglsl/lib/mx_environment_fis.glsl", context, stage); } else if (specularMethod == SPECULAR_ENVIRONMENT_PREFILTER) { emitLibraryInclude("pbrlib/genglsl/lib/mx_environment_prefilter.glsl", context, stage); } else if (specularMethod == SPECULAR_ENVIRONMENT_NONE) { emitLibraryInclude("pbrlib/genglsl/lib/mx_environment_none.glsl", context, stage); } else { throw ExceptionShaderGenError("Invalid hardware specular environment method specified: '" + std::to_string(specularMethod) + "'"); } emitLineBreak(stage); } void GlslShaderGenerator::emitTransmissionRender(GenContext& context, ShaderStage& stage) const { int transmissionMethod = context.getOptions().hwTransmissionRenderMethod; if (transmissionMethod == TRANSMISSION_REFRACTION) { emitLibraryInclude("pbrlib/genglsl/lib/mx_transmission_refract.glsl", context, stage); } else if (transmissionMethod == TRANSMISSION_OPACITY) { emitLibraryInclude("pbrlib/genglsl/lib/mx_transmission_opacity.glsl", context, stage); } else { throw ExceptionShaderGenError("Invalid transmission render specified: '" + std::to_string(transmissionMethod) + "'"); } emitLineBreak(stage); } void GlslShaderGenerator::emitDirectives(GenContext&, ShaderStage& stage) const { emitLine("#version " + getVersion(), stage, false); emitLineBreak(stage); } void GlslShaderGenerator::emitConstants(GenContext& context, ShaderStage& stage) const { const VariableBlock& constants = stage.getConstantBlock(); if (!constants.empty()) { emitVariableDeclarations(constants, _syntax->getConstantQualifier(), Syntax::SEMICOLON, context, stage); emitLineBreak(stage); } } void GlslShaderGenerator::emitUniforms(GenContext& context, ShaderStage& stage) const { for (const auto& it : stage.getUniformBlocks()) { const VariableBlock& uniforms = *it.second; // Skip light uniforms as they are handled separately if (!uniforms.empty() && uniforms.getName() != HW::LIGHT_DATA) { emitComment("Uniform block: " + uniforms.getName(), stage); HwResourceBindingContextPtr resourceBindingCtx = getResourceBindingContext(context); if (resourceBindingCtx) { resourceBindingCtx->emitResourceBindings(context, uniforms, stage); } else { emitVariableDeclarations(uniforms, _syntax->getUniformQualifier(), Syntax::SEMICOLON, context, stage); emitLineBreak(stage); } } } } void GlslShaderGenerator::emitLightData(GenContext& context, ShaderStage& stage) const { const VariableBlock& lightData = stage.getUniformBlock(HW::LIGHT_DATA); const string structArraySuffix = "[" + HW::LIGHT_DATA_MAX_LIGHT_SOURCES + "]"; const string structName = lightData.getInstance(); HwResourceBindingContextPtr resourceBindingCtx = getResourceBindingContext(context); if (resourceBindingCtx) { resourceBindingCtx->emitStructuredResourceBindings( context, lightData, stage, structName, structArraySuffix); } else { emitLine("struct " + lightData.getName(), stage, false); emitScopeBegin(stage); emitVariableDeclarations(lightData, EMPTY_STRING, Syntax::SEMICOLON, context, stage, false); emitScopeEnd(stage, true); emitLineBreak(stage); emitLine("uniform " + lightData.getName() + " " + structName + structArraySuffix, stage); } emitLineBreak(stage); } void GlslShaderGenerator::emitInputs(GenContext& context, ShaderStage& stage) const { DEFINE_SHADER_STAGE(stage, Stage::VERTEX) { const VariableBlock& vertexInputs = stage.getInputBlock(HW::VERTEX_INPUTS); if (!vertexInputs.empty()) { emitComment("Inputs block: " + vertexInputs.getName(), stage); emitVariableDeclarations(vertexInputs, _syntax->getInputQualifier(), Syntax::SEMICOLON, context, stage, false); emitLineBreak(stage); } } DEFINE_SHADER_STAGE(stage, Stage::PIXEL) { const VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); if (!vertexData.empty()) { emitLine("in " + vertexData.getName(), stage, false); emitScopeBegin(stage); emitVariableDeclarations(vertexData, EMPTY_STRING, Syntax::SEMICOLON, context, stage, false); emitScopeEnd(stage, false, false); emitString(" " + vertexData.getInstance() + Syntax::SEMICOLON, stage); emitLineBreak(stage); emitLineBreak(stage); } } } void GlslShaderGenerator::emitOutputs(GenContext& context, ShaderStage& stage) const { DEFINE_SHADER_STAGE(stage, Stage::VERTEX) { const VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); if (!vertexData.empty()) { emitLine("out " + vertexData.getName(), stage, false); emitScopeBegin(stage); emitVariableDeclarations(vertexData, EMPTY_STRING, Syntax::SEMICOLON, context, stage, false); emitScopeEnd(stage, false, false); emitString(" " + vertexData.getInstance() + Syntax::SEMICOLON, stage); emitLineBreak(stage); emitLineBreak(stage); } } DEFINE_SHADER_STAGE(stage, Stage::PIXEL) { emitComment("Pixel shader outputs", stage); const VariableBlock& outputs = stage.getOutputBlock(HW::PIXEL_OUTPUTS); emitVariableDeclarations(outputs, _syntax->getOutputQualifier(), Syntax::SEMICOLON, context, stage, false); emitLineBreak(stage); } } HwResourceBindingContextPtr GlslShaderGenerator::getResourceBindingContext(GenContext& context) const { return context.getUserData(HW::USER_DATA_BINDING_CONTEXT); } string GlslShaderGenerator::getVertexDataPrefix(const VariableBlock& vertexData) const { return vertexData.getInstance() + "."; } bool GlslShaderGenerator::requiresLighting(const ShaderGraph& graph) const { const bool isBsdf = graph.hasClassification(ShaderNode::Classification::BSDF); const bool isLitSurfaceShader = graph.hasClassification(ShaderNode::Classification::SHADER) && graph.hasClassification(ShaderNode::Classification::SURFACE) && !graph.hasClassification(ShaderNode::Classification::UNLIT); return isBsdf || isLitSurfaceShader; } void GlslShaderGenerator::emitPixelStage(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const { HwResourceBindingContextPtr resourceBindingCtx = getResourceBindingContext(context); // Add directives emitDirectives(context, stage); if (resourceBindingCtx) { resourceBindingCtx->emitDirectives(context, stage); } emitLineBreak(stage); // Add type definitions emitTypeDefinitions(context, stage); // Add all constants emitConstants(context, stage); // Add all uniforms emitUniforms(context, stage); // Add vertex data inputs block emitInputs(context, stage); // Add the pixel shader output. This needs to be a vec4 for rendering // and upstream connection will be converted to vec4 if needed in emitFinalOutput() emitOutputs(context, stage); // Add common math functions emitLibraryInclude("stdlib/genglsl/lib/mx_math.glsl", context, stage); emitLineBreak(stage); // Determine whether lighting is required bool lighting = requiresLighting(graph); // Define directional albedo approach if (lighting || context.getOptions().hwWriteAlbedoTable || context.getOptions().hwWriteEnvPrefilter) { emitLine("#define DIRECTIONAL_ALBEDO_METHOD " + std::to_string(int(context.getOptions().hwDirectionalAlbedoMethod)), stage, false); emitLineBreak(stage); } // Add lighting support if (lighting) { if (context.getOptions().hwMaxActiveLightSources > 0) { const unsigned int maxLights = std::max(1u, context.getOptions().hwMaxActiveLightSources); emitLine("#define " + HW::LIGHT_DATA_MAX_LIGHT_SOURCES + " " + std::to_string(maxLights), stage, false); } emitSpecularEnvironment(context, stage); emitTransmissionRender(context, stage); if (context.getOptions().hwMaxActiveLightSources > 0) { emitLightData(context, stage); } } // Add shadowing support bool shadowing = (lighting && context.getOptions().hwShadowMap) || context.getOptions().hwWriteDepthMoments; if (shadowing) { emitLibraryInclude("pbrlib/genglsl/lib/mx_shadow.glsl", context, stage); } // Emit directional albedo table code. if (context.getOptions().hwWriteAlbedoTable) { emitLibraryInclude("pbrlib/genglsl/lib/mx_generate_albedo_table.glsl", context, stage); emitLineBreak(stage); } // Emit environment prefiltering code if (context.getOptions().hwWriteEnvPrefilter) { emitLibraryInclude("pbrlib/genglsl/lib/mx_generate_prefilter_env.glsl", context, stage); 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.glsl"; } else { _tokenSubstitutions[ShaderGenerator::T_FILE_TRANSFORM_UV] = "mx_transform_uv.glsl"; } // Emit uv transform code globally if needed. if (context.getOptions().hwAmbientOcclusion) { emitLibraryInclude("stdlib/genglsl/lib/" + _tokenSubstitutions[ShaderGenerator::T_FILE_TRANSFORM_UV], context, stage); } emitLightFunctionDefinitions(graph, context, stage); // Emit function definitions for all nodes in the graph. emitFunctionDefinitions(graph, context, stage); const ShaderGraphOutputSocket* outputSocket = graph.getOutputSocket(); // Add main function setFunctionName("main", stage); emitLine("void main()", stage, false); emitFunctionBodyBegin(graph, context, stage); if (graph.hasClassification(ShaderNode::Classification::CLOSURE) && !graph.hasClassification(ShaderNode::Classification::SHADER)) { // Handle the case where the graph is a direct closure. // We don't support rendering closures without attaching // to a surface shader, so just output black. emitLine(outputSocket->getVariable() + " = vec4(0.0, 0.0, 0.0, 1.0)", stage); } else if (context.getOptions().hwWriteDepthMoments) { emitLine(outputSocket->getVariable() + " = vec4(mx_compute_depth_moments(), 0.0, 1.0)", stage); } else if (context.getOptions().hwWriteAlbedoTable) { emitLine(outputSocket->getVariable() + " = vec4(mx_generate_dir_albedo_table(), 1.0)", stage); } else if (context.getOptions().hwWriteEnvPrefilter) { emitLine(outputSocket->getVariable() + " = vec4(mx_generate_prefilter_env(), 1.0)", stage); } else { // Add all function calls. // // Surface shaders need special handling. if (graph.hasClassification(ShaderNode::Classification::SHADER | ShaderNode::Classification::SURFACE)) { // 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); } } } } else { // No surface shader graph so just generate all // function calls in order. emitFunctionCalls(graph, context, stage); } // Emit final output const ShaderOutput* outputConnection = outputSocket->getConnection(); if (outputConnection) { if (graph.hasClassification(ShaderNode::Classification::SURFACE)) { string outColor = outputConnection->getVariable() + ".color"; string outTransparency = outputConnection->getVariable() + ".transparency"; if (context.getOptions().hwSrgbEncodeOutput) { outColor = "mx_srgb_encode(" + outColor + ")"; } if (context.getOptions().hwTransparency) { emitLine("float outAlpha = clamp(1.0 - dot(" + outTransparency + ", vec3(0.3333)), 0.0, 1.0)", stage); emitLine(outputSocket->getVariable() + " = vec4(" + outColor + ", outAlpha)", stage); emitLine("if (outAlpha < " + HW::T_ALPHA_THRESHOLD + ")", stage, false); emitScopeBegin(stage); emitLine("discard", stage); emitScopeEnd(stage); } else { emitLine(outputSocket->getVariable() + " = vec4(" + outColor + ", 1.0)", stage); } } else { string outValue = outputConnection->getVariable(); if (context.getOptions().hwSrgbEncodeOutput && outputSocket->getType().isFloat3()) { outValue = "mx_srgb_encode(" + outValue + ")"; } if (!outputSocket->getType().isFloat4()) { toVec4(outputSocket->getType(), outValue); } emitLine(outputSocket->getVariable() + " = " + outValue, stage); } } else { string outputValue = outputSocket->getValue() ? _syntax->getValue(outputSocket->getType(), *outputSocket->getValue()) : _syntax->getDefaultValue(outputSocket->getType()); if (!outputSocket->getType().isFloat4()) { string finalOutput = outputSocket->getVariable() + "_tmp"; emitLine(_syntax->getTypeName(outputSocket->getType()) + " " + finalOutput + " = " + outputValue, stage); toVec4(outputSocket->getType(), finalOutput); emitLine(outputSocket->getVariable() + " = " + finalOutput, stage); } else { emitLine(outputSocket->getVariable() + " = " + outputValue, stage); } } } // End main function emitFunctionBodyEnd(graph, context, stage); } void GlslShaderGenerator::emitLightFunctionDefinitions(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const { DEFINE_SHADER_STAGE(stage, Stage::PIXEL) { // Emit Light functions if requested if (requiresLighting(graph) && context.getOptions().hwMaxActiveLightSources > 0) { // For surface shaders we need light shaders if (graph.hasClassification(ShaderNode::Classification::SHADER | ShaderNode::Classification::SURFACE)) { // Emit functions for all bound light shaders HwLightShadersPtr lightShaders = context.getUserData(HW::USER_DATA_LIGHT_SHADERS); if (lightShaders) { for (const auto& it : lightShaders->get()) { emitFunctionDefinition(*it.second, context, stage); } } // Emit functions for light sampling for (const auto& it : _lightSamplingNodes) { emitFunctionDefinition(*it, context, stage); } } } } } void GlslShaderGenerator::toVec4(TypeDesc type, string& variable) { if (type.isFloat3()) { variable = "vec4(" + variable + ", 1.0)"; } else if (type.isFloat2()) { variable = "vec4(" + variable + ", 0.0, 1.0)"; } else if (type == Type::FLOAT || type == Type::INTEGER) { variable = "vec4(" + variable + ", " + variable + ", " + variable + ", 1.0)"; } else if (type == Type::BSDF || type == Type::EDF) { variable = "vec4(" + variable + ", 1.0)"; } else { // Can't understand other types. Just return black. variable = "vec4(0.0, 0.0, 0.0, 1.0)"; } } void GlslShaderGenerator::emitVariableDeclaration(const ShaderPort* variable, const string& qualifier, GenContext&, ShaderStage& stage, bool assignValue) const { // A file texture input needs special handling on GLSL if (variable->getType() == Type::FILENAME) { // Samplers must always be uniforms string str = qualifier.empty() ? EMPTY_STRING : qualifier + " "; emitString(str + "sampler2D " + variable->getVariable(), stage); } else { string str = qualifier.empty() ? EMPTY_STRING : qualifier + " "; // Varying parameters of type int must be flat qualified on output from vertex stage and // input to pixel stage. The only way to get these is with geompropvalue_integer nodes. if (qualifier.empty() && variable->getType() == Type::INTEGER && !assignValue && variable->getName().rfind(HW::T_IN_GEOMPROP, 0) == 0) { str += GlslSyntax::FLAT_QUALIFIER + " "; } str += _syntax->getTypeName(variable->getType()) + " " + variable->getVariable(); // If an array we need an array qualifier (suffix) for the variable name if (variable->getType().isArray() && variable->getValue()) { str += _syntax->getArrayVariableSuffix(variable->getType(), *variable->getValue()); } if (!variable->getSemantic().empty()) { str += " : " + variable->getSemantic(); } if (assignValue) { const string valueStr = (variable->getValue() ? _syntax->getValue(variable->getType(), *variable->getValue(), true) : _syntax->getDefaultValue(variable->getType(), true)); str += valueStr.empty() ? EMPTY_STRING : " = " + valueStr; } emitString(str, stage); } } ShaderNodeImplPtr GlslShaderGenerator::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 = context.getTypeDesc(outputs[0]->getType()); if (implElement->isA()) { // Use a compound implementation. if (outputType == Type::LIGHTSHADER) { impl = LightCompoundNodeGlsl::create(); } else if (outputType.isClosure()) { impl = ClosureCompoundNode::create(); } else { impl = CompoundNode::create(); } } else if (implElement->isA()) { // Try creating a new in the factory. impl = _implFactory.create(name); if (!impl) { // Fall back to source code implementation. if (outputType.isClosure()) { impl = ClosureSourceCodeNode::create(); } else { impl = SourceCodeNode::create(); } } } if (!impl) { return nullptr; } impl->initialize(*implElement, context); // Cache it. context.addNodeImplementation(name, impl); return impl; } const string& GlslImplementation::getTarget() const { return GlslShaderGenerator::TARGET; } MATERIALX_NAMESPACE_END