Compositor: Turn Vector Blur node options to inputs

This patch turns the options of the Vector Blur node into inputs.

In the process, the factor input was renamed to Shutter and was remapped
to match the shutter in Cycles/EEVEE.

Reference #137223.

Pull Request: https://projects.blender.org/blender/blender/pulls/137725
This commit is contained in:
Omar Emara
2025-04-18 15:09:27 +02:00
committed by Omar Emara
parent 3933f45f52
commit e115467f8a
5 changed files with 173 additions and 35 deletions

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 36
#define BLENDER_FILE_SUBVERSION 37
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -698,6 +698,11 @@ static void write_compositor_legacy_properties(bNodeTree &node_tree)
property = input->default_value_typed<bNodeSocketValueInt>()->value;
};
auto write_input_to_property_short = [&](const char *identifier, short &property) {
const bNodeSocket *input = blender::bke::node_find_socket(*node, SOCK_IN, identifier);
property = input->default_value_typed<bNodeSocketValueInt>()->value;
};
auto write_input_to_property_int16 = [&](const char *identifier, int16_t &property) {
const bNodeSocket *input = blender::bke::node_find_socket(*node, SOCK_IN, identifier);
property = int16_t(input->default_value_typed<bNodeSocketValueInt>()->value);
@@ -809,6 +814,15 @@ static void write_compositor_legacy_properties(bNodeTree &node_tree)
const bNodeSocket *input = blender::bke::node_find_socket(*node, SOCK_IN, "Contrast Limit");
storage->contrast_limit = input->default_value_typed<bNodeSocketValueFloat>()->value / 10.0f;
}
if (node->type_legacy == CMP_NODE_VECBLUR) {
NodeBlurData *storage = static_cast<NodeBlurData *>(node->storage);
write_input_to_property_short("Samples", storage->samples);
/* Shutter was previously divided by 2. */
const bNodeSocket *input = blender::bke::node_find_socket(*node, SOCK_IN, "Shutter");
storage->fac = input->default_value_typed<bNodeSocketValueFloat>()->value / 2.0f;
}
}
}

View File

@@ -2686,6 +2686,64 @@ static void do_version_anti_alias_node_options_to_inputs_animation(bNodeTree *no
});
}
/* The options were converted into inputs. */
static void do_version_vector_blur_node_options_to_inputs(bNodeTree *node_tree, bNode *node)
{
NodeBlurData *storage = static_cast<NodeBlurData *>(node->storage);
if (!storage) {
return;
}
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Samples")) {
bNodeSocket *input = blender::bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_INT, PROP_NONE, "Samples", "Samples");
input->default_value_typed<bNodeSocketValueInt>()->value = storage->samples;
}
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Shutter")) {
bNodeSocket *input = blender::bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_NONE, "Shutter", "Shutter");
/* Shutter was previously divided by 2. */
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->fac * 2.0f;
}
}
/* The options were converted into inputs. */
static void do_version_vector_blur_node_options_to_inputs_animation(bNodeTree *node_tree,
bNode *node)
{
/* Compute the RNA path of the node. */
char escaped_node_name[sizeof(node->name) * 2 + 1];
BLI_str_escape(escaped_node_name, node->name, sizeof(escaped_node_name));
const std::string node_rna_path = fmt::format("nodes[\"{}\"]", escaped_node_name);
BKE_fcurves_id_cb(&node_tree->id, [&](ID * /*id*/, FCurve *fcurve) {
/* The FCurve does not belong to the node since its RNA path doesn't start with the node's RNA
* path. */
if (!blender::StringRef(fcurve->rna_path).startswith(node_rna_path)) {
return;
}
/* Change the RNA path of the FCurve from the old properties to the new inputs, adjusting the
* values of the FCurves frames when needed. */
char *old_rna_path = fcurve->rna_path;
if (BLI_str_endswith(fcurve->rna_path, "samples")) {
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[3].default_value");
}
else if (BLI_str_endswith(fcurve->rna_path, "factor")) {
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[4].default_value");
/* Shutter was previously divided by 2. */
adjust_fcurve_key_frame_values(
fcurve, PROP_FLOAT, [&](const float value) { return value * 2.0f; });
}
/* The RNA path was changed, free the old path. */
if (fcurve->rna_path != old_rna_path) {
MEM_freeN(old_rna_path);
}
});
}
static void do_version_viewer_shortcut(bNodeTree *node_tree)
{
LISTBASE_FOREACH_MUTABLE (bNode *, node, &node_tree->nodes) {
@@ -3322,6 +3380,19 @@ void do_versions_after_linking_400(FileData *fd, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 37)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_VECBLUR) {
do_version_vector_blur_node_options_to_inputs_animation(node_tree, node);
}
}
}
}
FOREACH_NODETREE_END;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
@@ -8076,6 +8147,19 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 37)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_VECBLUR) {
do_version_vector_blur_node_options_to_inputs(node_tree, node);
}
}
}
}
FOREACH_NODETREE_END;
}
/* Always run this versioning (keep at the bottom of the function). Meshes are written with the
* legacy format which always needs to be converted to the new format on file load. To be moved
* to a subversion check in 5.0. */

