Geometry Nodes: new Hash Value node

This node hashes various types into an integer. Note that hashes
cannot generally used as unique identifiers because they are not
guaranteed to be unique. It can be used to generate somewhat
stable randomness though in cases where White Noise does not
offer enough flexibility.

It uses hash functions from BLI_noise.hh. These are also used in
the White Noise node.

Pull Request: https://projects.blender.org/blender/blender/pulls/110769
This commit is contained in:
Charlie Jolly
2024-08-30 16:42:29 +02:00
committed by Jacques Lucke
parent b4287a4902
commit 294994e4b9
7 changed files with 204 additions and 0 deletions
@@ -674,6 +674,7 @@ class NODE_MT_category_GEO_UTILITIES_MATH(Menu):
node_add_menu.add_node_type(layout, "FunctionNodeCompare")
node_add_menu.add_node_type(layout, "ShaderNodeFloatCurve")
node_add_menu.add_node_type(layout, "FunctionNodeFloatToInt")
node_add_menu.add_node_type(layout, "FunctionNodeHashValue")
node_add_menu.add_node_type(layout, "ShaderNodeMapRange")
node_add_menu.add_node_type(layout, "ShaderNodeMath")
node_add_menu.add_node_type(layout, "ShaderNodeMix")
+1
View File
@@ -1424,6 +1424,7 @@ void node_tree_remove_layer_n(bNodeTree *ntree, Scene *scene, int layer_index);
#define FN_NODE_SEPARATE_MATRIX 1242
#define FN_NODE_INPUT_ROTATION 1243
#define FN_NODE_AXES_TO_ROTATION 1244
#define FN_NODE_HASH_VALUE 1245
/** \} */
+2
View File
@@ -4,6 +4,7 @@
#pragma once
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
namespace blender::noise {
@@ -28,6 +29,7 @@ uint32_t hash_float(float kx);
uint32_t hash_float(float2 k);
uint32_t hash_float(float3 k);
uint32_t hash_float(float4 k);
uint32_t hash_float(const float4x4 &k);
/* Hash integers to `float` between 0 and 1. */
+6
View File
@@ -10,6 +10,7 @@
#include "BLI_math_base.hh"
#include "BLI_math_base_safe.h"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_numbers.hh"
#include "BLI_math_vector.hh"
#include "BLI_noise.hh"
@@ -150,6 +151,11 @@ uint32_t hash_float(float4 k)
return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w));
}
uint32_t hash_float(const float4x4 &k)
{
return hash(hash_float(k.x), hash_float(k.y), hash_float(k.z), hash_float(k.w));
}
/* Hashing a number of uint32_t into a float in the range [0, 1]. */
BLI_INLINE float uint_to_float_01(uint32_t k)
+1
View File
@@ -277,6 +277,7 @@ DefNode(FunctionNode, FN_NODE_COMBINE_TRANSFORM, 0, "COMBINE_TRANSFORM", Combine
DefNode(FunctionNode, FN_NODE_COMPARE, 0, "COMPARE", Compare, "Compare", "")
DefNode(FunctionNode, FN_NODE_EULER_TO_ROTATION, 0, "EULER_TO_ROTATION", EulerToRotation, "Euler to Rotation", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
DefNode(FunctionNode, FN_NODE_HASH_VALUE, 0, "HASH_VALUE", HashValue, "Hash Value", "")
DefNode(FunctionNode, FN_NODE_INPUT_BOOL, def_fn_input_bool, "INPUT_BOOL", InputBool, "Boolean", "")
DefNode(FunctionNode, FN_NODE_INPUT_COLOR, def_fn_input_color, "INPUT_COLOR", InputColor, "Color", "")
DefNode(FunctionNode, FN_NODE_INPUT_INT, def_fn_input_int, "INPUT_INT", InputInt, "Integer", "")
@@ -29,6 +29,7 @@ set(SRC
nodes/node_fn_compare.cc
nodes/node_fn_euler_to_rotation.cc
nodes/node_fn_float_to_int.cc
nodes/node_fn_hash_value.cc
nodes/node_fn_input_bool.cc
nodes/node_fn_input_color.cc
nodes/node_fn_input_int.cc
@@ -0,0 +1,192 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_hash.h"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_quaternion.hh"
#include "BLI_noise.hh"
#include "NOD_rna_define.hh"
#include "NOD_socket.hh"
#include "NOD_socket_search_link.hh"
#include "RNA_enum_types.hh"
#include "node_function_util.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
namespace blender::nodes::node_fn_hash_value_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
const bNode *node = b.node_or_null();
if (node) {
const eNodeSocketDatatype data_type = eNodeSocketDatatype(node->custom1);
b.add_input(data_type, "Value");
}
b.add_input<decl::Int>("Seed", "Seed");
b.add_output<decl::Int>("Hash");
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
node->custom1 = SOCK_INT;
}
static const mf::MultiFunction *get_multi_function(const bNode &bnode)
{
const eNodeSocketDatatype socket_type = static_cast<eNodeSocketDatatype>(bnode.custom1);
static auto exec_preset = mf::build::exec_presets::AllSpanOrSingle();
static auto fn_hash_float = mf::build::SI2_SO<float, int, int>(
"Hash Float",
[](float a, int seed) { return noise::hash(noise::hash_float(a), seed); },
exec_preset);
static auto fn_hash_vector = mf::build::SI2_SO<float3, int, int>(
"Hash Vector",
[](float3 a, int seed) { return noise::hash(noise::hash_float(a), seed); },
exec_preset);
static auto fn_hash_color = mf::build::SI2_SO<ColorGeometry4f, int, int>(
"Hash Color",
[](ColorGeometry4f a, int seed) { return noise::hash(noise::hash_float(float4(a)), seed); },
exec_preset);
static auto fn_hash_int = mf::build::SI2_SO<int, int, int>(
"Hash Integer",
[](int a, int seed) { return noise::hash(noise::hash(a), seed); },
exec_preset);
static auto fn_hash_string = mf::build::SI2_SO<std::string, int, int>(
"Hash String",
[](std::string a, int seed) { return noise::hash(BLI_hash_string(a.c_str()), seed); },
exec_preset);
static auto fn_hash_rotation = mf::build::SI2_SO<math::Quaternion, int, int>(
"Hash Rotation",
[](math::Quaternion a, int seed) { return noise::hash(noise::hash_float(float4(a)), seed); },
exec_preset);
static auto fn_hash_matrix = mf::build::SI2_SO<float4x4, int, int>(
"Hash Matrix",
[](float4x4 a, int seed) { return noise::hash(noise::hash_float(a), seed); },
exec_preset);
switch (socket_type) {
case SOCK_MATRIX:
return &fn_hash_matrix;
case SOCK_ROTATION:
return &fn_hash_rotation;
case SOCK_STRING:
return &fn_hash_string;
case SOCK_FLOAT:
return &fn_hash_float;
case SOCK_VECTOR:
return &fn_hash_vector;
case SOCK_RGBA:
return &fn_hash_color;
case SOCK_INT:
return &fn_hash_int;
default:
BLI_assert_unreachable();
return nullptr;
}
}
class SocketSearchOp {
public:
const StringRef socket_name;
eNodeSocketDatatype socket_type;
void operator()(LinkSearchOpParams &params)
{
bNode &node = params.add_node("FunctionNodeHashValue");
node.custom1 = socket_type;
params.update_and_connect_available_socket(node, socket_name);
}
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
const mf::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
{
eNodeSocketDatatype socket_type = eNodeSocketDatatype(params.other_socket().type);
if (!ELEM(socket_type,
SOCK_BOOLEAN,
SOCK_FLOAT,
SOCK_INT,
SOCK_ROTATION,
SOCK_MATRIX,
SOCK_VECTOR,
SOCK_STRING,
SOCK_RGBA))
{
return;
}
if (params.in_out() == SOCK_IN) {
if (socket_type == SOCK_BOOLEAN) {
socket_type = SOCK_INT;
}
params.add_item(IFACE_("Value"), SocketSearchOp{"Value", socket_type});
params.add_item(IFACE_("Seed"), SocketSearchOp{"Seed", SOCK_INT});
}
else {
if (!ELEM(socket_type, SOCK_STRING)) {
const int weight = ELEM(params.other_socket().type, SOCK_INT) ? 0 : -1;
params.add_item(IFACE_("Hash"), SocketSearchOp{"Hash", SOCK_INT}, weight);
}
}
}
static void node_rna(StructRNA *srna)
{
RNA_def_node_enum(
srna,
"data_type",
"Data Type",
"",
rna_enum_node_socket_data_type_items,
NOD_inline_enum_accessors(custom1),
SOCK_INT,
[](bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free) {
*r_free = true;
return enum_items_filter(rna_enum_node_socket_data_type_items,
[](const EnumPropertyItem &item) -> bool {
return ELEM(item.value,
SOCK_FLOAT,
SOCK_INT,
SOCK_MATRIX,
SOCK_ROTATION,
SOCK_VECTOR,
SOCK_STRING,
SOCK_RGBA);
});
});
}
static void node_register()
{
static blender::bke::bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_HASH_VALUE, "Hash Value", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.initfunc = node_init;
ntype.build_multi_function = node_build_multi_function;
ntype.draw_buttons = node_layout;
ntype.gather_link_search_ops = node_gather_link_searches;
blender::bke::node_register_type(&ntype);
node_rna(ntype.rna_ext.srna);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_hash_value_cc