// // Copyright Contributors to the MaterialX Project // SPDX-License-Identifier: Apache-2.0 // #include #include MATERIALX_NAMESPACE_BEGIN const Color3 DEFAULT_SCREEN_COLOR_SRGB(0.3f, 0.3f, 0.32f); const Color3 DEFAULT_SCREEN_COLOR_LIN_REC709(DEFAULT_SCREEN_COLOR_SRGB.srgbToLinear()); ShaderPtr createShader(const string& shaderName, GenContext& context, ElementPtr elem) { return context.getShaderGenerator().generate(shaderName, elem, context); } ShaderPtr createConstantShader(GenContext& context, DocumentPtr stdLib, const string& shaderName, const Color3& color) { // Construct the constant color nodegraph DocumentPtr doc = createDocument(); doc->importLibrary(stdLib); NodeGraphPtr nodeGraph = doc->addNodeGraph(); NodePtr constant = nodeGraph->addNode("constant"); constant->setInputValue("value", color); OutputPtr output = nodeGraph->addOutput(); output->setConnectedNode(constant); // Generate the shader return createShader(shaderName, context, output); } ShaderPtr createDepthShader(GenContext& context, DocumentPtr stdLib, const string& shaderName) { // Construct a dummy nodegraph. DocumentPtr doc = createDocument(); doc->importLibrary(stdLib); NodeGraphPtr nodeGraph = doc->addNodeGraph(); NodePtr constant = nodeGraph->addNode("constant"); OutputPtr output = nodeGraph->addOutput(); output->setConnectedNode(constant); // Generate the shader GenContext depthContext = context; depthContext.getOptions().hwWriteDepthMoments = true; ShaderPtr shader = createShader(shaderName, depthContext, output); return shader; } ShaderPtr createAlbedoTableShader(GenContext& context, DocumentPtr stdLib, const string& shaderName) { // Construct a dummy nodegraph. DocumentPtr doc = createDocument(); doc->importLibrary(stdLib); NodeGraphPtr nodeGraph = doc->addNodeGraph(); NodePtr constant = nodeGraph->addNode("constant"); OutputPtr output = nodeGraph->addOutput(); output->setConnectedNode(constant); // Generate the shader GenContext tableContext = context; tableContext.getOptions().hwWriteAlbedoTable = true; tableContext.getOptions().hwDirectionalAlbedoMethod = DIRECTIONAL_ALBEDO_MONTE_CARLO; ShaderPtr shader = createShader(shaderName, tableContext, output); return shader; } ShaderPtr createEnvPrefilterShader(GenContext& context, DocumentPtr stdLib, const string& shaderName) { // Construct a dummy nodegraph. DocumentPtr doc = createDocument(); doc->importLibrary(stdLib); NodeGraphPtr nodeGraph = doc->addNodeGraph(); NodePtr constant = nodeGraph->addNode("constant"); OutputPtr output = nodeGraph->addOutput(); output->setConnectedNode(constant); // Generate the shader GenContext tableContext = context; tableContext.getOptions().hwWriteEnvPrefilter = true; ShaderPtr shader = createShader(shaderName, tableContext, output); return shader; } ShaderPtr createBlurShader(GenContext& context, DocumentPtr stdLib, const string& shaderName, const string& filterType, float filterSize) { // Construct the blur nodegraph DocumentPtr doc = createDocument(); doc->importLibrary(stdLib); NodeGraphPtr nodeGraph = doc->addNodeGraph(); NodePtr imageNode = nodeGraph->addNode("image", "image"); NodePtr blurNode = nodeGraph->addNode("blur", "blur"); blurNode->setConnectedNode("in", imageNode); blurNode->setInputValue("size", filterSize); blurNode->setInputValue("filtertype", filterType); OutputPtr output = nodeGraph->addOutput(); output->setConnectedNode(blurNode); // Generate the shader GenContext blurContext = context; blurContext.getOptions().fileTextureVerticalFlip = false; return createShader(shaderName, blurContext, output); } unsigned int getUIProperties(InputPtr input, const string& target, UIProperties& uiProperties) { if (!input) { return 0; } InputPtr nodeDefInput = getNodeDefInput(input, target); if (nodeDefInput) { input = nodeDefInput; } unsigned int propertyCount = 0; uiProperties.uiName = input->getAttribute(ValueElement::UI_NAME_ATTRIBUTE); if (!uiProperties.uiName.empty()) { propertyCount++; } uiProperties.uiFolder = input->getAttribute(ValueElement::UI_FOLDER_ATTRIBUTE); if (!uiProperties.uiFolder.empty()) { propertyCount++; } if (input->getIsUniform()) { uiProperties.enumeration = input->getTypedAttribute(ValueElement::ENUM_ATTRIBUTE); if (!uiProperties.enumeration.empty()) { propertyCount++; } const string& enumValuesAttr = input->getAttribute(ValueElement::ENUM_VALUES_ATTRIBUTE); if (!enumValuesAttr.empty()) { const string COMMA_SEPARATOR = ","; const TypeDesc* typeDesc = TypeDesc::get(input->getType()); string valueString; size_t index = 0; for (const string& val : splitString(enumValuesAttr, COMMA_SEPARATOR)) { if (index < typeDesc->getSize() - 1) { valueString += val + COMMA_SEPARATOR; index++; } else { valueString += val; uiProperties.enumerationValues.push_back(Value::createValueFromStrings(valueString, input->getType())); valueString.clear(); index = 0; } } if (uiProperties.enumeration.size() != uiProperties.enumerationValues.size()) { throw std::runtime_error("Every enum must have a value!"); } propertyCount++; } } const string& uiMinString = input->getAttribute(ValueElement::UI_MIN_ATTRIBUTE); if (!uiMinString.empty()) { ValuePtr value = Value::createValueFromStrings(uiMinString, input->getType()); if (value) { uiProperties.uiMin = value; propertyCount++; } } const string& uiMaxString = input->getAttribute(ValueElement::UI_MAX_ATTRIBUTE); if (!uiMaxString.empty()) { ValuePtr value = Value::createValueFromStrings(uiMaxString, input->getType()); if (value) { uiProperties.uiMax = value; propertyCount++; } } const string& uiSoftMinString = input->getAttribute(ValueElement::UI_SOFT_MIN_ATTRIBUTE); if (!uiSoftMinString.empty()) { ValuePtr value = Value::createValueFromStrings(uiSoftMinString, input->getType()); if (value) { uiProperties.uiSoftMin = value; propertyCount++; } } const string& uiSoftMaxString = input->getAttribute(ValueElement::UI_SOFT_MAX_ATTRIBUTE); if (!uiSoftMaxString.empty()) { ValuePtr value = Value::createValueFromStrings(uiSoftMaxString, input->getType()); if (value) { uiProperties.uiSoftMax = value; propertyCount++; } } const string& uiStepString = input->getAttribute(ValueElement::UI_STEP_ATTRIBUTE); if (!uiStepString.empty()) { ValuePtr value = Value::createValueFromStrings(uiStepString, input->getType()); if (value) { uiProperties.uiStep = value; propertyCount++; } } const string& uiAdvancedString = input->getAttribute(ValueElement::UI_ADVANCED_ATTRIBUTE); uiProperties.uiAdvanced = (uiAdvancedString == "true"); if (!uiAdvancedString.empty()) { propertyCount++; } return propertyCount; } void createUIPropertyGroups(DocumentPtr doc, const VariableBlock& block, UIPropertyGroup& groups, UIPropertyGroup& unnamedGroups, const string& pathSeparator) { // Assign a depth-first index to each element in the document. std::unordered_map indexMap; int curIndex = 0; for (ConstElementPtr elem : doc->traverseTree()) { indexMap[elem] = curIndex++; } // Generated an ordered map of shader inputs. using ShaderInputPair = std::pair; std::map shaderInputMap; for (ShaderPort* variable : block.getVariableOrder()) { if (!variable->getValue()) { continue; } // Get the input associated with this variable. ElementPtr pathElement = doc->getDescendant(variable->getPath()); InputPtr input = pathElement ? pathElement->asA() : nullptr; // Redirect to interface inputs when present. if (input) { InputPtr interfaceInput = input->getInterfaceInput(); if (interfaceInput) { input = interfaceInput; } } // Add the shader input if unique. if (input) { int treeIndex = indexMap[input]; if (shaderInputMap.count(treeIndex)) { continue; } shaderInputMap[treeIndex] = ShaderInputPair(input, variable); } } // Generate UI properties for each shader input in order. for (const auto& it : shaderInputMap) { // Retrieve the shader input pair. ShaderInputPair pair = it.second; // Gather the UI properties for this input. UIPropertyItem item; item.variable = pair.second; getUIProperties(pair.first, EMPTY_STRING, item.ui); // Generate the item label. if (!item.ui.uiName.empty()) { item.label = item.ui.uiName; } if (item.label.empty()) { item.label = pair.first->getName(); } // Prepend a parent label for unlabeled node inputs. ElementPtr parent = pair.first->getParent(); if (item.ui.uiFolder.empty() && parent && parent->isA()) { item.label = parent->getName() + pathSeparator + item.label; } // Add the new item. if (!item.ui.uiFolder.empty()) { groups.emplace(item.ui.uiFolder, item); } else { unnamedGroups.emplace(EMPTY_STRING, item); } } } MATERIALX_NAMESPACE_END