Blender V5.0
node_shader_map_range.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10
11#include "node_shader_util.hh"
12#include "node_util.hh"
13
14#include "BLI_math_base_safe.h"
15#include "BLI_math_vector.h"
16#include "BLI_math_vector.hh"
17
19
20#include "NOD_multi_function.hh"
22
23#include "RNA_access.hh"
24
26#include "UI_resources.hh"
27
29
31
33{
34 b.is_function_node();
35 b.add_input<decl::Float>("Value").min(-10000.0f).max(10000.0f).default_value(1.0f);
36 b.add_input<decl::Float>("From Min").min(-10000.0f).max(10000.0f);
37 b.add_input<decl::Float>("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f);
38 b.add_input<decl::Float>("To Min").min(-10000.0f).max(10000.0f);
39 b.add_input<decl::Float>("To Max").min(-10000.0f).max(10000.0f).default_value(1.0f);
40 b.add_input<decl::Float>("Steps").min(-10000.0f).max(10000.0f).default_value(4.0f);
41 b.add_input<decl::Vector>("Vector").min(0.0f).max(1.0f).hide_value();
42 b.add_input<decl::Vector>("From Min", "From_Min_FLOAT3");
43 b.add_input<decl::Vector>("From Max", "From_Max_FLOAT3").default_value(float3(1.0f));
44 b.add_input<decl::Vector>("To Min", "To_Min_FLOAT3");
45 b.add_input<decl::Vector>("To Max", "To_Max_FLOAT3").default_value(float3(1.0f));
46 b.add_input<decl::Vector>("Steps", "Steps_FLOAT3").default_value(float3(4.0f));
47 b.add_output<decl::Float>("Result");
48 b.add_output<decl::Vector>("Vector");
49}
50
52{
53 layout->prop(ptr, "data_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
54 layout->prop(ptr, "interpolation_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
55 if (!ELEM(RNA_enum_get(ptr, "interpolation_type"),
58 {
59 layout->prop(ptr, "clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
60 }
61}
62
64{
65 const NodeMapRange &storage = node_storage(*node);
66 const eCustomDataType data_type = eCustomDataType(storage.data_type);
67 if (data_type == CD_PROP_FLOAT3) {
69 }
71}
72
74{
75 const NodeMapRange &storage = node_storage(*node);
76 const eCustomDataType data_type = eCustomDataType(storage.data_type);
77 const int type = (data_type == CD_PROP_FLOAT) ? SOCK_FLOAT : SOCK_VECTOR;
78
79 Array<bool> new_input_availability(BLI_listbase_count(&node->inputs));
80 Array<bool> new_output_availability(BLI_listbase_count(&node->outputs));
81
82 int index;
83 LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
84 new_input_availability[index] = socket->type == type;
85 }
86 LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->outputs, index) {
87 new_output_availability[index] = socket->type == type;
88 }
89
91 if (type == SOCK_FLOAT) {
92 new_input_availability[5] = false;
93 }
94 else {
95 new_input_availability[11] = false;
96 }
97 }
98
99 LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
100 bke::node_set_socket_availability(*ntree, *socket, new_input_availability[index]);
101 }
102 LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->outputs, index) {
103 bke::node_set_socket_availability(*ntree, *socket, new_output_availability[index]);
104 }
105}
106
107static void node_shader_init_map_range(bNodeTree * /*ntree*/, bNode *node)
108{
110 data->clamp = 1;
111 data->data_type = CD_PROP_FLOAT;
112 data->interpolation_type = NODE_MAP_RANGE_LINEAR;
113 node->custom1 = true; /* use_clamp */
114 node->custom2 = NODE_MAP_RANGE_LINEAR; /* interpolation */
115 node->storage = data;
116}
117
119 public:
120 std::string socket_name;
123
125 {
126 bNode &node = params.add_node("ShaderNodeMapRange");
127 node_storage(node).data_type = data_type;
128 node_storage(node).interpolation_type = interpolation_type;
129 params.update_and_connect_available_socket(node, socket_name);
130 }
131};
132
133static std::optional<eCustomDataType> node_type_from_other_socket(const bNodeSocket &socket)
134{
135 switch (socket.type) {
136 case SOCK_FLOAT:
137 case SOCK_BOOLEAN:
138 case SOCK_INT:
139 return CD_PROP_FLOAT;
140 case SOCK_VECTOR:
141 case SOCK_RGBA:
142 return CD_PROP_FLOAT3;
143 default:
144 return {};
145 }
146}
147
149{
150 const std::optional<eCustomDataType> type = node_type_from_other_socket(params.other_socket());
151 if (!type) {
152 return;
153 }
154
155 if (params.in_out() == SOCK_IN) {
156 if (*type == CD_PROP_FLOAT3) {
157 params.add_item(IFACE_("Vector"), SocketSearchOp{"Vector", *type}, 0);
158 }
159 else {
160 params.add_item(IFACE_("Value"), SocketSearchOp{"Value", *type}, 0);
161 }
162 params.add_item(IFACE_("From Min"), SocketSearchOp{"From Min", *type}, -1);
163 params.add_item(IFACE_("From Max"), SocketSearchOp{"From Max", *type}, -1);
164 params.add_item(IFACE_("To Min"), SocketSearchOp{"To Min", *type}, -2);
165 params.add_item(IFACE_("To Max"), SocketSearchOp{"To Max", *type}, -2);
166 params.add_item(IFACE_("Steps"), SocketSearchOp{"Steps", *type, NODE_MAP_RANGE_STEPPED}, -3);
167 }
168 else {
169 if (*type == CD_PROP_FLOAT3) {
170 params.add_item(IFACE_("Vector"), SocketSearchOp{"Vector", *type});
171 }
172 else {
173 params.add_item(IFACE_("Result"), SocketSearchOp{"Result", *type});
174 }
175 }
176}
177
178static const char *gpu_shader_get_name(int mode, bool use_vector)
179{
180 if (use_vector) {
181 switch (mode) {
183 return "vector_map_range_linear";
185 return "vector_map_range_stepped";
187 return "vector_map_range_smoothstep";
189 return "vector_map_range_smootherstep";
190 }
191 }
192 else {
193 switch (mode) {
195 return "map_range_linear";
197 return "map_range_stepped";
199 return "map_range_smoothstep";
201 return "map_range_smootherstep";
202 }
203 }
204
205 return nullptr;
206}
207
209 bNode *node,
210 bNodeExecData * /*execdata*/,
213{
214 const NodeMapRange &storage = node_storage(*node);
215 bool use_vector = (storage.data_type == CD_PROP_FLOAT3);
216 const char *name = gpu_shader_get_name(storage.interpolation_type, use_vector);
217 float clamp = storage.clamp ? 1.0f : 0.0f;
218 int ret = 0;
219 if (name != nullptr) {
220 ret = GPU_stack_link(mat, node, name, in, out, GPU_constant(&clamp));
221 }
222 else {
223 ret = GPU_stack_link(mat, node, "map_range_linear", in, out, GPU_constant(&clamp));
224 }
225 if (ret && storage.clamp && !use_vector &&
227 {
228 GPU_link(mat, "clamp_range", out[0].link, in[3].link, in[4].link, &out[0].link);
229 }
230 return ret;
231}
232
233static inline float clamp_range(const float value, const float min, const float max)
234{
235 return (min > max) ? std::clamp(value, max, min) : std::clamp(value, min, max);
236}
237
238static float3 clamp_range(const float3 value, const float3 min, const float3 max)
239{
240 return float3(clamp_range(value.x, min.x, max.x),
241 clamp_range(value.y, min.y, max.y),
242 clamp_range(value.z, min.z, max.z));
243}
244
245template<bool Clamp> static auto build_float_linear()
246{
247 return mf::build::SI5_SO<float, float, float, float, float, float>(
248 Clamp ? "Map Range (clamped)" : "Map Range (unclamped)",
249 [](float value, float from_min, float from_max, float to_min, float to_max) -> float {
250 const float factor = safe_divide(value - from_min, from_max - from_min);
251 float result = to_min + factor * (to_max - to_min);
252 if constexpr (Clamp) {
253 result = clamp_range(result, to_min, to_max);
254 }
255 return result;
256 },
257 mf::build::exec_presets::SomeSpanOrSingle<0>());
258}
259
260template<bool Clamp> static auto build_float_stepped()
261{
262 return mf::build::SI6_SO<float, float, float, float, float, float, float>(
263 Clamp ? "Map Range Stepped (clamped)" : "Map Range Stepped (unclamped)",
264 [](float value, float from_min, float from_max, float to_min, float to_max, float steps)
265 -> float {
266 float factor = safe_divide(value - from_min, from_max - from_min);
267 factor = safe_divide(floorf(factor * (steps + 1.0f)), steps);
268 float result = to_min + factor * (to_max - to_min);
269 if constexpr (Clamp) {
270 result = clamp_range(result, to_min, to_max);
271 }
272 return result;
273 },
274 mf::build::exec_presets::SomeSpanOrSingle<0>());
275}
276
277template<bool Clamp> static auto build_vector_linear()
278{
279 return mf::build::SI5_SO<float3, float3, float3, float3, float3, float3>(
280 Clamp ? "Vector Map Range (clamped)" : "Vector Map Range (unclamped)",
281 [](const float3 &value,
282 const float3 &from_min,
283 const float3 &from_max,
284 const float3 &to_min,
285 const float3 &to_max) -> float3 {
286 float3 factor = math::safe_divide(value - from_min, from_max - from_min);
287 float3 result = factor * (to_max - to_min) + to_min;
288 if constexpr (Clamp) {
289 result = clamp_range(result, to_min, to_max);
290 }
291 return result;
292 },
293 mf::build::exec_presets::SomeSpanOrSingle<0>());
294}
295
296template<bool Clamp> static auto build_vector_stepped()
297{
298 return mf::build::SI6_SO<float3, float3, float3, float3, float3, float3, float3>(
299 Clamp ? "Vector Map Range Stepped (clamped)" : "Vector Map Range Stepped (unclamped)",
300 [](const float3 &value,
301 const float3 &from_min,
302 const float3 &from_max,
303 const float3 &to_min,
304 const float3 &to_max,
305 const float3 &steps) -> float3 {
306 float3 factor = math::safe_divide(value - from_min, from_max - from_min);
307 factor = math::safe_divide(math::floor(factor * (steps + 1.0f)), steps);
308 float3 result = factor * (to_max - to_min) + to_min;
309 if constexpr (Clamp) {
310 result = clamp_range(result, to_min, to_max);
311 }
312 return result;
313 },
314 mf::build::exec_presets::SomeSpanOrSingle<0>());
315}
316
318{
319 const NodeMapRange &storage = node_storage(builder.node());
320 bool clamp = storage.clamp != 0;
321 int interpolation_type = storage.interpolation_type;
322
323 switch (storage.data_type) {
324 case CD_PROP_FLOAT3:
325 switch (interpolation_type) {
327 if (clamp) {
328 static auto fn = build_vector_linear<true>();
329 builder.set_matching_fn(fn);
330 }
331 else {
332 static auto fn = build_vector_linear<false>();
333 builder.set_matching_fn(fn);
334 }
335 break;
336 }
338 if (clamp) {
339 static auto fn = build_vector_stepped<true>();
340 builder.set_matching_fn(fn);
341 }
342 else {
343 static auto fn = build_vector_stepped<false>();
344 builder.set_matching_fn(fn);
345 }
346 break;
347 }
349 static auto fn = mf::build::SI5_SO<float3, float3, float3, float3, float3, float3>(
350 "Vector Map Range Smoothstep",
351 [](const float3 &value,
352 const float3 &from_min,
353 const float3 &from_max,
354 const float3 &to_min,
355 const float3 &to_max) -> float3 {
356 float3 factor = math::safe_divide(value - from_min, from_max - from_min);
357 clamp_v3(factor, 0.0f, 1.0f);
358 factor = (float3(3.0f) - 2.0f * factor) * (factor * factor);
359 return factor * (to_max - to_min) + to_min;
360 },
361 mf::build::exec_presets::SomeSpanOrSingle<0>());
362 builder.set_matching_fn(fn);
363 break;
364 }
366 static auto fn = mf::build::SI5_SO<float3, float3, float3, float3, float3, float3>(
367 "Vector Map Range Smootherstep",
368 [](const float3 &value,
369 const float3 &from_min,
370 const float3 &from_max,
371 const float3 &to_min,
372 const float3 &to_max) -> float3 {
373 float3 factor = math::safe_divide(value - from_min, from_max - from_min);
374 clamp_v3(factor, 0.0f, 1.0f);
375 factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
376 return factor * (to_max - to_min) + to_min;
377 },
378 mf::build::exec_presets::SomeSpanOrSingle<0>());
379 builder.set_matching_fn(fn);
380 break;
381 }
382 default:
383 break;
384 }
385 break;
386 case CD_PROP_FLOAT:
387 switch (interpolation_type) {
389 if (clamp) {
390 static auto fn = build_float_linear<true>();
391 builder.set_matching_fn(fn);
392 }
393 else {
394 static auto fn = build_float_linear<false>();
395 builder.set_matching_fn(fn);
396 }
397 break;
398 }
400 if (clamp) {
401 static auto fn = build_float_stepped<true>();
402 builder.set_matching_fn(fn);
403 }
404 else {
405 static auto fn = build_float_stepped<false>();
406 builder.set_matching_fn(fn);
407 }
408 break;
409 }
411 static auto fn = mf::build::SI5_SO<float, float, float, float, float, float>(
412 "Map Range Smoothstep",
413 [](float value, float from_min, float from_max, float to_min, float to_max)
414 -> float {
415 float factor = safe_divide(value - from_min, from_max - from_min);
416 factor = std::clamp(factor, 0.0f, 1.0f);
417 factor = (3.0f - 2.0f * factor) * (factor * factor);
418 return to_min + factor * (to_max - to_min);
419 },
420 mf::build::exec_presets::SomeSpanOrSingle<0>());
421 builder.set_matching_fn(fn);
422 break;
423 }
425 static auto fn = mf::build::SI5_SO<float, float, float, float, float, float>(
426 "Map Range Smoothstep",
427 [](float value, float from_min, float from_max, float to_min, float to_max)
428 -> float {
429 float factor = safe_divide(value - from_min, from_max - from_min);
430 factor = std::clamp(factor, 0.0f, 1.0f);
431 factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
432 return to_min + factor * (to_max - to_min);
433 },
434 mf::build::exec_presets::SomeSpanOrSingle<0>());
435 builder.set_matching_fn(fn);
436 break;
437 }
438 default:
439 break;
440 }
441 break;
442 }
443}
444
446#ifdef WITH_MATERIALX
447{
448 const NodeMapRange *map_range = static_cast<NodeMapRange *>(node_->storage);
449 NodeItem::Type type;
450 NodeItem value = empty();
451 NodeItem from_min = empty();
452 NodeItem from_max = empty();
453 NodeItem to_min = empty();
454 NodeItem to_max = empty();
455 NodeItem steps = empty();
456 switch (map_range->data_type) {
457 case CD_PROP_FLOAT:
458 type = NodeItem::Type::Float;
459 value = get_input_value("Value", type);
460 from_min = get_input_value(1, type);
461 from_max = get_input_value(2, type);
462 to_min = get_input_value(3, type);
463 to_max = get_input_value(4, type);
464 if (map_range->interpolation_type == NODE_MAP_RANGE_STEPPED) {
465 steps = get_input_value(5, type);
466 }
467 break;
468 case CD_PROP_FLOAT3:
469 type = NodeItem::Type::Vector3;
470 value = get_input_value("Vector", type);
471 from_min = get_input_value(7, type);
472 from_max = get_input_value(8, type);
473 to_min = get_input_value(9, type);
474 to_max = get_input_value(10, type);
475 if (map_range->interpolation_type == NODE_MAP_RANGE_STEPPED) {
476 steps = get_input_value(11, type);
477 }
478 break;
479 default:
481 }
482
483 NodeItem res = empty();
484 switch (map_range->interpolation_type) {
486 res = create_node("remap",
487 type,
488 {{"in", value},
489 {"inlow", from_min},
490 {"inhigh", from_max},
491 {"outlow", to_min},
492 {"outhigh", to_max}});
493 break;
495 NodeItem factor = create_node(
496 "remap", type, {{"in", value}, {"inlow", from_min}, {"inhigh", from_max}});
497 value = (factor * (steps + val(1.0f))).floor() / steps;
498 res = create_node("remap", type, {{"in", value}, {"outlow", to_min}, {"outhigh", to_max}});
499 break;
500 }
503 value = create_node(
504 "smoothstep", type, {{"in", value}, {"low", from_min}, {"high", from_max}});
505 res = create_node("remap", type, {{"in", value}, {"outlow", to_min}, {"outhigh", to_max}});
506 break;
507 default:
509 }
510
511 if (map_range->clamp != 0) {
512 res = res.clamp(to_min, to_max);
513 }
514 return res;
515}
516#endif
518
519} // namespace blender::nodes::node_shader_map_range_cc
520
522{
524
525 static blender::bke::bNodeType ntype;
526
527 common_node_type_base(&ntype, "ShaderNodeMapRange", SH_NODE_MAP_RANGE);
528 ntype.ui_name = "Map Range";
529 ntype.ui_description = "Remap a value from a range to a target range";
530 ntype.enum_name_legacy = "MAP_RANGE";
532 ntype.declare = file_ns::sh_node_map_range_declare;
533 ntype.draw_buttons = file_ns::node_shader_buts_map_range;
534 ntype.ui_class = file_ns::node_shader_map_range_ui_class;
535 ntype.initfunc = file_ns::node_shader_init_map_range;
538 ntype.updatefunc = file_ns::node_shader_update_map_range;
539 ntype.gpu_fn = file_ns::gpu_shader_map_range;
540 ntype.build_multi_function = file_ns::sh_node_map_range_build_multi_function;
541 ntype.gather_link_search_ops = file_ns::node_map_range_gather_link_searches;
542 ntype.materialx_fn = file_ns::node_shader_materialx;
544}
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:453
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_OP_VECTOR
Definition BKE_node.hh:450
#define SH_NODE_MAP_RANGE
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE float safe_divide(float a, float b)
MINLINE void clamp_v3(float vec[3], float min, float max)
#define ELEM(...)
#define IFACE_(msgid)
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ SOCK_IN
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_FLOAT
@ SOCK_RGBA
@ NODE_MAP_RANGE_STEPPED
@ NODE_MAP_RANGE_SMOOTHERSTEP
@ NODE_MAP_RANGE_SMOOTHSTEP
@ NODE_MAP_RANGE_LINEAR
bool GPU_stack_link(GPUMaterial *mat, const bNode *node, const char *name, GPUNodeStack *in, GPUNodeStack *out,...)
GPUNodeLink * GPU_constant(const float *num)
bool GPU_link(GPUMaterial *mat, const char *name,...)
static double Clamp(const double x, const double min, const double max)
Definition IK_Math.h:30
@ UI_ITEM_R_SPLIT_EMPTY_NAME
BMesh const char void * data
void set_matching_fn(const mf::MultiFunction *fn)
#define in
#define out
#define floor
constexpr T clamp(T, U, U) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void node_set_socket_availability(bNodeTree &ntree, bNodeSocket &sock, bool is_available)
Definition node.cc:4739
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5414
T safe_divide(const T &a, const T &b)
T floor(const T &a)
static int gpu_shader_map_range(GPUMaterial *mat, bNode *node, bNodeExecData *, GPUNodeStack *in, GPUNodeStack *out)
static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &builder)
static void node_shader_buts_map_range(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_shader_init_map_range(bNodeTree *, bNode *node)
static const char * gpu_shader_get_name(int mode, bool use_vector)
static int node_shader_map_range_ui_class(const bNode *node)
static float clamp_range(const float value, const float min, const float max)
static std::optional< eCustomDataType > node_type_from_other_socket(const bNodeSocket &socket)
static void node_shader_update_map_range(bNodeTree *ntree, bNode *node)
static void node_map_range_gather_link_searches(GatherLinkSearchOpParams &params)
static void sh_node_map_range_declare(NodeDeclarationBuilder &b)
VecBase< float, 3 > float3
#define NODE_SHADER_MATERIALX_BEGIN
#define NODE_SHADER_MATERIALX_END
void register_node_type_sh_map_range()
void common_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
const char * name
return ret
#define floorf
int RNA_enum_get(PointerRNA *ptr, const char *name)
#define min(a, b)
Definition sort.cc:36
uint8_t interpolation_type
int16_t custom1
ListBase inputs
void * storage
ListBase outputs
int16_t custom2
Defines a node type.
Definition BKE_node.hh:238
NodeMaterialXFunction materialx_fn
Definition BKE_node.hh:344
std::string ui_description
Definition BKE_node.hh:244
int(* ui_class)(const bNode *node)
Definition BKE_node.hh:276
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeGPUExecFunction gpu_fn
Definition BKE_node.hh:342
NodeMultiFunctionBuildFunction build_multi_function
Definition BKE_node.hh:351
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:378
NodeDeclareFunction declare
Definition BKE_node.hh:362
void(* updatefunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:281
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
max
Definition text_draw.cc:251
PointerRNA * ptr
Definition wm_files.cc:4238