MaterialX: add convert nodes #15

Merged
Bogdan Nagirniak merged 8 commits from matx-add-convert-nodes into matx-export-material 2023-09-08 16:55:00 +02:00
34 changed files with 808 additions and 294 deletions
Showing only changes of commit e51f34488b - Show all commits

View File

@ -147,21 +147,33 @@ 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/blackbody.cc materialx/nodes/blackbody.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/clamp.cc materialx/nodes/clamp.cc
materialx/nodes/emission.cc
materialx/nodes/huesatval.cc materialx/nodes/huesatval.cc
materialx/nodes/invert.cc materialx/nodes/invert.cc
materialx/nodes/map_range.cc materialx/nodes/map_range.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,14 +25,14 @@ 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();
} }
CLOG_INFO(LOG_MATERIALX_SHADER, CLOG_INFO(LOG_MATERIALX_SHADER,
2, 1,
"Material: %s\n%s", "Material: %s\n%s",
material->id.name, material->id.name,
MaterialX::writeToXmlString(doc).c_str()); MaterialX::writeToXmlString(doc).c_str());

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

@ -13,7 +13,7 @@ NodeItem BrightContrastNodeParser::compute()
NodeItem contrast = get_input_value("Contrast", NodeItem::Type::Float); NodeItem contrast = get_input_value("Contrast", NodeItem::Type::Float);
/* This formula was given from OSL shader code in Cycles. */ /* This formula was given from OSL shader code in Cycles. */
return (bright + color * (contrast + value(1.0f)) - contrast * value(0.5f)).max(value(0.0f)); return (bright + color * (contrast + val(1.0f)) - contrast * val(0.5f)).max(val(0.0f));
} }
} // namespace blender::nodes::materialx } // 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", NodeItem::Type::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,13 +45,13 @@ 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", NodeItem::Type::SurfaceShader);
res.set_input("base", 1.0, "float"); res.set_input("base", val(1.0f));
res.set_input("base_color", base_color); res.set_input("base_color", base_color);
res.set_input("diffuse_roughness", roughness); res.set_input("diffuse_roughness", roughness);
if (normal) { if (normal) {

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", NodeItem::Type::EDF);
res.set_input("color", color * strength);
return res;
}
} // namespace blender::nodes::materialx

View File

@ -12,19 +12,19 @@ NodeItem HueSatValNodeParser::compute()
* source\blender\nodes\texture\nodes\node_texture_hueSatVal.cc */ * source\blender\nodes\texture\nodes\node_texture_hueSatVal.cc */
NodeItem hue = get_input_value("Hue", NodeItem::Type::Float); NodeItem hue = get_input_value("Hue", NodeItem::Type::Float);
NodeItem saturation = get_input_value("Saturation", NodeItem::Type::Float); NodeItem saturation = get_input_value("Saturation", NodeItem::Type::Float);
NodeItem val = get_input_value("Value", NodeItem::Type::Float); NodeItem value = get_input_value("Value", NodeItem::Type::Float);
NodeItem fac = get_input_value("Fac", NodeItem::Type::Float); NodeItem fac = get_input_value("Fac", NodeItem::Type::Float);
NodeItem color = get_input_value("Color", NodeItem::Type::Color3); NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
/* Modifier to follow Cycles result */ /* Modifier to follow Cycles result */
hue = hue - value(0.5f); hue = hue - val(0.5f);
NodeItem combine = create_node("combine3", "vector3"); NodeItem combine = create_node("combine3", NodeItem::Type::Vector3);
combine.set_input("in1", hue); combine.set_input("in1", hue);
combine.set_input("in2", saturation); combine.set_input("in2", saturation);
combine.set_input("in3", val); combine.set_input("in3", value);
NodeItem res = create_node("hsvadjust", "color3"); NodeItem res = create_node("hsvadjust", NodeItem::Type::Color3);
res.set_input("in", color); res.set_input("in", color);
res.set_input("amount", combine); res.set_input("amount", combine);
return res; return res;

View File

