diff options
Diffstat (limited to 'source')
15 files changed, 295 insertions, 41 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 4c55488ecd5..f1502a9b87d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1331,6 +1331,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define SIM_NODE_EMIT_PARTICLES 1009 #define SIM_NODE_TIME 1010 #define SIM_NODE_PARTICLE_ATTRIBUTE 1011 +#define SIM_NODE_AGE_REACHED_EVENT 1012 +#define SIM_NODE_KILL_PARTICLE 1013 /** \} */ @@ -1344,6 +1346,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_GROUP_INSTANCE_ID 1203 #define FN_NODE_COMBINE_STRINGS 1204 #define FN_NODE_OBJECT_TRANSFORMS 1205 +#define FN_NODE_RANDOM_FLOAT 1206 /** \} */ diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 8d7cb90fb71..91693abd1cf 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -4352,6 +4352,8 @@ static void registerSimulationNodes(void) register_node_type_sim_emit_particles(); register_node_type_sim_time(); register_node_type_sim_particle_attribute(); + register_node_type_sim_age_reached_event(); + register_node_type_sim_kill_particle(); } static void registerFunctionNodes(void) @@ -4362,6 +4364,7 @@ static void registerFunctionNodes(void) register_node_type_fn_group_instance_id(); register_node_type_fn_combine_strings(); register_node_type_fn_object_transforms(); + register_node_type_fn_random_float(); } void init_nodesystem(void) diff --git a/source/blender/functions/intern/attributes_ref.cc b/source/blender/functions/intern/attributes_ref.cc index 4686e217911..8f7f41be079 100644 --- a/source/blender/functions/intern/attributes_ref.cc +++ b/source/blender/functions/intern/attributes_ref.cc @@ -27,6 +27,10 @@ AttributesInfoBuilder::~AttributesInfoBuilder() bool AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value) { + if (name.size() == 0) { + std::cout << "Warning: Tried to add an attribute with empty name.\n"; + return false; + } if (names_.add_as(name)) { types_.append(&type); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index c53e01ac80f..33b95d50cc0 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -136,6 +136,7 @@ set(SRC function/nodes/node_fn_float_compare.cc function/nodes/node_fn_group_instance_id.cc function/nodes/node_fn_object_transforms.cc + function/nodes/node_fn_random_float.cc function/nodes/node_fn_switch.cc function/node_function_util.cc @@ -232,10 +233,12 @@ set(SRC shader/node_shader_tree.c shader/node_shader_util.c + simulation/nodes/node_sim_age_reached_event.cc simulation/nodes/node_sim_common.cc simulation/nodes/node_sim_emit_particles.cc simulation/nodes/node_sim_execute_condition.cc simulation/nodes/node_sim_force.cc + simulation/nodes/node_sim_kill_particle.cc simulation/nodes/node_sim_multi_execute.cc simulation/nodes/node_sim_particle_attribute.cc simulation/nodes/node_sim_particle_birth_event.cc diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 4c05da694f7..1d22e9c6811 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -27,6 +27,7 @@ void register_node_type_fn_switch(void); void register_node_type_fn_group_instance_id(void); void register_node_type_fn_combine_strings(void); void register_node_type_fn_object_transforms(void); +void register_node_type_fn_random_float(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_simulation.h b/source/blender/nodes/NOD_simulation.h index 2947d38fe83..fe3f73517ec 100644 --- a/source/blender/nodes/NOD_simulation.h +++ b/source/blender/nodes/NOD_simulation.h @@ -39,6 +39,8 @@ void register_node_type_sim_particle_mesh_collision_event(void); void register_node_type_sim_emit_particles(void); void register_node_type_sim_time(void); void register_node_type_sim_particle_attribute(void); +void register_node_type_sim_age_reached_event(void); +void register_node_type_sim_kill_particle(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 31ce3f81450..7922a73902c 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -270,6 +270,8 @@ DefNode(SimulationNode, SIM_NODE_PARTICLE_MESH_COLLISION_EVENT, 0, "PARTIC DefNode(SimulationNode, SIM_NODE_EMIT_PARTICLES, 0, "EMIT_PARTICLES", EmitParticles, "Emit Particles", "") DefNode(SimulationNode, SIM_NODE_TIME, def_sim_time, "TIME", Time, "Time", "") DefNode(SimulationNode, SIM_NODE_PARTICLE_ATTRIBUTE, def_sim_particle_attribute, "PARTICLE_ATTRIBUTE", ParticleAttribute, "Particle Attribute", "") +DefNode(SimulationNode, SIM_NODE_AGE_REACHED_EVENT, 0, "AGE_REACHED_EVENT", AgeReachedEvent, "Age Reached Event", "") +DefNode(SimulationNode, SIM_NODE_KILL_PARTICLE, 0, "KILL_PARTICLE", KillParticle, "Kill Particle", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") @@ -277,7 +279,7 @@ DefNode(FunctionNode, FN_NODE_SWITCH, def_fn_switch, "SWITCH", DefNode(FunctionNode, FN_NODE_GROUP_INSTANCE_ID, 0, "GROUP_INSTANCE_ID", GroupInstanceID, "Group Instance ID", "") DefNode(FunctionNode, FN_NODE_COMBINE_STRINGS, 0, "COMBINE_STRINGS", CombineStrings, "Combine Strings", "") DefNode(FunctionNode, FN_NODE_OBJECT_TRANSFORMS, 0, "OBJECT_TRANSFORMS", ObjectTransforms, "Object Transforms", "") - +DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") /* undefine macros */ diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc new file mode 100644 index 00000000000..2ee0830637a --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -0,0 +1,89 @@ +/* + * 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 "node_function_util.hh" + +#include "BLI_hash.h" + +static bNodeSocketTemplate fn_node_random_float_in[] = { + {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_INT, N_("Seed")}, + {-1, ""}, +}; + +static bNodeSocketTemplate fn_node_random_float_out[] = { + {SOCK_FLOAT, N_("Value")}, + {-1, ""}, +}; + +class RandomFloatFunction : public blender::fn::MultiFunction { + private: + uint32_t function_seed_; + + public: + RandomFloatFunction(uint32_t function_seed) : function_seed_(function_seed) + { + blender::fn::MFSignatureBuilder signature = this->get_builder("Random float"); + signature.single_input<float>("Min"); + signature.single_input<float>("Max"); + signature.single_input<int>("Seed"); + signature.single_output<float>("Value"); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + blender::fn::VSpan<float> min_values = params.readonly_single_input<float>(0, "Min"); + blender::fn::VSpan<float> max_values = params.readonly_single_input<float>(1, "Max"); + blender::fn::VSpan<int> seeds = params.readonly_single_input<int>(2, "Seed"); + blender::MutableSpan<float> values = params.uninitialized_single_output<float>(3, "Value"); + + for (int64_t i : mask) { + const float min_value = min_values[i]; + const float max_value = max_values[i]; + const int seed = seeds[i]; + const float value = BLI_hash_int_01((uint32_t)seed ^ function_seed_); + values[i] = value * (max_value - min_value) + min_value; + } + } +}; + +static void fn_node_random_float_expand_in_mf_network( + blender::nodes::NodeMFNetworkBuilder &builder) +{ + uint32_t function_seed = 1746872341u; + const blender::nodes::DNode &node = builder.dnode(); + const blender::DefaultHash<blender::StringRefNull> hasher; + function_seed = 33 * function_seed + hasher(node.name()); + for (const blender::nodes::DParentNode *parent = node.parent(); parent != nullptr; + parent = parent->parent()) { + function_seed = 33 * function_seed + hasher(parent->node_ref().name()); + } + + builder.construct_and_set_matching_fn<RandomFloatFunction>(function_seed); +} + +void register_node_type_fn_random_float() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); + node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out); + ntype.expand_in_mf_network = fn_node_random_float_expand_in_mf_network; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/simulation/nodes/node_sim_age_reached_event.cc b/source/blender/nodes/simulation/nodes/node_sim_age_reached_event.cc new file mode 100644 index 00000000000..add8c4eba4d --- /dev/null +++ b/source/blender/nodes/simulation/nodes/node_sim_age_reached_event.cc @@ -0,0 +1,38 @@ +/* + * 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 "node_simulation_util.h" + +static bNodeSocketTemplate sim_node_age_reached_event_in[] = { + {SOCK_FLOAT, N_("Age"), 3, 0, 0, 0, 0, 10000000}, + {SOCK_CONTROL_FLOW, N_("Execute")}, + {-1, ""}, +}; + +static bNodeSocketTemplate sim_node_age_reached_event_out[] = { + {SOCK_EVENTS, N_("Event")}, + {-1, ""}, +}; + +void register_node_type_sim_age_reached_event() +{ + static bNodeType ntype; + + sim_node_type_base(&ntype, SIM_NODE_AGE_REACHED_EVENT, "Age Reached Event", 0, 0); + node_type_socket_templates( + &ntype, sim_node_age_reached_event_in, sim_node_age_reached_event_out); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/simulation/nodes/node_sim_kill_particle.cc b/source/blender/nodes/simulation/nodes/node_sim_kill_particle.cc new file mode 100644 index 00000000000..793b40d9365 --- /dev/null +++ b/source/blender/nodes/simulation/nodes/node_sim_kill_particle.cc @@ -0,0 +1,36 @@ +/* + * 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 "node_simulation_util.h" + +static bNodeSocketTemplate sim_node_kill_particle_in[] = { + {SOCK_CONTROL_FLOW, N_("Execute")}, + {-1, ""}, +}; + +static bNodeSocketTemplate sim_node_kill_particle_out[] = { + {SOCK_CONTROL_FLOW, N_("Execute")}, + {-1, ""}, +}; + +void register_node_type_sim_kill_particle() +{ + static bNodeType ntype; + + sim_node_type_base(&ntype, SIM_NODE_KILL_PARTICLE, "Kill Particle", 0, 0); + node_type_socket_templates(&ntype, sim_node_kill_particle_in, sim_node_kill_particle_out); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc index 859ad81656b..5e1a6c35d52 100644 --- a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc +++ b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc @@ -21,6 +21,7 @@ static bNodeSocketTemplate sim_node_particle_mesh_emitter_in[] = { {SOCK_OBJECT, N_("Object")}, {SOCK_FLOAT, N_("Rate"), 100.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_CONTROL_FLOW, N_("Execute")}, {-1, ""}, }; 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); |