Nodes: add Bit Math node
This adds a new Bit Math node which supports the following operations: `and`, `or`, `xor`, `not`, `shift` and `rotate`. For the `shift` and `rotate` operations, a posititive shift is a left shift and a negative shift is a right shift. Currently, the node always works on 32-bit integers which is what Geometry Nodes uses internally for integers. If required, this can be extended to work on other bit widths in the future. The need for this came up every now and then. It can be useful when encoding specific bits in integer attributes (for efficiency or because the geometry is exported to other software that expects a certain format). Also, this node is useful for some people doing crazy but fun things with Geometry Nodes like emulating hardware. Even if the use-cases are not common, if they arise, it's hard to work around and the cost of having this node is quite low for us. Co-authored-by: Charlie Jolly <charliejolly@noreply.localhost> Pull Request: https://projects.blender.org/blender/blender/pulls/138290
This commit is contained in:
committed by
Jacques Lucke
parent
81275eff60
commit
39c2f01b51
@@ -729,6 +729,8 @@ class NODE_MT_category_GEO_UTILITIES_MATH(Menu):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
node_add_menu.add_node_type_with_searchable_enum(
|
||||
context, layout, "FunctionNodeBitMath", "operation", search_weight=-1.0)
|
||||
node_add_menu.add_node_type_with_searchable_enum(context, layout, "FunctionNodeBooleanMath", "operation")
|
||||
node_add_menu.add_node_type_with_searchable_enum(context, layout, "FunctionNodeIntegerMath", "operation")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeClamp")
|
||||
|
||||
@@ -13829,6 +13829,7 @@ static void rna_def_nodes(BlenderRNA *brna)
|
||||
define("FunctionNode", "FunctionNodeAlignRotationToVector");
|
||||
define("FunctionNode", "FunctionNodeAxesToRotation");
|
||||
define("FunctionNode", "FunctionNodeAxisAngleToRotation");
|
||||
define("FunctionNode", "FunctionNodeBitMath");
|
||||
define("FunctionNode", "FunctionNodeBooleanMath");
|
||||
define("FunctionNode", "FunctionNodeCombineColor");
|
||||
define("FunctionNode", "FunctionNodeCombineMatrix");
|
||||
|
||||
@@ -18,6 +18,7 @@ set(SRC
|
||||
nodes/node_fn_align_rotation_to_vector.cc
|
||||
nodes/node_fn_axes_to_rotation.cc
|
||||
nodes/node_fn_axis_angle_to_rotation.cc
|
||||
nodes/node_fn_bit_math.cc
|
||||
nodes/node_fn_boolean_math.cc
|
||||
nodes/node_fn_combine_color.cc
|
||||
nodes/node_fn_combine_matrix.cc
|
||||
|
||||
217
source/blender/nodes/function/nodes/node_fn_bit_math.cc
Normal file
217
source/blender/nodes/function/nodes/node_fn_bit_math.cc
Normal file
@@ -0,0 +1,217 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
|
||||
#include "NOD_rna_define.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
#include "node_function_util.hh"
|
||||
|
||||
static_assert(-1 == ~0, "Two's complement must be used for bitwise operations.");
|
||||
|
||||
namespace blender::nodes::node_fn_bit_math_cc {
|
||||
|
||||
enum BitMathOperation : int16_t {
|
||||
And = 0,
|
||||
Or = 1,
|
||||
Xor = 2,
|
||||
Not = 3,
|
||||
Shift = 4,
|
||||
Rotate = 5,
|
||||
};
|
||||
|
||||
const std::array<EnumPropertyItem, 7> bit_math_operation_items = {{
|
||||
{BitMathOperation::And,
|
||||
"AND",
|
||||
0,
|
||||
"And",
|
||||
"Returns a value where the bits of A and B are both set"},
|
||||
{BitMathOperation::Or,
|
||||
"OR",
|
||||
0,
|
||||
"Or",
|
||||
"Returns a value where the bits of either A or B are set"},
|
||||
{BitMathOperation::Xor,
|
||||
"XOR",
|
||||
0,
|
||||
"Exclusive Or",
|
||||
"Returns a value where only one bit from A and B is set"},
|
||||
{BitMathOperation::Not,
|
||||
"NOT",
|
||||
0,
|
||||
"Not",
|
||||
"Returns the opposite bit value of A, in decimal it is equivalent of A = -A - 1"},
|
||||
{BitMathOperation::Shift,
|
||||
"SHIFT",
|
||||
0,
|
||||
"Shift",
|
||||
"Shifts the bit values of A by the specified Shift amount. Positive values shift left, "
|
||||
"negative values shift right."},
|
||||
{BitMathOperation::Rotate,
|
||||
"ROTATE",
|
||||
0,
|
||||
"Rotate",
|
||||
"Rotates the bit values of A by the specified Shift amount. Positive values rotate left, "
|
||||
"negative values rotate right."},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
}};
|
||||
|
||||
constexpr static int32_t max_shift = sizeof(int32_t) * CHAR_BIT - 1;
|
||||
constexpr static int32_t min_shift = -max_shift;
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.is_function_node();
|
||||
b.add_input<decl::Int>("A");
|
||||
auto &b_socket = b.add_input<decl::Int>("B");
|
||||
auto &shift = b.add_input<decl::Int>("Shift").min(min_shift).max(max_shift);
|
||||
b.add_output<decl::Int>("Value");
|
||||
|
||||
if (const bNode *node = b.node_or_null()) {
|
||||
const BitMathOperation operation = BitMathOperation(node->custom1);
|
||||
b_socket.available(!ELEM(
|
||||
operation, BitMathOperation::Not, BitMathOperation::Shift, BitMathOperation::Rotate));
|
||||
shift.available(ELEM(operation, BitMathOperation::Shift, BitMathOperation::Rotate));
|
||||
}
|
||||
};
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout->prop(ptr, "operation", UI_ITEM_NONE, "", ICON_NONE);
|
||||
}
|
||||
|
||||
class SocketSearchOp {
|
||||
public:
|
||||
std::string socket_name;
|
||||
BitMathOperation operation;
|
||||
void operator()(LinkSearchOpParams ¶ms)
|
||||
{
|
||||
bNode &node = params.add_node("FunctionNodeBitMath");
|
||||
node.custom1 = int16_t(operation);
|
||||
params.update_and_connect_available_socket(node, socket_name);
|
||||
}
|
||||
};
|
||||
|
||||
static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
if (!params.node_tree().typeinfo->validate_link(eNodeSocketDatatype(params.other_socket().type),
|
||||
SOCK_INT))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_integer = params.other_socket().type == SOCK_INT;
|
||||
const int weight = is_integer ? 0 : -1;
|
||||
|
||||
for (const auto &item : bit_math_operation_items) {
|
||||
if (item.name != nullptr && item.identifier[0] != '\0') {
|
||||
params.add_item(
|
||||
IFACE_(item.name), SocketSearchOp{"A", BitMathOperation(item.value)}, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void node_label(const bNodeTree * /*ntree*/, const bNode *node, char *label, int maxlen)
|
||||
{
|
||||
char name[64] = {0};
|
||||
const char *operation_name = IFACE_("Unknown");
|
||||
RNA_enum_name(bit_math_operation_items.data(), node->custom1, &operation_name);
|
||||
SNPRINTF(name, IFACE_("Bitwise %s"), operation_name);
|
||||
BLI_strncpy(label, name, maxlen);
|
||||
}
|
||||
|
||||
static const mf::MultiFunction *get_multi_function(const bNode &bnode)
|
||||
{
|
||||
const BitMathOperation operation = BitMathOperation(bnode.custom1);
|
||||
static auto exec_preset = mf::build::exec_presets::AllSpanOrSingle();
|
||||
static auto and_fn = mf::build::SI2_SO<int, int, int>(
|
||||
"And", [](int a, int b) { return a & b; }, exec_preset);
|
||||
static auto or_fn = mf::build::SI2_SO<int, int, int>(
|
||||
"Or", [](int a, int b) { return a | b; }, exec_preset);
|
||||
static auto xor_fn = mf::build::SI2_SO<int, int, int>(
|
||||
"Xor", [](int a, int b) { return a ^ b; }, exec_preset);
|
||||
static auto not_fn = mf::build::SI1_SO<int, int>(
|
||||
"Not", [](int a) { return ~a; }, exec_preset);
|
||||
static auto shift_fn = mf::build::SI2_SO<int, int, int>(
|
||||
"Shift",
|
||||
[](int a, int b) {
|
||||
const uint32_t value = a;
|
||||
const int shift = math::clamp(b, -32, 32);
|
||||
const uint64_t wide_value = uint64_t(value) << 16;
|
||||
const uint64_t wide_result = shift > 0 ? wide_value << shift : wide_value >> -shift;
|
||||
return uint32_t(wide_result >> 16);
|
||||
},
|
||||
exec_preset);
|
||||
static auto rotate_fn = mf::build::SI2_SO<int, int, int>(
|
||||
"Rotate",
|
||||
[](int a, int b) {
|
||||
const uint32_t value = a;
|
||||
const int shift = math::mod_periodic(b, 32);
|
||||
const uint64_t wide_value = uint64_t(value) | (uint64_t(value) << 32);
|
||||
const uint64_t double_result = (wide_value << shift);
|
||||
return uint32_t((double_result | (double_result >> 32)) & ((uint64_t(1) << 33) - 1));
|
||||
},
|
||||
exec_preset);
|
||||
|
||||
switch (operation) {
|
||||
case BitMathOperation::And:
|
||||
return &and_fn;
|
||||
case BitMathOperation::Or:
|
||||
return &or_fn;
|
||||
case BitMathOperation::Xor:
|
||||
return &xor_fn;
|
||||
case BitMathOperation::Not:
|
||||
return ¬_fn;
|
||||
case BitMathOperation::Shift:
|
||||
return &shift_fn;
|
||||
case BitMathOperation::Rotate:
|
||||
return &rotate_fn;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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_rna(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop = RNA_def_node_enum(srna,
|
||||
"operation",
|
||||
"Operation",
|
||||
"",
|
||||
bit_math_operation_items.data(),
|
||||
NOD_inline_enum_accessors(custom1),
|
||||
BitMathOperation::And);
|
||||
RNA_def_property_update_runtime(prop, rna_Node_socket_update);
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static blender::bke::bNodeType ntype;
|
||||
|
||||
fn_node_type_base(&ntype, "FunctionNodeBitMath");
|
||||
ntype.ui_name = "Bit Math";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.declare = node_declare;
|
||||
ntype.labelfunc = node_label;
|
||||
ntype.build_multi_function = node_build_multi_function;
|
||||
ntype.draw_buttons = node_layout;
|
||||
ntype.gather_link_search_ops = node_gather_link_searches;
|
||||
ntype.ui_description = "Perform bitwise operations on 32-bit integers";
|
||||
|
||||
blender::bke::node_register_type(ntype);
|
||||
node_rna(ntype.rna_ext.srna);
|
||||
}
|
||||
NOD_REGISTER_NODE(node_register)
|
||||
|
||||
} // namespace blender::nodes::node_fn_bit_math_cc
|
||||
Reference in New Issue
Block a user