diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 7cd87b1f5c8..717d9785ce1 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 48 +#define BLENDER_FILE_SUBVERSION 49 /* 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 diff --git a/source/blender/blenloader/intern/versioning_500.cc b/source/blender/blenloader/intern/versioning_500.cc index eab6c122ad1..57aa32f7e7b 100644 --- a/source/blender/blenloader/intern/versioning_500.cc +++ b/source/blender/blenloader/intern/versioning_500.cc @@ -2019,6 +2019,26 @@ void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 49)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type != NTREE_COMPOSIT) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type_legacy != CMP_NODE_DISPLACE) { + continue; + } + if (node->storage == nullptr) { + continue; + } + NodeDisplaceData *data = static_cast(node->storage); + data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP; + data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP; + } + 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. diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 910ab86d229..d63d260eba9 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1595,6 +1595,8 @@ typedef struct NodeScaleData { typedef struct NodeDisplaceData { short interpolation; + char extension_x; + char extension_y; } NodeDisplaceData; typedef struct NodePlaneTrackDeformData { diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 60355be3a06..6aed63cf1f5 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -6682,6 +6682,18 @@ static void def_cmp_displace(BlenderRNA * /*brna*/, StructRNA *srna) RNA_def_property_enum_items(prop, cmp_interpolation_items); RNA_def_property_ui_text(prop, "Interpolation", "Interpolation method"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "extension_x", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "extension_x"); + RNA_def_property_enum_items(prop, cmp_extension_mode_items); + RNA_def_property_ui_text(prop, "X Extension Mode", "The extension mode applied to the X axis"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "extension_y", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "extension_y"); + RNA_def_property_enum_items(prop, cmp_extension_mode_items); + RNA_def_property_ui_text(prop, "Y Extension Mode", "The extension mode applied to the Y axis"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } static void def_cmp_scale(BlenderRNA * /*brna*/, StructRNA *srna) diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc index 668084d90d2..be75411a3e7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_displace.cc +++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc @@ -13,8 +13,10 @@ #include "BLI_assert.h" #include "BLI_math_vector.hh" #include "BLI_math_vector_types.hh" +#include "BLI_utildefines.h" #include "DNA_node_types.h" +#include "RNA_access.hh" #include "RNA_types.hh" #include "GPU_shader.hh" @@ -64,12 +66,22 @@ static void cmp_node_init_displace(bNodeTree * /*ntree*/, bNode *node) { NodeDisplaceData *data = MEM_callocN(__func__); data->interpolation = CMP_NODE_INTERPOLATION_ANISOTROPIC; + data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP; + data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP; node->storage = data; } static void cmp_buts_displace(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { - layout->prop(ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiLayout &column_interpolation_extension_modes = layout->column(true); + + column_interpolation_extension_modes.prop( + ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + if (RNA_enum_get(ptr, "interpolation") != CMP_NODE_INTERPOLATION_ANISOTROPIC) { + uiLayout &row = column_interpolation_extension_modes.row(true); + row.prop(ptr, "extension_x", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + row.prop(ptr, "extension_y", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + } } using namespace blender::compositor; @@ -98,6 +110,8 @@ class DisplaceOperation : public NodeOperation { void execute_gpu() { const Interpolation interpolation = this->get_interpolation(); + const ExtensionMode extension_x = this->get_extension_mode_x(); + const ExtensionMode extension_y = this->get_extension_mode_y(); GPUShader *shader = context().get_shader(this->get_shader_name(interpolation)); GPU_shader_bind(shader); @@ -111,7 +125,8 @@ class DisplaceOperation : public NodeOperation { interpolation, Interpolation::Bilinear, Interpolation::Bicubic); GPU_texture_filter_mode(input_image, use_bilinear); } - GPU_texture_extend_mode(input_image, GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER); + GPU_texture_extend_mode_x(input_image, map_extension_mode_to_extend_mode(extension_x)); + GPU_texture_extend_mode_y(input_image, map_extension_mode_to_extend_mode(extension_y)); input_image.bind_as_texture(shader, "input_tx"); const Result &input_displacement = get_input("Vector"); @@ -144,6 +159,8 @@ class DisplaceOperation : public NodeOperation { const Result &y_scale = get_input("Y Scale"); const Interpolation interpolation = this->get_interpolation(); + const ExtensionMode extension_x = this->get_extension_mode_x(); + const ExtensionMode extension_y = this->get_extension_mode_y(); const Domain domain = compute_domain(); Result &output = get_result("Image"); output.allocate_texture(domain); @@ -154,8 +171,15 @@ class DisplaceOperation : public NodeOperation { this->compute_anisotropic(size, image, output, input_displacement, x_scale, y_scale); } else { - this->compute_interpolation( - interpolation, size, image, output, input_displacement, x_scale, y_scale); + this->compute_interpolation(interpolation, + size, + image, + output, + input_displacement, + x_scale, + y_scale, + extension_x, + extension_y); } } @@ -165,26 +189,16 @@ class DisplaceOperation : public NodeOperation { Result &output, const Result &input_displacement, const Result &x_scale, - const Result &y_scale) const + const Result &y_scale, + const ExtensionMode &extension_mode_x, + const ExtensionMode &extension_mode_y) const { parallel_for(size, [&](const int2 base_texel) { const float2 coordinates = compute_coordinates( base_texel, size, input_displacement, x_scale, y_scale); - switch (interpolation) { - /* The anisotropic case requires gradient computation and is handled separately. */ - case Interpolation::Anisotropic: - BLI_assert_unreachable(); - break; - case Interpolation::Nearest: - output.store_pixel(base_texel, image.sample_nearest_zero(coordinates)); - break; - case Interpolation::Bilinear: - output.store_pixel(base_texel, image.sample_bilinear_zero(coordinates)); - break; - case Interpolation::Bicubic: - output.store_pixel(base_texel, image.sample_cubic_wrap(coordinates, false, false)); - break; - } + output.store_pixel( + base_texel, + image.sample(coordinates, interpolation, extension_mode_x, extension_mode_y)); }); } @@ -307,6 +321,36 @@ class DisplaceOperation : public NodeOperation { return Interpolation::Nearest; } + ExtensionMode get_extension_mode_x() + { + switch (static_cast(node_storage(bnode()).extension_x)) { + case CMP_NODE_EXTENSION_MODE_CLIP: + return ExtensionMode::Clip; + case CMP_NODE_EXTENSION_MODE_REPEAT: + return ExtensionMode::Repeat; + case CMP_NODE_EXTENSION_MODE_EXTEND: + return ExtensionMode::Extend; + } + + BLI_assert_unreachable(); + return ExtensionMode::Clip; + } + + ExtensionMode get_extension_mode_y() + { + switch (static_cast(node_storage(bnode()).extension_y)) { + case CMP_NODE_EXTENSION_MODE_CLIP: + return ExtensionMode::Clip; + case CMP_NODE_EXTENSION_MODE_REPEAT: + return ExtensionMode::Repeat; + case CMP_NODE_EXTENSION_MODE_EXTEND: + return ExtensionMode::Extend; + } + + BLI_assert_unreachable(); + return ExtensionMode::Clip; + } + bool is_identity() { const Result &input_image = get_input("Image");