Files
test/source/blender/simulation/intern/simulation_solver_influences.hh
Jacques Lucke 7cd2c1fd2e Particles: support custom particle events in solver
Previously, there were only particle-birth and time-step events.
Now the solver can handle custom events. On the user level
this does not change anything yet. This feature of the solver
will be used by an upcoming Age Reached Event node and
possibly others. When this node exists, I can finally remove
the hardcoded maximum particle age.
2020-08-01 21:56:05 +02:00

238 lines
6.7 KiB
C++

/*
* 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.
*/
#ifndef __SIM_SIMULATION_SOLVER_INFLUENCES_HH__
#define __SIM_SIMULATION_SOLVER_INFLUENCES_HH__
#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<std::string, const ParticleForce *> particle_forces;
MultiValueMap<std::string, const ParticleAction *> particle_birth_actions;
MultiValueMap<std::string, const ParticleAction *> particle_time_step_begin_actions;
MultiValueMap<std::string, const ParticleAction *> particle_time_step_end_actions;
MultiValueMap<std::string, const ParticleEvent *> particle_events;
Map<std::string, AttributesInfoBuilder *> particle_attributes_builder;
Vector<const ParticleEmitter *> particle_emitters;
};
class SimulationStateMap {
private:
Map<StringRefNull, SimulationState *> states_by_name_;
MultiValueMap<StringRefNull, SimulationState *> states_by_type_;
public:
void add(SimulationState *state)
{
states_by_name_.add_new(state->name, state);
states_by_type_.add(state->type, state);
}
template<typename StateType> StateType *lookup(StringRef name) const
{
const char *type = BKE_simulation_get_state_type_name<StateType>();
return (StateType *)this->lookup_name_type(name, type);
}
template<typename StateType> Span<StateType *> lookup() const
{
const char *type = BKE_simulation_get_state_type_name<StateType>();
return this->lookup_type(type).cast<StateType *>();
}
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<SimulationState *> 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<float> simulation_times,
MutableSpan<float4x4> 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<std::string, std::unique_ptr<ParticleAllocator>> &allocators_;
public:
ParticleAllocators(Map<std::string, std::unique_ptr<ParticleAllocator>> &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<float3> position_diffs;
MutableSpan<float3> velocity_diffs;
MutableSpan<float> 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<float> remaining_durations = integration->durations;
MutableSpan<float3> position_diffs = integration->position_diffs;
Span<float3> velocities = attributes.get<float3>("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<typename StateType> StateType *lookup_state(StringRef name)
{
return solve_context.state_map.lookup<StateType>(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<float3> force_dst;
};
struct ParticleActionContext {
SimulationSolveContext &solve_context;
ParticleChunkContext &particles;
};
struct ParticleEventFilterContext {
SimulationSolveContext &solve_context;
ParticleChunkContext &particles;
MutableSpan<float> factor_dst;
};
} // namespace blender::sim
#endif /* __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ */