Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_fill.c
| Show First 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | |||||
| /* Temporary stroke data including stroke extensions. */ | /* Temporary stroke data including stroke extensions. */ | ||||
| typedef struct tStroke { | typedef struct tStroke { | ||||
| /* Referenced layer. */ | /* Referenced layer. */ | ||||
| bGPDlayer *gpl; | bGPDlayer *gpl; | ||||
| /** Referenced frame. */ | /** Referenced frame. */ | ||||
| bGPDframe *gpf; | bGPDframe *gpf; | ||||
| /** Referenced stroke. */ | /** Referenced stroke. */ | ||||
| bGPDstroke *gps; | bGPDstroke *gps; | ||||
| /** Array of 2D points */ | |||||
| float (*points2d)[2]; | |||||
| /** Extreme Stroke A. */ | /** Extreme Stroke A. */ | ||||
| bGPDstroke *gps_ext_a; | bGPDstroke *gps_ext_a; | ||||
| /** Extreme Stroke B. */ | /** Extreme Stroke B. */ | ||||
| bGPDstroke *gps_ext_b; | bGPDstroke *gps_ext_b; | ||||
| } tStroke; | } tStroke; | ||||
| /* Temporary fill operation data `op->customdata`. */ | /* Temporary fill operation data `op->customdata`. */ | ||||
| typedef struct tGPDfill { | typedef struct tGPDfill { | ||||
| ▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | typedef struct tGPDfill { | ||||
| /** Size of stroke_array. */ | /** Size of stroke_array. */ | ||||
| int stroke_array_num; | int stroke_array_num; | ||||
| /** Temp strokes array to handle strokes and stroke extensions. */ | /** Temp strokes array to handle strokes and stroke extensions. */ | ||||
| tStroke **stroke_array; | tStroke **stroke_array; | ||||
| } tGPDfill; | } tGPDfill; | ||||
| bool skip_layer_check(short fill_layer_mode, int gpl_active_index, int gpl_index); | bool skip_layer_check(short fill_layer_mode, int gpl_active_index, int gpl_index); | ||||
| static void gpencil_draw_boundary_lines(const struct bContext *UNUSED(C), struct tGPDfill *tgpf); | static void gpencil_draw_boundary_lines(const struct bContext *UNUSED(C), struct tGPDfill *tgpf); | ||||
| static void gpencil_fill_status_indicators(struct tGPDfill *tgpf); | |||||
| /* Free temp stroke array. */ | /* Free temp stroke array. */ | ||||
| static void stroke_array_free(tGPDfill *tgpf) | static void stroke_array_free(tGPDfill *tgpf) | ||||
| { | { | ||||
| if (tgpf->stroke_array) { | if (tgpf->stroke_array) { | ||||
| for (int i = 0; i < tgpf->stroke_array_num; i++) { | for (int i = 0; i < tgpf->stroke_array_num; i++) { | ||||
| tStroke *stroke = tgpf->stroke_array[i]; | tStroke *stroke = tgpf->stroke_array[i]; | ||||
| MEM_SAFE_FREE(stroke->points2d); | |||||
| MEM_freeN(stroke); | MEM_freeN(stroke); | ||||
| } | } | ||||
| MEM_SAFE_FREE(tgpf->stroke_array); | MEM_SAFE_FREE(tgpf->stroke_array); | ||||
| } | } | ||||
| tgpf->stroke_array_num = 0; | tgpf->stroke_array_num = 0; | ||||
| } | } | ||||
| /* Delete any temporary stroke. */ | /* Delete any temporary stroke. */ | ||||
| ▲ Show 20 Lines • Show All 170 Lines • ▼ Show 20 Lines | if (skip) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_USE_PREV); | bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_USE_PREV); | ||||
| if (gpf == NULL) { | if (gpf == NULL) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| float diff_mat[4][4]; | |||||
| BKE_gpencil_layer_transform_matrix_get(tgpf->depsgraph, tgpf->ob, gpl, diff_mat); | |||||
| LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { | LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { | ||||
| /* Check if stroke can be drawn. */ | /* Check if stroke can be drawn. */ | ||||
| if ((gps->points == NULL) || (gps->totpoints < 2)) { | if ((gps->points == NULL) || (gps->totpoints < 2)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Check if the color is visible. */ | /* Check if the color is visible. */ | ||||
| MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); | MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); | ||||
| if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE)) { | if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Don't include temp strokes. */ | /* Don't include temp strokes. */ | ||||
| if ((gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG)) { | if ((gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| tStroke *stroke = MEM_callocN(sizeof(tStroke), "temp stroke data"); | tStroke *stroke = MEM_callocN(sizeof(tStroke), __func__); | ||||
| stroke->gpl = gpl; | stroke->gpl = gpl; | ||||
| stroke->gpf = gpf; | stroke->gpf = gpf; | ||||
| stroke->gps = gps; | stroke->gps = gps; | ||||
| /* Create the extension strokes only for Lines. */ | /* Create the extension strokes only for Lines. */ | ||||
| if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) { | if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) { | ||||
| /* Convert all points to 2D to speed up collision checks and avoid convert in each | |||||
| * iteration. */ | |||||
| stroke->points2d = (float(*)[2])MEM_mallocN(sizeof(*stroke->points2d) * gps->totpoints, | |||||
| "GP Stroke temp 2d points"); | |||||
| for (int i = 0; i < gps->totpoints; i++) { | |||||
| bGPDspoint *pt = &gps->points[i]; | |||||
| bGPDspoint pt2; | |||||
| gpencil_point_to_world_space(pt, diff_mat, &pt2); | |||||
| gpencil_point_to_xy_fl( | |||||
| &tgpf->gsc, gps, &pt2, &stroke->points2d[i][0], &stroke->points2d[i][1]); | |||||
| } | |||||
| /* Extend start. */ | /* Extend start. */ | ||||
| bGPDspoint *pt1 = &gps->points[0]; | bGPDspoint *pt1 = &gps->points[0]; | ||||
| stroke->gps_ext_a = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness); | stroke->gps_ext_a = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness); | ||||
| stroke->gps_ext_a->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG; | stroke->gps_ext_a->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG; | ||||
| stroke->gps_ext_a->fill_opacity_fac = FLT_MAX; | stroke->gps_ext_a->fill_opacity_fac = FLT_MAX; | ||||
| BLI_addtail(&gpf->strokes, stroke->gps_ext_a); | BLI_addtail(&gpf->strokes, stroke->gps_ext_a); | ||||
| bGPDspoint *pt = &stroke->gps_ext_a->points[0]; | bGPDspoint *pt = &stroke->gps_ext_a->points[0]; | ||||
| Show All 39 Lines | static void set_stroke_collide(bGPDstroke *gps_a, bGPDstroke *gps_b, const float connection_dist) | ||||
| gps_a->flag |= GP_STROKE_COLLIDE; | gps_a->flag |= GP_STROKE_COLLIDE; | ||||
| gps_b->flag |= GP_STROKE_COLLIDE; | gps_b->flag |= GP_STROKE_COLLIDE; | ||||
| /* It uses `fill_opacity_fac` to store distance because this variable is never | /* It uses `fill_opacity_fac` to store distance because this variable is never | ||||
| * used by this type of strokes and can be used for these | * used by this type of strokes and can be used for these | ||||
| * temp strokes without adding new variables to the bGPStroke struct. */ | * temp strokes without adding new variables to the bGPStroke struct. */ | ||||
| gps_a->fill_opacity_fac = connection_dist; | gps_a->fill_opacity_fac = connection_dist; | ||||
| gps_b->fill_opacity_fac = connection_dist; | gps_b->fill_opacity_fac = connection_dist; | ||||
| BKE_gpencil_stroke_boundingbox_calc(gps_a); | |||||
| BKE_gpencil_stroke_boundingbox_calc(gps_b); | |||||
| } | |||||
| static void gpencil_stroke_collision( | |||||
| tGPDfill *tgpf, bGPDlayer *gpl, bGPDstroke *gps_a, float a1xy[2], float a2xy[2]) | |||||
| { | |||||
| const float connection_dist = tgpf->fill_extend_fac * 0.1f; | |||||
| float diff_mat[4][4], inv_mat[4][4]; | |||||
| /* Transform matrix for original stroke.*/ | |||||
| BKE_gpencil_layer_transform_matrix_get(tgpf->depsgraph, tgpf->ob, gpl, diff_mat); | |||||
| invert_m4_m4(inv_mat, diff_mat); | |||||
| for (int idx = 0; idx < tgpf->stroke_array_num; idx++) { | |||||
| tStroke *stroke = tgpf->stroke_array[idx]; | |||||
| bGPDstroke *gps_b = stroke->gps; | |||||
| if (!extended_bbox_overlap(gps_a->boundbox_min, | |||||
| gps_a->boundbox_max, | |||||
| gps_b->boundbox_min, | |||||
| gps_b->boundbox_max, | |||||
| 1.1f)) { | |||||
| continue; | |||||
| } | |||||
| /* Loop all segments of the stroke. */ | |||||
| for (int i = 0; i < gps_b->totpoints - 1; i++) { | |||||
| /* Skip segments over same pixel. */ | |||||
| if (((int)a1xy[0] == (int)stroke->points2d[i + 1][0]) && | |||||
| ((int)a1xy[1] == (int)stroke->points2d[i + 1][1])) { | |||||
| continue; | |||||
| } | |||||
| /* Check if extensions cross. */ | |||||
| if (isect_seg_seg_v2_simple(a1xy, a2xy, stroke->points2d[i], stroke->points2d[i + 1])) { | |||||
| bGPDspoint *extreme_a = &gps_a->points[1]; | |||||
| float intersection2D[2]; | |||||
| isect_line_line_v2_point( | |||||
| a1xy, a2xy, stroke->points2d[i], stroke->points2d[i + 1], intersection2D); | |||||
| gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, intersection2D, &extreme_a->x); | |||||
| mul_m4_v3(inv_mat, &extreme_a->x); | |||||
| BKE_gpencil_stroke_boundingbox_calc(gps_a); | |||||
| gps_a->flag |= GP_STROKE_COLLIDE; | |||||
| gps_a->fill_opacity_fac = connection_dist; | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| /* Cut the extended lines if collide. */ | /* Cut the extended lines if collide. */ | ||||
| static void gpencil_cut_extensions(tGPDfill *tgpf) | static void gpencil_cut_extensions(tGPDfill *tgpf) | ||||
| { | { | ||||
| const float connection_dist = tgpf->fill_extend_fac * 0.1f; | const float connection_dist = tgpf->fill_extend_fac * 0.1f; | ||||
| const bool use_stroke_collide = (tgpf->flag & GP_BRUSH_FILL_STROKE_COLLIDE) != 0; | |||||
| bGPDlayer *gpl_prev = NULL; | bGPDlayer *gpl_prev = NULL; | ||||
| bGPDframe *gpf_prev = NULL; | bGPDframe *gpf_prev = NULL; | ||||
| float diff_mat[4][4], inv_mat[4][4]; | float diff_mat[4][4], inv_mat[4][4]; | ||||
| /* Allocate memory for all extend strokes. */ | /* Allocate memory for all extend strokes. */ | ||||
| bGPDstroke **gps_array = MEM_callocN(sizeof(bGPDstroke *) * tgpf->stroke_array_num * 2, | bGPDstroke **gps_array = MEM_callocN(sizeof(bGPDstroke *) * tgpf->stroke_array_num * 2, | ||||
| __func__); | __func__); | ||||
| Show All 34 Lines | for (int i = 0; i < tot_idx; i++) { | ||||
| bGPDstroke *gps_a = gps_array[i]; | bGPDstroke *gps_a = gps_array[i]; | ||||
| bGPDspoint pt2; | bGPDspoint pt2; | ||||
| float a1xy[2], a2xy[2]; | float a1xy[2], a2xy[2]; | ||||
| float b1xy[2], b2xy[2]; | float b1xy[2], b2xy[2]; | ||||
| /* First stroke. */ | /* First stroke. */ | ||||
| bGPDspoint *pt = &gps_a->points[0]; | bGPDspoint *pt = &gps_a->points[0]; | ||||
| gpencil_point_to_parent_space(pt, diff_mat, &pt2); | gpencil_point_to_world_space(pt, diff_mat, &pt2); | ||||
| gpencil_point_to_xy_fl(&tgpf->gsc, gps_a, &pt2, &a1xy[0], &a1xy[1]); | gpencil_point_to_xy_fl(&tgpf->gsc, gps_a, &pt2, &a1xy[0], &a1xy[1]); | ||||
| pt = &gps_a->points[1]; | pt = &gps_a->points[1]; | ||||
| gpencil_point_to_parent_space(pt, diff_mat, &pt2); | gpencil_point_to_world_space(pt, diff_mat, &pt2); | ||||
| gpencil_point_to_xy_fl(&tgpf->gsc, gps_a, &pt2, &a2xy[0], &a2xy[1]); | gpencil_point_to_xy_fl(&tgpf->gsc, gps_a, &pt2, &a2xy[0], &a2xy[1]); | ||||
| bGPDspoint *extreme_a = &gps_a->points[1]; | bGPDspoint *extreme_a = &gps_a->points[1]; | ||||
| /* Loop all other strokes and check the intersections. */ | /* Loop all other strokes and check the intersections. */ | ||||
| for (int z = 0; z < tot_idx; z++) { | for (int z = 0; z < tot_idx; z++) { | ||||
| bGPDstroke *gps_b = gps_array[z]; | bGPDstroke *gps_b = gps_array[z]; | ||||
| /* Don't check stroke with itself. */ | /* Don't check stroke with itself. */ | ||||
| if (i == z) { | if (i == z) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Don't check strokes unless the bounding boxes of the strokes | /* Don't check strokes unless the bounding boxes of the strokes | ||||
| * are close enough together that they can plausibly be connected. */ | * are close enough together that they can plausibly be connected. */ | ||||
| if (!extended_bbox_overlap(gps_a->boundbox_min, | if (!extended_bbox_overlap(gps_a->boundbox_min, | ||||
| gps_a->boundbox_max, | gps_a->boundbox_max, | ||||
| gps_b->boundbox_min, | gps_b->boundbox_min, | ||||
| gps_b->boundbox_max, | gps_b->boundbox_max, | ||||
| connection_dist)) { | 1.1f)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| pt = &gps_b->points[0]; | pt = &gps_b->points[0]; | ||||
| gpencil_point_to_parent_space(pt, diff_mat, &pt2); | gpencil_point_to_world_space(pt, diff_mat, &pt2); | ||||
| gpencil_point_to_xy_fl(&tgpf->gsc, gps_b, &pt2, &b1xy[0], &b1xy[1]); | gpencil_point_to_xy_fl(&tgpf->gsc, gps_b, &pt2, &b1xy[0], &b1xy[1]); | ||||
| pt = &gps_b->points[1]; | pt = &gps_b->points[1]; | ||||
| gpencil_point_to_parent_space(pt, diff_mat, &pt2); | gpencil_point_to_world_space(pt, diff_mat, &pt2); | ||||
| gpencil_point_to_xy_fl(&tgpf->gsc, gps_b, &pt2, &b2xy[0], &b2xy[1]); | gpencil_point_to_xy_fl(&tgpf->gsc, gps_b, &pt2, &b2xy[0], &b2xy[1]); | ||||
| bGPDspoint *extreme_b = &gps_b->points[1]; | bGPDspoint *extreme_b = &gps_b->points[1]; | ||||
| /* Check if extreme points are near. This case is when the | /* Check if extreme points are near. This case is when the | ||||
| * extended lines are co-linear or parallel and close together. */ | * extended lines are co-linear or parallel and close together. */ | ||||
| const float gap_pixsize_sq = 25.0f; | const float gap_pixsize_sq = 25.0f; | ||||
| float intersection3D[3]; | float intersection3D[3]; | ||||
| if (len_squared_v2v2(a2xy, b2xy) <= gap_pixsize_sq) { | if (len_squared_v2v2(a2xy, b2xy) <= gap_pixsize_sq) { | ||||
| gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, b2xy, intersection3D); | gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, b2xy, intersection3D); | ||||
| mul_m4_v3(inv_mat, intersection3D); | mul_m4_v3(inv_mat, intersection3D); | ||||
| copy_v3_v3(&extreme_a->x, intersection3D); | copy_v3_v3(&extreme_a->x, intersection3D); | ||||
| copy_v3_v3(&extreme_b->x, intersection3D); | copy_v3_v3(&extreme_b->x, intersection3D); | ||||
| set_stroke_collide(gps_a, gps_b, connection_dist); | set_stroke_collide(gps_a, gps_b, connection_dist); | ||||
| break; | break; | ||||
| } | } | ||||
| /* Check if extensions cross. */ | /* Check if extensions cross. */ | ||||
| if (isect_seg_seg_v2_simple(a1xy, a2xy, b1xy, b2xy)) { | if (isect_seg_seg_v2_simple(a1xy, a2xy, b1xy, b2xy)) { | ||||
| float intersection[2]; | float intersection2D[2]; | ||||
| isect_line_line_v2_point(a1xy, a2xy, b1xy, b2xy, intersection); | isect_line_line_v2_point(a1xy, a2xy, b1xy, b2xy, intersection2D); | ||||
| gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, intersection, intersection3D); | |||||
| gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, intersection2D, intersection3D); | |||||
| mul_m4_v3(inv_mat, intersection3D); | mul_m4_v3(inv_mat, intersection3D); | ||||
| copy_v3_v3(&extreme_a->x, intersection3D); | copy_v3_v3(&extreme_a->x, intersection3D); | ||||
| copy_v3_v3(&extreme_b->x, intersection3D); | copy_v3_v3(&extreme_b->x, intersection3D); | ||||
| set_stroke_collide(gps_a, gps_b, connection_dist); | set_stroke_collide(gps_a, gps_b, connection_dist); | ||||
| break; | break; | ||||
| } | } | ||||
| /* Check if extension extreme is near of the origin of any other extension. */ | /* Check if extension extreme is near of the origin of any other extension. */ | ||||
| if (len_squared_v2v2(a2xy, b1xy) <= gap_pixsize_sq) { | if (len_squared_v2v2(a2xy, b1xy) <= gap_pixsize_sq) { | ||||
| gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, b1xy, &extreme_a->x); | gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, b1xy, &extreme_a->x); | ||||
| mul_m4_v3(inv_mat, &extreme_a->x); | mul_m4_v3(inv_mat, &extreme_a->x); | ||||
| set_stroke_collide(gps_a, gps_b, connection_dist); | set_stroke_collide(gps_a, gps_b, connection_dist); | ||||
| break; | break; | ||||
| } | } | ||||
| if (len_squared_v2v2(a1xy, b2xy) <= gap_pixsize_sq) { | if (len_squared_v2v2(a1xy, b2xy) <= gap_pixsize_sq) { | ||||
| gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, a1xy, &extreme_b->x); | gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, a1xy, &extreme_b->x); | ||||
| mul_m4_v3(inv_mat, &extreme_b->x); | mul_m4_v3(inv_mat, &extreme_b->x); | ||||
| set_stroke_collide(gps_a, gps_b, connection_dist); | set_stroke_collide(gps_a, gps_b, connection_dist); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| /* Check if collide with normal strokes. */ | |||||
| if (use_stroke_collide && (gps_a->flag & GP_STROKE_COLLIDE) == 0) { | |||||
| gpencil_stroke_collision(tgpf, stroke->gpl, gps_a, a1xy, a2xy); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| MEM_SAFE_FREE(gps_array); | MEM_SAFE_FREE(gps_array); | ||||
| } | } | ||||
| /* Loop all strokes and update stroke line extensions. */ | /* Loop all strokes and update stroke line extensions. */ | ||||
| static void gpencil_update_extensions_line(tGPDfill *tgpf) | static void gpencil_update_extensions_line(tGPDfill *tgpf) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 157 Lines • ▼ Show 20 Lines | static void gpencil_update_extend(tGPDfill *tgpf) | ||||
| if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) { | if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) { | ||||
| gpencil_update_extensions_line(tgpf); | gpencil_update_extensions_line(tgpf); | ||||
| } | } | ||||
| else { | else { | ||||
| gpencil_delete_temp_stroke_extension(tgpf, false); | gpencil_delete_temp_stroke_extension(tgpf, false); | ||||
| gpencil_create_extensions_radius(tgpf); | gpencil_create_extensions_radius(tgpf); | ||||
| } | } | ||||
| gpencil_fill_status_indicators(tgpf); | |||||
| WM_event_add_notifier(tgpf->C, NC_GPENCIL | NA_EDITED, NULL); | WM_event_add_notifier(tgpf->C, NC_GPENCIL | NA_EDITED, NULL); | ||||
| } | } | ||||
| static bool gpencil_stroke_is_drawable(tGPDfill *tgpf, bGPDstroke *gps) | static bool gpencil_stroke_is_drawable(tGPDfill *tgpf, bGPDstroke *gps) | ||||
| { | { | ||||
| const bool is_line_mode = (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND); | |||||
| const bool show_help = (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) != 0; | |||||
| const bool show_extend = (tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) != 0; | |||||
| const bool use_stroke_collide = (tgpf->flag & GP_BRUSH_FILL_STROKE_COLLIDE) != 0; | |||||
| const bool is_extend_stroke = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG); | |||||
| const bool is_help_stroke = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_HELP); | |||||
| const bool stroke_collide = (gps->flag & GP_STROKE_COLLIDE) != 0; | |||||
| if (is_line_mode && is_extend_stroke && tgpf->is_render && use_stroke_collide && | |||||
| !stroke_collide) { | |||||
| return false; | |||||
| } | |||||
| if (tgpf->is_render) { | if (tgpf->is_render) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| const bool show_help = (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) != 0; | |||||
| const bool show_extend = (tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) != 0; | |||||
| const bool is_extend = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG); | |||||
| const bool is_extend_help = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_HELP); | |||||
| if ((!show_help) && (show_extend)) { | if ((!show_help) && (show_extend)) { | ||||
| if (!is_extend && !is_extend_help) { | if (!is_extend_stroke && !is_help_stroke) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| if ((show_help) && (!show_extend)) { | if ((show_help) && (!show_extend)) { | ||||
| if (is_extend || is_extend_help) { | if (is_extend_stroke || is_help_stroke) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* draw a given stroke using same thickness and color for all points */ | /* draw a given stroke using same thickness and color for all points */ | ||||
| Show All 14 Lines | static void gpencil_draw_basic_stroke(tGPDfill *tgpf, | ||||
| int totpoints = gps->totpoints; | int totpoints = gps->totpoints; | ||||
| float fpt[3]; | float fpt[3]; | ||||
| float col[4]; | float col[4]; | ||||
| const float extend_col[4] = {0.0f, 1.0f, 1.0f, 1.0f}; | const float extend_col[4] = {0.0f, 1.0f, 1.0f, 1.0f}; | ||||
| const float help_col[4] = {1.0f, 0.0f, 0.5f, 1.0f}; | const float help_col[4] = {1.0f, 0.0f, 0.5f, 1.0f}; | ||||
| const bool is_extend = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG) && | const bool is_extend = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG) && | ||||
| !(gps->flag & GP_STROKE_HELP); | !(gps->flag & GP_STROKE_HELP); | ||||
| const bool is_help = gps->flag & GP_STROKE_HELP; | const bool is_help = gps->flag & GP_STROKE_HELP; | ||||
| const bool is_line_mode = (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND); | |||||
| const bool use_stroke_collide = (tgpf->flag & GP_BRUSH_FILL_STROKE_COLLIDE) != 0; | |||||
| const bool stroke_collide = (gps->flag & GP_STROKE_COLLIDE) != 0; | |||||
| if (!gpencil_stroke_is_drawable(tgpf, gps)) { | if (!gpencil_stroke_is_drawable(tgpf, gps)) { | ||||
| return; | return; | ||||
| } | } | ||||
| if (is_help && tgpf->is_render) { | if (is_help && tgpf->is_render) { | ||||
| /* Help strokes are for display only and shouldn't render. */ | /* Help strokes are for display only and shouldn't render. */ | ||||
| return; | return; | ||||
| } | } | ||||
| else if (is_help) { | else if (is_help) { | ||||
| /* Color help strokes that won't affect fill or render separately from | /* Color help strokes that won't affect fill or render separately from | ||||
| * extended strokes, as they will affect them. */ | * extended strokes, as they will affect them. */ | ||||
| copy_v4_v4(col, help_col); | copy_v4_v4(col, help_col); | ||||
| /* If there is contact, hide the circles to avoid noise and keep the focus | /* If there is contact, hide the circles to avoid noise and keep the focus | ||||
| * in the pending gaps. */ | * in the pending gaps. */ | ||||
| col[3] = (gps->flag & GP_STROKE_TAG) ? 0.0f : 0.5f; | col[3] = (gps->flag & GP_STROKE_TAG) ? 0.0f : 0.5f; | ||||
| } | } | ||||
| else if ((is_extend) && (!tgpf->is_render)) { | else if ((is_extend) && (!tgpf->is_render)) { | ||||
| if (stroke_collide || !use_stroke_collide || !is_line_mode) { | |||||
| copy_v4_v4(col, extend_col); | copy_v4_v4(col, extend_col); | ||||
| } | } | ||||
| else { | else { | ||||
| copy_v4_v4(col, help_col); | |||||
| } | |||||
| } | |||||
| else { | |||||
| copy_v4_v4(col, ink); | copy_v4_v4(col, ink); | ||||
| } | } | ||||
| /* if cyclic needs more vertex */ | /* if cyclic needs more vertex */ | ||||
| int cyclic_add = (cyclic) ? 1 : 0; | int cyclic_add = (cyclic) ? 1 : 0; | ||||
| GPUVertFormat *format = immVertexFormat(); | GPUVertFormat *format = immVertexFormat(); | ||||
| uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); | uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); | ||||
| uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); | uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); | ||||
| ▲ Show 20 Lines • Show All 1,273 Lines • ▼ Show 20 Lines | if ((tgpf->lock_axis > GP_LOCKAXIS_VIEW) && | ||||
| ED_gpencil_drawing_reference_get(tgpf->scene, tgpf->ob, ts->gpencil_v3d_align, origin); | ED_gpencil_drawing_reference_get(tgpf->scene, tgpf->ob, ts->gpencil_v3d_align, origin); | ||||
| ED_gpencil_project_stroke_to_plane( | ED_gpencil_project_stroke_to_plane( | ||||
| tgpf->scene, tgpf->ob, tgpf->rv3d, tgpf->gpl, gps, origin, tgpf->lock_axis - 1); | tgpf->scene, tgpf->ob, tgpf->rv3d, tgpf->gpl, gps, origin, tgpf->lock_axis - 1); | ||||
| } | } | ||||
| /* if parented change position relative to parent object */ | /* if parented change position relative to parent object */ | ||||
| for (int a = 0; a < tgpf->sbuffer_used; a++) { | for (int a = 0; a < tgpf->sbuffer_used; a++) { | ||||
| pt = &gps->points[a]; | pt = &gps->points[a]; | ||||
| gpencil_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpl, pt); | gpencil_world_to_object_space_point(tgpf->depsgraph, tgpf->ob, tgpf->gpl, pt); | ||||
| } | } | ||||
| /* If camera view or view projection, reproject flat to view to avoid perspective effect. */ | /* If camera view or view projection, reproject flat to view to avoid perspective effect. */ | ||||
| if ((!is_depth) && (((align_flag & GP_PROJECT_VIEWSPACE) && is_lock_axis_view) || (is_camera))) { | if ((!is_depth) && (((align_flag & GP_PROJECT_VIEWSPACE) && is_lock_axis_view) || (is_camera))) { | ||||
| ED_gpencil_project_stroke_to_view(tgpf->C, tgpf->gpl, gps); | ED_gpencil_project_stroke_to_view(tgpf->C, tgpf->gpl, gps); | ||||
| } | } | ||||
| /* simplify stroke */ | /* simplify stroke */ | ||||
| for (int b = 0; b < tgpf->fill_simplylvl; b++) { | for (int b = 0; b < tgpf->fill_simplylvl; b++) { | ||||
| BKE_gpencil_stroke_simplify_fixed(tgpf->gpd, gps); | BKE_gpencil_stroke_simplify_fixed(tgpf->gpd, gps); | ||||
| } | } | ||||
| /* Calc geometry data. */ | /* Calc geometry data. */ | ||||
| BKE_gpencil_stroke_geometry_update(tgpf->gpd, gps); | BKE_gpencil_stroke_geometry_update(tgpf->gpd, gps); | ||||
| } | } | ||||
| /* ----------------------- */ | /* ----------------------- */ | ||||
| /* Drawing */ | /* Drawing */ | ||||
| /* Helper: Draw status message while the user is running the operator */ | /* Helper: Draw status message while the user is running the operator */ | ||||
| static void gpencil_fill_status_indicators(bContext *C) | static void gpencil_fill_status_indicators(tGPDfill *tgpf) | ||||
| { | { | ||||
| const char *status_str = TIP_( | const bool is_extend = (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND); | ||||
| "Fill: ESC/RMB cancel, LMB Fill, Shift Draw on Back, S: Switch Mode"); | const bool use_stroke_collide = (tgpf->flag & GP_BRUSH_FILL_STROKE_COLLIDE) != 0; | ||||
| ED_workspace_status_text(C, status_str); | |||||
| char status_str[UI_MAX_DRAW_STR]; | |||||
| BLI_snprintf(status_str, | |||||
| sizeof(status_str), | |||||
| TIP_("Fill: ESC/RMB cancel, LMB Fill, Shift Draw on Back, S: Switch Mode, D: " | |||||
| "Stroke Collision | %s %s (%.3f)"), | |||||
| (is_extend) ? TIP_("Extend") : TIP_("Radius"), | |||||
| (is_extend && use_stroke_collide) ? TIP_("Stroke: ON") : TIP_("Stroke: OFF"), | |||||
| tgpf->fill_extend_fac); | |||||
| ED_workspace_status_text(tgpf->C, status_str); | |||||
| } | } | ||||
| /* draw boundary lines to see fill limits */ | /* draw boundary lines to see fill limits */ | ||||
| static void gpencil_draw_boundary_lines(const bContext *UNUSED(C), tGPDfill *tgpf) | static void gpencil_draw_boundary_lines(const bContext *UNUSED(C), tGPDfill *tgpf) | ||||
| { | { | ||||
| if (!tgpf->gpd) { | if (!tgpf->gpd) { | ||||
| return; | return; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 250 Lines • ▼ Show 20 Lines | static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) | ||||
| if (help_lines) { | if (help_lines) { | ||||
| tgpf->draw_handle_3d = ED_region_draw_cb_activate( | tgpf->draw_handle_3d = ED_region_draw_cb_activate( | ||||
| tgpf->region->type, gpencil_fill_draw_3d, tgpf, REGION_DRAW_POST_VIEW); | tgpf->region->type, gpencil_fill_draw_3d, tgpf, REGION_DRAW_POST_VIEW); | ||||
| } | } | ||||
| WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_PAINT_BRUSH); | WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_PAINT_BRUSH); | ||||
| gpencil_fill_status_indicators(C); | gpencil_fill_status_indicators(tgpf); | ||||
| DEG_id_tag_update(&tgpf->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); | DEG_id_tag_update(&tgpf->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); | ||||
| /* Add a modal handler for this operator. */ | /* Add a modal handler for this operator. */ | ||||
| WM_event_add_modal_handler(C, op); | WM_event_add_modal_handler(C, op); | ||||
| return OPERATOR_RUNNING_MODAL; | return OPERATOR_RUNNING_MODAL; | ||||
| ▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | static bool gpencil_find_and_mark_empty_areas(tGPDfill *tgpf) | ||||
| const float blue_col[4] = {0.0f, 0.0f, 1.0f, 1.0f}; | const float blue_col[4] = {0.0f, 0.0f, 1.0f, 1.0f}; | ||||
| ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); | ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); | ||||
| const int maxpixel = (ibuf->x * ibuf->y) - 1; | const int maxpixel = (ibuf->x * ibuf->y) - 1; | ||||
| float rgba[4]; | float rgba[4]; | ||||
| for (int i = 0; i < maxpixel; i++) { | for (int i = 0; i < maxpixel; i++) { | ||||
| get_pixel(ibuf, i, rgba); | get_pixel(ibuf, i, rgba); | ||||
| if (rgba[3] == 0.0f) { | if (rgba[3] == 0.0f) { | ||||
| set_pixel(ibuf, i, blue_col); | set_pixel(ibuf, i, blue_col); | ||||
| BKE_image_release_ibuf(tgpf->ima, ibuf, NULL); | |||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| BKE_image_release_ibuf(tgpf->ima, ibuf, NULL); | |||||
| return false; | return false; | ||||
| } | } | ||||
| static bool gpencil_do_frame_fill(tGPDfill *tgpf, const bool is_inverted) | static bool gpencil_do_frame_fill(tGPDfill *tgpf, const bool is_inverted) | ||||
| { | { | ||||
| wmWindow *win = CTX_wm_window(tgpf->C); | wmWindow *win = CTX_wm_window(tgpf->C); | ||||
| /* render screen to temp image */ | /* render screen to temp image */ | ||||
| Show All 9 Lines | if (gpencil_render_offscreen(tgpf)) { | ||||
| /* Fill only if it never comes in contact with an edge. It is better not to fill than | /* Fill only if it never comes in contact with an edge. It is better not to fill than | ||||
| * to fill the entire area, as this is confusing for the artist. */ | * to fill the entire area, as this is confusing for the artist. */ | ||||
| if ((!border_contact) || (is_inverted)) { | if ((!border_contact) || (is_inverted)) { | ||||
| /* Invert direction if press Ctrl. */ | /* Invert direction if press Ctrl. */ | ||||
| if (is_inverted) { | if (is_inverted) { | ||||
| gpencil_invert_image(tgpf); | gpencil_invert_image(tgpf); | ||||
| while (gpencil_find_and_mark_empty_areas(tgpf)) { | while (gpencil_find_and_mark_empty_areas(tgpf)) { | ||||
| gpencil_boundaryfill_area(tgpf); | gpencil_boundaryfill_area(tgpf); | ||||
| if (FILL_DEBUG) { | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| /* Clean borders to avoid infinite loops. */ | /* Clean borders to avoid infinite loops. */ | ||||
| gpencil_set_borders(tgpf, false); | gpencil_set_borders(tgpf, false); | ||||
| WM_cursor_time(win, 50); | WM_cursor_time(win, 50); | ||||
| int totpoints_prv = 0; | int totpoints_prv = 0; | ||||
| int loop_limit = 0; | int loop_limit = 0; | ||||
| ▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| Brush *brush = tgpf->brush; | Brush *brush = tgpf->brush; | ||||
| BrushGpencilSettings *brush_settings = brush->gpencil_settings; | BrushGpencilSettings *brush_settings = brush->gpencil_settings; | ||||
| tgpf->on_back = RNA_boolean_get(op->ptr, "on_back"); | tgpf->on_back = RNA_boolean_get(op->ptr, "on_back"); | ||||
| const bool is_brush_inv = brush_settings->fill_direction == BRUSH_DIR_IN; | const bool is_brush_inv = brush_settings->fill_direction == BRUSH_DIR_IN; | ||||
| const bool is_inverted = (is_brush_inv && (event->modifier & KM_CTRL) == 0) || | const bool is_inverted = (is_brush_inv && (event->modifier & KM_CTRL) == 0) || | ||||
| (!is_brush_inv && (event->modifier & KM_CTRL) != 0); | (!is_brush_inv && (event->modifier & KM_CTRL) != 0); | ||||
| const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(tgpf->gpd); | const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(tgpf->gpd); | ||||
| const bool do_extend = (tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES); | const bool extend_lines = (tgpf->fill_extend_fac > 0.0f); | ||||
| const bool help_lines = ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) || (do_extend)); | const bool show_extend = ((tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) && !is_inverted); | ||||
| const bool help_lines = (((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) || show_extend) && | |||||
| !is_inverted); | |||||
| int estate = OPERATOR_RUNNING_MODAL; | int estate = OPERATOR_RUNNING_MODAL; | ||||
| switch (event->type) { | switch (event->type) { | ||||
| case EVT_ESCKEY: | case EVT_ESCKEY: | ||||
| case RIGHTMOUSE: | case RIGHTMOUSE: | ||||
| estate = OPERATOR_CANCELLED; | estate = OPERATOR_CANCELLED; | ||||
| break; | break; | ||||
| case LEFTMOUSE: | case LEFTMOUSE: | ||||
| ▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | case LEFTMOUSE: | ||||
| int total = BLI_ghash_len(frame_list); | int total = BLI_ghash_len(frame_list); | ||||
| int i = 1; | int i = 1; | ||||
| GHASH_ITER (gh_iter, frame_list) { | GHASH_ITER (gh_iter, frame_list) { | ||||
| /* Set active frame as current for filling. */ | /* Set active frame as current for filling. */ | ||||
| tgpf->active_cfra = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter)); | tgpf->active_cfra = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter)); | ||||
| int step = ((float)i / (float)total) * 100.0f; | int step = ((float)i / (float)total) * 100.0f; | ||||
| WM_cursor_time(win, step); | WM_cursor_time(win, step); | ||||
| if (do_extend) { | if (extend_lines) { | ||||
| gpencil_update_extend(tgpf); | gpencil_update_extend(tgpf); | ||||
| } | } | ||||
| /* Repeat loop until get something. */ | /* Repeat loop until get something. */ | ||||
| tgpf->done = false; | tgpf->done = false; | ||||
| int loop_limit = 0; | int loop_limit = 0; | ||||
| while ((!tgpf->done) && (loop_limit < 2)) { | while ((!tgpf->done) && (loop_limit < 2)) { | ||||
| WM_cursor_time(win, loop_limit + 1); | WM_cursor_time(win, loop_limit + 1); | ||||
| Show All 14 Lines | case LEFTMOUSE: | ||||
| tgpf->fill_factor = max_ff( | tgpf->fill_factor = max_ff( | ||||
| GPENCIL_MIN_FILL_FAC, | GPENCIL_MIN_FILL_FAC, | ||||
| min_ff(brush->gpencil_settings->fill_factor, GPENCIL_MAX_FILL_FAC)); | min_ff(brush->gpencil_settings->fill_factor, GPENCIL_MAX_FILL_FAC)); | ||||
| } | } | ||||
| } | } | ||||
| loop_limit++; | loop_limit++; | ||||
| } | } | ||||
| if (do_extend) { | if (extend_lines) { | ||||
| gpencil_delete_temp_stroke_extension(tgpf, true); | gpencil_delete_temp_stroke_extension(tgpf, true); | ||||
| } | } | ||||
| i++; | i++; | ||||
| } | } | ||||
| WM_cursor_modal_restore(win); | WM_cursor_modal_restore(win); | ||||
| /* Free hash table. */ | /* Free hash table. */ | ||||
| BLI_ghash_free(frame_list, NULL, NULL); | BLI_ghash_free(frame_list, NULL, NULL); | ||||
| Show All 12 Lines | case LEFTMOUSE: | ||||
| else { | else { | ||||
| estate = OPERATOR_CANCELLED; | estate = OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| estate = OPERATOR_CANCELLED; | estate = OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| else if (do_extend) { | else if (extend_lines) { | ||||
| gpencil_update_extend(tgpf); | gpencil_update_extend(tgpf); | ||||
| } | } | ||||
| tgpf->oldkey = event->type; | tgpf->oldkey = event->type; | ||||
| break; | break; | ||||
| case EVT_SKEY: | case EVT_SKEY: | ||||
| if ((do_extend) && (event->val == KM_PRESS)) { | if ((show_extend) && (event->val == KM_PRESS)) { | ||||
| /* Clean temp strokes. */ | /* Clean temp strokes. */ | ||||
| stroke_array_free(tgpf); | stroke_array_free(tgpf); | ||||
| /* Toggle mode. */ | /* Toggle mode. */ | ||||
| if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) { | if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) { | ||||
| tgpf->fill_extend_mode = GP_FILL_EMODE_RADIUS; | tgpf->fill_extend_mode = GP_FILL_EMODE_RADIUS; | ||||
| } | } | ||||
| else { | else { | ||||
| tgpf->fill_extend_mode = GP_FILL_EMODE_EXTEND; | tgpf->fill_extend_mode = GP_FILL_EMODE_EXTEND; | ||||
| } | } | ||||
| gpencil_delete_temp_stroke_extension(tgpf, true); | gpencil_delete_temp_stroke_extension(tgpf, true); | ||||
| gpencil_update_extend(tgpf); | gpencil_update_extend(tgpf); | ||||
| } | } | ||||
| break; | break; | ||||
| case EVT_DKEY: | |||||
| if ((show_extend) && (event->val == KM_PRESS)) { | |||||
| tgpf->flag ^= GP_BRUSH_FILL_STROKE_COLLIDE; | |||||
| /* Clean temp strokes. */ | |||||
| stroke_array_free(tgpf); | |||||
| gpencil_delete_temp_stroke_extension(tgpf, true); | |||||
| gpencil_update_extend(tgpf); | |||||
| } | |||||
| break; | |||||
| case EVT_PAGEUPKEY: | case EVT_PAGEUPKEY: | ||||
| case WHEELUPMOUSE: | case WHEELUPMOUSE: | ||||
| if (tgpf->oldkey == 1) { | if (tgpf->oldkey == 1) { | ||||
| tgpf->fill_extend_fac -= (event->modifier & KM_SHIFT) ? 0.01f : 0.1f; | tgpf->fill_extend_fac -= (event->modifier & KM_SHIFT) ? 0.01f : 0.1f; | ||||
| CLAMP_MIN(tgpf->fill_extend_fac, 0.0f); | CLAMP_MIN(tgpf->fill_extend_fac, 0.0f); | ||||
| gpencil_update_extend(tgpf); | gpencil_update_extend(tgpf); | ||||
| } | } | ||||
| break; | break; | ||||
| ▲ Show 20 Lines • Show All 51 Lines • Show Last 20 Lines | |||||