Compare commits
29 Commits
temp-viewp
...
temp-enum-
Author | SHA1 | Date | |
---|---|---|---|
c6f85d7a8d | |||
25f44ab631 | |||
1d729aa2f7 | |||
b17561f8b2 | |||
dea72dbb9d | |||
fe2cde8390 | |||
74257d6ccb | |||
b4d47523c2 | |||
d728c22181 | |||
1c31d62951 | |||
e6ba5ec37b | |||
62bd391187 | |||
e736900e9a | |||
65548da002 | |||
3481d13104 | |||
23ef20855b | |||
ebc81c6de4 | |||
ffd8b05e20 | |||
1006e84faa | |||
dfb86671fe | |||
2fb43d04cb | |||
dad7371b41 | |||
eaa9feb9a0 | |||
d12eff1a88 | |||
6069ff45c7 | |||
f121713ece | |||
165100d8ac | |||
ffb5d1205e | |||
805540c713 |
@@ -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,
|
||||
)
|
||||
|
@@ -754,6 +754,7 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeSwitch"),
|
||||
NodeItem("FunctionNodeRandomValue"),
|
||||
NodeItem("FunctionNodeAlignEulerToVector"),
|
||||
NodeItem("FunctionNodeEnum"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
|
||||
NodeItem("ShaderNodeVectorCurve"),
|
||||
|
@@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
* \{ */
|
||||
|
@@ -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", "")
|
||||
|
206
source/blender/nodes/function/nodes/node_fn_enum.cc
Normal file
206
source/blender/nodes/function/nodes/node_fn_enum.cc
Normal 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);
|
||||
}
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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
|
||||
* \{ */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user