Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_paint.c
| Show All 40 Lines | |||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_rand.h" | #include "BLI_rand.h" | ||||
| #include "BLI_math_geom.h" | #include "BLI_math_geom.h" | ||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| #include "PIL_time.h" | #include "PIL_time.h" | ||||
| #include "DNA_meshdata_types.h" | |||||
| #include "DNA_object_types.h" | |||||
| #include "DNA_scene_types.h" | |||||
| #include "DNA_gpencil_types.h" | |||||
| #include "DNA_material_types.h" | |||||
| #include "DNA_brush_types.h" | |||||
| #include "DNA_windowmanager_types.h" | |||||
| #include "BKE_colortools.h" | #include "BKE_colortools.h" | ||||
| #include "BKE_main.h" | |||||
| #include "BKE_brush.h" | |||||
| #include "BKE_paint.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "BKE_gpencil.h" | #include "BKE_gpencil.h" | ||||
| #include "BKE_main.h" | #include "BKE_main.h" | ||||
| #include "BKE_paint.h" | #include "BKE_paint.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_layer.h" | |||||
| #include "BKE_material.h" | |||||
| #include "BKE_screen.h" | #include "BKE_screen.h" | ||||
| #include "BKE_tracking.h" | #include "BKE_tracking.h" | ||||
| #include "DNA_object_types.h" | |||||
| #include "DNA_scene_types.h" | |||||
| #include "DNA_gpencil_types.h" | |||||
| #include "DNA_brush_types.h" | |||||
| #include "DNA_windowmanager_types.h" | |||||
| #include "UI_view2d.h" | #include "UI_view2d.h" | ||||
| #include "ED_gpencil.h" | #include "ED_gpencil.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_object.h" | |||||
| #include "ED_view3d.h" | #include "ED_view3d.h" | ||||
| #include "ED_clip.h" | #include "ED_clip.h" | ||||
| #include "BIF_glutil.h" | #include "BIF_glutil.h" | ||||
| #include "GPU_immediate.h" | #include "GPU_immediate.h" | ||||
| #include "GPU_immediate_util.h" | #include "GPU_immediate_util.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "DEG_depsgraph_query.h" | |||||
| #include "gpencil_intern.h" | #include "gpencil_intern.h" | ||||
| /* ******************************************* */ | /* ******************************************* */ | ||||
| /* 'Globals' and Defines */ | /* 'Globals' and Defines */ | ||||
| /* values for tGPsdata->status */ | /* values for tGPsdata->status */ | ||||
| typedef enum eGPencil_PaintStatus { | typedef enum eGPencil_PaintStatus { | ||||
| Show All 12 Lines | |||||
| } eGP_StrokeAdd_Result; | } eGP_StrokeAdd_Result; | ||||
| /* Runtime flags */ | /* Runtime flags */ | ||||
| typedef enum eGPencil_PaintFlags { | typedef enum eGPencil_PaintFlags { | ||||
| GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ | GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ | ||||
| GP_PAINTFLAG_STROKEADDED = (1 << 1), | GP_PAINTFLAG_STROKEADDED = (1 << 1), | ||||
| GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), | GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), | ||||
| GP_PAINTFLAG_SELECTMASK = (1 << 3), | GP_PAINTFLAG_SELECTMASK = (1 << 3), | ||||
| GP_PAINTFLAG_HARD_ERASER = (1 << 4), | |||||
| GP_PAINTFLAG_STROKE_ERASER = (1 << 5), | |||||
| } eGPencil_PaintFlags; | } eGPencil_PaintFlags; | ||||
| /* Temporary 'Stroke' Operation data | /* Temporary 'Stroke' Operation data | ||||
| * "p" = op->customdata | * "p" = op->customdata | ||||
| */ | */ | ||||
| typedef struct tGPsdata { | typedef struct tGPsdata { | ||||
| Main *bmain; | bContext *C; | ||||
| Main *bmain; /* main database pointer */ | |||||
| Scene *scene; /* current scene from context */ | Scene *scene; /* current scene from context */ | ||||
| struct Depsgraph *depsgraph; | struct Depsgraph *depsgraph; | ||||
| Object *ob; /* current object */ | |||||
| wmWindow *win; /* window where painting originated */ | wmWindow *win; /* window where painting originated */ | ||||
| ScrArea *sa; /* area where painting originated */ | ScrArea *sa; /* area where painting originated */ | ||||
| ARegion *ar; /* region where painting originated */ | ARegion *ar; /* region where painting originated */ | ||||
| View2D *v2d; /* needed for GP_STROKE_2DSPACE */ | View2D *v2d; /* needed for GP_STROKE_2DSPACE */ | ||||
| rctf *subrect; /* for using the camera rect within the 3d view */ | rctf *subrect; /* for using the camera rect within the 3d view */ | ||||
| rctf subrect_data; | rctf subrect_data; | ||||
| GP_SpaceConversion gsc; /* settings to pass to gp_points_to_xy() */ | GP_SpaceConversion gsc; /* settings to pass to gp_points_to_xy() */ | ||||
| Show All 28 Lines | typedef struct tGPsdata { | ||||
| float imat[4][4]; /* inverted transformation matrix applying when converting coords from screen-space | float imat[4][4]; /* inverted transformation matrix applying when converting coords from screen-space | ||||
| * to region space */ | * to region space */ | ||||
| float mat[4][4]; | float mat[4][4]; | ||||
| float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */ | float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */ | ||||
| void *erasercursor; /* radial cursor data for drawing eraser */ | void *erasercursor; /* radial cursor data for drawing eraser */ | ||||
| bGPDpalettecolor *palettecolor; /* current palette color */ | /* mat settings are only used for 3D view */ | ||||
| bGPDbrush *brush; /* current drawing brush */ | Material *material; /* current material */ | ||||
| Brush *brush; /* current drawing brush */ | |||||
| Brush *eraser; /* default eraser brush */ | |||||
| short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ | short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ | ||||
| int lock_axis; /* lock drawing to one axis */ | int lock_axis; /* lock drawing to one axis */ | ||||
| bool disable_fill; /* the stroke is no fill mode */ | |||||
| RNG *rng; | RNG *rng; | ||||
| short keymodifier; /* key used for invoking the operator */ | short keymodifier; /* key used for invoking the operator */ | ||||
| short shift; /* shift modifier flag */ | |||||
| float totpixlen; /* size in pixels for uv calculation */ | |||||
| ReportList *reports; | |||||
| } tGPsdata; | } tGPsdata; | ||||
| /* ------ */ | /* ------ */ | ||||
| /* Macros for accessing sensitivity thresholds... */ | /* Macros for accessing sensitivity thresholds... */ | ||||
| /* minimum number of pixels mouse should move before new point created */ | /* minimum number of pixels mouse should move before new point created */ | ||||
| #define MIN_MANHATTEN_PX (U.gp_manhattendist) | #define MIN_MANHATTEN_PX (U.gp_manhattendist) | ||||
| /* minimum length of new segment before new point can be added */ | /* minimum length of new segment before new point can be added */ | ||||
| #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) | #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) | ||||
| static void gp_update_cache(bGPdata *gpd) | |||||
| { | |||||
| if (gpd) { | |||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| gpd->flag |= GP_DATA_CACHE_IS_DIRTY; | |||||
| } | |||||
| } | |||||
| static bool gp_stroke_added_check(tGPsdata *p) | static bool gp_stroke_added_check(tGPsdata *p) | ||||
| { | { | ||||
| return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); | return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); | ||||
| } | } | ||||
| static void gp_stroke_added_enable(tGPsdata *p) | static void gp_stroke_added_enable(tGPsdata *p) | ||||
| { | { | ||||
| BLI_assert(p->gpf->strokes.last != NULL); | BLI_assert(p->gpf->strokes.last != NULL); | ||||
| p->flags |= GP_PAINTFLAG_STROKEADDED; | p->flags |= GP_PAINTFLAG_STROKEADDED; | ||||
| /* drawing batch cache is dirty now */ | |||||
| gp_update_cache(p->gpd); | |||||
| } | } | ||||
| /* ------ */ | /* ------ */ | ||||
| /* Forward defines for some functions... */ | /* Forward defines for some functions... */ | ||||
| static void gp_session_validatebuffer(tGPsdata *p); | static void gp_session_validatebuffer(tGPsdata *p); | ||||
| /* ******************************************* */ | /* ******************************************* */ | ||||
| /* Context Wrangling... */ | /* Context Wrangling... */ | ||||
| /* check if context is suitable for drawing */ | /* check if context is suitable for drawing */ | ||||
| static int gpencil_draw_poll(bContext *C) | static int gpencil_draw_poll(bContext *C) | ||||
| { | { | ||||
| if (ED_operator_regionactive(C)) { | if (ED_operator_regionactive(C)) { | ||||
| ScrArea *sa = CTX_wm_area(C); | |||||
| if (!ELEM(sa->spacetype, SPACE_VIEW3D)) { | |||||
| /* check if current context can support GPencil data */ | /* check if current context can support GPencil data */ | ||||
| if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { | if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { | ||||
| /* check if Grease Pencil isn't already running */ | /* check if Grease Pencil isn't already running */ | ||||
| if (ED_gpencil_session_active() == 0) | if (ED_gpencil_session_active() == 0) | ||||
| return 1; | return 1; | ||||
| else | else | ||||
| CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); | CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); | ||||
| } | } | ||||
| else { | else { | ||||
| CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); | CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); | ||||
| } | } | ||||
| return 0; | |||||
| } | } | ||||
| /* 3D Viewport */ | |||||
| else { | else { | ||||
| CTX_wm_operator_poll_msg_set(C, "Active region not set"); | if (ED_gpencil_session_active() == 0) { | ||||
| return 1; | |||||
| } | } | ||||
| else { | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| } | |||||
| } | |||||
| else { | |||||
| CTX_wm_operator_poll_msg_set(C, "Active region not set"); | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| /* check if projecting strokes into 3d-geometry in the 3D-View */ | /* check if projecting strokes into 3d-geometry in the 3D-View */ | ||||
| static bool gpencil_project_check(tGPsdata *p) | static bool gpencil_project_check(tGPsdata *p) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | bGPdata *gpd = p->gpd; | ||||
| return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); | return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); | ||||
| } | } | ||||
| /* ******************************************* */ | /* ******************************************* */ | ||||
| /* Calculations/Conversions */ | /* Calculations/Conversions */ | ||||
| /* Utilities --------------------------------- */ | /* Utilities --------------------------------- */ | ||||
| /* get the reference point for stroke-point conversions */ | /* get the reference point for stroke-point conversions */ | ||||
| static void gp_get_3d_reference(tGPsdata *p, float vec[3]) | static void gp_get_3d_reference(tGPsdata *p, float vec[3]) | ||||
| { | { | ||||
| View3D *v3d = p->sa->spacedata.first; | View3D *v3d = p->sa->spacedata.first; | ||||
| const float *fp = ED_view3d_cursor3d_get(p->scene, v3d)->location; | Object *ob = NULL; | ||||
| /* the reference point used depends on the owner... */ | |||||
| #if 0 /* XXX: disabled for now, since we can't draw relative to the owner yet */ | |||||
| if (p->ownerPtr.type == &RNA_Object) { | if (p->ownerPtr.type == &RNA_Object) { | ||||
| Object *ob = (Object *)p->ownerPtr.data; | ob = (Object *)p->ownerPtr.data; | ||||
| /* active Object | |||||
| * - use relative distance of 3D-cursor from object center | |||||
| */ | |||||
| sub_v3_v3v3(vec, fp, ob->loc); | |||||
| } | |||||
| else | |||||
| #endif | |||||
| { | |||||
| /* use 3D-cursor */ | |||||
| copy_v3_v3(vec, fp); | |||||
| } | } | ||||
| ED_gp_get_drawing_reference(v3d, p->scene, ob, p->gpl, *p->align_flag, vec); | |||||
| } | } | ||||
| /* Stroke Editing ---------------------------- */ | /* Stroke Editing ---------------------------- */ | ||||
| /* check if the current mouse position is suitable for adding a new point */ | /* check if the current mouse position is suitable for adding a new point */ | ||||
| static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) | static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) | ||||
| { | { | ||||
| Brush *brush = p->brush; | |||||
| int dx = abs(mval[0] - pmval[0]); | int dx = abs(mval[0] - pmval[0]); | ||||
| int dy = abs(mval[1] - pmval[1]); | int dy = abs(mval[1] - pmval[1]); | ||||
| brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; | |||||
| /* if buffer is empty, just let this go through (i.e. so that dots will work) */ | /* if buffer is empty, just let this go through (i.e. so that dots will work) */ | ||||
| if (p->gpd->sbuffer_size == 0) | if (p->gpd->sbuffer_size == 0) { | ||||
| return true; | return true; | ||||
| } | |||||
| /* if lazy mouse, check minimum distance */ | |||||
| else if (GPENCIL_LAZY_MODE(brush, p->shift)) { | |||||
| brush->gpencil_settings->flag |= GP_BRUSH_STABILIZE_MOUSE_TEMP; | |||||
| if ((dx * dx + dy * dy) > (brush->smooth_stroke_radius * brush->smooth_stroke_radius)) { | |||||
| return true; | |||||
| } | |||||
| else { | |||||
| /* If the mouse is moving within the radius of the last move, | |||||
| * don't update the mouse position. This allows sharp turns. */ | |||||
| copy_v2_v2_int(p->mval, p->mvalo); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| /* check if mouse moved at least certain distance on both axes (best case) | /* check if mouse moved at least certain distance on both axes (best case) | ||||
| * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand | * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand | ||||
| */ | */ | ||||
| else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) | else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) | ||||
| return true; | return true; | ||||
| /* check if the distance since the last point is significant enough | /* check if the distance since the last point is significant enough | ||||
| * - prevents points being added too densely | * - prevents points being added too densely | ||||
| * - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though | * - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though | ||||
| */ | */ | ||||
| else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) | else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) | ||||
| return true; | return true; | ||||
| /* mouse 'didn't move' */ | /* mouse 'didn't move' */ | ||||
| else | else | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* reproject the points of the stroke to a plane locked to axis to avoid stroke offset */ | |||||
| static void gp_project_points_to_plane(RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis) | |||||
| { | |||||
| float plane_normal[3]; | |||||
| float vn[3]; | |||||
| float ray[3]; | |||||
| float rpoint[3]; | |||||
| /* normal vector for a plane locked to axis */ | |||||
| zero_v3(plane_normal); | |||||
| plane_normal[axis] = 1.0f; | |||||
| /* Reproject the points in the plane */ | |||||
| for (int i = 0; i < gps->totpoints; i++) { | |||||
| bGPDspoint *pt = &gps->points[i]; | |||||
| /* get a vector from the point with the current view direction of the viewport */ | |||||
| ED_view3d_global_to_vector(rv3d, &pt->x, vn); | |||||
| /* calculate line extrem point to create a ray that cross the plane */ | |||||
| mul_v3_fl(vn, -50.0f); | |||||
| add_v3_v3v3(ray, &pt->x, vn); | |||||
| /* if the line never intersect, the point is not changed */ | |||||
| if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { | |||||
| copy_v3_v3(&pt->x, rpoint); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* reproject stroke to plane locked to axis in 3d cursor location */ | /* reproject stroke to plane locked to axis in 3d cursor location */ | ||||
| static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) | static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | bGPdata *gpd = p->gpd; | ||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| float origin[3]; | float origin[3]; | ||||
| float cursor[3]; | |||||
| RegionView3D *rv3d = p->ar->regiondata; | RegionView3D *rv3d = p->ar->regiondata; | ||||
| /* verify the stroke mode is CURSOR 3d space mode */ | /* verify the stroke mode is CURSOR 3d space mode */ | ||||
| if ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { | if ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { | if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| if ((*p->align_flag & GP_PROJECT_DEPTH_VIEW) || (*p->align_flag & GP_PROJECT_DEPTH_STROKE)) { | if ((*p->align_flag & GP_PROJECT_DEPTH_VIEW) || (*p->align_flag & GP_PROJECT_DEPTH_STROKE)) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* get 3d cursor and set origin for locked axis only. Uses axis-1 because the enum for XYZ start with 1 */ | /* get drawing origin */ | ||||
| gp_get_3d_reference(p, cursor); | gp_get_3d_reference(p, origin); | ||||
| zero_v3(origin); | ED_gp_project_stroke_to_plane(obact, rv3d, gps, origin, p->lock_axis - 1, p->scene->toolsettings->gpencil_src); | ||||
| origin[p->lock_axis - 1] = cursor[p->lock_axis - 1]; | |||||
| gp_project_points_to_plane(rv3d, gps, origin, p->lock_axis - 1); | |||||
| } | } | ||||
| /* convert screen-coordinates to buffer-coordinates */ | /* convert screen-coordinates to buffer-coordinates */ | ||||
| /* XXX this method needs a total overhaul! */ | /* XXX this method needs a total overhaul! */ | ||||
| static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3], float *depth) | static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3], float *depth) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | bGPdata *gpd = p->gpd; | ||||
| /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ | /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ | ||||
| if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) { | if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) { | ||||
| if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval, out, 0, depth))) { | if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval, out, 0, depth))) { | ||||
| /* projecting onto 3D-Geometry | /* projecting onto 3D-Geometry | ||||
| * - nothing more needs to be done here, since view_autodist_simple() has already done it | * - nothing more needs to be done here, since view_autodist_simple() has already done it | ||||
| */ | */ | ||||
| } | } | ||||
| else { | else { | ||||
| float mval_prj[2]; | float mval_prj[2]; | ||||
| float rvec[3], dvec[3]; | float rvec[3], dvec[3]; | ||||
| float mval_f[2] = {UNPACK2(mval)}; | float mval_f[2]; | ||||
| copy_v2fl_v2i(mval_f, mval); | |||||
| float zfac; | float zfac; | ||||
| /* Current method just converts each point in screen-coordinates to | /* Current method just converts each point in screen-coordinates to | ||||
| * 3D-coordinates using the 3D-cursor as reference. In general, this | * 3D-coordinates using the 3D-cursor as reference. In general, this | ||||
| * works OK, but it could of course be improved. | * works OK, but it could of course be improved. | ||||
| * | * | ||||
| * TODO: | * TODO: | ||||
| * - investigate using nearest point(s) on a previous stroke as | * - investigate using nearest point(s) on a previous stroke as | ||||
| Show All 29 Lines | else { | ||||
| else { /* camera view, use subrect */ | else { /* camera view, use subrect */ | ||||
| out[0] = ((mval[0] - p->subrect->xmin) / BLI_rctf_size_x(p->subrect)) * 100; | out[0] = ((mval[0] - p->subrect->xmin) / BLI_rctf_size_x(p->subrect)) * 100; | ||||
| out[1] = ((mval[1] - p->subrect->ymin) / BLI_rctf_size_y(p->subrect)) * 100; | out[1] = ((mval[1] - p->subrect->ymin) / BLI_rctf_size_y(p->subrect)) * 100; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* apply jitter to stroke */ | /* apply jitter to stroke */ | ||||
| static void gp_brush_jitter( | static void gp_brush_jitter(bGPdata *gpd, Brush *brush, tGPspoint *pt, const int mval[2], int r_mval[2], RNG *rng) | ||||
| bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2], int r_mval[2], RNG *rng) | |||||
| { | { | ||||
| float pressure = pt->pressure; | float pressure = pt->pressure; | ||||
| float tmp_pressure = pt->pressure; | float tmp_pressure = pt->pressure; | ||||
| if (brush->draw_jitter > 0.0f) { | if (brush->gpencil_settings->draw_jitter > 0.0f) { | ||||
| float curvef = curvemapping_evaluateF(brush->cur_jitter, 0, pressure); | float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, pressure); | ||||
| tmp_pressure = curvef * brush->draw_sensitivity; | tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; | ||||
| } | } | ||||
| const float exfactor = (brush->draw_jitter + 2.0f) * (brush->draw_jitter + 2.0f); /* exponential value */ | const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) * (brush->gpencil_settings->draw_jitter + 2.0f); /* exponential value */ | ||||
| const float fac = BLI_rng_get_float(rng) * exfactor * tmp_pressure; | const float fac = BLI_rng_get_float(rng) * exfactor * tmp_pressure; | ||||
| /* Jitter is applied perpendicular to the mouse movement vector (2D space) */ | /* Jitter is applied perpendicular to the mouse movement vector (2D space) */ | ||||
| float mvec[2], svec[2]; | float mvec[2], svec[2]; | ||||
| /* mouse movement in ints -> floats */ | /* mouse movement in ints -> floats */ | ||||
| if (gpd->sbuffer_size > 1) { | if (gpd->sbuffer_size > 1) { | ||||
| mvec[0] = (float)(mval[0] - (pt - 1)->x); | mvec[0] = (float)(mval[0] - (pt - 1)->x); | ||||
| mvec[1] = (float)(mval[1] - (pt - 1)->y); | mvec[1] = (float)(mval[1] - (pt - 1)->y); | ||||
| normalize_v2(mvec); | normalize_v2(mvec); | ||||
| Show All 14 Lines | static void gp_brush_jitter(bGPdata *gpd, Brush *brush, tGPspoint *pt, const int mval[2], int r_mval[2], RNG *rng) | ||||
| } | } | ||||
| r_mval[0] = mval[0] + svec[0]; | r_mval[0] = mval[0] + svec[0]; | ||||
| r_mval[1] = mval[1] + svec[1]; | r_mval[1] = mval[1] + svec[1]; | ||||
| } | } | ||||
| /* apply pressure change depending of the angle of the stroke to simulate a pen with shape */ | /* apply pressure change depending of the angle of the stroke to simulate a pen with shape */ | ||||
| static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2]) | static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const int mval[2]) | ||||
| { | { | ||||
| float mvec[2]; | float mvec[2]; | ||||
| float sen = brush->draw_angle_factor; /* sensitivity */; | float sen = brush->gpencil_settings->draw_angle_factor; /* sensitivity */; | ||||
| float fac; | float fac; | ||||
| float mpressure; | float mpressure; | ||||
| float angle = brush->draw_angle; /* default angle of brush in radians */; | float angle = brush->gpencil_settings->draw_angle; /* default angle of brush in radians */; | ||||
| float v0[2] = { cos(angle), sin(angle) }; /* angle vector of the brush with full thickness */ | float v0[2] = { cos(angle), sin(angle) }; /* angle vector of the brush with full thickness */ | ||||
| /* Apply to first point (only if there are 2 points because before no data to do it ) */ | /* Apply to first point (only if there are 2 points because before no data to do it ) */ | ||||
| if (gpd->sbuffer_size == 1) { | if (gpd->sbuffer_size == 1) { | ||||
| mvec[0] = (float)(mval[0] - (pt - 1)->x); | mvec[0] = (float)(mval[0] - (pt - 1)->x); | ||||
| mvec[1] = (float)(mval[1] - (pt - 1)->y); | mvec[1] = (float)(mval[1] - (pt - 1)->y); | ||||
| normalize_v2(mvec); | normalize_v2(mvec); | ||||
| Show All 15 Lines | if (gpd->sbuffer_size >= 1) { | ||||
| mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f); | mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f); | ||||
| pt->pressure = mpressure; | pt->pressure = mpressure; | ||||
| CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); | CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); | ||||
| } | } | ||||
| } | } | ||||
| /* Apply smooth to buffer while drawing | |||||
| * to smooth point C, use 2 before (A, B) and current point (D): | |||||
| * | |||||
| * A----B-----C------D | |||||
| * | |||||
| * \param p Temp data | |||||
| * \param inf Influence factor | |||||
| * \param idx Index of the last point (need minimum 3 points in the array) | |||||
| */ | |||||
| static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) | |||||
| { | |||||
| bGPdata *gpd = p->gpd; | |||||
| short num_points = gpd->sbuffer_size; | |||||
| /* Do nothing if not enough points to smooth out */ | |||||
| if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) { | |||||
| return; | |||||
| } | |||||
| tGPspoint *points = (tGPspoint *)gpd->sbuffer; | |||||
| float steps = 4.0f; | |||||
| if (idx < 4) { | |||||
| steps--; | |||||
| } | |||||
| tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL; | |||||
| tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL; | |||||
| tGPspoint *ptc = idx >= 2 ? &points[idx - 2] : NULL; | |||||
| tGPspoint *ptd = &points[idx - 1]; | |||||
| float sco[2] = { 0.0f }; | |||||
| float a[2], b[2], c[2], d[2]; | |||||
| const float average_fac = 1.0f / steps; | |||||
| /* Compute smoothed coordinate by taking the ones nearby */ | |||||
| if (pta) { | |||||
| copy_v2fl_v2i(a, &pta->x); | |||||
| madd_v2_v2fl(sco, a, average_fac); | |||||
| } | |||||
| if (ptb) { | |||||
| copy_v2fl_v2i(b, &ptb->x); | |||||
| madd_v2_v2fl(sco, b, average_fac); | |||||
| } | |||||
| if (ptc) { | |||||
| copy_v2fl_v2i(c, &ptc->x); | |||||
| madd_v2_v2fl(sco, c, average_fac); | |||||
| } | |||||
| if (ptd) { | |||||
| copy_v2fl_v2i(d, &ptd->x); | |||||
| madd_v2_v2fl(sco, d, average_fac); | |||||
| } | |||||
| /* Based on influence factor, blend between original and optimal smoothed coordinate */ | |||||
| interp_v2_v2v2(c, c, sco, inf); | |||||
| round_v2i_v2fl(&ptc->x, c); | |||||
| } | |||||
| /* add current stroke-point to buffer (returns whether point was successfully added) */ | /* add current stroke-point to buffer (returns whether point was successfully added) */ | ||||
| static short gp_stroke_addpoint( | static short gp_stroke_addpoint( | ||||
| tGPsdata *p, const int mval[2], float pressure, double curtime) | tGPsdata *p, const int mval[2], float pressure, double curtime) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | bGPdata *gpd = p->gpd; | ||||
| bGPDbrush *brush = p->brush; | Brush *brush = p->brush; | ||||
| tGPspoint *pt; | tGPspoint *pt; | ||||
| ToolSettings *ts = p->scene->toolsettings; | ToolSettings *ts = p->scene->toolsettings; | ||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| Depsgraph *depsgraph = p->depsgraph; \ | |||||
| RegionView3D *rv3d = p->ar->regiondata; | |||||
| View3D *v3d = p->sa->spacedata.first; | |||||
| MaterialGPencilStyle *gp_style = p->material->gp_style; | |||||
| /* check painting mode */ | /* check painting mode */ | ||||
| if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { | if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { | ||||
| /* straight lines only - i.e. only store start and end point in buffer */ | /* straight lines only - i.e. only store start and end point in buffer */ | ||||
| if (gpd->sbuffer_size == 0) { | if (gpd->sbuffer_size == 0) { | ||||
| /* first point in buffer (start point) */ | /* first point in buffer (start point) */ | ||||
| pt = (tGPspoint *)(gpd->sbuffer); | pt = (tGPspoint *)(gpd->sbuffer); | ||||
| Show All 17 Lines | else { | ||||
| pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ | pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ | ||||
| pt->strength = 1.0f; | pt->strength = 1.0f; | ||||
| pt->time = (float)(curtime - p->inittime); | pt->time = (float)(curtime - p->inittime); | ||||
| /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ | /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ | ||||
| gpd->sbuffer_size = 2; | gpd->sbuffer_size = 2; | ||||
| } | } | ||||
| /* tag depsgraph to update object */ | |||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); | |||||
| /* can keep carrying on this way :) */ | /* can keep carrying on this way :) */ | ||||
| return GP_STROKEADD_NORMAL; | return GP_STROKEADD_NORMAL; | ||||
| } | } | ||||
| else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ | else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ | ||||
| /* check if still room in buffer */ | /* check if still room in buffer */ | ||||
| if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX) | if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX) | ||||
| return GP_STROKEADD_OVERFLOW; | return GP_STROKEADD_OVERFLOW; | ||||
| /* get pointer to destination point */ | /* get pointer to destination point */ | ||||
| pt = ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size); | pt = ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size); | ||||
| /* store settings */ | /* store settings */ | ||||
| /* pressure */ | /* pressure */ | ||||
| if (brush->flag & GP_BRUSH_USE_PRESSURE) { | if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { | ||||
| float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); | float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_sensitivity, 0, pressure); | ||||
| pt->pressure = curvef * brush->draw_sensitivity; | pt->pressure = curvef * brush->gpencil_settings->draw_sensitivity; | ||||
| } | } | ||||
| else { | else { | ||||
| pt->pressure = 1.0f; | pt->pressure = 1.0f; | ||||
| } | } | ||||
| /* Apply jitter to position */ | /* Apply jitter to position */ | ||||
| if (brush->draw_jitter > 0.0f) { | if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (brush->gpencil_settings->draw_jitter > 0.0f)) { | ||||
| int r_mval[2]; | int r_mval[2]; | ||||
| gp_brush_jitter(gpd, brush, pt, mval, r_mval, p->rng); | gp_brush_jitter(gpd, brush, pt, mval, r_mval, p->rng); | ||||
| copy_v2_v2_int(&pt->x, r_mval); | copy_v2_v2_int(&pt->x, r_mval); | ||||
| } | } | ||||
| else { | else { | ||||
| copy_v2_v2_int(&pt->x, mval); | copy_v2_v2_int(&pt->x, mval); | ||||
| } | } | ||||
| /* apply randomness to pressure */ | /* apply randomness to pressure */ | ||||
| if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_PRESSURE)) { | if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && | ||||
| float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); | (brush->gpencil_settings->draw_random_press > 0.0f)) | ||||
| float tmp_pressure = curvef * brush->draw_sensitivity; | { | ||||
| float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_sensitivity, 0, pressure); | |||||
| float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; | |||||
| if (BLI_rng_get_float(p->rng) > 0.5f) { | if (BLI_rng_get_float(p->rng) > 0.5f) { | ||||
| pt->pressure -= tmp_pressure * brush->draw_random_press * BLI_rng_get_float(p->rng); | pt->pressure -= tmp_pressure * brush->gpencil_settings->draw_random_press * BLI_rng_get_float(p->rng); | ||||
| } | } | ||||
| else { | else { | ||||
| pt->pressure += tmp_pressure * brush->draw_random_press * BLI_rng_get_float(p->rng); | pt->pressure += tmp_pressure * brush->gpencil_settings->draw_random_press * BLI_rng_get_float(p->rng); | ||||
| } | } | ||||
| CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| } | } | ||||
| /* apply randomness to uv texture rotation */ | |||||
| if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (brush->gpencil_settings->uv_random > 0.0f)) { | |||||
| if (BLI_rng_get_float(p->rng) > 0.5f) { | |||||
| pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI * -1) * brush->gpencil_settings->uv_random; | |||||
| } | |||||
| else { | |||||
| pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI) * brush->gpencil_settings->uv_random; | |||||
| } | |||||
| CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); | |||||
| } | |||||
| else { | |||||
| pt->uv_rot = 0.0f; | |||||
| } | |||||
| /* apply angle of stroke to brush size */ | /* apply angle of stroke to brush size */ | ||||
| if (brush->draw_angle_factor > 0.0f) { | if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && | ||||
| (brush->gpencil_settings->draw_angle_factor > 0.0f)) | |||||
| { | |||||
| gp_brush_angle(gpd, brush, pt, mval); | gp_brush_angle(gpd, brush, pt, mval); | ||||
| } | } | ||||
| /* color strength */ | /* color strength */ | ||||
| if (brush->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { | if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { | ||||
| float curvef = curvemapping_evaluateF(brush->cur_strength, 0, pressure); | float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0, pressure); | ||||
| float tmp_pressure = curvef * brush->draw_sensitivity; | float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; | ||||
| pt->strength = tmp_pressure * brush->draw_strength; | pt->strength = tmp_pressure * brush->gpencil_settings->draw_strength; | ||||
| } | } | ||||
| else { | else { | ||||
| pt->strength = brush->draw_strength; | pt->strength = brush->gpencil_settings->draw_strength; | ||||
| } | } | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| /* apply randomness to color strength */ | /* apply randomness to color strength */ | ||||
| if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_STRENGTH)) { | if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && | ||||
| (brush->gpencil_settings->draw_random_strength > 0.0f)) | |||||
| { | |||||
| if (BLI_rng_get_float(p->rng) > 0.5f) { | if (BLI_rng_get_float(p->rng) > 0.5f) { | ||||
| pt->strength -= pt->strength * brush->draw_random_press * BLI_rng_get_float(p->rng); | pt->strength -= pt->strength * brush->gpencil_settings->draw_random_strength * BLI_rng_get_float(p->rng); | ||||
| } | } | ||||
| else { | else { | ||||
| pt->strength += pt->strength * brush->draw_random_press * BLI_rng_get_float(p->rng); | pt->strength += pt->strength * brush->gpencil_settings->draw_random_strength * BLI_rng_get_float(p->rng); | ||||
| } | } | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| } | } | ||||
| /* point time */ | /* point time */ | ||||
| pt->time = (float)(curtime - p->inittime); | pt->time = (float)(curtime - p->inittime); | ||||
| /* point uv (only 3d view) */ | |||||
| if ((p->sa->spacetype == SPACE_VIEW3D) && (gpd->sbuffer_size > 1)) { | |||||
| float pixsize = gp_style->texture_pixsize / 1000000.0f; | |||||
| tGPspoint *ptb = (tGPspoint *)gpd->sbuffer + gpd->sbuffer_size - 2; | |||||
| bGPDspoint spt, spt2; | |||||
| /* get origin to reproject point */ | |||||
| float origin[3]; | |||||
| gp_get_3d_reference(p, origin); | |||||
| /* reproject current */ | |||||
| ED_gpencil_tpoint_to_point(p->ar, origin, pt, &spt); | |||||
| ED_gp_project_point_to_plane(obact, rv3d, origin, ts->gp_sculpt.lock_axis - 1, ts->gpencil_src, &spt); | |||||
| /* reproject previous */ | |||||
| ED_gpencil_tpoint_to_point(p->ar, origin, ptb, &spt2); | |||||
| ED_gp_project_point_to_plane(obact, rv3d, origin, ts->gp_sculpt.lock_axis - 1, ts->gpencil_src, &spt2); | |||||
| p->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize; | |||||
| pt->uv_fac = p->totpixlen; | |||||
| if ((gp_style) && (gp_style->sima)) { | |||||
| pt->uv_fac /= gp_style->sima->gen_x; | |||||
| } | |||||
| } | |||||
| else { | |||||
| p->totpixlen = 0.0f; | |||||
| pt->uv_fac = 0.0f; | |||||
| } | |||||
| /* increment counters */ | /* increment counters */ | ||||
| gpd->sbuffer_size++; | gpd->sbuffer_size++; | ||||
| /* smooth while drawing previous points with a reduction factor for previous */ | |||||
| if (brush->gpencil_settings->active_smooth > 0.0f) { | |||||
| for (int s = 0; s < 3; s++) { | |||||
| gp_smooth_buffer(p, brush->gpencil_settings->active_smooth * ((3.0f - s) / 3.0f), gpd->sbuffer_size - s); | |||||
| } | |||||
| } | |||||
| /* tag depsgraph to update object */ | |||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); | |||||
| /* check if another operation can still occur */ | /* check if another operation can still occur */ | ||||
| if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX) | if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX) | ||||
| return GP_STROKEADD_FULL; | return GP_STROKEADD_FULL; | ||||
| else | else | ||||
| return GP_STROKEADD_NORMAL; | return GP_STROKEADD_NORMAL; | ||||
| } | } | ||||
| else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | ||||
| Show All 9 Lines | else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | ||||
| /* if there's stroke for this poly line session add (or replace last) point | /* if there's stroke for this poly line session add (or replace last) point | ||||
| * to stroke. This allows to draw lines more interactively (see new segment | * to stroke. This allows to draw lines more interactively (see new segment | ||||
| * during mouse slide, e.g.) | * during mouse slide, e.g.) | ||||
| */ | */ | ||||
| if (gp_stroke_added_check(p)) { | if (gp_stroke_added_check(p)) { | ||||
| bGPDstroke *gps = p->gpf->strokes.last; | bGPDstroke *gps = p->gpf->strokes.last; | ||||
| bGPDspoint *pts; | bGPDspoint *pts; | ||||
| MDeformVert *dvert; | |||||
| /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ | /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ | ||||
| if (gpd->sbuffer_size == 0) { | if (gpd->sbuffer_size == 0) { | ||||
| gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); | gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); | ||||
| gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); | |||||
| gps->totpoints++; | gps->totpoints++; | ||||
| } | } | ||||
| pts = &gps->points[gps->totpoints - 1]; | pts = &gps->points[gps->totpoints - 1]; | ||||
| dvert = &gps->dvert[gps->totpoints - 1]; | |||||
| /* special case for poly lines: normally, | /* special case for poly lines: normally, | ||||
| * depth is needed only when creating new stroke from buffer, | * depth is needed only when creating new stroke from buffer, | ||||
| * but poly lines are converting to stroke instantly, | * but poly lines are converting to stroke instantly, | ||||
| * so initialize depth buffer before converting coordinates | * so initialize depth buffer before converting coordinates | ||||
| */ | */ | ||||
| if (gpencil_project_check(p)) { | if (gpencil_project_check(p)) { | ||||
| View3D *v3d = p->sa->spacedata.first; | |||||
| view3d_region_operator_needs_opengl(p->win, p->ar); | view3d_region_operator_needs_opengl(p->win, p->ar); | ||||
| ED_view3d_autodist_init( | ED_view3d_autodist_init( | ||||
| p->depsgraph, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); | p->depsgraph, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); | ||||
| } | } | ||||
| /* convert screen-coordinates to appropriate coordinates (and store them) */ | /* convert screen-coordinates to appropriate coordinates (and store them) */ | ||||
| gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); | gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); | ||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | /* reproject to plane (only in 3d space) */ | ||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | |||||
| gp_reproject_toplane(p, gps); | gp_reproject_toplane(p, gps); | ||||
| } | |||||
| /* if parented change position relative to parent object */ | /* if parented change position relative to parent object */ | ||||
| if (gpl->parent != NULL) { | gp_apply_parent_point(depsgraph, obact, gpd, gpl, pts); | ||||
| gp_apply_parent_point(gpl, pts); | |||||
| } | |||||
| /* copy pressure and time */ | /* copy pressure and time */ | ||||
| pts->pressure = pt->pressure; | pts->pressure = pt->pressure; | ||||
| pts->strength = pt->strength; | pts->strength = pt->strength; | ||||
| pts->time = pt->time; | pts->time = pt->time; | ||||
| pts->uv_fac = pt->uv_fac; | |||||
| pts->uv_rot = pt->uv_rot; | |||||
| dvert->totweight = 0; | |||||
| dvert->dw = NULL; | |||||
| /* force fill recalc */ | /* force fill recalc */ | ||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | gps->flag |= GP_STROKE_RECALC_CACHES; | ||||
| /* drawing batch cache is dirty now */ | |||||
| gp_update_cache(p->gpd); | |||||
| } | } | ||||
| /* increment counters */ | /* increment counters */ | ||||
| if (gpd->sbuffer_size == 0) | if (gpd->sbuffer_size == 0) | ||||
| gpd->sbuffer_size++; | gpd->sbuffer_size++; | ||||
| /* tag depsgraph to update object */ | |||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); | |||||
| return GP_STROKEADD_NORMAL; | return GP_STROKEADD_NORMAL; | ||||
| } | } | ||||
| /* return invalid state for now... */ | /* return invalid state for now... */ | ||||
| return GP_STROKEADD_INVALID; | return GP_STROKEADD_INVALID; | ||||
| } | } | ||||
| /* simplify a stroke (in buffer) before storing it | /* simplify a stroke (in buffer) before storing it | ||||
| ▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | |||||
| /* make a new stroke from the buffer data */ | /* make a new stroke from the buffer data */ | ||||
| static void gp_stroke_newfrombuffer(tGPsdata *p) | static void gp_stroke_newfrombuffer(tGPsdata *p) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | bGPdata *gpd = p->gpd; | ||||
| bGPDlayer *gpl = p->gpl; | bGPDlayer *gpl = p->gpl; | ||||
| bGPDstroke *gps; | bGPDstroke *gps; | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| tGPspoint *ptc; | tGPspoint *ptc; | ||||
| bGPDbrush *brush = p->brush; | MDeformVert *dvert; | ||||
| Brush *brush = p->brush; | |||||
| ToolSettings *ts = p->scene->toolsettings; | ToolSettings *ts = p->scene->toolsettings; | ||||
| Depsgraph *depsgraph = p->depsgraph; | |||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| int i, totelem; | int i, totelem; | ||||
| /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ | /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ | ||||
| int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; | int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; | ||||
| /* get total number of points to allocate space for | /* get total number of points to allocate space for | ||||
| * - drawing straight-lines only requires the endpoints | * - drawing straight-lines only requires the endpoints | ||||
| */ | */ | ||||
| Show All 9 Lines | if (totelem == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* special case for poly line -- for already added stroke during session | /* special case for poly line -- for already added stroke during session | ||||
| * coordinates are getting added to stroke immediately to allow more | * coordinates are getting added to stroke immediately to allow more | ||||
| * interactive behavior | * interactive behavior | ||||
| */ | */ | ||||
| if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | ||||
| /* be sure to hide any lazy cursor */ | |||||
| ED_gpencil_toggle_brush_cursor(p->C, true, NULL); | |||||
| if (gp_stroke_added_check(p)) { | if (gp_stroke_added_check(p)) { | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| /* allocate memory for a new stroke */ | /* allocate memory for a new stroke */ | ||||
| gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); | gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); | ||||
| /* copy appropriate settings for stroke */ | /* copy appropriate settings for stroke */ | ||||
| gps->totpoints = totelem; | gps->totpoints = totelem; | ||||
| gps->thickness = brush->thickness; | gps->thickness = brush->size; | ||||
| gps->flag = gpd->sbuffer_sflag; | gps->flag = gpd->sbuffer_sflag; | ||||
| gps->inittime = p->inittime; | gps->inittime = p->inittime; | ||||
| /* enable recalculation flag by default (only used if hq fill) */ | /* enable recalculation flag by default (only used if hq fill) */ | ||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | gps->flag |= GP_STROKE_RECALC_CACHES; | ||||
| /* allocate enough memory for a continuous array for storage points */ | /* allocate enough memory for a continuous array for storage points */ | ||||
| int sublevel = brush->sublevel; | const int subdivide = brush->gpencil_settings->draw_subdivide; | ||||
| int new_totpoints = gps->totpoints; | |||||
| gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); | |||||
| gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); | |||||
| for (i = 0; i < sublevel; i++) { | |||||
| new_totpoints += new_totpoints - 1; | |||||
| } | |||||
| gps->points = MEM_callocN(sizeof(bGPDspoint) * new_totpoints, "gp_stroke_points"); | |||||
| /* initialize triangle memory to dummy data */ | /* initialize triangle memory to dummy data */ | ||||
| gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); | gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); | ||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | gps->flag |= GP_STROKE_RECALC_CACHES; | ||||
| gps->tot_triangles = 0; | gps->tot_triangles = 0; | ||||
| /* drawing batch cache is dirty now */ | |||||
| gp_update_cache(p->gpd); | |||||
| /* set pointer to first non-initialized point */ | /* set pointer to first non-initialized point */ | ||||
| pt = gps->points + (gps->totpoints - totelem); | pt = gps->points + (gps->totpoints - totelem); | ||||
| dvert = gps->dvert + (gps->totpoints - totelem); | |||||
| /* copy points from the buffer to the stroke */ | /* copy points from the buffer to the stroke */ | ||||
| if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { | if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { | ||||
| /* straight lines only -> only endpoints */ | /* straight lines only -> only endpoints */ | ||||
| { | { | ||||
| /* first point */ | /* first point */ | ||||
| ptc = gpd->sbuffer; | ptc = gpd->sbuffer; | ||||
| /* convert screen-coordinates to appropriate coordinates (and store them) */ | /* convert screen-coordinates to appropriate coordinates (and store them) */ | ||||
| gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | ||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | |||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | |||||
| gp_reproject_toplane(p, gps); | |||||
| } | |||||
| /* if parented change position relative to parent object */ | |||||
| if (gpl->parent != NULL) { | |||||
| gp_apply_parent_point(gpl, pt); | |||||
| } | |||||
| /* copy pressure and time */ | /* copy pressure and time */ | ||||
| pt->pressure = ptc->pressure; | pt->pressure = ptc->pressure; | ||||
| pt->strength = ptc->strength; | pt->strength = ptc->strength; | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| pt->time = ptc->time; | pt->time = ptc->time; | ||||
| dvert->totweight = 0; | |||||
| dvert->dw = NULL; | |||||
| pt++; | pt++; | ||||
| dvert++; | |||||
| } | } | ||||
| if (totelem == 2) { | if (totelem == 2) { | ||||
| /* last point if applicable */ | /* last point if applicable */ | ||||
| ptc = ((tGPspoint *)gpd->sbuffer) + (gpd->sbuffer_size - 1); | ptc = ((tGPspoint *)gpd->sbuffer) + (gpd->sbuffer_size - 1); | ||||
| /* convert screen-coordinates to appropriate coordinates (and store them) */ | /* convert screen-coordinates to appropriate coordinates (and store them) */ | ||||
| gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | ||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | |||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | |||||
| gp_reproject_toplane(p, gps); | |||||
| } | |||||
| /* if parented change position relative to parent object */ | |||||
| if (gpl->parent != NULL) { | |||||
| gp_apply_parent_point(gpl, pt); | |||||
| } | |||||
| /* copy pressure and time */ | /* copy pressure and time */ | ||||
| pt->pressure = ptc->pressure; | pt->pressure = ptc->pressure; | ||||
| pt->strength = ptc->strength; | pt->strength = ptc->strength; | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| pt->time = ptc->time; | pt->time = ptc->time; | ||||
| dvert->totweight = 0; | |||||
| dvert->dw = NULL; | |||||
| } | |||||
| /* reproject to plane (only in 3d space) */ | |||||
| gp_reproject_toplane(p, gps); | |||||
| pt = gps->points; | |||||
| for (i = 0; i < gps->totpoints; i++, pt++) { | |||||
| /* if parented change position relative to parent object */ | |||||
| gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); | |||||
| } | } | ||||
| } | } | ||||
| else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | ||||
| /* first point */ | /* first point */ | ||||
| ptc = gpd->sbuffer; | ptc = gpd->sbuffer; | ||||
| /* convert screen-coordinates to appropriate coordinates (and store them) */ | /* convert screen-coordinates to appropriate coordinates (and store them) */ | ||||
| gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | ||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | /* reproject to plane (only in 3d space) */ | ||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | |||||
| gp_reproject_toplane(p, gps); | gp_reproject_toplane(p, gps); | ||||
| } | |||||
| /* if parented change position relative to parent object */ | /* if parented change position relative to parent object */ | ||||
| if (gpl->parent != NULL) { | gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); | ||||
| gp_apply_parent_point(gpl, pt); | |||||
| } | |||||
| /* copy pressure and time */ | /* copy pressure and time */ | ||||
| pt->pressure = ptc->pressure; | pt->pressure = ptc->pressure; | ||||
| pt->strength = ptc->strength; | pt->strength = ptc->strength; | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| pt->time = ptc->time; | pt->time = ptc->time; | ||||
| dvert->totweight = 0; | |||||
| dvert->dw = NULL; | |||||
| } | } | ||||
| else { | else { | ||||
| float *depth_arr = NULL; | float *depth_arr = NULL; | ||||
| /* get an array of depths, far depths are blended */ | /* get an array of depths, far depths are blended */ | ||||
| if (gpencil_project_check(p)) { | if (gpencil_project_check(p)) { | ||||
| int mval[2], mval_prev[2] = { 0 }; | int mval[2], mval_prev[2] = { 0 }; | ||||
| int interp_depth = 0; | int interp_depth = 0; | ||||
| ▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | if (gpencil_project_check(p)) { | ||||
| } | } | ||||
| if (interp_depth) { | if (interp_depth) { | ||||
| interp_sparse_array(depth_arr, gpd->sbuffer_size, FLT_MAX); | interp_sparse_array(depth_arr, gpd->sbuffer_size, FLT_MAX); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| pt = gps->points; | pt = gps->points; | ||||
| /* convert all points (normal behavior) */ | /* convert all points (normal behavior) */ | ||||
| for (i = 0, ptc = gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++, pt++) { | for (i = 0, ptc = gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++, pt++) { | ||||
| /* convert screen-coordinates to appropriate coordinates (and store them) */ | /* convert screen-coordinates to appropriate coordinates (and store them) */ | ||||
| gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); | gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); | ||||
| /* copy pressure and time */ | /* copy pressure and time */ | ||||
| pt->pressure = ptc->pressure; | pt->pressure = ptc->pressure; | ||||
| pt->strength = ptc->strength; | pt->strength = ptc->strength; | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| pt->time = ptc->time; | pt->time = ptc->time; | ||||
| pt->uv_fac = ptc->uv_fac; | |||||
| pt->uv_rot = ptc->uv_rot; | |||||
| } | } | ||||
| /* subdivide the stroke */ | /* subdivide and smooth the stroke */ | ||||
| if (sublevel > 0) { | if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) { | ||||
| int totpoints = gps->totpoints; | gp_subdivide_stroke(gps, subdivide); | ||||
| for (i = 0; i < sublevel; i++) { | |||||
| /* we're adding one new point between each pair of verts on each step */ | |||||
| totpoints += totpoints - 1; | |||||
| gp_subdivide_stroke(gps, totpoints); | |||||
| } | |||||
| } | } | ||||
| /* apply randomness to stroke */ | /* apply randomness to stroke */ | ||||
| if (brush->draw_random_sub > 0.0f) { | if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && | ||||
| (brush->gpencil_settings->draw_random_sub > 0.0f)) | |||||
| { | |||||
| gp_randomize_stroke(gps, brush, p->rng); | gp_randomize_stroke(gps, brush, p->rng); | ||||
| } | } | ||||
| /* smooth stroke after subdiv - only if there's something to do | /* smooth stroke after subdiv - only if there's something to do | ||||
| * for each iteration, the factor is reduced to get a better smoothing without changing too much | * for each iteration, the factor is reduced to get a better smoothing without changing too much | ||||
| * the original stroke | * the original stroke | ||||
| */ | */ | ||||
| if (brush->draw_smoothfac > 0.0f) { | if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && | ||||
| (brush->gpencil_settings->draw_smoothfac > 0.0f)) | |||||
| { | |||||
| float reduce = 0.0f; | float reduce = 0.0f; | ||||
| for (int r = 0; r < brush->draw_smoothlvl; ++r) { | for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) { | ||||
| for (i = 0; i < gps->totpoints; i++) { | for (i = 0; i < gps->totpoints; i++) { | ||||
| /* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */ | BKE_gpencil_smooth_stroke(gps, i, brush->gpencil_settings->draw_smoothfac - reduce); | ||||
| gp_smooth_stroke(gps, i, brush->draw_smoothfac - reduce, false); | BKE_gpencil_smooth_stroke_strength(gps, i, brush->gpencil_settings->draw_smoothfac); | ||||
| } | } | ||||
| reduce += 0.25f; // reduce the factor | reduce += 0.25f; // reduce the factor | ||||
| } | } | ||||
| } | } | ||||
| /* smooth thickness */ | |||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && | ||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | (brush->gpencil_settings->thick_smoothfac > 0.0f)) | ||||
| gp_reproject_toplane(p, gps); | { | ||||
| for (int r = 0; r < brush->gpencil_settings->thick_smoothlvl * 2; r++) { | |||||
| for (i = 0; i < gps->totpoints; i++) { | |||||
| BKE_gpencil_smooth_stroke_thickness(gps, i, brush->gpencil_settings->thick_smoothfac); | |||||
| } | |||||
| } | } | ||||
| /* if parented change position relative to parent object */ | |||||
| if (gpl->parent != NULL) { | |||||
| gp_apply_parent(gpl, gps); | |||||
| } | } | ||||
| /* reproject to plane (only in 3d space) */ | |||||
| gp_reproject_toplane(p, gps); | |||||
| /* change position relative to parent object */ | |||||
| gp_apply_parent(depsgraph, obact, gpd, gpl, gps); | |||||
| if (depth_arr) | if (depth_arr) | ||||
| MEM_freeN(depth_arr); | MEM_freeN(depth_arr); | ||||
| } | } | ||||
| /* Save palette color */ | |||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(p->gpd); | /* Save material index */ | ||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | gps->mat_nr = BKE_object_material_slot_find_index(p->ob, p->material) - 1; | ||||
| gps->palcolor = palcolor; | |||||
| BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); | /* calculate UVs along the stroke */ | ||||
| ED_gpencil_calc_stroke_uv(obact, gps); | |||||
| /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head | /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head | ||||
| * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist | * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist | ||||
| * when drawing the background | * when drawing the background | ||||
| */ | */ | ||||
| if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode != GP_PAINTMODE_DRAW_POLY)) { | if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode != GP_PAINTMODE_DRAW_POLY)) { | ||||
| BLI_addhead(&p->gpf->strokes, gps); | BLI_addhead(&p->gpf->strokes, gps); | ||||
| } | } | ||||
| Show All 14 Lines | static float view3d_point_depth(const RegionView3D *rv3d, const float co[3]) | ||||
| else { | else { | ||||
| return -dot_v3v3(rv3d->viewinv[2], co); | return -dot_v3v3(rv3d->viewinv[2], co); | ||||
| } | } | ||||
| } | } | ||||
| /* only erase stroke points that are visible */ | /* only erase stroke points that are visible */ | ||||
| static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) | static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) | ||||
| { | { | ||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| if ((p->sa->spacetype == SPACE_VIEW3D) && | if ((p->sa->spacetype == SPACE_VIEW3D) && | ||||
| (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) | (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) | ||||
| { | { | ||||
| RegionView3D *rv3d = p->ar->regiondata; | RegionView3D *rv3d = p->ar->regiondata; | ||||
| bGPDlayer *gpl = p->gpl; | bGPDlayer *gpl = p->gpl; | ||||
| const int mval[2] = {x, y}; | const int mval[2] = {x, y}; | ||||
| float mval_3d[3]; | float mval_3d[3]; | ||||
| float fpt[3]; | float fpt[3]; | ||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| /* calculate difference matrix if parent object */ | /* calculate difference matrix if parent object */ | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | ED_gpencil_parent_location(p->depsgraph, obact, p->gpd, gpl, diff_mat); | ||||
| if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) { | if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) { | ||||
| const float depth_mval = view3d_point_depth(rv3d, mval_3d); | const float depth_mval = view3d_point_depth(rv3d, mval_3d); | ||||
| mul_v3_m4v3(fpt, diff_mat, &pt->x); | mul_v3_m4v3(fpt, diff_mat, &pt->x); | ||||
| const float depth_pt = view3d_point_depth(rv3d, fpt); | const float depth_pt = view3d_point_depth(rv3d, fpt); | ||||
| if (depth_pt > depth_mval) { | if (depth_pt > depth_mval) { | ||||
| Show All 16 Lines | static float gp_stroke_eraser_calc_influence(tGPsdata *p, const int mval[2], const int radius, const int co[2]) | ||||
| /* Control this further using pen pressure */ | /* Control this further using pen pressure */ | ||||
| fac *= p->pressure; | fac *= p->pressure; | ||||
| /* Return influence factor computed here */ | /* Return influence factor computed here */ | ||||
| return fac; | return fac; | ||||
| } | } | ||||
| /* helper to free a stroke */ | |||||
| static void gp_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps) | |||||
| { | |||||
| if (gps->points) { | |||||
| MEM_freeN(gps->points); | |||||
| } | |||||
| if (gps->dvert) { | |||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->dvert); | |||||
| } | |||||
| if (gps->triangles) | |||||
| MEM_freeN(gps->triangles); | |||||
| BLI_freelinkN(&gpf->strokes, gps); | |||||
| gp_update_cache(gpd); | |||||
| } | |||||
| /* eraser tool - evaluation per stroke */ | /* eraser tool - evaluation per stroke */ | ||||
| /* TODO: this could really do with some optimization (KD-Tree/BVH?) */ | /* TODO: this could really do with some optimization (KD-Tree/BVH?) */ | ||||
| static void gp_stroke_eraser_dostroke(tGPsdata *p, | static void gp_stroke_eraser_dostroke(tGPsdata *p, | ||||
| bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, | bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, | ||||
| const int mval[2], const int mvalo[2], | const int mval[2], const int mvalo[2], | ||||
| const int radius, const rcti *rect) | const int radius, const rcti *rect) | ||||
| { | { | ||||
| Depsgraph *depsgraph = p->depsgraph; | |||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| Brush *eraser = p->eraser; | |||||
| bGPDspoint *pt1, *pt2; | bGPDspoint *pt1, *pt2; | ||||
| int pc1[2] = {0}; | int pc1[2] = {0}; | ||||
| int pc2[2] = {0}; | int pc2[2] = {0}; | ||||
| int i; | int i; | ||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| /* calculate difference matrix if parent object */ | /* calculate difference matrix */ | ||||
| if (gpl->parent != NULL) { | ED_gpencil_parent_location(depsgraph, obact, p->gpd, gpl, diff_mat); | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | |||||
| } | |||||
| if (gps->totpoints == 0) { | if (gps->totpoints == 0) { | ||||
| /* just free stroke */ | /* just free stroke */ | ||||
| if (gps->points) | gp_free_stroke(p->gpd, gpf, gps); | ||||
| MEM_freeN(gps->points); | |||||
| if (gps->triangles) | |||||
| MEM_freeN(gps->triangles); | |||||
| BLI_freelinkN(&gpf->strokes, gps); | |||||
| } | } | ||||
| else if (gps->totpoints == 1) { | else if (gps->totpoints == 1) { | ||||
| /* only process if it hasn't been masked out... */ | /* only process if it hasn't been masked out... */ | ||||
| if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { | if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { | ||||
| if (gpl->parent == NULL) { | |||||
| gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); | |||||
| } | |||||
| else { | |||||
| bGPDspoint pt_temp; | bGPDspoint pt_temp; | ||||
| gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); | gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); | ||||
| gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); | gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); | ||||
| /* do boundbox check first */ | |||||
| 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, pc1) <= radius) { | |||||
| /* free stroke */ | |||||
| gp_free_stroke(p->gpd, gpf, gps); | |||||
| } | |||||
| } | } | ||||
| } | |||||
| } | |||||
| else if ((p->flags & GP_PAINTFLAG_STROKE_ERASER) || (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_STROKE)) { | |||||
| for (i = 0; (i + 1) < gps->totpoints; i++) { | |||||
| /* only process if it hasn't been masked out... */ | |||||
| if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) | |||||
| continue; | |||||
| /* get points to work with */ | |||||
| pt1 = gps->points + i; | |||||
| bGPDspoint npt; | |||||
| gp_point_to_parent_space(pt1, diff_mat, &npt); | |||||
| gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); | |||||
| /* do boundbox check first */ | /* do boundbox 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 */ | /* only check if point is inside */ | ||||
| if (len_v2v2_int(mval, pc1) <= radius) { | if (len_v2v2_int(mval, pc1) <= radius) { | ||||
| /* free stroke */ | /* free stroke */ | ||||
| // XXX: pressure sensitive eraser should apply here too? | gp_free_stroke(p->gpd, gpf, gps); | ||||
| MEM_freeN(gps->points); | return; | ||||
| if (gps->triangles) | |||||
| MEM_freeN(gps->triangles); | |||||
| BLI_freelinkN(&gpf->strokes, gps); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* Pressure threshold at which stroke should be culled: Calculated as pressure value | /* Pressure threshold at which stroke should be culled: Calculated as pressure value | ||||
| * below which we would have invisible strokes | * below which we would have invisible strokes | ||||
| */ | */ | ||||
| Show All 26 Lines | for (i = 0; (i + 1) < gps->totpoints; i++) { | ||||
| /* get points to work with */ | /* get points to work with */ | ||||
| pt1 = gps->points + i; | pt1 = gps->points + i; | ||||
| pt2 = gps->points + i + 1; | pt2 = gps->points + i + 1; | ||||
| /* only process if it hasn't been masked out... */ | /* only process if it hasn't been masked out... */ | ||||
| if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) | if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) | ||||
| continue; | continue; | ||||
| if (gpl->parent == NULL) { | |||||
| gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); | |||||
| gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); | |||||
| } | |||||
| else { | |||||
| bGPDspoint npt; | bGPDspoint npt; | ||||
| gp_point_to_parent_space(pt1, diff_mat, &npt); | gp_point_to_parent_space(pt1, diff_mat, &npt); | ||||
| gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); | gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); | ||||
| gp_point_to_parent_space(pt2, diff_mat, &npt); | gp_point_to_parent_space(pt2, diff_mat, &npt); | ||||
| gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); | gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); | ||||
| } | |||||
| /* Check that point segment of the boundbox of the eraser stroke */ | /* Check that point segment of the boundbox of the eraser stroke */ | ||||
| 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])) || | ||||
| ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[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 | /* Check if point segment of stroke had anything to do with | ||||
| * eraser region (either within stroke painted, or on its lines) | * eraser region (either within stroke painted, or on its lines) | ||||
| * - this assumes that linewidth is irrelevant | * - this assumes that linewidth is irrelevant | ||||
| */ | */ | ||||
| if (gp_stroke_inside_circle(mval, mvalo, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { | if (gp_stroke_inside_circle(mval, mvalo, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { | ||||
| if ((gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) || | if ((gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) || | ||||
| (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) | (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) | ||||
| { | { | ||||
| /* Point is affected: */ | /* Point is affected: */ | ||||
| /* 1) Adjust thickness | /* 1) Adjust thickness | ||||
| * - Influence of eraser falls off with distance from the middle of the eraser | * - Influence of eraser falls off with distance from the middle of the eraser | ||||
| * - Second point gets less influence, as it might get hit again in the next segment | * - Second point gets less influence, as it might get hit again in the next segment | ||||
| */ | */ | ||||
| pt1->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc1) * strength; | pt1->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc1) * strength; | ||||
| pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength / 2.0f; | pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength / 2.0f; | ||||
| /* 2) Tag any point with overly low influence for removal in the next pass */ | /* 2) Tag any point with overly low influence for removal in the next pass */ | ||||
| if (pt1->pressure < cull_thresh) { | if ((pt1->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || | ||||
| (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) | |||||
| { | |||||
| pt1->flag |= GP_SPOINT_TAG; | pt1->flag |= GP_SPOINT_TAG; | ||||
| do_cull = true; | do_cull = true; | ||||
| } | } | ||||
| if (pt2->pressure < cull_thresh) { | if ((pt2->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || | ||||
| (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) | |||||
| { | |||||
| pt2->flag |= GP_SPOINT_TAG; | pt2->flag |= GP_SPOINT_TAG; | ||||
| do_cull = true; | do_cull = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* Second Pass: Remove any points that are tagged */ | /* Second Pass: Remove any points that are tagged */ | ||||
| if (do_cull) { | if (do_cull) { | ||||
| gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG); | gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false); | ||||
| } | } | ||||
| gp_update_cache(p->gpd); | |||||
| } | } | ||||
| } | } | ||||
| /* erase strokes which fall under the eraser strokes */ | /* erase strokes which fall under the eraser strokes */ | ||||
| static void gp_stroke_doeraser(tGPsdata *p) | static void gp_stroke_doeraser(tGPsdata *p) | ||||
| { | { | ||||
| bGPDlayer *gpl; | bGPDlayer *gpl; | ||||
| bGPDstroke *gps, *gpn; | bGPDstroke *gps, *gpn; | ||||
| Show All 27 Lines | for (gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| else if (gpf == NULL) { | else if (gpf == NULL) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* loop over strokes, checking segments for intersections */ | /* loop over strokes, checking segments for intersections */ | ||||
| for (gps = gpf->strokes.first; gps; gps = gpn) { | for (gps = gpf->strokes.first; gps; gps = gpn) { | ||||
| gpn = gps->next; | gpn = gps->next; | ||||
| /* check if the color is editable */ | /* check if the color is editable */ | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) { | if (ED_gpencil_stroke_color_use(p->ob, gpl, gps) == false) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Not all strokes in the datablock may be valid in the current editor/context | /* Not all strokes in the datablock may be valid in the current editor/context | ||||
| * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) | * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) | ||||
| */ | */ | ||||
| if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) { | if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) { | ||||
| gp_stroke_eraser_dostroke(p, gpl, gpf, gps, p->mval, p->mvalo, p->radius, &rect); | gp_stroke_eraser_dostroke(p, gpl, gpf, gps, p->mval, p->mvalo, p->radius, &rect); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* ******************************************* */ | /* ******************************************* */ | ||||
| /* Sketching Operator */ | /* Sketching Operator */ | ||||
| /* clear the session buffers (call this before AND after a paint operation) */ | /* clear the session buffers (call this before AND after a paint operation) */ | ||||
| static void gp_session_validatebuffer(tGPsdata *p) | static void gp_session_validatebuffer(tGPsdata *p) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | bGPdata *gpd = p->gpd; | ||||
| Brush *brush = p->brush; | |||||
| /* clear memory of buffer (or allocate it if starting a new session) */ | /* clear memory of buffer (or allocate it if starting a new session) */ | ||||
| if (gpd->sbuffer) { | if (gpd->sbuffer) { | ||||
| /* printf("\t\tGP - reset sbuffer\n"); */ | /* printf("\t\tGP - reset sbuffer\n"); */ | ||||
| memset(gpd->sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); | memset(gpd->sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); | ||||
| } | } | ||||
| else { | else { | ||||
| /* printf("\t\tGP - allocate sbuffer\n"); */ | /* printf("\t\tGP - allocate sbuffer\n"); */ | ||||
| gpd->sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); | gpd->sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); | ||||
| } | } | ||||
| /* reset indices */ | /* reset indices */ | ||||
| gpd->sbuffer_size = 0; | gpd->sbuffer_size = 0; | ||||
| /* reset flags */ | /* reset flags */ | ||||
| gpd->sbuffer_sflag = 0; | gpd->sbuffer_sflag = 0; | ||||
| /* reset inittime */ | /* reset inittime */ | ||||
| p->inittime = 0.0; | p->inittime = 0.0; | ||||
| /* reset lazy */ | |||||
| if (brush) { | |||||
| brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; | |||||
| } | |||||
| } | } | ||||
| /* create a new palette color */ | /* helper to get default eraser and create one if no eraser brush */ | ||||
| static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette) | static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts) | ||||
| { | |||||
| Brush *brush_dft = NULL; | |||||
| Paint *paint = BKE_brush_get_gpencil_paint(ts); | |||||
| Brush *brush_old = paint->brush; | |||||
| for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) { | |||||
| if ((brush->ob_mode == OB_MODE_GPENCIL_PAINT) && | |||||
| (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) | |||||
| { | { | ||||
| bGPDpalettecolor *palcolor; | /* save first eraser to use later if no default */ | ||||
| if (brush_dft == NULL) { | |||||
| brush_dft = brush; | |||||
| } | |||||
| /* found default */ | |||||
| if(brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) { | |||||
| return brush; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* if no default, but exist eraser brush, return this and set as default */ | |||||
| if (brush_dft) { | |||||
| brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; | |||||
| return brush_dft; | |||||
| } | |||||
| /* create a new soft eraser brush */ | |||||
| else { | |||||
| brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser"); | |||||
| brush_dft->size = 30.0f; | |||||
| brush_dft->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); | |||||
| brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; | |||||
| brush_dft->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; | |||||
| brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; | |||||
| palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); | /* reset current brush */ | ||||
| BKE_paint_brush_set(paint, brush_old); | |||||
| return palcolor; | return brush_dft; | ||||
| } | |||||
| } | } | ||||
| /* initialize a drawing brush */ | /* initialize a drawing brush */ | ||||
| static void gp_init_drawing_brush(ToolSettings *ts, tGPsdata *p) | static void gp_init_drawing_brush(bContext *C, tGPsdata *p) | ||||
| { | { | ||||
| bGPDbrush *brush; | Brush *brush; | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| Paint *paint = BKE_brush_get_gpencil_paint(ts); | |||||
| /* if not exist, create a new one */ | /* if not exist, create a new one */ | ||||
| if (BLI_listbase_is_empty(&ts->gp_brushes)) { | if (paint->brush == NULL) { | ||||
| /* create new brushes */ | /* create new brushes */ | ||||
| BKE_gpencil_brush_init_presets(ts); | BKE_brush_gpencil_presets(C); | ||||
| brush = BKE_gpencil_brush_getactive(ts); | brush = BKE_brush_getactive_gpencil(ts); | ||||
| } | } | ||||
| else { | else { | ||||
| /* Use the current */ | /* Use the current */ | ||||
| brush = BKE_gpencil_brush_getactive(ts); | brush = BKE_brush_getactive_gpencil(ts); | ||||
| } | } | ||||
| /* be sure curves are initializated */ | /* be sure curves are initializated */ | ||||
| curvemapping_initialize(brush->cur_sensitivity); | curvemapping_initialize(brush->gpencil_settings->curve_sensitivity); | ||||
| curvemapping_initialize(brush->cur_strength); | curvemapping_initialize(brush->gpencil_settings->curve_strength); | ||||
| curvemapping_initialize(brush->cur_jitter); | curvemapping_initialize(brush->gpencil_settings->curve_jitter); | ||||
| /* asign to temp tGPsdata */ | /* asign to temp tGPsdata */ | ||||
| p->brush = brush; | p->brush = brush; | ||||
| if (brush->gpencil_settings->brush_type != GP_BRUSH_TYPE_ERASE) { | |||||
| p->eraser = gp_get_default_eraser(p->bmain, ts); | |||||
| } | |||||
| else { | |||||
| p->eraser = brush; | |||||
| } | |||||
| /* use radius of eraser */ | |||||
| p->radius = (short)p->eraser->size; | |||||
| } | } | ||||
| /* initialize a paint palette brush and a default color if not exist */ | /* initialize a paint brush and a default color if not exist */ | ||||
| static void gp_init_palette(tGPsdata *p) | static void gp_init_colors(tGPsdata *p) | ||||
| { | { | ||||
| bGPdata *gpd; | bGPdata *gpd = p->gpd; | ||||
| bGPDpalette *palette; | Brush *brush = p->brush; | ||||
| bGPDpalettecolor *palcolor; | |||||
| gpd = p->gpd; | Material *ma = NULL; | ||||
| MaterialGPencilStyle *gp_style = NULL; | |||||
| /* if not exist, create a new palette */ | /* use brush material */ | ||||
| if (BLI_listbase_is_empty(&gpd->palettes)) { | ma = BKE_gpencil_get_material_from_brush(brush); | ||||
| /* create new palette */ | |||||
| palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); | /* if no brush defaults, get material and color info | ||||
| /* now create a default color */ | * NOTE: Ensures that everything we need will exist... | ||||
| palcolor = gp_create_new_color(palette); | */ | ||||
| } | if ((ma == NULL) || (ma->gp_style == NULL)) { | ||||
| else { | BKE_gpencil_material_ensure(p->bmain, p->ob); | ||||
| /* Use the current palette and color */ | |||||
| palette = BKE_gpencil_palette_getactive(gpd); | /* assign always the first material to the brush */ | ||||
| /* the palette needs one color */ | p->material = give_current_material(p->ob, 1); | ||||
| if (BLI_listbase_is_empty(&palette->colors)) { | brush->gpencil_settings->material = p->material; | ||||
| palcolor = gp_create_new_color(palette); | |||||
| } | } | ||||
| else { | else { | ||||
| palcolor = BKE_gpencil_palettecolor_getactive(palette); | p->material = ma; | ||||
| } | } | ||||
| /* in some situations can be null, so use first */ | |||||
| if (palcolor == NULL) { | /* check if the material is already on object material slots and add it if missing */ | ||||
| BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); | if (BKE_object_material_slot_find_index(p->ob, p->material) == 0) { | ||||
| palcolor = palette->colors.first; | BKE_object_material_slot_add(p->bmain, p->ob); | ||||
| assign_material(p->bmain, p->ob, ma, p->ob->totcol, BKE_MAT_ASSIGN_EXISTING); | |||||
| } | } | ||||
| /* assign color information to temp tGPsdata */ | |||||
| gp_style = p->material->gp_style; | |||||
| if (gp_style) { | |||||
| /* set colors */ | |||||
| copy_v4_v4(gpd->scolor, gp_style->stroke_rgba); | |||||
| copy_v4_v4(gpd->sfill, gp_style->fill_rgba); | |||||
| /* add some alpha to make easy the filling without hide strokes */ | |||||
| if (gpd->sfill[3] > 0.8f) { | |||||
| gpd->sfill[3] = 0.8f; | |||||
| } | } | ||||
| /* asign to temp tGPsdata */ | gpd->mode = (short)gp_style->mode; | ||||
| p->palettecolor = palcolor; | gpd->bstroke_style = gp_style->stroke_style; | ||||
| gpd->bfill_style = gp_style->fill_style; | |||||
| } | |||||
| } | } | ||||
| /* (re)init new painting data */ | /* (re)init new painting data */ | ||||
| static bool gp_session_initdata(bContext *C, tGPsdata *p) | static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| bGPdata **gpd_ptr = NULL; | bGPdata **gpd_ptr = NULL; | ||||
| ScrArea *curarea = CTX_wm_area(C); | ScrArea *curarea = CTX_wm_area(C); | ||||
| ARegion *ar = CTX_wm_region(C); | ARegion *ar = CTX_wm_region(C); | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| Object *obact = CTX_data_active_object(C); | |||||
| View3D *v3d = curarea->spacedata.first; | |||||
| /* make sure the active view (at the starting time) is a 3d-view */ | /* make sure the active view (at the starting time) is a 3d-view */ | ||||
| if (curarea == NULL) { | if (curarea == NULL) { | ||||
| p->status = GP_STATUS_ERROR; | p->status = GP_STATUS_ERROR; | ||||
| if (G.debug & G_DEBUG) | if (G.debug & G_DEBUG) | ||||
| printf("Error: No active view for painting\n"); | printf("Error: No active view for painting\n"); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* pass on current scene and window */ | /* pass on current scene and window */ | ||||
| p->C = C; | |||||
| p->bmain = CTX_data_main(C); | p->bmain = CTX_data_main(C); | ||||
| p->scene = CTX_data_scene(C); | p->scene = CTX_data_scene(C); | ||||
| p->depsgraph = CTX_data_depsgraph(C); | p->depsgraph = CTX_data_depsgraph(C); | ||||
| p->win = CTX_wm_window(C); | p->win = CTX_wm_window(C); | ||||
| p->disable_fill = RNA_boolean_get(op->ptr, "disable_fill"); | |||||
| unit_m4(p->imat); | unit_m4(p->imat); | ||||
| unit_m4(p->mat); | unit_m4(p->mat); | ||||
| switch (curarea->spacetype) { | switch (curarea->spacetype) { | ||||
| /* supported views first */ | /* supported views first */ | ||||
| case SPACE_VIEW3D: | case SPACE_VIEW3D: | ||||
| { | { | ||||
| /* View3D *v3d = curarea->spacedata.first; */ | /* View3D *v3d = curarea->spacedata.first; */ | ||||
| /* RegionView3D *rv3d = ar->regiondata; */ | /* RegionView3D *rv3d = ar->regiondata; */ | ||||
| /* set current area | /* set current area | ||||
| * - must verify that region data is 3D-view (and not something else) | * - must verify that region data is 3D-view (and not something else) | ||||
| */ | */ | ||||
| /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ | /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ | ||||
| p->sa = curarea; | p->sa = curarea; | ||||
| p->ar = ar; | p->ar = ar; | ||||
| p->align_flag = &ts->gpencil_v3d_align; | p->align_flag = &ts->gpencil_v3d_align; | ||||
| if (ar->regiondata == NULL) { | if (ar->regiondata == NULL) { | ||||
| p->status = GP_STATUS_ERROR; | p->status = GP_STATUS_ERROR; | ||||
| if (G.debug & G_DEBUG) | if (G.debug & G_DEBUG) | ||||
| printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); | printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* if active object doesn't exist or it's not a Grease Pencil object, | |||||
| * use the scene's gp_object (), or create one if it doesn't exist | |||||
| */ | |||||
| float *cur = ED_view3d_cursor3d_get(p->scene, v3d)->location; | |||||
| if ((!obact) || (obact->type != OB_GPENCIL)) { | |||||
| if (p->scene->gp_object) { | |||||
| /* use existing default */ | |||||
| /* XXX: This will still lose whatever mode we were in before, | |||||
| * making GP less convenient for annotations than it used to be | |||||
| */ | |||||
| obact = p->scene->gp_object; | |||||
| /* temporarily activate the object */ | |||||
| ViewLayer *view_layer = CTX_data_view_layer(C); | |||||
| Base *base = BKE_view_layer_base_find(view_layer, obact); | |||||
| if (base) { | |||||
| if (CTX_data_edit_object(C)) | |||||
| ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR); /* freedata, and undo */ | |||||
| view_layer->basact = base; | |||||
| ED_object_base_activate(C, base); | |||||
| } | |||||
| else { | |||||
| printf("ERROR: Couldn't find base for active gp_object (view_layer = %p, obact = %s)\n", view_layer, obact->id.name); | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* create new default object */ | |||||
| obact = ED_add_gpencil_object(C, p->scene, cur); | |||||
| p->scene->gp_object = obact; | |||||
| } | |||||
| } | |||||
| /* assign object after all checks to be sure we have one active */ | |||||
| p->ob = obact; | |||||
| break; | break; | ||||
| } | } | ||||
| case SPACE_NODE: | case SPACE_NODE: | ||||
| { | { | ||||
| /* SpaceNode *snode = curarea->spacedata.first; */ | /* SpaceNode *snode = curarea->spacedata.first; */ | ||||
| /* set current area */ | /* set current area */ | ||||
| p->sa = curarea; | p->sa = curarea; | ||||
| ▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | if (ED_gpencil_session_active() == 0) { | ||||
| /* initialize undo stack, | /* initialize undo stack, | ||||
| * also, existing undo stack would make buffer drawn | * also, existing undo stack would make buffer drawn | ||||
| */ | */ | ||||
| gpencil_undo_init(p->gpd); | gpencil_undo_init(p->gpd); | ||||
| } | } | ||||
| /* clear out buffer (stored in gp-data), in case something contaminated it */ | /* clear out buffer (stored in gp-data), in case something contaminated it */ | ||||
| gp_session_validatebuffer(p); | gp_session_validatebuffer(p); | ||||
| /* set brush and create a new one if null */ | /* set brush and create a new one if null */ | ||||
| gp_init_drawing_brush(ts, p); | gp_init_drawing_brush(C, p); | ||||
| /* set palette info and create a new one if null */ | |||||
| gp_init_palette(p); | /* setup active color */ | ||||
| /* set palette colors */ | if (curarea->spacetype == SPACE_VIEW3D) { | ||||
| bGPDpalettecolor *palcolor = p->palettecolor; | /* NOTE: This is only done for 3D view, as Materials aren't used for | ||||
| bGPdata *pdata = p->gpd; | * annotations in 2D editors | ||||
| copy_v4_v4(pdata->scolor, palcolor->color); | */ | ||||
| copy_v4_v4(pdata->sfill, palcolor->fill); | gp_init_colors(p); | ||||
| pdata->sflag = palcolor->flag; | } | ||||
| else { | |||||
| #if 1 /* XXX: Temporary hack only - Materials won't be used here in future... */ | |||||
| gp_init_colors(p); | |||||
| #endif | |||||
| } | |||||
| /* lock axis */ | /* lock axis */ | ||||
| p->lock_axis = ts->gp_sculpt.lock_axis; | p->lock_axis = ts->gp_sculpt.lock_axis; | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| /* init new painting session */ | /* init new painting session */ | ||||
| static tGPsdata *gp_session_initpaint(bContext *C) | static tGPsdata *gp_session_initpaint(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| tGPsdata *p = NULL; | tGPsdata *p = NULL; | ||||
| /* create new context data */ | /* create new context data */ | ||||
| p = MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data"); | p = MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data"); | ||||
| gp_session_initdata(C, p); | gp_session_initdata(C, op, p); | ||||
| #if 0 | |||||
| /* radius for eraser circle is defined in userprefs now */ | /* radius for eraser circle is defined in userprefs now */ | ||||
| /* NOTE: we do this here, so that if we exit immediately, | /* NOTE: we do this here, so that if we exit immediately, | ||||
| * erase size won't get lost | * erase size won't get lost | ||||
| */ | */ | ||||
| p->radius = U.gp_eraser; | p->radius = U.gp_eraser; | ||||
| #endif | |||||
| /* Random generator, only init once. */ | /* Random generator, only init once. */ | ||||
| uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); | uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); | ||||
| rng_seed ^= GET_UINT_FROM_POINTER(p); | rng_seed ^= GET_UINT_FROM_POINTER(p); | ||||
| p->rng = BLI_rng_new(rng_seed); | p->rng = BLI_rng_new(rng_seed); | ||||
| /* return context data for running paint operator */ | /* return context data for running paint operator */ | ||||
| return p; | return p; | ||||
| Show All 30 Lines | |||||
| } | } | ||||
| /* init new stroke */ | /* init new stroke */ | ||||
| static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph) | static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph) | ||||
| { | { | ||||
| Scene *scene = p->scene; | Scene *scene = p->scene; | ||||
| ToolSettings *ts = scene->toolsettings; | ToolSettings *ts = scene->toolsettings; | ||||
| int cfra_eval = (int)DEG_get_ctime(p->depsgraph); | |||||
| /* get active layer (or add a new one if non-existent) */ | /* get active layer (or add a new one if non-existent) */ | ||||
| p->gpl = BKE_gpencil_layer_getactive(p->gpd); | p->gpl = BKE_gpencil_layer_getactive(p->gpd); | ||||
| if (p->gpl == NULL) { | if (p->gpl == NULL) { | ||||
| p->gpl = BKE_gpencil_layer_addnew(p->gpd, "GP_Layer", true); | p->gpl = BKE_gpencil_layer_addnew(p->gpd, "GP_Layer", true); | ||||
| if (p->custom_color[3]) | if (p->custom_color[3]) | ||||
| copy_v3_v3(p->gpl->color, p->custom_color); | copy_v3_v3(p->gpl->color, p->custom_color); | ||||
| Show All 22 Lines | for (bGPDlayer *gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| /* Add a new frame if needed (and based off the active frame, | /* Add a new frame if needed (and based off the active frame, | ||||
| * as we need some existing strokes to erase) | * as we need some existing strokes to erase) | ||||
| * | * | ||||
| * Note: We don't add a new frame if there's nothing there now, so | * Note: We don't add a new frame if there's nothing there now, so | ||||
| * -> If there are no frames at all, don't add one | * -> If there are no frames at all, don't add one | ||||
| * -> If there are no strokes in that frame, don't add a new empty frame | * -> If there are no strokes in that frame, don't add a new empty frame | ||||
| */ | */ | ||||
| if (gpl->actframe && gpl->actframe->strokes.first) { | if (gpl->actframe && gpl->actframe->strokes.first) { | ||||
| gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); | gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_COPY); | ||||
| has_layer_to_erase = true; | has_layer_to_erase = true; | ||||
| } | } | ||||
| /* XXX: we omit GP_FRAME_PAINT here for now, | /* XXX: we omit GP_FRAME_PAINT here for now, | ||||
| * as it is only really useful for doing | * as it is only really useful for doing | ||||
| * paintbuffer drawing | * paintbuffer drawing | ||||
| */ | */ | ||||
| } | } | ||||
| Show All 21 Lines | else { | ||||
| /* Drawing Modes - Add a new frame if needed on the active layer */ | /* Drawing Modes - Add a new frame if needed on the active layer */ | ||||
| short add_frame_mode; | short add_frame_mode; | ||||
| if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) | if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) | ||||
| add_frame_mode = GP_GETFRAME_ADD_COPY; | add_frame_mode = GP_GETFRAME_ADD_COPY; | ||||
| else | else | ||||
| add_frame_mode = GP_GETFRAME_ADD_NEW; | add_frame_mode = GP_GETFRAME_ADD_NEW; | ||||
| p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); | p->gpf = BKE_gpencil_layer_getframe(p->gpl, cfra_eval, add_frame_mode); | ||||
| /* set as dirty draw manager cache */ | |||||
| gp_update_cache(p->gpd); | |||||
| if (p->gpf == NULL) { | if (p->gpf == NULL) { | ||||
| p->status = GP_STATUS_ERROR; | p->status = GP_STATUS_ERROR; | ||||
| if (G.debug & G_DEBUG) | if (G.debug & G_DEBUG) | ||||
| printf("Error: No frame created (gpencil_paint_init)\n"); | printf("Error: No frame created (gpencil_paint_init)\n"); | ||||
| return; | return; | ||||
| } | } | ||||
| else { | else { | ||||
| Show All 19 Lines | else { | ||||
| if (p->sa->spacetype == SPACE_VIEW3D) { | if (p->sa->spacetype == SPACE_VIEW3D) { | ||||
| if (p->gpl->flag & GP_LAYER_NO_XRAY) { | if (p->gpl->flag & GP_LAYER_NO_XRAY) { | ||||
| p->flags &= ~GP_PAINTFLAG_V3D_ERASER_DEPTH; | p->flags &= ~GP_PAINTFLAG_V3D_ERASER_DEPTH; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* set special fill stroke mode */ | |||||
| if (p->disable_fill == true) { | |||||
| p->gpd->sbuffer_sflag |= GP_STROKE_NOFILL; | |||||
| /* replace stroke color with fill color */ | |||||
| copy_v4_v4(p->gpd->scolor, p->gpd->sfill); | |||||
| } | |||||
| /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ | /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ | ||||
| p->flags |= GP_PAINTFLAG_FIRSTRUN; | p->flags |= GP_PAINTFLAG_FIRSTRUN; | ||||
| /* when drawing in the camera view, in 2D space, set the subrect */ | /* when drawing in the camera view, in 2D space, set the subrect */ | ||||
| p->subrect = NULL; | p->subrect = NULL; | ||||
| if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { | if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { | ||||
| if (p->sa->spacetype == SPACE_VIEW3D) { | if (p->sa->spacetype == SPACE_VIEW3D) { | ||||
| View3D *v3d = p->sa->spacedata.first; | View3D *v3d = p->sa->spacedata.first; | ||||
| RegionView3D *rv3d = p->ar->regiondata; | RegionView3D *rv3d = p->ar->regiondata; | ||||
| /* for camera view set the subrect */ | /* for camera view set the subrect */ | ||||
| ▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | |||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| /* Helper callback for drawing the cursor itself */ | /* Helper callback for drawing the cursor itself */ | ||||
| static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr) | static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr) | ||||
| { | { | ||||
| tGPsdata *p = (tGPsdata *)p_ptr; | tGPsdata *p = (tGPsdata *)p_ptr; | ||||
| if (p->paintmode == GP_PAINTMODE_ERASER) { | if ((p) && (p->paintmode == GP_PAINTMODE_ERASER)) { | ||||
| Gwn_VertFormat *format = immVertexFormat(); | ED_gpencil_brush_draw_eraser(p->eraser, x, y); | ||||
| const uint shdr_pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); | |||||
| immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); | |||||
| glEnable(GL_LINE_SMOOTH); | |||||
| glEnable(GL_BLEND); | |||||
| glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |||||
| immUniformColor4ub(255, 100, 100, 20); | |||||
| imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40); | |||||
| immUnbindProgram(); | |||||
| immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); | |||||
| float viewport_size[4]; | |||||
| glGetFloatv(GL_VIEWPORT, viewport_size); | |||||
| immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); | |||||
| immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); | |||||
| immUniform1i("num_colors", 0); /* "simple" mode */ | |||||
| immUniform1f("dash_width", 12.0f); | |||||
| immUniform1f("dash_factor", 0.5f); | |||||
| imm_draw_circle_wire_2d(shdr_pos, x, y, p->radius, | |||||
| /* XXX Dashed shader gives bad results with sets of small segments currently, | |||||
| * temp hack around the issue. :( */ | |||||
| max_ii(8, p->radius / 2)); /* was fixed 40 */ | |||||
| immUnbindProgram(); | |||||
| glDisable(GL_BLEND); | |||||
| glDisable(GL_LINE_SMOOTH); | |||||
| } | } | ||||
| } | } | ||||
| /* Turn brush cursor in 3D view on/off */ | /* Turn brush cursor in 3D view on/off */ | ||||
| static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short enable) | static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short enable) | ||||
| { | { | ||||
| if (p->erasercursor && !enable) { | if (p->erasercursor && !enable) { | ||||
| /* clear cursor */ | /* clear cursor */ | ||||
| WM_paint_cursor_end(CTX_wm_manager(C), p->erasercursor); | WM_paint_cursor_end(CTX_wm_manager(C), p->erasercursor); | ||||
| p->erasercursor = NULL; | p->erasercursor = NULL; | ||||
| } | } | ||||
| else if (enable && !p->erasercursor) { | else if (enable && !p->erasercursor) { | ||||
| ED_gpencil_toggle_brush_cursor(p->C, false, NULL); | |||||
| /* enable cursor */ | /* enable cursor */ | ||||
| p->erasercursor = WM_paint_cursor_activate(CTX_wm_manager(C), | p->erasercursor = WM_paint_cursor_activate( | ||||
| CTX_wm_manager(C), | |||||
| NULL, /* XXX */ | NULL, /* XXX */ | ||||
| gpencil_draw_eraser, p); | gpencil_draw_eraser, p); | ||||
| } | } | ||||
| } | } | ||||
| /* Check if tablet eraser is being used (when processing events) */ | /* Check if tablet eraser is being used (when processing events) */ | ||||
| static bool gpencil_is_tablet_eraser_active(const wmEvent *event) | static bool gpencil_is_tablet_eraser_active(const wmEvent *event) | ||||
| { | { | ||||
| if (event->tablet_data) { | if (event->tablet_data) { | ||||
| const wmTabletData *wmtab = event->tablet_data; | const wmTabletData *wmtab = event->tablet_data; | ||||
| return (wmtab->Active == EVT_TABLET_ERASER); | return (wmtab->Active == EVT_TABLET_ERASER); | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| static void gpencil_draw_exit(bContext *C, wmOperator *op) | static void gpencil_draw_exit(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| tGPsdata *p = op->customdata; | tGPsdata *p = op->customdata; | ||||
| bGPdata *gpd = CTX_data_gpencil_data(C); | |||||
| /* clear undo stack */ | /* clear undo stack */ | ||||
| gpencil_undo_finish(); | gpencil_undo_finish(); | ||||
| /* restore cursor to indicate end of drawing */ | /* restore cursor to indicate end of drawing */ | ||||
| if (p->sa->spacetype != SPACE_VIEW3D) { | |||||
| WM_cursor_modal_restore(CTX_wm_window(C)); | WM_cursor_modal_restore(CTX_wm_window(C)); | ||||
| } | |||||
| else { | |||||
| /* or restore paint if 3D view */ | |||||
| if ((p) && (p->paintmode == GP_PAINTMODE_ERASER)) { | |||||
| WM_cursor_modal_set(p->win, CURSOR_STD); | |||||
| } | |||||
| /* drawing batch cache is dirty now */ | |||||
| if (gpd) { | |||||
| gp_update_cache(gpd); | |||||
| } | |||||
| } | |||||
| /* don't assume that operator data exists at all */ | /* don't assume that operator data exists at all */ | ||||
| if (p) { | if (p) { | ||||
| /* check size of buffer before cleanup, to determine if anything happened here */ | /* check size of buffer before cleanup, to determine if anything happened here */ | ||||
| if (p->paintmode == GP_PAINTMODE_ERASER) { | if (p->paintmode == GP_PAINTMODE_ERASER) { | ||||
| /* turn off radial brush cursor */ | /* turn off radial brush cursor */ | ||||
| gpencil_draw_toggle_eraser_cursor(C, p, false); | gpencil_draw_toggle_eraser_cursor(C, p, false); | ||||
| } | } | ||||
| /* always store the new eraser size to be used again next time | /* always store the new eraser size to be used again next time | ||||
| * NOTE: Do this even when not in eraser mode, as eraser may | * NOTE: Do this even when not in eraser mode, as eraser may | ||||
| * have been toggled at some point. | * have been toggled at some point. | ||||
| */ | */ | ||||
| U.gp_eraser = p->radius; | U.gp_eraser = p->radius; | ||||
| /* cleanup */ | /* cleanup */ | ||||
| gp_paint_cleanup(p); | gp_paint_cleanup(p); | ||||
| gp_session_cleanup(p); | gp_session_cleanup(p); | ||||
| ED_gpencil_toggle_brush_cursor(C, true, NULL); | |||||
| /* finally, free the temp data */ | |||||
| gp_session_free(p); | gp_session_free(p); | ||||
| } | } | ||||
| op->customdata = NULL; | op->customdata = NULL; | ||||
| } | } | ||||
| static void gpencil_draw_cancel(bContext *C, wmOperator *op) | static void gpencil_draw_cancel(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| /* this is just a wrapper around exit() */ | /* this is just a wrapper around exit() */ | ||||
| gpencil_draw_exit(C, op); | gpencil_draw_exit(C, op); | ||||
| } | } | ||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) | static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| { | { | ||||
| tGPsdata *p; | tGPsdata *p; | ||||
| eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); | eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| Brush *brush = BKE_brush_getactive_gpencil(ts); | |||||
| /* if mode is draw and the brush is eraser, cancel */ | |||||
| if (paintmode != GP_PAINTMODE_ERASER) { | |||||
| if ((brush) && (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) { | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| /* check context */ | /* check context */ | ||||
| p = op->customdata = gp_session_initpaint(C); | p = op->customdata = gp_session_initpaint(C, op); | ||||
| if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { | if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { | ||||
| /* something wasn't set correctly in context */ | /* something wasn't set correctly in context */ | ||||
| gpencil_draw_exit(C, op); | gpencil_draw_exit(C, op); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* init painting data */ | /* init painting data */ | ||||
| gp_paint_initstroke(p, paintmode, CTX_data_depsgraph(C)); | gp_paint_initstroke(p, paintmode, CTX_data_depsgraph(C)); | ||||
| if (p->status == GP_STATUS_ERROR) { | if (p->status == GP_STATUS_ERROR) { | ||||
| gpencil_draw_exit(C, op); | gpencil_draw_exit(C, op); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| if (event != NULL) { | if (event != NULL) { | ||||
| p->keymodifier = event->keymodifier; | p->keymodifier = event->keymodifier; | ||||
| } | } | ||||
| else { | else { | ||||
| p->keymodifier = -1; | p->keymodifier = -1; | ||||
| } | } | ||||
| p->reports = op->reports; | |||||
| /* everything is now setup ok */ | /* everything is now setup ok */ | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| /* ensure that the correct cursor icon is set */ | /* ensure that the correct cursor icon is set */ | ||||
| static void gpencil_draw_cursor_set(tGPsdata *p) | static void gpencil_draw_cursor_set(tGPsdata *p) | ||||
| { | { | ||||
| if (p->paintmode == GP_PAINTMODE_ERASER) | Brush *brush = p->brush; | ||||
| if ((p->paintmode == GP_PAINTMODE_ERASER) || | |||||
| (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) | |||||
| { | |||||
| WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ | WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ | ||||
| else | } | ||||
| WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); | else { | ||||
| WM_cursor_modal_set(p->win, CURSOR_STD); | |||||
| } | |||||
| } | } | ||||
| /* update UI indicators of status, including cursor and header prints */ | /* update UI indicators of status, including cursor and header prints */ | ||||
| static void gpencil_draw_status_indicators(tGPsdata *p) | static void gpencil_draw_status_indicators(tGPsdata *p) | ||||
| { | { | ||||
| /* header prints */ | /* header prints */ | ||||
| switch (p->status) { | switch (p->status) { | ||||
| case GP_STATUS_PAINTING: | case GP_STATUS_PAINTING: | ||||
| Show All 9 Lines | case GP_STATUS_IDLING: | ||||
| ED_area_headerprint(p->sa, IFACE_("Grease Pencil Erase Session: Hold and drag LMB or RMB to erase | " | ED_area_headerprint(p->sa, IFACE_("Grease Pencil Erase Session: Hold and drag LMB or RMB to erase | " | ||||
| "ESC/Enter to end (or click outside this area)")); | "ESC/Enter to end (or click outside this area)")); | ||||
| break; | break; | ||||
| case GP_PAINTMODE_DRAW_STRAIGHT: | case GP_PAINTMODE_DRAW_STRAIGHT: | ||||
| ED_area_headerprint(p->sa, IFACE_("Grease Pencil Line Session: Hold and drag LMB to draw | " | ED_area_headerprint(p->sa, IFACE_("Grease Pencil Line Session: Hold and drag LMB to draw | " | ||||
| "ESC/Enter to end (or click outside this area)")); | "ESC/Enter to end (or click outside this area)")); | ||||
| break; | break; | ||||
| case GP_PAINTMODE_DRAW: | case GP_PAINTMODE_DRAW: | ||||
| ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | " | ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw")); | ||||
| "E/ESC/Enter to end (or click outside this area)")); | |||||
| break; | break; | ||||
| case GP_PAINTMODE_DRAW_POLY: | case GP_PAINTMODE_DRAW_POLY: | ||||
| ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " | ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " | ||||
| "ESC/Enter to end (or click outside this area)")); | "Release Shift/ESC/Enter to end (or click outside this area)")); | ||||
| break; | break; | ||||
| default: /* unhandled future cases */ | default: /* unhandled future cases */ | ||||
| ED_area_headerprint(p->sa, IFACE_("Grease Pencil Session: ESC/Enter to end (or click outside this area)")); | ED_area_headerprint(p->sa, IFACE_("Grease Pencil Session: ESC/Enter to end (or click outside this area)")); | ||||
| break; | break; | ||||
| } | } | ||||
| break; | break; | ||||
| case GP_STATUS_ERROR: | case GP_STATUS_ERROR: | ||||
| case GP_STATUS_DONE: | case GP_STATUS_DONE: | ||||
| /* clear status string */ | /* clear status string */ | ||||
| ED_area_headerprint(p->sa, NULL); | ED_area_headerprint(p->sa, NULL); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| /* create a new stroke point at the point indicated by the painting context */ | /* create a new stroke point at the point indicated by the painting context */ | ||||
| static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) | static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | |||||
| tGPspoint *pt = NULL; | |||||
| /* handle drawing/erasing -> test for erasing first */ | /* handle drawing/erasing -> test for erasing first */ | ||||
| if (p->paintmode == GP_PAINTMODE_ERASER) { | if (p->paintmode == GP_PAINTMODE_ERASER) { | ||||
| /* do 'live' erasing now */ | /* do 'live' erasing now */ | ||||
| gp_stroke_doeraser(p); | gp_stroke_doeraser(p); | ||||
| /* store used values */ | /* store used values */ | ||||
| p->mvalo[0] = p->mval[0]; | p->mvalo[0] = p->mval[0]; | ||||
| p->mvalo[1] = p->mval[1]; | p->mvalo[1] = p->mval[1]; | ||||
| p->opressure = p->pressure; | p->opressure = p->pressure; | ||||
| } | } | ||||
| /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ | /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ | ||||
| else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { | else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { | ||||
| /* if lazy mouse, interpolate the last and current mouse positions */ | |||||
| if (GPENCIL_LAZY_MODE(p->brush, p->shift)) { | |||||
| float now_mouse[2]; | |||||
| float last_mouse[2]; | |||||
| copy_v2fl_v2i(now_mouse, p->mval); | |||||
| copy_v2fl_v2i(last_mouse, p->mvalo); | |||||
| interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor); | |||||
| round_v2i_v2fl(p->mval, now_mouse); | |||||
| } | |||||
| /* try to add point */ | /* try to add point */ | ||||
| short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); | short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); | ||||
| /* handle errors while adding point */ | /* handle errors while adding point */ | ||||
| if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { | if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { | ||||
| /* finish off old stroke */ | /* finish off old stroke */ | ||||
| gp_paint_strokeend(p); | gp_paint_strokeend(p); | ||||
| /* And start a new one!!! Else, projection errors! */ | /* And start a new one!!! Else, projection errors! */ | ||||
| Show All 21 Lines | else if (ok == GP_STROKEADD_INVALID) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* store used values */ | /* store used values */ | ||||
| p->mvalo[0] = p->mval[0]; | p->mvalo[0] = p->mval[0]; | ||||
| p->mvalo[1] = p->mval[1]; | p->mvalo[1] = p->mval[1]; | ||||
| p->opressure = p->pressure; | p->opressure = p->pressure; | ||||
| p->ocurtime = p->curtime; | p->ocurtime = p->curtime; | ||||
| pt = (tGPspoint *)gpd->sbuffer + gpd->sbuffer_size - 1; | |||||
| if (p->paintmode != GP_PAINTMODE_ERASER) { | |||||
| ED_gpencil_toggle_brush_cursor(C, true, &pt->x); | |||||
| } | |||||
| } | |||||
| else if ((p->brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) && | |||||
| (gpd->sbuffer_size > 0)) | |||||
| { | |||||
| pt = (tGPspoint *)gpd->sbuffer + gpd->sbuffer_size - 1; | |||||
| if (p->paintmode != GP_PAINTMODE_ERASER) { | |||||
| ED_gpencil_toggle_brush_cursor(C, true, &pt->x); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| /* handle draw event */ | /* handle draw event */ | ||||
| static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsgraph *depsgraph) | static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, int x, int y) | ||||
| { | { | ||||
| tGPsdata *p = op->customdata; | tGPsdata *p = op->customdata; | ||||
| PointerRNA itemptr; | PointerRNA itemptr; | ||||
| float mousef[2]; | float mousef[2]; | ||||
| int tablet = 0; | int tablet = 0; | ||||
| /* convert from window-space to area-space mouse coordinates | /* convert from window-space to area-space mouse coordinates | ||||
| * add any x,y override position for fake events | |||||
| * NOTE: float to ints conversions, +1 factor is probably used to ensure a bit more accurate rounding... | * NOTE: float to ints conversions, +1 factor is probably used to ensure a bit more accurate rounding... | ||||
| */ | */ | ||||
| p->mval[0] = event->mval[0] + 1; | p->mval[0] = event->mval[0] + 1 - x; | ||||
| p->mval[1] = event->mval[1] + 1; | p->mval[1] = event->mval[1] + 1 - y; | ||||
| p->shift = event->shift; | |||||
| /* verify key status for straight lines */ | /* verify key status for straight lines */ | ||||
| if ((event->ctrl > 0) || (event->alt > 0)) { | if ((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false)) { | ||||
| if (p->straight[0] == 0) { | if (p->straight[0] == 0) { | ||||
| int dx = abs(p->mval[0] - p->mvalo[0]); | int dx = abs(p->mval[0] - p->mvalo[0]); | ||||
| int dy = abs(p->mval[1] - p->mvalo[1]); | int dy = abs(p->mval[1] - p->mvalo[1]); | ||||
| if ((dx > 0) || (dy > 0)) { | if ((dx > 0) || (dy > 0)) { | ||||
| /* check mouse direction to replace the other coordinate with previous values */ | /* check mouse direction to replace the other coordinate with previous values */ | ||||
| if (dx >= dy) { | if (dx >= dy) { | ||||
| /* horizontal */ | /* horizontal */ | ||||
| p->straight[0] = 1; | p->straight[0] = 1; | ||||
| p->straight[1] = p->mval[1]; /* save y */ | p->straight[1] = (short)p->mval[1]; /* save y */ | ||||
| } | } | ||||
| else { | else { | ||||
| /* vertical */ | /* vertical */ | ||||
| p->straight[0] = 2; | p->straight[0] = 2; | ||||
| p->straight[1] = p->mval[0]; /* save x */ | p->straight[1] = (short)p->mval[0]; /* save x */ | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| p->straight[0] = 0; | p->straight[0] = 0; | ||||
| } | } | ||||
| Show All 18 Lines | if (p->paintmode == GP_PAINTMODE_ERASER) { | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* No tablet data -> No pressure info is available */ | /* No tablet data -> No pressure info is available */ | ||||
| p->pressure = 1.0f; | p->pressure = 1.0f; | ||||
| } | } | ||||
| /* special eraser modes */ | |||||
| if (p->paintmode == GP_PAINTMODE_ERASER) { | |||||
| if (event->shift > 0) { | |||||
| p->flags |= GP_PAINTFLAG_HARD_ERASER; | |||||
| } | |||||
| else { | |||||
| p->flags &= ~GP_PAINTFLAG_HARD_ERASER; | |||||
| } | |||||
| if (event->alt > 0) { | |||||
| p->flags |= GP_PAINTFLAG_STROKE_ERASER; | |||||
| } | |||||
| else { | |||||
| p->flags &= ~GP_PAINTFLAG_STROKE_ERASER; | |||||
| } | |||||
| } | |||||
| /* special exception for start of strokes (i.e. maybe for just a dot) */ | /* special exception for start of strokes (i.e. maybe for just a dot) */ | ||||
| if (p->flags & GP_PAINTFLAG_FIRSTRUN) { | if (p->flags & GP_PAINTFLAG_FIRSTRUN) { | ||||
| p->flags &= ~GP_PAINTFLAG_FIRSTRUN; | p->flags &= ~GP_PAINTFLAG_FIRSTRUN; | ||||
| p->mvalo[0] = p->mval[0]; | p->mvalo[0] = p->mval[0]; | ||||
| p->mvalo[1] = p->mval[1]; | p->mvalo[1] = p->mval[1]; | ||||
| p->opressure = p->pressure; | p->opressure = p->pressure; | ||||
| p->inittime = p->ocurtime = p->curtime; | p->inittime = p->ocurtime = p->curtime; | ||||
| Show All 26 Lines | static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, int x, int y) | ||||
| mousef[1] = p->mval[1]; | mousef[1] = p->mval[1]; | ||||
| RNA_float_set_array(&itemptr, "mouse", mousef); | RNA_float_set_array(&itemptr, "mouse", mousef); | ||||
| RNA_float_set(&itemptr, "pressure", p->pressure); | RNA_float_set(&itemptr, "pressure", p->pressure); | ||||
| RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0); | RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0); | ||||
| RNA_float_set(&itemptr, "time", p->curtime - p->inittime); | RNA_float_set(&itemptr, "time", p->curtime - p->inittime); | ||||
| /* apply the current latest drawing point */ | /* apply the current latest drawing point */ | ||||
| gpencil_draw_apply(op, p, depsgraph); | gpencil_draw_apply(C, op, p, depsgraph); | ||||
| /* force refresh */ | /* force refresh */ | ||||
| ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ | ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ | ||||
| } | } | ||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| /* operator 'redo' (i.e. after changing some properties, but also for repeat last) */ | /* operator 'redo' (i.e. after changing some properties, but also for repeat last) */ | ||||
| static int gpencil_draw_exec(bContext *C, wmOperator *op) | static int gpencil_draw_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| tGPsdata *p = NULL; | tGPsdata *p = NULL; | ||||
| Depsgraph *depsgraph = CTX_data_depsgraph(C); | Depsgraph *depsgraph = CTX_data_depsgraph(C); | ||||
| /* printf("GPencil - Starting Re-Drawing\n"); */ | /* printf("GPencil - Starting Re-Drawing\n"); */ | ||||
| /* try to initialize context data needed while drawing */ | /* try to initialize context data needed while drawing */ | ||||
| if (!gpencil_draw_init(C, op, NULL)) { | if (!gpencil_draw_init(C, op, NULL)) { | ||||
| if (op->customdata) MEM_freeN(op->customdata); | MEM_SAFE_FREE(op->customdata); | ||||
| /* printf("\tGP - no valid data\n"); */ | /* printf("\tGP - no valid data\n"); */ | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else | else | ||||
| p = op->customdata; | p = op->customdata; | ||||
| /* printf("\tGP - Start redrawing stroke\n"); */ | /* printf("\tGP - Start redrawing stroke\n"); */ | ||||
| Show All 30 Lines | if (p->flags & GP_PAINTFLAG_FIRSTRUN) { | ||||
| p->mvalo[0] = p->mval[0]; | p->mvalo[0] = p->mval[0]; | ||||
| p->mvalo[1] = p->mval[1]; | p->mvalo[1] = p->mval[1]; | ||||
| p->opressure = p->pressure; | p->opressure = p->pressure; | ||||
| p->ocurtime = p->curtime; | p->ocurtime = p->curtime; | ||||
| } | } | ||||
| /* apply this data as necessary now (as per usual) */ | /* apply this data as necessary now (as per usual) */ | ||||
| gpencil_draw_apply(op, p, depsgraph); | gpencil_draw_apply(C, op, p, depsgraph); | ||||
| } | } | ||||
| RNA_END; | RNA_END; | ||||
| /* printf("\tGP - done\n"); */ | /* printf("\tGP - done\n"); */ | ||||
| /* cleanup */ | /* cleanup */ | ||||
| gpencil_draw_exit(C, op); | gpencil_draw_exit(C, op); | ||||
| Show All 9 Lines | |||||
| /* start of interactive drawing part of operator */ | /* start of interactive drawing part of operator */ | ||||
| static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) | static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| { | { | ||||
| tGPsdata *p = NULL; | tGPsdata *p = NULL; | ||||
| if (G.debug & G_DEBUG) | if (G.debug & G_DEBUG) | ||||
| printf("GPencil - Starting Drawing\n"); | printf("GPencil - Starting Drawing\n"); | ||||
| /* support for tablets eraser pen */ | |||||
| if (gpencil_is_tablet_eraser_active(event)) { | |||||
| RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); | |||||
| } | |||||
| /* try to initialize context data needed while drawing */ | /* try to initialize context data needed while drawing */ | ||||
| if (!gpencil_draw_init(C, op, event)) { | if (!gpencil_draw_init(C, op, event)) { | ||||
| if (op->customdata) | if (op->customdata) | ||||
| MEM_freeN(op->customdata); | MEM_freeN(op->customdata); | ||||
| if (G.debug & G_DEBUG) | if (G.debug & G_DEBUG) | ||||
| printf("\tGP - no valid data\n"); | printf("\tGP - no valid data\n"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else | else | ||||
| p = op->customdata; | p = op->customdata; | ||||
| /* TODO: set any additional settings that we can take from the events? | /* TODO: set any additional settings that we can take from the events? | ||||
| * TODO? if tablet is erasing, force eraser to be on? */ | * TODO? if tablet is erasing, force eraser to be on? */ | ||||
| /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ | /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ | ||||
| /* if eraser is on, draw radial aid */ | /* if eraser is on, draw radial aid */ | ||||
| if (p->paintmode == GP_PAINTMODE_ERASER) { | if (p->paintmode == GP_PAINTMODE_ERASER) { | ||||
| gpencil_draw_toggle_eraser_cursor(C, p, true); | gpencil_draw_toggle_eraser_cursor(C, p, true); | ||||
| } | } | ||||
| else { | |||||
| ED_gpencil_toggle_brush_cursor(C, true, NULL); | |||||
| } | |||||
| /* set cursor | /* set cursor | ||||
| * NOTE: This may change later (i.e. intentionally via brush toggle, | * NOTE: This may change later (i.e. intentionally via brush toggle, | ||||
| * or unintentionally if the user scrolls outside the area)... | * or unintentionally if the user scrolls outside the area)... | ||||
| */ | */ | ||||
| gpencil_draw_cursor_set(p); | gpencil_draw_cursor_set(p); | ||||
| /* only start drawing immediately if we're allowed to do so... */ | /* only start drawing immediately if we're allowed to do so... */ | ||||
| if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { | if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { | ||||
| /* hotkey invoked - start drawing */ | /* hotkey invoked - start drawing */ | ||||
| /* printf("\tGP - set first spot\n"); */ | /* printf("\tGP - set first spot\n"); */ | ||||
| p->status = GP_STATUS_PAINTING; | p->status = GP_STATUS_PAINTING; | ||||
| /* handle the initial drawing - i.e. for just doing a simple dot */ | /* handle the initial drawing - i.e. for just doing a simple dot */ | ||||
| gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); | gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0, 0); | ||||
| op->flag |= OP_IS_MODAL_CURSOR_REGION; | op->flag |= OP_IS_MODAL_CURSOR_REGION; | ||||
| } | } | ||||
| else { | else { | ||||
| /* toolbar invoked - don't start drawing yet... */ | /* toolbar invoked - don't start drawing yet... */ | ||||
| /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ | /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ | ||||
| op->flag |= OP_IS_MODAL_CURSOR_REGION; | op->flag |= OP_IS_MODAL_CURSOR_REGION; | ||||
| } | } | ||||
| /* enable paint mode */ | |||||
| if (p->sa->spacetype == SPACE_VIEW3D) { | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) { | |||||
| /* Just set paintmode flag... */ | |||||
| p->gpd->flag |= GP_DATA_STROKE_PAINTMODE; | |||||
| /* disable other GP modes */ | |||||
| p->gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| p->gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| p->gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; | |||||
| /* set workspace mode */ | |||||
| ob->restore_mode = ob->mode; | |||||
| ob->mode = OB_MODE_GPENCIL_PAINT; | |||||
| /* redraw mode on screen */ | |||||
| WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); | |||||
| } | |||||
| } | |||||
| WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); | ||||
| /* add a modal handler for this operator, so that we can then draw continuous strokes */ | /* add a modal handler for this operator, so that we can then draw continuous strokes */ | ||||
| WM_event_add_modal_handler(C, op); | WM_event_add_modal_handler(C, op); | ||||
| return OPERATOR_RUNNING_MODAL; | 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 fullscreen) */ | ||||
| static bool gpencil_area_exists(bContext *C, ScrArea *sa_test) | static bool gpencil_area_exists(bContext *C, ScrArea *sa_test) | ||||
| { | { | ||||
| bScreen *sc = CTX_wm_screen(C); | bScreen *sc = CTX_wm_screen(C); | ||||
| return (BLI_findindex(&sc->areabase, sa_test) != -1); | return (BLI_findindex(&sc->areabase, sa_test) != -1); | ||||
| Show All 12 Lines | static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) | ||||
| } | } | ||||
| /* printf("\t\tGP - start stroke\n"); */ | /* printf("\t\tGP - start stroke\n"); */ | ||||
| /* we may need to set up paint env again if we're resuming */ | /* we may need to set up paint env again if we're resuming */ | ||||
| /* XXX: watch it with the paintmode! in future, | /* XXX: watch it with the paintmode! in future, | ||||
| * it'd be nice to allow changing paint-mode when in sketching-sessions */ | * it'd be nice to allow changing paint-mode when in sketching-sessions */ | ||||
| if (gp_session_initdata(C, p)) | if (gp_session_initdata(C, op, p)) | ||||
| gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); | gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); | ||||
| if (p->status != GP_STATUS_ERROR) { | if (p->status != GP_STATUS_ERROR) { | ||||
| p->status = GP_STATUS_PAINTING; | p->status = GP_STATUS_PAINTING; | ||||
| op->flag &= ~OP_IS_MODAL_CURSOR_REGION; | op->flag &= ~OP_IS_MODAL_CURSOR_REGION; | ||||
| } | } | ||||
| return op->customdata; | return op->customdata; | ||||
| Show All 34 Lines | static void gpencil_move_last_stroke_to_back(bContext *C) | ||||
| if (ELEM(NULL, gps)) { | if (ELEM(NULL, gps)) { | ||||
| return; | return; | ||||
| } | } | ||||
| BLI_remlink(&gpf->strokes, gps); | BLI_remlink(&gpf->strokes, gps); | ||||
| BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); | BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); | ||||
| } | } | ||||
| /* add events for missing mouse movements when the artist draw very fast */ | |||||
| static void gpencil_add_missing_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p) | |||||
| { | |||||
| Brush *brush = p->brush; | |||||
| if (brush->gpencil_settings->input_samples == 0) { | |||||
| return; | |||||
| } | |||||
| RegionView3D *rv3d = p->ar->regiondata; | |||||
| float defaultpixsize = rv3d->pixsize * 1000.0f; | |||||
| int samples = (GP_MAX_INPUT_SAMPLES - brush->gpencil_settings->input_samples + 1); | |||||
| float thickness = (float)brush->size; | |||||
| float pt[2], a[2], b[2]; | |||||
| float vec[3]; | |||||
| float scale = 1.0f; | |||||
| /* get pixel scale */ | |||||
| gp_get_3d_reference(p, vec); | |||||
| mul_m4_v3(rv3d->persmat, vec); | |||||
| if (rv3d->is_persp) { | |||||
| scale = vec[2] * defaultpixsize; | |||||
| } | |||||
| else { | |||||
| scale = defaultpixsize; | |||||
| } | |||||
| /* The thickness of the brush is reduced of thickness to get overlap dots */ | |||||
| float dot_factor = 0.50f; | |||||
| if (samples < 2) { | |||||
| dot_factor = 0.05f; | |||||
| } | |||||
| else if (samples < 4) { | |||||
| dot_factor = 0.10f; | |||||
| } | |||||
| else if (samples < 7) { | |||||
| dot_factor = 0.3f; | |||||
| } | |||||
| else if (samples < 10) { | |||||
| dot_factor = 0.4f; | |||||
| } | |||||
| float factor = ((thickness * dot_factor) / scale) * samples; | |||||
| copy_v2fl_v2i(a, p->mvalo); | |||||
| b[0] = event->mval[0] + 1; | |||||
| b[1] = event->mval[1] + 1; | |||||
| /* get distance in pixels */ | |||||
| float dist = len_v2v2(a, b); | |||||
| /* for very small distances, add a half way point */ | |||||
| if (dist <= 2.0f) { | |||||
| interp_v2_v2v2(pt, a, b, 0.5f); | |||||
| sub_v2_v2v2(pt, b, pt); | |||||
| /* create fake event */ | |||||
| gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), | |||||
| (int)pt[0], (int)pt[1]); | |||||
| } | |||||
| else if (dist >= factor) { | |||||
| int slices = 2 + (int)((dist - 1.0) / factor); | |||||
| float n = 1.0f / slices; | |||||
| for (int i = 1; i < slices; i++) { | |||||
| interp_v2_v2v2(pt, a, b, n * i); | |||||
| sub_v2_v2v2(pt, b, pt); | |||||
| /* create fake event */ | |||||
| gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), | |||||
| (int)pt[0], (int)pt[1]); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* events handling during interactive drawing part of operator */ | /* events handling during interactive drawing part of operator */ | ||||
| static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) | static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| { | { | ||||
| tGPsdata *p = op->customdata; | tGPsdata *p = op->customdata; | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through to support MMB view nav, etc. */ | int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through to support MMB view nav, etc. */ | ||||
| /* if (event->type == NDOF_MOTION) | /* if (event->type == NDOF_MOTION) | ||||
| Show All 26 Lines | else if (ELEM(event->type, PAD0, PAD1, PAD2, PAD3, PAD4, PAD5, PAD6, PAD7, PAD8, PAD9)) { | ||||
| /* allow numpad keys so that camera/view manipulations can still take place | /* allow numpad keys so that camera/view manipulations can still take place | ||||
| * - PAD0 in particular is really important for Grease Pencil drawing, | * - PAD0 in particular is really important for Grease Pencil drawing, | ||||
| * as animators may be working "to camera", so having this working | * as animators may be working "to camera", so having this working | ||||
| * is essential for ensuring that they can quickly return to that view | * is essential for ensuring that they can quickly return to that view | ||||
| */ | */ | ||||
| } | } | ||||
| else if ((ELEM(event->type, p->keymodifier)) && (event->val == KM_RELEASE)) { | else if ((ELEM(event->type, p->keymodifier)) && (event->val == KM_RELEASE)) { | ||||
| /* enable continuous if release D key in mid drawing */ | /* enable continuous if release D key in mid drawing */ | ||||
| if (p->sa->spacetype != SPACE_VIEW3D) { | |||||
| p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; | p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; | ||||
| } | } | ||||
| } | |||||
| else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { | else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { | ||||
| /* Add Blank Frame | /* Add Blank Frame | ||||
| * - Since this operator is non-modal, we can just call it here, and keep going... | * - Since this operator is non-modal, we can just call it here, and keep going... | ||||
| * - This operator is especially useful when animating | * - This operator is especially useful when animating | ||||
| */ | */ | ||||
| WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); | WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); | ||||
| estate = OPERATOR_RUNNING_MODAL; | estate = OPERATOR_RUNNING_MODAL; | ||||
| } | } | ||||
| else { | else { | ||||
| estate = OPERATOR_RUNNING_MODAL; | estate = OPERATOR_RUNNING_MODAL; | ||||
| } | } | ||||
| } | } | ||||
| //printf("\tGP - handle modal event...\n"); | //printf("\tGP - handle modal event...\n"); | ||||
| /* exit painting mode (and/or end current stroke) | /* exit painting mode (and/or end current stroke) | ||||
| * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] | * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] | ||||
| */ | */ | ||||
| if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { | /* if polyline and release shift must cancel */ | ||||
| if ((ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) || | |||||
| ((p->paintmode == GP_PAINTMODE_DRAW_POLY) && (event->shift == 0))) | |||||
| { | |||||
| /* exit() ends the current stroke before cleaning up */ | /* exit() ends the current stroke before cleaning up */ | ||||
| /* printf("\t\tGP - end of paint op + end of stroke\n"); */ | /* printf("\t\tGP - end of paint op + end of stroke\n"); */ | ||||
| /* if drawing polygon and enable on back, must move stroke */ | /* if drawing polygon and enable on back, must move stroke */ | ||||
| if (ts) { | if (ts) { | ||||
| if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { | if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { | ||||
| if (p->flags & GP_PAINTFLAG_STROKEADDED) { | if (p->flags & GP_PAINTFLAG_STROKEADDED) { | ||||
| gpencil_move_last_stroke_to_back(C); | gpencil_move_last_stroke_to_back(C); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | if (p->status == GP_STATUS_PAINTING) { | ||||
| /* if drawing polygon and enable on back, must move stroke */ | /* if drawing polygon and enable on back, must move stroke */ | ||||
| if (ts) { | if (ts) { | ||||
| if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { | if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { | ||||
| if (p->flags & GP_PAINTFLAG_STROKEADDED) { | if (p->flags & GP_PAINTFLAG_STROKEADDED) { | ||||
| gpencil_move_last_stroke_to_back(C); | gpencil_move_last_stroke_to_back(C); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* drawing batch cache is dirty now */ | |||||
| gp_update_cache(p->gpd); | |||||
| p->status = GP_STATUS_DONE; | p->status = GP_STATUS_DONE; | ||||
| estate = OPERATOR_FINISHED; | estate = OPERATOR_FINISHED; | ||||
| } | } | ||||
| } | } | ||||
| else if (event->val == KM_PRESS) { | else if (event->val == KM_PRESS) { | ||||
| bool in_bounds = false; | bool in_bounds = false; | ||||
| /* Check if we're outside the bounds of the active region | /* Check if we're outside the bounds of the active region | ||||
| ▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| } | } | ||||
| /* handle mode-specific events */ | /* handle mode-specific events */ | ||||
| if (p->status == GP_STATUS_PAINTING) { | if (p->status == GP_STATUS_PAINTING) { | ||||
| /* handle painting mouse-movements? */ | /* handle painting mouse-movements? */ | ||||
| if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { | if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { | ||||
| /* handle drawing event */ | /* handle drawing event */ | ||||
| /* printf("\t\tGP - add point\n"); */ | /* printf("\t\tGP - add point\n"); */ | ||||
| gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); | gpencil_add_missing_events(C, op, event, p); | ||||
| gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0, 0); | |||||
| /* finish painting operation if anything went wrong just now */ | /* finish painting operation if anything went wrong just now */ | ||||
| if (p->status == GP_STATUS_ERROR) { | if (p->status == GP_STATUS_ERROR) { | ||||
| printf("\t\t\t\tGP - add error done!\n"); | printf("\t\t\t\tGP - add error done!\n"); | ||||
| estate = OPERATOR_CANCELLED; | estate = OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else { | else { | ||||
| /* event handled, so just tag as running modal */ | /* event handled, so just tag as running modal */ | ||||
| ▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | void GPENCIL_OT_draw(wmOperatorType *ot) | ||||
| /* settings for drawing */ | /* settings for drawing */ | ||||
| ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); | ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); | ||||
| prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); | prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); | ||||
| RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); | RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); | ||||
| /* NOTE: wait for input is enabled by default, so that all UI code can work properly without needing users to know about this */ | /* NOTE: wait for input is enabled by default, so that all UI code can work properly without needing users to know about this */ | ||||
| prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); | prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); | ||||
| RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); | |||||
| prop = RNA_def_boolean(ot->srna, "disable_straight", false, "No Straight lines", "Disable key for straight lines"); | |||||
| RNA_def_property_flag(prop, PROP_SKIP_SAVE); | |||||
| prop = RNA_def_boolean(ot->srna, "disable_fill", false, "No Fill Areas", "Disable fill to use stroke as fill boundary"); | |||||
| RNA_def_property_flag(prop, PROP_SKIP_SAVE); | RNA_def_property_flag(prop, PROP_SKIP_SAVE); | ||||
| } | } | ||||