From 74fcb4d4c2f3c72747119a672c7e322f6f910478 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Jul 2020 13:37:55 +0200 Subject: Particles: initial particle birth action A particle action is some function that is triggered by some event. Right now, users cannot control this. There is just a randomize-velocity on-birth action. So the direction of spawned particles is slightly randomized now. This also adds a new integer attribute called "Hash" which is useful for a number of things. Mainly for generating random numbers for a specific particle. The ID of a particle is not necessarily a good source of randomness. --- .../simulation/intern/particle_allocator.cc | 9 ++ .../simulation/intern/particle_allocator.hh | 5 +- .../blender/simulation/intern/particle_function.cc | 11 +- .../simulation/intern/particle_mesh_emitter.cc | 6 +- .../intern/simulation_collect_influences.cc | 39 ++++- .../blender/simulation/intern/simulation_solver.cc | 98 +++++++----- .../intern/simulation_solver_influences.hh | 170 ++++++--------------- 7 files changed, 160 insertions(+), 178 deletions(-) (limited to 'source/blender') diff --git a/source/blender/simulation/intern/particle_allocator.cc b/source/blender/simulation/intern/particle_allocator.cc index 5ff5909f506..e47a6354d81 100644 --- a/source/blender/simulation/intern/particle_allocator.cc +++ b/source/blender/simulation/intern/particle_allocator.cc @@ -16,6 +16,8 @@ #include "particle_allocator.hh" +#include "BLI_rand.hh" + namespace blender::sim { AttributesAllocator::~AttributesAllocator() @@ -67,6 +69,13 @@ fn::MutableAttributesRef ParticleAllocator::allocate(int size) ids[pindex] = start_id + pindex; } } + else if (name == "Hash") { + MutableSpan hashes = attributes.get("Hash"); + RandomNumberGenerator rng(hash_seed_ ^ (uint32_t)next_id_); + for (int pindex : IndexRange(size)) { + hashes[pindex] = (int)rng.get_uint32(); + } + } else { type.fill_uninitialized(info.default_of(i), attributes.get(i).data(), size); } diff --git a/source/blender/simulation/intern/particle_allocator.hh b/source/blender/simulation/intern/particle_allocator.hh index 1e7578a75ed..1c412508fe6 100644 --- a/source/blender/simulation/intern/particle_allocator.hh +++ b/source/blender/simulation/intern/particle_allocator.hh @@ -70,10 +70,11 @@ class ParticleAllocator : NonCopyable, NonMovable { private: AttributesAllocator attributes_allocator_; std::atomic next_id_; + uint32_t hash_seed_; public: - ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id) - : attributes_allocator_(attributes_info), next_id_(next_id) + ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id, uint32_t hash_seed) + : attributes_allocator_(attributes_info), next_id_(next_id), hash_seed_(hash_seed) { } diff --git a/source/blender/simulation/intern/particle_function.cc b/source/blender/simulation/intern/particle_function.cc index 935ef7983d9..1b259938e34 100644 --- a/source/blender/simulation/intern/particle_function.cc +++ b/source/blender/simulation/intern/particle_function.cc @@ -56,12 +56,11 @@ ParticleFunctionEvaluator::ParticleFunctionEvaluator( : particle_fn_(particle_fn), solve_context_(solve_context), particle_chunk_context_(particle_chunk_context), - mask_(particle_chunk_context_.index_mask()), + mask_(particle_chunk_context_.index_mask), outputs_(particle_fn_.output_types_.size(), nullptr) { - global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map()); - per_particle_context_.add_global_context("PersistentDataHandleMap", - &solve_context_.handle_map()); + global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map); + per_particle_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map); } ParticleFunctionEvaluator::~ParticleFunctionEvaluator() @@ -117,7 +116,7 @@ void ParticleFunctionEvaluator::compute_globals() /* Add input parameters. */ for (const ParticleFunctionInput *input : particle_fn_.global_inputs_) { - input->add_input(particle_chunk_context_.attributes(), params, resources_); + input->add_input(particle_chunk_context_.attributes, params, resources_); } /* Add output parameters. */ @@ -144,7 +143,7 @@ void ParticleFunctionEvaluator::compute_per_particle() /* Add input parameters. */ for (const ParticleFunctionInput *input : particle_fn_.per_particle_inputs_) { - input->add_input(particle_chunk_context_.attributes(), params, resources_); + input->add_input(particle_chunk_context_.attributes, params, resources_); } /* Add output parameters. */ diff --git a/source/blender/simulation/intern/particle_mesh_emitter.cc b/source/blender/simulation/intern/particle_mesh_emitter.cc index 15a6bffa884..81640695f92 100644 --- a/source/blender/simulation/intern/particle_mesh_emitter.cc +++ b/source/blender/simulation/intern/particle_mesh_emitter.cc @@ -291,7 +291,7 @@ static BLI_NOINLINE EmitterSettings compute_settings(const fn::MultiFunction &in EmitterSettings parameters; fn::MFContextBuilder mf_context; - mf_context.add_global_context("PersistentDataHandleMap", &context.solve_context().handle_map()); + mf_context.add_global_context("PersistentDataHandleMap", &context.solve_context.handle_map); fn::MFParamsBuilder mf_params{inputs_fn, 1}; bke::PersistentObjectHandle object_handle; @@ -300,7 +300,7 @@ static BLI_NOINLINE EmitterSettings compute_settings(const fn::MultiFunction &in inputs_fn.call(IndexRange(1), mf_params, mf_context); - parameters.object = context.solve_context().handle_map().lookup(object_handle); + parameters.object = context.solve_context.handle_map.lookup(object_handle); return parameters; } @@ -318,7 +318,7 @@ void ParticleMeshEmitter::emit(ParticleEmitterContext &context) const Vector new_birth_times; if (!compute_new_particle_attributes(settings, - context.emit_interval(), + context.emit_interval, *state, new_positions, new_velocities, diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc index 7554f61404a..0fcaf78fe5b 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.cc +++ b/source/blender/simulation/intern/simulation_collect_influences.cc @@ -26,6 +26,7 @@ #include "DEG_depsgraph_query.h" +#include "BLI_hash.h" #include "BLI_rand.hh" namespace blender::sim { @@ -222,11 +223,11 @@ class ParticleFunctionForce : public ParticleForce { void add_force(ParticleForceContext &context) const override { - IndexMask mask = context.particle_chunk().index_mask(); - MutableSpan r_combined_force = context.force_dst(); + IndexMask mask = context.particle_chunk_context.index_mask; + MutableSpan r_combined_force = context.force_dst; ParticleFunctionEvaluator evaluator{ - particle_fn_, context.solve_context(), context.particle_chunk()}; + particle_fn_, context.solve_context, context.particle_chunk_context}; evaluator.compute(); fn::VSpan forces = evaluator.get(0, "Force"); @@ -341,6 +342,35 @@ static void collect_emitters(nodes::MFNetworkTreeMap &network_map, } } +class RandomizeVelocityAction : public ParticleAction { + public: + void execute(ParticleActionContext &context) const override + { + MutableSpan hashes = context.particle_chunk_context.attributes.get("Hash"); + MutableSpan velocities = context.particle_chunk_context.attributes.get( + "Velocity"); + for (int i : context.particle_chunk_context.index_mask) { + const float x = BLI_hash_int_01((uint32_t)hashes[i] ^ 23423523u) - 0.5f; + const float y = BLI_hash_int_01((uint32_t)hashes[i] ^ 76463521u) - 0.5f; + const float z = BLI_hash_int_01((uint32_t)hashes[i] ^ 43523762u) - 0.5f; + float3 vector{x, y, z}; + vector.normalize(); + velocities[i] += vector * 0.3; + } + } +}; + +static void collect_birth_events(nodes::MFNetworkTreeMap &network_map, + ResourceCollector &resources, + SimulationInfluences &r_influences) +{ + RandomizeVelocityAction &action = resources.construct(AT); + for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { + std::string particle_name = dnode_to_path(*dnode); + r_influences.particle_birth_actions.add_as(std::move(particle_name), &action); + } +} + static void prepare_particle_attribute_builders(nodes::MFNetworkTreeMap &network_map, ResourceCollector &resources, SimulationInfluences &r_influences) @@ -353,6 +383,8 @@ static void prepare_particle_attribute_builders(nodes::MFNetworkTreeMap &network builder.add("ID", 0); /* TODO: Use bool property, but need to add CD_PROP_BOOL first. */ builder.add("Dead", 0); + /* TODO: Use uint32_t, but we don't have a corresponding custom property type. */ + builder.add("Hash", 0); builder.add("Birth Time", 0.0f); r_influences.particle_attributes_builder.add_new(std::move(name), &builder); } @@ -381,6 +413,7 @@ void collect_simulation_influences(Simulation &simulation, collect_forces(network_map, resources, data_sources, r_influences); collect_emitters(network_map, resources, r_influences, r_required_states); + collect_birth_events(network_map, resources, r_influences); for (const nodes::DNode *dnode : get_particle_simulation_nodes(tree)) { r_required_states.add(dnode_to_path(*dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION); diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc index 5ab706dbb4b..a035a40f72f 100644 --- a/source/blender/simulation/intern/simulation_solver.cc +++ b/source/blender/simulation/intern/simulation_solver.cc @@ -34,7 +34,11 @@ ParticleEmitter::~ParticleEmitter() { } -static CustomDataType cpp_to_custom_data_type(const fn::CPPType &type) +ParticleAction::~ParticleAction() +{ +} + +static CustomDataType cpp_to_custom_data_type(const CPPType &type) { if (type.is()) { return CD_PROP_FLOAT3; @@ -49,18 +53,18 @@ static CustomDataType cpp_to_custom_data_type(const fn::CPPType &type) return CD_PROP_FLOAT; } -static const fn::CPPType &custom_to_cpp_data_type(CustomDataType type) +static const CPPType &custom_to_cpp_data_type(CustomDataType type) { switch (type) { case CD_PROP_FLOAT3: - return fn::CPPType::get(); + return CPPType::get(); case CD_PROP_FLOAT: - return fn::CPPType::get(); + return CPPType::get(); case CD_PROP_INT32: - return fn::CPPType::get(); + return CPPType::get(); default: BLI_assert(false); - return fn::CPPType::get(); + return CPPType::get(); } } @@ -68,33 +72,33 @@ class CustomDataAttributesRef { private: Array buffers_; int64_t size_; - const fn::AttributesInfo &info_; + const AttributesInfo &info_; public: - CustomDataAttributesRef(CustomData &custom_data, int64_t size, const fn::AttributesInfo &info) + CustomDataAttributesRef(CustomData &custom_data, int64_t size, const AttributesInfo &info) : buffers_(info.size(), nullptr), size_(size), info_(info) { for (int attribute_index : info.index_range()) { StringRefNull name = info.name_of(attribute_index); - const fn::CPPType &cpp_type = info.type_of(attribute_index); + const CPPType &cpp_type = info.type_of(attribute_index); 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; } } - operator fn::MutableAttributesRef() + operator MutableAttributesRef() { - return fn::MutableAttributesRef(info_, buffers_, size_); + return MutableAttributesRef(info_, buffers_, size_); } - operator fn::AttributesRef() const + operator AttributesRef() const { - return fn::AttributesRef(info_, buffers_, size_); + return AttributesRef(info_, buffers_, size_); } }; -static void ensure_attributes_exist(ParticleSimulationState *state, const fn::AttributesInfo &info) +static void ensure_attributes_exist(ParticleSimulationState *state, const AttributesInfo &info) { bool found_layer_to_remove; do { @@ -102,7 +106,7 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At for (int layer_index = 0; layer_index < state->attributes.totlayer; layer_index++) { CustomDataLayer *layer = &state->attributes.layers[layer_index]; BLI_assert(layer->name != nullptr); - const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type); + const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type); StringRefNull name = layer->name; if (!info.has_attribute(name, cpp_type)) { found_layer_to_remove = true; @@ -114,7 +118,7 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At for (int attribute_index : info.index_range()) { StringRefNull attribute_name = info.name_of(attribute_index); - const fn::CPPType &cpp_type = info.type_of(attribute_index); + const CPPType &cpp_type = info.type_of(attribute_index); CustomDataType custom_type = cpp_to_custom_data_type(cpp_type); if (CustomData_get_layer_named(&state->attributes, custom_type, attribute_name.c_str()) == nullptr) { @@ -131,13 +135,13 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_context, ParticleSimulationState &state, - fn::MutableAttributesRef attributes, + MutableAttributesRef attributes, MutableSpan remaining_durations, float end_time) { int particle_amount = attributes.size(); Array force_vectors{particle_amount, {0, 0, 0}}; - Span forces = solve_context.influences().particle_forces.lookup_as( + Span forces = solve_context.influences.particle_forces.lookup_as( state.head.name); ParticleChunkContext particle_chunk_context{IndexMask(particle_amount), attributes}; @@ -166,23 +170,23 @@ BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_c BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context, ParticleSimulationState &state, - const fn::AttributesInfo &attributes_info) + const AttributesInfo &attributes_info) { CustomDataAttributesRef custom_data_attributes{ state.attributes, state.tot_particles, attributes_info}; - fn::MutableAttributesRef attributes = custom_data_attributes; + MutableAttributesRef attributes = custom_data_attributes; - Array remaining_durations(state.tot_particles, solve_context.solve_interval().duration()); + Array remaining_durations(state.tot_particles, solve_context.solve_interval.duration()); simulate_particle_chunk( - solve_context, state, attributes, remaining_durations, solve_context.solve_interval().end()); + solve_context, state, attributes, remaining_durations, solve_context.solve_interval.end()); } BLI_NOINLINE static void run_emitters(SimulationSolveContext &solve_context, ParticleAllocators &particle_allocators) { - for (const ParticleEmitter *emitter : solve_context.influences().particle_emitters) { + for (const ParticleEmitter *emitter : solve_context.influences.particle_emitters) { ParticleEmitterContext emitter_context{ - solve_context, particle_allocators, solve_context.solve_interval()}; + solve_context, particle_allocators, solve_context.solve_interval}; emitter->emit(emitter_context); } } @@ -192,10 +196,10 @@ BLI_NOINLINE static int count_particles_after_time_step(ParticleSimulationState { CustomDataAttributesRef custom_data_attributes{ state.attributes, state.tot_particles, allocator.attributes_info()}; - fn::MutableAttributesRef attributes = custom_data_attributes; + MutableAttributesRef attributes = custom_data_attributes; int new_particle_amount = attributes.get("Dead").count(0); - for (fn::MutableAttributesRef emitted_attributes : allocator.get_allocations()) { + for (MutableAttributesRef emitted_attributes : allocator.get_allocations()) { new_particle_amount += emitted_attributes.get("Dead").count(0); } @@ -210,7 +214,7 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta CustomDataAttributesRef custom_data_attributes{ state.attributes, state.tot_particles, allocator.attributes_info()}; - Vector particle_sources; + Vector particle_sources; particle_sources.append(custom_data_attributes); particle_sources.extend(allocator.get_allocations()); @@ -222,16 +226,16 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta dead_layer = &layer; continue; } - const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type); - fn::GMutableSpan new_buffer{ + const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type); + 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) { + for (MutableAttributesRef attributes : particle_sources) { Span dead_states = attributes.get("Dead"); - fn::GSpan source_buffer = attributes.get(name); + GSpan source_buffer = attributes.get(name); BLI_assert(source_buffer.type() == cpp_type); for (int i : attributes.index_range()) { if (dead_states[i] == 0) { @@ -286,24 +290,26 @@ void solve_simulation_time_step(Simulation &simulation, Span particle_simulation_states = state_map.lookup(); - Map> attribute_infos; + Map> attribute_infos; Map> particle_allocators_map; for (ParticleSimulationState *state : particle_simulation_states) { - const fn::AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as( + const AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as( state->head.name); - auto info = std::make_unique(builder); + auto info = std::make_unique(builder); ensure_attributes_exist(state, *info); + uint32_t hash_seed = DefaultHash{}(state->head.name); particle_allocators_map.add_new( - state->head.name, std::make_unique(*info, state->next_particle_id)); + state->head.name, + std::make_unique(*info, state->next_particle_id, hash_seed)); attribute_infos.add_new(state->head.name, std::move(info)); } ParticleAllocators particle_allocators{particle_allocators_map}; for (ParticleSimulationState *state : particle_simulation_states) { - const fn::AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); + const AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); simulate_existing_particles(solve_context, *state, attributes_info); } @@ -312,10 +318,24 @@ void solve_simulation_time_step(Simulation &simulation, for (ParticleSimulationState *state : particle_simulation_states) { ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name); - for (fn::MutableAttributesRef attributes : allocator.get_allocations()) { + for (MutableAttributesRef attributes : allocator.get_allocations()) { + Span actions = influences.particle_birth_actions.lookup_as( + state->head.name); + for (const ParticleAction *action : actions) { + MutableParticleChunkContext chunk_context{IndexRange(attributes.size()), attributes}; + 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()) { Array remaining_durations(attributes.size()); Span birth_times = attributes.get("Birth Time"); - const float end_time = solve_context.solve_interval().end(); + const float end_time = solve_context.solve_interval.end(); for (int i : attributes.index_range()) { remaining_durations[i] = end_time - birth_times[i]; } @@ -325,7 +345,7 @@ void solve_simulation_time_step(Simulation &simulation, remove_dead_and_add_new_particles(*state, allocator); } - simulation.current_simulation_time = solve_context.solve_interval().end(); + simulation.current_simulation_time = solve_context.solve_interval.end(); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver_influences.hh b/source/blender/simulation/intern/simulation_solver_influences.hh index 95e6463e60d..2a173970edc 100644 --- a/source/blender/simulation/intern/simulation_solver_influences.hh +++ b/source/blender/simulation/intern/simulation_solver_influences.hh @@ -33,8 +33,17 @@ namespace blender::sim { +using fn::AttributesInfo; +using fn::AttributesInfoBuilder; +using fn::AttributesRef; +using fn::CPPType; +using fn::GMutableSpan; +using fn::GSpan; +using fn::MutableAttributesRef; + class ParticleEmitterContext; class ParticleForceContext; +class ParticleActionContext; class ParticleEmitter { public: @@ -48,9 +57,16 @@ class ParticleForce { virtual void add_force(ParticleForceContext &context) const = 0; }; +class ParticleAction { + public: + virtual ~ParticleAction(); + virtual void execute(ParticleActionContext &context) const = 0; +}; + struct SimulationInfluences { MultiValueMap particle_forces; - Map particle_attributes_builder; + MultiValueMap particle_birth_actions; + Map particle_attributes_builder; Vector particle_emitters; }; @@ -96,50 +112,13 @@ class SimulationStateMap { } }; -class SimulationSolveContext { - private: - Simulation &simulation_; - Depsgraph &depsgraph_; - const SimulationInfluences &influences_; - TimeInterval solve_interval_; - const SimulationStateMap &state_map_; - const bke::PersistentDataHandleMap &id_handle_map_; - - public: - SimulationSolveContext(Simulation &simulation, - Depsgraph &depsgraph, - const SimulationInfluences &influences, - TimeInterval solve_interval, - const SimulationStateMap &state_map, - const bke::PersistentDataHandleMap &handle_map) - : simulation_(simulation), - depsgraph_(depsgraph), - influences_(influences), - solve_interval_(solve_interval), - state_map_(state_map), - id_handle_map_(handle_map) - { - } - - TimeInterval solve_interval() const - { - return solve_interval_; - } - - const SimulationInfluences &influences() const - { - return influences_; - } - - const bke::PersistentDataHandleMap &handle_map() const - { - return id_handle_map_; - } - - const SimulationStateMap &state_map() const - { - return state_map_; - } +struct SimulationSolveContext { + Simulation &simulation; + Depsgraph &depsgraph; + const SimulationInfluences &influences; + TimeInterval solve_interval; + const SimulationStateMap &state_map; + const bke::PersistentDataHandleMap &handle_map; }; class ParticleAllocators { @@ -164,100 +143,41 @@ class ParticleAllocators { } }; -class ParticleChunkContext { - private: - IndexMask index_mask_; - fn::MutableAttributesRef attributes_; - - public: - ParticleChunkContext(IndexMask index_mask, fn::MutableAttributesRef attributes) - : index_mask_(index_mask), attributes_(attributes) - { - } - - IndexMask index_mask() const - { - return index_mask_; - } - - fn::MutableAttributesRef attributes() - { - return attributes_; - } +struct MutableParticleChunkContext { + IndexMask index_mask; + MutableAttributesRef attributes; +}; - fn::AttributesRef attributes() const - { - return attributes_; - } +struct ParticleChunkContext { + IndexMask index_mask; + AttributesRef attributes; }; -class ParticleEmitterContext { - private: - SimulationSolveContext &solve_context_; - ParticleAllocators &particle_allocators_; - TimeInterval emit_interval_; - - public: - ParticleEmitterContext(SimulationSolveContext &solve_context, - ParticleAllocators &particle_allocators, - TimeInterval emit_interval) - : solve_context_(solve_context), - particle_allocators_(particle_allocators), - emit_interval_(emit_interval) - { - } +struct ParticleEmitterContext { + SimulationSolveContext &solve_context; + ParticleAllocators &particle_allocators; + TimeInterval emit_interval; template StateType *lookup_state(StringRef name) { - return solve_context_.state_map().lookup(name); - } - - SimulationSolveContext &solve_context() - { - return solve_context_; + return solve_context.state_map.lookup(name); } ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name) { - return particle_allocators_.try_get_allocator(particle_simulation_name); - } - - TimeInterval emit_interval() const - { - return emit_interval_; + return particle_allocators.try_get_allocator(particle_simulation_name); } }; -class ParticleForceContext { - private: - SimulationSolveContext &solve_context_; - const ParticleChunkContext &particle_chunk_context_; - MutableSpan force_dst_; - - public: - ParticleForceContext(SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context, - MutableSpan force_dst) - : solve_context_(solve_context), - particle_chunk_context_(particle_chunk_context), - force_dst_(force_dst) - { - } - - SimulationSolveContext &solve_context() - { - return solve_context_; - } - - const ParticleChunkContext &particle_chunk() const - { - return particle_chunk_context_; - } +struct ParticleForceContext { + SimulationSolveContext &solve_context; + ParticleChunkContext &particle_chunk_context; + MutableSpan force_dst; +}; - MutableSpan force_dst() - { - return force_dst_; - } +struct ParticleActionContext { + SimulationSolveContext &solve_context; + MutableParticleChunkContext &particle_chunk_context; }; } // namespace blender::sim -- cgit v1.2.3