diff options
Diffstat (limited to 'source/blender/simulation')
8 files changed, 284 insertions, 46 deletions
diff --git a/source/blender/simulation/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt index 6466a6e67d4..cbc6ee65303 100644 --- a/source/blender/simulation/CMakeLists.txt +++ b/source/blender/simulation/CMakeLists.txt @@ -45,6 +45,7 @@ set(SRC intern/particle_function.cc intern/particle_mesh_emitter.cc intern/simulation_collect_influences.cc + intern/simulation_solver_influences.cc intern/simulation_solver.cc intern/simulation_update.cc diff --git a/source/blender/simulation/intern/particle_mesh_emitter.cc b/source/blender/simulation/intern/particle_mesh_emitter.cc index 81640695f92..c1482a29cb7 100644 --- a/source/blender/simulation/intern/particle_mesh_emitter.cc +++ b/source/blender/simulation/intern/particle_mesh_emitter.cc @@ -46,7 +46,7 @@ static BLI_NOINLINE void compute_birth_times(float rate, counter++; const float time_offset = counter * time_between_particles; const float birth_time = state.last_birth_time + time_offset; - if (birth_time > emit_interval.end()) { + if (birth_time > emit_interval.stop()) { break; } if (birth_time <= emit_interval.start()) { @@ -217,8 +217,8 @@ static BLI_NOINLINE void sample_looptris(RandomNumberGenerator &rng, } } -static BLI_NOINLINE bool compute_new_particle_attributes(EmitterSettings &settings, - TimeInterval emit_interval, +static BLI_NOINLINE bool compute_new_particle_attributes(ParticleEmitterContext &context, + EmitterSettings &settings, ParticleMeshEmitterSimulationState &state, Vector<float3> &r_positions, Vector<float3> &r_velocities, @@ -238,19 +238,17 @@ static BLI_NOINLINE bool compute_new_particle_attributes(EmitterSettings &settin return false; } - const float4x4 local_to_world = settings.object->obmat; - float4x4 local_to_world_normal = local_to_world.inverted_affine().transposed(); - - const float start_time = emit_interval.start(); + const float start_time = context.emit_interval.start(); const uint32_t seed = DefaultHash<StringRef>{}(state.head.name); RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed}; - compute_birth_times(settings.rate, emit_interval, state, r_birth_times); + compute_birth_times(settings.rate, context.emit_interval, state, r_birth_times); const int particle_amount = r_birth_times.size(); if (particle_amount == 0) { return false; } + const float last_birth_time = r_birth_times.last(); rng.shuffle(r_birth_times.as_mutable_span()); Span<MLoopTri> triangles = get_mesh_triangles(mesh); @@ -266,22 +264,36 @@ static BLI_NOINLINE bool compute_new_particle_attributes(EmitterSettings &settin return false; } - Array<float3> local_positions(particle_amount); - Array<float3> local_normals(particle_amount); - sample_looptris(rng, mesh, triangles, triangles_to_sample, local_positions, local_normals); + r_positions.resize(particle_amount); + r_velocities.resize(particle_amount); + sample_looptris(rng, mesh, triangles, triangles_to_sample, r_positions, r_velocities); - r_positions.reserve(particle_amount); - r_velocities.reserve(particle_amount); - for (int i : IndexRange(particle_amount)) { - float3 position = local_to_world * local_positions[i]; - float3 normal = local_to_world_normal.ref_3x3() * local_normals[i]; - normal.normalize(); + if (context.solve_context.dependency_animations.is_object_transform_changing(*settings.object)) { + Array<float4x4> local_to_world_matrices(particle_amount); + context.solve_context.dependency_animations.get_object_transforms( + *settings.object, r_birth_times, local_to_world_matrices); - r_positions.append(position); - r_velocities.append(normal); + for (int i : IndexRange(particle_amount)) { + const float4x4 &position_to_world = local_to_world_matrices[i]; + const float4x4 normal_to_world = position_to_world.inverted_transposed_affine(); + r_positions[i] = position_to_world * r_positions[i]; + r_velocities[i] = normal_to_world * r_velocities[i]; + } + } + else { + const float4x4 position_to_world = settings.object->obmat; + const float4x4 normal_to_world = position_to_world.inverted_transposed_affine(); + for (int i : IndexRange(particle_amount)) { + r_positions[i] = position_to_world * r_positions[i]; + r_velocities[i] = normal_to_world * r_velocities[i]; + } + } + + for (int i : IndexRange(particle_amount)) { + r_velocities[i].normalize(); } - state.last_birth_time = r_birth_times.last(); + state.last_birth_time = last_birth_time; return true; } @@ -317,12 +329,8 @@ void ParticleMeshEmitter::emit(ParticleEmitterContext &context) const Vector<float3> new_velocities; Vector<float> new_birth_times; - if (!compute_new_particle_attributes(settings, - context.emit_interval, - *state, - new_positions, - new_velocities, - new_birth_times)) { + if (!compute_new_particle_attributes( + context, settings, *state, new_positions, new_velocities, new_birth_times)) { return; } diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc index a035a40f72f..f650528edb6 100644 --- a/source/blender/simulation/intern/simulation_solver.cc +++ b/source/blender/simulation/intern/simulation_solver.cc @@ -26,18 +26,6 @@ namespace blender::sim { -ParticleForce::~ParticleForce() -{ -} - -ParticleEmitter::~ParticleEmitter() -{ -} - -ParticleAction::~ParticleAction() -{ -} - static CustomDataType cpp_to_custom_data_type(const CPPType &type) { if (type.is<float3>()) { @@ -178,7 +166,7 @@ BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &sol Array<float> 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.stop()); } BLI_NOINLINE static void run_emitters(SimulationSolveContext &solve_context, @@ -273,6 +261,7 @@ void solve_simulation_time_step(Simulation &simulation, Depsgraph &depsgraph, const SimulationInfluences &influences, const bke::PersistentDataHandleMap &handle_map, + const DependencyAnimations &dependency_animations, float time_step) { SimulationStateMap state_map; @@ -285,7 +274,8 @@ void solve_simulation_time_step(Simulation &simulation, influences, TimeInterval(simulation.current_simulation_time, time_step), state_map, - handle_map}; + handle_map, + dependency_animations}; Span<ParticleSimulationState *> particle_simulation_states = state_map.lookup<ParticleSimulationState>(); @@ -335,7 +325,7 @@ void solve_simulation_time_step(Simulation &simulation, for (MutableAttributesRef attributes : allocator.get_allocations()) { Array<float> remaining_durations(attributes.size()); Span<float> birth_times = attributes.get<float>("Birth Time"); - const float end_time = solve_context.solve_interval.end(); + const float end_time = solve_context.solve_interval.stop(); for (int i : attributes.index_range()) { remaining_durations[i] = end_time - birth_times[i]; } @@ -345,7 +335,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.stop(); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver.hh b/source/blender/simulation/intern/simulation_solver.hh index ccf4855a9a1..9d30ea87731 100644 --- a/source/blender/simulation/intern/simulation_solver.hh +++ b/source/blender/simulation/intern/simulation_solver.hh @@ -32,6 +32,7 @@ void solve_simulation_time_step(Simulation &simulation, Depsgraph &depsgraph, const SimulationInfluences &influences, const bke::PersistentDataHandleMap &handle_map, + const DependencyAnimations &dependency_animations, float time_step); } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver_influences.cc b/source/blender/simulation/intern/simulation_solver_influences.cc new file mode 100644 index 00000000000..75c2c820651 --- /dev/null +++ b/source/blender/simulation/intern/simulation_solver_influences.cc @@ -0,0 +1,53 @@ +/* + * 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_influences.hh" + +#include "DNA_object_types.h" + +namespace blender::sim { + +ParticleForce::~ParticleForce() +{ +} + +ParticleEmitter::~ParticleEmitter() +{ +} + +ParticleAction::~ParticleAction() +{ +} + +DependencyAnimations::~DependencyAnimations() +{ +} + +bool DependencyAnimations::is_object_transform_changing(Object &UNUSED(object)) const +{ + return false; +} + +void DependencyAnimations::get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const +{ + assert_same_size(simulation_times, r_transforms); + float4x4 world_matrix = object.obmat; + r_transforms.fill(world_matrix); +} + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver_influences.hh b/source/blender/simulation/intern/simulation_solver_influences.hh index 2a173970edc..94a1f9a926f 100644 --- a/source/blender/simulation/intern/simulation_solver_influences.hh +++ b/source/blender/simulation/intern/simulation_solver_influences.hh @@ -18,6 +18,7 @@ #define __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ #include "BLI_float3.hh" +#include "BLI_float4x4.hh" #include "BLI_multi_value_map.hh" #include "BLI_span.hh" @@ -112,6 +113,16 @@ class SimulationStateMap { } }; +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; @@ -119,6 +130,7 @@ struct SimulationSolveContext { TimeInterval solve_interval; const SimulationStateMap &state_map; const bke::PersistentDataHandleMap &handle_map; + const DependencyAnimations &dependency_animations; }; class ParticleAllocators { diff --git a/source/blender/simulation/intern/simulation_update.cc b/source/blender/simulation/intern/simulation_update.cc index e0eb9fa4a0f..32b582977d0 100644 --- a/source/blender/simulation/intern/simulation_update.cc +++ b/source/blender/simulation/intern/simulation_update.cc @@ -18,8 +18,10 @@ #include "BKE_customdata.h" #include "BKE_lib_id.h" +#include "BKE_object.h" #include "BKE_simulation.h" +#include "DNA_modifier_types.h" #include "DNA_scene_types.h" #include "DNA_simulation_types.h" @@ -92,6 +94,131 @@ static void update_simulation_state_list(Simulation *simulation, add_missing_states(simulation, required_states); } +class SampledDependencyAnimations : public DependencyAnimations { + private: + TimeInterval simulation_time_interval_; + MultiValueMap<Object *, float4x4> object_transforms_cache_; + + public: + SampledDependencyAnimations(TimeInterval simulation_time_interval) + : simulation_time_interval_(simulation_time_interval) + { + } + + void add_object_transforms(Object &object, Span<float4x4> transforms) + { + object_transforms_cache_.add_multiple(&object, transforms); + } + + bool is_object_transform_changing(Object &object) const + { + return object_transforms_cache_.lookup(&object).size() >= 2; + } + + void get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const + { + assert_same_size(simulation_times, r_transforms); + Span<float4x4> cached_transforms = object_transforms_cache_.lookup(&object); + if (cached_transforms.size() == 0) { + r_transforms.fill(object.obmat); + return; + } + if (cached_transforms.size() == 1) { + r_transforms.fill(cached_transforms[0]); + return; + } + + for (int i : simulation_times.index_range()) { + const float simulation_time = simulation_times[i]; + if (simulation_time <= simulation_time_interval_.start()) { + r_transforms[i] = cached_transforms.first(); + continue; + } + if (simulation_time >= simulation_time_interval_.stop()) { + r_transforms[i] = cached_transforms.last(); + continue; + } + const float factor = simulation_time_interval_.factor_at_time(simulation_time); + BLI_assert(factor > 0.0f && factor < 1.0f); + const float scaled_factor = factor * (cached_transforms.size() - 1); + const int lower_sample = (int)scaled_factor; + const int upper_sample = lower_sample + 1; + const float mix_factor = scaled_factor - lower_sample; + r_transforms[i] = float4x4::interpolate( + cached_transforms[lower_sample], cached_transforms[upper_sample], mix_factor); + } + } +}; + +static void sample_object_transforms(Object &object, + Depsgraph &depsgraph, + Scene &scene, + TimeInterval scene_frame_interval, + MutableSpan<float4x4> r_transforms) +{ + if (r_transforms.size() == 0) { + return; + } + if (r_transforms.size() == 1) { + r_transforms[0] = object.obmat; + return; + } + + Array<float> frames(r_transforms.size()); + scene_frame_interval.compute_uniform_samples(frames); + + for (int i : frames.index_range()) { + float frame = frames[i]; + const int recursion_depth = 5; + BKE_object_modifier_update_subframe( + &depsgraph, &scene, &object, false, recursion_depth, frame, eModifierType_None); + r_transforms[i] = object.obmat; + } +} + +template<typename T> static bool all_values_equal(Span<T> values) +{ + if (values.size() == 0) { + return true; + } + for (const T &value : values.drop_front(1)) { + if (value != values[0]) { + return false; + } + } + return true; +} + +static void prepare_dependency_animations(Depsgraph &depsgraph, + Scene &scene, + Simulation &simulation, + TimeInterval scene_frame_interval, + SampledDependencyAnimations &r_dependency_animations) +{ + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation.dependencies) { + ID *id_cow = DEG_get_evaluated_id(&depsgraph, dependency->id); + if (id_cow == nullptr) { + continue; + } + if (GS(id_cow->name) != ID_OB) { + continue; + } + Object &object_cow = *(Object *)id_cow; + constexpr int sample_count = 10; + Array<float4x4, sample_count> transforms(sample_count); + sample_object_transforms(object_cow, depsgraph, scene, scene_frame_interval, transforms); + + /* If all samples are the same, only store one. */ + Span<float4x4> transforms_to_use = (all_values_equal(transforms.as_span())) ? + transforms.as_span().take_front(1) : + transforms.as_span(); + + r_dependency_animations.add_object_transforms(object_cow, transforms_to_use); + } +} + void update_simulation_in_depsgraph(Depsgraph *depsgraph, Scene *scene_cow, Simulation *simulation_cow) @@ -117,7 +244,9 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, bke::PersistentDataHandleMap handle_map; LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation_orig->dependencies) { ID *id_cow = DEG_get_evaluated_id(depsgraph, dependency->id); - handle_map.add(dependency->handle, *id_cow); + if (id_cow != nullptr) { + handle_map.add(dependency->handle, *id_cow); + } } if (current_frame == 1) { @@ -131,8 +260,16 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, else if (current_frame == simulation_orig->current_frame + 1) { update_simulation_state_list(simulation_orig, required_states); - float time_step = 1.0f / 24.0f; - solve_simulation_time_step(*simulation_orig, *depsgraph, influences, handle_map, time_step); + const float fps = scene_cow->r.frs_sec / scene_cow->r.frs_sec_base; + const float time_step = 1.0f / fps; + TimeInterval scene_frame_interval(current_frame - 1, 1); + TimeInterval simulation_time_interval(simulation_orig->current_simulation_time, time_step); + SampledDependencyAnimations dependency_animations{simulation_time_interval}; + prepare_dependency_animations( + *depsgraph, *scene_cow, *simulation_orig, scene_frame_interval, dependency_animations); + + solve_simulation_time_step( + *simulation_orig, *depsgraph, influences, handle_map, dependency_animations, time_step); simulation_orig->current_frame = current_frame; copy_states_to_cow(simulation_orig, simulation_cow); diff --git a/source/blender/simulation/intern/time_interval.hh b/source/blender/simulation/intern/time_interval.hh index 75ef4b21c33..5996dc5d25b 100644 --- a/source/blender/simulation/intern/time_interval.hh +++ b/source/blender/simulation/intern/time_interval.hh @@ -41,7 +41,7 @@ class TimeInterval { return start_; } - float end() const + float stop() const { return start_ + duration_; } @@ -50,6 +50,42 @@ class TimeInterval { { return duration_; } + + float time_at_factor(float factor) const + { + return start_ + factor * duration_; + } + + float factor_at_time(float time) const + { + BLI_assert(duration_ > 0.0f); + return (time - start_) / duration_; + } + + float safe_factor_at_time(float time) const + { + if (duration_ > 0.0f) { + return this->factor_at_time(time); + } + return 0.0f; + } + + void compute_uniform_samples(MutableSpan<float> r_samples) const + { + int64_t amount = r_samples.size(); + if (amount == 0) { + return; + } + if (amount == 1) { + r_samples[0] = this->time_at_factor(0.5f); + return; + } + + const float step = duration_ / (float)(amount - 1); + for (int64_t i : r_samples.index_range()) { + r_samples[i] = start_ + i * step; + } + } }; } // namespace blender::sim |