WIP: Geometry Nodes: new Bake node #110137

Closed
Jacques Lucke wants to merge 83 commits from JacquesLucke/blender:bake into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
22 changed files with 1599 additions and 9 deletions

View File

@ -478,6 +478,92 @@ class RepeatZoneItemMoveOperator(RepeatZoneOperator, Operator):
return {'FINISHED'}
class BakeNodeOperator:
@classmethod
def poll(cls, context):
space = context.space_data
if not space or space.type != 'NODE_EDITOR' or not space.edit_tree or space.edit_tree.library:
return False
node = context.active_node
if node is None:
return False
if node.bl_idname != "GeometryNodeBake":
return False
return True
class BakeItemAddOperator(BakeNodeOperator, Operator):
"""Add a bake item to the bake node"""
bl_idname = "node.bake_item_add"
bl_label = "Add Bake Item"
bl_options = {'REGISTER', 'UNDO'}
default_socket_type = 'GEOMETRY'
def execute(self, context):
node = context.active_node
bake_items = node.bake_items
# Remember index to move the item.
if node.active_item:
dst_index = node.active_index + 1
dst_type = node.active_item.socket_type
dst_name = node.active_item.name
else:
dst_index = len(bake_items)
dst_type = self.default_socket_type
# Empty name so it is based on the type.
dst_name = ""
bake_items.new(dst_type, dst_name)
bake_items.move(len(bake_items) - 1, dst_index)
node.active_index = dst_index
return {'FINISHED'}
class BakeItemRemoveOperator(BakeNodeOperator, Operator):
"""Remove a bake item from the bake node"""
bl_idname = "node.bake_item_remove"
bl_label = "Remove Bake Item"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
node = context.active_node
bake_items = node.bake_items
if node.active_item:
bake_items.remove(node.active_item)
node.active_index = min(node.active_index, len(bake_items) - 1)
return {'FINISHED'}
class BakeItemMoveOperator(BakeNodeOperator, Operator):
"""Move a bake item up or down in the list"""
bl_idname = "node.bake_item_move"
bl_label = "Move Bake Item"
bl_options = {'REGISTER', 'UNDO'}
direction: EnumProperty(
name="Direction",
items=[('UP', "Up", ""), ('DOWN', "Down", "")],
default='UP',
)
def execute(self, context):
node = context.active_node
bake_items = node.bake_items
if self.direction == 'UP' and node.active_index > 0:
bake_items.move(node.active_index, node.active_index - 1)
node.active_index = node.active_index - 1
elif self.direction == 'DOWN' and node.active_index < len(bake_items) - 1:
bake_items.move(node.active_index, node.active_index + 1)
node.active_index = node.active_index + 1
return {'FINISHED'}
classes = (
NewGeometryNodesModifier,
NewGeometryNodeTreeAssign,
@ -489,4 +575,7 @@ classes = (
RepeatZoneItemAddOperator,
RepeatZoneItemRemoveOperator,
RepeatZoneItemMoveOperator,
BakeItemAddOperator,
BakeItemRemoveOperator,
BakeItemMoveOperator,
)

View File

@ -167,6 +167,7 @@ class NODE_MT_geometry_node_GEO_GEOMETRY(Menu):
layout.separator()
layout.menu("NODE_MT_geometry_node_GEO_GEOMETRY_OPERATIONS")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeBake")
node_add_menu.add_node_type(layout, "GeometryNodeGeometryToInstance")
node_add_menu.add_node_type(layout, "GeometryNodeJoinGeometry")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)

View File

@ -1312,6 +1312,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define GEO_NODE_TOOL_FACE_SET 2112
#define GEO_NODE_TOOL_SET_FACE_SET 2113
#define GEO_NODE_POINTS_TO_CURVES 2114
#define GEO_NODE_BAKE 2115
/** \} */

View File

@ -797,6 +797,13 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
BLO_write_string(writer, item.name);
}
}
if (node->type == GEO_NODE_BAKE) {
const NodeGeometryBake &storage = *static_cast<const NodeGeometryBake *>(node->storage);
BLO_write_struct_array(writer, NodeGeometryBakeItem, storage.items_num, storage.items);
for (const NodeGeometryBakeItem &item : storage.items_span()) {
BLO_write_string(writer, item.name);
}
}
}
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
@ -1004,6 +1011,14 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
}
break;
}
case GEO_NODE_BAKE: {
NodeGeometryBake &storage = *static_cast<NodeGeometryBake *>(node->storage);
BLO_read_data_address(reader, &storage.items);
for (const NodeGeometryBakeItem &item : storage.items_span()) {
BLO_read_data_address(reader, &item.name);
}
break;
}
default:
break;

View File

@ -632,3 +632,24 @@ bool bNodeTree::node_id_path_from_nested_node_ref(const int32_t nested_node_id,
}
return group->node_id_path_from_nested_node_ref(ref->path.id_in_node, r_node_ids);
}
const bNode *bNodeTree::find_nested_node(const int32_t nested_node_id) const
{
const bNestedNodeRef *ref = this->find_nested_node_ref(nested_node_id);
if (ref == nullptr) {
return nullptr;
}
const int32_t node_id = ref->path.node_id;
const bNode *node = this->node_by_id(node_id);
if (node == nullptr) {
return nullptr;
}
if (!node->is_group()) {
return node;
}
const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node->id);
if (group == nullptr) {
return nullptr;
}
return group->find_nested_node(ref->path.id_in_node);
}

View File

