diff options
27 files changed, 634 insertions, 57 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 50c1a2e4e4e..60eb0194a9a 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -485,6 +485,7 @@ simulation_node_categories = [ NodeItem("SimulationNodeParticleAttribute"), NodeItem("FunctionNodeGroupInstanceID"), NodeItem("ShaderNodeValue"), + NodeItem("FunctionNodeObjectTransforms"), ]), SimulationNodeCategory("SIM_EMITTERS", "Emitters", items=[ NodeItem("SimulationNodeParticleMeshEmitter"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 1d01436c7e4..4c55488ecd5 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1343,6 +1343,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_FLOAT_COMPARE 1202 #define FN_NODE_GROUP_INSTANCE_ID 1203 #define FN_NODE_COMBINE_STRINGS 1204 +#define FN_NODE_OBJECT_TRANSFORMS 1205 /** \} */ diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh new file mode 100644 index 00000000000..721132560e3 --- /dev/null +++ b/source/blender/blenkernel/BKE_persistent_data_handle.hh @@ -0,0 +1,132 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BKE_PERSISTENT_DATA_HANDLE_H__ +#define __BKE_PERSISTENT_DATA_HANDLE_H__ + +/** \file + * \ingroup bke + * + * A PersistentDataHandle is a weak reference to some data in a Blender file. The handle itself is + * just a number. A PersistentDataHandleMap is used to convert between handles and the actual data. + */ + +#include "BLI_map.hh" + +#include "DNA_ID.h" + +struct Object; + +namespace blender::bke { + +class PersistentDataHandleMap; + +class PersistentDataHandle { + private: + /* Negative values indicate that the handle is "empty". */ + int32_t handle_; + + friend PersistentDataHandleMap; + + protected: + PersistentDataHandle(int handle) : handle_(handle) + { + } + + public: + PersistentDataHandle() : handle_(-1) + { + } + + friend bool operator==(const PersistentDataHandle &a, const PersistentDataHandle &b) + { + return a.handle_ == b.handle_; + } + + friend bool operator!=(const PersistentDataHandle &a, const PersistentDataHandle &b) + { + return !(a == b); + } + + friend std::ostream &operator<<(std::ostream &stream, const PersistentDataHandle &a) + { + stream << a.handle_; + return stream; + } + + uint64_t hash() const + { + return (uint64_t)handle_; + } +}; + +class PersistentIDHandle : public PersistentDataHandle { + friend PersistentDataHandleMap; + using PersistentDataHandle::PersistentDataHandle; +}; + +class PersistentObjectHandle : public PersistentIDHandle { + friend PersistentDataHandleMap; + using PersistentIDHandle::PersistentIDHandle; +}; + +class PersistentDataHandleMap { + private: + Map<int32_t, const ID *> id_by_handle_; + Map<const ID *, int32_t> handle_by_id_; + + public: + void add(int32_t handle, const ID &id) + { + BLI_assert(handle >= 0); + handle_by_id_.add(&id, handle); + id_by_handle_.add(handle, &id); + } + + PersistentIDHandle lookup(const ID *id) const + { + const int handle = handle_by_id_.lookup_default(id, -1); + return PersistentIDHandle(handle); + } + + PersistentObjectHandle lookup(const Object *object) const + { + const int handle = handle_by_id_.lookup_default((const ID *)object, -1); + return PersistentObjectHandle(handle); + } + + const ID *lookup(const PersistentIDHandle &handle) const + { + const ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr); + return id; + } + + const Object *lookup(const PersistentObjectHandle &handle) const + { + const ID *id = this->lookup((const PersistentIDHandle &)handle); + if (id == nullptr) { + return nullptr; + } + if (GS(id->name) != ID_OB) { + return nullptr; + } + return (const Object *)id; + } +}; + +} // namespace blender::bke + +#endif /* __BKE_PERSISTENT_DATA_HANDLE_H__ */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 800fa7d18ee..d702da55ea8 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -363,6 +363,7 @@ set(SRC BKE_packedFile.h BKE_paint.h BKE_particle.h + BKE_persistent_data_handle.hh BKE_pbvh.h BKE_pointcache.h BKE_pointcloud.h diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 52f0d259058..20d65e52b09 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -4345,6 +4345,7 @@ static void registerFunctionNodes(void) register_node_type_fn_switch(); register_node_type_fn_group_instance_id(); register_node_type_fn_combine_strings(); + register_node_type_fn_object_transforms(); } void init_nodesystem(void) diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 1ac987d130d..1f11869e21c 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -89,6 +89,9 @@ static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons } BLI_listbase_clear(&simulation_dst->states); + + BLI_duplicatelist(&simulation_dst->persistent_data_handles, + &simulation_src->persistent_data_handles); } static void free_simulation_state_head(SimulationState *state) @@ -169,6 +172,8 @@ static void simulation_free_data(ID *id) } BKE_simulation_state_remove_all(simulation); + + BLI_freelistN(&simulation->persistent_data_handles); } static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) @@ -178,6 +183,10 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree); } + LISTBASE_FOREACH ( + PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { + BKE_LIB_FOREACHID_PROCESS_ID(data, handle_item->id, IDWALK_CB_USER); + } } IDTypeInfo IDType_ID_SIM = { diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 8bfab730313..f443859e62c 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -8675,8 +8675,12 @@ static void direct_link_volume(BlendDataReader *reader, Volume *volume) /** \name Read ID: Simulation * \{ */ -static void lib_link_simulation(BlendLibReader *UNUSED(reader), Simulation *UNUSED(simulation)) +static void lib_link_simulation(BlendLibReader *reader, Simulation *simulation) { + LISTBASE_FOREACH ( + PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { + BLO_read_id_address(reader, simulation->id.lib, &handle_item->id); + } } static void direct_link_simulation(BlendDataReader *reader, Simulation *simulation) @@ -8697,6 +8701,8 @@ static void direct_link_simulation(BlendDataReader *reader, Simulation *simulati }; } } + + BLO_read_list(reader, &simulation->persistent_data_handles); } /** \} */ @@ -11105,6 +11111,10 @@ static void expand_simulation(BlendExpander *expander, Simulation *simulation) if (simulation->adt) { expand_animdata(expander, simulation->adt); } + LISTBASE_FOREACH ( + PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { + BLO_expand(expander, handle_item->id); + } } /** diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 8b9de3eea24..d33d8032ed1 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -3859,6 +3859,8 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const } } } + + BLO_write_struct_list(writer, PersistentDataHandleItem, &simulation->persistent_data_handles); } } diff --git a/source/blender/functions/FN_multi_function.hh b/source/blender/functions/FN_multi_function.hh index 77ab2749377..eaddcee7964 100644 --- a/source/blender/functions/FN_multi_function.hh +++ b/source/blender/functions/FN_multi_function.hh @@ -98,6 +98,11 @@ class MultiFunction { return signature_.function_name; } + bool depends_on_context() const + { + return signature_.depends_on_context; + } + const MFSignature &signature() const { return signature_; diff --git a/source/blender/functions/FN_multi_function_context.hh b/source/blender/functions/FN_multi_function_context.hh index 3a448cc2c6e..8492fd86742 100644 --- a/source/blender/functions/FN_multi_function_context.hh +++ b/source/blender/functions/FN_multi_function_context.hh @@ -29,15 +29,39 @@ #include "BLI_utildefines.h" +#include "BLI_map.hh" + namespace blender::fn { +class MFContext; + class MFContextBuilder { + private: + Map<std::string, const void *> global_contexts_; + + friend MFContext; + + public: + template<typename T> void add_global_context(std::string name, const T *context) + { + global_contexts_.add_new(std::move(name), (const void *)context); + } }; class MFContext { + private: + MFContextBuilder &builder_; + public: - MFContext(MFContextBuilder &UNUSED(builder)) + MFContext(MFContextBuilder &builder) : builder_(builder) + { + } + + template<typename T> const T *get_global_context(StringRef name) const { + const void *context = builder_.global_contexts_.lookup_default_as(name, nullptr); + /* TODO: Implement type checking. */ + return (const T *)context; } }; diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh index f6d6c7417e7..20f8fb2ee43 100644 --- a/source/blender/functions/FN_multi_function_network.hh +++ b/source/blender/functions/FN_multi_function_network.hh @@ -95,7 +95,7 @@ class MFNode : NonCopyable, NonMovable { Span<MFOutputSocket *> outputs(); Span<const MFOutputSocket *> outputs() const; - bool all_inputs_have_origin() const; + bool has_unlinked_inputs() const; private: void destruct_sockets(); @@ -341,14 +341,14 @@ inline Span<const MFOutputSocket *> MFNode::outputs() const return outputs_; } -inline bool MFNode::all_inputs_have_origin() const +inline bool MFNode::has_unlinked_inputs() const { for (const MFInputSocket *socket : inputs_) { if (socket->origin() == nullptr) { - return false; + return true; } } - return true; + return false; } /* -------------------------------------------------------------------- diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 9cce8bb7401..93d7b47af83 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -68,6 +68,10 @@ class MFParamsBuilder { virtual_array_spans_.append(ref); } + template<typename T> void add_uninitialized_single_output(T *value) + { + this->add_uninitialized_single_output(GMutableSpan(CPPType::get<T>(), value, 1)); + } void add_uninitialized_single_output(GMutableSpan ref) { this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type())); diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index af5f61fe2ee..26df7c98e4a 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -36,6 +36,7 @@ struct MFSignature { RawVector<std::string> param_names; RawVector<MFParamType> param_types; RawVector<int> param_data_indices; + bool depends_on_context = false; int data_index(int param_index) const { @@ -157,6 +158,15 @@ class MFSignatureBuilder { break; } } + + /* Context */ + + /** This indicates that the function accesses the context. This disables optimizations that + * depend on the fact that the function always performes the same operation. */ + void depends_on_context() + { + data_.depends_on_context = true; + } }; } // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc index a7e1a2f42af..58577e31c42 100644 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ b/source/blender/functions/intern/multi_function_network_evaluation.cc @@ -230,7 +230,7 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs( } BLI_assert(node.is_function()); - BLI_assert(node.all_inputs_have_origin()); + BLI_assert(!node.has_unlinked_inputs()); const MFFunctionNode &function_node = node.as_function(); bool all_origins_are_computed = true; diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc index e76b2f51a15..f1e047f01a1 100644 --- a/source/blender/functions/intern/multi_function_network_optimization.cc +++ b/source/blender/functions/intern/multi_function_network_optimization.cc @@ -142,13 +142,24 @@ void dead_node_removal(MFNetwork &network) * * \{ */ +static bool function_node_can_be_constant(MFFunctionNode *node) +{ + if (node->has_unlinked_inputs()) { + return false; + } + if (node->function().depends_on_context()) { + return false; + } + return true; +} + static Vector<MFNode *> find_non_constant_nodes(MFNetwork &network) { Vector<MFNode *> non_constant_nodes; non_constant_nodes.extend(network.dummy_nodes()); for (MFFunctionNode *node : network.function_nodes()) { - if (!node->all_inputs_have_origin()) { + if (!function_node_can_be_constant(node)) { non_constant_nodes.append(node); } } @@ -319,17 +330,18 @@ void constant_folding(MFNetwork &network, ResourceCollector &resources) static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span<uint64_t> node_hashes) { + if (node.function().depends_on_context()) { + return BLI_rng_get_uint(rng); + } + if (node.has_unlinked_inputs()) { + return BLI_rng_get_uint(rng); + } + uint64_t combined_inputs_hash = 394659347u; for (MFInputSocket *input_socket : node.inputs()) { MFOutputSocket *origin_socket = input_socket->origin(); - uint64_t input_hash; - if (origin_socket == nullptr) { - input_hash = BLI_rng_get_uint(rng); - } - else { - input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()], - origin_socket->index()); - } + uint64_t input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()], + origin_socket->index()); combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash); } diff --git a/source/blender/makesdna/DNA_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h index c4ff51a107e..f7a7f94da53 100644 --- a/source/blender/makesdna/DNA_simulation_types.h +++ b/source/blender/makesdna/DNA_simulation_types.h @@ -37,6 +37,9 @@ typedef struct Simulation { /** List containing SimulationState objects. */ struct ListBase states; + + /** List containing PersistentDataHandleItem objects. */ + struct ListBase persistent_data_handles; } Simulation; typedef struct SimulationState { @@ -64,6 +67,15 @@ typedef struct ParticleSimulationState { struct ListBase ptcaches; } ParticleSimulationState; +/** Stores a mapping between an integer handle and a corresponding ID data block. */ +typedef struct PersistentDataHandleItem { + struct PersistentDataHandleItem *next; + struct PersistentDataHandleItem *prev; + struct ID *id; + int handle; + char _pad[4]; +} PersistentDataHandleItem; + /* Simulation.flag */ enum { SIM_DS_EXPAND = (1 << 0), diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index b3cb8163f2a..80720f5206a 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -135,6 +135,7 @@ set(SRC function/nodes/node_fn_combine_strings.cc function/nodes/node_fn_float_compare.cc function/nodes/node_fn_group_instance_id.cc + function/nodes/node_fn_object_transforms.cc function/nodes/node_fn_switch.cc function/node_function_util.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 377ae8bfb84..4c05da694f7 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -26,6 +26,7 @@ void register_node_type_fn_float_compare(void); void register_node_type_fn_switch(void); void register_node_type_fn_group_instance_id(void); void register_node_type_fn_combine_strings(void); +void register_node_type_fn_object_transforms(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh index 79c2b3c7ce8..81b467eca3a 100644 --- a/source/blender/nodes/NOD_node_tree_multi_function.hh +++ b/source/blender/nodes/NOD_node_tree_multi_function.hh @@ -288,7 +288,12 @@ class SocketMFNetworkBuilder : public MFNetworkBuilderBase { */ template<typename T> void set_constant_value(T value) { - const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_Constant<T>>(std::move(value)); + this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value)); + } + + template<typename T, typename... Args> void construct_generator_fn(Args &&... args) + { + const fn::MultiFunction &fn = this->construct_fn<T>(std::forward<Args>(args)...); this->set_generator_fn(fn); } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 91aa11566d3..31ce3f81450 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -276,6 +276,8 @@ DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", DefNode(FunctionNode, FN_NODE_SWITCH, def_fn_switch, "SWITCH", Switch, "Switch", "") DefNode(FunctionNode, FN_NODE_GROUP_INSTANCE_ID, 0, "GROUP_INSTANCE_ID", GroupInstanceID, "Group Instance ID", "") DefNode(FunctionNode, FN_NODE_COMBINE_STRINGS, 0, "COMBINE_STRINGS", CombineStrings, "Combine Strings", "") +DefNode(FunctionNode, FN_NODE_OBJECT_TRANSFORMS, 0, "OBJECT_TRANSFORMS", ObjectTransforms, "Object Transforms", "") + /* undefine macros */ diff --git a/source/blender/nodes/function/nodes/node_fn_object_transforms.cc b/source/blender/nodes/function/nodes/node_fn_object_transforms.cc new file mode 100644 index 00000000000..26b25406590 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_object_transforms.cc @@ -0,0 +1,88 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "BKE_persistent_data_handle.hh" + +static bNodeSocketTemplate fn_node_object_transforms_in[] = { + {SOCK_OBJECT, N_("Object")}, + {-1, ""}, +}; + +static bNodeSocketTemplate fn_node_object_transforms_out[] = { + {SOCK_VECTOR, N_("Location")}, + {-1, ""}, +}; + +class ObjectTransformsFunction : public blender::fn::MultiFunction { + public: + ObjectTransformsFunction() + { + blender::fn::MFSignatureBuilder signature = this->get_builder("Object Transforms"); + signature.depends_on_context(); + signature.single_input<blender::bke::PersistentObjectHandle>("Object"); + signature.single_output<blender::float3>("Location"); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext context) const override + { + blender::fn::VSpan handles = + params.readonly_single_input<blender::bke::PersistentObjectHandle>(0, "Object"); + blender::MutableSpan locations = params.uninitialized_single_output<blender::float3>( + 1, "Location"); + + const blender::bke::PersistentDataHandleMap *handle_map = + context.get_global_context<blender::bke::PersistentDataHandleMap>( + "PersistentDataHandleMap"); + if (handle_map == nullptr) { + locations.fill_indices(mask, {0, 0, 0}); + return; + } + + for (int64_t i : mask) { + blender::bke::PersistentObjectHandle handle = handles[i]; + const Object *object = handle_map->lookup(handle); + blender::float3 location; + if (object == nullptr) { + location = {0, 0, 0}; + } + else { + location = object->loc; + } + locations[i] = location; + } + } +}; + +static void fn_node_object_transforms_expand_in_mf_network( + blender::nodes::NodeMFNetworkBuilder &builder) +{ + static ObjectTransformsFunction fn; + builder.set_matching_fn(fn); +} + +void register_node_type_fn_object_transforms() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_OBJECT_TRANSFORMS, "Object Transforms", 0, 0); + node_type_socket_templates(&ntype, fn_node_object_transforms_in, fn_node_object_transforms_out); + ntype.expand_in_mf_network = fn_node_object_transforms_expand_in_mf_network; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 3a82438a211..49868505d68 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -34,6 +34,7 @@ #include "BKE_lib_id.h" #include "BKE_node.h" +#include "BKE_persistent_data_handle.hh" #include "RNA_access.h" #include "RNA_types.h" @@ -583,6 +584,59 @@ static bNodeSocketType *make_socket_type_string() return socktype; } +class ObjectSocketMultiFunction : public blender::fn::MultiFunction { + private: + const Object *object_; + + public: + ObjectSocketMultiFunction(const Object *object) : object_(object) + { + blender::fn::MFSignatureBuilder signature = this->get_builder("Object Socket"); + signature.depends_on_context(); + signature.single_output<blender::bke::PersistentObjectHandle>("Object"); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext context) const override + { + blender::MutableSpan output = + params.uninitialized_single_output<blender::bke::PersistentObjectHandle>(0, "Object"); + + /* Try to get a handle map, so that the object can be converted to a handle. */ + const blender::bke::PersistentDataHandleMap *handle_map = + context.get_global_context<blender::bke::PersistentDataHandleMap>( + "PersistentDataHandleMap"); + + if (handle_map == nullptr) { + /* Return empty handles when there is no handle map. */ + output.fill_indices(mask, blender::bke::PersistentObjectHandle()); + return; + } + + blender::bke::PersistentObjectHandle handle = handle_map->lookup(object_); + for (int64_t i : mask) { + output[i] = handle; + } + } +}; + +MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle); + +static bNodeSocketType *make_socket_type_object() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE); + socktype->get_mf_data_type = []() { + /* Objects are not passed along as raw pointers, but as handles. */ + return blender::fn::MFDataType::ForSingle<blender::bke::PersistentObjectHandle>(); + }; + socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) { + const Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value; + builder.construct_generator_fn<ObjectSocketMultiFunction>(object); + }; + return socktype; +} + void register_standard_node_socket_types(void) { /* draw callbacks are set in drawnode.c to avoid bad-level calls */ @@ -615,7 +669,7 @@ void register_standard_node_socket_types(void) nodeRegisterSocketType(make_standard_socket_type(SOCK_SHADER, PROP_NONE)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_OBJECT, PROP_NONE)); + nodeRegisterSocketType(make_socket_type_object()); nodeRegisterSocketType(make_standard_socket_type(SOCK_IMAGE, PROP_NONE)); diff --git a/source/blender/simulation/intern/particle_function.cc b/source/blender/simulation/intern/particle_function.cc index e0de68d9b29..935ef7983d9 100644 --- a/source/blender/simulation/intern/particle_function.cc +++ b/source/blender/simulation/intern/particle_function.cc @@ -50,12 +50,18 @@ ParticleFunction::ParticleFunction(const fn::MultiFunction *global_fn, } ParticleFunctionEvaluator::ParticleFunctionEvaluator( - const ParticleFunction &particle_fn, const ParticleChunkContext &particle_chunk_context) + const ParticleFunction &particle_fn, + const SimulationSolveContext &solve_context, + const ParticleChunkContext &particle_chunk_context) : particle_fn_(particle_fn), + solve_context_(solve_context), particle_chunk_context_(particle_chunk_context), mask_(particle_chunk_context_.index_mask()), outputs_(particle_fn_.output_types_.size(), nullptr) { + global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map()); + per_particle_context_.add_global_context("PersistentDataHandleMap", + &solve_context_.handle_map()); } ParticleFunctionEvaluator::~ParticleFunctionEvaluator() diff --git a/source/blender/simulation/intern/particle_function.hh b/source/blender/simulation/intern/particle_function.hh index bbb40efb388..eec4a700383 100644 --- a/source/blender/simulation/intern/particle_function.hh +++ b/source/blender/simulation/intern/particle_function.hh @@ -60,6 +60,7 @@ class ParticleFunctionEvaluator { private: ResourceCollector resources_; const ParticleFunction &particle_fn_; + const SimulationSolveContext &solve_context_; const ParticleChunkContext &particle_chunk_context_; IndexMask mask_; fn::MFContextBuilder global_context_; @@ -69,6 +70,7 @@ class ParticleFunctionEvaluator { public: ParticleFunctionEvaluator(const ParticleFunction &particle_fn, + const SimulationSolveContext &solve_context, const ParticleChunkContext &particle_chunk_context); ~ParticleFunctionEvaluator(); diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc index d4161b06a00..4760063daca 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.cc +++ b/source/blender/simulation/intern/simulation_collect_influences.cc @@ -50,31 +50,51 @@ static Span<const nodes::DNode *> get_particle_simulation_nodes(const nodes::Der return tree.nodes_by_type("SimulationNodeParticleSimulation"); } -static std::optional<Array<std::string>> compute_global_string_inputs( - nodes::MFNetworkTreeMap &network_map, Span<const fn::MFInputSocket *> sockets) +/* Returns true on success. */ +static bool compute_global_inputs(nodes::MFNetworkTreeMap &network_map, + ResourceCollector &resources, + Span<const fn::MFInputSocket *> sockets, + MutableSpan<fn::GMutableSpan> r_results) { int amount = sockets.size(); if (amount == 0) { - return Array<std::string>(); + return true; } if (network_map.network().have_dummy_or_unlinked_dependencies(sockets)) { - return {}; + return false; } fn::MFNetworkEvaluator network_fn{{}, sockets}; - fn::MFParamsBuilder params{network_fn, 1}; - - Array<std::string> strings(amount, NoInitialization()); - for (int i : IndexRange(amount)) { - params.add_uninitialized_single_output( - fn::GMutableSpan(fn::CPPType::get<std::string>(), strings.data() + i, 1)); + for (int param_index : network_fn.param_indices()) { + fn::MFParamType param_type = network_fn.param_type(param_index); + BLI_assert(param_type.category() == fn::MFParamType::Category::SingleOutput); /* For now. */ + const fn::CPPType &type = param_type.data_type().single_type(); + void *buffer = resources.linear_allocator().allocate(type.size(), type.alignment()); + resources.add(buffer, type.destruct_cb(), AT); + fn::GMutableSpan span{type, buffer, 1}; + r_results[param_index] = span; + params.add_uninitialized_single_output(span); } - fn::MFContextBuilder context; - network_fn.call({0}, params, context); + network_fn.call(IndexRange(1), params, context); + return true; +} +static std::optional<Array<std::string>> compute_global_string_inputs( + nodes::MFNetworkTreeMap &network_map, Span<const fn::MFInputSocket *> sockets) +{ + ResourceCollector local_resources; + Array<fn::GMutableSpan> computed_values(sockets.size(), NoInitialization()); + if (!compute_global_inputs(network_map, local_resources, sockets, computed_values)) { + return {}; + } + + Array<std::string> strings(sockets.size()); + for (int i : sockets.index_range()) { + strings[i] = std::move(computed_values[i].typed<std::string>()[0]); + } return strings; } @@ -203,7 +223,8 @@ class ParticleFunctionForce : public ParticleForce { IndexMask mask = context.particle_chunk().index_mask(); MutableSpan<float3> r_combined_force = context.force_dst(); - ParticleFunctionEvaluator evaluator{particle_fn_, context.particle_chunk()}; + ParticleFunctionEvaluator evaluator{ + particle_fn_, context.solve_context(), context.particle_chunk()}; evaluator.compute(); fn::VSpan<float3> forces = evaluator.get<float3>(0, "Force"); @@ -258,44 +279,120 @@ static void collect_forces(nodes::MFNetworkTreeMap &network_map, class MyBasicEmitter : public ParticleEmitter { private: - std::string name_; + Array<std::string> names_; + const fn::MultiFunction &inputs_fn_; + uint32_t seed_; public: - MyBasicEmitter(std::string name) : name_(std::move(name)) + MyBasicEmitter(Array<std::string> names, const fn::MultiFunction &inputs_fn, uint32_t seed) + : names_(std::move(names)), inputs_fn_(inputs_fn), seed_(seed) { } void emit(ParticleEmitterContext &context) const override { - ParticleAllocator *allocator = context.try_get_particle_allocator(name_); - if (allocator == nullptr) { + fn::MFContextBuilder mf_context; + mf_context.add_global_context("PersistentDataHandleMap", + &context.solve_context().handle_map()); + + fn::MFParamsBuilder mf_params{inputs_fn_, 1}; + bke::PersistentObjectHandle object_handle; + float rate; + mf_params.add_uninitialized_single_output(&object_handle); + mf_params.add_uninitialized_single_output(&rate); + inputs_fn_.call(IndexRange(1), mf_params, mf_context); + + const Object *object = context.solve_context().handle_map().lookup(object_handle); + if (object == nullptr) { return; } - fn::MutableAttributesRef attributes = allocator->allocate(10); - RandomNumberGenerator rng{(uint32_t)context.simulation_time_interval().start() ^ - (uint32_t)DefaultHash<std::string>{}(name_)}; + Vector<float3> new_positions; + Vector<float3> new_velocities; + Vector<float> new_birth_times; + + float start_time = context.simulation_time_interval().start(); + RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed_}; + + int amount = rate * 10; + for (int i : IndexRange(amount)) { + UNUSED_VARS(i); + new_positions.append(rng.get_unit_float3() * 0.3 + float3(object->loc)); + new_velocities.append(rng.get_unit_float3()); + new_birth_times.append(context.simulation_time_interval().start()); + } + + for (StringRef name : names_) { + ParticleAllocator *allocator = context.try_get_particle_allocator(name); + if (allocator == nullptr) { + return; + } - MutableSpan<float3> positions = attributes.get<float3>("Position"); - MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); - MutableSpan<float> birth_times = attributes.get<float>("Birth Time"); + fn::MutableAttributesRef attributes = allocator->allocate(amount); - for (int i : IndexRange(attributes.size())) { - positions[i] = rng.get_unit_float3(); - velocities[i] = rng.get_unit_float3(); - birth_times[i] = context.simulation_time_interval().start(); + initialized_copy_n(new_positions.data(), amount, attributes.get<float3>("Position").data()); + initialized_copy_n(new_velocities.data(), amount, attributes.get<float3>("Velocity").data()); + initialized_copy_n( + new_birth_times.data(), amount, attributes.get<float>("Birth Time").data()); } } }; +static Vector<const nodes::DNode *> find_linked_particle_simulations( + const nodes::DOutputSocket &output_socket) +{ + Vector<const nodes::DNode *> simulation_nodes; + for (const nodes::DInputSocket *target_socket : output_socket.linked_sockets()) { + if (target_socket->node().idname() == "SimulationNodeParticleSimulation") { + simulation_nodes.append(&target_socket->node()); + } + } + return simulation_nodes; +} + +static ParticleEmitter *create_particle_emitter(const nodes::DNode &dnode, + ResourceCollector &resources, + nodes::MFNetworkTreeMap &network_map) +{ + Vector<const nodes::DNode *> simulation_dnodes = find_linked_particle_simulations( + dnode.output(0)); + if (simulation_dnodes.size() == 0) { + return nullptr; + } + + Array<std::string> names{simulation_dnodes.size()}; + for (int i : simulation_dnodes.index_range()) { + names[i] = dnode_to_path(*simulation_dnodes[i]); + } + + Array<const fn::MFInputSocket *> input_sockets{dnode.inputs().size()}; + for (int i : input_sockets.index_range()) { + input_sockets[i] = &network_map.lookup_dummy(dnode.input(i)); + } + + if (network_map.network().have_dummy_or_unlinked_dependencies(input_sockets)) { + return nullptr; + } + + fn::MultiFunction &inputs_fn = resources.construct<fn::MFNetworkEvaluator>( + AT, Span<const fn::MFOutputSocket *>(), input_sockets.as_span()); + + uint32_t seed = DefaultHash<std::string>{}(dnode_to_path(dnode)); + ParticleEmitter &emitter = resources.construct<MyBasicEmitter>( + AT, std::move(names), inputs_fn, seed); + return &emitter; +} + static void collect_emitters(nodes::MFNetworkTreeMap &network_map, ResourceCollector &resources, SimulationInfluences &r_influences) { - for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { - std::string name = dnode_to_path(*dnode); - ParticleEmitter &emitter = resources.construct<MyBasicEmitter>(AT, name); - r_influences.particle_emitters.append(&emitter); + for (const nodes::DNode *dnode : + network_map.tree().nodes_by_type("SimulationNodeParticleMeshEmitter")) { + ParticleEmitter *emitter = create_particle_emitter(*dnode, resources, network_map); + if (emitter != nullptr) { + r_influences.particle_emitters.append(emitter); + } } } @@ -316,6 +413,23 @@ static void prepare_particle_attribute_builders(nodes::MFNetworkTreeMap &network } } +static void find_used_data_blocks(const nodes::DerivedNodeTree &tree, + SimulationInfluences &r_influences) +{ + const bNodeSocketType *socktype = nodeSocketTypeFind("NodeSocketObject"); + BLI_assert(socktype != nullptr); + + for (const nodes::DInputSocket *dsocket : tree.input_sockets()) { + const bNodeSocket *bsocket = dsocket->bsocket(); + if (bsocket->typeinfo == socktype) { + Object *value = ((const bNodeSocketValueObject *)bsocket->default_value)->value; + if (value != nullptr) { + r_influences.used_data_blocks.add(&value->id); + } + } + } +} + void collect_simulation_influences(Simulation &simulation, ResourceCollector &resources, SimulationInfluences &r_influences, @@ -343,6 +457,8 @@ void collect_simulation_influences(Simulation &simulation, for (const nodes::DNode *dnode : get_particle_simulation_nodes(tree)) { r_states_info.particle_simulation_names.add(dnode_to_path(*dnode)); } + + find_used_data_blocks(tree, r_influences); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc index f542d6ab2c8..158c50957df 100644 --- a/source/blender/simulation/intern/simulation_solver.cc +++ b/source/blender/simulation/intern/simulation_solver.cc @@ -17,8 +17,11 @@ #include "simulation_solver.hh" #include "BKE_customdata.h" +#include "BKE_lib_id.h" +#include "BKE_persistent_data_handle.hh" #include "BLI_rand.hh" +#include "BLI_set.hh" namespace blender::sim { @@ -243,6 +246,53 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta state.next_particle_id += allocator.total_allocated(); } +static void update_persistent_data_handles(Simulation &simulation, + const VectorSet<ID *> &used_data_blocks) +{ + Set<ID *> contained_ids; + Set<int> used_handles; + + /* Remove handles that have been invalidated. */ + LISTBASE_FOREACH_MUTABLE ( + PersistentDataHandleItem *, handle_item, &simulation.persistent_data_handles) { + if (handle_item->id == nullptr) { + BLI_remlink(&simulation.persistent_data_handles, handle_item); + continue; + } + if (!used_data_blocks.contains(handle_item->id)) { + id_us_min(handle_item->id); + BLI_remlink(&simulation.persistent_data_handles, handle_item); + MEM_freeN(handle_item); + continue; + } + contained_ids.add_new(handle_item->id); + used_handles.add_new(handle_item->handle); + } + + /* Add new handles that are not in the list yet. */ + int next_handle = 0; + for (ID *id : used_data_blocks) { + if (contained_ids.contains(id)) { + continue; + } + + /* Find the next available handle. */ + while (used_handles.contains(next_handle)) { + next_handle++; + } + used_handles.add_new(next_handle); + + PersistentDataHandleItem *handle_item = (PersistentDataHandleItem *)MEM_callocN( + sizeof(*handle_item), AT); + /* Cannot store const pointers in DNA. */ + id_us_plus(id); + handle_item->id = id; + handle_item->handle = next_handle; + + BLI_addtail(&simulation.persistent_data_handles, handle_item); + } +} + void initialize_simulation_states(Simulation &simulation, Depsgraph &UNUSED(depsgraph), const SimulationInfluences &UNUSED(influences)) @@ -255,11 +305,18 @@ void solve_simulation_time_step(Simulation &simulation, const SimulationInfluences &influences, float time_step) { - SimulationSolveContext solve_context{ - simulation, - depsgraph, - influences, - TimeInterval(simulation.current_simulation_time, time_step)}; + update_persistent_data_handles(simulation, influences.used_data_blocks); + + bke::PersistentDataHandleMap handle_map; + LISTBASE_FOREACH (PersistentDataHandleItem *, handle, &simulation.persistent_data_handles) { + handle_map.add(handle->handle, *handle->id); + } + + SimulationSolveContext solve_context{simulation, + depsgraph, + influences, + TimeInterval(simulation.current_simulation_time, time_step), + handle_map}; TimeInterval simulation_time_interval{simulation.current_simulation_time, time_step}; Vector<SimulationState *> simulation_states{simulation.states}; diff --git a/source/blender/simulation/intern/simulation_solver.hh b/source/blender/simulation/intern/simulation_solver.hh index b5e42b53846..1d1b9935661 100644 --- a/source/blender/simulation/intern/simulation_solver.hh +++ b/source/blender/simulation/intern/simulation_solver.hh @@ -24,6 +24,8 @@ #include "FN_attributes_ref.hh" +#include "BKE_persistent_data_handle.hh" + #include "particle_allocator.hh" #include "time_interval.hh" @@ -50,6 +52,7 @@ struct SimulationInfluences { Map<std::string, Vector<const ParticleForce *>> particle_forces; Map<std::string, fn::AttributesInfoBuilder *> particle_attributes_builder; Vector<const ParticleEmitter *> particle_emitters; + VectorSet<ID *> used_data_blocks; }; class SimulationSolveContext { @@ -58,16 +61,19 @@ class SimulationSolveContext { Depsgraph &depsgraph_; const SimulationInfluences &influences_; TimeInterval solve_interval_; + const bke::PersistentDataHandleMap &id_handle_map_; public: SimulationSolveContext(Simulation &simulation, Depsgraph &depsgraph, const SimulationInfluences &influences, - TimeInterval solve_interval) + TimeInterval solve_interval, + const bke::PersistentDataHandleMap &handle_map) : simulation_(simulation), depsgraph_(depsgraph), influences_(influences), - solve_interval_(solve_interval) + solve_interval_(solve_interval), + id_handle_map_(handle_map) { } @@ -80,6 +86,11 @@ class SimulationSolveContext { { return influences_; } + + const bke::PersistentDataHandleMap &handle_map() const + { + return id_handle_map_; + } }; class ParticleAllocators { @@ -147,6 +158,11 @@ class ParticleEmitterContext { { } + SimulationSolveContext &solve_context() + { + return solve_context_; + } + ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name) { return particle_allocators_.try_get_allocator(particle_simulation_name); @@ -174,6 +190,11 @@ class ParticleForceContext { { } + SimulationSolveContext &solve_context() + { + return solve_context_; + } + const ParticleChunkContext &particle_chunk() const { return particle_chunk_context_; |