mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Compositor: Add support for Bundles
This patch adds support for Bundles in the compositor. This is mostly a direct port of the Geometry Nodes code, except the compositor uses the BundleItemInternalValueMixin mechanism for storing results in bundles. The Result free function now resets the single value just like CPU/GPU buffers since bundles are stored in the single value needs to be freed. The Join Bundle and Get Nested Bundle Paths are not yet implemented because they require lists. Pull Request: https://projects.blender.org/blender/blender/pulls/162333
This commit is contained in:
parent
c1b95f1e72
commit
b2a937ef91
21 changed files with 485 additions and 14 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
from bl_ui import node_add_menu
|
||||
from bpy.app.translations import (
|
||||
pgettext_n as n_,
|
||||
contexts as i18n_contexts,
|
||||
)
|
||||
|
||||
|
|
@ -294,6 +295,7 @@ class NODE_MT_compositor_node_utilities_base(node_add_menu.NodeMenu):
|
|||
self.draw_menu(layout, path="Utilities/Text")
|
||||
self.draw_menu(layout, path="Utilities/Vector")
|
||||
layout.separator()
|
||||
self.draw_menu(layout, path="Utilities/Bundle")
|
||||
self.draw_menu(layout, path="Utilities/Matrix")
|
||||
self.draw_menu(layout, path="Utilities/Rotation")
|
||||
layout.separator()
|
||||
|
|
@ -404,6 +406,23 @@ class NODE_MT_compositor_node_text_base(node_add_menu.NodeMenu):
|
|||
self.draw_assets_for_catalog(layout, self.menu_path)
|
||||
|
||||
|
||||
class NODE_MT_compositor_utilities_bundle_base(node_add_menu.NodeMenu):
|
||||
bl_label = "Bundle"
|
||||
menu_path = "Utilities/Bundle"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
self.node_operator(layout, "NodeCombineBundle")
|
||||
self.node_operator(layout, "NodeSeparateBundle")
|
||||
layout.separator()
|
||||
self.node_operator(layout, "NodeGetBundleItem")
|
||||
self.node_operator(layout, "NodeStoreBundleItem")
|
||||
layout.separator()
|
||||
self.typed_bundle(layout, label=n_("Typed Bundle"))
|
||||
|
||||
self.draw_assets_for_catalog(layout, self.menu_path)
|
||||
|
||||
|
||||
class NODE_MT_compositor_utilities_matrix_base(node_add_menu.NodeMenu):
|
||||
bl_label = "Matrix"
|
||||
menu_path = "Utilities/Matrix"
|
||||
|
|
@ -517,6 +536,7 @@ add_menus = {
|
|||
"NODE_MT_category_compositor_math": NODE_MT_compositor_node_math_base,
|
||||
"NODE_MT_category_compositor_text": NODE_MT_compositor_node_text_base,
|
||||
"NODE_MT_category_compositor_matrix": NODE_MT_compositor_utilities_matrix_base,
|
||||
"NODE_MT_category_compositor_bundle": NODE_MT_compositor_utilities_bundle_base,
|
||||
"NODE_MT_category_compositor_rotation": NODE_MT_compositor_node_rotation_base,
|
||||
"NODE_MT_compositor_node_add_all": NODE_MT_compositor_node_all_base,
|
||||
}
|
||||
|
|
@ -548,6 +568,7 @@ swap_menus = {
|
|||
"NODE_MT_compositor_node_math_swap": NODE_MT_compositor_node_math_base,
|
||||
"NODE_MT_compositor_node_text_swap": NODE_MT_compositor_node_text_base,
|
||||
"NODE_MT_compositor_node_matrix_swap": NODE_MT_compositor_utilities_matrix_base,
|
||||
"NODE_MT_compositor_node_bundle_swap": NODE_MT_compositor_utilities_bundle_base,
|
||||
"NODE_MT_compositor_node_rotation_swap": NODE_MT_compositor_node_rotation_base,
|
||||
"NODE_MT_compositor_node_swap_all": NODE_MT_compositor_node_all_base,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ set(INC
|
|||
cached_resources
|
||||
derived_resources
|
||||
utilities
|
||||
../functions
|
||||
../nodes/geometry/include
|
||||
../gpu/intern
|
||||
../makesrna
|
||||
|
|
@ -19,6 +20,7 @@ set(INC_SYS
|
|||
)
|
||||
|
||||
set(SRC
|
||||
COM_bundle_item.hh
|
||||
COM_compile_state.hh
|
||||
COM_context.hh
|
||||
COM_conversion_operation.hh
|
||||
|
|
@ -47,6 +49,7 @@ set(SRC
|
|||
COM_undefined_node_operation.hh
|
||||
COM_utilities.hh
|
||||
|
||||
intern/bundle_item.cc
|
||||
intern/compile_state.cc
|
||||
intern/context.cc
|
||||
intern/conversion_operation.cc
|
||||
|
|
|
|||
44
source/blender/compositor/COM_bundle_item.hh
Normal file
44
source/blender/compositor/COM_bundle_item.hh
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_bundle.hh"
|
||||
|
||||
#include "COM_result.hh"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class Context;
|
||||
|
||||
/* A thin wrapper around a compositor result to be added and retrieved from bundles. */
|
||||
class BundleItem : public nodes::BundleItemInternalValueMixin {
|
||||
private:
|
||||
Result result_;
|
||||
|
||||
public:
|
||||
/* Constructs the bundle item by shallow copying the given result to the result_ member. */
|
||||
BundleItem(Context &context, Result &result);
|
||||
|
||||
/* Constructs a new BundleItemValue to be inserted into a bundle, which is essentially a wrapper
|
||||
* around the constructor. */
|
||||
static nodes::BundleItemValue new_bundle_item_value(Context &context, Result &result);
|
||||
|
||||
/* Get a copy of the result stored inside the bundle item given a bundle item value. Releasing
|
||||
* the returned result is the caller's responsibility. */
|
||||
static Result get_result(Context &context, const nodes::BundleItemValue &item_value);
|
||||
|
||||
/* A name for logging. */
|
||||
StringRefNull type_name() const override;
|
||||
|
||||
private:
|
||||
/* Release the stored result and delete self. */
|
||||
void delete_self() override;
|
||||
};
|
||||
|
||||
using BundleItemPtr = ImplicitSharingPtr<BundleItem>;
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_bundle.hh"
|
||||
#include "NOD_menu_value.hh"
|
||||
|
||||
#include "COM_domain.hh"
|
||||
|
|
@ -71,6 +72,7 @@ enum class ResultType : uint8_t {
|
|||
Scene,
|
||||
Text,
|
||||
Mask,
|
||||
Bundle,
|
||||
};
|
||||
|
||||
/* The precision of the data. CPU data is always stored using full precision at the moment. */
|
||||
|
|
@ -179,7 +181,8 @@ class Result {
|
|||
VFont *,
|
||||
Scene *,
|
||||
Text *,
|
||||
Mask *>
|
||||
Mask *,
|
||||
nodes::BundlePtr>
|
||||
single_value_ = 0.0f;
|
||||
/* The domain of the result. This only matters if the result was not a single value. See the
|
||||
* discussion in COM_domain.hh for more information. */
|
||||
|
|
|
|||
59
source/blender/compositor/intern/bundle_item.cc
Normal file
59
source/blender/compositor/intern/bundle_item.cc
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "COM_bundle_item.hh"
|
||||
#include "COM_context.hh"
|
||||
#include "COM_result.hh"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
BundleItem::BundleItem(Context &context, Result &result)
|
||||
: result_(context.create_result(result.type(), result.precision()))
|
||||
{
|
||||
result_.share_data(result);
|
||||
}
|
||||
|
||||
nodes::BundleItemValue BundleItem::new_bundle_item_value(Context &context, Result &result)
|
||||
{
|
||||
return nodes::BundleItemValue{nodes::BundleItemInternalValue{
|
||||
ImplicitSharingPtr<const BundleItem>(new BundleItem(context, result))}};
|
||||
}
|
||||
|
||||
Result BundleItem::get_result(Context &context, const nodes::BundleItemValue &item_value)
|
||||
{
|
||||
/* Handle a special case where nested bundles might be stored in BundleItemSocketValue as
|
||||
* opposed to BundleItemInternalValue. */
|
||||
std::optional<nodes::BundlePtr> child_bundle = item_value.as<nodes::BundlePtr>();
|
||||
if (child_bundle.has_value()) {
|
||||
Result result = context.create_result(ResultType::Bundle);
|
||||
result.allocate_single_value();
|
||||
result.set_single_value(child_bundle.value());
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Otherwise, the item value is stored in a BundleItemInternalValue. */
|
||||
BLI_assert(std::get_if<nodes::BundleItemInternalValue>(&item_value.value));
|
||||
std::optional<BundleItemPtr> bundle_item = item_value.as<BundleItemPtr>();
|
||||
BLI_assert(bundle_item.has_value());
|
||||
|
||||
const Result &bundle_result = bundle_item.value()->result_;
|
||||
Result result = context.create_result(bundle_result.type(), bundle_result.precision());
|
||||
result.share_data(bundle_result);
|
||||
return result;
|
||||
}
|
||||
|
||||
StringRefNull BundleItem::type_name() const
|
||||
{
|
||||
return "Compositor Bundle Item";
|
||||
}
|
||||
|
||||
void BundleItem::delete_self()
|
||||
{
|
||||
result_.release();
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
|
@ -330,6 +330,10 @@ mf::Variable *MultiFunctionProcedureOperation::get_constant_input_variable(
|
|||
constant_function = &procedure_.construct_function<mf::CustomMF_Constant<Mask *>>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_BUNDLE:
|
||||
/* Not supported in multi-function nodes. */
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "NOD_eval_log.hh"
|
||||
|
||||
#include "COM_algorithm_compute_preview.hh"
|
||||
#include "COM_bundle_item.hh"
|
||||
#include "COM_context.hh"
|
||||
#include "COM_input_descriptor.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
|
@ -120,6 +121,18 @@ void NodeOperation::set_needs_node_previews(const bool needed)
|
|||
needs_node_previews_ = needed;
|
||||
}
|
||||
|
||||
void NodeOperation::add_warning(nodes::NodeWarningType type, std::string message)
|
||||
{
|
||||
nodes::eval_log::NodesEvalLog *log = this->context().nodes_evaluation_log();
|
||||
if (!log) {
|
||||
return;
|
||||
}
|
||||
nodes::eval_log::NodeTreeLogger &tree_logger = log->get_local_tree_logger(
|
||||
this->get_compute_context());
|
||||
tree_logger.node_warnings.append(*tree_logger.allocator,
|
||||
{this->node().identifier, {type, message}});
|
||||
}
|
||||
|
||||
static destruct_ptr<nodes::eval_log::ImageInfoLog> get_image_info_log(LinearAllocator<> *allocator,
|
||||
const Result &result)
|
||||
{
|
||||
|
|
@ -135,16 +148,16 @@ static destruct_ptr<nodes::eval_log::ImageInfoLog> get_image_info_log(LinearAllo
|
|||
to_string(result.precision()));
|
||||
}
|
||||
|
||||
void NodeOperation::add_warning(nodes::NodeWarningType type, std::string message)
|
||||
static destruct_ptr<nodes::eval_log::BundleValueLog> get_bundle_info_log(
|
||||
Context &context, LinearAllocator<> *allocator, const Result &result)
|
||||
{
|
||||
nodes::eval_log::NodesEvalLog *log = this->context().nodes_evaluation_log();
|
||||
if (!log) {
|
||||
return;
|
||||
Vector<nodes::eval_log::BundleValueLog::Item> items;
|
||||
for (const auto &item : result.get_single_value<nodes::BundlePtr>()->items()) {
|
||||
Result bundle_result = BundleItem::get_result(context, item.value);
|
||||
BLI_SCOPED_DEFER([&]() { bundle_result.release(); });
|
||||
items.append({item.key.ustr(), {Result::type_name(bundle_result.type())}});
|
||||
}
|
||||
nodes::eval_log::NodeTreeLogger &tree_logger = log->get_local_tree_logger(
|
||||
this->get_compute_context());
|
||||
tree_logger.node_warnings.append(*tree_logger.allocator,
|
||||
{this->node().identifier, {type, message}});
|
||||
return allocator->construct<nodes::eval_log::BundleValueLog>(std::move(items));
|
||||
}
|
||||
|
||||
void NodeOperation::log_data()
|
||||
|
|
@ -168,6 +181,14 @@ void NodeOperation::log_data()
|
|||
|
||||
const Result &input = this->get_input(input_socket->identifier);
|
||||
if (input.is_single_value()) {
|
||||
if (input.type() == ResultType::Bundle) {
|
||||
tree_logger.input_socket_values.append(
|
||||
*tree_logger.allocator,
|
||||
{node_.identifier,
|
||||
input_socket->index(),
|
||||
get_bundle_info_log(this->context(), tree_logger.allocator, input)});
|
||||
continue;
|
||||
}
|
||||
tree_logger.log_value(this->node(), *input_socket, input.single_value());
|
||||
continue;
|
||||
}
|
||||
|
|
@ -190,6 +211,14 @@ void NodeOperation::log_data()
|
|||
}
|
||||
|
||||
if (result.is_single_value()) {
|
||||
if (result.type() == ResultType::Bundle) {
|
||||
tree_logger.output_socket_values.append(
|
||||
*tree_logger.allocator,
|
||||
{node_.identifier,
|
||||
output_socket->index(),
|
||||
get_bundle_info_log(this->context(), tree_logger.allocator, result)});
|
||||
continue;
|
||||
}
|
||||
tree_logger.log_value(this->node(), *output_socket, result.single_value());
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ const char *RealizeOnDomainOperation::get_realization_shader_name()
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(this->get_input().type()));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -226,6 +227,7 @@ const char *RealizeOnDomainOperation::get_realization_shader_name()
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(this->get_input().type()));
|
||||
BLI_assert_unreachable();
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
#include "GPU_texture.hh"
|
||||
#include "GPU_texture_pool.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_bundle.hh"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_derived_resources.hh"
|
||||
#include "COM_domain.hh"
|
||||
|
|
@ -67,6 +69,7 @@ bool Result::is_single_value_only_type(ResultType type)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +122,7 @@ gpu::TextureFormat Result::gpu_texture_format(ResultType type, ResultPrecision p
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -167,6 +171,7 @@ gpu::TextureFormat Result::gpu_texture_format(ResultType type, ResultPrecision p
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU storage. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -204,6 +209,7 @@ eGPUDataFormat Result::gpu_data_format(ResultType type)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU storage. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -398,6 +404,8 @@ const CPPType &Result::cpp_type(const ResultType type)
|
|||
return CPPType::get<Text *>();
|
||||
case ResultType::Mask:
|
||||
return CPPType::get<Mask *>();
|
||||
case ResultType::Bundle:
|
||||
return CPPType::get<nodes::BundlePtr>();
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -447,6 +455,8 @@ const char *Result::type_name(const ResultType type)
|
|||
return "text";
|
||||
case ResultType::Mask:
|
||||
return "mask";
|
||||
case ResultType::Bundle:
|
||||
return "bundle";
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -580,6 +590,9 @@ void Result::allocate_single_value()
|
|||
case ResultType::Mask:
|
||||
this->set_single_value(static_cast<Mask *>(nullptr));
|
||||
break;
|
||||
case ResultType::Bundle:
|
||||
this->set_single_value(nodes::Bundle::create());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -657,6 +670,7 @@ Result Result::upload_to_gpu(const bool from_pool) const
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU. */
|
||||
break;
|
||||
}
|
||||
|
|
@ -749,6 +763,7 @@ Result Result::download_to_cpu() const
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU. */
|
||||
break;
|
||||
}
|
||||
|
|
@ -907,6 +922,8 @@ void Result::free()
|
|||
delete derived_resources_;
|
||||
derived_resources_ = nullptr;
|
||||
|
||||
single_value_ = {};
|
||||
|
||||
sharing_info_ = {};
|
||||
switch (storage_type_) {
|
||||
case ResultStorageType::GPU:
|
||||
|
|
@ -1009,6 +1026,7 @@ int64_t Result::channels_count() const
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not have channels. */
|
||||
BLI_assert(Result::is_single_value_only_type(type_));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -1087,6 +1105,7 @@ void Result::update_single_value_data()
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU storage. */
|
||||
BLI_assert(Result::is_single_value_only_type(this->type()));
|
||||
BLI_assert_unreachable();
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ static GPUType gpu_type_from_socket(const bNodeSocket &socket)
|
|||
case SOCK_SCENE:
|
||||
case SOCK_TEXT_ID:
|
||||
case SOCK_MASK:
|
||||
case SOCK_BUNDLE:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(get_node_socket_result_type(&socket)));
|
||||
BLI_assert_unreachable();
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ static const char *get_set_function_name(const ResultType type)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -330,6 +331,7 @@ static GPUNodeLink *get_input_value_link(const bNodeSocket &input, GPUNodeStack
|
|||
case SOCK_SCENE:
|
||||
case SOCK_TEXT_ID:
|
||||
case SOCK_MASK:
|
||||
case SOCK_BUNDLE:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(get_node_socket_result_type(&input)));
|
||||
break;
|
||||
|
|
@ -461,6 +463,7 @@ static GPUNodeLink *get_result_single_value_link(const Result &result)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(result.type()));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -624,6 +627,7 @@ static const char *get_store_function_name(ResultType type)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -691,6 +695,7 @@ static GPUNodeLink *get_default_input_value_link(const ResultType type)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -850,6 +855,7 @@ static const char *glsl_store_expression_from_result_type(ResultType type)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -886,6 +892,7 @@ static ImageType gpu_image_type_from_result_type(const ResultType type)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -1027,6 +1034,7 @@ std::string ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(result.type()));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -1093,6 +1101,7 @@ static const char *glsl_type_from_result_type(ResultType type)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
@ -1141,6 +1150,7 @@ static const char *glsl_swizzle_from_result_type(ResultType type)
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Single only types do not support GPU code path. */
|
||||
BLI_assert(Result::is_single_value_only_type(type));
|
||||
BLI_assert_unreachable();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_bundle.hh"
|
||||
|
||||
#include "COM_operation.hh"
|
||||
#include "COM_result.hh"
|
||||
#include "COM_single_value_node_input_operation.hh"
|
||||
|
|
@ -148,6 +150,10 @@ void SingleValueNodeInputOperation::execute()
|
|||
result.set_single_value(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_BUNDLE: {
|
||||
result.set_single_value(nodes::Bundle::create());
|
||||
break;
|
||||
}
|
||||
case SOCK_CUSTOM:
|
||||
/* An undefined socket, its value does not matter. */
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@ ResultType socket_data_type_to_result_type(const eNodeSocketDatatype data_type,
|
|||
return ResultType::Text;
|
||||
case SOCK_MASK:
|
||||
return ResultType::Mask;
|
||||
case SOCK_BUNDLE:
|
||||
return ResultType::Bundle;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return ResultType::Float;
|
||||
|
|
|
|||
|
|
@ -120,7 +120,8 @@ static bool composite_node_tree_socket_type_valid(bke::bNodeTreeType * /*ntreety
|
|||
SOCK_ROTATION,
|
||||
SOCK_STRING,
|
||||
SOCK_OBJECT,
|
||||
SOCK_FONT);
|
||||
SOCK_FONT,
|
||||
SOCK_BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -647,6 +647,7 @@ class FileOutputOperation : public NodeOperation {
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Not supported. */
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
|
|
@ -697,6 +698,7 @@ class FileOutputOperation : public NodeOperation {
|
|||
case ResultType::Scene:
|
||||
case ResultType::Text:
|
||||
case ResultType::Mask:
|
||||
case ResultType::Bundle:
|
||||
/* Not supported. */
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
#include "UI_interface_layout.hh"
|
||||
|
||||
#include "COM_bundle_item.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
#include "shader/node_shader_util.hh"
|
||||
|
||||
|
|
@ -166,6 +169,39 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
params.set_output("Bundle"_ustr, std::move(bundle_ptr));
|
||||
}
|
||||
|
||||
using namespace blender::compositor;
|
||||
|
||||
class CombineBundleOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
BundlePtr bundle_ptr = Bundle::create();
|
||||
Bundle &bundle = bundle_ptr.ensure_mutable_inplace();
|
||||
|
||||
const NodeCombineBundle &storage = node_storage(this->node());
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
const StringRef name = storage.items[i].name;
|
||||
const std::optional<BundleKey> key = BundleKey::from_str(name);
|
||||
if (!key) {
|
||||
continue;
|
||||
}
|
||||
Result &input = this->get_input(this->node().input_socket(i).identifier);
|
||||
bundle.add(*key, BundleItem::new_bundle_item_value(this->context(), input));
|
||||
}
|
||||
|
||||
Result &output = this->get_result("Bundle");
|
||||
output.allocate_single_value();
|
||||
output.set_single_value(std::move(bundle_ptr));
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, const bNode &node)
|
||||
{
|
||||
return new CombineBundleOperation(context, node);
|
||||
}
|
||||
|
||||
static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
const bNodeSocket &other_socket = params.other_socket();
|
||||
|
|
@ -211,7 +247,7 @@ static void node_register()
|
|||
{
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_geo_node_type_base(&ntype, "NodeCombineBundle"_ustr, NODE_COMBINE_BUNDLE);
|
||||
common_node_type_base(&ntype, "NodeCombineBundle"_ustr, NODE_COMBINE_BUNDLE);
|
||||
ntype.ui_name = "Combine Bundle";
|
||||
ntype.ui_description = "Combine multiple socket values into one.";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
|
|
@ -224,6 +260,7 @@ static void node_register()
|
|||
ntype.register_operators = node_operators;
|
||||
ntype.blend_write_storage_content = node_blend_write;
|
||||
ntype.blend_data_read_storage_content = node_blend_read;
|
||||
ntype.get_compositor_operation = get_compositor_operation;
|
||||
bke::node_type_storage(ntype, "NodeCombineBundle", node_free_storage, node_copy_storage);
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@
|
|||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BKE_type_conversions.hh"
|
||||
|
||||
#include "COM_bundle_item.hh"
|
||||
#include "COM_conversion_operation.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace blender::nodes::node_geo_get_bundle_item_cc {
|
||||
|
|
@ -134,6 +140,99 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
params.set_output("Exists"_ustr, true);
|
||||
}
|
||||
|
||||
using namespace blender::compositor;
|
||||
|
||||
class GetBundleItemOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
Result &bundle_output = this->get_result("Bundle");
|
||||
const Result &bundle_input = this->get_input("Bundle");
|
||||
|
||||
const std::string path = this->get_input("Path").get_single_value<std::string>();
|
||||
const std::optional<Vector<BundleKey>> split_path = Bundle::split_path(path);
|
||||
if (!split_path.has_value()) {
|
||||
if (!path.empty()) {
|
||||
this->add_warning(NodeWarningType::Warning, "Invalid bundle path");
|
||||
}
|
||||
if (bundle_output.should_compute()) {
|
||||
bundle_output.share_data(bundle_input);
|
||||
}
|
||||
this->allocate_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
|
||||
Result &item_output = this->get_result("Item");
|
||||
Result &exists_output = this->get_result("Exists");
|
||||
|
||||
nodes::BundlePtr bundle = bundle_input.get_single_value<nodes::BundlePtr>();
|
||||
|
||||
const BundleItemValue *item_value = bundle->lookup_path(split_path.value());
|
||||
if (!item_value) {
|
||||
if (bundle_output.should_compute()) {
|
||||
bundle_output.share_data(bundle_input);
|
||||
}
|
||||
if (exists_output.should_compute()) {
|
||||
exists_output.allocate_single_value();
|
||||
exists_output.set_single_value(false);
|
||||
}
|
||||
else {
|
||||
this->add_warning(NodeWarningType::Warning, "Bundle path not found");
|
||||
}
|
||||
this->allocate_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
|
||||
Result bundle_result = BundleItem::get_result(this->context(), *item_value);
|
||||
BLI_SCOPED_DEFER([&]() { bundle_result.release(); });
|
||||
|
||||
if (item_output.should_compute()) {
|
||||
if (item_output.type() == bundle_result.type()) {
|
||||
item_output.share_data(bundle_result);
|
||||
}
|
||||
else {
|
||||
const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
|
||||
if (conversions.is_convertible(bundle_result.get_cpp_type(), item_output.get_cpp_type())) {
|
||||
ConversionOperation conversion_operation(
|
||||
this->context(), bundle_result.type(), item_output.type());
|
||||
Result conversion_input = this->context().create_result(bundle_result.type(),
|
||||
bundle_result.precision());
|
||||
conversion_input.share_data(bundle_result);
|
||||
conversion_operation.map_input_to_result(&conversion_input);
|
||||
conversion_operation.evaluate();
|
||||
item_output.share_data(conversion_operation.get_result());
|
||||
conversion_operation.get_result().release();
|
||||
}
|
||||
else {
|
||||
item_output.allocate_invalid();
|
||||
this->add_warning(NodeWarningType::Error,
|
||||
"Cannot implicitly convert item to the selected type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle_output.should_compute()) {
|
||||
if (this->get_input("Remove").get_single_value<bool>()) {
|
||||
bundle.ensure_mutable_inplace().remove_path(split_path.value());
|
||||
}
|
||||
bundle_output.allocate_single_value();
|
||||
bundle_output.set_single_value(std::move(bundle));
|
||||
}
|
||||
|
||||
if (exists_output.should_compute()) {
|
||||
exists_output.allocate_single_value();
|
||||
exists_output.set_single_value(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, const bNode &node)
|
||||
{
|
||||
return new GetBundleItemOperation(context, node);
|
||||
}
|
||||
|
||||
static void node_rna(StructRNA *srna)
|
||||
{
|
||||
RNA_def_node_enum(srna,
|
||||
|
|
@ -164,7 +263,7 @@ static void node_register()
|
|||
{
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
geo_node_type_base(&ntype, "NodeGetBundleItem"_ustr);
|
||||
geo_cmp_node_type_base(&ntype, "NodeGetBundleItem"_ustr);
|
||||
ntype.ui_name = "Get Bundle Item";
|
||||
ntype.ui_description = "Retrieve a bundle item by path.";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
|
|
@ -173,6 +272,7 @@ static void node_register()
|
|||
ntype.geometry_node_execute = node_geo_exec;
|
||||
ntype.draw_buttons = node_layout;
|
||||
ntype.draw_buttons_ex = node_layout_ex;
|
||||
ntype.get_compositor_operation = get_compositor_operation;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeGetBundleItem", node_free_standard_storage, node_copy_standard_storage);
|
||||
bke::node_register_type(ntype);
|
||||
|
|
|
|||
|
|
@ -15,9 +15,14 @@
|
|||
#include "NOD_sync_sockets.hh"
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_type_conversions.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "COM_bundle_item.hh"
|
||||
#include "COM_conversion_operation.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "shader/node_shader_util.hh"
|
||||
|
||||
|
|
@ -193,6 +198,85 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
params.set_default_remaining_outputs();
|
||||
}
|
||||
|
||||
using namespace blender::compositor;
|
||||
|
||||
class SeparateBundleOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
nodes::BundlePtr bundle = this->get_input("Bundle").get_single_value<nodes::BundlePtr>();
|
||||
|
||||
const NodeSeparateBundle &storage = node_storage(this->node());
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
Result &result = this->get_result(this->node().output_socket(i).identifier);
|
||||
if (!result.should_compute()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const NodeSeparateBundleItem &item = storage.items[i];
|
||||
const StringRef name = item.name;
|
||||
std::optional<BundleKey> key = BundleKey::from_str(name);
|
||||
if (!key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const BundleItemValue *item_value = bundle->lookup(key.value());
|
||||
if (!item_value) {
|
||||
this->add_warning(
|
||||
NodeWarningType::Error,
|
||||
fmt::format(fmt::runtime(TIP_("Value not found in bundle: \"{}\"")), name));
|
||||
continue;
|
||||
}
|
||||
|
||||
Result bundle_result = BundleItem::get_result(this->context(), *item_value);
|
||||
BLI_SCOPED_DEFER([&]() { bundle_result.release(); });
|
||||
if (result.type() == bundle_result.type()) {
|
||||
result.share_data(bundle_result);
|
||||
continue;
|
||||
}
|
||||
|
||||
const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
|
||||
if (!conversions.is_convertible(bundle_result.get_cpp_type(), result.get_cpp_type())) {
|
||||
this->add_warning(
|
||||
NodeWarningType::Error,
|
||||
fmt::format("{}: \"{}\" ({} " BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE " {})",
|
||||
TIP_("Conversion not supported when separating bundle"),
|
||||
name,
|
||||
TIP_(Result::type_name(bundle_result.type())),
|
||||
TIP_(Result::type_name(result.type()))));
|
||||
continue;
|
||||
}
|
||||
|
||||
ConversionOperation conversion_operation(
|
||||
this->context(), bundle_result.type(), result.type());
|
||||
Result conversion_input = this->context().create_result(bundle_result.type(),
|
||||
bundle_result.precision());
|
||||
conversion_input.share_data(bundle_result);
|
||||
conversion_operation.map_input_to_result(&conversion_input);
|
||||
conversion_operation.evaluate();
|
||||
result.share_data(conversion_operation.get_result());
|
||||
conversion_operation.get_result().release();
|
||||
|
||||
this->add_warning(
|
||||
NodeWarningType::Info,
|
||||
fmt::format("{}: \"{}\" ({} " BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE " {})",
|
||||
TIP_("Implicit type conversion when separating bundle"),
|
||||
name,
|
||||
TIP_(Result::type_name(bundle_result.type())),
|
||||
TIP_(Result::type_name(result.type()))));
|
||||
}
|
||||
|
||||
this->allocate_default_remaining_outputs();
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, const bNode &node)
|
||||
{
|
||||
return new SeparateBundleOperation(context, node);
|
||||
}
|
||||
|
||||
static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
const bNodeSocket &other_socket = params.other_socket();
|
||||
|
|
@ -238,7 +322,7 @@ static void node_register()
|
|||
{
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
sh_geo_node_type_base(&ntype, "NodeSeparateBundle"_ustr, NODE_SEPARATE_BUNDLE);
|
||||
common_node_type_base(&ntype, "NodeSeparateBundle"_ustr, NODE_SEPARATE_BUNDLE);
|
||||
ntype.ui_name = "Separate Bundle";
|
||||
ntype.ui_description = "Split a bundle into multiple sockets.";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
|
|
@ -251,6 +335,7 @@ static void node_register()
|
|||
ntype.register_operators = node_operators;
|
||||
ntype.blend_write_storage_content = node_blend_write;
|
||||
ntype.blend_data_read_storage_content = node_blend_read;
|
||||
ntype.get_compositor_operation = get_compositor_operation;
|
||||
bke::node_type_storage(ntype, "NodeSeparateBundle", node_free_storage, node_copy_storage);
|
||||
bke::node_register_type(ntype);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@
|
|||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "COM_bundle_item.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_store_bundle_item_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeStoreBundleItem)
|
||||
|
|
@ -102,6 +105,43 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
params.set_output("Bundle"_ustr, std::move(bundle_ptr));
|
||||
}
|
||||
|
||||
using namespace blender::compositor;
|
||||
|
||||
class StoreBundleItemOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
const Result &input_bundle = this->get_input("Bundle");
|
||||
BundlePtr bundle_ptr = input_bundle.get_single_value<nodes::BundlePtr>();
|
||||
Bundle &bundle = bundle_ptr.ensure_mutable_inplace();
|
||||
|
||||
Result &output_bundle = this->get_result("Bundle");
|
||||
|
||||
const std::string path = this->get_input("Path").get_single_value<std::string>();
|
||||
const std::optional<Vector<BundleKey>> split_path = Bundle::split_path(path);
|
||||
if (!split_path.has_value()) {
|
||||
if (!path.empty()) {
|
||||
this->add_warning(NodeWarningType::Warning, "Invalid bundle path");
|
||||
}
|
||||
output_bundle.share_data(input_bundle);
|
||||
return;
|
||||
}
|
||||
|
||||
Result &input_item = this->get_input("Item");
|
||||
bundle.add_path_override(*split_path,
|
||||
BundleItem::new_bundle_item_value(this->context(), input_item));
|
||||
output_bundle.allocate_single_value();
|
||||
output_bundle.set_single_value(bundle_ptr);
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, const bNode &node)
|
||||
{
|
||||
return new StoreBundleItemOperation(context, node);
|
||||
}
|
||||
|
||||
static void node_rna(StructRNA *srna)
|
||||
{
|
||||
RNA_def_node_enum(srna,
|
||||
|
|
@ -132,7 +172,7 @@ static void node_register()
|
|||
{
|
||||
static bke::bNodeType ntype;
|
||||
|
||||
geo_node_type_base(&ntype, "NodeStoreBundleItem"_ustr);
|
||||
geo_cmp_node_type_base(&ntype, "NodeStoreBundleItem"_ustr);
|
||||
ntype.ui_name = "Store Bundle Item";
|
||||
ntype.ui_description = "Store a bundle item by path and data type.";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
|
|
@ -141,6 +181,7 @@ static void node_register()
|
|||
ntype.geometry_node_execute = node_geo_exec;
|
||||
ntype.draw_buttons = node_layout;
|
||||
ntype.draw_buttons_ex = node_layout_ex;
|
||||
ntype.get_compositor_operation = get_compositor_operation;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeStoreBundleItem", node_free_standard_storage, node_copy_standard_storage);
|
||||
bke::node_register_type(ntype);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ set(INC
|
|||
../compositor
|
||||
../compositor/cached_resources
|
||||
../compositor/derived_resources
|
||||
../functions
|
||||
../draw/intern
|
||||
../gpu/intern
|
||||
../makesrna
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ set(INC
|
|||
../compositor/algorithms
|
||||
../compositor/cached_resources
|
||||
../compositor/derived_resources
|
||||
../functions
|
||||
../editors/include
|
||||
../animrig
|
||||
../makesrna
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue