/* SPDX-FileCopyrightText: 2011-2022 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "node_parser.h" #include "group_nodes.h" #include "BKE_node_runtime.hh" namespace blender::nodes::materialx { constexpr StringRef TEXCOORD_NODE_NAME = "node_texcoord"; CLG_LOGREF_DECLARE_GLOBAL(LOG_MATERIALX_SHADER, "materialx.shader"); NodeParser::NodeParser(NodeGraph &graph, const bNode *node, const bNodeSocket *socket_out, NodeItem::Type to_type, GroupNodeParser *group_parser) : graph_(graph), node_(node), socket_out_(socket_out), to_type_(to_type), group_parser_(group_parser) { } NodeItem NodeParser::compute_full() { NodeItem res = empty(); if (socket_out_ && !NodeItem::is_convertible((eNodeSocketDatatype)socket_out_->type, to_type_)) { return res; } /* Checking if node was already computed */ const std::string res_node_name = node_name(); res = graph_.get_node(res_node_name); if (!res.node) { CLOG_INFO(LOG_MATERIALX_SHADER, 1, "%s [%d] => %s", node_->name, node_->typeinfo->type_legacy, NodeItem::type(to_type_).c_str()); res = compute(); if (res.node) { res.node->setName(res_node_name); } } return res.convert(to_type_); } std::string NodeParser::node_name(const char *override_output_name) const { const NodeItem::Type to_type = ELEM(to_type_, NodeItem::Type::BSDF, NodeItem::Type::EDF, NodeItem::Type::SurfaceOpacity) ? to_type_ : NodeItem::Type::Empty; const StringRef socket_out_name = (override_output_name) ? override_output_name : (socket_out_) ? socket_out_->name : ""; return graph_.unique_node_name(node_, socket_out_name, to_type); } NodeItem NodeParser::create_node(const std::string &category, NodeItem::Type type) { return empty().create_node(category, type); } NodeItem NodeParser::create_node(const std::string &category, NodeItem::Type type, const NodeItem::Inputs &inputs) { return empty().create_node(category, type, inputs); } 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); } NodeItem NodeParser::get_input_default(int index, NodeItem::Type to_type) { return get_default(node_->input_socket(index), to_type); } NodeItem NodeParser::get_input_link(const std::string &name, NodeItem::Type to_type) { return get_input_link(node_->input_by_identifier(name), to_type, false); } NodeItem NodeParser::get_input_link(int index, NodeItem::Type to_type) { return get_input_link(node_->input_socket(index), to_type, false); } NodeItem NodeParser::get_input_value(const std::string &name, NodeItem::Type to_type) { return get_input_value(node_->input_by_identifier(name), to_type); } NodeItem NodeParser::get_input_value(int index, NodeItem::Type to_type) { return get_input_value(node_->input_socket(index), to_type); } NodeItem NodeParser::get_output_default(const std::string &name, NodeItem::Type to_type) { return get_default(node_->output_by_identifier(name), to_type); } NodeItem NodeParser::get_output_default(int index, NodeItem::Type to_type) { return get_default(node_->output_socket(index), to_type); } NodeItem NodeParser::empty() const { return graph_.empty_node(); } NodeItem NodeParser::texcoord_node(NodeItem::Type type, const std::string &attribute_name) { BLI_assert(ELEM(type, NodeItem::Type::Vector2, NodeItem::Type::Vector3)); std::string name = TEXCOORD_NODE_NAME; if (type == NodeItem::Type::Vector3) { name += "_vector3"; } NodeItem res = graph_.get_node(name); if (!res.node) { /* TODO: Use "Pref" generated texture coordinates for 3D, but needs * work in USD and Hydra mesh export. */ const bool is_active_uvmap = attribute_name == "" || attribute_name == graph_.export_params.original_active_uvmap_name; if (graph_.export_params.new_active_uvmap_name == "st" && is_active_uvmap) { res = create_node("texcoord", type); } else { const std::string &geomprop = (is_active_uvmap) ? graph_.export_params.new_active_uvmap_name : attribute_name; res = create_node("geompropvalue", type, {{"geomprop", val(geomprop)}}); } res.node->setName(name); } return res; } NodeItem NodeParser::get_default(const bNodeSocket &socket, NodeItem::Type to_type) { NodeItem res = empty(); if (!NodeItem::is_arithmetic(to_type) && to_type != NodeItem::Type::Any) { return res; } 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); break; } case SOCK_VECTOR: { const float *v = socket.default_value_typed()->value; res.value = MaterialX::Value::createValue( MaterialX::Vector3(v[0], v[1], v[2])); break; } case SOCK_RGBA: { const float *v = socket.default_value_typed()->value; res.value = MaterialX::Value::createValue( MaterialX::Color4(v[0], v[1], v[2], v[3])); break; } default: { CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported socket type: %d", socket.type); } } return res.convert(to_type); } NodeItem NodeParser::get_input_link(const bNodeSocket &socket, NodeItem::Type to_type, bool use_group_default) { const bNodeLink *link = socket.link; if (!(link && link->is_used())) { return empty(); } const bNode *from_node = link->fromnode; /* Passing reroute nodes. */ while (from_node->is_reroute()) { link = from_node->input_socket(0).link; if (!(link && link->is_used())) { return empty(); } from_node = link->fromnode; } if (from_node->is_group()) { return GroupNodeParser( graph_, from_node, link->fromsock, to_type, group_parser_, use_group_default) .compute_full(); } if (from_node->is_group_input()) { return GroupInputNodeParser( graph_, from_node, link->fromsock, to_type, group_parser_, use_group_default) .compute_full(); } if (!from_node->typeinfo->materialx_fn) { CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported node: %s [%d]", from_node->name, from_node->typeinfo->type_legacy); return empty(); } NodeParserData data = {graph_, to_type, group_parser_, empty()}; from_node->typeinfo->materialx_fn(&data, const_cast(from_node), link->fromsock); return data.result; } NodeItem NodeParser::get_input_value(const bNodeSocket &socket, NodeItem::Type to_type) { NodeItem res = get_input_link(socket, to_type, true); if (!res) { res = get_default(socket, to_type); } return res; } } // namespace blender::nodes::materialx