The general idea is to keep the 'old', C-style MEM_callocN signature, and slowly replace most of its usages with the new, C++-style type-safer template version. * `MEM_cnew<T>` allocation version is renamed to `MEM_callocN<T>`. * `MEM_cnew_array<T>` allocation version is renamed to `MEM_calloc_arrayN<T>`. * `MEM_cnew<T>` duplicate version is renamed to `MEM_dupallocN<T>`. Similar templates type-safe version of `MEM_mallocN` will be added soon as well. Following discussions in !134452. NOTE: For now static type checking in `MEM_callocN` and related are slightly different for Windows MSVC. This compiler seems to consider structs using the `DNA_DEFINE_CXX_METHODS` macro as non-trivial (likely because their default copy constructors are deleted). So using checks on trivially constructible/destructible instead on this compiler/system. Pull Request: https://projects.blender.org/blender/blender/pulls/134771
545 lines
19 KiB
C++
545 lines
19 KiB
C++
/* 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.hh"
|
|
#include "UI_resources.hh"
|
|
|
|
namespace blender::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").min(-10000.0f).max(10000.0f).default_value(1.0f);
|
|
b.add_input<decl::Float>("From Min").min(-10000.0f).max(10000.0f);
|
|
b.add_input<decl::Float>("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f);
|
|
b.add_input<decl::Float>("To Min").min(-10000.0f).max(10000.0f);
|
|
b.add_input<decl::Float>("To Max").min(-10000.0f).max(10000.0f).default_value(1.0f);
|
|
b.add_input<decl::Float>("Steps").min(-10000.0f).max(10000.0f).default_value(4.0f);
|
|
b.add_input<decl::Vector>("Vector").min(0.0f).max(1.0f).hide_value();
|
|
b.add_input<decl::Vector>("From Min", "From_Min_FLOAT3");
|
|
b.add_input<decl::Vector>("From Max", "From_Max_FLOAT3").default_value(float3(1.0f));
|
|
b.add_input<decl::Vector>("To Min", "To_Min_FLOAT3");
|
|
b.add_input<decl::Vector>("To Max", "To_Max_FLOAT3").default_value(float3(1.0f));
|
|
b.add_input<decl::Vector>("Steps", "Steps_FLOAT3").default_value(float3(4.0f));
|
|
b.add_output<decl::Float>("Result");
|
|
b.add_output<decl::Vector>("Vector");
|
|
}
|
|
|
|
static void node_shader_buts_map_range(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
|
{
|
|
uiItemR(layout, ptr, "data_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
|
uiItemR(layout, 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))
|
|
{
|
|
uiItemR(layout, 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 = static_cast<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 = static_cast<eCustomDataType>(storage.data_type);
|
|
const int type = (data_type == CD_PROP_FLOAT) ? SOCK_FLOAT : SOCK_VECTOR;
|
|
|
|
Array<bool> new_input_availability(BLI_listbase_count(&node->inputs));
|
|
Array<bool> new_output_availability(BLI_listbase_count(&node->outputs));
|
|
|
|
int index;
|
|
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
|
|
new_input_availability[index] = socket->type == type;
|
|
}
|
|
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->outputs, index) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
|
|
bke::node_set_socket_availability(*ntree, *socket, new_input_availability[index]);
|
|
}
|
|
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->outputs, index) {
|
|
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_callocN<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:
|
|
std::string socket_name;
|
|
eCustomDataType data_type;
|
|
int interpolation_type = NODE_MAP_RANGE_LINEAR;
|
|
|
|
void operator()(LinkSearchOpParams ¶ms)
|
|
{
|
|
bNode &node = params.add_node("ShaderNodeMapRange");
|
|
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", *type}, 0);
|
|
}
|
|
else {
|
|
params.add_item(IFACE_("Value"), SocketSearchOp{"Value", *type}, 0);
|
|
}
|
|
params.add_item(IFACE_("From Min"), SocketSearchOp{"From Min", *type}, -1);
|
|
params.add_item(IFACE_("From Max"), SocketSearchOp{"From Max", *type}, -1);
|
|
params.add_item(IFACE_("To Min"), SocketSearchOp{"To Min", *type}, -2);
|
|
params.add_item(IFACE_("To Max"), SocketSearchOp{"To Max", *type}, -2);
|
|
params.add_item(IFACE_("Steps"), SocketSearchOp{"Steps", *type, NODE_MAP_RANGE_STEPPED}, -3);
|
|
}
|
|
else {
|
|
if (*type == CD_PROP_FLOAT3) {
|
|
params.add_item(IFACE_("Vector"), SocketSearchOp{"Vector", *type});
|
|
}
|
|
else {
|
|
params.add_item(IFACE_("Result"), SocketSearchOp{"Result", *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 blender::nodes::node_shader_map_range_cc
|
|
|
|
void register_node_type_sh_map_range()
|
|
{
|
|
namespace file_ns = blender::nodes::node_shader_map_range_cc;
|
|
|
|
static blender::bke::bNodeType ntype;
|
|
|
|
common_node_type_base(&ntype, "ShaderNodeMapRange", 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;
|
|
blender::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;
|
|
blender::bke::node_register_type(ntype);
|
|
}
|