Blender V5.0
node_shader_radial_tiling.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024-2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "node_shader_util.hh"
6#include "node_util.hh"
7
9
10#include "RNA_access.hh"
11
13#include "UI_resources.hh"
14
15#include "BLI_radial_tiling.hh"
16
18
20
22{
23 b.is_function_node();
24
25 b.add_output<decl::Vector>("Segment Coordinates")
26 .no_muted_links()
27 .description("Segment coordinates for texture mapping within each angular segment");
28 b.add_output<decl::Float>("Segment ID")
29 .no_muted_links()
30 .description(
31 "Unique ID for every angular segment starting at 0 and increasing counterclockwise by "
32 "1");
33 b.add_output<decl::Float>("Segment Width")
34 .no_muted_links()
35 .description(
36 "Relative width of each angular segment. "
37 "May be used to scale textures to fit into each segment");
38 b.add_output<decl::Float>("Segment Rotation")
39 .no_muted_links()
40 .description(
41 "Counterclockwise rotation of each segment coordinates system. May be used to align the "
42 "rotation of the textures of each segment");
43
44 b.add_input<decl::Vector>("Vector")
45 .dimensions(2)
46 .default_value(float3{0.0f, 0.0f, 0.0f})
47 .description("Input texture coordinates");
48 b.add_input<decl::Float>("Sides").min(2.0f).max(1000.0f).default_value(5.0f).description(
49 "Number of angular segments for tiling. A non-integer value results in an irregular "
50 "segment");
51 b.add_input<decl::Float>("Roundness")
52 .min(0.0f)
53 .max(1.0f)
54 .default_value(0.0f)
55 .subtype(PROP_FACTOR)
56 .description("Roundness of the segment coordinates systems");
57}
58
60{
61 layout->prop(ptr, "normalize", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
62}
63
64static void node_shader_init_radial_tiling(bNodeTree * /*ntree*/, bNode *node)
65{
67 storage->normalize = false;
68
69 node->storage = storage;
70}
71
72static const char *gpu_shader_get_name()
73{
74 return "node_radial_tiling";
75}
76
78 bNode *node,
79 bNodeExecData * /*execdata*/,
82{
83 const NodeRadialTiling &storage = node_storage(*node);
84 float normalize_r_gon_parameter = storage.normalize;
85 float calculate_r_gon_parameter_field = out[0].hasoutput;
86 float calculate_segment_id = out[1].hasoutput;
87 float calculate_max_unit_parameter = out[2].hasoutput;
88 float calculate_x_axis_A_angle_bisector = out[3].hasoutput;
89
90 const char *name = gpu_shader_get_name();
91
92 return GPU_stack_link(mat,
93 node,
94 name,
95 in,
96 out,
97 GPU_constant(&normalize_r_gon_parameter),
98 GPU_constant(&calculate_r_gon_parameter_field),
99 GPU_constant(&calculate_segment_id),
100 GPU_constant(&calculate_max_unit_parameter),
101 GPU_constant(&calculate_x_axis_A_angle_bisector));
102}
103
104class RoundedPolygonFunction : public mf::MultiFunction {
105 private:
106 bool normalize_r_gon_parameter_;
107
108 mf::Signature signature_;
109
110 public:
111 RoundedPolygonFunction(bool normalize_r_gon_parameter)
112 : normalize_r_gon_parameter_(normalize_r_gon_parameter)
113 {
114 signature_ = create_signature();
115 this->set_signature(&signature_);
116 }
117
118 static mf::Signature create_signature()
119 {
120 mf::Signature signature;
121 mf::SignatureBuilder builder{"radial_tiling", signature};
122
123 builder.single_input<float3>("Vector");
124
125 builder.single_input<float>("Sides");
126 builder.single_input<float>("Roundness");
127
128 builder.single_output<float3>("Segment Coordinates", mf::ParamFlag::SupportsUnusedOutput);
129 builder.single_output<float>("Segment ID", mf::ParamFlag::SupportsUnusedOutput);
130 builder.single_output<float>("Segment Width", mf::ParamFlag::SupportsUnusedOutput);
131 builder.single_output<float>("Segment Rotation", mf::ParamFlag::SupportsUnusedOutput);
132
133 return signature;
134 }
135
136 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
137 {
138 int param = 0;
139
140 const VArray<float3> &coord = params.readonly_single_input<float3>(param++, "Vector");
141
142 const VArray<float> &r_gon_sides = params.readonly_single_input<float>(param++, "Sides");
143 const VArray<float> &r_gon_roundness = params.readonly_single_input<float>(param++,
144 "Roundness");
145
146 MutableSpan<float3> r_segment_coordinates =
147 params.uninitialized_single_output_if_required<float3>(param++, "Segment Coordinates");
148 MutableSpan<float> r_segment_id = params.uninitialized_single_output_if_required<float>(
149 param++, "Segment ID");
150 MutableSpan<float> r_max_unit_parameter =
151 params.uninitialized_single_output_if_required<float>(param++, "Segment Width");
152 MutableSpan<float> r_x_axis_A_angle_bisector =
153 params.uninitialized_single_output_if_required<float>(param++, "Segment Rotation");
154
155 const bool calculate_r_gon_parameter_field = !r_segment_coordinates.is_empty();
156 const bool calculate_segment_id = !r_segment_id.is_empty();
157 const bool calculate_max_unit_parameter = !r_max_unit_parameter.is_empty();
158 const bool calculate_x_axis_A_angle_bisector = !r_x_axis_A_angle_bisector.is_empty();
159
160 mask.foreach_index([&](const int64_t i) {
161 if (calculate_r_gon_parameter_field || calculate_max_unit_parameter ||
162 calculate_x_axis_A_angle_bisector)
163 {
164 float4 out_variables = calculate_out_variables(calculate_r_gon_parameter_field,
165 calculate_max_unit_parameter,
166 normalize_r_gon_parameter_,
167 math::max(r_gon_sides[i], 2.0f),
168 math::clamp(r_gon_roundness[i], 0.0f, 1.0f),
169 float2(coord[i].x, coord[i].y));
170
171 if (calculate_r_gon_parameter_field) {
172 r_segment_coordinates[i] = float3(out_variables.y, out_variables.x, 0.0f);
173 }
174 if (calculate_max_unit_parameter) {
175 r_max_unit_parameter[i] = out_variables.z;
176 }
177 if (calculate_x_axis_A_angle_bisector) {
178 r_x_axis_A_angle_bisector[i] = out_variables.w;
179 }
180 }
181
182 if (calculate_segment_id) {
183 r_segment_id[i] = calculate_out_segment_id(math::max(r_gon_sides[i], 2.0f),
184 float2(coord[i].x, coord[i].y));
185 }
186 });
187 }
188
190 {
191 ExecutionHints hints;
192 hints.allocates_array = false;
193 hints.min_grain_size = 50;
194 return hints;
195 }
196};
197
199{
200 const NodeRadialTiling &storage = node_storage(builder.node());
202}
203
204} // namespace blender::nodes::node_shader_radial_tiling_cc
205
207{
209
210 static blender::bke::bNodeType ntype;
211
212 sh_geo_node_type_base(&ntype, "ShaderNodeRadialTiling");
213 ntype.ui_name = "Radial Tiling";
214 ntype.ui_description = "Transform Coordinate System for Radial Tiling";
216 ntype.declare = file_ns::sh_node_radial_tiling_declare;
217 ntype.draw_buttons = file_ns::node_shader_buts_radial_tiling;
218 ntype.initfunc = file_ns::node_shader_init_radial_tiling;
220 ntype, "NodeRadialTiling", node_free_standard_storage, node_copy_standard_storage);
221 ntype.gpu_fn = file_ns::node_shader_gpu_radial_tiling;
222 ntype.build_multi_function = file_ns::sh_node_radial_tiling_build_multi_function;
223
225}
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_OP_VECTOR
Definition BKE_node.hh:450
bool GPU_stack_link(GPUMaterial *mat, const bNode *node, const char *name, GPUNodeStack *in, GPUNodeStack *out,...)
GPUNodeLink * GPU_constant(const float *num)
@ PROP_FACTOR
Definition RNA_types.hh:251
@ UI_ITEM_R_SPLIT_EMPTY_NAME
long long int int64_t
void set_signature(const Signature *signature)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
#define in
#define out
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
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:5414
T clamp(const T &a, const T &min, const T &max)
T max(const T &a, const T &b)
static void node_shader_init_radial_tiling(bNodeTree *, bNode *node)
static int node_shader_gpu_radial_tiling(GPUMaterial *mat, bNode *node, bNodeExecData *, GPUNodeStack *in, GPUNodeStack *out)
static void node_shader_buts_radial_tiling(uiLayout *layout, bContext *, PointerRNA *ptr)
static void sh_node_radial_tiling_build_multi_function(NodeMultiFunctionBuilder &builder)
static void sh_node_radial_tiling_declare(NodeDeclarationBuilder &b)
VecBase< float, 4 > float4
VecBase< float, 2 > float2
float calculate_out_segment_id(float r_gon_sides, float2 coord)
VecBase< float, 3 > float3
float4 calculate_out_variables(bool calculate_r_gon_parameter_field, bool calculate_max_unit_parameter, bool normalize_r_gon_parameter, float r_gon_sides, float r_gon_roundness, float2 coord)
void register_node_type_sh_radial_tiling()
void sh_geo_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
const char * name
void * storage
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeGPUExecFunction gpu_fn
Definition BKE_node.hh:342
NodeMultiFunctionBuildFunction build_multi_function
Definition BKE_node.hh:351
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
NodeDeclareFunction declare
Definition BKE_node.hh:362
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:4238