[Bf-blender-cvs] [4f4af0cbe17] master: Particles: support removing particles during the simulation

Jacques Lucke noreply at git.blender.org
Mon Jul 20 15:32:26 CEST 2020


Commit: 4f4af0cbe17fe9781aeaefb7e0d2f554bdb9c1a5
Author: Jacques Lucke
Date:   Mon Jul 20 15:30:12 2020 +0200
Branches: master
https://developer.blender.org/rB4f4af0cbe17fe9781aeaefb7e0d2f554bdb9c1a5

Particles: support removing particles during the simulation

This still cannot be controlled by the user. Currently, all particles are
killed after two seconds

===================================================================

M	source/blender/blenlib/BLI_span.hh
M	source/blender/functions/FN_attributes_ref.hh
M	source/blender/simulation/intern/particle_allocator.hh
M	source/blender/simulation/intern/simulation_collect_influences.cc
M	source/blender/simulation/intern/simulation_solver.cc
M	source/blender/simulation/intern/simulation_solver.hh

===================================================================

diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh
index 67bb32304de..2d875fe73be 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -593,6 +593,21 @@ template<typename T> class MutableSpan {
     return start_[size_ - 1];
   }
 
+  /**
+   * Does a linear search to count how often the value is in the array.
+   * Returns the number of occurrences.
+   */
+  int64_t count(const T &value) const
+  {
+    int64_t counter = 0;
+    for (const T &element : *this) {
+      if (element == value) {
+        counter++;
+      }
+    }
+    return counter;
+  }
+
   /**
    * Returns a new span to the same underlying memory buffer. No conversions are done.
    */
diff --git a/source/blender/functions/FN_attributes_ref.hh b/source/blender/functions/FN_attributes_ref.hh
index 2af35317827..ed14676731e 100644
--- a/source/blender/functions/FN_attributes_ref.hh
+++ b/source/blender/functions/FN_attributes_ref.hh
@@ -179,6 +179,11 @@ class MutableAttributesRef {
     return range_.size();
   }
 
+  IndexRange index_range() const
+  {
+    return IndexRange(this->size());
+  }
+
   const AttributesInfo &info() const
   {
     return *info_;
diff --git a/source/blender/simulation/intern/particle_allocator.hh b/source/blender/simulation/intern/particle_allocator.hh
index b742459b3c2..1e7578a75ed 100644
--- a/source/blender/simulation/intern/particle_allocator.hh
+++ b/source/blender/simulation/intern/particle_allocator.hh
@@ -77,6 +77,11 @@ class ParticleAllocator : NonCopyable, NonMovable {
   {
   }
 
+  const fn::AttributesInfo &attributes_info() const
+  {
+    return attributes_allocator_.attributes_info();
+  }
+
   Span<fn::MutableAttributesRef> get_allocations() const
   {
     return attributes_allocator_.get_allocations();
diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc
index c1b936d9aa3..d4161b06a00 100644
--- a/source/blender/simulation/intern/simulation_collect_influences.cc
+++ b/source/blender/simulation/intern/simulation_collect_influences.cc
@@ -278,10 +278,12 @@ class MyBasicEmitter : public ParticleEmitter {
 
     MutableSpan<float3> positions = attributes.get<float3>("Position");
     MutableSpan<float3> velocities = attributes.get<float3>("Velocity");
+    MutableSpan<float> birth_times = attributes.get<float>("Birth Time");
 
     for (int i : IndexRange(attributes.size())) {
       positions[i] = rng.get_unit_float3();
       velocities[i] = rng.get_unit_float3();
+      birth_times[i] = context.simulation_time_interval().start();
     }
   }
 };
@@ -307,6 +309,9 @@ static void prepare_particle_attribute_builders(nodes::MFNetworkTreeMap &network
     builder.add<float3>("Position", {0, 0, 0});
     builder.add<float3>("Velocity", {0, 0, 0});
     builder.add<int>("ID", 0);
+    /* TODO: Use bool property, but need to add CD_PROP_BOOL first. */
+    builder.add<int>("Dead", 0);
+    builder.add<float>("Birth Time", 0.0f);
     r_influences.particle_attributes_builder.add_new(std::move(name), &builder);
   }
 }
diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc
index 8460b9e148e..f542d6ab2c8 100644
--- a/source/blender/simulation/intern/simulation_solver.cc
+++ b/source/blender/simulation/intern/simulation_solver.cc
@@ -125,6 +125,124 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At
   }
 }
 
+BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context,
+                                                     ParticleSimulationState &state,
+                                                     const fn::AttributesInfo &attributes_info)
+{
+  CustomDataAttributesRef custom_data_attributes{
+      state.attributes, state.tot_particles, attributes_info};
+  fn::MutableAttributesRef attributes = custom_data_attributes;
+
+  Array<float3> force_vectors{state.tot_particles, {0, 0, 0}};
+  const Vector<const ParticleForce *> *forces =
+      solve_context.influences().particle_forces.lookup_ptr(state.head.name);
+
+  if (forces != nullptr) {
+    ParticleChunkContext particle_chunk_context{IndexMask(state.tot_particles), attributes};
+    ParticleForceContext particle_force_context{
+        solve_context, particle_chunk_context, force_vectors};
+
+    for (const ParticleForce *force : *forces) {
+      force->add_force(particle_force_context);
+    }
+  }
+
+  MutableSpan<float3> positions = attributes.get<float3>("Position");
+  MutableSpan<float3> velocities = attributes.get<float3>("Velocity");
+  MutableSpan<float> birth_times = attributes.get<float>("Birth Time");
+  MutableSpan<int> dead_states = attributes.get<int>("Dead");
+  float end_time = solve_context.solve_interval().end();
+  float time_step = solve_context.solve_interval().duration();
+  for (int i : positions.index_range()) {
+    velocities[i] += force_vectors[i] * time_step;
+    positions[i] += velocities[i] * time_step;
+
+    if (end_time - birth_times[i] > 2) {
+      dead_states[i] = true;
+    }
+  }
+}
+
+BLI_NOINLINE static void run_emitters(SimulationSolveContext &solve_context,
+                                      ParticleAllocators &particle_allocators)
+{
+  for (const ParticleEmitter *emitter : solve_context.influences().particle_emitters) {
+    ParticleEmitterContext emitter_context{
+        solve_context, particle_allocators, solve_context.solve_interval()};
+    emitter->emit(emitter_context);
+  }
+}
+
+BLI_NOINLINE static int count_particles_after_time_step(ParticleSimulationState &state,
+                                                        ParticleAllocator &allocator)
+{
+  CustomDataAttributesRef custom_data_attributes{
+      state.attributes, state.tot_particles, allocator.attributes_info()};
+  fn::MutableAttributesRef attributes = custom_data_attributes;
+  int new_particle_amount = attributes.get<int>("Dead").count(0);
+
+  for (fn::MutableAttributesRef emitted_attributes : allocator.get_allocations()) {
+    new_particle_amount += emitted_attributes.get<int>("Dead").count(0);
+  }
+
+  return new_particle_amount;
+}
+
+BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationState &state,
+                                                           ParticleAllocator &allocator)
+{
+  const int new_particle_amount = count_particles_after_time_step(state, allocator);
+
+  CustomDataAttributesRef custom_data_attributes{
+      state.attributes, state.tot_particles, allocator.attributes_info()};
+
+  Vector<fn::MutableAttributesRef> particle_sources;
+  particle_sources.append(custom_data_attributes);
+  particle_sources.extend(allocator.get_allocations());
+
+  CustomDataLayer *dead_layer = nullptr;
+
+  for (CustomDataLayer &layer : MutableSpan(state.attributes.layers, state.attributes.totlayer)) {
+    StringRefNull name = layer.name;
+    if (name == "Dead") {
+      dead_layer = &layer;
+      continue;
+    }
+    const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type);
+    fn::GMutableSpan new_buffer{
+        cpp_type,
+        MEM_mallocN_aligned(new_particle_amount * cpp_type.size(), cpp_type.alignment(), AT),
+        new_particle_amount};
+
+    int current = 0;
+    for (fn::MutableAttributesRef attributes : particle_sources) {
+      Span<int> dead_states = attributes.get<int>("Dead");
+      fn::GSpan source_buffer = attributes.get(name);
+      BLI_assert(source_buffer.type() == cpp_type);
+      for (int i : attributes.index_range()) {
+        if (dead_states[i] == 0) {
+          cpp_type.copy_to_uninitialized(source_buffer[i], new_buffer[current]);
+          current++;
+        }
+      }
+    }
+
+    if (layer.data != nullptr) {
+      MEM_freeN(layer.data);
+    }
+    layer.data = new_buffer.buffer();
+  }
+
+  BLI_assert(dead_layer != nullptr);
+  if (dead_layer->data != nullptr) {
+    MEM_freeN(dead_layer->data);
+  }
+  dead_layer->data = MEM_callocN(sizeof(int) * new_particle_amount, AT);
+
+  state.tot_particles = new_particle_amount;
+  state.next_particle_id += allocator.total_allocated();
+}
+
 void initialize_simulation_states(Simulation &simulation,
                                   Depsgraph &UNUSED(depsgraph),
                                   const SimulationInfluences &UNUSED(influences))
@@ -137,89 +255,47 @@ void solve_simulation_time_step(Simulation &simulation,
                                 const SimulationInfluences &influences,
                                 float time_step)
 {
-  SimulationSolveContext solve_context{simulation, depsgraph, influences};
+  SimulationSolveContext solve_context{
+      simulation,
+      depsgraph,
+      influences,
+      TimeInterval(simulation.current_simulation_time, time_step)};
   TimeInterval simulation_time_interval{simulation.current_simulation_time, time_step};
 
+  Vector<SimulationState *> simulation_states{simulation.states};
+  Vector<ParticleSimulationState *> particle_simulation_states;
+  for (SimulationState *state : simulation_states) {
+    if (state->type == SIM_STATE_TYPE_PARTICLES) {
+      particle_simulation_states.append((ParticleSimulationState *)state);
+    }
+  }
+
   Map<std::string, std::unique_ptr<fn::AttributesInfo>> attribute_infos;
-  Map<std::string, std::unique_ptr<ParticleAllocator>> particle_allocators;
-  LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation.states) {
+  Map<std::string, std::unique_ptr<ParticleAllocator>> particle_allocators_map;
+  for (ParticleSimulationState *state : particle_simulation_states) {
     const fn::AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as(
         state->head.name);
     auto info = std::make_unique<fn::AttributesInfo>(builder);
 
     ensure_attributes_exist(state, *info);
 
-    particle_allocators.add_new(
+    particle_allocators_map.add_new(
         state->head.name, std::make_unique<ParticleAllocator>(*info, state->next_particle_id));
     attribute_infos.add_new(state->head.name, std::move(info));
   }
 
-  LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation.states) {
+ 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list