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

339 lines
11 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXRender/Util.h>
#include <MaterialXGenShader/ShaderGenerator.h>
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<StringVec>(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<ConstElementPtr, int> indexMap;
int curIndex = 0;
for (ConstElementPtr elem : doc->traverseTree())
{
indexMap[elem] = curIndex++;
}
// Generated an ordered map of shader inputs.
using ShaderInputPair = std::pair<InputPtr, ShaderPort*>;
std::map<int, ShaderInputPair> 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<Input>() : 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<Node>())
{
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