Export material to MaterialX for Hydra render #111765

Manually merged
Brecht Van Lommel merged 48 commits from DagerD/blender:matx-export-material into main 2023-09-26 18:56:40 +02:00
79 changed files with 4062 additions and 19 deletions

View File

@ -225,6 +225,9 @@ typedef int (*NodeGPUExecFunction)(struct GPUMaterial *mat,
struct bNodeExecData *execdata,
struct GPUNodeStack *in,
struct GPUNodeStack *out);
typedef void (*NodeMaterialXFunction)(void *data,
BogdanNagirniak marked this conversation as resolved Outdated

I would name this just NodeMaterialXFunction, the Exec is from a time when these functions actually executed the nodes.

I would name this just `NodeMaterialXFunction`, the `Exec` is from a time when these functions actually executed the nodes.
struct bNode *node,
struct bNodeSocket *out);
/**
* \brief Defines a node type.
@ -339,6 +342,8 @@ typedef struct bNodeType {
NodeExecFunction exec_fn;
/* gpu */
NodeGPUExecFunction gpu_fn;
/* MaterialX */
NodeMaterialXFunction materialx_fn;
/* Get an instance of this node's compositor operation. Freeing the instance is the
* responsibility of the caller. */

View File

@ -209,6 +209,11 @@ if(WITH_OPENVDB)
)
endif()
if(WITH_MATERIALX)
add_definitions(-DWITH_MATERIALX)
BogdanNagirniak marked this conversation as resolved Outdated

Add add_definitions(-DWITH_MATERIALX)

