matx-code-improvements #8

Merged
Bogdan Nagirniak merged 6 commits from BogdanNagirniak/blender:matx-code-improvements into matx-export-material 2023-09-04 10:10:09 +02:00
13 changed files with 175 additions and 123 deletions

View File

@ -162,6 +162,7 @@ if(WITH_MATERIALX)
materialx/material.h materialx/material.h
materialx/nodes/node_item.h materialx/nodes/node_item.h
materialx/nodes/node_parser.h materialx/nodes/node_parser.h
materialx/nodes/output_material.h
) )
list(APPEND LIB list(APPEND LIB
MaterialXCore MaterialXCore

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */ * SPDX-License-Identifier: GPL-2.0-or-later */
#include "material.h" #include "material.h"
#include "nodes/node_parser.h" #include "nodes/output_material.h"
#include <MaterialXFormat/XmlIo.h> #include <MaterialXFormat/XmlIo.h>
@ -17,43 +17,27 @@ namespace blender::nodes::materialx {
CLG_LOGREF_DECLARE_GLOBAL(LOG_MATERIALX_SHADER, "materialx.shader"); CLG_LOGREF_DECLARE_GLOBAL(LOG_MATERIALX_SHADER, "materialx.shader");
static void export_nodegraph(MaterialX::GraphElement *graph, MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph,
Depsgraph *depsgraph, Material *material,
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(); MaterialX::DocumentPtr doc = MaterialX::createDocument();
if (material->use_nodes) { 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 { 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; return doc;
} }

View File

@ -15,6 +15,8 @@ namespace blender::nodes::materialx {
extern struct CLG_LogRef *LOG_MATERIALX_SHADER; 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 } // namespace blender::nodes::materialx

View File

@ -68,7 +68,7 @@ NodeItem BSDFPrincipledNodeParser::compute()
NodeItem tangent = get_input_link("Tangent"); NodeItem tangent = get_input_link("Tangent");
/* Creating standard_surface */ /* 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", 1.0, "float");
res.set_input("base_color", base_color.to_color3()); res.set_input("base_color", base_color.to_color3());
res.set_input("diffuse_roughness", roughness); res.set_input("diffuse_roughness", roughness);
@ -90,9 +90,11 @@ NodeItem BSDFPrincipledNodeParser::compute()
res.set_input("specular_IOR", ior); res.set_input("specular_IOR", ior);
if (anisotropic) { if (anisotropic) {
res.set_input("specular_anisotropy", anisotropic); res.set_input("specular_anisotropy", anisotropic);
if (anisotropic_rotation) {
res.set_input("specular_rotation", anisotropic_rotation); res.set_input("specular_rotation", anisotropic_rotation);
} }
} }
}
if (transmission != zero) { if (transmission != zero) {
res.set_input("transmission", transmission); res.set_input("transmission", transmission);

View File

@ -10,7 +10,7 @@ namespace blender::nodes::materialx {
NodeItem MathNodeParser::compute() NodeItem MathNodeParser::compute()
{ {
/* TODO: finish some math operations */ /* TODO: finish some math operations */
auto op = node->custom1; auto op = node_->custom1;
NodeItem res = empty(); NodeItem res = empty();
/* Single operand operations */ /* 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) { if (clamp_output && res) {
res = res.clamp(); res = res.clamp();
} }

View File

@ -12,56 +12,59 @@ namespace blender::nodes::materialx {
NodeParser::NodeParser(MaterialX::GraphElement *graph, NodeParser::NodeParser(MaterialX::GraphElement *graph,
const Depsgraph *depsgraph, const Depsgraph *depsgraph,
const Material *material, const Material *material,
const bNode *node) const bNode *node,
: graph(graph), depsgraph(depsgraph), material(material), node(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, std::string NodeParser::node_name(const bNode *node, const bNodeSocket *socket_out)
const std::string &mx_type, {
bool noname) 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(); NodeItem res = empty();
res.node = graph->addNode(mx_category, res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, mx_type);
noname ? MaterialX::EMPTY_STRING :
MaterialX::createValidName(node->name),
mx_type);
return res; return res;
} }
NodeItem NodeParser::get_input_default(const std::string &name) 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) 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) 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) 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) 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) 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 NodeItem NodeParser::empty() const
{ {
return NodeItem(graph); return NodeItem(graph_);
} }
NodeItem NodeParser::get_input_default(const bNodeSocket &socket) NodeItem NodeParser::get_input_default(const bNodeSocket &socket)
@ -98,50 +101,46 @@ NodeItem NodeParser::get_input_link(const bNodeSocket &socket)
return res; return res;
} }
const bNode *in_node = link->fromnode; const bNode *from_node = link->fromnode;
/* Passing NODE_REROUTE nodes */ /* Passing NODE_REROUTE nodes */
while (in_node->type == NODE_REROUTE) { while (from_node->type == NODE_REROUTE) {
link = in_node->input_socket(0).link; link = from_node->input_socket(0).link;
if (!(link && link->is_used())) { if (!(link && link->is_used())) {
return res; return res;
} }
in_node = link->fromnode; from_node = link->fromnode;
} }
/* Getting required NodeParser object */ /* Checking if node was already computed */
std::unique_ptr<NodeParser> parser; res.node = graph_->getNode(node_name(from_node, link->fromsock));
switch (in_node->typeinfo->type) { if (res.node) {
case SH_NODE_BSDF_PRINCIPLED:
parser = std::make_unique<BSDFPrincipledNodeParser>(graph, depsgraph, material, in_node);
break;
case SH_NODE_INVERT:
parser = std::make_unique<InvertNodeParser>(graph, depsgraph, material, in_node);
break;
case SH_NODE_MATH:
parser = std::make_unique<MathNodeParser>(graph, depsgraph, material, in_node);
break;
case SH_NODE_MIX_RGB_LEGACY:
parser = std::make_unique<MixRGBNodeParser>(graph, depsgraph, material, in_node);
break;
case SH_NODE_TEX_IMAGE:
parser = std::make_unique<TexImageNodeParser>(graph, depsgraph, material, in_node);
break;
case SH_NODE_TEX_ENVIRONMENT:
parser = std::make_unique<TexEnvironmentNodeParser>(graph, depsgraph, material, in_node);
break;
case SH_NODE_TEX_NOISE:
parser = std::make_unique<TexNoiseNodeParser>(graph, depsgraph, material, in_node);
break;
case SH_NODE_TEX_CHECKER:
parser = std::make_unique<TexCheckerNodeParser>(graph, depsgraph, material, in_node);
break;
default:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported node: %s (%d)", in_node->name, in_node->type);
return res; return res;
} }
res = parser->compute(); /* 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]",
from_node->name,
from_node->typeinfo->type);
}
return res; return res;
} }
@ -154,4 +153,14 @@ NodeItem NodeParser::get_input_value(const bNodeSocket &socket)
return res; 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 } // namespace blender::nodes::materialx

View File

@ -13,25 +13,25 @@
namespace blender::nodes::materialx { namespace blender::nodes::materialx {
class NodeParser { class NodeParser {
public: protected:
MaterialX::GraphElement *graph; MaterialX::GraphElement *graph_;
const Depsgraph *depsgraph; const Depsgraph *depsgraph_;
const Material *material; const Material *material_;
const bNode *node; const bNode *node_;
const bNodeSocket *socket_out_;
public: public:
NodeParser(MaterialX::GraphElement *graph, NodeParser(MaterialX::GraphElement *graph,
const Depsgraph *depsgraph, const Depsgraph *depsgraph,
const Material *material, const Material *material,
const bNode *node); const bNode *node,
const bNodeSocket *socket_out);
virtual ~NodeParser() = default; virtual ~NodeParser() = default;
virtual NodeItem compute() = 0; virtual NodeItem compute() = 0;
protected: protected:
NodeItem create_node(const std::string &mx_category, static std::string node_name(const bNode *node, const bNodeSocket *socket_out);
const std::string &mx_type, NodeItem create_node(const std::string &mx_category, const std::string &mx_type);
bool noname = true);
NodeItem get_input_default(const std::string &name); NodeItem get_input_default(const std::string &name);
NodeItem get_input_default(int index); NodeItem get_input_default(int index);
NodeItem get_input_link(const std::string &name); NodeItem get_input_link(const std::string &name);
@ -45,6 +45,7 @@ class NodeParser {
NodeItem get_input_default(const bNodeSocket &socket); NodeItem get_input_default(const bNodeSocket &socket);
NodeItem get_input_link(const bNodeSocket &socket); NodeItem get_input_link(const bNodeSocket &socket);
NodeItem get_input_value(const bNodeSocket &socket); NodeItem get_input_value(const bNodeSocket &socket);
NodeItem compute_full();
}; };
template<class T> NodeItem NodeParser::value(const T &data) const template<class T> NodeItem NodeParser::value(const T &data) const
@ -63,7 +64,6 @@ DECLARE_PARSER(BSDFPrincipledNodeParser)
DECLARE_PARSER(InvertNodeParser) DECLARE_PARSER(InvertNodeParser)
DECLARE_PARSER(MathNodeParser) DECLARE_PARSER(MathNodeParser)
DECLARE_PARSER(MixRGBNodeParser) DECLARE_PARSER(MixRGBNodeParser)
DECLARE_PARSER(OutputMaterialNodeParser)
DECLARE_PARSER(TexCheckerNodeParser) DECLARE_PARSER(TexCheckerNodeParser)
DECLARE_PARSER(TexEnvironmentNodeParser) DECLARE_PARSER(TexEnvironmentNodeParser)
DECLARE_PARSER(TexImageNodeParser) DECLARE_PARSER(TexImageNodeParser)

View File

@ -2,19 +2,56 @@
* *
* SPDX-License-Identifier: GPL-2.0-or-later */ * SPDX-License-Identifier: GPL-2.0-or-later */
#include "node_parser.h" #include "output_material.h"
namespace blender::nodes::materialx { 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 OutputMaterialNodeParser::compute()
{ {
NodeItem node = empty(); return empty();
NodeItem surface = get_input_link("Surface");
if (surface) {
node = create_node("surfacematerial", "material", false);
node.set_input("surfaceshader", surface);
} }
return node;
NodeItem OutputMaterialNodeParser::compute(const std::string &socket_name)
{
NodeItem surface = empty();
if (node_) {
surface = get_input_link(socket_name);
}
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 } // namespace blender::nodes::materialx

View File

@ -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

View File

@ -16,16 +16,11 @@ NodeItem TexCheckerNodeParser::compute()
float v = scale.value->asA<float>(); float v = scale.value->asA<float>();
scale = value(MaterialX::Vector2(v, v)); scale = value(MaterialX::Vector2(v, v));
} }
/* Modifier to follow Cycles result */
scale = scale * value(4.0f);
NodeItem texcoord = create_node("texcoord", "vector2"); NodeItem texcoord = create_node("texcoord", "vector2");
NodeItem place2d = create_node("place2d", "vector2"); NodeItem place2d = create_node("place2d", "vector2");
place2d.set_input("texcoord", texcoord * scale); place2d.set_input("texcoord", texcoord * scale);
/* TODO: fix offset:
* place2d.set_input("offset", offset); */
NodeItem separate = create_node("separate2", "multioutput"); NodeItem separate = create_node("separate2", "multioutput");
separate.set_input("in", place2d); separate.set_input("in", place2d);
separate.add_output("outx", "float"); separate.add_output("outx", "float");
@ -42,9 +37,9 @@ NodeItem TexCheckerNodeParser::compute()
NodeItem ifequal = NodeItem ifequal =
(modulo_x.floor() + modulo_y.floor()).if_else("==", value(1.0f), value(0.0f), value(1.0f)); (modulo_x.floor() + modulo_y.floor()).if_else("==", value(1.0f), value(0.0f), value(1.0f));
NodeItem res = create_node("mix", "color3", false); NodeItem res = create_node("mix", "color3");
res.set_input("fg", color1.to_color3()); res.set_input("bg", color1.to_color3());
res.set_input("bg", color2.to_color3()); res.set_input("fg", color2.to_color3());
res.set_input("mix", ifequal); res.set_input("mix", ifequal);
return res; return res;
} }

View File

@ -12,10 +12,10 @@ namespace blender::nodes::materialx {
NodeItem TexEnvironmentNodeParser::compute() NodeItem TexEnvironmentNodeParser::compute()
{ {
Image *image = (Image *)node->id; Image *image = (Image *)node_->id;
NodeTexEnvironment *tex = static_cast<NodeTexEnvironment *>(node->storage); NodeTexEnvironment *tex = static_cast<NodeTexEnvironment *>(node_->storage);
Scene *scene = DEG_get_input_scene(depsgraph); Scene *scene = DEG_get_input_scene(depsgraph_);
Main *bmain = DEG_get_bmain(depsgraph); Main *bmain = DEG_get_bmain(depsgraph_);
std::string image_path; std::string image_path;
/* TODO: What if Blender built without Hydra? Also io::hydra::cache_or_get_image_file contains /* 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. */ * pretty general code, so could be moved from bf_usd project. */
@ -24,7 +24,7 @@ NodeItem TexEnvironmentNodeParser::compute()
#endif #endif
NodeItem texcoord = create_node("texcoord", "vector2"); 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("file", image_path, "filename");
res.set_input("texcoord", texcoord); res.set_input("texcoord", texcoord);
return res; return res;

View File

@ -12,10 +12,10 @@ namespace blender::nodes::materialx {
NodeItem TexImageNodeParser::compute() NodeItem TexImageNodeParser::compute()
{ {
Image *image = (Image *)node->id; Image *image = (Image *)node_->id;
NodeTexImage *tex = static_cast<NodeTexImage *>(node->storage); NodeTexImage *tex = static_cast<NodeTexImage *>(node_->storage);
Scene *scene = DEG_get_input_scene(depsgraph); Scene *scene = DEG_get_input_scene(depsgraph_);
Main *bmain = DEG_get_bmain(depsgraph); Main *bmain = DEG_get_bmain(depsgraph_);
std::string image_path; std::string image_path;
/* TODO: What if Blender built without Hydra? Also io::hydra::cache_or_get_image_file contains /* 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. */ * pretty general code, so could be moved from bf_usd project. */
@ -24,7 +24,7 @@ NodeItem TexImageNodeParser::compute()
#endif #endif
NodeItem texcoord = create_node("texcoord", "vector2"); 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("file", image_path, "filename");
res.set_input("texcoord", texcoord); res.set_input("texcoord", texcoord);
return res; return res;

View File

@ -18,7 +18,7 @@ NodeItem TexNoiseNodeParser::compute()
NodeItem texcoord = create_node("position", "vector3"); 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("position", texcoord * scale);
res.set_input("octaves", detail); res.set_input("octaves", detail);
res.set_input("lacunarity", lacunarity); res.set_input("lacunarity", lacunarity);