@ -12,7 +12,7 @@ NodeItem MapRangeNodeParser::compute()
const NodeMapRange *map_range = static_cast<NodeMapRange *>(node_->storage); const NodeMapRange *map_range = static_cast<NodeMapRange *>(node_->storage);
NodeItem::Type type; NodeItem::Type type;
NodeItem val = empty(); NodeItem value = empty();
NodeItem from_min = empty(); NodeItem from_min = empty();
NodeItem from_max = empty(); NodeItem from_max = empty();
NodeItem to_min = empty(); NodeItem to_min = empty();
@ -20,7 +20,7 @@ NodeItem MapRangeNodeParser::compute()
switch (map_range->data_type) { switch (map_range->data_type) {
case CD_PROP_FLOAT: case CD_PROP_FLOAT:
type = NodeItem::Type::Float; type = NodeItem::Type::Float;
val = get_input_value("Value", type); value = get_input_value("Value", type);
from_min = get_input_value(1, type); from_min = get_input_value(1, type);
from_max = get_input_value(2, type); from_max = get_input_value(2, type);
to_min = get_input_value(3, type); to_min = get_input_value(3, type);
@ -28,7 +28,7 @@ NodeItem MapRangeNodeParser::compute()
break; break;
case CD_PROP_FLOAT3: case CD_PROP_FLOAT3:
type = NodeItem::Type::Vector3; type = NodeItem::Type::Vector3;
val = get_input_value("Vector", type); value = get_input_value("Vector", type);
from_min = get_input_value(7, type); from_min = get_input_value(7, type);
from_max = get_input_value(8, type); from_max = get_input_value(8, type);
to_min = get_input_value(9, type); to_min = get_input_value(9, type);
@ -38,13 +38,13 @@ NodeItem MapRangeNodeParser::compute()
BLI_assert_unreachable(); BLI_assert_unreachable();
} }
NodeItem res = create_node("range", NodeItem::type(type)); NodeItem res = create_node("range", type);
res.set_input("in", val); res.set_input("in", value);
res.set_input("inlow", from_min); res.set_input("inlow", from_min);
res.set_input("inhigh", from_max); res.set_input("inhigh", from_max);
res.set_input("outlow", to_min); res.set_input("outlow", to_min);
res.set_input("outhigh", to_max); res.set_input("outhigh", to_max);
res.set_input("doclamp", value(bool(map_range->clamp))); res.set_input("doclamp", val(bool(map_range->clamp)));
return res; return res;
} }

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();
@ -35,7 +35,7 @@ NodeItem MathNodeParser::compute()
res = x.atan(); res = x.atan();
break; break;
case NODE_MATH_ROUND: case NODE_MATH_ROUND:
res = (x + value(0.5f)).floor(); res = (x + val(0.5f)).floor();
break; break;
case NODE_MATH_ABSOLUTE: case NODE_MATH_ABSOLUTE:
res = x.abs(); res = x.abs();
@ -47,13 +47,13 @@ NodeItem MathNodeParser::compute()
res = x.ceil(); res = x.ceil();
break; break;
case NODE_MATH_FRACTION: case NODE_MATH_FRACTION:
res = x % value(1.0f); res = x % val(1.0f);
break; break;
case NODE_MATH_SQRT: case NODE_MATH_SQRT:
res = x.sqrt(); res = x.sqrt();
break; break;
case NODE_MATH_INV_SQRT: case NODE_MATH_INV_SQRT:
res = value(1.0f) / x.sqrt(); res = val(1.0f) / x.sqrt();
break; break;
case NODE_MATH_SIGN: case NODE_MATH_SIGN:
res = x.sign(); res = x.sign();
@ -62,10 +62,10 @@ NodeItem MathNodeParser::compute()
res = x.exp(); res = x.exp();
break; break;
case NODE_MATH_RADIANS: case NODE_MATH_RADIANS:
res = x * value(float(M_PI) / 180.0f); res = x * val(float(M_PI) / 180.0f);
break; break;
case NODE_MATH_DEGREES: case NODE_MATH_DEGREES:
res = x * value(180.0f * float(M_1_PI)); res = x * val(180.0f * float(M_1_PI));
break; break;
case NODE_MATH_SINH: case NODE_MATH_SINH:
res = x.sinh(); res = x.sinh();
@ -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;
@ -109,10 +109,10 @@ NodeItem MathNodeParser::compute()
res = x.max(y); res = x.max(y);
break; break;
case NODE_MATH_LESS_THAN: case NODE_MATH_LESS_THAN:
res = x.if_else(NodeItem::CompareOp::Less, y, value(1.0f), value(0.0f)); res = x.if_else(NodeItem::CompareOp::Less, y, val(1.0f), val(0.0f));
break; break;
case NODE_MATH_GREATER_THAN: case NODE_MATH_GREATER_THAN:
res = x.if_else(NodeItem::CompareOp::Greater, y, value(1.0f), value(0.0f)); res = x.if_else(NodeItem::CompareOp::Greater, y, val(1.0f), val(0.0f));
break; break;
case NODE_MATH_MODULO: case NODE_MATH_MODULO:
res = x % y; res = x % y;
@ -132,13 +132,13 @@ 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);
break; break;
case NODE_MATH_COMPARE: case NODE_MATH_COMPARE:
res = z.if_else(NodeItem::CompareOp::Less, (x - y).abs(), value(1.0f), value(0.0f)); res = z.if_else(NodeItem::CompareOp::Less, (x - y).abs(), val(1.0f), val(0.0f));
break; break;
case NODE_MATH_MULTIPLY_ADD: case NODE_MATH_MULTIPLY_ADD:
res = x * y + z; res = x * y + z;

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 * (val(1.0f) - fac);
}
else if (!shader1 && shader2) {
res = shader2 * fac;
}
else if (shader1 && shader2) {
res = create_node("mix", 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, NodeItem::Type::SurfaceShader);
if (!res) {
res = get_input_shader(2, NodeItem::Type::SurfaceShader);
}
break;
}
default:
BLI_assert_unreachable();
}
return res;
}
} // namespace blender::nodes::materialx

