diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 9a3b77502fbf..1d75ad77a3df 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -147,18 +147,30 @@ set(LIB if(WITH_MATERIALX) list(APPEND SRC materialx/material.cc + materialx/nodes/add_shader.cc materialx/nodes/brightness.cc + materialx/nodes/bsdf_diffuse.cc + materialx/nodes/bsdf_glass.cc + materialx/nodes/bsdf_glossy.cc materialx/nodes/bsdf_principled.cc + materialx/nodes/bsdf_refraction.cc + materialx/nodes/bsdf_sheen.cc + materialx/nodes/bsdf_toon.cc + materialx/nodes/bsdf_translucent.cc + materialx/nodes/bsdf_transparent.cc + materialx/nodes/emission.cc materialx/nodes/huesatval.cc materialx/nodes/invert.cc materialx/nodes/math.cc materialx/nodes/mix_rgb.cc + materialx/nodes/mix_shader.cc materialx/nodes/node_item.cc materialx/nodes/node_parser.cc materialx/nodes/normal_map.cc materialx/nodes/output_material.cc materialx/nodes/sepcomb_color.cc materialx/nodes/sepcomb_xyz.cc + materialx/nodes/subsurface_scattering.cc materialx/nodes/tex_checker.cc materialx/nodes/tex_environment.cc materialx/nodes/tex_image.cc diff --git a/source/blender/nodes/shader/materialx/material.cc b/source/blender/nodes/shader/materialx/material.cc index 1e88124b3570..a4d2f2264a7c 100644 --- a/source/blender/nodes/shader/materialx/material.cc +++ b/source/blender/nodes/shader/materialx/material.cc @@ -17,9 +17,7 @@ namespace blender::nodes::materialx { CLG_LOGREF_DECLARE_GLOBAL(LOG_MATERIALX_SHADER, "materialx.shader"); -MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, - Material *material, - const std::string &socket_name) +MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, Material *material) { CLOG_INFO(LOG_MATERIALX_SHADER, 0, "Material: %s", material->id.name); @@ -27,7 +25,7 @@ MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, if (material->use_nodes) { material->nodetree->ensure_topology_cache(); bNode *output_node = ntreeShaderOutputNode(material->nodetree, SHD_OUTPUT_ALL); - OutputMaterialNodeParser(doc.get(), depsgraph, material, output_node).compute(socket_name); + OutputMaterialNodeParser(doc.get(), depsgraph, material, output_node).compute_full(); } else { OutputMaterialNodeParser(doc.get(), depsgraph, material, nullptr).compute_default(); diff --git a/source/blender/nodes/shader/materialx/material.h b/source/blender/nodes/shader/materialx/material.h index 4c90febce6c9..b32f3ff2962a 100644 --- a/source/blender/nodes/shader/materialx/material.h +++ b/source/blender/nodes/shader/materialx/material.h @@ -15,8 +15,6 @@ namespace blender::nodes::materialx { extern struct CLG_LogRef *LOG_MATERIALX_SHADER; -MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, - Material *material, - const std::string &socket_name = "Surface"); +MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, Material *material); } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/add_shader.cc b/source/blender/nodes/shader/materialx/nodes/add_shader.cc new file mode 100644 index 000000000000..8ac9d7ab4c24 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/add_shader.cc @@ -0,0 +1,42 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem AddShaderNodeParser::compute() +{ + NodeItem res = empty(); + switch (shader_type_) { + case NodeItem::Type::BSDF: + case NodeItem::Type::EDF: { + NodeItem shader1 = get_input_shader(0, shader_type_); + NodeItem shader2 = get_input_shader(1, shader_type_); + + if (shader1 && !shader2) { + res = shader1; + } + else if (!shader1 && shader2) { + res = shader2; + } + else if (shader1 && shader2) { + res = shader1 + shader2; + } + break; + } + case NodeItem::Type::SurfaceShader: { + res = get_input_shader(0, shader_type_); + if (!res) { + res = get_input_shader(1, shader_type_); + } + break; + } + default: + BLI_assert_unreachable(); + } + return res; +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_diffuse.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_diffuse.cc new file mode 100644 index 000000000000..0b975cba89c0 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_diffuse.cc @@ -0,0 +1,28 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem BSDFDiffuseNodeParser::compute() +{ + if (shader_type_ != NodeItem::Type::BSDF) { + return empty(); + } + + NodeItem color = get_input_value("Color", NodeItem::Type::Color3); + NodeItem roughness = get_input_value("Roughness", NodeItem::Type::Float); + NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3); + + NodeItem res = create_node("oren_nayar_diffuse_bsdf", "BSDF"); + res.set_input("color", color); + res.set_input("roughness", roughness); + if (normal) { + res.set_input("normal", normal); + } + return res; +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_glass.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_glass.cc new file mode 100644 index 000000000000..092953e7eed5 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_glass.cc @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem BSDFGlassNodeParser::compute() +{ + /* TODO: implement */ + return empty(); +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_glossy.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_glossy.cc new file mode 100644 index 000000000000..98a372ec14c8 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_glossy.cc @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem BSDFGlossyNodeParser::compute() +{ + /* TODO: implement */ + return empty(); +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc index 41e93336471d..2a39bd542d81 100644 --- a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc @@ -8,6 +8,11 @@ namespace blender::nodes::materialx { NodeItem BSDFPrincipledNodeParser::compute() { + if (shader_type_ != NodeItem::Type::SurfaceShader) { + /* TODO: implement for BSDF and EDF */ + return empty(); + } + NodeItem base_color = get_input_value("Base Color", NodeItem::Type::Color3); NodeItem subsurface = get_input_value("Subsurface", NodeItem::Type::Float); @@ -40,9 +45,9 @@ NodeItem BSDFPrincipledNodeParser::compute() NodeItem alpha = get_input_value("Alpha", NodeItem::Type::Float); // transparency = 1.0 - alpha - NodeItem normal = get_input_link("Normal"); - NodeItem clearcoat_normal = get_input_link("Clearcoat Normal"); - NodeItem tangent = get_input_link("Tangent"); + NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3); + NodeItem clearcoat_normal = get_input_link("Clearcoat Normal", NodeItem::Type::Vector3); + NodeItem tangent = get_input_link("Tangent", NodeItem::Type::Vector3); /* Creating standard_surface */ NodeItem res = create_node("standard_surface", "surfaceshader"); diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_refraction.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_refraction.cc new file mode 100644 index 000000000000..2818cb52c298 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_refraction.cc @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem BSDFRefractionNodeParser::compute() +{ + /* TODO: implement */ + return empty(); +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_sheen.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_sheen.cc new file mode 100644 index 000000000000..7672813a63fb --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_sheen.cc @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem BSDFSheenNodeParser::compute() +{ + /* TODO: implement */ + return empty(); +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_toon.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_toon.cc new file mode 100644 index 000000000000..0c8fffa57804 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_toon.cc @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem BSDFToonNodeParser::compute() +{ + /* TODO: implement */ + return empty(); +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_translucent.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_translucent.cc new file mode 100644 index 000000000000..82779b4203b1 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_translucent.cc @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem BSDFTranslucentNodeParser::compute() +{ + /* TODO: implement */ + return empty(); +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/bsdf_transparent.cc b/source/blender/nodes/shader/materialx/nodes/bsdf_transparent.cc new file mode 100644 index 000000000000..c9310de8ac38 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_transparent.cc @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem BSDFTransparentNodeParser::compute() +{ + /* TODO: implement */ + return empty(); +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/emission.cc b/source/blender/nodes/shader/materialx/nodes/emission.cc new file mode 100644 index 000000000000..47d090e4e7fd --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/emission.cc @@ -0,0 +1,23 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem EmissionNodeParser::compute() +{ + if (shader_type_ != NodeItem::Type::EDF) { + return empty(); + } + + NodeItem color = get_input_value("Color", NodeItem::Type::Color3); + NodeItem strength = get_input_value("Strength", NodeItem::Type::Float); + + NodeItem res = create_node("uniform_edf", "EDF"); + res.set_input("color", color * strength); + return res; +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/math.cc b/source/blender/nodes/shader/materialx/nodes/math.cc index c974f9d46a30..b038733c6ab2 100644 --- a/source/blender/nodes/shader/materialx/nodes/math.cc +++ b/source/blender/nodes/shader/materialx/nodes/math.cc @@ -14,7 +14,7 @@ NodeItem MathNodeParser::compute() NodeItem res = empty(); /* Single operand operations */ - NodeItem x = get_input_value(0, NodeItem::Type::Empty); + NodeItem x = get_input_value(0, NodeItem::Type::Any); switch (op) { case NODE_MATH_SINE: res = x.sin(); @@ -82,7 +82,7 @@ NodeItem MathNodeParser::compute() default: { /* 2-operand operations */ - NodeItem y = get_input_value(1, NodeItem::Type::Empty); + NodeItem y = get_input_value(1, NodeItem::Type::Any); switch (op) { case NODE_MATH_ADD: res = x + y; @@ -132,7 +132,7 @@ NodeItem MathNodeParser::compute() default: { /* 3-operand operations */ - NodeItem z = get_input_value(2, NodeItem::Type::Empty); + NodeItem z = get_input_value(2, NodeItem::Type::Any); switch (op) { case NODE_MATH_WRAP: CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op); diff --git a/source/blender/nodes/shader/materialx/nodes/mix_shader.cc b/source/blender/nodes/shader/materialx/nodes/mix_shader.cc new file mode 100644 index 000000000000..fc27dc1a92ae --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/mix_shader.cc @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem MixShaderNodeParser::compute() +{ + NodeItem res = empty(); + switch (shader_type_) { + case NodeItem::Type::BSDF: + case NodeItem::Type::EDF: { + NodeItem fac = get_input_value(0, NodeItem::Type::Float); + NodeItem shader1 = get_input_shader(1, shader_type_); + NodeItem shader2 = get_input_shader(2, shader_type_); + + if (shader1 && !shader2) { + res = shader1 * (value(1.0f) - fac); + } + else if (!shader1 && shader2) { + res = shader2 * fac; + } + else if (shader1 && shader2) { + res = create_node("mix", NodeItem::type(shader_type_)); + res.set_input("fg", shader1); + res.set_input("bg", shader2); + res.set_input("mix", fac); + } + break; + } + case NodeItem::Type::SurfaceShader: { + res = get_input_shader(1, shader_type_); + if (!res) { + res = get_input_shader(2, shader_type_); + } + break; + } + default: + BLI_assert_unreachable(); + } + return res; +} + +} // 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 index 7801d5409135..8c62b0bf4267 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_item.cc +++ b/source/blender/nodes/shader/materialx/nodes/node_item.cc @@ -12,6 +12,86 @@ namespace blender::nodes::materialx { NodeItem::NodeItem(MaterialX::GraphElement *graph) : graph_(graph) {} +NodeItem::Type NodeItem::type(const std::string &type_str) +{ + if (type_str == "string") { + return Type::String; + } + if (type_str == "filename") { + return Type::Filename; + } + 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; + } + if (type_str == "BSDF") { + return Type::BSDF; + } + if (type_str == "EDF") { + return Type::EDF; + } + if (type_str == "surfaceshader") { + return Type::SurfaceShader; + } + if (type_str == "material") { + return Type::Material; + } + BLI_assert_unreachable(); + return Type::Empty; +} + +std::string NodeItem::type(Type type) +{ + switch (type) { + case Type::String: + return "string"; + case Type::Filename: + return "filename"; + 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"; + case Type::BSDF: + return "BSDF"; + case Type::EDF: + return "EDF"; + case Type::SurfaceShader: + return "surfaceshader"; + case Type::Material: + return "material"; + default: + BLI_assert_unreachable(); + } + return ""; +} + NodeItem::operator bool() const { return value || node; @@ -19,6 +99,21 @@ NodeItem::operator bool() const NodeItem NodeItem::operator+(const NodeItem &other) const { + Type type = this->type(); + if (ELEM(type, Type::BSDF, Type::EDF)) { + /* Special case: add BSDF/EDF shaders */ + NodeItem res = empty(); + if (other.type() == type) { + res.node = graph_->addNode("add", MaterialX::EMPTY_STRING, this->type(type)); + res.set_input("in1", *this); + res.set_input("in2", other); + } + else { + BLI_assert_unreachable(); + } + return res; + } + return arithmetic(other, "add", [](float a, float b) { return a + b; }); } @@ -34,6 +129,22 @@ NodeItem NodeItem::operator-() const NodeItem NodeItem::operator*(const NodeItem &other) const { + Type type = this->type(); + if (ELEM(type, Type::BSDF, Type::EDF)) { + /* Special case: multiple BSDF/EDF shader by Float or Color3 */ + NodeItem res = empty(); + Type other_type = other.type(); + if (ELEM(other_type, Type::Float, Type::Color3)) { + res.node = graph_->addNode("multiply", MaterialX::EMPTY_STRING, this->type(type)); + res.set_input("in1", *this); + res.set_input("in2", other); + } + else { + BLI_assert_unreachable(); + } + return res; + } + return arithmetic(other, "multiply", [](float a, float b) { return a * b; }); } @@ -70,7 +181,7 @@ bool NodeItem::operator==(const NodeItem &other) const NodeItem item1 = *this; NodeItem item2 = other; - Type to_type = adjust_types(item1, item2); + Type to_type = cast_types(item1, item2); if (to_type == Type::Empty) { return false; } @@ -248,10 +359,14 @@ NodeItem NodeItem::extract(const int index) const NodeItem NodeItem::convert(Type to_type) const { Type from_type = type(); - if (from_type == to_type) { + if (from_type == Type::Empty || from_type == to_type || to_type == Type::Any) { return *this; } if (!is_arithmetic(from_type) || !is_arithmetic(to_type)) { + CLOG_WARN(LOG_MATERIALX_SHADER, + "Cannot convert: %s -> %s", + type(from_type).c_str(), + type(to_type).c_str()); return empty(); } @@ -448,7 +563,7 @@ NodeItem NodeItem::if_else(CompareOp op, auto item1 = if_val; auto item2 = else_val; - Type to_type = adjust_types(item1, item2); + Type to_type = cast_types(item1, item2); if (to_type == Type::Empty) { return res; } @@ -554,66 +669,12 @@ 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; + return type >= Type::Float && type <= Type::Color4; } -NodeItem::Type NodeItem::adjust_types(NodeItem &item1, NodeItem &item2) +NodeItem::Type NodeItem::cast_types(NodeItem &item1, NodeItem &item2) { Type t1 = item1.type(); Type t2 = item2.type(); @@ -621,6 +682,8 @@ NodeItem::Type NodeItem::adjust_types(NodeItem &item1, NodeItem &item2) return t1; } if (!is_arithmetic(t1) || !is_arithmetic(t2)) { + CLOG_WARN( + LOG_MATERIALX_SHADER, "Can't adjust types: %s <-> %s", type(t1).c_str(), type(t2).c_str()); return Type::Empty; } if (t1 < t2) { @@ -701,7 +764,7 @@ NodeItem NodeItem::arithmetic(const NodeItem &other, NodeItem res = empty(); NodeItem item1 = *this; NodeItem item2 = other; - Type to_type = adjust_types(item1, item2); + Type to_type = cast_types(item1, item2); if (to_type == Type::Empty) { return res; } diff --git a/source/blender/nodes/shader/materialx/nodes/node_item.h b/source/blender/nodes/shader/materialx/nodes/node_item.h index 19e29ef1854f..6a223a4d8501 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_item.h +++ b/source/blender/nodes/shader/materialx/nodes/node_item.h @@ -11,16 +11,28 @@ namespace blender::nodes::materialx { class NodeItem { public: enum class Type { - Empty = 0, - Other, /* For MaterialX types like: surfaceshader, bsdf, edf, ...*/ + Any = 0, + Empty, + + /* Value types */ String, + Filename, Integer, + /* Block of arithmetic types. Ordered by type cast */ Float, Vector2, Vector3, - Vector4, Color3, - Color4 + Vector4, + Color4, + /* End of arithmetic types */ + + /* Shader types + * NOTE: There are only supported types */ + BSDF, + EDF, + SurfaceShader, + Material, }; enum class CompareOp { Less = 0, LessEq, Eq, GreaterEq, Greater, NotEq }; @@ -35,6 +47,9 @@ class NodeItem { NodeItem(MaterialX::GraphElement *graph); ~NodeItem() = default; + static Type type(const std::string &type_str); + static std::string type(Type type); + /* Operators */ operator bool() const; NodeItem operator+(const NodeItem &other) const; @@ -92,10 +107,8 @@ class NodeItem { void add_output(const std::string &in_name, Type out_type); private: - 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); + static Type cast_types(NodeItem &item1, NodeItem &item2); bool is_arithmetic() const; NodeItem arithmetic(const std::string &category, std::function func) const; diff --git a/source/blender/nodes/shader/materialx/nodes/node_parser.cc b/source/blender/nodes/shader/materialx/nodes/node_parser.cc index ec3462efb801..b1987955cd1b 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_parser.cc +++ b/source/blender/nodes/shader/materialx/nodes/node_parser.cc @@ -22,11 +22,21 @@ NodeParser::NodeParser(MaterialX::GraphElement *graph, { } -std::string NodeParser::node_name(const bNode *node, const bNodeSocket *socket_out) +NodeItem NodeParser::compute_full() { - return MaterialX::createValidName(node->output_sockets().size() <= 1 ? - std::string(node->name) : - std::string(node->name) + "_" + socket_out->name); + CLOG_INFO(LOG_MATERIALX_SHADER, 1, "%s [%d]", node_->name, node_->typeinfo->type); + NodeItem res = compute(); + if (res.node) { + res.node->setName(node_name()); + } + return res; +} + +std::string NodeParser::node_name() +{ + return MaterialX::createValidName(node_->output_sockets().size() <= 1 ? + std::string(node_->name) : + std::string(node_->name) + "_" + socket_out_->name); } NodeItem NodeParser::create_node(const std::string &mx_category, const std::string &mx_type) @@ -36,34 +46,34 @@ NodeItem NodeParser::create_node(const std::string &mx_category, const std::stri return res; } -NodeItem NodeParser::get_input_default(const std::string &name) +NodeItem NodeParser::get_input_default(const std::string &name, NodeItem::Type to_type) { - return get_input_default(node_->input_by_identifier(name)); + return get_input_default(node_->input_by_identifier(name), to_type); } -NodeItem NodeParser::get_input_default(int index) +NodeItem NodeParser::get_input_default(int index, NodeItem::Type to_type) { - return get_input_default(node_->input_socket(index)); + return get_input_default(node_->input_socket(index), to_type); } -NodeItem NodeParser::get_input_link(const std::string &name) +NodeItem NodeParser::get_input_link(const std::string &name, NodeItem::Type to_type) { - return get_input_link(node_->input_by_identifier(name)); + return get_input_link(node_->input_by_identifier(name), to_type); } -NodeItem NodeParser::get_input_link(int index) +NodeItem NodeParser::get_input_link(int index, NodeItem::Type to_type) { - return get_input_link(node_->input_socket(index)); + return get_input_link(node_->input_socket(index), to_type); } -NodeItem NodeParser::get_input_value(const std::string &name, const NodeItem::Type type) +NodeItem NodeParser::get_input_value(const std::string &name, NodeItem::Type to_type) { - return get_input_value(node_->input_by_identifier(name), type); + return get_input_value(node_->input_by_identifier(name), to_type); } -NodeItem NodeParser::get_input_value(int index, const NodeItem::Type type) +NodeItem NodeParser::get_input_value(int index, NodeItem::Type to_type) { - return get_input_value(node_->input_socket(index), type); + return get_input_value(node_->input_socket(index), to_type); } NodeItem NodeParser::empty() const @@ -71,7 +81,7 @@ NodeItem NodeParser::empty() const return NodeItem(graph_); } -NodeItem NodeParser::get_input_default(const bNodeSocket &socket) +NodeItem NodeParser::get_input_default(const bNodeSocket &socket, NodeItem::Type to_type) { NodeItem res = empty(); switch (socket.type) { @@ -96,10 +106,10 @@ NodeItem NodeParser::get_input_default(const bNodeSocket &socket) CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported socket type: %d", socket.type); } } - return res; + return res.convert(to_type); } -NodeItem NodeParser::get_input_link(const bNodeSocket &socket) +NodeItem NodeParser::get_input_link(const bNodeSocket &socket, NodeItem::Type to_type) { NodeItem res = empty(); @@ -119,21 +129,16 @@ NodeItem NodeParser::get_input_link(const bNodeSocket &socket) from_node = link->fromnode; } - /* Checking if node was already computed */ - res.node = graph_->getNode(node_name(from_node, link->fromsock)); - if (res.node) { - return res; - } + /* Creating required NodeParser object */ + std::unique_ptr parser; - /* Computing from_node with required NodeParser object */ #define CASE_NODE_TYPE(type, T) \ case type: \ - res = T(graph_, depsgraph_, material_, from_node, link->fromsock).compute_full(); \ + parser = std::make_unique(graph_, depsgraph_, material_, from_node, link->fromsock); \ break; switch (from_node->typeinfo->type) { CASE_NODE_TYPE(SH_NODE_BRIGHTCONTRAST, BrightContrastNodeParser) - CASE_NODE_TYPE(SH_NODE_BSDF_PRINCIPLED, BSDFPrincipledNodeParser) CASE_NODE_TYPE(SH_NODE_COMBINE_COLOR, CombineColorNodeParser) CASE_NODE_TYPE(SH_NODE_COMBXYZ, CombineXYZNodeParser) CASE_NODE_TYPE(SH_NODE_HUE_SAT, HueSatValNodeParser) @@ -155,27 +160,137 @@ NodeItem NodeParser::get_input_link(const bNodeSocket &socket) from_node->name, from_node->typeinfo->type); } + if (!parser) { + return res; + } + /* Checking if node was already computed */ + res.node = graph_->getNode(parser->node_name()); + if (res.node) { + return res; + } + + /* Computing */ + res = parser->compute_full(); + return res.convert(to_type); +} + +NodeItem NodeParser::get_input_value(const bNodeSocket &socket, NodeItem::Type to_type) +{ + NodeItem res = get_input_link(socket, to_type); + if (!res) { + res = get_input_default(socket, to_type); + } return res; } -NodeItem NodeParser::get_input_value(const bNodeSocket &socket, const NodeItem::Type type) +ShaderNodeParser::ShaderNodeParser(MaterialX::GraphElement *graph, + const Depsgraph *depsgraph, + const Material *material, + const bNode *node, + const bNodeSocket *socket_out, + NodeItem::Type shader_type) + : NodeParser(graph, depsgraph, material, node, socket_out), shader_type_(shader_type) { - NodeItem res = get_input_link(socket); - if (!res) { - res = get_input_default(socket); - } - return type == NodeItem::Type::Empty ? res : res.convert(type); } -NodeItem NodeParser::compute_full() +NodeItem ShaderNodeParser::compute_full() { - CLOG_INFO(LOG_MATERIALX_SHADER, 1, "%s [%d]", node_->name, node_->typeinfo->type); + CLOG_INFO(LOG_MATERIALX_SHADER, + 1, + "%s [%d] - %s", + node_->name, + node_->typeinfo->type, + NodeItem::type(shader_type_).c_str()); NodeItem res = compute(); if (res.node) { - res.node->setName(node_name(node_, socket_out_)); + res.node->setName(node_name()); } return res; } +std::string ShaderNodeParser::node_name() +{ + std::string name = NodeParser::node_name(); + if (shader_type_ != NodeItem::Type::SurfaceShader) { + name += "_" + NodeItem::type(shader_type_); + } + return name; +} + +NodeItem ShaderNodeParser::get_input_shader(const std::string &name, NodeItem::Type shader_type) +{ + return get_input_shader(node_->input_by_identifier(name), shader_type); +} + +NodeItem ShaderNodeParser::get_input_shader(int index, NodeItem::Type shader_type) +{ + return get_input_shader(node_->input_socket(index), shader_type); +} + +NodeItem ShaderNodeParser::get_input_shader(const bNodeSocket &socket, NodeItem::Type shader_type) +{ + NodeItem res = empty(); + + const bNodeLink *link = socket.link; + if (!(link && link->is_used())) { + return res; + } + + const bNode *from_node = link->fromnode; + + /* Passing NODE_REROUTE nodes */ + while (from_node->type == NODE_REROUTE) { + link = from_node->input_socket(0).link; + if (!(link && link->is_used())) { + return res; + } + from_node = link->fromnode; + } + + /* Creating required ShaderNodeParser object */ + std::unique_ptr parser; + +#define CASE_SHADER_NODE_TYPE(type, T) \ + case type: \ + parser = std::make_unique( \ + graph_, depsgraph_, material_, from_node, link->fromsock, shader_type); \ + break; + + switch (from_node->typeinfo->type) { + CASE_SHADER_NODE_TYPE(SH_NODE_ADD_SHADER, AddShaderNodeParser) + CASE_SHADER_NODE_TYPE(SH_NODE_BSDF_DIFFUSE, BSDFDiffuseNodeParser) + // CASE_SHADER_NODE_TYPE(SH_NODE_BSDF_GLASS, BSDFGlassNodeParser) + // CASE_SHADER_NODE_TYPE(SH_NODE_BSDF_GLOSSY, BSDFGlossyNodeParser) + CASE_SHADER_NODE_TYPE(SH_NODE_BSDF_PRINCIPLED, BSDFPrincipledNodeParser) + // CASE_SHADER_NODE_TYPE(SH_NODE_BSDF_REFRACTION, BSDFRefractionNodeParser) + // CASE_SHADER_NODE_TYPE(SH_NODE_BSDF_SHEEN, BSDFSheenNodeParser) + // CASE_SHADER_NODE_TYPE(SH_NODE_BSDF_TOON, BSDFToonNodeParser) + // CASE_SHADER_NODE_TYPE(SH_NODE_BSDF_TRANSLUCENT, BSDFTranslucentNodeParser) + // CASE_SHADER_NODE_TYPE(SH_NODE_BSDF_TRANSPARENT, BSDFTransparentNodeParser) + CASE_SHADER_NODE_TYPE(SH_NODE_EMISSION, EmissionNodeParser) + CASE_SHADER_NODE_TYPE(SH_NODE_MIX_SHADER, MixShaderNodeParser) + // CASE_SHADER_NODE_TYPE(SH_NODE_SUBSURFACE_SCATTERING, SubsurfaceScatteringNodeParser) + + default: + CLOG_WARN(LOG_MATERIALX_SHADER, + "Unsupported node: %s [%d]", + from_node->name, + from_node->typeinfo->type); + } + if (!parser) { + return res; + } + + /* Checking if node was already computed */ + res.node = graph_->getNode(parser->node_name()); + if (res.node) { + return res; + } + + /* Computing */ + res = parser->compute_full(); + return res; +} + } // 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 15243b74f992..eb4502d6cd06 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_parser.h +++ b/source/blender/nodes/shader/materialx/nodes/node_parser.h @@ -31,24 +31,44 @@ class NodeParser { virtual NodeItem compute() = 0; protected: - static std::string node_name(const bNode *node, const bNodeSocket *socket_out); + virtual NodeItem compute_full(); + virtual std::string node_name(); NodeItem create_node(const std::string &mx_category, const std::string &mx_type); - 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, - const NodeItem::Type type); - NodeItem get_input_value(int index, const NodeItem::Type type); + NodeItem get_input_default(const std::string &name, NodeItem::Type to_type); + NodeItem get_input_default(int index, NodeItem::Type to_type); + NodeItem get_input_link(const std::string &name, NodeItem::Type to_type); + NodeItem get_input_link(int index, NodeItem::Type to_type); + NodeItem get_input_value(const std::string &name, NodeItem::Type to_type); + NodeItem get_input_value(int index, NodeItem::Type to_type); 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, - const NodeItem::Type type); - NodeItem compute_full(); + NodeItem get_input_default(const bNodeSocket &socket, NodeItem::Type to_type); + NodeItem get_input_link(const bNodeSocket &socket, NodeItem::Type to_type); + NodeItem get_input_value(const bNodeSocket &socket, NodeItem::Type to_type); +}; + +class ShaderNodeParser : public NodeParser { + protected: + NodeItem::Type shader_type_; + + public: + ShaderNodeParser(MaterialX::GraphElement *graph, + const Depsgraph *depsgraph, + const Material *material, + const bNode *node, + const bNodeSocket *socket_out, + NodeItem::Type shader_type); + + protected: + NodeItem compute_full() override; + std::string node_name() override; + NodeItem get_input_shader(const std::string &name, NodeItem::Type shader_type); + NodeItem get_input_shader(int index, NodeItem::Type shader_type); + + private: + NodeItem get_input_shader(const bNodeSocket &socket, NodeItem::Type shader_type); }; template NodeItem NodeParser::value(const T &data) const @@ -56,28 +76,48 @@ template NodeItem NodeParser::value(const T &data) const return empty().val(data); } -#define DECLARE_PARSER(T) \ +#define DECLARE_NODE_PARSER(T) \ class T : public NodeParser { \ public: \ using NodeParser::NodeParser; \ NodeItem compute() override; \ }; -DECLARE_PARSER(BrightContrastNodeParser) -DECLARE_PARSER(BSDFPrincipledNodeParser) -DECLARE_PARSER(CombineColorNodeParser) -DECLARE_PARSER(CombineXYZNodeParser) -DECLARE_PARSER(HueSatValNodeParser) -DECLARE_PARSER(InvertNodeParser) -DECLARE_PARSER(MathNodeParser) -DECLARE_PARSER(MixRGBNodeParser) -DECLARE_PARSER(NormalMapNodeParser) -DECLARE_PARSER(SeparateColorNodeParser) -DECLARE_PARSER(SeparateXYZNodeParser) -DECLARE_PARSER(TexCheckerNodeParser) -DECLARE_PARSER(TexEnvironmentNodeParser) -DECLARE_PARSER(TexImageNodeParser) -DECLARE_PARSER(TexNoiseNodeParser) -DECLARE_PARSER(VectorMathNodeParser) +#define DECLARE_SHADER_NODE_PARSER(T) \ + class T : public ShaderNodeParser { \ + public: \ + using ShaderNodeParser::ShaderNodeParser; \ + NodeItem compute() override; \ + }; + +DECLARE_NODE_PARSER(BrightContrastNodeParser) +DECLARE_NODE_PARSER(CombineColorNodeParser) +DECLARE_NODE_PARSER(CombineXYZNodeParser) +DECLARE_NODE_PARSER(HueSatValNodeParser) +DECLARE_NODE_PARSER(InvertNodeParser) +DECLARE_NODE_PARSER(MathNodeParser) +DECLARE_NODE_PARSER(MixRGBNodeParser) +DECLARE_NODE_PARSER(NormalMapNodeParser) +DECLARE_NODE_PARSER(SeparateColorNodeParser) +DECLARE_NODE_PARSER(SeparateXYZNodeParser) +DECLARE_NODE_PARSER(TexCheckerNodeParser) +DECLARE_NODE_PARSER(TexEnvironmentNodeParser) +DECLARE_NODE_PARSER(TexImageNodeParser) +DECLARE_NODE_PARSER(TexNoiseNodeParser) +DECLARE_NODE_PARSER(VectorMathNodeParser) + +DECLARE_SHADER_NODE_PARSER(AddShaderNodeParser) +DECLARE_SHADER_NODE_PARSER(BSDFDiffuseNodeParser) +DECLARE_SHADER_NODE_PARSER(BSDFGlassNodeParser) +DECLARE_SHADER_NODE_PARSER(BSDFGlossyNodeParser) +DECLARE_SHADER_NODE_PARSER(BSDFPrincipledNodeParser) +DECLARE_SHADER_NODE_PARSER(BSDFRefractionNodeParser) +DECLARE_SHADER_NODE_PARSER(BSDFSheenNodeParser) +DECLARE_SHADER_NODE_PARSER(BSDFToonNodeParser) +DECLARE_SHADER_NODE_PARSER(BSDFTranslucentNodeParser) +DECLARE_SHADER_NODE_PARSER(BSDFTransparentNodeParser) +DECLARE_SHADER_NODE_PARSER(EmissionNodeParser) +DECLARE_SHADER_NODE_PARSER(MixShaderNodeParser) +DECLARE_SHADER_NODE_PARSER(SubsurfaceScatteringNodeParser) } // 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 92fd81dbbe22..99d071973c82 100644 --- a/source/blender/nodes/shader/materialx/nodes/output_material.cc +++ b/source/blender/nodes/shader/materialx/nodes/output_material.cc @@ -9,27 +9,34 @@ OutputMaterialNodeParser::OutputMaterialNodeParser(MaterialX::GraphElement *grap const Depsgraph *depsgraph, const Material *material, const bNode *node) - : NodeParser(graph, depsgraph, material, node, nullptr) + : ShaderNodeParser(graph, depsgraph, material, node, nullptr, NodeItem::Type::Material) { } NodeItem OutputMaterialNodeParser::compute() -{ - return empty(); -} - -NodeItem OutputMaterialNodeParser::compute(const std::string &socket_name) { NodeItem surface = empty(); if (node_) { - surface = get_input_link(socket_name); + NodeItem bsdf = get_input_shader("Surface", NodeItem::Type::BSDF); + NodeItem edf = get_input_shader("Surface", NodeItem::Type::EDF); + if (bsdf || edf) { + surface = create_node("surface", "surfaceshader"); + if (bsdf) { + surface.set_input("bsdf", bsdf); + } + if (edf) { + surface.set_input("edf", edf); + } + } + else { + surface = get_input_shader("Surface", NodeItem::Type::SurfaceShader); + } } else { surface = create_node("standard_surface", "surfaceshader"); surface.set_input("base_color", value(MaterialX::Color3(1.0f, 0.0f, 1.0f))); } NodeItem res = create_node("surfacematerial", "material"); - res.node->setName(node_name(node_, nullptr)); res.set_input("surfaceshader", surface); return res; } @@ -55,4 +62,9 @@ NodeItem OutputMaterialNodeParser::compute_default() return res; } +std::string OutputMaterialNodeParser::node_name() +{ + return NodeParser::node_name(); +} + } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/output_material.h b/source/blender/nodes/shader/materialx/nodes/output_material.h index 986c926f33d4..9f75d8761f87 100644 --- a/source/blender/nodes/shader/materialx/nodes/output_material.h +++ b/source/blender/nodes/shader/materialx/nodes/output_material.h @@ -8,15 +8,19 @@ namespace blender::nodes::materialx { -class OutputMaterialNodeParser : public NodeParser { +class OutputMaterialNodeParser : public ShaderNodeParser { public: OutputMaterialNodeParser(MaterialX::GraphElement *graph, const Depsgraph *depsgraph, const Material *material, const bNode *node); NodeItem compute() override; - NodeItem compute(const std::string &socket_name); + + using ShaderNodeParser::compute_full; NodeItem compute_default(); + + protected: + std::string node_name() override; }; } // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/subsurface_scattering.cc b/source/blender/nodes/shader/materialx/nodes/subsurface_scattering.cc new file mode 100644 index 000000000000..7efddb150a67 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/subsurface_scattering.cc @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +NodeItem SubsurfaceScatteringNodeParser::compute() +{ + /* TODO: implement */ + return empty(); +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/nodes/tex_checker.cc b/source/blender/nodes/shader/materialx/nodes/tex_checker.cc index 7cf1d8ee52a0..f039a6336e43 100644 --- a/source/blender/nodes/shader/materialx/nodes/tex_checker.cc +++ b/source/blender/nodes/shader/materialx/nodes/tex_checker.cc @@ -8,7 +8,7 @@ namespace blender::nodes::materialx { NodeItem TexCheckerNodeParser::compute() { - NodeItem vector = get_input_link("Vector"); + NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector2); NodeItem color1 = get_input_value("Color1", NodeItem::Type::Color3); NodeItem color2 = get_input_value("Color2", NodeItem::Type::Color3); NodeItem scale = get_input_value("Scale", NodeItem::Type::Float); @@ -16,28 +16,13 @@ NodeItem TexCheckerNodeParser::compute() if (!vector) { vector = create_node("texcoord", "vector2"); } - vector = vector * scale; - - NodeItem separate = create_node("separate2", "multioutput"); - separate.set_input("in", vector); - 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"); - modulo_x.set_input("in2", value(2.0f)); - - NodeItem modulo_y = create_node("modulo", "float"); - 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(NodeItem::CompareOp::Eq, value(1.0f), value(0.0f), value(1.0f)); - + vector = (vector * scale) % value(2.0f); + NodeItem mix = (vector.extract(0).floor() + vector.extract(1).floor()) + .if_else(NodeItem::CompareOp::Eq, value(1.0f), value(1.0f), value(0.0f)); NodeItem res = create_node("mix", "color3"); - res.set_input("bg", color1); - res.set_input("fg", color2); - res.set_input("mix", ifequal); + res.set_input("fg", color1); + res.set_input("bg", color2); + res.set_input("mix", mix); return res; } diff --git a/source/blender/nodes/shader/materialx/nodes/vector_math.cc b/source/blender/nodes/shader/materialx/nodes/vector_math.cc index df4764609699..a3267565772a 100644 --- a/source/blender/nodes/shader/materialx/nodes/vector_math.cc +++ b/source/blender/nodes/shader/materialx/nodes/vector_math.cc @@ -14,7 +14,7 @@ NodeItem VectorMathNodeParser::compute() NodeItem res = empty(); /* Single operand operations */ - NodeItem x = get_input_value(0, NodeItem::Type::Empty); + NodeItem x = get_input_value(0, NodeItem::Type::Any); switch (op) { case NODE_VECTOR_MATH_SINE: res = x.sin(); @@ -46,7 +46,7 @@ NodeItem VectorMathNodeParser::compute() default: { /* 2-operand operations */ - NodeItem y = get_input_value(1, NodeItem::Type::Empty); + NodeItem y = get_input_value(1, NodeItem::Type::Any); switch (op) { case NODE_VECTOR_MATH_ADD: res = x + y; @@ -93,7 +93,7 @@ NodeItem VectorMathNodeParser::compute() default: { /* 3-operand operations */ - NodeItem z = get_input_value(2, NodeItem::Type::Empty); + NodeItem z = get_input_value(2, NodeItem::Type::Any); switch (op) { case NODE_VECTOR_MATH_MULTIPLY_ADD: res = x * y + z;