Blender V4.3
node_shader_math.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 "node_shader_util.hh"
10#include "node_util.hh"
11
13#include "NOD_math_functions.hh"
14#include "NOD_multi_function.hh"
17
18#include "RNA_enum_types.hh"
19
20/* **************** SCALAR MATH ******************** */
21
23
25{
26 b.is_function_node();
27 b.add_input<decl::Float>("Value").default_value(0.5f).min(-10000.0f).max(10000.0f);
28 b.add_input<decl::Float>("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f);
29 b.add_input<decl::Float>("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f);
30 b.add_output<decl::Float>("Value");
31}
32
34 public:
35 std::string socket_name;
38 {
39 bNode &node = params.add_node("ShaderNodeMath");
40 node.custom1 = mode;
41 params.update_and_connect_available_socket(node, socket_name);
42 }
43};
44
46{
47 if (!params.node_tree().typeinfo->validate_link(
48 static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT))
49 {
50 return;
51 }
52
53 const bool is_geometry_node_tree = params.node_tree().type == NTREE_GEOMETRY;
54 const int weight = ELEM(params.other_socket().type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN) ? 0 : -1;
55
56 for (const EnumPropertyItem *item = rna_enum_node_math_items; item->identifier != nullptr;
57 item++)
58 {
59 if (item->name != nullptr && item->identifier[0] != '\0') {
60 const int gn_weight =
61 (is_geometry_node_tree &&
63 -1 :
64 weight;
65 params.add_item(CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, item->name),
66 SocketSearchOp{"Value", (NodeMathOperation)item->value},
67 gn_weight);
68 }
69 }
70}
71
72static const char *gpu_shader_get_name(int mode)
73{
75 if (!info) {
76 return nullptr;
77 }
78 if (info->shader_name.is_empty()) {
79 return nullptr;
80 }
81 return info->shader_name.c_str();
82}
83
85 bNode *node,
86 bNodeExecData * /*execdata*/,
87 GPUNodeStack *in,
88 GPUNodeStack *out)
89{
90 const char *name = gpu_shader_get_name(node->custom1);
91 if (name != nullptr) {
92 int ret = GPU_stack_link(mat, node, name, in, out);
93
94 if (ret && node->custom2 & SHD_MATH_CLAMP) {
95 float min[3] = {0.0f, 0.0f, 0.0f};
96 float max[3] = {1.0f, 1.0f, 1.0f};
98 mat, "clamp_value", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
99 }
100 return ret;
101 }
102
103 return 0;
104}
105
107{
108 const int mode = node.custom1;
109 const mf::MultiFunction *base_fn = nullptr;
110
112 mode, [&](auto devi_fn, auto function, const FloatMathOperationInfo &info) {
113 static auto fn = mf::build::SI1_SO<float, float>(
114 info.title_case_name.c_str(), function, devi_fn);
115 base_fn = &fn;
116 });
117 if (base_fn != nullptr) {
118 return base_fn;
119 }
120
122 mode, [&](auto devi_fn, auto function, const FloatMathOperationInfo &info) {
123 static auto fn = mf::build::SI2_SO<float, float, float>(
124 info.title_case_name.c_str(), function, devi_fn);
125 base_fn = &fn;
126 });
127 if (base_fn != nullptr) {
128 return base_fn;
129 }
130
132 mode, [&](auto devi_fn, auto function, const FloatMathOperationInfo &info) {
133 static auto fn = mf::build::SI3_SO<float, float, float, float>(
134 info.title_case_name.c_str(), function, devi_fn);
135 base_fn = &fn;
136 });
137 if (base_fn != nullptr) {
138 return base_fn;
139 }
140
141 return nullptr;
142}
143
145 private:
146 const mf::MultiFunction &fn_;
147
148 public:
150 {
151 this->set_signature(&fn.signature());
152 }
153
154 void call(const IndexMask &mask, mf::Params params, mf::Context context) const override
155 {
156 fn_.call(mask, params, context);
157
158 /* Assumes the output parameter is the last one. */
159 const int output_param_index = this->param_amount() - 1;
160 /* This has actually been initialized in the call above. */
161 MutableSpan<float> results = params.uninitialized_single_output<float>(output_param_index);
162
163 mask.foreach_index_optimized<int>([&](const int i) {
164 float &value = results[i];
165 CLAMP(value, 0.0f, 1.0f);
166 });
167 }
168};
169
171{
172 const mf::MultiFunction *base_function = get_base_multi_function(builder.node());
173
174 const bool clamp_output = builder.node().custom2 != 0;
175 if (clamp_output) {
177 }
178 else {
179 builder.set_matching_fn(base_function);
180 }
181}
182
184{
185 using namespace value_elem;
186 const NodeMathOperation op = NodeMathOperation(params.node.custom1);
187 switch (op) {
188 case NODE_MATH_ADD:
191 case NODE_MATH_DIVIDE: {
192 FloatElem output_elem = params.get_input_elem<FloatElem>("Value");
193 output_elem.merge(params.get_input_elem<FloatElem>("Value_001"));
194 params.set_output_elem("Value", output_elem);
195 break;
196 }
197 default:
198 break;
199 }
200}
201
203{
204 const NodeMathOperation op = NodeMathOperation(params.node.custom1);
205 switch (op) {
206 case NODE_MATH_ADD:
209 case NODE_MATH_DIVIDE: {
210 params.set_input_elem("Value", params.get_output_elem<value_elem::FloatElem>("Value"));
211 break;
212 }
213 default:
214 break;
215 }
216}
217
219{
220 const NodeMathOperation op = NodeMathOperation(params.node.custom1);
221 const StringRef first_input_id = "Value";
222 const StringRef second_input_id = "Value_001";
223 const StringRef output_id = "Value";
224 switch (op) {
225 case NODE_MATH_ADD: {
226 params.set_input(first_input_id,
227 params.get_output<float>(output_id) -
228 params.get_input<float>(second_input_id));
229 break;
230 }
231 case NODE_MATH_SUBTRACT: {
232 params.set_input(first_input_id,
233 params.get_output<float>(output_id) +
234 params.get_input<float>(second_input_id));
235 break;
236 }
237 case NODE_MATH_MULTIPLY: {
238 params.set_input(first_input_id,
239 math::safe_divide(params.get_output<float>(output_id),
240 params.get_input<float>(second_input_id)));
241 break;
242 }
243 case NODE_MATH_DIVIDE: {
244 params.set_input(first_input_id,
245 params.get_output<float>(output_id) *
246 params.get_input<float>(second_input_id));
247 break;
248 }
249 default: {
250 break;
251 }
252 }
253}
254
256#ifdef WITH_MATERIALX
257{
258 NodeMathOperation op = NodeMathOperation(node_->custom1);
259 NodeItem res = empty();
260
261 /* Single operand operations */
262 NodeItem x = get_input_value(0, NodeItem::Type::Float);
263
264 switch (op) {
265 case NODE_MATH_SINE:
266 res = x.sin();
267 break;
268 case NODE_MATH_COSINE:
269 res = x.cos();
270 break;
272 res = x.tan();
273 break;
275 res = x.asin();
276 break;
278 res = x.acos();
279 break;
281 res = x.atan();
282 break;
283 case NODE_MATH_ROUND:
284 res = (x + val(0.5f)).floor();
285 break;
287 res = x.abs();
288 break;
289 case NODE_MATH_FLOOR:
290 res = x.floor();
291 break;
292 case NODE_MATH_CEIL:
293 res = x.ceil();
294 break;
296 res = x % val(1.0f);
297 break;
298 case NODE_MATH_SQRT:
299 res = x.sqrt();
300 break;
302 res = val(1.0f) / x.sqrt();
303 break;
304 case NODE_MATH_SIGN:
305 res = x.sign();
306 break;
308 res = x.exp();
309 break;
311 res = x * val(float(M_PI) / 180.0f);
312 break;
314 res = x * val(180.0f * float(M_1_PI));
315 break;
316 case NODE_MATH_SINH:
317 res = x.sinh();
318 break;
319 case NODE_MATH_COSH:
320 res = x.cosh();
321 break;
322 case NODE_MATH_TANH:
323 res = x.tanh();
324 break;
325 case NODE_MATH_TRUNC:
326 res = x.sign() * x.abs().floor();
327 break;
328
329 default: {
330 /* 2-operand operations */
331 NodeItem y = get_input_value(1, NodeItem::Type::Float);
332
333 switch (op) {
334 case NODE_MATH_ADD:
335 res = x + y;
336 break;
338 res = x - y;
339 break;
341 res = x * y;
342 break;
343 case NODE_MATH_DIVIDE:
344 res = x / y;
345 break;
346 case NODE_MATH_POWER:
347 res = x ^ y;
348 break;
350 res = x.ln() / y.ln();
351 break;
353 res = x.min(y);
354 break;
356 res = x.max(y);
357 break;
359 res = x.if_else(NodeItem::CompareOp::Less, y, val(1.0f), val(0.0f));
360 break;
362 res = x.if_else(NodeItem::CompareOp::Greater, y, val(1.0f), val(0.0f));
363 break;
364 case NODE_MATH_MODULO:
365 res = x % y;
366 break;
368 res = x.atan2(y);
369 break;
370 case NODE_MATH_SNAP:
371 res = (x / y).floor() * y;
372 break;
373 case NODE_MATH_PINGPONG: {
374 NodeItem fract_part = (x - y) / (y * val(2.0f));
375 NodeItem if_branch = ((fract_part - fract_part.floor()) * y * val(2.0f) - y).abs();
376 res = y.if_else(NodeItem::CompareOp::NotEq, val(0.0f), if_branch, val(0.0f));
377 break;
378 }
380 res = y.if_else(
381 NodeItem::CompareOp::NotEq, val(0.0f), (x - (x / y).floor() * y), val(0.0f));
382 break;
383
384 default: {
385 /* 3-operand operations */
386 NodeItem z = get_input_value(2, NodeItem::Type::Float);
387
388 switch (op) {
389 case NODE_MATH_WRAP: {
390 NodeItem range = (y - z);
391 NodeItem if_branch = x - (range * ((x - z) / range).floor());
392 res = range.if_else(NodeItem::CompareOp::NotEq, val(0.0f), if_branch, z);
393 break;
394 }
396 res = z.if_else(NodeItem::CompareOp::Less, (x - y).abs(), val(1.0f), val(0.0f));
397 break;
399 res = x * y + z;
400 break;
403 auto make_smoothmin = [&](NodeItem a, NodeItem b, NodeItem k) {
404 NodeItem h = (k - (a - b).abs()).max(val(0.0f)) / k;
405 NodeItem if_branch = a.min(b) - h * h * h * k * val(1.0f / 6.0f);
406 return k.if_else(NodeItem::CompareOp::NotEq, val(0.0f), if_branch, a.min(b));
407 };
408 if (op == NODE_MATH_SMOOTH_MIN) {
409 res = make_smoothmin(x, y, z);
410 }
411 else {
412 res = -make_smoothmin(-x, -y, -z);
413 }
414 break;
415 }
416 default:
418 }
419 }
420 }
421 }
422 }
423
424 bool clamp_output = node_->custom2 != 0;
425 if (clamp_output && res) {
426 res = res.clamp();
427 }
428
429 return res;
430}
431#endif
433
434} // namespace blender::nodes::node_shader_math_cc
435
437{
438 namespace file_ns = blender::nodes::node_shader_math_cc;
439
440 static blender::bke::bNodeType ntype;
441
443 ntype.declare = file_ns::sh_node_math_declare;
445 ntype.gpu_fn = file_ns::gpu_shader_math;
447 ntype.build_multi_function = file_ns::sh_node_math_build_multi_function;
448 ntype.gather_link_search_ops = file_ns::sh_node_math_gather_link_searches;
449 ntype.materialx_fn = file_ns::node_shader_materialx;
450 ntype.eval_elem = file_ns::node_eval_elem;
451 ntype.eval_inverse_elem = file_ns::node_eval_inverse_elem;
452 ntype.eval_inverse = file_ns::node_eval_inverse;
453
455}
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:410
#define SH_NODE_MATH
Definition BKE_node.hh:904
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define M_1_PI
#define M_PI
#define CLAMP(a, b, c)
#define ELEM(...)
#define BLT_I18NCONTEXT_ID_NODETREE
#define CTX_IFACE_(context, msgid)
@ NTREE_GEOMETRY
NodeMathOperation
@ NODE_MATH_SINH
@ NODE_MATH_SMOOTH_MIN
@ NODE_MATH_TRUNC
@ NODE_MATH_COSH
@ NODE_MATH_SIGN
@ NODE_MATH_DEGREES
@ NODE_MATH_MODULO
@ NODE_MATH_ABSOLUTE
@ NODE_MATH_DIVIDE
@ NODE_MATH_SINE
@ NODE_MATH_FLOORED_MODULO
@ NODE_MATH_ARCTAN2
@ NODE_MATH_ARCCOSINE
@ NODE_MATH_MULTIPLY_ADD
@ NODE_MATH_POWER
@ NODE_MATH_WRAP
@ NODE_MATH_ARCTANGENT
@ NODE_MATH_MINIMUM
@ NODE_MATH_SQRT
@ NODE_MATH_CEIL
@ NODE_MATH_TANH
@ NODE_MATH_GREATER_THAN
@ NODE_MATH_ADD
@ NODE_MATH_FRACTION
@ NODE_MATH_EXPONENT
@ NODE_MATH_LESS_THAN
@ NODE_MATH_ARCSINE
@ NODE_MATH_MAXIMUM
@ NODE_MATH_LOGARITHM
@ NODE_MATH_COMPARE
@ NODE_MATH_INV_SQRT
@ NODE_MATH_MULTIPLY
@ NODE_MATH_PINGPONG
@ NODE_MATH_ROUND
@ NODE_MATH_FLOOR
@ NODE_MATH_SUBTRACT
@ NODE_MATH_COSINE
@ NODE_MATH_SNAP
@ NODE_MATH_TANGENT
@ NODE_MATH_SMOOTH_MAX
@ NODE_MATH_RADIANS
@ SHD_MATH_CLAMP
eNodeSocketDatatype
@ SOCK_INT
@ SOCK_BOOLEAN
@ SOCK_FLOAT
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,...)
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
constexpr bool is_empty() const
constexpr const char * c_str() const
void set_signature(const Signature *signature)
virtual void call(const IndexMask &mask, Params params, Context context) const =0
void set_matching_fn(const mf::MultiFunction *fn)
void call(const IndexMask &mask, mf::Params params, mf::Context context) const override
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 floor(const float2 a)
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
T safe_divide(const T &a, const T &b)
static int gpu_shader_math(GPUMaterial *mat, bNode *node, bNodeExecData *, GPUNodeStack *in, GPUNodeStack *out)
static void node_eval_inverse_elem(value_elem::InverseElemEvalParams &params)
static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams &params)
static void sh_node_math_build_multi_function(NodeMultiFunctionBuilder &builder)
static const char * gpu_shader_get_name(int mode)
static void sh_node_math_declare(NodeDeclarationBuilder &b)
static void node_eval_elem(value_elem::ElemEvalParams &params)
static void node_eval_inverse(inverse_eval::InverseEvalParams &params)
static const mf::MultiFunction * get_base_multi_function(const bNode &node)
bool try_dispatch_float_math_fl_fl_to_fl(const int operation, Callback &&callback)
bool try_dispatch_float_math_fl_to_fl(const int operation, Callback &&callback)
const FloatMathOperationInfo * get_float_math_operation_info(const int operation)
bool try_dispatch_float_math_fl_fl_fl_to_fl(const int operation, Callback &&callback)
#define NODE_SHADER_MATERIALX_BEGIN
#define NODE_SHADER_MATERIALX_END
void register_node_type_sh_math()
void sh_fn_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_math_label(const bNodeTree *, const bNode *node, char *label, int label_maxncpy)
Definition node_util.cc:203
void node_math_update(bNodeTree *ntree, bNode *node)
Definition node_util.cc:89
return ret
const EnumPropertyItem rna_enum_node_math_items[]
#define min(a, b)
Definition sort.c:32
const char * identifier
Definition RNA_types.hh:506
int16_t custom2
Defines a node type.
Definition BKE_node.hh:218
NodeInverseElemEvalFunction eval_inverse_elem
Definition BKE_node.hh:378
NodeMaterialXFunction materialx_fn
Definition BKE_node.hh:320
NodeInverseEvalFunction eval_inverse
Definition BKE_node.hh:385
void(* labelfunc)(const bNodeTree *ntree, const bNode *node, char *label, int label_maxncpy)
Definition BKE_node.hh:249
NodeGPUExecFunction gpu_fn
Definition BKE_node.hh:318
NodeElemEvalFunction eval_elem
Definition BKE_node.hh:372
NodeMultiFunctionBuildFunction build_multi_function
Definition BKE_node.hh:336
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
float max
ccl_device_inline int abs(int x)
Definition util/math.h:120