View File

@ -12,6 +12,91 @@ 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 == "boolean") {
return Type::Bool;
}
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::Bool:
return "boolean";
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 +104,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 = create_node("add", 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 +134,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 = create_node("multiply", 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 +186,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;
} }
@ -111,9 +227,8 @@ NodeItem NodeItem::dotproduct(const NodeItem &other) const
{ {
NodeItem d = arithmetic(other, "dotproduct", [](float a, float b) { return a * b; }); NodeItem d = arithmetic(other, "dotproduct", [](float a, float b) { return a * b; });
if (d.value) { if (d.value) {
Type mx_type = d.type();
float f = 0.0f; float f = 0.0f;
switch (mx_type) { switch (d.type()) {
case Type::Float: { case Type::Float: {
f = value->asA<float>(); f = value->asA<float>();
break; break;
@ -239,7 +354,7 @@ NodeItem NodeItem::exp() const
NodeItem NodeItem::extract(const int index) const NodeItem NodeItem::extract(const int index) const
{ {
NodeItem res = empty(); NodeItem res = empty();
res.node = graph_->addNode("extract", MaterialX::EMPTY_STRING, "float"); res = create_node("extract", Type::Float);
res.set_input("in", *this); res.set_input("in", *this);
res.set_input("index", val(index)); res.set_input("index", val(index));
return res; return res;
@ -248,10 +363,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();
} }
@ -419,7 +538,7 @@ NodeItem NodeItem::convert(Type to_type) const
} }
} }
else { else {
res.node = graph_->addNode("convert", MaterialX::EMPTY_STRING, type(to_type)); res = create_node("convert", to_type);
res.set_input("in", *this); res.set_input("in", *this);
} }
return res; return res;
@ -448,24 +567,24 @@ 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;
} }
std::function<bool(float, float)> func = nullptr; std::function<bool(float, float)> func = nullptr;
std::string mx_category; std::string category;
switch (op) { switch (op) {
case CompareOp::Greater: case CompareOp::Greater:
mx_category = "ifgreater"; category = "ifgreater";
func = [](float a, float b) { return a > b; }; func = [](float a, float b) { return a > b; };
break; break;
case CompareOp::GreaterEq: case CompareOp::GreaterEq:
mx_category = "ifgreatereq"; category = "ifgreatereq";
func = [](float a, float b) { return a >= b; }; func = [](float a, float b) { return a >= b; };
break; break;
case CompareOp::Eq: case CompareOp::Eq:
mx_category = "ifequal"; category = "ifequal";
func = [](float a, float b) { return a == b; }; func = [](float a, float b) { return a == b; };
break; break;
default: default:
@ -476,7 +595,7 @@ NodeItem NodeItem::if_else(CompareOp op,
res = func(value->asA<float>(), other.value->asA<float>()) ? item1 : item2; res = func(value->asA<float>(), other.value->asA<float>()) ? item1 : item2;
} }
else { else {
res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, type(to_type)); res = create_node(category, to_type);
res.set_input("value1", *this); res.set_input("value1", *this);
res.set_input("value2", other); res.set_input("value2", other);
res.set_input("in1", item1); res.set_input("in1", item1);
@ -502,127 +621,81 @@ NodeItem::Type NodeItem::type() const
return Type::Empty; return Type::Empty;
} }
void NodeItem::set_input(const std::string &name, NodeItem NodeItem::create_node(const std::string &category, NodeItem::Type type) const
const NodeItem &item, {
const std::string &output_name) std::string type_str = this->type(type);
CLOG_INFO(LOG_MATERIALX_SHADER, 2, "<%s type=%s>", category.c_str(), type_str.c_str());
NodeItem res = empty();
res.node = graph_->addNode(category, MaterialX::EMPTY_STRING, type_str);
return res;
}
void NodeItem::set_input(const std::string &in_name, const NodeItem &item)
{ {
if (item.value) { if (item.value) {
Type item_type = item.type(); Type item_type = item.type();
std::string mx_type = type(item_type);
switch (item_type) { switch (item_type) {
case Type::String: case Type::String:
set_input(name, item.value->asA<std::string>(), mx_type); set_input(in_name, item.value->asA<std::string>(), item_type);
break; break;
case Type::Bool: case Type::Bool:
set_input(name, item.value->asA<bool>(), mx_type); set_input(in_name, item.value->asA<bool>(), item_type);
break; break;
case Type::Integer: case Type::Integer:
set_input(name, item.value->asA<int>(), mx_type); set_input(in_name, item.value->asA<int>(), item_type);
break; break;
case Type::Float: case Type::Float:
set_input(name, item.value->asA<float>(), mx_type); set_input(in_name, item.value->asA<float>(), item_type);
break; break;
case Type::Vector2: case Type::Vector2:
set_input(name, item.value->asA<MaterialX::Vector2>(), mx_type); set_input(in_name, item.value->asA<MaterialX::Vector2>(), item_type);
break; break;
case Type::Vector3: case Type::Vector3:
set_input(name, item.value->asA<MaterialX::Vector3>(), mx_type); set_input(in_name, item.value->asA<MaterialX::Vector3>(), item_type);
break; break;
case Type::Vector4: case Type::Vector4:
set_input(name, item.value->asA<MaterialX::Vector4>(), mx_type); set_input(in_name, item.value->asA<MaterialX::Vector4>(), item_type);
break; break;
case Type::Color3: case Type::Color3:
set_input(name, item.value->asA<MaterialX::Color3>(), mx_type); set_input(in_name, item.value->asA<MaterialX::Color3>(), item_type);
break; break;
case Type::Color4: case Type::Color4:
set_input(name, item.value->asA<MaterialX::Color4>(), mx_type); set_input(in_name, item.value->asA<MaterialX::Color4>(), item_type);
break; break;
default: default:
BLI_assert_unreachable(); BLI_assert_unreachable();
} }
} }
else if (item.node) { else if (item.node) {
node->setConnectedNode(name, item.node); node->setConnectedNode(in_name, item.node);
if (output_name != "") {
node->setConnectedOutput(name, item.node->getOutput(output_name));
}
} }
else { else {
CLOG_WARN(LOG_MATERIALX_SHADER, "Empty item to input: %s", name.c_str()); CLOG_WARN(LOG_MATERIALX_SHADER, "Empty item to input: %s", in_name.c_str());
} }
} }
void NodeItem::set_input_output(const std::string &in_name,
const NodeItem &item,
const std::string &out_name)
{
if (!item.node) {
BLI_assert_unreachable();
}
node->setConnectedNode(in_name, item.node);
node->setConnectedOutput(in_name, item.node->getOutput(out_name));
}
void NodeItem::add_output(const std::string &name, Type out_type) 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 == "boolean") {
return Type::Bool;
}
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::Bool:
return "boolean";
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:
BLI_assert_unreachable();
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();
@ -630,6 +703,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) {
@ -697,7 +772,7 @@ NodeItem NodeItem::arithmetic(const std::string &category, std::function<float(f
else { else {
/* TODO: Some of math functions (sin, cos, ...) doesn't work with Color types, /* TODO: Some of math functions (sin, cos, ...) doesn't work with Color types,
* we have to convert to Vector */ * we have to convert to Vector */
res.node = graph_->addNode(category, MaterialX::EMPTY_STRING, this->type(type)); res = create_node(category, type);
res.set_input("in", *this); res.set_input("in", *this);
} }
return res; return res;
@ -710,7 +785,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;
} }
@ -763,7 +838,7 @@ NodeItem NodeItem::arithmetic(const NodeItem &other,
} }
} }
else { else {
res.node = graph_->addNode(category, MaterialX::EMPTY_STRING, type(to_type)); res = create_node(category, to_type);
res.set_input("in1", item1); res.set_input("in1", item1);
res.set_input("in2", item2); res.set_input("in2", item2);
} }