@ -1131,10 +1131,10 @@ class NodeTreeMainUpdater {
/* Simplify lookup of old ids. */
Map<bNestedNodePath, int32_t> old_id_by_path;
Set<int32_t> old_ids;
VectorSet<int32_t> sorted_old_ids;
for (const bNestedNodeRef &ref : ntree.nested_node_refs_span()) {
old_id_by_path.add(ref.path, ref.id);
old_ids.add(ref.id);
sorted_old_ids.add(ref.id);
}
Vector<bNestedNodePath> nested_node_paths;
@ -1153,6 +1153,10 @@ class NodeTreeMainUpdater {
for (const bNode *node : ntree.nodes_by_type("GeometryNodeSimulationOutput")) {
nested_node_paths.append({node->identifier, -1});
}
/* Create references for bake nodes. */
for (const bNode *node : ntree.nodes_by_type("GeometryNodeBake")) {
nested_node_paths.append({node->identifier, -1});
}
}
/* Propagate references to nested nodes in group nodes. */
for (const bNode *node : ntree.group_nodes()) {
@ -1180,7 +1184,7 @@ class NodeTreeMainUpdater {
int32_t new_id;
while (true) {
new_id = rng.get_int32(INT32_MAX);
if (!old_ids.contains(new_id) && !new_path_by_id.contains(new_id)) {
if (!sorted_old_ids.contains(new_id) && !new_path_by_id.contains(new_id)) {
break;
}
}
@ -1210,6 +1214,21 @@ class NodeTreeMainUpdater {
index++;
}
/* Keep references in the same order as before and only add new references at the end. */
std::sort(new_refs,
new_refs + new_path_by_id.size(),
[&](const bNestedNodeRef &a, const bNestedNodeRef &b) {
int a_index = sorted_old_ids.index_of_try(a.id);
int b_index = sorted_old_ids.index_of_try(b.id);
if (a_index == -1) {
a_index = INT32_MAX;
}
if (b_index == -1) {
b_index = INT32_MAX;
}
return a_index < b_index;
});
ntree.nested_node_refs = new_refs;
ntree.nested_node_refs_num = new_path_by_id.size();

View File

@ -4,6 +4,8 @@
#pragma once
#include <optional>
#include "BLI_vector_set.hh"
#include "ED_node_c.hh"
@ -33,4 +35,9 @@ void node_insert_on_link_flags_clear(bNodeTree &node_tree);
*/
void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale);
/**
* Find the nested node id of a currently visible node in the root tree.
*/
std::optional<int32_t> find_nested_node_id_in_root(const SpaceNode &snode, const bNode &node);
} // namespace blender::ed::space_node

View File

@ -33,6 +33,7 @@ set(SRC
object_add.cc
object_bake.cc
object_bake_api.cc
object_bake_geometry_nodes.cc
object_bake_simulation.cc
object_collection.cc
object_constraint.cc

View File

@ -0,0 +1,156 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_object.hh"
#include "ED_screen.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "DNA_modifier_types.h"
#include "MOD_nodes.hh"
#include "BKE_bake_items_serialize.hh"
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_scene.h"
#include "DEG_depsgraph.h"
#include "BLI_fileops.hh"
#include "BLI_path_util.h"
#include "BLI_string_utils.h"
#include "object_intern.h"
namespace blender::ed::object::bake_geometry_nodes {
static void bake_operator_props(wmOperatorType *ot)
{
PropertyRNA *prop;
WM_operator_properties_id_lookup(ot, false);
prop = RNA_def_string(
ot->srna, "modifier", nullptr, MAX_NAME, "Modifier", "Name of the modifier");
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_int(ot->srna, "bake_id", 0, 0, INT32_MAX, "Bake ID", "", 0, INT32_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
}
[[nodiscard]] static bool find_edit_bake(Main *bmain,
wmOperator *op,
Object **r_object,
NodesModifierData **r_modifier,
NodesModifierBake **r_bake)
{
Object *object = reinterpret_cast<Object *>(
WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB));
if (object == nullptr) {
return false;
}
char modifier_name[MAX_NAME];
RNA_string_get(op->ptr, "modifier", modifier_name);
ModifierData *md = BKE_modifiers_findby_name(object, modifier_name);
if (md == nullptr || md->type != eModifierType_Nodes) {
return false;
}
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
const int bake_id = RNA_int_get(op->ptr, "bake_id");
NodesModifierBake *bake = nullptr;
for (NodesModifierBake &bake_iter : MutableSpan(nmd->bakes, nmd->bakes_num)) {
if (bake_iter.id == bake_id) {
bake = &bake_iter;
}
}
if (bake == nullptr) {
return false;
}
*r_object = object;
*r_modifier = nmd;
*r_bake = bake;
return true;
}
static int geometry_node_bake_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *object;
NodesModifierData *modifier;
NodesModifierBake *bake;
if (!find_edit_bake(bmain, op, &object, &modifier, &bake)) {
return OPERATOR_CANCELLED;
}
NodesModifierData &nmd = *modifier;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr);
return OPERATOR_CANCELLED;
}
static int geometry_node_bake_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *object;
NodesModifierData *modifier;
NodesModifierBake *bake;
if (!find_edit_bake(bmain, op, &object, &modifier, &bake)) {
return OPERATOR_CANCELLED;
}
NodesModifierData &nmd = *modifier;
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr);
return OPERATOR_FINISHED;
}
static int geometry_node_bake_delete_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
if (edit_modifier_invoke_properties(C, op)) {
return geometry_node_bake_delete_exec(C, op);
}
return OPERATOR_CANCELLED;
}
} // namespace blender::ed::object::bake_geometry_nodes
void OBJECT_OT_geometry_node_bake(wmOperatorType *ot)
{
using namespace blender::ed::object::bake_geometry_nodes;
ot->name = "Bake Geometry Node";
ot->description = "Bake geometry in a Bake node in geometry nodes";
ot->idname = __func__;
ot->exec = geometry_node_bake_exec;
ot->poll = ED_operator_object_active_editable;
bake_operator_props(ot);
}
void OBJECT_OT_geometry_node_bake_delete(wmOperatorType *ot)
{
using namespace blender::ed::object::bake_geometry_nodes;
ot->name = "Delete Geometry Node Bake";
ot->description = "Delete baked data";
ot->idname = __func__;
ot->invoke = geometry_node_bake_delete_invoke;
ot->exec = geometry_node_bake_delete_exec;
ot->poll = ED_operator_object_active_editable;
bake_operator_props(ot);
}

View File

@ -348,6 +348,11 @@ void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot);
void OBJECT_OT_simulation_nodes_cache_bake(wmOperatorType *ot);
void OBJECT_OT_simulation_nodes_cache_delete(wmOperatorType *ot);
/* object_bake_geometry_nodes.cc */
void OBJECT_OT_geometry_node_bake(wmOperatorType *ot);
void OBJECT_OT_geometry_node_bake_delete(wmOperatorType *ot);
/* `object_random.cc` */
void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot);

View File

@ -257,6 +257,8 @@ void ED_operatortypes_object()
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_calculate_to_frame);
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_bake);
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_delete);
WM_operatortype_append(OBJECT_OT_geometry_node_bake);
WM_operatortype_append(OBJECT_OT_geometry_node_bake_delete);
WM_operatortype_append(OBJECT_OT_drop_named_material);
WM_operatortype_append(OBJECT_OT_drop_geometry_nodes);
WM_operatortype_append(OBJECT_OT_unlink_data);

View File

@ -237,6 +237,52 @@ float2 space_node_group_offset(const SpaceNode &snode)
return float2(0);
}
static const bNode *group_node_by_name(const bNodeTree &ntree, StringRef name)
{
for (const bNode *node : ntree.group_nodes()) {
if (node->name == name) {
return node;
}
}
return nullptr;
}
std::optional<int32_t> find_nested_node_id_in_root(const SpaceNode &snode, const bNode &query_node)
{
BLI_assert(snode.edittree->runtime->nodes_by_id.contains(const_cast<bNode *>(&query_node)));
std::optional<int32_t> id_in_node;
const char *group_node_name = nullptr;
const bNode *node = &query_node;
LISTBASE_FOREACH_BACKWARD (const bNodeTreePath *, path, &snode.treepath) {
const bNodeTree *ntree = path->nodetree;
if (group_node_name) {
node = group_node_by_name(*ntree, group_node_name);
}
bool found = false;
for (const bNestedNodeRef &ref : ntree->nested_node_refs_span()) {
if (node->is_group()) {
if (ref.path.node_id == node->identifier && ref.path.id_in_node == id_in_node) {
group_node_name = path->node_name;
id_in_node = ref.id;
found = true;
break;
}
}
else if (ref.path.node_id == node->identifier) {
group_node_name = path->node_name;
id_in_node = ref.id;
found = true;
break;
}
}
if (!found) {
return std::nullopt;
}
}
return id_in_node;
}
/* ******************** default callbacks for node space ***************** */
static SpaceLink *node_create(const ScrArea * /*area*/, const Scene * /*scene*/)

View File

