Export material to MaterialX for Hydra render #111765

Manually merged
Brecht Van Lommel merged 48 commits from DagerD/blender:matx-export-material into main 2023-09-26 18:56:40 +02:00
25 changed files with 685 additions and 181 deletions
Showing only changes of commit a464b5854e - Show all commits

View File

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

View File

@ -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();
BogdanNagirniak marked this conversation as resolved Outdated

This should create a temporary copy of the node tree with ntreeLocalize, similar as is done for generating the GLSL shader. That will resolve any muting and reroute nodes, as handling them as part of the export process is not easy.

Node groups might ideally be preserved in export, but it would be easiest to start with exporting them expanded. For this it would be easiest to use the functions also called from ntreeGPUMaterialNodes:

  ntree_shader_groups_remove_muted_links(localtree);
  ntree_shader_groups_expand_inputs(localtree);
  ntree_shader_groups_flatten(localtree);
This should create a temporary copy of the node tree with `ntreeLocalize`, similar as is done for generating the GLSL shader. That will resolve any muting and reroute nodes, as handling them as part of the export process is not easy. Node groups might ideally be preserved in export, but it would be easiest to start with exporting them expanded. For this it would be easiest to use the functions also called from `ntreeGPUMaterialNodes`: ``` ntree_shader_groups_remove_muted_links(localtree); ntree_shader_groups_expand_inputs(localtree); ntree_shader_groups_flatten(localtree); ```

Exporting of group nodes is implemented in materialx/group_nodes.cc/.h

Exporting of group nodes is implemented in `materialx/group_nodes.cc/.h`
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();

View File

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

View File

@ -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_);
BogdanNagirniak marked this conversation as resolved Outdated

Can you add a comment explaining this logic, why it picks one or the other instead of adding? Is this something that can be improved?

Can you add a comment explaining this logic, why it picks one or the other instead of adding? Is this something that can be improved?
}
break;
}
default:
BLI_assert_unreachable();
}
return res;
}
} // namespace blender::nodes::materialx

View File

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

View File

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

View File

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

View File

@ -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");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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_);
BogdanNagirniak marked this conversation as resolved Outdated

Similar questions as add shader, not sure what this is doing.

Similar questions as add shader, not sure what this is doing.
if (!res) {
res = get_input_shader(2, shader_type_);
}
break;
}
default:
BLI_assert_unreachable();
}
return res;
}
} // namespace blender::nodes::materialx

View File

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

View File

@ -11,16 +11,28 @@ namespace blender::nodes::materialx {
class NodeItem {
BogdanNagirniak marked this conversation as resolved Outdated

Can you add a comment explaining what this class is?

Can you add a comment explaining what this class is?
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;
brecht marked this conversation as resolved Outdated

Can you change this to bool is_empty() const? I would only use operator overloading when the meaning is more clear.

Can you change this to `bool is_empty() const`? I would only use operator overloading when the meaning is more clear.

Such bool operator is used during the code in several places, for example:

  NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3);
....
  if (normal) {
    res.set_input("normal", normal);
  }

And seems for me it suits there very nice. Are you sure using is_empty() would be better?

Such bool operator is used during the code in several places, for example: ``` NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3); .... if (normal) { res.set_input("normal", normal); } ``` And seems for me it suits there very nice. Are you sure using `is_empty()` would be better?

Ok, we can keep it as is since it's a bit like a null pointer check.

Ok, we can keep it as is since it's a bit like a null pointer check.
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<float(float)> func) const;

View File

@ -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<NodeParser> 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<T>(graph_, depsgraph_, material_, from_node, link->fromsock); \
break;
BogdanNagirniak marked this conversation as resolved Outdated

If there is a node type callback, this switch statement will not be needed anymore.

If there is a node type callback, this switch statement will not be needed anymore.
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<ShaderNodeParser> parser;
#define CASE_SHADER_NODE_TYPE(type, T) \
case type: \
parser = std::make_unique<T>( \
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

View File

@ -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<class T> 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<class T> NodeItem NodeParser::value(const T &data) const
@ -56,28 +76,48 @@ template<class T> 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

View File

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

View File

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

View File

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

View File

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

View File

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