diff options
author | Julian Eisel <eiseljulian@gmail.com> | 2017-01-22 23:16:00 +0300 |
---|---|---|
committer | Julian Eisel <eiseljulian@gmail.com> | 2017-01-22 23:16:00 +0300 |
commit | 181424152611aa7d842ece0265fb6c630c7a4d77 (patch) | |
tree | 9a592b9f8349195ab93a2f722acf63d01289a265 /source | |
parent | cdaed4d360e77f20c51e21a7b3fc800c3ca92afc (diff) | |
parent | ce8889175a553967583a1152e71b4390a240a112 (diff) |
Merge branch 'master' into blender2.8
Conflicts:
source/blender/editors/space_action/action_draw.c
Diffstat (limited to 'source')
43 files changed, 1964 insertions, 895 deletions
diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c index 7d3d12ac112..2f65e71c6d2 100644 --- a/source/blender/blenkernel/intern/anim.c +++ b/source/blender/blenkernel/intern/anim.c @@ -201,7 +201,15 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports, Scene *scene, Objec mpath->flag |= MOTIONPATH_FLAG_BHEAD; else mpath->flag &= ~MOTIONPATH_FLAG_BHEAD; - + + /* set default custom values */ + mpath->color[0] = 1.0; /* Red */ + mpath->color[1] = 0.0; + mpath->color[2] = 0.0; + + mpath->line_thickness = 1; + mpath->flag |= MOTIONPATH_FLAG_LINES; /* draw lines by default */ + /* allocate a cache */ mpath->points = MEM_callocN(sizeof(bMotionPathVert) * mpath->length, "bMotionPathVerts"); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 2b333941c6e..0287d6ae9ca 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -87,6 +87,7 @@ bArmature *BKE_armature_add(Main *bmain, const char *name) arm->deformflag = ARM_DEF_VGROUP | ARM_DEF_ENVELOPE; arm->flag = ARM_COL_CUSTOM; /* custom bone-group colors */ arm->layer = 1; + arm->ghostsize = 1; return arm; } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 51015abf4ad..de35d1e0eac 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -1408,7 +1408,8 @@ static ID *is_dupid(ListBase *lb, ID *id, const char *name) static bool check_for_dupid(ListBase *lb, ID *id, char *name) { ID *idtest; - int nr = 0, a, left_len; + int nr = 0, a; + size_t left_len; #define MAX_IN_USE 64 bool in_use[MAX_IN_USE]; /* to speed up finding unused numbers within [1 .. MAX_IN_USE - 1] */ @@ -1442,7 +1443,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name) /* Code above may have generated invalid utf-8 string, due to raw truncation. * Ensure we get a valid one now! */ - left_len -= BLI_utf8_invalid_strip(left, left_len); + left_len -= (size_t)BLI_utf8_invalid_strip(left, left_len); for (idtest = lb->first; idtest; idtest = idtest->next) { int nrtest; @@ -1484,7 +1485,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name) * shave off the end chars until we have a unique name. * Check the null terminators match as well so we don't get Cube.000 -> Cube.00 */ if (nr == 0 && name[left_len] == '\0') { - int len; + size_t len; /* FIXME: this code will never be executed, because either nr will be * at least 1, or name will not end at left_len! */ BLI_assert(0); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 39a97b08df1..69d3b4db54c 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -287,13 +287,16 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) ts->imapaint.paintcursor = NULL; id_us_plus((ID *)ts->imapaint.stencil); ts->particle.paintcursor = NULL; + /* duplicate Grease Pencil Drawing Brushes */ BLI_listbase_clear(&ts->gp_brushes); for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) { bGPDbrush *newbrush = BKE_gpencil_brush_duplicate(brush); BLI_addtail(&ts->gp_brushes, newbrush); } - + + /* duplicate Grease Pencil interpolation curve */ + ts->gp_interpolate.custom_ipo = curvemapping_copy(ts->gp_interpolate.custom_ipo); } /* make a private copy of the avicodecdata */ @@ -440,12 +443,17 @@ void BKE_scene_free(Scene *sce) BKE_paint_free(&sce->toolsettings->uvsculpt->paint); MEM_freeN(sce->toolsettings->uvsculpt); } + BKE_paint_free(&sce->toolsettings->imapaint.paint); + /* free Grease Pencil Drawing Brushes */ BKE_gpencil_free_brushes(&sce->toolsettings->gp_brushes); BLI_freelistN(&sce->toolsettings->gp_brushes); - - BKE_paint_free(&sce->toolsettings->imapaint.paint); - + + /* free Grease Pencil interpolation curve */ + if (sce->toolsettings->gp_interpolate.custom_ipo) { + curvemapping_free(sce->toolsettings->gp_interpolate.custom_ipo); + } + MEM_freeN(sce->toolsettings); sce->toolsettings = NULL; } diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 672857e88fe..88575c7d3be 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -235,8 +235,9 @@ Text *BKE_text_add(Main *bmain, const char *name) /* to a valid utf-8 sequences */ int txt_extended_ascii_as_utf8(char **str) { - int bad_char, added = 0, i = 0; - int length = strlen(*str); + ptrdiff_t bad_char, i = 0; + const ptrdiff_t length = (ptrdiff_t)strlen(*str); + int added = 0; while ((*str)[i]) { if ((bad_char = BLI_utf8_invalid_byte(*str + i, length - i)) == -1) @@ -248,7 +249,7 @@ int txt_extended_ascii_as_utf8(char **str) if (added != 0) { char *newstr = MEM_mallocN(length + added + 1, "text_line"); - int mi = 0; + ptrdiff_t mi = 0; i = 0; while ((*str)[i]) { diff --git a/source/blender/blenkernel/intern/tracking_util.c b/source/blender/blenkernel/intern/tracking_util.c index c34da4f6814..1c056cda68d 100644 --- a/source/blender/blenkernel/intern/tracking_util.c +++ b/source/blender/blenkernel/intern/tracking_util.c @@ -625,17 +625,17 @@ static ImBuf *make_grayscale_ibuf_copy(ImBuf *ibuf) */ size = (size_t)grayscale->x * (size_t)grayscale->y * sizeof(float); grayscale->channels = 1; - if ((grayscale->rect_float = MEM_mapallocN(size, "tracking grayscale image"))) { + if ((grayscale->rect_float = MEM_mapallocN(size, "tracking grayscale image")) != NULL) { grayscale->mall |= IB_rectfloat; grayscale->flags |= IB_rectfloat; - } - for (i = 0; i < grayscale->x * grayscale->y; ++i) { - const float *pixel = ibuf->rect_float + ibuf->channels * i; + for (i = 0; i < grayscale->x * grayscale->y; ++i) { + const float *pixel = ibuf->rect_float + ibuf->channels * i; - grayscale->rect_float[i] = 0.2126f * pixel[0] + - 0.7152f * pixel[1] + - 0.0722f * pixel[2]; + grayscale->rect_float[i] = 0.2126f * pixel[0] + + 0.7152f * pixel[1] + + 0.0722f * pixel[2]; + } } return grayscale; @@ -653,14 +653,14 @@ static void ibuf_to_float_image(const ImBuf *ibuf, libmv_FloatImage *float_image static ImBuf *float_image_to_ibuf(libmv_FloatImage *float_image) { ImBuf *ibuf = IMB_allocImBuf(float_image->width, float_image->height, 32, 0); - size_t size = (size_t)ibuf->x * (size_t)ibuf->y * - float_image->channels * sizeof(float); + size_t size = (size_t)ibuf->x * (size_t)ibuf->y * float_image->channels * sizeof(float); ibuf->channels = float_image->channels; - if ((ibuf->rect_float = MEM_mapallocN(size, "tracking grayscale image"))) { + if ((ibuf->rect_float = MEM_mapallocN(size, "tracking grayscale image")) != NULL) { ibuf->mall |= IB_rectfloat; ibuf->flags |= IB_rectfloat; + + memcpy(ibuf->rect_float, float_image->buffer, size); } - memcpy(ibuf->rect_float, float_image->buffer, size); return ibuf; } diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h index 0740b574c1a..32504a88b48 100644 --- a/source/blender/blenlib/BLI_string_utf8.h +++ b/source/blender/blenlib/BLI_string_utf8.h @@ -36,8 +36,8 @@ extern "C" { char *BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(); size_t BLI_strncpy_utf8_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(); char *BLI_strncat_utf8(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(); -int BLI_utf8_invalid_byte(const char *str, int length) ATTR_NONNULL(); -int BLI_utf8_invalid_strip(char *str, int length) ATTR_NONNULL(); +ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL(); +int BLI_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL(); int BLI_str_utf8_size(const char *p) ATTR_NONNULL(); /* warning, can return -1 on bad chars */ int BLI_str_utf8_size_safe(const char *p) ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_string_utils.h b/source/blender/blenlib/BLI_string_utils.h index 719d6e3c57b..bb19ed574bb 100644 --- a/source/blender/blenlib/BLI_string_utils.h +++ b/source/blender/blenlib/BLI_string_utils.h @@ -44,7 +44,7 @@ struct ListBase; typedef bool (*UniquenameCheckCallback)(void *arg, const char *name); -int BLI_split_name_num(char *left, int *nr, const char *name, const char delim); +size_t BLI_split_name_num(char *left, int *nr, const char *name, const char delim); void BLI_string_split_suffix(const char *string, char *r_body, char *r_suf, const size_t str_len); void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, const size_t str_len); diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index 0ab11810b48..83d4a75952f 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -74,7 +74,7 @@ static const size_t utf8_skip_data[256] = { * * \return the offset of the first invalid byte. */ -int BLI_utf8_invalid_byte(const char *str, int length) +ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) { const unsigned char *p, *perr, *pend = (const unsigned char *)str + length; unsigned char c; @@ -161,18 +161,24 @@ int BLI_utf8_invalid_byte(const char *str, int length) utf8_error: - return (int)((const char *)perr - (const char *)str); + return ((const char *)perr - (const char *)str); } -int BLI_utf8_invalid_strip(char *str, int length) +/** + * Remove any invalid utf-8 byte (taking into account multi-bytes sequence of course). + * + * @return number of stripped bytes. + */ +int BLI_utf8_invalid_strip(char *str, size_t length) { - int bad_char, tot = 0; + ptrdiff_t bad_char; + int tot = 0; BLI_assert(str[length] == '\0'); while ((bad_char = BLI_utf8_invalid_byte(str, length)) != -1) { str += bad_char; - length -= (bad_char + 1); + length -= (size_t)(bad_char + 1); if (length == 0) { /* last character bad, strip it */ @@ -182,7 +188,7 @@ int BLI_utf8_invalid_strip(char *str, int length) } else { /* strip, keep looking */ - memmove(str, str + 1, (size_t)length + 1); /* +1 for NULL char! */ + memmove(str, str + 1, length + 1); /* +1 for NULL char! */ tot++; } } diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c index 435a88d6e21..8d91a55a5ad 100644 --- a/source/blender/blenlib/intern/string_utils.c +++ b/source/blender/blenlib/intern/string_utils.c @@ -61,7 +61,7 @@ * \param delim Delimiter character * \return Length of \a left */ -int BLI_split_name_num(char *left, int *nr, const char *name, const char delim) +size_t BLI_split_name_num(char *left, int *nr, const char *name, const char delim) { const size_t name_len = strlen(name); @@ -70,7 +70,7 @@ int BLI_split_name_num(char *left, int *nr, const char *name, const char delim) /* name doesn't end with a delimiter "foo." */ if ((name_len > 1 && name[name_len - 1] == delim) == 0) { - int a = name_len; + size_t a = name_len; while (a--) { if (name[a] == delim) { left[a] = '\0'; /* truncate left part here */ @@ -295,7 +295,7 @@ bool BLI_uniquename_cb( char *tempname = alloca(name_len); char *left = alloca(name_len); int number; - int len = BLI_split_name_num(left, &number, name, delim); + size_t len = BLI_split_name_num(left, &number, name, delim); do { /* add 1 to account for \0 */ const size_t numlen = BLI_snprintf(numstr, sizeof(numstr), "%c%03d", delim, ++number) + 1; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 0e1861abcf1..1230a535fef 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -5932,6 +5932,7 @@ static void direct_link_scene(FileData *fd, Scene *sce) sce->toolsettings->wpaint->wpaint_prev = NULL; sce->toolsettings->wpaint->tot = 0; } + /* relink grease pencil drawing brushes */ link_list(fd, &sce->toolsettings->gp_brushes); for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) { @@ -5948,6 +5949,12 @@ static void direct_link_scene(FileData *fd, Scene *sce) direct_link_curvemapping(fd, brush->cur_jitter); } } + + /* relink grease pencil interpolation curves */ + sce->toolsettings->gp_interpolate.custom_ipo = newdataadr(fd, sce->toolsettings->gp_interpolate.custom_ipo); + if (sce->toolsettings->gp_interpolate.custom_ipo) { + direct_link_curvemapping(fd, sce->toolsettings->gp_interpolate.custom_ipo); + } } if (sce->ed) { diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index ea654ad6906..58cc06ddec6 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -1474,6 +1474,36 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) for (Brush *br = main->brush.first; br; br = br->id.next) { br->fill_threshold /= sqrt_3; } + + /* Custom motion paths */ + if (!DNA_struct_elem_find(fd->filesdna, "bMotionPath", "int", "line_thickness")) { + Object *ob; + for (ob = main->object.first; ob; ob = ob->id.next) { + bMotionPath *mpath; + bPoseChannel *pchan; + mpath = ob->mpath; + if (mpath) { + mpath->color[0] = 1.0f; + mpath->color[1] = 0.0f; + mpath->color[2] = 0.0f; + mpath->line_thickness = 1; + mpath->flag |= MOTIONPATH_FLAG_LINES; + } + /* bones motion path */ + if (ob->pose) { + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + mpath = pchan->mpath; + if (mpath) { + mpath->color[0] = 1.0f; + mpath->color[1] = 0.0f; + mpath->color[2] = 0.0f; + mpath->line_thickness = 1; + mpath->flag |= MOTIONPATH_FLAG_LINES; + } + } + } + } + } } /* To be added to next subversion bump! */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 27db83055bb..8ba36d149e6 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2690,6 +2690,10 @@ static void write_scenes(WriteData *wd, ListBase *scebase) write_curvemapping(wd, brush->cur_jitter); } } + /* write grease-pencil custom ipo curve to file */ + if (tos->gp_interpolate.custom_ipo) { + write_curvemapping(wd, tos->gp_interpolate.custom_ipo); + } write_paint(wd, &tos->imapaint.paint); diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 132a7ccd4fa..e46a31cb2e9 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -387,15 +387,11 @@ BMFace *BM_face_create_ngon_verts( * * \note Since this is a vcloud there is no direction. */ -BMFace *BM_face_create_ngon_vcloud( - BMesh *bm, BMVert **vert_arr, int len, - const BMFace *f_example, const eBMCreateFlag create_flag) +void BM_verts_sort_radial_plane(BMVert **vert_arr, int len) { struct SortIntByFloat *vang = BLI_array_alloca(vang, len); BMVert **vert_arr_map = BLI_array_alloca(vert_arr_map, len); - BMFace *f; - float totv_inv = 1.0f / (float)len; int i = 0; @@ -470,26 +466,9 @@ BMFace *BM_face_create_ngon_vcloud( /* now calculate every points angle around the normal (signed) */ for (i = 0; i < len; i++) { - float co[3]; - float proj_vec[3]; - float angle; - - /* center relative vec */ - sub_v3_v3v3(co, vert_arr[i]->co, cent); - - /* align to plane */ - project_v3_v3v3(proj_vec, co, nor); - sub_v3_v3(co, proj_vec); - - /* now 'co' is valid - we can compare its angle against the far vec */ - angle = angle_v3v3(far_vec, co); - - if (dot_v3v3(co, sign_vec) < 0.0f) { - angle = -angle; - } - - vang[i].sort_value = angle; + vang[i].sort_value = angle_signed_on_axis_v3v3v3_v3(far, cent, vert_arr[i]->co, nor); vang[i].data = i; + vert_arr_map[i] = vert_arr[i]; } /* sort by angle and magic! - we have our ngon */ @@ -497,14 +476,9 @@ BMFace *BM_face_create_ngon_vcloud( /* --- */ - /* create edges and find the winding (if faces are attached to any existing edges) */ for (i = 0; i < len; i++) { - vert_arr_map[i] = vert_arr[vang[i].data]; + vert_arr[i] = vert_arr_map[vang[i].data]; } - - f = BM_face_create_ngon_verts(bm, vert_arr_map, len, f_example, create_flag, true, true); - - return f; } /*************************************************************/ diff --git a/source/blender/bmesh/intern/bmesh_construct.h b/source/blender/bmesh/intern/bmesh_construct.h index 9c6483de42b..a52a17cd2f3 100644 --- a/source/blender/bmesh/intern/bmesh_construct.h +++ b/source/blender/bmesh/intern/bmesh_construct.h @@ -34,6 +34,9 @@ bool BM_verts_from_edges(BMVert **vert_arr, BMEdge **edge_arr, const int len); bool BM_edges_from_verts(BMEdge **edge_arr, BMVert **vert_arr, const int len); void BM_edges_from_verts_ensure(BMesh *bm, BMEdge **edge_arr, BMVert **vert_arr, const int len); +/* sort before creation */ +void BM_verts_sort_radial_plane(BMVert **vert_arr, int len); + BMFace *BM_face_create_quad_tri( BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, const BMFace *f_example, const eBMCreateFlag create_flag); @@ -50,10 +53,6 @@ BMFace *BM_face_create_ngon_verts( const BMFace *f_example, const eBMCreateFlag create_flag, const bool calc_winding, const bool create_edges); -BMFace *BM_face_create_ngon_vcloud( - BMesh *bm, BMVert **vert_arr, int len, - const BMFace *f_example, const eBMCreateFlag create_flag); - void BM_elem_attrs_copy_ex( BMesh *bm_src, BMesh *bm_dst, const void *ele_src_v, void *ele_dst_v, const char hflag_mask); diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c index 7178a8132d2..7f2032d5f53 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -70,7 +70,7 @@ static void recount_totsels(BMesh *bm) } } -/** \name BMesh helper functions for selection flushing. +/** \name BMesh helper functions for selection & hide flushing. * \{ */ static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_first) @@ -102,6 +102,20 @@ static bool bm_vert_is_edge_select_any(const BMVert *v) } #endif +static bool bm_vert_is_edge_visible_any(const BMVert *v) +{ + if (v->e) { + const BMEdge *e_iter, *e_first; + e_iter = e_first = v->e; + do { + if (!BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN)) { + return true; + } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); + } + return false; +} + static bool bm_edge_is_face_select_any_other(BMLoop *l_first) { const BMLoop *l_iter = l_first; @@ -131,6 +145,20 @@ static bool bm_edge_is_face_select_any(const BMEdge *e) } #endif +static bool bm_edge_is_face_visible_any(const BMEdge *e) +{ + if (e->l) { + const BMLoop *l_iter, *l_first; + l_iter = l_first = e->l; + do { + if (!BM_elem_flag_test(l_iter->f, BM_ELEM_HIDDEN)) { + return true; + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + return false; +} + /** \} */ /** @@ -1198,87 +1226,111 @@ void BM_mesh_elem_hflag_enable_all( /***************** Mesh Hiding stuff *********** */ +/** + * Hide unless any connected elements are visible. + * Run this after hiding a connected edge or face. + */ static void vert_flush_hide_set(BMVert *v) { - BMIter iter; - BMEdge *e; - bool hide = true; - - BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - hide = hide && BM_elem_flag_test(e, BM_ELEM_HIDDEN); - } - - BM_elem_flag_set(v, BM_ELEM_HIDDEN, hide); + BM_elem_flag_set(v, BM_ELEM_HIDDEN, !bm_vert_is_edge_visible_any(v)); } -static void edge_flush_hide(BMEdge *e) +/** + * Hide unless any connected elements are visible. + * Run this after hiding a connected face. + */ +static void edge_flush_hide_set(BMEdge *e) { - BMIter iter; - BMFace *f; - bool hide = true; - - BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) { - hide = hide && BM_elem_flag_test(f, BM_ELEM_HIDDEN); - } - - BM_elem_flag_set(e, BM_ELEM_HIDDEN, hide); + BM_elem_flag_set(e, BM_ELEM_HIDDEN, !bm_edge_is_face_visible_any(e)); } void BM_vert_hide_set(BMVert *v, const bool hide) { /* vert hiding: vert + surrounding edges and faces */ - BMIter iter, fiter; - BMEdge *e; - BMFace *f; - BLI_assert(v->head.htype == BM_VERT); + if (hide) { + BLI_assert(!BM_elem_flag_test(v, BM_ELEM_SELECT)); + } BM_elem_flag_set(v, BM_ELEM_HIDDEN, hide); - BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - BM_elem_flag_set(e, BM_ELEM_HIDDEN, hide); - - BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { - BM_elem_flag_set(f, BM_ELEM_HIDDEN, hide); - } + if (v->e) { + BMEdge *e_iter, *e_first; + e_iter = e_first = v->e; + do { + BM_elem_flag_set(e_iter, BM_ELEM_HIDDEN, hide); + if (e_iter->l) { + const BMLoop *l_radial_iter, *l_radial_first; + l_radial_iter = l_radial_first = e_iter->l; + do { + BM_elem_flag_set(l_radial_iter->f, BM_ELEM_HIDDEN, hide); + } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first); + } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); } } void BM_edge_hide_set(BMEdge *e, const bool hide) { - BMIter iter; - BMFace *f; - /* BMVert *v; */ - BLI_assert(e->head.htype == BM_EDGE); + if (hide) { + BLI_assert(!BM_elem_flag_test(e, BM_ELEM_SELECT)); + } /* edge hiding: faces around the edge */ - BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) { - BM_elem_flag_set(f, BM_ELEM_HIDDEN, hide); + if (e->l) { + const BMLoop *l_iter, *l_first; + l_iter = l_first = e->l; + do { + BM_elem_flag_set(l_iter->f, BM_ELEM_HIDDEN, hide); + } while ((l_iter = l_iter->radial_next) != l_first); } BM_elem_flag_set(e, BM_ELEM_HIDDEN, hide); /* hide vertices if necessary */ - vert_flush_hide_set(e->v1); - vert_flush_hide_set(e->v2); + if (hide) { + vert_flush_hide_set(e->v1); + vert_flush_hide_set(e->v2); + } + else { + BM_elem_flag_disable(e->v1, BM_ELEM_HIDDEN); + BM_elem_flag_disable(e->v2, BM_ELEM_HIDDEN); + } } void BM_face_hide_set(BMFace *f, const bool hide) { - BMIter iter; - BMLoop *l; - BLI_assert(f->head.htype == BM_FACE); + if (hide) { + BLI_assert(!BM_elem_flag_test(f, BM_ELEM_SELECT)); + } BM_elem_flag_set(f, BM_ELEM_HIDDEN, hide); - BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) { - edge_flush_hide(l->e); + if (hide) { + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter; + + l_iter = l_first; + do { + edge_flush_hide_set(l_iter->e); + } while ((l_iter = l_iter->next) != l_first); + + l_iter = l_first; + do { + vert_flush_hide_set(l_iter->v); + } while ((l_iter = l_iter->next) != l_first); } + else { + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter; - BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) { - vert_flush_hide_set(l->v); + l_iter = l_first; + do { + BM_elem_flag_disable(l_iter->e, BM_ELEM_HIDDEN); + BM_elem_flag_disable(l_iter->v, BM_ELEM_HIDDEN); + } while ((l_iter = l_iter->next) != l_first); } } diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c index 7b8cb36ab59..a980baf8626 100644 --- a/source/blender/bmesh/operators/bmo_create.c +++ b/source/blender/bmesh/operators/bmo_create.c @@ -290,7 +290,11 @@ void bmo_contextual_create_exec(BMesh *bm, BMOperator *op) BMFace *f; BMO_iter_as_array(op->slots_in, "geom", BM_VERT, (void **)vert_arr, totv); - f = BM_face_create_ngon_vcloud(bm, vert_arr, totv, NULL, BM_CREATE_NO_DOUBLE); + + BM_verts_sort_radial_plane(vert_arr, totv); + + /* create edges and find the winding (if faces are attached to any existing edges) */ + f = BM_face_create_ngon_verts(bm, vert_arr, totv, NULL, BM_CREATE_NO_DOUBLE, true, true); if (f) { BMO_face_flag_enable(bm, f, ELE_OUT); diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c index c52c608e671..e2ff09669d7 100644 --- a/source/blender/bmesh/operators/bmo_inset.c +++ b/source/blender/bmesh/operators/bmo_inset.c @@ -647,6 +647,10 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) } (void)0 #define VERT_ORIG_GET(_v) \ (const float *)BLI_ghash_lookup_default(vert_coords, (_v), (_v)->co) + /* memory for the coords isn't given back to the arena, + * acceptable in this case since it runs a fixed number of times. */ +#define VERT_ORIG_REMOVE(_v) \ + BLI_ghash_remove(vert_coords, (_v), NULL, NULL) for (i = 0, es = edge_info; i < edge_info_len; i++, es++) { @@ -972,7 +976,11 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) v_glue = v_split; } else { - BM_vert_splice(bm, v_glue, v_split); + if (BM_vert_splice(bm, v_glue, v_split)) { + if (use_vert_coords_orig) { + VERT_ORIG_REMOVE(v_split); + } + } } } } diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 6604d595573..3d5317b2ebd 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -44,6 +44,7 @@ set(SRC gpencil_convert.c gpencil_data.c gpencil_edit.c + gpencil_interpolate.c gpencil_ops.c gpencil_paint.c gpencil_select.c diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 8a4ba6687cd..e8a812817d1 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -1632,7 +1632,7 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i { bGPdata *gpd_source = NULL; ToolSettings *ts; - bGPDbrush *brush; + bGPDbrush *brush = NULL; if (scene) { ts = scene->toolsettings; brush = BKE_gpencil_brush_getactive(ts); @@ -1641,8 +1641,7 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i BKE_gpencil_brush_init_presets(ts); brush = BKE_gpencil_brush_getactive(ts); } - } - if (scene) { + if (spacetype == SPACE_VIEW3D) { gpd_source = (scene->gpd ? scene->gpd : NULL); } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 15f65b394a9..5b011b679a6 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -682,6 +682,87 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf); } +/* ********************* Add Blank Frame *************************** */ + +/* Basically the same as the drawing op */ +static int UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C) +{ + if (ED_operator_regionactive(C)) { + /* check if current context can support GPencil data */ + if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { + return 1; + } + else { + CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + } + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + } + + return 0; +} + +static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd); + + const bool all_layers = RNA_boolean_get(op->ptr, "all_layers"); + + /* Initialise datablock and an active layer if nothing exists yet */ + if (ELEM(NULL, gpd, active_gpl)) { + /* let's just be lazy, and call the "Add New Layer" operator, which sets everything up as required */ + WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL); + } + + /* Go through each layer, adding a frame after the active one + * and/or shunting all the others out of the way + */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + if ((all_layers == false) && (gpl != active_gpl)) { + continue; + } + + /* 1) Check for an existing frame on the current frame */ + bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, CFRA); + if (gpf) { + /* Shunt all frames after (and including) the existing one later by 1-frame */ + for (; gpf; gpf = gpf->next) { + gpf->framenum += 1; + } + } + + /* 2) Now add a new frame, with nothing in it */ + gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW); + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_blank_frame_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Blank Frame"; + ot->idname = "GPENCIL_OT_blank_frame_add"; + ot->description = "Add a new frame with nothing in it on the current frame. " + "If there is already a frame, all existing frames are shifted one frame later"; + + /* callbacks */ + ot->exec = gp_blank_frame_add_exec; + ot->poll = gp_add_poll; + + /* properties */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_boolean(ot->srna, "all_layers", false, "All Layers", "Create blank frame in all layers, not only active"); +} + /* ******************* Delete Active Frame ************************ */ static int gp_actframe_delete_poll(bContext *C) @@ -1897,6 +1978,13 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot) /* ***************** Reproject Strokes ********************** */ +typedef enum eGP_ReprojectModes { + /* On same plane, parallel to viewplane */ + GP_REPROJECT_PLANAR = 0, + /* Reprojected on to the scene geometry */ + GP_REPROJECT_SURFACE, +} eGP_ReprojectModes; + static int gp_strokes_reproject_poll(bContext *C) { /* 2 Requirements: @@ -1906,14 +1994,23 @@ static int gp_strokes_reproject_poll(bContext *C) return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); } -static int gp_strokes_reproject_exec(bContext *C, wmOperator *UNUSED(op)) +static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); GP_SpaceConversion gsc = {NULL}; + eGP_ReprojectModes mode = RNA_boolean_get(op->ptr, "type"); /* init space conversion stuff */ gp_point_conversion_init(C, &gsc); + /* init autodist for geometry projection */ + if (mode == GP_REPROJECT_SURFACE) { + view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar); + ED_view3d_autodist_init(scene, gsc.ar, CTX_wm_view3d(C), 0); + } + + // TODO: For deforming geometry workflow, create new frames? + /* Go through each editable + selected stroke, adjusting each of its points one by one... */ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { @@ -1949,7 +2046,27 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *UNUSED(op)) /* Project screenspace back to 3D space (from current perspective) * so that all points have been treated the same way */ - gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + if (mode == GP_REPROJECT_PLANAR) { + /* Planar - All on same plane parallel to the viewplane */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + } + else { + /* Geometry - Snap to surfaces of visible geometry */ + /* XXX: There will be precision loss (possible stairstep artifacts) from this conversion to satisfy the API's */ + const int screen_co[2] = {(int)xy[0], (int)xy[1]}; + + int depth_margin = 0; // XXX: 4 for strokes, 0 for normal + float depth; + + /* XXX: The proper procedure computes the depths into an array, to have smooth transitions when all else fails... */ + if (ED_view3d_autodist_depth(gsc.ar, screen_co, depth_margin, &depth)) { + ED_view3d_autodist_simple(gsc.ar, screen_co, &pt->x, 0, &depth); + } + else { + /* Default to planar */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + } + } /* Unapply parent corrections */ if (gpl->parent) { @@ -1966,21 +2083,36 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *UNUSED(op)) void GPENCIL_OT_reproject(wmOperatorType *ot) { + static EnumPropertyItem reproject_type[] = { + {GP_REPROJECT_PLANAR, "PLANAR", 0, "Planar", + "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " + "using 'Cursor' Stroke Placement"}, + {GP_REPROJECT_SURFACE, "SURFACE", 0, "Surface", + "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"}, + {0, NULL, 0, NULL, NULL} + }; + /* identifiers */ ot->name = "Reproject Strokes"; ot->idname = "GPENCIL_OT_reproject"; - ot->description = "Reproject the selected strokes from the current viewpoint to get all points on the same plane again " - "(e.g. to fix problems from accidental 3D cursor movement, or viewport changes)"; + ot->description = "Reproject the selected strokes from the current viewpoint as if they had been newly drawn " + "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, " + "or for matching deforming geometry)"; /* callbacks */ + ot->invoke = WM_menu_invoke; ot->exec = gp_strokes_reproject_exec; ot->poll = gp_strokes_reproject_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", reproject_type, GP_REPROJECT_PLANAR, "Projection Type", ""); } /* ******************* Stroke subdivide ************************** */ + /* helper: Count how many points need to be inserted */ static int gp_count_subdivision_cuts(bGPDstroke *gps) { @@ -2098,673 +2230,3 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } - -/* ========= Interpolation operators ========================== */ -/* Helper: Update point with interpolation */ -static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_to, bGPDstroke *new_stroke, float factor) -{ - bGPDspoint *prev, *pt, *next; - - /* update points */ - for (int i = 0; i < new_stroke->totpoints; i++) { - prev = &gps_from->points[i]; - pt = &new_stroke->points[i]; - next = &gps_to->points[i]; - - /* Interpolate all values */ - interp_v3_v3v3(&pt->x, &prev->x, &next->x, factor); - pt->pressure = interpf(prev->pressure, next->pressure, factor); - pt->strength = interpf(prev->strength, next->strength, factor); - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - } -} - -/* Helper: Update all strokes interpolated */ -static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) -{ - tGPDinterpolate_layer *tgpil; - bGPDstroke *new_stroke, *gps_from, *gps_to; - int cStroke; - float factor; - float shift = tgpi->shift; - - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - factor = tgpil->factor + shift; - for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; new_stroke = new_stroke->next) { - if (new_stroke->totpoints == 0) { - continue; - } - /* get strokes to interpolate */ - cStroke = BLI_findindex(&tgpil->interFrame->strokes, new_stroke); - gps_from = BLI_findlink(&tgpil->prevFrame->strokes, cStroke); - gps_to = BLI_findlink(&tgpil->nextFrame->strokes, cStroke); - /* update points position */ - if ((gps_from) && (gps_to)) { - gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); - } - } - } - - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); -} - -/* Helper: Verify valid strokes for interpolation */ -static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) -{ - ToolSettings *ts = CTX_data_tool_settings(C); - int flag = ts->gp_sculpt.flag; - - bGPDlayer *gpl; - bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); - bGPDstroke *gps_from, *gps_to; - int fFrame; - - /* get layers */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* all layers or only active */ - if (((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { - continue; - } - /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { - continue; - } - /* read strokes */ - for (gps_from = gpl->actframe->strokes.first; gps_from; gps_from = gps_from->next) { - /* only selected */ - if ((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { - continue; - } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps_from) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { - continue; - } - /* get final stroke to interpolate */ - fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from); - gps_to = BLI_findlink(&gpl->actframe->next->strokes, fFrame); - if (gps_to == NULL) { - continue; - } - return 1; - } - } - return 0; -} - -/* Helper: Create internal strokes interpolated */ -static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) -{ - bGPDlayer *gpl; - bGPdata *gpd = tgpi->gpd; - tGPDinterpolate_layer *tgpil; - bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); - bGPDstroke *gps_from, *gps_to, *new_stroke; - int fFrame; - - /* save initial factor for active layer to define shift limits */ - tgpi->init_factor = (float)(tgpi->cframe - active_gpl->actframe->framenum) / (active_gpl->actframe->next->framenum - active_gpl->actframe->framenum + 1); - /* limits are 100% below 0 and 100% over the 100% */ - tgpi->low_limit = -1.0f - tgpi->init_factor; - tgpi->high_limit = 2.0f - tgpi->init_factor; - - /* set layers */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* all layers or only active */ - if (((tgpi->flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { - continue; - } - /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { - continue; - } - /* create temp data for each layer */ - tgpil = NULL; - tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); - - tgpil->gpl = gpl; - tgpil->prevFrame = gpl->actframe; - tgpil->nextFrame = gpl->actframe->next; - - BLI_addtail(&tgpi->ilayers, tgpil); - /* create a new temporary frame */ - tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe"); - tgpil->interFrame->framenum = tgpi->cframe; - - /* get interpolation factor by layer (usually must be equal for all layers, but not sure) */ - tgpil->factor = (float)(tgpi->cframe - tgpil->prevFrame->framenum) / (tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1); - /* create new strokes data with interpolated points reading original stroke */ - for (gps_from = tgpil->prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { - bool valid = true; - /* only selected */ - if ((tgpi->flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { - valid = false; - } - - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps_from) == false) { - valid = false; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(tgpil->gpl, gps_from) == false) { - valid = false; - } - /* get final stroke to interpolate */ - fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from); - gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame); - if (gps_to == NULL) { - valid = false; - } - /* create new stroke */ - new_stroke = MEM_dupallocN(gps_from); - new_stroke->points = MEM_dupallocN(gps_from->points); - new_stroke->triangles = MEM_dupallocN(gps_from->triangles); - if (valid) { - /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ - if (gps_from->totpoints > gps_to->totpoints) { - new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); - new_stroke->totpoints = gps_to->totpoints; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_CACHES; - } - /* update points position */ - gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); - } - else { - /* need an empty stroke to keep index correct for lookup, but resize to smallest size */ - new_stroke->totpoints = 0; - new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points)); - new_stroke->tot_triangles = 0; - new_stroke->triangles = MEM_recallocN(new_stroke->triangles, sizeof(*new_stroke->triangles)); - } - /* add to strokes */ - BLI_addtail(&tgpil->interFrame->strokes, new_stroke); - } - } -} - -/* Helper: calculate shift based on position of mouse (we only use x-axis for now. -* since this is more convenient for users to do), and store new shift value -*/ -static void gpencil_mouse_update_shift(tGPDinterpolate *tgpi, wmOperator *op, const wmEvent *event) -{ - float mid = (float)(tgpi->ar->winx - tgpi->ar->winrct.xmin) / 2.0f; - float mpos = event->x - tgpi->ar->winrct.xmin; - if (mpos >= mid) { - tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid; - } - else { - tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid); - } - - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); -} - -/* Helper: Draw status message while the user is running the operator */ -static void gpencil_interpolate_status_indicators(tGPDinterpolate *p) -{ - Scene *scene = p->scene; - char status_str[UI_MAX_DRAW_STR]; - char msg_str[UI_MAX_DRAW_STR]; - BLI_strncpy(msg_str, IFACE_("GPencil Interpolation: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust, Factor"), UI_MAX_DRAW_STR); - - if (hasNumInput(&p->num)) { - char str_offs[NUM_STR_REP_LEN]; - - outputNumInput(&p->num, str_offs, &scene->unit); - - BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs); - } - else { - BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%", msg_str, (int)((p->init_factor + p->shift) * 100.0f)); - } - - ED_area_headerprint(p->sa, status_str); -} - -/* Helper: Update screen and stroke */ -static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) -{ - /* update shift indicator in header */ - gpencil_interpolate_status_indicators(tgpi); - /* apply... */ - tgpi->shift = RNA_float_get(op->ptr, "shift"); - /* update points position */ - gp_interpolate_update_strokes(C, tgpi); -} - -/* init new temporary interpolation data */ -static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) -{ - ToolSettings *ts = CTX_data_tool_settings(C); - bGPdata *gpd = CTX_data_gpencil_data(C); - - /* set current scene and window */ - tgpi->scene = CTX_data_scene(C); - tgpi->sa = CTX_wm_area(C); - tgpi->ar = CTX_wm_region(C); - tgpi->flag = ts->gp_sculpt.flag; - - /* set current frame number */ - tgpi->cframe = tgpi->scene->r.cfra; - - /* set GP datablock */ - tgpi->gpd = gpd; - - /* set interpolation weight */ - tgpi->shift = RNA_float_get(op->ptr, "shift"); - /* set layers */ - gp_interpolate_set_points(C, tgpi); - - return 1; -} - -/* Poll handler: check if context is suitable for interpolation */ -static int gpencil_interpolate_poll(bContext *C) -{ - bGPdata * gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - /* only 3D view */ - if (CTX_wm_area(C)->spacetype != SPACE_VIEW3D) { - return 0; - } - /* need data to interpolate */ - if (ELEM(NULL, gpd, gpl)) { - return 0; - } - - return 1; -} - -/* Allocate memory and initialize values */ -static tGPDinterpolate *gp_session_init_interpolation(bContext *C, wmOperator *op) -{ - tGPDinterpolate *tgpi = NULL; - - /* create new context data */ - tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data"); - - /* define initial values */ - gp_interpolate_set_init_values(C, op, tgpi); - - /* return context data for running operator */ - return tgpi; -} - -/* Exit and free memory */ -static void gpencil_interpolate_exit(bContext *C, wmOperator *op) -{ - tGPDinterpolate *tgpi = op->customdata; - tGPDinterpolate_layer *tgpil; - - /* don't assume that operator data exists at all */ - if (tgpi) { - /* remove drawing handler */ - if (tgpi->draw_handle_screen) { - ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_screen); - } - if (tgpi->draw_handle_3d) { - ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); - } - /* clear status message area */ - ED_area_headerprint(tgpi->sa, NULL); - /* finally, free memory used by temp data */ - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - BKE_gpencil_free_strokes(tgpil->interFrame); - MEM_freeN(tgpil->interFrame); - } - - BLI_freelistN(&tgpi->ilayers); - MEM_freeN(tgpi); - } - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - /* clear pointer */ - op->customdata = NULL; -} - -/* Cancel handler */ -static void gpencil_interpolate_cancel(bContext *C, wmOperator *op) -{ - /* this is just a wrapper around exit() */ - gpencil_interpolate_exit(C, op); -} - -/* Init interpolation: Allocate memory and set init values */ -static int gpencil_interpolate_init(bContext *C, wmOperator *op) -{ - tGPDinterpolate *tgpi; - /* check context */ - tgpi = op->customdata = gp_session_init_interpolation(C, op); - if (tgpi == NULL) { - /* something wasn't set correctly in context */ - gpencil_interpolate_exit(C, op); - return 0; - } - - /* everything is now setup ok */ - return 1; -} - -/* ********************** custom drawcall api ***************** */ -/* Helper: drawing callback for modal operator in screen mode */ -static void gpencil_interpolate_draw_screen(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) -{ - wmOperator *op = arg; - struct tGPDinterpolate *tgpi = op->customdata; - ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_PIXEL); -} - -/* Helper: drawing callback for modal operator in 3d mode */ -static void gpencil_interpolate_draw_3d(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) -{ - wmOperator *op = arg; - struct tGPDinterpolate *tgpi = op->customdata; - ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_VIEW); -} - -/* Invoke handler: Initialize the operator */ -static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - wmWindow *win = CTX_wm_window(C); - Scene *scene = CTX_data_scene(C); - bGPdata * gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - tGPDinterpolate *tgpi = NULL; - - /* cannot interpolate if not between 2 frames */ - if ((gpl->actframe == NULL) || (gpl->actframe->next == NULL)) { - BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames in active layer"); - return OPERATOR_CANCELLED; - } - - /* cannot interpolate in extremes */ - if ((gpl->actframe->framenum == scene->r.cfra) || (gpl->actframe->next->framenum == scene->r.cfra)) { - BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames in active layer"); - return OPERATOR_CANCELLED; - } - - /* need editable strokes */ - if (!gp_interpolate_check_todo(C, gpd)) { - BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable stroke"); - return OPERATOR_CANCELLED; - } - - /* try to initialize context data needed */ - if (!gpencil_interpolate_init(C, op)) { - if (op->customdata) - MEM_freeN(op->customdata); - return OPERATOR_CANCELLED; - } - else - tgpi = op->customdata; - - /* enable custom drawing handlers. It needs 2 handlers because can be strokes in 3d space and screen space and each handler use different - coord system */ - tgpi->draw_handle_screen = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_screen, op, REGION_DRAW_POST_PIXEL); - tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_3d, op, REGION_DRAW_POST_VIEW); - /* set cursor to indicate modal */ - WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); - /* update shift indicator in header */ - gpencil_interpolate_status_indicators(tgpi); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - /* add a modal handler for this operator */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -/* Modal handler: Events handling during interactive part */ -static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - tGPDinterpolate *tgpi = op->customdata; - wmWindow *win = CTX_wm_window(C); - bGPDframe *gpf_dst; - bGPDstroke *gps_src, *gps_dst; - tGPDinterpolate_layer *tgpil; - const bool has_numinput = hasNumInput(&tgpi->num); - - switch (event->type) { - case LEFTMOUSE: /* confirm */ - case RETKEY: - { - /* return to normal cursor and header status */ - ED_area_headerprint(tgpi->sa, NULL); - WM_cursor_modal_restore(win); - - /* insert keyframes as required... */ - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); - gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN; - - /* copy strokes */ - BLI_listbase_clear(&gpf_dst->strokes); - for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) { - if (gps_src->totpoints == 0) { - continue; - } - /* make copy of source stroke, then adjust pointer to points too */ - gps_dst = MEM_dupallocN(gps_src); - gps_dst->points = MEM_dupallocN(gps_src->points); - gps_dst->triangles = MEM_dupallocN(gps_src->triangles); - gps_dst->flag |= GP_STROKE_RECALC_CACHES; - BLI_addtail(&gpf_dst->strokes, gps_dst); - } - } - /* clean up temp data */ - gpencil_interpolate_exit(C, op); - - /* done! */ - return OPERATOR_FINISHED; - } - - case ESCKEY: /* cancel */ - case RIGHTMOUSE: - { - /* return to normal cursor and header status */ - ED_area_headerprint(tgpi->sa, NULL); - WM_cursor_modal_restore(win); - - /* clean up temp data */ - gpencil_interpolate_exit(C, op); - - /* canceled! */ - return OPERATOR_CANCELLED; - } - case WHEELUPMOUSE: - { - tgpi->shift = tgpi->shift + 0.01f; - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - break; - } - case WHEELDOWNMOUSE: - { - tgpi->shift = tgpi->shift - 0.01f; - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - break; - } - case MOUSEMOVE: /* calculate new position */ - { - /* only handle mousemove if not doing numinput */ - if (has_numinput == false) { - /* update shift based on position of mouse */ - gpencil_mouse_update_shift(tgpi, op, event); - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - } - break; - } - default: - if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { - float value; - float factor = tgpi->init_factor; - - /* Grab shift from numeric input, and store this new value (the user see an int) */ - value = (factor + tgpi->shift) * 100.0f; - applyNumInput(&tgpi->num, &value); - tgpi->shift = value / 100.0f; - /* recalculate the shift to get the right value in the frame scale */ - tgpi->shift = tgpi->shift - factor; - - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); - - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - - break; - } - else { - /* unhandled event - allow to pass through */ - return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; - } - } - - /* still running... */ - return OPERATOR_RUNNING_MODAL; -} - -/* Define modal operator for interpolation */ -void GPENCIL_OT_interpolate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Grease Pencil Interpolation"; - ot->idname = "GPENCIL_OT_interpolate"; - ot->description = "Interpolate grease pencil strokes between frames"; - - /* api callbacks */ - ot->invoke = gpencil_interpolate_invoke; - ot->modal = gpencil_interpolate_modal; - ot->cancel = gpencil_interpolate_cancel; - ot->poll = gpencil_interpolate_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - - RNA_def_float_percentage(ot->srna, "shift", 0.0f, -1.0f, 1.0f, "Shift", "Displacement factor for the interpolate operation", -0.9f, 0.9f); -} - -/* =============== Interpolate sequence ===============*/ -/* Create Sequence Interpolation */ -static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - bGPdata * gpd = CTX_data_gpencil_data(C); - bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); - bGPDlayer *gpl; - bGPDframe *prevFrame, *nextFrame, *interFrame; - bGPDstroke *gps_from, *gps_to, *new_stroke; - float factor; - int cframe, fFrame; - int flag = ts->gp_sculpt.flag; - - /* cannot interpolate if not between 2 frames */ - if ((active_gpl->actframe == NULL) || (active_gpl->actframe->next == NULL)) { - BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames"); - return OPERATOR_CANCELLED; - } - /* cannot interpolate in extremes */ - if ((active_gpl->actframe->framenum == scene->r.cfra) || (active_gpl->actframe->next->framenum == scene->r.cfra)) { - BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames"); - return OPERATOR_CANCELLED; - } - - /* loop all layer to check if need interpolation */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* all layers or only active */ - if (((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { - continue; - } - /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { - continue; - } - /* store extremes */ - prevFrame = gpl->actframe; - nextFrame = gpl->actframe->next; - /* Loop over intermediary frames and create the interpolation */ - for (cframe = prevFrame->framenum + 1; cframe < nextFrame->framenum; cframe++) { - interFrame = NULL; - - /* get interpolation factor */ - factor = (float)(cframe - prevFrame->framenum) / (nextFrame->framenum - prevFrame->framenum + 1); - - /* create new strokes data with interpolated points reading original stroke */ - for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { - /* only selected */ - if ((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { - continue; - } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps_from) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { - continue; - } - /* get final stroke to interpolate */ - fFrame = BLI_findindex(&prevFrame->strokes, gps_from); - gps_to = BLI_findlink(&nextFrame->strokes, fFrame); - if (gps_to == NULL) { - continue; - } - /* create a new frame if needed */ - if (interFrame == NULL) { - interFrame = BKE_gpencil_layer_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW); - interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; - } - /* create new stroke */ - new_stroke = MEM_dupallocN(gps_from); - new_stroke->points = MEM_dupallocN(gps_from->points); - new_stroke->triangles = MEM_dupallocN(gps_from->triangles); - /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ - if (gps_from->totpoints > gps_to->totpoints) { - new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); - new_stroke->totpoints = gps_to->totpoints; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_CACHES; - } - /* update points position */ - gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); - - /* add to strokes */ - BLI_addtail(&interFrame->strokes, new_stroke); - } - } - } - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -/* Define sequence interpolation */ -void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Grease Pencil Sequence Interpolation"; - ot->idname = "GPENCIL_OT_interpolate_sequence"; - ot->description = "Interpolate full grease pencil strokes sequence between frames"; - - /* api callbacks */ - ot->exec = gpencil_interpolate_seq_exec; - ot->poll = gpencil_interpolate_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} -/* ========= End Interpolation operators ========================== */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index e2e5fc28710..5c7c9b84adb 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -287,6 +287,8 @@ void GPENCIL_OT_unlock_all(struct wmOperatorType *ot); void GPENCIL_OT_layer_isolate(struct wmOperatorType *ot); void GPENCIL_OT_layer_merge(struct wmOperatorType *ot); +void GPENCIL_OT_blank_frame_add(struct wmOperatorType *ot); + void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); @@ -340,6 +342,7 @@ void gpencil_undo_finish(void); void GPENCIL_OT_interpolate(struct wmOperatorType *ot); void GPENCIL_OT_interpolate_sequence(struct wmOperatorType *ot); +void GPENCIL_OT_interpolate_reverse(struct wmOperatorType *ot); /* ****************************************************** */ /* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c new file mode 100644 index 00000000000..297058168a0 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -0,0 +1,1145 @@ +/* + * ***** 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) 2016, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + * + * Operators for interpolating new Grease Pencil frames from existing strokes + */ + +/** \file blender/editors/gpencil/gpencil_interpolate.c + * \ingroup edgpencil + */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_easing.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_color_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_library.h" +#include "BKE_report.h" +#include "BKE_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_view2d.h" + +#include "ED_gpencil.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_screen.h" +#include "ED_space_api.h" + +#include "gpencil_intern.h" + +/* ************************************************ */ +/* Core/Shared Utilities */ + +/* Poll callback for interpolation operators */ +static int gpencil_interpolate_poll(bContext *C) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + + /* only 3D view */ + if (CTX_wm_area(C)->spacetype != SPACE_VIEW3D) { + return 0; + } + + /* need data to interpolate */ + if (ELEM(NULL, gpd, gpl)) { + return 0; + } + + return 1; +} + +/* Perform interpolation */ +static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_to, bGPDstroke *new_stroke, float factor) +{ + bGPDspoint *prev, *pt, *next; + + /* update points */ + for (int i = 0; i < new_stroke->totpoints; i++) { + prev = &gps_from->points[i]; + pt = &new_stroke->points[i]; + next = &gps_to->points[i]; + + /* Interpolate all values */ + interp_v3_v3v3(&pt->x, &prev->x, &next->x, factor); + pt->pressure = interpf(prev->pressure, next->pressure, factor); + pt->strength = interpf(prev->strength, next->strength, factor); + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + } +} + +/* ****************** Interpolate Interactive *********************** */ + +/* Helper: Update all strokes interpolated */ +static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) +{ + tGPDinterpolate_layer *tgpil; + const float shift = tgpi->shift; + + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + bGPDstroke *new_stroke; + const float factor = tgpil->factor + shift; + + for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; new_stroke = new_stroke->next) { + bGPDstroke *gps_from, *gps_to; + int stroke_idx; + + if (new_stroke->totpoints == 0) { + continue; + } + + /* get strokes to interpolate */ + stroke_idx = BLI_findindex(&tgpil->interFrame->strokes, new_stroke); + + gps_from = BLI_findlink(&tgpil->prevFrame->strokes, stroke_idx); + gps_to = BLI_findlink(&tgpil->nextFrame->strokes, stroke_idx); + + /* update points position */ + if ((gps_from) && (gps_to)) { + gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + } + } + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); +} + +/* Helper: Verify valid strokes for interpolation */ +static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag; + + /* get layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* all layers or only active */ + if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* read strokes */ + for (bGPDstroke *gps_from = gpl->actframe->strokes.first; gps_from; gps_from = gps_from->next) { + bGPDstroke *gps_to; + int fFrame; + + /* only selected */ + if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { + continue; + } + + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from); + gps_to = BLI_findlink(&gpl->actframe->next->strokes, fFrame); + if (gps_to == NULL) { + continue; + } + + return true; + } + } + return false; +} + +/* Helper: Create internal strokes interpolated */ +static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) +{ + bGPdata *gpd = tgpi->gpd; + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDframe *actframe = active_gpl->actframe; + + /* save initial factor for active layer to define shift limits */ + tgpi->init_factor = (float)(tgpi->cframe - actframe->framenum) / (actframe->next->framenum - actframe->framenum + 1); + + /* limits are 100% below 0 and 100% over the 100% */ + tgpi->low_limit = -1.0f - tgpi->init_factor; + tgpi->high_limit = 2.0f - tgpi->init_factor; + + /* set layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + tGPDinterpolate_layer *tgpil; + + /* all layers or only active */ + if (!(tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && (gpl != active_gpl)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* create temp data for each layer */ + tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); + + tgpil->gpl = gpl; + tgpil->prevFrame = gpl->actframe; + tgpil->nextFrame = gpl->actframe->next; + + BLI_addtail(&tgpi->ilayers, tgpil); + + /* create a new temporary frame */ + tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe"); + tgpil->interFrame->framenum = tgpi->cframe; + + /* get interpolation factor by layer (usually must be equal for all layers, but not sure) */ + tgpil->factor = (float)(tgpi->cframe - tgpil->prevFrame->framenum) / (tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1); + + /* create new strokes data with interpolated points reading original stroke */ + for (bGPDstroke *gps_from = tgpil->prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { + bGPDstroke *gps_to; + int fFrame; + + bGPDstroke *new_stroke; + bool valid = true; + + + /* only selected */ + if ((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + valid = false; + } + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + valid = false; + } + + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(tgpil->gpl, gps_from) == false) { + valid = false; + } + + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from); + gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame); + if (gps_to == NULL) { + valid = false; + } + + /* create new stroke */ + new_stroke = MEM_dupallocN(gps_from); + new_stroke->points = MEM_dupallocN(gps_from->points); + new_stroke->triangles = MEM_dupallocN(gps_from->triangles); + + if (valid) { + /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ + if (gps_from->totpoints > gps_to->totpoints) { + new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); + new_stroke->totpoints = gps_to->totpoints; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + } + /* update points position */ + gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); + } + else { + /* need an empty stroke to keep index correct for lookup, but resize to smallest size */ + new_stroke->totpoints = 0; + new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points)); + new_stroke->tot_triangles = 0; + new_stroke->triangles = MEM_recallocN(new_stroke->triangles, sizeof(*new_stroke->triangles)); + } + + /* add to strokes */ + BLI_addtail(&tgpil->interFrame->strokes, new_stroke); + } + } +} + +/* ----------------------- */ +/* Drawing Callbacks */ + +/* Drawing callback for modal operator in screen mode */ +static void gpencil_interpolate_draw_screen(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +{ + tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; + ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_PIXEL); +} + +/* Drawing callback for modal operator in 3d mode */ +static void gpencil_interpolate_draw_3d(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +{ + tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; + ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_VIEW); +} + +/* ----------------------- */ + +/* Helper: calculate shift based on position of mouse (we only use x-axis for now. + * since this is more convenient for users to do), and store new shift value + */ +static void gpencil_mouse_update_shift(tGPDinterpolate *tgpi, wmOperator *op, const wmEvent *event) +{ + float mid = (float)(tgpi->ar->winx - tgpi->ar->winrct.xmin) / 2.0f; + float mpos = event->x - tgpi->ar->winrct.xmin; + + if (mpos >= mid) { + tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid; + } + else { + tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid); + } + + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); +} + +/* Helper: Draw status message while the user is running the operator */ +static void gpencil_interpolate_status_indicators(tGPDinterpolate *p) +{ + Scene *scene = p->scene; + char status_str[UI_MAX_DRAW_STR]; + char msg_str[UI_MAX_DRAW_STR]; + + BLI_strncpy(msg_str, IFACE_("GPencil Interpolation: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust factor"), UI_MAX_DRAW_STR); + + if (hasNumInput(&p->num)) { + char str_offs[NUM_STR_REP_LEN]; + + outputNumInput(&p->num, str_offs, &scene->unit); + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs); + } + else { + BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%", msg_str, (int)((p->init_factor + p->shift) * 100.0f)); + } + + ED_area_headerprint(p->sa, status_str); +} + +/* Update screen and stroke */ +static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) +{ + /* update shift indicator in header */ + gpencil_interpolate_status_indicators(tgpi); + /* apply... */ + tgpi->shift = RNA_float_get(op->ptr, "shift"); + /* update points position */ + gp_interpolate_update_strokes(C, tgpi); +} + +/* ----------------------- */ + +/* Exit and free memory */ +static void gpencil_interpolate_exit(bContext *C, wmOperator *op) +{ + tGPDinterpolate *tgpi = op->customdata; + tGPDinterpolate_layer *tgpil; + + /* don't assume that operator data exists at all */ + if (tgpi) { + /* remove drawing handler */ + if (tgpi->draw_handle_screen) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_screen); + } + if (tgpi->draw_handle_3d) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); + } + + /* clear status message area */ + ED_area_headerprint(tgpi->sa, NULL); + + /* finally, free memory used by temp data */ + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + BKE_gpencil_free_strokes(tgpil->interFrame); + MEM_freeN(tgpil->interFrame); + } + + BLI_freelistN(&tgpi->ilayers); + MEM_freeN(tgpi); + } + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* clear pointer */ + op->customdata = NULL; +} + +/* Init new temporary interpolation data */ +static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + + /* set current scene and window */ + tgpi->scene = CTX_data_scene(C); + tgpi->sa = CTX_wm_area(C); + tgpi->ar = CTX_wm_region(C); + tgpi->flag = ts->gp_interpolate.flag; + + /* set current frame number */ + tgpi->cframe = tgpi->scene->r.cfra; + + /* set GP datablock */ + tgpi->gpd = gpd; + + /* set interpolation weight */ + tgpi->shift = RNA_float_get(op->ptr, "shift"); + /* set layers */ + gp_interpolate_set_points(C, tgpi); + + return 1; +} + +/* Allocate memory and initialize values */ +static tGPDinterpolate *gp_session_init_interpolation(bContext *C, wmOperator *op) +{ + tGPDinterpolate *tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data"); + + /* define initial values */ + gp_interpolate_set_init_values(C, op, tgpi); + + /* return context data for running operator */ + return tgpi; +} + +/* Init interpolation: Allocate memory and set init values */ +static int gpencil_interpolate_init(bContext *C, wmOperator *op) +{ + tGPDinterpolate *tgpi; + + /* check context */ + tgpi = op->customdata = gp_session_init_interpolation(C, op); + if (tgpi == NULL) { + /* something wasn't set correctly in context */ + gpencil_interpolate_exit(C, op); + return 0; + } + + /* everything is now setup ok */ + return 1; +} + +/* ----------------------- */ + +/* Invoke handler: Initialize the operator */ +static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + wmWindow *win = CTX_wm_window(C); + Scene *scene = CTX_data_scene(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + bGPDframe *actframe = gpl->actframe; + tGPDinterpolate *tgpi = NULL; + + /* cannot interpolate if not between 2 frames */ + if (ELEM(NULL, actframe, actframe->next)) { + BKE_report(op->reports, RPT_ERROR, "Cannot find a pair of grease pencil frames to interpolate between in active layer"); + return OPERATOR_CANCELLED; + } + + /* cannot interpolate in extremes */ + if (ELEM(CFRA, actframe->framenum, actframe->next->framenum)) { + BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing grease pencil frames"); + return OPERATOR_CANCELLED; + } + + /* need editable strokes */ + if (!gp_interpolate_check_todo(C, gpd)) { + BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable strokes"); + return OPERATOR_CANCELLED; + } + + /* try to initialize context data needed */ + if (!gpencil_interpolate_init(C, op)) { + if (op->customdata) + MEM_freeN(op->customdata); + return OPERATOR_CANCELLED; + } + else { + tgpi = op->customdata; + } + + /* Enable custom drawing handlers + * It needs 2 handlers because strokes can in 3d space and screen space + * and each handler use different coord system + */ + tgpi->draw_handle_screen = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_screen, tgpi, REGION_DRAW_POST_PIXEL); + tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_3d, tgpi, REGION_DRAW_POST_VIEW); + + /* set cursor to indicate modal */ + WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); + + /* update shift indicator in header */ + gpencil_interpolate_status_indicators(tgpi); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* add a modal handler for this operator */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* Modal handler: Events handling during interactive part */ +static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPDinterpolate *tgpi = op->customdata; + wmWindow *win = CTX_wm_window(C); + bGPDframe *gpf_dst; + bGPDstroke *gps_src, *gps_dst; + tGPDinterpolate_layer *tgpil; + const bool has_numinput = hasNumInput(&tgpi->num); + + switch (event->type) { + case LEFTMOUSE: /* confirm */ + case RETKEY: + { + /* return to normal cursor and header status */ + ED_area_headerprint(tgpi->sa, NULL); + WM_cursor_modal_restore(win); + + /* insert keyframes as required... */ + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); + gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN; + + /* copy strokes */ + BLI_listbase_clear(&gpf_dst->strokes); + for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) { + if (gps_src->totpoints == 0) { + continue; + } + + /* make copy of source stroke, then adjust pointer to points too */ + gps_dst = MEM_dupallocN(gps_src); + gps_dst->points = MEM_dupallocN(gps_src->points); + gps_dst->triangles = MEM_dupallocN(gps_src->triangles); + gps_dst->flag |= GP_STROKE_RECALC_CACHES; + BLI_addtail(&gpf_dst->strokes, gps_dst); + } + } + + /* clean up temp data */ + gpencil_interpolate_exit(C, op); + + /* done! */ + return OPERATOR_FINISHED; + } + + case ESCKEY: /* cancel */ + case RIGHTMOUSE: + { + /* return to normal cursor and header status */ + ED_area_headerprint(tgpi->sa, NULL); + WM_cursor_modal_restore(win); + + /* clean up temp data */ + gpencil_interpolate_exit(C, op); + + /* canceled! */ + return OPERATOR_CANCELLED; + } + + case WHEELUPMOUSE: + { + tgpi->shift = tgpi->shift + 0.01f; + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + break; + } + case WHEELDOWNMOUSE: + { + tgpi->shift = tgpi->shift - 0.01f; + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + break; + } + case MOUSEMOVE: /* calculate new position */ + { + /* only handle mousemove if not doing numinput */ + if (has_numinput == false) { + /* update shift based on position of mouse */ + gpencil_mouse_update_shift(tgpi, op, event); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + } + break; + } + default: + if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { + const float factor = tgpi->init_factor; + float value; + + /* Grab shift from numeric input, and store this new value (the user see an int) */ + value = (factor + tgpi->shift) * 100.0f; + applyNumInput(&tgpi->num, &value); + tgpi->shift = value / 100.0f; + + /* recalculate the shift to get the right value in the frame scale */ + tgpi->shift = tgpi->shift - factor; + + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + + break; + } + else { + /* unhandled event - allow to pass through */ + return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + } + } + + /* still running... */ + return OPERATOR_RUNNING_MODAL; +} + +/* Cancel handler */ +static void gpencil_interpolate_cancel(bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + gpencil_interpolate_exit(C, op); +} + +void GPENCIL_OT_interpolate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Grease Pencil Interpolation"; + ot->idname = "GPENCIL_OT_interpolate"; + ot->description = "Interpolate grease pencil strokes between frames"; + + /* callbacks */ + ot->invoke = gpencil_interpolate_invoke; + ot->modal = gpencil_interpolate_modal; + ot->cancel = gpencil_interpolate_cancel; + ot->poll = gpencil_interpolate_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + RNA_def_float_percentage(ot->srna, "shift", 0.0f, -1.0f, 1.0f, "Shift", "Bias factor for which frame has more influence on the interpolated strokes", -0.9f, 0.9f); +} + +/* ****************** Interpolate Sequence *********************** */ + +/* Helper: Perform easing equation calculations for GP interpolation operator */ +static float gp_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_settings, float time) +{ + const float begin = 0.0f; + const float change = 1.0f; + const float duration = 1.0f; + + const float back = ipo_settings->back; + const float amplitude = ipo_settings->amplitude; + const float period = ipo_settings->period; + + eBezTriple_Easing easing = ipo_settings->easing; + float result = time; + + switch (ipo_settings->type) { + case GP_IPO_BACK: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_back_ease_in(time, begin, change, duration, back); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_back_ease_out(time, begin, change, duration, back); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_back_ease_in_out(time, begin, change, duration, back); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_back_ease_out(time, begin, change, duration, back); + break; + } + break; + + case GP_IPO_BOUNCE: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_bounce_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_bounce_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + } + break; + + case GP_IPO_CIRC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_circ_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_circ_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_CUBIC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_cubic_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_cubic_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_ELASTIC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); + break; + } + break; + + case GP_IPO_EXPO: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_expo_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_expo_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUAD: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quad_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quad_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUART: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quart_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quart_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUINT: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quint_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quint_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_SINE: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_sine_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_sine_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + } + break; + + default: + printf("%s: Unknown interpolation type - %d\n", __func__, ipo_settings->type); + break; + } + + return result; +} + +static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDframe *actframe = active_gpl->actframe; + + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate; + eGP_Interpolate_SettingsFlag flag = ipo_settings->flag; + + /* cannot interpolate if not between 2 frames */ + if (ELEM(NULL, actframe, actframe->next)) { + BKE_report(op->reports, RPT_ERROR, "Cannot find a pair of grease pencil frames to interpolate between in active layer"); + return OPERATOR_CANCELLED; + } + /* cannot interpolate in extremes */ + if (ELEM(CFRA, actframe->framenum, actframe->next->framenum)) { + BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing grease pencil frames"); + return OPERATOR_CANCELLED; + } + + /* loop all layer to check if need interpolation */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *prevFrame, *nextFrame; + bGPDstroke *gps_from, *gps_to; + int cframe, fFrame; + + /* all layers or only active */ + if (((flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* store extremes */ + prevFrame = gpl->actframe; + nextFrame = gpl->actframe->next; + + /* Loop over intermediary frames and create the interpolation */ + for (cframe = prevFrame->framenum + 1; cframe < nextFrame->framenum; cframe++) { + bGPDframe *interFrame = NULL; + float factor; + + /* get interpolation factor */ + factor = (float)(cframe - prevFrame->framenum) / (nextFrame->framenum - prevFrame->framenum + 1); + + if (ipo_settings->type == GP_IPO_CURVEMAP) { + /* custom curvemap */ + if (ipo_settings->custom_ipo) { + factor = curvemapping_evaluateF(ipo_settings->custom_ipo, 0, factor); + } + else { + BKE_report(op->reports, RPT_ERROR, "Custom interpolation curve does not exist"); + } + } + else if (ipo_settings->type >= GP_IPO_BACK) { + /* easing equation... */ + factor = gp_interpolate_seq_easing_calc(ipo_settings, factor); + } + + /* create new strokes data with interpolated points reading original stroke */ + for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { + bGPDstroke *new_stroke; + + /* only selected */ + if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { + continue; + } + + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&prevFrame->strokes, gps_from); + gps_to = BLI_findlink(&nextFrame->strokes, fFrame); + if (gps_to == NULL) { + continue; + } + + /* create a new frame if needed */ + if (interFrame == NULL) { + interFrame = BKE_gpencil_layer_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW); + interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; + } + + /* create new stroke */ + new_stroke = MEM_dupallocN(gps_from); + new_stroke->points = MEM_dupallocN(gps_from->points); + new_stroke->triangles = MEM_dupallocN(gps_from->triangles); + + /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ + if (gps_from->totpoints > gps_to->totpoints) { + new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); + new_stroke->totpoints = gps_to->totpoints; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + } + + /* update points position */ + gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + + /* add to strokes */ + BLI_addtail(&interFrame->strokes, new_stroke); + } + } + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Interpolate Sequence"; + ot->idname = "GPENCIL_OT_interpolate_sequence"; + ot->description = "Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames"; + + /* api callbacks */ + ot->exec = gpencil_interpolate_seq_exec; + ot->poll = gpencil_interpolate_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************** Remove Breakdowns ************************ */ + +/* Same as gpencil_interpolate_poll(), + * except we ALSO need to have an active frame that is a breakdown + */ +static int gpencil_interpolate_reverse_poll(bContext *C) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + + /* only 3D view */ + if (CTX_wm_area(C)->spacetype != SPACE_VIEW3D) { + return 0; + } + + /* need data to interpolate */ + if (ELEM(NULL, gpd, gpl)) { + return 0; + } + + /* need to be on a breakdown frame */ + if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) { + CTX_wm_operator_poll_msg_set(C, "Expected current frame to be a breakdown"); + return 0; + } + + return 1; +} + +static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op)) +{ + /* Go through each layer, deleting the breakdowns around the current frame, + * but only if there is a keyframe nearby to stop at + */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *start_key = NULL; + bGPDframe *end_key = NULL; + bGPDframe *gpf, *gpfn; + + /* Only continue if we're currently on a breakdown keyframe */ + if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) + continue; + + /* Search left for "start_key" (i.e. the first breakdown to remove) */ + gpf = gpl->actframe; + while (gpf) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { + /* A breakdown... keep going left */ + start_key = gpf; + gpf = gpf->prev; + } + else { + /* Not a breakdown (may be a key, or an extreme, or something else that wasn't generated)... stop */ + break; + } + } + + /* Search right for "end_key" (i.e. the last breakdown to remove) */ + gpf = gpl->actframe; + while (gpf) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { + /* A breakdown... keep going right */ + end_key = gpf; + gpf = gpf->next; + } + else { + /* Not a breakdown... stop */ + break; + } + } + + /* Did we find anything? */ + /* NOTE: We should only proceed if there's something before/after these extents... + * Otherwise, there's just an extent of breakdowns with no keys to interpolate between + */ + if ((start_key && end_key) && + ELEM(NULL, start_key->prev, end_key->next) == false) + { + /* Set actframe to the key before start_key, since the keys have been removed now */ + gpl->actframe = start_key->prev; + + /* Free each frame we're removing (except the last one) */ + for (gpf = start_key; gpf && gpf != end_key; gpf = gpfn) { + gpfn = gpf->next; + + /* free strokes and their associated memory */ + BKE_gpencil_free_strokes(gpf); + BLI_freelinkN(&gpl->frames, gpf); + } + + /* Now free the last one... */ + BKE_gpencil_free_strokes(end_key); + BLI_freelinkN(&gpl->frames, end_key); + } + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_interpolate_reverse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Breakdowns"; + ot->idname = "GPENCIL_OT_interpolate_reverse"; + ot->description = "Remove breakdown frames generated by interpolating between two Grease Pencil frames"; + + /* callbacks */ + ot->exec = gpencil_interpolate_reverse_exec; + ot->poll = gpencil_interpolate_reverse_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* *************************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 057d53ea458..78e1a0dda36 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -102,6 +102,10 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_tool_palette", QKEY, KM_PRESS, 0, DKEY); WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_settings_palette", WKEY, KM_PRESS, 0, DKEY); + /* Add Blank Frame */ + /* XXX: BKEY or NKEY? BKEY is easier to reach from DKEY, so we'll use that for now */ + WM_keymap_add_item(keymap, "GPENCIL_OT_blank_frame_add", BKEY, KM_PRESS, 0, DKEY); + /* Delete Active Frame - For easier video tutorials/review sessions */ /* NOTE: This works even when not in EditMode */ WM_keymap_add_item(keymap, "GPENCIL_OT_active_frames_delete_all", XKEY, KM_PRESS, 0, DKEY); @@ -401,6 +405,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_layer_isolate); WM_operatortype_append(GPENCIL_OT_layer_merge); + WM_operatortype_append(GPENCIL_OT_blank_frame_add); + WM_operatortype_append(GPENCIL_OT_active_frame_delete); WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); @@ -443,6 +449,7 @@ void ED_operatortypes_gpencil(void) /* Interpolation */ WM_operatortype_append(GPENCIL_OT_interpolate); WM_operatortype_append(GPENCIL_OT_interpolate_sequence); + WM_operatortype_append(GPENCIL_OT_interpolate_reverse); } void ED_operatormacros_gpencil(void) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index b483402d6c8..73f393537ba 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1625,16 +1625,24 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) * 2) Ensure that p->gpf refers to the frame used for the active layer * (to avoid problems with other tools which expect it to exist) */ - bGPDlayer *gpl; - for (gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { + bool has_layer_to_erase = false; + + for (bGPDlayer *gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { /* Skip if layer not editable */ if (gpencil_layer_is_editable(gpl) == false) continue; /* Add a new frame if needed (and based off the active frame, * as we need some existing strokes to erase) + * + * Note: We don't add a new frame if there's nothing there now, so + * -> If there are no frames at all, don't add one + * -> If there are no strokes in that frame, don't add a new empty frame */ - gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); + if (gpl->actframe && gpl->actframe->strokes.first) { + gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); + has_layer_to_erase = true; + } /* XXX: we omit GP_FRAME_PAINT here for now, * as it is only really useful for doing @@ -1654,10 +1662,10 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) } } - if (p->gpf == NULL) { + if (has_layer_to_erase == false) { p->status = GP_STATUS_ERROR; //if (G.debug & G_DEBUG) - printf("Error: No frame created (gpencil_paint_init)\n"); + printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n"); return; } } @@ -2434,6 +2442,14 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* enable continuous if release D key in mid drawing */ p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; } + else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { + /* Add Blank Frame + * - Since this operator is non-modal, we can just call it here, and keep going... + * - This operator is especially useful when animating + */ + WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); + estate = OPERATOR_RUNNING_MODAL; + } else { estate = OPERATOR_RUNNING_MODAL; } @@ -2596,7 +2612,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } else if (p->status != GP_STATUS_ERROR) { /* User clicked outside bounds of window while idling, so exit paintmode - * NOTE: Don't eter this case if an error occurred while finding the + * NOTE: Don't enter this case if an error occurred while finding the * region (as above) */ /* if drawing polygon and enable on back, must move stroke */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index a913421d12c..682db20af55 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1993,7 +1993,7 @@ uiBut *ui_but_drag_multi_edit_get(uiBut *but) static bool ui_but_icon_extra_is_visible_text_clear(const uiBut *but) { BLI_assert(but->type == UI_BTYPE_TEXT); - return ((but->flag & UI_BUT_VALUE_CLEAR) && but->drawstr && but->drawstr[0]); + return ((but->flag & UI_BUT_VALUE_CLEAR) && but->drawstr[0]); } static bool ui_but_icon_extra_is_visible_search_unlink(const uiBut *but) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 241ac99edde..9bdc4d29807 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2954,7 +2954,7 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in if (pbuf) { if (ui_but_is_utf8(but)) { - buf_len -= BLI_utf8_invalid_strip(pbuf, buf_len); + buf_len -= BLI_utf8_invalid_strip(pbuf, (size_t)buf_len); } ui_textedit_insert_buf(but, data, pbuf, buf_len); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index c57b0215d46..8f004bcf72b 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -684,27 +684,38 @@ static void edbm_add_edge_face_exec__tricky_finalize_sel(BMesh *bm, BMElem *ele_ /* now we need to find the edge that isnt connected to this element */ BM_select_history_clear(bm); + /* Notes on hidden geometry: + * - un-hide the face since its possible hidden was copied when copying surrounding face attributes. + * - un-hide before adding to select history + * since we may extend into an existing, hidden vert/edge. + */ + + BM_elem_flag_disable(f, BM_ELEM_HIDDEN); + BM_face_select_set(bm, f, false); + if (ele_desel->head.htype == BM_VERT) { BMLoop *l = BM_face_vert_share_loop(f, (BMVert *)ele_desel); BLI_assert(f->len == 3); - BM_face_select_set(bm, f, false); BM_vert_select_set(bm, (BMVert *)ele_desel, false); - BM_edge_select_set(bm, l->next->e, true); BM_select_history_store(bm, l->next->e); } else { BMLoop *l = BM_face_edge_share_loop(f, (BMEdge *)ele_desel); BLI_assert(f->len == 4 || f->len == 3); - BM_face_select_set(bm, f, false); + BM_edge_select_set(bm, (BMEdge *)ele_desel, false); if (f->len == 4) { - BM_edge_select_set(bm, l->next->next->e, true); - BM_select_history_store(bm, l->next->next->e); + BMEdge *e_active = l->next->next->e; + BM_elem_flag_disable(e_active, BM_ELEM_HIDDEN); + BM_edge_select_set(bm, e_active, true); + BM_select_history_store(bm, e_active); } else { - BM_vert_select_set(bm, l->next->next->v, true); - BM_select_history_store(bm, l->next->next->v); + BMVert *v_active = l->next->next->v; + BM_elem_flag_disable(v_active, BM_ELEM_HIDDEN); + BM_vert_select_set(bm, v_active, true); + BM_select_history_store(bm, v_active); } } } @@ -758,6 +769,14 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) else #endif { + /* Newly created faces may include existing hidden edges, + * copying face data from surrounding, may have copied hidden face flag too. + * + * Important that faces use flushing since 'edges.out' wont include hidden edges that already existed. + */ + BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_HIDDEN, true); + BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, false); + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); } diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index df9c67b1f2e..d82d38ac0eb 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -171,6 +171,8 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) unsigned char col1a[3], col2a[3]; unsigned char col1b[3], col2b[3]; + const bool show_group_colors = !(saction->flag & SACTION_NODRAWGCOLORS); + /* get theme colors */ UI_GetThemeColor3ubv(TH_BACK, col2); @@ -254,8 +256,36 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) } case ANIMTYPE_GROUP: { - if (sel) immUniformColor4ub(col1a[0], col1a[1], col1a[2], 0x22); - else immUniformColor4ub(col2a[0], col2a[1], col2a[2], 0x22); + bActionGroup *agrp = ale->data; + if (show_group_colors && agrp->customCol) { + if (sel) { + unsigned char *cp = agrp->cs.select; + immUniformColor4ub(cp[0], cp[1], cp[2], 0x45); + } + else { + unsigned char *cp = agrp->cs.solid; + immUniformColor4ub(cp[0], cp[1], cp[2], 0x1D); + } + } + else { + if (sel) immUniformColor4ub(col1a[0], col1a[1], col1a[2], 0x22); + else immUniformColor4ub(col2a[0], col2a[1], col2a[2], 0x22); + } + break; + } + case ANIMTYPE_FCURVE: + { + FCurve *fcu = ale->data; + if (show_group_colors && fcu->grp && fcu->grp->customCol) { + unsigned char *cp = fcu->grp->cs.active; + + if (sel) immUniformColor4ub(cp[0], cp[1], cp[2], 0x65); + else immUniformColor4ub(cp[0], cp[1], cp[2], 0x0B); + } + else { + if (sel) immUniformColor4ub(col1[0], col1[1], col1[2], 0x22); + else immUniformColor4ub(col2[0], col2[1], col2[2], 0x22); + } break; } default: diff --git a/source/blender/editors/space_view3d/drawanimviz.c b/source/blender/editors/space_view3d/drawanimviz.c index cf738de0202..f0e65f84205 100644 --- a/source/blender/editors/space_view3d/drawanimviz.c +++ b/source/blender/editors/space_view3d/drawanimviz.c @@ -75,6 +75,80 @@ void draw_motion_paths_init(View3D *v3d, ARegion *ar) glLoadMatrixf(rv3d->viewmat); } +/* set color +* - more intense for active/selected bones, less intense for unselected bones +* - black for before current frame, green for current frame, blue for after current frame +* - intensity decreases as distance from current frame increases +* +* If the user select custom color, the color is replaced for the color selected in UI panel +* - 75% Darker color is used for previous frames +* - 50% Darker color for current frame +* - User selected color for next frames +*/ +static void set_motion_path_color(Scene *scene, bMotionPath *mpath, int i, short sel, int sfra, int efra, + float prev_color[3], float frame_color[3], float next_color[3]) +{ + int frame = sfra + i; + int blend_base = (abs(frame - CFRA) == 1) ? TH_CFRAME : TH_BACK; /* "bleed" cframe color to ease color blending */ + +#define SET_INTENSITY(A, B, C, min, max) (((1.0f - ((C - B) / (C - A))) * (max - min)) + min) + float intensity; /* how faint */ + + if (frame < CFRA) { + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + /* Custom color: previous frames color is darker than current frame */ + glColor3fv(prev_color); + } + else { + /* black - before cfra */ + if (sel) { + /* intensity = 0.5f; */ + intensity = SET_INTENSITY(sfra, i, CFRA, 0.25f, 0.75f); + } + else { + /* intensity = 0.8f; */ + intensity = SET_INTENSITY(sfra, i, CFRA, 0.68f, 0.92f); + } + UI_ThemeColorBlend(TH_WIRE, blend_base, intensity); + } + } + else if (frame > CFRA) { + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + /* Custom color: next frames color is equal to user selected color */ + glColor3fv(next_color); + } + else { + /* blue - after cfra */ + if (sel) { + /* intensity = 0.5f; */ + intensity = SET_INTENSITY(CFRA, i, efra, 0.25f, 0.75f); + } + else { + /* intensity = 0.8f; */ + intensity = SET_INTENSITY(CFRA, i, efra, 0.68f, 0.92f); + } + UI_ThemeColorBlend(TH_BONE_POSE, blend_base, intensity); + } + } + else { + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + /* Custom color: current frame color is slightly darker than user selected color */ + glColor3fv(frame_color); + } + else { + /* green - on cfra */ + if (sel) { + intensity = 0.5f; + } + else { + intensity = 0.99f; + } + UI_ThemeColorBlendShade(TH_CFRAME, TH_BACK, intensity, 10); + } + } +#undef SET_INTENSITY +} + /* Draw the given motion path for an Object or a Bone * - assumes that the viewport has already been initialized properly * i.e. draw_motion_paths_init() has been called @@ -86,6 +160,28 @@ void draw_motion_path_instance(Scene *scene, bMotionPathVert *mpv, *mpv_start; int i, stepsize = avs->path_step; int sfra, efra, sind, len; + float prev_color[3]; + float frame_color[3]; + float next_color[3]; + + /* Custom color - Previous frames: color is darker than current frame */ + prev_color[0] = mpath->color[0] * 0.25f; + prev_color[1] = mpath->color[1] * 0.25f; + prev_color[2] = mpath->color[2] * 0.25f; + + /* Custom color - Current frame: color is slightly darker than user selected color */ + frame_color[0] = mpath->color[0] * 0.50f; + frame_color[1] = mpath->color[1] * 0.50f; + frame_color[2] = mpath->color[2] * 0.50f; + + /* Custom color - Next frames: color is equal to user selection */ + next_color[0] = mpath->color[0]; + next_color[1] = mpath->color[1]; + next_color[2] = mpath->color[2]; + + /* Save old line width */ + GLfloat old_width; + glGetFloatv(GL_LINE_WIDTH, &old_width); /* get frame ranges */ if (avs->path_type == MOTIONPATH_TYPE_ACFRA) { @@ -130,64 +226,27 @@ void draw_motion_path_instance(Scene *scene, mpv_start = (mpath->points + sind); /* draw curve-line of path */ - - glBegin(GL_LINE_STRIP); - for (i = 0, mpv = mpv_start; i < len; i++, mpv++) { - short sel = (pchan) ? (pchan->bone->flag & BONE_SELECTED) : (ob->flag & SELECT); - float intensity; /* how faint */ - - int frame = sfra + i; - int blend_base = (abs(frame - CFRA) == 1) ? TH_CFRAME : TH_BACK; /* "bleed" cframe color to ease color blending */ - - /* set color - * - more intense for active/selected bones, less intense for unselected bones - * - black for before current frame, green for current frame, blue for after current frame - * - intensity decreases as distance from current frame increases - */ -#define SET_INTENSITY(A, B, C, min, max) (((1.0f - ((C - B) / (C - A))) * (max - min)) + min) - if (frame < CFRA) { - /* black - before cfra */ - if (sel) { - /* intensity = 0.5f; */ - intensity = SET_INTENSITY(sfra, i, CFRA, 0.25f, 0.75f); - } - else { - /* intensity = 0.8f; */ - intensity = SET_INTENSITY(sfra, i, CFRA, 0.68f, 0.92f); - } - UI_ThemeColorBlend(TH_WIRE, blend_base, intensity); - } - else if (frame > CFRA) { - /* blue - after cfra */ - if (sel) { - /* intensity = 0.5f; */ - intensity = SET_INTENSITY(CFRA, i, efra, 0.25f, 0.75f); - } - else { - /* intensity = 0.8f; */ - intensity = SET_INTENSITY(CFRA, i, efra, 0.68f, 0.92f); - } - UI_ThemeColorBlend(TH_BONE_POSE, blend_base, intensity); - } - else { - /* green - on cfra */ - if (sel) { - intensity = 0.5f; - } - else { - intensity = 0.99f; - } - UI_ThemeColorBlendShade(TH_CFRAME, TH_BACK, intensity, 10); + /* Draw lines only if line drawing option is enabled */ + if (mpath->flag & MOTIONPATH_FLAG_LINES) { + /* set line thickness */ + glLineWidth(mpath->line_thickness); + + glBegin(GL_LINE_STRIP); + for (i = 0, mpv = mpv_start; i < len; i++, mpv++) { + short sel = (pchan) ? (pchan->bone->flag & BONE_SELECTED) : (ob->flag & SELECT); + /* Set color */ + set_motion_path_color(scene, mpath, i, sel, sfra, efra, prev_color, frame_color, next_color); + /* draw a vertex with this color */ + glVertex3fv(mpv->co); } -#undef SET_INTENSITY - /* draw a vertex with this color */ - glVertex3fv(mpv->co); + glEnd(); + /* back to old line thickness */ + glLineWidth(old_width); } - - glEnd(); - - glPointSize(1.0); + + /* Point must be bigger than line thickness */ + glPointSize(mpath->line_thickness + 1.0); /* draw little black point at each frame * NOTE: this is not really visible/noticeable @@ -197,8 +256,13 @@ void draw_motion_path_instance(Scene *scene, glVertex3fv(mpv->co); glEnd(); - /* Draw little white dots at each framestep value */ - UI_ThemeColor(TH_TEXT_HI); + /* Draw little white dots at each framestep value or replace with custom color */ + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + glColor4fv(mpath->color); + } + else { + UI_ThemeColor(TH_TEXT_HI); + } glBegin(GL_POINTS); for (i = 0, mpv = mpv_start; i < len; i += stepsize, mpv += stepsize) glVertex3fv(mpv->co); @@ -208,11 +272,11 @@ void draw_motion_path_instance(Scene *scene, * NOTE: this is only done when keyframes are shown, since this adds similar types of clutter */ if ((avs->path_viewflag & MOTIONPATH_VIEW_KFRAS) && - (sfra < CFRA) && (CFRA <= efra)) + (sfra < CFRA) && (CFRA <= efra)) { UI_ThemeColor(TH_CFRAME); - glPointSize(6.0f); + glPointSize(mpath->line_thickness + 5.0); glBegin(GL_POINTS); mpv = mpv_start + (CFRA - sfra); glVertex3fv(mpv->co); @@ -289,7 +353,13 @@ void draw_motion_path_instance(Scene *scene, UI_GetThemeColor3ubv(TH_VERTEX_SELECT, col); col[3] = 255; - glPointSize(4.0f); + /* if custom, point must be bigger than line */ + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + glPointSize(mpath->line_thickness + 3.0); + } + else { + glPointSize(4.0f); + } glColor3ubv(col); glBegin(GL_POINTS); diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c index 7ac176f82b1..af041e392c5 100644 --- a/source/blender/editors/space_view3d/drawarmature.c +++ b/source/blender/editors/space_view3d/drawarmature.c @@ -2459,6 +2459,10 @@ static void draw_ghost_poses_range(Scene *scene, View3D *v3d, ARegion *ar, Base if (end <= start) return; + /* prevent infinite loops if this is set to 0 - T49527 */ + if (arm->ghostsize < 1) + arm->ghostsize = 1; + stepsize = (float)(arm->ghostsize); range = (float)(end - start); @@ -2604,7 +2608,11 @@ static void draw_ghost_poses(Scene *scene, View3D *v3d, ARegion *ar, Base *base) calc_action_range(adt->action, &start, &end, 0); if (start == end) return; - + + /* prevent infinite loops if this is set to 0 - T49527 */ + if (arm->ghostsize < 1) + arm->ghostsize = 1; + stepsize = (float)(arm->ghostsize); range = (float)(arm->ghostep) * stepsize + 0.5f; /* plus half to make the for loop end correct */ diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 6a21592db22..751d750d5ac 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -2187,7 +2187,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve calculateCenter(t); if (event) { - initMouseInput(t, &t->mouse, t->center2d, event->mval); + initMouseInput(t, &t->mouse, t->center2d, event->mval, event->shift); } switch (mode) { @@ -8511,7 +8511,7 @@ static void initTimeScale(TransInfo *t) center[1] = t->mouse.imval[1]; /* force a reinit with the center2d used here */ - initMouseInput(t, &t->mouse, center, t->mouse.imval); + initMouseInput(t, &t->mouse, center, t->mouse.imval, false); initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index a59f9dc43dd..7ea4448a44e 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -728,7 +728,7 @@ typedef enum { INPUT_CUSTOM_RATIO_FLIP, } MouseInputMode; -void initMouseInput(TransInfo *t, MouseInput *mi, const float center[2], const int mval[2]); +void initMouseInput(TransInfo *t, MouseInput *mi, const float center[2], const int mval[2], const bool precision); void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode); eRedrawFlag handleMouseInput(struct TransInfo *t, struct MouseInput *mi, const struct wmEvent *event); void applyMouseInput(struct TransInfo *t, struct MouseInput *mi, const int mval[2], float output[3]); diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c index 9b7d19eacd5..42cc918ec8c 100644 --- a/source/blender/editors/transform/transform_input.c +++ b/source/blender/editors/transform/transform_input.c @@ -234,10 +234,10 @@ static void InputAngleSpring(TransInfo *t, MouseInput *mi, const double mval[2], output[1] = toutput[0]; } -void initMouseInput(TransInfo *UNUSED(t), MouseInput *mi, const float center[2], const int mval[2]) +void initMouseInput(TransInfo *UNUSED(t), MouseInput *mi, const float center[2], const int mval[2], const bool precision) { mi->factor = 0; - mi->precision = 0; + mi->precision = precision; mi->center[0] = center[0]; mi->center[1] = center[1]; diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 4416b6494f9..aa583c5ecf8 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -1358,7 +1358,7 @@ void mtex_cube_map_refl_from_refldir( samplerCube ima, vec3 reflecteddirection, out float value, out vec4 color) { color = textureCube(ima, reflecteddirection); - value = 1.0; + value = color.a; } void mtex_cube_map_refl( diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index f3df9090d41..1083400ece2 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -76,6 +76,8 @@ typedef struct bMotionPath { int start_frame; /* for drawing paths, the start frame number */ int end_frame; /* for drawing paths, the end frame number */ + float color[3]; /* optional custom color */ + int line_thickness; /* line thickness */ int flag; /* baking settings - eMotionPath_Flag */ } bMotionPath; @@ -84,7 +86,11 @@ typedef enum eMotionPath_Flag { /* (for bones) path represents the head of the bone */ MOTIONPATH_FLAG_BHEAD = (1 << 0), /* motion path is being edited */ - MOTIONPATH_FLAG_EDIT = (1 << 1) + MOTIONPATH_FLAG_EDIT = (1 << 1), + /* Custom colors */ + MOTIONPATH_FLAG_CUSTOM = (1 << 2), + /* Draw lines or only points */ + MOTIONPATH_FLAG_LINES = (1 << 3) } eMotionPath_Flag; /* Visualization General --------------------------- */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f5e71ae59a9..8ee15ef21a3 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1212,12 +1212,51 @@ typedef enum eGP_BrushEdit_SettingsFlag { GP_BRUSHEDIT_FLAG_APPLY_STRENGTH = (1 << 2), /* apply brush to thickness */ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3), +} eGP_BrushEdit_SettingsFlag; + + +/* Settings for GP Interpolation Operators */ +typedef struct GP_Interpolate_Settings { + short flag; /* eGP_Interpolate_SettingsFlag */ + + char type; /* eGP_Interpolate_Type - Interpolation Mode */ + char easing; /* eBezTriple_Easing - Easing mode (if easing equation used) */ + + float back; /* BEZT_IPO_BACK */ + float amplitude, period; /* BEZT_IPO_ELASTIC */ + + struct CurveMapping *custom_ipo; /* custom interpolation curve (for use with GP_IPO_CURVEMAP) */ +} GP_Interpolate_Settings; + +/* GP_Interpolate_Settings.flag */ +typedef enum eGP_Interpolate_SettingsFlag { /* apply interpolation to all layers */ - GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS = (1 << 4), + GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS = (1 << 0), /* apply interpolation to only selected */ - GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED = (1 << 5) + GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED = (1 << 1), +} eGP_Interpolate_SettingsFlag; + +/* GP_Interpolate_Settings.type */ +typedef enum eGP_Interpolate_Type { + /* Traditional Linear Interpolation */ + GP_IPO_LINEAR = 0, + + /* CurveMap Defined Interpolation */ + GP_IPO_CURVEMAP = 1, + + /* Easing Equations */ + GP_IPO_BACK = 3, + GP_IPO_BOUNCE = 4, + GP_IPO_CIRC = 5, + GP_IPO_CUBIC = 6, + GP_IPO_ELASTIC = 7, + GP_IPO_EXPO = 8, + GP_IPO_QUAD = 9, + GP_IPO_QUART = 10, + GP_IPO_QUINT = 11, + GP_IPO_SINE = 12, +} eGP_Interpolate_Type; -} eGP_BrushEdit_SettingsFlag; /* *************************************************************** */ /* Transform Orientations */ @@ -1435,7 +1474,10 @@ typedef struct ToolSettings { /* Grease Pencil Sculpt */ struct GP_BrushEdit_Settings gp_sculpt; - + + /* Grease Pencil Interpolation Tool(s) */ + struct GP_Interpolate_Settings gp_interpolate; + /* Grease Pencil Drawing Brushes (bGPDbrush) */ ListBase gp_brushes; diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index f97a5735c94..66e6f30feeb 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -262,6 +262,7 @@ extern StructRNA RNA_GPencilLayer; extern StructRNA RNA_GPencilPalette; extern StructRNA RNA_GPencilPaletteColor; extern StructRNA RNA_GPencilBrush; +extern StructRNA RNA_GPencilInterpolateSettings; extern StructRNA RNA_GPencilStroke; extern StructRNA RNA_GPencilStrokePoint; extern StructRNA RNA_GPencilSculptSettings; diff --git a/source/blender/makesrna/intern/rna_animviz.c b/source/blender/makesrna/intern/rna_animviz.c index 8e42e68ed1e..eea24bfb1e0 100644 --- a/source/blender/makesrna/intern/rna_animviz.c +++ b/source/blender/makesrna/intern/rna_animviz.c @@ -153,7 +153,20 @@ static void rna_def_animviz_motion_path(BlenderRNA *brna) prop = RNA_def_property(srna, "length", PROP_INT, PROP_TIME); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Length", "Number of frames cached"); - + + /* Custom Color */ + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Color", "Custom color for motion path"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + /* Line width */ + prop = RNA_def_property(srna, "line_thickness", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "line_thickness"); + RNA_def_property_range(prop, 1, 6); + RNA_def_property_ui_text(prop, "Line thickness", "Line thickness for drawing path"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + /* Settings */ prop = RNA_def_property(srna, "use_bone_head", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MOTIONPATH_FLAG_BHEAD); @@ -164,6 +177,19 @@ static void rna_def_animviz_motion_path(BlenderRNA *brna) prop = RNA_def_property(srna, "is_modified", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MOTIONPATH_FLAG_EDIT); RNA_def_property_ui_text(prop, "Edit Path", "Path is being edited"); + + /* Use custom color */ + prop = RNA_def_property(srna, "use_custom_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOTIONPATH_FLAG_CUSTOM); + RNA_def_property_ui_text(prop, "Custom colors", "Use custom color for this motion path"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + /* Draw lines between keyframes */ + prop = RNA_def_property(srna, "lines", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOTIONPATH_FLAG_LINES); + RNA_def_property_ui_text(prop, "Lines", "Draw straight lines between keyframe points"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + } /* --- */ @@ -337,6 +363,7 @@ static void rna_def_animviz_paths(BlenderRNA *brna) "Number of frames to show after the current frame " "(only for 'Around Current Frame' Onion-skinning method)"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); /* XXX since this is only for 3d-view drawing */ + } /* --- */ diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 9c66a86dcee..25cd7265c3e 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -60,6 +60,7 @@ static EnumPropertyItem parent_type_items[] = { #include "WM_api.h" +#include "BKE_animsys.h" #include "BKE_gpencil.h" #include "BKE_action.h" @@ -353,10 +354,16 @@ static void rna_GPencilLayer_info_set(PointerRNA *ptr, const char *value) bGPdata *gpd = ptr->id.data; bGPDlayer *gpl = ptr->data; + char oldname[128] = ""; + BLI_strncpy(oldname, gpl->info, sizeof(oldname)); + /* copy the new name into the name slot */ BLI_strncpy_utf8(gpl->info, value, sizeof(gpl->info)); BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); + + /* now fix animation paths */ + BKE_animdata_fix_paths_rename_all(&gpd->id, "layers", oldname, gpl->info); } static void rna_GPencil_use_onion_skinning_set(PointerRNA *ptr, const int value) @@ -814,14 +821,20 @@ static void rna_GPencilPaletteColor_info_set(PointerRNA *ptr, const char *value) bGPdata *gpd = ptr->id.data; bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); bGPDpalettecolor *palcolor = ptr->data; - - /* rename all strokes */ - BKE_gpencil_palettecolor_changename(gpd, palcolor->info, value); + + char oldname[64] = ""; + BLI_strncpy(oldname, palcolor->info, sizeof(oldname)); /* copy the new name into the name slot */ BLI_strncpy_utf8(palcolor->info, value, sizeof(palcolor->info)); BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), sizeof(palcolor->info)); + + /* rename all strokes */ + BKE_gpencil_palettecolor_changename(gpd, oldname, palcolor->info); + + /* now fix animation paths */ + BKE_animdata_fix_paths_rename_all(&gpd->id, "colors", oldname, palcolor->info); } static void rna_GPencilStrokeColor_info_set(PointerRNA *ptr, const char *value) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 6947a4104c8..1166fb89a0a 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -407,9 +407,34 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { {0, NULL, 0, NULL, NULL} }; +EnumPropertyItem rna_enum_gpencil_interpolation_mode_items[] = { + /* interpolation */ + {0, "", 0, N_("Interpolation"), "Standard transitions between keyframes"}, + {GP_IPO_LINEAR, "LINEAR", ICON_IPO_LINEAR, "Linear", "Straight-line interpolation between A and B (i.e. no ease in/out)"}, + {GP_IPO_CURVEMAP, "CUSTOM", ICON_IPO_BEZIER, "Custom", "Custom interpolation defined using a curvemap"}, + + /* easing */ + {0, "", 0, N_("Easing (by strength)"), "Predefined inertial transitions, useful for motion graphics (from least to most ''dramatic'')"}, + {GP_IPO_SINE, "SINE", ICON_IPO_SINE, "Sinusoidal", "Sinusoidal easing (weakest, almost linear but with a slight curvature)"}, + {GP_IPO_QUAD, "QUAD", ICON_IPO_QUAD, "Quadratic", "Quadratic easing"}, + {GP_IPO_CUBIC, "CUBIC", ICON_IPO_CUBIC, "Cubic", "Cubic easing"}, + {GP_IPO_QUART, "QUART", ICON_IPO_QUART, "Quartic", "Quartic easing"}, + {GP_IPO_QUINT, "QUINT", ICON_IPO_QUINT, "Quintic", "Quintic easing"}, + {GP_IPO_EXPO, "EXPO", ICON_IPO_EXPO, "Exponential", "Exponential easing (dramatic)"}, + {GP_IPO_CIRC, "CIRC", ICON_IPO_CIRC, "Circular", "Circular easing (strongest and most dynamic)"}, + + {0, "", 0, N_("Dynamic Effects"), "Simple physics-inspired easing effects"}, + {GP_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"}, + {GP_IPO_BOUNCE, "BOUNCE", ICON_IPO_BOUNCE, "Bounce", "Exponentially decaying parabolic bounce, like when objects collide"}, + {GP_IPO_ELASTIC, "ELASTIC", ICON_IPO_ELASTIC, "Elastic", "Exponentially decaying sine wave, like an elastic band"}, + + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME #include "DNA_anim_types.h" +#include "DNA_color_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_mesh_types.h" @@ -420,6 +445,7 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { #include "MEM_guardedalloc.h" #include "BKE_brush.h" +#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" @@ -447,6 +473,29 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { #include "FRS_freestyle.h" #endif +/* Grease Pencil Interpolation settings */ +static char *rna_GPencilInterpolateSettings_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.gpencil_interpolate"); +} + +static void rna_GPencilInterpolateSettings_type_set(PointerRNA *ptr, int value) +{ + GP_Interpolate_Settings *settings = (GP_Interpolate_Settings *)ptr->data; + + /* NOTE: This cast should be fine, as we have a small + finite set of values (eGP_Interpolate_Type) + * that should fit well within a char + */ + settings->type = (char)value; + + /* init custom interpolation curve here now the first time it's used */ + if ((settings->type == GP_IPO_CURVEMAP) && + (settings->custom_ipo == NULL)) + { + settings->custom_ipo = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + } +} + /* Grease pencil Drawing Brushes */ static bGPDbrush *rna_GPencil_brush_new(ToolSettings *ts, const char *name, int setactive) { @@ -2138,6 +2187,73 @@ static int rna_gpu_is_hq_supported_get(PointerRNA *UNUSED(ptr)) #else +/* Grease Pencil Interpolation tool settings */ +static void rna_def_gpencil_interpolate(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilInterpolateSettings", NULL); + RNA_def_struct_sdna(srna, "GP_Interpolate_Settings"); + RNA_def_struct_path_func(srna, "rna_GPencilInterpolateSettings_path"); + RNA_def_struct_ui_text(srna, "Grease Pencil Interpolate Settings", + "Settings for Grease Pencil interpolation tools"); + + /* flags */ + prop = RNA_def_property(srna, "interpolate_all_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS); + RNA_def_property_ui_text(prop, "Interpolate All Layers", "Interpolate all layers, not only active"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "interpolate_selected_only", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED); + RNA_def_property_ui_text(prop, "Interpolate Selected Strokes", "Interpolate only selected strokes in the original frame"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* interpolation type */ + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_interpolation_mode_items); + RNA_def_property_enum_funcs(prop, NULL, "rna_GPencilInterpolateSettings_type_set", NULL); + RNA_def_property_ui_text(prop, "Type", + "Interpolation method to use the next time 'Interpolate Sequence' is run"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* easing */ + prop = RNA_def_property(srna, "easing", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "easing"); + RNA_def_property_enum_items(prop, rna_enum_beztriple_interpolation_easing_items); + RNA_def_property_ui_text(prop, "Easing", + "Which ends of the segment between the preceding and following grease pencil frames " + "easing interpolation is applied to"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* easing options */ + prop = RNA_def_property(srna, "back", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "back"); + RNA_def_property_ui_text(prop, "Back", "Amount of overshoot for 'back' easing"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "amplitude"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); /* only positive values... */ + RNA_def_property_ui_text(prop, "Amplitude", "Amount to boost elastic bounces for 'elastic' easing"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "period", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "period"); + RNA_def_property_ui_text(prop, "Period", "Time between bounces for elastic easing"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* custom curvemap */ + prop = RNA_def_property(srna, "interpolation_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "custom_ipo"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Interpolation Curve", + "Custom curve to control 'sequence' interpolation between Grease Pencil frames"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); +} + /* Grease Pencil Drawing Brushes */ static void rna_def_gpencil_brush(BlenderRNA *brna) { @@ -2673,7 +2789,14 @@ static void rna_def_tool_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "gpencil_sculpt", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gp_sculpt"); RNA_def_property_struct_type(prop, "GPencilSculptSettings"); - RNA_def_property_ui_text(prop, "Grease Pencil Sculpt", ""); + RNA_def_property_ui_text(prop, "Grease Pencil Sculpt", + "Settings for stroke sculpting tools and brushes"); + + prop = RNA_def_property(srna, "gpencil_interpolate", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "gp_interpolate"); + RNA_def_property_struct_type(prop, "GPencilInterpolateSettings"); + RNA_def_property_ui_text(prop, "Grease Pencil Interpolate", + "Settings for Grease Pencil Interpolation tools"); /* Grease Pencil - Drawing brushes */ prop = RNA_def_property(srna, "gpencil_brushes", PROP_COLLECTION, PROP_NONE); @@ -7267,6 +7390,7 @@ void RNA_def_scene(BlenderRNA *brna) RNA_define_animate_sdna(false); rna_def_tool_settings(brna); rna_def_gpencil_brush(brna); + rna_def_gpencil_interpolate(brna); rna_def_unified_paint_settings(brna); rna_def_curve_paint_settings(brna); rna_def_statvis(brna); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 7f405f0fb1f..40aea37d9d2 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1047,15 +1047,6 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Affect Thickness", "The brush affects the thickness of the point"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - prop = RNA_def_property(srna, "interpolate_all_layers", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS); - RNA_def_property_ui_text(prop, "Interpolate All Layers", "Interpolate all layers, not only active"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "interpolate_selected_only", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED); - RNA_def_property_ui_text(prop, "Interpolate Selected Strokes", "Interpolate only selected strokes in the original frame"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "selection_alpha", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "alpha"); diff --git a/source/blender/nodes/shader/nodes/node_shader_light_path.c b/source/blender/nodes/shader/nodes/node_shader_light_path.c index b1001cd3937..052f2a66ec8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_light_path.c +++ b/source/blender/nodes/shader/nodes/node_shader_light_path.c @@ -39,6 +39,8 @@ static bNodeSocketTemplate sh_node_light_path_out[] = { { SOCK_FLOAT, 0, N_("Is Transmission Ray"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 0, N_("Ray Length"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 0, N_("Ray Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 0, N_("Diffuse Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 0, N_("Glossy Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 0, N_("Transparent Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 0, N_("Transmission Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { -1, 0, "" } |