diff options
Diffstat (limited to 'source/blender/editors/gpencil')
22 files changed, 1311 insertions, 395 deletions
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 287dce1a509..2fd58a9cee0 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -13,9 +13,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_math_geom.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -321,7 +318,7 @@ static void annotation_stroke_convertcoords(tGPsdata *p, int mval_i[2]; round_v2i_v2fl(mval_i, mval); if (annotation_project_check(p) && - (ED_view3d_autodist_simple(p->region, mval_i, out, 0, depth))) { + ED_view3d_autodist_simple(p->region, mval_i, out, 0, depth)) { /* projecting onto 3D-Geometry * - nothing more needs to be done here, since view_autodist_simple() has already done it */ @@ -1120,7 +1117,7 @@ static void annotation_stroke_eraser_dostroke(tGPsdata *p, gpencil_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); /* Do bound-box check first. */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + if (!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ if (len_v2v2_int(mval_i, pc1) <= radius) { /* free stroke */ @@ -1162,8 +1159,8 @@ static void annotation_stroke_eraser_dostroke(tGPsdata *p, gpencil_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); /* Check that point segment of the bound-box of the eraser stroke. */ - if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + (!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1]) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { /* Check if point segment of stroke had anything to do with * eraser region (either within stroke painted, or on its lines) * - this assumes that line-width is irrelevant. @@ -2346,7 +2343,7 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev return OPERATOR_RUNNING_MODAL; } -/* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ +/* gpencil modal operator stores area, which can be removed while using it (like full-screen). */ static bool annotation_area_exists(bContext *C, ScrArea *area_test) { bScreen *screen = CTX_wm_screen(C); @@ -2520,7 +2517,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve * (Disabling RIGHTMOUSE case here results in bugs like T32647) * also making sure we have a valid event value, to not exit too early */ - if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) { + if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && ELEM(event->val, KM_PRESS, KM_RELEASE)) { /* if painting, end stroke */ if (p->status == GP_STATUS_PAINTING) { int sketch = 0; @@ -2698,7 +2695,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve } } - /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ + /* gpencil modal operator stores area, which can be removed while using it (like full-screen). */ if (0 == annotation_area_exists(C, p->area)) { estate = OPERATOR_CANCELLED; } diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c index d389f7eb5dd..2b7e09b7f05 100644 --- a/source/blender/editors/gpencil/gpencil_armature.c +++ b/source/blender/editors/gpencil/gpencil_armature.c @@ -29,6 +29,7 @@ #include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_object_deform.h" #include "BKE_report.h" @@ -328,8 +329,8 @@ static void gpencil_add_verts_to_dgroups( copy_v3_v3(tip[j], bone->arm_tail); } - mul_m4_v3(ob_arm->obmat, root[j]); - mul_m4_v3(ob_arm->obmat, tip[j]); + mul_m4_v3(ob_arm->object_to_world, root[j]); + mul_m4_v3(ob_arm->object_to_world, tip[j]); selected[j] = 1; @@ -363,7 +364,7 @@ static void gpencil_add_verts_to_dgroups( /* transform stroke points to global space */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { copy_v3_v3(verts[i], &pt->x); - mul_m4_v3(ob->obmat, verts[i]); + mul_m4_v3(ob->object_to_world, verts[i]); } /* loop groups and assign weight */ @@ -528,6 +529,7 @@ static bool gpencil_generate_weights_poll(bContext *C) return false; } + Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); bGPdata *gpd = (bGPdata *)ob->data; @@ -536,7 +538,8 @@ static bool gpencil_generate_weights_poll(bContext *C) } /* need some armature in the view layer */ - LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { + BKE_view_layer_synced_ensure(scene, view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { if (base->object->type == OB_ARMATURE) { return true; } @@ -548,6 +551,7 @@ static bool gpencil_generate_weights_poll(bContext *C) static int gpencil_generate_weights_exec(bContext *C, wmOperator *op) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = CTX_data_active_object(C); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); @@ -566,7 +570,8 @@ static int gpencil_generate_weights_exec(bContext *C, wmOperator *op) /* get armature */ const int arm_idx = RNA_enum_get(op->ptr, "armature"); if (arm_idx > 0) { - Base *base = BLI_findlink(&view_layer->object_bases, arm_idx - 1); + BKE_view_layer_synced_ensure(scene, view_layer); + Base *base = BLI_findlink(BKE_view_layer_object_bases_get(view_layer), arm_idx - 1); ob_arm = base->object; } else { @@ -607,6 +612,7 @@ static const EnumPropertyItem *gpencil_armatures_enum_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { + Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); EnumPropertyItem *item = NULL, item_tmp = {0}; int totitem = 0; @@ -623,7 +629,8 @@ static const EnumPropertyItem *gpencil_armatures_enum_itemf(bContext *C, RNA_enum_item_add(&item, &totitem, &item_tmp); i++; - LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { + BKE_view_layer_synced_ensure(scene, view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { Object *ob = base->object; if (ob->type == OB_ARMATURE) { item_tmp.identifier = item_tmp.name = ob->id.name + 2; diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.cc b/source/blender/editors/gpencil/gpencil_bake_animation.cc index e480852a9bb..4ef2cf9ffd6 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.cc +++ b/source/blender/editors/gpencil/gpencil_bake_animation.cc @@ -65,8 +65,8 @@ const EnumPropertyItem rna_gpencil_reproject_type_items[] = { }; /* Check frame_end is always > start frame! */ -static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), - struct Scene *UNUSED(scene), +static void gpencil_bake_set_frame_end(struct Main * /*main*/, + struct Scene * /*scene*/, struct PointerRNA *ptr) { int frame_start = RNA_int_get(ptr, "frame_start"); @@ -86,7 +86,7 @@ static bool gpencil_bake_grease_pencil_animation_poll(bContext *C) } /* Check if grease pencil or empty for dupli groups. */ - if ((obact == nullptr) || (!ELEM(obact->type, OB_GPENCIL, OB_EMPTY))) { + if ((obact == nullptr) || !ELEM(obact->type, OB_GPENCIL, OB_EMPTY)) { return false; } @@ -119,7 +119,7 @@ static void animdata_keyframe_list_get(ListBase *ob_list, /* Keyframe number is x value of point. */ if ((bezt->f2 & SELECT) || (!only_selected)) { /* Insert only one key for each keyframe number. */ - int key = (int)bezt->vec[1][0]; + int key = int(bezt->vec[1][0]); if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) { BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); } @@ -222,7 +222,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; ob_gpencil = ED_gpencil_add_object(C, scene->cursor.location, local_view_bits); float invmat[4][4]; - invert_m4_m4(invmat, ob_gpencil->obmat); + invert_m4_m4(invmat, ob_gpencil->object_to_world); bGPdata *gpd_dst = (bGPdata *)ob_gpencil->data; gpd_dst->draw_mode = GP_DRAWMODE_2D; @@ -243,7 +243,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op } /* Loop all frame range. */ - int oldframe = (int)DEG_get_ctime(depsgraph); + int oldframe = int(DEG_get_ctime(depsgraph)); int key = -1; /* Get list of keyframes. */ @@ -260,7 +260,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op } /* Check if frame is in the list of frames to be exported. */ - if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) { + if ((only_selected) && !BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i))) { continue; } @@ -298,6 +298,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op BLI_addtail(&gpl_dst->frames, gpf_dst); LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_dst->strokes) { + gps->runtime.gps_orig = nullptr; /* Create material of the stroke. */ Material *ma_src = BKE_object_material_get(elem->ob, gps->mat_nr + 1); bool found = false; @@ -320,14 +321,16 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op /* Update point location to new object space. */ for (int j = 0; j < gps->totpoints; j++) { bGPDspoint *pt = &gps->points[j]; - mul_m4_v3(ob_eval->obmat, &pt->x); + pt->runtime.idx_orig = 0; + pt->runtime.pt_orig = nullptr; + mul_m4_v3(ob_eval->object_to_world, &pt->x); mul_m4_v3(invmat, &pt->x); } /* Reproject stroke. */ if (project_type != GP_REPROJECT_KEEP) { ED_gpencil_stroke_reproject( - depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false); + depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false, 0.0f); } else { BKE_gpencil_stroke_geometry_update(gpd_dst, gps); @@ -366,7 +369,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op static int gpencil_bake_grease_pencil_animation_invoke(bContext *C, wmOperator *op, - const wmEvent *UNUSED(event)) + const wmEvent * /*event*/) { PropertyRNA *prop; Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index e02a82f4555..17ec33dc2bd 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -270,14 +270,13 @@ static void gpencil_timing_data_add_point(tGpTimingData *gtd, } else if (time < 0.0f) { /* This is a gap, negative value! */ - gtd->times[cur_point] = -(((float)(stroke_inittime - gtd->inittime)) + time + - gtd->offset_time); + gtd->times[cur_point] = -((float)(stroke_inittime - gtd->inittime) + time + gtd->offset_time); delta_time = -gtd->times[cur_point] - gtd->times[cur_point - 1]; gtd->gap_tot_time += delta_time; } else { - gtd->times[cur_point] = (((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time); + gtd->times[cur_point] = ((float)(stroke_inittime - gtd->inittime) + time + gtd->offset_time); delta_time = gtd->times[cur_point] - fabsf(gtd->times[cur_point - 1]); } @@ -1303,6 +1302,7 @@ static void gpencil_layer_to_curve(bContext *C, ob = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, gpl->info); cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVES_LEGACY); BKE_collection_object_add(bmain, collection, ob); + BKE_view_layer_synced_ensure(scene, view_layer); base_new = BKE_view_layer_base_find(view_layer, ob); DEG_relations_tag_update(bmain); /* added object */ @@ -1482,7 +1482,7 @@ static bool gpencil_convert_poll(bContext *C) */ return ((area && area->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_active_get(gpd)) && (gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV)) && - (gpf->strokes.first) && (!GPENCIL_ANY_EDIT_MODE(gpd))); + (gpf->strokes.first) && !GPENCIL_ANY_EDIT_MODE(gpd)); } static int gpencil_convert_layer_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 340288b2d74..0417694d7bd 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -623,6 +623,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) true, "Only Active", "Copy only active Layer, uncheck to append all layers"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } @@ -2913,8 +2914,8 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) float offset_global[3]; float offset_local[3]; - sub_v3_v3v3(offset_global, ob_active->loc, ob_iter->obmat[3]); - copy_m3_m4(bmat, ob_active->obmat); + sub_v3_v3v3(offset_global, ob_active->loc, ob_iter->object_to_world[3]); + copy_m3_m4(bmat, ob_active->object_to_world); /* Inverse transform for all selected curves in this object, * See #object_join_exec for detailed comment on why the safe version is used. */ @@ -3686,6 +3687,7 @@ void GPENCIL_OT_materials_copy_to_object(wmOperatorType *ot) true, "Only Active", "Append only active material, uncheck to append all materials"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index d53c0af2c54..f9b40a4c79b 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1618,7 +1618,7 @@ static bool gpencil_strokes_paste_poll(bContext *C) * 2) Copy buffer must at least have something (though it may be the wrong sort...). */ return (ED_gpencil_data_get_active(C) != NULL) && - (!BLI_listbase_is_empty(&gpencil_strokes_copypastebuf)); + !BLI_listbase_is_empty(&gpencil_strokes_copypastebuf); } typedef enum eGP_PasteMode { @@ -2914,7 +2914,7 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) /* return data */ copy_v3_v3(&pt->x, fpt); - gpencil_apply_parent_point(depsgraph, obact, gpl, pt); + gpencil_world_to_object_space_point(depsgraph, obact, gpl, pt); changed = true; } @@ -3015,7 +3015,7 @@ static int gpencil_snap_to_cursor(bContext *C, wmOperator *op) for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (pt->flag & GP_SPOINT_SELECT) { copy_v3_v3(&pt->x, cursor_global); - gpencil_apply_parent_point(depsgraph, obact, gpl, pt); + gpencil_world_to_object_space_point(depsgraph, obact, gpl, pt); changed = true; } @@ -3848,7 +3848,12 @@ static int gpencil_stroke_start_set_exec(bContext *C, wmOperator *op) for (int i = 0; i < gps->totpoints; i++) { pt = &gps->points[i]; if (pt->flag & GP_SPOINT_SELECT) { - BKE_gpencil_stroke_start_set(gps, i); + if (i == gps->totpoints - 1) { + BKE_gpencil_stroke_flip(gps); + } + else { + BKE_gpencil_stroke_start_set(gps, i); + } BKE_gpencil_stroke_geometry_update(gpd, gps); changed = true; break; @@ -3905,6 +3910,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) const bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const float offset = RNA_float_get(op->ptr, "offset"); /* Init snap context for geometry projection. */ SnapObjectContext *sctx = NULL; @@ -3944,7 +3950,8 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) BKE_scene_graph_update_for_newframe(depsgraph); } - ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original); + ED_gpencil_stroke_reproject( + depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original, offset); if (is_curve_edit && gps->editcurve != NULL) { BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps); @@ -3984,8 +3991,29 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void gpencil_strokes_reproject_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *row; + + const eGP_ReprojectModes type = RNA_enum_get(op->ptr, "type"); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "type", 0, NULL, ICON_NONE); + + if (type == GP_REPROJECT_SURFACE) { + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "offset", 0, NULL, ICON_NONE); + } + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "keep_original", 0, NULL, ICON_NONE); +} + void GPENCIL_OT_reproject(wmOperatorType *ot) { + PropertyRNA *prop; static const EnumPropertyItem reproject_type[] = { {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, @@ -4023,6 +4051,7 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) ot->invoke = WM_menu_invoke; ot->exec = gpencil_strokes_reproject_exec; ot->poll = gpencil_strokes_edit3d_poll; + ot->ui = gpencil_strokes_reproject_ui; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -4031,12 +4060,15 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) ot->prop = RNA_def_enum( ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", ""); - RNA_def_boolean( + prop = RNA_def_boolean( ot->srna, "keep_original", 0, "Keep Original", "Keep original strokes and create a copy before reprojecting instead of reproject them"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MOVIECLIP); + + RNA_def_float(ot->srna, "offset", 0.0f, 0.0f, 10.0f, "Surface Offset", "", 0.0f, 10.0f); } static int gpencil_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op)) @@ -4130,7 +4162,7 @@ static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Object *cam_ob = scene->camera; if (cam_ob != NULL) { - invert_m4_m4(viewmat, cam_ob->obmat); + invert_m4_m4(viewmat, cam_ob->object_to_world); } break; } @@ -4197,7 +4229,7 @@ static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op) /* Apply layer thickness change. */ gps_duplicate->thickness += gpl->line_change; /* Apply object scale to thickness. */ - gps_duplicate->thickness *= mat4_to_scale(ob->obmat); + gps_duplicate->thickness *= mat4_to_scale(ob->object_to_world); CLAMP_MIN(gps_duplicate->thickness, 1.0f); /* Stroke. */ @@ -4987,7 +5019,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if ((mode == GP_SEPARATE_LAYER) && (BLI_listbase_is_single(&gpd_src->layers))) { + if ((mode == GP_SEPARATE_LAYER) && BLI_listbase_is_single(&gpd_src->layers)) { BKE_report(op->reports, RPT_ERROR, "Cannot separate an object with one layer only"); return OPERATOR_CANCELLED; } @@ -5194,7 +5226,9 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) for (int slot = 1; slot <= ob_dst->totcol; slot++) { while (slot <= ob_dst->totcol && !BKE_object_material_slot_used(ob_dst, slot)) { ob_dst->actcol = slot; - BKE_object_material_slot_remove(bmain, ob_dst); + if (!BKE_object_material_slot_remove(bmain, ob_dst)) { + break; + } if (actcol >= slot) { actcol--; } @@ -5431,10 +5465,10 @@ static bool gpencil_test_lasso(bGPDstroke *gps, const struct GP_SelectLassoUserData *data = user_data; bGPDspoint pt2; int x0, y0; - gpencil_point_to_parent_space(pt, diff_mat, &pt2); + gpencil_point_to_world_space(pt, diff_mat, &pt2); gpencil_point_to_xy(gsc, gps, &pt2, &x0, &y0); /* test if in lasso */ - return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) && + return (!ELEM(V2D_IS_CLIPPED, x0, y0) && BLI_rcti_isect_pt(&data->rect, x0, y0) && BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX)); } @@ -5559,7 +5593,7 @@ static int gpencil_cutter_lasso_select(bContext *C, /* Select points */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - if ((gpl->flag & GP_LAYER_LOCKED) || ((gpl->flag & GP_LAYER_HIDE))) { + if ((gpl->flag & GP_LAYER_LOCKED) || (gpl->flag & GP_LAYER_HIDE)) { continue; } diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 5305c764b3a..5c88e719b8c 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -67,6 +67,7 @@ #define LEAK_HORZ 0 #define LEAK_VERT 1 +#define FILL_LEAK 3.0f #define MIN_WINDOW_SIZE 128 /* Set to 1 to debug filling internal image. By default, the value must be 0. */ @@ -78,6 +79,22 @@ enum { GP_DRAWFILLS_ONLY3D = (1 << 1), /* only draw 3d-strokes */ }; +/* Temporary stroke data including stroke extensions. */ +typedef struct tStroke { + /* Referenced layer. */ + bGPDlayer *gpl; + /** Referenced frame. */ + bGPDframe *gpf; + /** Referenced stroke. */ + bGPDstroke *gps; + /** Array of 2D points */ + float (*points2d)[2]; + /** Extreme Stroke A. */ + bGPDstroke *gps_ext_a; + /** Extreme Stroke B. */ + bGPDstroke *gps_ext_b; +} tStroke; + /* Temporary fill operation data `op->customdata`. */ typedef struct tGPDfill { bContext *C; @@ -114,7 +131,7 @@ typedef struct tGPDfill { /** For operations that require occlusion testing. */ struct ViewDepths *depths; /** flags */ - short flag; + int flag; /** avoid too fast events */ short oldkey; /** send to back stroke */ @@ -140,12 +157,25 @@ typedef struct tGPDfill { int fill_simplylvl; /** boundary limits drawing mode */ int fill_draw_mode; + /** types of extensions **/ + int fill_extend_mode; /* scaling factor */ float fill_factor; /* Frame to use. */ int active_cfra; + /** Center mouse position for extend length. */ + float mouse_center[2]; + /** Init mouse position for extend length. */ + float mouse_init[2]; + /** Last mouse position. */ + float mouse_pos[2]; + /** Use when mouse input is interpreted as spatial distance. */ + float pixel_size; + /** Initial extend vector length. */ + float initial_length; + /** number of elements currently in cache */ short sbuffer_used; /** temporary points */ @@ -157,7 +187,7 @@ typedef struct tGPDfill { Image *ima; /** temp points data */ BLI_Stack *stack; - /** handle for drawing strokes while operator is running 3d stuff */ + /** handle for drawing strokes while operator is running 3d stuff. */ void *draw_handle_3d; /* Temporary size x. */ @@ -174,11 +204,29 @@ typedef struct tGPDfill { /** Factor of extension. */ float fill_extend_fac; - + /** Size of stroke_array. */ + int stroke_array_num; + /** Temp strokes array to handle strokes and stroke extensions. */ + tStroke **stroke_array; } tGPDfill; 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_fill_status_indicators(struct tGPDfill *tgpf); + +/* Free temp stroke array. */ +static void stroke_array_free(tGPDfill *tgpf) +{ + if (tgpf->stroke_array) { + for (int i = 0; i < tgpf->stroke_array_num; i++) { + tStroke *stroke = tgpf->stroke_array[i]; + MEM_SAFE_FREE(stroke->points2d); + MEM_freeN(stroke); + } + MEM_SAFE_FREE(tgpf->stroke_array); + } + tgpf->stroke_array_num = 0; +} /* Delete any temporary stroke. */ static void gpencil_delete_temp_stroke_extension(tGPDfill *tgpf, const bool all_frames) @@ -197,7 +245,8 @@ static void gpencil_delete_temp_stroke_extension(tGPDfill *tgpf, const bool all_ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* free stroke */ - if ((gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG)) { + if ((gps->flag & GP_STROKE_NOFILL) && + (gps->flag & GP_STROKE_TAG || gps->flag & GP_STROKE_HELP)) { BLI_remlink(&gpf->strokes, gps); BKE_gpencil_free_stroke(gps); } @@ -209,6 +258,70 @@ static void gpencil_delete_temp_stroke_extension(tGPDfill *tgpf, const bool all_ } } +static bool extended_bbox_overlap( + float min1[3], float max1[3], float min2[3], float max2[3], float extend) +{ + for (int axis = 0; axis < 3; axis++) { + float intersection_min = max_ff(min1[axis], min2[axis]) - extend; + float intersection_max = min_ff(max1[axis], max2[axis]) + extend; + if (intersection_min > intersection_max) { + return false; + } + } + return true; +} + +static void add_stroke_extension(bGPDframe *gpf, bGPDstroke *gps, float p1[3], float p2[3]) +{ + bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness); + gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG; + BLI_addtail(&gpf->strokes, gps_new); + + bGPDspoint *pt = &gps_new->points[0]; + copy_v3_v3(&pt->x, p1); + pt->strength = 1.0f; + pt->pressure = 1.0f; + + pt = &gps_new->points[1]; + copy_v3_v3(&pt->x, p2); + pt->strength = 1.0f; + pt->pressure = 1.0f; +} + +static void add_endpoint_radius_help(tGPDfill *tgpf, + bGPDframe *gpf, + bGPDstroke *gps, + const float endpoint[3], + const float radius, + const bool focused) +{ + float circumference = 2.0f * M_PI * radius; + float vertex_spacing = 0.005f; + int num_vertices = min_ii(max_ii((int)ceilf(circumference / vertex_spacing), 3), 40); + + bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, num_vertices, gps->thickness); + gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_CYCLIC | GP_STROKE_HELP; + if (focused) { + gps_new->flag |= GP_STROKE_TAG; + } + BLI_addtail(&gpf->strokes, gps_new); + + for (int i = 0; i < num_vertices; i++) { + float angle = ((float)i / (float)num_vertices) * 2.0f * M_PI; + bGPDspoint *pt = &gps_new->points[i]; + pt->x = endpoint[0] + radius * cosf(angle); + pt->y = endpoint[1]; + pt->z = endpoint[2] + radius * sinf(angle); + pt->strength = 1.0f; + pt->pressure = 1.0f; + + /* Rotate to object rotation. */ + sub_v3_v3(&pt->x, endpoint); + mul_mat3_m4_v3(tgpf->ob->object_to_world, &pt->x); + add_v3_v3(&pt->x, endpoint); + } +} + static void extrapolate_points_by_length(bGPDspoint *a, bGPDspoint *b, float length, @@ -221,8 +334,42 @@ static void extrapolate_points_by_length(bGPDspoint *a, add_v3_v3v3(r_point, &b->x, ab); } -/* Loop all layers create stroke extensions. */ -static void gpencil_create_extensions(tGPDfill *tgpf) +/* Calculate the size of the array for strokes. */ +static void gpencil_strokes_array_size(tGPDfill *tgpf) +{ + bGPdata *gpd = tgpf->gpd; + Brush *brush = tgpf->brush; + BrushGpencilSettings *brush_settings = brush->gpencil_settings; + + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); + BLI_assert(gpl_active != NULL); + + const int gpl_active_index = BLI_findindex(&gpd->layers, gpl_active); + BLI_assert(gpl_active_index >= 0); + + tgpf->stroke_array_num = 0; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + if (gpl->flag & GP_LAYER_HIDE) { + continue; + } + + /* Decide if the strokes of layers are included or not depending on the layer mode. */ + const int gpl_index = BLI_findindex(&gpd->layers, gpl); + bool skip = skip_layer_check(brush_settings->fill_layer_mode, gpl_active_index, gpl_index); + if (skip) { + continue; + } + + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_USE_PREV); + if (gpf == NULL) { + continue; + } + tgpf->stroke_array_num += BLI_listbase_count(&gpf->strokes); + } +} + +/** Load all strokes to be processed by extend lines. */ +static void gpencil_load_array_strokes(tGPDfill *tgpf) { Object *ob = tgpf->ob; bGPdata *gpd = tgpf->gpd; @@ -235,6 +382,14 @@ static void gpencil_create_extensions(tGPDfill *tgpf) const int gpl_active_index = BLI_findindex(&gpd->layers, gpl_active); BLI_assert(gpl_active_index >= 0); + /* Create array of strokes. */ + gpencil_strokes_array_size(tgpf); + if (tgpf->stroke_array_num == 0) { + return; + } + + tgpf->stroke_array = MEM_callocN(sizeof(tStroke *) * tgpf->stroke_array_num, __func__); + int idx = 0; LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl->flag & GP_LAYER_HIDE) { continue; @@ -252,85 +407,489 @@ static void gpencil_create_extensions(tGPDfill *tgpf) 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) { /* Check if stroke can be drawn. */ if ((gps->points == NULL) || (gps->totpoints < 2)) { continue; } - if (gps->flag & (GP_STROKE_NOFILL | GP_STROKE_TAG)) { - continue; - } /* Check if the color is visible. */ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE)) { continue; } + /* Don't include temp strokes. */ + if ((gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG)) { + continue; + } + + tStroke *stroke = MEM_callocN(sizeof(tStroke), __func__); + stroke->gpl = gpl; + stroke->gpf = gpf; + stroke->gps = gps; + + /* Create the extension strokes only for Lines. */ + 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. */ + bGPDspoint *pt1 = &gps->points[0]; + 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->fill_opacity_fac = FLT_MAX; + BLI_addtail(&gpf->strokes, stroke->gps_ext_a); + + bGPDspoint *pt = &stroke->gps_ext_a->points[0]; + copy_v3_v3(&pt->x, &pt1->x); + pt->strength = 1.0f; + pt->pressure = 1.0f; + + pt = &stroke->gps_ext_a->points[1]; + pt->strength = 1.0f; + pt->pressure = 1.0f; + + /* Extend end. */ + pt1 = &gps->points[gps->totpoints - 1]; + stroke->gps_ext_b = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness); + stroke->gps_ext_b->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG; + stroke->gps_ext_b->fill_opacity_fac = FLT_MAX; + BLI_addtail(&gpf->strokes, stroke->gps_ext_b); + + pt = &stroke->gps_ext_b->points[0]; + copy_v3_v3(&pt->x, &pt1->x); + pt->strength = 1.0f; + pt->pressure = 1.0f; + + pt = &stroke->gps_ext_b->points[1]; + pt->strength = 1.0f; + pt->pressure = 1.0f; + } + else { + stroke->gps_ext_a = NULL; + stroke->gps_ext_b = NULL; + } + + tgpf->stroke_array[idx] = stroke; + + idx++; + } + } + tgpf->stroke_array_num = idx; +} + +static void set_stroke_collide(bGPDstroke *gps_a, bGPDstroke *gps_b, const float connection_dist) +{ + gps_a->flag |= GP_STROKE_COLLIDE; + gps_b->flag |= GP_STROKE_COLLIDE; + + /* 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 + * temp strokes without adding new variables to the bGPStroke struct. */ + gps_a->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. */ +static void gpencil_cut_extensions(tGPDfill *tgpf) +{ + 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; + bGPDframe *gpf_prev = NULL; + float diff_mat[4][4], inv_mat[4][4]; + + /* Allocate memory for all extend strokes. */ + bGPDstroke **gps_array = MEM_callocN(sizeof(bGPDstroke *) * tgpf->stroke_array_num * 2, + __func__); + + for (int idx = 0; idx < tgpf->stroke_array_num; idx++) { + tStroke *stroke = tgpf->stroke_array[idx]; + bGPDframe *gpf = stroke->gpf; + if (stroke->gpl != gpl_prev) { + BKE_gpencil_layer_transform_matrix_get(tgpf->depsgraph, tgpf->ob, stroke->gpl, diff_mat); + invert_m4_m4(inv_mat, diff_mat); + gpl_prev = stroke->gpl; + } + + if (gpf == gpf_prev) { + continue; + } + gpf_prev = gpf; + + /* Store all frame extend strokes in an array. */ + int tot_idx = 0; + for (int i = 0; i < tgpf->stroke_array_num; i++) { + tStroke *s = tgpf->stroke_array[i]; + if (s->gpf != gpf) { + continue; + } + if ((s->gps_ext_a) && ((s->gps_ext_a->flag & GP_STROKE_COLLIDE) == 0)) { + gps_array[tot_idx] = s->gps_ext_a; + tot_idx++; + } + if ((s->gps_ext_b) && ((s->gps_ext_b->flag & GP_STROKE_COLLIDE) == 0)) { + gps_array[tot_idx] = s->gps_ext_b; + tot_idx++; + } + } + + /* Compare all strokes. */ + for (int i = 0; i < tot_idx; i++) { + bGPDstroke *gps_a = gps_array[i]; + + bGPDspoint pt2; + float a1xy[2], a2xy[2]; + float b1xy[2], b2xy[2]; + + /* First stroke. */ + bGPDspoint *pt = &gps_a->points[0]; + gpencil_point_to_world_space(pt, diff_mat, &pt2); + gpencil_point_to_xy_fl(&tgpf->gsc, gps_a, &pt2, &a1xy[0], &a1xy[1]); + + pt = &gps_a->points[1]; + gpencil_point_to_world_space(pt, diff_mat, &pt2); + gpencil_point_to_xy_fl(&tgpf->gsc, gps_a, &pt2, &a2xy[0], &a2xy[1]); + bGPDspoint *extreme_a = &gps_a->points[1]; + + /* Loop all other strokes and check the intersections. */ + for (int z = 0; z < tot_idx; z++) { + bGPDstroke *gps_b = gps_array[z]; + /* Don't check stroke with itself. */ + if (i == z) { + continue; + } + + /* Don't check strokes unless the bounding boxes of the strokes + * are close enough together that they can plausibly be connected. */ + if (!extended_bbox_overlap(gps_a->boundbox_min, + gps_a->boundbox_max, + gps_b->boundbox_min, + gps_b->boundbox_max, + 1.1f)) { + continue; + } + + pt = &gps_b->points[0]; + gpencil_point_to_world_space(pt, diff_mat, &pt2); + gpencil_point_to_xy_fl(&tgpf->gsc, gps_b, &pt2, &b1xy[0], &b1xy[1]); + + pt = &gps_b->points[1]; + gpencil_point_to_world_space(pt, diff_mat, &pt2); + gpencil_point_to_xy_fl(&tgpf->gsc, gps_b, &pt2, &b2xy[0], &b2xy[1]); + bGPDspoint *extreme_b = &gps_b->points[1]; + + /* Check if extreme points are near. This case is when the + * extended lines are co-linear or parallel and close together. */ + const float gap_pixsize_sq = 25.0f; + float intersection3D[3]; + if (len_squared_v2v2(a2xy, b2xy) <= gap_pixsize_sq) { + gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, b2xy, intersection3D); + mul_m4_v3(inv_mat, intersection3D); + copy_v3_v3(&extreme_a->x, intersection3D); + copy_v3_v3(&extreme_b->x, intersection3D); + set_stroke_collide(gps_a, gps_b, connection_dist); + break; + } + /* Check if extensions cross. */ + if (isect_seg_seg_v2_simple(a1xy, a2xy, b1xy, b2xy)) { + float intersection2D[2]; + isect_line_line_v2_point(a1xy, a2xy, b1xy, b2xy, intersection2D); + + gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, intersection2D, intersection3D); + mul_m4_v3(inv_mat, intersection3D); + copy_v3_v3(&extreme_a->x, intersection3D); + copy_v3_v3(&extreme_b->x, intersection3D); + set_stroke_collide(gps_a, gps_b, connection_dist); + break; + } + /* Check if extension extreme is near of the origin of any other extension. */ + if (len_squared_v2v2(a2xy, b1xy) <= gap_pixsize_sq) { + gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, b1xy, &extreme_a->x); + mul_m4_v3(inv_mat, &extreme_a->x); + set_stroke_collide(gps_a, gps_b, connection_dist); + break; + } + if (len_squared_v2v2(a1xy, b2xy) <= gap_pixsize_sq) { + gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, a1xy, &extreme_b->x); + mul_m4_v3(inv_mat, &extreme_b->x); + set_stroke_collide(gps_a, gps_b, connection_dist); + break; + } + } - /* Extend start. */ + /* 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); +} + +/* Loop all strokes and update stroke line extensions. */ +static void gpencil_update_extensions_line(tGPDfill *tgpf) +{ + float connection_dist = tgpf->fill_extend_fac * 0.1f; + + for (int idx = 0; idx < tgpf->stroke_array_num; idx++) { + tStroke *stroke = tgpf->stroke_array[idx]; + bGPDstroke *gps = stroke->gps; + bGPDstroke *gps_a = stroke->gps_ext_a; + bGPDstroke *gps_b = stroke->gps_ext_b; + + /* Extend start. */ + if (((gps_a->flag & GP_STROKE_COLLIDE) == 0) || (gps_a->fill_opacity_fac > connection_dist)) { bGPDspoint *pt0 = &gps->points[1]; bGPDspoint *pt1 = &gps->points[0]; - bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness); - gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG; - BLI_addtail(&gpf->strokes, gps_new); - - bGPDspoint *pt = &gps_new->points[0]; - copy_v3_v3(&pt->x, &pt1->x); - pt->strength = 1.0f; - pt->pressure = 1.0f; - - pt = &gps_new->points[1]; - pt->strength = 1.0f; - pt->pressure = 1.0f; - extrapolate_points_by_length(pt0, pt1, tgpf->fill_extend_fac * 0.1f, &pt->x); - - /* Extend end. */ - pt0 = &gps->points[gps->totpoints - 2]; - pt1 = &gps->points[gps->totpoints - 1]; - gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness); - gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG; - BLI_addtail(&gpf->strokes, gps_new); - - pt = &gps_new->points[0]; - copy_v3_v3(&pt->x, &pt1->x); - pt->strength = 1.0f; - pt->pressure = 1.0f; - - pt = &gps_new->points[1]; - pt->strength = 1.0f; - pt->pressure = 1.0f; - extrapolate_points_by_length(pt0, pt1, tgpf->fill_extend_fac * 0.1f, &pt->x); + bGPDspoint *pt = &gps_a->points[1]; + extrapolate_points_by_length(pt0, pt1, connection_dist, &pt->x); + gps_a->flag &= ~GP_STROKE_COLLIDE; + } + + /* Extend end. */ + if (((gps_b->flag & GP_STROKE_COLLIDE) == 0) || (gps_b->fill_opacity_fac > connection_dist)) { + bGPDspoint *pt0 = &gps->points[gps->totpoints - 2]; + bGPDspoint *pt1 = &gps->points[gps->totpoints - 1]; + bGPDspoint *pt = &gps_b->points[1]; + extrapolate_points_by_length(pt0, pt1, connection_dist, &pt->x); + gps_b->flag &= ~GP_STROKE_COLLIDE; } } + + /* Cut overlength strokes. */ + gpencil_cut_extensions(tgpf); +} + +/* Loop all strokes and create stroke radius extensions. */ +static void gpencil_create_extensions_radius(tGPDfill *tgpf) +{ + float connection_dist = tgpf->fill_extend_fac * 0.1f; + GSet *connected_endpoints = BLI_gset_ptr_new(__func__); + + for (int idx = 0; idx < tgpf->stroke_array_num; idx++) { + tStroke *stroke = tgpf->stroke_array[idx]; + bGPDframe *gpf = stroke->gpf; + bGPDstroke *gps = stroke->gps; + + /* Find points of high curvature. */ + float tan1[3]; + float tan2[3]; + float d1; + float d2; + float total_length = 0.f; + for (int i = 1; i < gps->totpoints; i++) { + if (i > 1) { + copy_v3_v3(tan1, tan2); + d1 = d2; + } + bGPDspoint *pt1 = &gps->points[i - 1]; + bGPDspoint *pt2 = &gps->points[i]; + sub_v3_v3v3(tan2, &pt2->x, &pt1->x); + d2 = normalize_v3(tan2); + total_length += d2; + if (i > 1) { + float curvature[3]; + sub_v3_v3v3(curvature, tan2, tan1); + float k = normalize_v3(curvature); + k /= min_ff(d1, d2); + float radius = 1.f / k; + /* + * The smaller the radius of curvature, the sharper the corner. + * The thicker the line, the larger the radius of curvature it + * takes to be visually indistinguishable from an endpoint. + */ + float min_radius = gps->thickness * 0.0001f; + + if (radius < min_radius) { + /* Extend along direction of curvature. */ + bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness); + gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG; + BLI_addtail(&gpf->strokes, gps_new); + + bGPDspoint *pt = &gps_new->points[0]; + copy_v3_v3(&pt->x, &pt1->x); + pt->strength = 1.0f; + pt->pressure = 1.0f; + + pt = &gps_new->points[1]; + pt->strength = 1.0f; + pt->pressure = 1.0f; + mul_v3_fl(curvature, -connection_dist); + add_v3_v3v3(&pt->x, &pt1->x, curvature); + } + } + } + + /* Connect endpoints within a radius */ + float *stroke1_start = &gps->points[0].x; + float *stroke1_end = &gps->points[gps->totpoints - 1].x; + /* Connect the start of the stroke to its own end if the whole stroke + * isn't already so short that it's within that distance + */ + if (len_v3v3(stroke1_start, stroke1_end) < connection_dist && total_length > connection_dist) { + add_stroke_extension(gpf, gps, stroke1_start, stroke1_end); + BLI_gset_add(connected_endpoints, stroke1_start); + BLI_gset_add(connected_endpoints, stroke1_end); + } + for (bGPDstroke *gps2 = (bGPDstroke *)(((Link *)gps)->next); gps2 != NULL; + gps2 = (bGPDstroke *)(((Link *)gps2)->next)) { + /* Don't check distance to temporary extensions. */ + if ((gps2->flag & GP_STROKE_NOFILL) && (gps2->flag & GP_STROKE_TAG)) { + continue; + } + + /* Don't check endpoint distances unless the bounding boxes of the strokes + are close enough together that they can plausibly be connected. */ + if (!extended_bbox_overlap(gps->boundbox_min, + gps->boundbox_max, + gps2->boundbox_min, + gps2->boundbox_max, + connection_dist)) { + continue; + } + + float *stroke2_start = &gps2->points[0].x; + float *stroke2_end = &gps2->points[gps2->totpoints - 1].x; + if (len_v3v3(stroke1_start, stroke2_start) < connection_dist) { + add_stroke_extension(gpf, gps, stroke1_start, stroke2_start); + BLI_gset_add(connected_endpoints, stroke1_start); + BLI_gset_add(connected_endpoints, stroke2_start); + } + if (len_v3v3(stroke1_start, stroke2_end) < connection_dist) { + add_stroke_extension(gpf, gps, stroke1_start, stroke2_end); + BLI_gset_add(connected_endpoints, stroke1_start); + BLI_gset_add(connected_endpoints, stroke2_end); + } + if (len_v3v3(stroke1_end, stroke2_start) < connection_dist) { + add_stroke_extension(gpf, gps, stroke1_end, stroke2_start); + BLI_gset_add(connected_endpoints, stroke1_end); + BLI_gset_add(connected_endpoints, stroke2_start); + } + if (len_v3v3(stroke1_end, stroke2_end) < connection_dist) { + add_stroke_extension(gpf, gps, stroke1_end, stroke2_end); + BLI_gset_add(connected_endpoints, stroke1_end); + BLI_gset_add(connected_endpoints, stroke2_end); + } + } + + bool start_connected = BLI_gset_haskey(connected_endpoints, stroke1_start); + bool end_connected = BLI_gset_haskey(connected_endpoints, stroke1_end); + add_endpoint_radius_help(tgpf, gpf, gps, stroke1_start, connection_dist, start_connected); + add_endpoint_radius_help(tgpf, gpf, gps, stroke1_end, connection_dist, end_connected); + } + + BLI_gset_free(connected_endpoints, NULL); } static void gpencil_update_extend(tGPDfill *tgpf) { - gpencil_delete_temp_stroke_extension(tgpf, false); + if (tgpf->stroke_array == NULL) { + gpencil_load_array_strokes(tgpf); + } - if (tgpf->fill_extend_fac > 0.0f) { - gpencil_create_extensions(tgpf); + if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) { + gpencil_update_extensions_line(tgpf); } + else { + gpencil_delete_temp_stroke_extension(tgpf, false); + gpencil_create_extensions_radius(tgpf); + } + gpencil_fill_status_indicators(tgpf); WM_event_add_notifier(tgpf->C, NC_GPENCIL | NA_EDITED, NULL); } 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) { 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); - if ((!show_help) && (show_extend)) { - if (!is_extend) { + if (!is_extend_stroke && !is_help_stroke) { return false; } } if ((show_help) && (!show_extend)) { - if (is_extend) { + if (is_extend_stroke || is_help_stroke) { return false; } } @@ -357,14 +916,38 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf, float fpt[3]; float col[4]; const float extend_col[4] = {0.0f, 1.0f, 1.0f, 1.0f}; - const bool is_extend = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG); + 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) && + !(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)) { return; } - if ((is_extend) && (!tgpf->is_render)) { - copy_v4_v4(col, extend_col); + if (is_help && tgpf->is_render) { + /* Help strokes are for display only and shouldn't render. */ + return; + } + else if (is_help) { + /* Color help strokes that won't affect fill or render separately from + * extended strokes, as they will affect them. */ + copy_v4_v4(col, help_col); + + /* If there is contact, hide the circles to avoid noise and keep the focus + * in the pending gaps. */ + col[3] = (gps->flag & GP_STROKE_TAG) ? 0.0f : 0.5f; + } + else if ((is_extend) && (!tgpf->is_render)) { + if (stroke_collide || !use_stroke_collide || !is_line_mode) { + copy_v4_v4(col, extend_col); + } + else { + copy_v4_v4(col, help_col); + } } else { copy_v4_v4(col, ink); @@ -379,7 +962,7 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf, immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR); /* draw stroke curve */ - GPU_line_width((!is_extend) ? thickness : thickness * 2.0f); + GPU_line_width((!is_extend && !is_help) ? thickness : thickness * 2.0f); immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); const bGPDspoint *pt = points; @@ -390,7 +973,7 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf, CLAMP(alpha, 0.0f, 1.0f); col[3] = alpha <= thershold ? 0.0f : 1.0f; } - else { + else if (!is_help) { col[3] = 1.0f; } /* set point */ @@ -486,6 +1069,7 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) Brush *brush = tgpf->brush; BrushGpencilSettings *brush_settings = brush->gpencil_settings; ToolSettings *ts = tgpf->scene->toolsettings; + const bool extend_lines = (tgpf->fill_extend_fac > 0.0f); tGPDdraw tgpw; tgpw.rv3d = tgpf->rv3d; @@ -582,12 +1166,26 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) /* Normal strokes. */ if (ELEM(tgpf->fill_draw_mode, GP_FILL_DMODE_STROKE, GP_FILL_DMODE_BOTH)) { - if (gpencil_stroke_is_drawable(tgpf, gps) && ((gps->flag & GP_STROKE_TAG) == 0)) { + if (gpencil_stroke_is_drawable(tgpf, gps) && ((gps->flag & GP_STROKE_TAG) == 0) && + ((gps->flag & GP_STROKE_HELP) == 0)) { ED_gpencil_draw_fill(&tgpw); } + /* In stroke mode, still must draw the extend lines. */ + if (extend_lines && (tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE)) { + if ((gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG)) { + gpencil_draw_basic_stroke(tgpf, + gps, + tgpw.diff_mat, + gps->flag & GP_STROKE_CYCLIC, + ink, + tgpf->flag, + tgpf->fill_threshold, + 1.0f); + } + } } - /* 3D Lines with basic shapes and invisible lines */ + /* 3D Lines with basic shapes and invisible lines. */ if (ELEM(tgpf->fill_draw_mode, GP_FILL_DMODE_CONTROL, GP_FILL_DMODE_BOTH)) { gpencil_draw_basic_stroke(tgpf, gps, @@ -1655,7 +2253,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) /* if parented change position relative to parent object */ for (int a = 0; a < tgpf->sbuffer_used; 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. */ @@ -1675,10 +2273,22 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) /* ----------------------- */ /* Drawing */ /* 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_("Fill: ESC/RMB cancel, LMB Fill, Shift Draw on Back"); - ED_workspace_status_text(C, status_str); + const bool is_extend = (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND); + const bool use_stroke_collide = (tgpf->flag & GP_BRUSH_FILL_STROKE_COLLIDE) != 0; + + 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, MMB Adjust Extend, 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 */ @@ -1770,6 +2380,11 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op) tgpf->sbuffer = NULL; tgpf->depth_arr = NULL; + /* Prepare extend handling for pen. */ + tgpf->mouse_init[0] = -1.0f; + tgpf->mouse_init[1] = -1.0f; + tgpf->pixel_size = tgpf->rv3d ? ED_view3d_pixel_size(tgpf->rv3d, tgpf->ob->loc) : 1.0f; + /* save filling parameters */ Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); tgpf->brush = brush; @@ -1777,13 +2392,17 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op) tgpf->fill_threshold = brush->gpencil_settings->fill_threshold; tgpf->fill_simplylvl = brush->gpencil_settings->fill_simplylvl; tgpf->fill_draw_mode = brush->gpencil_settings->fill_draw_mode; + tgpf->fill_extend_mode = brush->gpencil_settings->fill_extend_mode; tgpf->fill_extend_fac = brush->gpencil_settings->fill_extend_fac; tgpf->fill_factor = max_ff(GPENCIL_MIN_FILL_FAC, min_ff(brush->gpencil_settings->fill_factor, GPENCIL_MAX_FILL_FAC)); - tgpf->fill_leak = (int)ceil((float)brush->gpencil_settings->fill_leak * tgpf->fill_factor); + tgpf->fill_leak = (int)ceil(FILL_LEAK * tgpf->fill_factor); int totcol = tgpf->ob->totcol; + /* Extensions array */ + tgpf->stroke_array = NULL; + /* get color info */ Material *ma = BKE_gpencil_object_material_ensure_from_active_input_brush( bmain, tgpf->ob, brush); @@ -1832,6 +2451,9 @@ static void gpencil_fill_exit(bContext *C, wmOperator *op) MEM_SAFE_FREE(tgpf->sbuffer); MEM_SAFE_FREE(tgpf->depth_arr); + /* Clean temp strokes. */ + stroke_array_free(tgpf); + /* Remove any temp stroke. */ gpencil_delete_temp_stroke_extension(tgpf, true); @@ -1839,6 +2461,7 @@ static void gpencil_fill_exit(bContext *C, wmOperator *op) if (tgpf->draw_handle_3d) { ED_region_draw_cb_exit(tgpf->region->type, tgpf->draw_handle_3d); } + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_DEFAULT); /* Remove depth buffer in cache. */ if (tgpf->depths) { @@ -1859,7 +2482,8 @@ static void gpencil_fill_exit(bContext *C, wmOperator *op) gpd2->flag |= GP_DATA_CACHE_IS_DIRTY; } - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + WM_main_add_notifier(NC_GEOM | ND_DATA, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } static void gpencil_fill_cancel(bContext *C, wmOperator *op) @@ -1928,9 +2552,8 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE tgpf = op->customdata; /* Enable custom drawing handlers to show help lines */ - const bool do_extend = (tgpf->fill_extend_fac > 0.0f); - const bool help_lines = ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) || - ((tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) && (do_extend))); + const bool do_extend = (tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES); + const bool help_lines = ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) || (do_extend)); if (help_lines) { tgpf->draw_handle_3d = ED_region_draw_cb_activate( @@ -1939,7 +2562,7 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE 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); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); @@ -2071,9 +2694,12 @@ static bool gpencil_find_and_mark_empty_areas(tGPDfill *tgpf) get_pixel(ibuf, i, rgba); if (rgba[3] == 0.0f) { set_pixel(ibuf, i, blue_col); + BKE_image_release_ibuf(tgpf->ima, ibuf, NULL); return true; } } + + BKE_image_release_ibuf(tgpf->ima, ibuf, NULL); return false; } @@ -2099,6 +2725,9 @@ static bool gpencil_do_frame_fill(tGPDfill *tgpf, const bool is_inverted) gpencil_invert_image(tgpf); while (gpencil_find_and_mark_empty_areas(tgpf)) { gpencil_boundaryfill_area(tgpf); + if (FILL_DEBUG) { + break; + } } } @@ -2179,9 +2808,10 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) const bool is_inverted = (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 do_extend = (tgpf->fill_extend_fac > 0.0f); - const bool help_lines = ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) || - ((tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) && (do_extend))); + const bool extend_lines = (tgpf->fill_extend_fac > 0.0f); + 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; switch (event->type) { @@ -2195,6 +2825,12 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) estate = OPERATOR_CANCELLED; break; } + /* if doing a extend transform with the pen, avoid false contacts of + * the pen with the tablet. */ + if (tgpf->mouse_init[0] != -1.0f) { + break; + } + copy_v2fl_v2i(tgpf->mouse_center, event->mval); /* first time the event is not enabled to show help lines. */ if ((tgpf->oldkey != -1) || (!help_lines)) { @@ -2250,7 +2886,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) int step = ((float)i / (float)total) * 100.0f; WM_cursor_time(win, step); - if (do_extend) { + if (extend_lines) { gpencil_update_extend(tgpf); } @@ -2281,7 +2917,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) loop_limit++; } - if (do_extend) { + if (extend_lines) { gpencil_delete_temp_stroke_extension(tgpf, true); } @@ -2310,11 +2946,36 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) estate = OPERATOR_CANCELLED; } } - else if (do_extend) { + else if (extend_lines) { gpencil_update_extend(tgpf); } tgpf->oldkey = event->type; break; + case EVT_SKEY: + if ((show_extend) && (event->val == KM_PRESS)) { + /* Clean temp strokes. */ + stroke_array_free(tgpf); + + /* Toggle mode. */ + if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) { + tgpf->fill_extend_mode = GP_FILL_EMODE_RADIUS; + } + else { + tgpf->fill_extend_mode = GP_FILL_EMODE_EXTEND; + } + gpencil_delete_temp_stroke_extension(tgpf, true); + gpencil_update_extend(tgpf); + } + 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 WHEELUPMOUSE: if (tgpf->oldkey == 1) { @@ -2327,10 +2988,60 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) case WHEELDOWNMOUSE: if (tgpf->oldkey == 1) { tgpf->fill_extend_fac += (event->modifier & KM_SHIFT) ? 0.01f : 0.1f; - CLAMP_MAX(tgpf->fill_extend_fac, 100.0f); + CLAMP_MAX(tgpf->fill_extend_fac, 10.0f); gpencil_update_extend(tgpf); } break; + case MIDDLEMOUSE: { + if (event->val == KM_PRESS) { + /* Consider initial offset as zero position. */ + copy_v2fl_v2i(tgpf->mouse_init, event->mval); + float mlen[2]; + sub_v2_v2v2(mlen, tgpf->mouse_init, tgpf->mouse_center); + + /* Offset the center a little to get enough space to reduce the extend moving the pen. */ + const float gap = 300.0f; + if (len_v2(mlen) < gap) { + tgpf->mouse_center[0] -= gap; + sub_v2_v2v2(mlen, tgpf->mouse_init, tgpf->mouse_center); + } + + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_EW_ARROW); + + tgpf->initial_length = len_v2(mlen); + } + if (event->val == KM_RELEASE) { + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_DEFAULT); + + tgpf->mouse_init[0] = -1.0f; + tgpf->mouse_init[1] = -1.0f; + } + /* Update cursor line. */ + WM_main_add_notifier(NC_GEOM | ND_DATA, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + break; + } + case MOUSEMOVE: { + if (tgpf->mouse_init[0] == -1.0f) { + break; + } + copy_v2fl_v2i(tgpf->mouse_pos, event->mval); + + float mlen[2]; + sub_v2_v2v2(mlen, tgpf->mouse_pos, tgpf->mouse_center); + float delta = (len_v2(mlen) - tgpf->initial_length) * tgpf->pixel_size * 0.5f; + tgpf->fill_extend_fac += delta; + CLAMP(tgpf->fill_extend_fac, 0.0f, 10.0f); + + /* Update cursor line and extend lines. */ + WM_main_add_notifier(NC_GEOM | ND_DATA, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + gpencil_update_extend(tgpf); + + break; + } default: break; } diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 4d62f834d86..4847e3eabf3 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -276,35 +276,35 @@ void gpencil_point_to_xy_fl(const GP_SpaceConversion *gsc, float *r_y); /** - * Convert point to parent space + * Convert point to world space * * \param pt: Original point - * \param diff_mat: Matrix with the difference between original parent matrix + * \param diff_mat: Matrix with the transformation * \param[out] r_pt: Pointer to new point after apply matrix */ -void gpencil_point_to_parent_space(const bGPDspoint *pt, - const float diff_mat[4][4], - bGPDspoint *r_pt); +void gpencil_point_to_world_space(const bGPDspoint *pt, + const float diff_mat[4][4], + bGPDspoint *r_pt); /** * Change points position relative to parent object */ /** * Change position relative to parent object */ -void gpencil_apply_parent(struct Depsgraph *depsgraph, - struct Object *obact, - bGPDlayer *gpl, - bGPDstroke *gps); +void gpencil_world_to_object_space(struct Depsgraph *depsgraph, + struct Object *obact, + bGPDlayer *gpl, + bGPDstroke *gps); /** * Change point position relative to parent object */ /** * Change point position relative to parent object */ -void gpencil_apply_parent_point(struct Depsgraph *depsgraph, - struct Object *obact, - bGPDlayer *gpl, - bGPDspoint *pt); +void gpencil_world_to_object_space_point(struct Depsgraph *depsgraph, + struct Object *obact, + bGPDlayer *gpl, + bGPDspoint *pt); /** * generic based on gpencil_point_to_xy_fl diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index dc63acf5136..cc25ed66b3d 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -167,20 +167,20 @@ static bool gpencil_stroke_need_flip(Depsgraph *depsgraph, /* Line from start of strokes. */ pt = &gps_from->points[0]; - gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps); + gpencil_point_to_world_space(pt, diff_mat, &pt_dummy_ps); gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v_from_start[0], &v_from_start[1]); pt = &gps_to->points[0]; - gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps); + gpencil_point_to_world_space(pt, diff_mat, &pt_dummy_ps); gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v_to_start[0], &v_to_start[1]); /* Line from end of strokes. */ pt = &gps_from->points[gps_from->totpoints - 1]; - gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps); + gpencil_point_to_world_space(pt, diff_mat, &pt_dummy_ps); gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v_from_end[0], &v_from_end[1]); pt = &gps_to->points[gps_to->totpoints - 1]; - gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps); + gpencil_point_to_world_space(pt, diff_mat, &pt_dummy_ps); gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v_to_end[0], &v_to_end[1]); const bool isect_lines = (isect_seg_seg_v2(v_from_start, v_to_start, v_from_end, v_to_end) == @@ -690,7 +690,7 @@ static bool gpencil_interpolate_set_init_values(bContext *C, wmOperator *op, tGP tgpi->flag, (RNA_enum_get(op->ptr, "layers") == 1), GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS); SET_FLAG_FROM_TEST( tgpi->flag, - (GPENCIL_EDIT_MODE(tgpi->gpd) && (RNA_boolean_get(op->ptr, "interpolate_selected_only"))), + (GPENCIL_EDIT_MODE(tgpi->gpd) && RNA_boolean_get(op->ptr, "interpolate_selected_only")), GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED); tgpi->flipmode = RNA_enum_get(op->ptr, "flip"); diff --git a/source/blender/editors/gpencil/gpencil_mesh.cc b/source/blender/editors/gpencil/gpencil_mesh.cc index b27e1c75746..2b01fce5bf8 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.cc +++ b/source/blender/editors/gpencil/gpencil_mesh.cc @@ -43,8 +43,8 @@ #include "gpencil_intern.h" /* Check frame_end is always > start frame! */ -static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), - struct Scene *UNUSED(scene), +static void gpencil_bake_set_frame_end(struct Main * /*main*/, + struct Scene * /*scene*/, struct PointerRNA *ptr) { int frame_start = RNA_int_get(ptr, "frame_start"); @@ -91,7 +91,7 @@ static void animdata_keyframe_list_get(ListBase *ob_list, /* Keyframe number is x value of point. */ if ((bezt->f2 & SELECT) || (!only_selected)) { /* Insert only one key for each keyframe number. */ - int key = (int)bezt->vec[1][0]; + int key = int(bezt->vec[1][0]); if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) { BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); } @@ -213,7 +213,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) bool newob = false; if (target == GP_TARGET_OB_SELECTED) { - ob_gpencil = BKE_view_layer_non_active_selected_object(CTX_data_view_layer(C), v3d); + ob_gpencil = BKE_view_layer_non_active_selected_object(scene, CTX_data_view_layer(C), v3d); if (ob_gpencil != nullptr) { if (ob_gpencil->type != OB_GPENCIL) { BKE_report(op->reports, RPT_WARNING, "Target object not a grease pencil, ignoring!"); @@ -261,7 +261,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) } /* Loop all frame range. */ - int oldframe = (int)DEG_get_ctime(depsgraph); + int oldframe = int(DEG_get_ctime(depsgraph)); int key = -1; /* Get list of keyframes. */ @@ -278,7 +278,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) } /* Check if frame is in the list of frames to be exported. */ - if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) { + if ((only_selected) && !BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i))) { continue; } @@ -299,7 +299,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) angle, thickness, offset, - ob_eval->obmat, + ob_eval->object_to_world, frame_offset, use_seams, use_faces, @@ -315,7 +315,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if ((gps->flag & GP_STROKE_TAG) == 0) { ED_gpencil_stroke_reproject( - depsgraph, &gsc, sctx, gpl, gpf, gps, project_type, false); + depsgraph, &gsc, sctx, gpl, gpf, gps, project_type, false, 0.0f); gps->flag |= GP_STROKE_TAG; } } @@ -380,7 +380,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) static int gpencil_bake_mesh_animation_invoke(bContext *C, wmOperator *op, - const wmEvent *UNUSED(event)) + const wmEvent * /*event*/) { /* Show popup dialog to allow editing. */ /* FIXME: hard-coded dimensions here are just arbitrary. */ diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c index 8119646137c..60b58b6f513 100644 --- a/source/blender/editors/gpencil/gpencil_ops_versioning.c +++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c @@ -92,7 +92,7 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op) if ((!is_annotation) && (view_layer != NULL)) { Object *ob; ob = BKE_object_add_for_data( - bmain, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false); + bmain, scene, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false); zero_v3(ob->loc); DEG_relations_tag_update(bmain); /* added object */ @@ -122,7 +122,7 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - if ((gps->colorname[0] != '\0') && (STREQ(gps->colorname, palcolor->info))) { + if ((gps->colorname[0] != '\0') && STREQ(gps->colorname, palcolor->info)) { gps->mat_nr = ob->totcol - 1; gps->colorname[0] = '\0'; /* weights array */ @@ -160,7 +160,7 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op) gpl->tintcolor[3] = 0.0f; LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - if ((gps->colorname[0] != '\0') && (STREQ(gps->colorname, palcolor->info))) { + if ((gps->colorname[0] != '\0') && STREQ(gps->colorname, palcolor->info)) { /* copy color settings */ copy_v4_v4(gpl->color, palcolor->color); } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 8e33a029229..d2ed1720bf8 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -434,8 +434,7 @@ static void gpencil_stroke_convertcoords(tGPsdata *p, rmval[1] = mval[1] - 0.5f; round_v2i_v2fl(mval_i, rmval); - if (gpencil_project_check(p) && - (ED_view3d_autodist_simple(p->region, mval_i, out, 0, depth))) { + if (gpencil_project_check(p) && ED_view3d_autodist_simple(p->region, mval_i, out, 0, depth)) { /* projecting onto 3D-Geometry * - nothing more needs to be done here, since view_autodist_simple() has already done it */ @@ -937,7 +936,7 @@ static bGPDstroke *gpencil_stroke_to_outline(tGPsdata *p, bGPDstroke *gps) /* Apply layer thickness change. */ gps_duplicate->thickness += gpl->line_change; /* Apply object scale to thickness. */ - gps_duplicate->thickness *= mat4_to_scale(p->ob->obmat); + gps_duplicate->thickness *= mat4_to_scale(p->ob->object_to_world); CLAMP_MIN(gps_duplicate->thickness, 1.0f); /* Stroke. */ @@ -1134,11 +1133,9 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) /* reproject to plane (only in 3d space) */ gpencil_reproject_toplane(p, gps); - pt = gps->points; - for (int i = 0; i < gps->totpoints; i++, pt++) { - /* if parented change position relative to parent object */ - gpencil_apply_parent_point(depsgraph, obact, gpl, pt); - } + + /* Change position relative to object. */ + gpencil_world_to_object_space(depsgraph, obact, gpl, gps); /* If camera view or view projection, reproject flat to view to avoid perspective effect. */ if ((!is_depth) && @@ -1301,8 +1298,8 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) /* reproject to plane (only in 3d space) */ gpencil_reproject_toplane(p, gps); - /* change position relative to parent object */ - gpencil_apply_parent(depsgraph, obact, gpl, gps); + /* Change position relative to parent object. */ + gpencil_world_to_object_space(depsgraph, obact, gpl, gps); /* 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)) { ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); @@ -1387,7 +1384,8 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) } gpencil_update_cache(p->gpd); - BKE_gpencil_tag_full_update(p->gpd, gpl, p->gpf, NULL); + BKE_gpencil_tag_full_update( + p->gpd, gpl, GPENCIL_MULTIEDIT_SESSIONS_ON(p->gpd) ? NULL : p->gpf, NULL); } /* --- 'Eraser' for 'Paint' Tool ------ */ @@ -1551,10 +1549,10 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, /* only process if it hasn't been masked out... */ if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { bGPDspoint pt_temp; - gpencil_point_to_parent_space(gps->points, p->diff_mat, &pt_temp); + gpencil_point_to_world_space(gps->points, p->diff_mat, &pt_temp); gpencil_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); /* Do bound-box check first. */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + if (!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ if (len_v2v2_int(mval_i, pc1) <= radius) { /* free stroke */ @@ -1575,11 +1573,11 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, /* get points to work with */ pt1 = gps->points + i; bGPDspoint npt; - gpencil_point_to_parent_space(pt1, p->diff_mat, &npt); + gpencil_point_to_world_space(pt1, p->diff_mat, &npt); gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); /* Do bound-box check first. */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + if (!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ if (len_v2v2_int(mval_i, pc1) <= radius) { /* free stroke */ @@ -1633,14 +1631,14 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, } bGPDspoint npt; - gpencil_point_to_parent_space(pt1, p->diff_mat, &npt); + gpencil_point_to_world_space(pt1, p->diff_mat, &npt); gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); - gpencil_point_to_parent_space(pt2, p->diff_mat, &npt); + gpencil_point_to_world_space(pt2, p->diff_mat, &npt); gpencil_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); if (pt0) { - gpencil_point_to_parent_space(pt0, p->diff_mat, &npt); + gpencil_point_to_world_space(pt0, p->diff_mat, &npt); gpencil_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]); } else { @@ -1649,9 +1647,9 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, } /* Check that point segment of the bound-box of the eraser stroke. */ - if (((!ELEM(V2D_IS_CLIPPED, pc0[0], pc0[1])) && BLI_rcti_isect_pt(rect, pc0[0], pc0[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + if ((!ELEM(V2D_IS_CLIPPED, pc0[0], pc0[1]) && BLI_rcti_isect_pt(rect, pc0[0], pc0[1])) || + (!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + (!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1]) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { /* Check if point segment of stroke had anything to do with * eraser region (either within stroke painted, or on its lines) * - this assumes that line-width is irrelevant. @@ -1743,14 +1741,14 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, /* 2) Tag any point with overly low influence for removal in the next pass */ if ((inf1 > 0.0f) && - (((pt1->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || - (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)))) { + ((pt1->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || + (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD))) { pt1->flag |= GP_SPOINT_TAG; do_cull = true; } if ((inf1 > 2.0f) && - (((pt2->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || - (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)))) { + ((pt2->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || + (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD))) { pt2->flag |= GP_SPOINT_TAG; do_cull = true; } @@ -3331,7 +3329,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event return OPERATOR_RUNNING_MODAL; } -/* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ +/* gpencil modal operator stores area, which can be removed while using it (like full-screen). */ static bool gpencil_area_exists(bContext *C, ScrArea *area_test) { bScreen *screen = CTX_wm_screen(C); @@ -3716,7 +3714,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * is essential for ensuring that they can quickly return to that view. */ } - else if ((!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP))) { + else if (!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP)) { gpencil_guide_event_handling(C, op, event, p); estate = OPERATOR_RUNNING_MODAL; } @@ -3738,7 +3736,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * (Disabling RIGHTMOUSE case here results in bugs like T32647) * also making sure we have a valid event value, to not exit too early */ - if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) { + if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && ELEM(event->val, KM_PRESS, KM_RELEASE)) { /* if painting, end stroke */ if (p->status == GP_STATUS_PAINTING) { p->status = GP_STATUS_DONE; @@ -3888,7 +3886,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } } - /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ + /* gpencil modal operator stores area, which can be removed while using it (like full-screen). */ if (0 == gpencil_area_exists(C, p->area)) { estate = OPERATOR_CANCELLED; } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 4a4fffc9638..b693d4ae185 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -677,8 +677,8 @@ static void gpencil_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D) center[0] = tgpi->start[0] + ((tgpi->end[0] - tgpi->start[0]) / 2.0f); center[1] = tgpi->start[1] + ((tgpi->end[1] - tgpi->start[1]) / 2.0f); - radius[0] = fabsf(((tgpi->end[0] - tgpi->start[0]) / 2.0f)); - radius[1] = fabsf(((tgpi->end[1] - tgpi->start[1]) / 2.0f)); + radius[0] = fabsf((tgpi->end[0] - tgpi->start[0]) / 2.0f); + radius[1] = fabsf((tgpi->end[1] - tgpi->start[1]) / 2.0f); for (int i = tgpi->tot_stored_edges; i < totpoints; i++) { tGPspoint *p2d = &points2D[i]; @@ -1073,7 +1073,7 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) /* if parented change position relative to parent object */ for (int i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; - gpencil_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpl, pt); + gpencil_world_to_object_space_point(tgpi->depsgraph, tgpi->ob, tgpi->gpl, pt); } /* If camera view or view projection, reproject flat to view to avoid perspective effect. */ @@ -1515,7 +1515,7 @@ static void gpencil_primitive_edit_event_handling( break; } case EVT_MKEY: { - if ((event->val == KM_PRESS) && (tgpi->curve) && (ELEM(tgpi->orign_type, GP_STROKE_ARC))) { + if ((event->val == KM_PRESS) && (tgpi->curve) && ELEM(tgpi->orign_type, GP_STROKE_ARC)) { tgpi->flip ^= 1; gpencil_primitive_update_cps(tgpi); gpencil_primitive_update(C, op, tgpi); @@ -1703,7 +1703,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } case EVT_PADPLUSKEY: case WHEELUPMOUSE: { - if ((event->val != KM_RELEASE)) { + if (event->val != KM_RELEASE) { tgpi->tot_edges = tgpi->tot_edges + 1; CLAMP(tgpi->tot_edges, tgpi->type == GP_STROKE_BOX ? 1 : MIN_EDGES, MAX_EDGES); RNA_int_set(op->ptr, "edges", tgpi->tot_edges); @@ -1713,7 +1713,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } case EVT_PADMINUS: case WHEELDOWNMOUSE: { - if ((event->val != KM_RELEASE)) { + if (event->val != KM_RELEASE) { tgpi->tot_edges = tgpi->tot_edges - 1; CLAMP(tgpi->tot_edges, tgpi->type == GP_STROKE_BOX ? 1 : MIN_EDGES, MAX_EDGES); RNA_int_set(op->ptr, "edges", tgpi->tot_edges); @@ -1723,7 +1723,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } case EVT_FKEY: /* brush thickness/ brush strength */ { - if ((event->val == KM_PRESS)) { + if (event->val == KM_PRESS) { if (event->modifier & KM_SHIFT) { tgpi->prev_flag = tgpi->flag; tgpi->flag = IN_BRUSH_STRENGTH; @@ -1808,14 +1808,14 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } } else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && - (!ELEM(tgpi->type, GP_STROKE_POLYLINE))) { + !ELEM(tgpi->type, GP_STROKE_POLYLINE)) { /* set control points and enter edit mode */ tgpi->flag = IN_CURVE_EDIT; gpencil_primitive_update_cps(tgpi); gpencil_primitive_update(C, op, tgpi); } else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && - (!ELEM(tgpi->type, GP_STROKE_CURVE, GP_STROKE_POLYLINE))) { + !ELEM(tgpi->type, GP_STROKE_CURVE, GP_STROKE_POLYLINE)) { /* stop drawing primitive */ tgpi->flag = IDLE; gpencil_primitive_interaction_end(C, op, win, tgpi); @@ -1823,7 +1823,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e return OPERATOR_FINISHED; } else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && - (ELEM(tgpi->type, GP_STROKE_POLYLINE))) { + ELEM(tgpi->type, GP_STROKE_POLYLINE)) { /* set control points and enter edit mode */ tgpi->flag = IN_POLYLINE; gpencil_primitive_update(C, op, tgpi); @@ -1869,7 +1869,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } case EVT_PADPLUSKEY: case WHEELUPMOUSE: { - if ((event->val != KM_RELEASE)) { + if (event->val != KM_RELEASE) { tgpi->tot_edges = tgpi->tot_edges + 1; CLAMP(tgpi->tot_edges, tgpi->type == GP_STROKE_BOX ? 1 : MIN_EDGES, MAX_EDGES); RNA_int_set(op->ptr, "edges", tgpi->tot_edges); @@ -1881,7 +1881,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } case EVT_PADMINUS: case WHEELDOWNMOUSE: { - if ((event->val != KM_RELEASE)) { + if (event->val != KM_RELEASE) { tgpi->tot_edges = tgpi->tot_edges - 1; CLAMP(tgpi->tot_edges, tgpi->type == GP_STROKE_BOX ? 1 : MIN_EDGES, MAX_EDGES); RNA_int_set(op->ptr, "edges", tgpi->tot_edges); @@ -1893,7 +1893,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } case EVT_GKEY: /* grab mode */ { - if ((event->val == KM_PRESS)) { + if (event->val == KM_PRESS) { tgpi->flag = IN_MOVE; WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } @@ -1901,7 +1901,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } case EVT_FKEY: /* brush thickness/ brush strength */ { - if ((event->val == KM_PRESS)) { + if (event->val == KM_PRESS) { if (event->modifier & KM_SHIFT) { tgpi->prev_flag = tgpi->flag; tgpi->flag = IN_BRUSH_STRENGTH; diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index e27cd255217..ab3edfdd4fa 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -68,6 +68,8 @@ #include "gpencil_intern.h" +#define SEARCH_RADIUS_PIXEL 20 + /* ************************************************ */ /* General Brush Editing Context */ @@ -78,6 +80,7 @@ typedef struct tGP_BrushEditData { Main *bmain; Scene *scene; Object *object; + Object *ob_eval; ScrArea *area; ARegion *region; @@ -516,7 +519,7 @@ static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso) float mval_f[2]; - /* convert from 2D screenspace to 3D... */ + /* Convert from 2D screen-space to 3D. */ mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]); mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]); @@ -544,8 +547,10 @@ static void gpencil_brush_grab_apply_cached(tGP_BrushEditData *gso, return; } - float inverse_diff_mat[4][4]; - invert_m4_m4(inverse_diff_mat, diff_mat); + float matrix[4][4], inverse_diff_mat[4][4]; + copy_m4_m4(matrix, diff_mat); + zero_axis_bias_m4(matrix); + invert_m4_m4(inverse_diff_mat, matrix); /* Apply dvec to all of the stored points */ for (int i = 0; i < data->size; i++) { @@ -669,7 +674,7 @@ static bool gpencil_brush_pinch_apply(tGP_BrushEditData *gso, /* 1) Make this point relative to the cursor/midpoint (dvec) */ float fpt[3]; - mul_v3_m4v3(fpt, gso->object->obmat, &pt->x); + mul_v3_m4v3(fpt, gso->object->object_to_world, &pt->x); sub_v3_v3v3(vec, fpt, gso->dvec); /* 2) Shrink the distance by pulling the point towards the midpoint @@ -689,7 +694,7 @@ static bool gpencil_brush_pinch_apply(tGP_BrushEditData *gso, /* 3) Translate back to original space, with the shrinkage applied */ add_v3_v3v3(fpt, gso->dvec, vec); - mul_v3_m4v3(&pt->x, gso->object->imat, fpt); + mul_v3_m4v3(&pt->x, gso->object->world_to_object, fpt); /* compute lock axis */ gpencil_sculpt_compute_lock_axis(gso, pt, save_pt); @@ -700,8 +705,8 @@ static bool gpencil_brush_pinch_apply(tGP_BrushEditData *gso, /* ----------------------------------------------- */ /* Twist Brush - Rotate Around midpoint */ -/* Take the screenspace coordinates of the point, rotate this around the brush midpoint, - * convert the rotated point and convert it into "data" space +/* Take the screen-space coordinates of the point, rotate this around the brush midpoint, + * convert the rotated point and convert it into "data" space. */ static bool gpencil_brush_twist_apply(tGP_BrushEditData *gso, @@ -741,12 +746,12 @@ static bool gpencil_brush_twist_apply(tGP_BrushEditData *gso, /* Rotate point */ float fpt[3]; - mul_v3_m4v3(fpt, gso->object->obmat, &pt->x); + mul_v3_m4v3(fpt, gso->object->object_to_world, &pt->x); sub_v3_v3v3(vec, fpt, gso->dvec); /* make relative to center * (center is stored in dvec) */ mul_m3_v3(rmat, vec); add_v3_v3v3(fpt, vec, gso->dvec); /* restore */ - mul_v3_m4v3(&pt->x, gso->object->imat, fpt); + mul_v3_m4v3(&pt->x, gso->object->world_to_object, fpt); /* compute lock axis */ gpencil_sculpt_compute_lock_axis(gso, pt, save_pt); @@ -807,7 +812,7 @@ static bool gpencil_brush_randomize_apply(tGP_BrushEditData *gso, /* apply random to position */ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) { /* Jitter is applied perpendicular to the mouse movement vector - * - We compute all effects in screenspace (since it's easier) + * - We compute all effects in screen-space (since it's easier) * and then project these to get the points/distances in * view-space as needed. */ @@ -989,8 +994,8 @@ static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso) float delta[3]; size_t strokes_added = 0; - /* Compute amount to offset the points by */ - /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */ + /* Compute amount to offset the points by. */ + /* NOTE: This assumes that screen-space strokes are NOT used in the 3D view. */ gpencil_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */ sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint); @@ -1035,11 +1040,11 @@ static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso) */ for (i = 0, pt = new_stroke->points; i < new_stroke->totpoints; i++, pt++) { /* Rotate around center new position */ - mul_mat3_m4_v3(gso->object->obmat, &pt->x); /* only rotation component */ + mul_mat3_m4_v3(gso->object->object_to_world, &pt->x); /* only rotation component */ /* assume that the delta can just be applied, and then everything works */ add_v3_v3(&pt->x, delta); - mul_m4_v3(gso->object->imat, &pt->x); + mul_m4_v3(gso->object->world_to_object, &pt->x); } /* Store ref for later */ @@ -1063,7 +1068,7 @@ static void gpencil_brush_clone_adjust(tGP_BrushEditData *gso) /* For each of the stored strokes, apply the offset to each point */ /* NOTE: Again this assumes that in the 3D view, - * we only have 3d space and not screenspace strokes... */ + * we only have 3d space and not screen-space strokes. */ for (snum = 0; snum < data->totitems; snum++) { bGPDstroke *gps = data->new_strokes[snum]; bGPDspoint *pt; @@ -1169,13 +1174,18 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) gso->scene = scene; gso->object = ob; if (ob) { - invert_m4_m4(gso->inv_mat, ob->obmat); + float matrix[4][4]; + copy_m4_m4(matrix, ob->object_to_world); + zero_axis_bias_m4(matrix); + invert_m4_m4(gso->inv_mat, matrix); gso->vrgroup = gso->gpd->vertex_group_active_index - 1; if (!BLI_findlink(&gso->gpd->vertex_group_names, gso->vrgroup)) { gso->vrgroup = -1; } /* Check if some modifier can transform the stroke. */ gso->is_transformed = BKE_gpencil_has_transform_modifiers(ob); + + gso->ob_eval = (Object *)DEG_get_evaluated_id(gso->depsgraph, &ob->id); } else { unit_m4(gso->inv_mat); @@ -1191,12 +1201,19 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) gso->brush = brush; BKE_curvemapping_init(gso->brush->curve); - if (brush->gpencil_settings->sculpt_mode_flag & - (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | - GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL)) { + const bool is_automasking = (ts->gp_sculpt.flag & + (GP_SCULPT_SETT_FLAG_AUTOMASK_STROKE | + GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_STROKE | + GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_STROKE | + GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_ACTIVE | + GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_ACTIVE)) != 0; + if (is_automasking) { gso->automasking_strokes = BLI_ghash_ptr_new(__func__); } else { + if (gso->automasking_strokes != NULL) { + BLI_ghash_free(gso->automasking_strokes, NULL, NULL); + } gso->automasking_strokes = NULL; } /* save mask */ @@ -1287,6 +1304,10 @@ static void gpencil_sculpt_brush_exit(bContext *C, wmOperator *op) } default: + if (gso->stroke_customdata != NULL) { + BLI_ghash_free(gso->stroke_customdata, NULL, NULL); + gso->stroke_customdata = NULL; + } break; } @@ -1462,12 +1483,12 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDspoint pt_temp; pt = &gps->points[0]; if ((is_masking && (pt->flag & GP_SPOINT_SELECT) != 0) || (!is_masking)) { - gpencil_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gpencil_point_to_world_space(gps->points, diff_mat, &pt_temp); gpencil_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; /* Do bound-box check first. */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + if (!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ int mval_i[2]; round_v2i_v2fl(mval_i, gso->mval); @@ -1499,15 +1520,15 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso, } } bGPDspoint npt; - gpencil_point_to_parent_space(pt1, diff_mat, &npt); + gpencil_point_to_world_space(pt1, diff_mat, &npt); gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); - gpencil_point_to_parent_space(pt2, diff_mat, &npt); + gpencil_point_to_world_space(pt2, diff_mat, &npt); gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); /* Check that point segment of the bound-box of the selection stroke. */ - if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + (!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1]) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { /* Check if point segment of stroke had anything to do with * brush region (either within stroke painted, or on its lines) * - this assumes that line-width is irrelevant. @@ -1592,13 +1613,16 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, bGPdata *gpd = ob->data; const char tool = gso->brush->gpencil_sculpt_tool; GP_SpaceConversion *gsc = &gso->gsc; + ToolSettings *ts = gso->scene->toolsettings; Brush *brush = gso->brush; const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : gso->brush->size; - const bool is_automasking = (brush->gpencil_settings->sculpt_mode_flag & - (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | - GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | - GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL)) != 0; + const bool is_automasking = (ts->gp_sculpt.flag & + (GP_SCULPT_SETT_FLAG_AUTOMASK_STROKE | + GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_STROKE | + GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_STROKE | + GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_ACTIVE | + GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_ACTIVE)) != 0; /* Calc bound box matrix. */ float bound_mat[4][4]; BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat); @@ -1615,14 +1639,14 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, { bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; - if ((is_automasking) && (!BLI_ghash_haskey(gso->automasking_strokes, gps_active))) { + if ((is_automasking) && !BLI_ghash_haskey(gso->automasking_strokes, gps_active)) { continue; } } /* Check if the stroke collide with brush. */ if ((gps->totpoints > 1) && - (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat))) { + !ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { continue; } @@ -1731,27 +1755,111 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, return changed; } +/* Find the stroke nearer to the brush. */ +static void get_nearest_stroke_to_brush(tGP_BrushEditData *gso, + int mval_i[2], + bGPDlayer **r_gpl, + bGPDstroke **r_gps) +{ + const int radius = SEARCH_RADIUS_PIXEL; + + Object *ob_eval = gso->ob_eval; + bGPdata *gpd = (bGPdata *)ob_eval->data; + GP_SpaceConversion *gsc = &gso->gsc; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + float dist = FLT_MAX; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + /* Calculate bound box matrix. */ + float bound_mat[4][4]; + BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat); + + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + if (gps->totpoints == 0) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_material_editable(gso->object, gpl, gps) == false) { + continue; + } + + /* Check if the stroke collide with brush. */ + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { + continue; + } + + bGPDspoint *pt; + int pc2D[2] = {0}; + bGPDspoint npt; + + for (int i = 0; i < gps->totpoints; i++) { + pt = gps->points + i; + gpencil_point_to_world_space(pt, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc2D[0], &pc2D[1]); + float d = len_v2v2_int(mval_i, pc2D); + if (d < dist) { + dist = d; + *r_gpl = gpl; + *r_gps = gps_active; + } + } + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } + } + } +} + /* Get list of Auto-Masking strokes. */ static bool get_automasking_strokes_list(tGP_BrushEditData *gso) { - bGPdata *gpd = gso->gpd; + Object *ob_eval = gso->ob_eval; + bGPdata *gpd = (bGPdata *)ob_eval->data; GP_SpaceConversion *gsc = &gso->gsc; - Brush *brush = gso->brush; + ToolSettings *ts = gso->scene->toolsettings; Object *ob = gso->object; - Material *mat_active = BKE_gpencil_material(ob, ob->actcol); + const eGP_Sculpt_SettingsFlag flag = ts->gp_sculpt.flag; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const bool is_masking_stroke = (brush->gpencil_settings->sculpt_mode_flag & - GP_SCULPT_FLAGMODE_AUTOMASK_STROKE) != 0; - const bool is_masking_layer = (brush->gpencil_settings->sculpt_mode_flag & - GP_SCULPT_FLAGMODE_AUTOMASK_LAYER) != 0; - const bool is_masking_material = (brush->gpencil_settings->sculpt_mode_flag & - GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL) != 0; + const bool is_masking_stroke = (flag & GP_SCULPT_SETT_FLAG_AUTOMASK_STROKE) != 0; + const bool is_masking_layer_stroke = (flag & GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_STROKE) != 0; + const bool is_masking_material_stroke = (flag & GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_STROKE) != + 0; + const bool is_masking_layer_active = (flag & GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_ACTIVE) != 0; + const bool is_masking_material_active = (flag & GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_ACTIVE) != + 0; int mval_i[2]; round_v2i_v2fl(mval_i, gso->mval); /* Define a fix number of pixel as cursor radius. */ - const int radius = 10; + const int radius = SEARCH_RADIUS_PIXEL; bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); + Material *mat_active = BKE_gpencil_material(ob, ob->actcol); + + /* By default use active values. */ + bGPDlayer *gpl_active_stroke = gpl_active; + Material *mat_active_stroke = mat_active; + /* Find nearest stroke to find the layer and material. */ + if (is_masking_layer_stroke || is_masking_material_stroke) { + bGPDlayer *gpl_near = NULL; + bGPDstroke *gps_near = NULL; + get_nearest_stroke_to_brush(gso, mval_i, &gpl_near, &gps_near); + if (gps_near != NULL) { + if (is_masking_layer_stroke) { + gpl_active_stroke = gpl_near; + } + if (is_masking_material_stroke) { + mat_active_stroke = BKE_object_material_get(ob, gps_near->mat_nr + 1); + } + } + } LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* Only editable and visible layers are considered. */ @@ -1765,87 +1873,113 @@ static bool get_automasking_strokes_list(tGP_BrushEditData *gso) bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + bool pick_stroke = false; + bool pick_layer_stroke = false; + bool pick_material_stroke = false; + bool pick_layer_active = false; + bool pick_material_active = false; + if (gps->totpoints == 0) { continue; } - /* Check if the color is editable. */ + /* Check if the material is editable. */ if (ED_gpencil_stroke_material_editable(gso->object, gpl, gps) == false) { continue; } - /* Layer Auto-Masking. */ - if ((is_masking_layer) && (gpl == gpl_active)) { - BLI_ghash_insert(gso->automasking_strokes, gps, gps); - continue; + /* Stroke Layer Auto-Masking. */ + if (is_masking_layer_stroke && (gpl == gpl_active_stroke)) { + pick_layer_stroke = true; } - /* Material Auto-Masking. */ - if (is_masking_material) { + /* Active Layer Auto-Masking. */ + if (is_masking_layer_active && (gpl == gpl_active)) { + pick_layer_active = true; + } + /* Stroke Material Auto-Masking. */ + if (is_masking_material_stroke) { Material *mat = BKE_object_material_get(ob, gps->mat_nr + 1); - if (mat == mat_active) { - BLI_ghash_insert(gso->automasking_strokes, gps, gps); - continue; + if (mat == mat_active_stroke) { + pick_material_stroke = true; } } - - /* If Stroke Auto-Masking is not enabled, nothing else to do. */ - if (!is_masking_stroke) { - continue; + /* Active Material Auto-Masking. */ + if (is_masking_material_active) { + Material *mat = BKE_object_material_get(ob, gps->mat_nr + 1); + if (mat == mat_active) { + pick_material_active = true; + } } /* Check if the stroke collide with brush. */ - if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { - continue; - } - - bGPDspoint *pt1, *pt2; - int pc1[2] = {0}; - int pc2[2] = {0}; - bGPDspoint npt; + if ((is_masking_stroke) && + ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { - if (gps->totpoints == 1) { - gpencil_point_to_parent_space(gps->points, bound_mat, &npt); - gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + bGPDspoint *pt1, *pt2; + int pc1[2] = {0}; + int pc2[2] = {0}; + bGPDspoint npt; - /* Only check if point is inside. */ - if (len_v2v2_int(mval_i, pc1) <= radius) { - BLI_ghash_insert(gso->automasking_strokes, gps, gps); - } - } - else { - /* Loop over the points in the stroke, checking for intersections - * - an intersection means that we touched the stroke. - */ - for (int i = 0; (i + 1) < gps->totpoints; i++) { - /* Get points to work with. */ - pt1 = gps->points + i; - pt2 = gps->points + i + 1; - - /* Check first point. */ - gpencil_point_to_parent_space(pt1, bound_mat, &npt); + if (gps->totpoints == 1) { + gpencil_point_to_world_space(gps->points, bound_mat, &npt); gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); - if (len_v2v2_int(mval_i, pc1) <= radius) { - BLI_ghash_insert(gso->automasking_strokes, gps, gps); - i = gps->totpoints; - continue; - } - /* Check second point. */ - gpencil_point_to_parent_space(pt2, bound_mat, &npt); - gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); - if (len_v2v2_int(mval_i, pc2) <= radius) { - BLI_ghash_insert(gso->automasking_strokes, gps, gps); - i = gps->totpoints; - continue; + /* Only check if point is inside. */ + if (len_v2v2_int(mval_i, pc1) <= radius) { + pick_stroke = true; } - - /* Check segment. */ - if (gpencil_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { - BLI_ghash_insert(gso->automasking_strokes, gps, gps); - i = gps->totpoints; - continue; + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke. + */ + for (int i = 0; (i + 1) < gps->totpoints && !pick_stroke; i++) { + /* Get points to work with. */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* Check first point. */ + gpencil_point_to_world_space(pt1, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + if (len_v2v2_int(mval_i, pc1) <= radius) { + pick_stroke = true; + i = gps->totpoints; + } + + /* Check second point. */ + gpencil_point_to_world_space(pt2, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + if (len_v2v2_int(mval_i, pc2) <= radius) { + pick_stroke = true; + i = gps->totpoints; + } + + /* Check segment. */ + if (!pick_stroke && gpencil_stroke_inside_circle( + gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + pick_stroke = true; + i = gps->totpoints; + } } } } + /* if the stroke meets all the masking conditions, add to the hash table. */ + if (is_masking_stroke && !pick_stroke) { + continue; + } + if (is_masking_layer_stroke && !pick_layer_stroke) { + continue; + } + if (is_masking_material_stroke && !pick_material_stroke) { + continue; + } + if (is_masking_layer_active && !pick_layer_active) { + continue; + } + if (is_masking_material_active && !pick_material_active) { + continue; + } + BLI_ghash_insert(gso->automasking_strokes, gps_active, gps_active); } /* If not multi-edit, exit loop. */ if (!is_multiedit) { @@ -1865,7 +1999,7 @@ static bool gpencil_sculpt_brush_apply_standard(bContext *C, tGP_BrushEditData * Object *obact = gso->object; bool changed = false; - Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &obact->id); + Object *ob_eval = gso->ob_eval; bGPdata *gpd = (bGPdata *)ob_eval->data; /* Calculate brush-specific data which applies equally to all points */ @@ -1903,7 +2037,7 @@ static bool gpencil_sculpt_brush_apply_standard(bContext *C, tGP_BrushEditData * /* Find visible strokes, and perform operations on those if hit */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* If no active frame, don't do anything... */ - if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) { + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { continue; } @@ -1959,6 +2093,7 @@ static void gpencil_sculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA * { tGP_BrushEditData *gso = op->customdata; Brush *brush = gso->brush; + ToolSettings *ts = gso->scene->toolsettings; const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : gso->brush->size; float mousef[2]; @@ -2000,9 +2135,10 @@ static void gpencil_sculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA * /* Get list of Auto-Masking strokes. */ if ((!gso->automasking_ready) && - (brush->gpencil_settings->sculpt_mode_flag & - (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | - GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL))) { + (ts->gp_sculpt.flag & + (GP_SCULPT_SETT_FLAG_AUTOMASK_STROKE | GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_STROKE | + GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_STROKE | GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_ACTIVE | + GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_ACTIVE))) { gso->automasking_ready = get_automasking_strokes_list(gso); } diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index a19265720e8..13455861924 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -94,13 +94,13 @@ static bool gpencil_select_poll(bContext *C) ToolSettings *ts = CTX_data_tool_settings(C); if (GPENCIL_SCULPT_MODE(gpd)) { - if (!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) { + if (!GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt)) { return false; } } if (GPENCIL_VERTEX_MODE(gpd)) { - if (!(GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex))) { + if (!GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex)) { return false; } } @@ -267,7 +267,7 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op) /* For sculpt mode, if mask is disable, only allows deselect */ if (GPENCIL_SCULPT_MODE(gpd)) { ToolSettings *ts = CTX_data_tool_settings(C); - if ((!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) && (action != SEL_DESELECT)) { + if (!GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt) && (action != SEL_DESELECT)) { return OPERATOR_CANCELLED; } } @@ -529,7 +529,7 @@ static int gpencil_select_random_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); ToolSettings *ts = CTX_data_tool_settings(C); bGPdata *gpd = ED_gpencil_data_get_active(C); - if ((gpd == NULL) || (GPENCIL_NONE_EDIT_MODE(gpd))) { + if ((gpd == NULL) || GPENCIL_NONE_EDIT_MODE(gpd)) { return OPERATOR_CANCELLED; } @@ -576,7 +576,7 @@ static int gpencil_select_random_exec(bContext *C, wmOperator *op) if (selectmode == GP_SELECTMODE_STROKE) { RNG *rng = BLI_rng_new(seed_iter); - const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints; + const uint j = BLI_rng_get_uint(rng) % gps->totpoints; bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false; select_stroke ^= select; /* Curve function has select parameter inverted. */ @@ -647,7 +647,7 @@ static int gpencil_select_random_exec(bContext *C, wmOperator *op) if (selectmode == GP_SELECTMODE_STROKE) { RNG *rng = BLI_rng_new(seed_iter); - const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints; + const uint j = BLI_rng_get_uint(rng) % gps->totpoints; bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false; select_stroke ^= select; select_all_stroke_points(gpd, gps, select_stroke); @@ -1497,11 +1497,11 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd, pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; bGPDspoint pt_temp; - gpencil_point_to_parent_space(pt, diff_mat, &pt_temp); + gpencil_point_to_world_space(pt, diff_mat, &pt_temp); gpencil_point_to_xy(gsc, gps, &pt_temp, &x0, &y0); /* do boundbox check first */ - if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) { + if (!ELEM(V2D_IS_CLIPPED, x0, y0) && BLI_rcti_isect_pt(rect, x0, y0)) { /* only check if point is inside */ if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) { hit = true; @@ -2040,7 +2040,7 @@ static bool gpencil_generic_stroke_select(bContext *C, gpencil_point_conversion_init(C, &gsc); /* deselect all strokes first? */ - if (SEL_OP_USE_PRE_DESELECT(sel_op) || (GPENCIL_PAINT_MODE(gpd))) { + if (SEL_OP_USE_PRE_DESELECT(sel_op) || GPENCIL_PAINT_MODE(gpd)) { /* Set selection index to 0. */ gpd->select_last_index = 0; @@ -2071,8 +2071,9 @@ static bool gpencil_generic_stroke_select(bContext *C, for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; - /* convert point coords to screenspace */ - const bool is_inside = is_inside_fn(gsc.region, gpstroke_iter.diff_mat, &pt->x, user_data); + /* Convert point coords to screen-space. */ + const bool is_inside = is_inside_fn( + gsc.region, gpstroke_iter.diff_mat, &pt_active->x, user_data); if (strokemode == false) { const bool is_select = (pt_active->flag & GP_SPOINT_SELECT) != 0; const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); @@ -2477,7 +2478,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) int xy[2]; bGPDspoint pt2; - gpencil_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2); + gpencil_point_to_world_space(pt, gpstroke_iter.diff_mat, &pt2); gpencil_point_to_xy(&gsc, gps_active, &pt2, &xy[0], &xy[1]); /* do boundbox check first */ @@ -2766,7 +2767,7 @@ static bool gpencil_select_vertex_color_poll(bContext *C) bGPdata *gpd = (bGPdata *)ob->data; if (GPENCIL_VERTEX_MODE(gpd)) { - if (!(GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex))) { + if (!GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex)) { return false; } diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c index f6e88e05d46..1a18e10412e 100644 --- a/source/blender/editors/gpencil/gpencil_trace_ops.c +++ b/source/blender/editors/gpencil/gpencil_trace_ops.c @@ -45,7 +45,7 @@ typedef struct TraceJob { /* from wmJob */ struct Object *owner; - short *stop, *do_update; + bool *stop, *do_update; float *progress; bContext *C; @@ -71,6 +71,9 @@ typedef struct TraceJob { int32_t thickness; int32_t turnpolicy; int32_t mode; + /** Frame to render to be used by python API. Not exposed in UI. + * This feature is only used in Studios to run custom video trace for selected frames. */ + int32_t frame_num; bool success; bool was_canceled; @@ -195,7 +198,7 @@ static void trace_initialize_job_data(TraceJob *trace_job) } } -static void trace_start_job(void *customdata, short *stop, short *do_update, float *progress) +static void trace_start_job(void *customdata, bool *stop, bool *do_update, float *progress) { TraceJob *trace_job = customdata; @@ -212,7 +215,10 @@ static void trace_start_job(void *customdata, short *stop, short *do_update, flo (trace_job->mode == GPENCIL_TRACE_MODE_SINGLE)) { void *lock; ImageUser *iuser = trace_job->ob_active->iuser; - iuser->framenr = init_frame; + + iuser->framenr = ((trace_job->frame_num == 0) || (trace_job->frame_num > iuser->frames)) ? + init_frame : + trace_job->frame_num; ImBuf *ibuf = BKE_image_acquire_ibuf(trace_job->image, iuser, &lock); if (ibuf) { /* Create frame. */ @@ -251,7 +257,7 @@ static void trace_start_job(void *customdata, short *stop, short *do_update, flo trace_job->success = !trace_job->was_canceled; *do_update = true; - *stop = 0; + *stop = false; } static void trace_end_job(void *customdata) @@ -300,9 +306,10 @@ static int gpencil_trace_image_exec(bContext *C, wmOperator *op) /* Create a new grease pencil object or reuse selected. */ eGP_TargetObjectMode target = RNA_enum_get(op->ptr, "target"); - job->ob_gpencil = (target == GP_TARGET_OB_SELECTED) ? BKE_view_layer_non_active_selected_object( - CTX_data_view_layer(C), job->v3d) : - NULL; + job->ob_gpencil = (target == GP_TARGET_OB_SELECTED) ? + BKE_view_layer_non_active_selected_object( + scene, CTX_data_view_layer(C), job->v3d) : + NULL; if (job->ob_gpencil != NULL) { if (job->ob_gpencil->type != OB_GPENCIL) { @@ -324,14 +331,15 @@ static int gpencil_trace_image_exec(bContext *C, wmOperator *op) job->thickness = RNA_int_get(op->ptr, "thickness"); job->turnpolicy = RNA_enum_get(op->ptr, "turnpolicy"); job->mode = RNA_enum_get(op->ptr, "mode"); + job->frame_num = RNA_int_get(op->ptr, "frame_number"); trace_initialize_job_data(job); /* Back to active base. */ ED_object_base_activate(job->C, job->base_active); - if (job->image->source == IMA_SRC_FILE) { - short stop = 0, do_update = true; + if ((job->image->source == IMA_SRC_FILE) || (job->frame_num > 0)) { + bool stop = false, do_update = true; float progress; trace_start_job(job, &stop, &do_update, &progress); trace_end_job(job); @@ -364,6 +372,8 @@ static int gpencil_trace_image_invoke(bContext *C, wmOperator *op, const wmEvent void GPENCIL_OT_trace_image(wmOperatorType *ot) { + PropertyRNA *prop; + static const EnumPropertyItem turnpolicy_type[] = { {POTRACE_TURNPOLICY_BLACK, "BLACK", @@ -475,4 +485,15 @@ void GPENCIL_OT_trace_image(wmOperatorType *ot) true, "Start At Current Frame", "Trace Image starting in current image frame"); + prop = RNA_def_int( + ot->srna, + "frame_number", + 0, + 0, + 9999, + "Trace Frame", + "Used to trace only one frame of the image sequence, set to zero to trace all", + 0, + 9999); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c index 735759d40ec..aa9a2a653bb 100644 --- a/source/blender/editors/gpencil/gpencil_trace_utils.c +++ b/source/blender/editors/gpencil/gpencil_trace_utils.c @@ -119,7 +119,7 @@ static void pixel_at_index(const ImBuf *ibuf, const int32_t idx, float r_col[4]) copy_v4_v4(r_col, frgba); } else { - unsigned char *cp = (unsigned char *)(ibuf->rect + idx); + uchar *cp = (uchar *)(ibuf->rect + idx); r_col[0] = (float)cp[0] / 255.0f; r_col[1] = (float)cp[1] / 255.0f; r_col[2] = (float)cp[2] / 255.0f; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index e47f16ac466..7d814b43196 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -518,7 +518,7 @@ bool ED_gpencil_stroke_can_use_direct(const ScrArea *area, const bGPDstroke *gps /* filter stroke types by flags + spacetype */ if (gps->flag & GP_STROKE_3DSPACE) { /* 3D strokes - only in 3D view */ - return (ELEM(area->spacetype, SPACE_VIEW3D, SPACE_PROPERTIES)); + return ELEM(area->spacetype, SPACE_VIEW3D, SPACE_PROPERTIES); } if (gps->flag & GP_STROKE_2DIMAGE) { /* Special "image" strokes - only in Image Editor */ @@ -612,17 +612,17 @@ void gpencil_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) } } -void gpencil_point_to_parent_space(const bGPDspoint *pt, - const float diff_mat[4][4], - bGPDspoint *r_pt) +void gpencil_point_to_world_space(const bGPDspoint *pt, + const float diff_mat[4][4], + bGPDspoint *r_pt) { - float fpt[3]; - - mul_v3_m4v3(fpt, diff_mat, &pt->x); - copy_v3_v3(&r_pt->x, fpt); + mul_v3_m4v3(&r_pt->x, diff_mat, &pt->x); } -void gpencil_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDstroke *gps) +void gpencil_world_to_object_space(Depsgraph *depsgraph, + Object *obact, + bGPDlayer *gpl, + bGPDstroke *gps) { bGPDspoint *pt; int i; @@ -630,34 +630,31 @@ void gpencil_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, b /* undo matrix */ float diff_mat[4][4]; float inverse_diff_mat[4][4]; - float fpt[3]; BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); + zero_axis_bias_m4(diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); for (i = 0; i < gps->totpoints; i++) { pt = &gps->points[i]; - mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); - copy_v3_v3(&pt->x, fpt); + mul_m4_v3(inverse_diff_mat, &pt->x); } } -void gpencil_apply_parent_point(Depsgraph *depsgraph, - Object *obact, - bGPDlayer *gpl, - bGPDspoint *pt) +void gpencil_world_to_object_space_point(Depsgraph *depsgraph, + Object *obact, + bGPDlayer *gpl, + bGPDspoint *pt) { /* undo matrix */ float diff_mat[4][4]; float inverse_diff_mat[4][4]; - float fpt[3]; BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); + zero_axis_bias_m4(diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); - mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); - - copy_v3_v3(&pt->x, fpt); + mul_m4_v3(inverse_diff_mat, &pt->x); } void gpencil_point_to_xy( @@ -770,7 +767,7 @@ void gpencil_point_3d_to_xy(const GP_SpaceConversion *gsc, float xyval[2]; /* sanity checks */ - BLI_assert((gsc->area->spacetype == SPACE_VIEW3D)); + BLI_assert(gsc->area->spacetype == SPACE_VIEW3D); if (flag & GP_STROKE_3DSPACE) { if (ED_view3d_project_float_global(region, pt, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { @@ -857,7 +854,7 @@ void gpencil_stroke_convertcoords_tpoint(Scene *scene, int mval_i[2]; round_v2i_v2fl(mval_i, point2D->m_xy); - if ((depth != NULL) && (ED_view3d_autodist_simple(region, mval_i, r_out, 0, depth))) { + if ((depth != NULL) && ED_view3d_autodist_simple(region, mval_i, r_out, 0, depth)) { /* projecting onto 3D-Geometry * - nothing more needs to be done here, since view_autodist_simple() has already done it */ @@ -903,7 +900,7 @@ void ED_gpencil_drawing_reference_get(const Scene *scene, } else { /* use object location */ - copy_v3_v3(r_vec, ob->obmat[3]); + copy_v3_v3(r_vec, ob->object_to_world[3]); /* Apply layer offset. */ bGPdata *gpd = ob->data; bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); @@ -935,6 +932,7 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke * gpencil_point_conversion_init(C, &gsc); BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat); + zero_axis_bias_m4(diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); /* Adjust each point */ @@ -942,7 +940,7 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke * float xy[2]; bGPDspoint pt2; - gpencil_point_to_parent_space(pt, diff_mat, &pt2); + gpencil_point_to_world_space(pt, diff_mat, &pt2); gpencil_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); /* Planar - All on same plane parallel to the viewplane */ @@ -986,7 +984,7 @@ void ED_gpencil_project_stroke_to_plane(const Scene *scene, /* if object, apply object rotation */ if (ob && (ob->type == OB_GPENCIL)) { float mat[4][4]; - copy_m4_m4(mat, ob->obmat); + copy_m4_m4(mat, ob->object_to_world); /* move origin to cursor */ if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { @@ -1038,7 +1036,8 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, bGPDframe *gpf, bGPDstroke *gps, const eGP_ReprojectModes mode, - const bool keep_original) + const bool keep_original, + const float offset) { ToolSettings *ts = gsc->scene->toolsettings; ARegion *region = gsc->region; @@ -1050,6 +1049,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, float diff_mat[4][4], inverse_diff_mat[4][4]; BKE_gpencil_layer_transform_matrix_get(depsgraph, gsc->ob, gpl, diff_mat); + zero_axis_bias_m4(diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); float origin[3]; @@ -1087,7 +1087,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, * artifacts in the final points. */ bGPDspoint pt2; - gpencil_point_to_parent_space(pt, diff_mat, &pt2); + gpencil_point_to_world_space(pt, diff_mat, &pt2); gpencil_point_to_xy_fl(gsc, gps_active, &pt2, &xy[0], &xy[1]); /* Project stroke in one axis */ @@ -1121,7 +1121,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, copy_v3_v3(&pt->x, &pt2.x); /* apply parent again */ - gpencil_apply_parent_point(depsgraph, gsc->ob, gpl, pt); + gpencil_world_to_object_space_point(depsgraph, gsc->ob, gpl, pt); } /* Project screen-space back to 3D space (from current perspective) * so that all points have been treated the same way. */ @@ -1156,7 +1156,13 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, &depth, &location[0], &normal[0])) { - copy_v3_v3(&pt->x, location); + /* Apply offset over surface. */ + float normal_vector[3]; + sub_v3_v3v3(normal_vector, ray_start, location); + normalize_v3(normal_vector); + mul_v3_fl(normal_vector, offset); + + add_v3_v3v3(&pt->x, location, normal_vector); } else { /* Default to planar */ @@ -1200,7 +1206,7 @@ void ED_gpencil_project_point_to_plane(const Scene *scene, /* if object, apply object rotation */ if (ob && (ob->type == OB_GPENCIL)) { float mat[4][4]; - copy_m4_m4(mat, ob->obmat); + copy_m4_m4(mat, ob->object_to_world); if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { if (gpl != NULL) { add_v3_v3(mat[3], gpl->location); @@ -1227,7 +1233,7 @@ void ED_gpencil_project_point_to_plane(const Scene *scene, /* move origin to object */ if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { - copy_v3_v3(mat[3], ob->obmat[3]); + copy_v3_v3(mat[3], ob->object_to_world[3]); } mul_mat3_m4_v3(mat, plane_normal); @@ -1359,16 +1365,16 @@ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata if (gpl->parent != NULL) { /* calculate new matrix */ if (ELEM(gpl->partype, PAROBJECT, PARSKEL)) { - invert_m4_m4(cur_mat, gpl->parent->obmat); - copy_v3_v3(gpl_loc, obact->obmat[3]); + invert_m4_m4(cur_mat, gpl->parent->object_to_world); + copy_v3_v3(gpl_loc, obact->object_to_world[3]); } else if (gpl->partype == PARBONE) { bPoseChannel *pchan = BKE_pose_channel_find_name(gpl->parent->pose, gpl->parsubstr); if (pchan) { float tmp_mat[4][4]; - mul_m4_m4m4(tmp_mat, gpl->parent->obmat, pchan->pose_mat); + mul_m4_m4m4(tmp_mat, gpl->parent->object_to_world, pchan->pose_mat); invert_m4_m4(cur_mat, tmp_mat); - copy_v3_v3(gpl_loc, obact->obmat[3]); + copy_v3_v3(gpl_loc, obact->object_to_world[3]); } } @@ -1656,11 +1662,11 @@ static bool gpencil_check_cursor_region(bContext *C, const int mval_i[2]) ScrArea *area = CTX_wm_area(C); Object *ob = CTX_data_active_object(C); - if ((ob == NULL) || (!ELEM(ob->mode, - OB_MODE_PAINT_GPENCIL, - OB_MODE_SCULPT_GPENCIL, - OB_MODE_WEIGHT_GPENCIL, - OB_MODE_VERTEX_GPENCIL))) { + if ((ob == NULL) || !ELEM(ob->mode, + OB_MODE_PAINT_GPENCIL, + OB_MODE_SCULPT_GPENCIL, + OB_MODE_WEIGHT_GPENCIL, + OB_MODE_VERTEX_GPENCIL)) { return false; } @@ -1749,7 +1755,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat const int mval_i[2] = {x, y}; /* Check if cursor is in drawing region and has valid data-block. */ - if ((!gpencil_check_cursor_region(C, mval_i)) || (gpd == NULL)) { + if (!gpencil_check_cursor_region(C, mval_i) || (gpd == NULL)) { return; } @@ -1787,7 +1793,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat * is too disruptive and the size of cursor does not change with zoom factor. * The decision was to use a fix size, instead of brush->thickness value. */ - if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && + if ((gp_style) && GPENCIL_PAINT_MODE(gpd) && ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && (brush->gpencil_tool == GPAINT_TOOL_DRAW)) { @@ -1872,7 +1878,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat /* Inner Ring: Color from UI panel */ immUniformColor4f(color[0], color[1], color[2], 0.8f); - if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && + if ((gp_style) && GPENCIL_PAINT_MODE(gpd) && ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && (brush->gpencil_tool == GPAINT_TOOL_DRAW)) { @@ -2745,7 +2751,7 @@ void ED_gpencil_init_random_settings(Brush *brush, const int mval[2], GpRandomSettings *random_settings) { - int seed = ((uint)(ceil(PIL_check_seconds_timer())) + 1) % 128; + int seed = ((uint)ceil(PIL_check_seconds_timer()) + 1) % 128; /* Use mouse position to get randomness. */ int ix = mval[0] * seed; int iy = mval[1] * seed; @@ -2791,7 +2797,7 @@ static void gpencil_sbuffer_vertex_color_random( { BrushGpencilSettings *brush_settings = brush->gpencil_settings; if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) { - int seed = ((uint)(ceil(PIL_check_seconds_timer())) + 1) % 128; + int seed = ((uint)ceil(PIL_check_seconds_timer()) + 1) % 128; int ix = (int)(tpt->m_xy[0] * seed); int iy = (int)(tpt->m_xy[1] * seed); @@ -2941,7 +2947,7 @@ void ED_gpencil_projected_2d_bound_box(const GP_SpaceConversion *gsc, for (int i = 0; i < 8; i++) { bGPDspoint pt_dummy, pt_dummy_ps; copy_v3_v3(&pt_dummy.x, bb.vec[i]); - gpencil_point_to_parent_space(&pt_dummy, diff_mat, &pt_dummy_ps); + gpencil_point_to_world_space(&pt_dummy, diff_mat, &pt_dummy_ps); gpencil_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &bounds[i][0], &bounds[i][1]); } @@ -3005,7 +3011,7 @@ bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps, int i; for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { bGPDspoint pt2; - gpencil_point_to_parent_space(pt, diff_mat, &pt2); + gpencil_point_to_world_space(pt, diff_mat, &pt2); gpencil_point_to_xy(gsc, gps, &pt2, &mcoords[i][0], &mcoords[i][1]); } @@ -3014,7 +3020,7 @@ bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps, BLI_lasso_boundbox(&rect, mcoords, len); /* Test if point inside stroke. */ - hit = ((!ELEM(V2D_IS_CLIPPED, mval[0], mval[1])) && BLI_rcti_isect_pt(&rect, mval[0], mval[1]) && + hit = (!ELEM(V2D_IS_CLIPPED, mval[0], mval[1]) && BLI_rcti_isect_pt(&rect, mval[0], mval[1]) && BLI_lasso_is_point_inside(mcoords, len, mval[0], mval[1], INT_MAX)); /* Free memory. */ @@ -3031,9 +3037,9 @@ void ED_gpencil_stroke_extremes_to2d(const GP_SpaceConversion *gsc, { bGPDspoint pt_dummy_ps; - gpencil_point_to_parent_space(&gps->points[0], diff_mat, &pt_dummy_ps); + gpencil_point_to_world_space(&gps->points[0], diff_mat, &pt_dummy_ps); gpencil_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &r_ctrl1[0], &r_ctrl1[1]); - gpencil_point_to_parent_space(&gps->points[gps->totpoints - 1], diff_mat, &pt_dummy_ps); + gpencil_point_to_world_space(&gps->points[gps->totpoints - 1], diff_mat, &pt_dummy_ps); gpencil_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &r_ctrl2[0], &r_ctrl2[1]); } @@ -3061,11 +3067,11 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, float pt2d_start[2], pt2d_end[2]; bGPDspoint *pt = &gps->points[0]; - gpencil_point_to_parent_space(pt, diff_mat, &pt_parent); + gpencil_point_to_world_space(pt, diff_mat, &pt_parent); gpencil_point_to_xy_fl(gsc, gps, &pt_parent, &pt2d_start[0], &pt2d_start[1]); pt = &gps->points[gps->totpoints - 1]; - gpencil_point_to_parent_space(pt, diff_mat, &pt_parent); + gpencil_point_to_world_space(pt, diff_mat, &pt_parent); gpencil_point_to_xy_fl(gsc, gps, &pt_parent, &pt2d_end[0], &pt2d_end[1]); /* Loop all strokes of the active frame. */ @@ -3082,8 +3088,8 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, } /* Check if one of the ends is inside target stroke bounding box. */ - if ((!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_start, radius, diff_mat)) && - (!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_end, radius, diff_mat))) { + if (!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_start, radius, diff_mat) && + !ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_end, radius, diff_mat)) { continue; } /* Check the distance of the ends with the ends of target stroke to avoid middle contact. @@ -3091,11 +3097,11 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, float pt2d_target_start[2], pt2d_target_end[2]; pt = &gps_target->points[0]; - gpencil_point_to_parent_space(pt, diff_mat, &pt_parent); + gpencil_point_to_world_space(pt, diff_mat, &pt_parent); gpencil_point_to_xy_fl(gsc, gps, &pt_parent, &pt2d_target_start[0], &pt2d_target_start[1]); pt = &gps_target->points[gps_target->totpoints - 1]; - gpencil_point_to_parent_space(pt, diff_mat, &pt_parent); + gpencil_point_to_world_space(pt, diff_mat, &pt_parent); gpencil_point_to_xy_fl(gsc, gps, &pt_parent, &pt2d_target_end[0], &pt2d_target_end[1]); /* If the distance to the original stroke extremes is too big, the stroke must not be joined. @@ -3119,7 +3125,7 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, for (i = 0, pt = gps_target->points; i < gps_target->totpoints; i++, pt++) { /* Convert point to 2D. */ float pt2d[2]; - gpencil_point_to_parent_space(pt, diff_mat, &pt_parent); + gpencil_point_to_world_space(pt, diff_mat, &pt_parent); gpencil_point_to_xy_fl(gsc, gps, &pt_parent, &pt2d[0], &pt2d[1]); /* Check with Start point. */ diff --git a/source/blender/editors/gpencil/gpencil_uv.c b/source/blender/editors/gpencil/gpencil_uv.c index 51cb08cf592..0144ffa07c7 100644 --- a/source/blender/editors/gpencil/gpencil_uv.c +++ b/source/blender/editors/gpencil/gpencil_uv.c @@ -138,7 +138,7 @@ static bool gpencil_uv_transform_init(bContext *C, wmOperator *op) opdata->array_loc = NULL; opdata->array_rot = NULL; opdata->array_scale = NULL; - opdata->ob_scale = mat4_to_scale(opdata->ob->obmat); + opdata->ob_scale = mat4_to_scale(opdata->ob->object_to_world); opdata->vinit_rotation[0] = 1.0f; opdata->vinit_rotation[1] = 0.0f; @@ -158,7 +158,7 @@ static bool gpencil_uv_transform_init(bContext *C, wmOperator *op) float r_center[3]; gpencil_stroke_center(gps, r_center); /* Add object location. */ - add_v3_v3(r_center, opdata->ob->obmat[3]); + add_v3_v3(r_center, opdata->ob->object_to_world[3]); add_v3_v3(center, r_center); i++; } diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c index 865c4e360b5..41f939813e4 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_ops.c +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -131,7 +131,7 @@ static int gpencil_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator /* * The algorithm is by Werner D. Streidt * (http://visca.com/ffactory/archives/5-99/msg00021.html) - * Extracted of OpenCV demhist.c + * Extracted of OpenCV `demhist.c`. */ if (contrast > 0) { gain = 1.0f - delta * 2.0f; diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index c221d8e25e4..ca0729ecb94 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -846,12 +846,12 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso, if (gps->totpoints == 1) { bGPDspoint pt_temp; pt = &gps->points[0]; - gpencil_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gpencil_point_to_world_space(gps->points, diff_mat, &pt_temp); gpencil_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; /* Do bound-box check first. */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + if (!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ int mval_i[2]; round_v2i_v2fl(mval_i, gso->mval); @@ -884,15 +884,15 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso, } bGPDspoint npt; - gpencil_point_to_parent_space(pt1, diff_mat, &npt); + gpencil_point_to_world_space(pt1, diff_mat, &npt); gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); - gpencil_point_to_parent_space(pt2, diff_mat, &npt); + gpencil_point_to_world_space(pt2, diff_mat, &npt); gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); /* Check that point segment of the bound-box of the selection stroke. */ - if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + (!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1]) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { /* Check if point segment of stroke had anything to do with * brush region (either within stroke painted, or on its lines) * - this assumes that line-width is irrelevant. @@ -957,7 +957,7 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso, } /* If nothing hit, check if the mouse is inside any filled stroke. */ - if ((!hit) && (ELEM(tool, GPAINT_TOOL_TINT, GPVERTEX_TOOL_DRAW))) { + if ((!hit) && ELEM(tool, GPAINT_TOOL_TINT, GPVERTEX_TOOL_DRAW)) { MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(gso->object, gps_active->mat_nr + 1); if (gp_style->flag & GP_MATERIAL_FILL_SHOW) { @@ -1113,7 +1113,7 @@ static bool gpencil_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVert /* Find visible strokes, and perform operations on those if hit */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* If locked or no active frame, don't do anything. */ - if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) { + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { continue; } diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index 22fbf0021fc..f73c6f8c658 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -235,7 +235,7 @@ static bool brush_draw_apply(tGP_BrushWeightpaintData *gso, if (gso->vrgroup == -1) { if (gso->object) { Object *ob_armature = BKE_modifiers_is_deformed_by_armature(gso->object); - if ((ob_armature != NULL)) { + if (ob_armature != NULL) { Bone *actbone = ((bArmature *)ob_armature->data)->act_bone; if (actbone != NULL) { bPoseChannel *pchan = BKE_pose_channel_find_name(ob_armature->pose, actbone->name); @@ -413,12 +413,12 @@ static void gpencil_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso, if (gps->totpoints == 1) { bGPDspoint pt_temp; pt = &gps->points[0]; - gpencil_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gpencil_point_to_world_space(gps->points, diff_mat, &pt_temp); gpencil_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; /* Do bound-box check first. */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + if (!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ int mval_i[2]; round_v2i_v2fl(mval_i, gso->mval); @@ -440,15 +440,15 @@ static void gpencil_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso, pt2 = gps->points + i + 1; bGPDspoint npt; - gpencil_point_to_parent_space(pt1, diff_mat, &npt); + gpencil_point_to_world_space(pt1, diff_mat, &npt); gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); - gpencil_point_to_parent_space(pt2, diff_mat, &npt); + gpencil_point_to_world_space(pt2, diff_mat, &npt); gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); /* Check that point segment of the bound-box of the selection stroke */ - if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + (!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1]) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { /* Check if point segment of stroke had anything to do with * brush region (either within stroke painted, or on its lines) * - this assumes that line-width is irrelevant. @@ -579,7 +579,7 @@ static bool gpencil_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeig /* Find visible strokes, and perform operations on those if hit */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* If locked or no active frame, don't do anything. */ - if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) { + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { continue; } |