diff --git a/source/blender/simulation/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt index ec8880dd8cd..a19e96e1a91 100644 --- a/source/blender/simulation/CMakeLists.txt +++ b/source/blender/simulation/CMakeLists.txt @@ -41,13 +41,15 @@ set(SRC intern/hair_volume.cpp intern/implicit_blender.c intern/implicit_eigen.cpp - intern/simulation_solver.cc intern/particle_function.cc + intern/simulation_collect_influences.cc + intern/simulation_solver.cc intern/simulation_update.cc intern/ConstrainedConjugateGradient.h intern/eigen_utils.h intern/implicit.h + intern/simulation_collect_influences.hh intern/simulation_solver.hh SIM_mass_spring.h diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc new file mode 100644 index 00000000000..3feb0ccce5b --- /dev/null +++ b/source/blender/simulation/intern/simulation_collect_influences.cc @@ -0,0 +1,236 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "simulation_collect_influences.hh" +#include "SIM_particle_function.hh" + +#include "FN_attributes_ref.hh" +#include "FN_multi_function_network_evaluation.hh" +#include "FN_multi_function_network_optimization.hh" + +#include "NOD_node_tree_multi_function.hh" + +namespace blender::sim { + +extern "C" { +void WM_clipboard_text_set(const char *buf, bool selection); +} + +static Map deduplicate_attribute_nodes( + fn::MFNetwork &network, + nodes::MFNetworkTreeMap &network_map, + const nodes::DerivedNodeTree &tree) +{ + Span attribute_dnodes = tree.nodes_by_type( + "SimulationNodeParticleAttribute"); + uint amount = attribute_dnodes.size(); + if (amount == 0) { + return {}; + } + + Vector name_sockets; + for (const nodes::DNode *dnode : attribute_dnodes) { + fn::MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0)); + name_sockets.append(&name_socket); + } + + fn::MFNetworkEvaluator network_fn{{}, name_sockets.as_span()}; + + fn::MFParamsBuilder params{network_fn, 1}; + + Array attribute_names{amount, NoInitialization()}; + for (uint i : IndexRange(amount)) { + params.add_uninitialized_single_output( + fn::GMutableSpan(fn::CPPType::get(), attribute_names.data() + i, 1)); + } + + fn::MFContextBuilder context; + /* Todo: Check that the names don't depend on dummy nodes. */ + network_fn.call({0}, params, context); + + Map, Vector> + attribute_nodes_by_name_and_type; + for (uint i : IndexRange(amount)) { + attribute_nodes_by_name_and_type + .lookup_or_add_default({attribute_names[i], name_sockets[i]->node().output(0).data_type()}) + .append(&name_sockets[i]->node()); + } + + Map attribute_inputs; + for (auto item : attribute_nodes_by_name_and_type.items()) { + StringRef attribute_name = item.key.first; + fn::MFDataType data_type = item.key.second; + Span nodes = item.value; + + fn::MFOutputSocket &new_attribute_socket = network.add_input( + "Attribute '" + attribute_name + "'", data_type); + for (fn::MFNode *node : nodes) { + network.relink(node->output(0), new_attribute_socket); + } + network.remove(nodes); + + attribute_inputs.add_new(&new_attribute_socket, attribute_name); + } + + return attribute_inputs; +} + +class ParticleAttributeInput : public ParticleFunctionInput { + private: + std::string attribute_name_; + const fn::CPPType &attribute_type_; + + public: + ParticleAttributeInput(std::string attribute_name, const fn::CPPType &attribute_type) + : attribute_name_(std::move(attribute_name)), attribute_type_(attribute_type) + { + } + + void add_input(fn::AttributesRef attributes, + fn::MFParamsBuilder ¶ms, + ResourceCollector &UNUSED(resources)) const override + { + std::optional span = attributes.try_get(attribute_name_, attribute_type_); + if (span.has_value()) { + params.add_readonly_single_input(*span); + } + else { + params.add_readonly_single_input(fn::GVSpan::FromDefault(attribute_type_)); + } + } +}; + +static const ParticleFunction *create_particle_function_for_inputs( + Span sockets_to_compute, + ResourceCollector &resources, + const Map &attribute_inputs) +{ + BLI_assert(sockets_to_compute.size() >= 1); + const fn::MFNetwork &network = sockets_to_compute[0]->node().network(); + + VectorSet dummy_deps; + VectorSet unlinked_input_deps; + network.find_dependencies(sockets_to_compute, dummy_deps, unlinked_input_deps); + BLI_assert(unlinked_input_deps.size() == 0); + + Vector per_particle_inputs; + for (const fn::MFOutputSocket *socket : dummy_deps) { + const std::string *attribute_name = attribute_inputs.lookup_ptr(socket); + if (attribute_name == nullptr) { + return nullptr; + } + per_particle_inputs.append(&resources.construct( + AT, *attribute_name, socket->data_type().single_type())); + } + + const fn::MultiFunction &per_particle_fn = 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( + AT, + nullptr, + &per_particle_fn, + Span(), + per_particle_inputs.as_span(), + output_is_global.as_span()); + + return &particle_fn; +} + +class ParticleFunctionForce : public ParticleForce { + private: + const ParticleFunction &particle_fn_; + + public: + ParticleFunctionForce(const ParticleFunction &particle_fn) : particle_fn_(particle_fn) + { + } + + void add_force(fn::AttributesRef attributes, MutableSpan r_combined_force) const override + { + IndexMask mask = IndexRange(attributes.size()); + ParticleFunctionEvaluator evaluator{particle_fn_, mask, attributes}; + evaluator.compute(); + fn::VSpan forces = evaluator.get(0, "Force"); + for (uint i : mask) { + r_combined_force[i] += forces[i]; + } + } +}; + +static Vector create_forces_for_particle_simulation( + const nodes::DNode &simulation_node, + nodes::MFNetworkTreeMap &network_map, + ResourceCollector &resources, + const Map &attribute_inputs) +{ + Vector forces; + for (const nodes::DOutputSocket *origin_socket : + simulation_node.input(2, "Forces").linked_sockets()) { + const nodes::DNode &origin_node = origin_socket->node(); + if (origin_node.idname() != "SimulationNodeForce") { + continue; + } + + const fn::MFInputSocket &force_socket = network_map.lookup_dummy( + origin_node.input(0, "Force")); + + const ParticleFunction *particle_fn = create_particle_function_for_inputs( + {&force_socket}, resources, attribute_inputs); + + if (particle_fn == nullptr) { + continue; + } + + const ParticleForce &force = resources.construct(AT, *particle_fn); + forces.append(&force); + } + return forces; +} + +static void collect_forces(nodes::MFNetworkTreeMap &network_map, + ResourceCollector &resources, + const Map &attribute_inputs, + SimulationInfluences &r_influences) +{ + for (const nodes::DNode *dnode : + network_map.tree().nodes_by_type("SimulationNodeParticleSimulation")) { + std::string name = dnode_to_path(*dnode); + Vector forces = create_forces_for_particle_simulation( + *dnode, network_map, resources, attribute_inputs); + r_influences.particle_forces.add_new(std::move(name), std::move(forces)); + } +} + +void collect_simulation_influences(const nodes::DerivedNodeTree &tree, + ResourceCollector &resources, + SimulationInfluences &r_influences) +{ + fn::MFNetwork &network = resources.construct(AT); + nodes::MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); + Map attribute_inputs = deduplicate_attribute_nodes( + network, network_map, tree); + 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(network_map, resources, attribute_inputs, r_influences); +} + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_collect_influences.hh b/source/blender/simulation/intern/simulation_collect_influences.hh new file mode 100644 index 00000000000..a02a6320419 --- /dev/null +++ b/source/blender/simulation/intern/simulation_collect_influences.hh @@ -0,0 +1,45 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SIM_SIMULATION_COLLECT_INFLUENCES_HH__ +#define __SIM_SIMULATION_COLLECT_INFLUENCES_HH__ + +#include "NOD_derived_node_tree.hh" + +#include "BLI_resource_collector.hh" + +#include "simulation_solver.hh" + +namespace blender::sim { + +void collect_simulation_influences(const nodes::DerivedNodeTree &tree, + ResourceCollector &resources, + SimulationInfluences &r_influences); + +/* TODO: Move this to a better place. */ +inline std::string dnode_to_path(const nodes::DNode &dnode) +{ + std::string path; + for (const nodes::DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { + path = parent->node_ref().name() + "/" + path; + } + path = path + dnode.name(); + return path; +} + +} // namespace blender::sim + +#endif /* __SIM_SIMULATION_COLLECT_INFLUENCES_HH__ */ diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc index 289e586682e..c51e8e1a37f 100644 --- a/source/blender/simulation/intern/simulation_solver.cc +++ b/source/blender/simulation/intern/simulation_solver.cc @@ -121,7 +121,8 @@ void solve_simulation_time_step(Simulation &simulation, MutableSpan velocities = attributes.get("Velocity"); Array force_vectors{(uint)state->tot_particles, {0, 0, 0}}; - const Vector *forces = influences.particle_forces.lookup_ptr(state); + const Vector *forces = influences.particle_forces.lookup_ptr( + state->head.name); if (forces != nullptr) { for (const ParticleForce *force : *forces) { force->add_force(attributes, force_vectors); diff --git a/source/blender/simulation/intern/simulation_solver.hh b/source/blender/simulation/intern/simulation_solver.hh index c96cfee52da..818bcf067a3 100644 --- a/source/blender/simulation/intern/simulation_solver.hh +++ b/source/blender/simulation/intern/simulation_solver.hh @@ -36,7 +36,7 @@ class ParticleForce { }; struct SimulationInfluences { - Map> particle_forces; + Map> particle_forces; }; void initialize_simulation_states(Simulation &simulation, diff --git a/source/blender/simulation/intern/simulation_update.cc b/source/blender/simulation/intern/simulation_update.cc index 18d9ea8b853..a3fddbbe18f 100644 --- a/source/blender/simulation/intern/simulation_update.cc +++ b/source/blender/simulation/intern/simulation_update.cc @@ -32,18 +32,9 @@ #include "BLI_rand.h" #include "BLI_vector.hh" -#include "NOD_node_tree_multi_function.hh" - -#include "FN_attributes_ref.hh" -#include "FN_multi_function_network_evaluation.hh" -#include "FN_multi_function_network_optimization.hh" - +#include "simulation_collect_influences.hh" #include "simulation_solver.hh" -extern "C" { -void WM_clipboard_text_set(const char *buf, bool selection); -} - namespace blender::sim { static void copy_states_to_cow(Simulation *simulation_orig, Simulation *simulation_cow) @@ -69,75 +60,6 @@ static void copy_states_to_cow(Simulation *simulation_orig, Simulation *simulati } } -static Map deduplicate_attribute_nodes( - fn::MFNetwork &network, - nodes::MFNetworkTreeMap &network_map, - const nodes::DerivedNodeTree &tree) -{ - Span attribute_dnodes = tree.nodes_by_type( - "SimulationNodeParticleAttribute"); - uint amount = attribute_dnodes.size(); - if (amount == 0) { - return {}; - } - - Vector name_sockets; - for (const nodes::DNode *dnode : attribute_dnodes) { - fn::MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0)); - name_sockets.append(&name_socket); - } - - fn::MFNetworkEvaluator network_fn{{}, name_sockets.as_span()}; - - fn::MFParamsBuilder params{network_fn, 1}; - - Array attribute_names{amount, NoInitialization()}; - for (uint i : IndexRange(amount)) { - params.add_uninitialized_single_output( - fn::GMutableSpan(fn::CPPType::get(), attribute_names.data() + i, 1)); - } - - fn::MFContextBuilder context; - /* Todo: Check that the names don't depend on dummy nodes. */ - network_fn.call({0}, params, context); - - Map, Vector> - attribute_nodes_by_name_and_type; - for (uint i : IndexRange(amount)) { - attribute_nodes_by_name_and_type - .lookup_or_add_default({attribute_names[i], name_sockets[i]->node().output(0).data_type()}) - .append(&name_sockets[i]->node()); - } - - Map attribute_inputs; - for (auto item : attribute_nodes_by_name_and_type.items()) { - StringRef attribute_name = item.key.first; - fn::MFDataType data_type = item.key.second; - Span nodes = item.value; - - fn::MFOutputSocket &new_attribute_socket = network.add_input( - "Attribute '" + attribute_name + "'", data_type); - for (fn::MFNode *node : nodes) { - network.relink(node->output(0), new_attribute_socket); - } - network.remove(nodes); - - attribute_inputs.add_new(&new_attribute_socket, attribute_name); - } - - return attribute_inputs; -} - -static std::string dnode_to_path(const nodes::DNode &dnode) -{ - std::string path; - for (const nodes::DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { - path = parent->node_ref().name() + "/" + path; - } - path = path + dnode.name(); - return path; -} - static void remove_unused_states(Simulation *simulation, const VectorSet &state_names) { LISTBASE_FOREACH_MUTABLE (SimulationState *, state, &simulation->states) { @@ -209,138 +131,6 @@ static void update_simulation_state_list(Simulation *simulation, add_missing_particle_states(simulation, state_names); } -class ParticleAttributeInput : public ParticleFunctionInput { - private: - std::string attribute_name_; - const fn::CPPType &attribute_type_; - - public: - ParticleAttributeInput(std::string attribute_name, const fn::CPPType &attribute_type) - : attribute_name_(std::move(attribute_name)), attribute_type_(attribute_type) - { - } - - void add_input(fn::AttributesRef attributes, - fn::MFParamsBuilder ¶ms, - ResourceCollector &UNUSED(resources)) const override - { - std::optional span = attributes.try_get(attribute_name_, attribute_type_); - if (span.has_value()) { - params.add_readonly_single_input(*span); - } - else { - params.add_readonly_single_input(fn::GVSpan::FromDefault(attribute_type_)); - } - } -}; - -static const ParticleFunction *create_particle_function_for_inputs( - Span sockets_to_compute, - ResourceCollector &resources, - const Map &attribute_inputs) -{ - BLI_assert(sockets_to_compute.size() >= 1); - const fn::MFNetwork &network = sockets_to_compute[0]->node().network(); - - VectorSet dummy_deps; - VectorSet unlinked_input_deps; - network.find_dependencies(sockets_to_compute, dummy_deps, unlinked_input_deps); - BLI_assert(unlinked_input_deps.size() == 0); - - Vector per_particle_inputs; - for (const fn::MFOutputSocket *socket : dummy_deps) { - const std::string *attribute_name = attribute_inputs.lookup_ptr(socket); - if (attribute_name == nullptr) { - return nullptr; - } - per_particle_inputs.append(&resources.construct( - AT, *attribute_name, socket->data_type().single_type())); - } - - const fn::MultiFunction &per_particle_fn = 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( - AT, - nullptr, - &per_particle_fn, - Span(), - per_particle_inputs.as_span(), - output_is_global.as_span()); - - return &particle_fn; -} - -class ParticleFunctionForce : public ParticleForce { - private: - const ParticleFunction &particle_fn_; - - public: - ParticleFunctionForce(const ParticleFunction &particle_fn) : particle_fn_(particle_fn) - { - } - - void add_force(fn::AttributesRef attributes, MutableSpan r_combined_force) const override - { - IndexMask mask = IndexRange(attributes.size()); - ParticleFunctionEvaluator evaluator{particle_fn_, mask, attributes}; - evaluator.compute(); - fn::VSpan forces = evaluator.get(0, "Force"); - for (uint i : mask) { - r_combined_force[i] += forces[i]; - } - } -}; - -static Vector create_forces_for_particle_simulation( - const nodes::DNode &simulation_node, - nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - const Map &attribute_inputs) -{ - Vector forces; - for (const nodes::DOutputSocket *origin_socket : - simulation_node.input(2, "Forces").linked_sockets()) { - const nodes::DNode &origin_node = origin_socket->node(); - if (origin_node.idname() != "SimulationNodeForce") { - continue; - } - - const fn::MFInputSocket &force_socket = network_map.lookup_dummy( - origin_node.input(0, "Force")); - - const ParticleFunction *particle_fn = create_particle_function_for_inputs( - {&force_socket}, resources, attribute_inputs); - - if (particle_fn == nullptr) { - continue; - } - - const ParticleForce &force = resources.construct(AT, *particle_fn); - forces.append(&force); - } - return forces; -} - -static void collect_forces(Simulation &simulation, - nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - const Map &attribute_inputs, - SimulationInfluences &r_influences) -{ - for (const nodes::DNode *dnode : - network_map.tree().nodes_by_type("SimulationNodeParticleSimulation")) { - std::string name = dnode_to_path(*dnode); - Vector forces = create_forces_for_particle_simulation( - *dnode, network_map, resources, attribute_inputs); - ParticleSimulationState *state = (ParticleSimulationState *)try_find_state_by_name(&simulation, - name); - r_influences.particle_forces.add_new(state, std::move(forces)); - } -} - void update_simulation_in_depsgraph(Depsgraph *depsgraph, Scene *scene_cow, Simulation *simulation_cow) @@ -360,24 +150,15 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, nodes::NodeTreeRefMap tree_refs; /* TODO: Use simulation_cow, but need to add depsgraph relations before that. */ const nodes::DerivedNodeTree tree{simulation_orig->nodetree, tree_refs}; - fn::MFNetwork network; - ResourceCollector resources; - nodes::MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); - Map attribute_inputs = deduplicate_attribute_nodes( - network, network_map, tree); - 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); - SimulationInfluences simulation_influences; - collect_forces( - *simulation_orig, network_map, resources, attribute_inputs, simulation_influences); + ResourceCollector resources; + SimulationInfluences influences; + collect_simulation_influences(tree, resources, influences); if (current_frame == 1) { reinitialize_empty_simulation_states(simulation_orig, tree); - initialize_simulation_states(*simulation_orig, *depsgraph, simulation_influences); + initialize_simulation_states(*simulation_orig, *depsgraph, influences); simulation_orig->current_frame = 1; copy_states_to_cow(simulation_orig, simulation_cow); @@ -386,7 +167,7 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, update_simulation_state_list(simulation_orig, tree); float time_step = 1.0f / 24.0f; - solve_simulation_time_step(*simulation_orig, *depsgraph, simulation_influences, time_step); + solve_simulation_time_step(*simulation_orig, *depsgraph, influences, time_step); simulation_orig->current_frame = current_frame; copy_states_to_cow(simulation_orig, simulation_cow);