diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 064f203db349..abbe0851ff55 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -152,11 +152,13 @@ if(WITH_MATERIALX) materialx/nodes/invert.cc materialx/nodes/math.cc materialx/nodes/mix_rgb.cc + materialx/nodes/node_item.cc materialx/nodes/node_parser.cc materialx/nodes/output_material.cc materialx/nodes/tex_image.cc materialx/material.h + materialx/nodes/node_item.h materialx/nodes/node_parser.h ) endif() diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc index 5dee4cc40d61..3da7211f7ddf 100644 --- a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc @@ -8,31 +8,16 @@ namespace blender::nodes::materialx { NodeItem BSDFPrincipledNodeParser::compute() { - auto enabled = [](NodeItem &val) -> bool { - if (val.node) { - return true; - } - if (!val.value) { - return false; - } - if (val.value->isA()) { - return val.value->asA() != 0.0f; - } - if (val.value->isA()) { - auto c = val.value->asA(); - return c[0] != 0.0f || c[1] != 0.0f || c[2] != 0.0f; - } - return true; - }; + NodeItem zero = value(0.0f); /* Getting required inputs * Note: if some inputs are not needed they won't be taken */ NodeItem base_color = get_input_value("Base Color"); NodeItem subsurface = get_input_value("Subsurface"); - NodeItem subsurface_radius = empty_value(); - NodeItem subsurface_color = empty_value(); - if (enabled(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"); } @@ -42,33 +27,33 @@ NodeItem BSDFPrincipledNodeParser::compute() // NodeItem specular_tint = get_input_value("Specular Tint"); NodeItem roughness = get_input_value("Roughness"); - NodeItem anisotropic = empty_value(); - NodeItem anisotropic_rotation = empty_value(); - if (enabled(metallic)) { + NodeItem anisotropic = empty(); + NodeItem anisotropic_rotation = empty(); + if (metallic != zero) { /* TODO: use Specular Tint input */ anisotropic = get_input_value("Anisotropic"); - if (enabled(anisotropic)) { + if (anisotropic != zero) { anisotropic_rotation = get_input_value("Anisotropic Rotation"); // anisotropic_rotation = 0.5 - (anisotropic_rotation % 1.0) } } NodeItem sheen = get_input_value("Sheen"); - // sheen_tint = empty_value(); + // sheen_tint = empty(); // if enabled(sheen): // sheen_tint = get_input_value("Sheen Tint"); NodeItem clearcoat = get_input_value("Clearcoat"); - NodeItem clearcoat_roughness = empty_value(); - if (enabled(clearcoat)) { + NodeItem clearcoat_roughness = empty(); + if (clearcoat != zero) { clearcoat_roughness = get_input_value("Clearcoat Roughness"); } NodeItem ior = get_input_value("IOR"); NodeItem transmission = get_input_value("Transmission"); - NodeItem transmission_roughness = empty_value(); - if (enabled(transmission)) { + NodeItem transmission_roughness = empty(); + if (transmission != zero) { transmission_roughness = get_input_value("Transmission Roughness"); } @@ -90,11 +75,11 @@ NodeItem BSDFPrincipledNodeParser::compute() res.set_input("normal", normal); res.set_input("tangent", tangent); - if (enabled(metallic)) { + if (metallic != zero) { res.set_input("metalness", metallic); } - if (enabled(specular)) { + if (specular != zero) { res.set_input("specular", specular); res.set_input("specular_color", base_color.to_color3()); res.set_input("specular_roughness", roughness); @@ -103,26 +88,26 @@ NodeItem BSDFPrincipledNodeParser::compute() res.set_input("specular_rotation", anisotropic_rotation); } - if (enabled(transmission)) { + if (transmission != zero) { res.set_input("transmission", transmission); res.set_input("transmission_color", base_color.to_color3()); res.set_input("transmission_extra_roughness", transmission_roughness); } - if (enabled(subsurface)) { + if (subsurface != zero) { res.set_input("subsurface", subsurface); res.set_input("subsurface_color", subsurface_color); res.set_input("subsurface_radius", subsurface_radius); res.set_input("subsurface_anisotropy", anisotropic); } - if (enabled(sheen)) { + if (sheen != zero) { res.set_input("sheen", sheen); res.set_input("sheen_color", base_color.to_color3()); res.set_input("sheen_roughness", roughness); } - if (enabled(clearcoat)) { + if (clearcoat != zero) { res.set_input("coat", clearcoat); res.set_input("coat_color", base_color.to_color3()); res.set_input("coat_roughness", clearcoat_roughness); @@ -132,7 +117,7 @@ NodeItem BSDFPrincipledNodeParser::compute() res.set_input("coat_normal", clearcoat_normal); } - if (enabled(emission)) { + if (emission != zero) { res.set_input("emission", emission_strength); res.set_input("emission_color", emission); } diff --git a/source/blender/nodes/shader/materialx/nodes/math.cc b/source/blender/nodes/shader/materialx/nodes/math.cc index 3cee80450a6a..9fce9a7457d3 100644 --- a/source/blender/nodes/shader/materialx/nodes/math.cc +++ b/source/blender/nodes/shader/materialx/nodes/math.cc @@ -8,8 +8,164 @@ namespace blender::nodes::materialx { NodeItem MathNodeParser::compute() { - /* TODO: implement */ - return empty_value(); + /* TODO: finish some math operations */ + + auto op = node->custom1; + printf("%d\n", int(op)); + + NodeItem res = empty(); + + /* Single operand operations */ + NodeItem x = get_input_value(0); + switch (op) { + case NODE_MATH_SINE: + res = x.sin(); + break; + case NODE_MATH_COSINE: + res = x.cos(); + break; + case NODE_MATH_TANGENT: + res = x.tan(); + break; + case NODE_MATH_ARCSINE: + res = x.asin(); + break; + case NODE_MATH_ARCCOSINE: + res = x.acos(); + break; + case NODE_MATH_ARCTANGENT: + res = x.atan(); + break; + case NODE_MATH_ROUND: + res = (x + value(0.5f)).floor(); + break; + case NODE_MATH_ABSOLUTE: + res = x.abs(); + break; + case NODE_MATH_FLOOR: + res = x.floor(); + break; + case NODE_MATH_CEIL: + res = x.ceil(); + break; + case NODE_MATH_FRACTION: + res = x % value(1.0f); + break; + case NODE_MATH_SQRT: + res = x.sqrt(); + break; + case NODE_MATH_INV_SQRT: + res = value(1.0f) / x.sqrt(); + break; + case NODE_MATH_SIGN: + res = x.sign(); + break; + case NODE_MATH_EXPONENT: + res = x.exp(); + break; + case NODE_MATH_RADIANS: + res = x * value(float(M_PI) / 180.0f); + break; + case NODE_MATH_DEGREES: + res = x * value(180.0f * float(M_1_PI)); + break; + case NODE_MATH_SINH: + res = x.sinh(); + break; + case NODE_MATH_COSH: + res = x.cosh(); + break; + case NODE_MATH_TANH: + res = x.tanh(); + break; + case NODE_MATH_TRUNC: + res = x.sign() * x.abs().floor(); + break; + + default: { + /* 2-operand operations */ + NodeItem y = get_input_value(1); + switch (op) { + case NODE_MATH_ADD: + res = x + y; + break; + case NODE_MATH_SUBTRACT: + res = x - y; + break; + case NODE_MATH_MULTIPLY: + res = x * y; + break; + case NODE_MATH_DIVIDE: + res = x / y; + break; + case NODE_MATH_POWER: + res = x ^ y; + break; + case NODE_MATH_LOGARITHM: + res = x.ln() / y.ln(); + break; + case NODE_MATH_MINIMUM: + res = x.min(y); + break; + case NODE_MATH_MAXIMUM: + res = x.max(y); + break; + case NODE_MATH_LESS_THAN: + res = x.if_else("<", y, value(1.0f), value(0.0f)); + break; + case NODE_MATH_GREATER_THAN: + res = x.if_else(">", y, value(1.0f), value(0.0f)); + break; + case NODE_MATH_MODULO: + res = x % y; + break; + case NODE_MATH_ARCTAN2: + res = x.atan2(y); + break; + case NODE_MATH_SNAP: + // res = ; + break; + case NODE_MATH_PINGPONG: + // res = ; + break; + case NODE_MATH_FLOORED_MODULO: + // res = ; + break; + + default: { + /* 3-operand operations */ + NodeItem z = get_input_value(2); + switch (op) { + case NODE_MATH_WRAP: + // res = ; + break; + case NODE_MATH_COMPARE: + res = z.if_else("<", (x - y).abs(), value(1.0f), value(0.0f)); + break; + case NODE_MATH_MULTIPLY_ADD: + res = x * y + z; + break; + case NODE_MATH_SMOOTH_MIN: + // res = ; + break; + case NODE_MATH_SMOOTH_MAX: + // res = ; + break; + + default: + BLI_assert_unreachable(); + } + } + } + } + } + + bool clamp_output = node->custom2 != 0; + if (clamp_output && res) { + res = res.clamp(); + } + + return res; } } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/mix_rgb.cc b/source/blender/nodes/shader/materialx/nodes/mix_rgb.cc index 34cfad6891e5..c06cf322a0f3 100644 --- a/source/blender/nodes/shader/materialx/nodes/mix_rgb.cc +++ b/source/blender/nodes/shader/materialx/nodes/mix_rgb.cc @@ -9,7 +9,7 @@ namespace blender::nodes::materialx { NodeItem MixRGBNodeParser::compute() { /* TODO: implement */ - return empty_value(); + return empty(); } } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/node_item.cc b/source/blender/nodes/shader/materialx/nodes/node_item.cc new file mode 100644 index 000000000000..8a65fe155a2b --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/node_item.cc @@ -0,0 +1,599 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_item.h" + +#include "BLI_assert.h" +#include "BLI_utildefines.h" + +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) +{ + if (item.value) { + set_input(name, item.value); + } + else if (item.node) { + set_input(name, item.node); + } +} + +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) +{ + this->node->setConnectedNode(name, node); +} + +NodeItem::operator bool() const +{ + return value || node; +} + +NodeItem NodeItem::operator+(const NodeItem &other) const +{ + return arithmetic(other, "add", [](float a, float b) { return a + b; }); +} + +NodeItem NodeItem::operator-(const NodeItem &other) const +{ + return arithmetic(other, "subtract", [](float a, float b) { return a - b; }); +} + +NodeItem NodeItem::operator-() const +{ + return val(0.0f) - *this; +} + +NodeItem NodeItem::operator*(const NodeItem &other) const +{ + return arithmetic(other, "multiply", [](float a, float b) { return a * b; }); +} + +NodeItem NodeItem::operator/(const NodeItem &other) const +{ + return arithmetic(other, "divide", [](float a, float b) { return b == 0.0f ? 0.0f : a / b; }); +} + +NodeItem NodeItem::operator%(const NodeItem &other) const +{ + return arithmetic( + other, "modulo", [](float a, float b) { return b == 0.0f ? 0.0f : std::fmodf(a, b); }); +} + +NodeItem NodeItem::operator^(const NodeItem &other) const +{ + return arithmetic(other, "power", [](float a, float b) { return std::powf(a, b); }); +} + +bool NodeItem::operator==(const NodeItem &other) const +{ + if (!*this) { + return !other; + } + if (!other) { + return !*this; + } + if (node && node == other.node) { + return true; + } + if ((node && other.value) || (value && other.node)) { + return false; + } + + std::string mx_type; + auto val1 = value; + auto val2 = other.value; + if (!adjust_types(val1, val2, mx_type)) { + 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; +} + +bool NodeItem::operator!=(const NodeItem &other) const +{ + return !(*this == other); +} + +NodeItem NodeItem::abs() const +{ + return arithmetic("absval", [](float a) { return std::fabsf(a); }); +} + +NodeItem NodeItem::floor() const +{ + return arithmetic("floor", [](float a) { return std::floorf(a); }); +} + +NodeItem NodeItem::ceil() const +{ + return arithmetic("ceil", [](float a) { return std::ceilf(a); }); +} + +NodeItem NodeItem::min(const NodeItem &other) const +{ + return arithmetic(other, "min", [](float a, float b) { return std::min(a, b); }); +} + +NodeItem NodeItem::max(const NodeItem &other) const +{ + return arithmetic(other, "max", [](float a, float b) { return std::max(a, b); }); +} + +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(); + 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]; + } + 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; +} + +NodeItem NodeItem::clamp(const NodeItem &min_val, const NodeItem &max_val) const +{ + return min(max_val).max(min_val); +} + +NodeItem NodeItem::clamp(float min_val, float max_val) const +{ + return clamp(val(min_val), val(max_val)); +} + +NodeItem NodeItem::sin() const +{ + return arithmetic("sin", [](float a) { return std::sinf(a); }); +} + +NodeItem NodeItem::cos() const +{ + return arithmetic("cos", [](float a) { return std::cosf(a); }); +} + +NodeItem NodeItem::tan() const +{ + return arithmetic("tan", [](float a) { return std::tanf(a); }); +} + +NodeItem NodeItem::asin() const +{ + return arithmetic("asin", [](float a) { return std::asinf(a); }); +} + +NodeItem NodeItem::acos() const +{ + return arithmetic("acos", [](float a) { return std::acosf(a); }); +} + +NodeItem NodeItem::atan() const +{ + return arithmetic("atan", [](float a) { return std::atanf(a); }); +} + +NodeItem NodeItem::atan2(const NodeItem &other) const +{ + return arithmetic(other, "atan2", [](float a, float b) { return std::atan2f(a, b); }); +} + +NodeItem NodeItem::sinh() const +{ + return (exp() - (-*this).exp()) / val(2.0f); +} + +NodeItem NodeItem::cosh() const +{ + return (exp() - (-*this).exp()) / val(2.0f); +} + +NodeItem NodeItem::tanh() const +{ + return sinh() / cosh(); +} + +NodeItem NodeItem::ln() const +{ + return arithmetic("ln", [](float a) { return std::logf(a); }); +} + +NodeItem NodeItem::sqrt() const +{ + return arithmetic("sqrt", [](float a) { return std::sqrtf(a); }); +} + +NodeItem NodeItem::sign() const +{ + return arithmetic("sign", [](float a) { return a < 0.0f ? -1.0f : (a == 0.0f ? 0.0f : 1.0f); }); +} + +NodeItem NodeItem::exp() const +{ + return arithmetic("exp", [](float a) { return std::expf(a); }); +} + +NodeItem NodeItem::to_color3() const +{ + std::string t = type(); + NodeItem res = empty(); + if (value) { + MaterialX::Color3 c; + if (t == "float") { + float v = value->asA(); + c = {v, v, v}; + } + else if (t == "color3") { + auto v = value->asA(); + c = {v[0], v[1], v[2]}; + } + else if (t == "color4") { + auto v = value->asA(); + c = {v[0], v[1], v[2]}; + } + else if (t == "vector3") { + auto v = value->asA(); + c = {v[0], v[1], v[2]}; + } + else if (t == "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 (t != "color3") { + return res; + } + res.node = node; + } + return res; +} + +bool NodeItem::is_numeric() 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(); + } + + 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 { + BLI_assert_unreachable(); + } + } + else { + res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, t); + res.set_input("in", *this); + } + return res; +} + +NodeItem NodeItem::arithmetic(const NodeItem &other, + const std::string &mx_category, + std::function func) const +{ + NodeItem res = empty(); + if (!is_numeric() || !other.is_numeric()) { + 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(); + } + } + 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); + } + 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 new file mode 100644 index 000000000000..26d3f58b02f9 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/node_item.h @@ -0,0 +1,99 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +namespace blender::nodes::materialx { + +class NodeItem { + public: + MaterialX::ValuePtr value; + MaterialX::NodePtr node; + + private: + MaterialX::GraphElement *graph_; + + public: + 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); + void set_input(const std::string &name, const MaterialX::ValuePtr value); + void set_input(const std::string &name, const MaterialX::NodePtr node); + + operator bool() const; + NodeItem operator+(const NodeItem &other) const; + NodeItem operator-(const NodeItem &other) const; + NodeItem operator-() const; + NodeItem operator*(const NodeItem &other) const; + NodeItem operator/(const NodeItem &other) const; + NodeItem operator%(const NodeItem &other) const; + NodeItem operator^(const NodeItem &other) const; + bool operator==(const NodeItem &other) const; + bool operator!=(const NodeItem &other) const; + + 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; + NodeItem sin() const; + NodeItem cos() const; + NodeItem tan() const; + NodeItem asin() const; + NodeItem acos() const; + NodeItem atan() const; + NodeItem atan2(const NodeItem &other) const; + NodeItem sinh() const; + NodeItem cosh() const; + NodeItem tanh() const; + NodeItem ln() const; + NodeItem sqrt() const; + NodeItem sign() const; + NodeItem exp() const; + + NodeItem to_color3() const; + bool is_numeric() const; + std::string type() const; + + private: + NodeItem arithmetic(const std::string &mx_category, std::function func) const; + NodeItem arithmetic(const NodeItem &other, + const std::string &mx_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 +{ + NodeItem res(graph_); + res.value = MaterialX::Value::createValue(data); + return res; +} + +template +void NodeItem::set_input(const std::string &name, const T &value, const std::string &mx_type) +{ + node->setInputValue(name, value, mx_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 3b65ec9bf3f6..61bda4d32628 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_parser.cc +++ b/source/blender/nodes/shader/materialx/nodes/node_parser.cc @@ -8,323 +8,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) -{ - if (item.value) { - set_input(name, item.value); - } - else if (item.node) { - set_input(name, item.node); - } -} - -void NodeItem::set_input(const std::string &name, const MaterialX::ValuePtr value) -{ - std::string mx_type = value->getTypeString(); - if (value->isA()) { - set_input(name, value->asA(), mx_type); - } - else if (value->isA()) { - set_input(name, value->asA(), mx_type); - } - else if (value->isA()) { - set_input(name, value->asA(), mx_type); - } - else if (value->isA()) { - set_input(name, value->asA(), mx_type); - } - else if (value->isA()) { - set_input(name, value->asA(), mx_type); - } - else if (value->isA()) { - set_input(name, value->asA(), mx_type); - } - else if (value->isA()) { - set_input(name, value->asA(), mx_type); - } - else { - BLI_assert_unreachable(); - } -} - -void NodeItem::set_input(const std::string &name, const MaterialX::NodePtr node) -{ - this->node->setConnectedNode(name, node); -} - -NodeItem::operator bool() const -{ - return value || node; -} - -NodeItem NodeItem::operator+(const NodeItem &other) const -{ - return arithmetic(other, "add", [](float a, float b) { return a + b; }); -} - -NodeItem NodeItem::operator-(const NodeItem &other) const -{ - return arithmetic(other, "subtract", [](float a, float b) { return a - b; }); -} - -NodeItem NodeItem::operator*(const NodeItem &other) const -{ - return arithmetic(other, "multiply", [](float a, float b) { return a * b; }); -} - -NodeItem NodeItem::operator/(const NodeItem &other) const -{ - return arithmetic(other, "divide", [](float a, float b) { return b == 0.0f ? 0.0f : a / b; }); -} - -bool NodeItem::operator==(const NodeItem &other) const -{ - if (node && node == other.node) { - return true; - } - /* TODO: implement */ - return false; -} - -NodeItem NodeItem::min(const NodeItem &other) const -{ - return arithmetic(other, "min", [](float a, float b) { return std::min(a, b); }); -} - -NodeItem NodeItem::max(const NodeItem &other) const -{ - return arithmetic(other, "max", [](float a, float b) { return std::max(a, b); }); -} - -NodeItem NodeItem::blend(const NodeItem &a, const NodeItem &b) const -{ - return (val(1.0f) - *this) * a + *this * b; -} - -NodeItem NodeItem::to_color3() const -{ - std::string t = type(); - NodeItem res = empty(); - if (value) { - MaterialX::Color3 c; - if (t == "float") { - float v = value->asA(); - c = {v, v, v}; - } - else if (t == "color3") { - auto v = value->asA(); - c = {v[0], v[1], v[2]}; - } - else if (t == "color4") { - auto v = value->asA(); - c = {v[0], v[1], v[2]}; - } - else if (t == "vector3") { - auto v = value->asA(); - c = {v[0], v[1], v[2]}; - } - else if (t == "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 (t != "color3") { - return res; - } - res.node = node; - } - return res; -} - -bool NodeItem::is_numeric() 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(); - } - - 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 { - BLI_assert_unreachable(); - } - } - else { - res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, t); - res.set_input("in", *this); - } - return res; -} - -NodeItem NodeItem::arithmetic(const NodeItem &other, - const std::string &mx_category, - std::function func) const -{ - NodeItem res = empty(); - if (!is_numeric() || !other.is_numeric()) { - return res; - } - - std::string t1 = type(); - std::string t2 = other.type(); - - if (value && other.value) { - std::string t = t1; - auto val1 = value; - auto val2 = other.value; - if (t1 != t2) { - if (t1 == "float") { - val1 = float_to_type(val1->asA(), t2); - t = t2; - } - else if (t2 == "float") { - val2 = float_to_type(val2->asA(), t1); - } - else { - return res; - } - } - - if (t == "float") { - float v1 = val1->asA(); - float v2 = val2->asA(); - res.value = MaterialX::Value::createValue(func(v1, v2)); - } - else if (t == "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 (t == "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 (t == "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 (t == "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 (t == "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(); - } - } - else { - std::string t = t1; - auto val1 = *this; - auto val2 = other; - if (t1 != t2) { - if (val1.value && t1 == "float") { - val1.value = float_to_type(val1.value->asA(), t2); - t = t2; - } - else if (val2.value && t2 == "float") { - val2.value = float_to_type(val2.value->asA(), t1); - } - else { - return res; - } - } - - res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, t); - res.set_input("in1", val1); - res.set_input("in2", val2); - } - return res; -} - -MaterialX::ValuePtr NodeItem::float_to_type(float v, std::string t) const -{ - if (t == "float") { - return MaterialX::Value::createValue(v); - } - if (t == "color3") { - return MaterialX::Value::createValue({v, v, v}); - } - if (t == "color4") { - return MaterialX::Value::createValue({v, v, v, 1.0f}); - } - if (t == "vector2") { - return MaterialX::Value::createValue({v, v}); - } - if (t == "vector3") { - return MaterialX::Value::createValue({v, v, v}); - } - if (t == "vector4") { - return MaterialX::Value::createValue({v, v, v, 1.0f}); - } - - BLI_assert_unreachable(); - return nullptr; -} - NodeParser::NodeParser(MaterialX::GraphElement *graph, const Depsgraph *depsgraph, const Material *material, @@ -337,7 +20,7 @@ NodeItem NodeParser::create_node(const std::string &mx_category, const std::string &mx_type, bool accessory) { - NodeItem res = empty_value(); + NodeItem res = empty(); res.node = graph->addNode(mx_category, accessory ? MaterialX::EMPTY_STRING : MaterialX::createValidName(node->name), @@ -347,9 +30,42 @@ NodeItem NodeParser::create_node(const std::string &mx_category, NodeItem NodeParser::get_input_default(const std::string &name) { - NodeItem res = empty_value(); + return get_input_default(node->input_by_identifier(name)); +} - const bNodeSocket &socket = node->input_by_identifier(name); +NodeItem NodeParser::get_input_default(int index) +{ + return get_input_default(node->input_socket(index)); +} + +NodeItem NodeParser::get_input_link(const std::string &name) +{ + return get_input_link(node->input_by_identifier(name)); +} + +NodeItem NodeParser::get_input_link(int index) +{ + return get_input_link(node->input_socket(index)); +} + +NodeItem NodeParser::get_input_value(const std::string &name) +{ + return get_input_value(node->input_by_identifier(name)); +} + +NodeItem NodeParser::get_input_value(int index) +{ + return get_input_value(node->input_socket(index)); +} + +NodeItem NodeParser::empty() const +{ + return NodeItem(graph); +} + +NodeItem NodeParser::get_input_default(const bNodeSocket &socket) +{ + NodeItem res = empty(); switch (socket.type) { case SOCK_FLOAT: { float v = socket.default_value_typed()->value; @@ -372,11 +88,11 @@ NodeItem NodeParser::get_input_default(const std::string &name) return res; } -NodeItem NodeParser::get_input_link(const std::string &name) +NodeItem NodeParser::get_input_link(const bNodeSocket &socket) { - NodeItem res = empty_value(); + NodeItem res = empty(); - const bNodeLink *link = node->input_by_identifier(name).link; + const bNodeLink *link = socket.link; if (!(link && link->is_used())) { return res; } @@ -419,18 +135,13 @@ NodeItem NodeParser::get_input_link(const std::string &name) return res; } -NodeItem NodeParser::get_input_value(const std::string &name) +NodeItem NodeParser::get_input_value(const bNodeSocket &socket) { - NodeItem res = get_input_link(name); + NodeItem res = get_input_link(socket); if (!res) { - res = get_input_default(name); + res = get_input_default(socket); } return res; } -NodeItem NodeParser::empty_value() -{ - return NodeItem(graph); -} - } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/node_parser.h b/source/blender/nodes/shader/materialx/nodes/node_parser.h index d2604ff265b2..4849e430bc35 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_parser.h +++ b/source/blender/nodes/shader/materialx/nodes/node_parser.h @@ -4,7 +4,7 @@ #pragma once -#include +#include "node_item.h" #include "DEG_depsgraph.h" #include "DNA_material_types.h" @@ -12,63 +12,6 @@ namespace blender::nodes::materialx { -class NodeItem { - public: - MaterialX::ValuePtr value; - MaterialX::NodePtr node; - - private: - MaterialX::GraphElement *graph_; - - public: - 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); - void set_input(const std::string &name, const MaterialX::ValuePtr value); - void set_input(const std::string &name, const MaterialX::NodePtr node); - - operator bool() const; - NodeItem operator+(const NodeItem &other) const; - NodeItem operator-(const NodeItem &other) const; - NodeItem operator*(const NodeItem &other) const; - NodeItem operator/(const NodeItem &other) const; - bool operator==(const NodeItem &other) const; - - NodeItem min(const NodeItem &other) const; - NodeItem max(const NodeItem &other) const; - NodeItem blend(const NodeItem &a, const NodeItem &b) const; - - NodeItem to_color3() const; - bool is_numeric() const; - std::string type() const; - - private: - NodeItem arithmetic(const std::string &mx_category, std::function func) const; - NodeItem arithmetic(const NodeItem &other, - const std::string &mx_category, - std::function func) const; - MaterialX::ValuePtr float_to_type(float v, std::string t) const; -}; - -template NodeItem NodeItem::val(const T &data) const -{ - NodeItem res(graph_); - res.value = MaterialX::Value::createValue(data); - return res; -} - -template -void NodeItem::set_input(const std::string &name, const T &value, const std::string &mx_type) -{ - node->setInputValue(name, value, mx_type); -} - class NodeParser { public: MaterialX::GraphElement *graph; @@ -90,11 +33,25 @@ class NodeParser { const std::string &mx_type, bool accessory = false); NodeItem get_input_default(const std::string &name); + NodeItem get_input_default(int index); NodeItem get_input_link(const std::string &name); + NodeItem get_input_link(int index); NodeItem get_input_value(const std::string &name); - NodeItem empty_value(); + NodeItem get_input_value(int index); + NodeItem empty() const; + template NodeItem value(const T &data) const; + + private: + NodeItem get_input_default(const bNodeSocket &socket); + NodeItem get_input_link(const bNodeSocket &socket); + NodeItem get_input_value(const bNodeSocket &socket); }; +template NodeItem NodeParser::value(const T &data) const +{ + return empty().val(data); +} + #define DECLARE_PARSER(T) \ class T : public NodeParser { \ public: \ diff --git a/source/blender/nodes/shader/materialx/nodes/output_material.cc b/source/blender/nodes/shader/materialx/nodes/output_material.cc index dd44f7692ba9..94a106c40c03 100644 --- a/source/blender/nodes/shader/materialx/nodes/output_material.cc +++ b/source/blender/nodes/shader/materialx/nodes/output_material.cc @@ -8,7 +8,7 @@ namespace blender::nodes::materialx { NodeItem OutputMaterialNodeParser::compute() { - NodeItem node = empty_value(); + NodeItem node = empty(); NodeItem surface = get_input_link("Surface"); if (surface) { node = create_node("surfacematerial", "material");