View File

@ -11,17 +11,29 @@ 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,
Bool, Bool,
BogdanNagirniak marked this conversation as resolved Outdated

Maybe rename to Boolean, as we use full names for other types?

Maybe rename to Boolean, as we use full names for other types?
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 };
@ -36,6 +48,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;
@ -48,9 +63,6 @@ class NodeItem {
bool operator==(const NodeItem &other) const; bool operator==(const NodeItem &other) const;
bool operator!=(const NodeItem &other) const; bool operator!=(const NodeItem &other) const;
static Type type(const std::string &type_str);
static std::string type(Type type);
/* Math functions */ /* Math functions */
NodeItem abs() const; NodeItem abs() const;
NodeItem floor() const; NodeItem floor() const;
@ -86,18 +98,19 @@ class NodeItem {
NodeItem empty() const; NodeItem empty() const;
template<class T> NodeItem val(const T &data) const; template<class T> NodeItem val(const T &data) const;
Type type() const; Type type() const;
NodeItem create_node(const std::string &category, NodeItem::Type type) const;
/* Functions to set input and output */ /* Functions to set input and output */
template<class T> template<class T> void set_input(const std::string &in_name, const T &value, Type in_type);
void set_input(const std::string &in_name, const T &value, const std::string &in_type); void set_input(const std::string &in_name, const NodeItem &item);
void set_input(const std::string &in_name, void set_input_output(const std::string &in_name,
const NodeItem &item, const NodeItem &item,
const std::string &out_name = ""); const std::string &out_name);
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 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;
@ -114,9 +127,9 @@ template<class T> NodeItem NodeItem::val(const T &data) const
} }
template<class T> template<class T>
void NodeItem::set_input(const std::string &in_name, const T &value, const std::string &in_type) void NodeItem::set_input(const std::string &in_name, const T &value, Type in_type)
{ {
node->setInputValue(in_name, value, in_type); node->setInputValue(in_name, value, type(in_type));
} }
} // namespace blender::nodes::materialx } // namespace blender::nodes::materialx

