diff options
-rw-r--r-- | release/scripts/startup/bl_ui/properties_grease_pencil_common.py | 4 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_edit.c | 80 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_intern.h | 13 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_ops.c | 2 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_utils.c | 57 |
5 files changed, 156 insertions, 0 deletions
diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 80ab4e4d14b..ba7006da45e 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -225,6 +225,10 @@ class GreasePencilStrokeEditPanel: if gpd: col.prop(gpd, "show_stroke_direction", text="Show Directions") + if is_3d_view: + layout.separator() + layout.operator("gpencil.reproject") + class GreasePencilBrushPanel: # subclass must set diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 3fe53e3d0ab..d9b6c8046cd 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -72,6 +72,7 @@ #include "ED_gpencil.h" #include "ED_object.h" +#include "ED_screen.h" #include "ED_view3d.h" #include "gpencil_intern.h" @@ -1881,4 +1882,83 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ***************** Reproject Strokes ********************** */ + +static int gp_strokes_reproject_poll(bContext *C) +{ + /* 2 Requirements: + * - 1) Editable GP data + * - 2) 3D View only (2D editors don't have projection issues) + */ + return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); +} + +static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + GP_SpaceConversion gsc = {NULL}; + + /* init space conversion stuff */ + gp_point_conversion_init(C, &gsc); + + /* Go through each editable + selected stroke, adjusting each of its points one by one... */ + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) + { + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float xy[2]; + + /* 3D to Screenspace */ + /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace + * coordinates, resulting in lost precision, which in turn causes stairstepping + * artifacts in the final points. + */ + if (gpl->parent == NULL) { + gp_point_to_xy_fl(&gsc, gps, pt, &xy[0], &xy[1]); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); + } + + /* Project screenspace back to 3D space (from current perspective) + * so that all points have been treated the same way + */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + + /* Unapply parent corrections */ + if (gpl->parent) { + float inverse_diff_mat[4][4]; + invert_m4_m4(inverse_diff_mat, diff_mat); + mul_m4_v3(inverse_diff_mat, &pt->x); + } + } + } + } + GP_EDITABLE_STROKES_END; + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_reproject(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Reproject Strokes"; + ot->idname = "GPENCIL_OT_reproject"; + ot->description = "Reproject the selected strokes from the current viewpoint to get all points on the same plane again " + "(e.g. to fix problems from accidental 3D cursor movement, or viewport changes)"; + + /* callbacks */ + ot->exec = gp_strokes_reproject_exec; + ot->poll = gp_strokes_reproject_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ************************************************ */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index e8d007c6c19..4178d49d652 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -101,6 +101,18 @@ void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct int *r_x, int *r_y); /** + * Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D) + * + * Just like gp_point_to_xy(), except the resulting coordinates are floats not ints. + * Use this version to solve "stair-step" artifacts which may arise when roundtripping the calculations. + * + * \param[out] r_x The screen-space x-coordinate of the point + * \param[out] r_y The screen-space y-coordinate of the point + */ +void gp_point_to_xy_fl(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, + float *r_x, float *r_y); + +/** * Convert point to parent space * * \param pt Original point @@ -250,6 +262,7 @@ void GPENCIL_OT_snap_to_cursor(struct wmOperatorType *ot); void GPENCIL_OT_snap_cursor_to_selected(struct wmOperatorType *ot); void GPENCIL_OT_snap_cursor_to_center(struct wmOperatorType *ot); +void GPENCIL_OT_reproject(struct wmOperatorType *ot); /* stroke sculpting -- */ diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 50f4e795d70..ae1c5554521 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -375,6 +375,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_snap_to_cursor); WM_operatortype_append(GPENCIL_OT_snap_cursor_to_selected); + WM_operatortype_append(GPENCIL_OT_reproject); + WM_operatortype_append(GPENCIL_OT_brush_paint); /* Editing (Buttons) ------------ */ diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index a61c83be54f..564ba639983 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -628,6 +628,63 @@ void gp_point_to_xy(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, } } +/* Convert Grease Pencil points to screen-space values (as floats) + * WARNING: This assumes that the caller has already checked whether the stroke in question can be drawn + */ +void gp_point_to_xy_fl(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, + float *r_x, float *r_y) +{ + ARegion *ar = gsc->ar; + View2D *v2d = gsc->v2d; + rctf *subrect = gsc->subrect; + float xyval[2]; + + /* sanity checks */ + BLI_assert(!(gps->flag & GP_STROKE_3DSPACE) || (gsc->sa->spacetype == SPACE_VIEW3D)); + BLI_assert(!(gps->flag & GP_STROKE_2DSPACE) || (gsc->sa->spacetype != SPACE_VIEW3D)); + + + if (gps->flag & GP_STROKE_3DSPACE) { + if (ED_view3d_project_float_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + *r_x = xyval[0]; + *r_y = xyval[1]; + } + else { + *r_x = 0.0f; + *r_y = 0.0f; + } + } + else if (gps->flag & GP_STROKE_2DSPACE) { + float vec[3] = {pt->x, pt->y, 0.0f}; + int t_x, t_y; + + mul_m4_v3(gsc->mat, vec); + UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], &t_x, &t_y); + + if ((t_x == t_y) && (t_x == V2D_IS_CLIPPED)) { + /* XXX: Or should we just always use the values as-is? */ + *r_x = 0.0f; + *r_y = 0.0f; + } + else { + *r_x = (float)t_x; + *r_y = (float)t_y; + } + } + else { + if (subrect == NULL) { + /* normal 3D view (or view space) */ + *r_x = (pt->x / 100.0f * ar->winx); + *r_y = (pt->y / 100.0f * ar->winy); + } + else { + /* camera view, use subrect */ + *r_x = ((pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; + *r_y = ((pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; + } + } +} + /** * Project screenspace coordinates to 3D-space * |