1
1

Compare commits

...

38 Commits

Author SHA1 Message Date
ca50a1f762 Merge branch 'master' into geometry-nodes-unnamed-attributes 2021-07-18 12:14:23 +01:00
d35969a74f Merge branch 'master' into geometry-nodes-unnamed-attributes 2021-07-01 11:48:03 +01:00
efe2247e3c Merge branch 'master' into geometry-nodes-unnamed-attributes 2021-06-30 07:57:33 +01:00
a08ed1dce3 Merge branch 'master' into geometry-nodes-unnamed-attributes 2021-06-27 13:06:54 +01:00
a71af6fd2d Node socket type callback for display shape.
This can change the display shape per socket type instead of using
per instance variable.
2021-06-21 21:08:29 +01:00
083f2b1ae2 Added direct attribute search for inputs.
This way an explicit "Get Attribute" is rarely needed.
The "Get" node can still be useful for connecting multiple inputs
to a single attribute lookup, or when a string is passed in to be
turned into an attribute reference.
2021-06-21 19:47:52 +01:00
169eff3c58 Implemented conversion of single values to attributes for broadcasting. 2021-06-21 13:58:04 +01:00
bc72fe209f Fixed include paths for NOD_attribute_ref.hh. 2021-06-21 09:18:01 +01:00
b918334178 Merge branch 'master' into geometry-nodes-unnamed-attributes 2021-06-21 08:44:48 +01:00
961bba9d77 Generate unique names for output attributes. 2021-06-19 23:24:36 +01:00
16cc5215e0 Implemented Sample Texture node with attributes. 2021-06-19 17:05:26 +01:00
02804b3de6 Use generic attribute type for Remove Attribute. 2021-06-19 17:04:57 +01:00
ddbc1206db Converted the Attribute Remove node. 2021-06-19 16:40:21 +01:00
5ef0b2554b Added a guide doc for converting attribute sockets. 2021-06-19 16:16:01 +01:00
8958aa3e1c Convert the scalar math node. 2021-06-19 16:15:43 +01:00
58a9030353 Renamed and implemented the "Attribute Set" node. 2021-06-19 14:46:42 +01:00
f4dab78d54 Make attribute copy between nodes work. 2021-06-19 12:49:10 +01:00
5b6291d202 Functional vector math node. 2021-06-18 15:52:56 +01:00
c28d397d2f Nicer utility function to update attribute types. 2021-06-18 12:21:45 +01:00
0f4b9bfb3f General tree type function to set attribute shape.
Use the tree type to make sure attribute sockets
are displayed as squares.

It's not ideal that this property is per socket
and not tied directly to the socket type, but this
will do for now.
2021-06-18 10:49:42 +01:00
86277ed920 Simple implementation of attribute get node.
This does not currently check for existing attributes, which would be
mostly a helper feature to avoid invalid references further downstream.
2021-06-18 09:53:33 +01:00
a40ee3c86b Rename "Attribute Load" node to "Attribute Get".
This is to avoid the impression that it loads data when in fact it only
creates a reference to an attribute.
2021-06-18 09:35:38 +01:00
5d0cc7c02a Allow implicit attribute-to-string conversion.
This will allow connecting attributes directly to old-style string
input sockets, at least until most nodes can be converted.
2021-06-18 09:06:10 +01:00
346a8d6cfd Merge branch 'master' into geometry-nodes-unnamed-attributes 2021-06-18 09:05:56 +01:00
48514481de Merge branch 'master' into geometry-nodes-unnamed-attributes 2021-06-17 10:27:53 +01:00
0274a96af6 Dummy attribute sockets for a few nodes.
This is just for proposal screenshots and conceptual testing,
the nodes in question have been disabled to avoid crashes.
2021-06-11 18:55:04 +01:00
4af9963acd Change default_value_vector length to 3. 2021-06-11 16:29:39 +01:00
757611dbd2 Universal default value for attribute sockets.
This supports all the valid data types for attribute sockets:
float, int, bool, vector, color.

Type of default value can be changed by setting the "data_type" property
of the socket. Set to SOCK_ATTRIBUTE to make it a generic attribute
without a default value.
2021-06-11 16:11:21 +01:00
8c7aa2fc20 Draw attribute sockets with squares. 2021-06-11 10:25:09 +01:00
8a3963fe16 Disable load/store execution functions temporarily.
Branch will be used to create design mockups for now.
2021-06-11 09:32:14 +01:00
d74fa0cdcf Execution functions for load/store nodes. 2021-06-10 18:53:07 +01:00
b621a3e3da Add missing registration for load/store nodes. 2021-06-10 10:48:38 +01:00
32072995f2 Added an AttributeRef struct that is the actual data during node eval. 2021-06-09 19:53:28 +01:00
c0c1f0dae1 Merge branch 'master' into geometry-nodes-unnamed-attributes 2021-06-09 16:51:49 +01:00
23f20558a6 New Load and Store nodes for attributes. 2021-05-23 09:18:38 +01:00
0e2d77990c Resolve function to set runtime type details.
This is optional node function can be used to resolve type details such
as base type, attribute domain, etc. at runtime. It is intended for
nodes whose operation depends on the concrete runtime type of outputs.
It gets called in a back-propagating manner so all outputs are resolved
before the function is called on a node.
2021-05-23 07:41:24 +01:00
84a7090e82 Remove the string value from attribute socket.
Attribute sockets should come in varieties for basic data types (float,
vector, etc.) instead of a string. Existing attribute layers should be
accessed explicitly with a "Read Attribute" node (todo).

