This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenkernel/intern/mask.c

1938 lines
50 KiB
C
Raw Normal View History

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2012 Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation,
* Sergey Sharybin,
* Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenkernel/intern/mask.c
* \ingroup bke
*/
#include <stddef.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLF_translation.h"
#include "DNA_mask_types.h"
#include "DNA_node_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_sequence_types.h"
#include "BKE_curve.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_mask.h"
#include "BKE_node.h"
#include "BKE_sequencer.h"
#include "BKE_tracking.h"
#include "BKE_movieclip.h"
#include "BKE_image.h"
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
#include "NOD_composite.h"
static MaskSplinePoint *mask_spline_point_next(MaskSpline *spline, MaskSplinePoint *points_array, MaskSplinePoint *point)
{
if (point == &points_array[spline->tot_point - 1]) {
if (spline->flag & MASK_SPLINE_CYCLIC) {
return &points_array[0];
}
2012-06-27 18:29:47 +00:00
else {
return NULL;
}
}
else {
return point + 1;
}
}
static MaskSplinePoint *mask_spline_point_prev(MaskSpline *spline, MaskSplinePoint *points_array, MaskSplinePoint *point)
{
if (point == points_array) {
if (spline->flag & MASK_SPLINE_CYCLIC) {
return &points_array[spline->tot_point - 1];
}
2012-06-27 18:29:47 +00:00
else {
return NULL;
}
}
else {
return point - 1;
}
}
BezTriple *BKE_mask_spline_point_next_bezt(MaskSpline *spline, MaskSplinePoint *points_array, MaskSplinePoint *point)
{
if (point == &points_array[spline->tot_point - 1]) {
if (spline->flag & MASK_SPLINE_CYCLIC) {
return &(points_array[0].bezt);
}
2012-06-27 18:29:47 +00:00
else {
return NULL;
}
}
else {
return &((point + 1))->bezt;
}
}
MaskSplinePoint *BKE_mask_spline_point_array(MaskSpline *spline)
{
return spline->points_deform ? spline->points_deform : spline->points;
}
MaskSplinePoint *BKE_mask_spline_point_array_from_point(MaskSpline *spline, MaskSplinePoint *point_ref)
{
if ((point_ref >= spline->points) && (point_ref < &spline->points[spline->tot_point])) {
return spline->points;
}
if ((point_ref >= spline->points_deform) && (point_ref < &spline->points_deform[spline->tot_point])) {
return spline->points_deform;
}
BLI_assert(!"wrong array");
return NULL;
}
/* mask layers */
MaskLayer *BKE_mask_layer_new(Mask *mask, const char *name)
{
MaskLayer *masklay = MEM_callocN(sizeof(MaskLayer), __func__);
if (name && name[0])
BLI_strncpy(masklay->name, name, sizeof(masklay->name));
else
strcpy(masklay->name, "MaskLayer");
BLI_addtail(&mask->masklayers, masklay);
BKE_mask_layer_unique_name(mask, masklay);
mask->masklay_tot++;
masklay->blend = MASK_BLEND_MERGE_ADD;
masklay->alpha = 1.0f;
return masklay;
}
/* note: may still be hidden, caller needs to check */
MaskLayer *BKE_mask_layer_active(Mask *mask)
{
return BLI_findlink(&mask->masklayers, mask->masklay_act);
}
void BKE_mask_layer_active_set(Mask *mask, MaskLayer *masklay)
{
mask->masklay_act = BLI_findindex(&mask->masklayers, masklay);
}
void BKE_mask_layer_remove(Mask *mask, MaskLayer *masklay)
{
BLI_remlink(&mask->masklayers, masklay);
BKE_mask_layer_free(masklay);
mask->masklay_tot--;
if (mask->masklay_act >= mask->masklay_tot)
mask->masklay_act = mask->masklay_tot - 1;
}
void BKE_mask_layer_unique_name(Mask *mask, MaskLayer *masklay)
{
BLI_uniquename(&mask->masklayers, masklay, DATA_("MaskLayer"), '.', offsetof(MaskLayer, name),
sizeof(masklay->name));
}
MaskLayer *BKE_mask_layer_copy(MaskLayer *masklay)
{
MaskLayer *masklay_new;
MaskSpline *spline;
masklay_new = MEM_callocN(sizeof(MaskLayer), "new mask layer");
BLI_strncpy(masklay_new->name, masklay->name, sizeof(masklay_new->name));
masklay_new->alpha = masklay->alpha;
masklay_new->blend = masklay->blend;
masklay_new->blend_flag = masklay->blend_flag;
masklay_new->flag = masklay->flag;
masklay_new->restrictflag = masklay->restrictflag;
for (spline = masklay->splines.first; spline; spline = spline->next) {
MaskSpline *spline_new = BKE_mask_spline_copy(spline);
BLI_addtail(&masklay_new->splines, spline_new);
}
/* correct animation */
if (masklay->splines_shapes.first) {
MaskLayerShape *masklay_shape;
MaskLayerShape *masklay_shape_new;
for (masklay_shape = masklay->splines_shapes.first;
masklay_shape;
masklay_shape = masklay_shape->next)
{
masklay_shape_new = MEM_callocN(sizeof(MaskLayerShape), "new mask layer shape");
masklay_shape_new->data = MEM_dupallocN(masklay_shape->data);
masklay_shape_new->tot_vert = masklay_shape->tot_vert;
masklay_shape_new->flag = masklay_shape->flag;
masklay_shape_new->frame = masklay_shape->frame;
BLI_addtail(&masklay_new->splines_shapes, masklay_shape_new);
}
}
return masklay_new;
}
void BKE_mask_layer_copy_list(ListBase *masklayers_new, ListBase *masklayers)
{
MaskLayer *layer;
for (layer = masklayers->first; layer; layer = layer->next) {
MaskLayer *layer_new = BKE_mask_layer_copy(layer);
BLI_addtail(masklayers_new, layer_new);
}
}
/* splines */
MaskSpline *BKE_mask_spline_add(MaskLayer *masklay)
{
MaskSpline *spline;
spline = MEM_callocN(sizeof(MaskSpline), "new mask spline");
BLI_addtail(&masklay->splines, spline);
/* spline shall have one point at least */
spline->points = MEM_callocN(sizeof(MaskSplinePoint), "new mask spline point");
spline->tot_point = 1;
/* cyclic shapes are more usually used */
// spline->flag |= MASK_SPLINE_CYCLIC; // disable because its not so nice for drawing. could be done differently
2012-07-16 13:12:52 +00:00
spline->weight_interp = MASK_SPLINE_INTERP_EASE;
BKE_mask_parent_init(&spline->parent);
return spline;
}
bool BKE_mask_spline_remove(MaskLayer *mask_layer, MaskSpline *spline)
{
if (BLI_remlink_safe(&mask_layer->splines, spline) == FALSE) {
return false;
}
BKE_mask_spline_free(spline);
return true;
}
void BKE_mask_point_direction_switch(MaskSplinePoint *point)
{
const int tot_uw = point->tot_uw;
const int tot_uw_half = tot_uw / 2;
int i;
float co_tmp[2];
/* swap handles */
copy_v2_v2(co_tmp, point->bezt.vec[0]);
copy_v2_v2(point->bezt.vec[0], point->bezt.vec[2]);
copy_v2_v2(point->bezt.vec[2], co_tmp);
/* in this case the flags are unlikely to be different but swap anyway */
SWAP(char, point->bezt.f1, point->bezt.f3);
SWAP(char, point->bezt.h1, point->bezt.h2);
/* swap UW's */
if (tot_uw > 1) {
/* count */
for (i = 0; i < tot_uw_half; i++) {
MaskSplinePointUW *uw_a = &point->uw[i];
MaskSplinePointUW *uw_b = &point->uw[tot_uw - (i + 1)];
SWAP(MaskSplinePointUW, *uw_a, *uw_b);
}
}
for (i = 0; i < tot_uw; i++) {
MaskSplinePointUW *uw = &point->uw[i];
uw->u = 1.0f - uw->u;
}
}
void BKE_mask_spline_direction_switch(MaskLayer *masklay, MaskSpline *spline)
{
const int tot_point = spline->tot_point;
const int tot_point_half = tot_point / 2;
int i, i_prev;
if (tot_point < 2) {
return;
}
/* count */
for (i = 0; i < tot_point_half; i++) {
MaskSplinePoint *point_a = &spline->points[i];
MaskSplinePoint *point_b = &spline->points[tot_point - (i + 1)];
SWAP(MaskSplinePoint, *point_a, *point_b);
}
/* correct UW's */
i_prev = tot_point - 1;
for (i = 0; i < tot_point; i++) {
BKE_mask_point_direction_switch(&spline->points[i]);
SWAP(MaskSplinePointUW *, spline->points[i].uw, spline->points[i_prev].uw);
SWAP(int, spline->points[i].tot_uw, spline->points[i_prev].tot_uw);
i_prev = i;
}
/* correct animation */
if (masklay->splines_shapes.first) {
MaskLayerShape *masklay_shape;
const int spline_index = BKE_mask_layer_shape_spline_to_index(masklay, spline);
for (masklay_shape = masklay->splines_shapes.first;
masklay_shape;
masklay_shape = masklay_shape->next)
{
MaskLayerShapeElem *fp_arr = (MaskLayerShapeElem *)masklay_shape->data;
for (i = 0; i < tot_point_half; i++) {
MaskLayerShapeElem *fp_a = &fp_arr[spline_index + (i) ];
MaskLayerShapeElem *fp_b = &fp_arr[spline_index + (tot_point - (i + 1))];
SWAP(MaskLayerShapeElem, *fp_a, *fp_b);
}
}
}
}
float BKE_mask_spline_project_co(MaskSpline *spline, MaskSplinePoint *point,
float start_u, const float co[2], const eMaskSign sign)
{
const float proj_eps = 1e-3;
const float proj_eps_squared = proj_eps * proj_eps;
const int N = 1000;
float u = -1.0f, du = 1.0f / N, u1 = start_u, u2 = start_u;
float ang = -1.0f;
BLI_assert(ABS(sign) <= 1); /* (-1, 0, 1) */
while (u1 > 0.0f || u2 < 1.0f) {
float n1[2], n2[2], co1[2], co2[2];
float v1[2], v2[2];
float ang1, ang2;
if (u1 >= 0.0f) {
BKE_mask_point_segment_co(spline, point, u1, co1);
BKE_mask_point_normal(spline, point, u1, n1);
sub_v2_v2v2(v1, co, co1);
if ((sign == MASK_PROJ_ANY) ||
((sign == MASK_PROJ_NEG) && (dot_v2v2(v1, n1) <= 0.0f)) ||
((sign == MASK_PROJ_POS) && (dot_v2v2(v1, n1) >= 0.0f)))
{
if (len_squared_v2(v1) > proj_eps_squared) {
ang1 = angle_v2v2(v1, n1);
if (ang1 > (float)M_PI / 2.0f)
ang1 = (float)M_PI - ang1;
if (ang < 0.0f || ang1 < ang) {
ang = ang1;
u = u1;
}
}
else {
u = u1;
break;
}
}
}
if (u2 <= 1.0f) {
BKE_mask_point_segment_co(spline, point, u2, co2);
BKE_mask_point_normal(spline, point, u2, n2);
sub_v2_v2v2(v2, co, co2);
if ((sign == MASK_PROJ_ANY) ||
((sign == MASK_PROJ_NEG) && (dot_v2v2(v2, n2) <= 0.0f)) ||
((sign == MASK_PROJ_POS) && (dot_v2v2(v2, n2) >= 0.0f)))
{
if (len_squared_v2(v2) > proj_eps_squared) {
ang2 = angle_v2v2(v2, n2);
if (ang2 > (float)M_PI / 2.0f)
ang2 = (float)M_PI - ang2;
if (ang2 < ang) {
ang = ang2;
u = u2;
}
}
else {
u = u2;
break;
}
}
}
u1 -= du;
u2 += du;
}
return u;
}
/* point */
int BKE_mask_point_has_handle(MaskSplinePoint *point)
{
BezTriple *bezt = &point->bezt;
return bezt->h1 == HD_ALIGN;
}
void BKE_mask_point_handle(MaskSplinePoint *point, float handle[2])
{
float vec[2];
sub_v2_v2v2(vec, point->bezt.vec[0], point->bezt.vec[1]);
handle[0] = (point->bezt.vec[1][0] + vec[1]);
handle[1] = (point->bezt.vec[1][1] - vec[0]);
}
void BKE_mask_point_set_handle(MaskSplinePoint *point, float loc[2], bool keep_direction,
float orig_handle[2], float orig_vec[3][3])
{
BezTriple *bezt = &point->bezt;
float v1[2], v2[2], vec[2];
if (keep_direction) {
sub_v2_v2v2(v1, loc, orig_vec[1]);
sub_v2_v2v2(v2, orig_handle, orig_vec[1]);
project_v2_v2v2(vec, v1, v2);
if (dot_v2v2(v2, vec) > 0) {
float len = len_v2(vec);
sub_v2_v2v2(v1, orig_vec[0], orig_vec[1]);
mul_v2_fl(v1, len / len_v2(v1));
add_v2_v2v2(bezt->vec[0], bezt->vec[1], v1);
sub_v2_v2v2(bezt->vec[2], bezt->vec[1], v1);
}
else {
copy_v3_v3(bezt->vec[0], bezt->vec[1]);
copy_v3_v3(bezt->vec[2], bezt->vec[1]);
}
}
else {
sub_v2_v2v2(v1, loc, bezt->vec[1]);
v2[0] = -v1[1];
v2[1] = v1[0];
add_v2_v2v2(bezt->vec[0], bezt->vec[1], v2);
sub_v2_v2v2(bezt->vec[2], bezt->vec[1], v2);
}
}
void BKE_mask_point_segment_co(MaskSpline *spline, MaskSplinePoint *point, float u, float co[2])
{
MaskSplinePoint *points_array = BKE_mask_spline_point_array_from_point(spline, point);
2012-06-05 11:28:54 +00:00
BezTriple *bezt = &point->bezt, *bezt_next;
float q0[2], q1[2], q2[2], r0[2], r1[2];
bezt_next = BKE_mask_spline_point_next_bezt(spline, points_array, point);
2012-06-05 11:28:54 +00:00
if (!bezt_next) {
copy_v2_v2(co, bezt->vec[1]);
return;
}
interp_v2_v2v2(q0, bezt->vec[1], bezt->vec[2], u);
2012-06-05 11:28:54 +00:00
interp_v2_v2v2(q1, bezt->vec[2], bezt_next->vec[0], u);
interp_v2_v2v2(q2, bezt_next->vec[0], bezt_next->vec[1], u);
interp_v2_v2v2(r0, q0, q1, u);
interp_v2_v2v2(r1, q1, q2, u);
interp_v2_v2v2(co, r0, r1, u);
}
void BKE_mask_point_normal(MaskSpline *spline, MaskSplinePoint *point, float u, float n[2])
{
MaskSplinePoint *points_array = BKE_mask_spline_point_array_from_point(spline, point);
2012-06-05 11:28:54 +00:00
BezTriple *bezt = &point->bezt, *bezt_next;
float q0[2], q1[2], q2[2], r0[2], r1[2], vec[2];
bezt_next = BKE_mask_spline_point_next_bezt(spline, points_array, point);
2012-06-05 11:28:54 +00:00
if (!bezt_next) {
BKE_mask_point_handle(point, vec);
sub_v2_v2v2(n, vec, bezt->vec[1]);
normalize_v2(n);
return;
}
interp_v2_v2v2(q0, bezt->vec[1], bezt->vec[2], u);
2012-06-05 11:28:54 +00:00
interp_v2_v2v2(q1, bezt->vec[2], bezt_next->vec[0], u);
interp_v2_v2v2(q2, bezt_next->vec[0], bezt_next->vec[1], u);
interp_v2_v2v2(r0, q0, q1, u);
interp_v2_v2v2(r1, q1, q2, u);
sub_v2_v2v2(vec, r1, r0);
n[0] = -vec[1];
n[1] = vec[0];
normalize_v2(n);
}
static float mask_point_interp_weight(BezTriple *bezt, BezTriple *bezt_next, const float u)
{
return (bezt->weight * (1.0f - u)) + (bezt_next->weight * u);
}
float BKE_mask_point_weight_scalar(MaskSpline *spline, MaskSplinePoint *point, const float u)
{
MaskSplinePoint *points_array = BKE_mask_spline_point_array_from_point(spline, point);
BezTriple *bezt = &point->bezt, *bezt_next;
bezt_next = BKE_mask_spline_point_next_bezt(spline, points_array, point);
if (!bezt_next) {
return bezt->weight;
}
else if (u <= 0.0f) {
return bezt->weight;
}
else if (u >= 1.0f) {
return bezt_next->weight;
}
else {
return mask_point_interp_weight(bezt, bezt_next, u);
}
}
float BKE_mask_point_weight(MaskSpline *spline, MaskSplinePoint *point, const float u)
{
MaskSplinePoint *points_array = BKE_mask_spline_point_array_from_point(spline, point);
BezTriple *bezt = &point->bezt, *bezt_next;
bezt_next = BKE_mask_spline_point_next_bezt(spline, points_array, point);
if (!bezt_next) {
return bezt->weight;
}
else if (u <= 0.0f) {
return bezt->weight;
}
else if (u >= 1.0f) {
return bezt_next->weight;
}
else {
2012-06-07 18:21:07 +00:00
float cur_u = 0.0f, cur_w = 0.0f, next_u = 0.0f, next_w = 0.0f, fac; /* Quite warnings */
int i;
for (i = 0; i <= point->tot_uw; i++) {
if (i == 0) {
cur_u = 0.0f;
cur_w = 1.0f; /* mask_point_interp_weight will scale it */
}
else {
cur_u = point->uw[i - 1].u;
cur_w = point->uw[i - 1].w;
}
if (i == point->tot_uw) {
next_u = 1.0f;
next_w = 1.0f; /* mask_point_interp_weight will scale it */
}
else {
next_u = point->uw[i].u;
next_w = point->uw[i].w;
}
if (u >= cur_u && u <= next_u) {
break;
}
}
fac = (u - cur_u) / (next_u - cur_u);
cur_w *= mask_point_interp_weight(bezt, bezt_next, cur_u);
next_w *= mask_point_interp_weight(bezt, bezt_next, next_u);
if (spline->weight_interp == MASK_SPLINE_INTERP_EASE) {
return cur_w + (next_w - cur_w) * (3.0f * fac * fac - 2.0f * fac * fac * fac);
}
else {
return (1.0f - fac) * cur_w + fac * next_w;
}
}
}
MaskSplinePointUW *BKE_mask_point_sort_uw(MaskSplinePoint *point, MaskSplinePointUW *uw)
{
if (point->tot_uw > 1) {
int idx = uw - point->uw;
if (idx > 0 && point->uw[idx - 1].u > uw->u) {
while (idx > 0 && point->uw[idx - 1].u > point->uw[idx].u) {
SWAP(MaskSplinePointUW, point->uw[idx - 1], point->uw[idx]);
idx--;
}
}
if (idx < point->tot_uw - 1 && point->uw[idx + 1].u < uw->u) {
while (idx < point->tot_uw - 1 && point->uw[idx + 1].u < point->uw[idx].u) {
SWAP(MaskSplinePointUW, point->uw[idx + 1], point->uw[idx]);
idx++;
}
}
return &point->uw[idx];
}
return uw;
}
void BKE_mask_point_add_uw(MaskSplinePoint *point, float u, float w)
{
if (!point->uw)
point->uw = MEM_mallocN(sizeof(*point->uw), "mask point uw");
else
point->uw = MEM_reallocN(point->uw, (point->tot_uw + 1) * sizeof(*point->uw));
point->uw[point->tot_uw].u = u;
point->uw[point->tot_uw].w = w;
point->uw[point->tot_uw].flag = 0;
point->tot_uw++;
BKE_mask_point_sort_uw(point, &point->uw[point->tot_uw - 1]);
}
void BKE_mask_point_select_set(MaskSplinePoint *point, const short do_select)
{
int i;
if (do_select) {
MASKPOINT_SEL_ALL(point);
}
else {
MASKPOINT_DESEL_ALL(point);
}
for (i = 0; i < point->tot_uw; i++) {
if (do_select) {
point->uw[i].flag |= SELECT;
}
else {
point->uw[i].flag &= ~SELECT;
}
}
}
void BKE_mask_point_select_set_handle(MaskSplinePoint *point, const short do_select)
{
if (do_select) {
MASKPOINT_SEL_HANDLE(point);
}
else {
MASKPOINT_DESEL_HANDLE(point);
}
}
/* only mask block itself */
static Mask *mask_alloc(Main *bmain, const char *name)
{
Mask *mask;
mask = BKE_libblock_alloc(bmain, ID_MSK, name);
mask->id.flag |= LIB_FAKEUSER;
return mask;
}
Mask *BKE_mask_new(Main *bmain, const char *name)
{
Mask *mask;
char mask_name[MAX_ID_NAME - 2];
if (name && name[0])
BLI_strncpy(mask_name, name, sizeof(mask_name));
else
strcpy(mask_name, "Mask");
mask = mask_alloc(bmain, mask_name);
2012-06-07 18:24:36 +00:00
/* arbitrary defaults */
mask->sfra = 1;
mask->efra = 100;
return mask;
}
2012-07-27 08:18:11 +00:00
Mask *BKE_mask_copy_nolib(Mask *mask)
{
Mask *mask_new;
mask_new = MEM_dupallocN(mask);
/*take care here! - we may want to copy anim data */
mask_new->adt = NULL;
mask_new->masklayers.first = NULL;
mask_new->masklayers.last = NULL;
BKE_mask_layer_copy_list(&mask_new->masklayers, &mask->masklayers);
/* enable fake user by default */
if (!(mask_new->id.flag & LIB_FAKEUSER)) {
mask_new->id.flag |= LIB_FAKEUSER;
mask_new->id.us++;
}
return mask_new;
}
Mask *BKE_mask_copy(Mask *mask)
{
Mask *mask_new;
mask_new = BKE_libblock_copy(&mask->id);
mask_new->masklayers.first = NULL;
mask_new->masklayers.last = NULL;
BKE_mask_layer_copy_list(&mask_new->masklayers, &mask->masklayers);
/* enable fake user by default */
if (!(mask_new->id.flag & LIB_FAKEUSER)) {
mask_new->id.flag |= LIB_FAKEUSER;
mask_new->id.us++;
}
return mask_new;
}
void BKE_mask_point_free(MaskSplinePoint *point)
{
if (point->uw)
MEM_freeN(point->uw);
}
void BKE_mask_spline_free(MaskSpline *spline)
{
int i = 0;
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point;
point = &spline->points[i];
BKE_mask_point_free(point);
if (spline->points_deform) {
point = &spline->points_deform[i];
BKE_mask_point_free(point);
}
}
MEM_freeN(spline->points);
if (spline->points_deform) {
MEM_freeN(spline->points_deform);
}
MEM_freeN(spline);
}
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
void BKE_mask_spline_free_list(ListBase *splines)
{
MaskSpline *spline = splines->first;
while (spline) {
MaskSpline *next_spline = spline->next;
BLI_remlink(splines, spline);
BKE_mask_spline_free(spline);
spline = next_spline;
}
}
static MaskSplinePoint *mask_spline_points_copy(MaskSplinePoint *points, int tot_point)
{
MaskSplinePoint *npoints;
int i;
npoints = MEM_dupallocN(points);
for (i = 0; i < tot_point; i++) {
MaskSplinePoint *point = &npoints[i];
if (point->uw)
point->uw = MEM_dupallocN(point->uw);
}
return npoints;
}
MaskSpline *BKE_mask_spline_copy(MaskSpline *spline)
{
MaskSpline *nspline = MEM_callocN(sizeof(MaskSpline), "new spline");
*nspline = *spline;
nspline->points_deform = NULL;
nspline->points = mask_spline_points_copy(spline->points, spline->tot_point);
if (spline->points_deform) {
nspline->points_deform = mask_spline_points_copy(spline->points_deform, spline->tot_point);
}
return nspline;
}
/* note: does NOT add to the list */
MaskLayerShape *BKE_mask_layer_shape_alloc(MaskLayer *masklay, const int frame)
{
MaskLayerShape *masklay_shape;
int tot_vert = BKE_mask_layer_shape_totvert(masklay);
masklay_shape = MEM_mallocN(sizeof(MaskLayerShape), __func__);
masklay_shape->frame = frame;
masklay_shape->tot_vert = tot_vert;
masklay_shape->data = MEM_mallocN(tot_vert * sizeof(float) * MASK_OBJECT_SHAPE_ELEM_SIZE, __func__);
return masklay_shape;
}
void BKE_mask_layer_shape_free(MaskLayerShape *masklay_shape)
{
2012-08-06 13:04:40 +00:00
if (masklay_shape->data) {
MEM_freeN(masklay_shape->data);
}
MEM_freeN(masklay_shape);
}
/** \brief Free all animation keys for a mask layer
*/
void BKE_mask_layer_free_shapes(MaskLayer *masklay)
{
MaskLayerShape *masklay_shape;
/* free animation data */
masklay_shape = masklay->splines_shapes.first;
while (masklay_shape) {
MaskLayerShape *next_masklay_shape = masklay_shape->next;
BLI_remlink(&masklay->splines_shapes, masklay_shape);
BKE_mask_layer_shape_free(masklay_shape);
masklay_shape = next_masklay_shape;
}
}
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
void BKE_mask_layer_free(MaskLayer *masklay)
{
/* free splines */
BKE_mask_spline_free_list(&masklay->splines);
/* free animation data */
BKE_mask_layer_free_shapes(masklay);
MEM_freeN(masklay);
}
void BKE_mask_layer_free_list(ListBase *masklayers)
{
MaskLayer *masklay = masklayers->first;
while (masklay) {
MaskLayer *masklay_next = masklay->next;
BLI_remlink(masklayers, masklay);
BKE_mask_layer_free(masklay);
masklay = masklay_next;
}
}
/** free for temp copy, but don't manage unlinking from other pointers */
void BKE_mask_free_nolib(Mask *mask)
{
BKE_mask_layer_free_list(&mask->masklayers);
}
void BKE_mask_free(Main *bmain, Mask *mask)
{
bScreen *scr;
ScrArea *area;
SpaceLink *sl;
Scene *scene;
for (scr = bmain->screen.first; scr; scr = scr->id.next) {
for (area = scr->areabase.first; area; area = area->next) {
for (sl = area->spacedata.first; sl; sl = sl->next) {
switch (sl->spacetype) {
case SPACE_CLIP:
{
SpaceClip *sc = (SpaceClip *)sl;
if (sc->mask_info.mask == mask) {
sc->mask_info.mask = NULL;
}
break;
}
case SPACE_IMAGE:
{
SpaceImage *sima = (SpaceImage *)sl;
if (sima->mask_info.mask == mask) {
sima->mask_info.mask = NULL;
}
break;
}
}
}
}
}
for (scene = bmain->scene.first; scene; scene = scene->id.next) {
if (scene->ed) {
Sequence *seq;
SEQ_BEGIN (scene->ed, seq)
{
if (seq->mask == mask) {
seq->mask = NULL;
}
}
SEQ_END
}
}
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
FOREACH_NODETREE(bmain, ntree, id) {
BKE_node_tree_unlink_id((ID *)mask, ntree);
} FOREACH_NODETREE_END
/* free mask data */
BKE_mask_layer_free_list(&mask->masklayers);
}
void BKE_mask_coord_from_frame(float r_co[2], const float co[2], const float frame_size[2])
{
if (frame_size[0] == frame_size[1]) {
r_co[0] = co[0];
r_co[1] = co[1];
}
else if (frame_size[0] < frame_size[1]) {
r_co[0] = ((co[0] - 0.5f) * (frame_size[0] / frame_size[1])) + 0.5f;
r_co[1] = co[1];
}
else { /* (frame_size[0] > frame_size[1]) */
r_co[0] = co[0];
r_co[1] = ((co[1] - 0.5f) * (frame_size[1] / frame_size[0])) + 0.5f;
}
}
void BKE_mask_coord_from_movieclip(MovieClip *clip, MovieClipUser *user, float r_co[2], const float co[2])
{
float aspx, aspy;
float frame_size[2];
/* scaling for the clip */
BKE_movieclip_get_size_fl(clip, user, frame_size);
BKE_movieclip_get_aspect(clip, &aspx, &aspy);
frame_size[1] *= (aspy / aspx);
BKE_mask_coord_from_frame(r_co, co, frame_size);
}
void BKE_mask_coord_from_image(Image *image, ImageUser *iuser, float r_co[2], const float co[2])
{
float aspx, aspy;
float frame_size[2];
BKE_image_get_size_fl(image, iuser, frame_size);
BKE_image_get_aspect(image, &aspx, &aspy);
frame_size[1] *= (aspy / aspx);
BKE_mask_coord_from_frame(r_co, co, frame_size);
}
/* as above but divide */
void BKE_mask_coord_to_frame(float r_co[2], const float co[2], const float frame_size[2])
{
if (frame_size[0] == frame_size[1]) {
r_co[0] = co[0];
r_co[1] = co[1];
}
else if (frame_size[0] < frame_size[1]) {
r_co[0] = ((co[0] - 0.5f) / (frame_size[0] / frame_size[1])) + 0.5f;
r_co[1] = co[1];
}
else { /* (frame_size[0] > frame_size[1]) */
r_co[0] = co[0];
r_co[1] = ((co[1] - 0.5f) / (frame_size[1] / frame_size[0])) + 0.5f;
}
}
void BKE_mask_coord_to_movieclip(MovieClip *clip, MovieClipUser *user, float r_co[2], const float co[2])
{
float aspx, aspy;
float frame_size[2];
/* scaling for the clip */
BKE_movieclip_get_size_fl(clip, user, frame_size);
BKE_movieclip_get_aspect(clip, &aspx, &aspy);
frame_size[1] /= (aspy / aspx);
BKE_mask_coord_to_frame(r_co, co, frame_size);
}
void BKE_mask_coord_to_image(Image *image, ImageUser *iuser, float r_co[2], const float co[2])
{
float aspx, aspy;
float frame_size[2];
/* scaling for the clip */
BKE_image_get_size_fl(image, iuser, frame_size);
BKE_image_get_aspect(image, &aspx, &aspy);
frame_size[1] /= (aspy / aspx);
BKE_mask_coord_to_frame(r_co, co, frame_size);
}
void BKE_mask_point_parent_matrix_get(MaskSplinePoint *point, float ctime, float parent_matrix[3][3])
{
MaskParent *parent = &point->parent;
unit_m3(parent_matrix);
if (!parent) {
return;
}
if (parent->id_type == ID_MC) {
if (parent->id) {
MovieClip *clip = (MovieClip *) parent->id;
MovieTracking *tracking = (MovieTracking *) &clip->tracking;
MovieTrackingObject *ob = BKE_tracking_object_get_named(tracking, parent->parent);
if (ob) {
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
MovieClipUser user = {0};
float clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, ctime);
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
BKE_movieclip_user_set_frame(&user, ctime);
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
if (parent->type == MASK_PARENT_POINT_TRACK) {
MovieTrackingTrack *track = BKE_tracking_track_get_named(tracking, ob, parent->sub_parent);
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
if (track) {
float marker_position[2], parent_co[2];
BKE_tracking_marker_get_subframe_position(track, clip_framenr, marker_position);
BKE_mask_coord_from_movieclip(clip, &user, parent_co, marker_position);
sub_v2_v2v2(parent_matrix[2], parent_co, parent->parent_orig);
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
}
}
else /* if (parent->type == MASK_PARENT_PLANE_TRACK) */ {
MovieTrackingPlaneTrack *plane_track = BKE_tracking_plane_track_get_named(tracking, ob, parent->sub_parent);
if (plane_track) {
MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track, clip_framenr);
float aspx, aspy;
float frame_size[2], H[3][3], mask_from_clip_matrix[3][3], mask_to_clip_matrix[3][3];
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
BKE_tracking_homography_between_two_quads(parent->parent_corners_orig, plane_marker->corners, H);
unit_m3(mask_from_clip_matrix);
BKE_movieclip_get_size_fl(clip, &user, frame_size);
BKE_movieclip_get_aspect(clip, &aspx, &aspy);
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
frame_size[1] *= (aspy / aspx);
if (frame_size[0] == frame_size[1]) {
/* pass */
}
else if (frame_size[0] < frame_size[1]) {
mask_from_clip_matrix[0][0] = frame_size[1] / frame_size[0];
mask_from_clip_matrix[2][0] = -0.5f * (frame_size[1] / frame_size[0]) + 0.5f;
}
else { /* (frame_size[0] > frame_size[1]) */
mask_from_clip_matrix[1][1] = frame_size[1] / frame_size[0];
mask_from_clip_matrix[2][1] = -0.5f * (frame_size[1] / frame_size[0]) + 0.5f;
}
invert_m3_m3(mask_to_clip_matrix, mask_from_clip_matrix);
mul_serie_m3(parent_matrix, mask_to_clip_matrix, H, mask_from_clip_matrix, NULL, NULL, NULL, NULL, NULL);
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
}
}
}
}
}
}
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
static void mask_evaluate_apply_point_parent(MaskSplinePoint *point, float ctime)
{
float parent_matrix[3][3];
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
BKE_mask_point_parent_matrix_get(point, ctime, parent_matrix);
mul_m3_v2(parent_matrix, point->bezt.vec[0]);
mul_m3_v2(parent_matrix, point->bezt.vec[1]);
mul_m3_v2(parent_matrix, point->bezt.vec[2]);
}
2012-06-05 11:28:54 +00:00
static void mask_calc_point_handle(MaskSplinePoint *point, MaskSplinePoint *point_prev, MaskSplinePoint *point_next)
{
BezTriple *bezt = &point->bezt;
2012-06-05 11:28:54 +00:00
BezTriple *bezt_prev = NULL, *bezt_next = NULL;
//int handle_type = bezt->h1;
2012-06-05 11:28:54 +00:00
if (point_prev)
bezt_prev = &point_prev->bezt;
2012-06-05 11:28:54 +00:00
if (point_next)
bezt_next = &point_next->bezt;
#if 1
2012-06-05 11:28:54 +00:00
if (bezt_prev || bezt_next) {
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0);
}
#else
if (handle_type == HD_VECT) {
2012-06-05 11:28:54 +00:00
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0);
}
else if (handle_type == HD_AUTO) {
2012-06-05 11:28:54 +00:00
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0);
}
else if (handle_type == HD_ALIGN) {
float v1[3], v2[3];
float vec[3], h[3];
sub_v3_v3v3(v1, bezt->vec[0], bezt->vec[1]);
sub_v3_v3v3(v2, bezt->vec[2], bezt->vec[1]);
add_v3_v3v3(vec, v1, v2);
if (len_v3(vec) > 1e-3) {
h[0] = vec[1];
h[1] = -vec[0];
h[2] = 0.0f;
}
else {
copy_v3_v3(h, v1);
}
add_v3_v3v3(bezt->vec[0], bezt->vec[1], h);
sub_v3_v3v3(bezt->vec[2], bezt->vec[1], h);
}
#endif
}
2012-06-06 20:05:58 +00:00
void BKE_mask_get_handle_point_adjacent(MaskSpline *spline, MaskSplinePoint *point,
MaskSplinePoint **r_point_prev, MaskSplinePoint **r_point_next)
{
/* TODO, could avoid calling this at such low level */
MaskSplinePoint *points_array = BKE_mask_spline_point_array_from_point(spline, point);
*r_point_prev = mask_spline_point_prev(spline, points_array, point);
*r_point_next = mask_spline_point_next(spline, points_array, point);
}
/* calculates the tanget of a point by its previous and next
* (ignoring handles - as if its a poly line) */
2012-06-06 20:05:58 +00:00
void BKE_mask_calc_tangent_polyline(MaskSpline *spline, MaskSplinePoint *point, float t[2])
{
float tvec_a[2], tvec_b[2];
2012-06-05 11:28:54 +00:00
MaskSplinePoint *point_prev, *point_next;
2012-06-06 20:05:58 +00:00
BKE_mask_get_handle_point_adjacent(spline, point,
2012-06-05 11:28:54 +00:00
&point_prev, &point_next);
2012-06-05 11:28:54 +00:00
if (point_prev) {
sub_v2_v2v2(tvec_a, point->bezt.vec[1], point_prev->bezt.vec[1]);
normalize_v2(tvec_a);
}
else {
zero_v2(tvec_a);
}
2012-06-05 11:28:54 +00:00
if (point_next) {
sub_v2_v2v2(tvec_b, point_next->bezt.vec[1], point->bezt.vec[1]);
normalize_v2(tvec_b);
}
else {
zero_v2(tvec_b);
}
add_v2_v2v2(t, tvec_a, tvec_b);
normalize_v2(t);
}
2012-06-06 20:05:58 +00:00
void BKE_mask_calc_handle_point(MaskSpline *spline, MaskSplinePoint *point)
{
2012-06-05 11:28:54 +00:00
MaskSplinePoint *point_prev, *point_next;
2012-06-06 20:05:58 +00:00
BKE_mask_get_handle_point_adjacent(spline, point,
2012-06-05 11:28:54 +00:00
&point_prev, &point_next);
2012-06-05 11:28:54 +00:00
mask_calc_point_handle(point, point_prev, point_next);
}
2012-06-06 20:05:58 +00:00
void BKE_mask_calc_handle_adjacent_interp(MaskSpline *spline, MaskSplinePoint *point, const float u)
{
/* TODO! - make this interpolate between siblings - not always midpoint! */
int length_tot = 0;
float length_average = 0.0f;
float weight_average = 0.0f;
2012-06-05 11:28:54 +00:00
MaskSplinePoint *point_prev, *point_next;
BLI_assert(u >= 0.0f && u <= 1.0f);
2012-06-06 20:05:58 +00:00
BKE_mask_get_handle_point_adjacent(spline, point,
2012-06-05 11:28:54 +00:00
&point_prev, &point_next);
2012-06-05 11:28:54 +00:00
if (point_prev && point_next) {
length_average = ((len_v2v2(point_prev->bezt.vec[0], point_prev->bezt.vec[1]) * (1.0f - u)) +
(len_v2v2(point_next->bezt.vec[2], point_next->bezt.vec[1]) * u));
2012-06-05 11:28:54 +00:00
weight_average = (point_prev->bezt.weight * (1.0f - u) +
point_next->bezt.weight * u);
length_tot = 1;
}
else {
2012-06-05 11:28:54 +00:00
if (point_prev) {
length_average += len_v2v2(point_prev->bezt.vec[0], point_prev->bezt.vec[1]);
weight_average += point_prev->bezt.weight;
length_tot++;
}
2012-06-05 11:28:54 +00:00
if (point_next) {
length_average += len_v2v2(point_next->bezt.vec[2], point_next->bezt.vec[1]);
weight_average += point_next->bezt.weight;
length_tot++;
}
}
if (length_tot) {
length_average /= (float)length_tot;
weight_average /= (float)length_tot;
dist_ensure_v2_v2fl(point->bezt.vec[0], point->bezt.vec[1], length_average);
dist_ensure_v2_v2fl(point->bezt.vec[2], point->bezt.vec[1], length_average);
point->bezt.weight = weight_average;
}
}
/**
* \brief Resets auto handles even for non-auto bezier points
*
* Useful for giving sane defaults.
*/
2012-06-06 20:05:58 +00:00
void BKE_mask_calc_handle_point_auto(MaskSpline *spline, MaskSplinePoint *point,
const short do_recalc_length)
{
2012-06-05 11:28:54 +00:00
MaskSplinePoint *point_prev, *point_next;
const char h_back[2] = {point->bezt.h1, point->bezt.h2};
const float length_average = (do_recalc_length) ? 0.0f /* dummy value */ :
(len_v3v3(point->bezt.vec[0], point->bezt.vec[1]) +
len_v3v3(point->bezt.vec[1], point->bezt.vec[2])) / 2.0f;
2012-06-06 20:05:58 +00:00
BKE_mask_get_handle_point_adjacent(spline, point,
2012-06-05 11:28:54 +00:00
&point_prev, &point_next);
point->bezt.h1 = HD_AUTO;
point->bezt.h2 = HD_AUTO;
2012-06-05 11:28:54 +00:00
mask_calc_point_handle(point, point_prev, point_next);
point->bezt.h1 = h_back[0];
point->bezt.h2 = h_back[1];
/* preserve length by applying it back */
if (do_recalc_length == FALSE) {
dist_ensure_v2_v2fl(point->bezt.vec[0], point->bezt.vec[1], length_average);
dist_ensure_v2_v2fl(point->bezt.vec[2], point->bezt.vec[1], length_average);
}
}
void BKE_mask_layer_calc_handles(MaskLayer *masklay)
{
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
int i;
for (i = 0; i < spline->tot_point; i++) {
BKE_mask_calc_handle_point(spline, &spline->points[i]);
}
}
}
void BKE_mask_spline_ensure_deform(MaskSpline *spline)
{
int allocated_points = (MEM_allocN_len(spline->points_deform) / sizeof(*spline->points_deform));
// printf("SPLINE ALLOC %p %d\n", spline->points_deform, allocated_points);
if (spline->points_deform == NULL || allocated_points != spline->tot_point) {
// printf("alloc new deform spline\n");
if (spline->points_deform) {
int i;
for (i = 0; i < allocated_points; i++) {
MaskSplinePoint *point = &spline->points_deform[i];
BKE_mask_point_free(point);
}
MEM_freeN(spline->points_deform);
}
spline->points_deform = MEM_callocN(sizeof(*spline->points_deform) * spline->tot_point, __func__);
}
else {
// printf("alloc spline done\n");
}
}
void BKE_mask_layer_evaluate(MaskLayer *masklay, const float ctime, const bool do_newframe)
{
/* animation if available */
if (do_newframe) {
MaskLayerShape *masklay_shape_a;
MaskLayerShape *masklay_shape_b;
int found;
if ((found = BKE_mask_layer_shape_find_frame_range(masklay, ctime,
&masklay_shape_a, &masklay_shape_b)))
{
if (found == 1) {
#if 0
printf("%s: exact %d %d (%d)\n", __func__, (int)ctime, BLI_countlist(&masklay->splines_shapes),
masklay_shape_a->frame);
#endif
BKE_mask_layer_shape_to_mask(masklay, masklay_shape_a);
}
else if (found == 2) {
float w = masklay_shape_b->frame - masklay_shape_a->frame;
#if 0
printf("%s: tween %d %d (%d %d)\n", __func__, (int)ctime, BLI_countlist(&masklay->splines_shapes),
masklay_shape_a->frame, masklay_shape_b->frame);
#endif
BKE_mask_layer_shape_to_mask_interp(masklay, masklay_shape_a, masklay_shape_b,
(ctime - masklay_shape_a->frame) / w);
}
else {
/* always fail, should never happen */
BLI_assert(found == 2);
}
}
}
/* animation done... */
BKE_mask_layer_calc_handles(masklay);
/* update deform */
{
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
int i;
int need_handle_recalc = FALSE;
BKE_mask_spline_ensure_deform(spline);
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
MaskSplinePoint *point_deform = &spline->points_deform[i];
BKE_mask_point_free(point_deform);
*point_deform = *point;
point_deform->uw = point->uw ? MEM_dupallocN(point->uw) : NULL;
Merge plane track feature from tomato branch This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
2013-08-16 09:46:30 +00:00
mask_evaluate_apply_point_parent(point_deform, ctime);
if (ELEM(point->bezt.h1, HD_AUTO, HD_VECT)) {
need_handle_recalc = TRUE;
}
}
/* if the spline has auto or vector handles, these need to be recalculated after deformation */
if (need_handle_recalc) {
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point_deform = &spline->points_deform[i];
if (ELEM(point_deform->bezt.h1, HD_AUTO, HD_VECT)) {
BKE_mask_calc_handle_point(spline, point_deform);
}
}
}
/* end extra calc handles loop */
}
}
}
void BKE_mask_evaluate(Mask *mask, const float ctime, const bool do_newframe)
{
MaskLayer *masklay;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
BKE_mask_layer_evaluate(masklay, ctime, do_newframe);
}
}
/* the purpose of this function is to ensure spline->points_deform is never out of date.
* for now re-evaluate all. eventually this might work differently */
void BKE_mask_update_display(Mask *mask, float ctime)
{
#if 0
MaskLayer *masklay;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
if (spline->points_deform) {
int i = 0;
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point;
if (spline->points_deform) {
point = &spline->points_deform[i];
BKE_mask_point_free(point);
}
}
if (spline->points_deform) {
MEM_freeN(spline->points_deform);
}
spline->points_deform = NULL;
}
}
}
#endif
BKE_mask_evaluate(mask, ctime, false);
}
void BKE_mask_evaluate_all_masks(Main *bmain, float ctime, const bool do_newframe)
{
Mask *mask;
for (mask = bmain->mask.first; mask; mask = mask->id.next) {
BKE_mask_evaluate(mask, ctime, do_newframe);
}
}
void BKE_mask_update_scene(Main *bmain, Scene *scene)
{
Mask *mask;
for (mask = bmain->mask.first; mask; mask = mask->id.next) {
if (mask->id.flag & (LIB_ID_RECALC | LIB_ID_RECALC_DATA)) {
bool do_new_frame = (mask->id.flag & LIB_ID_RECALC_DATA) != 0;
BKE_mask_evaluate_all_masks(bmain, CFRA, do_new_frame);
}
}
}
void BKE_mask_parent_init(MaskParent *parent)
{
parent->id_type = ID_MC;
}
/* *** own animation/shapekey implementation ***
* BKE_mask_layer_shape_XXX */
int BKE_mask_layer_shape_totvert(MaskLayer *masklay)
{
int tot = 0;
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
tot += spline->tot_point;
}
return tot;
}
static void mask_layer_shape_from_mask_point(BezTriple *bezt, float fp[MASK_OBJECT_SHAPE_ELEM_SIZE])
{
copy_v2_v2(&fp[0], bezt->vec[0]);
copy_v2_v2(&fp[2], bezt->vec[1]);
copy_v2_v2(&fp[4], bezt->vec[2]);
fp[6] = bezt->weight;
fp[7] = bezt->radius;
}
static void mask_layer_shape_to_mask_point(BezTriple *bezt, float fp[MASK_OBJECT_SHAPE_ELEM_SIZE])
{
copy_v2_v2(bezt->vec[0], &fp[0]);
copy_v2_v2(bezt->vec[1], &fp[2]);
copy_v2_v2(bezt->vec[2], &fp[4]);
bezt->weight = fp[6];
bezt->radius = fp[7];
}
/* these functions match. copy is swapped */
void BKE_mask_layer_shape_from_mask(MaskLayer *masklay, MaskLayerShape *masklay_shape)
{
int tot = BKE_mask_layer_shape_totvert(masklay);
if (masklay_shape->tot_vert == tot) {
float *fp = masklay_shape->data;
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
int i;
for (i = 0; i < spline->tot_point; i++) {
mask_layer_shape_from_mask_point(&spline->points[i].bezt, fp);
fp += MASK_OBJECT_SHAPE_ELEM_SIZE;
}
}
}
else {
printf("%s: vert mismatch %d != %d (frame %d)\n",
__func__, masklay_shape->tot_vert, tot, masklay_shape->frame);
}
}
void BKE_mask_layer_shape_to_mask(MaskLayer *masklay, MaskLayerShape *masklay_shape)
{
int tot = BKE_mask_layer_shape_totvert(masklay);
if (masklay_shape->tot_vert == tot) {
float *fp = masklay_shape->data;
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
int i;
for (i = 0; i < spline->tot_point; i++) {
mask_layer_shape_to_mask_point(&spline->points[i].bezt, fp);
fp += MASK_OBJECT_SHAPE_ELEM_SIZE;
}
}
}
else {
printf("%s: vert mismatch %d != %d (frame %d)\n",
__func__, masklay_shape->tot_vert, tot, masklay_shape->frame);
}
}
BLI_INLINE void interp_v2_v2v2_flfl(float target[2], const float a[2], const float b[2],
const float t, const float s)
{
target[0] = s * a[0] + t * b[0];
target[1] = s * a[1] + t * b[1];
}
/* linear interpolation only */
void BKE_mask_layer_shape_to_mask_interp(MaskLayer *masklay,
MaskLayerShape *masklay_shape_a,
MaskLayerShape *masklay_shape_b,
const float fac)
{
int tot = BKE_mask_layer_shape_totvert(masklay);
if (masklay_shape_a->tot_vert == tot && masklay_shape_b->tot_vert == tot) {
float *fp_a = masklay_shape_a->data;
float *fp_b = masklay_shape_b->data;
const float ifac = 1.0f - fac;
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
int i;
for (i = 0; i < spline->tot_point; i++) {
BezTriple *bezt = &spline->points[i].bezt;
/* *** BKE_mask_layer_shape_from_mask - swapped *** */
interp_v2_v2v2_flfl(bezt->vec[0], fp_a, fp_b, fac, ifac); fp_a += 2; fp_b += 2;
interp_v2_v2v2_flfl(bezt->vec[1], fp_a, fp_b, fac, ifac); fp_a += 2; fp_b += 2;
interp_v2_v2v2_flfl(bezt->vec[2], fp_a, fp_b, fac, ifac); fp_a += 2; fp_b += 2;
bezt->weight = (fp_a[0] * ifac) + (fp_b[0] * fac);
bezt->radius = (fp_a[1] * ifac) + (fp_b[1] * fac); fp_a += 2; fp_b += 2;
}
}
}
else {
printf("%s: vert mismatch %d != %d != %d (frame %d - %d)\n",
__func__, masklay_shape_a->tot_vert, masklay_shape_b->tot_vert, tot,
masklay_shape_a->frame, masklay_shape_b->frame);
}
}
MaskLayerShape *BKE_mask_layer_shape_find_frame(MaskLayer *masklay, const int frame)
{
MaskLayerShape *masklay_shape;
for (masklay_shape = masklay->splines_shapes.first;
masklay_shape;
masklay_shape = masklay_shape->next)
{
if (frame == masklay_shape->frame) {
return masklay_shape;
}
else if (frame < masklay_shape->frame) {
break;
}
}
return NULL;
}
/* when returning 2 - the frame isnt found but before/after frames are */
int BKE_mask_layer_shape_find_frame_range(MaskLayer *masklay, const float frame,
MaskLayerShape **r_masklay_shape_a,
MaskLayerShape **r_masklay_shape_b)
{
MaskLayerShape *masklay_shape;
for (masklay_shape = masklay->splines_shapes.first;
masklay_shape;
masklay_shape = masklay_shape->next)
{
if (frame == masklay_shape->frame) {
*r_masklay_shape_a = masklay_shape;
*r_masklay_shape_b = NULL;
return 1;
}
else if (frame < masklay_shape->frame) {
if (masklay_shape->prev) {
*r_masklay_shape_a = masklay_shape->prev;
*r_masklay_shape_b = masklay_shape;
return 2;
}
else {
*r_masklay_shape_a = masklay_shape;
*r_masklay_shape_b = NULL;
return 1;
}
}
}
if ((masklay_shape = masklay->splines_shapes.last)) {
*r_masklay_shape_a = masklay_shape;
*r_masklay_shape_b = NULL;
return 1;
}
else {
*r_masklay_shape_a = NULL;
*r_masklay_shape_b = NULL;
return 0;
}
}
2013-11-04 11:27:11 +00:00
MaskLayerShape *BKE_mask_layer_shape_verify_frame(MaskLayer *masklay, const int frame)
{
MaskLayerShape *masklay_shape;
masklay_shape = BKE_mask_layer_shape_find_frame(masklay, frame);
if (masklay_shape == NULL) {
masklay_shape = BKE_mask_layer_shape_alloc(masklay, frame);
BLI_addtail(&masklay->splines_shapes, masklay_shape);
BKE_mask_layer_shape_sort(masklay);
}
#if 0
{
MaskLayerShape *masklay_shape;
int i = 0;
for (masklay_shape = masklay->splines_shapes.first;
masklay_shape;
masklay_shape = masklay_shape->next)
{
printf("mask %d, %d\n", i++, masklay_shape->frame);
}
}
#endif
return masklay_shape;
}
MaskLayerShape *BKE_mask_layer_shape_duplicate(MaskLayerShape *masklay_shape)
{
MaskLayerShape *masklay_shape_copy;
masklay_shape_copy = MEM_dupallocN(masklay_shape);
if (LIKELY(masklay_shape_copy->data)) {
masklay_shape_copy->data = MEM_dupallocN(masklay_shape_copy->data);
}
return masklay_shape_copy;
}
void BKE_mask_layer_shape_unlink(MaskLayer *masklay, MaskLayerShape *masklay_shape)
{
BLI_remlink(&masklay->splines_shapes, masklay_shape);
BKE_mask_layer_shape_free(masklay_shape);
}
static int mask_layer_shape_sort_cb(void *masklay_shape_a_ptr, void *masklay_shape_b_ptr)
{
MaskLayerShape *masklay_shape_a = (MaskLayerShape *)masklay_shape_a_ptr;
MaskLayerShape *masklay_shape_b = (MaskLayerShape *)masklay_shape_b_ptr;
if (masklay_shape_a->frame < masklay_shape_b->frame) return -1;
else if (masklay_shape_a->frame > masklay_shape_b->frame) return 1;
else return 0;
}
void BKE_mask_layer_shape_sort(MaskLayer *masklay)
{
BLI_sortlist(&masklay->splines_shapes, mask_layer_shape_sort_cb);
}
bool BKE_mask_layer_shape_spline_from_index(MaskLayer *masklay, int index,
MaskSpline **r_masklay_shape, int *r_index)
{
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
if (index < spline->tot_point) {
*r_masklay_shape = spline;
*r_index = index;
return true;
}
index -= spline->tot_point;
}
return false;
}
int BKE_mask_layer_shape_spline_to_index(MaskLayer *masklay, MaskSpline *spline)
{
MaskSpline *spline_iter;
int i_abs = 0;
for (spline_iter = masklay->splines.first;
spline_iter && spline_iter != spline;
i_abs += spline_iter->tot_point, spline_iter = spline_iter->next)
{
/* pass */
}
return i_abs;
}
/* basic 2D interpolation functions, could make more comprehensive later */
static void interp_weights_uv_v2_calc(float r_uv[2], const float pt[2], const float pt_a[2], const float pt_b[2])
{
float pt_on_line[2];
r_uv[0] = closest_to_line_v2(pt_on_line, pt, pt_a, pt_b);
r_uv[1] = (len_v2v2(pt_on_line, pt) / len_v2v2(pt_a, pt_b)) *
((line_point_side_v2(pt_a, pt_b, pt) < 0.0f) ? -1.0f : 1.0f); /* this line only sets the sign */
}
static void interp_weights_uv_v2_apply(const float uv[2], float r_pt[2], const float pt_a[2], const float pt_b[2])
{
const float dvec[2] = {pt_b[0] - pt_a[0],
pt_b[1] - pt_a[1]};
/* u */
madd_v2_v2v2fl(r_pt, pt_a, dvec, uv[0]);
/* v */
r_pt[0] += -dvec[1] * uv[1];
r_pt[1] += dvec[0] * uv[1];
}
/* when a new points added - resize all shapekey array */
void BKE_mask_layer_shape_changed_add(MaskLayer *masklay, int index,
bool do_init, bool do_init_interpolate)
{
MaskLayerShape *masklay_shape;
/* spline index from masklay */
MaskSpline *spline;
int spline_point_index;
if (BKE_mask_layer_shape_spline_from_index(masklay, index,
&spline, &spline_point_index))
{
/* sanity check */
/* the point has already been removed in this array so subtract one when comparing with the shapes */
int tot = BKE_mask_layer_shape_totvert(masklay) - 1;
/* for interpolation */
/* TODO - assumes closed curve for now */
float uv[3][2]; /* 3x 2D handles */
const int pi_curr = spline_point_index;
const int pi_prev = ((spline_point_index - 1) + spline->tot_point) % spline->tot_point;
const int pi_next = (spline_point_index + 1) % spline->tot_point;
const int index_offset = index - spline_point_index;
/* const int pi_curr_abs = index; */
const int pi_prev_abs = pi_prev + index_offset;
const int pi_next_abs = pi_next + index_offset;
int i;
if (do_init_interpolate) {
for (i = 0; i < 3; i++) {
interp_weights_uv_v2_calc(uv[i],
spline->points[pi_curr].bezt.vec[i],
spline->points[pi_prev].bezt.vec[i],
spline->points[pi_next].bezt.vec[i]);
}
}
for (masklay_shape = masklay->splines_shapes.first;
masklay_shape;
masklay_shape = masklay_shape->next)
{
if (tot == masklay_shape->tot_vert) {
float *data_resized;
masklay_shape->tot_vert++;
data_resized = MEM_mallocN(masklay_shape->tot_vert * sizeof(float) * MASK_OBJECT_SHAPE_ELEM_SIZE, __func__);
if (index > 0) {
memcpy(data_resized,
masklay_shape->data,
index * sizeof(float) * MASK_OBJECT_SHAPE_ELEM_SIZE);
}
if (index != masklay_shape->tot_vert - 1) {
memcpy(&data_resized[(index + 1) * MASK_OBJECT_SHAPE_ELEM_SIZE],
masklay_shape->data + (index * MASK_OBJECT_SHAPE_ELEM_SIZE),
(masklay_shape->tot_vert - (index + 1)) * sizeof(float) * MASK_OBJECT_SHAPE_ELEM_SIZE);
}
if (do_init) {
float *fp = &data_resized[index * MASK_OBJECT_SHAPE_ELEM_SIZE];
mask_layer_shape_from_mask_point(&spline->points[spline_point_index].bezt, fp);
if (do_init_interpolate && spline->tot_point > 2) {
for (i = 0; i < 3; i++) {
interp_weights_uv_v2_apply(uv[i],
&fp[i * 2],
&data_resized[(pi_prev_abs * MASK_OBJECT_SHAPE_ELEM_SIZE) + (i * 2)],
&data_resized[(pi_next_abs * MASK_OBJECT_SHAPE_ELEM_SIZE) + (i * 2)]);
}
}
}
else {
memset(&data_resized[index * MASK_OBJECT_SHAPE_ELEM_SIZE],
0,
sizeof(float) * MASK_OBJECT_SHAPE_ELEM_SIZE);
}
MEM_freeN(masklay_shape->data);
masklay_shape->data = data_resized;
}
else {
printf("%s: vert mismatch %d != %d (frame %d)\n",
__func__, masklay_shape->tot_vert, tot, masklay_shape->frame);
}
}
}
}
/* move array to account for removed point */
void BKE_mask_layer_shape_changed_remove(MaskLayer *masklay, int index, int count)
{
MaskLayerShape *masklay_shape;
/* the point has already been removed in this array so add one when comparing with the shapes */
int tot = BKE_mask_layer_shape_totvert(masklay);
for (masklay_shape = masklay->splines_shapes.first;
masklay_shape;
masklay_shape = masklay_shape->next)
{
if (tot == masklay_shape->tot_vert - count) {
float *data_resized;
masklay_shape->tot_vert -= count;
data_resized = MEM_mallocN(masklay_shape->tot_vert * sizeof(float) * MASK_OBJECT_SHAPE_ELEM_SIZE, __func__);
if (index > 0) {
memcpy(data_resized,
masklay_shape->data,
index * sizeof(float) * MASK_OBJECT_SHAPE_ELEM_SIZE);
}
if (index != masklay_shape->tot_vert) {
memcpy(&data_resized[index * MASK_OBJECT_SHAPE_ELEM_SIZE],
masklay_shape->data + ((index + count) * MASK_OBJECT_SHAPE_ELEM_SIZE),
(masklay_shape->tot_vert - index) * sizeof(float) * MASK_OBJECT_SHAPE_ELEM_SIZE);
}
MEM_freeN(masklay_shape->data);
masklay_shape->data = data_resized;
}
else {
printf("%s: vert mismatch %d != %d (frame %d)\n",
__func__, masklay_shape->tot_vert - count, tot, masklay_shape->frame);
}
}
}
2012-06-07 18:24:36 +00:00
int BKE_mask_get_duration(Mask *mask)
{
return max_ii(1, mask->efra - mask->sfra);
}