diff options
author | Campbell Barton <ideasman42@gmail.com> | 2017-11-02 07:44:33 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2017-11-02 07:45:19 +0300 |
commit | 4a85089abe6d5abb9f6bb0d8255eba33dc030fcb (patch) | |
tree | 25459e54af9cf6712ca349b924319939ba853909 /source/blender | |
parent | cf6e45b5224d16263d7c87411a2ff71ed928410f (diff) | |
parent | 765e28948e706d8fce97c23d4d050a607971488b (diff) |
Merge branch 'master' into blender2.8
Diffstat (limited to 'source/blender')
37 files changed, 1121 insertions, 148 deletions
diff --git a/source/blender/alembic/intern/abc_mball.cc b/source/blender/alembic/intern/abc_mball.cc index 31cc6d1fe3e..f1aa25c8f70 100644 --- a/source/blender/alembic/intern/abc_mball.cc +++ b/source/blender/alembic/intern/abc_mball.cc @@ -54,7 +54,7 @@ AbcMBallWriter::AbcMBallWriter( m_is_animated = isAnimated(); m_mesh_ob = BKE_object_copy(bmain, ob); - m_mesh_ob->curve_cache = (CurveCache*)MEM_callocN( + m_mesh_ob->curve_cache = (CurveCache *)MEM_callocN( sizeof(CurveCache), "CurveCache for AbcMBallWriter"); diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index fd5b20f3e38..fbd05552255 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -204,10 +204,12 @@ void BKE_nurb_bpoint_calc_normal(struct Nurb *nu, struct BPoint *bp, float r_nor void BKE_nurb_bpoint_calc_plane(struct Nurb *nu, struct BPoint *bp, float r_plane[3]); void BKE_nurb_handle_calc(struct BezTriple *bezt, struct BezTriple *prev, struct BezTriple *next, - const bool is_fcurve); + const bool is_fcurve, const char smoothing); void BKE_nurb_handle_calc_simple(struct Nurb *nu, struct BezTriple *bezt); void BKE_nurb_handle_calc_simple_auto(struct Nurb *nu, struct BezTriple *bezt); +void BKE_nurb_handle_smooth_fcurve(struct BezTriple *bezt, int total, bool cyclic); + void BKE_nurb_handles_calc(struct Nurb *nu); void BKE_nurb_handles_autocalc(struct Nurb *nu, int flag); void BKE_nurb_bezt_handle_test(struct BezTriple *bezt, const bool use_handle); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 6f7b3286e40..88c0aa6f35a 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -876,6 +876,8 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan pchan->curveInY = chan->curveInY; pchan->curveOutX = chan->curveOutX; pchan->curveOutY = chan->curveOutY; + pchan->ease1 = chan->ease1; + pchan->ease2 = chan->ease2; pchan->scaleIn = chan->scaleIn; pchan->scaleOut = chan->scaleOut; @@ -1367,6 +1369,7 @@ void BKE_pose_rest(bPose *pose) pchan->roll1 = pchan->roll2 = 0.0f; pchan->curveInX = pchan->curveInY = 0.0f; pchan->curveOutX = pchan->curveOutY = 0.0f; + pchan->ease1 = pchan->ease2 = 0.0f; pchan->scaleIn = pchan->scaleOut = 1.0f; pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); @@ -1410,6 +1413,8 @@ bool BKE_pose_copy_result(bPose *to, bPose *from) pchanto->curveInY = pchanfrom->curveInY; pchanto->curveOutX = pchanfrom->curveOutX; pchanto->curveOutY = pchanfrom->curveOutY; + pchanto->ease1 = pchanfrom->ease1; + pchanto->ease2 = pchanfrom->ease2; pchanto->scaleIn = pchanfrom->scaleIn; pchanto->scaleOut = pchanfrom->scaleOut; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 7dfc4df114c..e45e0596f3a 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -613,8 +613,10 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB { const float circle_factor = length * (cubic_tangent_factor_circle_v3(h1, h2) / 0.75f); - const float hlength1 = bone->ease1 * circle_factor; - const float hlength2 = bone->ease2 * circle_factor; + const float combined_ease1 = bone->ease1 + (!rest ? pchan->ease1 : 0.0f); + const float combined_ease2 = bone->ease2 + (!rest ? pchan->ease2 : 0.0f); + const float hlength1 = combined_ease1 * circle_factor; + const float hlength2 = combined_ease2 * circle_factor; /* and only now negate h2 */ mul_v3_fl(h1, hlength1); diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index a93bda215a0..88a15d1fe3b 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -41,6 +41,7 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" +#include "DNA_anim_types.h" #include "DNA_curve_types.h" #include "DNA_material_types.h" @@ -1241,8 +1242,9 @@ void BKE_nurb_makeFaces(Nurb *nu, float *coord_array, int rowstride, int resolu, basis += KNOTSV(nu); } u += ustep; - if (rowstride != 0) - in = (float *) (((unsigned char *) in) + (rowstride - 3 * totv * sizeof(*in))); + if (rowstride != 0) { + in = (float *)(((unsigned char *)in) + (rowstride - 3 * totv * sizeof(*in))); + } } /* free */ @@ -1685,7 +1687,7 @@ float *BKE_curve_make_orco(const EvaluationContext *eval_ctx, Scene *scene, Obje if (dl->flag & DL_CYCL_V) sizev++; } - else if (dl->flag & DL_CYCL_V) { + else if (dl->flag & DL_CYCL_V) { sizev++; } @@ -1802,7 +1804,7 @@ void BKE_curve_bevel_make( fp[3] = fp[4] = 0.0; fp[5] = cu->ext1; } - else if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0 && cu->ext1 == 0.0f) { // we make a full round bevel in that case + else if ((cu->flag & (CU_FRONT | CU_BACK)) == 0 && cu->ext1 == 0.0f) { /* we make a full round bevel in that case */ nr = 4 + 2 * cu->bevresol; dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); @@ -1834,9 +1836,9 @@ void BKE_curve_bevel_make( if ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT)) { dnr = nr = 2 + cu->bevresol; - if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0) + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { nr = 3 + 2 * cu->bevresol; - + } dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p1"); BLI_addtail(disp, dl); @@ -1876,7 +1878,7 @@ void BKE_curve_bevel_make( fp[4] = cu->ext2; fp[5] = cu->ext1; - if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0) { + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { dl = MEM_dupallocN(dl); dl->verts = MEM_dupallocN(dl->verts); BLI_addtail(disp, dl); @@ -1892,9 +1894,9 @@ void BKE_curve_bevel_make( /* part 3, front */ if ((cu->flag & CU_FRONT) || !(cu->flag & CU_BACK)) { dnr = nr = 2 + cu->bevresol; - if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0) + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { nr = 3 + 2 * cu->bevresol; - + } dl = MEM_callocN(sizeof(DispList), "makebevelcurve p3"); dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p3"); BLI_addtail(disp, dl); @@ -2112,7 +2114,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * if (tilt_array) { if (nu->tilt_interp == KEY_CU_EASE) { /* May as well support for tilt also 2.47 ease interp */ *tilt_array = prevbezt->alfa + - (bezt->alfa - prevbezt->alfa) * (3.0f * fac * fac - 2.0f * fac * fac * fac); + (bezt->alfa - prevbezt->alfa) * (3.0f * fac * fac - 2.0f * fac * fac * fac); } else { key_curve_position_weights(fac, t, nu->tilt_interp); @@ -2128,7 +2130,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * * Note! - this only takes the 2 points into account, * giving much more localized results to changes in radius, sometimes you want that */ *radius_array = prevbezt->radius + - (bezt->radius - prevbezt->radius) * (3.0f * fac * fac - 2.0f * fac * fac * fac); + (bezt->radius - prevbezt->radius) * (3.0f * fac * fac - 2.0f * fac * fac * fac); } else { @@ -2136,8 +2138,9 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * if (tilt_array == NULL || nu->tilt_interp != nu->radius_interp) { key_curve_position_weights(fac, t, nu->radius_interp); } - *radius_array = t[0] * pprev->radius + t[1] * prevbezt->radius + - t[2] * bezt->radius + t[3] * next->radius; + *radius_array = + t[0] * pprev->radius + t[1] * prevbezt->radius + + t[2] * bezt->radius + t[3] * next->radius; } radius_array = POINTER_OFFSET(radius_array, stride); @@ -2145,8 +2148,9 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * if (weight_array) { /* basic interpolation for now, could copy tilt interp too */ - *weight_array = prevbezt->weight + - (bezt->weight - prevbezt->weight) * (3.0f * fac * fac - 2.0f * fac * fac * fac); + *weight_array = + prevbezt->weight + + (bezt->weight - prevbezt->weight) * (3.0f * fac * fac - 2.0f * fac * fac * fac); weight_array = POINTER_OFFSET(weight_array, stride); } @@ -3139,7 +3143,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render) static void calchandleNurb_intern( BezTriple *bezt, const BezTriple *prev, const BezTriple *next, - bool is_fcurve, bool skip_align) + bool is_fcurve, bool skip_align, char fcurve_smoothing) { /* defines to avoid confusion */ #define p2_h1 ((p2) - 3) @@ -3153,6 +3157,9 @@ static void calchandleNurb_intern( float len_ratio; const float eps = 1e-5; + /* assume normal handle until we check */ + bezt->f5 = HD_AUTOTYPE_NORMAL; + if (bezt->h1 == 0 && bezt->h2 == 0) { return; } @@ -3204,7 +3211,14 @@ static void calchandleNurb_intern( tvec[2] = dvec_b[2] / len_b + dvec_a[2] / len_a; if (is_fcurve) { - len = tvec[0]; + if (fcurve_smoothing != FCURVE_SMOOTH_NONE) { + /* force the horizontal handle size to be 1/3 of the key interval so that + * the X component of the parametric bezier curve is a linear spline */ + len = 6.0f / 2.5614f; + } + else { + len = tvec[0]; + } } else { len = len_v3(tvec); @@ -3215,10 +3229,12 @@ static void calchandleNurb_intern( /* only for fcurves */ bool leftviolate = false, rightviolate = false; - if (len_a > 5.0f * len_b) - len_a = 5.0f * len_b; - if (len_b > 5.0f * len_a) - len_b = 5.0f * len_a; + if (!is_fcurve || fcurve_smoothing == FCURVE_SMOOTH_NONE) { + if (len_a > 5.0f * len_b) + len_a = 5.0f * len_b; + if (len_b > 5.0f * len_a) + len_b = 5.0f * len_a; + } if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) { len_a /= len; @@ -3229,6 +3245,7 @@ static void calchandleNurb_intern( float ydiff2 = next->vec[1][1] - bezt->vec[1][1]; if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) { bezt->vec[0][1] = bezt->vec[1][1]; + bezt->f5 = HD_AUTOTYPE_SPECIAL; } else { /* handles should not be beyond y coord of two others */ if (ydiff1 <= 0.0f) { @@ -3253,8 +3270,9 @@ static void calchandleNurb_intern( if ((bezt->h2 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */ float ydiff1 = prev->vec[1][1] - bezt->vec[1][1]; float ydiff2 = next->vec[1][1] - bezt->vec[1][1]; - if ( (ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f) ) { + if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) { bezt->vec[2][1] = bezt->vec[1][1]; + bezt->f5 = HD_AUTOTYPE_SPECIAL; } else { /* handles should not be beyond y coord of two others */ if (ydiff1 <= 0.0f) { @@ -3402,7 +3420,7 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align) next = bezt + 1; while (a--) { - calchandleNurb_intern(bezt, prev, next, 0, skip_align); + calchandleNurb_intern(bezt, prev, next, 0, skip_align, 0); prev = bezt; if (a == 1) { if (nu->flagu & CU_NURB_CYCLIC) @@ -3417,9 +3435,540 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align) } } -void BKE_nurb_handle_calc(BezTriple *bezt, BezTriple *prev, BezTriple *next, const bool is_fcurve) +/* A utility function for allocating a number of arrays of the same length + * with easy error checking and deallocation, and an easy way to add or remove + * arrays that are processed in this way when changing code. + * + * floats, chars: NULL-terminated arrays of pointers to array pointers that need to be allocated. + * + * Returns: pointer to the buffer that contains all of the arrays. + */ +static void *allocate_arrays(int count, float ***floats, char ***chars, const char *name) +{ + int num_floats = 0, num_chars = 0; + + while (floats && floats[num_floats]) { + num_floats++; + } + + while (chars && chars[num_chars]) { + num_chars++; + } + + void *buffer = (float *)MEM_mallocN(count * (sizeof(float) * num_floats + num_chars), name); + + if (!buffer) + return NULL; + + float *fptr = buffer; + + for (int i = 0; i < num_floats; i++, fptr += count) { + *floats[i] = fptr; + } + + char *cptr = (char *)fptr; + + for (int i = 0; i < num_chars; i++, cptr += count) { + *chars[i] = cptr; + } + + return buffer; +} + +static void free_arrays(void *buffer) +{ + MEM_freeN(buffer); +} + +/* computes in which direction to change h[i] to satisfy conditions better */ +static float bezier_relax_direction(float *a, float *b, float *c, float *d, float *h, int i, int count) +{ + /* current deviation between sides of the equation */ + float state = a[i] * h[(i + count - 1) % count] + + b[i] * h[i] + + c[i] * h[(i + 1) % count] - + d[i]; + + /* only the sign is meaningful */ + return -state * b[i]; +} + +static void bezier_lock_unknown(float *a, float *b, float *c, float *d, int i, float value) +{ + a[i] = c[i] = 0.0f; + b[i] = 1.0f; + d[i] = value; +} + +static void bezier_restore_equation(float *a, float *b, float *c, float *d, float *a0, float *b0, float *c0, float *d0, int i) +{ + a[i] = a0[i]; + b[i] = b0[i]; + c[i] = c0[i]; + d[i] = d0[i]; +} + +static bool tridiagonal_solve_with_limits(float *a, float *b, float *c, float *d, float *h, float *hmin, float *hmax, int solve_count) +{ + float *a0, *b0, *c0, *d0; + float **arrays[] = { &a0, &b0, &c0, &d0, NULL }; + char *is_locked, *num_unlocks; + char **flagarrays[] = { &is_locked, &num_unlocks, NULL }; + + void *tmps = allocate_arrays(solve_count, arrays, flagarrays, "tridiagonal_solve_with_limits"); + if (!tmps) + return false; + + memcpy(a0, a, sizeof(float) * solve_count); + memcpy(b0, b, sizeof(float) * solve_count); + memcpy(c0, c, sizeof(float) * solve_count); + memcpy(d0, d, sizeof(float) * solve_count); + + memset(is_locked, 0, solve_count); + memset(num_unlocks, 0, solve_count); + + bool overshoot, unlocked; + + do { + if (!BLI_tridiagonal_solve_cyclic(a, b, c, d, h, solve_count)) { + free_arrays(tmps); + return false; + } + + /* first check if any handles overshoot the limits, and lock them */ + bool all = false, locked = false; + + overshoot = unlocked = false; + + do { + for (int i = 0; i < solve_count; i++) { + if (h[i] >= hmin[i] && h[i] <= hmax[i]) + continue; + + overshoot = true; + + float target = h[i] > hmax[i] ? hmax[i] : hmin[i]; + + /* heuristically only lock handles that go in the right direction if there are such ones */ + if (target != 0.0f || all) { + /* mark item locked */ + is_locked[i] = 1; + + bezier_lock_unknown(a, b, c, d, i, target); + locked = true; + } + } + + all = true; + } while (overshoot && !locked); + + /* if no handles overshot and were locked, see if it may be a good idea to unlock some handles */ + if (!locked) { + for (int i = 0; i < solve_count; i++) { + // to definitely avoid infinite loops limit this to 2 times + if (!is_locked[i] || num_unlocks[i] >= 2) + continue; + + /* if the handle wants to move in allowable direction, release it */ + float relax = bezier_relax_direction(a0, b0, c0, d0, h, i, solve_count); + + if ((relax > 0 && h[i] < hmax[i]) || (relax < 0 && h[i] > hmin[i])) { + bezier_restore_equation(a, b, c, d, a0, b0, c0, d0, i); + + is_locked[i] = 0; + num_unlocks[i]++; + unlocked = true; + } + } + } + } while (overshoot || unlocked); + + free_arrays(tmps); + return true; +} + +/* + * This function computes the handles of a series of auto bezier points + * on the basis of 'no acceleration discontinuities' at the points. + * The first and last bezier points are considered 'fixed' (their handles are not touched) + * The result is the smoothest possible trajectory going through intemediate points. + * The difficulty is that the handles depends on their neighbours. + * + * The exact solution is found by solving a tridiagonal matrix equation formed + * by the continuity and boundary conditions. Although theoretically handle position + * is affected by all other points of the curve segment, in practice the influence + * decreases exponentially with distance. + * + * Note: this algorithm assumes that the handle horizontal size if always 1/3 of the + * of the interval to the next point. This rule ensures linear interpolation of time. + * + * ^ height (co 1) + * | yN + * | yN-1 | + * | y2 | | + * | y1 | | | + * | y0 | | | | + * | | | | | | + * | | | | | | + * | | | | | | + * |-------t1---------t2--------- ~ --------tN-------------------> time (co 0) + * + * + * Mathematical basis: + * + * 1. Handle lengths on either side of each point are connected by a factor + * ensuring continuity of the first derivative: + * + * l[i] = t[i+1]/t[i] + * + * 2. The tridiagonal system is formed by the following equation, which is derived + * by differentiating the bezier curve and specifies second derivative continuity + * at every point: + * + * l[i]^2 * h[i-1] + (2*l[i]+2) * h[i] + 1/l[i+1] * h[i+1] = (y[i]-y[i-1])*l[i]^2 + y[i+1]-y[i] + * + * 3. If this point is adjacent to a manually set handle with X size not equal to 1/3 + * of the horizontal interval, this equation becomes slightly more complex: + * + * l[i]^2 * h[i-1] + (3*(1-R[i-1])*l[i] + 3*(1-L[i+1])) * h[i] + 1/l[i+1] * h[i+1] = (y[i]-y[i-1])*l[i]^2 + y[i+1]-y[i] + * + * The difference between equations amounts to this, and it's obvious that when R[i-1] + * and L[i+1] are both 1/3, it becomes zero: + * + * ( (1-3*R[i-1])*l[i] + (1-3*L[i+1]) ) * h[i] + * + * 4. The equations for zero acceleration border conditions are basically the above + * equation with parts omitted, so the handle size correction also applies. + */ + + +static void bezier_eq_continuous(float *a, float *b, float *c, float *d, float *dy, float *l, int i) +{ + a[i] = l[i] * l[i]; + b[i] = 2.0f * (l[i] + 1); + c[i] = 1.0f / l[i + 1]; + d[i] = dy[i] * l[i] * l[i] + dy[i + 1]; +} + +static void bezier_eq_noaccel_right(float *a, float *b, float *c, float *d, float *dy, float *l, int i) +{ + a[i] = 0.0f; + b[i] = 2.0f; + c[i] = 1.0f / l[i + 1]; + d[i] = dy[i + 1]; +} + +static void bezier_eq_noaccel_left(float *a, float *b, float *c, float *d, float *dy, float *l, int i) +{ + a[i] = l[i] * l[i]; + b[i] = 2.0f * l[i]; + c[i] = 0.0f; + d[i] = dy[i] * l[i] * l[i]; +} + +/* auto clamp prevents its own point going the wrong way, and adjacent handles overshooting */ +static void bezier_clamp(float *hmax, float *hmin, int i, float dy, bool no_reverse, bool no_overshoot) +{ + if (dy > 0) { + if (no_overshoot) + hmax[i] = min_ff(hmax[i], dy); + if (no_reverse) + hmin[i] = 0.0f; + } + else if (dy < 0) { + if (no_reverse) + hmax[i] = 0.0f; + if (no_overshoot) + hmin[i] = max_ff(hmin[i], dy); + } + else if (no_reverse || no_overshoot) { + hmax[i] = hmin[i] = 0.0f; + } +} + +/* write changes to a bezier handle */ +static void bezier_output_handle_inner(BezTriple *bezt, bool right, float newval[3], bool endpoint) +{ + float tmp[3]; + + int idx = right ? 2 : 0; + char hr = right ? bezt->h2 : bezt->h1; + char hm = right ? bezt->h1 : bezt->h2; + + /* only assign Auto/Vector handles */ + if (!ELEM(hr, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) + return; + + copy_v3_v3(bezt->vec[idx], newval); + + /* fix up the Align handle if any */ + if (ELEM(hm, HD_ALIGN, HD_ALIGN_DOUBLESIDE)) { + float hlen = len_v3v3(bezt->vec[1], bezt->vec[2 - idx]); + float h2len = len_v3v3(bezt->vec[1], bezt->vec[idx]); + + sub_v3_v3v3(tmp, bezt->vec[1], bezt->vec[idx]); + madd_v3_v3v3fl(bezt->vec[2 - idx], bezt->vec[1], tmp, hlen / h2len); + } + /* at end points of the curve, mirror handle to the other side */ + else if (endpoint && ELEM(hm, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) { + sub_v3_v3v3(tmp, bezt->vec[1], bezt->vec[idx]); + add_v3_v3v3(bezt->vec[2 - idx], bezt->vec[1], tmp); + } +} + +static void bezier_output_handle(BezTriple *bezt, bool right, float dy, bool endpoint) +{ + float tmp[3]; + + copy_v3_v3(tmp, bezt->vec[right ? 2 : 0]); + + tmp[1] = bezt->vec[1][1] + dy; + + bezier_output_handle_inner(bezt, right, tmp, endpoint); +} + +static bool bezier_check_solve_end_handle(BezTriple *bezt, char htype, bool end) +{ + return (htype == HD_VECT) || (end && ELEM(htype, HD_AUTO, HD_AUTO_ANIM) && bezt->f5 == HD_AUTOTYPE_NORMAL); +} + +static float bezier_calc_handle_adj(float hsize[2], float dx) +{ + /* if handles intersect in x direction, they are scaled to fit */ + float fac = dx / (hsize[0] + dx / 3.0f); + if (fac < 1.0f) { + mul_v2_fl(hsize, fac); + } + return 1.0f - 3.0f * hsize[0] / dx; +} + +static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int start, int count, bool cycle) +{ + float *dx, *dy, *l, *a, *b, *c, *d, *h, *hmax, *hmin; + float **arrays[] = { &dx, &dy, &l, &a, &b, &c, &d, &h, &hmax, &hmin, NULL }; + + int solve_count = count; + + /* verify index ranges */ + + if (count < 2) + return; + + BLI_assert(start < total - 1 && count <= total); + BLI_assert(start + count <= total || cycle); + + bool full_cycle = (start == 0 && count == total && cycle); + + BezTriple *bezt_first = &bezt[start]; + BezTriple *bezt_last = &bezt[(start + count > total) ? start + count - total : start + count - 1]; + + bool solve_first = bezier_check_solve_end_handle(bezt_first, bezt_first->h2, start == 0); + bool solve_last = bezier_check_solve_end_handle(bezt_last, bezt_last->h1, start + count == total); + + if (count == 2 && !full_cycle && solve_first == solve_last) { + return; + } + + /* allocate all */ + + void *tmp_buffer = allocate_arrays(count, arrays, NULL, "bezier_calc_smooth_tmp"); + if (!tmp_buffer) + return; + + /* point locations */ + + dx[0] = dy[0] = NAN_FLT; + + for (int i = 1, j = start + 1; i < count; i++, j++) { + dx[i] = bezt[j].vec[1][0] - bezt[j - 1].vec[1][0]; + dy[i] = bezt[j].vec[1][1] - bezt[j - 1].vec[1][1]; + + /* when cyclic, jump from last point to first */ + if (cycle && j == total - 1) + j = 0; + } + + /* ratio of x intervals */ + + l[0] = l[count - 1] = 1.0f; + + for (int i = 1; i < count - 1; i++) { + l[i] = dx[i + 1] / dx[i]; + } + + /* compute handle clamp ranges */ + + bool clamped_prev = false, clamped_cur = ELEM(HD_AUTO_ANIM, bezt_first->h1, bezt_first->h2); + + for (int i = 0; i < count; i++) { + hmax[i] = FLT_MAX; + hmin[i] = -FLT_MAX; + } + + for (int i = 1, j = start + 1; i < count; i++, j++) { + clamped_prev = clamped_cur; + clamped_cur = ELEM(HD_AUTO_ANIM, bezt[j].h1, bezt[j].h2); + + if (cycle && j == total - 1) { + j = 0; + clamped_cur = clamped_cur || ELEM(HD_AUTO_ANIM, bezt[j].h1, bezt[j].h2); + } + + bezier_clamp(hmax, hmin, i - 1, dy[i], clamped_prev, clamped_prev); + bezier_clamp(hmax, hmin, i, dy[i] * l[i], clamped_cur, clamped_cur); + } + + /* full cycle merges first and last points into continuous loop */ + + float first_handle_adj = 0.0f, last_handle_adj = 0.0f; + + if (full_cycle) { + /* reduce the number of uknowns by one */ + int i = solve_count = count - 1; + + dx[0] = dx[i]; + dy[0] = dy[i]; + + l[0] = l[i] = dx[1] / dx[0]; + + hmin[0] = max_ff(hmin[0], hmin[i]); + hmax[0] = min_ff(hmax[0], hmax[i]); + + solve_first = solve_last = true; + + bezier_eq_continuous(a, b, c, d, dy, l, 0); + } + else { + float tmp[2]; + + /* boundary condition: fixed handles or zero curvature */ + if (!solve_first) { + sub_v2_v2v2(tmp, bezt_first->vec[2], bezt_first->vec[1]); + first_handle_adj = bezier_calc_handle_adj(tmp, dx[1]); + + bezier_lock_unknown(a, b, c, d, 0, tmp[1]); + } + else { + bezier_eq_noaccel_right(a, b, c, d, dy, l, 0); + } + + if (!solve_last) { + sub_v2_v2v2(tmp, bezt_last->vec[1], bezt_last->vec[0]); + last_handle_adj = bezier_calc_handle_adj(tmp, dx[count - 1]); + + bezier_lock_unknown(a, b, c, d, count - 1, tmp[1]); + } + else { + bezier_eq_noaccel_left(a, b, c, d, dy, l, count - 1); + } + } + + /* main tridiagonal system of equations */ + + for (int i = 1; i < count - 1; i++) { + bezier_eq_continuous(a, b, c, d, dy, l, i); + } + + /* apply correction for user-defined handles with nonstandard x positions */ + + if (!full_cycle) { + if (count > 2 || solve_last) { + b[1] += l[1] * first_handle_adj; + } + + if (count > 2 || solve_first) { + b[count - 2] += last_handle_adj; + } + } + + /* solve and output results */ + + if (tridiagonal_solve_with_limits(a, b, c, d, h, hmin, hmax, solve_count)) { + if (full_cycle) { + h[count - 1] = h[0]; + } + + for (int i = 1, j = start + 1; i < count - 1; i++, j++) { + bool end = (j == total - 1); + + bezier_output_handle(&bezt[j], false, -h[i] / l[i], end); + + if (end) + j = 0; + + bezier_output_handle(&bezt[j], true, h[i], end); + } + + if (solve_first) { + bezier_output_handle(bezt_first, true, h[0], start == 0); + } + + if (solve_last) { + bezier_output_handle(bezt_last, false, -h[count - 1] / l[count - 1], start + count == total); + } + } + + /* free all */ + + free_arrays(tmp_buffer); +} + +static bool is_free_auto_point(BezTriple *bezt) +{ + return BEZT_IS_AUTOH(bezt) && bezt->f5 == HD_AUTOTYPE_NORMAL; +} + +void BKE_nurb_handle_smooth_fcurve(BezTriple *bezt, int total, bool cycle) +{ + /* ignore cyclic extrapolation if end points are locked */ + cycle = cycle && is_free_auto_point(&bezt[0]) && is_free_auto_point(&bezt[total - 1]); + + /* if cyclic, try to find a sequence break point */ + int search_base = 0; + + if (cycle) { + for (int i = 1; i < total - 1; i++) { + if (!is_free_auto_point(&bezt[i])) { + search_base = i; + break; + } + } + + /* all points of the curve are freely changeable auto handles - solve as full cycle */ + if (search_base == 0) { + bezier_handle_calc_smooth_fcurve(bezt, total, 0, total, cycle); + return; + } + } + + /* Find continuous subsequences of free auto handles and smooth them, starting at + * search_base. In cyclic mode these subsequences can span the cycle boundary. */ + int start = search_base, count = 1; + + for (int i = 1, j = start + 1; i < total; i++, j++) { + /* in cyclic mode: jump from last to first point when necessary */ + if (j == total - 1 && cycle) + j = 0; + + /* non auto handle closes the list (we come here at least for the last handle, see above) */ + if (!is_free_auto_point(&bezt[j])) { + bezier_handle_calc_smooth_fcurve(bezt, total, start, count + 1, cycle); + start = j; + count = 1; + } + else { + count++; + } + } + + if (count > 1) { + bezier_handle_calc_smooth_fcurve(bezt, total, start, count, cycle); + } +} + +void BKE_nurb_handle_calc(BezTriple *bezt, BezTriple *prev, BezTriple *next, const bool is_fcurve, const char smoothing) { - calchandleNurb_intern(bezt, prev, next, is_fcurve, false); + calchandleNurb_intern(bezt, prev, next, is_fcurve, false, smoothing); } void BKE_nurb_handles_calc(Nurb *nu) /* first, if needed, set handle flags */ @@ -3459,7 +4008,7 @@ void BKE_nurb_handle_calc_simple(Nurb *nu, BezTriple *bezt) if (nu->pntsu > 1) { BezTriple *prev = BKE_nurb_bezt_get_prev(nu, bezt); BezTriple *next = BKE_nurb_bezt_get_next(nu, bezt); - BKE_nurb_handle_calc(bezt, prev, next, 0); + BKE_nurb_handle_calc(bezt, prev, next, 0, 0); } } @@ -3566,7 +4115,7 @@ void BKE_nurb_handles_autocalc(Nurb *nu, int flag) bool align = false, leftsmall = false, rightsmall = false; /* left handle: */ - if (flag == 0 || (bezt1->f1 & flag) ) { + if (flag == 0 || (bezt1->f1 & flag)) { bezt1->h1 = HD_FREE; /* distance too short: vectorhandle */ if (len_squared_v3v3(bezt1->vec[1], bezt0->vec[1]) < eps_sq) { @@ -3585,7 +4134,7 @@ void BKE_nurb_handles_autocalc(Nurb *nu, int flag) } } /* right handle: */ - if (flag == 0 || (bezt1->f3 & flag) ) { + if (flag == 0 || (bezt1->f3 & flag)) { bezt1->h2 = HD_FREE; /* distance too short: vectorhandle */ if (len_squared_v3v3(bezt1->vec[1], bezt2->vec[1]) < eps_sq) { diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 1a93031034b..382b26abbc6 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -896,7 +896,7 @@ bool BKE_fcurve_is_cyclic(FCurve *fcu) if (fcm->flag & (FMODIFIER_FLAG_RANGERESTRICT | FMODIFIER_FLAG_USEINFLUENCE)) return false; - FMod_Cycles *data = (FMod_Cycles*)fcm->data; + FMod_Cycles *data = (FMod_Cycles *)fcm->data; return data && data->after_cycles == 0 && data->before_cycles == 0 && ELEM(data->before_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET) && @@ -939,14 +939,14 @@ void calchandles_fcurve(FCurve *fcu) return; /* if the first modifier is Cycles, smooth the curve through the cycle */ - BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert-1]; + BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert - 1]; BezTriple tmp; bool cycle = BKE_fcurve_is_cyclic(fcu) && BEZT_IS_AUTOH(first) && BEZT_IS_AUTOH(last); /* get initial pointers */ bezt = fcu->bezt; - prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert-2], last, first); + prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert - 2], last, first); next = (bezt + 1); /* loop over all beztriples, adjusting handles */ @@ -956,7 +956,7 @@ void calchandles_fcurve(FCurve *fcu) if (bezt->vec[2][0] < bezt->vec[1][0]) bezt->vec[2][0] = bezt->vec[1][0]; /* calculate auto-handles */ - BKE_nurb_handle_calc(bezt, prev, next, true); + BKE_nurb_handle_calc(bezt, prev, next, true, fcu->auto_smoothing); /* for automatic ease in and out */ if (BEZT_IS_AUTOH(bezt) && !cycle) { @@ -965,9 +965,16 @@ void calchandles_fcurve(FCurve *fcu) /* set both handles to have same horizontal value as keyframe */ if (fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) { bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1]; + /* remember that these keyframes are special, they don't need to be adjusted */ + bezt->f5 = HD_AUTOTYPE_SPECIAL; } } } + + /* avoid total smoothing failure on duplicate keyframes (can happen during grab) */ + if (prev && prev->vec[1][0] >= bezt->vec[1][0]) { + prev->f5 = bezt->f5 = HD_AUTOTYPE_SPECIAL; + } /* advance pointers for next iteration */ prev = bezt; @@ -981,6 +988,18 @@ void calchandles_fcurve(FCurve *fcu) bezt++; } + + /* if cyclic extrapolation and Auto Clamp has triggered, ensure it is symmetric */ + if (cycle && (first->f5 != HD_AUTOTYPE_NORMAL || last->f5 != HD_AUTOTYPE_NORMAL)) { + first->vec[0][1] = first->vec[2][1] = first->vec[1][1]; + last->vec[0][1] = last->vec[2][1] = last->vec[1][1]; + first->f5 = last->f5 = HD_AUTOTYPE_SPECIAL; + } + + /* do a second pass for auto handle: compute the handle to have 0 accelaration step */ + if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { + BKE_nurb_handle_smooth_fcurve(fcu->bezt, fcu->totvert, cycle); + } } void testhandles_fcurve(FCurve *fcu, const bool use_handle) diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 1e9447c4f09..eaa2f89ab82 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -1197,14 +1197,14 @@ static void mask_calc_point_handle(MaskSplinePoint *point, MaskSplinePoint *poin #if 1 if (bezt_prev || bezt_next) { - BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0); + BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0); } #else if (handle_type == HD_VECT) { - BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0); + BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0); } else if (handle_type == HD_AUTO) { - BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0); + BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0); } else if (handle_type == HD_ALIGN || handle_type == HD_ALIGN_DOUBLESIDE) { float v1[3], v2[3]; diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index d4943b1b566..cbb7a766dfe 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -1387,6 +1387,7 @@ void BKE_nlastrip_validate_fcurves(NlaStrip *strip) /* set default flags */ fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); + fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; /* store path - make copy, and store that */ fcu->rna_path = BLI_strdupn("influence", 9); @@ -1408,6 +1409,7 @@ void BKE_nlastrip_validate_fcurves(NlaStrip *strip) /* set default flags */ fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); + fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; /* store path - make copy, and store that */ fcu->rna_path = BLI_strdupn("strip_time", 10); diff --git a/source/blender/blenlib/BLI_math_solvers.h b/source/blender/blenlib/BLI_math_solvers.h index 810c84cc830..b0193022837 100644 --- a/source/blender/blenlib/BLI_math_solvers.h +++ b/source/blender/blenlib/BLI_math_solvers.h @@ -48,6 +48,11 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3], float r_eigen_values[3 void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]); +/***************************** Simple Solvers ************************************/ + +bool BLI_tridiagonal_solve(const float *a, const float *b, const float *c, const float *d, float *r_x, const int count); +bool BLI_tridiagonal_solve_cyclic(const float *a, const float *b, const float *c, const float *d, float *r_x, const int count); + /**************************** Inline Definitions ******************************/ #if 0 /* None so far. */ # if BLI_MATH_DO_INLINE diff --git a/source/blender/blenlib/intern/math_solvers.c b/source/blender/blenlib/intern/math_solvers.c index 641e50c9bde..b8a22900ba1 100644 --- a/source/blender/blenlib/intern/math_solvers.c +++ b/source/blender/blenlib/intern/math_solvers.c @@ -72,3 +72,111 @@ void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3 { EIG_svd_square_matrix(3, (const float *)m3, (float *)r_U, (float *)r_S, (float *)r_V); } + +/***************************** Simple Solvers ************************************/ + +/** + * \brief Solve a tridiagonal system of equations: + * + * a[i] * r_x[i-1] + b[i] * r_x[i] + c[i] * r_x[i+1] = d[i] + * + * Ignores a[0] and c[count-1]. Uses the Thomas algorithm, e.g. see wiki. + * + * \param r_x output vector, may be shared with any of the input ones + * \return true if success + */ +bool BLI_tridiagonal_solve(const float *a, const float *b, const float *c, const float *d, float *r_x, const int count) +{ + if (count < 1) + return false; + + size_t bytes = sizeof(double) * (unsigned)count; + double *c1 = (double *)MEM_mallocN(bytes * 2, "tridiagonal_c1d1"); + double *d1 = c1 + count; + + if (!c1) + return false; + + int i; + double c_prev, d_prev, x_prev; + + /* forward pass */ + + c1[0] = c_prev = ((double)c[0]) / b[0]; + d1[0] = d_prev = ((double)d[0]) / b[0]; + + for (i = 1; i < count; i++) { + double denum = b[i] - a[i] * c_prev; + + c1[i] = c_prev = c[i] / denum; + d1[i] = d_prev = (d[i] - a[i] * d_prev) / denum; + } + + /* back pass */ + + x_prev = d_prev; + r_x[--i] = ((float)x_prev); + + while (--i >= 0) { + x_prev = d1[i] - c1[i] * x_prev; + r_x[i] = ((float)x_prev); + } + + MEM_freeN(c1); + + return isfinite(x_prev); +} + +/** + * \brief Solve a possibly cyclic tridiagonal system using the Sherman-Morrison formula. + * + * \param r_x output vector, may be shared with any of the input ones + * \return true if success + */ +bool BLI_tridiagonal_solve_cyclic(const float *a, const float *b, const float *c, const float *d, float *r_x, const int count) +{ + if (count < 1) + return false; + + float a0 = a[0], cN = c[count - 1]; + + /* if not really cyclic, fall back to the simple solver */ + if (a0 == 0.0f && cN == 0.0f) { + return BLI_tridiagonal_solve(a, b, c, d, r_x, count); + } + + size_t bytes = sizeof(float) * (unsigned)count; + float *tmp = (float *)MEM_mallocN(bytes * 2, "tridiagonal_ex"); + float *b2 = tmp + count; + + if (!tmp) + return false; + + /* prepare the noncyclic system; relies on tridiagonal_solve ignoring values */ + memcpy(b2, b, bytes); + b2[0] -= a0; + b2[count - 1] -= cN; + + memset(tmp, 0, bytes); + tmp[0] = a0; + tmp[count - 1] = cN; + + /* solve for partial solution and adjustment vector */ + bool success = + BLI_tridiagonal_solve(a, b2, c, tmp, tmp, count) && + BLI_tridiagonal_solve(a, b2, c, d, r_x, count); + + /* apply adjustment */ + if (success) { + float coeff = (r_x[0] + r_x[count - 1]) / (1.0f + tmp[0] + tmp[count - 1]); + + for (int i = 0; i < count; i++) { + r_x[i] -= coeff * tmp[i]; + } + } + + MEM_freeN(tmp); + + return success; +} + diff --git a/source/blender/bmesh/intern/bmesh_edgeloop.c b/source/blender/bmesh/intern/bmesh_edgeloop.c index 9d51b59825d..54fe2801d0c 100644 --- a/source/blender/bmesh/intern/bmesh_edgeloop.c +++ b/source/blender/bmesh/intern/bmesh_edgeloop.c @@ -168,8 +168,8 @@ int BM_mesh_edgeloops_find( /* add both directions */ if (bm_loop_build(el_store, e->v1, e->v2, 1) && - bm_loop_build(el_store, e->v2, e->v1, -1) && - el_store->len > 1) + bm_loop_build(el_store, e->v2, e->v1, -1) && + el_store->len > 1) { BLI_addtail(r_eloops, el_store); count++; diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index dde531a6193..2d04f73c4ac 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -104,6 +104,7 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde fcu = MEM_callocN(sizeof(FCurve), "FCurve"); fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); + fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; /* store path - make copy, and store that */ fcu->rna_path = BLI_strdup(rna_path); diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 93ce45e1e8f..afbd8e5bd41 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -188,6 +188,7 @@ FCurve *verify_fcurve(bAction *act, const char group[], PointerRNA *ptr, fcu = MEM_callocN(sizeof(FCurve), "FCurve"); fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); + fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; if (BLI_listbase_is_empty(&act->curves)) fcu->flag |= FCURVE_ACTIVE; /* first one added active */ diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index cff161d9d93..e419100bd04 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -77,19 +77,20 @@ EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name) bone->dist = 0.25f; bone->xwidth = 0.1f; bone->zwidth = 0.1f; - bone->ease1 = 1.0f; - bone->ease2 = 1.0f; bone->rad_head = 0.10f; bone->rad_tail = 0.05f; bone->segments = 1; bone->layer = arm->layer; + /* Bendy-Bone parameters */ bone->roll1 = 0.0f; bone->roll2 = 0.0f; bone->curveInX = 0.0f; bone->curveInY = 0.0f; bone->curveOutX = 0.0f; bone->curveOutY = 0.0f; + bone->ease1 = 1.0f; + bone->ease2 = 1.0f; bone->scaleIn = 1.0f; bone->scaleOut = 1.0f; @@ -899,19 +900,20 @@ static int armature_extrude_exec(bContext *C, wmOperator *op) newbone->dist = ebone->dist; newbone->xwidth = ebone->xwidth; newbone->zwidth = ebone->zwidth; - newbone->ease1 = ebone->ease1; - newbone->ease2 = ebone->ease2; newbone->rad_head = ebone->rad_tail; // don't copy entire bone... newbone->rad_tail = ebone->rad_tail; newbone->segments = 1; newbone->layer = ebone->layer; + /* Bendy-Bone parameters */ newbone->roll1 = ebone->roll1; newbone->roll2 = ebone->roll2; newbone->curveInX = ebone->curveInX; newbone->curveInY = ebone->curveInY; newbone->curveOutX = ebone->curveOutX; newbone->curveOutY = ebone->curveOutY; + newbone->ease1 = ebone->ease1; + newbone->ease2 = ebone->ease2; newbone->scaleIn = ebone->scaleIn; newbone->scaleOut = ebone->scaleOut; diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 8c1083dc290..89839af2ae0 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -173,6 +173,7 @@ typedef struct tPChanFCurveLink { float roll1, roll2; /* old bbone values (to be restored along with the transform properties) */ float curveInX, curveInY; /* (NOTE: we haven't renamed these this time, as their names are already long enough) */ float curveOutX, curveOutY; + float ease1, ease2; float scaleIn, scaleOut; struct IDProperty *oldprops; /* copy of custom properties at start of operator (to be restored before each modal step) */ diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index a2663cdd935..f27c4fdd96f 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -463,19 +463,20 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone eBone->weight = curBone->weight; eBone->xwidth = curBone->xwidth; eBone->zwidth = curBone->zwidth; - eBone->ease1 = curBone->ease1; - eBone->ease2 = curBone->ease2; eBone->rad_head = curBone->rad_head; eBone->rad_tail = curBone->rad_tail; eBone->segments = curBone->segments; eBone->layer = curBone->layer; + /* Bendy-Bone parameters */ eBone->roll1 = curBone->roll1; eBone->roll2 = curBone->roll2; eBone->curveInX = curBone->curveInX; eBone->curveInY = curBone->curveInY; eBone->curveOutX = curBone->curveOutX; eBone->curveOutY = curBone->curveOutY; + eBone->ease1 = curBone->ease1; + eBone->ease2 = curBone->ease2; eBone->scaleIn = curBone->scaleIn; eBone->scaleOut = curBone->scaleOut; @@ -627,19 +628,20 @@ void ED_armature_from_edit(bArmature *arm) newBone->xwidth = eBone->xwidth; newBone->zwidth = eBone->zwidth; - newBone->ease1 = eBone->ease1; - newBone->ease2 = eBone->ease2; newBone->rad_head = eBone->rad_head; newBone->rad_tail = eBone->rad_tail; newBone->segments = eBone->segments; newBone->layer = eBone->layer; + /* Bendy-Bone parameters */ newBone->roll1 = eBone->roll1; newBone->roll2 = eBone->roll2; newBone->curveInX = eBone->curveInX; newBone->curveInY = eBone->curveInY; newBone->curveOutX = eBone->curveOutX; newBone->curveOutY = eBone->curveOutY; + newBone->ease1 = eBone->ease1; + newBone->ease2 = eBone->ease2; newBone->scaleIn = eBone->scaleIn; newBone->scaleOut = eBone->scaleOut; diff --git a/source/blender/editors/armature/editarmature_retarget.c b/source/blender/editors/armature/editarmature_retarget.c index fa7bf6e7ad4..2fb216c2ef8 100644 --- a/source/blender/editors/armature/editarmature_retarget.c +++ b/source/blender/editors/armature/editarmature_retarget.c @@ -1444,19 +1444,20 @@ static EditBone *add_editbonetolist(char *name, ListBase *list) bone->dist = 0.25F; bone->xwidth = 0.1; bone->zwidth = 0.1; - bone->ease1 = 1.0; - bone->ease2 = 1.0; bone->rad_head = 0.10; bone->rad_tail = 0.05; bone->segments = 1; bone->layer = 1; //arm->layer; + /* Bendy-Bone parameters */ bone->roll1 = 0.0f; bone->roll2 = 0.0f; bone->curveInX = 0.0f; bone->curveInY = 0.0f; bone->curveOutX = 0.0f; bone->curveOutY = 0.0f; + bone->ease1 = 1.0f; + bone->ease2 = 1.0f; bone->scaleIn = 1.0f; bone->scaleOut = 1.0f; diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 49e16794588..948c7803420 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -362,6 +362,8 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bo pchan->roll1 = chan->roll1; pchan->roll2 = chan->roll2; + pchan->ease1 = chan->ease1; + pchan->ease2 = chan->ease2; pchan->scaleIn = chan->scaleIn; pchan->scaleOut = chan->scaleOut; @@ -584,6 +586,8 @@ static void pchan_clear_scale(bPoseChannel *pchan) if ((pchan->protectflag & OB_LOCK_SCALEZ) == 0) pchan->size[2] = 1.0f; + pchan->ease1 = 0.0f; + pchan->ease2 = 0.0f; pchan->scaleIn = 1.0f; pchan->scaleOut = 1.0f; } @@ -742,7 +746,7 @@ static int pose_clear_transform_generic_exec(bContext *C, wmOperator *op, /* clear any unkeyed tags */ if (pchan->bone) pchan->bone->flag &= ~BONE_UNKEYED; - + /* tag for autokeying later */ autokey = 1; } diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index b17cc286333..acb9363cf10 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -115,6 +115,8 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *a pfl->curveInY = pchan->curveInY; pfl->curveOutX = pchan->curveOutX; pfl->curveOutY = pchan->curveOutY; + pfl->ease1 = pchan->ease1; + pfl->ease2 = pchan->ease2; pfl->scaleIn = pchan->scaleIn; pfl->scaleOut = pchan->scaleOut; @@ -223,6 +225,8 @@ void poseAnim_mapping_reset(ListBase *pfLinks) pchan->curveInY = pfl->curveInY; pchan->curveOutX = pfl->curveOutX; pchan->curveOutY = pfl->curveOutY; + pchan->ease1 = pfl->ease1; + pchan->ease2 = pfl->ease2; pchan->scaleIn = pfl->scaleIn; pchan->scaleOut = pfl->scaleOut; diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 565521ad111..85715bf575b 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -595,7 +595,7 @@ static void calc_keyHandles(ListBase *nurb, float *key) if (nextp) key_to_bezt(nextfp, nextp, &next); if (prevp) key_to_bezt(prevfp, prevp, &prev); - BKE_nurb_handle_calc(&cur, prevp ? &prev : NULL, nextp ? &next : NULL, 0); + BKE_nurb_handle_calc(&cur, prevp ? &prev : NULL, nextp ? &next : NULL, 0, 0); bezt_to_key(&cur, fp); prevp = bezt; diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index d65ea8e79f0..6ce86f082b0 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -75,12 +75,15 @@ typedef struct EditBone { float dist, weight; float xwidth, length, zwidth; /* put them in order! transform uses this as scale */ - float ease1, ease2; float rad_head, rad_tail; + + /* Bendy-Bone parameters */ float roll1, roll2; float curveOutX, curveOutY; float curveInX, curveInY; + float ease1, ease2; float scaleIn, scaleOut; + float oldlength; /* for envelope scaling */ short segments; diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index b30d71941d6..de6d9d78974 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -136,6 +136,7 @@ enum { /* block->flag bits 14-17 are identical to but->drawflag bits */ +#define UI_BLOCK_POPUP_HOLD (1 << 18) #define UI_BLOCK_LIST_ITEM (1 << 19) #define UI_BLOCK_RADIAL (1 << 20) @@ -359,6 +360,7 @@ typedef struct uiSearchItems uiSearchItems; typedef void (*uiButHandleFunc)(struct bContext *C, void *arg1, void *arg2); typedef void (*uiButHandleRenameFunc)(struct bContext *C, void *arg, char *origstr); typedef void (*uiButHandleNFunc)(struct bContext *C, void *argN, void *arg2); +typedef void (*uiButHandleHoldFunc)(struct bContext *C, struct ARegion *butregion, uiBut *but); typedef int (*uiButCompleteFunc)(struct bContext *C, char *str, void *arg); typedef struct ARegion *(*uiButSearchCreateFunc)(struct bContext *C, struct ARegion *butregion, uiBut *but); typedef void (*uiButSearchFunc)(const struct bContext *C, void *arg, const char *str, uiSearchItems *items); @@ -400,6 +402,7 @@ void UI_popup_menu_reports(struct bContext *C, struct ReportList *reports) ATTR_ int UI_popup_menu_invoke(struct bContext *C, const char *idname, struct ReportList *reports) ATTR_NONNULL(1, 2); void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool enable); +void UI_popup_menu_but_set(uiPopupMenu *pup, struct ARegion *butregion, uiBut *but); /* Pie menus */ typedef struct uiPieMenu uiPieMenu; @@ -731,6 +734,8 @@ bool UI_textbutton_activate_but(const struct bContext *C, uiBut *but); void UI_but_focus_on_enter_event(struct wmWindow *win, uiBut *but); +void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN); + /* Autocomplete * * Tab complete helper functions, for use in uiButCompleteFunc callbacks. @@ -831,6 +836,7 @@ void UI_exit(void); #define UI_ITEM_R_FULL_EVENT (1 << 6) #define UI_ITEM_R_NO_BG (1 << 7) #define UI_ITEM_R_IMMEDIATE (1 << 8) +#define UI_ITEM_O_DEPRESS (1 << 9) /* uiTemplateOperatorPropertyButs flags */ #define UI_TEMPLATE_OP_PROPS_SHOW_TITLE 1 @@ -1005,6 +1011,11 @@ void uiItemFullO( uiLayout *layout, const char *idname, const char *name, int icon, struct IDProperty *properties, int context, int flag, PointerRNA *r_opptr); +void uiItemFullOMenuHold_ptr( + uiLayout *layout, struct wmOperatorType *ot, const char *name, int icon, + struct IDProperty *properties, int context, int flag, + const char *menu_id, /* extra menu arg. */ + PointerRNA *r_opptr); void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon); void uiItemFullR(uiLayout *layout, struct PointerRNA *ptr, struct PropertyRNA *prop, int index, int value, int flag, const char *name, int icon); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 96273f5e83c..0d166b874cb 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -2678,6 +2678,10 @@ static void ui_but_free(const bContext *C, uiBut *but) MEM_freeN(but->tip_argN); } + if (but->hold_argN) { + MEM_freeN(but->hold_argN); + } + if (!but->editstr && but->free_search_arg) { MEM_SAFE_FREE(but->search_arg); } @@ -4532,6 +4536,12 @@ void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but) wm_event_add(win, &event); } +void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN) +{ + but->hold_func = func; + but->hold_argN = argN; +} + void UI_but_string_info_get(bContext *C, uiBut *but, ...) { va_list args; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index bb813f399d6..09b79c19ae5 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -305,6 +305,9 @@ typedef struct uiHandleButtonData { bool used_mouse; wmTimer *autoopentimer; + /* auto open (hold) */ + wmTimer *hold_action_timer; + /* text selection/editing */ /* size of 'str' (including terminator) */ int maxlen; @@ -7809,6 +7812,15 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s data->flashtimer = NULL; } + /* add hold timer if it's used */ + if (state == BUTTON_STATE_WAIT_RELEASE && (but->hold_func != NULL)) { + data->hold_action_timer = WM_event_add_timer(data->wm, data->window, TIMER, BUTTON_AUTO_OPEN_THRESH); + } + else if (data->hold_action_timer) { + WM_event_remove_timer(data->wm, data->window, data->hold_action_timer); + data->hold_action_timer = NULL; + } + /* add a blocking ui handler at the window handler for blocking, modal states * but not for popups, because we already have a window level handler*/ if (!(but->block->handle && but->block->handle->popup)) { @@ -8423,6 +8435,25 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) button_activate_state(C, but, BUTTON_STATE_EXIT); break; + case TIMER: + { + if (event->customdata == data->hold_action_timer) { + if (true) { + data->cancel = true; + button_activate_state(C, but, BUTTON_STATE_EXIT); + } + else { + /* Do this so we can still mouse-up, closing the menu and running the button. + * This is nice to support but there are times when the button gets left pressed. + * Keep disavled for now. */ + WM_event_remove_timer(data->wm, data->window, data->hold_action_timer); + data->hold_action_timer = NULL; + } + retval = WM_UI_HANDLER_CONTINUE; + but->hold_func(C, data->region, but); + } + break; + } case MOUSEMOVE: if (ELEM(but->type, UI_BTYPE_LINK, UI_BTYPE_INLINK)) { but->flag |= UI_SELECT; @@ -9349,21 +9380,29 @@ static int ui_handle_menu_event( if (inside == 0) { uiSafetyRct *saferct = block->saferct.first; - if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) && - ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) - { - if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) { - /* for root menus, allow clicking to close */ - if (block->flag & (UI_BLOCK_OUT_1)) - menu->menuretval = UI_RETURN_OK; - else - menu->menuretval = UI_RETURN_OUT; + if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) { + if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) { + if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) { + /* for root menus, allow clicking to close */ + if (block->flag & (UI_BLOCK_OUT_1)) + menu->menuretval = UI_RETURN_OK; + else + menu->menuretval = UI_RETURN_OUT; + } + else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) { + if (block->flag & (UI_BLOCK_OUT_1)) + menu->menuretval = UI_RETURN_OK; + else + menu->menuretval = UI_RETURN_OUT; + } } - else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) { - if (block->flag & (UI_BLOCK_OUT_1)) - menu->menuretval = UI_RETURN_OK; - else - menu->menuretval = UI_RETURN_OUT; + else if (ELEM(event->val, KM_RELEASE, KM_CLICK)) { + /* For buttons that use a hold function, exit when mouse-up outside the menu. */ + if (block->flag & UI_BLOCK_POPUP_HOLD) { + /* Note, we could check the cursor is over the parent button. */ + menu->menuretval = UI_RETURN_CANCEL; + retval = WM_UI_HANDLER_CONTINUE; + } } } } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 2abb8dcf20f..983653675d6 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -266,6 +266,10 @@ struct uiBut { void *rename_arg1; void *rename_orig; + /* Run an action when holding the button down. */ + uiButHandleHoldFunc hold_func; + void *hold_argN; + uiLink *link; short linkto[2]; /* region relative coords */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 1ac54d5353d..212cd462aef 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -795,7 +795,7 @@ static void ui_item_disabled(uiLayout *layout, const char *name) * \param r_opptr: Optional, initialize with operator properties when not NULL. * Will always be written to even in the case of errors. */ -void uiItemFullO_ptr( +static uiBut *uiItemFullO_ptr_ex( uiLayout *layout, wmOperatorType *ot, const char *name, int icon, IDProperty *properties, int context, int flag, PointerRNA *r_opptr) @@ -845,6 +845,10 @@ void uiItemFullO_ptr( if (flag & UI_ITEM_R_NO_BG) UI_block_emboss_set(block, UI_EMBOSS); + if (flag & UI_ITEM_O_DEPRESS) { + but->flag |= UI_SELECT; + } + if (layout->redalert) UI_but_flag_enable(but, UI_BUT_REDALERT); @@ -862,6 +866,50 @@ void uiItemFullO_ptr( *r_opptr = *opptr; } } + + return but; +} + +static void ui_item_hold_menu(struct bContext *C, ARegion *butregion, uiBut *but) +{ + uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + uiBlock *block = layout->root->block; + UI_popup_menu_but_set(pup, butregion, but); + + block->flag |= UI_BLOCK_POPUP_HOLD; + + const char *menu_id = but->hold_argN; + MenuType *mt = WM_menutype_find(menu_id, true); + if (mt) { + Menu menu = {NULL}; + menu.layout = layout; + menu.type = mt; + mt->draw(C, &menu); + } + else { + uiItemL(layout, "Menu Missing:", ICON_NONE); + uiItemL(layout, menu_id, ICON_NONE); + } + UI_popup_menu_end(C, pup); +} + +void uiItemFullO_ptr( + uiLayout *layout, wmOperatorType *ot, + const char *name, int icon, IDProperty *properties, int context, int flag, + PointerRNA *r_opptr) +{ + uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr); +} + +void uiItemFullOMenuHold_ptr( + uiLayout *layout, wmOperatorType *ot, + const char *name, int icon, IDProperty *properties, int context, int flag, + const char *menu_id, + PointerRNA *r_opptr) +{ + uiBut *but = uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr); + UI_but_func_hold_set(but, ui_item_hold_menu, BLI_strdup(menu_id)); } void uiItemFullO( @@ -875,7 +923,7 @@ void uiItemFullO( ot, opname, { if (r_opptr) { *r_opptr = PointerRNA_NULL; - }; + } return; }); diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index aabb165b483..cd3aa2095b0 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -2635,6 +2635,7 @@ struct uiPopupMenu { uiBlock *block; uiLayout *layout; uiBut *but; + ARegion *butregion; int mx, my; bool popup, slideout; @@ -2881,17 +2882,33 @@ uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon) return UI_popup_menu_begin_ex(C, title, __func__, icon); } +/** + * Setting the button makes the popup open from the button instead of the cursor. + */ +void UI_popup_menu_but_set(uiPopupMenu *pup, struct ARegion *butregion, uiBut *but) +{ + pup->but = but; + pup->butregion = butregion; +} + /* set the whole structure to work */ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) { wmWindow *window = CTX_wm_window(C); uiPopupBlockHandle *menu; + uiBut *but = NULL; + ARegion *butregion = NULL; pup->popup = true; pup->mx = window->eventstate->x; pup->my = window->eventstate->y; - - menu = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup); + + if (pup->but) { + but = pup->but; + butregion = pup->butregion; + } + + menu = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup); menu->popup = true; UI_popup_handlers_add(C, &window->modalhandlers, menu, 0); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index e3977219eda..f419ab2d1ed 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -66,6 +66,15 @@ /* icons are 80% of height of button (16 pixels inside 20 height) */ #define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect)) +#define UI_BUT_FLAGS_PUBLIC \ + (UI_SELECT | UI_SCROLLED | UI_ACTIVE | UI_HAS_ICON | UI_TEXTINPUT | UI_HIDDEN) + +/* Bits 0..5 are from UI_SELECT .. etc */ +enum { + /* Show that holding the button opens a menu. */ + UI_STATE_HOLD_ACTION = (1 << 6), +}; + /* ************** widget base functions ************** */ /** * - in: roundbox codes for corner types and radius @@ -145,45 +154,54 @@ static const float jit[WIDGET_AA_JITTER][2] = { {-0.272855, 0.269918}, { 0.095909, 0.388710} }; -static const float num_tria_vert[3][2] = { +/* -------------------------------------------------------------------- */ +/** \name Shape Preset Data + * \{ */ + +static const float g_shape_preset_number_arrow_vert[3][2] = { {-0.352077, 0.532607}, {-0.352077, -0.549313}, {0.330000, -0.008353} }; - -static const unsigned int num_tria_face[1][3] = { +static const uint g_shape_preset_number_arrow_face[1][3] = { {0, 1, 2} }; -static const float scroll_circle_vert[16][2] = { +static const float g_shape_preset_scroll_circle_vert[16][2] = { {0.382684, 0.923879}, {0.000001, 1.000000}, {-0.382683, 0.923880}, {-0.707107, 0.707107}, {-0.923879, 0.382684}, {-1.000000, 0.000000}, {-0.923880, -0.382684}, {-0.707107, -0.707107}, {-0.382683, -0.923880}, {0.000000, -1.000000}, {0.382684, -0.923880}, {0.707107, -0.707107}, {0.923880, -0.382684}, {1.000000, -0.000000}, {0.923880, 0.382683}, {0.707107, 0.707107} }; - -static const unsigned int scroll_circle_face[14][3] = { +static const uint g_shape_preset_scroll_circle_face[14][3] = { {0, 1, 2}, {2, 0, 3}, {3, 0, 15}, {3, 15, 4}, {4, 15, 14}, {4, 14, 5}, {5, 14, 13}, {5, 13, 6}, {6, 13, 12}, {6, 12, 7}, {7, 12, 11}, {7, 11, 8}, {8, 11, 10}, {8, 10, 9} }; - -static const float menu_tria_vert[6][2] = { +static const float g_shape_preset_menu_arrow_vert[6][2] = { {-0.33, 0.16}, {0.33, 0.16}, {0, 0.82}, {0, -0.82}, {-0.33, -0.16}, {0.33, -0.16} }; +static const uint g_shape_preset_menu_arrow_face[2][3] = {{2, 0, 1}, {3, 5, 4}}; - - -static const unsigned int menu_tria_face[2][3] = {{2, 0, 1}, {3, 5, 4}}; - -static const float check_tria_vert[6][2] = { +static const float g_shape_preset_checkmark_vert[6][2] = { {-0.578579, 0.253369}, {-0.392773, 0.412794}, {-0.004241, -0.328551}, {-0.003001, 0.034320}, {1.055313, 0.864744}, {0.866408, 1.026895} }; -static const unsigned int check_tria_face[4][3] = { +static const uint g_shape_preset_checkmark_face[4][3] = { {3, 2, 4}, {3, 4, 5}, {1, 0, 3}, {0, 2, 3} }; +#define OY -0.2 +#define SC 0.35 +static const float g_shape_preset_hold_action_vert[6][2] = { + {-0.5 + SC, 1.0 + OY}, {0.5, 1.0 + OY}, {0.5, 0.0 + OY + SC}, +}; +static const uint g_shape_preset_hold_action_face[2][3] = {{2, 0, 1}, {3, 5, 4}}; +#undef OY +#undef SC + +/** \} */ + /* ************************************************* */ void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3, @@ -490,9 +508,12 @@ static void round_box_edges(uiWidgetBase *wt, int roundboxalign, const rcti *rec round_box__edges(wt, roundboxalign, rect, rad, rad - U.pixelsize); } +/* -------------------------------------------------------------------- */ +/** \name Shape Preset Mini API + * \{ */ /* based on button rect, return scaled array of triangles */ -static void widget_draw_tria_ex( +static void shape_preset_init_trias_ex( uiWidgetTrias *tria, const rcti *rect, float triasize, char where, /* input data */ const float verts[][2], const int verts_tot, @@ -531,23 +552,31 @@ static void widget_draw_tria_ex( tria->index = tris; } -static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +static void shape_preset_init_number_arrows(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +{ + shape_preset_init_trias_ex( + tria, rect, triasize, where, + g_shape_preset_number_arrow_vert, ARRAY_SIZE(g_shape_preset_number_arrow_vert), + g_shape_preset_number_arrow_face, ARRAY_SIZE(g_shape_preset_number_arrow_face)); +} + +static void shape_preset_init_hold_action(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { - widget_draw_tria_ex( + shape_preset_init_trias_ex( tria, rect, triasize, where, - num_tria_vert, ARRAY_SIZE(num_tria_vert), - num_tria_face, ARRAY_SIZE(num_tria_face)); + g_shape_preset_hold_action_vert, ARRAY_SIZE(g_shape_preset_hold_action_vert), + g_shape_preset_hold_action_face, ARRAY_SIZE(g_shape_preset_hold_action_face)); } -static void widget_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +static void shape_preset_init_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { - widget_draw_tria_ex( + shape_preset_init_trias_ex( tria, rect, triasize, where, - scroll_circle_vert, ARRAY_SIZE(scroll_circle_vert), - scroll_circle_face, ARRAY_SIZE(scroll_circle_face)); + g_shape_preset_scroll_circle_vert, ARRAY_SIZE(g_shape_preset_scroll_circle_vert), + g_shape_preset_scroll_circle_face, ARRAY_SIZE(g_shape_preset_scroll_circle_face)); } -static void widget_trias_draw(uiWidgetTrias *tria, unsigned int pos) +static void shape_preset_draw_trias(uiWidgetTrias *tria, uint pos) { immBegin(GWN_PRIM_TRIS, tria->tot * 3); for (int i = 0; i < tria->tot; ++i) @@ -570,7 +599,7 @@ static void widget_draw_vertex_buffer(unsigned int pos, unsigned int col, int mo immEnd(); } -static void widget_menu_trias(uiWidgetTrias *tria, const rcti *rect) +static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect) { float centx, centy, size; int a; @@ -581,15 +610,15 @@ static void widget_menu_trias(uiWidgetTrias *tria, const rcti *rect) size = 0.4f * BLI_rcti_size_y(rect); for (a = 0; a < 6; a++) { - tria->vec[a][0] = size * menu_tria_vert[a][0] + centx; - tria->vec[a][1] = size * menu_tria_vert[a][1] + centy; + tria->vec[a][0] = size * g_shape_preset_menu_arrow_vert[a][0] + centx; + tria->vec[a][1] = size * g_shape_preset_menu_arrow_vert[a][1] + centy; } tria->tot = 2; - tria->index = menu_tria_face; + tria->index = g_shape_preset_menu_arrow_face; } -static void widget_check_trias(uiWidgetTrias *tria, const rcti *rect) +static void shape_preset_trias_from_rect_checkmark(uiWidgetTrias *tria, const rcti *rect) { float centx, centy, size; int a; @@ -600,14 +629,16 @@ static void widget_check_trias(uiWidgetTrias *tria, const rcti *rect) size = 0.5f * BLI_rcti_size_y(rect); for (a = 0; a < 6; a++) { - tria->vec[a][0] = size * check_tria_vert[a][0] + centx; - tria->vec[a][1] = size * check_tria_vert[a][1] + centy; + tria->vec[a][0] = size * g_shape_preset_checkmark_vert[a][0] + centx; + tria->vec[a][1] = size * g_shape_preset_checkmark_vert[a][1] + centy; } tria->tot = 4; - tria->index = check_tria_face; + tria->index = g_shape_preset_checkmark_face; } +/** \} */ + /* prepares shade colors */ static void shadecolors4(char coltop[4], char coldown[4], const char *color, short shadetop, short shadedown) @@ -805,11 +836,11 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) gpuTranslate2fv(jit[j]); if (wtb->tria1.tot) - widget_trias_draw(&wtb->tria1, pos); + shape_preset_draw_trias(&wtb->tria1, pos); if (wtb->tria2.tot) - widget_trias_draw(&wtb->tria2, pos); - + shape_preset_draw_trias(&wtb->tria2, pos); + gpuTranslate2f(-jit[j][0], -jit[j][1]); } @@ -2742,8 +2773,8 @@ static void widget_numbut_draw(uiWidgetColors *wcol, rcti *rect, int state, int /* decoration */ if (!(state & UI_TEXTINPUT)) { - widget_num_tria(&wtb.tria1, rect, 0.6f, 'l'); - widget_num_tria(&wtb.tria2, rect, 0.6f, 'r'); + shape_preset_init_number_arrows(&wtb.tria1, rect, 0.6f, 'l'); + shape_preset_init_number_arrows(&wtb.tria2, rect, 0.6f, 'r'); } widgetbase_draw(&wtb, wcol); @@ -2885,12 +2916,12 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s wcol->item[3] = 255; if (horizontal) { - widget_scroll_circle(&wtb.tria1, slider, 0.6f, 'l'); - widget_scroll_circle(&wtb.tria2, slider, 0.6f, 'r'); + shape_preset_init_scroll_circle(&wtb.tria1, slider, 0.6f, 'l'); + shape_preset_init_scroll_circle(&wtb.tria2, slider, 0.6f, 'r'); } else { - widget_scroll_circle(&wtb.tria1, slider, 0.6f, 'b'); - widget_scroll_circle(&wtb.tria2, slider, 0.6f, 't'); + shape_preset_init_scroll_circle(&wtb.tria1, slider, 0.6f, 'b'); + shape_preset_init_scroll_circle(&wtb.tria2, slider, 0.6f, 't'); } } widgetbase_draw(&wtb, wcol); @@ -3233,7 +3264,7 @@ static void widget_menubut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), round_box_edges(&wtb, roundboxalign, rect, rad); /* decoration */ - widget_menu_trias(&wtb.tria1, rect); + shape_preset_trias_from_rect_menu(&wtb.tria1, rect); widgetbase_draw(&wtb, wcol); @@ -3372,7 +3403,7 @@ static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, int state, int UN /* decoration */ if (state & UI_SELECT) { - widget_check_trias(&wtb.tria1, &recttemp); + shape_preset_trias_from_rect_checkmark(&wtb.tria1, &recttemp); } widgetbase_draw(&wtb, wcol); @@ -3455,6 +3486,7 @@ static void widget_but(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int widgetbase_draw(&wtb, wcol); } +#if 0 static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign) { uiWidgetBase wtb; @@ -3467,6 +3499,25 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), widgetbase_draw(&wtb, wcol); } +#endif + +static void widget_roundbut_exec(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) +{ + uiWidgetBase wtb; + const float rad = 0.25f * U.widget_unit; + + widget_init(&wtb); + + if (state & UI_STATE_HOLD_ACTION) { + /* Show that keeping pressed performs another action (typically a menu). */ + shape_preset_init_hold_action(&wtb.tria1, rect, 0.75f, 'r'); + } + + /* half rounded */ + round_box_edges(&wtb, roundboxalign, rect, rad); + + widgetbase_draw(&wtb, wcol); +} static void widget_tab(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign) { @@ -3590,7 +3641,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) case UI_WTYPE_EXEC: wt.wcol_theme = &btheme->tui.wcol_tool; - wt.draw = widget_roundbut; + wt.draw = widget_roundbut_exec; break; case UI_WTYPE_TAB: @@ -3770,6 +3821,10 @@ static int widget_roundbox_set(uiBut *but, rcti *rect) return roundbox; } +/* -------------------------------------------------------------------- */ +/** \name Public API + * \{ */ + /* conversion from old to new buttons, so still messy */ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rcti *rect) { @@ -4009,7 +4064,7 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct roundboxalign = widget_roundbox_set(but, rect); - state = but->flag; + state = but->flag & UI_BUT_FLAGS_PUBLIC; if ((but->editstr) || (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI) && ui_but_drag_multi_edit_get(but))) @@ -4017,6 +4072,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct state |= UI_TEXTINPUT; } + if (but->hold_func) { + state |= UI_STATE_HOLD_ACTION; + } + if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) if (but->dt != UI_EMBOSS_PULLDOWN) disabled = true; @@ -4343,3 +4402,5 @@ void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int UI_fontstyle_draw(fstyle, &trect, drawstr, (unsigned char *)wt->wcol.text); } } + +/** \} */ diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index d6843cc959d..ab55221c1dc 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -208,7 +208,12 @@ static void graph_panel_properties(const bContext *C, Panel *pa) sub = uiLayoutRow(row, true); uiLayoutSetEnabled(sub, (fcu->color_mode == FCURVE_COLOR_CUSTOM)); uiItemR(sub, &fcu_ptr, "color", 0, "", ICON_NONE); - + + /* smoothing setting */ + col = uiLayoutColumn(layout, true); + uiItemL(col, IFACE_("Auto Handle Smoothing:"), ICON_NONE); + uiItemR(col, &fcu_ptr, "auto_smoothing", 0, "", ICON_NONE); + MEM_freeN(ale); } diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index afb5eca5263..44803f8f7ee 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -2578,7 +2578,11 @@ float schlick_fresnel(float u) float GTR1(float NdotH, float a) { - if (a >= 1.0) return M_1_PI; + if (a >= 1.0) { + return M_1_PI; + } + + a = max(a, 0.001); float a2 = a*a; float t = 1.0 + (a2 - 1.0) * NdotH*NdotH; return (a2 - 1.0) / (M_PI * log(a2) * t); diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index cc8e9a18655..c8780c6b4b8 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -268,6 +268,7 @@ typedef struct bPoseChannel { float roll1, roll2; float curveInX, curveInY; float curveOutX, curveOutY; + float ease1, ease2; float scaleIn, scaleOut; struct bPoseChannel *bbone_prev; /* next/prev bones to use as handle references when calculating bbones (optional) */ diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 977cd2347ad..099d5da7c49 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -490,7 +490,10 @@ typedef struct FCurve { float curval; /* value stored from last time curve was evaluated (not threadsafe, debug display only!) */ short flag; /* user-editable settings for this curve */ short extend; /* value-extending mode for this curve (does not cover */ + char auto_smoothing; /* auto-handle smoothing mode */ + char pad[7]; + /* RNA - data link */ int array_index; /* if applicable, the index of the RNA-array item to get */ char *rna_path; /* RNA-path to resolve data-access */ @@ -545,6 +548,12 @@ typedef enum eFCurve_Coloring { FCURVE_COLOR_CUSTOM = 2, /* custom color */ } eFCurve_Coloring; +/* curve smoothing modes */ +typedef enum eFCurve_Smoothing { + FCURVE_SMOOTH_NONE = 0, /* legacy mode: auto handles only consider adjacent points */ + FCURVE_SMOOTH_CONT_ACCEL = 1, /* maintain continuity of the acceleration */ +} eFCurve_Smoothing; + /* ************************************************ */ /* 'Action' Datatypes */ diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index cda6441f0ae..757c0eb8394 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -66,12 +66,12 @@ typedef struct Bone { float dist, weight; /* dist, weight: for non-deformgroup deforms */ float xwidth, length, zwidth; /* width: for block bones. keep in this order, transform! */ - float ease1, ease2; /* length of bezier handles */ float rad_head, rad_tail; /* radius for head/tail sphere, defining deform as well, parent->rad_tip overrides rad_head */ float roll1, roll2; /* curved bones settings - these define the "restpose" for a curved bone */ float curveInX, curveInY; float curveOutX, curveOutY; + float ease1, ease2; /* length of bezier handles */ float scaleIn, scaleOut; float size[3]; /* patch for upward compat, UNUSED! */ diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index f4f4c1818c3..261e0a5410f 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -124,7 +124,8 @@ typedef struct BezTriple { float back; /* BEZT_IPO_BACK */ float amplitude, period; /* BEZT_IPO_ELASTIC */ - char pad[4]; + char f5; /* f5: used for auto handle to distinguish between normal handle and exception (extrema) */ + char pad[3]; } BezTriple; /* note; alfa location in struct is abused by Key system */ @@ -396,6 +397,12 @@ typedef enum eBezTriple_Handle { HD_ALIGN_DOUBLESIDE = 5, /* align handles, displayed both of them. used for masks */ } eBezTriple_Handle; +/* f5 (beztriple) */ +typedef enum eBezTriple_Auto_Type { + HD_AUTOTYPE_NORMAL = 0, + HD_AUTOTYPE_SPECIAL = 1 +} eBezTriple_Auto_Type; + /* interpolation modes (used only for BezTriple->ipo) */ typedef enum eBezTriple_Interpolation { /* traditional interpolation */ diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 2ed147a75cf..f8a492c3cee 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -543,7 +543,22 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone) RNA_def_property_range(prop, -5.0f, 5.0f); RNA_def_property_ui_text(prop, "Out Y", "Y-axis handle offset for end of the B-Bone's curve, adjusts curvature"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + /* Ease In/Out */ + prop = RNA_def_property(srna, "bbone_easein", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "ease1"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "B-Bone Ease In", "Length of first Bezier Handle (for B-Bones only)"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + prop = RNA_def_property(srna, "bbone_easeout", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "ease2"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "B-Bone Ease Out", "Length of second Bezier Handle (for B-Bones only)"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + /* Scale In/Out */ prop = RNA_def_property(srna, "bbone_scalein", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "scaleIn"); @@ -681,18 +696,6 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_ui_text(prop, "B-Bone Segments", "Number of subdivisions of bone (for B-Bones only)"); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); - prop = RNA_def_property(srna, "bbone_in", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "ease1"); - RNA_def_property_range(prop, 0.0f, 2.0f); - RNA_def_property_ui_text(prop, "B-Bone Ease In", "Length of first Bezier Handle (for B-Bones only)"); - RNA_def_property_update(prop, 0, "rna_Armature_update_data"); - - prop = RNA_def_property(srna, "bbone_out", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "ease2"); - RNA_def_property_range(prop, 0.0f, 2.0f); - RNA_def_property_ui_text(prop, "B-Bone Ease Out", "Length of second Bezier Handle (for B-Bones only)"); - RNA_def_property_update(prop, 0, "rna_Armature_update_data"); - prop = RNA_def_property(srna, "bbone_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "xwidth"); RNA_def_property_range(prop, 0.0f, 1000.0f); diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 08aa681e17b..969424cd989 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1897,6 +1897,11 @@ static void rna_def_fcurve(BlenderRNA *brna) "Use custom hand-picked color for F-Curve"}, {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem prop_mode_smoothing_items[] = { + {FCURVE_SMOOTH_NONE, "NONE", 0, "None", "Auto handles only take adjacent keys into account (legacy mode)"}, + {FCURVE_SMOOTH_CONT_ACCEL, "CONT_ACCEL", 0, "Continuous Acceleration", "Auto handles are placed to avoid jumps in acceleration"}, + {0, NULL, 0, NULL, NULL} + }; srna = RNA_def_struct(brna, "FCurve", NULL); RNA_def_struct_ui_text(srna, "F-Curve", "F-Curve defining values of a period of time"); @@ -1970,6 +1975,11 @@ static void rna_def_fcurve(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Hide", "F-Curve and its keyframes are hidden in the Graph Editor graphs"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL); + prop = RNA_def_property(srna, "auto_smoothing", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_mode_smoothing_items); + RNA_def_property_ui_text(prop, "Auto Handle Smoothing", "Algorithm used to compute automatic handles"); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FCurve_update_data"); + /* State Info (for Debugging) */ prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FCURVE_DISABLED); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 267ac16e1be..2bed0eb7429 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -175,11 +175,11 @@ static void rna_uiItemPointerR(uiLayout *layout, struct PointerRNA *ptr, const c uiItemPointerR(layout, ptr, propname, searchptr, searchpropname, name, icon); } -static PointerRNA rna_uiItemO(uiLayout *layout, const char *opname, const char *name, const char *text_ctxt, - int translate, int icon, int emboss, int icon_value) +static PointerRNA rna_uiItemO( + uiLayout *layout, const char *opname, const char *name, const char *text_ctxt, + int translate, int icon, int emboss, int depress, int icon_value) { wmOperatorType *ot; - int flag; ot = WM_operatortype_find(opname, 0); /* print error next */ if (!ot || !ot->srna) { @@ -193,15 +193,39 @@ static PointerRNA rna_uiItemO(uiLayout *layout, const char *opname, const char * if (icon_value && !icon) { icon = icon_value; } - - flag = 0; - flag |= (emboss) ? 0 : UI_ITEM_R_NO_BG; + int flag = (emboss) ? 0 : UI_ITEM_R_NO_BG; + flag |= (depress) ? UI_ITEM_O_DEPRESS : 0; PointerRNA opptr; uiItemFullO_ptr(layout, ot, name, icon, NULL, uiLayoutGetOperatorContext(layout), flag, &opptr); return opptr; } +static PointerRNA rna_uiItemOMenuHold( + uiLayout *layout, const char *opname, const char *name, const char *text_ctxt, + int translate, int icon, int emboss, int depress, int icon_value, + const char *menu) +{ + wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */ + if (!ot || !ot->srna) { + RNA_warning("%s '%s'", ot ? "unknown operator" : "operator missing srna", opname); + return PointerRNA_NULL; + } + + /* Get translated name (label). */ + name = rna_translate_ui_text(name, text_ctxt, ot->srna, NULL, translate); + if (icon_value && !icon) { + icon = icon_value; + } + int flag = (emboss) ? 0 : UI_ITEM_R_NO_BG; + flag |= (depress) ? UI_ITEM_O_DEPRESS : 0; + + PointerRNA opptr; + uiItemFullOMenuHold_ptr(layout, ot, name, icon, NULL, uiLayoutGetOperatorContext(layout), flag, menu, &opptr); + return opptr; +} + + static void rna_uiItemMenuEnumO(uiLayout *layout, bContext *C, const char *opname, const char *propname, const char *name, const char *text_ctxt, int translate, int icon) { @@ -553,15 +577,24 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); api_ui_item_common(func); - func = RNA_def_function(srna, "operator", "rna_uiItemO"); - api_ui_item_op_common(func); - RNA_def_boolean(func, "emboss", true, "", "Draw the button itself, just the icon/text"); - parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED); - RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item"); - parm = RNA_def_pointer(func, "properties", "OperatorProperties", "", "Operator properties to fill in"); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED | PARM_RNAPTR); - RNA_def_function_return(func, parm); - RNA_def_function_ui_description(func, "Item. Places a button into the layout to call an Operator"); + for (int is_menu_hold = 0; is_menu_hold < 2; is_menu_hold++) { + func = (is_menu_hold) ? + RNA_def_function(srna, "operator_menu_hold", "rna_uiItemOMenuHold") : + RNA_def_function(srna, "operator", "rna_uiItemO"); + api_ui_item_op_common(func); + RNA_def_boolean(func, "emboss", true, "", "Draw the button itself, just the icon/text"); + RNA_def_boolean(func, "depress", false, "", "Draw pressed in"); + parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item"); + if (is_menu_hold) { + parm = RNA_def_string(func, "menu", NULL, 0, "", "Identifier of the menu"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + } + parm = RNA_def_pointer(func, "properties", "OperatorProperties", "", "Operator properties to fill in"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_function_return(func, parm); + RNA_def_function_ui_description(func, "Item. Places a button into the layout to call an Operator"); + } func = RNA_def_function(srna, "operator_enum", "uiItemsEnumO"); parm = RNA_def_string(func, "operator", NULL, 0, "", "Identifier of the operator"); |