diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index aacd2ed4e91..15477093117 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -540,6 +540,16 @@ inline blender::MutableSpan bNodeTree::panels_for_write() return blender::MutableSpan(panels_array, panels_num); } +inline blender::MutableSpan bNodeTree::nested_node_refs_span() +{ + return {this->nested_node_refs, this->nested_node_refs_num}; +} + +inline blender::Span bNodeTree::nested_node_refs_span() const +{ + return {this->nested_node_refs, this->nested_node_refs_num}; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/BKE_simulation_state.hh b/source/blender/blenkernel/BKE_simulation_state.hh index 5595523af8b..d6c4e246a88 100644 --- a/source/blender/blenkernel/BKE_simulation_state.hh +++ b/source/blender/blenkernel/BKE_simulation_state.hh @@ -9,6 +9,8 @@ #include "BLI_map.hh" #include "BLI_sub_frame.hh" +struct bNodeTree; + namespace blender::bke::sim { class BDataSharing; @@ -89,17 +91,17 @@ class SimulationZoneState { /** Identifies a simulation zone (input and output node pair) used by a modifier. */ struct SimulationZoneID { - /** Every node identifier in the hierarchy of compute contexts. */ - Vector node_ids; + /** ID of the #bNestedNodeRef that references the output node of the zone. */ + int32_t nested_node_id; uint64_t hash() const { - return get_default_hash(this->node_ids); + return this->nested_node_id; } friend bool operator==(const SimulationZoneID &a, const SimulationZoneID &b) { - return a.node_ids == b.node_ids; + return a.nested_node_id == b.nested_node_id; } }; @@ -122,7 +124,7 @@ class ModifierSimulationState { const SimulationZoneState *get_zone_state(const SimulationZoneID &zone_id) const; SimulationZoneState &get_zone_state_for_write(const SimulationZoneID &zone_id); - void ensure_bake_loaded() const; + void ensure_bake_loaded(const bNodeTree &ntree) const; }; struct ModifierSimulationStateAtFrame { diff --git a/source/blender/blenkernel/BKE_simulation_state_serialize.hh b/source/blender/blenkernel/BKE_simulation_state_serialize.hh index 39664de2d2f..9b64686bf75 100644 --- a/source/blender/blenkernel/BKE_simulation_state_serialize.hh +++ b/source/blender/blenkernel/BKE_simulation_state_serialize.hh @@ -163,7 +163,8 @@ void serialize_modifier_simulation_state(const ModifierSimulationState &state, * Fill the simulation state by parsing the provided #DictionaryValue which also contains * references to external binary data that is read using #bdata_reader. */ -void deserialize_modifier_simulation_state(const DictionaryValue &io_root, +void deserialize_modifier_simulation_state(const bNodeTree &ntree, + const DictionaryValue &io_root, const BDataReader &bdata_reader, const BDataSharing &bdata_sharing, ModifierSimulationState &r_state); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index e94fe33989f..d12abb46b1d 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -253,6 +253,13 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons } } + if (ntree_src->nested_node_refs) { + ntree_dst->nested_node_refs = static_cast( + MEM_malloc_arrayN(ntree_src->nested_node_refs_num, sizeof(bNestedNodeRef), __func__)); + uninitialized_copy_n( + ntree_src->nested_node_refs, ntree_src->nested_node_refs_num, ntree_dst->nested_node_refs); + } + if (flag & LIB_ID_COPY_NO_PREVIEW) { ntree_dst->preview = nullptr; } @@ -316,6 +323,10 @@ static void ntree_free_data(ID *id) BKE_libblock_free_data(&ntree->id, true); } + if (ntree->nested_node_refs) { + MEM_freeN(ntree->nested_node_refs); + } + BKE_previewimg_free(&ntree->preview); MEM_delete(ntree->runtime); } @@ -684,6 +695,9 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) BLO_write_string(writer, panel->name); } + BLO_write_struct_array( + writer, bNestedNodeRef, ntree->nested_node_refs_num, ntree->nested_node_refs); + BKE_previewimg_blend_write(writer, ntree->preview); } @@ -911,6 +925,8 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) BLO_read_data_address(reader, &ntree->panels_array[i]->name); } + BLO_read_data_address(reader, &ntree->nested_node_refs); + /* TODO: should be dealt by new generic cache handling of IDs... */ ntree->previews = nullptr; diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index 1e28a5130b9..c6ce1121355 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -554,3 +554,53 @@ void bNodeTree::ensure_topology_cache() const { blender::bke::node_tree_runtime::ensure_topology_cache(*this); } + +const bNestedNodeRef *bNodeTree::find_nested_node_ref(const int32_t nested_node_id) const +{ + for (const bNestedNodeRef &ref : this->nested_node_refs_span()) { + if (ref.id == nested_node_id) { + return &ref; + } + } + return nullptr; +} + +const bNestedNodeRef *bNodeTree::nested_node_ref_from_node_id_path( + const blender::Span node_ids) const +{ + if (node_ids.is_empty()) { + return nullptr; + } + for (const bNestedNodeRef &ref : this->nested_node_refs_span()) { + blender::Vector current_node_ids; + if (this->node_id_path_from_nested_node_ref(ref.id, current_node_ids)) { + if (current_node_ids.as_span() == node_ids) { + return &ref; + } + } + } + return nullptr; +} + +bool bNodeTree::node_id_path_from_nested_node_ref(const int32_t nested_node_id, + blender::Vector &r_node_ids) const +{ + const bNestedNodeRef *ref = this->find_nested_node_ref(nested_node_id); + if (ref == nullptr) { + return false; + } + const int32_t node_id = ref->path.node_id; + const bNode *node = this->node_by_id(node_id); + if (node == nullptr) { + return false; + } + r_node_ids.append(node_id); + if (!node->is_group()) { + return true; + } + const bNodeTree *group = reinterpret_cast(node->id); + if (group == nullptr) { + return false; + } + return group->node_id_path_from_nested_node_ref(ref->path.id_in_node, r_node_ids); +} diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index f6d3b3daa88..4032b48116e 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -5,11 +5,14 @@ #include "BLI_map.hh" #include "BLI_multi_value_map.hh" #include "BLI_noise.hh" +#include "BLI_rand.hh" #include "BLI_set.hh" #include "BLI_stack.hh" #include "BLI_timeit.hh" #include "BLI_vector_set.hh" +#include "PIL_time.h" + #include "DNA_anim_types.h" #include "DNA_modifier_types.h" #include "DNA_node_types.h" @@ -486,6 +489,10 @@ class NodeTreeMainUpdater { this->update_socket_link_and_use(ntree); this->update_link_validation(ntree); + if (this->update_nested_node_refs(ntree)) { + result.interface_changed = true; + } + if (ntree.type == NTREE_TEXTURE) { ntreeTexCheckCyclics(&ntree); } @@ -1086,6 +1093,115 @@ class NodeTreeMainUpdater { return false; } + /** + * Make sure that the #bNodeTree::nested_node_refs is up to date. It's supposed to contain a + * reference to all (nested) simulation zones. + */ + bool update_nested_node_refs(bNodeTree &ntree) + { + ntree.ensure_topology_cache(); + + /* Simplify lookup of old ids. */ + Map old_id_by_path; + Set 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); + } + + Vector nested_node_paths; + + /* Don't forget nested node refs just because the linked file is not available right now. */ + for (const bNestedNodePath &path : old_id_by_path.keys()) { + const bNode *node = ntree.node_by_id(path.node_id); + if (node && node->is_group() && node->id) { + if (node->id->tag & LIB_TAG_MISSING) { + nested_node_paths.append(path); + } + } + } + if (ntree.type == NTREE_GEOMETRY) { + /* Create references for simulations in geometry nodes. */ + for (const bNode *node : ntree.nodes_by_type("GeometryNodeSimulationOutput")) { + nested_node_paths.append({node->identifier, -1}); + } + } + /* Propagate references to nested nodes in group nodes. */ + for (const bNode *node : ntree.group_nodes()) { + const bNodeTree *group = reinterpret_cast(node->id); + if (group == nullptr) { + continue; + } + for (const int i : group->nested_node_refs_span().index_range()) { + const bNestedNodeRef &child_ref = group->nested_node_refs[i]; + nested_node_paths.append({node->identifier, child_ref.id}); + } + } + + /* Used to generate new unique IDs if necessary. */ + RandomNumberGenerator rng(int(PIL_check_seconds_timer() * 1000000.0)); + + Map new_path_by_id; + for (const bNestedNodePath &path : nested_node_paths) { + const int32_t old_id = old_id_by_path.lookup_default(path, -1); + if (old_id != -1) { + /* The same path existed before, it should keep the same ID as before. */ + new_path_by_id.add(old_id, path); + continue; + } + 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)) { + break; + } + } + /* The path is new, it should get a new ID that does not collide with any existing IDs. */ + new_path_by_id.add(new_id, path); + } + + /* Check if the old and new references are identical. */ + if (!this->nested_node_refs_changed(ntree, new_path_by_id)) { + return false; + } + + MEM_SAFE_FREE(ntree.nested_node_refs); + if (new_path_by_id.is_empty()) { + ntree.nested_node_refs_num = 0; + return true; + } + + /* Allocate new array for the nested node references contained in the node tree. */ + bNestedNodeRef *new_refs = static_cast( + MEM_malloc_arrayN(new_path_by_id.size(), sizeof(bNestedNodeRef), __func__)); + int index = 0; + for (const auto item : new_path_by_id.items()) { + bNestedNodeRef &ref = new_refs[index]; + ref.id = item.key; + ref.path = item.value; + index++; + } + + ntree.nested_node_refs = new_refs; + ntree.nested_node_refs_num = new_path_by_id.size(); + + return true; + } + + bool nested_node_refs_changed(const bNodeTree &ntree, + const Map &new_path_by_id) + { + if (ntree.nested_node_refs_num != new_path_by_id.size()) { + return true; + } + for (const bNestedNodeRef &ref : ntree.nested_node_refs_span()) { + if (!new_path_by_id.contains(ref.id)) { + return true; + } + } + return false; + } + void reset_changed_flags(bNodeTree &ntree) { ntree.runtime->changed_flag = NTREE_CHANGED_NOTHING; @@ -1227,6 +1343,16 @@ void BKE_ntree_update_tag_image_user_changed(bNodeTree *ntree, ImageUser * /*ius add_tree_tag(ntree, NTREE_CHANGED_ANY); } +uint64_t bNestedNodePath::hash() const +{ + return blender::get_default_hash_2(this->node_id, this->id_in_node); +} + +bool operator==(const bNestedNodePath &a, const bNestedNodePath &b) +{ + return a.node_id == b.node_id && a.id_in_node == b.id_in_node; +} + /** * Protect from recursive calls into the updating function. Some node update functions might * trigger this from Python or in other cases. diff --git a/source/blender/blenkernel/intern/simulation_state.cc b/source/blender/blenkernel/intern/simulation_state.cc index 8ec34a28f39..5a9e466b6c9 100644 --- a/source/blender/blenkernel/intern/simulation_state.cc +++ b/source/blender/blenkernel/intern/simulation_state.cc @@ -202,7 +202,7 @@ SimulationZoneState &ModifierSimulationState::get_zone_state_for_write( []() { return std::make_unique(); }); } -void ModifierSimulationState::ensure_bake_loaded() const +void ModifierSimulationState::ensure_bake_loaded(const bNodeTree &ntree) const { std::scoped_lock lock{mutex_}; if (bake_loaded_) { @@ -223,7 +223,8 @@ void ModifierSimulationState::ensure_bake_loaded() const } const DiskBDataReader bdata_reader{*bdata_dir_}; - deserialize_modifier_simulation_state(*io_root, + deserialize_modifier_simulation_state(ntree, + *io_root, bdata_reader, *owner_->bdata_sharing_, const_cast(*this)); diff --git a/source/blender/blenkernel/intern/simulation_state_serialize.cc b/source/blender/blenkernel/intern/simulation_state_serialize.cc index 8a5c78571e3..a181569b70a 100644 --- a/source/blender/blenkernel/intern/simulation_state_serialize.cc +++ b/source/blender/blenkernel/intern/simulation_state_serialize.cc @@ -7,6 +7,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_mesh.hh" +#include "BKE_node_runtime.hh" #include "BKE_pointcloud.h" #include "BKE_simulation_state_serialize.hh" @@ -790,12 +791,17 @@ static std::shared_ptr serialize_primitive_value( return {}; } +/** + * Version written to the baked data. + */ +static constexpr int serialize_format_version = 2; + void serialize_modifier_simulation_state(const ModifierSimulationState &state, BDataWriter &bdata_writer, BDataSharing &bdata_sharing, DictionaryValue &r_io_root) { - r_io_root.append_int("version", 1); + r_io_root.append_int("version", serialize_format_version); auto io_zones = r_io_root.append_array("zones"); for (const auto item : state.zone_states_.items()) { @@ -804,11 +810,7 @@ void serialize_modifier_simulation_state(const ModifierSimulationState &state, auto io_zone = io_zones->append_dict(); - auto io_zone_id = io_zone->append_array("zone_id"); - - for (const int node_id : zone_id.node_ids) { - io_zone_id->append_int(node_id); - } + io_zone->append_int("state_id", zone_id.nested_node_id); auto io_state_items = io_zone->append_array("state_items"); for (const MapItem> &state_item_with_id : @@ -980,7 +982,8 @@ template return false; } -void deserialize_modifier_simulation_state(const DictionaryValue &io_root, +void deserialize_modifier_simulation_state(const bNodeTree &ntree, + const DictionaryValue &io_root, const BDataReader &bdata_reader, const BDataSharing &bdata_sharing, ModifierSimulationState &r_state) @@ -990,7 +993,7 @@ void deserialize_modifier_simulation_state(const DictionaryValue &io_root, if (!version) { return; } - if (*version != 1) { + if (*version > serialize_format_version) { return; } const io::serialize::ArrayValue *io_zones = io_root.lookup_array("zones"); @@ -1002,14 +1005,27 @@ void deserialize_modifier_simulation_state(const DictionaryValue &io_root, if (!io_zone) { continue; } - const io::serialize::ArrayValue *io_zone_id = io_zone->lookup_array("zone_id"); bke::sim::SimulationZoneID zone_id; - for (const auto &io_zone_id_element : io_zone_id->elements()) { - const io::serialize::IntValue *io_node_id = io_zone_id_element->as_int_value(); - if (!io_node_id) { + if (const std::optional state_id = io_zone->lookup_int("state_id")) { + zone_id.nested_node_id = *state_id; + } + else if (const io::serialize::ArrayValue *io_zone_id = io_zone->lookup_array("zone_id")) { + /* In the initial release of simulation nodes, the entire node id path was written to the + * baked data. For backward compatibility the node ids are read here and then the nested node + * id is looked up. */ + Vector node_ids; + for (const auto &io_zone_id_element : io_zone_id->elements()) { + const io::serialize::IntValue *io_node_id = io_zone_id_element->as_int_value(); + if (!io_node_id) { + continue; + } + node_ids.append(io_node_id->value()); + } + const bNestedNodeRef *nested_node_ref = ntree.nested_node_ref_from_node_id_path(node_ids); + if (!nested_node_ref) { continue; } - zone_id.node_ids.append(io_node_id->value()); + zone_id.nested_node_id = nested_node_ref->id; } const io::serialize::ArrayValue *io_state_items = io_zone->lookup_array("state_items"); diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index a9751205f6a..b25496c7a34 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -17,10 +17,13 @@ #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_math_vector_types.hh" +#include "BLI_rand.hh" #include "BLI_set.hh" #include "BLI_string.h" #include "BLI_vector.hh" +#include "PIL_time.h" + #include "BLT_translation.h" #include "BKE_action.h" @@ -237,6 +240,30 @@ static void animation_basepath_change_free(AnimationBasePathChange *basepath_cha MEM_freeN(basepath_change); } +static void update_nested_node_refs_after_ungroup(bNodeTree &ntree, + const bNodeTree &ngroup, + const bNode &gnode, + const Map &node_identifier_map) +{ + for (bNestedNodeRef &ref : ntree.nested_node_refs_span()) { + if (ref.path.node_id != gnode.identifier) { + continue; + } + const bNestedNodeRef *child_ref = ngroup.find_nested_node_ref(ref.path.id_in_node); + if (!child_ref) { + continue; + } + constexpr int32_t missing_id = -1; + const int32_t new_node_id = node_identifier_map.lookup_default(child_ref->path.node_id, + missing_id); + if (new_node_id == missing_id) { + continue; + } + ref.path.node_id = new_node_id; + ref.path.id_in_node = child_ref->path.id_in_node; + } +} + /** * \return True if successful. */ @@ -421,6 +448,8 @@ static bool node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) /* delete the group instance and dereference group tree */ nodeRemoveNode(bmain, ntree, gnode, true); + update_nested_node_refs_after_ungroup(*ntree, *ngroup, *gnode, node_identifier_map); + return true; } @@ -852,6 +881,54 @@ static bNodeSocket *add_interface_from_socket(const bNodeTree &original_tree, socket_for_name.name); } +static void update_nested_node_refs_after_moving_nodes_into_group( + bNodeTree &ntree, + bNodeTree &group, + bNode &gnode, + const Map &node_identifier_map) +{ + /* Update nested node references in the parent and child node tree. */ + RandomNumberGenerator rng(int(PIL_check_seconds_timer() * 1000000.0)); + Vector new_nested_node_refs; + /* Keep all nested node references that were in the group before. */ + for (const bNestedNodeRef &state_id : group.nested_node_refs_span()) { + new_nested_node_refs.append(state_id); + } + Set used_nested_node_ref_ids; + for (const bNestedNodeRef &ref : group.nested_node_refs_span()) { + used_nested_node_ref_ids.add(ref.id); + } + Map new_id_by_old_path; + for (bNestedNodeRef &state_id : ntree.nested_node_refs_span()) { + const int32_t new_node_id = node_identifier_map.lookup_default(state_id.path.node_id, -1); + if (new_node_id == -1) { + /* The node was not moved between node groups. */ + continue; + } + bNestedNodeRef new_state_id = state_id; + new_state_id.path.node_id = new_node_id; + /* Find new unique identifier for the nested node ref. */ + while (true) { + const int32_t new_id = rng.get_int32(INT32_MAX); + if (used_nested_node_ref_ids.add(new_id)) { + new_state_id.id = new_id; + break; + } + } + new_id_by_old_path.add_new(state_id.path, new_state_id.id); + new_nested_node_refs.append(new_state_id); + /* Updated the nested node ref in the parent so that it points to the same node that is now + * inside of a nested group. */ + state_id.path.node_id = gnode.identifier; + state_id.path.id_in_node = new_state_id.id; + } + MEM_SAFE_FREE(group.nested_node_refs); + group.nested_node_refs = static_cast( + MEM_malloc_arrayN(new_nested_node_refs.size(), sizeof(bNestedNodeRef), __func__)); + uninitialized_copy_n( + new_nested_node_refs.data(), new_nested_node_refs.size(), group.nested_node_refs); +} + static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, bNode *gnode, @@ -1103,6 +1180,8 @@ static void node_group_make_insert_selected(const bContext &C, info.link->fromsock = node_group_find_output_socket(gnode, info.interface_socket->identifier); } + update_nested_node_refs_after_moving_nodes_into_group(ntree, group, *gnode, node_identifier_map); + ED_node_tree_propagate_change(&C, bmain, nullptr); } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 2f27db5912a..0dd3276d5dc 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -15,6 +15,8 @@ /** Workaround to forward-declare C++ type in C header. */ #ifdef __cplusplus +# include + namespace blender { template class Span; template class MutableSpan; @@ -568,6 +570,27 @@ typedef struct bNodePanel { char *name; } bNodePanel; +typedef struct bNestedNodePath { + /** ID of the node that is or contains the nested node. */ + int32_t node_id; + /** Unused if the node is the final nested node, otherwise an id inside of the (group) node. */ + int32_t id_in_node; + +#ifdef __cplusplus + uint64_t hash() const; + friend bool operator==(const bNestedNodePath &a, const bNestedNodePath &b); +#endif +} bNestedNodePath; + +typedef struct bNestedNodeRef { + /** Identifies a potentially nested node. This ID remains stable even if the node is moved into + * and out of node groups. */ + int32_t id; + char _pad[4]; + /** Where to find the nested node in the current node tree. */ + bNestedNodePath path; +} bNestedNodeRef; + /** * The basis for a Node tree, all links and nodes reside internal here. * @@ -632,7 +655,12 @@ typedef struct bNodeTree { */ bNodeInstanceKey active_viewer_key; - char _pad[4]; + /** + * Used to maintain stable IDs for a subset of nested nodes. For example, every simulation zone + * that is in the node tree has a unique entry here. + */ + int nested_node_refs_num; + bNestedNodeRef *nested_node_refs; /** Image representing what the node group does. */ struct PreviewImage *preview; @@ -654,6 +682,15 @@ typedef struct bNodeTree { struct bNode *node_by_id(int32_t identifier); const struct bNode *node_by_id(int32_t identifier) const; + blender::MutableSpan nested_node_refs_span(); + blender::Span nested_node_refs_span() const; + + 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 node_ids) const; + [[nodiscard]] bool node_id_path_from_nested_node_ref(const int32_t nested_node_id, + blender::Vector &r_node_ids) const; + /** * Update a run-time cache for the node tree based on it's current state. This makes many methods * available which allow efficient lookup for topology information (like neighboring sockets). diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index f39963e20e1..9bef790e33e 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -737,14 +737,14 @@ static void prepare_simulation_states_for_evaluation(const NodesModifierData &nm const bke::sim::StatesAroundFrame sim_states = simulation_cache.get_states_around_frame( current_frame); if (sim_states.current) { - sim_states.current->state.ensure_bake_loaded(); + sim_states.current->state.ensure_bake_loaded(*nmd.node_group); exec_data.current_simulation_state = &sim_states.current->state; } if (sim_states.prev) { - sim_states.prev->state.ensure_bake_loaded(); + sim_states.prev->state.ensure_bake_loaded(*nmd.node_group); exec_data.prev_simulation_state = &sim_states.prev->state; if (sim_states.next) { - sim_states.next->state.ensure_bake_loaded(); + sim_states.next->state.ensure_bake_loaded(*nmd.node_group); exec_data.next_simulation_state = &sim_states.next->state; exec_data.simulation_state_mix_factor = (float(current_frame) - float(sim_states.prev->frame)) / diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh index 78763d4e6f6..c0075b64398 100644 --- a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -102,6 +102,10 @@ struct GeoNodesLFUserData : public lf::UserData { * Log socket values in the current compute context. Child contexts might use logging again. */ bool log_socket_values = true; + /** + * Top-level node tree of the current evaluation. + */ + const bNodeTree *root_ntree = nullptr; destruct_ptr get_local(LinearAllocator<> &allocator) override; }; @@ -252,7 +256,7 @@ std::unique_ptr get_simulation_input_lazy_function( GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info); std::unique_ptr get_switch_node_lazy_function(const bNode &node); -bke::sim::SimulationZoneID get_simulation_zone_id(const ComputeContext &context, +bke::sim::SimulationZoneID get_simulation_zone_id(const GeoNodesLFUserData &user_data, const int output_node_id); /** diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index 0bf580e4f0b..240bb63a30b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -70,8 +70,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { params.set_output(0, fn::ValueOrField(delta_time)); } - const bke::sim::SimulationZoneID zone_id = get_simulation_zone_id(*user_data.compute_context, - output_node_id_); + const bke::sim::SimulationZoneID zone_id = get_simulation_zone_id(user_data, output_node_id_); const bke::sim::SimulationZoneState *prev_zone_state = modifier_data.prev_simulation_state == nullptr ? diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index fe05268c1c9..a6c8ebf09a7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -693,8 +693,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { EvalData &eval_data = *static_cast(context.storage); BLI_SCOPED_DEFER([&]() { eval_data.is_first_evaluation = false; }); - const bke::sim::SimulationZoneID zone_id = get_simulation_zone_id(*user_data.compute_context, - node_.identifier); + const bke::sim::SimulationZoneID zone_id = get_simulation_zone_id(user_data, node_.identifier); const bke::sim::SimulationZoneState *current_zone_state = modifier_data.current_simulation_state ? @@ -823,19 +822,23 @@ std::unique_ptr get_simulation_output_lazy_function( return std::make_unique(node, own_lf_graph_info); } -bke::sim::SimulationZoneID get_simulation_zone_id(const ComputeContext &compute_context, +bke::sim::SimulationZoneID get_simulation_zone_id(const GeoNodesLFUserData &user_data, const int output_node_id) { - bke::sim::SimulationZoneID zone_id; - for (const ComputeContext *context = &compute_context; context != nullptr; + Vector node_ids; + for (const ComputeContext *context = user_data.compute_context; context != nullptr; context = context->parent()) { if (const auto *node_context = dynamic_cast(context)) { - zone_id.node_ids.append(node_context->node_id()); + node_ids.append(node_context->node_id()); } } - std::reverse(zone_id.node_ids.begin(), zone_id.node_ids.end()); - zone_id.node_ids.append(output_node_id); + std::reverse(node_ids.begin(), node_ids.end()); + node_ids.append(output_node_id); + const bNestedNodeRef *nested_node_ref = user_data.root_ntree->nested_node_ref_from_node_id_path( + node_ids); + bke::sim::SimulationZoneID zone_id; + zone_id.nested_node_id = nested_node_ref->id; return zone_id; } diff --git a/source/blender/nodes/intern/geometry_nodes_execute.cc b/source/blender/nodes/intern/geometry_nodes_execute.cc index 374368b39a5..12a045556e6 100644 --- a/source/blender/nodes/intern/geometry_nodes_execute.cc +++ b/source/blender/nodes/intern/geometry_nodes_execute.cc @@ -564,6 +564,7 @@ bke::GeometrySet execute_geometry_nodes_on_geometry( nodes::GeoNodesLFUserData user_data; fill_user_data(user_data); + user_data.root_ntree = &btree; user_data.compute_context = &base_compute_context; LinearAllocator<> allocator;