diff --git a/source/blender/blenkernel/BKE_bake_items.hh b/source/blender/blenkernel/BKE_bake_items.hh index 074a4faf0d8..ac45121f226 100644 --- a/source/blender/blenkernel/BKE_bake_items.hh +++ b/source/blender/blenkernel/BKE_bake_items.hh @@ -19,6 +19,22 @@ class BakeItem { virtual ~BakeItem() = default; }; +struct BakeState { + /** + * The ids are usually correspond to socket ids, so that the mapping stays intact even if socket + * order changes. + */ + Map> items_by_id; +}; + +/** Same as above, but does not own the bake items. */ +struct BakeStateRef { + Map items_by_id; + + BakeStateRef() = default; + BakeStateRef(const BakeState &bake_state); +}; + class GeometryBakeItem : public BakeItem { public: GeometrySet geometry; diff --git a/source/blender/blenkernel/BKE_bake_items_serialize.hh b/source/blender/blenkernel/BKE_bake_items_serialize.hh index 6ea32d50674..bf15dc7a57b 100644 --- a/source/blender/blenkernel/BKE_bake_items_serialize.hh +++ b/source/blender/blenkernel/BKE_bake_items_serialize.hh @@ -150,16 +150,13 @@ std::unique_ptr deserialize_bake_item(const io::serialize::DictionaryV const BDataReader &bdata_reader, const BDataSharing &bdata_sharing); -void serialize_bake(const Map &items_by_id, - BDataWriter &bdata_writer, - BDataSharing &bdata_sharing, - std::ostream &r_stream); -void serialize_bake(const Map> &items_by_id, +void serialize_bake(const BakeState &bake_state, BDataWriter &bdata_writer, BDataSharing &bdata_sharing, std::ostream &r_stream); -std::optional>> deserialize_bake( - std::istream &stream, const BDataReader &bdata_reader, const BDataSharing &bdata_sharing); +std::optional deserialize_bake(std::istream &stream, + const BDataReader &bdata_reader, + const BDataSharing &bdata_sharing); } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_simulation_state.hh b/source/blender/blenkernel/BKE_simulation_state.hh index a87b4051aaa..4705e6a6fbb 100644 --- a/source/blender/blenkernel/BKE_simulation_state.hh +++ b/source/blender/blenkernel/BKE_simulation_state.hh @@ -31,13 +31,13 @@ enum class CacheState { struct SimulationZoneFrameCache { SubFrame frame; - Map> items; + BakeState state; /** Used when the baked data is loaded lazily. */ std::optional meta_path; }; struct SimulationZonePrevState { - Map> items; + BakeState state; SubFrame frame; }; diff --git a/source/blender/blenkernel/intern/bake_items.cc b/source/blender/blenkernel/intern/bake_items.cc index 4f89a1a7f18..7a0dcc55b68 100644 --- a/source/blender/blenkernel/intern/bake_items.cc +++ b/source/blender/blenkernel/intern/bake_items.cc @@ -73,4 +73,12 @@ PrimitiveBakeItem::~PrimitiveBakeItem() StringBakeItem::StringBakeItem(std::string value) : value_(std::move(value)) {} +BakeStateRef::BakeStateRef(const BakeState &bake_state) +{ + this->items_by_id.reserve(bake_state.items_by_id.size()); + for (auto item : bake_state.items_by_id.items()) { + this->items_by_id.add_new(item.key, item.value.get()); + } +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/bake_items_serialize.cc b/source/blender/blenkernel/intern/bake_items_serialize.cc index 5828b43c1e8..e8b17351f74 100644 --- a/source/blender/blenkernel/intern/bake_items_serialize.cc +++ b/source/blender/blenkernel/intern/bake_items_serialize.cc @@ -1066,7 +1066,7 @@ std::unique_ptr deserialize_bake_item(const DictionaryValue &io_item, static constexpr int bake_file_version = 3; -void serialize_bake(const Map &items_by_id, +void serialize_bake(const BakeState &bake_state, BDataWriter &bdata_writer, BDataSharing &bdata_sharing, std::ostream &r_stream) @@ -1074,7 +1074,7 @@ void serialize_bake(const Map &items_by_id, io::serialize::DictionaryValue io_root; io_root.append_int("version", bake_file_version); io::serialize::DictionaryValue &io_items = *io_root.append_dict("items"); - for (auto item : items_by_id.items()) { + for (auto item : bake_state.items_by_id.items()) { io::serialize::DictionaryValue &io_item = *io_items.append_dict(std::to_string(item.key)); bke::serialize_bake_item(*item.value, bdata_writer, bdata_sharing, io_item); } @@ -1083,21 +1083,9 @@ void serialize_bake(const Map &items_by_id, formatter.serialize(r_stream, io_root); } -void serialize_bake(const Map> &items_by_id, - BDataWriter &bdata_writer, - BDataSharing &bdata_sharing, - std::ostream &r_stream) -{ - Map map; - map.reserve(items_by_id.size()); - for (auto item : items_by_id.items()) { - map.add_new(item.key, item.value.get()); - } - serialize_bake(map, bdata_writer, bdata_sharing, r_stream); -} - -std::optional>> deserialize_bake( - std::istream &stream, const BDataReader &bdata_reader, const BDataSharing &bdata_sharing) +std::optional deserialize_bake(std::istream &stream, + const BDataReader &bdata_reader, + const BDataSharing &bdata_sharing) { JsonFormatter formatter; std::unique_ptr io_root_value = formatter.deserialize(stream); @@ -1116,7 +1104,7 @@ std::optional>> deserialize_bake( if (!io_items) { return std::nullopt; } - Map> bake_items; + BakeState bake_state; for (const auto &io_item_value : io_items->elements()) { const io::serialize::DictionaryValue *io_item = io_item_value.second->as_dictionary_value(); if (!io_item) { @@ -1129,7 +1117,7 @@ std::optional>> deserialize_bake( catch (...) { return std::nullopt; } - if (bake_items.contains(id)) { + if (bake_state.items_by_id.contains(id)) { return std::nullopt; } std::unique_ptr bake_item = deserialize_bake_item( @@ -1137,9 +1125,9 @@ std::optional>> deserialize_bake( if (!bake_item) { return std::nullopt; } - bake_items.add_new(id, std::move(bake_item)); + bake_state.items_by_id.add_new(id, std::move(bake_item)); } - return bake_items; + return bake_state; } } // namespace blender::bke diff --git a/source/blender/editors/object/object_bake_simulation.cc b/source/blender/editors/object/object_bake_simulation.cc index 38967252bb8..8d0f979553a 100644 --- a/source/blender/editors/object/object_bake_simulation.cc +++ b/source/blender/editors/object/object_bake_simulation.cc @@ -360,7 +360,7 @@ static void bake_simulation_job_startjob(void *customdata, bke::DiskBDataWriter bdata_writer{bdata_file_name, bdata_file, 0}; fstream meta_file{meta_path, std::ios::out}; bke::serialize_bake( - frame_cache.items, bdata_writer, *zone_bake_data.bdata_sharing, meta_file); + frame_cache.state, bdata_writer, *zone_bake_data.bdata_sharing, meta_file); } } } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 2d0624fc20c..59c72281b11 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -811,7 +811,7 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams { zone_cache.cache_state = CacheState::Invalid; } output_copy_info.delta_time = delta_frames / fps_; - output_copy_info.items_by_id = this->to_readonly_items_map(prev_frame_cache.items); + output_copy_info.state = prev_frame_cache.state; this->output_store_frame_cache(zone_cache, zone_behavior); return; } @@ -828,7 +828,7 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams { max_delta_frames, float(zone_cache.prev_state->frame) - float(current_frame_)); auto &output_move_info = zone_behavior.input.emplace(); output_move_info.delta_time = delta_frames / fps_; - output_move_info.items_by_id = std::move(zone_cache.prev_state->items); + output_move_info.state = std::move(zone_cache.prev_state->state); this->store_as_prev_items(zone_cache, zone_behavior); return; } @@ -836,9 +836,9 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams { /* Just read from the previous state if the frame has not changed. */ auto &output_copy_info = zone_behavior.input.emplace(); output_copy_info.delta_time = 0.0f; - output_copy_info.items_by_id = this->to_readonly_items_map(zone_cache.prev_state->items); + output_copy_info.state = zone_cache.prev_state->state; auto &read_single_info = zone_behavior.output.emplace(); - read_single_info.items_by_id = this->to_readonly_items_map(zone_cache.prev_state->items); + read_single_info.state = zone_cache.prev_state->state; return; } if (!depsgraph_is_active_) { @@ -900,16 +900,15 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams { { auto &store_and_pass_through_info = zone_behavior.output.emplace(); - store_and_pass_through_info.store_fn = - [simulation_cache = simulation_cache_, - zone_cache = &zone_cache, - current_frame = current_frame_](Map> items) { - std::lock_guard lock{simulation_cache->mutex}; - auto frame_cache = std::make_unique(); - frame_cache->frame = current_frame; - frame_cache->items = std::move(items); - zone_cache->frame_caches.append(std::move(frame_cache)); - }; + store_and_pass_through_info.store_fn = [simulation_cache = simulation_cache_, + zone_cache = &zone_cache, + current_frame = current_frame_](bke::BakeState state) { + std::lock_guard lock{simulation_cache->mutex}; + auto frame_cache = std::make_unique(); + frame_cache->frame = current_frame; + frame_cache->state = std::move(state); + zone_cache->frame_caches.append(std::move(frame_cache)); + }; } void store_as_prev_items(bke::sim::SimulationZoneCache &zone_cache, @@ -917,17 +916,16 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams { { auto &store_and_pass_through_info = zone_behavior.output.emplace(); - store_and_pass_through_info.store_fn = - [simulation_cache = simulation_cache_, - zone_cache = &zone_cache, - current_frame = current_frame_](Map> items) { - std::lock_guard lock{simulation_cache->mutex}; - if (!zone_cache->prev_state) { - zone_cache->prev_state.emplace(); - } - zone_cache->prev_state->items = std::move(items); - zone_cache->prev_state->frame = current_frame; - }; + store_and_pass_through_info.store_fn = [simulation_cache = simulation_cache_, + zone_cache = &zone_cache, + current_frame = current_frame_](bke::BakeState state) { + std::lock_guard lock{simulation_cache->mutex}; + if (!zone_cache->prev_state) { + zone_cache->prev_state.emplace(); + } + zone_cache->prev_state->state = std::move(state); + zone_cache->prev_state->frame = current_frame; + }; } void read_from_cache(const FrameIndices &frame_indices, @@ -941,9 +939,7 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams { const float delta_frames = std::min(max_delta_frames, float(current_frame_) - float(frame_cache.frame)); output_copy_info.delta_time = delta_frames / fps_; - for (auto item : frame_cache.items.items()) { - output_copy_info.items_by_id.add_new(item.key, item.value.get()); - } + output_copy_info.state = frame_cache.state; } else { zone_behavior.input.emplace(); @@ -980,7 +976,7 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams { bke::sim::SimulationZoneFrameCache &frame_cache = *zone_cache.frame_caches[frame_index]; this->ensure_bake_loaded(zone_cache, frame_cache); auto &read_single_info = zone_behavior.output.emplace(); - read_single_info.items_by_id = this->to_readonly_items_map(frame_cache.items); + read_single_info.state = frame_cache.state; } void read_interpolated(const int prev_frame_index, @@ -998,24 +994,14 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams { read_interpolated_info.mix_factor = (float(current_frame_) - float(prev_frame_cache.frame)) / (float(next_frame_cache.frame) - float(prev_frame_cache.frame)); - read_interpolated_info.prev_items_by_id = this->to_readonly_items_map(prev_frame_cache.items); - read_interpolated_info.next_items_by_id = this->to_readonly_items_map(next_frame_cache.items); - } - - Map to_readonly_items_map( - const Map> &items) const - { - Map map; - for (auto item : items.items()) { - map.add_new(item.key, item.value.get()); - } - return map; + read_interpolated_info.prev_state = prev_frame_cache.state; + read_interpolated_info.next_state = next_frame_cache.state; } void ensure_bake_loaded(bke::sim::SimulationZoneCache &zone_cache, bke::sim::SimulationZoneFrameCache &frame_cache) const { - if (!frame_cache.items.is_empty()) { + if (!frame_cache.state.items_by_id.is_empty()) { return; } if (!zone_cache.bdata_dir) { @@ -1026,12 +1012,12 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams { } bke::DiskBDataReader bdata_reader{*zone_cache.bdata_dir}; fstream meta_file{*frame_cache.meta_path}; - std::optional>> bake_items = bke::deserialize_bake( + std::optional bake_state = bke::deserialize_bake( meta_file, bdata_reader, *zone_cache.bdata_sharing); - if (!bake_items.has_value()) { + if (!bake_state.has_value()) { return; } - frame_cache.items = std::move(*bake_items); + frame_cache.state = std::move(*bake_state); } }; diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh index 4c1863f1d2a..f6b5e5d398e 100644 --- a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -57,7 +57,7 @@ struct PassThrough { */ struct OutputCopy { float delta_time; - Map items_by_id; + bke::BakeStateRef state; }; /** @@ -66,7 +66,7 @@ struct OutputCopy { */ struct OutputMove { float delta_time; - Map> items_by_id; + bke::BakeState state; }; using Behavior = std::variant; @@ -88,14 +88,14 @@ struct PassThrough { * This allows the caller of geometry nodes (e.g. the modifier), to cache the new simulation state. */ struct StoreAndPassThrough { - std::function> items_by_id)> store_fn; + std::function store_fn; }; /** * The inputs are not evaluated, instead the given cached items are output directly. */ struct ReadSingle { - Map items_by_id; + bke::BakeStateRef state; }; /** @@ -104,8 +104,8 @@ struct ReadSingle { struct ReadInterpolated { /** Factor between 0 and 1 that determines the influence of the two simulation states. */ float mix_factor; - Map prev_items_by_id; - Map next_items_by_id; + bke::BakeStateRef prev_state; + bke::BakeStateRef next_state; }; using Behavior = std::variant; diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 116e0985f16..2b1edaeee71 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -137,16 +137,16 @@ void socket_declarations_for_simulation_items(Span items, const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type); const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item); -Map> move_values_to_simulation_state( +bke::BakeState move_values_to_simulation_state( const Span node_simulation_items, const Span input_values); void move_simulation_state_to_values(const Span node_simulation_items, - Map> zone_state, + bke::BakeState zone_state, const Object &self_object, const ComputeContext &compute_context, const bNode &sim_output_node, Span r_output_values); void copy_simulation_state_to_values(const Span node_simulation_items, - const Map &zone_state, + const bke::BakeStateRef &zone_state, const Object &self_object, const ComputeContext &compute_context, const bNode &sim_output_node, diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index b5c6e8ba675..eb1044a3b0f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -86,11 +86,11 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { float delta_time = 0.0f; if (auto *info = std::get_if(&input_behavior)) { delta_time = info->delta_time; - this->output_simulation_state_copy(params, user_data, info->items_by_id); + this->output_simulation_state_copy(params, user_data, info->state); } else if (auto *info = std::get_if(&input_behavior)) { delta_time = info->delta_time; - this->output_simulation_state_move(params, user_data, std::move(info->items_by_id)); + this->output_simulation_state_move(params, user_data, std::move(info->state)); } else if (std::get_if(&input_behavior)) { delta_time = 0.0f; @@ -106,14 +106,14 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { void output_simulation_state_copy(lf::Params ¶ms, const GeoNodesLFUserData &user_data, - const Map &zone_state) const + const bke::BakeStateRef &zone_state) const { Array outputs(simulation_items_.size()); for (const int i : simulation_items_.index_range()) { outputs[i] = params.get_output_data_ptr(i + 1); } copy_simulation_state_to_values(simulation_items_, - std::move(zone_state), + zone_state, *user_data.modifier_data->self_object, *user_data.compute_context, node_, @@ -125,7 +125,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { void output_simulation_state_move(lf::Params ¶ms, const GeoNodesLFUserData &user_data, - Map> zone_state) const + bke::BakeState zone_state) const { Array outputs(simulation_items_.size()); for (const int i : simulation_items_.index_range()) { @@ -155,9 +155,8 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { /* Instead of outputting the initial values directly, convert them to a simulation state and * then back. This ensures that some geometry processing happens on the data consistently (e.g. * removing anonymous attributes). */ - Map> bake_items = move_values_to_simulation_state( - simulation_items_, input_values); - this->output_simulation_state_move(params, user_data, std::move(bake_items)); + bke::BakeState bake_state = move_values_to_simulation_state(simulation_items_, input_values); + this->output_simulation_state_move(params, user_data, std::move(bake_state)); } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index c470a4ef05c..b40d85fd710 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -185,7 +185,7 @@ static std::shared_ptr make_attribute_field( } void move_simulation_state_to_values(const Span node_simulation_items, - Map> zone_state, + bke::BakeState zone_state, const Object &self_object, const ComputeContext &compute_context, const bNode &node, @@ -194,7 +194,7 @@ void move_simulation_state_to_values(const Span node_simulat const bke::BakeSocketConfig config = make_bake_socket_config(node_simulation_items); Vector bake_items; for (const NodeSimulationItem &item : node_simulation_items) { - auto *bake_item = zone_state.lookup_ptr(item.identifier); + auto *bake_item = zone_state.items_by_id.lookup_ptr(item.identifier); bake_items.append(bake_item ? bake_item->get() : nullptr); } @@ -209,7 +209,7 @@ void move_simulation_state_to_values(const Span node_simulat } void copy_simulation_state_to_values(const Span node_simulation_items, - const Map &zone_state, + const bke::BakeStateRef &zone_state, const Object &self_object, const ComputeContext &compute_context, const bNode &node, @@ -218,7 +218,7 @@ void copy_simulation_state_to_values(const Span node_simulat const bke::BakeSocketConfig config = make_bake_socket_config(node_simulation_items); Vector bake_items; for (const NodeSimulationItem &item : node_simulation_items) { - const bke::BakeItem *const *bake_item = zone_state.lookup_ptr(item.identifier); + const bke::BakeItem *const *bake_item = zone_state.items_by_id.lookup_ptr(item.identifier); bake_items.append(bake_item ? *bake_item : nullptr); } @@ -232,7 +232,7 @@ void copy_simulation_state_to_values(const Span node_simulat r_output_values); } -Map> move_values_to_simulation_state( +bke::BakeState move_values_to_simulation_state( const Span node_simulation_items, const Span input_values) { const bke::BakeSocketConfig config = make_bake_socket_config(node_simulation_items); @@ -240,15 +240,15 @@ Map> move_values_to_simulation_state( Array> bake_items = bke::move_socket_values_to_bake_items( input_values, config); - Map> bake_items_map; + bke::BakeState bake_state; for (const int i : node_simulation_items.index_range()) { const NodeSimulationItem &item = node_simulation_items[i]; std::unique_ptr &bake_item = bake_items[i]; if (bake_item) { - bake_items_map.add_new(item.identifier, std::move(bake_item)); + bake_state.items_by_id.add_new(item.identifier, std::move(bake_item)); } } - return bake_items_map; + return bake_state; } } // namespace blender::nodes @@ -567,14 +567,14 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { } sim_output::Behavior &output_behavior = zone_behavior->output; if (auto *info = std::get_if(&output_behavior)) { - this->output_cached_state(params, user_data, info->items_by_id); + this->output_cached_state(params, user_data, info->state); } else if (auto *info = std::get_if(&output_behavior)) { this->output_mixed_cached_state(params, *modifier_data.self_object, *user_data.compute_context, - info->prev_items_by_id, - info->next_items_by_id, + info->prev_state, + info->next_state, info->mix_factor); } else if (std::get_if(&output_behavior)) { @@ -590,7 +590,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { void output_cached_state(lf::Params ¶ms, GeoNodesLFUserData &user_data, - const Map &state) const + const bke::BakeStateRef &state) const { Array output_values(simulation_items_.size()); for (const int i : simulation_items_.index_range()) { @@ -610,8 +610,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { void output_mixed_cached_state(lf::Params ¶ms, const Object &self_object, const ComputeContext &compute_context, - const Map &prev_state, - const Map &next_state, + const bke::BakeStateRef &prev_state, + const bke::BakeStateRef &next_state, const float mix_factor) const { Array output_values(simulation_items_.size()); @@ -649,9 +649,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { /* Instead of outputting the initial values directly, convert them to a simulation state and * then back. This ensures that some geometry processing happens on the data consistently (e.g. * removing anonymous attributes). */ - std::optional>> bake_items = - this->get_bake_items_from_inputs(params); - if (!bake_items) { + std::optional bake_state = this->get_bake_state_from_inputs(params); + if (!bake_state) { /* Wait for inputs to be computed. */ return; } @@ -661,7 +660,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { output_values[i] = params.get_output_data_ptr(i); } move_simulation_state_to_values(simulation_items_, - std::move(*bake_items), + std::move(*bake_state), *user_data.modifier_data->self_object, *user_data.compute_context, node_, @@ -675,22 +674,16 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { GeoNodesLFUserData &user_data, const sim_output::StoreAndPassThrough &info) const { - std::optional>> bake_items = - this->get_bake_items_from_inputs(params); - if (!bake_items) { + std::optional bake_state = this->get_bake_state_from_inputs(params); + if (!bake_state) { /* Wait for inputs to be computed. */ return; } - Map bake_item_pointers; - for (const auto item : bake_items->items()) { - bake_item_pointers.add_new(item.key, item.value.get()); - } - this->output_cached_state(params, user_data, bake_item_pointers); - info.store_fn(std::move(*bake_items)); + this->output_cached_state(params, user_data, *bake_state); + info.store_fn(std::move(*bake_state)); } - std::optional>> get_bake_items_from_inputs( - lf::Params ¶ms) const + std::optional get_bake_state_from_inputs(lf::Params ¶ms) const { Array input_values(inputs_.size()); for (const int i : inputs_.index_range()) {