Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2020-08-02 23:04:24 +0300
committerJacques Lucke <jacques@blender.org>2020-08-02 23:10:47 +0300
commit396d0b5cd0bcd9dd3dfa8d9006ee9f6f91c7196d (patch)
tree863a92ba15bd2d949c4b0de07735f635ac08e177 /source/blender/simulation
parentd1063575b536ed9490c5002007b45704e7755385 (diff)
Particles: new Age Reached Event, Kill Particle and Random Float node
The hardcoded age limit is now gone. The behavior can be implemented with an Age Reached Event and Kill Particle node. Other utility nodes to handle age limits of particles can be added later. Adding an Age Limit attribute to particles on birth will be useful for some effects, e.g. when you want to control the color or size of a particle over its life time. The Random Float node takes a seed currently. Different nodes will produce different values even with the same seed. However, the same node will generate the same random number for the same seed every time. The "Hash" of a particle can be used as seed. Later, we'd want to have more modes in the node to make it more user friendly. Modes could be: Per Particle, Per Time, Per Particle Per Time, Per Node Instance, ... Also a Random Vector node will be useful, as it currently has to be build using three Random Float nodes.
Diffstat (limited to 'source/blender/simulation')
-rw-r--r--source/blender/simulation/intern/particle_mesh_emitter.cc10
-rw-r--r--source/blender/simulation/intern/particle_mesh_emitter.hh7
-rw-r--r--source/blender/simulation/intern/simulation_collect_influences.cc132
-rw-r--r--source/blender/simulation/intern/simulation_solver.cc3
4 files changed, 112 insertions, 40 deletions
diff --git a/source/blender/simulation/intern/particle_mesh_emitter.cc b/source/blender/simulation/intern/particle_mesh_emitter.cc
index c1482a29cb7..26541d550eb 100644
--- a/source/blender/simulation/intern/particle_mesh_emitter.cc
+++ b/source/blender/simulation/intern/particle_mesh_emitter.cc
@@ -346,6 +346,16 @@ void ParticleMeshEmitter::emit(ParticleEmitterContext &context) const
attributes.get<float3>("Position").copy_from(new_positions);
attributes.get<float3>("Velocity").copy_from(new_velocities);
attributes.get<float>("Birth Time").copy_from(new_birth_times);
+
+ if (action_ != nullptr) {
+ ParticleChunkContext particles{
+ *context.solve_context.state_map.lookup<ParticleSimulationState>(name),
+ IndexRange(amount),
+ attributes,
+ nullptr};
+ ParticleActionContext action_context{context.solve_context, particles};
+ action_->execute(action_context);
+ }
}
}
diff --git a/source/blender/simulation/intern/particle_mesh_emitter.hh b/source/blender/simulation/intern/particle_mesh_emitter.hh
index 601697c9986..724d79c1aec 100644
--- a/source/blender/simulation/intern/particle_mesh_emitter.hh
+++ b/source/blender/simulation/intern/particle_mesh_emitter.hh
@@ -28,14 +28,17 @@ class ParticleMeshEmitter final : public ParticleEmitter {
std::string own_state_name_;
Array<std::string> particle_names_;
const fn::MultiFunction &inputs_fn_;
+ const ParticleAction *action_;
public:
ParticleMeshEmitter(std::string own_state_name,
Array<std::string> particle_names,
- const fn::MultiFunction &inputs_fn)
+ const fn::MultiFunction &inputs_fn,
+ const ParticleAction *action)
: own_state_name_(std::move(own_state_name)),
particle_names_(particle_names),
- inputs_fn_(inputs_fn)
+ inputs_fn_(inputs_fn),
+ action_(action)
{
}
diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc
index c309d18c43a..818415e5d88 100644
--- a/source/blender/simulation/intern/simulation_collect_influences.cc
+++ b/source/blender/simulation/intern/simulation_collect_influences.cc
@@ -367,6 +367,17 @@ static const ParticleFunction *create_particle_function_for_inputs(
return &particle_fn;
}
+static const ParticleFunction *create_particle_function_for_inputs(
+ CollectContext &context, Span<const DInputSocket *> dsockets_to_compute)
+{
+ Vector<const MFInputSocket *> sockets_to_compute;
+ for (const DInputSocket *dsocket : dsockets_to_compute) {
+ const MFInputSocket &socket = context.network_map.lookup_dummy(*dsocket);
+ sockets_to_compute.append(&socket);
+ }
+ return create_particle_function_for_inputs(context, sockets_to_compute);
+}
+
class ParticleFunctionForce : public ParticleForce {
private:
const ParticleFunction &particle_fn_;
@@ -401,11 +412,8 @@ static void create_forces_for_particle_simulation(CollectContext &context,
continue;
}
- const MFInputSocket &force_socket = context.network_map.lookup_dummy(
- origin_node.input(0, "Force"));
-
- const ParticleFunction *particle_fn = create_particle_function_for_inputs(context,
- {&force_socket});
+ const ParticleFunction *particle_fn = create_particle_function_for_inputs(
+ context, {&origin_node.input(0, "Force")});
if (particle_fn == nullptr) {
continue;
@@ -434,7 +442,7 @@ static ParticleEmitter *create_particle_emitter(CollectContext &context, const D
return nullptr;
}
- Array<const MFInputSocket *> input_sockets{dnode.inputs().size()};
+ Array<const MFInputSocket *> input_sockets{2};
for (int i : input_sockets.index_range()) {
input_sockets[i] = &context.network_map.lookup_dummy(dnode.input(i));
}
@@ -446,10 +454,13 @@ static ParticleEmitter *create_particle_emitter(CollectContext &context, const D
MultiFunction &inputs_fn = context.resources.construct<MFNetworkEvaluator>(
AT, Span<const MFOutputSocket *>(), input_sockets.as_span());
+ const ParticleAction *birth_action = create_particle_action(
+ context, dnode.input(2, "Execute"), names);
+
StringRefNull own_state_name = get_identifier(context, dnode);
context.required_states.add(own_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER);
ParticleEmitter &emitter = context.resources.construct<ParticleMeshEmitter>(
- AT, own_state_name, names.as_span(), inputs_fn);
+ AT, own_state_name, names.as_span(), inputs_fn, birth_action);
return &emitter;
}
@@ -490,15 +501,10 @@ static void collect_time_step_events(CollectContext &context)
{
for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleTimeStepEvent")) {
const DInputSocket &execute_input = event_dnode->input(0);
- if (execute_input.linked_sockets().size() != 1) {
- continue;
- }
-
Array<StringRefNull> particle_names = find_linked_particle_simulations(context,
event_dnode->output(0));
- const DOutputSocket &execute_source = *execute_input.linked_sockets()[0];
- const ParticleAction *action = create_particle_action(context, execute_source, particle_names);
+ const ParticleAction *action = create_particle_action(context, execute_input, particle_names);
if (action == nullptr) {
continue;
}
@@ -570,6 +576,10 @@ class SetParticleAttributeAction : public ParticleAction {
cpp_type_.copy_to_initialized_indices(
value_array.data(), attribute_array->data(), context.particles.index_mask);
}
+
+ if (attribute_name_ == "Velocity") {
+ context.particles.update_diffs_after_velocity_change();
+ }
}
};
@@ -595,21 +605,28 @@ static const ParticleAction *create_set_particle_attribute_action(
CollectContext &context, const DOutputSocket &dsocket, Span<StringRefNull> particle_names)
{
const DNode &dnode = dsocket.node();
+
+ const ParticleAction *previous_action = create_particle_action(
+ context, dnode.input(0), particle_names);
+
MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode.input(1));
MFInputSocket &value_socket = name_socket.node().input(1);
std::optional<Array<std::string>> names = compute_global_string_inputs(context.network_map,
{&name_socket});
if (!names.has_value()) {
- return nullptr;
+ return previous_action;
}
std::string attribute_name = (*names)[0];
+ if (attribute_name.empty()) {
+ return previous_action;
+ }
const CPPType &attribute_type = value_socket.data_type().single_type();
const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context,
{&value_socket});
if (inputs_fn == nullptr) {
- return nullptr;
+ return previous_action;
}
for (StringRef particle_name : particle_names) {
@@ -620,9 +637,6 @@ static const ParticleAction *create_set_particle_attribute_action(
ParticleAction &this_action = context.resources.construct<SetParticleAttributeAction>(
AT, attribute_name, attribute_type, *inputs_fn);
- const ParticleAction *previous_action = create_particle_action(
- context, dnode.input(0), particle_names);
-
return concatenate_actions(context, {previous_action, &this_action});
}
@@ -698,10 +712,9 @@ static const ParticleAction *create_particle_condition_action(CollectContext &co
Span<StringRefNull> particle_names)
{
const DNode &dnode = dsocket.node();
- MFInputSocket &condition_socket = context.network_map.lookup_dummy(dnode.input(0));
- const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context,
- {&condition_socket});
+ const ParticleFunction *inputs_fn = create_particle_function_for_inputs(
+ context, {&dnode.input(0, "Condition")});
if (inputs_fn == nullptr) {
return nullptr;
}
@@ -718,17 +731,32 @@ static const ParticleAction *create_particle_condition_action(CollectContext &co
AT, *inputs_fn, true_action, false_action);
}
+class KillParticleAction : public ParticleAction {
+ public:
+ void execute(ParticleActionContext &context) const override
+ {
+ MutableSpan<int> dead_states = context.particles.attributes.get<int>("Dead");
+ for (int i : context.particles.index_mask) {
+ dead_states[i] = true;
+ }
+ }
+};
+
static const ParticleAction *create_particle_action(CollectContext &context,
const DOutputSocket &dsocket,
Span<StringRefNull> particle_names)
{
const DNode &dnode = dsocket.node();
- if (dnode.idname() == "SimulationNodeSetParticleAttribute") {
+ StringRef idname = dnode.idname();
+ if (idname == "SimulationNodeSetParticleAttribute") {
return create_set_particle_attribute_action(context, dsocket, particle_names);
}
- if (dnode.idname() == "SimulationNodeExecuteCondition") {
+ if (idname == "SimulationNodeExecuteCondition") {
return create_particle_condition_action(context, dsocket, particle_names);
}
+ if (idname == "SimulationNodeKillParticle") {
+ return &context.resources.construct<KillParticleAction>(AT);
+ }
return nullptr;
}
@@ -762,25 +790,38 @@ static void optimize_function_network(CollectContext &context)
class AgeReachedEvent : public ParticleEvent {
private:
std::string attribute_name_;
+ const ParticleFunction &inputs_fn_;
+ const ParticleAction &action_;
public:
- AgeReachedEvent(std::string attribute_name) : attribute_name_(std::move(attribute_name))
+ AgeReachedEvent(std::string attribute_name,
+ const ParticleFunction &inputs_fn,
+ const ParticleAction &action)
+ : attribute_name_(std::move(attribute_name)), inputs_fn_(inputs_fn), action_(action)
{
}
void filter(ParticleEventFilterContext &context) const override
{
Span<float> birth_times = context.particles.attributes.get<float>("Birth Time");
- Span<int> has_been_triggered = context.particles.attributes.get<int>(attribute_name_);
- const float age = 5.0f;
+ std::optional<Span<int>> has_been_triggered = context.particles.attributes.try_get<int>(
+ attribute_name_);
+ if (!has_been_triggered.has_value()) {
+ return;
+ }
+
+ ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles};
+ evaluator.compute();
+ VSpan<float> trigger_ages = evaluator.get<float>(0, "Age");
const float end_time = context.particles.integration->end_time;
for (int i : context.particles.index_mask) {
- if (has_been_triggered[i]) {
+ if ((*has_been_triggered)[i]) {
continue;
}
+ const float trigger_age = trigger_ages[i];
const float birth_time = birth_times[i];
- const float trigger_time = birth_time + age;
+ const float trigger_time = birth_time + trigger_age;
if (trigger_time > end_time) {
continue;
}
@@ -795,24 +836,41 @@ class AgeReachedEvent : public ParticleEvent {
void execute(ParticleActionContext &context) const override
{
- MutableSpan<int> dead_states = context.particles.attributes.get<int>("Dead");
MutableSpan<int> has_been_triggered = context.particles.attributes.get<int>(attribute_name_);
for (int i : context.particles.index_mask) {
- dead_states[i] = true;
has_been_triggered[i] = 1;
}
+ action_.execute(context);
}
};
static void collect_age_reached_events(CollectContext &context)
{
- /* TODO: Actually implement an Age Reached Event node. */
- std::string attribute_name = "Has Been Triggered";
- const AgeReachedEvent &event = context.resources.construct<AgeReachedEvent>(AT, attribute_name);
- for (const DNode *dnode : context.particle_simulation_nodes) {
- StringRefNull name = get_identifier(context, *dnode);
- context.influences.particle_events.add_as(name, &event);
- context.influences.particle_attributes_builder.lookup_as(name)->add<int>(attribute_name, 0);
+ for (const DNode *dnode : nodes_by_type(context, "SimulationNodeAgeReachedEvent")) {
+ const DInputSocket &age_input = dnode->input(0, "Age");
+ const DInputSocket &execute_input = dnode->input(1, "Execute");
+ Array<StringRefNull> particle_names = find_linked_particle_simulations(context,
+ dnode->output(0));
+ const ParticleAction *action = create_particle_action(context, execute_input, particle_names);
+ if (action == nullptr) {
+ continue;
+ }
+ const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context, {&age_input});
+ if (inputs_fn == nullptr) {
+ continue;
+ }
+
+ std::string attribute_name = get_identifier(context, *dnode);
+ const ParticleEvent &event = context.resources.construct<AgeReachedEvent>(
+ AT, attribute_name, *inputs_fn, *action);
+ for (StringRefNull particle_name : particle_names) {
+ const bool added_attribute = context.influences.particle_attributes_builder
+ .lookup_as(particle_name)
+ ->add<int>(attribute_name, 0);
+ if (added_attribute) {
+ context.influences.particle_events.add_as(particle_name, &event);
+ }
+ }
}
}
diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc
index 9f2766aa24f..d53ccd2bd49 100644
--- a/source/blender/simulation/intern/simulation_solver.cc
+++ b/source/blender/simulation/intern/simulation_solver.cc
@@ -143,8 +143,9 @@ BLI_NOINLINE static void find_next_event_per_particle(
r_next_event_indices.fill_indices(particles.index_mask, -1);
r_time_factors_to_next_event.fill_indices(particles.index_mask, 1.0f);
- Array<float> time_factors(particles.index_mask.min_array_size(), -1.0f);
+ Array<float> time_factors(particles.index_mask.min_array_size());
for (int event_index : events.index_range()) {
+ time_factors.fill(-1.0f);
ParticleEventFilterContext event_context{solve_context, particles, time_factors};
const ParticleEvent &event = *events[event_index];
event.filter(event_context);