Typed attributes for basic data will do broadcasting eventually.
This will allow connecting singular value sockets to an attribute input
without the need for a "Fill" node and perform basic broadcasting.
2021-05-22 11:07:26 +01:00
465930675b Add a new socket type for attributes.
The default_value is a string, as it will basically behave like a string
socket with some additional management in geometry nodes.

When used as an output the attribute should be generated automatically.
Auto-generated attributes should be removed automatically as soon as all
using nodes are done reading them.
2021-05-22 10:35:59 +01:00
34 changed files with 1415 additions and 108 deletions

View File

@@ -0,0 +1,112 @@
# Instructions for converting nodes from strings to attribute sockets
1. Change `SOCK_STRING` sockets to `SOCK_ATTRIBUTE`
(Except where strings are not actually attribute names)
1. Move output attributes over to the output socket template list.
1. Add default value and limits to the attribute socket template as well as the single value socket.
This is needed because the attribute socket also has default values for basic data types.
Output sockets don't need default value and limits.
```c
{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},
```
The single-value socket may not be needed in future because of broadcasting, but for now we keep it.
1. Update attribute socket data types where types are known.
Use the `set_attribute_socket_data_type` function to update the type, like so:
```cpp
blender::nodes::set_attribute_socket_data_type(*node, "A", SOCK_VECTOR);
```
- If the attribute data type is fixed, change it in the _init_ callback.
- If the attribute data type depends on runtime options (e.g. math operation), change it in the _update_ callback.
1. In the _execute_ callback, create an `AttributeRef` for each output attribute. The output value must be set __exactly__ once, so watch out for early-exits and repeated function calls.
_TODO output attribute names will be auto-generated_
```cpp
AttributeRef result("DummyName this will be auto generated", CD_PROP_FLOAT);
do_the_actual_work(component, params, result);
params.set_output("Result", result);
```
1. Remove old lookups for attribute name strings and use the result `AttributeRef` instead.
Old:
```cpp
static void do_the_actual_work(GeometryComponent &component,
const GeoNodeExecParams &params)
{
const std::string result_name = params.get_input<std::string>("Result");
[...]
}
```
New:
```cpp
static void do_the_actual_work(GeometryComponent &component,
const GeoNodeExecParams &params,
const AttributeRef &result)
{
[...]
}
```
1. Many nodes have a `get_result_domain` function to determine the domain for output attributes. This currently uses the output attribute name to re-use existing attributes. Attribute outputs are now always unique, so this name lookup is deprecated and should be removed. Result domains should always be determined from inputs only.
Old:
```cpp
static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef input_name,
StringRef result_name)
{
/* 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 input attribute's domain. */
[...]
}
```
New:
```cpp
static AttributeDomain get_result_domain(const GeometryComponent &component,
AttributeRef input_attribute)
{
/* Use the input attribute's domain. */
[...]
}
```
1. Use the generated output attribute name to create actual attributes and data arrays:
```cpp
static void do_the_actual_work(GeometryComponent &component,
const GeoNodeExecParams &params,
const AttributeRef &result)
{
[...]
OutputAttribute_Typed<float> attribute_result =
component.attribute_try_get_for_output_only<float>(result.name(), result_domain);
[...]
}
```

View File

@@ -482,6 +482,8 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeConvert"),
NodeItem("GeometryNodeAttributeCurveMap"),
NodeItem("GeometryNodeAttributeFill"),
NodeItem("GeometryNodeAttributeGet"),
NodeItem("GeometryNodeAttributeSet"),
NodeItem("GeometryNodeAttributeMix"),
NodeItem("GeometryNodeAttributeProximity"),
NodeItem("GeometryNodeAttributeColorRamp"),

View File

@@ -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;
@@ -155,6 +158,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);
@@ -327,6 +334,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;
@@ -1464,6 +1472,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_PRIMITIVE_LINE 1068
#define GEO_NODE_CURVE_ENDPOINTS 1069
#define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070
#define GEO_NODE_ATTRIBUTE_GET 1071
#define GEO_NODE_ATTRIBUTE_SET 1072
/** \} */

View File

