mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-25 06:04:57 +00:00
1435 lines
61 KiB
C++
1435 lines
61 KiB
C++
//
|
|
// Copyright Contributors to the MaterialX Project
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
#include <MaterialXCore/Document.h>
|
|
|
|
MATERIALX_NAMESPACE_BEGIN
|
|
|
|
namespace
|
|
{
|
|
|
|
// Return the NodeDef associated with a legacy ShaderRef element.
|
|
NodeDefPtr getShaderNodeDef(ElementPtr shaderRef)
|
|
{
|
|
if (shaderRef->hasAttribute(NodeDef::NODE_DEF_ATTRIBUTE))
|
|
{
|
|
string nodeDefString = shaderRef->getAttribute(NodeDef::NODE_DEF_ATTRIBUTE);
|
|
ConstDocumentPtr doc = shaderRef->getDocument();
|
|
NodeDefPtr child = doc->getNodeDef(shaderRef->getQualifiedName(nodeDefString));
|
|
return child ? child : doc->getNodeDef(nodeDefString);
|
|
}
|
|
if (shaderRef->hasAttribute(NodeDef::NODE_ATTRIBUTE))
|
|
{
|
|
string nodeString = shaderRef->getAttribute(NodeDef::NODE_ATTRIBUTE);
|
|
string type = shaderRef->getAttribute(TypedElement::TYPE_ATTRIBUTE);
|
|
string target = shaderRef->getAttribute(InterfaceElement::TARGET_ATTRIBUTE);
|
|
string version = shaderRef->getAttribute(InterfaceElement::VERSION_ATTRIBUTE);
|
|
vector<NodeDefPtr> nodeDefs = shaderRef->getDocument()->getMatchingNodeDefs(shaderRef->getQualifiedName(nodeString));
|
|
vector<NodeDefPtr> secondary = shaderRef->getDocument()->getMatchingNodeDefs(nodeString);
|
|
nodeDefs.insert(nodeDefs.end(), secondary.begin(), secondary.end());
|
|
for (NodeDefPtr nodeDef : nodeDefs)
|
|
{
|
|
if (targetStringsMatch(nodeDef->getTarget(), target) &&
|
|
nodeDef->isVersionCompatible(version) &&
|
|
(type.empty() || nodeDef->getType() == type))
|
|
{
|
|
return nodeDef;
|
|
}
|
|
}
|
|
}
|
|
return NodeDefPtr();
|
|
}
|
|
|
|
// Copy an input between nodes, maintaining all existing bindings.
|
|
void copyInputWithBindings(NodePtr sourceNode, const string& sourceInputName,
|
|
NodePtr destNode, const string& destInputName)
|
|
{
|
|
InputPtr sourceInput = sourceNode->getInput(sourceInputName);
|
|
if (sourceInput)
|
|
{
|
|
InputPtr destInput = destNode->getInput(destInputName);
|
|
if (!destInput)
|
|
{
|
|
destInput = destNode->addInput(destInputName, sourceInput->getType());
|
|
}
|
|
destInput->copyContentFrom(sourceInput);
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
void Document::upgradeVersion()
|
|
{
|
|
std::pair<int, int> documentVersion = getVersionIntegers();
|
|
std::pair<int, int> expectedVersion(MATERIALX_MAJOR_VERSION, MATERIALX_MINOR_VERSION);
|
|
if (documentVersion >= expectedVersion)
|
|
{
|
|
return;
|
|
}
|
|
int majorVersion = documentVersion.first;
|
|
int minorVersion = documentVersion.second;
|
|
|
|
// Upgrade from v1.22 to v1.23
|
|
if (majorVersion == 1 && minorVersion == 22)
|
|
{
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
if (elem->getAttribute(TypedElement::TYPE_ATTRIBUTE) == "vector")
|
|
{
|
|
elem->setAttribute(TypedElement::TYPE_ATTRIBUTE, getTypeString<Vector3>());
|
|
}
|
|
}
|
|
minorVersion = 23;
|
|
}
|
|
|
|
// Upgrade from v1.23 to v1.24
|
|
if (majorVersion == 1 && minorVersion == 23)
|
|
{
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
if (elem->getCategory() == "shader" && elem->hasAttribute("shadername"))
|
|
{
|
|
elem->setAttribute(NodeDef::NODE_ATTRIBUTE, elem->getAttribute("shadername"));
|
|
elem->removeAttribute("shadername");
|
|
}
|
|
for (ElementPtr child : getChildrenOfType<Element>("assign"))
|
|
{
|
|
elem->changeChildCategory(child, "materialassign");
|
|
}
|
|
}
|
|
minorVersion = 24;
|
|
}
|
|
|
|
// Upgrade from v1.24 to v1.25
|
|
if (majorVersion == 1 && minorVersion == 24)
|
|
{
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
if (elem->isA<Input>() && elem->hasAttribute("graphname"))
|
|
{
|
|
elem->setAttribute("opgraph", elem->getAttribute("graphname"));
|
|
elem->removeAttribute("graphname");
|
|
}
|
|
}
|
|
minorVersion = 25;
|
|
}
|
|
|
|
// Upgrade from v1.25 to v1.26
|
|
if (majorVersion == 1 && minorVersion == 25)
|
|
{
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
if (elem->getCategory() == "constant")
|
|
{
|
|
ElementPtr param = elem->getChild("color");
|
|
if (param)
|
|
{
|
|
param->setName("value");
|
|
}
|
|
}
|
|
}
|
|
minorVersion = 26;
|
|
}
|
|
|
|
// Upgrade from v1.26 to v1.34
|
|
if (majorVersion == 1 && minorVersion == 26)
|
|
{
|
|
// Upgrade elements in place.
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
ElementVec origChildren = elem->getChildren();
|
|
for (ElementPtr child : origChildren)
|
|
{
|
|
if (child->getCategory() == "opgraph")
|
|
{
|
|
elem->changeChildCategory(child, "nodegraph");
|
|
}
|
|
else if (child->getCategory() == "shader")
|
|
{
|
|
NodeDefPtr nodeDef = elem->changeChildCategory(child, "nodedef")->asA<NodeDef>();
|
|
if (nodeDef->hasAttribute("shadertype"))
|
|
{
|
|
nodeDef->setType(SURFACE_SHADER_TYPE_STRING);
|
|
}
|
|
if (nodeDef->hasAttribute("shaderprogram"))
|
|
{
|
|
nodeDef->setNodeString(nodeDef->getAttribute("shaderprogram"));
|
|
}
|
|
}
|
|
else if (child->getCategory() == "shaderref")
|
|
{
|
|
if (child->hasAttribute("shadertype"))
|
|
{
|
|
child->setAttribute(TypedElement::TYPE_ATTRIBUTE, SURFACE_SHADER_TYPE_STRING);
|
|
child->removeAttribute("shadertype");
|
|
}
|
|
}
|
|
else if (child->getCategory() == "parameter")
|
|
{
|
|
if (child->getAttribute(TypedElement::TYPE_ATTRIBUTE) == "opgraphnode")
|
|
{
|
|
if (elem->isA<Node>())
|
|
{
|
|
InputPtr input = elem->changeChildCategory(child, "input")->asA<Input>();
|
|
input->setNodeName(input->getAttribute("value"));
|
|
input->removeAttribute("value");
|
|
if (input->getConnectedNode())
|
|
{
|
|
input->setType(input->getConnectedNode()->getType());
|
|
}
|
|
else
|
|
{
|
|
input->setType(getTypeString<Color3>());
|
|
}
|
|
}
|
|
else if (elem->isA<Output>())
|
|
{
|
|
if (child->getName() == "in")
|
|
{
|
|
elem->setAttribute("nodename", child->getAttribute("value"));
|
|
}
|
|
elem->removeChild(child->getName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assign nodedef names to shaderrefs.
|
|
for (ElementPtr mat : getChildrenOfType<Element>("material"))
|
|
{
|
|
for (ElementPtr shaderRef : mat->getChildrenOfType<Element>("shaderref"))
|
|
{
|
|
if (!getShaderNodeDef(shaderRef))
|
|
{
|
|
NodeDefPtr nodeDef = getNodeDef(shaderRef->getName());
|
|
if (nodeDef)
|
|
{
|
|
shaderRef->setAttribute(NodeDef::NODE_DEF_ATTRIBUTE, nodeDef->getName());
|
|
shaderRef->setAttribute(NodeDef::NODE_ATTRIBUTE, nodeDef->getNodeString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Move connections from nodedef inputs to bindinputs.
|
|
ElementVec materials = getChildrenOfType<Element>("material");
|
|
for (NodeDefPtr nodeDef : getNodeDefs())
|
|
{
|
|
for (InputPtr input : nodeDef->getActiveInputs())
|
|
{
|
|
if (input->hasAttribute("opgraph") && input->hasAttribute("graphoutput"))
|
|
{
|
|
for (ElementPtr mat : materials)
|
|
{
|
|
for (ElementPtr shaderRef : mat->getChildrenOfType<Element>("shaderref"))
|
|
{
|
|
if (getShaderNodeDef(shaderRef) == nodeDef && !shaderRef->getChild(input->getName()))
|
|
{
|
|
ElementPtr bindInput = shaderRef->addChildOfCategory("bindinput", input->getName());
|
|
bindInput->setAttribute(TypedElement::TYPE_ATTRIBUTE, input->getType());
|
|
bindInput->setAttribute("nodegraph", input->getAttribute("opgraph"));
|
|
bindInput->setAttribute("output", input->getAttribute("graphoutput"));
|
|
}
|
|
}
|
|
}
|
|
input->removeAttribute("opgraph");
|
|
input->removeAttribute("graphoutput");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Combine udim assignments into udim sets.
|
|
for (GeomInfoPtr geomInfo : getGeomInfos())
|
|
{
|
|
for (ElementPtr child : geomInfo->getChildrenOfType<Element>("geomattr"))
|
|
{
|
|
geomInfo->changeChildCategory(child, "geomprop");
|
|
}
|
|
}
|
|
if (getGeomPropValue("udim") && !getGeomPropValue("udimset"))
|
|
{
|
|
StringSet udimSet;
|
|
for (GeomInfoPtr geomInfo : getGeomInfos())
|
|
{
|
|
for (GeomPropPtr geomProp : geomInfo->getGeomProps())
|
|
{
|
|
if (geomProp->getName() == "udim")
|
|
{
|
|
udimSet.insert(geomProp->getValueString());
|
|
}
|
|
}
|
|
}
|
|
|
|
string udimSetString;
|
|
for (const string& udim : udimSet)
|
|
{
|
|
if (udimSetString.empty())
|
|
{
|
|
udimSetString = udim;
|
|
}
|
|
else
|
|
{
|
|
udimSetString += ", " + udim;
|
|
}
|
|
}
|
|
|
|
GeomInfoPtr udimSetInfo = addGeomInfo();
|
|
udimSetInfo->setGeomPropValue(UDIM_SET_PROPERTY, udimSetString, getTypeString<StringVec>());
|
|
}
|
|
|
|
minorVersion = 34;
|
|
}
|
|
|
|
// Upgrade from v1.34 to v1.35
|
|
if (majorVersion == 1 && minorVersion == 34)
|
|
{
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
if (elem->getAttribute(TypedElement::TYPE_ATTRIBUTE) == "matrix")
|
|
{
|
|
elem->setAttribute(TypedElement::TYPE_ATTRIBUTE, getTypeString<Matrix44>());
|
|
}
|
|
if (elem->hasAttribute("default") && !elem->hasAttribute(ValueElement::VALUE_ATTRIBUTE))
|
|
{
|
|
elem->setAttribute(ValueElement::VALUE_ATTRIBUTE, elem->getAttribute("default"));
|
|
elem->removeAttribute("default");
|
|
}
|
|
|
|
MaterialAssignPtr matAssign = elem->asA<MaterialAssign>();
|
|
if (matAssign)
|
|
{
|
|
matAssign->setMaterial(matAssign->getName());
|
|
}
|
|
}
|
|
minorVersion = 35;
|
|
}
|
|
|
|
// Upgrade from v1.35 to v1.36
|
|
if (majorVersion == 1 && minorVersion == 35)
|
|
{
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
LookPtr look = elem->asA<Look>();
|
|
GeomInfoPtr geomInfo = elem->asA<GeomInfo>();
|
|
|
|
if (elem->getAttribute(TypedElement::TYPE_ATTRIBUTE) == GEOMNAME_TYPE_STRING &&
|
|
elem->getAttribute(ValueElement::VALUE_ATTRIBUTE) == "*")
|
|
{
|
|
elem->setAttribute(ValueElement::VALUE_ATTRIBUTE, UNIVERSAL_GEOM_NAME);
|
|
}
|
|
if (elem->getAttribute(TypedElement::TYPE_ATTRIBUTE) == FILENAME_TYPE_STRING)
|
|
{
|
|
StringMap stringMap;
|
|
stringMap["%UDIM"] = UDIM_TOKEN;
|
|
stringMap["%UVTILE"] = UV_TILE_TOKEN;
|
|
elem->setAttribute(ValueElement::VALUE_ATTRIBUTE, replaceSubstrings(elem->getAttribute(ValueElement::VALUE_ATTRIBUTE), stringMap));
|
|
}
|
|
|
|
ElementVec origChildren = elem->getChildren();
|
|
for (ElementPtr child : origChildren)
|
|
{
|
|
if (elem->getCategory() == "material" && child->getCategory() == "override")
|
|
{
|
|
for (ElementPtr shaderRef : elem->getChildrenOfType<Element>("shaderref"))
|
|
{
|
|
NodeDefPtr nodeDef = getShaderNodeDef(shaderRef);
|
|
if (nodeDef)
|
|
{
|
|
for (ValueElementPtr activeValue : nodeDef->getActiveValueElements())
|
|
{
|
|
if (activeValue->getAttribute("publicname") == child->getName() &&
|
|
!shaderRef->getChild(child->getName()))
|
|
{
|
|
if (activeValue->getCategory() == "parameter")
|
|
{
|
|
ElementPtr bindParam = shaderRef->addChildOfCategory("bindparam", activeValue->getName());
|
|
bindParam->setAttribute(TypedElement::TYPE_ATTRIBUTE, activeValue->getType());
|
|
bindParam->setAttribute(ValueElement::VALUE_ATTRIBUTE, child->getAttribute("value"));
|
|
}
|
|
else if (activeValue->isA<Input>())
|
|
{
|
|
ElementPtr bindInput = shaderRef->addChildOfCategory("bindinput", activeValue->getName());
|
|
bindInput->setAttribute(TypedElement::TYPE_ATTRIBUTE, activeValue->getType());
|
|
bindInput->setAttribute(ValueElement::VALUE_ATTRIBUTE, child->getAttribute("value"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
elem->removeChild(child->getName());
|
|
}
|
|
else if (elem->getCategory() == "material" && child->getCategory() == "materialinherit")
|
|
{
|
|
elem->setInheritString(child->getAttribute("material"));
|
|
elem->removeChild(child->getName());
|
|
}
|
|
else if (look && child->getCategory() == "lookinherit")
|
|
{
|
|
elem->setInheritString(child->getAttribute("look"));
|
|
elem->removeChild(child->getName());
|
|
}
|
|
}
|
|
}
|
|
minorVersion = 36;
|
|
}
|
|
|
|
// Upgrade from 1.36 to 1.37
|
|
if (majorVersion == 1 && minorVersion == 36)
|
|
{
|
|
// Convert type attributes to child outputs.
|
|
for (NodeDefPtr nodeDef : getNodeDefs())
|
|
{
|
|
InterfaceElementPtr interfaceElem = std::static_pointer_cast<InterfaceElement>(nodeDef);
|
|
if (interfaceElem && interfaceElem->hasType())
|
|
{
|
|
string type = interfaceElem->getAttribute(TypedElement::TYPE_ATTRIBUTE);
|
|
OutputPtr outputPtr;
|
|
if (!type.empty() && type != MULTI_OUTPUT_TYPE_STRING)
|
|
{
|
|
outputPtr = interfaceElem->getOutput("out");
|
|
if (!outputPtr)
|
|
{
|
|
outputPtr = interfaceElem->addOutput("out", type);
|
|
}
|
|
}
|
|
interfaceElem->removeAttribute(TypedElement::TYPE_ATTRIBUTE);
|
|
|
|
const string& defaultInput = interfaceElem->getAttribute(Output::DEFAULT_INPUT_ATTRIBUTE);
|
|
if (outputPtr && !defaultInput.empty())
|
|
{
|
|
outputPtr->setAttribute(Output::DEFAULT_INPUT_ATTRIBUTE, defaultInput);
|
|
}
|
|
interfaceElem->removeAttribute(Output::DEFAULT_INPUT_ATTRIBUTE);
|
|
}
|
|
}
|
|
|
|
// Remove legacy shader nodedefs.
|
|
for (NodeDefPtr nodeDef : getNodeDefs())
|
|
{
|
|
if (nodeDef->hasAttribute("shadertype"))
|
|
{
|
|
for (ElementPtr mat : getChildrenOfType<Element>("material"))
|
|
{
|
|
for (ElementPtr shaderRef : mat->getChildrenOfType<Element>("shaderref"))
|
|
{
|
|
if (shaderRef->getAttribute(InterfaceElement::NODE_DEF_ATTRIBUTE) == nodeDef->getName())
|
|
{
|
|
shaderRef->removeAttribute(InterfaceElement::NODE_DEF_ATTRIBUTE);
|
|
}
|
|
}
|
|
}
|
|
removeNodeDef(nodeDef->getName());
|
|
}
|
|
}
|
|
|
|
// Convert geometric attributes to geometric properties.
|
|
for (GeomInfoPtr geomInfo : getGeomInfos())
|
|
{
|
|
for (ElementPtr child : geomInfo->getChildrenOfType<Element>("geomattr"))
|
|
{
|
|
geomInfo->changeChildCategory(child, "geomprop");
|
|
}
|
|
}
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
NodePtr node = elem->asA<Node>();
|
|
if (!node)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (node->getCategory() == "geomattrvalue")
|
|
{
|
|
node->setCategory("geompropvalue");
|
|
if (node->hasAttribute("attrname"))
|
|
{
|
|
node->setAttribute("geomprop", node->getAttribute("attrname"));
|
|
node->removeAttribute("attrname");
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<NodePtr> unusedNodes;
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
NodePtr node = elem->asA<Node>();
|
|
if (!node)
|
|
{
|
|
continue;
|
|
}
|
|
const string& nodeCategory = node->getCategory();
|
|
|
|
// Change category from "invert to "invertmatrix" for matrix invert nodes
|
|
if (nodeCategory == "invert" &&
|
|
(node->getType() == getTypeString<Matrix33>() || node->getType() == getTypeString<Matrix44>()))
|
|
{
|
|
node->setCategory("invertmatrix");
|
|
}
|
|
|
|
// Change category from "rotate" to "rotate2d" or "rotate3d" nodes
|
|
else if (nodeCategory == "rotate")
|
|
{
|
|
node->setCategory((node->getType() == getTypeString<Vector2>()) ? "rotate2d" : "rotate3d");
|
|
}
|
|
|
|
// Convert "compare" node to "ifgreatereq".
|
|
else if (nodeCategory == "compare")
|
|
{
|
|
node->setCategory("ifgreatereq");
|
|
InputPtr intest = node->getInput("intest");
|
|
if (intest)
|
|
{
|
|
intest->setName("value1");
|
|
}
|
|
ElementPtr cutoff = node->getChild("cutoff");
|
|
if (cutoff)
|
|
{
|
|
cutoff = node->changeChildCategory(cutoff, "input");
|
|
cutoff->setName("value2");
|
|
}
|
|
InputPtr in1 = node->getInput("in1");
|
|
InputPtr in2 = node->getInput("in2");
|
|
if (in1 && in2)
|
|
{
|
|
in1->setName(createValidChildName("temp"));
|
|
in2->setName("in1");
|
|
in1->setName("in2");
|
|
}
|
|
}
|
|
|
|
// Change nodes with category "transform[vector|point|normal]",
|
|
// which are not fromspace/tospace variants, to "transformmatrix"
|
|
else if (nodeCategory == "transformpoint" ||
|
|
nodeCategory == "transformvector" ||
|
|
nodeCategory == "transformnormal")
|
|
{
|
|
if (!node->getChild("fromspace") && !node->getChild("tospace"))
|
|
{
|
|
node->setCategory("transformmatrix");
|
|
}
|
|
}
|
|
|
|
// Convert "combine" to "combine2", "combine3" or "combine4"
|
|
else if (nodeCategory == "combine")
|
|
{
|
|
if (node->getChild("in4"))
|
|
{
|
|
node->setCategory("combine4");
|
|
}
|
|
else if (node->getChild("in3"))
|
|
{
|
|
node->setCategory("combine3");
|
|
}
|
|
else
|
|
{
|
|
node->setCategory("combine2");
|
|
}
|
|
}
|
|
|
|
// Convert "separate" to "separate2", "separate3" or "separate4"
|
|
else if (nodeCategory == "separate")
|
|
{
|
|
InputPtr in = node->getInput("in");
|
|
if (in)
|
|
{
|
|
const string& inType = in->getType();
|
|
if (inType == getTypeString<Vector4>() || inType == getTypeString<Color4>())
|
|
{
|
|
node->setCategory("separate4");
|
|
}
|
|
else if (inType == getTypeString<Vector3>() || inType == getTypeString<Color3>())
|
|
{
|
|
node->setCategory("separate3");
|
|
}
|
|
else
|
|
{
|
|
node->setCategory("separate2");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert backdrop nodes to backdrop elements
|
|
else if (nodeCategory == "backdrop")
|
|
{
|
|
BackdropPtr backdrop = addBackdrop(node->getName());
|
|
for (ElementPtr child : node->getChildrenOfType<Element>("parameter"))
|
|
{
|
|
if (child->hasAttribute(ValueElement::VALUE_ATTRIBUTE))
|
|
{
|
|
backdrop->setAttribute(child->getName(), child->getAttribute(ValueElement::VALUE_ATTRIBUTE));
|
|
}
|
|
}
|
|
unusedNodes.push_back(node);
|
|
}
|
|
}
|
|
for (NodePtr node : unusedNodes)
|
|
{
|
|
node->getParent()->removeChild(node->getName());
|
|
}
|
|
|
|
minorVersion = 37;
|
|
}
|
|
|
|
// Upgrade from 1.37 to 1.38
|
|
if (majorVersion == 1 && minorVersion == 37)
|
|
{
|
|
// Convert color2 types to vector2
|
|
const StringMap COLOR2_CHANNEL_MAP = { { "r", "x" }, { "a", "y" } };
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
if (elem->getAttribute(TypedElement::TYPE_ATTRIBUTE) == "color2")
|
|
{
|
|
elem->setAttribute(TypedElement::TYPE_ATTRIBUTE, getTypeString<Vector2>());
|
|
NodePtr parentNode = elem->getParent()->asA<Node>();
|
|
if (!parentNode)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (PortElementPtr port : parentNode->getDownstreamPorts())
|
|
{
|
|
if (port->hasAttribute("channels"))
|
|
{
|
|
string channels = port->getAttribute("channels");
|
|
channels = replaceSubstrings(channels, COLOR2_CHANNEL_MAP);
|
|
port->setAttribute("channels", channels);
|
|
}
|
|
if (port->hasOutputString())
|
|
{
|
|
string output = port->getOutputString();
|
|
output = replaceSubstrings(output, COLOR2_CHANNEL_MAP);
|
|
port->setOutputString(output);
|
|
}
|
|
}
|
|
|
|
ElementPtr channels = parentNode->getChild("channels");
|
|
if (channels && channels->hasAttribute(ValueElement::VALUE_ATTRIBUTE))
|
|
{
|
|
string value = channels->getAttribute(ValueElement::VALUE_ATTRIBUTE);
|
|
value = replaceSubstrings(value, COLOR2_CHANNEL_MAP);
|
|
channels->setAttribute(ValueElement::VALUE_ATTRIBUTE, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert material elements to material nodes
|
|
for (ElementPtr mat : getChildrenOfType<Element>("material"))
|
|
{
|
|
NodePtr materialNode = nullptr;
|
|
|
|
for (ElementPtr shaderRef : mat->getChildrenOfType<Element>("shaderref"))
|
|
{
|
|
NodeDefPtr nodeDef = getShaderNodeDef(shaderRef);
|
|
|
|
// Get the shader node type and category, using the shader nodedef if present.
|
|
string shaderNodeType = nodeDef ? nodeDef->getType() : SURFACE_SHADER_TYPE_STRING;
|
|
string shaderNodeCategory = nodeDef ? nodeDef->getNodeString() : shaderRef->getAttribute(NodeDef::NODE_ATTRIBUTE);
|
|
|
|
// Add the shader node.
|
|
string shaderNodeName = createValidChildName(shaderRef->getName());
|
|
NodePtr shaderNode = addNode(shaderNodeCategory, shaderNodeName, shaderNodeType);
|
|
|
|
// Copy attributes to the shader node.
|
|
string nodeDefString = shaderRef->getAttribute(NodeDef::NODE_DEF_ATTRIBUTE);
|
|
string target = shaderRef->getAttribute(InterfaceElement::TARGET_ATTRIBUTE);
|
|
string version = shaderRef->getAttribute(InterfaceElement::VERSION_ATTRIBUTE);
|
|
if (!nodeDefString.empty())
|
|
{
|
|
shaderNode->setNodeDefString(nodeDefString);
|
|
}
|
|
if (!target.empty())
|
|
{
|
|
shaderNode->setTarget(target);
|
|
}
|
|
if (!version.empty())
|
|
{
|
|
shaderNode->setVersionString(version);
|
|
}
|
|
shaderNode->setSourceUri(shaderRef->getSourceUri());
|
|
|
|
// Copy child elements to the shader node.
|
|
for (ElementPtr child : shaderRef->getChildren())
|
|
{
|
|
ElementPtr newChild;
|
|
if (child->getCategory() == "bindinput" || child->getCategory() == "bindparam")
|
|
{
|
|
newChild = shaderNode->addInput(child->getName());
|
|
}
|
|
else if (child->getCategory() == "bindtoken")
|
|
{
|
|
newChild = shaderNode->addToken(child->getName());
|
|
}
|
|
if (newChild)
|
|
{
|
|
newChild->copyContentFrom(child);
|
|
}
|
|
}
|
|
|
|
// Create a material node if needed, making a connection to the new shader node.
|
|
if (!materialNode)
|
|
{
|
|
materialNode = addMaterialNode(createValidName("temp"), shaderNode);
|
|
materialNode->setSourceUri(mat->getSourceUri());
|
|
}
|
|
|
|
// Assign additional shader inputs to the material as needed.
|
|
if (!materialNode->getInput(shaderNodeType))
|
|
{
|
|
InputPtr shaderInput = materialNode->addInput(shaderNodeType, shaderNodeType);
|
|
shaderInput->setNodeName(shaderNode->getName());
|
|
}
|
|
}
|
|
|
|
// Remove the material element, transferring its name and attributes to the material node.
|
|
removeChild(mat->getName());
|
|
if (materialNode)
|
|
{
|
|
materialNode->setName(mat->getName());
|
|
for (const string& attr : mat->getAttributeNames())
|
|
{
|
|
if (!materialNode->hasAttribute(attr))
|
|
{
|
|
materialNode->setAttribute(attr, mat->getAttribute(attr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Define BSDF node pairs.
|
|
using StringPair = std::pair<string, string>;
|
|
const StringPair DIELECTRIC_BRDF = { "dielectric_brdf", "dielectric_bsdf" };
|
|
const StringPair DIELECTRIC_BTDF = { "dielectric_btdf", "dielectric_bsdf" };
|
|
const StringPair GENERALIZED_SCHLICK_BRDF = { "generalized_schlick_brdf", "generalized_schlick_bsdf" };
|
|
const StringPair CONDUCTOR_BRDF = { "conductor_brdf", "conductor_bsdf" };
|
|
const StringPair SHEEN_BRDF = { "sheen_brdf", "sheen_bsdf" };
|
|
const StringPair DIFFUSE_BRDF = { "diffuse_brdf", "oren_nayar_diffuse_bsdf" };
|
|
const StringPair BURLEY_DIFFUSE_BRDF = { "burley_diffuse_brdf", "burley_diffuse_bsdf" };
|
|
const StringPair DIFFUSE_BTDF = { "diffuse_btdf", "translucent_bsdf" };
|
|
const StringPair SUBSURFACE_BRDF = { "subsurface_brdf", "subsurface_bsdf" };
|
|
const StringPair THIN_FILM_BRDF = { "thin_film_brdf", "thin_film_bsdf" };
|
|
|
|
// Function for upgrading old nested layering setup
|
|
// to new setup with layer operators.
|
|
auto upgradeBsdfLayering = [](NodePtr node)
|
|
{
|
|
InputPtr base = node->getInput("base");
|
|
if (base)
|
|
{
|
|
NodePtr baseNode = base->getConnectedNode();
|
|
if (baseNode)
|
|
{
|
|
GraphElementPtr parent = node->getParent()->asA<GraphElement>();
|
|
// Rename the top bsdf node, and give its old name to the layer operator
|
|
// so we don't need to update any connection references.
|
|
const string oldName = node->getName();
|
|
node->setName(oldName + "__layer_top");
|
|
NodePtr layer = parent->addNode("layer", oldName, "BSDF");
|
|
InputPtr layerTop = layer->addInput("top", "BSDF");
|
|
InputPtr layerBase = layer->addInput("base", "BSDF");
|
|
layerTop->setConnectedNode(node);
|
|
layerBase->setConnectedNode(baseNode);
|
|
}
|
|
node->removeInput("base");
|
|
}
|
|
};
|
|
|
|
// Storage for inputs found connected downstream from artistic_ior node.
|
|
vector<InputPtr> artisticIorConnections, artisticExtConnections;
|
|
|
|
// Update all nodes.
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
NodePtr node = elem->asA<Node>();
|
|
if (!node)
|
|
{
|
|
continue;
|
|
}
|
|
const string& nodeCategory = node->getCategory();
|
|
if (nodeCategory == "atan2")
|
|
{
|
|
InputPtr input = node->getInput("in1");
|
|
InputPtr input2 = node->getInput("in2");
|
|
if (input && input2)
|
|
{
|
|
input->setName(EMPTY_STRING);
|
|
input2->setName("in1");
|
|
input->setName("in2");
|
|
}
|
|
else
|
|
{
|
|
if (input)
|
|
{
|
|
input->setName("in2");
|
|
}
|
|
if (input2)
|
|
{
|
|
input2->setName("in1");
|
|
}
|
|
}
|
|
}
|
|
else if (nodeCategory == "rotate3d")
|
|
{
|
|
ElementPtr axis = node->getChild("axis");
|
|
if (axis)
|
|
{
|
|
node->changeChildCategory(axis, "input");
|
|
}
|
|
}
|
|
else if (nodeCategory == DIELECTRIC_BRDF.first)
|
|
{
|
|
node->setCategory(DIELECTRIC_BRDF.second);
|
|
upgradeBsdfLayering(node);
|
|
}
|
|
else if (nodeCategory == DIELECTRIC_BTDF.first)
|
|
{
|
|
node->setCategory(DIELECTRIC_BTDF.second);
|
|
node->removeInput("interior");
|
|
InputPtr mode = node->addInput("scatter_mode", STRING_TYPE_STRING);
|
|
mode->setValueString("T");
|
|
}
|
|
else if (nodeCategory == GENERALIZED_SCHLICK_BRDF.first)
|
|
{
|
|
node->setCategory(GENERALIZED_SCHLICK_BRDF.second);
|
|
upgradeBsdfLayering(node);
|
|
}
|
|
else if (nodeCategory == SHEEN_BRDF.first)
|
|
{
|
|
node->setCategory(SHEEN_BRDF.second);
|
|
upgradeBsdfLayering(node);
|
|
}
|
|
else if (nodeCategory == THIN_FILM_BRDF.first)
|
|
{
|
|
node->setCategory(THIN_FILM_BRDF.second);
|
|
upgradeBsdfLayering(node);
|
|
}
|
|
else if (nodeCategory == CONDUCTOR_BRDF.first)
|
|
{
|
|
node->setCategory(CONDUCTOR_BRDF.second);
|
|
|
|
// Create an artistic_ior node to convert from artistic to physical parameterization.
|
|
GraphElementPtr parent = node->getParent()->asA<GraphElement>();
|
|
NodePtr artisticIor = parent->addNode("artistic_ior", node->getName() + "__artistic_ior", "multioutput");
|
|
OutputPtr artisticIor_ior = artisticIor->addOutput("ior", "color3");
|
|
OutputPtr artisticIor_extinction = artisticIor->addOutput("extinction", "color3");
|
|
|
|
// Copy inputs and bindings from conductor node to artistic_ior node.
|
|
copyInputWithBindings(node, "reflectivity", artisticIor, "reflectivity");
|
|
copyInputWithBindings(node, "edge_color", artisticIor, "edge_color");
|
|
|
|
// Update the parameterization on the conductor node
|
|
// and connect it to the artistic_ior node.
|
|
node->removeInput("reflectivity");
|
|
node->removeInput("edge_color");
|
|
InputPtr ior = node->addInput("ior", "color3");
|
|
ior->setNodeName(artisticIor->getName());
|
|
ior->setOutputString(artisticIor_ior->getName());
|
|
InputPtr extinction = node->addInput("extinction", "color3");
|
|
extinction->setNodeName(artisticIor->getName());
|
|
extinction->setOutputString(artisticIor_extinction->getName());
|
|
}
|
|
else if (nodeCategory == DIFFUSE_BRDF.first)
|
|
{
|
|
node->setCategory(DIFFUSE_BRDF.second);
|
|
}
|
|
else if (nodeCategory == BURLEY_DIFFUSE_BRDF.first)
|
|
{
|
|
node->setCategory(BURLEY_DIFFUSE_BRDF.second);
|
|
}
|
|
else if (nodeCategory == DIFFUSE_BTDF.first)
|
|
{
|
|
node->setCategory(DIFFUSE_BTDF.second);
|
|
}
|
|
else if (nodeCategory == SUBSURFACE_BRDF.first)
|
|
{
|
|
node->setCategory(SUBSURFACE_BRDF.second);
|
|
}
|
|
else if (nodeCategory == "artistic_ior")
|
|
{
|
|
OutputPtr ior = node->getOutput("ior");
|
|
if (ior)
|
|
{
|
|
ior->setType("color3");
|
|
}
|
|
OutputPtr extinction = node->getOutput("extinction");
|
|
if (extinction)
|
|
{
|
|
extinction->setType("color3");
|
|
}
|
|
}
|
|
|
|
// Search for connections to artistic_ior with vector3 type.
|
|
// If found we must insert a conversion node color3->vector3
|
|
// since the outputs of artistic_ior is now color3.
|
|
// Save the inputs here and insert the conversion nodes below,
|
|
// since we can't modify the graph while traversing it.
|
|
for (InputPtr input : node->getInputs())
|
|
{
|
|
if (input->getOutputString() == "ior" && input->getType() == "vector3")
|
|
{
|
|
NodePtr connectedNode = input->getConnectedNode();
|
|
if (connectedNode && connectedNode->getCategory() == "artistic_ior")
|
|
{
|
|
artisticIorConnections.push_back(input);
|
|
}
|
|
}
|
|
else if (input->getOutputString() == "extinction" && input->getType() == "vector3")
|
|
{
|
|
NodePtr connectedNode = input->getConnectedNode();
|
|
if (connectedNode && connectedNode->getCategory() == "artistic_ior")
|
|
{
|
|
artisticExtConnections.push_back(input);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Insert conversion nodes for artistic_ior connections found above.
|
|
for (InputPtr input : artisticIorConnections)
|
|
{
|
|
NodePtr artisticIorNode = input->getConnectedNode();
|
|
ElementPtr node = input->getParent();
|
|
GraphElementPtr parent = node->getParent()->asA<GraphElement>();
|
|
NodePtr convert = parent->addNode("convert", node->getName() + "__convert_ior", "vector3");
|
|
InputPtr convertInput = convert->addInput("in", "color3");
|
|
convertInput->setNodeName(artisticIorNode->getName());
|
|
convertInput->setOutputString("ior");
|
|
input->setNodeName(convert->getName());
|
|
input->removeAttribute(PortElement::OUTPUT_ATTRIBUTE);
|
|
}
|
|
for (InputPtr input : artisticExtConnections)
|
|
{
|
|
NodePtr artisticIorNode = input->getConnectedNode();
|
|
ElementPtr node = input->getParent();
|
|
GraphElementPtr parent = node->getParent()->asA<GraphElement>();
|
|
NodePtr convert = parent->addNode("convert", node->getName() + "__convert_extinction", "vector3");
|
|
InputPtr convertInput = convert->addInput("in", "color3");
|
|
convertInput->setNodeName(artisticIorNode->getName());
|
|
convertInput->setOutputString("extinction");
|
|
input->setNodeName(convert->getName());
|
|
input->removeAttribute(PortElement::OUTPUT_ATTRIBUTE);
|
|
}
|
|
|
|
// Make it so that interface names and nodes in a nodegraph are not duplicates
|
|
// If they are, rename the nodes.
|
|
for (NodeGraphPtr nodegraph : getNodeGraphs())
|
|
{
|
|
// Clear out any erroneously set version
|
|
nodegraph->removeAttribute(InterfaceElement::VERSION_ATTRIBUTE);
|
|
|
|
StringSet interfaceNames;
|
|
for (auto child : nodegraph->getChildren())
|
|
{
|
|
NodePtr node = child->asA<Node>();
|
|
if (node)
|
|
{
|
|
for (ValueElementPtr elem : node->getChildrenOfType<ValueElement>())
|
|
{
|
|
const string& interfaceName = elem->getInterfaceName();
|
|
if (!interfaceName.empty())
|
|
{
|
|
interfaceNames.insert(interfaceName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (string interfaceName : interfaceNames)
|
|
{
|
|
NodePtr node = nodegraph->getNode(interfaceName);
|
|
if (node)
|
|
{
|
|
string newNodeName = nodegraph->createValidChildName(interfaceName);
|
|
vector<MaterialX::PortElementPtr> downstreamPorts = node->getDownstreamPorts();
|
|
for (MaterialX::PortElementPtr downstreamPort : downstreamPorts)
|
|
{
|
|
if (downstreamPort->getNodeName() == interfaceName)
|
|
{
|
|
downstreamPort->setNodeName(newNodeName);
|
|
}
|
|
}
|
|
node->setName(newNodeName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert parameters to inputs, applying uniform markings to converted inputs
|
|
// of nodedefs.
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
if (elem->isA<InterfaceElement>())
|
|
{
|
|
for (ElementPtr param : elem->getChildrenOfType<Element>("parameter"))
|
|
{
|
|
InputPtr input = elem->changeChildCategory(param, "input")->asA<Input>();
|
|
if (elem->isA<NodeDef>())
|
|
{
|
|
input->setIsUniform(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
minorVersion = 38;
|
|
}
|
|
|
|
// Upgrade from 1.38 to 1.39
|
|
if (majorVersion == 1 && minorVersion == 38)
|
|
{
|
|
const std::unordered_map<char, size_t> CHANNEL_INDEX_MAP =
|
|
{
|
|
{ 'r', 0 }, { 'g', 1 }, { 'b', 2 }, { 'a', 3 },
|
|
{ 'x', 0 }, { 'y', 1 }, { 'z', 2 }, { 'w', 3 }
|
|
};
|
|
const std::unordered_map<char, float> CHANNEL_CONSTANT_MAP =
|
|
{
|
|
{ '0', 0.0f }, { '1', 1.0f }
|
|
};
|
|
const std::unordered_map<string, size_t> CHANNEL_COUNT_MAP =
|
|
{
|
|
{ "float", 1 },
|
|
{ "color3", 3 }, { "color4", 4 },
|
|
{ "vector2", 2 }, { "vector3", 3 }, { "vector4", 4 }
|
|
};
|
|
const std::array<std::pair<string, size_t>, 10> CHANNEL_CONVERT_PATTERNS =
|
|
{ {
|
|
{ "rgb", 3 }, { "rgb", 4 }, { "rgba", 4 },
|
|
{ "xyz", 3 }, { "xyz", 4 }, { "xyzw", 4 },
|
|
{ "rr", 1 }, { "rrr", 1 },
|
|
{ "xx", 1 }, { "xxx", 1 }
|
|
} };
|
|
const std::array<std::pair<StringSet, string>, 3> CHANNEL_ATTRIBUTE_PATTERNS =
|
|
{ {
|
|
{ { "xx", "xxx", "xxxx" }, "float" },
|
|
{ { "xyz", "x", "y", "z" }, "vector3" },
|
|
{ { "rgba", "a" }, "color4" }
|
|
} };
|
|
|
|
// Convert channels attributes to legacy swizzle nodes, which are then converted
|
|
// to modern nodes in a second pass.
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
PortElementPtr port = elem->asA<PortElement>();
|
|
if (!port)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const string& channelString = port->getAttribute("channels");
|
|
if (channelString.empty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Determine the upstream type.
|
|
ElementPtr parent = port->getParent();
|
|
GraphElementPtr graph = port->getAncestorOfType<GraphElement>();
|
|
NodePtr upstreamNode = port->getConnectedNode();
|
|
string upstreamType = upstreamNode ? upstreamNode->getType() : EMPTY_STRING;
|
|
if (upstreamType.empty() || upstreamType == MULTI_OUTPUT_TYPE_STRING)
|
|
{
|
|
for (const auto& pair : CHANNEL_ATTRIBUTE_PATTERNS)
|
|
{
|
|
if (pair.first.count(channelString))
|
|
{
|
|
upstreamType = pair.second;
|
|
break;
|
|
}
|
|
}
|
|
if (upstreamType.empty() || upstreamType == MULTI_OUTPUT_TYPE_STRING)
|
|
{
|
|
upstreamType = (port->getType() == "color3") ? "color4" : "color3";
|
|
}
|
|
}
|
|
|
|
// Ignore the channels string for purely scalar connections.
|
|
if (upstreamType == getTypeString<float>() && port->getType() == getTypeString<float>())
|
|
{
|
|
port->removeAttribute("channels");
|
|
continue;
|
|
}
|
|
|
|
// Create the new swizzle node.
|
|
NodePtr swizzleNode = graph->addNode("swizzle", graph->createValidChildName("swizzle"), port->getType());
|
|
int childIndex = (parent->getParent() == graph) ? graph->getChildIndex(parent->getName()) : graph->getChildIndex(port->getName());
|
|
if (childIndex != -1)
|
|
{
|
|
graph->setChildIndex(swizzleNode->getName(), childIndex);
|
|
}
|
|
InputPtr in = swizzleNode->addInput("in");
|
|
in->copyContentFrom(port);
|
|
in->removeAttribute("channels");
|
|
in->setType(upstreamType);
|
|
swizzleNode->setInputValue("channels", channelString);
|
|
|
|
// Connect the original port to this node.
|
|
port->setConnectedNode(swizzleNode);
|
|
port->removeAttribute(PortElement::OUTPUT_ATTRIBUTE);
|
|
port->removeAttribute(PortElement::INTERFACE_NAME_ATTRIBUTE);
|
|
port->removeAttribute("channels");
|
|
|
|
// Update any nodegraph reference
|
|
if (graph)
|
|
{
|
|
const string& portNodeGraphString = port->getNodeGraphString();
|
|
if (!portNodeGraphString.empty())
|
|
{
|
|
const string& graphName = graph->getName();
|
|
if (graphName.empty())
|
|
{
|
|
port->removeAttribute(PortElement::NODE_GRAPH_ATTRIBUTE);
|
|
}
|
|
else if (graphName != portNodeGraphString)
|
|
{
|
|
port->setNodeGraphString(graphName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update all nodes.
|
|
vector<NodePtr> unusedNodes;
|
|
for (ElementPtr elem : traverseTree())
|
|
{
|
|
NodePtr node = elem->asA<Node>();
|
|
if (!node)
|
|
{
|
|
continue;
|
|
}
|
|
const string& nodeCategory = node->getCategory();
|
|
if (nodeCategory == "layer")
|
|
{
|
|
// Convert layering of thin_film_bsdf nodes to thin-film parameters on the affected BSDF nodes.
|
|
NodePtr top = node->getConnectedNode("top");
|
|
NodePtr base = node->getConnectedNode("base");
|
|
if (top && base && top->getCategory() == "thin_film_bsdf")
|
|
{
|
|
// Apply thin-film parameters to all supported BSDF's upstream.
|
|
const StringSet BSDF_WITH_THINFILM = { "dielectric_bsdf", "conductor_bsdf", "generalized_schlick_bsdf" };
|
|
for (Edge edge : node->traverseGraph())
|
|
{
|
|
NodePtr upstream = edge.getUpstreamElement()->asA<Node>();
|
|
if (upstream && BSDF_WITH_THINFILM.count(upstream->getCategory()))
|
|
{
|
|
InputPtr scatterMode = upstream->getInput("scatter_mode");
|
|
if (!scatterMode || scatterMode->getValueString() != "T")
|
|
{
|
|
copyInputWithBindings(top, "thickness", upstream, "thinfilm_thickness");
|
|
copyInputWithBindings(top, "ior", upstream, "thinfilm_ior");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bypass the thin-film layer operator.
|
|
vector<MaterialX::PortElementPtr> downstreamPorts = node->getDownstreamPorts();
|
|
for (auto port : downstreamPorts)
|
|
{
|
|
port->setNodeName(base->getName());
|
|
}
|
|
|
|
// Mark original nodes as unused.
|
|
unusedNodes.push_back(node);
|
|
unusedNodes.push_back(top);
|
|
}
|
|
}
|
|
else if (nodeCategory == "subsurface_bsdf")
|
|
{
|
|
InputPtr radiusInput = node->getInput("radius");
|
|
if (radiusInput && radiusInput->getType() == "vector3")
|
|
{
|
|
GraphElementPtr graph = node->getAncestorOfType<GraphElement>();
|
|
NodePtr convertNode = graph->addNode("convert", graph->createValidChildName("convert"), "color3");
|
|
copyInputWithBindings(node, "radius", convertNode, "in");
|
|
radiusInput->setConnectedNode(convertNode);
|
|
radiusInput->setType("color3");
|
|
}
|
|
}
|
|
else if (nodeCategory == "switch")
|
|
{
|
|
// Upgrade switch nodes from 5 to 10 inputs, handling the fallback behavior for
|
|
// constant "which" values that were previously out of range.
|
|
InputPtr which = node->getInput("which");
|
|
if (which && which->hasValue())
|
|
{
|
|
auto whichValue = which->getValue();
|
|
if (whichValue->isA<int>() && whichValue->asA<int>() >= 5)
|
|
{
|
|
which->setValue(0);
|
|
}
|
|
else if (whichValue->isA<float>() && whichValue->asA<float>() >= 5)
|
|
{
|
|
which->setValue(0.0);
|
|
}
|
|
}
|
|
}
|
|
else if (nodeCategory == "swizzle")
|
|
{
|
|
InputPtr inInput = node->getInput("in");
|
|
const string sourceType = inInput ? inInput->getType() : "float";
|
|
if (CHANNEL_COUNT_MAP.count(sourceType) &&
|
|
CHANNEL_COUNT_MAP.count(node->getType()))
|
|
{
|
|
InputPtr channelsInput = node->getInput("channels");
|
|
string channelString = channelsInput ? channelsInput->getValueString() : EMPTY_STRING;
|
|
string destType = node->getType();
|
|
size_t sourceChannelCount = CHANNEL_COUNT_MAP.at(sourceType);
|
|
size_t destChannelCount = CHANNEL_COUNT_MAP.at(destType);
|
|
|
|
// Resolve the invalid case of having both a connection and a value
|
|
// by removing the value attribute.
|
|
if (inInput && inInput->hasValue())
|
|
{
|
|
if (inInput->hasNodeName() || inInput->hasNodeGraphString() || inInput->hasInterfaceName())
|
|
{
|
|
inInput->removeAttribute(ValueElement::VALUE_ATTRIBUTE);
|
|
}
|
|
}
|
|
|
|
// We convert to a constant node if "in" input is a constant value or does not exist:
|
|
bool convertToConstantNode = !inInput || inInput->hasValue();
|
|
// We also convert to a constant node if every destination
|
|
// channel is constant:
|
|
// eg: "ND_swizzle_color3_color3" node with
|
|
// "010" in the "channels" input.
|
|
if (!convertToConstantNode)
|
|
{
|
|
convertToConstantNode = true;
|
|
for (size_t i = 0; i < destChannelCount; i++)
|
|
{
|
|
if (i < channelString.size())
|
|
{
|
|
if (CHANNEL_CONSTANT_MAP.count(channelString[i]))
|
|
{
|
|
// Still in constant territory:
|
|
continue;
|
|
}
|
|
}
|
|
// Every other scenario: not constant
|
|
convertToConstantNode = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (convertToConstantNode)
|
|
{
|
|
// Replace swizzle with constant.
|
|
node->setCategory("constant");
|
|
if (node->hasNodeDefString())
|
|
{
|
|
node->setNodeDefString("ND_constant_" + node->getType());
|
|
}
|
|
string valueString = inInput ? inInput->getValueString() : "0";
|
|
StringVec origValueTokens = splitString(valueString, ARRAY_VALID_SEPARATORS);
|
|
StringVec newValueTokens;
|
|
for (size_t i = 0; i < destChannelCount; i++)
|
|
{
|
|
if (i < channelString.size())
|
|
{
|
|
if (CHANNEL_INDEX_MAP.count(channelString[i]))
|
|
{
|
|
size_t index = CHANNEL_INDEX_MAP.at(channelString[i]);
|
|
if (index < origValueTokens.size())
|
|
{
|
|
newValueTokens.push_back(origValueTokens[index]);
|
|
continue;
|
|
}
|
|
}
|
|
else if (CHANNEL_CONSTANT_MAP.count(channelString[i]))
|
|
{
|
|
newValueTokens.push_back(std::to_string(CHANNEL_CONSTANT_MAP.at(channelString[i])));
|
|
continue;
|
|
}
|
|
}
|
|
// Invalid channel name, or missing channel name:
|
|
newValueTokens.push_back(origValueTokens[0]);
|
|
}
|
|
InputPtr valueInput = node->addInput("value", node->getType());
|
|
valueInput->setValueString(joinStrings(newValueTokens, ", "));
|
|
// This is the last place we need to check for nullptr for inInput.
|
|
if (inInput)
|
|
{
|
|
node->removeInput(inInput->getName());
|
|
}
|
|
}
|
|
else if (destChannelCount == 1)
|
|
{
|
|
// Replace swizzle with extract.
|
|
node->setCategory("extract");
|
|
if (node->hasNodeDefString())
|
|
{
|
|
node->setNodeDefString("ND_extract_" + sourceType);
|
|
}
|
|
if (!channelString.empty() && CHANNEL_INDEX_MAP.count(channelString[0]))
|
|
{
|
|
node->setInputValue("index", (int) CHANNEL_INDEX_MAP.at(channelString[0]));
|
|
}
|
|
}
|
|
else if (sourceType != destType && std::find(CHANNEL_CONVERT_PATTERNS.begin(), CHANNEL_CONVERT_PATTERNS.end(),
|
|
std::make_pair(channelString, sourceChannelCount)) != CHANNEL_CONVERT_PATTERNS.end())
|
|
{
|
|
// Replace swizzle with convert.
|
|
node->setCategory("convert");
|
|
if (node->hasNodeDefString())
|
|
{
|
|
node->setNodeDefString("ND_convert_" + sourceType + "_" + destType);
|
|
}
|
|
}
|
|
else if (sourceChannelCount == 1)
|
|
{
|
|
// Replace swizzle with combine.
|
|
node->setCategory("combine" + std::to_string(destChannelCount));
|
|
if (node->hasNodeDefString())
|
|
{
|
|
node->setNodeDefString("ND_combine" + std::to_string(destChannelCount) + "_" + node->getType());
|
|
}
|
|
for (size_t i = 0; i < destChannelCount; i++)
|
|
{
|
|
InputPtr combineInInput = node->addInput(std::string("in") + std::to_string(i + 1), "float");
|
|
if (i < channelString.size() && CHANNEL_CONSTANT_MAP.count(channelString[i]))
|
|
{
|
|
combineInInput->setValue(CHANNEL_CONSTANT_MAP.at(channelString[i]));
|
|
}
|
|
else
|
|
{
|
|
copyInputWithBindings(node, inInput->getName(), node, combineInInput->getName());
|
|
}
|
|
}
|
|
node->removeInput(inInput->getName());
|
|
}
|
|
else
|
|
{
|
|
// Replace swizzle with separate and combine.
|
|
GraphElementPtr graph = node->getAncestorOfType<GraphElement>();
|
|
NodePtr separateNode = graph->addNode(std::string("separate") + std::to_string(sourceChannelCount),
|
|
graph->createValidChildName("separate"), MULTI_OUTPUT_TYPE_STRING);
|
|
int childIndex = graph->getChildIndex(node->getName());
|
|
if (childIndex != -1)
|
|
{
|
|
graph->setChildIndex(separateNode->getName(), childIndex);
|
|
}
|
|
node->setCategory("combine" + std::to_string(destChannelCount));
|
|
if (node->hasNodeDefString())
|
|
{
|
|
node->setNodeDefString("ND_combine" + std::to_string(destChannelCount) + "_" + node->getType());
|
|
}
|
|
for (size_t i = 0; i < destChannelCount; i++)
|
|
{
|
|
InputPtr combineInInput = node->addInput(std::string("in") + std::to_string(i + 1), "float");
|
|
if (i < channelString.size())
|
|
{
|
|
if (CHANNEL_INDEX_MAP.count(channelString[i]))
|
|
{
|
|
combineInInput->setConnectedNode(separateNode);
|
|
combineInInput->setOutputString(std::string("out") + channelString[i]);
|
|
continue;
|
|
}
|
|
else if (CHANNEL_CONSTANT_MAP.count(channelString[i]))
|
|
{
|
|
combineInInput->setValue(CHANNEL_CONSTANT_MAP.at(channelString[i]));
|
|
continue;
|
|
}
|
|
}
|
|
// Invalid channel name, or missing channel name:
|
|
combineInInput->setConnectedNode(separateNode);
|
|
combineInInput->setOutputString(inInput->isColorType() ? "outr" : "outx");
|
|
}
|
|
copyInputWithBindings(node, inInput->getName(), separateNode, "in");
|
|
node->removeInput(inInput->getName());
|
|
}
|
|
|
|
// Remove the channels input from the converted node.
|
|
if (channelsInput)
|
|
{
|
|
node->removeInput(channelsInput->getName());
|
|
}
|
|
}
|
|
}
|
|
else if (nodeCategory == "atan2")
|
|
{
|
|
InputPtr input1 = node->getInput("in1");
|
|
if (input1)
|
|
{
|
|
input1->setName("iny");
|
|
}
|
|
InputPtr input2 = node->getInput("in2");
|
|
if (input2)
|
|
{
|
|
input2->setName("inx");
|
|
}
|
|
}
|
|
else if (nodeCategory == "normalmap")
|
|
{
|
|
InputPtr space = node->getInput("space");
|
|
if (space && space->getValueString() == "object")
|
|
{
|
|
// Replace object-space normalmap with a graph.
|
|
GraphElementPtr graph = node->getAncestorOfType<GraphElement>();
|
|
NodePtr multiply = graph->addNode("multiply", graph->createValidChildName("multiply"), "vector3");
|
|
copyInputWithBindings(node, "in", multiply, "in1");
|
|
multiply->setInputValue("in2", 2.0f);
|
|
NodePtr subtract = graph->addNode("subtract", graph->createValidChildName("subtract"), "vector3");
|
|
subtract->addInput("in1", "vector3")->setConnectedNode(multiply);
|
|
subtract->setInputValue("in2", 1.0f);
|
|
node->setCategory("normalize");
|
|
vector<InputPtr> origInputs = node->getInputs();
|
|
for (InputPtr input : origInputs)
|
|
{
|
|
node->removeChild(input->getName());
|
|
}
|
|
node->addInput("in", "vector3")->setConnectedNode(subtract);
|
|
|
|
// Update nodedef name if present.
|
|
if (node->hasNodeDefString())
|
|
{
|
|
node->setNodeDefString("ND_normalize_vector3");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Clear tangent-space input.
|
|
node->removeInput("space");
|
|
|
|
// If the normal or tangent inputs are set and the bitangent input is not,
|
|
// the bitangent should be set to normalize(cross(N, T))
|
|
InputPtr normalInput = node->getInput("normal");
|
|
InputPtr tangentInput = node->getInput("tangent");
|
|
InputPtr bitangentInput = node->getInput("bitangent");
|
|
if ((normalInput || tangentInput) && !bitangentInput)
|
|
{
|
|
GraphElementPtr graph = node->getAncestorOfType<GraphElement>();
|
|
NodePtr crossNode = graph->addNode("crossproduct", graph->createValidChildName("normalmap_cross"), "vector3");
|
|
copyInputWithBindings(node, "normal", crossNode, "in1");
|
|
copyInputWithBindings(node, "tangent", crossNode, "in2");
|
|
|
|
NodePtr normalizeNode = graph->addNode("normalize", graph->createValidChildName("normalmap_cross_norm"), "vector3");
|
|
normalizeNode->addInput("in", "vector3")->setConnectedNode(crossNode);
|
|
|
|
node->addInput("bitangent", "vector3")->setConnectedNode(normalizeNode);
|
|
}
|
|
|
|
// Update nodedef name if present.
|
|
if (node->getNodeDefString() == "ND_normalmap")
|
|
{
|
|
node->setNodeDefString("ND_normalmap_float");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (NodePtr node : unusedNodes)
|
|
{
|
|
node->getParent()->removeChild(node->getName());
|
|
}
|
|
|
|
minorVersion = 39;
|
|
}
|
|
|
|
std::pair<int, int> upgradedVersion(majorVersion, minorVersion);
|
|
if (upgradedVersion == expectedVersion)
|
|
{
|
|
setVersionIntegers(majorVersion, minorVersion);
|
|
}
|
|
}
|
|
|
|
MATERIALX_NAMESPACE_END
|