diff options
author | Hans Goudey <h.goudey@me.com> | 2021-02-12 22:25:45 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-02-12 22:25:45 +0300 |
commit | d4fd06d6ce89113310bb769f4777c9a4d77cb155 (patch) | |
tree | f94c3e05a65bca55a0e54f4c1fb72d9e23734e17 /source/blender/nodes | |
parent | d7c2c889a688067dab280fc137ad4c3c7ce4cb2b (diff) |
Geometry Nodes: Add operation setting to attribute randomize node
This commit adds a drop-down to the attribute randomize node to support
a few operations on the values of existing attributes: "Replace/Create"
(the existing behavior), "Add", "Subtract", and "Multiply".
At this point, the operations are limited by what is simple to implement.
More could be added in the future, but there isn't a strong use case
for more complex operations anyway, and a second math node can be used.
Differential Revision: https://developer.blender.org/D10269
Diffstat (limited to 'source/blender/nodes')
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc | 200 |
1 files changed, 136 insertions, 64 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index 7e95714a44b..dc924ede3a1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -32,6 +32,8 @@ static bNodeSocketTemplate geo_node_attribute_randomize_in[] = { {SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, + {SOCK_INT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -100000, 100000}, + {SOCK_INT, N_("Max"), 100.0f, 0.0f, 0.0f, 0.0f, -100000, 100000}, {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000}, {-1, ""}, }; @@ -46,11 +48,17 @@ static void geo_node_attribute_random_layout(uiLayout *layout, PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node) { - node->custom1 = CD_PROP_FLOAT; + NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN( + sizeof(NodeAttributeRandomize), __func__); + data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_POINT; + data->operation = GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE; + node->storage = data; } static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node) @@ -59,71 +67,97 @@ static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode bNodeSocket *sock_max_vector = sock_min_vector->next; bNodeSocket *sock_min_float = sock_max_vector->next; bNodeSocket *sock_max_float = sock_min_float->next; + bNodeSocket *sock_min_int = sock_max_float->next; + bNodeSocket *sock_max_int = sock_min_int->next; - const CustomDataType data_type = static_cast<CustomDataType>(node->custom1); + const NodeAttributeRandomize &storage = *(const NodeAttributeRandomize *)node->storage; + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3); nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3); nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT); nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32); } namespace blender::nodes { -/** Rehash to combine the seed with the "id" hash and a mutator for each dimension. */ -static float noise_from_index_and_mutator(const int seed, const int hash, const int mutator) +template<typename T> T get_random_value(const uint32_t id, const uint32_t seed); + +template<> inline bool get_random_value(const uint32_t id, const uint32_t seed) { - const int combined_hash = BLI_hash_int_3d(seed, hash, mutator); - return BLI_hash_int_01(combined_hash); + return BLI_hash_int_2d_to_float(id, seed) > 0.5f; } -/** Rehash to combine the seed with the "id" hash. */ -static float noise_from_index(const int seed, const int hash) +template<> inline int get_random_value(const uint32_t id, const uint32_t seed) { - const int combined_hash = BLI_hash_int_2d(seed, hash); - return BLI_hash_int_01(combined_hash); + return BLI_hash_int_2d(id, seed); } -static void randomize_attribute_bool(BooleanWriteAttribute attribute, - Span<uint32_t> hashes, - const int seed) +template<> inline float get_random_value(const uint32_t id, const uint32_t seed) { - MutableSpan<bool> attribute_span = attribute.get_span(); - for (const int i : IndexRange(attribute.size())) { - const bool value = noise_from_index(seed, (int)hashes[i]) > 0.5f; - attribute_span[i] = value; - } - attribute.apply_span(); + return BLI_hash_int_2d_to_float(id, seed); } -static void randomize_attribute_float(FloatWriteAttribute attribute, - const float min, - const float max, - Span<uint32_t> hashes, - const int seed) +template<> inline float3 get_random_value(const uint32_t id, const uint32_t seed) { - MutableSpan<float> attribute_span = attribute.get_span(); - for (const int i : IndexRange(attribute.size())) { - const float value = noise_from_index(seed, (int)hashes[i]) * (max - min) + min; - attribute_span[i] = value; + const float x = BLI_hash_int_3d_to_float(seed, id, 435109); + const float y = BLI_hash_int_3d_to_float(seed, id, 380867); + const float z = BLI_hash_int_3d_to_float(seed, id, 1059217); + + return float3(x, y, z); +} + +template<typename T> +static void randomize_attribute(MutableSpan<T> span, + const T min, + const T max, + Span<uint32_t> ids, + const uint32_t seed, + const GeometryNodeAttributeRandomizeMode operation) +{ + /* The operations could be templated too, but it doesn't make the code much shorter. */ + switch (operation) { + case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE: + for (const int i : span.index_range()) { + const T random_value = get_random_value<T>(ids[i], seed) * (max - min) + min; + span[i] = random_value; + } + break; + case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD: + for (const int i : span.index_range()) { + const T random_value = get_random_value<T>(ids[i], seed) * (max - min) + min; + span[i] = span[i] + random_value; + } + break; + case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT: + for (const int i : span.index_range()) { + const T random_value = get_random_value<T>(ids[i], seed) * (max - min) + min; + span[i] = span[i] - random_value; + } + break; + case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY: + for (const int i : span.index_range()) { + const T random_value = get_random_value<T>(ids[i], seed) * (max - min) + min; + span[i] = span[i] * random_value; + } + break; + default: + BLI_assert(false); + break; } - attribute.apply_span(); } -static void randomize_attribute_float3(Float3WriteAttribute attribute, - const float3 min, - const float3 max, - Span<uint32_t> hashes, - const int seed) +static void randomize_attribute_bool(MutableSpan<bool> span, + Span<uint32_t> ids, + const uint32_t seed, + const GeometryNodeAttributeRandomizeMode operation) { - MutableSpan<float3> attribute_span = attribute.get_span(); - for (const int i : IndexRange(attribute.size())) { - const float x = noise_from_index_and_mutator(seed, (int)hashes[i], 47); - const float y = noise_from_index_and_mutator(seed, (int)hashes[i], 8); - const float z = noise_from_index_and_mutator(seed, (int)hashes[i], 64); - const float3 value = float3(x, y, z) * (max - min) + min; - attribute_span[i] = value; + BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE); + for (const int i : span.index_range()) { + const bool random_value = get_random_value<bool>(ids[i], seed); + span[i] = random_value; } - attribute.apply_span(); } Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, @@ -168,16 +202,21 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, return static_cast<AttributeDomain>(node.custom2); } -static void randomize_attribute(GeometryComponent &component, - const GeoNodeExecParams ¶ms, - const int seed) +static void randomize_attribute_on_component(GeometryComponent &component, + const GeoNodeExecParams ¶ms, + StringRef attribute_name, + const CustomDataType data_type, + const GeometryNodeAttributeRandomizeMode operation, + const int seed) { - const std::string attribute_name = params.get_input<std::string>("Attribute"); - if (attribute_name.empty()) { - return; + /* If the node is not in "replace / create" mode and the attribute + * doesn't already exist, don't do the operation. */ + if (operation != GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) { + if (!component.attribute_exists(attribute_name)) { + return; + } } - const bNode &node = params.node(); - const CustomDataType data_type = static_cast<CustomDataType>(node.custom1); + const AttributeDomain domain = get_result_domain(component, params, attribute_name); OutputAttributePtr attribute = component.attribute_try_get_for_output( @@ -186,44 +225,75 @@ static void randomize_attribute(GeometryComponent &component, return; } + fn::GMutableSpan span = (operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) ? + attribute->get_span_for_write_only() : + attribute->get_span(); + Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain); switch (data_type) { - case CD_PROP_FLOAT: { - const float min_value = params.get_input<float>("Min_001"); - const float max_value = params.get_input<float>("Max_001"); - randomize_attribute_float(*attribute, min_value, max_value, hashes, seed); + case CD_PROP_FLOAT3: { + const float3 min = params.get_input<float3>("Min"); + const float3 max = params.get_input<float3>("Max"); + randomize_attribute<float3>(span.typed<float3>(), min, max, hashes, seed, operation); break; } - case CD_PROP_FLOAT3: { - const float3 min_value = params.get_input<float3>("Min"); - const float3 max_value = params.get_input<float3>("Max"); - randomize_attribute_float3(*attribute, min_value, max_value, hashes, seed); + case CD_PROP_FLOAT: { + const float min = params.get_input<float>("Min_001"); + const float max = params.get_input<float>("Max_001"); + randomize_attribute<float>(span.typed<float>(), min, max, hashes, seed, operation); break; } case CD_PROP_BOOL: { - randomize_attribute_bool(*attribute, hashes, seed); + randomize_attribute_bool(span.typed<bool>(), hashes, seed, operation); break; } - default: + case CD_PROP_INT32: { + const int min = params.get_input<int>("Min_002"); + const int max = params.get_input<int>("Max_002"); + randomize_attribute<int>(span.typed<int>(), min, max, hashes, seed, operation); + break; + } + default: { + BLI_assert(false); break; + } } - attribute.save(); -} + attribute.apply_span_and_save(); +} // namespace blender::nodes static void geo_node_random_attribute_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + const std::string attribute_name = params.get_input<std::string>("Attribute"); + if (attribute_name.empty()) { + params.set_output("Geometry", geometry_set); + return; + } const int seed = params.get_input<int>("Seed"); + const NodeAttributeRandomize &storage = *(const NodeAttributeRandomize *)params.node().storage; + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + const GeometryNodeAttributeRandomizeMode operation = + static_cast<GeometryNodeAttributeRandomizeMode>(storage.operation); geometry_set = geometry_set_realize_instances(geometry_set); if (geometry_set.has<MeshComponent>()) { - randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, seed); + randomize_attribute_on_component(geometry_set.get_component_for_write<MeshComponent>(), + params, + attribute_name, + data_type, + operation, + seed); } if (geometry_set.has<PointCloudComponent>()) { - randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, seed); + randomize_attribute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), + params, + attribute_name, + data_type, + operation, + seed); } params.set_output("Geometry", geometry_set); @@ -243,5 +313,7 @@ void register_node_type_geo_attribute_randomize() node_type_update(&ntype, geo_node_attribute_randomize_update); ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec; ntype.draw_buttons = geo_node_attribute_random_layout; + node_type_storage( + &ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); } |