diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc index 43a046cc9292..a8829ddf92fb 100644 --- a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc @@ -15,12 +15,8 @@ NodeItem BSDFPrincipledNodeParser::compute() NodeItem base_color = get_input_value("Base Color"); NodeItem subsurface = get_input_value("Subsurface"); - NodeItem subsurface_radius = empty(); - NodeItem subsurface_color = empty(); - if (subsurface != zero) { - subsurface_radius = get_input_value("Subsurface Radius"); - subsurface_color = get_input_value("Subsurface Color"); - } + NodeItem subsurface_radius = get_input_value("Subsurface Radius"); + NodeItem subsurface_color = get_input_value("Subsurface Color"); NodeItem metallic = get_input_value("Metallic"); NodeItem specular = get_input_value("Specular"); @@ -70,7 +66,7 @@ NodeItem BSDFPrincipledNodeParser::compute() /* Creating standard_surface */ NodeItem res = create_node("standard_surface", "surfaceshader"); res.set_input("base", 1.0, "float"); - res.set_input("base_color", base_color.to_color3()); + res.set_input("base_color", base_color, NodeItem::Type::Color3); res.set_input("diffuse_roughness", roughness); if (normal) { res.set_input("normal", normal); @@ -85,7 +81,7 @@ NodeItem BSDFPrincipledNodeParser::compute() if (specular != zero) { res.set_input("specular", specular); - res.set_input("specular_color", base_color.to_color3()); + res.set_input("specular_color", base_color, NodeItem::Type::Color3); res.set_input("specular_roughness", roughness); res.set_input("specular_IOR", ior); if (anisotropic) { @@ -98,28 +94,26 @@ NodeItem BSDFPrincipledNodeParser::compute() if (transmission != zero) { res.set_input("transmission", transmission); - res.set_input("transmission_color", base_color.to_color3()); + res.set_input("transmission_color", base_color, NodeItem::Type::Color3); res.set_input("transmission_extra_roughness", transmission_roughness); } - if (subsurface != zero) { - res.set_input("subsurface", subsurface); - res.set_input("subsurface_color", subsurface_color); - res.set_input("subsurface_radius", subsurface_radius); - if (anisotropic) { - res.set_input("subsurface_anisotropy", anisotropic); - } + res.set_input("subsurface", subsurface); + res.set_input("subsurface_color", subsurface_color); + res.set_input("subsurface_radius", subsurface_radius); + if (anisotropic) { + res.set_input("subsurface_anisotropy", anisotropic); } if (sheen != zero) { res.set_input("sheen", sheen); - res.set_input("sheen_color", base_color.to_color3()); + res.set_input("sheen_color", base_color, NodeItem::Type::Color3); res.set_input("sheen_roughness", roughness); } if (clearcoat != zero) { res.set_input("coat", clearcoat); - res.set_input("coat_color", base_color.to_color3()); + res.set_input("coat_color", base_color, NodeItem::Type::Color3); res.set_input("coat_roughness", clearcoat_roughness); res.set_input("coat_IOR", ior); if (anisotropic) { @@ -131,7 +125,7 @@ NodeItem BSDFPrincipledNodeParser::compute() if (emission != zero) { res.set_input("emission", emission_strength); - res.set_input("emission_color", emission); + res.set_input("emission_color", emission, NodeItem::Type::Color3); } return res; diff --git a/source/blender/nodes/shader/materialx/nodes/math.cc b/source/blender/nodes/shader/materialx/nodes/math.cc index cbd5d6fd023e..598c80a2faee 100644 --- a/source/blender/nodes/shader/materialx/nodes/math.cc +++ b/source/blender/nodes/shader/materialx/nodes/math.cc @@ -2,8 +2,8 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "node_parser.h" #include "../material.h" +#include "node_parser.h" namespace blender::nodes::materialx { @@ -109,10 +109,10 @@ NodeItem MathNodeParser::compute() res = x.max(y); break; case NODE_MATH_LESS_THAN: - res = x.if_else("<", y, value(1.0f), value(0.0f)); + res = x.if_else(NodeItem::CompareOp::Less, y, value(1.0f), value(0.0f)); break; case NODE_MATH_GREATER_THAN: - res = x.if_else(">", y, value(1.0f), value(0.0f)); + res = x.if_else(NodeItem::CompareOp::Greater, y, value(1.0f), value(0.0f)); break; case NODE_MATH_MODULO: res = x % y; @@ -138,7 +138,7 @@ NodeItem MathNodeParser::compute() CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op); break; case NODE_MATH_COMPARE: - res = z.if_else("<", (x - y).abs(), value(1.0f), value(0.0f)); + res = z.if_else(NodeItem::CompareOp::Less, (x - y).abs(), value(1.0f), value(0.0f)); break; case NODE_MATH_MULTIPLY_ADD: res = x * y + z; diff --git a/source/blender/nodes/shader/materialx/nodes/node_item.cc b/source/blender/nodes/shader/materialx/nodes/node_item.cc index f3cf3779058c..5e6df0555fce 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_item.cc +++ b/source/blender/nodes/shader/materialx/nodes/node_item.cc @@ -12,73 +12,6 @@ namespace blender::nodes::materialx { NodeItem::NodeItem(MaterialX::GraphElement *graph) : graph_(graph) {} -NodeItem NodeItem::empty() const -{ - return NodeItem(graph_); -} - -void NodeItem::set_input(const std::string &name, - const NodeItem &item, - const std::string &output_name) -{ - if (item.value) { - set_input(name, item.value); - } - else if (item.node) { - set_input(name, item.node, output_name); - } - else { - CLOG_WARN(LOG_MATERIALX_SHADER, "Empty item to input: %s", name.c_str()); - } -} - -void NodeItem::set_input(const std::string &name, const MaterialX::ValuePtr value) -{ - std::string mx_type = value->getTypeString(); - if (mx_type == "float") { - set_input(name, value->asA(), mx_type); - } - else if (mx_type == "integer") { - set_input(name, value->asA(), mx_type); - } - else if (mx_type == "vector2") { - set_input(name, value->asA(), mx_type); - } - else if (mx_type == "vector3") { - set_input(name, value->asA(), mx_type); - } - else if (mx_type == "vector4") { - set_input(name, value->asA(), mx_type); - } - else if (mx_type == "color3") { - set_input(name, value->asA(), mx_type); - } - else if (mx_type == "color4") { - set_input(name, value->asA(), mx_type); - } - else if (mx_type == "string") { - set_input(name, value->asA(), mx_type); - } - else { - BLI_assert_unreachable(); - } -} - -void NodeItem::set_input(const std::string &name, - const MaterialX::NodePtr node, - const std::string &output_name) -{ - this->node->setConnectedNode(name, node); - if (output_name != "") { - this->node->setConnectedOutput("in1", node->getOutput(output_name)); - } -} - -void NodeItem::add_output(const std::string &name, const std::string &mx_type) -{ - node->addOutput(name, mx_type); -} - NodeItem::operator bool() const { return value || node; @@ -135,32 +68,13 @@ bool NodeItem::operator==(const NodeItem &other) const return false; } - std::string mx_type; - auto val1 = value; - auto val2 = other.value; - if (!adjust_types(val1, val2, mx_type)) { + NodeItem item1 = *this; + NodeItem item2 = other; + Type to_type = adjust_types(item1, item2); + if (to_type == Type::Empty) { return false; } - if (mx_type == "float") { - return val1->asA() == val2->asA(); - } - if (mx_type == "color3") { - return val1->asA() == val2->asA(); - } - if (mx_type == "color4") { - return val1->asA() == val2->asA(); - } - if (mx_type == "vector2") { - return val1->asA() == val2->asA(); - } - if (mx_type == "vector3") { - return val1->asA() == val2->asA(); - } - if (mx_type == "vector4") { - return val1->asA() == val2->asA(); - } - - return false; + return item1.value->getValueString() == item2.value->getValueString(); } bool NodeItem::operator!=(const NodeItem &other) const @@ -197,98 +111,46 @@ NodeItem NodeItem::dotproduct(const NodeItem &other) const { NodeItem d = arithmetic(other, "dotproduct", [](float a, float b) { return a * b; }); if (d.value) { - std::string mx_type = d.type(); + Type mx_type = d.type(); float f = 0.0f; - if (mx_type == "float") { - f = value->asA(); - } - else if (mx_type == "color3") { - auto v = value->asA(); - f = v[0] + v[1] + v[2]; - } - else if (mx_type == "color4") { - auto v = value->asA(); - f = v[0] + v[1] + v[2] + v[3]; - } - else if (mx_type == "vector2") { - auto v = value->asA(); - f = v[0] + v[1]; - } - else if (mx_type == "vector3") { - auto v = value->asA(); - f = v[0] + v[1] + v[2]; - } - else if (mx_type == "vector4") { - auto v = value->asA(); - f = v[0] + v[1] + v[2] + v[3]; - } - else { - BLI_assert_unreachable(); + switch (mx_type) { + case Type::Float: { + f = value->asA(); + break; + } + case Type::Vector2: { + auto v = value->asA(); + f = v[0] + v[1]; + break; + } + case Type::Vector3: { + auto v = value->asA(); + f = v[0] + v[1] + v[2]; + break; + } + case Type::Vector4: { + auto v = value->asA(); + f = v[0] + v[1] + v[2] + v[3]; + break; + } + case Type::Color3: { + auto v = value->asA(); + f = v[0] + v[1] + v[2]; + break; + } + case Type::Color4: { + auto v = value->asA(); + f = v[0] + v[1] + v[2] + v[3]; + break; + } + default: + BLI_assert_unreachable(); } d.value = MaterialX::Value::createValue(f); } return d; } -NodeItem NodeItem::if_else(const std::string &condition, - const NodeItem &other, - const NodeItem &if_val, - const NodeItem &else_val) const -{ - if (condition == "<") { - return other.if_else(">", *this, else_val, if_val); - } - if (condition == "<=") { - return other.if_else(">=", *this, else_val, if_val); - } - if (condition == "!=") { - return if_else("==", other, else_val, if_val); - } - - NodeItem res = empty(); - if (type() != "float" || other.type() != "float") { - return res; - } - - auto val1 = if_val; - auto val2 = else_val; - std::string mx_type; - if (!adjust_types(val1, val2, mx_type)) { - return res; - } - - std::function func = nullptr; - std::string mx_category; - if (condition == ">") { - mx_category = "ifgreater"; - func = [](float a, float b) { return a > b; }; - } - else if (condition == ">=") { - mx_category = "ifgreatereq"; - func = [](float a, float b) { return a >= b; }; - } - else if (condition == "==") { - mx_category = "ifequal"; - func = [](float a, float b) { return a == b; }; - } - else { - BLI_assert_unreachable(); - } - - if (value && other.value) { - res = func(value->asA(), other.value->asA()) ? val1 : val2; - } - else { - res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, mx_type); - res.set_input("value1", *this); - res.set_input("value2", other); - res.set_input("in1", val1); - res.set_input("in2", val2); - } - - return res; -} - NodeItem NodeItem::blend(const NodeItem &a, const NodeItem &b) const { return (val(1.0f) - *this) * a + *this * b; @@ -374,247 +236,528 @@ NodeItem NodeItem::exp() const return arithmetic("exp", [](float a) { return std::expf(a); }); } -NodeItem NodeItem::to_color3() const +NodeItem NodeItem::convert(Type to_type) const { - std::string mx_type = type(); + Type from_type = type(); + if (from_type == to_type) { + return *this; + } + if (!is_arithmetic(from_type) || !is_arithmetic(to_type)) { + return empty(); + } + + if (to_type == Type::Float) { + /* TODO: Convert to float, should be used */ + } + + /* Converting types which requires > 1 iteration */ + switch (from_type) { + case Type::Vector2: + switch (to_type) { + case Type::Vector4: + return convert(Type::Vector3).convert(Type::Vector4); + case Type::Color3: + return convert(Type::Vector3).convert(Type::Color3); + case Type::Color4: + return convert(Type::Vector3).convert(Type::Color3).convert(Type::Color4); + default: + break; + } + break; + case Type::Vector3: + switch (to_type) { + case Type::Color4: + return convert(Type::Color3).convert(Type::Color4); + default: + break; + } + break; + case Type::Vector4: + switch (to_type) { + case Type::Vector2: + return convert(Type::Vector3).convert(Type::Vector2); + case Type::Color3: + return convert(Type::Vector3).convert(Type::Color3); + default: + break; + } + break; + case Type::Color3: + switch (to_type) { + case Type::Vector2: + return convert(Type::Vector3).convert(Type::Vector2); + case Type::Vector4: + return convert(Type::Vector3).convert(Type::Vector4); + default: + break; + } + break; + case Type::Color4: + switch (to_type) { + case Type::Vector2: + return convert(Type::Vector4).convert(Type::Vector3).convert(Type::Vector2); + case Type::Vector3: + return convert(Type::Vector4).convert(Type::Vector3); + default: + break; + } + break; + default: + break; + } + + /* Converting 1 iteration types */ NodeItem res = empty(); if (value) { - MaterialX::Color3 c; - if (mx_type == "float") { - float v = value->asA(); - c = {v, v, v}; + switch (from_type) { + case Type::Float: { + float v = value->asA(); + switch (to_type) { + case Type::Vector2: + res.value = MaterialX::Value::createValue({v, v}); + break; + case Type::Vector3: + res.value = MaterialX::Value::createValue({v, v, v}); + break; + case Type::Vector4: + res.value = MaterialX::Value::createValue({v, v, v, 1.0f}); + break; + case Type::Color3: + res.value = MaterialX::Value::createValue({v, v, v}); + break; + case Type::Color4: + res.value = MaterialX::Value::createValue({v, v, v, 1.0f}); + break; + default: + BLI_assert_unreachable(); + } + break; + } + case Type::Vector2: { + auto v = value->asA(); + switch (to_type) { + case Type::Vector3: + res.value = MaterialX::Value::createValue({v[0], v[1], 0.0f}); + break; + default: + BLI_assert_unreachable(); + } + break; + } + case Type::Vector3: { + auto v = value->asA(); + switch (to_type) { + case Type::Vector2: + res.value = MaterialX::Value::createValue({v[0], v[1]}); + break; + case Type::Vector4: + res.value = MaterialX::Value::createValue( + {v[0], v[1], v[2], 0.0f}); + break; + case Type::Color3: + res.value = MaterialX::Value::createValue({v[0], v[1], v[2]}); + break; + default: + BLI_assert_unreachable(); + } + break; + } + case Type::Vector4: { + auto v = value->asA(); + switch (to_type) { + case Type::Vector3: + res.value = MaterialX::Value::createValue({v[0], v[1], v[2]}); + break; + case Type::Color4: + res.value = MaterialX::Value::createValue({v[0], v[1], v[2], v[3]}); + break; + default: + BLI_assert_unreachable(); + } + break; + } + case Type::Color3: { + auto v = value->asA(); + switch (to_type) { + case Type::Vector3: + res.value = MaterialX::Value::createValue({v[0], v[1], v[2]}); + break; + case Type::Color4: + res.value = MaterialX::Value::createValue({v[0], v[1], v[2], 1.0f}); + break; + default: + BLI_assert_unreachable(); + } + break; + } + case Type::Color4: { + auto v = value->asA(); + switch (to_type) { + case Type::Vector4: + res.value = MaterialX::Value::createValue( + {v[0], v[1], v[2], v[3]}); + break; + case Type::Color3: + res.value = MaterialX::Value::createValue({v[0], v[1], v[2]}); + break; + default: + BLI_assert_unreachable(); + } + break; + } + default: + BLI_assert_unreachable(); } - else if (mx_type == "color3") { - auto v = value->asA(); - c = {v[0], v[1], v[2]}; - } - else if (mx_type == "color4") { - auto v = value->asA(); - c = {v[0], v[1], v[2]}; - } - else if (mx_type == "vector3") { - auto v = value->asA(); - c = {v[0], v[1], v[2]}; - } - else if (mx_type == "vector4") { - auto v = value->asA(); - c = {v[0], v[1], v[2]}; - } - else { - return res; - } - res.value = MaterialX::Value::createValue(c); } - else if (node) { - if (mx_type != "color3") { - return res; - } - res.node = node; + else { + res.node = graph_->addNode("convert", MaterialX::EMPTY_STRING, type(to_type)); + res.set_input("in", *this); } return res; } -bool NodeItem::is_numeric() const +NodeItem NodeItem::if_else(CompareOp op, + const NodeItem &other, + const NodeItem &if_val, + const NodeItem &else_val) const { - std::string t = type(); - return ELEM(t, "float", "color3", "color4", "vector2", "vector3", "vector4"); -} - -std::string NodeItem::type() const -{ - return value ? value->getTypeString() : node->getType(); -} - -NodeItem NodeItem::arithmetic(const std::string &mx_category, - std::function func) const -{ - if (!is_numeric()) { - return empty(); + switch (op) { + case CompareOp::Less: + return other.if_else(CompareOp::Greater, *this, else_val, if_val); + case CompareOp::LessEq: + return other.if_else(CompareOp::GreaterEq, *this, else_val, if_val); + case CompareOp::NotEq: + return if_else(CompareOp::Eq, other, else_val, if_val); + default: + break; } - std::string t = value ? value->getTypeString() : node->getType(); - NodeItem res(graph_); - if (value) { - if (t == "float") { - float v = value->asA(); - res.value = MaterialX::Value::createValue(func(v)); - } - else if (t == "color3") { - auto v = value->asA(); - res.value = MaterialX::Value::createValue( - {func(v[0]), func(v[1]), func(v[2])}); - } - else if (t == "color4") { - auto v = value->asA(); - res.value = MaterialX::Value::createValue( - {func(v[0]), func(v[1]), func(v[2]), func(v[3])}); - } - else if (t == "vector2") { - auto v = value->asA(); - res.value = MaterialX::Value::createValue({func(v[0]), func(v[1])}); - } - else if (t == "vector3") { - auto v = value->asA(); - res.value = MaterialX::Value::createValue( - {func(v[0]), func(v[1]), func(v[2])}); - } - else if (t == "vector4") { - auto v = value->asA(); - res.value = MaterialX::Value::createValue( - {func(v[0]), func(v[1]), func(v[2]), func(v[3])}); - } - else { + NodeItem res = empty(); + if (type() != Type::Float || other.type() != Type::Float) { + return res; + } + + auto item1 = if_val; + auto item2 = else_val; + Type to_type = adjust_types(item1, item2); + if (to_type == Type::Empty) { + return res; + } + + std::function func = nullptr; + std::string mx_category; + switch (op) { + case CompareOp::Greater: + mx_category = "ifgreater"; + func = [](float a, float b) { return a > b; }; + break; + case CompareOp::GreaterEq: + mx_category = "ifgreatereq"; + func = [](float a, float b) { return a >= b; }; + break; + case CompareOp::Eq: + mx_category = "ifequal"; + func = [](float a, float b) { return a == b; }; + break; + default: BLI_assert_unreachable(); + } + + if (value && other.value) { + res = func(value->asA(), other.value->asA()) ? item1 : item2; + } + else { + res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, type(to_type)); + res.set_input("value1", *this); + res.set_input("value2", other); + res.set_input("in1", item1); + res.set_input("in2", item2); + } + + return res; +} + +NodeItem NodeItem::empty() const +{ + return NodeItem(graph_); +} + +NodeItem::Type NodeItem::type() const +{ + if (value) { + return type(value->getTypeString()); + } + if (node) { + return type(node->getType()); + } + return Type::Empty; +} + +void NodeItem::set_input(const std::string &name, + const NodeItem &item, + const std::string &output_name) +{ + if (item.value) { + Type item_type = item.type(); + std::string mx_type = type(item_type); + switch (item_type) { + case Type::String: + set_input(name, item.value->asA(), mx_type); + break; + case Type::Integer: + set_input(name, item.value->asA(), mx_type); + break; + case Type::Float: + set_input(name, item.value->asA(), mx_type); + break; + case Type::Vector2: + set_input(name, item.value->asA(), mx_type); + break; + case Type::Vector3: + set_input(name, item.value->asA(), mx_type); + break; + case Type::Vector4: + set_input(name, item.value->asA(), mx_type); + break; + case Type::Color3: + set_input(name, item.value->asA(), mx_type); + break; + case Type::Color4: + set_input(name, item.value->asA(), mx_type); + break; + default: + BLI_assert_unreachable(); + } + } + else if (item.node) { + node->setConnectedNode(name, item.node); + if (output_name != "") { + node->setConnectedOutput(name, item.node->getOutput(output_name)); } } else { - res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, t); + CLOG_WARN(LOG_MATERIALX_SHADER, "Empty item to input: %s", name.c_str()); + } +} + +void NodeItem::set_input(const std::string &name, + const NodeItem &item, + Type in_type, + const std::string &output_name) +{ + set_input(name, item.convert(in_type), output_name); +} + +void NodeItem::add_output(const std::string &name, Type out_type) +{ + node->addOutput(name, type(out_type)); +} + +NodeItem::Type NodeItem::type(const std::string &type_str) +{ + if (type_str == "string") { + return Type::String; + } + if (type_str == "integer") { + return Type::Integer; + } + if (type_str == "float") { + return Type::Float; + } + if (type_str == "vector2") { + return Type::Vector2; + } + if (type_str == "vector3") { + return Type::Vector3; + } + if (type_str == "vector4") { + return Type::Vector4; + } + if (type_str == "color3") { + return Type::Color3; + } + if (type_str == "color4") { + return Type::Color4; + } + return Type::Other; +} + +std::string NodeItem::type(Type type) +{ + switch (type) { + case Type::String: + return "string"; + case Type::Integer: + return "integer"; + case Type::Float: + return "float"; + case Type::Vector2: + return "vector2"; + case Type::Vector3: + return "vector3"; + case Type::Vector4: + return "vector4"; + case Type::Color3: + return "color3"; + case Type::Color4: + return "color4"; + default: + break; + } + return ""; +} + +bool NodeItem::is_arithmetic(Type type) +{ + return type >= Type::Float; +} + +NodeItem::Type NodeItem::adjust_types(NodeItem &item1, NodeItem &item2) +{ + Type t1 = item1.type(); + Type t2 = item2.type(); + if (t1 == t2) { + return t1; + } + if (!is_arithmetic(t1) || !is_arithmetic(t2)) { + return Type::Empty; + } + if (t1 < t2) { + item1 = item1.convert(t2); + return t2; + } + else { + item2 = item2.convert(t1); + return t1; + } +} + +bool NodeItem::is_arithmetic() const +{ + return is_arithmetic(type()); +} + +NodeItem NodeItem::arithmetic(const std::string &category, std::function func) const +{ + NodeItem res = empty(); + Type type = this->type(); + if (!is_arithmetic(type)) { + return res; + } + + if (value) { + switch (type) { + case Type::Float: { + float v = value->asA(); + res.value = MaterialX::Value::createValue(func(v)); + break; + } + case Type::Color3: { + auto v = value->asA(); + res.value = MaterialX::Value::createValue( + {func(v[0]), func(v[1]), func(v[2])}); + break; + } + case Type::Color4: { + auto v = value->asA(); + res.value = MaterialX::Value::createValue( + {func(v[0]), func(v[1]), func(v[2]), func(v[3])}); + break; + } + case Type::Vector2: { + auto v = value->asA(); + res.value = MaterialX::Value::createValue({func(v[0]), func(v[1])}); + } + case Type::Vector3: { + auto v = value->asA(); + res.value = MaterialX::Value::createValue( + {func(v[0]), func(v[1]), func(v[2])}); + break; + } + case Type::Vector4: { + auto v = value->asA(); + res.value = MaterialX::Value::createValue( + {func(v[0]), func(v[1]), func(v[2]), func(v[3])}); + break; + } + default: + BLI_assert_unreachable(); + } + } + else { + /* TODO: Some of math functions (sin, cos, ...) doesn't work with Color types, + * we have to convert to Vector */ + res.node = graph_->addNode(category, MaterialX::EMPTY_STRING, this->type(type)); res.set_input("in", *this); } return res; } NodeItem NodeItem::arithmetic(const NodeItem &other, - const std::string &mx_category, + const std::string &category, std::function func) const { NodeItem res = empty(); - if (!is_numeric() || !other.is_numeric()) { + NodeItem item1 = *this; + NodeItem item2 = other; + Type to_type = adjust_types(item1, item2); + if (to_type == Type::Empty) { return res; } - std::string mx_type; if (value && other.value) { - auto val1 = value; - auto val2 = other.value; - if (!adjust_types(val1, val2, mx_type)) { - return res; - } - - if (mx_type == "float") { - float v1 = val1->asA(); - float v2 = val2->asA(); - res.value = MaterialX::Value::createValue(func(v1, v2)); - } - else if (mx_type == "color3") { - auto v1 = val1->asA(); - auto v2 = val2->asA(); - res.value = MaterialX::Value::createValue( - {func(v1[0], v2[0]), func(v1[1], v2[1]), func(v1[2], v2[2])}); - } - else if (mx_type == "color4") { - auto v1 = val1->asA(); - auto v2 = val2->asA(); - res.value = MaterialX::Value::createValue( - {func(v1[0], v2[0]), func(v1[1], v2[1]), func(v1[2], v2[2]), func(v1[3], v2[3])}); - } - else if (mx_type == "vector2") { - auto v1 = val1->asA(); - auto v2 = val2->asA(); - res.value = MaterialX::Value::createValue( - {func(v1[0], v2[0]), func(v1[1], v2[1])}); - } - else if (mx_type == "vector3") { - auto v1 = val1->asA(); - auto v2 = val2->asA(); - res.value = MaterialX::Value::createValue( - {func(v1[0], v2[0]), func(v1[1], v2[1]), func(v1[2], v2[2])}); - } - else if (mx_type == "vector4") { - auto v1 = val1->asA(); - auto v2 = val2->asA(); - res.value = MaterialX::Value::createValue( - {func(v1[0], v2[0]), func(v1[1], v2[1]), func(v1[2], v2[2]), func(v1[3], v2[3])}); - } - else { - BLI_assert_unreachable(); + switch (to_type) { + case Type::Float: { + float v1 = item1.value->asA(); + float v2 = item2.value->asA(); + res.value = MaterialX::Value::createValue(func(v1, v2)); + break; + } + case Type::Color3: { + auto v1 = item1.value->asA(); + auto v2 = item2.value->asA(); + res.value = MaterialX::Value::createValue( + {func(v1[0], v2[0]), func(v1[1], v2[1]), func(v1[2], v2[2])}); + break; + } + case Type::Color4: { + auto v1 = item1.value->asA(); + auto v2 = item2.value->asA(); + res.value = MaterialX::Value::createValue( + {func(v1[0], v2[0]), func(v1[1], v2[1]), func(v1[2], v2[2]), func(v1[3], v2[3])}); + break; + } + case Type::Vector2: { + auto v1 = item1.value->asA(); + auto v2 = item2.value->asA(); + res.value = MaterialX::Value::createValue( + {func(v1[0], v2[0]), func(v1[1], v2[1])}); + break; + } + case Type::Vector3: { + auto v1 = item1.value->asA(); + auto v2 = item2.value->asA(); + res.value = MaterialX::Value::createValue( + {func(v1[0], v2[0]), func(v1[1], v2[1]), func(v1[2], v2[2])}); + break; + } + case Type::Vector4: { + auto v1 = item1.value->asA(); + auto v2 = item2.value->asA(); + res.value = MaterialX::Value::createValue( + {func(v1[0], v2[0]), func(v1[1], v2[1]), func(v1[2], v2[2]), func(v1[3], v2[3])}); + break; + } + default: + BLI_assert_unreachable(); } } else { - auto val1 = *this; - auto val2 = other; - if (!adjust_types(val1, val2, mx_type)) { - return res; - } - - res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, mx_type); - res.set_input("in1", val1); - res.set_input("in2", val2); + res.node = graph_->addNode(category, MaterialX::EMPTY_STRING, type(to_type)); + res.set_input("in1", item1); + res.set_input("in2", item2); } return res; } -MaterialX::ValuePtr NodeItem::float_to_type(float v, std::string mx_type) -{ - if (mx_type == "float") { - return MaterialX::Value::createValue(v); - } - if (mx_type == "color3") { - return MaterialX::Value::createValue({v, v, v}); - } - if (mx_type == "color4") { - return MaterialX::Value::createValue({v, v, v, 1.0f}); - } - if (mx_type == "vector2") { - return MaterialX::Value::createValue({v, v}); - } - if (mx_type == "vector3") { - return MaterialX::Value::createValue({v, v, v}); - } - if (mx_type == "vector4") { - return MaterialX::Value::createValue({v, v, v, 1.0f}); - } - - BLI_assert_unreachable(); - return nullptr; -} - -bool NodeItem::adjust_types(MaterialX::ValuePtr &val1, - MaterialX::ValuePtr &val2, - std::string &mx_type) -{ - std::string t1 = val1->getTypeString(); - std::string t2 = val2->getTypeString(); - if (t1 != t2) { - if (t1 == "float") { - val1 = float_to_type(val1->asA(), t2); - mx_type = t2; - } - else if (t2 == "float") { - val2 = float_to_type(val2->asA(), t1); - mx_type = t1; - } - else { - return false; - } - } - else { - mx_type = t1; - } - return true; -} - -bool NodeItem::adjust_types(NodeItem &val1, NodeItem &val2, std::string &mx_type) -{ - std::string t1 = val1.type(); - std::string t2 = val2.type(); - if (t1 != t2) { - if (val1.value && t1 == "float") { - val1.value = float_to_type(val1.value->asA(), t2); - mx_type = t2; - } - else if (val2.value && t2 == "float") { - val2.value = float_to_type(val2.value->asA(), t1); - mx_type = t1; - } - else { - return false; - } - } - else { - mx_type = t1; - } - return true; -} - } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/node_item.h b/source/blender/nodes/shader/materialx/nodes/node_item.h index 2f746cda36b7..466ce42b954d 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_item.h +++ b/source/blender/nodes/shader/materialx/nodes/node_item.h @@ -9,6 +9,21 @@ namespace blender::nodes::materialx { class NodeItem { + public: + enum class Type { + Empty = 0, + Other, /* For MaterialX types like: surfaceshader, bsdf, edf, ...*/ + String, + Integer, + Float, + Vector2, + Vector3, + Vector4, + Color3, + Color4 + }; + enum class CompareOp { Less = 0, LessEq, Eq, GreaterEq, Greater, NotEq }; + public: MaterialX::ValuePtr value; MaterialX::NodePtr node; @@ -20,20 +35,7 @@ class NodeItem { NodeItem(MaterialX::GraphElement *graph); ~NodeItem() = default; - NodeItem empty() const; - template NodeItem val(const T &data) const; - - template - void set_input(const std::string &name, const T &value, const std::string &mx_type); - void set_input(const std::string &name, - const NodeItem &item, - const std::string &output_name = ""); - void set_input(const std::string &name, const MaterialX::ValuePtr value); - void set_input(const std::string &name, - const MaterialX::NodePtr node, - const std::string &output_name = ""); - void add_output(const std::string &name, const std::string &mx_type); - + /* Operators */ operator bool() const; NodeItem operator+(const NodeItem &other) const; NodeItem operator-(const NodeItem &other) const; @@ -45,16 +47,13 @@ class NodeItem { bool operator==(const NodeItem &other) const; bool operator!=(const NodeItem &other) const; + /* Math functions */ NodeItem abs() const; NodeItem floor() const; NodeItem ceil() const; NodeItem min(const NodeItem &other) const; NodeItem max(const NodeItem &other) const; NodeItem dotproduct(const NodeItem &other) const; - NodeItem if_else(const std::string &condition, - const NodeItem &other, - const NodeItem &if_val, - const NodeItem &else_val) const; NodeItem blend(const NodeItem &a, const NodeItem &b) const; NodeItem clamp(const NodeItem &min_val, const NodeItem &max_val) const; NodeItem clamp(float min_val = 0.0f, float max_val = 1.0f) const; @@ -72,22 +71,40 @@ class NodeItem { NodeItem sqrt() const; NodeItem sign() const; NodeItem exp() const; + NodeItem convert(Type to_type) const; + NodeItem if_else(CompareOp op, + const NodeItem &other, + const NodeItem &if_val, + const NodeItem &else_val) const; - NodeItem to_color3() const; - bool is_numeric() const; - std::string type() const; + /* Useful functions */ + NodeItem empty() const; + template NodeItem val(const T &data) const; + Type type() const; + + /* Functions to set input and output */ + template + void set_input(const std::string &in_name, const T &value, const std::string &in_type); + void set_input(const std::string &in_name, + const NodeItem &item, + const std::string &out_name = ""); + void set_input(const std::string &in_name, + const NodeItem &item, + Type in_type, + const std::string &out_name = ""); + void add_output(const std::string &in_name, Type out_type); private: - NodeItem arithmetic(const std::string &mx_category, std::function func) const; + static Type type(const std::string &type_str); + static std::string type(Type type); + static bool is_arithmetic(Type type); + static Type adjust_types(NodeItem &item1, NodeItem &item2); + + bool is_arithmetic() const; + NodeItem arithmetic(const std::string &category, std::function func) const; NodeItem arithmetic(const NodeItem &other, - const std::string &mx_category, + const std::string &category, std::function func) const; - static MaterialX::ValuePtr float_to_type(float v, std::string mx_type); - /* Functions for adjusting values to make equal types */ - static bool adjust_types(MaterialX::ValuePtr &val1, - MaterialX::ValuePtr &val2, - std::string &mx_type); - static bool adjust_types(NodeItem &val1, NodeItem &val2, std::string &mx_type); }; template NodeItem NodeItem::val(const T &data) const @@ -98,9 +115,9 @@ template NodeItem NodeItem::val(const T &data) const } template -void NodeItem::set_input(const std::string &name, const T &value, const std::string &mx_type) +void NodeItem::set_input(const std::string &in_name, const T &value, const std::string &in_type) { - node->setInputValue(name, value, mx_type); + node->setInputValue(in_name, value, in_type); } } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/node_parser.cc b/source/blender/nodes/shader/materialx/nodes/node_parser.cc index 0b7c6e82ad60..187590c7332f 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_parser.cc +++ b/source/blender/nodes/shader/materialx/nodes/node_parser.cc @@ -14,7 +14,11 @@ NodeParser::NodeParser(MaterialX::GraphElement *graph, const Material *material, const bNode *node, const bNodeSocket *socket_out) - : graph_(graph), depsgraph_(depsgraph), material_(material), node_(node), socket_out_(socket_out) + : graph_(graph), + depsgraph_(depsgraph), + material_(material), + node_(node), + socket_out_(socket_out) { } @@ -74,17 +78,20 @@ NodeItem NodeParser::get_input_default(const bNodeSocket &socket) case SOCK_FLOAT: { float v = socket.default_value_typed()->value; res.value = MaterialX::Value::createValue(v); - } break; + break; + } case SOCK_VECTOR: { const float *v = socket.default_value_typed()->value; res.value = MaterialX::Value::createValue( MaterialX::Vector3(v[0], v[1], v[2])); - } break; + break; + } case SOCK_RGBA: { const float *v = socket.default_value_typed()->value; res.value = MaterialX::Value::createValue( MaterialX::Color4(v[0], v[1], v[2], v[3])); - } break; + break; + } default: { CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported socket type: %d", socket.type); } @@ -115,7 +122,7 @@ NodeItem NodeParser::get_input_link(const bNodeSocket &socket) /* Checking if node was already computed */ res.node = graph_->getNode(node_name(from_node, link->fromsock)); if (res.node) { - return res; + return res; } /* Computing from_node with required NodeParser object */ diff --git a/source/blender/nodes/shader/materialx/nodes/output_material.cc b/source/blender/nodes/shader/materialx/nodes/output_material.cc index 5b5d9d0b7c66..92fd81dbbe22 100644 --- a/source/blender/nodes/shader/materialx/nodes/output_material.cc +++ b/source/blender/nodes/shader/materialx/nodes/output_material.cc @@ -37,7 +37,8 @@ NodeItem OutputMaterialNodeParser::compute(const std::string &socket_name) NodeItem OutputMaterialNodeParser::compute_default() { NodeItem surface = create_node("standard_surface", "surfaceshader"); - surface.set_input("base_color", value(MaterialX::Color3(material_->r, material_->g, material_->b))); + surface.set_input("base_color", + value(MaterialX::Color3(material_->r, material_->g, material_->b))); surface.set_input("diffuse_roughness", value(material_->roughness)); if (material_->metallic > 0.0f) { surface.set_input("metalness", value(material_->metallic)); diff --git a/source/blender/nodes/shader/materialx/nodes/tex_checker.cc b/source/blender/nodes/shader/materialx/nodes/tex_checker.cc index 63266bd5081a..8325bb0e06ef 100644 --- a/source/blender/nodes/shader/materialx/nodes/tex_checker.cc +++ b/source/blender/nodes/shader/materialx/nodes/tex_checker.cc @@ -8,23 +8,20 @@ namespace blender::nodes::materialx { NodeItem TexCheckerNodeParser::compute() { - NodeItem scale = get_input_value("Scale"); + NodeItem vector = get_input_link("Vector"); NodeItem color1 = get_input_value("Color1"); NodeItem color2 = get_input_value("Color2"); + NodeItem scale = get_input_value("Scale"); - if (scale.value && scale.type() == "float") { - float v = scale.value->asA(); - scale = value(MaterialX::Vector2(v, v)); + if (!vector) { + vector = create_node("texcoord", "vector2"); } - - NodeItem texcoord = create_node("texcoord", "vector2"); - NodeItem place2d = create_node("place2d", "vector2"); - place2d.set_input("texcoord", texcoord * scale); + vector = vector * scale; NodeItem separate = create_node("separate2", "multioutput"); - separate.set_input("in", place2d); - separate.add_output("outx", "float"); - separate.add_output("outy", "float"); + separate.set_input("in", vector, NodeItem::Type::Vector2); + separate.add_output("outx", NodeItem::Type::Float); + separate.add_output("outy", NodeItem::Type::Float); NodeItem modulo_x = create_node("modulo", "float"); modulo_x.set_input("in1", separate, "outx"); @@ -34,12 +31,12 @@ NodeItem TexCheckerNodeParser::compute() modulo_y.set_input("in1", separate, "outy"); modulo_y.set_input("in2", value(2.0f)); - NodeItem ifequal = - (modulo_x.floor() + modulo_y.floor()).if_else("==", value(1.0f), value(0.0f), value(1.0f)); + NodeItem ifequal = (modulo_x.floor() + modulo_y.floor()) + .if_else(NodeItem::CompareOp::Eq, value(1.0f), value(0.0f), value(1.0f)); NodeItem res = create_node("mix", "color3"); - res.set_input("bg", color1.to_color3()); - res.set_input("fg", color2.to_color3()); + res.set_input("bg", color1, NodeItem::Type::Color3); + res.set_input("fg", color2, NodeItem::Type::Color3); res.set_input("mix", ifequal); return res; } diff --git a/source/blender/nodes/shader/materialx/nodes/tex_noise.cc b/source/blender/nodes/shader/materialx/nodes/tex_noise.cc index d3a016c63208..2136f8abd066 100644 --- a/source/blender/nodes/shader/materialx/nodes/tex_noise.cc +++ b/source/blender/nodes/shader/materialx/nodes/tex_noise.cc @@ -12,14 +12,15 @@ NodeItem TexNoiseNodeParser::compute() NodeItem detail = get_input_value("Detail"); NodeItem lacunarity = get_input_value("Lacunarity"); - if (detail.value && detail.type() == "float") { + if (detail.value && detail.type() == NodeItem::Type::Float) { detail = value(int(detail.value->asA())); } - NodeItem texcoord = create_node("position", "vector3"); + NodeItem position = create_node("position", "vector3"); + position = position * scale; NodeItem res = create_node("fractal3d", "color3"); - res.set_input("position", texcoord * scale); + res.set_input("position", position, NodeItem::Type::Vector3); res.set_input("octaves", detail); res.set_input("lacunarity", lacunarity); return res;