View File

@ -22,48 +22,56 @@ 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());
}
NodeItem NodeParser::create_node(const std::string &mx_category, const std::string &mx_type)
{
NodeItem res = empty();
res.node = graph_->addNode(mx_category, MaterialX::EMPTY_STRING, mx_type);
return res; return res;
} }
NodeItem NodeParser::get_input_default(const std::string &name) std::string NodeParser::node_name()
{ {
return get_input_default(node_->input_by_identifier(name)); return MaterialX::createValidName(node_->output_sockets().size() <= 1 ?
std::string(node_->name) :
std::string(node_->name) + "_" + socket_out_->name);
} }
NodeItem NodeParser::get_input_default(int index) NodeItem NodeParser::create_node(const std::string &category, NodeItem::Type type)
{ {
return get_input_default(node_->input_socket(index)); return empty().create_node(category, type);
} }
NodeItem NodeParser::get_input_link(const std::string &name) NodeItem NodeParser::get_input_default(const std::string &name, NodeItem::Type to_type)
{ {
return get_input_link(node_->input_by_identifier(name)); return get_input_default(node_->input_by_identifier(name), to_type);
} }
NodeItem NodeParser::get_input_link(int index) NodeItem NodeParser::get_input_default(int index, NodeItem::Type to_type)
{ {
return get_input_link(node_->input_socket(index)); return get_input_default(node_->input_socket(index), to_type);
} }
NodeItem NodeParser::get_input_value(const std::string &name, const NodeItem::Type type) NodeItem NodeParser::get_input_link(const std::string &name, NodeItem::Type to_type)
{ {
return get_input_value(node_->input_by_identifier(name), type); return get_input_link(node_->input_by_identifier(name), to_type);
} }
NodeItem NodeParser::get_input_value(int index, const NodeItem::Type type) NodeItem NodeParser::get_input_link(int index, NodeItem::Type to_type)
{ {
return get_input_value(node_->input_socket(index), type); return get_input_link(node_->input_socket(index), to_type);
}
NodeItem NodeParser::get_input_value(const std::string &name, NodeItem::Type to_type)
{
return get_input_value(node_->input_by_identifier(name), to_type);
}
NodeItem NodeParser::get_input_value(int index, NodeItem::Type to_type)
{
return get_input_value(node_->input_socket(index), to_type);
} }
NodeItem NodeParser::empty() const NodeItem NodeParser::empty() const
@ -71,7 +79,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 +104,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,22 +127,17 @@ 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_BLACKBODY, BlackbodyNodeParser) CASE_NODE_TYPE(SH_NODE_BLACKBODY, BlackbodyNodeParser)
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_CLAMP, ClampNodeParser) CASE_NODE_TYPE(SH_NODE_CLAMP, ClampNodeParser)
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)
@ -158,27 +161,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,56 +31,96 @@ 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();
NodeItem create_node(const std::string &mx_category, const std::string &mx_type); virtual std::string node_name();
NodeItem get_input_default(const std::string &name); NodeItem create_node(const std::string &category, NodeItem::Type type);
NodeItem get_input_default(int index); NodeItem get_input_default(const std::string &name, NodeItem::Type to_type);
NodeItem get_input_link(const std::string &name); NodeItem get_input_default(int index, NodeItem::Type to_type);
NodeItem get_input_link(int index); NodeItem get_input_link(const std::string &name, NodeItem::Type to_type);
NodeItem get_input_value(const std::string &name, NodeItem get_input_link(int index, NodeItem::Type to_type);
const NodeItem::Type type); NodeItem get_input_value(const std::string &name, NodeItem::Type to_type);
NodeItem get_input_value(int index, const NodeItem::Type type); NodeItem get_input_value(int index, NodeItem::Type to_type);
NodeItem empty() const; NodeItem empty() const;
template<class T> NodeItem value(const T &data) const; template<class T> NodeItem val(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();
}; };
template<class T> NodeItem NodeParser::value(const T &data) const 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::val(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(BlackbodyNodeParser) #define DECLARE_SHADER_NODE_PARSER(T) \
DECLARE_PARSER(BrightContrastNodeParser) class T : public ShaderNodeParser { \
DECLARE_PARSER(BSDFPrincipledNodeParser) public: \
DECLARE_PARSER(ClampNodeParser) using ShaderNodeParser::ShaderNodeParser; \
DECLARE_PARSER(CombineColorNodeParser) NodeItem compute() override; \
DECLARE_PARSER(CombineXYZNodeParser) };
DECLARE_PARSER(HueSatValNodeParser)
DECLARE_PARSER(InvertNodeParser) DECLARE_NODE_PARSER(BlackbodyNodeParser)
DECLARE_PARSER(MapRangeNodeParser) DECLARE_NODE_PARSER(BrightContrastNodeParser)
DECLARE_PARSER(MathNodeParser) DECLARE_NODE_PARSER(ClampNodeParser)
DECLARE_PARSER(MixRGBNodeParser) DECLARE_NODE_PARSER(CombineColorNodeParser)
DECLARE_PARSER(NormalMapNodeParser) DECLARE_NODE_PARSER(CombineXYZNodeParser)
DECLARE_PARSER(SeparateColorNodeParser) DECLARE_NODE_PARSER(HueSatValNodeParser)
DECLARE_PARSER(SeparateXYZNodeParser) DECLARE_NODE_PARSER(InvertNodeParser)
DECLARE_PARSER(TexCheckerNodeParser) DECLARE_NODE_PARSER(MapRangeNodeParser)
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

@ -14,19 +14,19 @@ NodeItem NormalMapNodeParser::compute()
NodeItem color = get_input_value("Color", NodeItem::Type::Color3); NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
NodeItem strength = get_input_value("Strength", NodeItem::Type::Float); NodeItem strength = get_input_value("Strength", NodeItem::Type::Float);
NodeItem res = create_node("normalmap", "vector3"); NodeItem res = create_node("normalmap", NodeItem::Type::Vector3);
res.set_input("in", color); res.set_input("in", color);
res.set_input("scale", strength); res.set_input("scale", strength);
switch (normal_map_node->space) { switch (normal_map_node->space) {
case SHD_SPACE_TANGENT: case SHD_SPACE_TANGENT:
res.set_input("space", value(std::string("tangent"))); res.set_input("space", val(std::string("tangent")));
break; break;
case SHD_SPACE_OBJECT: case SHD_SPACE_OBJECT:
res.set_input("space", value(std::string("tangent"))); res.set_input("space", val(std::string("tangent")));
break; break;
default: default:
res.set_input("space", value(default_space)); res.set_input("space", val(default_space));
CLOG_WARN(LOG_MATERIALX_SHADER, CLOG_WARN(LOG_MATERIALX_SHADER,
"Ignoring unsupported Space: %d %s (%d), %s will be used", "Ignoring unsupported Space: %d %s (%d), %s will be used",
normal_map_node->space, normal_map_node->space,

View File

@ -9,50 +9,62 @@ 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", NodeItem::Type::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", NodeItem::Type::SurfaceShader);
surface.set_input("base_color", value(MaterialX::Color3(1.0f, 0.0f, 1.0f))); surface.set_input("base_color", val(MaterialX::Color3(1.0f, 0.0f, 1.0f)));
} }
NodeItem res = create_node("surfacematerial", "material"); NodeItem res = create_node("surfacematerial", NodeItem::Type::Material);
res.node->setName(node_name(node_, nullptr));
res.set_input("surfaceshader", surface); res.set_input("surfaceshader", surface);
return res; return res;
} }
NodeItem OutputMaterialNodeParser::compute_default() NodeItem OutputMaterialNodeParser::compute_default()
{ {
NodeItem surface = create_node("standard_surface", "surfaceshader"); NodeItem surface = create_node("standard_surface", NodeItem::Type::SurfaceShader);
surface.set_input("base_color", surface.set_input("base_color",
value(MaterialX::Color3(material_->r, material_->g, material_->b))); val(MaterialX::Color3(material_->r, material_->g, material_->b)));
surface.set_input("diffuse_roughness", value(material_->roughness)); surface.set_input("diffuse_roughness", val(material_->roughness));
if (material_->metallic > 0.0f) { if (material_->metallic > 0.0f) {
surface.set_input("metalness", value(material_->metallic)); surface.set_input("metalness", val(material_->metallic));
} }
if (material_->spec) { if (material_->spec) {
surface.set_input("specular", value(material_->spec)); surface.set_input("specular", val(material_->spec));
surface.set_input("specular_color", value(material_->spec)); surface.set_input("specular_color", val(material_->spec));
surface.set_input("specular_roughness", value(material_->roughness)); surface.set_input("specular_roughness", val(material_->roughness));
} }
NodeItem res = create_node("surfacematerial", "material"); NodeItem res = create_node("surfacematerial", NodeItem::Type::Material);
res.node->setName("Material_Default"); res.node->setName("Material_Default");
res.set_input("surfaceshader", surface); res.set_input("surfaceshader", surface);
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

@ -18,12 +18,12 @@ NodeItem SeparateColorNodeParser::compute()
case NODE_COMBSEP_COLOR_RGB: case NODE_COMBSEP_COLOR_RGB:
break; break;
case NODE_COMBSEP_COLOR_HSV: case NODE_COMBSEP_COLOR_HSV:
convert = create_node("rgbtohsv", "color3"); convert = create_node("rgbtohsv", NodeItem::Type::Color3);
convert.set_input("in", color); convert.set_input("in", color);
break; break;
case NODE_COMBSEP_COLOR_HSL: case NODE_COMBSEP_COLOR_HSL:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported color model, using HSV instead: %d", mode); CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported color model, using HSV instead: %d", mode);
convert = create_node("rgbtohsv", "color3"); convert = create_node("rgbtohsv", NodeItem::Type::Color3);
convert.set_input("in", color); convert.set_input("in", color);
break; break;
default: default:
@ -44,7 +44,7 @@ NodeItem CombineColorNodeParser::compute()
NodeItem blue = get_input_value("Blue", NodeItem::Type::Float); NodeItem blue = get_input_value("Blue", NodeItem::Type::Float);
NodeItem convert = empty(); NodeItem convert = empty();
NodeItem combine = create_node("combine3", "color3"); NodeItem combine = create_node("combine3", NodeItem::Type::Color3);
combine.set_input("in1", red); combine.set_input("in1", red);
combine.set_input("in2", green); combine.set_input("in2", green);
combine.set_input("in3", blue); combine.set_input("in3", blue);
@ -53,12 +53,12 @@ NodeItem CombineColorNodeParser::compute()
case NODE_COMBSEP_COLOR_RGB: case NODE_COMBSEP_COLOR_RGB:
break; break;
case NODE_COMBSEP_COLOR_HSV: case NODE_COMBSEP_COLOR_HSV:
convert = create_node("hsvtorgb", "color3"); convert = create_node("hsvtorgb", NodeItem::Type::Color3);
convert.set_input("in", combine); convert.set_input("in", combine);
break; break;
case NODE_COMBSEP_COLOR_HSL: case NODE_COMBSEP_COLOR_HSL:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported color model, using HSV instead: %d", mode); CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported color model, using HSV instead: %d", mode);
convert = create_node("hsvtorgb", "color3"); convert = create_node("hsvtorgb", NodeItem::Type::Color3);
convert.set_input("in", combine); convert.set_input("in", combine);
break; break;
default: default:

