1
1

Compare commits

...

29 Commits

Author SHA1 Message Date
c6f85d7a8d improve handling of incompatible enums 2021-11-10 11:47:36 +01:00
25f44ab631 update collection info node 2021-11-10 11:11:12 +01:00
1d729aa2f7 Merge branch 'master' into temp-enum-socket 2021-11-10 11:04:50 +01:00
b17561f8b2 support enum in interface socket 2021-11-09 20:00:05 +01:00
dea72dbb9d fixes 2021-11-09 19:54:42 +01:00
fe2cde8390 improve 2021-11-09 19:28:55 +01:00
74257d6ccb Merge branch 'master' into temp-enum-socket 2021-11-09 18:29:57 +01:00
b4d47523c2 evaluate enum node 2021-11-08 15:53:24 +01:00
d728c22181 add index outut 2021-11-08 15:24:26 +01:00
1c31d62951 fix 2021-11-08 15:24:21 +01:00
e6ba5ec37b progress 2021-11-08 14:50:48 +01:00
62bd391187 improved inferencing 2021-11-08 13:51:57 +01:00
e736900e9a Merge branch 'master' into temp-enum-socket 2021-11-08 12:25:52 +01:00
65548da002 fix missing update 2021-11-08 11:13:37 +01:00
3481d13104 Merge branch 'master' into temp-enum-socket 2021-11-08 11:02:09 +01:00
23ef20855b progress 2021-11-07 13:42:25 +01:00
ebc81c6de4 progress 2021-11-07 13:07:00 +01:00
ffd8b05e20 initial enum inferencin 2021-11-07 12:07:38 +01:00
1006e84faa progress 2021-11-07 11:53:12 +01:00
dfb86671fe show enum labels in node 2021-11-06 19:34:24 +01:00
2fb43d04cb support custom socket drawing 2021-11-06 19:26:58 +01:00
dad7371b41 progress 2021-11-06 19:11:52 +01:00
eaa9feb9a0 progress 2021-11-06 18:25:11 +01:00
d12eff1a88 improve enum storage 2021-11-06 18:15:55 +01:00
6069ff45c7 add more enum storage 2021-11-06 17:12:38 +01:00
f121713ece Merge branch 'master' into temp-enum-socket 2021-11-06 16:46:21 +01:00
165100d8ac add node storage 2021-11-04 13:48:04 +01:00
ffb5d1205e add initial enum node 2021-11-04 13:38:18 +01:00
805540c713 initial enum socket 2021-11-04 13:28:24 +01:00
26 changed files with 1022 additions and 68 deletions

View File

