Blender  V2.93
node_geo_attribute_randomize.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #include "BLI_hash.h"
18 #include "BLI_rand.hh"
19 
20 #include "UI_interface.h"
21 #include "UI_resources.h"
22 
23 #include "node_geometry_util.hh"
24 
26  {SOCK_GEOMETRY, N_("Geometry")},
27  {SOCK_STRING, N_("Attribute")},
28  {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
29  {SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
30  {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
31  {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
32  {SOCK_INT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -100000, 100000},
33  {SOCK_INT, N_("Max"), 100.0f, 0.0f, 0.0f, 0.0f, -100000, 100000},
34  {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
35  {-1, ""},
36 };
37 
39  {SOCK_GEOMETRY, N_("Geometry")},
40  {-1, ""},
41 };
42 
44  bContext *UNUSED(C),
45  PointerRNA *ptr)
46 {
47  uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
48  uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
49 }
50 
52 {
54  sizeof(NodeAttributeRandomize), __func__);
55  data->data_type = CD_PROP_FLOAT;
56  data->domain = ATTR_DOMAIN_POINT;
58  node->storage = data;
59 }
60 
62 {
63  bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
64  bNodeSocket *sock_max_vector = sock_min_vector->next;
65  bNodeSocket *sock_min_float = sock_max_vector->next;
66  bNodeSocket *sock_max_float = sock_min_float->next;
67  bNodeSocket *sock_min_int = sock_max_float->next;
68  bNodeSocket *sock_max_int = sock_min_int->next;
69 
70  const NodeAttributeRandomize &storage = *(const NodeAttributeRandomize *)node->storage;
71  const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
72  nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
73  nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
74  nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
75  nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
76  nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32);
77  nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32);
78 }
79 
80 namespace blender::nodes {
81 
82 template<typename T>
83 T random_value_in_range(const uint32_t id, const uint32_t seed, const T min, const T max);
84 
85 template<>
86 inline float random_value_in_range(const uint32_t id,
87  const uint32_t seed,
88  const float min,
89  const float max)
90 {
91  return BLI_hash_int_2d_to_float(id, seed) * (max - min) + min;
92 }
93 
94 template<>
95 inline int random_value_in_range(const uint32_t id,
96  const uint32_t seed,
97  const int min,
98  const int max)
99 {
100  return round_fl_to_int(
101  random_value_in_range<float>(id, seed, static_cast<float>(min), static_cast<float>(max)));
102 }
103 
104 template<>
106  const uint32_t seed,
107  const float3 min,
108  const float3 max)
109 {
110  const float x = BLI_hash_int_3d_to_float(seed, id, 435109);
111  const float y = BLI_hash_int_3d_to_float(seed, id, 380867);
112  const float z = BLI_hash_int_3d_to_float(seed, id, 1059217);
113 
114  return float3(x, y, z) * (max - min) + min;
115 }
116 
117 template<typename T>
119  const T min,
120  const T max,
121  Span<uint32_t> ids,
122  const uint32_t seed,
123  const GeometryNodeAttributeRandomizeMode operation)
124 {
125  /* The operations could be templated too, but it doesn't make the code much shorter. */
126  switch (operation) {
128  for (const int i : span.index_range()) {
129  const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
130  span[i] = random_value;
131  }
132  break;
134  for (const int i : span.index_range()) {
135  const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
136  span[i] = span[i] + random_value;
137  }
138  break;
140  for (const int i : span.index_range()) {
141  const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
142  span[i] = span[i] - random_value;
143  }
144  break;
146  for (const int i : span.index_range()) {
147  const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
148  span[i] = span[i] * random_value;
149  }
150  break;
151  default:
152  BLI_assert(false);
153  break;
154  }
155 }
156 
158  Span<uint32_t> ids,
159  const uint32_t seed,
160  const GeometryNodeAttributeRandomizeMode operation)
161 {
163  UNUSED_VARS_NDEBUG(operation);
164  for (const int i : span.index_range()) {
165  const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
166  span[i] = random_value;
167  }
168 }
169 
171  const AttributeDomain domain)
172 {
173  const int domain_size = component.attribute_domain_size(domain);
174 
175  /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
176  ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain);
177  Array<uint32_t> hashes(domain_size);
178  if (hash_attribute) {
179  BLI_assert(hashes.size() == hash_attribute->size());
180  const CPPType &cpp_type = hash_attribute->cpp_type();
181  fn::GSpan items = hash_attribute->get_span();
182  for (const int i : hashes.index_range()) {
183  hashes[i] = cpp_type.hash(items[i]);
184  }
185  }
186  else {
187  /* If there is no "id" attribute for per-point variation, just create it here. */
188  RandomNumberGenerator rng(0);
189  for (const int i : hashes.index_range()) {
190  hashes[i] = rng.get_uint32();
191  }
192  }
193 
194  return hashes;
195 }
196 
198  const GeoNodeExecParams &params,
199  StringRef attribute_name)
200 {
201  /* Use the domain of the result attribute if it already exists. */
202  ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
203  if (result_attribute) {
204  return result_attribute->domain();
205  }
206 
207  /* Otherwise use the input domain chosen in the interface. */
208  const bNode &node = params.node();
209  return static_cast<AttributeDomain>(node.custom2);
210 }
211 
213  const GeoNodeExecParams &params,
214  StringRef attribute_name,
215  const CustomDataType data_type,
216  const GeometryNodeAttributeRandomizeMode operation,
217  const int seed)
218 {
219  /* If the node is not in "replace / create" mode and the attribute
220  * doesn't already exist, don't do the operation. */
222  if (!component.attribute_exists(attribute_name)) {
223  params.error_message_add(NodeWarningType::Error,
224  TIP_("No attribute with name \"") + attribute_name + "\"");
225  return;
226  }
227  }
228 
229  const AttributeDomain domain = get_result_domain(component, params, attribute_name);
230 
231  OutputAttributePtr attribute = component.attribute_try_get_for_output(
232  attribute_name, domain, data_type);
233  if (!attribute) {
234  return;
235  }
236 
238  attribute->get_span_for_write_only() :
239  attribute->get_span();
240 
242 
243  switch (data_type) {
244  case CD_PROP_FLOAT3: {
245  const float3 min = params.get_input<float3>("Min");
246  const float3 max = params.get_input<float3>("Max");
247  randomize_attribute<float3>(span.typed<float3>(), min, max, hashes, seed, operation);
248  break;
249  }
250  case CD_PROP_FLOAT: {
251  const float min = params.get_input<float>("Min_001");
252  const float max = params.get_input<float>("Max_001");
253  randomize_attribute<float>(span.typed<float>(), min, max, hashes, seed, operation);
254  break;
255  }
256  case CD_PROP_BOOL: {
257  randomize_attribute_bool(span.typed<bool>(), hashes, seed, operation);
258  break;
259  }
260  case CD_PROP_INT32: {
261  const int min = params.get_input<int>("Min_002");
262  const int max = params.get_input<int>("Max_002");
263  randomize_attribute<int>(span.typed<int>(), min, max, hashes, seed, operation);
264  break;
265  }
266  default: {
267  BLI_assert(false);
268  break;
269  }
270  }
271 
272  attribute.apply_span_and_save();
273 } // namespace blender::nodes
274 
276 {
277  GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
278  const std::string attribute_name = params.get_input<std::string>("Attribute");
279  if (attribute_name.empty()) {
280  params.set_output("Geometry", geometry_set);
281  return;
282  }
283  const int seed = params.get_input<int>("Seed");
284  const NodeAttributeRandomize &storage = *(const NodeAttributeRandomize *)params.node().storage;
285  const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
286  const GeometryNodeAttributeRandomizeMode operation =
287  static_cast<GeometryNodeAttributeRandomizeMode>(storage.operation);
288 
289  geometry_set = geometry_set_realize_instances(geometry_set);
290 
291  if (geometry_set.has<MeshComponent>()) {
293  params,
294  attribute_name,
295  data_type,
296  operation,
297  seed);
298  }
299  if (geometry_set.has<PointCloudComponent>()) {
301  params,
302  attribute_name,
303  data_type,
304  operation,
305  seed);
306  }
307 
308  params.set_output("Geometry", geometry_set);
309 }
310 
311 } // namespace blender::nodes
312 
314 {
315  static bNodeType ntype;
316 
318  &ntype, GEO_NODE_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0);
326  &ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage);
327  nodeRegisterType(&ntype);
328 }
AttributeDomain
Definition: BKE_attribute.h:41
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:43
void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available)
Definition: node.cc:3726
void node_type_socket_templates(struct bNodeType *ntype, struct bNodeSocketTemplate *inputs, struct bNodeSocketTemplate *outputs)
Definition: node.cc:4527
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4623
#define GEO_NODE_ATTRIBUTE_RANDOMIZE
Definition: BKE_node.h:1384
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4559
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:4599
#define NODE_CLASS_ATTRIBUTE
Definition: BKE_node.h:360
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1298
#define BLI_assert(a)
Definition: BLI_assert.h:58
BLI_INLINE float BLI_hash_int_2d_to_float(uint32_t kx, uint32_t ky)
Definition: BLI_hash.h:93
BLI_INLINE float BLI_hash_int_3d_to_float(uint32_t kx, uint32_t ky, uint32_t kz)
Definition: BLI_hash.h:98
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int round_fl_to_int(float a)
#define UNUSED_VARS_NDEBUG(...)
#define UNUSED(x)
#define TIP_(msgid)
#define N_(msgid)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:126
CustomDataType
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_INT32
@ CD_PROP_BOOL
GeometryNodeAttributeRandomizeMode
@ GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY
@ GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT
@ GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE
@ GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_FLOAT
@ SOCK_GEOMETRY
@ SOCK_STRING
_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 const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble z
_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 const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
#define C
Definition: RandGen.cpp:39
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
static unsigned long seed
Definition: btSoftBody.h:39
int64_t size() const
Definition: BLI_array.hh:258
IndexRange index_range() const
Definition: BLI_array.hh:345
constexpr IndexRange index_range() const
Definition: BLI_span.hh:659
fn::GMutableSpan get_span_for_write_only()
uint64_t hash(const void *value) const
Definition: FN_cpp_type.hh:647
MutableSpan< T > typed() const
OperationNode * node
void * tree
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
#define T
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
std::unique_ptr< ReadAttribute > ReadAttributePtr
static void randomize_attribute(MutableSpan< T > span, const T min, const T max, Span< uint32_t > ids, const uint32_t seed, const GeometryNodeAttributeRandomizeMode operation)
static void randomize_attribute_on_component(GeometryComponent &component, const GeoNodeExecParams &params, StringRef attribute_name, const CustomDataType data_type, const GeometryNodeAttributeRandomizeMode operation, const int seed)
T random_value_in_range(const uint32_t id, const uint32_t seed, const T min, const T max)
static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef source_name, StringRef result_name)
static void randomize_attribute_bool(MutableSpan< bool > span, Span< uint32_t > ids, const uint32_t seed, const GeometryNodeAttributeRandomizeMode operation)
static void geo_node_random_attribute_exec(GeoNodeExecParams params)
Array< uint32_t > get_geometry_element_ids_as_uints(const GeometryComponent &component, const AttributeDomain domain)
static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node)
void register_node_type_geo_attribute_randomize()
static bNodeSocketTemplate geo_node_attribute_randomize_in[]
static void geo_node_attribute_random_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node)
static bNodeSocketTemplate geo_node_attribute_randomize_out[]
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
void node_copy_standard_storage(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
Definition: node_util.c:67
void node_free_standard_storage(bNode *node)
Definition: node_util.c:55
#define min(a, b)
Definition: sort.c:51
unsigned int uint32_t
Definition: stdint.h:83
GeometryComponent & get_component_for_write(GeometryComponentType component_type)
bool has(const GeometryComponentType component_type) const
Compact definition of a node socket.
Definition: BKE_node.h:95
struct bNodeSocket * next
Defines a node type.
Definition: BKE_node.h:221
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:327
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:253
float max
PointerRNA * ptr
Definition: wm_files.c:3157