2020-07-17 20:51:52 +02:00
|
|
|
/*
|
|
|
|
|
* 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_solver.hh"
|
|
|
|
|
|
|
|
|
|
#include "BKE_customdata.h"
|
2020-07-21 17:20:05 +02:00
|
|
|
#include "BKE_persistent_data_handle.hh"
|
2020-07-17 20:51:52 +02:00
|
|
|
|
|
|
|
|
#include "BLI_rand.hh"
|
2020-07-21 17:20:05 +02:00
|
|
|
#include "BLI_set.hh"
|
2020-07-17 20:51:52 +02:00
|
|
|
|
2020-07-22 17:04:18 +02:00
|
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
|
|
2020-07-17 20:51:52 +02:00
|
|
|
namespace blender::sim {
|
|
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
static CustomDataType cpp_to_custom_data_type(const CPPType &type)
|
2020-07-19 22:06:35 +02:00
|
|
|
{
|
|
|
|
|
if (type.is<float3>()) {
|
|
|
|
|
return CD_PROP_FLOAT3;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<float>()) {
|
|
|
|
|
return CD_PROP_FLOAT;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<int32_t>()) {
|
|
|
|
|
return CD_PROP_INT32;
|
|
|
|
|
}
|
|
|
|
|
BLI_assert(false);
|
|
|
|
|
return CD_PROP_FLOAT;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
static const CPPType &custom_to_cpp_data_type(CustomDataType type)
|
2020-07-19 22:06:35 +02:00
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case CD_PROP_FLOAT3:
|
2020-07-24 13:37:55 +02:00
|
|
|
return CPPType::get<float3>();
|
2020-07-19 22:06:35 +02:00
|
|
|
case CD_PROP_FLOAT:
|
2020-07-24 13:37:55 +02:00
|
|
|
return CPPType::get<float>();
|
2020-07-19 22:06:35 +02:00
|
|
|
case CD_PROP_INT32:
|
2020-07-24 13:37:55 +02:00
|
|
|
return CPPType::get<int32_t>();
|
2020-07-19 22:06:35 +02:00
|
|
|
default:
|
|
|
|
|
BLI_assert(false);
|
2020-07-24 13:37:55 +02:00
|
|
|
return CPPType::get<float>();
|
2020-07-19 22:06:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-17 20:51:52 +02:00
|
|
|
class CustomDataAttributesRef {
|
|
|
|
|
private:
|
2020-07-19 22:06:35 +02:00
|
|
|
Array<void *> buffers_;
|
2020-07-20 12:16:20 +02:00
|
|
|
int64_t size_;
|
2020-07-24 13:37:55 +02:00
|
|
|
const AttributesInfo &info_;
|
2020-07-17 20:51:52 +02:00
|
|
|
|
|
|
|
|
public:
|
2020-07-24 13:37:55 +02:00
|
|
|
CustomDataAttributesRef(CustomData &custom_data, int64_t size, const AttributesInfo &info)
|
2020-07-19 22:06:35 +02:00
|
|
|
: buffers_(info.size(), nullptr), size_(size), info_(info)
|
2020-07-17 20:51:52 +02:00
|
|
|
{
|
2020-07-20 12:16:20 +02:00
|
|
|
for (int attribute_index : info.index_range()) {
|
2020-07-19 22:06:35 +02:00
|
|
|
StringRefNull name = info.name_of(attribute_index);
|
2020-07-24 13:37:55 +02:00
|
|
|
const CPPType &cpp_type = info.type_of(attribute_index);
|
2020-07-19 22:06:35 +02:00
|
|
|
CustomDataType custom_type = cpp_to_custom_data_type(cpp_type);
|
|
|
|
|
void *data = CustomData_get_layer_named(&custom_data, custom_type, name.c_str());
|
|
|
|
|
buffers_[attribute_index] = data;
|
2020-07-17 20:51:52 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
operator MutableAttributesRef()
|
2020-07-17 20:51:52 +02:00
|
|
|
{
|
2020-07-24 13:37:55 +02:00
|
|
|
return MutableAttributesRef(info_, buffers_, size_);
|
2020-07-17 20:51:52 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
operator AttributesRef() const
|
2020-07-17 20:51:52 +02:00
|
|
|
{
|
2020-07-24 13:37:55 +02:00
|
|
|
return AttributesRef(info_, buffers_, size_);
|
2020-07-17 20:51:52 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
static void ensure_attributes_exist(ParticleSimulationState *state, const AttributesInfo &info)
|
2020-07-17 20:51:52 +02:00
|
|
|
{
|
2020-07-19 22:06:35 +02:00
|
|
|
bool found_layer_to_remove;
|
|
|
|
|
do {
|
|
|
|
|
found_layer_to_remove = false;
|
|
|
|
|
for (int layer_index = 0; layer_index < state->attributes.totlayer; layer_index++) {
|
|
|
|
|
CustomDataLayer *layer = &state->attributes.layers[layer_index];
|
|
|
|
|
BLI_assert(layer->name != nullptr);
|
2020-07-24 13:37:55 +02:00
|
|
|
const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type);
|
2020-07-19 22:06:35 +02:00
|
|
|
StringRefNull name = layer->name;
|
|
|
|
|
if (!info.has_attribute(name, cpp_type)) {
|
|
|
|
|
found_layer_to_remove = true;
|
|
|
|
|
CustomData_free_layer(&state->attributes, layer->type, state->tot_particles, layer_index);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (found_layer_to_remove);
|
|
|
|
|
|
2020-07-20 12:16:20 +02:00
|
|
|
for (int attribute_index : info.index_range()) {
|
2020-07-19 22:06:35 +02:00
|
|
|
StringRefNull attribute_name = info.name_of(attribute_index);
|
2020-07-24 13:37:55 +02:00
|
|
|
const CPPType &cpp_type = info.type_of(attribute_index);
|
2020-07-19 22:06:35 +02:00
|
|
|
CustomDataType custom_type = cpp_to_custom_data_type(cpp_type);
|
|
|
|
|
if (CustomData_get_layer_named(&state->attributes, custom_type, attribute_name.c_str()) ==
|
|
|
|
|
nullptr) {
|
|
|
|
|
void *data = CustomData_add_layer_named(&state->attributes,
|
|
|
|
|
custom_type,
|
|
|
|
|
CD_CALLOC,
|
|
|
|
|
nullptr,
|
|
|
|
|
state->tot_particles,
|
|
|
|
|
attribute_name.c_str());
|
2020-07-20 12:16:20 +02:00
|
|
|
cpp_type.fill_uninitialized(info.default_of(attribute_index), data, state->tot_particles);
|
2020-07-19 22:06:35 +02:00
|
|
|
}
|
2020-07-17 20:51:52 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-23 23:03:19 +02:00
|
|
|
BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_context,
|
|
|
|
|
ParticleSimulationState &state,
|
2020-07-24 13:37:55 +02:00
|
|
|
MutableAttributesRef attributes,
|
2020-07-23 23:03:19 +02:00
|
|
|
MutableSpan<float> remaining_durations,
|
|
|
|
|
float end_time)
|
2020-07-20 15:30:12 +02:00
|
|
|
{
|
2020-07-23 23:03:19 +02:00
|
|
|
int particle_amount = attributes.size();
|
2020-07-27 16:26:32 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-23 23:03:19 +02:00
|
|
|
Array<float3> force_vectors{particle_amount, {0, 0, 0}};
|
2020-07-24 13:37:55 +02:00
|
|
|
Span<const ParticleForce *> forces = solve_context.influences.particle_forces.lookup_as(
|
2020-07-23 23:03:19 +02:00
|
|
|
state.head.name);
|
|
|
|
|
for (const ParticleForce *force : forces) {
|
2020-07-27 16:26:32 +02:00
|
|
|
ParticleChunkContext particles{IndexMask(particle_amount), attributes};
|
|
|
|
|
ParticleForceContext particle_force_context{solve_context, particles, force_vectors};
|
2020-07-23 23:03:19 +02:00
|
|
|
force->add_force(particle_force_context);
|
2020-07-20 15:30:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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");
|
2020-07-23 23:03:19 +02:00
|
|
|
|
|
|
|
|
for (int i : IndexRange(particle_amount)) {
|
|
|
|
|
const float time_step = remaining_durations[i];
|
2020-07-20 15:30:12 +02:00
|
|
|
velocities[i] += force_vectors[i] * time_step;
|
|
|
|
|
positions[i] += velocities[i] * time_step;
|
|
|
|
|
|
|
|
|
|
if (end_time - birth_times[i] > 2) {
|
|
|
|
|
dead_states[i] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-27 16:26:32 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
2020-07-20 15:30:12 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-23 23:03:19 +02:00
|
|
|
BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context,
|
|
|
|
|
ParticleSimulationState &state,
|
2020-07-24 13:37:55 +02:00
|
|
|
const AttributesInfo &attributes_info)
|
2020-07-23 23:03:19 +02:00
|
|
|
{
|
|
|
|
|
CustomDataAttributesRef custom_data_attributes{
|
|
|
|
|
state.attributes, state.tot_particles, attributes_info};
|
2020-07-24 13:37:55 +02:00
|
|
|
MutableAttributesRef attributes = custom_data_attributes;
|
2020-07-23 23:03:19 +02:00
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
Array<float> remaining_durations(state.tot_particles, solve_context.solve_interval.duration());
|
2020-07-23 23:03:19 +02:00
|
|
|
simulate_particle_chunk(
|
2020-07-25 14:51:15 +02:00
|
|
|
solve_context, state, attributes, remaining_durations, solve_context.solve_interval.stop());
|
2020-07-23 23:03:19 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 15:30:12 +02:00
|
|
|
BLI_NOINLINE static void run_emitters(SimulationSolveContext &solve_context,
|
|
|
|
|
ParticleAllocators &particle_allocators)
|
|
|
|
|
{
|
2020-07-24 13:37:55 +02:00
|
|
|
for (const ParticleEmitter *emitter : solve_context.influences.particle_emitters) {
|
2020-07-20 15:30:12 +02:00
|
|
|
ParticleEmitterContext emitter_context{
|
2020-07-24 13:37:55 +02:00
|
|
|
solve_context, particle_allocators, solve_context.solve_interval};
|
2020-07-20 15:30:12 +02:00
|
|
|
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()};
|
2020-07-24 13:37:55 +02:00
|
|
|
MutableAttributesRef attributes = custom_data_attributes;
|
2020-07-20 15:30:12 +02:00
|
|
|
int new_particle_amount = attributes.get<int>("Dead").count(0);
|
|
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
for (MutableAttributesRef emitted_attributes : allocator.get_allocations()) {
|
2020-07-20 15:30:12 +02:00
|
|
|
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()};
|
|
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
Vector<MutableAttributesRef> particle_sources;
|
2020-07-20 15:30:12 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2020-07-24 13:37:55 +02:00
|
|
|
const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type);
|
|
|
|
|
GMutableSpan new_buffer{
|
2020-07-20 15:30:12 +02:00
|
|
|
cpp_type,
|
|
|
|
|
MEM_mallocN_aligned(new_particle_amount * cpp_type.size(), cpp_type.alignment(), AT),
|
|
|
|
|
new_particle_amount};
|
|
|
|
|
|
|
|
|
|
int current = 0;
|
2020-07-24 13:37:55 +02:00
|
|
|
for (MutableAttributesRef attributes : particle_sources) {
|
2020-07-20 15:30:12 +02:00
|
|
|
Span<int> dead_states = attributes.get<int>("Dead");
|
2020-07-24 13:37:55 +02:00
|
|
|
GSpan source_buffer = attributes.get(name);
|
2020-07-20 15:30:12 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2020-07-23 18:07:31 +02:00
|
|
|
layer.data = new_buffer.data();
|
2020-07-20 15:30:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-17 20:51:52 +02:00
|
|
|
void initialize_simulation_states(Simulation &simulation,
|
|
|
|
|
Depsgraph &UNUSED(depsgraph),
|
2020-07-22 17:04:18 +02:00
|
|
|
const SimulationInfluences &UNUSED(influences),
|
|
|
|
|
const bke::PersistentDataHandleMap &UNUSED(handle_map))
|
2020-07-17 20:51:52 +02:00
|
|
|
{
|
2020-07-19 13:58:49 +02:00
|
|
|
simulation.current_simulation_time = 0.0f;
|
2020-07-17 20:51:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void solve_simulation_time_step(Simulation &simulation,
|
2020-07-19 13:58:49 +02:00
|
|
|
Depsgraph &depsgraph,
|
2020-07-17 20:51:52 +02:00
|
|
|
const SimulationInfluences &influences,
|
2020-07-22 17:04:18 +02:00
|
|
|
const bke::PersistentDataHandleMap &handle_map,
|
2020-07-25 14:51:15 +02:00
|
|
|
const DependencyAnimations &dependency_animations,
|
2020-07-17 20:51:52 +02:00
|
|
|
float time_step)
|
|
|
|
|
{
|
2020-07-22 14:16:08 +02:00
|
|
|
SimulationStateMap state_map;
|
|
|
|
|
LISTBASE_FOREACH (SimulationState *, state, &simulation.states) {
|
|
|
|
|
state_map.add(state);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-21 17:20:05 +02:00
|
|
|
SimulationSolveContext solve_context{simulation,
|
|
|
|
|
depsgraph,
|
|
|
|
|
influences,
|
|
|
|
|
TimeInterval(simulation.current_simulation_time, time_step),
|
2020-07-22 14:16:08 +02:00
|
|
|
state_map,
|
2020-07-25 14:51:15 +02:00
|
|
|
handle_map,
|
|
|
|
|
dependency_animations};
|
2020-07-19 13:58:49 +02:00
|
|
|
|
2020-07-22 14:16:08 +02:00
|
|
|
Span<ParticleSimulationState *> particle_simulation_states =
|
|
|
|
|
state_map.lookup<ParticleSimulationState>();
|
2020-07-20 15:30:12 +02:00
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
Map<std::string, std::unique_ptr<AttributesInfo>> attribute_infos;
|
2020-07-20 15:30:12 +02:00
|
|
|
Map<std::string, std::unique_ptr<ParticleAllocator>> particle_allocators_map;
|
|
|
|
|
for (ParticleSimulationState *state : particle_simulation_states) {
|
2020-07-24 13:37:55 +02:00
|
|
|
const AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as(
|
2020-07-19 22:06:35 +02:00
|
|
|
state->head.name);
|
2020-07-24 13:37:55 +02:00
|
|
|
auto info = std::make_unique<AttributesInfo>(builder);
|
2020-07-19 22:06:35 +02:00
|
|
|
|
|
|
|
|
ensure_attributes_exist(state, *info);
|
|
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
uint32_t hash_seed = DefaultHash<StringRef>{}(state->head.name);
|
2020-07-20 15:30:12 +02:00
|
|
|
particle_allocators_map.add_new(
|
2020-07-24 13:37:55 +02:00
|
|
|
state->head.name,
|
|
|
|
|
std::make_unique<ParticleAllocator>(*info, state->next_particle_id, hash_seed));
|
2020-07-19 13:58:49 +02:00
|
|
|
attribute_infos.add_new(state->head.name, std::move(info));
|
|
|
|
|
}
|
2020-07-17 20:51:52 +02:00
|
|
|
|
2020-07-20 15:30:12 +02:00
|
|
|
ParticleAllocators particle_allocators{particle_allocators_map};
|
2020-07-20 12:16:20 +02:00
|
|
|
|
2020-07-20 15:30:12 +02:00
|
|
|
for (ParticleSimulationState *state : particle_simulation_states) {
|
2020-07-24 13:37:55 +02:00
|
|
|
const AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name);
|
2020-07-20 15:30:12 +02:00
|
|
|
simulate_existing_particles(solve_context, *state, attributes_info);
|
2020-07-17 20:51:52 +02:00
|
|
|
}
|
2020-07-19 13:58:49 +02:00
|
|
|
|
2020-07-20 15:30:12 +02:00
|
|
|
run_emitters(solve_context, particle_allocators);
|
2020-07-19 13:58:49 +02:00
|
|
|
|
2020-07-20 15:30:12 +02:00
|
|
|
for (ParticleSimulationState *state : particle_simulation_states) {
|
|
|
|
|
ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name);
|
2020-07-23 23:03:19 +02:00
|
|
|
|
2020-07-24 13:37:55 +02:00
|
|
|
for (MutableAttributesRef attributes : allocator.get_allocations()) {
|
|
|
|
|
Span<const ParticleAction *> actions = influences.particle_birth_actions.lookup_as(
|
|
|
|
|
state->head.name);
|
|
|
|
|
for (const ParticleAction *action : actions) {
|
2020-07-27 16:26:32 +02:00
|
|
|
ParticleChunkContext chunk_context{IndexRange(attributes.size()), attributes};
|
2020-07-24 13:37:55 +02:00
|
|
|
ParticleActionContext action_context{solve_context, chunk_context};
|
|
|
|
|
action->execute(action_context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (ParticleSimulationState *state : particle_simulation_states) {
|
|
|
|
|
ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name);
|
|
|
|
|
|
|
|
|
|
for (MutableAttributesRef attributes : allocator.get_allocations()) {
|
2020-07-23 23:03:19 +02:00
|
|
|
Array<float> remaining_durations(attributes.size());
|
|
|
|
|
Span<float> birth_times = attributes.get<float>("Birth Time");
|
2020-07-25 14:51:15 +02:00
|
|
|
const float end_time = solve_context.solve_interval.stop();
|
2020-07-23 23:03:19 +02:00
|
|
|
for (int i : attributes.index_range()) {
|
|
|
|
|
remaining_durations[i] = end_time - birth_times[i];
|
|
|
|
|
}
|
|
|
|
|
simulate_particle_chunk(solve_context, *state, attributes, remaining_durations, end_time);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 15:30:12 +02:00
|
|
|
remove_dead_and_add_new_particles(*state, allocator);
|
2020-07-19 13:58:49 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-25 14:51:15 +02:00
|
|
|
simulation.current_simulation_time = solve_context.solve_interval.stop();
|
2020-07-17 20:51:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace blender::sim
|