@@ -306,6 +306,31 @@ class NODE_OT_tree_path_parent(Operator):
return {'FINISHED'}
class NODE_OT_enum_item_add(Operator):
'''Add enum item'''
bl_idname = "node.enum_item_add"
bl_label = "Add Enum Item"
bl_options = {'UNDO'}
node_name: StringProperty()
@classmethod
def poll(cls, context):
space = context.space_data
return space is not None and space.type == 'NODE_EDITOR' and space.edit_tree is not None
def execute(self, context):
import random
space = context.space_data
node = space.edit_tree.nodes.get(self.node_name)
if node is None:
return {'CANCELLED'}
item = node.enum_items.new()
item.value = random.randint(0, 1e7)
return {'FINISHED'}
classes = (
NodeSetting,
@@ -314,4 +339,5 @@ classes = (
NODE_OT_add_search,
NODE_OT_collapse_hide_unused_toggle,
NODE_OT_tree_path_parent,
NODE_OT_enum_item_add,
)

View File

@@ -754,6 +754,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeSwitch"),
NodeItem("FunctionNodeRandomValue"),
NodeItem("FunctionNodeAlignEulerToVector"),
NodeItem("FunctionNodeEnum"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeVectorCurve"),

View File

@@ -264,6 +264,12 @@ typedef struct bNodeType {
/* Additional parameters in the side panel */
void (*draw_buttons_ex)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr);
bool (*draw_socket)(struct uiLayout *layout,
const struct bContext *C,
struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocket *socket);
/* Additional drawing on backdrop */
void (*draw_backdrop)(
struct SpaceNode *snode, struct ImBuf *backdrop, struct bNode *node, int x, int y);
@@ -735,6 +741,7 @@ void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available);
int nodeSocketLinkLimit(const struct bNodeSocket *sock);
void nodeUpdateFromDeclaration(struct bNodeTree *ntree, struct bNode *node);
bool nodeDeclarationEnsure(struct bNodeTree *ntree, struct bNode *node);
bool nodeDeclarationEnsureOnOutdatedNode(struct bNodeTree *ntree, struct bNode *node);
void nodeSocketDeclarationsUpdate(struct bNode *node);
@@ -1577,6 +1584,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define FN_NODE_REPLACE_STRING 1218
#define FN_NODE_INPUT_BOOL 1219
#define FN_NODE_INPUT_INT 1220
#define FN_NODE_ENUM 1221
/** \} */

View File

@@ -87,6 +87,7 @@
#include "NOD_node_tree_ref.hh"
#include "NOD_shader.h"
#include "NOD_socket.h"
#include "NOD_socket_declarations.hh"
#include "NOD_texture.h"
#include "DEG_depsgraph.h"
@@ -130,10 +131,6 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree,
struct bNode *node,
const bool mute);
static FieldInferencingInterface *node_field_inferencing_interface_copy(
const FieldInferencingInterface &field_inferencing_interface);
static void node_field_inferencing_interface_free(
const FieldInferencingInterface *field_inferencing_interface);
static void ntree_init_data(ID *id)
{
@@ -246,9 +243,13 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
ntree_dst->interface_type = nullptr;
if (ntree_src->field_inferencing_interface) {
ntree_dst->field_inferencing_interface = node_field_inferencing_interface_copy(
ntree_dst->field_inferencing_interface = new blender::nodes::FieldInferencingInterface(
*ntree_src->field_inferencing_interface);
}
if (ntree_src->enum_inferencing_interface) {
ntree_dst->enum_inferencing_interface = new blender::nodes::EnumInferencingInterface(
*ntree_src->enum_inferencing_interface);
}
if (flag & LIB_ID_COPY_NO_PREVIEW) {
ntree_dst->preview = nullptr;
@@ -301,7 +302,8 @@ static void ntree_free_data(ID *id)
MEM_freeN(sock);
}
node_field_inferencing_interface_free(ntree->field_inferencing_interface);
delete ntree->field_inferencing_interface;
delete ntree->enum_inferencing_interface;
/* free preview hash */
if (ntree->previews) {
@@ -359,6 +361,7 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_ENUM:
break;
}
}
@@ -490,6 +493,9 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case SOCK_MATERIAL:
BLO_write_struct(writer, bNodeSocketValueMaterial, sock->default_value);
break;
case SOCK_ENUM:
BLO_write_struct(writer, bNodeSocketValueEnum, sock->default_value);
break;
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
@@ -620,6 +626,15 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
}
BLO_write_struct_by_name(writer, node->typeinfo->storagename, storage);
}
else if (node->type == FN_NODE_ENUM) {
NodeFunctionEnum *storage = (NodeFunctionEnum *)node->storage;
BLO_write_struct(writer, NodeFunctionEnum, storage);
BLO_write_struct_list(writer, NodeFunctionEnumItem, &storage->items);
LISTBASE_FOREACH (NodeFunctionEnumItem *, item, &storage->items) {
BLO_write_string(writer, item->name);
BLO_write_string(writer, item->description);
}
}
else if (node->typeinfo != &NodeTypeUndefined) {
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
}
@@ -682,6 +697,10 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
sock->total_inputs = 0; /* Clear runtime data set before drawing. */
sock->cache = nullptr;
sock->declaration = nullptr;
if (sock->type == SOCK_ENUM) {
bNodeSocketValueEnum *socket_value = (bNodeSocketValueEnum *)sock->default_value;
socket_value->items = nullptr;
}
}
/* ntree itself has been read! */
@@ -697,6 +716,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
ntree->execdata = nullptr;
ntree->field_inferencing_interface = nullptr;
ntree->enum_inferencing_interface = nullptr;
BLO_read_data_address(reader, &ntree->adt);
BKE_animdata_blend_read_data(reader, ntree->adt);
@@ -801,6 +821,17 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
BLO_read_data_address(reader, &storage->string);
break;
}
case FN_NODE_ENUM: {
NodeFunctionEnum *storage = (NodeFunctionEnum *)node->storage;
BLO_read_data_address(reader, &storage->owner_node);
BLO_read_list(reader, &storage->items);
LISTBASE_FOREACH (NodeFunctionEnumItem *, item, &storage->items) {
BLO_read_data_address(reader, &item->owner_node);
BLO_read_data_address(reader, &item->name);
BLO_read_data_address(reader, &item->description);
}
break;
}
default:
break;
}
@@ -905,6 +936,7 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_ENUM:
break;
}
}
@@ -1000,6 +1032,7 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_ENUM:
break;
}
}
@@ -1127,8 +1160,6 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
BLI_strncpy(node->name, DATA_(ntype->ui_name), NODE_MAXSTR);
nodeUniqueName(ntree, node);
node_add_sockets_from_type(ntree, node, ntype);
if (ntype->initfunc != nullptr) {
ntype->initfunc(ntree, node);
}
@@ -1137,6 +1168,8 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
ntree->typeinfo->node_add_init(ntree, node);
}
node_add_sockets_from_type(ntree, node, ntype);
if (node->id) {
id_us_plus(node->id);
}
@@ -1414,7 +1447,7 @@ void nodeRegisterType(bNodeType *nt)
if (nt->declare && !nt->declaration_is_dynamic) {
if (nt->fixed_declaration == nullptr) {
nt->fixed_declaration = new blender::nodes::NodeDeclaration();
blender::nodes::NodeDeclarationBuilder builder{*nt->fixed_declaration};
blender::nodes::NodeDeclarationBuilder builder{*nt->fixed_declaration, nullptr};
nt->declare(builder);
}
}
@@ -1600,6 +1633,7 @@ static void socket_id_user_increment(bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_ENUM:
break;
}
}
@@ -1653,6 +1687,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_ENUM:
break;
}
}
@@ -1815,6 +1850,8 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketTexture";
case SOCK_MATERIAL:
return "NodeSocketMaterial";
case SOCK_ENUM:
return "NodeSocketEnum";
}
return nullptr;
}
@@ -1892,6 +1929,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceTexture";
case SOCK_MATERIAL:
return "NodeSocketInterfaceMaterial";
case SOCK_ENUM:
return "NodeSocketInterfaceEnum";
}
return nullptr;
}
@@ -1925,6 +1964,8 @@ const char *nodeStaticSocketLabel(int type, int UNUSED(subtype))
return "Texture";
case SOCK_MATERIAL:
return "Material";
case SOCK_ENUM:
return "Enum";
}
return nullptr;
}
@@ -4053,7 +4094,7 @@ bool nodeDeclarationEnsureOnOutdatedNode(bNodeTree *UNUSED(ntree), bNode *node)
}
if (node->typeinfo->declaration_is_dynamic) {
node->declaration = new blender::nodes::NodeDeclaration();
blender::nodes::NodeDeclarationBuilder builder{*node->declaration};
blender::nodes::NodeDeclarationBuilder builder{*node->declaration, node};
node->typeinfo->declare(builder);
}
else {
@@ -4077,6 +4118,16 @@ bool nodeDeclarationEnsure(bNodeTree *ntree, bNode *node)
return false;
}
void nodeUpdateFromDeclaration(bNodeTree *ntree, bNode *node)
{
if (!node->typeinfo->declaration_is_dynamic) {
return;
}
delete node->declaration;
node->declaration = nullptr;
node_verify_sockets(ntree, node, true);
}
/* ************** Node Clipboard *********** */
#define USE_NODE_CB_VALIDATE
@@ -4540,24 +4591,14 @@ void ntreeUpdateAllNew(Main *main)
}
}
ntreeUpdateTree(nullptr, ntree);
/* Ideally this would first sort the trees based on their dependencies and then update every
* tree just once. */
ntreeUpdateTree(main, ntree);
}
}
FOREACH_NODETREE_END;
}
static FieldInferencingInterface *node_field_inferencing_interface_copy(
const FieldInferencingInterface &field_inferencing_interface)
{
return new FieldInferencingInterface(field_inferencing_interface);
}
static void node_field_inferencing_interface_free(
const FieldInferencingInterface *field_inferencing_interface)
{
delete field_inferencing_interface;
}
namespace blender::bke::node_field_inferencing {
static bool is_field_socket_type(eNodeSocketDatatype type)
@@ -4570,7 +4611,7 @@ static bool is_field_socket_type(const SocketRef &socket)
return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type);
}
static bool update_field_inferencing(bNodeTree &btree);
static bool update_field_inferencing(const NodeTreeRef &tree);
static InputSocketFieldType get_interface_input_field_type(const NodeRef &node,
const InputSocketRef &socket)
@@ -4669,7 +4710,8 @@ static FieldInferencingInterface get_node_field_inferencing_interface(const Node
}
if (group->field_inferencing_interface == nullptr) {
/* Update group recursively. */
update_field_inferencing(*group);
const NodeTreeRef group_tree{group};
update_field_inferencing(group_tree);
}
return *group->field_inferencing_interface;
}
@@ -5027,12 +5069,10 @@ static void update_socket_shapes(const NodeTreeRef &tree,
}
}
static bool update_field_inferencing(bNodeTree &btree)
static bool update_field_inferencing(const NodeTreeRef &tree)
{
using namespace blender::nodes;
if (btree.type != NTREE_GEOMETRY) {
return false;
}
bNodeTree &btree = *tree.btree();
/* Create new inferencing interface for this node group. */
FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface();
@@ -5041,9 +5081,6 @@ static bool update_field_inferencing(bNodeTree &btree)
new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs),
OutputFieldDependency::ForDataSource());
/* Create #NodeTreeRef to accelerate various queries on the node tree (e.g. linked sockets). */
const NodeTreeRef tree{&btree};
/* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */
Array<SocketFieldState> field_state_by_socket_id(tree.sockets().size());
@@ -5065,6 +5102,185 @@ static bool update_field_inferencing(bNodeTree &btree)
} // namespace blender::bke::node_field_inferencing
namespace blender::bke::enum_inferencing {
using namespace blender::nodes;
static bool update_enum_inferencing(const NodeTreeRef &tree);
static EnumInputInferencingInfo get_input_enum_socket_inferencing_info(
const InputSocketRef &socket)
{
BLI_assert(socket.typeinfo()->type == SOCK_ENUM);
const NodeRef &node = socket.node();
if (node.is_group_node()) {
bNodeTree *group = (bNodeTree *)node.bnode()->id;
if (group == nullptr) {
return {};
}
if (!ntreeIsRegistered(group)) {
return {};
}
if (group->enum_inferencing_interface == nullptr) {
/* Update group recursively. */
const NodeTreeRef group_tree{group};
update_enum_inferencing(group_tree);
}
return group->enum_inferencing_interface->inputs[socket.index()];
}
if (node.is_reroute_node()) {
return {};
}
const decl::Enum &socket_decl = static_cast<const decl::Enum &>(*socket.bsocket()->declaration);
return socket_decl.inferencing_info();
}
struct EnumState {
std::shared_ptr<EnumItems> items;
int group_output_index = -1;
bool targets_are_invalid = false;
};
static bool enum_states_are_compatible(const EnumState &a, const EnumState &b)
{
if (a.items && b.items) {
return a.items->items() == b.items->items();
}
if (a.group_output_index == -1 || b.group_output_index == -1) {
return true;
}
if (a.group_output_index == b.group_output_index) {
return true;
}
return false;
}
static bool update_enum_inferencing(const NodeTreeRef &tree)
{
using namespace blender::nodes;
bNodeTree &btree = *tree.btree();
/* TODO: handle cyclic trees */
const NodeTreeRef::ToposortResult toposort_result = tree.toposort(
NodeTreeRef::ToposortDirection::RightToLeft);
Map<const SocketRef *, EnumState> state_by_socket;
for (const NodeRef *node : toposort_result.sorted_nodes) {
bNode &bnode = *node->bnode();
nodeDeclarationEnsure(&btree, &bnode);
if (node->is_group_output_node()) {
for (const InputSocketRef *input_socket : node->inputs()) {
if (input_socket->typeinfo()->type == SOCK_ENUM) {
state_by_socket.add(input_socket, EnumState{nullptr, input_socket->index()});
}
}
continue;
}
for (const OutputSocketRef *socket : node->outputs()) {
if (socket->typeinfo()->type != SOCK_ENUM) {
continue;
}
EnumState enum_state;
for (const InputSocketRef *target_socket : socket->directly_linked_sockets()) {
if (target_socket->is_available()) {
EnumState new_state = state_by_socket.lookup_default(target_socket, {});
if (enum_states_are_compatible(enum_state, new_state)) {
enum_state = new_state;
enum_state.targets_are_invalid = false;
}
else {
enum_state = {};
enum_state.targets_are_invalid = true;
break;
}
}
}
state_by_socket.add_new(socket, enum_state);
}
for (const InputSocketRef *socket : node->inputs()) {
if (socket->typeinfo()->type != SOCK_ENUM) {
continue;
}
EnumInputInferencingInfo inferencing_info = get_input_enum_socket_inferencing_info(*socket);
EnumState input_state;
if (inferencing_info.items) {
input_state.items = inferencing_info.items;
}
else {
int inference_index = inferencing_info.inference_index;
if (inference_index == -1) {
for (const OutputSocketRef *output_socket : node->outputs()) {
if (output_socket->typeinfo()->type == SOCK_ENUM) {
inference_index = output_socket->index();
break;
}
}
}
BLI_assert(inference_index >= 0);
const OutputSocketRef &output_socket = node->output(inference_index);
input_state = state_by_socket.lookup(&output_socket);
input_state.targets_are_invalid = false;
}
state_by_socket.add_new(socket, input_state);
}
}
for (const auto item : state_by_socket.items()) {
const SocketRef *socket = item.key;
const EnumState &state = item.value;
bNodeSocketValueEnum *socket_value = (bNodeSocketValueEnum *)socket->bsocket()->default_value;
if (state.items) {
socket_value->items = state.items->items();
}
else {
socket_value->items = nullptr;
}
socket_value->targets_are_invalid = state.targets_are_invalid;
}
EnumInferencingInterface *new_inferencing_interface = new EnumInferencingInterface();
new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs));
for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) {
for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) {
if (output_socket->typeinfo()->type != SOCK_ENUM) {
continue;
}
const EnumState &state = state_by_socket.lookup(output_socket);
EnumInputInferencingInfo &group_input_info =
new_inferencing_interface->inputs[output_socket->index()];
if (state.items) {
group_input_info.items = state.items;
}
else if (state.group_output_index >= 0) {
group_input_info.inference_index = state.group_output_index;
}
else {
group_input_info.items = std::make_shared<EnumItems>();
}
bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&btree.inputs,
output_socket->index());
BLI_assert(interface_socket->type == SOCK_ENUM);
bNodeSocketValueEnum *interface_value = (bNodeSocketValueEnum *)
interface_socket->default_value;
interface_value->items = group_input_info.items ? group_input_info.items->items() : nullptr;
}
}
delete btree.enum_inferencing_interface;
btree.enum_inferencing_interface = new_inferencing_interface;
/* TODO: Only return true when the interface changed. */
return true;
}
} // namespace blender::bke::enum_inferencing
/**
* \param tree_update_flag: #eNodeTreeUpdate enum.
*/
@@ -5133,6 +5349,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
/* node tree update tags override individual node update flags */
if ((node->update & NODE_UPDATE) || (ntree->update & NTREE_UPDATE)) {
nodeUpdateFromDeclaration(ntree, node);
if (node->typeinfo->updatefunc) {
node->typeinfo->updatefunc(ntree, node);
}
@@ -5155,10 +5372,19 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
int tree_user_update_flag = 0;
if (ntree->update & NTREE_UPDATE) {
/* If the field interface of this node tree has changed, all node trees using
* this group will need to recalculate their interface as well. */
if (blender::bke::node_field_inferencing::update_field_inferencing(*ntree)) {
tree_user_update_flag |= NTREE_UPDATE_FIELD_INFERENCING;
if (ntree->type == NTREE_GEOMETRY) {
/* Create #NodeTreeRef to accelerate various queries on the node tree (e.g. linked
* sockets). */
const NodeTreeRef tree{ntree};
/* If the field interface of this node tree has changed, all node trees using
* this group will need to recalculate their interface as well. */
if (blender::bke::node_field_inferencing::update_field_inferencing(tree)) {
tree_user_update_flag |= NTREE_UPDATE_FIELD_INFERENCING;
}
if (blender::bke::enum_inferencing::update_enum_inferencing(tree)) {
/* TODO: Fix flag handling. */
tree_user_update_flag |= NTREE_UPDATE_FIELD_INFERENCING;
}
}
}
@@ -5194,6 +5420,7 @@ void nodeUpdate(bNodeTree *ntree, bNode *node)
}
ntree->is_updating = true;
nodeUpdateFromDeclaration(ntree, node);
if (node->typeinfo->updatefunc) {
node->typeinfo->updatefunc(ntree, node);
}
@@ -5224,6 +5451,7 @@ bool nodeUpdateID(bNodeTree *ntree, ID *id)
if (node->id == id) {
changed = true;
node->update |= NODE_UPDATE_ID;
nodeUpdateFromDeclaration(ntree, node);
if (node->typeinfo->updatefunc) {
node->typeinfo->updatefunc(ntree, node);
}
@@ -5956,6 +6184,7 @@ static void registerFunctionNodes()
register_node_type_fn_align_euler_to_vector();
register_node_type_fn_boolean_math();
register_node_type_fn_enum();
register_node_type_fn_float_compare();
register_node_type_fn_float_to_int();
register_node_type_fn_input_bool();

View File

@@ -3472,6 +3472,7 @@ static const float std_node_socket_colors[][4] = {
{0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */
{0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */
{0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */
{0.00, 0.00, 0.00, 1.0}, /* SOCK_ENUM */
};
/* common color callbacks for standard types */
@@ -3690,6 +3691,10 @@ static void std_node_socket_draw(
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
case SOCK_ENUM: {
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
default:
node_socket_button_label(C, layout, ptr, node_ptr, text);
break;
@@ -3727,7 +3732,8 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout
}
case SOCK_BOOLEAN:
case SOCK_RGBA:
case SOCK_STRING: {
case SOCK_STRING:
case SOCK_ENUM: {
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), 0);
break;
}
@@ -4444,6 +4450,12 @@ void node_draw_link(const bContext *C, View2D *v2d, SpaceNode *snode, bNodeLink
th_col1 = th_col2 = th_col3 = TH_REDALERT;
}
}
if (link->fromsock && link->fromsock->type == SOCK_ENUM) {
bNodeSocketValueEnum *socket_value = (bNodeSocketValueEnum *)link->fromsock->default_value;
if (socket_value->targets_are_invalid) {
th_col1 = th_col2 = th_col3 = TH_REDALERT;
}
}
node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3);
}

View File

@@ -419,7 +419,10 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
if (node->typeinfo->draw_socket == NULL ||
!node->typeinfo->draw_socket(row, C, ntree, node, nsock)) {
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
}
UI_block_align_end(node->block);
UI_block_layout_resolve(node->block, nullptr, &buty);
@@ -554,8 +557,11 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
uiLayout *row = uiLayoutRow(layout, true);
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
if (node->typeinfo->draw_socket == NULL ||
!node->typeinfo->draw_socket(row, C, ntree, node, nsock)) {
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
}
UI_block_align_end(node->block);
UI_block_layout_resolve(node->block, nullptr, &buty);

View File

@@ -1335,6 +1335,8 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* add modal handler */
WM_event_add_modal_handler(C, op);
ntreeUpdateTree(bmain, snode->edittree);
return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
@@ -2178,6 +2180,7 @@ static int get_main_socket_priority(const bNodeSocket *socket)
case SOCK_COLLECTION:
case SOCK_TEXTURE:
case SOCK_MATERIAL:
case SOCK_ENUM:
return 5;
}
return -1;

View File

@@ -377,7 +377,7 @@ static Vector<NodeLinkItem> ui_node_link_items(NodeLinkArg *arg,
using namespace blender::nodes;
r_node_decl.emplace(NodeDeclaration());
NodeDeclarationBuilder node_decl_builder{*r_node_decl};
NodeDeclarationBuilder node_decl_builder{*r_node_decl, nullptr};
arg->node_type->declare(node_decl_builder);
Span<SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs() :
r_node_decl->outputs();

View File

@@ -191,6 +191,7 @@ typedef enum eNodeSocketDatatype {
SOCK_COLLECTION = 11,
SOCK_TEXTURE = 12,
SOCK_MATERIAL = 13,
SOCK_ENUM = 14,
} eNodeSocketDatatype;
/* Socket shape. */
@@ -477,10 +478,13 @@ typedef struct bNodeLink {
#ifdef __cplusplus
namespace blender::nodes {
struct FieldInferencingInterface;
}
struct EnumInferencingInterface;
} // namespace blender::nodes
using FieldInferencingInterfaceHandle = blender::nodes::FieldInferencingInterface;
using EnumInferencingInterfaceHandle = blender::nodes::EnumInferencingInterface;
#else
typedef struct FieldInferencingInterfaceHandle FieldInferencingInterfaceHandle;
typedef struct EnumInferencingInterfaceHandle EnumInferencingInterfaceHandle;
#endif
/* the basis for a Node tree, all links and nodes reside internal here */
@@ -507,6 +511,7 @@ typedef struct bNodeTree {
ListBase nodes, links;
/** Information about how inputs and outputs of the node group interact with fields. */
FieldInferencingInterfaceHandle *field_inferencing_interface;
EnumInferencingInterfaceHandle *enum_inferencing_interface;
/** Set init on fileread. */
int type, init;
@@ -679,6 +684,17 @@ typedef struct bNodeSocketValueMaterial {
struct Material *value;
} bNodeSocketValueMaterial;
/* TODO: Add something to help with versioning for built-in enums. */
typedef struct bNodeSocketValueEnum {
int value;
char targets_are_invalid;
char _pad[3];
/**
* Possible items for the enum. This is only runtime data and is not owned by the socket.
*/
const struct EnumPropertyItem *items;
} bNodeSocketValueEnum;
/* Data structs, for node->storage. */
enum {
CMP_NODE_MASKTYPE_ADD = 0,
@@ -1606,6 +1622,21 @@ typedef struct NodeGeometryViewer {
int8_t data_type;
} NodeGeometryViewer;
typedef struct NodeFunctionEnumItem {
struct NodeFunctionEnumItem *next, *prev;
bNode *owner_node;
int value;
char _pad[4];
char *name;
char *description;
} NodeFunctionEnumItem;
typedef struct NodeFunctionEnum {
bNode *owner_node;
/** NodeFunctionEnumItem. */
ListBase items;
} NodeFunctionEnum;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1

View File

@@ -449,6 +449,8 @@ extern StructRNA RNA_MusgraveTexture;
extern StructRNA RNA_NlaStrip;
extern StructRNA RNA_NlaTrack;
extern StructRNA RNA_Node;
extern StructRNA RNA_NodeFunctionEnum;
extern StructRNA RNA_NodeFunctionEnumItem;
extern StructRNA RNA_NodeInstanceHash;
extern StructRNA RNA_NodeLink;
extern StructRNA RNA_NodeOutputFileSlotFile;

View File

@@ -78,6 +78,7 @@ static const EnumPropertyItem node_socket_data_type_items[] = {
{SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
{SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
{SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
{SOCK_ENUM, "ENUM", 0, "Enum", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -106,6 +107,7 @@ static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
{SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
{SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
{SOCK_ENUM, "ENUM", 0, "Enum", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -1235,8 +1237,8 @@ static bNode *rna_NodeTree_node_new(bNodeTree *ntree,
ntreeTexCheckCyclics(ntree);
}
ntreeUpdateTree(CTX_data_main(C), ntree);
nodeUpdate(ntree, node);
ntreeUpdateTree(CTX_data_main(C), ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
return node;
@@ -2057,7 +2059,8 @@ static bool switch_type_supported(const EnumPropertyItem *item)
SOCK_COLLECTION,
SOCK_TEXTURE,
SOCK_MATERIAL,
SOCK_IMAGE);
SOCK_IMAGE,
SOCK_ENUM);
}
static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C),
@@ -3438,6 +3441,7 @@ static bool rna_NodeInternal_poll_instance(bNode *node, bNodeTree *ntree)
static void rna_NodeInternal_update(ID *id, bNode *node)
{
bNodeTree *ntree = (bNodeTree *)id;
nodeUpdateFromDeclaration(ntree, node);
if (node->typeinfo->updatefunc) {
node->typeinfo->updatefunc(ntree, node);
}
@@ -4569,6 +4573,58 @@ bool rna_NodeSocketMaterial_default_value_poll(PointerRNA *UNUSED(ptr), PointerR
return ma->gp_style == NULL;
}
static void rna_nodeFunctionEnum_update_enum(Main *bmain, bNodeTree *ntree, bNode *node)
{
ntree->update |= NTREE_UPDATE_FIELD_INFERENCING;
nodeUpdate(ntree, node);
ED_node_tag_update_nodetree(bmain, ntree, node);
ntreeUpdateTree(bmain, ntree);
}
static void rna_NodeFunctionEnumItem_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
NodeFunctionEnumItem *item = (NodeFunctionEnumItem *)ptr->data;
bNode *node = item->owner_node;
rna_nodeFunctionEnum_update_enum(bmain, (bNodeTree *)ptr->owner_id, node);
}
static NodeFunctionEnumItem *rna_NodeFunctionEnumItems_items_new(ID *id, bNode *node, bContext *C)
{
NodeFunctionEnum *storage = node->storage;
NodeFunctionEnumItem *item = MEM_callocN(sizeof(NodeFunctionEnumItem), __func__);
item->owner_node = node;
BLI_addtail(&storage->items, item);
PointerRNA node_ptr;
RNA_pointer_create(id, &RNA_Node, node, &node_ptr);
Main *bmain = CTX_data_main(C);
bNodeTree *ntree = (bNodeTree *)id;
rna_nodeFunctionEnum_update_enum(bmain, ntree, node);
return item;
}
static const EnumPropertyItem *rna_NodeSocketEnum_items(bContext *UNUSED(C),
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
*r_free = false;
bNodeSocket *socket = ptr->data;
bNodeSocketValueEnum *storage = (bNodeSocketValueEnum *)socket->default_value;
if (storage->items == NULL) {
return DummyRNA_NULL_items;
}
return storage->items;
}
static void rna_NodeSocketEnum_set(PointerRNA *ptr, int value)
{
bNodeSocket *socket = ptr->data;
bNodeSocketValueEnum *storage = (bNodeSocketValueEnum *)socket->default_value;
storage->value = value;
}
#else
static const EnumPropertyItem prop_image_layer_items[] = {
@@ -5064,6 +5120,60 @@ static void def_fn_input_string(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void rna_def_fn_enum_item(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "NodeFunctionEnumItem", NULL);
RNA_def_struct_ui_text(srna, "Node Function Enum Item", "");
prop = RNA_def_property(srna, "value", PROP_INT, PROP_NONE);
RNA_def_property_ui_text(prop, "Value", "Internal identifier if the enum item");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeFunctionEnumItem_update");
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Name", "Display name of the enum item");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeFunctionEnumItem_update");
prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Description", "Tooltip for the enum item");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeFunctionEnumItem_update");
}
static void rna_def_fn_enum_items_api(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
FunctionRNA *func;
PropertyRNA *parm;
RNA_def_property_srna(cprop, "NodeFunctionEnumItems");
srna = RNA_def_struct(brna, "NodeFunctionEnumItems", NULL);
RNA_def_struct_sdna(srna, "bNode");
RNA_def_struct_ui_text(srna, "Enum Items", "");
func = RNA_def_function(srna, "new", "rna_NodeFunctionEnumItems_items_new");
RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Add a new enum item");
parm = RNA_def_pointer(func, "item", "NodeFunctionEnumItem", "", "New enum item");
RNA_def_function_return(func, parm);
}
static void def_fn_enum(BlenderRNA *brna, StructRNA *srna)
{
PropertyRNA *prop;
rna_def_fn_enum_item(brna);
RNA_def_struct_sdna_from(srna, "NodeFunctionEnum", "storage");
prop = RNA_def_property(srna, "enum_items", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "items", NULL);
RNA_def_property_struct_type(prop, "NodeFunctionEnumItem");
RNA_def_property_ui_text(prop, "Items", "");
rna_def_fn_enum_items_api(brna, prop);
}
/* -- Shader Nodes ---------------------------------------------------------- */
static void def_sh_output(StructRNA *srna)
@@ -11960,6 +12070,47 @@ static void rna_def_node_socket_material(BlenderRNA *brna,
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
}
static void rna_def_node_socket_enum(BlenderRNA *brna,
const char *identifier,
const char *interface_idname)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
RNA_def_struct_ui_text(srna, "Enum Node Socket", "Enum socket of a node");
RNA_def_struct_sdna(srna, "bNodeSocket");
RNA_def_struct_sdna_from(srna, "bNodeSocketValueEnum", "default_value");
prop = RNA_def_property(srna, "default_value", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "value");
RNA_def_property_enum_funcs(prop, NULL, "rna_NodeSocketEnum_set", "rna_NodeSocketEnum_items");
RNA_def_property_enum_items(prop, DummyRNA_DEFAULT_items);
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update");
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_struct_sdna_from(srna, "bNodeSocket", NULL);
/* socket interface */
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
RNA_def_struct_ui_text(srna, "Enum Node Socket Interface", "Enum socket of a node");
RNA_def_struct_sdna(srna, "bNodeSocket");
RNA_def_struct_sdna_from(srna, "bNodeSocketValueEnum", "default_value");
prop = RNA_def_property(srna, "default_value", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "value");
RNA_def_property_enum_funcs(prop, NULL, "rna_NodeSocketEnum_set", "rna_NodeSocketEnum_items");
RNA_def_property_enum_items(prop, DummyRNA_DEFAULT_items);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
RNA_def_struct_sdna_from(srna, "bNodeSocket", NULL);
}
static void rna_def_node_socket_standard_types(BlenderRNA *brna)
{
/* XXX Workaround: Registered functions are not exposed in python by bpy,
@@ -12112,6 +12263,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_texture(brna, "NodeSocketTexture", "NodeSocketInterfaceTexture");
rna_def_node_socket_material(brna, "NodeSocketMaterial", "NodeSocketInterfaceMaterial");
rna_def_node_socket_enum(brna, "NodeSocketEnum", "NodeSocketInterfaceEnum");
}
static void rna_def_internal_node(BlenderRNA *brna)
@@ -13121,6 +13274,9 @@ void RNA_def_nodetree(BlenderRNA *brna)
/* needs brna argument, can't use NOD_static_types.h */ \
def_cmp_output_file(brna, srna); \
} \
if (ID == FN_NODE_ENUM) { \
def_fn_enum(brna, srna); \
} \
}
/* hack, don't want to add include path to RNA just for this, since in the future RNA types

View File

@@ -139,6 +139,7 @@ set(SRC
function/nodes/node_fn_align_euler_to_vector.cc
function/nodes/node_fn_boolean_math.cc
function/nodes/node_fn_enum.cc
function/nodes/node_fn_float_compare.cc
function/nodes/node_fn_float_to_int.cc
function/nodes/node_fn_input_bool.cc

View File

@@ -24,6 +24,7 @@ void register_node_type_fn_legacy_random_float(void);
void register_node_type_fn_align_euler_to_vector(void);
void register_node_type_fn_boolean_math(void);
void register_node_type_fn_enum(void);
void register_node_type_fn_float_compare(void);
void register_node_type_fn_float_to_int(void);
void register_node_type_fn_input_bool(void);

View File

@@ -66,6 +66,27 @@ using fn::GVMutableArray_Typed;
using fn::GVMutableArrayPtr;
using geometry_nodes_eval_log::NodeWarningType;
/** Wrapper for an integer so that one can not perform various operations like addition on it. */
struct EnumValue {
int value;
friend std::ostream &operator<<(std::ostream &stream, const EnumValue &value)
{
stream << value.value;
return stream;
}
friend bool operator==(const EnumValue &a, const EnumValue &b)
{
return a.value == b.value;
}
uint64_t hash() const
{
return this->value;
}
};
/**
* This class exists to separate the memory management details of the geometry nodes evaluator
* from the node execution functions and related utilities.
@@ -139,7 +160,8 @@ class GeoNodeExecParams {
std::is_same_v<T, bool> ||
std::is_same_v<T, ColorGeometry4f> ||
std::is_same_v<T, float3> ||
std::is_same_v<T, std::string>;
std::is_same_v<T, std::string> ||
std::is_same_v<T, EnumValue>;
/**
* Get the input value for the input socket with the given identifier.
@@ -211,6 +233,10 @@ class GeoNodeExecParams {
const Field<T> &field = this->get_input<Field<T>>(identifier);
return fn::evaluate_constant_field(field);
}
else if constexpr (std::is_enum_v<T>) {
const int value = this->get_input<EnumValue>(identifier).value;
return (T)this->validate_enum_value(identifier, value);
}
else {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
@@ -354,6 +380,8 @@ class GeoNodeExecParams {
/* Find the active socket with the input name (not the identifier). */
const bNodeSocket *find_available_socket(const StringRef name) const;
const int validate_enum_value(StringRef identifier, const int value) const;
};
} // namespace blender::nodes

View File

@@ -243,11 +243,13 @@ class NodeDeclaration {
class NodeDeclarationBuilder {
private:
/** The node may be null. */
NodeDeclaration &declaration_;
const bNode *node_;
Vector<std::unique_ptr<BaseSocketDeclarationBuilder>> builders_;
public:
NodeDeclarationBuilder(NodeDeclaration &declaration);
NodeDeclarationBuilder(NodeDeclaration &declaration, const bNode *node);
/**
* All inputs support fields, and all outputs are fields if any of the inputs is a field.
@@ -258,6 +260,12 @@ class NodeDeclarationBuilder {
declaration_.is_function_node_ = value;
}
/** Return the node this declaration is build for. It may be null. */
const bNode *node() const
{
return node_;
}
template<typename DeclType>
typename DeclType::Builder &add_input(StringRef name, StringRef identifier = "");
template<typename DeclType>
@@ -391,8 +399,9 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency()
/** \name #NodeDeclarationBuilder Inline Methods
* \{ */
inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declaration)
: declaration_(declaration)
inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declaration,
const bNode *node)
: declaration_(declaration), node_(node)
{
}

View File

@@ -16,12 +16,41 @@
#pragma once
#include <functional>
#include "NOD_node_declaration.hh"
#include "RNA_types.h"
#include "BLI_color.hh"
#include "BLI_float3.hh"
#include "BLI_utility_mixins.hh"
namespace blender::nodes {
class EnumItems : NonCopyable, NonMovable {
private:
const EnumPropertyItem *items_;
std::function<void()> free_fn_;
public:
EnumItems();
EnumItems(const EnumPropertyItem *items, std::function<void()> free_fn);
~EnumItems();
const EnumPropertyItem *items() const;
};
struct EnumInputInferencingInfo {
std::shared_ptr<EnumItems> items;
int inference_index = -1;
};
struct EnumInferencingInterface {
Vector<EnumInputInferencingInfo> inputs;
};
} // namespace blender::nodes
namespace blender::nodes::decl {
@@ -165,6 +194,32 @@ class StringBuilder : public SocketDeclarationBuilder<String> {
StringBuilder &default_value(const std::string value);
};
class EnumBuilder;
class Enum : public SocketDeclaration {
private:
int default_value_ = 0;
EnumInputInferencingInfo inferencing_info_;
friend EnumBuilder;
public:
using Builder = EnumBuilder;
const EnumInputInferencingInfo &inferencing_info() const;
bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override;
bool matches(const bNodeSocket &socket) const override;
};
class EnumBuilder : public SocketDeclarationBuilder<Enum> {
public:
EnumBuilder &default_value(const int value);
EnumBuilder &static_items(const EnumPropertyItem *items);
EnumBuilder &dynamic_items(std::shared_ptr<EnumItems> items);
EnumBuilder &inference_items(const int output_index = -1);
};
class IDSocketDeclaration : public SocketDeclaration {
private:
const char *idname_;
@@ -338,6 +393,18 @@ inline StringBuilder &StringBuilder::default_value(std::string value)
/** \} */
/* -------------------------------------------------------------------- */
/** \name #EnumBuilder Inline Methods
* \{ */
inline EnumBuilder &EnumBuilder::default_value(const int value)
{
decl_->default_value_ = value;
return *this;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #IDSocketDeclaration and Children Inline Methods
* \{ */

View File

@@ -268,6 +268,7 @@ DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", Leg
DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "")
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_COMPARE_FLOATS, def_float_compare, "COMPARE_FLOATS", CompareFloats, "Compare Floats", "")
DefNode(FunctionNode, FN_NODE_ENUM, 0, "ENUM", Enum, "Enum", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
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", "")

View File

@@ -0,0 +1,206 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cmath>
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_geometry_exec.hh"
#include "node_function_util.hh"
namespace blender::nodes {
static void fn_node_enum_declare(NodeDeclarationBuilder &b)
{
const bNode *node = b.node();
if (node == nullptr) {
return;
}
b.add_output<decl::Int>("Index");
EnumPropertyItem *items = nullptr;
int tot_items = 0;
const NodeFunctionEnum *storage = (const NodeFunctionEnum *)node->storage;
LISTBASE_FOREACH (const NodeFunctionEnumItem *, item, &storage->items) {
b.add_output<decl::Bool>(item->name ? item->name : "Bool",
"item_" + std::to_string(item->value));
EnumPropertyItem enum_item = {0};
enum_item.identifier = BLI_strdup(item->name ? item->name : "");
enum_item.name = enum_item.identifier;
enum_item.description = BLI_strdup(item->description ? item->description : "");
enum_item.value = item->value;
RNA_enum_item_add(&items, &tot_items, &enum_item);
}
RNA_enum_item_end(&items, &tot_items);
std::shared_ptr<EnumItems> socket_items = std::make_shared<EnumItems>(
items, [tot_items, items]() {
for (const int i : IndexRange(tot_items - 1)) {
EnumPropertyItem &enum_item = items[i];
MEM_freeN((void *)enum_item.identifier);
MEM_freeN((void *)enum_item.description);
}
MEM_freeN(items);
});
b.add_input<decl::Enum>("Enum").dynamic_items(std::move(socket_items)).hide_label();
};
static bool fn_node_enum_draw_socket(uiLayout *layout,
const bContext *UNUSED(C),
bNodeTree *ntree,
bNode *node,
bNodeSocket *socket)
{
const int socket_index = BLI_findindex(&node->outputs, socket);
if (socket_index <= 0) {
return false;
}
NodeFunctionEnum *storage = (NodeFunctionEnum *)node->storage;
NodeFunctionEnumItem *item = (NodeFunctionEnumItem *)BLI_findlink(&storage->items,
socket_index - 1);
PointerRNA item_ptr;
RNA_pointer_create(&ntree->id, &RNA_NodeFunctionEnumItem, item, &item_ptr);
uiItemR(layout, &item_ptr, "name", 0, "", ICON_NONE);
return true;
}
static void fn_node_enum_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeFunctionEnum *data = (NodeFunctionEnum *)MEM_callocN(sizeof(NodeFunctionEnum), __func__);
data->owner_node = node;
node->storage = data;
}
static void fn_node_enum_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
uiItemStringO(layout, "Add", ICON_PLUS, "node.enum_item_add", "node_name", node->name);
}
class EnumFunction : public fn::MultiFunction {
private:
Vector<int> enum_values_;
fn::MFSignature signature_;
public:
EnumFunction(const bNode &node)
{
fn::MFSignatureBuilder signature{"Enum Function"};
signature.single_input<EnumValue>("Enum");
signature.single_output<int>("Index");
const NodeFunctionEnum *storage = (NodeFunctionEnum *)node.storage;
LISTBASE_FOREACH (const NodeFunctionEnumItem *, item, &storage->items) {
signature.single_output<bool>(item->name);
enum_values_.append(item->value);
}
signature_ = signature.build();
this->set_signature(&signature_);
}
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<EnumValue> &enum_in = params.readonly_single_input<EnumValue>(0, "Enum");
MutableSpan<int> r_indices = params.uninitialized_single_output_if_required<int>(1, "Index");
if (!r_indices.is_empty()) {
for (const int i : mask) {
const int in_value = enum_in[i].value;
r_indices[i] = enum_values_.first_index_of_try(in_value);
}
}
for (const int enum_index : enum_values_.index_range()) {
MutableSpan<bool> r_bools = params.uninitialized_single_output_if_required<bool>(2 +
enum_index);
if (r_bools.is_empty()) {
continue;
}
const int enum_value = enum_values_[enum_index];
for (const int i : mask) {
const int in_value = enum_in[i].value;
r_bools[i] = in_value == enum_value;
}
}
}
};
static void fn_node_enum_build_multi_function(NodeMultiFunctionBuilder &builder)
{
builder.construct_and_set_matching_fn<EnumFunction>(builder.node());
}
static void fn_node_enum_copy_storage(bNodeTree *UNUSED(dest_ntree),
bNode *dst_node,
const bNode *src_node)
{
const NodeFunctionEnum *src_storage = (const NodeFunctionEnum *)src_node->storage;
NodeFunctionEnum *dst_storage = (NodeFunctionEnum *)MEM_dupallocN(src_storage);
dst_storage->owner_node = dst_node;
BLI_listbase_clear(&dst_storage->items);
LISTBASE_FOREACH (const NodeFunctionEnumItem *, src_item, &src_storage->items) {
NodeFunctionEnumItem *dst_item = (NodeFunctionEnumItem *)MEM_dupallocN(src_item);
dst_item->owner_node = dst_node;
dst_item->name = (char *)MEM_dupallocN(src_item->name);
dst_item->description = (char *)MEM_dupallocN(src_item->description);
BLI_addtail(&dst_storage->items, dst_item);
}
dst_node->storage = dst_storage;
}
static void fn_node_enum_free_storage(bNode *node)
{
NodeFunctionEnum *storage = (NodeFunctionEnum *)node->storage;
LISTBASE_FOREACH_MUTABLE (NodeFunctionEnumItem *, item, &storage->items) {
if (item->name) {
MEM_freeN(item->name);
}
if (item->description) {
MEM_freeN(item->description);
}
MEM_freeN(item);
}
MEM_freeN(storage);
}
} // namespace blender::nodes
void register_node_type_fn_enum()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_ENUM, "Enum", NODE_CLASS_SCRIPT, 0);
node_type_storage(&ntype,
"NodeFunctionEnum",
blender::nodes::fn_node_enum_free_storage,
blender::nodes::fn_node_enum_copy_storage);
node_type_init(&ntype, blender::nodes::fn_node_enum_init);
ntype.declare = blender::nodes::fn_node_enum_declare;
ntype.declaration_is_dynamic = true;
ntype.build_multi_function = blender::nodes::fn_node_enum_build_multi_function;
ntype.draw_buttons = blender::nodes::fn_node_enum_layout;
ntype.draw_socket = blender::nodes::fn_node_enum_draw_socket;
nodeRegisterType(&ntype);
}

View File

@@ -110,7 +110,8 @@ static bool geometry_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype
SOCK_COLLECTION,
SOCK_TEXTURE,
SOCK_IMAGE,
SOCK_MATERIAL);
SOCK_MATERIAL,
SOCK_ENUM);
}
void register_node_tree_type_geo(void)

