From 580d50091cfb0467cbde165058a6e78ef4898045 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 9 Jul 2020 15:40:27 +0200 Subject: Particles: Create a simulation state for every Particle Simulation node Every Particle Simulation node has a name (or a path when it is in a node group). This name has to be used in the Simulation modifier on a point cloud to see the particles. Caching has been disabled for now, because it was holding back development a bit. To reset the simulation, go back to frame 1. Currently, there is no way to influence the simulation. There are just some randomly moving points. Changing that is the next step. --- source/blender/blenkernel/intern/simulation.cc | 366 +++++++++++++++++++------ 1 file changed, 287 insertions(+), 79 deletions(-) (limited to 'source/blender/blenkernel/intern/simulation.cc') diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index c4a35141b0d..35ef664dce3 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -31,6 +31,7 @@ #include "BLI_float3.hh" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_span.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -44,6 +45,7 @@ #include "BKE_lib_remap.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_multi_function.hh" #include "BKE_pointcache.h" #include "BKE_simulation.h" @@ -51,9 +53,18 @@ #include "BLT_translation.h" +#include "FN_attributes_ref.hh" +#include "FN_cpp_types.hh" +#include "FN_multi_function_network_evaluation.hh" +#include "FN_multi_function_network_optimization.hh" + #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +extern "C" { +void WM_clipboard_text_set(const char *buf, bool selection); +} + static void simulation_init_data(ID *id) { Simulation *simulation = (Simulation *)id; @@ -63,14 +74,6 @@ static void simulation_init_data(ID *id) bNodeTree *ntree = ntreeAddTree(nullptr, "Simulation Nodetree", ntreeType_Simulation->idname); simulation->nodetree = ntree; - - /* Add a default particle simulation state for now. */ - ParticleSimulationState *state = (ParticleSimulationState *)MEM_callocN( - sizeof(ParticleSimulationState), __func__); - CustomData_reset(&state->attributes); - - state->point_cache = BKE_ptcache_add(&state->ptcaches); - BLI_addtail(&simulation->states, state); } static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int flag) @@ -89,19 +92,19 @@ static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons } BLI_listbase_clear(&simulation_dst->states); +} - LISTBASE_FOREACH (const SimulationState *, state_src, &simulation_src->states) { - switch ((eSimulationStateType)state_src->type) { - case SIM_STATE_TYPE_PARTICLES: { - ParticleSimulationState *particle_state_dst = (ParticleSimulationState *)MEM_callocN( - sizeof(ParticleSimulationState), __func__); - CustomData_reset(&particle_state_dst->attributes); +static void free_simulation_state_head(SimulationState *state) +{ + MEM_freeN(state->name); +} - BLI_addtail(&simulation_dst->states, particle_state_dst); - break; - } - } - } +static void free_particle_simulation_state(ParticleSimulationState *state) +{ + free_simulation_state_head(&state->head); + CustomData_free(&state->attributes, state->tot_particles); + BKE_ptcache_free_list(&state->ptcaches); + MEM_freeN(state); } static void simulation_free_data(ID *id) @@ -119,13 +122,10 @@ static void simulation_free_data(ID *id) LISTBASE_FOREACH_MUTABLE (SimulationState *, state, &simulation->states) { switch ((eSimulationStateType)state->type) { case SIM_STATE_TYPE_PARTICLES: { - ParticleSimulationState *particle_state = (ParticleSimulationState *)state; - CustomData_free(&particle_state->attributes, particle_state->tot_particles); - BKE_ptcache_free_list(&particle_state->ptcaches); + free_particle_simulation_state((ParticleSimulationState *)state); break; } } - MEM_freeN(state); } } @@ -166,59 +166,230 @@ void *BKE_simulation_add(Main *bmain, const char *name) namespace blender::bke { -static MutableSpan get_particle_positions(ParticleSimulationState *state) -{ - return MutableSpan( - (float3 *)CustomData_get_layer_named(&state->attributes, CD_LOCATION, "Position"), - state->tot_particles); -} - static void ensure_attributes_exist(ParticleSimulationState *state) { if (CustomData_get_layer_named(&state->attributes, CD_LOCATION, "Position") == nullptr) { CustomData_add_layer_named( &state->attributes, CD_LOCATION, CD_CALLOC, nullptr, state->tot_particles, "Position"); } + if (CustomData_get_layer_named(&state->attributes, CD_LOCATION, "Velocity") == nullptr) { + CustomData_add_layer_named( + &state->attributes, CD_LOCATION, CD_CALLOC, nullptr, state->tot_particles, "Velocity"); + } } -static void copy_particle_state_to_cow(ParticleSimulationState *state_orig, - ParticleSimulationState *state_cow) +static void copy_states_to_cow(Simulation *simulation_orig, Simulation *simulation_cow) { - ensure_attributes_exist(state_cow); - CustomData_free(&state_cow->attributes, state_cow->tot_particles); - CustomData_copy(&state_orig->attributes, - &state_cow->attributes, - CD_MASK_ALL, - CD_DUPLICATE, - state_orig->tot_particles); - state_cow->current_frame = state_orig->current_frame; - state_cow->tot_particles = state_orig->tot_particles; + LISTBASE_FOREACH_MUTABLE (SimulationState *, state_cow, &simulation_cow->states) { + switch ((eSimulationStateType)state_cow->type) { + case SIM_STATE_TYPE_PARTICLES: { + BLI_remlink(&simulation_cow->states, state_cow); + free_particle_simulation_state((ParticleSimulationState *)state_cow); + break; + } + } + } + simulation_cow->current_frame = simulation_orig->current_frame; + + LISTBASE_FOREACH (SimulationState *, state_orig, &simulation_orig->states) { + switch ((eSimulationStateType)state_orig->type) { + case SIM_STATE_TYPE_PARTICLES: { + ParticleSimulationState *particle_state_orig = (ParticleSimulationState *)state_orig; + ParticleSimulationState *particle_state_cow = (ParticleSimulationState *)MEM_callocN( + sizeof(*particle_state_cow), AT); + particle_state_cow->tot_particles = particle_state_orig->tot_particles; + particle_state_cow->head.name = BLI_strdup(state_orig->name); + CustomData_copy(&particle_state_orig->attributes, + &particle_state_cow->attributes, + CD_MASK_ALL, + CD_DUPLICATE, + particle_state_orig->tot_particles); + BLI_addtail(&simulation_cow->states, particle_state_cow); + break; + } + } + } } -static void simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation) +using AttributeNodeMap = Map>; + +static AttributeNodeMap deduplicate_attribute_nodes(fn::MFNetwork &network, + MFNetworkTreeMap &network_map, + const DerivedNodeTree &tree) { - int current_frame = scene->r.cfra; + Span attribute_dnodes = tree.nodes_by_type("SimulationNodeParticleAttribute"); + uint amount = attribute_dnodes.size(); + if (amount == 0) { + return {}; + } - ParticleSimulationState *state_cow = (ParticleSimulationState *)simulation->states.first; - ParticleSimulationState *state_orig = (ParticleSimulationState *)state_cow->head.orig_state; + Vector name_sockets; + for (const DNode *dnode : attribute_dnodes) { + fn::MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0)); + name_sockets.append(&name_socket); + } - if (current_frame == state_cow->current_frame) { - return; + 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_string, 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]->data_type()}) + .append(&name_sockets[i]->node()); } - /* Number of particles should be stored in the cache, but for now assume it is constant. */ - state_cow->tot_particles = state_orig->tot_particles; - CustomData_realloc(&state_cow->attributes, state_orig->tot_particles); - ensure_attributes_exist(state_cow); + AttributeNodeMap final_attribute_nodes; + 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; - PTCacheID pid_cow; - BKE_ptcache_id_from_sim_particles(&pid_cow, state_cow); - BKE_ptcache_id_time(&pid_cow, scene, current_frame, nullptr, nullptr, nullptr); + 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); + + final_attribute_nodes.add_new(&new_attribute_socket.node().as_dummy(), item.key); + } + + return final_attribute_nodes; +} + +class CustomDataAttributesRef { + private: + Vector buffers_; + uint size_; + std::unique_ptr info_; + + public: + CustomDataAttributesRef(CustomData &custom_data, uint size) + { + fn::AttributesInfoBuilder builder; + for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) { + buffers_.append(layer.data); + builder.add(layer.name, {0, 0, 0}); + } + info_ = std::make_unique(builder); + size_ = size; + } + + operator fn::MutableAttributesRef() + { + return fn::MutableAttributesRef(*info_, buffers_, size_); + } - /* If successfull, this will read the state directly into the cow state. */ - int cache_result = BKE_ptcache_read(&pid_cow, current_frame, true); - if (cache_result == PTCACHE_READ_EXACT) { - state_cow->current_frame = current_frame; + operator fn::AttributesRef() const + { + return fn::AttributesRef(*info_, buffers_, size_); + } +}; + +static std::string dnode_to_path(const DNode &dnode) +{ + std::string path; + for (const 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) { + if (!state_names.contains(state->name)) { + BLI_remlink(&simulation->states, state); + free_particle_simulation_state((ParticleSimulationState *)state); + } + } +} + +static void reset_states(Simulation *simulation) +{ + LISTBASE_FOREACH (SimulationState *, state, &simulation->states) { + switch ((eSimulationStateType)state->type) { + case SIM_STATE_TYPE_PARTICLES: { + ParticleSimulationState *particle_state = (ParticleSimulationState *)state; + CustomData_free(&particle_state->attributes, particle_state->tot_particles); + particle_state->tot_particles = 0; + break; + } + } + } +} + +static SimulationState *try_find_state_by_name(Simulation *simulation, StringRef name) +{ + LISTBASE_FOREACH (SimulationState *, state, &simulation->states) { + if (state->name == name) { + return state; + } + } + return nullptr; +} + +static void add_missing_particle_states(Simulation *simulation, Span state_names) +{ + for (StringRefNull name : state_names) { + SimulationState *state = try_find_state_by_name(simulation, name); + if (state != nullptr) { + BLI_assert(state->type == SIM_STATE_TYPE_PARTICLES); + continue; + } + + ParticleSimulationState *particle_state = (ParticleSimulationState *)MEM_callocN( + sizeof(*particle_state), AT); + particle_state->head.type = SIM_STATE_TYPE_PARTICLES; + particle_state->head.name = BLI_strdup(name.data()); + CustomData_reset(&particle_state->attributes); + particle_state->point_cache = BKE_ptcache_add(&particle_state->ptcaches); + BLI_addtail(&simulation->states, particle_state); + } +} + +static void reinitialize_empty_simulation_states(Simulation *simulation, + const DerivedNodeTree &tree) +{ + VectorSet state_names; + for (const DNode *dnode : tree.nodes_by_type("SimulationNodeParticleSimulation")) { + state_names.add(dnode_to_path(*dnode)); + } + + remove_unused_states(simulation, state_names); + reset_states(simulation); + add_missing_particle_states(simulation, state_names); +} + +static void update_simulation_state_list(Simulation *simulation, const DerivedNodeTree &tree) +{ + VectorSet state_names; + for (const DNode *dnode : tree.nodes_by_type("SimulationNodeParticleSimulation")) { + state_names.add(dnode_to_path(*dnode)); + } + + remove_unused_states(simulation, state_names); + add_missing_particle_states(simulation, state_names); +} + +static void simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation_cow) +{ + int current_frame = scene->r.cfra; + if (simulation_cow->current_frame == current_frame) { return; } @@ -227,34 +398,71 @@ static void simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulatio return; } - PTCacheID pid_orig; - BKE_ptcache_id_from_sim_particles(&pid_orig, state_orig); - BKE_ptcache_id_time(&pid_orig, scene, current_frame, nullptr, nullptr, nullptr); + Simulation *simulation_orig = (Simulation *)DEG_get_original_id(&simulation_cow->id); + + NodeTreeRefMap tree_refs; + /* TODO: Use simulation_cow, but need to add depsgraph relations before that. */ + const DerivedNodeTree tree{simulation_orig->nodetree, tree_refs}; + fn::MFNetwork network; + ResourceCollector resources; + MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); + AttributeNodeMap attribute_node_map = 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); + UNUSED_VARS(attribute_node_map); + // WM_clipboard_text_set(network.to_dot().c_str(), false); if (current_frame == 1) { - state_orig->tot_particles = 100; - state_orig->current_frame = 1; - CustomData_realloc(&state_orig->attributes, state_orig->tot_particles); - ensure_attributes_exist(state_orig); - - MutableSpan positions = get_particle_positions(state_orig); - for (uint i : positions.index_range()) { - positions[i] = {i / 10.0f, 0, 0}; + reinitialize_empty_simulation_states(simulation_orig, tree); + + RNG *rng = BLI_rng_new(0); + + simulation_orig->current_frame = 1; + LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation_orig->states) { + state->tot_particles = 100; + CustomData_realloc(&state->attributes, state->tot_particles); + ensure_attributes_exist(state); + + CustomDataAttributesRef custom_data_attributes{state->attributes, + (uint)state->tot_particles}; + + fn::MutableAttributesRef attributes = custom_data_attributes; + MutableSpan positions = attributes.get("Position"); + MutableSpan velocities = attributes.get("Velocity"); + + for (uint i : positions.index_range()) { + positions[i] = {i / 10.0f, 0, 0}; + velocities[i] = {0, BLI_rng_get_float(rng), BLI_rng_get_float(rng) * 2 + 1}; + } } - BKE_ptcache_write(&pid_orig, current_frame); - copy_particle_state_to_cow(state_orig, state_cow); + BLI_rng_free(rng); + + copy_states_to_cow(simulation_orig, simulation_cow); } - else if (current_frame == state_orig->current_frame + 1) { - state_orig->current_frame = current_frame; - ensure_attributes_exist(state_orig); - MutableSpan positions = get_particle_positions(state_orig); - for (float3 &position : positions) { - position.z += 0.1f; + else if (current_frame == simulation_orig->current_frame + 1) { + update_simulation_state_list(simulation_orig, tree); + float time_step = 1.0f / 24.0f; + simulation_orig->current_frame = current_frame; + + LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation_orig->states) { + ensure_attributes_exist(state); + + CustomDataAttributesRef custom_data_attributes{state->attributes, + (uint)state->tot_particles}; + + fn::MutableAttributesRef attributes = custom_data_attributes; + MutableSpan positions = attributes.get("Position"); + MutableSpan velocities = attributes.get("Velocity"); + + for (uint i : positions.index_range()) { + velocities[i].z += -1.0f * time_step; + positions[i] += velocities[i] * time_step; + } } - BKE_ptcache_write(&pid_orig, current_frame); - copy_particle_state_to_cow(state_orig, state_cow); + copy_states_to_cow(simulation_orig, simulation_cow); } } -- cgit v1.2.3