Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_node.h10
-rw-r--r--source/blender/blenkernel/intern/node.cc14
-rw-r--r--source/blender/editors/include/ED_node.h3
-rw-r--r--source/blender/editors/interface/interface_widgets.c3
-rw-r--r--source/blender/editors/space_node/drawnode.cc128
-rw-r--r--source/blender/editors/space_node/node_draw.cc46
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc20
-rw-r--r--source/blender/editors/space_node/node_intern.h4
-rw-r--r--source/blender/editors/space_node/node_relationships.cc1
-rw-r--r--source/blender/editors/space_node/node_templates.cc39
-rw-r--r--source/blender/makesdna/DNA_node_types.h23
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c223
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc68
-rw-r--r--source/blender/nodes/CMakeLists.txt4
-rw-r--r--source/blender/nodes/NOD_attribute_ref.hh77
-rw-r--r--source/blender/nodes/NOD_geometry.h2
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh8
-rw-r--r--source/blender/nodes/NOD_static_types.h2
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc24
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc57
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_get.cc99
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc43
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_set.cc107
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc64
-rw-r--r--source/blender/nodes/intern/attribute_ref.cc67
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc36
-rw-r--r--source/blender/nodes/intern/node_socket.cc96
-rw-r--r--source/blender/nodes/intern/node_util.c7
-rw-r--r--source/blender/nodes/intern/type_conversions.cc52
32 files changed, 1302 insertions, 109 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 27f9edac731..fab387377b2 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -113,6 +113,7 @@ namespace blender {
namespace nodes {
class SocketMFNetworkBuilder;
class NodeMFNetworkBuilder;
+class GeoNodeResolveParams;
class GeoNodeExecParams;
} // namespace nodes
namespace fn {
@@ -122,6 +123,7 @@ class MFDataType;
} // namespace blender
using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder);
+using NodeGeometryResolveFunction = void (*)(blender::nodes::GeoNodeResolveParams params);
using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params);
using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)();
using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value);
@@ -130,6 +132,7 @@ using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetwork
#else
typedef void *NodeExpandInMFNetworkFunction;
typedef void *SocketExpandInMFNetworkFunction;
+typedef void *NodeGeometryResolveFunction;
typedef void *NodeGeometryExecFunction;
typedef void *SocketGetCPPTypeFunction;
typedef void *SocketGetCPPValueFunction;
@@ -152,6 +155,10 @@ typedef struct bNodeSocketType {
struct PointerRNA *ptr,
struct PointerRNA *node_ptr,
float *r_color);
+ void (*draw_shape)(struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PointerRNA *node_ptr,
+ eNodeSocketDisplayShape *r_display_shape);
void (*interface_draw)(struct bContext *C, struct uiLayout *layout, struct PointerRNA *ptr);
void (*interface_draw_color)(struct bContext *C, struct PointerRNA *ptr, float *r_color);
@@ -324,6 +331,7 @@ typedef struct bNodeType {
NodeExpandInMFNetworkFunction expand_in_mf_network;
/* Execute a geometry node. */
+ NodeGeometryResolveFunction geometry_node_resolve;
NodeGeometryExecFunction geometry_node_execute;
bool geometry_node_execute_supports_laziness;
@@ -1443,6 +1451,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_PRIMITIVE_SPIRAL 1063
#define GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER 1064
#define GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT 1065
+#define GEO_NODE_ATTRIBUTE_GET 1066
+#define GEO_NODE_ATTRIBUTE_SET 1067
/** \} */
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 87b069d7c50..ce76562b3af 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -323,6 +323,7 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
+ case SOCK_ATTRIBUTE:
break;
}
}
@@ -450,6 +451,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_ATTRIBUTE:
+ BLO_write_struct(writer, bNodeSocketValueAttribute, sock->default_value);
+ break;
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
@@ -856,6 +860,7 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
+ case SOCK_ATTRIBUTE:
break;
}
}
@@ -951,6 +956,7 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
+ case SOCK_ATTRIBUTE:
break;
}
}
@@ -1528,6 +1534,7 @@ static void socket_id_user_increment(bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
+ case SOCK_ATTRIBUTE:
break;
}
}
@@ -1581,6 +1588,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
+ case SOCK_ATTRIBUTE:
break;
}
}
@@ -1720,6 +1728,8 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketTexture";
case SOCK_MATERIAL:
return "NodeSocketMaterial";
+ case SOCK_ATTRIBUTE:
+ return "NodeSocketAttribute";
}
return nullptr;
}
@@ -1797,6 +1807,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceTexture";
case SOCK_MATERIAL:
return "NodeSocketInterfaceMaterial";
+ case SOCK_ATTRIBUTE:
+ return "NodeSocketInterfaceAttribute";
}
return nullptr;
}
@@ -5039,12 +5051,14 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_convert();
register_node_type_geo_attribute_curve_map();
register_node_type_geo_attribute_fill();
+ register_node_type_geo_attribute_get();
register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
register_node_type_geo_attribute_mix();
register_node_type_geo_attribute_proximity();
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_separate_xyz();
+ register_node_type_geo_attribute_set();
register_node_type_geo_attribute_transfer();
register_node_type_geo_attribute_vector_math();
register_node_type_geo_attribute_vector_rotate();
diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h
index ba65840dc99..ee2fbbe4ff5 100644
--- a/source/blender/editors/include/ED_node.h
+++ b/source/blender/editors/include/ED_node.h
@@ -89,7 +89,8 @@ void ED_node_draw_snap(
void ED_node_socket_draw(struct bNodeSocket *sock,
const struct rcti *rect,
const float color[4],
- float scale);
+ float scale,
+ char display_shape);
void ED_node_tree_update(const struct bContext *C);
void ED_node_tag_update_id(struct ID *id);
void ED_node_tag_update_nodetree(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 91a4c430023..b4deb0a554b 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -36,6 +36,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
+#include "BKE_node.h"
#include "RNA_access.h"
@@ -2308,7 +2309,7 @@ static void widget_draw_node_link_socket(const uiWidgetColors *wcol,
UI_widgetbase_draw_cache_flush();
GPU_blend(GPU_BLEND_NONE);
- ED_node_socket_draw(but->custom_data, rect, col, scale);
+ ED_node_socket_draw(but->custom_data, rect, col, scale, SOCK_DISPLAY_SHAPE_CIRCLE);
}
else {
widget_draw_icon(but, ICON_LAYER_USED, alpha, rect, wcol->text);
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index 1c7ee9e622f..d99433b47a8 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -3345,7 +3345,7 @@ static void node_socket_undefined_draw(bContext *UNUSED(C),
}
static void node_socket_undefined_draw_color(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
+ PointerRNA *ptr,
PointerRNA *UNUSED(node_ptr),
float *r_color)
{
@@ -3455,6 +3455,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 */
+ {1.0, 0.0, 1.0, 1.0}, /* SOCK_ATTRIBUTE (placeholder, copies from other types) */
};
/* common color callbacks for standard types */
@@ -3465,15 +3466,43 @@ static void std_node_socket_draw_color(bContext *UNUSED(C),
{
bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
- copy_v4_v4(r_color, std_node_socket_colors[type]);
+ if (type != SOCK_ATTRIBUTE) {
+ copy_v4_v4(r_color, std_node_socket_colors[type]);
+ }
+ else {
+ int data_type = ((bNodeSocketValueAttribute *)sock->default_value)->data_type;
+ copy_v4_v4(r_color, std_node_socket_colors[data_type]);
+ }
}
+
+static void std_node_socket_draw_shape(bContext *UNUSED(C),
+ PointerRNA *ptr,
+ PointerRNA *UNUSED(node_ptr),
+ eNodeSocketDisplayShape *r_display_shape)
+{
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
+ int type = sock->typeinfo->type;
+ if (type != SOCK_ATTRIBUTE) {
+ *r_display_shape = (eNodeSocketDisplayShape)sock->display_shape;
+ }
+ else {
+ *r_display_shape = SOCK_DISPLAY_SHAPE_SQUARE;
+ }
+}
+
static void std_node_socket_interface_draw_color(bContext *UNUSED(C),
PointerRNA *ptr,
float *r_color)
{
bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
- copy_v4_v4(r_color, std_node_socket_colors[type]);
+ if (type != SOCK_ATTRIBUTE) {
+ copy_v4_v4(r_color, std_node_socket_colors[type]);
+ }
+ else {
+ int data_type = ((bNodeSocketValueAttribute *)sock->default_value)->data_type;
+ copy_v4_v4(r_color, std_node_socket_colors[data_type]);
+ }
}
/* draw function for file output node sockets,
@@ -3577,7 +3606,7 @@ static void std_node_socket_draw(
const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id;
if (node_tree->type == NTREE_GEOMETRY) {
- node_geometry_add_attribute_search_button(C, node_tree, node, ptr, row);
+ node_geometry_add_attribute_search_button(C, node_tree, node, ptr, "default_value", row);
}
else {
uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0);
@@ -3606,6 +3635,60 @@ static void std_node_socket_draw(
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
+ case SOCK_ATTRIBUTE: {
+ const int data_type = ((bNodeSocketValueAttribute *)sock->default_value)->data_type;
+ const bool use_attribute_name = ((bNodeSocketValueAttribute *)sock->default_value)->flag &
+ SOCK_ATTRIBUTE_USE_NAME;
+
+ uiLayout *row = uiLayoutRow(layout, false);
+ if (use_attribute_name) {
+ uiLayout *split = uiLayoutSplit(row, 0.4f, false);
+ uiItemL(split, text, 0);
+
+ const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id;
+ if (node_tree->type == NTREE_GEOMETRY) {
+ node_geometry_add_attribute_search_button(C, node_tree, node, ptr, "attribute_name", split);
+ }
+ else {
+ uiItemR(split, ptr, "attribute_name", DEFAULT_FLAGS, "", 0);
+ }
+ }
+ else {
+ switch (data_type) {
+ case SOCK_FLOAT:
+ uiItemR(row, ptr, "default_value_float", DEFAULT_FLAGS, text, 0);
+ break;
+ case SOCK_INT:
+ uiItemR(row, ptr, "default_value_int", DEFAULT_FLAGS, text, 0);
+ break;
+ case SOCK_BOOLEAN:
+ uiItemR(row, ptr, "default_value_bool", DEFAULT_FLAGS, text, 0);
+ break;
+ case SOCK_VECTOR:
+ if (sock->flag & SOCK_COMPACT) {
+ uiTemplateComponentMenu(row, ptr, "default_value_vector", text);
+ }
+ else {
+ if (sock->typeinfo->subtype == PROP_DIRECTION) {
+ uiItemR(row, ptr, "default_value_vector", DEFAULT_FLAGS, "", ICON_NONE);
+ }
+ else {
+ uiLayout *column = uiLayoutColumn(row, true);
+ uiItemR(column, ptr, "default_value_vector", DEFAULT_FLAGS, text, ICON_NONE);
+ }
+ }
+ break;
+ case SOCK_RGBA: {
+ uiLayout *split = uiLayoutSplit(row, 0.4f, false);
+ uiItemL(split, text, 0);
+ uiItemR(split, ptr, "default_value_color", DEFAULT_FLAGS, "", 0);
+ break;
+ }
+ }
+ }
+ uiItemR(row, ptr, "use_attribute_name", 0, "", ICON_VIEWZOOM);
+ break;
+ }
default:
node_socket_button_label(C, layout, ptr, node_ptr, text);
break;
@@ -3647,6 +3730,40 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), 0);
break;
}
+ case SOCK_ATTRIBUTE: {
+ int data_type = ((bNodeSocketValueAttribute *)sock->default_value)->data_type;
+ switch (data_type) {
+ case SOCK_FLOAT: {
+ uiItemR(col, ptr, "default_value_float", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
+ uiLayout *sub = uiLayoutColumn(col, true);
+ uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
+ uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
+ break;
+ }
+ case SOCK_INT: {
+ uiItemR(col, ptr, "default_value_int", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
+ uiLayout *sub = uiLayoutColumn(col, true);
+ uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
+ uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
+ break;
+ }
+ case SOCK_VECTOR: {
+ uiItemR(col, ptr, "default_value_vector", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE);
+ uiLayout *sub = uiLayoutColumn(col, true);
+ uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
+ uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
+ break;
+ }
+ case SOCK_BOOLEAN:
+ uiItemR(col, ptr, "default_value_bool", DEFAULT_FLAGS, IFACE_("Default"), 0);
+ break;
+ case SOCK_RGBA: {
+ uiItemR(col, ptr, "default_value_color", DEFAULT_FLAGS, IFACE_("Default"), 0);
+ break;
+ }
+ }
+ break;
+ }
}
uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, nullptr, 0);
@@ -3656,12 +3773,13 @@ void ED_init_standard_node_socket_type(bNodeSocketType *stype)
{
stype->draw = std_node_socket_draw;
stype->draw_color = std_node_socket_draw_color;
+ stype->draw_shape = std_node_socket_draw_shape;
stype->interface_draw = std_node_socket_interface_draw;
stype->interface_draw_color = std_node_socket_interface_draw_color;
}
static void node_socket_virtual_draw_color(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
+ PointerRNA *ptr,
PointerRNA *UNUSED(node_ptr),
float *r_color)
{
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index b9dad060121..80b5975ce30 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -717,9 +717,9 @@ static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const
#define MARKER_SHAPE_CIRCLE 0x2
#define MARKER_SHAPE_INNER_DOT 0x10
-static void node_socket_draw(const bNodeSocket *sock,
- const float color[4],
+static void node_socket_draw(const float color[4],
const float color_outline[4],
+ const eNodeSocketDisplayShape display_shape,
float size,
int locx,
int locy,
@@ -732,7 +732,7 @@ static void node_socket_draw(const bNodeSocket *sock,
int flags;
/* Set shape flags. */
- switch (sock->display_shape) {
+ switch (display_shape) {
case SOCK_DISPLAY_SHAPE_DIAMOND:
case SOCK_DISPLAY_SHAPE_DIAMOND_DOT:
flags = MARKER_SHAPE_DIAMOND;
@@ -748,7 +748,7 @@ static void node_socket_draw(const bNodeSocket *sock,
break;
}
- if (ELEM(sock->display_shape,
+ if (ELEM(display_shape,
SOCK_DISPLAY_SHAPE_DIAMOND_DOT,
SOCK_DISPLAY_SHAPE_SQUARE_DOT,
SOCK_DISPLAY_SHAPE_CIRCLE_DOT)) {
@@ -764,6 +764,7 @@ static void node_socket_draw(const bNodeSocket *sock,
static void node_socket_draw_multi_input(const float color[4],
const float color_outline[4],
+ const eNodeSocketDisplayShape UNUSED(display_shape),
const float width,
const float height,
const int locx,
@@ -807,14 +808,24 @@ static void node_socket_outline_color_get(const bool selected,
/* Usual convention here would be node_socket_get_color(), but that's already used (for setting a
* color property socket). */
-void node_socket_color_get(
- bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4])
+void node_socket_color_get(bContext *C,
+ bNodeTree *ntree,
+ PointerRNA *node_ptr,
+ bNodeSocket *sock,
+ float r_color[4],
+ eNodeSocketDisplayShape *r_display_shape)
{
PointerRNA ptr;
BLI_assert(RNA_struct_is_a(node_ptr->type, &RNA_Node));
RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
sock->typeinfo->draw_color(C, &ptr, node_ptr, r_color);
+ if (sock->typeinfo->draw_shape) {
+ sock->typeinfo->draw_shape(C, &ptr, node_ptr, r_display_shape);
+ }
+ else {
+ *r_display_shape = (eNodeSocketDisplayShape)sock->display_shape;
+ }
bNode *node = (bNode *)node_ptr->data;
if (node->flag & NODE_MUTED) {
@@ -836,13 +847,14 @@ static void node_socket_draw_nested(const bContext *C,
{
float color[4];
float outline_color[4];
+ eNodeSocketDisplayShape display_shape;
- node_socket_color_get((bContext *)C, ntree, node_ptr, sock, color);
+ node_socket_color_get((bContext *)C, ntree, node_ptr, sock, color, &display_shape);
node_socket_outline_color_get(selected, sock->type, outline_color);
- node_socket_draw(sock,
- color,
+ node_socket_draw(color,
outline_color,
+ display_shape,
size,
sock->locx,
sock->locy,
@@ -858,7 +870,11 @@ static void node_socket_draw_nested(const bContext *C,
* \note this is only called from external code, internally #node_socket_draw_nested() is used for
* optimized drawing of multiple/all sockets of a node.
*/
-void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale)
+void ED_node_socket_draw(bNodeSocket *sock,
+ const rcti *rect,
+ const float color[4],
+ float scale,
+ char display_shape)
{
const float size = 2.25f * NODE_SOCKSIZE * scale;
rcti draw_rect = *rect;
@@ -886,9 +902,9 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[
/* Single point. */
immBegin(GPU_PRIM_POINTS, 1);
- node_socket_draw(sock,
- color,
+ node_socket_draw(color,
outline_color,
+ (eNodeSocketDisplayShape)display_shape,
BLI_rcti_size_y(&draw_rect),
BLI_rcti_cent_x(&draw_rect),
BLI_rcti_cent_y(&draw_rect),
@@ -1178,10 +1194,12 @@ void node_draw_sockets(const View2D *v2d,
float color[4];
float outline_color[4];
- node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color);
+ eNodeSocketDisplayShape display_shape;
+ node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color, &display_shape);
node_socket_outline_color_get(selected, socket->type, outline_color);
- node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy);
+ node_socket_draw_multi_input(
+ color, outline_color, display_shape, width, height, socket->locx, socket->locy);
}
}
diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc
index 94080a7b616..e92449e3535 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -140,8 +140,19 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
AvailableAttributeInfo *item = static_cast<AvailableAttributeInfo *>(item_v);
bNodeSocket &socket = data->socket;
- bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value);
- BLI_strncpy(value->value, item->name.c_str(), MAX_NAME);
+ switch (socket.type) {
+ case SOCK_STRING: {
+ bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value);
+ BLI_strncpy(value->value, item->name.c_str(), MAX_NAME);
+ break;
+ }
+ case SOCK_ATTRIBUTE: {
+ bNodeSocketValueAttribute *value = static_cast<bNodeSocketValueAttribute *>(
+ socket.default_value);
+ BLI_strncpy(value->attribute_name, item->name.c_str(), MAX_NAME);
+ break;
+ }
+ }
ED_undo_push(C, "Assign Attribute Name");
}
@@ -150,13 +161,14 @@ void node_geometry_add_attribute_search_button(const bContext *C,
const bNodeTree *node_tree,
const bNode *node,
PointerRNA *socket_ptr,
+ const char *propname,
uiLayout *layout)
{
const NodeUIStorage *ui_storage = BKE_node_tree_ui_storage_get_from_context(
C, *node_tree, *node);
if (ui_storage == nullptr) {
- uiItemR(layout, socket_ptr, "default_value", 0, "", 0);
+ uiItemR(layout, socket_ptr, propname, 0, "", 0);
return;
}
@@ -173,7 +185,7 @@ void node_geometry_add_attribute_search_button(const bContext *C,
10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
UI_UNIT_Y,
socket_ptr,
- "default_value",
+ propname,
0,
0.0f,
0.0f,
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index 8dfc43333e3..73de87b8c14 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -125,7 +125,8 @@ void node_socket_color_get(struct bContext *C,
struct bNodeTree *ntree,
struct PointerRNA *node_ptr,
struct bNodeSocket *sock,
- float r_color[4]);
+ float r_color[4],
+ eNodeSocketDisplayShape *r_display_shape);
void node_update_nodetree(const struct bContext *C, struct bNodeTree *ntree);
void node_draw_nodetree(const struct bContext *C,
struct ARegion *region,
@@ -318,6 +319,7 @@ void node_geometry_add_attribute_search_button(const struct bContext *C,
const struct bNodeTree *node_tree,
const struct bNode *node,
struct PointerRNA *socket_ptr,
+ const char *propname,
struct uiLayout *layout);
extern const char *node_context_dir[];
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index a1b8e4e3395..5c251b47746 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -1976,6 +1976,7 @@ static int get_main_socket_priority(const bNodeSocket *socket)
case SOCK_COLLECTION:
case SOCK_TEXTURE:
case SOCK_MATERIAL:
+ case SOCK_ATTRIBUTE:
return 5;
}
return -1;
diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc
index fae180ca6c5..93599bc3c2a 100644
--- a/source/blender/editors/space_node/node_templates.cc
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -685,6 +685,7 @@ void uiTemplateNodeLink(
NodeLinkArg *arg;
uiBut *but;
float socket_col[4];
+ eNodeSocketDisplayShape display_shape;
arg = (NodeLinkArg *)MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg");
arg->ntree = ntree;
@@ -693,7 +694,7 @@ void uiTemplateNodeLink(
PointerRNA node_ptr;
RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr);
- node_socket_color_get(C, ntree, &node_ptr, input, socket_col);
+ node_socket_color_get(C, ntree, &node_ptr, input, socket_col, &display_shape);
UI_block_layout_set_current(block, layout);
@@ -849,7 +850,7 @@ static void ui_node_draw_input(
case SOCK_STRING: {
const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id;
if (node_tree->type == NTREE_GEOMETRY) {
- node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, row);
+ node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, "default_value", row);
}
else {
uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE);
@@ -858,6 +859,40 @@ static void ui_node_draw_input(
split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX);
break;
}
+ case SOCK_ATTRIBUTE: {
+ int data_type = ((bNodeSocketValueAttribute *)input->default_value)->data_type;
+ switch (data_type) {
+ case SOCK_VECTOR:
+ if (input->type == SOCK_VECTOR) {
+ uiItemS(row);
+ sub = uiLayoutColumn(row, true);
+ }
+ uiItemR(sub, &inputptr, "default_value_vector", 0, "", ICON_NONE);
+ uiItemDecoratorR(
+ split_wrapper.decorate_column, &inputptr, "default_value_vector", RNA_NO_INDEX);
+ case SOCK_FLOAT:
+ uiItemR(sub, &inputptr, "default_value_float", 0, "", ICON_NONE);
+ uiItemDecoratorR(
+ split_wrapper.decorate_column, &inputptr, "default_value_float", RNA_NO_INDEX);
+ break;
+ case SOCK_INT:
+ uiItemR(sub, &inputptr, "default_value_int", 0, "", ICON_NONE);
+ uiItemDecoratorR(
+ split_wrapper.decorate_column, &inputptr, "default_value_int", RNA_NO_INDEX);
+ break;
+ case SOCK_BOOLEAN:
+ uiItemR(sub, &inputptr, "default_value_bool", 0, "", ICON_NONE);
+ uiItemDecoratorR(
+ split_wrapper.decorate_column, &inputptr, "default_value_bool", RNA_NO_INDEX);
+ break;
+ case SOCK_RGBA:
+ uiItemR(sub, &inputptr, "default_value_color", 0, "", ICON_NONE);
+ uiItemDecoratorR(
+ split_wrapper.decorate_column, &inputptr, "default_value_color", RNA_NO_INDEX);
+ break;
+ }
+ break;
+ }
default:
add_dummy_decorator = true;
}
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 480a8c03c41..2a639ad85d9 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -169,6 +169,7 @@ typedef enum eNodeSocketDatatype {
SOCK_COLLECTION = 11,
SOCK_TEXTURE = 12,
SOCK_MATERIAL = 13,
+ SOCK_ATTRIBUTE = 14,
} eNodeSocketDatatype;
/* Socket shape. */
@@ -617,6 +618,28 @@ typedef struct bNodeSocketValueMaterial {
struct Material *value;
} bNodeSocketValueMaterial;
+typedef struct bNodeSocketValueAttribute {
+ int data_type; /* eNodeSocketDatatype */
+ int flag; /* eNodeSocketAttributeFlag */
+
+ /* XXX Does DNA support union? */
+ float value_float[4];
+ int value_int;
+ char value_bool;
+ char _pad[3];
+ float min, max;
+
+ /** 1024 = FILEMAX. */
+ char attribute_name[1024];
+} bNodeSocketValueAttribute;
+
+typedef enum eNodeSocketAttributeFlag {
+ /* Use the name string to look up an attribute when not connected,
+ * instead of a default value.
+ */
+ SOCK_ATTRIBUTE_USE_NAME = (1 << 0),
+} eNodeSocketAttributeFlag;
+
/* Data structs, for node->storage. */
enum {
CMP_NODE_MASKTYPE_ADD = 0,
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index b3f46509955..b2743ca60d2 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -78,6 +78,18 @@ static const EnumPropertyItem node_socket_data_type_items[] = {
{SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
{SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
{SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
+ {SOCK_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+/* Subset of socket data types that are supported by attributes. */
+static const EnumPropertyItem node_socket_attribute_data_type_items[] = {
+ {SOCK_FLOAT, "FLOAT", 0, "Float", ""},
+ {SOCK_INT, "INT", 0, "Integer", ""},
+ {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
+ {SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
+ {SOCK_RGBA, "RGBA", 0, "Color", ""},
+ {SOCK_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -106,6 +118,7 @@ static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
{SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
{SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
+ {SOCK_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -2001,6 +2014,7 @@ static bool switch_type_supported(const EnumPropertyItem *item)
SOCK_STRING,
SOCK_RGBA,
SOCK_GEOMETRY,
+ SOCK_ATTRIBUTE,
SOCK_OBJECT,
SOCK_COLLECTION,
SOCK_TEXTURE,
@@ -2232,6 +2246,20 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeCurveMap_type_itemf(
return itemf_function_check(rna_enum_attribute_type_items, attribute_curve_map_type_supported);
}
+static bool attribute_get_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(
+ item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL, CD_PROP_INT32);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeGet_type_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_type_items, attribute_get_type_supported);
+}
+
static StructRNA *rna_ShaderNode_register(Main *bmain,
ReportList *reports,
void *data,
@@ -3245,6 +3273,38 @@ static void rna_NodeSocketStandard_vector_range(
*softmax = dval->max;
}
+static void rna_NodeSocketStandard_attribute_range_float(
+ PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
+{
+ bNodeSocket *sock = ptr->data;
+ bNodeSocketValueAttribute *dval = sock->default_value;
+
+ if (dval->max < dval->min) {
+ dval->max = dval->min;
+ }
+
+ *min = -FLT_MAX;
+ *max = FLT_MAX;
+ *softmin = dval->min;
+ *softmax = dval->max;
+}
+
+static void rna_NodeSocketStandard_attribute_range_int(
+ PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
+{
+ bNodeSocket *sock = ptr->data;
+ bNodeSocketValueAttribute *dval = sock->default_value;
+
+ if (dval->max < dval->min) {
+ dval->max = dval->min;
+ }
+
+ *min = INT_MIN;
+ *max = INT_MAX;
+ *softmin = (int)dval->min;
+ *softmax = (int)dval->max;
+}
+
/* using a context update function here, to avoid searching the node if possible */
static void rna_NodeSocketStandard_value_update(struct bContext *C, PointerRNA *ptr)
{
@@ -10013,6 +10073,19 @@ static void def_geo_raycast(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_attribute_get(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeGet_type_itemf");
+ RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -10640,6 +10713,154 @@ static void rna_def_node_socket_string(BlenderRNA *brna,
RNA_def_struct_sdna_from(srna, "bNodeSocket", NULL);
}
+static void rna_def_node_socket_attribute(BlenderRNA *brna,
+ const char *identifier,
+ const char *interface_idname)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ PropertySubType subtype = PROP_NONE;
+
+ srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
+ RNA_def_struct_ui_text(srna, "Attribute Node Socket", "Attribute socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueAttribute", "default_value");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, node_socket_attribute_data_type_items);
+ RNA_def_property_enum_default(prop, SOCK_ATTRIBUTE);
+ RNA_def_property_ui_text(
+ prop, "Attribute Data Type", "Type of data stored in attribute elements");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update");
+ RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
+
+ prop = RNA_def_property(srna, "use_attribute_name", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SOCK_ATTRIBUTE_USE_NAME);
+ RNA_def_property_ui_text(prop, "Use Attribute Name", "Use the name string to look up an attribute when not connected");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update");
+ RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
+
+ prop = RNA_def_property(srna, "default_value_float", PROP_FLOAT, subtype);
+ RNA_def_property_float_sdna(prop, NULL, "value_float[0]");
+ // RNA_def_property_float_array_default(prop, value_default);
+ RNA_def_property_float_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_float");
+ 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);
+
+ prop = RNA_def_property(srna, "default_value_int", PROP_INT, subtype);
+ RNA_def_property_int_sdna(prop, NULL, "value_int");
+ // RNA_def_property_float_array_default(prop, value_default);
+ RNA_def_property_int_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_int");
+ 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);
+
+ prop = RNA_def_property(srna, "default_value_bool", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "value_bool", 1);
+ // RNA_def_property_float_array_default(prop, value_default);
+ 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);
+
+ prop = RNA_def_property(srna, "default_value_vector", PROP_FLOAT, subtype);
+ RNA_def_property_float_sdna(prop, NULL, "value_float");
+ RNA_def_property_array(prop, 3);
+ // RNA_def_property_float_array_default(prop, value_default);
+ RNA_def_property_float_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_float");
+ 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);
+
+ prop = RNA_def_property(srna, "default_value_color", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_float_sdna(prop, NULL, "value_float");
+ 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);
+
+ prop = RNA_def_property(srna, "attribute_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "attribute_name");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Attribute Name", "Name of an attribute");
+ 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, "Attribute Node Socket Interface", "Attribute socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueAttribute", "default_value");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, node_socket_attribute_data_type_items);
+ RNA_def_property_enum_default(prop, SOCK_ATTRIBUTE);
+ RNA_def_property_ui_text(
+ prop, "Attribute Data Type", "Type of data stored in attribute elements");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+
+ prop = RNA_def_property(srna, "default_value_float", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "value_float");
+ // RNA_def_property_float_default(prop, value_default);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_float_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_float");
+ 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");
+
+ prop = RNA_def_property(srna, "default_value_int", PROP_INT, subtype);
+ RNA_def_property_int_sdna(prop, NULL, "value_int");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_int_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_int");
+ 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");
+
+ prop = RNA_def_property(srna, "default_value_bool", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "value_bool", 1);
+ 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");
+
+ prop = RNA_def_property(srna, "default_value_vector", PROP_FLOAT, subtype);
+ RNA_def_property_float_sdna(prop, NULL, "value_float");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_float_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_float");
+ 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");
+
+ prop = RNA_def_property(srna, "default_value_color", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_float_sdna(prop, NULL, "value_float");
+ 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");
+
+ prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "min");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+
+ prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "max");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+
+ prop = RNA_def_property(srna, "attribute_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "attribute_name");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Attribute Name", "Name of an attribute");
+ 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_shader(BlenderRNA *brna,
const char *identifier,
const char *interface_idname)
@@ -10996,6 +11217,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_string(brna, "NodeSocketString", "NodeSocketInterfaceString");
+ rna_def_node_socket_attribute(brna, "NodeSocketAttribute", "NodeSocketInterfaceAttribute");
+
rna_def_node_socket_shader(brna, "NodeSocketShader", "NodeSocketInterfaceShader");
rna_def_node_socket_virtual(brna, "NodeSocketVirtual");
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index 8314a443ba6..c2f2f358b9e 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -34,6 +34,7 @@ namespace blender::modifiers::geometry_nodes {
using fn::CPPType;
using fn::GValueMap;
+using nodes::GeoNodeResolveParams;
using nodes::GeoNodeExecParams;
using namespace fn::multi_function_types;
@@ -202,6 +203,11 @@ struct NodeState {
bool has_been_executed = false;
/**
+ * Used to check that nodes are not resolved more than once.
+ */
+ bool has_been_resolved = false;
+
+ /**
* Becomes true when the node will never be executed again and its inputs are destructed.
* Generally, a node has finished once all of its outputs with (potential) users have been
* computed.
@@ -384,6 +390,7 @@ class GeometryNodesEvaluator {
task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH);
this->create_states_for_reachable_nodes();
+ this->resolve_runtime_types();
this->forward_group_inputs();
this->schedule_initial_nodes();
@@ -436,6 +443,42 @@ class GeometryNodesEvaluator {
});
}
+ /**
+ * Back-propagate base type, domain, etc. for automatic sockets.
+ * Output nodes are resolved first, so their inputs can determine
+ * the most appropriate type details among all the targets.
+ */
+ void resolve_runtime_types()
+ {
+ /* Breadth first topological starting at the output side. */
+ /* TODO find a better FIFO container */
+ Vector<DNode> nodes_to_check;
+ VectorSet<DNode> resolved_nodes;
+ /* Start at the output sockets. */
+ for (const DInputSocket &socket : params_.output_sockets) {
+ nodes_to_check.prepend(socket.node());
+ }
+ while (!nodes_to_check.is_empty()) {
+ const DNode node = nodes_to_check.pop_last();
+ if (resolved_nodes.contains(node)) {
+ /* This node has been handled already. */
+ continue;
+ }
+ resolved_nodes.add(node);
+
+ /* Resolve node socket types. */
+ NodeState *node_state = node_states_.lookup_key_as(node).state;
+ this->resolve_node_runtime_types(node, *node_state);
+
+ /* Push all linked origins on the stack. */
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+ input.foreach_origin_socket(
+ [&](const DSocket origin) { nodes_to_check.prepend(origin.node()); });
+ }
+ }
+ }
+
void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator)
{
/* Construct arrays of the correct size. */
@@ -549,6 +592,31 @@ class GeometryNodesEvaluator {
node_state.~NodeState();
}
+ void resolve_node_runtime_types(const DNode node, NodeState &node_state)
+ {
+ const bNode &bnode = *node->bnode();
+
+ if (node_state.has_been_resolved) {
+ /* Nodes must not be resolved more than once. */
+ BLI_assert_unreachable();
+ }
+ node_state.has_been_resolved = true;
+
+ /* Use the geometry node resolve callback if it exists. */
+ if (bnode.typeinfo->geometry_node_resolve != nullptr) {
+ this->resolve_geometry_node(node, node_state);
+ return;
+ }
+ }
+
+ void resolve_geometry_node(const DNode node, NodeState &node_state)
+ {
+ const bNode &bnode = *node->bnode();
+
+ GeoNodeResolveParams params;
+ bnode.typeinfo->geometry_node_resolve(params);
+ }
+
void forward_group_inputs()
{
for (auto &&item : params_.input_values.items()) {
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 926a81e87fd..d2151e79c56 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -147,6 +147,7 @@ set(SRC
geometry/nodes/node_geo_attribute_convert.cc
geometry/nodes/node_geo_attribute_curve_map.cc
geometry/nodes/node_geo_attribute_fill.cc
+ geometry/nodes/node_geo_attribute_get.cc
geometry/nodes/node_geo_attribute_map_range.cc
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_mix.cc
@@ -155,6 +156,7 @@ set(SRC
geometry/nodes/node_geo_attribute_remove.cc
geometry/nodes/node_geo_attribute_sample_texture.cc
geometry/nodes/node_geo_attribute_separate_xyz.cc
+ geometry/nodes/node_geo_attribute_set.cc
geometry/nodes/node_geo_attribute_transfer.cc
geometry/nodes/node_geo_attribute_vector_math.cc
geometry/nodes/node_geo_attribute_vector_rotate.cc
@@ -331,6 +333,7 @@ set(SRC
intern/derived_node_tree.cc
intern/math_functions.cc
+ intern/attribute_ref.cc
intern/node_common.c
intern/node_exec.cc
intern/node_geometry_exec.cc
@@ -347,6 +350,7 @@ set(SRC
geometry/node_geometry_util.hh
texture/node_texture_util.h
+ NOD_attribute_ref.hh
NOD_common.h
NOD_composite.h
NOD_derived_node_tree.hh
diff --git a/source/blender/nodes/NOD_attribute_ref.hh b/source/blender/nodes/NOD_attribute_ref.hh
new file mode 100644
index 00000000000..529a98da727
--- /dev/null
+++ b/source/blender/nodes/NOD_attribute_ref.hh
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "BLI_color.hh"
+#include "BLI_float3.hh"
+
+#include "FN_cpp_type.hh"
+
+#include "BKE_attribute.h"
+#include "BKE_attribute_access.hh"
+
+/**
+ * Runtime data struct that references attributes during evaluation of geometry node trees.
+ * Attributes are identified by a name and a domain type.
+ */
+struct AttributeRef {
+ private:
+ std::string name_;
+ CustomDataType data_type_;
+
+ /* Single value to use when the socket is not connected. */
+ union {
+ float value_float_;
+ int value_int_;
+ bool value_bool_;
+ blender::float3 value_float3_;
+ blender::ColorGeometry4f value_color_;
+ };
+
+ public:
+ static const AttributeRef None;
+
+ public:
+ const std::string &name() const;
+ CustomDataType data_type() const;
+
+ AttributeRef();
+ AttributeRef(CustomDataType data_type);
+ AttributeRef(const std::string &name, CustomDataType data_type);
+
+ friend std::ostream &operator<<(std::ostream &stream, const AttributeRef &geometry_set);
+
+ bool valid() const;
+
+ void *single_value_ptr();
+ const void *single_value_ptr() const;
+
+ template<typename T> T &single_value()
+ {
+ BLI_assert(blender::fn::CPPType::get<T>() ==
+ *blender::bke::custom_data_type_to_cpp_type(data_type_));
+ return *(T *)single_value_ptr();
+ }
+ template<typename T> const T &single_value() const
+ {
+ BLI_assert(blender::fn::CPPType::get<T>() ==
+ *blender::bke::custom_data_type_to_cpp_type(data_type_));
+ return *(const T *)single_value_ptr();
+ }
+};
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index ba646e7654a..7251c91710b 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -37,6 +37,7 @@ void register_node_type_geo_attribute_compare(void);
void register_node_type_geo_attribute_convert(void);
void register_node_type_geo_attribute_curve_map(void);
void register_node_type_geo_attribute_fill(void);
+void register_node_type_geo_attribute_get(void);
void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
@@ -47,6 +48,7 @@ void register_node_type_geo_attribute_transfer(void);
void register_node_type_geo_attribute_vector_math(void);
void register_node_type_geo_attribute_vector_rotate(void);
void register_node_type_geo_attribute_remove(void);
+void register_node_type_geo_attribute_set(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_bounding_box(void);
void register_node_type_geo_collection_info(void);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index 23474201daa..caefccfdba0 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -25,6 +25,7 @@
#include "DNA_node_types.h"
+#include "NOD_attribute_ref.hh"
#include "NOD_derived_node_tree.hh"
struct Depsgraph;
@@ -110,6 +111,9 @@ class GeoNodeExecParamsProvider {
virtual bool lazy_output_is_required(StringRef identifier) const = 0;
};
+class GeoNodeResolveParams {
+};
+
class GeoNodeExecParams {
private:
GeoNodeExecParamsProvider *provider_;
@@ -289,6 +293,10 @@ class GeoNodeExecParams {
const GeometryComponent &component,
const AttributeDomain default_domain) const;
+ /* Create an attribute reference with a unique name to use for an output attribute. */
+ AttributeRef declare_output_attribute(const StringRef identifier,
+ CustomDataType data_type) const;
+
private:
/* Utilities for detecting common errors at when using this class. */
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 19256f7383a..12008a84b3d 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -275,6 +275,8 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_co
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_GET, def_geo_attribute_get, "ATTRIBUTE_GET", AttributeGet, "Attribute Get", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SET, 0, "ATTRIBUTE_SET", AttributeSet, "Attribute Set", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index f4cd00b88ed..f36d5b3c9ca 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -20,6 +20,8 @@
#include "NOD_geometry.h"
+#include "BLI_listbase.h"
+
#include "BKE_context.h"
#include "BKE_node.h"
#include "BKE_object.h"
@@ -86,10 +88,21 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
{
- /* Geometry, string, object, material, texture and collection sockets can only be connected to
- * themselves. The other types can be converted between each other. */
- if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) &&
- ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) {
+ /* Temporary exception: For the time being allow direct conversion of attributes to strings
+ * to facilitate use of new attribute sockets with old-style attribute name inputs. */
+ if (link->fromsock->type == SOCK_ATTRIBUTE && link->tosock->type == SOCK_STRING) {
+ return true;
+ }
+
+ /* Geometry, attribute, string, object, material, texture and collection sockets can only be
+ * connected to themselves. Basic data types can be converted between each other. Basic data
+ * types can be connected to attributes */
+ bool from_elemental = ELEM(
+ link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT);
+ bool to_elemental = ELEM(
+ link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT);
+ bool to_attribute = (link->tosock->type == SOCK_ATTRIBUTE);
+ if (from_elemental && (to_elemental || to_attribute)) {
return true;
}
return (link->tosock->type == link->fromsock->type);
@@ -109,7 +122,8 @@ static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type
SOCK_GEOMETRY,
SOCK_COLLECTION,
SOCK_TEXTURE,
- SOCK_MATERIAL);
+ SOCK_MATERIAL,
+ SOCK_ATTRIBUTE);
}
void register_node_tree_type_geo(void)
diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
index 46e9d36c09c..d253a27ae59 100644
--- a/source/blender/nodes/geometry/node_geometry_util.cc
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -46,6 +46,7 @@ void update_attribute_input_socket_availabilities(bNode &node,
const bool socket_is_available =
name_is_available &&
((socket->type == SOCK_STRING && mode_ == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) ||
+ (socket->type == SOCK_ATTRIBUTE && mode_ == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) ||
(socket->type == SOCK_FLOAT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ||
(socket->type == SOCK_INT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_INTEGER) ||
(socket->type == SOCK_VECTOR && mode_ == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) ||
@@ -55,6 +56,62 @@ void update_attribute_input_socket_availabilities(bNode &node,
}
}
+void set_attribute_socket_data_type(bNode &node,
+ const StringRef name,
+ eNodeSocketDatatype data_type)
+{
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
+ if (socket->type == SOCK_ATTRIBUTE && name == socket->name) {
+ ((bNodeSocketValueAttribute *)socket->default_value)->data_type = data_type;
+ }
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
+ if (socket->type == SOCK_ATTRIBUTE && name == socket->name) {
+ ((bNodeSocketValueAttribute *)socket->default_value)->data_type = data_type;
+ }
+ }
+}
+
+static eNodeSocketDatatype customdata_to_socket_type(CustomDataType custom_data_type)
+{
+ switch (custom_data_type) {
+ case CD_AUTO_FROM_NAME:
+ return SOCK_ATTRIBUTE;
+ case CD_PROP_FLOAT:
+ return SOCK_FLOAT;
+ case CD_PROP_INT32:
+ return SOCK_INT;
+ case CD_PROP_FLOAT3:
+ return SOCK_VECTOR;
+ case CD_PROP_COLOR:
+ return SOCK_RGBA;
+ case CD_MLOOPCOL:
+ return SOCK_RGBA;
+ case CD_PROP_STRING:
+ return SOCK_STRING;
+ case CD_PROP_BOOL:
+ return SOCK_BOOLEAN;
+ case CD_PROP_FLOAT2:
+ return SOCK_VECTOR;
+ default:
+ BLI_assert_unreachable();
+ return SOCK_ATTRIBUTE;
+ }
+ BLI_assert_unreachable();
+}
+
+void set_attribute_socket_data_type(bNode &node,
+ const StringRef name,
+ CustomDataType custom_data_type)
+{
+ set_attribute_socket_data_type(node, name, customdata_to_socket_type(custom_data_type));
+}
+
+void reset_attribute_socket_data_type(bNode &node, const StringRef name)
+{
+ set_attribute_socket_data_type(node, name, SOCK_ATTRIBUTE);
+}
+
} // namespace blender::nodes
bool geo_node_poll_default(bNodeType *UNUSED(ntype),
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index 79a98c5ebf0..67d32d540a8 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -29,6 +29,7 @@
#include "BLT_translation.h"
+#include "NOD_attribute_ref.hh"
#include "NOD_geometry.h"
#include "NOD_geometry_exec.hh"
@@ -46,6 +47,14 @@ void update_attribute_input_socket_availabilities(bNode &node,
const GeometryNodeAttributeInputMode mode,
const bool name_is_available = true);
+void set_attribute_socket_data_type(bNode &node,
+ const StringRef name,
+ eNodeSocketDatatype data_type);
+void set_attribute_socket_data_type(bNode &node,
+ const StringRef name,
+ CustomDataType custom_data_type);
+void reset_attribute_socket_data_type(bNode &node, const StringRef name);
+
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
const AttributeDomain domain);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_get.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_get.cc
new file mode 100644
index 00000000000..e8f9b6afc2b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_get.cc
@@ -0,0 +1,99 @@
+/*
+ * 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 "BLI_kdopbvh.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_get_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Name")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_get_out[] = {
+ {SOCK_ATTRIBUTE, N_("Attribute")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_get_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_get_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = CD_PROP_FLOAT;
+}
+
+static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ blender::nodes::set_attribute_socket_data_type(
+ *node, "Attribute", (CustomDataType)node->custom1);
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_get_exec(GeoNodeExecParams params)
+{
+ const CustomDataType data_type = (CustomDataType)params.node().custom1;
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const std::string attribute_name = params.extract_input<std::string>("Name");
+
+ if (attribute_name.empty()) {
+ params.set_output("Attribute", AttributeRef::None);
+ return;
+ }
+
+ AttributeRef attribute(attribute_name, data_type);
+
+ /* TODO check for existence of the attribute on the geometry.
+ * This isn't really necessary for it to function, but can help catch invalid
+ * references early in the node graph.
+ * Technically this node does not even need a geometry input other than for
+ * checking validity. It could also just create an AttributeRef on good faith.
+ */
+
+ params.set_output("Attribute", attribute);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_get()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_GET, "Attribute Get", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_get_in, geo_node_attribute_get_out);
+ node_type_init(&ntype, geo_node_attribute_get_init);
+ node_type_update(&ntype, geo_node_attribute_vector_math_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_get_exec;
+ ntype.draw_buttons = geo_node_attribute_get_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
index 9309863f4e9..7ff68e78356 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -25,18 +25,18 @@
static bNodeSocketTemplate geo_node_attribute_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("A")},
+ {SOCK_ATTRIBUTE, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
- {SOCK_STRING, N_("B")},
+ {SOCK_ATTRIBUTE, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
- {SOCK_STRING, N_("C")},
+ {SOCK_ATTRIBUTE, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
- {SOCK_STRING, N_("Result")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_math_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_ATTRIBUTE, N_("Result")},
{-1, ""},
};
@@ -128,6 +128,11 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node)
data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
node->storage = data;
+
+ blender::nodes::set_attribute_socket_data_type(*node, "A", SOCK_FLOAT);
+ blender::nodes::set_attribute_socket_data_type(*node, "B", SOCK_FLOAT);
+ blender::nodes::set_attribute_socket_data_type(*node, "C", SOCK_FLOAT);
+ blender::nodes::set_attribute_socket_data_type(*node, "Result", SOCK_FLOAT);
}
namespace blender::nodes {
@@ -204,16 +209,9 @@ static void do_math_operation(const VArray<float> &span_input,
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- const NodeMathOperation operation,
- StringRef result_name)
+ const NodeMathOperation operation)
{
- /* Use the domain of the result attribute if it already exists. */
- std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
- if (result_info) {
- return result_info->domain;
- }
-
- /* Otherwise use the highest priority domain from existing input attributes, or the default. */
+ /* Use the highest priority domain from existing input attributes, or the default. */
const AttributeDomain default_domain = ATTR_DOMAIN_POINT;
if (operation_use_input_b(operation)) {
if (operation_use_input_c(operation)) {
@@ -224,19 +222,19 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
return params.get_highest_priority_input_domain({"A"}, component, default_domain);
}
-static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecParams &params)
+static void attribute_math_calc(GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ const AttributeRef &result)
{
const bNode &node = params.node();
const NodeAttributeMath *node_storage = (const NodeAttributeMath *)node.storage;
const NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage->operation);
- const std::string result_name = params.get_input<std::string>("Result");
/* The result type of this node is always float. */
- const AttributeDomain result_domain = get_result_domain(
- component, params, operation, result_name);
+ const AttributeDomain result_domain = get_result_domain(component, params, operation);
OutputAttribute_Typed<float> attribute_result =
- component.attribute_try_get_for_output_only<float>(result_name, result_domain);
+ component.attribute_try_get_for_output_only<float>(result.name(), result_domain);
if (!attribute_result) {
return;
}
@@ -270,20 +268,23 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
static void geo_node_attribute_math_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ AttributeRef result = params.declare_output_attribute("Result", CD_PROP_FLOAT);
geometry_set = geometry_set_realize_instances(geometry_set);
if (geometry_set.has<MeshComponent>()) {
- attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
+ attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params, result);
}
if (geometry_set.has<PointCloudComponent>()) {
- attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ attribute_math_calc(
+ geometry_set.get_component_for_write<PointCloudComponent>(), params, result);
}
if (geometry_set.has<CurveComponent>()) {
- attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params, result);
}
params.set_output("Geometry", geometry_set);
+ params.set_output("Result", result);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
index e4f3230ebb9..0a2ace160e6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -18,7 +18,7 @@
static bNodeSocketTemplate geo_node_attribute_remove_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING,
+ {SOCK_ATTRIBUTE,
N_("Attribute"),
0.0f,
0.0f,
@@ -36,21 +36,26 @@ static bNodeSocketTemplate geo_node_attribute_remove_out[] = {
{-1, ""},
};
+static void geo_node_attribute_remove_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ blender::nodes::set_attribute_socket_data_type(*node, "Attribute", SOCK_ATTRIBUTE);
+}
+
namespace blender::nodes {
static void remove_attribute(GeometryComponent &component,
GeoNodeExecParams &params,
- Span<std::string> attribute_names)
+ Span<AttributeRef> attribute_refs)
{
- for (std::string attribute_name : attribute_names) {
- if (attribute_name.empty()) {
+ for (const AttributeRef &attribute_ref : attribute_refs) {
+ if (!attribute_ref.valid()) {
continue;
}
- if (!component.attribute_try_delete(attribute_name)) {
+ if (!component.attribute_try_delete(attribute_ref.name())) {
params.error_message_add(NodeWarningType::Error,
- TIP_("Cannot delete attribute with name \"") + attribute_name +
- "\"");
+ TIP_("Cannot delete attribute with name \"") +
+ attribute_ref.name() + "\"");
}
}
}
@@ -58,21 +63,21 @@ static void remove_attribute(GeometryComponent &component,
static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute");
+ Vector<AttributeRef> attribute_refs = params.extract_multi_input<AttributeRef>("Attribute");
geometry_set = geometry_set_realize_instances(geometry_set);
if (geometry_set.has<MeshComponent>()) {
remove_attribute(
- geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names);
+ geometry_set.get_component_for_write<MeshComponent>(), params, attribute_refs);
}
if (geometry_set.has<PointCloudComponent>()) {
remove_attribute(
- geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names);
+ geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_refs);
}
if (geometry_set.has<CurveComponent>()) {
remove_attribute(
- geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names);
+ geometry_set.get_component_for_write<CurveComponent>(), params, attribute_refs);
}
params.set_output("Geometry", geometry_set);
@@ -86,6 +91,7 @@ void register_node_type_geo_attribute_remove()
geo_node_type_base(
&ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(&ntype, geo_node_attribute_remove_in, geo_node_attribute_remove_out);
+ node_type_init(&ntype, geo_node_attribute_remove_init);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_remove_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
index e0a3f5ad334..46c6ab409a7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
@@ -31,29 +31,28 @@
static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_TEXTURE, N_("Texture")},
- {SOCK_STRING, N_("Mapping")},
- {SOCK_STRING, N_("Result")},
+ {SOCK_ATTRIBUTE, N_("Mapping"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_ATTRIBUTE, N_("Result")},
{-1, ""},
};
+static void geo_node_attribute_sample_texture_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ blender::nodes::set_attribute_socket_data_type(*node, "Mapping", SOCK_VECTOR);
+ blender::nodes::set_attribute_socket_data_type(*node, "Result", SOCK_RGBA);
+}
+
namespace blender::nodes {
static AttributeDomain get_result_domain(const GeometryComponent &component,
- const StringRef result_name,
const StringRef map_name)
{
- /* Use the domain of the result attribute if it already exists. */
- std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
- if (result_info) {
- return result_info->domain;
- }
-
- /* Otherwise use the name of the map attribute. */
+ /* Use the name of the map attribute. */
std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name);
if (map_info) {
return map_info->domain;
@@ -63,31 +62,31 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
return ATTR_DOMAIN_POINT;
}
-static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams &params)
+static void execute_on_component(GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ const AttributeRef &result_ref)
{
Tex *texture = params.get_input<Tex *>("Texture");
if (texture == nullptr) {
return;
}
- const std::string result_attribute_name = params.get_input<std::string>("Result");
- const std::string mapping_name = params.get_input<std::string>("Mapping");
- if (!component.attribute_exists(mapping_name)) {
+ const AttributeRef mapping_ref = params.get_input<AttributeRef>("Mapping");
+ if (!component.attribute_exists(mapping_ref.name())) {
return;
}
- const AttributeDomain result_domain = get_result_domain(
- component, result_attribute_name, mapping_name);
+ const AttributeDomain result_domain = get_result_domain(component, mapping_ref.name());
OutputAttribute_Typed<ColorGeometry4f> attribute_out =
- component.attribute_try_get_for_output_only<ColorGeometry4f>(result_attribute_name,
+ component.attribute_try_get_for_output_only<ColorGeometry4f>(result_ref.name(),
result_domain);
if (!attribute_out) {
return;
}
GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
- mapping_name, result_domain, {0, 0, 0});
+ mapping_ref.name(), result_domain, {0, 0, 0});
MutableSpan<ColorGeometry4f> colors = attribute_out.as_span();
threading::parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) {
@@ -107,20 +106,25 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ AttributeRef result_attribute_ref = params.declare_output_attribute("Result", CD_PROP_COLOR);
geometry_set = geometry_set_realize_instances(geometry_set);
if (geometry_set.has<MeshComponent>()) {
- execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
+ execute_on_component(
+ geometry_set.get_component_for_write<MeshComponent>(), params, result_attribute_ref);
}
if (geometry_set.has<PointCloudComponent>()) {
- execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ execute_on_component(
+ geometry_set.get_component_for_write<PointCloudComponent>(), params, result_attribute_ref);
}
if (geometry_set.has<CurveComponent>()) {
- execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ execute_on_component(
+ geometry_set.get_component_for_write<CurveComponent>(), params, result_attribute_ref);
}
params.set_output("Geometry", geometry_set);
+ params.set_output("Result", result_attribute_ref);
}
} // namespace blender::nodes
@@ -135,6 +139,7 @@ void register_node_type_geo_sample_texture()
NODE_CLASS_ATTRIBUTE,
0);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ node_type_init(&ntype, &geo_node_attribute_sample_texture_init);
node_type_socket_templates(
&ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_set.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_set.cc
new file mode 100644
index 00000000000..8fc96d4b616
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_set.cc
@@ -0,0 +1,107 @@
+/*
+ * 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 "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_set_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_ATTRIBUTE, N_("Attribute")},
+ {SOCK_STRING, N_("Name")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_set_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_set_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ blender::nodes::set_attribute_socket_data_type(*node, "Attribute", SOCK_ATTRIBUTE);
+}
+
+namespace blender::nodes {
+
+static void set_attribute(GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ const AttributeRef &attribute_ref,
+ const StringRef attribute_name)
+{
+ ReadAttributeLookup attribute_input = component.attribute_try_get_for_read(
+ attribute_ref.name(), attribute_ref.data_type());
+
+ if (attribute_input) {
+ OutputAttribute attribute_output = component.attribute_try_get_for_output_only(
+ attribute_name, attribute_input.domain, attribute_ref.data_type());
+
+ if (attribute_output) {
+ threading::parallel_for(IndexRange(attribute_output->size()), 512, [&](IndexRange range) {
+ BUFFER_FOR_CPP_TYPE_VALUE(attribute_output.cpp_type(), buffer);
+ for (const int i : range) {
+ attribute_input.varray->get(i, buffer);
+ attribute_output->set_by_relocate(i, buffer);
+ }
+ });
+ }
+
+ attribute_output.save();
+ }
+}
+
+static void geo_node_attribute_set_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const AttributeRef &attribute_ref = params.extract_input<AttributeRef>("Attribute");
+ const std::string attribute_name = params.extract_input<std::string>("Name");
+
+ if (attribute_name.empty()) {
+ params.set_output("Geometry", geometry_set);
+ return;
+ }
+
+ for (const GeometryComponentType component_type : {GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_INSTANCES,
+ GEO_COMPONENT_TYPE_VOLUME,
+ GEO_COMPONENT_TYPE_CURVE}) {
+ if (geometry_set.has(component_type)) {
+ set_attribute(geometry_set.get_component_for_write(component_type),
+ params,
+ attribute_ref,
+ attribute_name);
+ }
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_set()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_SET, "Attribute Set", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_set_in, geo_node_attribute_set_out);
+ node_type_init(&ntype, geo_node_attribute_set_init);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_set_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
index e2cf6e8b480..dbdf42f38b4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
@@ -26,20 +26,20 @@
static bNodeSocketTemplate geo_node_attribute_vector_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("A")},
+ {SOCK_ATTRIBUTE, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_VECTOR, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
- {SOCK_STRING, N_("B")},
+ {SOCK_ATTRIBUTE, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_VECTOR, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
- {SOCK_STRING, N_("C")},
+ {SOCK_ATTRIBUTE, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_VECTOR, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
- {SOCK_STRING, N_("Result")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_vector_math_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_ATTRIBUTE, N_("Result")},
{-1, ""},
};
@@ -112,6 +112,11 @@ static void geo_node_attribute_vector_math_init(bNodeTree *UNUSED(tree), bNode *
data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
node->storage = data;
+
+ blender::nodes::set_attribute_socket_data_type(*node, "A", SOCK_VECTOR);
+ blender::nodes::set_attribute_socket_data_type(*node, "B", SOCK_VECTOR);
+ blender::nodes::set_attribute_socket_data_type(*node, "C", SOCK_VECTOR);
+ blender::nodes::set_attribute_socket_data_type(*node, "Result", SOCK_VECTOR);
}
static CustomDataType operation_get_result_type(const NodeVectorMathOperation operation)
@@ -171,6 +176,20 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod
"C",
(GeometryNodeAttributeInputMode)node_storage->input_type_c,
operation_use_input_c(operation));
+
+ blender::nodes::set_attribute_socket_data_type(*node, "A", SOCK_VECTOR);
+ blender::nodes::set_attribute_socket_data_type(
+ *node, "B", ELEM(operation, NODE_VECTOR_MATH_SCALE) ? SOCK_FLOAT : SOCK_VECTOR);
+ blender::nodes::set_attribute_socket_data_type(
+ *node, "C", ELEM(operation, NODE_VECTOR_MATH_REFRACT) ? SOCK_FLOAT : SOCK_VECTOR);
+ blender::nodes::set_attribute_socket_data_type(*node,
+ "Result",
+ ELEM(operation,
+ NODE_VECTOR_MATH_DOT_PRODUCT,
+ NODE_VECTOR_MATH_DISTANCE,
+ NODE_VECTOR_MATH_LENGTH) ?
+ SOCK_FLOAT :
+ SOCK_VECTOR);
}
static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
@@ -385,15 +404,8 @@ static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- const NodeVectorMathOperation operation,
- StringRef result_name)
+ const NodeVectorMathOperation operation)
{
- /* Use the domain of the result attribute if it already exists. */
- ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute.domain;
- }
-
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
const AttributeDomain default_domain = ATTR_DOMAIN_POINT;
if (operation_use_input_b(operation)) {
@@ -406,12 +418,12 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
}
static void attribute_vector_math_calc(GeometryComponent &component,
- const GeoNodeExecParams &params)
+ GeoNodeExecParams &params,
+ const AttributeRef& result)
{
const bNode &node = params.node();
const NodeAttributeVectorMath *node_storage = (const NodeAttributeVectorMath *)node.storage;
const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation;
- const std::string result_name = params.get_input<std::string>("Result");
/* The number and type of the input attribute depend on the operation. */
const CustomDataType read_type_a = CD_PROP_FLOAT3;
@@ -421,9 +433,7 @@ static void attribute_vector_math_calc(GeometryComponent &component,
const CustomDataType read_type_c = operation_get_read_type_c(operation);
/* The result domain is always point for now. */
- const CustomDataType result_type = operation_get_result_type(operation);
- const AttributeDomain result_domain = get_result_domain(
- component, params, operation, result_name);
+ const AttributeDomain result_domain = get_result_domain(component, params, operation);
GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, read_type_a, nullptr);
@@ -447,7 +457,7 @@ static void attribute_vector_math_calc(GeometryComponent &component,
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
- result_name, result_domain, result_type);
+ result.name(), result_domain, result.data_type());
if (!attribute_result) {
return;
}
@@ -519,22 +529,32 @@ static void attribute_vector_math_calc(GeometryComponent &component,
static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
{
+ const bNode &node = params.node();
+ const NodeAttributeVectorMath *node_storage = (const NodeAttributeVectorMath *)node.storage;
+ const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation;
+
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
+ const CustomDataType result_type = operation_get_result_type(operation);
+ AttributeRef result = params.declare_output_attribute("Result", result_type);
+
if (geometry_set.has<MeshComponent>()) {
- attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
+ attribute_vector_math_calc(
+ geometry_set.get_component_for_write<MeshComponent>(), params, result);
}
if (geometry_set.has<PointCloudComponent>()) {
- attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
- params);
+ attribute_vector_math_calc(
+ geometry_set.get_component_for_write<PointCloudComponent>(), params, result);
}
if (geometry_set.has<CurveComponent>()) {
- attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ attribute_vector_math_calc(
+ geometry_set.get_component_for_write<CurveComponent>(), params, result);
}
params.set_output("Geometry", geometry_set);
+ params.set_output("Result", result);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/attribute_ref.cc b/source/blender/nodes/intern/attribute_ref.cc
new file mode 100644
index 00000000000..e021a471415
--- /dev/null
+++ b/source/blender/nodes/intern/attribute_ref.cc
@@ -0,0 +1,67 @@
+/*
+ * 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 "NOD_attribute_ref.hh"
+
+#include "FN_cpp_type_make.hh"
+
+const AttributeRef AttributeRef::None = AttributeRef();
+
+const std::string &AttributeRef::name() const
+{
+ return name_;
+}
+
+CustomDataType AttributeRef::data_type() const
+{
+ return data_type_;
+}
+
+AttributeRef::AttributeRef() : name_(""), data_type_(CD_PROP_FLOAT)
+{
+}
+
+AttributeRef::AttributeRef(CustomDataType data_type) : name_(""), data_type_(data_type)
+{
+}
+
+AttributeRef::AttributeRef(const std::string &name, CustomDataType data_type)
+ : name_(name), data_type_(data_type)
+{
+}
+
+std::ostream &operator<<(std::ostream &stream, const AttributeRef &attr)
+{
+ stream << "<AttributeRef name=" << attr.name_ << ", data_type=" << attr.data_type_ << ">";
+ return stream;
+}
+
+bool AttributeRef::valid() const
+{
+ return !name_.empty();
+}
+
+void *AttributeRef::single_value_ptr()
+{
+ return &value_float_;
+}
+
+const void *AttributeRef::single_value_ptr() const
+{
+ return &value_float_;
+}
+
+MAKE_CPP_TYPE(AttributeRef, AttributeRef, CPPTypeFlags::Printable);
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index 17a13f2d1b0..8b61758a673 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -73,6 +73,34 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name,
return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
+ const DataTypeConversions &conversions = get_implicit_type_conversions();
+ if (found_socket->type == SOCK_ATTRIBUTE) {
+ const AttributeRef attribute_ref = this->get_input<AttributeRef>(found_socket->identifier);
+
+ if (attribute_ref.valid()) {
+ /* Try getting the attribute without the default value. */
+ GVArrayPtr attribute = component.attribute_try_get_for_read(
+ attribute_ref.name(), domain, type);
+ if (attribute) {
+ return attribute;
+ }
+
+ /* If the attribute doesn't exist, use the default value and output an error message */
+ this->error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + attribute_ref.name() + "\"");
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
+ }
+ else {
+ /* Broadcast the attribute single value */
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(
+ *blender::bke::custom_data_type_to_cpp_type(attribute_ref.data_type()),
+ *cpp_type,
+ attribute_ref.single_value_ptr(),
+ buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
+ }
+ }
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
/* Try getting the attribute without the default value. */
@@ -90,7 +118,6 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name,
}
return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
- const DataTypeConversions &conversions = get_implicit_type_conversions();
if (found_socket->type == SOCK_FLOAT) {
const float value = this->get_input<float>(found_socket->identifier);
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
@@ -190,6 +217,13 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
return default_domain;
}
+AttributeRef GeoNodeExecParams::declare_output_attribute(const StringRef identifier,
+ CustomDataType data_type) const
+{
+ return AttributeRef("Node" + std::to_string(provider_->dnode->id()) + "::" + identifier,
+ data_type);
+}
+
void GeoNodeExecParams::check_input_access(StringRef identifier,
const CPPType *requested_type) const
{
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index 9f0a145ace2..c0a8382a661 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -44,6 +44,7 @@
#include "MEM_guardedalloc.h"
+#include "NOD_attribute_ref.hh"
#include "NOD_node_tree_multi_function.hh"
#include "NOD_socket.h"
@@ -97,6 +98,19 @@ struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
dval->value[3] = stemp->val4;
break;
}
+ case SOCK_ATTRIBUTE: {
+ bNodeSocketValueAttribute *dval = (bNodeSocketValueAttribute *)sock->default_value;
+ dval->data_type = SOCK_FLOAT;
+ dval->value_int = (int)stemp->val1;
+ dval->value_float[0] = stemp->val1;
+ dval->value_float[1] = stemp->val2;
+ dval->value_float[2] = stemp->val3;
+ dval->value_float[3] = stemp->val4;
+ dval->value_bool = (bool)stemp->val1;
+ dval->min = stemp->min;
+ dval->max = stemp->max;
+ break;
+ }
}
return sock;
@@ -271,6 +285,16 @@ void node_socket_init_default_value(bNodeSocket *sock)
sock->default_value = dval;
break;
}
+ case SOCK_ATTRIBUTE: {
+ bNodeSocketValueAttribute *dval = (bNodeSocketValueAttribute *)MEM_callocN(
+ sizeof(bNodeSocketValueAttribute), "node socket value attribute");
+ dval->data_type = SOCK_FLOAT;
+ dval->min = -FLT_MAX;
+ dval->max = FLT_MAX;
+
+ sock->default_value = dval;
+ break;
+ }
case SOCK_OBJECT: {
bNodeSocketValueObject *dval = (bNodeSocketValueObject *)MEM_callocN(
sizeof(bNodeSocketValueObject), "node socket value object");
@@ -369,6 +393,12 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
*toval = *fromval;
break;
}
+ case SOCK_ATTRIBUTE: {
+ bNodeSocketValueAttribute *toval = (bNodeSocketValueAttribute *)to->default_value;
+ bNodeSocketValueAttribute *fromval = (bNodeSocketValueAttribute *)from->default_value;
+ *toval = *fromval;
+ break;
+ }
case SOCK_OBJECT: {
bNodeSocketValueObject *toval = (bNodeSocketValueObject *)to->default_value;
bNodeSocketValueObject *fromval = (bNodeSocketValueObject *)from->default_value;
@@ -662,6 +692,70 @@ static bNodeSocketType *make_socket_type_string()
return socktype;
}
+static CustomDataType socket_to_customdata_type(eNodeSocketDatatype socket_type)
+{
+ switch (socket_type) {
+ case SOCK_ATTRIBUTE:
+ return CD_AUTO_FROM_NAME;
+ case SOCK_FLOAT:
+ return CD_PROP_FLOAT;
+ case SOCK_INT:
+ return CD_PROP_INT32;
+ case SOCK_VECTOR:
+ return CD_PROP_FLOAT3;
+ case SOCK_RGBA:
+ return CD_PROP_COLOR;
+ case SOCK_STRING:
+ return CD_PROP_STRING;
+ case SOCK_BOOLEAN:
+ return CD_PROP_BOOL;
+ default:
+ BLI_assert_unreachable();
+ return CD_AUTO_FROM_NAME;
+ }
+ BLI_assert_unreachable();
+}
+
+static bNodeSocketType *make_socket_type_attribute()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_ATTRIBUTE, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<AttributeRef>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ const bNodeSocketValueAttribute *default_value = (bNodeSocketValueAttribute *)
+ socket.default_value;
+ const eNodeSocketDatatype socket_data_type = (eNodeSocketDatatype)default_value->data_type;
+ if (default_value->flag & SOCK_ATTRIBUTE_USE_NAME) {
+ new (r_value) AttributeRef(default_value->attribute_name, socket_to_customdata_type(socket_data_type));
+ }
+ else {
+ AttributeRef *attribute_ref = new (r_value)
+ AttributeRef(socket_to_customdata_type(socket_data_type));
+ switch (socket_data_type) {
+ case SOCK_FLOAT:
+ attribute_ref->single_value<float>() = default_value->value_float[0];
+ break;
+ case SOCK_VECTOR:
+ attribute_ref->single_value<blender::float3>() = blender::float3(
+ default_value->value_float);
+ break;
+ case SOCK_RGBA:
+ attribute_ref->single_value<blender::ColorGeometry4f>() = blender::ColorGeometry4f(
+ default_value->value_float);
+ break;
+ case SOCK_INT:
+ attribute_ref->single_value<int>() = default_value->value_int;
+ break;
+ case SOCK_BOOLEAN:
+ attribute_ref->single_value<bool>() = default_value->value_bool;
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ return socktype;
+}
+
MAKE_CPP_TYPE(Object, Object *, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(Collection, Collection *, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(Texture, Tex *, CPPTypeFlags::BasicType)
@@ -749,6 +843,8 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_string());
+ nodeRegisterSocketType(make_socket_type_attribute());
+
nodeRegisterSocketType(make_standard_socket_type(SOCK_SHADER, PROP_NONE));
nodeRegisterSocketType(make_socket_type_object());
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index 1aec280fd2b..b0917e2d1cb 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -475,6 +475,13 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype
return -1;
}
}
+ case SOCK_ATTRIBUTE:
+ switch (from) {
+ case SOCK_ATTRIBUTE:
+ return 1;
+ default:
+ return -1;
+ }
default:
return -1;
}
diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc
index 1a71a3418a5..15612524941 100644
--- a/source/blender/nodes/intern/type_conversions.cc
+++ b/source/blender/nodes/intern/type_conversions.cc
@@ -22,6 +22,8 @@
#include "BLI_float2.hh"
#include "BLI_float3.hh"
+#include "NOD_attribute_ref.hh"
+
namespace blender::nodes {
using fn::GVArrayPtr;
@@ -70,6 +72,12 @@ static ColorGeometry4f float_to_color(const float &a)
{
return ColorGeometry4f(a, a, a, 1.0f);
}
+static AttributeRef float_to_attribute(const float &a)
+{
+ AttributeRef attr_ref;
+ attr_ref.single_value<float>() = a;
+ return attr_ref;
+}
static float3 float2_to_float3(const float2 &a)
{
@@ -91,6 +99,12 @@ static ColorGeometry4f float2_to_color(const float2 &a)
{
return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f);
}
+static AttributeRef float2_to_attribute(const float2 &a)
+{
+ AttributeRef attr_ref;
+ attr_ref.single_value<float3>() = float3(a.x, a.y, 0.0f);
+ return attr_ref;
+}
static bool float3_to_bool(const float3 &a)
{
@@ -112,6 +126,12 @@ static ColorGeometry4f float3_to_color(const float3 &a)
{
return ColorGeometry4f(a.x, a.y, a.z, 1.0f);
}
+static AttributeRef float3_to_attribute(const float3 &a)
+{
+ AttributeRef attr_ref;
+ attr_ref.single_value<float3>() = a;
+ return attr_ref;
+}
static bool int_to_bool(const int32_t &a)
{
@@ -133,6 +153,12 @@ static ColorGeometry4f int_to_color(const int32_t &a)
{
return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
}
+static AttributeRef int_to_attribute(const int &a)
+{
+ AttributeRef attr_ref;
+ attr_ref.single_value<int>() = a;
+ return attr_ref;
+}
static float bool_to_float(const bool &a)
{
@@ -154,6 +180,12 @@ static ColorGeometry4f bool_to_color(const bool &a)
{
return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f);
}
+static AttributeRef bool_to_attribute(const bool &a)
+{
+ AttributeRef attr_ref;
+ attr_ref.single_value<bool>() = a;
+ return attr_ref;
+}
static bool color_to_bool(const ColorGeometry4f &a)
{
@@ -175,6 +207,18 @@ static float3 color_to_float3(const ColorGeometry4f &a)
{
return float3(a.r, a.g, a.b);
}
+static AttributeRef color_to_attribute(const ColorGeometry4f &a)
+{
+ AttributeRef attr_ref;
+ attr_ref.single_value<ColorGeometry4f>() = a;
+ return attr_ref;
+}
+
+/* Temporary implicit conversion to allow attributes directly connected to string inputs. */
+static std::string attribute_to_string(const AttributeRef& a)
+{
+ return a.name();
+}
static DataTypeConversions create_implicit_conversions()
{
@@ -185,36 +229,44 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<float, int32_t, float_to_int>(conversions);
add_implicit_conversion<float, bool, float_to_bool>(conversions);
add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions);
+ add_implicit_conversion<float, AttributeRef, float_to_attribute>(conversions);
add_implicit_conversion<float2, float3, float2_to_float3>(conversions);
add_implicit_conversion<float2, float, float2_to_float>(conversions);
add_implicit_conversion<float2, int32_t, float2_to_int>(conversions);
add_implicit_conversion<float2, bool, float2_to_bool>(conversions);
add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions);
+ add_implicit_conversion<float2, AttributeRef, float2_to_attribute>(conversions);
add_implicit_conversion<float3, bool, float3_to_bool>(conversions);
add_implicit_conversion<float3, float, float3_to_float>(conversions);
add_implicit_conversion<float3, int32_t, float3_to_int>(conversions);
add_implicit_conversion<float3, float2, float3_to_float2>(conversions);
add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions);
+ add_implicit_conversion<float3, AttributeRef, float3_to_attribute>(conversions);
add_implicit_conversion<int32_t, bool, int_to_bool>(conversions);
add_implicit_conversion<int32_t, float, int_to_float>(conversions);
add_implicit_conversion<int32_t, float2, int_to_float2>(conversions);
add_implicit_conversion<int32_t, float3, int_to_float3>(conversions);
add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions);
+ add_implicit_conversion<int32_t, AttributeRef, int_to_attribute>(conversions);
add_implicit_conversion<bool, float, bool_to_float>(conversions);
add_implicit_conversion<bool, int32_t, bool_to_int>(conversions);
add_implicit_conversion<bool, float2, bool_to_float2>(conversions);
add_implicit_conversion<bool, float3, bool_to_float3>(conversions);
add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions);
+ add_implicit_conversion<bool, AttributeRef, bool_to_attribute>(conversions);
add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions);
add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions);
add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions);
add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions);
add_implicit_conversion<ColorGeometry4f, float3, color_to_float3>(conversions);
+ add_implicit_conversion<ColorGeometry4f, AttributeRef, color_to_attribute>(conversions);
+
+ add_implicit_conversion<AttributeRef, std::string, attribute_to_string>(conversions);
return conversions;
}