View File

@@ -31,7 +31,25 @@ namespace blender::nodes {
static void geo_node_collection_info_declare(NodeDeclarationBuilder &b)
{
static const EnumPropertyItem rna_node_geometry_collection_info_transform_space_items[] = {
{GEO_NODE_TRANSFORM_SPACE_ORIGINAL,
"ORIGINAL",
0,
"Original",
"Output the geometry relative to the collection offset"},
{GEO_NODE_TRANSFORM_SPACE_RELATIVE,
"RELATIVE",
0,
"Relative",
"Bring the input collection geometry into the modified object, maintaining the relative "
"position between the objects in the scene"},
{0, NULL, 0, NULL, NULL},
};
b.add_input<decl::Collection>(N_("Collection")).hide_label();
b.add_input<decl::Enum>(N_("Transform Space"))
.static_items(rna_node_geometry_collection_info_transform_space_items)
.hide_label();
b.add_input<decl::Bool>(N_("Separate Children"))
.description(
N_("Output each child of the collection as a separate instance, sorted alphabetically"));
@@ -42,11 +60,6 @@ static void geo_node_collection_info_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
@@ -80,10 +93,8 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
return;
}
const bNode &bnode = params.node();
NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage;
const bool use_relative_transform = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
const bool use_relative_transform = params.get_input<GeometryNodeTransformSpace>(
"Transform Space") == GEO_NODE_TRANSFORM_SPACE_RELATIVE;
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
@@ -169,6 +180,5 @@ void register_node_type_geo_collection_info()
node_free_standard_storage,
node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_collection_info_exec;
ntype.draw_buttons = blender::nodes::geo_node_collection_info_layout;
nodeRegisterType(&ntype);
}

