diff options
author | Jacques Lucke <jacques@blender.org> | 2020-07-09 16:40:27 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2020-07-09 16:40:27 +0300 |
commit | 580d50091cfb0467cbde165058a6e78ef4898045 (patch) | |
tree | f123808f3397df3a273f991e9e3239e603d68ce0 /source/blender/blenkernel | |
parent | 31ad43a3c7dde771cb6d92b6d20bf7b2e3f2bed6 (diff) |
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.
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_pointcache.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/customdata.c | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pointcache.c | 20 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/simulation.cc | 366 |
4 files changed, 296 insertions, 96 deletions
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index b0973ed458c..f919a31c165 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -296,7 +296,9 @@ void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, struct Object *ob, struct DynamicPaintSurface *surface); void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, struct Object *ob, struct RigidBodyWorld *rbw); -void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, struct ParticleSimulationState *state); +void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, + struct ParticleSimulationState *state_orig, + struct ParticleSimulationState *state_cow); PTCacheID BKE_ptcache_id_find(struct Object *ob, struct Scene *scene, struct PointCache *cache); void BKE_ptcache_ids_from_object(struct ListBase *lb, diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 76bc7a7bfb7..c11fa69db76 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1772,7 +1772,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 42: CD_SCULPT_FACE_SETS */ {sizeof(int), "", 0, NULL, NULL, NULL, NULL, NULL, NULL}, /* 43: CD_LOCATION */ - {sizeof(float[3]), "vec3f", 1, NULL, NULL, NULL, NULL, NULL, NULL}, + {sizeof(float[3]), "vec3f", 1, "Location", NULL, NULL, NULL, NULL, NULL}, /* 44: CD_RADIUS */ {sizeof(float), "MFloatProperty", 1, NULL, NULL, NULL, NULL, NULL, NULL}, /* 45: CD_HAIRCURVE */ diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index dbd6d99c7fe..ce5402551b6 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -1898,19 +1898,13 @@ static void ptcache_sim_particle_read( PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, positions + (index * 3)); } -void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, ParticleSimulationState *state) +void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, + ParticleSimulationState *state_orig, + ParticleSimulationState *state_cow) { memset(pid, 0, sizeof(PTCacheID)); - ParticleSimulationState *state_orig; - if (state->head.orig_state != NULL) { - state_orig = (ParticleSimulationState *)state->head.orig_state; - } - else { - state_orig = state; - } - - pid->calldata = state; + pid->calldata = state_cow; pid->type = PTCACHE_TYPE_SIM_PARTICLES; pid->cache = state_orig->point_cache; pid->cache_ptr = &state_orig->point_cache; @@ -2050,11 +2044,7 @@ static bool foreach_object_modifier_ptcache(Object *object, LISTBASE_FOREACH (SimulationState *, state, &smd->simulation->states) { switch ((eSimulationStateType)state->type) { case SIM_STATE_TYPE_PARTICLES: { - ParticleSimulationState *particle_state = (ParticleSimulationState *)state; - BKE_ptcache_id_from_sim_particles(&pid, particle_state); - if (!callback(&pid, callback_user_data)) { - return false; - } + /* TODO(jacques) */ break; } } 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<float3> get_particle_positions(ParticleSimulationState *state) -{ - return MutableSpan<float3>( - (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<fn::MFDummyNode *, std::pair<std::string, fn::MFDataType>>; + +static AttributeNodeMap deduplicate_attribute_nodes(fn::MFNetwork &network, + MFNetworkTreeMap &network_map, + const DerivedNodeTree &tree) { - int current_frame = scene->r.cfra; + Span<const DNode *> 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<fn::MFInputSocket *> 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<std::string> 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<std::pair<std::string, fn::MFDataType>, Vector<fn::MFNode *>> + 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<fn::MFNode *> 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<void *> buffers_; + uint size_; + std::unique_ptr<fn::AttributesInfo> 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<float3>(layer.name, {0, 0, 0}); + } + info_ = std::make_unique<fn::AttributesInfo>(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<std::string> &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<std::string> 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<std::string> 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<std::string> 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<float3> 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<float3> positions = attributes.get<float3>("Position"); + MutableSpan<float3> velocities = attributes.get<float3>("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<float3> 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<float3> positions = attributes.get<float3>("Position"); + MutableSpan<float3> velocities = attributes.get<float3>("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); } } |