Compare commits
154 Commits
wintab
...
temp-attri
Author | SHA1 | Date | |
---|---|---|---|
206f3352dd | |||
dd92220f0b | |||
5639dcef62 | |||
a20e0fd1a5 | |||
448710996b | |||
3b41749245 | |||
00944353be | |||
623818f2d5 | |||
4b7eae5cd3 | |||
1172c0ae44 | |||
7f17d15125 | |||
b33fd7e89e | |||
10b78013eb | |||
3d057de976 | |||
3817b408e0 | |||
b8d3ad515a | |||
022b541cb3 | |||
e294e7a99c | |||
7928578f18 | |||
369aa3a8a0 | |||
243b5ac98c | |||
b63483baec | |||
4ee0e62986 | |||
b2184402ae | |||
028777bdba | |||
e43c171455 | |||
e433e4ae42 | |||
f22aa19845 | |||
3391649edd | |||
9e95222cb7 | |||
ef07d5a297 | |||
09ec88413b | |||
b9c8bb4254 | |||
64eb1df4b3 | |||
9a9b87d9c5 | |||
257b6bcc61 | |||
b53babaae7 | |||
aa9efbedf1 | |||
93a5fe3a37 | |||
90de4edc06 | |||
138a0c8109 | |||
3a06d098da | |||
10636857f8 | |||
e69f9fe0ca | |||
3b39482e2c | |||
82c72ccdbc | |||
6ea2f106a0 | |||
c9d4f5d625 | |||
22c7967205 | |||
56088650be | |||
14ec1f4e13 | |||
33d262a7c8 | |||
ec04ff8a33 | |||
c5c607bdaa | |||
f4392d1423 | |||
005f7318b2 | |||
93e08fdf90 | |||
92e16e35dc | |||
2abb09aa2b | |||
a371d4bb2b | |||
03f9dba616 | |||
![]() |
5f2ad6751c | ||
04e6656ff1 | |||
![]() |
0ed005d48a | ||
1f33e20f55 | |||
946f53d86e | |||
7ab79ac6ba | |||
![]() |
049924c240 | ||
6118cbb78e | |||
bb2e2ad3ff | |||
467f97fb57 | |||
236ff239bb | |||
5e0068f3f7 | |||
![]() |
dfcb4abd4c | ||
60bd05f920 | |||
688671b9d7 | |||
07961e5358 | |||
01f2dad596 | |||
ffa4c53d10 | |||
88c16c4e7a | |||
fed3c58f92 | |||
77f3b9ea9f | |||
1dd65919e5 | |||
bdada925e3 | |||
89602a8366 | |||
96a8189d74 | |||
8f18d076e9 | |||
1077c37f1b | |||
f54fc8ac6e | |||
a7e337957d | |||
d6a773345b | |||
ff0259de92 | |||
6dac1211b1 | |||
7568501934 | |||
2200c5f761 | |||
f11182b1be | |||
a069bd219c | |||
3d1a0f97f7 | |||
2d37e469a2 | |||
20b0de7db1 | |||
1b5288b87f | |||
e83d96753c | |||
de5e3ef8e5 | |||
91544db137 | |||
2cc7f32f18 | |||
9e16740ca1 | |||
fc94932596 | |||
97e363a06a | |||
7d47eea06d | |||
5dbe82f129 | |||
9bd5dff790 | |||
c3427196c2 | |||
21c61092f0 | |||
59ffc4945a | |||
07b7158c29 | |||
1b29d18790 | |||
f68eac3945 | |||
e99d523901 | |||
aa46b36a3f | |||
cf84056954 | |||
47d90d9a96 | |||
bbf78af3c5 | |||
3da0ad6bb2 | |||
002e75fa24 | |||
427ea6e4ec | |||
c1e587bc46 | |||
0a082f9ccd | |||
01321c7825 | |||
abbd01aff0 | |||
40cbdf56fd | |||
29e5a8b200 | |||
aa363a71cd | |||
d42a2cfef4 | |||
1564743475 | |||
d932728ac1 | |||
dcc7786f14 | |||
27fc7ca66c | |||
9807cb0eef | |||
1f25ab4bb9 | |||
cadfba7aad | |||
be5f911e2a | |||
c37de30fdb | |||
95d64038f4 | |||
7f32a3ddd3 | |||
fcba670314 | |||
4fe0ef5eab | |||
4a6a755afd | |||
64b4e66afb | |||
ec77f2d11d | |||
e36fe7b78f | |||
a795d243cb | |||
4c322f3fba | |||
03d31e568f | |||
1291e369f9 |
@@ -367,6 +367,39 @@ class NODE_OT_active_preview_toggle(Operator):
|
||||
return spreadsheets
|
||||
|
||||
|
||||
class NODE_OT_new_attribute_processor_group(Operator):
|
||||
bl_idname = "node.new_attribute_processor_group"
|
||||
bl_label = "New Attribute Processor Group"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
node_name: StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
space = context.space_data
|
||||
if space is None:
|
||||
return False
|
||||
if space.type != 'NODE_EDITOR':
|
||||
return False
|
||||
return space.edit_tree is not None
|
||||
|
||||
def execute(self, context):
|
||||
node = context.space_data.edit_tree.nodes[self.node_name]
|
||||
if node.bl_idname != "GeometryNodeAttributeProcessor":
|
||||
return {'CANCELLED'}
|
||||
|
||||
group = bpy.data.node_groups.new("Attribute Group", "AttributeNodeTree")
|
||||
|
||||
input_node = group.nodes.new('NodeGroupInput')
|
||||
output_node = group.nodes.new('NodeGroupOutput')
|
||||
|
||||
input_node.location.x = -200 - input_node.width
|
||||
output_node.location.x = 200
|
||||
|
||||
node.node_tree = group
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
NodeSetting,
|
||||
|
||||
@@ -376,4 +409,5 @@ classes = (
|
||||
NODE_OT_collapse_hide_unused_toggle,
|
||||
NODE_OT_tree_path_parent,
|
||||
NODE_OT_active_preview_toggle,
|
||||
NODE_OT_new_attribute_processor_group,
|
||||
)
|
||||
|
@@ -30,39 +30,60 @@ from nodeitems_utils import (
|
||||
|
||||
class SortedNodeCategory(NodeCategory):
|
||||
def __init__(self, identifier, name, description="", items=None):
|
||||
# for builtin nodes the convention is to sort by name
|
||||
if isinstance(items, list):
|
||||
items = sorted(items, key=lambda item: item.label.lower())
|
||||
# Sort node items by name but leave custom node items where they are.
|
||||
sort_key = lambda value: value.label.lower()
|
||||
new_items = []
|
||||
section = []
|
||||
for item in items:
|
||||
if isinstance(item, NodeItemCustom):
|
||||
new_items.extend(sorted(section, key=sort_key))
|
||||
new_items.append(item)
|
||||
section.clear()
|
||||
else:
|
||||
section.append(item)
|
||||
new_items.extend(sorted(section, key=sort_key))
|
||||
items = new_items
|
||||
|
||||
super().__init__(identifier, name, description=description, items=items)
|
||||
|
||||
|
||||
def edit_tree_is_type(context, idname):
|
||||
space = context.space_data
|
||||
if space.type != 'NODE_EDITOR':
|
||||
return False
|
||||
tree = space.edit_tree
|
||||
if tree is None:
|
||||
return space.tree_type == idname
|
||||
return tree.bl_idname == idname
|
||||
|
||||
class CompositorNodeCategory(SortedNodeCategory):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.space_data.type == 'NODE_EDITOR' and
|
||||
context.space_data.tree_type == 'CompositorNodeTree')
|
||||
return edit_tree_is_type(context, 'CompositorNodeTree')
|
||||
|
||||
|
||||
class ShaderNodeCategory(SortedNodeCategory):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.space_data.type == 'NODE_EDITOR' and
|
||||
context.space_data.tree_type == 'ShaderNodeTree')
|
||||
return edit_tree_is_type(context, 'ShaderNodeTree')
|
||||
|
||||
|
||||
class TextureNodeCategory(SortedNodeCategory):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.space_data.type == 'NODE_EDITOR' and
|
||||
context.space_data.tree_type == 'TextureNodeTree')
|
||||
return edit_tree_is_type(context, 'TextureNodeTree')
|
||||
|
||||
|
||||
class GeometryNodeCategory(SortedNodeCategory):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.space_data.type == 'NODE_EDITOR' and
|
||||
context.space_data.tree_type == 'GeometryNodeTree')
|
||||
return edit_tree_is_type(context, 'GeometryNodeTree')
|
||||
|
||||
class AttributeNodeCategory(SortedNodeCategory):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return edit_tree_is_type(context, 'AttributeNodeTree')
|
||||
|
||||
|
||||
# menu entry for node group tools
|
||||
@@ -78,6 +99,7 @@ node_tree_group_type = {
|
||||
'ShaderNodeTree': 'ShaderNodeGroup',
|
||||
'TextureNodeTree': 'TextureNodeGroup',
|
||||
'GeometryNodeTree': 'GeometryNodeGroup',
|
||||
'AttributeNodeTree': 'AttributeNodeGroup',
|
||||
}
|
||||
|
||||
|
||||
@@ -475,6 +497,8 @@ texture_node_categories = [
|
||||
geometry_node_categories = [
|
||||
# Geometry Nodes
|
||||
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
|
||||
NodeItem("GeometryNodeAttributeProcessor"),
|
||||
NodeItemCustom(draw=lambda self, layout, context: layout.separator()),
|
||||
NodeItem("GeometryNodeAttributeRandomize"),
|
||||
NodeItem("GeometryNodeAttributeMath"),
|
||||
NodeItem("GeometryNodeAttributeClamp"),
|
||||
@@ -585,12 +609,57 @@ geometry_node_categories = [
|
||||
]),
|
||||
]
|
||||
|
||||
attribute_node_categories = [
|
||||
# Attribute Processor Nodes
|
||||
AttributeNodeCategory("ATTR_COLOR", "Color", items=[
|
||||
NodeItem("ShaderNodeValToRGB"),
|
||||
NodeItem("ShaderNodeSeparateRGB"),
|
||||
NodeItem("ShaderNodeCombineRGB"),
|
||||
]),
|
||||
AttributeNodeCategory("ATTR_INPUT", "Input", items=[
|
||||
NodeItem("FunctionNodeRandomFloat"),
|
||||
NodeItem("ShaderNodeValue"),
|
||||
NodeItem("FunctionNodeInputString"),
|
||||
NodeItem("FunctionNodeInputVector"),
|
||||
NodeItem("AttributeNodeIndex"),
|
||||
NodeItem("AttributeNodeAttributeInput"),
|
||||
NodeItem("AttributeNodePositionInput"),
|
||||
]),
|
||||
AttributeNodeCategory("ATTR_OUTPUT", "Output", items=[
|
||||
NodeItem("AttributeNodeSetAttribute"),
|
||||
NodeItem("AttributeNodePositionOutput"),
|
||||
]),
|
||||
AttributeNodeCategory("ATTR_TEXTURE", "Texture", items=[
|
||||
NodeItem("ShaderNodeTexNoise"),
|
||||
NodeItem("ShaderNodeTexVoronoi"),
|
||||
]),
|
||||
AttributeNodeCategory("ATTR_UTILITIES", "Utilities", items=[
|
||||
NodeItem("ShaderNodeMapRange"),
|
||||
NodeItem("ShaderNodeClamp"),
|
||||
NodeItem("ShaderNodeMath"),
|
||||
NodeItem("FunctionNodeBooleanMath"),
|
||||
NodeItem("FunctionNodeFloatCompare"),
|
||||
]),
|
||||
AttributeNodeCategory("ATTR_VECTOR", "Vector", items=[
|
||||
NodeItem("ShaderNodeSeparateXYZ"),
|
||||
NodeItem("ShaderNodeCombineXYZ"),
|
||||
NodeItem("ShaderNodeVectorMath"),
|
||||
NodeItem("ShaderNodeVectorRotate"),
|
||||
]),
|
||||
AttributeNodeCategory("ATTR_GROUP", "Group", items=node_group_items),
|
||||
AttributeNodeCategory("ATTR_LAYOUT", "Layout", items=[
|
||||
NodeItem("NodeFrame"),
|
||||
NodeItem("NodeReroute"),
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
nodeitems_utils.register_node_categories('SHADER', shader_node_categories)
|
||||
nodeitems_utils.register_node_categories('COMPOSITING', compositor_node_categories)
|
||||
nodeitems_utils.register_node_categories('TEXTURE', texture_node_categories)
|
||||
nodeitems_utils.register_node_categories('GEOMETRY', geometry_node_categories)
|
||||
nodeitems_utils.register_node_categories('ATTRIBUTE', attribute_node_categories)
|
||||
|
||||
|
||||
def unregister():
|
||||
@@ -598,6 +667,7 @@ def unregister():
|
||||
nodeitems_utils.unregister_node_categories('COMPOSITING')
|
||||
nodeitems_utils.unregister_node_categories('TEXTURE')
|
||||
nodeitems_utils.unregister_node_categories('GEOMETRY')
|
||||
nodeitems_utils.unregister_node_categories('ATTRIBUTE')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@@ -1439,6 +1439,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_SEPARATE_COMPONENTS 1059
|
||||
#define GEO_NODE_CURVE_SUBDIVIDE 1060
|
||||
#define GEO_NODE_RAYCAST 1061
|
||||
#define GEO_NODE_ATTRIBUTE_PROCESSOR 1062
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -1454,6 +1455,18 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Attribute Nodes
|
||||
* \{ */
|
||||
|
||||
#define ATTR_NODE_INDEX 1400
|
||||
#define ATTR_NODE_SET_ATTRIBUTE 1401
|
||||
#define ATTR_NODE_POSITION_INPUT 1402
|
||||
#define ATTR_NODE_POSITION_OUTPUT 1403
|
||||
#define ATTR_NODE_ATTRIBUTE_INPUT 1404
|
||||
|
||||
/** \} */
|
||||
|
||||
void BKE_node_system_init(void);
|
||||
void BKE_node_system_exit(void);
|
||||
|
||||
|
@@ -480,6 +480,7 @@ static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock)
|
||||
}
|
||||
|
||||
write_node_socket_default_value(writer, sock);
|
||||
BLO_write_string(writer, sock->default_attribute_name);
|
||||
}
|
||||
|
||||
/* this is only direct data, tree itself should have been written */
|
||||
@@ -579,6 +580,21 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
|
||||
}
|
||||
BLO_write_struct_by_name(writer, node->typeinfo->storagename, storage);
|
||||
}
|
||||
else if (node->type == GEO_NODE_ATTRIBUTE_PROCESSOR) {
|
||||
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
|
||||
BLO_write_struct(writer, NodeGeometryAttributeProcessor, storage);
|
||||
BLO_write_struct_list(writer, AttributeProcessorInputSettings, &storage->inputs_settings);
|
||||
BLO_write_struct_list(
|
||||
writer, AttributeProcessorOutputSettings, &storage->outputs_settings);
|
||||
LISTBASE_FOREACH (
|
||||
AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
|
||||
BLO_write_string(writer, input_settings->identifier);
|
||||
}
|
||||
LISTBASE_FOREACH (
|
||||
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
|
||||
BLO_write_string(writer, output_settings->identifier);
|
||||
}
|
||||
}
|
||||
else if (node->typeinfo != &NodeTypeUndefined) {
|
||||
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
|
||||
}
|
||||
@@ -638,6 +654,7 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
|
||||
sock->typeinfo = nullptr;
|
||||
BLO_read_data_address(reader, &sock->storage);
|
||||
BLO_read_data_address(reader, &sock->default_value);
|
||||
BLO_read_data_address(reader, &sock->default_attribute_name);
|
||||
sock->total_inputs = 0; /* Clear runtime data set before drawing. */
|
||||
sock->cache = nullptr;
|
||||
}
|
||||
@@ -763,6 +780,21 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* TODO: Handling this separately now so that it doesn't break when node->type changes when
|
||||
* merging master. */
|
||||
if (STREQ(node->idname, "GeometryNodeAttributeProcessor")) {
|
||||
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
|
||||
BLO_read_list(reader, &storage->inputs_settings);
|
||||
BLO_read_list(reader, &storage->outputs_settings);
|
||||
LISTBASE_FOREACH (
|
||||
AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
|
||||
BLO_read_data_address(reader, &input_settings->identifier);
|
||||
}
|
||||
LISTBASE_FOREACH (
|
||||
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
|
||||
BLO_read_data_address(reader, &output_settings->identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BLO_read_list(reader, &ntree->links);
|
||||
@@ -2124,6 +2156,9 @@ static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src,
|
||||
socket_id_user_increment(sock_dst);
|
||||
}
|
||||
}
|
||||
if (sock_src->default_attribute_name) {
|
||||
sock_dst->default_attribute_name = (char *)MEM_dupallocN(sock_src->default_attribute_name);
|
||||
}
|
||||
|
||||
sock_dst->stack_index = 0;
|
||||
/* XXX some compositor node (e.g. image, render layers) still store
|
||||
@@ -3076,6 +3111,9 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
|
||||
}
|
||||
MEM_freeN(sock->default_value);
|
||||
}
|
||||
if (sock->default_attribute_name) {
|
||||
MEM_freeN(sock->default_attribute_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_localized_node_groups(bNodeTree *ntree)
|
||||
@@ -3610,7 +3648,8 @@ bool ntreeHasTree(const bNodeTree *ntree, const bNodeTree *lookup)
|
||||
return true;
|
||||
}
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id) {
|
||||
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP, GEO_NODE_ATTRIBUTE_PROCESSOR) &&
|
||||
node->id) {
|
||||
if (ntreeHasTree((bNodeTree *)node->id, lookup)) {
|
||||
return true;
|
||||
}
|
||||
@@ -5042,6 +5081,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_attribute_map_range();
|
||||
register_node_type_geo_attribute_math();
|
||||
register_node_type_geo_attribute_mix();
|
||||
register_node_type_geo_attribute_processor();
|
||||
register_node_type_geo_attribute_proximity();
|
||||
register_node_type_geo_attribute_randomize();
|
||||
register_node_type_geo_attribute_separate_xyz();
|
||||
@@ -5093,6 +5133,14 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_transform();
|
||||
register_node_type_geo_triangulate();
|
||||
register_node_type_geo_volume_to_mesh();
|
||||
|
||||
register_node_type_attr_group();
|
||||
|
||||
register_node_type_attr_attribute_input();
|
||||
register_node_type_attr_index();
|
||||
register_node_type_attr_set_attribute();
|
||||
register_node_type_attr_position_input();
|
||||
register_node_type_attr_position_output();
|
||||
}
|
||||
|
||||
static void registerFunctionNodes()
|
||||
@@ -5118,6 +5166,7 @@ void BKE_node_system_init(void)
|
||||
register_node_tree_type_sh();
|
||||
register_node_tree_type_tex();
|
||||
register_node_tree_type_geo();
|
||||
register_node_tree_type_attr();
|
||||
|
||||
register_node_type_frame();
|
||||
register_node_type_reroute();
|
||||
|
@@ -1764,7 +1764,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
|
||||
else if (id_type == ID_MC) {
|
||||
build_movieclip((MovieClip *)id);
|
||||
}
|
||||
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
|
||||
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP, GEO_NODE_ATTRIBUTE_PROCESSOR)) {
|
||||
bNodeTree *group_ntree = (bNodeTree *)id;
|
||||
build_nodetree(group_ntree);
|
||||
}
|
||||
|
@@ -2490,7 +2490,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
|
||||
OperationKey clip_key(id, NodeType::PARAMETERS, OperationCode::MOVIECLIP_EVAL);
|
||||
add_relation(clip_key, shading_key, "Clip -> Node");
|
||||
}
|
||||
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
|
||||
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP, GEO_NODE_ATTRIBUTE_PROCESSOR)) {
|
||||
bNodeTree *group_ntree = (bNodeTree *)id;
|
||||
build_nodetree(group_ntree);
|
||||
ComponentKey group_shading_key(&group_ntree->id, NodeType::SHADING);
|
||||
|
@@ -618,6 +618,44 @@ static int node_tweak_area_reroute(bNode *node, int x, int y)
|
||||
return (dx * dx + dy * dy <= tweak_radius_sq);
|
||||
}
|
||||
|
||||
static void node_draw_buttons_group_input(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree *ngroup = (bNodeTree *)ptr->owner_id;
|
||||
if (ngroup->type != NTREE_ATTRIBUTE) {
|
||||
return;
|
||||
}
|
||||
|
||||
PointerRNA props;
|
||||
uiItemFullO(layout,
|
||||
"node.group_interface_add",
|
||||
"New",
|
||||
ICON_ADD,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
0,
|
||||
&props);
|
||||
RNA_enum_set(&props, "in_out", SOCK_IN);
|
||||
}
|
||||
|
||||
static void node_draw_buttons_group_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree *ngroup = (bNodeTree *)ptr->owner_id;
|
||||
if (ngroup->type != NTREE_ATTRIBUTE) {
|
||||
return;
|
||||
}
|
||||
|
||||
PointerRNA props;
|
||||
uiItemFullO(layout,
|
||||
"node.group_interface_add",
|
||||
"New",
|
||||
ICON_ADD,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
0,
|
||||
&props);
|
||||
RNA_enum_set(&props, "in_out", SOCK_OUT);
|
||||
}
|
||||
|
||||
static void node_common_set_butfunc(bNodeType *ntype)
|
||||
{
|
||||
switch (ntype->type) {
|
||||
@@ -635,6 +673,12 @@ static void node_common_set_butfunc(bNodeType *ntype)
|
||||
ntype->draw_nodetype_prepare = node_draw_reroute_prepare;
|
||||
ntype->tweak_area_func = node_tweak_area_reroute;
|
||||
break;
|
||||
case NODE_GROUP_INPUT:
|
||||
ntype->draw_buttons = node_draw_buttons_group_input;
|
||||
break;
|
||||
case NODE_GROUP_OUTPUT:
|
||||
ntype->draw_buttons = node_draw_buttons_group_output;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,7 +768,10 @@ static void node_shader_buts_vect_transform(uiLayout *layout, bContext *UNUSED(C
|
||||
|
||||
static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "attribute_type", DEFAULT_FLAGS, IFACE_("Type"), ICON_NONE);
|
||||
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
|
||||
if (ntree->type == NTREE_SHADER) {
|
||||
uiItemR(layout, ptr, "attribute_type", DEFAULT_FLAGS, IFACE_("Type"), ICON_NONE);
|
||||
}
|
||||
uiItemR(layout, ptr, "attribute_name", DEFAULT_FLAGS, IFACE_("Name"), ICON_NONE);
|
||||
}
|
||||
|
||||
@@ -3419,6 +3466,7 @@ void ED_node_init_butfuncs(void)
|
||||
ntreeType_Shader->ui_icon = ICON_NODE_MATERIAL;
|
||||
ntreeType_Texture->ui_icon = ICON_NODE_TEXTURE;
|
||||
ntreeType_Geometry->ui_icon = ICON_NODETREE;
|
||||
ntreeType_Attribute->ui_icon = ICON_NODETREE;
|
||||
}
|
||||
|
||||
void ED_init_custom_node_type(bNodeType *ntype)
|
||||
|
@@ -165,6 +165,10 @@ static void draw_socket_list(const bContext *C,
|
||||
if (socket->typeinfo->interface_draw) {
|
||||
socket->typeinfo->interface_draw((bContext *)C, layout, &socket_ptr);
|
||||
}
|
||||
|
||||
if (ntree->type == NTREE_ATTRIBUTE) {
|
||||
uiItemR(layout, &socket_ptr, "default_attribute_name", 0, "Attribute", ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -50,6 +50,7 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
@@ -76,7 +77,8 @@ static bool node_group_operator_active_poll(bContext *C)
|
||||
"ShaderNodeTree",
|
||||
"CompositorNodeTree",
|
||||
"TextureNodeTree",
|
||||
"GeometryNodeTree")) {
|
||||
"GeometryNodeTree",
|
||||
"AttributeNodeTree")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -102,25 +104,33 @@ static bool node_group_operator_editable(bContext *C)
|
||||
static const char *group_ntree_idname(bContext *C)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
return snode->tree_idname;
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
return ntree ? ntree->idname : "";
|
||||
}
|
||||
|
||||
const char *node_group_idname(bContext *C)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
if (ntree == NULL) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ED_node_is_shader(snode)) {
|
||||
if (STREQ(ntree->idname, "ShaderNodeTree")) {
|
||||
return "ShaderNodeGroup";
|
||||
}
|
||||
if (ED_node_is_compositor(snode)) {
|
||||
if (STREQ(ntree->idname, "CompositorNodeTree")) {
|
||||
return "CompositorNodeGroup";
|
||||
}
|
||||
if (ED_node_is_texture(snode)) {
|
||||
if (STREQ(ntree->idname, "TextureNodeTree")) {
|
||||
return "TextureNodeGroup";
|
||||
}
|
||||
if (ED_node_is_geometry(snode)) {
|
||||
if (STREQ(ntree->idname, "GeometryNodeTree")) {
|
||||
return "GeometryNodeGroup";
|
||||
}
|
||||
if (STREQ(ntree->idname, "AttributeNodeTree")) {
|
||||
return "AttributeNodeGroup";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
@@ -145,18 +155,15 @@ static bNode *node_group_get_active(bContext *C, const char *node_idname)
|
||||
static int node_group_edit_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
const char *node_idname = node_group_idname(C);
|
||||
const bool exit = RNA_boolean_get(op->ptr, "exit");
|
||||
|
||||
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
||||
|
||||
bNode *gnode = node_group_get_active(C, node_idname);
|
||||
|
||||
if (gnode && !exit) {
|
||||
bNodeTree *ngroup = (bNodeTree *)gnode->id;
|
||||
|
||||
bNode *node = nodeGetActive(snode->edittree);
|
||||
if (!exit && node != NULL && node->id != NULL && GS(node->id->name) == ID_NT) {
|
||||
bNodeTree *ngroup = (bNodeTree *)node->id;
|
||||
if (ngroup) {
|
||||
ED_node_tree_push(snode, ngroup, gnode);
|
||||
ED_node_tree_push(snode, ngroup, node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1130,3 +1137,110 @@ void NODE_OT_group_insert(wmOperatorType *ot)
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Group Interface Add Operator
|
||||
* \{ */
|
||||
|
||||
static int node_group_interface_add_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *UNUSED(event))
|
||||
{
|
||||
return WM_operator_props_dialog_popup(C, op, 300);
|
||||
}
|
||||
|
||||
static int node_group_interface_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
|
||||
const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
|
||||
const eNodeSocketDatatype type = (eNodeSocketDatatype)RNA_enum_get(op->ptr, "type");
|
||||
char *name = RNA_string_get_alloc(op->ptr, "name", nullptr, 0);
|
||||
|
||||
const char *socket_idname = nullptr;
|
||||
NODE_SOCKET_TYPES_BEGIN (st) {
|
||||
if (st->type == type && st->subtype == PROP_NONE) {
|
||||
socket_idname = st->idname;
|
||||
break;
|
||||
}
|
||||
}
|
||||
NODE_SOCKET_TYPES_END;
|
||||
BLI_assert(socket_idname != nullptr);
|
||||
|
||||
bNodeSocket *sock = ntreeAddSocketInterface(ntree, in_out, socket_idname, name);
|
||||
sock->default_attribute_name = RNA_string_get_alloc(op->ptr, "attribute_name", nullptr, 0);
|
||||
|
||||
if (name != nullptr) {
|
||||
MEM_freeN(name);
|
||||
}
|
||||
|
||||
ntreeUpdateTree(bmain, ntree);
|
||||
snode_notify(C, snode);
|
||||
snode_dag_update(C, snode);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *node_group_interface_add_type_items(bContext *C,
|
||||
PointerRNA *UNUSED(ptr),
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
EnumPropertyItem *items = nullptr;
|
||||
int totitem = 0;
|
||||
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
|
||||
for (const EnumPropertyItem *item = rna_enum_node_socket_type_items; item->identifier; item++) {
|
||||
if (ntree->typeinfo->valid_socket_type((eNodeSocketDatatype)item->value, ntree->typeinfo)) {
|
||||
RNA_enum_item_add(&items, &totitem, item);
|
||||
}
|
||||
}
|
||||
RNA_enum_item_end(&items, &totitem);
|
||||
|
||||
*r_free = true;
|
||||
return items;
|
||||
}
|
||||
|
||||
static void node_group_interface_add_ui(bContext *UNUSED(C), wmOperator *op)
|
||||
{
|
||||
uiLayout *layout = op->layout;
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
uiLayoutSetPropDecorate(layout, false);
|
||||
uiLayoutSetActivateInit(layout, true);
|
||||
uiItemR(layout, op->ptr, "name", 0, "Name", ICON_NONE);
|
||||
uiItemR(layout, op->ptr, "attribute_name", 0, "Default Attribute", ICON_NONE);
|
||||
uiItemR(layout, op->ptr, "type", 0, "Type", ICON_NONE);
|
||||
}
|
||||
|
||||
void NODE_OT_group_interface_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Group Interface Add";
|
||||
ot->description = "Add interface to node group";
|
||||
ot->idname = "NODE_OT_group_interface_add";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = node_group_interface_add_invoke;
|
||||
ot->exec = node_group_interface_add_exec;
|
||||
ot->poll = node_group_operator_editable;
|
||||
ot->ui = node_group_interface_add_ui;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Side", "");
|
||||
|
||||
RNA_def_string(ot->srna, "name", nullptr, MAX_NAME, "Name", "Name of the new interface socket");
|
||||
RNA_def_string(ot->srna, "attribute_name", nullptr, MAX_NAME, "Attribute Name", "");
|
||||
|
||||
prop = RNA_def_property(ot->srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, node_group_interface_add_type_items);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -228,6 +228,7 @@ void NODE_OT_group_insert(struct wmOperatorType *ot);
|
||||
void NODE_OT_group_ungroup(struct wmOperatorType *ot);
|
||||
void NODE_OT_group_separate(struct wmOperatorType *ot);
|
||||
void NODE_OT_group_edit(struct wmOperatorType *ot);
|
||||
void NODE_OT_group_interface_add(struct wmOperatorType *ot);
|
||||
|
||||
/* node_relationships.c */
|
||||
void sort_multi_input_socket_links(struct SpaceNode *snode,
|
||||
|
@@ -76,6 +76,7 @@ void node_operatortypes(void)
|
||||
WM_operatortype_append(NODE_OT_group_ungroup);
|
||||
WM_operatortype_append(NODE_OT_group_separate);
|
||||
WM_operatortype_append(NODE_OT_group_edit);
|
||||
WM_operatortype_append(NODE_OT_group_interface_add);
|
||||
|
||||
WM_operatortype_append(NODE_OT_link_viewer);
|
||||
|
||||
|
@@ -1027,7 +1027,14 @@ static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item,
|
||||
{
|
||||
bool free;
|
||||
const EnumPropertyItem *item_src = RNA_enum_node_tree_types_itemf_impl(C, &free);
|
||||
RNA_enum_items_add(item, totitem, item_src);
|
||||
for (const EnumPropertyItem *item_iter = item_src; item_iter->identifier; item_iter++) {
|
||||
/* Attribute node trees don't have their own space subtype, they can be accessed through
|
||||
* geometry nodes. */
|
||||
if (STREQ(item_iter->identifier, "AttributeNodeTree")) {
|
||||
continue;
|
||||
}
|
||||
RNA_enum_item_add(item, totitem, item_iter);
|
||||
}
|
||||
if (free) {
|
||||
MEM_freeN((void *)item_src);
|
||||
}
|
||||
|
@@ -114,6 +114,7 @@ typedef struct bNodeSocket {
|
||||
|
||||
/** Default input value used for unlinked sockets. */
|
||||
void *default_value;
|
||||
char *default_attribute_name;
|
||||
|
||||
/* execution data */
|
||||
/** Local stack index. */
|
||||
@@ -525,6 +526,7 @@ typedef struct bNodeTree {
|
||||
#define NTREE_COMPOSIT 1
|
||||
#define NTREE_TEXTURE 2
|
||||
#define NTREE_GEOMETRY 3
|
||||
#define NTREE_ATTRIBUTE 4
|
||||
|
||||
/* ntree->init, flag */
|
||||
#define NTREE_TYPE_INIT 1
|
||||
@@ -1388,6 +1390,50 @@ typedef struct NodeGeometryRaycast {
|
||||
char _pad[1];
|
||||
} NodeGeometryRaycast;
|
||||
|
||||
typedef struct AttributeProcessorInputSettings {
|
||||
struct AttributeProcessorInputSettings *next, *prev;
|
||||
|
||||
char *identifier;
|
||||
|
||||
/* GeometryNodeAttributeProcessorInputMode. */
|
||||
uint8_t input_mode;
|
||||
char _pad[7];
|
||||
} AttributeProcessorInputSettings;
|
||||
|
||||
typedef struct AttributeProcessorOutputSettings {
|
||||
struct AttributeProcessorOutputSettings *next, *prev;
|
||||
|
||||
char *identifier;
|
||||
|
||||
/* GeometryNodeAttributeProcessorOutputMode. */
|
||||
uint8_t output_mode;
|
||||
char _pad[7];
|
||||
} AttributeProcessorOutputSettings;
|
||||
|
||||
typedef struct NodeGeometryAttributeProcessor {
|
||||
/* AttributeDomain. */
|
||||
int8_t domain;
|
||||
char _pad[7];
|
||||
/* List of AttributeProcessorInputSettings. */
|
||||
ListBase inputs_settings;
|
||||
/* List of AttributeProcessorOutputSettings. */
|
||||
ListBase outputs_settings;
|
||||
} NodeGeometryAttributeProcessor;
|
||||
|
||||
typedef struct NodeAttributeSetAttribute {
|
||||
char attribute_name[64];
|
||||
/* eNodeSocketDatatype */
|
||||
uint8_t type;
|
||||
char _pad[7];
|
||||
} NodeAttributeSetAttribute;
|
||||
|
||||
typedef struct NodeAttributeAttributeInput {
|
||||
char attribute_name[64];
|
||||
/* eNodeSocketDatatype */
|
||||
uint8_t type;
|
||||
char _pad[7];
|
||||
} NodeAttributeAttributeInput;
|
||||
|
||||
/* script node mode */
|
||||
#define NODE_SCRIPT_INTERNAL 0
|
||||
#define NODE_SCRIPT_EXTERNAL 1
|
||||
@@ -1905,6 +1951,17 @@ typedef enum GeometryNodeRaycastMapMode {
|
||||
GEO_NODE_RAYCAST_NEAREST = 1,
|
||||
} GeometryNodeRaycastMapMode;
|
||||
|
||||
typedef enum GeometryNodeAttributeProcessorInputMode {
|
||||
GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_VALUE = 0,
|
||||
GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_ATTRIBUTE = 1,
|
||||
GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_DEFAULT = 2,
|
||||
} GeometryNodeAttributeProcessorInputMode;
|
||||
|
||||
typedef enum GeometryNodeAttributeProcessorOutputMode {
|
||||
GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_ATTRIBUTE = 0,
|
||||
GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_DEFAULT = 1,
|
||||
} GeometryNodeAttributeProcessorOutputMode;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -71,6 +71,9 @@ extern StructRNA RNA_ArrayGpencilModifier;
|
||||
extern StructRNA RNA_ArrayModifier;
|
||||
extern StructRNA RNA_Attribute;
|
||||
extern StructRNA RNA_AttributeGroup;
|
||||
extern StructRNA RNA_AttributeNodeTree;
|
||||
extern StructRNA RNA_AttributeProcessorInputSettings;
|
||||
extern StructRNA RNA_AttributeProcessorOutputSettings;
|
||||
extern StructRNA RNA_AssetMetaData;
|
||||
extern StructRNA RNA_AssetTag;
|
||||
extern StructRNA RNA_BackgroundImage;
|
||||
|
@@ -197,6 +197,7 @@ extern const EnumPropertyItem rna_enum_shading_type_items[];
|
||||
extern const EnumPropertyItem rna_enum_navigation_mode_items[];
|
||||
|
||||
extern const EnumPropertyItem rna_enum_node_socket_in_out_items[];
|
||||
extern const EnumPropertyItem rna_enum_node_socket_type_items[];
|
||||
|
||||
extern const EnumPropertyItem rna_enum_node_math_items[];
|
||||
extern const EnumPropertyItem rna_enum_mapping_type_items[];
|
||||
|
@@ -81,17 +81,7 @@ static const EnumPropertyItem node_socket_data_type_items[] = {
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
#ifndef RNA_RUNTIME
|
||||
static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
|
||||
{SOCK_DISPLAY_SHAPE_CIRCLE, "CIRCLE", 0, "Circle", ""},
|
||||
{SOCK_DISPLAY_SHAPE_SQUARE, "SQUARE", 0, "Square", ""},
|
||||
{SOCK_DISPLAY_SHAPE_DIAMOND, "DIAMOND", 0, "Diamond", ""},
|
||||
{SOCK_DISPLAY_SHAPE_CIRCLE_DOT, "CIRCLE_DOT", 0, "Circle with inner dot", ""},
|
||||
{SOCK_DISPLAY_SHAPE_SQUARE_DOT, "SQUARE_DOT", 0, "Square with inner dot", ""},
|
||||
{SOCK_DISPLAY_SHAPE_DIAMOND_DOT, "DIAMOND_DOT", 0, "Diamond with inner dot", ""},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
static const EnumPropertyItem node_socket_type_items[] = {
|
||||
const EnumPropertyItem rna_enum_node_socket_type_items[] = {
|
||||
{SOCK_CUSTOM, "CUSTOM", 0, "Custom", ""},
|
||||
{SOCK_FLOAT, "VALUE", 0, "Value", ""},
|
||||
{SOCK_INT, "INT", 0, "Integer", ""},
|
||||
@@ -109,6 +99,16 @@ static const EnumPropertyItem node_socket_type_items[] = {
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
#ifndef RNA_RUNTIME
|
||||
static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
|
||||
{SOCK_DISPLAY_SHAPE_CIRCLE, "CIRCLE", 0, "Circle", ""},
|
||||
{SOCK_DISPLAY_SHAPE_SQUARE, "SQUARE", 0, "Square", ""},
|
||||
{SOCK_DISPLAY_SHAPE_DIAMOND, "DIAMOND", 0, "Diamond", ""},
|
||||
{SOCK_DISPLAY_SHAPE_CIRCLE_DOT, "CIRCLE_DOT", 0, "Circle with inner dot", ""},
|
||||
{SOCK_DISPLAY_SHAPE_SQUARE_DOT, "SQUARE_DOT", 0, "Square with inner dot", ""},
|
||||
{SOCK_DISPLAY_SHAPE_DIAMOND_DOT, "DIAMOND_DOT", 0, "Diamond with inner dot", ""},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
static const EnumPropertyItem node_quality_items[] = {
|
||||
{NTREE_QUALITY_HIGH, "HIGH", 0, "High", "High quality"},
|
||||
{NTREE_QUALITY_MEDIUM, "MEDIUM", 0, "Medium", "Medium quality"},
|
||||
@@ -896,6 +896,20 @@ static const EnumPropertyItem *rna_node_static_type_itemf(bContext *UNUSED(C),
|
||||
# undef DefNode
|
||||
}
|
||||
|
||||
if (RNA_struct_is_a(ptr->type, &RNA_AttributeNode)) {
|
||||
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
|
||||
if (STREQ(#Category, "AttributeNode")) { \
|
||||
tmp.value = ID; \
|
||||
tmp.identifier = EnumName; \
|
||||
tmp.name = UIName; \
|
||||
tmp.description = UIDesc; \
|
||||
tmp.icon = ICON_NONE; \
|
||||
RNA_enum_item_add(&item, &totitem, &tmp); \
|
||||
}
|
||||
# include "../../nodes/NOD_static_types.h"
|
||||
# undef DefNode
|
||||
}
|
||||
|
||||
if (RNA_struct_is_a(ptr->type, &RNA_FunctionNode)) {
|
||||
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
|
||||
if (STREQ(#Category, "FunctionNode")) { \
|
||||
@@ -2016,6 +2030,20 @@ static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSE
|
||||
return itemf_function_check(node_socket_data_type_items, switch_type_supported);
|
||||
}
|
||||
|
||||
static bool set_attribute_type_supported(const EnumPropertyItem *item)
|
||||
{
|
||||
return ELEM(item->value, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA);
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_AttributeNodeSetAttribute_type_itemf(bContext *UNUSED(C),
|
||||
PointerRNA *UNUSED(ptr),
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
*r_free = true;
|
||||
return itemf_function_check(node_socket_data_type_items, set_attribute_type_supported);
|
||||
}
|
||||
|
||||
static bool attribute_clamp_type_supported(const EnumPropertyItem *item)
|
||||
{
|
||||
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
|
||||
@@ -2320,6 +2348,28 @@ static StructRNA *rna_GeometryNode_register(Main *bmain,
|
||||
return nt->rna_ext.srna;
|
||||
}
|
||||
|
||||
static StructRNA *rna_AttributeNode_register(Main *bmain,
|
||||
ReportList *reports,
|
||||
void *data,
|
||||
const char *identifier,
|
||||
StructValidateFunc validate,
|
||||
StructCallbackFunc call,
|
||||
StructFreeFunc free)
|
||||
{
|
||||
bNodeType *nt = rna_Node_register_base(
|
||||
bmain, reports, &RNA_AttributeNode, data, identifier, validate, call, free);
|
||||
if (!nt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nodeRegisterType(nt);
|
||||
|
||||
/* update while blender is running */
|
||||
WM_main_add_notifier(NC_NODE | NA_EDITED, NULL);
|
||||
|
||||
return nt->rna_ext.srna;
|
||||
}
|
||||
|
||||
static StructRNA *rna_FunctionNode_register(Main *bmain,
|
||||
ReportList *reports,
|
||||
void *data,
|
||||
@@ -3452,7 +3502,32 @@ static StructRNA *rna_GeometryNodeCustomGroup_register(Main *bmain,
|
||||
return nt->rna_ext.srna;
|
||||
}
|
||||
|
||||
void register_node_type_geo_custom_group(bNodeType *ntype);
|
||||
static StructRNA *rna_AttributeNodeCustomGroup_register(Main *bmain,
|
||||
ReportList *reports,
|
||||
void *data,
|
||||
const char *identifier,
|
||||
StructValidateFunc validate,
|
||||
StructCallbackFunc call,
|
||||
StructFreeFunc free)
|
||||
{
|
||||
bNodeType *nt = rna_Node_register_base(
|
||||
bmain, reports, &RNA_AttributeNodeCustomGroup, data, identifier, validate, call, free);
|
||||
|
||||
if (!nt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nt->group_update_func = node_group_update;
|
||||
nt->type = NODE_CUSTOM_GROUP;
|
||||
|
||||
register_node_type_attribute_custom_group(nt);
|
||||
|
||||
nodeRegisterType(nt);
|
||||
|
||||
WM_main_add_notifier(NC_NODE | NA_EDITED, NULL);
|
||||
|
||||
return nt->rna_ext.srna;
|
||||
}
|
||||
|
||||
static StructRNA *rna_ShaderNodeCustomGroup_register(Main *bmain,
|
||||
ReportList *reports,
|
||||
@@ -4497,6 +4572,29 @@ bool rna_NodeSocketMaterial_default_value_poll(PointerRNA *UNUSED(ptr), PointerR
|
||||
return ma->gp_style == NULL;
|
||||
}
|
||||
|
||||
static bool rna_GeometryNodeAttributeProcessor_node_tree_poll(PointerRNA *UNUSED(ptr),
|
||||
const PointerRNA value)
|
||||
{
|
||||
bNodeTree *ngroup = value.data;
|
||||
return STREQ(ngroup->idname, "AttributeNodeTree");
|
||||
}
|
||||
|
||||
static void rna_GeometryNodeAttributeProcessor_mode_update(Main *bmain,
|
||||
Scene *scene,
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
/* Unfortunately, `ptr->data` points to data within the node storage. We can't get the node
|
||||
* without iterating over all nodes. */
|
||||
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
if (node->type == GEO_NODE_ATTRIBUTE_PROCESSOR) {
|
||||
PointerRNA node_ptr;
|
||||
RNA_pointer_create(ptr->owner_id, &RNA_Node, node, &node_ptr);
|
||||
rna_Node_socket_update(bmain, scene, &node_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static const EnumPropertyItem prop_image_layer_items[] = {
|
||||
@@ -6219,7 +6317,7 @@ static void def_sh_script(StructRNA *srna)
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
|
||||
parm = RNA_def_string(func, "name", NULL, 0, "Name", "");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
parm = RNA_def_enum(func, "type", node_socket_type_items, SOCK_FLOAT, "Type", "");
|
||||
parm = RNA_def_enum(func, "type", rna_enum_node_socket_type_items, SOCK_FLOAT, "Type", "");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
/*parm =*/RNA_def_boolean(func, "is_output", false, "Output", "Whether the socket is an output");
|
||||
parm = RNA_def_pointer(func, "result", "NodeSocket", "", "");
|
||||
@@ -9986,6 +10084,142 @@ 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_processor_group_input(BlenderRNA *brna)
|
||||
{
|
||||
static EnumPropertyItem input_mode_items[] = {
|
||||
{GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_VALUE,
|
||||
"VALUE",
|
||||
0,
|
||||
"Value",
|
||||
"Pass a value into the group"},
|
||||
{GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_ATTRIBUTE,
|
||||
"ATTRIBUTE",
|
||||
0,
|
||||
"Attribute",
|
||||
"Pass a attribute name into the group"},
|
||||
{GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_DEFAULT,
|
||||
"DEFAULT",
|
||||
0,
|
||||
"Default",
|
||||
"Use the default attribute name or default value if the attribute does not exist"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "AttributeProcessorInputSettings", NULL);
|
||||
RNA_def_struct_ui_text(srna, "Attribute Processor Input", "");
|
||||
|
||||
prop = RNA_def_property(srna, "identifier", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "identifier", "Identifier of the matching socket in the group");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "input_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Input Mode", "How the group input is provided");
|
||||
RNA_def_property_enum_items(prop, input_mode_items);
|
||||
RNA_def_property_update(
|
||||
prop, NC_NODE | NA_EDITED, "rna_GeometryNodeAttributeProcessor_mode_update");
|
||||
}
|
||||
|
||||
static void def_geo_attribute_processor_group_output(BlenderRNA *brna)
|
||||
{
|
||||
static EnumPropertyItem input_mode_items[] = {
|
||||
{GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_ATTRIBUTE,
|
||||
"ATTRIBUTE",
|
||||
0,
|
||||
"Attribute",
|
||||
"Specify the attribute the result should be stored in"},
|
||||
{GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_DEFAULT,
|
||||
"DEFAULT",
|
||||
0,
|
||||
"Default",
|
||||
"Use the default attribute name for the output"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "AttributeProcessorOutputSettings", NULL);
|
||||
RNA_def_struct_ui_text(srna, "Attribute Processor Output", "");
|
||||
|
||||
prop = RNA_def_property(srna, "identifier", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "identifier", "Identifier of the matching socket in the group");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "output_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Output Mode", "Where the group output is stored");
|
||||
RNA_def_property_enum_items(prop, input_mode_items);
|
||||
RNA_def_property_update(
|
||||
prop, NC_NODE | NA_EDITED, "rna_GeometryNodeAttributeProcessor_mode_update");
|
||||
}
|
||||
|
||||
static void def_geo_attribute_processor(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "id");
|
||||
RNA_def_property_struct_type(prop, "NodeTree");
|
||||
RNA_def_property_pointer_funcs(
|
||||
prop, NULL, NULL, NULL, "rna_GeometryNodeAttributeProcessor_node_tree_poll");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_ui_text(prop, "Node Tree", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeGroup_update");
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeProcessor", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
|
||||
RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
|
||||
RNA_def_property_ui_text(prop, "Domain", "The geometry domain to process attributes in");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "inputs_settings", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "AttributeProcessorInputSettings");
|
||||
RNA_def_property_ui_text(prop, "Group Inputs", "");
|
||||
|
||||
prop = RNA_def_property(srna, "outputs_settings", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "AttributeProcessorOutputSettings");
|
||||
RNA_def_property_ui_text(prop, "Group Outputs", "");
|
||||
}
|
||||
|
||||
static void def_attr_set_attribute(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeAttributeSetAttribute", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "attribute_name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Attribute Name", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, node_socket_data_type_items);
|
||||
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_AttributeNodeSetAttribute_type_itemf");
|
||||
RNA_def_property_ui_text(prop, "Type", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_attr_attribute_input(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeAttributeAttributeInput", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "attribute_name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Attribute Name", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, node_socket_data_type_items);
|
||||
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_AttributeNodeSetAttribute_type_itemf");
|
||||
RNA_def_property_ui_text(prop, "Type", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void rna_def_shader_node(BlenderRNA *brna)
|
||||
@@ -10035,6 +10269,16 @@ static void rna_def_geometry_node(BlenderRNA *brna)
|
||||
RNA_def_struct_register_funcs(srna, "rna_GeometryNode_register", "rna_Node_unregister", NULL);
|
||||
}
|
||||
|
||||
static void rna_def_attribute_node(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
srna = RNA_def_struct(brna, "AttributeNode", "NodeInternal");
|
||||
RNA_def_struct_ui_text(srna, "Attribute Node", "");
|
||||
RNA_def_struct_sdna(srna, "bNode");
|
||||
RNA_def_struct_register_funcs(srna, "rna_AttributeNode_register", "rna_Node_unregister", NULL);
|
||||
}
|
||||
|
||||
static void rna_def_function_node(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
@@ -10146,7 +10390,7 @@ static void rna_def_node_socket(BlenderRNA *brna)
|
||||
*/
|
||||
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "type");
|
||||
RNA_def_property_enum_items(prop, node_socket_type_items);
|
||||
RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items);
|
||||
RNA_def_property_enum_default(prop, SOCK_FLOAT);
|
||||
RNA_def_property_enum_funcs(prop, NULL, "rna_NodeSocket_type_set", NULL);
|
||||
RNA_def_property_ui_text(prop, "Type", "Data type");
|
||||
@@ -10235,6 +10479,10 @@ static void rna_def_node_socket_interface(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Tooltip", "Socket tooltip");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
|
||||
|
||||
prop = RNA_def_property(srna, "default_attribute_name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Default Attribute Name", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
|
||||
|
||||
prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_funcs(prop, "rna_NodeSocket_is_output_get", NULL);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
@@ -10889,7 +11137,7 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
|
||||
/* for easier type comparison in python */
|
||||
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "typeinfo->type");
|
||||
RNA_def_property_enum_items(prop, node_socket_type_items);
|
||||
RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items);
|
||||
RNA_def_property_enum_default(prop, SOCK_FLOAT);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Type", "Data type");
|
||||
@@ -11013,7 +11261,7 @@ static void rna_def_internal_node(BlenderRNA *brna)
|
||||
|
||||
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_funcs(prop, "rna_NodeInternalSocketTemplate_type_get", NULL, NULL);
|
||||
RNA_def_property_enum_items(prop, node_socket_type_items);
|
||||
RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items);
|
||||
RNA_def_property_ui_text(prop, "Type", "Data type of the socket");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
@@ -11633,6 +11881,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
|
||||
{NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"},
|
||||
{NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"},
|
||||
{NTREE_GEOMETRY, "GEOMETRY", ICON_NODETREE, "Geometry", "Geometry nodes"},
|
||||
{NTREE_ATTRIBUTE, "ATTRIBUTE", ICON_NODETREE, "Attribute", "Attribute Nodes"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
@@ -11773,7 +12022,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
|
||||
func = RNA_def_function(srna, "valid_socket_type", NULL);
|
||||
RNA_def_function_ui_description(func, "Check if the socket type is valid for the node tree");
|
||||
RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL);
|
||||
parm = RNA_def_enum(func, "type", node_socket_type_items, 0, "", "");
|
||||
parm = RNA_def_enum(func, "type", rna_enum_node_socket_type_items, 0, "", "");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
|
||||
RNA_def_function_return(func, RNA_def_boolean(func, "valid", false, "", ""));
|
||||
}
|
||||
@@ -11881,6 +12130,18 @@ static void rna_def_geometry_nodetree(BlenderRNA *brna)
|
||||
RNA_def_struct_ui_icon(srna, ICON_NODETREE);
|
||||
}
|
||||
|
||||
static void rna_def_attribute_nodetree(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
srna = RNA_def_struct(brna, "AttributeNodeTree", "NodeTree");
|
||||
RNA_def_struct_ui_text(srna,
|
||||
"Attribute Node Tree",
|
||||
"Node tree consisting of linked nodes used for attribute processing");
|
||||
RNA_def_struct_sdna(srna, "bNodeTree");
|
||||
RNA_def_struct_ui_icon(srna, ICON_NODETREE);
|
||||
}
|
||||
|
||||
static StructRNA *define_specific_node(BlenderRNA *brna,
|
||||
const char *struct_name,
|
||||
const char *base_name,
|
||||
@@ -11968,6 +12229,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
|
||||
rna_def_compositor_node(brna);
|
||||
rna_def_texture_node(brna);
|
||||
rna_def_geometry_node(brna);
|
||||
rna_def_attribute_node(brna);
|
||||
rna_def_function_node(brna);
|
||||
|
||||
rna_def_nodetree(brna);
|
||||
@@ -11978,6 +12240,10 @@ void RNA_def_nodetree(BlenderRNA *brna)
|
||||
rna_def_shader_nodetree(brna);
|
||||
rna_def_texture_nodetree(brna);
|
||||
rna_def_geometry_nodetree(brna);
|
||||
rna_def_attribute_nodetree(brna);
|
||||
|
||||
def_geo_attribute_processor_group_input(brna);
|
||||
def_geo_attribute_processor_group_output(brna);
|
||||
|
||||
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
|
||||
{ \
|
||||
@@ -12001,6 +12267,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
|
||||
define_specific_node(brna, "CompositorNodeGroup", "CompositorNode", "Group", "", def_group);
|
||||
define_specific_node(brna, "TextureNodeGroup", "TextureNode", "Group", "", def_group);
|
||||
define_specific_node(brna, "GeometryNodeGroup", "GeometryNode", "Group", "", def_group);
|
||||
define_specific_node(brna, "AttributeNodeGroup", "AttributeNode", "Group", "", def_group);
|
||||
def_custom_group(brna,
|
||||
"ShaderNodeCustomGroup",
|
||||
"ShaderNode",
|
||||
@@ -12025,6 +12292,12 @@ void RNA_def_nodetree(BlenderRNA *brna)
|
||||
"Geometry Custom Group",
|
||||
"Custom Geometry Group Node for Python nodes",
|
||||
"rna_GeometryNodeCustomGroup_register");
|
||||
def_custom_group(brna,
|
||||
"AttributeNodeCustomGroup",
|
||||
"AttributeNode",
|
||||
"Attribute Custom Group",
|
||||
"Custom Attribute Group for Python nodes",
|
||||
"rna_AttributeNodeCustomGroup_register");
|
||||
|
||||
/* special socket types */
|
||||
rna_def_cmp_output_file_slot_file(brna);
|
||||
|
@@ -139,6 +139,12 @@ set(SRC
|
||||
function/nodes/node_fn_random_float.cc
|
||||
function/node_function_util.cc
|
||||
|
||||
geometry/nodes/node_attr_attribute_input.cc
|
||||
geometry/nodes/node_attr_group.cc
|
||||
geometry/nodes/node_attr_index.cc
|
||||
geometry/nodes/node_attr_position_input.cc
|
||||
geometry/nodes/node_attr_position_output.cc
|
||||
geometry/nodes/node_attr_set_attribute.cc
|
||||
geometry/nodes/node_geo_align_rotation_to_vector.cc
|
||||
geometry/nodes/node_geo_attribute_clamp.cc
|
||||
geometry/nodes/node_geo_attribute_color_ramp.cc
|
||||
@@ -150,6 +156,7 @@ set(SRC
|
||||
geometry/nodes/node_geo_attribute_map_range.cc
|
||||
geometry/nodes/node_geo_attribute_math.cc
|
||||
geometry/nodes/node_geo_attribute_mix.cc
|
||||
geometry/nodes/node_geo_attribute_processor.cc
|
||||
geometry/nodes/node_geo_attribute_proximity.cc
|
||||
geometry/nodes/node_geo_attribute_randomize.cc
|
||||
geometry/nodes/node_geo_attribute_remove.cc
|
||||
@@ -163,7 +170,7 @@ set(SRC
|
||||
geometry/nodes/node_geo_collection_info.cc
|
||||
geometry/nodes/node_geo_common.cc
|
||||
geometry/nodes/node_geo_convex_hull.cc
|
||||
geometry/nodes/node_geo_curve_length.cc
|
||||
geometry/nodes/node_geo_curve_length.cc
|
||||
geometry/nodes/node_geo_curve_to_mesh.cc
|
||||
geometry/nodes/node_geo_curve_to_points.cc
|
||||
geometry/nodes/node_geo_curve_resample.cc
|
||||
@@ -204,6 +211,7 @@ set(SRC
|
||||
geometry/nodes/node_geo_volume_to_mesh.cc
|
||||
geometry/node_geometry_exec.cc
|
||||
geometry/node_geometry_tree.cc
|
||||
geometry/node_attribute_tree.cc
|
||||
geometry/node_geometry_util.cc
|
||||
|
||||
shader/nodes/node_shader_add_shader.c
|
||||
@@ -275,10 +283,10 @@ set(SRC
|
||||
shader/nodes/node_shader_tex_image.c
|
||||
shader/nodes/node_shader_tex_magic.c
|
||||
shader/nodes/node_shader_tex_musgrave.c
|
||||
shader/nodes/node_shader_tex_noise.c
|
||||
shader/nodes/node_shader_tex_noise.cc
|
||||
shader/nodes/node_shader_tex_pointdensity.c
|
||||
shader/nodes/node_shader_tex_sky.c
|
||||
shader/nodes/node_shader_tex_voronoi.c
|
||||
shader/nodes/node_shader_tex_voronoi.cc
|
||||
shader/nodes/node_shader_tex_wave.c
|
||||
shader/nodes/node_shader_tex_white_noise.c
|
||||
shader/nodes/node_shader_uvAlongStroke.c
|
||||
|
@@ -178,6 +178,7 @@ class DerivedNodeTree {
|
||||
bool has_link_cycles() const;
|
||||
bool has_undefined_nodes_or_sockets() const;
|
||||
void foreach_node(FunctionRef<void(DNode)> callback) const;
|
||||
void foreach_node_with_type(StringRef idname, FunctionRef<void(DNode)> callback) const;
|
||||
|
||||
std::string to_dot() const;
|
||||
|
||||
@@ -373,7 +374,7 @@ inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocket
|
||||
|
||||
inline DInputSocket::DInputSocket(const DSocket &base_socket) : DSocket(base_socket)
|
||||
{
|
||||
BLI_assert(base_socket->is_input());
|
||||
BLI_assert(!base_socket || base_socket->is_input());
|
||||
}
|
||||
|
||||
inline const InputSocketRef *DInputSocket::socket_ref() const
|
||||
@@ -397,7 +398,7 @@ inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSoc
|
||||
|
||||
inline DOutputSocket::DOutputSocket(const DSocket &base_socket) : DSocket(base_socket)
|
||||
{
|
||||
BLI_assert(base_socket->is_output());
|
||||
BLI_assert(!base_socket || base_socket->is_output());
|
||||
}
|
||||
|
||||
inline const OutputSocketRef *DOutputSocket::socket_ref() const
|
||||
|
@@ -23,6 +23,7 @@ extern "C" {
|
||||
#include "BKE_node.h"
|
||||
|
||||
extern struct bNodeTreeType *ntreeType_Geometry;
|
||||
extern struct bNodeTreeType *ntreeType_Attribute;
|
||||
|
||||
void register_node_tree_type_geo(void);
|
||||
|
||||
@@ -40,6 +41,7 @@ void register_node_type_geo_attribute_fill(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);
|
||||
void register_node_type_geo_attribute_processor(void);
|
||||
void register_node_type_geo_attribute_proximity(void);
|
||||
void register_node_type_geo_attribute_randomize(void);
|
||||
void register_node_type_geo_attribute_separate_xyz(void);
|
||||
@@ -92,6 +94,17 @@ void register_node_type_geo_transform(void);
|
||||
void register_node_type_geo_triangulate(void);
|
||||
void register_node_type_geo_volume_to_mesh(void);
|
||||
|
||||
void register_node_tree_type_attr(void);
|
||||
|
||||
void register_node_type_attr_group(void);
|
||||
void register_node_type_attribute_custom_group(bNodeType *ntype);
|
||||
|
||||
void register_node_type_attr_attribute_input(void);
|
||||
void register_node_type_attr_index(void);
|
||||
void register_node_type_attr_set_attribute(void);
|
||||
void register_node_type_attr_position_input(void);
|
||||
void register_node_type_attr_position_output(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -175,6 +175,14 @@ class GeoNodeExecParams {
|
||||
return *(const T *)gvalue.get();
|
||||
}
|
||||
|
||||
GPointer get_input(StringRef identifier) const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
this->check_input_access(identifier);
|
||||
#endif
|
||||
return provider_->get_input(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the output value for the given socket identifier.
|
||||
*/
|
||||
|
@@ -49,6 +49,7 @@ class MFNetworkTreeMap {
|
||||
const DerivedNodeTree &tree_;
|
||||
fn::MFNetwork &network_;
|
||||
MultiValueMap<DSocket, fn::MFSocket *> sockets_by_dsocket_;
|
||||
Map<const fn::MFSocket *, DSocket> dsocket_by_sockets_;
|
||||
|
||||
public:
|
||||
MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network)
|
||||
@@ -76,11 +77,13 @@ class MFNetworkTreeMap {
|
||||
BLI_assert(dsocket->is_input() == socket.is_input());
|
||||
BLI_assert(dsocket->is_input() || sockets_by_dsocket_.lookup(dsocket).is_empty());
|
||||
sockets_by_dsocket_.add(dsocket, &socket);
|
||||
dsocket_by_sockets_.add_new(&socket, dsocket);
|
||||
}
|
||||
|
||||
void add(const DInputSocket &dsocket, fn::MFInputSocket &socket)
|
||||
{
|
||||
sockets_by_dsocket_.add(dsocket, &socket);
|
||||
dsocket_by_sockets_.add_new(&socket, dsocket);
|
||||
}
|
||||
|
||||
void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket)
|
||||
@@ -88,6 +91,7 @@ class MFNetworkTreeMap {
|
||||
/* There can be at most one matching output socket. */
|
||||
BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty());
|
||||
sockets_by_dsocket_.add(dsocket, &socket);
|
||||
dsocket_by_sockets_.add_new(&socket, dsocket);
|
||||
}
|
||||
|
||||
void add(const DTreeContext &context,
|
||||
@@ -180,6 +184,11 @@ class MFNetworkTreeMap {
|
||||
return socket;
|
||||
}
|
||||
|
||||
DOutputSocket try_lookup(const fn::MFOutputSocket &socket)
|
||||
{
|
||||
return DOutputSocket(dsocket_by_sockets_.lookup_default(&socket, {}));
|
||||
}
|
||||
|
||||
bool is_mapped(const DSocket &dsocket) const
|
||||
{
|
||||
return !sockets_by_dsocket_.lookup(dsocket).is_empty();
|
||||
|
@@ -278,6 +278,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUT
|
||||
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", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROCESSOR, def_geo_attribute_processor, "ATTRIBUTE_PROCESSOR", AttributeProcessor, "Attribute Processor", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
|
||||
@@ -330,6 +331,12 @@ DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform"
|
||||
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
|
||||
DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
|
||||
|
||||
DefNode(AttributeNode, ATTR_NODE_INDEX, 0, "INDEX", Index, "Index", "")
|
||||
DefNode(AttributeNode, ATTR_NODE_SET_ATTRIBUTE, def_attr_set_attribute, "SET_ATTRIBUTE", SetAttribute, "Set Attribute", "")
|
||||
DefNode(AttributeNode, ATTR_NODE_POSITION_INPUT, 0, "POSITION_INPUT", PositionInput, "Position Input", "")
|
||||
DefNode(AttributeNode, ATTR_NODE_POSITION_OUTPUT, 0, "POSITION_OUTPUT", PositionOutput, "Position Output", "")
|
||||
DefNode(AttributeNode, ATTR_NODE_ATTRIBUTE_INPUT, def_attr_attribute_input, "ATTRIBUTE_INPUT", AttributeInput, "Attribute Input", "")
|
||||
|
||||
/* undefine macros */
|
||||
#undef DefNode
|
||||
|
||||
|
@@ -22,8 +22,8 @@ static bool fn_node_poll_default(bNodeType *UNUSED(ntype),
|
||||
const char **r_disabled_hint)
|
||||
{
|
||||
/* Function nodes are only supported in simulation node trees so far. */
|
||||
if (!STREQ(ntree->idname, "GeometryNodeTree")) {
|
||||
*r_disabled_hint = "Not a geometry node tree";
|
||||
if (!STREQ(ntree->idname, "GeometryNodeTree") && !STREQ(ntree->idname, "AttributeNodeTree")) {
|
||||
*r_disabled_hint = "Not a geometry or attribute node tree";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
61
source/blender/nodes/geometry/node_attribute_tree.cc
Normal file
61
source/blender/nodes/geometry/node_attribute_tree.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 <cstring>
|
||||
|
||||
#include "BKE_node.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "NOD_geometry.h"
|
||||
|
||||
#include "node_common.h"
|
||||
|
||||
bNodeTreeType *ntreeType_Attribute;
|
||||
|
||||
static void attribute_node_tree_update(bNodeTree *ntree)
|
||||
{
|
||||
ntreeSetOutput(ntree);
|
||||
|
||||
/* Needed to give correct types to reroutes. */
|
||||
ntree_update_reroute_nodes(ntree);
|
||||
}
|
||||
|
||||
static bool attribute_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
|
||||
bNodeTreeType *UNUSED(ntreetype))
|
||||
{
|
||||
return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT);
|
||||
}
|
||||
|
||||
void register_node_tree_type_attr()
|
||||
{
|
||||
bNodeTreeType *tt = ntreeType_Attribute = static_cast<bNodeTreeType *>(
|
||||
MEM_callocN(sizeof(bNodeTreeType), "attribute node tree type"));
|
||||
tt->type = NTREE_ATTRIBUTE;
|
||||
strcpy(tt->idname, "AttributeNodeTree");
|
||||
strcpy(tt->ui_name, N_("Attribute Node Editor"));
|
||||
tt->ui_icon = 0; /* defined in drawnode.c */
|
||||
strcpy(tt->ui_description, N_("Attribute nodes"));
|
||||
tt->rna_ext.srna = &RNA_AttributeNodeTree;
|
||||
tt->update = attribute_node_tree_update;
|
||||
tt->valid_socket_type = attribute_node_tree_socket_type_valid;
|
||||
|
||||
ntreeTypeAdd(tt);
|
||||
}
|
@@ -68,6 +68,17 @@ bool geo_node_poll_default(bNodeType *UNUSED(ntype),
|
||||
return true;
|
||||
}
|
||||
|
||||
bool attr_node_poll_default(bNodeType *UNUSED(ntype),
|
||||
bNodeTree *ntree,
|
||||
const char **r_disabled_hint)
|
||||
{
|
||||
if (!STREQ(ntree->idname, "AttributeNodeTree")) {
|
||||
*r_disabled_hint = "Not a attribute node tree";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
|
||||
{
|
||||
node_type_base(ntype, type, name, nclass, flag);
|
||||
@@ -75,3 +86,12 @@ void geo_node_type_base(bNodeType *ntype, int type, const char *name, short ncla
|
||||
ntype->update_internal_links = node_update_internal_links_default;
|
||||
ntype->insert_link = node_insert_link_default;
|
||||
}
|
||||
|
||||
void attr_node_type_base(
|
||||
struct bNodeType *ntype, int type, const char *name, short nclass, short flag)
|
||||
{
|
||||
node_type_base(ntype, type, name, nclass, flag);
|
||||
ntype->poll = attr_node_poll_default;
|
||||
ntype->update_internal_links = node_update_internal_links_default;
|
||||
ntype->insert_link = node_insert_link_default;
|
||||
}
|
||||
|
@@ -40,6 +40,12 @@ bool geo_node_poll_default(struct bNodeType *ntype,
|
||||
struct bNodeTree *ntree,
|
||||
const char **r_disabled_hint);
|
||||
|
||||
void attr_node_type_base(
|
||||
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
|
||||
bool attr_node_poll_default(struct bNodeType *ntype,
|
||||
struct bNodeTree *ntree,
|
||||
const char **r_disabled_hint);
|
||||
|
||||
namespace blender::nodes {
|
||||
void update_attribute_input_socket_availabilities(bNode &node,
|
||||
const StringRef name,
|
||||
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate attr_node_attribute_input_out[] = {
|
||||
{SOCK_FLOAT, N_("Value")},
|
||||
{SOCK_INT, N_("Value")},
|
||||
{SOCK_BOOLEAN, N_("Value")},
|
||||
{SOCK_VECTOR, N_("Value")},
|
||||
{SOCK_RGBA, N_("Value")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static void attr_node_attribute_input_layout(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "type", 0, "Type", ICON_NONE);
|
||||
uiItemR(layout, ptr, "attribute_name", 0, "Name", ICON_NONE);
|
||||
}
|
||||
|
||||
static void attr_node_attribute_input_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||
{
|
||||
NodeAttributeSetAttribute *data = (NodeAttributeSetAttribute *)MEM_callocN(
|
||||
sizeof(NodeAttributeSetAttribute), __func__);
|
||||
data->type = SOCK_FLOAT;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static void attr_node_attribute_input_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeAttributeSetAttribute *node_storage = (NodeAttributeSetAttribute *)node->storage;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
|
||||
nodeSetSocketAvailability(socket, socket->type == (eNodeSocketDatatype)node_storage->type);
|
||||
}
|
||||
}
|
||||
|
||||
void register_node_type_attr_attribute_input()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
attr_node_type_base(&ntype, ATTR_NODE_ATTRIBUTE_INPUT, "Attribute Input", NODE_CLASS_INPUT, 0);
|
||||
node_type_socket_templates(&ntype, nullptr, attr_node_attribute_input_out);
|
||||
node_type_storage(&ntype,
|
||||
"NodeAttributeAttributeInput",
|
||||
node_free_standard_storage,
|
||||
node_copy_standard_storage);
|
||||
node_type_init(&ntype, attr_node_attribute_input_init);
|
||||
node_type_update(&ntype, attr_node_attribute_input_update);
|
||||
ntype.draw_buttons = attr_node_attribute_input_layout;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
59
source/blender/nodes/geometry/nodes/node_attr_group.cc
Normal file
59
source/blender/nodes/geometry/nodes/node_attr_group.cc
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 "BKE_node.h"
|
||||
|
||||
#include "NOD_geometry.h"
|
||||
|
||||
#include "NOD_common.h"
|
||||
#include "node_common.h"
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
void register_node_type_attr_group(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
node_type_base_custom(&ntype, "AttributeNodeGroup", "Group", NODE_CLASS_GROUP, 0);
|
||||
ntype.type = NODE_GROUP;
|
||||
ntype.poll = attr_node_poll_default;
|
||||
ntype.poll_instance = node_group_poll_instance;
|
||||
ntype.insert_link = node_insert_link_default;
|
||||
ntype.update_internal_links = node_update_internal_links_default;
|
||||
ntype.rna_ext.srna = RNA_struct_find("AttributeNodeGroup");
|
||||
BLI_assert(ntype.rna_ext.srna != nullptr);
|
||||
RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);
|
||||
|
||||
node_type_socket_templates(&ntype, nullptr, nullptr);
|
||||
node_type_size(&ntype, 140, 60, 400);
|
||||
node_type_label(&ntype, node_group_label);
|
||||
node_type_group_update(&ntype, node_group_update);
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
||||
void register_node_type_attribute_custom_group(bNodeType *ntype)
|
||||
{
|
||||
/* These methods can be overridden but need a default implementation otherwise. */
|
||||
if (ntype->poll == nullptr) {
|
||||
ntype->poll = attr_node_poll_default;
|
||||
}
|
||||
if (ntype->insert_link == nullptr) {
|
||||
ntype->insert_link = node_insert_link_default;
|
||||
}
|
||||
if (ntype->update_internal_links == nullptr) {
|
||||
ntype->update_internal_links = node_update_internal_links_default;
|
||||
}
|
||||
}
|
34
source/blender/nodes/geometry/nodes/node_attr_index.cc
Normal file
34
source/blender/nodes/geometry/nodes/node_attr_index.cc
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate attr_node_index_out[] = {
|
||||
{SOCK_INT, N_("Index")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
void register_node_type_attr_index()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
attr_node_type_base(&ntype, ATTR_NODE_INDEX, "Index", NODE_CLASS_INPUT, 0);
|
||||
node_type_socket_templates(&ntype, nullptr, attr_node_index_out);
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate attr_node_position_input_out[] = {
|
||||
{SOCK_VECTOR, N_("Position")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
void register_node_type_attr_position_input()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
attr_node_type_base(&ntype, ATTR_NODE_POSITION_INPUT, "Position Input", NODE_CLASS_INPUT, 0);
|
||||
node_type_socket_templates(&ntype, nullptr, attr_node_position_input_out);
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate attr_node_position_output_in[] = {
|
||||
{SOCK_VECTOR, N_("Position"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
void register_node_type_attr_position_output()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
attr_node_type_base(&ntype, ATTR_NODE_POSITION_OUTPUT, "Position Output", NODE_CLASS_OUTPUT, 0);
|
||||
node_type_socket_templates(&ntype, attr_node_position_output_in, nullptr);
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate attr_node_set_attribute_in[] = {
|
||||
{SOCK_FLOAT, N_("Value"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
|
||||
{SOCK_INT, N_("Value"), 0, 0, 0, 0, -100000, 100000},
|
||||
{SOCK_BOOLEAN, N_("Value")},
|
||||
{SOCK_VECTOR, N_("Value"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
|
||||
{SOCK_RGBA, N_("Value"), 0.8, 0.8, 0.8, 1.0},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static void attr_node_set_attribute_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "type", 0, "Type", ICON_NONE);
|
||||
uiItemR(layout, ptr, "attribute_name", 0, "Name", ICON_NONE);
|
||||
}
|
||||
|
||||
static void attr_node_set_attribute_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||
{
|
||||
NodeAttributeSetAttribute *data = (NodeAttributeSetAttribute *)MEM_callocN(
|
||||
sizeof(NodeAttributeSetAttribute), __func__);
|
||||
data->type = SOCK_FLOAT;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static void attr_node_set_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeAttributeSetAttribute *node_storage = (NodeAttributeSetAttribute *)node->storage;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
|
||||
nodeSetSocketAvailability(socket, socket->type == (eNodeSocketDatatype)node_storage->type);
|
||||
}
|
||||
}
|
||||
|
||||
void register_node_type_attr_set_attribute()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
attr_node_type_base(&ntype, ATTR_NODE_SET_ATTRIBUTE, "Set Attribute", NODE_CLASS_OUTPUT, 0);
|
||||
node_type_socket_templates(&ntype, attr_node_set_attribute_in, nullptr);
|
||||
node_type_storage(
|
||||
&ntype, "NodeAttributeSetAttribute", node_free_standard_storage, node_copy_standard_storage);
|
||||
node_type_init(&ntype, attr_node_set_attribute_init);
|
||||
node_type_update(&ntype, attr_node_set_attribute_update);
|
||||
ntype.draw_buttons = attr_node_set_attribute_layout;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -0,0 +1,739 @@
|
||||
/*
|
||||
* 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 "BLI_kdtree.h"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "BKE_bvhutils.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "NOD_node_tree_multi_function.hh"
|
||||
#include "NOD_type_callbacks.hh"
|
||||
|
||||
#include "FN_multi_function_network_evaluation.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static void geo_node_attribute_processor_layout(uiLayout *layout, bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
uiLayoutSetPropDecorate(layout, false);
|
||||
|
||||
bNode *node = (bNode *)ptr->data;
|
||||
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
|
||||
|
||||
uiLayout *row = uiLayoutRow(layout, true);
|
||||
uiTemplateIDBrowse(
|
||||
row, C, ptr, "node_tree", nullptr, nullptr, nullptr, UI_TEMPLATE_ID_FILTER_ALL, nullptr);
|
||||
uiItemStringO(row, "", ICON_PLUS, "node.new_attribute_processor_group", "node_name", node->name);
|
||||
|
||||
uiItemR(layout, ptr, "domain", 0, "Domain", ICON_NONE);
|
||||
|
||||
bNodeTree *group = (bNodeTree *)node->id;
|
||||
if (group == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
|
||||
bNodeSocket *interface_socket = (bNodeSocket *)group->inputs.first;
|
||||
AttributeProcessorInputSettings *input_settings = (AttributeProcessorInputSettings *)
|
||||
storage->inputs_settings.first;
|
||||
for (; interface_socket && input_settings;
|
||||
interface_socket = interface_socket->next, input_settings = input_settings->next) {
|
||||
PointerRNA input_ptr;
|
||||
RNA_pointer_create(
|
||||
ptr->owner_id, &RNA_AttributeProcessorInputSettings, input_settings, &input_ptr);
|
||||
uiItemR(col, &input_ptr, "input_mode", 0, interface_socket->name, ICON_NONE);
|
||||
}
|
||||
}
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
|
||||
bNodeSocket *interface_socket = (bNodeSocket *)group->outputs.first;
|
||||
AttributeProcessorOutputSettings *output_settings = (AttributeProcessorOutputSettings *)
|
||||
storage->outputs_settings.first;
|
||||
for (; interface_socket && output_settings;
|
||||
interface_socket = interface_socket->next, output_settings = output_settings->next) {
|
||||
PointerRNA input_ptr;
|
||||
RNA_pointer_create(
|
||||
ptr->owner_id, &RNA_AttributeProcessorOutputSettings, output_settings, &input_ptr);
|
||||
uiItemR(col, &input_ptr, "output_mode", 0, interface_socket->name, ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void geo_node_attribute_processor_init(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
NodeGeometryAttributeProcessor *node_storage = (NodeGeometryAttributeProcessor *)MEM_callocN(
|
||||
sizeof(NodeGeometryAttributeProcessor), __func__);
|
||||
|
||||
node->storage = node_storage;
|
||||
|
||||
nodeAddSocket(ntree, node, SOCK_IN, "NodeSocketGeometry", "Geometry", "Geometry");
|
||||
nodeAddSocket(ntree, node, SOCK_IN, "NodeSocketString", "Selection", "Selection");
|
||||
nodeAddSocket(ntree, node, SOCK_OUT, "NodeSocketGeometry", "Geometry", "Geometry");
|
||||
}
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void free_input_settings(AttributeProcessorInputSettings *input_settings)
|
||||
{
|
||||
MEM_freeN(input_settings->identifier);
|
||||
MEM_freeN(input_settings);
|
||||
}
|
||||
|
||||
static void free_output_settings(AttributeProcessorOutputSettings *output_settings)
|
||||
{
|
||||
MEM_freeN(output_settings->identifier);
|
||||
MEM_freeN(output_settings);
|
||||
}
|
||||
|
||||
static void free_inputs_settings(ListBase *inputs_settings)
|
||||
{
|
||||
LISTBASE_FOREACH_MUTABLE (AttributeProcessorInputSettings *, input_settings, inputs_settings) {
|
||||
free_input_settings(input_settings);
|
||||
}
|
||||
BLI_listbase_clear(inputs_settings);
|
||||
}
|
||||
|
||||
static void free_outputs_settings(ListBase *outputs_settings)
|
||||
{
|
||||
LISTBASE_FOREACH_MUTABLE (
|
||||
AttributeProcessorOutputSettings *, output_settings, outputs_settings) {
|
||||
free_output_settings(output_settings);
|
||||
}
|
||||
BLI_listbase_clear(outputs_settings);
|
||||
}
|
||||
|
||||
static void geo_node_attribute_processor_storage_free(bNode *node)
|
||||
{
|
||||
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
|
||||
free_inputs_settings(&storage->inputs_settings);
|
||||
free_outputs_settings(&storage->outputs_settings);
|
||||
MEM_freeN(storage);
|
||||
}
|
||||
|
||||
static void geo_node_attribute_processor_storage_copy(bNodeTree *UNUSED(dest_ntree),
|
||||
bNode *dst_node,
|
||||
const bNode *src_node)
|
||||
{
|
||||
const NodeGeometryAttributeProcessor *src_storage = (const NodeGeometryAttributeProcessor *)
|
||||
src_node->storage;
|
||||
NodeGeometryAttributeProcessor *dst_storage = (NodeGeometryAttributeProcessor *)MEM_callocN(
|
||||
sizeof(NodeGeometryAttributeProcessor), __func__);
|
||||
|
||||
*dst_storage = *src_storage;
|
||||
|
||||
BLI_listbase_clear(&dst_storage->inputs_settings);
|
||||
LISTBASE_FOREACH (
|
||||
const AttributeProcessorInputSettings *, src_input_settings, &src_storage->inputs_settings) {
|
||||
AttributeProcessorInputSettings *dst_input_settings = (AttributeProcessorInputSettings *)
|
||||
MEM_callocN(sizeof(AttributeProcessorInputSettings), __func__);
|
||||
*dst_input_settings = *src_input_settings;
|
||||
dst_input_settings->identifier = BLI_strdup(src_input_settings->identifier);
|
||||
BLI_addtail(&dst_storage->inputs_settings, dst_input_settings);
|
||||
}
|
||||
|
||||
BLI_listbase_clear(&dst_storage->outputs_settings);
|
||||
LISTBASE_FOREACH (const AttributeProcessorOutputSettings *,
|
||||
src_output_settings,
|
||||
&src_storage->outputs_settings) {
|
||||
AttributeProcessorOutputSettings *dst_output_settings = (AttributeProcessorOutputSettings *)
|
||||
MEM_callocN(sizeof(AttributeProcessorOutputSettings), __func__);
|
||||
*dst_output_settings = *src_output_settings;
|
||||
dst_output_settings->identifier = BLI_strdup(src_output_settings->identifier);
|
||||
BLI_addtail(&dst_storage->outputs_settings, dst_output_settings);
|
||||
}
|
||||
|
||||
dst_node->storage = dst_storage;
|
||||
}
|
||||
|
||||
static void geo_node_attribute_processor_group_update(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
|
||||
bNodeTree *ngroup = (bNodeTree *)node->id;
|
||||
|
||||
if (ngroup && (ID_IS_LINKED(ngroup) && (ngroup->id.tag & LIB_TAG_MISSING))) {
|
||||
/* Missing datablock, leave sockets unchanged so that when it comes back
|
||||
* the links remain valid. */
|
||||
return;
|
||||
}
|
||||
|
||||
ListBase ngroup_inputs;
|
||||
ListBase ngroup_outputs;
|
||||
if (ngroup != nullptr) {
|
||||
ngroup_inputs = ngroup->inputs;
|
||||
ngroup_outputs = ngroup->outputs;
|
||||
}
|
||||
else {
|
||||
BLI_listbase_clear(&ngroup_inputs);
|
||||
BLI_listbase_clear(&ngroup_outputs);
|
||||
}
|
||||
|
||||
Map<StringRefNull, bNodeSocket *> old_inputs_by_identifier;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
|
||||
old_inputs_by_identifier.add_new(socket->identifier, socket);
|
||||
}
|
||||
Map<StringRefNull, AttributeProcessorInputSettings *> old_inputs_settings_by_identifier;
|
||||
LISTBASE_FOREACH (AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
|
||||
old_inputs_settings_by_identifier.add_new(input_settings->identifier, input_settings);
|
||||
}
|
||||
Map<StringRefNull, AttributeProcessorOutputSettings *> old_outputs_settings_by_identifier;
|
||||
LISTBASE_FOREACH (
|
||||
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
|
||||
old_outputs_settings_by_identifier.add_new(output_settings->identifier, output_settings);
|
||||
}
|
||||
|
||||
VectorSet<bNodeSocket *> new_inputs;
|
||||
VectorSet<AttributeProcessorInputSettings *> new_inputs_settings;
|
||||
VectorSet<AttributeProcessorOutputSettings *> new_output_settings;
|
||||
|
||||
/* Keep geometry and selection socket. */
|
||||
new_inputs.add_new((bNodeSocket *)node->inputs.first);
|
||||
new_inputs.add_new(((bNodeSocket *)node->inputs.first)->next);
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, interface_sock, &ngroup_inputs) {
|
||||
AttributeProcessorInputSettings *input_settings =
|
||||
old_inputs_settings_by_identifier.lookup_default(interface_sock->identifier, nullptr);
|
||||
|
||||
char identifier1[MAX_NAME];
|
||||
char identifier2[MAX_NAME];
|
||||
BLI_snprintf(identifier1, sizeof(identifier1), "inA%s", interface_sock->identifier);
|
||||
BLI_snprintf(identifier2, sizeof(identifier2), "inB%s", interface_sock->identifier);
|
||||
|
||||
if (input_settings == nullptr) {
|
||||
input_settings = (AttributeProcessorInputSettings *)MEM_callocN(
|
||||
sizeof(AttributeProcessorInputSettings), __func__);
|
||||
input_settings->identifier = BLI_strdup(interface_sock->identifier);
|
||||
if (!StringRef(interface_sock->default_attribute_name).is_empty()) {
|
||||
input_settings->input_mode = GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_DEFAULT;
|
||||
}
|
||||
BLI_addtail(&storage->inputs_settings, input_settings);
|
||||
|
||||
new_inputs_settings.add_new(input_settings);
|
||||
bNodeSocket *sock_value = nodeAddSocket(
|
||||
ntree, node, SOCK_IN, interface_sock->idname, identifier1, interface_sock->name);
|
||||
bNodeSocket *sock_attribute = nodeAddSocket(
|
||||
ntree, node, SOCK_IN, "NodeSocketString", identifier2, interface_sock->name);
|
||||
node_socket_copy_default_value(sock_value, interface_sock);
|
||||
new_inputs.add_new(sock_value);
|
||||
new_inputs.add_new(sock_attribute);
|
||||
}
|
||||
else {
|
||||
new_inputs_settings.add_new(input_settings);
|
||||
bNodeSocket *sock_value = old_inputs_by_identifier.lookup(identifier1);
|
||||
bNodeSocket *sock_attribute = old_inputs_by_identifier.lookup(identifier2);
|
||||
STRNCPY(sock_value->name, interface_sock->name);
|
||||
STRNCPY(sock_attribute->name, interface_sock->name);
|
||||
new_inputs.add_new(sock_value);
|
||||
new_inputs.add_new(sock_attribute);
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, interface_sock, &ngroup_outputs) {
|
||||
AttributeProcessorOutputSettings *output_settings =
|
||||
old_outputs_settings_by_identifier.lookup_default(interface_sock->identifier, nullptr);
|
||||
|
||||
char identifier[MAX_NAME];
|
||||
BLI_snprintf(identifier, sizeof(identifier), "out%s", interface_sock->identifier);
|
||||
|
||||
if (output_settings == nullptr) {
|
||||
output_settings = (AttributeProcessorOutputSettings *)MEM_callocN(
|
||||
sizeof(AttributeProcessorOutputSettings), __func__);
|
||||
output_settings->identifier = BLI_strdup(interface_sock->identifier);
|
||||
if (!StringRef(interface_sock->default_attribute_name).is_empty()) {
|
||||
output_settings->output_mode = GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_DEFAULT;
|
||||
}
|
||||
BLI_addtail(&storage->outputs_settings, output_settings);
|
||||
|
||||
new_output_settings.add_new(output_settings);
|
||||
new_inputs.add_new(nodeAddSocket(
|
||||
ntree, node, SOCK_IN, "NodeSocketString", identifier, interface_sock->name));
|
||||
}
|
||||
else {
|
||||
new_output_settings.add_new(output_settings);
|
||||
bNodeSocket *socket = old_inputs_by_identifier.lookup(identifier);
|
||||
STRNCPY(socket->name, interface_sock->name);
|
||||
new_inputs.add_new(socket);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear the maps to avoid accidental access later on when they point to invalid memory. */
|
||||
old_inputs_by_identifier.clear();
|
||||
old_inputs_settings_by_identifier.clear();
|
||||
old_outputs_settings_by_identifier.clear();
|
||||
|
||||
/* Remove unused data. */
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &node->inputs) {
|
||||
if (!new_inputs.contains(socket)) {
|
||||
nodeRemoveSocket(ntree, node, socket);
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH_MUTABLE (
|
||||
AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
|
||||
if (!new_inputs_settings.contains(input_settings)) {
|
||||
BLI_remlink(&storage->inputs_settings, input_settings);
|
||||
free_input_settings(input_settings);
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH_MUTABLE (
|
||||
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
|
||||
if (!new_output_settings.contains(output_settings)) {
|
||||
BLI_remlink(&storage->outputs_settings, output_settings);
|
||||
free_output_settings(output_settings);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort remaining sockets and settings. */
|
||||
BLI_listbase_clear(&node->inputs);
|
||||
for (bNodeSocket *socket : new_inputs) {
|
||||
BLI_addtail(&node->inputs, socket);
|
||||
}
|
||||
BLI_listbase_clear(&storage->inputs_settings);
|
||||
for (AttributeProcessorInputSettings *input_settings : new_inputs_settings) {
|
||||
BLI_addtail(&storage->inputs_settings, input_settings);
|
||||
}
|
||||
BLI_listbase_clear(&storage->outputs_settings);
|
||||
for (AttributeProcessorOutputSettings *output_settings : new_output_settings) {
|
||||
BLI_addtail(&storage->outputs_settings, output_settings);
|
||||
}
|
||||
}
|
||||
|
||||
static void geo_node_attribute_processor_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeGeometryAttributeProcessor *storage = (NodeGeometryAttributeProcessor *)node->storage;
|
||||
bNodeTree *group = (bNodeTree *)node->id;
|
||||
if (group == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
bNodeSocket *next_socket = (bNodeSocket *)node->inputs.first;
|
||||
/* Skip geometry and selection socket. */
|
||||
next_socket = next_socket->next->next;
|
||||
LISTBASE_FOREACH (AttributeProcessorInputSettings *, input_settings, &storage->inputs_settings) {
|
||||
bNodeSocket *value_socket = next_socket;
|
||||
bNodeSocket *attribute_socket = value_socket->next;
|
||||
nodeSetSocketAvailability(
|
||||
value_socket, input_settings->input_mode == GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_VALUE);
|
||||
nodeSetSocketAvailability(attribute_socket,
|
||||
input_settings->input_mode ==
|
||||
GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_ATTRIBUTE);
|
||||
next_socket = attribute_socket->next;
|
||||
}
|
||||
LISTBASE_FOREACH (
|
||||
AttributeProcessorOutputSettings *, output_settings, &storage->outputs_settings) {
|
||||
nodeSetSocketAvailability(next_socket,
|
||||
output_settings->output_mode ==
|
||||
GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_ATTRIBUTE);
|
||||
next_socket = next_socket->next;
|
||||
}
|
||||
}
|
||||
|
||||
static CustomDataType get_custom_data_type(const eNodeSocketDatatype type)
|
||||
{
|
||||
switch (type) {
|
||||
case SOCK_FLOAT:
|
||||
return CD_PROP_FLOAT;
|
||||
case SOCK_VECTOR:
|
||||
return CD_PROP_FLOAT3;
|
||||
case SOCK_RGBA:
|
||||
return CD_PROP_COLOR;
|
||||
case SOCK_BOOLEAN:
|
||||
return CD_PROP_BOOL;
|
||||
case SOCK_INT:
|
||||
return CD_PROP_INT32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return CD_PROP_FLOAT;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct InputsCache {
|
||||
Map<int, GVArrayPtr> group_inputs;
|
||||
Map<std::pair<std::string, CustomDataType>, GVArrayPtr> attributes;
|
||||
GVArrayPtr index;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static IndexMask prepare_index_mask_from_selection(Vector<int64_t> &selection_indices,
|
||||
const AttributeDomain domain,
|
||||
const int domain_size,
|
||||
const GeometryComponent &component,
|
||||
const GeoNodeExecParams &geo_params)
|
||||
{
|
||||
IndexMask selection;
|
||||
const std::string selection_name = geo_params.get_input<std::string>("Selection");
|
||||
if (selection_name.empty()) {
|
||||
selection = IndexRange(domain_size);
|
||||
}
|
||||
else {
|
||||
GVArray_Typed<bool> selection_attribute = component.attribute_get_for_read<bool>(
|
||||
selection_name, domain, false);
|
||||
for (const int i : IndexRange(domain_size)) {
|
||||
if (selection_attribute[i]) {
|
||||
selection_indices.append(i);
|
||||
}
|
||||
}
|
||||
selection = selection_indices.as_span();
|
||||
}
|
||||
return selection;
|
||||
}
|
||||
|
||||
static bool load_input_varrays(InputsCache &inputs_cache,
|
||||
const Span<DOutputSocket> used_group_inputs,
|
||||
const NodeGeometryAttributeProcessor &storage,
|
||||
const bNodeTree &group,
|
||||
const GeoNodeExecParams &geo_params,
|
||||
const GeometryComponent &component,
|
||||
const AttributeDomain domain,
|
||||
const int domain_size,
|
||||
fn::MFParamsBuilder &fn_params)
|
||||
{
|
||||
for (const DOutputSocket &dsocket : used_group_inputs) {
|
||||
const DNode dnode = dsocket.node();
|
||||
const GVArray *input_varray = nullptr;
|
||||
const bNodeSocketType *socket_typeinfo = dsocket->typeinfo();
|
||||
const CustomDataType data_type = get_custom_data_type(
|
||||
(eNodeSocketDatatype)socket_typeinfo->type);
|
||||
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
|
||||
if (dnode->is_group_input_node()) {
|
||||
const int index = dsocket->index();
|
||||
input_varray = &*inputs_cache.group_inputs.lookup_or_add_cb(index, [&]() -> GVArrayPtr {
|
||||
const AttributeProcessorInputSettings *input_settings = (AttributeProcessorInputSettings *)
|
||||
BLI_findlink(&storage.inputs_settings, index);
|
||||
const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&group.inputs, index);
|
||||
if (cpp_type == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const StringRefNull identifier = interface_socket->identifier;
|
||||
GVArrayPtr input_varray;
|
||||
switch ((GeometryNodeAttributeProcessorInputMode)input_settings->input_mode) {
|
||||
case GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_ATTRIBUTE: {
|
||||
const std::string input_name = "inB" + identifier;
|
||||
const std::string attribute_name = geo_params.get_input<std::string>(input_name);
|
||||
return component.attribute_get_for_read(attribute_name, domain, data_type);
|
||||
}
|
||||
case GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_VALUE: {
|
||||
const std::string input_name = "inA" + identifier;
|
||||
GPointer value = geo_params.get_input(input_name);
|
||||
GVArrayPtr varray = std::make_unique<fn::GVArray_For_SingleValueRef>(
|
||||
*value.type(), domain_size, value.get());
|
||||
return varray;
|
||||
}
|
||||
case GEO_NODE_ATTRIBUTE_PROCESSOR_INPUT_MODE_DEFAULT: {
|
||||
const StringRef default_attribute_name = interface_socket->default_attribute_name;
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
|
||||
socket_cpp_value_get(*interface_socket, buffer);
|
||||
GVArrayPtr varray = component.attribute_get_for_read(
|
||||
default_attribute_name, domain, data_type, buffer);
|
||||
cpp_type->destruct(buffer);
|
||||
return varray;
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
});
|
||||
}
|
||||
else if (dnode->idname() == "AttributeNodeIndex") {
|
||||
if (!inputs_cache.index) {
|
||||
auto get_index_func = [](const int64_t index) { return (int)index; };
|
||||
inputs_cache.index = std::make_unique<
|
||||
fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(get_index_func)>>>(
|
||||
domain_size, domain_size, get_index_func);
|
||||
}
|
||||
input_varray = &*inputs_cache.index;
|
||||
}
|
||||
else if (dnode->idname() == "ShaderNodeAttribute") {
|
||||
NodeShaderAttribute *storage = dnode->storage<NodeShaderAttribute>();
|
||||
const StringRefNull attribute_name = storage->name;
|
||||
|
||||
input_varray = &*inputs_cache.attributes.lookup_or_add_cb(
|
||||
{attribute_name, data_type}, [&]() -> GVArrayPtr {
|
||||
return component.attribute_get_for_read(attribute_name, domain, data_type);
|
||||
});
|
||||
}
|
||||
else if (dnode->idname() == "AttributeNodeAttributeInput") {
|
||||
NodeAttributeAttributeInput *storage = dnode->storage<NodeAttributeAttributeInput>();
|
||||
const StringRefNull attribute_name = storage->attribute_name;
|
||||
input_varray = &*inputs_cache.attributes.lookup_or_add_cb(
|
||||
{attribute_name, data_type}, [&]() -> GVArrayPtr {
|
||||
return component.attribute_get_for_read(attribute_name, domain, data_type);
|
||||
});
|
||||
}
|
||||
else if (dnode->idname() == "AttributeNodePositionInput") {
|
||||
input_varray = &*inputs_cache.attributes.lookup_or_add_cb(
|
||||
{"position", CD_PROP_FLOAT3}, [&]() -> GVArrayPtr {
|
||||
return component.attribute_get_for_read("position", domain, CD_PROP_FLOAT3);
|
||||
});
|
||||
}
|
||||
|
||||
if (input_varray == nullptr) {
|
||||
return false;
|
||||
}
|
||||
fn_params.add_readonly_single_input(*input_varray);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prepare_group_outputs(const Span<DInputSocket> used_group_outputs,
|
||||
const NodeGeometryAttributeProcessor &storage,
|
||||
bNodeTree &group,
|
||||
GeoNodeExecParams &geo_params,
|
||||
GeometryComponent &component,
|
||||
const AttributeDomain domain,
|
||||
const int domain_size,
|
||||
fn::MFParamsBuilder &fn_params,
|
||||
Vector<std::unique_ptr<OutputAttribute>> &r_output_attributes)
|
||||
{
|
||||
for (const DInputSocket &socket : used_group_outputs) {
|
||||
const DNode node = socket.node();
|
||||
std::string attribute_name;
|
||||
CustomDataType attribute_type;
|
||||
if (node->is_group_output_node()) {
|
||||
const int index = socket->index();
|
||||
const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&group.outputs, index);
|
||||
const AttributeProcessorOutputSettings *output_settings =
|
||||
(AttributeProcessorOutputSettings *)BLI_findlink(&storage.outputs_settings, index);
|
||||
attribute_type = get_custom_data_type((eNodeSocketDatatype)interface_socket->typeinfo->type);
|
||||
switch ((GeometryNodeAttributeProcessorOutputMode)output_settings->output_mode) {
|
||||
case GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_ATTRIBUTE: {
|
||||
const StringRefNull identifier = interface_socket->identifier;
|
||||
const std::string socket_identifier = "out" + identifier;
|
||||
attribute_name = geo_params.extract_input<std::string>(socket_identifier);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_ATTRIBUTE_PROCESSOR_OUTPUT_MODE_DEFAULT: {
|
||||
attribute_name = StringRef(interface_socket->default_attribute_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node->idname() == "AttributeNodeSetAttribute") {
|
||||
const NodeAttributeSetAttribute *storage = node->storage<NodeAttributeSetAttribute>();
|
||||
attribute_name = storage->attribute_name;
|
||||
attribute_type = get_custom_data_type((eNodeSocketDatatype)storage->type);
|
||||
}
|
||||
else if (node->idname() == "AttributeNodePositionOutput") {
|
||||
attribute_name = "position";
|
||||
attribute_type = CD_PROP_FLOAT3;
|
||||
}
|
||||
|
||||
if (attribute_name.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto attribute = std::make_unique<OutputAttribute>(
|
||||
component.attribute_try_get_for_output(attribute_name, domain, attribute_type));
|
||||
if (!*attribute) {
|
||||
/* Cannot create the output attribute. */
|
||||
return false;
|
||||
}
|
||||
GMutableSpan attribute_span = attribute->as_span();
|
||||
/* Destruct because the function expects an uninitialized array. */
|
||||
attribute_span.type().destruct_n(attribute_span.data(), domain_size);
|
||||
fn_params.add_uninitialized_single_output(attribute_span);
|
||||
r_output_attributes.append(std::move(attribute));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void process_attributes_on_component(GeoNodeExecParams &geo_params,
|
||||
GeometryComponent &component,
|
||||
const fn::MultiFunction &network_fn,
|
||||
const Span<DOutputSocket> used_group_inputs,
|
||||
const Span<DInputSocket> used_group_outputs)
|
||||
{
|
||||
const bNode &node = geo_params.node();
|
||||
bNodeTree *group = (bNodeTree *)node.id;
|
||||
const NodeGeometryAttributeProcessor &storage = *(NodeGeometryAttributeProcessor *)node.storage;
|
||||
const AttributeDomain domain = (AttributeDomain)storage.domain;
|
||||
|
||||
const int domain_size = component.attribute_domain_size(domain);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<int64_t> selection_indices;
|
||||
const IndexMask selection = prepare_index_mask_from_selection(
|
||||
selection_indices, domain, domain_size, component, geo_params);
|
||||
|
||||
fn::MFParamsBuilder fn_params{network_fn, domain_size};
|
||||
fn::MFContextBuilder context;
|
||||
|
||||
InputsCache inputs_cache;
|
||||
if (!load_input_varrays(inputs_cache,
|
||||
used_group_inputs,
|
||||
storage,
|
||||
*group,
|
||||
geo_params,
|
||||
component,
|
||||
domain,
|
||||
domain_size,
|
||||
fn_params)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<std::unique_ptr<OutputAttribute>> output_attributes;
|
||||
if (!prepare_group_outputs(used_group_outputs,
|
||||
storage,
|
||||
*group,
|
||||
geo_params,
|
||||
component,
|
||||
domain,
|
||||
domain_size,
|
||||
fn_params,
|
||||
output_attributes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
network_fn.call(selection, fn_params, context);
|
||||
|
||||
for (std::unique_ptr<OutputAttribute> &output_attribute : output_attributes) {
|
||||
output_attribute->save();
|
||||
}
|
||||
}
|
||||
|
||||
static void process_attributes(GeoNodeExecParams &geo_params, GeometrySet &geometry_set)
|
||||
{
|
||||
const bNode &node = geo_params.node();
|
||||
bNodeTree *group = (bNodeTree *)node.id;
|
||||
|
||||
if (group == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
|
||||
NodeTreeRefMap tree_refs;
|
||||
DerivedNodeTree tree{*group, tree_refs};
|
||||
fn::MFNetwork network;
|
||||
ResourceScope scope;
|
||||
MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, scope);
|
||||
|
||||
Vector<const fn::MFOutputSocket *> fn_input_sockets;
|
||||
Vector<const fn::MFInputSocket *> fn_output_sockets;
|
||||
|
||||
const DTreeContext &root_context = tree.root_context();
|
||||
const NodeTreeRef &root_tree_ref = root_context.tree();
|
||||
|
||||
Span<const NodeRef *> output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput");
|
||||
|
||||
if (output_nodes.size() >= 2) {
|
||||
return;
|
||||
}
|
||||
Vector<DInputSocket> used_group_outputs;
|
||||
if (output_nodes.size() == 1) {
|
||||
const DNode output_node{&root_context, output_nodes[0]};
|
||||
for (const InputSocketRef *socket_ref : output_node->inputs().drop_back(1)) {
|
||||
used_group_outputs.append({&root_context, socket_ref});
|
||||
}
|
||||
}
|
||||
tree.foreach_node_with_type("AttributeNodeSetAttribute", [&](const DNode dnode) {
|
||||
NodeAttributeSetAttribute *storage = dnode->storage<NodeAttributeSetAttribute>();
|
||||
if (storage->attribute_name[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
for (const InputSocketRef *socket_ref : dnode->inputs()) {
|
||||
if (socket_ref->is_available()) {
|
||||
used_group_outputs.append({dnode.context(), socket_ref});
|
||||
}
|
||||
}
|
||||
});
|
||||
tree.foreach_node_with_type("AttributeNodePositionOutput", [&](const DNode dnode) {
|
||||
used_group_outputs.append(dnode.input(0));
|
||||
});
|
||||
if (used_group_outputs.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<fn::MFInputSocket *> network_outputs;
|
||||
for (const DInputSocket &socket : used_group_outputs) {
|
||||
network_outputs.append(network_map.lookup(socket).first());
|
||||
}
|
||||
|
||||
VectorSet<const fn::MFOutputSocket *> network_inputs;
|
||||
VectorSet<const fn::MFInputSocket *> unlinked_inputs;
|
||||
network.find_dependencies(network_outputs, network_inputs, unlinked_inputs);
|
||||
BLI_assert(unlinked_inputs.is_empty());
|
||||
|
||||
Vector<DOutputSocket> used_group_inputs;
|
||||
for (const fn::MFOutputSocket *dummy_socket : network_inputs) {
|
||||
const DOutputSocket dsocket = network_map.try_lookup(*dummy_socket);
|
||||
BLI_assert(dsocket);
|
||||
used_group_inputs.append(dsocket);
|
||||
}
|
||||
|
||||
fn::MFNetworkEvaluator network_fn{Vector<const fn::MFOutputSocket *>(network_inputs.as_span()),
|
||||
Vector<const fn::MFInputSocket *>(network_outputs.as_span())};
|
||||
|
||||
if (geometry_set.has_mesh()) {
|
||||
process_attributes_on_component(geo_params,
|
||||
geometry_set.get_component_for_write<MeshComponent>(),
|
||||
network_fn,
|
||||
used_group_inputs,
|
||||
used_group_outputs);
|
||||
}
|
||||
if (geometry_set.has_pointcloud()) {
|
||||
process_attributes_on_component(geo_params,
|
||||
geometry_set.get_component_for_write<PointCloudComponent>(),
|
||||
network_fn,
|
||||
used_group_inputs,
|
||||
used_group_outputs);
|
||||
}
|
||||
if (geometry_set.has_curve()) {
|
||||
process_attributes_on_component(geo_params,
|
||||
geometry_set.get_component_for_write<CurveComponent>(),
|
||||
network_fn,
|
||||
used_group_inputs,
|
||||
used_group_outputs);
|
||||
}
|
||||
}
|
||||
|
||||
static void geo_node_attribute_processor_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
process_attributes(params, geometry_set);
|
||||
params.set_output("Geometry", geometry_set);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
void register_node_type_geo_attribute_processor()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
geo_node_type_base(
|
||||
&ntype, GEO_NODE_ATTRIBUTE_PROCESSOR, "Attribute Processor", NODE_CLASS_GROUP, 0);
|
||||
node_type_init(&ntype, geo_node_attribute_processor_init);
|
||||
node_type_storage(&ntype,
|
||||
"NodeGeometryAttributeProcessor",
|
||||
blender::nodes::geo_node_attribute_processor_storage_free,
|
||||
blender::nodes::geo_node_attribute_processor_storage_copy);
|
||||
node_type_update(&ntype, blender::nodes::geo_node_attribute_processor_update);
|
||||
node_type_group_update(&ntype, blender::nodes::geo_node_attribute_processor_group_update);
|
||||
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_processor_exec;
|
||||
ntype.draw_buttons = geo_node_attribute_processor_layout;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -100,6 +100,17 @@ void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
|
||||
this->foreach_node_in_context_recursive(*root_context_, callback);
|
||||
}
|
||||
|
||||
void DerivedNodeTree::foreach_node_with_type(StringRef idname,
|
||||
FunctionRef<void(DNode)> callback) const
|
||||
{
|
||||
/* TODO: Use more efficient approach that does not require iterating over all nodes. */
|
||||
this->foreach_node([&](const DNode node) {
|
||||
if (node->idname() == idname) {
|
||||
callback(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context,
|
||||
FunctionRef<void(DNode)> callback) const
|
||||
{
|
||||
|
@@ -160,8 +160,29 @@ static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderD
|
||||
return node.output(0);
|
||||
}
|
||||
|
||||
static fn::MFOutputSocket &insert_optional_conversion(CommonMFNetworkBuilderData &common,
|
||||
fn::MFOutputSocket &socket,
|
||||
const fn::MFDataType &to_type)
|
||||
{
|
||||
const fn::MFDataType from_type = socket.data_type();
|
||||
if (from_type == to_type) {
|
||||
return socket;
|
||||
}
|
||||
const fn::MultiFunction *conversion_fn =
|
||||
get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
|
||||
if (conversion_fn != nullptr) {
|
||||
fn::MFNode &node = common.network.add_function(*conversion_fn);
|
||||
common.network.add_link(socket, node.input(0));
|
||||
return node.output(0);
|
||||
}
|
||||
else {
|
||||
return insert_default_value_for_type(common, to_type);
|
||||
}
|
||||
}
|
||||
|
||||
static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &common,
|
||||
const DInputSocket &dsocket)
|
||||
const DInputSocket &dsocket,
|
||||
const fn::MFDataType &to_type)
|
||||
{
|
||||
BLI_assert(socket_is_mf_data_socket(*dsocket->typeinfo()));
|
||||
|
||||
@@ -170,7 +191,7 @@ static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &com
|
||||
|
||||
fn::MFOutputSocket *built_socket = builder.built_socket();
|
||||
BLI_assert(built_socket != nullptr);
|
||||
return built_socket;
|
||||
return &insert_optional_conversion(common, *built_socket, to_type);
|
||||
}
|
||||
|
||||
static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
|
||||
@@ -200,7 +221,7 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
|
||||
}
|
||||
if (from_dsockets.is_empty()) {
|
||||
/* The socket is not linked. Need to use the value of the socket itself. */
|
||||
fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket);
|
||||
fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket, to_type);
|
||||
for (fn::MFInputSocket *to_socket : to_sockets) {
|
||||
common.network.add_link(*built_socket, *to_socket);
|
||||
}
|
||||
@@ -208,7 +229,7 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
|
||||
}
|
||||
if (from_dsockets[0]->is_input()) {
|
||||
DInputSocket from_dsocket{from_dsockets[0]};
|
||||
fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket);
|
||||
fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket, to_type);
|
||||
for (fn::MFInputSocket *to_socket : to_sockets) {
|
||||
common.network.add_link(*built_socket, *to_socket);
|
||||
}
|
||||
@@ -216,20 +237,7 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
|
||||
}
|
||||
DOutputSocket from_dsocket{from_dsockets[0]};
|
||||
fn::MFOutputSocket *from_socket = &common.network_map.lookup(from_dsocket);
|
||||
const fn::MFDataType from_type = from_socket->data_type();
|
||||
|
||||
if (from_type != to_type) {
|
||||
const fn::MultiFunction *conversion_fn =
|
||||
get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
|
||||
if (conversion_fn != nullptr) {
|
||||
fn::MFNode &node = common.network.add_function(*conversion_fn);
|
||||
common.network.add_link(*from_socket, node.input(0));
|
||||
from_socket = &node.output(0);
|
||||
}
|
||||
else {
|
||||
from_socket = &insert_default_value_for_type(common, to_type);
|
||||
}
|
||||
}
|
||||
from_socket = &insert_optional_conversion(common, *from_socket, to_type);
|
||||
|
||||
for (fn::MFInputSocket *to_socket : to_sockets) {
|
||||
common.network.add_link(*from_socket, *to_socket);
|
||||
|
@@ -40,7 +40,8 @@ static bool sh_fn_poll_default(bNodeType *UNUSED(ntype),
|
||||
bNodeTree *ntree,
|
||||
const char **r_disabled_hint)
|
||||
{
|
||||
if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree")) {
|
||||
if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree") &&
|
||||
!STREQ(ntree->idname, "AttributeNodeTree")) {
|
||||
*r_disabled_hint = "Not a shader or geometry node tree";
|
||||
return false;
|
||||
}
|
||||
|
@@ -86,7 +86,7 @@ void register_node_type_sh_attribute(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_ATTRIBUTE, "Attribute", NODE_CLASS_INPUT, 0);
|
||||
sh_fn_node_type_base(&ntype, SH_NODE_ATTRIBUTE, "Attribute", NODE_CLASS_INPUT, 0);
|
||||
node_type_socket_templates(&ntype, NULL, sh_node_attribute_out);
|
||||
node_type_init(&ntype, node_shader_init_attribute);
|
||||
node_type_storage(
|
||||
|
@@ -17,6 +17,8 @@
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#include "BLI_noise.h"
|
||||
|
||||
#include "../node_shader_util.h"
|
||||
|
||||
/* **************** NOISE ******************** */
|
||||
@@ -48,7 +50,7 @@ static bNodeSocketTemplate sh_node_tex_noise_out[] = {
|
||||
|
||||
static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeTexNoise *tex = MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
|
||||
NodeTexNoise *tex = (NodeTexNoise *)MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
tex->dimensions = 3;
|
||||
@@ -86,18 +88,80 @@ static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4);
|
||||
}
|
||||
|
||||
class NoiseTextureFunction : public blender::fn::MultiFunction {
|
||||
public:
|
||||
NoiseTextureFunction()
|
||||
{
|
||||
static blender::fn::MFSignature signature = create_signature();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
static blender::fn::MFSignature create_signature()
|
||||
{
|
||||
blender::fn::MFSignatureBuilder signature{"Noise Texture"};
|
||||
signature.single_input<blender::float3>("Vector");
|
||||
signature.single_input<float>("Scale");
|
||||
signature.single_input<float>("Detail");
|
||||
signature.single_input<float>("Roughness");
|
||||
signature.single_input<float>("Distortion");
|
||||
signature.single_output<float>("Fac");
|
||||
signature.single_output<blender::ColorGeometry4f>("Color");
|
||||
return signature.build();
|
||||
}
|
||||
|
||||
void call(blender::IndexMask mask,
|
||||
blender::fn::MFParams params,
|
||||
blender::fn::MFContext UNUSED(context)) const override
|
||||
{
|
||||
const blender::VArray<blender::float3> &vectors =
|
||||
params.readonly_single_input<blender::float3>(0, "Vector");
|
||||
const blender::VArray<float> &scales = params.readonly_single_input<float>(1, "Scale");
|
||||
const blender::VArray<float> &details = params.readonly_single_input<float>(2, "Detail");
|
||||
|
||||
blender::MutableSpan<float> r_values = params.uninitialized_single_output<float>(5, "Fac");
|
||||
blender::MutableSpan<blender::ColorGeometry4f> r_colors =
|
||||
params.uninitialized_single_output<blender::ColorGeometry4f>(6, "Color");
|
||||
|
||||
for (int i : mask) {
|
||||
const blender::float3 vector = vectors[i];
|
||||
const float scale = scales[i];
|
||||
const float detail = details[i];
|
||||
const float noise1 = BLI_noise_generic_turbulence(
|
||||
scale, vector.x, vector.y, vector.z, detail, false, 1);
|
||||
const float noise2 = BLI_noise_generic_turbulence(
|
||||
scale, vector.y, vector.x + 100.0f, vector.z, detail, false, 1);
|
||||
const float noise3 = BLI_noise_generic_turbulence(
|
||||
scale, vector.z + 100.0f, vector.y, vector.x, detail, false, 1);
|
||||
r_values[i] = noise1;
|
||||
r_colors[i] = {noise1, noise2, noise3, 1.0f};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_tex_noise_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
|
||||
{
|
||||
/* TODO: Not only support 3D. */
|
||||
NodeTexNoise *tex = builder.dnode()->storage<NodeTexNoise>();
|
||||
if (tex->dimensions != 3) {
|
||||
builder.set_not_implemented();
|
||||
return;
|
||||
}
|
||||
static NoiseTextureFunction fn;
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_tex_noise(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
|
||||
sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
|
||||
node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out);
|
||||
node_type_init(&ntype, node_shader_init_tex_noise);
|
||||
node_type_storage(
|
||||
&ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage);
|
||||
node_type_gpu(&ntype, node_shader_gpu_tex_noise);
|
||||
node_type_update(&ntype, node_shader_update_tex_noise);
|
||||
|
||||
ntype.expand_in_mf_network = sh_node_tex_noise_expand_in_mf_network;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -19,6 +19,9 @@
|
||||
|
||||
#include "../node_shader_util.h"
|
||||
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_noise.h"
|
||||
|
||||
/* **************** VORONOI ******************** */
|
||||
|
||||
static bNodeSocketTemplate sh_node_tex_voronoi_in[] = {
|
||||
@@ -69,7 +72,7 @@ static bNodeSocketTemplate sh_node_tex_voronoi_out[] = {
|
||||
|
||||
static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeTexVoronoi *tex = MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi");
|
||||
NodeTexVoronoi *tex = (NodeTexVoronoi *)MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi");
|
||||
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
|
||||
BKE_texture_colormapping_default(&tex->base.color_mapping);
|
||||
tex->dimensions = 3;
|
||||
@@ -79,6 +82,49 @@ static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
node->storage = tex;
|
||||
}
|
||||
|
||||
static const char *node_shader_gpu_name_tex_voronoi(NodeTexVoronoi *tex)
|
||||
{
|
||||
|
||||
static const char *names[][5] = {
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_f1_1d",
|
||||
"node_tex_voronoi_f1_2d",
|
||||
"node_tex_voronoi_f1_3d",
|
||||
"node_tex_voronoi_f1_4d",
|
||||
},
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_f2_1d",
|
||||
"node_tex_voronoi_f2_2d",
|
||||
"node_tex_voronoi_f2_3d",
|
||||
"node_tex_voronoi_f2_4d",
|
||||
},
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_smooth_f1_1d",
|
||||
"node_tex_voronoi_smooth_f1_2d",
|
||||
"node_tex_voronoi_smooth_f1_3d",
|
||||
"node_tex_voronoi_smooth_f1_4d",
|
||||
},
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_distance_to_edge_1d",
|
||||
"node_tex_voronoi_distance_to_edge_2d",
|
||||
"node_tex_voronoi_distance_to_edge_3d",
|
||||
"node_tex_voronoi_distance_to_edge_4d",
|
||||
},
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_n_sphere_radius_1d",
|
||||
"node_tex_voronoi_n_sphere_radius_2d",
|
||||
"node_tex_voronoi_n_sphere_radius_3d",
|
||||
"node_tex_voronoi_n_sphere_radius_4d",
|
||||
},
|
||||
};
|
||||
return names[tex->feature][tex->dimensions];
|
||||
}
|
||||
|
||||
static int node_shader_gpu_tex_voronoi(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData *UNUSED(execdata),
|
||||
@@ -88,49 +134,6 @@ static int node_shader_gpu_tex_voronoi(GPUMaterial *mat,
|
||||
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
|
||||
node_shader_gpu_tex_mapping(mat, node, in, out);
|
||||
|
||||
static const char *names[][5] = {
|
||||
[SHD_VORONOI_F1] =
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_f1_1d",
|
||||
"node_tex_voronoi_f1_2d",
|
||||
"node_tex_voronoi_f1_3d",
|
||||
"node_tex_voronoi_f1_4d",
|
||||
},
|
||||
[SHD_VORONOI_F2] =
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_f2_1d",
|
||||
"node_tex_voronoi_f2_2d",
|
||||
"node_tex_voronoi_f2_3d",
|
||||
"node_tex_voronoi_f2_4d",
|
||||
},
|
||||
[SHD_VORONOI_SMOOTH_F1] =
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_smooth_f1_1d",
|
||||
"node_tex_voronoi_smooth_f1_2d",
|
||||
"node_tex_voronoi_smooth_f1_3d",
|
||||
"node_tex_voronoi_smooth_f1_4d",
|
||||
},
|
||||
[SHD_VORONOI_DISTANCE_TO_EDGE] =
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_distance_to_edge_1d",
|
||||
"node_tex_voronoi_distance_to_edge_2d",
|
||||
"node_tex_voronoi_distance_to_edge_3d",
|
||||
"node_tex_voronoi_distance_to_edge_4d",
|
||||
},
|
||||
[SHD_VORONOI_N_SPHERE_RADIUS] =
|
||||
{
|
||||
"",
|
||||
"node_tex_voronoi_n_sphere_radius_1d",
|
||||
"node_tex_voronoi_n_sphere_radius_2d",
|
||||
"node_tex_voronoi_n_sphere_radius_3d",
|
||||
"node_tex_voronoi_n_sphere_radius_4d",
|
||||
},
|
||||
};
|
||||
|
||||
NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;
|
||||
float metric = tex->distance;
|
||||
|
||||
@@ -138,7 +141,7 @@ static int node_shader_gpu_tex_voronoi(GPUMaterial *mat,
|
||||
BLI_assert(tex->dimensions > 0 && tex->dimensions < 5);
|
||||
|
||||
return GPU_stack_link(
|
||||
mat, node, names[tex->feature][tex->dimensions], in, out, GPU_constant(&metric));
|
||||
mat, node, node_shader_gpu_name_tex_voronoi(tex), in, out, GPU_constant(&metric));
|
||||
}
|
||||
|
||||
static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
@@ -178,17 +181,81 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node
|
||||
nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS);
|
||||
}
|
||||
|
||||
class VoronoiTextureFunction : public blender::fn::MultiFunction {
|
||||
public:
|
||||
VoronoiTextureFunction()
|
||||
{
|
||||
static blender::fn::MFSignature signature = create_signature();
|
||||
this->set_signature(&signature);
|
||||
}
|
||||
|
||||
static blender::fn::MFSignature create_signature()
|
||||
{
|
||||
blender::fn::MFSignatureBuilder signature{"Noise Texture"};
|
||||
signature.single_input<blender::float3>("Vector");
|
||||
signature.single_input<float>("Scale");
|
||||
signature.single_input<float>("Randomness");
|
||||
signature.single_output<float>("Distance");
|
||||
signature.single_output<blender::ColorGeometry4f>("Color");
|
||||
signature.single_output<blender::float3>("Position");
|
||||
return signature.build();
|
||||
}
|
||||
|
||||
void call(blender::IndexMask mask,
|
||||
blender::fn::MFParams params,
|
||||
blender::fn::MFContext UNUSED(context)) const override
|
||||
{
|
||||
const blender::VArray<blender::float3> &vectors =
|
||||
params.readonly_single_input<blender::float3>(0, "Vector");
|
||||
const blender::VArray<float> &scales = params.readonly_single_input<float>(1, "Scale");
|
||||
|
||||
blender::MutableSpan<float> r_distances = params.uninitialized_single_output<float>(
|
||||
3, "Distance");
|
||||
blender::MutableSpan<blender::ColorGeometry4f> r_colors =
|
||||
params.uninitialized_single_output<blender::ColorGeometry4f>(4, "Color");
|
||||
blender::MutableSpan<blender::float3> r_positions =
|
||||
params.uninitialized_single_output<blender::float3>(5, "Position");
|
||||
|
||||
for (int i : mask) {
|
||||
const float scale = scales[i];
|
||||
const blender::float3 vector = vectors[i] * scale;
|
||||
float da[4];
|
||||
float pa[12];
|
||||
BLI_noise_voronoi(vector.x, vector.y, vector.z, da, pa, 1, 0);
|
||||
blender::ColorGeometry4f color;
|
||||
BLI_noise_cell_v3(pa[0], pa[1], pa[2], color);
|
||||
color.a = 1.0f;
|
||||
r_distances[i] = da[0];
|
||||
r_colors[i] = color;
|
||||
r_positions[i] = {pa[0], pa[1], pa[2]};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_tex_voronoi_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
|
||||
{
|
||||
NodeTexVoronoi *tex = builder.dnode()->storage<NodeTexVoronoi>();
|
||||
if (tex->dimensions != 3 || tex->feature != SHD_VORONOI_F1 ||
|
||||
tex->distance != SHD_VORONOI_EUCLIDEAN) {
|
||||
builder.set_not_implemented();
|
||||
return;
|
||||
}
|
||||
static VoronoiTextureFunction fn;
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
|
||||
void register_node_type_sh_tex_voronoi(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0);
|
||||
sh_fn_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0);
|
||||
node_type_socket_templates(&ntype, sh_node_tex_voronoi_in, sh_node_tex_voronoi_out);
|
||||
node_type_init(&ntype, node_shader_init_tex_voronoi);
|
||||
node_type_storage(
|
||||
&ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage);
|
||||
node_type_gpu(&ntype, node_shader_gpu_tex_voronoi);
|
||||
node_type_update(&ntype, node_shader_update_tex_voronoi);
|
||||
ntype.expand_in_mf_network = sh_node_tex_voronoi_expand_in_mf_network;
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
Reference in New Issue
Block a user