View File

@@ -35,13 +35,15 @@ namespace blender::nodes {
static void geo_node_curve_fill_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
b.add_output<decl::Geometry>(N_("Mesh"));
}
static const EnumPropertyItem mode_items[] = {
{GEO_NODE_CURVE_FILL_MODE_TRIANGULATED, "TRIANGLES", 0, "Triangles", ""},
{GEO_NODE_CURVE_FILL_MODE_NGONS, "NGONS", 0, "N-gons", ""},
{0, NULL, 0, NULL, NULL},
};
static void geo_node_curve_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
b.add_input<decl::Enum>(N_("Mode")).static_items(mode_items);
b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_curve_fill_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -151,8 +153,7 @@ static void geo_node_curve_fill_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage;
const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode;
const GeometryNodeCurveFillMode mode = params.get_input<GeometryNodeCurveFillMode>("Mode");
geometry_set.modify_geometry_sets(
[&](GeometrySet &geometry_set) { curve_fill_calculate(geometry_set, mode); });
@@ -173,6 +174,5 @@ void register_node_type_geo_curve_fill()
&ntype, "NodeGeometryCurveFill", node_free_standard_storage, node_copy_standard_storage);
ntype.declare = blender::nodes::geo_node_curve_fill_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_fill_exec;
ntype.draw_buttons = blender::nodes::geo_node_curve_fill_layout;
nodeRegisterType(&ntype);
}

