Blender V5.0
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
8
9#include "node_shader_util.hh"
10#include "node_util.hh"
11
13#include "NOD_math_functions.hh"
16
17#include "RNA_enum_types.hh"
18
19/* **************** SCALAR MATH ******************** */
20
22
24{
25 b.is_function_node();
26 b.add_input<decl::Float>("Value").default_value(0.5f).min(-10000.0f).max(10000.0f);
27 b.add_input<decl::Float>("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f);
28 b.add_input<decl::Float>("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f);
29 b.add_output<decl::Float>("Value");
30}
31
33 public:
34 std::string socket_name;
37 {
38 bNode &node = params.add_node("ShaderNodeMath");
39 node.custom1 = mode;
40 params.update_and_connect_available_socket(node, socket_name);
41 }
42};
43
45{
46 if (!params.node_tree().typeinfo->validate_link(eNodeSocketDatatype(params.other_socket().type),
48 {
49 return;
50 }
51
52 const bool is_geometry_node_tree = params.node_tree().type == NTREE_GEOMETRY;
53 const int weight = ELEM(params.other_socket().type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN) ? 0 : -1;
54
55 for (const EnumPropertyItem *item = rna_enum_node_math_items; item->identifier != nullptr;
56 item++)
57 {
58 if (item->name != nullptr && item->identifier[0] != '\0') {
59 const int gn_weight =
60 (is_geometry_node_tree &&
62 -1 :
63 weight;
64 params.add_item(CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, item->name),
65 SocketSearchOp{"Value", (NodeMathOperation)item->value},
66 gn_weight);
67 }
68 }
69}
70
71static const char *gpu_shader_get_name(int mode)
72{
74 if (!info) {
75 return nullptr;
76 }
77 if (info->shader_name.is_empty()) {
78 return nullptr;
79 }
80 return info->shader_name.c_str();
81}
82
84 bNode *node,
85 bNodeExecData * /*execdata*/,
88{
89 const char *name = gpu_shader_get_name(node->custom1);
90 if (name != nullptr) {
91 int ret = GPU_stack_link(mat, node, name, in, out);
92
93 if (ret && node->custom2 & SHD_MATH_CLAMP) {
94 float min[3] = {0.0f, 0.0f, 0.0f};
95 float max[3] = {1.0f, 1.0f, 1.0f};
97 mat, "clamp_value", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
98 }
99 return ret;
100 }
101
102 return 0;
103}
104
106{
107 using namespace value_elem;
108 const NodeMathOperation op = NodeMathOperation(params.node.custom1);
109 switch (op) {
110 case NODE_MATH_ADD:
113 case NODE_MATH_DIVIDE: {
114 FloatElem output_elem = params.get_input_elem<FloatElem>("Value");
115 output_elem.merge(params.get_input_elem<FloatElem>("Value_001"));
116 params.set_output_elem("Value", output_elem);
117 break;
118 }
119 default:
120 break;
121 }
122}
123
125{
126 const NodeMathOperation op = NodeMathOperation(params.node.custom1);
127 switch (op) {
128 case NODE_MATH_ADD:
131 case NODE_MATH_DIVIDE: {
132 params.set_input_elem("Value", params.get_output_elem<value_elem::FloatElem>("Value"));
133 break;
134 }
135 default:
136 break;
137 }
138}
139
141{
142 const NodeMathOperation op = NodeMathOperation(params.node.custom1);
143 const StringRef first_input_id = "Value";
144 const StringRef second_input_id = "Value_001";
145 const StringRef output_id = "Value";
146 switch (op) {
147 case NODE_MATH_ADD: {
148 params.set_input(first_input_id,
149 params.get_output<float>(output_id) -
150 params.get_input<float>(second_input_id));
151 break;
152 }
153 case NODE_MATH_SUBTRACT: {
154 params.set_input(first_input_id,
155 params.get_output<float>(output_id) +
156 params.get_input<float>(second_input_id));
157 break;
158 }
159 case NODE_MATH_MULTIPLY: {
160 params.set_input(first_input_id,
161 math::safe_divide(params.get_output<float>(output_id),
162 params.get_input<float>(second_input_id)));
163 break;
164 }
165 case NODE_MATH_DIVIDE: {
166 params.set_input(first_input_id,
167 params.get_output<float>(output_id) *
168 params.get_input<float>(second_input_id));
169 break;
170 }
171 default: {
172 break;
173 }
174 }
175}
176
178#ifdef WITH_MATERIALX
179{
180 NodeMathOperation op = NodeMathOperation(node_->custom1);
181 NodeItem res = empty();
182
183 /* Single operand operations */
184 NodeItem x = get_input_value(0, NodeItem::Type::Float);
185
186 switch (op) {
187 case NODE_MATH_SINE:
188 res = x.sin();
189 break;
190 case NODE_MATH_COSINE:
191 res = x.cos();
192 break;
194 res = x.tan();
195 break;
197 res = x.asin();
198 break;
200 res = x.acos();
201 break;
203 res = x.atan();
204 break;
205 case NODE_MATH_ROUND:
206 res = (x + val(0.5f)).floor();
207 break;
209 res = x.abs();
210 break;
211 case NODE_MATH_FLOOR:
212 res = x.floor();
213 break;
214 case NODE_MATH_CEIL:
215 res = x.ceil();
216 break;
218 res = x % val(1.0f);
219 break;
220 case NODE_MATH_SQRT:
221 res = x.sqrt();
222 break;
224 res = val(1.0f) / x.sqrt();
225 break;
226 case NODE_MATH_SIGN:
227 res = x.sign();
228 break;
230 res = x.exp();
231 break;
233 res = x * val(float(M_PI) / 180.0f);
234 break;
236 res = x * val(180.0f * float(M_1_PI));
237 break;
238 case NODE_MATH_SINH:
239 res = x.sinh();
240 break;
241 case NODE_MATH_COSH:
242 res = x.cosh();
243 break;
244 case NODE_MATH_TANH:
245 res = x.tanh();
246 break;
247 case NODE_MATH_TRUNC:
248 res = x.sign() * x.abs().floor();
249 break;
250
251 default: {
252 /* 2-operand operations */
253 NodeItem y = get_input_value(1, NodeItem::Type::Float);
254
255 switch (op) {
256 case NODE_MATH_ADD:
257 res = x + y;
258 break;
260 res = x - y;
261 break;
263 res = x * y;
264 break;
265 case NODE_MATH_DIVIDE:
266 res = x / y;
267 break;
268 case NODE_MATH_POWER:
269 res = x ^ y;
270 break;
272 res = x.ln() / y.ln();
273 break;
275 res = x.min(y);
276 break;
278 res = x.max(y);
279 break;
281 res = x.if_else(NodeItem::CompareOp::Less, y, val(1.0f), val(0.0f));
282 break;
284 res = x.if_else(NodeItem::CompareOp::Greater, y, val(1.0f), val(0.0f));
285 break;
286 case NODE_MATH_MODULO:
287 res = x % y;
288 break;
290 res = x.atan2(y);
291 break;
292 case NODE_MATH_SNAP:
293 res = (x / y).floor() * y;
294 break;
295 case NODE_MATH_PINGPONG: {
296 NodeItem fract_part = (x - y) / (y * val(2.0f));
297 NodeItem if_branch = ((fract_part - fract_part.floor()) * y * val(2.0f) - y).abs();
298 res = y.if_else(NodeItem::CompareOp::NotEq, val(0.0f), if_branch, val(0.0f));
299 break;
300 }
302 res = y.if_else(
303 NodeItem::CompareOp::NotEq, val(0.0f), (x - (x / y).floor() * y), val(0.0f));
304 break;
305
306 default: {
307 /* 3-operand operations */
308 NodeItem z = get_input_value(2, NodeItem::Type::Float);
309
310 switch (op) {
311 case NODE_MATH_WRAP: {
312 NodeItem range = (y - z);
313 NodeItem if_branch = x - (range * ((x - z) / range).floor());
314 res = range.if_else(NodeItem::CompareOp::NotEq, val(0.0f), if_branch, z);
315 break;
316 }
318 res = z.if_else(NodeItem::CompareOp::Less, (x - y).abs(), val(1.0f), val(0.0f));
319 break;
321 res = x * y + z;
322 break;
325 auto make_smoothmin = [&](NodeItem a, NodeItem b, NodeItem k) {
326 NodeItem h = (k - (a - b).abs()).max(val(0.0f)) / k;
327 NodeItem if_branch = a.min(b) - h * h * h * k * val(1.0f / 6.0f);
328 return k.if_else(NodeItem::CompareOp::NotEq, val(0.0f), if_branch, a.min(b));
329 };
330 if (op == NODE_MATH_SMOOTH_MIN) {
331 res = make_smoothmin(x, y, z);
332 }
333 else {
334 res = -make_smoothmin(-x, -y, -z);
335 }
336 break;
337 }
338 default:
340 }
341 }
342 }
343 }
344 }
345
346 bool clamp_output = node_->custom2 != 0;
347 if (clamp_output && res) {
348 res = res.clamp();
349 }
350
351 return res;
352}
353#endif
355
356} // namespace blender::nodes::node_shader_math_cc
357
359{
360 namespace file_ns = blender::nodes::node_shader_math_cc;
361
362 static blender::bke::bNodeType ntype;
363
364 common_node_type_base(&ntype, "ShaderNodeMath", SH_NODE_MATH);
365 ntype.ui_name = "Math";
366 ntype.ui_description = "Perform math operations";
367 ntype.enum_name_legacy = "MATH";
369 ntype.declare = file_ns::sh_node_math_declare;
371 ntype.gpu_fn = file_ns::gpu_shader_math;
374 ntype.gather_link_search_ops = file_ns::sh_node_math_gather_link_searches;
375 ntype.materialx_fn = file_ns::node_shader_materialx;
376 ntype.eval_elem = file_ns::node_eval_elem;
377 ntype.eval_inverse_elem = file_ns::node_eval_inverse_elem;
378 ntype.eval_inverse = file_ns::node_eval_inverse;
379
381}
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:453
#define SH_NODE_MATH
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define M_1_PI
#define M_PI
#define ELEM(...)
#define BLT_I18NCONTEXT_ID_NODETREE
#define CTX_IFACE_(context, msgid)
@ SHD_MATH_CLAMP
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
@ NTREE_GEOMETRY
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
#define in
#define out
#define abs
#define floor
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
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 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)
void node_math_build_multi_function(NodeMultiFunctionBuilder &builder)
const FloatMathOperationInfo * get_float_math_operation_info(const int operation)
#define NODE_SHADER_MATERIALX_BEGIN
#define NODE_SHADER_MATERIALX_END
void register_node_type_sh_math()
void common_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_math_label(const bNodeTree *, const bNode *node, char *label, int label_maxncpy)
Definition node_util.cc:201
void node_math_update(bNodeTree *ntree, bNode *node)
Definition node_util.cc:85
const char * name
return ret
const EnumPropertyItem rna_enum_node_math_items[]
#define min(a, b)
Definition sort.cc:36
int16_t custom1
int16_t custom2
Defines a node type.
Definition BKE_node.hh:238
NodeInverseElemEvalFunction eval_inverse_elem
Definition BKE_node.hh:403
NodeMaterialXFunction materialx_fn
Definition BKE_node.hh:344
NodeInverseEvalFunction eval_inverse
Definition BKE_node.hh:410
std::string ui_description
Definition BKE_node.hh:244
void(* labelfunc)(const bNodeTree *ntree, const bNode *node, char *label, int label_maxncpy)
Definition BKE_node.hh:270
NodeGPUExecFunction gpu_fn
Definition BKE_node.hh:342
NodeElemEvalFunction eval_elem
Definition BKE_node.hh:397
NodeMultiFunctionBuildFunction build_multi_function
Definition BKE_node.hh:351
const char * enum_name_legacy
Definition BKE_node.hh:247
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
max
Definition text_draw.cc:251