@@ -316,6 +316,7 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_ATTRIBUTE:
break;
}
}
@@ -443,6 +444,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:
@@ -847,6 +851,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;
}
}
@@ -942,6 +947,7 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_ATTRIBUTE:
break;
}
}
@@ -1525,6 +1531,7 @@ static void socket_id_user_increment(bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_ATTRIBUTE:
break;
}
}
@@ -1578,6 +1585,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_ATTRIBUTE:
break;
}
}
@@ -1740,6 +1748,8 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketTexture";
case SOCK_MATERIAL:
return "NodeSocketMaterial";
case SOCK_ATTRIBUTE:
return "NodeSocketAttribute";
}
return nullptr;
}
@@ -1817,6 +1827,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceTexture";
case SOCK_MATERIAL:
return "NodeSocketInterfaceMaterial";
case SOCK_ATTRIBUTE:
return "NodeSocketInterfaceAttribute";
}
return nullptr;
}
@@ -5092,6 +5104,7 @@ 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();
@@ -5099,6 +5112,7 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_remove();
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();

View File

@@ -90,7 +90,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);

View File

@@ -36,6 +36,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_node.h"
#include "RNA_access.h"
@@ -2336,7 +2337,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);

View File

@@ -3343,7 +3343,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)
{
@@ -3453,6 +3453,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 */
@@ -3463,15 +3464,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,
@@ -3575,7 +3604,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);
@@ -3604,6 +3633,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;
@@ -3645,6 +3728,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);
@@ -3654,12 +3771,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)
{

View File

@@ -722,9 +722,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,
@@ -737,7 +737,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;
@@ -753,7 +753,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)) {
@@ -769,6 +769,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,
@@ -812,14 +813,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) {
@@ -984,13 +995,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,
@@ -1055,7 +1067,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;
@@ -1083,9 +1099,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),
@@ -1375,10 +1391,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);
}
}

View File

@@ -162,8 +162,19 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
GeometryAttributeInfo *item = (GeometryAttributeInfo *)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");
}
@@ -172,6 +183,7 @@ void node_geometry_add_attribute_search_button(const bContext *UNUSED(C),
const bNodeTree *node_tree,
const bNode *node,
PointerRNA *socket_ptr,
const char *propname,
uiLayout *layout)
{
uiBlock *block = uiLayoutGetBlock(layout);
@@ -185,7 +197,7 @@ void node_geometry_add_attribute_search_button(const bContext *UNUSED(C),
10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
UI_UNIT_Y,
socket_ptr,
"default_value",
propname,
0,
0.0f,
0.0f,

View File

@@ -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,
@@ -317,6 +318,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[];

View File

@@ -2004,6 +2004,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;

View File

@@ -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;
}

View File

@@ -168,6 +168,7 @@ typedef enum eNodeSocketDatatype {
SOCK_COLLECTION = 11,
SOCK_TEXTURE = 12,
SOCK_MATERIAL = 13,
SOCK_ATTRIBUTE = 14,
} eNodeSocketDatatype;
/* Socket shape. */
@@ -615,6 +616,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,

View File

@@ -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},
};
@@ -2025,6 +2038,7 @@ static bool switch_type_supported(const EnumPropertyItem *item)
SOCK_STRING,
SOCK_RGBA,
SOCK_GEOMETRY,
SOCK_ATTRIBUTE,
SOCK_OBJECT,
SOCK_COLLECTION,
SOCK_TEXTURE,
@@ -2256,6 +2270,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,
@@ -3251,6 +3279,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)
{
@@ -10122,6 +10182,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)
@@ -10759,6 +10832,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)
@@ -11115,6 +11336,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");

View File

@@ -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;
@@ -201,6 +202,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
@@ -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();
@@ -439,6 +446,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. */
@@ -553,6 +596,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()) {

View File

@@ -148,6 +148,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
@@ -156,6 +157,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
@@ -338,6 +340,7 @@ set(SRC
intern/derived_node_tree.cc
intern/geometry_nodes_eval_log.cc
intern/math_functions.cc
intern/attribute_ref.cc
intern/node_common.c
intern/node_exec.cc
intern/node_geometry_exec.cc
@@ -354,6 +357,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

View File

@@ -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();
}
};

View File

@@ -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_separate_xyz(void);
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_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);

View File

@@ -24,6 +24,7 @@
#include "DNA_node_types.h"
#include "NOD_attribute_ref.hh"
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry_nodes_eval_log.hh"
@@ -112,6 +113,9 @@ class GeoNodeExecParamsProvider {
virtual bool lazy_output_is_required(StringRef identifier) const = 0;
};
class GeoNodeResolveParams {
};
class GeoNodeExecParams {
private:
GeoNodeExecParamsProvider *provider_;
@@ -291,6 +295,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;

View File

@@ -276,6 +276,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", "")

View File

@@ -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(bNodeTreeType *UNUSED(ntreetype
SOCK_GEOMETRY,
SOCK_COLLECTION,
SOCK_TEXTURE,
SOCK_MATERIAL);
SOCK_MATERIAL,
SOCK_ATTRIBUTE);
}
void register_node_tree_type_geo(void)

View File

@@ -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),

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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. */
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. */
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

View File

@@ -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);

View File

@@ -67,6 +67,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. */
@@ -84,7 +112,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);
@@ -184,6 +211,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
{

View File

@@ -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;
@@ -664,6 +694,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)
@@ -751,6 +845,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());

View File

@@ -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;
}

View File

@@ -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;
}