View File

@ -18,7 +18,7 @@ NodeItem CombineXYZNodeParser::compute()
NodeItem x = get_input_value("X", NodeItem::Type::Float); NodeItem x = get_input_value("X", NodeItem::Type::Float);
NodeItem y = get_input_value("Y", NodeItem::Type::Float); NodeItem y = get_input_value("Y", NodeItem::Type::Float);
NodeItem z = get_input_value("Z", NodeItem::Type::Float); NodeItem z = get_input_value("Z", NodeItem::Type::Float);
NodeItem res = create_node("combine3", "vector3"); NodeItem res = create_node("combine3", NodeItem::Type::Vector3);
res.set_input("in1", x); res.set_input("in1", x);
res.set_input("in2", y); res.set_input("in2", y);
res.set_input("in3", z); res.set_input("in3", z);

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,36 +8,21 @@ 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);
if (!vector) { if (!vector) {
vector = create_node("texcoord", "vector2"); vector = create_node("texcoord", NodeItem::Type::Vector2);
} }
vector = vector * scale; vector = (vector * scale) % val(2.0f);
NodeItem mix = (vector.extract(0).floor() + vector.extract(1).floor())
NodeItem separate = create_node("separate2", "multioutput"); .if_else(NodeItem::CompareOp::Eq, val(1.0f), val(1.0f), val(0.0f));
separate.set_input("in", vector); NodeItem res = create_node("mix", NodeItem::Type::Color3);
separate.add_output("outx", NodeItem::Type::Float); res.set_input("fg", color1);
separate.add_output("outy", NodeItem::Type::Float); res.set_input("bg", color2);
res.set_input("mix", mix);
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");
res.set_input("bg", color1);
res.set_input("fg", color2);
res.set_input("mix", ifequal);
return res; return res;
} }