@ -2332,6 +2332,20 @@ typedef struct NodesModifierSettings {
struct IDProperty *properties;
} NodesModifierSettings;
typedef struct NodesModifierBake {
int id;
/** #NodesModifierBakeType. */
uint32_t bake_type;
int frame_start;
int frame_end;
char *directory;
} NodesModifierBake;
typedef enum NodesModifierBakeType {
NODES_MODIFIER_BAKE_TYPE_STILL = 0,
NODES_MODIFIER_BAKE_TYPE_ANIMATED = 1,
} NodesModifierBakeFlag;
typedef struct NodesModifierData {
ModifierData modifier;
struct bNodeTree *node_group;
@ -2340,7 +2354,13 @@ typedef struct NodesModifierData {
* Directory where baked simulation states are stored. This may be relative to the .blend file.
*/
char *simulation_bake_directory;
int active_bake;
int bakes_num;
NodesModifierBake *bakes;
NodesModifierRuntimeHandle *runtime;
void *_pad2;
} NodesModifierData;
typedef struct MeshToVolumeModifierData {

View File

@ -709,8 +709,9 @@ typedef struct bNodeTree {
const bNestedNodeRef *find_nested_node_ref(int32_t nested_node_id) const;
/** Conversions between node id paths and their corresponding nested node ref. */
const bNestedNodeRef *nested_node_ref_from_node_id_path(blender::Span<int> node_ids) const;
[[nodiscard]] bool node_id_path_from_nested_node_ref(const int32_t nested_node_id,
[[nodiscard]] bool node_id_path_from_nested_node_ref(int32_t nested_node_id,
blender::Vector<int32_t> &r_node_ids) const;
const bNode *find_nested_node(int32_t nested_node_id) const;
/**
* Update a run-time cache for the node tree based on it's current state. This makes many methods
@ -1827,6 +1828,39 @@ typedef struct NodeGeometryRepeatOutput {
#endif
} NodeGeometryRepeatOutput;
typedef struct NodeGeometryBakeItem {
char *name;
/** #eNodeSocketDatatype. */
short socket_type;
/** #eAttrDomain. */
short attribute_domain;
/**
* Generated unique identifier for sockets which stays the same even when the item order or
* names change.
*/
int identifier;
#ifdef __cplusplus
static bool supports_type(eNodeSocketDatatype type);
std::string identifier_str() const;
#endif
} NodeGeometryBakeItem;
typedef struct NodeGeometryBake {
NodeGeometryBakeItem *items;
int items_num;
int active_index;
int next_identifier;
char _pad[4];
#ifdef __cplusplus
blender::Span<NodeGeometryBakeItem> items_span() const;
blender::MutableSpan<NodeGeometryBakeItem> items_span();
NodeGeometryBakeItem *add_item(const char *name, eNodeSocketDatatype type);
void set_item_name(NodeGeometryBakeItem &item, const char *name);
#endif
} NodeGeometryBake;
typedef struct NodeGeometryDistributePointsInVolume {
/** #GeometryNodePointDistributeVolumeMode. */
uint8_t mode;

View File

@ -7050,11 +7050,72 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
{
static EnumPropertyItem bake_type_items[] = {
{NODES_MODIFIER_BAKE_TYPE_STILL,
"STILL",
0,
"Still",
"Bake the geometry at one frame and use it for all frames"},
{NODES_MODIFIER_BAKE_TYPE_ANIMATED,
"ANIMATED",
0,
"Animated",
"Bake the geometry at multiple frames"},
{0, nullptr, 0, nullptr, nullptr},
};
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "NodesModifierBake", nullptr);
RNA_def_struct_ui_text(srna, "Nodes Modifier Bake", "");
prop = RNA_def_property(srna, "directory", PROP_STRING, PROP_DIRPATH);
RNA_def_property_ui_text(prop, "Directory", "Location on disk where the bake data is stored");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "bake_type", PROP_ENUM, PROP_NONE);
RNA_def_property_ui_text(prop, "Bake Type", "");
RNA_def_property_enum_items(prop, bake_type_items);
RNA_def_property_update(prop, 0, nullptr);
prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_NONE);
RNA_def_property_ui_text(prop, "Frame Start", "Frame to start baking at");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, nullptr);
prop = RNA_def_property(srna, "frame_end", PROP_INT, PROP_NONE);
RNA_def_property_ui_text(prop, "Frame End", "Frame to end baking at");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, nullptr);
}
static void rna_def_modifier_nodes_bakes(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "NodesModifierBakes", nullptr);
RNA_def_struct_sdna(srna, "NodesModifierData");
RNA_def_struct_ui_text(srna, "Bakes", "Bake data for every bake node");
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "active_bake");
RNA_def_property_ui_text(prop, "Active Bake Index", "");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, nullptr);
}
static void rna_def_modifier_nodes(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
rna_def_modifier_nodes_bake(brna);
rna_def_modifier_nodes_bakes(brna);
srna = RNA_def_struct(brna, "NodesModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Nodes Modifier", "");
RNA_def_struct_sdna(srna, "NodesModifierData");
@ -7075,6 +7136,11 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
prop, "Simulation Bake Directory", "Location on disk where the bake data is stored");
RNA_def_property_update(prop, 0, nullptr);
prop = RNA_def_property(srna, "bakes", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "NodesModifierBake");
RNA_def_property_collection_sdna(prop, nullptr, "bakes", "bakes_num");
RNA_def_property_srna(prop, "NodesModifierBakes");
RNA_define_lib_overridable(false);
}

View File

