diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index dd035dbf537..9e85282ab42 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1337,12 +1337,6 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_CHAN_RGB 1 #define CMP_CHAN_A 2 -/* track position node, in custom1 */ -#define CMP_TRACKPOS_ABSOLUTE 0 -#define CMP_TRACKPOS_RELATIVE_START 1 -#define CMP_TRACKPOS_RELATIVE_FRAME 2 -#define CMP_TRACKPOS_ABSOLUTE_FRAME 3 - /* Cryptomatte source. */ #define CMP_CRYPTOMATTE_SRC_RENDER 0 #define CMP_CRYPTOMATTE_SRC_IMAGE 1 diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.cc b/source/blender/compositor/nodes/COM_TrackPositionNode.cc index da12f72b451..25daf3306a1 100644 --- a/source/blender/compositor/nodes/COM_TrackPositionNode.cc +++ b/source/blender/compositor/nodes/COM_TrackPositionNode.cc @@ -30,7 +30,7 @@ static TrackPositionOperation *create_motion_operation(NodeConverter &converter, operation->set_track_name(trackpos_data->track_name); operation->set_framenumber(frame_number); operation->set_axis(axis); - operation->set_position(CMP_TRACKPOS_ABSOLUTE); + operation->set_position(CMP_NODE_TRACK_POSITION_ABSOLUTE); operation->set_relative_frame(frame_number + delta); operation->set_speed_output(true); converter.add_operation(operation); @@ -49,7 +49,7 @@ void TrackPositionNode::convert_to_operations(NodeConverter &converter, NodeOutput *output_speed = this->get_output_socket(2); int frame_number; - if (editor_node->custom1 == CMP_TRACKPOS_ABSOLUTE_FRAME) { + if (editor_node->custom1 == CMP_NODE_TRACK_POSITION_ABSOLUTE_FRAME) { frame_number = editor_node->custom2; } else { @@ -62,7 +62,7 @@ void TrackPositionNode::convert_to_operations(NodeConverter &converter, operationX->set_track_name(trackpos_data->track_name); operationX->set_framenumber(frame_number); operationX->set_axis(0); - operationX->set_position(editor_node->custom1); + operationX->set_position(static_cast(editor_node->custom1)); operationX->set_relative_frame(editor_node->custom2); converter.add_operation(operationX); converter.map_output_socket(outputX, operationX->get_output_socket()); @@ -73,7 +73,7 @@ void TrackPositionNode::convert_to_operations(NodeConverter &converter, operationY->set_track_name(trackpos_data->track_name); operationY->set_framenumber(frame_number); operationY->set_axis(1); - operationY->set_position(editor_node->custom1); + operationX->set_position(static_cast(editor_node->custom1)); operationY->set_relative_frame(editor_node->custom2); converter.add_operation(operationY); converter.map_output_socket(outputY, operationY->get_output_socket()); diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index 3e7a2d3cbd9..6763293b519 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -19,7 +19,7 @@ TrackPositionOperation::TrackPositionOperation() tracking_object_name_[0] = 0; track_name_[0] = 0; axis_ = 0; - position_ = CMP_TRACKPOS_ABSOLUTE; + position_ = CMP_NODE_TRACK_POSITION_ABSOLUTE; relative_frame_ = 0; speed_output_ = false; flags_.is_set_operation = true; @@ -80,7 +80,7 @@ void TrackPositionOperation::calc_track_position() swap_v2_v2(relative_pos_, marker_pos_); } } - else if (position_ == CMP_TRACKPOS_RELATIVE_START) { + else if (position_ == CMP_NODE_TRACK_POSITION_RELATIVE_START) { int i; for (i = 0; i < track->markersnr; i++) { @@ -93,7 +93,7 @@ void TrackPositionOperation::calc_track_position() } } } - else if (position_ == CMP_TRACKPOS_RELATIVE_FRAME) { + else if (position_ == CMP_NODE_TRACK_POSITION_RELATIVE_FRAME) { int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, relative_frame_); diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h index 6130aa20f0e..efe3ec16887 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.h +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h @@ -25,7 +25,7 @@ class TrackPositionOperation : public ConstantOperation { char tracking_object_name_[64]; char track_name_[64]; int axis_; - int position_; + CMPNodeTrackPositionMode position_; int relative_frame_; bool speed_output_; @@ -63,7 +63,7 @@ class TrackPositionOperation : public ConstantOperation { { axis_ = value; } - void set_position(int value) + void set_position(CMPNodeTrackPositionMode value) { position_ = value; } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 4e57cd82b22..2831e488322 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2048,6 +2048,14 @@ typedef enum CMPNodeToneMapType { CMP_NODE_TONE_MAP_PHOTORECEPTOR = 1, } CMPNodeToneMapType; +/* Track Position Node. Stored in custom1. */ +typedef enum CMPNodeTrackPositionMode { + CMP_NODE_TRACK_POSITION_ABSOLUTE = 0, + CMP_NODE_TRACK_POSITION_RELATIVE_START = 1, + CMP_NODE_TRACK_POSITION_RELATIVE_FRAME = 2, + CMP_NODE_TRACK_POSITION_ABSOLUTE_FRAME = 3, +} CMPNodeTrackPositionMode; + /* Plane track deform node. */ enum { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index e57f0af6f7d..b33e5543219 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -8986,18 +8986,22 @@ static void def_cmp_trackpos(StructRNA *srna) PropertyRNA *prop; static const EnumPropertyItem position_items[] = { - {CMP_TRACKPOS_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Output absolute position of a marker"}, - {CMP_TRACKPOS_RELATIVE_START, + {CMP_NODE_TRACK_POSITION_ABSOLUTE, + "ABSOLUTE", + 0, + "Absolute", + "Output absolute position of a marker"}, + {CMP_NODE_TRACK_POSITION_RELATIVE_START, "RELATIVE_START", 0, "Relative Start", "Output position of a marker relative to first marker of a track"}, - {CMP_TRACKPOS_RELATIVE_FRAME, + {CMP_NODE_TRACK_POSITION_RELATIVE_FRAME, "RELATIVE_FRAME", 0, "Relative Frame", "Output position of a marker relative to marker at given frame number"}, - {CMP_TRACKPOS_ABSOLUTE_FRAME, + {CMP_NODE_TRACK_POSITION_ABSOLUTE_FRAME, "ABSOLUTE_FRAME", 0, "Absolute Frame", diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc index 3609c235c4b..fc71160da16 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -5,11 +5,16 @@ * \ingroup cmpnodes */ +#include "BLI_index_range.hh" +#include "BLI_math_vec_types.hh" + +#include "DNA_defaults.h" #include "DNA_movieclip_types.h" #include "DNA_tracking_types.h" #include "BKE_context.h" #include "BKE_lib_id.h" +#include "BKE_movieclip.h" #include "BKE_tracking.h" #include "RNA_access.h" @@ -24,6 +29,8 @@ namespace blender::nodes::node_composite_trackpos_cc { +NODE_STORAGE_FUNCS(NodeTrackPosData) + static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b) { b.add_output(N_("X")); @@ -97,7 +104,9 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN uiItemR(layout, ptr, "position", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); - if (ELEM(node->custom1, CMP_TRACKPOS_RELATIVE_FRAME, CMP_TRACKPOS_ABSOLUTE_FRAME)) { + if (ELEM(node->custom1, + CMP_NODE_TRACK_POSITION_RELATIVE_FRAME, + CMP_NODE_TRACK_POSITION_ABSOLUTE_FRAME)) { uiItemR(layout, ptr, "frame_relative", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } } @@ -111,9 +120,234 @@ class TrackPositionOperation : public NodeOperation { void execute() override { - get_result("X").allocate_invalid(); - get_result("Y").allocate_invalid(); - get_result("Speed").allocate_invalid(); + MovieTrackingTrack *track = get_movie_tracking_track(); + + if (!track) { + execute_invalid(); + return; + } + + const float2 current_marker_position = compute_marker_position_at_frame(track, get_frame()); + const int2 size = get_size(); + + execute_position(track, current_marker_position, size); + execute_speed(track, current_marker_position, size); + } + + void execute_position(MovieTrackingTrack *track, float2 current_marker_position, int2 size) + { + const bool should_compute_x = should_compute_output("X"); + const bool should_compute_y = should_compute_output("Y"); + if (!should_compute_x && !should_compute_y) { + return; + } + + /* Compute the position relative to the reference marker position. Multiply by the size to get + * the position in pixel space. */ + const float2 reference_marker_position = compute_reference_marker_position(track); + const float2 position = (current_marker_position - reference_marker_position) * float2(size); + + if (should_compute_x) { + Result &result = get_result("X"); + result.allocate_single_value(); + result.set_float_value(position.x); + } + + if (should_compute_y) { + Result &result = get_result("Y"); + result.allocate_single_value(); + result.set_float_value(position.y); + } + } + + void execute_speed(MovieTrackingTrack *track, float2 current_marker_position, int2 size) + { + if (!should_compute_output("Speed")) { + return; + } + + /* Compute the speed as the difference between the previous marker position and the current + * marker position. Notice that we compute the speed from the current to the previous position, + * not the other way around. */ + const float2 previous_marker_position = compute_temporally_neighbouring_marker_position( + track, current_marker_position, -1); + const float2 speed_toward_previous = previous_marker_position - current_marker_position; + + /* Compute the speed as the difference between the current marker position and the next marker + * position. */ + const float2 next_marker_position = compute_temporally_neighbouring_marker_position( + track, current_marker_position, 1); + const float2 speed_toward_next = current_marker_position - next_marker_position; + + /* Encode both speeds in a 4D vector. Multiply by the size to get the speed in pixel space. */ + const float4 speed = float4(speed_toward_previous, speed_toward_next) * float4(size, size); + + Result &result = get_result("Speed"); + result.allocate_single_value(); + result.set_vector_value(speed); + } + + void execute_invalid() + { + if (should_compute_output("X")) { + Result &result = get_result("X"); + result.allocate_single_value(); + result.set_float_value(0.0f); + } + if (should_compute_output("Y")) { + Result &result = get_result("Y"); + result.allocate_single_value(); + result.set_float_value(0.0f); + } + if (should_compute_output("Speed")) { + Result &result = get_result("Speed"); + result.allocate_single_value(); + result.set_vector_value(float4(0.0f)); + } + } + + /* Compute the position of the marker that is delta time away from the evaluation frame. If no + * marker exist for that particular frame or is disabled, the current marker position is + * returned. This is useful for computing the speed by providing small negative and positive + * delta times. */ + float2 compute_temporally_neighbouring_marker_position(MovieTrackingTrack *track, + float2 current_marker_position, + int time_delta) + { + const int local_frame_number = BKE_movieclip_remap_scene_to_clip_frame( + get_movie_clip(), get_frame() + time_delta); + MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, local_frame_number); + + if (marker == nullptr || marker->flag & MARKER_DISABLED) { + return current_marker_position; + } + + return float2(marker->pos); + } + + /* Compute the position of the reference marker which the output position will be computed + * relative to. For non-relative modes, this is just the zero origin or the tracking space. See + * the get_mode() method for more information. */ + float2 compute_reference_marker_position(MovieTrackingTrack *track) + { + switch (get_mode()) { + case CMP_NODE_TRACK_POSITION_RELATIVE_START: + return compute_first_marker_position(track); + case CMP_NODE_TRACK_POSITION_RELATIVE_FRAME: + return compute_marker_position_at_frame(track, get_relative_frame()); + default: + return float2(0.0f); + } + } + + /* Compute the position of the first non-disabled marker in the track. */ + float2 compute_first_marker_position(MovieTrackingTrack *track) + { + for (const int i : IndexRange(track->markersnr)) { + MovieTrackingMarker &marker = track->markers[i]; + + if (marker.flag & MARKER_DISABLED) { + continue; + } + + return float2(marker.pos); + } + + return float2(0.0f); + } + + /* Compute the marker position at the given frame, if no such marker exist, return the position + * of the temporally nearest marker before it, if no such marker exist, return the position of + * the temporally nearest marker after it. */ + float2 compute_marker_position_at_frame(MovieTrackingTrack *track, int frame) + { + const int local_frame_number = BKE_movieclip_remap_scene_to_clip_frame(get_movie_clip(), + frame); + MovieTrackingMarker *marker = BKE_tracking_marker_get(track, local_frame_number); + return float2(marker->pos); + } + + /* Get the movie tracking track corresponding to the given object and track names. If no such + * track exist, return nullptr. */ + MovieTrackingTrack *get_movie_tracking_track() + { + MovieClip *movie_clip = get_movie_clip(); + if (!movie_clip) { + return nullptr; + } + + MovieTracking *movie_tracking = &movie_clip->tracking; + + MovieTrackingObject *movie_tracking_object = BKE_tracking_object_get_named( + movie_tracking, node_storage(bnode()).tracking_object); + if (!movie_tracking_object) { + return nullptr; + } + + return BKE_tracking_object_find_track_with_name(movie_tracking_object, + node_storage(bnode()).track_name); + } + + /* Get the size of the movie clip at the evaluation frame. This is constant for all frames in + * most cases. */ + int2 get_size() + { + MovieClipUser user = *DNA_struct_default_get(MovieClipUser); + BKE_movieclip_user_set_frame(&user, get_frame()); + + int2 size; + BKE_movieclip_get_size(get_movie_clip(), &user, &size.x, &size.y); + + return size; + } + + /* In the CMP_NODE_TRACK_POSITION_RELATIVE_FRAME mode, this represents the offset that will be + * added to the current scene frame. See the get_mode() method for more information. */ + int get_relative_frame() + { + return bnode().custom2; + } + + /* Get the frame where the marker will be retrieved. This is the absolute frame for the absolute + * mode and the current scene frame otherwise. */ + int get_frame() + { + if (get_mode() == CMP_NODE_TRACK_POSITION_ABSOLUTE_FRAME) { + return get_absolute_frame(); + } + + return context().get_frame_number(); + } + + /* In the CMP_NODE_TRACK_POSITION_ABSOLUTE_FRAME mode, this represents the frame where the marker + * will be retrieved. See the get_mode() method for more information. */ + int get_absolute_frame() + { + return bnode().custom2; + } + + /* CMP_NODE_TRACK_POSITION_ABSOLUTE: + * Returns the position and speed of the marker at the current scene frame relative to the zero + * origin of the tracking space. + * + * CMP_NODE_TRACK_POSITION_RELATIVE_START: + * Returns the position and speed of the marker at the current scene frame relative to the + * position of the first non-disabled marker in the track. + * + * CMP_NODE_TRACK_POSITION_RELATIVE_FRAME: + * Returns the position and speed of the marker at the current scene frame relative to the + * position of the marker at the current scene frame plus the user given relative frame. + * + * CMP_NODE_TRACK_POSITION_ABSOLUTE_FRAME: + * Returns the position and speed of the marker at the given absolute frame. */ + CMPNodeTrackPositionMode get_mode() + { + return static_cast(bnode().custom1); + } + + MovieClip *get_movie_clip() + { + return (MovieClip *)bnode().id; } };