From 38e65331a8345054874e81668772dc8c66ad1a1e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 27 Jul 2020 16:26:32 +0200 Subject: Particles: initial support for events and actions The following nodes work now (although things can still be improved of course): Particle Birth Event, Praticle Time Step Event, Set Particle Attribute and Execute Condition. Multiple Set Particle Attribute nodes can be chained using the "Execute" sockets. They will be executed from left to right. --- source/blender/blenkernel/intern/node.c | 4 +- source/blender/functions/FN_attributes_ref.hh | 6 +- source/blender/functions/FN_cpp_type.hh | 16 +- source/blender/functions/FN_spans.hh | 40 ++ source/blender/functions/intern/attributes_ref.cc | 11 +- .../nodes/node_sim_set_particle_attribute.cc | 3 +- .../blender/simulation/intern/particle_function.cc | 21 +- .../blender/simulation/intern/particle_function.hh | 8 +- .../intern/simulation_collect_influences.cc | 518 ++++++++++++++++----- .../blender/simulation/intern/simulation_solver.cc | 26 +- .../intern/simulation_solver_influences.hh | 13 +- 11 files changed, 496 insertions(+), 170 deletions(-) (limited to 'source/blender') 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 void add(StringRef name, const T &default_value) + template bool add(StringRef name, const T &default_value) { - this->add(name, CPPType::get(), (const void *)&default_value); + return this->add(name, CPPType::get(), (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 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 class VSpan : public VSpanBase { BLI_assert(false); return *this->data_.single.data; } + + const T &as_single_element() const + { + BLI_assert(this->is_single_element()); + return (*this)[0]; + } + + Span as_full_array() const + { + BLI_assert(this->is_full_array()); + if (this->virtual_size_ == 0) { + return Span(); + } + const T *data = &(*this)[0]; + return Span(data, this->virtual_size_); + } }; /** @@ -395,6 +425,16 @@ class GVSpan : public VSpanBase { 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 fn::VSpan get(int output_index, StringRef expected_name) const + template fn::VSpan get(int output_index, StringRef expected_name = "") const { return this->get(output_index, expected_name).typed(); } 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 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 particle_simulation_nodes; + Map 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 particle_names); + +static const ParticleAction *create_particle_action(CollectContext &context, + const DInputSocket &dsocket, + Span 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 nodes_by_type(CollectContext &context, StringRefNull idname) +{ + return context.tree.nodes_by_type(idname); +} + +static Array find_linked_particle_simulations(CollectContext &context, + const DOutputSocket &output_socket) +{ + VectorSet 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> 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 attribute_dnodes = tree.nodes_by_type("SimulationNodeParticleAttribute"); + Span attribute_dnodes = nodes_by_type(context, "SimulationNodeParticleAttribute"); Vector 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> attribute_names = compute_global_string_inputs(network_map, - name_sockets); + std::optional> 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 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 sockets_to_compute, - ResourceCollector &resources, - DummyDataSources &data_sources) + CollectContext &context, Span 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 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( + per_particle_inputs.append(&context.resources.construct( AT, *attribute_name, socket->data_type().single_type())); } - const MultiFunction &per_particle_fn = resources.construct( + const MultiFunction &per_particle_fn = context.resources.construct( AT, dummy_deps.as_span(), sockets_to_compute); Array output_is_global(sockets_to_compute.size(), false); - const ParticleFunction &particle_fn = resources.construct( + const ParticleFunction &particle_fn = context.resources.construct( 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 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 forces = evaluator.get(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 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(AT, *particle_fn); + const ParticleForce &force = context.resources.construct(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 find_linked_particle_simulations(const DOutputSocket &output_socket) +static ParticleEmitter *create_particle_emitter(CollectContext &context, const DNode &dnode) { - Vector 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 simulation_dnodes = find_linked_particle_simulations(dnode.output(0)); - if (simulation_dnodes.size() == 0) { + Array names = find_linked_particle_simulations(context, dnode.output(0)); + if (names.size() == 0) { return nullptr; } - Array names{simulation_dnodes.size()}; - for (int i : simulation_dnodes.index_range()) { - names[i] = dnode_to_path(*simulation_dnodes[i]); - } - Array 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( + MultiFunction &inputs_fn = context.resources.construct( AT, Span(), 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( - 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( + 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 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 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 actions_; + public: + SequenceParticleAction(Span actions) : actions_(std::move(actions)) + { + } + void execute(ParticleActionContext &context) const override { - MutableSpan hashes = context.particle_chunk_context.attributes.get("Hash"); - MutableSpan velocities = context.particle_chunk_context.attributes.get( - "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 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 actions) +{ + Vector 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(AT, std::move(non_null_actions)); +} + +static const ParticleAction *create_set_particle_attribute_action( + CollectContext &context, const DOutputSocket &dsocket, Span 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> 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( + 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 conditions = evaluator.get(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 conditions_array = conditions.as_full_array(); + + Vector true_indices; + Vector 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 particle_names) { - RandomizeVelocityAction &action = resources.construct(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( + 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 particle_names) { - for (const DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { - std::string name = dnode_to_path(*dnode); - AttributesInfoBuilder &builder = resources.construct(AT); - builder.add("Position", {0, 0, 0}); - builder.add("Velocity", {0, 0, 0}); - builder.add("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( + AT); + attributes_builder.add("Position", {0, 0, 0}); + attributes_builder.add("Velocity", {0, 0, 0}); + attributes_builder.add("ID", 0); /* TODO: Use bool property, but need to add CD_PROP_BOOL first. */ - builder.add("Dead", 0); + attributes_builder.add("Dead", 0); /* TODO: Use uint32_t, but we don't have a corresponding custom property type. */ - builder.add("Hash", 0); - builder.add("Birth Time", 0.0f); - r_influences.particle_attributes_builder.add_new(std::move(name), &builder); + attributes_builder.add("Hash", 0); + attributes_builder.add("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(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 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 force_vectors{particle_amount, {0, 0, 0}}; Span 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 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 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 particle_forces; MultiValueMap particle_birth_actions; + MultiValueMap particle_time_step_begin_actions; + MultiValueMap particle_time_step_end_actions; Map particle_attributes_builder; Vector 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 force_dst; }; struct ParticleActionContext { SimulationSolveContext &solve_context; - MutableParticleChunkContext &particle_chunk_context; + ParticleChunkContext &particles; }; } // namespace blender::sim -- cgit v1.2.3