From 2e6a7353852f432f626269b3e7d9246701c690e2 Mon Sep 17 00:00:00 2001 From: "Henrik Dick (weasel)" <> Date: Tue, 21 Sep 2021 23:00:49 +0800 Subject: GPencil: Curvature support for length modifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the stroke following an approximated circluar/helical curve. This can be used as an effect for lineart or on its own as helix generator. Reviewed By: Sebastian Parborg (zeddb), Hans Goudey (HooglyBoogly), YimingWu (NicksBest), Antonio Vazquez (antoniov), Henrik Dick (weasel) Differential Revision: https://developer.blender.org/D11668 # 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交 # 说明将会终止提交。 # # 位于分支 master # 您的分支与上游分支 'origin/master' 一致。 # # 要提交的变更: # 修改: source/blender/blenkernel/BKE_gpencil_geom.h # 修改: source/blender/blenkernel/intern/gpencil_geom.cc # 修改: source/blender/gpencil_modifiers/intern/MOD_gpencillength.c # 修改: source/blender/makesdna/DNA_gpencil_modifier_defaults.h # 修改: source/blender/makesdna/DNA_gpencil_modifier_types.h # 修改: source/blender/makesrna/intern/rna_gpencil_modifier.c # --- source/blender/blenkernel/BKE_gpencil_geom.h | 7 +- source/blender/blenkernel/intern/gpencil_geom.cc | 258 ++++++++++++++++++--- .../gpencil_modifiers/intern/MOD_gpencillength.c | 136 +++++++++-- .../makesdna/DNA_gpencil_modifier_defaults.h | 6 +- .../blender/makesdna/DNA_gpencil_modifier_types.h | 7 +- .../blender/makesrna/intern/rna_gpencil_modifier.c | 75 +++++- 6 files changed, 415 insertions(+), 74 deletions(-) diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index d472fd6f02b..a9cd553a8fe 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -114,7 +114,12 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd, bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode); + const short mode, + const bool follow_curvature, + const int extra_point_count, + const float segment_influence, + const float max_angle, + const bool invert_curvature); bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps, const int index_from, const int index_to); diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 8ff026231f5..d7c906be18e 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -540,65 +540,242 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, return true; } +/** + * Give extra stroke points before and after the original tip points. + * \param gps: Target stroke + * \param count_before: how many extra points to be added before a stroke + * \param count_after: how many extra points to be added after a stroke + */ +static bool BKE_gpencil_stroke_extra_points(bGPDstroke *gps, + const int count_before, + const int count_after) +{ + bGPDspoint *pts = gps->points; + + BLI_assert(count_before >= 0); + BLI_assert(count_after >= 0); + if (!count_before && !count_after) { + return false; + } + + const int new_count = count_before + count_after + gps->totpoints; + + bGPDspoint *new_pts = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, __func__); + + for (int i = 0; i < count_before; i++) { + memcpy(&new_pts[i], &pts[0], sizeof(bGPDspoint)); + } + memcpy(&new_pts[count_before], pts, sizeof(bGPDspoint) * gps->totpoints); + for (int i = new_count - count_after; i < new_count; i++) { + memcpy(&new_pts[i], &pts[gps->totpoints - 1], sizeof(bGPDspoint)); + } + + if (gps->dvert) { + MDeformVert *new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, __func__); + + for (int i = 0; i < new_count; i++) { + MDeformVert *dv = &gps->dvert[CLAMPIS(i - count_before, 0, gps->totpoints - 1)]; + int inew = i; + new_dv[inew].flag = dv->flag; + new_dv[inew].totweight = dv->totweight; + new_dv[inew].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, + __func__); + memcpy(new_dv[inew].dw, dv->dw, sizeof(MDeformWeight) * dv->totweight); + } + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + gps->dvert = new_dv; + } + + MEM_freeN(gps->points); + gps->points = new_pts; + gps->totpoints = new_count; + + return true; +} + /** * Backbone stretch similar to Freestyle. * \param gps: Stroke to sample. - * \param dist: Distance of one segment. - * \param overshoot_fac: How exact is the follow curve algorithm. + * \param dist: Length of the added section. + * \param overshoot_fac: Relative length of the curve which is used to determine the extension. * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End) + * \param follow_curvature: True for appproximating curvature of given overshoot. + * \param extra_point_count: When follow_curvature is true, use this amount of extra points */ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode) + const short mode, + const bool follow_curvature, + const int extra_point_count, + const float segment_influence, + const float max_angle, + const bool invert_curvature) { #define BOTH 0 #define START 1 #define END 2 - bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt; - int i; - float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac); + const bool do_start = ELEM(mode, BOTH, START); + const bool do_end = ELEM(mode, BOTH, END); + float used_percent_length = overshoot_fac; + CLAMP(used_percent_length, 1e-4f, 1.0f); + if (!isfinite(used_percent_length)) { + /* #used_percent_length must always be finite, otherwise a segfault occurs. + * Since this function should never segfault, set #used_percent_length to a safe fallback. */ + /* NOTE: This fallback is used if gps->totpoints == 2, see MOD_gpencillength.c */ + used_percent_length = 0.1f; + } - if (gps->totpoints < 2 || dist < FLT_EPSILON) { + if (gps->totpoints <= 1 || dist < FLT_EPSILON || extra_point_count <= 0) { return false; } - last_pt = &pt[gps->totpoints - 1]; - second_last = &pt[gps->totpoints - 2]; - next_pt = &pt[1]; - - if (mode == BOTH || mode == START) { - float len1 = 0.0f; - i = 1; - while (len1 < threshold && gps->totpoints > i) { - next_pt = &pt[i]; - len1 = len_v3v3(&next_pt->x, &pt->x); - i++; + /* NOTE: When it's just a straight line, we don't need to do the curvature stuff. */ + if (!follow_curvature || gps->totpoints <= 2) { + /* Not following curvature, just straight line. */ + /* NOTE: #overshoot_point_param can not be zero. */ + float overshoot_point_param = used_percent_length * (gps->totpoints - 1); + float result[3]; + + if (do_start) { + int index1 = floor(overshoot_point_param); + int index2 = ceil(overshoot_point_param); + interp_v3_v3v3(result, + &gps->points[index1].x, + &gps->points[index2].x, + fmodf(overshoot_point_param, 1.0f)); + sub_v3_v3(result, &gps->points[0].x); + if (UNLIKELY(is_zero_v3(result))) { + sub_v3_v3v3(result, &gps->points[1].x, &gps->points[0].x); + } + madd_v3_v3fl(&gps->points[0].x, result, -dist / len_v3(result)); + } + + if (do_end) { + int index1 = gps->totpoints - 1 - floor(overshoot_point_param); + int index2 = gps->totpoints - 1 - ceil(overshoot_point_param); + interp_v3_v3v3(result, + &gps->points[index1].x, + &gps->points[index2].x, + fmodf(overshoot_point_param, 1.0f)); + sub_v3_v3(result, &gps->points[gps->totpoints - 1].x); + if (UNLIKELY(is_zero_v3(result))) { + sub_v3_v3v3( + result, &gps->points[gps->totpoints - 2].x, &gps->points[gps->totpoints - 1].x); + } + madd_v3_v3fl(&gps->points[gps->totpoints - 1].x, result, -dist / len_v3(result)); } - float extend1 = (len1 + dist) / len1; - float result1[3]; - - interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1); - copy_v3_v3(&pt->x, result1); + return true; } - if (mode == BOTH || mode == END) { - float len2 = 0.0f; - i = 2; - while (len2 < threshold && gps->totpoints >= i) { - second_last = &pt[gps->totpoints - i]; - len2 = len_v3v3(&last_pt->x, &second_last->x); - i++; + /* Curvature calculation. */ + + /* First allocate the new stroke size. */ + const int first_old_index = do_start ? extra_point_count : 0; + const int last_old_index = gps->totpoints - 1 + first_old_index; + const int orig_totpoints = gps->totpoints; + BKE_gpencil_stroke_extra_points(gps, first_old_index, do_end ? extra_point_count : 0); + + /* The fractional amount of points to query when calculating the average curvature of the + * strokes. */ + const float overshoot_parameter = used_percent_length * (orig_totpoints - 2); + int overshoot_pointcount = ceil(overshoot_parameter); + CLAMP(overshoot_pointcount, 1, orig_totpoints - 2); + + /* Do for both sides without code duplication. */ + float no[3], vec1[3], vec2[3], total_angle[3]; + for (int k = 0; k < 2; k++) { + if ((k == 0 && !do_start) || (k == 1 && !do_end)) { + continue; } - float extend2 = (len2 + dist) / len2; - float result2[3]; - interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2); + const int start_i = k == 0 ? first_old_index : + last_old_index; // first_old_index, last_old_index + const int dir_i = 1 - k * 2; // 1, -1 - copy_v3_v3(&last_pt->x, result2); - } + sub_v3_v3v3(vec1, &gps->points[start_i + dir_i].x, &gps->points[start_i].x); + zero_v3(total_angle); + float segment_length = normalize_v3(vec1); + float overshoot_length = 0.0f; + + /* Accumulate rotation angle and length. */ + int j = 0; + for (int i = start_i; j < overshoot_pointcount; i += dir_i, j++) { + /* Don't fully add last segment to get continuity in overshoot_fac. */ + float fac = fmin(overshoot_parameter - j, 1.0f); + + /* Read segments. */ + copy_v3_v3(vec2, vec1); + sub_v3_v3v3(vec1, &gps->points[i + dir_i * 2].x, &gps->points[i + dir_i].x); + const float len = normalize_v3(vec1); + float angle = angle_normalized_v3v3(vec1, vec2) * fac; + + /* Add half of both adjacent legs of the current angle. */ + const float added_len = (segment_length + len) * 0.5f * fac; + overshoot_length += added_len; + segment_length = len; + + if (angle > max_angle) { + continue; + } + if (angle > M_PI * 0.995f) { + continue; + } + + angle *= powf(added_len, segment_influence); + + cross_v3_v3v3(no, vec1, vec2); + normalize_v3_length(no, angle); + add_v3_v3(total_angle, no); + } + if (UNLIKELY(overshoot_length == 0.0f)) { + /* Don't do a proper extension if the used points are all in the same position. */ + continue; + } + + sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x); + /* In general curvature = 1/radius. For the case without the + * weights introduced by #segment_influence, the calculation is + * curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length */ + float curvature = normalize_v3(total_angle) / overshoot_length; + /* Compensate for the weights powf(added_len, segment_influence). */ + curvature /= powf(overshoot_length / fminf(overshoot_parameter, (float)j), segment_influence); + if (invert_curvature) { + curvature = -curvature; + } + const float angle_step = curvature * dist / extra_point_count; + float step_length = dist / extra_point_count; + if (fabsf(angle_step) > FLT_EPSILON) { + /* Make a direct step length from the assigned arc step length. */ + step_length *= sin(angle_step * 0.5f) / (angle_step * 0.5f); + } + else { + zero_v3(total_angle); + } + const float prev_length = normalize_v3_length(vec1, step_length); + + /* Build rotation matrix here to get best performance. */ + float rot[3][3]; + float q[4]; + axis_angle_to_quat(q, total_angle, angle_step); + quat_to_mat3(rot, q); + + /* Rotate the starting direction to account for change in edge lengths. */ + axis_angle_to_quat(q, + total_angle, + fmaxf(0.0f, 1.0f - fabs(segment_influence)) * + (curvature * prev_length - angle_step) / 2.0f); + mul_qt_v3(q, vec1); + + /* Now iteratively accumulate the segments with a rotating added direction. */ + for (int i = start_i - dir_i, j = 0; j < extra_point_count; i -= dir_i, j++) { + mul_v3_m3v3(vec1, rot, vec1); + add_v3_v3v3(&gps->points[i].x, vec1, &gps->points[i + dir_i].x); + } + } return true; } @@ -749,6 +926,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo second_last = &pt[gps->totpoints - 2]; + float len; float len1, cut_len1; float len2, cut_len2; len1 = len2 = cut_len1 = cut_len2 = 0.0f; @@ -759,11 +937,13 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 0; index_end = gps->totpoints - 1; while (len1 < dist && gps->totpoints > i + 1) { - len1 += len_v3v3(&pt[i].x, &pt[i + 1].x); + len = len_v3v3(&pt[i].x, &pt[i + 1].x); + len1 += len; cut_len1 = len1 - dist; i++; } index_start = i - 1; + interp_v3_v3v3(&pt[index_start].x, &pt[index_start + 1].x, &pt[index_start].x, cut_len1 / len); } if (mode == END) { @@ -771,18 +951,20 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 2; while (len2 < dist && gps->totpoints >= i) { second_last = &pt[gps->totpoints - i]; - len2 += len_v3v3(&second_last[1].x, &second_last->x); + len = len_v3v3(&second_last[1].x, &second_last->x); + len2 += len; cut_len2 = len2 - dist; i++; } index_end = gps->totpoints - i + 2; + interp_v3_v3v3(&pt[index_end].x, &pt[index_end - 1].x, &pt[index_end].x, cut_len2 / len); } if (index_end <= index_start) { index_start = index_end = 0; /* empty stroke */ } - if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) { + if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < 0)) { index_start = index_end = 0; /* no length left to cut */ } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index 6aa0e6c152e..e4a48925bf0 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -72,9 +72,14 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) } static bool gpencil_modify_stroke(bGPDstroke *gps, - float length, + const float length, const float overshoot_fac, - const short len_mode) + const short len_mode, + const bool use_curvature, + const int extra_point_count, + const float segment_influence, + const float max_angle, + const bool invert_curvature) { bool changed = false; if (length == 0.0f) { @@ -82,10 +87,18 @@ static bool gpencil_modify_stroke(bGPDstroke *gps, } if (length > 0.0f) { - BKE_gpencil_stroke_stretch(gps, length, overshoot_fac, len_mode); + changed = BKE_gpencil_stroke_stretch(gps, + length, + overshoot_fac, + len_mode, + use_curvature, + extra_point_count, + segment_influence, + max_angle, + invert_curvature); } else { - changed |= BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); + changed = BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); } return changed; @@ -96,12 +109,51 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke bool changed = false; const float len = (lmd->mode == GP_LENGTH_ABSOLUTE) ? 1.0f : BKE_gpencil_stroke_length(gps, true); + const int totpoints = gps->totpoints; if (len < FLT_EPSILON) { return; } - changed |= gpencil_modify_stroke(gps, len * lmd->start_fac, lmd->overshoot_fac, 1); - changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2); + /* Always do the stretching first since it might depend on points which could be deleted by the + * shrink. */ + float first_fac = lmd->start_fac; + int first_mode = 1; + float second_fac = lmd->end_fac; + int second_mode = 2; + if (first_fac < 0) { + SWAP(float, first_fac, second_fac); + SWAP(int, first_mode, second_mode); + } + + const int first_extra_point_count = ceil(first_fac * lmd->point_density); + const int second_extra_point_count = ceil(second_fac * lmd->point_density); + + changed |= gpencil_modify_stroke(gps, + len * first_fac, + lmd->overshoot_fac, + first_mode, + lmd->flag & GP_LENGTH_USE_CURVATURE, + first_extra_point_count, + lmd->segment_influence, + lmd->max_angle, + lmd->flag & GP_LENGTH_INVERT_CURVATURE); + /* HACK: The second #overshoot_fac needs to be adjusted because it is not + * done in the same stretch call, because it can have a different length. + * The adjustment needs to be stable when + * ceil(overshoot_fac*(gps->totpoints - 2)) is used in stretch and never + * produce a result highter than totpoints - 2. */ + const float second_overshoot_fac = lmd->overshoot_fac * (totpoints - 2) / + ((float)gps->totpoints - 2) * + (1.0f - 0.1f / (totpoints - 1.0f)); + changed |= gpencil_modify_stroke(gps, + len * second_fac, + second_overshoot_fac, + second_mode, + lmd->flag & GP_LENGTH_USE_CURVATURE, + second_extra_point_count, + lmd->segment_influence, + lmd->max_angle, + lmd->flag & GP_LENGTH_INVERT_CURVATURE); if (changed) { BKE_gpencil_stroke_geometry_update(gpd, gps); @@ -117,20 +169,25 @@ static void deformStroke(GpencilModifierData *md, { bGPdata *gpd = ob->data; LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md; - if (is_stroke_affected_by_modifier(ob, - lmd->layername, - lmd->material, - lmd->pass_index, - lmd->layer_pass, - 1, - gpl, - gps, - lmd->flag & GP_LENGTH_INVERT_LAYER, - lmd->flag & GP_LENGTH_INVERT_PASS, - lmd->flag & GP_LENGTH_INVERT_LAYERPASS, - lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { - applyLength(lmd, gpd, gps); + if (!is_stroke_affected_by_modifier(ob, + lmd->layername, + lmd->material, + lmd->pass_index, + lmd->layer_pass, + 1, + gpl, + gps, + lmd->flag & GP_LENGTH_INVERT_LAYER, + lmd->flag & GP_LENGTH_INVERT_PASS, + lmd->flag & GP_LENGTH_INVERT_LAYERPASS, + lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { + return; } + if ((gps->flag & GP_STROKE_CYCLIC) != 0) { + /* Don't affect cyclic strokes as they have no start/end. */ + return; + } + applyLength(lmd, gpd, gps); } static void bakeModifier(Main *UNUSED(bmain), @@ -168,10 +225,16 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); - uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); + if (RNA_enum_get(ptr, "mode") == GP_LENGTH_RELATIVE) { + uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); + uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); + } + else { + uiItemR(col, ptr, "start_length", 0, IFACE_("Start"), ICON_NONE); + uiItemR(col, ptr, "end_length", 0, IFACE_("End"), ICON_NONE); + } - uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Overshoot"), ICON_NONE); + uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Used Length"), ICON_NONE); gpencil_modifier_panel_end(layout, ptr); } @@ -181,10 +244,39 @@ static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) gpencil_modifier_masking_panel_draw(panel, true, false); } +static void curvature_header_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiItemR(layout, ptr, "use_curvature", 0, IFACE_("Curvature"), ICON_NONE); +} + +static void curvature_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiLayout *col = uiLayoutColumn(layout, false); + + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_curvature")); + + uiItemR(col, ptr, "point_density", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "segment_influence", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "max_angle", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "invert_curvature", 0, IFACE_("Invert"), ICON_NONE); +} + static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Length, panel_draw); + gpencil_modifier_subpanel_register( + region_type, "curvature", "", curvature_header_draw, curvature_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); } diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 450527c7443..3a100c00999 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -314,9 +314,13 @@ { \ .start_fac = 0.1f,\ .end_fac = 0.1f,\ - .overshoot_fac = 0.01f,\ + .overshoot_fac = 0.1f,\ .pass_index = 0,\ .material = NULL,\ + .flag = GP_LENGTH_USE_CURVATURE,\ + .point_density = 30.0f,\ + .segment_influence = 0.0f,\ + .max_angle = DEG2RAD(170.0f),\ } #define _DNA_DEFAULT_DashGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index d3429329ef6..43469fbb46b 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -493,7 +493,10 @@ typedef struct LengthGpencilModifierData { float overshoot_fac; /** Modifier mode. */ int mode; - char _pad[4]; + /* Curvature parameters. */ + float point_density; + float segment_influence; + float max_angle; } LengthGpencilModifierData; typedef enum eLengthGpencil_Flag { @@ -501,6 +504,8 @@ typedef enum eLengthGpencil_Flag { GP_LENGTH_INVERT_PASS = (1 << 1), GP_LENGTH_INVERT_LAYERPASS = (1 << 2), GP_LENGTH_INVERT_MATERIAL = (1 << 3), + GP_LENGTH_USE_CURVATURE = (1 << 4), + GP_LENGTH_INVERT_CURVATURE = (1 << 5), } eLengthGpencil_Flag; typedef enum eLengthGpencil_Type { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 4fa33424994..d5ef7cf2651 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -74,6 +74,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_DASH, "Dot Dash", "Generate dot-dash styled strokes"}, + {eGpencilModifierType_Length, + "GP_LENGTH", + ICON_MOD_LENGTH, + "Length", + "Extend or shrink strokes"}, {eGpencilModifierType_Lineart, "GP_LINEART", ICON_MOD_LINEART, @@ -120,11 +125,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_LATTICE, "Lattice", "Deform strokes using lattice"}, - {eGpencilModifierType_Length, - "GP_LENGTH", - ICON_MOD_LENGTH, - "Length", - "Extend or shrink strokes"}, {eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"}, {eGpencilModifierType_Offset, "GP_OFFSET", @@ -3278,14 +3278,29 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) prop = RNA_def_property(srna, "start_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "start_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); - RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment"); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); + RNA_def_property_ui_text( + prop, "Start Factor", "Added length to the start of each stroke relative to its length"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "end_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "end_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); - RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment"); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); + RNA_def_property_ui_text( + prop, "End Factor", "Added length to the end of each stroke relative to its length"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "start_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "start_fac"); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); + RNA_def_property_ui_text( + prop, "Start Factor", "Absolute added length to the start of each stroke"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "end_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "end_fac"); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); + RNA_def_property_ui_text(prop, "End Factor", "Absolute added length to the end of each stroke"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "overshoot_factor", PROP_FLOAT, PROP_FACTOR); @@ -3293,8 +3308,8 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text( prop, - "Overshoot Factor", - "Defines how precise must follow the stroke trajectory for the overshoot extremes"); + "Used Length", + "Defines what portion of the stroke is used for the calculation of the extension"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); @@ -3303,6 +3318,44 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Mode to define length"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_curvature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_USE_CURVATURE); + RNA_def_property_ui_text(prop, "Use Curvature", "Follow the curvature of the stroke"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_curvature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_CURVATURE); + RNA_def_property_ui_text( + prop, "Invert Curvature", "Invert the curvature of the stroke's extension"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "point_density", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.1f, 1000.0f); + RNA_def_property_ui_range(prop, 0.1f, 1000.0f, 1.0f, 1); + RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC); + RNA_def_property_ui_text( + prop, "Point Density", "Multiplied by Start/End for the total added point count"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "segment_influence", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, -2.0f, 3.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2); + RNA_def_property_ui_text(prop, + "Segment Influence", + "Factor to determine how much the length of the individual segments " + "should influence the final computed curvature. Higher factors makes " + "small segments influence the overall curvature less"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "max_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_ui_text(prop, + "Filter Angle", + "Ignore points on the stroke that deviate from their neighbors by more " + "than this angle when determining the extrapolation shape"); + RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f)); + RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(179.5f), 10.0f, 1); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "layername"); RNA_def_property_ui_text(prop, "Layer", "Layer name"); -- cgit v1.2.3