diff options
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 |