View File

@ -23,9 +23,9 @@ NodeItem TexEnvironmentNodeParser::compute()
image_path = io::hydra::cache_or_get_image_file(bmain, scene, image, &tex->iuser); image_path = io::hydra::cache_or_get_image_file(bmain, scene, image, &tex->iuser);
#endif #endif
NodeItem texcoord = create_node("texcoord", "vector2"); NodeItem texcoord = create_node("texcoord", NodeItem::Type::Vector2);
NodeItem res = create_node("image", "color3"); NodeItem res = create_node("image", NodeItem::Type::Color3);
res.set_input("file", image_path, "filename"); res.set_input("file", image_path, NodeItem::Type::Filename);
res.set_input("texcoord", texcoord); res.set_input("texcoord", texcoord);
return res; return res;
} }

View File

@ -23,9 +23,9 @@ NodeItem TexImageNodeParser::compute()
image_path = io::hydra::cache_or_get_image_file(bmain, scene, image, &tex->iuser); image_path = io::hydra::cache_or_get_image_file(bmain, scene, image, &tex->iuser);
#endif #endif
NodeItem texcoord = create_node("texcoord", "vector2"); NodeItem texcoord = create_node("texcoord", NodeItem::Type::Vector2);
NodeItem res = create_node("image", "color3"); NodeItem res = create_node("image", NodeItem::Type::Color3);
res.set_input("file", image_path, "filename"); res.set_input("file", image_path, NodeItem::Type::Filename);
res.set_input("texcoord", texcoord); res.set_input("texcoord", texcoord);
return res; return res;
} }

