Blender  V3.3
node_shader_map_range.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2005 Blender Foundation. All rights reserved. */
3 
8 #include <algorithm>
9 
10 #include "node_shader_util.hh"
11 
12 #include "BLI_math_base_safe.h"
13 
15 
16 #include "UI_interface.h"
17 #include "UI_resources.h"
18 
20 
22 
24 {
25  b.is_function_node();
26  b.add_input<decl::Float>(N_("Value")).min(-10000.0f).max(10000.0f).default_value(1.0f);
27  b.add_input<decl::Float>(N_("From Min")).min(-10000.0f).max(10000.0f);
28  b.add_input<decl::Float>(N_("From Max")).min(-10000.0f).max(10000.0f).default_value(1.0f);
29  b.add_input<decl::Float>(N_("To Min")).min(-10000.0f).max(10000.0f);
30  b.add_input<decl::Float>(N_("To Max")).min(-10000.0f).max(10000.0f).default_value(1.0f);
31  b.add_input<decl::Float>(N_("Steps")).min(-10000.0f).max(10000.0f).default_value(4.0f);
32  b.add_input<decl::Vector>(N_("Vector")).min(0.0f).max(1.0f).hide_value();
33  b.add_input<decl::Vector>(N_("From Min"), "From_Min_FLOAT3");
34  b.add_input<decl::Vector>(N_("From Max"), "From_Max_FLOAT3").default_value(float3(1.0f));
35  b.add_input<decl::Vector>(N_("To Min"), "To_Min_FLOAT3");
36  b.add_input<decl::Vector>(N_("To Max"), "To_Max_FLOAT3").default_value(float3(1.0f));
37  b.add_input<decl::Vector>(N_("Steps"), "Steps_FLOAT3").default_value(float3(4.0f));
38  b.add_output<decl::Float>(N_("Result"));
39  b.add_output<decl::Vector>(N_("Vector"));
40 }
41 
43 {
44  uiItemR(layout, ptr, "data_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
45  uiItemR(layout, ptr, "interpolation_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
46  if (!ELEM(RNA_enum_get(ptr, "interpolation_type"),
49  uiItemR(layout, ptr, "clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
50  }
51 }
52 
54 {
55  const NodeMapRange &storage = node_storage(*node);
56  const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
57  if (data_type == CD_PROP_FLOAT3) {
58  return NODE_CLASS_OP_VECTOR;
59  }
60  return NODE_CLASS_CONVERTER;
61 }
62 
64 {
65  const NodeMapRange &storage = node_storage(*node);
66  const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
67  const int type = (data_type == CD_PROP_FLOAT) ? SOCK_FLOAT : SOCK_VECTOR;
68 
69  Array<bool> new_input_availability(BLI_listbase_count(&node->inputs));
70  Array<bool> new_output_availability(BLI_listbase_count(&node->outputs));
71 
72  int index;
73  LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
74  new_input_availability[index] = socket->type == type;
75  }
76  LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->outputs, index) {
77  new_output_availability[index] = socket->type == type;
78  }
79 
81  if (type == SOCK_FLOAT) {
82  new_input_availability[5] = false;
83  }
84  else {
85  new_input_availability[11] = false;
86  }
87  }
88 
89  LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
90  nodeSetSocketAvailability(ntree, socket, new_input_availability[index]);
91  }
92  LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->outputs, index) {
93  nodeSetSocketAvailability(ntree, socket, new_output_availability[index]);
94  }
95 }
96 
98 {
99  NodeMapRange *data = MEM_cnew<NodeMapRange>(__func__);
100  data->clamp = 1;
101  data->data_type = CD_PROP_FLOAT;
102  data->interpolation_type = NODE_MAP_RANGE_LINEAR;
103  node->custom1 = true; /* use_clamp */
104  node->custom2 = NODE_MAP_RANGE_LINEAR; /* interpolation */
105  node->storage = data;
106 }
107 
109  public:
110  std::string socket_name;
113 
115  {
116  bNode &node = params.add_node("ShaderNodeMapRange");
117  node_storage(node).data_type = data_type;
118  node_storage(node).interpolation_type = interpolation_type;
119  params.update_and_connect_available_socket(node, socket_name);
120  }
121 };
122 
123 static std::optional<eCustomDataType> node_type_from_other_socket(const bNodeSocket &socket)
124 {
125  switch (socket.type) {
126  case SOCK_FLOAT:
127  case SOCK_BOOLEAN:
128  case SOCK_INT:
129  return CD_PROP_FLOAT;
130  case SOCK_VECTOR:
131  case SOCK_RGBA:
132  return CD_PROP_FLOAT3;
133  default:
134  return {};
135  }
136 }
137 
139 {
140  const std::optional<eCustomDataType> type = node_type_from_other_socket(params.other_socket());
141  if (!type) {
142  return;
143  }
144 
145  if (params.in_out() == SOCK_IN) {
146  if (*type == CD_PROP_FLOAT3) {
147  params.add_item(IFACE_("Vector"), SocketSearchOp{"Vector", *type}, 0);
148  }
149  else {
150  params.add_item(IFACE_("Value"), SocketSearchOp{"Value", *type}, 0);
151  }
152  params.add_item(IFACE_("From Min"), SocketSearchOp{"From Min", *type}, -1);
153  params.add_item(IFACE_("From Max"), SocketSearchOp{"From Max", *type}, -1);
154  params.add_item(IFACE_("To Min"), SocketSearchOp{"To Min", *type}, -2);
155  params.add_item(IFACE_("To Max"), SocketSearchOp{"To Max", *type}, -2);
156  params.add_item(IFACE_("Steps"), SocketSearchOp{"Steps", *type, NODE_MAP_RANGE_STEPPED}, -3);
157  }
158  else {
159  if (*type == CD_PROP_FLOAT3) {
160  params.add_item(IFACE_("Vector"), SocketSearchOp{"Vector", *type});
161  }
162  else {
163  params.add_item(IFACE_("Result"), SocketSearchOp{"Result", *type});
164  }
165  }
166 }
167 
168 static const char *gpu_shader_get_name(int mode, bool use_vector)
169 {
170  if (use_vector) {
171  switch (mode) {
173  return "vector_map_range_linear";
175  return "vector_map_range_stepped";
177  return "vector_map_range_smoothstep";
179  return "vector_map_range_smootherstep";
180  }
181  }
182  else {
183  switch (mode) {
185  return "map_range_linear";
187  return "map_range_stepped";
189  return "map_range_smoothstep";
191  return "map_range_smootherstep";
192  }
193  }
194 
195  return nullptr;
196 }
197 
199  bNode *node,
200  bNodeExecData *UNUSED(execdata),
201  GPUNodeStack *in,
202  GPUNodeStack *out)
203 {
204  const NodeMapRange &storage = node_storage(*node);
205  bool use_vector = (storage.data_type == CD_PROP_FLOAT3);
206  const char *name = gpu_shader_get_name(storage.interpolation_type, use_vector);
207  float clamp = storage.clamp ? 1.0f : 0.0f;
208  int ret = 0;
209  if (name != nullptr) {
210  ret = GPU_stack_link(mat, node, name, in, out, GPU_constant(&clamp));
211  }
212  else {
213  ret = GPU_stack_link(mat, node, "map_range_linear", in, out, GPU_constant(&clamp));
214  }
215  if (ret && storage.clamp && !use_vector &&
217  GPU_link(mat, "clamp_range", out[0].link, in[3].link, in[4].link, &out[0].link);
218  }
219  return ret;
220 }
221 
222 static inline float clamp_range(const float value, const float min, const float max)
223 {
224  return (min > max) ? std::clamp(value, max, min) : std::clamp(value, min, max);
225 }
226 
227 static float3 clamp_range(const float3 value, const float3 min, const float3 max)
228 {
229  return float3(clamp_range(value.x, min.x, max.x),
230  clamp_range(value.y, min.y, max.y),
231  clamp_range(value.z, min.z, max.z));
232 }
233 
234 template<bool Clamp> static auto build_float_linear()
235 {
242  Clamp ? "Map Range (clamped)" : "Map Range (unclamped)",
243  [](float value, float from_min, float from_max, float to_min, float to_max, float *r_value) {
244  const float factor = safe_divide(value - from_min, from_max - from_min);
245  float result = to_min + factor * (to_max - to_min);
246  if constexpr (Clamp) {
247  result = clamp_range(result, to_min, to_max);
248  }
249  *r_value = result;
250  },
252 }
253 
254 template<bool Clamp> static auto build_float_stepped()
255 {
263  Clamp ? "Map Range Stepped (clamped)" : "Map Range Stepped (unclamped)",
264  [](float value,
265  float from_min,
266  float from_max,
267  float to_min,
268  float to_max,
269  float steps,
270  float *r_value) {
271  float factor = safe_divide(value - from_min, from_max - from_min);
272  factor = safe_divide(floorf(factor * (steps + 1.0f)), steps);
273  float result = to_min + factor * (to_max - to_min);
274  if constexpr (Clamp) {
275  result = clamp_range(result, to_min, to_max);
276  }
277  *r_value = result;
278  },
280 }
281 
282 template<bool Clamp> static auto build_vector_linear()
283 {
290  Clamp ? "Vector Map Range (clamped)" : "Vector Map Range (unclamped)",
291  [](const float3 &value,
292  const float3 &from_min,
293  const float3 &from_max,
294  const float3 &to_min,
295  const float3 &to_max,
296  float3 *r_value) {
297  float3 factor = math::safe_divide(value - from_min, from_max - from_min);
298  float3 result = factor * (to_max - to_min) + to_min;
299  if constexpr (Clamp) {
300  result = clamp_range(result, to_min, to_max);
301  }
302  *r_value = result;
303  },
305 }
306 
307 template<bool Clamp> static auto build_vector_stepped()
308 {
316  Clamp ? "Vector Map Range Stepped (clamped)" : "Vector Map Range Stepped (unclamped)",
317  [](const float3 &value,
318  const float3 &from_min,
319  const float3 &from_max,
320  const float3 &to_min,
321  const float3 &to_max,
322  const float3 &steps,
323  float3 *r_value) {
324  float3 factor = math::safe_divide(value - from_min, from_max - from_min);
325  factor = math::safe_divide(math::floor(factor * (steps + 1.0f)), steps);
326  float3 result = factor * (to_max - to_min) + to_min;
327  if constexpr (Clamp) {
328  result = clamp_range(result, to_min, to_max);
329  }
330  *r_value = result;
331  },
333 }
334 
336 {
337  const NodeMapRange &storage = node_storage(builder.node());
338  bool clamp = storage.clamp != 0;
339  int interpolation_type = storage.interpolation_type;
340 
341  switch (storage.data_type) {
342  case CD_PROP_FLOAT3:
343  switch (interpolation_type) {
344  case NODE_MAP_RANGE_LINEAR: {
345  if (clamp) {
346  static auto fn = build_vector_linear<true>();
347  builder.set_matching_fn(fn);
348  }
349  else {
350  static auto fn = build_vector_linear<false>();
351  builder.set_matching_fn(fn);
352  }
353  break;
354  }
355  case NODE_MAP_RANGE_STEPPED: {
356  if (clamp) {
357  static auto fn = build_vector_stepped<true>();
358  builder.set_matching_fn(fn);
359  }
360  else {
361  static auto fn = build_vector_stepped<false>();
362  builder.set_matching_fn(fn);
363  }
364  break;
365  }
373  fn{"Vector Map Range Smoothstep",
374  [](const float3 &value,
375  const float3 &from_min,
376  const float3 &from_max,
377  const float3 &to_min,
378  const float3 &to_max,
379  float3 *r_value) {
380  float3 factor = math::safe_divide(value - from_min, from_max - from_min);
381  clamp_v3(factor, 0.0f, 1.0f);
382  factor = (float3(3.0f) - 2.0f * factor) * (factor * factor);
383  *r_value = factor * (to_max - to_min) + to_min;
384  },
386  builder.set_matching_fn(fn);
387  break;
388  }
396  fn{"Vector Map Range Smootherstep",
397  [](const float3 &value,
398  const float3 &from_min,
399  const float3 &from_max,
400  const float3 &to_min,
401  const float3 &to_max,
402  float3 *r_value) {
403  float3 factor = math::safe_divide(value - from_min, from_max - from_min);
404  clamp_v3(factor, 0.0f, 1.0f);
405  factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
406  *r_value = factor * (to_max - to_min) + to_min;
407  },
409  builder.set_matching_fn(fn);
410  break;
411  }
412  default:
413  break;
414  }
415  break;
416  case CD_PROP_FLOAT:
417  switch (interpolation_type) {
418  case NODE_MAP_RANGE_LINEAR: {
419  if (clamp) {
420  static auto fn = build_float_linear<true>();
421  builder.set_matching_fn(fn);
422  }
423  else {
424  static auto fn = build_float_linear<false>();
425  builder.set_matching_fn(fn);
426  }
427  break;
428  }
429  case NODE_MAP_RANGE_STEPPED: {
430  if (clamp) {
431  static auto fn = build_float_stepped<true>();
432  builder.set_matching_fn(fn);
433  }
434  else {
435  static auto fn = build_float_stepped<false>();
436  builder.set_matching_fn(fn);
437  }
438  break;
439  }
447  fn{"Map Range Smoothstep",
448  [](float value,
449  float from_min,
450  float from_max,
451  float to_min,
452  float to_max,
453  float *r_value) {
454  float factor = safe_divide(value - from_min, from_max - from_min);
455  factor = std::clamp(factor, 0.0f, 1.0f);
456  factor = (3.0f - 2.0f * factor) * (factor * factor);
457  *r_value = to_min + factor * (to_max - to_min);
458  },
460  builder.set_matching_fn(fn);
461  break;
462  }
470  fn{"Map Range Smoothstep",
471  [](float value,
472  float from_min,
473  float from_max,
474  float to_min,
475  float to_max,
476  float *r_value) {
477  float factor = safe_divide(value - from_min, from_max - from_min);
478  factor = std::clamp(factor, 0.0f, 1.0f);
479  factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
480  *r_value = to_min + factor * (to_max - to_min);
481  },
483  builder.set_matching_fn(fn);
484  break;
485  }
486  default:
487  break;
488  }
489  break;
490  }
491 }
492 
493 } // namespace blender::nodes::node_shader_map_range_cc
494 
496 {
497  namespace file_ns = blender::nodes::node_shader_map_range_cc;
498 
499  static bNodeType ntype;
500 
507  &ntype, "NodeMapRange", node_free_standard_storage, node_copy_standard_storage);
512  nodeRegisterType(&ntype);
513 }
void node_type_gpu(struct bNodeType *ntype, NodeGPUExecFunction gpu_fn)
Definition: node.cc:4465
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4443
#define NODE_CLASS_CONVERTER
Definition: BKE_node.h:351
#define NODE_STORAGE_FUNCS(StorageT)
Definition: BKE_node.h:1563
#define SH_NODE_MAP_RANGE
Definition: BKE_node.h:1173
void nodeSetSocketAvailability(struct bNodeTree *ntree, struct bNodeSocket *sock, bool is_available)
Definition: node.cc:3664
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4390
void node_type_storage(struct bNodeType *ntype, const char *storagename, void(*freefunc)(struct bNode *node), void(*copyfunc)(struct bNodeTree *dest_ntree, struct bNode *dest_node, const struct bNode *src_node))
Definition: node.cc:4426
#define NODE_CLASS_OP_VECTOR
Definition: BKE_node.h:348
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
Definition: BLI_listbase.h:344
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE void clamp_v3(float vec[3], float min, float max)
#define UNUSED(x)
#define ELEM(...)
#define IFACE_(msgid)
eCustomDataType
@ 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
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
GPUNodeLink * GPU_constant(const float *num)
bool GPU_link(GPUMaterial *mat, const char *name,...)
bool GPU_stack_link(GPUMaterial *mat, struct bNode *node, const char *name, GPUNodeStack *in, GPUNodeStack *out,...)
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 Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp
@ UI_ITEM_R_SPLIT_EMPTY_NAME
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void set_matching_fn(const MultiFunction *fn)
OperationNode * node
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define floorf(x)
Definition: metal/compat.h:224
T clamp(const T &a, const T &min, const T &max)
T floor(const T &a)
T safe_divide(const T &a, const T &b)
static void node_shader_init_map_range(bNodeTree *UNUSED(ntree), bNode *node)
static const char * gpu_shader_get_name(int mode, bool use_vector)
static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &builder)
static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static int node_shader_map_range_ui_class(const bNode *node)
static std::optional< eCustomDataType > node_type_from_other_socket(const bNodeSocket &socket)
static float clamp_range(const float value, const float min, const float max)
static void node_shader_update_map_range(bNodeTree *ntree, bNode *node)
static int gpu_shader_map_range(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
static void node_map_range_gather_link_searches(GatherLinkSearchOpParams &params)
static void sh_node_map_range_declare(NodeDeclarationBuilder &b)
vec_base< float, 3 > float3
static const pxr::TfToken out("out", pxr::TfToken::Immortal)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
void register_node_type_sh_map_range()
void sh_fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
void node_copy_standard_storage(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
Definition: node_util.c:55
void node_free_standard_storage(bNode *node)
Definition: node_util.c:43
return ret
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
static const int steps
Definition: sky_nishita.cpp:19
#define min(a, b)
Definition: sort.c:35
uint8_t interpolation_type
Defines a node type.
Definition: BKE_node.h:226
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition: BKE_node.h:335
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
NodeMultiFunctionBuildFunction build_multi_function
Definition: BKE_node.h:313
NodeDeclareFunction declare
Definition: BKE_node.h:324
int(* ui_class)(const struct bNode *node)
Definition: BKE_node.h:262
float max
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480