Implement export of Shader BSDF nodes #13

Merged
Bogdan Nagirniak merged 13 commits from BogdanNagirniak/blender:matx-shader-bsdf-nodes into matx-export-material 2023-09-07 11:22:44 +02:00
25 changed files with 685 additions and 181 deletions

View File

@ -147,18 +147,30 @@ set(LIB
if(WITH_MATERIALX) if(WITH_MATERIALX)
list(APPEND SRC list(APPEND SRC
materialx/material.cc materialx/material.cc
materialx/nodes/add_shader.cc
materialx/nodes/brightness.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_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/huesatval.cc
materialx/nodes/invert.cc materialx/nodes/invert.cc
materialx/nodes/math.cc materialx/nodes/math.cc
materialx/nodes/mix_rgb.cc materialx/nodes/mix_rgb.cc
materialx/nodes/mix_shader.cc
materialx/nodes/node_item.cc materialx/nodes/node_item.cc
materialx/nodes/node_parser.cc materialx/nodes/node_parser.cc
materialx/nodes/normal_map.cc materialx/nodes/normal_map.cc
materialx/nodes/output_material.cc materialx/nodes/output_material.cc
materialx/nodes/sepcomb_color.cc materialx/nodes/sepcomb_color.cc
materialx/nodes/sepcomb_xyz.cc materialx/nodes/sepcomb_xyz.cc
materialx/nodes/subsurface_scattering.cc
materialx/nodes/tex_checker.cc materialx/nodes/tex_checker.cc
materialx/nodes/tex_environment.cc materialx/nodes/tex_environment.cc
materialx/nodes/tex_image.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"); CLG_LOGREF_DECLARE_GLOBAL(LOG_MATERIALX_SHADER, "materialx.shader");
MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, Material *material)
Material *material,
const std::string &socket_name)
{ {
CLOG_INFO(LOG_MATERIALX_SHADER, 0, "Material: %s", material->id.name); 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) { if (material->use_nodes) {
material->nodetree->ensure_topology_cache(); material->nodetree->ensure_topology_cache();
bNode *output_node = ntreeShaderOutputNode(material->nodetree, SHD_OUTPUT_ALL); 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 { else {
OutputMaterialNodeParser(doc.get(), depsgraph, material, nullptr).compute_default(); 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; extern struct CLG_LogRef *LOG_MATERIALX_SHADER;
MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph, Material *material);
Material *material,
const std::string &socket_name = "Surface");
} // namespace blender::nodes::materialx } // 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_);
}
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() 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 base_color = get_input_value("Base Color", NodeItem::Type::Color3);
NodeItem subsurface = get_input_value("Subsurface", NodeItem::Type::Float); 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); NodeItem alpha = get_input_value("Alpha", NodeItem::Type::Float);
// transparency = 1.0 - alpha // transparency = 1.0 - alpha
NodeItem normal = get_input_link("Normal"); NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3);
NodeItem clearcoat_normal = get_input_link("Clearcoat Normal"); NodeItem clearcoat_normal = get_input_link("Clearcoat Normal", NodeItem::Type::Vector3);
NodeItem tangent = get_input_link("Tangent"); NodeItem tangent = get_input_link("Tangent", NodeItem::Type::Vector3);
/* Creating standard_surface */ /* Creating standard_surface */
NodeItem res = create_node("standard_surface", "surfaceshader"); 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(); NodeItem res = empty();
/* Single operand operations */ /* Single operand operations */
NodeItem x = get_input_value(0, NodeItem::Type::Empty); NodeItem x = get_input_value(0, NodeItem::Type::Any);
switch (op) { switch (op) {
case NODE_MATH_SINE: case NODE_MATH_SINE:
res = x.sin(); res = x.sin();
@ -82,7 +82,7 @@ NodeItem MathNodeParser::compute()
default: { default: {
/* 2-operand operations */ /* 2-operand operations */
NodeItem y = get_input_value(1, NodeItem::Type::Empty); NodeItem y = get_input_value(1, NodeItem::Type::Any);
switch (op) { switch (op) {
case NODE_MATH_ADD: case NODE_MATH_ADD:
res = x + y; res = x + y;
@ -132,7 +132,7 @@ NodeItem MathNodeParser::compute()
default: { default: {
/* 3-operand operations */ /* 3-operand operations */
NodeItem z = get_input_value(2, NodeItem::Type::Empty); NodeItem z = get_input_value(2, NodeItem::Type::Any);
switch (op) { switch (op) {
case NODE_MATH_WRAP: case NODE_MATH_WRAP:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op); 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_);
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::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 NodeItem::operator bool() const
{ {
return value || node; return value || node;
@ -19,6 +99,21 @@ NodeItem::operator bool() const
NodeItem NodeItem::operator+(const NodeItem &other) 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; }); 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 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; }); 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 item1 = *this;
NodeItem item2 = other; NodeItem item2 = other;
Type to_type = adjust_types(item1, item2); Type to_type = cast_types(item1, item2);
if (to_type == Type::Empty) { if (to_type == Type::Empty) {
return false; return false;
} }
@ -248,10 +359,14 @@ NodeItem NodeItem::extract(const int index) const
NodeItem NodeItem::convert(Type to_type) const NodeItem NodeItem::convert(Type to_type) const
{ {
Type from_type = type(); Type from_type = type();
if (from_type == to_type) { if (from_type == Type::Empty || from_type == to_type || to_type == Type::Any) {
return *this; return *this;
} }
if (!is_arithmetic(from_type) || !is_arithmetic(to_type)) { 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(); return empty();
} }
@ -448,7 +563,7 @@ NodeItem NodeItem::if_else(CompareOp op,
auto item1 = if_val; auto item1 = if_val;
auto item2 = else_val; auto item2 = else_val;
Type to_type = adjust_types(item1, item2); Type to_type = cast_types(item1, item2);
if (to_type == Type::Empty) { if (to_type == Type::Empty) {
return res; return res;
} }
@ -554,66 +669,12 @@ void NodeItem::add_output(const std::string &name, Type out_type)
node->addOutput(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) 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 t1 = item1.type();
Type t2 = item2.type(); Type t2 = item2.type();
@ -621,6 +682,8 @@ NodeItem::Type NodeItem::adjust_types(NodeItem &item1, NodeItem &item2)
return t1; return t1;
} }
if (!is_arithmetic(t1) || !is_arithmetic(t2)) { 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; return Type::Empty;
} }
if (t1 < t2) { if (t1 < t2) {
@ -701,7 +764,7 @@ NodeItem NodeItem::arithmetic(const NodeItem &other,
NodeItem res = empty(); NodeItem res = empty();
NodeItem item1 = *this; NodeItem item1 = *this;
NodeItem item2 = other; NodeItem item2 = other;
Type to_type = adjust_types(item1, item2); Type to_type = cast_types(item1, item2);
if (to_type == Type::Empty) { if (to_type == Type::Empty) {
return res; return res;
} }

View File

@ -11,16 +11,28 @@ namespace blender::nodes::materialx {
class NodeItem { class NodeItem {
public: public:
enum class Type { enum class Type {
Empty = 0, Any = 0,
Other, /* For MaterialX types like: surfaceshader, bsdf, edf, ...*/ Empty,
/* Value types */
String, String,
Filename,
Integer, Integer,
/* Block of arithmetic types. Ordered by type cast */
Float, Float,
Vector2, Vector2,
Vector3, Vector3,
Vector4,
Color3, 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 }; enum class CompareOp { Less = 0, LessEq, Eq, GreaterEq, Greater, NotEq };
@ -35,6 +47,9 @@ class NodeItem {
NodeItem(MaterialX::GraphElement *graph); NodeItem(MaterialX::GraphElement *graph);
~NodeItem() = default; ~NodeItem() = default;
static Type type(const std::string &type_str);
static std::string type(Type type);
/* Operators */ /* Operators */
operator bool() const; operator bool() const;
NodeItem operator+(const NodeItem &other) const; NodeItem operator+(const NodeItem &other) const;
@ -92,10 +107,8 @@ class NodeItem {
void add_output(const std::string &in_name, Type out_type); void add_output(const std::string &in_name, Type out_type);
private: private:
static Type type(const std::string &type_str);
static std::string type(Type type);
static bool is_arithmetic(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; bool is_arithmetic() const;
NodeItem arithmetic(const std::string &category, std::function<float(float)> func) 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 ? CLOG_INFO(LOG_MATERIALX_SHADER, 1, "%s [%d]", node_->name, node_->typeinfo->type);
std::string(node->name) : NodeItem res = compute();
std::string(node->name) + "_" + socket_out->name); 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) 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; 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 NodeItem NodeParser::empty() const
@ -71,7 +81,7 @@ 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, NodeItem::Type to_type)
{ {
NodeItem res = empty(); NodeItem res = empty();
switch (socket.type) { 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); 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(); NodeItem res = empty();
@ -119,21 +129,16 @@ NodeItem NodeParser::get_input_link(const bNodeSocket &socket)
from_node = link->fromnode; from_node = link->fromnode;
} }
/* Checking if node was already computed */ /* Creating required NodeParser object */
res.node = graph_->getNode(node_name(from_node, link->fromsock)); std::unique_ptr<NodeParser> parser;
if (res.node) {
return res;
}
/* Computing from_node with required NodeParser object */
#define CASE_NODE_TYPE(type, T) \ #define CASE_NODE_TYPE(type, T) \
case type: \ 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; break;
switch (from_node->typeinfo->type) { switch (from_node->typeinfo->type) {
CASE_NODE_TYPE(SH_NODE_BRIGHTCONTRAST, BrightContrastNodeParser) 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_COMBINE_COLOR, CombineColorNodeParser)
CASE_NODE_TYPE(SH_NODE_COMBXYZ, CombineXYZNodeParser) CASE_NODE_TYPE(SH_NODE_COMBXYZ, CombineXYZNodeParser)
CASE_NODE_TYPE(SH_NODE_HUE_SAT, HueSatValNodeParser) 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->name,
from_node->typeinfo->type); 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; 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(); NodeItem res = compute();
if (res.node) { if (res.node) {
res.node->setName(node_name(node_, socket_out_)); res.node->setName(node_name());
} }
return res; 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 } // namespace blender::nodes::materialx

View File

@ -31,24 +31,44 @@ class NodeParser {
virtual NodeItem compute() = 0; virtual NodeItem compute() = 0;
protected: 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 create_node(const std::string &mx_category, const std::string &mx_type);
NodeItem get_input_default(const std::string &name); NodeItem get_input_default(const std::string &name, NodeItem::Type to_type);
NodeItem get_input_default(int index); NodeItem get_input_default(int index, NodeItem::Type to_type);
NodeItem get_input_link(const std::string &name); NodeItem get_input_link(const std::string &name, NodeItem::Type to_type);
NodeItem get_input_link(int index); NodeItem get_input_link(int index, NodeItem::Type to_type);
NodeItem get_input_value(const std::string &name, NodeItem get_input_value(const std::string &name, NodeItem::Type to_type);
const NodeItem::Type type); NodeItem get_input_value(int index, NodeItem::Type to_type);
NodeItem get_input_value(int index, const NodeItem::Type type);
NodeItem empty() const; NodeItem empty() const;
template<class T> NodeItem value(const T &data) const; template<class T> NodeItem value(const T &data) const;
private: private:
NodeItem get_input_default(const bNodeSocket &socket); NodeItem get_input_default(const bNodeSocket &socket, NodeItem::Type to_type);
NodeItem get_input_link(const bNodeSocket &socket); NodeItem get_input_link(const bNodeSocket &socket, NodeItem::Type to_type);
NodeItem get_input_value(const bNodeSocket &socket, NodeItem get_input_value(const bNodeSocket &socket, NodeItem::Type to_type);
const NodeItem::Type type); };
NodeItem compute_full();
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 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); return empty().val(data);
} }
#define DECLARE_PARSER(T) \ #define DECLARE_NODE_PARSER(T) \
class T : public NodeParser { \ class T : public NodeParser { \
public: \ public: \
using NodeParser::NodeParser; \ using NodeParser::NodeParser; \
NodeItem compute() override; \ NodeItem compute() override; \
}; };
DECLARE_PARSER(BrightContrastNodeParser) #define DECLARE_SHADER_NODE_PARSER(T) \
DECLARE_PARSER(BSDFPrincipledNodeParser) class T : public ShaderNodeParser { \
DECLARE_PARSER(CombineColorNodeParser) public: \
DECLARE_PARSER(CombineXYZNodeParser) using ShaderNodeParser::ShaderNodeParser; \
DECLARE_PARSER(HueSatValNodeParser) NodeItem compute() override; \
DECLARE_PARSER(InvertNodeParser) };
DECLARE_PARSER(MathNodeParser)
DECLARE_PARSER(MixRGBNodeParser) DECLARE_NODE_PARSER(BrightContrastNodeParser)
DECLARE_PARSER(NormalMapNodeParser) DECLARE_NODE_PARSER(CombineColorNodeParser)
DECLARE_PARSER(SeparateColorNodeParser) DECLARE_NODE_PARSER(CombineXYZNodeParser)
DECLARE_PARSER(SeparateXYZNodeParser) DECLARE_NODE_PARSER(HueSatValNodeParser)
DECLARE_PARSER(TexCheckerNodeParser) DECLARE_NODE_PARSER(InvertNodeParser)
DECLARE_PARSER(TexEnvironmentNodeParser) DECLARE_NODE_PARSER(MathNodeParser)
DECLARE_PARSER(TexImageNodeParser) DECLARE_NODE_PARSER(MixRGBNodeParser)
DECLARE_PARSER(TexNoiseNodeParser) DECLARE_NODE_PARSER(NormalMapNodeParser)
DECLARE_PARSER(VectorMathNodeParser) 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 } // namespace blender::nodes::materialx

View File

@ -9,27 +9,34 @@ OutputMaterialNodeParser::OutputMaterialNodeParser(MaterialX::GraphElement *grap
const Depsgraph *depsgraph, const Depsgraph *depsgraph,
const Material *material, const Material *material,
const bNode *node) const bNode *node)
: NodeParser(graph, depsgraph, material, node, nullptr) : ShaderNodeParser(graph, depsgraph, material, node, nullptr, NodeItem::Type::Material)
{ {
} }
NodeItem OutputMaterialNodeParser::compute() NodeItem OutputMaterialNodeParser::compute()
{
return empty();
}
NodeItem OutputMaterialNodeParser::compute(const std::string &socket_name)
{ {
NodeItem surface = empty(); NodeItem surface = empty();
if (node_) { 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 { else {
surface = create_node("standard_surface", "surfaceshader"); surface = create_node("standard_surface", "surfaceshader");
surface.set_input("base_color", value(MaterialX::Color3(1.0f, 0.0f, 1.0f))); surface.set_input("base_color", value(MaterialX::Color3(1.0f, 0.0f, 1.0f)));
} }
NodeItem res = create_node("surfacematerial", "material"); NodeItem res = create_node("surfacematerial", "material");
res.node->setName(node_name(node_, nullptr));
res.set_input("surfaceshader", surface); res.set_input("surfaceshader", surface);
return res; return res;
} }
@ -55,4 +62,9 @@ NodeItem OutputMaterialNodeParser::compute_default()
return res; return res;
} }
std::string OutputMaterialNodeParser::node_name()
{
return NodeParser::node_name();
}
} // namespace blender::nodes::materialx } // namespace blender::nodes::materialx

View File

@ -8,15 +8,19 @@
namespace blender::nodes::materialx { namespace blender::nodes::materialx {
class OutputMaterialNodeParser : public NodeParser { class OutputMaterialNodeParser : public ShaderNodeParser {
public: public:
OutputMaterialNodeParser(MaterialX::GraphElement *graph, OutputMaterialNodeParser(MaterialX::GraphElement *graph,
const Depsgraph *depsgraph, const Depsgraph *depsgraph,
const Material *material, const Material *material,
const bNode *node); const bNode *node);
NodeItem compute() override; NodeItem compute() override;
NodeItem compute(const std::string &socket_name);
using ShaderNodeParser::compute_full;
NodeItem compute_default(); NodeItem compute_default();
protected:
std::string node_name() override;
}; };
} // namespace blender::nodes::materialx } // 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 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 color1 = get_input_value("Color1", NodeItem::Type::Color3);
NodeItem color2 = get_input_value("Color2", NodeItem::Type::Color3); NodeItem color2 = get_input_value("Color2", NodeItem::Type::Color3);
NodeItem scale = get_input_value("Scale", NodeItem::Type::Float); NodeItem scale = get_input_value("Scale", NodeItem::Type::Float);
@ -16,28 +16,13 @@ NodeItem TexCheckerNodeParser::compute()
if (!vector) { if (!vector) {
vector = create_node("texcoord", "vector2"); vector = create_node("texcoord", "vector2");
} }
vector = vector * scale; vector = (vector * scale) % value(2.0f);
NodeItem mix = (vector.extract(0).floor() + vector.extract(1).floor())
NodeItem separate = create_node("separate2", "multioutput"); .if_else(NodeItem::CompareOp::Eq, value(1.0f), value(1.0f), value(0.0f));
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));
NodeItem res = create_node("mix", "color3"); NodeItem res = create_node("mix", "color3");
res.set_input("bg", color1); res.set_input("fg", color1);
res.set_input("fg", color2); res.set_input("bg", color2);
res.set_input("mix", ifequal); res.set_input("mix", mix);
return res; return res;
} }

View File

@ -14,7 +14,7 @@ NodeItem VectorMathNodeParser::compute()
NodeItem res = empty(); NodeItem res = empty();
/* Single operand operations */ /* Single operand operations */
NodeItem x = get_input_value(0, NodeItem::Type::Empty); NodeItem x = get_input_value(0, NodeItem::Type::Any);
switch (op) { switch (op) {
case NODE_VECTOR_MATH_SINE: case NODE_VECTOR_MATH_SINE:
res = x.sin(); res = x.sin();
@ -46,7 +46,7 @@ NodeItem VectorMathNodeParser::compute()
default: { default: {
/* 2-operand operations */ /* 2-operand operations */
NodeItem y = get_input_value(1, NodeItem::Type::Empty); NodeItem y = get_input_value(1, NodeItem::Type::Any);
switch (op) { switch (op) {
case NODE_VECTOR_MATH_ADD: case NODE_VECTOR_MATH_ADD:
res = x + y; res = x + y;
@ -93,7 +93,7 @@ NodeItem VectorMathNodeParser::compute()
default: { default: {
/* 3-operand operations */ /* 3-operand operations */
NodeItem z = get_input_value(2, NodeItem::Type::Empty); NodeItem z = get_input_value(2, NodeItem::Type::Any);
switch (op) { switch (op) {
case NODE_VECTOR_MATH_MULTIPLY_ADD: case NODE_VECTOR_MATH_MULTIPLY_ADD:
res = x * y + z; res = x * y + z;