WIP: Geometry Nodes: For Each Group / For Elements #112456
|
@ -209,6 +209,16 @@ class NODE_OT_add_repeat_zone(NodeAddZoneOperator, Operator):
|
|||
output_node_type = "GeometryNodeRepeatOutput"
|
||||
|
||||
|
||||
class NODE_OT_add_for_each_group_zone(NodeAddZoneOperator, Operator):
|
||||
"""Add a repeat zone that allows executing nodes a dynamic number of times"""
|
||||
bl_idname = "node.add_for_each_group_zone"
|
||||
bl_label = "Add For Each Group Zone"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
input_node_type = "GeometryNodeForEachGroupInput"
|
||||
output_node_type = "GeometryNodeForEachGroupOutput"
|
||||
|
||||
|
||||
class NODE_OT_collapse_hide_unused_toggle(Operator):
|
||||
"""Toggle collapsed nodes and hide unused sockets"""
|
||||
bl_idname = "node.collapse_hide_unused_toggle"
|
||||
|
@ -372,6 +382,7 @@ classes = (
|
|||
NODE_OT_add_node,
|
||||
NODE_OT_add_simulation_zone,
|
||||
NODE_OT_add_repeat_zone,
|
||||
NODE_OT_add_for_each_group_zone,
|
||||
NODE_OT_collapse_hide_unused_toggle,
|
||||
NODE_OT_interface_item_new,
|
||||
NODE_OT_interface_item_duplicate,
|
||||
|
|
|
@ -75,6 +75,11 @@ def add_repeat_zone(layout, label):
|
|||
return props
|
||||
|
||||
|
||||
def add_for_each_group_zone(layout, label):
|
||||
props = layout.operator("node.add_for_each_group_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"
|
||||
bl_label = "Layout"
|
||||
|
|
|
@ -169,6 +169,7 @@ class NODE_MT_geometry_node_GEO_GEOMETRY(Menu):
|
|||
layout.separator()
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeGeometryToInstance")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeJoinGeometry")
|
||||
node_add_menu.add_for_each_group_zone(layout, label="For Each Group Zone")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
|
|
|
@ -1306,6 +1306,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_GROUP_INPUT 2116
|
||||
#define GEO_NODE_FOR_EACH_GROUP_OUTPUT 2117
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -606,6 +606,13 @@ class NodeTreeMainUpdater {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (node.type == GEO_NODE_FOR_EACH_GROUP_INPUT) {
|
||||
if (const bNode *output_node = ntree.node_by_id(reinterpret_cast<const int &>(node.custom3))) {
|
||||
if (output_node->runtime->changed_flag & NTREE_CHANGED_NODE_PROPERTY) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ static Vector<std::unique_ptr<bNodeTreeZone>> find_zone_nodes(
|
|||
Vector<const bNode *> zone_output_nodes;
|
||||
zone_output_nodes.extend(tree.nodes_by_type("GeometryNodeSimulationOutput"));
|
||||
zone_output_nodes.extend(tree.nodes_by_type("GeometryNodeRepeatOutput"));
|
||||
zone_output_nodes.extend(tree.nodes_by_type("GeometryNodeForEachGroupOutput"));
|
||||
for (const bNode *node : zone_output_nodes) {
|
||||
auto zone = std::make_unique<bNodeTreeZone>();
|
||||
zone->owner = &owner;
|
||||
|
@ -62,6 +63,15 @@ static Vector<std::unique_ptr<bNodeTreeZone>> find_zone_nodes(
|
|||
}
|
||||
}
|
||||
}
|
||||
for (const bNode *node : tree.nodes_by_type("GeometryNodeForEachGroupInput")) {
|
||||
printf(">> %d;\n", reinterpret_cast<const int &>(node->custom3));
|
||||
if (const bNode *feg_output_node = tree.node_by_id(reinterpret_cast<const int &>(node->custom3))) {
|
||||
if (bNodeTreeZone *zone = r_zone_by_inout_node.lookup_default(feg_output_node, nullptr)) {
|
||||
zone->input_node = node;
|
||||
r_zone_by_inout_node.add(node, zone);
|
||||
}
|
||||
}
|
||||
}
|
||||
return zones;
|
||||
}
|
||||
|
||||
|
@ -257,13 +267,13 @@ static std::unique_ptr<bNodeTreeZones> discover_tree_zones(const bNodeTree &tree
|
|||
depend_on_output_flags |= depend_on_output_flag_array[from_node_i];
|
||||
}
|
||||
}
|
||||
if (ELEM(node->type, GEO_NODE_SIMULATION_INPUT, GEO_NODE_REPEAT_INPUT)) {
|
||||
if (ELEM(node->type, GEO_NODE_SIMULATION_INPUT, GEO_NODE_REPEAT_INPUT, GEO_NODE_FOR_EACH_GROUP_INPUT)) {
|
||||
if (const bNodeTreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) {
|
||||
/* Now entering a zone, so set the corresponding bit. */
|
||||
depend_on_input_flags[zone->index].set();
|
||||
}
|
||||
}
|
||||
else if (ELEM(node->type, GEO_NODE_SIMULATION_OUTPUT, GEO_NODE_REPEAT_OUTPUT)) {
|
||||
else if (ELEM(node->type, GEO_NODE_SIMULATION_OUTPUT, GEO_NODE_REPEAT_OUTPUT, GEO_NODE_FOR_EACH_GROUP_OUTPUT)) {
|
||||
if (const bNodeTreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) {
|
||||
/* The output is implicitly linked to the input, so also propagate the bits from there. */
|
||||
if (const bNode *zone_input_node = zone->input_node) {
|
||||
|
@ -349,6 +359,17 @@ static std::unique_ptr<bNodeTreeZones> discover_tree_zones(const bNodeTree &tree
|
|||
tree_zones->zones[zone_i]->child_nodes.append(node);
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
for (std::unique_ptr<bNodeTreeZone> &zone : tree_zones->zones) {
|
||||
if (const bNode *zone_input_node = zone->input_node) {
|
||||
std::cout << zone.get()->index << ": " << zone_input_node->name << std::endl;
|
||||
}
|
||||
if (const bNode *zone_input_node = zone->output_node) {
|
||||
std::cout << zone.get()->index << ": " << zone_input_node->name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
update_zone_border_links(tree, *tree_zones);
|
||||
|
||||
return tree_zones;
|
||||
|
|
|
@ -3237,6 +3237,36 @@ static PointerRNA rna_NodeGeometrySimulationInput_paired_output_get(PointerRNA *
|
|||
return r_ptr;
|
||||
}
|
||||
|
||||
static PointerRNA rna_NodeGeometryForEachGroupInput_paired_output_get(PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
|
||||
bNode *node = static_cast<bNode *>(ptr->data);
|
||||
bNode *output_node = ntree->node_by_id(reinterpret_cast<const int &>(node->custom3));
|
||||
return RNA_pointer_create(&ntree->id, &RNA_Node, output_node);
|
||||
}
|
||||
|
||||
static bool rna_GeometryNodeForEachGroupInput_pair_with_output(ID *id, bNode *node, bContext *C, ReportList *reports, bNode *output_node)
|
||||
{
|
||||
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
|
||||
|
||||
if (!NOD_geometry_for_each_group_input_pair_with_output(ntree, node, output_node)) {
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR,
|
||||
"Failed to pair for each group input node %s with output node %s",
|
||||
node->name,
|
||||
output_node->name);
|
||||
std::cout << __func__ << __LINE__ << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << ">> >> >> >> " << reinterpret_cast<const int &>(node->custom3) << std::endl;
|
||||
|
||||
BKE_ntree_update_tag_node_property(ntree, node);
|
||||
ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
|
||||
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
|
||||
return true;
|
||||
}
|
||||
|
||||
static PointerRNA rna_NodeGeometryRepeatInput_paired_output_get(PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
|
||||
|
@ -8876,6 +8906,49 @@ static void def_geo_simulation_input(StructRNA *srna)
|
|||
RNA_def_function_return(func, parm);
|
||||
}
|
||||
|
||||
static void def_geo_for_each_group_input(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
FunctionRNA *func;
|
||||
PropertyRNA *parm;
|
||||
|
||||
static const EnumPropertyItem mode_items[] = {
|
||||
{GEO_NODE_DELETE_GEOMETRY_MODE_ALL, "ALL", 0, "All", ""},
|
||||
{GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE, "EDGE_FACE", 0, "Only Edges & Faces", ""},
|
||||
{GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE, "ONLY_FACE", 0, "Only Faces", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "custom1");
|
||||
RNA_def_property_enum_items(prop, mode_items);
|
||||
RNA_def_property_enum_default(prop, GEO_NODE_DELETE_GEOMETRY_MODE_ALL);
|
||||
RNA_def_property_ui_text(prop, "Mode", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "custom2");
|
||||
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_without_corner_items);
|
||||
RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
|
||||
RNA_def_property_ui_text(prop, "Domain", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "paired_output", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "Node");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_pointer_funcs(prop, "rna_NodeGeometryForEachGroupInput_paired_output_get", nullptr, nullptr, nullptr);
|
||||
RNA_def_property_ui_text(prop, "Paired Output", "Repeat output node that this input node is paired with");
|
||||
|
||||
func = RNA_def_function(srna, "pair_with_output", "rna_GeometryNodeForEachGroupInput_pair_with_output");
|
||||
RNA_def_function_ui_description(func, "Pair a repeat input node with an output node.");
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS | FUNC_USE_CONTEXT);
|
||||
parm = RNA_def_pointer(func, "output_node", "GeometryNode", "Output Node", "Repeat output node to pair with");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
/* return value */
|
||||
parm = RNA_def_boolean(func, "result", false, "Result", "True if pairing the node was successful");
|
||||
RNA_def_function_return(func, parm);
|
||||
}
|
||||
|
||||
static void def_geo_repeat_input(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
|
|
@ -28,7 +28,9 @@ bool NOD_geometry_simulation_input_pair_with_output(const bNodeTree *node_tree,
|
|||
bool NOD_geometry_repeat_input_pair_with_output(const bNodeTree *node_tree,
|
||||
bNode *repeat_input_node,
|
||||
const bNode *repeat_output_node);
|
||||
|
||||
bool NOD_geometry_for_each_group_input_pair_with_output(const bNodeTree *node_tree,
|
||||
bNode *repeat_input_node,
|
||||
const bNode *repeat_output_node);
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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_GROUP_INPUT, def_geo_for_each_group_input, "FOR_EACH_GROUP_INPUT", ForEachGroupInput, "For Each Group Input", "")
|
||||
DefNode(GeometryNode, GEO_NODE_FOR_EACH_GROUP_OUTPUT, 0, "FOR_EACH_GROUP_OUTPUT", ForEachGroupOutput, "For Each Group 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")
|
||||
|
|
|
@ -76,6 +76,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_for_each_group_input.cc
|
||||
nodes/node_geo_for_each_group_output.cc
|
||||
nodes/node_geo_geometry_to_instance.cc
|
||||
nodes/node_geo_image.cc
|
||||
nodes/node_geo_image_info.cc
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/* 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.h"
|
||||
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "NOD_geometry.hh"
|
||||
#include "NOD_socket.hh"
|
||||
#include "NOD_rna_define.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_for_each_group_input_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Geometry>("Geometry");
|
||||
b.add_input<decl::Int>("Group ID").hide_value().field_on_all();
|
||||
|
||||
b.add_output<decl::Geometry>("Group Part").propagate_all();
|
||||
b.add_output<decl::Int>("Group Value");
|
||||
}
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
const bNode *node = static_cast<bNode *>(ptr->data);
|
||||
printf(">>++ %d;\n", reinterpret_cast<const int &>(node->custom3));
|
||||
uiItemR(layout, ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
|
||||
if (ELEM(eAttrDomain(node->custom2), ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE)) {
|
||||
uiItemR(layout, ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = GEO_NODE_DELETE_GEOMETRY_MODE_ALL;
|
||||
node->custom2 = ATTR_DOMAIN_POINT;
|
||||
reinterpret_cast<int &>(node->custom3) = 0;
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
geo_node_type_base(&ntype, GEO_NODE_FOR_EACH_GROUP_INPUT, "For Each Group Input", NODE_CLASS_INTERFACE);
|
||||
ntype.initfunc = node_init;
|
||||
ntype.declare = node_declare;
|
||||
ntype.draw_buttons = node_layout;
|
||||
ntype.gather_link_search_ops = nullptr;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
NOD_REGISTER_NODE(node_register)
|
||||
|
||||
} // namespace blender::nodes::
|
||||
|
||||
bool NOD_geometry_for_each_group_input_pair_with_output(const bNodeTree *node_tree,
|
||||
bNode *feg_input_node,
|
||||
const bNode *feg_output_node)
|
||||
{
|
||||
namespace file_ns = blender::nodes::node_geo_for_each_group_input_cc;
|
||||
|
||||
BLI_assert(feg_input_node->type == GEO_NODE_FOR_EACH_GROUP_INPUT);
|
||||
if (feg_output_node->type != GEO_NODE_FOR_EACH_GROUP_OUTPUT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Allow only one input paired to an output. */
|
||||
for (const bNode *other_input_node : node_tree->nodes_by_type("GeometryNodeForEachGroupInput")) {
|
||||
if (other_input_node != feg_input_node) {
|
||||
if (reinterpret_cast<const int &>(other_input_node->custom3) == feg_output_node->identifier) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reinterpret_cast<int &>(feg_input_node->custom3) = feg_output_node->identifier;
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/* 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.h"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "NOD_geometry.hh"
|
||||
#include "NOD_socket.hh"
|
||||
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_for_each_group_output_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Geometry>("Group Part");
|
||||
|
||||
b.add_output<decl::Geometry>("Geometry").propagate_all();
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
geo_node_type_base(&ntype, GEO_NODE_FOR_EACH_GROUP_OUTPUT, "For Each Group Output", NODE_CLASS_INTERFACE);
|
||||
ntype.declare = node_declare;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
NOD_REGISTER_NODE(node_register)
|
||||
|
||||
} // namespace blender::nodes::
|
||||
|
||||
/*
|
||||
blender::Span<NodeRepeatItem> NodeGeometryForEachGroupOutput::items_span() const
|
||||
{
|
||||
return blender::Span<NodeRepeatItem>(items, items_num);
|
||||
}
|
||||
|
||||
blender::MutableSpan<NodeRepeatItem> NodeGeometryRepeatOutput::items_span()
|
||||
{
|
||||
return blender::MutableSpan<NodeRepeatItem>(items, items_num);
|
||||
}
|
||||
|
||||
bool NodeRepeatItem::supports_type(const eNodeSocketDatatype type)
|
||||
{
|
||||
return ELEM(type,
|
||||
SOCK_FLOAT,
|
||||
SOCK_VECTOR,
|
||||
SOCK_RGBA,
|
||||
SOCK_BOOLEAN,
|
||||
SOCK_ROTATION,
|
||||
SOCK_INT,
|
||||
SOCK_STRING,
|
||||
SOCK_GEOMETRY,
|
||||
SOCK_OBJECT,
|
||||
SOCK_MATERIAL,
|
||||
SOCK_IMAGE,
|
||||
SOCK_COLLECTION);
|
||||
}
|
||||
|
||||
std::string NodeRepeatItem::identifier_str() const
|
||||
{
|
||||
return "Item_" + std::to_string(this->identifier);
|
||||
}
|
||||
|
||||
NodeRepeatItem *NodeGeometryRepeatOutput::add_item(const char *name,
|
||||
const eNodeSocketDatatype type)
|
||||
{
|
||||
if (!NodeRepeatItem::supports_type(type)) {
|
||||
return nullptr;
|
||||
}
|
||||
const int insert_index = this->items_num;
|
||||
NodeRepeatItem *old_items = this->items;
|
||||
|
||||
this->items = MEM_cnew_array<NodeRepeatItem>(this->items_num + 1, __func__);
|
||||
std::copy_n(old_items, insert_index, this->items);
|
||||
NodeRepeatItem &new_item = this->items[insert_index];
|
||||
std::copy_n(old_items + insert_index + 1,
|
||||
this->items_num - insert_index,
|
||||
this->items + insert_index + 1);
|
||||
|
||||
new_item.identifier = this->next_identifier++;
|
||||
this->set_item_name(new_item, name);
|
||||
new_item.socket_type = type;
|
||||
|
||||
this->items_num++;
|
||||
MEM_SAFE_FREE(old_items);
|
||||
return &new_item;
|
||||
}
|
||||
|
||||
void NodeGeometryRepeatOutput::set_item_name(NodeRepeatItem &item, const char *name)
|
||||
{
|
||||
char unique_name[MAX_NAME + 4];
|
||||
STRNCPY(unique_name, name);
|
||||
|
||||
struct Args {
|
||||
NodeGeometryRepeatOutput *storage;
|
||||
const NodeRepeatItem *item;
|
||||
} args = {this, &item};
|
||||
|
||||
const char *default_name = nodeStaticSocketLabel(item.socket_type, 0);
|
||||
BLI_uniquename_cb(
|
||||
[](void *arg, const char *name) {
|
||||
const Args &args = *static_cast<Args *>(arg);
|
||||
for (const NodeRepeatItem &item : args.storage->items_span()) {
|
||||
if (&item != args.item) {
|
||||
if (STREQ(item.name, name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
&args,
|
||||
default_name,
|
||||
'.',
|
||||
unique_name,
|
||||
ARRAY_SIZE(unique_name));
|
||||
|
||||
MEM_SAFE_FREE(item.name);
|
||||
item.name = BLI_strdup(unique_name);
|
||||
}
|
||||
*/
|
|
@ -1765,6 +1765,238 @@ class LazyFunctionForRepeatZone : public LazyFunction {
|
|||
}
|
||||
};
|
||||
|
||||
class LazyFunctionForFEGZone : public LazyFunction {
|
||||
private:
|
||||
const bNodeTreeZone &zone_;
|
||||
const bNode &repeat_output_bnode_;
|
||||
const ZoneBuildInfo &zone_info_;
|
||||
const LazyFunction &body_fn_;
|
||||
const RepeatBodyIndices &body_indices_;
|
||||
|
||||
public:
|
||||
LazyFunctionForFEGZone(const bNodeTreeZone &zone,
|
||||
ZoneBuildInfo &zone_info,
|
||||
const LazyFunction &body_fn,
|
||||
const RepeatBodyIndices &body_indices)
|
||||
: zone_(zone),
|
||||
repeat_output_bnode_(*zone.output_node),
|
||||
zone_info_(zone_info),
|
||||
body_fn_(body_fn),
|
||||
body_indices_(body_indices)
|
||||
{
|
||||
debug_name_ = "For Each Group Zone";
|
||||
|
||||
for (const bNodeSocket *socket : zone.input_node->input_sockets()) {
|
||||
inputs_.append_as(socket->name, *socket->typeinfo->geometry_nodes_cpp_type);
|
||||
}
|
||||
zone_info.main_input_indices = inputs_.index_range();
|
||||
|
||||
for (const bNodeLink *link : zone.border_links) {
|
||||
inputs_.append_as(link->fromsock->name, *link->tosock->typeinfo->geometry_nodes_cpp_type);
|
||||
}
|
||||
zone_info.border_link_input_indices = inputs_.index_range().take_back(zone.border_links.size());
|
||||
|
||||
for (const bNodeSocket *socket : zone.output_node->output_sockets()) {
|
||||
inputs_.append_as("Usage", CPPType::get<bool>());
|
||||
outputs_.append_as(socket->name, *socket->typeinfo->geometry_nodes_cpp_type);
|
||||
}
|
||||
zone_info.main_output_usage_indices = inputs_.index_range().take_back(zone.output_node->output_sockets().size());
|
||||
zone_info.main_output_indices = outputs_.index_range();
|
||||
|
||||
for ([[maybe_unused]] const bNodeSocket *socket : zone.input_node->input_sockets()) {
|
||||
outputs_.append_as("Usage", CPPType::get<bool>());
|
||||
}
|
||||
zone_info.main_input_usage_indices = outputs_.index_range().take_back(zone.input_node->input_sockets().size());
|
||||
for ([[maybe_unused]] const bNodeLink *link : zone.border_links) {
|
||||
outputs_.append_as("Border Link Usage", CPPType::get<bool>());
|
||||
}
|
||||
zone_info.border_link_input_usage_indices = outputs_.index_range().take_back(zone.border_links.size());
|
||||
|
||||
for (const auto item : body_indices.attribute_set_input_by_field_source_index.items()) {
|
||||
const int index = inputs_.append_and_get_index_as("Attribute Set", CPPType::get<bke::AnonymousAttributeSet>());
|
||||
zone_info.attribute_set_input_by_field_source_index.add_new(item.key, index);
|
||||
}
|
||||
for (const auto item : body_indices.attribute_set_input_by_caller_propagation_index.items()) {
|
||||
const int index = inputs_.append_and_get_index_as("Attribute Set", CPPType::get<bke::AnonymousAttributeSet>());
|
||||
zone_info.attribute_set_input_by_caller_propagation_index.add_new(item.key, index);
|
||||
}
|
||||
}
|
||||
|
||||
void execute_impl(lf::Params &/*params*/, const lf::Context &/*context*/) const override
|
||||
{
|
||||
#if (0)
|
||||
GeoNodesLFUserData &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
||||
|
||||
const NodeGeometryRepeatOutput &node_storage = *static_cast<const NodeGeometryRepeatOutput *>(repeat_output_bnode_.storage);
|
||||
|
||||
/* Number of iterations to evaluate. */
|
||||
const GeometrySet geometry = params.get_input<GeometrySet>(zone_info_.main_input_indices[0]);
|
||||
const Field<int> group_id = params.get_input<ValueOrField<int>>(zone_info_.main_input_indices[1]).as_field();
|
||||
|
||||
LinearAllocator<> allocator;
|
||||
|
||||
/* Load border link values. */
|
||||
const int border_links_num = zone_info_.border_link_input_indices.size();
|
||||
Array<GMutablePointer> border_link_inputs(border_links_num);
|
||||
for (const int i : IndexRange(border_links_num)) {
|
||||
const int input_index = zone_info_.border_link_input_indices[i];
|
||||
const CPPType &type = *body_fn_.inputs()[input_index].type;
|
||||
void *value = params.try_get_input_data_ptr(input_index);
|
||||
BLI_assert(value != nullptr);
|
||||
border_link_inputs[i] = {type, value};
|
||||
}
|
||||
|
||||
/* Load attribute sets that are needed to propagate attributes correctly in the zone. */
|
||||
Map<int, bke::AnonymousAttributeSet *> attribute_set_by_field_source_index;
|
||||
Map<int, bke::AnonymousAttributeSet *> attribute_set_by_caller_propagation_index;
|
||||
for (const auto item : zone_info_.attribute_set_input_by_field_source_index.items()) {
|
||||
bke::AnonymousAttributeSet &attribute_set = params.get_input<bke::AnonymousAttributeSet>(item.value);
|
||||
attribute_set_by_field_source_index.add_new(item.key, &attribute_set);
|
||||
}
|
||||
for (const auto item : zone_info_.attribute_set_input_by_caller_propagation_index.items()) {
|
||||
bke::AnonymousAttributeSet &attribute_set = params.get_input<bke::AnonymousAttributeSet>(item.value);
|
||||
attribute_set_by_caller_propagation_index.add_new(item.key, &attribute_set);
|
||||
}
|
||||
|
||||
const int body_inputs_num = body_fn_.inputs().size();
|
||||
const int body_outputs_num = body_fn_.outputs().size();
|
||||
/* Evaluate the repeat zone eagerly, one iteration at a time.
|
||||
* This can be made more lazy as a separate step. */
|
||||
for (const int iteration : IndexRange(10)) {
|
||||
/* Prepare all data that has to be passed into the evaluation of the repeat zone body. */
|
||||
Array<GMutablePointer> inputs(body_inputs_num);
|
||||
Array<GMutablePointer> outputs(body_outputs_num);
|
||||
Array<std::optional<lf::ValueUsage>> input_usages(body_inputs_num);
|
||||
Array<lf::ValueUsage> output_usages(body_outputs_num, lf::ValueUsage::Used);
|
||||
Array<bool> set_outputs(body_outputs_num, false);
|
||||
|
||||
/* Prepare pointers to the main input and output values of the repeat zone,
|
||||
* as well as their usages. */
|
||||
Array<bool> tmp_main_input_usages(10);
|
||||
for (const int i : IndexRange(10)) {
|
||||
const CPPType &type = *repeat_item_types[i];
|
||||
void *prev_value = repeat_item_values[iteration * 10 + i];
|
||||
void *next_value = repeat_item_values[(iteration + 1) * 10 + i];
|
||||
inputs[body_indices_.main_inputs[i]] = {type, prev_value};
|
||||
outputs[body_indices_.main_outputs[i]] = {type, next_value};
|
||||
outputs[body_indices_.main_input_usages[i]] = &tmp_main_input_usages[i];
|
||||
}
|
||||
static bool static_true = true;
|
||||
for (const int input_index : body_indices_.main_output_usages) {
|
||||
/* All main outputs are used currently. */
|
||||
inputs[input_index] = &static_true;
|
||||
}
|
||||
/* Prepare border link values for the repeat body. */
|
||||
Array<bool> tmp_border_link_usages(border_links_num);
|
||||
for (const int i : IndexRange(border_links_num)) {
|
||||
const int input_index = body_indices_.border_link_inputs[i];
|
||||
const int usage_index = body_indices_.border_link_usages[i];
|
||||
const CPPType &type = *body_fn_.inputs()[input_index].type;
|
||||
/* Need to copy because a lazy function is allowed to modify the input (e.g. move from
|
||||
* it). */
|
||||
void *value_copy = allocator.allocate(type.size(), type.alignment());
|
||||
type.copy_construct(border_link_input_values[i], value_copy);
|
||||
inputs[input_index] = {type, value_copy};
|
||||
outputs[usage_index] = &tmp_border_link_usages[i];
|
||||
}
|
||||
|
||||
/* Prepare attribute sets that are passed into the repeat body. */
|
||||
for (const auto item : body_indices_.attribute_set_input_by_field_source_index.items()) {
|
||||
bke::AnonymousAttributeSet &attribute_set =
|
||||
*allocator
|
||||
.construct<bke::AnonymousAttributeSet>(
|
||||
*attribute_set_by_field_source_index.lookup(item.key))
|
||||
.release();
|
||||
inputs[item.value] = &attribute_set;
|
||||
}
|
||||
for (const auto item : body_indices_.attribute_set_input_by_caller_propagation_index.items())
|
||||
{
|
||||
bke::AnonymousAttributeSet &attribute_set =
|
||||
*allocator
|
||||
.construct<bke::AnonymousAttributeSet>(
|
||||
*attribute_set_by_caller_propagation_index.lookup(item.key))
|
||||
.release();
|
||||
inputs[item.value] = &attribute_set;
|
||||
}
|
||||
|
||||
/* Prepare evaluation context for the repeat body. */
|
||||
bke::RepeatZoneComputeContext body_compute_context{
|
||||
user_data.compute_context, repeat_output_bnode_, iteration};
|
||||
GeoNodesLFUserData body_user_data = user_data;
|
||||
body_user_data.compute_context = &body_compute_context;
|
||||
if (user_data.modifier_data && user_data.modifier_data->socket_log_contexts) {
|
||||
body_user_data.log_socket_values = user_data.modifier_data->socket_log_contexts->contains(
|
||||
body_compute_context.hash());
|
||||
}
|
||||
GeoNodesLFLocalUserData body_local_user_data{body_user_data};
|
||||
void *body_storage = body_fn_.init_storage(allocator);
|
||||
lf::Context body_context{body_storage, &body_user_data, &body_local_user_data};
|
||||
|
||||
lf::BasicParams body_params{
|
||||
body_fn_, inputs, outputs, input_usages, output_usages, set_outputs};
|
||||
/* Actually evaluate the repeat body. */
|
||||
body_fn_.execute(body_params, body_context);
|
||||
|
||||
/* Destruct values that are not needed after the evaluation anymore. */
|
||||
body_fn_.destruct_storage(body_storage);
|
||||
for (const int i : body_indices_.border_link_inputs) {
|
||||
inputs[i].destruct();
|
||||
}
|
||||
for (const int i : body_indices_.attribute_set_input_by_field_source_index.values()) {
|
||||
inputs[i].destruct();
|
||||
}
|
||||
for (const int i : body_indices_.attribute_set_input_by_caller_propagation_index.values()) {
|
||||
inputs[i].destruct();
|
||||
}
|
||||
}
|
||||
|
||||
/* Set outputs of the repeat zone. */
|
||||
for (const int i : IndexRange(repeat_items_num)) {
|
||||
void *computed_value = repeat_item_values[iterations * repeat_items_num + i];
|
||||
const int output_index = zone_info_.main_output_indices[i];
|
||||
void *r_value = params.get_output_data_ptr(output_index);
|
||||
const CPPType &type = *repeat_item_types[i];
|
||||
type.move_construct(computed_value, r_value);
|
||||
params.output_set(output_index);
|
||||
}
|
||||
for (const int i : zone_info_.main_input_usage_indices) {
|
||||
params.set_output(i, true);
|
||||
}
|
||||
for (const int i : IndexRange(border_links_num)) {
|
||||
params.set_output(zone_info_.border_link_input_usage_indices[i], true);
|
||||
}
|
||||
|
||||
/* Destruct remaining values. */
|
||||
for (const int iteration : IndexRange(iterations)) {
|
||||
MutableSpan<void *> item_values = repeat_item_values.as_mutable_span().slice(
|
||||
(iteration + 1) * repeat_items_num, repeat_items_num);
|
||||
for (const int item_i : IndexRange(repeat_items_num)) {
|
||||
const CPPType &type = *repeat_item_types[item_i];
|
||||
type.destruct(item_values[item_i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string input_name(const int i) const override
|
||||
{
|
||||
if (zone_info_.main_output_usage_indices.contains(i)) {
|
||||
const bNodeSocket &bsocket = zone_.output_node->output_socket(i - zone_info_.main_output_usage_indices.first());
|
||||
return "Usage: " + StringRef(bsocket.name);
|
||||
}
|
||||
return inputs_[i].debug_name;
|
||||
}
|
||||
|
||||
std::string output_name(const int i) const override
|
||||
{
|
||||
if (zone_info_.main_input_usage_indices.contains(i)) {
|
||||
const bNodeSocket &bsocket = zone_.input_node->input_socket(i - zone_info_.main_input_usage_indices.first());
|
||||
return "Usage: " + StringRef(bsocket.name);
|
||||
}
|
||||
return outputs_[i].debug_name;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class to build a lazy-function graph based on a geometry nodes tree.
|
||||
* This is mainly a separate class because it makes it easier to have variables that can be
|
||||
|
@ -1853,6 +2085,10 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
|||
this->build_repeat_zone_function(zone);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_FOR_EACH_GROUP_OUTPUT: {
|
||||
this->build_for_each_group_zone_function(zone);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
|
@ -2158,6 +2394,126 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
|||
zone_info.lazy_function = &fn;
|
||||
}
|
||||
|
||||
void build_for_each_group_zone_function(const bNodeTreeZone &zone)
|
||||
{
|
||||
ZoneBuildInfo &zone_info = zone_build_infos_[zone.index];
|
||||
lf::Graph &lf_body_graph = scope_.construct<lf::Graph>();
|
||||
|
||||
BuildGraphParams graph_params{lf_body_graph};
|
||||
|
||||
Vector<const lf::OutputSocket *, 16> lf_body_inputs;
|
||||
Vector<const lf::InputSocket *, 16> lf_body_outputs;
|
||||
RepeatBodyIndices &body_indices = scope_.construct<RepeatBodyIndices>();
|
||||
|
||||
lf::DummyNode &lf_main_input_node = this->build_dummy_node_for_sockets(
|
||||
"Repeat Input", {}, zone.input_node->output_sockets(), lf_body_graph);
|
||||
for (const int i : zone.input_node->output_sockets().index_range()) {
|
||||
const bNodeSocket &bsocket = zone.input_node->output_socket(i);
|
||||
lf::OutputSocket &lf_socket = lf_main_input_node.output(i);
|
||||
graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket);
|
||||
}
|
||||
lf_body_inputs.extend(lf_main_input_node.outputs());
|
||||
body_indices.main_inputs = lf_body_inputs.index_range();
|
||||
|
||||
lf::DummyNode &lf_main_output_node = this->build_dummy_node_for_sockets(
|
||||
"Repeat Output", zone.output_node->input_sockets(), {}, lf_body_graph);
|
||||
lf_body_outputs.extend(lf_main_output_node.inputs());
|
||||
body_indices.main_outputs = lf_body_outputs.index_range();
|
||||
|
||||
lf::Node &lf_main_input_usage_node = this->build_dummy_node_for_socket_usages(
|
||||
"Input Usages", zone.input_node->output_sockets(), {}, lf_body_graph);
|
||||
lf_body_outputs.extend(lf_main_input_usage_node.inputs());
|
||||
body_indices.main_input_usages = lf_body_outputs.index_range().take_back(
|
||||
lf_main_input_usage_node.inputs().size());
|
||||
|
||||
lf::Node &lf_main_output_usage_node = this->build_dummy_node_for_socket_usages(
|
||||
"Output Usages", {}, zone.output_node->input_sockets(), lf_body_graph);
|
||||
lf_body_inputs.extend(lf_main_output_usage_node.outputs());
|
||||
body_indices.main_output_usages = lf_body_inputs.index_range().take_back(
|
||||
lf_main_output_usage_node.outputs().size());
|
||||
|
||||
for (const int i : zone.output_node->input_sockets().index_range()) {
|
||||
const bNodeSocket &bsocket = zone.output_node->input_socket(i);
|
||||
lf::InputSocket &lf_socket = lf_main_output_node.input(i);
|
||||
lf::OutputSocket &lf_usage = lf_main_output_usage_node.output(i);
|
||||
graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket);
|
||||
graph_params.usage_by_bsocket.add(&bsocket, &lf_usage);
|
||||
}
|
||||
|
||||
lf::Node &lf_border_link_input_node = this->build_zone_border_links_input_node(zone, lf_body_graph);
|
||||
lf_body_inputs.extend(lf_border_link_input_node.outputs());
|
||||
body_indices.border_link_inputs = lf_body_inputs.index_range().take_back(lf_border_link_input_node.outputs().size());
|
||||
|
||||
lf::Node &lf_border_link_usage_node = this->build_border_link_input_usage_node(zone, lf_body_graph);
|
||||
lf_body_outputs.extend(lf_border_link_usage_node.inputs());
|
||||
body_indices.border_link_usages = lf_body_outputs.index_range().take_back(lf_border_link_usage_node.inputs().size());
|
||||
|
||||
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().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::InputSocket &lf_usage_output = lf_main_input_usage_node.input(i);
|
||||
if (lf_usage) {
|
||||
lf_body_graph.add_link(*lf_usage, lf_usage_output);
|
||||
}
|
||||
else {
|
||||
static const bool static_false = false;
|
||||
lf_usage_output.set_default_value(&static_false);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto item : graph_params.lf_output_by_bsocket.items()) {
|
||||
this->insert_links_from_socket(*item.key, *item.value, graph_params);
|
||||
}
|
||||
|
||||
this->link_border_link_inputs_and_usages(
|
||||
zone, lf_border_link_input_node, lf_border_link_usage_node, graph_params);
|
||||
|
||||
this->add_default_inputs(graph_params);
|
||||
|
||||
Map<int, lf::OutputSocket *> lf_attribute_set_by_field_source_index;
|
||||
Map<int, lf::OutputSocket *> lf_attribute_set_by_caller_propagation_index;
|
||||
|
||||
this->build_attribute_set_inputs_for_zone(graph_params,
|
||||
lf_attribute_set_by_field_source_index,
|
||||
lf_attribute_set_by_caller_propagation_index);
|
||||
for (const auto item : lf_attribute_set_by_field_source_index.items()) {
|
||||
lf::OutputSocket &lf_attribute_set_socket = *item.value;
|
||||
if (lf_attribute_set_socket.node().is_dummy()) {
|
||||
const int body_input_index = lf_body_inputs.append_and_get_index(&lf_attribute_set_socket);
|
||||
body_indices.attribute_set_input_by_field_source_index.add_new(item.key, body_input_index);
|
||||
}
|
||||
}
|
||||
for (const auto item : lf_attribute_set_by_caller_propagation_index.items()) {
|
||||
lf::OutputSocket &lf_attribute_set_socket = *item.value;
|
||||
if (lf_attribute_set_socket.node().is_dummy()) {
|
||||
const int body_input_index = lf_body_inputs.append_and_get_index(&lf_attribute_set_socket);
|
||||
body_indices.attribute_set_input_by_caller_propagation_index.add_new(item.key,
|
||||
body_input_index);
|
||||
}
|
||||
}
|
||||
this->link_attribute_set_inputs(lf_body_graph,
|
||||
graph_params,
|
||||
lf_attribute_set_by_field_source_index,
|
||||
lf_attribute_set_by_caller_propagation_index);
|
||||
this->fix_link_cycles(lf_body_graph, graph_params.socket_usage_inputs);
|
||||
|
||||
lf_body_graph.update_node_indices();
|
||||
|
||||
auto &logger = scope_.construct<GeometryNodesLazyFunctionLogger>(*lf_graph_info_);
|
||||
auto &side_effect_provider = scope_.construct<GeometryNodesLazyFunctionSideEffectProvider>();
|
||||
LazyFunction &body_graph_fn = scope_.construct<lf::GraphExecutor>(
|
||||
lf_body_graph, lf_body_inputs, lf_body_outputs, &logger, &side_effect_provider);
|
||||
|
||||
// std::cout << "\n\n" << lf_body_graph.to_dot() << "\n\n";
|
||||
|
||||
auto &fn = scope_.construct<LazyFunctionForFEGZone>(
|
||||
zone, zone_info, body_graph_fn, body_indices);
|
||||
zone_info.lazy_function = &fn;
|
||||
}
|
||||
|
||||
lf::DummyNode &build_zone_border_links_input_node(const bNodeTreeZone &zone, lf::Graph &lf_graph)
|
||||
{
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
|
|
Loading…
Reference in New Issue