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.py8
-rw-r--r--source/blender/blenkernel/intern/node.c4
-rw-r--r--source/blender/functions/FN_attributes_ref.hh6
-rw-r--r--source/blender/functions/FN_cpp_type.hh16
-rw-r--r--source/blender/functions/FN_spans.hh40
-rw-r--r--source/blender/functions/intern/attributes_ref.cc11
-rw-r--r--source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc3
-rw-r--r--source/blender/simulation/intern/particle_function.cc21
-rw-r--r--source/blender/simulation/intern/particle_function.hh8
-rw-r--r--source/blender/simulation/intern/simulation_collect_influences.cc518
-rw-r--r--source/blender/simulation/intern/simulation_solver.cc26
-rw-r--r--source/blender/simulation/intern/simulation_solver_influences.hh13
12 files changed, 500 insertions, 174 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 8439eadcec3..8ae41a9e19c 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -498,16 +498,16 @@ simulation_node_categories = [
not_implemented_node("SimulationNodeEmitParticles"),
]),
SimulationNodeCategory("SIM_EVENTS", "Events", items=[
- not_implemented_node("SimulationNodeParticleBirthEvent"),
- not_implemented_node("SimulationNodeParticleTimeStepEvent"),
+ NodeItem("SimulationNodeParticleBirthEvent"),
+ NodeItem("SimulationNodeParticleTimeStepEvent"),
not_implemented_node("SimulationNodeParticleMeshCollisionEvent"),
]),
SimulationNodeCategory("SIM_FORCES", "Forces", items=[
NodeItem("SimulationNodeForce"),
]),
SimulationNodeCategory("SIM_EXECUTE", "Execute", items=[
- not_implemented_node("SimulationNodeSetParticleAttribute"),
- not_implemented_node("SimulationNodeExecuteCondition"),
+ NodeItem("SimulationNodeSetParticleAttribute"),
+ NodeItem("SimulationNodeExecuteCondition"),
not_implemented_node("SimulationNodeMultiExecute"),
]),
SimulationNodeCategory("SIM_NOISE", "Noise", items=[
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index ca1354e9fea..8d7cb90fb71 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -846,12 +846,12 @@ static void socket_id_user_increment(bNodeSocket *sock)
switch ((eNodeSocketDatatype)sock->type) {
case SOCK_OBJECT: {
bNodeSocketValueObject *default_value = sock->default_value;
- id_us_plus(&default_value->value->id);
+ id_us_plus((ID *)default_value->value);
break;
}
case SOCK_IMAGE: {
bNodeSocketValueImage *default_value = sock->default_value;
- id_us_plus(&default_value->value->id);
+ id_us_plus((ID *)default_value->value);
break;
}
case SOCK_FLOAT:
diff --git a/source/blender/functions/FN_attributes_ref.hh b/source/blender/functions/FN_attributes_ref.hh
index ed14676731e..c694f11b7a7 100644
--- a/source/blender/functions/FN_attributes_ref.hh
+++ b/source/blender/functions/FN_attributes_ref.hh
@@ -50,12 +50,12 @@ class AttributesInfoBuilder : NonCopyable, NonMovable {
AttributesInfoBuilder() = default;
~AttributesInfoBuilder();
- template<typename T> void add(StringRef name, const T &default_value)
+ template<typename T> bool add(StringRef name, const T &default_value)
{
- this->add(name, CPPType::get<T>(), (const void *)&default_value);
+ return this->add(name, CPPType::get<T>(), (const void *)&default_value);
}
- void add(StringRef name, const CPPType &type, const void *default_value = nullptr);
+ bool add(StringRef name, const CPPType &type, const void *default_value = nullptr);
};
/**
diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh
index 594890e353a..531a9073784 100644
--- a/source/blender/functions/FN_cpp_type.hh
+++ b/source/blender/functions/FN_cpp_type.hh
@@ -371,7 +371,7 @@ class CPPType : NonCopyable, NonMovable {
void copy_to_initialized_n(const void *src, void *dst, int64_t n) const
{
- BLI_assert(src != dst);
+ BLI_assert(n == 0 || src != dst);
BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
@@ -380,7 +380,7 @@ class CPPType : NonCopyable, NonMovable {
void copy_to_initialized_indices(const void *src, void *dst, IndexMask mask) const
{
- BLI_assert(src != dst);
+ BLI_assert(mask.size() == 0 || src != dst);
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
@@ -406,7 +406,7 @@ class CPPType : NonCopyable, NonMovable {
void copy_to_uninitialized_n(const void *src, void *dst, int64_t n) const
{
- BLI_assert(src != dst);
+ BLI_assert(n == 0 || src != dst);
BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
@@ -415,7 +415,7 @@ class CPPType : NonCopyable, NonMovable {
void copy_to_uninitialized_indices(const void *src, void *dst, IndexMask mask) const
{
- BLI_assert(src != dst);
+ BLI_assert(mask.size() == 0 || src != dst);
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
@@ -441,7 +441,7 @@ class CPPType : NonCopyable, NonMovable {
void relocate_to_initialized_n(void *src, void *dst, int64_t n) const
{
- BLI_assert(src != dst);
+ BLI_assert(n == 0 || src != dst);
BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
@@ -450,7 +450,7 @@ class CPPType : NonCopyable, NonMovable {
void relocate_to_initialized_indices(void *src, void *dst, IndexMask mask) const
{
- BLI_assert(src != dst);
+ BLI_assert(mask.size() == 0 || src != dst);
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
@@ -476,7 +476,7 @@ class CPPType : NonCopyable, NonMovable {
void relocate_to_uninitialized_n(void *src, void *dst, int64_t n) const
{
- BLI_assert(src != dst);
+ BLI_assert(n == 0 || src != dst);
BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
@@ -485,7 +485,7 @@ class CPPType : NonCopyable, NonMovable {
void relocate_to_uninitialized_indices(void *src, void *dst, IndexMask mask) const
{
- BLI_assert(src != dst);
+ BLI_assert(mask.size() == 0 || src != dst);
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
diff --git a/source/blender/functions/FN_spans.hh b/source/blender/functions/FN_spans.hh
index c50c92cd16d..a81f3ea3633 100644
--- a/source/blender/functions/FN_spans.hh
+++ b/source/blender/functions/FN_spans.hh
@@ -209,6 +209,20 @@ template<typename T> struct VSpanBase {
return false;
}
+ bool is_full_array() const
+ {
+ switch (category_) {
+ case VSpanCategory::Single:
+ return virtual_size_ == 1;
+ case VSpanCategory::FullArray:
+ return true;
+ case VSpanCategory::FullPointerArray:
+ return false;
+ }
+ BLI_assert(false);
+ return false;
+ }
+
bool is_empty() const
{
return this->virtual_size_ == 0;
@@ -285,6 +299,22 @@ template<typename T> class VSpan : public VSpanBase<T> {
BLI_assert(false);
return *this->data_.single.data;
}
+
+ const T &as_single_element() const
+ {
+ BLI_assert(this->is_single_element());
+ return (*this)[0];
+ }
+
+ Span<T> as_full_array() const
+ {
+ BLI_assert(this->is_full_array());
+ if (this->virtual_size_ == 0) {
+ return Span<T>();
+ }
+ const T *data = &(*this)[0];
+ return Span<T>(data, this->virtual_size_);
+ }
};
/**
@@ -395,6 +425,16 @@ class GVSpan : public VSpanBase<void> {
return (*this)[0];
}
+ GSpan as_full_array() const
+ {
+ BLI_assert(this->is_full_array());
+ if (this->virtual_size_ == 0) {
+ return GSpan(*this->type_);
+ }
+ const void *data = (*this)[0];
+ return GSpan(*this->type_, data, this->virtual_size_);
+ }
+
void materialize_to_uninitialized(void *dst) const
{
this->materialize_to_uninitialized(IndexRange(virtual_size_), dst);
diff --git a/source/blender/functions/intern/attributes_ref.cc b/source/blender/functions/intern/attributes_ref.cc
index 7bfcc69671a..4686e217911 100644
--- a/source/blender/functions/intern/attributes_ref.cc
+++ b/source/blender/functions/intern/attributes_ref.cc
@@ -25,7 +25,7 @@ AttributesInfoBuilder::~AttributesInfoBuilder()
}
}
-void AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value)
+bool AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value)
{
if (names_.add_as(name)) {
types_.append(&type);
@@ -36,10 +36,15 @@ void AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void
void *dst = allocator_.allocate(type.size(), type.alignment());
type.copy_to_uninitialized(default_value, dst);
defaults_.append(dst);
+ return true;
}
else {
- /* The same name can be added more than once as long as the type is always the same. */
- BLI_assert(types_[names_.index_of_as(name)] == &type);
+ const CPPType &stored_type = *types_[names_.index_of_as(name)];
+ if (stored_type != type) {
+ std::cout << "Warning: Tried to add an attribute twice with different types (" << name
+ << ": " << stored_type.name() << ", " << type.name() << ").\n";
+ }
+ return false;
}
}
diff --git a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc
index 8696dbe340c..8f5c6818cb4 100644
--- a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc
+++ b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc
@@ -18,6 +18,7 @@
#include "node_simulation_util.h"
static bNodeSocketTemplate sim_node_set_particle_attribute_in[] = {
+ {SOCK_CONTROL_FLOW, N_("Execute")},
{SOCK_STRING, N_("Name")},
{SOCK_FLOAT, N_("Float"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
{SOCK_INT, N_("Int"), 0, 0, 0, 0, -10000, 10000},
@@ -38,7 +39,7 @@ static void sim_node_set_particle_attribute_update(bNodeTree *UNUSED(ntree), bNo
{
int index = 0;
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
- if (index >= 1) {
+ if (index >= 2) {
nodeSetSocketAvailability(sock, sock->type == node->custom1);
}
index++;
diff --git a/source/blender/simulation/intern/particle_function.cc b/source/blender/simulation/intern/particle_function.cc
index 1b259938e34..d1e9019a85a 100644
--- a/source/blender/simulation/intern/particle_function.cc
+++ b/source/blender/simulation/intern/particle_function.cc
@@ -49,14 +49,13 @@ ParticleFunction::ParticleFunction(const fn::MultiFunction *global_fn,
}
}
-ParticleFunctionEvaluator::ParticleFunctionEvaluator(
- const ParticleFunction &particle_fn,
- const SimulationSolveContext &solve_context,
- const ParticleChunkContext &particle_chunk_context)
+ParticleFunctionEvaluator::ParticleFunctionEvaluator(const ParticleFunction &particle_fn,
+ const SimulationSolveContext &solve_context,
+ const ParticleChunkContext &particles)
: particle_fn_(particle_fn),
solve_context_(solve_context),
- particle_chunk_context_(particle_chunk_context),
- mask_(particle_chunk_context_.index_mask),
+ particles_(particles),
+ mask_(particles_.index_mask),
outputs_(particle_fn_.output_types_.size(), nullptr)
{
global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map);
@@ -91,8 +90,10 @@ void ParticleFunctionEvaluator::compute()
fn::GVSpan ParticleFunctionEvaluator::get(int output_index, StringRef expected_name) const
{
#ifdef DEBUG
- StringRef real_name = particle_fn_.output_names_[output_index];
- BLI_assert(expected_name == real_name);
+ if (expected_name != "") {
+ StringRef real_name = particle_fn_.output_names_[output_index];
+ BLI_assert(expected_name == real_name);
+ }
BLI_assert(is_computed_);
#endif
UNUSED_VARS_NDEBUG(expected_name);
@@ -116,7 +117,7 @@ void ParticleFunctionEvaluator::compute_globals()
/* Add input parameters. */
for (const ParticleFunctionInput *input : particle_fn_.global_inputs_) {
- input->add_input(particle_chunk_context_.attributes, params, resources_);
+ input->add_input(particles_.attributes, params, resources_);
}
/* Add output parameters. */
@@ -143,7 +144,7 @@ void ParticleFunctionEvaluator::compute_per_particle()
/* Add input parameters. */
for (const ParticleFunctionInput *input : particle_fn_.per_particle_inputs_) {
- input->add_input(particle_chunk_context_.attributes, params, resources_);
+ input->add_input(particles_.attributes, params, resources_);
}
/* Add output parameters. */
diff --git a/source/blender/simulation/intern/particle_function.hh b/source/blender/simulation/intern/particle_function.hh
index eec4a700383..abe0aae8522 100644
--- a/source/blender/simulation/intern/particle_function.hh
+++ b/source/blender/simulation/intern/particle_function.hh
@@ -61,7 +61,7 @@ class ParticleFunctionEvaluator {
ResourceCollector resources_;
const ParticleFunction &particle_fn_;
const SimulationSolveContext &solve_context_;
- const ParticleChunkContext &particle_chunk_context_;
+ const ParticleChunkContext &particles_;
IndexMask mask_;
fn::MFContextBuilder global_context_;
fn::MFContextBuilder per_particle_context_;
@@ -71,13 +71,13 @@ class ParticleFunctionEvaluator {
public:
ParticleFunctionEvaluator(const ParticleFunction &particle_fn,
const SimulationSolveContext &solve_context,
- const ParticleChunkContext &particle_chunk_context);
+ const ParticleChunkContext &particles);
~ParticleFunctionEvaluator();
void compute();
- fn::GVSpan get(int output_index, StringRef expected_name) const;
+ fn::GVSpan get(int output_index, StringRef expected_name = "") const;
- template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name) const
+ template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name = "") const
{
return this->get(output_index, expected_name).typed<T>();
}
diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc
index 539a9d689d5..40a29c95d25 100644
--- a/source/blender/simulation/intern/simulation_collect_influences.cc
+++ b/source/blender/simulation/intern/simulation_collect_influences.cc
@@ -69,9 +69,68 @@ static std::string dnode_to_path(const DNode &dnode)
return path;
}
-static Span<const DNode *> get_particle_simulation_nodes(const DerivedNodeTree &tree)
+struct CollectContext : NonCopyable, NonMovable {
+ SimulationInfluences &influences;
+ RequiredStates &required_states;
+ ResourceCollector &resources;
+ MFNetworkTreeMap &network_map;
+ MFNetwork &network;
+ const DerivedNodeTree &tree;
+
+ DummyDataSources data_sources;
+ Span<const DNode *> particle_simulation_nodes;
+ Map<const DNode *, std::string> node_paths;
+
+ CollectContext(SimulationInfluences &influences,
+ RequiredStates &required_states,
+ ResourceCollector &resources,
+ MFNetworkTreeMap &network_map)
+ : influences(influences),
+ required_states(required_states),
+ resources(resources),
+ network_map(network_map),
+ network(network_map.network()),
+ tree(network_map.tree())
+ {
+ particle_simulation_nodes = tree.nodes_by_type("SimulationNodeParticleSimulation");
+ }
+};
+
+static const ParticleAction *create_particle_action(CollectContext &context,
+ const DOutputSocket &dsocket,
+ Span<StringRefNull> particle_names);
+
+static const ParticleAction *create_particle_action(CollectContext &context,
+ const DInputSocket &dsocket,
+ Span<StringRefNull> particle_names)
+{
+ BLI_assert(dsocket.bsocket()->type == SOCK_CONTROL_FLOW);
+ if (dsocket.linked_sockets().size() != 1) {
+ return nullptr;
+ }
+ return create_particle_action(context, *dsocket.linked_sockets()[0], particle_names);
+}
+
+static StringRefNull get_identifier(CollectContext &context, const DNode &dnode)
{
- return tree.nodes_by_type("SimulationNodeParticleSimulation");
+ return context.node_paths.lookup_or_add_cb(&dnode, [&]() { return dnode_to_path(dnode); });
+}
+
+static Span<const DNode *> nodes_by_type(CollectContext &context, StringRefNull idname)
+{
+ return context.tree.nodes_by_type(idname);
+}
+
+static Array<StringRefNull> find_linked_particle_simulations(CollectContext &context,
+ const DOutputSocket &output_socket)
+{
+ VectorSet<StringRefNull> names;
+ for (const DInputSocket *target_socket : output_socket.linked_sockets()) {
+ if (target_socket->node().idname() == "SimulationNodeParticleSimulation") {
+ names.add(get_identifier(context, target_socket->node()));
+ }
+ }
+ return names.as_span();
}
/* Returns true on success. */
@@ -122,22 +181,23 @@ static std::optional<Array<std::string>> compute_global_string_inputs(
return strings;
}
-static void find_and_deduplicate_particle_attribute_nodes(MFNetworkTreeMap &network_map,
- DummyDataSources &r_data_sources)
+/**
+ * This will find all the particle attribute input nodes. Then it will compute the attribute names
+ * by evaluating the network (those names should not depend on per particle data). In the end,
+ * input nodes that access the same attribute are combined.
+ */
+static void prepare_particle_attribute_nodes(CollectContext &context)
{
- MFNetwork &network = network_map.network();
- const DerivedNodeTree &tree = network_map.tree();
-
- Span<const DNode *> attribute_dnodes = tree.nodes_by_type("SimulationNodeParticleAttribute");
+ Span<const DNode *> attribute_dnodes = nodes_by_type(context, "SimulationNodeParticleAttribute");
Vector<MFInputSocket *> name_sockets;
for (const DNode *dnode : attribute_dnodes) {
- MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0));
+ MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode->input(0));
name_sockets.append(&name_socket);
}
- std::optional<Array<std::string>> attribute_names = compute_global_string_inputs(network_map,
- name_sockets);
+ std::optional<Array<std::string>> attribute_names = compute_global_string_inputs(
+ context.network_map, name_sockets);
if (!attribute_names.has_value()) {
return;
}
@@ -155,14 +215,14 @@ static void find_and_deduplicate_particle_attribute_nodes(MFNetworkTreeMap &netw
MFDataType data_type = item.key.second;
Span<MFNode *> nodes = item.value;
- MFOutputSocket &new_attribute_socket = network.add_input("Attribute '" + attribute_name + "'",
- data_type);
+ MFOutputSocket &new_attribute_socket = context.network.add_input(
+ "Attribute '" + attribute_name + "'", data_type);
for (MFNode *node : nodes) {
- network.relink(node->output(0), new_attribute_socket);
+ context.network.relink(node->output(0), new_attribute_socket);
}
- network.remove(nodes);
+ context.network.remove(nodes);
- r_data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name);
+ context.data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name);
}
}
@@ -192,9 +252,7 @@ class ParticleAttributeInput : public ParticleFunctionInput {
};
static const ParticleFunction *create_particle_function_for_inputs(
- Span<const MFInputSocket *> sockets_to_compute,
- ResourceCollector &resources,
- DummyDataSources &data_sources)
+ CollectContext &context, Span<const MFInputSocket *> sockets_to_compute)
{
BLI_assert(sockets_to_compute.size() >= 1);
const MFNetwork &network = sockets_to_compute[0]->node().network();
@@ -206,20 +264,21 @@ static const ParticleFunction *create_particle_function_for_inputs(
Vector<const ParticleFunctionInput *> per_particle_inputs;
for (const MFOutputSocket *socket : dummy_deps) {
- const std::string *attribute_name = data_sources.particle_attributes.lookup_ptr(socket);
+ const std::string *attribute_name = context.data_sources.particle_attributes.lookup_ptr(
+ socket);
if (attribute_name == nullptr) {
return nullptr;
}
- per_particle_inputs.append(&resources.construct<ParticleAttributeInput>(
+ per_particle_inputs.append(&context.resources.construct<ParticleAttributeInput>(
AT, *attribute_name, socket->data_type().single_type()));
}
- const MultiFunction &per_particle_fn = resources.construct<MFNetworkEvaluator>(
+ const MultiFunction &per_particle_fn = context.resources.construct<MFNetworkEvaluator>(
AT, dummy_deps.as_span(), sockets_to_compute);
Array<bool> output_is_global(sockets_to_compute.size(), false);
- const ParticleFunction &particle_fn = resources.construct<ParticleFunction>(
+ const ParticleFunction &particle_fn = context.resources.construct<ParticleFunction>(
AT,
nullptr,
&per_particle_fn,
@@ -241,11 +300,10 @@ class ParticleFunctionForce : public ParticleForce {
void add_force(ParticleForceContext &context) const override
{
- IndexMask mask = context.particle_chunk_context.index_mask;
+ IndexMask mask = context.particles.index_mask;
MutableSpan<float3> r_combined_force = context.force_dst;
- ParticleFunctionEvaluator evaluator{
- particle_fn_, context.solve_context, context.particle_chunk_context};
+ ParticleFunctionEvaluator evaluator{particle_fn_, context.solve_context, context.particles};
evaluator.compute();
VSpan<float3> forces = evaluator.get<float3>(0, "Force");
@@ -255,11 +313,8 @@ class ParticleFunctionForce : public ParticleForce {
}
};
-static void create_forces_for_particle_simulation(const DNode &simulation_node,
- MFNetworkTreeMap &network_map,
- ResourceCollector &resources,
- DummyDataSources &data_sources,
- SimulationInfluences &r_influences)
+static void create_forces_for_particle_simulation(CollectContext &context,
+ const DNode &simulation_node)
{
Vector<const ParticleForce *> forces;
for (const DOutputSocket *origin_socket : simulation_node.input(2, "Forces").linked_sockets()) {
@@ -268,142 +323,357 @@ static void create_forces_for_particle_simulation(const DNode &simulation_node,
continue;
}
- const MFInputSocket &force_socket = network_map.lookup_dummy(origin_node.input(0, "Force"));
+ const MFInputSocket &force_socket = context.network_map.lookup_dummy(
+ origin_node.input(0, "Force"));
- const ParticleFunction *particle_fn = create_particle_function_for_inputs(
- {&force_socket}, resources, data_sources);
+ const ParticleFunction *particle_fn = create_particle_function_for_inputs(context,
+ {&force_socket});
if (particle_fn == nullptr) {
continue;
}
- const ParticleForce &force = resources.construct<ParticleFunctionForce>(AT, *particle_fn);
+ const ParticleForce &force = context.resources.construct<ParticleFunctionForce>(AT,
+ *particle_fn);
forces.append(&force);
}
- std::string particle_name = dnode_to_path(simulation_node);
- r_influences.particle_forces.add_multiple(std::move(particle_name), forces);
+ StringRef particle_name = get_identifier(context, simulation_node);
+ context.influences.particle_forces.add_multiple_as(particle_name, forces);
}
-static void collect_forces(MFNetworkTreeMap &network_map,
- ResourceCollector &resources,
- DummyDataSources &data_sources,
- SimulationInfluences &r_influences)
+static void collect_forces(CollectContext &context)
{
- for (const DNode *dnode : get_particle_simulation_nodes(network_map.tree())) {
- create_forces_for_particle_simulation(
- *dnode, network_map, resources, data_sources, r_influences);
+ for (const DNode *dnode : context.particle_simulation_nodes) {
+ create_forces_for_particle_simulation(context, *dnode);
}
}
-static Vector<const DNode *> find_linked_particle_simulations(const DOutputSocket &output_socket)
+static ParticleEmitter *create_particle_emitter(CollectContext &context, const DNode &dnode)
{
- Vector<const DNode *> simulation_nodes;
- for (const 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 DNode &dnode,
- ResourceCollector &resources,
- MFNetworkTreeMap &network_map,
- RequiredStates &r_required_states)
-{
- Vector<const DNode *> simulation_dnodes = find_linked_particle_simulations(dnode.output(0));
- if (simulation_dnodes.size() == 0) {
+ Array<StringRefNull> names = find_linked_particle_simulations(context, dnode.output(0));
+ if (names.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 MFInputSocket *> input_sockets{dnode.inputs().size()};
for (int i : input_sockets.index_range()) {
- input_sockets[i] = &network_map.lookup_dummy(dnode.input(i));
+ input_sockets[i] = &context.network_map.lookup_dummy(dnode.input(i));
}
- if (network_map.network().have_dummy_or_unlinked_dependencies(input_sockets)) {
+ if (context.network.have_dummy_or_unlinked_dependencies(input_sockets)) {
return nullptr;
}
- MultiFunction &inputs_fn = resources.construct<MFNetworkEvaluator>(
+ MultiFunction &inputs_fn = context.resources.construct<MFNetworkEvaluator>(
AT, Span<const MFOutputSocket *>(), input_sockets.as_span());
- std::string own_state_name = dnode_to_path(dnode);
- r_required_states.add(own_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER);
- ParticleEmitter &emitter = resources.construct<ParticleMeshEmitter>(
- AT, std::move(own_state_name), std::move(names), inputs_fn);
+ StringRefNull own_state_name = get_identifier(context, dnode);
+ context.required_states.add(own_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER);
+ ParticleEmitter &emitter = context.resources.construct<ParticleMeshEmitter>(
+ AT, own_state_name, names.as_span(), inputs_fn);
return &emitter;
}
-static void collect_emitters(MFNetworkTreeMap &network_map,
- ResourceCollector &resources,
- SimulationInfluences &r_influences,
- RequiredStates &r_required_states)
+static void collect_emitters(CollectContext &context)
{
- for (const DNode *dnode :
- network_map.tree().nodes_by_type("SimulationNodeParticleMeshEmitter")) {
- ParticleEmitter *emitter = create_particle_emitter(
- *dnode, resources, network_map, r_required_states);
+ for (const DNode *dnode : nodes_by_type(context, "SimulationNodeParticleMeshEmitter")) {
+ ParticleEmitter *emitter = create_particle_emitter(context, *dnode);
if (emitter != nullptr) {
- r_influences.particle_emitters.append(emitter);
+ context.influences.particle_emitters.append(emitter);
}
}
}
-class RandomizeVelocityAction : public ParticleAction {
+static void collect_birth_events(CollectContext &context)
+{
+ for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleBirthEvent")) {
+ const DInputSocket &execute_input = event_dnode->input(0);
+ if (execute_input.linked_sockets().size() != 1) {
+ continue;
+ }
+
+ Array<StringRefNull> particle_names = find_linked_particle_simulations(context,
+ event_dnode->output(0));
+
+ const DOutputSocket &execute_source = *execute_input.linked_sockets()[0];
+ const ParticleAction *action = create_particle_action(context, execute_source, particle_names);
+ if (action == nullptr) {
+ continue;
+ }
+
+ for (StringRefNull particle_name : particle_names) {
+ context.influences.particle_birth_actions.add_as(particle_name, action);
+ }
+ }
+}
+
+static void collect_time_step_events(CollectContext &context)
+{
+ for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleTimeStepEvent")) {
+ const DInputSocket &execute_input = event_dnode->input(0);
+ if (execute_input.linked_sockets().size() != 1) {
+ continue;
+ }
+
+ Array<StringRefNull> particle_names = find_linked_particle_simulations(context,
+ event_dnode->output(0));
+
+ const DOutputSocket &execute_source = *execute_input.linked_sockets()[0];
+ const ParticleAction *action = create_particle_action(context, execute_source, particle_names);
+ if (action == nullptr) {
+ continue;
+ }
+
+ NodeSimParticleTimeStepEventType type =
+ (NodeSimParticleTimeStepEventType)event_dnode->node_ref().bnode()->custom1;
+ if (type == NODE_PARTICLE_TIME_STEP_EVENT_BEGIN) {
+ for (StringRefNull particle_name : particle_names) {
+ context.influences.particle_time_step_begin_actions.add_as(particle_name, action);
+ }
+ }
+ else {
+ for (StringRefNull particle_name : particle_names) {
+ context.influences.particle_time_step_end_actions.add_as(particle_name, action);
+ }
+ }
+ }
+}
+
+class SequenceParticleAction : public ParticleAction {
+ private:
+ Vector<const ParticleAction *> actions_;
+
public:
+ SequenceParticleAction(Span<const ParticleAction *> actions) : actions_(std::move(actions))
+ {
+ }
+
void execute(ParticleActionContext &context) const override
{
- MutableSpan<int> hashes = context.particle_chunk_context.attributes.get<int>("Hash");
- MutableSpan<float3> velocities = context.particle_chunk_context.attributes.get<float3>(
- "Velocity");
- for (int i : context.particle_chunk_context.index_mask) {
- const float x = BLI_hash_int_01((uint32_t)hashes[i] ^ 23423523u) - 0.5f;
- const float y = BLI_hash_int_01((uint32_t)hashes[i] ^ 76463521u) - 0.5f;
- const float z = BLI_hash_int_01((uint32_t)hashes[i] ^ 43523762u) - 0.5f;
- float3 vector{x, y, z};
- vector.normalize();
- velocities[i] += vector * 0.3;
+ for (const ParticleAction *action : actions_) {
+ action->execute(context);
}
}
};
-static void collect_birth_events(MFNetworkTreeMap &network_map,
- ResourceCollector &resources,
- SimulationInfluences &r_influences)
+class SetParticleAttributeAction : public ParticleAction {
+ private:
+ std::string attribute_name_;
+ const CPPType &cpp_type_;
+ const ParticleFunction &inputs_fn_;
+
+ public:
+ SetParticleAttributeAction(std::string attribute_name,
+ const CPPType &cpp_type,
+ const ParticleFunction &inputs_fn)
+ : attribute_name_(std::move(attribute_name)), cpp_type_(cpp_type), inputs_fn_(inputs_fn)
+ {
+ }
+
+ void execute(ParticleActionContext &context) const override
+ {
+ std::optional<GMutableSpan> attribute_array = context.particles.attributes.try_get(
+ attribute_name_, cpp_type_);
+ if (!attribute_array.has_value()) {
+ return;
+ }
+
+ ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles};
+ evaluator.compute();
+ GVSpan values = evaluator.get(0);
+
+ if (values.is_single_element()) {
+ cpp_type_.fill_initialized_indices(
+ values.as_single_element(), attribute_array->data(), context.particles.index_mask);
+ }
+ else {
+ GSpan value_array = values.as_full_array();
+ cpp_type_.copy_to_initialized_indices(
+ value_array.data(), attribute_array->data(), context.particles.index_mask);
+ }
+ }
+};
+
+static const ParticleAction *concatenate_actions(CollectContext &context,
+ Span<const ParticleAction *> actions)
+{
+ Vector<const ParticleAction *> non_null_actions;
+ for (const ParticleAction *action : actions) {
+ if (action != nullptr) {
+ non_null_actions.append(action);
+ }
+ }
+ if (non_null_actions.size() == 0) {
+ return nullptr;
+ }
+ if (non_null_actions.size() == 1) {
+ return non_null_actions[0];
+ }
+ return &context.resources.construct<SequenceParticleAction>(AT, std::move(non_null_actions));
+}
+
+static const ParticleAction *create_set_particle_attribute_action(
+ CollectContext &context, const DOutputSocket &dsocket, Span<StringRefNull> particle_names)
+{
+ const DNode &dnode = dsocket.node();
+ MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode.input(1));
+ MFInputSocket &value_socket = name_socket.node().input(1);
+ std::optional<Array<std::string>> names = compute_global_string_inputs(context.network_map,
+ {&name_socket});
+ if (!names.has_value()) {
+ return nullptr;
+ }
+
+ std::string attribute_name = (*names)[0];
+ const CPPType &attribute_type = value_socket.data_type().single_type();
+
+ const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context,
+ {&value_socket});
+ if (inputs_fn == nullptr) {
+ return nullptr;
+ }
+
+ for (StringRef particle_name : particle_names) {
+ context.influences.particle_attributes_builder.lookup_as(particle_name)
+ ->add(attribute_name, attribute_type);
+ }
+
+ ParticleAction &this_action = context.resources.construct<SetParticleAttributeAction>(
+ AT, attribute_name, attribute_type, *inputs_fn);
+
+ const ParticleAction *previous_action = create_particle_action(
+ context, dnode.input(0), particle_names);
+
+ return concatenate_actions(context, {previous_action, &this_action});
+}
+
+class ParticleConditionAction : public ParticleAction {
+ private:
+ const ParticleFunction &inputs_fn_;
+ const ParticleAction *action_true_;
+ const ParticleAction *action_false_;
+
+ public:
+ ParticleConditionAction(const ParticleFunction &inputs_fn,
+ const ParticleAction *action_true,
+ const ParticleAction *action_false)
+ : inputs_fn_(inputs_fn), action_true_(action_true), action_false_(action_false)
+ {
+ }
+
+ void execute(ParticleActionContext &context) const override
+ {
+ ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles};
+ evaluator.compute();
+ VSpan<bool> conditions = evaluator.get<bool>(0, "Condition");
+
+ if (conditions.is_single_element()) {
+ const bool condition = conditions.as_single_element();
+ if (condition) {
+ if (action_true_ != nullptr) {
+ action_true_->execute(context);
+ }
+ }
+ else {
+ if (action_false_ != nullptr) {
+ action_false_->execute(context);
+ }
+ }
+ }
+ else {
+ Span<bool> conditions_array = conditions.as_full_array();
+
+ Vector<int64_t> true_indices;
+ Vector<int64_t> false_indices;
+ for (int i : context.particles.index_mask) {
+ if (conditions_array[i]) {
+ true_indices.append(i);
+ }
+ else {
+ false_indices.append(i);
+ }
+ }
+
+ if (action_true_ != nullptr) {
+ ParticleChunkContext chunk_context{true_indices.as_span(), context.particles.attributes};
+ ParticleActionContext action_context{context.solve_context, chunk_context};
+ action_true_->execute(action_context);
+ }
+ if (action_false_ != nullptr) {
+ ParticleChunkContext chunk_context{false_indices.as_span(), context.particles.attributes};
+ ParticleActionContext action_context{context.solve_context, chunk_context};
+ action_false_->execute(action_context);
+ }
+ }
+ }
+};
+
+static const ParticleAction *create_particle_condition_action(CollectContext &context,
+ const DOutputSocket &dsocket,
+ Span<StringRefNull> particle_names)
{
- RandomizeVelocityAction &action = resources.construct<RandomizeVelocityAction>(AT);
- for (const DNode *dnode : get_particle_simulation_nodes(network_map.tree())) {
- std::string particle_name = dnode_to_path(*dnode);
- r_influences.particle_birth_actions.add_as(std::move(particle_name), &action);
+ const DNode &dnode = dsocket.node();
+ MFInputSocket &condition_socket = context.network_map.lookup_dummy(dnode.input(0));
+
+ const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context,
+ {&condition_socket});
+ if (inputs_fn == nullptr) {
+ return nullptr;
}
+
+ const ParticleAction *true_action = create_particle_action(
+ context, dnode.input(1), particle_names);
+ const ParticleAction *false_action = create_particle_action(
+ context, dnode.input(2), particle_names);
+
+ if (true_action == nullptr && false_action == nullptr) {
+ return nullptr;
+ }
+ return &context.resources.construct<ParticleConditionAction>(
+ AT, *inputs_fn, true_action, false_action);
}
-static void prepare_particle_attribute_builders(MFNetworkTreeMap &network_map,
- ResourceCollector &resources,
- SimulationInfluences &r_influences)
+static const ParticleAction *create_particle_action(CollectContext &context,
+ const DOutputSocket &dsocket,
+ Span<StringRefNull> particle_names)
{
- for (const DNode *dnode : get_particle_simulation_nodes(network_map.tree())) {
- std::string name = dnode_to_path(*dnode);
- AttributesInfoBuilder &builder = resources.construct<AttributesInfoBuilder>(AT);
- builder.add<float3>("Position", {0, 0, 0});
- builder.add<float3>("Velocity", {0, 0, 0});
- builder.add<int>("ID", 0);
+ const DNode &dnode = dsocket.node();
+ if (dnode.idname() == "SimulationNodeSetParticleAttribute") {
+ return create_set_particle_attribute_action(context, dsocket, particle_names);
+ }
+ if (dnode.idname() == "SimulationNodeExecuteCondition") {
+ return create_particle_condition_action(context, dsocket, particle_names);
+ }
+ return nullptr;
+}
+
+static void initialize_particle_attribute_builders(CollectContext &context)
+{
+ for (const DNode *dnode : context.particle_simulation_nodes) {
+ StringRef name = get_identifier(context, *dnode);
+ AttributesInfoBuilder &attributes_builder = context.resources.construct<AttributesInfoBuilder>(
+ AT);
+ attributes_builder.add<float3>("Position", {0, 0, 0});
+ attributes_builder.add<float3>("Velocity", {0, 0, 0});
+ attributes_builder.add<int>("ID", 0);
/* TODO: Use bool property, but need to add CD_PROP_BOOL first. */
- builder.add<int>("Dead", 0);
+ attributes_builder.add<int>("Dead", 0);
/* TODO: Use uint32_t, but we don't have a corresponding custom property type. */
- builder.add<int>("Hash", 0);
- builder.add<float>("Birth Time", 0.0f);
- r_influences.particle_attributes_builder.add_new(std::move(name), &builder);
+ attributes_builder.add<int>("Hash", 0);
+ attributes_builder.add<float>("Birth Time", 0.0f);
+ context.influences.particle_attributes_builder.add_new(name, &attributes_builder);
}
}
+static void optimize_function_network(CollectContext &context)
+{
+ fn::mf_network_optimization::constant_folding(context.network, context.resources);
+ fn::mf_network_optimization::common_subnetwork_elimination(context.network);
+ fn::mf_network_optimization::dead_node_removal(context.network);
+ // WM_clipboard_text_set(network.to_dot().c_str(), false);
+}
+
void collect_simulation_influences(Simulation &simulation,
ResourceCollector &resources,
SimulationInfluences &r_influences,
@@ -415,22 +685,20 @@ void collect_simulation_influences(Simulation &simulation,
MFNetwork &network = resources.construct<MFNetwork>(AT);
MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources);
- prepare_particle_attribute_builders(network_map, resources, r_influences);
+ CollectContext context{r_influences, r_required_states, resources, network_map};
+ initialize_particle_attribute_builders(context);
- DummyDataSources data_sources;
- find_and_deduplicate_particle_attribute_nodes(network_map, data_sources);
+ prepare_particle_attribute_nodes(context);
- fn::mf_network_optimization::constant_folding(network, resources);
- fn::mf_network_optimization::common_subnetwork_elimination(network);
- fn::mf_network_optimization::dead_node_removal(network);
- // WM_clipboard_text_set(network.to_dot().c_str(), false);
+ collect_forces(context);
+ collect_emitters(context);
+ collect_birth_events(context);
+ collect_time_step_events(context);
- collect_forces(network_map, resources, data_sources, r_influences);
- collect_emitters(network_map, resources, r_influences, r_required_states);
- collect_birth_events(network_map, resources, r_influences);
+ optimize_function_network(context);
- for (const DNode *dnode : get_particle_simulation_nodes(tree)) {
- r_required_states.add(dnode_to_path(*dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION);
+ for (const DNode *dnode : context.particle_simulation_nodes) {
+ r_required_states.add(get_identifier(context, *dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION);
}
}
diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc
index f650528edb6..3c159eb1c58 100644
--- a/source/blender/simulation/intern/simulation_solver.cc
+++ b/source/blender/simulation/intern/simulation_solver.cc
@@ -128,15 +128,21 @@ BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_c
float end_time)
{
int particle_amount = attributes.size();
+
+ Span<const ParticleAction *> begin_actions =
+ solve_context.influences.particle_time_step_begin_actions.lookup_as(state.head.name);
+ for (const ParticleAction *action : begin_actions) {
+ ParticleChunkContext particles{IndexMask(particle_amount), attributes};
+ ParticleActionContext action_context{solve_context, particles};
+ action->execute(action_context);
+ }
+
Array<float3> force_vectors{particle_amount, {0, 0, 0}};
Span<const ParticleForce *> forces = solve_context.influences.particle_forces.lookup_as(
state.head.name);
-
- ParticleChunkContext particle_chunk_context{IndexMask(particle_amount), attributes};
- ParticleForceContext particle_force_context{
- solve_context, particle_chunk_context, force_vectors};
-
for (const ParticleForce *force : forces) {
+ ParticleChunkContext particles{IndexMask(particle_amount), attributes};
+ ParticleForceContext particle_force_context{solve_context, particles, force_vectors};
force->add_force(particle_force_context);
}
@@ -154,6 +160,14 @@ BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_c
dead_states[i] = true;
}
}
+
+ Span<const ParticleAction *> end_actions =
+ solve_context.influences.particle_time_step_end_actions.lookup_as(state.head.name);
+ for (const ParticleAction *action : end_actions) {
+ ParticleChunkContext particles{IndexMask(particle_amount), attributes};
+ ParticleActionContext action_context{solve_context, particles};
+ action->execute(action_context);
+ }
}
BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context,
@@ -312,7 +326,7 @@ void solve_simulation_time_step(Simulation &simulation,
Span<const ParticleAction *> actions = influences.particle_birth_actions.lookup_as(
state->head.name);
for (const ParticleAction *action : actions) {
- MutableParticleChunkContext chunk_context{IndexRange(attributes.size()), attributes};
+ ParticleChunkContext chunk_context{IndexRange(attributes.size()), attributes};
ParticleActionContext action_context{solve_context, chunk_context};
action->execute(action_context);
}
diff --git a/source/blender/simulation/intern/simulation_solver_influences.hh b/source/blender/simulation/intern/simulation_solver_influences.hh
index 94a1f9a926f..6633c1ebb1d 100644
--- a/source/blender/simulation/intern/simulation_solver_influences.hh
+++ b/source/blender/simulation/intern/simulation_solver_influences.hh
@@ -67,6 +67,8 @@ class ParticleAction {
struct SimulationInfluences {
MultiValueMap<std::string, const ParticleForce *> particle_forces;
MultiValueMap<std::string, const ParticleAction *> particle_birth_actions;
+ MultiValueMap<std::string, const ParticleAction *> particle_time_step_begin_actions;
+ MultiValueMap<std::string, const ParticleAction *> particle_time_step_end_actions;
Map<std::string, AttributesInfoBuilder *> particle_attributes_builder;
Vector<const ParticleEmitter *> particle_emitters;
};
@@ -155,14 +157,9 @@ class ParticleAllocators {
}
};
-struct MutableParticleChunkContext {
- IndexMask index_mask;
- MutableAttributesRef attributes;
-};
-
struct ParticleChunkContext {
IndexMask index_mask;
- AttributesRef attributes;
+ MutableAttributesRef attributes;
};
struct ParticleEmitterContext {
@@ -183,13 +180,13 @@ struct ParticleEmitterContext {
struct ParticleForceContext {
SimulationSolveContext &solve_context;
- ParticleChunkContext &particle_chunk_context;
+ ParticleChunkContext &particles;
MutableSpan<float3> force_dst;
};
struct ParticleActionContext {
SimulationSolveContext &solve_context;
- MutableParticleChunkContext &particle_chunk_context;
+ ParticleChunkContext &particles;
};
} // namespace blender::sim