Blender V4.5
node_composite_lensdist.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_base.h"
10#include "BLI_math_base.hh"
11#include "BLI_math_vector.hh"
13#include "BLI_noise.hh"
14
15#include "RNA_access.hh"
16
17#include "UI_interface.hh"
18#include "UI_resources.hh"
19
20#include "GPU_shader.hh"
21#include "GPU_texture.hh"
22
23#include "COM_node_operation.hh"
24#include "COM_utilities.hh"
25
27
28/* Distortion can't be exactly -1.0 as it will cause infinite pincushion distortion. */
29#define MINIMUM_DISTORTION -0.999f
30/* Arbitrary scaling factor for the dispersion input in horizontal distortion mode. */
31#define HORIZONTAL_DISPERSION_SCALE 5.0f
32/* Arbitrary scaling factor for the dispersion input in radial distortion mode. */
33#define RADIAL_DISPERSION_SCALE 4.0f
34/* Arbitrary scaling factor for the distortion input. */
35#define DISTORTION_SCALE 4.0f
36
38
40
42{
43 b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
44 b.add_input<decl::Float>("Distortion")
45 .default_value(0.0f)
46 .subtype(PROP_FACTOR)
48 .max(1.0f)
49 .description(
50 "The amount of distortion. 0 means no distortion, -1 means full Pincushion distortion, "
51 "and 1 means full Barrel distortion")
52 .compositor_expects_single_value();
53 b.add_input<decl::Float>("Dispersion")
54 .default_value(0.0f)
55 .subtype(PROP_FACTOR)
56 .min(0.0f)
57 .max(1.0f)
58 .description("The amount of chromatic aberration to add to the distortion")
59 .compositor_expects_single_value();
60 b.add_input<decl::Bool>("Jitter")
61 .default_value(false)
62 .description(
63 "Introduces jitter while doing distortion, which can be faster but can produce grainy "
64 "or noisy results")
65 .compositor_expects_single_value();
66 b.add_input<decl::Bool>("Fit")
67 .default_value(false)
68 .description(
69 "Scales the image such that it fits entirely in the frame, leaving no empty spaces at "
70 "the corners")
71 .compositor_expects_single_value();
72 b.add_output<decl::Color>("Image");
73}
74
75static void node_composit_init_lensdist(bNodeTree * /*ntree*/, bNode *node)
76{
78 data->distortion_type = CMP_NODE_LENS_DISTORTION_RADIAL;
79 node->storage = data;
80}
81
83{
84 layout->prop(ptr, "distortion_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
85}
86
87static void node_update(bNodeTree *ntree, bNode *node)
88{
90 node_storage(*node).distortion_type);
91
92 bNodeSocket *distortion_input = bke::node_find_socket(*node, SOCK_IN, "Distortion");
94 *ntree, *distortion_input, distortion_type == CMP_NODE_LENS_DISTORTION_RADIAL);
95
96 bNodeSocket *jitter_input = bke::node_find_socket(*node, SOCK_IN, "Jitter");
98 *ntree, *jitter_input, distortion_type == CMP_NODE_LENS_DISTORTION_RADIAL);
99
100 bNodeSocket *fit_input = bke::node_find_socket(*node, SOCK_IN, "Fit");
102 *ntree, *fit_input, distortion_type == CMP_NODE_LENS_DISTORTION_RADIAL);
103}
104
105using namespace blender::compositor;
106
107/* --------------------------------------------------------------------
108 * Screen Lens Distortion
109 */
110
111/* A model that approximates lens distortion parameterized by a distortion parameter and dependent
112 * on the squared distance to the center of the image. The distorted pixel is then computed as the
113 * scalar multiplication of the pixel coordinates with the value returned by this model. See the
114 * compute_distorted_uv function for more details. */
115static float compute_distortion_scale(const float distortion, const float distance_squared)
116{
117 return 1.0f / (1.0f + math::sqrt(math::max(0.0f, 1.0f - distortion * distance_squared)));
118}
119
120/* A vectorized version of compute_distortion_scale that is applied on the chromatic distortion
121 * parameters passed to the shader. */
122static float3 compute_chromatic_distortion_scale(const float3 &chromatic_distortion,
123 const float distance_squared)
124{
125 return 1.0f / (1.0f + math::sqrt(math::max(float3(0.0f),
126 1.0f - chromatic_distortion * distance_squared)));
127}
128
129/* Compute the image coordinates after distortion by the given distortion scale computed by the
130 * compute_distortion_scale function. Note that the function expects centered normalized UV
131 * coordinates but outputs non-centered image coordinates. */
132static float2 compute_distorted_uv(const float2 &uv, const float uv_scale, const int2 &size)
133{
134 return (uv * uv_scale + 0.5f) * float2(size);
135}
136
137/* Compute the number of integration steps that should be used to approximate the distorted pixel
138 * using a heuristic, see the compute_number_of_steps function for more details. The numbers of
139 * steps is proportional to the number of pixels spanned by the distortion amount. For jitter
140 * distortion, the square root of the distortion amount plus 1 is used with a minimum of 2 steps.
141 * For non-jitter distortion, the distortion amount plus 1 is used as the number of steps */
142static int compute_number_of_integration_steps_heuristic(const float distortion,
143 const bool use_jitter)
144{
145 if (use_jitter) {
146 return distortion < 4.0f ? 2 : int(math::sqrt(distortion + 1.0f));
147 }
148 return int(distortion + 1.0f);
149}
150
151/* Compute the number of integration steps that should be used to compute each channel of the
152 * distorted pixel. Each of the channels are distorted by their respective chromatic distortion
153 * amount, then the amount of distortion between each two consecutive channels is computed, this
154 * amount is then used to heuristically infer the number of needed integration steps, see the
155 * integrate_distortion function for more information. */
156static int3 compute_number_of_integration_steps(const float3 &chromatic_distortion,
157 const int2 &size,
158 const float2 &uv,
159 const float distance_squared,
160 const bool use_jitter)
161{
162 /* Distort each channel by its respective chromatic distortion amount. */
163 float3 distortion_scale = compute_chromatic_distortion_scale(chromatic_distortion,
164 distance_squared);
165 float2 distorted_uv_red = compute_distorted_uv(uv, distortion_scale.x, size);
166 float2 distorted_uv_green = compute_distorted_uv(uv, distortion_scale.y, size);
167 float2 distorted_uv_blue = compute_distorted_uv(uv, distortion_scale.z, size);
168
169 /* Infer the number of needed integration steps to compute the distorted red channel starting
170 * from the green channel. */
171 float distortion_red = math::distance(distorted_uv_red, distorted_uv_green);
172 int steps_red = compute_number_of_integration_steps_heuristic(distortion_red, use_jitter);
173
174 /* Infer the number of needed integration steps to compute the distorted blue channel starting
175 * from the green channel. */
176 float distortion_blue = math::distance(distorted_uv_green, distorted_uv_blue);
177 int steps_blue = compute_number_of_integration_steps_heuristic(distortion_blue, use_jitter);
178
179 /* The number of integration steps used to compute the green channel is the sum of both the red
180 * and the blue channel steps because it is computed once with each of them. */
181 return int3(steps_red, steps_red + steps_blue, steps_blue);
182}
183
184/* Returns a random jitter amount, which is essentially a random value in the [0, 1] range. If
185 * jitter is not enabled, return a constant 0.5 value instead. */
186static float get_jitter(const int2 &texel, const int seed, const bool use_jitter)
187{
188 if (use_jitter) {
189 return noise::hash_to_float(texel.x, texel.y, seed);
190 }
191 return 0.5f;
192}
193
194/* Each color channel may have a different distortion with the guarantee that the red will have the
195 * lowest distortion while the blue will have the highest one. If each channel is distorted
196 * independently, the image will look disintegrated, with each channel seemingly merely shifted.
197 * Consequently, the distorted pixels needs to be computed by integrating along the path of change
198 * of distortion starting from one channel to another. For instance, to compute the distorted red
199 * from the distorted green, we accumulate the color of the distorted pixel starting from the
200 * distortion of the red, taking small steps until we reach the distortion of the green. The pixel
201 * color is weighted such that it is maximum at the start distortion and zero at the end distortion
202 * in an arithmetic progression. The integration steps can be augmented with random values to
203 * simulate lens jitter. Finally, it should be noted that this function integrates both the start
204 * and end channels in reverse directions for more efficient computation. */
205static float3 integrate_distortion(const int2 &texel,
206 const Result &input,
207 const int2 &size,
208 const float3 &chromatic_distortion,
209 const int start,
210 const int end,
211 const float distance_squared,
212 const float2 &uv,
213 const int steps,
214 const bool use_jitter)
215{
216 float3 accumulated_color = float3(0.0f);
217 float distortion_amount = chromatic_distortion[end] - chromatic_distortion[start];
218 for (int i = 0; i < steps; i++) {
219 /* The increment will be in the [0, 1) range across iterations. Include the start channel in
220 * the jitter seed to make sure each channel gets a different jitter. */
221 float increment = (i + get_jitter(texel, start * steps + i, use_jitter)) / steps;
222 float distortion = chromatic_distortion[start] + increment * distortion_amount;
223 float distortion_scale = compute_distortion_scale(distortion, distance_squared);
224
225 /* Sample the color at the distorted coordinates and accumulate it weighted by the increment
226 * value for both the start and end channels. */
227 float2 distorted_uv = compute_distorted_uv(uv, distortion_scale, size);
228 float4 color = input.sample_bilinear_zero(distorted_uv / float2(size));
229 accumulated_color[start] += (1.0f - increment) * color[start];
230 accumulated_color[end] += increment * color[end];
231 }
232 return accumulated_color;
233}
234
235static void radial_lens_distortion(const int2 texel,
236 const Result &input,
237 Result &output,
238 const int2 &size,
239 const float3 &chromatic_distortion,
240 const float scale,
241 const bool use_jitter)
242{
243 /* Compute the UV image coordinates in the range [-1, 1] as well as the squared distance to the
244 * center of the image, which is at (0, 0) in the UV coordinates. */
245 float2 center = float2(size) / 2.0f;
246 float2 uv = scale * (float2(texel) + float2(0.5f) - center) / center;
247 float distance_squared = math::dot(uv, uv);
248
249 /* If any of the color channels will get distorted outside of the screen beyond what is possible,
250 * write a zero transparent color and return. */
251 float3 distortion_bounds = chromatic_distortion * distance_squared;
252 if (distortion_bounds.x > 1.0f || distortion_bounds.y > 1.0f || distortion_bounds.z > 1.0f) {
253 output.store_pixel(texel, float4(0.0f));
254 return;
255 }
256
257 /* Compute the number of integration steps that should be used to compute each channel of the
258 * distorted pixel. */
260 chromatic_distortion, size, uv, distance_squared, use_jitter);
261
262 /* Integrate the distortion of the red and green, then the green and blue channels. That means
263 * the green will be integrated twice, but this is accounted for in the number of steps which the
264 * color will later be divided by. See the compute_number_of_integration_steps function for more
265 * details. */
266 float3 color = float3(0.0f);
268 input,
269 size,
270 chromatic_distortion,
271 0,
272 1,
273 distance_squared,
274 uv,
275 number_of_steps.x,
276 use_jitter);
278 input,
279 size,
280 chromatic_distortion,
281 1,
282 2,
283 distance_squared,
284 uv,
285 number_of_steps.z,
286 use_jitter);
287
288 /* The integration above performed weighted accumulation, and thus the color needs to be divided
289 * by the sum of the weights. Assuming no jitter, the weights are generated as an arithmetic
290 * progression starting from (0.5 / n) to ((n - 0.5) / n) for n terms. The sum of an arithmetic
291 * progression can be computed as (n * (start + end) / 2), which when subsisting the start and
292 * end reduces to (n / 2). So the color should be multiplied by 2 / n. The jitter sequence
293 * approximately sums to the same value because it is a uniform random value whose mean value is
294 * 0.5, so the expression doesn't change regardless of jitter. */
295 color *= 2.0f / float3(number_of_steps);
296
297 output.store_pixel(texel, float4(color, 1.0f));
298}
299
301 public:
303
304 void execute() override
305 {
306 if (this->is_identity()) {
307 const Result &input = this->get_input("Image");
308 Result &output = this->get_result("Image");
309 output.share_data(input);
310 return;
311 }
312
313 switch (this->get_type()) {
316 break;
319 break;
320 }
321 }
322
324 {
325 if (this->context().use_gpu()) {
327 }
328 else {
330 }
331 }
332
334 {
335 GPUShader *shader = context().get_shader("compositor_horizontal_lens_distortion");
336 GPU_shader_bind(shader);
337
338 const Result &input_image = get_input("Image");
339 GPU_texture_filter_mode(input_image, true);
341 input_image.bind_as_texture(shader, "input_tx");
342
343 const Domain domain = compute_domain();
344
345 const float dispersion = (get_dispersion() * HORIZONTAL_DISPERSION_SCALE) / domain.size.x;
346 GPU_shader_uniform_1f(shader, "dispersion", dispersion);
347
348 Result &output_image = get_result("Image");
349 output_image.allocate_texture(domain);
350 output_image.bind_as_image(shader, "output_img");
351
353
354 input_image.unbind_as_texture();
355 output_image.unbind_as_image();
357 }
358
360 {
361 const Domain domain = compute_domain();
362 const float dispersion = (get_dispersion() * HORIZONTAL_DISPERSION_SCALE) / domain.size.x;
363
364 const Result &input = get_input("Image");
365
366 Result &output = get_result("Image");
367 output.allocate_texture(domain);
368
369 const int2 size = domain.size;
370 parallel_for(size, [&](const int2 texel) {
371 /* Get the normalized coordinates of the pixel centers. */
372 float2 normalized_texel = (float2(texel) + float2(0.5f)) / float2(size);
373
374 /* Sample the red and blue channels shifted by the dispersion amount. */
375 const float red = input.sample_bilinear_zero(normalized_texel + float2(dispersion, 0.0f)).x;
376 const float green = input.load_pixel<float4>(texel).y;
377 const float blue = input.sample_bilinear_zero(normalized_texel - float2(dispersion, 0.0f)).z;
378
379 output.store_pixel(texel, float4(red, green, blue, 1.0f));
380 });
381 }
382
384 {
385 if (this->context().use_gpu()) {
387 }
388 else {
390 }
391 }
392
394 {
395 GPUShader *shader = context().get_shader(get_radial_distortion_shader());
396 GPU_shader_bind(shader);
397
398 const Result &input_image = get_input("Image");
399 GPU_texture_filter_mode(input_image, true);
401 input_image.bind_as_texture(shader, "input_tx");
402
403 const Domain domain = compute_domain();
404
405 const float3 chromatic_distortion = compute_chromatic_distortion();
406 GPU_shader_uniform_3fv(shader, "chromatic_distortion", chromatic_distortion);
407
408 GPU_shader_uniform_1f(shader, "scale", compute_scale());
409
410 Result &output_image = get_result("Image");
411 output_image.allocate_texture(domain);
412 output_image.bind_as_image(shader, "output_img");
413
415
416 input_image.unbind_as_texture();
417 output_image.unbind_as_image();
419 }
420
422 {
423 if (get_use_jitter()) {
424 return "compositor_radial_lens_distortion_jitter";
425 }
426 return "compositor_radial_lens_distortion";
427 }
428
430 {
431 const float scale = this->compute_scale();
432 const bool use_jitter = this->get_use_jitter();
433 const float3 chromatic_distortion = this->compute_chromatic_distortion();
434
435 const Result &input = get_input("Image");
436
437 const Domain domain = compute_domain();
438 Result &output = get_result("Image");
439 output.allocate_texture(domain);
440
441 const int2 size = domain.size;
442 parallel_for(size, [&](const int2 texel) {
443 radial_lens_distortion(texel, input, output, size, chromatic_distortion, scale, use_jitter);
444 });
445 }
446
448 {
449 const Result &input = get_input("Distortion");
450 return clamp_f(input.get_single_value_default(0.0f), MINIMUM_DISTORTION, 1.0f);
451 }
452
454 {
455 const Result &input = get_input("Dispersion");
456 return clamp_f(input.get_single_value_default(0.0f), 0.0f, 1.0f);
457 }
458
459 /* Get the distortion amount for each channel. The green channel has a distortion amount that
460 * matches that specified in the node inputs, while the red and blue channels have higher and
461 * lower distortion amounts respectively based on the dispersion value. */
463 {
464 const float green_distortion = get_distortion();
465 const float dispersion = get_dispersion() / RADIAL_DISPERSION_SCALE;
466 const float red_distortion = clamp_f(green_distortion + dispersion, MINIMUM_DISTORTION, 1.0f);
467 const float blue_distortion = clamp_f(green_distortion - dispersion, MINIMUM_DISTORTION, 1.0f);
468 return float3(red_distortion, green_distortion, blue_distortion) * DISTORTION_SCALE;
469 }
470
471 /* The distortion model will distort the image in such a way that the result will no longer
472 * fit the domain of the original image, so we scale the image to account for that. If get_is_fit
473 * is false, then the scaling factor will be such that the furthest pixels horizontally and
474 * vertically are at the boundary of the image. Otherwise, if get_is_fit is true, the scaling
475 * factor will be such that the furthest pixels diagonally are at the corner of the image. */
477 {
479 const float maximum_distortion = max_fff(distortion[0], distortion[1], distortion[2]);
480
481 if (get_is_fit() && (maximum_distortion > 0.0f)) {
482 return 1.0f / (1.0f + 2.0f * maximum_distortion);
483 }
484 return 1.0f / (1.0f + maximum_distortion);
485 }
486
488 {
489 return CMPNodeLensDistortionType(node_storage(bnode()).distortion_type);
490 }
491
493 {
494 return this->get_input("Jitter").get_single_value_default(false);
495 }
496
498 {
499 return this->get_input("Fit").get_single_value_default(false);
500 }
501
502 /* Returns true if the operation does nothing and the input can be passed through. */
504 {
505 /* The input is a single value and the operation does nothing. */
506 if (this->get_input("Image").is_single_value()) {
507 return true;
508 }
509
510 /* Projector have zero dispersion and does nothing. */
512 return this->get_dispersion() == 0.0f;
513 }
514
515 /* Both distortion and dispersion are zero and the operation does nothing. Jittering has an
516 * effect regardless, so its gets an exemption. */
517 if (!this->get_use_jitter() && this->get_distortion() == 0.0f &&
518 this->get_dispersion() == 0.0f)
519 {
520 return true;
521 }
522
523 return false;
524 }
525};
526
528{
529 return new LensDistortionOperation(context, node);
530}
531
532} // namespace blender::nodes::node_composite_lensdist_cc
533
535{
537
538 static blender::bke::bNodeType ntype;
539
540 cmp_node_type_base(&ntype, "CompositorNodeLensdist", CMP_NODE_LENSDIST);
541 ntype.ui_name = "Lens Distortion";
542 ntype.ui_description = "Simulate distortion and dispersion from camera lenses";
543 ntype.enum_name_legacy = "LENSDIST";
545 ntype.declare = file_ns::cmp_node_lensdist_declare;
546 ntype.updatefunc = file_ns::node_update;
547 ntype.draw_buttons = file_ns::node_composit_buts_lensdist;
548 ntype.initfunc = file_ns::node_composit_init_lensdist;
551 ntype.get_compositor_operation = file_ns::get_compositor_operation;
552
554}
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define NODE_CLASS_DISTORT
Definition BKE_node.hh:441
#define CMP_NODE_LENSDIST
MINLINE float max_fff(float a, float b, float c)
MINLINE float clamp_f(float value, float min, float max)
@ SOCK_IN
CMPNodeLensDistortionType
@ CMP_NODE_LENS_DISTORTION_RADIAL
@ CMP_NODE_LENS_DISTORTION_HORIZONTAL
void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float value)
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3])
void GPU_shader_unbind()
void GPU_texture_extend_mode(GPUTexture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER
void GPU_texture_filter_mode(GPUTexture *texture, bool use_filter)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_FACTOR
Definition RNA_types.hh:239
@ UI_ITEM_R_SPLIT_EMPTY_NAME
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static unsigned long seed
Definition btSoftBody.h:39
GPUShader * get_shader(const char *info_name, ResultPrecision precision)
NodeOperation(Context &context, DNode node)
Result & get_result(StringRef identifier)
Definition operation.cc:39
Result & get_input(StringRef identifier) const
Definition operation.cc:138
virtual Domain compute_domain()
Definition operation.cc:56
void share_data(const Result &source)
Definition result.cc:401
T get_single_value_default(const T &default_value) const
void allocate_texture(Domain domain, bool from_pool=true)
Definition result.cc:309
void unbind_as_texture() const
Definition result.cc:389
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:365
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
Definition result.cc:376
void unbind_as_image() const
Definition result.cc:395
#define input
#define output
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2864
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
void node_set_socket_availability(bNodeTree &ntree, bNodeSocket &sock, bool is_available)
Definition node.cc:5011
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:5603
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:170
void parallel_for(const int2 range, const Function &function)
T sqrt(const T &a)
T distance(const T &a, const T &b)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T max(const T &a, const T &b)
static void node_composit_buts_lensdist(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_update(bNodeTree *ntree, bNode *node)
static int3 compute_number_of_integration_steps(const float3 &chromatic_distortion, const int2 &size, const float2 &uv, const float distance_squared, const bool use_jitter)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static float2 compute_distorted_uv(const float2 &uv, const float uv_scale, const int2 &size)
static float3 integrate_distortion(const int2 &texel, const Result &input, const int2 &size, const float3 &chromatic_distortion, const int start, const int end, const float distance_squared, const float2 &uv, const int steps, const bool use_jitter)
static float get_jitter(const int2 &texel, const int seed, const bool use_jitter)
static int compute_number_of_integration_steps_heuristic(const float distortion, const bool use_jitter)
static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b)
static float compute_distortion_scale(const float distortion, const float distance_squared)
static void node_composit_init_lensdist(bNodeTree *, bNode *node)
static float3 compute_chromatic_distortion_scale(const float3 &chromatic_distortion, const float distance_squared)
static void radial_lens_distortion(const int2 texel, const Result &input, Result &output, const int2 &size, const float3 &chromatic_distortion, const float scale, const bool use_jitter)
float hash_to_float(uint32_t kx)
Definition noise.cc:171
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
#define DISTORTION_SCALE
#define HORIZONTAL_DISPERSION_SCALE
#define MINIMUM_DISTORTION
#define RADIAL_DISPERSION_SCALE
static void register_node_type_cmp_lensdist()
void cmp_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
static const int steps
#define min(a, b)
Definition sort.cc:36
void * storage
Defines a node type.
Definition BKE_node.hh:226
std::string ui_description
Definition BKE_node.hh:232
NodeGetCompositorOperationFunction get_compositor_operation
Definition BKE_node.hh:336
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:355
void(* updatefunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:269
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)
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
PointerRNA * ptr
Definition wm_files.cc:4227