diff --git a/source/blender/io/usd/hydra/material.cc b/source/blender/io/usd/hydra/material.cc index a992dbdbfa3..9fdf47965a8 100644 --- a/source/blender/io/usd/hydra/material.cc +++ b/source/blender/io/usd/hydra/material.cc @@ -81,6 +81,16 @@ void MaterialData::init() MaterialX::DocumentPtr doc = blender::nodes::materialx::export_to_materialx( scene_delegate_->depsgraph, (Material *)id); pxr::UsdMtlxRead(doc, stage); + + /* Logging stage: creating lambda stage_str() for not to call stage->ExportToString() + * if log won't be printed. */ + auto stage_str = [&stage]() { + std::string str; + stage->ExportToString(&str); + return str; + }; + ID_LOGN(2, "Stage:\n%s", stage_str().c_str()); + if (pxr::UsdPrim materials = stage->GetPrimAtPath(pxr::SdfPath("/MaterialX/Materials"))) { pxr::UsdPrimSiblingRange children = materials.GetChildren(); if (!children.empty()) { diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 25990bdcd1a..f437420e5d4 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -150,10 +150,12 @@ if(WITH_MATERIALX) materialx/material.cc materialx/node_item.cc materialx/node_parser.cc + materialx/group_nodes.cc materialx/material.h materialx/node_item.h materialx/node_parser.h + materialx/group_nodes.h ) list(APPEND LIB MaterialXCore diff --git a/source/blender/nodes/shader/materialx/group_nodes.cc b/source/blender/nodes/shader/materialx/group_nodes.cc new file mode 100644 index 00000000000..f2de9e68b4d --- /dev/null +++ b/source/blender/nodes/shader/materialx/group_nodes.cc @@ -0,0 +1,151 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "group_nodes.h" +#include "node_parser.h" + +#include "BLI_vector.hh" + +namespace blender::nodes::materialx { + +NodeItem GroupNodeParser::compute() +{ + NodeItem res = empty(); + + const bNodeTree *ngroup = reinterpret_cast(node_->id); + ngroup->ensure_topology_cache(); + const bNode *node_out = ngroup->group_output_node(); + if (!node_out) { + return res; + } + + MaterialX::GraphElement *graph = graph_; +#ifdef USE_MATERIALX_NODEGRAPH + std::string name = MaterialX::createValidName(ngroup->id.name); + MaterialX::NodeGraphPtr group_graph = graph_->getChildOfType(name); + if (!group_graph) { + CLOG_INFO(LOG_MATERIALX_SHADER, 1, "", name.c_str()); + group_graph = graph_->addChild(name); + } + graph = group_graph.get(); +#endif + + NodeItem out = + GroupOutputNodeParser( + graph, depsgraph_, material_, node_out, socket_out_, NodeItem::Type::Any, this) + .compute_full(); + +#ifdef USE_MATERIALX_NODEGRAPH + /* We have to be in NodeParser's graph_, therefore copying output */ + res.output = out.output; +#else + res = out; +#endif + return res; +} + +NodeItem GroupNodeParser::compute_full() +{ + NodeItem res = compute(); + if (NodeItem::is_arithmetic(to_type_)) { + res = res.convert(to_type_); + } + return res; +} + +NodeItem GroupOutputNodeParser::compute() +{ +#ifdef USE_MATERIALX_NODEGRAPH + Vector values; + for (auto socket_in : node_->input_sockets()) { + NodeItem value = get_input_value(socket_in->index(), NodeItem::Type::Any); + if (value.value) { + NodeItem constant = create_node("constant", value.type()); + constant.set_input("value", value); + value = constant; + } + values.append(value); + } + Vector outputs; + for (int i = 0; i < values.size(); ++i) { + if (values[i]) { + outputs.append(create_output("output" + std::to_string(i + 1), values[i])); + } + } + return outputs[socket_out_->index()]; +#else + return get_input_value(socket_out_->index(), NodeItem::Type::Any); +#endif +} + +NodeItem GroupOutputNodeParser::compute_full() +{ +#ifdef USE_MATERIALX_NODEGRAPH + NodeItem res = empty(); + + /* Checking if output was already computed */ + res.output = graph_->getOutput("output" + std::to_string(socket_out_->index() + 1)); + if (res.output) { + return res; + } + + CLOG_INFO(LOG_MATERIALX_SHADER, + 1, + "%s [%d] => %s", + node_->name, + node_->typeinfo->type, + NodeItem::type(to_type_).c_str()); + + res = compute(); + return res; +#else + return NodeParser::compute_full(); +#endif +} + +NodeItem GroupInputNodeParser::compute() +{ +#ifdef USE_MATERIALX_NODEGRAPH + NodeItem value = group_parser_->get_input_link(socket_out_->index(), to_type_); + if (!value) { + return empty(); + } + + if (value.value) { + NodeItem constant = create_node("constant", value.type()); + constant.set_input("value", value); + value = constant; + } + return create_input("input" + std::to_string(socket_out_->index() + 1), value); +#else + return group_parser_->get_input_link(socket_out_->index(), to_type_); +#endif +} + +NodeItem GroupInputNodeParser::compute_full() +{ +#ifdef USE_MATERIALX_NODEGRAPH + NodeItem res = empty(); + + /* Checking if output was already computed */ + res.input = graph_->getInput("input" + std::to_string(socket_out_->index() + 1)); + if (res.input) { + return res; + } + + CLOG_INFO(LOG_MATERIALX_SHADER, + 1, + "%s [%d] => %s", + node_->name, + node_->typeinfo->type, + NodeItem::type(to_type_).c_str()); + + res = compute(); + return res; +#else + return NodeParser::compute_full(); +#endif +} + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/group_nodes.h b/source/blender/nodes/shader/materialx/group_nodes.h new file mode 100644 index 00000000000..a486a4af5fd --- /dev/null +++ b/source/blender/nodes/shader/materialx/group_nodes.h @@ -0,0 +1,41 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "node_parser.h" + +/* TODO: pxr::UsdMtlxRead() doesn't perform nodegraphs. + * Uncomment USE_MATERIALX_NODEGRAPH after fixing it. */ +//#define USE_MATERIALX_NODEGRAPH + +namespace blender::nodes::materialx { + +class GroupInputNodeParser; + +class GroupNodeParser : public NodeParser { + friend NodeParser; + friend GroupInputNodeParser; + + public: + using NodeParser::NodeParser; + NodeItem compute() override; + NodeItem compute_full() override; +}; + +class GroupOutputNodeParser : public NodeParser { + public: + using NodeParser::NodeParser; + NodeItem compute() override; + NodeItem compute_full() override; +}; + +class GroupInputNodeParser : public NodeParser { + public: + using NodeParser::NodeParser; + NodeItem compute() override; + NodeItem compute_full() override; +}; + +} // namespace blender::nodes::materialx diff --git a/source/blender/nodes/shader/materialx/material.cc b/source/blender/nodes/shader/materialx/material.cc index 540204a8d87..9e40a51489b 100644 --- a/source/blender/nodes/shader/materialx/material.cc +++ b/source/blender/nodes/shader/materialx/material.cc @@ -62,18 +62,18 @@ MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, Material *mater bNode *output_node = ntreeShaderOutputNode(material->nodetree, SHD_OUTPUT_ALL); if (output_node) { NodeParserData data = { - doc.get(), depsgraph, material, NodeItem::Type::Material, NodeItem(doc.get())}; + doc.get(), depsgraph, material, NodeItem::Type::Material, nullptr, NodeItem(doc.get())}; output_node->typeinfo->materialx_fn(&data, output_node, nullptr); } else { DefaultMaterialNodeParser( - doc.get(), depsgraph, material, nullptr, nullptr, NodeItem::Type::Material) + doc.get(), depsgraph, material, nullptr, nullptr, NodeItem::Type::Material, nullptr) .compute_error(); } } else { DefaultMaterialNodeParser( - doc.get(), depsgraph, material, nullptr, nullptr, NodeItem::Type::Material) + doc.get(), depsgraph, material, nullptr, nullptr, NodeItem::Type::Material, nullptr) .compute(); } diff --git a/source/blender/nodes/shader/materialx/node_item.cc b/source/blender/nodes/shader/materialx/node_item.cc index ddade2e7704..f431a711ce3 100644 --- a/source/blender/nodes/shader/materialx/node_item.cc +++ b/source/blender/nodes/shader/materialx/node_item.cc @@ -66,7 +66,7 @@ std::string NodeItem::type(Type type) { switch (type) { case Type::Any: - return ""; + return "any"; case Type::Multioutput: return "multioutput"; case Type::String: @@ -103,9 +103,14 @@ std::string NodeItem::type(Type type) return ""; } +bool NodeItem::is_arithmetic(Type type) +{ + return type >= Type::Float && type <= Type::Color4; +} + NodeItem::operator bool() const { - return value || node; + return value || node || input || output; } NodeItem NodeItem::operator+(const NodeItem &other) const @@ -604,8 +609,8 @@ NodeItem NodeItem::if_else(CompareOp op, NodeItem NodeItem::extract(const int index) const { - NodeItem res = empty(); - res = create_node("extract", Type::Float); + /* TODO: Add check if (value) { ... } */ + NodeItem res = create_node("extract", Type::Float); res.set_input("in", *this); res.set_input("index", val(index)); return res; @@ -624,6 +629,9 @@ NodeItem::Type NodeItem::type() const if (node) { return type(node->getType()); } + if (output) { + return type(output->getType()); + } return Type::Empty; } @@ -675,30 +683,59 @@ void NodeItem::set_input(const std::string &in_name, const NodeItem &item) else if (item.node) { node->setConnectedNode(in_name, item.node); } + else if (item.input) { + node->setAttribute("interfacename", item.input->getName()); + } + else if (item.output) { + node->setConnectedOutput(in_name, item.output); + } else { CLOG_WARN(LOG_MATERIALX_SHADER, "Empty item to input: %s", in_name.c_str()); } } -void NodeItem::set_input_output(const std::string &in_name, - const NodeItem &item, - const std::string &out_name) +NodeItem NodeItem::add_output(const std::string &out_name, Type out_type) { - if (!item.node) { + NodeItem res = empty(); + res.output = node->addOutput(out_name, type(out_type)); + return res; +} + +NodeItem NodeItem::create_input(const std::string &name, const NodeItem &item) const +{ + NodeItem res = empty(); + res.input = graph_->addInput(name); + + Type item_type = item.type(); + if (item.node) { + res.input->setConnectedNode(item.node); + } + else { BLI_assert_unreachable(); } - node->setConnectedNode(in_name, item.node); - node->setConnectedOutput(in_name, item.node->getOutput(out_name)); + res.input->setType(type(item_type)); + + return res; } -void NodeItem::add_output(const std::string &name, Type out_type) +NodeItem NodeItem::create_output(const std::string &name, const NodeItem &item) const { - node->addOutput(name, type(out_type)); -} + NodeItem res = empty(); + res.output = graph_->addOutput(name); -bool NodeItem::is_arithmetic(Type type) -{ - return type >= Type::Float && type <= Type::Color4; + Type item_type = item.type(); + if (item.node) { + res.output->setConnectedNode(item.node); + } + else if (item.input) { + res.output->setInterfaceName(item.input->getName()); + } + else { + BLI_assert_unreachable(); + } + res.output->setType(type(item_type)); + + return res; } NodeItem::Type NodeItem::cast_types(NodeItem &item1, NodeItem &item2) @@ -776,10 +813,16 @@ NodeItem NodeItem::arithmetic(const std::string &category, std::function NodeItem val(const T &data) const; Type type() const; - NodeItem create_node(const std::string &category, NodeItem::Type type) const; - /* Functions to set input and output */ + /* Node functions */ + NodeItem create_node(const std::string &category, NodeItem::Type type) const; template void set_input(const std::string &in_name, const T &value, Type in_type); void set_input(const std::string &in_name, const NodeItem &item); - void set_input_output(const std::string &in_name, - const NodeItem &item, - const std::string &out_name); - void add_output(const std::string &in_name, Type out_type); + NodeItem add_output(const std::string &out_name, Type out_type); + + /* Output functions */ + NodeItem create_input(const std::string &name, const NodeItem &item) const; + NodeItem create_output(const std::string &name, const NodeItem &item) const; private: static Type cast_types(NodeItem &item1, NodeItem &item2); diff --git a/source/blender/nodes/shader/materialx/node_parser.cc b/source/blender/nodes/shader/materialx/node_parser.cc index 297d39aec01..73303ec03fe 100644 --- a/source/blender/nodes/shader/materialx/node_parser.cc +++ b/source/blender/nodes/shader/materialx/node_parser.cc @@ -4,6 +4,8 @@ #include "node_parser.h" +#include "group_nodes.h" + #include "BKE_node_runtime.hh" namespace blender::nodes::materialx { @@ -17,13 +19,15 @@ NodeParser::NodeParser(MaterialX::GraphElement *graph, const Material *material, const bNode *node, const bNodeSocket *socket_out, - NodeItem::Type to_type) + NodeItem::Type to_type, + GroupNodeParser *group_parser) : graph_(graph), depsgraph_(depsgraph), material_(material), node_(node), socket_out_(socket_out), - to_type_(to_type) + to_type_(to_type), + group_parser_(group_parser) { } @@ -52,7 +56,7 @@ NodeItem NodeParser::compute_full() return res; } -std::string NodeParser::node_name() +std::string NodeParser::node_name() const { std::string name = node_->name; if (node_->output_sockets().size() > 1) { @@ -61,7 +65,18 @@ std::string NodeParser::node_name() if (ELEM(to_type_, NodeItem::Type::BSDF, NodeItem::Type::EDF)) { name += "_" + NodeItem::type(to_type_); } +#ifdef USE_MATERIALX_NODEGRAPH return MaterialX::createValidName(name); +#else + std::string prefix; + GroupNodeParser *gr = group_parser_; + while (gr) { + const bNodeTree *ngroup = reinterpret_cast(gr->node_->id); + prefix = MaterialX::createValidName(ngroup->id.name) + "_" + prefix; + gr = gr->group_parser_; + } + return prefix + MaterialX::createValidName(name); +#endif } NodeItem NodeParser::create_node(const std::string &category, NodeItem::Type type) @@ -69,6 +84,16 @@ NodeItem NodeParser::create_node(const std::string &category, NodeItem::Type typ return empty().create_node(category, type); } +NodeItem NodeParser::create_input(const std::string &name, const NodeItem &item) +{ + return empty().create_input(name, item); +} + +NodeItem NodeParser::create_output(const std::string &name, const NodeItem &item) +{ + return empty().create_output(name, item); +} + NodeItem NodeParser::get_input_default(const std::string &name, NodeItem::Type to_type) { return get_default(node_->input_by_identifier(name), to_type); @@ -129,6 +154,9 @@ NodeItem NodeParser::get_default(const bNodeSocket &socket, NodeItem::Type to_ty { NodeItem res = empty(); switch (socket.type) { + case SOCK_CUSTOM: + /* Return empty */ + break; case SOCK_FLOAT: { float v = socket.default_value_typed()->value; res.value = MaterialX::Value::createValue(v); @@ -163,7 +191,7 @@ NodeItem NodeParser::get_input_link(const bNodeSocket &socket, NodeItem::Type to const bNode *from_node = link->fromnode; /* Passing NODE_REROUTE nodes */ - while (from_node->type == NODE_REROUTE) { + while (from_node->is_reroute()) { link = from_node->input_socket(0).link; if (!(link && link->is_used())) { return empty(); @@ -171,6 +199,17 @@ NodeItem NodeParser::get_input_link(const bNodeSocket &socket, NodeItem::Type to from_node = link->fromnode; } + if (from_node->is_group()) { + return GroupNodeParser( + graph_, depsgraph_, material_, from_node, link->fromsock, to_type, group_parser_) + .compute_full(); + } + if (from_node->is_group_input()) { + return GroupInputNodeParser( + graph_, depsgraph_, material_, from_node, link->fromsock, to_type, group_parser_) + .compute_full(); + } + if (!from_node->typeinfo->materialx_fn) { CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported node: %s [%d]", @@ -179,7 +218,7 @@ NodeItem NodeParser::get_input_link(const bNodeSocket &socket, NodeItem::Type to return empty(); } - NodeParserData data = {graph_, depsgraph_, material_, to_type, empty()}; + NodeParserData data = {graph_, depsgraph_, material_, to_type, group_parser_, empty()}; from_node->typeinfo->materialx_fn(&data, const_cast(from_node), link->fromsock); return data.result; } diff --git a/source/blender/nodes/shader/materialx/node_parser.h b/source/blender/nodes/shader/materialx/node_parser.h index 464c92c0e64..208b44320b1 100644 --- a/source/blender/nodes/shader/materialx/node_parser.h +++ b/source/blender/nodes/shader/materialx/node_parser.h @@ -16,6 +16,8 @@ namespace blender::nodes::materialx { extern struct CLG_LogRef *LOG_MATERIALX_SHADER; +class GroupNodeParser; + class NodeParser { protected: MaterialX::GraphElement *graph_; @@ -24,6 +26,7 @@ class NodeParser { const bNode *node_; const bNodeSocket *socket_out_; NodeItem::Type to_type_; + GroupNodeParser *group_parser_; public: NodeParser(MaterialX::GraphElement *graph, @@ -31,15 +34,18 @@ class NodeParser { const Material *material, const bNode *node, const bNodeSocket *socket_out, - NodeItem::Type to_type); + NodeItem::Type to_type, + GroupNodeParser *group_parser); virtual ~NodeParser() = default; virtual NodeItem compute() = 0; virtual NodeItem compute_full(); protected: - std::string node_name(); + std::string node_name() const; NodeItem create_node(const std::string &category, NodeItem::Type type); + NodeItem create_input(const std::string &name, const NodeItem &item); + NodeItem create_output(const std::string &name, const NodeItem &item); NodeItem get_input_default(const std::string &name, NodeItem::Type to_type); NodeItem get_input_default(int index, NodeItem::Type to_type); NodeItem get_output_default(const std::string &name, NodeItem::Type to_type); @@ -71,6 +77,7 @@ struct NodeParserData { const Depsgraph *depsgraph; const Material *material; NodeItem::Type to_type; + GroupNodeParser *group_parser; NodeItem result; }; @@ -91,7 +98,8 @@ struct NodeParserData { void node_shader_materialx(void *data, struct bNode *node, struct bNodeSocket *out) \ { \ materialx::NodeParserData *d = reinterpret_cast(data); \ - d->result = MaterialXNodeParser(d->graph, d->depsgraph, d->material, node, out, d->to_type) \ + d->result = MaterialXNodeParser( \ + d->graph, d->depsgraph, d->material, node, out, d->to_type, d->group_parser) \ .compute_full(); \ } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc index 5469569339b..d4467eb5765 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc @@ -64,17 +64,17 @@ NODE_SHADER_MATERIALX_BEGIN dielectric.set_input("scatter_mode", val(std::string("RT"))); NodeItem artistic_ior = create_node("artistic_ior", NodeItem::Type::Multioutput); - artistic_ior.add_output("ior", NodeItem::Type::Color3); - artistic_ior.add_output("extinction", NodeItem::Type::Color3); artistic_ior.set_input("reflectivity", color); artistic_ior.set_input("edge_color", color); + NodeItem ior_out = artistic_ior.add_output("ior", NodeItem::Type::Color3); + NodeItem extinction_out = artistic_ior.add_output("extinction", NodeItem::Type::Color3); NodeItem conductor = create_node("conductor_bsdf", NodeItem::Type::BSDF); if (normal) { conductor.set_input("normal", normal); } - conductor.set_input_output("ior", artistic_ior, "ior"); - conductor.set_input_output("extinction", artistic_ior, "extinction"); + conductor.set_input("ior", ior_out); + conductor.set_input("extinction", extinction_out); conductor.set_input("roughness", roughness); NodeItem res = create_node("mix", NodeItem::Type::BSDF); @@ -82,7 +82,7 @@ NODE_SHADER_MATERIALX_BEGIN res.set_input("bg", conductor); res.set_input("mix", val(0.5f)); - return res ; + return res; } #endif NODE_SHADER_MATERIALX_END diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc index 60956f9f0a2..b340b8e62a4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc @@ -70,10 +70,10 @@ NODE_SHADER_MATERIALX_BEGIN NodeItem tangent = get_input_link("Tangent", NodeItem::Type::Vector3); NodeItem artistic_ior = create_node("artistic_ior", NodeItem::Type::Multioutput); - artistic_ior.add_output("ior", NodeItem::Type::Color3); - artistic_ior.add_output("extinction", NodeItem::Type::Color3); artistic_ior.set_input("reflectivity", color); artistic_ior.set_input("edge_color", color); + NodeItem ior_out = artistic_ior.add_output("ior", NodeItem::Type::Color3); + NodeItem extinction_out = artistic_ior.add_output("extinction", NodeItem::Type::Color3); NodeItem res = create_node("conductor_bsdf", NodeItem::Type::BSDF); if (normal) { @@ -82,8 +82,8 @@ NODE_SHADER_MATERIALX_BEGIN if (tangent) { res.set_input("tangent", tangent); } - res.set_input_output("ior", artistic_ior, "ior"); - res.set_input_output("extinction", artistic_ior, "extinction"); + res.set_input("ior", ior_out); + res.set_input("extinction", extinction_out); res.set_input("roughness", roughness); return res; diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc index c3427ec9138..dfaf30c5e98 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc @@ -60,7 +60,7 @@ NODE_SHADER_MATERIALX_BEGIN res.set_input("roughness", roughness); res.set_input("ior", ior); res.set_input("scatter_mode", val(std::string("T"))); - + return res; } #endif diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 8fe27900be4..faa16e50cf8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -261,7 +261,7 @@ NODE_SHADER_MATERIALX_BEGIN #ifdef WITH_MATERIALX { NodeItem scale = get_input_value("Scale", NodeItem::Type::Float); - NodeItem detail = get_input_value("Detail", NodeItem::Type::Float); + NodeItem detail = get_input_default("Detail", NodeItem::Type::Float); NodeItem lacunarity = get_input_value("Lacunarity", NodeItem::Type::Float); NodeItem position = create_node("position", NodeItem::Type::Vector3); @@ -269,9 +269,7 @@ NODE_SHADER_MATERIALX_BEGIN NodeItem res = create_node("fractal3d", NodeItem::Type::Color3); res.set_input("position", position); - if (detail.value) { - res.set_input("octaves", val(int(detail.value->asA()))); - } + res.set_input("octaves", val(int(detail.value->asA()))); res.set_input("lacunarity", lacunarity); return res; }