View File

@@ -3827,6 +3827,26 @@ static void rna_NodeAntiAlias_contrast_limit_set(PointerRNA *ptr, const float va
RNA_float_set(&input_rna_pointer, "default_value", value * 10.0f);
}
static float rna_NodeVectorBlur_shutter_get(PointerRNA *ptr)
{
bNode *node = static_cast<bNode *>(ptr->data);
bNodeSocket *input = blender::bke::node_find_socket(*node, SOCK_IN, "Shutter");
PointerRNA input_rna_pointer = RNA_pointer_create_discrete(
ptr->owner_id, &RNA_NodeSocket, input);
/* Shutter was previously divided by 2. */
return RNA_float_get(&input_rna_pointer, "default_value") / 2.0f;
}
static void rna_NodeVectorBlur_shutter_set(PointerRNA *ptr, const float value)
{
bNode *node = static_cast<bNode *>(ptr->data);
bNodeSocket *input = blender::bke::node_find_socket(*node, SOCK_IN, "Shutter");
PointerRNA input_rna_pointer = RNA_pointer_create_discrete(
ptr->owner_id, &RNA_NodeSocket, input);
/* Shutter was previously divided by 2. */
RNA_float_set(&input_rna_pointer, "default_value", value * 2.0f);
}
/* A getter that returns the value of the input socket with the given template identifier and type.
* The RNA pointer is assumed to represent a node. */
template<typename T, const char *identifier>
@@ -3938,9 +3958,11 @@ static const char node_input_hdr[] = "HDR";
/* Anti-Alias node. */
static const char node_input_threshold[] = "Threshold";
static const char node_input_contrast_limit[] = "Contrast Limit";
static const char node_input_corner_rounding[] = "Corner Rounding";
/* Vector Blur node. */
static const char node_input_samples[] = "Samples";
/* --------------------------------------------------------------------
* White Balance Node.
*/
@@ -7188,40 +7210,46 @@ static void def_cmp_vector_blur(BlenderRNA * /*brna*/, StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeBlurData", "storage");
prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "samples");
RNA_def_property_int_funcs(prop,
"rna_node_property_to_input_getter<int, node_input_samples>",
"rna_node_property_to_input_setter<int, node_input_samples>",
nullptr);
RNA_def_property_range(prop, 1, 256);
RNA_def_property_ui_text(prop, "Samples", "");
RNA_def_property_ui_text(prop, "Samples", "(Deprecated: Use Samples input instead.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "speed_min", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "minspeed");
RNA_def_property_range(prop, 0, 1024);
RNA_def_property_ui_text(
prop,
"Min Speed",
"Minimum speed for a pixel to be blurred (used to separate background from foreground)");
RNA_def_property_ui_text(prop,
"Min Speed",
"Minimum speed for a pixel to be blurred (used to separate background "
"from foreground). (Deprecated.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "speed_max", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "maxspeed");
RNA_def_property_range(prop, 0, 1024);
RNA_def_property_ui_text(prop, "Max Speed", "Maximum speed, or zero for none");
RNA_def_property_ui_text(prop, "Max Speed", "Maximum speed, or zero for none. (Deprecated.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "fac");
RNA_def_property_float_funcs(
prop, "rna_NodeVectorBlur_shutter_get", "rna_NodeVectorBlur_shutter_set", nullptr);
RNA_def_property_range(prop, 0.0, 20.0);
RNA_def_property_ui_range(prop, 0.0, 2.0, 1.0, 2);
RNA_def_property_ui_text(
prop,
"Blur Factor",
"Scaling factor for motion vectors (actually, 'shutter speed', in frames)");
RNA_def_property_ui_text(prop,
"Blur Factor",
"Scaling factor for motion vectors (actually, 'shutter speed', in "
"frames). (Deprecated: Use Shutter input instead.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "use_curved", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "curved", 1);
RNA_def_property_ui_text(
prop, "Curved", "Interpolate between frames in a Bézier curve, rather than linearly");
prop,
"Curved",
"Interpolate between frames in a Bézier curve, rather than linearly. (Deprecated.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}

View File

@@ -32,38 +32,39 @@
namespace blender::nodes::node_composite_vec_blur_cc {
NODE_STORAGE_FUNCS(NodeBlurData)
static void cmp_node_vec_blur_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>("Image")
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(0);
b.add_input<decl::Float>("Z").default_value(0.0f).min(0.0f).max(1.0f).compositor_domain_priority(
2);
b.add_input<decl::Float>("Z").default_value(0.0f).min(0.0f).compositor_domain_priority(2);
b.add_input<decl::Vector>("Speed")
.default_value({0.0f, 0.0f, 0.0f})
.min(0.0f)
.max(1.0f)
.subtype(PROP_VELOCITY)
.compositor_domain_priority(1);
b.add_input<decl::Int>("Samples")
.default_value(32)
.min(1)
.max(256)
.description("The number of samples used to approximate the motion blur")
.compositor_expects_single_value();
b.add_input<decl::Float>("Shutter")
.default_value(0.5f)
.min(0.0f)
.description("Time between shutter opening and closing in frames")
.compositor_expects_single_value();
b.add_output<decl::Color>("Image");
}
/* custom1: iterations, custom2: max_speed (0 = no_limit). */
static void node_composit_init_vecblur(bNodeTree * /*ntree*/, bNode *node)
{
/* All members are deprecated and needn't be set, but the data is still allocated for forward
* compatibility. */
NodeBlurData *nbd = MEM_callocN<NodeBlurData>(__func__);
node->storage = nbd;
nbd->samples = 32;
nbd->fac = 0.25f;
}
static void node_composit_buts_vecblur(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiLayout *col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "samples", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Blur"), ICON_NONE);
}
using namespace blender::compositor;
@@ -580,7 +581,7 @@ class VectorBlurOperation : public NodeOperation {
GPUShader *shader = context().get_shader("compositor_motion_blur_max_velocity_dilate");
GPU_shader_bind(shader);
GPU_shader_uniform_1f(shader, "shutter_speed", node_storage(bnode()).fac);
GPU_shader_uniform_1f(shader, "shutter_speed", this->get_shutter());
max_tile_velocity.bind_as_texture(shader, "input_tx");
@@ -608,8 +609,8 @@ class VectorBlurOperation : public NodeOperation {
GPUShader *shader = context().get_shader("compositor_motion_blur");
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "samples_count", node_storage(bnode()).samples);
GPU_shader_uniform_1f(shader, "shutter_speed", node_storage(bnode()).fac);
GPU_shader_uniform_1i(shader, "samples_count", this->get_samples_count());
GPU_shader_uniform_1f(shader, "shutter_speed", this->get_shutter());
Result &input = get_input("Image");
input.bind_as_texture(shader, "input_tx");
@@ -643,8 +644,8 @@ class VectorBlurOperation : public NodeOperation {
void execute_cpu()
{
const float shutter_speed = node_storage(bnode()).fac;
const int samples_count = node_storage(bnode()).samples;
const float shutter_speed = this->get_shutter();
const int samples_count = this->get_samples_count();
const Result &input_image = get_input("Image");
const Result &input_depth = get_input("Z");
@@ -667,6 +668,18 @@ class VectorBlurOperation : public NodeOperation {
shutter_speed);
max_velocity.release();
}
int get_samples_count()
{
return math::clamp(this->get_input("Samples").get_single_value_default(32), 1, 256);
}
float get_shutter()
{
/* Divide by two since the motion blur algorithm expects shutter per motion step and has two
* motion steps, while the user inputs the entire shutter across all steps. */
return math::max(0.0f, this->get_input("Shutter").get_single_value_default(0.5f)) / 2.0f;
}
};
static NodeOperation *get_compositor_operation(Context &context, DNode node)
@@ -688,7 +701,6 @@ void register_node_type_cmp_vecblur()
ntype.enum_name_legacy = "VECBLUR";
ntype.nclass = NODE_CLASS_OP_FILTER;
ntype.declare = file_ns::cmp_node_vec_blur_declare;
ntype.draw_buttons = file_ns::node_composit_buts_vecblur;
ntype.initfunc = file_ns::node_composit_init_vecblur;
blender::bke::node_type_storage(
ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage);