[Bf-blender-cvs] [299bb01] master: GPencil: "Reproject Strokes" operator

Joshua Leung noreply at git.blender.org
Mon Aug 29 04:52:49 CEST 2016


Commit: 299bb019b5e5bf48098a85f510d3daaaa99f53d5
Author: Joshua Leung
Date:   Mon Aug 29 03:03:09 2016 +1200
Branches: master
https://developer.blender.org/rB299bb019b5e5bf48098a85f510d3daaaa99f53d5

GPencil: "Reproject Strokes" operator

A common problem encountered by artists was that they would accidentally move
the 3D cursor while drawing, causing their strokes to end up in weird places in
3D space when viewing the drawing again from other perspectives.

This operator helps fix up this mess by taking the selected strokes, projecting them
to screenspace, and then back to 3D space again. As a result, it should be as if
you had directly drawn the whole thing again, but from the current viewpoint instead.
Unfortunately, if there was originally some depth information present (i.e. you already
started reshaping the sketch in 3D), then that will get lost during this process.
But so far, my tests indicate that this seems to work well enough.

===================================================================

M	release/scripts/startup/bl_ui/properties_grease_pencil_common.py
M	source/blender/editors/gpencil/gpencil_edit.c
M	source/blender/editors/gpencil/gpencil_intern.h
M	source/blender/editors/gpencil/gpencil_ops.c
M	source/blender/editors/gpencil/gpencil_utils.c

===================================================================

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 80ab4e4..ba7006d 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 3fe53e3..d9b6c80 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 e8d007c..4178d49 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 50f4e79..ae1c555 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 a61c83b..564ba63 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
  *




More information about the Bf-blender-cvs mailing list