diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 1dec91b8b3ad..114ab576101d 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -162,6 +162,7 @@ if(WITH_MATERIALX) materialx/material.h materialx/nodes/node_item.h materialx/nodes/node_parser.h + materialx/nodes/output_material.h ) list(APPEND LIB MaterialXCore diff --git a/source/blender/nodes/shader/materialx/material.cc b/source/blender/nodes/shader/materialx/material.cc index 502ea74f5fd8..1e88124b3570 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/node_parser.h" +#include "nodes/output_material.h" #include @@ -17,43 +17,27 @@ namespace blender::nodes::materialx { CLG_LOGREF_DECLARE_GLOBAL(LOG_MATERIALX_SHADER, "materialx.shader"); -static void export_nodegraph(MaterialX::GraphElement *graph, - Depsgraph *depsgraph, - Material *material) +MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, + Material *material, + const std::string &socket_name) { - material->nodetree->ensure_topology_cache(); + CLOG_INFO(LOG_MATERIALX_SHADER, 0, "Material: %s", material->id.name); - bNode *output_node = ntreeShaderOutputNode(material->nodetree, SHD_OUTPUT_ALL); - OutputMaterialNodeParser parser(graph, depsgraph, material, output_node); - parser.compute(); -} - -static void create_standard_surface(MaterialX::GraphElement *graph, Material *material) -{ - MaterialX::NodePtr standard_surface = graph->addNode( - "standard_surface", MaterialX::EMPTY_STRING, "surfaceshader"); - - standard_surface->addInput("base", "float")->setValue(1.0); - standard_surface->addInput("base_color", "color3") - ->setValue(MaterialX::Color3(material->r, material->g, material->b)); - - MaterialX::NodePtr surfacematerial = graph->addNode( - "surfacematerial", MaterialX::EMPTY_STRING, "material"); - surfacematerial->addInput(standard_surface->getType(), standard_surface->getType()) - ->setNodeName(standard_surface->getName()); -} - -MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, Material *material) -{ MaterialX::DocumentPtr doc = MaterialX::createDocument(); if (material->use_nodes) { - export_nodegraph(doc.get(), depsgraph, material); + material->nodetree->ensure_topology_cache(); + bNode *output_node = ntreeShaderOutputNode(material->nodetree, SHD_OUTPUT_ALL); + OutputMaterialNodeParser(doc.get(), depsgraph, material, output_node).compute(socket_name); } else { - create_standard_surface(doc.get(), material); + OutputMaterialNodeParser(doc.get(), depsgraph, material, nullptr).compute_default(); } - std::string str = MaterialX::writeToXmlString(doc); - CLOG_INFO(LOG_MATERIALX_SHADER, 1, "Material: %s\n%s", material->id.name, str.c_str()); + + CLOG_INFO(LOG_MATERIALX_SHADER, + 2, + "Material: %s\n%s", + material->id.name, + MaterialX::writeToXmlString(doc).c_str()); return doc; } diff --git a/source/blender/nodes/shader/materialx/material.h b/source/blender/nodes/shader/materialx/material.h index b32f3ff2962a..4c90febce6c9 100644 --- a/source/blender/nodes/shader/materialx/material.h +++ b/source/blender/nodes/shader/materialx/material.h @@ -15,6 +15,8 @@ namespace blender::nodes::materialx { extern struct CLG_LogRef *LOG_MATERIALX_SHADER; -MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, Material *material); +MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, + Material *material, + const std::string &socket_name = "Surface"); } // 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 5a5e9f7c053d..43a046cc9292 100644 --- a/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc +++ b/source/blender/nodes/shader/materialx/nodes/bsdf_principled.cc @@ -68,7 +68,7 @@ NodeItem BSDFPrincipledNodeParser::compute() NodeItem tangent = get_input_link("Tangent"); /* Creating standard_surface */ - NodeItem res = create_node("standard_surface", "surfaceshader", false); + NodeItem res = create_node("standard_surface", "surfaceshader"); res.set_input("base", 1.0, "float"); res.set_input("base_color", base_color.to_color3()); res.set_input("diffuse_roughness", roughness); @@ -90,7 +90,9 @@ NodeItem BSDFPrincipledNodeParser::compute() res.set_input("specular_IOR", ior); if (anisotropic) { res.set_input("specular_anisotropy", anisotropic); - res.set_input("specular_rotation", anisotropic_rotation); + if (anisotropic_rotation) { + res.set_input("specular_rotation", anisotropic_rotation); + } } } diff --git a/source/blender/nodes/shader/materialx/nodes/math.cc b/source/blender/nodes/shader/materialx/nodes/math.cc index 8787bf2ca390..cbd5d6fd023e 100644 --- a/source/blender/nodes/shader/materialx/nodes/math.cc +++ b/source/blender/nodes/shader/materialx/nodes/math.cc @@ -10,7 +10,7 @@ namespace blender::nodes::materialx { NodeItem MathNodeParser::compute() { /* TODO: finish some math operations */ - auto op = node->custom1; + auto op = node_->custom1; NodeItem res = empty(); /* Single operand operations */ @@ -158,7 +158,7 @@ NodeItem MathNodeParser::compute() } } - bool clamp_output = node->custom2 != 0; + bool clamp_output = node_->custom2 != 0; if (clamp_output && res) { res = res.clamp(); } diff --git a/source/blender/nodes/shader/materialx/nodes/node_parser.cc b/source/blender/nodes/shader/materialx/nodes/node_parser.cc index 2c8974406d9b..0b7c6e82ad60 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_parser.cc +++ b/source/blender/nodes/shader/materialx/nodes/node_parser.cc @@ -12,56 +12,59 @@ namespace blender::nodes::materialx { NodeParser::NodeParser(MaterialX::GraphElement *graph, const Depsgraph *depsgraph, const Material *material, - const bNode *node) - : graph(graph), depsgraph(depsgraph), material(material), node(node) + const bNode *node, + const bNodeSocket *socket_out) + : graph_(graph), depsgraph_(depsgraph), material_(material), node_(node), socket_out_(socket_out) { } -NodeItem NodeParser::create_node(const std::string &mx_category, - const std::string &mx_type, - bool noname) +std::string NodeParser::node_name(const bNode *node, const bNodeSocket *socket_out) +{ + 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) { NodeItem res = empty(); - res.node = graph->addNode(mx_category, - noname ? MaterialX::EMPTY_STRING : - MaterialX::createValidName(node->name), - mx_type); + res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, mx_type); return res; } NodeItem NodeParser::get_input_default(const std::string &name) { - return get_input_default(node->input_by_identifier(name)); + return get_input_default(node_->input_by_identifier(name)); } NodeItem NodeParser::get_input_default(int index) { - return get_input_default(node->input_socket(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)); + return get_input_link(node_->input_by_identifier(name)); } NodeItem NodeParser::get_input_link(int index) { - return get_input_link(node->input_socket(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)); + return get_input_value(node_->input_by_identifier(name)); } NodeItem NodeParser::get_input_value(int index) { - return get_input_value(node->input_socket(index)); + return get_input_value(node_->input_socket(index)); } NodeItem NodeParser::empty() const { - return NodeItem(graph); + return NodeItem(graph_); } NodeItem NodeParser::get_input_default(const bNodeSocket &socket) @@ -98,50 +101,46 @@ NodeItem NodeParser::get_input_link(const bNodeSocket &socket) return res; } - const bNode *in_node = link->fromnode; + const bNode *from_node = link->fromnode; /* Passing NODE_REROUTE nodes */ - while (in_node->type == NODE_REROUTE) { - link = in_node->input_socket(0).link; + while (from_node->type == NODE_REROUTE) { + link = from_node->input_socket(0).link; if (!(link && link->is_used())) { return res; } - in_node = link->fromnode; + from_node = link->fromnode; } - /* Getting required NodeParser object */ - std::unique_ptr parser; - switch (in_node->typeinfo->type) { - 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; - case SH_NODE_TEX_ENVIRONMENT: - parser = std::make_unique(graph, depsgraph, material, in_node); - break; - case SH_NODE_TEX_NOISE: - parser = std::make_unique(graph, depsgraph, material, in_node); - break; - case SH_NODE_TEX_CHECKER: - parser = std::make_unique(graph, depsgraph, material, in_node); - break; + /* Checking if node was already computed */ + res.node = graph_->getNode(node_name(from_node, link->fromsock)); + if (res.node) { + return res; + } + + /* 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(); \ + break; + + switch (from_node->typeinfo->type) { + CASE_NODE_TYPE(SH_NODE_BSDF_PRINCIPLED, BSDFPrincipledNodeParser) + CASE_NODE_TYPE(SH_NODE_INVERT, InvertNodeParser) + CASE_NODE_TYPE(SH_NODE_MATH, MathNodeParser) + CASE_NODE_TYPE(SH_NODE_MIX_RGB_LEGACY, MixRGBNodeParser) + CASE_NODE_TYPE(SH_NODE_TEX_CHECKER, TexCheckerNodeParser) + CASE_NODE_TYPE(SH_NODE_TEX_ENVIRONMENT, TexEnvironmentNodeParser) + CASE_NODE_TYPE(SH_NODE_TEX_IMAGE, TexImageNodeParser) + CASE_NODE_TYPE(SH_NODE_TEX_NOISE, TexNoiseNodeParser) + default: - CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported node: %s (%d)", in_node->name, in_node->type); - return res; + CLOG_WARN(LOG_MATERIALX_SHADER, + "Unsupported node: %s [%d]", + from_node->name, + from_node->typeinfo->type); } - res = parser->compute(); return res; } @@ -154,4 +153,14 @@ NodeItem NodeParser::get_input_value(const bNodeSocket &socket) return res; } +NodeItem NodeParser::compute_full() +{ + CLOG_INFO(LOG_MATERIALX_SHADER, 1, "%s [%d]", node_->name, node_->typeinfo->type); + NodeItem res = compute(); + if (res.node) { + res.node->setName(node_name(node_, socket_out_)); + } + 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 86d1935e41fd..bb9986896d99 100644 --- a/source/blender/nodes/shader/materialx/nodes/node_parser.h +++ b/source/blender/nodes/shader/materialx/nodes/node_parser.h @@ -13,25 +13,25 @@ namespace blender::nodes::materialx { class NodeParser { - public: - MaterialX::GraphElement *graph; - const Depsgraph *depsgraph; - const Material *material; - const bNode *node; + protected: + MaterialX::GraphElement *graph_; + const Depsgraph *depsgraph_; + const Material *material_; + const bNode *node_; + const bNodeSocket *socket_out_; public: NodeParser(MaterialX::GraphElement *graph, const Depsgraph *depsgraph, const Material *material, - const bNode *node); + const bNode *node, + const bNodeSocket *socket_out); virtual ~NodeParser() = default; - virtual NodeItem compute() = 0; protected: - NodeItem create_node(const std::string &mx_category, - const std::string &mx_type, - bool noname = true); + static std::string node_name(const bNode *node, const bNodeSocket *socket_out); + 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); @@ -45,6 +45,7 @@ class NodeParser { NodeItem get_input_default(const bNodeSocket &socket); NodeItem get_input_link(const bNodeSocket &socket); NodeItem get_input_value(const bNodeSocket &socket); + NodeItem compute_full(); }; template NodeItem NodeParser::value(const T &data) const @@ -63,7 +64,6 @@ DECLARE_PARSER(BSDFPrincipledNodeParser) DECLARE_PARSER(InvertNodeParser) DECLARE_PARSER(MathNodeParser) DECLARE_PARSER(MixRGBNodeParser) -DECLARE_PARSER(OutputMaterialNodeParser) DECLARE_PARSER(TexCheckerNodeParser) DECLARE_PARSER(TexEnvironmentNodeParser) DECLARE_PARSER(TexImageNodeParser) diff --git a/source/blender/nodes/shader/materialx/nodes/output_material.cc b/source/blender/nodes/shader/materialx/nodes/output_material.cc index 81d00737027d..5b5d9d0b7c66 100644 --- a/source/blender/nodes/shader/materialx/nodes/output_material.cc +++ b/source/blender/nodes/shader/materialx/nodes/output_material.cc @@ -2,19 +2,56 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "node_parser.h" +#include "output_material.h" namespace blender::nodes::materialx { +OutputMaterialNodeParser::OutputMaterialNodeParser(MaterialX::GraphElement *graph, + const Depsgraph *depsgraph, + const Material *material, + const bNode *node) + : NodeParser(graph, depsgraph, material, node, nullptr) +{ +} NodeItem OutputMaterialNodeParser::compute() { - NodeItem node = empty(); - NodeItem surface = get_input_link("Surface"); - if (surface) { - node = create_node("surfacematerial", "material", false); - node.set_input("surfaceshader", surface); + return empty(); +} + +NodeItem OutputMaterialNodeParser::compute(const std::string &socket_name) +{ + NodeItem surface = empty(); + if (node_) { + surface = get_input_link(socket_name); } - return node; + 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; +} + +NodeItem OutputMaterialNodeParser::compute_default() +{ + NodeItem surface = create_node("standard_surface", "surfaceshader"); + surface.set_input("base_color", value(MaterialX::Color3(material_->r, material_->g, material_->b))); + surface.set_input("diffuse_roughness", value(material_->roughness)); + if (material_->metallic > 0.0f) { + surface.set_input("metalness", value(material_->metallic)); + } + if (material_->spec) { + surface.set_input("specular", value(material_->spec)); + surface.set_input("specular_color", value(material_->spec)); + surface.set_input("specular_roughness", value(material_->roughness)); + } + + NodeItem res = create_node("surfacematerial", "material"); + res.node->setName("Material_Default"); + res.set_input("surfaceshader", surface); + return res; } } // 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 new file mode 100644 index 000000000000..986c926f33d4 --- /dev/null +++ b/source/blender/nodes/shader/materialx/nodes/output_material.h @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "node_parser.h" + +namespace blender::nodes::materialx { + +class OutputMaterialNodeParser : public NodeParser { + public: + OutputMaterialNodeParser(MaterialX::GraphElement *graph, + const Depsgraph *depsgraph, + const Material *material, + const bNode *node); + NodeItem compute() override; + NodeItem compute(const std::string &socket_name); + NodeItem compute_default(); +}; + +} // 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 f04f8ed2c457..63266bd5081a 100644 --- a/source/blender/nodes/shader/materialx/nodes/tex_checker.cc +++ b/source/blender/nodes/shader/materialx/nodes/tex_checker.cc @@ -16,16 +16,11 @@ NodeItem TexCheckerNodeParser::compute() float v = scale.value->asA(); scale = value(MaterialX::Vector2(v, v)); } - /* Modifier to follow Cycles result */ - scale = scale * value(4.0f); NodeItem texcoord = create_node("texcoord", "vector2"); NodeItem place2d = create_node("place2d", "vector2"); place2d.set_input("texcoord", texcoord * scale); - /* TODO: fix offset: - * place2d.set_input("offset", offset); */ - NodeItem separate = create_node("separate2", "multioutput"); separate.set_input("in", place2d); separate.add_output("outx", "float"); @@ -42,9 +37,9 @@ NodeItem TexCheckerNodeParser::compute() NodeItem ifequal = (modulo_x.floor() + modulo_y.floor()).if_else("==", value(1.0f), value(0.0f), value(1.0f)); - NodeItem res = create_node("mix", "color3", false); - res.set_input("fg", color1.to_color3()); - res.set_input("bg", color2.to_color3()); + NodeItem res = create_node("mix", "color3"); + res.set_input("bg", color1.to_color3()); + res.set_input("fg", color2.to_color3()); res.set_input("mix", ifequal); return res; } diff --git a/source/blender/nodes/shader/materialx/nodes/tex_environment.cc b/source/blender/nodes/shader/materialx/nodes/tex_environment.cc index a6048db0412c..f2aede4cb4b6 100644 --- a/source/blender/nodes/shader/materialx/nodes/tex_environment.cc +++ b/source/blender/nodes/shader/materialx/nodes/tex_environment.cc @@ -12,10 +12,10 @@ namespace blender::nodes::materialx { NodeItem TexEnvironmentNodeParser::compute() { - Image *image = (Image *)node->id; - NodeTexEnvironment *tex = static_cast(node->storage); - Scene *scene = DEG_get_input_scene(depsgraph); - Main *bmain = DEG_get_bmain(depsgraph); + Image *image = (Image *)node_->id; + NodeTexEnvironment *tex = static_cast(node_->storage); + Scene *scene = DEG_get_input_scene(depsgraph_); + Main *bmain = DEG_get_bmain(depsgraph_); std::string image_path; /* TODO: What if Blender built without Hydra? Also io::hydra::cache_or_get_image_file contains * pretty general code, so could be moved from bf_usd project. */ @@ -24,7 +24,7 @@ NodeItem TexEnvironmentNodeParser::compute() #endif NodeItem texcoord = create_node("texcoord", "vector2"); - NodeItem res = create_node("image", "color3", false); + NodeItem res = create_node("image", "color3"); res.set_input("file", image_path, "filename"); res.set_input("texcoord", texcoord); return res; diff --git a/source/blender/nodes/shader/materialx/nodes/tex_image.cc b/source/blender/nodes/shader/materialx/nodes/tex_image.cc index 9d0d3d033195..2a2625259ef7 100644 --- a/source/blender/nodes/shader/materialx/nodes/tex_image.cc +++ b/source/blender/nodes/shader/materialx/nodes/tex_image.cc @@ -12,10 +12,10 @@ namespace blender::nodes::materialx { NodeItem TexImageNodeParser::compute() { - Image *image = (Image *)node->id; - NodeTexImage *tex = static_cast(node->storage); - Scene *scene = DEG_get_input_scene(depsgraph); - Main *bmain = DEG_get_bmain(depsgraph); + Image *image = (Image *)node_->id; + NodeTexImage *tex = static_cast(node_->storage); + Scene *scene = DEG_get_input_scene(depsgraph_); + Main *bmain = DEG_get_bmain(depsgraph_); std::string image_path; /* TODO: What if Blender built without Hydra? Also io::hydra::cache_or_get_image_file contains * pretty general code, so could be moved from bf_usd project. */ @@ -24,7 +24,7 @@ NodeItem TexImageNodeParser::compute() #endif NodeItem texcoord = create_node("texcoord", "vector2"); - NodeItem res = create_node("image", "color3", false); + NodeItem res = create_node("image", "color3"); res.set_input("file", image_path, "filename"); res.set_input("texcoord", texcoord); return res; diff --git a/source/blender/nodes/shader/materialx/nodes/tex_noise.cc b/source/blender/nodes/shader/materialx/nodes/tex_noise.cc index 63268d1fd8c6..d3a016c63208 100644 --- a/source/blender/nodes/shader/materialx/nodes/tex_noise.cc +++ b/source/blender/nodes/shader/materialx/nodes/tex_noise.cc @@ -18,7 +18,7 @@ NodeItem TexNoiseNodeParser::compute() NodeItem texcoord = create_node("position", "vector3"); - NodeItem res = create_node("fractal3d", "color3", false); + NodeItem res = create_node("fractal3d", "color3"); res.set_input("position", texcoord * scale); res.set_input("octaves", detail); res.set_input("lacunarity", lacunarity);