/* * 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. */ #pragma once #include "BLI_float3.hh" #include "BLI_float4x4.hh" #include "BLI_multi_value_map.hh" #include "BLI_span.hh" #include "DNA_simulation_types.h" #include "FN_attributes_ref.hh" #include "BKE_persistent_data_handle.hh" #include "BKE_simulation.h" #include "particle_allocator.hh" #include "time_interval.hh" namespace blender::sim { using fn::AttributesInfo; using fn::AttributesInfoBuilder; using fn::AttributesRef; using fn::CPPType; using fn::GMutableSpan; using fn::GSpan; using fn::MutableAttributesRef; struct ParticleEmitterContext; struct ParticleForceContext; struct ParticleActionContext; struct ParticleEventFilterContext; class ParticleEmitter { public: virtual ~ParticleEmitter(); virtual void emit(ParticleEmitterContext &context) const = 0; }; class ParticleForce { public: virtual ~ParticleForce(); virtual void add_force(ParticleForceContext &context) const = 0; }; class ParticleAction { public: virtual ~ParticleAction(); virtual void execute(ParticleActionContext &context) const = 0; }; class ParticleEvent { public: virtual ~ParticleEvent(); virtual void filter(ParticleEventFilterContext &context) const = 0; virtual void execute(ParticleActionContext &context) const = 0; }; struct SimulationInfluences { MultiValueMap particle_forces; MultiValueMap particle_birth_actions; MultiValueMap particle_time_step_begin_actions; MultiValueMap particle_time_step_end_actions; MultiValueMap particle_events; Map particle_attributes_builder; Vector particle_emitters; }; class SimulationStateMap { private: Map states_by_name_; MultiValueMap states_by_type_; public: void add(SimulationState *state) { states_by_name_.add_new(state->name, state); states_by_type_.add(state->type, state); } template StateType *lookup(StringRef name) const { const char *type = BKE_simulation_get_state_type_name(); return (StateType *)this->lookup_name_type(name, type); } template Span lookup() const { const char *type = BKE_simulation_get_state_type_name(); return this->lookup_type(type).cast(); } SimulationState *lookup_name_type(StringRef name, StringRef type) const { SimulationState *state = states_by_name_.lookup_default_as(name, nullptr); if (state == nullptr) { return nullptr; } if (state->type == type) { return state; } return nullptr; } Span lookup_type(StringRef type) const { return states_by_type_.lookup_as(type); } }; class DependencyAnimations { public: ~DependencyAnimations(); virtual bool is_object_transform_changing(Object &object) const; virtual void get_object_transforms(Object &object, Span simulation_times, MutableSpan r_transforms) const; }; struct SimulationSolveContext { Simulation &simulation; Depsgraph &depsgraph; const SimulationInfluences &influences; TimeInterval solve_interval; const SimulationStateMap &state_map; const bke::PersistentDataHandleMap &handle_map; const DependencyAnimations &dependency_animations; }; class ParticleAllocators { private: Map> &allocators_; public: ParticleAllocators(Map> &allocators) : allocators_(allocators) { } ParticleAllocator *try_get_allocator(StringRef particle_simulation_name) { auto *ptr = allocators_.lookup_ptr_as(particle_simulation_name); if (ptr != nullptr) { return ptr->get(); } else { return nullptr; } } }; struct ParticleChunkIntegrationContext { MutableSpan position_diffs; MutableSpan velocity_diffs; MutableSpan durations; float end_time; }; struct ParticleChunkContext { ParticleSimulationState &state; IndexMask index_mask; MutableAttributesRef attributes; ParticleChunkIntegrationContext *integration = nullptr; void update_diffs_after_velocity_change() { if (integration == nullptr) { return; } Span remaining_durations = integration->durations; MutableSpan position_diffs = integration->position_diffs; Span velocities = attributes.get("Velocity"); for (int i : index_mask) { const float duration = remaining_durations[i]; /* This is certainly not a perfect way to "re-integrate" the velocity, but it should be good * enough for most use cases. Changing the velocity in an instant is not physically correct * anyway. */ position_diffs[i] = velocities[i] * duration; } } }; struct ParticleEmitterContext { SimulationSolveContext &solve_context; ParticleAllocators &particle_allocators; TimeInterval emit_interval; template StateType *lookup_state(StringRef name) { 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); } }; struct ParticleForceContext { SimulationSolveContext &solve_context; ParticleChunkContext &particles; MutableSpan force_dst; }; struct ParticleActionContext { SimulationSolveContext &solve_context; ParticleChunkContext &particles; }; struct ParticleEventFilterContext { SimulationSolveContext &solve_context; ParticleChunkContext &particles; MutableSpan factor_dst; }; } // namespace blender::sim