Compare commits
38 Commits
temp-field
...
geometry-n
Author | SHA1 | Date | |
---|---|---|---|
ca50a1f762 | |||
d35969a74f | |||
efe2247e3c | |||
a08ed1dce3 | |||
a71af6fd2d | |||
083f2b1ae2 | |||
169eff3c58 | |||
bc72fe209f | |||
b918334178 | |||
961bba9d77 | |||
16cc5215e0 | |||
02804b3de6 | |||
ddbc1206db | |||
5ef0b2554b | |||
8958aa3e1c | |||
58a9030353 | |||
f4dab78d54 | |||
5b6291d202 | |||
c28d397d2f | |||
0f4b9bfb3f | |||
86277ed920 | |||
a40ee3c86b | |||
5d0cc7c02a | |||
346a8d6cfd | |||
48514481de | |||
0274a96af6 | |||
4af9963acd | |||
757611dbd2 | |||
8c7aa2fc20 | |||
8a3963fe16 | |||
d74fa0cdcf | |||
b621a3e3da | |||
32072995f2 | |||
c0c1f0dae1 | |||
23f20558a6 | |||
0e2d77990c | |||
84a7090e82 | |||
465930675b |
112
doc/guides/GN_converting_attribute_sockets.md
Normal file
112
doc/guides/GN_converting_attribute_sockets.md
Normal 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 ¶ms)
|
||||
{
|
||||
const std::string result_name = params.get_input<std::string>("Result");
|
||||
|
||||
[...]
|
||||
}
|
||||
```
|
||||
|
||||
New:
|
||||
|
||||
```cpp
|
||||
static void do_the_actual_work(GeometryComponent &component,
|
||||
const GeoNodeExecParams ¶ms,
|
||||
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 ¶ms,
|
||||
const AttributeRef &result)
|
||||
{
|
||||
[...]
|
||||
OutputAttribute_Typed<float> attribute_result =
|
||||
component.attribute_try_get_for_output_only<float>(result.name(), result_domain);
|
||||
|
||||
[...]
|
||||
}
|
||||
```
|
@@ -482,6 +482,8 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeAttributeConvert"),
|
||||
NodeItem("GeometryNodeAttributeCurveMap"),
|
||||
NodeItem("GeometryNodeAttributeFill"),
|
||||
NodeItem("GeometryNodeAttributeGet"),
|
||||
NodeItem("GeometryNodeAttributeSet"),
|
||||
NodeItem("GeometryNodeAttributeMix"),
|
||||
NodeItem("GeometryNodeAttributeProximity"),
|
||||
NodeItem("GeometryNodeAttributeColorRamp"),
|
||||
|
@@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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[];
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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");
|
||||
|
@@ -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()) {
|
||||
|
@@ -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
|
||||
|
77
source/blender/nodes/NOD_attribute_ref.hh
Normal file
77
source/blender/nodes/NOD_attribute_ref.hh
Normal 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();
|
||||
}
|
||||
};
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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", "")
|
||||
|
@@ -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)
|
||||
|
@@ -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),
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
@@ -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 ¶ms,
|
||||
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 ¶ms)
|
||||
static void attribute_math_calc(GeometryComponent &component,
|
||||
const GeoNodeExecParams ¶ms,
|
||||
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
|
||||
|
@@ -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 ¶ms,
|
||||
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);
|
||||
}
|
||||
|
@@ -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 ¶ms)
|
||||
static void execute_on_component(GeometryComponent &component,
|
||||
const GeoNodeExecParams ¶ms,
|
||||
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;
|
||||
|
107
source/blender/nodes/geometry/nodes/node_geo_attribute_set.cc
Normal file
107
source/blender/nodes/geometry/nodes/node_geo_attribute_set.cc
Normal 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 ¶ms,
|
||||
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);
|
||||
}
|
@@ -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 ¶ms,
|
||||
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 ¶ms)
|
||||
GeoNodeExecParams ¶ms,
|
||||
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
|
||||
|
67
source/blender/nodes/intern/attribute_ref.cc
Normal file
67
source/blender/nodes/intern/attribute_ref.cc
Normal 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);
|
@@ -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
|
||||
{
|
||||
|
@@ -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());
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user