diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 1888021af16c..064f203db349 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -149,15 +149,15 @@ if(WITH_MATERIALX) list(APPEND SRC materialx/material.cc materialx/nodes/bsdf_principled.cc + materialx/nodes/invert.cc + materialx/nodes/math.cc + materialx/nodes/mix_rgb.cc materialx/nodes/node_parser.cc materialx/nodes/output_material.cc materialx/nodes/tex_image.cc materialx/material.h - materialx/nodes/bsdf_principled.h materialx/nodes/node_parser.h - materialx/nodes/output_material.h - materialx/nodes/tex_image.h ) endif() diff --git a/source/blender/nodes/shader/materialx/material.cc b/source/blender/nodes/shader/materialx/material.cc index 8e3350199e85..5fec3563571f 100644 --- a/source/blender/nodes/shader/materialx/material.cc +++ b/source/blender/nodes/shader/materialx/material.cc @@ -3,7 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "material.h" -#include "nodes/output_material.h" +#include "nodes/node_parser.h" #include #include diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc index e00bbce4d598..5dee4cc40d61 100644 --- a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc @@ -2,9 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "bsdf_principled.h" - -#include +#include "node_parser.h" namespace blender::nodes::materialx { diff --git a/source/blender/nodes/shader/materialx/nodes/output_material.h b/source/blender/nodes/shader/materialx/nodes/invert.cc similarity index 54% rename from source/blender/nodes/shader/materialx/nodes/output_material.h rename to source/blender/nodes/shader/materialx/nodes/invert.cc index 0701c0769e8a..b6ca706330fe 100644 --- a/source/blender/nodes/shader/materialx/nodes/output_material.h +++ b/source/blender/nodes/shader/materialx/nodes/invert.cc @@ -2,16 +2,15 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#pragma once - #include "node_parser.h" namespace blender::nodes::materialx { -class OutputMaterialNodeParser : public NodeParser { - public: - using NodeParser::NodeParser; - NodeItem compute() override; -}; +NodeItem InvertNodeParser::compute() +{ + NodeItem fac = get_input_value("Fac"); + NodeItem color = get_input_value("Color"); + return fac.blend(color, fac.val(1.0f) - color); +} } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/tex_image.h b/source/blender/nodes/shader/materialx/nodes/math.cc similarity index 61% rename from source/blender/nodes/shader/materialx/nodes/tex_image.h rename to source/blender/nodes/shader/materialx/nodes/math.cc index cc6f962ef88a..3cee80450a6a 100644 --- a/source/blender/nodes/shader/materialx/nodes/tex_image.h +++ b/source/blender/nodes/shader/materialx/nodes/math.cc @@ -2,16 +2,14 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#pragma once - #include "node_parser.h" namespace blender::nodes::materialx { -class TexImageNodeParser : public NodeParser { - public: - using NodeParser::NodeParser; - NodeItem compute() override; -}; +NodeItem MathNodeParser::compute() +{ + /* TODO: implement */ + return empty_value(); +} } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.h b/source/blender/nodes/shader/materialx/nodes/mix_rgb.cc similarity index 60% rename from source/blender/nodes/shader/materialx/nodes/bsdf_principled.h rename to source/blender/nodes/shader/materialx/nodes/mix_rgb.cc index b4cace6d8216..34cfad6891e5 100644 --- a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.h +++ b/source/blender/nodes/shader/materialx/nodes/mix_rgb.cc @@ -2,16 +2,14 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#pragma once - #include "node_parser.h" namespace blender::nodes::materialx { -class BSDFPrincipledNodeParser : public NodeParser { - public: - using NodeParser::NodeParser; - NodeItem compute() override; -}; +NodeItem MixRGBNodeParser::compute() +{ + /* TODO: implement */ + return empty_value(); +} } // 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 11da5c8158a7..3b65ec9bf3f6 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_parser.cc +++ b/source/blender/nodes/shader/materialx/nodes/node_parser.cc @@ -4,15 +4,17 @@ #include "node_parser.h" -#include "bsdf_principled.h" -#include "tex_image.h" - #include 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) { @@ -25,20 +27,27 @@ void NodeItem::set_input(const std::string &name, const NodeItem &item) 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(), "float"); + 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(), "vector3"); + set_input(name, value->asA(), mx_type); } else if (value->isA()) { - set_input(name, value->asA(), "vector4"); + set_input(name, value->asA(), mx_type); } else if (value->isA()) { - set_input(name, value->asA(), "color3"); + set_input(name, value->asA(), mx_type); } else if (value->isA()) { - set_input(name, value->asA(), "color4"); + set_input(name, value->asA(), mx_type); + } + else if (value->isA()) { + set_input(name, value->asA(), mx_type); } else { BLI_assert_unreachable(); @@ -55,29 +64,267 @@ NodeItem::operator bool() const return value || node; } -NodeItem NodeItem::to_color3() +NodeItem NodeItem::operator+(const NodeItem &other) const { - NodeItem res(graph_); + 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) { - if (value->isA()) { + MaterialX::Color3 c; + if (t == "float") { float v = value->asA(); - res.value = MaterialX::Value::createValue(MaterialX::Color3(v, v, v)); + c = {v, v, v}; } - else if (value->isA()) { - res.value = value; + else if (t == "color3") { + auto v = value->asA(); + c = {v[0], v[1], v[2]}; } - else if (value->isA()) { - auto c = value->asA(); - res.value = MaterialX::Value::createValue( - MaterialX::Color3(c[0], c[1], c[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, @@ -151,6 +398,15 @@ NodeItem NodeParser::get_input_link(const std::string &name) case SH_NODE_BSDF_PRINCIPLED: parser = std::make_unique(graph, depsgraph, material, in_node); break; + case SH_NODE_INVERT: + parser = std::make_unique(graph, depsgraph, material, in_node); + break; + case SH_NODE_MATH: + parser = std::make_unique(graph, depsgraph, material, in_node); + break; + case SH_NODE_MIX_RGB_LEGACY: + parser = std::make_unique(graph, depsgraph, material, in_node); + break; case SH_NODE_TEX_IMAGE: parser = std::make_unique(graph, depsgraph, material, in_node); break; diff --git a/source/blender/nodes/shader/materialx/nodes/node_parser.h b/source/blender/nodes/shader/materialx/nodes/node_parser.h index 5a021e8bb33f..d2604ff265b2 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_parser.h +++ b/source/blender/nodes/shader/materialx/nodes/node_parser.h @@ -24,6 +24,9 @@ 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); @@ -31,10 +34,35 @@ class NodeItem { 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 to_color3(); + 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) { @@ -67,4 +95,18 @@ class NodeParser { NodeItem empty_value(); }; +#define DECLARE_PARSER(T) \ + class T : public NodeParser { \ + public: \ + using NodeParser::NodeParser; \ + NodeItem compute() override; \ + }; + +DECLARE_PARSER(BSDFPrincipledNodeParser) +DECLARE_PARSER(InvertNodeParser) +DECLARE_PARSER(MathNodeParser) +DECLARE_PARSER(MixRGBNodeParser) +DECLARE_PARSER(OutputMaterialNodeParser) +DECLARE_PARSER(TexImageNodeParser) + } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/output_material.cc b/source/blender/nodes/shader/materialx/nodes/output_material.cc index 6b3a4584ec53..dd44f7692ba9 100644 --- a/source/blender/nodes/shader/materialx/nodes/output_material.cc +++ b/source/blender/nodes/shader/materialx/nodes/output_material.cc @@ -2,7 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "output_material.h" +#include "node_parser.h" namespace blender::nodes::materialx { diff --git a/source/blender/nodes/shader/materialx/nodes/tex_image.cc b/source/blender/nodes/shader/materialx/nodes/tex_image.cc index 3d1a0423c9fa..6c0239459e06 100644 --- a/source/blender/nodes/shader/materialx/nodes/tex_image.cc +++ b/source/blender/nodes/shader/materialx/nodes/tex_image.cc @@ -2,7 +2,6 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "tex_image.h" #include "node_parser.h" #include "hydra/image.h"