Realtime Compositor: Add basic input nodes
This patch implements the following nodes for the realtime compositor: - Image node. - Movie clip node. - Render layers node. - RGB node. - Scene time node. - Value node. Differential Revision: https://developer.blender.org/D15227 Reviewed By: Clement Foucault
This commit is contained in:
parent
365fbb447e
commit
865204fef0
|
@ -8,6 +8,7 @@
|
|||
#include "node_composite_util.hh"
|
||||
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
@ -17,6 +18,8 @@
|
|||
#include "BKE_main.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
@ -27,6 +30,12 @@
|
|||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
/* **************** IMAGE (and RenderResult, multilayer image) ******************** */
|
||||
|
||||
static bNodeSocketTemplate cmp_node_rlayers_out[] = {
|
||||
|
@ -433,6 +442,215 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree),
|
|||
}
|
||||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class ImageOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
if (!is_valid()) {
|
||||
allocate_invalid();
|
||||
return;
|
||||
}
|
||||
|
||||
update_image_frame_number();
|
||||
|
||||
for (const OutputSocketRef *output : node()->outputs()) {
|
||||
compute_output(output->identifier());
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if the node results can be computed, otherwise, returns false. */
|
||||
bool is_valid()
|
||||
{
|
||||
Image *image = get_image();
|
||||
ImageUser *image_user = get_image_user();
|
||||
if (!image || !image_user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BKE_image_is_multilayer(image)) {
|
||||
if (!image->rr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RenderLayer *render_layer = get_render_layer();
|
||||
if (!render_layer) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Allocate all needed outputs as invalid. This should be called when is_valid returns false. */
|
||||
void allocate_invalid()
|
||||
{
|
||||
for (const OutputSocketRef *output : node()->outputs()) {
|
||||
if (!should_compute_output(output->identifier())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Result &result = get_result(output->identifier());
|
||||
result.allocate_invalid();
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute the effective frame number of the image if it was animated and invalidate the cached
|
||||
* GPU texture if the computed frame number is different. */
|
||||
void update_image_frame_number()
|
||||
{
|
||||
BKE_image_user_frame_calc(get_image(), get_image_user(), context().get_frame_number());
|
||||
}
|
||||
|
||||
void compute_output(StringRef identifier)
|
||||
{
|
||||
if (!should_compute_output(identifier)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImageUser image_user = compute_image_user_for_output(identifier);
|
||||
GPUTexture *image_texture = BKE_image_get_gpu_texture(get_image(), &image_user, nullptr);
|
||||
|
||||
const int2 size = int2(GPU_texture_width(image_texture), GPU_texture_height(image_texture));
|
||||
Result &result = get_result(identifier);
|
||||
result.allocate_texture(Domain(size));
|
||||
|
||||
GPUShader *shader = shader_manager().get(get_shader_name(identifier));
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
|
||||
GPU_texture_bind(image_texture, input_unit);
|
||||
|
||||
result.bind_as_image(shader, "output_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, size);
|
||||
|
||||
GPU_shader_unbind();
|
||||
GPU_texture_unbind(image_texture);
|
||||
result.unbind_as_image();
|
||||
}
|
||||
|
||||
/* Get a copy of the image user that is appropriate to retrieve the image buffer for the output
|
||||
* with the given identifier. This essentially sets the appropriate pass and view indices that
|
||||
* corresponds to the output. */
|
||||
ImageUser compute_image_user_for_output(StringRef identifier)
|
||||
{
|
||||
ImageUser image_user = *get_image_user();
|
||||
|
||||
/* Set the needed view. */
|
||||
image_user.view = get_view_index();
|
||||
|
||||
/* Set the needed pass. */
|
||||
if (BKE_image_is_multilayer(get_image())) {
|
||||
image_user.pass = get_pass_index(get_pass_name(identifier));
|
||||
BKE_image_multilayer_index(get_image()->rr, &image_user);
|
||||
}
|
||||
else {
|
||||
BKE_image_multiview_index(get_image(), &image_user);
|
||||
}
|
||||
|
||||
return image_user;
|
||||
}
|
||||
|
||||
/* Get the shader that should be used to compute the output with the given identifier. The
|
||||
* shaders just copy the retrieved image textures into the results except for the alpha output,
|
||||
* which extracts the alpha and writes it to the result instead. Note that a call to a host
|
||||
* texture copy doesn't work because results are stored in a different half float formats. */
|
||||
const char *get_shader_name(StringRef identifier)
|
||||
{
|
||||
if (identifier == "Alpha") {
|
||||
return "compositor_extract_alpha_from_color";
|
||||
}
|
||||
else if (get_result(identifier).type() == ResultType::Color) {
|
||||
return "compositor_convert_color_to_half_color";
|
||||
}
|
||||
else {
|
||||
return "compositor_convert_float_to_half_float";
|
||||
}
|
||||
}
|
||||
|
||||
Image *get_image()
|
||||
{
|
||||
return (Image *)bnode().id;
|
||||
}
|
||||
|
||||
ImageUser *get_image_user()
|
||||
{
|
||||
return static_cast<ImageUser *>(bnode().storage);
|
||||
}
|
||||
|
||||
/* Get the render layer selected in the node assuming the image is a multilayer image. */
|
||||
RenderLayer *get_render_layer()
|
||||
{
|
||||
const ListBase *layers = &get_image()->rr->layers;
|
||||
return static_cast<RenderLayer *>(BLI_findlink(layers, get_image_user()->layer));
|
||||
}
|
||||
|
||||
/* Get the name of the pass corresponding to the output with the given identifier assuming the
|
||||
* image is a multilayer image. */
|
||||
const char *get_pass_name(StringRef identifier)
|
||||
{
|
||||
DOutputSocket output = node().output_by_identifier(identifier);
|
||||
return static_cast<NodeImageLayer *>(output->bsocket()->storage)->pass_name;
|
||||
}
|
||||
|
||||
/* Get the index of the pass with the given name in the selected render layer's passes list
|
||||
* assuming the image is a multilayer image. */
|
||||
int get_pass_index(const char *name)
|
||||
{
|
||||
return BLI_findstringindex(&get_render_layer()->passes, name, offsetof(RenderPass, name));
|
||||
}
|
||||
|
||||
/* Get the index of the view selected in the node. If the image is not a multi-view image or only
|
||||
* has a single view, then zero is returned. Otherwise, if the image is a multi-view image, the
|
||||
* index of the selected view is returned. However, note that the value of the view member of the
|
||||
* image user is not the actual index of the view. More specifically, the index 0 is reserved to
|
||||
* denote the special mode of operation "All", which dynamically selects the view whose name
|
||||
* matches the view currently being rendered. It follows that the views are then indexed starting
|
||||
* from 1. So for non zero view values, the actual index of the view is the value of the view
|
||||
* member of the image user minus 1. */
|
||||
int get_view_index()
|
||||
{
|
||||
/* The image is not a multi-view image, so just return zero. */
|
||||
if (!BKE_image_is_multiview(get_image())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ListBase *views = &get_image()->rr->views;
|
||||
/* There is only one view and its index is 0. */
|
||||
if (BLI_listbase_count_at_most(views, 2) < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int view = get_image_user()->view;
|
||||
/* The view is not zero, which means it is manually specified and the actual index is then the
|
||||
* view value minus 1. */
|
||||
if (view != 0) {
|
||||
return view - 1;
|
||||
}
|
||||
|
||||
/* Otherwise, the view value is zero, denoting the special mode of operation "All", which finds
|
||||
* the index of the view whose name matches the view currently being rendered. */
|
||||
const char *view_name = context().get_view_name().data();
|
||||
const int matched_view = BLI_findstringindex(views, view_name, offsetof(RenderView, name));
|
||||
|
||||
/* No view matches the view currently being rendered, so fallback to the first view. */
|
||||
if (matched_view == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return matched_view;
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new ImageOperation(context, node);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_composite_image_cc
|
||||
|
||||
void register_node_type_cmp_image()
|
||||
|
@ -446,6 +664,7 @@ void register_node_type_cmp_image()
|
|||
node_type_storage(
|
||||
&ntype, "ImageUser", file_ns::node_composit_free_image, file_ns::node_composit_copy_image);
|
||||
node_type_update(&ntype, file_ns::cmp_node_image_update);
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
ntype.labelfunc = node_image_label;
|
||||
ntype.flag |= NODE_PREVIEW;
|
||||
|
||||
|
@ -469,7 +688,7 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index)
|
|||
return (STREQ(name, "Alpha")) ? RE_PASSNAME_COMBINED : name;
|
||||
}
|
||||
|
||||
namespace blender::nodes::node_composite_image_cc {
|
||||
namespace blender::nodes::node_composite_render_layer_cc {
|
||||
|
||||
static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
|
@ -595,11 +814,60 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer
|
|||
RNA_string_set(&op_ptr, "scene", scene_name);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_composite_image_cc
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class RenderLayerOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
const int view_layer = bnode().custom1;
|
||||
GPUTexture *pass_texture = context().get_input_texture(view_layer, SCE_PASS_COMBINED);
|
||||
const int2 size = int2(GPU_texture_width(pass_texture), GPU_texture_height(pass_texture));
|
||||
|
||||
/* Compute image output. */
|
||||
Result &image_result = get_result("Image");
|
||||
image_result.allocate_texture(Domain(size));
|
||||
GPU_texture_copy(image_result.texture(), pass_texture);
|
||||
|
||||
/* Compute alpha output. */
|
||||
Result &alpha_result = get_result("Alpha");
|
||||
alpha_result.allocate_texture(Domain(size));
|
||||
|
||||
GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
|
||||
GPU_texture_bind(pass_texture, input_unit);
|
||||
|
||||
alpha_result.bind_as_image(shader, "output_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, size);
|
||||
|
||||
GPU_shader_unbind();
|
||||
GPU_texture_unbind(pass_texture);
|
||||
alpha_result.unbind_as_image();
|
||||
|
||||
/* Other output passes are not supported for now, so allocate them as invalid. */
|
||||
for (const OutputSocketRef *output : node()->outputs()) {
|
||||
if (output->identifier() != "Image" && output->identifier() != "Alpha") {
|
||||
get_result(output->identifier()).allocate_invalid();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new RenderLayerOperation(context, node);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_composite_render_layer_cc
|
||||
|
||||
void register_node_type_cmp_rlayers()
|
||||
{
|
||||
namespace file_ns = blender::nodes::node_composite_image_cc;
|
||||
namespace file_ns = blender::nodes::node_composite_render_layer_cc;
|
||||
|
||||
static bNodeType ntype;
|
||||
|
||||
|
@ -608,6 +876,7 @@ void register_node_type_cmp_rlayers()
|
|||
ntype.draw_buttons = file_ns::node_composit_buts_viewlayers;
|
||||
ntype.initfunc_api = file_ns::node_composit_init_rlayers;
|
||||
ntype.poll = file_ns::node_composit_poll_rlayers;
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
ntype.flag |= NODE_PREVIEW;
|
||||
node_type_storage(
|
||||
&ntype, nullptr, file_ns::node_composit_free_rlayers, file_ns::node_composit_copy_rlayers);
|
||||
|
|
|
@ -5,8 +5,13 @@
|
|||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "BLI_math_vec_types.hh"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_movieclip.h"
|
||||
#include "BKE_tracking.h"
|
||||
|
||||
#include "DNA_defaults.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
@ -14,6 +19,12 @@
|
|||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
namespace blender::nodes::node_composite_movieclip_cc {
|
||||
|
@ -79,6 +90,177 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point
|
|||
uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings");
|
||||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class MovieClipOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
GPUTexture *movie_clip_texture = get_movie_clip_texture();
|
||||
|
||||
compute_image(movie_clip_texture);
|
||||
compute_alpha(movie_clip_texture);
|
||||
compute_stabilization_data(movie_clip_texture);
|
||||
|
||||
free_movie_clip_texture();
|
||||
}
|
||||
|
||||
void compute_image(GPUTexture *movie_clip_texture)
|
||||
{
|
||||
if (!should_compute_output("Image")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Result &result = get_result("Image");
|
||||
|
||||
/* The movie clip texture is invalid or missing, set an appropriate fallback value. */
|
||||
if (!movie_clip_texture) {
|
||||
result.allocate_invalid();
|
||||
return;
|
||||
}
|
||||
|
||||
const int2 size = int2(GPU_texture_width(movie_clip_texture),
|
||||
GPU_texture_height(movie_clip_texture));
|
||||
result.allocate_texture(Domain(size));
|
||||
|
||||
GPUShader *shader = shader_manager().get("compositor_convert_color_to_half_color");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
|
||||
GPU_texture_bind(movie_clip_texture, input_unit);
|
||||
|
||||
result.bind_as_image(shader, "output_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, size);
|
||||
|
||||
GPU_shader_unbind();
|
||||
GPU_texture_unbind(movie_clip_texture);
|
||||
result.unbind_as_image();
|
||||
}
|
||||
|
||||
void compute_alpha(GPUTexture *movie_clip_texture)
|
||||
{
|
||||
if (!should_compute_output("Alpha")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Result &result = get_result("Alpha");
|
||||
|
||||
/* The movie clip texture is invalid or missing, set an appropriate fallback value. */
|
||||
if (!movie_clip_texture) {
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
const int2 size = int2(GPU_texture_width(movie_clip_texture),
|
||||
GPU_texture_height(movie_clip_texture));
|
||||
result.allocate_texture(Domain(size));
|
||||
|
||||
GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
|
||||
GPU_texture_bind(movie_clip_texture, input_unit);
|
||||
|
||||
result.bind_as_image(shader, "output_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, size);
|
||||
|
||||
GPU_shader_unbind();
|
||||
GPU_texture_unbind(movie_clip_texture);
|
||||
result.unbind_as_image();
|
||||
}
|
||||
|
||||
void compute_stabilization_data(GPUTexture *movie_clip_texture)
|
||||
{
|
||||
/* The movie clip texture is invalid or missing, set appropriate fallback values. */
|
||||
if (!movie_clip_texture) {
|
||||
if (should_compute_output("Offset X")) {
|
||||
Result &result = get_result("Offset X");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(0.0f);
|
||||
}
|
||||
if (should_compute_output("Offset Y")) {
|
||||
Result &result = get_result("Offset Y");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(0.0f);
|
||||
}
|
||||
if (should_compute_output("Scale")) {
|
||||
Result &result = get_result("Scale");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(1.0f);
|
||||
}
|
||||
if (should_compute_output("Angle")) {
|
||||
Result &result = get_result("Angle");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(0.0f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MovieClip *movie_clip = get_movie_clip();
|
||||
const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip,
|
||||
context().get_frame_number());
|
||||
const int width = GPU_texture_width(movie_clip_texture);
|
||||
const int height = GPU_texture_height(movie_clip_texture);
|
||||
|
||||
/* If the movie clip has no stabilization data, it will initialize the given values with
|
||||
* fallback values regardless, so no need to handle that case. */
|
||||
float2 offset;
|
||||
float scale, angle;
|
||||
BKE_tracking_stabilization_data_get(
|
||||
movie_clip, frame_number, width, height, offset, &scale, &angle);
|
||||
|
||||
if (should_compute_output("Offset X")) {
|
||||
Result &result = get_result("Offset X");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(offset.x);
|
||||
}
|
||||
if (should_compute_output("Offset Y")) {
|
||||
Result &result = get_result("Offset Y");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(offset.y);
|
||||
}
|
||||
if (should_compute_output("Scale")) {
|
||||
Result &result = get_result("Scale");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(scale);
|
||||
}
|
||||
if (should_compute_output("Angle")) {
|
||||
Result &result = get_result("Angle");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(angle);
|
||||
}
|
||||
}
|
||||
|
||||
GPUTexture *get_movie_clip_texture()
|
||||
{
|
||||
MovieClip *movie_clip = get_movie_clip();
|
||||
MovieClipUser *movie_clip_user = static_cast<MovieClipUser *>(bnode().storage);
|
||||
BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number());
|
||||
return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user);
|
||||
}
|
||||
|
||||
void free_movie_clip_texture()
|
||||
{
|
||||
MovieClip *movie_clip = get_movie_clip();
|
||||
return BKE_movieclip_free_gputexture(movie_clip);
|
||||
}
|
||||
|
||||
MovieClip *get_movie_clip()
|
||||
{
|
||||
return (MovieClip *)bnode().id;
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new MovieClipOperation(context, node);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_composite_movieclip_cc
|
||||
|
||||
void register_node_type_cmp_movieclip()
|
||||
|
@ -91,6 +273,7 @@ void register_node_type_cmp_movieclip()
|
|||
ntype.declare = file_ns::cmp_node_movieclip_declare;
|
||||
ntype.draw_buttons = file_ns::node_composit_buts_movieclip;
|
||||
ntype.draw_buttons_ex = file_ns::node_composit_buts_movieclip_ex;
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
ntype.initfunc_api = file_ns::init;
|
||||
ntype.flag |= NODE_PREVIEW;
|
||||
node_type_storage(
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "BLI_math_vec_types.hh"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
/* **************** RGB ******************** */
|
||||
|
@ -16,6 +22,29 @@ static void cmp_node_rgb_declare(NodeDeclarationBuilder &b)
|
|||
b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f});
|
||||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class RGBOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
Result &result = get_result("RGBA");
|
||||
result.allocate_single_value();
|
||||
|
||||
const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first);
|
||||
float4 color = float4(static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value);
|
||||
|
||||
result.set_color_value(color);
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new RGBOperation(context, node);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_composite_rgb_cc
|
||||
|
||||
void register_node_type_cmp_rgb()
|
||||
|
@ -27,6 +56,7 @@ void register_node_type_cmp_rgb()
|
|||
cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT);
|
||||
ntype.declare = file_ns::cmp_node_rgb_declare;
|
||||
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
@ -13,6 +15,38 @@ static void cmp_node_scene_time_declare(NodeDeclarationBuilder &b)
|
|||
b.add_output<decl::Float>(N_("Frame"));
|
||||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class SceneTimeOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
execute_seconds();
|
||||
execute_frame();
|
||||
}
|
||||
|
||||
void execute_seconds()
|
||||
{
|
||||
Result &result = get_result("Seconds");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(context().get_time());
|
||||
}
|
||||
|
||||
void execute_frame()
|
||||
{
|
||||
Result &result = get_result("Frame");
|
||||
result.allocate_single_value();
|
||||
result.set_float_value(static_cast<float>(context().get_frame_number()));
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new SceneTimeOperation(context, node);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
void register_node_type_cmp_scene_time()
|
||||
|
@ -21,5 +55,7 @@ void register_node_type_cmp_scene_time()
|
|||
|
||||
cmp_node_type_base(&ntype, CMP_NODE_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT);
|
||||
ntype.declare = blender::nodes::cmp_node_scene_time_declare;
|
||||
ntype.get_compositor_operation = blender::nodes::get_compositor_operation;
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
/* **************** VALUE ******************** */
|
||||
|
@ -16,6 +18,29 @@ static void cmp_node_value_declare(NodeDeclarationBuilder &b)
|
|||
b.add_output<decl::Float>(N_("Value")).default_value(0.5f);
|
||||
}
|
||||
|
||||
using namespace blender::realtime_compositor;
|
||||
|
||||
class ValueOperation : public NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
Result &result = get_result("Value");
|
||||
result.allocate_single_value();
|
||||
|
||||
const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first);
|
||||
float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value;
|
||||
|
||||
result.set_float_value(value);
|
||||
}
|
||||
};
|
||||
|
||||
static NodeOperation *get_compositor_operation(Context &context, DNode node)
|
||||
{
|
||||
return new ValueOperation(context, node);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_composite_value_cc
|
||||
|
||||
void register_node_type_cmp_value()
|
||||
|
@ -27,6 +52,7 @@ void register_node_type_cmp_value()
|
|||
cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT);
|
||||
ntype.declare = file_ns::cmp_node_value_declare;
|
||||
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue