WIP: Geometry Nodes: new For-Each zone #112446
|
@ -880,6 +880,7 @@ const bTheme U_theme_default = {
|
|||
.nodeclass_attribute = RGBA(0x001566ff),
|
||||
.node_zone_simulation = RGBA(0x66416233),
|
||||
.node_zone_repeat = RGBA(0x76512f33),
|
||||
.node_zone_foreach = RGBA(0x2f517633),
|
||||
.movie = RGBA(0x0f0f0fcc),
|
||||
.gp_vertex_size = 3,
|
||||
.gp_vertex = RGBA(0x97979700),
|
||||
|
|
|
@ -458,6 +458,63 @@ class RepeatZoneItemMoveOperator(RepeatZoneOperator, ZoneMoveItemOperator, Opera
|
|||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
|
||||
class ForEachZoneOperator(ZoneOperator):
|
||||
input_node_type = 'GeometryNodeForEachInput'
|
||||
output_node_type = 'GeometryNodeForEachOutput'
|
||||
|
||||
|
||||
class ForEachInputOperator(ForEachZoneOperator):
|
||||
items_name = "input_items"
|
||||
active_index_name = "input_active_index"
|
||||
|
||||
|
||||
class ForEachInputItemAddOperator(ForEachInputOperator, ZoneItemAddOperator, Operator):
|
||||
"""Add an item to the for-each input node"""
|
||||
bl_idname = "node.foreach_zone_input_item_add"
|
||||
bl_label = "Add For-Each Input"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
default_socket_type = 'FLOAT'
|
||||
|
||||
class ForEachInputItemRemoveOperator(ForEachInputOperator, ZoneItemRemoveOperator, Operator):
|
||||
"""Remove an item from the for-each input node"""
|
||||
bl_idname = "node.foreach_zone_input_item_remove"
|
||||
bl_label = "Remove For-Each Input"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
|
||||
class ForEachInputItemMoveOperator(ForEachInputOperator, ZoneMoveItemOperator, Operator):
|
||||
"""Move an item up or down in the list"""
|
||||
bl_idname = "node.foreach_zone_input_item_move"
|
||||
bl_label = "Move For-Each Input"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
|
||||
class ForEachOutputOperator(ForEachZoneOperator):
|
||||
items_name = "output_items"
|
||||
active_index_name = "output_active_index"
|
||||
|
||||
|
||||
class ForEachOutputItemAddOperator(ForEachOutputOperator, ZoneItemAddOperator, Operator):
|
||||
"""Add an item to the for-each output node"""
|
||||
bl_idname = "node.foreach_zone_output_item_add"
|
||||
bl_label = "Add For-Each Output"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
class ForEachOutputItemRemoveOperator(ForEachOutputOperator, ZoneItemRemoveOperator, Operator):
|
||||
"""Remove an item from the for-each output node"""
|
||||
bl_idname = "node.foreach_zone_output_item_remove"
|
||||
bl_label = "Remove For-Each Output"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
|
||||
class ForEachOutputItemMoveOperator(ForEachOutputOperator, ZoneMoveItemOperator, Operator):
|
||||
"""Move an item up or down in the list"""
|
||||
bl_idname = "node.foreach_zone_output_item_move"
|
||||
bl_label = "Move For-Each Output"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
|
||||
classes = (
|
||||
NewGeometryNodesModifier,
|
||||
NewGeometryNodeTreeAssign,
|
||||
|
@ -469,4 +526,10 @@ classes = (
|
|||
RepeatZoneItemAddOperator,
|
||||
RepeatZoneItemRemoveOperator,
|
||||
RepeatZoneItemMoveOperator,
|
||||
ForEachInputItemAddOperator,
|
||||
ForEachInputItemRemoveOperator,
|
||||
ForEachInputItemMoveOperator,
|
||||
ForEachOutputItemAddOperator,
|
||||
ForEachOutputItemRemoveOperator,
|
||||
ForEachOutputItemMoveOperator,
|
||||
)
|
||||
|
|
|
@ -182,9 +182,12 @@ class NodeAddZoneOperator(NodeAddOperator):
|
|||
|
||||
# Connect geometry sockets by default.
|
||||
# Get the sockets by their types, because the name is not guaranteed due to i18n.
|
||||
from_socket = next(s for s in input_node.outputs if s.type == 'GEOMETRY')
|
||||
to_socket = next(s for s in output_node.inputs if s.type == 'GEOMETRY')
|
||||
tree.links.new(to_socket, from_socket)
|
||||
try:
|
||||
from_socket = next(s for s in input_node.outputs if s.type == 'GEOMETRY')
|
||||
to_socket = next(s for s in output_node.inputs if s.type == 'GEOMETRY')
|
||||
tree.links.new(to_socket, from_socket)
|
||||
except:
|
||||
pass
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
@ -209,6 +212,15 @@ class NODE_OT_add_repeat_zone(NodeAddZoneOperator, Operator):
|
|||
output_node_type = "GeometryNodeRepeatOutput"
|
||||
|
||||
|
||||
class NODE_OT_add_foreach_zone(NodeAddZoneOperator, Operator):
|
||||
"""Add a for-each zone that allows executing nodes in parallel a dynamic number of times"""
|
||||
bl_idname = "node.add_foreach_zone"
|
||||
bl_label = "Add For-Each Zone"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
input_node_type = "GeometryNodeForEachInput"
|
||||
output_node_type = "GeometryNodeForEachOutput"
|
||||
|
||||
class NODE_OT_collapse_hide_unused_toggle(Operator):
|
||||
"""Toggle collapsed nodes and hide unused sockets"""
|
||||
bl_idname = "node.collapse_hide_unused_toggle"
|
||||
|
@ -389,6 +401,7 @@ classes = (
|
|||
NODE_OT_add_node,
|
||||
NODE_OT_add_simulation_zone,
|
||||
NODE_OT_add_repeat_zone,
|
||||
NODE_OT_add_foreach_zone,
|
||||
NODE_OT_collapse_hide_unused_toggle,
|
||||
NODE_OT_interface_item_new,
|
||||
NODE_OT_interface_item_duplicate,
|
||||
|
|
|
@ -74,6 +74,11 @@ def add_repeat_zone(layout, label):
|
|||
props.use_transform = True
|
||||
return props
|
||||
|
||||
def add_foreach_zone(layout, label):
|
||||
props = layout.operator("node.add_foreach_zone", text=label, text_ctxt=i18n_contexts.default)
|
||||
props.use_transform = True
|
||||
return props
|
||||
|
||||
|
||||
class NODE_MT_category_layout(Menu):
|
||||
bl_idname = "NODE_MT_category_layout"
|
||||
|
|
|
@ -542,6 +542,7 @@ class NODE_MT_category_GEO_UTILITIES(Menu):
|
|||
layout.menu("NODE_MT_category_GEO_UTILITIES_ROTATION")
|
||||
layout.separator()
|
||||
node_add_menu.add_node_type(layout, "FunctionNodeRandomValue")
|
||||
node_add_menu.add_foreach_zone(layout, label="For-Each Zone")
|
||||
node_add_menu.add_repeat_zone(layout, label="Repeat Zone")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSwitch")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
|
|
@ -1143,6 +1143,114 @@ class NODE_PT_repeat_zone_items(Panel):
|
|||
layout.prop(output_node, "inspection_index")
|
||||
|
||||
|
||||
class NODE_UL_foreach_zone_input_items(bpy.types.UIList):
|
||||
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
|
||||
draw_socket_item_in_list(self, layout, item, icon)
|
||||
|
||||
|
||||
class NODE_UL_foreach_zone_output_items(bpy.types.UIList):
|
||||
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
|
||||
draw_socket_item_in_list(self, layout, item, icon)
|
||||
|
||||
|
||||
class NODE_PT_foreach_zone_items(Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Node"
|
||||
bl_label = "For-Each"
|
||||
|
||||
input_node_type = 'GeometryNodeForEachInput'
|
||||
output_node_type = 'GeometryNodeForEachOutput'
|
||||
|
||||
@classmethod
|
||||
def get_output_node(cls, context):
|
||||
node = context.active_node
|
||||
if node.bl_idname == cls.input_node_type:
|
||||
return node.paired_output
|
||||
if node.bl_idname == cls.output_node_type:
|
||||
return node
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
snode = context.space_data
|
||||
if snode is None:
|
||||
return False
|
||||
node = context.active_node
|
||||
if node is None or node.bl_idname not in (cls.input_node_type, cls.output_node_type):
|
||||
return False
|
||||
if cls.get_output_node(context) is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
output_node = self.get_output_node(context)
|
||||
self.draw_input_items(layout, output_node)
|
||||
self.draw_output_items(layout, output_node)
|
||||
|
||||
def draw_input_items(self, layout, output_node):
|
||||
split = layout.row()
|
||||
split.template_list(
|
||||
"NODE_UL_foreach_zone_input_items",
|
||||
"",
|
||||
output_node,
|
||||
"input_items",
|
||||
output_node,
|
||||
"input_active_index")
|
||||
|
||||
ops_col = split.column()
|
||||
|
||||
add_remove_col = ops_col.column(align=True)
|
||||
add_remove_col.operator("node.foreach_zone_input_item_add", icon='ADD', text="")
|
||||
add_remove_col.operator("node.foreach_zone_input_item_remove", icon='REMOVE', text="")
|
||||
|
||||
ops_col.separator()
|
||||
|
||||
up_down_col = ops_col.column(align=True)
|
||||
props = up_down_col.operator("node.foreach_zone_input_item_move", icon='TRIA_UP', text="")
|
||||
props.direction = 'UP'
|
||||
props = up_down_col.operator("node.foreach_zone_input_item_move", icon='TRIA_DOWN', text="")
|
||||
props.direction = 'DOWN'
|
||||
|
||||
active_item = output_node.input_active_item
|
||||
if active_item is not None:
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
layout.prop(active_item, "socket_type")
|
||||
|
||||
def draw_output_items(self, layout, output_node):
|
||||
split = layout.row()
|
||||
split.template_list(
|
||||
"NODE_UL_foreach_zone_output_items",
|
||||
"",
|
||||
output_node,
|
||||
"output_items",
|
||||
output_node,
|
||||
"output_active_index")
|
||||
|
||||
ops_col = split.column()
|
||||
|
||||
add_remove_col = ops_col.column(align=True)
|
||||
add_remove_col.operator("node.foreach_zone_output_item_add", icon='ADD', text="")
|
||||
add_remove_col.operator("node.foreach_zone_output_item_remove", icon='REMOVE', text="")
|
||||
|
||||
ops_col.separator()
|
||||
|
||||
up_down_col = ops_col.column(align=True)
|
||||
props = up_down_col.operator("node.foreach_zone_output_item_move", icon='TRIA_UP', text="")
|
||||
props.direction = 'UP'
|
||||
props = up_down_col.operator("node.foreach_zone_output_item_move", icon='TRIA_DOWN', text="")
|
||||
props.direction = 'DOWN'
|
||||
|
||||
active_item = output_node.output_active_item
|
||||
if active_item is not None:
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
layout.prop(active_item, "socket_type")
|
||||
|
||||
|
||||
|
||||
# Grease Pencil properties
|
||||
class NODE_PT_annotation(AnnotationDataPanel, Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
|
@ -1212,6 +1320,9 @@ classes = (
|
|||
NODE_PT_simulation_zone_items,
|
||||
NODE_UL_repeat_zone_items,
|
||||
NODE_PT_repeat_zone_items,
|
||||
NODE_UL_foreach_zone_input_items,
|
||||
NODE_UL_foreach_zone_output_items,
|
||||
NODE_PT_foreach_zone_items,
|
||||
NODE_PT_active_node_properties,
|
||||
|
||||
node_panel(EEVEE_MATERIAL_PT_settings),
|
||||
|
|
|
@ -93,6 +93,8 @@ class SPREADSHEET_HT_header(bpy.types.Header):
|
|||
layout.label(text="Simulation Zone")
|
||||
elif ctx.type == 'REPEAT_ZONE':
|
||||
layout.label(text="Repeat Zone")
|
||||
elif ctx.type == 'FOREACH_ZONE':
|
||||
layout.label(text='For-Each Zone')
|
||||
elif ctx.type == 'VIEWER_NODE':
|
||||
layout.label(text=ctx.ui_name)
|
||||
|
||||
|
|
|
@ -108,4 +108,29 @@ class RepeatZoneComputeContext : public ComputeContext {
|
|||
void print_current_in_line(std::ostream &stream) const override;
|
||||
};
|
||||
|
||||
class ForEachZoneComputeContext : public ComputeContext {
|
||||
private:
|
||||
static constexpr const char *s_static_type = "FOREACH_ZONE";
|
||||
|
||||
int32_t output_node_id_;
|
||||
int index_;
|
||||
|
||||
public:
|
||||
ForEachZoneComputeContext(const ComputeContext *parent, int32_t output_node_id, int index);
|
||||
ForEachZoneComputeContext(const ComputeContext *parent, const bNode &node, int index);
|
||||
|
||||
int32_t output_node_id() const
|
||||
{
|
||||
return output_node_id_;
|
||||
}
|
||||
|
||||
int index() const
|
||||
{
|
||||
return index_;
|
||||
}
|
||||
|
||||
private:
|
||||
void print_current_in_line(std::ostream &stream) const override;
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -1309,6 +1309,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
|
|||
#define GEO_NODE_TOOL_SET_FACE_SET 2113
|
||||
#define GEO_NODE_POINTS_TO_CURVES 2114
|
||||
#define GEO_NODE_INPUT_EDGE_SMOOTH 2115
|
||||
#define GEO_NODE_FOR_EACH_INPUT 2116
|
||||
#define GEO_NODE_FOR_EACH_OUTPUT 2117
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ extern "C" {
|
|||
|
||||
enum ViewerPathEqualFlag {
|
||||
VIEWER_PATH_EQUAL_FLAG_IGNORE_REPEAT_ITERATION = (1 << 0),
|
||||
VIEWER_PATH_EQUAL_FLAG_IGNORE_FOREACH_INDEX = (1 << 1),
|
||||
};
|
||||
|
||||
void BKE_viewer_path_init(ViewerPath *viewer_path);
|
||||
|
@ -57,6 +58,7 @@ GroupNodeViewerPathElem *BKE_viewer_path_elem_new_group_node(void);
|
|||
SimulationZoneViewerPathElem *BKE_viewer_path_elem_new_simulation_zone(void);
|
||||
ViewerNodeViewerPathElem *BKE_viewer_path_elem_new_viewer_node(void);
|
||||
RepeatZoneViewerPathElem *BKE_viewer_path_elem_new_repeat_zone(void);
|
||||
ForEachZoneViewerPathElem *BKE_viewer_path_elem_new_foreach_zone(void);
|
||||
ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src);
|
||||
bool BKE_viewer_path_elem_equal(const ViewerPathElem *a,
|
||||
const ViewerPathElem *b,
|
||||
|
|
|
@ -119,4 +119,33 @@ void RepeatZoneComputeContext::print_current_in_line(std::ostream &stream) const
|
|||
stream << "Repeat Zone ID: " << output_node_id_;
|
||||
}
|
||||
|
||||
ForEachZoneComputeContext::ForEachZoneComputeContext(const ComputeContext *parent,
|
||||
const int32_t output_node_id,
|
||||
const int index)
|
||||
: ComputeContext(s_static_type, parent), output_node_id_(output_node_id), index_(index)
|
||||
{
|
||||
/* Mix static type and node id into a single buffer so that only a single call to #mix_in is
|
||||
* necessary. */
|
||||
const int type_size = strlen(s_static_type);
|
||||
const int buffer_size = type_size + 1 + sizeof(int32_t) + sizeof(int);
|
||||
DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8);
|
||||
char *buffer = static_cast<char *>(buffer_owner.buffer());
|
||||
memcpy(buffer, s_static_type, type_size + 1);
|
||||
memcpy(buffer + type_size + 1, &output_node_id_, sizeof(int32_t));
|
||||
memcpy(buffer + type_size + 1 + sizeof(int32_t), &index_, sizeof(int));
|
||||
hash_.mix_in(buffer, buffer_size);
|
||||
}
|
||||
|
||||
ForEachZoneComputeContext::ForEachZoneComputeContext(const ComputeContext *parent,
|
||||
const bNode &node,
|
||||
const int index)
|
||||
: ForEachZoneComputeContext(parent, node.identifier, index)
|
||||
{
|
||||
}
|
||||
|
||||
void ForEachZoneComputeContext::print_current_in_line(std::ostream &stream) const
|
||||
{
|
||||
stream << "Repeat Zone ID: " << output_node_id_;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -788,6 +788,10 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
|
|||
if (node->type == GEO_NODE_REPEAT_OUTPUT) {
|
||||
blender::nodes::RepeatItemsAccessor::blend_write(writer, *node);
|
||||
}
|
||||
if (node->type == GEO_NODE_FOR_EACH_OUTPUT) {
|
||||
blender::nodes::ForEachInputItemsAccessor::blend_write(writer, *node);
|
||||
blender::nodes::ForEachOutputItemsAccessor::blend_write(writer, *node);
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
||||
|
@ -985,6 +989,11 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
|
|||
blender::nodes::RepeatItemsAccessor::blend_read_data(reader, *node);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_FOR_EACH_OUTPUT: {
|
||||
blender::nodes::ForEachInputItemsAccessor::blend_read_data(reader, *node);
|
||||
blender::nodes::ForEachOutputItemsAccessor::blend_read_data(reader, *node);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -984,15 +984,29 @@ class NodeTreeMainUpdater {
|
|||
}
|
||||
}
|
||||
else {
|
||||
bool all_available_inputs_computed = true;
|
||||
Vector<const bNodeSocket *, 16> src_sockets;
|
||||
for (const bNodeSocket *input_socket : node.input_sockets()) {
|
||||
if (input_socket->is_available()) {
|
||||
if (!hash_by_socket_id[input_socket->index_in_tree()].has_value()) {
|
||||
sockets_to_check.push(input_socket);
|
||||
all_available_inputs_computed = false;
|
||||
src_sockets.append(input_socket);
|
||||
}
|
||||
}
|
||||
if (all_zone_output_node_types().contains(node.type)) {
|
||||
const bNodeZoneType &zone_type = *zone_type_by_node_type(node.type);
|
||||
if (const bNode *zone_input_node = zone_type.get_corresponding_input(tree, node)) {
|
||||
for (const bNodeSocket *input_socket : zone_input_node->input_sockets()) {
|
||||
if (input_socket->is_available()) {
|
||||
src_sockets.append(input_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool all_available_inputs_computed = true;
|
||||
for (const bNodeSocket *socket : src_sockets) {
|
||||
if (!hash_by_socket_id[socket->index_in_tree()].has_value()) {
|
||||
sockets_to_check.push(socket);
|
||||
all_available_inputs_computed = false;
|
||||
}
|
||||
}
|
||||
if (!all_available_inputs_computed) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1015,10 +1029,10 @@ class NodeTreeMainUpdater {
|
|||
}
|
||||
else {
|
||||
socket_hash = get_socket_ptr_hash(socket);
|
||||
for (const bNodeSocket *input_socket : node.input_sockets()) {
|
||||
if (input_socket->is_available()) {
|
||||
const uint32_t input_socket_hash = *hash_by_socket_id[input_socket->index_in_tree()];
|
||||
socket_hash = noise::hash(socket_hash, input_socket_hash);
|
||||
for (const bNodeSocket *src_socket : src_sockets) {
|
||||
if (src_socket->is_available()) {
|
||||
const uint32_t src_socket_hash = *hash_by_socket_id[src_socket->index_in_tree()];
|
||||
socket_hash = noise::hash(socket_hash, src_socket_hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1096,6 +1110,21 @@ class NodeTreeMainUpdater {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (all_zone_output_node_types().contains(node.type)) {
|
||||
const bNodeZoneType &zone_type = *zone_type_by_node_type(node.type);
|
||||
if (const bNode *zone_input_node = zone_type.get_corresponding_input(tree, node)) {
|
||||
if (zone_input_node->runtime->changed_flag != NTREE_CHANGED_NOTHING) {
|
||||
return true;
|
||||
}
|
||||
for (const bNodeSocket *input_socket : zone_input_node->input_sockets()) {
|
||||
bool &pushed = pushed_by_socket_id[input_socket->index_in_tree()];
|
||||
if (!pushed) {
|
||||
sockets_to_check.push(input_socket);
|
||||
pushed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* The Normal node has a special case, because the value stored in the first output socket
|
||||
* is used as input in the node. */
|
||||
if (node.type == SH_NODE_NORMAL && socket.index() == 1) {
|
||||
|
|
|
@ -95,6 +95,11 @@ void BKE_viewer_path_blend_write(BlendWriter *writer, const ViewerPath *viewer_p
|
|||
BLO_write_struct(writer, RepeatZoneViewerPathElem, typed_elem);
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: {
|
||||
const auto *typed_elem = reinterpret_cast<ForEachZoneViewerPathElem *>(elem);
|
||||
BLO_write_struct(writer, ForEachZoneViewerPathElem, typed_elem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
BLO_write_string(writer, elem->ui_name);
|
||||
}
|
||||
|
@ -110,6 +115,7 @@ void BKE_viewer_path_blend_read_data(BlendDataReader *reader, ViewerPath *viewer
|
|||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_ID: {
|
||||
break;
|
||||
}
|
||||
|
@ -135,7 +141,8 @@ void BKE_viewer_path_foreach_id(LibraryForeachIDData *data, ViewerPath *viewer_p
|
|||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
|
||||
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +162,8 @@ void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const IDRemapper *mapping
|
|||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
|
||||
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -190,6 +198,9 @@ ViewerPathElem *BKE_viewer_path_elem_new(const ViewerPathElemType type)
|
|||
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
|
||||
return &make_elem<RepeatZoneViewerPathElem>(type)->base;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: {
|
||||
return &make_elem<ForEachZoneViewerPathElem>(type)->base;
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
|
@ -230,6 +241,12 @@ RepeatZoneViewerPathElem *BKE_viewer_path_elem_new_repeat_zone()
|
|||
BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE));
|
||||
}
|
||||
|
||||
ForEachZoneViewerPathElem *BKE_viewer_path_elem_new_foreach_zone()
|
||||
{
|
||||
return reinterpret_cast<ForEachZoneViewerPathElem *>(
|
||||
BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE));
|
||||
}
|
||||
|
||||
ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src)
|
||||
{
|
||||
ViewerPathElem *dst = BKE_viewer_path_elem_new(ViewerPathElemType(src->type));
|
||||
|
@ -276,6 +293,12 @@ ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src)
|
|||
new_elem->iteration = old_elem->iteration;
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: {
|
||||
const auto *old_elem = reinterpret_cast<const ForEachZoneViewerPathElem *>(src);
|
||||
auto *new_elem = reinterpret_cast<ForEachZoneViewerPathElem *>(dst);
|
||||
new_elem->foreach_output_node_id = old_elem->foreach_output_node_id;
|
||||
new_elem->index = old_elem->index;
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
@ -320,6 +343,13 @@ bool BKE_viewer_path_elem_equal(const ViewerPathElem *a,
|
|||
((flag & VIEWER_PATH_EQUAL_FLAG_IGNORE_REPEAT_ITERATION) != 0 ||
|
||||
a_elem->iteration == b_elem->iteration);
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: {
|
||||
const auto *a_elem = reinterpret_cast<const ForEachZoneViewerPathElem *>(a);
|
||||
const auto *b_elem = reinterpret_cast<const ForEachZoneViewerPathElem *>(b);
|
||||
return a_elem->foreach_output_node_id == b_elem->foreach_output_node_id &&
|
||||
((flag & VIEWER_PATH_EQUAL_FLAG_IGNORE_FOREACH_INDEX) != 0 ||
|
||||
a_elem->index == b_elem->index);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -331,7 +361,8 @@ void BKE_viewer_path_elem_free(ViewerPathElem *elem)
|
|||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
|
||||
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: {
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_MODIFIER: {
|
||||
|
|
|
@ -140,6 +140,7 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
|
|||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
FROM_DEFAULT_V4_UCHAR(space_node.node_zone_foreach);
|
||||
}
|
||||
|
||||
#undef FROM_DEFAULT_V4_UCHAR
|
||||
|
|
|
@ -190,6 +190,7 @@ enum ThemeColorID {
|
|||
|
||||
TH_NODE_ZONE_SIMULATION,
|
||||
TH_NODE_ZONE_REPEAT,
|
||||
TH_NODE_ZONE_FOR_EACH,
|
||||
TH_SIMULATED_FRAMES,
|
||||
|
||||
TH_CONSOLE_OUTPUT,
|
||||
|
|
|
@ -666,6 +666,9 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
|
|||
case TH_NODE_ZONE_REPEAT:
|
||||
cp = ts->node_zone_repeat;
|
||||
break;
|
||||
case TH_NODE_ZONE_FOR_EACH:
|
||||
cp = ts->node_zone_foreach;
|
||||
break;
|
||||
case TH_SIMULATED_FRAMES:
|
||||
cp = ts->simulated_frames;
|
||||
break;
|
||||
|
|
|
@ -44,6 +44,12 @@ static ViewerPathElem *viewer_path_elem_for_zone(const bNodeTreeZone &zone)
|
|||
node_elem->iteration = storage.inspection_index;
|
||||
return &node_elem->base;
|
||||
}
|
||||
case GEO_NODE_FOR_EACH_OUTPUT: {
|
||||
ForEachZoneViewerPathElem *node_elem = BKE_viewer_path_elem_new_foreach_zone();
|
||||
node_elem->foreach_output_node_id = zone.output_node->identifier;
|
||||
node_elem->index = 0;
|
||||
return &node_elem->base;
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
|
@ -253,7 +259,8 @@ std::optional<ViewerPathForGeometryNodesViewer> parse_geometry_nodes_viewer(
|
|||
if (!ELEM(elem->type,
|
||||
VIEWER_PATH_ELEM_TYPE_GROUP_NODE,
|
||||
VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE,
|
||||
VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE))
|
||||
VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE,
|
||||
VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -320,6 +327,19 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed
|
|||
zone = next_zone;
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: {
|
||||
const auto &typed_elem = *reinterpret_cast<const ForEachZoneViewerPathElem *>(path_elem);
|
||||
const bNodeTreeZone *next_zone = tree_zones->get_zone_by_node(
|
||||
typed_elem.foreach_output_node_id);
|
||||
if (next_zone == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (next_zone->parent_zone != zone) {
|
||||
return false;
|
||||
}
|
||||
zone = next_zone;
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
|
||||
const auto &typed_elem = *reinterpret_cast<const GroupNodeViewerPathElem *>(path_elem);
|
||||
const bNode *group_node = ngroup->node_by_id(typed_elem.node_id);
|
||||
|
@ -484,6 +504,12 @@ bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snod
|
|||
elem.iteration);
|
||||
return true;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: {
|
||||
const auto &elem = reinterpret_cast<const ForEachZoneViewerPathElem &>(elem_generic);
|
||||
compute_context_builder.push<bke::ForEachZoneComputeContext>(elem.foreach_output_node_id,
|
||||
elem.index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1836,6 +1836,73 @@ typedef struct NodeGeometryRepeatOutput {
|
|||
#endif
|
||||
} NodeGeometryRepeatOutput;
|
||||
|
||||
typedef struct NodeForEachInputItem {
|
||||
char *name;
|
||||
/** #eNodeSocketDatatype. */
|
||||
short socket_type;
|
||||
char _pad[2];
|
||||
/**
|
||||
* Generated unique identifier for sockets which stays the same even when the item order or
|
||||
* names change.
|
||||
*/
|
||||
int identifier;
|
||||
} NodeForEachInputItem;
|
||||
|
||||
typedef struct NodeForEachOutputItem {
|
||||
char *name;
|
||||
/** #eNodeSocketDatatype. */
|
||||
short socket_type;
|
||||
char _pad[2];
|
||||
/**
|
||||
* Generated unique identifier for sockets which stays the same even when the item order or
|
||||
* names change.
|
||||
*/
|
||||
int identifier;
|
||||
} NodeForEachOutputItem;
|
||||
|
||||
typedef struct NodeGeometryForEachInput {
|
||||
/** bNode.identifier of the corresponding output node. */
|
||||
int output_node_id;
|
||||
} NodeGeometryForEachInput;
|
||||
|
||||
typedef struct NodeGeometryForEachOutput {
|
||||
NodeForEachInputItem *input_items;
|
||||
int input_items_num;
|
||||
int input_active_index;
|
||||
int input_next_identifier;
|
||||
char _pad1[4];
|
||||
|
||||
NodeForEachOutputItem *output_items;
|
||||
int output_items_num;
|
||||
int output_active_index;
|
||||
int output_next_identifier;
|
||||
|
||||
/** #GeometryNodeForEachMode. */
|
||||
uint8_t mode;
|
||||
/** #eAttrDomain. */
|
||||
uint8_t domain;
|
||||
char _pad2[2];
|
||||
|
||||
#ifdef __cplusplus
|
||||
blender::Span<NodeForEachInputItem> input_items_span() const
|
||||
{
|
||||
return {this->input_items, this->input_items_num};
|
||||
}
|
||||
blender::MutableSpan<NodeForEachInputItem> input_items_span()
|
||||
{
|
||||
return {this->input_items, this->input_items_num};
|
||||
}
|
||||
blender::Span<NodeForEachOutputItem> output_items_span() const
|
||||
{
|
||||
return {this->output_items, this->output_items_num};
|
||||
}
|
||||
blender::MutableSpan<NodeForEachOutputItem> output_items_span()
|
||||
{
|
||||
return {this->output_items, this->output_items_num};
|
||||
}
|
||||
#endif
|
||||
} NodeGeometryForEachOutput;
|
||||
|
||||
typedef struct NodeGeometryDistributePointsInVolume {
|
||||
/** #GeometryNodePointDistributeVolumeMode. */
|
||||
uint8_t mode;
|
||||
|
@ -2742,3 +2809,9 @@ typedef enum NodeCombSepColorMode {
|
|||
NODE_COMBSEP_COLOR_HSV = 1,
|
||||
NODE_COMBSEP_COLOR_HSL = 2,
|
||||
} NodeCombSepColorMode;
|
||||
|
||||
typedef enum GeometryNodeForEachMode {
|
||||
GEO_NODE_FOR_EACH_MODE_INDEX = 0,
|
||||
GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT = 1,
|
||||
GEO_NODE_FOR_EACH_MODE_INSTANCE = 2,
|
||||
} GeometryNodeForEachMode;
|
||||
|
|
|
@ -349,7 +349,7 @@ typedef struct ThemeSpace {
|
|||
|
||||
unsigned char node_zone_simulation[4];
|
||||
unsigned char node_zone_repeat[4];
|
||||
unsigned char _pad9[4];
|
||||
unsigned char node_zone_foreach[4];
|
||||
unsigned char simulated_frames[4];
|
||||
|
||||
/** For sequence editor. */
|
||||
|
|
|
@ -16,6 +16,7 @@ typedef enum ViewerPathElemType {
|
|||
VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE = 3,
|
||||
VIEWER_PATH_ELEM_TYPE_VIEWER_NODE = 4,
|
||||
VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE = 5,
|
||||
VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE = 6,
|
||||
} ViewerPathElemType;
|
||||
|
||||
typedef struct ViewerPathElem {
|
||||
|
@ -56,6 +57,13 @@ typedef struct RepeatZoneViewerPathElem {
|
|||
int iteration;
|
||||
} RepeatZoneViewerPathElem;
|
||||
|
||||
typedef struct ForEachZoneViewerPathElem {
|
||||
ViewerPathElem base;
|
||||
|
||||
int foreach_output_node_id;
|
||||
int index;
|
||||
} ForEachZoneViewerPathElem;
|
||||
|
||||
typedef struct ViewerNodeViewerPathElem {
|
||||
ViewerPathElem base;
|
||||
|
||||
|
|
|
@ -608,6 +608,8 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = {
|
|||
# include "DNA_scene_types.h"
|
||||
# include "WM_api.hh"
|
||||
|
||||
using blender::nodes::ForEachInputItemsAccessor;
|
||||
using blender::nodes::ForEachOutputItemsAccessor;
|
||||
using blender::nodes::RepeatItemsAccessor;
|
||||
using blender::nodes::SimulationItemsAccessor;
|
||||
|
||||
|
@ -3267,14 +3269,20 @@ static void rna_Node_ItemArray_item_update(Main *bmain, Scene * /*scene*/, Point
|
|||
|
||||
template<typename Accessor>
|
||||
static const EnumPropertyItem *rna_Node_ItemArray_socket_type_itemf(bContext * /*C*/,
|
||||
PointerRNA * /*ptr*/,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
using ItemT = typename Accessor::ItemT;
|
||||
bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
|
||||
ItemT &item = *static_cast<ItemT *>(ptr->data);
|
||||
const bNode *node = blender::nodes::socket_items::find_node_by_item<Accessor>(ntree, item);
|
||||
BLI_assert(node != nullptr);
|
||||
|
||||
*r_free = true;
|
||||
return itemf_function_check(
|
||||
rna_enum_node_socket_data_type_items, [](const EnumPropertyItem *item) {
|
||||
return Accessor::supports_socket_type(eNodeSocketDatatype(item->value));
|
||||
rna_enum_node_socket_data_type_items, [&](const EnumPropertyItem *item) {
|
||||
return Accessor::supports_socket_type(*node, eNodeSocketDatatype(item->value));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3303,7 +3311,7 @@ typename Accessor::ItemT *rna_Node_ItemArray_new_with_socket_and_name(
|
|||
ID *id, bNode *node, Main *bmain, ReportList *reports, int socket_type, const char *name)
|
||||
{
|
||||
using ItemT = typename Accessor::ItemT;
|
||||
if (!Accessor::supports_socket_type(eNodeSocketDatatype(socket_type))) {
|
||||
if (!Accessor::supports_socket_type(*node, eNodeSocketDatatype(socket_type))) {
|
||||
BKE_report(reports, RPT_ERROR, "Unable to create item with this socket type");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3318,6 +3326,18 @@ typename Accessor::ItemT *rna_Node_ItemArray_new_with_socket_and_name(
|
|||
return new_item;
|
||||
}
|
||||
|
||||
static void rna_ForEachOutputNode_mode_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
|
||||
bNode &node = *static_cast<bNode *>(ptr->data);
|
||||
|
||||
blender::nodes::socket_items::remove_unsupported_socket_types<ForEachInputItemsAccessor>(node);
|
||||
blender::nodes::socket_items::remove_unsupported_socket_types<ForEachOutputItemsAccessor>(node);
|
||||
|
||||
BKE_ntree_update_tag_node_property(&ntree, &node);
|
||||
ED_node_tree_propagate_change(nullptr, bmain, &ntree);
|
||||
}
|
||||
|
||||
/* ******** Node Socket Types ******** */
|
||||
|
||||
static PointerRNA rna_NodeOutputFile_slot_layer_get(CollectionPropertyIterator *iter)
|
||||
|
@ -8725,22 +8745,29 @@ static void def_geo_repeat_input(StructRNA *srna)
|
|||
def_common_zone_input(srna);
|
||||
}
|
||||
|
||||
static void def_geo_foreach_input(StructRNA *srna)
|
||||
{
|
||||
RNA_def_struct_sdna_from(srna, "NodeGeometryForEachInput", "storage");
|
||||
|
||||
def_common_zone_input(srna);
|
||||
}
|
||||
|
||||
static void rna_def_node_item_array_socket_item_common(StructRNA *srna, const char *accessor)
|
||||
{
|
||||
static blender::LinearAllocator<> allocator;
|
||||
PropertyRNA *prop;
|
||||
|
||||
char name_set_func[64];
|
||||
char name_set_func[128];
|
||||
SNPRINTF(name_set_func, "rna_Node_ItemArray_item_name_set<%s>", accessor);
|
||||
|
||||
char item_update_func[64];
|
||||
char item_update_func[128];
|
||||
SNPRINTF(item_update_func, "rna_Node_ItemArray_item_update<%s>", accessor);
|
||||
const char *item_update_func_ptr = allocator.copy_string(item_update_func).c_str();
|
||||
|
||||
char socket_type_itemf[64];
|
||||
char socket_type_itemf[128];
|
||||
SNPRINTF(socket_type_itemf, "rna_Node_ItemArray_socket_type_itemf<%s>", accessor);
|
||||
|
||||
char color_get_func[64];
|
||||
char color_get_func[128];
|
||||
SNPRINTF(color_get_func, "rna_Node_ItemArray_item_color_get<%s>", accessor);
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
|
@ -8775,11 +8802,11 @@ static void rna_def_node_item_array_common_functions(StructRNA *srna,
|
|||
PropertyRNA *parm;
|
||||
FunctionRNA *func;
|
||||
|
||||
char remove_call[64];
|
||||
char remove_call[128];
|
||||
SNPRINTF(remove_call, "rna_Node_ItemArray_remove<%s>", accessor_name);
|
||||
char clear_call[64];
|
||||
char clear_call[128];
|
||||
SNPRINTF(clear_call, "rna_Node_ItemArray_clear<%s>", accessor_name);
|
||||
char move_call[64];
|
||||
char move_call[128];
|
||||
SNPRINTF(move_call, "rna_Node_ItemArray_move<%s>", accessor_name);
|
||||
|
||||
func = RNA_def_function(srna, "remove", allocator.copy_string(remove_call).c_str());
|
||||
|
@ -8956,6 +8983,127 @@ static void def_geo_repeat_output(StructRNA *srna)
|
|||
RNA_def_property_update(prop, NC_NODE, "rna_Node_update");
|
||||
}
|
||||
|
||||
static void rna_def_foreach_input_item(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna = RNA_def_struct(brna, "ForEachInputItem", nullptr);
|
||||
RNA_def_struct_ui_text(srna, "For-Each Input Item", "");
|
||||
RNA_def_struct_sdna(srna, "NodeForEachInputItem");
|
||||
|
||||
rna_def_node_item_array_socket_item_common(srna, "ForEachInputItemsAccessor");
|
||||
}
|
||||
|
||||
static void rna_def_foreach_output_item(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna = RNA_def_struct(brna, "ForEachOutputItem", nullptr);
|
||||
RNA_def_struct_ui_text(srna, "For-Each Output Item", "");
|
||||
RNA_def_struct_sdna(srna, "NodeForEachOutputItem");
|
||||
|
||||
rna_def_node_item_array_socket_item_common(srna, "ForEachOutputItemsAccessor");
|
||||
}
|
||||
|
||||
static void rna_def_geo_foreach_input_items(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna = RNA_def_struct(brna, "NodeGeometryForEachInputItems", nullptr);
|
||||
RNA_def_struct_sdna(srna, "bNode");
|
||||
RNA_def_struct_ui_text(srna, "Input Items", "Collection of for-each input items");
|
||||
|
||||
rna_def_node_item_array_new_with_socket_and_name(
|
||||
srna, "ForEachInputItem", "ForEachInputItemsAccessor");
|
||||
rna_def_node_item_array_common_functions(srna, "ForEachInputItem", "ForEachInputItemsAccessor");
|
||||
}
|
||||
|
||||
static void rna_def_geo_foreach_output_items(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna = RNA_def_struct(brna, "NodeGeometryForEachOutputItems", nullptr);
|
||||
RNA_def_struct_sdna(srna, "bNode");
|
||||
RNA_def_struct_ui_text(srna, "Output Items", "Collection of for-each output items");
|
||||
|
||||
rna_def_node_item_array_new_with_socket_and_name(
|
||||
srna, "ForEachOutputItem", "ForEachOutputItemsAccessor");
|
||||
rna_def_node_item_array_common_functions(
|
||||
srna, "ForEachOutputItem", "ForEachOutputItemsAccessor");
|
||||
}
|
||||
|
||||
static void def_geo_foreach_output(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
static const EnumPropertyItem mode_items[] = {
|
||||
{GEO_NODE_FOR_EACH_MODE_INDEX, "INDEX", 0, "Index", "Do something for each index"},
|
||||
{GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT,
|
||||
"GEOMETRY_ELEMENT",
|
||||
0,
|
||||
"Geometry Element",
|
||||
"Do something for each element in a geometry on a specific domain"},
|
||||
{GEO_NODE_FOR_EACH_MODE_INSTANCE,
|
||||
"INSTANCE",
|
||||
0,
|
||||
"Instance",
|
||||
"Do something for each unique top level instance"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeGeometryForEachOutput", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, mode_items);
|
||||
RNA_def_property_ui_text(prop, "Mode", "");
|
||||
RNA_def_property_update(prop, NC_NODE, "rna_ForEachOutputNode_mode_update");
|
||||
|
||||
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
|
||||
RNA_def_property_ui_text(prop, "Domain", "");
|
||||
RNA_def_property_update(prop, NC_NODE, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "input_items", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "input_items", "input_items_num");
|
||||
RNA_def_property_struct_type(prop, "ForEachInputItem");
|
||||
RNA_def_property_ui_text(prop, "Input Items", "");
|
||||
RNA_def_property_srna(prop, "NodeGeometryForEachInputItems");
|
||||
|
||||
prop = RNA_def_property(srna, "input_active_index", PROP_INT, PROP_UNSIGNED);
|
||||
RNA_def_property_int_sdna(prop, nullptr, "input_active_index");
|
||||
RNA_def_property_ui_text(prop, "Active Input Item Index", "Index of the active item");
|
||||
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_NODE, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "input_active_item", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "ForEachInputItem");
|
||||
RNA_def_property_pointer_funcs(prop,
|
||||
"rna_Node_ItemArray_active_get<ForEachInputItemsAccessor>",
|
||||
"rna_Node_ItemArray_active_set<ForEachInputItemsAccessor>",
|
||||
nullptr,
|
||||
nullptr);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NO_DEG_UPDATE);
|
||||
RNA_def_property_ui_text(prop, "Active Item Index", "Index of the active item");
|
||||
RNA_def_property_update(prop, NC_NODE, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "output_items", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "output_items", "output_items_num");
|
||||
RNA_def_property_struct_type(prop, "ForEachOutputItem");
|
||||
RNA_def_property_ui_text(prop, "OutputItems", "");
|
||||
RNA_def_property_srna(prop, "NodeGeometryForEachOutputItems");
|
||||
|
||||
prop = RNA_def_property(srna, "output_active_index", PROP_INT, PROP_UNSIGNED);
|
||||
RNA_def_property_int_sdna(prop, nullptr, "output_active_index");
|
||||
RNA_def_property_ui_text(prop, "Active Output Item Index", "Index of the active item");
|
||||
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_NODE, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "output_active_item", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "ForEachOutputItem");
|
||||
RNA_def_property_pointer_funcs(prop,
|
||||
"rna_Node_ItemArray_active_get<ForEachOutputItemsAccessor>",
|
||||
"rna_Node_ItemArray_active_set<ForEachOutputItemsAccessor>",
|
||||
nullptr,
|
||||
nullptr);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NO_DEG_UPDATE);
|
||||
RNA_def_property_ui_text(prop, "Active Item Index", "Index of the active item");
|
||||
RNA_def_property_update(prop, NC_NODE, nullptr);
|
||||
}
|
||||
|
||||
static void def_geo_curve_handle_type_selection(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
@ -10278,6 +10426,8 @@ void RNA_def_nodetree(BlenderRNA *brna)
|
|||
|
||||
rna_def_simulation_state_item(brna);
|
||||
rna_def_repeat_item(brna);
|
||||
rna_def_foreach_input_item(brna);
|
||||
rna_def_foreach_output_item(brna);
|
||||
|
||||
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
|
||||
{ \
|
||||
|
@ -10330,6 +10480,8 @@ void RNA_def_nodetree(BlenderRNA *brna)
|
|||
rna_def_cmp_output_file_slot_layer(brna);
|
||||
rna_def_geo_simulation_output_items(brna);
|
||||
rna_def_geo_repeat_output_items(brna);
|
||||
rna_def_geo_foreach_input_items(brna);
|
||||
rna_def_geo_foreach_output_items(brna);
|
||||
|
||||
rna_def_node_instance_hash(brna);
|
||||
}
|
||||
|
|
|
@ -3348,6 +3348,8 @@ static StructRNA *rna_viewer_path_elem_refine(PointerRNA *ptr)
|
|||
return &RNA_ViewerNodeViewerPathElem;
|
||||
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE:
|
||||
return &RNA_RepeatZoneViewerPathElem;
|
||||
case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE:
|
||||
return &RNA_ForEachZoneViewerPathElem;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
|
@ -8090,6 +8092,7 @@ static const EnumPropertyItem viewer_path_elem_type_items[] = {
|
|||
{VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE, "SIMULATION_ZONE", ICON_NONE, "Simulation Zone", ""},
|
||||
{VIEWER_PATH_ELEM_TYPE_VIEWER_NODE, "VIEWER_NODE", ICON_NONE, "Viewer Node", ""},
|
||||
{VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE, "REPEAT_ZONE", ICON_NONE, "Repeat", ""},
|
||||
{VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE, "FOREACH_ZONE", ICON_NONE, "For-Each Zone", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
|
@ -8168,6 +8171,17 @@ static void rna_def_repeat_zone_viewer_path_elem(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Repeat Output Node ID", "");
|
||||
}
|
||||
|
||||
static void rna_def_foreach_zone_viewer_path_elem(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "ForEachZoneViewerPathElem", "ViewerPathElem");
|
||||
|
||||
prop = RNA_def_property(srna, "foreach_output_node_id", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "For-Each Output Node ID", "");
|
||||
}
|
||||
|
||||
static void rna_def_viewer_node_viewer_path_elem(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
@ -8190,6 +8204,7 @@ static void rna_def_viewer_path(BlenderRNA *brna)
|
|||
rna_def_group_node_viewer_path_elem(brna);
|
||||
rna_def_simulation_zone_viewer_path_elem(brna);
|
||||
rna_def_repeat_zone_viewer_path_elem(brna);
|
||||
rna_def_foreach_zone_viewer_path_elem(brna);
|
||||
rna_def_viewer_node_viewer_path_elem(brna);
|
||||
|
||||
srna = RNA_def_struct(brna, "ViewerPath", nullptr);
|
||||
|
|
|
@ -3208,6 +3208,12 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna)
|
|||
RNA_def_property_array(prop, 4);
|
||||
RNA_def_property_ui_text(prop, "Repeat Zone", "");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
|
||||
|
||||
prop = RNA_def_property(srna, "foreach_zone", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "node_zone_foreach");
|
||||
RNA_def_property_array(prop, 4);
|
||||
RNA_def_property_ui_text(prop, "For-Each Zone", "");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
|
||||
}
|
||||
|
||||
static void rna_def_userdef_theme_space_buts(BlenderRNA *brna)
|
||||
|
|
|
@ -606,6 +606,28 @@ static void try_add_side_effect_node(const ComputeContext &final_compute_context
|
|||
compute_context->iteration());
|
||||
current_zone = repeat_zone;
|
||||
}
|
||||
else if (const auto *compute_context = dynamic_cast<const bke::ForEachZoneComputeContext *>(
|
||||
compute_context_generic))
|
||||
{
|
||||
const bke::bNodeTreeZone *foreach_zone = current_zones->get_zone_by_node(
|
||||
compute_context->output_node_id());
|
||||
if (foreach_zone == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (foreach_zone->parent_zone != current_zone) {
|
||||
return;
|
||||
}
|
||||
const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default(
|
||||
foreach_zone, nullptr);
|
||||
if (lf_zone_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_zone_node);
|
||||
local_side_effect_nodes.indices_by_foreach_zone.add(
|
||||
{parent_compute_context_hash, compute_context->output_node_id()},
|
||||
compute_context->index());
|
||||
current_zone = foreach_zone;
|
||||
}
|
||||
else if (const auto *compute_context = dynamic_cast<const bke::NodeGroupComputeContext *>(
|
||||
compute_context_generic))
|
||||
{
|
||||
|
|
|
@ -135,6 +135,7 @@ struct GeoNodesSideEffectNodes {
|
|||
* repeat output node.
|
||||
*/
|
||||
MultiValueMap<std::pair<ComputeContextHash, int32_t>, int> iterations_by_repeat_zone;
|
||||
MultiValueMap<std::pair<ComputeContextHash, int32_t>, int> indices_by_foreach_zone;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -137,6 +137,56 @@ inline void move_item(T *items, const int items_num, const int from_index, const
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Low level utility to remove all socket items for which the predicate is true.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void remove_if(T **items,
|
||||
int *items_num,
|
||||
int *active_index,
|
||||
void (*destruct_item)(T *),
|
||||
const FunctionRef<bool(const T &item)> predicate)
|
||||
{
|
||||
static_assert(std::is_trivial_v<T>);
|
||||
T *old_items = *items;
|
||||
const int old_items_num = *items_num;
|
||||
Vector<int> indices_to_keep;
|
||||
for (const int i : IndexRange(old_items_num)) {
|
||||
T &item = old_items[i];
|
||||
if (predicate(item)) {
|
||||
destruct_item(&item);
|
||||
}
|
||||
else {
|
||||
indices_to_keep.append(i);
|
||||
}
|
||||
}
|
||||
const int new_items_num = indices_to_keep.size();
|
||||
if (old_items_num == new_items_num) {
|
||||
return;
|
||||
}
|
||||
T *new_items = MEM_cnew_array<T>(new_items_num, __func__);
|
||||
for (const int i : IndexRange(new_items_num)) {
|
||||
new_items[i] = old_items[indices_to_keep[i]];
|
||||
}
|
||||
MEM_SAFE_FREE(old_items);
|
||||
|
||||
*items = new_items;
|
||||
*items_num = new_items_num;
|
||||
*active_index = std::max(0, std::min(*active_index, new_items_num - 1));
|
||||
}
|
||||
|
||||
template<typename Accessor> inline void remove_unsupported_socket_types(bNode &node)
|
||||
{
|
||||
using ItemT = typename Accessor::ItemT;
|
||||
SocketItemsRef ref = Accessor::get_items_from_node(node);
|
||||
remove_if<ItemT>(
|
||||
ref.items, ref.items_num, ref.active_index, Accessor::destruct_item, [&](const ItemT &item) {
|
||||
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(
|
||||
*Accessor::get_socket_type(const_cast<ItemT &>(item)));
|
||||
return !Accessor::supports_socket_type(node, socket_type);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Destruct all the items and the free the array itself.
|
||||
*/
|
||||
|
@ -217,7 +267,7 @@ inline typename Accessor::ItemT *add_item_with_socket_and_name(
|
|||
bNode &node, const eNodeSocketDatatype socket_type, const char *name)
|
||||
{
|
||||
using ItemT = typename Accessor::ItemT;
|
||||
BLI_assert(Accessor::supports_socket_type(socket_type));
|
||||
BLI_assert(Accessor::supports_socket_type(node, socket_type));
|
||||
|
||||
SocketItemsRef array = Accessor::get_items_from_node(node);
|
||||
|
||||
|
@ -263,7 +313,7 @@ template<typename Accessor>
|
|||
return false;
|
||||
}
|
||||
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(src_socket->type);
|
||||
if (!Accessor::supports_socket_type(socket_type)) {
|
||||
if (!Accessor::supports_socket_type(storage_node, socket_type)) {
|
||||
return false;
|
||||
}
|
||||
const ItemT *item = add_item_with_socket_and_name<Accessor>(
|
||||
|
|
|
@ -331,6 +331,8 @@ DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, 0, "EXTRUDE_MESH", ExtrudeMesh, "Ex
|
|||
DefNode(GeometryNode, GEO_NODE_FILL_CURVE, 0, "FILL_CURVE", FillCurve, "Fill Curve", "Generate a mesh on the XY plane with faces on the inside of input curves")
|
||||
DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, 0, "FILLET_CURVE", FilletCurve, "Fillet Curve", "Round corners by generating circular arcs on each control point")
|
||||
DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "Reverse the order of the vertices and edges of selected faces, flipping their normal direction")
|
||||
DefNode(GeometryNode, GEO_NODE_FOR_EACH_INPUT, def_geo_foreach_input, "FOREACH_INPUT", ForEachInput, "For-Each Input", "")
|
||||
DefNode(GeometryNode, GEO_NODE_FOR_EACH_OUTPUT, def_geo_foreach_output, "FOREACH_OUTPUT", ForEachOutput, "For-Each Output", "")
|
||||
DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "Convert each input geometry into an instance, which can be much faster than the Join Geometry node when the inputs are large")
|
||||
DefNode(GeometryNode, GEO_NODE_IMAGE_INFO, 0, "IMAGE_INFO", ImageInfo, "Image Info", "Retrieve information about an image")
|
||||
DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "Sample values from an image texture")
|
||||
|
|
|
@ -47,7 +47,7 @@ struct SimulationItemsAccessor {
|
|||
{
|
||||
return &item.name;
|
||||
}
|
||||
static bool supports_socket_type(const eNodeSocketDatatype socket_type)
|
||||
static bool supports_socket_type(const bNode & /*node*/, const eNodeSocketDatatype socket_type)
|
||||
{
|
||||
return ELEM(socket_type,
|
||||
SOCK_FLOAT,
|
||||
|
@ -109,7 +109,7 @@ struct RepeatItemsAccessor {
|
|||
{
|
||||
return &item.name;
|
||||
}
|
||||
static bool supports_socket_type(const eNodeSocketDatatype socket_type)
|
||||
static bool supports_socket_type(const bNode & /*node*/, const eNodeSocketDatatype socket_type)
|
||||
{
|
||||
return ELEM(socket_type,
|
||||
SOCK_FLOAT,
|
||||
|
@ -141,4 +141,123 @@ struct RepeatItemsAccessor {
|
|||
}
|
||||
};
|
||||
|
||||
struct ForEachInputItemsAccessor {
|
||||
using ItemT = NodeForEachInputItem;
|
||||
static StructRNA *item_srna;
|
||||
/* This refers to the node that stores the item array. */
|
||||
static int node_type;
|
||||
static constexpr const char *node_idname = "GeometryNodeForEachOutput";
|
||||
|
||||
static socket_items::SocketItemsRef<NodeForEachInputItem> get_items_from_node(bNode &node)
|
||||
{
|
||||
auto *storage = static_cast<NodeGeometryForEachOutput *>(node.storage);
|
||||
return {&storage->input_items, &storage->input_items_num, &storage->input_active_index};
|
||||
}
|
||||
static void copy_item(const NodeForEachInputItem &src, NodeForEachInputItem &dst)
|
||||
{
|
||||
dst = src;
|
||||
dst.name = BLI_strdup_null(dst.name);
|
||||
}
|
||||
static void destruct_item(NodeForEachInputItem *item)
|
||||
{
|
||||
MEM_SAFE_FREE(item->name);
|
||||
}
|
||||
static void blend_write(BlendWriter *, const bNode &node);
|
||||
static void blend_read_data(BlendDataReader *reader, bNode &node);
|
||||
static short *get_socket_type(NodeForEachInputItem &item)
|
||||
{
|
||||
return &item.socket_type;
|
||||
}
|
||||
static char **get_name(NodeForEachInputItem &item)
|
||||
{
|
||||
return &item.name;
|
||||
}
|
||||
static bool supports_socket_type(const bNode &node, const eNodeSocketDatatype socket_type)
|
||||
{
|
||||
auto *storage = static_cast<NodeGeometryForEachOutput *>(node.storage);
|
||||
switch (GeometryNodeForEachMode(storage->mode)) {
|
||||
case GEO_NODE_FOR_EACH_MODE_INDEX:
|
||||
case GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT: {
|
||||
return socket_type_supports_fields(socket_type);
|
||||
}
|
||||
case GEO_NODE_FOR_EACH_MODE_INSTANCE: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static void init_with_socket_type_and_name(bNode &node,
|
||||
NodeForEachInputItem &item,
|
||||
const eNodeSocketDatatype socket_type,
|
||||
const char *name)
|
||||
{
|
||||
auto *storage = static_cast<NodeGeometryForEachOutput *>(node.storage);
|
||||
item.socket_type = socket_type;
|
||||
item.identifier = storage->input_next_identifier++;
|
||||
socket_items::set_item_name_and_make_unique<ForEachInputItemsAccessor>(node, item, name);
|
||||
}
|
||||
static std::string socket_identifier_for_item(const NodeForEachInputItem &item)
|
||||
{
|
||||
return "Item_" + std::to_string(item.identifier);
|
||||
}
|
||||
};
|
||||
|
||||
struct ForEachOutputItemsAccessor {
|
||||
using ItemT = NodeForEachOutputItem;
|
||||
static StructRNA *item_srna;
|
||||
static int node_type;
|
||||
static constexpr const char *node_idname = "GeometryNodeForEachOutput";
|
||||
|
||||
static socket_items::SocketItemsRef<NodeForEachOutputItem> get_items_from_node(bNode &node)
|
||||
{
|
||||
auto *storage = static_cast<NodeGeometryForEachOutput *>(node.storage);
|
||||
return {&storage->output_items, &storage->output_items_num, &storage->output_active_index};
|
||||
}
|
||||
static void copy_item(const NodeForEachOutputItem &src, NodeForEachOutputItem &dst)
|
||||
{
|
||||
dst = src;
|
||||
dst.name = BLI_strdup_null(dst.name);
|
||||
}
|
||||
static void destruct_item(NodeForEachOutputItem *item)
|
||||
{
|
||||
MEM_SAFE_FREE(item->name);
|
||||
}
|
||||
static void blend_write(BlendWriter *, const bNode &node);
|
||||
static void blend_read_data(BlendDataReader *reader, bNode &node);
|
||||
static short *get_socket_type(NodeForEachOutputItem &item)
|
||||
{
|
||||
return &item.socket_type;
|
||||
}
|
||||
static char **get_name(NodeForEachOutputItem &item)
|
||||
{
|
||||
return &item.name;
|
||||
}
|
||||
static bool supports_socket_type(const bNode &node, const eNodeSocketDatatype socket_type)
|
||||
{
|
||||
auto *storage = static_cast<NodeGeometryForEachOutput *>(node.storage);
|
||||
switch (GeometryNodeForEachMode(storage->mode)) {
|
||||
case GEO_NODE_FOR_EACH_MODE_INDEX:
|
||||
case GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT:
|
||||
case GEO_NODE_FOR_EACH_MODE_INSTANCE: {
|
||||
return socket_type == SOCK_GEOMETRY || socket_type_supports_fields(socket_type);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static void init_with_socket_type_and_name(bNode &node,
|
||||
NodeForEachOutputItem &item,
|
||||
const eNodeSocketDatatype socket_type,
|
||||
const char *name)
|
||||
{
|
||||
auto *storage = static_cast<NodeGeometryForEachOutput *>(node.storage);
|
||||
item.socket_type = socket_type;
|
||||
item.identifier = storage->output_next_identifier++;
|
||||
socket_items::set_item_name_and_make_unique<ForEachOutputItemsAccessor>(node, item, name);
|
||||
}
|
||||
static std::string socket_identifier_for_item(const NodeForEachOutputItem &item)
|
||||
{
|
||||
return "Item_" + std::to_string(item.identifier);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
|
|
@ -77,6 +77,8 @@ set(SRC
|
|||
nodes/node_geo_evaluate_on_domain.cc
|
||||
nodes/node_geo_extrude_mesh.cc
|
||||
nodes/node_geo_flip_faces.cc
|
||||
nodes/node_geo_foreach_input.cc
|
||||
nodes/node_geo_foreach_output.cc
|
||||
nodes/node_geo_geometry_to_instance.cc
|
||||
nodes/node_geo_image.cc
|
||||
nodes/node_geo_image_info.cc
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#endif
|
||||
|
||||
struct BVHTreeFromMesh;
|
||||
struct GeometrySet;
|
||||
namespace blender::nodes {
|
||||
class GatherAddNodeSearchParams;
|
||||
class GatherLinkSearchOpParams;
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "NOD_geometry.hh"
|
||||
#include "NOD_socket.hh"
|
||||
#include "NOD_zone_socket_items.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_foreach_input_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeGeometryForEachInput);
|
||||
|
||||
static void node_declare_dynamic(const bNodeTree &tree,
|
||||
const bNode &node,
|
||||
NodeDeclarationBuilder &b)
|
||||
{
|
||||
const NodeGeometryForEachInput &input_storage = node_storage(node);
|
||||
const bNode *output_node = tree.node_by_id(input_storage.output_node_id);
|
||||
if (output_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto &output_storage = *static_cast<const NodeGeometryForEachOutput *>(
|
||||
output_node->storage);
|
||||
const GeometryNodeForEachMode mode = GeometryNodeForEachMode(output_storage.mode);
|
||||
{
|
||||
/* Add standard inputs. */
|
||||
switch (mode) {
|
||||
case GEO_NODE_FOR_EACH_MODE_INDEX: {
|
||||
b.add_input<decl::Int>("Amount").min(0).default_value(1);
|
||||
b.add_output<decl::Int>("Index");
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT: {
|
||||
b.add_input<decl::Geometry>("Geometry");
|
||||
b.add_input<decl::Bool>("Selection").default_value(true).hide_value(true).supports_field();
|
||||
b.add_output<decl::Int>("Index");
|
||||
if (output_storage.domain != ATTR_DOMAIN_CORNER) {
|
||||
b.add_output<decl::Geometry>("Element");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_FOR_EACH_MODE_INSTANCE: {
|
||||
b.add_input<decl::Geometry>("Instances");
|
||||
b.add_output<decl::Geometry>("Geometry");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode == GEO_NODE_FOR_EACH_MODE_INSTANCE) {
|
||||
/* Other inputs are not allowed in this mode. */
|
||||
return;
|
||||
}
|
||||
/* Add dynamic sockets. */
|
||||
for (const int i : IndexRange(output_storage.input_items_num)) {
|
||||
const NodeForEachInputItem &item = output_storage.input_items[i];
|
||||
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
|
||||
const StringRef name = item.name;
|
||||
const std::string identifier = ForEachInputItemsAccessor::socket_identifier_for_item(item);
|
||||
auto &input_decl = b.add_input(socket_type, name, identifier);
|
||||
b.add_output(socket_type, name, identifier);
|
||||
if (socket_type_supports_fields(socket_type)) {
|
||||
input_decl.supports_field();
|
||||
}
|
||||
}
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_output<decl::Extend>("", "__extend__");
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
||||
{
|
||||
NodeGeometryForEachInput *data = MEM_cnew<NodeGeometryForEachInput>(__func__);
|
||||
/* Needs to be initialized for the node to work. */
|
||||
data->output_node_id = 0;
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static bool node_insert_link(bNodeTree *tree, bNode *node, bNodeLink *link)
|
||||
{
|
||||
const NodeGeometryForEachInput &input_storage = node_storage(*node);
|
||||
bNode *output_node = tree->node_by_id(input_storage.output_node_id);
|
||||
if (output_node == nullptr) {
|
||||
return true;
|
||||
}
|
||||
return socket_items::try_add_item_via_any_extend_socket<ForEachInputItemsAccessor>(
|
||||
*tree, *node, *output_node, *link);
|
||||
}
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree &tree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
|
||||
bNode &input_node = *static_cast<bNode *>(ptr->data);
|
||||
const NodeGeometryForEachInput &input_storage = node_storage(input_node);
|
||||
bNode *output_node = tree.node_by_id(input_storage.output_node_id);
|
||||
if (output_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto &output_storage = *static_cast<const NodeGeometryForEachOutput *>(
|
||||
output_node->storage);
|
||||
PointerRNA output_node_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Node, output_node);
|
||||
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
uiLayoutSetPropDecorate(layout, false);
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiItemR(col, &output_node_ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
|
||||
if (output_storage.mode == GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT) {
|
||||
uiItemR(col, &output_node_ptr, "domain", UI_ITEM_NONE, "Domain", ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
geo_node_type_base(&ntype, GEO_NODE_FOR_EACH_INPUT, "For-Each Input", NODE_CLASS_INTERFACE);
|
||||
ntype.initfunc = node_init;
|
||||
ntype.declare_dynamic = node_declare_dynamic;
|
||||
ntype.gather_link_search_ops = nullptr;
|
||||
ntype.insert_link = node_insert_link;
|
||||
ntype.draw_buttons = node_layout;
|
||||
node_type_storage(
|
||||
&ntype, "NodeGeometryForEachInput", node_free_standard_storage, node_copy_standard_storage);
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
NOD_REGISTER_NODE(node_register)
|
||||
|
||||
} // namespace blender::nodes::node_geo_foreach_input_cc
|
|
@ -0,0 +1,97 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "NOD_geometry.hh"
|
||||
#include "NOD_socket.hh"
|
||||
#include "NOD_zone_socket_items.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_foreach_output_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeGeometryForEachOutput);
|
||||
|
||||
static void node_declare_dynamic(const bNodeTree & /*node_tree*/,
|
||||
const bNode &node,
|
||||
NodeDeclarationBuilder &b)
|
||||
{
|
||||
const NodeGeometryForEachOutput &storage = node_storage(node);
|
||||
for (const int i : IndexRange(storage.output_items_num)) {
|
||||
const NodeForEachOutputItem &item = storage.output_items[i];
|
||||
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
|
||||
const std::string identifier = ForEachOutputItemsAccessor::socket_identifier_for_item(item);
|
||||
const StringRef name = item.name;
|
||||
|
||||
auto &input_decl = b.add_input(socket_type, name, identifier);
|
||||
auto &output_decl = b.add_output(socket_type, name, identifier);
|
||||
|
||||
if (socket_type_supports_fields(socket_type)) {
|
||||
input_decl.supports_field();
|
||||
output_decl.field_on_all();
|
||||
}
|
||||
}
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_output<decl::Extend>("", "__extend__");
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
||||
{
|
||||
NodeGeometryForEachOutput *data = MEM_cnew<NodeGeometryForEachOutput>(__func__);
|
||||
|
||||
data->output_items = MEM_cnew_array<NodeForEachOutputItem>(1, __func__);
|
||||
data->output_items_num = 1;
|
||||
|
||||
NodeForEachOutputItem &item = data->output_items[0];
|
||||
item.name = BLI_strdup(DATA_("Geometry"));
|
||||
item.socket_type = SOCK_GEOMETRY;
|
||||
item.identifier = data->output_next_identifier++;
|
||||
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static void node_free_storage(bNode *node)
|
||||
{
|
||||
socket_items::destruct_array<ForEachInputItemsAccessor>(*node);
|
||||
socket_items::destruct_array<ForEachOutputItemsAccessor>(*node);
|
||||
MEM_freeN(node->storage);
|
||||
}
|
||||
|
||||
static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
|
||||
{
|
||||
const NodeGeometryForEachOutput &src_storage = node_storage(*src_node);
|
||||
auto *dst_storage = MEM_new<NodeGeometryForEachOutput>(__func__, src_storage);
|
||||
dst_node->storage = dst_storage;
|
||||
|
||||
socket_items::copy_array<ForEachInputItemsAccessor>(*src_node, *dst_node);
|
||||
socket_items::copy_array<ForEachOutputItemsAccessor>(*src_node, *dst_node);
|
||||
}
|
||||
|
||||
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
|
||||
{
|
||||
return socket_items::try_add_item_via_any_extend_socket<ForEachOutputItemsAccessor>(
|
||||
*ntree, *node, *node, *link);
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
geo_node_type_base(&ntype, GEO_NODE_FOR_EACH_OUTPUT, "For-Each Output", NODE_CLASS_INTERFACE);
|
||||
ntype.initfunc = node_init;
|
||||
ntype.declare_dynamic = node_declare_dynamic;
|
||||
ntype.gather_link_search_ops = nullptr;
|
||||
ntype.insert_link = node_insert_link;
|
||||
node_type_storage(&ntype, "NodeGeometryForEachOutput", node_free_storage, node_copy_storage);
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
NOD_REGISTER_NODE(node_register)
|
||||
|
||||
} // namespace blender::nodes::node_geo_foreach_output_cc
|
|
@ -37,6 +37,8 @@
|
|||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "GEO_join_geometries.hh"
|
||||
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_node_tree_anonymous_attributes.hh"
|
||||
|
@ -1483,6 +1485,67 @@ struct RepeatEvalStorage {
|
|||
Vector<int> output_index_map;
|
||||
};
|
||||
|
||||
static void build_interface_for_zone_function(const bNodeTreeZone &zone,
|
||||
const ZoneBodyFunction &body_fn,
|
||||
ZoneBuildInfo &r_zone_info,
|
||||
Vector<lf::Input> &r_inputs,
|
||||
Vector<lf::Output> &r_outputs)
|
||||
{
|
||||
for (const bNodeSocket *socket : zone.input_node->input_sockets()) {
|
||||
if (socket->typeinfo->geometry_nodes_cpp_type == nullptr) {
|
||||
/* Skip "empty" sockets. */
|
||||
continue;
|
||||
}
|
||||
r_inputs.append_as(
|
||||
socket->name, *socket->typeinfo->geometry_nodes_cpp_type, lf::ValueUsage::Maybe);
|
||||
}
|
||||
r_zone_info.indices.inputs.main = r_inputs.index_range();
|
||||
|
||||
for (const bNodeLink *link : zone.border_links) {
|
||||
r_inputs.append_as(link->fromsock->name,
|
||||
*link->tosock->typeinfo->geometry_nodes_cpp_type,
|
||||
lf::ValueUsage::Maybe);
|
||||
}
|
||||
r_zone_info.indices.inputs.border_links = r_inputs.index_range().take_back(
|
||||
zone.border_links.size());
|
||||
|
||||
for (const bNodeSocket *socket : zone.output_node->output_sockets()) {
|
||||
if (socket->typeinfo->geometry_nodes_cpp_type == nullptr) {
|
||||
continue;
|
||||
}
|
||||
r_inputs.append_as("Usage", CPPType::get<bool>(), lf::ValueUsage::Maybe);
|
||||
r_outputs.append_as(socket->name, *socket->typeinfo->geometry_nodes_cpp_type);
|
||||
}
|
||||
r_zone_info.indices.outputs.main = r_outputs.index_range();
|
||||
r_zone_info.indices.inputs.output_usages = r_inputs.index_range().take_back(
|
||||
r_zone_info.indices.outputs.main.size());
|
||||
|
||||
for ([[maybe_unused]] const bNodeSocket *socket : zone.input_node->input_sockets()) {
|
||||
if (socket->typeinfo->geometry_nodes_cpp_type == nullptr) {
|
||||
continue;
|
||||
}
|
||||
r_outputs.append_as("Usage", CPPType::get<bool>());
|
||||
}
|
||||
r_zone_info.indices.outputs.input_usages = r_outputs.index_range().take_back(
|
||||
r_zone_info.indices.inputs.main.size());
|
||||
for ([[maybe_unused]] const bNodeLink *link : zone.border_links) {
|
||||
r_outputs.append_as("Border Link Usage", CPPType::get<bool>());
|
||||
}
|
||||
r_zone_info.indices.outputs.border_link_usages = r_outputs.index_range().take_back(
|
||||
zone.border_links.size());
|
||||
|
||||
for (const auto item : body_fn.indices.inputs.attributes_by_field_source_index.items()) {
|
||||
const int index = r_inputs.append_and_get_index_as(
|
||||
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe);
|
||||
r_zone_info.indices.inputs.attributes_by_field_source_index.add_new(item.key, index);
|
||||
}
|
||||
for (const auto item : body_fn.indices.inputs.attributes_by_caller_propagation_index.items()) {
|
||||
const int index = r_inputs.append_and_get_index_as(
|
||||
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe);
|
||||
r_zone_info.indices.inputs.attributes_by_caller_propagation_index.add_new(item.key, index);
|
||||
}
|
||||
}
|
||||
|
||||
class LazyFunctionForRepeatZone : public LazyFunction {
|
||||
private:
|
||||
const bNodeTreeZone &zone_;
|
||||
|
@ -1500,53 +1563,10 @@ class LazyFunctionForRepeatZone : public LazyFunction {
|
|||
body_fn_(body_fn)
|
||||
{
|
||||
debug_name_ = "Repeat Zone";
|
||||
build_interface_for_zone_function(zone, body_fn, zone_info, inputs_, outputs_);
|
||||
|
||||
inputs_.append_as("Iterations", CPPType::get<ValueOrField<int>>(), lf::ValueUsage::Used);
|
||||
for (const bNodeSocket *socket : zone.input_node->input_sockets().drop_front(1).drop_back(1)) {
|
||||
inputs_.append_as(
|
||||
socket->name, *socket->typeinfo->geometry_nodes_cpp_type, lf::ValueUsage::Maybe);
|
||||
}
|
||||
zone_info.indices.inputs.main = inputs_.index_range();
|
||||
|
||||
for (const bNodeLink *link : zone.border_links) {
|
||||
inputs_.append_as(link->fromsock->name,
|
||||
*link->tosock->typeinfo->geometry_nodes_cpp_type,
|
||||
lf::ValueUsage::Maybe);
|
||||
}
|
||||
zone_info.indices.inputs.border_links = inputs_.index_range().take_back(
|
||||
zone.border_links.size());
|
||||
|
||||
for (const bNodeSocket *socket : zone.output_node->output_sockets().drop_back(1)) {
|
||||
inputs_.append_as("Usage", CPPType::get<bool>(), lf::ValueUsage::Maybe);
|
||||
outputs_.append_as(socket->name, *socket->typeinfo->geometry_nodes_cpp_type);
|
||||
}
|
||||
zone_info.indices.inputs.output_usages = inputs_.index_range().take_back(
|
||||
zone.output_node->output_sockets().drop_back(1).size());
|
||||
zone_info.indices.outputs.main = outputs_.index_range();
|
||||
|
||||
for ([[maybe_unused]] const bNodeSocket *socket :
|
||||
zone.input_node->input_sockets().drop_back(1)) {
|
||||
outputs_.append_as("Usage", CPPType::get<bool>());
|
||||
}
|
||||
zone_info.indices.outputs.input_usages = outputs_.index_range().take_back(
|
||||
zone.input_node->input_sockets().drop_back(1).size());
|
||||
for ([[maybe_unused]] const bNodeLink *link : zone.border_links) {
|
||||
outputs_.append_as("Border Link Usage", CPPType::get<bool>());
|
||||
}
|
||||
zone_info.indices.outputs.border_link_usages = outputs_.index_range().take_back(
|
||||
zone.border_links.size());
|
||||
|
||||
for (const auto item : body_fn_.indices.inputs.attributes_by_field_source_index.items()) {
|
||||
const int index = inputs_.append_and_get_index_as(
|
||||
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe);
|
||||
zone_info.indices.inputs.attributes_by_field_source_index.add_new(item.key, index);
|
||||
}
|
||||
for (const auto item : body_fn_.indices.inputs.attributes_by_caller_propagation_index.items())
|
||||
{
|
||||
const int index = inputs_.append_and_get_index_as(
|
||||
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe);
|
||||
zone_info.indices.inputs.attributes_by_caller_propagation_index.add_new(item.key, index);
|
||||
}
|
||||
/* Iterations input is always used. */
|
||||
inputs_[zone_info.indices.inputs.main[0]].usage = lf::ValueUsage::Used;
|
||||
}
|
||||
|
||||
void *init_storage(LinearAllocator<> &allocator) const override
|
||||
|
@ -1806,6 +1826,241 @@ class LazyFunctionForRepeatZone : public LazyFunction {
|
|||
}
|
||||
};
|
||||
|
||||
class LazyFunctionForForEachSplit : public lf::LazyFunction {
|
||||
private:
|
||||
const int amount_;
|
||||
|
||||
public:
|
||||
LazyFunctionForForEachSplit(const int amount) : amount_(amount)
|
||||
{
|
||||
debug_name_ = "For-Each Split";
|
||||
const CPPType &type = CPPType::get<ValueOrField<int>>();
|
||||
outputs_.resize(amount, lf::Output("Index", type));
|
||||
}
|
||||
|
||||
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
||||
{
|
||||
for (const int i : IndexRange(amount_)) {
|
||||
params.set_output(i, ValueOrField<int>(i));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class LazyFunctionForForEachReduce : public lf::LazyFunction {
|
||||
private:
|
||||
const int amount_;
|
||||
|
||||
public:
|
||||
LazyFunctionForForEachReduce(const int amount) : amount_(amount)
|
||||
{
|
||||
debug_name_ = "For-Each Reduce";
|
||||
const CPPType &geo_cpp_type = CPPType::get<GeometrySet>();
|
||||
for ([[maybe_unused]] const int i : IndexRange(amount)) {
|
||||
inputs_.append_as("Geometry", geo_cpp_type);
|
||||
}
|
||||
outputs_.append_as("Geometry", geo_cpp_type);
|
||||
}
|
||||
|
||||
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
||||
{
|
||||
Vector<GeometrySet> geometries(amount_);
|
||||
for (const int i : IndexRange(amount_)) {
|
||||
geometries[i] = params.extract_input<GeometrySet>(i);
|
||||
}
|
||||
GeometrySet joined_geometries = geometry::join_geometries(geometries, {});
|
||||
params.set_output(0, std::move(joined_geometries));
|
||||
}
|
||||
};
|
||||
|
||||
struct ForEachEvalStorage {
|
||||
LinearAllocator<> allocator;
|
||||
lf::Graph graph;
|
||||
std::optional<lf::GraphExecutor> graph_executor;
|
||||
std::optional<LazyFunctionForForEachSplit> split_fn;
|
||||
std::optional<LazyFunctionForForEachReduce> reduce_fn;
|
||||
std::optional<LazyFunctionForLogicalOr> or_fn;
|
||||
void *graph_executor_storage = nullptr;
|
||||
Vector<int> input_index_map;
|
||||
Vector<int> output_index_map;
|
||||
bool multi_threading_enabled = false;
|
||||
};
|
||||
|
||||
class LazyFunctionForForeachZone : public LazyFunction {
|
||||
private:
|
||||
const bNodeTreeZone &zone_;
|
||||
const ZoneBuildInfo &zone_info_;
|
||||
const ZoneBodyFunction &body_fn_;
|
||||
|
||||
public:
|
||||
LazyFunctionForForeachZone(const bNodeTreeZone &zone,
|
||||
ZoneBuildInfo &zone_info,
|
||||
const ZoneBodyFunction &body_fn)
|
||||
: zone_(zone), zone_info_(zone_info), body_fn_(body_fn)
|
||||
{
|
||||
debug_name_ = "For-Each Zone";
|
||||
build_interface_for_zone_function(zone, body_fn, zone_info, inputs_, outputs_);
|
||||
/* The Amount input is always used. */
|
||||
inputs_[zone_info.indices.inputs.main[0]].usage = lf::ValueUsage::Used;
|
||||
}
|
||||
|
||||
void *init_storage(LinearAllocator<> &allocator) const override
|
||||
{
|
||||
return allocator.construct<ForEachEvalStorage>().release();
|
||||
}
|
||||
void destruct_storage(void *storage) const override
|
||||
{
|
||||
ForEachEvalStorage *s = static_cast<ForEachEvalStorage *>(storage);
|
||||
if (s->graph_executor_storage) {
|
||||
s->graph_executor->destruct_storage(s->graph_executor_storage);
|
||||
}
|
||||
std::destroy_at(s);
|
||||
}
|
||||
|
||||
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
||||
{
|
||||
auto &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
||||
auto &local_user_data = *static_cast<GeoNodesLFLocalUserData *>(context.local_user_data);
|
||||
|
||||
const auto &node_storage = *static_cast<const NodeGeometryForEachOutput *>(
|
||||
zone_.output_node->storage);
|
||||
ForEachEvalStorage &eval_storage = *static_cast<ForEachEvalStorage *>(context.storage);
|
||||
|
||||
const int iteration_usage_index = zone_info_.indices.outputs.input_usages[0];
|
||||
if (!params.output_was_set(iteration_usage_index)) {
|
||||
params.set_output(iteration_usage_index, true);
|
||||
}
|
||||
|
||||
if (!eval_storage.graph_executor) {
|
||||
this->initialize_execution_graph(
|
||||
params, eval_storage, node_storage, user_data, local_user_data);
|
||||
}
|
||||
|
||||
lf::RemappedParams eval_graph_params{*eval_storage.graph_executor,
|
||||
params,
|
||||
eval_storage.input_index_map,
|
||||
eval_storage.output_index_map,
|
||||
eval_storage.multi_threading_enabled};
|
||||
lf::Context eval_graph_context{
|
||||
eval_storage.graph_executor_storage, context.user_data, context.local_user_data};
|
||||
eval_storage.graph_executor->execute(eval_graph_params, eval_graph_context);
|
||||
}
|
||||
|
||||
void initialize_execution_graph(lf::Params ¶ms,
|
||||
ForEachEvalStorage &eval_storage,
|
||||
const NodeGeometryForEachOutput &node_storage,
|
||||
GeoNodesLFUserData &user_data,
|
||||
GeoNodesLFLocalUserData &local_user_data) const
|
||||
{
|
||||
UNUSED_VARS(params, eval_storage, node_storage, user_data, local_user_data);
|
||||
|
||||
const int amount = params.get_input<ValueOrField<int>>(0).as_value();
|
||||
const int main_outputs_num = node_storage.output_items_num;
|
||||
const int border_links_num = zone_.border_links.size();
|
||||
|
||||
lf::Graph &lf_graph = eval_storage.graph;
|
||||
|
||||
for (const int i : inputs_.index_range()) {
|
||||
const lf::Input &input = inputs_[i];
|
||||
lf_graph.add_input(*input.type, input.debug_name);
|
||||
}
|
||||
for (const int i : outputs_.index_range()) {
|
||||
const lf::Output &output = outputs_[i];
|
||||
lf_graph.add_output(*output.type, output.debug_name);
|
||||
}
|
||||
|
||||
VectorSet<lf::FunctionNode *> lf_body_nodes;
|
||||
for ([[maybe_unused]] const int i : IndexRange(amount)) {
|
||||
lf::FunctionNode &lf_node = lf_graph.add_function(*body_fn_.function);
|
||||
lf_body_nodes.add_new(&lf_node);
|
||||
}
|
||||
|
||||
eval_storage.split_fn.emplace(amount);
|
||||
lf::FunctionNode &lf_split_node = lf_graph.add_function(*eval_storage.split_fn);
|
||||
|
||||
eval_storage.reduce_fn.emplace(amount);
|
||||
lf::FunctionNode &lf_reduce_node = lf_graph.add_function(*eval_storage.reduce_fn);
|
||||
|
||||
for (const int node_i : IndexRange(amount)) {
|
||||
lf::FunctionNode &lf_body_node = *lf_body_nodes[node_i];
|
||||
lf_graph.add_link(lf_split_node.output(node_i),
|
||||
lf_body_node.input(body_fn_.indices.inputs.main[0]));
|
||||
lf_graph.add_link(lf_body_node.output(body_fn_.indices.outputs.main[0]),
|
||||
lf_reduce_node.input(node_i));
|
||||
|
||||
for (const int main_output_i : IndexRange(main_outputs_num)) {
|
||||
lf_graph.add_link(
|
||||
*lf_graph.graph_inputs()[zone_info_.indices.inputs.output_usages[main_output_i]],
|
||||
lf_body_node.input(body_fn_.indices.inputs.output_usages[main_output_i]));
|
||||
}
|
||||
for (const int border_link_i : IndexRange(border_links_num)) {
|
||||
lf_graph.add_link(
|
||||
*lf_graph.graph_inputs()[zone_info_.indices.inputs.border_links[border_link_i]],
|
||||
lf_body_node.input(body_fn_.indices.inputs.border_links[border_link_i]));
|
||||
}
|
||||
}
|
||||
|
||||
static bool static_true = true;
|
||||
for (const int i : zone_info_.indices.outputs.border_link_usages) {
|
||||
lf_graph.graph_outputs()[i]->set_default_value(&static_true);
|
||||
}
|
||||
for (const int i : zone_info_.indices.outputs.input_usages) {
|
||||
lf_graph.graph_outputs()[i]->set_default_value(&static_true);
|
||||
}
|
||||
|
||||
lf_graph.add_link(lf_reduce_node.output(0), *lf_graph.graph_outputs()[0]);
|
||||
|
||||
eval_storage.input_index_map.reinitialize(inputs_.size());
|
||||
array_utils::fill_index_range<int>(eval_storage.input_index_map);
|
||||
eval_storage.output_index_map.reinitialize(outputs_.size());
|
||||
array_utils::fill_index_range<int>(eval_storage.output_index_map);
|
||||
|
||||
eval_storage.input_index_map.remove(zone_info_.indices.inputs.main[0]);
|
||||
eval_storage.output_index_map.remove(zone_info_.indices.outputs.input_usages[0]);
|
||||
|
||||
Vector<const lf::GraphInputSocket *> lf_graph_inputs;
|
||||
Vector<const lf::GraphOutputSocket *> lf_graph_outputs;
|
||||
for (const int i : eval_storage.input_index_map) {
|
||||
lf_graph_inputs.append(lf_graph.graph_inputs()[i]);
|
||||
}
|
||||
for (const int i : eval_storage.output_index_map) {
|
||||
lf_graph_outputs.append(lf_graph.graph_outputs()[i]);
|
||||
}
|
||||
|
||||
lf_graph.update_node_indices();
|
||||
|
||||
eval_storage.graph_executor.emplace(lf_graph,
|
||||
std::move(lf_graph_inputs),
|
||||
std::move(lf_graph_outputs),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
eval_storage.graph_executor_storage = eval_storage.graph_executor->init_storage(
|
||||
eval_storage.allocator);
|
||||
|
||||
std::cout << "\n\n" << lf_graph.to_dot() << "\n\n";
|
||||
}
|
||||
|
||||
std::string input_name(const int i) const override
|
||||
{
|
||||
if (zone_info_.indices.inputs.output_usages.contains(i)) {
|
||||
const bNodeSocket &bsocket = zone_.output_node->output_socket(
|
||||
i - zone_info_.indices.inputs.output_usages.first());
|
||||
return "Usage: " + StringRef(bsocket.name);
|
||||
}
|
||||
return inputs_[i].debug_name;
|
||||
}
|
||||
|
||||
std::string output_name(const int i) const override
|
||||
{
|
||||
if (zone_info_.indices.outputs.input_usages.contains(i)) {
|
||||
const bNodeSocket &bsocket = zone_.input_node->input_socket(
|
||||
i - zone_info_.indices.outputs.input_usages.first());
|
||||
return "Usage: " + StringRef(bsocket.name);
|
||||
}
|
||||
return outputs_[i].debug_name;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Logs intermediate values from the lazy-function graph evaluation into #GeoModifierLog based on
|
||||
* the mapping between the lazy-function graph and the corresponding #bNodeTree.
|
||||
|
@ -2063,6 +2318,10 @@ struct GeometryNodesLazyFunctionBuilder {
|
|||
this->build_repeat_zone_function(zone);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_FOR_EACH_OUTPUT: {
|
||||
this->build_foreach_zone_function(zone);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
|
@ -2257,6 +2516,14 @@ struct GeometryNodesLazyFunctionBuilder {
|
|||
zone_info.lazy_function = &zone_fn;
|
||||
}
|
||||
|
||||
void build_foreach_zone_function(const bNodeTreeZone &zone)
|
||||
{
|
||||
ZoneBuildInfo &zone_info = zone_build_infos_[zone.index];
|
||||
ZoneBodyFunction &body_fn = this->build_zone_body_function(zone);
|
||||
auto &zone_fn = scope_.construct<LazyFunctionForForeachZone>(zone, zone_info, body_fn);
|
||||
zone_info.lazy_function = &zone_fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lazy-function for the "body" of a zone, i.e. for all the nodes within the zone.
|
||||
*/
|
||||
|
@ -2273,7 +2540,11 @@ struct GeometryNodesLazyFunctionBuilder {
|
|||
Vector<lf::GraphInputSocket *> lf_main_inputs;
|
||||
Vector<lf::GraphOutputSocket *> lf_main_input_usages;
|
||||
|
||||
for (const bNodeSocket *bsocket : zone.input_node->output_sockets().drop_back(1)) {
|
||||
for (const bNodeSocket *bsocket : zone.input_node->output_sockets()) {
|
||||
if (bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) {
|
||||
/* Skip empty sockets. */
|
||||
continue;
|
||||
}
|
||||
lf::GraphInputSocket &lf_input = lf_body_graph.add_input(
|
||||
*bsocket->typeinfo->geometry_nodes_cpp_type, bsocket->name);
|
||||
lf::GraphOutputSocket &lf_input_usage = lf_body_graph.add_output(
|
||||
|
@ -2293,7 +2564,11 @@ struct GeometryNodesLazyFunctionBuilder {
|
|||
Vector<lf::GraphOutputSocket *> lf_main_outputs;
|
||||
Vector<lf::GraphInputSocket *> lf_main_output_usages;
|
||||
|
||||
for (const bNodeSocket *bsocket : zone.output_node->input_sockets().drop_back(1)) {
|
||||
for (const bNodeSocket *bsocket : zone.output_node->input_sockets()) {
|
||||
if (bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) {
|
||||
/* Skip empty sockets. */
|
||||
continue;
|
||||
}
|
||||
lf::GraphOutputSocket &lf_output = lf_body_graph.add_output(
|
||||
*bsocket->typeinfo->geometry_nodes_cpp_type, bsocket->name);
|
||||
lf::GraphInputSocket &lf_output_usage = lf_body_graph.add_input(
|
||||
|
@ -2326,10 +2601,14 @@ struct GeometryNodesLazyFunctionBuilder {
|
|||
this->insert_nodes_and_zones(zone.child_nodes, zone.child_zones, graph_params);
|
||||
|
||||
this->build_output_socket_usages(*zone.input_node, graph_params);
|
||||
for (const int i : zone.input_node->output_sockets().drop_back(1).index_range()) {
|
||||
const bNodeSocket &bsocket = zone.input_node->output_socket(i);
|
||||
lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(&bsocket, nullptr);
|
||||
lf::GraphOutputSocket &lf_usage_output = *lf_main_input_usages[i];
|
||||
int lf_input_index = 0;
|
||||
for (const bNodeSocket *bsocket : zone.input_node->output_sockets()) {
|
||||
if (bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) {
|
||||
/* Skip empty socket. */
|
||||
continue;
|
||||
}
|
||||
lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(bsocket, nullptr);
|
||||
lf::GraphOutputSocket &lf_usage_output = *lf_main_input_usages[lf_input_index];
|
||||
if (lf_usage) {
|
||||
lf_body_graph.add_link(*lf_usage, lf_usage_output);
|
||||
}
|
||||
|
@ -2337,6 +2616,7 @@ struct GeometryNodesLazyFunctionBuilder {
|
|||
static const bool static_false = false;
|
||||
lf_usage_output.set_default_value(&static_false);
|
||||
}
|
||||
lf_input_index++;
|
||||
}
|
||||
|
||||
for (const auto item : graph_params.lf_output_by_bsocket.items()) {
|
||||
|
@ -2886,6 +3166,7 @@ struct GeometryNodesLazyFunctionBuilder {
|
|||
*child_zone_info.lazy_function);
|
||||
mapping_->zone_node_map.add_new(&child_zone, &child_zone_node);
|
||||
|
||||
/* TODO: Handle "empty" sockets that are not at the end. */
|
||||
for (const int i : child_zone_info.indices.inputs.main.index_range()) {
|
||||
const bNodeSocket &bsocket = child_zone.input_node->input_socket(i);
|
||||
lf::InputSocket &lf_input_socket = child_zone_node.input(
|
||||
|
|
|
@ -88,12 +88,32 @@ class RepeatZoneType : public blender::bke::bNodeZoneType {
|
|||
}
|
||||
};
|
||||
|
||||
class ForEachZoneType : public blender::bke::bNodeZoneType {
|
||||
public:
|
||||
ForEachZoneType()
|
||||
{
|
||||
this->input_idname = "GeometryNodeForEachInput";
|
||||
this->output_idname = "GeometryNodeForEachOutput";
|
||||
this->input_type = GEO_NODE_FOR_EACH_INPUT;
|
||||
this->output_type = GEO_NODE_FOR_EACH_OUTPUT;
|
||||
this->theme_id = TH_NODE_ZONE_FOR_EACH;
|
||||
}
|
||||
|
||||
const int &get_corresponding_output_id(const bNode &input_bnode) const override
|
||||
{
|
||||
BLI_assert(input_bnode.type == this->input_type);
|
||||
return static_cast<NodeGeometryForEachInput *>(input_bnode.storage)->output_node_id;
|
||||
}
|
||||
};
|
||||
|
||||
static void register_zone_types()
|
||||
{
|
||||
static SimulationZoneType simulation_zone_type;
|
||||
static RepeatZoneType repeat_zone_type;
|
||||
static ForEachZoneType foreach_zone_type;
|
||||
blender::bke::register_node_zone_type(simulation_zone_type);
|
||||
blender::bke::register_node_zone_type(repeat_zone_type);
|
||||
blender::bke::register_node_zone_type(foreach_zone_type);
|
||||
}
|
||||
|
||||
void register_nodes()
|
||||
|
|
|
@ -56,4 +56,48 @@ void RepeatItemsAccessor::blend_read_data(BlendDataReader *reader, bNode &node)
|
|||
}
|
||||
}
|
||||
|
||||
StructRNA *ForEachInputItemsAccessor::item_srna = &RNA_ForEachInputItem;
|
||||
int ForEachInputItemsAccessor::node_type = GEO_NODE_FOR_EACH_OUTPUT;
|
||||
|
||||
void ForEachInputItemsAccessor::blend_write(BlendWriter *writer, const bNode &node)
|
||||
{
|
||||
const auto &storage = *static_cast<const NodeGeometryForEachOutput *>(node.storage);
|
||||
BLO_write_struct_array(
|
||||
writer, NodeForEachInputItem, storage.input_items_num, storage.input_items);
|
||||
for (const NodeForEachInputItem &item : Span(storage.input_items, storage.input_items_num)) {
|
||||
BLO_write_string(writer, item.name);
|
||||
}
|
||||
}
|
||||
|
||||
void ForEachInputItemsAccessor::blend_read_data(BlendDataReader *reader, bNode &node)
|
||||
{
|
||||
auto &storage = *static_cast<NodeGeometryForEachOutput *>(node.storage);
|
||||
BLO_read_data_address(reader, &storage.input_items);
|
||||
for (const NodeForEachInputItem &item : Span(storage.input_items, storage.input_items_num)) {
|
||||
BLO_read_data_address(reader, &item.name);
|
||||
}
|
||||
}
|
||||
|
||||
StructRNA *ForEachOutputItemsAccessor::item_srna = &RNA_ForEachOutputItem;
|
||||
int ForEachOutputItemsAccessor::node_type = GEO_NODE_REPEAT_OUTPUT;
|
||||
|
||||
void ForEachOutputItemsAccessor::blend_write(BlendWriter *writer, const bNode &node)
|
||||
{
|
||||
const auto &storage = *static_cast<const NodeGeometryForEachOutput *>(node.storage);
|
||||
BLO_write_struct_array(
|
||||
writer, NodeForEachOutputItem, storage.output_items_num, storage.output_items);
|
||||
for (const NodeForEachOutputItem &item : Span(storage.output_items, storage.output_items_num)) {
|
||||
BLO_write_string(writer, item.name);
|
||||
}
|
||||
}
|
||||
|
||||
void ForEachOutputItemsAccessor::blend_read_data(BlendDataReader *reader, bNode &node)
|
||||
{
|
||||
auto &storage = *static_cast<NodeGeometryForEachOutput *>(node.storage);
|
||||
BLO_read_data_address(reader, &storage.output_items);
|
||||
for (const NodeForEachOutputItem &item : Span(storage.output_items, storage.output_items_num)) {
|
||||
BLO_read_data_address(reader, &item.name);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
|
Loading…
Reference in New Issue