diff options
author | Antonio Vazquez <blendergit@gmail.com> | 2020-06-09 16:41:26 +0300 |
---|---|---|
committer | Antonio Vazquez <blendergit@gmail.com> | 2020-06-09 16:46:55 +0300 |
commit | 37d68871b7c5e6dea36ce4e08013eb645ef550c9 (patch) | |
tree | bc221a53f7683850526a3fb51d22561c9d1714cf /source/blender/editors/gpencil | |
parent | c23b1de2cf8ad13f102d2d5484d09e280b36dda4 (diff) |
GPencil: Keep original stroke when reproject
When reproject a stroke sometimes is good to keep the copy of the original stroke to create volume effects
Related to T77639
{F8603513}
Reviewed By: mendio, pepeland
Maniphest Tasks: T77639
Differential Revision: https://developer.blender.org/D7963
50258
Diffstat (limited to 'source/blender/editors/gpencil')
-rw-r--r-- | source/blender/editors/gpencil/gpencil_edit.c | 138 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_utils.c | 131 |
2 files changed, 145 insertions, 124 deletions
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 8771fcb0c8d..76776b5c23c 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3633,43 +3633,25 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot) /** \name Stroke Re-project Operator * \{ */ -typedef enum eGP_ReprojectModes { - /* Axis */ - GP_REPROJECT_FRONT = 0, - GP_REPROJECT_SIDE, - GP_REPROJECT_TOP, - /* On same plane, parallel to view-plane. */ - GP_REPROJECT_VIEW, - /* Reprojected on to the scene geometry */ - GP_REPROJECT_SURFACE, - /* Reprojected on 3D cursor orientation */ - GP_REPROJECT_CURSOR, -} eGP_ReprojectModes; - static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); Scene *scene = CTX_data_scene(C); Main *bmain = CTX_data_main(C); - ToolSettings *ts = CTX_data_tool_settings(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Object *ob = CTX_data_active_object(C); ARegion *region = CTX_wm_region(C); - RegionView3D *rv3d = region->regiondata; - SnapObjectContext *sctx = NULL; int oldframe = (int)DEG_get_ctime(depsgraph); + const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); + const bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); + /* Init space conversion stuff. */ GP_SpaceConversion gsc = {NULL}; - eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); - - float origin[3]; - - /* init space conversion stuff */ + SnapObjectContext *sctx = NULL; gp_point_conversion_init(C, &gsc); + /* Init snap context for geometry projection. */ + sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); int cfra_prv = INT_MIN; - /* init snap context for geometry projection */ - sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); /* Go through each editable + selected stroke, adjusting each of its points one by one... */ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { @@ -3682,106 +3664,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) BKE_scene_graph_update_for_newframe(depsgraph, bmain); } - bGPDspoint *pt; - int i; - /* Adjust each point */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - float xy[2]; - - /* 3D to Screen-space */ - /* Note: We can't use gp_point_to_xy() here because that uses ints for the screen-space - * coordinates, resulting in lost precision, which in turn causes stair-stepping - * artifacts in the final points. */ - - bGPDspoint pt2; - gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2); - gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); - - /* Project stroke in one axis */ - if (ELEM(mode, - GP_REPROJECT_FRONT, - GP_REPROJECT_SIDE, - GP_REPROJECT_TOP, - GP_REPROJECT_CURSOR)) { - if (mode != GP_REPROJECT_CURSOR) { - ED_gpencil_drawing_reference_get(scene, ob, ts->gpencil_v3d_align, origin); - } - else { - copy_v3_v3(origin, scene->cursor.location); - } - - int axis = 0; - switch (mode) { - case GP_REPROJECT_FRONT: { - axis = 1; - break; - } - case GP_REPROJECT_SIDE: { - axis = 0; - break; - } - case GP_REPROJECT_TOP: { - axis = 2; - break; - } - case GP_REPROJECT_CURSOR: { - axis = 3; - break; - } - default: { - axis = 1; - break; - } - } - - ED_gp_project_point_to_plane(scene, ob, rv3d, origin, axis, &pt2); - - copy_v3_v3(&pt->x, &pt2.x); - - /* apply parent again */ - gp_apply_parent_point(depsgraph, ob, gpl, pt); - } - /* Project screen-space back to 3D space (from current perspective) - * so that all points have been treated the same way. */ - else if (mode == GP_REPROJECT_VIEW) { - /* Planar - All on same plane parallel to the view-plane. */ - gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); - } - else { - /* Geometry - Snap to surfaces of visible geometry */ - float ray_start[3]; - float ray_normal[3]; - /* magic value for initial depth copied from the default - * value of Python's Scene.ray_cast function - */ - float depth = 1.70141e+38f; - float location[3] = {0.0f, 0.0f, 0.0f}; - float normal[3] = {0.0f, 0.0f, 0.0f}; - - ED_view3d_win_to_ray(region, xy, &ray_start[0], &ray_normal[0]); - if (ED_transform_snap_object_project_ray(sctx, - depsgraph, - &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, - }, - &ray_start[0], - &ray_normal[0], - &depth, - &location[0], - &normal[0])) { - copy_v3_v3(&pt->x, location); - } - else { - /* Default to planar */ - gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); - } - } - - /* Unapply parent corrections */ - if (!ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP)) { - mul_m4_v3(gpstroke_iter.inverse_diff_mat, &pt->x); - } - } + ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original); } } GP_EDITABLE_STROKES_END(gpstroke_iter); @@ -3844,6 +3727,13 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum( ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", ""); + + RNA_def_boolean( + ot->srna, + "keep_original", + 0, + "Keep Original", + "Keep original strokes and create a copy before reprojecting instead of reproject them"); } static int gp_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 876fa7c9874..467b7ee86bf 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -1118,6 +1118,137 @@ void ED_gp_project_stroke_to_plane(const Scene *scene, } } +/* Reproject selected strokes */ +void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, + const GP_SpaceConversion *gsc, + SnapObjectContext *sctx, + bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *gps, + const eGP_ReprojectModes mode, + const bool keep_original) +{ + ToolSettings *ts = gsc->scene->toolsettings; + ARegion *region = gsc->region; + RegionView3D *rv3d = region->regiondata; + + float diff_mat[4][4], inverse_diff_mat[4][4]; + BKE_gpencil_parent_matrix_get(depsgraph, gsc->ob, gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + float origin[3]; + if (mode != GP_REPROJECT_CURSOR) { + ED_gpencil_drawing_reference_get(gsc->scene, gsc->ob, ts->gpencil_v3d_align, origin); + } + else { + copy_v3_v3(origin, gsc->scene->cursor.location); + } + + bGPDspoint *pt; + int i; + + /* If keep original, do a copy. */ + bGPDstroke *gps_active = gps; + /* if duplicate, deselect all points. */ + if (keep_original) { + gps_active = BKE_gpencil_stroke_duplicate(gps, true); + gps_active->flag &= ~GP_STROKE_SELECT; + for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + /* Add to frame. */ + BLI_addtail(&gpf->strokes, gps_active); + } + + /* Adjust each point */ + for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) { + float xy[2]; + + /* 3D to Screen-space */ + /* Note: We can't use gp_point_to_xy() here because that uses ints for the screen-space + * coordinates, resulting in lost precision, which in turn causes stair-stepping + * artifacts in the final points. */ + + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy_fl(gsc, gps_active, &pt2, &xy[0], &xy[1]); + + /* Project stroke in one axis */ + if (ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP, GP_REPROJECT_CURSOR)) { + int axis = 0; + switch (mode) { + case GP_REPROJECT_FRONT: { + axis = 1; + break; + } + case GP_REPROJECT_SIDE: { + axis = 0; + break; + } + case GP_REPROJECT_TOP: { + axis = 2; + break; + } + case GP_REPROJECT_CURSOR: { + axis = 3; + break; + } + default: { + axis = 1; + break; + } + } + + ED_gp_project_point_to_plane(gsc->scene, gsc->ob, rv3d, origin, axis, &pt2); + + copy_v3_v3(&pt->x, &pt2.x); + + /* apply parent again */ + gp_apply_parent_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. */ + else if (mode == GP_REPROJECT_VIEW) { + /* Planar - All on same plane parallel to the view-plane. */ + gp_point_xy_to_3d(gsc, gsc->scene, xy, &pt->x); + } + else { + /* Geometry - Snap to surfaces of visible geometry */ + float ray_start[3]; + float ray_normal[3]; + /* magic value for initial depth copied from the default + * value of Python's Scene.ray_cast function + */ + float depth = 1.70141e+38f; + float location[3] = {0.0f, 0.0f, 0.0f}; + float normal[3] = {0.0f, 0.0f, 0.0f}; + + ED_view3d_win_to_ray(region, xy, &ray_start[0], &ray_normal[0]); + if (ED_transform_snap_object_project_ray(sctx, + depsgraph, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + }, + &ray_start[0], + &ray_normal[0], + &depth, + &location[0], + &normal[0])) { + copy_v3_v3(&pt->x, location); + } + else { + /* Default to planar */ + gp_point_xy_to_3d(gsc, gsc->scene, xy, &pt->x); + } + } + + /* Unapply parent corrections */ + if (!ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP)) { + mul_m4_v3(inverse_diff_mat, &pt->x); + } + } +} + /** * Reproject given point to a plane locked to axis to avoid stroke offset * \param[in,out] pt: Point to affect |