@ -3151,6 +3151,20 @@ static bNode *find_node_by_repeat_item(PointerRNA *ptr)
return nullptr;
}
static bNode *find_node_by_bake_item(PointerRNA *ptr)
{
const NodeGeometryBakeItem *item = static_cast<const NodeGeometryBakeItem *>(ptr->data);
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
ntree->ensure_topology_cache();
for (bNode *node : ntree->nodes_by_type("GeometryNodeBake")) {
auto *storage = static_cast<NodeGeometryBake *>(node->storage);
if (storage->items_span().contains_ptr(item)) {
return node;
}
}
return nullptr;
}
static void rna_RepeatItem_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr)
{
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
@ -3160,6 +3174,15 @@ static void rna_RepeatItem_update(Main *bmain, Scene * /*scene*/, PointerRNA *pt
ED_node_tree_propagate_change(nullptr, bmain, ntree);
}
static void rna_NodeGeometryBakeItem_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr)
{
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
bNode *node = find_node_by_bake_item(ptr);
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(nullptr, bmain, ntree);
}
static bool rna_SimulationStateItem_socket_type_supported(const EnumPropertyItem *item)
{
return NOD_geometry_simulation_output_item_socket_type_supported(
@ -3191,6 +3214,21 @@ static const EnumPropertyItem *rna_RepeatItem_socket_type_itemf(bContext * /*C*/
rna_RepeatItem_socket_type_supported);
}
static bool rna_NodeGeometryBakeItem_socket_type_supported(const EnumPropertyItem *item)
{
return NodeGeometryBakeItem::supports_type(eNodeSocketDatatype(item->value));
}
static const EnumPropertyItem *rna_NodeGeometryBakeItem_socket_type_itemf(bContext * /*C*/,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
*r_free = true;
return itemf_function_check(rna_enum_node_socket_data_type_items,
rna_NodeGeometryBakeItem_socket_type_supported);
}
static void rna_SimulationStateItem_name_set(PointerRNA *ptr, const char *value)
{
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
@ -3210,6 +3248,14 @@ static void rna_RepeatItem_name_set(PointerRNA *ptr, const char *value)
storage->set_item_name(*item, value);
}
static void rna_NodeGeometryBakeItem_name_set(PointerRNA *ptr, const char *value)
{
bNode *node = find_node_by_bake_item(ptr);
NodeGeometryBakeItem *item = static_cast<NodeGeometryBakeItem *>(ptr->data);
auto *storage = static_cast<NodeGeometryBake *>(node->storage);
storage->set_item_name(*item, value);
}
static void rna_SimulationStateItem_color_get(PointerRNA *ptr, float *values)
{
NodeSimulationItem *item = static_cast<NodeSimulationItem *>(ptr->data);
@ -3226,6 +3272,14 @@ static void rna_RepeatItem_color_get(PointerRNA *ptr, float *values)
ED_node_type_draw_color(socket_type_idname, values);
}
static void rna_NodeGeometryBakeItem_color_get(PointerRNA *ptr, float *values)
{
NodeGeometryBakeItem *item = static_cast<NodeGeometryBakeItem *>(ptr->data);
const char *socket_type_idname = nodeStaticSocketType(item->socket_type, 0);
ED_node_type_draw_color(socket_type_idname, values);
}
static PointerRNA rna_NodeGeometrySimulationInput_paired_output_get(PointerRNA *ptr)
{
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
@ -3327,6 +3381,24 @@ static NodeRepeatItem *rna_NodeGeometryRepeatOutput_items_new(
return item;
}
static NodeGeometryBakeItem *rna_NodeGeometryBake_items_new(
ID *id, bNode *node, Main *bmain, ReportList *reports, int socket_type, const char *name)
{
NodeGeometryBake *storage = static_cast<NodeGeometryBake *>(node->storage);
NodeGeometryBakeItem *item = storage->add_item(name, eNodeSocketDatatype(socket_type));
if (item == nullptr) {
BKE_report(reports, RPT_ERROR, "Unable to create socket");
}
else {
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(nullptr, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
return item;
}
static void rna_NodeGeometrySimulationOutput_items_remove(
ID *id, bNode *node, Main *bmain, ReportList *reports, NodeSimulationItem *item)
{
@ -3371,6 +3443,33 @@ static void rna_NodeGeometryRepeatOutput_items_remove(
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
static void rna_NodeGeometryBake_items_remove(
ID *id, bNode *node, Main *bmain, ReportList *reports, NodeGeometryBakeItem *item)
{
NodeGeometryBake *storage = static_cast<NodeGeometryBake *>(node->storage);
if (!storage->items_span().contains_ptr(item)) {
BKE_reportf(reports, RPT_ERROR, "Unable to locate item '%s' in node", item->name);
return;
}
const int remove_index = item - storage->items;
NodeGeometryBakeItem *old_items = storage->items;
storage->items = MEM_cnew_array<NodeGeometryBakeItem>(storage->items_num - 1, __func__);
std::copy_n(old_items, remove_index, storage->items);
std::copy_n(old_items + remove_index + 1,
storage->items_num - remove_index - 1,
storage->items + remove_index);
MEM_SAFE_FREE(old_items[remove_index].name);
storage->items_num--;
MEM_SAFE_FREE(old_items);
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(nullptr, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
static void rna_NodeGeometrySimulationOutput_items_clear(ID *id, bNode *node, Main *bmain)
{
NodeGeometrySimulationOutput *sim = static_cast<NodeGeometrySimulationOutput *>(node->storage);
@ -3393,6 +3492,17 @@ static void rna_NodeGeometryRepeatOutput_items_clear(ID * /*id*/, bNode *node, M
storage->active_index = 0;
}
static void rna_NodeGeometryBake_items_clear(ID * /*id*/, bNode *node, Main * /*bmain*/)
{
NodeGeometryBake *storage = static_cast<NodeGeometryBake *>(node->storage);
for (NodeGeometryBakeItem &item : storage->items_span()) {
MEM_SAFE_FREE(item.name);
}
MEM_SAFE_FREE(storage->items);
storage->items_num = 0;
storage->active_index = 0;
}
static void rna_NodeGeometrySimulationOutput_items_move(
ID *id, bNode *node, Main *bmain, int from_index, int to_index)
{
@ -3442,6 +3552,37 @@ static void rna_NodeGeometryRepeatOutput_items_move(
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
static void rna_NodeGeometryBake_items_move(
ID *id, bNode *node, Main *bmain, int from_index, int to_index)
{
NodeGeometryBake *storage = static_cast<NodeGeometryBake *>(node->storage);
if (from_index < 0 || from_index >= storage->items_num || to_index < 0 ||
to_index >= storage->items_num)
{
return;
}
if (from_index < to_index) {
const NodeGeometryBakeItem tmp = storage->items[from_index];
for (int i = from_index; i < to_index; i++) {
storage->items[i] = storage->items[i + 1];
}
storage->items[to_index] = tmp;
}
else if (from_index > to_index) {
const NodeGeometryBakeItem tmp = storage->items[from_index];
for (int i = from_index; i > to_index; i--) {
storage->items[i] = storage->items[i - 1];
}
storage->items[to_index] = tmp;
}
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(nullptr, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
static PointerRNA rna_NodeGeometrySimulationOutput_active_item_get(PointerRNA *ptr)
{
bNode *node = static_cast<bNode *>(ptr->data);
@ -3464,6 +3605,19 @@ static PointerRNA rna_NodeGeometryRepeatOutput_active_item_get(PointerRNA *ptr)
return r_ptr;
}
static PointerRNA rna_NodeGeometryBake_active_item_get(PointerRNA *ptr)
{
bNode *node = static_cast<bNode *>(ptr->data);
NodeGeometryBake *storage = static_cast<NodeGeometryBake *>(node->storage);
blender::MutableSpan<NodeGeometryBakeItem> items = storage->items_span();
PointerRNA r_ptr{};
if (items.index_range().contains(storage->active_index)) {
RNA_pointer_create(
ptr->owner_id, &RNA_NodeGeometryBakeItem, &items[storage->active_index], &r_ptr);
}
return r_ptr;
}
static void rna_NodeGeometrySimulationOutput_active_item_set(PointerRNA *ptr,
PointerRNA value,
ReportList * /*reports*/)
@ -3486,6 +3640,18 @@ static void rna_NodeGeometryRepeatOutput_active_item_set(PointerRNA *ptr,
}
}
static void rna_NodeGeometryBake_active_item_set(PointerRNA *ptr,
PointerRNA value,
ReportList * /*reports*/)
{
bNode *node = static_cast<bNode *>(ptr->data);
NodeGeometryBake *storage = static_cast<NodeGeometryBake *>(node->storage);
NodeGeometryBakeItem *item = static_cast<NodeGeometryBakeItem *>(value.data);
if (storage->items_span().contains_ptr(item)) {
storage->active_index = item - storage->items;
}
}
/* ******** Node Socket Types ******** */
static PointerRNA rna_NodeOutputFile_slot_layer_get(CollectionPropertyIterator *iter)
@ -9126,6 +9292,120 @@ static void def_geo_repeat_output(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE, nullptr);
}
static void rna_def_bake_item(BlenderRNA *brna)
{
PropertyRNA *prop;
StructRNA *srna = RNA_def_struct(brna, "NodeGeometryBakeItem", nullptr);
RNA_def_struct_ui_text(srna, "Bake Item", "");
RNA_def_struct_sdna(srna, "NodeGeometryBakeItem");
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_NodeGeometryBakeItem_name_set");
RNA_def_property_ui_text(prop, "Name", "");
RNA_def_struct_name_property(srna, prop);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeGeometryBakeItem_update");
prop = RNA_def_property(srna, "socket_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_node_socket_data_type_items);
RNA_def_property_enum_funcs(
prop, nullptr, nullptr, "rna_NodeGeometryBakeItem_socket_type_itemf");
RNA_def_property_ui_text(prop, "Socket Type", "");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeGeometryBakeItem_update");
prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_ui_text(
prop, "Attribute Domain", "Attribute domain where the attribute is stored");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeGeometryBakeItem_update");
prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 4);
RNA_def_property_float_funcs(prop, "rna_NodeGeometryBakeItem_color_get", nullptr, nullptr);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(
prop, "Color", "Color of the corresponding socket type in the node editor");
}
static void rna_def_geo_bake_items(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *parm;
FunctionRNA *func;
srna = RNA_def_struct(brna, "NodeGeometryBakeItems", nullptr);
RNA_def_struct_sdna(srna, "bNode");
RNA_def_struct_ui_text(srna, "Items", "Collection of bake items");
func = RNA_def_function(srna, "new", "rna_NodeGeometryBake_items_new");
RNA_def_function_ui_description(func, "Add a item to this bake node");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_enum(func,
"socket_type",
rna_enum_node_socket_data_type_items,
SOCK_GEOMETRY,
"Socket Type",
"Socket type of the item");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
parm = RNA_def_string(func, "name", nullptr, MAX_NAME, "Name", "");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
/* return value */
parm = RNA_def_pointer(func, "item", "NodeGeometryBakeItem", "Item", "New item");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_NodeGeometryBake_items_remove");
RNA_def_function_ui_description(func, "Remove an item from this bake node");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_pointer(func, "item", "NodeGeometryBakeItem", "Item", "The item to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
func = RNA_def_function(srna, "clear", "rna_NodeGeometryBake_items_clear");
RNA_def_function_ui_description(func, "Remove all items from this bake node");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
func = RNA_def_function(srna, "move", "rna_NodeGeometryBake_items_move");
RNA_def_function_ui_description(func, "Move an item to another position");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
parm = RNA_def_int(
func, "from_index", -1, 0, INT_MAX, "From Index", "Index of the item to move", 0, 10000);
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
parm = RNA_def_int(
func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the item", 0, 10000);
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
}
static void def_geo_bake(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryBake", "storage");
prop = RNA_def_property(srna, "bake_items", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "items", "items_num");
RNA_def_property_struct_type(prop, "NodeGeometryBakeItem");
RNA_def_property_ui_text(prop, "Items", "");
RNA_def_property_srna(prop, "NodeGeometryBakeItems");
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, nullptr, "active_index");
RNA_def_property_ui_text(prop, "Active Item Index", "");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_NODE, nullptr);
prop = RNA_def_property(srna, "active_item", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "NodeGeometryBakeItem");
RNA_def_property_pointer_funcs(prop,
"rna_NodeGeometryBake_active_item_get",
"rna_NodeGeometryBake_active_item_set",
nullptr,
nullptr);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Active Item", "");
RNA_def_property_update(prop, NC_NODE, nullptr);
}
static void def_geo_curve_handle_type_selection(StructRNA *srna)
{
PropertyRNA *prop;
@ -10440,6 +10720,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_simulation_state_item(brna);
rna_def_repeat_item(brna);
rna_def_bake_item(brna);
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
{ \
@ -10492,6 +10773,7 @@ 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_bake_items(brna);
rna_def_node_instance_hash(brna);
}

View File

@ -66,6 +66,7 @@
#include "BLT_translation.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include "RNA_access.hh"
@ -383,11 +384,8 @@ static bool logging_enabled(const ModifierEvalContext *ctx)
return true;
}
} // namespace blender
void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
static void update_id_properties_from_node_group(NodesModifierData *nmd)
{
using namespace blender;
if (nmd->node_group == nullptr) {
if (nmd->settings.properties) {
IDP_FreeProperty(nmd->settings.properties);
@ -411,6 +409,66 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
if (old_properties != nullptr) {
IDP_FreeProperty(old_properties);
}
}
static void update_bakes_from_node_group(NodesModifierData &nmd)
{
Map<int, const NodesModifierBake *> old_bake_by_id;
for (const NodesModifierBake &bake : Span(nmd.bakes, nmd.bakes_num)) {
old_bake_by_id.add(bake.id, &bake);
}
Vector<int> new_bake_ids;
for (const bNestedNodeRef &ref : nmd.node_group->nested_node_refs_span()) {
const bNode *node = nmd.node_group->find_nested_node(ref.id);
if (node) {
if (node->type == GEO_NODE_BAKE) {
new_bake_ids.append(ref.id);
}
}
else if (old_bake_by_id.contains(ref.id)) {
/* Keep baked data in case linked data is missing so that it still exists when the linked
* data has been found. */
new_bake_ids.append(ref.id);
}
}
NodesModifierBake *new_bake_data = static_cast<NodesModifierBake *>(
MEM_callocN(sizeof(NodesModifierBake) * new_bake_ids.size(), __func__));
for (const int i : new_bake_ids.index_range()) {
const int id = new_bake_ids[i];
const NodesModifierBake *old_bake = old_bake_by_id.lookup_default(id, nullptr);
NodesModifierBake &new_bake = new_bake_data[i];
if (old_bake) {
new_bake = *old_bake;
if (new_bake.directory) {
new_bake.directory = BLI_strdup(new_bake.directory);
}
}
else {
new_bake.id = id;
new_bake.frame_start = 1;
new_bake.frame_end = 100;
}
}
for (NodesModifierBake &old_bake : MutableSpan(nmd.bakes, nmd.bakes_num)) {
MEM_SAFE_FREE(old_bake.directory);
}
MEM_SAFE_FREE(nmd.bakes);
nmd.bakes = new_bake_data;
nmd.bakes_num = new_bake_ids.size();
nmd.active_bake = std::max(0, std::min(nmd.bakes_num - 1, nmd.active_bake));
}
} // namespace blender
void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
{
using namespace blender;
update_id_properties_from_node_group(nmd);
update_bakes_from_node_group(*nmd);
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
}
@ -557,11 +615,90 @@ static void find_side_effect_nodes_for_viewer_path(
}
}
static void find_side_effect_nodes_for_nested_node_ref(
const bNestedNodeRef &node_ref,
const NodesModifierData &nmd,
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes)
{
ComputeContextBuilder compute_context_builder;
compute_context_builder.push<bke::ModifierComputeContext>(nmd.modifier.name);
/* Write side effect nodes to a new map and only add them to r_side_effect_nodes if this function
* does not return early. */
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> local_side_effect_nodes;
const bNodeTree *group = nmd.node_group;
Vector<int32_t> node_id_path;
if (!group->node_id_path_from_nested_node_ref(node_ref.id, node_id_path)) {
return;
}
for (const int32_t node_id : node_id_path) {
const bNode *node = group->node_by_id(node_id);
if (node == nullptr) {
return;
}
const bke::bNodeTreeZones *tree_zones = group->zones();
if (tree_zones == nullptr) {
return;
}
const auto lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*group);
if (lf_graph_info == nullptr) {
return;
}
const bke::bNodeTreeZone *zone = tree_zones->get_zone_by_node(node_id);
if (zone != nullptr) {
/* Referenced node must be outside of any zone to be able to make it a side effect node. */
return;
}
if (node->is_group()) {
if (node->id == nullptr) {
return;
}
if (node->is_muted()) {
return;
}
const lf::FunctionNode *lf_group_node = lf_graph_info->mapping.group_node_map.lookup_default(
node, nullptr);
if (lf_group_node == nullptr) {
return;
}
local_side_effect_nodes.add(compute_context_builder.hash(), lf_group_node);
compute_context_builder.push<bke::NodeGroupComputeContext>(*node);
group = reinterpret_cast<const bNodeTree *>(node->id);
}
else {
const lf::FunctionNode *lf_bake_node = lf_graph_info->mapping.bake_node_map.lookup_default(
node, nullptr);
if (lf_bake_node == nullptr) {
return;
}
local_side_effect_nodes.add(compute_context_builder.hash(), lf_bake_node);
break;
}
}
/* Successfully found all compute contexts for the nested node. */
for (const auto item : local_side_effect_nodes.items()) {
r_side_effect_nodes.add_multiple(item.key, item.value);
}
}
static void find_side_effect_nodes(
const NodesModifierData &nmd,
const ModifierEvalContext &ctx,
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes)
{
// if (nmd.runtime->bakes) {
// std::lock_guard lock{nmd.runtime->bakes->mutex};
// for (const auto item : nmd.runtime->bakes->storage_by_id.items()) {
// if (item.value->current_bake_state) {
// const bNestedNodeRef *node_ref = nmd.node_group->find_nested_node_ref(item.key);
// BLI_assert(node_ref != nullptr);
// find_side_effect_nodes_for_nested_node_ref(*node_ref, nmd, r_side_effect_nodes);
// }
// }
// }
Main *bmain = DEG_get_bmain(ctx.depsgraph);
wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
if (wm == nullptr) {
@ -1068,6 +1205,7 @@ static void modifyGeometry(ModifierData *md,
}
nodes::GeoNodesModifierData modifier_eval_data{};
modifier_eval_data.nmd = nmd;
modifier_eval_data.depsgraph = ctx->depsgraph;
modifier_eval_data.self_object = ctx->object;
auto eval_log = std::make_unique<geo_log::GeoModifierLog>();
@ -1086,8 +1224,24 @@ static void modifyGeometry(ModifierData *md,
find_side_effect_nodes(*nmd, *ctx, side_effect_nodes);
modifier_eval_data.side_effect_nodes = &side_effect_nodes;
bke::ModifierComputeContext modifier_compute_context{nullptr, nmd->modifier.name};
{
for (const bNestedNodeRef &node_ref : nmd->node_group->nested_node_refs_span()) {
Vector<int32_t> node_ids;
if (!nmd->node_group->node_id_path_from_nested_node_ref(node_ref.id, node_ids)) {
continue;
}
ComputeContextBuilder compute_context_builder;
compute_context_builder.push<bke::ModifierComputeContext>(nmd->modifier.name);
const int32_t leaf_node_id = node_ids.pop_last();
for (const int32_t node_id : node_ids.as_span()) {
compute_context_builder.push<bke::NodeGroupComputeContext>(node_id);
}
modifier_eval_data.nested_node_id_by_compute_context.add(
{compute_context_builder.hash(), leaf_node_id}, node_ref.id);
}
}
bke::ModifierComputeContext modifier_compute_context{nullptr, nmd->modifier.name};
geometry_set = nodes::execute_geometry_nodes_on_geometry(
tree,
nmd->settings.properties,
@ -1675,6 +1829,11 @@ static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const Modi
* and don't necessarily need to be written, but we can't just free them. */
IDP_BlendWrite(writer, nmd->settings.properties);
BLO_write_struct_array(writer, NodesModifierBake, nmd->bakes_num, nmd->bakes);
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
BLO_write_string(writer, bake.directory);
}
if (!BLO_write_is_undo(writer)) {
LISTBASE_FOREACH (IDProperty *, prop, &nmd->settings.properties->data.group) {
if (prop->type == IDP_INT) {
@ -1701,6 +1860,12 @@ static void blend_read(BlendDataReader *reader, ModifierData *md)
BLO_read_data_address(reader, &nmd->settings.properties);
IDP_BlendDataRead(reader, &nmd->settings.properties);
}
BLO_read_data_address(reader, &nmd->bakes);
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
BLO_read_data_address(reader, &bake.directory);
}
nmd->runtime = MEM_new<NodesModifierRuntime>(__func__);
nmd->runtime->cache = std::make_shared<bake::ModifierCache>();
}
@ -1712,6 +1877,16 @@ static void copy_data(const ModifierData *md, ModifierData *target, const int fl
BKE_modifier_copydata_generic(md, target, flag);
if (nmd->bakes) {
tnmd->bakes = static_cast<NodesModifierBake *>(MEM_dupallocN(nmd->bakes));
for (const int i : IndexRange(nmd->bakes_num)) {
NodesModifierBake &bake = tnmd->bakes[i];
if (bake.directory) {
bake.directory = BLI_strdup(bake.directory);
}
}
}
tnmd->runtime = MEM_new<NodesModifierRuntime>(__func__);
if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) {
@ -1741,6 +1916,11 @@ static void free_data(ModifierData *md)
nmd->settings.properties = nullptr;
}
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
MEM_SAFE_FREE(bake.directory);
}
MEM_SAFE_FREE(nmd->bakes);
MEM_SAFE_FREE(nmd->simulation_bake_directory);
MEM_delete(nmd->runtime);
}

View File

@ -35,6 +35,7 @@
struct Object;
struct Depsgraph;
struct NodesModifierData;
struct Scene;
namespace blender::nodes {
@ -132,6 +133,7 @@ class GeoNodesSimulationParams {
* Data that is passed into geometry nodes evaluation from the modifier.
*/
struct GeoNodesModifierData {
const NodesModifierData *nmd = nullptr;
/** Object that is currently evaluated. */
const Object *self_object = nullptr;
/** Depsgraph that is evaluating the modifier. */
@ -154,6 +156,8 @@ struct GeoNodesModifierData {
* If this is null, all socket values will be logged.
*/
const Set<ComputeContextHash> *socket_log_contexts = nullptr;
Map<std::pair<ComputeContextHash, int32_t>, int32_t> nested_node_id_by_compute_context;
};
struct GeoNodesOperatorData {
@ -275,6 +279,7 @@ struct GeometryNodeLazyFunctionGraphMapping {
Map<const bNode *, const lf::FunctionNode *> group_node_map;
Map<const bNode *, const lf::FunctionNode *> viewer_node_map;
Map<const bke::bNodeTreeZone *, const lf::FunctionNode *> zone_node_map;
Map<const bNode *, const lf::FunctionNode *> bake_node_map;
/* Indexed by #bNodeSocket::index_in_all_outputs. */
Array<int> lf_input_index_for_output_bsocket_usage;
@ -338,6 +343,9 @@ std::unique_ptr<LazyFunction> get_simulation_input_lazy_function(
const bNode &node,
GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info);
std::unique_ptr<LazyFunction> get_switch_node_lazy_function(const bNode &node);
std::unique_ptr<LazyFunction> get_bake_node_lazy_function(
const bNode &node, GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info);
std::unique_ptr<LazyFunction> get_bake_node_input_usage_lazy_function(const bNode &node);
struct FoundNestedNodeID {
int id;

View File

@ -293,6 +293,7 @@ DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToStri
DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, 0, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "Add the values of an evaluated field together and output the running total for each element")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, 0, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "Retrieve the number of elements in a geometry for each attribute domain")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, 0, "ATTRIBUTE_STATISTIC",AttributeStatistic, "Attribute Statistic", "Calculate statistics about a data set from a field evaluated on a geometry")
DefNode(GeometryNode, GEO_NODE_BAKE, def_geo_bake, "BAKE", Bake, "Bake", "Store the geometry to having to recompute it every time")
DefNode(GeometryNode, GEO_NODE_BLUR_ATTRIBUTE, 0, "BLUR_ATTRIBUTE", BlurAttribute, "Blur Attribute", "Mix attribute values of neighboring elements")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "Calculate the limits of a geometry's positions and generate a box mesh with those dimensions")
DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, 0, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "Store the result of a field on a geometry and output the data as a node socket. Allows remembering or interpolating data as the geometry changes, such as positions before deformation")

View File

@ -16,6 +16,7 @@ set(INC
../../gpu
../../imbuf
../../makesrna
../../modifiers
../../render
../../windowmanager
../../../../extern/fmtlib/include
@ -31,6 +32,7 @@ set(SRC
nodes/node_geo_attribute_capture.cc
nodes/node_geo_attribute_domain_size.cc
nodes/node_geo_attribute_statistic.cc
nodes/node_geo_bake.cc
nodes/node_geo_blur_attribute.cc
nodes/node_geo_boolean.cc
nodes/node_geo_bounding_box.cc

View File

@ -0,0 +1,590 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "node_geometry_util.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "BKE_bake_geometry_nodes_modifier.hh"
#include "BKE_bake_items_socket.hh"
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BLI_binary_search.hh"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_string_utils.h"
#include "DNA_modifier_types.h"
#include "DNA_space_types.h"
#include "ED_node.hh"
#include "MOD_nodes.hh"
#include "NOD_socket.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "DEG_depsgraph_query.h"
#include "WM_api.hh"
namespace blender::nodes::node_geo_bake_cc {
NODE_STORAGE_FUNCS(NodeGeometryBake);
static std::unique_ptr<SocketDeclaration> socket_declaration_for_bake_item(
const NodeGeometryBakeItem &item,
const eNodeSocketInOut in_out,
const int corresponding_input = -1)
{
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
std::unique_ptr<SocketDeclaration> decl;
auto handle_field_decl = [&](SocketDeclaration &decl) {
if (in_out == SOCK_IN) {
decl.input_field_type = InputSocketFieldType::IsSupported;
}
else {
decl.output_field_dependency = OutputFieldDependency::ForPartiallyDependentField(
{corresponding_input});
}
};
switch (socket_type) {
case SOCK_FLOAT:
decl = std::make_unique<decl::Float>();
handle_field_decl(*decl);
break;
case SOCK_VECTOR:
decl = std::make_unique<decl::Vector>();
handle_field_decl(*decl);
break;
case SOCK_RGBA:
decl = std::make_unique<decl::Color>();
handle_field_decl(*decl);
break;
case SOCK_BOOLEAN:
decl = std::make_unique<decl::Bool>();
handle_field_decl(*decl);
break;
case SOCK_ROTATION:
decl = std::make_unique<decl::Rotation>();
handle_field_decl(*decl);
break;
case SOCK_INT:
decl = std::make_unique<decl::Int>();
handle_field_decl(*decl);
break;
case SOCK_GEOMETRY:
decl = std::make_unique<decl::Geometry>();
break;
default:
BLI_assert_unreachable();
break;
}
decl->name = item.name ? item.name : "";
decl->identifier = item.identifier_str();
decl->in_out = in_out;
return decl;
}
static void node_declare_dynamic(const bNodeTree & /*node_tree*/,
const bNode &node,
NodeDeclaration &r_declaration)
{
const NodeGeometryBake &storage = node_storage(node);
for (const int i : IndexRange(storage.items_num)) {
const NodeGeometryBakeItem &item = storage.items[i];
SocketDeclarationPtr input_decl = socket_declaration_for_bake_item(item, SOCK_IN);
SocketDeclarationPtr output_decl = socket_declaration_for_bake_item(item, SOCK_OUT, i);
r_declaration.inputs.append(input_decl.get());
r_declaration.items.append(std::move(input_decl));
r_declaration.outputs.append(output_decl.get());
r_declaration.items.append(std::move(output_decl));
}
SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN);
SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT);
r_declaration.inputs.append(input_extend_decl.get());
r_declaration.items.append(std::move(input_extend_decl));
r_declaration.outputs.append(output_extend_decl.get());
r_declaration.items.append(std::move(output_extend_decl));
aal::RelationsInNode &relations =
NodeDeclarationBuilder(r_declaration).get_anonymous_attribute_relations();
int last_geometry_index = -1;
for (const int i : IndexRange(storage.items_num)) {
const NodeGeometryBakeItem &item = storage.items[i];
if (item.socket_type == SOCK_GEOMETRY) {
last_geometry_index = i;
}
else if (last_geometry_index != -1) {
relations.eval_relations.append({i, last_geometry_index});
relations.available_relations.append({i, last_geometry_index});
}
}
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
NodeGeometryBake *data = MEM_cnew<NodeGeometryBake>(__func__);
data->next_identifier = 0;
data->items = MEM_cnew_array<NodeGeometryBakeItem>(1, __func__);
data->items[0].name = BLI_strdup(DATA_("Geometry"));
data->items[0].socket_type = SOCK_GEOMETRY;
data->items[0].identifier = data->next_identifier++;
data->items_num = 1;
node->storage = data;
}
static void node_free_storage(bNode *node)
{
NodeGeometryBake &storage = node_storage(*node);
for (NodeGeometryBakeItem &item : storage.items_span()) {
MEM_SAFE_FREE(item.name);
}
MEM_SAFE_FREE(storage.items);
MEM_freeN(node->storage);
}
static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
{
const NodeGeometryBake &src_storage = node_storage(*src_node);
NodeGeometryBake *dst_storage = MEM_new<NodeGeometryBake>(__func__, src_storage);
dst_storage->items = MEM_cnew_array<NodeGeometryBakeItem>(src_storage.items_num, __func__);
for (const int i : IndexRange(src_storage.items_num)) {
dst_storage->items[i] = src_storage.items[i];
if (dst_storage->items[i].name) {
dst_storage->items[i].name = BLI_strdup(dst_storage->items[i].name);
}
}
dst_node->storage = dst_storage;
}
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
{
NodeGeometryBake &storage = node_storage(*node);
if (link->tonode == node) {
if (link->tosock->identifier == StringRef("__extend__")) {
if (const NodeGeometryBakeItem *item = storage.add_item(
link->fromsock->name, eNodeSocketDatatype(link->fromsock->type)))
{
update_node_declaration_and_sockets(*ntree, *node);
link->tosock = nodeFindSocket(node, SOCK_IN, item->identifier_str().c_str());
return true;
}
}
else {
return true;
}
}
if (link->fromnode == node) {
if (link->fromsock->identifier == StringRef("__extend__")) {
if (const NodeGeometryBakeItem *item = storage.add_item(
link->tosock->name, eNodeSocketDatatype(link->tosock->type)))
{
update_node_declaration_and_sockets(*ntree, *node);
link->fromsock = nodeFindSocket(node, SOCK_OUT, item->identifier_str().c_str());
return true;
}
}
else {
return true;
}
}
return false;
}
static NodesModifierBake *get_bake(NodesModifierData &nmd, const int32_t bake_id)
{
for (NodesModifierBake &bake : MutableSpan(nmd.bakes, nmd.bakes_num)) {
if (bake.id == bake_id) {
return &bake;
}
}
return nullptr;
}
static void draw_bake_ui(uiLayout *layout,
Object &object,
NodesModifierData &nmd,
NodesModifierBake &bake)
{
PointerRNA bake_ptr;
RNA_pointer_create(&object.id, &RNA_NodesModifierBake, &bake, &bake_ptr);
uiLayout *settings_col = uiLayoutColumn(layout, false);
{
uiLayout *row = uiLayoutRow(settings_col, false);
uiItemR(row, &bake_ptr, "bake_type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
if (bake.bake_type == NODES_MODIFIER_BAKE_TYPE_ANIMATED) {
uiLayout *subcol = uiLayoutColumn(settings_col, true);
uiItemR(subcol, &bake_ptr, "frame_start", UI_ITEM_NONE, "Start", ICON_NONE);
uiItemR(subcol, &bake_ptr, "frame_end", UI_ITEM_NONE, "End", ICON_NONE);
}
uiItemR(settings_col, &bake_ptr, "directory", UI_ITEM_NONE, "", ICON_NONE);
uiLayout *row = uiLayoutRow(layout, true);
{
PointerRNA op_ptr;
uiItemFullO(row,
"OBJECT_OT_geometry_node_bake",
"Bake",
ICON_NONE,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&op_ptr);
WM_operator_properties_id_lookup_set_from_id(&op_ptr, &object.id);
RNA_string_set(&op_ptr, "modifier", nmd.modifier.name);
RNA_int_set(&op_ptr, "bake_id", bake.id);
}
{
PointerRNA op_ptr;
uiItemFullO(row,
"OBJECT_OT_geometry_node_bake_delete",
"",
ICON_TRASH,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&op_ptr);
WM_operator_properties_id_lookup_set_from_id(&op_ptr, &object.id);
RNA_string_set(&op_ptr, "modifier", nmd.modifier.name);
RNA_int_set(&op_ptr, "bake_id", bake.id);
}
}
static void node_layout(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
const bNode *node = static_cast<bNode *>(ptr->data);
SpaceNode *snode = CTX_wm_space_node(C);
if (snode == nullptr) {
return;
}
if (snode->id == nullptr) {
return;
}
if (GS(snode->id->name) != ID_OB) {
return;
}
Object *object = reinterpret_cast<Object *>(snode->id);
ModifierData *md = BKE_object_active_modifier(object);
if (md == nullptr || md->type != eModifierType_Nodes) {
return;
}
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
if (nmd.node_group != snode->nodetree) {
return;
}
const std::optional<int32_t> nested_node_id = ed::space_node::find_nested_node_id_in_root(*snode,
*node);
if (!nested_node_id.has_value()) {
return;
}
NodesModifierBake *bake = get_bake(nmd, *nested_node_id);
if (bake == nullptr) {
return;
}
draw_bake_ui(layout, *object, nmd, *bake);
}
static void bake_items_list_draw_item(uiList * /*ui_list*/,
const bContext *C,
uiLayout *layout,
PointerRNA * /*idataptr*/,
PointerRNA *itemptr,
int /*icon*/,
PointerRNA * /*active_dataptr*/,
const char * /*active_propname*/,
int /*index*/,
int /*flt_flag*/)
{
const NodeGeometryBakeItem &item = *static_cast<NodeGeometryBakeItem *>(itemptr->data);
float4 color;
const char *socket_type_idname = nodeStaticSocketType(item.socket_type, 0);
ED_node_type_draw_color(socket_type_idname, color);
uiLayout *row = uiLayoutRow(layout, true);
uiTemplateNodeSocket(row, const_cast<bContext *>(C), color);
uiLayoutSetEmboss(layout, UI_EMBOSS_NONE);
uiItemR(layout, itemptr, "name", UI_ITEM_NONE, "", ICON_NONE);
}
static uiListType *create_items_ui_list()
{
uiListType *items_list = MEM_cnew<uiListType>(__func__);
STRNCPY(items_list->idname, "NODE_UL_bake_items");
items_list->draw_item = bake_items_list_draw_item;
WM_uilisttype_add(items_list);
return items_list;
}
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
static const uiListType *items_list = create_items_ui_list();
PointerRNA items_ptr;
RNA_pointer_create(ptr->owner_id, &RNA_NodeGeometryBakeItems, ptr->data, &items_ptr);
uiLayout *list_row = uiLayoutRow(layout, false);
uiTemplateList(list_row,
C,
items_list->idname,
"",
ptr,
"bake_items",
ptr,
"active_index",
nullptr,
3,
5,
UILST_LAYOUT_DEFAULT,
0,
UI_TEMPLATE_LIST_FLAG_NONE);
{
uiLayout *list_ops_col = uiLayoutColumn(list_row, false);
uiLayout *add_remove_col = uiLayoutColumn(list_ops_col, true);
uiItemO(add_remove_col, "", ICON_ADD, "NODE_OT_bake_item_add");
uiItemO(add_remove_col, "", ICON_REMOVE, "NODE_OT_bake_item_remove");
uiItemS(list_ops_col);
uiLayout *up_down_col = uiLayoutColumn(list_ops_col, true);
uiItemEnumO(up_down_col, "NODE_OT_bake_item_move", "", ICON_TRIA_UP, "direction", 0);
uiItemEnumO(up_down_col, "NODE_OT_bake_item_move", "", ICON_TRIA_DOWN, "direction", 1);
}
bNode &node = *static_cast<bNode *>(ptr->data);
NodeGeometryBake &storage = node_storage(node);
if (storage.active_index < 0 || storage.active_index >= storage.items_num) {
return;
}
NodeGeometryBakeItem &active_item = storage.items[storage.active_index];
PointerRNA active_item_ptr;
RNA_pointer_create(ptr->owner_id, &RNA_NodeGeometryBakeItem, &active_item, &active_item_ptr);
uiItemR(layout, &active_item_ptr, "socket_type", UI_ITEM_NONE, nullptr, ICON_NONE);
if (ELEM(active_item.socket_type,
SOCK_BOOLEAN,
SOCK_INT,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_ROTATION))
{
uiItemR(layout, &active_item_ptr, "attribute_domain", UI_ITEM_NONE, nullptr, ICON_NONE);
}
}
class LazyFunctionForBakeNode : public LazyFunction {
const bNode &node_;
bke::bake::BakeSocketConfig bake_socket_config_;
public:
LazyFunctionForBakeNode(const bNode &node, GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
: node_(node)
{
debug_name_ = "Bake";
const NodeGeometryBake &storage = node_storage(node);
MutableSpan<int> lf_index_by_bsocket = own_lf_graph_info.mapping.lf_index_by_bsocket;
bake_socket_config_.types.resize(storage.items_num);
bake_socket_config_.domains.resize(storage.items_num);
bake_socket_config_.geometries_by_attribute.resize(storage.items_num);
int last_geometry_index = -1;
for (const int i : IndexRange(storage.items_num)) {
const NodeGeometryBakeItem &item = storage.items[i];
const bNodeSocket &input_bsocket = node.input_socket(i);
const bNodeSocket &output_bsocket = node.output_socket(i);
const CPPType &type = *input_bsocket.typeinfo->geometry_nodes_cpp_type;
lf_index_by_bsocket[input_bsocket.index_in_tree()] = inputs_.append_and_get_index_as(
item.name, type, lf::ValueUsage::Maybe);
lf_index_by_bsocket[output_bsocket.index_in_tree()] = outputs_.append_and_get_index_as(
item.name, type);
bake_socket_config_.types[i] = eNodeSocketDatatype(item.socket_type);
bake_socket_config_.domains[i] = eAttrDomain(item.attribute_domain);
if (item.socket_type == SOCK_GEOMETRY) {
last_geometry_index = i;
}
else if (last_geometry_index != -1) {
bake_socket_config_.geometries_by_attribute[i].append(last_geometry_index);
}
}
}
void execute_impl(lf::Params &params, const lf::Context &context) const final
{
params.set_default_remaining_outputs();
}
};
class LazyFunctionForBakeNodeInputUsage : public LazyFunction {
const bNode &node_;
public:
LazyFunctionForBakeNodeInputUsage(const bNode &node) : node_(node)
{
debug_name_ = "Bake Input Usage";
/* TODO: Handle case when not all outputs are required in pass-through mode. */
outputs_.append_as("Usage", CPPType::get<bool>());
}
void execute_impl(lf::Params &params, const lf::Context &context) const final
{
const bool inputs_required = this->bake_inputs_required(context);
params.set_output(0, inputs_required);
}
bool bake_inputs_required(const lf::Context & /*context*/) const
{
return true;
}
};
static void node_register()
{
namespace file_ns = blender::nodes::node_geo_bake_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_BAKE, "Bake", NODE_CLASS_GEOMETRY);
ntype.initfunc = file_ns::node_init;
ntype.declare_dynamic = file_ns::node_declare_dynamic;
ntype.draw_buttons = file_ns::node_layout;
ntype.draw_buttons_ex = file_ns::node_layout_ex;
ntype.insert_link = file_ns::node_insert_link;
node_type_storage(
&ntype, "NodeGeometryBake", file_ns::node_free_storage, file_ns::node_copy_storage);
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_geo_bake_cc
namespace blender::nodes {
std::unique_ptr<LazyFunction> get_bake_node_lazy_function(
const bNode &node, GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
{
namespace file_ns = blender::nodes::node_geo_bake_cc;
BLI_assert(node.type == GEO_NODE_BAKE);
return std::make_unique<file_ns::LazyFunctionForBakeNode>(node, own_lf_graph_info);
}
std::unique_ptr<LazyFunction> get_bake_node_input_usage_lazy_function(const bNode &node)
{
namespace file_ns = blender::nodes::node_geo_bake_cc;
BLI_assert(node.type == GEO_NODE_BAKE);
return std::make_unique<file_ns::LazyFunctionForBakeNodeInputUsage>(node);
}
} // namespace blender::nodes
blender::Span<NodeGeometryBakeItem> NodeGeometryBake::items_span() const
{
return blender::Span<NodeGeometryBakeItem>(items, items_num);
}
blender::MutableSpan<NodeGeometryBakeItem> NodeGeometryBake::items_span()
{
return blender::MutableSpan<NodeGeometryBakeItem>(items, items_num);
}
bool NodeGeometryBakeItem::supports_type(const eNodeSocketDatatype type)
{
return ELEM(type,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_INT,
SOCK_GEOMETRY);
}
std::string NodeGeometryBakeItem::identifier_str() const
{
return "Item_" + std::to_string(this->identifier);
}
NodeGeometryBakeItem *NodeGeometryBake::add_item(const char *name, const eNodeSocketDatatype type)
{
if (!NodeGeometryBakeItem::supports_type(type)) {
return nullptr;
}
const int insert_index = this->items_num;
NodeGeometryBakeItem *old_items = this->items;
this->items = MEM_cnew_array<NodeGeometryBakeItem>(this->items_num + 1, __func__);
std::copy_n(old_items, insert_index, this->items);
NodeGeometryBakeItem &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 NodeGeometryBake::set_item_name(NodeGeometryBakeItem &item, const char *name)
{
char unique_name[MAX_NAME + 4];
STRNCPY(unique_name, name);
struct Args {
NodeGeometryBake *storage;
const NodeGeometryBakeItem *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 NodeGeometryBakeItem &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);
}

View File

@ -2730,6 +2730,10 @@ struct GeometryNodesLazyFunctionGraphBuilder {
this->build_switch_node(bnode, graph_params);
break;
}
case GEO_NODE_BAKE: {
this->build_bake_node(bnode, graph_params);
break;
}
default: {
if (node_type->geometry_node_execute) {
this->build_geometry_node(bnode, graph_params);
@ -3286,6 +3290,46 @@ struct GeometryNodesLazyFunctionGraphBuilder {
}
}
void build_bake_node(const bNode &bnode, BuildGraphParams &graph_params)
{
std::unique_ptr<LazyFunction> lazy_function = get_bake_node_lazy_function(bnode,
*lf_graph_info_);
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function);
scope_.add(std::move(lazy_function));
const NodeGeometryBake &storage = *static_cast<const NodeGeometryBake *>(bnode.storage);
for (const int i : IndexRange(storage.items_num)) {
{
lf::InputSocket &lf_socket = lf_node.input(i);
const bNodeSocket &bsocket = bnode.input_socket(i);
graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket);
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
}
{
lf::OutputSocket &lf_socket = lf_node.output(i);
const bNodeSocket &bsocket = bnode.output_socket(i);
graph_params.lf_output_by_bsocket.add(&bsocket, &lf_socket);
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
}
}
mapping_->bake_node_map.add(&bnode, &lf_node);
this->build_bake_node_socket_usage(bnode, graph_params);
}
void build_bake_node_socket_usage(const bNode &bnode, BuildGraphParams &graph_params)
{
std::unique_ptr<LazyFunction> lazy_function = get_bake_node_input_usage_lazy_function(bnode);
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function);
scope_.add(std::move(lazy_function));
const NodeGeometryBake &storage = *static_cast<const NodeGeometryBake *>(bnode.storage);
for (const int i : IndexRange(storage.items_num)) {
graph_params.usage_by_bsocket.add(&bnode.input_socket(i), &lf_node.output(0));
}
}
void build_undefined_node(const bNode &bnode, BuildGraphParams &graph_params)
{
auto &lazy_function = scope_.construct<LazyFunctionForUndefinedNode>(