Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-rw-r--r--source/blender/blenkernel/BKE_node.h1
-rw-r--r--source/blender/blenkernel/BKE_persistent_data_handle.hh132
-rw-r--r--source/blender/blenkernel/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/intern/node.c1
-rw-r--r--source/blender/blenkernel/intern/simulation.cc9
-rw-r--r--source/blender/blenloader/intern/readfile.c12
-rw-r--r--source/blender/blenloader/intern/writefile.c2
-rw-r--r--source/blender/functions/FN_multi_function.hh5
-rw-r--r--source/blender/functions/FN_multi_function_context.hh26
-rw-r--r--source/blender/functions/FN_multi_function_network.hh8
-rw-r--r--source/blender/functions/FN_multi_function_params.hh4
-rw-r--r--source/blender/functions/FN_multi_function_signature.hh10
-rw-r--r--source/blender/functions/intern/multi_function_network_evaluation.cc2
-rw-r--r--source/blender/functions/intern/multi_function_network_optimization.cc30
-rw-r--r--source/blender/makesdna/DNA_simulation_types.h12
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_function.h1
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh7
-rw-r--r--source/blender/nodes/NOD_static_types.h2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_object_transforms.cc88
-rw-r--r--source/blender/nodes/intern/node_socket.cc56
-rw-r--r--source/blender/simulation/intern/particle_function.cc8
-rw-r--r--source/blender/simulation/intern/particle_function.hh2
-rw-r--r--source/blender/simulation/intern/simulation_collect_influences.cc178
-rw-r--r--source/blender/simulation/intern/simulation_solver.cc67
-rw-r--r--source/blender/simulation/intern/simulation_solver.hh25
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_;