View File

@@ -68,6 +68,8 @@ static void geo_node_switch_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Material>(N_("True"), "True_010");
b.add_input<decl::Image>(N_("False"), "False_011");
b.add_input<decl::Image>(N_("True"), "True_011");
b.add_input<decl::Enum>(N_("False"), "False_012");
b.add_input<decl::Enum>(N_("True"), "True_012");
b.add_output<decl::Float>(N_("Output")).dependent_field();
b.add_output<decl::Int>(N_("Output"), "Output_001").dependent_field();
@@ -81,6 +83,7 @@ static void geo_node_switch_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Texture>(N_("Output"), "Output_009");
b.add_output<decl::Material>(N_("Output"), "Output_010");
b.add_output<decl::Image>(N_("Output"), "Output_011");
b.add_output<decl::Enum>(N_("Output"), "Output_012").inference_items();
}
static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -287,6 +290,10 @@ static void geo_node_switch_exec(GeoNodeExecParams params)
switch_no_fields<Image *>(params, "_011");
break;
}
case SOCK_ENUM: {
switch_no_fields<EnumValue>(params, "_012");
break;
}
default:
BLI_assert_unreachable();
break;

View File

@@ -254,6 +254,29 @@ std::string GeoNodeExecParams::attribute_producer_name() const
return provider_->dnode->label_or_name() + TIP_(" node");
}
const int GeoNodeExecParams::validate_enum_value(StringRef identifier, const int value) const
{
const bNodeSocket &socket = *provider_->dnode->input_by_identifier(identifier).bsocket();
BLI_assert(socket.type == SOCK_ENUM);
const bNodeSocketValueEnum *socket_value = (const bNodeSocketValueEnum *)socket.default_value;
BLI_assert(socket_value->items != nullptr);
for (const EnumPropertyItem *item = socket_value->items; item->identifier != nullptr; item++) {
if (item->identifier[0]) {
if (item->value == value) {
return value;
}
}
}
/* Use the first value as default if the passed in value is invalid. */
for (const EnumPropertyItem *item = socket_value->items; item->identifier != nullptr; item++) {
if (item->identifier[0]) {
return item->value;
}
}
BLI_assert_unreachable();
return 0;
}
void GeoNodeExecParams::check_input_access(StringRef identifier,
const CPPType *requested_type) const
{

View File

@@ -44,11 +44,13 @@
#include "MEM_guardedalloc.h"
#include "NOD_geometry_exec.hh"
#include "NOD_node_declaration.hh"
#include "NOD_socket.h"
#include "FN_cpp_type_make.hh"
#include "FN_field.hh"
#include "FN_field_cpp_type.hh"
using namespace blender;
using blender::nodes::SocketDeclarationPtr;
@@ -398,6 +400,14 @@ void node_socket_init_default_value(bNodeSocket *sock)
sizeof(bNodeSocketValueMaterial), "node socket value material");
dval->value = nullptr;
sock->default_value = dval;
break;
}
case SOCK_ENUM: {
bNodeSocketValueEnum *dval = (bNodeSocketValueEnum *)MEM_callocN(
sizeof(bNodeSocketValueEnum), "node socket value enum");
dval->value = 0;
sock->default_value = dval;
break;
}
@@ -494,6 +504,12 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
id_us_plus(&toval->value->id);
break;
}
case SOCK_ENUM: {
bNodeSocketValueEnum *toval = (bNodeSocketValueEnum *)to->default_value;
bNodeSocketValueEnum *fromval = (bNodeSocketValueEnum *)from->default_value;
*toval = *fromval;
break;
}
}
to->flag |= (from->flag & SOCK_HIDE_VALUE);
@@ -811,6 +827,8 @@ MAKE_CPP_TYPE(Collection, Collection *, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(Texture, Tex *, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(Image, Image *, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(Material, Material *, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(EnumValue, blender::nodes::EnumValue, CPPTypeFlags::None)
MAKE_FIELD_CPP_TYPE(EnumValueField, blender::nodes::EnumValue);
static bNodeSocketType *make_socket_type_object()
{
@@ -884,6 +902,26 @@ static bNodeSocketType *make_socket_type_material()
return socktype;
}
static bNodeSocketType *make_socket_type_enum()
{
using blender::nodes::EnumValue;
bNodeSocketType *socktype = make_standard_socket_type(SOCK_ENUM, PROP_NONE);
socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<EnumValue>(); };
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
((EnumValue *)r_value)->value = ((bNodeSocketValueEnum *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = []() {
return &blender::fn::CPPType::get<blender::fn::Field<EnumValue>>();
};
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
EnumValue value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) blender::fn::Field<EnumValue>(blender::fn::make_constant_field(value));
};
return socktype;
}
void register_standard_node_socket_types(void)
{
/* Draw callbacks are set in `drawnode.c` to avoid bad-level calls. */
@@ -930,5 +968,7 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_material());
nodeRegisterSocketType(make_socket_type_enum());
nodeRegisterSocketType(make_socket_type_virtual());
}

View File

@@ -19,8 +19,36 @@
#include "BKE_node.h"
#include "RNA_enum_types.h"
#include "BLI_math_vector.h"
namespace blender::nodes {
EnumItems::EnumItems() : items_(DummyRNA_NULL_items)
{
}
EnumItems::EnumItems(const EnumPropertyItem *items, std::function<void()> free_fn)
: items_(items), free_fn_(std::move(free_fn))
{
BLI_assert(items != nullptr);
}
EnumItems::~EnumItems()
{
if (free_fn_) {
free_fn_();
}
}
const EnumPropertyItem *EnumItems::items() const
{
return items_;
}
} // namespace blender::nodes
namespace blender::nodes::decl {
static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subtype)
@@ -273,6 +301,56 @@ bool String::matches(const bNodeSocket &socket) const
/** \} */
/* -------------------------------------------------------------------- */
/** \name #Enum
* \{ */
const EnumInputInferencingInfo &Enum::inferencing_info() const
{
return inferencing_info_;
}
bNodeSocket &Enum::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const
{
bNodeSocket &socket = *nodeAddStaticSocket(
&ntree, &node, in_out, SOCK_ENUM, PROP_NONE, identifier_.c_str(), name_.c_str());
((bNodeSocketValueEnum *)socket.default_value)->value = default_value_;
this->set_common_flags(socket);
return socket;
}
bool Enum::matches(const bNodeSocket &socket) const
{
if (!this->matches_common_data(socket)) {
return false;
}
if (socket.type != SOCK_ENUM) {
return false;
}
return true;
}
EnumBuilder &EnumBuilder::static_items(const EnumPropertyItem *items)
{
BLI_assert(items != nullptr);
decl_->inferencing_info_.items = std::make_shared<EnumItems>(items, nullptr);
return *this;
}
EnumBuilder &EnumBuilder::dynamic_items(std::shared_ptr<EnumItems> items)
{
decl_->inferencing_info_.items = std::move(items);
return *this;
}
EnumBuilder &EnumBuilder::inference_items(const int output_index)
{
decl_->inferencing_info_.inference_index = output_index;
return *this;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #IDSocketDeclaration
* \{ */

View File

@@ -483,6 +483,14 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype
return -1;
}
}
case SOCK_ENUM: {
switch (from) {
case SOCK_ENUM:
return 1;
default:
return -1;
}
}
default:
return -1;
}