Add `add_definitions(-DWITH_MATERIALX)`
list(APPEND LIB MaterialXCore)
endif()
blender_add_lib(bf_io_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# RNA_prototypes.h

View File

@ -52,7 +52,9 @@ class HydraSceneDelegate : public pxr::HdSceneDelegate {
const View3D *view3d = nullptr;
Main *bmain = nullptr;
Scene *scene = nullptr;
ShadingSettings shading_settings;
bool use_materialx = true;
private:
ObjectDataMap objects_;

View File

@ -12,6 +12,11 @@
#include <pxr/imaging/hd/tokens.h>
#include <pxr/usdImaging/usdImaging/materialParamUtils.h>
#ifdef WITH_MATERIALX
BogdanNagirniak marked this conversation as resolved Outdated

Add #ifdef WITH_MATERIALX here and other places in this file, to make it possible to build without materialx.

Add `#ifdef WITH_MATERIALX` here and other places in this file, to make it possible to build without materialx.
# include <pxr/usd/usdMtlx/reader.h>
# include <pxr/usd/usdMtlx/utils.h>
#endif
#include "MEM_guardedalloc.h"
#include "BKE_lib_id.h"
@ -30,7 +35,10 @@
#include "intern/usd_exporter_context.h"
#include "intern/usd_writer_material.h"
#ifdef WITH_MATERIALX
# include "shader/materialx/node_parser.h"
# include "shader/materialx/material.h"
#endif
namespace blender::io::hydra {
MaterialData::MaterialData(HydraSceneDelegate *scene_delegate,
@ -67,10 +75,35 @@ void MaterialData::init()
get_time_code,
export_params,
image_cache_file_path()};
/* Create USD material. */
pxr::UsdShadeMaterial usd_material = usd::create_usd_material(
export_context, material_path, (Material *)id, "st");
pxr::UsdShadeMaterial usd_material;
#ifdef WITH_MATERIALX
if (scene_delegate_->use_materialx) {
MaterialX::DocumentPtr doc = blender::nodes::materialx::export_to_materialx(
scene_delegate_->depsgraph, (Material *)id, cache_or_get_image_file);
pxr::UsdMtlxRead(doc, stage);
BogdanNagirniak marked this conversation as resolved

for not to -> to not

for not to -> to not
/* Logging stage: creating lambda stage_str() to not call stage->ExportToString()
* if log won't be printed. */
auto stage_str = [&stage]() {
std::string str;
stage->ExportToString(&str);
return str;
};
ID_LOGN(2, "Stage:\n%s", stage_str().c_str());
if (pxr::UsdPrim materials = stage->GetPrimAtPath(pxr::SdfPath("/MaterialX/Materials"))) {
pxr::UsdPrimSiblingRange children = materials.GetChildren();
if (!children.empty()) {
usd_material = pxr::UsdShadeMaterial(*children.begin());
}
}
}
else
#endif
{
usd_material = usd::create_usd_material(export_context, material_path, (Material *)id, "st");
}
/* Convert USD material to Hydra material network map, adapted for render delegate. */
const pxr::HdRenderDelegate *render_delegate =

View File

@ -998,6 +998,11 @@ static void rna_def_render_engine(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Use Alembic Procedural", "Support loading Alembic data at render time");
prop = RNA_def_property(srna, "bl_use_materialx", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "type->flag", RE_USE_MATERIALX);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_ui_text(prop, "Use MaterialX", "Use MaterialX for exporting materials to Hydra");
RNA_define_verify_sdna(true);
}

View File

@ -16,6 +16,7 @@ set(INC
../../makesrna
../../render
../../windowmanager
../../../../intern/clog
../../../../intern/sky/include
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@ -136,6 +137,25 @@ set(LIB
bf_nodes
)
if(WITH_MATERIALX)
add_definitions(-DWITH_MATERIALX)
list(APPEND SRC
materialx/material.cc
materialx/node_item.cc
materialx/node_parser.cc
materialx/group_nodes.cc
materialx/material.h
materialx/node_item.h
materialx/node_parser.h
materialx/group_nodes.h
)
list(APPEND LIB
MaterialXCore
MaterialXFormat
)
endif()
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()

View File

@ -0,0 +1,188 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "group_nodes.h"
#include "node_parser.h"
#include "BLI_vector.hh"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
namespace blender::nodes::materialx {
GroupNodeParser::GroupNodeParser(MaterialX::GraphElement *graph,
const Depsgraph *depsgraph,
const Material *material,
const bNode *node,
const bNodeSocket *socket_out,
NodeItem::Type to_type,
GroupNodeParser *group_parser,
ExportImageFunction export_image_fn,
bool use_group_default)
: NodeParser(
graph, depsgraph, material, node, socket_out, to_type, group_parser, export_image_fn),
use_group_default_(use_group_default)
{
}
NodeItem GroupNodeParser::compute()
{
NodeItem res = empty();
const bNodeTree *ngroup = reinterpret_cast<const bNodeTree *>(node_->id);
ngroup->ensure_topology_cache();
const bNode *node_out = ngroup->group_output_node();
if (!node_out) {
return res;
}
MaterialX::GraphElement *graph = graph_;
#ifdef USE_MATERIALX_NODEGRAPH
std::string name = MaterialX::createValidName(ngroup->id.name);
MaterialX::NodeGraphPtr group_graph = graph_->getChildOfType<MaterialX::NodeGraph>(name);
if (!group_graph) {
CLOG_INFO(LOG_MATERIALX_SHADER, 1, "<nodegraph name=%s>", name.c_str());
group_graph = graph_->addChild<MaterialX::NodeGraph>(name);
}
graph = group_graph.get();
#endif
NodeItem out = GroupOutputNodeParser(graph,
depsgraph_,
material_,
node_out,
socket_out_,
to_type_,
this,
export_image_fn_,
use_group_default_)
.compute_full();
#ifdef USE_MATERIALX_NODEGRAPH
/* We have to be in NodeParser's graph_, therefore copying output */
res.output = out.output;
#else
res = out;
#endif
return res;
}
BogdanNagirniak marked this conversation as resolved Outdated

Can you explain why this is using output + index rather than output socket names?

I guess it's not to avoid naming conflicts, because output1 isn't unique enough that a user would never name their socket like this.

Can you explain why this is using `output + index` rather than output socket names? I guess it's not to avoid naming conflicts, because `output1` isn't unique enough that a user would never name their socket like this.

Changed name to out_<out socket name>

Changed name to `out_<out socket name>`
NodeItem GroupNodeParser::compute_full()
{
NodeItem res = compute();
if (NodeItem::is_arithmetic(to_type_)) {
res = res.convert(to_type_);
}
return res;
}
NodeItem GroupOutputNodeParser::compute()
{
#ifdef USE_MATERIALX_NODEGRAPH
Vector<NodeItem> values;
for (auto socket_in : node_->input_sockets()) {
NodeItem value = get_input_value(
socket_in->index(), NodeItem::is_arithmetic(to_type_) ? NodeItem::Type::Any : to_type_);
if (value.value) {
value = create_node("constant", value.type(), {{"value", value}});
}
values.append(value);
}
Vector<NodeItem> outputs;
for (int i = 0; i < values.size(); ++i) {
if (values[i]) {
outputs.append(create_output(out_name(node_->input_sockets()[i]), values[i]));
}
}
return outputs[socket_out_->index()];
#else
if (use_group_default_) {
return get_input_value(socket_out_->index(), to_type_);
}
return get_input_link(socket_out_->index(), to_type_);
#endif
}
NodeItem GroupOutputNodeParser::compute_full()
{
CLOG_INFO(LOG_MATERIALX_SHADER,
1,
"%s [%d] => %s",
node_->name,
node_->typeinfo->type,
NodeItem::type(to_type_).c_str());
#ifdef USE_MATERIALX_NODEGRAPH
NodeItem res = empty();
/* Checking if output was already computed */
res.output = graph_->getOutput(out_name(socket_out_));
if (res.output) {
return res;
}
res = compute();
return res;
#else
return compute();
#endif
}
std::string GroupOutputNodeParser::out_name(const bNodeSocket *out_socket)
{
return MaterialX::createValidName(std::string("out_") + out_socket->name);
}
NodeItem GroupInputNodeParser::compute()
{
#ifdef USE_MATERIALX_NODEGRAPH
NodeItem value = group_parser_->get_input_link(socket_out_->index(), to_type_);
if (!value) {
return empty();
}
if (value.value) {
value = group_parser_->create_node("constant", value.type(), {{"value", value}});
}
return create_input(in_name(), value);
#else
if (use_group_default_) {
return group_parser_->get_input_value(socket_out_->index(), to_type_);
}
return group_parser_->get_input_link(socket_out_->index(), to_type_);
#endif
}
NodeItem GroupInputNodeParser::compute_full()
{
CLOG_INFO(LOG_MATERIALX_SHADER,
1,
"%s [%d] => %s",
node_->name,
node_->typeinfo->type,
NodeItem::type(to_type_).c_str());
#ifdef USE_MATERIALX_NODEGRAPH
NodeItem res = empty();
/* Checking if input was already computed */
res.input = graph_->getInput(in_name());
if (res.input) {
return res;
}
res = compute();
return res;
#else
return compute();
#endif
}
std::string GroupInputNodeParser::in_name() const
{
return MaterialX::createValidName(std::string("in_") + socket_out_->name);
}
} // namespace blender::nodes::materialx

View File

@ -0,0 +1,57 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "node_parser.h"
/* TODO: pxr::UsdMtlxRead() doesn't perform nodegraphs.
* Uncomment USE_MATERIALX_NODEGRAPH after fixing it. */
//#define USE_MATERIALX_NODEGRAPH
namespace blender::nodes::materialx {
class GroupInputNodeParser;
class GroupNodeParser : public NodeParser {
friend GroupInputNodeParser;
protected:
bool use_group_default_;
public:
GroupNodeParser(MaterialX::GraphElement *graph,
const Depsgraph *depsgraph,
const Material *material,
const bNode *node,
const bNodeSocket *socket_out,
NodeItem::Type to_type,
GroupNodeParser *group_parser,
ExportImageFunction export_image_fn,
bool use_group_default);
NodeItem compute() override;
NodeItem compute_full() override;
};
class GroupOutputNodeParser : public GroupNodeParser {
public:
using GroupNodeParser::GroupNodeParser;
NodeItem compute() override;
NodeItem compute_full() override;
private:
static std::string out_name(const bNodeSocket *out_socket);
};
class GroupInputNodeParser : public GroupNodeParser {
public:
using GroupNodeParser::GroupNodeParser;
NodeItem compute() override;
NodeItem compute_full() override;
private:
std::string in_name() const;
};
} // namespace blender::nodes::materialx

View File

@ -0,0 +1,109 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "material.h"
#include "node_parser.h"
#include <MaterialXFormat/XmlIo.h>
#include "DEG_depsgraph.hh"
#include "DNA_material_types.h"
#include "NOD_shader.h"
namespace blender::nodes::materialx {
class DefaultMaterialNodeParser : public NodeParser {
public:
using NodeParser::NodeParser;
NodeItem compute() override
{
NodeItem surface = create_node(
"standard_surface",
NodeItem::Type::SurfaceShader,
BogdanNagirniak marked this conversation as resolved Outdated

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

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

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

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

Exporting of group nodes is implemented in `materialx/group_nodes.cc/.h`
{{"base_color", val(MaterialX::Color3(material_->r, material_->g, material_->b))},
{"diffuse_roughness", val(material_->roughness)}});
if (material_->metallic > 0.0f) {
surface.set_input("metalness", val(material_->metallic));
}
if (material_->spec) {
surface.set_input("specular", val(material_->spec));
surface.set_input("specular_color", val(material_->spec));
surface.set_input("specular_roughness", val(material_->roughness));
}
NodeItem res = create_node(
"surfacematerial", NodeItem::Type::Material, {{"surfaceshader", surface}});
res.node->setName("Material_Default");
return res;
}
NodeItem compute_error()
{
NodeItem surface = create_node("standard_surface",
NodeItem::Type::SurfaceShader,
{{"base_color", val(MaterialX::Color3(1.0f, 0.0f, 1.0f))}});
NodeItem res = create_node(
"surfacematerial", NodeItem::Type::Material, {{"surfaceshader", surface}});
res.node->setName("Material_Error");
return res;
}
};
MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph,
Material *material,
ExportImageFunction export_image_fn)
{
CLOG_INFO(LOG_MATERIALX_SHADER, 0, "Material: %s", material->id.name);
MaterialX::DocumentPtr doc = MaterialX::createDocument();
if (material->use_nodes) {
material->nodetree->ensure_topology_cache();
bNode *output_node = ntreeShaderOutputNode(material->nodetree, SHD_OUTPUT_ALL);
if (output_node) {
NodeParserData data = {doc.get(),
depsgraph,
material,
NodeItem::Type::Material,
nullptr,
NodeItem(doc.get()),
export_image_fn};
output_node->typeinfo->materialx_fn(&data, output_node, nullptr);
}
else {
DefaultMaterialNodeParser(doc.get(),
depsgraph,
material,
nullptr,
nullptr,
NodeItem::Type::Material,
nullptr,
export_image_fn)
.compute_error();
}
}
else {
DefaultMaterialNodeParser(doc.get(),
depsgraph,
material,
nullptr,
nullptr,
NodeItem::Type::Material,
nullptr,
export_image_fn)
.compute();
}
CLOG_INFO(LOG_MATERIALX_SHADER,
1,
"Material: %s\n%s",
material->id.name,
MaterialX::writeToXmlString(doc).c_str());
return doc;
}
} // namespace blender::nodes::materialx

View File

@ -0,0 +1,20 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <MaterialXCore/Document.h>
struct Depsgraph;
struct Material;
class ExportImageFunction;
namespace blender::nodes::materialx {
MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph,
Material *material,
ExportImageFunction export_image_fn);
} // namespace blender::nodes::materialx

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,161 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <map>
#include <MaterialXCore/Node.h>
namespace blender::nodes::materialx {
/**
* This class serves as abstraction from MateralX API. It implements arithmetic operations,
* convertions between different types, adding new nodes, setting inputs, etc.
* All work should be done via this class instead of using MaterialX API directly.
*/
class NodeItem {
public:
using Inputs = std::vector<std::pair<std::string, NodeItem>>;
enum class Type {
Any = 0,
Empty,
Multioutput,
/* Value types */
String,
Filename,
Boolean,
Integer,
/* Arithmetic types. NOTE: Ordered by type cast */
Float,
Vector2,
Vector3,
Color3,
Vector4,
Color4,
/* Shader types. NOTE: There are only supported types */
BSDF,
EDF,
DisplacementShader,
SurfaceShader,
Material,
/* Special type to retrieve opacity for <surface> */
SurfaceOpacity,
};
enum class CompareOp { Less = 0, LessEq, Eq, GreaterEq, Greater, NotEq };
public:
MaterialX::ValuePtr value;
MaterialX::NodePtr node;
MaterialX::InputPtr input;
MaterialX::OutputPtr output;
private:
MaterialX::GraphElement *graph_ = nullptr;
public:
/* NOTE: Default constructor added to allow easy work with std::map.
* Don't use this constructor to create NodeItem. */
NodeItem() = default;
NodeItem(MaterialX::GraphElement *graph);
~NodeItem() = default;
static Type type(const std::string &type_str);
static std::string type(Type type);
static bool is_arithmetic(Type type);
/* Operators */
operator bool() const;
NodeItem operator+(const NodeItem &other) const;
NodeItem operator-(const NodeItem &other) const;
NodeItem operator-() const;
NodeItem operator*(const NodeItem &other) const;
NodeItem operator/(const NodeItem &other) const;
NodeItem operator%(const NodeItem &other) const;
NodeItem operator^(const NodeItem &other) const;
NodeItem operator[](int index) const;
bool operator==(const NodeItem &other) const;
bool operator!=(const NodeItem &other) const;
/* Math functions */
NodeItem abs() const;
NodeItem floor() const;
NodeItem ceil() const;
NodeItem length() const;
NodeItem normalize() const;
NodeItem min(const NodeItem &other) const;
NodeItem max(const NodeItem &other) const;
NodeItem dotproduct(const NodeItem &other) const;
NodeItem mix(const NodeItem &val1, const NodeItem &val2) const;
NodeItem clamp(const NodeItem &min_val, const NodeItem &max_val) const;
NodeItem clamp(float min_val = 0.0f, float max_val = 1.0f) const;
NodeItem rotate(const NodeItem &angle, const NodeItem &axis); /* angle in degrees */
NodeItem rotate(const NodeItem &angle_xyz, bool invert = false); /* angle in degrees */
NodeItem sin() const;
NodeItem cos() const;
NodeItem tan() const;
NodeItem asin() const;
NodeItem acos() const;
NodeItem atan() const;
NodeItem atan2(const NodeItem &other) const;
NodeItem sinh() const;
NodeItem cosh() const;
NodeItem tanh() const;
NodeItem ln() const;
NodeItem sqrt() const;
NodeItem sign() const;
NodeItem exp() const;
NodeItem convert(Type to_type) const;
NodeItem to_vector() const;
NodeItem if_else(CompareOp op,
const NodeItem &other,
const NodeItem &if_val,
const NodeItem &else_val) const;
/* Useful functions */
NodeItem empty() const;
template<class T> NodeItem val(const T &data) const;
Type type() const;
/* Node functions */
NodeItem create_node(const std::string &category, Type type) const;
NodeItem create_node(const std::string &category, Type type, const Inputs &inputs) const;
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 NodeItem &item);
NodeItem add_output(const std::string &out_name, Type out_type);
/* Output functions */
NodeItem create_input(const std::string &name, const NodeItem &item) const;
NodeItem create_output(const std::string &name, const NodeItem &item) const;
private:
static Type cast_types(NodeItem &item1, NodeItem &item2);
bool is_arithmetic() const;
NodeItem arithmetic(const std::string &category, std::function<float(float)> func) const;
NodeItem arithmetic(const NodeItem &other,
const std::string &category,
std::function<float(float, float)> func,
Type to_type = Type::Any) const;
};
template<class T> NodeItem NodeItem::val(const T &data) const
{
NodeItem res(graph_);
res.value = MaterialX::Value::createValue<T>(data);
return res;
}
template<class T>
void NodeItem::set_input(const std::string &in_name, const T &value, Type in_type)
{
node->setInputValue(in_name, value, type(in_type));
}
} // namespace blender::nodes::materialx

View File

@ -0,0 +1,270 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "node_parser.h"
#include "group_nodes.h"
#include "BKE_node_runtime.hh"
namespace blender::nodes::materialx {
static const std::string TEXCOORD_NODE_NAME = "node_texcoord";
CLG_LOGREF_DECLARE_GLOBAL(LOG_MATERIALX_SHADER, "materialx.shader");
NodeParser::NodeParser(MaterialX::GraphElement *graph,
const Depsgraph *depsgraph,
const Material *material,
const bNode *node,
const bNodeSocket *socket_out,
NodeItem::Type to_type,
GroupNodeParser *group_parser,
ExportImageFunction export_image_fn)
: graph_(graph),
depsgraph_(depsgraph),
material_(material),
node_(node),
socket_out_(socket_out),
to_type_(to_type),
group_parser_(group_parser),
export_image_fn_(export_image_fn)
{
}
NodeItem NodeParser::compute_full()
{
NodeItem res = empty();
/* Checking if node was already computed */
res.node = graph_->getNode(node_name());
if (!res.node) {
CLOG_INFO(LOG_MATERIALX_SHADER,
1,
"%s [%d] => %s",
node_->name,
node_->typeinfo->type,
NodeItem::type(to_type_).c_str());
res = compute();
if (res.node) {
res.node->setName(node_name());
}
}
if (NodeItem::is_arithmetic(to_type_)) {
res = res.convert(to_type_);
}
return res;
}
std::string NodeParser::node_name() const
{
std::string name = node_->name;
if (node_->output_sockets().size() > 1) {
name += std::string("_") + socket_out_->name;
}
if (ELEM(to_type_, NodeItem::Type::BSDF, NodeItem::Type::EDF, NodeItem::Type::SurfaceOpacity)) {
name += "_" + NodeItem::type(to_type_);
}
#ifdef USE_MATERIALX_NODEGRAPH
return MaterialX::createValidName(name);
#else
std::string prefix;
GroupNodeParser *gr = group_parser_;
while (gr) {
const bNodeTree *ngroup = reinterpret_cast<const bNodeTree *>(gr->node_->id);
prefix = MaterialX::createValidName(ngroup->id.name) + "_" + prefix;
gr = gr->group_parser_;
}
return prefix + MaterialX::createValidName(name);
#endif
}
NodeItem NodeParser::create_node(const std::string &category, NodeItem::Type type)
{
return empty().create_node(category, type);
}
NodeItem NodeParser::create_node(const std::string &category,
NodeItem::Type type,
const NodeItem::Inputs &inputs)
{
return empty().create_node(category, type, inputs);
}
NodeItem NodeParser::create_input(const std::string &name, const NodeItem &item)
{
return empty().create_input(name, item);
}
NodeItem NodeParser::create_output(const std::string &name, const NodeItem &item)
{
return empty().create_output(name, item);
}
NodeItem NodeParser::get_input_default(const std::string &name, NodeItem::Type to_type)
{
return get_default(node_->input_by_identifier(name), to_type);
}
NodeItem NodeParser::get_input_default(int index, NodeItem::Type to_type)
{
return get_default(node_->input_socket(index), to_type);
}
NodeItem NodeParser::get_input_link(const std::string &name, NodeItem::Type to_type)
{
return get_input_link(node_->input_by_identifier(name), to_type, false);
}
NodeItem NodeParser::get_input_link(int index, NodeItem::Type to_type)
{
return get_input_link(node_->input_socket(index), to_type, false);
}
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::get_output_default(const std::string &name, NodeItem::Type to_type)
{
return get_default(node_->output_by_identifier(name), to_type);
}
NodeItem NodeParser::get_output_default(int index, NodeItem::Type to_type)
{
return get_default(node_->output_socket(index), to_type);
}
NodeItem NodeParser::empty() const
{
return NodeItem(graph_);
}
NodeItem NodeParser::texcoord_node(NodeItem::Type type)
{
BLI_assert(ELEM(type, NodeItem::Type::Vector2, NodeItem::Type::Vector3));
std::string name = TEXCOORD_NODE_NAME;
if (type == NodeItem::Type::Vector3) {
name += "_vector3";
}
NodeItem res = empty();
res.node = graph_->getNode(name);
if (!res.node) {
res = create_node("texcoord", type);
res.node->setName(name);
}
return res;
}
NodeItem NodeParser::get_default(const bNodeSocket &socket, NodeItem::Type to_type)
{
NodeItem res = empty();
if (!NodeItem::is_arithmetic(to_type) && to_type != NodeItem::Type::Any) {
return res;
}
switch (socket.type) {
case SOCK_CUSTOM:
/* Return empty */
break;
case SOCK_FLOAT: {
float v = socket.default_value_typed<bNodeSocketValueFloat>()->value;
res.value = MaterialX::Value::createValue<float>(v);
break;
}
case SOCK_VECTOR: {
const float *v = socket.default_value_typed<bNodeSocketValueVector>()->value;
res.value = MaterialX::Value::createValue<MaterialX::Vector3>(
MaterialX::Vector3(v[0], v[1], v[2]));
break;
}
case SOCK_RGBA: {
const float *v = socket.default_value_typed<bNodeSocketValueRGBA>()->value;
res.value = MaterialX::Value::createValue<MaterialX::Color4>(
MaterialX::Color4(v[0], v[1], v[2], v[3]));
break;
}
default: {
CLOG_WARN(LOG_MATERIALX_SHADER, "Unsupported socket type: %d", socket.type);
}
}
return res.convert(to_type);
}
NodeItem NodeParser::get_input_link(const bNodeSocket &socket,
NodeItem::Type to_type,
bool use_group_default)
{
const bNodeLink *link = socket.link;
if (!(link && link->is_used())) {
return empty();
}
const bNode *from_node = link->fromnode;
/* Passing NODE_REROUTE nodes */
while (from_node->is_reroute()) {
link = from_node->input_socket(0).link;
if (!(link && link->is_used())) {
return empty();
}
from_node = link->fromnode;
}
if (from_node->is_group()) {
return GroupNodeParser(graph_,
depsgraph_,
material_,
from_node,
link->fromsock,
to_type,
group_parser_,
export_image_fn_,
use_group_default)
.compute_full();
}
if (from_node->is_group_input()) {
return GroupInputNodeParser(graph_,
depsgraph_,
material_,
from_node,
link->fromsock,
to_type,
group_parser_,
export_image_fn_,
use_group_default)
.compute_full();
}
if (!from_node->typeinfo->materialx_fn) {
CLOG_WARN(LOG_MATERIALX_SHADER,
"Unsupported node: %s [%d]",
from_node->name,
from_node->typeinfo->type);
return empty();
}
NodeParserData data = {
graph_, depsgraph_, material_, to_type, group_parser_, empty(), export_image_fn_};
from_node->typeinfo->materialx_fn(&data, const_cast<bNode *>(from_node), link->fromsock);
return data.result;
}
NodeItem NodeParser::get_input_value(const bNodeSocket &socket, NodeItem::Type to_type)
{
NodeItem res = get_input_link(socket, to_type, true);
if (!res) {
res = get_default(socket, to_type);
}
return res;
}
} // namespace blender::nodes::materialx

View File

@ -0,0 +1,139 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "node_item.h"
#include "DEG_depsgraph.hh"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "CLG_log.h"
namespace blender::nodes::materialx {
extern struct CLG_LogRef *LOG_MATERIALX_SHADER;
class GroupNodeParser;
using ExportImageFunction = std::function<std::string(Main *, Scene *, Image *, ImageUser *)>;
/**
* This is base abstraction class for parsing Blender nodes into MaterialX nodes.
* NodeParser::compute() should be overrided in child classes.
*/
class NodeParser {
protected:
MaterialX::GraphElement *graph_;
const Depsgraph *depsgraph_;
const Material *material_;
const bNode *node_;
const bNodeSocket *socket_out_;
NodeItem::Type to_type_;
GroupNodeParser *group_parser_;
ExportImageFunction export_image_fn_;
public:
NodeParser(MaterialX::GraphElement *graph,
const Depsgraph *depsgraph,
const Material *material,
const bNode *node,
const bNodeSocket *socket_out,
NodeItem::Type to_type,
GroupNodeParser *group_parser,
ExportImageFunction export_image_fn);
virtual ~NodeParser() = default;
virtual NodeItem compute() = 0;
virtual NodeItem compute_full();
protected:
std::string node_name() const;
NodeItem create_node(const std::string &category, NodeItem::Type type);
NodeItem create_node(const std::string &category,
NodeItem::Type type,
const NodeItem::Inputs &inputs);
NodeItem create_input(const std::string &name, const NodeItem &item);
NodeItem create_output(const std::string &name, const NodeItem &item);
NodeItem get_input_default(const std::string &name, NodeItem::Type to_type);
NodeItem get_input_default(int index, NodeItem::Type to_type);
NodeItem get_output_default(const std::string &name, NodeItem::Type to_type);
NodeItem get_output_default(int index, NodeItem::Type to_type);
NodeItem get_input_link(const std::string &name, NodeItem::Type to_type);
NodeItem get_input_link(int index, NodeItem::Type to_type);
NodeItem get_input_value(const std::string &name, NodeItem::Type to_type);
NodeItem get_input_value(int index, NodeItem::Type to_type);
NodeItem empty() const;
template<class T> NodeItem val(const T &data) const;
NodeItem texcoord_node(NodeItem::Type type = NodeItem::Type::Vector2);
private:
NodeItem get_default(const bNodeSocket &socket, NodeItem::Type to_type);
NodeItem get_input_link(const bNodeSocket &socket,
NodeItem::Type to_type,
bool use_group_default);
NodeItem get_input_value(const bNodeSocket &socket, NodeItem::Type to_type);
};
template<class T> NodeItem NodeParser::val(const T &data) const
{
return empty().val(data);
}
/**
* Defines for including MaterialX node parsing code into node_shader_<name>.cc
*
* Example:
* \code{.c}
* NODE_SHADER_MATERIALX_BEGIN
* #ifdef WITH_MATERIALX
* {
* NodeItem color = get_input_value("Color", NodeItem::Type::Color4);
* NodeItem gamma = get_input_value("Gamma", NodeItem::Type::Float);
* return color ^ gamma;
* }
* #endif
* NODE_SHADER_MATERIALX_END
* \endcode
*/
struct NodeParserData {
MaterialX::GraphElement *graph;
const Depsgraph *depsgraph;
const Material *material;
NodeItem::Type to_type;
GroupNodeParser *group_parser;
NodeItem result;
ExportImageFunction export_image_fn;
};
#define NODE_SHADER_MATERIALX_BEGIN \
class MaterialXNodeParser : public materialx::NodeParser { \
public: \
using materialx::NodeParser::NodeParser; \
materialx::NodeItem compute() override; \
}; \
\
materialx::NodeItem MaterialXNodeParser::compute() \
{ \
using NodeItem = materialx::NodeItem;
#define NODE_SHADER_MATERIALX_END \
} \
\
void node_shader_materialx(void *data, struct bNode *node, struct bNodeSocket *out) \
{ \
materialx::NodeParserData *d = reinterpret_cast<materialx::NodeParserData *>(data); \
d->result = MaterialXNodeParser(d->graph, \
d->depsgraph, \
d->material, \
node, \
out, \
d->to_type, \
d->group_parser, \
d->export_image_fn) \
.compute_full(); \
}
} // namespace blender::nodes::materialx

View File

@ -22,6 +22,13 @@
#include "node_shader_register.hh"
#ifdef WITH_MATERIALX
# include "materialx/node_parser.h"
#else
# define NODE_SHADER_MATERIALX_BEGIN NodeMaterialXFunction node_shader_materialx = nullptr;
# define NODE_SHADER_MATERIALX_END
#endif
struct bContext;
typedef struct bContext bContext;
struct bNodeExecContext;

View File

@ -22,6 +22,33 @@ static int node_shader_gpu_add_shader(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_add_shader", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
if (!ELEM(to_type_, NodeItem::Type::BSDF, NodeItem::Type::EDF, NodeItem::Type::SurfaceOpacity)) {
return empty();
}
NodeItem shader1 = get_input_link(0, to_type_);
NodeItem shader2 = get_input_link(1, to_type_);
if (!shader1 && !shader2) {
return empty();
}
if (shader1 && !shader2) {
return shader1;
}
if (!shader1 && shader2) {
return shader2;
}
if (to_type_ == NodeItem::Type::SurfaceOpacity) {
return (shader1 + shader2) * val(0.5f);
}
return shader1 + shader2;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_add_shader_cc
/* node type definition */
@ -34,6 +61,7 @@ void register_node_type_sh_add_shader()
sh_node_type_base(&ntype, SH_NODE_ADD_SHADER, "Add Shader", NODE_CLASS_SHADER);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_add_shader;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -55,6 +55,21 @@ static void node_shader_init_ambient_occlusion(bNodeTree * /*ntree*/, bNode *nod
node->custom2 = 0;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: observed crash while rendering MaterialX_v1_38_6::ExceptionShaderGenError */
/*
* NodeItem maxdistance = get_input_value("Distance", NodeItem::Type::Float);
* NodeItem res = create_node("ambientocclusion", NodeItem::Type::Float);
* res.set_input("coneangle", val(90.0f));
* res.set_input("maxdistance", maxdistance);
*/
return get_output_default(socket_out_->name, NodeItem::Type::Any);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_ambient_occlusion_cc
/* node type definition */
@ -69,6 +84,7 @@ void register_node_type_sh_ambient_occlusion()
ntype.draw_buttons = file_ns::node_shader_buts_ambient_occlusion;
ntype.initfunc = file_ns::node_shader_init_ambient_occlusion;
ntype.gpu_fn = file_ns::node_shader_gpu_ambient_occlusion;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -77,6 +77,16 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
return 1;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: some outputs expected be implemented within the next iteration (see nodedef
* <geompropvalue>) */
return get_output_default(socket_out_->name, NodeItem::Type::Any);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_attribute_cc
/* node type definition */
@ -93,6 +103,7 @@ void register_node_type_sh_attribute()
node_type_storage(
&ntype, "NodeShaderAttribute", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::node_shader_gpu_attribute;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -39,6 +39,15 @@ static int gpu_shader_bevel(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_bevel", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: This node isn't supported by MaterialX.*/
return get_input_link("Normal", NodeItem::Type::Vector3);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bevel_cc
/* node type definition */
@ -53,6 +62,7 @@ void register_node_type_sh_bevel()
ntype.draw_buttons = file_ns::node_shader_buts_bevel;
ntype.initfunc = file_ns::node_shader_init_bevel;
ntype.gpu_fn = file_ns::gpu_shader_bevel;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -31,6 +31,23 @@ static int node_shader_gpu_blackbody(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_blackbody", in, out, ramp_texture, GPU_constant(&layer));
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: This node doesn't have an implementation in MaterialX 1.38.6.
* It's added in MaterialX 1.38.8. Uncomment this code after switching to 1.38.8.
*
* NodeItem temperature = get_input_value("Temperature", NodeItem::Type::Float);
* NodeItem res = create_node("blackbody", NodeItem::Type::Color3);
* res.set_input("temperature", temperature);
* return res; */
NodeItem res = empty();
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_blackbody_cc
/* node type definition */
@ -44,6 +61,7 @@ void register_node_type_sh_blackbody()
ntype.declare = file_ns::node_declare;
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::MIDDLE);
ntype.gpu_fn = file_ns::node_shader_gpu_blackbody;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -23,6 +23,19 @@ static int gpu_shader_brightcontrast(GPUMaterial *mat,
return GPU_stack_link(mat, node, "brightness_contrast", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
NodeItem bright = get_input_value("Bright", NodeItem::Type::Float);
NodeItem contrast = get_input_value("Contrast", NodeItem::Type::Float);
/* This formula was given from OSL shader code in Cycles. */
return (bright + color * (contrast + val(1.0f)) - contrast * val(0.5f)).max(val(0.0f));
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_brightness_cc
void register_node_type_sh_brightcontrast()
@ -34,6 +47,7 @@ void register_node_type_sh_brightcontrast()
sh_node_type_base(&ntype, SH_NODE_BRIGHTCONTRAST, "Brightness/Contrast", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::gpu_shader_brightcontrast;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -34,6 +34,24 @@ static int node_shader_gpu_bsdf_diffuse(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_bsdf_diffuse", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
if (to_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);
return create_node("oren_nayar_diffuse_bsdf",
NodeItem::Type::BSDF,
{{"color", color}, {"roughness", roughness}, {"normal", normal}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bsdf_diffuse_cc
/* node type definition */
@ -48,6 +66,7 @@ void register_node_type_sh_bsdf_diffuse()
ntype.add_ui_poll = object_shader_nodes_poll;
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::MIDDLE);
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_diffuse;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -42,6 +42,29 @@ static int node_shader_gpu_bsdf_glass(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_bsdf_glass", in, out, GPU_constant(&use_multi_scatter));
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
if (to_type_ != NodeItem::Type::BSDF) {
return empty();
}
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
NodeItem roughness = get_input_value("Roughness", NodeItem::Type::Vector2);
NodeItem ior = get_input_value("IOR", NodeItem::Type::Float);
NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3);
return create_node("dielectric_bsdf",
NodeItem::Type::BSDF,
{{"normal", normal},
{"tint", color},
{"roughness", roughness},
{"ior", ior},
{"scatter_mode", val(std::string("RT"))}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bsdf_glass_cc
/* node type definition */
@ -57,6 +80,7 @@ void register_node_type_sh_bsdf_glass()
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::MIDDLE);
ntype.initfunc = file_ns::node_shader_init_glass;
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_glass;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -56,6 +56,36 @@ static int node_shader_gpu_bsdf_glossy(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_bsdf_glossy", in, out, GPU_constant(&use_multi_scatter));
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
if (to_type_ != NodeItem::Type::BSDF) {
return empty();
}
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
NodeItem roughness = get_input_value("Roughness", NodeItem::Type::Vector2);
NodeItem anisotropy = get_input_value("Anisotropy", NodeItem::Type::Color3);
NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3);
NodeItem tangent = get_input_link("Tangent", NodeItem::Type::Vector3);
NodeItem artistic_ior = create_node("artistic_ior",
NodeItem::Type::Multioutput,
{{"reflectivity", color}, {"edge_color", color}});
NodeItem ior_out = artistic_ior.add_output("ior", NodeItem::Type::Color3);
NodeItem extinction_out = artistic_ior.add_output("extinction", NodeItem::Type::Color3);
return create_node("conductor_bsdf",
NodeItem::Type::BSDF,
{{"normal", normal},
{"tangent", tangent},
{"ior", ior_out},
{"extinction", extinction_out},
{"roughness", roughness}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bsdf_glossy_cc
/* node type definition */
@ -72,6 +102,7 @@ void register_node_type_sh_bsdf_glossy()
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::MIDDLE);
ntype.initfunc = file_ns::node_shader_init_glossy;
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_glossy;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);

View File

@ -2,6 +2,8 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <map>
#include "BLI_string.h"
#include "node_shader_util.hh"
@ -319,6 +321,272 @@ static void node_shader_update_principled(bNodeTree *ntree, bNode *node)
sss_method != SHD_SUBSURFACE_BURLEY);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
using InputsType = std::map<std::string, NodeItem>;
/* NOTE: commented inputs aren't used for node creation. */
auto bsdf_inputs = [&]() -> InputsType {
return {
{"base_color", get_input_value("Base Color", NodeItem::Type::Color3)},
{"subsurface", get_input_value("Subsurface Weight", NodeItem::Type::Float)},
{"subsurface_scale", get_input_value("Subsurface Scale", NodeItem::Type::Float)},
{"subsurface_radius", get_input_value("Subsurface Radius", NodeItem::Type::Vector3)},
//{"subsurface_ior", get_input_value("Subsurface IOR", NodeItem::Type::Vector3)},
{"subsurface_anisotropy", get_input_value("Subsurface Anisotropy", NodeItem::Type::Float)},
{"metallic", get_input_value("Metallic", NodeItem::Type::Float)},
{"specular", get_input_value("Specular IOR Level", NodeItem::Type::Float)},
{"specular_tint", get_input_value("Specular Tint", NodeItem::Type::Color3)},
{"roughness", get_input_value("Roughness", NodeItem::Type::Float)},
{"anisotropic", get_input_value("Anisotropic", NodeItem::Type::Float)},
{"anisotropic_rotation", get_input_value("Anisotropic Rotation", NodeItem::Type::Float)},
{"sheen", get_input_value("Sheen Weight", NodeItem::Type::Float)},
{"sheen_roughness", get_input_value("Sheen Roughness", NodeItem::Type::Float)},
{"sheen_tint", get_input_value("Sheen Tint", NodeItem::Type::Color3)},
{"coat", get_input_value("Coat Weight", NodeItem::Type::Float)},
{"coat_roughness", get_input_value("Coat Roughness", NodeItem::Type::Float)},
{"coat_ior", get_input_value("Coat IOR", NodeItem::Type::Float)},
{"coat_tint", get_input_value("Coat Tint", NodeItem::Type::Color3)},
{"ior", get_input_value("IOR", NodeItem::Type::Float)},
{"transmission", get_input_value("Transmission Weight", NodeItem::Type::Float)},
{"alpha", get_input_value("Alpha", NodeItem::Type::Float)},
{"normal", get_input_link("Normal", NodeItem::Type::Vector3)},
{"coat_normal", get_input_link("Coat Normal", NodeItem::Type::Vector3)},
{"tangent", get_input_link("Tangent", NodeItem::Type::Vector3)},
};
};
auto edf_inputs = [&]() -> InputsType {
return {
{"emission", get_input_value("Emission Strength", NodeItem::Type::Float)},
{"emission_color", get_input_value("Emission Color", NodeItem::Type::Color3)},
};
};
NodeItem res = empty();
switch (to_type_) {
case NodeItem::Type::BSDF: {
auto in = bsdf_inputs();
NodeItem roughness = in["roughness"];
NodeItem anisotropy = in["anisotropic"];
NodeItem rotation = in["anisotropic_rotation"] * val(360.0f);
NodeItem base_color = in["base_color"];
NodeItem specular = in["specular"];
NodeItem coat = in["coat"];
NodeItem ior = in["ior"];
NodeItem normal = in["normal"];
NodeItem tangent = in["tangent"];
NodeItem coat_normal = in["coat_normal"];
NodeItem n_main_tangent = empty();
if (tangent && normal) {
NodeItem n_tangent_rotate_normalize = tangent.rotate(rotation, normal).normalize();
n_main_tangent = anisotropy.if_else(
NodeItem::CompareOp::Greater, val(0.0f), n_tangent_rotate_normalize, tangent);
}
NodeItem n_coat_roughness_vector = create_node(
"roughness_anisotropy",
NodeItem::Type::Vector2,
{{"roughness", in["coat_roughness"]}, {"anisotropy", anisotropy}});
NodeItem n_coat_bsdf = create_node("dielectric_bsdf",
NodeItem::Type::BSDF,
{{"weight", coat},
{"tint", in["coat_tint"]},
{"ior", in["coat_ior"]},
{"scatter_mode", val(std::string("R"))},
{"roughness", n_coat_roughness_vector},
{"normal", coat_normal}});
if (tangent && coat_normal) {
NodeItem n_coat_tangent_rotate_normalize =
tangent.rotate(rotation, coat_normal).normalize();
NodeItem n_coat_tangent = anisotropy.if_else(
NodeItem::CompareOp::Greater, val(0.0f), n_coat_tangent_rotate_normalize, tangent);
n_coat_bsdf.set_input("tangent", n_coat_tangent);
}
NodeItem n_thin_film_bsdf = create_node(
"thin_film_bsdf", NodeItem::Type::BSDF, {{"thickness", val(0.0f)}, {"ior", val(1.5f)}});
NodeItem n_artistic_ior = create_node(
"artistic_ior",
NodeItem::Type::Multioutput,
{{"reflectivity", base_color * val(1.0f)}, {"edge_color", base_color * specular}});
NodeItem n_ior_out = n_artistic_ior.add_output("ior", NodeItem::Type::Color3);
NodeItem n_extinction_out = n_artistic_ior.add_output("extinction", NodeItem::Type::Color3);
NodeItem n_coat_affect_roughness_multiply2 = coat * val(0.0f) * in["coat_roughness"];
NodeItem n_coat_affected_roughness = n_coat_affect_roughness_multiply2.mix(roughness,
val(1.0f));
NodeItem n_main_roughness = create_node(
"roughness_anisotropy",
NodeItem::Type::Vector2,
{{"roughness", n_coat_affected_roughness}, {"anisotropy", anisotropy}});
NodeItem n_metal_bsdf = create_node("conductor_bsdf",
NodeItem::Type::BSDF,
{{"ior", n_ior_out},
{"extinction", n_extinction_out},
{"roughness", n_main_roughness},
{"normal", normal},
{"tangent", n_main_tangent}});
NodeItem n_specular_bsdf = create_node("dielectric_bsdf",
NodeItem::Type::BSDF,
{{"weight", specular},
{"tint", in["specular_tint"]},
{"ior", ior},
{"scatter_mode", val(std::string("R"))},
{"roughness", n_main_roughness},
{"normal", normal},
{"tangent", n_main_tangent}});
NodeItem n_coat_affected_transmission_roughness = n_coat_affect_roughness_multiply2.mix(
(roughness + roughness).clamp(), val(1.0f));
NodeItem n_transmission_roughness = create_node(
"roughness_anisotropy",
NodeItem::Type::Vector2,
{{"roughness", n_coat_affected_transmission_roughness}, {"anisotropy", anisotropy}});
NodeItem n_transmission_bsdf = create_node("dielectric_bsdf",
NodeItem::Type::BSDF,
{{"tint", base_color},
{"ior", ior},
{"roughness", n_transmission_roughness},
{"normal", normal},
{"tangent", n_main_tangent}});
NodeItem n_coat_gamma = coat.clamp(0.0f, 1.0f) * val(0.0f) + val(1.0f);
NodeItem n_coat_affected_subsurface_color = base_color.max(val(0.0f)) ^ n_coat_gamma;
NodeItem n_translucent_bsdf = create_node(
"translucent_bsdf",
NodeItem::Type::BSDF,
{{"color", n_coat_affected_subsurface_color}, {"normal", normal}});
NodeItem n_subsurface_bsdf = create_node(
"subsurface_bsdf",
NodeItem::Type::BSDF,
{{"color", n_coat_affected_subsurface_color},
{"radius", in["subsurface_radius"] * in["subsurface_scale"]},
{"anisotropy", in["subsurface_anisotropy"]},
{"normal", normal}});
NodeItem n_sheen_bsdf = create_node("sheen_bsdf",
NodeItem::Type::BSDF,
{{"weight", in["sheen"]},
{"color", in["sheen_tint"]},
{"roughness", in["sheen_roughness"]},
{"normal", normal}});
NodeItem n_diffuse_bsdf = create_node("oren_nayar_diffuse_bsdf",
NodeItem::Type::BSDF,
{{"color", base_color.max(val(0.0f)) ^ n_coat_gamma},
{"roughness", roughness},
{"weight", val(1.0f)},
{"normal", normal}});
NodeItem n_subsurface_mix = in["subsurface"].mix(n_diffuse_bsdf, n_subsurface_bsdf);
NodeItem n_sheen_layer = create_node(
"layer", NodeItem::Type::BSDF, {{"top", n_sheen_bsdf}, {"base", n_subsurface_mix}});
NodeItem n_transmission_mix = in["transmission"].mix(n_sheen_layer, n_transmission_bsdf);
NodeItem n_specular_layer = create_node(
"layer", NodeItem::Type::BSDF, {{"top", n_specular_bsdf}, {"base", n_transmission_mix}});
NodeItem n_metalness_mix = in["metallic"].mix(n_specular_layer, n_metal_bsdf);
NodeItem n_thin_film_layer = create_node(
"layer", NodeItem::Type::BSDF, {{"top", n_thin_film_bsdf}, {"base", n_metalness_mix}});
NodeItem n_coat_attenuation = coat.mix(val(MaterialX::Color3(1.0f, 1.0f, 1.0f)),
in["coat_tint"]);
res = create_node("layer",
NodeItem::Type::BSDF,
{{"top", n_coat_bsdf}, {"base", n_thin_film_layer * n_coat_attenuation}});
break;
}
case NodeItem::Type::EDF: {
auto in = edf_inputs();
res = create_node(
"uniform_edf", NodeItem::Type::EDF, {{"color", in["emission_color"] * in["emission"]}});
break;
}
case NodeItem::Type::SurfaceShader: {
auto in = bsdf_inputs();
auto e_in = edf_inputs();
in.insert(e_in.begin(), e_in.end());
NodeItem roughness = in["roughness"];
NodeItem base_color = in["base_color"];
NodeItem anisotropic = in["anisotropic"];
NodeItem rotation = in["anisotropic_rotation"];
res = create_node(
"standard_surface",
NodeItem::Type::SurfaceShader,
{{"base", val(1.0f)},
{"base_color", base_color},
{"diffuse_roughness", roughness},
{"metalness", in["metallic"]},
{"specular", in["specular"]},
{"specular_color", in["specular_tint"]},
{"specular_roughness", roughness},
{"specular_IOR", in["ior"]},
{"specular_anisotropy", anisotropic},
{"specular_rotation", rotation},
{"transmission", in["transmission"]},
{"transmission_color", base_color},
{"transmission_extra_roughness", roughness},
{"subsurface", in["subsurface"]},
{"subsurface_color", base_color},
{"subsurface_radius",
(in["subsurface_radius"] * in["subsurface_scale"]).convert(NodeItem::Type::Color3)},
{"subsurface_anisotropy", in["subsurface_anisotropy"]},
{"sheen", in["sheen"]},
{"sheen_color", in["sheen_tint"]},
{"sheen_roughness", in["sheen_roughness"]},
{"coat", in["coat"]},
{"coat_color", in["coat_tint"]},
{"coat_roughness", in["coat_roughness"]},
{"coat_IOR", in["coat_ior"]},
{"coat_anisotropy", anisotropic},
{"coat_rotation", rotation},
{"coat_normal", in["coat_normal"]},
{"emission", in["emission"]},
{"emission_color", in["emission_color"]},
{"normal", in["normal"]},
{"tangent", in["tangent"]},
{"opacity", in["alpha"].convert(NodeItem::Type::Color3)}});
break;
}
case NodeItem::Type::SurfaceOpacity: {
res = get_input_value("Alpha", NodeItem::Type::Float);
break;
}
default:
BLI_assert_unreachable();
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bsdf_principled_cc
/* node type definition */
@ -335,6 +603,7 @@ void register_node_type_sh_bsdf_principled()
ntype.initfunc = file_ns::node_shader_init_principled;
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_principled;
ntype.updatefunc = file_ns::node_shader_update_principled;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -40,6 +40,29 @@ static int node_shader_gpu_bsdf_refraction(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_bsdf_refraction", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
if (to_type_ != NodeItem::Type::BSDF) {
return empty();
}
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
NodeItem roughness = get_input_value("Roughness", NodeItem::Type::Vector2);
NodeItem ior = get_input_value("IOR", NodeItem::Type::Float);
NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3);
return create_node("dielectric_bsdf",
NodeItem::Type::BSDF,
{{"normal", normal},
{"tint", color},
{"roughness", roughness},
{"ior", ior},
{"scatter_mode", val(std::string("T"))}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bsdf_refraction_cc
/* node type definition */
@ -55,6 +78,7 @@ void register_node_type_sh_bsdf_refraction()
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::MIDDLE);
ntype.initfunc = file_ns::node_shader_init_refraction;
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_refraction;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -47,6 +47,24 @@ static int node_shader_gpu_bsdf_sheen(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_bsdf_sheen", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
if (to_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);
return create_node("sheen_bsdf",
NodeItem::Type::BSDF,
{{"color", color}, {"roughness", roughness}, {"normal", normal}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bsdf_sheen_cc
/* node type definition */
@ -62,6 +80,7 @@ void register_node_type_sh_bsdf_sheen()
ntype.initfunc = file_ns::node_shader_init_sheen;
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_sheen;
ntype.draw_buttons = file_ns::node_shader_buts_sheen;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -29,6 +29,22 @@ static int node_shader_gpu_bsdf_translucent(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_bsdf_translucent", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
if (to_type_ != NodeItem::Type::BSDF) {
return empty();
}
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3);
return create_node(
"translucent_bsdf", NodeItem::Type::BSDF, {{"color", color}, {"normal", normal}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bsdf_translucent_cc
/* node type definition */
@ -42,6 +58,7 @@ void register_node_type_sh_bsdf_translucent()
ntype.declare = file_ns::node_declare;
ntype.add_ui_poll = object_shader_nodes_poll;
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_translucent;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -27,6 +27,28 @@ static int node_shader_gpu_bsdf_transparent(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_bsdf_transparent", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
switch (to_type_) {
case NodeItem::Type::BSDF: {
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
/* Returning diffuse node as BSDF component */
return create_node("oren_nayar_diffuse_bsdf", NodeItem::Type::BSDF, {{"color", color}});
}
case NodeItem::Type::SurfaceOpacity: {
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
/* Returning: 1 - <average of color components> */
return val(1.0f) - color.dotproduct(val(1.0f / 3.0f));
}
default:
break;
}
return empty();
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bsdf_transparent_cc
/* node type definition */
@ -40,6 +62,7 @@ void register_node_type_sh_bsdf_transparent()
ntype.add_ui_poll = object_shader_nodes_poll;
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_transparent;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -71,6 +71,34 @@ static int gpu_shader_bump(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_bump", in, out, dheight, GPU_constant(&invert));
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem height = get_input_link("Height", NodeItem::Type::Float);
NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3);
if (!height) {
if (!normal) {
return create_node(
"normal", NodeItem::Type::Vector3, {{"space", val(std::string("world"))}});
}
return normal;
}
NodeItem strength = get_input_value("Strength", NodeItem::Type::Float);
NodeItem distance = get_input_value("Distance", NodeItem::Type::Float);
NodeItem height_normal = create_node(
"heighttonormal", NodeItem::Type::Vector3, {{"in", height}, {"scale", strength}});
return create_node("normalmap",
NodeItem::Type::Vector3,
{{"in", height_normal},
{"scale", node_->custom1 ? distance * val(-1.0f) : distance},
{"normal", normal}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_bump_cc
/* node type definition */
@ -84,6 +112,7 @@ void register_node_type_sh_bump()
ntype.declare = file_ns::node_declare;
ntype.draw_buttons = file_ns::node_shader_buts_bump;
ntype.gpu_fn = file_ns::gpu_shader_bump;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -26,6 +26,15 @@ static int gpu_shader_camera(GPUMaterial *mat,
return GPU_stack_link(mat, node, "camera", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: This node doesn't have an implementation in MaterialX.*/
return get_output_default(socket_out_->name, NodeItem::Type::Any);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_camera_cc
void register_node_type_sh_camera()
@ -37,6 +46,7 @@ void register_node_type_sh_camera()
sh_node_type_base(&ntype, SH_NODE_CAMERA, "Camera Data", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::gpu_shader_camera;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -70,6 +70,27 @@ static void sh_node_clamp_build_multi_function(NodeMultiFunctionBuilder &builder
}
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
auto type = node_->custom1;
NodeItem value = get_input_value("Value", NodeItem::Type::Float);
NodeItem min = get_input_value("Min", NodeItem::Type::Float);
NodeItem max = get_input_value("Max", NodeItem::Type::Float);
NodeItem res = empty();
if (type == NODE_CLAMP_RANGE) {
res = min.if_else(
NodeItem::CompareOp::Less, max, value.clamp(min, max), value.clamp(max, min));
}
else {
res = value.clamp(min, max);
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_clamp_cc
void register_node_type_sh_clamp()
@ -84,6 +105,7 @@ void register_node_type_sh_clamp()
ntype.initfunc = file_ns::node_shader_init_clamp;
ntype.gpu_fn = file_ns::gpu_shader_clamp;
ntype.build_multi_function = file_ns::sh_node_clamp_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -136,6 +136,16 @@ static void sh_node_valtorgb_build_multi_function(nodes::NodeMultiFunctionBuilde
builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: Implement */
NodeItem res = empty();
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_color_ramp_cc
void register_node_type_sh_valtorgb()
@ -151,6 +161,7 @@ void register_node_type_sh_valtorgb()
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::gpu_shader_valtorgb;
ntype.build_multi_function = file_ns::sh_node_valtorgb_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -16,7 +16,7 @@
#include "node_util.hh"
namespace blender::nodes::node_shader_curves_cc {
namespace blender::nodes::node_shader_curves_cc::vec {
static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b)
{
@ -109,11 +109,20 @@ static void sh_node_curve_vec_build_multi_function(NodeMultiFunctionBuilder &bui
builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap);
}
} // namespace blender::nodes::node_shader_curves_cc
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: implement */
return get_input_value("Value", NodeItem::Type::Vector3);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_curves_cc::vec
void register_node_type_sh_curve_vec()
{
namespace file_ns = blender::nodes::node_shader_curves_cc;
namespace file_ns = blender::nodes::node_shader_curves_cc::vec;
static bNodeType ntype;
@ -124,13 +133,14 @@ void register_node_type_sh_curve_vec()
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
ntype.gpu_fn = file_ns::gpu_shader_curve_vec;
ntype.build_multi_function = file_ns::sh_node_curve_vec_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}
/* **************** CURVE RGB ******************** */
namespace blender::nodes::node_shader_curves_cc {
namespace blender::nodes::node_shader_curves_cc::rgb {
static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b)
{
@ -251,11 +261,20 @@ static void sh_node_curve_rgb_build_multi_function(NodeMultiFunctionBuilder &bui
builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap);
}
} // namespace blender::nodes::node_shader_curves_cc
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: implement */
return get_input_value("Color", NodeItem::Type::Color4);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_curves_cc::rgb
void register_node_type_sh_curve_rgb()
{
namespace file_ns = blender::nodes::node_shader_curves_cc;
namespace file_ns = blender::nodes::node_shader_curves_cc::rgb;
static bNodeType ntype;
@ -266,13 +285,14 @@ void register_node_type_sh_curve_rgb()
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
ntype.gpu_fn = file_ns::gpu_shader_curve_rgb;
ntype.build_multi_function = file_ns::sh_node_curve_rgb_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}
/* **************** CURVE FLOAT ******************** */
namespace blender::nodes::node_shader_curves_cc {
namespace blender::nodes::node_shader_curves_cc::flt {
static void sh_node_curve_float_declare(NodeDeclarationBuilder &b)
{
@ -369,11 +389,20 @@ static void sh_node_curve_float_build_multi_function(NodeMultiFunctionBuilder &b
builder.construct_and_set_matching_fn<CurveFloatFunction>(*cumap);
}
} // namespace blender::nodes::node_shader_curves_cc
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: implement */
return get_input_value("Value", NodeItem::Type::Float);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_curves_cc::flt
void register_node_type_sh_curve_float()
{
namespace file_ns = blender::nodes::node_shader_curves_cc;
namespace file_ns = blender::nodes::node_shader_curves_cc::flt;
static bNodeType ntype;
@ -384,6 +413,7 @@ void register_node_type_sh_curve_float()
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
ntype.gpu_fn = file_ns::gpu_shader_curve_float;
ntype.build_multi_function = file_ns::sh_node_curve_float_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -37,6 +37,21 @@ static int gpu_shader_displacement(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_displacement_world", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: Normal input and Space feature don't have an implementation in MaterialX.*/
NodeItem midlevel = get_input_value("Midlevel", NodeItem::Type::Float);
NodeItem height = get_input_value("Height", NodeItem::Type::Float) - midlevel;
NodeItem scale = get_input_value("Scale", NodeItem::Type::Float);
return create_node("displacement",
NodeItem::Type::DisplacementShader,
{{"displacement", height}, {"scale", scale}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_displacement_cc
/* node type definition */
@ -50,6 +65,7 @@ void register_node_type_sh_displacement()
ntype.declare = file_ns::node_declare;
ntype.initfunc = file_ns::node_shader_init_displacement;
ntype.gpu_fn = file_ns::gpu_shader_displacement;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -24,6 +24,21 @@ static int node_shader_gpu_emission(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_emission", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
if (to_type_ != NodeItem::Type::EDF) {
return empty();
}
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
NodeItem strength = get_input_value("Strength", NodeItem::Type::Float);
return create_node("uniform_edf", NodeItem::Type::EDF, {{"color", color * strength}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_emission_cc
/* node type definition */
@ -36,6 +51,7 @@ void register_node_type_sh_emission()
sh_node_type_base(&ntype, SH_NODE_EMISSION, "Emission", NODE_CLASS_SHADER);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_emission;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -26,6 +26,16 @@ static int node_shader_gpu_fresnel(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_fresnel", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: some outputs expected be implemented within the next iteration (see nodedef
* <artistic_ior>) */
return get_input_value("IOR", NodeItem::Type::Float);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_fresnel_cc
/* node type definition */
@ -38,6 +48,7 @@ void register_node_type_sh_fresnel()
sh_node_type_base(&ntype, SH_NODE_FRESNEL, "Fresnel", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_fresnel;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -23,6 +23,16 @@ static int node_shader_gpu_gamma(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_gamma", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem color = get_input_value("Color", NodeItem::Type::Color4);
NodeItem gamma = get_input_value("Gamma", NodeItem::Type::Float);
return color ^ gamma;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_gamma_cc
void register_node_type_sh_gamma()
@ -34,6 +44,7 @@ void register_node_type_sh_gamma()
sh_node_type_base(&ntype, SH_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_gamma;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -58,6 +58,30 @@ static int node_shader_gpu_geometry(GPUMaterial *mat,
return success;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: Some outputs aren't supported by MaterialX.*/
NodeItem res = empty();
std::string name = socket_out_->name;
if (name == "Position") {
res = create_node("position", NodeItem::Type::Vector3, {{"space", val(std::string("world"))}});
}
else if (name == "Normal") {
res = create_node("normal", NodeItem::Type::Vector3, {{"space", val(std::string("world"))}});
}
else if (ELEM(name, "Tangent", "True Normal")) {
res = create_node("tangent", NodeItem::Type::Vector3, {{"space", val(std::string("world"))}});
}
else {
res = get_output_default(name, NodeItem::Type::Any);
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_geometry_cc
/* node type definition */
@ -70,6 +94,7 @@ void register_node_type_sh_geometry()
sh_node_type_base(&ntype, SH_NODE_NEW_GEOMETRY, "Geometry", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_geometry;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -29,6 +29,15 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: This node doesn't have an implementation in MaterialX.*/
return get_output_default(socket_out_->name, NodeItem::Type::Any);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_hair_info_cc
/* node type definition */
@ -41,6 +50,7 @@ void register_node_type_sh_hair_info()
sh_node_type_base(&ntype, SH_NODE_HAIR_INFO, "Curves Info", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_hair_info;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -30,6 +30,27 @@ static int gpu_shader_hue_sat(GPUMaterial *mat,
return GPU_stack_link(mat, node, "hue_sat", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: implement fac */
NodeItem hue = get_input_value("Hue", NodeItem::Type::Float);
NodeItem saturation = get_input_value("Saturation", NodeItem::Type::Float);
NodeItem value = get_input_value("Value", NodeItem::Type::Float);
NodeItem fac = get_input_value("Fac", NodeItem::Type::Float);
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
/* Modifier to follow Cycles result */
hue = hue - val(0.5f);
NodeItem combine = create_node(
"combine3", NodeItem::Type::Vector3, {{"in1", hue}, {"in2", saturation}, {"in3", value}});
return create_node("hsvadjust", NodeItem::Type::Color3, {{"in", color}, {"amount", combine}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_hueSatVal_cc
void register_node_type_sh_hue_sat()
@ -42,6 +63,7 @@ void register_node_type_sh_hue_sat()
ntype.declare = file_ns::node_declare;
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::MIDDLE);
ntype.gpu_fn = file_ns::gpu_shader_hue_sat;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -26,6 +26,16 @@ static int gpu_shader_invert(GPUMaterial *mat,
return GPU_stack_link(mat, node, "invert", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem fac = get_input_value("Fac", NodeItem::Type::Float);
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
return fac.mix(color, val(1.0f) - color);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_invert_cc
void register_node_type_sh_invert()
@ -37,6 +47,7 @@ void register_node_type_sh_invert()
sh_node_type_base(&ntype, SH_NODE_INVERT, "Invert Color", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::gpu_shader_invert;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -27,6 +27,16 @@ static int node_shader_gpu_layer_weight(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_layer_weight", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: some outputs expected be implemented partially within the next iteration (see nodedef
* <artistic_ior>) */
return get_input_link("Blend", NodeItem::Type::Float);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_layer_weight_cc
/* node type definition */
@ -39,6 +49,7 @@ void register_node_type_sh_layer_weight()
sh_node_type_base(&ntype, SH_NODE_LAYER_WEIGHT, "Layer Weight", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_layer_weight;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -24,6 +24,19 @@ static int node_shader_gpu_light_falloff(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_light_falloff", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem strength = get_input_value("Strength", NodeItem::Type::Float);
NodeItem smooth = get_input_value("Smooth", NodeItem::Type::Float);
/* This node isn't supported by MaterialX. This formula was given from OSL shader code in Cycles
* node_light_falloff.osl. Considered ray_length=1.0f. */
return strength / (smooth + val(1.0f));
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_light_falloff_cc
/* node type definition */
@ -37,6 +50,7 @@ void register_node_type_sh_light_falloff()
ntype.declare = file_ns::node_declare;
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::MIDDLE);
ntype.gpu_fn = file_ns::node_shader_gpu_light_falloff;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -32,6 +32,22 @@ static int node_shader_gpu_light_path(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_light_path", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: This node isn't supported by MaterialX. Only default values returned. */
if (STREQ(socket_out_->name, "Is Camera Ray")) {
return val(1.0f);
}
if (STREQ(socket_out_->name, "Ray Length")) {
return val(1.0f);
}
NodeItem res = val(0.0f);
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_light_path_cc
/* node type definition */
@ -44,6 +60,7 @@ void register_node_type_sh_light_path()
sh_node_type_base(&ntype, SH_NODE_LIGHT_PATH, "Light Path", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_light_path;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -442,6 +442,51 @@ static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &bui
}
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: Implement steps */
const NodeMapRange *map_range = static_cast<NodeMapRange *>(node_->storage);
NodeItem::Type type;
NodeItem value = empty();
NodeItem from_min = empty();
NodeItem from_max = empty();
NodeItem to_min = empty();
NodeItem to_max = empty();
switch (map_range->data_type) {
case CD_PROP_FLOAT:
type = NodeItem::Type::Float;
value = get_input_value("Value", type);
from_min = get_input_value(1, type);
from_max = get_input_value(2, type);
to_min = get_input_value(3, type);
to_max = get_input_value(4, type);
break;
case CD_PROP_FLOAT3:
type = NodeItem::Type::Vector3;
value = get_input_value("Vector", type);
from_min = get_input_value(7, type);
from_max = get_input_value(8, type);
to_min = get_input_value(9, type);
to_max = get_input_value(10, type);
break;
default:
BLI_assert_unreachable();
}
return create_node("range",
type,
{{"in", value},
{"inlow", from_min},
{"inhigh", from_max},
{"outlow", to_min},
{"outhigh", to_max},
{"doclamp", val(bool(map_range->clamp))}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_map_range_cc
void register_node_type_sh_map_range()
@ -461,5 +506,6 @@ void register_node_type_sh_map_range()
ntype.gpu_fn = file_ns::gpu_shader_map_range;
ntype.build_multi_function = file_ns::sh_node_map_range_build_multi_function;
ntype.gather_link_search_ops = file_ns::node_map_range_gather_link_searches;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -74,6 +74,38 @@ static void node_shader_update_mapping(bNodeTree *ntree, bNode *node)
ntree, sock, ELEM(node->custom1, NODE_MAPPING_TYPE_POINT, NODE_MAPPING_TYPE_TEXTURE));
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem vector = get_input_value("Vector", NodeItem::Type::Vector3);
NodeItem scale = get_input_value("Scale", NodeItem::Type::Vector3);
NodeItem rotation = get_input_value("Rotation", NodeItem::Type::Vector3) *
val(float(180.0f / M_PI));
int type = node_->custom1;
switch (type) {
case NODE_MAPPING_TYPE_POINT: {
NodeItem location = get_input_value("Location", NodeItem::Type::Vector3);
return (vector * scale).rotate(rotation) + location;
}
case NODE_MAPPING_TYPE_TEXTURE: {
NodeItem location = get_input_value("Location", NodeItem::Type::Vector3);
return (vector - location).rotate(rotation, true) / scale;
}
case NODE_MAPPING_TYPE_VECTOR: {
return (vector * scale).rotate(rotation * val(MaterialX::Vector3(1.0f, 1.0f, -1.0f)));
}
case NODE_MAPPING_TYPE_NORMAL: {
return (vector / scale).rotate(rotation).normalize();
}
default:
BLI_assert_unreachable();
}
return empty();
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_mapping_cc
void register_node_type_sh_mapping()
@ -87,6 +119,7 @@ void register_node_type_sh_mapping()
ntype.draw_buttons = file_ns::node_shader_buts_mapping;
ntype.gpu_fn = file_ns::gpu_shader_mapping;
ntype.updatefunc = file_ns::node_shader_update_mapping;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -177,6 +177,172 @@ static void sh_node_math_build_multi_function(NodeMultiFunctionBuilder &builder)
}
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
CLG_LogRef *LOG_MATERIALX_SHADER = materialx::LOG_MATERIALX_SHADER;
/* TODO: finish some math operations */
NodeMathOperation op = NodeMathOperation(node_->custom1);
NodeItem res = empty();
/* Single operand operations */
NodeItem x = get_input_value(0, NodeItem::Type::Float);
/* TODO: Seems we have to use average if Vector or Color are added */
switch (op) {
case NODE_MATH_SINE:
res = x.sin();
break;
case NODE_MATH_COSINE:
res = x.cos();
break;
case NODE_MATH_TANGENT:
res = x.tan();
break;
case NODE_MATH_ARCSINE:
res = x.asin();
break;
case NODE_MATH_ARCCOSINE:
res = x.acos();
break;
case NODE_MATH_ARCTANGENT:
res = x.atan();
break;
case NODE_MATH_ROUND:
res = (x + val(0.5f)).floor();
break;
case NODE_MATH_ABSOLUTE:
res = x.abs();
break;
case NODE_MATH_FLOOR:
res = x.floor();
break;
case NODE_MATH_CEIL:
res = x.ceil();
break;
case NODE_MATH_FRACTION:
res = x % val(1.0f);
break;
case NODE_MATH_SQRT:
res = x.sqrt();
break;
case NODE_MATH_INV_SQRT:
res = val(1.0f) / x.sqrt();
break;
case NODE_MATH_SIGN:
res = x.sign();
break;
case NODE_MATH_EXPONENT:
res = x.exp();
break;
case NODE_MATH_RADIANS:
res = x * val(float(M_PI) / 180.0f);
break;
case NODE_MATH_DEGREES:
res = x * val(180.0f * float(M_1_PI));
break;
case NODE_MATH_SINH:
res = x.sinh();
break;
case NODE_MATH_COSH:
res = x.cosh();
break;
case NODE_MATH_TANH:
res = x.tanh();
break;
case NODE_MATH_TRUNC:
res = x.sign() * x.abs().floor();
break;
default: {
/* 2-operand operations */
NodeItem y = get_input_value(1, NodeItem::Type::Float);
switch (op) {
case NODE_MATH_ADD:
res = x + y;
break;
case NODE_MATH_SUBTRACT:
res = x - y;
break;
case NODE_MATH_MULTIPLY:
res = x * y;
break;
case NODE_MATH_DIVIDE:
res = x / y;
break;
case NODE_MATH_POWER:
res = x ^ y;
break;
case NODE_MATH_LOGARITHM:
res = x.ln() / y.ln();
break;
case NODE_MATH_MINIMUM:
res = x.min(y);
break;
case NODE_MATH_MAXIMUM:
res = x.max(y);
break;
case NODE_MATH_LESS_THAN:
res = x.if_else(NodeItem::CompareOp::Less, y, val(1.0f), val(0.0f));
break;
case NODE_MATH_GREATER_THAN:
res = x.if_else(NodeItem::CompareOp::Greater, y, val(1.0f), val(0.0f));
break;
case NODE_MATH_MODULO:
res = x % y;
break;
case NODE_MATH_ARCTAN2:
res = x.atan2(y);
break;
case NODE_MATH_SNAP:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_MATH_PINGPONG:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_MATH_FLOORED_MODULO:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
default: {
/* 3-operand operations */
NodeItem z = get_input_value(2, NodeItem::Type::Float);
switch (op) {
case NODE_MATH_WRAP:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_MATH_COMPARE:
res = z.if_else(NodeItem::CompareOp::Less, (x - y).abs(), val(1.0f), val(0.0f));
break;
case NODE_MATH_MULTIPLY_ADD:
res = x * y + z;
break;
case NODE_MATH_SMOOTH_MIN:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_MATH_SMOOTH_MAX:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
default:
BLI_assert_unreachable();
}
}
}
}
}
bool clamp_output = node_->custom2 != 0;
if (clamp_output && res) {
res = res.clamp();
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_math_cc
void register_node_type_sh_math()
@ -192,6 +358,7 @@ void register_node_type_sh_math()
ntype.updatefunc = node_math_update;
ntype.build_multi_function = file_ns::sh_node_math_build_multi_function;
ntype.gather_link_search_ops = file_ns::sh_node_math_gather_link_searches;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -543,6 +543,58 @@ static void sh_node_mix_build_multi_function(NodeMultiFunctionBuilder &builder)
}
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
const NodeShaderMix *data = (NodeShaderMix *)node_->storage;
NodeItem factor = empty();
NodeItem value1 = empty();
NodeItem value2 = empty();
switch (data->data_type) {
case SOCK_FLOAT:
factor = get_input_value(0, NodeItem::Type::Float);
value1 = get_input_value(2, NodeItem::Type::Float);
value2 = get_input_value(3, NodeItem::Type::Float);
break;
case SOCK_VECTOR:
if (data->factor_mode == NODE_MIX_MODE_UNIFORM) {
factor = get_input_value(0, NodeItem::Type::Float);
}
else {
factor = get_input_value(1, NodeItem::Type::Vector3);
}
value1 = get_input_value(4, NodeItem::Type::Vector3);
value2 = get_input_value(5, NodeItem::Type::Vector3);
break;
case SOCK_RGBA:
factor = get_input_value(0, NodeItem::Type::Float);
value1 = get_input_value(6, NodeItem::Type::Color4);
value2 = get_input_value(7, NodeItem::Type::Color4);
break;
default:
BLI_assert_unreachable();
}
if (data->clamp_factor) {
factor = factor.clamp();
}
NodeItem res = factor.mix(value1, value2);
if (data->data_type == SOCK_RGBA) {
/* TODO: Apply data->blend_type */
if (data->clamp_result) {
res = res.clamp();
}
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_sh_mix_cc
void register_node_type_sh_mix()
@ -562,5 +614,7 @@ void register_node_type_sh_mix()
ntype.draw_buttons = file_ns::sh_node_mix_layout;
ntype.labelfunc = file_ns::sh_node_mix_label;
ntype.gather_link_search_ops = file_ns::node_mix_gather_link_searches;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -23,6 +23,32 @@ static int node_shader_gpu_mix_shader(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_mix_shader", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
if (!ELEM(to_type_, NodeItem::Type::BSDF, NodeItem::Type::EDF, NodeItem::Type::SurfaceOpacity)) {
return empty();
}
NodeItem shader1 = get_input_link(1, to_type_);
NodeItem shader2 = get_input_link(2, to_type_);
if (!shader1 && !shader2) {
return empty();
}
NodeItem fac = get_input_value(0, NodeItem::Type::Float);
if (shader1 && !shader2) {
return shader1 * (val(1.0f) - fac);
}
if (!shader1 && shader2) {
return shader2 * fac;
}
return fac.mix(shader1, shader2);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_mix_shader_cc
/* node type definition */
@ -35,6 +61,7 @@ void register_node_type_sh_mix_shader()
sh_node_type_base(&ntype, SH_NODE_MIX_SHADER, "Mix Shader", NODE_CLASS_SHADER);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_mix_shader;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -35,6 +35,20 @@ static int gpu_shader_normal(GPUMaterial *mat,
return GPU_stack_link(mat, node, "normal_new_shading", in, out, vec);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem res = get_output_default("Normal", NodeItem::Type::Vector3);
if (STREQ(socket_out_->name, "Dot")) {
return res.dotproduct(get_input_value("Normal", NodeItem::Type::Vector3));
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_normal_cc
void register_node_type_sh_normal()
@ -46,6 +60,7 @@ void register_node_type_sh_normal()
sh_node_type_base(&ntype, SH_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::gpu_shader_normal;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -122,6 +122,38 @@ static int gpu_shader_normal_map(GPUMaterial *mat,
return true;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeShaderNormalMap *normal_map_node = static_cast<NodeShaderNormalMap *>(node_->storage);
NodeItem color = get_input_value("Color", NodeItem::Type::Vector3);
NodeItem strength = get_input_value("Strength", NodeItem::Type::Float);
std::string space;
switch (normal_map_node->space) {
case SHD_SPACE_TANGENT:
space = "tangent";
break;
case SHD_SPACE_OBJECT:
case SHD_SPACE_BLENDER_OBJECT:
space = "object";
break;
case SHD_SPACE_WORLD:
case SHD_SPACE_BLENDER_WORLD:
/* World isn't supported, tangent space will be used */
space = "tangent";
break;
default:
BLI_assert_unreachable();
}
return create_node("normalmap",
NodeItem::Type::Vector3,
{{"in", color}, {"scale", strength}, {"space", val(space)}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_normal_map_cc
/* node type definition */
@ -139,6 +171,7 @@ void register_node_type_sh_normal_map()
node_type_storage(
&ntype, "NodeShaderNormalMap", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::gpu_shader_normal_map;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -30,6 +30,33 @@ static int node_shader_gpu_object_info(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_object_info", in, out, GPU_constant(&index));
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: Some outputs isn't supported by MaterialX.*/
NodeItem res = empty();
std::string name = socket_out_->name;
if (name == "Location") {
res = create_node("position", NodeItem::Type::Vector3, {{"space", val(std::string("world"))}});
}
/* TODO: This node doesn't have an implementation in MaterialX.
* It's added in MaterialX 1.38.8. Uncomment this code after switching to 1.38.8.
* if (name=="Random") {
* res = create_node("randomfloat", NodeItem::Type::Float);
* res.set_input("in", val(0.0));
* res.set_input("min", val(0.0));
* res.set_input("max", val(1.0));
* res.set_input("seed", val(0));
*}*/
else {
res = get_output_default(name, NodeItem::Type::Any);
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_object_info_cc
void register_node_type_sh_object_info()
@ -41,6 +68,7 @@ void register_node_type_sh_object_info()
sh_node_type_base(&ntype, SH_NODE_OBJECT_INFO, "Object Info", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_object_info;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -43,6 +43,25 @@ static int node_shader_gpu_output_material(GPUMaterial *mat,
return true;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem surface = get_input_link("Surface", NodeItem::Type::SurfaceShader);
if (!surface) {
NodeItem bsdf = get_input_link("Surface", NodeItem::Type::BSDF);
NodeItem edf = get_input_link("Surface", NodeItem::Type::EDF);
if (bsdf || edf) {
NodeItem opacity = get_input_link("Surface", NodeItem::Type::SurfaceOpacity);
surface = create_node("surface",
NodeItem::Type::SurfaceShader,
{{"bsdf", bsdf}, {"edf", edf}, {"opacity", opacity}});
}
}
return create_node("surfacematerial", NodeItem::Type::Material, {{"surfaceshader", surface}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_output_material_cc
/* node type definition */
@ -56,6 +75,7 @@ void register_node_type_sh_output_material()
ntype.declare = file_ns::node_declare;
ntype.add_ui_poll = object_shader_nodes_poll;
ntype.gpu_fn = file_ns::node_shader_gpu_output_material;
ntype.materialx_fn = file_ns::node_shader_materialx;
ntype.no_muting = true;

View File

@ -34,6 +34,15 @@ static int gpu_shader_particle_info(GPUMaterial *mat,
return GPU_stack_link(mat, node, "particle_info", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: This node isn't supported by MaterialX.*/
return get_output_default(socket_out_->name, NodeItem::Type::Any);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_particle_info_cc
/* node type definition */
@ -46,6 +55,7 @@ void register_node_type_sh_particle_info()
sh_node_type_base(&ntype, SH_NODE_PARTICLE_INFO, "Particle Info", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::gpu_shader_particle_info;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -22,6 +22,15 @@ static int node_shader_gpu_point_info(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_point_info", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: This node isn't supported by MaterialX.*/
return get_output_default(socket_out_->name, NodeItem::Type::Any);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_point_info_cc
/* node type definition */
@ -34,6 +43,7 @@ void register_node_type_sh_point_info()
sh_node_type_base(&ntype, SH_NODE_POINT_INFO, "Point Info", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::node_shader_gpu_point_info;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -26,6 +26,15 @@ static int gpu_shader_rgb(GPUMaterial *mat,
return GPU_link(mat, "set_rgba", GPU_uniform(value), &out->link);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem color = get_output_default("Color", NodeItem::Type::Color4);
return create_node("constant", NodeItem::Type::Color4, {{"value", color}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_rgb_cc
void register_node_type_sh_rgb()
@ -37,6 +46,7 @@ void register_node_type_sh_rgb()
sh_node_type_base(&ntype, SH_NODE_RGB, "RGB", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::gpu_shader_rgb;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -27,6 +27,15 @@ static int gpu_shader_rgbtobw(GPUMaterial *mat,
return GPU_stack_link(mat, node, "rgbtobw", in, out);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem color = get_input_value("Color", NodeItem::Type::Color4);
return create_node("luminance", NodeItem::Type::Color4, {{"in", color}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_rgb_to_bw_cc
void register_node_type_sh_rgbtobw()
@ -38,6 +47,7 @@ void register_node_type_sh_rgbtobw()
sh_node_type_base(&ntype, SH_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::sh_node_rgbtobw_declare;
ntype.gpu_fn = file_ns::gpu_shader_rgbtobw;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -68,6 +68,32 @@ static int gpu_shader_sepcolor(GPUMaterial *mat,
return 0;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
int mode = static_cast<NodeCombSepColor *>(node_->storage)->mode;
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
NodeItem convert = empty();
switch (mode) {
case NODE_COMBSEP_COLOR_RGB:
convert = color;
break;
case NODE_COMBSEP_COLOR_HSV:
case NODE_COMBSEP_COLOR_HSL:
/* NOTE: HSL is unsupported color model, using HSV instead */
convert = create_node("rgbtohsv", NodeItem::Type::Color3, {{"in", color}});
break;
default:
BLI_assert_unreachable();
}
int index = STREQ(socket_out_->name, "Red") ? 0 : STREQ(socket_out_->name, "Green") ? 1 : 2;
return convert[index];
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_separate_color_cc
void register_node_type_sh_sepcolor()
@ -83,6 +109,7 @@ void register_node_type_sh_sepcolor()
node_type_storage(
&ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::gpu_shader_sepcolor;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}
@ -136,6 +163,38 @@ static int gpu_shader_combcolor(GPUMaterial *mat,
return 0;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
int mode = static_cast<NodeCombSepColor *>(node_->storage)->mode;
NodeItem red = get_input_value("Red", NodeItem::Type::Float);
NodeItem green = get_input_value("Green", NodeItem::Type::Float);
NodeItem blue = get_input_value("Blue", NodeItem::Type::Float);
NodeItem combine = create_node("combine3", NodeItem::Type::Color3);
combine.set_input("in1", red);
combine.set_input("in2", green);
combine.set_input("in3", blue);
NodeItem res = empty();
switch (mode) {
case NODE_COMBSEP_COLOR_RGB:
res = combine;
break;
case NODE_COMBSEP_COLOR_HSV:
case NODE_COMBSEP_COLOR_HSL:
/* NOTE: HSL is unsupported color model, using HSV instead */
res = create_node("hsvtorgb", NodeItem::Type::Color3);
res.set_input("in", combine);
break;
default:
BLI_assert_unreachable();
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_combine_color_cc
void register_node_type_sh_combcolor()
@ -151,6 +210,7 @@ void register_node_type_sh_combcolor()
node_type_storage(
&ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::gpu_shader_combcolor;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -12,7 +12,7 @@
#include "NOD_multi_function.hh"
namespace blender::nodes::node_shader_sepcomb_xyz_cc {
namespace blender::nodes::node_shader_sepcomb_xyz_cc::sep {
static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b)
{
@ -90,11 +90,21 @@ static void sh_node_sepxyz_build_multi_function(NodeMultiFunctionBuilder &builde
builder.set_matching_fn(separate_fn);
}
} // namespace blender::nodes::node_shader_sepcomb_xyz_cc
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem vector = get_input_value("Vector", NodeItem::Type::Vector3);
int index = STREQ(socket_out_->name, "X") ? 0 : STREQ(socket_out_->name, "Y") ? 1 : 2;
return vector[index];
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_sepcomb_xyz_cc::sep
void register_node_type_sh_sepxyz()
{
namespace file_ns = blender::nodes::node_shader_sepcomb_xyz_cc;
namespace file_ns = blender::nodes::node_shader_sepcomb_xyz_cc::sep;
static bNodeType ntype;
@ -102,11 +112,12 @@ void register_node_type_sh_sepxyz()
ntype.declare = file_ns::sh_node_sepxyz_declare;
ntype.gpu_fn = file_ns::gpu_shader_sepxyz;
ntype.build_multi_function = file_ns::sh_node_sepxyz_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}
namespace blender::nodes::node_shader_sepcomb_xyz_cc {
namespace blender::nodes::node_shader_sepcomb_xyz_cc::comb {
static void sh_node_combxyz_declare(NodeDeclarationBuilder &b)
{
@ -135,11 +146,23 @@ static void sh_node_combxyz_build_multi_function(NodeMultiFunctionBuilder &build
builder.set_matching_fn(fn);
}
} // namespace blender::nodes::node_shader_sepcomb_xyz_cc
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem x = get_input_value("X", NodeItem::Type::Float);
NodeItem y = get_input_value("Y", NodeItem::Type::Float);
NodeItem z = get_input_value("Z", NodeItem::Type::Float);
return create_node("combine3", NodeItem::Type::Vector3, {{"in1", x}, {"in2", y}, {"in3", z}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_sepcomb_xyz_cc::comb
void register_node_type_sh_combxyz()
{
namespace file_ns = blender::nodes::node_shader_sepcomb_xyz_cc;
namespace file_ns = blender::nodes::node_shader_sepcomb_xyz_cc::comb;
static bNodeType ntype;
@ -147,6 +170,7 @@ void register_node_type_sh_combxyz()
ntype.declare = file_ns::sh_node_combxyz_declare;
ntype.gpu_fn = file_ns::gpu_shader_combxyz;
ntype.build_multi_function = file_ns::sh_node_combxyz_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -77,6 +77,31 @@ static void node_shader_update_subsurface_scattering(bNodeTree *ntree, bNode *no
}
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: IOR and Subsurface Method isn't supported for this node in MaterialX. */
if (to_type_ != NodeItem::Type::BSDF) {
return empty();
}
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
NodeItem scale = get_input_value("Scale", NodeItem::Type::Float);
NodeItem radius = get_input_value("Radius", NodeItem::Type::Vector3);
NodeItem anisotropy = get_input_value("Anisotropy", NodeItem::Type::Float);
NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3);
return create_node("subsurface_bsdf",
NodeItem::Type::BSDF,
{{"weight", val(1.0f)},
{"color", color},
{"radius", radius * scale},
{"anisotropy", anisotropy},
{"normal", normal}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_subsurface_scattering_cc
/* node type definition */
@ -95,6 +120,7 @@ void register_node_type_sh_subsurface_scattering()
ntype.initfunc = file_ns::node_shader_init_subsurface_scattering;
ntype.gpu_fn = file_ns::node_shader_gpu_subsurface_scattering;
ntype.updatefunc = file_ns::node_shader_update_subsurface_scattering;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -86,6 +86,15 @@ static int node_shader_gpu_tangent(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_tangent", in, out, orco);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: implement other features */
return create_node("tangent", NodeItem::Type::Vector3, {{"space", val(std::string("world"))}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_tangent_cc
/* node type definition */
@ -103,6 +112,7 @@ void register_node_type_sh_tangent()
ntype.gpu_fn = file_ns::node_shader_gpu_tangent;
node_type_storage(
&ntype, "NodeShaderTangent", node_free_standard_storage, node_copy_standard_storage);
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -102,6 +102,28 @@ static void sh_node_tex_checker_build_multi_function(NodeMultiFunctionBuilder &b
builder.set_matching_fn(fn);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector2);
if (!vector) {
vector = texcoord_node();
}
NodeItem value1 = val(1.0f);
NodeItem value2 = val(0.0f);
if (STREQ(socket_out_->name, "Color")) {
value1 = get_input_value("Color1", NodeItem::Type::Color4);
value2 = get_input_value("Color2", NodeItem::Type::Color4);
}
NodeItem scale = get_input_value("Scale", NodeItem::Type::Float);
vector = (vector * scale) % val(2.0f);
return (vector[0].floor() + vector[1].floor())
.if_else(NodeItem::CompareOp::Eq, val(1.0f), value1, value2);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_tex_checker_cc
void register_node_type_sh_tex_checker()
@ -117,6 +139,7 @@ void register_node_type_sh_tex_checker()
&ntype, "NodeTexChecker", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::node_shader_gpu_tex_checker;
ntype.build_multi_function = file_ns::sh_node_tex_checker_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -71,6 +71,31 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat,
return 1;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: Some outputs aren't supported by MaterialX.*/
NodeItem res = empty();
std::string name = socket_out_->name;
if (ELEM(name, "Generated", "UV")) {
res = texcoord_node();
}
else if (name == "Normal") {
res = create_node("normal", NodeItem::Type::Vector3, {{"space", val(std::string("world"))}});
}
else if (name == "Object") {
res = create_node("position", NodeItem::Type::Vector3, {{"space", val(std::string("world"))}});
}
else {
res = get_output_default(name, NodeItem::Type::Any);
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_tex_coord_cc
/* node type definition */
@ -84,6 +109,7 @@ void register_node_type_sh_tex_coord()
ntype.declare = file_ns::node_declare;
ntype.draw_buttons = file_ns::node_shader_buts_tex_coord;
ntype.gpu_fn = file_ns::node_shader_gpu_tex_coord;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -11,6 +11,8 @@
#include "IMB_colormanagement.h"
#include "DEG_depsgraph_query.hh"
namespace blender::nodes::node_shader_tex_environment_cc {
static void node_declare(NodeDeclarationBuilder &b)
@ -127,6 +129,57 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
return true;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem res = val(MaterialX::Color4(1.0f, 0.0f, 1.0f, 1.0f));
Image *image = (Image *)node_->id;
if (!image) {
return res;
}
NodeTexEnvironment *tex_env = static_cast<NodeTexEnvironment *>(node_->storage);
std::string image_path = image->id.name;
if (export_image_fn_) {
Scene *scene = DEG_get_input_scene(depsgraph_);
Main *bmain = DEG_get_bmain(depsgraph_);
image_path = export_image_fn_(bmain, scene, image, &tex_env->iuser);
}
NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector2);
if (!vector) {
vector = texcoord_node();
}
/* TODO: texcoords should be translated to spherical coordinates */
std::string filtertype;
switch (tex_env->interpolation) {
case SHD_INTERP_LINEAR:
filtertype = "linear";
break;
case SHD_INTERP_CLOSEST:
filtertype = "closest";
break;
case SHD_INTERP_CUBIC:
case SHD_INTERP_SMART:
filtertype = "cubic";
break;
default:
BLI_assert_unreachable();
}
res = create_node("image", NodeItem::Type::Color4);
res.set_input("file", image_path, NodeItem::Type::Filename);
res.set_input("texcoord", vector);
res.set_input("filtertype", val(filtertype));
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_tex_environment_cc
/* node type definition */
@ -144,6 +197,7 @@ void register_node_type_sh_tex_environment()
ntype.gpu_fn = file_ns::node_shader_gpu_tex_environment;
ntype.labelfunc = node_image_label;
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::LARGE);
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -147,6 +147,50 @@ static void sh_node_gradient_tex_build_multi_function(NodeMultiFunctionBuilder &
builder.construct_and_set_matching_fn<GradientFunction>(tex->gradient_type);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeTexGradient *tex = (NodeTexGradient *)node_->storage;
const int gradient_type = tex->gradient_type;
NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector2);
if (!vector) {
vector = texcoord_node();
}
NodeItem res = empty();
switch (gradient_type) {
case SHD_BLEND_LINEAR:
res = vector[0];
break;
case SHD_BLEND_QUADRATIC:
res = vector[0];
res = res * res;
break;
case SHD_BLEND_EASING:
res = vector[0].clamp();
res = res * res * (val(3.0f) - val(2.0f) * res);
break;
case SHD_BLEND_DIAGONAL:
res = (vector[0] + vector[1]) * val(0.5f);
break;
case SHD_BLEND_RADIAL:
res = vector[1].atan2(vector[0]) / (val(float(M_PI * 2.0f))) + val(0.5f);
break;
case SHD_BLEND_QUADRATIC_SPHERE:
res = (val(1.0f) - vector.dotproduct(vector).sqrt()).max(val(0.0f));
res = res * res;
break;
case SHD_BLEND_SPHERICAL:
res = (val(1.0f) - vector.dotproduct(vector).sqrt()).max(val(0.0f));
break;
default:
BLI_assert_unreachable();
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_tex_gradient_cc
void register_node_type_sh_tex_gradient()
@ -163,6 +207,7 @@ void register_node_type_sh_tex_gradient()
&ntype, "NodeTexGradient", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::node_shader_gpu_tex_gradient;
ntype.build_multi_function = file_ns::sh_node_gradient_tex_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -11,6 +11,8 @@
#include "IMB_colormanagement.h"
#include "DEG_depsgraph_query.hh"
namespace blender::nodes::node_shader_tex_image_cc {
static void sh_node_tex_image_declare(NodeDeclarationBuilder &b)
@ -172,6 +174,87 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
return true;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* Getting node name for Color output. This name will be used for <image> node. */
std::string image_node_name = node_name();
image_node_name = image_node_name.substr(0, image_node_name.rfind('_')) + "_Color";
NodeItem res = empty();
res.node = graph_->getNode(image_node_name);
if (!res.node) {
res = val(MaterialX::Color4(1.0f, 0.0f, 1.0f, 1.0f));
Image *image = (Image *)node_->id;
if (image) {
NodeTexImage *tex_image = static_cast<NodeTexImage *>(node_->storage);
std::string image_path = image->id.name;
if (export_image_fn_) {
Scene *scene = DEG_get_input_scene(depsgraph_);
Main *bmain = DEG_get_bmain(depsgraph_);
image_path = export_image_fn_(bmain, scene, image, &tex_image->iuser);
}
NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector2);
if (!vector) {
vector = texcoord_node();
}
/* TODO: add math to vector depending of tex_image->projection */
std::string filtertype;
switch (tex_image->interpolation) {
case SHD_INTERP_LINEAR:
filtertype = "linear";
break;
case SHD_INTERP_CLOSEST:
filtertype = "closest";
break;
case SHD_INTERP_CUBIC:
case SHD_INTERP_SMART:
filtertype = "cubic";
break;
default:
BLI_assert_unreachable();
}
std::string addressmode;
switch (tex_image->extension) {
case SHD_IMAGE_EXTENSION_REPEAT:
addressmode = "periodic";
break;
case SHD_IMAGE_EXTENSION_EXTEND:
addressmode = "clamp";
break;
case SHD_IMAGE_EXTENSION_CLIP:
addressmode = "constant";
break;
case SHD_IMAGE_EXTENSION_MIRROR:
addressmode = "mirror";
break;
default:
BLI_assert_unreachable();
}
res = create_node("image",
NodeItem::Type::Color4,
{{"texcoord", vector},
{"filtertype", val(filtertype)},
{"uaddressmode", val(addressmode)},
{"vaddressmode", val(addressmode)}});
res.set_input("file", image_path, NodeItem::Type::Filename);
res.node->setName(image_node_name);
}
}
if (STREQ(socket_out_->name, "Alpha")) {
res = res[3];
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_tex_image_cc
void register_node_type_sh_tex_image()
@ -188,6 +271,7 @@ void register_node_type_sh_tex_image()
ntype.gpu_fn = file_ns::node_shader_gpu_tex_image;
ntype.labelfunc = node_image_label;
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::LARGE);
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -257,6 +257,26 @@ static void sh_node_noise_build_multi_function(NodeMultiFunctionBuilder &builder
builder.construct_and_set_matching_fn<NoiseFunction>(storage.dimensions, storage.normalize);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: Some inputs aren't supported by MaterialX.*/
NodeItem scale = get_input_value("Scale", NodeItem::Type::Float);
NodeItem detail = get_input_default("Detail", NodeItem::Type::Float);
NodeItem lacunarity = get_input_value("Lacunarity", NodeItem::Type::Float);
NodeItem position = create_node("position", NodeItem::Type::Vector3);
position = position * scale;
return create_node("fractal3d",
NodeItem::Type::Color3,
{{"position", position},
{"octaves", val(int(detail.value->asA<float>()))},
{"lacunarity", lacunarity}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_tex_noise_cc
void register_node_type_sh_tex_noise()
@ -274,6 +294,7 @@ void register_node_type_sh_tex_noise()
ntype.gpu_fn = file_ns::node_shader_gpu_tex_noise;
ntype.updatefunc = file_ns::node_shader_update_tex_noise;
ntype.build_multi_function = file_ns::sh_node_noise_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -219,6 +219,96 @@ static void sh_node_wave_tex_build_multi_function(NodeMultiFunctionBuilder &buil
tex->wave_type, tex->bands_direction, tex->rings_direction, tex->wave_profile);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeTexWave *tex = (NodeTexWave *)node_->storage;
NodeItem scale = get_input_value("Scale", NodeItem::Type::Float);
NodeItem distortion = get_input_value("Distortion", NodeItem::Type::Float);
NodeItem detail = get_input_default("Detail", NodeItem::Type::Float);
NodeItem detail_scale = get_input_value("Detail Scale", NodeItem::Type::Float);
NodeItem detail_rough = get_input_value("Detail Roughness", NodeItem::Type::Float);
NodeItem phase_offset = get_input_value("Phase Offset", NodeItem::Type::Float);
NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector3);
if (!vector) {
vector = texcoord_node(NodeItem::Type::Vector3);
}
/* adjusment to get result as Cycles */
distortion = distortion * val(10.0f);
detail_scale = detail_scale * val(10.0f);
NodeItem pos = vector * scale;
NodeItem fractal = create_node("fractal3d",
NodeItem::Type::Float,
{{"position", pos},
{"octaves", val(int(detail.value->asA<float>()))},
{"lacunarity", val(2.0f)}});
NodeItem value = val(0.0f);
switch (tex->wave_type) {
case SHD_WAVE_BANDS:
switch (tex->bands_direction) {
case SHD_WAVE_BANDS_DIRECTION_X:
value = pos[0] * val(20.0f);
break;
case SHD_WAVE_BANDS_DIRECTION_Y:
value = pos[1] * val(20.0f);
break;
case SHD_WAVE_BANDS_DIRECTION_Z:
value = pos[2] * val(20.0f);
break;
case SHD_WAVE_BANDS_DIRECTION_DIAGONAL:
value = (pos[0] + pos[1] + pos[2]) * val(10.0f);
break;
default:
BLI_assert_unreachable();
}
break;
case SHD_WAVE_RINGS:
NodeItem rpos = pos;
switch (tex->rings_direction) {
case SHD_WAVE_RINGS_DIRECTION_X:
rpos = pos * val(MaterialX::Vector3(0.0f, 1.0f, 1.0f));
break;
case SHD_WAVE_RINGS_DIRECTION_Y:
rpos = pos * val(MaterialX::Vector3(1.0f, 0.0f, 1.0f));
break;
case SHD_WAVE_RINGS_DIRECTION_Z:
rpos = pos * val(MaterialX::Vector3(1.0f, 1.0f, 0.0f));
break;
case SHD_WAVE_RINGS_DIRECTION_SPHERICAL:
/* Ignore. */
break;
default:
BLI_assert_unreachable();
}
value = rpos.length() * val(20.0f);
break;
}
value = value + phase_offset + distortion * detail_scale * fractal;
NodeItem res = empty();
switch (tex->wave_profile) {
case SHD_WAVE_PROFILE_SIN:
res = val(0.5f) + val(0.5f) * (value - val(float(M_PI_2))).sin();
break;
case SHD_WAVE_PROFILE_SAW:
value = value / val(float(M_PI * 2.0f));
res = value - value.floor();
break;
case SHD_WAVE_PROFILE_TRI:
value = value / val(float(M_PI * 2.0f));
res = (value - (value + val(0.5f)).floor()).abs() * val(2.0f);
break;
default:
BLI_assert_unreachable();
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_tex_wave_cc
void register_node_type_sh_tex_wave()
@ -235,6 +325,7 @@ void register_node_type_sh_tex_wave()
node_type_storage(&ntype, "NodeTexWave", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::node_shader_gpu_tex_wave;
ntype.build_multi_function = file_ns::sh_node_wave_tex_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -67,6 +67,18 @@ static int node_shader_gpu_uvmap(GPUMaterial *mat,
return 1;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NODE: "From Instances" not implemented
* UV selection not implemented
*/
NodeItem res = texcoord_node();
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_uvmap_cc
/* node type definition */
@ -84,6 +96,7 @@ void register_node_type_sh_uvmap()
node_type_storage(
&ntype, "NodeShaderUVMap", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::node_shader_gpu_uvmap;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -38,6 +38,15 @@ static void sh_node_value_build_multi_function(NodeMultiFunctionBuilder &builder
builder.construct_and_set_matching_fn<mf::CustomMF_Constant<float>>(value->value);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem value = get_output_default("Value", NodeItem::Type::Float);
return create_node("constant", NodeItem::Type::Float, {{"value", value}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_value_cc
void register_node_type_sh_value()
@ -50,6 +59,7 @@ void register_node_type_sh_value()
ntype.declare = file_ns::sh_node_value_declare;
ntype.gpu_fn = file_ns::gpu_shader_value;
ntype.build_multi_function = file_ns::sh_node_value_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -41,6 +41,21 @@ static int gpu_shader_vector_displacement(GPUMaterial *mat,
}
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: Midlevel input and Space feature don't have an implementation in MaterialX.*/
// NodeItem midlevel = get_input_value("midlevel", NodeItem::Type::Float);
NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector3);
NodeItem scale = get_input_value("Scale", NodeItem::Type::Float);
return create_node("displacement",
NodeItem::Type::DisplacementShader,
{{"displacement", vector}, {"scale", scale}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_vector_displacement_cc
/* node type definition */
@ -55,6 +70,7 @@ void register_node_type_sh_vector_displacement()
ntype.declare = file_ns::node_declare;
ntype.initfunc = file_ns::node_shader_init_vector_displacement;
ntype.gpu_fn = file_ns::gpu_shader_vector_displacement;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -316,6 +316,123 @@ static void sh_node_vector_math_build_multi_function(NodeMultiFunctionBuilder &b
builder.set_matching_fn(fn);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
CLG_LogRef *LOG_MATERIALX_SHADER = materialx::LOG_MATERIALX_SHADER;
/* TODO: finish some math operations */
auto op = node_->custom1;
NodeItem res = empty();
/* Single operand operations */
NodeItem x = get_input_value(0, NodeItem::Type::Vector3);
switch (op) {
case NODE_VECTOR_MATH_SINE:
res = x.sin();
break;
case NODE_VECTOR_MATH_COSINE:
res = x.cos();
break;
case NODE_VECTOR_MATH_TANGENT:
res = x.tan();
break;
case NODE_VECTOR_MATH_ABSOLUTE:
res = x.abs();
break;
case NODE_VECTOR_MATH_FLOOR:
res = x.floor();
break;
case NODE_VECTOR_MATH_CEIL:
res = x.ceil();
break;
case NODE_VECTOR_MATH_FRACTION:
res = x % val(1.0f);
break;
case NODE_VECTOR_MATH_LENGTH:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_VECTOR_MATH_NORMALIZE:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
default: {
/* 2-operand operations */
NodeItem y = get_input_value(1, NodeItem::Type::Vector3);
switch (op) {
case NODE_VECTOR_MATH_ADD:
res = x + y;
break;
case NODE_VECTOR_MATH_SUBTRACT:
res = x - y;
break;
case NODE_VECTOR_MATH_MULTIPLY:
res = x * y;
break;
case NODE_VECTOR_MATH_DIVIDE:
res = x / y;
break;
case NODE_VECTOR_MATH_MINIMUM:
res = x.min(y);
break;
case NODE_VECTOR_MATH_MAXIMUM:
res = x.max(y);
break;
case NODE_VECTOR_MATH_MODULO:
res = x % y;
break;
case NODE_VECTOR_MATH_SNAP:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_VECTOR_MATH_CROSS_PRODUCT:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_VECTOR_MATH_PROJECT:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_VECTOR_MATH_REFLECT:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_VECTOR_MATH_DISTANCE:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_VECTOR_MATH_SCALE:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
default: {
/* 3-operand operations */
NodeItem z = get_input_value(2, NodeItem::Type::Vector3);
switch (op) {
case NODE_VECTOR_MATH_MULTIPLY_ADD:
res = x * y + z;
break;
case NODE_VECTOR_MATH_REFRACT:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_VECTOR_MATH_FACEFORWARD:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
case NODE_VECTOR_MATH_WRAP:
CLOG_WARN(LOG_MATERIALX_SHADER, "Unimplemented math operation %d", op);
break;
default:
BLI_assert_unreachable();
}
}
}
}
}
return res;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_vector_math_cc
void register_node_type_sh_vect_math()
@ -332,6 +449,7 @@ void register_node_type_sh_vect_math()
ntype.updatefunc = file_ns::node_shader_update_vector_math;
ntype.build_multi_function = file_ns::sh_node_vector_math_build_multi_function;
ntype.gather_link_search_ops = file_ns::sh_node_vector_math_gather_link_searches;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -212,6 +212,49 @@ static void node_shader_update_vector_rotate(bNodeTree *ntree, bNode *node)
ntree, sock_angle, !ELEM(node->custom1, NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
int mode = node_->custom1;
bool invert = node_->custom2;
NodeItem vector = get_input_value("Vector", NodeItem::Type::Vector3);
NodeItem center = get_input_value("Center", NodeItem::Type::Vector3) *
val(MaterialX::Vector3(1.0f, 1.0f, -1.0f));
vector = vector - center;
if (mode == NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
NodeItem rotation = get_input_value("Rotation", NodeItem::Type::Vector3) *
val(MaterialX::Vector3(1.0f, 1.0f, -1.0f) * 180.0f / M_PI);
return vector.rotate(invert ? -rotation : rotation, invert) + center;
}
NodeItem angle = get_input_value("Angle", NodeItem::Type::Float) * val(float(180.0f / M_PI));
NodeItem axis = empty();
switch (mode) {
case NODE_VECTOR_ROTATE_TYPE_AXIS:
axis = get_input_value("Axis", NodeItem::Type::Vector3) *
val(MaterialX::Vector3(1.0f, 1.0f, -1.0f));
break;
case NODE_VECTOR_ROTATE_TYPE_AXIS_X:
axis = val(MaterialX::Vector3(1.0f, 0.0f, 0.0f));
break;
case NODE_VECTOR_ROTATE_TYPE_AXIS_Y:
axis = val(MaterialX::Vector3(0.0f, 1.0f, 0.0f));
break;
case NODE_VECTOR_ROTATE_TYPE_AXIS_Z:
axis = val(MaterialX::Vector3(0.0f, 0.0f, -1.0f));
break;
default:
BLI_assert_unreachable();
}
return vector.rotate(invert ? -angle : angle, axis) + center;
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_vector_rotate_cc
void register_node_type_sh_vector_rotate()
@ -226,6 +269,7 @@ void register_node_type_sh_vector_rotate()
ntype.gpu_fn = file_ns::gpu_shader_vector_rotate;
ntype.updatefunc = file_ns::node_shader_update_vector_rotate;
ntype.build_multi_function = file_ns::sh_node_vector_rotate_build_multi_function;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -137,6 +137,68 @@ static int gpu_shader_vect_transform(GPUMaterial *mat,
return true;
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
NodeItem res = empty();
NodeShaderVectTransform *nodeprop = (NodeShaderVectTransform *)node_->storage;
std::string fromspace;
std::string tospace;
std::string category;
NodeItem vector = get_input_value("Vector", NodeItem::Type::Vector3);
switch (nodeprop->convert_from) {
case SHD_VECT_TRANSFORM_SPACE_WORLD:
fromspace = "world";
break;
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
fromspace = "object";
break;
default:
/* NOTE: SHD_VECT_TRANSFORM_SPACE_CAMERA don't have an implementation in MaterialX.*/
BLI_assert_unreachable();
return vector;
}
switch (nodeprop->convert_to) {
case SHD_VECT_TRANSFORM_SPACE_WORLD:
tospace = "world";
break;
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
tospace = "object";
break;
default:
/* NOTE: SHD_VECT_TRANSFORM_SPACE_CAMERA don't have an implementation in MaterialX.*/
BLI_assert_unreachable();
return vector;
}
if (fromspace == tospace) {
return vector;
}
switch (nodeprop->type) {
case SHD_VECT_TRANSFORM_TYPE_POINT:
category = "transformpoint";
break;
case SHD_VECT_TRANSFORM_TYPE_NORMAL:
category = "transformnormal";
break;
case SHD_VECT_TRANSFORM_TYPE_VECTOR:
category = "transformvector";
break;
default:
BLI_assert_unreachable();
return vector;
}
return create_node(category,
NodeItem::Type::Vector3,
{{"in", vector}, {"fromspace", val(fromspace)}, {"tospace", val(tospace)}});
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_vector_transform_cc
void register_node_type_sh_vect_transform()
@ -152,6 +214,7 @@ void register_node_type_sh_vect_transform()
node_type_storage(
&ntype, "NodeShaderVectTransform", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::gpu_shader_vect_transform;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -67,6 +67,16 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink);
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* TODO: some output expected be implemented within the next iteration (see nodedef
* <geomcolor>)*/
return get_output_default(socket_out_->name, NodeItem::Type::Any);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_vertex_color_cc
void register_node_type_sh_vertex_color()
@ -82,6 +92,7 @@ void register_node_type_sh_vertex_color()
node_type_storage(
&ntype, "NodeShaderVertexColor", node_free_standard_storage, node_copy_standard_storage);
ntype.gpu_fn = file_ns::node_shader_gpu_vertex_color;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -40,6 +40,15 @@ static int node_shader_gpu_wireframe(GPUMaterial *mat,
}
}
NODE_SHADER_MATERIALX_BEGIN
#ifdef WITH_MATERIALX
{
/* NOTE: This node isn't supported by MaterialX.*/
return get_output_default(socket_out_->name, NodeItem::Type::Float);
}
#endif
NODE_SHADER_MATERIALX_END
} // namespace blender::nodes::node_shader_wireframe_cc
/* node type definition */
@ -53,6 +62,7 @@ void register_node_type_sh_wireframe()
ntype.declare = file_ns::node_declare;
ntype.draw_buttons = file_ns::node_shader_buts_wireframe;
ntype.gpu_fn = file_ns::node_shader_gpu_wireframe;
ntype.materialx_fn = file_ns::node_shader_materialx;
nodeRegisterType(&ntype);
}

View File

@ -55,6 +55,7 @@ enum RenderEngineTypeFlag {
RE_USE_CUSTOM_FREESTYLE = (1 << 8),
RE_USE_NO_IMAGE_SAVE = (1 << 9),
RE_USE_ALEMBIC_PROCEDURAL = (1 << 10),
RE_USE_MATERIALX = (1 << 11),
};
/** #RenderEngine.flag */

View File

@ -89,6 +89,7 @@ void Engine::sync(Depsgraph *depsgraph, bContext *context)
pxr::SdfPath scene_path = pxr::SdfPath::AbsoluteRootPath().AppendElementString("scene");
hydra_scene_delegate_ = std::make_unique<io::hydra::HydraSceneDelegate>(render_index_.get(),
scene_path);
hydra_scene_delegate_->use_materialx = bl_engine_->type->flag & RE_USE_MATERIALX;
}
hydra_scene_delegate_->populate(depsgraph, context ? CTX_wm_view3d(context) : nullptr);
}