View File

@ -13,13 +13,13 @@ NodeItem TexNoiseNodeParser::compute()
NodeItem lacunarity = get_input_value("Lacunarity", NodeItem::Type::Float); NodeItem lacunarity = get_input_value("Lacunarity", NodeItem::Type::Float);
if (detail.value && detail.type() == NodeItem::Type::Float) { if (detail.value && detail.type() == NodeItem::Type::Float) {
detail = value(int(detail.value->asA<float>())); detail = val(int(detail.value->asA<float>()));
} }
NodeItem position = create_node("position", "vector3"); NodeItem position = create_node("position", NodeItem::Type::Vector3);
position = position * scale; position = position * scale;
NodeItem res = create_node("fractal3d", "color3"); NodeItem res = create_node("fractal3d", NodeItem::Type::Color3);
res.set_input("position", position); res.set_input("position", position);
res.set_input("octaves", detail); res.set_input("octaves", detail);
res.set_input("lacunarity", lacunarity); res.set_input("lacunarity", lacunarity);

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();
@ -35,7 +35,7 @@ NodeItem VectorMathNodeParser::compute()
res = x.ceil(); res = x.ceil();
break; break;
case NODE_VECTOR_MATH_FRACTION: case NODE_VECTOR_MATH_FRACTION:
res = x % value(1.0f); res = x % val(1.0f);
break; break;
case NODE_VECTOR_MATH_LENGTH: case NODE_VECTOR_MATH_LENGTH:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op); CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
@ -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;