From c4e4924096d1dcfd971aafbbf82d848a774b57db Mon Sep 17 00:00:00 2001 From: YimingWu Date: Tue, 29 Mar 2022 22:46:30 +0800 Subject: GPencil: Fading for build modifier. Adds fading support for build modifier so it's not a hard cut off Reviewed By: Antonio Vazquez (antoniov), Matias Mendiola (mendio) Differential Revision: https://developer.blender.org/D14309 --- .../gpencil_modifiers/intern/MOD_gpencilbuild.c | 340 +++++++++++++++++---- 1 file changed, 287 insertions(+), 53 deletions(-) (limited to 'source/blender/gpencil_modifiers') diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index 1a69a6a8a38..18ce9a12f7b 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -6,6 +6,7 @@ */ #include +#include #include "MEM_guardedalloc.h" @@ -13,6 +14,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_sort.h" #include "BLT_translation.h" @@ -25,9 +27,12 @@ #include "DNA_screen_types.h" #include "BKE_context.h" +#include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" +#include "BKE_lib_query.h" +#include "BKE_modifier.h" #include "BKE_screen.h" #include "UI_interface.h" @@ -99,17 +104,22 @@ static void gpf_clear_all_strokes(bGPDframe *gpf) * NOTE: This won't be called if all points are present/removed */ static void reduce_stroke_points(bGPdata *gpd, + bGPDframe *gpf, bGPDstroke *gps, const int num_points, const eBuildGpencil_Transition transition) { + if (num_points == 0) { + clear_stroke(gpf, gps); + return; + } bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * num_points, __func__); MDeformVert *new_dvert = NULL; if ((gps->dvert != NULL) && (num_points > 0)) { new_dvert = MEM_callocN(sizeof(MDeformVert) * num_points, __func__); } - /* Which end should points be removed from */ + /* Which end should points be removed from. */ switch (transition) { case GP_BUILD_TRANSITION_GROW: /* Show in forward order = * Remove ungrown-points from end of stroke. */ @@ -131,7 +141,7 @@ static void reduce_stroke_points(bGPdata *gpd, } /* Hide in forward order = Remove points from start of stroke */ - case GP_BUILD_TRANSITION_FADE: { + case GP_BUILD_TRANSITION_VANISH: { /* num_points is the number of points left after reducing. * We need to know how many to remove */ @@ -167,6 +177,57 @@ static void reduce_stroke_points(bGPdata *gpd, BKE_gpencil_stroke_geometry_update(gpd, gps); } +static void fade_stroke_points(bGPDstroke *gps, + const int starting_index, + const int ending_index, + const float starting_weight, + const float ending_weight, + const int target_def_nr, + const eBuildGpencil_Transition transition, + const float thickness_strength, + const float opacity_strength) +{ + MDeformVert *dvert; + + int range = ending_index - starting_index; + if (!range) { + range = 1; + } + + /* Which end should points be removed from */ + switch (transition) { + /* Because starting_weight and ending_weight are set in correct order before calling this + * function, those three modes can use the same interpolation code. */ + case GP_BUILD_TRANSITION_GROW: + case GP_BUILD_TRANSITION_SHRINK: + case GP_BUILD_TRANSITION_VANISH: { + for (int i = starting_index; i <= ending_index; i++) { + float weight = interpf( + ending_weight, starting_weight, (float)(i - starting_index) / range); + if (target_def_nr >= 0) { + dvert = &gps->dvert[i]; + MDeformWeight *dw = BKE_defvert_ensure_index(dvert, target_def_nr); + if (dw) { + dw->weight = weight; + CLAMP(dw->weight, 0.0f, 1.0f); + } + } + if (thickness_strength > 1e-5) { + gps->points[i].pressure *= interpf(weight, 1.0f, thickness_strength); + } + if (opacity_strength > 1e-5) { + gps->points[i].strength *= interpf(weight, 1.0f, opacity_strength); + } + } + break; + } + + default: + printf("ERROR: Unknown transition %d in %s()\n", (int)transition, __func__); + break; + } +} + /* --------------------------------------------- */ /* Stroke Data Table Entry - This represents one stroke being generated */ @@ -178,11 +239,26 @@ typedef struct tStrokeBuildDetails { /* Number of points - Cache for more convenient access */ int totpoints; + + /* Distance to control object, used to sort the strokes if set. */ + float distance; } tStrokeBuildDetails; +static int cmp_stroke_build_details(const void *ps1, const void *ps2) +{ + tStrokeBuildDetails *p1 = (tStrokeBuildDetails *)ps1; + tStrokeBuildDetails *p2 = (tStrokeBuildDetails *)ps2; + return p1->distance > p2->distance ? 1 : (p1->distance == p2->distance ? 0 : -1); +} + /* Sequential and additive - Show strokes one after the other. */ -static void build_sequential( - BuildGpencilModifierData *mmd, bGPdata *gpd, bGPDframe *gpf, float fac, bool additive) +static void build_sequential(Object *ob, + BuildGpencilModifierData *mmd, + bGPdata *gpd, + bGPDframe *gpf, + const int target_def_nr, + float fac, + bool additive) { size_t tot_strokes = BLI_listbase_count(&gpf->strokes); size_t start_stroke; @@ -222,6 +298,26 @@ static void build_sequential( cell->totpoints = gps->totpoints; totpoints += cell->totpoints; + + /* Compute distance to control object if set, and build according to that order. */ + if (mmd->object) { + float sv1[3], sv2[3]; + mul_v3_m4v3(sv1, ob->obmat, &gps->points[0].x); + mul_v3_m4v3(sv2, ob->obmat, &gps->points[gps->totpoints - 1].x); + float dist_l = len_v3v3(sv1, mmd->object->loc); + float dist_r = len_v3v3(sv2, mmd->object->loc); + if (dist_r < dist_l) { + BKE_gpencil_stroke_flip(gps); + cell->distance = dist_r; + } + else { + cell->distance = dist_l; + } + } + } + + if (mmd->object) { + qsort(table, tot_strokes, sizeof(tStrokeBuildDetails), cmp_stroke_build_details); } /* 2.2) Second pass - Compute the overall indices for points */ @@ -240,6 +336,17 @@ static void build_sequential( /* 3) Determine the global indices for points that should be visible */ size_t first_visible = 0; size_t last_visible = 0; + /* Need signed numbers because the representation of fading offset would exceed the beginning and + * the end of offsets. */ + int fade_start = 0; + int fade_end = 0; + + bool fading_enabled = (mmd->flag & GP_BUILD_USE_FADING); + + float set_fade_fac = fading_enabled ? mmd->fade_fac : 0.0f; + float use_fac = interpf(1 + set_fade_fac, 0, fac); + float use_fade_fac = use_fac - set_fade_fac; + CLAMP(use_fade_fac, 0.0f, 1.0f); switch (mmd->transition) { /* Show in forward order @@ -247,7 +354,9 @@ static void build_sequential( */ case GP_BUILD_TRANSITION_GROW: first_visible = 0; /* always visible */ - last_visible = (size_t)roundf(totpoints * fac); + last_visible = (size_t)roundf(totpoints * use_fac); + fade_start = (int)roundf(totpoints * use_fade_fac); + fade_end = last_visible; break; /* Hide in reverse order @@ -255,15 +364,19 @@ static void build_sequential( */ case GP_BUILD_TRANSITION_SHRINK: first_visible = 0; /* always visible (until last point removed) */ - last_visible = (size_t)(totpoints * (1.0f - fac)); + last_visible = (size_t)(totpoints * (1.0f + set_fade_fac - use_fac)); + fade_start = (int)roundf(totpoints * (1.0f - use_fade_fac - set_fade_fac)); + fade_end = last_visible; break; /* Hide in forward order * - As fac increases, the early points start getting hidden */ - case GP_BUILD_TRANSITION_FADE: - first_visible = (size_t)(totpoints * fac); + case GP_BUILD_TRANSITION_VANISH: + first_visible = (size_t)(totpoints * use_fade_fac); last_visible = totpoints; /* i.e. visible until the end, unless first overlaps this */ + fade_start = first_visible; + fade_end = (int)roundf(totpoints * use_fac); break; } @@ -277,6 +390,30 @@ static void build_sequential( clear_stroke(gpf, cell->gps); } else { + if (fade_start != fade_end && (int)cell->start_idx < fade_end && + (int)cell->end_idx > fade_start) { + int start_index = fade_start - cell->start_idx; + int end_index = cell->totpoints + fade_end - cell->end_idx - 1; + CLAMP(start_index, 0, cell->totpoints - 1); + CLAMP(end_index, 0, cell->totpoints - 1); + float start_weight = ratiof(fade_start, fade_end, cell->start_idx + start_index); + float end_weight = ratiof(fade_start, fade_end, cell->start_idx + end_index); + if (mmd->transition != GP_BUILD_TRANSITION_VANISH) { + start_weight = 1.0f - start_weight; + end_weight = 1.0f - end_weight; + } + fade_stroke_points(cell->gps, + start_index, + end_index, + start_weight, + end_weight, + target_def_nr, + mmd->transition, + mmd->fade_thickness_strength, + mmd->fade_opacity_strength); + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gpd, cell->gps); + } /* Some proportion of stroke is visible */ if ((first_visible <= cell->start_idx) && (last_visible >= cell->end_idx)) { /* Do nothing - whole stroke is visible */ @@ -284,12 +421,12 @@ static void build_sequential( else if (first_visible > cell->start_idx) { /* Starts partway through this stroke */ int num_points = cell->end_idx - first_visible; - reduce_stroke_points(gpd, cell->gps, num_points, mmd->transition); + reduce_stroke_points(gpd, gpf, cell->gps, num_points, mmd->transition); } else { /* Ends partway through this stroke */ int num_points = last_visible - cell->start_idx; - reduce_stroke_points(gpd, cell->gps, num_points, mmd->transition); + reduce_stroke_points(gpd, gpf, cell->gps, num_points, mmd->transition); } } } @@ -304,6 +441,7 @@ static void build_sequential( static void build_concurrent(BuildGpencilModifierData *mmd, bGPdata *gpd, bGPDframe *gpf, + const int target_def_nr, float fac) { bGPDstroke *gps, *gps_next; @@ -323,6 +461,12 @@ static void build_concurrent(BuildGpencilModifierData *mmd, return; } + bool fading_enabled = (mmd->flag & GP_BUILD_USE_FADING); + float set_fade_fac = fading_enabled ? mmd->fade_fac : 0.0f; + float use_fac = interpf(1 + set_fade_fac, 0, fac); + use_fac = reverse ? use_fac - set_fade_fac : use_fac; + int fade_points = set_fade_fac * max_points; + /* 2) For each stroke, determine how it should be handled */ for (gps = gpf->strokes.first; gps; gps = gps_next) { gps_next = gps->next; @@ -338,26 +482,14 @@ static void build_concurrent(BuildGpencilModifierData *mmd, switch (mmd->time_alignment) { case GP_BUILD_TIMEALIGN_START: /* all start on frame 1 */ { - /* Build effect occurs over when fac = 0, to fac = relative_len */ - if (fac <= relative_len) { - /* Scale fac to fit relative_len */ - const float scaled_fac = fac / MAX2(relative_len, PSEUDOINVERSE_EPSILON); + /* Scale fac to fit relative_len */ + const float scaled_fac = use_fac / MAX2(relative_len, PSEUDOINVERSE_EPSILON); - if (reverse) { - num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); - } - else { - num_points = (int)roundf(scaled_fac * gps->totpoints); - } + if (reverse) { + num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); } else { - /* Build effect has ended */ - if (reverse) { - num_points = 0; - } - else { - num_points = gps->totpoints; - } + num_points = (int)roundf(scaled_fac * gps->totpoints); } break; @@ -368,24 +500,13 @@ static void build_concurrent(BuildGpencilModifierData *mmd, */ const float start_fac = 1.0f - relative_len; - if (fac >= start_fac) { - const float scaled_fac = (fac - start_fac) / MAX2(relative_len, PSEUDOINVERSE_EPSILON); + const float scaled_fac = (use_fac - start_fac) / MAX2(relative_len, PSEUDOINVERSE_EPSILON); - if (reverse) { - num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); - } - else { - num_points = (int)roundf(scaled_fac * gps->totpoints); - } + if (reverse) { + num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); } else { - /* Build effect hasn't started */ - if (reverse) { - num_points = gps->totpoints; - } - else { - num_points = 0; - } + num_points = (int)roundf(scaled_fac * gps->totpoints); } break; @@ -397,17 +518,50 @@ static void build_concurrent(BuildGpencilModifierData *mmd, /* Nothing Left - Delete the stroke */ clear_stroke(gpf, gps); } - else if (num_points < gps->totpoints) { - /* Remove some points */ - reduce_stroke_points(gpd, gps, num_points, mmd->transition); + else { + int more_points = num_points - gps->totpoints; + CLAMP(more_points, 0, fade_points + 1); + float max_weight = (float)(num_points + more_points) / fade_points; + CLAMP(max_weight, 0.0f, 1.0f); + int starting_index = mmd->transition == GP_BUILD_TRANSITION_VANISH ? + gps->totpoints - num_points - more_points : + num_points - 1 - fade_points + more_points; + int ending_index = mmd->transition == GP_BUILD_TRANSITION_VANISH ? + gps->totpoints - num_points + fade_points - more_points : + num_points - 1 + more_points; + float starting_weight = mmd->transition == GP_BUILD_TRANSITION_VANISH ? + ((float)more_points / fade_points) : + max_weight; + float ending_weight = mmd->transition == GP_BUILD_TRANSITION_VANISH ? + max_weight : + ((float)more_points / fade_points); + CLAMP(starting_index, 0, gps->totpoints - 1); + CLAMP(ending_index, 0, gps->totpoints - 1); + fade_stroke_points(gps, + starting_index, + ending_index, + starting_weight, + ending_weight, + target_def_nr, + mmd->transition, + mmd->fade_thickness_strength, + mmd->fade_opacity_strength); + if (num_points < gps->totpoints) { + /* Remove some points */ + reduce_stroke_points(gpd, gpf, gps, num_points, mmd->transition); + } } } } /* --------------------------------------------- */ -static void generate_geometry( - GpencilModifierData *md, Depsgraph *depsgraph, bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf) +static void generate_geometry(GpencilModifierData *md, + Depsgraph *depsgraph, + Object *ob, + bGPdata *gpd, + bGPDlayer *gpl, + bGPDframe *gpf) { BuildGpencilModifierData *mmd = (BuildGpencilModifierData *)md; if (mmd->mode == GP_BUILD_MODE_ADDITIVE) { @@ -450,6 +604,25 @@ static void generate_geometry( } } + int target_def_nr = -1; + if (mmd->flag & GP_BUILD_USE_FADING) { + /* If there are weight output, initialize it with a default weight of 1. */ + target_def_nr = BKE_object_defgroup_name_index(ob, mmd->target_vgname); + if (target_def_nr >= 0) { + LISTBASE_FOREACH (bGPDstroke *, fgps, &gpf->strokes) { + BKE_gpencil_dvert_ensure(fgps); + /* Assign a initial weight of 1, and only process those who needs additional fading. */ + for (int j = 0; j < fgps->totpoints; j++) { + MDeformVert *dvert = &fgps->dvert[j]; + MDeformWeight *dw = BKE_defvert_ensure_index(dvert, target_def_nr); + if (dw) { + dw->weight = 1.0f; + } + } + } + } + } + /* Early exit if outside of the frame range for this modifier * (e.g. to have one forward, and one backwards modifier) */ @@ -517,15 +690,15 @@ static void generate_geometry( /* Time management mode */ switch (mmd->mode) { case GP_BUILD_MODE_SEQUENTIAL: - build_sequential(mmd, gpd, gpf, fac, false); + build_sequential(ob, mmd, gpd, gpf, target_def_nr, fac, false); break; case GP_BUILD_MODE_CONCURRENT: - build_concurrent(mmd, gpd, gpf, fac); + build_concurrent(mmd, gpd, gpf, target_def_nr, fac); break; case GP_BUILD_MODE_ADDITIVE: - build_sequential(mmd, gpd, gpf, fac, true); + build_sequential(ob, mmd, gpd, gpf, target_def_nr, fac, true); break; default: @@ -547,7 +720,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec if (gpf == NULL) { continue; } - generate_geometry(md, depsgraph, gpd, gpl, gpf); + generate_geometry(md, depsgraph, ob, gpd, gpl, gpf); } } @@ -591,6 +764,12 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(sub, ptr, "percentage_factor", 0, "", ICON_NONE); uiItemDecoratorR(row, ptr, "percentage_factor", 0); + uiItemS(layout); + + if (ELEM(mode, GP_BUILD_MODE_SEQUENTIAL, GP_BUILD_MODE_ADDITIVE)) { + uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE); + } + /* Check for incompatible time modifier. */ Object *ob = ob_ptr.data; GpencilModifierData *md = ptr->data; @@ -624,6 +803,40 @@ static void frame_range_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "frame_end", 0, IFACE_("End"), ICON_NONE); } +static void fading_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_fading", 0, IFACE_("Fading"), ICON_NONE); +} + +static void fading_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "fade_factor", 0, IFACE_("Factor"), ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "fade_thickness_strength", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "fade_opacity_strength", 0, NULL, ICON_NONE); + + uiItemPointerR(layout, + ptr, + "target_vertex_group", + &ob_ptr, + "vertex_groups", + IFACE_("Weight Output"), + ICON_NONE); +} + static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) { gpencil_modifier_masking_panel_draw(panel, false, false); @@ -635,10 +848,31 @@ static void panelRegister(ARegionType *region_type) region_type, eGpencilModifierType_Build, panel_draw); gpencil_modifier_subpanel_register( region_type, "frame_range", "", frame_range_header_draw, frame_range_panel_draw, panel_type); + gpencil_modifier_subpanel_register( + region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "_mask", "Influence", NULL, mask_panel_draw, panel_type); } +static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + BuildGpencilModifierData *mmd = (BuildGpencilModifierData *)md; + + walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP); +} + +static void updateDepsgraph(GpencilModifierData *md, + const ModifierUpdateDepsgraphContext *ctx, + const int UNUSED(mode)) +{ + BuildGpencilModifierData *lmd = (BuildGpencilModifierData *)md; + if (lmd->object != NULL) { + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Build Modifier"); + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Build Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Build Modifier"); +} + /* ******************************************** */ GpencilModifierTypeInfo modifierType_Gpencil_Build = { @@ -658,9 +892,9 @@ GpencilModifierTypeInfo modifierType_Gpencil_Build = { /* initData */ initData, /* freeData */ NULL, /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, + /* updateDepsgraph */ updateDepsgraph, /* dependsOnTime */ dependsOnTime, - /* foreachIDLink */ NULL, + /* foreachIDLink */ foreachIDLink, /* foreachTexLink */ NULL, /* panelRegister */ panelRegister, }; -- cgit v1.2.3