Blender V4.3
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
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
25#include "UI_interface.hh"
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 uiItemR(layout, ptr, "data_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
54 uiItemR(layout, ptr, "interpolation_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
55 if (!ELEM(RNA_enum_get(ptr, "interpolation_type"),
58 {
59 uiItemR(layout, ptr, "clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
60 }
61}
62
64{
65 const NodeMapRange &storage = node_storage(*node);
66 const eCustomDataType data_type = static_cast<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 = static_cast<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{
109 NodeMapRange *data = MEM_cnew<NodeMapRange>(__func__);
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*/,
211 GPUNodeStack *in,
212 GPUNodeStack *out)
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
528 ntype.declare = file_ns::sh_node_map_range_declare;
529 ntype.draw_buttons = file_ns::node_shader_buts_map_range;
530 ntype.ui_class = file_ns::node_shader_map_range_ui_class;
531 ntype.initfunc = file_ns::node_shader_init_map_range;
534 ntype.updatefunc = file_ns::node_shader_update_map_range;
535 ntype.gpu_fn = file_ns::gpu_shader_map_range;
536 ntype.build_multi_function = file_ns::sh_node_map_range_build_multi_function;
537 ntype.gather_link_search_ops = file_ns::node_map_range_gather_link_searches;
538 ntype.materialx_fn = file_ns::node_shader_materialx;
540}
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:410
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define SH_NODE_MAP_RANGE
Definition BKE_node.hh:985
#define NODE_CLASS_OP_VECTOR
Definition BKE_node.hh:407
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
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
@ NODE_MAP_RANGE_STEPPED
@ NODE_MAP_RANGE_SMOOTHERSTEP
@ NODE_MAP_RANGE_SMOOTHSTEP
@ NODE_MAP_RANGE_LINEAR
@ SOCK_IN
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_FLOAT
@ SOCK_RGBA
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,...)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
void set_matching_fn(const mf::MultiFunction *fn)
local_group_size(16, 16) .push_constant(Type b
#define floorf(x)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_set_socket_availability(bNodeTree *ntree, bNodeSocket *sock, bool is_available)
Definition node.cc:3911
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
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 sh_fn_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
return ret
int RNA_enum_get(PointerRNA *ptr, const char *name)
static const int steps
#define min(a, b)
Definition sort.c:32
uint8_t interpolation_type
Defines a node type.
Definition BKE_node.hh:218
NodeMaterialXFunction materialx_fn
Definition BKE_node.hh:320
int(* ui_class)(const bNode *node)
Definition BKE_node.hh:252
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGPUExecFunction gpu_fn
Definition BKE_node.hh:318
NodeMultiFunctionBuildFunction build_multi_function
Definition BKE_node.hh:336
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:363
NodeDeclareFunction declare
Definition BKE_node.hh:347
void(* updatefunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:257
PointerRNA * ptr
Definition wm_files.cc:4126