Realtime Compositor: Immediately realize transformations #112332
|
@ -67,17 +67,21 @@ set(SRC
|
|||
algorithms/intern/morphological_distance.cc
|
||||
algorithms/intern/morphological_distance_feather.cc
|
||||
algorithms/intern/parallel_reduction.cc
|
||||
algorithms/intern/realize_on_domain.cc
|
||||
algorithms/intern/smaa.cc
|
||||
algorithms/intern/summed_area_table.cc
|
||||
algorithms/intern/symmetric_separable_blur.cc
|
||||
algorithms/intern/transform.cc
|
||||
|
||||
algorithms/COM_algorithm_jump_flooding.hh
|
||||
algorithms/COM_algorithm_morphological_distance.hh
|
||||
algorithms/COM_algorithm_morphological_distance_feather.hh
|
||||
algorithms/COM_algorithm_parallel_reduction.hh
|
||||
algorithms/COM_algorithm_realize_on_domain.hh
|
||||
algorithms/COM_algorithm_smaa.hh
|
||||
algorithms/COM_algorithm_summed_area_table.hh
|
||||
algorithms/COM_algorithm_symmetric_separable_blur.hh
|
||||
algorithms/COM_algorithm_transform.hh
|
||||
|
||||
cached_resources/intern/cached_mask.cc
|
||||
cached_resources/intern/cached_texture.cc
|
||||
|
|
|
@ -16,14 +16,6 @@ namespace blender::realtime_compositor {
|
|||
struct InputRealizationOptions {
|
||||
/* The input should be realized on the operation domain of the operation. */
|
||||
bool realize_on_operation_domain : 1;
|
||||
/* The input should be realized on a domain that is identical to the domain of the input but with
|
||||
* an identity rotation and an increased size that completely fits the image after rotation. This
|
||||
* is useful for operations that are not rotation invariant. */
|
||||
bool realize_rotation : 1;
|
||||
/* The input should be realized on a domain that is identical to the domain of the input but with
|
||||
* an identity scale and an increased/decreased size that completely fits the image after
|
||||
* scaling. This is useful for operations that are not scale invariant. */
|
||||
bool realize_scale : 1;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -42,40 +42,6 @@ class RealizeOnDomainOperation : public SimpleOperation {
|
|||
protected:
|
||||
/* The operation domain is just the target domain. */
|
||||
Domain compute_domain() override;
|
||||
|
||||
private:
|
||||
/* Get the realization shader of the appropriate type. */
|
||||
GPUShader *get_realization_shader();
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Realize Transformation Operation
|
||||
*
|
||||
* A simple operation that realizes its input on a domain such that its transformations become
|
||||
* identity and its size is increased/decreased to adapt to the new domain. The transformations
|
||||
* that become identity are the ones marked to be realized in the given InputRealizationOptions.
|
||||
* For instance, if InputRealizationOptions.realize_rotation is true and the input is rotated, the
|
||||
* output of the operation will be a result of zero rotation but expanded size to account for the
|
||||
* bounding box of the input after rotation. This is useful for operations that are not rotation
|
||||
* invariant and thus require an input of zero rotation for correct operation.
|
||||
*
|
||||
* Notice that this class is not an actual operation, but constructs a RealizeOnDomainOperation
|
||||
* with the appreciate domain in its construct_if_needed static constructor. */
|
||||
class RealizeTransformationOperation {
|
||||
public:
|
||||
/* Determine if a realize transformation operation is needed for the input with the given result
|
||||
* and descriptor. If it is not needed, return a null pointer. If it is needed, return an
|
||||
* instance of RealizeOnDomainOperation with the appropriate domain. */
|
||||
static SimpleOperation *construct_if_needed(Context &context,
|
||||
const Result &input_result,
|
||||
const InputDescriptor &input_descriptor);
|
||||
|
||||
private:
|
||||
/* Given the domain of an input and its realization options, compute a domain such that the
|
||||
* appropriate transformations specified in the realization options become identity and the size
|
||||
* of the domain is increased/reduced to adapt to the new domain. */
|
||||
static Domain compute_target_domain(const Domain &input_domain,
|
||||
const InputRealizationOptions &realization_options);
|
||||
};
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_domain.hh"
|
||||
#include "COM_result.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* Projects the input on a target domain, copies the area of the input that intersects the target
|
||||
* domain, and fill the rest with zeros or repetitions of the input depending on the realization
|
||||
* options. The transformation and realization options of the input are ignored and the given
|
||||
* input_transformation and realization_options are used instead to allow the caller to change them
|
||||
* without mutating the input result directly. See the discussion in COM_domain.hh for more
|
||||
* information on what realization on domain means. */
|
||||
void realize_on_domain(Context &context,
|
||||
Result &input,
|
||||
Result &output,
|
||||
const Domain &domain,
|
||||
const float3x3 &input_transformation,
|
||||
const RealizationOptions &realization_options);
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_domain.hh"
|
||||
#include "COM_result.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* Transforms the given result based on the given transformation and interpolation, writing the
|
||||
* transformed result to the given output.
|
||||
*
|
||||
* The rotation and scale components of the transformation are realized and the size of the result
|
||||
* is increased/reduced to adapt to the new transformation. For instance, if the transformation is
|
||||
* a rotation, the input will be rotated and expanded in size to account for the bounding box of
|
||||
* the input after rotation. The size of the returned result is bound and clipped by the maximum
|
||||
* possible GPU texture size to avoid allocations that surpass hardware limits, which is typically
|
||||
* 16k.
|
||||
*
|
||||
* The translation component of the transformation is delayed and only stored in the domain of the
|
||||
* result to be realized later when needed. */
|
||||
void transform(Context &context,
|
||||
Result &input,
|
||||
Result &output,
|
||||
float3x3 transformation,
|
||||
Interpolation interpolation);
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -0,0 +1,113 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_math_angle_types.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_domain.hh"
|
||||
#include "COM_result.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "COM_algorithm_realize_on_domain.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
static const char *get_realization_shader(Result &input,
|
||||
const RealizationOptions &realization_options)
|
||||
{
|
||||
if (realization_options.interpolation == Interpolation::Bicubic) {
|
||||
switch (input.type()) {
|
||||
case ResultType::Color:
|
||||
return "compositor_realize_on_domain_bicubic_color";
|
||||
case ResultType::Vector:
|
||||
return "compositor_realize_on_domain_bicubic_vector";
|
||||
case ResultType::Float:
|
||||
return "compositor_realize_on_domain_bicubic_float";
|
||||
case ResultType::Int2:
|
||||
/* Realization does not support integer images. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (input.type()) {
|
||||
case ResultType::Color:
|
||||
return "compositor_realize_on_domain_color";
|
||||
case ResultType::Vector:
|
||||
return "compositor_realize_on_domain_vector";
|
||||
case ResultType::Float:
|
||||
return "compositor_realize_on_domain_float";
|
||||
case ResultType::Int2:
|
||||
/* Realization does not support integer images. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void realize_on_domain(Context &context,
|
||||
Result &input,
|
||||
Result &output,
|
||||
const Domain &domain,
|
||||
const float3x3 &input_transformation,
|
||||
const RealizationOptions &realization_options)
|
||||
{
|
||||
|
||||
GPUShader *shader = context.shader_manager().get(
|
||||
get_realization_shader(input, realization_options));
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
/* Transform the input space into the domain space. */
|
||||
const float3x3 local_transformation = math::invert(domain.transformation) * input_transformation;
|
||||
|
||||
/* Set the origin of the transformation to be the center of the domain. */
|
||||
const float3x3 transformation = math::from_origin_transform<float3x3>(
|
||||
local_transformation, float2(domain.size) / 2.0f);
|
||||
|
||||
/* Invert the transformation because the shader transforms the domain coordinates instead of the
|
||||
* input image itself and thus expect the inverse. */
|
||||
const float3x3 inverse_transformation = math::invert(transformation);
|
||||
|
||||
GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr());
|
||||
|
||||
/* The texture sampler should use bilinear interpolation for both the bilinear and bicubic
|
||||
* cases, as the logic used by the bicubic realization shader expects textures to use bilinear
|
||||
* interpolation. */
|
||||
const bool use_bilinear = ELEM(
|
||||
realization_options.interpolation, Interpolation::Bilinear, Interpolation::Bicubic);
|
||||
GPU_texture_filter_mode(input.texture(), use_bilinear);
|
||||
|
||||
/* If the input repeats, set a repeating wrap mode for out-of-bound texture access. Otherwise,
|
||||
* make out-of-bound texture access return zero by setting a clamp to border extend mode. */
|
||||
GPU_texture_extend_mode_x(input.texture(),
|
||||
realization_options.repeat_x ?
|
||||
GPU_SAMPLER_EXTEND_MODE_REPEAT :
|
||||
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
|
||||
GPU_texture_extend_mode_y(input.texture(),
|
||||
realization_options.repeat_y ?
|
||||
GPU_SAMPLER_EXTEND_MODE_REPEAT :
|
||||
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
|
||||
|
||||
input.bind_as_texture(shader, "input_tx");
|
||||
|
||||
output.allocate_texture(domain);
|
||||
output.bind_as_image(shader, "domain_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, domain.size);
|
||||
|
||||
input.unbind_as_texture();
|
||||
output.unbind_as_image();
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -0,0 +1,88 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_math_angle_types.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_algorithm_realize_on_domain.hh"
|
||||
#include "COM_context.hh"
|
||||
#include "COM_domain.hh"
|
||||
#include "COM_result.hh"
|
||||
|
||||
#include "COM_algorithm_transform.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* Given a potentially transformed domain, compute a domain such that its rotation and scale become
|
||||
* identity and the size of the domain is increased/reduced to adapt to the new transformation. For
|
||||
* instance, if the domain is rotated, the returned domain will have zero rotation but expanded
|
||||
* size to account for the bounding box of the domain after rotation. The size of the returned
|
||||
* domain is bound and clipped by the maximum possible GPU texture size to avoid allocations that
|
||||
* surpass hardware limits, which is typically 16k. */
|
||||
static Domain compute_realized_transformation_domain(const Domain &domain)
|
||||
{
|
||||
math::AngleRadian rotation;
|
||||
float2 translation, scale;
|
||||
float2 size = float2(domain.size);
|
||||
math::to_loc_rot_scale(domain.transformation, translation, rotation, scale);
|
||||
|
||||
/* Set the rotation to zero and expand the domain size to fit the bounding box of the rotated
|
||||
* result. */
|
||||
const float sine = math::abs(math::sin(rotation));
|
||||
const float cosine = math::abs(math::cos(rotation));
|
||||
size = float2(size.x * sine + size.y * cosine, size.x * cosine + size.y * sine);
|
||||
rotation = 0.0f;
|
||||
|
||||
/* Set the scale to 1 and scale the domain size to adapt to the new domain. */
|
||||
size *= scale;
|
||||
scale = float2(1.0f);
|
||||
|
||||
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(translation, rotation, scale);
|
||||
|
||||
const int2 domain_size = math::clamp(
|
||||
int2(math::round(size)), int2(1), int2(GPU_max_texture_size()));
|
||||
|
||||
return Domain(domain_size, transformation);
|
||||
}
|
||||
|
||||
void transform(Context &context,
|
||||
Result &input,
|
||||
Result &output,
|
||||
float3x3 transformation,
|
||||
Interpolation interpolation)
|
||||
{
|
||||
math::AngleRadian rotation;
|
||||
float2 translation, scale;
|
||||
math::to_loc_rot_scale(transformation, translation, rotation, scale);
|
||||
|
||||
/* Rotation and scale transformations are immediately realized. */
|
||||
if (rotation != 0.0f || scale != float2(1.0f)) {
|
||||
RealizationOptions realization_options = input.get_realization_options();
|
||||
realization_options.interpolation = interpolation;
|
||||
|
||||
Domain input_domain = input.domain();
|
||||
input_domain.transform(transformation);
|
||||
|
||||
const Domain target_domain = compute_realized_transformation_domain(input_domain);
|
||||
|
||||
realize_on_domain(
|
||||
context, input, output, target_domain, input_domain.transformation, realization_options);
|
||||
}
|
||||
else {
|
||||
input.pass_through(output);
|
||||
}
|
||||
|
||||
/* Translation transformations are delayed and are only stored in the result. */
|
||||
const float3x3 translation_matrix = math::from_location<float3x3>(translation);
|
||||
output.transform(translation_matrix);
|
||||
output.get_realization_options().interpolation = interpolation;
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -106,12 +106,6 @@ void Operation::add_and_evaluate_input_processors()
|
|||
add_and_evaluate_input_processor(identifier, conversion);
|
||||
}
|
||||
|
||||
for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
|
||||
SimpleOperation *realize_transformation = RealizeTransformationOperation::construct_if_needed(
|
||||
context(), get_input(identifier), get_input_descriptor(identifier));
|
||||
add_and_evaluate_input_processor(identifier, realize_transformation);
|
||||
}
|
||||
|
||||
for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
|
||||
SimpleOperation *realize_on_domain = RealizeOnDomainOperation::construct_if_needed(
|
||||
context(), get_input(identifier), get_input_descriptor(identifier), compute_domain());
|
||||
|
|
|
@ -2,22 +2,13 @@
|
|||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_math_angle_types.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_algorithm_realize_on_domain.hh"
|
||||
#include "COM_context.hh"
|
||||
#include "COM_domain.hh"
|
||||
#include "COM_input_descriptor.hh"
|
||||
#include "COM_realize_on_domain_operation.hh"
|
||||
#include "COM_result.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "COM_realize_on_domain_operation.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
|
@ -38,90 +29,12 @@ RealizeOnDomainOperation::RealizeOnDomainOperation(Context &context,
|
|||
|
||||
void RealizeOnDomainOperation::execute()
|
||||
{
|
||||
Result &input = get_input();
|
||||
Result &result = get_result();
|
||||
|
||||
result.allocate_texture(domain_);
|
||||
|
||||
GPUShader *shader = get_realization_shader();
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
/* Transform the input space into the domain space. */
|
||||
const float3x3 local_transformation = math::invert(domain_.transformation) *
|
||||
input.domain().transformation;
|
||||
|
||||
/* Set the origin of the transformation to be the center of the domain. */
|
||||
const float3x3 transformation = math::from_origin_transform<float3x3>(
|
||||
local_transformation, float2(domain_.size) / 2.0f);
|
||||
|
||||
/* Invert the transformation because the shader transforms the domain coordinates instead of the
|
||||
* input image itself and thus expect the inverse. */
|
||||
const float3x3 inverse_transformation = math::invert(transformation);
|
||||
|
||||
GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr());
|
||||
|
||||
/* The texture sampler should use bilinear interpolation for both the bilinear and bicubic
|
||||
* cases, as the logic used by the bicubic realization shader expects textures to use bilinear
|
||||
* interpolation. */
|
||||
const bool use_bilinear = ELEM(input.get_realization_options().interpolation,
|
||||
Interpolation::Bilinear,
|
||||
Interpolation::Bicubic);
|
||||
GPU_texture_filter_mode(input.texture(), use_bilinear);
|
||||
|
||||
/* If the input repeats, set a repeating wrap mode for out-of-bound texture access. Otherwise,
|
||||
* make out-of-bound texture access return zero by setting a clamp to border extend mode. */
|
||||
GPU_texture_extend_mode_x(input.texture(),
|
||||
input.get_realization_options().repeat_x ?
|
||||
GPU_SAMPLER_EXTEND_MODE_REPEAT :
|
||||
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
|
||||
GPU_texture_extend_mode_y(input.texture(),
|
||||
input.get_realization_options().repeat_y ?
|
||||
GPU_SAMPLER_EXTEND_MODE_REPEAT :
|
||||
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
|
||||
|
||||
input.bind_as_texture(shader, "input_tx");
|
||||
result.bind_as_image(shader, "domain_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, domain_.size);
|
||||
|
||||
input.unbind_as_texture();
|
||||
result.unbind_as_image();
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
GPUShader *RealizeOnDomainOperation::get_realization_shader()
|
||||
{
|
||||
if (get_input().get_realization_options().interpolation == Interpolation::Bicubic) {
|
||||
switch (get_result().type()) {
|
||||
case ResultType::Color:
|
||||
return shader_manager().get("compositor_realize_on_domain_bicubic_color");
|
||||
case ResultType::Vector:
|
||||
return shader_manager().get("compositor_realize_on_domain_bicubic_vector");
|
||||
case ResultType::Float:
|
||||
return shader_manager().get("compositor_realize_on_domain_bicubic_float");
|
||||
default:
|
||||
/* Other types are internal and needn't be handled by operations. */
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (get_result().type()) {
|
||||
case ResultType::Color:
|
||||
return shader_manager().get("compositor_realize_on_domain_color");
|
||||
case ResultType::Vector:
|
||||
return shader_manager().get("compositor_realize_on_domain_vector");
|
||||
case ResultType::Float:
|
||||
return shader_manager().get("compositor_realize_on_domain_float");
|
||||
default:
|
||||
/* Other types are internal and needn't be handled by operations. */
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
realize_on_domain(context(),
|
||||
get_input(),
|
||||
get_result(),
|
||||
domain_,
|
||||
get_input().domain().transformation,
|
||||
get_input().get_realization_options());
|
||||
}
|
||||
|
||||
Domain RealizeOnDomainOperation::compute_domain()
|
||||
|
@ -161,68 +74,4 @@ SimpleOperation *RealizeOnDomainOperation::construct_if_needed(
|
|||
return new RealizeOnDomainOperation(context, operation_domain, input_descriptor.type);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Realize Transformation Operation
|
||||
*/
|
||||
|
||||
Domain RealizeTransformationOperation::compute_target_domain(
|
||||
const Domain &input_domain, const InputRealizationOptions &realization_options)
|
||||
{
|
||||
if (!realization_options.realize_rotation && !realization_options.realize_scale) {
|
||||
return input_domain;
|
||||
}
|
||||
|
||||
math::AngleRadian rotation;
|
||||
float2 translation, scale;
|
||||
float2 size = float2(input_domain.size);
|
||||
math::to_loc_rot_scale(input_domain.transformation, translation, rotation, scale);
|
||||
|
||||
/* Set the rotation to zero and expand the domain size to fit the bounding box of the rotated
|
||||
* result. */
|
||||
if (realization_options.realize_rotation) {
|
||||
const float sine = math::abs(math::sin(rotation));
|
||||
const float cosine = math::abs(math::cos(rotation));
|
||||
size = float2(size.x * sine + size.y * cosine, size.x * cosine + size.y * sine);
|
||||
rotation = 0.0f;
|
||||
}
|
||||
|
||||
/* Set the scale to 1 and scale the domain size to adapt to the new domain. */
|
||||
if (realization_options.realize_scale) {
|
||||
size *= scale;
|
||||
scale = float2(1.0f);
|
||||
}
|
||||
|
||||
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(translation, rotation, scale);
|
||||
|
||||
return Domain(int2(math::ceil(size)), transformation);
|
||||
}
|
||||
|
||||
SimpleOperation *RealizeTransformationOperation::construct_if_needed(
|
||||
Context &context, const Result &input_result, const InputDescriptor &input_descriptor)
|
||||
{
|
||||
/* The input expects a single value and if no single value is provided, it will be ignored and a
|
||||
* default value will be used, so no need to realize it and the operation is not needed. */
|
||||
if (input_descriptor.expects_single_value) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Input result is a single value and does not need realization, the operation is not needed. */
|
||||
if (input_result.is_single_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Domain target_domain = compute_target_domain(input_result.domain(),
|
||||
input_descriptor.realization_options);
|
||||
|
||||
/* The input have an identical domain to the target domain, either because the input doesn't need
|
||||
* to realize its transformations or because it has identity transformations, so no need to
|
||||
* realize it and the operation is not needed. */
|
||||
if (target_domain == input_result.domain()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Otherwise, realization on the target domain is needed. */
|
||||
return new RealizeOnDomainOperation(context, target_domain, input_descriptor.type);
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
|
|
|
@ -129,12 +129,6 @@ InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket)
|
|||
input_descriptor.realization_options.realize_on_operation_domain = bool(
|
||||
socket_declaration->compositor_realization_options() &
|
||||
CompositorInputRealizationOptions::RealizeOnOperationDomain);
|
||||
input_descriptor.realization_options.realize_rotation = bool(
|
||||
socket_declaration->compositor_realization_options() &
|
||||
CompositorInputRealizationOptions::RealizeRotation);
|
||||
input_descriptor.realization_options.realize_scale = bool(
|
||||
socket_declaration->compositor_realization_options() &
|
||||
CompositorInputRealizationOptions::RealizeScale);
|
||||
|
||||
return input_descriptor;
|
||||
}
|
||||
|
|
|
@ -52,10 +52,9 @@ enum class OutputSocketFieldType {
|
|||
enum class CompositorInputRealizationOptions : uint8_t {
|
||||
None = 0,
|
||||
RealizeOnOperationDomain = (1 << 0),
|
||||
RealizeRotation = (1 << 1),
|
||||
RealizeScale = (1 << 2),
|
||||
};
|
||||
ENUM_OPERATORS(CompositorInputRealizationOptions, CompositorInputRealizationOptions::RealizeScale)
|
||||
ENUM_OPERATORS(CompositorInputRealizationOptions,
|
||||
CompositorInputRealizationOptions::RealizeOnOperationDomain)
|
||||
|
||||
/**
|
||||
* Contains information about how a node output's field state depends on inputs of the same node.
|
||||
|
|
|
@ -30,7 +30,7 @@ static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b)
|
|||
.compositor_domain_priority(0);
|
||||
b.add_input<decl::Color>("Bokeh")
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.compositor_realization_options(CompositorInputRealizationOptions::RealizeRotation);
|
||||
.compositor_realization_options(CompositorInputRealizationOptions::None);
|
||||
b.add_input<decl::Float>("Size")
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
*/
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_math_angle_types.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "COM_algorithm_transform.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
@ -53,15 +55,12 @@ class RotateOperation : public NodeOperation {
|
|||
void execute() override
|
||||
{
|
||||
Result &input = get_input("Image");
|
||||
Result &result = get_result("Image");
|
||||
input.pass_through(result);
|
||||
Result &output = get_result("Image");
|
||||
|
||||
const math::AngleRadian rotation = get_input("Degr").get_float_value_default(0.0f);
|
||||
|
||||
const float3x3 transformation = math::from_rotation<float3x3>(rotation);
|
||||
|
||||
result.transform(transformation);
|
||||
result.get_realization_options().interpolation = get_interpolation();
|
||||
transform(context(), input, output, transformation, get_interpolation());
|
||||
}
|
||||
|
||||
Interpolation get_interpolation()
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
*/
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_math_angle_types.hh"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
|
@ -17,6 +19,7 @@
|
|||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "COM_algorithm_transform.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
@ -82,13 +85,16 @@ class ScaleOperation : public NodeOperation {
|
|||
void execute() override
|
||||
{
|
||||
Result &input = get_input("Image");
|
||||
Result &result = get_result("Image");
|
||||
input.pass_through(result);
|
||||
Result &output = get_result("Image");
|
||||
|
||||
const float2 scale = get_scale();
|
||||
const math::AngleRadian rotation = 0.0f;
|
||||
const float2 translation = get_translation();
|
||||
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(
|
||||
get_translation(), math::AngleRadian(0.0f), get_scale());
|
||||
translation, rotation, scale);
|
||||
|
||||
result.transform(transformation);
|
||||
const Interpolation interpolation = input.get_realization_options().interpolation;
|
||||
transform(context(), input, output, transformation, interpolation);
|
||||
}
|
||||
|
||||
float2 get_scale()
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "BKE_movieclip.h"
|
||||
#include "BKE_tracking.h"
|
||||
|
||||
#include "COM_algorithm_transform.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
@ -82,17 +83,17 @@ class Stabilize2DOperation : public NodeOperation {
|
|||
|
||||
void execute() override
|
||||
{
|
||||
Result &input_image = get_input("Image");
|
||||
Result &output_image = get_result("Image");
|
||||
input_image.pass_through(output_image);
|
||||
Result &input = get_input("Image");
|
||||
Result &output = get_result("Image");
|
||||
|
||||
MovieClip *movie_clip = get_movie_clip();
|
||||
if (input_image.is_single_value() || !movie_clip) {
|
||||
if (input.is_single_value() || !movie_clip) {
|
||||
input.pass_through(output);
|
||||
return;
|
||||
}
|
||||
|
||||
const int width = input_image.domain().size.x;
|
||||
const int height = input_image.domain().size.y;
|
||||
const int width = input.domain().size.x;
|
||||
const int height = input.domain().size.y;
|
||||
const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip,
|
||||
context().get_frame_number());
|
||||
|
||||
|
@ -107,8 +108,7 @@ class Stabilize2DOperation : public NodeOperation {
|
|||
transformation = math::invert(transformation);
|
||||
}
|
||||
|
||||
output_image.transform(transformation);
|
||||
output_image.get_realization_options().interpolation = get_interpolation();
|
||||
transform(context(), input, output, transformation, get_interpolation());
|
||||
}
|
||||
|
||||
Interpolation get_interpolation()
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
*/
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_math_angle_types.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "COM_algorithm_transform.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
@ -56,7 +58,6 @@ static void node_composit_buts_transform(uiLayout *layout, bContext * /*C*/, Poi
|
|||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
using namespace blender::math;
|
||||
|
||||
class TransformOperation : public NodeOperation {
|
||||
public:
|
||||
|
@ -65,18 +66,16 @@ class TransformOperation : public NodeOperation {
|
|||
void execute() override
|
||||
{
|
||||
Result &input = get_input("Image");
|
||||
Result &result = get_result("Image");
|
||||
input.pass_through(result);
|
||||
Result &output = get_result("Image");
|
||||
|
||||
const float2 translation = float2(get_input("X").get_float_value_default(0.0f),
|
||||
get_input("Y").get_float_value_default(0.0f));
|
||||
const AngleRadian rotation = AngleRadian(get_input("Angle").get_float_value_default(0.0f));
|
||||
const math::AngleRadian rotation = get_input("Angle").get_float_value_default(0.0f);
|
||||
const float2 scale = float2(get_input("Scale").get_float_value_default(1.0f));
|
||||
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(
|
||||
translation, rotation, scale);
|
||||
|
||||
const float3x3 transformation = from_loc_rot_scale<float3x3>(translation, rotation, scale);
|
||||
|
||||
result.transform(transformation);
|
||||
result.get_realization_options().interpolation = get_interpolation();
|
||||
transform(context(), input, output, transformation, get_interpolation());
|
||||
}
|
||||
|
||||
Interpolation get_interpolation()
|
||||
|
|
Loading…
Reference in New Issue