Add Chromium-only Blender WebEngine parity work
This commit is contained in:
184
blender-5.2.0/source/blender/nodes/shader/CMakeLists.txt
Normal file
184
blender-5.2.0/source/blender/nodes/shader/CMakeLists.txt
Normal file
@@ -0,0 +1,184 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
.
|
||||
..
|
||||
PUBLIC include
|
||||
PRIVATE ../intern
|
||||
PRIVATE ../../editors/include
|
||||
PRIVATE ../../makesrna
|
||||
PRIVATE ../../../../intern/sky/include
|
||||
# RNA_prototypes.hh
|
||||
PRIVATE ${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
nodes/node_shader_add_shader.cc
|
||||
nodes/node_shader_ambient_occlusion.cc
|
||||
nodes/node_shader_attribute.cc
|
||||
nodes/node_shader_background.cc
|
||||
nodes/node_shader_bevel.cc
|
||||
nodes/node_shader_blackbody.cc
|
||||
nodes/node_shader_brightness.cc
|
||||
nodes/node_shader_bsdf_diffuse.cc
|
||||
nodes/node_shader_bsdf_glass.cc
|
||||
nodes/node_shader_bsdf_glossy.cc
|
||||
nodes/node_shader_bsdf_hair.cc
|
||||
nodes/node_shader_bsdf_hair_principled.cc
|
||||
nodes/node_shader_bsdf_metallic.cc
|
||||
nodes/node_shader_bsdf_principled.cc
|
||||
nodes/node_shader_bsdf_ray_portal.cc
|
||||
nodes/node_shader_bsdf_refraction.cc
|
||||
nodes/node_shader_bsdf_sheen.cc
|
||||
nodes/node_shader_bsdf_toon.cc
|
||||
nodes/node_shader_bsdf_translucent.cc
|
||||
nodes/node_shader_bsdf_transparent.cc
|
||||
nodes/node_shader_bump.cc
|
||||
nodes/node_shader_camera.cc
|
||||
nodes/node_shader_clamp.cc
|
||||
nodes/node_shader_color_ramp.cc
|
||||
nodes/node_shader_common.cc
|
||||
nodes/node_shader_curves.cc
|
||||
nodes/node_shader_displacement.cc
|
||||
nodes/node_shader_eevee_specular.cc
|
||||
nodes/node_shader_emission.cc
|
||||
nodes/node_shader_fresnel.cc
|
||||
nodes/node_shader_gamma.cc
|
||||
nodes/node_shader_geometry.cc
|
||||
nodes/node_shader_hair_info.cc
|
||||
nodes/node_shader_holdout.cc
|
||||
nodes/node_shader_hueSatVal.cc
|
||||
nodes/node_shader_ies_light.cc
|
||||
nodes/node_shader_invert.cc
|
||||
nodes/node_shader_layer_weight.cc
|
||||
nodes/node_shader_light_falloff.cc
|
||||
nodes/node_shader_light_path.cc
|
||||
nodes/node_shader_map_range.cc
|
||||
nodes/node_shader_mapping.cc
|
||||
nodes/node_shader_math.cc
|
||||
nodes/node_shader_mix.cc
|
||||
nodes/node_shader_mix_rgb.cc
|
||||
nodes/node_shader_mix_shader.cc
|
||||
nodes/node_shader_normal.cc
|
||||
nodes/node_shader_normal_map.cc
|
||||
nodes/node_shader_object_info.cc
|
||||
nodes/node_shader_output_aov.cc
|
||||
nodes/node_shader_output_light.cc
|
||||
nodes/node_shader_output_linestyle.cc
|
||||
nodes/node_shader_output_material.cc
|
||||
nodes/node_shader_output_world.cc
|
||||
nodes/node_shader_particle_info.cc
|
||||
nodes/node_shader_point_info.cc
|
||||
nodes/node_shader_radial_tiling.cc
|
||||
nodes/node_shader_raycast.cc
|
||||
nodes/node_shader_rgb.cc
|
||||
nodes/node_shader_rgb_to_bw.cc
|
||||
nodes/node_shader_script.cc
|
||||
nodes/node_shader_sepcomb_color.cc
|
||||
nodes/node_shader_sepcomb_xyz.cc
|
||||
nodes/node_shader_shader_to_rgb.cc
|
||||
nodes/node_shader_squeeze.cc
|
||||
nodes/node_shader_subsurface_scattering.cc
|
||||
nodes/node_shader_tangent.cc
|
||||
nodes/node_shader_tex_brick.cc
|
||||
nodes/node_shader_tex_checker.cc
|
||||
nodes/node_shader_tex_coord.cc
|
||||
nodes/node_shader_tex_environment.cc
|
||||
nodes/node_shader_tex_gabor.cc
|
||||
nodes/node_shader_tex_gradient.cc
|
||||
nodes/node_shader_tex_image.cc
|
||||
nodes/node_shader_tex_magic.cc
|
||||
nodes/node_shader_tex_noise.cc
|
||||
nodes/node_shader_tex_sky.cc
|
||||
nodes/node_shader_tex_voronoi.cc
|
||||
nodes/node_shader_tex_wave.cc
|
||||
nodes/node_shader_tex_white_noise.cc
|
||||
nodes/node_shader_uv_along_stroke.cc
|
||||
nodes/node_shader_uvmap.cc
|
||||
nodes/node_shader_value.cc
|
||||
nodes/node_shader_vector_displacement.cc
|
||||
nodes/node_shader_vector_math.cc
|
||||
nodes/node_shader_vector_rotate.cc
|
||||
nodes/node_shader_vector_transform.cc
|
||||
nodes/node_shader_vertex_color.cc
|
||||
nodes/node_shader_volume_absorption.cc
|
||||
nodes/node_shader_volume_coefficients.cc
|
||||
nodes/node_shader_volume_info.cc
|
||||
nodes/node_shader_volume_principled.cc
|
||||
nodes/node_shader_volume_scatter.cc
|
||||
nodes/node_shader_wavelength.cc
|
||||
nodes/node_shader_wireframe.cc
|
||||
|
||||
node_shader_register.cc
|
||||
node_shader_tree.cc
|
||||
node_shader_util.cc
|
||||
|
||||
node_shader_register.hh
|
||||
node_shader_util.hh
|
||||
|
||||
include/NOD_shader_raycast.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::blenloader
|
||||
PRIVATE bf::blentranslation
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::functions
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::extern::xxhash
|
||||
bf_intern_sky
|
||||
bf_nodes
|
||||
PRIVATE bf::render
|
||||
PRIVATE bf::windowmanager
|
||||
)
|
||||
|
||||
if(WITH_MATERIALX)
|
||||
add_definitions(-DWITH_MATERIALX)
|
||||
list(APPEND SRC
|
||||
materialx/group_nodes.cc
|
||||
materialx/material.cc
|
||||
materialx/node_graph.cc
|
||||
materialx/node_item.cc
|
||||
materialx/node_parser.cc
|
||||
|
||||
materialx/group_nodes.h
|
||||
materialx/material.h
|
||||
materialx/node_graph.h
|
||||
materialx/node_item.h
|
||||
materialx/node_parser.h
|
||||
)
|
||||
list(APPEND LIB
|
||||
MaterialXCore
|
||||
MaterialXFormat
|
||||
)
|
||||
|
||||
if(WITH_USD)
|
||||
add_definitions(-DWITH_USD)
|
||||
list(APPEND LIB
|
||||
PRIVATE bf::dependencies::optional::usd
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_FREESTYLE)
|
||||
add_definitions(-DWITH_FREESTYLE)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_nodes_shader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
add_library(bf::nodes::shader ALIAS bf_nodes_shader)
|
||||
|
||||
blender_set_target_unity_build(bf_nodes_shader 10)
|
||||
|
||||
# RNA_prototypes.hh
|
||||
add_dependencies(bf_nodes_shader bf_rna)
|
||||
@@ -0,0 +1,105 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
#include "NOD_socket_items.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
struct RaycastSampleAttributeItemsAccessor : public socket_items::SocketItemsAccessorDefaults {
|
||||
using ItemT = NodeRaycastSampleAttributeItem;
|
||||
static StructRNA **item_srna;
|
||||
static int node_type;
|
||||
static constexpr StringRefNull node_idname = "ShaderNodeRaycast";
|
||||
static constexpr bool has_type = true;
|
||||
static constexpr bool has_name = true;
|
||||
static constexpr bool has_single_identifier_str = false;
|
||||
struct operator_idnames {
|
||||
static constexpr StringRefNull add_item = "NODE_OT_sample_attribute_items_item_add";
|
||||
static constexpr StringRefNull remove_item = "NODE_OT_sample_attribute_items_item_remove";
|
||||
static constexpr StringRefNull move_item = "NODE_OT_sample_attribute_items_item_move";
|
||||
};
|
||||
struct ui_idnames {
|
||||
static constexpr StringRefNull list = "NODE_UL_capture_items_list";
|
||||
};
|
||||
struct rna_names {
|
||||
static constexpr StringRefNull items = "sample_attribute_items";
|
||||
static constexpr StringRefNull active_index = "active_index";
|
||||
};
|
||||
|
||||
static socket_items::SocketItemsRef<NodeRaycastSampleAttributeItem> get_items_from_node(
|
||||
bNode &node)
|
||||
{
|
||||
auto *storage = static_cast<NodeShaderRaycast *>(node.storage);
|
||||
return {&storage->sample_attribute_items,
|
||||
&storage->sample_attribute_items_num,
|
||||
&storage->active_index};
|
||||
}
|
||||
|
||||
static void copy_item(const NodeRaycastSampleAttributeItem &src,
|
||||
NodeRaycastSampleAttributeItem &dst)
|
||||
{
|
||||
dst = src;
|
||||
dst.name = BLI_strdup_null(dst.name);
|
||||
}
|
||||
|
||||
static void destruct_item(NodeRaycastSampleAttributeItem *item)
|
||||
{
|
||||
MEM_SAFE_DELETE(item->name);
|
||||
}
|
||||
|
||||
static void blend_write_item(BlendWriter *writer, const ItemT &item);
|
||||
static void blend_read_data_item(BlendDataReader *reader, ItemT &item);
|
||||
|
||||
static eNodeSocketDatatype get_socket_type(const NodeRaycastSampleAttributeItem &item)
|
||||
{
|
||||
return *bke::custom_data_type_to_socket_type(eCustomDataType(item.data_type));
|
||||
}
|
||||
|
||||
static char **get_name(NodeRaycastSampleAttributeItem &item)
|
||||
{
|
||||
return &item.name;
|
||||
}
|
||||
|
||||
static bool supports_socket_type(const eNodeSocketDatatype socket_type, const int /*ntree_type*/)
|
||||
{
|
||||
return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
|
||||
}
|
||||
|
||||
static void init_with_socket_type_and_name(bNode &node,
|
||||
NodeRaycastSampleAttributeItem &item,
|
||||
const eNodeSocketDatatype socket_type,
|
||||
const char *name)
|
||||
{
|
||||
auto *storage = static_cast<NodeShaderRaycast *>(node.storage);
|
||||
item.data_type = *bke::socket_type_to_custom_data_type(socket_type);
|
||||
item.identifier = storage->next_identifier++;
|
||||
socket_items::set_item_name_and_make_unique<RaycastSampleAttributeItemsAccessor>(
|
||||
node, item, name);
|
||||
}
|
||||
|
||||
static std::string input_socket_identifier_for_item(const NodeRaycastSampleAttributeItem &item)
|
||||
{
|
||||
return "Value_" + std::to_string(item.identifier);
|
||||
}
|
||||
static std::string output_socket_identifier_for_item(const NodeRaycastSampleAttributeItem &item)
|
||||
{
|
||||
return "Attribute_" + std::to_string(item.identifier);
|
||||
}
|
||||
static std::string output_socket_identifier_for_item_alpha(
|
||||
const NodeRaycastSampleAttributeItem &item)
|
||||
{
|
||||
BLI_assert(item.data_type == CD_PROP_COLOR);
|
||||
return "Attribute_" + std::to_string(item.identifier) + "_Alpha";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::nodes
|
||||
@@ -0,0 +1,162 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "group_nodes.h"
|
||||
#include "node_parser.h"
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#ifdef USE_MATERIALX_NODEGRAPH
|
||||
# include "BLI_vector.hh"
|
||||
#endif
|
||||
|
||||
namespace blender::nodes::materialx {
|
||||
|
||||
GroupNodeParser::GroupNodeParser(NodeGraph &graph,
|
||||
const bNode *node,
|
||||
const bNodeSocket *socket_out,
|
||||
NodeItem::Type to_type,
|
||||
GroupNodeParser *group_parser,
|
||||
bool use_group_default)
|
||||
: NodeParser(graph, node, socket_out, to_type, group_parser),
|
||||
use_group_default_(use_group_default)
|
||||
{
|
||||
}
|
||||
|
||||
NodeItem GroupNodeParser::compute()
|
||||
{
|
||||
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 empty();
|
||||
}
|
||||
|
||||
NodeGraph group_graph(graph_, ngroup->id.name + 2);
|
||||
|
||||
NodeItem out = GroupOutputNodeParser(
|
||||
group_graph, node_out, socket_out_, to_type_, this, use_group_default_)
|
||||
.compute_full();
|
||||
|
||||
#ifdef USE_MATERIALX_NODEGRAPH
|
||||
/* We have to be in NodeParser's graph_, therefore copying output */
|
||||
NodeItem res = empty();
|
||||
res.output = out.output;
|
||||
return res;
|
||||
#else
|
||||
return out;
|
||||
#endif
|
||||
}
|
||||
|
||||
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 (const 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_DEBUG(LOG_IO_MATERIALX,
|
||||
"%s [%d] => %s",
|
||||
node_->name,
|
||||
node_->typeinfo->type_legacy,
|
||||
NodeItem::type(to_type_).c_str());
|
||||
|
||||
#ifdef USE_MATERIALX_NODEGRAPH
|
||||
/* Checking if output was already computed */
|
||||
NodeItem res = graph_.get_output(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->identifier);
|
||||
}
|
||||
|
||||
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_IO_MATERIALX,
|
||||
"%s [%d] => %s",
|
||||
node_->name,
|
||||
node_->typeinfo->type_legacy,
|
||||
NodeItem::type(to_type_).c_str());
|
||||
|
||||
#ifdef USE_MATERIALX_NODEGRAPH
|
||||
/* Checking if input was already computed */
|
||||
NodeItem res = graph_.get_input(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_->identifier);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::materialx
|
||||
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "node_parser.h"
|
||||
|
||||
/* TODO: #pxr::UsdMtlxRead() doesn't perform node-graphs.
|
||||
* 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(NodeGraph &graph,
|
||||
const bNode *node,
|
||||
const bNodeSocket *socket_out,
|
||||
NodeItem::Type to_type,
|
||||
GroupNodeParser *group_parser,
|
||||
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
|
||||
@@ -0,0 +1,95 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <MaterialXFormat/XmlIo.h>
|
||||
|
||||
#include "node_parser.h"
|
||||
|
||||
#include "BKE_lib_id.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "NOD_shader.h"
|
||||
#include "NOD_shader_nodes_inline.hh"
|
||||
|
||||
#include "material.h"
|
||||
|
||||
namespace blender::nodes::materialx {
|
||||
|
||||
class DefaultMaterialNodeParser : public NodeParser {
|
||||
public:
|
||||
using NodeParser::NodeParser;
|
||||
|
||||
NodeItem compute() override
|
||||
{
|
||||
const Material *material = graph_.material;
|
||||
NodeItem surface = create_node(
|
||||
"open_pbr_surface",
|
||||
NodeItem::Type::SurfaceShader,
|
||||
{{"base_weight", val(1.0f)},
|
||||
{"base_color", val(MaterialX::Color3(material->r, material->g, material->b))},
|
||||
{"base_diffuse_roughness", val(material->roughness)},
|
||||
{"specular_weight", val(material->spec)},
|
||||
{"base_metalness", val(material->metallic)}});
|
||||
|
||||
NodeItem res = create_node(
|
||||
"surfacematerial", NodeItem::Type::Material, {{"surfaceshader", surface}});
|
||||
return res;
|
||||
}
|
||||
|
||||
NodeItem compute_error()
|
||||
{
|
||||
NodeItem surface = create_node("open_pbr_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}});
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph,
|
||||
Material *material,
|
||||
const ExportParams &export_params)
|
||||
{
|
||||
CLOG_DEBUG(LOG_IO_MATERIALX, "Material: %s", material->id.name);
|
||||
|
||||
MaterialX::DocumentPtr doc = MaterialX::createDocument();
|
||||
NodeItem output_item;
|
||||
|
||||
NodeGraph graph(depsgraph, material, export_params, doc);
|
||||
|
||||
bNodeTree *local_tree = bke::node_tree_add_tree(
|
||||
nullptr, "Inlined Tree", material->nodetree->idname);
|
||||
BLI_SCOPED_DEFER([&]() { BKE_id_free(nullptr, &local_tree->id); });
|
||||
|
||||
InlineShaderNodeTreeParams params;
|
||||
inline_shader_node_tree(*material->nodetree, *local_tree, params);
|
||||
|
||||
local_tree->ensure_topology_cache();
|
||||
bNode *output_node = ntreeShaderOutputNode(local_tree, SHD_OUTPUT_ALL);
|
||||
if (output_node && output_node->typeinfo->materialx_fn) {
|
||||
NodeParserData data = {graph, NodeItem::Type::Material, nullptr, graph.empty_node()};
|
||||
output_node->typeinfo->materialx_fn(&data, output_node, nullptr);
|
||||
output_item = data.result;
|
||||
}
|
||||
else {
|
||||
output_item = DefaultMaterialNodeParser(
|
||||
graph, nullptr, nullptr, NodeItem::Type::Material, nullptr)
|
||||
.compute_error();
|
||||
}
|
||||
|
||||
/* This node is expected to have a specific name to link up to USD. */
|
||||
graph.set_output_node_name(output_item);
|
||||
|
||||
CLOG_DEBUG(LOG_IO_MATERIALX,
|
||||
"Material: %s\n%s",
|
||||
material->id.name,
|
||||
MaterialX::writeToXmlString(doc).c_str());
|
||||
return doc;
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::materialx
|
||||
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <MaterialXCore/Document.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct Image;
|
||||
struct ImageUser;
|
||||
struct Main;
|
||||
struct Material;
|
||||
struct Scene;
|
||||
|
||||
namespace nodes::materialx {
|
||||
|
||||
struct ExportParams {
|
||||
std::string output_node_name;
|
||||
std::function<std::string(Main *, Scene *, Image *, ImageUser *)> image_fn;
|
||||
std::string new_active_uvmap_name;
|
||||
std::string original_active_uvmap_name;
|
||||
};
|
||||
|
||||
MaterialX::DocumentPtr export_to_materialx(Depsgraph *depsgraph,
|
||||
Material *material,
|
||||
const ExportParams &export_params);
|
||||
|
||||
} // namespace nodes::materialx
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,178 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef WITH_USD
|
||||
# include <pxr/base/tf/stringUtils.h>
|
||||
#endif
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_string_utils.hh"
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "node_graph.h"
|
||||
|
||||
namespace blender::nodes::materialx {
|
||||
|
||||
/* Prefix for nodes that don't map directly to a Blender shader node. */
|
||||
static const char *ANONYMOUS_NODE_NAME_PREFIX = "node";
|
||||
|
||||
/* Valid name for MaterialX and USD. */
|
||||
static std::string valid_name(const StringRef name)
|
||||
{
|
||||
#ifdef WITH_USD
|
||||
/* Node name should suite to MatX and USD valid names.
|
||||
* It shouldn't start from '_', due to error occurred in Storm delegate. */
|
||||
std::string res = MaterialX::createValidName(pxr::TfMakeValidIdentifier(name));
|
||||
#else
|
||||
std::string res = MaterialX::createValidName(name);
|
||||
#endif
|
||||
if (res[0] == '_') {
|
||||
res = "node" + res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Node Key */
|
||||
|
||||
uint64_t NodeGraph::NodeKey::hash() const
|
||||
{
|
||||
return get_default_hash(node, socket_name, to_type, graph_element);
|
||||
}
|
||||
|
||||
bool NodeGraph::NodeKey::operator==(const NodeGraph::NodeKey &other) const
|
||||
{
|
||||
return node == other.node && socket_name == other.socket_name && to_type == other.to_type &&
|
||||
graph_element == other.graph_element;
|
||||
}
|
||||
|
||||
/* Node Graph */
|
||||
|
||||
NodeGraph::NodeGraph(const Depsgraph *depsgraph,
|
||||
const Material *material,
|
||||
const ExportParams &export_params,
|
||||
const MaterialX::DocumentPtr &document)
|
||||
: depsgraph(depsgraph),
|
||||
material(material),
|
||||
export_params(export_params),
|
||||
graph_element_(document.get()),
|
||||
key_to_name_map_(root_key_to_name_map_)
|
||||
{
|
||||
}
|
||||
|
||||
NodeGraph::NodeGraph(const NodeGraph &parent, const StringRef child_name)
|
||||
: depsgraph(parent.depsgraph),
|
||||
material(parent.material),
|
||||
export_params(parent.export_params),
|
||||
#ifdef USE_MATERIALX_NODEGRAPH
|
||||
key_to_name_map_(root_key_to_name_map_)
|
||||
#else
|
||||
key_to_name_map_(parent.key_to_name_map_)
|
||||
#endif
|
||||
{
|
||||
std::string valid_child_name = valid_name(child_name);
|
||||
|
||||
#ifdef USE_MATERIALX_NODEGRAPH
|
||||
MaterialX::NodeGraphPtr graph = parent.graph_element_->getChildOfType<MaterialX::NodeGraph>(
|
||||
valid_child_name);
|
||||
if (!graph) {
|
||||
CLOG_DEBUG(LOG_IO_MATERIALX, "<nodegraph name=%s>", valid_child_name.c_str());
|
||||
graph = parent.graph_element_->addChild<MaterialX::NodeGraph>(valid_child_name);
|
||||
}
|
||||
graph_element_ = graph.get();
|
||||
#else
|
||||
graph_element_ = parent.graph_element_;
|
||||
node_name_prefix_ = node_name_prefix_ + valid_child_name + "_";
|
||||
#endif
|
||||
}
|
||||
|
||||
NodeItem NodeGraph::empty_node() const
|
||||
{
|
||||
return NodeItem(graph_element_);
|
||||
}
|
||||
|
||||
NodeItem NodeGraph::get_node(const StringRef name) const
|
||||
{
|
||||
NodeItem item = empty_node();
|
||||
item.node = graph_element_->getNode(name);
|
||||
return item;
|
||||
}
|
||||
|
||||
NodeItem NodeGraph::get_output(const StringRef name) const
|
||||
{
|
||||
NodeItem item = empty_node();
|
||||
item.output = graph_element_->getOutput(name);
|
||||
return item;
|
||||
}
|
||||
|
||||
NodeItem NodeGraph::get_input(const StringRef name) const
|
||||
{
|
||||
NodeItem item = empty_node();
|
||||
item.input = graph_element_->getInput(name);
|
||||
return item;
|
||||
}
|
||||
|
||||
std::string NodeGraph::unique_node_name(const bNode *node,
|
||||
const StringRef socket_out_name,
|
||||
NodeItem::Type to_type)
|
||||
{
|
||||
/* Reuse existing name, important in case it got changed due to conflicts. */
|
||||
NodeKey key{node, socket_out_name, to_type, graph_element_};
|
||||
const std::string *existing_name = key_to_name_map_.lookup_ptr(key);
|
||||
if (existing_name) {
|
||||
return *existing_name;
|
||||
}
|
||||
|
||||
/* Generate name based on node, socket, to type and node groups. */
|
||||
std::string name = node->name;
|
||||
|
||||
if (!socket_out_name.is_empty() && node->output_sockets().size() > 1) {
|
||||
name += std::string("_") + socket_out_name;
|
||||
}
|
||||
if (to_type != NodeItem::Type::Empty) {
|
||||
name += "_" + NodeItem::type(to_type);
|
||||
}
|
||||
|
||||
name = node_name_prefix_ + valid_name(name);
|
||||
|
||||
/* Avoid conflicts with anonymous node names. */
|
||||
if (StringRef(name).startswith(ANONYMOUS_NODE_NAME_PREFIX)) {
|
||||
name = "b" + name;
|
||||
}
|
||||
|
||||
/* Ensure the name does not conflict with other nodes in the graph, which may happen when
|
||||
* another Blender node name happens to match the complete name here. Can not just check
|
||||
* the graph because the node with this name might not get added to it immediately. */
|
||||
name = BLI_uniquename_cb(
|
||||
[this](const StringRef check_name) {
|
||||
return check_name == export_params.output_node_name ||
|
||||
graph_element_->getNode(check_name) != nullptr ||
|
||||
used_node_names_.contains(check_name);
|
||||
},
|
||||
'_',
|
||||
name);
|
||||
|
||||
used_node_names_.add(name);
|
||||
key_to_name_map_.add_new(key, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
void NodeGraph::set_output_node_name(const NodeItem &item) const
|
||||
{
|
||||
if (item.node) {
|
||||
item.node->setName(export_params.output_node_name);
|
||||
}
|
||||
}
|
||||
|
||||
std::string NodeGraph::unique_anonymous_node_name(MaterialX::GraphElement *graph_element)
|
||||
{
|
||||
return BLI_uniquename_cb(
|
||||
[graph_element](const StringRef check_name) {
|
||||
return graph_element->getNode(check_name) != nullptr;
|
||||
},
|
||||
'_',
|
||||
ANONYMOUS_NODE_NAME_PREFIX);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::materialx
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <MaterialXCore/Document.h>
|
||||
|
||||
#include "material.h"
|
||||
#include "node_item.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct bNode;
|
||||
struct Material;
|
||||
namespace nodes::materialx {
|
||||
|
||||
/*
|
||||
* Wrapper around MaterialX graph.
|
||||
*/
|
||||
struct NodeGraph {
|
||||
public:
|
||||
const Depsgraph *depsgraph = nullptr;
|
||||
const Material *material = nullptr;
|
||||
const ExportParams &export_params;
|
||||
|
||||
NodeGraph(const Depsgraph *depsgraph,
|
||||
const Material *material,
|
||||
const ExportParams &export_params,
|
||||
const MaterialX::DocumentPtr &document);
|
||||
NodeGraph(const NodeGraph &parent, StringRef child_name);
|
||||
|
||||
NodeItem empty_node() const;
|
||||
NodeItem get_node(StringRef name) const;
|
||||
NodeItem get_output(StringRef name) const;
|
||||
NodeItem get_input(StringRef name) const;
|
||||
|
||||
std::string unique_node_name(const bNode *node,
|
||||
StringRef socket_out_name,
|
||||
NodeItem::Type to_type);
|
||||
void set_output_node_name(const NodeItem &item) const;
|
||||
|
||||
static std::string unique_anonymous_node_name(MaterialX::GraphElement *graph_element);
|
||||
|
||||
protected:
|
||||
struct NodeKey {
|
||||
const bNode *node;
|
||||
std::string socket_name;
|
||||
NodeItem::Type to_type;
|
||||
MaterialX::GraphElement *graph_element;
|
||||
|
||||
uint64_t hash() const;
|
||||
bool operator==(const NodeKey &other) const;
|
||||
};
|
||||
|
||||
MaterialX::GraphElement *graph_element_ = nullptr;
|
||||
Map<NodeKey, const std::string> root_key_to_name_map_;
|
||||
Map<NodeKey, const std::string> &key_to_name_map_;
|
||||
Set<std::string> used_node_names_;
|
||||
std::string node_name_prefix_;
|
||||
};
|
||||
|
||||
} // namespace nodes::materialx
|
||||
} // namespace blender
|
||||
1169
blender-5.2.0/source/blender/nodes/shader/materialx/node_item.cc
Normal file
1169
blender-5.2.0/source/blender/nodes/shader/materialx/node_item.cc
Normal file
File diff suppressed because it is too large
Load Diff
161
blender-5.2.0/source/blender/nodes/shader/materialx/node_item.h
Normal file
161
blender-5.2.0/source/blender/nodes/shader/materialx/node_item.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include <MaterialXCore/Node.h>
|
||||
|
||||
namespace blender::nodes::materialx {
|
||||
|
||||
/**
|
||||
* This class serves as abstraction from MateralX API. It implements arithmetic operations,
|
||||
* conversions 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,
|
||||
|
||||
/* Arithmetic types. NOTE: Ordered by type cast */
|
||||
Boolean,
|
||||
Integer,
|
||||
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 };
|
||||
|
||||
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);
|
||||
static bool is_convertible(eNodeSocketDatatype from_type, Type to_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
|
||||
@@ -0,0 +1,260 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* 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 {
|
||||
|
||||
constexpr StringRef TEXCOORD_NODE_NAME = "node_texcoord";
|
||||
|
||||
CLG_LOGREF_DECLARE_GLOBAL(LOG_IO_MATERIALX, "io.materialx");
|
||||
|
||||
NodeParser::NodeParser(NodeGraph &graph,
|
||||
const bNode *node,
|
||||
const bNodeSocket *socket_out,
|
||||
NodeItem::Type to_type,
|
||||
GroupNodeParser *group_parser)
|
||||
: graph_(graph),
|
||||
node_(node),
|
||||
socket_out_(socket_out),
|
||||
to_type_(to_type),
|
||||
group_parser_(group_parser)
|
||||
{
|
||||
}
|
||||
|
||||
NodeItem NodeParser::compute_full()
|
||||
{
|
||||
NodeItem res = empty();
|
||||
|
||||
if (socket_out_ && !NodeItem::is_convertible(socket_out_->type, to_type_)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Checking if node was already computed */
|
||||
const std::string res_node_name = node_name();
|
||||
res = graph_.get_node(res_node_name);
|
||||
if (!res.node) {
|
||||
CLOG_DEBUG(LOG_IO_MATERIALX,
|
||||
"%s [%d] => %s",
|
||||
node_->name,
|
||||
node_->typeinfo->type_legacy,
|
||||
NodeItem::type(to_type_).c_str());
|
||||
|
||||
res = compute();
|
||||
if (res.node) {
|
||||
res.node->setName(res_node_name);
|
||||
}
|
||||
}
|
||||
return res.convert(to_type_);
|
||||
}
|
||||
|
||||
std::string NodeParser::node_name(const char *override_output_name) const
|
||||
{
|
||||
const NodeItem::Type to_type =
|
||||
ELEM(to_type_, NodeItem::Type::BSDF, NodeItem::Type::EDF, NodeItem::Type::SurfaceOpacity) ?
|
||||
to_type_ :
|
||||
NodeItem::Type::Empty;
|
||||
const StringRef socket_out_name = (override_output_name) ? override_output_name :
|
||||
(socket_out_) ? socket_out_->identifier :
|
||||
"";
|
||||
return graph_.unique_node_name(node_, socket_out_name, to_type);
|
||||
}
|
||||
|
||||
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(UString(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(UString(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(UString(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(UString(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 graph_.empty_node();
|
||||
}
|
||||
|
||||
NodeItem NodeParser::texcoord_node(NodeItem::Type type, const std::string &attribute_name)
|
||||
{
|
||||
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 = graph_.get_node(name);
|
||||
if (!res.node) {
|
||||
/* TODO: Use "Pref" generated texture coordinates for 3D, but needs
|
||||
* work in USD and Hydra mesh export. */
|
||||
const bool is_active_uvmap = attribute_name == "" ||
|
||||
attribute_name == graph_.export_params.original_active_uvmap_name;
|
||||
if (graph_.export_params.new_active_uvmap_name == "st" && is_active_uvmap) {
|
||||
res = create_node("texcoord", type);
|
||||
}
|
||||
else {
|
||||
const std::string &geomprop = (is_active_uvmap) ?
|
||||
graph_.export_params.new_active_uvmap_name :
|
||||
attribute_name;
|
||||
res = create_node("geompropvalue", type, {{"geomprop", val(geomprop)}});
|
||||
}
|
||||
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_BOOLEAN: {
|
||||
bool v = socket.default_value_typed<bNodeSocketValueBoolean>()->value;
|
||||
res.value = MaterialX::Value::createValue<bool>(v);
|
||||
break;
|
||||
}
|
||||
case SOCK_INT: {
|
||||
int v = socket.default_value_typed<bNodeSocketValueInt>()->value;
|
||||
res.value = MaterialX::Value::createValue<int>(v);
|
||||
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_IO_MATERIALX, "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 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_, from_node, link->fromsock, to_type, group_parser_, use_group_default)
|
||||
.compute_full();
|
||||
}
|
||||
if (from_node->is_group_input()) {
|
||||
return GroupInputNodeParser(
|
||||
graph_, from_node, link->fromsock, to_type, group_parser_, use_group_default)
|
||||
.compute_full();
|
||||
}
|
||||
|
||||
if (!from_node->typeinfo->materialx_fn) {
|
||||
CLOG_WARN(LOG_IO_MATERIALX,
|
||||
"Unsupported node: %s [%d]",
|
||||
from_node->name,
|
||||
from_node->typeinfo->type_legacy);
|
||||
return empty();
|
||||
}
|
||||
|
||||
NodeParserData data = {graph_, to_type, group_parser_, empty()};
|
||||
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
|
||||
@@ -0,0 +1,123 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "material.h"
|
||||
#include "node_graph.h"
|
||||
#include "node_item.h"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender::nodes::materialx {
|
||||
|
||||
extern CLG_LogRef *LOG_IO_MATERIALX;
|
||||
|
||||
class GroupNodeParser;
|
||||
|
||||
/**
|
||||
* This is base abstraction class for parsing Blender nodes into MaterialX nodes.
|
||||
* #NodeParser::compute() should be overridden in child classes.
|
||||
*/
|
||||
class NodeParser {
|
||||
protected:
|
||||
NodeGraph &graph_;
|
||||
const bNode *node_;
|
||||
const bNodeSocket *socket_out_;
|
||||
NodeItem::Type to_type_;
|
||||
GroupNodeParser *group_parser_;
|
||||
|
||||
public:
|
||||
NodeParser(NodeGraph &graph,
|
||||
const bNode *node,
|
||||
const bNodeSocket *socket_out,
|
||||
NodeItem::Type to_type,
|
||||
GroupNodeParser *group_parser);
|
||||
virtual ~NodeParser() = default;
|
||||
|
||||
virtual NodeItem compute() = 0;
|
||||
virtual NodeItem compute_full();
|
||||
|
||||
protected:
|
||||
std::string node_name(const char *override_output_name = nullptr) 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,
|
||||
const std::string &attribute_name = "");
|
||||
|
||||
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 {
|
||||
NodeGraph &graph;
|
||||
NodeItem::Type to_type;
|
||||
GroupNodeParser *group_parser;
|
||||
NodeItem result;
|
||||
ExportParams export_params;
|
||||
};
|
||||
|
||||
#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 \
|
||||
} \
|
||||
\
|
||||
static 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, node, out, d->to_type, d->group_parser).compute_full(); \
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::materialx
|
||||
@@ -0,0 +1,117 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "NOD_register.hh"
|
||||
|
||||
#include "node_shader_register.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void register_shader_nodes()
|
||||
{
|
||||
register_node_tree_type_sh();
|
||||
|
||||
register_node_type_sh_group();
|
||||
|
||||
register_node_type_sh_add_shader();
|
||||
register_node_type_sh_ambient_occlusion();
|
||||
register_node_type_sh_attribute();
|
||||
register_node_type_sh_background();
|
||||
register_node_type_sh_bevel();
|
||||
register_node_type_sh_blackbody();
|
||||
register_node_type_sh_brightcontrast();
|
||||
register_node_type_sh_bsdf_diffuse();
|
||||
register_node_type_sh_bsdf_glass();
|
||||
register_node_type_sh_bsdf_glossy();
|
||||
register_node_type_sh_bsdf_hair_principled();
|
||||
register_node_type_sh_bsdf_hair();
|
||||
register_node_type_sh_bsdf_metallic();
|
||||
register_node_type_sh_bsdf_principled();
|
||||
register_node_type_sh_bsdf_ray_portal();
|
||||
register_node_type_sh_bsdf_refraction();
|
||||
register_node_type_sh_bsdf_toon();
|
||||
register_node_type_sh_bsdf_translucent();
|
||||
register_node_type_sh_bsdf_transparent();
|
||||
register_node_type_sh_bsdf_sheen();
|
||||
register_node_type_sh_bump();
|
||||
register_node_type_sh_camera();
|
||||
register_node_type_sh_clamp();
|
||||
register_node_type_sh_combcolor();
|
||||
register_node_type_sh_combxyz();
|
||||
register_node_type_sh_curve_float();
|
||||
register_node_type_sh_curve_rgb();
|
||||
register_node_type_sh_curve_vec();
|
||||
register_node_type_sh_displacement();
|
||||
register_node_type_sh_eevee_specular();
|
||||
register_node_type_sh_emission();
|
||||
register_node_type_sh_fresnel();
|
||||
register_node_type_sh_gamma();
|
||||
register_node_type_sh_geometry();
|
||||
register_node_type_sh_hair_info();
|
||||
register_node_type_sh_holdout();
|
||||
register_node_type_sh_hue_sat();
|
||||
register_node_type_sh_invert();
|
||||
register_node_type_sh_layer_weight();
|
||||
register_node_type_sh_light_falloff();
|
||||
register_node_type_sh_light_path();
|
||||
register_node_type_sh_map_range();
|
||||
register_node_type_sh_mapping();
|
||||
register_node_type_sh_math();
|
||||
register_node_type_sh_mix_rgb();
|
||||
register_node_type_sh_mix_shader();
|
||||
register_node_type_sh_mix();
|
||||
register_node_type_sh_normal_map();
|
||||
register_node_type_sh_normal();
|
||||
register_node_type_sh_object_info();
|
||||
register_node_type_sh_output_aov();
|
||||
register_node_type_sh_output_light();
|
||||
register_node_type_sh_output_linestyle();
|
||||
register_node_type_sh_output_material();
|
||||
register_node_type_sh_output_world();
|
||||
register_node_type_sh_particle_info();
|
||||
register_node_type_sh_point_info();
|
||||
register_node_type_sh_radial_tiling();
|
||||
register_node_type_sh_raycast();
|
||||
register_node_type_sh_rgb();
|
||||
register_node_type_sh_rgbtobw();
|
||||
register_node_type_sh_script();
|
||||
register_node_type_sh_sepcolor();
|
||||
register_node_type_sh_sepxyz();
|
||||
register_node_type_sh_shadertorgb();
|
||||
register_node_type_sh_squeeze();
|
||||
register_node_type_sh_subsurface_scattering();
|
||||
register_node_type_sh_tangent();
|
||||
register_node_type_sh_tex_brick();
|
||||
register_node_type_sh_tex_checker();
|
||||
register_node_type_sh_tex_coord();
|
||||
register_node_type_sh_tex_environment();
|
||||
register_node_type_sh_tex_gabor();
|
||||
register_node_type_sh_tex_gradient();
|
||||
register_node_type_sh_tex_ies();
|
||||
register_node_type_sh_tex_image();
|
||||
register_node_type_sh_tex_magic();
|
||||
register_node_type_sh_tex_noise();
|
||||
register_node_type_sh_tex_sky();
|
||||
register_node_type_sh_tex_voronoi();
|
||||
register_node_type_sh_tex_wave();
|
||||
register_node_type_sh_tex_white_noise();
|
||||
register_node_type_sh_uvalongstroke();
|
||||
register_node_type_sh_uvmap();
|
||||
register_node_type_sh_valtorgb();
|
||||
register_node_type_sh_value();
|
||||
register_node_type_sh_vect_math();
|
||||
register_node_type_sh_vect_transform();
|
||||
register_node_type_sh_vector_displacement();
|
||||
register_node_type_sh_vector_rotate();
|
||||
register_node_type_sh_vertex_color();
|
||||
register_node_type_sh_volume_absorption();
|
||||
register_node_type_sh_volume_info();
|
||||
register_node_type_sh_volume_principled();
|
||||
register_node_type_sh_volume_scatter();
|
||||
register_node_type_sh_volume_coefficients();
|
||||
register_node_type_sh_wavelength();
|
||||
register_node_type_sh_wireframe();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,115 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
void register_node_tree_type_sh();
|
||||
|
||||
void register_node_type_sh_group();
|
||||
|
||||
void register_node_type_sh_add_shader();
|
||||
void register_node_type_sh_ambient_occlusion();
|
||||
void register_node_type_sh_attribute();
|
||||
void register_node_type_sh_background();
|
||||
void register_node_type_sh_bevel();
|
||||
void register_node_type_sh_blackbody();
|
||||
void register_node_type_sh_brightcontrast();
|
||||
void register_node_type_sh_bsdf_diffuse();
|
||||
void register_node_type_sh_bsdf_glass();
|
||||
void register_node_type_sh_bsdf_glossy();
|
||||
void register_node_type_sh_bsdf_hair_principled();
|
||||
void register_node_type_sh_bsdf_hair();
|
||||
void register_node_type_sh_bsdf_metallic();
|
||||
void register_node_type_sh_bsdf_principled();
|
||||
void register_node_type_sh_bsdf_ray_portal();
|
||||
void register_node_type_sh_bsdf_refraction();
|
||||
void register_node_type_sh_bsdf_toon();
|
||||
void register_node_type_sh_bsdf_translucent();
|
||||
void register_node_type_sh_bsdf_transparent();
|
||||
void register_node_type_sh_bsdf_sheen();
|
||||
void register_node_type_sh_bump();
|
||||
void register_node_type_sh_camera();
|
||||
void register_node_type_sh_clamp();
|
||||
void register_node_type_sh_combcolor();
|
||||
void register_node_type_sh_combxyz();
|
||||
void register_node_type_sh_curve_float();
|
||||
void register_node_type_sh_curve_rgb();
|
||||
void register_node_type_sh_curve_vec();
|
||||
void register_node_type_sh_displacement();
|
||||
void register_node_type_sh_dynamic();
|
||||
void register_node_type_sh_eevee_metallic();
|
||||
void register_node_type_sh_eevee_specular();
|
||||
void register_node_type_sh_emission();
|
||||
void register_node_type_sh_fresnel();
|
||||
void register_node_type_sh_gamma();
|
||||
void register_node_type_sh_geometry();
|
||||
void register_node_type_sh_hair_info();
|
||||
void register_node_type_sh_holdout();
|
||||
void register_node_type_sh_hue_sat();
|
||||
void register_node_type_sh_invert();
|
||||
void register_node_type_sh_layer_weight();
|
||||
void register_node_type_sh_light_falloff();
|
||||
void register_node_type_sh_light_path();
|
||||
void register_node_type_sh_map_range();
|
||||
void register_node_type_sh_mapping();
|
||||
void register_node_type_sh_math();
|
||||
void register_node_type_sh_mix_rgb();
|
||||
void register_node_type_sh_mix_shader();
|
||||
void register_node_type_sh_mix();
|
||||
void register_node_type_sh_normal_map();
|
||||
void register_node_type_sh_normal();
|
||||
void register_node_type_sh_object_info();
|
||||
void register_node_type_sh_output_aov();
|
||||
void register_node_type_sh_output_eevee_material();
|
||||
void register_node_type_sh_output_light();
|
||||
void register_node_type_sh_output_linestyle();
|
||||
void register_node_type_sh_output_material();
|
||||
void register_node_type_sh_output_world();
|
||||
void register_node_type_sh_particle_info();
|
||||
void register_node_type_sh_point_info();
|
||||
void register_node_type_sh_radial_tiling();
|
||||
void register_node_type_sh_raycast();
|
||||
void register_node_type_sh_rgb();
|
||||
void register_node_type_sh_rgbtobw();
|
||||
void register_node_type_sh_script();
|
||||
void register_node_type_sh_sepcolor();
|
||||
void register_node_type_sh_sepxyz();
|
||||
void register_node_type_sh_shadertorgb();
|
||||
void register_node_type_sh_squeeze();
|
||||
void register_node_type_sh_subsurface_scattering();
|
||||
void register_node_type_sh_tangent();
|
||||
void register_node_type_sh_tex_brick();
|
||||
void register_node_type_sh_tex_checker();
|
||||
void register_node_type_sh_tex_coord();
|
||||
void register_node_type_sh_tex_environment();
|
||||
void register_node_type_sh_tex_gabor();
|
||||
void register_node_type_sh_tex_gradient();
|
||||
void register_node_type_sh_tex_ies();
|
||||
void register_node_type_sh_tex_image();
|
||||
void register_node_type_sh_tex_magic();
|
||||
void register_node_type_sh_tex_noise();
|
||||
void register_node_type_sh_tex_sky();
|
||||
void register_node_type_sh_tex_voronoi();
|
||||
void register_node_type_sh_tex_wave();
|
||||
void register_node_type_sh_tex_white_noise();
|
||||
void register_node_type_sh_uvalongstroke();
|
||||
void register_node_type_sh_uvmap();
|
||||
void register_node_type_sh_valtorgb();
|
||||
void register_node_type_sh_value();
|
||||
void register_node_type_sh_vect_math();
|
||||
void register_node_type_sh_vect_transform();
|
||||
void register_node_type_sh_vector_displacement();
|
||||
void register_node_type_sh_vector_rotate();
|
||||
void register_node_type_sh_vertex_color();
|
||||
void register_node_type_sh_volume_absorption();
|
||||
void register_node_type_sh_volume_info();
|
||||
void register_node_type_sh_volume_principled();
|
||||
void register_node_type_sh_volume_scatter();
|
||||
void register_node_type_sh_volume_coefficients();
|
||||
void register_node_type_sh_wavelength();
|
||||
void register_node_type_sh_wireframe();
|
||||
|
||||
} // namespace blender
|
||||
1093
blender-5.2.0/source/blender/nodes/shader/node_shader_tree.cc
Normal file
1093
blender-5.2.0/source/blender/nodes/shader/node_shader_tree.cc
Normal file
File diff suppressed because it is too large
Load Diff
472
blender-5.2.0/source/blender/nodes/shader/node_shader_util.cc
Normal file
472
blender-5.2.0/source/blender/nodes/shader/node_shader_util.cc
Normal file
@@ -0,0 +1,472 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup nodes
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "node_exec.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
bool sh_node_poll_default(const bke::bNodeType * /*ntype*/,
|
||||
const bNodeTree *ntree,
|
||||
const char **r_disabled_hint)
|
||||
{
|
||||
if (!STREQ(ntree->idname, "ShaderNodeTree")) {
|
||||
*r_disabled_hint = RPT_("Not a shader node tree");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sh_geo_poll_default(const bke::bNodeType * /*ntype*/,
|
||||
const bNodeTree *ntree,
|
||||
const char **r_disabled_hint)
|
||||
{
|
||||
if (!STR_ELEM(ntree->idname, "ShaderNodeTree", "GeometryNodeTree")) {
|
||||
*r_disabled_hint = RPT_("Not a shader or geometry node tree");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool common_poll_default(const bke::bNodeType * /*ntype*/,
|
||||
const bNodeTree *ntree,
|
||||
const char **r_disabled_hint)
|
||||
{
|
||||
if (!STR_ELEM(ntree->idname, "ShaderNodeTree", "GeometryNodeTree", "CompositorNodeTree")) {
|
||||
*r_disabled_hint = RPT_("Not a shader, geometry, or compositor node tree");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void sh_node_type_base(bke::bNodeType *ntype,
|
||||
UString idname,
|
||||
const std::optional<int16_t> legacy_type)
|
||||
{
|
||||
bke::node_type_base(*ntype, idname, legacy_type);
|
||||
|
||||
ntype->poll = sh_node_poll_default;
|
||||
ntype->insert_link = node_insert_link_default;
|
||||
ntype->gather_link_search_ops = nodes::search_link_ops_for_basic_node;
|
||||
}
|
||||
|
||||
void sh_geo_node_type_base(bke::bNodeType *ntype,
|
||||
UString idname,
|
||||
const std::optional<int16_t> legacy_type)
|
||||
{
|
||||
bke::node_type_base(*ntype, idname, legacy_type);
|
||||
|
||||
ntype->poll = sh_geo_poll_default;
|
||||
ntype->insert_link = node_insert_link_default;
|
||||
ntype->gather_link_search_ops = nodes::search_link_ops_for_basic_node;
|
||||
}
|
||||
|
||||
void common_node_type_base(bke::bNodeType *ntype,
|
||||
UString idname,
|
||||
const std::optional<int16_t> legacy_type)
|
||||
{
|
||||
sh_node_type_base(ntype, idname, legacy_type);
|
||||
ntype->poll = common_poll_default;
|
||||
ntype->gather_link_search_ops = nodes::search_link_ops_for_basic_node;
|
||||
}
|
||||
|
||||
bool line_style_shader_nodes_poll(const bContext *C)
|
||||
{
|
||||
const SpaceNode *snode = CTX_wm_space_node(C);
|
||||
return snode->shaderfrom == SNODE_SHADER_LINESTYLE;
|
||||
}
|
||||
|
||||
bool world_shader_nodes_poll(const bContext *C)
|
||||
{
|
||||
const SpaceNode *snode = CTX_wm_space_node(C);
|
||||
return snode->shaderfrom == SNODE_SHADER_WORLD;
|
||||
}
|
||||
|
||||
bool object_shader_nodes_poll(const bContext *C)
|
||||
{
|
||||
const SpaceNode *snode = CTX_wm_space_node(C);
|
||||
return snode->shaderfrom == SNODE_SHADER_OBJECT;
|
||||
}
|
||||
|
||||
bool object_cycles_shader_nodes_poll(const bContext *C)
|
||||
{
|
||||
if (!object_shader_nodes_poll(C)) {
|
||||
return false;
|
||||
}
|
||||
const RenderEngineType *engine_type = CTX_data_engine_type(C);
|
||||
return STREQ(engine_type->idname, "CYCLES");
|
||||
}
|
||||
|
||||
bool object_eevee_shader_nodes_poll(const bContext *C)
|
||||
{
|
||||
if (!object_shader_nodes_poll(C)) {
|
||||
return false;
|
||||
}
|
||||
const RenderEngineType *engine_type = CTX_data_engine_type(C);
|
||||
return STREQ(engine_type->idname, "BLENDER_EEVEE") ||
|
||||
STREQ(engine_type->idname, "BLENDER_EEVEE");
|
||||
}
|
||||
|
||||
/* ****** */
|
||||
|
||||
static void nodestack_get_vec(float *in, short type_in, bNodeStack *ns)
|
||||
{
|
||||
const float *from = ns->vec;
|
||||
|
||||
if (type_in == SOCK_FLOAT) {
|
||||
if (ns->sockettype == SOCK_FLOAT) {
|
||||
*in = *from;
|
||||
}
|
||||
else {
|
||||
*in = (from[0] + from[1] + from[2]) / 3.0f;
|
||||
}
|
||||
}
|
||||
else if (type_in == SOCK_VECTOR) {
|
||||
if (ns->sockettype == SOCK_FLOAT) {
|
||||
in[0] = from[0];
|
||||
in[1] = from[0];
|
||||
in[2] = from[0];
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(in, from);
|
||||
}
|
||||
}
|
||||
else if (type_in == SOCK_ROTATION) {
|
||||
copy_v4_v4(in, from);
|
||||
}
|
||||
else { /* type_in==SOCK_RGBA */
|
||||
if (ns->sockettype == SOCK_RGBA) {
|
||||
copy_v4_v4(in, from);
|
||||
}
|
||||
else if (ns->sockettype == SOCK_FLOAT) {
|
||||
in[0] = from[0];
|
||||
in[1] = from[0];
|
||||
in[2] = from[0];
|
||||
in[3] = 1.0f;
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(in, from);
|
||||
in[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void node_gpu_stack_from_data(GPUNodeStack *gs, bNodeSocket *socket, bNodeStack *ns)
|
||||
{
|
||||
memset(gs, 0, sizeof(*gs));
|
||||
|
||||
if (ns == nullptr) {
|
||||
/* node_get_stack() will generate nullptr bNodeStack pointers
|
||||
* for unknown/unsupported types of sockets. */
|
||||
zero_v4(gs->vec);
|
||||
gs->link = nullptr;
|
||||
gs->type = GPU_NONE;
|
||||
gs->hasinput = false;
|
||||
gs->hasoutput = false;
|
||||
gs->sockettype = socket->type;
|
||||
}
|
||||
else {
|
||||
nodestack_get_vec(gs->vec, socket->type, ns);
|
||||
gs->link = static_cast<GPUNodeLink *>(ns->data);
|
||||
|
||||
if (socket->type == SOCK_FLOAT) {
|
||||
gs->type = GPU_FLOAT;
|
||||
}
|
||||
else if (socket->type == SOCK_INT) {
|
||||
gs->type = GPU_FLOAT; /* HACK: Support as float. */
|
||||
}
|
||||
else if (socket->type == SOCK_BOOLEAN) {
|
||||
gs->type = GPU_FLOAT; /* HACK: Support as float. */
|
||||
}
|
||||
else if (socket->type == SOCK_VECTOR) {
|
||||
switch (socket->default_value_typed<bNodeSocketValueVector>()->dimensions) {
|
||||
case 2:
|
||||
gs->type = GPU_VEC2;
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
gs->type = GPU_VEC3;
|
||||
break;
|
||||
case 4:
|
||||
gs->type = GPU_VEC4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (socket->type == SOCK_RGBA) {
|
||||
gs->type = GPU_VEC4;
|
||||
}
|
||||
else if (socket->type == SOCK_SHADER) {
|
||||
gs->type = GPU_CLOSURE;
|
||||
}
|
||||
else if (socket->type == SOCK_ROTATION) {
|
||||
gs->type = GPU_VEC4;
|
||||
}
|
||||
else {
|
||||
gs->type = GPU_NONE;
|
||||
}
|
||||
|
||||
gs->hasinput = ns->hasinput && ns->data;
|
||||
/* XXX Commented out the ns->data check here, as it seems it's not always set,
|
||||
* even though there *is* a valid connection/output... But that might need
|
||||
* further investigation.
|
||||
*/
|
||||
gs->hasoutput = ns->hasoutput /*&& ns->data*/;
|
||||
gs->sockettype = ns->sockettype;
|
||||
}
|
||||
}
|
||||
|
||||
void node_data_from_gpu_stack(bNodeStack *ns, GPUNodeStack *gs)
|
||||
{
|
||||
copy_v4_v4(ns->vec, gs->vec);
|
||||
ns->data = gs->link;
|
||||
ns->sockettype = gs->sockettype;
|
||||
}
|
||||
|
||||
static void gpu_stack_from_data_list(GPUNodeStack *gs,
|
||||
ListBaseT<bNodeSocket> *sockets,
|
||||
bNodeStack **ns)
|
||||
{
|
||||
int i = 0;
|
||||
for (bNodeSocket &socket : *sockets) {
|
||||
node_gpu_stack_from_data(&gs[i], &socket, ns[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
gs[i].end = true;
|
||||
}
|
||||
|
||||
static void data_from_gpu_stack_list(ListBaseT<bNodeSocket> *sockets,
|
||||
bNodeStack **ns,
|
||||
GPUNodeStack *gs)
|
||||
{
|
||||
int i = 0;
|
||||
for (bNodeSocket &socket : *sockets) {
|
||||
if (ELEM(socket.type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER))
|
||||
{
|
||||
node_data_from_gpu_stack(ns[i], &gs[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bke::node_supports_active_flag(const bNode &node, int sub_activity)
|
||||
{
|
||||
BLI_assert(ELEM(sub_activity, NODE_ACTIVE_TEXTURE, NODE_ACTIVE_PAINT_CANVAS));
|
||||
switch (sub_activity) {
|
||||
case NODE_ACTIVE_TEXTURE:
|
||||
return node.typeinfo->nclass == NODE_CLASS_TEXTURE;
|
||||
case NODE_ACTIVE_PAINT_CANVAS:
|
||||
return ELEM(node.type_legacy, SH_NODE_TEX_IMAGE, SH_NODE_ATTRIBUTE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bNode *node_get_active(bNodeTree *ntree, int sub_activity)
|
||||
{
|
||||
BLI_assert(ELEM(sub_activity, NODE_ACTIVE_TEXTURE, NODE_ACTIVE_PAINT_CANVAS));
|
||||
/* this is the node we texture paint and draw in textured draw */
|
||||
bNode *inactivenode = nullptr, *activetexnode = nullptr, *activegroup = nullptr;
|
||||
bool hasgroup = false;
|
||||
|
||||
if (!ntree) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (bNode *node : ntree->all_nodes()) {
|
||||
if (node->flag & sub_activity) {
|
||||
activetexnode = node;
|
||||
/* if active we can return immediately */
|
||||
if (node->flag & NODE_ACTIVE) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else if (!inactivenode && bke::node_supports_active_flag(*node, sub_activity)) {
|
||||
inactivenode = node;
|
||||
}
|
||||
else if (node->type_legacy == NODE_GROUP) {
|
||||
if (node->flag & NODE_ACTIVE) {
|
||||
activegroup = node;
|
||||
}
|
||||
else {
|
||||
hasgroup = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* first, check active group for textures */
|
||||
if (activegroup) {
|
||||
bNode *tnode = node_get_active(id_cast<bNodeTree *>(activegroup->id), sub_activity);
|
||||
/* active node takes priority, so ignore any other possible nodes here */
|
||||
if (tnode) {
|
||||
return tnode;
|
||||
}
|
||||
}
|
||||
|
||||
if (activetexnode) {
|
||||
return activetexnode;
|
||||
}
|
||||
|
||||
if (hasgroup) {
|
||||
/* node active texture node in this tree, look inside groups */
|
||||
for (bNode *node : ntree->all_nodes()) {
|
||||
if (node->type_legacy == NODE_GROUP) {
|
||||
bNode *tnode = node_get_active(id_cast<bNodeTree *>(node->id), sub_activity);
|
||||
if (tnode && ((tnode->flag & sub_activity) || !inactivenode)) {
|
||||
return tnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inactivenode;
|
||||
}
|
||||
|
||||
namespace bke {
|
||||
|
||||
bNode *node_get_active_texture(bNodeTree &ntree)
|
||||
{
|
||||
return node_get_active(&ntree, NODE_ACTIVE_TEXTURE);
|
||||
}
|
||||
|
||||
bNode *node_get_active_paint_canvas(bNodeTree &ntree)
|
||||
{
|
||||
return node_get_active(&ntree, NODE_ACTIVE_PAINT_CANVAS);
|
||||
}
|
||||
} // namespace bke
|
||||
|
||||
void ntreeExecGPUNodes(bNodeTreeExec *exec,
|
||||
GPUMaterial *mat,
|
||||
bNode *output_node,
|
||||
const int *depth_level)
|
||||
{
|
||||
bNodeExec *nodeexec;
|
||||
bNode *node;
|
||||
int n;
|
||||
bNodeStack *stack;
|
||||
bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */
|
||||
bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */
|
||||
GPUNodeStack gpuin[MAX_SOCKET + 1], gpuout[MAX_SOCKET + 1];
|
||||
bool do_it;
|
||||
|
||||
stack = exec->stack;
|
||||
|
||||
for (n = 0, nodeexec = exec->nodeexec; n < exec->totnodes; n++, nodeexec++) {
|
||||
node = nodeexec->node;
|
||||
|
||||
if (depth_level && node->runtime->tmp_flag != *depth_level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
do_it = false;
|
||||
/* for groups, only execute outputs for edited group */
|
||||
if (node->typeinfo->nclass == NODE_CLASS_OUTPUT) {
|
||||
if ((output_node != nullptr) && (node == output_node)) {
|
||||
do_it = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
do_it = node->runtime->need_exec;
|
||||
node->runtime->need_exec = 0;
|
||||
}
|
||||
|
||||
if (do_it) {
|
||||
BLI_assert(!depth_level || node->runtime->tmp_flag >= 0);
|
||||
if (node->typeinfo->gpu_fn) {
|
||||
node_get_stack(node, stack, nsin, nsout);
|
||||
gpu_stack_from_data_list(gpuin, &node->inputs, nsin);
|
||||
gpu_stack_from_data_list(gpuout, &node->outputs, nsout);
|
||||
if (node->typeinfo->gpu_fn(mat, node, &nodeexec->data, gpuin, gpuout)) {
|
||||
data_from_gpu_stack_list(&node->outputs, nsout, gpuout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void node_shader_gpu_bump_tex_coord(GPUMaterial *mat, bNode * /*node*/, GPUNodeLink **link)
|
||||
{
|
||||
GPU_link(mat, "differentiate_texco", *link, link);
|
||||
}
|
||||
|
||||
void node_shader_gpu_default_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link)
|
||||
{
|
||||
if (!*link) {
|
||||
*link = GPU_attribute(mat, CD_ORCO, "");
|
||||
node_shader_gpu_bump_tex_coord(mat, node, link);
|
||||
}
|
||||
}
|
||||
|
||||
void node_shader_gpu_tex_mapping(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack * /*out*/)
|
||||
{
|
||||
NodeTexBase *base = static_cast<NodeTexBase *>(node->storage);
|
||||
TexMapping *texmap = &base->tex_mapping;
|
||||
float domin = (texmap->flag & TEXMAP_CLIP_MIN) != 0;
|
||||
float domax = (texmap->flag & TEXMAP_CLIP_MAX) != 0;
|
||||
|
||||
if (domin || domax || !(texmap->flag & TEXMAP_UNIT_MATRIX)) {
|
||||
static float max[3] = {FLT_MAX, FLT_MAX, FLT_MAX};
|
||||
static float min[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
|
||||
GPUNodeLink *tmin, *tmax, *tmat0, *tmat1, *tmat2, *tmat3;
|
||||
|
||||
tmin = GPU_uniform((domin) ? texmap->min : min);
|
||||
tmax = GPU_uniform((domax) ? texmap->max : max);
|
||||
tmat0 = GPU_uniform(static_cast<float *>(texmap->mat[0]));
|
||||
tmat1 = GPU_uniform(static_cast<float *>(texmap->mat[1]));
|
||||
tmat2 = GPU_uniform(static_cast<float *>(texmap->mat[2]));
|
||||
tmat3 = GPU_uniform(static_cast<float *>(texmap->mat[3]));
|
||||
|
||||
GPU_link(mat, "mapping_mat4", in[0].link, tmat0, tmat1, tmat2, tmat3, tmin, tmax, &in[0].link);
|
||||
|
||||
if (texmap->type == TEXMAP_TYPE_NORMAL) {
|
||||
GPU_link(mat, "vector_normalize", in[0].link, &in[0].link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
|
||||
{
|
||||
float3x3 xyz_to_rgb = IMB_colormanagement_get_xyz_to_scene_linear();
|
||||
data->r[0] = xyz_to_rgb[0][0];
|
||||
data->r[1] = xyz_to_rgb[1][0];
|
||||
data->r[2] = xyz_to_rgb[2][0];
|
||||
data->g[0] = xyz_to_rgb[0][1];
|
||||
data->g[1] = xyz_to_rgb[1][1];
|
||||
data->g[2] = xyz_to_rgb[2][1];
|
||||
data->b[0] = xyz_to_rgb[0][2];
|
||||
data->b[1] = xyz_to_rgb[1][2];
|
||||
data->b[2] = xyz_to_rgb[2][2];
|
||||
}
|
||||
|
||||
void search_link_ops_for_shader_bsdf_node(nodes::GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
static Set<UString> skip_socket_identifiers = {"Weight"_ustr};
|
||||
nodes::search_filtered_link_ops_for_basic_node(params, skip_socket_identifiers);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup nodes
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cfloat>
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_listbase.h" // IWYU pragma: export
|
||||
#include "BLI_math_constants.h" // IWYU pragma: export
|
||||
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_legacy_types.hh" // IWYU pragma: export
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "GPU_material.hh"
|
||||
|
||||
#include "NOD_socket_declarations.hh" // IWYU pragma: export
|
||||
|
||||
#include "node_shader_register.hh" // IWYU pragma: export
|
||||
|
||||
#ifdef WITH_MATERIALX
|
||||
# include "materialx/node_parser.h" // IWYU pragma: export
|
||||
#else
|
||||
# define NODE_SHADER_MATERIALX_BEGIN bke::NodeMaterialXFunction node_shader_materialx = nullptr;
|
||||
# define NODE_SHADER_MATERIALX_END
|
||||
#endif
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct bNodeExecContext;
|
||||
struct bNodeTreeExec;
|
||||
struct GPUNodeLink;
|
||||
struct GPUNodeStack;
|
||||
struct GPUMaterial;
|
||||
|
||||
bool sh_node_poll_default(const bke::bNodeType *ntype,
|
||||
const bNodeTree *ntree,
|
||||
const char **r_disabled_hint);
|
||||
void sh_node_type_base(bke::bNodeType *ntype,
|
||||
UString idname,
|
||||
std::optional<int16_t> legacy_type = std::nullopt);
|
||||
void sh_geo_node_type_base(bke::bNodeType *ntype,
|
||||
UString idname,
|
||||
std::optional<int16_t> legacy_type = std::nullopt);
|
||||
void common_node_type_base(bke::bNodeType *ntype,
|
||||
UString idname,
|
||||
std::optional<int16_t> legacy_type = std::nullopt);
|
||||
bool line_style_shader_nodes_poll(const bContext *C);
|
||||
bool world_shader_nodes_poll(const bContext *C);
|
||||
bool object_shader_nodes_poll(const bContext *C);
|
||||
bool object_cycles_shader_nodes_poll(const bContext *C);
|
||||
bool object_eevee_shader_nodes_poll(const bContext *C);
|
||||
|
||||
/* ********* exec data struct, remains internal *********** */
|
||||
|
||||
struct XYZ_to_RGB /* Transposed #imbuf_xyz_to_rgb, passed as 3x vec3. */
|
||||
{
|
||||
float r[3], g[3], b[3];
|
||||
};
|
||||
|
||||
void node_gpu_stack_from_data(GPUNodeStack *gs, bNodeSocket *socket, bNodeStack *ns);
|
||||
void node_data_from_gpu_stack(bNodeStack *ns, GPUNodeStack *gs);
|
||||
void node_shader_gpu_bump_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link);
|
||||
void node_shader_gpu_default_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link);
|
||||
void node_shader_gpu_tex_mapping(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out);
|
||||
|
||||
bNodeTreeExec *ntreeShaderBeginExecTree_internal(bNodeExecContext *context,
|
||||
bNodeTree *ntree,
|
||||
bNodeInstanceKey parent_key);
|
||||
void ntreeShaderEndExecTree_internal(bNodeTreeExec *exec);
|
||||
|
||||
/* If depth_level is not null, only nodes where `node->runtime->tmp_flag == depth_level` will be
|
||||
* executed. This allows finer control over node execution order without modifying the tree
|
||||
* topology. */
|
||||
void ntreeExecGPUNodes(bNodeTreeExec *exec,
|
||||
GPUMaterial *mat,
|
||||
bNode *output_node,
|
||||
const int *depth_level = nullptr);
|
||||
|
||||
void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data);
|
||||
|
||||
/* Link search callback that ignores the "Weight" socket in shader nodes.
|
||||
* These sockets are never available and must be ignored to avoid invalid link operations. */
|
||||
void search_link_ops_for_shader_bsdf_node(nodes::GatherLinkSearchOpParams ¶ms);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,75 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_add_shader_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Shader>("Shader"_ustr);
|
||||
b.add_input<decl::Shader>("Shader"_ustr, "Shader_001"_ustr);
|
||||
b.add_output<decl::Shader>("Shader"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_add_shader(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
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 nodes::node_shader_add_shader_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_add_shader()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_add_shader_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeAddShader"_ustr, SH_NODE_ADD_SHADER);
|
||||
ntype.ui_name = "Add Shader";
|
||||
ntype.ui_description = "Add two Shaders together";
|
||||
ntype.enum_name_legacy = "ADD_SHADER";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,106 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_ambient_occlusion_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({1.0f, 1.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Float>("Distance"_ustr).default_value(1.0f).min(0.0f).max(1000.0f);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).min(-1.0f).max(1.0f).hide_value();
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
b.add_output<decl::Float>("AO"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_ambient_occlusion(ui::Layout &layout,
|
||||
bContext * /*C*/,
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "samples", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
layout.prop(ptr, "inside", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
layout.prop(ptr, "only_local", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[2].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[2].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_AO);
|
||||
|
||||
float inverted = (node->custom2 & SHD_AO_INSIDE) ? 1.0f : 0.0f;
|
||||
float f_samples = divide_ceil_u(node->custom1, 4);
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"node_ambient_occlusion",
|
||||
in,
|
||||
out,
|
||||
GPU_constant(&inverted),
|
||||
GPU_constant(&f_samples));
|
||||
}
|
||||
|
||||
static void node_shader_init_ambient_occlusion(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = 16; /* samples */
|
||||
node->custom2 = 0;
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
/* TODO: observed crash while rendering MaterialX_v1_38_6::ExceptionShaderGenError */
|
||||
/**
|
||||
* \code{.cc}
|
||||
* 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);
|
||||
* \endcode
|
||||
*/
|
||||
return get_output_default(socket_out_->identifier, NodeItem::Type::Any);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_ambient_occlusion_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_ambient_occlusion()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_ambient_occlusion_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeAmbientOcclusion"_ustr, SH_NODE_AMBIENT_OCCLUSION);
|
||||
ntype.ui_name = "Ambient Occlusion";
|
||||
ntype.ui_description =
|
||||
"Compute how much the hemisphere above the shading point is occluded, for example to add "
|
||||
"weathering effects to corners.\nNote: For Cycles, this may slow down renders significantly";
|
||||
ntype.enum_name_legacy = "AMBIENT_OCCLUSION";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,126 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_attribute_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
b.add_output<decl::Vector>("Vector"_ustr);
|
||||
b.add_output<decl::Float>("Factor"_ustr, "Fac"_ustr);
|
||||
b.add_output<decl::Float>("Alpha"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_attribute(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "attribute_type", UI_ITEM_NONE, "", ICON_NONE);
|
||||
layout.prop(ptr,
|
||||
RNA_struct_find_property(ptr, "attribute_name"),
|
||||
-1,
|
||||
0,
|
||||
UI_ITEM_NONE,
|
||||
"",
|
||||
ICON_NONE,
|
||||
IFACE_("Name"));
|
||||
}
|
||||
|
||||
static void node_shader_init_attribute(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeShaderAttribute *attr = MEM_new<NodeShaderAttribute>("NodeShaderAttribute");
|
||||
node->storage = attr;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_attribute(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
NodeShaderAttribute *attr = static_cast<NodeShaderAttribute *>(node->storage);
|
||||
bool is_varying = attr->type == SHD_ATTRIBUTE_GEOMETRY;
|
||||
float attr_hash = 0.0f;
|
||||
|
||||
GPUNodeLink *cd_attr;
|
||||
|
||||
if (is_varying) {
|
||||
cd_attr = GPU_attribute(mat, CD_AUTO_FROM_NAME, attr->name);
|
||||
|
||||
if (STREQ(attr->name, "color")) {
|
||||
GPU_link(mat, "node_attribute_color", cd_attr, &cd_attr);
|
||||
}
|
||||
else if (STREQ(attr->name, "temperature")) {
|
||||
GPU_link(mat, "node_attribute_temperature", cd_attr, &cd_attr);
|
||||
}
|
||||
}
|
||||
else if (attr->type == SHD_ATTRIBUTE_VIEW_LAYER) {
|
||||
cd_attr = GPU_layer_attribute(mat, attr->name);
|
||||
}
|
||||
else {
|
||||
cd_attr = GPU_uniform_attribute(mat,
|
||||
attr->name,
|
||||
attr->type == SHD_ATTRIBUTE_INSTANCER,
|
||||
reinterpret_cast<uint32_t *>(&attr_hash));
|
||||
|
||||
GPU_link(mat, "node_attribute_uniform", cd_attr, GPU_constant(&attr_hash), &cd_attr);
|
||||
}
|
||||
|
||||
GPU_stack_link(mat, node, "node_attribute", in, out, cd_attr);
|
||||
|
||||
if (is_varying) {
|
||||
|
||||
for (const auto [i, sock] : node->outputs.enumerate()) {
|
||||
node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
/* TODO: some outputs expected be implemented within the next iteration
|
||||
* (see node-definition `<geompropvalue>`). */
|
||||
return get_output_default(socket_out_->identifier, NodeItem::Type::Any);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_attribute_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_attribute()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_attribute_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeAttribute"_ustr, SH_NODE_ATTRIBUTE);
|
||||
ntype.ui_name = "Attribute";
|
||||
ntype.ui_description = "Retrieve attributes attached to objects or geometry";
|
||||
ntype.enum_name_legacy = "ATTRIBUTE";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_attribute;
|
||||
ntype.initfunc = file_ns::node_shader_init_attribute;
|
||||
bke::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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,61 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_background_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr)
|
||||
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
|
||||
.description("Color of the emitted light");
|
||||
b.add_input<decl::Float>("Strength"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1000000.0f)
|
||||
.description("Strength of the emitted light")
|
||||
.translation_context(BLT_I18NCONTEXT_AMOUNT);
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("Background"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_background(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
return GPU_stack_link(mat, node, "node_background", in, out);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_background_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_background()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_background_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBackground"_ustr, SH_NODE_BACKGROUND);
|
||||
ntype.ui_name = "Background";
|
||||
ntype.ui_description =
|
||||
"Add background light emission.\nNote: This node should only be used for the world surface "
|
||||
"output";
|
||||
ntype.enum_name_legacy = "BACKGROUND";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.add_ui_poll = world_shader_nodes_poll;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_background;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,78 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bevel_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Radius"_ustr).default_value(0.05f).min(0.0f).max(1000.0f);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_output<decl::Vector>("Normal"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_bevel(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "samples", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_bevel(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = 4; /* samples */
|
||||
}
|
||||
|
||||
static int gpu_shader_bevel(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[1].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[1].link);
|
||||
}
|
||||
|
||||
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 nodes::node_shader_bevel_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bevel()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bevel_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBevel"_ustr, SH_NODE_BEVEL);
|
||||
ntype.ui_name = "Bevel";
|
||||
ntype.ui_description =
|
||||
"Generates normals with round corners.\nNote: only supported in Cycles, and may slow down "
|
||||
"renders";
|
||||
ntype.enum_name_legacy = "BEVEL";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,89 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "FN_multi_function_builder.hh"
|
||||
#include "NOD_multi_function.hh"
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_color_types.hh"
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_blackbody_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("Temperature"_ustr)
|
||||
.default_value(6500.0f)
|
||||
.min(800.0f)
|
||||
.max(12000.0f)
|
||||
.subtype(PROP_COLOR_TEMPERATURE);
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_blackbody(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const int size = CM_TABLE + 1;
|
||||
float *data = MEM_new_array_uninitialized<float>(size * 4, "blackbody texture");
|
||||
|
||||
IMB_colormanagement_blackbody_temperature_to_rgb_table(data, size, 800.0f, 12000.0f);
|
||||
|
||||
float layer;
|
||||
GPUNodeLink *ramp_texture = GPU_color_band(mat, size, data, &layer);
|
||||
|
||||
return GPU_stack_link(mat, node, "node_blackbody", in, out, ramp_texture, GPU_constant(&layer));
|
||||
}
|
||||
|
||||
static void sh_node_blackbody_build_multi_function(nodes::NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
static auto fn = mf::build::SI1_SO<float, ColorGeometry4f>("Blackbody", [](float temperature) {
|
||||
float color[4];
|
||||
IMB_colormanagement_blackbody_temperature_to_rgb(color, temperature);
|
||||
return ColorGeometry4f(color);
|
||||
});
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
NodeItem temperature = get_input_value("Temperature", NodeItem::Type::Float);
|
||||
|
||||
NodeItem res = create_node("blackbody", NodeItem::Type::Color3);
|
||||
res.set_input("temperature", temperature);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_blackbody_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_blackbody()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_blackbody_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeBlackbody"_ustr, SH_NODE_BLACKBODY);
|
||||
ntype.ui_name = "Blackbody";
|
||||
ntype.ui_description = "Convert a blackbody temperature to an RGB value";
|
||||
ntype.enum_name_legacy = "BLACKBODY";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_blackbody;
|
||||
ntype.build_multi_function = file_ns::sh_node_blackbody_build_multi_function;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,78 @@
|
||||
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_brightness_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Color"_ustr)
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.description("Color input on which correction will be applied");
|
||||
b.add_input<decl::Float>("Brightness"_ustr, "Bright"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(-100.0f)
|
||||
.max(100.0f)
|
||||
.description(
|
||||
"Brightness correction value.\n"
|
||||
"An additive-type factor by which to increase the overall brightness of the image. "
|
||||
"Use a negative number to darken an image, and a positive number to brighten it");
|
||||
b.add_input<decl::Float>("Contrast"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(-100.0f)
|
||||
.max(100.0f)
|
||||
.description(
|
||||
"Contrast correction value.\n"
|
||||
"A scaling type factor by which to make brighter pixels brighter, but keeping the "
|
||||
"darker pixels dark. "
|
||||
"Use a negative number to decrease contrast, and a positive number to increase it");
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_brightcontrast(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
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 nodes::node_shader_brightness_cc
|
||||
|
||||
void register_node_type_sh_brightcontrast()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_brightness_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBrightContrast"_ustr, SH_NODE_BRIGHTCONTRAST);
|
||||
ntype.ui_name = "Brightness/Contrast";
|
||||
ntype.ui_description = "Control the brightness and contrast of the input color";
|
||||
ntype.enum_name_legacy = "BRIGHTCONTRAST";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,84 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_diffuse_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_diffuse(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[2].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[2].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
|
||||
|
||||
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 nodes::node_shader_bsdf_diffuse_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_diffuse()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_diffuse_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfDiffuse"_ustr, SH_NODE_BSDF_DIFFUSE);
|
||||
ntype.ui_name = "Diffuse BSDF";
|
||||
ntype.ui_description = "Lambertian and Oren-Nayar diffuse reflection";
|
||||
ntype.enum_name_legacy = "BSDF_DIFFUSE";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_diffuse;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,134 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_glass_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.use_custom_socket_order();
|
||||
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
|
||||
b.add_default_layout();
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({1.0f, 1.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("IOR"_ustr).default_value(1.5f).min(0.0f).max(1000.0f);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
|
||||
PanelDeclarationBuilder &film = b.add_panel("Thin Film"_ustr).default_closed(true);
|
||||
film.add_input<decl::Float>("Thin Film Thickness"_ustr)
|
||||
.default_value(0.0)
|
||||
.min(0.0f)
|
||||
.max(100000.0f)
|
||||
.subtype(PROP_WAVELENGTH)
|
||||
.description("Thickness of the film in nanometers");
|
||||
film.add_input<decl::Float>("Thin Film IOR"_ustr)
|
||||
.default_value(1.33f)
|
||||
.min(1.0f)
|
||||
.max(1000.0f)
|
||||
.description("Index of refraction (IOR) of the thin film");
|
||||
}
|
||||
|
||||
static void node_shader_buts_glass(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "distribution", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_glass(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_GLOSSY_MULTI_GGX;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_glass(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[3].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[3].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT);
|
||||
|
||||
if (in[0].might_be_tinted()) {
|
||||
GPU_material_flag_set(
|
||||
mat, GPU_MATFLAG_REFLECTION_MAYBE_COLORED | GPU_MATFLAG_REFRACTION_MAYBE_COLORED);
|
||||
}
|
||||
|
||||
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
|
||||
|
||||
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);
|
||||
NodeItem thin_film_thickness = get_input_value("Thin Film Thickness", NodeItem::Type::Float);
|
||||
NodeItem thin_film_ior = get_input_value("Thin Film IOR", NodeItem::Type::Float);
|
||||
|
||||
return create_node("dielectric_bsdf",
|
||||
NodeItem::Type::BSDF,
|
||||
{{"normal", normal},
|
||||
{"tint", color},
|
||||
{"roughness", roughness},
|
||||
{"ior", ior},
|
||||
{"thinfilm_thickness", thin_film_thickness},
|
||||
{"thinfilm_ior", thin_film_ior},
|
||||
{"scatter_mode", val(std::string("RT"))}});
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_bsdf_glass_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_glass()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_glass_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfGlass"_ustr, SH_NODE_BSDF_GLASS);
|
||||
ntype.ui_name = "Glass BSDF";
|
||||
ntype.ui_description = "Glass-like shader mixing refraction and reflection at grazing angles";
|
||||
ntype.enum_name_legacy = "BSDF_GLASS";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_glass;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,129 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_glossy_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("Anisotropy"_ustr).default_value(0.0f).min(-1.0f).max(1.0f);
|
||||
b.add_input<decl::Float>("Rotation"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Vector>("Tangent"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_glossy(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "distribution", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_glossy(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_GLOSSY_MULTI_GGX;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_glossy(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[4].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[4].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY);
|
||||
|
||||
if (in[0].might_be_tinted()) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_REFLECTION_MAYBE_COLORED);
|
||||
}
|
||||
|
||||
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
|
||||
|
||||
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 nodes::node_shader_bsdf_glossy_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_glossy()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_glossy_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfAnisotropic"_ustr, SH_NODE_BSDF_GLOSSY);
|
||||
ntype.ui_name = "Glossy BSDF";
|
||||
ntype.ui_description =
|
||||
"Reflection with microfacet distribution, used for materials such as metal or mirrors";
|
||||
ntype.enum_name_legacy = "BSDF_GLOSSY";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_glossy;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
|
||||
/* Needed to preserve API compatibility with older versions which had separate
|
||||
* Glossy and Anisotropic nodes. */
|
||||
bke::node_register_alias(ntype, "ShaderNodeBsdfGlossy"_ustr);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,80 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_hair_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_input<decl::Float>("Offset"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(-M_PI_2)
|
||||
.max(M_PI_2)
|
||||
.subtype(PROP_ANGLE);
|
||||
b.add_input<decl::Float>("RoughnessU"_ustr)
|
||||
.default_value(0.1f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("RoughnessV"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Vector>("Tangent"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_hair(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "component", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_hair(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
|
||||
|
||||
return GPU_stack_link(mat, node, "node_bsdf_hair", in, out);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_bsdf_hair_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_hair()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_hair_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfHair"_ustr, SH_NODE_BSDF_HAIR);
|
||||
ntype.ui_name = "Hair BSDF";
|
||||
ntype.ui_description = "Reflection and transmission shaders optimized for hair rendering";
|
||||
ntype.enum_name_legacy = "BSDF_HAIR";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_cycles_shader_nodes_poll;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_hair;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_hair;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,276 @@
|
||||
/* SPDX-FileCopyrightText: 2018 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_hair_principled_cc {
|
||||
|
||||
/* Color, melanin and absorption coefficient default to approximately same brownish hair. */
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
auto set_parametrization_reflectance = [](bNode &node) {
|
||||
static_cast<NodeShaderHairPrincipled *>(node.storage)->parametrization =
|
||||
SHD_PRINCIPLED_HAIR_REFLECTANCE;
|
||||
};
|
||||
auto set_parametrization_pigment_concentration = [](bNode &node) {
|
||||
static_cast<NodeShaderHairPrincipled *>(node.storage)->parametrization =
|
||||
SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION;
|
||||
};
|
||||
auto set_parametrization_direct_absorption = [](bNode &node) {
|
||||
static_cast<NodeShaderHairPrincipled *>(node.storage)->parametrization =
|
||||
SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION;
|
||||
};
|
||||
auto set_model_huang = [](bNode &node) {
|
||||
static_cast<NodeShaderHairPrincipled *>(node.storage)->model = SHD_PRINCIPLED_HAIR_HUANG;
|
||||
};
|
||||
auto set_model_chiang = [](bNode &node) {
|
||||
static_cast<NodeShaderHairPrincipled *>(node.storage)->model = SHD_PRINCIPLED_HAIR_CHIANG;
|
||||
};
|
||||
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr)
|
||||
.default_value({0.017513f, 0.005763f, 0.002059f, 1.0f})
|
||||
.description("The RGB color of the strand. Only used in Direct Coloring")
|
||||
.make_available(set_parametrization_reflectance);
|
||||
b.add_input<decl::Float>("Melanin"_ustr)
|
||||
.default_value(0.8f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Hair pigment. Specify its absolute quantity between 0 and 1")
|
||||
.make_available(set_parametrization_pigment_concentration);
|
||||
b.add_input<decl::Float>("Melanin Redness"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Fraction of pheomelanin in melanin, gives yellowish to reddish color, as opposed to "
|
||||
"the brownish to black color of eumelanin")
|
||||
.make_available(set_parametrization_pigment_concentration);
|
||||
b.add_input<decl::Color>("Tint"_ustr)
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.description("Additional color used for dyeing the hair")
|
||||
.make_available(set_parametrization_pigment_concentration);
|
||||
b.add_input<decl::Vector>("Absorption Coefficient"_ustr)
|
||||
.default_value({0.245531f, 0.52f, 1.365f})
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description(
|
||||
"Specifies energy absorption per unit length as light passes through the hair. A higher "
|
||||
"value leads to a darker color")
|
||||
.make_available(set_parametrization_direct_absorption);
|
||||
b.add_input<decl::Float>("Aspect Ratio"_ustr)
|
||||
.default_value(0.85f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"The ratio of the minor axis to the major axis of an elliptical cross-section. "
|
||||
"Recommended values are 0.8~1 for Asian hair, 0.65~0.9 for Caucasian hair, 0.5~0.65 for "
|
||||
"African hair. The major axis is aligned with the curve normal, which is not supported "
|
||||
"in particle hair")
|
||||
.make_available(set_model_huang);
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(0.3f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Hair roughness. A low value leads to a metallic look");
|
||||
b.add_input<decl::Float>("Radial Roughness"_ustr)
|
||||
.default_value(0.3f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.make_available(set_model_chiang);
|
||||
b.add_input<decl::Float>("Coat"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Simulate a shiny coat by reducing the roughness to the given factor only for the first "
|
||||
"light bounce (diffuse). Range [0, 1] is equivalent to a reduction of [0%, 100%] of the "
|
||||
"original roughness")
|
||||
.make_available(set_model_chiang);
|
||||
b.add_input<decl::Float>("IOR"_ustr)
|
||||
.default_value(1.55f)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description(
|
||||
"Index of refraction determines how much the ray is bent. At 1.0 rays pass straight "
|
||||
"through "
|
||||
"like in a transparent material; higher values cause larger deflection in angle. "
|
||||
"Default "
|
||||
"value is 1.55 (the IOR of keratin)");
|
||||
b.add_input<decl::Float>("Offset"_ustr)
|
||||
.default_value(2.0f * float(M_PI) / 180.0f)
|
||||
.min(-M_PI_2)
|
||||
.max(M_PI_2)
|
||||
.subtype(PROP_ANGLE)
|
||||
.description(
|
||||
"The tilt angle of the cuticle scales (the outermost part of the hair). They are always "
|
||||
"tilted towards the hair root. The value is usually between 2 and 4 for human hair");
|
||||
b.add_input<decl::Float>("Random Color"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Vary the melanin concentration for each strand")
|
||||
.make_available(set_parametrization_pigment_concentration);
|
||||
b.add_input<decl::Float>("Random Roughness"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Vary roughness values for each strand");
|
||||
b.add_input<decl::Float>("Random"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_input<decl::Float>("Reflection"_ustr, "R lobe"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Optional factor for modulating the first light bounce off the hair surface. The color "
|
||||
"of this component is always white. Keep this 1.0 for physical correctness")
|
||||
.make_available(set_model_huang);
|
||||
b.add_input<decl::Float>("Transmission"_ustr, "TT lobe"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Optional factor for modulating the transmission component. Picks up the color of the "
|
||||
"pigment inside the hair. Keep this 1.0 for physical correctness")
|
||||
.make_available(set_model_huang);
|
||||
b.add_input<decl::Float>("Secondary Reflection"_ustr, "TRT lobe"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Optional factor for modulating the component which is transmitted into the hair, "
|
||||
"reflected off the backside of the hair and then transmitted out of the hair. This "
|
||||
"component is oriented approximately around the incoming direction, and picks up the "
|
||||
"color of the pigment inside the hair. Keep this 1.0 for physical correctness")
|
||||
.make_available(set_model_huang);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_principled_hair(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "model", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
layout.prop(ptr, "parametrization", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
/* Initialize custom properties. */
|
||||
static void node_shader_init_hair_principled(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeShaderHairPrincipled *data = MEM_new<NodeShaderHairPrincipled>(__func__);
|
||||
|
||||
data->model = SHD_PRINCIPLED_HAIR_CHIANG;
|
||||
data->parametrization = SHD_PRINCIPLED_HAIR_REFLECTANCE;
|
||||
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
/* Triggers (in)visibility of some sockets when changing the parametrization or the model. */
|
||||
static void node_shader_update_hair_principled(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
NodeShaderHairPrincipled *data = static_cast<NodeShaderHairPrincipled *>(node->storage);
|
||||
|
||||
int parametrization = data->parametrization;
|
||||
int model = data->model;
|
||||
|
||||
for (bNodeSocket &sock : node->inputs) {
|
||||
if (STREQ(sock.name, "Color")) {
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_REFLECTANCE);
|
||||
}
|
||||
else if (STREQ(sock.name, "Melanin")) {
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION);
|
||||
}
|
||||
else if (STREQ(sock.name, "Melanin Redness")) {
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION);
|
||||
}
|
||||
else if (STREQ(sock.name, "Tint")) {
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION);
|
||||
}
|
||||
else if (STREQ(sock.name, "Absorption Coefficient")) {
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION);
|
||||
}
|
||||
else if (STREQ(sock.name, "Random Color")) {
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION);
|
||||
}
|
||||
else if (STREQ(sock.name, "Radial Roughness")) {
|
||||
bke::node_set_socket_availability(*ntree, sock, model == SHD_PRINCIPLED_HAIR_CHIANG);
|
||||
}
|
||||
else if (STREQ(sock.name, "Coat")) {
|
||||
bke::node_set_socket_availability(*ntree, sock, model == SHD_PRINCIPLED_HAIR_CHIANG);
|
||||
}
|
||||
else if (STREQ(sock.name, "Aspect Ratio")) {
|
||||
bke::node_set_socket_availability(*ntree, sock, model == SHD_PRINCIPLED_HAIR_HUANG);
|
||||
}
|
||||
else if (STR_ELEM(sock.name, "Reflection", "Transmission", "Secondary Reflection")) {
|
||||
bke::node_set_socket_availability(*ntree, sock, model == SHD_PRINCIPLED_HAIR_HUANG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int node_shader_gpu_hair_principled(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
|
||||
|
||||
return GPU_stack_link(mat, node, "node_bsdf_hair_principled", in, out);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_bsdf_hair_principled_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_hair_principled()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_hair_principled_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfHairPrincipled"_ustr, SH_NODE_BSDF_HAIR_PRINCIPLED);
|
||||
ntype.ui_name = "Principled Hair BSDF";
|
||||
ntype.ui_description = "Physically-based, easy-to-use shader for rendering hair and fur";
|
||||
ntype.enum_name_legacy = "BSDF_HAIR_PRINCIPLED";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_cycles_shader_nodes_poll;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_principled_hair;
|
||||
ntype.default_width = bke::NodeWidth::_240;
|
||||
ntype.initfunc = file_ns::node_shader_init_hair_principled;
|
||||
ntype.updatefunc = file_ns::node_shader_update_hair_principled;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_hair_principled;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeShaderHairPrincipled", node_free_standard_storage, node_copy_standard_storage);
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,214 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_metallic_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.use_custom_socket_order();
|
||||
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
b.add_default_layout();
|
||||
|
||||
b.add_input<decl::Color>("Base Color"_ustr)
|
||||
.default_value({0.617f, 0.577f, 0.540f, 1.0f})
|
||||
.description("Color of the material")
|
||||
.make_available([](bNode &node) { node.custom2 = SHD_CONDUCTOR_F82; });
|
||||
b.add_input<decl::Color>("Edge Tint"_ustr)
|
||||
.default_value({0.695f, 0.726f, 0.770f, 1.0f})
|
||||
.description(
|
||||
"Tint reflection at near-grazing incidence to simulate complex index of refraction")
|
||||
.make_available([](bNode &node) { node.custom2 = SHD_CONDUCTOR_F82; });
|
||||
b.add_input<decl::Vector>("IOR"_ustr)
|
||||
.default_value({2.757f, 2.513f, 2.231f})
|
||||
.min(0.0f)
|
||||
.max(100.0f)
|
||||
.description("Real part of the conductor's refractive index, often called n")
|
||||
.make_available([](bNode &node) { node.custom2 = SHD_PHYSICAL_CONDUCTOR; });
|
||||
b.add_input<decl::Vector>("Extinction"_ustr)
|
||||
.default_value({3.867f, 3.404f, 3.009f})
|
||||
.min(0.0f)
|
||||
.max(100.0f)
|
||||
.description("Imaginary part of the conductor's refractive index, often called k")
|
||||
.make_available([](bNode &node) { node.custom2 = SHD_PHYSICAL_CONDUCTOR; });
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Microfacet roughness of the surface (0.0 is a perfect mirror reflection, 1.0 is "
|
||||
"completely rough)");
|
||||
b.add_input<decl::Float>("Anisotropy"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Amount of anisotropy for reflection. Higher values give elongated highlights along the "
|
||||
"tangent direction");
|
||||
b.add_input<decl::Float>("Rotation"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Rotates the direction of anisotropy, with 1.0 going full circle");
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Vector>("Tangent"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
|
||||
PanelDeclarationBuilder &film = b.add_panel("Thin Film"_ustr).default_closed(true);
|
||||
film.add_input<decl::Float>("Thin Film Thickness"_ustr)
|
||||
.default_value(0.0)
|
||||
.min(0.0f)
|
||||
.max(100000.0f)
|
||||
.subtype(PROP_WAVELENGTH)
|
||||
.description("Thickness of the film in nanometers");
|
||||
film.add_input<decl::Float>("Thin Film IOR"_ustr)
|
||||
.default_value(1.33f)
|
||||
.min(1.0f)
|
||||
.max(1000.0f)
|
||||
.description("Index of refraction (IOR) of the thin film");
|
||||
}
|
||||
|
||||
static void node_shader_buts_metallic(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "distribution", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
layout.prop(ptr, "fresnel_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_metallic(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_GLOSSY_MULTI_GGX;
|
||||
node->custom2 = SHD_CONDUCTOR_F82;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_metallic(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
|
||||
float use_complex_ior = (node->custom2 == SHD_PHYSICAL_CONDUCTOR) ? 1.0f : 0.0f;
|
||||
|
||||
if (!in[7].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[7].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY);
|
||||
if (use_complex_ior == 0.0f) {
|
||||
if (in[0].might_be_tinted() || in[1].might_be_tinted()) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_REFLECTION_MAYBE_COLORED);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (in[2].might_be_tinted() || in[3].might_be_tinted()) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_REFLECTION_MAYBE_COLORED);
|
||||
}
|
||||
}
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"node_bsdf_metallic",
|
||||
in,
|
||||
out,
|
||||
GPU_constant(&use_multi_scatter),
|
||||
GPU_constant(&use_complex_ior));
|
||||
}
|
||||
|
||||
static void node_shader_update_metallic(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
const bool is_physical = (node->custom2 == SHD_PHYSICAL_CONDUCTOR);
|
||||
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, *bke::node_find_socket(*node, SOCK_IN, "Base Color"_ustr), !is_physical);
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, *bke::node_find_socket(*node, SOCK_IN, "Edge Tint"_ustr), !is_physical);
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, *bke::node_find_socket(*node, SOCK_IN, "IOR"_ustr), is_physical);
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, *bke::node_find_socket(*node, SOCK_IN, "Extinction"_ustr), is_physical);
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
if (to_type_ != NodeItem::Type::BSDF) {
|
||||
return empty();
|
||||
}
|
||||
|
||||
NodeItem color = get_input_value("Base Color", NodeItem::Type::Color3);
|
||||
NodeItem edge_tint = get_input_value("Edge Tint", 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 thin_film_thickness = get_input_value("Thin Film Thickness", NodeItem::Type::Float);
|
||||
NodeItem thin_film_ior = get_input_value("Thin Film IOR", NodeItem::Type::Float);
|
||||
|
||||
NodeItem ior_out, extinction_out;
|
||||
if (node_->custom2 == SHD_PHYSICAL_CONDUCTOR) {
|
||||
ior_out = get_input_value("IOR", NodeItem::Type::Color3);
|
||||
extinction_out = get_input_value("Extinction", NodeItem::Type::Color3);
|
||||
}
|
||||
else {
|
||||
NodeItem artistic_ior = create_node("artistic_ior",
|
||||
NodeItem::Type::Multioutput,
|
||||
{{"reflectivity", color}, {"edge_color", edge_tint}});
|
||||
ior_out = artistic_ior.add_output("ior", NodeItem::Type::Color3);
|
||||
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},
|
||||
{"thinfilm_thickness", thin_film_thickness},
|
||||
{"thinfilm_ior", thin_film_ior}});
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_bsdf_metallic_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_metallic()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_metallic_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfMetallic"_ustr, SH_NODE_BSDF_METALLIC);
|
||||
ntype.ui_name = "Metallic BSDF";
|
||||
ntype.ui_description = "Metallic reflection with microfacet distribution, and metallic fresnel";
|
||||
ntype.enum_name_legacy = "BSDF_METALLIC";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_metallic;
|
||||
ntype.default_width = bke::NodeWidth::_240;
|
||||
ntype.initfunc = file_ns::node_shader_init_metallic;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_metallic;
|
||||
ntype.updatefunc = file_ns::node_shader_update_metallic;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,841 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "NOD_socket_usage_inference.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_principled_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
/**
|
||||
* Define static socket numbers to avoid string based lookups for GPU material creation as these
|
||||
* could run on animated materials.
|
||||
*/
|
||||
|
||||
b.use_custom_socket_order();
|
||||
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
|
||||
b.add_input<decl::Color>("Base Color"_ustr)
|
||||
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
|
||||
.description(
|
||||
"Color of the material used for diffuse, subsurface, metallic and transmission");
|
||||
#define SOCK_BASE_COLOR_ID 0
|
||||
b.add_input<decl::Float>("Metallic"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Blends between a dielectric and metallic material model. "
|
||||
"At 0.0 the material consists of a diffuse or transmissive base layer, "
|
||||
"with a specular reflection layer on top. A value of 1.0 gives a fully specular "
|
||||
"reflection tinted with the base color, without diffuse reflection or transmission");
|
||||
#define SOCK_METALLIC_ID 1
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Specifies microfacet roughness of the surface for specular reflection and transmission"
|
||||
" (0.0 is a perfect mirror reflection, 1.0 is completely rough)");
|
||||
#define SOCK_ROUGHNESS_ID 2
|
||||
b.add_input<decl::Float>("IOR"_ustr)
|
||||
.default_value(1.5f)
|
||||
.min(1.0f)
|
||||
.max(1000.0f)
|
||||
.description(
|
||||
"Index of Refraction (IOR) for specular reflection and transmission. "
|
||||
"For most materials, the IOR is between 1.0 (vacuum and air) and 4.0 (germanium). "
|
||||
"The default value of 1.5 is a good approximation for glass");
|
||||
#define SOCK_IOR_ID 3
|
||||
b.add_input<decl::Float>("Alpha"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Controls the transparency of the surface, with 1.0 fully opaque");
|
||||
#define SOCK_ALPHA_ID 4
|
||||
b.add_input<decl::Bool>("Thin Wall"_ustr)
|
||||
.default_value(false)
|
||||
.description(
|
||||
"A thin structure with the same material on both sides, useful for modeling thin "
|
||||
"objects such as window, papers and leaves");
|
||||
#define SOCK_THIN_WALL_ID 5
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
#define SOCK_NORMAL_ID 6
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
#define SOCK_WEIGHT_ID 7
|
||||
|
||||
/* Panel for Diffuse settings. */
|
||||
PanelDeclarationBuilder &diffuse = b.add_panel("Diffuse"_ustr).default_closed(true);
|
||||
diffuse.add_input<decl::Float>("Diffuse Roughness"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Specifies microfacet roughness of the diffuse base"
|
||||
" (0.0 is perfect lambertian reflection, 1.0 is completely rough)");
|
||||
#define SOCK_DIFFUSE_ROUGHNESS_ID 8
|
||||
|
||||
/* Panel for Subsurface scattering settings. */
|
||||
PanelDeclarationBuilder &sss = b.add_panel("Subsurface"_ustr).default_closed(true);
|
||||
sss.add_layout([](ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr) {
|
||||
layout.prop(ptr, "subsurface_method", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
/* Not used by Thin Wall. Infer the value from Subsurface Radius. */
|
||||
const bNode &node = *ptr->data_as<bNode>();
|
||||
const bNodeSocket &radius_socket = *bke::node_find_socket(
|
||||
node, SOCK_IN, "Subsurface Radius"_ustr);
|
||||
if (radius_socket.is_inactive()) {
|
||||
layout.active_set(false);
|
||||
}
|
||||
});
|
||||
sss.add_input<decl::Float>("Subsurface Weight"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("Weight")
|
||||
.description(
|
||||
"Blend between diffuse surface and subsurface scattering. "
|
||||
"Typically should be zero or one (either fully diffuse or subsurface)");
|
||||
#define SOCK_SUBSURFACE_WEIGHT_ID 9
|
||||
sss.add_input<decl::Vector>("Subsurface Radius"_ustr)
|
||||
.default_value({1.0f, 0.2f, 0.1f})
|
||||
.min(0.0f)
|
||||
.max(100.0f)
|
||||
.short_label("Radius")
|
||||
.description("Scattering radius per color channel (RGB), multiplied with Scale")
|
||||
.usage_by_bool("Thin Wall"_ustr, false);
|
||||
#define SOCK_SUBSURFACE_RADIUS_ID 10
|
||||
sss.add_input<decl::Float>("Subsurface Scale"_ustr)
|
||||
.default_value(0.005f)
|
||||
.min(0.0f)
|
||||
.max(10.0f)
|
||||
.subtype(PROP_DISTANCE)
|
||||
.short_label("Scale")
|
||||
.description("Scale factor of the subsurface scattering radius")
|
||||
.usage_by_bool("Thin Wall"_ustr, false);
|
||||
#define SOCK_SUBSURFACE_SCALE_ID 11
|
||||
sss.add_input<decl::Float>("Subsurface IOR"_ustr)
|
||||
.default_value(1.4f)
|
||||
.min(1.01f)
|
||||
.max(3.8f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("IOR")
|
||||
.description("Index of Refraction (IOR) used for rays that enter the subsurface component")
|
||||
.make_available([](bNode &node) { node.custom2 = SHD_SUBSURFACE_RANDOM_WALK_SKIN; });
|
||||
#define SOCK_SUBSURFACE_IOR_ID 12
|
||||
sss.add_input<decl::Float>("Subsurface Anisotropy"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(-1.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("Anisotropy")
|
||||
.description(
|
||||
"Directionality of volume scattering within the subsurface medium. "
|
||||
"Zero scatters uniformly in all directions, positive values scatter more in the forward "
|
||||
"direction, and negative values scatter more backwards. "
|
||||
"For example, skin has been measured to have an anisotropy of 0.8")
|
||||
.make_available([](bNode &node) { node.custom2 = SHD_SUBSURFACE_RANDOM_WALK; })
|
||||
.usage_inference(
|
||||
[](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional<bool> {
|
||||
return params.bool_input_may_be("Thin Wall"_ustr, true) ||
|
||||
params.node.custom2 != SHD_SUBSURFACE_BURLEY;
|
||||
});
|
||||
#define SOCK_SUBSURFACE_ANISOTROPY_ID 13
|
||||
|
||||
/* Panel for Specular settings. */
|
||||
PanelDeclarationBuilder &spec = b.add_panel("Specular"_ustr).default_closed(true);
|
||||
spec.add_layout([](ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr) {
|
||||
layout.prop(ptr, "distribution", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
});
|
||||
spec.add_input<decl::Float>("Specular IOR Level"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("IOR Level")
|
||||
.description(
|
||||
"Adjustment to the Index of Refraction (IOR) to increase or decrease specular intensity "
|
||||
"(0.5 means no adjustment, 0 removes all reflections, 1 doubles them at normal "
|
||||
"incidence)");
|
||||
#define SOCK_SPECULAR_ID 14
|
||||
spec.add_input<decl::Color>("Specular Tint"_ustr)
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.short_label("Tint")
|
||||
.description(
|
||||
"Tint dielectric reflection at normal incidence for artistic control, and metallic "
|
||||
"reflection at near-grazing incidence to simulate complex index of refraction")
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE);
|
||||
#define SOCK_SPECULAR_TINT_ID 15
|
||||
spec.add_input<decl::Float>("Anisotropic"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("Anisotropy")
|
||||
.description(
|
||||
"Amount of anisotropy for specular reflection. "
|
||||
"Higher values give elongated highlights along the tangent direction");
|
||||
#define SOCK_ANISOTROPIC_ID 16
|
||||
spec.add_input<decl::Float>("Anisotropic Rotation"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Rotates the direction of anisotropy, with 1.0 going full circle");
|
||||
#define SOCK_ANISOTROPIC_ROTATION_ID 17
|
||||
spec.add_input<decl::Vector>("Tangent"_ustr)
|
||||
.hide_value()
|
||||
.description("Controls the tangent direction for anisotropy");
|
||||
#define SOCK_TANGENT_ID 18
|
||||
|
||||
/* Panel for Transmission settings. */
|
||||
PanelDeclarationBuilder &transmission = b.add_panel("Transmission"_ustr).default_closed(true);
|
||||
transmission.add_input<decl::Float>("Transmission Weight"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("Weight")
|
||||
.description("Blend between transmission and other base layer components");
|
||||
#define SOCK_TRANSMISSION_WEIGHT_ID 19
|
||||
|
||||
/* Panel for Coat settings. */
|
||||
PanelDeclarationBuilder &coat = b.add_panel("Coat"_ustr).default_closed(true);
|
||||
coat.add_input<decl::Float>("Coat Weight"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("Weight")
|
||||
.description(
|
||||
"Controls the intensity of the coat layer, both the reflection and the tinting. "
|
||||
"Typically should be zero or one for physically-based materials");
|
||||
#define SOCK_COAT_WEIGHT_ID 20
|
||||
coat.add_input<decl::Float>("Coat Roughness"_ustr)
|
||||
.default_value(0.03f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("Roughness")
|
||||
.description("The roughness of the coat layer");
|
||||
#define SOCK_COAT_ROUGHNESS_ID 21
|
||||
coat.add_input<decl::Float>("Coat IOR"_ustr)
|
||||
.default_value(1.5f)
|
||||
.min(1.0f)
|
||||
.max(4.0f)
|
||||
.short_label("IOR")
|
||||
.description(
|
||||
"The Index of Refraction (IOR) of the coat layer "
|
||||
"(affects its reflectivity as well as the falloff of coat tinting)");
|
||||
#define SOCK_COAT_IOR_ID 22
|
||||
coat.add_input<decl::Color>("Coat Tint"_ustr)
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.short_label("Tint")
|
||||
.description(
|
||||
"Adds a colored tint to the coat layer by modeling absorption in the layer. "
|
||||
"Saturation increases at shallower angles, as the light travels farther "
|
||||
"through the medium (depending on the Coat IOR)")
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE);
|
||||
#define SOCK_COAT_TINT_ID 23
|
||||
coat.add_input<decl::Vector>("Coat Normal"_ustr).short_label("Normal").hide_value();
|
||||
#define SOCK_COAT_NORMAL_ID 24
|
||||
|
||||
/* Panel for Sheen settings. */
|
||||
PanelDeclarationBuilder &sheen = b.add_panel("Sheen"_ustr).default_closed(true);
|
||||
sheen.add_input<decl::Float>("Sheen Weight"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("Weight")
|
||||
.description(
|
||||
"Intensity of the sheen layer, which simulates very small fibers on the surface");
|
||||
#define SOCK_SHEEN_WEIGHT_ID 25
|
||||
sheen.add_input<decl::Float>("Sheen Roughness"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.short_label("Roughness")
|
||||
.description(
|
||||
"Roughness of the sheen layer. Low and high roughness values produce fuzzy or dusty "
|
||||
"appearance, respectively");
|
||||
#define SOCK_SHEEN_ROUGHNESS_ID 26
|
||||
sheen.add_input<decl::Color>("Sheen Tint"_ustr)
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE)
|
||||
.short_label("Tint")
|
||||
.description("Color of the sheen reflection");
|
||||
#define SOCK_SHEEN_TINT_ID 27
|
||||
|
||||
/* Panel for Emission settings. */
|
||||
PanelDeclarationBuilder &emis = b.add_panel("Emission"_ustr).default_closed(true);
|
||||
emis.add_input<decl::Color>("Emission Color"_ustr)
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.short_label("Color")
|
||||
.description("Color of light emission from the surface");
|
||||
#define SOCK_EMISSION_ID 28
|
||||
emis.add_input<decl::Float>("Emission Strength"_ustr)
|
||||
.default_value(0.0)
|
||||
.min(0.0f)
|
||||
.max(1000000.0f)
|
||||
.short_label("Strength")
|
||||
.description(
|
||||
"Strength of the emitted light. A value of 1.0 ensures "
|
||||
"that the object in the image has the exact same color as the Emission Color")
|
||||
.translation_context(BLT_I18NCONTEXT_AMOUNT);
|
||||
#define SOCK_EMISSION_STRENGTH_ID 29
|
||||
|
||||
/* Panel for Thin Film settings. */
|
||||
PanelDeclarationBuilder &film = b.add_panel("Thin Film"_ustr).default_closed(true);
|
||||
film.add_input<decl::Float>("Thin Film Thickness"_ustr)
|
||||
.default_value(0.0)
|
||||
.min(0.0f)
|
||||
.max(100000.0f)
|
||||
.subtype(PROP_WAVELENGTH)
|
||||
.description("Thickness of the film in nanometers");
|
||||
#define SOCK_THIN_FILM_THICKNESS_ID 30
|
||||
film.add_input<decl::Float>("Thin Film IOR"_ustr)
|
||||
.default_value(1.33f)
|
||||
.min(1.0f)
|
||||
.max(1000.0f)
|
||||
.description("Index of refraction (IOR) of the thin film");
|
||||
#define SOCK_THIN_FILM_IOR_ID 31
|
||||
}
|
||||
|
||||
static void node_shader_init_principled(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_GLOSSY_MULTI_GGX;
|
||||
node->custom2 = SHD_SUBSURFACE_RANDOM_WALK;
|
||||
}
|
||||
|
||||
static bool might_have_tinted_specular(const GPUNodeStack &base_color,
|
||||
const GPUNodeStack &metallic,
|
||||
const GPUNodeStack &specular_tint)
|
||||
{
|
||||
if (metallic.socket_not_zero()) {
|
||||
/* Metals might have colored specular. */
|
||||
return base_color.might_be_tinted() || specular_tint.might_be_tinted();
|
||||
}
|
||||
/* Dielectrics get colored if tint is used. */
|
||||
return specular_tint.might_be_tinted();
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
/* Normals */
|
||||
if (!in[SOCK_NORMAL_ID].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[SOCK_NORMAL_ID].link);
|
||||
}
|
||||
|
||||
/* Coat Normals */
|
||||
if (!in[SOCK_COAT_NORMAL_ID].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[SOCK_COAT_NORMAL_ID].link);
|
||||
}
|
||||
|
||||
#if 0 /* Not used at the moment. */
|
||||
/* Tangents */
|
||||
if (!in[SOCK_TANGENT_ID].link) {
|
||||
GPUNodeLink *orco = GPU_attribute(CD_ORCO, "");
|
||||
GPU_link(mat, "tangent_orco_z", orco, &in[SOCK_TANGENT_ID].link);
|
||||
GPU_link(mat, "node_tangent", in[SOCK_TANGENT_ID].link, &in[SOCK_TANGENT_ID].link);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool use_diffuse = in[SOCK_SHEEN_WEIGHT_ID].socket_not_zero() ||
|
||||
(in[SOCK_METALLIC_ID].socket_not_one() &&
|
||||
in[SOCK_TRANSMISSION_WEIGHT_ID].socket_not_one());
|
||||
bool use_subsurf = in[SOCK_SUBSURFACE_WEIGHT_ID].socket_not_zero() && use_diffuse;
|
||||
bool use_refract = in[SOCK_METALLIC_ID].socket_not_one() &&
|
||||
in[SOCK_TRANSMISSION_WEIGHT_ID].socket_not_zero();
|
||||
bool use_transparency = in[SOCK_ALPHA_ID].socket_not_one();
|
||||
bool use_coat = in[SOCK_COAT_WEIGHT_ID].socket_not_zero();
|
||||
bool use_translucent = use_subsurf && in[SOCK_THIN_WALL_ID].socket_not_zero();
|
||||
|
||||
/* EEVEE's subsurface closure still uses the legacy approximation where the radius is scaled by
|
||||
* 1/(4pi) (e.g., SHD_SUBSURFACE_RANDOM_WALK_LEGACY) */
|
||||
GPUNodeLink *subsurface_random_walk_radius_scale = nullptr;
|
||||
float random_walk_scale = 1.0f;
|
||||
if (node->custom2 == SHD_SUBSURFACE_RANDOM_WALK) {
|
||||
random_walk_scale = 4.0f * M_PI;
|
||||
}
|
||||
subsurface_random_walk_radius_scale = GPU_constant(&random_walk_scale);
|
||||
|
||||
eGPUMaterialFlag flag = GPU_MATFLAG_GLOSSY;
|
||||
if (use_diffuse) {
|
||||
flag |= GPU_MATFLAG_DIFFUSE;
|
||||
}
|
||||
if (use_refract) {
|
||||
flag |= GPU_MATFLAG_REFRACT;
|
||||
}
|
||||
if (use_subsurf && in[SOCK_THIN_WALL_ID].socket_not_one()) {
|
||||
flag |= GPU_MATFLAG_SUBSURFACE;
|
||||
}
|
||||
if (use_transparency) {
|
||||
flag |= GPU_MATFLAG_TRANSPARENT;
|
||||
}
|
||||
if (use_coat) {
|
||||
flag |= GPU_MATFLAG_COAT;
|
||||
}
|
||||
if (use_translucent) {
|
||||
flag |= GPU_MATFLAG_TRANSLUCENT;
|
||||
}
|
||||
|
||||
const bool refraction_might_be_tinted = use_refract && in[SOCK_BASE_COLOR_ID].might_be_tinted();
|
||||
|
||||
if (might_have_tinted_specular(
|
||||
in[SOCK_BASE_COLOR_ID], in[SOCK_METALLIC_ID], in[SOCK_SPECULAR_TINT_ID]) ||
|
||||
/* Multiscatter GGX can tint the reflection lobe. See `bsdf_lut`. */
|
||||
(refraction_might_be_tinted && node->custom1 == SHD_GLOSSY_MULTI_GGX))
|
||||
{
|
||||
flag |= GPU_MATFLAG_REFLECTION_MAYBE_COLORED;
|
||||
}
|
||||
if (refraction_might_be_tinted) {
|
||||
flag |= GPU_MATFLAG_REFRACTION_MAYBE_COLORED;
|
||||
}
|
||||
if (use_coat && in[SOCK_COAT_TINT_ID].might_be_tinted()) {
|
||||
/* Coat tints lower layers. */
|
||||
flag |= GPU_MATFLAG_REFLECTION_MAYBE_COLORED;
|
||||
if (use_refract) {
|
||||
flag |= GPU_MATFLAG_REFRACTION_MAYBE_COLORED;
|
||||
}
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, flag);
|
||||
|
||||
/* Make constant link for the cases we optimize. This allows the driver to constant fold.
|
||||
* Note that doing so specialize the final tree topology, and thus the shader becomes less
|
||||
* reusable. So to be used with care.
|
||||
* Also note that we do note override existing links. This is because it would leak the current
|
||||
* nodes otherwise. */
|
||||
const float zero = 0.0f;
|
||||
if (!use_coat && in[SOCK_COAT_WEIGHT_ID].link == nullptr) {
|
||||
in[SOCK_COAT_WEIGHT_ID].link = GPU_constant(&zero);
|
||||
}
|
||||
if (!use_subsurf && in[SOCK_SUBSURFACE_WEIGHT_ID].link == nullptr) {
|
||||
in[SOCK_SUBSURFACE_WEIGHT_ID].link = GPU_constant(&zero);
|
||||
}
|
||||
if (!use_refract && in[SOCK_TRANSMISSION_WEIGHT_ID].link == nullptr) {
|
||||
in[SOCK_TRANSMISSION_WEIGHT_ID].link = GPU_constant(&zero);
|
||||
}
|
||||
|
||||
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"node_bsdf_principled",
|
||||
in,
|
||||
out,
|
||||
GPU_constant(&use_multi_scatter),
|
||||
subsurface_random_walk_radius_scale);
|
||||
}
|
||||
|
||||
static void node_shader_update_principled(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
const int sss_method = node->custom2;
|
||||
|
||||
bke::node_set_socket_availability(*ntree,
|
||||
*bke::node_find_socket(*node, SOCK_IN, "Subsurface IOR"_ustr),
|
||||
sss_method == SHD_SUBSURFACE_RANDOM_WALK_SKIN);
|
||||
}
|
||||
|
||||
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)},
|
||||
{"diffuse_roughness", get_input_value("Diffuse Roughness", NodeItem::Type::Float)},
|
||||
{"subsurface", get_input_value("Subsurface Weight", NodeItem::Type::Float)},
|
||||
{"subsurface_scale", get_input_value("Subsurface Scale", NodeItem::Type::Float)},
|
||||
# if MATERIALX_MAJOR_VERSION <= 1 && MATERIALX_MINOR_VERSION <= 38
|
||||
{"subsurface_radius", get_input_value("Subsurface Radius", NodeItem::Type::Vector3)},
|
||||
# else
|
||||
{"subsurface_radius", get_input_value("Subsurface Radius", NodeItem::Type::Color3)},
|
||||
# endif
|
||||
//{"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)},
|
||||
{"thin_film_thickness", get_input_value("Thin Film Thickness", NodeItem::Type::Float)},
|
||||
{"thin_film_IOR", get_input_value("Thin Film IOR", 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)},
|
||||
{"geometry_thin_walled", get_input_value("Thin Wall", NodeItem::Type::Boolean)},
|
||||
};
|
||||
};
|
||||
|
||||
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 diffuse_roughness = in["diffuse_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 thin_film_thickness = in["thin_film_thickness"];
|
||||
NodeItem thin_film_ior = in["thin_film_IOR"];
|
||||
NodeItem n_thin_film_bsdf = create_node(
|
||||
"thin_film_bsdf",
|
||||
NodeItem::Type::BSDF,
|
||||
{{"thickness", thin_film_thickness}, {"ior", thin_film_ior}});
|
||||
|
||||
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;
|
||||
|
||||
/* Subsurface (non-thin-walled) */
|
||||
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}});
|
||||
|
||||
/* Subsurface (thin-walled) - a 50/50 mix between reflection and transmission sub-graphs */
|
||||
NodeItem n_subsurface_thin_walled_reflection_bsdf = create_node(
|
||||
"oren_nayar_diffuse_bsdf",
|
||||
NodeItem::Type::BSDF,
|
||||
{{"color", n_coat_affected_subsurface_color},
|
||||
{"roughness", diffuse_roughness},
|
||||
{"weight", val(1.0f)},
|
||||
{"normal", normal}});
|
||||
NodeItem n_subsurface_thin_walled_transmission_bsdf = create_node(
|
||||
"translucent_bsdf",
|
||||
NodeItem::Type::BSDF,
|
||||
{{"color", n_coat_affected_subsurface_color}, {"normal", normal}});
|
||||
|
||||
NodeItem n_subsurface_thin_walled_brdf_factor = val(1.0f) - in["subsurface_anisotropy"];
|
||||
NodeItem n_one_plus_subsurface_scatter_anisotropy = val(1.0f) + in["subsurface_anisotropy"];
|
||||
|
||||
NodeItem n_subsurface_thin_walled_reflection = n_subsurface_thin_walled_reflection_bsdf *
|
||||
n_subsurface_thin_walled_brdf_factor;
|
||||
NodeItem n_subsurface_thin_walled_transmission = n_subsurface_thin_walled_transmission_bsdf *
|
||||
n_one_plus_subsurface_scatter_anisotropy;
|
||||
NodeItem n_subsurface_thin_walled_mix = val(0.5f).mix(n_subsurface_thin_walled_reflection,
|
||||
n_subsurface_thin_walled_transmission);
|
||||
|
||||
/* Select the correct subsurface graph based on the thin-walled option. */
|
||||
NodeItem n_subsurface_selector_mix = in["geometry_thin_walled"]
|
||||
.convert(NodeItem::Type::Float)
|
||||
.mix(n_subsurface_bsdf,
|
||||
n_subsurface_thin_walled_mix);
|
||||
|
||||
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", diffuse_roughness},
|
||||
{"weight", val(1.0f)},
|
||||
{"normal", normal}});
|
||||
|
||||
NodeItem n_subsurface_mix = in["subsurface"].mix(n_diffuse_bsdf, n_subsurface_selector_mix);
|
||||
|
||||
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 base_color = in["base_color"];
|
||||
|
||||
NodeItem anisotropy = in["anisotropic"];
|
||||
NodeItem tangent = in["tangent"];
|
||||
if (anisotropy) {
|
||||
/* Anisotropy scaled down to approximately match the principled BSDF. */
|
||||
anisotropy = anisotropy * val(0.7f);
|
||||
|
||||
/* Rotation is offset by 90 degrees and inverted to approximately align visually with
|
||||
* principled BSDF direction. */
|
||||
NodeItem rotation = -((in["anisotropic_rotation"] * val(360.0f)) + val(90.0f));
|
||||
|
||||
/* Only create a normal node locally if we need to use it to rotate the tangent vector.
|
||||
* we don't actually pass this to the exported material. */
|
||||
NodeItem normal = in["normal"];
|
||||
if (!normal) {
|
||||
const std::string world = "world";
|
||||
normal =
|
||||
create_node("normal", NodeItem::Type::Vector3, {{"space", val(world)}}).normalize();
|
||||
}
|
||||
|
||||
if (!tangent) {
|
||||
const std::string world = "world";
|
||||
tangent =
|
||||
create_node("tangent", NodeItem::Type::Vector3, {{"space", val(world)}}).normalize();
|
||||
}
|
||||
|
||||
NodeItem n_tangent_rotate_normalize = tangent.rotate(rotation, normal).normalize();
|
||||
tangent = anisotropy.if_else(
|
||||
NodeItem::CompareOp::Greater, val(0.0f), n_tangent_rotate_normalize, tangent);
|
||||
}
|
||||
|
||||
/* Enable OpenPBR thin film only if thickness > 0. */
|
||||
NodeItem thin_film_thickness = in["thin_film_thickness"] * val(0.001f);
|
||||
NodeItem thin_film_weight = thin_film_thickness.if_else(
|
||||
NodeItem::CompareOp::Greater, val(0.0f), val(1.0f), val(0.0f));
|
||||
|
||||
/* "specular" here is "Specular IOR Level" in principled BSDF
|
||||
* 0 = no specular
|
||||
* 0.5 = full weight specular
|
||||
* 1 = double specular weight */
|
||||
NodeItem specular_weight = in["specular"] * val(2.0f);
|
||||
|
||||
res = create_node("open_pbr_surface",
|
||||
NodeItem::Type::SurfaceShader,
|
||||
{{"base_weight", val(1.0f)},
|
||||
{"base_color", base_color},
|
||||
{"base_diffuse_roughness", in["diffuse_roughness"]},
|
||||
{"base_metalness", in["metallic"]},
|
||||
{"specular_weight", specular_weight},
|
||||
{"specular_color", in["specular_tint"]},
|
||||
{"specular_roughness", in["roughness"]},
|
||||
{"specular_ior", in["ior"]},
|
||||
{"specular_roughness_anisotropy", anisotropy},
|
||||
{"transmission_weight", in["transmission"]},
|
||||
{"transmission_color", base_color},
|
||||
{"subsurface_weight", in["subsurface"]},
|
||||
{"subsurface_color", base_color},
|
||||
{"subsurface_radius_scale", in["subsurface_radius"]},
|
||||
{"subsurface_radius", in["subsurface_scale"]},
|
||||
{"subsurface_scatter_anisotropy", in["subsurface_anisotropy"]},
|
||||
{"fuzz_weight", in["sheen"]},
|
||||
{"fuzz_color", in["sheen_tint"]},
|
||||
{"fuzz_roughness", in["sheen_roughness"]},
|
||||
{"coat_weight", in["coat"]},
|
||||
{"coat_color", in["coat_tint"]},
|
||||
{"coat_roughness", in["coat_roughness"]},
|
||||
{"coat_ior", in["coat_ior"]},
|
||||
/* Principled BSDF does not support anisotropy for the coat
|
||||
* {"coat_roughness_anisotropy", anisotropic},
|
||||
* {"geometry_coat_tangent", tangent}, */
|
||||
{"emission_luminance", in["emission"]},
|
||||
{"emission_color", in["emission_color"]},
|
||||
{"thin_film_weight", thin_film_weight},
|
||||
{"thin_film_thickness", thin_film_thickness},
|
||||
{"thin_film_ior", in["thin_film_IOR"]},
|
||||
{"geometry_normal", in["normal"]},
|
||||
{"geometry_coat_normal", in["coat_normal"]},
|
||||
{"geometry_tangent", tangent},
|
||||
{"geometry_opacity", in["alpha"]},
|
||||
{"geometry_thin_walled", in["geometry_thin_walled"]}});
|
||||
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 nodes::node_shader_bsdf_principled_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_principled()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_principled_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfPrincipled"_ustr, SH_NODE_BSDF_PRINCIPLED);
|
||||
ntype.ui_name = "Principled BSDF";
|
||||
ntype.ui_description =
|
||||
"Physically-based, easy-to-use shader for rendering surface materials, based on the OpenPBR "
|
||||
"model";
|
||||
ntype.enum_name_legacy = "BSDF_PRINCIPLED";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.default_width = bke::NodeWidth::_240;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,82 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_ray_portal_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({1.0f, 1.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Vector>("Position"_ustr).hide_value();
|
||||
b.add_input<decl::Vector>("Direction"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_ray_portal(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (in[0].link || !is_zero_v3(in[0].vec)) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_TRANSPARENT);
|
||||
}
|
||||
return GPU_stack_link(mat, node, "node_bsdf_ray_portal", 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 nodes::node_shader_bsdf_ray_portal_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_ray_portal()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_ray_portal_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfRayPortal"_ustr, SH_NODE_BSDF_RAY_PORTAL);
|
||||
ntype.ui_name = "Ray Portal BSDF";
|
||||
ntype.ui_description = "Continue tracing from an arbitrary new position and in a new direction";
|
||||
ntype.enum_name_legacy = "BSDF_RAY_PORTAL";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.add_ui_poll = object_cycles_shader_nodes_poll;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_ray_portal;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,101 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_refraction_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({1.0f, 1.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("IOR"_ustr).default_value(1.45f).min(0.0f).max(1000.0f);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_init_refraction(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_GLOSSY_BECKMANN;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_refraction(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[3].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[3].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_REFRACT);
|
||||
if (in[0].might_be_tinted()) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_REFRACTION_MAYBE_COLORED);
|
||||
}
|
||||
|
||||
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 nodes::node_shader_bsdf_refraction_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_refraction()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_refraction_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfRefraction"_ustr, SH_NODE_BSDF_REFRACTION);
|
||||
ntype.ui_name = "Refraction BSDF";
|
||||
ntype.ui_description =
|
||||
"Glossy refraction with sharp or microfacet distribution, typically used for materials that "
|
||||
"transmit light";
|
||||
ntype.enum_name_legacy = "BSDF_REFRACTION";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,112 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_sheen_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_sheen(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "distribution", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_sheen(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_SHEEN_MICROFIBER;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_sheen(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[2].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[2].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
|
||||
|
||||
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);
|
||||
# if !(MATERIALX_MAJOR_VERSION <= 1 && MATERIALX_MINOR_VERSION <= 38)
|
||||
NodeItem mode = node_->custom1 == SHD_SHEEN_MICROFIBER ? val(std::string("zeltner")) :
|
||||
val(std::string("conty_kulla"));
|
||||
# endif
|
||||
|
||||
return create_node("sheen_bsdf",
|
||||
NodeItem::Type::BSDF,
|
||||
{{"color", color},
|
||||
{"roughness", roughness},
|
||||
{"normal", normal}
|
||||
# if !(MATERIALX_MAJOR_VERSION <= 1 && MATERIALX_MINOR_VERSION <= 38)
|
||||
,
|
||||
{"mode", mode}});
|
||||
# else
|
||||
});
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_bsdf_sheen_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_sheen()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_sheen_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfSheen"_ustr, SH_NODE_BSDF_SHEEN);
|
||||
ntype.ui_name = "Sheen BSDF";
|
||||
ntype.ui_description =
|
||||
"Reflection for materials such as cloth.\nTypically mixed with other shaders (such as a "
|
||||
"Diffuse Shader) and is not particularly useful on its own";
|
||||
ntype.enum_name_legacy = "BSDF_SHEEN";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.add_ui_poll = object_cycles_shader_nodes_poll;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_toon_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_input<decl::Float>("Size"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("Smooth"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_toon(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "component", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_toon(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[3].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[3].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
|
||||
|
||||
return GPU_stack_link(mat, node, "node_bsdf_toon", in, out);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_bsdf_toon_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_toon()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_toon_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfToon"_ustr, SH_NODE_BSDF_TOON);
|
||||
ntype.ui_name = "Toon BSDF";
|
||||
ntype.ui_description = "Diffuse and Glossy shaders with cartoon light effects";
|
||||
ntype.enum_name_legacy = "BSDF_TOON";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_cycles_shader_nodes_poll;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_toon;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_toon;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,76 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_translucent_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_translucent(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[1].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[1].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_TRANSLUCENT);
|
||||
|
||||
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 nodes::node_shader_bsdf_translucent_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_translucent()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_translucent_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfTranslucent"_ustr, SH_NODE_BSDF_TRANSLUCENT);
|
||||
ntype.ui_name = "Translucent BSDF";
|
||||
ntype.ui_description = "Lambertian diffuse transmission";
|
||||
ntype.enum_name_legacy = "BSDF_TRANSLUCENT";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,86 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_bsdf_transparent_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({1.0f, 1.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_transparent(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (in[0].link || !is_zero_v3(in[0].vec)) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_TRANSPARENT);
|
||||
|
||||
if (in[0].might_be_tinted()) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_TRANSPARENT_MAYBE_COLORED);
|
||||
}
|
||||
}
|
||||
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 nodes::node_shader_bsdf_transparent_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_transparent()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bsdf_transparent_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBsdfTransparent"_ustr, SH_NODE_BSDF_TRANSPARENT);
|
||||
ntype.ui_name = "Transparent BSDF";
|
||||
ntype.ui_description =
|
||||
"Transparency without refraction, passing straight through the surface as if there were no "
|
||||
"geometry";
|
||||
ntype.enum_name_legacy = "BSDF_TRANSPARENT";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_transparent;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,156 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* **************** BUMP ******************** */
|
||||
|
||||
namespace nodes::node_shader_bump_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
#define SOCK_STRENGTH_ID 0
|
||||
b.add_input<decl::Float>("Strength"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Strength of the bump mapping effect, interpolating between "
|
||||
"no bump mapping and full bump mapping")
|
||||
.translation_context(BLT_I18NCONTEXT_AMOUNT);
|
||||
#define SOCK_DISTANCE_ID 1
|
||||
b.add_input<decl::Float>("Distance"_ustr)
|
||||
.default_value(0.001f)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description(
|
||||
"Multiplier for the height value to control the overall distance for bump mapping");
|
||||
#define SOCK_FILTER_WIDTH_ID 2
|
||||
b.add_input<decl::Float>("Filter Width"_ustr)
|
||||
.default_value(0.1f)
|
||||
.min(0.001)
|
||||
.max(10.0f)
|
||||
.subtype(PROP_PIXEL)
|
||||
.description(
|
||||
"Filter width in pixels, used to compute the bump mapping direction. For most textures "
|
||||
"the default value of 0.1 enables subpixel filtering for stable results. For stepwise "
|
||||
"textures a larger filter width can be used to get a bevel like effect on the edges");
|
||||
#define SOCK_HEIGHT_ID 3
|
||||
b.add_input<decl::Float>("Height"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.hide_value()
|
||||
.description("Height above surface. Connect the height map texture to this input");
|
||||
#define SOCK_NORMAL_ID 4
|
||||
b.add_input<decl::Vector>("Normal"_ustr).min(-1.0f).max(1.0f).hide_value();
|
||||
b.add_output<decl::Vector>("Normal"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_bump(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "invert", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
static int gpu_shader_bump(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
/* If there is no Height input, the node becomes a no-op. */
|
||||
if (!in[SOCK_HEIGHT_ID].link) {
|
||||
if (!in[SOCK_NORMAL_ID].link) {
|
||||
return GPU_link(mat, "world_normals_get", &out[0].link);
|
||||
}
|
||||
/* Actually running the bump code would normalize, but Cycles handles it as total no-op. */
|
||||
return GPU_link(mat, "vector_copy", in[SOCK_NORMAL_ID].link, &out[0].link);
|
||||
}
|
||||
|
||||
if (!in[SOCK_NORMAL_ID].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[SOCK_NORMAL_ID].link);
|
||||
}
|
||||
|
||||
const float filter_width = in[SOCK_FILTER_WIDTH_ID].vec[0];
|
||||
const char *height_function = GPU_material_split_sub_function(
|
||||
mat, GPU_FLOAT, &in[SOCK_HEIGHT_ID].link);
|
||||
|
||||
/* TODO (Miguel Pozo):
|
||||
* Currently, this doesn't compute the actual differentials, just the height at dX and dY
|
||||
* offsets. The actual differentials are computed inside the GLSL node_bump function by
|
||||
* subtracting the height input. This avoids redundant computations when the height input is
|
||||
* also needed by regular nodes as part in the main function (See #103903 for context).
|
||||
* A better option would be to add a "value" input socket (in this case the height) to the
|
||||
* differentiate node, but currently this kind of intermediate nodes are pruned in the
|
||||
* code generation process (see #104265), so we need to fix that first. */
|
||||
GPUNodeLink *dheight = GPU_differentiate_float_function(height_function, filter_width);
|
||||
|
||||
float invert = (node->custom1) ? -1.0 : 1.0;
|
||||
|
||||
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 nodes::node_shader_bump_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bump()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_bump_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeBump"_ustr, SH_NODE_BUMP);
|
||||
ntype.ui_name = "Bump";
|
||||
ntype.ui_description =
|
||||
"Generate a perturbed normal from a height texture for bump mapping. Typically used for "
|
||||
"faking highly detailed surfaces";
|
||||
ntype.enum_name_legacy = "BUMP";
|
||||
ntype.nclass = NODE_CLASS_OP_VECTOR;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_camera_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Vector>("View Vector"_ustr);
|
||||
b.add_output<decl::Float>("View Z Depth"_ustr);
|
||||
b.add_output<decl::Float>("View Distance"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_camera(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
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_->identifier, NodeItem::Type::Any);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_camera_cc
|
||||
|
||||
void register_node_type_sh_camera()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_camera_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeCameraData"_ustr, SH_NODE_CAMERA);
|
||||
ntype.ui_name = "Camera Data";
|
||||
ntype.ui_description =
|
||||
"Retrieve information about the camera and how it relates to the current shading point's "
|
||||
"position";
|
||||
ntype.enum_name_legacy = "CAMERA";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,121 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
#include "FN_multi_function_builder.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_clamp_cc {
|
||||
|
||||
static void sh_node_clamp_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("Value"_ustr).default_value(1.0f);
|
||||
b.add_input<decl::Float>("Min"_ustr).default_value(0.0f).min(-10000.0f).max(10000.0f);
|
||||
b.add_input<decl::Float>("Max"_ustr).default_value(1.0f).min(-10000.0f).max(10000.0f);
|
||||
b.add_output<decl::Float>("Result"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_clamp(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "clamp_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_clamp(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = NODE_CLAMP_MINMAX; /* clamp type */
|
||||
}
|
||||
|
||||
static int gpu_shader_clamp(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
return (node->custom1 == NODE_CLAMP_MINMAX) ?
|
||||
GPU_stack_link(mat, node, "clamp_minmax", in, out) :
|
||||
GPU_stack_link(mat, node, "clamp_range", in, out);
|
||||
}
|
||||
|
||||
static void sh_node_clamp_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
static auto minmax_fn = mf::build::SI3_SO<float, float, float, float>(
|
||||
"Clamp (Min Max)",
|
||||
[](float value, float min, float max) { return std::min(std::max(value, min), max); });
|
||||
static auto range_fn = mf::build::SI3_SO<float, float, float, float>(
|
||||
"Clamp (Range)", [](float value, float a, float b) {
|
||||
if (a < b) {
|
||||
return clamp_f(value, a, b);
|
||||
}
|
||||
|
||||
return clamp_f(value, b, a);
|
||||
});
|
||||
|
||||
int clamp_type = builder.node().custom1;
|
||||
if (clamp_type == NODE_CLAMP_MINMAX) {
|
||||
builder.set_matching_fn(minmax_fn);
|
||||
}
|
||||
else {
|
||||
builder.set_matching_fn(range_fn);
|
||||
}
|
||||
}
|
||||
|
||||
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 nodes::node_shader_clamp_cc
|
||||
|
||||
void register_node_type_sh_clamp()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_clamp_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeClamp"_ustr, SH_NODE_CLAMP);
|
||||
ntype.ui_name = "Clamp";
|
||||
ntype.ui_description = "Clamp a value between a minimum and a maximum";
|
||||
ntype.enum_name_legacy = "CLAMP";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::sh_node_clamp_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,194 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "BKE_colorband.hh"
|
||||
|
||||
#include "BLI_color_types.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_color_ramp_cc {
|
||||
|
||||
static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("Factor"_ustr, "Fac"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"The value used to map onto the color gradient. 0.0 results in the leftmost color, "
|
||||
"while 1.0 results in the rightmost");
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
b.add_output<decl::Float>("Alpha"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_init_valtorgb(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->storage = BKE_colorband_add(true);
|
||||
}
|
||||
|
||||
static int gpu_shader_valtorgb(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
ColorBand *coba = static_cast<ColorBand *>(node->storage);
|
||||
float *array, layer;
|
||||
int size;
|
||||
|
||||
/* Common / easy case optimization. */
|
||||
if (coba->tot == 1) {
|
||||
return GPU_link(mat, "set_rgba", GPU_uniform(&coba->data[0].r), &out[0].link);
|
||||
}
|
||||
if ((coba->tot == 2) && (coba->color_mode == COLBAND_BLEND_RGB)) {
|
||||
float mul_bias[2];
|
||||
switch (coba->ipotype) {
|
||||
case COLBAND_INTERP_LINEAR:
|
||||
mul_bias[0] = 1.0f / (coba->data[1].pos - coba->data[0].pos);
|
||||
mul_bias[1] = -mul_bias[0] * coba->data[0].pos;
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"valtorgb_opti_linear",
|
||||
in,
|
||||
out,
|
||||
GPU_uniform(mul_bias),
|
||||
GPU_uniform(&coba->data[0].r),
|
||||
GPU_uniform(&coba->data[1].r));
|
||||
case COLBAND_INTERP_CONSTANT:
|
||||
mul_bias[1] = max_ff(coba->data[0].pos, coba->data[1].pos);
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"valtorgb_opti_constant",
|
||||
in,
|
||||
out,
|
||||
GPU_uniform(&mul_bias[1]),
|
||||
GPU_uniform(&coba->data[0].r),
|
||||
GPU_uniform(&coba->data[1].r));
|
||||
case COLBAND_INTERP_EASE:
|
||||
mul_bias[0] = 1.0f / (coba->data[1].pos - coba->data[0].pos);
|
||||
mul_bias[1] = -mul_bias[0] * coba->data[0].pos;
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"valtorgb_opti_ease",
|
||||
in,
|
||||
out,
|
||||
GPU_uniform(mul_bias),
|
||||
GPU_uniform(&coba->data[0].r),
|
||||
GPU_uniform(&coba->data[1].r));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_colorband_evaluate_table_rgba(coba, &array, &size);
|
||||
GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
|
||||
|
||||
if (coba->ipotype == COLBAND_INTERP_CONSTANT) {
|
||||
return GPU_stack_link(mat, node, "valtorgb_nearest", in, out, tex, GPU_constant(&layer));
|
||||
}
|
||||
|
||||
return GPU_stack_link(mat, node, "valtorgb", in, out, tex, GPU_constant(&layer));
|
||||
}
|
||||
|
||||
class ColorBandFunction : public mf::MultiFunction {
|
||||
private:
|
||||
/** Take ownership of the tree because it contains the color ramp. */
|
||||
std::shared_ptr<const bNodeTree> tree_;
|
||||
const ColorBand &color_band_;
|
||||
|
||||
public:
|
||||
ColorBandFunction(const ColorBand &color_band, std::shared_ptr<const bNodeTree> tree)
|
||||
: tree_(tree), color_band_(color_band)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Color Band", signature};
|
||||
builder.single_input<float>("Value");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Alpha");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float> &values = params.readonly_single_input<float>(0, "Value");
|
||||
MutableSpan<ColorGeometry4f> colors = params.uninitialized_single_output<ColorGeometry4f>(
|
||||
1, "Color");
|
||||
MutableSpan<float> alphas = params.uninitialized_single_output<float>(2, "Alpha");
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
ColorGeometry4f color;
|
||||
BKE_colorband_evaluate(&color_band_, values[i], color);
|
||||
colors[i] = color;
|
||||
alphas[i] = color.a;
|
||||
});
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(&color_band_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_valtorgb_build_multi_function(nodes::NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &bnode = builder.node();
|
||||
const ColorBand *color_band = static_cast<const ColorBand *>(bnode.storage);
|
||||
builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band, builder.shared_tree());
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
/* TODO: Implement */
|
||||
NodeItem res = empty();
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_color_ramp_cc
|
||||
|
||||
void register_node_type_sh_valtorgb()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_color_ramp_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeValToRGB"_ustr, SH_NODE_VALTORGB);
|
||||
ntype.ui_name = "Color Ramp";
|
||||
ntype.ui_description = "Map values to colors with the use of a gradient";
|
||||
ntype.enum_name_legacy = "VALTORGB";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::sh_node_valtorgb_declare;
|
||||
ntype.initfunc = file_ns::node_shader_init_valtorgb;
|
||||
ntype.default_width = bke::NodeWidth::_240;
|
||||
bke::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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,121 @@
|
||||
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "NOD_common.hh"
|
||||
#include "NOD_shader.h"
|
||||
#include "node_common.h"
|
||||
#include "node_exec.hh"
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**** GROUP ****/
|
||||
|
||||
static void group_gpu_copy_inputs(bNode *gnode, GPUNodeStack *in, bNodeStack *gstack)
|
||||
{
|
||||
bNodeTree *ngroup = id_cast<bNodeTree *>(gnode->id);
|
||||
|
||||
for (bNode *node : ngroup->all_nodes()) {
|
||||
if (node->is_group_input()) {
|
||||
|
||||
for (const auto [a, sock] : node->outputs.enumerate()) {
|
||||
bNodeStack *ns = node_get_socket_stack(gstack, &sock);
|
||||
if (ns) {
|
||||
/* convert the external gpu stack back to internal node stack data */
|
||||
node_data_from_gpu_stack(ns, &in[a]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy internal results to the external outputs.
|
||||
*/
|
||||
static void group_gpu_move_outputs(bNode *gnode, GPUNodeStack *out, bNodeStack *gstack)
|
||||
{
|
||||
bNodeTree &ngroup = *reinterpret_cast<bNodeTree *>(gnode->id);
|
||||
|
||||
ngroup.ensure_topology_cache();
|
||||
bNode *group_output_node = ngroup.group_output_node();
|
||||
if (!group_output_node) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto [a, sock] : group_output_node->inputs.enumerate()) {
|
||||
bNodeStack *ns = node_get_socket_stack(gstack, &sock);
|
||||
if (ns) {
|
||||
/* convert the node stack data result back to gpu stack */
|
||||
node_gpu_stack_from_data(&out[a], &sock, ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int gpu_group_execute(
|
||||
GPUMaterial *mat, bNode *node, bNodeExecData *execdata, GPUNodeStack *in, GPUNodeStack *out)
|
||||
{
|
||||
bNodeTreeExec *exec = static_cast<bNodeTreeExec *>(execdata->data);
|
||||
|
||||
if (!node->id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
group_gpu_copy_inputs(node, in, exec->stack);
|
||||
ntreeExecGPUNodes(exec, mat, nullptr);
|
||||
group_gpu_move_outputs(node, out, exec->stack);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void register_node_type_sh_group()
|
||||
{
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
/* NOTE: cannot use #sh_node_type_base for node group, because it would map the node type
|
||||
* to the shared #NODE_GROUP integer type id. */
|
||||
|
||||
bke::node_type_base_custom(ntype, "ShaderNodeGroup", "Group", "GROUP", NODE_CLASS_GROUP);
|
||||
ntype.enum_name_legacy = "GROUP";
|
||||
ntype.type_legacy = NODE_GROUP;
|
||||
ntype.poll = sh_node_poll_default;
|
||||
ntype.poll_instance = node_group_poll_instance;
|
||||
ntype.insert_link = node_insert_link_default;
|
||||
ntype.ui_class = node_group_ui_class;
|
||||
ntype.ui_description_fn = node_group_ui_description;
|
||||
ntype.rna_ext.srna = RNA_struct_find("ShaderNodeGroup");
|
||||
BLI_assert(ntype.rna_ext.srna != nullptr);
|
||||
RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);
|
||||
|
||||
ntype.minwidth = bke::NodeWidth::GroupMin;
|
||||
ntype.labelfunc = node_group_label;
|
||||
ntype.declare = nodes::node_group_declare;
|
||||
ntype.gpu_fn = gpu_group_execute;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
void register_node_type_sh_custom_group(bke::bNodeType *ntype)
|
||||
{
|
||||
/* These methods can be overridden but need a default implementation otherwise. */
|
||||
if (ntype->poll == nullptr) {
|
||||
ntype->poll = sh_node_poll_default;
|
||||
}
|
||||
if (ntype->insert_link == nullptr) {
|
||||
ntype->insert_link = node_insert_link_default;
|
||||
}
|
||||
ntype->declare = nodes::node_group_declare;
|
||||
ntype->gpu_fn = gpu_group_execute;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,489 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BKE_colortools.hh"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "node_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_curves_cc::vec {
|
||||
|
||||
static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("Factor"_ustr, "Fac"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.no_muted_links()
|
||||
.description("Amount of influence the node exerts on the output vector")
|
||||
.compositor_domain_priority(1);
|
||||
b.add_input<decl::Vector>("Vector"_ustr)
|
||||
.min(-1.0f)
|
||||
.max(1.0f)
|
||||
.description("Vector which would be mapped to the curve")
|
||||
.compositor_domain_priority(0);
|
||||
b.add_output<decl::Vector>("Vector"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_init_curve_vec(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->storage = BKE_curvemapping_add(3, -1.0f, -1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
static int gpu_shader_curve_vec(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
CurveMapping *curve_mapping = static_cast<CurveMapping *>(node->storage);
|
||||
|
||||
BKE_curvemapping_init(curve_mapping);
|
||||
float *band_values;
|
||||
int band_size;
|
||||
BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
|
||||
float band_layer;
|
||||
GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer);
|
||||
|
||||
float start_slopes[CM_TOT];
|
||||
float end_slopes[CM_TOT];
|
||||
BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
|
||||
float range_minimums[CM_TOT];
|
||||
BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
|
||||
float range_dividers[CM_TOT];
|
||||
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"curves_vector_mixed",
|
||||
in,
|
||||
out,
|
||||
band_texture,
|
||||
GPU_constant(&band_layer),
|
||||
GPU_uniform(range_minimums),
|
||||
GPU_uniform(range_dividers),
|
||||
GPU_uniform(start_slopes),
|
||||
GPU_uniform(end_slopes));
|
||||
}
|
||||
|
||||
class CurveVecFunction : public mf::MultiFunction {
|
||||
private:
|
||||
/** Take ownership of the tree because it contains the curve mapping. */
|
||||
std::shared_ptr<const bNodeTree> tree_;
|
||||
const CurveMapping &cumap_;
|
||||
|
||||
public:
|
||||
CurveVecFunction(const CurveMapping &cumap, std::shared_ptr<const bNodeTree> tree)
|
||||
: tree_(std::move(tree)), cumap_(cumap)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Curve Vec", signature};
|
||||
builder.single_input<float>("Fac");
|
||||
builder.single_input<float3>("Vector");
|
||||
builder.single_output<float3>("Vector");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
|
||||
const VArray<float3> &vec_in = params.readonly_single_input<float3>(1, "Vector");
|
||||
MutableSpan<float3> vec_out = params.uninitialized_single_output<float3>(2, "Vector");
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
BKE_curvemapping_evaluate3F(&cumap_, vec_out[i], vec_in[i]);
|
||||
if (fac[i] != 1.0f) {
|
||||
interp_v3_v3v3(vec_out[i], vec_in[i], vec_out[i], fac[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(&cumap_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_curve_vec_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &bnode = builder.node();
|
||||
CurveMapping *cumap = static_cast<CurveMapping *>(bnode.storage);
|
||||
BKE_curvemapping_init(cumap);
|
||||
builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap, builder.shared_tree());
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
/* TODO: implement */
|
||||
return get_input_value("Vector", NodeItem::Type::Vector3);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_curves_cc::vec
|
||||
|
||||
void register_node_type_sh_curve_vec()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_curves_cc::vec;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeVectorCurve"_ustr, SH_NODE_CURVE_VEC);
|
||||
ntype.ui_name = "Vector Curves";
|
||||
ntype.ui_description = "Map input vector components with curves";
|
||||
ntype.enum_name_legacy = "CURVE_VEC";
|
||||
ntype.nclass = NODE_CLASS_OP_VECTOR;
|
||||
ntype.declare = file_ns::sh_node_curve_vec_declare;
|
||||
ntype.initfunc = file_ns::node_shader_init_curve_vec;
|
||||
ntype.default_width = bke::NodeWidth::_240;
|
||||
bke::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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
/* **************** CURVE RGB ******************** */
|
||||
|
||||
namespace nodes::node_shader_curves_cc::rgb {
|
||||
|
||||
static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("Factor"_ustr, "Fac"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.no_muted_links()
|
||||
.description("Amount of influence the node exerts on the output color")
|
||||
.compositor_domain_priority(1);
|
||||
b.add_input<decl::Color>("Color"_ustr)
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.description("Color input on which correction will be applied")
|
||||
.compositor_domain_priority(0);
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_init_curve_rgb(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->storage = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
static int gpu_shader_curve_rgb(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
CurveMapping *curve_mapping = static_cast<CurveMapping *>(node->storage);
|
||||
|
||||
BKE_curvemapping_init(curve_mapping);
|
||||
float *band_values;
|
||||
int band_size;
|
||||
BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
|
||||
float band_layer;
|
||||
GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer);
|
||||
|
||||
float start_slopes[CM_TOT];
|
||||
float end_slopes[CM_TOT];
|
||||
BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
|
||||
float range_minimums[CM_TOT];
|
||||
BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
|
||||
float range_dividers[CM_TOT];
|
||||
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
|
||||
|
||||
/* Shader nodes don't do white balancing. */
|
||||
float black_level[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
float white_level[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
|
||||
/* If the RGB curves do nothing, use a function that skips RGB computations. */
|
||||
if (BKE_curvemapping_is_map_identity(curve_mapping, 0) &&
|
||||
BKE_curvemapping_is_map_identity(curve_mapping, 1) &&
|
||||
BKE_curvemapping_is_map_identity(curve_mapping, 2))
|
||||
{
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"curves_combined_only",
|
||||
in,
|
||||
out,
|
||||
GPU_constant(black_level),
|
||||
GPU_constant(white_level),
|
||||
band_texture,
|
||||
GPU_constant(&band_layer),
|
||||
GPU_uniform(&range_minimums[3]),
|
||||
GPU_uniform(&range_dividers[3]),
|
||||
GPU_uniform(&start_slopes[3]),
|
||||
GPU_uniform(&end_slopes[3]));
|
||||
}
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"curves_combined_rgb",
|
||||
in,
|
||||
out,
|
||||
GPU_constant(black_level),
|
||||
GPU_constant(white_level),
|
||||
band_texture,
|
||||
GPU_constant(&band_layer),
|
||||
GPU_uniform(range_minimums),
|
||||
GPU_uniform(range_dividers),
|
||||
GPU_uniform(start_slopes),
|
||||
GPU_uniform(end_slopes));
|
||||
}
|
||||
|
||||
class CurveRGBFunction : public mf::MultiFunction {
|
||||
private:
|
||||
/** Take ownership of the tree because it contains the curve mapping. */
|
||||
std::shared_ptr<const bNodeTree> tree_;
|
||||
const CurveMapping &cumap_;
|
||||
|
||||
public:
|
||||
CurveRGBFunction(const CurveMapping &cumap, std::shared_ptr<const bNodeTree> tree)
|
||||
: tree_(std::move(tree)), cumap_(cumap)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Curve RGB", signature};
|
||||
builder.single_input<float>("Fac");
|
||||
builder.single_input<ColorGeometry4f>("Color");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
|
||||
const VArray<ColorGeometry4f> &col_in = params.readonly_single_input<ColorGeometry4f>(1,
|
||||
"Color");
|
||||
MutableSpan<ColorGeometry4f> col_out = params.uninitialized_single_output<ColorGeometry4f>(
|
||||
2, "Color");
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
BKE_curvemapping_evaluateRGBF(&cumap_, col_out[i], col_in[i]);
|
||||
if (fac[i] != 1.0f) {
|
||||
interp_v3_v3v3(col_out[i], col_in[i], col_out[i], fac[i]);
|
||||
}
|
||||
col_out[i].a = 1.0f;
|
||||
});
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(&cumap_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_curve_rgb_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &bnode = builder.node();
|
||||
CurveMapping *cumap = static_cast<CurveMapping *>(bnode.storage);
|
||||
BKE_curvemapping_init(cumap);
|
||||
builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap, builder.shared_tree());
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
/* TODO: implement */
|
||||
return get_input_value("Color", NodeItem::Type::Color3);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_curves_cc::rgb
|
||||
|
||||
void register_node_type_sh_curve_rgb()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_curves_cc::rgb;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeRGBCurve"_ustr, SH_NODE_CURVE_RGB);
|
||||
ntype.ui_name = "RGB Curves";
|
||||
ntype.ui_description = "Apply color corrections for each color channel";
|
||||
ntype.enum_name_legacy = "CURVE_RGB";
|
||||
ntype.nclass = NODE_CLASS_OP_COLOR;
|
||||
ntype.declare = file_ns::sh_node_curve_rgb_declare;
|
||||
ntype.initfunc = file_ns::node_shader_init_curve_rgb;
|
||||
ntype.default_width = bke::NodeWidth::_240;
|
||||
bke::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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
/* **************** CURVE FLOAT ******************** */
|
||||
|
||||
namespace nodes::node_shader_curves_cc::flt {
|
||||
|
||||
static void sh_node_curve_float_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("Factor"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.no_muted_links()
|
||||
.compositor_domain_priority(1);
|
||||
b.add_input<decl::Float>("Value"_ustr)
|
||||
.default_value(1.0f)
|
||||
.is_default_link_socket()
|
||||
.compositor_domain_priority(0);
|
||||
b.add_output<decl::Float>("Value"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_init_curve_float(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
static int gpu_shader_curve_float(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
CurveMapping *curve_mapping = static_cast<CurveMapping *>(node->storage);
|
||||
|
||||
BKE_curvemapping_init(curve_mapping);
|
||||
float *band_values;
|
||||
int band_size;
|
||||
BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
|
||||
float band_layer;
|
||||
GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer);
|
||||
|
||||
float start_slopes[CM_TOT];
|
||||
float end_slopes[CM_TOT];
|
||||
BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
|
||||
float range_minimums[CM_TOT];
|
||||
BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
|
||||
float range_dividers[CM_TOT];
|
||||
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"curves_float_mixed",
|
||||
in,
|
||||
out,
|
||||
band_texture,
|
||||
GPU_constant(&band_layer),
|
||||
GPU_uniform(range_minimums),
|
||||
GPU_uniform(range_dividers),
|
||||
GPU_uniform(start_slopes),
|
||||
GPU_uniform(end_slopes));
|
||||
}
|
||||
|
||||
class CurveFloatFunction : public mf::MultiFunction {
|
||||
private:
|
||||
/** Take ownership of the tree because it contains the curve mapping. */
|
||||
std::shared_ptr<const bNodeTree> tree_;
|
||||
const CurveMapping &cumap_;
|
||||
|
||||
public:
|
||||
CurveFloatFunction(const CurveMapping &cumap, std::shared_ptr<const bNodeTree> tree)
|
||||
: tree_(std::move(tree)), cumap_(cumap)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Curve Float", signature};
|
||||
builder.single_input<float>("Factor");
|
||||
builder.single_input<float>("Value");
|
||||
builder.single_output<float>("Value");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float> &fac = params.readonly_single_input<float>(0, "Factor");
|
||||
const VArray<float> &val_in = params.readonly_single_input<float>(1, "Value");
|
||||
MutableSpan<float> val_out = params.uninitialized_single_output<float>(2, "Value");
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
val_out[i] = BKE_curvemapping_evaluateF(&cumap_, 0, val_in[i]);
|
||||
if (fac[i] != 1.0f) {
|
||||
val_out[i] = (1.0f - fac[i]) * val_in[i] + fac[i] * val_out[i];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(&cumap_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_curve_float_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &bnode = builder.node();
|
||||
CurveMapping *cumap = static_cast<CurveMapping *>(bnode.storage);
|
||||
BKE_curvemapping_init(cumap);
|
||||
builder.construct_and_set_matching_fn<CurveFloatFunction>(*cumap, builder.shared_tree());
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
/* TODO: implement */
|
||||
return get_input_value("Value", NodeItem::Type::Float);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_curves_cc::flt
|
||||
|
||||
void register_node_type_sh_curve_float()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_curves_cc::flt;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeFloatCurve"_ustr, SH_NODE_CURVE_FLOAT);
|
||||
ntype.ui_name = "Float Curve";
|
||||
ntype.ui_description = "Map an input float to a curve and outputs a float value";
|
||||
ntype.enum_name_legacy = "CURVE_FLOAT";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::sh_node_curve_float_declare;
|
||||
ntype.initfunc = file_ns::node_shader_init_curve_float;
|
||||
ntype.default_width = bke::NodeWidth::_240;
|
||||
bke::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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,98 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_displacement_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Height"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description("Distance to displace the surface along the normal");
|
||||
b.add_input<decl::Float>("Midlevel"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description(
|
||||
"Neutral displacement value that causes no displacement.\n"
|
||||
"Lower values cause the surface to move inwards, "
|
||||
"higher values push the surface outwards");
|
||||
b.add_input<decl::Float>("Scale"_ustr)
|
||||
.default_value(0.01f)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description("Increase or decrease the amount of displacement");
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_output<decl::Vector>("Displacement"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_init_displacement(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_SPACE_OBJECT; /* space */
|
||||
}
|
||||
|
||||
static int gpu_shader_displacement(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[3].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[3].link);
|
||||
}
|
||||
|
||||
if (node->custom1 == SHD_SPACE_OBJECT) {
|
||||
return GPU_stack_link(mat, node, "node_displacement_object", in, out);
|
||||
}
|
||||
|
||||
return GPU_stack_link(mat, node, "node_displacement_world", in, out);
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
if (to_type_ != NodeItem::Type::DisplacementShader) {
|
||||
return empty();
|
||||
}
|
||||
|
||||
/* 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 nodes::node_shader_displacement_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_displacement()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_displacement_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeDisplacement"_ustr, SH_NODE_DISPLACEMENT);
|
||||
ntype.ui_name = "Displacement";
|
||||
ntype.ui_description = "Displace the surface along the surface normal";
|
||||
ntype.enum_name_legacy = "DISPLACEMENT";
|
||||
ntype.nclass = NODE_CLASS_OP_VECTOR;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,109 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_eevee_specular_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Base Color"_ustr).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_input<decl::Color>("Specular"_ustr).default_value({0.03f, 0.03f, 0.03f, 1.0f});
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(0.2f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Color>("Emissive Color"_ustr).default_value({0.0f, 0.0f, 0.0f, 1.0f});
|
||||
b.add_input<decl::Float>("Transparency"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Clear Coat"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("Clear Coat Roughness"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Vector>("Clear Coat Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSDF"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_eevee_specular(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
/* Normals */
|
||||
if (!in[5].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[5].link);
|
||||
}
|
||||
|
||||
/* Coat Normals */
|
||||
if (!in[8].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[8].link);
|
||||
}
|
||||
|
||||
bool use_transparency = in[4].socket_not_zero();
|
||||
bool use_coat = in[6].socket_not_zero();
|
||||
|
||||
eGPUMaterialFlag flag = GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY;
|
||||
|
||||
if (in[1].might_be_tinted()) {
|
||||
flag |= GPU_MATFLAG_REFLECTION_MAYBE_COLORED;
|
||||
}
|
||||
if (use_coat) {
|
||||
flag |= GPU_MATFLAG_COAT;
|
||||
}
|
||||
if (use_transparency) {
|
||||
flag |= GPU_MATFLAG_TRANSPARENT;
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, flag);
|
||||
|
||||
float use_coat_f = use_coat ? 1.0f : 0.0f;
|
||||
return GPU_stack_link(mat, node, "node_eevee_specular", in, out, GPU_constant(&use_coat_f));
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_eevee_specular_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_eevee_specular()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_eevee_specular_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeEeveeSpecular"_ustr, SH_NODE_EEVEE_SPECULAR);
|
||||
ntype.ui_name = "Specular BSDF";
|
||||
ntype.ui_description =
|
||||
"Similar to the Principled BSDF node but uses the specular workflow instead of metallic, "
|
||||
"which functions by specifying the facing (along normal) reflection color. Energy is not "
|
||||
"conserved, so the result may not be physically accurate";
|
||||
ntype.enum_name_legacy = "EEVEE_SPECULAR";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_eevee_shader_nodes_poll;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_eevee_specular;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,73 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_emission_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({1.0f, 1.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Float>("Strength"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1000000.0f)
|
||||
.translation_context(BLT_I18NCONTEXT_AMOUNT);
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("Emission"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_emission(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_EMISSION);
|
||||
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 nodes::node_shader_emission_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_emission()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_emission_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeEmission"_ustr, SH_NODE_EMISSION);
|
||||
ntype.ui_name = "Emission";
|
||||
ntype.ui_description = "Lambertian emission shader";
|
||||
ntype.enum_name_legacy = "EMISSION";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_emission;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_fresnel_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("IOR"_ustr).default_value(1.5f).min(0.0f).max(1000.0f);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_output<decl::Float>("Factor"_ustr, "Fac"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_fresnel(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[1].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[1].link);
|
||||
}
|
||||
|
||||
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 node-definition `<artistic_ior>`). */
|
||||
return get_input_value("IOR", NodeItem::Type::Float);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_fresnel_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_fresnel()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_fresnel_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeFresnel"_ustr, SH_NODE_FRESNEL);
|
||||
ntype.ui_name = "Fresnel";
|
||||
ntype.ui_description =
|
||||
"Produce a blending factor depending on the angle between the surface normal and the view "
|
||||
"direction using Fresnel equations.\nTypically used for mixing reflections at grazing "
|
||||
"angles";
|
||||
ntype.enum_name_legacy = "FRESNEL";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,95 @@
|
||||
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "FN_multi_function_builder.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_gamma_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.use_custom_socket_order();
|
||||
b.allow_any_socket_order();
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Color>("Color"_ustr)
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.hide_value()
|
||||
.description("Color input on which correction will be applied");
|
||||
b.add_output<decl::Color>("Color"_ustr).align_with_previous();
|
||||
|
||||
b.add_input<decl::Float>("Gamma"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.001f)
|
||||
.max(10.0f)
|
||||
.subtype(PROP_NONE)
|
||||
.description(
|
||||
"Gamma correction value, applied as color^gamma.\n"
|
||||
"Gamma controls the relative intensity of the mid-tones compared to the full black and "
|
||||
"full white");
|
||||
}
|
||||
|
||||
static int node_shader_gpu_gamma(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
return GPU_stack_link(mat, node, "node_gamma", in, out);
|
||||
}
|
||||
|
||||
using namespace blender::math;
|
||||
|
||||
static void node_build_multi_function(nodes::NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
static auto fn = mf::build::SI2_SO<ColorGeometry4f, float, ColorGeometry4f>(
|
||||
"Gamma",
|
||||
[](const ColorGeometry4f &color, const float gamma) -> ColorGeometry4f {
|
||||
const float3 rgb = float3(color.r, color.g, color.b);
|
||||
const float3 rgb_gamma = math::safe_pow(rgb, gamma);
|
||||
return ColorGeometry4f(rgb_gamma.x, rgb_gamma.y, rgb_gamma.z, color.a);
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<0>());
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
|
||||
NodeItem gamma = get_input_value("Gamma", NodeItem::Type::Float);
|
||||
return color ^ gamma;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_gamma_cc
|
||||
|
||||
void register_node_type_sh_gamma()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_gamma_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeGamma"_ustr, SH_NODE_GAMMA);
|
||||
ntype.ui_name = "Gamma";
|
||||
ntype.ui_description = "Apply a gamma correction";
|
||||
ntype.enum_name_legacy = "GAMMA";
|
||||
ntype.nclass = NODE_CLASS_OP_COLOR;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_gamma;
|
||||
ntype.build_multi_function = file_ns::node_build_multi_function;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,107 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_geometry_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Vector>("Position"_ustr);
|
||||
b.add_output<decl::Vector>("Normal"_ustr);
|
||||
b.add_output<decl::Vector>("Tangent"_ustr);
|
||||
b.add_output<decl::Vector>("True Normal"_ustr);
|
||||
b.add_output<decl::Vector>("Incoming"_ustr);
|
||||
b.add_output<decl::Vector>("Parametric"_ustr);
|
||||
b.add_output<decl::Float>("Backfacing"_ustr);
|
||||
b.add_output<decl::Float>("Pointiness"_ustr);
|
||||
b.add_output<decl::Float>("Random Per Island"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_geometry(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
/* HACK: Don't request GPU_MATFLAG_BARYCENTRIC if not used because it will
|
||||
* trigger the use of geometry shader (and the performance penalty it implies). */
|
||||
if (out[5].hasoutput) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC);
|
||||
}
|
||||
/* Optimization: don't request orco if not needed. */
|
||||
const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPUNodeLink *orco_link = out[2].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(val);
|
||||
|
||||
const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link);
|
||||
|
||||
for (const auto [i, sock] : node->outputs.enumerate()) {
|
||||
node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
|
||||
/* Normalize some vectors after dFdx/dFdy offsets.
|
||||
* This is the case for interpolated, non linear functions.
|
||||
* The resulting vector can still be a bit wrong but not as much.
|
||||
* (see #70644) */
|
||||
if (ELEM(i, 1, 2, 4)) {
|
||||
GPU_link(mat,
|
||||
"vector_math_normalize",
|
||||
out[i].link,
|
||||
out[i].link,
|
||||
out[i].link,
|
||||
out[i].link,
|
||||
&out[i].link,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
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_->identifier;
|
||||
|
||||
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 nodes::node_shader_geometry_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_geometry()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_geometry_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeNewGeometry"_ustr, SH_NODE_NEW_GEOMETRY);
|
||||
ntype.ui_name = "Geometry";
|
||||
ntype.ui_description = "Retrieve geometric information about the current shading point";
|
||||
ntype.enum_name_legacy = "NEW_GEOMETRY";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,69 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_hair_info_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Float>("Is Strand"_ustr);
|
||||
#define INTERCEPT_SOCKET_INDEX 1
|
||||
b.add_output<decl::Float>("Intercept"_ustr);
|
||||
#define LENGTH_SOCKET_INDEX 2
|
||||
b.add_output<decl::Float>("Length"_ustr);
|
||||
b.add_output<decl::Float>("Thickness"_ustr);
|
||||
b.add_output<decl::Vector>("Tangent Normal"_ustr);
|
||||
b.add_output<decl::Float>("Random"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_hair_info(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
/* Length: don't request length if not needed. */
|
||||
static const float zero = 0;
|
||||
GPUNodeLink *length_link = out[LENGTH_SOCKET_INDEX].hasoutput ? GPU_attribute_hair_length(mat) :
|
||||
GPU_constant(&zero);
|
||||
GPUNodeLink *intercept_link = out[INTERCEPT_SOCKET_INDEX].hasoutput ?
|
||||
GPU_attribute_hair_intercept(mat) :
|
||||
GPU_constant(&zero);
|
||||
return GPU_stack_link(mat, node, "node_hair_info", in, out, intercept_link, 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_->identifier, NodeItem::Type::Any);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_hair_info_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_hair_info()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_hair_info_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeHairInfo"_ustr, SH_NODE_HAIR_INFO);
|
||||
ntype.ui_name = "Curves Info";
|
||||
ntype.ui_description = "Retrieve hair curve information";
|
||||
ntype.enum_name_legacy = "HAIR_INFO";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,55 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_holdout_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("Holdout"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_rgb(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_HOLDOUT);
|
||||
return GPU_stack_link(mat, node, "node_holdout", in, out);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_holdout_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_holdout()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_holdout_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeHoldout"_ustr, SH_NODE_HOLDOUT);
|
||||
ntype.ui_name = "Holdout";
|
||||
ntype.ui_description =
|
||||
"Create a \"hole\" in the image with zero alpha transparency, which is useful for "
|
||||
"compositing.\nNote: the holdout shader can only create alpha when transparency is enabled "
|
||||
"in the film settings";
|
||||
ntype.enum_name_legacy = "HOLDOUT";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.gpu_fn = file_ns::gpu_shader_rgb;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,104 @@
|
||||
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_hueSatVal_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Hue"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.description(
|
||||
"Hue rotation offset, from 0 (-180°) to 1 (+180°). Note that 0 and 1 have the same "
|
||||
"result");
|
||||
b.add_input<decl::Float>("Saturation"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(2.0f)
|
||||
.description(
|
||||
"Value of 0 removes color from the image, making it black-and-white. "
|
||||
"A value greater than 1.0 increases saturation");
|
||||
b.add_input<decl::Float>("Value"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(2.0f)
|
||||
.translation_context(BLT_I18NCONTEXT_COLOR)
|
||||
.description(
|
||||
"Value shift. 0 makes the color black, 1 keeps it the same, and higher values make it "
|
||||
"brighter");
|
||||
b.add_input<decl::Float>("Factor"_ustr, "Fac"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Amount of influence the node exerts on the image");
|
||||
b.add_input<decl::Color>("Color"_ustr)
|
||||
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
|
||||
.description("Color input on which HSV color transformation will be applied");
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_hue_sat(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
return GPU_stack_link(mat, node, "hue_sat", in, out);
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
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}});
|
||||
|
||||
NodeItem hsv = create_node(
|
||||
"hsvadjust", NodeItem::Type::Color3, {{"in", color}, {"amount", combine}});
|
||||
|
||||
return fac.mix(color, hsv);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_hueSatVal_cc
|
||||
|
||||
void register_node_type_sh_hue_sat()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_hueSatVal_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeHueSaturation"_ustr, SH_NODE_HUE_SAT);
|
||||
ntype.ui_name = "Hue/Saturation/Value";
|
||||
ntype.ui_description = "Apply a color transformation in the HSV color model";
|
||||
ntype.enum_name_legacy = "HUE_SAT";
|
||||
ntype.nclass = NODE_CLASS_OP_COLOR;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.gpu_fn = file_ns::gpu_shader_hue_sat;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2018 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_ies_light_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Vector>("Vector"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Strength"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1000000.0f)
|
||||
.description("Strength of the light source")
|
||||
.translation_context(BLT_I18NCONTEXT_AMOUNT);
|
||||
b.add_output<decl::Float>("Factor"_ustr, "Fac"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_ies(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
{
|
||||
ui::Layout &row = layout.row(false);
|
||||
row.prop(
|
||||
ptr, "mode", ui::ITEM_R_SPLIT_EMPTY_NAME | ui::ITEM_R_EXPAND, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
{
|
||||
ui::Layout &row = layout.row(true);
|
||||
if (RNA_enum_get(ptr, "mode") == NODE_IES_INTERNAL) {
|
||||
row.prop(ptr, "ies", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
else {
|
||||
row.prop(ptr, "filepath", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_ies(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeShaderTexIES *tex = MEM_new<NodeShaderTexIES>("NodeShaderIESLight");
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_ies_light_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_tex_ies()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_ies_light_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeTexIES"_ustr, SH_NODE_TEX_IES);
|
||||
ntype.ui_name = "IES Texture";
|
||||
ntype.ui_description =
|
||||
"Match real world lights with IES files, which store the directional intensity distribution "
|
||||
"of light sources";
|
||||
ntype.enum_name_legacy = "TEX_IES";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_ies;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_ies;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeShaderTexIES", node_free_standard_storage, node_copy_standard_storage);
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_invert_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Factor"_ustr, "Fac"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Amount of influence the node exerts on the image");
|
||||
b.add_input<decl::Color>("Color"_ustr)
|
||||
.default_value({0.0f, 0.0f, 0.0f, 1.0f})
|
||||
.description("Color input on which inversion will be applied");
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_invert(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
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 nodes::node_shader_invert_cc
|
||||
|
||||
void register_node_type_sh_invert()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_invert_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeInvert"_ustr, SH_NODE_INVERT);
|
||||
ntype.ui_name = "Invert Color";
|
||||
ntype.ui_description = "Invert a color, producing a negative";
|
||||
ntype.enum_name_legacy = "INVERT";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_layer_weight_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Blend"_ustr).default_value(0.5f).min(0.0f).max(1.0f);
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_output<decl::Float>("Fresnel"_ustr);
|
||||
b.add_output<decl::Float>("Facing"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_layer_weight(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[1].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[1].link);
|
||||
}
|
||||
|
||||
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 node-definition `<artistic_ior>`). */
|
||||
return get_input_link("Blend", NodeItem::Type::Float);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_layer_weight_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_layer_weight()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_layer_weight_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeLayerWeight"_ustr, SH_NODE_LAYER_WEIGHT);
|
||||
ntype.ui_name = "Layer Weight";
|
||||
ntype.ui_description =
|
||||
"Produce a blending factor depending on the angle between the surface normal and the view "
|
||||
"direction.\nTypically used for layering shaders with the Mix Shader node";
|
||||
ntype.enum_name_legacy = "LAYER_WEIGHT";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_light_falloff_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Strength"_ustr)
|
||||
.default_value(100.0f)
|
||||
.min(0.0f)
|
||||
.max(1000000.0f)
|
||||
.description("Light strength before applying falloff modification")
|
||||
.translation_context(BLT_I18NCONTEXT_AMOUNT);
|
||||
|
||||
b.add_input<decl::Float>("Smooth"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description(
|
||||
"Smooth intensity of light near light sources.\n"
|
||||
"This can avoid harsh highlights, and reduce global illumination noise. "
|
||||
"0.0 corresponds to no smoothing; higher values smooth more");
|
||||
b.add_output<decl::Float>("Quadratic"_ustr);
|
||||
b.add_output<decl::Float>("Linear"_ustr);
|
||||
b.add_output<decl::Float>("Constant"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_light_falloff(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
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 nodes::node_shader_light_falloff_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_light_falloff()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_light_falloff_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeLightFalloff"_ustr, SH_NODE_LIGHT_FALLOFF);
|
||||
ntype.ui_name = "Light Falloff";
|
||||
ntype.ui_description =
|
||||
"Manipulate how light intensity decreases over distance. Typically used for "
|
||||
"non-physically-based effects; in reality light always falls off quadratically";
|
||||
ntype.enum_name_legacy = "LIGHT_FALLOFF";
|
||||
ntype.nclass = NODE_CLASS_OP_COLOR;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_light_falloff;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,90 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_light_path_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Float>("Is Camera Ray"_ustr);
|
||||
b.add_output<decl::Float>("Is Shadow Ray"_ustr);
|
||||
b.add_output<decl::Float>("Is Diffuse Ray"_ustr);
|
||||
b.add_output<decl::Float>("Is Glossy Ray"_ustr);
|
||||
b.add_output<decl::Float>("Is Singular Ray"_ustr);
|
||||
b.add_output<decl::Float>("Is Reflection Ray"_ustr);
|
||||
b.add_output<decl::Float>("Is Transmission Ray"_ustr);
|
||||
b.add_output<decl::Float>("Is Volume Scatter Ray"_ustr);
|
||||
b.add_output<decl::Float>("Ray Length"_ustr);
|
||||
b.add_output<decl::Float>("Ray Depth"_ustr);
|
||||
b.add_output<decl::Float>("Diffuse Depth"_ustr);
|
||||
b.add_output<decl::Float>("Glossy Depth"_ustr);
|
||||
b.add_output<decl::Float>("Transparent Depth"_ustr);
|
||||
b.add_output<decl::Float>("Transmission Depth"_ustr);
|
||||
b.add_output<decl::Float>("Portal Depth"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_light_path(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (out[2].hasoutput || /* Is Diffuse Ray. */
|
||||
out[3].hasoutput || /* Is Glossy Ray. */
|
||||
out[4].hasoutput || /* Is Singular Ray. */
|
||||
out[5].hasoutput || /* Is Reflection Ray. */
|
||||
out[6].hasoutput || /* Is Transmission Ray. */
|
||||
out[10].hasoutput || /* Diffuse Depth. */
|
||||
out[11].hasoutput || /* Glossy Depth. */
|
||||
out[13].hasoutput) /* Transmission Depth. */
|
||||
{
|
||||
/* Used to detect that the world has a specific look for diffuse path. */
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_IS_DIFFUSE_OR_GLOSSY_RAY_FLAG);
|
||||
}
|
||||
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_->identifier, "Is Camera Ray")) {
|
||||
return val(1.0f);
|
||||
}
|
||||
if (STREQ(socket_out_->identifier, "Ray Length")) {
|
||||
return val(1.0f);
|
||||
}
|
||||
NodeItem res = val(0.0f);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_light_path_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_light_path()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_light_path_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeLightPath"_ustr, SH_NODE_LIGHT_PATH);
|
||||
ntype.ui_name = "Light Path";
|
||||
ntype.ui_description =
|
||||
"Retrieve the type of incoming ray for which the shader is being executed.\nTypically used "
|
||||
"for non-physically-based tricks";
|
||||
ntype.enum_name_legacy = "LIGHT_PATH";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,548 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BLI_math_base_safe.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
#include "FN_multi_function_builder.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_map_range_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeMapRange)
|
||||
|
||||
static void sh_node_map_range_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("Value"_ustr).min(-10000.0f).max(10000.0f).default_value(1.0f);
|
||||
b.add_input<decl::Float>("From Min"_ustr).min(-10000.0f).max(10000.0f);
|
||||
b.add_input<decl::Float>("From Max"_ustr).min(-10000.0f).max(10000.0f).default_value(1.0f);
|
||||
b.add_input<decl::Float>("To Min"_ustr).min(-10000.0f).max(10000.0f);
|
||||
b.add_input<decl::Float>("To Max"_ustr).min(-10000.0f).max(10000.0f).default_value(1.0f);
|
||||
b.add_input<decl::Float>("Steps"_ustr).min(-10000.0f).max(10000.0f).default_value(4.0f);
|
||||
b.add_input<decl::Vector>("Vector"_ustr).min(0.0f).max(1.0f).hide_value();
|
||||
b.add_input<decl::Vector>("From Min"_ustr, "From_Min_FLOAT3"_ustr);
|
||||
b.add_input<decl::Vector>("From Max"_ustr, "From_Max_FLOAT3"_ustr).default_value(float3(1.0f));
|
||||
b.add_input<decl::Vector>("To Min"_ustr, "To_Min_FLOAT3"_ustr);
|
||||
b.add_input<decl::Vector>("To Max"_ustr, "To_Max_FLOAT3"_ustr).default_value(float3(1.0f));
|
||||
b.add_input<decl::Vector>("Steps"_ustr, "Steps_FLOAT3"_ustr).default_value(float3(4.0f));
|
||||
b.add_output<decl::Float>("Result"_ustr);
|
||||
b.add_output<decl::Vector>("Vector"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_map_range(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "data_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
layout.prop(ptr, "interpolation_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
if (!ELEM(RNA_enum_get(ptr, "interpolation_type"),
|
||||
NODE_MAP_RANGE_SMOOTHSTEP,
|
||||
NODE_MAP_RANGE_SMOOTHERSTEP))
|
||||
{
|
||||
layout.prop(ptr, "clamp", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static int node_shader_map_range_ui_class(const bNode *node)
|
||||
{
|
||||
const NodeMapRange &storage = node_storage(*node);
|
||||
const eCustomDataType data_type = eCustomDataType(storage.data_type);
|
||||
if (data_type == CD_PROP_FLOAT3) {
|
||||
return NODE_CLASS_OP_VECTOR;
|
||||
}
|
||||
return NODE_CLASS_CONVERTER;
|
||||
}
|
||||
|
||||
static void node_shader_update_map_range(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
const NodeMapRange &storage = node_storage(*node);
|
||||
const eCustomDataType data_type = eCustomDataType(storage.data_type);
|
||||
const int type = (data_type == CD_PROP_FLOAT) ? SOCK_FLOAT : SOCK_VECTOR;
|
||||
|
||||
Array<bool> new_input_availability(node->inputs.count());
|
||||
Array<bool> new_output_availability(node->outputs.count());
|
||||
|
||||
for (const auto [index, socket] : node->inputs.enumerate()) {
|
||||
new_input_availability[index] = socket.type == type;
|
||||
}
|
||||
for (const auto [index, socket] : node->outputs.enumerate()) {
|
||||
new_output_availability[index] = socket.type == type;
|
||||
}
|
||||
|
||||
if (storage.interpolation_type != NODE_MAP_RANGE_STEPPED) {
|
||||
if (type == SOCK_FLOAT) {
|
||||
new_input_availability[5] = false;
|
||||
}
|
||||
else {
|
||||
new_input_availability[11] = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto [index, socket] : node->inputs.enumerate()) {
|
||||
bke::node_set_socket_availability(*ntree, socket, new_input_availability[index]);
|
||||
}
|
||||
for (const auto [index, socket] : node->outputs.enumerate()) {
|
||||
bke::node_set_socket_availability(*ntree, socket, new_output_availability[index]);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_map_range(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeMapRange *data = MEM_new<NodeMapRange>(__func__);
|
||||
data->clamp = 1;
|
||||
data->data_type = CD_PROP_FLOAT;
|
||||
data->interpolation_type = NODE_MAP_RANGE_LINEAR;
|
||||
node->custom1 = true; /* use_clamp */
|
||||
node->custom2 = NODE_MAP_RANGE_LINEAR; /* interpolation */
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
class SocketSearchOp {
|
||||
public:
|
||||
UString socket_name;
|
||||
eCustomDataType data_type;
|
||||
int interpolation_type = NODE_MAP_RANGE_LINEAR;
|
||||
|
||||
void operator()(LinkSearchOpParams ¶ms)
|
||||
{
|
||||
bNode &node = params.add_node("ShaderNodeMapRange"_ustr);
|
||||
node_storage(node).data_type = data_type;
|
||||
node_storage(node).interpolation_type = interpolation_type;
|
||||
params.update_and_connect_available_socket(node, socket_name);
|
||||
}
|
||||
};
|
||||
|
||||
static std::optional<eCustomDataType> node_type_from_other_socket(const bNodeSocket &socket)
|
||||
{
|
||||
switch (socket.type) {
|
||||
case SOCK_FLOAT:
|
||||
case SOCK_BOOLEAN:
|
||||
case SOCK_INT:
|
||||
return CD_PROP_FLOAT;
|
||||
case SOCK_VECTOR:
|
||||
case SOCK_RGBA:
|
||||
return CD_PROP_FLOAT3;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static void node_map_range_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
const std::optional<eCustomDataType> type = node_type_from_other_socket(params.other_socket());
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.in_out() == SOCK_IN) {
|
||||
if (*type == CD_PROP_FLOAT3) {
|
||||
params.add_item(IFACE_("Vector"), SocketSearchOp{"Vector"_ustr, *type}, 0);
|
||||
}
|
||||
else {
|
||||
params.add_item(IFACE_("Value"), SocketSearchOp{"Value"_ustr, *type}, 0);
|
||||
}
|
||||
params.add_item(IFACE_("From Min"), SocketSearchOp{"From Min"_ustr, *type}, -1);
|
||||
params.add_item(IFACE_("From Max"), SocketSearchOp{"From Max"_ustr, *type}, -1);
|
||||
params.add_item(IFACE_("To Min"), SocketSearchOp{"To Min"_ustr, *type}, -2);
|
||||
params.add_item(IFACE_("To Max"), SocketSearchOp{"To Max"_ustr, *type}, -2);
|
||||
params.add_item(
|
||||
IFACE_("Steps"), SocketSearchOp{"Steps"_ustr, *type, NODE_MAP_RANGE_STEPPED}, -3);
|
||||
}
|
||||
else {
|
||||
if (*type == CD_PROP_FLOAT3) {
|
||||
params.add_item(IFACE_("Vector"), SocketSearchOp{"Vector"_ustr, *type});
|
||||
}
|
||||
else {
|
||||
params.add_item(IFACE_("Result"), SocketSearchOp{"Result"_ustr, *type});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(int mode, bool use_vector)
|
||||
{
|
||||
if (use_vector) {
|
||||
switch (mode) {
|
||||
case NODE_MAP_RANGE_LINEAR:
|
||||
return "vector_map_range_linear";
|
||||
case NODE_MAP_RANGE_STEPPED:
|
||||
return "vector_map_range_stepped";
|
||||
case NODE_MAP_RANGE_SMOOTHSTEP:
|
||||
return "vector_map_range_smoothstep";
|
||||
case NODE_MAP_RANGE_SMOOTHERSTEP:
|
||||
return "vector_map_range_smootherstep";
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (mode) {
|
||||
case NODE_MAP_RANGE_LINEAR:
|
||||
return "map_range_linear";
|
||||
case NODE_MAP_RANGE_STEPPED:
|
||||
return "map_range_stepped";
|
||||
case NODE_MAP_RANGE_SMOOTHSTEP:
|
||||
return "map_range_smoothstep";
|
||||
case NODE_MAP_RANGE_SMOOTHERSTEP:
|
||||
return "map_range_smootherstep";
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int gpu_shader_map_range(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const NodeMapRange &storage = node_storage(*node);
|
||||
bool use_vector = (storage.data_type == CD_PROP_FLOAT3);
|
||||
const char *name = gpu_shader_get_name(storage.interpolation_type, use_vector);
|
||||
float clamp = storage.clamp ? 1.0f : 0.0f;
|
||||
int ret = 0;
|
||||
if (name != nullptr) {
|
||||
ret = GPU_stack_link(mat, node, name, in, out, GPU_constant(&clamp));
|
||||
}
|
||||
else {
|
||||
ret = GPU_stack_link(mat, node, "map_range_linear", in, out, GPU_constant(&clamp));
|
||||
}
|
||||
if (ret && storage.clamp && !use_vector &&
|
||||
!ELEM(storage.interpolation_type, NODE_MAP_RANGE_SMOOTHSTEP, NODE_MAP_RANGE_SMOOTHERSTEP))
|
||||
{
|
||||
GPU_link(mat, "clamp_range", out[0].link, in[3].link, in[4].link, &out[0].link);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline float clamp_range(const float value, const float min, const float max)
|
||||
{
|
||||
return (min > max) ? std::clamp(value, max, min) : std::clamp(value, min, max);
|
||||
}
|
||||
|
||||
static float3 clamp_range(const float3 value, const float3 min, const float3 max)
|
||||
{
|
||||
return float3(clamp_range(value.x, min.x, max.x),
|
||||
clamp_range(value.y, min.y, max.y),
|
||||
clamp_range(value.z, min.z, max.z));
|
||||
}
|
||||
|
||||
template<bool Clamp> static auto build_float_linear()
|
||||
{
|
||||
return mf::build::SI5_SO<float, float, float, float, float, float>(
|
||||
Clamp ? "Map Range (clamped)" : "Map Range (unclamped)",
|
||||
[](float value, float from_min, float from_max, float to_min, float to_max) -> float {
|
||||
const float factor = safe_divide(value - from_min, from_max - from_min);
|
||||
float result = to_min + factor * (to_max - to_min);
|
||||
if constexpr (Clamp) {
|
||||
result = clamp_range(result, to_min, to_max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<0>());
|
||||
}
|
||||
|
||||
template<bool Clamp> static auto build_float_stepped()
|
||||
{
|
||||
return mf::build::SI6_SO<float, float, float, float, float, float, float>(
|
||||
Clamp ? "Map Range Stepped (clamped)" : "Map Range Stepped (unclamped)",
|
||||
[](float value, float from_min, float from_max, float to_min, float to_max, float steps)
|
||||
-> float {
|
||||
float factor = safe_divide(value - from_min, from_max - from_min);
|
||||
factor = safe_divide(floorf(factor * (steps + 1.0f)), steps);
|
||||
float result = to_min + factor * (to_max - to_min);
|
||||
if constexpr (Clamp) {
|
||||
result = clamp_range(result, to_min, to_max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<0>());
|
||||
}
|
||||
|
||||
template<bool Clamp> static auto build_vector_linear()
|
||||
{
|
||||
return mf::build::SI5_SO<float3, float3, float3, float3, float3, float3>(
|
||||
Clamp ? "Vector Map Range (clamped)" : "Vector Map Range (unclamped)",
|
||||
[](const float3 &value,
|
||||
const float3 &from_min,
|
||||
const float3 &from_max,
|
||||
const float3 &to_min,
|
||||
const float3 &to_max) -> float3 {
|
||||
float3 factor = math::safe_divide(value - from_min, from_max - from_min);
|
||||
float3 result = factor * (to_max - to_min) + to_min;
|
||||
if constexpr (Clamp) {
|
||||
result = clamp_range(result, to_min, to_max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<0>());
|
||||
}
|
||||
|
||||
template<bool Clamp> static auto build_vector_stepped()
|
||||
{
|
||||
return mf::build::SI6_SO<float3, float3, float3, float3, float3, float3, float3>(
|
||||
Clamp ? "Vector Map Range Stepped (clamped)" : "Vector Map Range Stepped (unclamped)",
|
||||
[](const float3 &value,
|
||||
const float3 &from_min,
|
||||
const float3 &from_max,
|
||||
const float3 &to_min,
|
||||
const float3 &to_max,
|
||||
const float3 &steps) -> float3 {
|
||||
float3 factor = math::safe_divide(value - from_min, from_max - from_min);
|
||||
factor = math::safe_divide(math::floor(factor * (steps + 1.0f)), steps);
|
||||
float3 result = factor * (to_max - to_min) + to_min;
|
||||
if constexpr (Clamp) {
|
||||
result = clamp_range(result, to_min, to_max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<0>());
|
||||
}
|
||||
|
||||
static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const NodeMapRange &storage = node_storage(builder.node());
|
||||
bool clamp = storage.clamp != 0;
|
||||
int interpolation_type = storage.interpolation_type;
|
||||
|
||||
switch (storage.data_type) {
|
||||
case CD_PROP_FLOAT3:
|
||||
switch (interpolation_type) {
|
||||
case NODE_MAP_RANGE_LINEAR: {
|
||||
if (clamp) {
|
||||
static auto fn = build_vector_linear<true>();
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
else {
|
||||
static auto fn = build_vector_linear<false>();
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_STEPPED: {
|
||||
if (clamp) {
|
||||
static auto fn = build_vector_stepped<true>();
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
else {
|
||||
static auto fn = build_vector_stepped<false>();
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_SMOOTHSTEP: {
|
||||
static auto fn = mf::build::SI5_SO<float3, float3, float3, float3, float3, float3>(
|
||||
"Vector Map Range Smoothstep",
|
||||
[](const float3 &value,
|
||||
const float3 &from_min,
|
||||
const float3 &from_max,
|
||||
const float3 &to_min,
|
||||
const float3 &to_max) -> float3 {
|
||||
float3 factor = math::safe_divide(value - from_min, from_max - from_min);
|
||||
clamp_v3(factor, 0.0f, 1.0f);
|
||||
factor = (float3(3.0f) - 2.0f * factor) * (factor * factor);
|
||||
return factor * (to_max - to_min) + to_min;
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<0>());
|
||||
builder.set_matching_fn(fn);
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_SMOOTHERSTEP: {
|
||||
static auto fn = mf::build::SI5_SO<float3, float3, float3, float3, float3, float3>(
|
||||
"Vector Map Range Smootherstep",
|
||||
[](const float3 &value,
|
||||
const float3 &from_min,
|
||||
const float3 &from_max,
|
||||
const float3 &to_min,
|
||||
const float3 &to_max) -> float3 {
|
||||
float3 factor = math::safe_divide(value - from_min, from_max - from_min);
|
||||
clamp_v3(factor, 0.0f, 1.0f);
|
||||
factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
|
||||
return factor * (to_max - to_min) + to_min;
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<0>());
|
||||
builder.set_matching_fn(fn);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CD_PROP_FLOAT:
|
||||
switch (interpolation_type) {
|
||||
case NODE_MAP_RANGE_LINEAR: {
|
||||
if (clamp) {
|
||||
static auto fn = build_float_linear<true>();
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
else {
|
||||
static auto fn = build_float_linear<false>();
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_STEPPED: {
|
||||
if (clamp) {
|
||||
static auto fn = build_float_stepped<true>();
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
else {
|
||||
static auto fn = build_float_stepped<false>();
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_SMOOTHSTEP: {
|
||||
static auto fn = mf::build::SI5_SO<float, float, float, float, float, float>(
|
||||
"Map Range Smoothstep",
|
||||
[](float value, float from_min, float from_max, float to_min, float to_max)
|
||||
-> float {
|
||||
float factor = safe_divide(value - from_min, from_max - from_min);
|
||||
factor = std::clamp(factor, 0.0f, 1.0f);
|
||||
factor = (3.0f - 2.0f * factor) * (factor * factor);
|
||||
return to_min + factor * (to_max - to_min);
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<0>());
|
||||
builder.set_matching_fn(fn);
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_SMOOTHERSTEP: {
|
||||
static auto fn = mf::build::SI5_SO<float, float, float, float, float, float>(
|
||||
"Map Range Smoothstep",
|
||||
[](float value, float from_min, float from_max, float to_min, float to_max)
|
||||
-> float {
|
||||
float factor = safe_divide(value - from_min, from_max - from_min);
|
||||
factor = std::clamp(factor, 0.0f, 1.0f);
|
||||
factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
|
||||
return to_min + factor * (to_max - to_min);
|
||||
},
|
||||
mf::build::exec_presets::SomeSpanOrSingle<0>());
|
||||
builder.set_matching_fn(fn);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
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();
|
||||
NodeItem steps = 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);
|
||||
if (map_range->interpolation_type == NODE_MAP_RANGE_STEPPED) {
|
||||
steps = get_input_value(5, 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);
|
||||
if (map_range->interpolation_type == NODE_MAP_RANGE_STEPPED) {
|
||||
steps = get_input_value(11, type);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
NodeItem res = empty();
|
||||
switch (map_range->interpolation_type) {
|
||||
case NODE_MAP_RANGE_LINEAR:
|
||||
res = create_node("remap",
|
||||
type,
|
||||
{{"in", value},
|
||||
{"inlow", from_min},
|
||||
{"inhigh", from_max},
|
||||
{"outlow", to_min},
|
||||
{"outhigh", to_max}});
|
||||
break;
|
||||
case NODE_MAP_RANGE_STEPPED: {
|
||||
NodeItem factor = create_node(
|
||||
"remap", type, {{"in", value}, {"inlow", from_min}, {"inhigh", from_max}});
|
||||
value = (factor * (steps + val(1.0f))).floor() / steps;
|
||||
res = create_node("remap", type, {{"in", value}, {"outlow", to_min}, {"outhigh", to_max}});
|
||||
break;
|
||||
}
|
||||
case NODE_MAP_RANGE_SMOOTHSTEP:
|
||||
case NODE_MAP_RANGE_SMOOTHERSTEP:
|
||||
value = create_node(
|
||||
"smoothstep", type, {{"in", value}, {"low", from_min}, {"high", from_max}});
|
||||
res = create_node("remap", type, {{"in", value}, {"outlow", to_min}, {"outhigh", to_max}});
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
if (map_range->clamp != 0) {
|
||||
res = res.clamp(to_min, to_max);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_map_range_cc
|
||||
|
||||
void register_node_type_sh_map_range()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_map_range_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeMapRange"_ustr, SH_NODE_MAP_RANGE);
|
||||
ntype.ui_name = "Map Range";
|
||||
ntype.ui_description = "Remap a value from a range to a target range";
|
||||
ntype.enum_name_legacy = "MAP_RANGE";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::sh_node_map_range_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_map_range;
|
||||
ntype.ui_class = file_ns::node_shader_map_range_ui_class;
|
||||
ntype.initfunc = file_ns::node_shader_init_map_range;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeMapRange", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.updatefunc = file_ns::node_shader_update_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;
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,140 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_mapping_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Vector>("Vector"_ustr)
|
||||
.default_value({0.0f, 0.0f, 0.0f})
|
||||
.min(-FLT_MAX)
|
||||
.max(FLT_MAX)
|
||||
.description("The vector to be transformed");
|
||||
b.add_input<decl::Vector>("Location"_ustr)
|
||||
.default_value({0.0f, 0.0f, 0.0f})
|
||||
.min(-FLT_MAX)
|
||||
.max(FLT_MAX)
|
||||
.subtype(PROP_TRANSLATION)
|
||||
.description("The amount of translation along each axis");
|
||||
b.add_input<decl::Vector>("Rotation"_ustr)
|
||||
.default_value({0.0f, 0.0f, 0.0f})
|
||||
.min(-FLT_MAX)
|
||||
.max(FLT_MAX)
|
||||
.subtype(PROP_EULER)
|
||||
.description("The amount of rotation along each axis, XYZ order");
|
||||
b.add_input<decl::Vector>("Scale"_ustr)
|
||||
.default_value({1.0f, 1.0f, 1.0f})
|
||||
.min(-FLT_MAX)
|
||||
.max(FLT_MAX)
|
||||
.subtype(PROP_XYZ)
|
||||
.description("The amount of scaling along each axis");
|
||||
b.add_output<decl::Vector>("Vector"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_mapping(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "vector_type", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case NODE_MAPPING_TYPE_POINT:
|
||||
return "mapping_point";
|
||||
case NODE_MAPPING_TYPE_TEXTURE:
|
||||
return "mapping_texture";
|
||||
case NODE_MAPPING_TYPE_VECTOR:
|
||||
return "mapping_vector";
|
||||
case NODE_MAPPING_TYPE_NORMAL:
|
||||
return "mapping_normal";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int gpu_shader_mapping(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (gpu_shader_get_name(node->custom1)) {
|
||||
return GPU_stack_link(mat, node, gpu_shader_get_name(node->custom1), in, out);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void node_shader_update_mapping(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
bNodeSocket *sock = bke::node_find_socket(*node, SOCK_IN, "Location"_ustr);
|
||||
bke::node_set_socket_availability(
|
||||
*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 nodes::node_shader_mapping_cc
|
||||
|
||||
void register_node_type_sh_mapping()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_mapping_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeMapping"_ustr, SH_NODE_MAPPING);
|
||||
ntype.ui_name = "Mapping";
|
||||
ntype.ui_description = "Transform the input vector by applying translation, rotation, and scale";
|
||||
ntype.enum_name_legacy = "MAPPING";
|
||||
ntype.nclass = NODE_CLASS_OP_VECTOR;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,478 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_node.hh"
|
||||
|
||||
#include "NOD_inverse_eval_params.hh"
|
||||
#include "NOD_math_functions.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
#include "NOD_value_elem_eval.hh"
|
||||
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* **************** SCALAR MATH ******************** */
|
||||
|
||||
namespace nodes::node_shader_math_cc {
|
||||
|
||||
static void sh_node_math_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("Value"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.label_fn([](const bNode &node) {
|
||||
switch (node.custom1) {
|
||||
case NODE_MATH_POWER:
|
||||
return IFACE_("Base");
|
||||
case NODE_MATH_DEGREES:
|
||||
return IFACE_("Radians");
|
||||
case NODE_MATH_RADIANS:
|
||||
return IFACE_("Degrees");
|
||||
default:
|
||||
return IFACE_("Value");
|
||||
}
|
||||
});
|
||||
b.add_input<decl::Float>("Value"_ustr, "Value_001"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.label_fn([](const bNode &node) {
|
||||
switch (node.custom1) {
|
||||
case NODE_MATH_WRAP:
|
||||
return IFACE_("Max");
|
||||
case NODE_MATH_MULTIPLY_ADD:
|
||||
return IFACE_("Multiplier");
|
||||
case NODE_MATH_LESS_THAN:
|
||||
case NODE_MATH_GREATER_THAN:
|
||||
return IFACE_("Threshold");
|
||||
case NODE_MATH_PINGPONG:
|
||||
return IFACE_("Scale");
|
||||
case NODE_MATH_SNAP:
|
||||
return IFACE_("Increment");
|
||||
case NODE_MATH_POWER:
|
||||
return IFACE_("Exponent");
|
||||
case NODE_MATH_LOGARITHM:
|
||||
return IFACE_("Base");
|
||||
default:
|
||||
return IFACE_("Value");
|
||||
}
|
||||
});
|
||||
b.add_input<decl::Float>("Value"_ustr, "Value_002"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.label_fn([](const bNode &node) {
|
||||
switch (node.custom1) {
|
||||
case NODE_MATH_WRAP:
|
||||
return IFACE_("Min");
|
||||
case NODE_MATH_MULTIPLY_ADD:
|
||||
return IFACE_("Addend");
|
||||
case NODE_MATH_COMPARE:
|
||||
return IFACE_("Epsilon");
|
||||
case NODE_MATH_SMOOTH_MAX:
|
||||
case NODE_MATH_SMOOTH_MIN:
|
||||
return IFACE_("Distance");
|
||||
default:
|
||||
return IFACE_("Value");
|
||||
}
|
||||
});
|
||||
b.add_output<decl::Float>("Value"_ustr);
|
||||
}
|
||||
|
||||
static void math_input_defaults(bNode &node, const NodeMathOperation mode)
|
||||
{
|
||||
bNodeSocket *socket_2 = bke::node_find_socket(node, SOCK_IN, "Value_001"_ustr);
|
||||
BLI_assert(socket_2 != nullptr);
|
||||
float &value_2 = socket_2->default_value_typed<bNodeSocketValueFloat>()->value;
|
||||
|
||||
bNodeSocket *socket_3 = bke::node_find_socket(node, SOCK_IN, "Value_002"_ustr);
|
||||
BLI_assert(socket_3 != nullptr);
|
||||
float &value_3 = socket_3->default_value_typed<bNodeSocketValueFloat>()->value;
|
||||
|
||||
switch (mode) {
|
||||
case NODE_MATH_MULTIPLY:
|
||||
case NODE_MATH_DIVIDE:
|
||||
case NODE_MATH_POWER:
|
||||
case NODE_MATH_FLOORED_MODULO:
|
||||
case NODE_MATH_MODULO:
|
||||
case NODE_MATH_ARCTAN2:
|
||||
value_2 = 1.0f;
|
||||
break;
|
||||
case NODE_MATH_ADD:
|
||||
case NODE_MATH_SUBTRACT:
|
||||
value_2 = 0.0f;
|
||||
break;
|
||||
case NODE_MATH_MULTIPLY_ADD:
|
||||
value_2 = 1.0f;
|
||||
value_3 = 0.0f;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Use the default defined in the node declaration otherwise. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class SocketSearchOp {
|
||||
public:
|
||||
UString socket_name;
|
||||
NodeMathOperation mode = NODE_MATH_ADD;
|
||||
void operator()(LinkSearchOpParams ¶ms)
|
||||
{
|
||||
bNode &node = params.add_node("ShaderNodeMath"_ustr);
|
||||
node.custom1 = mode;
|
||||
math_input_defaults(node, mode);
|
||||
params.update_and_connect_available_socket(node, socket_name);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
if (!params.node_tree().typeinfo->validate_link(params.other_socket().type, SOCK_FLOAT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_geometry_node_tree = params.node_tree().type == NTREE_GEOMETRY;
|
||||
const int weight = ELEM(params.other_socket().type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN) ? 0 : -1;
|
||||
|
||||
for (const EnumPropertyItem *item = rna_enum_node_math_items; item->identifier != nullptr;
|
||||
item++)
|
||||
{
|
||||
if (item->name != nullptr && item->identifier[0] != '\0') {
|
||||
const int gn_weight =
|
||||
(is_geometry_node_tree &&
|
||||
ELEM(item->value, NODE_MATH_COMPARE, NODE_MATH_GREATER_THAN, NODE_MATH_LESS_THAN)) ?
|
||||
-1 :
|
||||
weight;
|
||||
params.add_item(CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, item->name),
|
||||
SocketSearchOp{"Value"_ustr, NodeMathOperation(item->value)},
|
||||
gn_weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(int mode)
|
||||
{
|
||||
const FloatMathOperationInfo *info = get_float_math_operation_info(mode);
|
||||
if (!info) {
|
||||
return nullptr;
|
||||
}
|
||||
if (info->shader_name.is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return info->shader_name.c_str();
|
||||
}
|
||||
|
||||
static int gpu_shader_math(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const char *name = gpu_shader_get_name(node->custom1);
|
||||
if (name != nullptr) {
|
||||
int ret = GPU_stack_link(mat, node, name, in, out);
|
||||
|
||||
if (ret && node->custom2 & SHD_MATH_CLAMP) {
|
||||
float min[3] = {0.0f, 0.0f, 0.0f};
|
||||
float max[3] = {1.0f, 1.0f, 1.0f};
|
||||
GPU_link(
|
||||
mat, "clamp_value", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void node_eval_elem(value_elem::ElemEvalParams ¶ms)
|
||||
{
|
||||
using namespace value_elem;
|
||||
const NodeMathOperation op = NodeMathOperation(params.node.custom1);
|
||||
switch (op) {
|
||||
case NODE_MATH_ADD:
|
||||
case NODE_MATH_SUBTRACT:
|
||||
case NODE_MATH_MULTIPLY:
|
||||
case NODE_MATH_DIVIDE: {
|
||||
FloatElem output_elem = params.get_input_elem<FloatElem>("Value"_ustr);
|
||||
output_elem.merge(params.get_input_elem<FloatElem>("Value_001"_ustr));
|
||||
params.set_output_elem("Value"_ustr, output_elem);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void node_eval_inverse_elem(value_elem::InverseElemEvalParams ¶ms)
|
||||
{
|
||||
const NodeMathOperation op = NodeMathOperation(params.node.custom1);
|
||||
switch (op) {
|
||||
case NODE_MATH_ADD:
|
||||
case NODE_MATH_SUBTRACT:
|
||||
case NODE_MATH_MULTIPLY:
|
||||
case NODE_MATH_DIVIDE: {
|
||||
params.set_input_elem("Value"_ustr,
|
||||
params.get_output_elem<value_elem::FloatElem>("Value"_ustr));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void node_eval_inverse(inverse_eval::InverseEvalParams ¶ms)
|
||||
{
|
||||
const NodeMathOperation op = NodeMathOperation(params.node.custom1);
|
||||
const UString first_input_id = "Value"_ustr;
|
||||
const UString second_input_id = "Value_001"_ustr;
|
||||
const UString output_id = "Value"_ustr;
|
||||
switch (op) {
|
||||
case NODE_MATH_ADD: {
|
||||
params.set_input(first_input_id,
|
||||
params.get_output<float>(output_id) -
|
||||
params.get_input<float>(second_input_id));
|
||||
break;
|
||||
}
|
||||
case NODE_MATH_SUBTRACT: {
|
||||
params.set_input(first_input_id,
|
||||
params.get_output<float>(output_id) +
|
||||
params.get_input<float>(second_input_id));
|
||||
break;
|
||||
}
|
||||
case NODE_MATH_MULTIPLY: {
|
||||
params.set_input(first_input_id,
|
||||
math::safe_divide(params.get_output<float>(output_id),
|
||||
params.get_input<float>(second_input_id)));
|
||||
break;
|
||||
}
|
||||
case NODE_MATH_DIVIDE: {
|
||||
params.set_input(first_input_id,
|
||||
params.get_output<float>(output_id) *
|
||||
params.get_input<float>(second_input_id));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
NodeMathOperation op = NodeMathOperation(node_->custom1);
|
||||
NodeItem res = empty();
|
||||
|
||||
/* Single operand operations */
|
||||
NodeItem x = get_input_value(0, NodeItem::Type::Float);
|
||||
|
||||
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:
|
||||
res = (x / y).floor() * y;
|
||||
break;
|
||||
case NODE_MATH_PINGPONG: {
|
||||
NodeItem fract_part = (x - y) / (y * val(2.0f));
|
||||
NodeItem if_branch = ((fract_part - fract_part.floor()) * y * val(2.0f) - y).abs();
|
||||
res = y.if_else(NodeItem::CompareOp::NotEq, val(0.0f), if_branch, val(0.0f));
|
||||
break;
|
||||
}
|
||||
case NODE_MATH_FLOORED_MODULO:
|
||||
res = y.if_else(
|
||||
NodeItem::CompareOp::NotEq, val(0.0f), (x - (x / y).floor() * y), val(0.0f));
|
||||
break;
|
||||
|
||||
default: {
|
||||
/* 3-operand operations */
|
||||
NodeItem z = get_input_value(2, NodeItem::Type::Float);
|
||||
|
||||
switch (op) {
|
||||
case NODE_MATH_WRAP: {
|
||||
NodeItem range = (y - z);
|
||||
NodeItem if_branch = x - (range * ((x - z) / range).floor());
|
||||
res = range.if_else(NodeItem::CompareOp::NotEq, val(0.0f), if_branch, z);
|
||||
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:
|
||||
case NODE_MATH_SMOOTH_MAX: {
|
||||
auto make_smoothmin = [&](NodeItem a, NodeItem b, NodeItem k) {
|
||||
NodeItem h = (k - (a - b).abs()).max(val(0.0f)) / k;
|
||||
NodeItem if_branch = a.min(b) - h * h * h * k * val(1.0f / 6.0f);
|
||||
return k.if_else(NodeItem::CompareOp::NotEq, val(0.0f), if_branch, a.min(b));
|
||||
};
|
||||
if (op == NODE_MATH_SMOOTH_MIN) {
|
||||
res = make_smoothmin(x, y, z);
|
||||
}
|
||||
else {
|
||||
res = -make_smoothmin(-x, -y, -z);
|
||||
}
|
||||
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 nodes::node_shader_math_cc
|
||||
|
||||
void register_node_type_sh_math()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_math_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeMath"_ustr, SH_NODE_MATH);
|
||||
ntype.ui_name = "Math";
|
||||
ntype.ui_description = "Perform math operations";
|
||||
ntype.enum_name_legacy = "MATH";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::sh_node_math_declare;
|
||||
ntype.labelfunc = node_math_label;
|
||||
ntype.gpu_fn = file_ns::gpu_shader_math;
|
||||
ntype.updatefunc = node_math_update;
|
||||
ntype.build_multi_function = nodes::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;
|
||||
ntype.eval_elem = file_ns::node_eval_elem;
|
||||
ntype.eval_inverse_elem = file_ns::node_eval_inverse_elem;
|
||||
ntype.eval_inverse = file_ns::node_eval_inverse;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,700 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BKE_material.hh"
|
||||
|
||||
#include "BLI_math_quaternion.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "FN_multi_function_builder.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_sh_mix_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeShaderMix)
|
||||
|
||||
static void sh_node_mix_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
/* WARNING:
|
||||
* Input socket indices must be kept in sync with ntree_shader_disconnect_inactive_mix_branches
|
||||
*/
|
||||
b.add_input<decl::Float>("Factor"_ustr, "Factor_Float"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.no_muted_links()
|
||||
.description("Amount of mixing between the A and B inputs")
|
||||
.compositor_domain_priority(2);
|
||||
b.add_input<decl::Vector>("Factor"_ustr, "Factor_Vector"_ustr)
|
||||
.default_value(float3(0.5f))
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.no_muted_links()
|
||||
.description("Amount of mixing between the A and B vector inputs")
|
||||
.compositor_domain_priority(2);
|
||||
|
||||
b.add_input<decl::Float>("A"_ustr, "A_Float"_ustr)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.is_default_link_socket()
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE)
|
||||
.description("Value of the first floating number input")
|
||||
.compositor_domain_priority(0);
|
||||
b.add_input<decl::Float>("B"_ustr, "B_Float"_ustr)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE)
|
||||
.description("Value of the second floating number input")
|
||||
.compositor_domain_priority(1);
|
||||
|
||||
b.add_input<decl::Vector>("A"_ustr, "A_Vector"_ustr)
|
||||
.is_default_link_socket()
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE)
|
||||
.description("Value of the first vector input")
|
||||
.compositor_domain_priority(0);
|
||||
b.add_input<decl::Vector>("B"_ustr, "B_Vector"_ustr)
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE)
|
||||
.description("Value of the second vector input")
|
||||
.compositor_domain_priority(1);
|
||||
|
||||
b.add_input<decl::Color>("A"_ustr, "A_Color"_ustr)
|
||||
.default_value({0.5f, 0.5f, 0.5f, 1.0f})
|
||||
.is_default_link_socket()
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE)
|
||||
.description("Value of the first color input")
|
||||
.compositor_domain_priority(0);
|
||||
b.add_input<decl::Color>("B"_ustr, "B_Color"_ustr)
|
||||
.default_value({0.5f, 0.5f, 0.5f, 1.0f})
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE)
|
||||
.description("Value of the second color input")
|
||||
.compositor_domain_priority(1);
|
||||
|
||||
b.add_input<decl::Rotation>("A"_ustr, "A_Rotation"_ustr)
|
||||
.is_default_link_socket()
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE)
|
||||
.compositor_domain_priority(0);
|
||||
b.add_input<decl::Rotation>("B"_ustr, "B_Rotation"_ustr)
|
||||
.translation_context(BLT_I18NCONTEXT_ID_NODETREE)
|
||||
.compositor_domain_priority(1);
|
||||
|
||||
b.add_output<decl::Float>("Result"_ustr, "Result_Float"_ustr);
|
||||
b.add_output<decl::Vector>("Result"_ustr, "Result_Vector"_ustr);
|
||||
b.add_output<decl::Color>("Result"_ustr, "Result_Color"_ustr);
|
||||
b.add_output<decl::Rotation>("Result"_ustr, "Result_Rotation"_ustr);
|
||||
};
|
||||
|
||||
static void sh_node_mix_layout(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
const NodeShaderMix &data = node_storage(*static_cast<const bNode *>(ptr->data));
|
||||
layout.prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
|
||||
switch (data.data_type) {
|
||||
case SOCK_FLOAT:
|
||||
break;
|
||||
case SOCK_VECTOR:
|
||||
layout.prop(ptr, "factor_mode", UI_ITEM_NONE, "", ICON_NONE);
|
||||
break;
|
||||
case SOCK_RGBA:
|
||||
layout.prop(ptr, "blend_type", UI_ITEM_NONE, "", ICON_NONE);
|
||||
layout.prop(ptr, "clamp_result", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
break;
|
||||
case SOCK_ROTATION:
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
layout.prop(ptr, "clamp_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
static void sh_node_mix_label(const bNodeTree * /*ntree*/,
|
||||
const bNode *node,
|
||||
char *label,
|
||||
int label_maxncpy)
|
||||
{
|
||||
const NodeShaderMix &storage = node_storage(*node);
|
||||
if (storage.data_type == SOCK_RGBA) {
|
||||
const char *name;
|
||||
bool enum_label = RNA_enum_name(rna_enum_ramp_blend_items, storage.blend_type, &name);
|
||||
if (!enum_label) {
|
||||
name = CTX_N_(BLT_I18NCONTEXT_COLOR, "Unknown");
|
||||
}
|
||||
BLI_strncpy_utf8(label, CTX_IFACE_(BLT_I18NCONTEXT_COLOR, name), label_maxncpy);
|
||||
return;
|
||||
}
|
||||
BLI_strncpy_utf8(label, IFACE_("Mix"), label_maxncpy);
|
||||
}
|
||||
|
||||
static int sh_node_mix_ui_class(const bNode *node)
|
||||
{
|
||||
const NodeShaderMix &storage = node_storage(*node);
|
||||
const eNodeSocketDatatype data_type = storage.data_type;
|
||||
|
||||
switch (data_type) {
|
||||
case SOCK_VECTOR:
|
||||
return NODE_CLASS_OP_VECTOR;
|
||||
case SOCK_RGBA:
|
||||
return NODE_CLASS_OP_COLOR;
|
||||
default:
|
||||
return NODE_CLASS_CONVERTER;
|
||||
}
|
||||
}
|
||||
|
||||
static void sh_node_mix_update(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
const NodeShaderMix &storage = node_storage(*node);
|
||||
const eNodeSocketDatatype data_type = storage.data_type;
|
||||
|
||||
bNodeSocket *sock_factor = static_cast<bNodeSocket *>(node->inputs.first);
|
||||
bNodeSocket *sock_factor_vec = static_cast<bNodeSocket *>(sock_factor->next);
|
||||
|
||||
bool use_vector_factor = data_type == SOCK_VECTOR &&
|
||||
storage.factor_mode != NODE_MIX_MODE_UNIFORM;
|
||||
|
||||
bke::node_set_socket_availability(*ntree, *sock_factor, !use_vector_factor);
|
||||
|
||||
bke::node_set_socket_availability(*ntree, *sock_factor_vec, use_vector_factor);
|
||||
|
||||
for (bNodeSocket *socket = sock_factor_vec->next; socket != nullptr; socket = socket->next) {
|
||||
bke::node_set_socket_availability(*ntree, *socket, socket->type == data_type);
|
||||
}
|
||||
|
||||
for (bNodeSocket &socket : node->outputs) {
|
||||
bke::node_set_socket_availability(*ntree, socket, socket.type == data_type);
|
||||
}
|
||||
}
|
||||
|
||||
class SocketSearchOp {
|
||||
public:
|
||||
UString socket_name;
|
||||
int type = MA_RAMP_BLEND;
|
||||
void operator()(LinkSearchOpParams ¶ms)
|
||||
{
|
||||
bNode &node = params.add_node("ShaderNodeMix"_ustr);
|
||||
node_storage(node).data_type = SOCK_RGBA;
|
||||
node_storage(node).blend_type = type;
|
||||
params.update_and_connect_available_socket(node, socket_name);
|
||||
}
|
||||
};
|
||||
|
||||
static void node_mix_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
eNodeSocketDatatype type;
|
||||
switch (params.other_socket().type) {
|
||||
case SOCK_BOOLEAN:
|
||||
case SOCK_INT:
|
||||
case SOCK_FLOAT:
|
||||
type = SOCK_FLOAT;
|
||||
break;
|
||||
case SOCK_VECTOR:
|
||||
type = SOCK_VECTOR;
|
||||
break;
|
||||
case SOCK_RGBA:
|
||||
type = SOCK_RGBA;
|
||||
break;
|
||||
case SOCK_ROTATION:
|
||||
type = SOCK_ROTATION;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure color math operations have higher priority than vector or float math operations. */
|
||||
int weight = type == SOCK_RGBA ? 4 : 0;
|
||||
if (params.in_out() == SOCK_OUT) {
|
||||
params.add_item(IFACE_("Result"), [type](LinkSearchOpParams ¶ms) {
|
||||
bNode &node = params.add_node("ShaderNodeMix"_ustr);
|
||||
node_storage(node).data_type = type;
|
||||
params.update_and_connect_available_socket(node, "Result"_ustr);
|
||||
});
|
||||
}
|
||||
else {
|
||||
params.add_item(
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, "A"),
|
||||
[type](LinkSearchOpParams ¶ms) {
|
||||
bNode &node = params.add_node("ShaderNodeMix"_ustr);
|
||||
node_storage(node).data_type = type;
|
||||
params.update_and_connect_available_socket(node, "A"_ustr);
|
||||
},
|
||||
weight);
|
||||
weight--;
|
||||
params.add_item(
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, "B"),
|
||||
[type](LinkSearchOpParams ¶ms) {
|
||||
bNode &node = params.add_node("ShaderNodeMix"_ustr);
|
||||
node_storage(node).data_type = type;
|
||||
params.update_and_connect_available_socket(node, "B"_ustr);
|
||||
},
|
||||
weight);
|
||||
weight--;
|
||||
if (ELEM(type, SOCK_VECTOR, SOCK_RGBA)) {
|
||||
params.add_item(
|
||||
IFACE_("Factor (Non-Uniform)"),
|
||||
[](LinkSearchOpParams ¶ms) {
|
||||
bNode &node = params.add_node("ShaderNodeMix"_ustr);
|
||||
node_storage(node).data_type = SOCK_VECTOR;
|
||||
node_storage(node).factor_mode = NODE_MIX_MODE_NON_UNIFORM;
|
||||
params.update_and_connect_available_socket(node, "Factor"_ustr);
|
||||
},
|
||||
weight);
|
||||
weight--;
|
||||
}
|
||||
if (type != SOCK_ROTATION) {
|
||||
params.add_item(
|
||||
IFACE_("Factor"),
|
||||
[type](LinkSearchOpParams ¶ms) {
|
||||
bNode &node = params.add_node("ShaderNodeMix"_ustr);
|
||||
node_storage(node).data_type = type;
|
||||
params.update_and_connect_available_socket(node, "Factor"_ustr);
|
||||
},
|
||||
weight);
|
||||
weight--;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == SOCK_ROTATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != SOCK_RGBA) {
|
||||
weight--;
|
||||
}
|
||||
const UString socket_name = params.in_out() == SOCK_IN ? "A"_ustr : "Result"_ustr;
|
||||
for (const EnumPropertyItem *item = rna_enum_ramp_blend_items; item->identifier != nullptr;
|
||||
item++)
|
||||
{
|
||||
if (item->name != nullptr && item->identifier[0] != '\0') {
|
||||
params.add_item(IFACE_(item->name), SocketSearchOp{socket_name, item->value}, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void node_mix_init(bNodeTree * /*tree*/, bNode *node)
|
||||
{
|
||||
NodeShaderMix *data = MEM_new<NodeShaderMix>(__func__);
|
||||
data->data_type = SOCK_FLOAT;
|
||||
data->factor_mode = NODE_MIX_MODE_UNIFORM;
|
||||
data->clamp_factor = 1;
|
||||
data->clamp_result = 0;
|
||||
data->blend_type = MA_RAMP_BLEND;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(eNodeSocketDatatype data_type,
|
||||
const bool non_uniform,
|
||||
const int blend_type)
|
||||
{
|
||||
switch (data_type) {
|
||||
case SOCK_FLOAT:
|
||||
return "node_mix_float";
|
||||
case SOCK_VECTOR:
|
||||
return (non_uniform) ? "node_mix_vector_non_uniform" : "node_mix_vector";
|
||||
case SOCK_RGBA:
|
||||
switch (blend_type) {
|
||||
case MA_RAMP_BLEND:
|
||||
return "node_mix_blend";
|
||||
case MA_RAMP_ADD:
|
||||
return "node_mix_add";
|
||||
case MA_RAMP_MULT:
|
||||
return "node_mix_mult";
|
||||
case MA_RAMP_SUB:
|
||||
return "node_mix_sub";
|
||||
case MA_RAMP_SCREEN:
|
||||
return "node_mix_screen";
|
||||
case MA_RAMP_DIV:
|
||||
return "node_mix_div_fallback";
|
||||
case MA_RAMP_DIFF:
|
||||
return "node_mix_diff";
|
||||
case MA_RAMP_EXCLUSION:
|
||||
return "node_mix_exclusion";
|
||||
case MA_RAMP_DARK:
|
||||
return "node_mix_dark";
|
||||
case MA_RAMP_LIGHT:
|
||||
return "node_mix_light";
|
||||
case MA_RAMP_OVERLAY:
|
||||
return "node_mix_overlay";
|
||||
case MA_RAMP_DODGE:
|
||||
return "node_mix_dodge";
|
||||
case MA_RAMP_BURN:
|
||||
return "node_mix_burn";
|
||||
case MA_RAMP_HUE:
|
||||
return "node_mix_hue";
|
||||
case MA_RAMP_SAT:
|
||||
return "node_mix_sat";
|
||||
case MA_RAMP_VAL:
|
||||
return "node_mix_val";
|
||||
case MA_RAMP_COLOR:
|
||||
return "node_mix_color";
|
||||
case MA_RAMP_SOFT:
|
||||
return "node_mix_soft";
|
||||
case MA_RAMP_LINEAR:
|
||||
return "node_mix_linear";
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
case SOCK_ROTATION:
|
||||
return "node_mix_rotation";
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static int gpu_shader_mix(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const NodeShaderMix &storage = node_storage(*node);
|
||||
const bool is_non_uniform = storage.factor_mode == NODE_MIX_MODE_NON_UNIFORM;
|
||||
const bool is_color_mode = storage.data_type == SOCK_RGBA;
|
||||
const bool is_vector_mode = storage.data_type == SOCK_VECTOR;
|
||||
const int blend_type = storage.blend_type;
|
||||
const char *name = gpu_shader_get_name(storage.data_type, is_non_uniform, blend_type);
|
||||
|
||||
if (name == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (storage.clamp_factor) {
|
||||
if (is_non_uniform && is_vector_mode) {
|
||||
const float min[3] = {0.0f, 0.0f, 0.0f};
|
||||
const float max[3] = {1.0f, 1.0f, 1.0f};
|
||||
const GPUNodeLink *factor_link = in[1].link ? in[1].link : GPU_uniform(in[1].vec);
|
||||
GPU_link(mat,
|
||||
"node_mix_clamp_vector",
|
||||
factor_link,
|
||||
GPU_constant(min),
|
||||
GPU_constant(max),
|
||||
&in[1].link);
|
||||
}
|
||||
else {
|
||||
const float min = 0.0f;
|
||||
const float max = 1.0f;
|
||||
const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
|
||||
GPU_link(mat,
|
||||
"node_mix_clamp_value",
|
||||
factor_link,
|
||||
GPU_constant(&min),
|
||||
GPU_constant(&max),
|
||||
&in[0].link);
|
||||
}
|
||||
}
|
||||
|
||||
/* Avoid GPU_stack_link here because it creates uniforms for unavailable sockets. */
|
||||
int ret = 0;
|
||||
switch (storage.data_type) {
|
||||
case SOCK_FLOAT:
|
||||
ret = GPU_link(mat,
|
||||
name,
|
||||
GPU_node_get_input_link(*node, in, "Factor_Float"),
|
||||
GPU_node_get_input_link(*node, in, "A_Float"),
|
||||
GPU_node_get_input_link(*node, in, "B_Float"),
|
||||
&GPU_node_get_output(*node, out, "Result_Float").link);
|
||||
break;
|
||||
case SOCK_VECTOR:
|
||||
if (is_non_uniform) {
|
||||
ret = GPU_link(mat,
|
||||
name,
|
||||
GPU_node_get_input_link(*node, in, "Factor_Vector"),
|
||||
GPU_node_get_input_link(*node, in, "A_Vector"),
|
||||
GPU_node_get_input_link(*node, in, "B_Vector"),
|
||||
&GPU_node_get_output(*node, out, "Result_Vector").link);
|
||||
}
|
||||
else {
|
||||
ret = GPU_link(mat,
|
||||
name,
|
||||
GPU_node_get_input_link(*node, in, "Factor_Float"),
|
||||
GPU_node_get_input_link(*node, in, "A_Vector"),
|
||||
GPU_node_get_input_link(*node, in, "B_Vector"),
|
||||
&GPU_node_get_output(*node, out, "Result_Vector").link);
|
||||
}
|
||||
break;
|
||||
case SOCK_RGBA:
|
||||
ret = GPU_link(mat,
|
||||
name,
|
||||
GPU_node_get_input_link(*node, in, "Factor_Float"),
|
||||
GPU_node_get_input_link(*node, in, "A_Color"),
|
||||
GPU_node_get_input_link(*node, in, "B_Color"),
|
||||
&GPU_node_get_output(*node, out, "Result_Color").link);
|
||||
break;
|
||||
case SOCK_ROTATION:
|
||||
ret = GPU_link(mat,
|
||||
name,
|
||||
GPU_node_get_input_link(*node, in, "Factor_Float"),
|
||||
GPU_node_get_input_link(*node, in, "A_Rotation"),
|
||||
GPU_node_get_input_link(*node, in, "B_Rotation"),
|
||||
&GPU_node_get_output(*node, out, "Result_Rotation").link);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret && is_color_mode && storage.clamp_result) {
|
||||
const float min[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
const float max[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
GPUNodeStack &result = GPU_node_get_output(*node, out, "Result_Color");
|
||||
GPU_link(mat,
|
||||
"node_mix_clamp_color",
|
||||
result.link,
|
||||
GPU_constant(min),
|
||||
GPU_constant(max),
|
||||
&result.link);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
class MixColorFunction : public mf::MultiFunction {
|
||||
private:
|
||||
const bool clamp_factor_;
|
||||
const bool clamp_result_;
|
||||
const int blend_type_;
|
||||
|
||||
public:
|
||||
MixColorFunction(const bool clamp_factor, const bool clamp_result, const int blend_type)
|
||||
: clamp_factor_(clamp_factor), clamp_result_(clamp_result), blend_type_(blend_type)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"MixColor", signature};
|
||||
builder.single_input<float>("Factor");
|
||||
builder.single_input<ColorGeometry4f>("A");
|
||||
builder.single_input<ColorGeometry4f>("B");
|
||||
builder.single_output<ColorGeometry4f>("Result");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float> &fac = params.readonly_single_input<float>(0, "Factor");
|
||||
const VArray<ColorGeometry4f> &col1 = params.readonly_single_input<ColorGeometry4f>(1, "A");
|
||||
const VArray<ColorGeometry4f> &col2 = params.readonly_single_input<ColorGeometry4f>(2, "B");
|
||||
MutableSpan<ColorGeometry4f> results = params.uninitialized_single_output<ColorGeometry4f>(
|
||||
3, "Result");
|
||||
|
||||
if (clamp_factor_) {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
results[i] = col1[i];
|
||||
ramp_blend(blend_type_, results[i], std::clamp(fac[i], 0.0f, 1.0f), col2[i]);
|
||||
});
|
||||
}
|
||||
else {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
results[i] = col1[i];
|
||||
ramp_blend(blend_type_, results[i], fac[i], col2[i]);
|
||||
});
|
||||
}
|
||||
|
||||
if (clamp_result_) {
|
||||
mask.foreach_index_optimized<int64_t>(
|
||||
[&](const int64_t i) { clamp_v4(results[i], 0.0f, 1.0f); });
|
||||
}
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(clamp_factor_);
|
||||
hash.add(clamp_result_);
|
||||
hash.add(blend_type_);
|
||||
}
|
||||
};
|
||||
|
||||
static const mf::MultiFunction *get_multi_function(const bNode &node)
|
||||
{
|
||||
const NodeShaderMix *data = static_cast<NodeShaderMix *>(node.storage);
|
||||
bool uniform_factor = data->factor_mode == NODE_MIX_MODE_UNIFORM;
|
||||
const bool clamp_factor = data->clamp_factor;
|
||||
switch (data->data_type) {
|
||||
case SOCK_FLOAT: {
|
||||
if (clamp_factor) {
|
||||
static auto fn = mf::build::SI3_SO<float, float, float, float>(
|
||||
"Clamp Mix Float", [](float t, const float a, const float b) {
|
||||
return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
|
||||
});
|
||||
return &fn;
|
||||
}
|
||||
static auto fn = mf::build::SI3_SO<float, float, float, float>(
|
||||
"Mix Float",
|
||||
[](const float t, const float a, const float b) { return math::interpolate(a, b, t); });
|
||||
return &fn;
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
if (clamp_factor) {
|
||||
if (uniform_factor) {
|
||||
static auto fn = mf::build::SI3_SO<float, float3, float3, float3>(
|
||||
"Clamp Mix Vector", [](const float t, const float3 a, const float3 b) {
|
||||
return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
|
||||
});
|
||||
return &fn;
|
||||
}
|
||||
static auto fn = mf::build::SI3_SO<float3, float3, float3, float3>(
|
||||
"Clamp Mix Vector Non Uniform", [](float3 t, const float3 a, const float3 b) {
|
||||
t = math::clamp(t, 0.0f, 1.0f);
|
||||
return a * (float3(1.0f) - t) + b * t;
|
||||
});
|
||||
return &fn;
|
||||
}
|
||||
if (uniform_factor) {
|
||||
static auto fn = mf::build::SI3_SO<float, float3, float3, float3>(
|
||||
"Mix Vector", [](const float t, const float3 a, const float3 b) {
|
||||
return math::interpolate(a, b, t);
|
||||
});
|
||||
return &fn;
|
||||
}
|
||||
static auto fn = mf::build::SI3_SO<float3, float3, float3, float3>(
|
||||
"Mix Vector Non Uniform", [](const float3 t, const float3 a, const float3 b) {
|
||||
return a * (float3(1.0f) - t) + b * t;
|
||||
});
|
||||
return &fn;
|
||||
}
|
||||
case SOCK_ROTATION: {
|
||||
if (clamp_factor) {
|
||||
static auto fn =
|
||||
mf::build::SI3_SO<float, math::Quaternion, math::Quaternion, math::Quaternion>(
|
||||
"Clamp Mix Rotation",
|
||||
[](const float t, const math::Quaternion &a, const math::Quaternion &b) {
|
||||
return math::interpolate(a, b, math::clamp(t, 0.0f, 1.0f));
|
||||
});
|
||||
return &fn;
|
||||
}
|
||||
static auto fn =
|
||||
mf::build::SI3_SO<float, math::Quaternion, math::Quaternion, math::Quaternion>(
|
||||
"Mix Rotation",
|
||||
[](const float t, const math::Quaternion &a, const math::Quaternion &b) {
|
||||
return math::interpolate(a, b, t);
|
||||
});
|
||||
return &fn;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sh_node_mix_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const NodeShaderMix &storage = node_storage(builder.node());
|
||||
|
||||
if (storage.data_type == SOCK_RGBA) {
|
||||
builder.construct_and_set_matching_fn<MixColorFunction>(
|
||||
storage.clamp_factor, storage.clamp_result, storage.blend_type);
|
||||
}
|
||||
else {
|
||||
const mf::MultiFunction *fn = get_multi_function(builder.node());
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
}
|
||||
|
||||
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::Color3);
|
||||
value2 = get_input_value(7, NodeItem::Type::Color3);
|
||||
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 nodes::node_sh_mix_cc
|
||||
|
||||
void register_node_type_sh_mix()
|
||||
{
|
||||
namespace file_ns = nodes::node_sh_mix_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
common_node_type_base(&ntype, "ShaderNodeMix"_ustr, SH_NODE_MIX);
|
||||
ntype.ui_name = "Mix";
|
||||
ntype.ui_description = "Mix values by a factor";
|
||||
ntype.enum_name_legacy = "MIX";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::sh_node_mix_declare;
|
||||
ntype.ui_class = file_ns::sh_node_mix_ui_class;
|
||||
ntype.gpu_fn = file_ns::gpu_shader_mix;
|
||||
ntype.updatefunc = file_ns::sh_node_mix_update;
|
||||
ntype.initfunc = file_ns::node_mix_init;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeShaderMix", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.build_multi_function = file_ns::sh_node_mix_build_multi_function;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,181 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_material.hh"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_mix_rgb_cc {
|
||||
|
||||
static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("Factor"_ustr, "Fac"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Color>("Color1"_ustr).default_value({0.5f, 0.5f, 0.5f, 1.0f});
|
||||
b.add_input<decl::Color>("Color2"_ustr).default_value({0.5f, 0.5f, 0.5f, 1.0f});
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case MA_RAMP_BLEND:
|
||||
return "mix_blend";
|
||||
case MA_RAMP_ADD:
|
||||
return "mix_add";
|
||||
case MA_RAMP_MULT:
|
||||
return "mix_mult";
|
||||
case MA_RAMP_SUB:
|
||||
return "mix_sub";
|
||||
case MA_RAMP_SCREEN:
|
||||
return "mix_screen";
|
||||
case MA_RAMP_DIV:
|
||||
return "mix_div_fallback";
|
||||
case MA_RAMP_DIFF:
|
||||
return "mix_diff";
|
||||
case MA_RAMP_EXCLUSION:
|
||||
return "mix_exclusion";
|
||||
case MA_RAMP_DARK:
|
||||
return "mix_dark";
|
||||
case MA_RAMP_LIGHT:
|
||||
return "mix_light";
|
||||
case MA_RAMP_OVERLAY:
|
||||
return "mix_overlay";
|
||||
case MA_RAMP_DODGE:
|
||||
return "mix_dodge";
|
||||
case MA_RAMP_BURN:
|
||||
return "mix_burn";
|
||||
case MA_RAMP_HUE:
|
||||
return "mix_hue";
|
||||
case MA_RAMP_SAT:
|
||||
return "mix_sat";
|
||||
case MA_RAMP_VAL:
|
||||
return "mix_val";
|
||||
case MA_RAMP_COLOR:
|
||||
return "mix_color";
|
||||
case MA_RAMP_SOFT:
|
||||
return "mix_soft";
|
||||
case MA_RAMP_LINEAR:
|
||||
return "mix_linear";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int gpu_shader_mix_rgb(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const char *name = gpu_shader_get_name(node->custom1);
|
||||
|
||||
if (name == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const float min = 0.0f;
|
||||
const float max = 1.0f;
|
||||
const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
|
||||
GPU_link(mat, "clamp_value", factor_link, GPU_constant(&min), GPU_constant(&max), &in[0].link);
|
||||
|
||||
int ret = GPU_stack_link(mat, node, name, in, out);
|
||||
|
||||
if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
|
||||
const float min[3] = {0.0f, 0.0f, 0.0f};
|
||||
const float max[3] = {1.0f, 1.0f, 1.0f};
|
||||
GPU_link(mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
class MixRGBFunction : public mf::MultiFunction {
|
||||
private:
|
||||
bool clamp_;
|
||||
int type_;
|
||||
|
||||
public:
|
||||
MixRGBFunction(bool clamp, int type) : clamp_(clamp), type_(type)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"MixRGB", signature};
|
||||
builder.single_input<float>("Fac");
|
||||
builder.single_input<ColorGeometry4f>("Color1");
|
||||
builder.single_input<ColorGeometry4f>("Color2");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
|
||||
const VArray<ColorGeometry4f> &col1 = params.readonly_single_input<ColorGeometry4f>(1,
|
||||
"Color1");
|
||||
const VArray<ColorGeometry4f> &col2 = params.readonly_single_input<ColorGeometry4f>(2,
|
||||
"Color2");
|
||||
MutableSpan<ColorGeometry4f> results = params.uninitialized_single_output<ColorGeometry4f>(
|
||||
3, "Color");
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
results[i] = col1[i];
|
||||
ramp_blend(type_, results[i], clamp_f(fac[i], 0.0f, 1.0f), col2[i]);
|
||||
});
|
||||
|
||||
if (clamp_) {
|
||||
mask.foreach_index_optimized<int64_t>(
|
||||
[&](const int64_t i) { clamp_v4(results[i], 0.0f, 1.0f); });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_mix_rgb_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &node = builder.node();
|
||||
bool clamp = node.custom2 & SHD_MIXRGB_CLAMP;
|
||||
int mix_type = node.custom1;
|
||||
builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_mix_rgb_cc
|
||||
|
||||
void register_node_type_sh_mix_rgb()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_mix_rgb_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeMixRGB"_ustr, SH_NODE_MIX_RGB_LEGACY);
|
||||
ntype.ui_name = "Mix (Legacy)";
|
||||
ntype.ui_description = "Mix two input colors";
|
||||
ntype.enum_name_legacy = "MIX_RGB";
|
||||
ntype.nclass = NODE_CLASS_OP_COLOR;
|
||||
ntype.declare = file_ns::sh_node_mix_rgb_declare;
|
||||
ntype.labelfunc = node_blend_label;
|
||||
ntype.gpu_fn = file_ns::gpu_shader_mix_rgb;
|
||||
ntype.build_multi_function = file_ns::sh_node_mix_rgb_build_multi_function;
|
||||
ntype.gather_link_search_ops = nullptr;
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,82 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_mix_shader_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Factor"_ustr, "Fac"_ustr)
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Blend weight to use for mixing two shaders. "
|
||||
"At zero it uses the first shader entirely and at one the second shader");
|
||||
b.add_input<decl::Shader>("Shader"_ustr);
|
||||
b.add_input<decl::Shader>("Shader"_ustr, "Shader_001"_ustr);
|
||||
b.add_output<decl::Shader>("Shader"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_mix_shader(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
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 nodes::node_shader_mix_shader_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_mix_shader()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_mix_shader_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeMixShader"_ustr, SH_NODE_MIX_SHADER);
|
||||
ntype.ui_name = "Mix Shader";
|
||||
ntype.ui_description = "Mix two shaders together. Typically used for material layering";
|
||||
ntype.enum_name_legacy = "MIX_SHADER";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,78 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_normal_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Vector>("Normal"_ustr)
|
||||
.default_value({0.0f, 0.0f, 1.0f})
|
||||
.min(-1.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_DIRECTION)
|
||||
.description(
|
||||
"Normal direction vector.\n"
|
||||
"\u2022 LMB click and drag on the sphere to set the direction of the normal.\n"
|
||||
"\u2022 Holding Ctrl while dragging snaps to 45 degree rotation increments");
|
||||
b.add_output<decl::Vector>("Normal"_ustr)
|
||||
.default_value({0.0f, 0.0f, 1.0f})
|
||||
.min(-1.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_DIRECTION);
|
||||
b.add_output<decl::Float>("Dot"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_normal(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
GPUNodeLink *vec = GPU_uniform(out[0].vec);
|
||||
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_->identifier, "Dot")) {
|
||||
return res.dotproduct(get_input_value("Normal", NodeItem::Type::Vector3));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_normal_cc
|
||||
|
||||
void register_node_type_sh_normal()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_normal_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeNormal"_ustr, SH_NODE_NORMAL);
|
||||
ntype.ui_name = "Normal";
|
||||
ntype.ui_description = "Generate a normal vector and a dot product";
|
||||
ntype.enum_name_legacy = "NORMAL";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,226 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_normal_map_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Strength"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(10.0f)
|
||||
.description("Strength of the normal mapping effect")
|
||||
.translation_context(BLT_I18NCONTEXT_AMOUNT);
|
||||
b.add_input<decl::Color>("Color"_ustr)
|
||||
.default_value({0.5f, 0.5f, 1.0f, 1.0f})
|
||||
.description("Color that encodes the normal map in the specified space");
|
||||
b.add_output<decl::Vector>("Normal"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_normal_map(ui::Layout &layout, bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "space", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
layout.prop(ptr, "convention", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
|
||||
if (RNA_enum_get(ptr, "space") == SHD_SPACE_TANGENT) {
|
||||
layout.prop(ptr, "base", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
|
||||
PointerRNA obptr = CTX_data_pointer_get(C, "active_object");
|
||||
Object *object = static_cast<Object *>(obptr.data);
|
||||
|
||||
if (object && object->type == OB_MESH) {
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
|
||||
if (depsgraph) {
|
||||
Object *object_eval = DEG_get_evaluated(depsgraph, object);
|
||||
PointerRNA dataptr = RNA_id_pointer_create(object_eval->data);
|
||||
layout.prop_search(ptr, "uv_map", &dataptr, "uv_layers", "", ICON_GROUP_UVS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
layout.prop(ptr, "uv_map", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_normal_map(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeShaderNormalMap *attr = MEM_new<NodeShaderNormalMap>("NodeShaderNormalMap");
|
||||
node->storage = attr;
|
||||
}
|
||||
|
||||
static int gpu_shader_normal_map(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
NodeShaderNormalMap *nm = static_cast<NodeShaderNormalMap *>(node->storage);
|
||||
|
||||
GPUNodeLink *strength;
|
||||
if (in[0].link) {
|
||||
strength = in[0].link;
|
||||
}
|
||||
else {
|
||||
strength = GPU_uniform(in[0].vec);
|
||||
}
|
||||
|
||||
GPUNodeLink *newnormal;
|
||||
if (in[1].link) {
|
||||
newnormal = in[1].link;
|
||||
}
|
||||
else {
|
||||
newnormal = GPU_uniform(in[1].vec);
|
||||
}
|
||||
|
||||
const char *color_to_normal_fnc_name = "color_to_normal_new_shading";
|
||||
if (ELEM(nm->space, SHD_SPACE_BLENDER_OBJECT, SHD_SPACE_BLENDER_WORLD)) {
|
||||
color_to_normal_fnc_name = "color_to_blender_normal_new_shading";
|
||||
}
|
||||
|
||||
GPU_link(mat, color_to_normal_fnc_name, newnormal, &newnormal);
|
||||
|
||||
if (nm->convention == SHD_NORMAL_MAP_CONVENTION_DIRECTX) {
|
||||
GPU_link(mat, "color_invert_green_channel", newnormal, &newnormal);
|
||||
}
|
||||
|
||||
const char *input_fn_name = (nm->base == SHD_NORMAL_MAP_BASE_DISPLACED) ?
|
||||
"input_normal_displaced" :
|
||||
"input_normal_original";
|
||||
|
||||
GPUNodeLink *input_normal;
|
||||
GPU_link(mat, input_fn_name, &input_normal);
|
||||
|
||||
switch (nm->space) {
|
||||
case SHD_SPACE_TANGENT:
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
|
||||
/* We return directly from the node_normal_map as strength
|
||||
* has already been applied for the tangent case */
|
||||
GPU_link(mat,
|
||||
"node_normal_map",
|
||||
GPU_attribute(mat, CD_TANGENT, nm->uv_map),
|
||||
strength,
|
||||
newnormal,
|
||||
input_normal,
|
||||
&out[0].link);
|
||||
return true;
|
||||
case SHD_SPACE_OBJECT:
|
||||
case SHD_SPACE_BLENDER_OBJECT:
|
||||
GPU_link(mat, "normal_transform_object_to_world", newnormal, &newnormal);
|
||||
break;
|
||||
case SHD_SPACE_WORLD:
|
||||
case SHD_SPACE_BLENDER_WORLD:
|
||||
/* Nothing to do. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Final step - mix and apply strength for all other than tangent space. */
|
||||
GPU_link(mat, "node_normal_map_mix", strength, newnormal, &out[0].link);
|
||||
|
||||
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);
|
||||
|
||||
if (normal_map_node->convention == SHD_NORMAL_MAP_CONVENTION_DIRECTX) {
|
||||
NodeItem green_mask = val(MaterialX::Vector3(1.0f, -1.0f, 1.0f));
|
||||
color = color * green_mask;
|
||||
}
|
||||
|
||||
# if MATERIALX_MAJOR_VERSION <= 1 && MATERIALX_MINOR_VERSION <= 38
|
||||
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)}});
|
||||
# else
|
||||
if (normal_map_node->space == SHD_SPACE_TANGENT) {
|
||||
return create_node("normalmap", NodeItem::Type::Vector3, {{"in", color}, {"scale", strength}});
|
||||
}
|
||||
|
||||
/* Object space not supported yet. Despite the 1.38 implementation accepting
|
||||
* object space argument, that seems to work either. */
|
||||
NodeItem tangent = val(MaterialX::Vector3(1.0f, 0.0f, 0.0f));
|
||||
NodeItem bitangent = val(MaterialX::Vector3(0.0f, 1.0f, 0.0f));
|
||||
NodeItem normal = val(MaterialX::Vector3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
return create_node("normalmap",
|
||||
NodeItem::Type::Vector3,
|
||||
{{"in", color},
|
||||
{"scale", strength},
|
||||
{"tangent", tangent},
|
||||
{"bitangent", bitangent},
|
||||
{"normal", normal}});
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_normal_map_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_normal_map()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_normal_map_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeNormalMap"_ustr, SH_NODE_NORMAL_MAP);
|
||||
ntype.ui_name = "Normal Map";
|
||||
ntype.ui_description =
|
||||
"Generate a perturbed normal from an RGB normal map image. Typically used for faking highly "
|
||||
"detailed surfaces";
|
||||
ntype.enum_name_legacy = "NORMAL_MAP";
|
||||
ntype.nclass = NODE_CLASS_OP_VECTOR;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_normal_map;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.initfunc = file_ns::node_shader_init_normal_map;
|
||||
bke::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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,76 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_object_info_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Vector>("Location"_ustr);
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
b.add_output<decl::Float>("Alpha"_ustr);
|
||||
b.add_output<decl::Float>("Object Index"_ustr);
|
||||
b.add_output<decl::Float>("Material Index"_ustr);
|
||||
b.add_output<decl::Float>("Random"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_object_info(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
Material *ma = GPU_material_get_material(mat);
|
||||
float index = ma ? ma->index : 0.0f;
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
|
||||
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_->identifier;
|
||||
|
||||
if (name == "Location") {
|
||||
res = create_node("position", NodeItem::Type::Vector3, {{"space", val(std::string("world"))}});
|
||||
}
|
||||
else if (name == "Random") {
|
||||
res = create_node("randomfloat", NodeItem::Type::Float);
|
||||
}
|
||||
else {
|
||||
res = get_output_default(name, NodeItem::Type::Any);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_object_info_cc
|
||||
|
||||
void register_node_type_sh_object_info()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_object_info_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeObjectInfo"_ustr, SH_NODE_OBJECT_INFO);
|
||||
ntype.ui_name = "Object Info";
|
||||
ntype.ui_description = "Retrieve information about the object instance";
|
||||
ntype.enum_name_legacy = "OBJECT_INFO";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,121 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BLI_hash.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_output_aov_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.0f, 0.0f, 0.0f, 1.0f});
|
||||
b.add_input<decl::Float>("Value"_ustr).default_value(0.0f).min(0.0f).max(1.0f);
|
||||
}
|
||||
|
||||
static BIFIconID aov_icon(const ViewLayer *view_layer, PointerRNA *ptr)
|
||||
{
|
||||
std::string aov_name = RNA_string_get(ptr, "aov_name");
|
||||
if (aov_name.empty()) {
|
||||
return ICON_RECORD_OFF;
|
||||
}
|
||||
|
||||
const ViewLayerAOV *aov = static_cast<const ViewLayerAOV *>(
|
||||
BLI_findstring(&view_layer->aovs, aov_name.c_str(), offsetof(ViewLayerAOV, name)));
|
||||
|
||||
if (aov) {
|
||||
switch (aov->type) {
|
||||
case AOV_TYPE_COLOR:
|
||||
return ICON_NODE_SOCKET_RGBA;
|
||||
case AOV_TYPE_VALUE:
|
||||
return ICON_NODE_SOCKET_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
return ICON_RECORD_OFF;
|
||||
}
|
||||
|
||||
static void node_shader_buts_output_aov(ui::Layout &layout, bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
if (scene && view_layer) {
|
||||
PointerRNA view_layer_rna_ptr = RNA_pointer_create_id_subdata(
|
||||
scene->id, RNA_ViewLayer, view_layer);
|
||||
layout.prop_search(
|
||||
ptr, "aov_name", &view_layer_rna_ptr, "aovs", "", aov_icon(view_layer, ptr));
|
||||
}
|
||||
else {
|
||||
layout.prop(ptr, "aov_name", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_output_aov(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeShaderOutputAOV *aov = MEM_new<NodeShaderOutputAOV>("NodeShaderOutputAOV");
|
||||
node->storage = aov;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_output_aov(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
GPUNodeLink *outlink;
|
||||
NodeShaderOutputAOV *aov = static_cast<NodeShaderOutputAOV *>(node->storage);
|
||||
uint hash = BLI_hash_string(aov->name);
|
||||
/* WORKAROUND: We don't support int/uint constants for now. So make sure the aliasing works.
|
||||
* We cast back to uint in GLSL. */
|
||||
BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint),
|
||||
"GPUCodegen: AOV hash needs float and uint to be the same size.");
|
||||
GPUNodeLink *hash_link = GPU_constant(reinterpret_cast<float *>(&hash));
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_AOV | GPU_MATFLAG_OBJECT_INFO);
|
||||
GPU_stack_link(mat, node, "node_output_aov", in, out, hash_link, &outlink);
|
||||
GPU_material_add_output_link_aov(mat, outlink, hash);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_output_aov_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_output_aov()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_output_aov_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeOutputAOV"_ustr, SH_NODE_OUTPUT_AOV);
|
||||
ntype.ui_name = "AOV Output";
|
||||
ntype.ui_description =
|
||||
"Arbitrary Output Variables.\nProvide custom render passes for arbitrary shader node "
|
||||
"outputs";
|
||||
ntype.enum_name_legacy = "OUTPUT_AOV";
|
||||
ntype.nclass = NODE_CLASS_OUTPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_output_aov;
|
||||
ntype.initfunc = file_ns::node_shader_init_output_aov;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeShaderOutputAOV", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_output_aov;
|
||||
|
||||
ntype.no_muting = true;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,55 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_output_light_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Shader>("Surface"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_output_light(GPUMaterial *mat,
|
||||
bNode * /*node*/,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack * /*out*/)
|
||||
{
|
||||
GPUNodeLink *outlink_surface;
|
||||
/* Passthrough node in order to do the right socket conversions. */
|
||||
if (in[0].link) {
|
||||
/* Reuse material output. */
|
||||
GPU_link(mat, "node_output_material_surface", in[0].link, &outlink_surface);
|
||||
GPU_material_output_surface(mat, outlink_surface);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_output_light_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_output_light()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_output_light_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeOutputLight"_ustr, SH_NODE_OUTPUT_LIGHT);
|
||||
ntype.ui_name = "Light Output";
|
||||
ntype.ui_description = "Output light information to a light object";
|
||||
ntype.enum_name_legacy = "OUTPUT_LIGHT";
|
||||
ntype.nclass = NODE_CLASS_OUTPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.add_ui_poll = object_cycles_shader_nodes_poll;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_output_light;
|
||||
|
||||
ntype.no_muting = true;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_output_linestyle_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({1.0f, 0.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Float>("Color Fac"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("Alpha"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("Alpha Fac"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
}
|
||||
|
||||
static void node_buts_output_linestyle(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
ui::Layout &col = layout.column(false);
|
||||
ui::Layout &row = col.row(true);
|
||||
row.prop(ptr, "blend_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
col.prop(ptr, "use_clamp", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_output_linestyle_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_output_linestyle()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_output_linestyle_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeOutputLineStyle"_ustr, SH_NODE_OUTPUT_LINESTYLE);
|
||||
ntype.ui_name = "Line Style Output";
|
||||
ntype.ui_description =
|
||||
"Control the mixing of texture information into the base color of line styles";
|
||||
ntype.enum_name_legacy = "OUTPUT_LINESTYLE";
|
||||
ntype.nclass = NODE_CLASS_OUTPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.add_ui_poll = line_style_shader_nodes_poll;
|
||||
ntype.draw_buttons = file_ns::node_buts_output_linestyle;
|
||||
ntype.no_muting = true;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,98 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_output_material_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Shader>("Surface"_ustr);
|
||||
b.add_input<decl::Shader>("Volume"_ustr).translation_context(BLT_I18NCONTEXT_ID_ID);
|
||||
b.add_input<decl::Vector>("Displacement"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Thickness"_ustr).hide_value();
|
||||
}
|
||||
|
||||
static int node_shader_gpu_output_material(GPUMaterial *mat,
|
||||
bNode * /*node*/,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack * /*out*/)
|
||||
{
|
||||
GPUNodeLink *outlink_surface, *outlink_volume, *outlink_displacement, *outlink_thickness;
|
||||
/* Passthrough node in order to do the right socket conversions (important for displacement). */
|
||||
if (in[0].link) {
|
||||
GPU_link(mat, "node_output_material_surface", in[0].link, &outlink_surface);
|
||||
GPU_material_output_surface(mat, outlink_surface);
|
||||
}
|
||||
if (in[1].link) {
|
||||
GPU_link(mat, "node_output_material_volume", in[1].link, &outlink_volume);
|
||||
GPU_material_output_volume(mat, outlink_volume);
|
||||
}
|
||||
if (in[2].link) {
|
||||
GPU_link(mat, "node_output_material_displacement", in[2].link, &outlink_displacement);
|
||||
GPU_material_output_displacement(mat, outlink_displacement);
|
||||
}
|
||||
if (in[3].link) {
|
||||
GPU_link(mat, "node_output_material_thickness", in[3].link, &outlink_thickness);
|
||||
GPU_material_output_thickness(mat, outlink_thickness);
|
||||
}
|
||||
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}});
|
||||
}
|
||||
}
|
||||
|
||||
/* Displacement cannot be enabled just yet.
|
||||
* - Verify coordinate system for Tangent Space displacement maps
|
||||
* - Wait on fix for scalar displacement (present in USD 2408+)
|
||||
*/
|
||||
// NodeItem displacement = get_input_link("Displacement", NodeItem::Type::DisplacementShader);
|
||||
// return create_node("surfacematerial",
|
||||
// NodeItem::Type::Material,
|
||||
// {{"surfaceshader", surface}, {"displacementshader", displacement}});
|
||||
return create_node("surfacematerial", NodeItem::Type::Material, {{"surfaceshader", surface}});
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_output_material_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_output_material()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_output_material_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeOutputMaterial"_ustr, SH_NODE_OUTPUT_MATERIAL);
|
||||
ntype.ui_name = "Material Output";
|
||||
ntype.ui_description = "Output surface material information for use in rendering";
|
||||
ntype.enum_name_legacy = "OUTPUT_MATERIAL";
|
||||
ntype.nclass = NODE_CLASS_OUTPUT;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_output_world_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Shader>("Surface"_ustr);
|
||||
b.add_input<decl::Shader>("Volume"_ustr).translation_context(BLT_I18NCONTEXT_ID_ID);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_output_world(GPUMaterial *mat,
|
||||
bNode * /*node*/,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack * /*out*/)
|
||||
{
|
||||
GPUNodeLink *outlink_surface, *outlink_volume;
|
||||
if (in[0].link) {
|
||||
GPU_link(mat, "node_output_world_surface", in[0].link, &outlink_surface);
|
||||
GPU_material_output_surface(mat, outlink_surface);
|
||||
}
|
||||
if (in[1].link) {
|
||||
GPU_link(mat, "node_output_world_volume", in[1].link, &outlink_volume);
|
||||
GPU_material_output_volume(mat, outlink_volume);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_output_world_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_output_world()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_output_world_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeOutputWorld"_ustr, SH_NODE_OUTPUT_WORLD);
|
||||
ntype.ui_name = "World Output";
|
||||
ntype.ui_description = "Output light color information to the scene's World";
|
||||
ntype.enum_name_legacy = "OUTPUT_WORLD";
|
||||
ntype.nclass = NODE_CLASS_OUTPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.add_ui_poll = world_shader_nodes_poll;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_output_world;
|
||||
|
||||
ntype.no_muting = true;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,69 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_particle_info_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Float>("Index"_ustr);
|
||||
b.add_output<decl::Float>("Random"_ustr);
|
||||
b.add_output<decl::Float>("Age"_ustr);
|
||||
b.add_output<decl::Float>("Lifetime"_ustr);
|
||||
b.add_output<decl::Vector>("Location"_ustr);
|
||||
#if 0 /* quaternion sockets not yet supported */
|
||||
b.add_output<decl::Quaternion>("Rotation"_ustr);
|
||||
#endif
|
||||
b.add_output<decl::Float>("Size"_ustr);
|
||||
b.add_output<decl::Vector>("Velocity"_ustr);
|
||||
b.add_output<decl::Vector>("Angular Velocity"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_particle_info(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
|
||||
/* TODO(fclem) Pass particle data in obinfo. */
|
||||
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_->identifier, NodeItem::Type::Any);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_particle_info_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_particle_info()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_particle_info_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeParticleInfo"_ustr, SH_NODE_PARTICLE_INFO);
|
||||
ntype.ui_name = "Particle Info";
|
||||
ntype.ui_description =
|
||||
"Retrieve the data of the particle that spawned the object instance, for example to give "
|
||||
"variation to multiple instances of an object";
|
||||
ntype.enum_name_legacy = "PARTICLE_INFO";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_point_info_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Vector>("Position"_ustr);
|
||||
b.add_output<decl::Float>("Radius"_ustr);
|
||||
b.add_output<decl::Float>("Random"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_point_info(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
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_->identifier, NodeItem::Type::Any);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_point_info_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_point_info()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_point_info_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodePointInfo"_ustr, SH_NODE_POINT_INFO);
|
||||
ntype.ui_name = "Point Info";
|
||||
ntype.ui_description = "Retrieve information about points in a point cloud";
|
||||
ntype.enum_name_legacy = "POINT_INFO";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,240 @@
|
||||
/* SPDX-FileCopyrightText: 2024-2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BLI_radial_tiling.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_radial_tiling_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeRadialTiling)
|
||||
|
||||
static void sh_node_radial_tiling_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
b.add_output<decl::Vector>("Segment Coordinates"_ustr)
|
||||
.no_muted_links()
|
||||
.description("Segment coordinates for texture mapping within each angular segment");
|
||||
b.add_output<decl::Float>("Segment ID"_ustr)
|
||||
.no_muted_links()
|
||||
.description(
|
||||
"Unique ID for every angular segment starting at 0 and increasing counterclockwise by "
|
||||
"1");
|
||||
b.add_output<decl::Float>("Segment Width"_ustr)
|
||||
.no_muted_links()
|
||||
.description(
|
||||
"Relative width of each angular segment. "
|
||||
"May be used to scale textures to fit into each segment");
|
||||
b.add_output<decl::Float>("Segment Rotation"_ustr)
|
||||
.no_muted_links()
|
||||
.description(
|
||||
"Counterclockwise rotation of each segment coordinates system. May be used to align the "
|
||||
"rotation of the textures of each segment");
|
||||
|
||||
b.add_input<decl::Vector>("Vector"_ustr)
|
||||
.dimensions(2)
|
||||
.default_value(float3{0.0f, 0.0f, 0.0f})
|
||||
.description("Input texture coordinates");
|
||||
b.add_input<decl::Float>("Sides"_ustr)
|
||||
.min(2.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(5.0f)
|
||||
.description(
|
||||
"Number of angular segments for tiling. A non-integer value results in an irregular "
|
||||
"segment");
|
||||
b.add_input<decl::Float>("Roundness"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(0.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Roundness of the segment coordinates systems");
|
||||
}
|
||||
|
||||
static void node_shader_buts_radial_tiling(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "normalize", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_radial_tiling(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeRadialTiling *storage = MEM_new<NodeRadialTiling>(__func__);
|
||||
storage->normalize = false;
|
||||
|
||||
node->storage = storage;
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name()
|
||||
{
|
||||
return "node_radial_tiling";
|
||||
}
|
||||
|
||||
static int node_shader_gpu_radial_tiling(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const NodeRadialTiling &storage = node_storage(*node);
|
||||
float normalize_r_gon_parameter = storage.normalize;
|
||||
float calculate_r_gon_parameter_field = out[0].hasoutput;
|
||||
float calculate_segment_id = out[1].hasoutput;
|
||||
float calculate_max_unit_parameter = out[2].hasoutput;
|
||||
float calculate_x_axis_A_angle_bisector = out[3].hasoutput;
|
||||
|
||||
const char *name = gpu_shader_get_name();
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
name,
|
||||
in,
|
||||
out,
|
||||
GPU_constant(&normalize_r_gon_parameter),
|
||||
GPU_constant(&calculate_r_gon_parameter_field),
|
||||
GPU_constant(&calculate_segment_id),
|
||||
GPU_constant(&calculate_max_unit_parameter),
|
||||
GPU_constant(&calculate_x_axis_A_angle_bisector));
|
||||
}
|
||||
|
||||
class RoundedPolygonFunction : public mf::MultiFunction {
|
||||
private:
|
||||
bool normalize_r_gon_parameter_;
|
||||
|
||||
mf::Signature signature_;
|
||||
|
||||
public:
|
||||
RoundedPolygonFunction(bool normalize_r_gon_parameter)
|
||||
: normalize_r_gon_parameter_(normalize_r_gon_parameter)
|
||||
{
|
||||
signature_ = create_signature();
|
||||
this->set_signature(&signature_);
|
||||
}
|
||||
|
||||
static mf::Signature create_signature()
|
||||
{
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"radial_tiling", signature};
|
||||
|
||||
builder.single_input<float3>("Vector");
|
||||
|
||||
builder.single_input<float>("Sides");
|
||||
builder.single_input<float>("Roundness");
|
||||
|
||||
builder.single_output<float3>("Segment Coordinates", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Segment ID", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Segment Width", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Segment Rotation", mf::ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
int param = 0;
|
||||
|
||||
const VArray<float3> &coord = params.readonly_single_input<float3>(param++, "Vector");
|
||||
|
||||
const VArray<float> &r_gon_sides = params.readonly_single_input<float>(param++, "Sides");
|
||||
const VArray<float> &r_gon_roundness = params.readonly_single_input<float>(param++,
|
||||
"Roundness");
|
||||
|
||||
MutableSpan<float3> r_segment_coordinates =
|
||||
params.uninitialized_single_output_if_required<float3>(param++, "Segment Coordinates");
|
||||
MutableSpan<float> r_segment_id = params.uninitialized_single_output_if_required<float>(
|
||||
param++, "Segment ID");
|
||||
MutableSpan<float> r_max_unit_parameter =
|
||||
params.uninitialized_single_output_if_required<float>(param++, "Segment Width");
|
||||
MutableSpan<float> r_x_axis_A_angle_bisector =
|
||||
params.uninitialized_single_output_if_required<float>(param++, "Segment Rotation");
|
||||
|
||||
const bool calculate_r_gon_parameter_field = !r_segment_coordinates.is_empty();
|
||||
const bool calculate_segment_id = !r_segment_id.is_empty();
|
||||
const bool calculate_max_unit_parameter = !r_max_unit_parameter.is_empty();
|
||||
const bool calculate_x_axis_A_angle_bisector = !r_x_axis_A_angle_bisector.is_empty();
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
if (calculate_r_gon_parameter_field || calculate_max_unit_parameter ||
|
||||
calculate_x_axis_A_angle_bisector)
|
||||
{
|
||||
float4 out_variables = calculate_out_variables(calculate_r_gon_parameter_field,
|
||||
calculate_max_unit_parameter,
|
||||
normalize_r_gon_parameter_,
|
||||
math::max(r_gon_sides[i], 2.0f),
|
||||
math::clamp(r_gon_roundness[i], 0.0f, 1.0f),
|
||||
float2(coord[i].x, coord[i].y));
|
||||
|
||||
if (calculate_r_gon_parameter_field) {
|
||||
r_segment_coordinates[i] = float3(out_variables.y, out_variables.x, 0.0f);
|
||||
}
|
||||
if (calculate_max_unit_parameter) {
|
||||
r_max_unit_parameter[i] = out_variables.z;
|
||||
}
|
||||
if (calculate_x_axis_A_angle_bisector) {
|
||||
r_x_axis_A_angle_bisector[i] = out_variables.w;
|
||||
}
|
||||
}
|
||||
|
||||
if (calculate_segment_id) {
|
||||
r_segment_id[i] = calculate_out_segment_id(math::max(r_gon_sides[i], 2.0f),
|
||||
float2(coord[i].x, coord[i].y));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(normalize_r_gon_parameter_);
|
||||
}
|
||||
|
||||
ExecutionHints get_execution_hints() const override
|
||||
{
|
||||
ExecutionHints hints;
|
||||
hints.allocates_array = false;
|
||||
hints.min_grain_size = 50;
|
||||
return hints;
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_radial_tiling_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const NodeRadialTiling &storage = node_storage(builder.node());
|
||||
builder.construct_and_set_matching_fn<RoundedPolygonFunction>(storage.normalize);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_radial_tiling_cc
|
||||
|
||||
void register_node_type_sh_radial_tiling()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_radial_tiling_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeRadialTiling"_ustr);
|
||||
ntype.ui_name = "Radial Tiling";
|
||||
ntype.ui_description = "Transform Coordinate System for Radial Tiling";
|
||||
ntype.nclass = NODE_CLASS_OP_VECTOR;
|
||||
ntype.declare = file_ns::sh_node_radial_tiling_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_radial_tiling;
|
||||
ntype.initfunc = file_ns::node_shader_init_radial_tiling;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeRadialTiling", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_radial_tiling;
|
||||
ntype.build_multi_function = file_ns::sh_node_radial_tiling_build_multi_function;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,284 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "NOD_node_extra_info.hh"
|
||||
#include "NOD_shader_raycast.hh"
|
||||
#include "NOD_socket_items_blend.hh"
|
||||
#include "NOD_socket_items_ops.hh"
|
||||
#include "NOD_socket_items_ui.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_raycast_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeShaderRaycast)
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNode *node = b.node_or_null();
|
||||
const bNodeTree *tree = b.tree_or_null();
|
||||
|
||||
b.use_custom_socket_order();
|
||||
b.allow_any_socket_order();
|
||||
|
||||
b.add_default_layout();
|
||||
|
||||
b.add_output<decl::Float>("Is Hit"_ustr);
|
||||
b.add_output<decl::Float>("Self Hit"_ustr);
|
||||
b.add_output<decl::Float>("Hit Distance"_ustr);
|
||||
b.add_output<decl::Vector>("Hit Position"_ustr);
|
||||
b.add_output<decl::Vector>("Hit Normal"_ustr);
|
||||
|
||||
b.add_input<decl::Vector>("Position"_ustr).hide_value();
|
||||
b.add_input<decl::Vector>("Direction"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Length"_ustr).default_value(1.0);
|
||||
|
||||
auto &panel = b.add_panel("Attributes"_ustr).default_closed(true);
|
||||
if (node != nullptr) {
|
||||
const NodeShaderRaycast &storage = node_storage(*node);
|
||||
for (const NodeRaycastSampleAttributeItem &item :
|
||||
Span(storage.sample_attribute_items, storage.sample_attribute_items_num))
|
||||
{
|
||||
const UString name(item.name);
|
||||
const eCustomDataType data_type = eCustomDataType(item.data_type);
|
||||
const UString input_identifier(
|
||||
RaycastSampleAttributeItemsAccessor::input_socket_identifier_for_item(item));
|
||||
const UString output_identifier(
|
||||
RaycastSampleAttributeItemsAccessor::output_socket_identifier_for_item(item));
|
||||
panel.add_input<decl::String>(name, input_identifier)
|
||||
.socket_name_ptr(
|
||||
&tree->id, *RaycastSampleAttributeItemsAccessor::item_srna, &item, "name")
|
||||
.optional_label();
|
||||
panel.add_output(data_type, name, output_identifier).align_with_previous();
|
||||
|
||||
/* In shaders color sockets are RGB. To access color alpha, add a separate output socket,
|
||||
* similar to how color access is implemented in the Attribute shader node. */
|
||||
if (item.data_type == CD_PROP_COLOR) {
|
||||
const UString alpha_name(std::string(name.c_str()) + " Alpha");
|
||||
const UString alpha_output_identifier(
|
||||
RaycastSampleAttributeItemsAccessor::output_socket_identifier_for_item_alpha(item));
|
||||
panel.add_output(CD_PROP_FLOAT, alpha_name, alpha_output_identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
panel.add_input<decl::Extend>(""_ustr, "__extend__"_ustr)
|
||||
.custom_draw(socket_items::ui::draw_extend_socket_fn<RaycastSampleAttributeItemsAccessor>());
|
||||
panel.add_output<decl::Extend>(""_ustr, "__extend__"_ustr).align_with_previous();
|
||||
}
|
||||
|
||||
static void node_shader_init(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeShaderRaycast *data = MEM_new<NodeShaderRaycast>(__func__);
|
||||
node->storage = data;
|
||||
|
||||
node->custom1 = 0; /* Only Local */
|
||||
}
|
||||
|
||||
static bool node_insert_link(bke::NodeInsertLinkParams ¶ms)
|
||||
{
|
||||
return socket_items::try_add_item_via_any_extend_socket<RaycastSampleAttributeItemsAccessor>(
|
||||
params.ntree, params.node, params.node, params.link);
|
||||
}
|
||||
|
||||
static void node_shader_buts(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "only_local", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_layout_ex(ui::Layout &layout, bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree &tree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
|
||||
bNode &node = *static_cast<bNode *>(ptr->data);
|
||||
|
||||
layout.prop(ptr, "only_local", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
if (ui::Layout *panel = layout.panel(
|
||||
C, "sample_attribute_items", false, IFACE_("Sample Attributes")))
|
||||
{
|
||||
socket_items::ui::draw_items_list_with_operators<RaycastSampleAttributeItemsAccessor>(
|
||||
C, panel, tree, node);
|
||||
socket_items::ui::draw_active_item_props<RaycastSampleAttributeItemsAccessor>(
|
||||
tree, node, [&](PointerRNA *item_ptr) {
|
||||
panel->use_property_split_set(true);
|
||||
panel->use_property_decorate_set(false);
|
||||
panel->prop(item_ptr, "data_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void node_extra_info(NodeExtraInfoParams ¶meters)
|
||||
{
|
||||
const Scene *scene = CTX_data_scene(¶meters.C);
|
||||
if (StringRef(scene->r.engine) != RE_engine_id_BLENDER_EEVEE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const NodeShaderRaycast &storage = node_storage(parameters.node);
|
||||
if (storage.sample_attribute_items_num == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeExtraInfoRow row;
|
||||
row.text = RPT_("Attributes Not Supported");
|
||||
row.tooltip = TIP_("Accessing attributes is not supported by EEVEE renderer");
|
||||
row.icon = ICON_ERROR;
|
||||
parameters.rows.append(std::move(row));
|
||||
}
|
||||
|
||||
static void node_operators()
|
||||
{
|
||||
socket_items::ops::make_common_operators<RaycastSampleAttributeItemsAccessor>();
|
||||
}
|
||||
|
||||
static void node_free_storage(bNode *node)
|
||||
{
|
||||
socket_items::destruct_array<RaycastSampleAttributeItemsAccessor>(*node);
|
||||
MEM_delete(reinterpret_cast<NodeShaderRaycast *>(node->storage));
|
||||
}
|
||||
|
||||
static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
|
||||
{
|
||||
const NodeShaderRaycast &src_storage = node_storage(*src_node);
|
||||
NodeShaderRaycast *dst_storage = MEM_new<NodeShaderRaycast>(__func__,
|
||||
dna::shallow_copy(src_storage));
|
||||
dst_node->storage = dst_storage;
|
||||
|
||||
socket_items::copy_array<RaycastSampleAttributeItemsAccessor>(*src_node, *dst_node);
|
||||
}
|
||||
|
||||
static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
|
||||
{
|
||||
socket_items::blend_write<RaycastSampleAttributeItemsAccessor>(&writer, node);
|
||||
}
|
||||
|
||||
static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
|
||||
{
|
||||
socket_items::blend_read_data<RaycastSampleAttributeItemsAccessor>(&reader, node);
|
||||
}
|
||||
|
||||
static int node_shader_gpu(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_RAYCAST);
|
||||
|
||||
if (!in[0].link) {
|
||||
GPU_link(mat, "world_position_get", &in[0].link);
|
||||
}
|
||||
|
||||
if (!in[1].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[1].link);
|
||||
}
|
||||
|
||||
/* GPU raycast node does not support attribute sampling. Relink all attribute outputs to a zero
|
||||
* value. */
|
||||
const NodeShaderRaycast &storage = node_storage(*node);
|
||||
for (const NodeRaycastSampleAttributeItem &item :
|
||||
Span(storage.sample_attribute_items, storage.sample_attribute_items_num))
|
||||
{
|
||||
static const float ZERO[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
const UString output_identifier(
|
||||
RaycastSampleAttributeItemsAccessor::output_socket_identifier_for_item(item));
|
||||
GPUNodeStack &output = GPU_node_get_output(*node, out, output_identifier.ref());
|
||||
GPU_link(mat, "set_value", GPU_constant(ZERO), &output.link);
|
||||
|
||||
if (item.data_type == CD_PROP_COLOR) {
|
||||
const UString alpha_output_identifier(
|
||||
RaycastSampleAttributeItemsAccessor::output_socket_identifier_for_item_alpha(item));
|
||||
GPUNodeStack &alpha_output = GPU_node_get_output(*node, out, alpha_output_identifier.ref());
|
||||
GPU_link(mat, "set_value", GPU_constant(ZERO), &alpha_output.link);
|
||||
}
|
||||
}
|
||||
|
||||
const bool only_local = node->custom1;
|
||||
const char *shader_name = only_local ? "node_raycast_only_local" : "node_raycast";
|
||||
|
||||
/* Explicitly link all sockets that are expected by the shader.
|
||||
* Do not rely on the stack linking as it will attempt to link dynamically added attribute
|
||||
* sockets. */
|
||||
return GPU_link(mat,
|
||||
shader_name,
|
||||
GPU_node_get_input_link(*node, in, "Position"),
|
||||
GPU_node_get_input_link(*node, in, "Direction"),
|
||||
GPU_node_get_input_link(*node, in, "Length"),
|
||||
&GPU_node_get_output(*node, out, "Is Hit").link,
|
||||
&GPU_node_get_output(*node, out, "Self Hit").link,
|
||||
&GPU_node_get_output(*node, out, "Hit Distance").link,
|
||||
&GPU_node_get_output(*node, out, "Hit Position").link,
|
||||
&GPU_node_get_output(*node, out, "Hit Normal").link);
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
return get_output_default(socket_out_->identifier, NodeItem::Type::Any);
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_raycast_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_raycast()
|
||||
{
|
||||
namespace file_ns = blender::nodes::node_shader_raycast_cc;
|
||||
|
||||
static blender::bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeRaycast"_ustr, SH_NODE_RAYCAST);
|
||||
ntype.ui_name = "Raycast";
|
||||
ntype.ui_description = "Cast rays and retrieve information from the hit point";
|
||||
ntype.enum_name_legacy = "MATERIAL_RAYCAST";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
ntype.initfunc = file_ns::node_shader_init;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.insert_link = file_ns::node_insert_link;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts;
|
||||
ntype.draw_buttons_ex = file_ns::node_layout_ex;
|
||||
ntype.register_operators = file_ns::node_operators;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
ntype.get_extra_info = file_ns::node_extra_info;
|
||||
ntype.blend_write_storage_content = file_ns::node_blend_write;
|
||||
ntype.blend_data_read_storage_content = file_ns::node_blend_read;
|
||||
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeShaderRaycast", file_ns::node_free_storage, file_ns::node_copy_storage);
|
||||
|
||||
blender::bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
namespace nodes {
|
||||
|
||||
StructRNA **RaycastSampleAttributeItemsAccessor::item_srna = &RNA_NodeRaycastSampleAttributeItem;
|
||||
|
||||
void RaycastSampleAttributeItemsAccessor::blend_write_item(BlendWriter *writer, const ItemT &item)
|
||||
{
|
||||
writer->write_string(item.name);
|
||||
}
|
||||
|
||||
void RaycastSampleAttributeItemsAccessor::blend_read_data_item(BlendDataReader *reader,
|
||||
ItemT &item)
|
||||
{
|
||||
BLO_read_string(reader, &item.name);
|
||||
}
|
||||
|
||||
} // namespace nodes
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,72 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_rgb_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Color>("Color"_ustr)
|
||||
.default_value({0.5f, 0.5f, 0.5f, 1.0f})
|
||||
.custom_draw([](CustomSocketDrawParams ¶ms) {
|
||||
params.layout.alignment_set(ui::LayoutAlign::Expand);
|
||||
ui::Layout &col = params.layout.column(false);
|
||||
template_color_picker(
|
||||
&col, ¶ms.socket_ptr, "default_value", true, false, false, false);
|
||||
col.prop(¶ms.socket_ptr, "default_value", ui::ITEM_R_SLIDER, "", ICON_NONE);
|
||||
});
|
||||
}
|
||||
|
||||
static int gpu_shader_rgb(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack * /*in*/,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first);
|
||||
float *value = static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value;
|
||||
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::Color3);
|
||||
return create_node("constant", NodeItem::Type::Color3, {{"value", color}});
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_rgb_cc
|
||||
|
||||
void register_node_type_sh_rgb()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_rgb_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeRGB"_ustr, SH_NODE_RGB);
|
||||
ntype.ui_name = "Color";
|
||||
ntype.ui_description = "A color picker";
|
||||
ntype.enum_name_legacy = "RGB";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "GPU_material.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_rgb_to_bw_cc {
|
||||
|
||||
static void sh_node_rgbtobw_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.5f, 0.5f, 0.5f, 1.0f});
|
||||
b.add_output<decl::Float>("Val"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_rgbtobw(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
float coefficients[3];
|
||||
IMB_colormanagement_get_luminance_coefficients(coefficients);
|
||||
return GPU_stack_link(mat, node, "rgbtobw", in, out, GPU_constant(coefficients));
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
NodeItem color = get_input_value("Color", NodeItem::Type::Color3);
|
||||
return create_node("luminance", NodeItem::Type::Color3, {{"in", color}});
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_rgb_to_bw_cc
|
||||
|
||||
void register_node_type_sh_rgbtobw()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_rgb_to_bw_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeRGBToBW"_ustr, SH_NODE_RGBTOBW);
|
||||
ntype.ui_name = "RGB to BW";
|
||||
ntype.ui_description = "Convert a color's luminance to a grayscale value";
|
||||
ntype.enum_name_legacy = "RGBTOBW";
|
||||
ntype.nclass = 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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,108 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_script_cc {
|
||||
|
||||
static void node_shader_buts_script(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
{
|
||||
ui::Layout &row = layout.row(false);
|
||||
row.prop(
|
||||
ptr, "mode", ui::ITEM_R_SPLIT_EMPTY_NAME | ui::ITEM_R_EXPAND, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
{
|
||||
ui::Layout &row = layout.row(true);
|
||||
if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_INTERNAL) {
|
||||
row.prop(ptr, "script", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
else {
|
||||
row.prop(ptr, "filepath", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
row.op("node.shader_script_update", "", ICON_FILE_REFRESH);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_buts_script_ex(ui::Layout &layout, bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
layout.separator();
|
||||
|
||||
node_shader_buts_script(layout, C, ptr);
|
||||
|
||||
#if 0 /* not implemented yet */
|
||||
if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_EXTERNAL) {
|
||||
layout.prop(ptr, "use_auto_update", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void init(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeShaderScript *nss = MEM_new<NodeShaderScript>("shader script node");
|
||||
node->storage = nss;
|
||||
}
|
||||
|
||||
static void node_free_script(bNode *node)
|
||||
{
|
||||
NodeShaderScript *nss = static_cast<NodeShaderScript *>(node->storage);
|
||||
|
||||
if (nss) {
|
||||
if (nss->bytecode) {
|
||||
MEM_delete(nss->bytecode);
|
||||
}
|
||||
|
||||
MEM_delete(nss);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_copy_script(bNodeTree * /*dst_ntree*/, bNode *dest_node, const bNode *src_node)
|
||||
{
|
||||
NodeShaderScript *src_nss = static_cast<NodeShaderScript *>(src_node->storage);
|
||||
NodeShaderScript *dest_nss = MEM_dupalloc(src_nss);
|
||||
|
||||
if (src_nss->bytecode) {
|
||||
dest_nss->bytecode = MEM_dupalloc(src_nss->bytecode);
|
||||
}
|
||||
|
||||
dest_node->storage = dest_nss;
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_script_cc
|
||||
|
||||
void register_node_type_sh_script()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_script_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeScript"_ustr, SH_NODE_SCRIPT);
|
||||
ntype.ui_name = "Script";
|
||||
ntype.ui_description =
|
||||
"Generate an OSL shader from a file or text data-block.\nNote: OSL shaders are not "
|
||||
"supported on all GPU backends";
|
||||
ntype.enum_name_legacy = "SCRIPT";
|
||||
ntype.nclass = NODE_CLASS_SCRIPT;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_script;
|
||||
ntype.draw_buttons_ex = file_ns::node_shader_buts_script_ex;
|
||||
ntype.initfunc = file_ns::init;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeShaderScript", file_ns::node_free_script, file_ns::node_copy_script);
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,284 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static void node_combsep_color_init(bNodeTree * /*tree*/, bNode *node)
|
||||
{
|
||||
NodeCombSepColor *data = MEM_new<NodeCombSepColor>(__func__);
|
||||
data->mode = NODE_COMBSEP_COLOR_RGB;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
/* **************** SEPARATE COLOR ******************** */
|
||||
|
||||
namespace nodes::node_shader_separate_color_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeCombSepColor)
|
||||
|
||||
static void sh_node_sepcolor_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_output<decl::Float>("Red"_ustr).label_fn([](const bNode &node) {
|
||||
switch (node_storage(node).mode) {
|
||||
case NODE_COMBSEP_COLOR_RGB:
|
||||
default:
|
||||
return IFACE_("Red");
|
||||
case NODE_COMBSEP_COLOR_HSL:
|
||||
case NODE_COMBSEP_COLOR_HSV:
|
||||
return IFACE_("Hue");
|
||||
}
|
||||
});
|
||||
b.add_output<decl::Float>("Green"_ustr).label_fn([](const bNode &node) {
|
||||
switch (node_storage(node).mode) {
|
||||
case NODE_COMBSEP_COLOR_RGB:
|
||||
default:
|
||||
return IFACE_("Green");
|
||||
case NODE_COMBSEP_COLOR_HSL:
|
||||
case NODE_COMBSEP_COLOR_HSV:
|
||||
return IFACE_("Saturation");
|
||||
}
|
||||
});
|
||||
b.add_output<decl::Float>("Blue"_ustr).label_fn([](const bNode &node) {
|
||||
switch (node_storage(node).mode) {
|
||||
case NODE_COMBSEP_COLOR_RGB:
|
||||
default:
|
||||
return IFACE_("Blue");
|
||||
case NODE_COMBSEP_COLOR_HSL:
|
||||
return IFACE_("Lightness");
|
||||
case NODE_COMBSEP_COLOR_HSV:
|
||||
return CTX_IFACE_(BLT_I18NCONTEXT_COLOR, "Value");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case NODE_COMBSEP_COLOR_RGB:
|
||||
return "separate_color_rgb";
|
||||
case NODE_COMBSEP_COLOR_HSV:
|
||||
return "separate_color_hsv";
|
||||
case NODE_COMBSEP_COLOR_HSL:
|
||||
return "separate_color_hsl";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int gpu_shader_sepcolor(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const NodeCombSepColor &storage = node_storage(*node);
|
||||
const char *name = gpu_shader_get_name(storage.mode);
|
||||
if (name != nullptr) {
|
||||
return GPU_stack_link(mat, node, name, in, out);
|
||||
}
|
||||
|
||||
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_->identifier, "Red") ? 0 :
|
||||
STREQ(socket_out_->identifier, "Green") ? 1 :
|
||||
2;
|
||||
return convert[index];
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_separate_color_cc
|
||||
|
||||
void register_node_type_sh_sepcolor()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_separate_color_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeSeparateColor"_ustr, SH_NODE_SEPARATE_COLOR);
|
||||
ntype.ui_name = "Separate Color";
|
||||
ntype.ui_description = "Split a color into its individual components using multiple models";
|
||||
ntype.enum_name_legacy = "SEPARATE_COLOR";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::sh_node_sepcolor_declare;
|
||||
ntype.initfunc = node_combsep_color_init;
|
||||
bke::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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
/* **************** COMBINE COLOR ******************** */
|
||||
|
||||
namespace nodes::node_shader_combine_color_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeCombSepColor)
|
||||
|
||||
static void sh_node_combcolor_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Red"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.label_fn([](const bNode &node) {
|
||||
switch (node_storage(node).mode) {
|
||||
case NODE_COMBSEP_COLOR_RGB:
|
||||
default:
|
||||
return IFACE_("Red");
|
||||
case NODE_COMBSEP_COLOR_HSL:
|
||||
case NODE_COMBSEP_COLOR_HSV:
|
||||
return IFACE_("Hue");
|
||||
}
|
||||
});
|
||||
b.add_input<decl::Float>("Green"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.label_fn([](const bNode &node) {
|
||||
switch (node_storage(node).mode) {
|
||||
case NODE_COMBSEP_COLOR_RGB:
|
||||
default:
|
||||
return IFACE_("Green");
|
||||
case NODE_COMBSEP_COLOR_HSL:
|
||||
case NODE_COMBSEP_COLOR_HSV:
|
||||
return IFACE_("Saturation");
|
||||
}
|
||||
});
|
||||
b.add_input<decl::Float>("Blue"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.label_fn([](const bNode &node) {
|
||||
switch (node_storage(node).mode) {
|
||||
case NODE_COMBSEP_COLOR_RGB:
|
||||
default:
|
||||
return IFACE_("Blue");
|
||||
case NODE_COMBSEP_COLOR_HSL:
|
||||
return IFACE_("Lightness");
|
||||
case NODE_COMBSEP_COLOR_HSV:
|
||||
return CTX_IFACE_(BLT_I18NCONTEXT_COLOR, "Value");
|
||||
}
|
||||
});
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case NODE_COMBSEP_COLOR_RGB:
|
||||
return "combine_color_rgb";
|
||||
case NODE_COMBSEP_COLOR_HSV:
|
||||
return "combine_color_hsv";
|
||||
case NODE_COMBSEP_COLOR_HSL:
|
||||
return "combine_color_hsl";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int gpu_shader_combcolor(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const NodeCombSepColor &storage = node_storage(*node);
|
||||
const char *name = gpu_shader_get_name(storage.mode);
|
||||
if (name != nullptr) {
|
||||
return GPU_stack_link(mat, node, name, in, out);
|
||||
}
|
||||
|
||||
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 nodes::node_shader_combine_color_cc
|
||||
|
||||
void register_node_type_sh_combcolor()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_combine_color_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeCombineColor"_ustr, SH_NODE_COMBINE_COLOR);
|
||||
ntype.ui_name = "Combine Color";
|
||||
ntype.ui_description = "Create a color from individual components using multiple models";
|
||||
ntype.enum_name_legacy = "COMBINE_COLOR";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::sh_node_combcolor_declare;
|
||||
ntype.initfunc = node_combsep_color_init;
|
||||
bke::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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,252 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "FN_multi_function_builder.hh"
|
||||
|
||||
#include "NOD_inverse_eval_params.hh"
|
||||
#include "NOD_multi_function.hh"
|
||||
#include "NOD_value_elem_eval.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_sepcomb_xyz_cc::sep {
|
||||
|
||||
static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Vector>("Vector"_ustr).min(-10000.0f).max(10000.0f);
|
||||
b.add_output<decl::Float>("X"_ustr);
|
||||
b.add_output<decl::Float>("Y"_ustr);
|
||||
b.add_output<decl::Float>("Z"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_sepxyz(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
return GPU_stack_link(mat, node, "separate_xyz", in, out);
|
||||
}
|
||||
|
||||
class MF_SeparateXYZ : public mf::MultiFunction {
|
||||
public:
|
||||
MF_SeparateXYZ()
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Separate XYZ", signature};
|
||||
builder.single_input<float3>("XYZ");
|
||||
builder.single_output<float>("X", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Y", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Z", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "XYZ");
|
||||
MutableSpan<float> xs = params.uninitialized_single_output_if_required<float>(1, "X");
|
||||
MutableSpan<float> ys = params.uninitialized_single_output_if_required<float>(2, "Y");
|
||||
MutableSpan<float> zs = params.uninitialized_single_output_if_required<float>(3, "Z");
|
||||
|
||||
std::array<MutableSpan<float>, 3> outputs = {xs, ys, zs};
|
||||
Vector<int> used_outputs;
|
||||
if (!xs.is_empty()) {
|
||||
used_outputs.append(0);
|
||||
}
|
||||
if (!ys.is_empty()) {
|
||||
used_outputs.append(1);
|
||||
}
|
||||
if (!zs.is_empty()) {
|
||||
used_outputs.append(2);
|
||||
}
|
||||
|
||||
devirtualize_varray(vectors, [&](auto vectors) {
|
||||
mask.foreach_segment_optimized([&](const auto segment) {
|
||||
const int used_outputs_num = used_outputs.size();
|
||||
const int *used_outputs_data = used_outputs.data();
|
||||
|
||||
for (const int64_t i : segment) {
|
||||
const float3 &vector = vectors[i];
|
||||
for (const int out_i : IndexRange(used_outputs_num)) {
|
||||
const int coordinate = used_outputs_data[out_i];
|
||||
outputs[coordinate][i] = vector[coordinate];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_sepxyz_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
static MF_SeparateXYZ separate_fn;
|
||||
builder.set_matching_fn(separate_fn);
|
||||
}
|
||||
|
||||
static void sh_node_sepxyz_eval_elem(value_elem::ElemEvalParams ¶ms)
|
||||
{
|
||||
using namespace value_elem;
|
||||
const VectorElem vector_elem = params.get_input_elem<VectorElem>("Vector"_ustr);
|
||||
params.set_output_elem("X"_ustr, vector_elem.x);
|
||||
params.set_output_elem("Y"_ustr, vector_elem.y);
|
||||
params.set_output_elem("Z"_ustr, vector_elem.z);
|
||||
}
|
||||
|
||||
static void sh_node_sepxyz_eval_inverse_elem(value_elem::InverseElemEvalParams ¶ms)
|
||||
{
|
||||
using namespace value_elem;
|
||||
value_elem::VectorElem result;
|
||||
result.x = params.get_output_elem<FloatElem>("X"_ustr);
|
||||
result.y = params.get_output_elem<FloatElem>("Y"_ustr);
|
||||
result.z = params.get_output_elem<FloatElem>("Z"_ustr);
|
||||
params.set_input_elem("Vector"_ustr, result);
|
||||
}
|
||||
|
||||
static void sh_node_sepxyz_eval_inverse(inverse_eval::InverseEvalParams ¶ms)
|
||||
{
|
||||
params.set_input("Vector"_ustr,
|
||||
float3(params.get_output<float>("X"_ustr),
|
||||
params.get_output<float>("Y"_ustr),
|
||||
params.get_output<float>("Z"_ustr)));
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
NodeItem vector = get_input_value("Vector", NodeItem::Type::Vector3);
|
||||
int index = STREQ(socket_out_->identifier, "X") ? 0 :
|
||||
STREQ(socket_out_->identifier, "Y") ? 1 :
|
||||
2;
|
||||
return vector[index];
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_sepcomb_xyz_cc::sep
|
||||
|
||||
void register_node_type_sh_sepxyz()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_sepcomb_xyz_cc::sep;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeSeparateXYZ"_ustr, SH_NODE_SEPXYZ);
|
||||
ntype.ui_name = "Separate XYZ";
|
||||
ntype.ui_description = "Split a vector into its X, Y, and Z components";
|
||||
ntype.enum_name_legacy = "SEPXYZ";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
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;
|
||||
ntype.eval_elem = file_ns::sh_node_sepxyz_eval_elem;
|
||||
ntype.eval_inverse_elem = file_ns::sh_node_sepxyz_eval_inverse_elem;
|
||||
ntype.eval_inverse = file_ns::sh_node_sepxyz_eval_inverse;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
namespace nodes::node_shader_sepcomb_xyz_cc::comb {
|
||||
|
||||
static void sh_node_combxyz_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Float>("X"_ustr).min(-10000.0f).max(10000.0f);
|
||||
b.add_input<decl::Float>("Y"_ustr).min(-10000.0f).max(10000.0f);
|
||||
b.add_input<decl::Float>("Z"_ustr).min(-10000.0f).max(10000.0f);
|
||||
b.add_output<decl::Vector>("Vector"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_combxyz(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
return GPU_stack_link(mat, node, "combine_xyz", in, out);
|
||||
}
|
||||
|
||||
static void sh_node_combxyz_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
static auto fn = mf::build::SI3_SO<float, float, float, float3>(
|
||||
"Combine Vector",
|
||||
[](float x, float y, float z) { return float3(x, y, z); },
|
||||
mf::build::exec_presets::AllSpanOrSingle());
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
|
||||
static void sh_node_combxyz_eval_elem(value_elem::ElemEvalParams ¶ms)
|
||||
{
|
||||
using namespace value_elem;
|
||||
VectorElem vector_elem;
|
||||
vector_elem.x = params.get_input_elem<FloatElem>("X"_ustr);
|
||||
vector_elem.y = params.get_input_elem<FloatElem>("Y"_ustr);
|
||||
vector_elem.z = params.get_input_elem<FloatElem>("Z"_ustr);
|
||||
params.set_output_elem("Vector"_ustr, vector_elem);
|
||||
}
|
||||
|
||||
static void sh_node_combxyz_eval_inverse_elem(value_elem::InverseElemEvalParams ¶ms)
|
||||
{
|
||||
using namespace value_elem;
|
||||
const VectorElem output_elem = params.get_output_elem<VectorElem>("Vector"_ustr);
|
||||
params.set_input_elem("X"_ustr, output_elem.x);
|
||||
params.set_input_elem("Y"_ustr, output_elem.y);
|
||||
params.set_input_elem("Z"_ustr, output_elem.z);
|
||||
}
|
||||
|
||||
static void sh_node_combxyz_eval_inverse(inverse_eval::InverseEvalParams ¶ms)
|
||||
{
|
||||
const float3 output = params.get_output<float3>("Vector"_ustr);
|
||||
params.set_input("X"_ustr, output.x);
|
||||
params.set_input("Y"_ustr, output.y);
|
||||
params.set_input("Z"_ustr, output.z);
|
||||
}
|
||||
|
||||
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 nodes::node_shader_sepcomb_xyz_cc::comb
|
||||
|
||||
void register_node_type_sh_combxyz()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_sepcomb_xyz_cc::comb;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeCombineXYZ"_ustr, SH_NODE_COMBXYZ);
|
||||
ntype.ui_name = "Combine XYZ";
|
||||
ntype.ui_description = "Create a vector from X, Y, and Z components";
|
||||
ntype.enum_name_legacy = "COMBXYZ";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
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;
|
||||
ntype.eval_elem = file_ns::sh_node_combxyz_eval_elem;
|
||||
ntype.eval_inverse_elem = file_ns::sh_node_combxyz_eval_inverse_elem;
|
||||
ntype.eval_inverse = file_ns::sh_node_combxyz_eval_inverse;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,53 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_shader_to_rgb_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Shader>("Shader"_ustr);
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
b.add_output<decl::Float>("Alpha"_ustr);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_shadertorgb(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_SHADER_TO_RGBA);
|
||||
|
||||
return GPU_stack_link(mat, node, "node_shader_to_rgba", in, out);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_shader_to_rgb_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_shadertorgb()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_shader_to_rgb_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeShaderToRGB"_ustr, SH_NODE_SHADERTORGB);
|
||||
ntype.ui_name = "Shader to RGB";
|
||||
ntype.ui_description =
|
||||
"Convert rendering effect (such as light and shadow) to color. Typically used for "
|
||||
"non-photorealistic rendering, to apply additional effects on the output of BSDFs.\nNote: "
|
||||
"only supported in EEVEE";
|
||||
ntype.enum_name_legacy = "SHADERTORGB";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.add_ui_poll = object_eevee_shader_nodes_poll;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_shadertorgb;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,52 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_squeeze_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Value"_ustr).default_value(0.0f).min(-100.0f).max(100.0f);
|
||||
b.add_input<decl::Float>("Width"_ustr).default_value(1.0f).min(-100.0f).max(100.0f);
|
||||
b.add_input<decl::Float>("Center"_ustr).default_value(0.0f).min(-100.0f).max(100.0f);
|
||||
b.add_output<decl::Float>("Value"_ustr);
|
||||
}
|
||||
|
||||
static int gpu_shader_squeeze(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
return GPU_stack_link(mat, node, "squeeze", in, out);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_squeeze_cc
|
||||
|
||||
void register_node_type_sh_squeeze()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_squeeze_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeSqueeze"_ustr, SH_NODE_SQUEEZE);
|
||||
ntype.ui_name = "Squeeze Value (Legacy)";
|
||||
ntype.ui_description = "Deprecated";
|
||||
ntype.enum_name_legacy = "SQUEEZE";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.gather_link_search_ops = nullptr;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gpu_fn = file_ns::gpu_shader_squeeze;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,169 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_subsurface_scattering_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNodeTree *ntree = b.tree_or_null();
|
||||
const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);
|
||||
|
||||
b.add_input<decl::Color>("Color"_ustr).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_input<decl::Float>("Scale"_ustr)
|
||||
.default_value(0.005f)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description("Scale factor of the subsurface scattering radius");
|
||||
b.add_input<decl::Vector>("Radius"_ustr)
|
||||
.default_value({1.0f, 0.2f, 0.1f})
|
||||
.min(0.0f)
|
||||
.max(100.0f)
|
||||
.description("Scattering radius per color channel (RGB), multiplied with Scale");
|
||||
b.add_input<decl::Float>("IOR"_ustr)
|
||||
.default_value(1.4f)
|
||||
.min(1.01f)
|
||||
.max(3.8f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Float>("Anisotropy"_ustr)
|
||||
.default_value(0.0f)
|
||||
.min(-1.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Directionality of volume scattering within the subsurface medium. "
|
||||
"Zero scatters uniformly in all directions, positive values scatter more in the forward "
|
||||
"direction, and negative values scatter more backwards. "
|
||||
"For example, skin has been measured to have an anisotropy of 0.8");
|
||||
b.add_input<decl::Vector>("Normal"_ustr).hide_value();
|
||||
b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
|
||||
b.add_output<decl::Shader>("BSSRDF"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_subsurface(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "falloff", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_subsurface_scattering(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_SUBSURFACE_RANDOM_WALK;
|
||||
node->custom2 = true;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[6].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[6].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SUBSURFACE);
|
||||
|
||||
/* EEVEE's subsurface closure still uses the legacy approximation where the radius is scaled by
|
||||
* 1/(4pi) (e.g., SHD_SUBSURFACE_RANDOM_WALK_LEGACY) */
|
||||
GPUNodeLink *random_walk_radius_scale = nullptr;
|
||||
float random_walk_scale = 1.0f;
|
||||
if (node->custom1 == SHD_SUBSURFACE_RANDOM_WALK) {
|
||||
random_walk_scale = 4.0f * M_PI;
|
||||
}
|
||||
random_walk_radius_scale = GPU_constant(&random_walk_scale);
|
||||
|
||||
return GPU_stack_link(
|
||||
mat, node, "node_subsurface_scattering", in, out, random_walk_radius_scale);
|
||||
}
|
||||
|
||||
static void node_shader_update_subsurface_scattering(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
const int sss_method = node->custom1;
|
||||
|
||||
for (bNodeSocket &sock : node->inputs) {
|
||||
if (STR_ELEM(sock.name, "IOR", "Anisotropy")) {
|
||||
bke::node_set_socket_availability(*ntree, sock, sss_method != SHD_SUBSURFACE_BURLEY);
|
||||
}
|
||||
if (STR_ELEM(sock.name, "Roughness")) {
|
||||
const bool is_random_walk = sss_method == SHD_SUBSURFACE_RANDOM_WALK ||
|
||||
sss_method == SHD_SUBSURFACE_RANDOM_WALK_LEGACY;
|
||||
bke::node_set_socket_availability(*ntree, sock, is_random_walk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
# if MATERIALX_MAJOR_VERSION <= 1 && MATERIALX_MINOR_VERSION <= 38
|
||||
NodeItem radius = get_input_value("Radius", NodeItem::Type::Vector3);
|
||||
# else
|
||||
NodeItem radius = get_input_value("Radius", NodeItem::Type::Color3);
|
||||
# endif
|
||||
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 nodes::node_shader_subsurface_scattering_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_subsurface_scattering()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_subsurface_scattering_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeSubsurfaceScattering"_ustr, SH_NODE_SUBSURFACE_SCATTERING);
|
||||
ntype.ui_name = "Subsurface Scattering";
|
||||
ntype.ui_description =
|
||||
"Subsurface multiple scattering shader to simulate light entering the surface and bouncing "
|
||||
"internally.\nTypically used for materials such as skin, wax, marble or milk";
|
||||
ntype.enum_name_legacy = "SUBSURFACE_SCATTERING";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_subsurface;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,123 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tangent_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Vector>("Tangent"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_tangent(ui::Layout &layout, bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "direction_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
|
||||
if (RNA_enum_get(ptr, "direction_type") == SHD_TANGENT_UVMAP) {
|
||||
PointerRNA obptr = CTX_data_pointer_get(C, "active_object");
|
||||
Object *object = static_cast<Object *>(obptr.data);
|
||||
|
||||
if (object && object->type == OB_MESH) {
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
|
||||
if (depsgraph) {
|
||||
Object *object_eval = DEG_get_evaluated(depsgraph, object);
|
||||
PointerRNA dataptr = RNA_id_pointer_create(object_eval->data);
|
||||
layout.prop_search(ptr, "uv_map", &dataptr, "uv_layers", "", ICON_GROUP_UVS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
layout.prop(ptr, "uv_map", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_GROUP_UVS);
|
||||
}
|
||||
else {
|
||||
layout.prop(
|
||||
ptr, "axis", ui::ITEM_R_SPLIT_EMPTY_NAME | ui::ITEM_R_EXPAND, std::nullopt, ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_tangent(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeShaderTangent *attr = MEM_new<NodeShaderTangent>("NodeShaderTangent");
|
||||
attr->axis = SHD_TANGENT_AXIS_Z;
|
||||
node->storage = attr;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tangent(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
NodeShaderTangent *attr = static_cast<NodeShaderTangent *>(node->storage);
|
||||
|
||||
if (attr->direction_type == SHD_TANGENT_UVMAP) {
|
||||
return GPU_stack_link(
|
||||
mat, node, "node_tangentmap", in, out, GPU_attribute(mat, CD_TANGENT, attr->uv_map));
|
||||
}
|
||||
|
||||
GPUNodeLink *orco = GPU_attribute(mat, CD_ORCO, "");
|
||||
|
||||
if (attr->axis == SHD_TANGENT_AXIS_X) {
|
||||
GPU_link(mat, "tangent_orco_x", orco, &orco);
|
||||
}
|
||||
else if (attr->axis == SHD_TANGENT_AXIS_Y) {
|
||||
GPU_link(mat, "tangent_orco_y", orco, &orco);
|
||||
}
|
||||
else {
|
||||
GPU_link(mat, "tangent_orco_z", orco, &orco);
|
||||
}
|
||||
|
||||
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 nodes::node_shader_tangent_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_tangent()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tangent_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeTangent"_ustr, SH_NODE_TANGENT);
|
||||
ntype.ui_name = "Tangent";
|
||||
ntype.ui_description = "Generate a tangent direction for the Anisotropic BSDF";
|
||||
ntype.enum_name_legacy = "TANGENT";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_tangent;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.initfunc = file_ns::node_shader_init_tangent;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_tangent;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeShaderTangent", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,344 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_brick_cc {
|
||||
|
||||
static void sh_node_tex_brick_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
const bool is_compositor = b.tree_or_null() && b.tree_or_null()->type == NTREE_COMPOSIT;
|
||||
const NodeDefaultInputType default_input_type =
|
||||
is_compositor ? NODE_DEFAULT_INPUT_UNIFORM_IMAGE_COORDINATES :
|
||||
NODE_DEFAULT_INPUT_POSITION_FIELD;
|
||||
b.add_input<decl::Vector>("Vector"_ustr)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.default_input_type(default_input_type);
|
||||
|
||||
b.add_input<decl::Color>("Color1"_ustr)
|
||||
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
|
||||
.description("Color of the first reference brick");
|
||||
b.add_input<decl::Color>("Color2"_ustr)
|
||||
.default_value({0.2f, 0.2f, 0.2f, 1.0f})
|
||||
.description("Color of the second reference brick");
|
||||
b.add_input<decl::Color>("Mortar"_ustr)
|
||||
.default_value({0.0f, 0.0f, 0.0f, 1.0f})
|
||||
.no_muted_links()
|
||||
.description("Color of the area between bricks");
|
||||
b.add_input<decl::Float>("Scale"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(5.0f)
|
||||
.no_muted_links()
|
||||
.description("Scale of the texture");
|
||||
b.add_input<decl::Float>("Mortar Size"_ustr)
|
||||
.min(0.0f)
|
||||
.max(0.125f)
|
||||
.default_value(0.02f)
|
||||
.no_muted_links()
|
||||
.description(
|
||||
"Size of the filling between the bricks (known as \"mortar\"). "
|
||||
"0 means no mortar");
|
||||
b.add_input<decl::Float>("Mortar Smooth"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(0.1f)
|
||||
.no_muted_links()
|
||||
.description(
|
||||
"Blurs/softens the edge between the mortar and the bricks. "
|
||||
"This can be useful with a texture and displacement textures");
|
||||
b.add_input<decl::Float>("Bias"_ustr)
|
||||
.min(-1.0f)
|
||||
.max(1.0f)
|
||||
.no_muted_links()
|
||||
.description(
|
||||
"The color variation between Color1 and Color2. "
|
||||
"Values of -1 and 1 only use one of the two colors. "
|
||||
"Values in between mix the colors");
|
||||
b.add_input<decl::Float>("Brick Width"_ustr)
|
||||
.min(0.01f)
|
||||
.max(100.0f)
|
||||
.default_value(0.5f)
|
||||
.no_muted_links()
|
||||
.description("Ratio of brick's width relative to the texture scale");
|
||||
b.add_input<decl::Float>("Row Height"_ustr)
|
||||
.min(0.01f)
|
||||
.max(100.0f)
|
||||
.default_value(0.25f)
|
||||
.no_muted_links()
|
||||
.description("Ratio of brick's row height relative to the texture scale");
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
b.add_output<decl::Float>("Factor"_ustr, "Fac"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_tex_brick(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
{
|
||||
ui::Layout &col = layout.column(true);
|
||||
col.prop(ptr,
|
||||
"offset",
|
||||
ui::ITEM_R_SPLIT_EMPTY_NAME | ui::ITEM_R_SLIDER,
|
||||
IFACE_("Offset"),
|
||||
ICON_NONE);
|
||||
col.prop(ptr, "offset_frequency", ui::ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Frequency"), ICON_NONE);
|
||||
}
|
||||
{
|
||||
ui::Layout &col = layout.column(true);
|
||||
col.prop(ptr, "squash", ui::ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Squash"), ICON_NONE);
|
||||
col.prop(ptr, "squash_frequency", ui::ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Frequency"), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_brick(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeTexBrick *tex = MEM_new<NodeTexBrick>(__func__);
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
|
||||
tex->offset = 0.5f;
|
||||
tex->squash = 1.0f;
|
||||
tex->offset_freq = 2;
|
||||
tex->squash_freq = 2;
|
||||
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_brick(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
NodeTexBrick *tex = static_cast<NodeTexBrick *>(node->storage);
|
||||
float offset_freq = tex->offset_freq;
|
||||
float squash_freq = tex->squash_freq;
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"node_tex_brick",
|
||||
in,
|
||||
out,
|
||||
GPU_uniform(&tex->offset),
|
||||
GPU_constant(&offset_freq),
|
||||
GPU_uniform(&tex->squash),
|
||||
GPU_constant(&squash_freq));
|
||||
}
|
||||
|
||||
class BrickFunction : public mf::MultiFunction {
|
||||
private:
|
||||
const float offset_;
|
||||
const int offset_freq_;
|
||||
const float squash_;
|
||||
const int squash_freq_;
|
||||
|
||||
public:
|
||||
BrickFunction(const float offset,
|
||||
const int offset_freq,
|
||||
const float squash,
|
||||
const int squash_freq)
|
||||
: offset_(offset), offset_freq_(offset_freq), squash_(squash), squash_freq_(squash_freq)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"BrickTexture", signature};
|
||||
builder.single_input<float3>("Vector");
|
||||
builder.single_input<ColorGeometry4f>("Color1");
|
||||
builder.single_input<ColorGeometry4f>("Color2");
|
||||
builder.single_input<ColorGeometry4f>("Mortar");
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_input<float>("Mortar Size");
|
||||
builder.single_input<float>("Mortar Smooth");
|
||||
builder.single_input<float>("Bias");
|
||||
builder.single_input<float>("Brick Width");
|
||||
builder.single_input<float>("Row Height");
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Fac", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
/* Fast integer noise. */
|
||||
static float brick_noise(uint n)
|
||||
{
|
||||
n = (n + 1013) & 0x7fffffff;
|
||||
n = (n >> 13) ^ n;
|
||||
const uint nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
|
||||
return 0.5f * (float(nn) / 1073741824.0f);
|
||||
}
|
||||
|
||||
static float smoothstepf(const float f)
|
||||
{
|
||||
const float ff = f * f;
|
||||
return (3.0f * ff - 2.0f * ff * f);
|
||||
}
|
||||
|
||||
static float2 brick(float3 p,
|
||||
float mortar_size,
|
||||
float mortar_smooth,
|
||||
float bias,
|
||||
float brick_width,
|
||||
float row_height,
|
||||
float offset_amount,
|
||||
int offset_frequency,
|
||||
float squash_amount,
|
||||
int squash_frequency)
|
||||
{
|
||||
float offset = 0.0f;
|
||||
|
||||
const int rownum = int(floorf(p.y / row_height));
|
||||
|
||||
if (offset_frequency && squash_frequency) {
|
||||
brick_width *= (rownum % squash_frequency) ? 1.0f : squash_amount;
|
||||
offset = (rownum % offset_frequency) ? 0.0f : (brick_width * offset_amount);
|
||||
}
|
||||
|
||||
const int bricknum = int(floorf((p.x + offset) / brick_width));
|
||||
|
||||
const float x = (p.x + offset) - brick_width * bricknum;
|
||||
const float y = p.y - row_height * rownum;
|
||||
|
||||
const float tint = clamp_f(
|
||||
brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias, 0.0f, 1.0f);
|
||||
float min_dist = std::min({x, y, brick_width - x, row_height - y});
|
||||
|
||||
float mortar;
|
||||
if (min_dist >= mortar_size) {
|
||||
mortar = 0.0f;
|
||||
}
|
||||
else if (mortar_smooth == 0.0f) {
|
||||
mortar = 1.0f;
|
||||
}
|
||||
else {
|
||||
min_dist = 1.0f - min_dist / mortar_size;
|
||||
mortar = (min_dist < mortar_smooth) ? smoothstepf(min_dist / mortar_smooth) : 1.0f;
|
||||
}
|
||||
|
||||
return float2(tint, mortar);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
const VArray<ColorGeometry4f> &color1_values = params.readonly_single_input<ColorGeometry4f>(
|
||||
1, "Color1");
|
||||
const VArray<ColorGeometry4f> &color2_values = params.readonly_single_input<ColorGeometry4f>(
|
||||
2, "Color2");
|
||||
const VArray<ColorGeometry4f> &mortar_values = params.readonly_single_input<ColorGeometry4f>(
|
||||
3, "Mortar");
|
||||
const VArray<float> &scale = params.readonly_single_input<float>(4, "Scale");
|
||||
const VArray<float> &mortar_size = params.readonly_single_input<float>(5, "Mortar Size");
|
||||
const VArray<float> &mortar_smooth = params.readonly_single_input<float>(6, "Mortar Smooth");
|
||||
const VArray<float> &bias = params.readonly_single_input<float>(7, "Bias");
|
||||
const VArray<float> &brick_width = params.readonly_single_input<float>(8, "Brick Width");
|
||||
const VArray<float> &row_height = params.readonly_single_input<float>(9, "Row Height");
|
||||
|
||||
MutableSpan<ColorGeometry4f> r_color =
|
||||
params.uninitialized_single_output_if_required<ColorGeometry4f>(10, "Color");
|
||||
MutableSpan<float> r_fac = params.uninitialized_single_output_if_required<float>(11, "Fac");
|
||||
|
||||
const bool store_fac = !r_fac.is_empty();
|
||||
const bool store_color = !r_color.is_empty();
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float2 f2 = brick(vector[i] * scale[i],
|
||||
mortar_size[i],
|
||||
mortar_smooth[i],
|
||||
bias[i],
|
||||
brick_width[i],
|
||||
row_height[i],
|
||||
offset_,
|
||||
offset_freq_,
|
||||
squash_,
|
||||
squash_freq_);
|
||||
|
||||
float4 color_data, color1, color2, mortar;
|
||||
copy_v4_v4(color_data, color1_values[i]);
|
||||
copy_v4_v4(color1, color1_values[i]);
|
||||
copy_v4_v4(color2, color2_values[i]);
|
||||
copy_v4_v4(mortar, mortar_values[i]);
|
||||
const float tint = f2.x;
|
||||
const float f = f2.y;
|
||||
|
||||
if (f != 1.0f) {
|
||||
const float facm = 1.0f - tint;
|
||||
color_data = color1 * facm + color2 * tint;
|
||||
}
|
||||
|
||||
if (store_color) {
|
||||
color_data = color_data * (1.0f - f) + mortar * f;
|
||||
copy_v4_v4(r_color[i], color_data);
|
||||
}
|
||||
if (store_fac) {
|
||||
r_fac[i] = f;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(offset_);
|
||||
hash.add(offset_freq_);
|
||||
hash.add(squash_);
|
||||
hash.add(squash_freq_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_brick_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &node = builder.node();
|
||||
NodeTexBrick *tex = static_cast<NodeTexBrick *>(node.storage);
|
||||
|
||||
builder.construct_and_set_matching_fn<BrickFunction>(
|
||||
tex->offset, tex->offset_freq, tex->squash, tex->squash_freq);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_tex_brick_cc
|
||||
|
||||
void register_node_type_sh_tex_brick()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_brick_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeTexBrick"_ustr, SH_NODE_TEX_BRICK);
|
||||
ntype.ui_name = "Brick Texture";
|
||||
ntype.ui_description = "Generate a procedural texture producing bricks";
|
||||
ntype.enum_name_legacy = "TEX_BRICK";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_brick_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_tex_brick;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_brick;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeTexBrick", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_tex_brick;
|
||||
ntype.build_multi_function = file_ns::sh_node_brick_build_multi_function;
|
||||
ntype.default_width = bke::NodeWidth::_180;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,168 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_checker_cc {
|
||||
|
||||
static void sh_node_tex_checker_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
const bool is_compositor = b.tree_or_null() && b.tree_or_null()->type == NTREE_COMPOSIT;
|
||||
const NodeDefaultInputType default_input_type =
|
||||
is_compositor ? NODE_DEFAULT_INPUT_UNIFORM_IMAGE_COORDINATES :
|
||||
NODE_DEFAULT_INPUT_POSITION_FIELD;
|
||||
b.add_input<decl::Vector>("Vector"_ustr)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.default_input_type(default_input_type);
|
||||
|
||||
b.add_input<decl::Color>("Color1"_ustr)
|
||||
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
|
||||
.description("Color of the first checker");
|
||||
b.add_input<decl::Color>("Color2"_ustr)
|
||||
.default_value({0.2f, 0.2f, 0.2f, 1.0f})
|
||||
.description("Color of the second checker");
|
||||
b.add_input<decl::Float>("Scale"_ustr)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.default_value(5.0f)
|
||||
.no_muted_links()
|
||||
.description(
|
||||
"Overall texture scale.\n"
|
||||
"The scale is a factor of the bounding box of the face divided by the Scale value");
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
b.add_output<decl::Float>("Factor"_ustr, "Fac"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_checker(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeTexChecker *tex = MEM_new<NodeTexChecker>(__func__);
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_checker(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
|
||||
return GPU_stack_link(mat, node, "node_tex_checker", in, out);
|
||||
}
|
||||
|
||||
class NodeTexChecker : public mf::MultiFunction {
|
||||
public:
|
||||
NodeTexChecker()
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Checker", signature};
|
||||
builder.single_input<float3>("Vector");
|
||||
builder.single_input<ColorGeometry4f>("Color1");
|
||||
builder.single_input<ColorGeometry4f>("Color2");
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Fac");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
const VArray<ColorGeometry4f> &color1 = params.readonly_single_input<ColorGeometry4f>(
|
||||
1, "Color1");
|
||||
const VArray<ColorGeometry4f> &color2 = params.readonly_single_input<ColorGeometry4f>(
|
||||
2, "Color2");
|
||||
const VArray<float> &scale = params.readonly_single_input<float>(3, "Scale");
|
||||
MutableSpan<ColorGeometry4f> r_color =
|
||||
params.uninitialized_single_output_if_required<ColorGeometry4f>(4, "Color");
|
||||
MutableSpan<float> r_fac = params.uninitialized_single_output<float>(5, "Fac");
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
/* Avoid precision issues on unit coordinates. */
|
||||
const float3 p = (vector[i] * scale[i] + 0.000001f) * 0.999999f;
|
||||
|
||||
const int xi = abs(int(floorf(p.x)));
|
||||
const int yi = abs(int(floorf(p.y)));
|
||||
const int zi = abs(int(floorf(p.z)));
|
||||
|
||||
r_fac[i] = ((xi % 2 == yi % 2) == (zi % 2)) ? 1.0f : 0.0f;
|
||||
});
|
||||
|
||||
if (!r_color.is_empty()) {
|
||||
mask.foreach_index_optimized<int64_t>(
|
||||
[&](const int64_t i) { r_color[i] = (r_fac[i] == 1.0f) ? color1[i] : color2[i]; });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_tex_checker_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
static NodeTexChecker fn;
|
||||
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_->identifier, "Color")) {
|
||||
value1 = get_input_value("Color1", NodeItem::Type::Color3);
|
||||
value2 = get_input_value("Color2", NodeItem::Type::Color3);
|
||||
}
|
||||
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 nodes::node_shader_tex_checker_cc
|
||||
|
||||
void register_node_type_sh_tex_checker()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_checker_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeTexChecker"_ustr, SH_NODE_TEX_CHECKER);
|
||||
ntype.ui_name = "Checker Texture";
|
||||
ntype.ui_description = "Generate a checkerboard texture";
|
||||
ntype.enum_name_legacy = "TEX_CHECKER";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_checker_declare;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_checker;
|
||||
bke::node_type_storage(
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,125 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_coord_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Vector>("Generated"_ustr).translation_context(BLT_I18NCONTEXT_ID_TEXTURE);
|
||||
b.add_output<decl::Vector>("Normal"_ustr);
|
||||
b.add_output<decl::Vector>("UV"_ustr);
|
||||
b.add_output<decl::Vector>("Object"_ustr);
|
||||
b.add_output<decl::Vector>("Camera"_ustr);
|
||||
b.add_output<decl::Vector>("Window"_ustr);
|
||||
b.add_output<decl::Vector>("Reflection"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_tex_coord(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "object", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
layout.prop(ptr, "from_instancer", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_coord(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
Object *ob = (Object *)node->id;
|
||||
|
||||
/* Use special matrix to let the shader branch to using the render object's matrix. */
|
||||
float dummy_matrix[4][4];
|
||||
dummy_matrix[3][3] = 0.0f;
|
||||
GPUNodeLink *inv_obmat = (ob != nullptr) ? GPU_uniform(&ob->world_to_object()[0][0]) :
|
||||
GPU_uniform(&dummy_matrix[0][0]);
|
||||
|
||||
/* Optimization: don't request orco if not needed. */
|
||||
float4 zero(0.0f);
|
||||
GPUNodeLink *orco = out[0].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(zero);
|
||||
GPUNodeLink *mtface = GPU_attribute(mat, CD_AUTO_FROM_NAME, "");
|
||||
|
||||
GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface);
|
||||
|
||||
for (const auto [i, sock] : node->outputs.enumerate()) {
|
||||
node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
|
||||
/* Normalize some vectors after dFdx/dFdy offsets.
|
||||
* This is the case for interpolated, non linear functions.
|
||||
* The resulting vector can still be a bit wrong but not as much.
|
||||
* (see #70644) */
|
||||
if (ELEM(i, 1, 6)) {
|
||||
GPU_link(mat,
|
||||
"vector_math_normalize",
|
||||
out[i].link,
|
||||
out[i].link,
|
||||
out[i].link,
|
||||
out[i].link,
|
||||
&out[i].link,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
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_->identifier;
|
||||
|
||||
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("object"))}});
|
||||
}
|
||||
else {
|
||||
res = get_output_default(name, NodeItem::Type::Any);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_tex_coord_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_tex_coord()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_coord_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeTexCoord"_ustr, SH_NODE_TEX_COORD);
|
||||
ntype.ui_name = "Texture Coordinate";
|
||||
ntype.ui_description =
|
||||
"Retrieve multiple types of texture coordinates.\nTypically used as inputs for texture "
|
||||
"nodes";
|
||||
ntype.enum_name_legacy = "TEX_COORD";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,215 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_environment_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Vector>("Vector"_ustr).hide_value();
|
||||
b.add_output<decl::Color>("Color"_ustr).no_muted_links();
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_environment(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeTexEnvironment *tex = MEM_new<NodeTexEnvironment>("NodeTexEnvironment");
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
tex->projection = SHD_PROJ_EQUIRECTANGULAR;
|
||||
BKE_imageuser_default(&tex->iuser);
|
||||
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_environment(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
Image *ima = id_cast<Image *>(node->id);
|
||||
NodeTexEnvironment *tex = static_cast<NodeTexEnvironment *>(node->storage);
|
||||
|
||||
/* We get the image user from the original node, since GPU image keeps
|
||||
* a pointer to it and the dependency refreshes the original. */
|
||||
bNode *node_original = node->runtime->original ? node->runtime->original : node;
|
||||
NodeTexImage *tex_original = static_cast<NodeTexImage *>(node_original->storage);
|
||||
ImageUser *iuser = &tex_original->iuser;
|
||||
/* Setting the GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE enables anisotropic filtering. The
|
||||
* exact number of samples are being determined at bind time by the engine.
|
||||
* See #blender::draw::PassBase<T>::material_set */
|
||||
GPUSamplerState sampler = {GPU_SAMPLER_FILTERING_LINEAR |
|
||||
GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE,
|
||||
GPU_SAMPLER_EXTEND_MODE_REPEAT,
|
||||
GPU_SAMPLER_EXTEND_MODE_REPEAT};
|
||||
/* TODO(@fclem): For now assume mipmap is always enabled. */
|
||||
if (true) {
|
||||
sampler.enable_filtering_flag(GPU_SAMPLER_FILTERING_MIPMAP);
|
||||
}
|
||||
|
||||
GPUNodeLink *outalpha;
|
||||
|
||||
if (!ima) {
|
||||
return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out);
|
||||
}
|
||||
|
||||
if (!in[0].link) {
|
||||
GPU_link(mat, "node_tex_coord_position", &in[0].link);
|
||||
node_shader_gpu_bump_tex_coord(mat, node, &in[0].link);
|
||||
}
|
||||
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
|
||||
/* Compute texture coordinate. */
|
||||
if (tex->projection == SHD_PROJ_EQUIRECTANGULAR) {
|
||||
GPU_link(mat, "node_tex_environment_equirectangular", in[0].link, &in[0].link);
|
||||
/* To fix pole issue we clamp the v coordinate. */
|
||||
sampler.extend_yz = GPU_SAMPLER_EXTEND_MODE_EXTEND;
|
||||
/* Force the highest mipmap and don't do anisotropic filtering.
|
||||
* This is to fix the artifact caused by derivatives discontinuity. */
|
||||
sampler.disable_filtering_flag(GPU_SAMPLER_FILTERING_MIPMAP |
|
||||
GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK);
|
||||
}
|
||||
else {
|
||||
GPU_link(mat, "node_tex_environment_mirror_ball", in[0].link, &in[0].link);
|
||||
/* Fix pole issue. */
|
||||
sampler.extend_x = GPU_SAMPLER_EXTEND_MODE_EXTEND;
|
||||
sampler.extend_yz = GPU_SAMPLER_EXTEND_MODE_EXTEND;
|
||||
}
|
||||
|
||||
const char *gpu_fn;
|
||||
static const char *names[] = {
|
||||
"node_tex_image_linear",
|
||||
"node_tex_image_cubic",
|
||||
};
|
||||
|
||||
switch (tex->interpolation) {
|
||||
case SHD_INTERP_LINEAR:
|
||||
gpu_fn = names[0];
|
||||
break;
|
||||
case SHD_INTERP_CLOSEST:
|
||||
sampler.disable_filtering_flag(GPU_SAMPLER_FILTERING_LINEAR | GPU_SAMPLER_FILTERING_MIPMAP);
|
||||
gpu_fn = names[0];
|
||||
break;
|
||||
default:
|
||||
gpu_fn = names[1];
|
||||
break;
|
||||
}
|
||||
|
||||
/* Sample texture with correct interpolation. */
|
||||
GPU_link(mat, gpu_fn, in[0].link, GPU_image(mat, ima, iuser, sampler), &out[0].link, &outalpha);
|
||||
|
||||
if (out[0].hasoutput && ima) {
|
||||
if (ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) ||
|
||||
IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name))
|
||||
{
|
||||
/* Don't let alpha affect color output in these cases. */
|
||||
GPU_link(mat, "color_alpha_clear", out[0].link, &out[0].link);
|
||||
}
|
||||
else {
|
||||
/* Always output with premultiplied alpha. */
|
||||
if (ima->alpha_mode == IMA_ALPHA_PREMUL) {
|
||||
GPU_link(mat, "color_alpha_clear", out[0].link, &out[0].link);
|
||||
}
|
||||
else {
|
||||
GPU_link(mat, "color_alpha_premultiply", out[0].link, &out[0].link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (graph_.export_params.image_fn) {
|
||||
Scene *scene = DEG_get_input_scene(graph_.depsgraph);
|
||||
Main *bmain = DEG_get_bmain(graph_.depsgraph);
|
||||
image_path = graph_.export_params.image_fn(bmain, scene, image, &tex_env->iuser);
|
||||
}
|
||||
|
||||
NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector2);
|
||||
if (!vector) {
|
||||
vector = texcoord_node();
|
||||
}
|
||||
/* TODO: texture-coordinates 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 nodes::node_shader_tex_environment_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_tex_environment()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_environment_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeTexEnvironment"_ustr, SH_NODE_TEX_ENVIRONMENT);
|
||||
ntype.ui_name = "Environment Texture";
|
||||
ntype.ui_description =
|
||||
"Sample an image file as an environment texture. Typically used to light the scene with the "
|
||||
"background node";
|
||||
ntype.enum_name_legacy = "TEX_ENVIRONMENT";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_environment;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeTexEnvironment", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_tex_environment;
|
||||
ntype.labelfunc = node_image_label;
|
||||
ntype.default_width = bke::NodeWidth::_240;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,236 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <numbers>
|
||||
|
||||
#include "BLI_noise.hh"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_gabor_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeTexGabor)
|
||||
|
||||
static void sh_node_tex_gabor_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
const bool is_compositor = b.tree_or_null() && b.tree_or_null()->type == NTREE_COMPOSIT;
|
||||
const NodeDefaultInputType default_input_type =
|
||||
is_compositor ? NODE_DEFAULT_INPUT_UNIFORM_IMAGE_COORDINATES :
|
||||
NODE_DEFAULT_INPUT_POSITION_FIELD;
|
||||
b.add_input<decl::Vector>("Vector"_ustr)
|
||||
.default_input_type(default_input_type)
|
||||
.description(
|
||||
"The coordinates at which Gabor noise will be evaluated. The Z component is ignored in "
|
||||
"the 2D case");
|
||||
|
||||
b.add_input<decl::Float>("Scale"_ustr)
|
||||
.default_value(5.0f)
|
||||
.description("The scale of the Gabor noise");
|
||||
b.add_input<decl::Float>("Frequency"_ustr)
|
||||
.default_value(2.0f)
|
||||
.min(0.0f)
|
||||
.description(
|
||||
"The rate at which the Gabor noise changes across space. This is different from the "
|
||||
"Scale input in that it only scales perpendicular to the Gabor noise direction");
|
||||
b.add_input<decl::Float>("Anisotropy"_ustr)
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"The directionality of Gabor noise. 1 means the noise is completely directional, while "
|
||||
"0 means the noise is omnidirectional");
|
||||
|
||||
const NodeGaborType gabor_type = b.node_or_null() ? node_storage(*b.node_or_null()).type :
|
||||
SHD_GABOR_TYPE_2D;
|
||||
b.add_input<decl::Float>("Orientation"_ustr, "Orientation 2D"_ustr)
|
||||
.default_value(std::numbers::pi / 4)
|
||||
.subtype(PROP_ANGLE)
|
||||
.available(gabor_type == SHD_GABOR_TYPE_2D)
|
||||
.description("The direction of the anisotropic Gabor noise");
|
||||
b.add_input<decl::Vector>("Orientation"_ustr, "Orientation 3D"_ustr)
|
||||
.default_value({std::numbers::sqrt2, std::numbers::sqrt2, 0.0f})
|
||||
.subtype(PROP_DIRECTION)
|
||||
.available(gabor_type == SHD_GABOR_TYPE_3D)
|
||||
.description("The direction of the anisotropic Gabor noise");
|
||||
|
||||
b.add_output<decl::Float>("Value"_ustr)
|
||||
.description(
|
||||
"The Gabor noise value with both random intensity and phase. This is equal to sine the "
|
||||
"phase multiplied by the intensity");
|
||||
b.add_output<decl::Float>("Phase"_ustr)
|
||||
.description("The phase of the Gabor noise, which has no random intensity");
|
||||
b.add_output<decl::Float>("Intensity"_ustr)
|
||||
.description("The intensity of the Gabor noise, which has no random phase");
|
||||
}
|
||||
|
||||
static void node_shader_buts_tex_gabor(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "gabor_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_gabor(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeTexGabor *storage = MEM_new<NodeTexGabor>(__func__);
|
||||
BKE_texture_mapping_default(&storage->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&storage->base.color_mapping);
|
||||
|
||||
storage->type = SHD_GABOR_TYPE_2D;
|
||||
|
||||
node->storage = storage;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_gabor(GPUMaterial *material,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
node_shader_gpu_default_tex_coord(material, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(material, node, in, out);
|
||||
|
||||
const float type = float(node_storage(*node).type);
|
||||
return GPU_stack_link(material, node, "node_tex_gabor", in, out, GPU_constant(&type));
|
||||
}
|
||||
|
||||
class GaborNoiseFunction : public mf::MultiFunction {
|
||||
private:
|
||||
NodeGaborType type_;
|
||||
|
||||
public:
|
||||
GaborNoiseFunction(const NodeGaborType type) : type_(type)
|
||||
{
|
||||
static std::array<mf::Signature, 2> signatures{
|
||||
create_signature(SHD_GABOR_TYPE_2D),
|
||||
create_signature(SHD_GABOR_TYPE_3D),
|
||||
};
|
||||
this->set_signature(&signatures[type]);
|
||||
}
|
||||
|
||||
static mf::Signature create_signature(const NodeGaborType type)
|
||||
{
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"GaborNoise", signature};
|
||||
|
||||
builder.single_input<float3>("Vector");
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_input<float>("Frequency");
|
||||
builder.single_input<float>("Anisotropy");
|
||||
|
||||
if (type == SHD_GABOR_TYPE_2D) {
|
||||
builder.single_input<float>("Orientation");
|
||||
}
|
||||
else {
|
||||
builder.single_input<float3>("Orientation");
|
||||
}
|
||||
|
||||
builder.single_output<float>("Value", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Phase", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Intensity", mf::ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
const VArray<float> &scale = params.readonly_single_input<float>(1, "Scale");
|
||||
const VArray<float> &frequency = params.readonly_single_input<float>(2, "Frequency");
|
||||
const VArray<float> &anisotropy = params.readonly_single_input<float>(3, "Anisotropy");
|
||||
/* A parameter index of 4 is reserved for Orientation input below. */
|
||||
MutableSpan<float> r_value = params.uninitialized_single_output_if_required<float>(5, "Value");
|
||||
MutableSpan<float> r_phase = params.uninitialized_single_output_if_required<float>(6, "Phase");
|
||||
MutableSpan<float> r_intensity = params.uninitialized_single_output_if_required<float>(
|
||||
7, "Intensity");
|
||||
|
||||
switch (type_) {
|
||||
case SHD_GABOR_TYPE_2D: {
|
||||
const VArray<float> &orientation = params.readonly_single_input<float>(4, "Orientation");
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
noise::gabor(vector[i].xy(),
|
||||
scale[i],
|
||||
frequency[i],
|
||||
anisotropy[i],
|
||||
orientation[i],
|
||||
r_value.is_empty() ? nullptr : &r_value[i],
|
||||
r_phase.is_empty() ? nullptr : &r_phase[i],
|
||||
r_intensity.is_empty() ? nullptr : &r_intensity[i]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SHD_GABOR_TYPE_3D: {
|
||||
const VArray<float3> &orientation = params.readonly_single_input<float3>(4, "Orientation");
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
noise::gabor(vector[i],
|
||||
scale[i],
|
||||
frequency[i],
|
||||
anisotropy[i],
|
||||
orientation[i],
|
||||
r_value.is_empty() ? nullptr : &r_value[i],
|
||||
r_phase.is_empty() ? nullptr : &r_phase[i],
|
||||
r_intensity.is_empty() ? nullptr : &r_intensity[i]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(type_);
|
||||
}
|
||||
|
||||
ExecutionHints get_execution_hints() const override
|
||||
{
|
||||
ExecutionHints hints;
|
||||
hints.allocates_array = false;
|
||||
hints.min_grain_size = 100;
|
||||
return hints;
|
||||
}
|
||||
};
|
||||
|
||||
static void build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const NodeTexGabor &storage = node_storage(builder.node());
|
||||
builder.construct_and_set_matching_fn<GaborNoiseFunction>(NodeGaborType(storage.type));
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_tex_gabor_cc
|
||||
|
||||
void register_node_type_sh_tex_gabor()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_gabor_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeTexGabor"_ustr, SH_NODE_TEX_GABOR);
|
||||
ntype.ui_name = "Gabor Texture";
|
||||
ntype.ui_description = "Generate Gabor noise";
|
||||
ntype.enum_name_legacy = "TEX_GABOR";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_gabor_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_tex_gabor;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_gabor;
|
||||
node_type_storage(ntype, "NodeTexGabor", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_tex_gabor;
|
||||
ntype.build_multi_function = file_ns::build_multi_function;
|
||||
|
||||
node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,238 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Foundation. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "BLI_math_constants.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_gradient_cc {
|
||||
|
||||
static void sh_node_tex_gradient_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
const bool is_compositor = b.tree_or_null() && b.tree_or_null()->type == NTREE_COMPOSIT;
|
||||
const NodeDefaultInputType default_input_type =
|
||||
is_compositor ? NODE_DEFAULT_INPUT_UNIFORM_IMAGE_COORDINATES :
|
||||
NODE_DEFAULT_INPUT_POSITION_FIELD;
|
||||
b.add_input<decl::Vector>("Vector"_ustr).default_input_type(default_input_type);
|
||||
|
||||
b.add_output<decl::Color>("Color"_ustr).no_muted_links();
|
||||
b.add_output<decl::Float>("Factor"_ustr, "Fac"_ustr).no_muted_links();
|
||||
}
|
||||
|
||||
static void node_shader_buts_tex_gradient(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "gradient_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_gradient(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeTexGradient *tex = MEM_new<NodeTexGradient>(__func__);
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
tex->gradient_type = SHD_BLEND_LINEAR;
|
||||
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_gradient(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
|
||||
NodeTexGradient *tex = static_cast<NodeTexGradient *>(node->storage);
|
||||
float gradient_type = tex->gradient_type;
|
||||
return GPU_stack_link(mat, node, "node_tex_gradient", in, out, GPU_constant(&gradient_type));
|
||||
}
|
||||
|
||||
class GradientFunction : public mf::MultiFunction {
|
||||
private:
|
||||
int gradient_type_;
|
||||
|
||||
public:
|
||||
GradientFunction(int gradient_type) : gradient_type_(gradient_type)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"GradientFunction", signature};
|
||||
builder.single_input<float3>("Vector");
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Fac");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
|
||||
MutableSpan<ColorGeometry4f> r_color =
|
||||
params.uninitialized_single_output_if_required<ColorGeometry4f>(1, "Color");
|
||||
MutableSpan<float> fac = params.uninitialized_single_output<float>(2, "Fac");
|
||||
|
||||
const bool compute_color = !r_color.is_empty();
|
||||
|
||||
switch (gradient_type_) {
|
||||
case SHD_BLEND_LINEAR: {
|
||||
mask.foreach_index_optimized<int64_t>(
|
||||
[&](const int64_t i) { fac[i] = math::clamp(vector[i].x, 0.0f, 1.0f); });
|
||||
break;
|
||||
}
|
||||
case SHD_BLEND_QUADRATIC: {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
const float r = std::max(vector[i].x, 0.0f);
|
||||
fac[i] = math::clamp(r * r, 0.0f, 1.0f);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SHD_BLEND_EASING: {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
const float r = std::min(std::max(vector[i].x, 0.0f), 1.0f);
|
||||
const float t = r * r;
|
||||
fac[i] = (3.0f * t - 2.0f * t * r);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SHD_BLEND_DIAGONAL: {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
fac[i] = (vector[i].x + vector[i].y) * 0.5f;
|
||||
fac[i] = math::clamp(fac[i], 0.0f, 1.0f);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SHD_BLEND_RADIAL: {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
fac[i] = atan2f(vector[i].y, vector[i].x) / (M_PI * 2.0f) + 0.5f;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SHD_BLEND_QUADRATIC_SPHERE: {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
/* Bias a little bit for the case where input is a unit length vector,
|
||||
* to get exactly zero instead of a small random value depending
|
||||
* on float precision. */
|
||||
const float r = std::max(0.999999f - math::length(vector[i]), 0.0f);
|
||||
fac[i] = r * r;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SHD_BLEND_SPHERICAL: {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
/* Bias a little bit for the case where input is a unit length vector,
|
||||
* to get exactly zero instead of a small random value depending
|
||||
* on float precision. */
|
||||
fac[i] = std::max(0.999999f - math::length(vector[i]), 0.0f);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (compute_color) {
|
||||
mask.foreach_index_optimized<int64_t>(
|
||||
[&](const int64_t i) { r_color[i] = ColorGeometry4f(fac[i], fac[i], fac[i], 1.0f); });
|
||||
}
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(gradient_type_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_gradient_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &node = builder.node();
|
||||
NodeTexGradient *tex = static_cast<NodeTexGradient *>(node.storage);
|
||||
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 nodes::node_shader_tex_gradient_cc
|
||||
|
||||
void register_node_type_sh_tex_gradient()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_gradient_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeTexGradient"_ustr, SH_NODE_TEX_GRADIENT);
|
||||
ntype.ui_name = "Gradient Texture";
|
||||
ntype.ui_description =
|
||||
"Generate interpolated color and intensity values based on the input vector";
|
||||
ntype.enum_name_legacy = "TEX_GRADIENT";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_gradient_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_tex_gradient;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_gradient;
|
||||
bke::node_type_storage(
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,306 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_image_cc {
|
||||
|
||||
static void sh_node_tex_image_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Vector>("Vector"_ustr).default_input_type(NODE_DEFAULT_INPUT_POSITION_FIELD);
|
||||
b.add_output<decl::Color>("Color"_ustr).no_muted_links();
|
||||
b.add_output<decl::Float>("Alpha"_ustr).no_muted_links();
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_image(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeTexImage *tex = MEM_new<NodeTexImage>(__func__);
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
BKE_imageuser_default(&tex->iuser);
|
||||
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_image(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
Image *ima = id_cast<Image *>(node->id);
|
||||
NodeTexImage *tex = static_cast<NodeTexImage *>(node->storage);
|
||||
|
||||
/* We get the image user from the original node, since GPU image keeps
|
||||
* a pointer to it and the dependency refreshes the original. */
|
||||
bNode *node_original = node->runtime->original ? node->runtime->original : node;
|
||||
NodeTexImage *tex_original = static_cast<NodeTexImage *>(node_original->storage);
|
||||
ImageUser *iuser = &tex_original->iuser;
|
||||
|
||||
if (!ima) {
|
||||
return GPU_stack_link(mat, node, "node_tex_image_empty", in, out);
|
||||
}
|
||||
|
||||
GPUNodeLink **texco = &in[0].link;
|
||||
if (!*texco) {
|
||||
*texco = GPU_attribute(mat, CD_AUTO_FROM_NAME, "");
|
||||
node_shader_gpu_bump_tex_coord(mat, node, texco);
|
||||
}
|
||||
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
|
||||
GPUSamplerState sampler_state = GPUSamplerState::default_sampler();
|
||||
|
||||
switch (tex->extension) {
|
||||
case SHD_IMAGE_EXTENSION_EXTEND:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_EXTEND;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_EXTEND;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_REPEAT:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_CLIP:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_MIRROR:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (tex->interpolation != SHD_INTERP_CLOSEST) {
|
||||
/* TODO(fclem): For now assume mipmap is always enabled. */
|
||||
/* Setting the GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE enables anisotropic filtering. The
|
||||
* exact number of samples are being determined at bind time by the engine.
|
||||
* See #blender::draw::PassBase<T>::material_set */
|
||||
sampler_state.filtering = GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE |
|
||||
GPU_SAMPLER_FILTERING_LINEAR | GPU_SAMPLER_FILTERING_MIPMAP;
|
||||
}
|
||||
const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART);
|
||||
|
||||
/* Only use UDIM tiles if projection is flat.
|
||||
* Otherwise treat the first tile as a single image. (See #141776). */
|
||||
const bool use_udim = ima->source == IMA_SRC_TILED && tex->projection == SHD_PROJ_FLAT;
|
||||
if (use_udim) {
|
||||
const char *gpu_node_name = use_cubic ? "node_tex_tile_cubic" : "node_tex_tile_linear";
|
||||
GPUNodeLink *gpu_image, *gpu_image_tile_mapping;
|
||||
GPU_image_tiled(mat, ima, iuser, sampler_state, &gpu_image, &gpu_image_tile_mapping);
|
||||
/* UDIM tiles needs a `sampler2DArray` and `sampler1DArray` for tile mapping. */
|
||||
GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image, gpu_image_tile_mapping);
|
||||
}
|
||||
else {
|
||||
const char *gpu_node_name = use_cubic ? "node_tex_image_cubic" : "node_tex_image_linear";
|
||||
|
||||
switch (tex->projection) {
|
||||
case SHD_PROJ_FLAT: {
|
||||
GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state);
|
||||
GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image);
|
||||
break;
|
||||
}
|
||||
case SHD_PROJ_BOX: {
|
||||
gpu_node_name = use_cubic ? "tex_box_sample_cubic" : "tex_box_sample_linear";
|
||||
GPUNodeLink *vnor, *wnor, *col1, *col2, *col3;
|
||||
GPUNodeLink *blend = GPU_uniform(&tex->projection_blend);
|
||||
GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state);
|
||||
GPU_link(mat, "world_normals_get", &vnor);
|
||||
GPU_link(mat, "normal_transform_world_to_object", vnor, &wnor);
|
||||
GPU_link(mat, gpu_node_name, in[0].link, wnor, gpu_image, &col1, &col2, &col3);
|
||||
GPU_link(mat, "tex_box_blend", wnor, col1, col2, col3, blend, &out[0].link, &out[1].link);
|
||||
break;
|
||||
}
|
||||
case SHD_PROJ_SPHERE: {
|
||||
/* This projection is known to have a derivative discontinuity.
|
||||
* Hide it by turning off mipmapping. */
|
||||
sampler_state.disable_filtering_flag(GPU_SAMPLER_FILTERING_MIPMAP);
|
||||
GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state);
|
||||
GPU_link(mat, "point_texco_remap_square", *texco, texco);
|
||||
GPU_link(mat, "point_map_to_sphere", *texco, texco);
|
||||
GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image);
|
||||
break;
|
||||
}
|
||||
case SHD_PROJ_TUBE: {
|
||||
/* This projection is known to have a derivative discontinuity.
|
||||
* Hide it by turning off mipmapping. */
|
||||
sampler_state.disable_filtering_flag(GPU_SAMPLER_FILTERING_MIPMAP);
|
||||
GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state);
|
||||
GPU_link(mat, "point_texco_remap_square", *texco, texco);
|
||||
GPU_link(mat, "point_map_to_tube", *texco, texco);
|
||||
GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (out[0].hasoutput) {
|
||||
if (ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) ||
|
||||
IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name))
|
||||
{
|
||||
/* Don't let alpha affect color output in these cases. */
|
||||
GPU_link(mat, "color_alpha_clear", out[0].link, &out[0].link);
|
||||
}
|
||||
else {
|
||||
/* Output premultiplied alpha depending on alpha socket usage. This makes
|
||||
* it so that if we blend the color with a transparent shader using alpha as
|
||||
* a factor, we don't multiply alpha into the color twice. And if we do
|
||||
* not, then there will be no artifacts from zero alpha areas. */
|
||||
if (ima->alpha_mode == IMA_ALPHA_PREMUL) {
|
||||
if (out[1].hasoutput) {
|
||||
GPU_link(mat, "color_alpha_unpremultiply", out[0].link, &out[0].link);
|
||||
}
|
||||
else {
|
||||
GPU_link(mat, "color_alpha_clear", out[0].link, &out[0].link);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (out[1].hasoutput) {
|
||||
GPU_link(mat, "color_alpha_clear", out[0].link, &out[0].link);
|
||||
}
|
||||
else {
|
||||
GPU_link(mat, "color_alpha_premultiply", out[0].link, &out[0].link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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("Color");
|
||||
|
||||
NodeItem res = graph_.get_node(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 (graph_.export_params.image_fn) {
|
||||
Scene *scene = DEG_get_input_scene(graph_.depsgraph);
|
||||
Main *bmain = DEG_get_bmain(graph_.depsgraph);
|
||||
image_path = graph_.export_params.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();
|
||||
}
|
||||
|
||||
NodeItem::Type node_type = NodeItem::Type::Color4;
|
||||
const char *node_colorspace = nullptr;
|
||||
|
||||
const char *image_colorspace = image->colorspace_settings.name;
|
||||
if (IMB_colormanagement_space_name_is_data(image_colorspace)) {
|
||||
node_type = NodeItem::Type::Vector4;
|
||||
}
|
||||
else if (IMB_colormanagement_space_name_is_scene_linear(image_colorspace)) {
|
||||
node_colorspace = "lin_rec709";
|
||||
}
|
||||
else if (IMB_colormanagement_space_name_is_srgb(image_colorspace)) {
|
||||
node_colorspace = "srgb_texture";
|
||||
}
|
||||
|
||||
res = create_node("image",
|
||||
node_type,
|
||||
{{"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 (node_colorspace) {
|
||||
res.node->setAttribute("colorspace", node_colorspace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (STREQ(socket_out_->identifier, "Alpha")) {
|
||||
res = res[3];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_tex_image_cc
|
||||
|
||||
void register_node_type_sh_tex_image()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_image_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeTexImage"_ustr, SH_NODE_TEX_IMAGE);
|
||||
ntype.ui_name = "Image Texture";
|
||||
ntype.ui_description = "Sample an image file as a texture";
|
||||
ntype.enum_name_legacy = "TEX_IMAGE";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_image_declare;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_image;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeTexImage", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_tex_image;
|
||||
ntype.labelfunc = node_image_label;
|
||||
ntype.default_width = bke::NodeWidth::_240;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,220 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_magic_cc {
|
||||
|
||||
static void sh_node_tex_magic_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
const bool is_compositor = b.tree_or_null() && b.tree_or_null()->type == NTREE_COMPOSIT;
|
||||
const NodeDefaultInputType default_input_type =
|
||||
is_compositor ? NODE_DEFAULT_INPUT_UNIFORM_IMAGE_COORDINATES :
|
||||
NODE_DEFAULT_INPUT_POSITION_FIELD;
|
||||
b.add_input<decl::Vector>("Vector"_ustr).default_input_type(default_input_type);
|
||||
|
||||
b.add_input<decl::Float>("Scale"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(5.0f)
|
||||
.description("Scale of the texture");
|
||||
b.add_input<decl::Float>("Distortion"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(1.0f)
|
||||
.description("Amount of distortion");
|
||||
b.add_output<decl::Color>("Color"_ustr).no_muted_links();
|
||||
b.add_output<decl::Float>("Factor"_ustr, "Fac"_ustr).no_muted_links();
|
||||
}
|
||||
|
||||
static void node_shader_buts_tex_magic(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "turbulence_depth", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_magic(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeTexMagic *tex = MEM_new<NodeTexMagic>(__func__);
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
tex->depth = 2;
|
||||
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_magic(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
NodeTexMagic *tex = static_cast<NodeTexMagic *>(node->storage);
|
||||
float depth = tex->depth;
|
||||
|
||||
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
|
||||
return GPU_stack_link(mat, node, "node_tex_magic", in, out, GPU_constant(&depth));
|
||||
}
|
||||
|
||||
class MagicFunction : public mf::MultiFunction {
|
||||
private:
|
||||
int depth_;
|
||||
|
||||
public:
|
||||
MagicFunction(int depth) : depth_(depth)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"MagicFunction", signature};
|
||||
builder.single_input<float3>("Vector");
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_input<float>("Distortion");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Fac", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
const VArray<float> &scale = params.readonly_single_input<float>(1, "Scale");
|
||||
const VArray<float> &distortion = params.readonly_single_input<float>(2, "Distortion");
|
||||
|
||||
MutableSpan<ColorGeometry4f> r_color = params.uninitialized_single_output<ColorGeometry4f>(
|
||||
3, "Color");
|
||||
MutableSpan<float> r_fac = params.uninitialized_single_output_if_required<float>(4, "Fac");
|
||||
|
||||
const bool compute_factor = !r_fac.is_empty();
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 co = vector[i] * scale[i];
|
||||
const float distort = distortion[i];
|
||||
float x = sinf((co[0] + co[1] + co[2]) * 5.0f);
|
||||
float y = cosf((-co[0] + co[1] - co[2]) * 5.0f);
|
||||
float z = -cosf((-co[0] - co[1] + co[2]) * 5.0f);
|
||||
|
||||
if (depth_ > 0) {
|
||||
x *= distort;
|
||||
y *= distort;
|
||||
z *= distort;
|
||||
y = -cosf(x - y + z);
|
||||
y *= distort;
|
||||
|
||||
if (depth_ > 1) {
|
||||
x = cosf(x - y - z);
|
||||
x *= distort;
|
||||
|
||||
if (depth_ > 2) {
|
||||
z = sinf(-x - y - z);
|
||||
z *= distort;
|
||||
|
||||
if (depth_ > 3) {
|
||||
x = -cosf(-x + y - z);
|
||||
x *= distort;
|
||||
|
||||
if (depth_ > 4) {
|
||||
y = -sinf(-x + y + z);
|
||||
y *= distort;
|
||||
|
||||
if (depth_ > 5) {
|
||||
y = -cosf(-x + y + z);
|
||||
y *= distort;
|
||||
|
||||
if (depth_ > 6) {
|
||||
x = cosf(x + y + z);
|
||||
x *= distort;
|
||||
|
||||
if (depth_ > 7) {
|
||||
z = sinf(x + y - z);
|
||||
z *= distort;
|
||||
|
||||
if (depth_ > 8) {
|
||||
x = -cosf(-x - y + z);
|
||||
x *= distort;
|
||||
|
||||
if (depth_ > 9) {
|
||||
y = -sinf(x - y + z);
|
||||
y *= distort;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (distort != 0.0f) {
|
||||
const float d = distort * 2.0f;
|
||||
x /= d;
|
||||
y /= d;
|
||||
z /= d;
|
||||
}
|
||||
|
||||
r_color[i] = ColorGeometry4f(0.5f - x, 0.5f - y, 0.5f - z, 1.0f);
|
||||
});
|
||||
if (compute_factor) {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
r_fac[i] = (r_color[i].r + r_color[i].g + r_color[i].b) * (1.0f / 3.0f);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(depth_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_magic_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &node = builder.node();
|
||||
NodeTexMagic *tex = static_cast<NodeTexMagic *>(node.storage);
|
||||
builder.construct_and_set_matching_fn<MagicFunction>(tex->depth);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_tex_magic_cc
|
||||
|
||||
void register_node_type_sh_tex_magic()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_magic_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeTexMagic"_ustr, SH_NODE_TEX_MAGIC);
|
||||
ntype.ui_name = "Magic Texture";
|
||||
ntype.ui_description = "Generate a psychedelic color texture";
|
||||
ntype.enum_name_legacy = "TEX_MAGIC";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_magic_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_tex_magic;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_magic;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeTexMagic", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_tex_magic;
|
||||
ntype.build_multi_function = file_ns::sh_node_magic_tex_build_multi_function;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,515 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "BLI_noise.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_noise_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeTexNoise)
|
||||
|
||||
static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
const int dimensions = b.node_or_null() ? node_storage(*b.node_or_null()).dimensions : 3;
|
||||
const bool is_compositor = b.tree_or_null() && b.tree_or_null()->type == NTREE_COMPOSIT;
|
||||
const NodeDefaultInputType default_input_type =
|
||||
is_compositor ? NODE_DEFAULT_INPUT_UNIFORM_IMAGE_COORDINATES :
|
||||
NODE_DEFAULT_INPUT_POSITION_FIELD;
|
||||
b.add_input<decl::Vector>("Vector"_ustr)
|
||||
.default_input_type(default_input_type)
|
||||
.available(dimensions != 1);
|
||||
|
||||
b.add_input<decl::Float>("W"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.available(dimensions == 1 || dimensions == 4)
|
||||
.make_available([](bNode &node) {
|
||||
/* Default to 1 instead of 4, because it is much faster. */
|
||||
node_storage(node).dimensions = 1;
|
||||
});
|
||||
|
||||
b.add_input<decl::Float>("Scale"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(5.0f)
|
||||
.description("Scale of the base noise octave");
|
||||
b.add_input<decl::Float>("Detail"_ustr)
|
||||
.min(0.0f)
|
||||
.max(15.0f)
|
||||
.default_value(2.0f)
|
||||
.description(
|
||||
"The number of noise octaves. Higher values give more detailed noise but increase "
|
||||
"render "
|
||||
"time");
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(0.5f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Blend factor between an octave and its previous one. A value of zero corresponds to "
|
||||
"zero detail");
|
||||
b.add_input<decl::Float>("Lacunarity"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(2.0f)
|
||||
.description(
|
||||
"The difference between the scale of each two consecutive octaves. Larger values "
|
||||
"corresponds to larger scale for higher octaves");
|
||||
|
||||
const eNodeNoiseTexture_Type noise_type = eNodeNoiseTexture_Type(
|
||||
b.node_or_null() ? eNodeNoiseTexture_Type(node_storage(*b.node_or_null()).type) :
|
||||
SHD_NOISE_FBM);
|
||||
b.add_input<decl::Float>("Offset"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(0.0f)
|
||||
.available(noise_type != SHD_NOISE_MULTIFRACTAL && noise_type != SHD_NOISE_FBM)
|
||||
.make_available([](bNode &node) { node_storage(node).type = SHD_NOISE_RIDGED_MULTIFRACTAL; })
|
||||
.description(
|
||||
"An added offset to each octave, determines the level where the highest octave will "
|
||||
"appear");
|
||||
b.add_input<decl::Float>("Gain"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(1.0f)
|
||||
.available(noise_type == SHD_NOISE_HYBRID_MULTIFRACTAL ||
|
||||
noise_type == SHD_NOISE_RIDGED_MULTIFRACTAL)
|
||||
.make_available([](bNode &node) { node_storage(node).type = SHD_NOISE_RIDGED_MULTIFRACTAL; })
|
||||
.description("An extra multiplier to tune the magnitude of octaves");
|
||||
b.add_input<decl::Float>("Distortion"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(0.0f)
|
||||
.description("Amount of distortion");
|
||||
b.add_output<decl::Float>("Factor"_ustr, "Fac"_ustr).no_muted_links();
|
||||
b.add_output<decl::Color>("Color"_ustr).no_muted_links();
|
||||
}
|
||||
|
||||
static void node_shader_buts_tex_noise(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "noise_dimensions", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
layout.prop(ptr, "noise_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
if (ELEM(RNA_enum_get(ptr, "noise_type"), SHD_NOISE_FBM)) {
|
||||
layout.prop(ptr, "normalize", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_noise(bNodeTree *node_tree, bNode *node)
|
||||
{
|
||||
NodeTexNoise *tex = MEM_new<NodeTexNoise>(__func__);
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
tex->dimensions = node_tree->type == NTREE_COMPOSIT ? 2 : 3;
|
||||
tex->type = SHD_NOISE_FBM;
|
||||
tex->normalize = true;
|
||||
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(const int dimensions, const int type)
|
||||
{
|
||||
BLI_assert(dimensions > 0 && dimensions < 5);
|
||||
BLI_assert(type >= 0 && type < 5);
|
||||
|
||||
switch (type) {
|
||||
case SHD_NOISE_MULTIFRACTAL:
|
||||
return std::array{"node_noise_tex_multi_fractal_1d",
|
||||
"node_noise_tex_multi_fractal_2d",
|
||||
"node_noise_tex_multi_fractal_3d",
|
||||
"node_noise_tex_multi_fractal_4d"}[dimensions - 1];
|
||||
case SHD_NOISE_FBM:
|
||||
return std::array{"node_noise_tex_fbm_1d",
|
||||
"node_noise_tex_fbm_2d",
|
||||
"node_noise_tex_fbm_3d",
|
||||
"node_noise_tex_fbm_4d"}[dimensions - 1];
|
||||
case SHD_NOISE_HYBRID_MULTIFRACTAL:
|
||||
return std::array{"node_noise_tex_hybrid_multi_fractal_1d",
|
||||
"node_noise_tex_hybrid_multi_fractal_2d",
|
||||
"node_noise_tex_hybrid_multi_fractal_3d",
|
||||
"node_noise_tex_hybrid_multi_fractal_4d"}[dimensions - 1];
|
||||
case SHD_NOISE_RIDGED_MULTIFRACTAL:
|
||||
return std::array{"node_noise_tex_ridged_multi_fractal_1d",
|
||||
"node_noise_tex_ridged_multi_fractal_2d",
|
||||
"node_noise_tex_ridged_multi_fractal_3d",
|
||||
"node_noise_tex_ridged_multi_fractal_4d"}[dimensions - 1];
|
||||
case SHD_NOISE_HETERO_TERRAIN:
|
||||
return std::array{"node_noise_tex_hetero_terrain_1d",
|
||||
"node_noise_tex_hetero_terrain_2d",
|
||||
"node_noise_tex_hetero_terrain_3d",
|
||||
"node_noise_tex_hetero_terrain_4d"}[dimensions - 1];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_noise(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
|
||||
const NodeTexNoise &storage = node_storage(*node);
|
||||
float normalize = storage.normalize;
|
||||
float compute_color = out[1].hasoutput;
|
||||
|
||||
const char *name = gpu_shader_get_name(storage.dimensions, storage.type);
|
||||
return GPU_stack_link(
|
||||
mat, node, name, in, out, GPU_constant(&normalize), GPU_constant(&compute_color));
|
||||
}
|
||||
|
||||
class NoiseFunction : public mf::MultiFunction {
|
||||
private:
|
||||
int dimensions_;
|
||||
int type_;
|
||||
bool normalize_;
|
||||
|
||||
public:
|
||||
NoiseFunction(int dimensions, int type, bool normalize)
|
||||
: dimensions_(dimensions), type_(type), normalize_(normalize)
|
||||
{
|
||||
BLI_assert(dimensions >= 1 && dimensions <= 4);
|
||||
BLI_assert(type >= 0 && type <= 4);
|
||||
static std::array<mf::Signature, 20> signatures{
|
||||
create_signature(1, SHD_NOISE_MULTIFRACTAL),
|
||||
create_signature(2, SHD_NOISE_MULTIFRACTAL),
|
||||
create_signature(3, SHD_NOISE_MULTIFRACTAL),
|
||||
create_signature(4, SHD_NOISE_MULTIFRACTAL),
|
||||
|
||||
create_signature(1, SHD_NOISE_FBM),
|
||||
create_signature(2, SHD_NOISE_FBM),
|
||||
create_signature(3, SHD_NOISE_FBM),
|
||||
create_signature(4, SHD_NOISE_FBM),
|
||||
|
||||
create_signature(1, SHD_NOISE_HYBRID_MULTIFRACTAL),
|
||||
create_signature(2, SHD_NOISE_HYBRID_MULTIFRACTAL),
|
||||
create_signature(3, SHD_NOISE_HYBRID_MULTIFRACTAL),
|
||||
create_signature(4, SHD_NOISE_HYBRID_MULTIFRACTAL),
|
||||
|
||||
create_signature(1, SHD_NOISE_RIDGED_MULTIFRACTAL),
|
||||
create_signature(2, SHD_NOISE_RIDGED_MULTIFRACTAL),
|
||||
create_signature(3, SHD_NOISE_RIDGED_MULTIFRACTAL),
|
||||
create_signature(4, SHD_NOISE_RIDGED_MULTIFRACTAL),
|
||||
|
||||
create_signature(1, SHD_NOISE_HETERO_TERRAIN),
|
||||
create_signature(2, SHD_NOISE_HETERO_TERRAIN),
|
||||
create_signature(3, SHD_NOISE_HETERO_TERRAIN),
|
||||
create_signature(4, SHD_NOISE_HETERO_TERRAIN),
|
||||
};
|
||||
this->set_signature(&signatures[dimensions + type * 4 - 1]);
|
||||
}
|
||||
|
||||
static mf::Signature create_signature(int dimensions, int type)
|
||||
{
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Noise", signature};
|
||||
|
||||
if (ELEM(dimensions, 2, 3, 4)) {
|
||||
builder.single_input<float3>("Vector");
|
||||
}
|
||||
if (ELEM(dimensions, 1, 4)) {
|
||||
builder.single_input<float>("W");
|
||||
}
|
||||
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_input<float>("Detail");
|
||||
builder.single_input<float>("Roughness");
|
||||
builder.single_input<float>("Lacunarity");
|
||||
if (ELEM(type,
|
||||
SHD_NOISE_RIDGED_MULTIFRACTAL,
|
||||
SHD_NOISE_HYBRID_MULTIFRACTAL,
|
||||
SHD_NOISE_HETERO_TERRAIN))
|
||||
{
|
||||
builder.single_input<float>("Offset");
|
||||
}
|
||||
if (ELEM(type, SHD_NOISE_RIDGED_MULTIFRACTAL, SHD_NOISE_HYBRID_MULTIFRACTAL)) {
|
||||
builder.single_input<float>("Gain");
|
||||
}
|
||||
builder.single_input<float>("Distortion");
|
||||
|
||||
builder.single_output<float>("Fac", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4);
|
||||
const VArray<float> &scale = params.readonly_single_input<float>(param++, "Scale");
|
||||
const VArray<float> &detail = params.readonly_single_input<float>(param++, "Detail");
|
||||
const VArray<float> &roughness = params.readonly_single_input<float>(param++, "Roughness");
|
||||
const VArray<float> &lacunarity = params.readonly_single_input<float>(param++, "Lacunarity");
|
||||
/* Initialize to any other variable when unused to avoid unnecessary conditionals. */
|
||||
const VArray<float> &offset = ELEM(type_,
|
||||
SHD_NOISE_RIDGED_MULTIFRACTAL,
|
||||
SHD_NOISE_HYBRID_MULTIFRACTAL,
|
||||
SHD_NOISE_HETERO_TERRAIN) ?
|
||||
params.readonly_single_input<float>(param++, "Offset") :
|
||||
scale;
|
||||
/* Initialize to any other variable when unused to avoid unnecessary conditionals. */
|
||||
const VArray<float> &gain = ELEM(type_,
|
||||
SHD_NOISE_RIDGED_MULTIFRACTAL,
|
||||
SHD_NOISE_HYBRID_MULTIFRACTAL) ?
|
||||
params.readonly_single_input<float>(param++, "Gain") :
|
||||
scale;
|
||||
const VArray<float> &distortion = params.readonly_single_input<float>(param++, "Distortion");
|
||||
|
||||
MutableSpan<float> r_factor = params.uninitialized_single_output_if_required<float>(param++,
|
||||
"Fac");
|
||||
MutableSpan<ColorGeometry4f> r_color =
|
||||
params.uninitialized_single_output_if_required<ColorGeometry4f>(param++, "Color");
|
||||
|
||||
const bool compute_factor = !r_factor.is_empty();
|
||||
const bool compute_color = !r_color.is_empty();
|
||||
|
||||
switch (dimensions_) {
|
||||
case 1: {
|
||||
const VArray<float> &w = params.readonly_single_input<float>(0, "W");
|
||||
if (compute_color) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float position = w[i] * scale[i];
|
||||
const float3 c = noise::perlin_float3_fractal_distorted(
|
||||
position,
|
||||
math::clamp(detail[i], 0.0f, 15.0f),
|
||||
math::max(roughness[i], 0.0f),
|
||||
lacunarity[i],
|
||||
offset[i],
|
||||
gain[i],
|
||||
distortion[i],
|
||||
type_,
|
||||
normalize_);
|
||||
r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f);
|
||||
if (compute_factor) {
|
||||
r_factor[i] = c[0];
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (compute_factor) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float position = w[i] * scale[i];
|
||||
r_factor[i] = noise::perlin_fractal_distorted(position,
|
||||
math::clamp(detail[i], 0.0f, 15.0f),
|
||||
math::max(roughness[i], 0.0f),
|
||||
lacunarity[i],
|
||||
offset[i],
|
||||
gain[i],
|
||||
distortion[i],
|
||||
type_,
|
||||
normalize_);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
if (compute_color) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float2 position = float2(vector[i] * scale[i]);
|
||||
const float3 c = noise::perlin_float3_fractal_distorted(
|
||||
position,
|
||||
math::clamp(detail[i], 0.0f, 15.0f),
|
||||
math::max(roughness[i], 0.0f),
|
||||
lacunarity[i],
|
||||
offset[i],
|
||||
gain[i],
|
||||
distortion[i],
|
||||
type_,
|
||||
normalize_);
|
||||
r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f);
|
||||
if (compute_factor) {
|
||||
r_factor[i] = c[0];
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (compute_factor) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float2 position = float2(vector[i] * scale[i]);
|
||||
r_factor[i] = noise::perlin_fractal_distorted(position,
|
||||
math::clamp(detail[i], 0.0f, 15.0f),
|
||||
math::max(roughness[i], 0.0f),
|
||||
lacunarity[i],
|
||||
offset[i],
|
||||
gain[i],
|
||||
distortion[i],
|
||||
type_,
|
||||
normalize_);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
if (compute_color) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 position = vector[i] * scale[i];
|
||||
const float3 c = noise::perlin_float3_fractal_distorted(
|
||||
position,
|
||||
math::clamp(detail[i], 0.0f, 15.0f),
|
||||
math::max(roughness[i], 0.0f),
|
||||
lacunarity[i],
|
||||
offset[i],
|
||||
gain[i],
|
||||
distortion[i],
|
||||
type_,
|
||||
normalize_);
|
||||
r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f);
|
||||
if (compute_factor) {
|
||||
r_factor[i] = c[0];
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (compute_factor) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 position = vector[i] * scale[i];
|
||||
r_factor[i] = noise::perlin_fractal_distorted(position,
|
||||
math::clamp(detail[i], 0.0f, 15.0f),
|
||||
math::max(roughness[i], 0.0f),
|
||||
lacunarity[i],
|
||||
offset[i],
|
||||
gain[i],
|
||||
distortion[i],
|
||||
type_,
|
||||
normalize_);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
const VArray<float> &w = params.readonly_single_input<float>(1, "W");
|
||||
if (compute_color) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 position_vector = vector[i] * scale[i];
|
||||
const float position_w = w[i] * scale[i];
|
||||
const float4 position{
|
||||
position_vector[0], position_vector[1], position_vector[2], position_w};
|
||||
const float3 c = noise::perlin_float3_fractal_distorted(
|
||||
position,
|
||||
math::clamp(detail[i], 0.0f, 15.0f),
|
||||
math::max(roughness[i], 0.0f),
|
||||
lacunarity[i],
|
||||
offset[i],
|
||||
gain[i],
|
||||
distortion[i],
|
||||
type_,
|
||||
normalize_);
|
||||
r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f);
|
||||
if (compute_factor) {
|
||||
r_factor[i] = c[0];
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (compute_factor) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 position_vector = vector[i] * scale[i];
|
||||
const float position_w = w[i] * scale[i];
|
||||
const float4 position{
|
||||
position_vector[0], position_vector[1], position_vector[2], position_w};
|
||||
r_factor[i] = noise::perlin_fractal_distorted(position,
|
||||
math::clamp(detail[i], 0.0f, 15.0f),
|
||||
math::max(roughness[i], 0.0f),
|
||||
lacunarity[i],
|
||||
offset[i],
|
||||
gain[i],
|
||||
distortion[i],
|
||||
type_,
|
||||
normalize_);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExecutionHints get_execution_hints() const override
|
||||
{
|
||||
ExecutionHints hints;
|
||||
hints.allocates_array = false;
|
||||
hints.min_grain_size = 100;
|
||||
return hints;
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(dimensions_);
|
||||
hash.add(type_);
|
||||
hash.add(normalize_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_noise_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const NodeTexNoise &storage = node_storage(builder.node());
|
||||
builder.construct_and_set_matching_fn<NoiseFunction>(
|
||||
storage.dimensions, storage.type, 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);
|
||||
/* Empirically, higher octaves lead to NaNs on e.g. Metal and NVIDIA. */
|
||||
const int octaves = int(math::clamp(detail.value->asA<float>(), 1.0f, 13.0f));
|
||||
|
||||
NodeItem position = create_node("position", NodeItem::Type::Vector3);
|
||||
position = position * scale;
|
||||
|
||||
return create_node(
|
||||
"fractal3d",
|
||||
STREQ(socket_out_->identifier, "Fac") ? NodeItem::Type::Float : NodeItem::Type::Color3,
|
||||
{{"position", position}, {"octaves", val(octaves)}, {"lacunarity", lacunarity}});
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_tex_noise_cc
|
||||
|
||||
void register_node_type_sh_tex_noise()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_noise_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeTexNoise"_ustr, SH_NODE_TEX_NOISE);
|
||||
ntype.ui_name = "Noise Texture";
|
||||
ntype.ui_description = "Generate fractal Perlin noise";
|
||||
ntype.enum_name_legacy = "TEX_NOISE";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_noise_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_tex_noise;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_noise;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_tex_noise;
|
||||
ntype.build_multi_function = file_ns::sh_node_noise_build_multi_function;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,383 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
#include "sky_hosek.h"
|
||||
#include "sky_nishita.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_sky_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Vector>("Vector"_ustr).hide_value();
|
||||
b.add_output<decl::Color>("Color"_ustr).no_muted_links();
|
||||
}
|
||||
|
||||
static void node_shader_buts_tex_sky(ui::Layout &layout, bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "sky_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
|
||||
if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_PREETHAM) {
|
||||
layout.prop(ptr, "sun_direction", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
layout.prop(ptr, "turbidity", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
else if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_HOSEK) {
|
||||
layout.prop(ptr, "sun_direction", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
layout.prop(ptr, "turbidity", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
layout.prop(ptr, "ground_albedo", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
else {
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
if (BKE_scene_uses_blender_eevee(scene)) {
|
||||
layout.label(RPT_("Sun disc not available in EEVEE"), ICON_ERROR);
|
||||
}
|
||||
layout.prop(ptr, "sun_disc", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
if (RNA_boolean_get(ptr, "sun_disc")) {
|
||||
ui::Layout &col = layout.column(true);
|
||||
col.prop(ptr, "sun_size", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
col.prop(ptr, "sun_intensity", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
{
|
||||
ui::Layout &col = layout.column(true);
|
||||
col.prop(ptr, "sun_elevation", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
col.prop(ptr, "sun_rotation", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
layout.prop(ptr, "altitude", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
{
|
||||
ui::Layout &col = layout.column(true);
|
||||
col.prop(ptr, "air_density", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
col.prop(ptr, "aerosol_density", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
col.prop(ptr, "ozone_density", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_sky(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeTexSky *tex = MEM_new<NodeTexSky>("NodeTexSky");
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
tex->sun_direction[0] = 0.0f;
|
||||
tex->sun_direction[1] = 0.0f;
|
||||
tex->sun_direction[2] = 1.0f;
|
||||
tex->turbidity = 2.2f;
|
||||
tex->ground_albedo = 0.3f;
|
||||
tex->sun_disc = true;
|
||||
tex->sun_size = DEG2RADF(0.545f);
|
||||
tex->sun_intensity = 1.0f;
|
||||
tex->sun_elevation = DEG2RADF(15.0f);
|
||||
tex->sun_rotation = 0.0f;
|
||||
tex->altitude = 100.0f;
|
||||
tex->air_density = 1.0f;
|
||||
tex->aerosol_density = 1.0f;
|
||||
tex->ozone_density = 1.0f;
|
||||
tex->sky_model = SHD_SKY_MULTIPLE_SCATTERING;
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
struct SkyModelPreetham {
|
||||
float config_Y[5], config_x[5], config_y[5]; /* named after xyY color space */
|
||||
float radiance[3];
|
||||
};
|
||||
|
||||
static float sky_perez_function(const float *lam, float theta, float gamma)
|
||||
{
|
||||
float ctheta = cosf(theta);
|
||||
float cgamma = cosf(gamma);
|
||||
|
||||
return (1.0 + lam[0] * expf(lam[1] / ctheta)) *
|
||||
(1.0 + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma);
|
||||
}
|
||||
|
||||
static void sky_precompute_old(SkyModelPreetham *sunsky, const float sun_angles[], float turbidity)
|
||||
{
|
||||
float theta = sun_angles[0];
|
||||
float theta2 = theta * theta;
|
||||
float theta3 = theta2 * theta;
|
||||
float T = turbidity;
|
||||
float T2 = T * T;
|
||||
float chi = (4.0f / 9.0f - T / 120.0f) * (M_PI - 2.0f * theta);
|
||||
|
||||
sunsky->radiance[0] = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f;
|
||||
sunsky->radiance[0] *= 0.06f;
|
||||
|
||||
sunsky->radiance[1] = (0.00166f * theta3 - 0.00375f * theta2 + 0.00209f * theta) * T2 +
|
||||
(-0.02903f * theta3 + 0.06377f * theta2 - 0.03202f * theta + 0.00394f) *
|
||||
T +
|
||||
(0.11693f * theta3 - 0.21196f * theta2 + 0.06052f * theta + 0.25886f);
|
||||
|
||||
sunsky->radiance[2] = (0.00275f * theta3 - 0.00610f * theta2 + 0.00317f * theta) * T2 +
|
||||
(-0.04214f * theta3 + 0.08970f * theta2 - 0.04153f * theta + 0.00516f) *
|
||||
T +
|
||||
(0.15346f * theta3 - 0.26756f * theta2 + 0.06670f * theta + 0.26688f);
|
||||
|
||||
sunsky->config_Y[0] = (0.1787f * T - 1.4630f);
|
||||
sunsky->config_Y[1] = (-0.3554f * T + 0.4275f);
|
||||
sunsky->config_Y[2] = (-0.0227f * T + 5.3251f);
|
||||
sunsky->config_Y[3] = (0.1206f * T - 2.5771f);
|
||||
sunsky->config_Y[4] = (-0.0670f * T + 0.3703f);
|
||||
|
||||
sunsky->config_x[0] = (-0.0193f * T - 0.2592f);
|
||||
sunsky->config_x[1] = (-0.0665f * T + 0.0008f);
|
||||
sunsky->config_x[2] = (-0.0004f * T + 0.2125f);
|
||||
sunsky->config_x[3] = (-0.0641f * T - 0.8989f);
|
||||
sunsky->config_x[4] = (-0.0033f * T + 0.0452f);
|
||||
|
||||
sunsky->config_y[0] = (-0.0167f * T - 0.2608f);
|
||||
sunsky->config_y[1] = (-0.0950f * T + 0.0092f);
|
||||
sunsky->config_y[2] = (-0.0079f * T + 0.2102f);
|
||||
sunsky->config_y[3] = (-0.0441f * T - 1.6537f);
|
||||
sunsky->config_y[4] = (-0.0109f * T + 0.0529f);
|
||||
|
||||
sunsky->radiance[0] /= sky_perez_function(sunsky->config_Y, 0, theta);
|
||||
sunsky->radiance[1] /= sky_perez_function(sunsky->config_x, 0, theta);
|
||||
sunsky->radiance[2] /= sky_perez_function(sunsky->config_y, 0, theta);
|
||||
}
|
||||
|
||||
static void sky_simplify_multiscatter_elevation_rotation(float &sun_elevation, float &sun_rotation)
|
||||
{
|
||||
/* Patch Sun position so users are able to animate the daylight cycle while keeping the shading
|
||||
* code simple. */
|
||||
float new_sun_elevation = sun_elevation;
|
||||
float new_sun_rotation = sun_rotation;
|
||||
|
||||
/* Wrap `new_sun_elevation` into [-2PI..2PI] range. */
|
||||
new_sun_elevation = fmodf(new_sun_elevation, 2.0f * M_PI);
|
||||
/* Wrap `new_sun_elevation` into [-PI..PI] range. */
|
||||
if (fabsf(new_sun_elevation) >= M_PI) {
|
||||
new_sun_elevation -= copysignf(2.0f, new_sun_elevation) * M_PI;
|
||||
}
|
||||
/* Wrap `new_sun_elevation` into [-PI/2..PI/2] range while keeping the same absolute position.
|
||||
*/
|
||||
if (new_sun_elevation >= M_PI / 2.0f || new_sun_elevation <= -M_PI / 2.0f) {
|
||||
new_sun_elevation = copysignf(M_PI, new_sun_elevation) - new_sun_elevation;
|
||||
new_sun_rotation += M_PI;
|
||||
}
|
||||
|
||||
/* Wrap `new_sun_rotation` into [-2PI..2PI] range. */
|
||||
new_sun_rotation = fmodf(new_sun_rotation, 2.0f * M_PI);
|
||||
/* Wrap `new_sun_rotation` into [0..2PI] range. */
|
||||
if (new_sun_rotation < 0.0f) {
|
||||
new_sun_rotation += 2.0f * M_PI;
|
||||
}
|
||||
new_sun_rotation = 2.0f * M_PI - new_sun_rotation;
|
||||
|
||||
sun_elevation = new_sun_elevation;
|
||||
sun_rotation = new_sun_rotation;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_sky(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
NodeTexSky *tex = static_cast<NodeTexSky *>(node->storage);
|
||||
float sun_angles[2]; /* [0]=theta=zenith angle [1]=phi=azimuth */
|
||||
sun_angles[0] = acosf(tex->sun_direction[2]);
|
||||
sun_angles[1] = atan2f(tex->sun_direction[0], tex->sun_direction[1]);
|
||||
|
||||
if (tex->sky_model == SHD_SKY_PREETHAM) {
|
||||
/* Preetham */
|
||||
SkyModelPreetham sunsky;
|
||||
sky_precompute_old(&sunsky, sun_angles, tex->turbidity);
|
||||
XYZ_to_RGB xyz_to_rgb;
|
||||
get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"node_tex_sky_preetham",
|
||||
in,
|
||||
out,
|
||||
/* Pass config_Y/x/y as 3x(vec4+float) */
|
||||
GPU_uniform(&sunsky.config_Y[0]),
|
||||
GPU_uniform(&sunsky.config_Y[4]),
|
||||
GPU_uniform(&sunsky.config_x[0]),
|
||||
GPU_uniform(&sunsky.config_x[4]),
|
||||
GPU_uniform(&sunsky.config_y[0]),
|
||||
GPU_uniform(&sunsky.config_y[4]),
|
||||
GPU_uniform(sun_angles),
|
||||
GPU_uniform(sunsky.radiance),
|
||||
GPU_uniform(xyz_to_rgb.r),
|
||||
GPU_uniform(xyz_to_rgb.g),
|
||||
GPU_uniform(xyz_to_rgb.b));
|
||||
}
|
||||
if (tex->sky_model == SHD_SKY_HOSEK) {
|
||||
/* Hosek / Wilkie */
|
||||
sun_angles[0] = fmin(M_PI_2, sun_angles[0]); /* clamp to horizon */
|
||||
SKY_ArHosekSkyModelState *sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init(
|
||||
tex->turbidity, tex->ground_albedo, fmax(0.0, M_PI_2 - sun_angles[0]));
|
||||
/* Pass sky_state->configs[3][9] as 3*(vec4+vec4)+vec3 */
|
||||
float config_x07[8], config_y07[8], config_z07[8], config_xyz8[3];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
config_x07[i] = float(sky_state->configs[0][i]);
|
||||
config_y07[i] = float(sky_state->configs[1][i]);
|
||||
config_z07[i] = float(sky_state->configs[2][i]);
|
||||
}
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
config_xyz8[i] = float(sky_state->configs[i][8]);
|
||||
}
|
||||
float radiance[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
radiance[i] = sky_state->radiances[i] * (2 * M_PI / 683);
|
||||
}
|
||||
SKY_arhosekskymodelstate_free(sky_state);
|
||||
XYZ_to_RGB xyz_to_rgb;
|
||||
get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"node_tex_sky_hosekwilkie",
|
||||
in,
|
||||
out,
|
||||
GPU_uniform(&config_x07[0]),
|
||||
GPU_uniform(&config_x07[4]),
|
||||
GPU_uniform(&config_y07[0]),
|
||||
GPU_uniform(&config_y07[4]),
|
||||
GPU_uniform(&config_z07[0]),
|
||||
GPU_uniform(&config_z07[4]),
|
||||
GPU_uniform(config_xyz8),
|
||||
GPU_uniform(sun_angles),
|
||||
GPU_uniform(radiance),
|
||||
GPU_uniform(xyz_to_rgb.r),
|
||||
GPU_uniform(xyz_to_rgb.g),
|
||||
GPU_uniform(xyz_to_rgb.b));
|
||||
}
|
||||
|
||||
/* Nishita */
|
||||
Array<float> pixels(4 * GPU_SKY_WIDTH * GPU_SKY_HEIGHT);
|
||||
|
||||
float sun_rotation = tex->sun_rotation;
|
||||
if (tex->sky_model == SHD_SKY_SINGLE_SCATTERING) {
|
||||
SKY_single_scattering_precompute_texture(pixels.data(),
|
||||
4,
|
||||
GPU_SKY_WIDTH,
|
||||
GPU_SKY_HEIGHT,
|
||||
tex->sun_elevation,
|
||||
tex->altitude,
|
||||
tex->air_density,
|
||||
tex->aerosol_density,
|
||||
tex->ozone_density);
|
||||
|
||||
/* The multi-scatter case takes care of rotation wrapping in the
|
||||
* sky_simplify_multiscatter_elevation_rotation(). */
|
||||
sun_rotation = fmodf(sun_rotation, 2.0f * M_PI);
|
||||
if (sun_rotation < 0.0f) {
|
||||
sun_rotation += 2.0f * M_PI;
|
||||
}
|
||||
sun_rotation = 2.0f * M_PI - sun_rotation;
|
||||
}
|
||||
else {
|
||||
float sun_elevation = tex->sun_elevation;
|
||||
sky_simplify_multiscatter_elevation_rotation(sun_elevation, sun_rotation);
|
||||
SKY_multiple_scattering_precompute_texture(pixels.data(),
|
||||
4,
|
||||
GPU_SKY_WIDTH,
|
||||
GPU_SKY_HEIGHT,
|
||||
sun_elevation,
|
||||
tex->altitude,
|
||||
tex->air_density,
|
||||
tex->aerosol_density,
|
||||
tex->ozone_density);
|
||||
}
|
||||
|
||||
XYZ_to_RGB xyz_to_rgb;
|
||||
get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
|
||||
|
||||
/* To fix pole issue we clamp the v coordinate. */
|
||||
GPUSamplerState sampler = {GPU_SAMPLER_FILTERING_LINEAR,
|
||||
GPU_SAMPLER_EXTEND_MODE_REPEAT,
|
||||
GPU_SAMPLER_EXTEND_MODE_EXTEND};
|
||||
float layer;
|
||||
float sky_type = (tex->sky_model == SHD_SKY_SINGLE_SCATTERING) ? 0.0f : 1.0f;
|
||||
GPUNodeLink *sky_texture = GPU_image_sky(
|
||||
mat, GPU_SKY_WIDTH, GPU_SKY_HEIGHT, pixels.data(), &layer, sampler);
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"node_tex_sky_nishita",
|
||||
in,
|
||||
out,
|
||||
GPU_constant(&sky_type),
|
||||
GPU_uniform(&sun_rotation),
|
||||
GPU_uniform(xyz_to_rgb.r),
|
||||
GPU_uniform(xyz_to_rgb.g),
|
||||
GPU_uniform(xyz_to_rgb.b),
|
||||
sky_texture,
|
||||
GPU_constant(&layer));
|
||||
}
|
||||
|
||||
static void node_shader_update_sky(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
bNodeSocket *sockVector = bke::node_find_socket(*node, SOCK_IN, "Vector"_ustr);
|
||||
|
||||
NodeTexSky *tex = static_cast<NodeTexSky *>(node->storage);
|
||||
bke::node_set_socket_availability(
|
||||
*ntree,
|
||||
*sockVector,
|
||||
!(ELEM(tex->sky_model, SHD_SKY_SINGLE_SCATTERING, SHD_SKY_MULTIPLE_SCATTERING) &&
|
||||
tex->sun_disc == 1));
|
||||
}
|
||||
|
||||
static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
const NodeDeclaration &declaration = *params.node_type().static_declaration;
|
||||
if (params.in_out() == SOCK_OUT) {
|
||||
search_link_ops_for_declarations(params, declaration.outputs);
|
||||
return;
|
||||
}
|
||||
if (params.node_tree().typeinfo->validate_link(params.other_socket().type, SOCK_FLOAT)) {
|
||||
params.add_item(IFACE_("Vector"), [](LinkSearchOpParams ¶ms) {
|
||||
bNode &node = params.add_node("ShaderNodeTexSky"_ustr);
|
||||
NodeTexSky *tex = static_cast<NodeTexSky *>(node.storage);
|
||||
tex->sun_disc = false;
|
||||
params.update_and_connect_available_socket(node, "Vector"_ustr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_tex_sky_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_tex_sky()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_sky_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeTexSky"_ustr, SH_NODE_TEX_SKY);
|
||||
ntype.ui_name = "Sky Texture";
|
||||
ntype.ui_description = "Generate a procedural sky texture";
|
||||
ntype.enum_name_legacy = "TEX_SKY";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_tex_sky;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_sky;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeTexSky", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_tex_sky;
|
||||
ntype.updatefunc = file_ns::node_shader_update_sky;
|
||||
ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,856 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "BLI_noise.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_voronoi_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeTexVoronoi)
|
||||
|
||||
static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
const int dimensions = b.node_or_null() ? node_storage(*b.node_or_null()).dimensions : 3;
|
||||
const bool is_compositor = b.tree_or_null() && b.tree_or_null()->type == NTREE_COMPOSIT;
|
||||
const NodeDefaultInputType default_input_type =
|
||||
is_compositor ? NODE_DEFAULT_INPUT_UNIFORM_IMAGE_COORDINATES :
|
||||
NODE_DEFAULT_INPUT_POSITION_FIELD;
|
||||
b.add_input<decl::Vector>("Vector"_ustr)
|
||||
.default_input_type(default_input_type)
|
||||
.available(dimensions != 1);
|
||||
|
||||
b.add_input<decl::Float>("W"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.available(dimensions == 1 || dimensions == 4)
|
||||
.make_available([](bNode &node) {
|
||||
/* Default to 1 instead of 4, because it is much faster. */
|
||||
node_storage(node).dimensions = 1;
|
||||
});
|
||||
|
||||
b.add_input<decl::Float>("Scale"_ustr).min(-1000.0f).max(1000.0f).default_value(5.0f);
|
||||
|
||||
const eNodeVoronoi_Type feature = eNodeVoronoi_Type(
|
||||
b.node_or_null() ? node_storage(*b.node_or_null()).feature : SHD_VORONOI_F1);
|
||||
b.add_input<decl::Float>("Detail"_ustr)
|
||||
.min(0.0f)
|
||||
.max(15.0f)
|
||||
.default_value(0.0f)
|
||||
.available(feature != SHD_VORONOI_N_SPHERE_RADIUS)
|
||||
.make_available([](bNode &node) { node_storage(node).feature = SHD_VORONOI_F1; })
|
||||
.description("The number of Voronoi layers to sum");
|
||||
b.add_input<decl::Float>("Roughness"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(0.5f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.available(feature != SHD_VORONOI_N_SPHERE_RADIUS)
|
||||
.make_available([](bNode &node) { node_storage(node).feature = SHD_VORONOI_F1; })
|
||||
.description("The influence of a Voronoi layer relative to that of the previous layer");
|
||||
b.add_input<decl::Float>("Lacunarity"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(2.0f)
|
||||
.available(feature != SHD_VORONOI_N_SPHERE_RADIUS)
|
||||
.make_available([](bNode &node) { node_storage(node).feature = SHD_VORONOI_F1; })
|
||||
.description("The scale of a Voronoi layer relative to that of the previous layer");
|
||||
|
||||
b.add_input<decl::Float>("Smoothness"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.available(feature == SHD_VORONOI_SMOOTH_F1)
|
||||
.make_available([](bNode &node) { node_storage(node).feature = SHD_VORONOI_SMOOTH_F1; });
|
||||
|
||||
const eNodeVoronoi_Dist distance_metric = eNodeVoronoi_Dist(
|
||||
b.node_or_null() ? node_storage(*b.node_or_null()).distance : SHD_VORONOI_EUCLIDEAN);
|
||||
b.add_input<decl::Float>("Exponent"_ustr)
|
||||
.min(0.0f)
|
||||
.max(32.0f)
|
||||
.default_value(0.5f)
|
||||
.available(distance_metric == SHD_VORONOI_MINKOWSKI && dimensions != 1 &&
|
||||
!ELEM(feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS))
|
||||
.make_available([](bNode &node) { node_storage(node).distance = SHD_VORONOI_MINKOWSKI; });
|
||||
|
||||
b.add_input<decl::Float>("Randomness"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
|
||||
b.add_output<decl::Float>("Distance"_ustr)
|
||||
.no_muted_links()
|
||||
.available(feature != SHD_VORONOI_N_SPHERE_RADIUS);
|
||||
b.add_output<decl::Color>("Color"_ustr)
|
||||
.no_muted_links()
|
||||
.available(feature != SHD_VORONOI_DISTANCE_TO_EDGE &&
|
||||
feature != SHD_VORONOI_N_SPHERE_RADIUS);
|
||||
b.add_output<decl::Vector>("Position"_ustr)
|
||||
.no_muted_links()
|
||||
.available(feature != SHD_VORONOI_DISTANCE_TO_EDGE &&
|
||||
feature != SHD_VORONOI_N_SPHERE_RADIUS && dimensions != 1);
|
||||
b.add_output<decl::Float>("W"_ustr)
|
||||
.no_muted_links()
|
||||
.available(feature != SHD_VORONOI_DISTANCE_TO_EDGE &&
|
||||
feature != SHD_VORONOI_N_SPHERE_RADIUS && ELEM(dimensions, 1, 4))
|
||||
.make_available([](bNode &node) {
|
||||
/* Default to 1 instead of 4, because it is much faster. */
|
||||
node_storage(node).dimensions = 1;
|
||||
});
|
||||
b.add_output<decl::Float>("Radius"_ustr)
|
||||
.no_muted_links()
|
||||
.available(feature == SHD_VORONOI_N_SPHERE_RADIUS)
|
||||
.make_available(
|
||||
[](bNode &node) { node_storage(node).feature = SHD_VORONOI_N_SPHERE_RADIUS; });
|
||||
}
|
||||
|
||||
static void node_shader_buts_tex_voronoi(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "voronoi_dimensions", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
layout.prop(ptr, "feature", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
int feature = RNA_enum_get(ptr, "feature");
|
||||
if (!ELEM(feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS) &&
|
||||
RNA_enum_get(ptr, "voronoi_dimensions") != 1)
|
||||
{
|
||||
layout.prop(ptr, "distance", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
if (!ELEM(feature, SHD_VORONOI_N_SPHERE_RADIUS)) {
|
||||
layout.prop(ptr, "normalize", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_voronoi(bNodeTree *node_tree, bNode *node)
|
||||
{
|
||||
NodeTexVoronoi *tex = MEM_new<NodeTexVoronoi>(__func__);
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
tex->dimensions = node_tree->type == NTREE_COMPOSIT ? 2 : 3;
|
||||
tex->distance = SHD_VORONOI_EUCLIDEAN;
|
||||
tex->feature = SHD_VORONOI_F1;
|
||||
tex->normalize = false;
|
||||
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(const int feature, const int dimensions)
|
||||
{
|
||||
BLI_assert(feature >= 0 && feature < 5);
|
||||
BLI_assert(dimensions > 0 && dimensions < 5);
|
||||
|
||||
switch (feature) {
|
||||
case SHD_VORONOI_F1:
|
||||
return std::array{
|
||||
"node_tex_voronoi_f1_1d",
|
||||
"node_tex_voronoi_f1_2d",
|
||||
"node_tex_voronoi_f1_3d",
|
||||
"node_tex_voronoi_f1_4d",
|
||||
}[dimensions - 1];
|
||||
case SHD_VORONOI_F2:
|
||||
return std::array{
|
||||
"node_tex_voronoi_f2_1d",
|
||||
"node_tex_voronoi_f2_2d",
|
||||
"node_tex_voronoi_f2_3d",
|
||||
"node_tex_voronoi_f2_4d",
|
||||
}[dimensions - 1];
|
||||
case SHD_VORONOI_SMOOTH_F1:
|
||||
return std::array{
|
||||
"node_tex_voronoi_smooth_f1_1d",
|
||||
"node_tex_voronoi_smooth_f1_2d",
|
||||
"node_tex_voronoi_smooth_f1_3d",
|
||||
"node_tex_voronoi_smooth_f1_4d",
|
||||
}[dimensions - 1];
|
||||
case SHD_VORONOI_DISTANCE_TO_EDGE:
|
||||
return std::array{
|
||||
"node_tex_voronoi_distance_to_edge_1d",
|
||||
"node_tex_voronoi_distance_to_edge_2d",
|
||||
"node_tex_voronoi_distance_to_edge_3d",
|
||||
"node_tex_voronoi_distance_to_edge_4d",
|
||||
}[dimensions - 1];
|
||||
case SHD_VORONOI_N_SPHERE_RADIUS:
|
||||
return std::array{
|
||||
"node_tex_voronoi_n_sphere_radius_1d",
|
||||
"node_tex_voronoi_n_sphere_radius_2d",
|
||||
"node_tex_voronoi_n_sphere_radius_3d",
|
||||
"node_tex_voronoi_n_sphere_radius_4d",
|
||||
}[dimensions - 1];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_voronoi(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
|
||||
NodeTexVoronoi *tex = static_cast<NodeTexVoronoi *>(node->storage);
|
||||
float metric = tex->distance;
|
||||
float normalize = tex->normalize;
|
||||
|
||||
const char *name = gpu_shader_get_name(tex->feature, tex->dimensions);
|
||||
|
||||
return GPU_stack_link(mat, node, name, in, out, GPU_constant(&metric), GPU_constant(&normalize));
|
||||
}
|
||||
|
||||
static mf::MultiFunction::ExecutionHints voronoi_execution_hints{50, false};
|
||||
|
||||
class VoronoiMetricFunction : public mf::MultiFunction {
|
||||
private:
|
||||
int dimensions_;
|
||||
int feature_;
|
||||
int metric_;
|
||||
bool normalize_;
|
||||
|
||||
public:
|
||||
VoronoiMetricFunction(int dimensions, int feature, int metric, bool normalize)
|
||||
: dimensions_(dimensions), feature_(feature), metric_(metric), normalize_(normalize)
|
||||
{
|
||||
BLI_assert(dimensions >= 1 && dimensions <= 4);
|
||||
BLI_assert(feature >= 0 && feature <= 4);
|
||||
if (ELEM(metric_, SHD_VORONOI_MINKOWSKI)) {
|
||||
static std::array<mf::Signature, 12> signatures{
|
||||
create_signature(1, SHD_VORONOI_F1, SHD_VORONOI_MINKOWSKI),
|
||||
create_signature(2, SHD_VORONOI_F1, SHD_VORONOI_MINKOWSKI),
|
||||
create_signature(3, SHD_VORONOI_F1, SHD_VORONOI_MINKOWSKI),
|
||||
create_signature(4, SHD_VORONOI_F1, SHD_VORONOI_MINKOWSKI),
|
||||
|
||||
create_signature(1, SHD_VORONOI_F2, SHD_VORONOI_MINKOWSKI),
|
||||
create_signature(2, SHD_VORONOI_F2, SHD_VORONOI_MINKOWSKI),
|
||||
create_signature(3, SHD_VORONOI_F2, SHD_VORONOI_MINKOWSKI),
|
||||
create_signature(4, SHD_VORONOI_F2, SHD_VORONOI_MINKOWSKI),
|
||||
|
||||
create_signature(1, SHD_VORONOI_SMOOTH_F1, SHD_VORONOI_MINKOWSKI),
|
||||
create_signature(2, SHD_VORONOI_SMOOTH_F1, SHD_VORONOI_MINKOWSKI),
|
||||
create_signature(3, SHD_VORONOI_SMOOTH_F1, SHD_VORONOI_MINKOWSKI),
|
||||
create_signature(4, SHD_VORONOI_SMOOTH_F1, SHD_VORONOI_MINKOWSKI),
|
||||
};
|
||||
this->set_signature(&signatures[dimensions + feature * 4 - 1]);
|
||||
}
|
||||
else {
|
||||
static std::array<mf::Signature, 12> signatures{
|
||||
create_signature(1, SHD_VORONOI_F1, SHD_VORONOI_EUCLIDEAN),
|
||||
create_signature(2, SHD_VORONOI_F1, SHD_VORONOI_EUCLIDEAN),
|
||||
create_signature(3, SHD_VORONOI_F1, SHD_VORONOI_EUCLIDEAN),
|
||||
create_signature(4, SHD_VORONOI_F1, SHD_VORONOI_EUCLIDEAN),
|
||||
|
||||
create_signature(1, SHD_VORONOI_F2, SHD_VORONOI_EUCLIDEAN),
|
||||
create_signature(2, SHD_VORONOI_F2, SHD_VORONOI_EUCLIDEAN),
|
||||
create_signature(3, SHD_VORONOI_F2, SHD_VORONOI_EUCLIDEAN),
|
||||
create_signature(4, SHD_VORONOI_F2, SHD_VORONOI_EUCLIDEAN),
|
||||
|
||||
create_signature(1, SHD_VORONOI_SMOOTH_F1, SHD_VORONOI_EUCLIDEAN),
|
||||
create_signature(2, SHD_VORONOI_SMOOTH_F1, SHD_VORONOI_EUCLIDEAN),
|
||||
create_signature(3, SHD_VORONOI_SMOOTH_F1, SHD_VORONOI_EUCLIDEAN),
|
||||
create_signature(4, SHD_VORONOI_SMOOTH_F1, SHD_VORONOI_EUCLIDEAN),
|
||||
};
|
||||
this->set_signature(&signatures[dimensions + feature * 4 - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
static mf::Signature create_signature(int dimensions, int feature, int metric)
|
||||
{
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"voronoi_metric", signature};
|
||||
|
||||
if (ELEM(dimensions, 2, 3, 4)) {
|
||||
builder.single_input<float3>("Vector");
|
||||
}
|
||||
if (ELEM(dimensions, 1, 4)) {
|
||||
builder.single_input<float>("W");
|
||||
}
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_input<float>("Detail");
|
||||
builder.single_input<float>("Roughness");
|
||||
builder.single_input<float>("Lacunarity");
|
||||
if (feature == SHD_VORONOI_SMOOTH_F1) {
|
||||
builder.single_input<float>("Smoothness");
|
||||
}
|
||||
if ((dimensions != 1) && (metric == SHD_VORONOI_MINKOWSKI)) {
|
||||
builder.single_input<float>("Exponent");
|
||||
}
|
||||
builder.single_input<float>("Randomness");
|
||||
|
||||
builder.single_output<float>("Distance", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
if (dimensions != 1) {
|
||||
builder.single_output<float3>("Position", mf::ParamFlag::SupportsUnusedOutput);
|
||||
}
|
||||
if (ELEM(dimensions, 1, 4)) {
|
||||
builder.single_output<float>("W", mf::ParamFlag::SupportsUnusedOutput);
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params mf_params, mf::Context /*context*/) const override
|
||||
{
|
||||
auto get_vector = [&](int param_index) -> VArray<float3> {
|
||||
return mf_params.readonly_single_input<float3>(param_index, "Vector");
|
||||
};
|
||||
auto get_w = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "W");
|
||||
};
|
||||
auto get_scale = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Scale");
|
||||
};
|
||||
auto get_detail = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Detail");
|
||||
};
|
||||
auto get_roughness = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Roughness");
|
||||
};
|
||||
auto get_lacunarity = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Lacunarity");
|
||||
};
|
||||
auto get_smoothness = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Smoothness");
|
||||
};
|
||||
auto get_exponent = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Exponent");
|
||||
};
|
||||
auto get_randomness = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Randomness");
|
||||
};
|
||||
auto get_r_distance = [&](int param_index) -> MutableSpan<float> {
|
||||
return mf_params.uninitialized_single_output_if_required<float>(param_index, "Distance");
|
||||
};
|
||||
auto get_r_color = [&](int param_index) -> MutableSpan<ColorGeometry4f> {
|
||||
return mf_params.uninitialized_single_output_if_required<ColorGeometry4f>(param_index,
|
||||
"Color");
|
||||
};
|
||||
auto get_r_position = [&](int param_index) -> MutableSpan<float3> {
|
||||
return mf_params.uninitialized_single_output_if_required<float3>(param_index, "Position");
|
||||
};
|
||||
auto get_r_w = [&](int param_index) -> MutableSpan<float> {
|
||||
return mf_params.uninitialized_single_output_if_required<float>(param_index, "W");
|
||||
};
|
||||
|
||||
int param = 0;
|
||||
|
||||
const VArray<float3> &vector = !ELEM(dimensions_, 1) ? get_vector(param++) : VArray<float3>{};
|
||||
const VArray<float> &w = ELEM(dimensions_, 1, 4) ? get_w(param++) : VArray<float>{};
|
||||
const VArray<float> &scale = get_scale(param++);
|
||||
const VArray<float> &detail = get_detail(param++);
|
||||
const VArray<float> &roughness = get_roughness(param++);
|
||||
const VArray<float> &lacunarity = get_lacunarity(param++);
|
||||
const VArray<float> &smoothness = ELEM(feature_, SHD_VORONOI_SMOOTH_F1) ?
|
||||
get_smoothness(param++) :
|
||||
VArray<float>{};
|
||||
const VArray<float> &exponent = ELEM(metric_, SHD_VORONOI_MINKOWSKI) && !ELEM(dimensions_, 1) ?
|
||||
get_exponent(param++) :
|
||||
VArray<float>{};
|
||||
const VArray<float> &randomness = get_randomness(param++);
|
||||
MutableSpan<float> r_distance = get_r_distance(param++);
|
||||
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
|
||||
MutableSpan<float3> r_position = !ELEM(dimensions_, 1) ? get_r_position(param++) :
|
||||
MutableSpan<float3>{};
|
||||
MutableSpan<float> r_w = ELEM(dimensions_, 1, 4) ? get_r_w(param++) : MutableSpan<float>{};
|
||||
const bool calc_distance = !r_distance.is_empty();
|
||||
const bool calc_color = !r_color.is_empty();
|
||||
const bool calc_position = !r_position.is_empty();
|
||||
const bool calc_w = !r_w.is_empty();
|
||||
|
||||
noise::VoronoiParams params;
|
||||
params.feature = feature_;
|
||||
params.metric = metric_;
|
||||
params.normalize = normalize_;
|
||||
|
||||
noise::VoronoiOutput output;
|
||||
switch (dimensions_) {
|
||||
case 1: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.detail = detail[i];
|
||||
params.roughness = roughness[i];
|
||||
params.lacunarity = lacunarity[i];
|
||||
params.smoothness = ELEM(feature_, SHD_VORONOI_SMOOTH_F1) ?
|
||||
std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f) :
|
||||
0.0f;
|
||||
params.exponent = 0.0f;
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
params.max_distance = (0.5f + 0.5f * params.randomness) *
|
||||
((params.feature == SHD_VORONOI_F2) ? 2.0f : 1.0f);
|
||||
|
||||
output = noise::fractal_voronoi_x_fx<float>(params, w[i] * params.scale, calc_color);
|
||||
if (calc_distance) {
|
||||
r_distance[i] = output.distance;
|
||||
}
|
||||
if (calc_color) {
|
||||
r_color[i] = ColorGeometry4f(output.color.x, output.color.y, output.color.z, 1.0f);
|
||||
}
|
||||
if (calc_w) {
|
||||
r_w[i] = output.position.w;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.detail = detail[i];
|
||||
params.roughness = roughness[i];
|
||||
params.lacunarity = lacunarity[i];
|
||||
params.smoothness = ELEM(feature_, SHD_VORONOI_SMOOTH_F1) ?
|
||||
std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f) :
|
||||
0.0f;
|
||||
params.exponent = ELEM(metric_, SHD_VORONOI_MINKOWSKI) && !ELEM(dimensions_, 1) ?
|
||||
exponent[i] :
|
||||
0.0f;
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
params.max_distance = noise::voronoi_distance(float2{0.0f, 0.0f},
|
||||
float2(0.5f + 0.5f * params.randomness,
|
||||
0.5f + 0.5f * params.randomness),
|
||||
params) *
|
||||
((params.feature == SHD_VORONOI_F2) ? 2.0f : 1.0f);
|
||||
|
||||
output = noise::fractal_voronoi_x_fx<float2>(
|
||||
params, float2{vector[i].x, vector[i].y} * params.scale, calc_color);
|
||||
if (calc_distance) {
|
||||
r_distance[i] = output.distance;
|
||||
}
|
||||
if (calc_color) {
|
||||
r_color[i] = ColorGeometry4f(output.color.x, output.color.y, output.color.z, 1.0f);
|
||||
}
|
||||
if (calc_position) {
|
||||
r_position[i] = float3{output.position.x, output.position.y, 0.0f};
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.detail = detail[i];
|
||||
params.roughness = roughness[i];
|
||||
params.lacunarity = lacunarity[i];
|
||||
params.smoothness = ELEM(feature_, SHD_VORONOI_SMOOTH_F1) ?
|
||||
std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f) :
|
||||
0.0f;
|
||||
params.exponent = ELEM(metric_, SHD_VORONOI_MINKOWSKI) && !ELEM(dimensions_, 1) ?
|
||||
exponent[i] :
|
||||
0.0f;
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
params.max_distance = noise::voronoi_distance(float3{0.0f, 0.0f, 0.0f},
|
||||
float3(0.5f + 0.5f * params.randomness,
|
||||
0.5f + 0.5f * params.randomness,
|
||||
0.5f + 0.5f * params.randomness),
|
||||
params) *
|
||||
((params.feature == SHD_VORONOI_F2) ? 2.0f : 1.0f);
|
||||
|
||||
output = noise::fractal_voronoi_x_fx<float3>(
|
||||
params, vector[i] * params.scale, calc_color);
|
||||
if (calc_distance) {
|
||||
r_distance[i] = output.distance;
|
||||
}
|
||||
if (calc_color) {
|
||||
r_color[i] = ColorGeometry4f(output.color.x, output.color.y, output.color.z, 1.0f);
|
||||
}
|
||||
if (calc_position) {
|
||||
r_position[i] = float3{output.position.x, output.position.y, output.position.z};
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.detail = detail[i];
|
||||
params.roughness = roughness[i];
|
||||
params.lacunarity = lacunarity[i];
|
||||
params.smoothness = ELEM(feature_, SHD_VORONOI_SMOOTH_F1) ?
|
||||
std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f) :
|
||||
0.0f;
|
||||
params.exponent = ELEM(metric_, SHD_VORONOI_MINKOWSKI) && !ELEM(dimensions_, 1) ?
|
||||
exponent[i] :
|
||||
0.0f;
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
params.max_distance = noise::voronoi_distance(float4{0.0f, 0.0f, 0.0f, 0.0f},
|
||||
float4(0.5f + 0.5f * params.randomness,
|
||||
0.5f + 0.5f * params.randomness,
|
||||
0.5f + 0.5f * params.randomness,
|
||||
0.5f + 0.5f * params.randomness),
|
||||
params) *
|
||||
((params.feature == SHD_VORONOI_F2) ? 2.0f : 1.0f);
|
||||
|
||||
output = noise::fractal_voronoi_x_fx<float4>(
|
||||
params,
|
||||
float4{vector[i].x, vector[i].y, vector[i].z, w[i]} * params.scale,
|
||||
calc_color);
|
||||
if (calc_distance) {
|
||||
r_distance[i] = output.distance;
|
||||
}
|
||||
if (calc_color) {
|
||||
r_color[i] = ColorGeometry4f(output.color.x, output.color.y, output.color.z, 1.0f);
|
||||
}
|
||||
if (calc_position) {
|
||||
r_position[i] = float3{output.position.x, output.position.y, output.position.z};
|
||||
}
|
||||
if (calc_w) {
|
||||
r_w[i] = output.position.w;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(dimensions_);
|
||||
hash.add(feature_);
|
||||
hash.add(metric_);
|
||||
hash.add(normalize_);
|
||||
}
|
||||
|
||||
ExecutionHints get_execution_hints() const override
|
||||
{
|
||||
return voronoi_execution_hints;
|
||||
}
|
||||
};
|
||||
|
||||
class VoronoiDistToEdgeFunction : public mf::MultiFunction {
|
||||
private:
|
||||
int dimensions_;
|
||||
bool normalize_;
|
||||
|
||||
public:
|
||||
VoronoiDistToEdgeFunction(int dimensions, bool normalize)
|
||||
: dimensions_(dimensions), normalize_(normalize)
|
||||
{
|
||||
BLI_assert(dimensions >= 1 && dimensions <= 4);
|
||||
static std::array<mf::Signature, 4> signatures{
|
||||
create_signature(1),
|
||||
create_signature(2),
|
||||
create_signature(3),
|
||||
create_signature(4),
|
||||
};
|
||||
this->set_signature(&signatures[dimensions - 1]);
|
||||
}
|
||||
|
||||
static mf::Signature create_signature(int dimensions)
|
||||
{
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"voronoi_dist_to_edge", signature};
|
||||
|
||||
if (ELEM(dimensions, 2, 3, 4)) {
|
||||
builder.single_input<float3>("Vector");
|
||||
}
|
||||
if (ELEM(dimensions, 1, 4)) {
|
||||
builder.single_input<float>("W");
|
||||
}
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_input<float>("Detail");
|
||||
builder.single_input<float>("Roughness");
|
||||
builder.single_input<float>("Lacunarity");
|
||||
builder.single_input<float>("Randomness");
|
||||
|
||||
builder.single_output<float>("Distance");
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params mf_params, mf::Context /*context*/) const override
|
||||
{
|
||||
auto get_vector = [&](int param_index) -> VArray<float3> {
|
||||
return mf_params.readonly_single_input<float3>(param_index, "Vector");
|
||||
};
|
||||
auto get_w = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "W");
|
||||
};
|
||||
auto get_scale = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Scale");
|
||||
};
|
||||
auto get_detail = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Detail");
|
||||
};
|
||||
auto get_roughness = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Roughness");
|
||||
};
|
||||
auto get_lacunarity = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Lacunarity");
|
||||
};
|
||||
auto get_randomness = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Randomness");
|
||||
};
|
||||
auto get_r_distance = [&](int param_index) -> MutableSpan<float> {
|
||||
return mf_params.uninitialized_single_output<float>(param_index, "Distance");
|
||||
};
|
||||
|
||||
int param = 0;
|
||||
|
||||
const VArray<float3> &vector = !ELEM(dimensions_, 1) ? get_vector(param++) : VArray<float3>{};
|
||||
const VArray<float> &w = ELEM(dimensions_, 1, 4) ? get_w(param++) : VArray<float>{};
|
||||
const VArray<float> &scale = get_scale(param++);
|
||||
const VArray<float> &detail = get_detail(param++);
|
||||
const VArray<float> &roughness = get_roughness(param++);
|
||||
const VArray<float> &lacunarity = get_lacunarity(param++);
|
||||
const VArray<float> &randomness = get_randomness(param++);
|
||||
MutableSpan<float> r_distance = get_r_distance(param++);
|
||||
|
||||
noise::VoronoiParams params;
|
||||
params.normalize = normalize_;
|
||||
|
||||
switch (dimensions_) {
|
||||
case 1: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.detail = detail[i];
|
||||
params.roughness = roughness[i];
|
||||
params.lacunarity = lacunarity[i];
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
params.max_distance = 0.5f + 0.5f * params.randomness;
|
||||
|
||||
r_distance[i] = noise::fractal_voronoi_distance_to_edge<float>(params,
|
||||
w[i] * params.scale);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.detail = detail[i];
|
||||
params.roughness = roughness[i];
|
||||
params.lacunarity = lacunarity[i];
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
params.max_distance = 0.5f + 0.5f * params.randomness;
|
||||
|
||||
r_distance[i] = noise::fractal_voronoi_distance_to_edge<float2>(
|
||||
params, float2{vector[i].x, vector[i].y} * params.scale);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.detail = detail[i];
|
||||
params.roughness = roughness[i];
|
||||
params.lacunarity = lacunarity[i];
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
params.max_distance = 0.5f + 0.5f * params.randomness;
|
||||
|
||||
r_distance[i] = noise::fractal_voronoi_distance_to_edge<float3>(
|
||||
params, vector[i] * params.scale);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.detail = detail[i];
|
||||
params.roughness = roughness[i];
|
||||
params.lacunarity = lacunarity[i];
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
params.max_distance = 0.5f + 0.5f * params.randomness;
|
||||
|
||||
r_distance[i] = noise::fractal_voronoi_distance_to_edge<float4>(
|
||||
params, float4{vector[i].x, vector[i].y, vector[i].z, w[i]} * params.scale);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(dimensions_);
|
||||
hash.add(normalize_);
|
||||
}
|
||||
|
||||
ExecutionHints get_execution_hints() const override
|
||||
{
|
||||
return voronoi_execution_hints;
|
||||
}
|
||||
};
|
||||
|
||||
class VoronoiNSphereFunction : public mf::MultiFunction {
|
||||
private:
|
||||
int dimensions_;
|
||||
|
||||
public:
|
||||
VoronoiNSphereFunction(int dimensions) : dimensions_(dimensions)
|
||||
{
|
||||
BLI_assert(dimensions >= 1 && dimensions <= 4);
|
||||
static std::array<mf::Signature, 4> signatures{
|
||||
create_signature(1),
|
||||
create_signature(2),
|
||||
create_signature(3),
|
||||
create_signature(4),
|
||||
};
|
||||
this->set_signature(&signatures[dimensions - 1]);
|
||||
}
|
||||
|
||||
static mf::Signature create_signature(int dimensions)
|
||||
{
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"voronoi_n_sphere", signature};
|
||||
|
||||
if (ELEM(dimensions, 2, 3, 4)) {
|
||||
builder.single_input<float3>("Vector");
|
||||
}
|
||||
if (ELEM(dimensions, 1, 4)) {
|
||||
builder.single_input<float>("W");
|
||||
}
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_input<float>("Randomness");
|
||||
|
||||
builder.single_output<float>("Radius");
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params mf_params, mf::Context /*context*/) const override
|
||||
{
|
||||
auto get_vector = [&](int param_index) -> VArray<float3> {
|
||||
return mf_params.readonly_single_input<float3>(param_index, "Vector");
|
||||
};
|
||||
auto get_w = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "W");
|
||||
};
|
||||
auto get_scale = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Scale");
|
||||
};
|
||||
auto get_randomness = [&](int param_index) -> VArray<float> {
|
||||
return mf_params.readonly_single_input<float>(param_index, "Randomness");
|
||||
};
|
||||
auto get_r_radius = [&](int param_index) -> MutableSpan<float> {
|
||||
return mf_params.uninitialized_single_output<float>(param_index, "Radius");
|
||||
};
|
||||
|
||||
int param = 0;
|
||||
|
||||
const VArray<float3> &vector = !ELEM(dimensions_, 1) ? get_vector(param++) : VArray<float3>{};
|
||||
const VArray<float> &w = ELEM(dimensions_, 1, 4) ? get_w(param++) : VArray<float>{};
|
||||
const VArray<float> &scale = get_scale(param++);
|
||||
const VArray<float> &randomness = get_randomness(param++);
|
||||
MutableSpan<float> r_radius = get_r_radius(param++);
|
||||
|
||||
noise::VoronoiParams params;
|
||||
|
||||
switch (dimensions_) {
|
||||
case 1: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
|
||||
r_radius[i] = noise::voronoi_n_sphere_radius(params, w[i] * params.scale);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
|
||||
r_radius[i] = noise::voronoi_n_sphere_radius(
|
||||
params, float2{vector[i].x, vector[i].y} * params.scale);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
|
||||
r_radius[i] = noise::voronoi_n_sphere_radius(params, vector[i] * params.scale);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
params.scale = scale[i];
|
||||
params.randomness = std::min(std::max(randomness[i], 0.0f), 1.0f);
|
||||
|
||||
r_radius[i] = noise::voronoi_n_sphere_radius(
|
||||
params, float4{vector[i].x, vector[i].y, vector[i].z, w[i]} * params.scale);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(dimensions_);
|
||||
}
|
||||
|
||||
ExecutionHints get_execution_hints() const override
|
||||
{
|
||||
return voronoi_execution_hints;
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_voronoi_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const NodeTexVoronoi &storage = node_storage(builder.node());
|
||||
switch (storage.feature) {
|
||||
case SHD_VORONOI_DISTANCE_TO_EDGE: {
|
||||
builder.construct_and_set_matching_fn<VoronoiDistToEdgeFunction>(storage.dimensions,
|
||||
storage.normalize);
|
||||
break;
|
||||
}
|
||||
case SHD_VORONOI_N_SPHERE_RADIUS: {
|
||||
builder.construct_and_set_matching_fn<VoronoiNSphereFunction>(storage.dimensions);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
builder.construct_and_set_matching_fn<VoronoiMetricFunction>(
|
||||
storage.dimensions, storage.feature, storage.distance, storage.normalize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_tex_voronoi_cc
|
||||
|
||||
void register_node_type_sh_tex_voronoi()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_voronoi_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeTexVoronoi"_ustr, SH_NODE_TEX_VORONOI);
|
||||
ntype.ui_name = "Voronoi Texture";
|
||||
ntype.ui_description =
|
||||
"Generate Worley noise based on the distance to random points. Typically used to generate "
|
||||
"textures such as stones, water, or biological cells";
|
||||
ntype.enum_name_legacy = "TEX_VORONOI";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_voronoi_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_tex_voronoi;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_voronoi;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_tex_voronoi;
|
||||
ntype.build_multi_function = file_ns::sh_node_voronoi_build_multi_function;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,381 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_noise.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_wave_cc {
|
||||
|
||||
static void sh_node_tex_wave_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
const bool is_compositor = b.tree_or_null() && b.tree_or_null()->type == NTREE_COMPOSIT;
|
||||
const NodeDefaultInputType default_input_type =
|
||||
is_compositor ? NODE_DEFAULT_INPUT_UNIFORM_IMAGE_COORDINATES :
|
||||
NODE_DEFAULT_INPUT_POSITION_FIELD;
|
||||
b.add_input<decl::Vector>("Vector"_ustr).default_input_type(default_input_type);
|
||||
|
||||
b.add_input<decl::Float>("Scale"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(5.0f)
|
||||
.description("Overall texture scale");
|
||||
b.add_input<decl::Float>("Distortion"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(0.0f)
|
||||
.description("Amount of distortion of the wave");
|
||||
b.add_input<decl::Float>("Detail"_ustr)
|
||||
.min(0.0f)
|
||||
.max(15.0f)
|
||||
.default_value(2.0f)
|
||||
.description("Amount of distortion noise detail");
|
||||
b.add_input<decl::Float>("Detail Scale"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(1.0f)
|
||||
.description("Scale of distortion noise");
|
||||
b.add_input<decl::Float>("Detail Roughness"_ustr)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.default_value(0.5f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Blend between a smoother noise pattern, and rougher with sharper peaks");
|
||||
b.add_input<decl::Float>("Phase Offset"_ustr)
|
||||
.min(-1000.0f)
|
||||
.max(1000.0f)
|
||||
.default_value(0.0f)
|
||||
.description(
|
||||
"Position of the wave along the Bands Direction.\n"
|
||||
"This can be used as an input for more control over the distortion");
|
||||
b.add_output<decl::Color>("Color"_ustr).no_muted_links();
|
||||
b.add_output<decl::Float>("Factor"_ustr, "Fac"_ustr).no_muted_links();
|
||||
}
|
||||
|
||||
static void node_shader_buts_tex_wave(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "wave_type", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
int type = RNA_enum_get(ptr, "wave_type");
|
||||
if (type == SHD_WAVE_BANDS) {
|
||||
layout.prop(ptr, "bands_direction", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
else { /* SHD_WAVE_RINGS */
|
||||
layout.prop(ptr, "rings_direction", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
layout.prop(ptr, "wave_profile", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_wave(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeTexWave *tex = MEM_new<NodeTexWave>(__func__);
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
tex->wave_type = SHD_WAVE_BANDS;
|
||||
tex->bands_direction = SHD_WAVE_BANDS_DIRECTION_X;
|
||||
tex->rings_direction = SHD_WAVE_RINGS_DIRECTION_X;
|
||||
tex->wave_profile = SHD_WAVE_PROFILE_SIN;
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_wave(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
|
||||
NodeTexWave *tex = static_cast<NodeTexWave *>(node->storage);
|
||||
float wave_type = tex->wave_type;
|
||||
float bands_direction = tex->bands_direction;
|
||||
float rings_direction = tex->rings_direction;
|
||||
float wave_profile = tex->wave_profile;
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"node_tex_wave",
|
||||
in,
|
||||
out,
|
||||
GPU_constant(&wave_type),
|
||||
GPU_constant(&bands_direction),
|
||||
GPU_constant(&rings_direction),
|
||||
GPU_constant(&wave_profile));
|
||||
}
|
||||
|
||||
class WaveFunction : public mf::MultiFunction {
|
||||
private:
|
||||
int wave_type_;
|
||||
int bands_direction_;
|
||||
int rings_direction_;
|
||||
int wave_profile_;
|
||||
|
||||
public:
|
||||
WaveFunction(int wave_type, int bands_direction, int rings_direction, int wave_profile)
|
||||
: wave_type_(wave_type),
|
||||
bands_direction_(bands_direction),
|
||||
rings_direction_(rings_direction),
|
||||
wave_profile_(wave_profile)
|
||||
{
|
||||
static const mf::Signature signature = []() {
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"MagicFunction", signature};
|
||||
builder.single_input<float3>("Vector");
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_input<float>("Distortion");
|
||||
builder.single_input<float>("Detail");
|
||||
builder.single_input<float>("Detail Scale");
|
||||
builder.single_input<float>("Detail Roughness");
|
||||
builder.single_input<float>("Phase Offset");
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Fac");
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
const VArray<float> &scale = params.readonly_single_input<float>(1, "Scale");
|
||||
const VArray<float> &distortion = params.readonly_single_input<float>(2, "Distortion");
|
||||
const VArray<float> &detail = params.readonly_single_input<float>(3, "Detail");
|
||||
const VArray<float> &dscale = params.readonly_single_input<float>(4, "Detail Scale");
|
||||
const VArray<float> &droughness = params.readonly_single_input<float>(5, "Detail Roughness");
|
||||
const VArray<float> &phase = params.readonly_single_input<float>(6, "Phase Offset");
|
||||
|
||||
MutableSpan<ColorGeometry4f> r_color =
|
||||
params.uninitialized_single_output_if_required<ColorGeometry4f>(7, "Color");
|
||||
MutableSpan<float> r_fac = params.uninitialized_single_output<float>(8, "Fac");
|
||||
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
float3 p = vector[i] * scale[i];
|
||||
/* Prevent precision issues on unit coordinates. */
|
||||
p = (p + 0.000001f) * 0.999999f;
|
||||
|
||||
float n = 0.0f;
|
||||
float val = 0.0f;
|
||||
|
||||
switch (wave_type_) {
|
||||
case SHD_WAVE_BANDS:
|
||||
switch (bands_direction_) {
|
||||
case SHD_WAVE_BANDS_DIRECTION_X:
|
||||
n = p.x * 20.0f;
|
||||
break;
|
||||
case SHD_WAVE_BANDS_DIRECTION_Y:
|
||||
n = p.y * 20.0f;
|
||||
break;
|
||||
case SHD_WAVE_BANDS_DIRECTION_Z:
|
||||
n = p.z * 20.0f;
|
||||
break;
|
||||
case SHD_WAVE_BANDS_DIRECTION_DIAGONAL:
|
||||
n = (p.x + p.y + p.z) * 10.0f;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SHD_WAVE_RINGS:
|
||||
float3 rp = p;
|
||||
switch (rings_direction_) {
|
||||
case SHD_WAVE_RINGS_DIRECTION_X:
|
||||
rp *= float3(0.0f, 1.0f, 1.0f);
|
||||
break;
|
||||
case SHD_WAVE_RINGS_DIRECTION_Y:
|
||||
rp *= float3(1.0f, 0.0f, 1.0f);
|
||||
break;
|
||||
case SHD_WAVE_RINGS_DIRECTION_Z:
|
||||
rp *= float3(1.0f, 1.0f, 0.0f);
|
||||
break;
|
||||
case SHD_WAVE_RINGS_DIRECTION_SPHERICAL:
|
||||
/* Ignore. */
|
||||
break;
|
||||
}
|
||||
n = len_v3(rp) * 20.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
n += phase[i];
|
||||
|
||||
if (distortion[i] != 0.0f) {
|
||||
n += distortion[i] *
|
||||
(noise::perlin_fbm<float3>(p * dscale[i], detail[i], droughness[i], 2.0f, true) *
|
||||
2.0f -
|
||||
1.0f);
|
||||
}
|
||||
|
||||
switch (wave_profile_) {
|
||||
case SHD_WAVE_PROFILE_SIN:
|
||||
val = 0.5f + 0.5f * sinf(n - M_PI_2);
|
||||
break;
|
||||
case SHD_WAVE_PROFILE_SAW:
|
||||
n /= M_PI * 2.0f;
|
||||
val = n - floorf(n);
|
||||
break;
|
||||
case SHD_WAVE_PROFILE_TRI:
|
||||
n /= M_PI * 2.0f;
|
||||
val = fabsf(n - floorf(n + 0.5f)) * 2.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
r_fac[i] = val;
|
||||
});
|
||||
if (!r_color.is_empty()) {
|
||||
mask.foreach_index_optimized<int64_t>([&](const int64_t i) {
|
||||
r_color[i] = ColorGeometry4f(r_fac[i], r_fac[i], r_fac[i], 1.0f);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(wave_type_);
|
||||
hash.add(bands_direction_);
|
||||
hash.add(rings_direction_);
|
||||
hash.add(wave_profile_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_wave_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &node = builder.node();
|
||||
NodeTexWave *tex = static_cast<NodeTexWave *>(node.storage);
|
||||
builder.construct_and_set_matching_fn<WaveFunction>(
|
||||
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);
|
||||
}
|
||||
|
||||
/* Adjustment 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 nodes::node_shader_tex_wave_cc
|
||||
|
||||
void register_node_type_sh_tex_wave()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_wave_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeTexWave"_ustr, SH_NODE_TEX_WAVE);
|
||||
ntype.ui_name = "Wave Texture";
|
||||
ntype.ui_description = "Generate procedural bands or rings with noise";
|
||||
ntype.enum_name_legacy = "TEX_WAVE";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_wave_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_tex_wave;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_wave;
|
||||
bke::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;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,290 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "BLI_noise.hh"
|
||||
|
||||
#include "FN_multi_function.hh"
|
||||
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_tex_white_noise_cc {
|
||||
|
||||
static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
|
||||
const int dimensions = b.node_or_null() ? b.node_or_null()->custom1 : 3;
|
||||
const bool is_compositor = b.tree_or_null() && b.tree_or_null()->type == NTREE_COMPOSIT;
|
||||
const NodeDefaultInputType default_input_type =
|
||||
is_compositor ? NODE_DEFAULT_INPUT_UNIFORM_IMAGE_COORDINATES :
|
||||
NODE_DEFAULT_INPUT_POSITION_FIELD;
|
||||
b.add_input<decl::Vector>("Vector"_ustr)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.default_input_type(default_input_type)
|
||||
.available(dimensions != 1);
|
||||
|
||||
b.add_input<decl::Float>("W"_ustr)
|
||||
.min(-10000.0f)
|
||||
.max(10000.0f)
|
||||
.available(dimensions == 1 || dimensions == 4)
|
||||
.make_available([](bNode &node) {
|
||||
/* Default to 1 instead of 4, because it is faster. */
|
||||
node.custom1 = 1;
|
||||
})
|
||||
.description("Value used as seed in 1D and 4D dimensions");
|
||||
|
||||
b.add_output<decl::Float>("Value"_ustr);
|
||||
b.add_output<decl::Color>("Color"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_white_noise(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "noise_dimensions", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_tex_white_noise(bNodeTree *node_tree, bNode *node)
|
||||
{
|
||||
node->custom1 = node_tree->type == NTREE_COMPOSIT ? 2 : 3;
|
||||
}
|
||||
|
||||
static const char *gpu_shader_get_name(const int dimensions)
|
||||
{
|
||||
BLI_assert(dimensions >= 1 && dimensions <= 4);
|
||||
return std::array{"node_white_noise_1d",
|
||||
"node_white_noise_2d",
|
||||
"node_white_noise_3d",
|
||||
"node_white_noise_4d"}[dimensions - 1];
|
||||
}
|
||||
|
||||
static int gpu_shader_tex_white_noise(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const char *name = gpu_shader_get_name(node->custom1);
|
||||
return GPU_stack_link(mat, node, name, in, out);
|
||||
}
|
||||
|
||||
class WhiteNoiseFunction : public mf::MultiFunction {
|
||||
private:
|
||||
int dimensions_;
|
||||
|
||||
public:
|
||||
WhiteNoiseFunction(int dimensions) : dimensions_(dimensions)
|
||||
{
|
||||
BLI_assert(dimensions >= 1 && dimensions <= 4);
|
||||
static std::array<mf::Signature, 4> signatures{
|
||||
create_signature(1),
|
||||
create_signature(2),
|
||||
create_signature(3),
|
||||
create_signature(4),
|
||||
};
|
||||
this->set_signature(&signatures[dimensions - 1]);
|
||||
}
|
||||
|
||||
static mf::Signature create_signature(int dimensions)
|
||||
{
|
||||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"WhiteNoise", signature};
|
||||
|
||||
if (ELEM(dimensions, 2, 3, 4)) {
|
||||
builder.single_input<float3>("Vector");
|
||||
}
|
||||
if (ELEM(dimensions, 1, 4)) {
|
||||
builder.single_input<float>("W");
|
||||
}
|
||||
|
||||
builder.single_output<float>("Value", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
|
||||
{
|
||||
int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4);
|
||||
|
||||
MutableSpan<float> r_value = params.uninitialized_single_output_if_required<float>(param++,
|
||||
"Value");
|
||||
MutableSpan<ColorGeometry4f> r_color =
|
||||
params.uninitialized_single_output_if_required<ColorGeometry4f>(param++, "Color");
|
||||
|
||||
const bool compute_value = !r_value.is_empty();
|
||||
const bool compute_color = !r_color.is_empty();
|
||||
|
||||
switch (dimensions_) {
|
||||
case 1: {
|
||||
const VArray<float> &w = params.readonly_single_input<float>(0, "W");
|
||||
if (compute_color) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 c = noise::hash_float_to_float3(w[i]);
|
||||
r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f);
|
||||
});
|
||||
}
|
||||
if (compute_value) {
|
||||
mask.foreach_index(
|
||||
[&](const int64_t i) { r_value[i] = noise::hash_float_to_float(w[i]); });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
if (compute_color) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 c = noise::hash_float_to_float3(float2(vector[i].x, vector[i].y));
|
||||
r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f);
|
||||
});
|
||||
}
|
||||
if (compute_value) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
r_value[i] = noise::hash_float_to_float(float2(vector[i].x, vector[i].y));
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
if (compute_color) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 c = noise::hash_float_to_float3(vector[i]);
|
||||
r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f);
|
||||
});
|
||||
}
|
||||
if (compute_value) {
|
||||
mask.foreach_index(
|
||||
[&](const int64_t i) { r_value[i] = noise::hash_float_to_float(vector[i]); });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
|
||||
const VArray<float> &w = params.readonly_single_input<float>(1, "W");
|
||||
if (compute_color) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
const float3 c = noise::hash_float_to_float3(
|
||||
float4(vector[i].x, vector[i].y, vector[i].z, w[i]));
|
||||
r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f);
|
||||
});
|
||||
}
|
||||
if (compute_value) {
|
||||
mask.foreach_index([&](const int64_t i) {
|
||||
r_value[i] = noise::hash_float_to_float(
|
||||
float4(vector[i].x, vector[i].y, vector[i].z, w[i]));
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const override
|
||||
{
|
||||
static constexpr int8_t id = 0;
|
||||
hash.add(&id);
|
||||
hash.add(dimensions_);
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_noise_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNode &node = builder.node();
|
||||
builder.construct_and_set_matching_fn<WhiteNoiseFunction>(int(node.custom1));
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
/* MaterialX cellnoise node rounds float value of texture coordinate.
|
||||
* Therefore it changes at different integer coordinates.
|
||||
* The simple trick would be to multiply the texture coordinate by a large number. */
|
||||
const float LARGE_NUMBER = 10000.0f;
|
||||
|
||||
NodeItem noise = empty();
|
||||
NodeItem vector = empty();
|
||||
NodeItem w = empty();
|
||||
|
||||
int dimension = node_->custom1;
|
||||
switch (dimension) {
|
||||
case 1:
|
||||
w = get_input_value("W", NodeItem::Type::Vector2);
|
||||
noise = create_node(
|
||||
"cellnoise2d", NodeItem::Type::Float, {{"texcoord", w * val(LARGE_NUMBER)}});
|
||||
break;
|
||||
case 2:
|
||||
vector = get_input_link("Vector", NodeItem::Type::Vector2);
|
||||
if (!vector) {
|
||||
vector = texcoord_node();
|
||||
}
|
||||
noise = create_node(
|
||||
"cellnoise2d", NodeItem::Type::Float, {{"texcoord", vector * val(LARGE_NUMBER)}});
|
||||
break;
|
||||
case 3:
|
||||
vector = get_input_link("Vector", NodeItem::Type::Vector3);
|
||||
if (!vector) {
|
||||
vector = texcoord_node(NodeItem::Type::Vector3);
|
||||
}
|
||||
noise = create_node(
|
||||
"cellnoise3d", NodeItem::Type::Float, {{"position", vector * val(LARGE_NUMBER)}});
|
||||
break;
|
||||
case 4:
|
||||
vector = get_input_link("Vector", NodeItem::Type::Vector3);
|
||||
if (!vector) {
|
||||
vector = texcoord_node(NodeItem::Type::Vector3);
|
||||
}
|
||||
w = get_input_value("W", NodeItem::Type::Float);
|
||||
noise = create_node(
|
||||
"cellnoise3d", NodeItem::Type::Float, {{"position", (vector + w) * val(LARGE_NUMBER)}});
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
|
||||
if (STREQ(socket_out_->identifier, "Value")) {
|
||||
return noise;
|
||||
}
|
||||
|
||||
/* NOTE: cellnoise node doesn't have colored output, so we create hsvtorgb node and put
|
||||
* noise in first (Hue) channel to generate color. */
|
||||
NodeItem combine = create_node("combine3",
|
||||
NodeItem::Type::Color3,
|
||||
{{"in1", noise}, {"in2", val(1.0f)}, {"in3", val(0.5f)}});
|
||||
return create_node("hsvtorgb", NodeItem::Type::Color3, {{"in", combine}});
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_tex_white_noise_cc
|
||||
|
||||
void register_node_type_sh_tex_white_noise()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_tex_white_noise_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeTexWhiteNoise"_ustr, SH_NODE_TEX_WHITE_NOISE);
|
||||
ntype.ui_name = "White Noise Texture";
|
||||
ntype.ui_description = "Calculate a random value or color based on an input seed";
|
||||
ntype.enum_name_legacy = "TEX_WHITE_NOISE";
|
||||
ntype.nclass = NODE_CLASS_TEXTURE;
|
||||
ntype.declare = file_ns::sh_node_tex_white_noise_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_white_noise;
|
||||
ntype.initfunc = file_ns::node_shader_init_tex_white_noise;
|
||||
ntype.gpu_fn = file_ns::gpu_shader_tex_white_noise;
|
||||
ntype.build_multi_function = file_ns::sh_node_noise_build_multi_function;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,45 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_uv_along_stroke_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Vector>("UV"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_uvalongstroke(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "use_tips", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
}
|
||||
|
||||
} // namespace nodes::node_shader_uv_along_stroke_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_uvalongstroke()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_uv_along_stroke_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeUVAlongStroke"_ustr, SH_NODE_UVALONGSTROKE);
|
||||
ntype.ui_name = "UV Along Stroke";
|
||||
ntype.ui_description = "UV coordinates that map a texture along the stroke length";
|
||||
ntype.enum_name_legacy = "UVALONGSTROKE";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.add_ui_poll = line_style_shader_nodes_poll;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_uvalongstroke;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,115 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
#include "node_util.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_uvmap_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Vector>("UV"_ustr);
|
||||
}
|
||||
|
||||
static void node_shader_buts_uvmap(ui::Layout &layout, bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
layout.prop(ptr, "from_instancer", ui::ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
|
||||
|
||||
if (!RNA_boolean_get(ptr, "from_instancer")) {
|
||||
PointerRNA obptr = CTX_data_pointer_get(C, "active_object");
|
||||
Object *object = static_cast<Object *>(obptr.data);
|
||||
|
||||
if (object && object->type == OB_MESH) {
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
|
||||
if (depsgraph) {
|
||||
Object *object_eval = DEG_get_evaluated(depsgraph, object);
|
||||
PointerRNA dataptr = RNA_id_pointer_create(object_eval->data);
|
||||
layout.prop_search(ptr, "uv_map", &dataptr, "uv_layers", "", ICON_GROUP_UVS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
layout.prop(ptr, "uv_map", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_GROUP_UVS);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_uvmap(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeShaderUVMap *attr = MEM_new<NodeShaderUVMap>("NodeShaderUVMap");
|
||||
node->storage = attr;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_uvmap(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
NodeShaderUVMap *attr = static_cast<NodeShaderUVMap *>(node->storage);
|
||||
|
||||
/* NOTE: using CD_AUTO_FROM_NAME instead of CD_MTFACE as geometry nodes may overwrite data which
|
||||
* will also change the eCustomDataType. This will also make EEVEE and Cycles consistent. See
|
||||
* #93179. */
|
||||
GPUNodeLink *mtface = GPU_attribute(mat, CD_AUTO_FROM_NAME, attr->uv_map);
|
||||
|
||||
GPU_stack_link(mat, node, "node_uvmap", in, out, mtface);
|
||||
|
||||
node_shader_gpu_bump_tex_coord(mat, node, &out[0].link);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
/* NODE: "From Instances" not implemented */
|
||||
NodeShaderUVMap *attr = static_cast<NodeShaderUVMap *>(node_->storage);
|
||||
NodeItem res = texcoord_node(NodeItem::Type::Vector2, attr->uv_map);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace nodes::node_shader_uvmap_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_uvmap()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_uvmap_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeUVMap"_ustr, SH_NODE_UVMAP);
|
||||
ntype.ui_name = "UV Map";
|
||||
ntype.ui_description =
|
||||
"Retrieve a UV map from the geometry, or the default fallback if none is specified";
|
||||
ntype.enum_name_legacy = "UVMAP";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_uvmap;
|
||||
ntype.default_width = bke::NodeWidth::_160;
|
||||
ntype.initfunc = file_ns::node_shader_init_uvmap;
|
||||
bke::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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,84 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "FN_multi_function_builder.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_gizmos.hh"
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace nodes::node_shader_value_cc {
|
||||
|
||||
static void sh_node_value_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_output<decl::Float>("Value"_ustr).custom_draw([](CustomSocketDrawParams ¶ms) {
|
||||
params.layout.alignment_set(ui::LayoutAlign::Expand);
|
||||
ui::Layout &row = params.layout.row(true);
|
||||
row.prop(¶ms.socket_ptr, "default_value", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
if (gizmos::value_node_has_gizmo(params.tree, params.node)) {
|
||||
row.prop(¶ms.socket_ptr, "pin_gizmo", UI_ITEM_NONE, "", ICON_GIZMO);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static int gpu_shader_value(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack * /*in*/,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first);
|
||||
float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value;
|
||||
return GPU_link(mat, "set_value", GPU_uniform(&value), &out->link);
|
||||
}
|
||||
|
||||
static void sh_node_value_build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const bNodeSocket *bsocket = static_cast<bNodeSocket *>(builder.node().outputs.first);
|
||||
const bNodeSocketValueFloat *value = static_cast<const bNodeSocketValueFloat *>(
|
||||
bsocket->default_value);
|
||||
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 nodes::node_shader_value_cc
|
||||
|
||||
void register_node_type_sh_value()
|
||||
{
|
||||
namespace file_ns = nodes::node_shader_value_cc;
|
||||
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
common_node_type_base(&ntype, "ShaderNodeValue"_ustr, SH_NODE_VALUE);
|
||||
ntype.ui_name = "Value";
|
||||
ntype.ui_description = "Input numerical values to other nodes in the tree";
|
||||
ntype.enum_name_legacy = "VALUE";
|
||||
ntype.nclass = NODE_CLASS_INPUT;
|
||||
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;
|
||||
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user